# 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:///` 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: ``` atheos-blog-lxc/ ├── README.md ├── RUNBOOK.md ├── docs/{security-notes.md,resiliency-notes.md} ├── scripts/{provision.sh,update-atheos.sh} └── aidlc-docs/ ← process trail only; never code ```