The console routes to nodes (rendezvous), so it is the single public
port: server -> :8081 (no host forward), console -> :8080, 8090
forward dropped. Verified: node unreachable from host, routed
workstream creation lands on turnstone-vm-main at 127.0.0.1:8081,
route/command proxy works, cluster view tracks the workstream.
Default node_id is {hostname}_{random-per-boot}, so every boot
inserted a new services row and the console probed stale ones (409
node_id mismatch -> 'not found' node pages). Pin TURNSTONE_NODE_ID
(one upserted row) and TURNSTONE_ADVERTISE_URL to loopback — the
guest's slirp IPv6 lands in the SSRF guard's never-allowed lane.
Also drop the futile /etc/hostname baking (podman regenerates it at
boco build).
Console (cluster dashboard + admin panel) starts as a background
daemon under marvin on :8090, sharing the same config.toml and SQLite
DB (WAL multi-writer). Forwarded via run.sh and exposed through
tailcat; same login as the server.
skills/ carries the SKILL.md files; scripts/import_skills.py runs
inside the VM via turnstone's own storage layer (SKILL.md parser +
risk scanner) so rows match what the console admin API would write.
Server must be restarted after import (stale WAL snapshot).
Server-only deployment has no console admin API, so the persona is
upserted directly into the SQLite DB via scripts/persona.py with the
prompt text versioned in the repo (VM persistence rules, Gitea/GitHub
remotes, rootless podman). Built-in personas are seeded by migration
063; this sets marvin as the interactive default.
tailcat (Tailscale data plane, no control plane) forwards the VM's
web UI and SSH ports to another machine via a stable saved-key
address; outbound-only, so it works through the nested slirp NAT.
Arch image with turnstone (venv), dev toolkit, rootless podman; boots
via boco (virtiofs shares, direct UID mapping for nested virtiofs).
sshd ops channel + turnstone-server as guest marvin; state (SQLite db,
config, work) on host shares; LLM via the TensorX OpenAI-compatible
gateway (https://api.tensorx.ai/v1), reachable from the guest through
both slirp layers with no relay.