# adampowell.pro — repo guide (read this first)

> # 🔴 THIS SERVER IS BEING RETIRED — read before you touch anything
>
> **As of 2026-08-02 the site has been rebuilt on self-hosted hardware** (Proxmox LXC **CT 116**,
> `192.168.1.37`, at home) and is served publicly through a **Cloudflare Tunnel**. The DigitalOcean
> droplet `198.211.114.12` that every doc in this repo describes is now **legacy + rollback only**.
>
> **Cutover status: DONE — flipped 2026-08-03.** `adampowell.pro` and `www` are now `CNAME` → the
> Cloudflare Tunnel and serve from CT 116; the `*` wildcard was deleted. **This droplet no longer
> receives production traffic.** It is kept running and paid purely as instant rollback for ~2 weeks
> (revert = apex + `www` back to `A 198.211.114.12`, proxied), after which it gets cancelled.
> `bp`/`mickey`/`mac`/`dns` still point here on purpose — they are 301 redirects that die with it.
>
> **Where current-state hosting truth now lives:** `c:\dev\homelab` →
> `HOMELAB_MASTER_INFRASTRUCTURE.md` (CT 116 row) and its `TODO.md`. **Do not** document the new
> host here — this repo documents the droplet.
>
> **What changed in the rebuild** (so you don't assume droplet behaviour on the new box): apps run as
> a **non-root `app` user** (droplet runs everything as root); secrets moved **off-repo** to
> `/etc/app/secrets.env`; systemd units are `Restart=on-failure` (droplet uses `Restart=always`); the
> monolithic nginx config is **split in two** — a public vhost that structurally omits the
> confidential apps, and a tailnet-only vhost that carries them (see the note in
> [`docs/SERVER_NGINX_ROUTES.md`](docs/SERVER_NGINX_ROUTES.md)); mail relays through Proton SMTP.
>
> **🔑 Secret-rotation debt:** every credential ever committed to this repo's history is considered
> **burned** and must be rotated at cutover — this history also carries signed-NDA **PII**, which is
> why the new site repo (`c:\dev\adampowell-pro`) starts clean and code is ported file-by-file rather
> than by pushing this history anywhere.

This is the single git monorepo for the `adampowell.pro` server (`198.211.114.12`). It is both the source
of truth and the deploy artifact. Three docs in [`docs/`](docs/) describe the **entire** setup — keep them
authoritative:

- **[`docs/SYSTEM.md`](docs/SYSTEM.md)** — everything: hardware, OS, software/versions, ports + services +
  live status, deploy model, domains/DNS/Cloudflare, TLS, auth, secrets inventory, full URL/app index,
  per-app inventory, file trees, databases, cron/timers, disk/backups/logs, gotchas, smoke tests, changelog.
- **[`docs/SERVER_NGINX_ROUTES.md`](docs/SERVER_NGINX_ROUTES.md)** — the live nginx config (verbatim),
  port map, subdomain configs, route reference, nginx patterns/gotchas.
- **[`docs/WHEN CREATING A NEW APP.md`](docs/WHEN%20CREATING%20A%20NEW%20APP.md)** — the new-app playbook.

## The two rules that matter most

1. **Edit locally → `git commit` → `git push origin main`. NEVER edit, scp, build, `git stash`, or
   `git reset` on the server.** A GitHub webhook + a 5-minute `git pull` timer deploy automatically; a dirty
   server tree breaks the `--ff-only` merge and blocks the whole monorepo's deploy. (nginx config is the one
   server-side-only thing — back up both `sites-available` + `sites-enabled`, edit both, `nginx -t`, reload.)

   > ⚠️ **A push to `main` here deploys to the DROPLET ONLY (since 2026-08-03).** CT 116 was
   > repointed that day to the **clean repo `t3h28/adampowell-pro`** (`c:\dev\adampowell-pro`) —
   > its `ap-deploy.timer` (every 5 min: read-only deploy key as the non-root `app` user →
   > `git merge --ff-only origin/main` → `scripts/rebuild-symlinks.sh` → service restarts → ntfy)
   > now pulls THAT repo, E2E-proven with a real deploy the same day. **Site changes go in the
   > clean repo now; commits here reach only the rollback droplet** and stop mattering entirely
   > at droplet-cancel. (Between 2026-08-02 and -03 a push here deployed to both hosts.)

2. **After ANY change to the server, an app, a port, nginx, a service, or a secret — update the three docs
   in the same unit of work. No exceptions.** They are the canonical "hand this to anyone and they know the
   whole setup" reference. Bump the "Last verified/pulled" date at the top of whatever you touch. Re-pull
   live state read-only when needed (`ss -tlnp`, `systemctl is-active`, `pm2 list`, `df -h`, `node -v`,
   `certbot certificates`, `cat /etc/nginx/sites-enabled/adampowell.pro`). Never put secret *values* in the
   docs — reference them by name/location and keep the security-debt list current.

## Quick orientation

- App sources: `apps/<name>/` (frontends/self-contained) and `apps/server/<name>/` (backends, systemd).
- Web root `html/` is almost all symlinks into `apps/`, rebuilt by `scripts/rebuild-symlinks.sh` on deploy.
- One global login gates the site (`auth.service`, cookie `apsess`, nginx `auth_request`). A few apps add a
  second factor (command-center WebAuthn; nda/PO PHP password; remote-daemon/sharedtodo bearer token).
- Secrets live in `/root/secrets.env` (server-only) or `*/secrets.js` (client-side, gitignored).
- A new static/PWA app needs only a `rebuild-symlinks.sh` line + a `html/nav.json` tile — no nginx edit.

When in doubt, read `docs/SYSTEM.md` before acting on server infra.
