Panama learns what a server is: from a root login to running containers

A machine's role is now the interview's first question and the one answer
Panama records. Servers get the same shell minus the screen: core packages,
nvm, Bun, Claude Code and Codex (desktops get Codex too), linger, rootless
ports from 80, firewalld, the nginx-bridge network, and a nightly image
updater that replaced watchtower for cause.

server/containers/ carries junior's 23 compose services -- secrets moved to
per-machine .env files that never enter this public repo, every transformed
compose proven to render byte-identical to what is live. 'panama server'
enables, disables and relinks them; nothing here restarts a running service.
'boot --server' walks a fresh VPS from its root login to a normal install.

Five new contracts pin the secrets rule, the catalog's shape, panama-server's
behavior, the role plumbing, and the dotfile classification.

Claude-Session: https://claude.ai/code/session_01NU5JGiN3JfzqrLQB6wmJ1E
This commit is contained in:
Gabriel Brown
2026-08-25 23:11:49 -04:00
parent 9b338608ef
commit f33da41cc6
93 changed files with 4735 additions and 247 deletions
+83
View File
@@ -0,0 +1,83 @@
#!/usr/bin/env bash
# This repository is public, and server/ describes real infrastructure. The
# rule that makes that safe has three parts, and each is pinned here because
# each fails silently:
#
# 1. No tracked file under server/ carries a secret. Compose files reference
# secrets as ${VAR} interpolations resolved from the .env beside them on
# the machine; .env.example names the variables with CHANGE_ME in place
# of every value that matters.
# 2. .gitignore keeps .env and data/ out of server/containers/ even when a
# cutover or a mistake puts one there. The live files belong in
# ~/Server/<Name>/, outside the checkout entirely -- the ignore is a
# seatbelt, and a seatbelt that got deleted should fail loudly.
# 3. Nothing named .env is tracked, full stop.
set -uo pipefail
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
server_dir="$repo_dir/server"
findings=()
note() { findings+=("$1"); }
[[ -d "$server_dir" ]] || { printf 'compose secrets contract: no server/ directory\n' >&2; exit 1; }
# ── 1. Tracked content is clean ──────────────────────────────────────────────
#
# Only tracked files: the live .env a cutover briefly leaves in a service
# directory is exactly what the gitignore exists for, and flagging it here
# would punish the ignore for working.
while IFS= read -r file; do
path="$repo_dir/$file"
[[ -f "$path" ]] || continue
# A secret-bearing key with a literal value. ${VAR} interpolations, empty
# values, the CHANGE_ME placeholder, and booleans (ALLOW_EMPTY_PASSWORD=yes
# is a switch, not a credential) are the allowed shapes; anything else
# after PASSWORD/SECRET/TOKEN/KEY is treated as a leak. Keys that merely
# configure where a secret lives (a *_FILE path, a key NAME) are not
# values.
while IFS= read -r hit; do
note "$file looks like it carries a secret: ${hit%%[=:]*}"
done < <(grep -inE '(password|secret|token|api_key|private_key|access_key)[a-z0-9_]*[[:space:]]*[:=]' "$path" 2>/dev/null \
| grep -vE '[:=][[:space:]]*["'"'"']?(\$\{|CHANGE_ME|(true|false|yes|no|[01])["'"'"']?[[:space:]]*$|["'"'"']?[[:space:]]*$)' \
| grep -viE '(_file|_path|_name|_key_name)[[:space:]]*[:=]' \
| grep -vE '^[0-9]+:[[:space:]]*#')
if grep -qE 'BEGIN [A-Z ]*PRIVATE KEY' "$path" 2>/dev/null; then
note "$file contains a private key"
fi
if grep -qE 'sk-ant-[A-Za-z0-9]|ghp_[A-Za-z0-9]{20}|xox[baprs]-[A-Za-z0-9]' "$path" 2>/dev/null; then
note "$file contains something that looks like an API token"
fi
done < <(git -C "$repo_dir" ls-files 'server/')
# ── 2. The ignore still stands ───────────────────────────────────────────────
#
# check-ignore consults the real gitignore for a path that need not exist, so
# this asserts the rule rather than the current absence of violations.
git -C "$repo_dir" check-ignore -q 'server/containers/AnyService/.env' \
|| note '.gitignore no longer covers .env under server/containers/'
git -C "$repo_dir" check-ignore -q 'server/containers/AnyService/data/dump.sql' \
|| note '.gitignore no longer covers data/ under server/containers/'
# ── 3. No .env is tracked ────────────────────────────────────────────────────
while IFS= read -r tracked; do
note "a live .env is tracked: $tracked"
done < <(git -C "$repo_dir" ls-files 'server/**/.env' 'server/.env')
# ── Report ───────────────────────────────────────────────────────────────────
if (( ${#findings[@]} > 0 )); then
mapfile -t findings < <(printf '%s\n' "${findings[@]}" | sort -u)
printf 'compose secrets contract: %d finding(s)\n' "${#findings[@]}" >&2
printf ' - %s\n' "${findings[@]}" >&2
exit 1
fi
printf 'compose secrets contract: PASS\n'
+76
View File
@@ -0,0 +1,76 @@
#!/usr/bin/env bash
# Every service in the catalog has the same shape, because panama-server and
# the update timer both depend on it blindly:
#
# * a compose.yml, which is the only file enable symlinks into ~/Server
# * exactly one podman-*.service unit -- its filename is the unit's
# identity, so two would be an ambiguity and zero an unenableable service
# * a WorkingDirectory of %h/Server/<Name>, matching the directory enable
# creates -- a unit pointing anywhere else starts compose against a
# directory that has no .env and no data
# * every ${VAR} the compose interpolates without a default is named in
# .env.example, or the first enable renders a compose full of empty
# strings and the service runs misconfigured rather than failing
set -uo pipefail
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
containers_dir="$repo_dir/server/containers"
findings=()
note() { findings+=("$1"); }
[[ -d "$containers_dir" ]] || { printf 'containers shape contract: PASS (no services yet)\n'; exit 0; }
shopt -s nullglob
for dir in "$containers_dir"/*/; do
name="$(basename "$dir")"
[[ -f "$dir/compose.yml" ]] || note "$name has no compose.yml"
units=("$dir"/*.service)
if (( ${#units[@]} == 0 )); then
note "$name has no unit file"
continue
elif (( ${#units[@]} > 1 )); then
note "$name has ${#units[@]} unit files; its identity is ambiguous"
continue
fi
unit="${units[0]}"
case "$(basename "$unit")" in
podman-*.service) ;;
*) note "$name's unit is not named podman-<name>.service: $(basename "$unit")" ;;
esac
grep -q "^WorkingDirectory=%h/Server/$name\$" "$unit" \
|| note "$name's unit does not work in %h/Server/$name"
grep -q 'podman compose' "$unit" \
|| note "$name's unit does not run podman compose"
# ${VAR} without a :- default has nowhere to come from but the .env, and
# the .env is seeded from .env.example -- so a variable the example does
# not name is one the first enable silently renders empty.
[[ -f "$dir/compose.yml" ]] || continue
while IFS= read -r var; do
[[ -n "$var" ]] || continue
if [[ ! -f "$dir/.env.example" ]]; then
note "$name interpolates \${$var} but has no .env.example"
continue
fi
grep -qE "^${var}=" "$dir/.env.example" \
|| note "$name interpolates \${$var}, which .env.example does not name"
done < <(grep -vE '^[[:space:]]*#' "$dir/compose.yml" 2>/dev/null \
| grep -oE '\$\{[A-Za-z_][A-Za-z0-9_]*\}' \
| sed 's/^\${//; s/}$//' | sort -u)
done
if (( ${#findings[@]} > 0 )); then
mapfile -t findings < <(printf '%s\n' "${findings[@]}" | sort -u)
printf 'containers shape contract: %d finding(s)\n' "${#findings[@]}" >&2
printf ' - %s\n' "${findings[@]}" >&2
exit 1
fi
printf 'containers shape contract: PASS\n'
+160
View File
@@ -0,0 +1,160 @@
#!/usr/bin/env bash
# panama-server against a throwaway HOME, a fixture catalog, and a stubbed
# systemctl -- the behaviors that protect a machine's live services:
#
# * enable seeds .env from .env.example and REFUSES to start while a
# CHANGE_ME placeholder survives -- a service brought up with the
# placeholder as its database password runs, wrongly, forever
# * a completed enable links exactly the tracked files (compose.yml and the
# unit), reloads systemd, and enables the unit
# * disable removes the unit and nothing else: the .env and data stay
# * relink refreshes links for enabled services only, and names a service
# whose definition changed since the machine last looked -- without
# restarting anything
set -uo pipefail
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
panama_server="$repo_dir/bin/panama-server"
findings=()
note() { findings+=("$1"); }
[[ -x "$panama_server" ]] || { printf 'panama-server contract: %s is not executable\n' "$panama_server" >&2; exit 1; }
work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT
home="$work/home"
catalog="$work/catalog"
stub_dir="$work/bin"
calls="$work/calls"
mkdir -p "$home" "$stub_dir" "$catalog/Example"
: >"$calls"
cat >"$catalog/Example/compose.yml" <<'YML'
services:
example:
image: docker.io/library/nginx:latest
environment:
DB_PASSWORD: ${DB_PASSWORD}
YML
cat >"$catalog/Example/.env.example" <<'ENV'
DB_PASSWORD=CHANGE_ME
ENV
cat >"$catalog/Example/podman-example.service" <<'UNIT'
[Unit]
Description=Podman Compose: Example
[Service]
Type=oneshot
WorkingDirectory=%h/Server/Example
ExecStart=/usr/bin/podman compose up -d
ExecStop=/usr/bin/podman compose down
RemainAfterExit=yes
[Install]
WantedBy=default.target
UNIT
# The stub records every invocation; is-active answers 'inactive' so nothing
# here depends on a real systemd.
cat >"$stub_dir/systemctl" <<STUB
#!/usr/bin/env bash
printf 'systemctl %s\n' "\$*" >>"$calls"
case "\$*" in
*is-active*) echo inactive; exit 3 ;;
*is-enabled*) exit 1 ;;
esac
exit 0
STUB
cat >"$stub_dir/podman" <<STUB
#!/usr/bin/env bash
printf 'podman %s\n' "\$*" >>"$calls"
exit 0
STUB
chmod +x "$stub_dir/systemctl" "$stub_dir/podman"
run() {
HOME="$home" XDG_CONFIG_HOME="$home/.config" XDG_STATE_HOME="$home/.local/state" \
PANAMA_SERVER_DIR="$catalog" PANAMA_SERVER_TARGET="$home/Server" \
PATH="$stub_dir:$PATH" "$panama_server" "$@"
}
unit_link="$home/.config/systemd/user/podman-example.service"
# ── enable stops on placeholders ─────────────────────────────────────────────
run enable Example >/dev/null 2>&1 \
&& note 'enable exited zero with CHANGE_ME still in the .env'
[[ -f "$home/Server/Example/.env" ]] \
|| note 'enable did not seed .env from .env.example'
[[ -L "$home/Server/Example/compose.yml" ]] \
|| note 'enable did not link compose.yml into ~/Server'
[[ -e "$unit_link" ]] \
&& note 'enable installed the unit despite the placeholder refusal'
grep -q 'systemctl .*enable' "$calls" \
&& note 'enable reached systemctl despite the placeholder refusal'
# ── a filled .env enables ────────────────────────────────────────────────────
printf 'DB_PASSWORD=s3cret\n' >"$home/Server/Example/.env"
: >"$calls"
run enable Example >/dev/null 2>&1 \
|| note 'enable failed with a completed .env'
[[ -L "$unit_link" ]] \
|| note 'enable did not link the unit into the systemd user directory'
grep -q 'systemctl --user daemon-reload' "$calls" \
|| note 'enable did not daemon-reload'
grep -q 'systemctl --user enable --now podman-example.service' "$calls" \
|| note 'enable did not enable --now the unit'
[[ "$(cat "$home/Server/Example/.env")" == "DB_PASSWORD=s3cret" ]] \
|| note 'enable rewrote an .env that already existed'
# ── relink names a changed definition, restarts nothing ──────────────────────
printf ' # a changed line\n' >>"$catalog/Example/compose.yml"
: >"$calls"
relink_out="$(run relink 2>&1)" || note 'relink failed'
grep -q 'Example' <<<"$relink_out" \
|| note 'relink did not name the service whose definition changed'
grep -q 'restart' "$calls" \
&& note 'relink restarted something; the restart is deliberately manual'
# A second relink with nothing new must not cry wolf.
relink_out="$(run relink 2>&1)" || note 'a repeat relink failed'
grep -q 'restart podman-example' <<<"$relink_out" \
&& note 'relink reports the same change twice'
# ── disable removes the unit and only the unit ───────────────────────────────
mkdir -p "$home/Server/Example/data"
printf 'precious\n' >"$home/Server/Example/data/keep"
: >"$calls"
run disable Example >/dev/null 2>&1 || note 'disable failed'
[[ -e "$unit_link" ]] \
&& note 'disable left the unit installed'
grep -q 'systemctl --user disable --now podman-example.service' "$calls" \
|| note 'disable did not stop the unit'
[[ -f "$home/Server/Example/.env" ]] \
|| note 'disable removed the .env'
[[ -f "$home/Server/Example/data/keep" ]] \
|| note 'disable removed data'
# ── an unknown service is a real answer ──────────────────────────────────────
run enable NoSuchThing >/dev/null 2>&1 \
&& note 'enabling an unknown service exited zero'
# ── Report ───────────────────────────────────────────────────────────────────
if (( ${#findings[@]} > 0 )); then
mapfile -t findings < <(printf '%s\n' "${findings[@]}" | sort -u)
printf 'panama-server contract: %d finding(s)\n' "${#findings[@]}" >&2
printf ' - %s\n' "${findings[@]}" >&2
exit 1
fi
printf 'panama-server contract: PASS\n'