@kinedb/kdmem (0.1.11)
Installation
@kinedb:registry=https://git.kinedb.com/api/packages/kinedb/npm/npm install @kinedb/kdmem@0.1.11"@kinedb/kdmem": "0.1.11"About this package
kdmem: the shared live memory of a team
One central kinedb server (kdmem.kinedb.com) holds the shared state. A daemon on each machine
publishes the state of its Claude sessions. A web page shows all machines and branches.
Install
kdmem is published on the kinedb package registry, never on npmjs (browse: https://git.kinedb.com/kinedb/-/packages). Reading it needs no login. Run this ONE command on each machine. It makes the device key, installs the mod and starts the daemon:
bunx --package https://git.kinedb.com/api/packages/kinedb/npm/%40kinedb%2Fkdmem/-/0.1.11/kdmem-0.1.11.tgz kdmem --user you@yourcompany.example --company yourcompany
Replace you@yourcompany.example with your own e-mail address and yourcompany with the name of your own
company. The command stops with an error if you keep the example text. The number 0.1.11 is the
latest version; the package page names the newest one.
The command asks the server whether the company exists. A company that exists adds a NEW person by itself (B14): the
server makes the user, adds it to the company and attaches the key of the device, so nobody approves anything and the
setup ends with state: APPROVED. The device pins the root key of the company. The e-mail address is NOT proven:
anyone who knows the company name can join. The operator turns this off with KDMEM_AUTO_JOIN=0. An address that
already exists is never joined this way, because a key must not go onto an existing account. An admin of the company
adds that device on /manage (Devices tab). A new company is made by the command itself when the
server allows it (KDMEM_SELF_SERVICE=1; off on kdmem.kinedb.com): the user becomes the first admin, and the
key of the device becomes the root key of the company.
Status: @kinedb/kdmem 0.1.11 is published. The command of 0.1.5 is checked on Linux, macOS and Windows. NOT YET
checked for 0.1.6 to 0.1.11: the full command on macOS and Windows. 0.1.8 added the messages between sessions, the mod from the
database and the self reload of running sessions (B14). 0.1.9 adds the join of a new person with no admin step. 0.1.10 stops the
command when the example address or company of the README is kept. 0.1.11 carries the mod with the commands, hooks and prompts
from the database (a new machine gets them from the database at once anyway); the install of 0.1.11
is NOT YET run on a clean machine. The signup, the lists of signers and the company layer
are checked against the live server by the probe q14-signup-e2e.ts.
What starts the daemon
No operating system service starts it. The install command starts it once. After a restart, the
first Claude session that starts runs the mod hook: it reads the heartbeat file of the daemon
(~/.config/kdmem/heartbeat.json). A live daemon gives no command at all. A stale heartbeat (2
minutes) makes the hook run <daemon file> --ensure once, and that call starts the daemon
detached. One start in a minute at most. The daemon replaces its own code from the signed database.
The daemon does not poll for it: a WATCH on the table kd_assets wakes it when a skill or the code
changes (a safety pull every 5 minutes stays; without a working watch it pulls every 30 seconds).
The mod is a thin shell. A mod cannot evaluate script text (probe 2026-10-18: new Function and eval
fail with "Code generation from strings disallowed"), so it cannot load a script file. Its numbers
(the stale limit, the gap between starts, the check interval) come from ~/.config/kdmem/mod-config.json,
which the daemon writes at its start. A new daemon bundle from the database changes them with no
reinstall of the mod. The mod code itself also comes from the database (B14): bun daemon/assets-cli.ts <env> mod publishes the hooks and the
plugin.json as signed rows mod/.... A mod can load only files inside its own plugin folder, so the daemon writes a marketplace
folder of its own (~/.config/kdmem/mod-market) and runs claude plugin marketplace add|update and claude plugin install|update.
A running session reloads by itself: after each pull the daemon writes ~/.config/kdmem/reload.json ({ mod, skills }). The mod reads
the file every 5 seconds. A changed value makes the mod run the commands reload-plugins or reload-skills ($.command.run, the name
has no slash). Only a session that started before this feature needs one restart.
Messages between Claude sessions (B14)
The mod gives each Claude session two tools: peers (the other sessions) and send (a message to a session by name or session id).
It gives a third tool, context: it tells Claude the tokens used, the window size, the percent and the tokens left. Claude cannot see this itself.
A message reaches an idle session: the mod wakes it with a new turn that names the sender and says that it is not the user.
- One computer. The mod writes
~/.config/kdmem/msg/outbox/<id>.json. The daemon routes it intomsg/inbox/<session>/. The mod reads the inbox every 2 seconds and writes an.ackfile. - Two computers of one user. Put
"messageTable": "kd_messages"into~/.config/kdmem/device.json. The daemon then sends a message for another computer as a row of the table and delivers the rows for this computer. A message that cannot be delivered comes back as a message fromkdmemwith the reason. Without this setting the daemon sends nothing over the network. A message never crosses to another user: the server limits the rows byowner, and the daemon checks the owner again. A message row is NOT signed. - The table. Make it once, as root (
daemon/messages-remote.tsnames the columns):kd_messageswithmkey(key),owner,from_machine,from_session,from_name,to_machine,to_session,body,at,delivery,note, andGRANT INSERT, UPDATE, DELETE, SELECT ... TO MATCH owner. The server refusestextandstatusas column names. - The page. The fleet page has a Messages view: all messages, grouped by session, computer, project or user. Each session row shows how many it sent and received, with a button to its messages.
Files that sessions edit (B14)
The mod records each file that a session edits (the tools Edit, Write, MultiEdit and NotebookEdit) as a claim. A claim is live for
30 minutes after the last edit. The daemon sends the claims to the table fleet_files and gives the claims of all computers of the user
back to the mod.
- A read or a search. When Claude reads a file, or runs a
Bash,GreporGlobcall that names a file that another live session edited, the mod adds a note after the result. The note names the session and the computer, and tells Claude to use thesendtool. A function is not tracked: only a file. - An edit. The first edit of such a file is refused. The refusal gives the same note. The same edit again passes: Claude has then agreed with the other session, or it has decided to go on. The mod never blocks twice.
- The same file. Two claims are the same file when the path is equal, or when the repository and the path under its root are equal (another computer, or a worktree).
- The files.
~/.config/kdmem/files/<session>.json(the mod writes it) andfiles-all.json(the daemon writes it). - The table.
bun daemon/setup-fleet.tsmakesfleet_files(the columns are in that file) and the grants, as it does for the other two. - The page. The fleet page has a Files view, grouped by session, computer, project or user. A file that two sessions edit has a
shared with Nchip. A user who sees more than one company gets a list in the top bar to pick the company to watch. - Companies.
fleet_filesis a fleet table: it has the columnscompanyanddept, and a company reads and writes only its own rows.
Commands, hooks and prompts from the database (B14)
Three kinds of row go to the mod: commands/<name>.md (a slash command), hooks/<name>.json (a hook) and prompts/<name>.md (text for
Claude, like a CLAUDE.md file). The daemon writes NO file for them: not into a repository and not into ~/.claude. It writes
~/.config/kdmem/commands.json, hooks.json and prompts.json. The mod reads them and checks them again every 15 seconds. A change
so works in a session that is already open, with no restart.
Global and project layers. A row projects/<repo>/commands/<name>.md (also hooks/ and prompts/) belongs to ONE project. <repo> is
the repository name that the fleet page shows. The mod learns the project of its session from files-all.json. In a session of that project the
project entry REPLACES the global entry of the same name. In every other project the global entry works. A hook of the project
{"off": true} switches the global hook of that name off in that project. Until the project is known, only the global entries apply.
Hooks are data. A mod cannot load code from the database (on needs a function literal, and eval is blocked), so a hook row holds a rule:
| Field | Meaning |
|---|---|
event |
tool.call, prompt.submit or turn.complete. |
tool |
A tool name or a list (tool.call only). No tool means every tool. |
match |
A regular expression for the subject: the command (Bash), the path (Edit, Write, Read), the pattern (Grep, Glob), the prompt text, or the answer. |
deny |
tool.call refuses the call with this text. prompt.submit drops the prompt. |
note |
tool.call adds the text after the result. prompt.submit adds it as context. |
run |
An argv list, never a shell line. {subject} in an element becomes the subject. Exit code 2 denies with the message. Other output is a note. |
prompt |
turn.complete starts one follow-up turn with this text. That turn runs no hook, so there is no loop. |
A hook in a sub agent works too: the sub agent has the session id of its parent and an extra agentId (probe 2026-10-18). The hooks of
tool.call run for it. A turn.complete hook ignores the turn of a sub agent. The hooks of the mod run BEFORE the hooks of
settings.json. They cannot remove a hook that a repository defines in its own .claude/settings.json.
Prompts. The first prompt of a session carries the visible prompts as context. A prompt that changes (or appears, or goes away)
WHILE the session runs reaches the session as a message from the mod, once, within about 15 seconds. The text is not repeated after a
/compact: the session keeps the message in its summary only if the summary keeps it. A sub agent does not get the prompts (NOT DETERMINED).
Commands. A row commands/<name>.md becomes a slash command /<name>.
- The text. The body is the prompt.
$ARGUMENTStakes the text after the command; a body with no$ARGUMENTSgets it at the end. A frontmatter block may givedescription:andargument-hint:. Without a description the first line of the body is used. - A run. The mod queues the prompt and submits it as if the user typed it. A timer of 0.5 second submits it, because a direct call in the handler started no turn (probe 2026-10-18).
- A change or a removal. A changed body counts at once (within 15 seconds). Claude Code cannot unregister a command: a removed command stays in the list and answers that it was removed, until the session ends. A changed description shows after a restart of the session.
- The name. Letters, digits,
_and-, up to 64 characters. A name of a built-in command is refused by Claude Code and skipped. - Old files. A command file that an older daemon made is removed once (a file that the user edited stays).
- Skills are not changed. A skill is a prompt that Claude follows with its own files, so it stays a
SKILL.mdfile.
This folder is NOT copied into a project by setup.ts. It is the product, and it lives here
so that the kinedb repository stays free of it.
| Path | What it is |
|---|---|
daemon/ |
The publisher (agents-publish.ts), the device key and login (setup.ts, device.ts), the signed delivery of skills and code (assets-*.ts, release-sign.ts, code-update.ts), the company admin (company-*.ts). bun test kdmem/daemon/ runs its tests. |
mod/ |
The kdmem-usage mod. claude plugin test kdmem/mod runs its tests. |
.claude-plugin/marketplace.json |
The marketplace that offers the mod. |
skills/ |
The skills that the database delivers to every machine (/fleet-setup). Publish with bun daemon/assets-cli.ts <env-file> publish kdmem skills/fleet-setup. |
rows/ |
The first rows for the database: the prompt kdmem and the hook no-rm-root. Publish with bun daemon/assets-cli.ts <env-file> publish kdmem/rows prompts hooks. They go to the core table, so every company gets them. Remove one with ... delete <path>. |
site/ |
The fleet page and the admin page. sh deploy-site.sh deploys them. |
deploy-*.sh, provision-server.sh, Caddyfile |
The server side. |
q*.ts, q*.sh |
Probes and end-to-end checks from the design phase. |
DESIGN.md |
The design. |
The daemon and the skills of a machine come from the database, signed with an ed25519 release
key. They never come from a git pull. The private key stays on the owner machine
(~/.config/kdmem/release.key.pem).