# ai-memory CLI Reference

Complete reference for every subcommand, flag, and environment variable
the `ai-memory` binary exposes.

```bash
ai-memory [GLOBAL_OPTIONS] <COMMAND> [COMMAND_OPTIONS]
```

Run `ai-memory <command> --help` for the authoritative flag list
generated by clap. This document is kept in sync with the source
(the `Command` enum in `src/daemon_runtime.rs`; 80 top-level
subcommands in the default build, 82 under `--features sal` — SSOT:
`EXPECTED_CLI_SUBCOMMANDS_DEFAULT` / `_SAL` in `src/lib.rs`) and
supplements `--help` with examples and context.

## Global flags

| Flag | Type | Default | Description |
|------|------|---------|-------------|
| `--db <PATH>` | path | `ai-memory.db` | SQLite database path. Honours `AI_MEMORY_DB` env var. |
| `--json` | bool | `false` | Emit machine-parseable JSON on stdout. |
| `--agent-id <ID>` | string | synthesized NHI default | Stamps `metadata.agent_id`. Honours `AI_MEMORY_AGENT_ID` env var. |
| `--db-passphrase-file <PATH>` | path | — | v0.6.0.0+. Root-readable file holding the SQLCipher passphrase. Only meaningful on `--features sqlcipher` builds. Exports `AI_MEMORY_DB_PASSPHRASE` for the process. |

## Environment variables

