KDB knowledge

Install packages and the Claude skill

The Windows and Linux install packages, the Docker image, and the skill file that teaches an AI assistant in another project how to use KDB.

KDB ships as one program and three folders of plain files next to it: the web console, the help pages and the Claude skill. There is nothing else to install - no runtime, no libraries, no service to configure first. There are three ways to get it onto a machine.

WayYou getBest for
The Windows packagekdb-<version>-windows-x64.zipa desktop or a Windows server
The Linux packagekdb-<version>-linux-x64.tar.gz, built static so it runs on any distributiona VPS or a Linux desktop
The Docker imagea small Alpine image with the program and its foldersa server you would rather not install anything on

An unpacked package looks like this:

kdb-<version>-<os>-x64/
  kdb.exe (Windows) or kdb (Linux)   the program - nothing else is inside it
  web/                               the web console
  help/                              the help pages (missing when the package was made without them)
  skill/skill.md                     the Claude skill
  install.ps1 or install.sh, kdb.conf.example, README.md

Windows

Unpack the zip and run the installer from the folder it made:

powershell -ExecutionPolicy Bypass -File .\install.ps1

It copies kdb.exe and its web, help and skill folders into %LOCALAPPDATA%\Programs\kdb, adds that folder to your PATH (no administrator rights needed) and writes the Claude skill. Add -Token my-secret to save your token beside the skill, in a file only you can read.

OptionMeaningDefault
-Prefix <folder>where the program goes%LOCALAPPDATA%\Programs\kdb
-Token <secret>the token, saved in the file token beside the skill$env:KDB_TOKEN
-Data <folder>the data folder it suggests%LOCALAPPDATA%\kdb\data
-NoPathdo not touch PATH-
-NoSkilldo not write the skill-

Uninstalling is deleting that folder and taking it off your PATH. Nothing is registered with Windows.

Linux

Unpack the tar.gz and run the installer from the folder it made:

./install.sh                                              # just you: ~/.local/bin, folders in ~/.local/share/kdb
sudo ./install.sh --system                                # everyone: /usr/local/bin, folders in /usr/local/share/kdb
sudo ./install.sh --system --service --token my-secret    # ...and start it at boot
OptionMeaningDefault
--systeminstall into /usr/local instead of ~/.local-
--prefix <folder>install somewhere else~/.local
--servicewrite a systemd unit and start it (needs root and --token)-
--token <secret>the token for the service and the skill$KDB_TOKEN
--data <folder>the data folder/var/lib/kdb or ~/.local/share/kdb

With --service the server runs as its own kdb user, is restarted if it stops, and can write to its data folder and nowhere else. The unit file holds the token, so it is readable only by root.

systemctl status kdb
journalctl -u kdb -f

Where kdb finds the console, the help and the skill

The web console, the help pages and the Claude skill are files, so a new console or help needs the files copied, not a new program. kdb looks for each folder in this order and uses the first one that has its files in it (index.html, or skill.md):

OrderWeb consoleHelp pagesClaude skill
1. kdb.conf"web_dir""help_dir""skill_dir"
2. environmentKDB_WEB_DIRKDB_HELP_DIRKDB_SKILL_DIR
3. next to the programweb/help/skill/
4. share/kdb beside the program folder../share/kdb/web../share/kdb/help../share/kdb/skill
5. a source checkout (build/bin/kdb)web/consoledocs/help/site.claude/skills/kdb

A kdb.conf that names all three:

{
  "data_dir": "E:/kdb-data",
  "web_dir": "E:/kdb/web",
  "help_dir": "E:/kdb/help",
  "skill_dir": "E:/kdb/skill"
}

Docker

docker run -d --name kdb -p 9900:9900 -p 8832:8832 \
  -e KDB_TOKEN=<token> -v kdb-data:/data kdb

The data lives in the named volume kdb-data, so removing and recreating the container does not lose it. The server refuses to start without KDB_TOKEN: the image listens on all addresses, so a published port can reach it, and KDB does not do that without a secret.

