allow, challenge or deny from what it already knows about that identity — its lifecycle state, its credentials, the tools it is entitled to use, the classification of what it is reaching for, and whether a person has granted it access.
Asking the gate
requestedOperation and declaredActionClass are required; toolKey, resourceKey, intent and requestId are optional.
A refusal is HTTP 200 with a deny decision, not an error status. The gate answered; the answer was no. 422 is reserved for a request the gate could not read at all — a missing operation, or an action class that is not one of the known values.
The answer carries the decision, the effective action class, the rules consulted and, where the refusal has a named cause, a reason such as tool_entitlement_uncollected or resource_unclassified. When a request for the same target is already awaiting a person’s decision, the answer names it, so a runtime can tell “ask someone” from “somebody already did”.
Report what happened afterwards with POST /api/v1/mediation/{id}/outcome and {"outcome": "succeeded"} or {"outcome": "failed", "failureReason": "..."}. An outcome can be reported once, only for a decision that allowed the action.
Replay
If you send arequestId, a repeat of the same request returns the decision already recorded rather than evaluating again. A runtime that retries after a network failure cannot turn a deny into an allow by asking twice.
How the gate decides
Thirteen rules run. Each returnsallow, challenge, deny or abstains, and the strictest answer wins. The combination starts at deny: a rule set in which everything abstains denies.
The four rules you cannot switch off
These four are re-enabled in code whatever the stored configuration says. No access grant, approval or rule change lifts them. This is what quarantine means at runtime: every action by a quarantined identity, of every class, is denied.
The nine rules you can tune
Each of these raises a challenge at most — a refusal that says “a person should decide”, not “never”.
The last two abstain rather than challenge when there is no baseline yet, so a newly integrated runtime is not challenged on every call while its history is being built.
GET /api/v1/runtime/rules shows the catalogue with your organization’s configuration; POST /api/v1/runtime/rules/{key} with {"isEnabled": false} switches one off. That needs runtime:rule:manage — inside TRUSTIVAN, an Organization Admin. Attempting it on a mandatory rule is 409.
Action class
The caller declares an action class, and NHI Security takes the stricter of that declaration and what the tool is known to do. A runtime cannot downgrade a mutating tool by calling the action a read, and an unclassified tool forces the class tounknown, which is treated as potentially mutating. The decision record stores the effective class, not the claim.
It fails closed
An unknown identity is denied — and the request is still recorded, so an identity nobody has inventoried asking for access is visible rather than invisible. A request naming no tool is challenged, never allowed by omission. A credential that is absent, malformed, revoked or expired is refused before the gate runs at all. If the gate cannot read the facts it needs, the request fails with an error; it never resolves to an allow.Runtime credentials
An agent runtime authenticates with a bearer credential issued in NHI Security.
Both write permissions belong to the Organization Admin role inside TRUSTIVAN.
- The token is 32 random bytes, base64url-encoded. Nothing is encoded in it: no organization, no identity, no prefix, no expiry.
- It is returned once, at issue. Only a hash is stored, so it cannot be read back. Lose it and you reissue.
- Lifetime is a whole number of days, at least 1 and at most 90. There is no “never expires”.
- Expiry and revocation are enforced inside the lookup, so there is no path that authenticates a dead credential and no cache to wait on. A revoked token and a token that never existed are indistinguishable to the caller.
- There is no rotation. Issue the replacement, move the runtime onto it, then revoke the old one.
- It can only be issued to an identity that is an AI agent and is active.
Access grants
An access grant is how a person lifts a challenge: a time-boxed authorization for one identity to reach one tool or one resource.- Duration is 5 minutes to 7 days. The window starts when the grant is approved, not when it was asked for.
- One approval is enough, and the approver must be a different person from the requester — refused with
403, in the domain, in the repository and by a database constraint. A single refusal defeats the request however many approvals it has. - A grant is in force only while its state is granted, it has not been revoked, and the clock is inside its window. A grant past its end date stops covering anything immediately, whatever its stored state says.
- Exactly one of a tool or a resource must be named. A resource grant never covers a tool.
A grant matches on the target, not on the operation: a grant on a tool covers any operation that identity performs against that tool for the length of the window. Scope a grant by choosing a narrow tool or resource and a short duration.
The decision log
Every decision is recorded, including every refusal, and including requests from identities the organization has never seen. Records cannot be amended or deleted; the database refuses it.
All four need
runtime:read, which every role holds. Evidence rows are written in the same transaction as the decision, so a decision never exists without its reasons.
Gate decisions are not written to the audit trail. The audit trail records what people did — issuing a credential, approving a grant, changing a rule. What the gate decided lives here, in a log that is equally append-only. See Governance and audit.
Getting to a first allow
- Have an identity of type
ai_agentin the inventory. No connector produces one, so today this means an estate seeded for demonstration, or a connector you build against the discovery contract. - Declare that identity’s autonomy —
PATCH /api/v1/agents/{identityId}/autonomy,agent:manage. - Declare what its tools do —
PATCH /api/v1/tools/{toolId}/effect— and which of them it may use —POST /api/v1/tools/entitlements, bothagent:manage. This is what stopstool_not_entitledandtool_effect_unclassifiedfiring. - Classify the resources it will reach —
PATCH /api/v1/resources/{resourceId}/classification,resource:classify— soresource_unresolvedstops firing. - Issue it a runtime credential and point the runtime at
POST /api/v1/mediation. - Approve an access grant for anything still challenged.

