Agent identity (NHI)

Every memory carries the identity of the AI that learned it. Every recall surfaces it. Provenance for AI knowledge — built in from day one.

metadata.agent_id first-class field claimed → attested defense in depth
Every recall result tells you which AI learned the memory. This is the differentiator: ai-memory doesn't just store knowledge — it remembers who added it, and that information is in front of every agent that recalls.

metadata.agent_id is a first-class field on every memory. When your AI calls memory_recall, the response includes a column telling you which agent each result came from — by default, in the token-efficient TOON compact format that AI clients are already optimised for.

In action

See it.

Default recall output (TOON compact format):

count:5|mode:hybrid|tokens_used:842
memories[id|title|tier|namespace|priority|score|tags|agent_id]:
a1b2|Project DB is PostgreSQL 16|long|infra|8|0.91|database,postgres|ai:claude-code@workstation:pid-3812
c3d4|API rate limit is 100 rps|long|infra|7|0.87|api,limits|ai:claude-desktop@laptop:pid-5219
e5f6|Use TOON not JSON for MCP|long|protocol|9|0.82|toon,mcp,tokens|host:pop-os:pid-1144-ad1b9c
g7h8|Deploy gate fixed for v0.6.3|long|releases|6|0.79|deploy,gate|ai:codex@server:pid-9911
i9j0|Ship-gate cleanup complete|long|releases|5|0.74|cleanup|alice

Eight columns. Last column is agent_id. The recall blends what's relevant (text, semantics, priority, recency) and tells you who wrote it.

Definition

What agent_id means.

agent_id is a claimed identity attached to every memory at write time. It survives every operation that touches the memory: update, dedup, import, sync, consolidate. It's the substrate for filtering, scoping, governance, and — as of v0.7.0 (#626 Layer-3) — cryptographic attestation that upgrades a claimed id to an attested one.

Honest framing: by default an unsigned agent_id is claimed, not attested — don't make security decisions on it alone. As of v0.7.0 (#626 Layer-3) a caller holding the agent's keypair can present a detached Ed25519 signature over the canonical SignableWrite envelope on every store surface (CLI --sign, MCP memory_store, HTTP POST /api/v1/memories); the daemon verifies it against the agent's bound public key and stamps metadata.attest_level = "agent_attested" (forged → 403 ATTESTATION_FAILED). Operators can require it process-wide with AI_MEMORY_REQUIRE_AGENT_ATTESTATION. The earlier link-level signature reservation (memory_links.signature, schema v15, v0.6.3) remains the attestation substrate for KG edges. Two edges stay claimed-by-design at v0.7.0 — the federation receive path (mTLS + peer allowlist is the trust boundary) and the permissive default posture — both tracked for v0.8 hardening under #1464.
Resolution

Resolution precedence.

When ai-memory decides what agent_id to stamp on a new memory:

CLI and MCP path

  1. Explicit value from caller — --agent-id <id> flag, or agent_id field on the MCP tool, or metadata.agent_id embedded in a store request
  2. AI_MEMORY_AGENT_ID environment variable
  3. (MCP only) Captured from initialize.clientInfo.name → e.g. ai:claude-code@workstation:pid-3812
  4. Stable per-process fallback → host:<hostname>:pid-<pid>-<uuid8>
  5. anonymous:pid-<pid>-<uuid8> if hostname unavailable

HTTP daemon path (multi-tenant — no process-level default)

  1. agent_id field in POST /api/v1/memories body
  2. X-Agent-Id request header
  3. Per-request anonymous:req-<uuid8> (logged at WARN)

Read-path visibility caller (v0.7.0 #1468 / #1469)

The ladders above resolve the write identity stamped into metadata.agent_id. The MCP read tools that enforce per-row scope=private ownership — memory_session_start, memory_list, memory_search, memory_recall — resolve their visibility caller through a separate, narrower ladder:

  1. AI_MEMORY_AGENT_ID environment variable (when set + shape-valid)
  2. None — trust-all, single-tenant read posture

The pid-synthesized ai:<client>@<host>:pid-<pid> clientInfo id is deliberately not used here: it embeds the live PID, so it could never equal the metadata.agent_id an earlier process wrote, which would hide every prior-session private row from its own owner. When AI_MEMORY_AGENT_ID is set, the read tools drop cross-agent scope=private rows (owned by a different agent, not shared/targeted at the caller) before they reach the wire; collective and caller-owned rows always pass. When unset, the read path keeps the trust-all single-tenant behavior. Multi-tenant MCP hosts must set AI_MEMORY_AGENT_ID per tenant to get private-row isolation on reads.

Validation

What's valid.

Immutability

Defense in depth.

Once a memory is stored, its agent_id doesn't drift. Both the caller layer (identity::preserve_agent_id) and the SQL layer (json_set CASE clauses in db::insert and db::insert_if_newer) enforce preservation across:

If a malicious or buggy caller tries to overwrite agent_id, the SQL layer refuses. This is the defense-in-depth NHI invariant from issue #148 / Task 1.2.

Filtering

Filter by agent.

Every recall, list, and search supports agent_id filtering:

# CLI
ai-memory list --agent-id ai:claude-desktop@laptop:pid-5219
ai-memory search --agent-id alice "deploy"

# MCP — the agent_id property
{"name": "memory_search", "arguments": {"query": "deploy", "agent_id": "alice"}}

# HTTP
curl 'http://127.0.0.1:9077/api/v1/search?q=deploy&agent_id=alice'
System metadata

System-stamped metadata (don't overwrite).

These keys are produced by ai-memory; the system sets them, callers must not.

KeyWhen stamped
imported_from_agent_idai-memory import re-stamps the originator's agent_id (absent when --trust-source is passed)
consolidated_from_agentsmemory_consolidate records the array of source authors; the consolidator's id becomes the new agent_id
mined_fromai-memory mine stamps the source format (claude / chatgpt / slack) alongside the caller's agent_id
Privacy

Scrub the leaky default.

The fallback host:<hostname>:pid-<pid>-<uuid8> exposes hostname and PID. When writing memories to a shared or upstream database, set an opaque --agent-id or AI_MEMORY_AGENT_ID. Tracking issue: #198.

# Production daemon — opaque identity
AI_MEMORY_AGENT_ID=fleet-prod-9 ai-memory serve --host 0.0.0.0 --port 9077

# Or per-CLI-call
ai-memory --agent-id alice store -T "Auth flow notes" -c "..."
Why this matters

Provenance is infrastructure.

Every other memory product treats AI knowledge as anonymous text in a vector DB. ai-memory treats every memory as a dated, attributable assertion. When your fleet of agents grows from one to ten to hundreds:

ai-memory ships provenance on day one.

Next

Where to go.