@@ -15,8 +15,15 @@ entry links back to the research that drove it, so the reasoning survives.
---
## 2026-09-08 — PROPOSED (not decided): run Claude Code inside an isolation boundary + brokers
- **Decision:** PROPOSED adopt — Claude Code runs in an **Incus KVM VM on server-01** (non-root, no sudo, no
## 2026-09-08 — DECIDED: the four small Agent-Sudo / recovery calls (grill-me 21:15–21:30)
- **Decision:** **A1 = (a)** tier 1 captures scoped-undo (implement D4; fix the test that pins tier-3-only). **A3 = (c)** primary tier-2 stays 503 — dissolved by CA-D11. **A7(A) = no**, an explicit tier-4 SUDO.md rule is not an attack signal (audit-only). **Recovery layer = host systemd level-0** (`control-plane-up` oneshot+timer, fixed command list, `ip_nonlocal_bind=1`) with Hermes as level-1; row 193 recommendation = keep fail-closed once the daemon has backoff. A7(A) and the recovery shape were defaults I took — object to reopen.
- **Why:** the 09-07 outage was boot order (LAN-IP port binds before WiFi), not a crash; an irreversible tier-1 is a gate removed without a constraint added.
- **Revisit when:** Ethernet + a redundant host exist (D3 long-term), or tier-1 undo latency is measured >2 s.
## 2026-09-08 — DECIDED (grill-me 21:10): run Claude Code inside an isolation boundary + brokers (CA-D11)
- **Decision:** ADOPT — **autonomous runner first** (background agents + unattended runs move into the VM; the interactive session stays on primary under auto mode + hook until brokers are proven, then migrates). Supersedes personal_projects rows 217 (jail on primary) + 216 (primary substrate). Detail: Claude Code runs in an **Incus KVM VM on server-01** (non-root, no sudo, no
docker.sock, no Vault creds, egress allowlist); all host reach goes through the existing brokers
(Agent-Sudo, secrets-proxy, Jenkins). Inside that boundary, auto mode / `--dangerously-skip-permissions`
is the Anthropic-sanctioned case. Docker Sandboxes rejected for now (unsupported on LMDE 7 / Debian 13;
@@ -24,12 +31,12 @@ entry links back to the research that drove it, so the reasoning survives.
- **Why:** today the agent shell is root-equivalent on the production host; the July design brokered
actions but never said where the agent runs. The hypervisor already exists (Incus qemu driver, KVM).
| A3 | primary tier-2 | **(c)** stays 503 on primary; dissolved by CA-D11 (Claude never runs *on* primary) |
| A7(A) | is an explicit tier-4 SUDO.md rule an attack signal? | **No** — `rule_match` tier-4 = operator said refuse, not an attack; stays audit-only. (Default taken; object if wrong.) |
| Recovery layer | shape | **systemd on the host = level-0**, Hermes = level-1 (see §2). Fixed command list = EXECUTE-vetted, no allowlist widening. (Default taken; object if wrong.) |
---
## 1. Ground truth that changes the order (measured 2026-09-08)
1.**Root cause of the 09-07 outage = boot order, not a crash.** Docker starts before the WiFi has its LAN IP;
every control-plane container binds `192.168.1.x:port` → `cannot assign requested address` → left stopped,
`RestartCount=0` (start failures never engage the restart policy). primary IP arrived 16:20:28 (Docker up 15:43);
server-01 WiFi is a USB `wlx…` adapter under wpa_supplicant, **not NetworkManager-managed → `network-online.target`
is meaningless there**. Vault failed the same way → daemon AppRole busy-loop for 24 h.
2.**Primary has ZERO working backups** (row 202: Timeshift dead since 07-05, cron silenced). Tier-3 snapshot on
primary has no substrate. Agent-Sudo may never auto-restore primary anyway (D3), but "snapshot before execute" is
currently a no-op there.
3.**server-01 1TB SSD (870 EVO) IS mounted** at `/data` (sda4, ext4, 17 G used: ollama, models, compose). Row 206
("not readable") is stale. SMART CRC count **UNVERIFIED** (needs root; last known 48 on 06-10, cable reseat pending).
Only one ATA line this boot (`ata8: failed to resume link` — an empty port, not sda). Treat `/data` as
*usable but untrusted for large sequential reads* until SMART is re-read.
4.**Row 192: a live server-01 Agent-Sudo `BRIDGE_API_KEY` is in git** (agent-builder/.claude/context.md). Anyone on
the LAN with it runs tier-0/1 as root on server-01. Still unrotated since 07-15.
5.**Row 177: Bitwarden↔Vault loop** makes every `secret/*` identity root-equivalent. Needs a Bitwarden collection
restructure (user decision). Not fixed by anything in this plan; it caps how much the VM boundary buys until done.
6. Incus on server-01 is VM-ready: qemu 10.0.11, `/dev/kvm` (group kvm), `incus-admin` membership, `images:debian/13`
VM image cached, `incusbr0` 10.85.172.0/24 NAT, 27 GB RAM / 24 cores free. Storage pool = `sandbox-dir` (dir) —
a VM needs a block-capable pool (btrfs/dir works for VMs via qcow2 in Incus 6; verify at build).
7. Hook v2.1 live; Agent-Sudo containers exited 128 both hosts; secrets-proxy `Exited (0)` since 07-09; Jenkins
provisioned but idle (row 181).
---
## 2. Build order (freeing-capability-first)
Each step = one Opus session or one background agent unless marked HUMAN. Rollback + blast radius per step.
### Step 0 — bring the control plane up **and make it survive the next reboot** 🔴 tonight/tomorrow
- 0.1 `sysctl net.ipv4.ip_nonlocal_bind=1` in `/etc/sysctl.d/90-docker-lan-bind.conf`, both hosts (via Agent-Sudo
once up, or sudo-bridge). Lets `192.168.1.x` binds succeed before the NIC has the address. **Rollback:** delete
file, `sysctl --system`. Blast radius: nil (kernel flag; no behaviour change when the IP is present).
- 0.2 `compose up -d` in order on each host: primary Vault → bitwarden-bridge → sudo-bridge → agent-sudo →
secrets-proxy (leave DOWN until S1) ; server-01 vault-sandbox → bitwarden-bridge-sandbox → agent-sudo → jenkins →
hermes → n8n prod/sandbox. Check `project_coolify_env_var_debt``${VAR}` placeholders BEFORE each up.
- 0.3 **`control-plane-up.service` + `.timer`** (host systemd, root, both hosts): `ExecStartPre` loop until the LAN
IP is present (`ip -4 addr show | grep 192.168.1.<88|90>`), then the fixed ordered `docker compose up -d` list
above, then NTFY. Timer every 10 min (idempotent; a no-op when healthy). Log every action to
`/var/log/control-plane-up.jsonl` (**training data**: Hermes reads it later; no new schema).
- This is level-0 recovery and the only piece outside the failure domain. **Hermes = level-1** (stays a container;
revived by level-0; CA-P5 ladder unchanged).
- **Fixed command list, no arguments from anywhere** → EXECUTE-vetted per `feedback_autonomous_security_constrain_not_gate`.
- Rollback: `systemctl disable --now`. Blast radius: at worst it starts stacks that were deliberately stopped —
so secrets-proxy is **excluded** until S2 lands, and a `/etc/control-plane-up/skip` file suppresses a stack.
- 0.4 **HUMAN (tier-4 Set B):**`agent-sudo-daemon.service` both hosts: `After=network-online.target docker.service`,
@@ -7,7 +7,7 @@ re-verify versions, repos, and Linux support before acting on anything.
| Topic | File | Status | Key open decision |
|---|---|---|---|
| **Infrastructure synthesis** — the whole landscape + reconciliation of the new research against decisions already made | [infrastructure-synthesis.md](infrastructure-synthesis.md) | Synthesized 2026-09-08 | **Confirm the July→Sept reality** (Max upgrade / Voice-Chat / Tailscale-Twingate / Agent-Sudo) before acting on anything. |
| **Autonomy + isolation evaluation** — does the plan work, gaps, and the 2026 Docker/Anthropic isolation landscape (auto mode, Bash sandbox, sandbox-runtime, Docker Sandboxes microVMs) | [autonomy-isolation-evaluation.md](autonomy-isolation-evaluation.md) | Evaluated 2026-09-08 (Fable 5.1) | **Adopt the VM-boundary + brokers shape?** (Incus KVM VM on server-01 running Claude Code; Agent-Sudo/secrets-proxy/Jenkins as the only host reach). Also: what killed the control plane at 16:07 on 2026-09-07. |
| **Autonomy + isolation evaluation** — does the plan work, gaps, and the 2026 Docker/Anthropic isolation landscape (auto mode, Bash sandbox, sandbox-runtime, Docker Sandboxes microVMs) | [autonomy-isolation-evaluation.md](autonomy-isolation-evaluation.md) | Evaluated 2026-09-08 (Fable 5.1) | DECIDED 2026-09-08 (adopt, autonomous runner first) → [decisions/GAMEPLAN_security-infra-deploy.md](../decisions/GAMEPLAN_security-infra-deploy.md). 16:07 outage = boot order (LAN-IP port binds before WiFi), solved. |
| Local AI coding stack (inference engine, coding harness, LifeOS, voice, skill porting) | [local-ai-coding-stack-research.md](local-ai-coding-stack-research.md) | Surface-level, in progress | **Goal framing:** RESOLVED by the existing vision = cost-reduction + tooling-independence, Claude stays the brain (NOT fully-local). See synthesis Part 3. |
### Where the prior infrastructure research lives (memory corpus)
@@ -21,7 +21,7 @@ Not duplicated here — cited in [infrastructure-synthesis.md](infrastructure-sy
## Cross-cutting open decisions
Pulled up from the individual briefs so they don't get buried:
0.**Isolation boundary** — where does Claude Code itself run? Proposed: Incus KVM VM on server-01, non-root, egress allowlist, brokers only. Decides whether `--dangerously-skip-permissions`/auto mode is safe. (autonomy-isolation-evaluation.md §4)
0. ✅ DECIDED 2026-09-08 —**Isolation boundary**: Incus KVM VM on server-01, non-root, egress allowlist, brokers only. Decides whether `--dangerously-skip-permissions`/auto mode is safe. (autonomy-isolation-evaluation.md §4)
1.**The goal** — cost / independence / fully-local. Governs every other choice in the local
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.