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
+35
View File
@@ -0,0 +1,35 @@
# Which kind of machine this is: 'desktop' or 'server'. Sourced, not run.
#
# The role is the one interview answer Panama keeps. Everything else is
# transient by design because it is personal -- a name, an email -- and
# re-asking costs less than a state file that drifts. The role is neither
# personal nor re-askable: `panama update` runs with no interview at all, and
# a server updated as a desktop would acquire Hyprland, so the answer has to
# outlive the run that gave it.
#
# Precedence: an exported PANAMA_ROLE (a fresh install, where the interview
# just asked) beats the recorded file (every later run), which beats the
# default. The default is desktop because every machine that predates roles
# is one.
PANAMA_ROLE_FILE="${XDG_STATE_HOME:-$HOME/.local/state}/panama/role"
panama_role() {
local role="${PANAMA_ROLE:-}"
if [[ -z "$role" && -r "$PANAMA_ROLE_FILE" ]]; then
role="$(<"$PANAMA_ROLE_FILE")"
fi
case "$role" in
server) printf 'server' ;;
*) printf 'desktop' ;;
esac
}
# Anything but 'server' records 'desktop' rather than erroring: the recorded
# file must never hold a value panama_role would refuse to read back.
panama_role_record() {
local role="$1"
[[ "$role" == server ]] || role=desktop
mkdir -p "$(dirname "$PANAMA_ROLE_FILE")"
printf '%s\n' "$role" >"$PANAMA_ROLE_FILE"
}