Skip to content

Scopes and spawns

A scope is one instance at one place in the tree. The root kind is the kind that no kind spawns. spawns.<kind> is a function of the parent scope. It gives the instances that live under it.

modules/tree.nix
# `spawns.<kind>` gives the children of a scope: a function of the parent scope.
{ yen, lib, ... }:
let
inherit (yen.lib) same is;
in
{
yen.kinds.fleet.spawns.environment = _: yen.environment;
yen.kinds.environment.spawns.host = s: lib.filterAttrs (_: h: same h.environment s) yen.host;
yen.kinds.host = {
fields.environment = yen.kinds.environment;
spawns.user = s: lib.filterAttrs (_: u: lib.any (same s) u.hosts) yen.user;
};
yen.kinds.user.fields.hosts = is lib.isList; # refs to the hosts the user logs in to
yen.fleet.main = { };
}
modules/instances.nix
{ yen, ... }:
{
yen.environment.prod = { };
yen.environment.lab = { };
yen.host.web.environment = yen.environment.prod;
yen.host.db.environment = yen.environment.prod;
yen.host.box.environment = yen.environment.lab;
yen.user.alice.hosts = [
yen.host.web
yen.host.box
]; # one user, two scopes
yen.user.bob.hosts = [ yen.host.db ];
}

The tree comes from the data. Nothing lists the children by hand:

flowchart TD
  n6c4c773b6d["fleet main"]
  nf631382c77["environment lab"]
  nc33ee814ad["host box"]
  n86d1c002a8["user alice"]
  n4a308930d1["environment prod"]
  n4019172c9d["host web"]
  n8bd47cb553["user alice"]
  nc60807bd8b["host db"]
  n614abf7a41["user bob"]
  n6c4c773b6d --> nf631382c77
  nf631382c77 --> nc33ee814ad
  nc33ee814ad --> n86d1c002a8
  n6c4c773b6d --> n4a308930d1
  n4a308930d1 --> n4019172c9d
  n4019172c9d --> n8bd47cb553
  n4a308930d1 --> nc60807bd8b
  nc60807bd8b --> n614abf7a41

The id of a scope is its path: fleet:main/environment:prod/host:web/user:alice. One instance can live in several places. alice has two scopes, one under each of her hosts. Names need not be unique.

r.scopes gives the scopes in order:

result
[
"fleet:main"
"fleet:main/environment:lab"
"fleet:main/environment:lab/host:box"
"fleet:main/environment:lab/host:box/user:alice"
"fleet:main/environment:prod"
"fleet:main/environment:prod/host:web"
"fleet:main/environment:prod/host:web/user:alice"
"fleet:main/environment:prod/host:db"
"fleet:main/environment:prod/host:db/user:bob"
]

A parent comes before its children. Siblings sort by the order sink (default 0), then by name. web comes before db because of this aspect:

modules/order.nix
# Siblings sort by the `order` sink (default 0), then by name. A parent comes before its children.
{ yen, ... }:
{
yen.aspects.web-first = {
at = yen.host.web;
order = -1;
};
}

yen.lib.graph is readable by any module. This example gives its scope ids as a flake output:

modules/outputs.nix
# The flake gives the scope ids: `yen.lib.graph` is readable inside the fleet.
{ yen, ... }:
{
yen.kinds.fleet.sinks.out.reduce = yen.lib.reduce.merge;
yen.flake = { out, ... }: out;
yen.aspects.tree = {
at = yen.kinds.fleet;
out.scopes = map (s: s.id) yen.lib.graph.scopes;
};
}
Contribute Community Sponsor