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

atheos-blog-lxc

Provisioning for a browser-based code editor (Atheos) in an Alpine LXC, wired to Gitea, with the armistace/blog repository cloned and ready to edit.

Built with the AI-DLC workflow — the full process trail (requirements, design, units, decisions, audit log) lives in aidlc-docs/.


What this is

A small, single-purpose Proxmox container that puts an editor in your browser so you can write blog posts and commit them to Gitea — without VS Code, without a cloud IDE, and without a build toolchain.

Guest Proxmox LXC 136 on node scotty
OS Alpine 3.23.2 (unprivileged, OpenRC)
URL http://192.168.178.57/ — LAN only
Stack nginx → php-fpm 8.3 (unix socket) → Atheos IDE
Sizes 2 vCPU · 1 GB RAM · 20 GB rootfs · onboot=1
Editor Atheos — PHP, no database, real vim keybindings
Project armistace/blog cloned to /var/www/atheos/workspace/blog
Git auth per-repository deploy key (write-enabled)

What it deliberately is NOT

  • Not VS Code / code-server / Coder. Atheos is an independent, lightweight PHP editor.
  • Not a build environment. The blog is a Pelican site, but building/publishing happens in your existing pipeline. There is intentionally no Python, no pelican, no make here — the presence of Python would be a defect (verified by acceptance criterion AC-8).
  • Not internet-facing. No reverse proxy host, no DNS record, no port-forward is created.

Quick start

If you just want to use it:

  1. Open http://192.168.178.57/ and complete the Atheos first-run installer.
  2. ssh root@192.168.178.57, then set your git identity — see RUNBOOK.md STEP 1. Git will not let you commit until you do, and that is deliberate: the agent does not create your identity.
  3. Add the deploy key to armistace/blog and switch the remote to SSH — RUNBOOK.md STEP 2 & 3.

👉 The full operational guide is RUNBOOK.md. Start at STEP 1.


Repo layout

.
├── README.md                     this file
├── RUNBOOK.md                    ← operational guide; START AT STEP 1
├── docs/
│   ├── security-notes.md         credential model + hardening TODOs if ever exposed
│   └── resiliency-notes.md       what a non-PoC deployment would need
├── files/
│   ├── atheos.conf               nginx vhost (install to /etc/nginx/http.d/atheos.conf)
│   └── ssh-config                ~atheos/.ssh/config (the gitea-blog alias)
├── scripts/
│   ├── provision.sh              rebuild the whole guest from scratch
│   └── update-atheos.sh          update Atheos to latest upstream + re-pin
└── aidlc-docs/                   AI-DLC process trail (requirements → design → units → audit)

Rebuilding from scratch

The guest holds nothing unique — the blog's source of truth is Gitea — so a rebuild is cheap:

# on scotty (192.168.178.69), as root
pct destroy 136
sh scripts/provision.sh

scripts/provision.sh is written to be re-runnable and reproduces the documented build exactly, including the pinned Atheos commit and its verified tarball checksum.


Design decisions worth knowing

# Decision Why
D1 No git identity is configured by the agent. You set it (RUNBOOK STEP 1). You own the armistace identity; setting it programmatically would impersonate you and blur attribution (requirement FR-11).
D2 Atheos is pinned to a commit SHA, with update-atheos.sh to deliberately move it. Atheos has no releases; a moving branch is not reproducible.
D3 nginx :80, web root /var/www/atheos, php-fpm over a unix socket. Smallest network surface — nothing extra listens on the LAN.
D6 Per-repository deploy key, not a personal access token. A Gitea PAT's scopes are route groups matched against your whole account, so a stolen token reaches every repo you can write. A deploy key reaches exactly one.

Full rationale, including the alternatives that were rejected and the verification behind them, is in aidlc-docs/design-artifacts/application-design.md and docs/security-notes.md.


Security at a glance

  • Git auth: deploy key scoped to armistace/blog, private key mode 600, owned by the php-fpm user.
  • The SSH alias uses IdentitiesOnly yes, so no other key is ever offered.
  • The web app runs as a dedicated unprivileged user (atheos), which also owns the workspace.
  • ⚠️ Accepted risk: Atheos runs as a web app with shell-exec reach, so a compromise can use the deploy key to write blog. That is the intended floor (one repo, one branch) rather than the ceiling. Keep branch protection on blog's master.
  • If this is ever exposed to the wider web, work through docs/security-notes.md §2 first.

Provenance

Built 2026-09-29
AI-DLC stages executed Workspace Detection, Requirements, Workflow Planning, Application Design, Units Generation, Infrastructure Design, Code Generation, Build & Test
Stages skipped (with rationale) Reverse Engineering, User Stories, Functional Design, NFR Requirements, NFR Design — see aidlc-docs/plans/execution-plan.md
Atheos pin f90cd1806dbe0e25673a77e27390e8b1a240c3f6 (build 611), tarball sha256 03ebcb1479b59f1898b5fa50808b181750b42211698f7f7b4042cf2514d78b20
S
Description
Alpine LXC running Atheos (browser editor) wired to Gitea + armistace/blog. LAN-only. Built with AI-DLC.
Readme
237 KiB
Languages
Shell 92.7%
PHP 7.3%