# Local memory

> Save notes, findings and rules on your machine for coding agents to recall, search them, and retire or purge them.

Save findings, decisions and rules for agents to recall, without an account.

## Prerequisites

- The Blop CLI ([Quickstart](/quickstart/)). Memory commands work without a running collector.

## What is stored

`memory.db` sits beside the telemetry store (`blop status` prints its path). It holds authored nodes and read-only folder projections, not captured telemetry:

- **Kinds:** `entity`, `document`, `finding`, `decision`, `rule` or `procedure`.
- **Authored revisions:** every authored change makes a new immutable revision; writes must name the revision they expect. Markdown projection retains only its current indexed snapshot.
- **Relations and sources:** typed node links and supporting trace/session references. Unsourced nodes are claims, not evidence.

Opening memory never uploads anything. To share knowledge with a team, see [Organization memory](/cloud/organization-memory/).

## Add and read memory

```bash
blop memory add --kind decision --title "Round two probe" --body "the flags work"
blop memory search --text "Round two probe"
```

Default ID: `local:` plus lowercase SHA-256 of exact UTF-8 `JSON.stringify([title, body])`.
Identical retries preserve revisions. See `blop help memory add`.

1. Write the change as JSON:

   ```json
   {
     "type": "add",
     "id": "note:checkout-timeout",
     "scope": "project",
     "projectId": "my-app",
     "expectedRevision": 0,
     "content": {
       "kind": "finding",
       "title": "Checkout times out under load",
       "body": "POST /api/pay exceeds 5 s when the payment stub is cold."
     }
   }
   ```

2. Add it:

   ```bash
   blop memory add --input add.json --json
   ```

3. Find it again:

   ```bash
   blop memory search --text checkout --project my-app --json
   ```

| Command | Purpose |
| --- | --- |
| `blop memory search --text <text>` | Exact text search in titles and bodies, in stable node-ID order |
| `blop memory get <node-id>` | One node; `--revision N` for an older one, `--body-offset` and `--body-length` for long bodies |
| `blop memory history <node-id>` | Revision list with previews |
| `blop memory neighbors <node-id>` | Recorded relations |
| `blop memory context --text <text>` | A bounded brief for an agent |
| `blop memory edit`, `patch`, `retire`, `restore`, `purge` | Change a node with an `--input` JSON file |
| `blop memory skills list`, `skills get <id>` | Saved procedures |
| `blop memory index` | Build vectors for search by meaning |

