Expand description
Signal-driven hot reload of the catalog/policy generation (basil-y3e).
reload_generation is the single, fail-closed reload engine shared by the
SIGHUP handler and (later) the permission-scoped gRPC admin-reload follow-on
(basil-atq). It re-reads the catalog/policy from the same on-disk paths
the broker was started with (never from the wire), runs the full
startup/check validation on the candidate, enforces that only reloadable
dimensions changed, and (only on success) atomically swaps in a new
Generation with a bumped id. On any failure it does not swap: the
previous generation keeps serving and the rejection is returned to the caller
(the SIGHUP handler audits it). It never panics or exits.
§Reloadable vs restart-only
The reloadable surface is the content the Pdp and
the audit trail consume: the entire policy (rules / roles / name + membership
tables) and the per-key authorization attributes: writable, labels,
description, missing. The routing shape is restart-only:
the BackendManager and the live backend
instances were built from the sealed bundle at startup, so adding/removing a
backend, or changing any key’s class/backend/path/engine/key_type/
public_path, needs a re-unlock and is rejected here (the Nix module routes
such edits to ExecStart, i.e. a restart). [routing_shape] captures exactly
the dimensions baked into the manager; a candidate whose shape differs from the
running generation is rejected with ReloadError::RoutingShapeChanged.
§Non-mutating
Reload is non-mutating: it validates (and the loader’s guardrails run) but
it performs no backend I/O and no CSPRNG side effects: it never
reconciles or generates missing material on the signal path. A candidate that
adds a missing:error key whose material is absent is accepted (its routing
shape is unchanged by construction, since a new key would change the shape and
be rejected anyway); a missing:error key that already exists in both
generations simply keeps failing closed at use if its material is absent. The
routing-shape guard means a reload can only ever change a pre-existing key’s
authorization attributes, never introduce a new key/backend that would demand
fresh material, so there is no missing-material decision to make on the signal
path beyond what startup reconcile already settled.
Structs§
- Reload
Inputs - The on-disk inputs a
reload_generationre-reads: the configured catalog and policy JSON paths the broker was started with. - Reload
Outcome - The result of a successful
reload_generation.
Enums§
- Reload
Error - Why a
reload_generationwas rejected. On any of these the previous generation keeps serving (fail closed); none of them swap.
Functions§
- check_
reload - Validate the candidate catalog/policy without swapping (the
--checkdry-run, basil-atq). - reload_
generation - Re-read the configured catalog/policy, validate the candidate, enforce that
only reloadable dimensions changed, and on success atomically swap in a new
Generationwith a bumped id.