Votre flotte d'agents peut créer un état digne de confiance avec ses propres clés
Voir la démarqueLorsque de nombreux agents écrivent dans une seule mémoire, vous avez besoin d'une preuve sur chaque ligne : qui a signé, ce qu'ils ont été autorisés à faire et aucun secret de porteur partagé. Neotoma intègre AAuth afin que chaque agent signe avec sa propre clé et écrit via une admission limitée.
Points clés à retenir
- Votre flotte d'agents peut créer un état de confiance lorsque chaque rédacteur signe avec sa propre clé et que les capacités sont limitées à l'octroi et non au porteur.
- Neotoma tamponne chaque observation, relation, source et interprétation avec une identité d'agent signée résolue par rapport à une échelle de confiance à cinq niveaux.
- Capacités déplacées des fichiers de configuration statiques vers des entités « agent_grant » de première classe que vous pouvez suspendre, révoquer et restaurer sans redémarrer le serveur.
- Trois agents de service de ma pile écrivent déjà sur Neotoma sous AAuth : le proxy de signature Cursor, le pipeline de commentaires et la réécriture d'introduction chaleureuse de Darkmesh.
- Les instructions MCP sont expédiées depuis le serveur, donc une mise à niveau propage les nouvelles valeurs par défaut à chaque agent connecté.

