Skip to content

Workshop Operator Guide

Daniel Ellison edited this page Sep 3, 2026 · 1 revision

This page is for the person who runs a Kai install: choosing which client surfaces to enable, giving people access to the Workshop, deciding how far to expose it on the network, and operating it day to day with the CLI and diagnostics. For what users see once they are in, start with Workshop Collaboration Basics.

Client modes

KAI_ENABLED_ADAPTERS decides which client surfaces the install serves. The configuration wizard asks under "-- Client mode --" and writes the value for you; re-running make config is the supported way to change modes later.

Wizard choice KAI_ENABLED_ADAPTERS Effect
hybrid (default) telegram,workshop Both surfaces
workshop-only workshop No Telegram application and no bot token; the wizard skips the token prompt
telegram-only telegram No Workshop client routes

An absent variable means both adapters, so an install upgraded from an older release keeps working without edits. An empty value or an unknown adapter name is a hard startup error. The HTTP server starts in every mode; the mode only gates which client surfaces it serves.

A fresh workshop-only install provisions its first admin automatically: the wizard creates one canonical human, the Kai agent, their direct channel, and a protected runtime profile, then prints the first enrollment token once. Copy it before the screen scrolls away.

Access lifecycle

Workshop humans are canonical records in the database, not entries in users.yaml (that file is Telegram-only now). The full lifecycle runs through the CLI:

  1. Provision. provision-human creates the person and their direct channel, with a role (admin or member) and optionally a handle. A freshly provisioned human has no transport access and no runtime.
  2. Assign a runtime. assign-runtime binds a protected runtime profile to the human's direct-channel agent. Without this step they can enroll but their agent cannot run.
  3. Issue an enrollment token. issue-enrollment takes --principal-id or --telegram-user-id (exactly one) and prints a short-lived token. Defaults: 10 minutes to redeem, 1 hour maximum. The server stores only a hash.
  4. The person enrolls. They open the Workshop URL, name their device, and redeem the token for a session credential: 30 days by default, 90 maximum, also stored hashed.
  5. Revoke when needed. revoke-device --device-id ends a device and all of its sessions; revoke-enrollment --grant-id cancels an unredeemed token.

Enrollment and redemption are rate limited, and both listeners share the same limiters, so the LAN listener gives an attacker no separate budget.

Enabling Telegram later for a Workshop-provisioned person links the Telegram identity to the same canonical human; they keep their channels and history.

Network exposure

The default posture is loopback only: the Workshop lives at http://127.0.0.1:8080/workshop/ and nothing else can reach it.

WORKSHOP_LAN_HOST opens a second listener for one private, non-loopback IPv4 address on the same port as the loopback listener (WEBHOOK_PORT, default 8080). Validation is strict: loopback, public, multicast, and IPv6 addresses are all rejected, and setting the variable while Workshop is disabled is a startup error. The LAN listener registers only the client API routes; webhook ingress, internal APIs, and Telegram stay loopback-only.

Both listeners speak plain HTTP. The wizard warns: "Workshop bearer sessions will use plain HTTP on this trusted LAN address. Use a TLS terminator for untrusted networks." Take that seriously; a bearer token on an untrusted network is a stolen session. Front the LAN listener with a TLS-terminating proxy for anything beyond a trusted home or office network.

The server caps long-lived event streams at 12 per person and 96 per process; beyond that it answers 429 with a Retry-After header. The caps mostly matter when one person leaves many tabs open.

CLI reference

Every command runs as python -m kai workshop <command>, and must run as the OS account that owns $KAI_DATA_DIR/kai.db.

client-access

Subcommand Purpose
list-humans List canonical humans and their direct channels
provision-human --provisioning-key K --display-name N --role admin|member [--handle H] [--workshop-id W] Create a canonical human without transport or runtime access
list-runtime-profiles List protected transport-neutral runtime profiles
assign-runtime --principal-id P --channel-id C --runtime-profile-id R Bind a runtime profile to a human's direct-channel agent
issue-enrollment (--principal-id P | --telegram-user-id T) [--channel-id C] Issue one short-lived enrollment token
revoke-device (--principal-id P | --telegram-user-id T) --device-id D Revoke one device and all of its sessions
revoke-enrollment (--principal-id P | --telegram-user-id T) --grant-id G Revoke one unredeemed enrollment grant

transcript

transcript export --channel-id C writes a channel's canonical conversation history as NDJSON to stdout.

integration-route

status, reconcile, and set --channel-id C inspect and repair where Kai delivers integration notifications (GitHub activity).

model-catalogue

status, list, refresh, refresh-all, upsert --model-id M --display-label L --capabilities-json J, and deactivate manage the durable model catalogue behind runtime model pickers. Discovery adapters only fetch metadata; upsert is how you add an operator-curated entry by hand.

Diagnostics

make install-status prints the Workshop status functions alongside the rest of the install report: bootstrap state, agent authority, handles, notifications, unread and lifecycle state, delivery authority, runtime sessions, memory authority, canonical message integrity, transcript authority, and legacy residue checks. The report is deliberate about privacy: it prints counts and health, never message text, so it is safe to paste into an issue.

The client bundle

The browser client is a React application in workshop-client/, but operators never need Node to run Kai: the built bundle is committed to the repository and Python serves those committed bytes directly. CI enforces the arrangement with npm run verify-generated, which rebuilds the bundle and fails if the committed output is stale.

Two development lanes exist:

  • make client-check runs the client typecheck, tests, and the verify-generated comparison; this is the client half of CI.
  • make workshop-dev starts a Vite dev server against a running Kai install for live preview. It is foreground-only by design: review, then Ctrl-C. The dev origin is separate from the installed one, so enroll that origin once with its own token.

See also

Clone this wiki locally