Commit Graph
2 Commits
Author SHA1 Message Date
hermes 8f07c377c0 fix: address independent review - provision the vhost, fix host-vs-container reporting
Addresses the two valid items from the independent review (code/security/infra all
Approved; these were Minor/Needs-Change items).

1. provision.sh left the nginx vhost as a manual "see README" step, which broke the
   project's own repeatability requirement (FR-9): a rebuild from scratch would have
   served the distro default page instead of Atheos. The vhost is now written by the
   script itself, the distro default.conf is removed, and the script ASSERTS the socket
   contract (nginx fastcgi_pass == php-fpm pool listen) before Atheos is installed - so
   a 502 can only ever mean an nginx/php-fpm problem, never be mistaken for an Atheos one.

2. `free` and `uptime` reported the PVE HOST inside the container (a container that had
   restarted seconds earlier claimed "up 2 days"), which is misleading in monitoring and
   in the test output.

   The review suggested `apk add lxcfs` in the guest. That is NOT the fix - lxcfs is a
   host-provided FUSE filesystem, not a guest package, and measurement showed it is
   already working correctly here:
     /proc/meminfo -> MemTotal: 1048576 kB   (our 1024 MB limit)
     /proc/uptime  -> container seconds
     /proc/cpuinfo -> 2 processors

   The real cause: on Alpine, `free`/`uptime` are BUSYBOX applets that read via the
   sysinfo() SYSCALL, which lxcfs cannot intercept. procps-ng ships binaries that read
   the lxcfs-virtualised /proc files instead. Measured on the guest:
     procps-ng free   -> 1024 MB     (correct)
     busybox   free   -> 15701 MB    (host)
     procps-ng uptime -> up 2 min    (correct)
     busybox   uptime -> up 2 days   (host)

   So provision.sh now installs procps-ng, and build-and-test.sh reports container-
   accurate figures (detecting it via apk rather than a binary-existence probe, since
   the busybox applets are plain files too).

Verified after the change: build-and-test 40 passed / 0 failed; socket contract MATCH;
resource line now reads "18MB used of 1024MB, up 2 min".
2026-09-29 15:49:21 +10:00
hermes c2897ed660 feat: provision Alpine LXC 136 running Atheos, wired to Gitea
Provisions a LAN-only browser code editor (Atheos) in an Alpine 3.23 LXC on the
scotty PVE node, with armistace/blog cloned and ready to edit. Built via the
AI-DLC workflow; the full process trail is in aidlc-docs/.

Deliverables
- scripts/provision.sh          rebuild the guest from scratch (re-runnable)
- scripts/update-atheos.sh      move Atheos to latest upstream and RE-PIN
- scripts/build-and-test.sh     captures evidence for every acceptance criterion
- files/atheos.conf             nginx vhost (socket path byte-matches the FPM pool)
- files/ssh-config              ~atheos/.ssh/config deploy-key alias
- RUNBOOK.md                    operational guide (START AT STEP 1)
- docs/security-notes.md        credential model + wider-web hardening TODOs
- docs/resiliency-notes.md      what a non-PoC deployment would need

Design decisions
- D1 no git identity is configured by the agent; the operator sets it. Git will
  refuse the first commit until then, which is deliberate (requirement FR-11).
- D2 Atheos pinned to commit f90cd18 (build 611) with a verified tarball sha256,
  plus update-atheos.sh as the deliberate escape hatch.
- D3 nginx :80, web root /var/www/atheos, php-fpm over a unix socket.
- D6 git auth is a per-repository DEPLOY KEY scoped to armistace/blog, not a
  personal access token (a PAT's write:repository scope reaches every repo the
  account can write).

Verified on the live guest (40 checks, 0 failures)
- AC-2  Atheos served over HTTP 200 by nginx+php-fpm; socket paths byte-match
- AC-3  20 posts + themes/cleanblog present; owned by the php-fpm user
- AC-4  all five writable paths writable as the service user
- AC-5  remote targets armistace/blog; deploy key authenticates over SSH
- AC-6  survival proven with a real container restart, marker byte-identical
- AC-8  no python/pelican/pip/make and no DB engine - scope boundary enforced
- FR-11 git identity is empty; deploy key mode 600 owned by the service user

Traps documented in the RUNBOOK (each cost real debugging time)
- The Gitea SSH user is `gitea`, NOT `git` (app.ini SSH_USER=gitea). With `git`,
  sshd answers "Invalid user git" and rejects before reading authorized_keys,
  which is indistinguishable from a bad key.
- The Gitea SSH endpoint is the LAN address (:22 is not forwarded publicly).
- `uptime` and `free` report the PVE HOST inside an LXC without lxcfs, so a
  container that just restarted can claim days of uptime.
2026-09-29 15:44:11 +10:00