No description
  • Go 72.5%
  • Nix 27.5%
Find a file
Arne Skaar Fismen b01f0d63fb image: fish shell + usage MOTD + friendlier default tooling
Make a fresh sandbox more welcoming:
- fish as the login shell (programs.fish.enable; the provisioner now creates
  trainees with -s fish, and the vestigial client account matches);
- a global MOTD printed once per login shell (the broker enters via su - <user>),
  guiding usage: the box name + http://<name>:<port> reachability, claude, the
  tooling, and how to leave/destroy;
- friendlier defaults: micro, bat, eza, tree, fzf, htop.

Also drops the deprecated pkgs.system alias in the image.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 13:31:13 +02:00
cmd/firsthouse module: own Incus image+profile, LoadCredential secrets, assert on enable 2026-05-31 13:14:36 +02:00
images image: fish shell + usage MOTD + friendlier default tooling 2026-05-31 13:31:13 +02:00
internal image: fish shell + usage MOTD + friendlier default tooling 2026-05-31 13:31:13 +02:00
nix module: own Incus image+profile, LoadCredential secrets, assert on enable 2026-05-31 13:14:36 +02:00
.gitignore firsthouse scaffold: tested identity/store/box core + flake 2026-05-30 22:37:59 +02:00
DESIGN.md firsthouse scaffold: tested identity/store/box core + flake 2026-05-30 22:37:59 +02:00
flake.lock Add tsnet+wish auth spine, sandbox image, and NixOS module 2026-05-31 00:15:11 +02:00
flake.nix flake/module: use stdenv.hostPlatform.system (drop deprecated pkgs.system) 2026-05-31 12:44:30 +02:00
go.mod Provision/enter/destroy: incus + tailnet adapters, broker, menu 2026-05-31 00:37:54 +02:00
go.sum Provision/enter/destroy: incus + tailnet adapters, broker, menu 2026-05-31 00:37:54 +02:00
README.md Provision/enter/destroy: incus + tailnet adapters, broker, menu 2026-05-31 00:37:54 +02:00

firsthouse

Self-service Incus sandbox portal, gated on Tailscale identity. Trainees ssh firsthouse; the portal learns who they are from their verified Tailscale identity (tsnet + LocalClient.WhoIs) and gives them a tiny TUI to provision / enter / destroy their one sandbox. Sandboxes are Incus containers that join the sandbox tailnet but run no SSH — the only shell is back through firsthouse, which bridges the session via incus exec.

This is the application half of Phase 6. The deployment (systemd unit, hardening, Incus socket access, sops-wired OAuth secret + tag:firsthouse key) lives in the consuming NixOS config (nixcfg, hosts/oink/), which pins this repo as a flake input. See DESIGN.md for the full picture.

Layout

cmd/firsthouse/      entrypoint — tsnet node + wish SSH server, WhoIs adapter
internal/identity/   verified Tailscale identity → owner key   (the security core)
internal/box/        sandbox name validation (charset + uniqueness backends)
internal/store/      persistent one-box-per-identity ownership map (JSON)
internal/portal/     resolve identity, dispatch subcommands (status/new/enter/destroy)
internal/provision/  the lifecycle recipe: mint → launch → user → join → record
internal/incus/      CLI-backed container manager (launch/exec/delete + shell cmd)
internal/tailnet/    tailnet-B control client (mint key / list / delete device)
internal/broker/     local-PTY ↔ SSH-session bridge for `incus exec -t`

The owner check ("the only thing keeping tenants apart") and the provisioning orchestration are unit-tested against fakes, in isolation from the SSH / Incus / Tailscale plumbing.

Interface

Non-interactive subcommands carried as the SSH command (identity is from the verified tailnet connection, not keys):

ssh firsthouse                # status: who you are + your sandbox (if any)
ssh firsthouse new [name]     # provision your one sandbox (default name from identity)
ssh -t firsthouse enter       # shell into it (brokered via incus exec; needs a tty)
ssh firsthouse destroy yes    # tear it down + free the name

Status

The connect path, provisioning/destroy orchestration, and name rules are implemented and unit-tested; the binary + sandbox image + NixOS module all build under Nix. The Incus/Tailscale calls compile and mirror the validated Phase-5 scripts, but the end-to-end provision/enter/destroy flow is verified live on the host (Incus daemon + a real tag:sandbox key), not in CI. An interactive TUI is a later UX nicety.

Develop

nix develop          # go_1_26 + gopls + gotools
go test ./...
nix build            # builds the firsthouse binary