- Module = atom. One package or service. Lives in
modules/<name>/default.bnix(with a regenerateddefault.nixsibling). - Tags = composition. A module joins a tag via
:tags(default-on) or:tags-opt-in(opt-in) in its.bnix. Hosts declare a tag selection; the resolver unions per-tag memberships and subtracts a per-host disabled list. See TAGS.md. - Host = leaf.
hosts/<host>/configuration.bnixsets options;hosts/<host>/enabled-tags.bnixpicks the tag set.
firn rebuild provisionally refreshes local harness inputs, then runs
firn repo build → firn repo validate → host-closure build → verified lock commit →
system switch → tag. Modules auto-discover via the flake's dynamic imports — adding a
module means creating the directory + .bnix, running firn repo build,
and git add-ing both files. No flake edits.
.
├── flake.bnix source-of-truth flake (#lang beagle/nix)
├── flake.nix generated
├── modules/ hosts/ .bnix source (+ generated .nix siblings)
├── scripts/ retained Beagle build-time repository builder
├── native/ typed Firn commands, policies, and workflow controllers
├── template/ starting point for `nix flake init -t`
├── dotfiles/ secrets/ assets/
├── docs/ TAGS.md — composition model
└── tests/ validator regression fixtures (.bnix)
Both .bnix and .nix are committed because the flake reads from the
git tree. Edit the .bnix — firn repo build overwrites direct .nix
edits.
North-v2 is promoted independently and reached through ~/.local/bin/north;
Firn owns only the stable launcher, guard wiring, and delivery-liveness entry.