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.
- package/AGENTS.md +194 -0
- package/CHANGELOG.md +970 -0
- package/README.md +54 -423
- package/SECURITY.md +68 -0
- package/bin/memhouse.js +202 -92
- package/docs/README.md +46 -0
- package/docs/guides/admin-password.md +95 -0
- package/docs/guides/configuration.md +109 -0
- package/docs/guides/daemons.md +53 -0
- package/docs/guides/install.md +128 -0
- package/docs/guides/invite.md +87 -0
- package/docs/guides/operate.md +112 -0
- package/docs/guides/search-and-resume.md +95 -0
- package/docs/guides/share.md +102 -0
- package/docs/guides/storage.md +90 -0
- package/docs/guides/troubleshooting.md +78 -0
- package/docs/guides/uninstall.md +35 -0
- package/docs/guides/update.md +68 -0
- package/docs/releases/README.md +10 -0
- package/docs/releases/v0.18.10.md +226 -0
- package/docs/tutorials/01-first-house.md +132 -0
- package/docs/tutorials/02-join-a-house.md +91 -0
- package/docs/tutorials/03-share-your-memory.md +96 -0
- package/examples/01-local-one-machine/README.md +99 -0
- package/examples/01-local-one-machine/run.sh +61 -0
- package/examples/02-existing-clickhouse/README.md +121 -0
- package/examples/02-existing-clickhouse/run.sh +71 -0
- package/examples/03-team-house/README.md +129 -0
- package/examples/03-team-house/run.sh +124 -0
- package/examples/04-self-hosted-team/.env.example +22 -0
- package/examples/04-self-hosted-team/README.md +187 -0
- package/examples/04-self-hosted-team/compose.yml +158 -0
- package/examples/04-self-hosted-team/smoke.sh +113 -0
- package/examples/README.md +42 -0
- package/examples/fixtures/README.md +24 -0
- package/examples/fixtures/make-sessions.js +63 -0
- package/memhouse/admin-secret.js +81 -0
- package/memhouse/delivery/plugin/.claude-plugin/plugin.json +1 -1
- package/memhouse/deploy.js +4 -2
- package/memhouse/flags.js +4 -4
- package/memhouse/provision.js +12 -3
- package/memhouse/server/queries.js +18 -4
- package/memhouse/server/server.js +9 -1
- package/memhouse/service.js +5 -0
- package/package.json +10 -2
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Admin password
|
|
2
|
+
|
|
3
|
+
A few commands need the ClickHouse server's administrator:
|
|
4
|
+
|
|
5
|
+
- `install --admin-user …`, which creates a member;
|
|
6
|
+
- `invite`;
|
|
7
|
+
- `passwd --admin-user …`, for a member whose account predates self-rotation (0.11);
|
|
8
|
+
- `members`;
|
|
9
|
+
- `convert`.
|
|
10
|
+
|
|
11
|
+
Shipping, searching and sharing never need it.
|
|
12
|
+
|
|
13
|
+
**The admin password owns the whole server.** Keep it out of three places:
|
|
14
|
+
|
|
15
|
+
- **the command line**, where `ps` shows it to every user on the machine while the command
|
|
16
|
+
runs, and shell history keeps it afterwards;
|
|
17
|
+
- **an agent's conversation**, because memhouse ships that conversation into the house,
|
|
18
|
+
and the archive is insert-only;
|
|
19
|
+
- **disk**, unless you chose that: `--keep-admin`, or `deploy --local`.
|
|
20
|
+
|
|
21
|
+
## Where memhouse takes it from
|
|
22
|
+
|
|
23
|
+
In order, first answer wins:
|
|
24
|
+
|
|
25
|
+
| # | source | use it when |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| 1 | `--admin-password-file <file>` | a file you control, mode 600. One trailing newline is dropped |
|
|
28
|
+
| 1 | `--admin-password-file -` | piping it in: `printf '%s' "$P" \| memhouse … --admin-password-file -` |
|
|
29
|
+
| 2 | `--admin-password <value>` | **deprecated**: works in 0.18.x with a warning, **removed in 0.19.0** |
|
|
30
|
+
| 3 | `MEMHOUSE_ADMIN_PASSWORD` | a secret manager lends it to one command (below) |
|
|
31
|
+
| 4 | the credential this install keeps | only when it belongs to the same admin user |
|
|
32
|
+
| 5 | a prompt | when there is a terminal; input is hidden, and pasting works |
|
|
33
|
+
|
|
34
|
+
The two forms of `--admin-password-file` are one option, and it cannot be combined with
|
|
35
|
+
`--admin-password`. With no source and no terminal, the command stops and prints these
|
|
36
|
+
options. It never hangs, and it never guesses.
|
|
37
|
+
|
|
38
|
+
## With a secret manager
|
|
39
|
+
|
|
40
|
+
The best form: the value exists only in the one command's environment.
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
with-secret MEMHOUSE_ADMIN_PASSWORD=keychain:pilot/house-admin -- \
|
|
44
|
+
memhouse invite alice --url https://house.example.com --admin-user default
|
|
45
|
+
|
|
46
|
+
# 1Password
|
|
47
|
+
op run --env-file=<(echo 'MEMHOUSE_ADMIN_PASSWORD=op://Infra/house-admin/password') -- \
|
|
48
|
+
memhouse members --admin-user default
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
In CI, put it in the job's secret store and expose it as `MEMHOUSE_ADMIN_PASSWORD` for
|
|
52
|
+
that step only.
|
|
53
|
+
|
|
54
|
+
## Typing it once, in a terminal
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
memhouse invite alice --url https://house.example.com --admin-user default
|
|
58
|
+
# prompts for the admin password; what you type or paste is not shown
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Or, to reuse it for a few commands in one shell without it reaching history:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
read -rs MEMHOUSE_ADMIN_PASSWORD && export MEMHOUSE_ADMIN_PASSWORD
|
|
65
|
+
memhouse members --admin-user default
|
|
66
|
+
unset MEMHOUSE_ADMIN_PASSWORD
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Keeping it on this machine
|
|
70
|
+
|
|
71
|
+
`install --admin-user … --keep-admin` writes the admin credential into
|
|
72
|
+
`~/.memhouse/env`, so later `invite` and `members` need no flags. `deploy --local` does
|
|
73
|
+
the same, because a local house's admin is yours. Otherwise memhouse does not save it.
|
|
74
|
+
|
|
75
|
+
Even when it is saved, the daemons never carry it: service units and the started
|
|
76
|
+
shipper and dashboard have `MEMHOUSE_ADMIN_*` removed from their environment.
|
|
77
|
+
|
|
78
|
+
To stop keeping it, delete the `MEMHOUSE_ADMIN_USER` and `MEMHOUSE_ADMIN_PASSWORD` lines
|
|
79
|
+
from the env file.
|
|
80
|
+
|
|
81
|
+
## What `whoami --admin` tells you
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
memhouse whoami --admin --json
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
This reports what the admin credential (`MEMHOUSE_ADMIN_*`, or the kept one) may
|
|
88
|
+
actually do on the server, for example `canProvision`. It never prints the password. Use
|
|
89
|
+
it before a scripted run, rather than guessing from a grant string.
|
|
90
|
+
|
|
91
|
+
## If a password reached a transcript
|
|
92
|
+
|
|
93
|
+
Treat it as burned. Rotate it on the server (`ALTER USER … IDENTIFIED BY …`, as the
|
|
94
|
+
admin) and update every place that legitimately holds it. Deleting the transcript does
|
|
95
|
+
not help: the house is insert-only, and it may already have shipped.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
Everything memhouse needs is in one env file, `~/.memhouse/env` (mode 600), or in the
|
|
4
|
+
directory named by `MEMHOUSE_HOME`. Values resolve in this order:
|
|
5
|
+
|
|
6
|
+
1. command-line flags;
|
|
7
|
+
2. exported `MEMHOUSE_*` variables;
|
|
8
|
+
3. `$MEMHOUSE_HOME/env`.
|
|
9
|
+
|
|
10
|
+
With nothing configured, commands that touch the house refuse and say so. There is no
|
|
11
|
+
default house.
|
|
12
|
+
|
|
13
|
+
| Variable | Meaning |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `MEMHOUSE_URL` | the house's HTTP(S) endpoint |
|
|
16
|
+
| `MEMHOUSE_USER` / `MEMHOUSE_PASSWORD` | the member credential the shipper writes as |
|
|
17
|
+
| `MEMHOUSE_DB` | the database holding the rooms, normally `mem` |
|
|
18
|
+
| `MEMHOUSE_PORT` | the dashboard port, default `4640` |
|
|
19
|
+
| `MEMHOUSE_CHANNEL` | the npm dist-tag `update` follows |
|
|
20
|
+
| `MEMHOUSE_EDITORS` | which adapters run (default: all) |
|
|
21
|
+
| `MEMHOUSE_<EDITOR>_ROOTS` | where one adapter looks |
|
|
22
|
+
| `MEMHOUSE_HOME` | where the env file, host identity, logs and pidfiles live |
|
|
23
|
+
| `MEMHOUSE_ENGINE` | `docker` or `podman`, for `deploy --local` |
|
|
24
|
+
|
|
25
|
+
Never print this file or these variables in a terminal an agent is reading. memhouse ships
|
|
26
|
+
transcripts, and a password that appears in one is in the archive.
|
|
27
|
+
|
|
28
|
+
## Which sessions ship
|
|
29
|
+
|
|
30
|
+
By default, everything on the machine: every adapter, from wherever that editor keeps its
|
|
31
|
+
sessions. `memhouse discover` prints what each adapter watches and the variable that moves
|
|
32
|
+
it:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
Watched directories (override with the variable shown, in the env file):
|
|
36
|
+
claude ~/.claude, ~/.claude-playbooks/kommander, … [MEMHOUSE_CLAUDE_ROOTS]
|
|
37
|
+
codex ~/.codex [MEMHOUSE_CODEX_ROOTS]
|
|
38
|
+
gemini-cli ~/.gemini [MEMHOUSE_GEMINI_CLI_ROOTS]
|
|
39
|
+
cursor ~/Library/Application Support/Cursor/User, … [built-in]
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Two kinds of line in the env file narrow it, or `--editors` / `--claude-roots` at install:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
MEMHOUSE_EDITORS='claude'
|
|
46
|
+
MEMHOUSE_CLAUDE_ROOTS='~/.claude-playbooks/kommander-chaos'
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
- `MEMHOUSE_EDITORS` names the adapters to run.
|
|
50
|
+
- `MEMHOUSE_<EDITOR>_ROOTS` replaces one adapter's location. The variable name is the
|
|
51
|
+
adapter's name upper-cased, with dashes turned into underscores.
|
|
52
|
+
- Claude is the one adapter with many roots, one per Claude Code config directory, so its
|
|
53
|
+
list may hold several. Every other adapter has one store and takes one path.
|
|
54
|
+
- Adapters marked `[built-in]` resolve a platform app-data directory and cannot be moved
|
|
55
|
+
yet.
|
|
56
|
+
|
|
57
|
+
An adapter name or a directory that does not exist is refused and reported against that
|
|
58
|
+
adapter. It is never shipped from the default instead: a typo that quietly shipped
|
|
59
|
+
nothing would look like a working install with an empty house.
|
|
60
|
+
|
|
61
|
+
Restart the shipper after changing these (`memhouse stop && memhouse start`, or
|
|
62
|
+
`memhouse service restart`).
|
|
63
|
+
|
|
64
|
+
## Instances: several memhouses on one machine
|
|
65
|
+
|
|
66
|
+
An **instance** is a home. It has:
|
|
67
|
+
|
|
68
|
+
- an env file naming a house;
|
|
69
|
+
- the daemons shipping to that house;
|
|
70
|
+
- the playbooks whose sessions it ships (`MEMHOUSE_CLAUDE_ROOTS`).
|
|
71
|
+
|
|
72
|
+
A machine can run several, each under its own `MEMHOUSE_HOME` and on its own channel:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
MEMHOUSE_HOME=~/.memhouse-work memhouse install --env invite-work.env
|
|
76
|
+
MEMHOUSE_HOME=~/.memhouse-work memhouse instance # which memhouse this is
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`memhouse instance` prints one screen:
|
|
80
|
+
|
|
81
|
+
- the instance's name and home;
|
|
82
|
+
- the binary and its channel;
|
|
83
|
+
- the house, its database and member, and whether an admin credential is present;
|
|
84
|
+
- the rooms, with row counts;
|
|
85
|
+
- what this machine ships;
|
|
86
|
+
- the host identity;
|
|
87
|
+
- the daemons;
|
|
88
|
+
- which playbooks are bound to this instance.
|
|
89
|
+
|
|
90
|
+
`plugins install claude`, run from an instance, installs the skills into exactly the
|
|
91
|
+
playbooks that instance ships, and **binds** them. It writes `MEMHOUSE_HOME` and
|
|
92
|
+
`MEMHOUSE_BIN` into each playbook's `settings.json` `env`, and Claude Code applies that to
|
|
93
|
+
every session there. So the `/mem:*` skills in a bound playbook read that instance's house
|
|
94
|
+
and run that instance's binary, whatever the launching shell had set. `plugins list` shows
|
|
95
|
+
the binding; `plugins remove claude` takes it out.
|
|
96
|
+
|
|
97
|
+
## Channels
|
|
98
|
+
|
|
99
|
+
`memhouse update` follows the npm dist-tag the install came from, not `latest` by reflex.
|
|
100
|
+
|
|
101
|
+
- The channel is inferred from the installed version.
|
|
102
|
+
- To pin it, add `MEMHOUSE_CHANNEL='team'` to the env file, or run
|
|
103
|
+
`memhouse update --channel team`, which writes the same line.
|
|
104
|
+
- An invite from a house on a channel carries that channel, so the invitee follows it
|
|
105
|
+
too.
|
|
106
|
+
- A tarball or checkout build sits on no tag and is never auto-updated. `update --check`
|
|
107
|
+
says so.
|
|
108
|
+
|
|
109
|
+
Today there is one release line: `latest`, with `team` as an alias for the same build.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Daemons and services
|
|
2
|
+
|
|
3
|
+
memhouse runs two long-lived processes:
|
|
4
|
+
|
|
5
|
+
- the **shipper**, which runs an incremental pass every 300 seconds;
|
|
6
|
+
- the **dashboard**, on `http://localhost:4640` (`MEMHOUSE_PORT` changes it).
|
|
7
|
+
|
|
8
|
+
## Until the next reboot
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
memhouse start # both, detached, with pidfiles in $MEMHOUSE_HOME
|
|
12
|
+
memhouse stop
|
|
13
|
+
memhouse status # running or not, and whether the last pass succeeded
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`start` detaches, so the processes outlive the shell, but not a restart.
|
|
17
|
+
|
|
18
|
+
## Across reboots
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
memhouse service install # systemd --user on Linux, a LaunchAgent on macOS
|
|
22
|
+
memhouse service install --interval 600
|
|
23
|
+
memhouse service status | stop | start | restart | uninstall
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`service install` writes a real user service and takes over from any `start` daemons.
|
|
27
|
+
|
|
28
|
+
- **The unit gets the member credential only.** `MEMHOUSE_ADMIN_*` is dropped before the
|
|
29
|
+
environment is written into the unit, and the shipper and dashboard are started without
|
|
30
|
+
it, even when your shell exported it.
|
|
31
|
+
- **On Linux, a `--user` unit stops at logout unless lingering is on.** `service install`
|
|
32
|
+
detects this and prints the `loginctl enable-linger <you>` command.
|
|
33
|
+
- **The unit runs the Node binary that installed it.** After a Homebrew or nvm Node
|
|
34
|
+
upgrade, run `memhouse service uninstall`, then `memhouse service install`, so the unit
|
|
35
|
+
points at the new one.
|
|
36
|
+
|
|
37
|
+
## One pass by hand
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
memhouse ship # one incremental pass, in the foreground
|
|
41
|
+
memhouse ship --full # re-read every session, not just the changed ones
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
A pass is incremental. A session is skipped when the house already holds it at least as
|
|
45
|
+
fresh and at least as large. For a session that grew, only the new turns are sent, when
|
|
46
|
+
every earlier turn is already in the house unchanged. `--full` rereads everything. It
|
|
47
|
+
never deletes anything: see [How memory is stored](storage.md).
|
|
48
|
+
|
|
49
|
+
## Logs
|
|
50
|
+
|
|
51
|
+
The daemons log to `$MEMHOUSE_HOME/logs/` (default `~/.memhouse/logs/`): `shipper.log` and
|
|
52
|
+
`dashboard.log`. `memhouse status` reads the shipper's last pass from its log, and reports
|
|
53
|
+
a failing pass as a failure even while the process is alive.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Install and onboard
|
|
2
|
+
|
|
3
|
+
memhouse is one npm package with no build step and no native dependency. It needs
|
|
4
|
+
**Node 24 or newer**. SQLite comes from `node:sqlite`, which is stable in 24. On 22.13+ it
|
|
5
|
+
works but prints an `ExperimentalWarning` on every command, and Node 20 is past end of life.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g memhouse
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Then pick the path that matches your situation.
|
|
12
|
+
|
|
13
|
+
| You have | Run |
|
|
14
|
+
|---|---|
|
|
15
|
+
| nothing yet, and want the guided route | `memhouse onboard` |
|
|
16
|
+
| nothing, but docker or podman | `memhouse deploy --local` |
|
|
17
|
+
| an invite file from someone's house | `memhouse install --env invite-<you>.env` |
|
|
18
|
+
| a ClickHouse and a member credential | `memhouse install --url … --user … --db mem` |
|
|
19
|
+
| a ClickHouse and its admin credential | `memhouse install --url … --admin-user … --member <you>` |
|
|
20
|
+
| a ClickHouse someone else administers | `memhouse install --print-sql`, then hand them the SQL |
|
|
21
|
+
|
|
22
|
+
## `onboard`: the guided route
|
|
23
|
+
|
|
24
|
+
`memhouse onboard` is interactive:
|
|
25
|
+
|
|
26
|
+
1. it discovers your editors;
|
|
27
|
+
2. it asks where the house is. If none exists and docker or podman is present, it offers
|
|
28
|
+
to run one locally;
|
|
29
|
+
3. it ships;
|
|
30
|
+
4. it offers to start the daemons and to install the Claude Code skills.
|
|
31
|
+
|
|
32
|
+
Everything it does can also be done with the scriptable commands below.
|
|
33
|
+
|
|
34
|
+
## `deploy --local`: a house on this machine
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
memhouse deploy --local # ClickHouse 25.11 on 127.0.0.1:8123
|
|
38
|
+
memhouse deploy --local --house-port 18123 # another port, if 8123 is taken
|
|
39
|
+
memhouse deploy --local --tag 26.7 # another ClickHouse version
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
It runs ClickHouse in a container bound to loopback, with the data in a named volume. The
|
|
43
|
+
container's superuser is `memhouse_root`, which administers the house. You ship as a
|
|
44
|
+
**member** named after your OS user (the command asks; `--user` sets it). The member holds
|
|
45
|
+
one grant, on `mem.<you>_*`. Both credentials go into `~/.memhouse/env`, mode 600, so that
|
|
46
|
+
inviting someone later needs no flags. It then installs and ships.
|
|
47
|
+
|
|
48
|
+
The user name and password are fixed when the volume is first initialised and reused on
|
|
49
|
+
every redeploy. `memhouse deploy --down` removes the container **and the volume, which
|
|
50
|
+
deletes the memory**.
|
|
51
|
+
|
|
52
|
+
When both docker and podman are installed and one of them cannot answer, choose with
|
|
53
|
+
`MEMHOUSE_ENGINE=docker` or `MEMHOUSE_ENGINE=podman`. With rootless podman on Linux, the
|
|
54
|
+
container may stop at logout unless lingering is on. `deploy` detects this and prints the
|
|
55
|
+
`loginctl enable-linger` command.
|
|
56
|
+
|
|
57
|
+
## `install`: the scriptable route
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# a member credential you already have
|
|
61
|
+
MEMHOUSE_PASSWORD=… memhouse install --url https://house.example.com --user alice --db mem --yes
|
|
62
|
+
|
|
63
|
+
# from an invite file (the usual way to join a team)
|
|
64
|
+
memhouse install --env invite-alice.env
|
|
65
|
+
|
|
66
|
+
# you hold the server's admin: memhouse creates the member, then ships as the member
|
|
67
|
+
with-secret MEMHOUSE_ADMIN_PASSWORD=keychain:pilot/house-admin -- \
|
|
68
|
+
memhouse install --url https://house.example.com --admin-user default --member alice
|
|
69
|
+
|
|
70
|
+
# you hold nothing: print the SQL for whoever administers the server
|
|
71
|
+
memhouse install --print-sql --member alice
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Useful flags:
|
|
75
|
+
|
|
76
|
+
| Flag | Effect |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `--yes` | no questions; take the defaults |
|
|
79
|
+
| `--no-ship` | configure and verify, but do not ship yet |
|
|
80
|
+
| `--editors claude,codex` | ship only these adapters ([configuration](configuration.md)) |
|
|
81
|
+
| `--claude-roots DIR[,DIR]` | ship only these Claude Code config directories |
|
|
82
|
+
| `--channel team` | follow this npm dist-tag on `update` |
|
|
83
|
+
| `--keep-admin` | keep the admin credential in the env file (not done by default) |
|
|
84
|
+
| `--ensure-schema` | create any room or stats table the house is missing |
|
|
85
|
+
|
|
86
|
+
The admin password never needs to be on the command line. See
|
|
87
|
+
[Admin password](admin-password.md).
|
|
88
|
+
|
|
89
|
+
`install --env <file>` reads the connection **only** from the file. Exported `MEMHOUSE_*`
|
|
90
|
+
variables are ignored, including `MEMHOUSE_EDITORS` and `MEMHOUSE_CLAUDE_ROOTS`. To narrow
|
|
91
|
+
what an invite install ships, pass `--editors` / `--claude-roots` as flags.
|
|
92
|
+
|
|
93
|
+
### What install checks
|
|
94
|
+
|
|
95
|
+
Before it writes any config, install connects, confirms who the credential is, and checks
|
|
96
|
+
that it can reach its rooms. The config is written last, so a refused install leaves no
|
|
97
|
+
half-configured machine behind.
|
|
98
|
+
|
|
99
|
+
Configuration resolves in this order: flags, then exported `MEMHOUSE_*` variables, then
|
|
100
|
+
`~/.memhouse/env`. There is no built-in default house. With nothing configured, commands
|
|
101
|
+
that read or write memory refuse and say so. They never guess `localhost:8123`, which on
|
|
102
|
+
many machines is somebody else's house.
|
|
103
|
+
|
|
104
|
+
## `EACCES` on install
|
|
105
|
+
|
|
106
|
+
`sudo` is right for only one of the two causes. What tells them apart is **who owns the
|
|
107
|
+
prefix root**, not the path, and not the file npm named.
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
ls -ld "$(npm prefix -g)"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
- **Owned by `root`:** a system-managed Node (distro packages under `/usr`). Re-run with
|
|
114
|
+
`sudo npm install -g memhouse`.
|
|
115
|
+
- **Owned by you:** the prefix is yours (Homebrew, fnm, nvm, volta). The root-owned file
|
|
116
|
+
npm tripped on is left over from an earlier `sudo npm`, and another `sudo` adds more.
|
|
117
|
+
Repair just that path: `sudo chown -R "$(id -u):$(id -g)" <the path npm named>`. Do the
|
|
118
|
+
same for `~/.npm` if npm names it.
|
|
119
|
+
|
|
120
|
+
The path proves nothing on its own: Homebrew's prefix is `/usr/local` on Intel and
|
|
121
|
+
`/opt/homebrew` on Apple Silicon, and it is yours in both cases.
|
|
122
|
+
|
|
123
|
+
## After installing
|
|
124
|
+
|
|
125
|
+
- `memhouse status`: connection, counts, freshness.
|
|
126
|
+
- `memhouse start` or `memhouse service install`: keep shipping. See [Daemons](daemons.md).
|
|
127
|
+
- `memhouse plugins install claude`: the `/mem:*` skills. See
|
|
128
|
+
[Search, resume and skills](search-and-resume.md).
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Invite members
|
|
2
|
+
|
|
3
|
+
Inviting creates a ClickHouse user and their rooms, and writes one file the invitee
|
|
4
|
+
installs from. It needs a credential that can create users, which means the server's
|
|
5
|
+
admin.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
memhouse invite alice --url https://house.example.com
|
|
9
|
+
# invite written: invite-alice.env
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
- **`--url` is the address the invitee will reach the house at,** not yours. It cannot be
|
|
13
|
+
loopback: `localhost` on their machine is their machine. Use a LAN address, a hostname
|
|
14
|
+
or a tunnel. `--allow-local` overrides this, for the case where the invitee is on the
|
|
15
|
+
same machine.
|
|
16
|
+
- **The rooms are named for them:** `mem.alice_sessions`, `mem.alice_messages`, and so on.
|
|
17
|
+
Alice is granted exactly `mem.alice_*`, nothing else. A different database:
|
|
18
|
+
`--db <name>`.
|
|
19
|
+
- **The file is a credential.** It holds the house URL, alice's user name, a generated
|
|
20
|
+
password and the database. Hand it over a channel you trust: croc, a password manager.
|
|
21
|
+
Not chat or email. `--out <path>` writes it somewhere else.
|
|
22
|
+
- **Alice installs with `memhouse install --env invite-alice.env`.** It offers to rotate
|
|
23
|
+
the password to one only she knows, which works because she is granted `ALTER USER` on
|
|
24
|
+
herself. Then it deletes the file. Once she rotates, the password you saw stops working.
|
|
25
|
+
|
|
26
|
+
## Which admin credential
|
|
27
|
+
|
|
28
|
+
`invite` uses, in order:
|
|
29
|
+
|
|
30
|
+
1. the admin credential this install keeps. `deploy --local` keeps one;
|
|
31
|
+
`install --keep-admin` keeps one;
|
|
32
|
+
2. otherwise, `--admin-user <name>` plus the password from `MEMHOUSE_ADMIN_PASSWORD`, from
|
|
33
|
+
`--admin-password-file`, or from a prompt.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
with-secret MEMHOUSE_ADMIN_PASSWORD=keychain:pilot/house-admin -- \
|
|
37
|
+
memhouse invite alice --url https://house.example.com --admin-user default
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Never paste the admin password into an agent's conversation. memhouse ships that
|
|
41
|
+
conversation into the house. See [Admin password](admin-password.md).
|
|
42
|
+
|
|
43
|
+
Not the admin? `memhouse invite alice --url … --print-sql` prints the statements for
|
|
44
|
+
whoever is, plus the env-file lines to hand alice. The printed plan never holds a password:
|
|
45
|
+
it says `IDENTIFIED BY <member-password>`, which does not parse until the administrator
|
|
46
|
+
puts a quoted password they generate in its place, and the same password goes into the
|
|
47
|
+
env file. `install --print-sql` works the same way.
|
|
48
|
+
|
|
49
|
+
## When the name is taken
|
|
50
|
+
|
|
51
|
+
`invite` refuses a name whose messages room already holds rows. Inviting into it would
|
|
52
|
+
hand the newcomer somebody else's memory, and their first ship would land on top of it.
|
|
53
|
+
It suggests another name or another database.
|
|
54
|
+
|
|
55
|
+
`--adopt` overrides the refusal for one case: **the same person with a new credential**.
|
|
56
|
+
For example, someone lost their password and their old machine. The rooms and their
|
|
57
|
+
history stay; the new credential takes them over.
|
|
58
|
+
|
|
59
|
+
**Inviting a ClickHouse user who already exists is refused.** memhouse does not reset an
|
|
60
|
+
existing member's password through an invite.
|
|
61
|
+
|
|
62
|
+
- To change your own password: `memhouse passwd`. Since 0.11 a member may alter their own
|
|
63
|
+
account, so no admin is needed. memhouse writes the new password to the env file and
|
|
64
|
+
never prints it.
|
|
65
|
+
- There is no verb for resetting **someone else's** password. If a member is locked out,
|
|
66
|
+
an administrator runs `ALTER USER <name> IDENTIFIED BY '<new password>'` in a ClickHouse
|
|
67
|
+
client, then hands the member an env file holding it, the way an invite does. Keep the
|
|
68
|
+
new password out of any agent's conversation.
|
|
69
|
+
|
|
70
|
+
Either way, every machine shipping under the old password fails authentication until its
|
|
71
|
+
env file has the new one.
|
|
72
|
+
|
|
73
|
+
## Seeing who is in a house
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
memhouse members # every member, and what each one's grants reach
|
|
77
|
+
memhouse members --db other # another database
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`members` needs the admin credential. A member sees their own grants with
|
|
81
|
+
`memhouse whoami`.
|
|
82
|
+
|
|
83
|
+
## Removing a member
|
|
84
|
+
|
|
85
|
+
There is no `memhouse` verb for it yet. With the admin credential, `DROP USER <name>`
|
|
86
|
+
stops every machine shipping as them. Their rooms stay until you drop them too. See
|
|
87
|
+
[Operate a house](operate.md) before you drop anything: there is no undo.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Operate a house
|
|
2
|
+
|
|
3
|
+
For whoever administers the ClickHouse server behind a house. Each command below takes the
|
|
4
|
+
admin credential as described in [Admin password](admin-password.md). From Claude
|
|
5
|
+
Code, `/mem:admin` does the same work and keeps the password out of the conversation.
|
|
6
|
+
|
|
7
|
+
## Who is here
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
memhouse members # every member of the house, and what each grant reaches
|
|
11
|
+
memhouse members --db other
|
|
12
|
+
memhouse whoami --admin # what the admin credential may actually do
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Useful read-only SQL, run as the admin:
|
|
16
|
+
|
|
17
|
+
```sql
|
|
18
|
+
-- every house and its size
|
|
19
|
+
SELECT database, formatReadableSize(sum(bytes_on_disk)) AS disk, sum(rows) AS rows
|
|
20
|
+
FROM system.parts WHERE active GROUP BY database ORDER BY sum(bytes_on_disk) DESC;
|
|
21
|
+
|
|
22
|
+
-- one member's rooms
|
|
23
|
+
SELECT table, formatReadableSize(sum(bytes_on_disk)) AS disk, sum(rows) AS rows
|
|
24
|
+
FROM system.parts WHERE active AND database = 'mem' AND table LIKE 'alice\_%'
|
|
25
|
+
GROUP BY table;
|
|
26
|
+
|
|
27
|
+
-- what someone may do
|
|
28
|
+
SHOW GRANTS FOR alice;
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Adding and removing people
|
|
32
|
+
|
|
33
|
+
- **Add:** [`memhouse invite`](invite.md).
|
|
34
|
+
- **Reset a member's password:** the member runs `memhouse passwd` themselves. For a
|
|
35
|
+
member who is locked out there is no verb: run `ALTER USER <name> IDENTIFIED BY '…'` as
|
|
36
|
+
the admin, and hand them an env file with the new password. Every machine shipping as
|
|
37
|
+
them stops until its env file has it.
|
|
38
|
+
- **Remove:** there is no verb yet. `DROP USER <name>` stops every machine shipping as
|
|
39
|
+
that member. Their rooms remain until you drop them as well.
|
|
40
|
+
|
|
41
|
+
**Before any destructive statement, look first.**
|
|
42
|
+
|
|
43
|
+
- Count the rows in each room, find who wrote them and when, and from which machines.
|
|
44
|
+
- Run every predicate as a `SELECT` before you run it as a `DELETE` or `DROP`.
|
|
45
|
+
|
|
46
|
+
There are no backups unless you made them, and the transcripts behind a room may already
|
|
47
|
+
be gone from the editors that produced them.
|
|
48
|
+
|
|
49
|
+
## Migrations
|
|
50
|
+
|
|
51
|
+
When a release needs the rooms rebuilt, the shipper refuses to write until the house is
|
|
52
|
+
migrated:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
memhouse migrate --dry-run
|
|
56
|
+
memhouse migrate # copy, atomic swap; the old room is kept as <room>_pre_<suffix>
|
|
57
|
+
memhouse migrate-rooms # the same, rooms only
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Nothing is deleted. Drop the `_pre_` tables when you are satisfied. See
|
|
61
|
+
[Update](update.md#when-the-house-itself-must-change).
|
|
62
|
+
|
|
63
|
+
## Convert
|
|
64
|
+
|
|
65
|
+
A house from before 0.18 has a database per member. `memhouse convert` moves it to the one
|
|
66
|
+
layout, `mem.<member>_*`:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
memhouse convert --admin-user default --dry-run # what would move
|
|
70
|
+
memhouse convert --admin-user default --print-sql # the statements, to run yourself
|
|
71
|
+
memhouse convert --admin-user default # do it
|
|
72
|
+
memhouse convert --admin-user default --guides --out ~/Downloads/upgrade
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`--guides` writes a per-member upgrade guide. Members then run `memhouse update`, and
|
|
76
|
+
their shipper adopts the moved rooms itself. Take a server snapshot first.
|
|
77
|
+
|
|
78
|
+
## Moving a house to another server
|
|
79
|
+
|
|
80
|
+
`memhouse relocate --to <url>` copies a whole house server-to-server and repoints this
|
|
81
|
+
install. **Known gap:** relocate predates the one layout and fails on a one-layout house
|
|
82
|
+
before it changes anything. Until it is updated, moving a house is an operator
|
|
83
|
+
task with ClickHouse's own tools (`BACKUP`/`RESTORE`, or `INSERT … SELECT FROM remote()`).
|
|
84
|
+
Afterwards, point each member at the new URL with `memhouse setup --url …`.
|
|
85
|
+
|
|
86
|
+
## Backups
|
|
87
|
+
|
|
88
|
+
memhouse does not back up the house. Use ClickHouse's own `BACKUP DATABASE mem TO …`, to a
|
|
89
|
+
disk or an object store, on a schedule, and test a `RESTORE` into a scratch server.
|
|
90
|
+
[`examples/04-self-hosted-team`](../../examples/04-self-hosted-team/README.md) shows a nightly job,
|
|
91
|
+
rotation, and a tested restore.
|
|
92
|
+
|
|
93
|
+
- **Keep backups as private as the house.** They hold every member's transcripts.
|
|
94
|
+
- **Backups outlive deletions.** A row removed from the house remains in every backup
|
|
95
|
+
taken before the removal, until that backup rotates out.
|
|
96
|
+
|
|
97
|
+
## Server settings that matter
|
|
98
|
+
|
|
99
|
+
| setting | why |
|
|
100
|
+
|---|---|
|
|
101
|
+
| async inserts off for members | `user_id` is stamped at insert; memhouse pins `async_insert=0` on each member, and the shipper sets it per insert too |
|
|
102
|
+
| `users_without_row_policies_can_read_rows` | scoped shares need it to behave as memhouse expects; `share` measures it and refuses if a policy would blind the owner |
|
|
103
|
+
| `old_parts_lifetime` | how long replaced parts stay on disk after merges and mutations (480 s by default) |
|
|
104
|
+
| HTTP behind TLS | members connect over HTTP(S); put the server behind TLS before exposing it beyond a LAN |
|
|
105
|
+
|
|
106
|
+
ClickHouse 25.11 and 26.x are tested in CI.
|
|
107
|
+
|
|
108
|
+
## A build to test with
|
|
109
|
+
|
|
110
|
+
`memhouse nightly --out DIR`, run from a checkout, builds an installable, version-stamped
|
|
111
|
+
tarball and publishes nothing. Install it on a test machine with
|
|
112
|
+
`npm install -g <tarball>`.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Search, resume and skills
|
|
2
|
+
|
|
3
|
+
## Search
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
memhouse search "connection refused"
|
|
7
|
+
memhouse search clickhouse auth --limit 50
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
This is a full-text search across every session in your rooms, from every machine and
|
|
11
|
+
every editor. Each hit names the session id, project, date and the matching line.
|
|
12
|
+
|
|
13
|
+
## Resume
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
memhouse resume claude-code:6b1f…
|
|
17
|
+
# cd /path/to/project && claude --resume 6b1f…
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`resume` **prints** the command and does not run it. A resume from the wrong directory,
|
|
21
|
+
or with a stale id, does not fail: it opens a **new** session, and the tool reports
|
|
22
|
+
success while the transcript you wanted is still gone. Printing it lets you see the
|
|
23
|
+
directory and the id first.
|
|
24
|
+
|
|
25
|
+
| editor | resume |
|
|
26
|
+
|---|---|
|
|
27
|
+
| Claude Code, Codex, OpenCode | verified: prints the command |
|
|
28
|
+
| Cursor, Zed, VS Code, Kiro, Copilot for JetBrains, Antigravity, Devin | no CLI that takes a session id; `resume` says so |
|
|
29
|
+
| goose, Gemini CLI, Cursor Agent, Copilot CLI | refused as *unverified*: nobody has confirmed the flag from the tool's own `--help` yet |
|
|
30
|
+
|
|
31
|
+
## Numbers
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
memhouse stats # sessions, messages and tokens per editor
|
|
35
|
+
memhouse sessions-query # the per-session rollup as SQL, to paste into your own queries
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The dashboard (`memhouse start`, then http://localhost:4640) shows the same and more:
|
|
39
|
+
|
|
40
|
+
- cost by model, editor and project;
|
|
41
|
+
- tool-call rankings;
|
|
42
|
+
- activity over time;
|
|
43
|
+
- which machine wrote what.
|
|
44
|
+
|
|
45
|
+
**Some editors record no token usage.** Kiro, VS Code, Zed and one of Cursor's two
|
|
46
|
+
storage paths extract none, so their cost shows as zero. Read that as *unknown*, not
|
|
47
|
+
*free*.
|
|
48
|
+
|
|
49
|
+
## Skills for your agents
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
memhouse plugins install claude # every Claude Code config dir found; choose, or --yes for all
|
|
53
|
+
memhouse plugins install claude --target ~/.claude
|
|
54
|
+
memhouse plugins list
|
|
55
|
+
memhouse plugins remove claude
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The five skills:
|
|
59
|
+
|
|
60
|
+
| skill | for |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `/mem:recall` | answer a question from past sessions: search, read the transcripts, answer, cite |
|
|
63
|
+
| `/mem:house` | is memory working, what does the house hold, which machines write, who can read it |
|
|
64
|
+
| `/mem:sql` | read-only SQL for numbers: spend, models, tools, activity |
|
|
65
|
+
| `/mem:access` | invite someone, share your house, list or revoke shares |
|
|
66
|
+
| `/mem:admin` | operate the server as its administrator, without the password entering the conversation |
|
|
67
|
+
|
|
68
|
+
`plugins install` acts on **every** Claude Code config directory on the machine:
|
|
69
|
+
`~/.claude`, whatever `CLAUDE_CONFIG_DIR` points at, and each playbook under
|
|
70
|
+
`~/.claude-playbooks/`. All are selected by default. Installing into the default config
|
|
71
|
+
alone is how `/mem:recall` ends up missing from the playbook you actually work in, and a
|
|
72
|
+
missing skill never announces itself.
|
|
73
|
+
|
|
74
|
+
For an agent that does not use Claude Code skills, `memhouse prompt` prints a snippet for
|
|
75
|
+
its system prompt: where memory lives and how to query it.
|
|
76
|
+
|
|
77
|
+
## Writing your own queries
|
|
78
|
+
|
|
79
|
+
The rooms are documented column by column in
|
|
80
|
+
[`memhouse/house/HOUSE.md`](../../memhouse/house/HOUSE.md). Two rules matter for every
|
|
81
|
+
query:
|
|
82
|
+
|
|
83
|
+
1. **Join on `(session_id, user_id)`**, never on `session_id` alone. Two members' editors
|
|
84
|
+
can produce the same local session id.
|
|
85
|
+
2. **Filter to the current parse.** A session's rows can hold more than one parse (an
|
|
86
|
+
`epoch`; see [How memory is stored](storage.md)). Counts must keep only the newest:
|
|
87
|
+
|
|
88
|
+
```sql
|
|
89
|
+
WHERE origin != 'ship'
|
|
90
|
+
OR (session_id, user_id, epoch) IN (
|
|
91
|
+
SELECT session_id, user_id, max(epoch) FROM mem.<you>_messages
|
|
92
|
+
WHERE origin = 'ship' GROUP BY session_id, user_id)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Read with `FINAL`, or the `final=1` setting, so that ReplacingMergeTree versions collapse.
|