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.
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
# Requirements — atheos-blog-lxc
|
||||
|
||||
**Project**: Alpine LXC running Atheos, wired to Gitea, with `armistace/blog` cloned and ready to edit.
|
||||
**Stage**: INCEPTION — Requirements Analysis
|
||||
**Date**: 2026-09-29
|
||||
**Deploy Target**: Proxmox LXC on node **scotty** (.69). Not the K3s cluster.
|
||||
|
||||
---
|
||||
|
||||
## 1. Intent Analysis Summary
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **User request** | An Alpine LXC that (1) installs Atheos with nginx+PHP, (2) sets up git integration, (3) has `git.aridgwayweb.com/armistace/blog` cloned and ready to use, (4) leaves SSH key setup to the operator, (5) is backed by a 20 GB root. |
|
||||
| **Request type** | New Project (greenfield repo; provisioning a new guest) |
|
||||
| **Scope estimate** | Multiple components — guest creation, web stack, web app, VCS wiring, docs |
|
||||
| **Complexity estimate** | Moderate — no application code to write; the risk is in correct provisioning and in not fighting Atheos's install model |
|
||||
|
||||
### The one finding that shaped this
|
||||
|
||||
`armistace/blog` is a **Pelican static site**, not plain markdown: it has `pelicanconf.py`,
|
||||
`Makefile`, `publishconf.py` and `themes/cleanblog`. Building/publishing needs **Python + pelican**,
|
||||
which Atheos (PHP) cannot do. Andrew confirmed (**Q1=A**) that the existing pipeline already handles
|
||||
the build and that *"just writing markdown is more than enough"* — so the **build is explicitly out
|
||||
of scope**. This box is an **editor + git client**, nothing more.
|
||||
|
||||
---
|
||||
|
||||
## 2. Functional Requirements
|
||||
|
||||
| ID | Requirement | Source |
|
||||
|---|---|---|
|
||||
| **FR-1** | An **Alpine 3.23 LXC** is created on PVE node **scotty** (.69) with a **20 GB rootfs**, 2 vCPU and 1 GB RAM, `onboot=1`, unprivileged. | user point 5 + Q3 |
|
||||
| **FR-2** | **nginx + PHP-FPM** are installed and configured to serve Atheos; PHP includes the extensions Atheos wants for its plugin marketplace (**ZIP, OPENSSL, MBSTRING**) and `allow_url_fopen=On`. | user point 1 |
|
||||
| **FR-3** | **Atheos** (github.com/Atheos/Atheos) is installed into the web root, pinned to a specific commit, with `config.php`, `data/`, `workspace/`, `plugins/` and `themes/` writable by the PHP-FPM user. No database is required. | user point 1 |
|
||||
| **FR-4** | Atheos's **git integration** is configured and usable: git installed, a safe.directory set for the workspace, and Atheos's own git settings present (committer identity, default branch). | user point 2 |
|
||||
| **FR-5** | `armistace/blog` is **cloned into `workspace/`** on its default branch (`master`) so it appears as an Atheos project, ready to open and edit. | user points 3, 6 |
|
||||
| **FR-6** | The clone is **read-from-Gitea ready**, and the repo is left with instructions to **switch the remote to SSH** once the operator adds their key. The agent must **not** switch the remote nor install credentials itself. | Q2 + Q6 |
|
||||
| **FR-7** | A **RUNBOOK.md** documents: the container's IP, how to log in over SSH, where the workspace/project lives, exactly how to move to an SSH remote (`git remote set-url`) after adding the key, plus credential/identity setup and common ops. | Q2 + Q6 + standing preference |
|
||||
| **FR-8** | A **README.md** documents what this is, the install method, and the one-time provisioning steps. | standing preference |
|
||||
| **FR-9** | The project repo (`hermes/atheos-blog-lxc`) holds a **repeatable provisioning script** so the whole LXC can be re-created from scratch. | AI-DLC / standing preference |
|
||||
| **FR-10** | **Reachability is LAN-only**: Atheos served on the LXC's own IP on port 80. No NPM proxy host, no DNS record, no router port-forward is created. | Q4 |
|
||||
| **FR-11** | The agent **does not create or configure `armistace` credentials or git identity**. The operator performs that over SSH, under their own identity. | Q2 (explicit restriction) |
|
||||
|
||||
---
|
||||
|
||||
## 3. Non-Functional Requirements
|
||||
|
||||
| ID | Requirement |
|
||||
|---|---|
|
||||
| **NFR-1** | Rootfs is 20 GB and must stay comfortably within it (Atheos + nginx + PHP + git + the blog repo is well under 1 GB). |
|
||||
| **NFR-2** | The container must survive a **reboot** and return serving Atheos with the clone still present and functional (verify, don't assume). |
|
||||
| **NFR-3** | Provisioning must be **repeatable and idempotent-friendly**: a single documented script plus manual steps. |
|
||||
| **NFR-4** | The guest must not depend on the K3s cluster; it is a standalone LXC. |
|
||||
| **NFR-5** | Timezone should match the operator (**Australia/Brisbane**) so Atheos/git timestamps are sane. |
|
||||
|
||||
---
|
||||
|
||||
## 4. Explicitly Out of Scope (confirmed by the operator)
|
||||
|
||||
- **Building/publishing the Pelican site.** No Python, pelican, `make html`, or preview server on
|
||||
this box (Q1=A). The existing pipeline handles the build.
|
||||
- **SSH key creation/installation for `armistace`.** Operator does this (Q2, user point 4).
|
||||
- **Any NPM / public exposure** (Q4=LAN only).
|
||||
- **Atheos user accounts beyond the initial install.** The operator will create their own login
|
||||
during Atheos's first-run installer.
|
||||
|
||||
---
|
||||
|
||||
## 5. Constraints & Risks
|
||||
|
||||
| ID | Constraint / risk | Mitigation |
|
||||
|---|---|---|
|
||||
| **C-1** | 🔴 **Atheos on Alpine is off the beaten path.** Upstream only documents Ubuntu/CentOS/Apache/nginx. Alpine's PHP packaging and paths differ. | Verify the package set on Alpine 3.23 before installing (php83 + php83-fpm + php83-session + php83-zip + php83-openssl + php83-mbstring). Confirm the FPM socket path and nginx `fastcgi_pass` match. |
|
||||
| **C-2** | 🔴 **Alpine is OpenRC, not systemd.** Service management is `rc-service`/`rc-update`. | Provision with OpenRC idioms; do not emit systemd units. |
|
||||
| **C-3** | 🔴 **The Alpine template is not yet downloaded on scotty.** | `pveam update` + `pveam download local alpine-3.23-default_...` before `pct create`. |
|
||||
| **C-4** | 🟠 **Atheos first-run installer needs the web root writable** and will create a config + admin user. | Set ownership/permissions correctly; document that the operator completes the installer in the browser. |
|
||||
| **C-5** | 🟠 **`git clone` of an internet-facing Gitea from a new guest** — the clone is public (repo is public), so no credential is needed for the *clone*. | Confirm the repo stays public; clone over the public URL. If it ever goes private, the RUNBOOK covers adding a credential. |
|
||||
| **C-6** | 🟡 Agent must respect the **no-armistace-credentials** restriction. | FR-11; state it plainly in the RUNBOOK so a future agent does not "helpfully" set it. |
|
||||
|
||||
---
|
||||
|
||||
## 6. Extension Configuration
|
||||
|
||||
| Extension | Enabled | Decided At |
|
||||
|---|---|---|
|
||||
| security-baseline | ❌ No | Requirements Analysis (Q5=A) |
|
||||
| resiliency-baseline | ❌ No | Requirements Analysis (Q5=A) |
|
||||
| property-based-testing | ❌ No | Requirements Analysis (Q5=A) |
|
||||
|
||||
Per the standing preference, the opted-out concerns are **not dropped** — they are written up as
|
||||
"what to do if this ever moves external" in `docs/security-notes.md` and `docs/resiliency-notes.md`
|
||||
(see FR-12 below).
|
||||
|
||||
| ID | Requirement | Source |
|
||||
|---|---|---|
|
||||
| **FR-12** | Even with both extensions opted out, produce `docs/security-notes.md` and `docs/resiliency-notes.md` listing the concrete hardening TODOs and their code touch-points, and cross-reference them from the README. | standing preference |
|
||||
|
||||
---
|
||||
|
||||
## 7. Acceptance Criteria (Given/When/Then)
|
||||
|
||||
- **AC-1** — *Given* the LXC is created, *when* I run `pct config 136` on scotty, *then* it shows
|
||||
Alpine 3.23, rootfs 20 GB, and it is onboot-enabled.
|
||||
- **AC-2** — *Given* provisioning is complete, *when* I open `http://<lxc-ip>/` from the LAN, *then*
|
||||
Atheos's first-run installer (or login) is served by nginx/PHP — not a 502/404.
|
||||
- **AC-3** — *Given* I complete the Atheos installer, *when* I open the workspace, *then* `blog` is
|
||||
present as a project whose files I can open and edit.
|
||||
- **AC-4** — *Given* the `blog` project is open, *when* I edit a markdown file and save, *then* the
|
||||
change persists to disk inside the container.
|
||||
- **AC-5** — *Given* the clone, *when* I run `git -C workspace/blog remote -v`, *then* it points at
|
||||
Gitea (initially HTTP), and the RUNBOOK gives the exact `git remote set-url` command to move to SSH.
|
||||
- **AC-6** — *Given* the container is rebooted, *when* it comes back, *then* Atheos serves again and
|
||||
the clone and any edits are still present.
|
||||
- **AC-7** — *Given* the RUNBOOK, *when* a different agent picks the repo up, *then* they can
|
||||
re-provision or hand off without prior context.
|
||||
- **AC-8** — *Given* the build is out of scope, *when* I search the container, *then* no Python/pelican
|
||||
build tooling is installed (proving scope was respected).
|
||||
|
||||
---
|
||||
|
||||
## 8. Key Requirements Summary
|
||||
|
||||
- One small **Alpine LXC (136) on scotty**, 20 GB root, serving **Atheos** over **nginx+PHP** on the LAN.
|
||||
- **Atheos is an editor + git client** here — deliberately **not** a build environment.
|
||||
- **`armistace/blog` cloned into Atheos's workspace**, ready to write in.
|
||||
- **Operator-owned credentials**: the agent sets up the clone and the instructions; the operator adds
|
||||
the SSH key and, later, switches the remote.
|
||||
- **RUNBOOK + README + re-provision script**, because a later agent or a re-build must not need me.
|
||||
Reference in New Issue
Block a user