title: "Your agent fleet can build trustworthy state with their own keys" excerpt: "When many agents write to one memory, you need proof on every row: who signed, what they were allowed to do, and no shared bearer secrets. Neotoma integrates AAuth so each agent signs with its own key and writes through grant-scoped admission." published: true published_date: "2026-04-28" category: "Agent Architecture" tags: ["neotoma", "aauth", "agent identity", "attribution", "multi-agent", "state integrity", "build-in-public"] read_time: 8 hero_image: "know-which-of-your-agents-wrote-what-hero.png" hero_image_square: "know-which-of-your-agents-wrote-what-hero-square.png" hero_image_style: "keep-proportions"
The problem ahead of most builders
If you run one agent against one store, attribution is trivial. You know who wrote every row. You wrote the agent.
The moment you run two, three, five agents against the same Neotoma instance, the picture changes. Each agent writes observations, relationships, sources, interpretations. The store accumulates state from all of them. If one of those agents starts writing bad data, subtly wrong summaries, stale dates, misattributed relationships, the only way to reason about which records to trust is to reason about which agent wrote them.
Without per-row attribution, your options when something goes wrong are rough: wipe the store and re-ingest, or leave the bad rows in and live with the drift. Both get worse as the store grows.
This problem gets sharper for anyone shipping an agent-driven product. Your customers' records are being written by a fleet: your own agents, third-party agents integrated via MCP, maybe a plugin someone installed last week. When a customer asks "who wrote this to my account and on whose authority?", that question has to be answerable from the data, not from a correlation exercise across server logs and conversation transcripts.
I run agents across Cursor, Claude Code, Codex, and ChatGPT, all writing to one Neotoma instance. I wrote about what that stack actually does. Neotoma's AAuth integration is what closes the gap for my stack and for anyone building on it: each agent brings its own key, and the store can stay trustworthy as the fleet grows.
Why AAuth
The attribution layer is built on AAuth, an open protocol that gives every HTTP client its own cryptographic identity. No pre-registration. No shared secrets. No bearer tokens. Every request is signed with RFC 9421 HTTP Message Signatures, so a stolen token is worthless without the signing key.
I picked AAuth because the person behind it, Dick Hardt, is a friend and one of the deepest identity experts I know. He edited OAuth 2.0 (RFC 6749), co-authored OpenID Authentication 2.0, and was a founding board member of the OpenID Foundation. That is the same lineage most developers meet through authorization code flows and federated login. When someone with that history starts a new protocol specifically for agents, that is the one worth building against.
What every write now carries
Neotoma now ships per-row agent attribution across every write surface: /store, /observations/create, /create_relationship, /correct, /entities/split, the MCP store tools, and CLI writes over both MCP and HTTP. Each observation, relationship, source, and interpretation stamps:
- A verified agent identifier (public-key thumbprint for signed writers, JWT subject and issuer for agent tokens, clientInfo name and version as a fallback).
- A trust tier that classifies how strongly the identity is proven.
- The transport the write arrived on.
Five tiers cover the spectrum:
hardware: the agent provided acnf.attestationenvelope (Apple Secure Enclave, WebAuthn packed, or TPM2) that the server verified against trusted roots.operator_attested: the signature verified, and the operator has allowlisted the issuer or issuer-subject pair. The operator vouches for the agent's process without requiring hardware attestation.software: the agent signed the request with a valid key, verified by the server. This is where most agents land today, including my own Cursor proxy signing with a file-backed ES256 JWK.unverified_client: the agent declared itself with a recognizable clientInfo but did not sign.anonymous: no identity at all.
The upshot: you can look at any row in your store and answer "which agent wrote this" as a read against first-class data.
Grants instead of config files
Early in the build, capabilities were loaded from environment-variable JSON files. That worked for a static set of agents but broke the moment you wanted to suspend one agent without restarting the server.
Now: each agent_grant is a first-class Neotoma entity. It matches an AAuth identity (by subject, issuer, thumbprint, or a combination), carries capability entries scoped per operation and entity type, and has a lifecycle: active, suspended, revoked. The admission middleware resolves a verified AAuth identity to its matching grant on every request, stamps the grant's user and capabilities onto the request context, and downstream enforcement checks each operation against the grant.
Grants are managed through the Inspector UI, the REST API (POST /agents/grants, PATCH, suspend, revoke, restore), or migrated once from the old env-config via neotoma agents grants import. The legacy env vars (NEOTOMA_AGENT_CAPABILITIES_*) cause a boot-time failure if still set, with a structured error pointing to the migration command.
Suspending a grant is instant. The agent's next request fails admission. Restoring is equally instant. No server restart, no config reload.
For anyone running a product with customer-facing agents, this means incident response moves from "restart the service with a new config" to "suspend one grant and investigate." The blast radius of a misbehaving agent is bounded to the operations that grant authorized.
Identity preflight
Every agent can now ask Neotoma, before it produces any data, whether it is recognized as a trusted writer.
Three equivalent entry points:
GET /sessionover HTTP.get_session_identityas an MCP tool.neotoma auth sessionon the CLI.
Each returns the resolved trust tier, the grant status (admitted or not, with reason), anonymous-write policy, and a boolean eligible_for_trusted_writes. The response includes a diagnostic block explaining how the tier resolved. A new agent fails loudly at session start instead of writing anonymous rows until someone notices.
The shipped MCP instructions tell every connected agent to run this check before enabling writes.
Where I am running this
Three distinct service agents in my stack write to Neotoma under AAuth today.
Cursor MCP proxy. Every MCP request from Cursor flows through a signing proxy (mcp_identity_proxy.py) that injects an RFC 9421 signature with an aa-agent+jwt agent token. Neotoma verifies the signature, resolves the identity ([email protected], iss=https://markmhendrickson.com), matches the agent_grant, and admits the write at tier=software. The proxy also runs session preflight on startup and can fail closed if the server reports anonymous tier.
Feedback pipeline. A Netlify relay at agent.neotoma.io forwards agent bug reports into Neotoma over an AAuth-signed Cloudflare Access tunnel. Its grant is scoped to neotoma_feedback operations only.
Darkmesh warm-intro writeback. My Darkmesh fork (context) records warm-intro reveals back into Neotoma with RFC 9421 signatures and an aa-agent+jwt token. Each reveal lands with the node's agent_sub, agent_iss, and key thumbprint, scoped by a per-node grant.
The Darkmesh joint tests proved the enforcement in an adversarial shape. A second simulated agent from a peer node tried to write a warm_intro_reveal without that entity type in its grant. Neotoma rejected the write. The authorized node's writes went through unchanged.
Next on the roadmap: the public agent on markmhendrickson.com wraps a Neotoma instance as its memory and today only serves entities I have explicitly marked public. I plan to add AAuth-gated reads so authorized visitors can query specific non-public entity types. Same signed-identity-plus-grant machinery, applied to the read path.
Fleet-wide upgrade
Neotoma ships its canonical MCP instructions from the server to every connected client on every handshake. Those instructions now codify attribution preflight, observation_source tagging, reply-cited provenance edges, an Ambiguous (N) display group for heuristic-merge warnings, and a structured feedback-submission loop.
When I upgraded my server, my Cursor, Claude Code, Codex, and OpenCode hooks all picked up the new behaviors. No client-side releases. No per-tool migration. One server bump, five agents updated. For anyone running customer fleets, the same pattern applies: upgrade the Neotoma instance and every connected agent picks up the new defaults without a client deploy.
The audit surface
For product-builders in regulated markets, the follow-up question from a customer is rarely "did your system remember this." It is "who wrote it, and can you prove they were authorized."
That is now a read against first-class data:
GET /agentsenumerates every agent identity the server has seen.GET /agents/{key}returns the per-agent detail view.GET /agents/{key}/recordsaudits which records a given agent authored.GET /agents/grantslists all grants, their capabilities, and their lifecycle status.
If you ship agentic functionality to customers in healthcare, finance, legal, or enterprise verticals, this is the surface your customers will eventually demand.
How to turn it on
neotoma auth keygen --alg ES256
neotoma auth sign-example
neotoma auth session
Create a grant for the new identity via the Inspector or the REST API, scoping capabilities to the operations your agent needs. If you are upgrading from the old env-config model, run neotoma agents grants import --owner-user-id <your_user_id> once, then unset the legacy variables.
For programmatic signing, @aauth/local-keys or an equivalent AAuth library signs requests with RFC 9421 HTTP Message Signatures plus an aa-agent+jwt token. Neotoma verifies the signature on the raw bytes the client signed.
Writes without AAuth still work. They land in the anonymous tier. Builders who want hard failures can flip NEOTOMA_AAUTH_STRICT=1 and add specific subjects to NEOTOMA_STRICT_AAUTH_SUBS.
Also shipped
The same release cycle lands entity split for over-merged records, fleet snapshot export plus drift tooling, hardware attestation across macOS, Linux, Windows, and YubiKey, first-class multi-agent conversations via conversation_message and sender_kind, a public sandbox at sandbox.neotoma.io, and a tightened API perimeter. The full changelog is on the releases page.
Install and upgrade
npm install -g neotoma
neotoma init
neotoma auth keygen
neotoma auth session
Upgrading the server gives you the new attribution stamping and the MCP instruction refresh on the next client handshake. No client-side install required for agents already connected via MCP.
Full install: neotoma.io/install. Repo: github.com/markmhendrickson/neotoma. Releases: github.com/markmhendrickson/neotoma/releases.