Skip to content

Routing and walls

An aspect does not say who receives its elements. The tree does. An element goes to the nearest ancestor-or-self scope that has the sink, and where the sink’s at holds.

This example has a fleet, two environments, two hosts, a user and a home. First the sinks of each kind:

modules/fleet.nix
# fleet > environment > host > user and home. Who receives an element depends on where the sinks are.
{ yen, ... }:
let
inherit (yen.lib) opt;
in
{
yen.kinds.fleet = {
spawns.environment = _: yen.environment;
sinks.out.reduce = yen.lib.reduce.merge;
sinks.dns.out =
{ dns, ... }:
{
dns.fleet = dns;
}; # (1) the fallback receiver
};
yen.kinds.environment = {
fields.public = opt yen.lib.bool false;
spawns.host = yen.lib.pointing "environment" yen.host;
sinks.dns = {
at = s: s.public; # (2) a conditional receiver: only public environments
out =
{ environment, dns, ... }:
{
dns.${environment.name} = dns;
};
};
};
yen.kinds.host = {
fields.environment = yen.kinds.environment;
spawns = {
user = yen.lib.pointing "on" yen.user;
home = yen.lib.pointing "on" yen.home;
};
sinks.os.out = # (3)
{ host, os, ... }:
{
os.${host.name} = os;
};
};
yen.kinds.user.fields.on = yen.kinds.host;
yen.kinds.home = {
fields.on = yen.kinds.host;
sinks.os.out =
{ home, os, ... }:
{
os.${home.name} = os;
}; # (4) a home builds its own config
};
yen.fleet.main = { };
yen.flake = { out, ... }: out;
}

(1) is the dns sink of the fleet: the fallback receiver. (2) is the dns sink of an environment. Its at holds only where public is true. (3) is the os sink of a host. (4) is the os sink of a home. The users and homes:

modules/instances.nix
{ yen, ... }:
{
yen.environment.prod.public = true;
yen.environment.lab = { };
yen.host.web.environment = yen.environment.prod;
yen.host.box.environment = yen.environment.lab;
yen.user.alice.on = yen.host.web;
yen.home.bob.on = yen.host.web;
}

And the aspects that give the elements:

modules/aspects.nix
{ yen, ... }:
{
yen.aspects.accounts = {
at = [
yen.kinds.user
yen.kinds.home
];
os = { scope, ... }: "account ${scope.name}"; # (1)
};
yen.aspects.records = {
at = yen.kinds.host;
dns = { host, ... }: "${host.name} A"; # (2)
};
}

(1) gives an os element at every user and home. (2) gives a dns element at every host. This is what the flake gets, and the table says why each element lands where it does:

result
{
dns = {
fleet = [
"box A"
];
prod = [
"web A"
];
};
os = {
bob = [
"account bob"
];
web = [
"account alice"
];
};
packages = {
web = [
"vim for alice"
];
};
}
Element Given at Received at Why
os user alice host web a user has no os sink; the host above has one
os home bob home bob the home has its own os sink: ancestor-or-self
dns host web environment prod prod is public, so its dns sink holds
dns host box fleet main lab is not public; the next receiver is the fleet

The routes, drawn from result.graph.edges:

flowchart BT
  n6c4c773b6d["fleet main"]
  nf631382c77["environment lab"]
  nc33ee814ad["host box"]
  n4a308930d1["environment prod"]
  n4019172c9d["host web"]
  n85c31fb914["home bob"]
  n8bd47cb553["user alice"]
  nc33ee814ad -->|"dns"| n6c4c773b6d
  n4019172c9d -->|"dns"| n4a308930d1
  n8bd47cb553 -->|"os"| n4019172c9d
  n8bd47cb553 -->|"packages"| n4019172c9d
  n4a308930d1 -->|"out"| n6c4c773b6d
  n85c31fb914 -->|"out"| n6c4c773b6d
  n4019172c9d -->|"out"| n6c4c773b6d

A sink with no keys is a wall. It receives elements and gives nothing on. A home under a host can keep its packages away from the host:

modules/walls.nix
# A sink with no keys is a wall: a home's packages stop at the home and never reach the host.
{ yen, ... }:
{
yen.kinds.host.sinks.packages.out =
{ host, packages, ... }:
{
packages.${host.name} = packages;
};
yen.kinds.home.sinks.packages = { }; # (1)
yen.aspects.editor = {
at = [
yen.kinds.user
yen.kinds.home
];
packages = { scope, ... }: "vim for ${scope.name}";
};
}

(1) is the wall: the packages sink of a home has no keys. result.reduced.packages shows both receivers. Only the host gives its packages to the flake:

result
{
"fleet:main/environment:prod/host:web" = [
"vim for alice"
];
"fleet:main/environment:prod/host:web/home:bob" = [
"vim for bob"
];
}
ContributeCommunitySponsor