Running the host
One computer that stays on, its boot order, and the settings it takes.
The host is one computer that stays on. A Linux server, a Mac that does not sleep, a small box in a cupboard. It needs an outbound network and nothing else from the network.
The boot order
host/entrypoint.sh brings the bundle up, and the order is the design:
The relay, before anything else, even before Postgres answers. It talks only to the central plane and the messenger, so the chat screen keeps answering while everything below is down or restarting. The entrypoint restarts it five seconds after any exit.
Wait for Postgres at DATABASE_URL — the agent's own store.
blueclaw-llmd on /run/internkim/llmd/llmd.sock. capabilityd counts it
in its own health, so it still has to be up; it is no longer the model path,
because the agent reaches the model itself through bluecollar's
OpenAI-compatible client.
internkim-capabilityd on /run/internkim/capability.sock. Optional in
the sense that not_configured is a passing state, but the calendar, task,
mail and site tools disappear without it.
The agent: blueclaw -runtime /etc/blueclaw/runtime.json -policy /etc/blueclaw/policy.json, answering on 127.0.0.1:8080.
runtime.template.json is rendered into that config from DATABASE_URL and
MESSENGER_PLATFORM.
internkim-maild on 127.0.0.1:18092, then the connector (chatd)
on 127.0.0.1:18090, which signs into the messenger as the bot and hands
arriving messages to the agent.
What the box does without: it boots to status: ok with no Firecracker, no
setuid helper, no Mattermost of its own and no cloudflared. The device stack
required all four.
Settings
The entrypoint requires six values and passes the rest through:
SUPABASE_URL=https://<project>.supabase.co
SUPABASE_PUBLISHABLE_KEY=<the project's publishable key>
INTERNKIM_APP_URL=https://<company>.intern.kim
CHATD_BOT_USER_NAME=<the bot's display name>
MESSENGER_PLATFORM=mattermost # or buzz
DATABASE_URL=postgres://…The agent key is never one of these. It is a file at /secrets/agent-key,
mode 0600, read by path, so it stays out of the process environment where
anything reading /proc could find it. Rotating the file is the whole
rotation procedure. The relay takes the path as AGENT_API_KEY_PATH.
Then the messenger the company runs, one of:
CHATD_MATTERMOST_BASE_URL=… CHATD_MATTERMOST_BOT_TOKEN=…
CHATD_BUZZ_RELAY_URL=wss://… CHATD_BUZZ_PRIVATE_KEY=<64 hex>And the gateway the relay answers through:
GATEWAY_URL=… GATEWAY_SERVER_KEY=…With the two gateway settings absent the relay starts, logs nothing about it, and never answers the app. An empty messenger screen with a green process table is the symptom.
A company that chats on Buzz also runs the Buzz stack itself; host/buzz/
carries a compose file that brings up Postgres, Redis, MinIO and the relay
binary from .dependency/buzz-relay/ on this same machine.
How big an answer may be
The transport refuses an oversize frame silently, leaving the caller waiting
forever, so the relay measures every answer first and replies 413 with the
byte count, which does reach them. The default ceiling is 3,000,000 bytes
(decimal — a Supabase figure, so 3 * 1024 * 1024 lands 145 KB over), and
ANSWER_BYTE_CEILING overrides it. Anything that is not a positive number
stops the boot rather than silently refusing every answer. The
profile-picture limit is derived from the ceiling, since base64 inflates by a
third. host/relay/measure-broadcast-ceiling.ts settles the real ceiling
against a project by binary search.
Keeping it running
Hand the bundle to whatever supervises processes on that computer, so it comes back after a crash or a reboot.
| systemd | host/relay/internkim-relay.service — settings in /etc/internkim/relay.env, Restart=always |
| launchd | host/relay/launchagent.plist.template — KeepAlive |
| the bundle | entrypoint.sh starts and restarts the relay itself |
A relay that dies without coming back leaves the chat screen empty, which is the failure worth guarding against.
make build-relay compiles the relay into a single internkim-relay
executable, so the box needs no Bun and no node_modules; RELAY_TARGET
cross-builds it (bun-linux-arm64, bun-linux-x64, bun-darwin-arm64).
make build-maild does the same for the mail answerer.
Checking it is really outbound-only
ss -ltnp | grep -v '127.0.0.1\|::1' # prints nothing
systemctl list-units | grep -E 'cloudflared|stunnel' # prints nothingAdministering the box is your own arrangement: a shell on the same network,
or a ProxyCommand in your ~/.ssh/config from anywhere else. A Cloudflare
Tunnel is a convenient option there and the one we develop against, and it is
only that.