Files
atheos-blog-lxc/aidlc-docs/audit.md
hermes a6fb61d0ea 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.
2026-09-29 15:50:34 +10:00

9.3 KiB

AI-DLC Audit Log

APPEND-ONLY. Every user input captured verbatim, never summarized or paraphrased, with ISO-8601 timestamps. NEVER overwrite this file — always append new sections.

Workspace Detection

Timestamp: 2026-09-29T05:05:05Z User Input: "ok back to the plan. We need to engage AIDLC I want an alpine lxc that

  1. install atheos (nginx php etc)
  2. sets up git integrations (if required)
  3. downloads https://git.aridgwayweb.com/armistace/blog ready for me to use
  4. I will set upt he armistace ssh keys myself
  5. is backed by a 20gb root (tiny its basically going to be used to write blog)" AI Response: "Workspace Detection executed. New project (no existing aidlc-docs/). Created workspace scaffold at ~/dev/atheos-blog-lxc. Conducted pre-kickoff reconnaissance: (A) blog repo armistace/blog is a PELICAN STATIC SITE (Python), not plain markdown — it has src/{content,pelicanconf.py,devpelconf.py,publishconf.py,Makefile,tasks.py,themes/cleanblog}, builds with make html and publishes with make github/publishconf.py. Atheos alone CANNOT build/regenerate it; it edits the .md sources + git only. (B) Atheos requires: PHP + web server (nginx+php-fpm), NO database; writable config.php, data, workspace, plugins, themes; optional extensions ZIP, OPENSSL, MBSTRING (needed for plugin marketplace); optionally allow_url_fopen=On. Ships git integration (codegit component). (C) Alpine 3.23 (alpine-3.23-default_20260116_amd64.tar.xz) is available as a PVE template but NOT yet downloaded on any node. (D) CORRECTION to the 2026-09-29 Gitea incident record: 192.168.178.151 is LXC 110 (nginxpm / NPM), NOT the Gitea Actions runner. The .151 traffic observed against Gitea was NPM proxying Gitea's own web traffic; the runners run on the k3s nodes (k3s-3 .161, k3s-2 .162). Memory corrected accordingly. (E) git.aridgwayweb.com resolves to a PUBLIC IP (115.70.15.16) — Gitea is internet-facing via NPM." Context: INCEPTION — Workspace Detection

Requirements Analysis

Timestamp: 2026-09-29T05:05:05Z User Input: (none yet — awaiting answers) AI Response: "Created aidlc-docs scaffold (aidlc-state.md, audit.md, plans/, requirements/, story-artifacts/, design-artifacts/, incidents/). Generated aidlc-docs/requirements/requirement-verification-questions.md with 5 questions + stated assumptions. Key gate: Q1 asks whether the box must also BUILD the Pelican site (Python) or is an editor only (Atheos/PHP), because the blog repo is a Pelican static site, not plain markdown. Presented the questions inline in Matrix (Andrew reviews in-chat, not in .md files)." Context: INCEPTION — Requirements Analysis (Step 6 gate: awaiting user answers)

Requirements Analysis — Gate Answers

Timestamp: 2026-09-29T05:10:00Z User Input (verbatim): "1. A - It doesn't need to build the pipeline is well known just writing markdown is more than enough 2. I'll supply them, give me the ip address and I'll ssh in and set up my credentials in gitea, you can't do this and I want it under armistace 3. scotty 4. LAN only 5. A 6. clone it but leave instructions to get it over to ssh once I add the key" AI Response: "Answers recorded: Q1=A (editor only; Pelican build explicitly out of scope — the existing pipeline handles it, 'just writing markdown is more than enough'); Q2=Andrew supplies credentials himself over SSH, under the armistace identity (agent must NOT set an armistace git identity or credentials); Q3=PVE node scotty (.69) — verified: 15.7 GB RAM (12.1 GB available), 8 GB swap, scotty-local-lvm thin pool 771 GB free, in-use VMIDs 104-107/119/125/126/133-135 so 136 is free; Alpine template not yet downloaded on scotty; Q4=LAN only (no NPM/DNS/forward); Q5=A (opt out of security+resiliency, document the TODOs); Q6=clone the repo but leave RUNBOOK instructions for switching the remote to SSH once the key is added." Context: INCEPTION — Requirements Analysis (Step 7: generating requirements.md)

Requirements Analysis — Approval

Timestamp: 2026-09-29T05:12:00Z User Input (verbatim): "no I'm good to skip lets do it" AI Response: "Requirements APPROVED. User Stories stage explicitly SKIPPED (user confirmed). Per the AI-DLC rule for skipped stories, a requirement→unit map will be substituted for unit-of-work-story-map.md. Proceeding to Workflow Planning." Context: INCEPTION — Requirements Analysis complete → Workflow Planning