| Variable | Purpose |
|----------|---------|
| `AI_MEMORY_DB` | Override default database path. |
| `AI_MEMORY_AGENT_ID` | Default `metadata.agent_id` for memories written by this process. |
| `AI_MEMORY_DB_PASSPHRASE` | SQLCipher passphrase (set via `--db-passphrase-file` or by operator). |
| `AI_MEMORY_NO_CONFIG=1` | Skip loading `~/.config/ai-memory/config.toml`. Used by tests. |
| `AI_MEMORY_ANONYMIZE=1` | Suppress hostname/PID from fallback `agent_id` generation. |
| `AI_MEMORY_AUTONOMOUS_HOOKS=1` | Enable post-store LLM hooks (v0.6.0.0). Overrides config. |
| `AI_MEMORY_BOOT_ENABLED` | Enable/disable session-boot context. Set to `0` to disable. Overrides config. |
| `AI_MEMORY_AUDIT_DIR` | Override directory for the security audit trail. Default: `~/.local/state/ai-memory/audit/`. |
| `AI_MEMORY_LOG_DIR` | Override directory for operational logs. Default: `~/.local/state/ai-memory/logs/`. |
| `AI_MEMORY_REQUIRE_API_KEY=1` | Hard-require `api_key` on **every** daemon bind, including loopback (#1458). Use when fronting the daemon with a reverse proxy / `--network=host` container / `socat` forward, where the loopback host string the daemon sees does not reflect off-host reachability. Default off (keyless loopback permitted with a warning; keyless non-loopback already refused). |
| `AI_MEMORY_EMBED_BACKEND` | #1598 — embedding backend selector: `ollama` (default), any #1067 vendor alias (`openrouter`, `openai`, `gemini`, …), or `openai-compatible` (self-hosted TEI/vLLM/llama.cpp server). Precedence: env > `[embeddings]` section > legacy flat > compiled default. |
| `AI_MEMORY_EMBED_BASE_URL` | #1598 — embedding endpoint base URL. Required for `openai-compatible`; vendor default applies for named aliases. |
| `AI_MEMORY_EMBED_MODEL` | #1598 — embedding model id (e.g. `google/gemini-embedding-2`). Dim resolves from `KNOWN_EMBEDDING_DIMS`; set `[embeddings].dim` for models outside the table. |
| `AI_MEMORY_EMBED_API_KEY` | #1598 — **secret.** Bearer key for API embedding backends; highest-precedence layer over the per-vendor alias env, `[embeddings].api_key_env`, and `[embeddings].api_key_file` (0400 enforced). |
| `RUST_LOG` | Tracing filter, e.g. `RUST_LOG=ai_memory=debug`. (Standard Rust ecosystem env, not product-specific.) |

Resolution precedence for any setting: **CLI flag > `AI_MEMORY_*` env
var > config file > compiled default**.

## Core memory operations

### `store`

Create or upsert a memory. Upsert key is `(title, namespace)`. Tier
never downgrades.

| Flag | Type | Default | Notes |
|------|------|---------|-------|
| `--tier`/`-t` | enum | `mid` | `short`/`mid`/`long`. |
| `--namespace`/`-n` | string | git-remote or dir name | |
| `--title`/`-T` | string | required | |
| `--content`/`-c` | string | required | Pass `-` to read stdin. |
| `--tags` | comma-list | `""` | |
| `--priority`/`-p` | int 1–10 | `5` | |
| `--confidence` | float 0.0–1.0 | `1.0` | |
| `--source`/`-S` | string | `cli` | One of the canonical `VALID_SOURCES` (`src/validate.rs`): `user`, `nhi`, `claude` (deprecated), `hook`, `api`, `cli`, `import`, `consolidation`, `system`, `chaos`, `notify`. |
| `--expires-at` | RFC3339 | — | Overrides tier default. |
| `--ttl-secs` | int | — | Overrides tier default. |
| `--scope` | enum | `private` | Task 1.5 visibility. `private`/`team`/`unit`/`org`/`collective`. |
| `--kind` | enum | `observation` | v0.7.0 F2.3 (#1427) Form-6 memory kind: `observation`, `reflection`, `persona`, `concept`, `entity`, `claim`, `relation`, `event`, `conversation`, `decision`. |
| `--citations` | JSON | — | Form-4 provenance: JSON array of `{uri, accessed_at, hash?, span?}`. |
| `--source-uri` | string | — | Form-4 source pointer; `uri:` / `doc:` / `file:` schemes. |
| `--source-span` | JSON | — | Form-4 byte-range pin: `{start, end}`. |
| `--entity-id` | string | — | QW-2 persona binding; required with `--kind persona`. |
| `--sign` | bool | `false` | **#626 Layer-3** — sign the write with the resolved agent's local Ed25519 keypair so the stored row is *attested* (`attest_level = "agent_attested"`) rather than merely *claimed*. Requires a `<agent_id>.priv` under the key directory (`AI_MEMORY_KEY_DIR` or the platform default) and a matching bound public key (`ai-memory agents bind-key`). Without `--sign` the write is *claimed* unless `AI_MEMORY_REQUIRE_AGENT_ATTESTATION` is set (which then rejects the unsigned write). |

```bash
ai-memory store --title "Q2 roadmap" \
  --content "Aligned on microservices cut-over by July" \
  --tier long --namespace planning --tags strategic,quarterly

# Attested write: sign with the agent's bound Ed25519 keypair (#626 Layer-3).
ai-memory store --title "Prod incident postmortem" \
  --content "Root cause: connection-pool exhaustion under retry storm" \
  --tier long --namespace incidents --agent-id ai:claude-opus-4.7 --sign
```

### `recall`

Semantic + keyword hybrid recall. **Mutates the database**: increments
`access_count`, raises `expires_at` to
`MAX(current expires_at, now + per-tier-extend_secs)` (extension FLOOR —
an access can never move an expiry earlier; issue
[#1596](https://github.com/alphaonedev/ai-memory-mcp/issues/1596),
superseding the [#830](https://github.com/alphaonedev/ai-memory-mcp/issues/830)
replacement contract), auto-promotes mid→long at 5 accesses.

| Flag | Type | Default | Notes |
|------|------|---------|-------|
| `<context>` | positional | required | Natural-language query. |
| `--namespace`/`-n` | string | — | |
| `--limit` | int | `10` | Capped at 50. |
| `--tags` | comma-list | — | |
| `--since`/`--until` | RFC3339 | — | |
| `--tier`/`-T` | enum | config | `keyword`/`semantic`/`smart`/`autonomous`. |
| `--as-agent` | string | — | Task 1.5 scope-aware recall. |
| `--budget-tokens` | int | — | Task 1.11 context-budget. |
| `--context-tokens` | comma-list | — | v0.6.0.0 conversation bias; 70/30 fusion. |
| `--session-default` | bool | `false` | v0.7.0 (#518) — splice `[agents.defaults.recall_scope]` defaults for unset filters. |
| `--include-archived` | bool | `false` | v0.7.0 WT-1-E — also return archived atomisation sources. |
| `--has-citations` | bool | `false` | v0.7.0 Form 4 (#757) — only memories with a non-empty `citations` array. |
| `--source-uri-prefix` | string | — | v0.7.0 Form 4 (#757) — only memories whose `source_uri` starts with this prefix. |
| `--kind` / `--kinds` | comma-list | — | v0.7.x Form 6 (#759) — Batman-taxonomy kind filter (`concept,entity,claim`, …; `all` or omit = no filter). |

```bash
ai-memory recall "what deployment pattern did we agree on" \
  --tier semantic --namespace planning --limit 5
```

### `search`

Read-only FTS5 keyword search. Does not mutate the database.

| Flag | Type | Default | Notes |
|------|------|---------|-------|
| `<query>` | positional | required | AND semantics. |
| `--namespace`/`-n` | string | — | |
| `--tier`/`-t` | enum | — | |
| `--limit` | int | `20` | (The MCP `memory_search` tool caps at 200; the CLI passes the limit through.) |
| `--since`/`--until` | RFC3339 | — | |
| `--tags` | comma-list | — | |
| `--agent-id` | string | — | Exact `metadata.agent_id` match. |
| `--as-agent` | string | — | Scope-aware filter. |
| `--include-archived` | bool | `false` | v0.7.0 WT-1-E — include archived atomisation sources. |

```bash
ai-memory search "kubernetes rollout" --tier long --limit 10
```

### `list`, `get`, `update`, `delete`

Standard CRUD.

```bash
ai-memory list --namespace planning --tier long --limit 50
ai-memory get abc123
ai-memory update abc123 --title "Renamed" --priority 8
ai-memory delete abc123
```

`list` supports the same filters as `search`. `get` accepts a UUID or
unique prefix. `update` omits any flag you don't pass. `delete`
archives first when `archive_on_gc=true` (default).

### `promote`, `forget`, `link`

```bash
ai-memory promote abc123                    # bump to long tier
ai-memory promote abc123 --to-namespace org # Task 1.7 clone-to-ancestor
ai-memory forget --namespace scratch --tier short
ai-memory link src-id dst-id --relation supersedes
```

Relations (six at v0.7.0): `related_to` (default), `supersedes`,
`contradicts`, `derived_from`, `reflects_on`, `derives_from`.

`forget` safety rail (Round-2 F11): when `--namespace` is omitted and
`--pattern` or `--tier` is set, the delete is global-scope and refuses
to run without `--confirm-global`.

## Lifecycle

### `consolidate`

Merge multiple memories into a single summary memory. Sources deleted
(archived); new memory carries `derived_from` links.

```bash
ai-memory consolidate "id1,id2,id3" \
  --title "Consolidated Q2 planning" \
  --summary "Consensus notes across three sessions" \
  --namespace planning
```

Min 2, max 100 source ids.

### `auto-consolidate`

Automatic batch consolidation of similar short-tier memories. Identifies
clusters via Jaccard similarity, then merges each cluster into a single
mid-tier consolidated memory (sources archived, `derived_from` links
preserved). Pairs with `curator` for autonomous mode.

| Flag | Type | Default | Notes |
|------|------|---------|-------|
| `--namespace`/`-n` | string | — | Restrict scan to one namespace. |
| `--short-only` | bool | `false` | Only consolidate short-tier candidates. |
| `--min-count` | usize | `3` | Skip clusters below this size. |
| `--dry-run` | bool | `false` | Report only — no archive, no merge. |

```bash
ai-memory auto-consolidate --namespace planning --min-count 5 --dry-run
```

Cluster threshold: `CONSOLIDATE_JACCARD_THRESHOLD = 0.55`. Cluster
ceiling: `CONSOLIDATE_MAX_CLUSTER_SIZE = 8` (per `src/autonomy.rs`).

### `resolve`

Mark one memory as superseding another (adds `supersedes` link +
archives loser).

```bash
ai-memory resolve winner-id loser-id
```

### `gc`, `stats`, `namespaces`

```bash
ai-memory gc                      # immediate garbage collection
ai-memory stats                   # counts by tier/namespace, DB size
ai-memory namespaces              # list every namespace + count
```

### `export`, `import`

```bash
ai-memory export > backup.json
ai-memory import < backup.json                # restamps agent_id
ai-memory import --trust-source < trusted.json # preserves agent_id
```

`--trust-source` is only safe when the source is your own backup or a
fully-trusted peer. Without it, `metadata.imported_from_agent_id`
records the original claim.

## Integration surfaces

### `mcp`

Run as an MCP tool server over stdio (JSON-RPC 2.0). At v0.7.0,
`--profile full` advertises 74 entries (73 callable memory tools + the
always-on `memory_capabilities` bootstrap; see issue
[#862](https://github.com/alphaonedev/ai-memory-mcp/issues/862) for the
disambiguation). Default `--profile core` ships 7 tools + the bootstrap.
Plus 2 prompts (`recall-first`, `memory-workflow`).

```bash
ai-memory mcp --tier semantic
```

See `docs/API_REFERENCE.md` and the MCP section of `USER_GUIDE.md`.

### `serve`

Axum-based HTTP daemon on port 9077. There is **no `--tier` flag**
(tier comes from `config.toml`, #703) and **no `--api-key` flag** (the
API key comes from the `api_key` field in `config.toml`).

| Flag | Type | Default | Notes |
|------|------|---------|-------|
| `--host` | string | `127.0.0.1` | |
| `--port` | u16 | `9077` | |
| `--tls-cert`/`--tls-key` | path | — | Enable HTTPS. rustls, no OpenSSL. |
| `--mtls-allowlist` | path | — | SHA-256 cert-fingerprint allowlist (requires `--tls-cert`). |
| `--shutdown-grace-secs` | u64 | `30` | SIGINT grace period. |
| `--quorum-writes` | usize | `0` | v0.7 federation: W (peer acks required). `0` = federation off. |
| `--quorum-peers` | comma-list | — | Peer base URLs; each must expose `POST /api/v1/sync/push`. |
| `--quorum-timeout-ms` | u64 | `2000` | Quorum-ack deadline; after it the write returns 503 `quorum_not_met`. Default assumes same-DC peers; cross-region (WAN) meshes need 5000-10000 (the do-1461 reference deployment uses 8000 — see `docs/federation.md`, [#1565](https://github.com/alphaonedev/ai-memory-mcp/issues/1565)). |
| `--quorum-client-cert`/`--quorum-client-key` | path | — | mTLS client pair for outbound quorum fanout. |
| `--quorum-ca-cert` | path | — | CA for verifying quorum peers. |
| `--catchup-interval-secs` | u64 | `30` | Federation catch-up loop cadence. |
| `--federation-identity` | string | — | Identity this node presents to peers (also `AI_MEMORY_FED_IDENTITY`). |
| `--store-url` | URL | — | SAL backend selector (`postgres://…` under `--features sal-postgres`). |

```bash
ai-memory serve --host 0.0.0.0 --port 9077 \
  --tls-cert /etc/ai-memory/cert.pem \
  --tls-key /etc/ai-memory/key.pem
```

### `sync`, `sync-daemon`

```bash
ai-memory sync /path/to/remote.db --direction merge --dry-run
ai-memory sync-daemon \
  --peers http://peer-a:9077,http://peer-b:9077 \
  --interval 30 --batch-size 500
```

`sync` flags: `--direction pull|push|merge` (default `merge`),
`--trust-source`, `--dry-run`. `sync-daemon` flags: `--peers`
(comma-list), `--interval` (seconds, default `2`), `--batch-size`
(default `500`), `--api-key` (X-API-Key presented to peers), plus the
mTLS options `--client-cert` / `--client-key`.
`--insecure-skip-server-verify` is documented as DANGEROUS.

## Mining & archives

### `mine`

Import memories from Claude, ChatGPT, or Slack exports.

```bash
ai-memory mine ./claude-export --format claude --min-messages 3
```

`--format`: `claude` / `chatgpt` / `slack`. `--dry-run` previews.

### `archive`

```bash
ai-memory archive list --limit 100
ai-memory archive restore abc123
ai-memory archive purge --older-than-days 30
ai-memory archive stats
```

## Governance & agents

```bash
ai-memory agents list
ai-memory agents register \
  --agent-id ai:claude@prod \
  --agent-type ai:claude-opus-4.7 \
  --capabilities recall,search,store

ai-memory pending list --status pending
ai-memory pending approve pending-id
ai-memory pending reject pending-id
```

`--agent-type` accepts: `human`, `system`, `ai:claude-opus-4.6`,
`ai:claude-opus-4.7`, `ai:codex-5.4`, `ai:grok-4.2`, or any
`ai:<name>` form (e.g., `ai:gpt-5`, `ai:gemini-2.5`).

`agents` also exposes `bind-key` / `revoke-key` — bind (or revoke) an
agent's Ed25519 public key for #626 Layer-3 store-path attestation
(pairs with `ai-memory store --sign`).

## Backup & restore (v0.6.0.0)

```bash
ai-memory backup --to /var/backups/ai-memory --keep 48
ai-memory restore --from /var/backups/ai-memory
```

`backup` uses SQLite `VACUUM INTO` (hot-backup safe) and writes a
sha256 manifest. `restore` verifies the manifest before replacing the
DB; use `--skip-verify` only for forensic recoveries.

## Autonomy (v0.6.1)

### `curator`

| Flag | Type | Default | Notes |
|------|------|---------|-------|
| `--once` | bool | — | Single sweep + JSON report. |
| `--daemon` | bool | — | Loop until SIGINT/SIGTERM. |
| `--interval-secs` | u64 | `3600` | Daemon cadence; clamped to [60, 86400]. |
| `--max-ops` | usize | `100` | Hard cap on LLM calls per cycle. |
| `--dry-run` | bool | — | No DB writes. |
| `--include-namespace` | repeatable | — | Restrict to listed namespaces. |
| `--exclude-namespace` | repeatable | — | Skip listed namespaces. |
| `--json` | bool | — | Machine-parseable report. |
| `--rollback <id>` | string | — | Reverse one rollback-log entry. |
| `--rollback-last <N>` | usize | — | Reverse the N most recent. |

```bash
# Once with JSON report
ai-memory curator --once --max-ops 50 --json

# Daemon mode
ai-memory curator --daemon --interval-secs 1800

# Reverse the last 5 autonomous actions
ai-memory curator --rollback-last 5
```

See `docs/RUNBOOK-curator-soak.md` for the week-long soak procedure.

## Session-boot, install, wrap, logs, audit, bench, doctor (v0.6.3.1)

These seven subcommands land in v0.6.3.1.

### `boot`

Universal session-boot CLI primitive. Read-only, fast (no embedder, no
daemon). Always emits a 5-field diagnostic manifest (version, db_path +
schema_version + memory count, tier with embedder/reranker/llm, latency,
namespace + loaded count) so the agent and the human always see what
loaded and why.

| Flag | Type | Default | Notes |
|------|------|---------|-------|
| `--namespace` | string | inferred from `--working-dir` | Override the auto-namespace. |
| `--limit` | int | `10` | Clamped to `1..=50`. |
| `--budget-tokens` | int | `4096` | Optional context-budget cap (0 = unlimited). |
| `--format` | enum | `text` | `text` / `json` / `toon`. |
| `--no-header` | bool | `false` | Suppress the manifest header. NOT recommended for production hooks (silent failure becomes indistinguishable from "no memories yet"). |
| `--quiet` | bool | `false` | Exit 0 silently when DB unavailable. |
| `--cwd <PATH>` | path | `.` | Override the working-directory inference for auto-namespace. |

`AI_MEMORY_BOOT_ENABLED=0` is the highest-precedence privacy opt-out
(silent exit 0). `boot` exits **0 in every state** — never wedges an
agent's first turn.

```bash
ai-memory boot --limit 3
ai-memory boot --namespace planning --format json --quiet
AI_MEMORY_BOOT_ENABLED=0 ai-memory boot   # privacy opt-out: silent exit 0
```

See the `[boot]` config block in `docs/ADMIN_GUIDE.md` for `enabled` /
`redact_titles` settings.

### `install <agent>`

Multi-target config-file installer. 10 targets:
`claude-code`, `openclaw`, `cursor`, `cline`, `continue`, `windsurf`,
`claude-desktop`, `codex`, `grok-cli`, `gemini-cli`.

| Flag | Type | Default | Notes |
|------|------|---------|-------|
| `--apply` | bool | `false` | Write changes. Without it, runs in dry-run mode and emits a unified diff to stdout. |
| `--dry-run` | bool | `true` | Default mode — preview only. Conflicts with `--apply`. |
| `--uninstall` | bool | `false` | Remove the managed marker block precisely. Combine with `--apply` to actually delete. |
| `--config <PATH>` | path | per-target canonical | Override discovery for targets where the canonical path can't be auto-detected. |
| `--binary <PATH>` | path | resolved on `PATH` | Override the resolved `ai-memory` binary path. |
| `--hook <KIND>` | enum | — | Also install the named hook (`claude-code` only today). |
| `--force` | bool | `false` | Overwrite an existing managed block. |

Behaviours:

- Idempotent marker block (`// ai-memory:managed-block:start/end` plus a
  `managed-keys` allowlist) — re-running is safe.
- JSON roundtrip validation before write.
- `.bak.<rfc3339>` backup file written next to the destination.
- World-writable destination paths are refused.

```bash
ai-memory install claude-code               # preview the diff
ai-memory install claude-code --apply       # actually write
ai-memory install claude-code --uninstall --apply
```

### `wrap <agent>`

Cross-platform Rust replacement for shell-wrapper recipes. Spawns the
named CLI with `ai-memory boot` context delivered via the appropriate
strategy.

| Strategy | Behaviour | Default for |
|----------|-----------|-------------|
| `SystemFlag` | `--system <msg>` (or whatever the target accepts) | `codex` / `codex-cli`, `gemini` |
| `SystemEnv` | env-var injection | `ollama` |
| `MessageFile` | tempfile + `--message-file <path>` | `aider` |
| `Auto` | per-agent lookup table | (selector) |

| Flag | Type | Default | Notes |
|------|------|---------|-------|
| `--system-flag <FLAG>` | string | per-agent | Override the system-message flag. Mutually exclusive with `--system-env` and `--message-file-flag`. |
| `--system-env <NAME>` | string | per-agent | Override the system-message env var. |
| `--message-file-flag <FLAG>` | string | per-agent | Override the message-file flag. |
| `--no-boot` | bool | `false` | Skip the `ai-memory boot` call entirely. |
| `--limit` | int | `10` | Boot row limit (clamped `[1, 50]`). |
| `--budget-tokens` | int | `4096` | Boot token budget. |
| trailing `<args>...` | — | — | Forwarded to the wrapped agent after `--`. |

Fall-through is `--system`. Same binary on macOS / Linux / Windows /
Docker / Kubernetes. Exit code is propagated.

```bash
ai-memory wrap codex -- "draft a release note"
ai-memory wrap aider -- src/main.rs
```

### `logs`

Operator CLI for the operational logging facility (default-OFF; opt-in
via `[logging] enabled = true`). Subcommands:

| Subcommand | Notes |
|------------|-------|
| `tail [--follow]` | Stream the active log file. `--follow-interval-ms` (default `1000`) sets the poll rate. `--lines <N>` (default `50`) sets the initial buffer. |
| `cat` | Concatenate rotated log files in order. |
| `archive` | zstd-compress files past their retention window. |
| `purge --before <date>` | Delete log files older than `<date>` (RFC3339). Emits an audit-gap warning. |

Global filters (apply to `tail` / `cat`): `--since`, `--until`,
`--level`, `--namespace`, `--actor`, `--action-filter`,
`--format text|json`, `--log-dir <PATH>`.

Path resolution precedence: `--log-dir` flag > `AI_MEMORY_LOG_DIR` env >
`config.toml` > platform default. See `docs/ADMIN_GUIDE.md` for the
`[logging]` block and platform default paths.

```bash
ai-memory logs tail --follow --level warn
ai-memory logs archive
ai-memory logs purge --before 2026-01-01
```

### `audit`

Operator CLI for the security audit trail (default-OFF; opt-in via
`[audit] enabled = true`). Three subcommands:

| Subcommand | Behaviour | Exit codes |
|------------|-----------|------------|
| `verify` | Walks the hash chain, confirms every `prev_hash` matches the prior line's `self_hash`. | `0` on integrity, `2` on tamper detection. |
| `tail [--lines N]` | Prints the most recent N events. Default `50`. Filters: `--actor`, `--namespace`, `--action`, `--format text|json`. | `0` |
| `path` | Prints the resolved audit log path. | `0` |

Global flag: `--audit-dir <PATH>` (overrides `AI_MEMORY_AUDIT_DIR` env).

Path precedence: `--audit-dir` flag > `AI_MEMORY_AUDIT_DIR` env >
`config.toml` > platform default. Append-only at the OS level where
supported (`chattr +a` on Linux, `fs_chflags` UF_APPEND on macOS/BSD).

```bash
ai-memory audit verify          # exits 0 on integrity, 2 on tamper
ai-memory audit tail --lines 50
ai-memory audit path
```

See `docs/ADMIN_GUIDE.md` `[audit]` block + compliance presets.

### `bench`

In-process performance harness against an ephemeral SQLite database
(operator's main DB is never touched). Reports per-operation p50/p95/p99
against the budgets in `PERFORMANCE.md`. Used by
`.github/workflows/bench.yml` on every PR; exits non-zero when any
operation's measured p95 exceeds its target by more than 10%.

| Flag | Type | Default | Notes |
|------|------|---------|-------|
| `--iterations` | usize | `bench::DEFAULT_ITERATIONS` | Sample size, clamped to `[1, 100_000]`. |
| `--warmup` | usize | `bench::DEFAULT_WARMUP` | Warmup iterations, clamped to `[0, 10_000]`. |
| `--json` | bool | `false` | Emit a single JSON document instead of the table. |
| `--baseline <PATH>` | string | — | Compare against a saved `--json` baseline; flag regressions. |
| `--regression-threshold <PCT>` | f64 | `bench::DEFAULT_REGRESSION_THRESHOLD_PCT` | Growth threshold for regression flagging (clamped `[0.0, 1000.0]`). |
| `--history <PATH>` | path | — | Append the run as one JSONL row to a history file (rolling p95 trend). |
| `--scale <ROWS>` | usize | — | #1579 B8: seed a scratch corpus of `<ROWS>` rows first and gate against the `PERFORMANCE.md` §"Corpus-scale budgets" table (clamped `[1, 1_000_000]`). |

```bash
ai-memory bench
ai-memory bench --json --history ./bench/history.jsonl
ai-memory bench --baseline ./bench/baseline-v0.6.3.json
ai-memory bench --scale 10000
```

Operations covered by `src/bench.rs`: `memory_store` (no embedding),
`memory_search` (FTS5), `memory_recall` (hot, depth=1),
`memory_kg_query` (depth 1 / 3 / 5), `memory_kg_timeline`. Embedder-bound
operations and federation paths are deferred to Stream E follow-ups —
see the `*[advisory]*` rows in `PERFORMANCE.md`.

### `doctor`

**10-section** health dashboard (v0.7.x post-#1146/#1598): Storage /
Index / Recall / Governance / Sync / Webhook / Capabilities /
Reflection Health / **LLM Reachability (#1146)** / **Embeddings
Reachability (#1598)**. Each section is severity-tagged.

| Flag | Type | Default | Notes |
|------|------|---------|-------|
| `--remote <URL>` | string | — | Query a remote daemon's HTTP API instead of the local DB. |
| `--json` | bool | `false` | Emit JSON instead of the human-readable dashboard. |
| `--fail-on-warn` | bool | `false` | Exit `1` on warnings (default behaviour: exit `0` on warnings). |
| `--tokens` | bool | `false` | v0.6.4-004 — per-tool / per-family / per-profile `cl100k_base` token-cost report instead of the health report. |
| `--profile <PROFILE>` | string | `core` | With `--tokens`: evaluate cost under this hypothetical profile (same vocabulary as `mcp --profile`). |
| `--raw-table` | bool | `false` | Dump the full per-tool size table as JSON. Implies `--tokens`. |
| `--hooks` | bool | `false` | v0.7-G3 — hook-executor backpressure report (loaded `hooks.toml` shape + metric placeholders). |

Exit codes: `0` healthy, `1` warning (only when `--fail-on-warn`), `2`
critical.

```bash
ai-memory doctor
ai-memory doctor --json | jq '.sections[] | select(.severity != "ok")'
ai-memory doctor --remote https://memory.prod.example.com
```

**`LLM Reachability (#1146)` section.** Resolves the canonical LLM
configuration via `AppConfig::resolve_llm` (the same path used by
MCP, HTTP daemon, atomise, curator, boot banner) and probes the
endpoint:

- Ollama backends → `GET <base_url>/api/tags` (no auth)
- OpenAI-compatible backends → `GET <base_url>/models` (Bearer auth)

7-bucket severity partition: INFO = 200 OK; WARN = 401/403 auth
failure, 429 rate-limited, 5xx vendor outage; CRIT = 4xx-other
(likely wrong `base_url`), network/DNS/TLS error. Surfaces the
resolved provenance facts (`backend`, `model`, `base_url`,
`config_source`, `key_source`). See
[`docs/CONFIG_SCHEMA.md`](CONFIG_SCHEMA.md) for the full resolver
contract.

**`Embeddings Reachability (#1598)` section.** The embeddings sibling
of the LLM section. Resolves the canonical embeddings configuration
via `AppConfig::resolve_embeddings` (the same ladder the MCP stdio
init + daemon `build_embedder` consume) and probes:

- `ollama` backend → `GET <url>/api/tags` (no auth)
- API backends → `POST <url>/embeddings` with a 1-char input + the
  resolved Bearer key

Same severity mapping (INFO 2xx; WARN 401/403/429/5xx; CRIT other
4xx / network / DNS) plus the full provenance facts (`backend`,
`model`, `base_url`, `config_source`, `key_source` — never the key
itself). When no operator embeddings configuration exists anywhere
(fresh install), the section is INFO without probing — the tier
preset governs. Additionally fires the **operator GPU-policy WARN**
when the resolved backend is `ollama` on a host with no detectable
NVIDIA GPU (`nvidia-smi -L`): operator policy runs local Ollama
embeddings only on GPU-equipped nodes; CPU-only nodes should use an
API backend (see the enterprise reference architectures).

### `config migrate` (v0.7.x #1146)

One-shot rewrite of legacy v1 `~/.config/ai-memory/config.toml`
(flat fields: `llm_model`, `ollama_url`, `embed_url`,
`embedding_model`, `cross_encoder`, `default_namespace`,
`archive_on_gc`, `archive_max_days`, `max_memory_mb`,
`auto_tag_model`) into the v2 sectioned shape (`[llm]`,
`[llm.auto_tag]`, `[embeddings]`, `[reranker]`, `[storage]`).
Idempotent — running against a v2 file is a no-op INFO log.

| Flag | Type | Default | Notes |
|------|------|---------|-------|
| `--dry-run` | bool | `false` | Print the diff to stderr, no writes. |
| `--also-clean-claude-json` | bool | `false` | Additionally remove `mcpServers.<*>.env` blocks from `~/.claude.json` whose `command` resolves to `ai-memory`. Opt-in — operator verifies the new `config.toml` works first. |

Exit codes: `0` success or already-v2; `1` dry-run (informational);
`2` file not found at `~/.config/ai-memory/config.toml`; `3` parse
error (invalid TOML); `4` write error.

```bash
ai-memory config migrate              # write <file>.bak.<ts> + rewrite
ai-memory config migrate --dry-run    # preview diff, no writes
ai-memory config migrate \
    --also-clean-claude-json          # also strip MCP env block
```

The migrator never lowers security posture or removes secrets.
Inline `[llm].api_key = "<literal>"` in the source file is REJECTED
at v0.7.x parse time — operators must use `api_key_env` or
`api_key_file` instead. See
[`docs/CONFIG_SCHEMA.md`](CONFIG_SCHEMA.md) for the secret-handling
discipline.

## v0.7 feature-gated commands

### `migrate` (`--features sal`)

One-shot bidirectional migration between SAL backends.

| Flag | Type | Default | Notes |
|------|------|---------|-------|
| `--from <URL>` | string | required | `sqlite:///path.db` or `postgres://…` (needs `sal-postgres`). |
| `--to <URL>` | string | required | Same shapes. |
| `--batch` | usize | `1000` (`MIGRATE_BATCH_DEFAULT`) | Page-size **hint**, retained for API compatibility — the current migrator reads one page capped at `MAX_ROWS` (1,000,000) and refuses loudly past it (see `src/migrate.rs`). |
| `--namespace` | string | — | Restrict to one namespace. |
| `--dry-run` | bool | — | Report only, no writes. |
| `--json` | bool | — | Machine-parseable report. |

```bash
ai-memory migrate \
  --from sqlite:///var/lib/ai-memory/ai-memory.db \
  --to postgres://ai_memory:pass@pg:5432/ai_memory \
  --dry-run
```

## v0.7.0 net-new CLI subcommands

These subcommands joined the CLI surface between v0.6.4 and v0.7.0
HEAD `c9472c1`. Source files in `src/cli/` (and `src/cli/commands/`
for the WT-1/QW/Form additions).

### `atomise` — Form 2 / WT-1-C decomposition

Decomposes a long memory into 2-10 atomic propositions. Backs the
`memory_atomise` MCP tool. See [`docs/atomisation.md`](atomisation.md).

### `calibrate confidence` — Form 5 sweep (#758)

Per-source baseline calibration:
`ai-memory calibrate confidence --from-shadow` reads
`confidence_shadow_observations` and emits per-`(namespace, source)`
baselines. Backs `memory_calibrate_confidence`.
See [`docs/confidence-calibration.md`](confidence-calibration.md).

### `export-reflections` — QW-1 file-backed export

Walks the reflection-chain and writes Markdown to
`~/.ai-memory/reflections/<ns>/<id>.md` by default. Pair with
`auto_export_reflections_to_filesystem = true` for hands-off operation.

### `persona` — QW-2 persona-as-artifact

Build a persona from observation-kind memories for a given entity.
Backs `memory_persona` / `memory_persona_generate`.

### `offload` — QW-3 context offload

Move a large blob out of the agent context window. Pairs with the
background `offload_ttl_sweep` worker.

### `expand` — LLM query expansion (#1443)

Expand a free-text query into semantic reformulations via the
configured LLM. Closes the three-surface-parity gap: the same
expansion primitive backs the MCP `memory_expand_query` tool, the
HTTP `POST /api/v1/expand_query` route, and this CLI subcommand — the
expanded-terms set is byte-equal across all three because they share
one code path (`crate::mcp::handle_expand_query`).

```bash
ai-memory expand "neural nets"          # human-readable summary
ai-memory expand --json "neural nets"   # {query, expanded_terms, elapsed_ms, key_source}
```

| Flag | Notes |
|---|---|
| `<QUERY>` | Positional free-text query to expand. |
| `--json` | Emit the raw JSON envelope on stdout (harness consumption). The no-LLM error envelope also lands on stdout under `--json`. |

**Exit codes.** `0` success; `2` no LLM backend configured
(503-equivalent — the expansion primitive is unreachable); `3` an LLM
backend is configured but the call failed (502-equivalent — upstream
error).

**No-Ollama operation.** Query expansion is LLM-backend-agnostic
(post-#1067). An entirely Ollama-free configuration drives expansion
against a cloud backend — e.g. `AI_MEMORY_LLM_BACKEND=openrouter` plus
`AI_MEMORY_LLM_API_KEY` (or the `OPENROUTER_API_KEY` fallback). This is
the no-Ollama path the v0.7.0 LongMemEval reproduction exercised; the
in-process one-shot lets `benchmarks/longmemeval/harness.py` inject LLM
query-expansion without standing up an MCP stdio server or HTTP daemon
per call. The `key_source` field in the envelope echoes which
precedence layer supplied the key (`env` / `config` / `none`) for
harness observability.

### `reembed` — vector-space migration (#1598)

Re-embeds the corpus under the **currently-resolved** embedding
backend/model (`AppConfig::resolve_embeddings` — the same #1598
ladder the daemon's `build_embedder` consumes). This is the operator
tool for switching embedding models: vectors from different models
(or different dims) are not comparable, so after changing
`[embeddings].model` / `AI_MEMORY_EMBED_MODEL` the existing corpus
must be re-embedded into the new vector space.

```bash
ai-memory reembed --dry-run                 # plan only, no writes
ai-memory reembed                            # re-embed every row
ai-memory reembed --namespace prod --json    # scope to one namespace
ai-memory reembed --batch 50                 # smaller batches
```

| Flag | Notes |
|---|---|
| `--namespace <ns>` | Restrict the re-embed to a single namespace. |
| `--dry-run` | Print `{total_rows, rows_missing_embeddings, target_model, target_dim, backend}` and exit without writing. |
| `--batch <n>` | Rows per embedding batch (defaults to the resolved `backfill_batch`). |
| `--json` | Emit the machine-parseable summary envelope on stdout. |

A live run replaces **all** vectors (not just missing ones). Rows
whose batch fails are retried per-row (#1595 resilience); rows that
still fail ("poison rows") are skipped with a WARN naming the row id,
and the run continues — a single un-embeddable row never aborts the
migration. The run ends with a summary (JSON under `--json`).

Pair with `ai-memory doctor` (section "Embeddings Reachability
(#1598)") to verify the target backend is reachable and authenticated
*before* a long re-embed run.

### `identity` — Ed25519 keypair management (H-track)

| Subcommand | Notes |
|---|---|
| `ai-memory identity generate --agent-id <id>` | Create a keypair under `~/.config/ai-memory/keys/<agent_id>.{pub,priv}` (0644/0600). Refuses on an existing id; `--force` opts into rotation. |
| `ai-memory identity import --agent-id <id> --pub <path> [--priv <path>]` | Import a keypair (or public-only handle) written by another tool. |
| `ai-memory identity list` | List enrolled keypairs (never loads private keys). |
| `ai-memory identity export-pub --agent-id <id>` | Print the base64 public key. |

Global flag: `--key-dir <PATH>` overrides the key storage directory
(default: platform config dir; `AI_MEMORY_KEY_DIR` env honoured).

### `verify-signed-events-chain` — V-4 closeout verifier

Walks the `signed_events` cross-row hash chain end-to-end. Supports
`--since N` (skip the first N rows) and `--format text|json`. See
[`docs/signed-events-v4.md`](signed-events-v4.md).

### `schema-init` — postgres + AGE bootstrap (`--features sal`)

Idempotent schema bootstrap for a fresh SAL store by URL
(`--store-url <URL>`; flags: `--json`, `--embedding-dim`, default 384).
Enumerates the resulting catalog (tables, views, functions, indices,
extensions, schema_version); on Postgres with Apache AGE installed it
also bootstraps the `memory_graph` projection — without AGE the
recursive-CTE fallback stays in place automatically. See
[`docs/postgres-age-guide.md`](postgres-age-guide.md).

### `governance` subcommands

| Subcommand | Notes |
|---|---|
| `ai-memory governance migrate-to-permissions` | Dry-run by default; `--apply` to commit the v0.6.x governance → v0.7 permissions migration. Idempotent. |
| `ai-memory governance install-defaults` | Install the operator-signed seed rules `R001..R004`. |
| `ai-memory governance check-action` | Evaluate an action against the live permissions/rules engine (CLI mirror of `memory_check_agent_action`). |

### `rules` subcommand (7th-form, #691)

CRUD over the `governance_rules` table consulted by
`check_agent_action`. Mutation verbs (`add` / `enable` / `disable` /
`remove`) require the operator's Ed25519 keypair on disk at
`<key-dir>/operator.priv` (mode 0600); without it they refuse with
`governance.no_operator_key`. Read verbs (`list` / `check`) are
unprivileged. Global flag: `--key-dir <PATH>`.

| Subcommand | Notes |
|---|---|
| `ai-memory rules keygen` | Generate the operator keypair used to sign rules. |
| `ai-memory rules sign-seed --key <path>` | Sign the seed R001-R004 governance rules with the operator key (rules ship unsigned + disabled by design; sign-seed is the first-time operator-attestation step). |
| `ai-memory rules add …` | Add a signed rule (flags include `--decision`, default `refuse`, and `--namespace`, default the global sentinel). |
| `ai-memory rules list` | List the active rule corpus (CLI equivalent of `memory_rule_list`). |
| `ai-memory rules check …` | Evaluate a hypothetical action against the corpus. |
| `ai-memory rules enable / disable / remove …` | Toggle or delete a rule (operator-signed). |

### `export-forensic-bundle` / `verify-forensic-bundle` — L2-5

Deterministic in-process POSIX-ustar tar with byte-identical mod
timestamps. Operator-signed when an operator keypair is present. See
[`docs/forensic-export.md`](forensic-export.md).

### FX-12 / FX-C3 MCP↔CLI parity subcommands (v0.7.0)

Each is a thin CLI surface over the same substrate handler as its MCP
twin (byte-equal envelopes; `--json` for the raw envelope):

| Subcommand | MCP twin | Notes |
|---|---|---|
| `kg-query` | `memory_kg_query` | Outbound KG traversal from a source memory (≤ 5 hops). |
| `find-paths` | `memory_find_paths` | Enumerate KG paths between two memories (BFS, `max_depth` ≤ 7, default 4). |
| `kg-invalidate` | `memory_kg_invalidate` | Set `valid_until` on a link (non-destructive supersession). |
| `kg-timeline` | `memory_kg_timeline` | Ordered link timeline anchored at a source memory. |
| `recall-observations` | `memory_recall_observations` | List rows from the recall-consumption ledger (#886). |
| `check-duplicate` | `memory_check_duplicate` | Pre-write near-duplicate check (cosine over stored embeddings; requires semantic tier+). |
| `replay` | `memory_replay` | Reconstruct the transcript chain that produced a memory. |
| `reflect` | `memory_reflect` | Synthesize a reflection over source memories (CLI dispatcher runs unsigned / no LLM dedup — use MCP/HTTP for those). |
| `subscribe` / `unsubscribe` / `list-subscriptions` | `memory_subscribe` / `memory_unsubscribe` / `memory_list_subscriptions` | Webhook subscription CRUD. |
| `subscription-replay` / `subscription-dlq-list` | `memory_subscription_replay` / `memory_subscription_dlq_list` | Webhook DLQ replay + inspection. |
| `notify` / `inbox` | `memory_notify` / `memory_inbox` | Agent-to-agent inbox send / read. |
| `ingest-multistep` | `memory_ingest_multistep` | Form 3 multi-step ingest (CLI passes no LLM handler; tier-locked advisory on every tier). |
| `entity-register` / `entity-get-by-alias` | `memory_entity_register` / `memory_entity_get_by_alias` | Entity registry. |
| `dependents-of-invalidated` | `memory_dependents_of_invalidated` | Memories citing invalidated KG edges. |
| `reflection-origin` | `memory_reflection_origin` | Walk a reflection back to its origin chain. |
| `quota-status` | `memory_quota_status` | K8 per-agent quota row. |

### `recover-previous-session` — #1389 L2

Fail-safe recovery of agent context from a host's per-turn transcript
file after an ungraceful session end (SIGKILL, tmux lockup, host
crash). Designed for SessionStart-hook chaining after `ai-memory boot`;
the in-session counterpart is the `memory_recover_previous_session`
MCP surface.

### Additional v0.7.0 subcommands

| Subcommand | Notes |
|---|---|
| `deref <ref_id>` | QW-3 — dereference a previously-offloaded blob; refuses tampered rows (SHA-256 mismatch). Pairs with `offload`. |
| `share` | #1095 — copy a memory into `_shared/<from>→<to>/` (same primitive as `memory_share` / `POST /api/v1/share`). |
| `skill <register\|list\|get\|resource\|export\|promote\|compose>` | Cluster E API-2 (#767) — CLI parity for the 7 `memory_skill_*` MCP tools. |
| `namespace <set-standard\|get-standard\|clear-standard\|batman-policy>` | #800 — operator CRUD for the per-namespace standard policy pointer. |
| `verify-reflection-chain <memory_id>` | L1-3 — walk `reflects_on` edges to depth 0, verify each Ed25519 signature, emit a chain-integrity report. Distinct from `verify-signed-events-chain` and `audit verify`. |

## Shell, completions, man

```bash
ai-memory shell                              # interactive REPL
ai-memory completions bash > ~/.bash_completion.d/ai-memory
ai-memory completions zsh  > ~/.zsh/completions/_ai-memory
ai-memory man | man -l -
```

## Agent identity (NHI)

Every memory stamps `metadata.agent_id`. Resolution order:

1. Explicit `--agent-id` CLI flag.
2. `AI_MEMORY_AGENT_ID` env var.
3. (MCP only) `initialize.clientInfo.name` → `ai:<client>@<host>:pid-<pid>`.
4. `host:<hostname>:pid-<pid>-<uuid8>` (per-process stable).
5. `anonymous:pid-<pid>-<uuid8>` (no hostname).

Validation: `^[A-Za-z0-9_\-:@./]{1,128}$`. Once stored, preserved
across update / upsert / import / sync / consolidate.

For production deployments, always set `--agent-id` or
`AI_MEMORY_AGENT_ID` to an opaque identity. The default fallback
leaks hostname + PID.

The ladder above is the **write-path** 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*
separately: `AI_MEMORY_AGENT_ID` if set, else `None` (trust-all). The
pid-synthesized clientInfo id is **not** used there — it embeds the live
PID and could never match a prior process's `metadata.agent_id`. Set
`AI_MEMORY_AGENT_ID` to filter cross-agent private rows out of read
results (#1468 / #1469); leave it unset for single-tenant trust-all reads.

## Tiers & TTL at a glance

| Tier | TTL | Typical use |
|------|-----|-------------|
| `short` | 6 hours | Session context, scratch. |
| `mid` | 7 days | Normal memories (default). |
| `long` | permanent | Important records. |

Accessing a memory extends TTL (short +1 h, mid +1 d). At 5 accesses,
mid auto-promotes to long.

## See also

- `docs/QUICKSTART.md` — first memory in under 5 minutes.
- `docs/API_REFERENCE.md` — HTTP endpoint reference.
- `docs/USER_GUIDE.md` — MCP tool reference + agent integration.
- `docs/ADMIN_GUIDE.md` — deployment, security, observability.
- `docs/TROUBLESHOOTING.md` — common errors.
