Releases: denful/den
v0.16.0 - iDENtity
I
DENtity is brought to you by @sini
IDENtity is the codename json was going to use for his own Den if he was not to take it over.
With this release let me also introduce two new maintainers, people who have been committed to Den
- making sustained and high quality improvements, and showing willingness to keep doing so:
Welcome @sini and @DylanRJohnston.
Breaking changes:
- Now entity.aspect is a proper aspect reference, not a name.
If you have any custom forwarding classes or places that interpolated into den.aspects from entities, you now need to use entity.aspect directly. Migration path: #434
What's Changed
- feat(core): add fixedTo.{atLeast,exactly,upTo} by @DylanRJohnston in #340
- docs: add source links to examples by @theutz in #391
- docs: import astro component into mdx by @theutz in #392
- docs: actually rename to .mdx by @theutz in #393
- fix: Revert "docs: add source links to examples (#391)" by @theutz in #394
- feat: per-aspect adapter accumulation in resolve by @sini in #397
- feat: preserve aspect identity and adapter through functor evaluation by @sini in #398
- feat: structural provider provenance via meta.provider by @sini in #399
- fix: strip _ aliases from external denful to prevent include duplication by @sini in #402
- cleanup: give a name to our default composite adapter. by @vic in #403
- feat: Add adapters.traceName and re-use at tests by @vic in #404
- feat: forward and output resolution respects meta.adapter by @sini in #405
- feat: adapters.trace = (filterIncludes traceNames) by @vic in #407
- feat: tombstones, identity paths, and substitution in filterIncludes by @sini in #409
- fix: perHost/perUser/perHome preserve aspect identity by @sini in #410
- fix: excludeAspect cascades to provider sub-aspects by @sini in #411
- improve(ci): rename tags to allow-ci and allow-bench by @vic in #418
- fix: provider sub-aspect functions receive parametric context by @sini in #419
- feat: tombstones record excludedBy for provenance tracking by @sini in #420
- fix: rename excludedBy to excludedFrom on tombstones by @sini in #421
- feat(ci): Add bogus action for quick drity tests by @vic in #422
- feat(ci): Make denTest re-usable between CI and bogus templates by @vic in #424
- fix: applyDeep skips static subs that don't consume parametric ctx by @sini in #426
- docs: all contributions welcome by @theutz in #427
- docs: add "Aspect Fixed Point" section by @drupol in #430
- fix(core): make entity.aspect be real aspect reference not an string by @vic in #433
- fix(ci): test using
just cilike developers do by @vic in #435 - fix(ci): Fix failing test by @vic in #436
- fix: don't coerce factory-function aspects by @sini in #437
- feat: add
insecurebattery by @drupol in #438 - fix: parametric - preserve meta on materialized parametric results by @sini in #440
- fix: propagate context through named parametric includes by @sini in #444
- feat(repo): Add
just replthat adds den and denTest into repl by @vic in #445 - feat(core): add den.lib.strict for disabling freeform types by @DylanRJohnston in #428
- feat:
entity.hasAspect <ref>query method on context entities by @sini in #439 - feat: adapterOwner tracking in filterIncludes + collectSelfPath helper by @sini in #451
- fix: make home hostName nullable for standalone homes by @sini in #452
New Contributors
Full Changelog: v0.15.0...v0.16.0
v0.15.0
Den v0.15.0
More human than ever.
This release main feature is more humans! Not only the user base seems to be growing, our matrix channel is very active, and we also having more questions at Zulip, but now.. Den has new maintainers <3. People that are actively using Den for themselves, frequent code contributors, actively participating at our different community channels and well experienced with Nix. I'm really really grateful for their willingness to help me and all other people at making Den work for all of us. Please welcome them: @HeitorAugustoLN, @Gwenodai, @theutz. Honorary mention to @drupol who has always been influential to Den design, as well as pushing its limits and promoting it.
Breaking changes
None that I can remember. We have made refactors and cleanup rounds at internal APIs but nothing user-facing have changed.
Den flake outputs and flake-parts forwards example.
Den aspects can directly contribute to flake outputs like packages, checks, and other per-system outputs. We also have a new template example showcasing third-party flake-parts modules being used from aspects, like writing tests for your aspec
Better cross-flake namespace
Thanks to work being done by @DylanRJohnston we now have a cleaner namespace.nix, previously I had some horrible hacks trying to make aspects correctly pass over the flake frontier. As part of that work we also have a new takes.upTo combinator that hopefully be the default combinator in the future instead of take.atLeast, the reason is upTo does not need functions to have { ... }.
Aspect introspection and adapted class resolution.
We are now starting to make our own custom optimizations to the aspect types and resolution that we initially inherited from flake-aspects. We now have aspect.meta initially inspired by @drupol infra usage, this and our new resolve.withAdapter will hopefully enable other neat features by @sini later.
What's Changed
- fix:
nhinvocation for standalone homes by @theutz in #347 - feat(class): Classes for flake exposed system outputs by @vic in #346
- improve(core): Split outputs.nix into modules by @vic in #350
- feat(template): forward classes for 3rd-party flake-parts modules by @vic in #351
- feat(batteries): mutual-provider allows user-to-users provides by @vic in #354
- feat(ctx): Aspect contributions can access source and target contexts by @vic in #356
- cleanup(repo): Remove unused code by @vic in #357
- Fix HTML tags in README sponsorship section by @drupol in #358
- fix(namespaces): fix missing reflection information on imported providers from a namespace by @DylanRJohnston in #353
- fix(core): Add defaultText to some types having default values by @vic in #361
- docs: add Flake Parts Modules to documentation sidebar by @theutz in #366
- fix: don't make perSystemFwd a function by @theutz in #370
- docs: comment in flake-parts-module template about
systemsby @theutz in #374 - feat(ctx): Allow .into transformations be merged from definitions by @vic in #362
- test: check that devShell is properly created by @theutz in #375
- fix(batteries): Make wsl.enable = true for wsl hosts. by @cfrenette in #379
- fix(bug): inputs' on namespaced aspect by @vic in #377
- fix: correctly forward user class using fixedTo by @HeitorAugustoLN in #380
- fix(class): class-based parametric include coverage by @HeitorAugustoLN in #381
- feat(core): Merging take.upTo from #340 by @vic in #384
- cleanup(core): aspect types by @vic in #385
- fix(docs): Flake-parts compatible output by @vic in #387
- feat(core):
aspect.metaallow attaching freeform attributes to aspects by @vic in #376 - feat(core):
resolve.withAdapterto collect custom results from resolve or change resolution. by @vic in #389 - docs: add section about Aspects custom modules by @drupol in #388
New Contributors
- @theutz made their first contribution in #347
- @DylanRJohnston made their first contribution in #353
- @cfrenette made their first contribution in #379
Full Changelog: v0.14.0...v0.15.0
Thanks to everyone that has been using Den, reporting issues, participating at the different channels, contributing docs and code. Den is growing thanks to you :)
v0.14.0
Den v0.14.0
Den now standing on its own.
Highlights
Den is now a zero dependencies library
Migration path: #335. Den no longer depends on flake-aspects, so you can remove that input from your projects.
Versioned documentation
Each release has its own documentation snapshot accessible at https://den.oeiuwq.com/<tag>, including the
latest tag. Note that they are snapshots of the full documentation site at that release point, so the navbar is what it was at that version.
Forward class intoPath can access module args
See the example by @Damnjelly at #342
Den standalone: templates/nvf-standalone
This example is about using Den aspects to configure things without NixOS / nix-Darwin OS pipeline.
What's Changed
- feat(ci): Add benchmark to PRs by @vic in #316
- feat(homes): Support standalone HM without existing host by @vic in #318
- feat(core): better error message on non-mergable flake outputs by @vic in #319
- feat(homes): Support home context on den._.mutual-provider by @vic in #320
- fix(ci): benchmark compare head against base by @vic in #321
- docs: host-specific standalone homes by @musjj in #323
- feat(template): NVF Standalone showing Den custom confs by @vic in #324
- fix(class): Test top-level forward allows multiple aspects by @vic in #325
- docs: fix broken link to nvf-standalone tutorial, typos by @kriswilk in #332
- fix(template): update default template to use mutual-providers by @vic in #337
- Revert "fix(template): update default template to use mutual-providers" by @vic in #338
- fix(template): update default template to use mutual-providers by @vic in #339
- improve(core): Remove flake-aspects dependency and code cleanup. by @vic in #335
- Fix typo in schema.mdx from 'hardedned' to 'hardened' by @drupol in #343
- feat(core): support functionArgs in IntoPath by @Damnjelly in #342
New Contributors
- @kriswilk made their first contribution in #332
- @Damnjelly made their first contribution in #342
Full Changelog: v0.13.0...v0.14.0
v0.13.0
Den v0.13.0
More Elven Shamanic Nix now with less Bifurcated Polynomial Applicatives, much better for health!.
What's Changed
- feat(batteries): Opt-in den._.bidirectional by @vic in #272
- Add hjem to noflake example by @vic in #275
- feat(batteries): mutual provider can configure generic users and hosts by @vic in #283
- feat(core): Add host fixed-point in user schema. by @vic in #284
- feat(batteries): allow mutual-provider pass ctx to generic aspect by @vic in #285
- feat(lib): den.lib.perHost and den.lib.perUser by @vic in #288
- improve(lib): split lib.nix into files by @vic in #290
- docs: clarify how to enable angle-brackets in a file by @crasm in #291
- fix(ctx): should not use hm from host when no-bidir enabled. by @vic in #293
- doc: expand on docs contribution instructions by @crasm in #295
- fix(batteries): den._.bidirectional uses fixedTo to include from host. by @vic in #298
- feat(repo): Add devShell for tooling by @vic in #299
- docs: remove absolute size from main den logo by @crasm in #301
- docs: fix console command style in contributing.md by @crasm in #302
- feat(lib): add den.lib.perHome and add documentation for context shortcuts by @HeitorAugustoLN in #303
- fix(ci): Test using nh apps for minimal and noflake. by @vic in #305
- feat(homes): OS bound standalone HM by @vic in #306
- feat(class): Allow test config values on forward classes. by @vic in #307
- feat(batteries): remove bidirectional by @vic in #308
- feat(class): Allow top-level forwarded-alias classes by @vic in #309
- fix(bug): nested plain-attrs aspects are parametric by @vic in #312
New Contributors
Full Changelog: v0.12.0...v0.13.0
Den v0.13 Release Notes
This release focuses on context-pipeline behavior clarity, class forwarding, and API ergonomics. It also removes the old bidirectional behaviour that was causing duplicate-values in favor of explicit mutual routing.
Den v0.13 depends on flake-aspects v0.7.0, no changes to flake-aspects were needed.
Highlights
- Mutual host/user routing replaces bidirectional battery
den._.bidirectionalwas first extracted from Den core into a battery and then removed.den._.mutual-provideris the supported mechanism for Host providing home-environments to all users.- New explicit routing conventions:
- host -> users:
den.aspects.<host>.provides.to-users.* - user -> hosts:
den.aspects.<user>.provides.to-hosts.* - specific peer routing still supported via
den.aspects.<user>.provides.<host>orden.aspects.<host>.provides.<user>
- host -> users:
- Forwarding classes now support top-level alias forwarding
den._.forwardnow supportsintoPath = [ ]for root forwarding.- Guarded root forwarding and adapted args are supported for alias-style classes.
- Added Examples for for guarded forwards with config-sensitive conditions like platform-specific hm classes.
- Parametric behavior fix for nested plain-attrs includes
- Nested includes are treated correctly as parametric when function args are declared.
- New context helpers in
den.lib
den.lib.perHostden.lib.perUserden.lib.perHome- These provide explicit context-scoped include intent and reduce usage of
den.lib.take.exactly({host,user})patterns in code. - They work with plain attrs or ctx-taking functions.
- Better den.lib and framework module organization
- Core lib split into
nix/lib/*modules. default.nixandflake.nixare basically the same.
Migration Notes
1) Replace bidirectional patterns
Until v0.12.0 Den had built-in Bidirectionality in its core.
What that meant was that Host aspects could contribute
home classes like homeManager to all its users. However
this was also the source of many duplicate reports, becuse
the Host aspect was being activated more than once: when the host was reading its own config and then later for each user context {host,user}.
We also had a "routing example" that @musjj then moved to what is now den.provides.mutual-provider #236, that contribution made us wonder if the built-in bidirectionality could be extracted from core #273 (we did) and then further conversations led to actually removing it #300 after realizing it was more complicated than necessary.
2) Forward alias classes can target class root
Previsouly or forward battery was not able to use intoPath = [] (forward into top-level target module).
Now it is possible, enabling other patterns like classes
that guard on reading from config values or things like pkgs platform:
den._.forward {
each = [ "Linux" "Darwin" ];
fromClass = p: "hm${p}";
intoClass = _: "homeManager";
intoPath = _: [ ];
fromAspect = _: lib.head aspect-chain;
guard = { pkgs, ... }: p: lib.mkIf pkgs.stdenv."is${p}";
adaptArgs = { config, ... }: { osConfig = config; };
}3) Use explicit helpers for context intent
Prefer:
perHost ({ host }: { ... })
perUser ({ host, user }: { ... })
perHome ({ home }: { ... })Over generic den.lib.take.exactly forms when intent is narrow. These forms document better the code intention. All these three work with plain attrs or functions.
4) Standalone homes bound to existing host/user gain automatic os args
Homes named like "user@host" now behaves consistently with hosted HM flows. For example, having:
den.homes.x86_64-linux."tux@igloo" = {};
den.hosts.x86_64-linux.igloo.users.tux = {};Will
- Correctly set
<home>.userNameso that batteries likedefine-userwork. - Automatically bind the standalone HM instance to the OS host configuration.
This means that by declaring tux@igloo you can quickly rebuild your home
environment without rebuilding the whole igloo OS.
5) Extended schema modules for users and homes.
User schema modules an also access their host:
den.schema.user = { host, user, lib, ... }: {
options.foo = lib.mkOption { };
};Also standalone HM bound to an "user@host" pair give you access to home.user and home.host meaning the home object is bound to those.
User can access the host it was difined-in, via user.host, possibly rendering {host,user} into only {user} in the future -not sure-.
CI/Test and Tooling Changes
- Added CI/workflow. update
latesttag upon release, docs/test workflow changes, semantic PR updates. - Added
shell.nixand exporteddevShellsfrom flake output for contributors.
Docs and Naming Updates
- New docs page for mutual routing:
guides/mutual. - Custom classes and batteries docs updated to emphasize forward/mutual patterns.
- Release/versioning docs linked from README.
Recommended Upgrade Checklist
- Remove any
den._.bidirectionalusage. - Enable mutual routing explicitly with
den.ctx.user.includes = [ den._.mutual-provider ];where needed. - Move host->user routing to
<host>.provides.to-usersand user->host routing to<user>.provides.to-hosts. - Replace
den.lib.take.atLeast/exactlyincludes withperHost/perUser/perHomewhere appropriate.
v0.12.0
Den v0.12.0. aka. All Things Must Pass
Where All Things = contexts, aspects and schemas that are now part of custom namespaces and can be passed around between flakes.
This release was about stabilization and performance, moving towards a more stable Den that can actually be used for sharing dendritic aspects across projects.
This paves the road for Den social goals and in particular to enable what is needed for github:vic/denful project in coming weeks.
NOTE Den@0.12.0 requires flake-aspects@0.7.0
What's Changed
All aspects are now parametric {} by default.
This is huge QOL improvement for Den users, previously an aspect foo = { includes = [ x y ]; } was simply an attribute set that knew nothing about propagating context, for that we had to use foo = parametric { ... } so that included aspects were given the correct context.
From now on, all aspects that don't have an explicit functor set by people, are parametric {} by default.
den.base is now den.schema.
See #235
Templates now expose nh-based apps for building hosts/homes.
PR #233
Thanks to Dendritic, you can just copy modules/nh.nix module from them into your projects and it should just work.
New batteries
den._.hostname -- sets the hostName.
den._.mutual-provider -- for one-to-one mutual config contribution between host and user. See the documentation for more on these.
MicroVM template
Our new MicroVM template showcases Den extensibility, custom extensions to den.schema and den.ctx to enable microvm guests nested in microvm hosts.
New Guides: From Zero To Den and From Flake to Den
Available at https://den.oeiuwq.com/guides/from-zero-to-den .
Small Performance Improvements
We had a discussion about Den evaluation time #249, up until now we had done absolutely nothing about performance. As good (lazy) engineer I've delayed thinking about performance until it is actually measurable and actionable.
So I used hyperfine to measure some changes on a couple of places from flake-aspects resolve and Den where I believe we could have improvements. No profiling yet specific functions or zones to see what needs to be changed, I just went to modify what I believed from the top of my head to be quick fixes. I used our own test suite to benchmark changes:
Run comparission 20 runs (10 warmup) of all our CI tests between perf branch and main before it.
perf is: den@dd8fac7355be4cfe7a143bce45cbba3d786d8be1 with flake-aspects@81a51a8997abe392b9d0794424a4823adc9bd3af
main is: den@69682fffa09d57fb8a8dbf367252dbedca023207 with flake-aspects@778d8d7c1352c3b173cdcb9fd0f65acbaf4744a6
hyperfine -m 20 -w 10 -n perf "cd perf && just ci" -n main "cd main && just ci"
Benchmark 1: perf
Time (mean ± σ): 25.840 s ± 0.931 s [User: 24.157 s, System: 0.670 s]
Range (min … max): 24.236 s … 27.626 s 20 runs
Benchmark 2: main
Time (mean ± σ): 39.205 s ± 2.053 s [User: 37.377 s, System: 0.711 s]
Range (min … max): 36.156 s … 43.566 s 20 runs
Summary
perf ran
1.52 ± 0.10 times faster than mainBenchmarks are always to be taken with a graint of salt, because specific hardware, and we are talking about Nix (IO intensive) and not the fastest interpreter, I'm using community nix btw. I'm not as focused in lowering these numbers unless we can get some help from people who are actually performance engineers. I'm not.
And this benchmark is not that accurate because perf had additional tests than old main .
Anyways a bit of speed is never bad.
hjem,maid,home-manager have same den.ctx behaviour
Unified how hm-user, hjem-user and maid-user contexts work. This also fixed #254 where den.ctx.hm-user was never being applied. We now have test ensuring that this works also for den.ctx.hjem-user and other home envs. See hjem.nix and others, they are now really simple, people can create their own home environments using den.lib.home-env to integrate other nix libs.
Fixed importing parametric aspects across flakes
This was previously a big limitation that was preventing us to share parametric aspects. We were able to share static ones, but Den is about parametric, so this was really important specially for the Denful project to start. See #261 and #259
"From Zero To Den" Fixes
@ulfbayte helped us by following the Zero To Den guide and actually executing each command there and fixing inaccuracies introduced by me while writing and not running stuff :) Thanks to that the guide will be much better for people new to Nix and Den.
Merged Pull-Requests
- host/home apps at flakes/noflake for building with nh by @vic in #233
- rename den.base to den.schema to clarify intention by @vic in #234
- improve(ci): Semantic PR checks by @vic in #237
- feat(battery): New
den._.hostnameand .. by @musjj in #236 - feat(template): MicroVM example integration by @vic in #240
- fix(battery): den._.hostname strictness by @vic in #244
- feat(docs): New guides from Zero to Den and From Flake to Den by @vic in #247
- feat(ctx): Allow
.intoto be a function to attrs by @vic in #251 - perf(core): Hyperfine benchmark and perf improvements by @vic in #255
- refactor(homes): Make hm,hjem,maid have the same behaviour. by @vic in #257
- fix(homes): ctx.hm-user owned config was not being included. by @vic in #258
- fix(namespaces): test that deeply nested parametric aspects work across flakes by @vic in #259
- feat(ctx): Allow transformations into nested contexts by @vic in #260
- Update home-manager command to specify master branch by @ulfbayte in #264
- feat(core): Use a parametric default functor by @vic in #243
- feat(namespaces): Allow sharing custom ctx and schemas on namespaces by @vic in #265
- fix(namespaces): Parametric aspect from imported namespace by @vic in #266
- fix(docs): Fix tutorial steps on failure by @ulfbayte in #268
- feat(ci): Add Docs build at CI by @vic in #270
New Contributors
Full Changelog: v0.11.0...v0.12.0
v0.11.0
Here's Den v0.11.0. This release is mostly about improvements to forwarding classes, multi home-environment support and bug fixes.
Improvements to forwarding classes and their documentation
Many people started using forwarding classes, and came up with great ideas that either became examples in our documentation or built-in batteries like our os class (which forwards to both nixos and darwin).
People using this uncovered some bugs we fixed in this release. As always I'm really thankful to people using Den and reporting issues, because that's the only way I can notice something went wrong.
den.default is a real aspect (again)
But now den.ctx.default = den.default. This is because using lib.mkAliasOption was producing wrong locations. The location is always reported as a file in Den source code and not the real location where people defined settings. The same problem happens with ._. alias for .provides. from flake-aspects, however we cannot remove that alias. Bug report here: https://discourse.nixos.org/t/lib-mkalias-renamedoptionmodule-doesnt-preserve-location-of-option-definition/75879
den.lib and /default.nix
Improved usability of Den as a library, our default.nix exposes den.lib and a nixModule that can be included in any module system (be it flake-parts, falake, lib.evalModules, etc).
Our Den library includes den.ctx and den.aspects but without any NixOS / Homes specifics, allowing people to use Den configuration model (context-driven + aspects) for other Nix domains (like cloud infra).
We have tests for this at den-as-library.nix CI test.
This den.lib feature also helped us with a very weird bug #216 by not having anything of the NixOS/Homes stuff normal Den framework does.
Made all batteries use take.exactly
Batteries are also examples, so it is important for them to be as correct as possible. Just in case people copy from them, we now make them use exactly matching on context.
Enabling home-manager or other home-envs.
Den has always been feature opt-in, the reason is not all people have the same needs and also because not having all features enabled at the same time helps with hunting bugs.
Previously, our home-manager integration was enabled via an aspect den.provides.home-manager. Which many people used to include in den.default.includes = [ den._.home-manager ]. We are now trying to move away from den.default as much as we can at least in core. den.default is useful and will not be removed, but since we introduced den.ctx we have better ways to include re-usable aspects.
We also have support for other home-environments, hjem and nix-maid, because of this, a single user can now choose to use any combination of home-manager and others, or none (simply using the user forwarded class). Because we now many home environments, none of them is preferred by Den. And, it is a user concern which of them each user chooses to have.
home-manager, hjem, and nix-maid work the same in Den, they use forwarding classes and are enabled when host.hjem.enable = true (or equivalent). By default this .enable value is: "has any of my users a hjem in its classes?", because the context types for each type of home den.ctx.hjem-host only activate when at least one user has requested hjem.
So now instead of using den.provides.home-manager, which home environments are active is a responsability of each user.
den.hosts.x86_64-linux.igloo.users.tux.classes = [ "homeManager" ];However this can get repetitive when having multiple hosts or users, so you can use the den.ctx.user base schema to specify in YOUR configuration that homeManager is preferred.
den.base.user.classes = lib.mkDefault [ "homeManager" ]The old den.provides.home-manager aspect does nothing, it raises an error telling you to now specify user.classes.
We now have proper WSL support
And a corresponding den.ctx.wsl-host pipeline when WSL is enabled.
intoAttr is now a path.
You can change where a configuration is placed, this is useful for standalone home-manager and some WSL nixos configs.
den.homes.x86_64-darwin.tuxie = {
intoPath = [ "homeConfigurations" "tux@igloo" ];
}
noflakes template updated with multi-host suppot and nh apps.
Den can produce an nh based app for activating each host, this is currently only part of the noflake template, but will later be added to other flakes and documented on how to use in existing flakes.
What's Changed
- Fix nix-maid link in noflake tutorial by @drupol in #194
- Only apply unfree to nixos/darwin/homeManager by @OscarMarshall in #196
- CI: tests first and templates until approved by @vic in #199
- fix: den.fwd.adapter.user already defined by @vic in #202
- fix include-less parametric in provides by @musjj in #208
- Make intoAttr a path by @vic in #211
- add
wslclass and den.ctx.wsl-host by @vic in #213 - Home integration now enabled via host config for hm, nix-maid, hjem by @vic in #215
- docs by @vic in #217
- move context apply functions to own file by @vic in #218
- make batteries use parametric.exactly by @vic in #220
- fix wrong location issue of option definied in den.default by @phhai-hcmut in #221
- feat:
osclass forwards to NixOS and MacOS by @vic in #225 - feat: apps per host for build/swith/repl using nix-community/nh by @vic in #227
- remove duplicate code between homes by @vic in #229
- feat: default.nix exposes clean den API without NixOS domain by @vic in #230
- update flake-aspects, test no merged aspect.__functor by @vic in #231
New Contributors
- @musjj made their first contribution in #208
- @phhai-hcmut made their first contribution in #221
Full Changelog: v0.10.0...v0.11.0
v0.10.0
Den v0.10.0
Highlights
Updated documentation
I have put a lot of my time on trying to detail as much as I can about Den in the docs. As always everyone is invited to improve the docs and fix my english grammar-errors/typos.
As always the source of truth is the source code itself, and all CI tests are self-isolated intended to be used also as complete den examples each of them.
bogus and CI run on MacOS (again)
To enable people contribute more tests to Den. I've also updated the bogus repo with cleaner test setup, in hope that people can contribute more tests. You are welcome to send PRs with new tests even if they are failing and have no fix, the bogus and CI templates both use the same test helpers. Also updated docs about this.
den.ctx now deduplicates includes at context-application time docs code.
Den keeps track of which aspect-owned configs have already been seen, but functional includes are always called (there's no way for den to know { host, user } was included elsewhere). However this static config deduplication already fixes some failing tests contributed from bogus-reports.
The documented recommendation is not to naively use den.default.includes = [ ({ host, ... }: ...) ], and prefer including at specific host or den.ctx.host and friends. Or read about the documented limitations of den.default and the need of take.exactly.
Den now has a user class that works on NixOS and nix-Darwin.
This is powered by our den.provides.forward higher-order aspect,
the exact same that powers our homeManager class.
User aspects can use the user class to specify any setting that will be forwarded to the host: nixos.users.users.<username>:
# works with static configs
den.aspects.tux.user.extraGroups = [ "wheel" ];
# can also access the host pkgs and osConfig
den.aspects.tux.user = { pkgs, osConfig, ... }: {
packages = [ pkgs.hello ];
}This is a convenience for people like me not using homeManager.
Users can now declare many home-environment classes.
Previously user.class defaulted to "homeManager", now it was replaced by user.classes defaults to [ "homeManager" ],
this is because a user might want to manage some files using nix-maid or hjem instead or addition to homeManager.
These two work exactly like homeManager does, that means we also have den.ctx.maid-host and den.ctx.hjem-host context types that are activated when a host has users with the corresponding user.classes.
Guarded forward classes.
This is another feature built on top of den.provides.forward.
People requested the following feature: I want my aspect to assign configs directly to a class and that config be included in the host only if some third-party module was imported (main use case was Impermanence options):
Aspect defines config under a custom user-defined persist class:
# any file can contribute to persist class
{ den.aspects.tux.persist.directories = [ "Firefox/.mozilla" ]; }host defines how and when the persist class is forwarded into the host configuration. all other files need not to be filled with mkIf or any other conditional checking for impermanence support, only one file does this:
persist = { host }: { class, aspect-chain }: den._.forward {
each = lib.attrValues host.users;
fromClass = _: "persist";
intoClass = _: class; # nixos or darwin, dynamically.
# path taken from https://nixos.wiki/wiki/Impermanence
intoPath = user: [ "home-manager" "users" user.userName "home" "persistance" "/nix/dotfiles" ];
fromAspect = user: den.aspects.${user.aspect};
guard = { options, ... }: options ? environment.persistance; # takes nixos module args and checks if impermanence was imported
};
den.hosts.my-laptop.includes = [ persist ];What's Changed
userclass, convenience for OSusers.users.userNameby @vic in #170- fix: add missing
noflaketemplate by @drupol in #178 - Remove OS from example defaults.nix by @OscarMarshall in #179
- Dedup includes on ctxApply by @vic in #185
- Improve Docs by @vic in #186
- update flake-file (removes systems input) by @vic in #187
- run tests and bogus on linux and macos by @vic in #188
- add hm-host isolation test by @vic in #189
- user class modules can take os-level pkgs and osConfig. by @vic in #190
- A single user can support many classes: homeManager / hjem / maid by @vic in #191
- hjem support by @vic in #173
- nix-maid integration by @vic in #172
- Guarded classes for configs that depend on options presence. (eg Impermanence) by @vic in #192
New Contributors
- @drupol made their first contribution in #178
- @OscarMarshall made their first contribution in #179
Full Changelog: v0.9.0...v0.10.0
v0.9.0
Den v0.9.0 — Declarative Context Definitions and new documentation website!
This release introduces den.ctx, a declarative system for defining how context (data) is transformed and which aspects are applied at every stage of the configuration pipeline.
We now have a new documentation with more diagrams, the docs are now part of the repo to aid AI tools looking at the repo to also read the docs.
I also re-wrote all tests to be self contained and isolated. Previous to this, we all tests shared the same aspects and hosts, making it very difficult for people to use CI tests as practial examples. Now each test is a self contained den configuration and works or fails by itself. Read templates/ci/modules !
Upgrading
If you had any custom context like { OS, host } it now becomes { host }, same for { HM, user, host } -> { user, host }
What's Changed
- Update Den at npins to make noflake work from checkout by @vic in #162
- feat: forward classes by @vic in #164
- update den and flake-aspects by @vic in #166
- Example tests and documentation for forwarding classes. by @vic in #167
- Fix usage of obsolete options and remove passthru requirement (#169) by @jhakonen in #171
- den.ctx. Den declarative context definitions. by @vic in #175
New Contributors
Full Changelog: v0.8.0...v0.9.0
Context flows (our new dependency system)
Before this release, den.default served two purposes:
- A place to define global or generic includes for
host,user, andhomeentities. - The backbone of context propagation — moving data from
{ host }to{ host, user }to{ host, user }in the HM pipeline, etc.
As a consequence, den.default.includes was abused by many of us, including Den itself, because it was where context transformation happened [2]. This "dependency system" — parametric aspects installed unconditionally at den.default.includes — was hard to reason about, hard to document, and hard for people to understand.
The symptoms were duplicate configuration values, caused by lax parametric functions matching too many pipeline stages.
What den.ctx Provides
- Keep
den.defaultfor what it's good at: global settings. You can still useden.default.includes, but there are better alternatives now. - Move the dependency system out of
den.default.includes: those parametric aspects were not individually testable, and you couldn't change how data flows. They were Den's hardcoded backbone. - Declarative data stages: context transformations are now explicit. Given a
host, you declare how to enumerate users, detect HM support, etc. - Named contexts: previously we identified contexts only by their
attrNames—{ host },{ host, user }. Now they have names:ctx.host,ctx.hm-host. Names allow different contexts with the same structural shape but different semantic guarantees. - Extensible context flows: one core principle of Den is not getting in your way. You can create alternative flows, or use Den purely as a library.
Context Transformations: Parse don't Validate principle
Named contexts carry semantic meaning beyond their structure. ctx.host { host } and ctx.hm-host { host } hold the same data, but hm-host guarantees that home-manager support was validated:
inputs.home-managerexists (or the host has a customhm-module)- The host has at least one user with
class = "homeManager"
You cannot obtain an hm-host context unless these conditions hold. This follows the transform-don't-validate principle.
How a Context Type Works
A context type has four components: desc, conf, includes, and into.
den.ctx.foo.desc = "The foo context requires { foo } data.";
den.ctx.foo.conf = { foo }: my-aspects.${foo.name};When ctx.foo is applied — it works like a function taking { foo } — it locates the responsible aspect via conf. For example, ctx.foo { foo.name = "bar"; } uses my-aspects.bar. The aspect's owned config, static includes, and parametric includes matching { foo } all contribute to whatever ctx.foo is being used to configure.
Context types are independent of NixOS. Den can be used as a library for network topologies, declarative cloud infrastructure, or anything describable as data transformations.
How a NixOS Configuration Is Built
The initial data for nixosConfigurations.igloo is the host itself:
# Nothing NixOS-specific yet — just a graph of dependencies.
aspect = den.ctx.host {
host = den.hosts.x86_64-linux.igloo;
};The result of ctxApply is a new aspect that includes den.aspects.igloo plus the entire transformation chain — user enumeration, HM detection, defaults.
# This is where things enter the NixOS domain.
nixosModule = aspect.resolve { class = "nixos"; };
nixosConfigurations.igloo = lib.nixosSystem {
modules = [ nixosModule ];
};These two steps can be adapted for any class, for anything Nix-configurable.
Context Propagation
Context transformation is declarative. If your data fans out to other contexts, you specify the transformations using .into:
den.ctx.foo.conf = { foo }: ...;
den.ctx.moo.conf = { moo }: ...;
den.ctx.foo.into.moo = { foo }: lib.singleton { moo = deriveMoo foo; };All <source>.into.<target> transformations are taken into account by ctxApply.
Why Lists?
Transformations have the type source → [ target ]. This enables:
- Fan-out: one host produces many
{ host, user }contexts (map) - Conditional propagation: zero or one contexts (
lib.optional) - Pass-through: identity transformation (
lib.singleton)
For example, HM detection uses conditional propagation:
den.ctx.host.into.hm-host = { host }:
lib.optional (isHmSupported host) { inherit host; };Same data, but the named context guarantees validation passed.
Contexts as Aspect Cutting-Points
Contexts are aspect-like themselves. They have owned configs and .includes:
# Owned config — only for validated HM hosts:
den.ctx.hm-host.nixos.home-manager.useGlobalPkgs = true;
# Scoped includes — only for validated HM hosts:
den.ctx.hm-host.includes = [
({ host, ... }: { nixos.home-manager.backupFileExtension = "bak"; })
];This is like den.default.includes but scoped — it only activates for hosts with validated home-manager support.
Extending the Context Flow
You can add new transformations to any existing context type:
den.ctx.hm-host.into.foo = { host }: [ { foo = host.name; } ];
den.ctx.foo.conf = { foo }: ...;
den.ctx.foo.includes = [ ({ foo, ... }: ...) ];The module system merges these definitions. You can extend the pipeline without modifying any built-in file.
Custom Context Flows
Each host has a mainModule option that defaults to:
(den.ctx.host { host }).resolve { class = "nixos"; }You can override mainModule to use a completely alternative context flow, independent of ctx.host. Custom flows can be designed and tested in isolation — Den's CI uses a funny.names class that has nothing to do with NixOS to verify context mechanics independently.
What Happened to den.default?
den.default stays and is still useful for truly global settings. The issue was abusing den.default.includes as the context propagation backbone.
Internal Changes
Previously, all host, user, and home aspects had:
includes = [ den.default ]Now they no longer include den.default directly. Including den.default explicitly is discouraged.
How Defaults Are Applied Now
Each context type transforms into default:
den.ctx.host.into.default = lib.singleton; # passes { host }
den.ctx.user.into.default = lib.singleton; # passes { host, user }
den.ctx.home.into.default = lib.singleton; # passes { home }den.default is now an alias for den.ctx.default. The data that flows into den.default.includes comes from these declarative transformations, not from direct aspect inclusion.
Best Practices
| Instead of | Use |
|---|---|
den.default.includes = [ hostFunc ] |
den.ctx.host.includes = [ hostFunc ] |
den.default.includes = [ hmFunc ] |
den.ctx.hm-host.includes = [ hmFunc ] |
den.default.nixos.x = 1 |
den.ctx.host.nixos.x = 1 |
den.default remains the right place for values that genuinely apply everywhere — like stateVersion. Use context-specific includes for anything that belongs to a particular pipeline stage.
v0.8.0
We did not have a release during January since it I got a bit occupied with real life. But now I'm back to the matrix, and here is Den 0.8.0. Thanks to all people using/reporting-bugs and contributing code.
What's Changed
- update flake-aspects by @vic in #125
- Refactor unfree aspect to use a module by @Shawn8901 in #128
- fix(tests): Fix typo in ssh-server capability description by @Adda0 in #130
- Fix a single letter typo in vm.nix comment by @nikolai-in in #132
- update flake-aspects by @vic in #135
- namespace tests by @vic in #136
- cross flake test by @vic in #137
- feat(noflake): Add no flake-parts no home-manager no flakes example. by @vic in #139
- fix(noflake): Avoid user need to know of flake top-level attr. by @vic in #140
- Simplify default template by @vic in #142
- test template/minimal by @vic in #143
- README: Den is about parametric aspects. by @vic in #144
- simplify bogus template, add link to debugging tips. by @vic in #146
- fix(hm): parametric aspects at user.includes are accounted now by @vic in #148
- Fix contexts for user aspects to avoid duplicates. by @vic in #153
- feat(provides): new tty-autologin battery used for vm. by @vic in #155
- fix: remove withSystem dependency for standalone hm by @vic in #156
- update flakes by @vic in #157
New Contributors
- @Shawn8901 made their first contribution in #128
- @Adda0 made their first contribution in #130
- @nikolai-in made their first contribution in #132
Full Changelog: v0.7.0...v0.8.0
v0.7.0
This release includes a couple of new features as we work on decoupling Den from flake-parts.
In the future Den will be module-type agnostic, you will be able to use it with flake-parts or any alternative (froyo / falake / lib.evalModules). We are trying to make flake-parts specific feature opt-in so we have compatibility between flakes and non-flakes worlds.
What's Changed
- test accessing namespaces from inputs by @vic in #114
- feat: add inputs' and self' aspects by @HeitorAugustoLN in #117
- docs: improve inputs' and self' descriptions by @HeitorAugustoLN in #118
- add base module for user/host/home configs by @vic in #119
- fix: inputs' and self' exactly arguments by @HeitorAugustoLN in #122
- feat: Context for host with home-manager users. by @vic in #123
Full Changelog: v0.6.0...v0.7.0