From 459c347c127d9608a3f62e437f264194946f4b46 Mon Sep 17 00:00:00 2001 From: Sluijsens Date: Wed, 29 Jul 2026 14:14:58 +0200 Subject: [PATCH] Labels every command with who runs it and where Four contexts got tangled together across the doc as it grew: pi-main as root, pi-main as gitea-workflow, the separate proxy Pi, and Gitea's own web UI. Added a legend up front and tagged every single command block so it's no longer something the reader has to infer. --- .../gitea-deployment-workflow/audit.md | 10 +++ .../deployment/deployment-instructions.md | 67 +++++++++++++++---- 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/aidlc-docs/features/gitea-deployment-workflow/audit.md b/aidlc-docs/features/gitea-deployment-workflow/audit.md index 7d9a0d4..c65d329 100644 --- a/aidlc-docs/features/gitea-deployment-workflow/audit.md +++ b/aidlc-docs/features/gitea-deployment-workflow/audit.md @@ -1409,3 +1409,13 @@ by Infrastructure Design. U5 carries REF-U3-01's Umami origin gate plus FR-21 an **Context**: OPERATIONS — Deployment Setup, real host config revealed the reference project's nginx Sentry-tunnel pattern was carried over unnecessarily and conflicts with this app's own built-in tunnel --- +## Deployment Setup — Explicit Who/Where Labels on Every Command + +**Timestamp**: 2026-07-29T01:00:00Z + +**User Input**: "in de deployment instructions i snou niet echt duidelijk wanneer je als welke gebruiker de commando's moet draaien" + +**AI Response**: "Added a legend near the top of deployment-instructions.md defining four contexts (pi-main/root, pi-main/gitea-workflow, proxy Pi/root, Gitea web UI) and labelled every single command block throughout the document with one of them — account creation, lingering, directory setup, shared/env, systemd units, the entire nginx/certbot section (now clearly proxy-Pi-only except the pi-main firewall step), backup credentials, the backup script itself and its manual verification, and the Gitea Actions variables (explicitly called out as not a host command at all)." + +**Context**: OPERATIONS — Deployment Setup, clarity pass across the whole instructions document +--- diff --git a/aidlc-docs/features/gitea-deployment-workflow/operations/deployment/deployment-instructions.md b/aidlc-docs/features/gitea-deployment-workflow/operations/deployment/deployment-instructions.md index 2698b4c..218541e 100644 --- a/aidlc-docs/features/gitea-deployment-workflow/operations/deployment/deployment-instructions.md +++ b/aidlc-docs/features/gitea-deployment-workflow/operations/deployment/deployment-instructions.md @@ -10,6 +10,15 @@ software itself. SlpModularCms can host multiple customers; `slpsoftware` is thi and the owner's own site. A future second customer on the same Pi would get its own instance name throughout, following the same pattern. +**Who runs what, where** — three hosts and two accounts appear below; every command block is +labelled with one of these: +- 🖥️ **pi-main / root** — as yourself with `sudo`, on the Pi this application runs on +- 👤 **pi-main / `gitea-workflow`** — as the deploy account, in a **real login shell** (SSH in + directly as `gitea-workflow`, not `su -`/`sudo -i -u` from your own session — § 1.3 explains why + that distinction matters) +- 🌐 **proxy Pi / root** — as yourself with `sudo`, on the *separate* Pi that terminates TLS (§ 1.7) +- ☁️ **Gitea web UI** — not a host command at all; done in this repository's Actions settings + --- ## 1. One-Time Host Setup @@ -37,6 +46,8 @@ named after this project, since it may end up deploying other projects on this P `SlpSoftware` project's own pipeline may already have exactly this kind of account; check its `PI_MAIN_USERNAME` secret first and reuse it directly if it already has SSH shell access, rather than creating a second one). Example name: `gitea-workflow`. + +🖥️ **pi-main / root:** ```bash sudo useradd -m -s /bin/bash gitea-workflow sudo passwd gitea-workflow @@ -56,6 +67,8 @@ The rest of this document uses `gitea-workflow` as the example account name — if you pick something else. ### 1.3 Enable Lingering (INFRA-U6-01 — do this first, easy to forget) + +🖥️ **pi-main / root:** ```bash sudo loginctl enable-linger gitea-workflow ``` @@ -69,6 +82,8 @@ running independent of any login session. setting the rest of this up manually (before the deploy workflow ever runs): enabling lingering does not retroactively start the user manager — that happens on the next real login, reboot, or manually: + +🖥️ **pi-main / root:** ```bash sudo systemctl start user@$(id -u gitea-workflow).service ``` @@ -84,6 +99,8 @@ Two fixes, in order of preference: - **SSH in directly as `gitea-workflow`** instead of logging in as yourself and switching user — a real SSH login does go through `sshd`'s PAM stack and sets `XDG_RUNTIME_DIR` correctly - Or, after `su -`/`sudo -i -u`, just set it by hand once per shell: + + 👤 **pi-main / `gitea-workflow`:** ```bash export XDG_RUNTIME_DIR=/run/user/$(id -u) ``` @@ -102,6 +119,7 @@ Deliberately placed under `gitea-workflow`'s **own home directory**, not under for the CMS's own release/current/shared structure to live anywhere near the other websites at all, which directly avoids interfering with them (as you asked in Q2): +👤 **pi-main / `gitea-workflow`:** ```bash mkdir -p ~/apps/slpsoftware//releases ``` @@ -113,6 +131,7 @@ uploads *this* customer's website via FileZilla, e.g. `/mnt/storage1/www/html/sl (adjust the exact folder name to whatever convention the other sites under `html/` already use, if one exists). Rather than a plain directory, make it a symlink across accounts: +👤 **pi-main / `gitea-workflow`:** ```bash mkdir -p ~/apps/slpsoftware/ ln -s /mnt/storage1/www/html/slpsoftware/ ~/apps/slpsoftware//shared/wwwroot-web @@ -122,12 +141,17 @@ ln -s /mnt/storage1/www/html/slpsoftware/ ~/apps/slpsoftware//shared/w needs no changes for this — it only ever resolves the symlink chain, it doesn't care how many hops that chain has. What **does** need attention: `gitea-workflow` needs read + traverse permission on `/mnt/storage1/www/html/slpsoftware//` and its parent directories, which `webadmin` owns. -Simplest fix: put both accounts in a shared group (e.g. `webshared`), `chgrp -R webshared` that -folder, and make sure webadmin's FTP server creates new uploads group-readable (`g+rx`, not just +Simplest fix: put both accounts in a shared group (e.g. `webshared`), then, 🖥️ **pi-main / root**: +```bash +sudo chgrp -R webshared /mnt/storage1/www/html/slpsoftware/ +``` +and make sure webadmin's FTP server creates new uploads group-readable (`g+rx`, not just owner-readable) — a one-time permission setup, not something either pipeline touches per deploy. ### 1.5 Runtime Configuration File One file per environment, **outside** the release directory so it survives every switch: + +👤 **pi-main / `gitea-workflow`:** ```bash touch ~/apps/slpsoftware//shared/env chmod 600 ~/apps/slpsoftware//shared/env @@ -187,7 +211,8 @@ gate only catches drift between the frontend build and that Gitea variable; it c so keeping the two in sync is a manual discipline, not something enforced automatically. ### 1.6 systemd User Units -Create `~/.config/systemd/user/slpsoftware-test.service`: + +👤 **pi-main / `gitea-workflow`** — create `~/.config/systemd/user/slpsoftware-test.service`: ```ini [Unit] Description=SlpModularCms API (test) @@ -209,6 +234,8 @@ And `~/.config/systemd/user/slpsoftware-production.service` — identical, with `production` throughout (including the port inside `shared/env`). Enable both (does not start them yet — nothing is deployed there until the first CI run): + +👤 **pi-main / `gitea-workflow`** (real login shell — § 1.3): ```bash systemctl --user daemon-reload systemctl --user enable slpsoftware-test.service @@ -251,6 +278,7 @@ or container on the LAN — pi-main and its two ports, in this deployment's case server block for the domain to attach to and to answer the HTTP-01 validation challenge; asking for a certificate before this exists will fail. +🌐 **proxy Pi / root:** ```nginx server { listen 80; @@ -275,12 +303,15 @@ is answering on port 80, which is the proxy Pi. **Step 2 — request and install the certificate.** The nginx plugin edits the file from step 1 in place: it adds the `listen 443 ssl` block, the certificate/key paths, and (by default) an HTTP→HTTPS redirect for the port 80 block: + +🌐 **proxy Pi / root:** ```bash sudo certbot --nginx -d ``` Certbot's own systemd timer handles renewal automatically — nothing further to set up for that. -**Step 3 — verify.** After certbot finishes: +**Step 3 — verify.** Can be run from anywhere with network access to the domain (your own machine +is fine — this isn't tied to either Pi). After certbot finishes: ```bash curl -I https:///health # or whichever path this new site/API actually serves ``` @@ -292,8 +323,9 @@ site — nginx and certbot don't know or care what's actually listening on the b **Step 4 — restrict the backend port on pi-main (do this once the domain works end to end).** Since Kestrel now listens on `0.0.0.0:` (§ 1.5), anything on the LAN can reach it -directly, bypassing the proxy Pi's TLS entirely, unless pi-main's firewall says otherwise. On -**pi-main**: +directly, bypassing the proxy Pi's TLS entirely, unless pi-main's firewall says otherwise. + +🖥️ **pi-main / root:** ```bash sudo ufw allow from to any port proto tcp sudo ufw deny /tcp @@ -331,6 +363,8 @@ server { ``` ### 1.8 Database Backup Credentials (§ 4 depends on this) + +👤 **pi-main / `gitea-workflow`:** ```bash touch ~/.config/slpsoftware-db-backup.env chmod 600 ~/.config/slpsoftware-db-backup.env @@ -347,7 +381,9 @@ credential, ideally scoped to just read access (`SELECT`, `LOCK TABLES` — ever needs) rather than the application's own data-access login. ### 1.9 Gitea Actions Variables and Secrets -Set once, in this repository's Gitea Actions settings. This is exactly where the real path lives — + +☁️ **Gitea web UI** — not a host command, no SSH involved. Set once, in this repository's Actions +settings. This is exactly where the real path lives — `deploy-scp.yaml` never hardcodes it, it only reads `${{ vars.DEPLOY_PATH_TEST }}` / `${{ vars.DEPLOY_PATH_PRODUCTION }}`, so changing the path later is a variable edit, not a workflow change: @@ -389,13 +425,15 @@ Already built (U5/U6) — this is the read-only walkthrough for whoever operates - `wwwroot/web/` will be empty until a website workspace deploys into it — `/` serves the built-in placeholder until then (this is expected, not a failure) - The very first run has no "previous release" to keep — pruning naturally has nothing to prune -- Verify manually after the first run: `curl https://test.slpsoftware.nl/health` and - `curl https://slpsoftware.nl/health` (after the first production run) both return `200` +- Verify manually after the first run (from anywhere — not tied to either Pi): + `curl https://test.slpsoftware.nl/health` and `curl https://slpsoftware.nl/health` (after the + first production run) both return `200` ## 4. Database Backup Script -Create `~/scripts/backup-slpmodularcms-db.sh` on the Pi (this script is host-side by design — never -part of this repository, so no DB credential ever reaches Gitea): +👤 **pi-main / `gitea-workflow`** — create `~/scripts/backup-slpmodularcms-db.sh` on the Pi (this +script is host-side by design — never part of this repository, so no DB credential ever reaches +Gitea): ```bash #!/usr/bin/env bash @@ -433,11 +471,13 @@ ls -1t "$BACKUP_DIR"/*.sql.gz 2>/dev/null | tail -n +8 | xargs -r rm -f `mariadb-dump` is MariaDB's own name for the tool (present since MariaDB 10.4-ish); if the host only has the older `mysqldump` name, substitute it — same tool, same flags. +👤 **pi-main / `gitea-workflow`:** ```bash chmod +x ~/scripts/backup-slpmodularcms-db.sh ``` -**Verify once, manually**, before relying on it in a real deploy: +**Verify once, manually**, before relying on it in a real deploy — 👤 **pi-main / `gitea-workflow`** +(this matches exactly how `deploy-scp.yaml` itself invokes the script over SSH): ```bash ~/scripts/backup-slpmodularcms-db.sh production ``` @@ -445,6 +485,9 @@ Confirm a `.sql.gz` file appears under `~/backups/slpsoftware/production/` and t silently fail (the script uses `set -euo pipefail`, so a real error does propagate as a non-zero exit — which fails the calling `deploy-scp.yaml` step, correctly blocking the deploy). Worth a one-time restore rehearsal too — an untested backup is not a verified one: + +👤 **pi-main / `gitea-workflow`** (or wherever you have a MariaDB client and network access to +`127.0.0.1`'s database — this one isn't identity-sensitive, just needs DB access): ```bash gunzip -c ~/backups/slpsoftware/production/.sql.gz | mariadb -h 127.0.0.1 -u root -p ```