Core Concepts
Five pillars work together: a passport says what an agent may do, a policy decides each action, approvals bring in humans, drift monitoring catches tools that change, and the evidence vault proves all of it afterwards.
Action Passports
An Action Passport is a short-lived, EdDSA-signed credential (a JWS) that scopes one delegation: this agent, acting for this user, toward this goal, may use these tools on these resources, within these limits. Think of it as a visa: it expires (typically in minutes), names exactly what it permits, and is single-use: each passport's jtiis claimed atomically on first use, so a stolen passport can't be replayed against a different request.
{
"tenant_id": "t_acme",
"agent_id": "support_agent",
"user_id": "user_456", // who delegated
"goal": "resolve_refund_request",
"allowed_tools": ["stripe.refund.create"],
"allowed_resources": ["stripe:charge:ch_123"],
"resource_constraints": { "max_amount": 10000, "currency": "usd" },
"risk_tier": "critical",
"approval_hash": null, // set after a human approves
"exp": 1718200000, "jti": "ap_..."
}Constraints are values, not just names: a passport with max_amount: 10000 blocks a $250 refund even if the tool itself is allowed. Verification covers signature, issuer, audience, tenant, expiry, revocation, replay, and constraint values, all server-side.
The deterministic policy engine
Policies are ordered JSON rules over the action's full context: tool, arguments, resource, passport claims, risk tier. The first matching rule wins. There is no model in the loop: the same input produces the same decision, every time, with the matched rules returned so you can see exactly why.
{
"when": [
{ "path": "args.amount", "operator": ">", "value": 10000 },
{ "path": "args.amount", "operator": "<=", "value": 50000 }
],
"decision": "require_approval",
"reason": "refund.medium_needs_approval"
}Start from a prebuilt policy pack (refunds, code deploys, outbound email, CRM writes, and more) and edit in the dashboard's Policy Manager. Every save is validated and versioned, and the engine pins decisions to a policy_hashso a passport issued under one policy version can't silently run under another.
Human approvals (HITL)
When policy returns require_approval, the action pauses and an approval request appears in the dashboard (and Slack, if connected). Reviewers approve, deny, modify, escalate, or request context. Transitions are guarded by a strict state machine, and every reviewer action is sealed into an immutable audit row with a content hash.
That hash closes the loop: a passport re-issued with the approval's approval_hash proves the human decision at the next preflight: verified against the ledger, bound to the same tenant and tool. A forged or borrowed hash is denied. Pending approvals expire automatically after your SLA window.
Manifest drift monitoring
"Approved yesterday" doesn't mean "safe today." Tool definitions (MCP manifests, OpenAPI operations) are canonicalized and hashed when you approve them. On every change ActPass classifies the drift semantically:
| Drift class | Example |
|---|---|
| Whitespace-only | Reformatting, no action needed |
| Read-to-write conversion | A "list invoices" tool gains a delete parameter |
| Authority expansion | Input schema accepts new sensitive fields |
| Origin / publisher change | The MCP server moved or changed hands |
| Financial authority expanded | Spend limits or payment scopes broadened |
Material drift flips the tool to require_tool_reapproval: in enforce mode it cannot run again until a human re-approves the new definition. This is your defense against rug-pulls and supply-chain swaps in agent tooling.
The Evidence Vault
Every decision, approval, execution, and credential binding is appended to a per-tenant hash chain: each event stores the canonical event JSON and a hash linking it to the previous event. The tables are append-only at the database level: even a DBA can't quietly rewrite history without breaking the chain.
Export bundles as JSON, Markdown, CSV, or SIEM-JSONL. Each export is Ed25519-signed, and GET /api/v1/evidence/verify recomputes the chain on demand so an auditor can independently confirm nothing was altered. Sensitive arguments are redacted before persistence according to your redaction mode.
Decisions at a glance
| Decision | Meaning |
|---|---|
allow | Proceed. Recorded with the matched rules. |
deny | Blocked. The reason_code says exactly why. |
require_approval | Paused for a human. Returns an approval_request_id to track. |
require_tool_reapproval | The tool drifted materially since approval; re-approve it first. |
warn | Would have been blocked under enforce; allowed in warn mode. |
reason_code (e.g. passport.replay_detected, refund.medium_needs_approval, approval.satisfied), stable identifiers you can alert on, branch on, and audit against.