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>
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
claudeuser — for attribution + revocation, not containment. In thedockergroup (Molecule); no local sudo (boma deploys run over SSH asansible; the agent needs Docker, not root). Reached viasudo -iu claudefromsjat. Owned25519key 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/24oneno1. 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
sjatduring the build (/etc/sudoers.d/99-boma-build); removed in Task F2. Without it, non-interactive SSHsudohangs. rbw unlockonfisibefore any commit (pre-commit decryptsvault.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.
rbw1.15.0 — tryapt install rbw; if the version doesn't match, install the pinned release binary to/usr/local/bin(matchfisi). - 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 theclaudeuser 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 viasudo -iu claudefromsjator its own key. - B2. Add
claudeto thedockergroup. - B3. No sudo for
claude(explicit decision). Confirmsudo -iu claudeworks.
Stage C — Repo + Claude Code (as claude)
- C1. Generate
claude'sed25519key; [USER] register the public key in Forgejo (Settings → SSH keys). - C2. Clone
ssh://git@forgejo.nyumbani.baobab.band:7577/sjat/boma.gitinto/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 perdocs/runbooks/claude-code-setup.md. Set gituser.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) onubongo; thenrbw sync,rbw unlock; verifyrbw get boma-ansible-vaultreturns the vault password. - D3. Offline-cache verification (ADR-015 open item, security-relevant):
confirm
rbwdecrypts its local cache with Vaultwarden unreachable. Stamp the result into ADR-015 /rotate-secrets.md(replaces theTO VERIFYnote).
Stage E — Inventory + base (partial)
- E1. Add
ubongotoinventories/production/hosts.ymlundercontrol(manual exception; notetf-inventorywill overwrite — re-add after). - E2. Set
base__firewall_control_addrto10.20.10.151in the appropriategroup_vars(the dormantssh-from-controlknob, ADR-020/021). - E3.
make check PLAYBOOK=siteagainstcontrol; apply the builtfirewallconcern 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; reloadsshd(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: fillubongo's real specs (M70q, i3-10100T, 16 GB, 256 GB SanDisk X600) into the TBD skeleton; node-capacity row already present. - H3.
STATUS.md: moveubongofrom "Designed but not built" toward built (note what's live vs. still pending — mesh, fullbase). - H4. Note the dedicated-
claudeidentity 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
basehardening — SSH/fail2ban/auditd concerns not built (onlyfirewall). - Recovery wiring (G) — TF-state backup to
mamba, rbw mirror — no TF state yet (TF un-init'd).mambaas break-glass clone tracked separately.