Defense in depth at the API boundary. 23 validator functions, every limit explicit, every closed set enumerated. The same checks run on HTTP, MCP, CLI, and inbound federation sync — there is no privileged path that bypasses them.
Constants live in src/validate.rs at module top. Bumping any of these is a wire-format change — clients should treat them as the contract.
\n, \t)\n, \t)/ is the segment delimiter; flat namespaces ("global", "ai-memory") remain valid — hierarchy is opt-in./, no trailing /, no empty segment (//). and .. rejected — red-team #240 (path-traversal-style scope leak)normalize_namespace(input) trims, strips leading/trailing slashes, collapses //, lowercases. Not auto-applied by write paths — opt-in to preserve case sensitivity on existing flat namespaces.user — human-typedclaude — Claude Code or other Claude clienthook — emitted by a Claude Code hookapi — generic HTTP APIcli — local ai-memory CLIimport — bulk import pathconsolidation — output of memory_consolidatesystem — daemon-internal stampschaos — chaos-engineering test harnessnotify — v0.6.2 (S32) handle_notify inbox stamps_ - : @ . /private by the query layer.private — visible only to the originating agentteam — visible to agents at the same namespace prefix (one level)unit — visible up the hierarchy two levelsorg — visible across the whole organization namespacecollective — federation-wide visibilityai:<name> namespace + curated short-list. Red-team #235: the original closed whitelist blocked future agents — now operators can register ai:claude-opus-4.8, ai:gpt-5, ai:gemini-2.5 without a code release.human, system, ai:claude-opus-4.6, ai:claude-opus-4.7, ai:codex-5.4, ai:grok-4.2ai:<name> form: name 1–60 chars, ASCII alphanumeric + _ - .related_to — generic association (default for memory_link)supersedes — newer memory replaces older (typed cognition)contradicts — explicit disagreement (Task: contradiction detection)derived_from — provenance (consolidation outputs)validate_tags. Same rules.expires_at = now + ttl_secs. Capped at 1 year so a typo can't accidentally make a memory effectively immortal.governance, agent_id, scope, taxonomy fields all live here, so the cap has to be high enough for real-world use.ApproverType::Agent(id) → must pass validate_agent_idApproverType::Consensus(n) → quorum must be ≥ 1write/promote/delete is Approve, approver must be meaningful (not Consensus(0))ApproverType::Human always validvalidate_create but for fully-realized Memory records. Permits past expires_at (importing historical data).validate_id(&mem.id)access_count >= 0created_at, updated_at are RFC3339last_accessed_at, expires_at RFC3339 if present (no chronological check)UpdateMemory is Option). Uses validate_expires_at_format not validate_expires_at — past dates allowed for programmatic TTL management.validate_idvalidate_relationvalidate_idvalidate_titlevalidate_contentvalidate_namespaceThere is no privileged path that bypasses the validators. Every entry that creates or mutates state on the server runs the same set of checks.
validate_create / validate_update / validate_link at request decodePOST /sync/push) → validate_memory on every received row before insert; rejects malformed peer payloadsmemory_import) → validate_memory on every rowA failed validator returns a 400-class HTTP error with the exact bail! message. AI clients can surface the reason verbatim — there is no "validation failed" placeholder.