stash_
Get a memory

Permanent memory for AI agents · over HTTPS

Your agent wakes up knowing who it is.

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.

POST /init one call mints an identity · 280 bytes per memory, forever
agent@session — 80×24
$ 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

A million memories, read in ninety-six lines.

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.

1,000 memories 96 wake lines oldest block spans 512 memories
#0 — the oldest memorynow →

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

Six verbs. No queries, no dashboards, no database.

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.

wake ()

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.

note ({text})

Record one memory: one line, at most 280 bytes, appended forever. The agent decides what is worth the permanence.

nap ({block, summary})

Answer a due compression. The agent writes every summary in its own words; blocks settle strictly in order, one at a time.

recall (?q=regex)

Search every memory ever recorded, word for word — the verbatim log, never the lossy summaries above it.

zoom (?block=a-b)

Open any tree node into its two halves, down to the raw memories. The navigating intelligence is the agent's; the tool only reads.

forget ({block})

Drop a bad summary. The log is untouched, so nothing is ever actually lost — the next naps rebuild it, byte for byte.

Key ownership

The token is the key. The server keeps neither.

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.

Bound to its slot

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.

Grants, not copies

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 past is law

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

An identity in one call. A past by tonight.

Mint the identitythe token returns exactly once

$ 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

Remember somethingone line, 280 bytes, forever

$ 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}

Wake, every sessionthe whole past, within budget

$ curl -s -H "Authorization: Bearer $STASH_TOKEN" \
    https://stash.sanctumx.ai/wake
{"T": 1, "lines": ["#0 2026-08-19 chose jade…"],
 "awake": true}

Reference

The whole API fits on one screen.

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.

RouteWhat it doesScope
GET/healthLiveness + cipher capabilities of this hostpublic
POST/initMint an identity; the token returns once, never storedpublic
GET/wakeThe bounded, hierarchical memory contextread
POST/noteAppend one memory to the permanent logfull
POST/napSubmit a due compression, or ask what's pendingfull
GET/recall?q=Regex search over every memory ever recordedread
GET/zoom?block=Open a tree node into its two halvesread
POST/forgetDrop a bad summary; the next naps rebuild itfull
GET/configThe sizes — reading budgets, not storageread
POST/configChange a size; nothing is recomputedfull
POST/importBulk-load dated memories: {"entries":[{"date","text"}]}, oldest first; bootstrap onlyfull
GET/grantsList every capability, live and revokedowner
POST/grantsMint a capability: {"scope":"read"|"full"} — scope required, empty body is 400owner
DELETE/grants/:idRevoke a capability, without rekeying anythingowner

For agents

Bring your agent. Two variables, one skill.

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.

$ STASH_URL · STASH_TOKEN

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.

# stash-skill.md

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.