DOCS

Everything the CLI already knows,
on one page.

This page is generated by hand from the same source the CLI's own memhouse --help and README.md come from, kept here so it works whether or not you have access to the source repository. Install, concepts, the full command reference, team & sharing, staying current, troubleshooting.

Getting started

One command, no flags, no compiler — Node 24's own node:sqlite covers every adapter that used to need a native binding.

# install
$ npm install -g memhouse

# no ClickHouse yet? stand one up locally, loopback-bound
$ memhouse deploy --local

# the wizard: discover → configure → ship → start
$ memhouse onboard
 17 adapters · 9 with sessions on this machine
 sessions shipped · dashboard on http://localhost:4640

# read-only preflight — writes nothing
$ npx -y memhouse discover

Already have a ClickHouse — your own server, ClickHouse Cloud, one provisioned by an ultimagent kernel? memhouse setup --yes --url … --user … --password … --db … writes the config directly, no wizard.

Every command is dual-mode: interactive for a human, --yes / flags / --json for an agent. An agent can install its own memory, unattended.

Concepts

A house is a ClickHouse database. Three shared tables are its rooms. Everything else follows from that.

House, rooms, member

sessions, messages, tool_calls — real typed columns for what other tools derive at query time, plus one extra JSON escape hatch per table so no adapter field is ever dropped. A member is a ClickHouse user with a database (their house) and full grants on it.

Provenance no client can fake

user_id is MATERIALIZED currentUser() — the server stamps it during the insert, with async inserts pinned off so the stamp cannot be skipped. host is a random fingerprint minted once per install (~/.memhouse/host.json), not derived from the hostname — two laptops both called MacBook-Pro stay two machines, and renaming one doesn't split its history.

One person: WHERE user_id = 'alice'. One machine: WHERE host = 'macbook-4127a95b'. The whole house: no filter.

Rows carry an epoch — nothing is deleted to make room

A re-ship that comes back shorter used to overwrite the longer parse it replaced. Now every row in messages/tool_calls carries an epoch: unchanged sessions keep their epoch, a changed session writes under a new one, and the old epoch stays complete and queryable. Reads default to the current epoch only; sessions is the deliberate exception — one row per session, no epoch in its key. Rows also carry origin (ship vs import), so the shipper's own clear can never touch history it didn't write.

Commands

Every command is scriptable. --help or -h on any of them prints this same reference instead of running anything.

