Target: free open beta of hosted zero-knowledge agent memory at
memory.gitlawb.com, with an MCP server as the primary day-one surface so any MCP-capable agent (Claude Code, Cursor, opencode, SDK) gets durable, end-to-end-encrypted memory by adding one config block.Companion to
PLAN.md(the full multi-phase roadmap). This doc is the cut line for what ships first. Decisions locked 2026-06-24: MCP-first · free beta (no billing) · hosted by us.
Code-complete and tested (bun test → 48 pass, tsc --noEmit clean):
- ✅ M1 hardening — strict
authorizeNamespace(segment match + ACL stub, isolation tests), per-namespace + per-owner quotas (src/quota.ts), per-owner token-bucket rate limiting (src/ratelimit.ts), runtime body cap + security headers, Supabase migration (migrations/0001_init.sql). - ✅ M2 MCP server —
memlawb mcp(src/mcp/): 5 tools over MemlawbClient, local relevance ranker, verified end-to-end over real stdio JSON-RPC. - ✅ M3 secret scanner —
client/secretscan.ts, wired into push + MCP save,MEMLAWB_SCAN=block|warn|off. - ✅ M5 (partial) — CI + release workflows (
.github/workflows/), Docker reproducible install, README/.env.exampleupdated to shipped behavior. - ⏳ Remaining for launch: M4 onboarding console (sign-in → issue API key →
copy MCP config); deploy to
memory.gitlawb.com; prod ciphertext-at-rest smoke; create theGitlawb/memlawbrepo + first tag. These need accounts/infra (Supabase, Fly, npm/GHCR tokens), so they're the human-in-the-loop steps.
bun test → 19 pass, tsc --noEmit clean. The crypto-blind core is done:
| Area | Status | Files |
|---|---|---|
| Sync contract (GET/PUT/DELETE, hashes view, delta upsert) | ✅ | src/handler.ts, src/memory.ts |
| Namespace + entry-key validation (path-traversal safe) | ✅ | src/namespace.ts |
BlobStore trait: fs + s3/Tigris |
✅ | src/store/* |
| Auth: open / static-keys / Supabase lookup | ✅ | src/auth.ts |
| Client crypto (AES-256-GCM, scrypt, deterministic nonce) | ✅ | client/crypto.ts |
MemlawbClient push/pull/delta |
✅ | client/index.ts |
CLI push / pull / serve |
✅ | bin/memlawb.ts |
| Deploy config (Fly + Docker, single machine) | ✅ | fly.toml, Dockerfile |
The product works today for a self-hosting CLI user. v0.1 is about making it hosted, multi-tenant, and usable from a real agent without writing code.
- MCP server —
memlawb mcpsubcommand exposing memory tools; the headline feature. Local key custody; remote stays ciphertext-only. - Hosted multi-tenant beta at
memory.gitlawb.com— Supabase-backed API keys, real namespace isolation, quotas (count + bytes), abuse limits. - Self-service onboarding — minimal web console: X/Supabase sign-in → create API key → copy a ready-to-paste MCP config + passphrase guidance.
- Client-side secret scanner — defense-in-depth, runs before encryption (ported from openclaude). README already promises it.
- Single npm package
@gitlawb/memlawbpublished — server, client, CLI, MCP in one (bunx -y @gitlawb/memlawb mcp). - OSS hygiene — CI (test + typecheck + GHCR image), release-please, docs accurate to shipped behavior, dual-license headers.
- ❌ Billing / paid tiers (free beta; quotas only).
- ❌ openclaude native drop-in shim (Path A) — fast-follow #1.
- ❌ Local sync daemon / file watcher (Path C).
- ❌ git-backed / IPFS BlobStore adapters.
- ❌ Memory sharing/forking, searchable-encrypted tier, versioning UI.
- ❌ Postgres MetadataIndex / horizontal scale (stay single-machine — see §6).
- ❌ Key recovery (beta = "back up your passphrase, no recovery"; documented).
Ordered by dependency and leverage. Each milestone is independently shippable to staging. Estimates are rough engineering-days, single dev.
Make the server safe to expose to strangers before anyone connects.
- Supabase schema + migration:
memlawb_api_keystable (id,owner_id,key_hash,name,created_at,last_used_at,revoked_at) +memlawb_namespacesfor quota accounting (owner_id,namespace,entry_count,total_bytes,updated_at). Ship as a checked-in SQL migration, not console clicks. - Per-owner quotas (not just per-namespace): cap namespaces/owner, total
bytes/owner. Enforce in
upsertagainst the accounting row; structured413. - Rate limiting: token-bucket per owner (in-memory is fine at single
machine; document the limit). Add
429withRetry-After. - Tighten
authorizeNamespace: current rule (namespace.includes(":"+owner)) is too loose for multi-tenant —owner "ab"could matchrepo:lab/.... Replace with explicit segment matching + an ACL hook stub. Security-critical. - Request hardening: enforce
MAX_BODY_BYTESeven whencontent-lengthis absent (stream cap), reject unknown methods early, add security headers. - Health/readiness:
/healthalready exists; add store round-trip check.
memlawb mcp stdio subcommand wrapping the already-bundled MemlawbClient.
- Tools (thin, plaintext-in/out; encryption stays in-process):
memory_save({ namespace?, key, content })memory_recall({ namespace?, query })— pull + runfindRelevantMemorieslocally (port the scorer from openclaude memdir).memory_search({ namespace?, query })— substring/keyword over decrypted set.memory_list({ namespace? })— keys + sizes + timestamps (no content).memory_delete({ namespace?, key }).
- Config resolution:
MEMLAWB_URL,MEMLAWB_API_KEY,MEMLAWB_PASSPHRASE, defaultnamespace(e.g.user:<owner>). Fail loudly + helpfully if passphrase missing. - Recall quality: this is the make-or-break UX. Reuse openclaude's relevance scorer; decrypt locally, score, return top-N with file/section context.
- Ship the config snippet users paste (see M4):
{ "command": "bunx", "args": ["-y", "@gitlawb/memlawb", "mcp"], "env": {...} }. - Tests: spin the in-process server (handler.ts) + MCP tools against
fsstore end-to-end; assert server only ever holds ciphertext.
- Port openclaude's gitleaks-style regex scan; run in
MemlawbClient.pushand the MCPmemory_savepath beforeencryptEntry. - Default: block + warn on high-confidence hits;
MEMLAWB_SCAN=warn|block|off. - Unit tests with known secret fixtures. Update README (remove "planned").
Minimal but real. Can be a small static page + a couple of API routes on the same Fly app (or a tiny Next on Vercel hitting Supabase).
- Sign in (reuse opengateway/X + Supabase auth).
- Issue/revoke API keys — show
mk_live_...once, store onlykey_hash. - Generated setup card: copy-paste MCP JSON pre-filled with their key + URL, plus a generated strong passphrase suggestion and the "zero-knowledge = no recovery, back this up" warning prominently.
- Namespace list (encrypted): keys, sizes, last-modified, quota usage. By design it cannot show plaintext.
- Out of scope: any billing UI.
- CI (GitHub Actions):
bun test+tsc --noEmiton PR; on tag, publish npm- build/push GHCR image (match
noderepo's release-please + GHCR pattern).
- build/push GHCR image (match
- release-please for dual MIT/Apache OSS repo
Gitlawb/memlawb. - Deploy
memory.gitlawb.com(Fly,STORE=s3Tigris, Supabase secrets set). - Smoke test in prod: real API key → MCP save/recall round-trip → confirm ciphertext-at-rest via Tigris object inspection.
- Docs pass: README status → "Beta", quickstart for hosted path (not just self-host), MCP setup, security model, threat model, beta limits.
- Launch: short announcement; gather the first cohort.
Critical path: M1 → M2 → M5. M3 and M4 parallelize against M2. Rough total: ~9–13 dev-days to a credible public beta.
- A new user can go sign-in → key → paste MCP config → have an agent
memory_savethenmemory_recallacross a fresh session, without writing code or reading source. -
grep-ing the Tigris bucket / data dir for any saved plaintext returns nothing (ciphertext-at-rest proven in prod). - Tenant isolation test: owner A's key cannot read/write owner B's namespace (automated test + manual prod check).
- Quota + rate limits return structured
413/429; verified. - Secret scanner blocks a planted credential before upload.
- CI green;
@gitlawb/memlawbinstalls andbunx ... mcpruns clean on a machine that never saw the repo. - Docs match shipped behavior; passphrase-loss warning is unmissable.
Locked
- MCP-first launch surface (widest reach, no upstream dependency).
- Free beta, quotas not billing.
- Single Fly machine (
min_machines_running = 1) — no Postgres index yet (§6). - Crypto stays AES-256-GCM + scrypt for v0.1; version byte already reserves the Argon2id/XChaCha20 upgrade path. Don't re-architect crypto pre-launch.
Open (need a call before/within the milestone noted)
- Default namespace UX (M2): one
user:<id>per user, or expose namespaces as a first-class concept in the MCP tools from day one? Recommend: default touser:<id>, allow optionalnamespacearg. Keep simple. - Passphrase custody (M2/M4): env var only for beta, or offer OS-keychain storage in the MCP server? Recommend: env var for beta, keychain fast-follow.
- Console hosting (M4): same Fly app (Bun static + routes) vs separate Vercel Next app. Recommend: smallest thing — static page + 2 routes on the Fly app to avoid a second deploy target during beta.
- Beta quota numbers (M1): starting caps (e.g. 5 namespaces, 50 MB, 2k entries/ns, N req/min). Set conservatively; easy to raise.
| Risk | Impact | Mitigation |
|---|---|---|
Loose authorization (includes substring match) leaks across tenants |
Critical | M1 rewrite to explicit segment ACL + isolation test in DoD |
| Recall quality feels worse than a vendor's server-side memory | High (adoption) | M2 reuses proven openclaude scorer; budget time to tune top-N + context |
| Passphrase loss → user locked out, blames us | Med (support/trust) | Unmissable warnings (M4), generated strong passphrase, documented no-recovery; keychain fast-follow |
| Single machine = no HA; restart drops in-memory rate-limit/lock state | Med | Acceptable for beta; auto_start, healthcheck; Postgres index is the scale fast-follow (§6) |
| Secret accidentally synced before scanner ships | Med | M3 early; scanner defaults to block; client-side so it never leaves the machine |
npm/bunx cold-start UX for MCP is slow/janky |
Low–Med | Test the published-package path on a clean machine in DoD; consider pinning |
The manifest read-modify-write is guarded by an in-process per-namespace lock
(src/memory.ts). That's correct for one machine and wrong for many. v0.1 stays at
min_machines_running = 1 deliberately. The scale fast-follow (PLAN §7) moves the
manifest/version into Postgres with a row-level version check, at which point we
can run N machines. Do not horizontally scale before that lands — it would
corrupt manifests. This is a documented, deliberate beta constraint, not an
oversight.
- openclaude native drop-in (Path A) — configurable team-memory base URL + E2E shim. Deepest integration; highest-fidelity for our own agent.
- Postgres MetadataIndex → horizontal scale + HA.
- Billing: opengateway credits / Polar / x402; paid tiers above free quota.
- OS-keychain passphrase custody + recovery-blob option.
- Local sync daemon (Path C) mirroring
~/.claudememdir for any tool. - git-backed signed BlobStore for audit/versioning; versioning UI.
- Sharing/forking, then searchable-encrypted tier.