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,76 @@
|
||||
# Application Design (consolidated) — atheos-blog-lxc
|
||||
|
||||
**Stage**: INCEPTION — Application Design (minimal depth) · **Date**: 2026-09-29
|
||||
**Deploy target**: Proxmox LXC `136` on node `scotty` (.69) — Alpine 3.23 · LAN-only
|
||||
|
||||
---
|
||||
|
||||
## 1. What this system is
|
||||
|
||||
A small, single-purpose guest whose only job is to put a **browser editor** in front of the
|
||||
`armistace/blog` working copy. It is deliberately **not** a build environment.
|
||||
|
||||
```
|
||||
┌───────────────────────── LXC 136 (scotty) ─────────────────────────┐
|
||||
Browser (LAN) │ │
|
||||
http://<ip>/ ────┼──► nginx :80 ──unix socket──► php-fpm (php83) ──► Atheos (PHP) │
|
||||
│ │ │
|
||||
│ ├──► workspace/blog/** (rw) │
|
||||
│ └──► git CLI ──► local commit│
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
(operator, later) SSH identity + SSH remote
|
||||
▼
|
||||
Gitea git.aridgwayweb.com
|
||||
```
|
||||
|
||||
## 2. Components
|
||||
|
||||
| ID | Component | Role |
|
||||
|---|---|---|
|
||||
| C1 | LXC `136` on scotty, Alpine 3.23 | isolated runtime, OpenRC, 20 GB root, onboot |
|
||||
| C2 | nginx :80 | HTTP front end; sends `*.php` to C3 |
|
||||
| C3 | php-fpm `php83` | PHP runtime; ZIP/OPENSSL/MBSTRING; `allow_url_fopen=On` |
|
||||
| C4 | Atheos (pinned SHA) | the editor; git via its `codegit` component |
|
||||
| C5 | `/var/www/atheos/workspace` | project storage (writable) |
|
||||
| C6 | git CLI | VCS; `safe.directory` set; **no identity** (operator-owned) |
|
||||
| C7 | `blog` working copy | clone of `armistace/blog` on `master` |
|
||||
| C8 | Gitea (external) | clone source; agent never pushes |
|
||||
| C9 | Operator (external) | owns SSH key, git identity, Atheos admin, remote switch |
|
||||
|
||||
## 3. Key interfaces
|
||||
|
||||
1. **nginx → php-fpm**: `fastcgi_pass unix:<FPM_SOCKET>` must equal the pool's `listen =`. Mismatch ⇒ **502**.
|
||||
2. **Atheos → filesystem**: writable `config.php`, `data/`, `workspace/`, `plugins/`, `themes/`. Failure ⇒ installer dependency error.
|
||||
3. **Atheos → git**: needs `git` on PATH **and** `safe.directory` for the workspace (else *dubious ownership*). Identity intentionally unset.
|
||||
4. **git → Gitea**: HTTPS clone (public); operator later switches to SSH.
|
||||
5. **OpenRC**: `nginx` + `php-fpm83` enabled at `default` runlevel — otherwise first reboot breaks service (AC-6).
|
||||
|
||||
## 4. Design decisions (from the gate)
|
||||
|
||||
| # | Decision | Source |
|
||||
|---|---|---|
|
||||
| D1 | **No git identity configured by the agent.** `user.name`/`user.email` left unset; RUNBOOK instructs the operator to set them first. | Q1=B |
|
||||
| D2 | **Atheos pinned to a resolved commit SHA**, with an additional **update script** that re-clones at latest and re-pins. | Q2 |
|
||||
| D3 | Web root `/var/www/atheos`; workspace `/var/www/atheos/workspace`; nginx :80; php-fpm via unix socket. | Q3 |
|
||||
| D4 | Atheos served at the **site root**. | assumed, accepted |
|
||||
| D5 | Operator completes Atheos's first-run installer; **agent seeds no users**. | FR-11 / Q2 of requirements |
|
||||
| **D6** | **Credential = a per-repository Gitea deploy key scoped to `armistace/blog`, write-enabled** (`read_only:false`), owned by the php-fpm user, aliased via `~/.ssh/config` with `IdentitiesOnly yes`. **Not** a PAT (account-wide scopes) and **not** a dedicated Gitea user (no blast-radius reduction for `blog`). Private key generated on the guest; public half handed to the operator to add in-repo. | Andrew, 2026-09-29 (security review) |
|
||||
|
||||
## 5. Scope guards (each is a testable negative)
|
||||
|
||||
- No database installed
|
||||
- **No Python / pelican / `make`** — the presence of Python is a defect (AC-8)
|
||||
- No NPM proxy host / DNS / port-forward (FR-10)
|
||||
- No `armistace` credential, SSH key, or git identity created by the agent (FR-11)
|
||||
|
||||
## 6. Design-level risks handed to Infrastructure Design
|
||||
|
||||
- **C-1** Atheos on Alpine is under-documented → resolve the FPM socket and extension package names from the *installed* system, not from upstream docs.
|
||||
- **C-2** OpenRC, not systemd → all service work uses `rc-service` / `rc-update`.
|
||||
- **C-3** Alpine template must be downloaded on scotty first (`pveam update` + `download`).
|
||||
- **C-4** The installer needs a writable web root before it will run.
|
||||
|
||||
## 7. Unit mapping
|
||||
|
||||
U1 guest (C1) · U2 web stack (C2,C3) · U3 Atheos (C4,C5) · U4 git + clone (C6,C7) · U5 docs + script (all)
|
||||
Reference in New Issue
Block a user