Chain-Based Authentication for AI Agents
If you run an API that receives requests from AI agents, here is the entire integration:
import requests
def verify_d3cipher(request):
auth = request.headers.get("Authorization", "")
if not auth.startswith("D3cipher "):
return None # Not a D3cipher request — handle normally
agent_id, state_hash = auth[len("D3cipher "):].split(":", 1)
resp = requests.get(
f"https://api.d3cipher.ai/v1/verify/{agent_id}",
params={"hash": state_hash},
)
if resp.json().get("status") == "current":
return None # Agent verified — proceed
else:
return {"error": "Agent not verified"}, 401
That is the complete counterparty integration. Parse one header. Make one GET call. Check one word. No SDK. No shared secret. No key exchange. No certificate.
For that one call, you get: real-time standing verification (not "was this authorized
an hour ago" but "is this authorized right now"), instant freeze propagation
(the operator presses a button, your next verify returns frozen),
automatic credential rotation (no key rotation schedule — the chain handles it),
and an audit trail you can reference later if there is a dispute.
The Verify Playground lets you decode D3cipher credentials and call the verification endpoint against real agents. A reference counterparty is running on Render — 120 lines of Python, zero secrets, one environment variable.
This is the most important design decision in the protocol: the agent does not know the credential exists. The agent does not obtain it, store it, or present it. The infrastructure handles everything.
The agent sends a normal API request to the LockStock gateway. The gateway stamps the
request (advancing a cryptographic hash chain), gets back a one-time credential, and
attaches it to the outgoing request as an Authorization: D3cipher header.
The agent sees a normal API response. It has no idea authentication happened.
This is the same model as OAuth/JWT for agents today — the orchestrator obtains the token and injects it into the environment. The agent carries it blindly. The difference: OAuth tokens are static and obtained at startup. LockStock credentials are dynamic and generated on every action. Both are invisible to the agent. One is alive. One is dead on arrival.
Why this matters: Adoption does not require modifying agents. No SDK in the agent. No code changes. No credential management. Point the agent at the gateway instead of directly at the provider. The audit trail, the verification, and the encryption happen transparently. Any agent that already uses a gateway or proxy pattern — which is most production agent deployments — can be retrofitted with one configuration change.
An AI agent calls your API. How do you know it's authorized right now?
Today's answer is a static credential. An API key that lives until someone rotates it. An OAuth token valid for an hour regardless of what the agent does in that hour. A JWT whose claims are frozen at minting time. If the agent is compromised at minute 3, the token is still valid at minute 59.
These mechanisms were designed for humans delegating permissions to web applications. They answer the question "was this request allowed at some point in the past?" They do not answer the question that matters for autonomous AI agents: "Is this agent in good standing right now, at this exact moment?"
The gap is not theoretical. An enterprise running 50 agents across 12 departments calling 30 external APIs has no mechanism to answer "which agents are active and what are they authorized to do right now?" with any credential system that has a time-to-live window. Freezing a compromised agent means waiting for tokens to expire, manually rotating keys across every integration, and hoping nothing slipped through in the propagation delay.
LockStock maintains a cryptographic hash chain for every registered AI agent. Each action the agent takes — a chat completion, a tool call, a file write — advances the chain by one position:
state_hash = SHA-256(previous_hash | matrix_elements | payload_hash)
The resulting state_hash has three properties that make it fundamentally
different from any issued credential:
It has never existed before. The hash depends on every prior action in the chain. Two different histories produce two different hashes. The credential encodes the agent's entire provenance.
It will never be valid again. The next action produces a new hash. The old hash immediately becomes "stale." There is no TTL, no expiry window, no grace period. The credential dies the instant the chain advances.
It was not issued. OAuth mints tokens. Certificate authorities sign certificates. LockStock does neither. The hash is a mathematical consequence of the chain advancing. LockStock witnesses the state and confirms it — it is a notary, not an issuer. There is no signing key to steal. There is no token database to breach.
BEGIN → advance_chain → INSERT audit_log → COMMIT.
The credential cannot exist without the audit record. The audit record cannot exist
without the credential. They are created in a single database transaction. This is
what "atomic" means — not "small," but "indivisible."
Any service can verify a LockStock agent's identity. The protocol requires one HTTP call.
Step 1 — Stamp. The gateway stamps an action through the LockStock
server. The server advances the chain and returns the new state_hash. Any
stamp works — there is a dedicated Authenticate action type for
credentials whose sole purpose is authentication, but the hash from a
ChatCompletion or Shell action is equally valid.
Step 2 — Present. The gateway attaches the credential to the outgoing
request: Authorization: D3cipher {agent_id}:{state_hash}. The agent
does not see this header. The gateway adds it after the agent's request has been received.
Step 3 — Verify. The counterparty makes one GET request to the LockStock verification endpoint. No SDK. No shared secret. No key exchange.
GET https://api.d3cipher.ai/v1/verify/{agent_id}?hash={state_hash}
The response is one word:
| Status | Meaning | Action |
|---|---|---|
current | Hash matches the agent's latest chain head | Accept |
stale | Agent has advanced past this hash | Reject — ask agent to re-authenticate |
frozen | Agent's chain is frozen (kill switch active) | Reject — operator intervention required |
revoked | Agent has been permanently revoked | Reject — this agent is dead |
unknown | Agent ID not found | Reject — not a registered agent |
The simplest possible integration — if status == "current" —
automatically rejects frozen, revoked, stale, and unknown agents. The secure behavior
is the default behavior.
The instant an agent takes its next action, the previous state_hash becomes
stale. Any third party holding the old credential and calling verify gets
"stale". This is not a revocation — it is the normal lifecycle.
Credentials are not long-lived tokens that occasionally get rotated. They are
single-use proofs that expire by design.
| Mechanism | Valid For | Invalidation | Compromise Window |
|---|---|---|---|
| API Key | Until manually rotated | Manual | Hours to days (human notices, rotates) |
| OAuth Token | 1 hour (typical) | Expiry | Up to 1 hour |
| JWT | Minutes to hours | Expiry (no revocation) | Full TTL window |
| Atomic Credential | Until next action | Automatic | Zero — next action kills it |
If an operator detects anomalous behavior and freezes the agent, the credential dies
immediately. Not "after the token expires." Not "after someone rotates the key." The
next verification call, from any counterparty anywhere, returns "frozen".
Propagation delay: zero.
LockStock is not a point solution for one authentication pattern. The same mechanism — one hash in, one bit out — covers every trust relationship in an AI agent deployment:
| Relationship | How It Works |
|---|---|
| Agent → API Provider | Gateway holds the API key from the operator's vault. Agent sends to localhost. Gateway attaches auth, forwards, returns response. Agent never sees the provider key. |
| Agent → Agent (MLS) | MLS sidecar stamps, puts state_hash in the MLS BasicCredential. Both agents verify each other through LockStock. Neither trusts the other without chain proof. |
| Agent → MCP Server | SSE connection presents a chain credential on connect. Middleware verifies once, stores identity for the session. Multi-tenant: one wallet serves N agents. |
| Agent → Counterparty | Gateway stamps Authenticate, attaches D3cipher header, forwards. Counterparty calls /v1/verify. The agent never knows authentication happened. |
| Agent → Workroom | Every broadcast carries sender_chain_hash. Group membership, intent announcements, sync requests — all chain-linked. |
| Container → Server | The gateway is itself an agent on the chain. It authenticates to the server using its own chain state. Infrastructure auth, same protocol. |
| Cross-Organization | Org A's agent talks to Org B's agent. Both present chain credentials. Both verify. MLS (RFC 9420) encrypts the channel. No shared secrets between organizations. |
| Operator → Kill Switch | Freeze an agent on the dashboard. Next /v1/verify returns frozen. Not "wait for the token to expire" — dead now. |
| Auditor → Chain | Download the chain, recompute every hash offline. No server access needed. No trust needed. The chain is the proof. |
All nine relationships use the same verification endpoint: GET /v1/verify/{agent_id}?hash={state_hash}.
One protocol. One endpoint. One bit.
The verify endpoint returns progressively more information based on the
detail parameter.
Answers: "Is this credential current?" No authentication required.
GET /v1/verify/{agent_id}?hash={hash}
{ "status": "current", "agent_id": "agent_0d20..." }
Answers: "Who is this agent and when did it last act?"
GET /v1/verify/{agent_id}?hash={hash}&detail=identity
{ "status": "current", "agent_id": "agent_0d20...",
"last_server_timestamp": 1774425600,
"created_at": "2026-03-01T03:00:13Z",
"generator_version": 2, "chain_frozen": false }
Answers: "Should I trust this agent?" Requires API key (gateway or admin scope).
GET /v1/verify/{agent_id}?hash={hash}&detail=trust
Authorization: Bearer lsk_gateway_...
{ "status": "current", ...,
"hardware_fingerprint": "a1b2c3d4...",
"enforce_hardware_bind": true,
"policy_rules": {"allowed_models": ["claude-sonnet-4-20250514", "gpt-4o"]},
"token_budget": 100000, "tokens_consumed": 47832 }
The trust tier exposes governance metadata: what models the agent is allowed to use, how much of its token budget remains, whether it is hardware-bound. This is the tier an enterprise compliance system would query when deciding whether to engage an agent from another organization.
Authentication is one layer of a five-layer verification stack. Each layer answers a different question. They share the chain but not each other's inputs.
| Layer | Question | Mechanism |
|---|---|---|
| 1. Ordering | Did this happen in this sequence? | SHA-256 hash chain — each hash depends on the previous |
| 2. Task Integrity | Were the right types of actions performed? | Mathematical state evolution independent of content |
| 3. Content Binding | Was this the exact content at this position? | Deterministic canonicalization + cryptographic seal per payload |
| 4. Confidentiality | Can the server read the content? | AES-256-GCM envelope encryption — Account Key held by operator, not server |
| 5. Cross-Org E2EE | Can the other organization read it? | MLS (RFC 9420) with per-agent Ed25519 keys derived via HKDF |
The atomic credential emerges from Layer 1. The state_hash is the chain
head after each stamp. Layers 2–5 provide additional guarantees — task
integrity, content sealing, encryption, and cross-organization privacy — but the
credential itself requires only Layer 1.
When agents from different organizations communicate, the LockStock server brokers the
connection but cannot read the content. Layer 5 uses the Messaging Layer Security
protocol (RFC 9420) with cipher suite
MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519.
The state_hash serves as the MLS BasicCredential. When Agent A initiates
a session with Agent B, B's sidecar verifies A's state_hash via
/v1/verify before joining the group. If A is frozen or revoked, the session
is denied. The chain and the encryption layer are bound together — there is no way
to establish an encrypted session without a valid chain credential.
Cross-org E2E testing: two separate customers, two separate MLS seeds, two gateways, bidirectional communication through the Render broker. 19/19 positive tests and 15/15 negative-space tests (ciphertext from one organization is unreadable by the other's agents).
LockStock is a chain witness, not a credential issuer. The distinction matters.
A certificate authority holds signing keys. If compromised, every outstanding certificate is compromised. An OAuth provider stores token databases. If breached, every active session is compromised.
LockStock holds chain state: a hash and a sequence number per agent. There are no signing keys. There are no token databases. There are no shared secrets. The chain evolves deterministically from the genesis block. There is nothing to steal from the server that would allow credential forgery.
If LockStock is compromised: An attacker with write access to
agent_registry.last_hash could make a stale credential appear current (or
vice versa). They could not forge chain history — modifying any historical row
breaks the hash chain linkage. They could not decrypt content — Layer 4 keys are
derived from the Account Key, which LockStock does not hold. The blast radius of a
compromise is real-time verification integrity, not historical audit integrity or content
confidentiality.
If LockStock is unreachable: Verification fails. This is fail-closed by design. The counterparty cannot verify the agent. The correct behavior is to reject the request. This is a liveness dependency, not a security weakness — the same dependency every TLS-based system has on certificate validation.
Four questions that matter for autonomous AI agents. No existing authentication protocol answers any of them.
| Question | OAuth / JWT / API Key | LockStock |
|---|---|---|
| Is this agent compromised right now? | Unknown until token expires | /v1/verify → frozen instantly |
| Has this agent been frozen by its operator? | No concept of freeze | Freeze propagates to every counterparty with zero delay |
| Can this agent prove it performed specific actions in order? | No — credentials authenticate the caller, not the history | The state_hash encodes the agent's entire action history |
| Can two agents prove they interacted without a shared identity provider? | No — requires a common OAuth/OIDC provider | MLS BasicCredential = state_hash — mutual verification via chain |
Because the credential and the audit record are the same atomic operation, the authentication system IS the compliance system. There is no separate logging pipeline to keep in sync. The chain serves both purposes simultaneously.
| Framework | Requirement | How LockStock Satisfies It |
|---|---|---|
| SOC 2 CC7.2 | Monitor system components for anomalies | Every agent action is a chain entry. Anomalies break the hash linkage. 6-pass Rust verification kernel. |
| HIPAA §164.312(b) | Audit controls for information systems | Chain provides immutable audit trail. Envelope encryption (Layer 4) ensures the server cannot read PHI. |
| PCI-DSS 10.5 | Secure audit trails so they cannot be altered | SHA-256 chain — altering any row invalidates all subsequent hashes. Mathematical guarantee, not access control. |
The protocol described in this paper is not a design document. It is deployed and verified against production infrastructure.
| Component | Status |
|---|---|
Verification endpoint (/v1/verify) | Live on Render, handling production traffic |
| Gateway credential auth | Default-on since v3.4.2 |
| Gateway outbound D3cipher injection (§14a) | Deployed v4.9.0 — agent-oblivious counterparty auth validated E2E |
| Reference counterparty | Live on Render — 120 lines Python, zero secrets |
| Verify playground | Live at d3cipher.ai/verify.html |
| Three-step exchange E2E | 8/8 passed against production chain |
| A2A bilateral handshake | 19/19 passed — mutual non-repudiation verified |
| Frozen agent propagation | 14/14 passed — freeze → verify returns frozen instantly |
| Cross-org MLS E2EE | 19/19 positive + 15/15 negative-space tests |
| Chain verification kernel | 6-pass Rust implementation, 182 unit tests |
| MCP wallet (multi-tenant AAAA) | 14 tools, per-connection chain auth, 5 agents in production |
| Nine trust relationships | All implemented; agent→counterparty validated E2E March 2026 |
LockStock agents publish Agent Cards with the D3cipher security scheme, compatible
with Google's A2A protocol specification. The machine-readable protocol spec is
served at GET /extensions/v1 on the LockStock API — any A2A-aware
client that reads the extension URI can discover the verification protocol automatically.