Add CLAUDE.md

Orients future Claude Code sessions: this is an ops/docs repo for two
live remote hosts, not an application codebase - documents the file
map, the scripts/ deploy-and-diff workflow, and sanity-check.sh as the
closest thing to a test suite.
This commit is contained in:
2026-07-26 22:52:28 +02:00
parent 7153fd761b
commit 1518dc74c7
+93
View File
@@ -0,0 +1,93 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## What this repository is
This is **not an application codebase** — there's nothing to build, lint,
or test. It's the operations/documentation repo for administering two
real, live hosts remotely over SSH:
- **`alpha.jayfield.org`** (`89.58.8.149`) — public VPS, Ubuntu 24.04 LTS,
reached via `ssh -p 10022 jens@alpha.jayfield.org`. Runs authoritative
DNS (BIND9), a full mail stack (Postfix/Dovecot/rspamd/MariaDB), Apache
reverse-proxying into a Docker layer (Portainer, Nextcloud, Gitea, a
plain nginx container), and fail2ban.
- **`vlda-01`** — home intranet server (Unraid 7.3.2), reached via
`ssh root@vlda-01` from the same LAN. Not part of `alpha`'s public
`jayfield.org` zone — lives under the private, unresolvable
`home.jayfield.org` namespace. Currently the pull side of the Gitea
backup from `alpha`.
**All actual changes happen live on those hosts over SSH, not in this
repo.** This repo records what's true afterward and keeps reference
copies of deployed scripts/configs. There is no local checkout of any
live service config to edit — the working pattern for every task is:
SSH in → make the change on the real host → verify it live (curl a vhost,
run `sanity-check.sh`, check a real log line) → record it here.
## Document map (read the right one, not all of them)
| File | Purpose | Rule |
|---|---|---|
| `README.md` | **Live state** — what's actually running on `alpha` right now | Source of truth when it disagrees with `SETUP.md` |
| `SETUP.md` | Fresh-install build runbook (currently: from-scratch on 24.04) | How to reproduce the host from nothing, not what's live |
| `TODO.md` | Full chronological audit log of every bug/fix/hardening pass | **Append-only. Never consolidate or summarize existing entries unprompted** — this has been corrected before; keep the full narrative detail even when it's long |
| `SYNC-PLAN.md` | Point-in-time plan for the `vlda-01``alpha` intranet migration | Move a service's write-up into `README.md` once it's actually built and live; only update the plan for what's still pending |
| `MAIL-ACCOUNTS.md` | Day-to-day recipe for adding/disabling mail accounts | References `SETUP.md` §3 for the underlying schema |
| `DIFF.md` | One-time (2026-07-19) diff-vs-clean-install investigation | Explicitly a snapshot, not a live inventory — don't treat its findings as current without re-checking |
| `scripts/` | Reference copies of everything deployed on `alpha` and its clients | **Not auto-deployed** — see below |
## The `scripts/` deployment workflow
`scripts/` is split by host (`scripts/alpha/`, `scripts/clients/<host>/`)
because each host has a genuinely different OS with different
persistence/deployment conventions — each subdirectory's own `README.md`
states the exact OS/version and the live deployment path for every file.
**Editing a file under `scripts/` changes nothing on the real host.**
After editing, the actual workflow is:
1. Deploy the change to the live host (`scp`/`cat > file` over SSH to the
path listed in that host's `README.md`).
2. `diff` the deployed copy against the repo copy — they must match
byte-for-byte before moving on.
3. Test the actual behavior live (run the script, hit the endpoint,
reboot if it's boot-triggered) — not just "it deployed without error."
4. Only then update `README.md`/`TODO.md` to describe the change.
`vlda-01` (Unraid) needs particular care here: `/` and `/usr` are
RAM-backed and don't survive a reboot, and `/boot` (the persistent USB
stick) is VFAT with no Unix execute bit — scripts there are deployed via
the **User Scripts** plugin and invoked with `bash <path>`, never `exec`.
See `scripts/clients/vlda-01/README.md` before touching anything there.
## Verifying a change is the closest thing to a test suite
There's no CI here — "does it work" is checked directly against the live
host:
- `sudo /usr/local/sbin/sanity-check.sh` on `alpha` — checks the whole
stack (packages, DNS/DNSSEC, mail, MariaDB, fail2ban, every public
vhost by expected HTTP status, all Docker containers, plus a version
dump of every involved service) and prints PASS/WARN/FAIL. Runs
automatically once per boot via `sanity-check.service`; run it manually
after any config change that doesn't itself trigger a reboot.
- `sudo /usr/local/sbin/attacker-check.py` — weekly fail2ban log analysis
(also runs on its own via cron); safe to run manually any time.
- For anything touching the mail stack, DNS, or a vhost: a real end-to-end
check beats trusting the config on paper (e.g. an actual `dig` against
the authoritative server, a real test email round-trip via
`/var/log/mail.log`, a real `curl` against the public hostname) — this
repo's history (`TODO.md`) has repeated cases of a config that "looked"
correct but silently didn't work.
## A few standing facts worth knowing before editing docs
- `jayfield.org` (public, this host's zone) and `home.jayfield.org`
(private home-LAN namespace, `vlda-01` and friends) are deliberately
separate — see `README.md`'s "Domain namespace" section.
- Gitea (`git.jayfield.org`) is the canonical git host for this repo
itself (migrated from `vlda-01`, which keeps a cold, stopped backup
copy). There is no GitHub remote.
- SSH access details (ports, users) belong in `README.md`/host
`README.md`s, not duplicated here — check there rather than assuming.