Neotoma

AAuth (Agent Authentication)

AAuth is Neotoma's mechanism for cryptographically verifiable agent identity on every write. It lives alongside (not in place of) human user authentication: where user_id answers "whose data is this?", AAuth answers "which agent wrote it?". The pair is stamped onto every observation, relationship, source, interpretation, and timeline event.

AAuth is built on two open standards: RFC 9421 HTTP Message Signatures for the request signature, and an aa-agent+jwt agent token that carries the agent's confirmation key and stable identifiers. Optional hardware attestation (Apple Secure Enclave, WebAuthn-packed, TPM 2.0) promotes signed clients into the highest trust tier.

Protocol-level specifications, Internet-Drafts, SDKs, and the live playground are on aauth.dev. The rest of this page is how Neotoma implements that contract for writes and attribution.

Why AAuth

  • Bearer tokens are operator-set shared secrets that gate connection access; they resolve a user_id but never mint an attribution tier above anonymous on their own. OAuth authenticates the human behind the connection. AAuth identifies the agent writing within that authenticated session, in parallel with whichever human-identity flow is in use.
  • Neotoma's AAuth is an identity-based contract today, not a delegation-token system: the agent presents a stable cryptographic identity, and the server records it alongside the human user_id on every write. Per-agent scoping is handled separately via agent_grant entities (see capabilities).
  • Every durable row carries the agent's stable identifiers (agent_thumbprint, agent_sub, agent_iss) and a resolved trust_tier.
  • Operators can require a minimum tier per route via attribution policy. Self-reported clients fall back gracefully to the unverified_client tier.
  • The contract is uniform across HTTP /mcp, direct REST routes, MCP stdio, and CLI-over-MCP / CLI-over-HTTP.

Trust tiers

A single enum is stamped onto every durable row. Tier resolution happens once per request inside src/middleware/aauth_verify.ts; services and clients MUST read the resolved tier from the request context rather than re-deriving it.

  • hardware, AAuth verified AND the JWT carries a cnf.attestation envelope the verifier accepts AND the bound key is not revoked.
  • operator_attested, AAuth verified AND iss (or iss:sub) is in the operator allowlist (NEOTOMA_OPERATOR_ATTESTED_ISSUERS / NEOTOMA_OPERATOR_ATTESTED_SUBS).
  • software, AAuth verified, but no attestation envelope (or attestation failed and operator allowlist did not match), regardless of signing algorithm.
  • unverified_client, No AAuth signature was verified, but the caller self-reported a distinctive clientInfo.name (or X-Client-Name) that survived generic-name normalisation. The name is recorded on the row but is not cryptographically attested, anyone can claim it.
  • anonymous, No AAuth, and no usable client name either: clientInfo and X-Client-Name were absent, empty, non-strings, or matched the generic-names blocklist (mcp, client, mcp-client, unknown, anonymous). The row carries no stable identifying name at all.

The practical difference: unverified_client rows still let you filter and group by which integration produced the write (e.g. cursor-agent vs claude-code), whereas anonymous rows are an undifferentiated bucket that operators usually want to surface or block via attribution policy.

Tier resolution surfaces directly in the Inspector agents view: every distinct writer is one row, badged with the tier it resolved to (hardware, operator_attested, software, unverified_client, anonymous) plus the signing algorithm and last-seen activity.

See the Inspector agents view for a live table of all signing identities, trust tiers, and write counts.

See AAuth wire format and verification for signature components, headers, the aa-agent+jwt token contract, and the trust-tier verification cascade.

See Integration guide for per-request precedence rules, preflight checks, and the diagnostic surface (GET /session, attribution.decision).

See Attestation for hardware attestation envelope formats (Apple Secure Enclave, WebAuthn-packed, TPM 2.0), per-format verifiers, and revocation policy.

Operators control attribution requirements through environment variables and the Inspector attribution policy panel: global mode (allow / warn / reject), minimum tier, and per-path overrides. See Integration for the full contract.

See CLI keys and hardware backends for keygen commands, on-disk key layout, and per-platform hardware support.

Inspect attribution from the operator console

Everything on this page has a counterpart in the Inspector, Neotoma's read-only operator UI. Use these entry points when debugging an integration or auditing writes:

  • Agents & grants , every signing identity that ever wrote, with tier badges, thumbprint, algorithm, attestation outcome, last-seen activity, and the per-agent (op, entity_type) grant table.
  • Settings · Attribution policy , global mode, minimum tier, and per-path overrides resolved from NEOTOMA_ATTRIBUTION_POLICY / NEOTOMA_MIN_ATTRIBUTION_TIER / NEOTOMA_ATTRIBUTION_POLICY_JSON, with a live decision summary.
  • Observations & sources , every immutable write tagged with resolved_tier and agent identifiers; filter by tier or by agent_thumbprint.
  • Timeline & interpretations , chronological view of every signed event across the instance, including verification failures and tier promotions.

Specs and deeper reading

Each topic below has its own dedicated reference page with the full implementation contract:

See REST API reference for HTTP endpoints, MCP reference for agent-native transport, and CLI reference for keygen and session inspection commands.