VotalAI · LLM Shield

Agentic identity — what holds, and where it breaks

Three properties decide whether an agent's identity means anything: who is this agent, on whose behalf is it acting, and is the presenter the party the credential was issued to. Shield answers the first well, the second partially, the third not at all.

Status verified against the codebase, 2026-07-27.

built and enforced present but not wired, or off by default missing
Identity plane · who vouches
Issues the credential the agent carries.
Keycloak / OIDCbuilt Service-account tokens verified by oidc_sa: signature, allow-listed issuer, mandatory audience.
Shield agent tokensbuilt Ed25519, alg pinned, revocation by jti / instance / user, kid retirement.
SPIFFE SVIDoff by default Real X.509 chain verification. Needs SHIELD_SPIFFE_ENABLED.
mTLS fingerprintforgeable A fingerprint header is accepted with no certificate present. Gated by a trusted-proxy boundary that is off by default.
Shield · the chokepoint
Every prompt, tool call and secret passes through here.
01
Verify identity → IdentityTuplebuilt Middleware attaches the verified principal to the request.
02
Resolve rolethe keystone Read from X-User-Role — a header the caller types. No identity method carries a role claim, so RBAC constrains a value its own subject supplies.
03
Authorize the toolbuilt Agent registry · role permissions · data policies on the arguments.
04
Control planebuilt Killswitch · circuit breaker · parameter policy · workflow limits · signed approval grants.
05
Capability at executionoptional Nonce-burned, resource-bound, ≤60s. Enforced by the client integration; the central tool route has no capability field.
06
Auditpartial Tamper-evident hash chain exists but is off by default. Provenance of the role is now recorded.
Protected · reachable only via Shield
The checkpoint holds only while these have no other route in.
The modelPrompts and replies screened both ways.
ToolsRBAC-gated; MCP gateway now runs the same guard set.
SecretsVault placeholders; materialised only at a bound destination.
EgressAllow-listed destinations.
Bearer tokensno binding No cnf, no DPoP, no x5t#S256. Steal the token, be the agent.

The three properties

PropertyQuestionStatusWhat is missing
Agent principalWho is this agent? built Claims are caller-supplied at mint and sealed by the signature — integrity-protected, not independently attested. oidc_sa has no subject allowlist.
DelegationOn whose behalf, and who authorized it? not enforced parent_agent_id is carried and read by nothing. No attenuation, no chain provenance. Token exchange converts an external token; it does not model an actor.
BindingIs the presenter the party this was issued to? absent Pure bearer. Keycloak can issue DPoP-bound tokens today, but Shield ignores cnf — binding is a two-party property and the verifier half is missing.

What closes each gap

WorkUnlocksSizeNotes
Verified identity reaches the decisionProperty 1 becomes load-bearing2–3 weeks Do this first. Until it lands, binding a token nobody checks and delegating an identity nobody reads change no outcome.
Role binding spec-agent-role-binding.mdRBAC stops trusting its own subject2–4 weeks Off → prefer → strict. The MCP gateway must be in scope or it is the bypass.
Token binding spec-token-binding.mdProperty 32–3 weeks Verify-only if Keycloak issues: thumbprint, proof verifier, replay claim. Roughly half the build.
Delegation attenuationProperty 24–6 weeks Parent-token presentation, subset check, chain provenance.
The one rule the whole picture depends on: the model, the tools, the secrets and the egress must be reachable only through Shield. A client that reaches a resource directly bypasses every box in the middle column — and no amount of identity work changes that.

Each status above was checked against the code rather than the documentation. Amber means the capability exists but is not on the decision path or is off by default — which, for a security control, is closer to red than to green.