CommandWhat it does
onboardInteractive wizard: discover → configure → ship → start.
installScriptable setup — --url --user --password --db [--yes] [--no-ship]. --env FILE installs from an invite file.
invite <name>Mint a member + house on the server, write the env file their install needs — --url --admin-user --admin-password [--db NAME] [--out FILE]. This machine untouched; the admin credential is never stored.
passwdRotate this member's password and rewrite the env file. Admin-assisted (--admin-user --admin-password) or --print-sql to run it yourself.
setup(Re)write the connection config only. --yes skips every prompt.
discoverRead-only preflight: editors found, sessions on disk, reachable ClickHouses. Writes nothing.
uninstallStop daemons + service, clear runtime state. Default tier keeps config and host identity — --credentials also forgets the house, --full-removal takes everything (asks first). No tier touches house data.
updateUpgrade the package, restart the daemons, migrate the house if needed. --check compares versions only.
resetClear the shipper's rows and re-ship everything (asks; --yes skips). Imported rows are kept unless --all-origins.
nightlyBuild an installable, version-stamped tarball from this checkout. Publishes nothing. [--out DIR]
shipOne incremental pass. --full re-ships everything; --loop [sec] runs continuously.
statsPer-source session/message/token counts.
search <terms…>Full-text search across every session.
resume <session-id>Print (not run) the command that reopens that session in its editor.
sessions-queryPrint the session-rollup SQL for the configured credential.
start / stopShipper loop + dashboard as background (pidfile) daemons.
statusDaemons, connection, counts, freshness. --json for scripts.
doctorDiagnose the whole pipeline — schema, fleet, pricing coverage, daemons.
pluginslist / install claude [--target DIR] / remove claude — acts on every Claude config dir found (~/.claude, CLAUDE_CONFIG_DIR, every ~/.claude-playbooks/*), all selected by default.
promptPrint the memory system-prompt snippet. --install prints an install prompt for an agent instead.
deploy --localRun ClickHouse in docker/podman, bound to loopback, then install. --down removes it (container + volume).
migrate / migrate-roomsRun every migration the house still needs (or just the rooms) — copy + atomic swap, deletes nothing. [--dry-run] [--yes]
relocate --to URLCopy the whole house to a new ClickHouse, server-to-server, and repoint this install. The shipper does not re-ingest; source is untouched until confirmed.
service installRun the shipper as a real user service (systemd --user / launchd). Also stop | start | restart | uninstall | status.

Config resolves: flags > MEMHOUSE_* env > ~/.memhouse/env > defaults. There is no house-shaped default — with nothing configured, commands that touch memory refuse rather than guess localhost:8123.

Team & sharing

Two different things: owning a house (invite) and reading someone else's (share).

Bringing in a housemate

memhouse invite <name> mints the ClickHouse user, their database, and the grants — GRANT ALL … WITH GRANT OPTION, so a member is a full owner of their own house and can hand grants on to someone else, but still cannot CREATE USER or reach another house. It writes a portable env file; the admin credential used to run invite is never stored anywhere. The invitee runs one command:

$ memhouse invite alice --url https://ch.example.com:8443 \
    --admin-user root --admin-password *****
 alice.env written

# alice runs, on her own machine:
$ memhouse install --env alice.env

Reading a housemate's memory without owning it

memhouse share opens a read-only SELECT window into your house for another member — for asking "how did bob fix this" without handing bob write access to your rooms. Members can also see who else is on the server (GRANT SHOW USERS ON *.*), so /mem:users can answer "everyone on this ClickHouse" without an admin.

Rotating your own password

$ memhouse passwd
 password rotated and ~/.memhouse/env updated
 every OTHER machine shipping under this user now needs the new
    password too — run `memhouse setup --password …` there.

memhouse passwd --help prints this reference instead of rotating anything — every command honors --help / -h before it touches the house.

Staying current

A bare npm i -g memhouse@latest does about two-thirds of the job.

memhouse update upgrades the package, restarts the daemons (which otherwise keep parsing with the code they booted with — one shipper here ran 1d16h out of a directory that had been moved), and checks whether the house is missing something the new shipper needs. update --check compares versions and changes nothing. Each daemon also checks on its own, once per pass, and hands over when it's behind — exiting under a supervisor (systemd/launchd) so it restarts on the new code, or re-executing itself when nothing is supervising it.

When the house itself needs to change

memhouse migrate runs whatever this house still needs — detected from the house's own house_meta record, not from anything the client remembers. Every migration is copy + atomic swap; the old room is kept as <room>_pre_epoch for you to drop once you've checked it. migrate --dry-run shows the plan without touching anything.

Moving to a different ClickHouse

memhouse relocate --to <url> copies the whole house server-to-server (remoteSecure()/remote() INSERT SELECT, preserving provenance) and repoints this install at the new one. It gates on a matching row count before switching, and the old env is kept until you confirm.

Troubleshooting

An editor reads zero sessions

Five adapters — cursor, zed, opencode, goose, antigravity — read sessions out of SQLite files. On versions before node:sqlite that meant a native binding and an easy-to-forget --allow-scripts flag; that failure class is gone now. What can still go wrong is one store at a time — a locked state.vscdb, a corrupt file, a schema an editor release changed — and it's never silent: memhouse discover and memhouse doctor both name the skipped adapter and say why, while the other sixteen keep working.

EACCES on install

Check who owns the prefix root, not the path: ls -ld "$(npm prefix -g)". Owned by root (a system-managed Node) — re-run with sudo. Owned by you (Homebrew, fnm, nvm, volta) — the prefix is already yours, and the root-owned file npm tripped on is a stray from an earlier sudo npm; fix just that path with chown instead of adding another sudo.

Daemons don't survive a reboot

memhouse start detaches with pidfiles, which outlive the shell but not a restart. memhouse service install writes a real user service — systemd --user on Linux, a launchd agent on macOS — and takes over. On Linux, a --user unit stops at logout unless lingering is enabled; install detects that and prints the exact loginctl command.

Still stuck

memhouse doctor diagnoses the whole pipeline in one pass — schema, fleet health, pricing coverage, daemon state — and says in words what's wrong rather than leaving you to infer it from a low number.