Inbound
A message can carry untrusted content and injection attempts straight into the agent's reasoning.
Agent Mailbox Access Protocol
AMAP is a wire contract that lets sandboxed AI agents take part in message-based workflows through a trusted runtime that does not trust them. Press Next to walk through the ideas, watch each flow move through the real pieces, and then run it yourself.
The invariantThe runtime's authority does not depend on the agent or its connector behaving correctly. A request file cannot, on its own, grant permission to send, change the sending identity, or bypass policy.
The problem
It can be prompt-injected, buggy or compromised. Connecting it to a workflow does not make its decisions trustworthy, and message access brings three risks.
A message can carry untrusted content and injection attempts straight into the agent's reasoning.
A message can disclose information, or misuse an identity the agent should not control.
Anything the agent can read, it can leak. Credentials in its reach are credentials at risk.
What matters is not whether the agent can write a message. It is whether it can authorise one, choose the identity it goes out under, or step around policy.
The problem
what was delivered to it, from a place it cannot write.
a file saying what it would like to happen. Nothing more.
whether anything happens, to whom, and under which identity.
AMAP was designed for email workflows, where the runtime holds the mail credentials. This walkthrough uses amap-router-local, a reference runtime that carries no mail at all: no provider, no credentials, no network. It moves the same artifacts between agents on one machine, so what is left is exactly what the protocol asks of a runtime.
Isolation first
The protocol defines who may do what across a boundary. It does not create the boundary. Running each agent in isolation is the operational baseline that gives the protocol's guarantees meaning, and it is good practice even when nothing is malicious.
A compromised or confused agent damages its own workspace, not its neighbours' or the host's.
The runtime holds every credential and the sending identity. The connector holds none.
It runs as your user, so chmod would succeed. A read-only mount makes the kernel refuse the write: EROFS.
The runtime binds each sender to its own outbox, named by the host.
Isolation first
The delivery process restarts if it dies, and containers are rebuilt on a schedule, so drift and damage do not accumulate.
Which agents take part is a rule the operator writes and the host evaluates. Nothing inside a workspace can add itself.
Every check ends in pass, fail or unknown, and unknown is reported as a problem.
If your environment cannot provide one of these, the matching guarantee does not hold. It is not merely weaker. This walkthrough uses sandy for isolation because it provides every one of them, and shows each.
The pieces
The directory layout, message and verdict shapes as JSON Schema, and golden fixtures. Each side proves conformance against the fixtures, without the other.
Holds the graph of who may reach whom, binds replies, quarantines on first sight, writes the roster. Runs on the host with no network.
MCP tools to read (inbox, delegation) and to request (inbox-submit), plus inbox-delivery, which injects deliveries into the session. No credentials, no allowlist.
The sandbox per agent, the read-only mounts, the agent's name, the supervised process and the membership rule.
Turns one policy into sandy's feature manifest and the router's config, then verifies that what runs is what was declared.
The seam
Runtime and connector never call each other. They share directories, and the design rests on which side can write which one. Everything the agent reads, it cannot forge; everything it writes is only a request.
| Path | Written by | In the sandbox | Holds |
|---|---|---|---|
| instances/<name>/ | host | One agent's lanes. The name is the agent's identity. | |
| inbox/ | runtime | read-only | Mail delivered to this agent. |
| peer/ | runtime | read-only | Tasks from agents allowed to send them. |
| outbox/ | agent | writable | Inert requests; the runtime's verdicts come back in results/. |
| payload/ | operator | read-only | The relay, the connector, the MCP config and the agent's policy text. |
| roster/ | runtime | read-only | Who exists in the fleet. Never who may task whom. |
| router state | runtime | host only | Ledgers and first-sight markers. No agent can reach it. |
Two lanes
They are declared separately, and a pair of agents may sit on only one of them, so a sender can never pick which trust it is judged under.
inbox tool.On both lanes a reply goes back to the sender and nowhere else, even against the direction of a delegation edge.
Knowing it holds
The deployment's verify checks the manifest against the policy, the payload byte for byte, each agent's lanes and mounts, and the router's container and health. Every check ends one of three ways.
Established from the thing itself: the container's mount table, not the host directory; the router's own status, not a guess.
Known to be broken. Every failure names its remedy, and the tempting wrong fix.
Could not be answered, as when the router is stopped. Reported as a problem, never rounded to a pass.
The fleet roster keeps the same honesty. It lists who exists so agents can find addresses, and says it is neither permission nor a prediction. The verdict in outbox/results is the only report of what happened.
Run it yourself · with sandy
The runtime and the connector need no changes between environments; only the deployment knows where agents run. sandy supplies every property AMAP needs, and makes each one visible.
| AMAP needs | Why | sandy provides it as |
|---|---|---|
| A space per agent | Each agent's lanes must be its own. | One sandbox per workspace. |
| An identity it cannot forge | The runtime binds a sender to its outbox. | The slug, reported by sandy, never derived by the agent. |
| Read-only mounts | Inbound lanes, the connector and the agent's instructions must be out of reach. | Feature mounts: a write fails with EROFS. |
| A supervised process | Something must inject deliveries into the session, and restart if it dies. | The manifest's entry, run by sandy's relay supervisor. |
| Operator-owned membership | Joining is a security decision. | Include and exclude rules, evaluated by sandy at each launch. |
sandy is one choice, not a requirement. You can replace the sandy container with the isolation method of your choice, provided it implements at a minimum credential, network and file-system isolation: for example, NVIDIA's OpenShell, a runtime built for isolating autonomous agents, or the gVisor sandbox with an egress allowlist in defending-code-reference-harness. The runtime and the connector stay as they are. What you replace is this deployment, with one that supplies each row of the table above in your environment.
Run it yourself · 1 of 4
# install, then check the host is ready curl -fsSL https://raw.githubusercontent.com/rappdw/sandy/main/install.sh | bash curl -fsSL https://raw.githubusercontent.com/rappdw/sandy/main/doctor.sh | bash sandy --print-schema | jq '.schema_version' # must print 3 or 4
Clones the three repos into ~/amap, with the router and the connector at the commits this repo is tested against, installs the default policy (every agent may task every other), and builds and starts the router. Then make two workspaces and start an agent in each.
curl -fsSL https://raw.githubusercontent.com/proofpoint/amap-deploy-sandy/main/install.sh | bash mkdir -p ~/amap-demo/alpha ~/amap-demo/beta cd ~/amap-demo/alpha && sandy --start cd ~/amap-demo/beta && sandy --start
Clone amap-deploy-sandy, and put the router and the connector beside it at the commits it is tested against (pinned in siblings.json). Then install, start the router and start agents yourself, one screen at a time.
mkdir -p ~/amap && cd ~/amap git clone https://github.com/proofpoint/amap-deploy-sandy python3 amap-deploy-sandy/amap-siblings.py --apply
Run it yourself · 2 of 4
# sandy creates each sandbox at its first launch (3 of 4) mkdir -p ~/amap-demo/alpha ~/amap-demo/beta cd ~/amap/amap-deploy-sandy python3 amap-sandy.py install --apply # writes a working policy, the payload, and the router's config
"feature": {
"version": 1,
"groups": {},
"default_peers": [],
"peers": {},
"task_graph": "ALL",
"task_deny": [],
"fleet_domain": "sandy.<this host>.internal",
"container_recreate_interval_hours": 24
}[sender, recipient] to block even though task_graph grants them. None.<slug>@sandy.<this host>.internal. A name only; nothing touches a network.amap-sandy.py cadence prints.This works as written: every sandbox launched with claude is selected, and every one may task every other. To narrow it, edit ~/.sandy/features/amap/feature.json in place and run install --apply again. A glob in its top-level sandboxes.exclude keeps a sandbox out, and explicit task_graph edges replace "ALL". Nothing is delivered until you start the router in the next step: that is the approval.
Run it yourself · 3 of 4
# the router needs no agent running yet cd ~/amap/amap-router-local docker/build.sh docker/run.sh --config ~/.sandy/features/amap/router.json \ --detach # then agents, in any order, whenever you need them cd ~/amap-demo/alpha && sandy --start cd ~/amap-demo/beta && sandy --start
cd ~/amap/amap-deploy-sandy python3 amap-sandy.py list # both: selected python3 amap-sandy.py verify # exit 0 before going on
Starting the router is the approval: its first poll of each agent is first sight, and anything staged before it is never delivered. The router picks up each agent on its next poll after the agent launches, so agents can delegate to each other as they come online. It runs with no network and re-reads its config every poll, so later policy changes need only install --apply.
Run it yourself · 4 of 4
cd ~/amap-demo/alpha && sandy --attach # terminal 1 cd ~/amap-demo/beta && sandy --attach # terminal 2: watch it arrive
Ask alpha, in plain words:
Ask beta to list the files in its workspace and report back.
Its system prompt already tells it where the fleet roster is, how an address is formed, and that inbox-submit is how to ask.
You have just watched the first flow for real. The runbook then breaks each guarantee on purpose: a write to the payload fails with EROFS, a task that was never granted is held, and a stopped router makes verify say UNKNOWN.
amap-deploy-sandy · Apache License 2.0 · AMAP is a draft contract; the spec repository is normative.
Watch it work
Scroll the diagram sideways to see beta →