Do not share a filesystem. Share objects by reference. AFP lets an agent put a file, name it, prove what it is, and hand it to another agent or a person on any host, with no mounted folders.
Draft specification. Companion to AMP (messages), AAP (UI actions) and AID (identity).
THE PROBLEM
| Today | What goes wrong |
|---|---|
| Mounted network folders (SMB, NFS) | macOS drops the mount. The agent hangs or errors on a path that vanished. |
| Attach the file to a message | Fine for one recipient. Capped at 25 MB, expires with the message, nothing to browse later. |
| A web file server on one host | People can use it. Agents on other hosts cannot. |
| Per-vendor cloud storage | Each runtime invents its own way, and nothing interoperates. |
THE IDEA
Nothing is mounted, so nothing can drop. The store is the truth. A message that says "a file arrived" is only a hint.
agent A: afp-put.sh report.pdf --space artifacts -> afp://artifacts/2026/10/report.pdf (sha256:3b2c...) agent A: sends an AMP message that carries the reference, not the bytes agent B: afp-get.sh afp://artifacts/2026/10/report.pdf -> verified against the digest human: opens the same file from the agent UI, or from any S3 client
A named place with one backend and one access scope, such as shared or artifacts. A host configures its spaces.
An object is one file at a path. Its reference, afp://space/path, travels with a SHA-256 digest and a size.
A small JSON file next to each object: digest, size, content type, owner, created, optional expiry, and the scan result.
A backend declares what it can do (link, expire, versions, offline, ui). Callers check before relying on one.
Not in v0.1: merging concurrent edits, end-to-end encryption, quotas, cross-organization federation.
OPERATIONS
| Operation | What it does | Reference client |
|---|---|---|
| put | Computes the SHA-256, uploads, reads the digest back from the store, writes the manifest. Reports stored: true only after that check. Never falls back silently to a local copy. An existing path is not overwritten unless force is set; otherwise the code is exists. | afp-put.sh <file> [--space NAME] [--path PATH] [--ttl TTL] [--content-type TYPE] [--force] |
| get | Downloads and compares the digest (from the reference object, else the manifest). A mismatch deletes the partial file and fails with digest_mismatch. A reference with neither a digest nor a manifest cannot be verified and also returns digest_mismatch. A suspicious or rejected scan blocks the fetch with scan_blocked. Without credentials for the space, the url in a reference object may be used; the digest check still applies. | afp-get.sh <ref> [--digest SHA] [--dest PATH] [--force] |
| ls | Lists objects in a space, optionally by prefix, from their manifests (default limit 100). Returns truncated: true when more objects matched than the limit returned. | afp-ls.sh [--space NAME] [--prefix PREFIX] [--limit N] |
| link | Returns the full reference object with a time-limited download url (default 1 hour, maximum 7 days). Needs the link capability, otherwise unsupported. Needs the object's manifest, because the reference carries its digest; without one the code is not_found. | afp-link.sh <ref> [--ttl TTL] |
| rm | Deletes an object and its manifest. By default only the owner or a host administrator may delete. Returns ok: true only when the store confirms the deletion. | afp-rm.sh <ref> [--admin] |
| capabilities | Returns what a space's backend can do, so an agent does not promise a link where none can be made. | afp-capabilities.sh [--space NAME] [--probe] |
All output is JSON. A failure returns { "ok": false, "error": { "code": "...", "message": "..." } }, and a command-line implementation exits non-zero. An unreachable store returns unreachable and is never hidden.
Error codes: exists, not_found, digest_mismatch, scan_blocked, unsupported, unreachable, invalid_path, invalid_space, forbidden, too_large, usage. usage means missing or malformed arguments.
BACKENDS
v0.1 defines one profile, s3. Garage is the reference backend: a small, self-hosted store with a single-node mode and optional replication. Nothing in AFP requires it.
| Topology | Typical use | Trade-offs |
|---|---|---|
| Local only: Garage on one host, reached over a LAN or a private mesh | A team's fleet on its own network | No cost, no data leaves the network. One host is a single point of failure until replicated. |
| Self-hosted in the cloud: Garage on a VPS or cloud instance behind a TLS reverse proxy | Agents and people in several places, links that must work from outside | You run and patch it. Reachable from anywhere, so the network is no longer the trust boundary and access keys matter more. |
| Hosted S3-compatible service | No server to run | Data leaves your network. Provider terms, egress and pricing apply. Capabilities vary and must be declared. |
| Multi-node Garage | Surviving a host failure | More to operate. Needs a replication factor above 1. |
A host may configure several spaces on different topologies, for example shared on a local store and public on a cloud store. Garage supports presigned URLs, multipart upload, CORS and lifecycle expiry. It does not support object versioning, S3 ACLs, bucket policies or quotas, and AFP does not need them.
AMP INTEGRATION
A message's payload.attachments entry has a storage field. An entry with no field is a provider attachment, so existing messages are unaffected.
storage | Meaning |
|---|---|
| provider | Existing AMP behavior: uploaded to the provider, scanned, signed, expires with the message. |
| afp | An AFP reference. No bytes pass through the provider. |
{
"storage": "afp",
"filename": "report.pdf",
"content_type": "application/pdf",
"size": 1827341,
"digest": "sha256:3b2c9f5da87e4f1c8b0a2d6e9f3c7a1b5d8e2f4a6c0b3d7e9f1a4c6d8e0b2a40",
"ref": "afp://artifacts/2026/10/report.pdf",
"endpoint": "https://files.example.net",
"url": "https://files.example.net/artifacts/2026/10/report.pdf?X-Amz-Expires=3600&..."
}
The attachment lives inside payload, so the message signature binds the reference and digest. Use a provider attachment for a file that belongs to one message and is under 25 MB. Use afp when the file is large, shared by several parties, must outlive the message, or is published for a person to retrieve.
A sender puts the object, and gets stored: true, before routing the message. A recipient verifies the digest on fetch. A provider that accepts AFP attachments advertises the capability attachments:afp; routing one to a provider that does not list it returns 422 attachments_not_supported. Local delivery needs no negotiation.
SECURITY
What AFP does not protect: a store operator can read every object (no end-to-end encryption in v0.1), a leaked presigned link works until it expires, and a compromised access key exposes everything in its scope.
INSTALL
The reference client is a set of Bash scripts (bash 3.2 and later, curl 7.75+, jq, openssl, shasum or sha256sum). It is tested against Garage; any S3-compatible store that accepts path-style requests should work.
git clone https://github.com/agentmessaging/agent-files.git ~/agent-files ~/agent-files/install.sh # copies the scripts to ~/.local/bin ~/agent-files/install.sh /usr/local/bin # or to another directory
A space is a bucket (optionally a key prefix in it) on an S3-compatible store. Add it on every machine that should use it. Put the secret key on the first line of the --secret-file and keep that file mode 600. Spaces are stored in ~/.agent-files/spaces.json, and secrets are never printed.
afp-config.sh add shared --endpoint http://host:3900 --bucket afp --region garage \
--access-key <key> --secret-file <file> --capabilities link,expire,ui --default
afp-put.sh report.pdf # -> afp://shared/2026/10/report.pdf afp-get.sh afp://shared/2026/10/report.pdf
| Command | What it does |
|---|---|
| afp-put.sh <file> | Store a file, read it back, verify the digest, write the manifest, print the reference |
| afp-get.sh <ref> | Fetch an object and verify its SHA-256 (refuses suspicious and rejected) |
| afp-ls.sh | List objects from their manifests |
| afp-link.sh <ref> | Print a reference object with a time-limited download URL |
| afp-rm.sh <ref> | Delete an object and its manifest |
| afp-capabilities.sh | Report what a space can do |
| afp-config.sh | Add, list and remove spaces, set the default |
The agent-files skill teaches an agent to use these commands, with setup, space examples and troubleshooting in its references/setup.md. AI Maestro is the first provider: as a Claude Code plugin, its update-aimaestro.sh installs the skill and puts the afp-*.sh scripts on the PATH.