Skip to content

Queries

(import yen).lib.eval module gives a set of queries. Each one is lazy: you pay only for what you read. Every answer is plain data, so builtins.toJSON prints it. This small fleet answers each query below.

modules/fleet.nix
{ yen, lib, ... }:
{
yen.kinds.fleet = {
spawns.host = _: yen.host;
sinks.out.reduce = yen.lib.reduce.merge;
};
yen.kinds.host = {
fields.addr = lib.types.str;
spawns.user = s: lib.filterAttrs (_: u: yen.lib.same u.on s) yen.user;
sinks.packages = {
reduce = yen.lib.reduce.set;
out =
{ host, packages, ... }:
{
packages.${host.name} = packages;
};
};
};
yen.kinds.user.fields.on = yen.kinds.host;
yen.fleet.main = { };
yen.host.web.addr = "10.0.0.1";
yen.user.ana.on = yen.host.web;
yen.aspects.git = {
at = [
yen.kinds.host
yen.kinds.user
];
packages = "git";
};
yen.aspects.nginx = {
at = yen.host.web;
packages = "nginx";
};
yen.flake = { out, ... }: out;
}
QueryAnswer
errors[ { error; from; code; … } ], empty when the fleet is valid
graph{ scopes; edges; }: every scope and every routed element, with its origin
scopesthe scopes in order
activeaspect, then scope id, then true or false
undefinedaspects that at cannot decide (a cycle through not)
reducedsink, then receiver id, then the value
flakethe answer of yen.flake
dataevery instance’s declared fields
schemaevery kind, aspect and invariant, as JSON-safe data
declaredIn paththe files that answer a declaration path
factsthe fleet as ground atoms, for Datalog
context id { }one scope: parent, children, aspects, what it gives and receives
checkthe invariants: see Invariants
result
{
out = {
"fleet:main" = {
packages = {
web = [
"git"
"nginx"
];
};
};
};
packages = {
"fleet:main/host:web" = [
"git"
"nginx"
];
};
}

r.context id { maxEdges; maxAspects; } is what an agent or a person reads first about a scope. Each list says how much it shows of how much there is:

result
{
aspects = {
items = [
"git"
"nginx"
];
truncated = {
of = 2;
shown = 2;
};
};
children = [
"fleet:main/host:web/user:ana"
];
errors = [ ];
given = {
items = [
{
from = "fleet:main/host:web";
origin = "aspect git";
sink = "packages";
to = "fleet:main/host:web";
}
{
from = "fleet:main/host:web";
origin = "aspect nginx";
sink = "packages";
to = "fleet:main/host:web";
}
{
from = "fleet:main/host:web";
origin = "sink packages";
sink = "out";
to = "fleet:main";
}
];
truncated = {
of = 3;
shown = 3;
};
};
kind = "host";
name = "web";
parent = "fleet:main";
received = {
items = [
{
from = "fleet:main/host:web";
origin = "aspect git";
sink = "packages";
to = "fleet:main/host:web";
}
{
from = "fleet:main/host:web";
origin = "aspect nginx";
sink = "packages";
to = "fleet:main/host:web";
}
{
from = "fleet:main/host:web/user:ana";
origin = "aspect git";
sink = "packages";
to = "fleet:main/host:web";
}
];
truncated = {
of = 3;
shown = 3;
};
};
scope = "fleet:main/host:web";
undefined = [ ];
}

Each edge of graph has origin: the aspect that gave the element. “Which aspects reach web” is a filter, not a registry: map (e: e.origin) (filter (e: e.to == "fleet:main/host:web") r.graph.edges).

Terminal window
nix eval --impure --file query.nix errors
nix eval --impure --json --file query.nix graph
nix eval --impure --expr '(import ./query.nix).context "fleet:main/host:web" { }'
Contribute Community Sponsor