Non-human identity security
Almost every organisation can list its employees, and almost none can list its service accounts. Hanu builds that list from the access you already grant it, then answers the three questions that decide whether a machine identity is a liability: is it used, who owns it, and what can it reach.
The problem
A non-human identity is anything that authenticates without a person behind it: an IAM access key, a role, a Secrets Manager secret, an SSM parameter, an instance profile, a credential sitting in a Lambda environment variable, an OIDC provider. The count in a growing cloud account is in the hundreds, and nobody enumerates it, because nothing in the console is titled "identities we do not know about".
The risk is not that these exist. It is that the unused and the over-privileged ones do. A key nobody has called in nine months still works, still carries whatever policy somebody attached during a migration, and has no owner to ask. When it is used again, the usage itself is the only signal available — a machine identity has no MFA event, no impossible-travel login, and none of the behavioural baseline a human account accumulates over years.
Attackers have made this the easier door. The poisoned LiteLLM 1.107.0 release harvested cloud credentials from machines that installed it and validated them with sts:GetCallerIdentity within about four and a half hours, then probed model capacity. ConnectWise's own cloud application credentials were used to authenticate into downstream customer tenants because those applications were already trusted inside the tenants, so interactive sign-in and MFA never applied — and app credentials can remain a working backdoor after the compromised application is remediated. Device-code phishing reaches the same place without a password at all, and a password reset does not evict the token.
How Hanu approaches it
Discovery is API-first and repeated, not imported once. Seven identity types are enumerated on every scan across IAM, Secrets Manager, SSM and Lambda, and each one carries creation date, last-used date and service, rotation state, owner tag and the permissions attached. A new identity appearing is an INFO finding — NHI_LIFECYCLE_NEW — so the inventory stays complete without turning completeness into a queue.
Risk is a weighted sum of named factors with the band boundaries written down: 70 or above is CRITICAL, 50 HIGH, 30 MEDIUM. The finding names the factors that produced it, so OVERPRIVILEGED_ADMIN reads as "an admin policy is attached to a machine", not as a decimal. Machine-identity scoring is separate from human IAM scoring on purpose; a dormant key and a dormant employee are not the same problem and should not share a queue.
Lifecycle state is derived from a snapshot and then diffed against the previous scan. DORMANT at 30 days of no use, STALE at 90 days with no rotation, EXPIRED for an access key older than a year, plus ACTIVE, ROTATED and REVOKED. The transitions are where the meaning is: STALE → ACTIVE is HIGH with the reason stated plainly, because a credential that has been quiet for ninety days and is suddenly in use is an indicator of compromise. ACTIVE → DORMANT is not.
Because there is no baseline to compare against, the second anchor is first use and shape. CREDENTIAL_VALIDATION, MODEL_ENUMERATION and MODEL_INVOCATION are ordered stages over CloudTrail, and a validation only counts when it is the first ever recorded for that key inside 24 hours. Two distinct stages, each within 12 hours of the last, produce LLM_JACKING_ACTIVITY: HIGH during reconnaissance, CRITICAL once inference is reached, with the stages, event names, source addresses and elapsed time attached to the signal.
The third anchor is the key's own history. NhiItdrEngine establishes a 30-day source-IP baseline per access key and emits NHI_ITDR_NEW_SOURCE_IP for any address that has never appeared in it. A key silent across the 60-day window ending two days ago and active now is NHI_ITDR_DORMANT_KEY_REACTIVATED at CRITICAL, because dormant-then-active is a compromise shape and not a change of schedule. Service-account keys that reach for CreateAccessKey, AttachUserPolicy, CreateLoginProfile or AssumeRole are flagged by name pattern and by API, and one key working across regions is flagged separately.
Federated trust gets its own rules, because it is persistence that survives rotation. RULE-BEH-AWS-NEW-018 fires on an OIDC provider thumbprint being updated, RULE-BEH-AWS-NEW-105 on a provider being created, RULE-BEH-AWS-NEW-095 on AssumeRoleWithWebIdentity. On Azure, RULE-BEH-AZURE-NEW-014 fires on a federated credential added to an application and RULE-BEH-AZURE-NEW-015 when its subject is a wildcard that lets any repository in an external organisation impersonate the app. On GCP the equivalents are a Workload Identity Pool provider created with an external OIDC issuer, a GenerateIdToken call that impersonates a service account, and an iam.workloadIdentityUser grant that lets a federated workload do it without a key.
The joins are where the inventory earns its place. A leaked secret plus a PRIVILEGE_ESCALATION_PATH through the same identity is RULE-T2-001. A leaked secret plus an OIDC trust change on the same role is RULE-T2-NEW-HI-002. A credential is only interesting in relation to what it opens, which is why identities are graph nodes and not just rows — see attack path analysis for how reach is walked.
What you get
NHI-01Identity inventory
Owner, Team and Service tag keys, so an unowned credential is visible as unowned instead of quietly filed under the account that created it.NHI-02Composite risk score per identity
NHI-03Lifecycle transitions and per-key usage history
Limits
Hanu is not a PAM and not a secrets vault. It never reads a credential value, never stores one and never proxies one: the NHI engines work from creation, last-use, trust-policy and permission metadata plus CloudTrail events, and the secret scanner records a sha256 prefix of a match rather than the secret itself. Rotation stays your mechanism — where a Secrets Manager rotation Lambda already exists, a remediation can trigger it, but nothing here holds or manages a credential lifecycle.
There is no provisioning integration with an identity provider: no SCIM, no just-in-time grant, nothing that creates or deprovisions an identity. Coverage follows the collectors, too. AWS IAM, Secrets Manager, SSM, Lambda and OIDC providers are enumerated; Kubernetes service accounts appear through audit-event rules on bound-token minting rather than through the NHI inventory; and a cloud platform with no collector contributes no identities and no findings at all.
Questions
How do you find the identities nobody told us about?
GetAccessKeyLastUsed, IAM roles and their trust policies, OIDC providers, Secrets Manager secrets, SSM parameters, instance profiles and Lambda environment credentials are read from the provider's own APIs on each scan. Anything that appears is NHI_LIFECYCLE_NEW at INFO severity with its owner tag status attached, so the first thing you learn about a new credential is whether anyone admits to owning it.What makes a dormant credential dangerous if nothing is using it?
NHI_ITDR_DORMANT_KEY_REACTIVATED at CRITICAL, and a lifecycle transition from STALE to ACTIVE is HIGH. Both say indicator of compromise rather than drift, because a credential nobody has needed for two months has no legitimate reason to suddenly be worth invoking.Do you rotate or store our credentials?
How does this connect to the rest of the platform?
Run it in your own account
One Docker Compose stack, your datastores, read-only credentials into your clouds. The Community edition is free to run in production.