boma/docs/superpowers/plans/2026-06-11-ubongo-build.md
sjat 7b190e4313 Add ubongo physical-build plan (2026-06-11 session)
Captures the interactive build decisions (no-encryption + accepted risk,
simple partition, dedicated claude identity, LAN-only access, pinned
versions) and the A-F + H task breakdown. Sequel to the 2026-06-05
docs-only ADR-015 plan.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 10:01:41 +02:00

6.8 KiB

Ubongo Physical Build — Implementation Plan

For agentic workers: Execute task-by-task. This is the physical bring-up of ubongo. The 2026-06-05 plan (2026-06-05-ubongo-control-host.md) was documentation-only (it authored ADR-015); this is its sequel — taking the actual box from bare Debian 13 to a working control / AI-worker node.

Goal: Bring the Lenovo ThinkCentre M70q from a fresh Debian 13 install to a working control node: toolchain, dedicated claude identity, repo + Claude Code, vault access, inventory wiring, keys-only SSH, and reconciliation of the docs to "built."

Spec / decisions of record: ADR-015 + docs/superpowers/specs/2026-06-05-ubongo-control-host-design.md, plus the interactive build decisions captured below (2026-06-11 session).


Decisions made this session (2026-06-11)

  • Hardware: Lenovo ThinkCentre M70q Tiny · i3-10100T (4c/8t) · 16 GB · 256 GB SanDisk X600 SATA SSD (TCG Opal-capable; Opal unused, see encryption).
  • BIOS: auto-power-on after loss; Wake-on-LAN on; ErP/deep-S5 off; supervisor password set; external/USB + PXE boot disabled; Secure Boot on; TPM (PTT) on; VT-x/VT-d on; Better-Thermal cooling.
  • Disk encryption: NONE. Accepted risk — compensated by physical security + BIOS supervisor password + disabled external boot. Recorded in accepted-risks.md (Task H1).
  • Partitioning: simple single ext4 root (/dev/sda2, 221 G) + 12 G swap, no LVM. Revisit via reinstall onto LVM/bigger drive only if the layout bites.
  • Identity: dedicated claude user — for attribution + revocation, not containment. In the docker group (Molecule); no local sudo (boma deploys run over SSH as ansible; the agent needs Docker, not root). Reached via sudo -iu claude from sjat. Own ed25519 key for Forgejo. ADR-021 leaves this identity open — note it.
  • Access: LAN SSH only for now — the NetBird mesh (ADR-016) is deferred (askari + service machinery unbuilt). Keys-only enforced after bootstrap.
  • Address: 10.20.10.151/24 on eno1. Make stable via an OPNsense DHCP reservation.

Pinned versions (match fisi): docker 29.5.2 · rbw 1.15.0 · node 20.19.2 · claude 2.1.173. Terraform is absent on fisi (TF un-init'd) — install deferred.


Pre-flight

  • Temp passwordless sudo for sjat during the build (/etc/sudoers.d/99-boma-build); removed in Task F2. Without it, non-interactive SSH sudo hangs.
  • rbw unlock on fisi before any commit (pre-commit decrypts vault.yml).
  • Commit style: one commit per logical unit; imperative subject ≤72 chars; trailer Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>.
  • Drive the live box (ubongo) directly over SSH; do repo/doc tasks (H) as clean commits.

Stage A — Toolchain (on ubongo, via sjat sudo)

  • A1. apt base: git make build-essential python3-venv python3-pip curl ca-certificates gnupg jq (+ apt update).
  • A2. Docker Engine from Docker's official apt repo (Debian 13/trixie); enable + start; confirm docker --version ≈ 29.5.2.
  • A3. rbw 1.15.0 — try apt install rbw; if the version doesn't match, install the pinned release binary to /usr/local/bin (match fisi).
  • A4. Node 20.19.2 (nodesource or distro) — only if Claude Code needs it; the native installer bundles its runtime, so Node may be optional.
  • A5. Claude Code via the native installer (matches fisi's ~/.local/share/claude/versions/), installed under the claude user in Stage C.
  • Defer Terraform (absent on fisi).

Stage B — Identity (claude user)

  • B1. useradd -m -s /bin/bash claude; lock the password (passwd -l claude) — reached only via sudo -iu claude from sjat or its own key.
  • B2. Add claude to the docker group.
  • B3. No sudo for claude (explicit decision). Confirm sudo -iu claude works.

Stage C — Repo + Claude Code (as claude)

  • C1. Generate claude's ed25519 key; [USER] register the public key in Forgejo (Settings → SSH keys).
  • C2. Clone ssh://git@forgejo.nyumbani.baobab.band:7577/sjat/boma.git into /home/claude/Projects/boma.
  • C3. make setup (venv + requirements.txt); make collections.
  • C4. Install Claude Code (native installer) for claude; set up plugins/MCP/ settings per docs/runbooks/claude-code-setup.md. Set git user.name/user.email.

Stage D — Vault (rbw)

  • D1. rbw config set base_url https://vaultwarden.baobab.band; set email.
  • D2. [USER] rbw login (master password) on ubongo; then rbw sync, rbw unlock; verify rbw get boma-ansible-vault returns the vault password.
  • D3. Offline-cache verification (ADR-015 open item, security-relevant): confirm rbw decrypts its local cache with Vaultwarden unreachable. Stamp the result into ADR-015 / rotate-secrets.md (replaces the TO VERIFY note).

Stage E — Inventory + base (partial)

  • E1. Add ubongo to inventories/production/hosts.yml under control (manual exception; note tf-inventory will overwrite — re-add after).
  • E2. Set base__firewall_control_addr to 10.20.10.151 in the appropriate group_vars (the dormant ssh-from-control knob, ADR-020/021).
  • E3. make check PLAYBOOK=site against control; apply the built firewall concern only (SSH-hardening/fail2ban/auditd concerns are unbuilt — note the gap).

Stage F — Hardening / address

  • F1. Disable SSH password auth (keys-only) via /etc/ssh/sshd_config.d/; PermitRootLogin no; reload sshd (we're on a key, so safe).
  • F2. Remove the temp NOPASSWD drop-in (/etc/sudoers.d/99-boma-build).
  • F3. [USER] OPNsense DHCP reservation for 10.20.10.151.

Stage H — Docs reconciliation (repo commits)

  • H1. accepted-risks.md: add the plaintext-disk accepted risk (compensations: physical security, BIOS supervisor password, no external boot).
  • H2. docs/hardware/reference.md: fill ubongo's real specs (M70q, i3-10100T, 16 GB, 256 GB SanDisk X600) into the TBD skeleton; node-capacity row already present.
  • H3. STATUS.md: move ubongo from "Designed but not built" toward built (note what's live vs. still pending — mesh, full base).
  • H4. Note the dedicated-claude identity decision (short amendment to ADR-021 or ADR-015) and the LAN address.

Out of scope this session

  • Mesh VPN (NetBird) — needs askari + service roles (ADR-016). SSH stays LAN-only.
  • Full base hardening — SSH/fail2ban/auditd concerns not built (only firewall).
  • Recovery wiring (G) — TF-state backup to mamba, rbw mirror — no TF state yet (TF un-init'd). mamba as break-glass clone tracked separately.