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,98 @@
|
||||
# Requirement Verification Questions — atheos-blog-lxc
|
||||
|
||||
**Project**: Alpine LXC running Atheos, wired to Gitea, with `armistace/blog` cloned and ready.
|
||||
**Stage**: INCEPTION — Requirements Analysis
|
||||
**Date**: 2026-09-29
|
||||
|
||||
Answer each question by writing your choice after the `[Answer]:` tag (e.g. `[Answer]: B`).
|
||||
Option **X) Other** is always available — describe it after the tag.
|
||||
|
||||
---
|
||||
|
||||
## 0. CRITICAL FINDING — please read before answering Q1
|
||||
|
||||
Reconnaissance found that **`armistace/blog` is a Pelican static site, not a folder of markdown**:
|
||||
|
||||
```
|
||||
src/content/*.md ← ~20 posts (the actual writing)
|
||||
src/themes/cleanblog/ ← theme
|
||||
src/pelicanconf.py ← build config
|
||||
src/Makefile ← `make html`, `make serve-global`, `make github`
|
||||
requirements.txt ← pelican[markdown], markdown-markup-emoji
|
||||
```
|
||||
|
||||
So there are two different needs, and they need different tools:
|
||||
|
||||
- **Atheos** = a browser editor. It edits files and does git (its `codegit` component).
|
||||
It can edit the `.md`, commit and push — but it **cannot build or publish** the site.
|
||||
- **Publishing the site** = running `make html` (+ `make github` / `pelican`), which needs
|
||||
**Python + pelican**, not PHP.
|
||||
|
||||
**Q1. How important is it that this box can also BUILD/PUBLISH the site?**
|
||||
- A) **Editor only** — I'll build/publish elsewhere (CI or another box). Atheos just edits + commits.
|
||||
- B) **Also include Pelican** — install Python + pelican + make so I can run `make html` /
|
||||
`make serve` on this box to preview and regenerate the site.
|
||||
- C) **Also include Pelican AND a local preview server** — so I can click through the built site
|
||||
from the browser on this LXC before pushing.
|
||||
- X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]:
|
||||
|
||||
---
|
||||
|
||||
**Q2. Does Atheos's git integration need to work non-interactively (credentials pre-wired),
|
||||
or will you supply them when you first push?**
|
||||
- A) **Bake in the git identity + let me push over HTTPS with a Gitea token** (I'll give you /
|
||||
you use an existing scoped token) so push works immediately without a prompt.
|
||||
- B) **Clone read-only / leave push to me** — set up the clone and git config, but leave the
|
||||
credential to me (e.g. via SSH key I install myself, per my point 4).
|
||||
- C) **Use SSH from the start** — configure the remote as `git@git.aridgwayweb.com:armistace/blog.git`
|
||||
and just wait for the key I'll add.
|
||||
- X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]:
|
||||
|
||||
---
|
||||
|
||||
**Q3. Which PVE node should host the LXC, and on which storage?**
|
||||
- A) **janeway (.225)** — 3.3 GB RAM free, `extra` storage 874 GB free. (Recommended: roomy.)
|
||||
- B) **data (.42)** — only ~0.95 GB RAM free and no swap; a 20 GB guest is fine on disk but the
|
||||
node is tight. (Not recommended.)
|
||||
- C) **You pick** whatever is healthiest at build time.
|
||||
- X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]:
|
||||
|
||||
---
|
||||
|
||||
**Q4. Should the container be reachable only on the LAN, or exposed via NPM
|
||||
(nginx proxy manager) with a hostname like `blog-ide.aridgwayweb.com`?**
|
||||
- A) **LAN only** — browse to `http://<lxc-ip>/`. Simplest, nothing new exposed.
|
||||
- B) **Add an NPM proxy host** — I get a hostname; you add the proxy host + DNS (you own NPM).
|
||||
- C) **LAN now, NPM later** — build LAN-only, leave a RUNBOOK section for adding NPM.
|
||||
- X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]:
|
||||
|
||||
---
|
||||
|
||||
**Q5. Extensions (AI-DLC security/resiliency baseline). Your usual choice is to opt OUT for a
|
||||
LAN-only PoC, and I document the "what to do if this ever moves external" TODOs instead. Confirm?**
|
||||
- A) **Opt out of both, document the TODOs** (your standing preference).
|
||||
- B) **Enable security-baseline** too.
|
||||
- C) **Enable resiliency-baseline** too.
|
||||
- D) **Enable both.**
|
||||
- X) Other (please describe after [Answer]: tag below)
|
||||
|
||||
[Answer]:
|
||||
|
||||
---
|
||||
|
||||
## Notes / assumptions I'll carry unless you say otherwise
|
||||
|
||||
- **Q6 (implicit, shout if wrong):** "downloads … ready for me to use" = the repo is cloned to a
|
||||
path Atheos can open as a **project**, on the correct branch, with git identity configured —
|
||||
not that the container does anything to the blog's remote. The blog stays a separate repo; this
|
||||
project's repo holds the **provisioning**.
|
||||
- LXC defaults I propose: **2 vCPU, 1 GB RAM, 20 GB root**, Alpine 3.23, `onboot=1`, unprivileged.
|
||||
- I will **not** touch `armistace` SSH keys — that's your point 4.
|
||||
@@ -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