Skip to content

Select API

The select context is available in rule functions, class functions, and synth. It provides filtered views of the DOM relative to the current node.

In a rule function:

{
is = nest.host;
nixos = { select, host, ... }:
let webs = select.siblings nest.web;
in { services.haproxy.backends = map (w: w.addr) webs; };
}

In a class function:

nest.trait.host.class.nixos =
select: cfg:
nixpkgs.lib.nixosSystem {
system = select.node.system;
modules = [ cfg ];
};

In a synth:

nest.trait.host.synth = select: {
node.userCount = builtins.length (select.children nest.user);
};

The current node attrset. Contains all inherited and own attributes, plus name, __path, __parentPath, and any synth-derived attrs:

select.node.name # "web-1"
select.node.system # "x86_64-linux"
select.node.env # "prod" (inherited from namespace)
select.node.__path # "prod.web-1"
select.node.__parentPath # null (or parent node's __path)

The direct parent DOM node, or null if this is a root node. (Namespace containers are not parent nodes — parentNode is the nearest ancestor with is.)

select.parentNode # the parent node attrset, or null
select.parentNode.name # parent's name
select.parentNode.system # parent's system attr

Calling select sel filters all nodes in the DOM by selector sel. Returns a list of matching nodes:

select nest.web # all web-trait nodes
select nest.admin # all admin-trait nodes (including marker-only)
select (nest.attrs { env = "prod"; }) # all prod nodes

This is the primary way to find nodes across the entire DOM from a rule.

Filter the siblings of the current node — nodes that share the same __parentPath. Excludes the current node:

select.siblings nest.web # sibling web nodes
select.siblings nest.host # all sibling hosts

Sibling scoping is determined by __parentPath. Nodes under the same namespace (e.g., prod) share __parentPath = null and are siblings of each other across sub-namespaces.

Filter the direct children of the current node — nodes whose __parentPath equals this node’s __path:

select.children nest.user # direct user children
select.children nest.host # direct host children

In rule functions, select.children uses contextCache (pre-rule-synth). In class functions (processNode), it uses finalAnnotated and includes rule-synth children.

Filter the direct parent (0 or 1 results) by selector:

select.parent nest.host # [ parentHost ] or []
select.parent "#cluster" # parent named cluster, or []

Filter all ancestors up to the root by selector:

select.parents nest.host # all ancestor host nodes
select.parents nest.cluster # all ancestor cluster nodes

Filter all descendants of DOM node nd by selector:

select.within hostNode nest.user # all users under hostNode
select.within clusterNode nest.web # all web nodes under cluster

Unlike select.siblings (which is relative to the current node), select.within takes an explicit DOM node as its scope.

Rule functions access select directly. If you need raw context data for advanced use:

{
is = nest.host;
nixos = { select, ... }:
let
ctx = select; # select IS the context (has __functor)
children = ctx.children; # list of direct child nodes (unfiltered)
ancestors = ctx.ancestors; # list of ancestors (unfiltered)
allNodes = ctx.allNodes; # all nodes in DOM
in ;
}

select.children (without a selector arg) is the raw children list. select.children nest.user applies the filter.

{
is = nest.lb;
nixos = { select, ... }:
let webs = select.siblings nest.web;
in {
services.haproxy.backends =
map (w: "${w.addr}:${toString w.httpPort}") webs;
};
}
{
is = nest.host;
nixos = { select, ... }:
let peers = select.siblings nest.host;
in {
networking.extraHosts =
lib.concatMapStringsSep "\n" (p: "${p.addr} ${p.name}") peers;
};
}
{
is = nest.user;
user = { select, user, ... }:
let parentHost = builtins.head (select.parent nest.host);
in { parentSystem = parentHost.system; };
}
nest.trait.host.synth = select: {
node.userCount = builtins.length (select.children nest.user);
};
Contribute Community Sponsor