marvin 50b215f759 Turnstone container-VM: podman image -> boco qcow2 boot
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.
2026-09-26 14:53:56 +02:00

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)
  • boco CLI on PATH and boco-init at ~/.local/share/boco/boco-init (build from the boco repo)
  • Kernel at ~/.local/share/boco/registry/vmlinuz (x86 bzImage with CONFIG_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
  • marvin user; sshd + turnstone-server started by start-turnstone.sh (server runs as marvin, 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-turnstone unless ADMIN_PASSWORD was 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_key must be set (currently mirrored from opencode's auth.json).
  • [auth] jwt_secret — required, generate once.
  • [database] path — SQLite on the data/ share.

Notes / trade-offs

  • boco boots with -snapshot: only the virtiofs shares persist. Agent-installed packages, git clones outside work/, and podman image storage inside the VM are discarded on shutdown.
  • boco hardcodes -smp 8; memory via VM_MEMORY=...M scripts/run.sh.
  • The old setup's Traefik is dropped — hostfwd replaces it.
S
Description
No description provided
Readme
115 KiB
0 Stars 0 Watchers 0 Forks
Languages
Python 49.6%
Shell 33.8%
Dockerfile 16.6%