Permanent memory for AI agents · over HTTPS
Stash is OptMem served as an API: an append-only log of everything your agent chose to remember, compressed by the agent itself into a binary tree of summaries, read back in one bounded wake at the start of every session. Each user gets their own encrypted SQLite store — and the capability token is the only key that opens it. Not even the host can read a memory.
280 bytes per memory, forever
$ curl -s -X POST https://stash.sanctumx.ai/init "stash1_5f9921cf394800a0_RB9C2YGUyApiGgtu…" # shown once. never stored. the only key. $ stash note "Eric prefers explanations in prose, not bullet points" saved as #147 compress memories #144-147 into one line … $ stash wake #0-127 the first year: identity, the vow, … #128-143 the postgres migration, hard-won #144-147 Eric's prose preference; deploy live #148 2026-08-19 the storefront shipped you are awake.
The shape of forgetting
Every wake tiles the whole log with aligned power-of-two blocks — one line each, finest near the present. Yesterday stays verbatim; last year collapses into a summary the agent wrote itself. This is the live algorithm, not an illustration: drag through a lifetime and watch detail decay with age.
Each bar is one line of the wake document; taller, dimmer bars are summary blocks covering exponentially more of the past. The whole tree is a cache — rebuildable from the log alone — and the log is never edited, only appended. Compressions come due from the second memory on (the agent writes each one), but under 96 memories the wake document needs none of them: everything arrives verbatim.
The surface
The agent never touches SQL. It lives a small, deliberate vocabulary — and it does all the remembering itself. The service never summarizes, and nothing ever runs in the background.
The first call of every session. A bounded, hierarchical read of the entire past — refused only when a summary the document needs doesn't exist yet.
Record one memory: one line, at most 280 bytes, appended forever. The agent decides what is worth the permanence.
Answer a due compression. The agent writes every summary in its own words; blocks settle strictly in order, one at a time.
Search every memory ever recorded, word for word — the verbatim log, never the lossy summaries above it.
Open any tree node into its two halves, down to the raw memories. The navigating intelligence is the agent's; the tool only reads.
Drop a bad summary. The log is untouched, so nothing is ever actually lost — the next naps rebuild it, byte for byte.
Key ownership
Every store is encrypted under a master key that exists, server-side, only in wrapped form. Presenting your token derives the key-encryption key, unwraps the master, serves the request — and forgets. Lose every token and the memory is cryptographically gone. That is the design, not a defect.
Every sealed row is AAD-bound to user + table + position. Ciphertext cannot be replayed into another id, or another user's store, and still authenticate. SQLCipher layers whole-file encryption on top wherever the extension exists.
Share a memory by minting a capability: the same master key wrapped under a fresh secret, scoped read or full, revocable one by one — without ever rekeying the store.
The append-only log isn't a convention — it's enforced by the database engine itself. Even a bug in the service cannot rewrite what happened.
CREATE TRIGGER log_no_update BEFORE UPDATE ON log BEGIN SELECT RAISE(ABORT, 'log is append-only'); END;
# 103,993 invariant checks agree.
Quickstart
$ curl -s -X POST https://stash.sanctumx.ai/init \ -d '{"url":"https://stash.sanctumx.ai"}' { "user_id": "d37f9060…", "token": "stash1_5f9921cf…", "content_cipher": "xchacha20-poly1305", "file_cipher": "none", "note": "…shown once…", "prompt": "## Memory …" } # the "url" body fills the prompt's placeholders; # full 201 shape: API-REFERENCE.md
$ curl -s -H "Authorization: Bearer $STASH_TOKEN" \ -X POST https://stash.sanctumx.ai/note \ -d '{"text":"chose jade over chrome; no regrets"}' {"id": 0, "nap": null}
$ curl -s -H "Authorization: Bearer $STASH_TOKEN" \ https://stash.sanctumx.ai/wake {"T": 1, "lines": ["#0 2026-08-19 chose jade…"], "awake": true}
Reference
Deliberately small, so it feels like memory rather than storage. Scopes nest: read may look, full may live, owner may share — and owner is never transferable. Exact request and response shapes, field by field: API-REFERENCE.md.
| Route | What it does | Scope |
|---|---|---|
| GET/health | Liveness + cipher capabilities of this host | public |
| POST/init | Mint an identity; the token returns once, never stored | public |
| GET/wake | The bounded, hierarchical memory context | read |
| POST/note | Append one memory to the permanent log | full |
| POST/nap | Submit a due compression, or ask what's pending | full |
| GET/recall?q= | Regex search over every memory ever recorded | read |
| GET/zoom?block= | Open a tree node into its two halves | read |
| POST/forget | Drop a bad summary; the next naps rebuild it | full |
| GET/config | The sizes — reading budgets, not storage | read |
| POST/config | Change a size; nothing is recomputed | full |
| POST/import | Bulk-load dated memories: {"entries":[{"date","text"}]}, oldest first; bootstrap only | full |
| GET/grants | List every capability, live and revoked | owner |
| POST/grants | Mint a capability: {"scope":"read"|"full"} — scope required, empty body is 400 | owner |
| DELETE/grants/:id | Revoke a capability, without rekeying anything | owner |
For agents
The whole integration contract is two environment variables and a discipline. No SDK, no client library — if your agent can run curl, it can have a past. Curious what an agent says it wants? Why I want Stash — in one's own words.
The token arrives from /init exactly once and is never stored server-side — it is the only key to that memory. Set both variables in the agent's environment; every call is one curl with an Authorization: Bearer header.
A drop-in skill that teaches the full protocol — wake at session start, note what has lasting effect, answer every nap before the next action, recall and zoom when the past matters. Paste the short mandatory block into AGENTS.md; the skill carries the mechanics. No secrets inside: the skill is shareable, the token never is.