memhouse 0.18.8 → 0.18.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/AGENTS.md +194 -0
  2. package/CHANGELOG.md +970 -0
  3. package/README.md +54 -423
  4. package/SECURITY.md +68 -0
  5. package/bin/memhouse.js +202 -92
  6. package/docs/README.md +46 -0
  7. package/docs/guides/admin-password.md +95 -0
  8. package/docs/guides/configuration.md +109 -0
  9. package/docs/guides/daemons.md +53 -0
  10. package/docs/guides/install.md +128 -0
  11. package/docs/guides/invite.md +87 -0
  12. package/docs/guides/operate.md +112 -0
  13. package/docs/guides/search-and-resume.md +95 -0
  14. package/docs/guides/share.md +102 -0
  15. package/docs/guides/storage.md +90 -0
  16. package/docs/guides/troubleshooting.md +78 -0
  17. package/docs/guides/uninstall.md +35 -0
  18. package/docs/guides/update.md +68 -0
  19. package/docs/releases/README.md +10 -0
  20. package/docs/releases/v0.18.10.md +226 -0
  21. package/docs/tutorials/01-first-house.md +132 -0
  22. package/docs/tutorials/02-join-a-house.md +91 -0
  23. package/docs/tutorials/03-share-your-memory.md +96 -0
  24. package/examples/01-local-one-machine/README.md +99 -0
  25. package/examples/01-local-one-machine/run.sh +61 -0
  26. package/examples/02-existing-clickhouse/README.md +121 -0
  27. package/examples/02-existing-clickhouse/run.sh +71 -0
  28. package/examples/03-team-house/README.md +129 -0
  29. package/examples/03-team-house/run.sh +124 -0
  30. package/examples/04-self-hosted-team/.env.example +22 -0
  31. package/examples/04-self-hosted-team/README.md +187 -0
  32. package/examples/04-self-hosted-team/compose.yml +158 -0
  33. package/examples/04-self-hosted-team/smoke.sh +113 -0
  34. package/examples/README.md +42 -0
  35. package/examples/fixtures/README.md +24 -0
  36. package/examples/fixtures/make-sessions.js +63 -0
  37. package/memhouse/admin-secret.js +81 -0
  38. package/memhouse/delivery/plugin/.claude-plugin/plugin.json +1 -1
  39. package/memhouse/deploy.js +4 -2
  40. package/memhouse/flags.js +4 -4
  41. package/memhouse/provision.js +12 -3
  42. package/memhouse/server/queries.js +18 -4
  43. package/memhouse/server/server.js +9 -1
  44. package/memhouse/service.js +5 -0
  45. package/package.json +10 -2
package/README.md CHANGED
@@ -1,452 +1,83 @@
1
1
  # memhouse
2
2
 
3
- **Claude Code deletes your transcripts after 30 days.** (`cleanupPeriodDays`,
4
- default 30 — session files are removed at startup.) Measured on the machine this
5
- was built on: months of daily use, and the oldest surviving local transcript was
6
- nine days old. Every problem you solved before that exists nowhere — unless
7
- something durable was keeping it.
3
+ **Your coding agents forget. Their transcripts get deleted too.** Claude Code removes
4
+ session files after 30 days (`cleanupPeriodDays`); on the machine this was built on, the
5
+ oldest surviving local transcript was nine days old. Every problem solved before that
6
+ exists nowhere, unless something durable kept it.
8
7
 
9
- memhouse is that something. It reads the session transcripts your editors already
10
- write to disk — **17 of them**: Claude Code, Codex, Cursor, Zed, Copilot, Gemini
11
- CLI and the rest — parses them locally, and ships typed rows into a ClickHouse
12
- **you choose**: a container on this laptop, a box you run, your company's cluster,
13
- or ClickHouse Cloud. memhouse runs no service of its own — there is no memhouse
14
- cloud to sign up for and nothing is proxied through anyone — so the account is
15
- yours and the data is wherever you decided to put it. Then you can search every
16
- past session, see what it cost, resume the conversation that solved this before,
17
- and let an agent query its own history.
8
+ memhouse keeps it. It reads the session transcripts your editors already write to disk
9
+ (16 adapters covering 18 apps: Claude Code, Codex, Cursor, Zed, Copilot, Gemini CLI and
10
+ the rest), parses them locally, and ships typed rows into a ClickHouse **you choose**. That
11
+ can be a container on this laptop, a box you run, your company's cluster, or ClickHouse
12
+ Cloud. memhouse runs no service of its own and proxies nothing. Then you, and your agents,
13
+ can search every past session, see what it cost, and reopen the one that solved this
14
+ before.
18
15
 
