Skip to main content
Glama
README.md
# memlink

**Your Claude memory, everywhere.** memlink syncs Claude Code's memory files from your PC to an encrypted cloud store, and serves them to Claude on your phone (or any MCP client) — even while the PC is off. Whatever the phone writes lands back on your disk as plain `.md` files.

*Citește în [română](./README.ro.md).*

```
   PC (Claude Code)                    cloud                       phone (Claude app)
   ~/.claude/**/*.md   ──encrypt──▶  memlink server  ◀──MCP────  memory_search / write
         ▲                            (zero plaintext)                    │
         └───────────── memlink watch pulls changes back ◀───────────────┘
```

## Quickstart

```bash
npx memlink-cli init --server https://memlink-omega.vercel.app --email you@example.com
# prompts for a passphrase (min. 12 characters, twice) - it never leaves your machine

npx memlink-cli watch
# watches your Claude memory folders, pushes changes, pulls what other devices wrote
```

Then connect Claude:

- **Phone / claude.ai:** Settings → Connectors → *Add custom connector* → URL `https://memlink-omega.vercel.app/mcp`. Claude opens memlink's authorization page: enter your email and passphrase (the key is derived **in your browser**; the passphrase is never sent), and the connector is live — with `memory_search`, `memory_get`, `memory_write`, `memory_list`.
- **Claude Code (another machine):** `npx memlink-cli login --server ... --email ...`, then
  `claude mcp add --transport http memlink https://memlink-omega.vercel.app/mcp --header "Authorization: Bearer <output of memlink token>"`.

`memlink sync` runs one push/pull round if you prefer manual control.

## The security model — honest, both sentences

> "My database is useless if stolen, and I never store the key. The code is public — verify it."

- Notes are encrypted **on your machine**: Scrypt key derivation + XChaCha20-Poly1305 (`@noble/hashes`, `@noble/ciphers` — audited libraries). File paths are encrypted too. The server stores zero plaintext and never stores your key.
- **The limit, stated plainly:** this is NOT end-to-end encryption. To answer an MCP client the server decrypts in RAM per request — Anthropic's cloud receives plaintext in responses, otherwise Claude would have nothing to read. The key transits inside your token and lives in server RAM only for the duration of a request.
- OAuth 2.1 (PKCE + dynamic client registration), fully stateless: access tokens are sealed payloads; rotating the server's seal key revokes everything at once.
- More plainly-stated limits: the derived key sits cleartext in `~/.memlink/credentials.json` (0600 on POSIX; unprotected on Windows), and `memlink token` prints a secret granting full access to your corpus — treat both like passwords.

## Conflicts: nothing is ever lost

Last-write-wins per note, with optimistic versions. The losing version is always preserved in `~/.claude/memory-conflicts/` — same for notes deleted from the cloud (moved there, not destroyed) and for anything suspicious (undecryptable or path-escaping notes are quarantined, never written over your files).

## Self-hosting

The server is a single Vercel function backed by Supabase Postgres:

1. Create a Supabase project (or reuse one) and run `supabase/migrations/0001_init.sql` — optionally inside a dedicated schema, then expose it in PostgREST and set `MEMLINK_DB_SCHEMA`.
2. Set Vercel env vars: `SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`, `MEMLINK_SEAL_KEY` (64 random bytes, base64 — rotating it revokes all tokens), `MEMLINK_BASE_URL` (your deployment URL), optionally `MEMLINK_DB_SCHEMA`.
3. `vercel deploy --prod` from the repo root (the build bundles everything).

## Development

npm workspaces: `packages/shared` (crypto + wire format), `packages/server` (Hono API + MCP + OAuth), `packages/cli`. `npm test` (vitest), `npm run typecheck`. The CLI speaks ten languages (en native + ro, da, de, fr, es, it, pt, nl, pl) via `MEMLINK_LANG` or your system locale.

## License

MIT — see [LICENSE](./LICENSE).