The image holds /usr/local/bin/kdb and its folders in /usr/local/share/kdb/web, help and skill (named by KDB_WEB_DIR, KDB_HELP_DIR and KDB_SKILL_DIR). The image build only compiles kdb and copies those folders from the source tree, so run the help build first when the help pages should be in it.

The settings of the image, all given with -e NAME=value:

VariableMeaningDefault in the image
KDB_TOKENthe secret every request sends as Authorization: Bearer <token>. The start script passes it as --token; the program itself reads no KDB_TOKEN.none: required
KDB_MEMORY--memory, in MB (passed as the flag)256
KDB_MEMORY_LIMITone limit for everything the server holds (memory limit), like 3GB85% of the container's memory limit, else of Docker's memory
KDB_DATA_DIRthe data folder/data
KDB_CONFIGthe settings file (passed as --config)/data/kdb.conf
KDB_DRAIN_TIMEOUThow long running requests may finish on a stop5s
KDB_READER_YIELDon: reads do not wait for a long trigger or transaction (--reader-yield). off: they wait.on
KDB_SNAPSHOT_READSon: a read of a table being written reads a snapshot of the committed rows and does not wait (--snapshot-reads)off
KDB_PER_TABLE_READSoff: every read waits for every writer, as before (--per-table-reads)on
KDB_READ_ONLY_CALLSoff: every CALL is a transaction, also one that only reads (--read-only-calls)on
KDB_CORE_DUMPSon: a crash leaves a core dump in /data/_logs/cores (kdb runs from that folder). At the next start the dump is renamed with its time. off: no dumps. The unstripped program for reading one is /usr/local/lib/kdb/kdb.debug (gdb kdb.debug core.<time>).on
KDB_CORE_KEEPhow many old core dumps are kept; a dump is about as big as the server's memory2
every other KDB_* variable of kdb serveas listed in every option, for example KDB_HTTP_THREADS, KDB_TIMER_WORKERS, KDB_CACHE_MB, KDB_TIMEZONEthe program's defaults
GLIBC_TUNABLESfor example glibc.malloc.arena_max=8: how many memory arenas the C library keeps (fewer than the query threads makes concurrent queries wait for each other's allocations; 2 arenas halved the request rate of 8 clients) (see allocator_arena_max in /api/stats). With trim_threshold set, KDB puts glibc's mmap line at 32 MB, where glibc would move it itself (allocator_mmap_threshold)KDB chooses

A fuller run line, as a long-running server uses it:

docker run -d --name kdb --restart unless-stopped --stop-timeout 60 \
  -p 9900:9900 -p 8832:8832 \
  -e KDB_TOKEN=<token> -e KDB_MEMORY=1536 -e KDB_READER_YIELD=on \
  -e GLIBC_TUNABLES=glibc.malloc.arena_max=8:glibc.malloc.trim_threshold=67108864 \
  -v kdb-data:/data kdb

On Windows, -v "D:\kdb_data:/data" puts the data in a Windows folder instead of a named volume.

The data folder and kdb.conf in the container

Stopping and restarting the container

CommandWhat KDB getsNext start says
docker stop kdb, container run with --stop-timeout 60SIGTERM, then a kill after 60 slast shutdown: clean
docker stop -t 60 kdbSIGTERM, then a kill after 60 slast shutdown: clean
docker stop kdb, container run without --stop-timeoutSIGTERM, then a kill after Docker's default wait - seen as short as 1 sclean only if nothing was running
docker rm -f kdb, docker kill kdba kill at oncelast shutdown: not clean (crash or kill)

An image cannot choose how long Docker waits, so give the container --stop-timeout when you create it. Without it, a stop while a request runs, or while a big database closes, ends in a kill.

On SIGTERM the server:

  1. keeps the port open and lets requests that are already running finish, up to KDB_DRAIN_TIMEOUT (5 s in the image);
  2. answers every new request with 503 and Retry-After: 2;
  3. cancels what still runs (a writer that waits for a busy table stops waiting too), closes every database side by side and writes the clean-shutdown mark.

A kill is safe too - every confirmed write is in the log - but the next start has to replay the log. So stop the container before you remove it:

docker stop -t 60 kdb && docker rm kdb
docker run -d --name kdb --restart unless-stopped --stop-timeout 60 \
  -e KDB_DRAIN_TIMEOUT=30s -e KDB_TOKEN=<token> \
  -p 9900:9900 -p 8832:8832 -v kdb-data:/data kdb

What a client should do

While the container is stopped and started again there is a short gap, about 2 s on a laptop.

The client seesMeaningDo
HTTP 503 with Retry-Afterthe server is stopping; the request was not runwait that many seconds, send it again
connection refusedthe server is not listening yet (or any more)wait a second, try again
the connection closed before a replythe request was cut offsend it again

The Claude skill

Claude Code, or any other AI assistant, working in some other project has no idea KDB exists. It will not guess the port, the token, the shape of /exec or where the help is. A skill file tells it, once, and every project on the machine benefits.

The installers write it for you. To do it by hand, or again after the address or token changed:

Syntax:

kdb skill
kdb skill install [--dir <folder>] [--url <address>] [--token <secret>] [--home <checkout>]
kdb skill install                                  # writes ~/.claude/skills/kdb/SKILL.md
kdb skill install --token <token> --home /srv/kdb-source
kdb skill                                          # print it instead, to pipe somewhere
OptionMeaningDefault
--dir <folder>where to write SKILL.md~/.claude/skills/kdb
--url <address>the address the assistant should use$KDB_URL, else http://localhost:9900
--token <secret>the token, saved in the file token beside SKILL.md (see below)$KDB_TOKEN
--home <folder>a source checkout, so the assistant can read docs/help/topics/-

Command line: Install into a folder of your choice, with another address

kdb skill install --dir my-skills/kdb --url http://kdb.example.lan:9900

Output:

wrote my-skills/kdb/SKILL.md
Claude Code picks it up in any project the next time it starts.

The token is never written into SKILL.md. An assistant may quote that file, or you may copy it somewhere, so it only says where the token is. The token goes into its own file, token, in the same folder:

SKILL.mdtoken
holdsthe skill, the address, the paths, where the token file isthe token, on one line
who can read itlike any of your filesonly you: mode 0600 on Linux and macOS, an owner-only ACL on Windows

Run without a token, kdb skill install leaves an existing token file alone and still points to it. A SKILL.md written by an older kdb held the token in clear: run kdb skill install again to replace it.

--dir is refused when it is, or is inside, a folder the server hands out without a login: the skill folder (GET /skill.md), the console folder or the help folder, wherever kdb looks for them (the folders). A token there could be fetched by anyone who reaches the server.

The text is the file skill/skill.md next to the program, found the same way as the other folders, so this works with no source folder and no network.

A running server also hands the same text out, without a token, at GET /skill.md. That is how a machine that can only reach the server over the network gets it:

mkdir -p ~/.claude/skills/kdb
curl -s http://your-server:9900/skill.md -o ~/.claude/skills/kdb/SKILL.md

From Docker, ask the program inside the container:

docker exec kdb kdb skill > ~/.claude/skills/kdb/SKILL.md

Claude Code reads the folder when it starts, so open a new terminal afterwards.

Building the packages yourself

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --target kdb
node tools/help/build_help.mjs --kdb build/bin/kdb.exe   # the help pages -> docs/help/site (runs every example)
cpack --config build/CPackConfig.cmake          # -> dist/kdb-<version>-<os>-<arch>

The Linux package is built inside the Docker image, which also links it static:

docker build --target package -o dist -f docker/Dockerfile .

A package holds the program, its web, help and skill folders, the installer and kdb.conf.example. No source, no tests, no build folder. The help build is its own step: without it the package has no help folder, and /help says how to add one.