
Cryptographically signed delegation receipts for AI agents. Define exactly what an AI can and can't do — signed, verifiable, tamper-proof.
AuthProof is an audit and attribution layer for AI agents. It lets you prove what a user authorized before the agent acted, and it records what the agent actually did against that authorization.
The mechanism is simple. The user signs a delegation receipt with their own private key. The receipt carries scope, boundaries, operator instructions (hashed), and a time window. A pre-execution verifier checks each proposed action against the receipt, and an append-only action log records what ran. The result is evidence: what was authorized, diffed against what happened.
This is not a security control against a malicious operator. The verifier runs in the same process as the agent, and a dishonest operator can simply not call it. No cryptographic property can fix that from inside the same runtime. AuthProof proves authorization for deployments that run the gate, and it gives auditors, regulators, and users a record they can check after the fact.
What makes it different:
The user is the signing authority. The authorization object is signed with the user's private key, not written into an operator-managed policy. The signed scope and program hash in the receipt always win over operator log metadata. If operator metadata conflicts with the signed receipt, the gate denies the action instead of following the metadata.
Two-phase model-state commitment. The model is measured at authorization time and re-measured before execution. These measurements are software-computed and self-attested, not hardware-rooted. They are tamper-evidence for honest deployments, not proof against a hostile host.
Provider update versus malicious substitution, distinguished. Model-state changes classify into legitimate provider updates (PROVIDER_UPDATE_REQUIRES_REAUTH) and unauthorized swaps (MALICIOUS_MODEL_SUBSTITUTION). Each produces a machine-readable denial reason code identifying which components changed.
The gate that runs before any agent action executes.
PreExecutionVerifier.check() verifies each proposed action against the signed delegation receipt. Route every action through it in your agent's process, before the action runs. The verifier signs its own allow/deny decisions into an audit log, so every result is part of the record.
One hard truth to build around: the verifier is same-process. It verifies proof; it cannot stop an operator who refuses to call it. Use it where you want evidence, not where you need enforcement against the operator running the code.
Most agent authorization today is the operator’s word: a policy the operator wrote, enforced by code the operator controls. When something goes wrong, there is no record of what the user actually authorized, only the operator’s reconstruction of it. AuthProof flips that. The user’s signed receipt predates execution, and the action log records what ran against it. The question “did the user authorize this?” becomes checkable instead of arguable.
import { PreExecutionVerifier, DelegationLog } from 'authproof-sdk/pre-execution-verifier'
import { RevocationRegistry } from 'authproof-sdk'
// 1. Set up the gate
const delegationLog = new DelegationLog()
const revocationRegistry = new RevocationRegistry()
await revocationRegistry.init({ privateKey, publicJwk })
const verifier = new PreExecutionVerifier({ delegationLog, revocationRegistry })
await verifier.init({ privateKey: verifierKey, publicJwk: verifierPub })
// 2. Register your delegation receipt
delegationLog.add(receiptHash, receipt)
// 3. Gate every action â before the agent runs
const result = await verifier.check({
receiptHash,
action: { operation: 'read', resource: 'calendar' },
operatorInstructions: 'Summarize meetings. Stay within scope.',
programHash, // optional: prevents code substitution attacks
})
if (!result.allowed) {
throw new Error(`Blocked: ${result.blockedReason}`)
}
// Agent runtime only reaches here after all thirteen checks pass
| # | Check | Blocks when |
|---|---|---|
| 1 | Receipt signature | ECDSA P-256 signature invalid or receipt tampered |
| 2 | Revocation | Receipt has been revoked via RevocationRegistry (cascade: a revoked ancestor denies its descendants) |
| 3 | Time window | Receipt expired or not yet valid (log timestamp oracle, not client clock) |
| 4 | Scope | Action not in ScopeSchema.allowedActions; without a structured schema, keyword matching is advisory-only and never gates |
| 5 | Operator instructions | Provided instructions don't match the hash locked into the receipt at issuance; skipped with a warning when the caller omits them |
| 6 | Program hash | Provided programHash doesn't match the committed executes hash (optional) |
| 7 | Session risk | Trust/risk thresholds exceeded (optional) |
| 8 | Tool schema integrity | toolSchemaHash doesn't match the receipt (optional) |
| 9 | Model state | Model drift detected (optional; software-computed, self-attested measurement) |
| 10 | Tool output hash binding | Output hash doesn't match the receipt (optional) |
| 11 | Instruction provenance | Instruction source not in the receipt's trustedSources (optional) |
| 14 | Parent scope containment | Sub-receipt exceeds the parent's scope (sub-receipts only); revoking any ancestor denies the child at check time |
| 15 | Authority state drift | authorityStateCommitment drifted since issuance (optional) |
Slots 12 and 13 were never implemented; the numbering gap is historical. The gate stops at the first failed check. One-time-use consumption runs last, after every check passes, so a consumption slot is never burned on a receipt that would be denied for another reason.
Every check result, pass or fail, is automatically logged to an immutable ActionLog signed with the verifier's own key.
Signed data wins. The signed receipt's scope and program hash always win over operator log metadata. If unsigned DelegationLog metadata disagrees with the signed receipt, the gate denies the action (SCOPE_METADATA_MISMATCH / PROGRAM_METADATA_MISMATCH). Log metadata is a fallback only when the receipt carries no structured scope or signed executes value.
Guided delegation is guided by observation. ScopeDiscovery.guided() and AuthProofClient.delegateGuided() run observe → generateScope → approve → finalize in one call, and the full observed draft scope is auto-approved with no human review. For a genuine human-authorization moment, pass the reviewDraft hook (approve, modify, or decline the draft before issuance) or run the steps separately with an explicit approve().
Drop-in wrappers for common frameworks. Each wrapper gates every call through PreExecutionVerifier before the wrapped code executes.
invoke() method// LangChain
import { authproofMiddleware } from 'authproof-sdk/middleware/langchain'
const guardedAgent = authproofMiddleware(agent, { receiptHash, verifier })
// Express
import { authproofMiddleware } from 'authproof-sdk/middleware/express'
app.use(authproofMiddleware({ verifier, getReceiptHash: (req) => req.headers['x-receipt-hash'] }))
// Any function
import { guardFunction } from 'authproof-sdk/middleware/generic'
const guardedExecute = guardFunction(executeAction, { receiptHash, verifier, action })
Read this before deciding what AuthProof proves for you.