Agent Mandate Protocol
/install agent-mandate-protocol
A-MAP Skill
A-MAP (Agent Mandate Protocol) gives AI agents cryptographic proof of what they are authorized to do — and lets services verify that proof before acting.
Install
npm install @agentmandateprotocol/core
Part 1: Verify — Authenticate an Incoming Agent Request
Use this when another agent sends you a request and you need to confirm it was authorized by a human before acting on it.
When to verify
- A request includes
X-AMAP-Mandate,X-AMAP-Signature,X-AMAP-Timestamp,X-AMAP-Nonce, orX-AMAP-Agent-DIDheaders - You need to detect agent impersonation or replay attacks
- You need cryptographic proof of who authorized this agent to act
What you need
- The five A-MAP headers from the incoming request
- The expected permission the caller claims to have
- The public keys of all agents in the chain (distribute out-of-band)
How to verify
import { amap, InMemoryNonceStore, LocalKeyResolver } from '@agentmandateprotocol/core'
const keyResolver = new LocalKeyResolver(new Map([
['did:amap:sender-agent:1.0:abc', process.env.SENDER_PUBKEY],
]))
// Use Redis or Cloudflare KV in production — see Guardrails
const nonceStore = new InMemoryNonceStore()
try {
const result = await amap.verifyRequest({
headers: {
'X-AMAP-Agent-DID': request.headers['x-amap-agent-did'],
'X-AMAP-Mandate': request.headers['x-amap-mandate'],
'X-AMAP-Signature': request.headers['x-amap-signature'],
'X-AMAP-Timestamp': request.headers['x-amap-timestamp'],
'X-AMAP-Nonce': request.headers['x-amap-nonce'],
},
method: request.method,
path: request.path,
body: request.body,
expectedPermission: 'book_flight',
keyResolver,
nonceStore,
})
// Safe to proceed
console.log('Authorized by:', result.principal)
console.log('Effective limits:', result.effectiveConstraints)
console.log('Audit ID:', result.auditId) // always log this
} catch (err) {
// A-MAP throws on any failure — never returns { valid: false }
console.error(`Authorization failed: [${err.code}] ${err.message}`)
// Reject the request
}
Interpreting the result
On success (no error thrown):
result.principal— the human who originally authorized this chainresult.effectiveConstraints— merged limits across all hops (e.g.maxSpend: 347)result.chain— array of verified links, one per hopresult.auditId— UUID for this verification event — log it for audit trail
On failure (AmapError thrown):
err.code— specific error code (seereferences/error-codes.md)err.hop— which link in the chain failed (0 = root), if applicable
Verify guardrails
- Never proceed with an action if
verifyRequest()throws - Always log
result.auditIdfor audit trail - The default
InMemoryNonceStoredoes not work behind a load balancer — use a shared store (Redis, Cloudflare KV) in multi-instance deployments - Always check
result.effectiveConstraintsbefore consequential actions (e.g. checkmaxSpendbefore charging a card) - An
AmapErrormeans the agent was not authorized, the request is a replay, the chain was forged, or the identity is being spoofed — always reject
Part 2: Sign — Authenticate an Outgoing Request
Use this before calling any A-MAP-protected service to attach cryptographic proof that a human authorized your action.
When to sign
- You are calling a service that uses A-MAP to verify agents
- You need to prove a human authorized your action
- You are forwarding a delegation chain to a downstream service
Prerequisites
- A mandate chain (from
amap.issue()oramap.delegate()) - Your agent's Ed25519 private key in
AMAP_PRIVATE_KEY
How to sign
import { amap } from '@agentmandateprotocol/core'
const headers = amap.signRequest({
mandateChain: myMandateChain,
method: 'POST',
path: '/api/book-flight',
body: JSON.stringify(requestBody), // omit if no body
privateKey: process.env.AMAP_PRIVATE_KEY,
})
await fetch('https://api.example.com/book-flight', {
method: 'POST',
headers: { 'Content-Type': 'application/json', ...headers },
body: JSON.stringify(requestBody),
})
amap.signRequest() returns five headers ready to spread:
| Header | Content |
|---|---|
X-AMAP-Agent-DID |
DID of the signing agent |
X-AMAP-Mandate |
Base64url-encoded DelegationToken chain |
X-AMAP-Signature |
Ed25519 signature over canonical payload |
X-AMAP-Timestamp |
ISO8601 UTC timestamp |
X-AMAP-Nonce |
128-bit random hex string (single-use) |
See references/signed-request-format.md for the full payload schema.
Sign guardrails
- Never hardcode
AMAP_PRIVATE_KEY— always use an environment variable - Never log the private key
- A fresh nonce is generated on every
signRequest()call — never reuse headers - Check mandate expiry before signing — an expired mandate produces headers
the receiver will reject with
TOKEN_EXPIRED
Part 3: Delegate — Authorize a Sub-Agent
Use this when spawning a sub-agent that needs its own cryptographic proof of authorization to call external services on your behalf.
When to delegate
- You are spawning a sub-agent to handle part of a task
- A sub-agent needs to call A-MAP-protected services directly
- You want to limit what the sub-agent can do to a safe subset of your permissions
How to delegate
import { amap } from '@agentmandateprotocol/core'
// myToken = DelegationToken you received; myChain = full chain including myToken
let childToken
try {
childToken = await amap.delegate({
parentToken: myToken,
parentChain: myChain,
delegate: 'did:amap:sub-agent:1.0:xyz',
permissions: ['charge_card'], // must be subset of myToken.permissions
constraints: { maxSpend: 347 }, // can only tighten, never relax
expiresIn: '15m', // cannot exceed parent's remaining TTL
privateKey: process.env.AMAP_PRIVATE_KEY,
})
} catch (err) {
// AmapError thrown BEFORE signing if an invariant is violated:
// PERMISSION_INFLATION — permissions not in parent
// CONSTRAINT_RELAXATION — constraint looser than parent
// EXPIRY_VIOLATION — TTL exceeds parent's remaining time
throw err
}
// Pass the full chain to the sub-agent — not just the child token
const subAgentChain = [...myChain, childToken]
The sub-agent uses amap.signRequest({ mandateChain: subAgentChain, ... }) to
attach this chain to its outgoing requests.
Expiry strategy
| Task type | Recommended TTL |
|---|---|
| Single API call | 15s |
| One-off task | 60s |
| Short workflow | 5m |
| Extended session | Match parent — SDK enforces the ceiling |
The three rules (enforced by SDK — see references/delegation-invariants.md)
- Permissions can only narrow — you cannot grant what you do not have
- Constraints can only tighten — you cannot relax a limit set above you
- Expiry can only shorten — sub-agent tokens expire before yours
Delegate guardrails
- Always pass
subAgentChain(full chain), not just the new token - Set the shortest possible
expiresInfor sub-agents - Log
childToken.tokenIdfor audit trail - Never share your
AMAP_PRIVATE_KEY— each agent has its own keypair
- Make sure OpenClaw is installed (local or Docker)
- Run the install command in chat:
/install agent-mandate-protocol - After installation, invoke the skill by name or use
/agent-mandate-protocol - Provide required inputs per the skill's parameter spec and get structured output
What is Agent Mandate Protocol?
Use A-MAP (Agent Mandate Protocol) to verify incoming agent requests, sign outgoing requests, and delegate permissions to sub-agents. Covers the full cryptog... It is an AI Agent Skill for Claude Code / OpenClaw, with 175 downloads so far.
How do I install Agent Mandate Protocol?
Run "/install agent-mandate-protocol" in the OpenClaw or Claude Code chat to install it in one step — no extra setup required.
Is Agent Mandate Protocol free?
Yes, Agent Mandate Protocol is completely free, licensed under MIT-0. You can download, install and use it at no cost.
Which platforms does Agent Mandate Protocol support?
Agent Mandate Protocol is cross-platform and runs anywhere OpenClaw / Claude Code is available (cross-platform).
Who created Agent Mandate Protocol?
It is built and maintained by Shu-Yu (Jimmy) Lee (@jimmyshuyulee); the current version is v1.0.1.