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.
5.4 KiB
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 viassh -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 viassh root@vlda-01from the same LAN. Not part ofalpha's publicjayfield.orgzone — lives under the private, unresolvablehome.jayfield.orgnamespace. Currently the pull side of the Gitea backup fromalpha.
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:
- Deploy the change to the live host (
scp/cat > fileover SSH to the path listed in that host'sREADME.md). diffthe deployed copy against the repo copy — they must match byte-for-byte before moving on.- Test the actual behavior live (run the script, hit the endpoint, reboot if it's boot-triggered) — not just "it deployed without error."
- Only then update
README.md/TODO.mdto 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.shonalpha— 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 viasanity-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
digagainst the authoritative server, a real test email round-trip via/var/log/mail.log, a realcurlagainst 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) andhome.jayfield.org(private home-LAN namespace,vlda-01and friends) are deliberately separate — seeREADME.md's "Domain namespace" section.- Gitea (
git.jayfield.org) is the canonical git host for this repo itself (migrated fromvlda-01, which keeps a cold, stopped backup copy). There is no GitHub remote. - SSH access details (ports, users) belong in
README.md/hostREADME.mds, not duplicated here — check there rather than assuming.