The governance model rapidvalue.eu →

Non-human identities and ownership

Service accounts, API tokens, applications, agents and devices usually outnumber the people in an estate, and tend to belong to nobody in particular. That is a governance problem rather than a lament, so this page is how it is modelled: what counts as a non-human identity, where an owner comes from when nobody set one, and what the platform refuses to conclude when it cannot observe enough to be right.

What counts as a non-human identity

Five identity types are non-human: service account, system account, application, AI agent and IoT device. The set is defined once and shared by every consumer, because a signal about "machines" and a signal about "people" cannot be allowed to disagree on which is which.

The distinction earns its keep by changing behaviour, not by tidying a taxonomy:

Where they come from, honestly. There is no scanner that finds every class of machine identity. An NHI exists here because a connected source said so: a directory or HR record carrying the type, a connector object declaring the subtype it produces, the Entra agent-discovery hook, or a deliberate creation through the UI or the identity-push API. An account that merely looks like a service account is classified as an account — purpose_class = service — a statement about the account, not the minting of a governed machine identity. Anything in a system nobody has connected is, correctly, invisible.

One store, one writer

Ownership of a non-human identity is recorded in exactly one place: the canonical ownership table, as an nhi entity with a business owner and, optionally, a technical one. Every surface that answers "who owns this?" — the resolution cascade, certification's owner routing, the coverage rollup, the ownership workbench, evidence, the compliance control — reads that one table.

That sounds obvious. It was not always true, and the failure mode is why the design is now this rigid. The NHI workbench once kept its own membership records — a machine belonged to a management group, and that membership was the ownership, invisible to the canonical table. So an owner assigned from the orphan inbox was invisible to every resolver-backed consumer, and an owner imported from an agent's sponsor was invisible to the NHI page's own coverage figure. Two stores do not produce two opinions occasionally; they produce two opinions permanently, and each is convincing on the surface that owns it.

The management-group layer still exists, because it does real work the generic workbench does not — claims, delegations, escalation, task routing. It is a workflow layer, not a second store: its assertions land canonically, through the same write path any other owned object uses.

Writes are graded, and the grading is the point:

The inverse holds too. Removing an identity from a management group retires the canonical row only if that row still points at that group, so an owner someone re-pointed in the workbench survives the workflow-layer unassign. And a sponsor that cannot be resolved to a real person produces no owner row at all — the agent surfaces as unowned and enters the claim flow, rather than acquiring a placeholder that reads like an answer.

Where an owner comes from when nobody set one

Resolution walks an ordered cascade and stops at the first step that yields somebody. For a non-human identity the default order is: an explicit owner row; then an approval bucket the identity falls into by membership or rule; then the owner of a context it belongs to — its cost centre, department, function, location or company, in that priority; and finally the terminal bucket.

Two properties make the cascade honest rather than merely convenient.

The terminal step is a gap, not an answer. A cascade that always produces somebody is lying: it converts "nobody has accepted accountability for this machine" into a name, and that name will be the same operations queue for thousands of identities. The terminal bucket routes work somewhere so nothing falls on the floor, but an identity that reaches it counts as uncovered everywhere coverage is reported.

A broken owner falls through. If an explicit owner row points at an identity that no longer exists — after a re-sync, a reorganisation, a deletion — the step is treated as "no owner here" and the walk continues. Treating it instead as a match with an empty result stops the cascade dead and hands the caller an identity that cannot be assigned work.

The same cascade serves all three places it could have drifted: the detail pane, the owners table, and the batched coverage rollup. The batch path preloads its inputs once and then walks the identical per-entity code, so list, rollup and detail agree by construction rather than by review.

Coverage and the orphan inbox

"Unowned" has exactly one definition: the cascade ran and ended at the terminal bucket. Coverage is its complement. One predicate serves the coverage dashboard, the orphan inbox, the first-week quick scan and the compliance control, so the percentage and the length of the inbox cannot disagree.

Note what that excludes: coverage is counted neither from explicit owner rows nor from management-group membership. An identity owned through a context, or owned directly by a person because an agent's sponsor was imported, is covered even though the workflow layer never saw it.

For identities that are genuinely unowned, the inbox proposes candidates, ranked: a match against a group's naming-pattern rule, a match on the system the identity lives in, the owner of that system, and — only if nothing else matched — a tenant-wide fallback group. Each carries a confidence figure, and that figure is a fixed ranking constant, not a measured probability. It says a naming-pattern match is a better guess than a system-owner fallback. It does not say the guess is right 85% of the time, and nothing measures whether it is. A naming convention is evidence that somebody once had a convention, not that a particular person accepts accountability today.

So a suggestion is not an assignment. It becomes a claim sent to the proposed owner with a grace window — 14 days by default — which they accept, redirect, escalate or reject; an unanswered claim expires rather than silently becoming true. Bulk-sending claims is gated on a confidence floor, and bulk auto-assignment is set higher still. The human step is the product, not the friction.

Dormancy needs capability and evidence

A grant's last-used timestamp is empty for two completely different reasons. Either the connected system reports activity, the platform has looked, and this grant has not been touched — a real finding. Or the platform cannot see activity on that system at all, in which case the empty value means "we do not track this", and reading it as dormancy is a fabrication.

A system therefore counts as usage-tracked only when both hold:

  1. Capability — the connector's engine can actually retrieve usage events. This is a property of the code, probed per engine, not a checkbox somebody could tick optimistically. Today that means Microsoft Entra and Google Workspace; every other connected system has unknown dormancy by construction, and says so.
  2. Evidence — an ingest cycle for that specific connector has completed at least once. Capability is a claim about what could be observed; this is the record that somebody looked. Fetching zero events is a valid completed cycle — "we polled, nothing new" — and counts. A failed cycle does not, and a later failure never erases evidence already earned.

If a tenant has no system meeting both conditions, dormancy is unknown for every grant and nothing is flagged. That is not a degraded mode; it is the correct answer. It exists because the alternative shipped once: a tenant with no usage ingest saw its entire grant population — 8,841 of them — reported as dormant, which is a confident, comprehensive, worthless finding.

Both consumers of that rule — the advisor's dormancy detector and the identity risk score's dormancy components — share one implementation, because they previously held byte-identical copies kept in step by a comment asking the next person to remember. A tenant seeing dormant grants in one place and not the other has no way to tell which is wrong.

Within tracked systems, a second and much narrower control decides how to read an empty timestamp: by default it means "never used since we started watching", which on a system that is being watched is the strongest dormancy signal there is. It can be suppressed per tenant. The two never overlap — one concerns systems we cannot observe, the other grants we have observed and never seen used.

Acting on a dormant finding revokes the grant on the target system but keeps the row, so the record that the access once existed survives for audit.

Known edges, published rather than discovered


Further reading: