Unit of Work — atheos-blog-lxc
Stage: INCEPTION — Units Generation (Part 2: Generation)
Date: 2026-09-29
A unit = a logical grouping of work that ends in a verifiable state.
U1 — Guest provisioning
|
|
| Goal |
An Alpine 3.23 LXC exists, reachable, correctly sized, and boot-persistent |
| Components |
C1 |
| Produces |
VMID 136 on node scotty, 20 GB root, 2 vCPU, 1 GB RAM, unprivileged, onboot=1, TZ Australia/Brisbane, package index updated |
| Preconditions |
Alpine 3.23 template downloaded on scotty (pveam update + download) |
| Verification |
pct config 136 shows Alpine + 20 GB root; the guest answers ping/ssh; date is Brisbane |
| ACs |
AC-1 |
| Notes |
Template download is part of this unit — it does not exist on scotty yet (risk C-3) |
U2 — Web stack
|
|
| Goal |
nginx serves PHP, and the two sides agree on the socket |
| Components |
C2, C3 |
| Produces |
nginx, php83-fpm, php83-{session,zip,openssl,mbstring} installed; pool + vhost configured; allow_url_fopen=On; both services enabled at the OpenRC default runlevel |
| Preconditions |
U1 |
| Verification |
A phpinfo()-style probe renders over HTTP (not 502/404); the nginx fastcgi_pass socket path string-matches the pool's listen (interface I-1); both services are in rc-update show |
| ACs |
AC-2 |
| Notes |
Resolve the actual FPM socket from the installed pool config, not from upstream docs (risk C-1). OpenRC idioms only — no systemd (risk C-2) |
U3 — Atheos install
|
|
| Goal |
Atheos runs and its installer page loads |
| Components |
C4, C5 |
| Produces |
Atheos at /var/www/atheos pinned to a resolved commit SHA; config.php present; data/, workspace/, plugins/, themes/ writable by the FPM user |
| Preconditions |
U2 |
| Verification |
http://<ip>/ returns the Atheos installer (HTTP 200, no PHP fatal in the error log); a write test inside workspace/ succeeds as the FPM user |
| ACs |
AC-2, AC-3 |
| Notes |
The genuinely uncertain unit (risk C-1). If Atheos fights Alpine's PHP layout, stop and report rather than papering over it |
U4 — Git + clone + deploy key
|
|
| Goal |
The operator can open blog, edit it, and push from here once they add the key |
| Components |
C6, C7 |
| Produces |
git installed; safe.directory set for the workspace; clone of armistace/blog (branch master) at workspace/blog; ed25519 deploy keypair generated on the guest (private key owned by the FPM user, mode 600); ~/.ssh/config host alias gitea-blog with IdentitiesOnly yes; user.name/user.email deliberately left unset |
| Preconditions |
U1 (git), U3 (workspace exists) |
| Verification |
blog appears as a project and its files open; git -C workspace/blog remote -v shows Gitea; the public key is emitted for the operator; git config --get user.name returns empty (proving D1) |
| ACs |
AC-3, AC-4, AC-5 |
| Notes |
Two phases by necessity: this unit leaves the remote on HTTPS; the SSH switch happens after the operator installs the deploy key (D6). The agent must not install it |
U5 — Docs, scripts, handoff
|
|
| Goal |
A different agent or a rebuild needs no prior context |
| Components |
all |
| Produces |
README.md, RUNBOOK.md (incl. "set your git identity first" prompt, the deploy-key install steps, and the exact git remote set-url command), docs/security-notes.md, docs/resiliency-notes.md, scripts/provision.sh, scripts/update-atheos.sh |
| Preconditions |
U1–U4 |
| Verification |
README + RUNBOOK exist and are linked; provision.sh is the exact sequence that was actually run; update-atheos.sh re-clones Atheos at latest and re-pins the SHA (D2) |
| ACs |
AC-7 |
| Notes |
The RUNBOOK is the operator-facing artifact: it must open with their required first action |
Deferred / not a unit
- The SSH remote switch — operator-owned (D6). Documented in RUNBOOK, not built.
- Atheos admin user creation — operator completes the installer (D5).
- Pelican build/publish — out of scope entirely (Q1=A, AC-8).
Greenfield code-organization strategy
No application code is generated, so the repo holds only provisioning + docs: