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, not an ID card — 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.