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
@@ -0,0 +1,102 @@
1
+ # Share and revoke
2
+
3
+ A share lets another member of the same server read your rooms. It is a ClickHouse
4
+ `GRANT SELECT`, so it is read-only, and you can withdraw it. Run these on a machine
5
+ installed as yourself. Each command acts on your own rooms only.
6
+
7
+ ```bash
8
+ memhouse share bob # full: everything in mem.<you>_*
9
+ memhouse share bob --only project=billing # scoped: three rooms, filtered
10
+ memhouse share --list # what memhouse has recorded
11
+ memhouse share bob --revoke # take it back
12
+ ```
13
+
14
+ ## Full or scoped
15
+
16
+ | | full (`share bob`) | scoped (`share bob --only …`) |
17
+ |---|---|---|
18
+ | grant | `SELECT ON mem.<you>_*` | `SELECT` on `<you>_sessions`, `<you>_messages`, `<you>_tool_calls` |
19
+ | rows | all | those matching the scope, through row policies |
20
+ | stats tables (`<you>_session_stats`, model and tool stats) | readable: first prompts, folders, projects and tokens of every session | not granted |
21
+ | `<you>_meta`, `<you>_events` | readable, including your other share records | not granted |
22
+ | rooms created later | readable | not granted |
23
+
24
+ **A full share is for someone you would hand your laptop to.** It reaches every table in
25
+ your pattern, including ones memhouse adds in a later release. The command says so when
26
+ you run it.
27
+
28
+ ## Scopes
29
+
30
+ `--only key=value[,key=value…]`. All the keys must hold (AND):
31
+
32
+ | key | filters on |
33
+ |---|---|
34
+ | `project=` | the project name |
35
+ | `folder=` | the working directory |
36
+ | `session=` | one session id |
37
+ | `host=` | one machine (its fingerprint, as `status` shows it) |
38
+ | `source=` | one editor, by the value stored in the `source` column (below) |
39
+ | `since=` / `until=` | a date range: message time, or session creation time for `sessions` |
40
+
41
+ `source=` compares against the stored value, which is not always the adapter name you
42
+ use in `MEMHOUSE_EDITORS`. Claude Code rows, for example, say `claude-code`, not `claude`.
43
+ The values:
44
+
45
+ | editor | `source` |
46
+ |---|---|
47
+ | Claude Code | `claude-code` |
48
+ | Codex | `codex` |
49
+ | Cursor / Cursor Agent | `cursor` / `cursor-agent` |
50
+ | VS Code / VS Code Insiders | `vscode` / `vscode-insiders` |
51
+ | GitHub Copilot / Copilot for JetBrains | `copilot-cli` / `copilot-jetbrains` |
52
+ | Gemini CLI | `gemini-cli` |
53
+ | Zed, OpenCode, Goose, Kiro, Codebuff, Antigravity | `zed`, `opencode`, `goose`, `kiro`, `codebuff`, `antigravity` |
54
+ | Devin / Devin Next | `devin` / `devin-next` |
55
+ | Command Code | `commandcode` |
56
+
57
+ To see what your rooms actually hold:
58
+ `SELECT source, count() FROM mem.<you>_sessions FINAL GROUP BY source`.
59
+
60
+ An empty scope (`--only ""`, or `--only "$SCOPE"` with the variable unset) is refused.
61
+ It never falls back to a full share.
62
+
63
+ ## How the server must be configured
64
+
65
+ Row policies in ClickHouse have one sharp edge. On some server configurations, the first
66
+ policy on a table hides every row from readers who have no policy of their own: you, and
67
+ your full-share grantees. Before it creates the first policy, `share` measures how this
68
+ server behaves, and refuses if a policy would blind you. The fix is server configuration
69
+ (`users_without_row_policies_can_read_rows`), and it is the administrator's to make.
70
+
71
+ ## Changing a share
72
+
73
+ - **Scoped to full:** `memhouse share bob`.
74
+ - **Full to scoped:** `memhouse share bob --only …`. The wildcard grant is withdrawn
75
+ first, so a failure part-way leaves bob with less access, never more.
76
+ - **One scope to another:** run `--only` again with the new scope. It replaces the
77
+ policies.
78
+
79
+ **Scoped shares made before 0.18.10** granted the whole pattern while filtering only three
80
+ rooms. Re-run `memhouse share <user> --only <scope>` once for each, and it is narrowed to
81
+ the three rooms.
82
+
83
+ ## Revoking
84
+
85
+ `--revoke` withdraws the grants (the wildcard and the three rooms), then drops the row
86
+ policies. If the server refuses a step, the policies are kept unless every grant is
87
+ confirmed gone, so a failed revoke never leaves an unfiltered share behind.
88
+
89
+ A revoke cannot recall what was already read.
90
+
91
+ ## What `--list` shows
92
+
93
+ `share --list` reads memhouse's own record, the `share:` keys in `<you>_meta`. A grant
94
+ someone made by hand in SQL does not appear there. A member cannot read `system.grants`;
95
+ an administrator can, with `SHOW GRANTS FOR <user>`.
96
+
97
+ ## Reading what was shared with you
98
+
99
+ Shared rooms sit in the same database as yours, under the other member's name, for
100
+ example `mem.alice_messages`. Query them with `/mem:sql` or any ClickHouse client, joining
101
+ on `(session_id, user_id)` as you would on your own rooms. A table you were not granted is
102
+ refused, and a scope you are under returns only its rows.
@@ -0,0 +1,90 @@
1
+ # How memory is stored
2
+
3
+ ## A house, rooms, and members
4
+
5
+ - **A house is a ClickHouse database,** normally `mem`.
6
+ - **A member is a ClickHouse user** who ships into it.
7
+ - **Each member's rooms are tables named for them:**
8
+
9
+ | room | holds |
10
+ |---|---|
11
+ | `<member>_sessions` | one row per session: editor, project, folder, branch, timestamps, title |
12
+ | `<member>_messages` | every turn: role, text, model, tokens, timestamp, position |
13
+ | `<member>_tool_calls` | every tool call: name, arguments, which message made it |
14
+ | `<member>_session_stats`, `_session_model_stats`, `_session_tool_stats` | precomputed rollups for the dashboard, refreshed after each pass |
15
+ | `<member>_meta` | memhouse's own records: schema version, writers, shares |
16
+ | `<member>_events` | an append-only log: migrations, version changes |
17
+
18
+ **A member holds one grant: `ON mem.<member>_*`.** That covers their own rooms, present and
19
+ future, and nothing else in the database. Nobody is granted the database itself:
20
+ `ALL ON mem.*` would also reach rooms created later, which is how two people in one
21
+ database once leaked into each other. A member can read their isolation back with
22
+ `SHOW GRANTS`.
23
+
24
+ | | rooms | each member holds |
25
+ |---|---|---|
26
+ | alone on a laptop | `mem.polat_*` | one grant on `mem.polat_*` |
27
+ | a team on one server | `mem.polat_*`, `mem.alice_*`, … | one grant each, on their own |
28
+
29
+ Reading across members is a `UNION ALL` over the rooms you hold: your own, and any shared
30
+ with you. Naming a room you were not granted is an error, never a silent omission.
31
+
32
+ ## Every row says where it came from
33
+
34
+ - **`user_id`** is stamped by the server: `MATERIALIZED currentUser()`. Async inserts are
35
+ pinned off for members, so the stamp cannot be skipped. `WHERE user_id = 'alice'` is one
36
+ person.
37
+ - **`host`** is the machine. Each install mints a random fingerprint once and keeps it in
38
+ `$MEMHOUSE_HOME/host.json`, and every row carries `<hostname>-<8 hex>`. It is random,
39
+ not derived from the machine:
40
+ - a derived id would merge two laptops that both answer to `MacBook-Pro`;
41
+ - a derived id would split one machine in two when it is renamed.
42
+
43
+ `memhouse status` shows this machine's id. `memhouse doctor` names every host in your
44
+ rooms.
45
+ - **`origin`** says who wrote the row. The shipper writes `ship`; an import writes its own
46
+ value.
47
+
48
+ ## The house never destroys what it cannot rebuild
49
+
50
+ **The shipper is insert-only.** It runs no `DELETE`, no `TRUNCATE`, and no mutation of
51
+ any kind, so no privilege it holds can lose you a row.
52
+
53
+ That has a cost. ReplacingMergeTree deduplicates but does not diff: a re-parse that yields
54
+ *fewer* messages leaves the old, higher-positioned rows in place. A shorter re-parse has
55
+ three possible causes:
56
+
57
+ - an adapter bug was fixed, and the extra rows were junk;
58
+ - Claude Code **compacted** the transcript;
59
+ - the editor's retention window removed it.
60
+
61
+ In the last two cases the house holds the only surviving copy. So rows carry an `epoch`,
62
+ which says which parse of the session they belong to:
63
+
64
+ - **Nothing changed**, or the session just grew: the same epoch. Rows deduplicate exactly,
65
+ and no extra storage is used.
66
+ - **Shorter, or rewritten at a position the house already holds:** the new parse goes to
67
+ `epoch + 1`. The old parse stays complete and readable.
68
+
69
+ Reads show one parse per session, the newest, so counts, tokens and cost are not
70
+ inflated. The skills, the dashboard and `memhouse search` all filter this way; hand-written
71
+ queries should too ([the clause](search-and-resume.md#writing-your-own-queries)).
72
+
73
+ **Imported history is protected the same way.** `origin` is in the sorting key of
74
+ `messages` and `tool_calls`, so a shipped row can never collapse an imported one. Anything
75
+ imported from an older house, another product, or a machine that no longer exists survives
76
+ every re-ship. `sessions` is the deliberate exception: it holds one row per session, with
77
+ no `origin` or `epoch` in its key. A session's metadata has a single current version, and
78
+ a second row would make every rollup count its messages twice.
79
+
80
+ The shipper checks the rooms' sorting keys before it writes, and refuses if they are
81
+ wrong. See [Update](update.md#when-the-house-itself-must-change).
82
+
83
+ ## What is not in the house
84
+
85
+ - Nothing is embedded, summarised or rewritten. Text is stored as the editor wrote it,
86
+ truncated at 50,000 characters per message and 20,000 per tool argument.
87
+ - No LLM runs anywhere in memhouse's write or read path. The skills are read by your agent,
88
+ which does the reading and reasoning.
89
+ - The local transcript files stay where the editor put them. memhouse reads them and never
90
+ modifies them.
@@ -0,0 +1,78 @@
1
+ # Troubleshooting
2
+
3
+ Start with the two diagnostics. Both are read-only.
4
+
5
+ ```bash
6
+ memhouse doctor # the whole pipeline, one line per check
7
+ memhouse discover # what each editor adapter sees on this machine
8
+ ```
9
+
10
+ ## An editor ships nothing
11
+
12
+ `discover` lists every adapter. Each one is in one of three states:
13
+
14
+ - it has sessions, with a count;
15
+ - it has none, because the editor is not used on this machine;
16
+ - it was **skipped**, with the reason.
17
+
18
+ A skipped adapter and an editor you do not have both contribute zero sessions, so
19
+ memhouse always says which one it is. Common causes, one store at a time:
20
+
21
+ - **A SQLite store locked by the running editor:** `state.vscdb` for Cursor and VS Code.
22
+ The next pass retries.
23
+ - **A store the editor moved or changed** in a new release.
24
+ - **A scope that excludes it:** `MEMHOUSE_EDITORS` or a `MEMHOUSE_<EDITOR>_ROOTS` in the
25
+ env file. See [Configuration](configuration.md#which-sessions-ship).
26
+
27
+ SQLite comes from Node itself (`node:sqlite`), so there is no native module to rebuild.
28
+
29
+ ## Status says the shipper runs, but nothing new arrives
30
+
31
+ `memhouse status` reads the shipper's last pass from its log, and shows a failing pass
32
+ as a failure.
33
+
34
+ - **`Authentication failed`:** the password in `~/.memhouse/env` is no longer the
35
+ member's. This happens after a `passwd` on another machine, or a reset by the admin. Put
36
+ the current password into `MEMHOUSE_PASSWORD` in `~/.memhouse/env` (or run
37
+ `memhouse setup`, which asks), then restart the shipper.
38
+ - **A pass that keeps withholding a session:** an adapter returned nothing for a session
39
+ the house already holds. The shipper refuses to replace a transcript with an empty one,
40
+ and says so on every pass, until the store is readable again.
41
+ - **Nothing to ship:** sessions are only shipped once the editor has written them.
42
+
43
+ ## Daemons don't survive a reboot
44
+
45
+ `memhouse start` uses pidfiles. Its daemons outlive the shell but not a restart. Use
46
+ `memhouse service install` ([Daemons](daemons.md)). On Linux, also enable lingering, or
47
+ user services stop at logout. A rootless podman house from `deploy --local` has the same
48
+ exposure, and `deploy` prints the fix.
49
+
50
+ ## The dashboard is empty, or stale
51
+
52
+ - **It reads the house, not the machine.** Check `memhouse status` for the connection
53
+ first.
54
+ - **It reads precomputed stats,** refreshed at the end of each pass that shipped
55
+ something. On a house where nothing has shipped for a day, the stats age out, and a
56
+ running dashboard keeps reading them. Restart it (`memhouse stop && memhouse start`). It
57
+ then reads the rooms directly, until the next pass that ships refreshes the stats.
58
+ A fix is planned.
59
+ - **After an upgrade it shows the old version:** restart it (`memhouse update` does this).
60
+
61
+ ## `EACCES` during `npm install -g`
62
+
63
+ See [Install](install.md#eacces-on-install). `sudo` is right only when the npm prefix is
64
+ owned by root.
65
+
66
+ ## `refused`, `ACCESS_DENIED`, `Not enough privileges`
67
+
68
+ - For a member, these are expected outside `mem.<you>_*` and the rooms shared with you.
69
+ `memhouse whoami` shows what your credential may do.
70
+ - For `invite`, `members` or `convert`: you need the admin credential. See
71
+ [Admin password](admin-password.md).
72
+ - A room that should be yours but is refused usually means the house is older than this
73
+ version. Run `memhouse doctor`, then `memhouse migrate` if it says so.
74
+
75
+ ## Still stuck
76
+
77
+ Run `memhouse doctor` and `memhouse status --json`, and open an issue with both outputs.
78
+ Check them before posting, and remove anything private, such as a host name.
@@ -0,0 +1,35 @@
1
+ # Uninstall
2
+
3
+ Three tiers. **None of them touches the memory in the house.**
4
+
5
+ ```bash
6
+ memhouse uninstall # stop everything; KEEP the config and this machine's identity
7
+ memhouse uninstall --credentials # also forget the house and its password
8
+ memhouse uninstall --full-removal # all of ~/.memhouse, identity included
9
+ npm uninstall -g memhouse # then remove the package
10
+ ```
11
+
12
+ - **The default is the conservative one.** Stopping the shipper is routine: before an
13
+ upgrade, while debugging, or when a laptop should go quiet for a week. It should not
14
+ also mean "forget which house I use and who this machine is".
15
+ - **`--full-removal` asks first.** A later reinstall mints a **new** machine identity, and
16
+ this machine then reads as two machines in your history.
17
+ - **Your memory stays.** The rooms in ClickHouse are untouched by every tier. What the
18
+ shipper wrote can be rebuilt from the local session stores with `memhouse ship --full`,
19
+ for as long as the editors keep them.
20
+
21
+ ## Removing the house itself
22
+
23
+ - **A local house** from `deploy --local`: `memhouse deploy --down` removes the container
24
+ **and its data volume**. That deletes the memory, and there is no undo.
25
+ - **A shared server:** an administrator drops your rooms and user. See
26
+ [Operate a house](operate.md).
27
+
28
+ ## Removing the skills
29
+
30
+ ```bash
31
+ memhouse plugins remove claude
32
+ ```
33
+
34
+ This removes the `/mem:*` skills, and the instance binding, from the Claude Code config
35
+ directories they were installed into.
@@ -0,0 +1,68 @@
1
+ # Update
2
+
3
+ ```bash
4
+ memhouse update # upgrade along this install's channel, restart the daemons, check the house
5
+ memhouse update --check # compare versions, change nothing
6
+ ```
7
+
8
+ `update` does three things that a bare `npm install -g memhouse@latest` does not:
9
+
10
+ 1. **It restarts the daemons.** A running shipper keeps parsing with the code it booted
11
+ with. One once ran for 1 day 16 hours out of a directory that had since been moved.
12
+ 2. **It checks whether the house needs a migration** for the new version, and offers to run
13
+ it. `--migrate` runs it without asking.
14
+ 3. **It stays on your channel** (`MEMHOUSE_CHANNEL`). See
15
+ [Configuration](configuration.md#channels).
16
+
17
+ Upgraded by hand already? Finish the job without reinstalling:
18
+
19
+ ```bash
20
+ npm install -g memhouse@latest
21
+ memhouse update --no-install
22
+ ```
23
+
24
+ The daemons also notice on their own. Each one compares the installed version with the
25
+ version it booted with, and hands over when they differ: under systemd or launchd it
26
+ exits so the supervisor restarts it; with nothing supervising, it re-executes itself.
27
+ `update` just makes the switch immediate.
28
+
29
+ ## Several machines on one house
30
+
31
+ Every machine of every member runs its own shipper. Upgrade them all.
32
+
33
+ - The release notes for each version say whether the order matters and whether mixed
34
+ versions can ship side by side. See [`docs/releases/`](../releases/).
35
+ - `memhouse status` lists every machine writing to your rooms, with the version each
36
+ last reported and when it last shipped. A machine that is behind is named there.
37
+ - Machines that stopped writing long ago are folded into one line. That is history, not
38
+ a fault. `memhouse status --all` lists them.
39
+
40
+ ## When the house itself must change
41
+
42
+ Some releases need the rooms rebuilt, for example when a sorting key changes. The shipper
43
+ checks the rooms' keys before it writes, and **refuses** to write into a house it does not
44
+ match, so an old house cannot be corrupted by a new shipper. To bring the house up to
45
+ date:
46
+
47
+ ```bash
48
+ memhouse migrate --dry-run # what would run
49
+ memhouse migrate # copy, atomic swap; nothing is deleted
50
+ memhouse doctor # every line a check mark
51
+ ```
52
+
53
+ A migration copies each room, swaps it in atomically, and keeps the old one as
54
+ `<room>_pre_<migration>` for you to drop when you are satisfied. The house records the move
55
+ in its `events` room, and `doctor` reads it back.
56
+
57
+ ## Upgrading from 0.9.x
58
+
59
+ 0.9.x predates migrations, and `update` runs the **old** version's code (it replaces itself
60
+ mid-run), so it cannot prompt. After the upgrade, the first ship refuses (nothing is lost)
61
+ and names the one command to run by hand: `memhouse migrate`.
62
+
63
+ ## Upgrading a pre-0.18 house (a database per member)
64
+
65
+ 0.18 moved every member's rooms into one database, as `mem.<member>_*`. An operator moves
66
+ an old house once with `memhouse convert`; see [Operate a house](operate.md#convert). After
67
+ that, members upgrade with `memhouse update`, and the shipper adopts the moved rooms
68
+ itself.
@@ -0,0 +1,10 @@
1
+ # Release notes
2
+
3
+ One document per release, from 0.18.10 onward. Each says what changed, what you need to
4
+ do, how to upgrade, what is deprecated and when it goes, and what was deferred.
5
+
6
+ | version | date | headline |
7
+ |---|---|---|
8
+ | [0.18.10](v0.18.10.md) | 2026-10-08 | security: the admin password never on argv; no member password printed; a scoped share reaches only its scope |
9
+
10
+ Earlier versions: [`CHANGELOG.md`](../../CHANGELOG.md).
@@ -0,0 +1,226 @@
1
+ # memhouse 0.18.10 — 2026-10-08
2
+
3
+ A security release:
4
+
5
+ - an admin password no longer has to go on the command line;
6
+ - no command prints a member password;
7
+ - a scoped share no longer reaches past its scope.
8
+
9
+ Everyone on 0.18.x should upgrade.
10
+
11
+ **No drill was run for this release; waived by the pilot (2026-10-08). The drill runs before 0.19.0.**
12
+
13
+ ```bash
14
+ memhouse update # or: npm install -g memhouse@latest, then memhouse update --no-install
15
+ memhouse status # every line a check mark
16
+ ```
17
+
18
+ Nothing in the house changes: no migration, no new room, no re-ship. Machines on 0.18.9
19
+ and 0.18.10 can ship into the same house side by side.
20
+
21
+ ## Security
22
+
23
+ ### An admin password never has to go on the command line (#14)
24
+
25
+ The admin-assisted commands (`install --member`, `invite`, `passwd`, `convert`,
26
+ `members`) now take the admin password from, in order:
27
+
28
+ 1. `--admin-password-file <file>`, a file holding the password (one trailing newline is
29
+ dropped), or `--admin-password-file -` to read it from stdin;
30
+ 2. `--admin-password <value>` (still accepted, with a warning; see below);
31
+ 3. `MEMHOUSE_ADMIN_PASSWORD` in the environment, which is how `with-secret` lends a secret
32
+ to one command;
33
+ 4. a credential this install already keeps, but only for the same admin user;
34
+ 5. a prompt, when there is a terminal to prompt on.
35
+
36
+ ```bash
37
+ with-secret MEMHOUSE_ADMIN_PASSWORD=keychain:pilot/house-admin -- \
38
+ memhouse invite alice --url https://house.example.com --admin-user default
39
+ printf '%s' "$ADMIN_PASSWORD" | memhouse invite alice --url … --admin-user default --admin-password-file -
40
+ ```
41
+
42
+ Before this release, `--admin-user` as a flag switched every other source off. The only
43
+ scriptable admin path therefore put the password on argv, where `ps` shows it for as long
44
+ as the command runs and shell history keeps it. Found by O's sandbox rehearsal (finding 6).
45
+ With no source and no terminal, the command stops and prints the three ways above instead
46
+ of hanging or guessing.
47
+
48
+ ### An admin install no longer saves the admin credential (#14)
49
+
50
+ `install --admin-user …` builds the member and then ships as the member. It used to write
51
+ the admin credential into `~/.memhouse/env` as well. The help said it was saved; every
52
+ agent-facing text said it never was. Now it is saved only with `--keep-admin`, or when
53
+ the env file already keeps that same admin. Credentials already saved keep working.
54
+
55
+ ### A daemon never carries the admin credential (#14)
56
+
57
+ - Service units (systemd, launchd) drop `MEMHOUSE_ADMIN_*` before they inline the env.
58
+ - The shipper and the dashboard are started without it, even when the shell exported it.
59
+ - `deploy --local` passes the ClickHouse password to `docker run` through the environment,
60
+ no longer as `-e CLICKHOUSE_PASSWORD=<value>` on its argv.
61
+
62
+ A unit test now fails on any spawn or exec in the shipped code that puts a password on
63
+ argv.
64
+
65
+ ### No command prints a member password (G23)
66
+
67
+ `install --admin-user` used to show the generated member password once, on the terminal.
68
+ It was already written to the env file, so the terminal copy served no one. It landed in
69
+ scrollback and, through `/mem:access` and `/mem:admin`, in an agent's transcript, which
70
+ memhouse ships into the house, where the archive is insert-only. (#20)
71
+
72
+ - **Install and invite never print a generated member password.** It goes to the env file
73
+ (install) or the invite file (invite), and nowhere else.
74
+ - **A failed install cleans up after itself.** If the grant fails after the member was
75
+ created, the run drops that member, so no account is left with a password nobody holds.
76
+ - **`install --print-sql` and `invite --print-sql` print a placeholder,
77
+ `IDENTIFIED BY <member-password>`.** As printed, that line does not parse. The
78
+ administrator puts a quoted password they generate in its place, and the same password
79
+ goes into the env-file lines the command prints for the member. From `invite`, those
80
+ lines carry `MEMHOUSE_INVITE=1`, so the member's `install --env` offers to rotate it.
81
+ A `--member-password` given with `--print-sql` is not echoed.
82
+ - **Tests guard it.**
83
+ - A unit test fails on any print call that evaluates a password: as an argument,
84
+ concatenated, or interpolated.
85
+ - `misc/invite-matrix.sh` runs every install, invite and passwd path against a real
86
+ ClickHouse and checks each output against the real passwords.
87
+
88
+ ### A scoped share shares only what its scope filters (#13)
89
+
90
+ `memhouse share <user> --only <scope>` granted `SELECT` on the member's whole pattern,
91
+ `mem.<member>_*`, while its row policies covered only `sessions`, `messages` and
92
+ `tool_calls`. A grantee scoped to one project could therefore read these in full:
93
+
94
+ - `<member>_session_stats`: the opening prompt, folder, project and token totals of
95
+ every session in every project;
96
+ - the model and tool stats;
97
+ - `<member>_meta`, including the member's other `share:` records;
98
+ - `<member>_events`;
99
+ - any table the pattern matched later.
100
+
101
+ Now:
102
+
103
+ - **A scoped share is granted table by table,** on exactly the three filtered rooms.
104
+ Narrowing an existing full share to a scope takes the wildcard back first, so a failure
105
+ part-way leaves the grantee with less access, never more.
106
+ - **`--revoke` withdraws the grants before it drops the row policies.** The old order
107
+ failed open: a `REVOKE` refused after the policies were gone left an unfiltered share.
108
+ A failed cleanup now keeps the policies unless every grant is confirmed withdrawn.
109
+ - **`--only ""` and whitespace-only scopes are refused.** An unset `--only "$SCOPE"`
110
+ used to fall through to a full share.
111
+ - **The permissive-server check before a scoped share runs.** Its probe table sat
112
+ outside the member's grant, so it was refused on every server and always reported
113
+ "could not verify".
114
+
115
+ A full share (no `--only`) is unchanged: it reaches everything in `mem.<member>_*`, by
116
+ design, and says so.
117
+
118
+ **Action for existing scoped shares:** they keep their old wildcard until re-run. On the
119
+ sharing member's machine, run `memhouse share <user> --only <scope>` again for each one;
120
+ `memhouse share --list` shows them.
121
+
122
+ ## Deprecations
123
+
124
+ | what | since | removed in | use instead |
125
+ |---|---|---|---|
126
+ | `--admin-password <value>` on the command line | 0.18.10 (2026-10-08) | **0.19.0** | `MEMHOUSE_ADMIN_PASSWORD` (via `with-secret`), `--admin-password-file <file>`, or `--admin-password-file -` |
127
+
128
+ It still works in 0.18.x and prints one warning on stderr. Move scripts now.
129
+
130
+ ## Upgrading
131
+
132
+ - **One machine:** `memhouse update`. The daemons restart on the new code.
133
+ - **Upgraded by hand** (`npm install -g memhouse@latest`): run
134
+ `memhouse update --no-install` afterwards to restart the daemons.
135
+ - **Several machines on one house:** no ordering is needed. This release changes no row
136
+ shape, no room and no shipper behaviour.
137
+ - **Share owners:** re-run each scoped share once (above).
138
+ - **Scripts and CI that pass `--admin-password`:** move to `MEMHOUSE_ADMIN_PASSWORD` or
139
+ `--admin-password-file` before 0.19.0.
140
+ - **Scripts that read a member password from `install --admin-user` output:** read
141
+ `MEMHOUSE_PASSWORD` from the env file instead. The output no longer contains it.
142
+ - **Scripts that run `--print-sql` output as is:** put a quoted password where
143
+ `<member-password>` stands. As printed, the line is refused by the server.
144
+ - **If an admin credential is in `~/.memhouse/env`** and you did not mean to keep it,
145
+ remove the `MEMHOUSE_ADMIN_USER` / `MEMHOUSE_ADMIN_PASSWORD` lines. memhouse never
146
+ needs it to ship.
147
+
148
+ ## Also in this release
149
+
150
+ - **The documentation standard** (#17). See [Documentation](#documentation) below. This
151
+ does change the package: it now also ships `docs/`, `examples/`, `AGENTS.md`,
152
+ `SECURITY.md` and `CHANGELOG.md`.
153
+
154
+ These do not change the installed package:
155
+
156
+ - **CI's tarball audit can fail now.** Its forbidden-file check was `! grep`, which
157
+ `set -e` never acts on, so it passed whatever the tarball held.
158
+ - **gentar arena adoption** (#10). memhouse is now a gentar subject. Its suites are
159
+ `memhouse-house` and `memhouse-install`, kit v0.9.2, and they install from `npm pack`
160
+ exactly as a publish ships. The phase-1 check runs on GitHub-hosted runners on every
161
+ push and PR. Full arena runs need a self-hosted bench; the one used before the
162
+ repository went public was deregistered then.
163
+ - **memhouse.io describes 0.18** (#12): one layout, five skills, the honest editor count
164
+ (16 adapters reading 18 apps).
165
+ - `memhouse.io/.wrangler` (wrangler's local cache) is no longer tracked.
166
+ - `misc/one-layout-matrix.sh` now asserts the scoped-share boundary, and
167
+ `misc/invite-matrix.sh` the admin-password sources: environment, stdin, `--keep-admin`,
168
+ and none. The file source is covered by unit tests. CI runs both matrices against
169
+ ClickHouse 25.11 and 26.7.
170
+
171
+ ## Not in this release
172
+
173
+ **The gentar kit v0.9.3 bump (#18) also moves to 0.18.11.** It changes CI only.
174
+
175
+ **The subagent re-ship fix moves to 0.18.11.** It is on branch `claude/subagent-stable-seq`
176
+ and stops a Claude Code session with subagents from re-shipping whole on every pass, which
177
+ is what grew one house to 16.2 M message rows for about 295 k real ones. It was reviewed
178
+ before release by four models. Three said "ship with fixes"; the fourth said "do not ship",
179
+ mostly for pre-existing issues. Its pre-tag fixes are not all done, and this release was
180
+ not held for them.
181
+
182
+ Status of the 0.18.10 pre-tag list (`review-0.18.10-consolidated`):
183
+
184
+ | item | what | status |
185
+ |---|---|---|
186
+ | B1 | scoped share reads past its scope | **landed** (#13) |
187
+ | G3 | share re-scope or revoke fails open | **landed** (#13) |
188
+ | G24 | `--only ""` is a full share | **landed** (#13) |
189
+ | B7 | permissive-server probe never runs | **landed** (#13) |
190
+ | B5 (part) | admin password in argv, units and daemons | **landed** (#14); `0600` enforcement on existing env/unit files is still open |
191
+ | A1 | version bump incl. `package-lock.json` | **landed** here |
192
+ | G23 | admin install and `--print-sql` print the member password | **landed** (#20) |
193
+ | A2 | `/mem:recall` "recent sessions" uses a column the table lacks | deferred to 0.18.11 |
194
+ | A3 | plugin `HOUSE.md` subagent queries lack the epoch filter | deferred to 0.18.11 (with the RC, which makes it matter) |
195
+ | A4–A7 | RC log text, `update --no-install` checks, RC changelog notes | deferred to 0.18.11 (they are the RC's own surface) |
196
+ | A8 | stale prose (ts interpolation, shrink, host id) | deferred to 0.18.11 |
197
+ | G4 | `invite --url` connects with the stored credential to the invitee-facing URL | deferred to 0.18.11 (security) |
198
+ | G26 | skills point at `../reference/HOUSE.md`; the file is `../../reference/HOUSE.md` | deferred to 0.18.11 |
199
+ | G11 | an idle house's dashboard goes empty after the stats TTL | deferred to 0.18.11 |
200
+ | GLM-F1 | a subagent file read before its first line is written re-sorts its siblings | deferred to 0.18.11 (with the RC) |
201
+
202
+ ## Documentation
203
+
204
+ This release meets the documentation standard (#17):
205
+
206
+ - a short README: what memhouse is, why, and how to install it;
207
+ - `docs/`, with three tutorials and twelve guides;
208
+ - `examples/`, four runnable setups from one machine to a self-hosted team house, each
209
+ with its own README;
210
+ - an install and deploy section for agents in `AGENTS.md`.
211
+
212
+ The npm package now ships `docs/`, `examples/`, `AGENTS.md`, `SECURITY.md` and
213
+ `CHANGELOG.md`, so an installed copy carries its own manual. `change.log` (#19) lists
214
+ every tag and what it changed.
215
+
216
+ ## Verification
217
+
218
+ - `npm test` passes.
219
+ - CI is green on `main` at the release commit: the syntax gate, unit tests, the
220
+ one-layout and invite matrices, and a headless e2e on ClickHouse 25.11 and 26.7.
221
+ - No live house was touched to prepare this release.
222
+ - **No drill was run for this release; waived by the pilot (2026-10-08). The drill runs before 0.19.0.**
223
+ `AGENTS.md` asks for a drill before a release that changes a surface a person touches,
224
+ and this one changes install, invite and share. A drill was prepared on the testbed, but
225
+ its setup was refused by this session's permission layer, and the pilot chose to release
226
+ without it.