Workflow Planning — Approval

Timestamp: 2026-09-29T05:15:00Z AI Prompt: "Approve this execution plan? 6 stages to execute (Application Design, Units Generation, Infrastructure Design, Code Generation, Build and Test), 5 to skip (Reverse Engineering, User Stories, Functional Design, NFR Requirements, NFR Design)." User Response (verbatim): "continue looks good" Status: APPROVED Context: INCEPTION — Workflow Planning complete → Application Design

Application Design — Gate Answers

Timestamp: 2026-09-29T05:18:00Z User Input (verbatim): "1. B jI'll set it in there, remind me in the runbbok 2. good idea for now, but leave an update script that will reclone at latest and pin 3. looks good to me" AI Response: "Answers recorded. Q1=B — the agent configures NO git identity (user.name/ user.email left unset); the RUNBOOK must explicitly prompt the operator to set them as their first action after SSH. Q2 — pin Atheos to a resolved commit SHA and additionally ship an update/re-pin script that re-clones Atheos at latest and re-pins the SHA. Q3 — layout accepted (web root /var/www/atheos, workspace /var/www/atheos/workspace, nginx :80, php-fpm unix socket). Generated the five application-design artifacts (components.md, component-methods.md, services.md, component-dependency.md, application-design.md) with design decisions D1-D5 recorded." Context: INCEPTION — Application Design complete (awaiting approval)

Application Design — Security Model Decision (D6)

Timestamp: 2026-09-29T05:22:00Z User Input (verbatim): "would it be safer to have an atheos user or use a application key? lets get this security model for the key right incase I want to expose to the wider web" AI Response: "Researched against official sources + the live 1.27.3 instance rather than opining. Verified: (a) Gitea PAT scopes are ROUTE GROUPS (e.g. write:repository) matched against the USER's access, NOT per-repository — so a stolen armistace PAT reaches every repo the account can write; the instance's DB shows armistace holding 8 tokens, one with scope all. (b) Deploy keys are genuinely per-repository: swagger confirms /repos/{owner}/{repo}/keys (get,post,delete) and the DeployKey schema carries read_only. (c) CVE-2026-28744 (fixed 1.26.2) confirms the scope mechanism is real — 'repository-scoped access-token permissions on repository operations'. RECOMMENDED and user ACCEPTED a per-repo deploy key over both a PAT and a dedicated Gitea user. Rationale: a dedicated user still owns/needs access to blog, so it reduces nothing while adding a credential and muddying attribution; the deploy key bounds blast radius to ONE repo natively. Recorded as design decision D6. Wrote docs/security-notes.md (S-1..S-14 for wider-web exposure) and docs/resiliency-notes.md (R-1..R-8). Agent confirmed it CANNOT add the deploy key itself (API returns 'user should be an owner or a collaborator with admin write') — operator will add the public half in the repo." Context: INCEPTION — Application Design (security model finalised)

Application Design — Approval

Timestamp: 2026-09-29T05:23:00Z User Input (verbatim): "I like the model lets go with that. When you give me the public key (in here) I'll get it in the repo" AI Response: "Design APPROVED including D6. Proceeding to Units Generation, then CONSTRUCTION." Context: INCEPTION — Application Design complete → Units Generation

Units Generation — Approval

Timestamp: 2026-09-29T05:26:00Z AI Prompt: "Approve these 5 units and the U4 two-phase approach (clone over HTTPS now, deploy key generated + public half handed to operator, SSH switch as Phase 2 once the key is installed)?" User Response (verbatim): "phase 2 is all you I'll give you the go ahead and you can switch it over. lets get building" Status: APPROVED — units approved; U4 two-phase approved; Phase 2 (SSH remote switch) is agent-owned, to be executed once the operator signals the deploy key is installed. Construction to run AUTONOMOUSLY per the user's standing preference (no per-unit gates; stop only for hard blockers). Context: INCEPTION complete → CONSTRUCTION PHASE (Infrastructure Design → Code Generation → Build and Test)

OPERATIONAL NOTE — pre-existing Gitea CVE remediation (executed 2026-09-29, pre-construction)

Timestamp: 2026-09-29T05:26:00Z AI Response: "Recorded for trail integrity: earlier in this same session (before this AI-DLC workflow was engaged) the Gitea instance was found compromised via CVE-2026-60004 and remediated + upgraded 1.23.8 → 1.27.3. This deviates from the stage order — it happened before Inception. It is NOT an output of any stage of this workflow. Full record: spine partition hermes-ops, source_ref gitea-cve-2026-60004-incident. Also corrected a factual error in that record during this session: 192.168.178.151 is LXC 110 (nginxpm), NOT the Actions runner (user correction: '161 is the runner')." Context: OUT-OF-STAGE — recorded for audit honesty