The AI agent security community has been converging on a problem. A researcher recently ran an experiment — feeding a memory-retrieval framework 10 scenarios involving certificate operations: signing, issuing, revoking, delegating. The system retrieved the right memory 8 out of 10 times. It matched the external authorization gate 7 out of 10.
The conclusion: metadata per item isn't enough. You need a separate authorization gate over the proposed operation.
That conclusion is correct. But I want to show what that gate actually looks like when you build it — because the primitive already exists, and it's older than LLMs.
The problem is authorization, not retrieval
Most agent frameworks today invest in memory and observability. The agent can recall what it did before. You can see what tools it called. Logs, traces, dashboards.
What they don't have is a cryptographically enforced answer to the question: was this agent authorized to do this, before it did it?
Those are different problems. Retrieval tells you what the agent remembers about its permissions. Authorization tells you what it was actually granted — signed, tamper-proof, at dispatch time.
An agent that retrieves "I have revocation permissions" from memory and then revokes a certificate it shouldn't touch is not an authorization failure at the retrieval layer. It's an authorization failure at the gate layer — because there was no gate.
Certificates are that gate
A certificate is a signed declaration of what an entity is authorized to do. Issued once, verifiable offline in ~1ms, revocable instantly. We've used them for TLS, for IoT devices, for code signing. The same primitive works for agents.
The model is simple:
- Orchestrator issues a certificate at dispatch time
- The certificate carries the agent's identity and its exact scope in
meta - Every tool call goes through a gate that verifies the certificate offline
- On completion — or abort, or timeout — the orchestrator revokes it
// Orchestrator — dispatch
const { certificate, meta } = await pq.ca.issue({
subject: `agent_payment_processor_run_${runId}`,
publicKey: agentPublicKey,
expiresInSeconds: 3600,
meta: {
action_types: ["execute", "write"],
resource_scope: "payments:process",
resource_sensitivity: "high",
verification_required: false,
max_amount_usd: 1000,
environment: "production",
},
})
The certificate is signed with ML-DSA-65. Any attempt to modify meta after issuance invalidates the signature. The agent cannot promote its own scope.
The gate — offline, zero tokens, ~1ms
function verifyAgentScope(certificate, requiredScope) {
// Signature + expiry — offline, no API call
const { valid, error, cert } = pq.ca.verifyCert(certificate, rootCert)
if (!valid) throw new Error(`Gate: ${error}`)
const meta = cert.meta ?? {}
// Action type check
if (!meta.action_types?.includes(requiredScope.action)) {
throw new Error(
`Gate: action "${requiredScope.action}" not in scope [${meta.action_types}]`
)
}
// Sensitivity ceiling
const LEVELS = { low: 0, medium: 1, high: 2, critical: 3 }
if (LEVELS[requiredScope.sensitivity] > LEVELS[meta.resource_sensitivity]) {
throw new Error(`Gate: sensitivity ceiling exceeded`)
}
return meta
}
Every tool call, before execution:
async function processPendingPayments(certificate) {
verifyAgentScope(certificate, {
action: "execute",
sensitivity: "high",
})
// Gate passed — proceed
return await paymentService.processNext()
}
If the certificate doesn't authorize this action, the gate throws before any external call is made. Not after. Not in the logs.
Revocation closes the window
Short-lived certificates handle the happy path. Eager revocation handles everything else.
async function runAgent(task, scope) {
const { certificate, certId } = await dispatchAgent(task, scope)
try {
return await agent.run(task, certificate)
} finally {
// Always — success, exception, or timeout
await pq.ca.revokeCert(certId, "run complete").catch(() => {})
}
}
The finally block is not optional. If the agent is compromised mid-run, the orchestrator can call revokeCert() immediately — the next gate check on the compromised agent fails instantly.
This is what the memory-retrieval approach can't do. You can't retroactively un-retrieve a memory. You can revoke a certificate.
What this solves from the packet experiment
Going back to the experiment that opened this post — the 4 cases that didn't match the external gate:
Excessive delegation — a delegate_depth field in meta, decremented on each sub-agent issue. When it reaches 0, the gate blocks delegation. The agent cannot grant itself deeper delegation than it was given.
Issue without verification — verification_required: true in meta. The gate blocks execution until a human approves. The approval request carries the certificate ID, the scope, and the expiry — everything needed to make an informed decision.
Ambiguous batch — the gate runs once per operation, not once per batch. Each item in the batch hits the same gate. No ambiguity about whether the scope covers "some items" or "all items."
Revocation by project — resource_scope: "certs:own" vs "certs:*". The gate checks the scope string. An agent with certs:own cannot revoke a certificate it didn't issue, regardless of what it retrieves from memory.
In every case, the fix is the same: enforce at the gate, not at the retrieval layer.
The post-quantum angle
The signing infrastructure matters here. If you build this on RSA or ECDSA, you're building on algorithms that quantum computers will break. NIST finalized ML-DSA-65 (FIPS 204) in August 2024 — the certificate signatures in the examples above use that standard.
Building the authorization layer now means building it on algorithms that hold when the threat materializes. The migration cost later is significantly higher than doing it right today.
FIPSign is the API I built for this — post-quantum certificates, signing, and revocation as a service. The Private CA feature is what powers the agent authorization model above. JS/TS and Python SDKs available. The gate itself costs zero tokens — only issuance and revocation are charged.
The authorization gap in agent systems is a solvable problem with existing primitives. If you're working on agent security and want to discuss the architecture — especially the delegation chain problem — drop a comment.
Top comments (0)