19
- And every row says where it came from, in a way no client can fake: `user_id` is
20
- stamped **by the server** (`MATERIALIZED currentUser()`, async inserts pinned off
21
- so the stamp cannot be skipped) and `host` is a fingerprint minted once per
22
- install. A team pointed at one house is a team memory with real attribution — not
23
- a label somebody's client claimed.
16
+ Every row says where it came from, in a way no client can fake. `user_id` is stamped by
17
+ the server (`MATERIALIZED currentUser()`), and `host` is a fingerprint minted once per
18
+ install. A team pointed at one house gets a shared memory with real attribution.
24
19
 
25
- ```bash
26
- npm install -g memhouse
27
- memhouse onboard
28
- ```
29
-
30
- That is the whole install — no flags, nothing to compile. `onboard` finds your
31
- editors, sets up a house, ships, and starts the dashboard.
32
-
33
- **On most Linux distros that first line needs `sudo`.** A distro-packaged Node puts
34
- its global prefix under `/usr`, which you do not own, so npm stops with `EACCES`.
35
- That is the single most likely place a first install stalls — measured on a clean
36
- Ubuntu machine:
37
-
38
- ```bash
39
- ls -ld "$(npm prefix -g)" # root-owned? use sudo. Owned by you? do NOT — see EACCES below
40
- sudo npm install -g memhouse
41
- ```
42
-
43
- If the prefix is yours (Homebrew, fnm, nvm, volta), `sudo` is the wrong fix and
44
- makes it worse — [EACCES on install](#eacces-on-install) tells them apart.
45
-
46
- ## No ClickHouse yet?
47
-
48
- `onboard` offers to run one for you if docker or podman is present:
49
-
50
- ```bash
51
- memhouse deploy --local # a loopback-bound ClickHouse, then install and ship
52
- ```
53
-
54
- The house it stands up is **yours by name**: its superuser defaults to your OS
55
- username (`polat`, not `memhouse_root` — override with `--user`), and the house
56
- database defaults to the same name, so your memory lives at `polat.messages` and
57
- `WHERE user_id = 'polat'` reads like it should. The name
58
- and password are fixed when the data volume is first initialised, like any ClickHouse
59
- container, and reused on every redeploy after that.
60
-
61
- Otherwise point at one you already run, or hand the SQL to whoever administers it:
62
-
63
- ```bash
64
- memhouse install --url https://… --user … --password … # one you have
65
- memhouse install --admin-user … --admin-password … # let memhouse build it
66
- memhouse install --print-sql # print the SQL, run it yourself
67
- ```
68
-
69
- memhouse does not embed a database. If you have neither a ClickHouse nor a
70
- container engine, install one of the two.
71
-
72
- ## Commands
73
-
74
- ```text
75
- memhouse onboard | install | setup | discover | doctor | uninstall | reset
76
- memhouse ship [--full|--loop N] | search <terms> | stats | status
77
- memhouse resume <session-id> print the command that reopens a session
78
- memhouse update [--check] [--channel TAG] upgrade along the channel this install follows,
79
- restart daemons, check the schema
80
- memhouse start | stop dashboard + shipper as daemons
81
- memhouse service install | uninstall survive a reboot
82
- memhouse deploy --local | --down stand up (or remove) a local house
83
- memhouse invite <name> mint a member + house, hand them one env file
84
- memhouse members [--db X] who is in a house, and what each can reach
85
- memhouse whoami [--admin] which credential is in play, and what it may do
86
- memhouse instance which memhouse this is: home, binary, house, rooms, scope, bindings
87
- memhouse rooms what your rooms are actually called
88
- memhouse share <user> [--only …|--revoke|--list]
89
- let a housemate read yours, in whole or in part
90
- memhouse sessions-query print the session rollup SQL
91
- memhouse passwd rotate this member's password
92
- memhouse migrate | migrate-rooms [--dry-run] [--yes]
93
- run whatever this house still needs
94
- memhouse relocate --to <url> copy this house to a new ClickHouse, then repoint
95
- memhouse nightly [--out DIR] build an installable tarball from this checkout
96
- memhouse plugins install claude 5 skills — house, recall, sql, access,
97
- admin — into every Claude Code config
98
- dir found
99
- memhouse prompt memory snippet for an agent's system prompt
100
- memhouse prompt --install an install prompt, rendered for this machine
101
- ```
102
-
103
- Any command's own usage: `memhouse <command> --help` (or `-h`) — always prints usage,
104
- never runs the command.
105
-
106
- Every command works both ways: interactive for you, `--yes` / flags / `--json` for
107
- an agent — so an agent can install its own memory unattended.
108
-
109
- `onboard` offers the skills at the end, and `plugins` works across **every Claude Code
110
- config directory on the machine** — `~/.claude`, whatever `CLAUDE_CONFIG_DIR` points
111
- at, and each Kommander-style playbook under `~/.claude-playbooks/`. All are selected by
112
- default; answer with numbers to narrow it, or pass `--target DIR` for exactly one.
113
- Installing into just the default config is how `/mem:recall` ends up missing from
114
- the instance you actually work in, silently, since a missing skill never announces
115
- itself.
116
-
117
- Config resolves: flags → `MEMHOUSE_*` env → `$MEMHOUSE_HOME/env` (default
118
- `~/.memhouse/env`). There is no house-shaped default: with nothing configured, the
119
- commands that read or write memory refuse and say so rather than guessing
120
- `localhost:8123`, which on a lot of machines is a real house belonging to someone else.
121
-
122
- ## Which sessions ship
123
-
124
- Everything on the machine, by default: every adapter, from wherever that editor keeps its
125
- sessions. `memhouse discover` prints what each adapter is watching and the variable that
126
- moves it:
127
-
128
- ```
129
- Watched directories (override with the variable shown, in the env file):
130
- claude ~/.claude, ~/.claude-playbooks/kommander, … [MEMHOUSE_CLAUDE_ROOTS]
131
- codex ~/.codex [MEMHOUSE_CODEX_ROOTS]
132
- gemini-cli ~/.gemini [MEMHOUSE_GEMINI_CLI_ROOTS]
133
- cursor ~/Library/Application Support/Cursor/User, … [built-in]
134
- ```
135
-
136
- Two kinds of line in the env file narrow it, or `--editors` / `--claude-roots` at install:
137
-
138
- ```
139
- MEMHOUSE_EDITORS='claude'
140
- MEMHOUSE_CLAUDE_ROOTS='~/.claude-playbooks/kommander-chaos'
141
- ```
142
-
143
- `MEMHOUSE_EDITORS` names the adapters to run. `MEMHOUSE_<EDITOR>_ROOTS` replaces one
144
- adapter's location: the adapter's name upper-cased, dashes to underscores. Claude is the
145
- one adapter with many roots (every Claude Code config directory), so its list may hold
146
- several; every other adapter has one store, so one path. Adapters marked `[built-in]`
147
- resolve a platform app-data directory and cannot be moved yet.
148
-
149
- An adapter name or a directory that does not exist is refused and reported against that
150
- adapter, never shipped from the default instead: a typo that quietly shipped nothing would
151
- look like a working install with an empty house, and one that quietly shipped the wrong
152
- store would be worse.
153
-
154
- ## Channels
155
-
156
- `memhouse update` follows the npm dist-tag the install came from, not `latest` by
157
- reflex. A house published under another tag — `npm publish --tag team`, so one team can
158
- run a newer layout without every other house being dragged along — installs with
159
- `npm install -g memhouse@team`, and `update` keeps it there. The channel is inferred from
160
- the installed version; pin it with `MEMHOUSE_CHANNEL='team'` in the env file or
161
- `memhouse update --channel team`, which writes the same line. A tarball or checkout
162
- build sits on no tag and is never auto-updated; `update --check` says so and names the
163
- way forward. An invite from a house on a channel carries it, so the invitee follows it
164
- too.
165
-
166
- ## Instances
167
-
168
- An instance is a home: an env file naming a house, the daemons shipping to it, and the
169
- playbooks whose sessions it ships (`MEMHOUSE_CLAUDE_ROOTS`). A machine can run several,
170
- each under its own `MEMHOUSE_HOME`, on different channels. `plugins install claude` from an
171
- instance installs into exactly the playbooks that instance ships and **binds** them: it
172
- writes `MEMHOUSE_HOME` and `MEMHOUSE_BIN` into each playbook's `settings.json` `env`, which
173
- Claude Code applies to every session there. So the `/mem:*` skills in a bound playbook read
174
- that instance's house and run that instance's binary, whatever the shell they were started
175
- from had. `plugins list` shows the binding; `plugins remove` takes it out. Everything else
176
- in `settings.json` is left as it was.
177
-
178
- ## Going back into a session
179
-
180
- `search` finds the conversation; `resume` hands you back into it.
181
-
182
- ```bash
183
- memhouse resume claude:6b1f… # prints: cd /path/to/project && claude --resume 6b1f…
184
- ```
185
-
186
- It **prints** the command instead of running it. A resume run from the wrong directory
187
- or with a stale id does not fail — it opens a *new* session, and the transcript you
188
- wanted is still gone while the tool reports success. You paste it, so you see it first.
189
-
190
- Verified for **claude, codex and opencode**. Cursor, Zed, VS Code, Kiro, Copilot for
191
- JetBrains, Antigravity and Devin/Windsurf have no CLI that takes a session id, so
192
- there is nothing to resume into and `resume` says exactly that, rather than opening a
193
- folder and calling it the same thing. Everything else — goose, gemini-cli,
194
- cursor-agent, copilot-cli — is refused as *unverified*, which is a different sentence
195
- on purpose: nobody has read the flag out of its own `--help` yet, and guessing is how
196
- you print a command that quietly does the wrong thing. See `memhouse/resume.js`.
197
-
198
- ## Which machine is which
199
-
200
- All of *your* machines ship into *your* rooms, and the `host` column is what tells them
201
- apart. So each install writes `~/.memhouse/host.json` — a random fingerprint, minted once
202
- — and every row that machine ships carries `<hostname>-<8 hex>` built from it.
203
-
204
- Random, not derived, because deriving it fails in both directions: two laptops that both
205
- answer to the default `MacBook-Pro` on the same platform hash to the **same** id and merge
206
- into one apparent machine, and renaming a machine **moves** a derived id so its own history
207
- appears to stop and a stranger's to begin. A stored fingerprint cannot collide and does not
208
- move — rename the machine and `status` says so while the id stays put.
209
-
210
- ```bash
211
- memhouse status # ✓ host: macminim-4127a95b
212
- memhouse doctor # names every host in your rooms, and whether this one is among them
213
- ```
214
-
215
- Then query one machine with `WHERE host = 'macminim-4127a95b'`.
216
-
217
- ## Uninstalling, in three tiers
218
-
219
- ```bash
220
- memhouse uninstall # stop everything; KEEP the config and this host's identity
221
- memhouse uninstall --credentials # also forget the house and its password
222
- memhouse uninstall --full-removal # all of ~/.memhouse, identity included
223
- ```
224
-
225
- The default is deliberately the conservative one. Stopping the shipper is routine — before
226
- an upgrade, while debugging, when a laptop should go quiet for a week — and it should not
227
- also mean "forget which house I use and who this machine is". `--full-removal` asks first,
228
- because a later reinstall mints a **new** identity and this machine then reads as two
229
- machines in your history.
230
-
231
- **No tier touches your memory.** Transcripts live in ClickHouse, and everything the shipper
232
- wrote rebuilds from the local session stores with `ship --full`.
233
-
234
- ## Staying current
20
+ ## Install
235
21
 
236
22
  ```bash
237
- memhouse update # upgrade, restart the daemons, check the house schema
238
- memhouse update --check # compare versions, change nothing
239
- ```
240
-
241
- A bare `npm i -g memhouse@latest` does half the job. It does not restart the daemons,
242
- which keep parsing with the code they booted with (a shipper here once ran 1d16h out of
243
- a directory that had been *moved*); and it does not check whether your house predates a
244
- room the new shipper needs. `update` does both, and from a checkout it rebuilds
245
- `public/` — otherwise `git pull` serves you the previous release's dashboard forever.
246
-
247
- The daemons also notice on their own: each one compares the installed version against
248
- what it booted with and hands over — exiting under systemd/launchd so the supervisor
249
- restarts it, re-execing itself when nothing is supervising. `update` just makes it
250
- immediate.
251
-
252
- ## How your memory is stored
253
-
254
- **A house is a database; its rooms are tables.** `sessions`, `messages` and `tool_calls`
255
- hold your turns. Every row says where it came from: `user_id`, stamped by the server
256
- (`MATERIALIZED currentUser()`, with async inserts pinned off so the stamp cannot be
257
- skipped), and `host`, the machine's install fingerprint. `WHERE user_id = 'alice'` is one
258
- person; `WHERE host = '…'` is one machine. Neither is forgeable from the client.
259
-
260
- **Your rooms are named for you, and one grant covers them.** Every member of a house holds
261
- `GRANT … ON mem.<name>_* TO <name>`: their own rooms, present and future, and nothing else
262
- in the database. That is the whole access model, and it is the same on a laptop, a team's
263
- server, or a kernel:
264
-
265
- | | rooms | the member holds |
266
- |---|---|---|
267
- | alone on your laptop | `mem.polat_*` | one grant on `mem.polat_*` |
268
- | a team on one server | `mem.polat_*`, `mem.alice_*`, … | each their own |
269
-
270
- A colleague can read their isolation back with `SHOW GRANTS` in one line, which is the
271
- property this rests on. There is no row policy to trust and no database-wide grant to
272
- worry about: `ALL ON mem.*` would cover rooms created later, which is how two people in one
273
- database once became a leak, so memhouse never issues it — not to a member, not to you.
274
-
275
- Standalone is a team of one. `memhouse deploy --local` creates the house with an admin
276
- credential (`memhouse_root`) and a member named after your OS user, keeps both in the env
277
- file, and ships as the member. Inviting a colleague later is `memhouse invite <name>`; the
278
- admin credential is already there.
279
-
280
- Reading across everyone is a `UNION ALL` over the rooms you hold:
281
-
282
- ```sql
283
- SELECT user_id, count() FROM mem.polat_messages GROUP BY user_id
284
- UNION ALL
285
- SELECT user_id, count() FROM mem.alice_messages GROUP BY user_id
23
+ npm install -g memhouse # Node 24+; on a distro-packaged Node this needs sudo
24
+ memhouse onboard # finds your editors, sets up a house, ships, starts the dashboard
286
25
  ```
287
26
 
288
- An operator with an admin credential reads every room; a member reads the ones they own
289
- plus whatever was shared with them, and naming a room they do not hold is an error rather
290
- than a silent omission.
291
-
292
- ### The house never destroys what it cannot rebuild
293
-
294
- **The shipper is insert-only.** It runs no `DELETE`, no `TRUNCATE`, no mutation of any
295
- kind, so no privilege it holds can lose you a row.
296
-
297
- That is not free, because ReplacingMergeTree is a dedupe engine, not a diff engine: a
298
- re-parse yielding *fewer* messages leaves the old higher-`seq` rows with nothing written
299
- over them. The shipper used to delete the session's rows first to clear that tail — and
300
- a shorter re-parse has two indistinguishable causes. Either an adapter bug was fixed and
301
- the extra rows are junk, or Claude Code **compacted** the transcript, or the retention
302
- window (`cleanupPeriodDays`, 30 days by default) took it. In the last two cases the house
303
- holds the only surviving copy, and the delete destroyed it.
304
-
305
- So rows carry an `epoch`: which parse of the session they belong to.
306
-
307
- - **Nothing changed** (the common case, including ordinary growth): same epoch, rows
308
- dedupe exactly as before, no extra storage.
309
- - **Shorter, or rewritten at a position the house already holds**: the new parse goes to
310
- `epoch + 1`. The old one stays complete and readable. You pay storage only when the old
311
- rows are irreplaceable.
312
-
313
- Reads show one parse per session — the newest — so counts, tokens and cost are unchanged.
314
- `/mem:sql` and any hand-written query should filter the same way; the skill carries
315
- the clause.
316
-
317
- **Imported history is protected the same way.** `origin` says who wrote a row, and it is
318
- in the sorting key of `messages` and `tool_calls`, so ReplacingMergeTree cannot collapse
319
- an imported row against a shipped one. Anything you imported from an older house, another
320
- product, or a machine that no longer exists survives every re-ship. `sessions` is the
321
- deliberate exception — one row per session, no `origin` and no `epoch` in its key, because
322
- a session's metadata has a single current version and a second row makes every rollup
323
- count its messages twice.
324
-
325
- The shipper verifies the sorting keys before it writes and refuses if they are wrong, so
326
- an old house cannot be corrupted by a new shipper. `memhouse migrate-rooms` rebuilds it:
327
- copy, atomic swap, and the old room kept as `<room>_pre_epoch` for you to drop. The house
328
- records the move in `events`, and `memhouse doctor` reads it back.
329
-
330
- ## Upgrading
331
-
332
- `memhouse update` upgrades the code, restarts the daemons, and — from 0.10.0 onward —
333
- detects a schema migration the house needs and prompts to run it.
27
+ No ClickHouse yet? `memhouse deploy --local` runs one in docker or podman, bound to
28
+ loopback, and installs against it. Already have one, or an invite from someone who does?
29
+ See [install and onboard](docs/guides/install.md).
334
30
 
335
- **One exception: upgrading from 0.9.x.** That release predates migrations, and `update`
336
- runs the *old* version's code (it replaces itself mid-run), so it cannot prompt. After
337
- the upgrade the first ship refuses (nothing is lost) and tells you the one command to
338
- run by hand:
31
+ ## What you get
339
32
 
340
33
  ```bash
341
- memhouse update # installs the new version, refuses to ship the old-schema house
342
- memhouse migrate # the one-time rebuild it named — copy + atomic swap, nothing deleted
343
- memhouse doctor # every line a check mark
34
+ memhouse status # is it shipping, and how fresh
35
+ memhouse search "auth error" # full-text across every session, every machine
36
+ memhouse resume claude-code:6b1f… # prints the command that reopens that session
37
+ memhouse plugins install claude # /mem:recall and four more skills for your agents
344
38
  ```
345
39
 
346
- If other machines ship into the same house as the same member, upgrade them too — a
347
- pre-0.10 shipper still deletes-before-reinsert; `memhouse doctor` flags any that are
348
- behind, and prints the `REVOKE ALTER DELETE, ALTER UPDATE` fallback that stops them
349
- destructively until you can.
40
+ The dashboard is at `http://localhost:4640`, showing sessions, cost, models, tools and
41
+ machines. From any Claude Code session, `/mem:recall how did I fix the ClickHouse auth
42
+ error` answers from your own history.
350
43
 
351
- ## Troubleshooting
44
+ ## Documentation
352
45
 
353
- ### An editor reads zero sessions
354
-
355
- Five adapters — **cursor, zed, opencode, goose, antigravity** — read sessions out of
356
- SQLite files. That used to mean `better-sqlite3`, whose native binding is built by
357
- an npm install script; npm 12 blocks install scripts by default, so a plain install left
358
- all five reading nothing and the fix was a flag
359
- (`--allow-scripts=better-sqlite3`) you had to re-type on every upgrade. SQLite is
360
- `node:sqlite` now — part of Node — so that failure class is gone along with the flag.
361
-
362
- What can still go wrong is one store at a time: a `state.vscdb` locked by a running
363
- editor, a corrupt file, a schema a new editor release changed. None of it is silent —
364
- `memhouse discover` and `memhouse doctor` both name the skipped adapter and say why,
365
- and the other sixteen keep working.
366
-
367
- ```bash
368
- npx -y memhouse discover # no flags here either
369
- ```
370
-
371
- ### `EACCES` on install
372
-
373
- `sudo` is right for only one of the two causes, and what tells them apart is **who
374
- owns the prefix root** — not the path, and not the file npm named.
375
-
376
- ```bash
377
- ls -ld "$(npm prefix -g)"
378
- ```
379
-
380
- - **Owned by `root`** — a system-managed Node (distro packages under `/usr`).
381
- Re-run with `sudo`.
382
- - **Owned by you** — the prefix is yours (Homebrew, fnm, nvm, volta), so the
383
- root-owned file npm tripped on is a stray from an earlier `sudo npm`. Another
384
- `sudo` adds more. Repair just that path:
385
- `sudo chown -R "$(id -u):$(id -g)" <the path npm named>`. Same for `~/.npm`.
386
-
387
- The path proves nothing on its own: Homebrew's prefix is `/usr/local` on Intel and
388
- `/opt/homebrew` on Apple Silicon, and is yours in both cases.
46
+ | | |
47
+ |---|---|
48
+ | [Tutorials](docs/README.md#tutorials) | your first house, joining a team's house, sharing memory |
49
+ | [Guides](docs/README.md#guides) | install, update, invite, share, admin credentials, daemons, search, troubleshooting |
50
+ | [Examples](examples/README.md) | runnable setups, from one machine up to a self-hosted team house |
51
+ | [Release notes](docs/releases/) | what changed in each version, and how to upgrade |
52
+ | [`AGENTS.md`](AGENTS.md) | installing memhouse as an agent, and working on this repository |
389
53
 
390
- ### Daemons don't survive a reboot
54
+ Any command's own usage: `memhouse <command> --help`. Every command works both ways:
55
+ interactive for you, and `--yes` / flags / `--json` for an agent.
391
56
 
392
- `memhouse start` detaches with pidfiles — they outlive the shell, not a restart.
393
- `memhouse service install` writes a real user service instead (systemd `--user`,
394
- or a launchd LaunchAgent) and takes over. On Linux a `--user` unit stops at logout
395
- unless lingering is on; install detects that and prints the `loginctl` command.
57
+ ## What's supported
396
58
 
397
- A rootless podman house has the same problem for the same reason, and
398
- `deploy --local` says so.
59
+ | Surface | Status |
60
+ |---|---|
61
+ | macOS | primary; developed and driven here daily |
62
+ | Linux | installs, ships, `service install` (systemd `--user`), rootless podman `deploy --local`; exercised on Debian and Ubuntu |
63
+ | Windows | **untested**; the adapters declare Windows paths, but no one has run an install |
64
+ | ClickHouse | 25.11 and 26.x, both in CI |
65
+ | Node | **24 or newer**. SQLite comes from `node:sqlite`; no native dependency, no build step |
399
66
 
400
67
  ## Working from a checkout
401
68
 
402
69
  ```bash
403
- npm install # two runtime deps, no build step
70
+ npm install # two runtime dependencies, no build step
404
71
  node bin/memhouse.js …
405
72
  npm test # syntax gate + unit checks
406
73
  ```
407
74
 
408
- `misc/origin-matrix.sh <label> <url> <user> <pass>` checks the import-protection
409
- guarantees against a real ClickHouse. `misc/tier-matrix-test.sh` walks the
410
- tier/ownership matrix against a real container engine on Linux. Both take minutes
411
- and are not part of `npm test`.
412
-
413
- ## Layout
414
-
415
- | Path | What |
416
- |---|---|
417
- | `memhouse/` | the product — `DESIGN.md`, `per-member/`, `shipper/`, `server/`, `delivery/` |
418
- | `editors/` | the 17 editor adapters (inherited from agentlytics; the crown jewels) |
419
- | `pricing.js` + `pricing.json` | the cost engine |
420
- | `ui/` | the dashboard SPA (built to `public/`, served by the memhouse server) |
421
- | `agency/` | the earlier agentlytics-agency wrap — prior art, not how memhouse works |
422
- | `TERMINOLOGY.md` | the constellation terminology canon |
423
-
424
- Deeper reading: `memhouse/DESIGN.md` (the bets, and why the room layout has moved three
425
- times), `memhouse/house/HOUSE.md` (the layout and the schema), `SECURITY.md` (what
426
- holds, and what does not), `memhouse/delivery/kernel-install.md`, `memhouse/COMPETITION.md`.
427
-
428
- Deferred designs (captured, not yet built): `docs/design/host-repoint-reconciliation.md`
429
- — what should happen when the shipper is repointed at a new, empty host (house identity,
430
- local binding state, and a refuse-then-`init-here` gate so transcripts never land on the
431
- wrong server).
432
-
433
- In constellation terms (`TERMINOLOGY.md`) memhouse is an **agency**: a **house**
434
- — the `mem` database — plus a **resident** working in it, the shipper. The house
435
- alone would hold; the shipper is what makes it act. It competes with
436
- memory-house; if it wins, it becomes memory-house v4.
437
-
438
- ## What's supported
439
-
440
- | Surface | Status |
441
- |---|---|
442
- | macOS | primary — developed and driven here daily |
443
- | Linux | real — installs, ships, `service install` (systemd `--user`), rootless podman `deploy --local`; exercised on Debian and Ubuntu machines |
444
- | Windows | **untested** — the adapters declare Windows paths but no one has run a single install; treat it as unknown until this row changes |
445
- | ClickHouse | 25.11 and 26.x, both in CI; 26.2+ needs no flags, 25.x needs the text-index flag the shipper already passes |
446
- | Node | **≥ 24** — SQLite comes from `node:sqlite`, which is stable in 24. It works flagless on 22.13+ but prints `ExperimentalWarning` on every command (measured on v22.23.2), and Node 20 is past EOL. There is no native dependency and no build step at any version. |
75
+ See [`AGENTS.md`](AGENTS.md) before your first change. Deeper reading:
76
+ `memhouse/DESIGN.md` (the design and why), `memhouse/house/HOUSE.md` (layout and schema),
77
+ `SECURITY.md` (what holds, and what does not).
447
78
 
448
- ## Heritage & license
79
+ ## Heritage and license
449
80
 
450
- Built on [agentlytics](https://github.com/f/agentlytics) by Fatih Kadir Akın (MIT)
451
- — the adapters, dashboard, and cost engine come from there, and this repo's history
452
- carries the full lineage. MIT.
81
+ Built on [agentlytics](https://github.com/f/agentlytics) by Fatih Kadir Akın (MIT). The
82
+ adapters, dashboard and cost engine come from there, and this repository's history carries
83
+ the full lineage. MIT.
package/SECURITY.md ADDED
@@ -0,0 +1,68 @@
1
+ # Security model — stated plainly
2
+
3
+ memhouse ships your coding-agent transcripts to a ClickHouse **you choose** — a container
4
+ on your laptop, a box you run, your company's cluster, or ClickHouse Cloud. memhouse runs
5
+ no service of its own: no memhouse cloud to sign up for, no telemetry, no phone-home. The
6
+ credential is yours and the data is in your account, wherever you decided that is. That
7
+ makes the security model short, and worth stating without varnish.
8
+
9
+ ## What holds
10
+
11
+ - **A member reaches their own rooms and nothing else.** One grant, on `mem.<name>_*`:
12
+ their rooms, present and future. Ungranted rooms are not merely unreadable but absent
13
+ from `SHOW TABLES`, and a member cannot create, drop or re-grant outside their pattern
14
+ (measured on 25.11 and 26.7). Reserved databases (`system`, `information_schema`) are
15
+ refused as house names everywhere.
16
+ - **Nobody is granted the database.** `ALL ON db.*` is dynamic — it covers rooms created
17
+ later — and a member holding it beside others could read *and* re-grant every
18
+ housemate's transcripts, needing no admin and notifying nobody. memhouse never issues
19
+ it: not to a member, not to the operator's own member account. The operator's reach is
20
+ the admin credential, kept separately.
21
+ - **A member can only share what is theirs.** Grant option is scoped to their own rooms,
22
+ so `memhouse share` needs no operator and cannot reach a housemate's rows. A scoped
23
+ share (`--only`) builds its row filters BEFORE granting anything, so a scoping failure
24
+ grants nothing rather than leaving the grantee with everything.
25
+ - **Attribution cannot be faked by a client.** `user_id` is `MATERIALIZED
26
+ currentUser()` — computed by the server during the insert — and `async_insert = 0
27
+ CONST` is pinned on every member so the stamp cannot be skipped (an async flush
28
+ stores it empty; measured on 25.11).
29
+ - **The dashboard binds loopback** (`127.0.0.1`) by default, and `deploy --local`
30
+ binds the ClickHouse container to loopback too.
31
+ - **Credentials are never printed.** The skills are under standing instruction not to
32
+ echo the env file, and the API's responses and error bodies were checked for
33
+ credential leakage under the acceptance suite.
34
+ - **The tarball is audited in CI** for env files, credentials and host identities.
35
+
36
+ ## What you must know
37
+
38
+ - **The dashboard has no authentication.** Loopback-only is the protection. If you
39
+ expose `MEMHOUSE_HOST=0.0.0.0`, anyone who can reach the port can read every
40
+ transcript in the house and run read-only SQL. Do not expose it; put a reverse proxy
41
+ with auth in front if you must.
42
+ - **`/api/query` is read-only by enforcement, within limits.** Writes and table
43
+ functions (`url()`, `file()`, …) are refused server-side per request; resource use is
44
+ capped per query. It is still full SQL over your own house — treat dashboard access
45
+ as house access.
46
+ - **The env file holds the house credential** (`~/.memhouse/env`, mode-restricted).
47
+ `service install` inlines the same credential into the systemd/launchd unit, and says
48
+ so. `uninstall` keeps it by default; `--credentials` or `--full-removal` remove it.
49
+ - **Transcripts are shipped as-is.** memhouse does not redact. Whatever your editors
50
+ wrote to disk — including any secret an agent echoed into a session — is what lands
51
+ in the house. The house is as sensitive as your shell history; place it accordingly.
52
+ - **Isolation is grants, and grants are checkable.** A housemate can run `SHOW GRANTS`
53
+ and see exactly what they hold; memhouse does not ask anyone to trust a row policy they
54
+ cannot inspect. What it does NOT defend against is the operator: whoever administers
55
+ the ClickHouse can read every room by definition, and no arrangement inside the server
56
+ changes that. If you should not be readable by the person running the server, you need
57
+ a different server, not a different layout.
58
+ - **Members can enumerate each other** on a shared server: a table you may not read
59
+ errors differently (`Code: 497`) from one that does not exist (`Code: 60`). ClickHouse
60
+ behaviour, verified independent of memhouse's grants. It leaks names, never content —
61
+ and in a shared house the names are guessable anyway (`<member>_messages`), while
62
+ `SHOW USERS` is granted to members deliberately so `/mem:house` can list who to share
63
+ with. Treat membership as public and content as private.
64
+
65
+ ## Reporting
66
+
67
+ The repository is private during beta. Report security issues to the maintainer
68
+ directly (ramazanpolat@gmail.com) rather than in a public channel.