Change programs
Writing Nix is a poor interface for an agent: it can write anything, and a typo is a parse error.
yen.lib.program writes a change as data. Four ops cover a fleet change:
| Op | Effect |
|---|---|
{ op = "set"; path; value; } | one declaration path gets a value |
{ op = "ref"; path; kind; name; } | one declaration path gets an instance ref |
{ op = "place"; aspect; kind; name; } | the aspect is also active at that instance |
{ op = "new"; kind; name; fields; } | a new instance; a field { ref = { kind; name; }; } is a ref |
[ { "op": "new", "kind": "host", "name": "cache", "fields": { "addr": "10.0.0.3", "form": "server" } }, { "op": "place", "aspect": "postgres", "kind": "host", "name": "cache" }]# Validate and stage a change program. Nothing is applied.# nix eval --impure --json --file dry-run.nix --argstr program programs/new-cache.json{ program, 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" ),}:(import yen).lib.program.dryRun { base = { imports = [ (import-tree ./modules) ]; _module.args = { lib = import "${nixpkgs}/lib"; }; }; prog = builtins.fromJSON (builtins.readFile (./. + "/${program}"));}{ diff = { active = { added = [ "postgres@fleet:main/host:cache" ]; removed = [ ]; }; edges = { added = [ { from = "fleet:main/host:cache"; origin = "aspect postgres"; sink = "services"; to = "fleet:main/host:cache"; } ]; removed = [ ]; }; scopes = { added = [ "fleet:main/host:cache" ]; removed = [ ]; }; }; errors = [ ]; ok = true; refused = [ ]; sha = "605593a3464e48fec6c8f1d9a44ac93798c9e8e2dda68e9d2dba5702dfd623eb"; valid = true; violations = [ ];}Checked against the schema
Section titled “Checked against the schema”validate r prog checks each op before anything runs: kinds, fields and aspects must exist, with near for typos.
dryRun validates first, and stages only a valid program:
[ { "op": "set", "path": [ "host", "web", "adr" ], "value": "10.0.0.9" }, { "op": "place", "aspect": "postgress", "kind": "host", "name": "web" }]{ errors = [ { class = "fixable"; code = "program"; error = "kind host has no field adr"; from = "program[0]"; near = [ "addr" ]; path = [ ]; } { class = "fixable"; code = "program"; error = "no aspect postgress"; from = "program[1]"; near = [ "postgres" ]; path = [ ]; } ]; ok = false; valid = false;}| Function | Use |
|---|---|
validate r prog | error records, [ ] when the program is valid |
toModule prog | the program as a yen module, to import |
dryRun { base; prog; grant; rejected; } | validate, then stage |
render prog | JSON, to store or send |