From bebcf8a29d7fd798a36e898de3bfecf9f44bf35a Mon Sep 17 00:00:00 2001 From: Rockachopa Date: Sun, 26 Apr 2026 10:54:09 -0400 Subject: [PATCH] feat: implement thin config ephemerality and upstream pull fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Make config.yaml read-only (0444) — enforces ephemeral thin config pattern Agents cannot mutate their config at runtime. Any changes are lost on restart because config is re-deployed from immutable golden state on each boot. - Add upstream pull fallback in agent_startup.yml If git pull of timmy-config fails, restore config from deadman snapshot before proceeding. Ensures startup succeeds even when upstream is unreachable. Design rationale: - config.yaml is now ephemeral (read-only file) - Only thin_config.yml is mutable (local_overrides section), but even that is restricted by filesystem permissions (0444) — runtime overrides are in-memory only - Failure recovery: deadman snapshots act as last-known-good config source - No wizard can permanently modify config without a Gitea PR + Ansible deploy Related to #443 — Thin Config Pattern: Immutable Local Config with Upstream Pull. This addresses acceptance criteria: - Runtime config mutations are ephemeral (file is read-only) - Fallback to last-known-good if upstream pull fails Closes #443 --- ansible/playbooks/agent_startup.yml | 18 ++++++++++++++++++ ansible/roles/golden_state/tasks/main.yml | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/ansible/playbooks/agent_startup.yml b/ansible/playbooks/agent_startup.yml index 75c74962..d13a57a2 100644 --- a/ansible/playbooks/agent_startup.yml +++ b/ansible/playbooks/agent_startup.yml @@ -19,6 +19,24 @@ version: "{{ upstream_branch }}" force: true tags: [pull] + register: git_pull + ignore_errors: true + + - name: "Fallback: restore config from deadman snapshot if upstream pull failed" + shell: | + if [ ! -f "{{ wizard_home }}/config.yaml" ] || [ ! -f "{{ deadman_snapshot_dir }}/config.yaml.known_good" ]; then + echo "SKIP: config or snapshot missing" + exit 0 + fi + if [ {{ git_pull.failed | default('false') }} = true ] || [ {{ git_pull.rc | default(0) }} -ne 0 ]; then + echo "Upstream pull failed — restoring config from deadman snapshot..." + cp "{{ deadman_snapshot_dir }}/config.yaml.known_good" "{{ wizard_home }}/config.yaml" + echo "Config restored from snapshot." + else + echo "Upstream pull succeeded — no action needed." + fi + tags: [pull, fallback] + when: deadman_enabled | default(true) - name: "Deploy golden state config" include_role: diff --git a/ansible/roles/golden_state/tasks/main.yml b/ansible/roles/golden_state/tasks/main.yml index 9c69c8a9..f5cde063 100644 --- a/ansible/roles/golden_state/tasks/main.yml +++ b/ansible/roles/golden_state/tasks/main.yml @@ -15,7 +15,7 @@ template: src: "../../wizard_base/templates/wizard_config.yaml.j2" dest: "{{ wizard_home }}/config.yaml" - mode: "0644" + mode: "0444" # Read-only — ephemeral thin config backup: true notify: - "Restart hermes agent (systemd)"