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.
3.9 KiB
marvin-turnstone
Local turnstone deployment as a container-VM: a podman image is turned
into a bootable VM with boco (custom
init + ext4 qcow2 + QEMU direct kernel boot). Replaces the older
opencode-in-a-container setup (~/marvin).
host podman build ──> boco build qcow2 ──> boco boot
│ QEMU/KVM (virtio, user-net)
┌─────────────────────┴───────────────────┐
│ VM (Arch) │
│ /boco/init → start-turnstone.sh (root) │
│ ├─ sshd (ops channel) │
│ └─ setpriv marvin │
│ └─ turnstone-server :8080 PID 1 │
└─────────────────────────────────────────┘
host :8080 ──hostfwd──> web UI host :2222 ──> guest sshd :22
guest ──HTTPS (double slirp egress)──> api.tensorx.ai (LLM)
Prerequisites
Host (Arch):
podman,qemu-base,virtiofsd,croc(only if fetching the kernel)bocoCLI on PATH andboco-initat~/.local/share/boco/boco-init(build from the boco repo)- Kernel at
~/.local/share/boco/registry/vmlinuz(x86 bzImage withCONFIG_VIRTIO_FS=y; transfer via croc)
VM image:
- Arch base with python + turnstone (venv at
/opt/turnstone), git, helix, podman (rootless, subuid range), openssh, build toolchain marvinuser; sshd + turnstone-server started bystart-turnstone.sh(server runs asmarvin, PID 1)
Build
scripts/build.sh
Creates configs/ssh/id_ed25519 (ops keypair, once), builds the podman
image, then packs it as boco qcow2 into the boco registry.
Run
scripts/run.sh
- Web UI: http://localhost:8080 (login
marvin/marvin-turnstoneunlessADMIN_PASSWORDwas overridden — change it) - Ops SSH:
ssh -i configs/ssh/id_ed25519 -p 2222 marvin@localhost - QEMU runs foreground; quit with
ctrl-a x
LLM backend
[models.tensorx] in configs/turnstone/config.toml points at the
TensorX OpenAI-compatible gateway (https://api.tensorx.ai/v1, same
key this machine's opencode uses — see
~/.local/share/opencode/auth.json). Guest egress to HTTPS endpoints
works through both slirp layers; no relay needed.
Self-hosted provider later: add a [models.*] entry with any
OpenAI-compatible endpoint reachable from the guest (see
config.toml.example).
Shares (virtiofs, survives reboots)
| Host path | Guest path | Purpose |
|---|---|---|
configs/turnstone/ |
/home/marvin/.config/turnstone |
turnstone config.toml |
data/ |
/var/lib/turnstone |
SQLite database |
work/ |
/home/marvin/work |
agent workspace |
Guest-written files appear on the host owned by subuid-range UIDs
(virtiofsd runs under podman unshare) — cosmetic, and stable across
boots.
Configuration
Edit configs/turnstone/config.toml (gitignored; see
config.toml.example):
[models.tensorx]— TensorX gateway;api_keymust be set (currently mirrored from opencode'sauth.json).[auth] jwt_secret— required, generate once.[database] path— SQLite on thedata/share.
Notes / trade-offs
- boco boots with
-snapshot: only the virtiofs shares persist. Agent-installed packages, git clones outsidework/, and podman image storage inside the VM are discarded on shutdown. - boco hardcodes
-smp 8; memory viaVM_MEMORY=...M scripts/run.sh. - The old setup's Traefik is dropped — hostfwd replaces it.