{"id":51223070,"url":"https://github.com/ij5a/subrosa","last_synced_at":"2026-07-16T19:01:03.028Z","repository":{"id":364308630,"uuid":"1266739057","full_name":"ij5a/subrosa","owner":"ij5a","description":"Persistent, private memory for Claude Code — every session archived locally, searchable. Sub rosa.","archived":false,"fork":false,"pushed_at":"2026-06-20T21:21:33.000Z","size":1296,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-20T22:03:27.318Z","etag":null,"topics":["claude","claude-code","claude-code-plugin","fts5","memory","rust","sqlite"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ij5a.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-11T22:59:31.000Z","updated_at":"2026-06-20T21:21:37.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ij5a/subrosa","commit_stats":null,"previous_names":["ij5a/subrosa"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/ij5a/subrosa","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ij5a%2Fsubrosa","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ij5a%2Fsubrosa/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ij5a%2Fsubrosa/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ij5a%2Fsubrosa/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ij5a","download_url":"https://codeload.github.com/ij5a/subrosa/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ij5a%2Fsubrosa/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35555509,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-16T02:00:06.687Z","response_time":83,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["claude","claude-code","claude-code-plugin","fts5","memory","rust","sqlite"],"created_at":"2026-06-28T09:00:31.116Z","updated_at":"2026-07-16T19:01:03.020Z","avatar_url":"https://github.com/ij5a.png","language":"Rust","funding_links":[],"categories":["Source Catalog"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/subrosa-wordmark-animated.svg\" alt=\"subrosa\" width=\"100%\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  Persistent, private memory for Claude Code — that never spends your tokens on itself.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/ij5a/subrosa/releases/latest\"\u003e\u003cimg src=\"https://img.shields.io/github/v/release/ij5a/subrosa\" alt=\"latest release\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/ij5a/subrosa/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/ij5a/subrosa/ci.yml?branch=main\" alt=\"CI status\"\u003e\u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/github/license/ij5a/subrosa\" alt=\"MIT license\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nEvery session is archived into a local SQLite database and made searchable: last month's work is one `subrosa search` away, relevant past sessions resurface when you type a related prompt, and Claude stops rediscovering what it already figured out. Pulling up an old answer costs a few hundred tokens; having Claude re-derive it from scratch runs into the thousands.\n\n- **No LLM calls to save memory.** Saving a session is plain-text parsing — zero tokens. Most memory plugins run your sessions through an LLM to save them ([the comparison](docs/comparison.md) has the numbers, from their own docs).\n- **Hard token limits, set in the code.** Recall adds ~180 tokens on a strong match, usually nothing — it stays silent otherwise. The always-loaded index is capped at 23 KB. [Check it yourself](#proof-verify-it-yourself), or see [where your tokens go](docs/faq.md#how-many-tokens-does-it-cost-me).\n- **One ~4 MB static binary.** No daemon, no worker port, no background process — a hook fires, finishes in under 10 ms, and exits.\n- **Your transcripts stay on your machine.** The binary makes zero network calls — no cloud, no telemetry — and obvious secret shapes are masked before storage. [Verify every claim yourself](#proof-verify-it-yourself).\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/demo.gif\" alt=\"subrosa demo: search the archive, automatic recall on a prompt, dashboard\" width=\"800\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#why-subrosa\"\u003eWhy \"subrosa\"?\u003c/a\u003e ·\n  \u003ca href=\"#quick-start\"\u003eQuick start\u003c/a\u003e ·\n  \u003ca href=\"#what-it-does\"\u003eWhat it does\u003c/a\u003e ·\n  \u003ca href=\"#commands\"\u003eCommands\u003c/a\u003e ·\n  \u003ca href=\"#the-memory-workflow\"\u003eThe memory workflow\u003c/a\u003e ·\n  \u003ca href=\"#make-claude-use-the-archive-itself\"\u003eMake Claude use the archive itself\u003c/a\u003e\u003cbr/\u003e\n  \u003ca href=\"#where-your-data-lives\"\u003eWhere your data lives\u003c/a\u003e ·\n  \u003ca href=\"#privacy-model\"\u003ePrivacy model\u003c/a\u003e ·\n  \u003ca href=\"#proof-verify-it-yourself\"\u003eProof\u003c/a\u003e ·\n  \u003ca href=\"#performance\"\u003ePerformance\u003c/a\u003e ·\n  \u003ca href=\"#development\"\u003eDevelopment\u003c/a\u003e ·\n  \u003ca href=\"docs/comparison.md\"\u003eHow it compares\u003c/a\u003e ·\n  \u003ca href=\"docs/faq.md\"\u003eFAQ\u003c/a\u003e\n\u003c/p\u003e\n\n## Why \"subrosa\"?\n\nIn ancient Rome, a rose over the table meant everything said under it stayed in the room — *sub rosa*. That's the contract here: every transcript stays on your machine, in a local database, secret shapes masked before storage. No cloud, no telemetry.\n\n## Quick start\n\nInside Claude Code, run these two commands:\n\n```\n/plugin marketplace add ij5a/subrosa\n/plugin install subrosa@subrosa\n```\n\nThen start a new Claude Code session (quit and reopen, or run `claude` again). That's the whole install:\n\n- On first start, the plugin downloads the right prebuilt program for your computer (~2 MB, checksum-verified against this repo) and archives every session already on your disk.\n- After that it runs itself: archives each session as you work and again when it ends, shows Claude related past sessions when you prompt, and notes when ended sessions are waiting to be saved into long-term memory.\n\nThe one-time download fetches the program only — your data never moves.\n\nOne more command, for the best experience — it teaches Claude to work the archive on its own: search it at the start of any task, and clear the checkpoint backlog when sessions queue up ([what it adds](#make-claude-use-the-archive-itself); safe to re-run, ~250 tokens):\n\n```sh\n~/.claude/subrosa/bin/subrosa init --claude-md   # or just: subrosa init --claude-md, with the CLI below\n```\n\n### Optional: the `subrosa` command in your terminal\n\nThe plugin works without this. Install the CLI to also search and manage the archive yourself:\n\n```sh\n# homebrew\nbrew install ij5a/tap/subrosa\n\n# or the install script (prebuilt, checksum-verified)\ncurl -fsSL https://raw.githubusercontent.com/ij5a/subrosa/main/install.sh | sh\n\n# or from source, if you have Rust\ncargo install --git https://github.com/ij5a/subrosa\n```\n\nThe plugin finds a binary on PATH and uses it, so both stay in sync. Two useful first commands:\n\n```sh\nsubrosa setup    # one-time: pick where backup snapshots mirror to (iCloud / Dropbox / ... / none)\nsubrosa          # the dashboard\n```\n\n## What it does\n\n- **Archives every session.** When one ends — quitting Claude Code, `/clear`, or logging out — its transcript is saved to a local SQLite (full-text search) database, with a catch-up sweep at the next start.\n- **Recalls on its own.** A prompt with enough distinctive terms pulls the top matching past sessions from the same project into context — silent unless the match is strong.\n- **Builds long-term memory.** Ended sessions queue up; `/subrosa:checkpoint` and `/subrosa:checkpoint-backlog` distill them into curated facts (one per small markdown file). `subrosa generate` then renders `MEMORY.md` — a short, size-capped index Claude Code loads every session, and `subrosa fact search` finds a specific fact once a project has dozens.\n- **Makes everything searchable.** `subrosa search \u003cterms\u003e` runs ranked full-text search — identifiers like `my-app-prod` or `TICKET-123` stay exact. Narrow with `--project`/`--after`/`--before`/`--tag`, read around a hit with `-C/--context`, filter with `--exclude` (drop a term) and `--any` (match any term, not all), or fall back to `--fuzzy` for partial names and typos.\n- **Lists and filters sessions.** `subrosa sessions` shows past sessions newest-first, filterable by `--project`, date (`--after`/`--before`), and `--tag`. Each session carries auto-derived tags (`tool:bash`, `ext:rs`, `topic:cache-prod`) — computed locally at archive time, so they cost zero tokens and nothing to maintain.\n- **Shows what clusters together.** `subrosa related \u003cidentifier\u003e` ranks the terms and sessions that recur alongside something like `auth.ts` or `TICKET-123` — read from the archive, not guessed. It answers \"what did this work touch,\" which `search` can't.\n- **Follows your curated links.** Notes link to each other with `[[name]]`; `subrosa fact link \u003cslug\u003e` shows what a note links to and what links back, and flags dead links.\n- **Shows you the picture.** `subrosa` alone prints the dashboard: activity sparkline, store size, per-project share, index budget.\n- **Backs itself up.** Consistent snapshots on a 24h throttle, plus an optional mirror of the latest to a folder you pick.\n- **Masks secrets at the door.** Private key blocks, AWS keys, bearer tokens, and `password=`-style values are redacted before they're written.\n\n## Commands\n\n```sh\nsubrosa                                  # dashboard (same as: subrosa stats)\nsubrosa search aurora failover           # find that thing from three weeks ago\nsubrosa search --project api deploy      # scope to one project\nsubrosa search -n 30 --raw 'cache OR redis'\nsubrosa search --fuzzy ratelimiter       # substring/typo matching (builds a trigram index on first use)\nsubrosa search deploy --after 2026-05-01 # only turns from May onward (--before too; YYYY-MM-DD, inclusive)\nsubrosa search api --tag tool:kubectl    # only sessions that used a given tool (repeat --tag to AND)\nsubrosa search pgbouncer -C 2            # print 2 turns on each side of every hit (read around the match)\nsubrosa search timeout --exclude test    # drop hits that also mention a term (repeat --exclude to add more)\nsubrosa search redis valkey --any        # match any of the terms (OR) instead of all (AND)\nsubrosa related cache-prod               # terms + sessions that co-occur with an identifier\nsubrosa related TICKET-123 --project api # what clustered around it, scoped to one project\n\nsubrosa sessions                         # past sessions, newest first, with their tags\nsubrosa sessions --tag topic:cache-prod --after 2026-05-01   # filter by tag and/or date\nsubrosa session \u003cid\u003e --tags              # dump a session and show its auto-derived tags\n\nsubrosa fact list                        # curated facts for the current project\nsubrosa fact search pgbouncer            # full-text search the curated facts (bm25-ranked)\nsubrosa fact upsert --leaf note.md       # add/update a fact from a markdown file (one fact per file)\nsubrosa fact link auth-decision          # show [[name]] links into/out of a fact (flags dead links)\nsubrosa generate                         # rebuild MEMORY.md (byte-budgeted)\nsubrosa import ~/.claude/projects/\u003cproject\u003e/memory   # one-time import of an existing MEMORY.md\n\nsubrosa session \u003cid\u003e                     # dump one archived session (full id or unique prefix)\nsubrosa pending                          # sessions queued for checkpointing\nsubrosa checkpoint-drop \u003cid\u003e             # de-queue one session after saving it\n\nsubrosa sweep                            # catch up on changed transcripts\nsubrosa backup --force                   # snapshot now\nsubrosa setup                            # one-time backup-mirror question\n```\n\n## The memory workflow\n\n```mermaid\nflowchart TD\n    ended[\"session ends\u003cbr/\u003equit, /clear, or log out\"] --\u003e archived[\"archived into the local database\u003cbr/\u003e+ queued for checkpoint\"]\n    archived --\u003e nudge[\"next session start\u003cbr/\u003ean action-required note lands in Claude's context:\u003cbr/\u003eN sessions queued for checkpoint\"]\n    nudge --\u003e backlog[\"/subrosa:checkpoint-backlog\u003cbr/\u003esaves durable facts from each queued session\"]\n    livesession[\"live session\u003cbr/\u003ebefore /clear or /compact\"] --\u003e checkpoint[\"/subrosa:checkpoint\u003cbr/\u003esaves durable facts from the current session\"]\n    backlog --\u003e facts[\"curated facts\u003cbr/\u003eone file per fact + the facts database\"]\n    checkpoint --\u003e facts\n    facts --\u003e|subrosa generate| memorymd[\"MEMORY.md\u003cbr/\u003ebyte-budgeted, loaded every session\"]\n    archived -.-\u003e|stays searchable either way| recall[\"subrosa search + automatic prompt recall\"]\n```\n\n1. A session ends → it's archived and queued.\n2. At the next start, an `ACTION REQUIRED` note lands in Claude's context (`[subrosa] ACTION REQUIRED — N session(s) queued for checkpoint…`) — hook output goes to Claude, not your chat window, so Claude acts on it (or you run the next step yourself). The same reminder then rides each prompt until the queue clears, so a busy first task can't bury it. Prefer the calmer one-liner? Set `checkpoint_nudge=quiet` (or `off`) — see [Where your data lives](#where-your-data-lives).\n3. Run `/subrosa:checkpoint-backlog` — Claude saves the important facts from each queued session into that project's memory, then rebuilds `MEMORY.md`.\n4. Before `/clear` or `/compact`, run `/subrosa:checkpoint` to do the same for the live session.\n\n`MEMORY.md` is built under a byte budget — important facts (pinned, feedback) win when space runs out, and everything that doesn't fit stays searchable in the archive.\n\n## Make Claude use the archive itself\n\nRecall only fires when you type a prompt. To have Claude work the archive on its own, add these standing instructions to your `CLAUDE.md` (`~/.claude/CLAUDE.md` covers every project, a repo's own covers just that one) — or run `subrosa init --claude-md` to append them for you (idempotent, and it adds only the sections you're missing).\n\nThe first has Claude search the archive at the start of any task:\n\n```markdown\n## Memory recall (subrosa)\n\nEvery past Claude Code session is archived locally and searchable with\n`subrosa search \"\u003ckeywords\u003e\"` — scope with `--project \u003cname\u003e`, narrow by date or\ntag with `--after`/`--before`/`--tag`, more results with `-n 20`, and retry with\n`--fuzzy` if an exact search finds nothing (partial names, typos).\n(If `subrosa` isn't on PATH, it's at `~/.claude/subrosa/bin/subrosa`.)\nAt the start of any task — investigating, debugging, designing, reviewing, or when\na ticket, environment, resource, person, or past decision comes up — search the\narchive first and build on what past sessions already worked out instead of\nstarting cold. Announce the search (\"Searching past sessions for [topic]...\") and\ncite hits with their date. Skip only for trivial one-liners. `MEMORY.md` is\ngenerated — never hand-edit it; update facts with `subrosa fact` + `subrosa generate`,\nor run `/subrosa:checkpoint`.\n```\n\nThe second has Claude clear the checkpoint backlog in the background when sessions queue up:\n\n```markdown\n## Memory auto-checkpoint (subrosa)\n\nWhen a `[subrosa] ACTION REQUIRED` note says sessions are queued for checkpoint\n(or `subrosa pending` is non-empty), run the `/subrosa:checkpoint-backlog` skill\nin the background — never before or blocking the task you're working on. It saves\nthe durable facts from each queued session into that project's memory, then clears\nthe queue as it finishes. Skip it silently when nothing is queued.\n```\n\nTogether they add about 250 tokens of always-loaded context — your call. The first makes Claude write its own searches mid-task; the second keeps ended sessions from piling up unsaved.\n\n## Where your data lives\n\n| What | Where | Why |\n|---|---|---|\n| Live database | `~/.claude/subrosa/memory.db` | Not in synced folders — cloud sync corrupts a live SQLite database (its WAL/SHM helper files) |\n| Snapshots | `~/.claude/subrosa/backups/` | Last 7 kept, owner-only permissions |\n| Mirror | the folder you picked in `subrosa setup` | A single static snapshot file is safe to sync |\n| Checkpoint queue | `~/.claude/subrosa/pending-checkpoint.log` | Plain text, one session per line |\n\nEverything is overridable with env vars: `SUBROSA_DIR`, `SUBROSA_DB`, `SUBROSA_PROJECTS_DIR`, `SUBROSA_PENDING_LOG`, `SUBROSA_MIRROR`, `SUBROSA_CHECKPOINT_NUDGE`.\n\nTwo settings also live in `~/.claude/subrosa/config` (plain `KEY=VALUE`): `mirror` (the snapshot folder, or `none`) and `checkpoint_nudge` — `loud` (default, the `ACTION REQUIRED` block), `quiet` (a one-line reminder), or `off`. The matching env var wins when set.\n\n## Privacy model\n\n- **Local-only.** The binary makes zero network calls; recall reads only your local database, and hook output goes only into your own session.\n- **Locked down.** The database and its folder are readable only by you (`0600`/`0700`), and secret shapes are redacted before storage.\n- **One opt-in exit.** The only thing that can leave the machine is a backup-mirror snapshot, if you point it at a synced folder (off by default). The live database is never synced.\n\nFull limits — what redaction misses, why the archive isn't encrypted, what recall re-injects — are in the [FAQ](docs/faq.md#what-does-subrosa-not-protect).\n\n## Proof: verify it yourself\n\nClaims are only worth the commands that check them. The whole thing is ~7,800 lines of Rust, MIT licensed.\n\n| Claim | Check it | What you'll see |\n|---|---|---|\n| Token limits are constants in the code | read `MAX_INJECT` + `SNIPPET_CHARS` in `src/recall.rs`, `DEFAULT_BUDGET` in `src/generate.rs` | `MAX_INJECT = 3`, `SNIPPET_CHARS = 160` (≈ 180 tokens at recall), and a 23 KB index budget — values you can read, not settings that drift |\n| Recall stays near ~180 tokens a prompt | `scripts/bench.sh` — the `recall injection` line | the injected block weighed in bytes → ~180 tokens on a strong match (3 snippets) |\n| The binary makes zero network calls | `cargo tree -e normal \\| grep -Ei 'reqwest\\|hyper\\|tokio\\|rustls\\|openssl\\|curl'` | no output — no HTTP or networking library in the build (trace it live with `strace`/`dtruss` and see no `connect()`) |\n| The supply chain is small and audited | `cargo tree --depth 1` · `cargo audit` | 5 direct dependencies, no known advisories; CI runs `cargo audit` on every push, and release binaries ship a pinned `sha256sums.txt` |\n\nMore checks (redaction, file permissions, no background process) and the honest limits are in the [FAQ](docs/faq.md#what-does-subrosa-not-protect).\n\n## Performance\n\nA hook that runs on every prompt has to be invisible. Measured with `scripts/bench.sh` (hyperfine, synthetic 50,000-turn / 28 MB archive, Apple M3 Max):\n\n| Operation | Time |\n|---|---|\n| Prompt recall check, no match — the usual case | ~4 ms |\n| Prompt recall check, match found and injected | ~14 ms |\n| A full hook fire as Claude Code runs it (shell wrapper + binary) | under 10 ms |\n| Session-start catch-up sweep, nothing changed | ~5 ms |\n| Live-session ingest after a turn (one transcript, flat at any session length) | ~7 ms |\n| `subrosa search` over 50,000 turns | 5–11 ms |\n| `subrosa related \u003cidentifier\u003e` over 50,000 turns | 0.3–0.4 s |\n| Archiving 50,000 turns from scratch (first install) | ~1.1 s |\n\nOne static ~4 MB binary, no background process, no runtime dependencies.\n\n## Development\n\n```sh\nmise install                          # pinned Rust toolchain (mise.lock)\ngit config core.hooksPath .githooks   # once per clone: sweep + fmt + clippy + tests on commit\ncargo build \u0026\u0026 cargo test\n```\n\nThe output formats (stored text, session dump, `MEMORY.md`, recall block) are pinned byte-for-byte by the golden tests in `tests/` — a failing golden means a deliberate format change. Point everything at a throwaway dir so you never touch live data:\n\n```sh\nSUBROSA_DIR=/tmp/x SUBROSA_PROJECTS_DIR=/tmp/x/projects cargo run -- init\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fij5a%2Fsubrosa","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fij5a%2Fsubrosa","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fij5a%2Fsubrosa/lists"}