Stage a change
Before you apply a change, stage it. yen.lib.stage { base; change; rejected; grant; } evaluates the fleet
without and with the change. Nothing is applied. ok is true only when nothing new is wrong.
The fleet is the one from Invariants. A change is plain data, so an agent can write it as JSON:
{ "yen": { "aspects": { "desktop": { "at": [ { "kind": "host", "name": "web" } ] } } } }# Stage a change file against the fleet. Nothing is applied.# nix eval --impure --json --file stage.nix --argstr change changes/desktop-on-web.json{ change, grant ? null, # where the change may give elements: yen.lib.grant { scopes = [ … ]; } rejected ? [ ], # sha256 of changes refused before yen ? builtins.fetchTarball "https://github.com/vic/yen/archive/main.tar.gz", nixpkgs ? builtins.fetchTarball "https://nixos.org/channels/nixos-25.05/nixexprs.tar.xz", import-tree ? import ( builtins.fetchTarball "https://github.com/denful/import-tree/archive/main.tar.gz" ),}:let base = { imports = [ (import-tree ./modules) ]; _module.args = { lib = import "${nixpkgs}/lib"; }; }; s = (import yen).lib.stage { inherit base grant rejected; change = builtins.fromJSON (builtins.readFile (./. + "/${change}")); };inremoveAttrs s [ "render" ]nix eval --impure --json --file stage.nix --argstr change changes/desktop-on-web.json{ diff = { active = { added = [ "desktop@fleet:main/host:web" ]; removed = [ ]; }; edges = { added = [ ]; removed = [ ]; }; scopes = { added = [ ]; removed = [ ]; }; }; errors = [ ]; ok = false; refused = [ ]; sha = "a1969acc86001f4528de7a4ec227f9cf7a5af809fd2555b04adeed0dd6c04146"; violations = [ "bad:desktop-on-server:web" ];}The change makes desktop active at the server web, and breaks an invariant. The answer has the violation,
the diff (scopes, routed elements, active aspects) and the change’s sha.
Grants only narrow
Section titled “Grants only narrow”yen.lib.grant { scopes = [ id prefixes ]; sinks ? null; } says where a change may give elements.
narrow g h keeps what both allow. No function makes a grant wider. An element outside the grant is refused,
with class = "authority": no edit by this author can fix it.
{ "yen": { "aspects": { "postgres": { "at": [ { "kind": "host", "name": "thinkpad" } ] } } } }[ { class = "authority"; code = "outside-grant"; error = "the change gives services to fleet:main/host:thinkpad, outside the grant"; from = "fleet:main/host:thinkpad"; got = { from = "fleet:main/host:thinkpad"; origin = "aspect postgres"; sink = "services"; to = "fleet:main/host:thinkpad"; }; path = [ ]; }]Rejected before
Section titled “Rejected before”Pass the sha of changes refused before in rejected. The same change is refused again, whoever sends it:
[ { class = "authority"; code = "rejected"; error = "this change was rejected before"; from = "stage"; got = "a1969acc86001f4528de7a4ec227f9cf7a5af809fd2555b04adeed0dd6c04146"; path = [ ]; }]