add Phase 2B contained runtime artifacts

This commit is contained in:
makearmy 2026-07-12 21:01:17 -04:00
parent a9fec0d83d
commit 82e64cec43
35 changed files with 5177 additions and 40 deletions

View file

@ -1,6 +1,6 @@
# Milestone 0 host runtime and security plan
Status: corrected planning checkpoint, 2026-07-12. Nothing in this document authorizes execution. Phase 1 created only the inert `le_app_codex` identity and directory skeleton. Package installation, systemd changes, namespace creation, firewall changes, user-manager startup, Docker startup, Codex authentication, repository/source copying, and production changes remain separately approval-gated.
Status: Phase 2A completed and verified, 2026-07-12; Phase 2B is review-only. Nothing in this document authorizes Phase 2B execution. Phase 1 created only the inert `le_app_codex` identity and directory skeleton. Phase 2B systemd changes, namespace creation, firewall changes, user-manager startup, Docker startup, Codex authentication, repository/source copying, and production changes remain separately approval-gated.
This document supersedes the earlier runtime proposal. In particular, the project will not use a system service running `dockerd` with `User=le_app_codex`, will not enable lingering, and will not install the unmodified AUR `docker-rootless-extras` package.
@ -42,26 +42,15 @@ Root starts and stops `user@1200.service` explicitly after inert acceptance test
A system unit with `User=le_app_codex` must not run `dockerd`. That model bypasses the supported per-user lifecycle and does not provide the required containment of Codex and every sibling user process.
## Package decision gate and pinned launcher
## Completed Phase 2A and pinned launcher
The last isolated preview of:
Phase 2A host maintenance is complete and supersedes the provisional package-maintenance gate. The host booted successfully on `7.1.3-zen1-2-zen`; both standard and Zen 7.1.3 kernels and their NVIDIA DKMS modules are installed. `rootlesskit 3.0.1-1`, `slirp4netns 1.3.4-1`, `libslirp 4.9.3-1`, and `openai-codex 0.144.1-2` are installed; `/usr/bin/codex` reports `codex-cli 0.144.1`.
```text
pacman -Syu --needed rootlesskit slirp4netns openai-codex
```
was refreshed after removal of unused NoMachine and is now a 220-package full host upgrade (215 upgrades and 5 new packages) affecting both installed kernels and OpenSSH. It requires reboot planning, service-health checks, `.pacnew` review, explicit Snapper snapshot verification, and separate operator approval. The checksummed baseline is in `phase2a-host-maintenance/approved-transaction/`. A partial Arch upgrade is not acceptable.
The package paths are:
1. schedule and approve the complete refreshed host upgrade; or
2. investigate clean-chroot, checksum-pinned packages compatible with the current system, without partially upgrading repository packages.
The operator provisionally selected Path A on 2026-07-12 as the preferred Phase 2 package path because it is the shortest safe route consistent with Arch's complete-upgrade model. This records planning direction only: the transaction, maintenance window, package execution, and reboot remain separately approval-gated. The uncommitted review package is under `docs/le-app-database-migration/phase2a-host-maintenance/`.
Post-maintenance verification also established: Laser Everything HTTP 200; NoMachine removed; stale 6.19 DKMS state removed; Snapper repaired with valid Pacman pre/post snapshots; protected Directus archive checksum valid; retired Directus stopped with restart policy `no`; `/root/server-compose-runner.sh` no longer references `lasereverything.net.forms`; and no unexpected failed system units. The retained review/evidence package is under `phase2a-host-maintenance/`.
Do not install the unmodified AUR `docker-rootless-extras` package. Its install hook reloads global sysctls, which exceeds this project's boundary. Do not curl an installer into a shell.
Install only a reviewed Moby `contrib/dockerd-rootless.sh` matching the Docker engine actually installed after the chosen package path. At the current checkpoint Docker is `29.6.1`; the candidate is:
Install only a reviewed Moby `contrib/dockerd-rootless.sh` matching the Docker engine installed by the completed Phase 2A maintenance. Docker is `29.6.1`; the reviewed artifact is:
```text
Moby tag: docker-v29.6.1
@ -71,7 +60,7 @@ SHA-256: 904c9b9e35f6927c0a5e65afb4d35b6bc9eb1278c878044501281fc728c9be46
Owner/mode: root:root 0755
```
Revalidate the installed Docker version, upstream tag, source content, checksum, and launcher compatibility immediately before installation. If Docker changes, this filename and checksum are invalid until a matching launcher is reviewed. A clean-chroot package containing only the pinned launcher is acceptable; it must have no global sysctl hook, setup tool, service enablement, or post-install execution.
Docker was revalidated as Arch package `1:29.6.1-1`, client `29.6.1` build `8900f1d330`, daemon binary `29.6.1` build `8ec5ab355a`. The upstream tag, source, and checksum were re-fetched and revalidated on 2026-07-12. Revalidate all four again immediately before installation. If Docker changes, this filename and checksum are invalid until a matching launcher is reviewed. A clean-chroot package containing only the pinned launcher is acceptable; it must have no global sysctl hook, setup tool, service enablement, or post-install execution.
## User-manager filesystem and device containment
@ -132,7 +121,7 @@ ProtectControlGroups=yes
ProcSubset=pid
```
`ProtectProc=invisible` and hostname isolation remain acceptance-test candidates. They may be added only after the user manager, RootlessKit, Docker, Codex, and test containers pass with them. The final contract must prevent unrelated host-process inspection without breaking the supported runtime.
`ProtectProc=invisible` and hostname isolation remain acceptance-test candidates. Compatibility must not be assumed; they may remain only after the user manager, RootlessKit, Docker, Codex, and test containers pass with them. The final contract must prevent unrelated host-process inspection without breaking the supported runtime.
## Resource boundary
@ -150,7 +139,7 @@ TasksMax=4096
These limits cover Codex, RootlessKit, `dockerd`, containerd, builds, jobs, and all containers. Validate generated properties and cgroup delegation before relying on the limits.
Disk-growth containment remains unresolved. Btrfs quotas remain intentionally disabled. The repaired `/.snapshots` Btrfs subvolume and successful Snapper snapshots 1, 2, and 3 establish snapshot operation, not quota accounting. Do not enable quotas, run `snapper setup-quota`, trigger a rescan, or impose the earlier proposed 250 GiB qgroup limit incidentally. Filesystem visibility constrains where the account may write but does not cap growth within `/srv/le-app-codex`. The operator must separately choose and approve a disk-quota policy.
Hard disk-growth enforcement is deferred and is not a Phase 2B installation blocker. Btrfs quotas remain intentionally disabled. The repaired `/.snapshots` Btrfs subvolume and successful Snapper snapshots 1, 2, and 3 establish snapshot operation, not quota accounting. Do not enable quotas, run `snapper setup-quota`, trigger a rescan, or impose the earlier proposed 250 GiB qgroup limit incidentally. Mandatory filesystem visibility and resource controls constrain access and CPU/memory/tasks but do not cap growth within `/srv/le-app-codex` and are not a hard storage quota.
Soft planning budgets remain useful for monitoring: 80 GiB container runtime, 25 GiB PostgreSQL, 15 GiB media, 10 GiB legacy uploads, 35 GiB BGBye models, 10 GiB temporary data, 5 GiB browser artifacts, 10 GiB migration reports, and 10 GiB checkout/home metadata. Startup should fail when host/project free-space thresholds are unsafe, but soft checks do not replace a hard quota.
@ -219,15 +208,16 @@ Docker client configuration lives only below `/srv/le-app-codex/home/.docker`. T
Root creates and owns the persistent namespace `/run/netns/le-app-codex`. The complete `user@1200.service` enters it through `NetworkNamespacePath`; the user cannot replace the namespace or firewall policy.
Current read-only findings at this checkpoint:
Current read-only findings from the checksummed root inventory:
- approved DNS resolver IPv4 addresses remain unresolved;
- the host resolver currently includes `192.168.10.1`, but observation is not approval;
- the operator approved Quad9 IPv4 `9.9.9.9` and `149.112.112.112`;
- the host resolver includes `192.168.10.1`, but it remains denied and no LAN resolver exception is proposed;
- no persistent `le-app-codex` network namespace currently exists;
- no project-specific nftables rules are deployed;
- the host has numerous existing Docker IPv4 and IPv6 bridge ranges.
- observed host public/NAT IPv4 is `74.67.173.56`, with host/LAN/WireGuard addresses `192.168.10.151`, `192.168.10.0/24`, `10.98.0.2`, and `10.98.0.0/24`;
- all 41 Docker networks and their IPv4/IPv6 IPAM ranges are retained in the Phase 2B inventory and covered by the private/IPv6 denials.
All host addresses, routes, LAN/VPN/WireGuard/management networks, public/NAT addresses, Docker bridges, and resolver endpoints must be dynamically inventoried immediately before drafting the final rules. Future policy must deny every observed Docker IPv4 and IPv6 bridge range and must not rely only on a stale hard-coded list.
All host addresses, routes, LAN/VPN/WireGuard/management networks, public/NAT addresses, Docker bridges, resolver endpoints, nftables rules, ACLs, and mounts were inventoried by root on 2026-07-12 at 20:24 EDT. The complete checksummed record is retained with the Phase 2B artifacts. The preflight dynamically repeats this inventory immediately before installation and hard-stops on drift.
The namespace is IPv4-only: assign no IPv6 address or route and reject IPv6 on the project veth. Do not change host-global IPv6 or sysctls.
@ -250,15 +240,13 @@ Explicitly reject:
- TCP 25, 465, and 587;
- all IPv6 traffic.
Known addresses requiring explicit coverage include `192.168.10.151`, `10.98.0.2`, and `2603:7080:7500:1279:75aa:d64d:4cf0:8f10`; these are not a complete inventory.
Required operator inputs are approved DNS resolver IPv4 addresses, the complete host/public/NAT address set, every LAN/VPN/WireGuard/bridge/management subnet, and any approved host DNS/proxy endpoint. Do not finalize or deploy nftables without those inputs and a fresh read-only ruleset inventory. Forge SSH is outside the sandbox; no TCP/22 or LAN exception is proposed.
Explicit coverage includes `192.168.10.151`, `10.98.0.2`, `74.67.173.56`, and `2603:7080:7500:1279:75aa:d64d:4cf0:8f10`, plus the inventoried network ranges. Public/NAT address drift is a fail-closed configuration-review condition before later namespace restarts or policy regeneration. Forge SSH is outside the sandbox; no TCP/22 or LAN exception is proposed.
## Acceptance gates and order of operations
Every stage stops for review. No real migration data, nonproduction credentials, or Codex authentication enters the boundary until inert tests pass.
1. Resolve and approve the package path; install only the approved packages in a separately controlled stage.
1. Verify the completed Phase 2A state and do not rerun its package transaction.
2. If a full host upgrade was selected, reboot and complete production health verification.
3. Install only the version-matched, checksum-pinned Moby launcher.
4. Install the resource slice and user-manager containment files without starting `user@1200.service`.
@ -313,9 +301,6 @@ Never recursively copy or `rsync` the nonproduction workspace into production. C
## Unresolved decisions checklist
- Phase 2 package path: Path A is provisionally preferred; package execution and reboot are not approved.
- Approved DNS resolver IPv4 addresses; `192.168.10.1` is currently observed but not approved.
- Complete current host, public/NAT, LAN, management, VPN, WireGuard, Docker bridge, and other container-network inventory.
- Codex authentication: device authentication or dedicated API key.
- Disk-growth enforcement: Btrfs quota policy or another separately approved mechanism.
- Whether deferred process/hostname hardening options pass acceptance tests.