Read commands take `--json` and return bounded pages; follow the returned `--cursor`. Coding agents connected over MCP get the same reads as read-only tools ([Connect a coding agent](/guides/connect-agent/#what-the-mcp-server-gives-the-agent)); writes always go through the CLI.

## Attach Markdown knowledge

No account, collector, model or network:
Attachment/indexing never rewrite authoritative Markdown:

```bash
blop memory markdown init ./knowledge # Optional template; never overwrites.
blop memory markdown attach ./knowledge --json # Save attachment ID.
blop memory markdown index <attachment-id> --json
blop memory search --text checkout --json
blop memory get <node-id> --json
blop memory neighbors <node-id> --json
blop memory context --text checkout --json
blop memory markdown list --json
blop memory markdown detach <attachment-id> --json
```

Reindex after edits/deletes; replacement is atomic. Detach removes retrieval/vectors,
not files or authored assertions. Missing endpoints hide relations; restored IDs
recover original approval provenance. **`memory index` remains optional vector/model
indexing.** `--project` configures attachment scope only on `markdown attach`;
unrelated flags, including `--dry-run`, fail.

### Identity and authority

- Arbitrary layouts with `.md`, `.mdown` or `.markdown` work without frontmatter.
  Optional scalar `id: checkout-policy` preserves note identity across renames;
  duplicate IDs fail. Without an ID, rename means delete/add.
- Whole notes and ATX/Setext heading sections are addressable. Anchors lowercase
  Unicode letters/numbers, retain `_`/`-`, remove other punctuation and replace
  whitespace with `-`. Duplicates receive the first unused numeric suffix;
  heading edits/reordering may change section IDs.
- Relative Markdown/reference-style links and `[[relative/path#Heading]]`
  create unapproved **`references`**, not causal assertions. Wikilinks may omit
  `.md`; no global basename matching. Code creates no links. Unresolved/external
  links produce warnings; URLs, images and HTML are never fetched/executed.
- Projections are unapproved documents. Frontmatter grants no policy, guards,
  scope or upload permission. Edit source and reindex; authored mutations fail.
  [Publication](/cloud/organization-memory/#publish-local-memory) copies one selected
  snapshot, never syncs. Projection `history` is empty; `get --revision` addresses
  current only, otherwise `null`.

Reads preserve node/page envelopes and include path/section/hash/snapshot,
indexing time and `freshness`: `indexed-snapshot` means last successful scan,
not live filesystem freshness; `last-scan-failed` labels retained prior content.
There is no watcher. Local CLI/MCP assessment is explicitly `not_configured`,
not “assessed without findings.” MCP exposes no Markdown writes; its existing
collector health announcement contains no note content.

### Safety and evidence

Initialization requires Linux with accessible `/proc/self/fd`; other platforms
return `markdown_init_unsupported`, never unsafe pathname writes. Directory handles
anchor exclusive creation against symlink swaps. Attach/index reject symlink
roots/ancestors and exclude child symlinks. Hidden entries, `node_modules`, `dist`,
`build`, `vendor`, `coverage`, `secrets` and `config` are ignored. `.blopignore`
accepts literal relative prefixes with `#` comments, not globs.

Limits: 100,000 bytes/file, 16,000,000 bytes/scan, 20,000 entries, depth 32,
10,000 nodes, 200 link occurrences/file (including whole-note copies),
100 attachments. Paths cap at 1,024 characters; headings at 512. Exact citations
allow 1,029-character `sourceId` (`path:` prefix) and 1,537-character `path`
(path + `#` + heading), without truncation. Invalid/oversized sources or detected
concurrent changes preserve the prior snapshot; successful empty scans remove it.
Scan checks are not locks; pause concurrent writers.

Optional frontmatter `sources` uses the existing reference contract:

```yaml
---
sources:
  - namespace: otlp:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    sourceId: "1111111111111111"
    recordedAt: "2026-11-06T00:00:00.000Z"
    plane: runtime
    accessibility: unknown
---
```

This addresses an exact trace+span; `otlp.trace` with a trace ID addresses the
trace. `opencode:<encoded-session-id>` keeps exact source-ID semantics. Only
existence metadata is read: present is `local-only`, missing is `unavailable`;
absent stores/tables, unknown namespaces or unverifiable hash/revision constraints
are `unknown`. Existence is not verification. No hosted run or upload is implied.

## Browse memory in the Inspector

Open `/inspector?view=memory` on the collector, for example `http://127.0.0.1:27811/inspector?view=memory&project=my-app&q=checkout`. The view is read-only. Filter with `project`, `q` (text), `kind`, `status` (`active` or `retired`) and `semantic=1` (rank by meaning).

## Search by meaning

```bash
blop memory index
```

Search by meaning needs an index you build explicitly. The first index or search downloads a local embedding model and caches it; your notes and queries are never uploaded. Reindex after changes: a `stale` index state means vectors cover older revisions. If the index is not ready, search falls back to exact text. Similarity is not evidence, and ranking never creates a relation.

## Delete memory

Retire a node to hide it from search while keeping its history:

```bash
echo '{"type":"retire","nodeId":"note:checkout-timeout","expectedRevision":1}' > retire.json
blop memory retire --input retire.json
```

`restore` brings a retired revision back. To erase a node's content, revisions and index entries, purge it at its current revision:

```bash
echo '{"type":"purge","nodeId":"note:checkout-timeout","expectedRevision":2}' > purge.json
blop memory purge --input purge.json
```

A purged ID cannot be reused. Purging is logical deletion: database write-ahead pages and backups are not erased at once. Telemetry is deleted separately, with [`blop delete`](/guides/investigate/#delete-local-evidence).

## Check it worked

`blop memory get note:checkout-timeout --json` returns the node with its `revision`, and `null` after a purge.

## Next

- [Skills](/guides/skills/): instructions your agent loads for a task.
- [Organization memory](/cloud/organization-memory/): share memory with your team.
