Multi-machine dev setup (MacOS ↔ kloud)¶
The repo is designed so you can switch between MacOS (laptop) and kloud
(Docker host) without losing state. The same commands work on either side;
each machine owns a role and the seam between them is just git + SSH.
Roles¶
| Machine | Primary role | Why |
|---|---|---|
| MacOS | Harness + TUI iteration. The day-to-day dev loop. | Best TUI rendering, all local tools, fastest iteration on src/kin/harness/ + src/kin/tui/. |
| kloud | Outpost + container + live-verifies. The Docker-host seat. | task outpost:build/task outpost:up and task live-drive run against the real Docker host + the real tailnet vLLM — coverage the laptop can't match. |
Either machine can do the other's job — task run works fine over SSH + tmux,
task verify is fast on both — but each is best at its lane.
Bootstrap a fresh machine¶
From inside the checkout:
setup-dev.sh is the dev-machine bootstrap — it assumes you already
have a checkout and want the tmux/.bashrc dev-loop plumbing. It's a
different script from the public one-liner at
get.kinra.ai (scripts/install.sh), which
clones the repo itself and skips the tmux step — see
Install for that consumer-device path.
Does four things, none of which need sudo:
uv sync— creates.venv, installs all deps fromuv.lock.uv tool install --editable .— installs thekincommand on$PATH.- Copies
examples/settings.toml.shared-template.toml→~/.kin/settings.toml(only if not already present). Secrets stay out — enter viakin→/providers. - Appends a one-liner to
~/.bashrcthat auto-attaches (or creates) a tmux session namedkinon every SSH login.
The verify gate (details):
kin doctor is a complementary, lighter-weight check for a freshly
bootstrapped machine — it verifies the install itself (uv/kin/git on
PATH), that a provider actually resolves (mirroring make_backend's
config chain), and that the resolved endpoint is reachable, without
needing a checkout or task at all. See Install.
Re-running the script on a machine that's already set up is a no-op — each step checks for its precondition.
The build-worktree split (deploy safety)¶
The kloud checkout has a second worktree at ~/kin-textual-build/ — a
separate git worktree of the same repo, checked out at the deploy SHA.
The deploy task operates there; your dev working tree at ~/kin-textual/
never gets reset --hard. The two worktrees share git history (one .git/)
but have independent file state.
~/kin-textual/ ← dev tree, on branch dev, your WIP commits live here
~/kin-textual-build/ ← detached HEAD at the deploy SHA, gets reset each deploy
The deploy task (task outpost:deploy-dev) creates the build worktree on
first run, then fetch + reset --hard origin/<branch>es it each subsequent
run. Your dev tree is untouched — the silent-wipe footgun is gone.
Container secrets (oauth-client-id, oauth-client-secret, oauth-cookie-secret,
kin-settings) live in the dev tree under container/secrets/ —
gitignored fail-closed, mode 600, only you drop them. The deploy script
symlinks container/secrets/ from the dev tree into the build worktree
before building, so docker compose resolves the same paths regardless of
which worktree owns the compose file.
The publish profile (get.kinra.ai + docs.kinra.ai)¶
This section documents Blake's own reference deployment (kloud +
kloud-vps) as a worked example — substitute your own host/SSH-alias/DNS
if you're standing up a similar pair of public docroots elsewhere. For the
consumer-facing side of this (what a visitor to get.kinra.ai actually
runs), see Install; for the Outpost
container itself, see Outpost.
The same deploy also builds the two public static docroots in the build
worktree, before compose up (any build failure aborts the deploy before
anything is published):
uv run --group docs mkdocs build→site/(served as docs.kinra.ai).uv build --wheel+ assemblecontainer/installer-www/— theextras/install-page/page,scripts/install.sh+scripts/outpost-install.sh, the wheel (real PEP 427 filename foruvplus a stablekin-latest.whlcurl alias), a static PEP 503simple/kin/index, and a.sha256per artifact (served as get.kinra.ai).
compose up then runs with --profile publish, which adds two
nginx:alpine services from container/compose.yaml: get on
${OUTPOST_BIND_IP}:7018 and docs on :7019 (both read-only mounts of
the docroots above). The kloud-vps Traefik routes get.kinra.ai /
docs.kinra.ai to those ports over WireGuard (routers kin-get /
kin-docs in ~/kloud/traefik/dynamic/routes.yml, mirroring the
kin-outpost block). A stranger's outpost-install.sh runs plain
compose up with no profile, so those services never start on a
non-kloud host. All three docroot dirs (site/, dist/,
container/installer-www/) are gitignored and survive between deploys
(the reset --hard leaves untracked dirs alone) — each deploy just
rebuilds them.
Per-host overrides¶
The shared template is the baseline — the non-secret defaults both machines start from. Per-host overrides happen three ways, in precedence order:
- CLI flag (
kin --base-url ... --model ...) — one-off, for a single launch. - Env var (
KIN_BASE_URL=... KIN_MODEL=...) — set in shell for a session. ~/.kin/settings.toml— durable per-machine config.
So MacOS and kloud can both start from the same template, then diverge on
a single KIN_* env var — e.g., MacOS uses the tailnet's MagicDNS
(kin-one:8000), kloud uses the tailnet IP directly, or a different model
alias on one side.
The Taskfile verbs¶
All under the existing OUTPOST_SSH (default kloud) + OUTPOST_DIR
(default ~/kin-textual) vars — so they pick up the same overrides as
the deploy task:
| Task | What it does |
|---|---|
task kloud:setup |
Run scripts/setup-dev.sh on kloud over SSH (bootstrap a fresh box). |
task kloud:dev |
SSH into kloud, attach (or create) the kin tmux session. |
task kloud:verify |
Run task verify on kloud — headless suites against the real Docker host. |
task kloud:status |
One-shot summary: git state, active tmux sessions, kin tool install. |
What stays out of this repo¶
This page is the boundary. Per-host concerns don't belong here:
| Concern | Where it goes |
|---|---|
~/.ssh/config (aliases, multiplexing, keys) |
Per-host shell/dotfiles |
~/.zshrc, aliases, completions |
Per-host shell/dotfiles |
~/.gitconfig includeIf host |
Per-host git config |
| mosh, Tailscale SSH, devcontainers | Per-host preference |
| VSCode / Neovim configs | Per-host IDE |
BRAVE_API_KEY, provider secrets |
~/.kin/settings.toml [provider_keys] OR env vars |
If you want any of those synced across machines, they go in a separate
dotfiles repo — keep the dev-workflow concerns here, the personal-config
concerns there.
Flipping between machines¶
A typical day:
# Morning on MacOS — harness work
task run
task verify
git push origin dev
# Afternoon on kloud — Outpost-specific work
task kloud:dev # ssh + tmux auto-attach
cd ~/kin-textual # dev tree, safe from deploys
task outpost:build # real Docker host image build
task outpost:up # smoke-test the image locally
git push origin dev
# Back to MacOS — pick up the kloud commits
git pull --ff-only
task run
The repo state stays identical because both machines are symmetric peers
of origin/dev. Whoever makes the commit pushes; whoever pulls next
gets it.