Aspects
An aspect is one feature. It is active at every scope where its at predicate holds.
Each other key gives one element to the sink of that name. The element is a value, or a function
that asks for args (see Args).
First the fleet that the aspects below act on. Hosts have users:
# Hosts with users. A host collects `packages`; a user has no such sink, so its packages go up to its host.{ yen, ... }:{ yen.kinds.fleet = { spawns.host = _: yen.host; sinks.out.reduce = yen.lib.reduce.merge; }; yen.kinds.host = { spawns.user = yen.lib.pointing "hosts" yen.user; sinks.packages.out = # (1) { host, packages, ... }: { packages.${host.name} = packages; }; }; yen.kinds.user.fields.hosts = yen.lib.listOf yen.kinds.host;
yen.fleet.main = { }; yen.flake = { out, ... }: out; yen.host.web = { }; yen.host.db = { }; yen.user.alice.hosts = [ yen.host.web ]; yen.user.bob.hosts = [ yen.host.web yen.host.db ];}(1) is the packages sink of host: hosts collect packages. Users live under hosts and have no packages sink,
so the packages of a user go up to its host.
One aspect, many files
Section titled “One aspect, many files”at from several files is a union. The keys of every file apply wherever the aspect is active.
The aspect nginx has one file for each host:
# One aspect, two files. `at` is the union of both files.{ yen, ... }:{ yen.aspects.nginx = { at = yen.host.web; # (1) packages = "nginx"; };}{ yen, ... }:{ yen.aspects.nginx = { at = yen.host.db; # (2) packages = { host, ... }: "nginx-conf-${host.name}"; # a function under the same key: one more element };}(1) in web.nix puts nginx at the host web. (2) in db.nix puts it at the host db.
nginx is active at both hosts, never at the users. Read it with result.active.nginx:
{ "fleet:main" = false; "fleet:main/host:db" = true; "fleet:main/host:db/user:bob" = false; "fleet:main/host:web" = true; "fleet:main/host:web/user:alice" = false; "fleet:main/host:web/user:bob" = false;}Aspects a kind or an instance owns
Section titled “Aspects a kind or an instance owns”A kind can own aspects, as it owns nested kinds: yen.kinds.host.aspects.ssh = { … };. By default the aspect
runs on every scope of its kind, the way a nested kind is spawned by default; an explicit at replaces that
default. A nested kind owns its own: yen.kinds.host.kinds.user.aspects.shell.
A self-contained part, such as a kit, keeps its aspects in its own instance:
yen.kit.disko.aspects.default = { at = …; … };. The real kit is
lib/kits/disko.nix.
An owned aspect is named by its declaration path, as a nested kind is: [ "kinds" "host" "aspects" "ssh" ],
[ "kit" "disko" "aspects" "default" ]. Its ref follows the same path: yen.kinds.host.aspects.ssh,
yen.kit.disko.aspects.default. An aspect that yen.aspects names cannot start with [ (the error
reserved), so no aspect of the fleet can take the name of an owned one.
Static content lands once
Section titled “Static content lands once”Static content counts once per receiver. Content that names the scope lands once per scope. These two aspects show both:
{ yen, ... }:{ # active at hosts and at users; static content lands once at each receiver yen.aspects.zsh = { # (1) at = [ yen.kinds.host yen.kinds.user ]; packages = "zsh"; # (2) }; # content that names the scope lands once per scope yen.aspects.dotfiles = { at = yen.kinds.user; packages = { user, ... }: "dotfiles-${user.name}"; # (3) };}(1) makes zsh active at hosts and at users. (2) is static: zsh is active at web and at its two users,
and web gets one zsh. (3) names the scope ({ user, ... }:): each user gives its own dotfiles.
{ db = [ "nginx-conf-db" "nginx" "zsh" "dotfiles-bob" ]; web = [ "nginx-conf-web" "nginx" "zsh" "dotfiles-alice" "dotfiles-bob" ];}