diff --git a/docker/stage2-hook.sh b/docker/stage2-hook.sh index 5844d4ff41..a07b8eec31 100755 --- a/docker/stage2-hook.sh +++ b/docker/stage2-hook.sh @@ -476,14 +476,21 @@ fi # (gateway/scale_to_zero.py suspend_self) must POST to it, but the gateway runs # as the unprivileged `hermes` user — without this it gets EACCES on every # suspend attempt and the machine can never sleep (fail-awake; verified live on -# staging 2026-08-20: "flaps suspend request failed: [Errno 13]"). stage2 runs -# as root before the supervision tree starts, so grant group access here. -# Group-write is the minimal widening: the socket stays root-owned and -# non-hermes users gain nothing. No-op off Fly (socket absent). +# staging 2026-08-20: "flaps suspend request failed: [Errno 13]"). This hook +# runs as root before user services (the gateway) start, so grant group access +# here. Scope note: group-write exposes the WHOLE local Machines API to the +# hermes group (any group member could e.g. stop/suspend this machine), not +# just the suspend endpoint — accepted because the agent already executes +# arbitrary user code as that same principal and the socket only controls THIS +# machine. No-op off Fly (socket absent). if [ -S /.fly/api ]; then - chgrp hermes /.fly/api 2>/dev/null || true - chmod g+w /.fly/api 2>/dev/null || true - echo "[stage2] Granted hermes group access to the Fly Machines API socket" + if refuse_symlinked_path "chgrp/chmod" /.fly/api; then + : + elif chgrp hermes /.fly/api 2>/dev/null && chmod g+w /.fly/api 2>/dev/null; then + echo "[stage2] Granted hermes group access to the Fly Machines API socket" + else + echo "[stage2] Warning: could not grant group access to /.fly/api — scale-to-zero self-suspend will fail EACCES (fail-awake)" + fi fi # --- Migrate persisted config schema ---