host: add supervisor-gated clean core.restart RPC

Return success before scheduling the existing shutdown path, coalesce pending
requests, and mark #loop invocations with SAUSAGE_LOOP=1. Keep core.exit
compatibility and document explicit per-call approval for all agents.

Card: SAU-134
This commit is contained in:
SAU-134 implementer
2026-10-26 09:16:10 +00:00
parent 895be183c9
commit 8c46d9b9d0
10 changed files with 257 additions and 9 deletions
+31 -1
View File
@@ -322,7 +322,37 @@ At start, the core starts each service in `/config/boot.yaml`, in order:
| `core.activate` | `plugin` and `wasm`, or `plugins: [{plugin, wasm}]`. `force`? | the new `rpc.discover` documents |
| `core.revert` | `plugin`, `force`? | like `core.activate` |
| `core.log` | filters (section 7) | journal entries |
| `core.exit` | none | `{}`. Then the host stops with exit code 75. |
| `core.restart` | none | `{}`. With `SAUSAGE_LOOP=1`, schedule clean shutdown and exit code 75. |
| `core.exit` | none | `{}`. Legacy ungated shutdown with exit code 75; prefer `core.restart`. |
### Host restart
**HARD RULE: the mediator and all agents NEVER call `core.restart` without
the person's explicit approval each time.** The same approval rule applies
to the legacy `core.exit`; it is not a workaround for the restart gate.
`core.restart {}` is enabled only when the host starts with `SAUSAGE_LOOP=1`.
The host captures the marker at startup. An absent marker (or any value other
than `1`) returns error -32600: `restart needs the loop supervisor
(SAUSAGE_LOOP not set); start with SAUSAGE_LOOP=1`, without scheduling an exit.
This is a supervisor marker, not a permission system.
The method returns `{}` immediately and schedules the shutdown request after
250 milliseconds to let the response reach the caller before stdin closes.
Repeated requests while a restart is pending return `{}` without scheduling
another restart or extending that delay. The executable then uses the same
`Host::stop` path as a normal stop: reject new processes, stop all processes
(including detached ones) in parallel with the normal five-second grace,
wait for outstanding file operations, release locks, and drain journal writes.
Only after that does the executable return the dedicated restart exit code
**75**. The library signals this path; it never kills its embedding process.
`nix run .#loop -- <world>` sets `SAUSAGE_LOOP=1` on the host invocation and
starts the newest host again after exit code 75 (and, as before, other nonzero
exits). Exit code 0 ends the loop. If using an older loop script, the person
must set `SAUSAGE_LOOP=1` in that supervisor's environment; do not set it for
an unsupervised host. The NixEnv service invokes this flake's `#loop`, so a
new loop script supplies the marker without changes to NixEnv.
Everything else is a file.