SentnelOpsSentnelOpsbeta
LAYER 1 · AGENT IDENTITY

How agent identity works

The agent record, the token lifecycle from mint to revocation, and why an agent token, a user session, and an org API key can never stand in for each other.

The agent record

Registering an agent creates one row, scoped to your organisation. Every field below is returned by GET /agents/{agent_id}:

FieldMeaning
idUUID, generated at registration. The token's sub claim and the CIMD URL both use it.
nameHuman-readable agent name.
ownerThe person or team responsible — who gets the call when this agent misbehaves.
environmentOne of production / staging / dev. Decides the initial status: dev and staging start active; production starts as a draft that needs a second person's approval (layer 2).
risk_levellow / medium / high — drives the layer-2 review cadence.
allowed_mcp / denied_mcpThe declared MCP-server scope. Baked into the token as a claim; also read live by the policy engine.
statusdraftapprovedactivesuspendeddecommissioned. Changed only via the lifecycle endpoints, never via PATCH.
created_by / approved_byCreator and approver on record. The API refuses self-approval — they are always different people.
org_idThe owning organisation. Every read and write is filtered by it; other tenants get a 404, not a 403.
created_at / updated_atSet by the database; updated_at advances on every change.
expires_atOptional hard end date for the agent (NULL = none).
last_review / next_reviewReview bookkeeping; next_review advances by the risk cadence when a review is recorded.
suspended_at / suspend_reasonSet on suspension; the reason is mandatory and lands in the audit trail.

Token lifecycle

The agent token is a self-contained HS256 JWT — decoding it yields identity and scope with no callback. It is minted inside the registration transaction and returned once, in the token field of the response. The server keeps only two derivatives: a bcrypt hash (proof of what was issued) and a SHA-256 index (a searchable fingerprint). The plaintext is never stored and can never be shown again — losing it means rotating it.

Token lifecycle
register ──▶ mint JWT (sub, scope, jti) ──▶ shown ONCE in the response
                 │
                 └─▶ stored: bcrypt hash + SHA-256 index   (never plaintext)

every proxy call:  verify signature + issuer ──▶ look up SHA-256 index
                                                  │
rotate / decommission ─▶ revoked_at = NOW() ──────┘  revoked? → 401
                                                     (cached ≤ ~5 s)

Verification. On every /proxy/… call the proxy verifies the JWT signature and issuer, then checks revocation: the exact token is looked up by its SHA-256 in agent_tokens, and it is rejected with 401 revoked agent token once every row bearing that fingerprint is revoked. The result is cached for proxy_identity_ttl_seconds (default 5), so a just-revoked token survives at most that window.

Revocation. Rotation and decommission both set revoked_at on every live token row for the agent. Rotation then mints a replacement; decommission does not. Each issuance carries a jti nonce — without it, two mints in the same second would be byte-identical, and rotating would revoke the very token it hands back.

Pre-006 tokens. Tokens issued before the revocation index existed have no stored SHA-256. For those the proxy falls back to “does this agent still have any live token row?” — so they keep working until fully revoked, and become strictly per-token at their next rotation.

Three credentials, never interchangeable

CredentialWho presents itHard boundary
Agent tokenAI agents, at the proxyA JWT with typ: "user" is rejected as an agent identity — a human session can never impersonate an agent.
User session JWTHumans, in the dashboardCarries typ: "user" and a 12-hour expiry; only valid for the management API, never the proxy.
snops_key_…Scripts, CI, the SDKsOrg-scoped, stored as a SHA-256 index, compared in constant time. Resolves to the admin role — treat it like a root credential.
Invariants. The plaintext token is retrievable exactly zero times after issuance — SentnelOps stores only its bcrypt hash and SHA-256 fingerprint. Revocation reaches the proxy within proxy_identity_ttl_seconds (default 5 s). And identity is org-scoped end to end: agents, tokens, and audit events all carry org_id, so no query crosses a tenant boundary.

What an active identity is allowed to do — approvals, suspension, policy — is the next layer: Lifecycle & policy.