Sponsor Vic Dendritic Nix License CI Status

den - an aspect-oriented approach to Dendritic Nix configurations.

den and vic's dendritic libs made for you with Love++ and AI--. If you like my work, consider sponsoring

den
  • Dendritic: each module configures same concern over different Nix classes.

  • Create DRY & class-generic modules.

  • Parametric over host/home/user.

  • Share aspects across systems & repos.

  • Context-aware dependencies: user/host contributions.

  • Routable configurations.

  • Custom factories for any Nix class.

  • Use stable/unstable channels per config.

  • Freeform host/user/home schemas (no specialArgs).

  • Multi-platform, multi-tenant hosts.

  • Batteries: Opt-in, replaceable aspects.

  • Opt-in <angle/brackets> aspect resolution.

  • Templates tested along examples.

  • Concepts documented.

Need more batteries? See vic/denful.

For real-world examples, see vic/vix or this GH search.

❄️ Try it now!

Launch our template VM:

nix run github:vic/den

Or, initialize a project:

nix flake init -t github:vic/den
nix flake update den
nix run .#vm

Our default template provides an annotated quick-start.

🏠 Define Hosts, Users & Homes concisely.

See schema in _types.nix.

# modules/hosts.nix
{
  # same home-manager vic configuration
  # over laptop, macbook and standalone-hm
  den.hosts.x86-64-linux.lap.users.vic = {};
  den.hosts.aarch64-darwin.mac.users.vic = {};
  den.homes.aarch64-darwin.vic = {};
}
$ nixos-rebuild  switch --flake .#lap
$ darwin-rebuild switch --flake .#mac
$ home-manager   switch --flake .#vic

🧩 Aspect-oriented incremental features. (example)

Any module can contribute configurations to aspects.

# modules/my-laptop.nix
{ den, inputs, ... }: {

  # Example: enhance the my-laptop aspect.
  # This can be done from any file, multiple times.
  den.aspects.my-laptop = {

    # this aspect includes configurations
    # available from other aspects
    includes = [
      # your own parametric aspects
      den.aspects.workplace-vpn
      # den's opt-in batteries includes.
      den.provides.home-manager
    ];

    # any file can contribute to this aspect, so
    # best practice is to keep concerns separated,
    # each on their own file, instead of having huge
    # modules in a single file:

    # any NixOS configuration
    nixos  = { 
      # A nixos class module, see NixOS options.
      # import third-party NixOS modules
      imports = [ 
        inputs.disko.nixosModules.disko
      ];
      disko.devices = { /* ... */ };
    };
    # any nix-darwin configuration
    darwin = { 
      # import third-party Darwin modules
      imports = [ 
        inputs.nix-homebrew.darwinModules.nix-homebrew 
      ];
      nix-homebrew.enableRosetta = true;
    };
    # For all users of my-laptop
    homeManager.programs.vim.enable = true;

  };
}

# modules/vic.nix
{ den, ... }: {
  den.aspects.vic = {
    homeManager = { /* ... */ };
    # User contribs to host
    nixos.users.users = {
      vic.description = "oeiuwq";
    };
    includes = [ 
      den.aspects.tiling-wm 
      den._.primary-user 
    ];
  };
}

You are done! You know everything to start creating configurations with den.

Feel free to to explore the codebase, particularly our included batteries and tests.

Learn more at our documentation website

Join our community discussion.

Welcome to Den

Den is part of vic's dendritic libs. Its goal is to enable you to create powerful Dendritic Nix modules that are composable and reusable in your own infrastructure and by other people.

Den focuses on declaring Dendritic Nix configurations. This typically involves NixOS, nix-Darwin, and Home-Manager setups, but Den is not limited to any particular Nix configuration class.

Den is powered by the flake-aspects library, extending it with a dependency system for host/user configurations and providing helpers for context-aware, parametric aspects.

Den includes a small set of batteries that serve as generic aspect examples and as opt-in integrations for tools like Home-Manager or to aid migration from non-dendritic setups.

Getting Started

The quickest way to get started is by using our default template.

mkdir my-den && cd my-den
nix flake init -t github:vic/den
nix flake update den

After initializing it, edit the annotated modules/den.nix file. We have made the den.nix file self-explanatory and interesting. You can also read about Den basics or other advanced topics.

Your new den includes a runnable virtual machine, so you can edit the configuration and run the VM to test if things work, instead of having to reboot constantly.

Run the vm with:

nix run .#vm

It also includes a GitHub action. We will soon provide an easy way to integrate scheduled updates using your own cachix cache. This way you won't need to re-build locally things that can be built on your CI.

Declarative Systems

The first step to using Den is to declare the hosts and homes that exist in your infrastructure.

This is intentionally done via a concise, one-liner syntax for the most common use cases. Usage is up to you; some people prefer a single hosts.nix file with one line per host. Urgently decommissioning a host requires only a single-line change in your repository.

Hosts and Users

In the following example, my-laptop is the hostname and vic is the username.

den.hosts.x86_64-linux.my-laptop.users.vic = { };

However, you can also nest several hosts per platform or create multiple users on a single host.

You can change any of their attributes. See the schema for reference.

If you expand the attribute set, we recommend using a single module per host definition.

den.hosts.x86_64-linux = {
  my-laptop = {
    hostName = "yavanna"; # default was my-laptop
    description = "lover of all things that grow on earth";
    class = "nixos"; # default is guessed from platform
    aspect = "workstation"; # default was my-laptop
    users = {
      vic = {
        description = "Victor Borja";
        userName = "vborja"; # default was vic 
        aspect = "oeiuwq"; # default was vic
        class = "homeManager"; # default is homeManager
      };
    };
  };
};

Standalone Home-Manager

Similarly, home declarations are simple.

den.homes.aarch64-darwin.vic = { };

Hosts, Users and Homes are freeform types.

You can add arbitrary custom attributes to your Host, User, and Home attribute sets. This is useful for adding metadata that aspects can later use for configuration.

For example, our primary-user.nix aspect can set wsl.defaultUser if it finds that its { host } has a wsl attribute.

Host, User, and Home are also submodules, allowing you to go beyond freeform types and add new options that will be type-checked by the Nix module system.

Next Step

Once you have some system definitions, you can attach configurations via aspects.

Aspect-Oriented Configurations

Den creates an aspect for each Host, User, and Home you define. The aspect can be accessed via its name, den.aspects.<name>.

For our previous example, the user vic has an aspect den.aspects.vic. If desired, multiple hosts, users, or homes can share the same aspect name. For example, you can have the same vic aspect in a NixOS Home-managed instance and as a standalone Home-Manager instance on Darwin.

Any file inside modules/ can contribute to any aspect. Because of this, we say that in a Dendritic setup, features are incremental.

Aspect Structure

Aspects are provided by the flake-aspects library, so you may want to read its documentation to learn more. We will also provide more information about how Den leverages flake-aspects features in later sections.

For now, it is enough to mention that aspects have three fundamental kinds of attributes.

Owned Nix Configuration Modules

Owned modules are those assigned directly to an aspect, each under the name of a different Nix configuration class.

den.aspects.my-laptop = {
  nixos = { pkgs, ... }: {
    # Anything that is possible in any NixOS module.
    environment.systemPackages = [ pkgs.hello ];
  };
  darwin = { 
    # same for nix-darwin options.
  };
  nixvim = { };
  # or any other possible nix class.
};

Provides

The provides attribute allows aspects to form a nested tree structure. This is the recommended way to organize your related aspects. You decide how to organize them.

flake-aspects defines _ as an alias for provides, and you will likely find many examples using ._. faces.

den.aspects.gaming = {
  description = "Games";
  nixos = { };
  provides = {
    emulation = {
      description = "Old is still good";
      nixos = { };
    };
  };
};

Includes

The includes attribute is how flake-aspects lets you define a dependency graph between aspects.

den.aspects.vic = {
  homeManager = { };
  includes = [ den.aspects.gaming._.emulation ];
};

When the vic.homeManager module is retrieved, all homeManager modules from its transitive dependencies (includes) will also be imported into a single module.

One aspect to define them all, and in dependencies.nix bind them.

Den uses the den.default aspect as a backbone for providing shared values, since each Host, User, and Home automatically includes it.

Global Settings

You can use it to define settings that will never change.

den.default.nixos.system.stateVersion = "25.11";
den.default.darwin.system.stateVersion = 6;
den.default.homeManager.programs.vim.enable = true;

Default Dependencies

den.default also serves as the main router for context-aware aspects, a topic we will explore in later sections.

default.includes

As with any aspect, you can make default depend on other aspects, causing them to be included in all Homes, Users, and Hosts. This topic is also explored incrementally in the following sections.

You can also make any aspect include den.default, but be sure to avoid recursion.

Statics: Including Plain Attribute Sets

There are two fundamental types of aspects that can be used in an <aspect>.includes. The first are static includes, which means you are including an anonymous aspect by using a plain attribute set.

den.default.includes = [
  { nixos.system.stateVersion = "25.11"; }
];

# instead of assigning **owned** attributes in den.default:
# den.default.nixos.system.stateVersion = "25.11";

Our previous example is trivial, but statics make more sense when they are part of an aspect tree and are not included in den.default but selectively on other aspects.

den.aspects.editors._.vim = {
  homeManager.programs.vim.enable = true;
};

den.aspects.vic.includes = [
  den.aspects.editors._.vim # a static include
];

Dynamic Aspects: Functions into Aspects

The second type are functions that return an aspect, much like a Nix module can be a function that returns a module.

This is where Den configurations become truly generic and reusable.

Functional aspects can be defined as let-bindings for short, one-shot usage or as part of an <aspect>.provides attribute set.

Here's a simplified version of our user-shell included battery:

den.aspects.utils.provides.myShell = shell: {
  nixos.programs.${shell}.enable = true;
  homeManager.programs.${shell}.enable = true;
};

den.aspects.vic.includes = [
  (den.aspects.utils._.myShell "fish")
];

We will continue exploring more advanced functional aspects in the next sections.

The __functor Pattern

In Nix, any attribute set with a __functor attribute can be applied as if it were a function with internal state.

let 
  dup = {
    by = 2;
    __functor = self: num: self.by * num;
  };
in dup 3 # => 6

Using <aspect>.__functor as a Context-Aware Router

All aspects in flake-aspects have a default __functor attribute that looks like this:

{
  nixos = { a = 1; };
  __functor = aspect: context: aspect; # ignores context
}

The default __functor ignores its given argument and always returns the aspect being applied.

This means you can provide another __functor that, instead of ignoring context, inspects it along with its own internal state and, as a result, returns a different aspect that will ultimately provide the settings.

{
  nixos.foo = 24;
  __functor = aspect: context:
    if context.venus_in_aquarius then aspect
    else { includes = [ den.aspects.other-stuff ] };
}

Context-Aware Functional Aspects

Functors are how Den achieves context adaptation and configuration parameterization.

Be sure to read the section about Functors.

A Parametric aspect is one whose sole purpose is to dispatch to its .includes functions based on an existing context.

You create a parametric aspect by using one of the den.lib.parametric functors detailed below.

The most common and frequenly used is the den.lib.parametric function itself, it expects an non-functional aspect as an attribute-set and creates a parametric aspect from it.

den.aspects.foo = den.lib.parametric {
  nixos.foo = 1;
  includes = [ den.aspects.bar ];
};

The foo aspect will be able to take any type of context (argument), for example it can be applied with foo { x = 1; } and foo will forward the same context { x } to bar or any other of its .includes as long as they can take atLeast the same argument names.

The parametric.exactly Functor

IMPORTANT: parametric.exactly only dispatches to .includes functions. It will not take care of owned configurations. For that use parametric function itself, or (parametric.withOwn parametric.exactly).

For example:

let
  a = { x, ... }: { nixos.foo = x; };
  b = { x, y }: { nixos.foo = y; };
  c = { z }: { nixos.foo = z; };

  F = parametric.exactly {
    includes = [ a b c ];
  };
in F

When the F aspect is applied like F { x = 0; y = 1; }, it will only invoke the b function, since it is the ONLY function that takes exactly the named arguments { x, y }, no more, no less.

The parametric.atLeast Functor

IMPORTANT: parametric.atLeast only dispatches to .includes functions. It will not take care of owned configurations. For that use parametric function itself, or (parametric.withOwn parametric.atLeast).

Using the same example as before:

let
  a = { x, ... }: { nixos.foo = x; };
  b = { x, y }: { nixos.foo = y; };
  c = { z }: { nixos.foo = z; };

  F = parametric.atLeast {
    includes = [ a b c ];
  };
in F

Calling F { x = 1; y = 2; } will invoke a and b, including both of their results. In this example, this causes different values of nixos.foo to be set.

The Definition of den.default

The complete definition of den.default is as follows:

den.default = den.lib.parametric.atLeast { };

This means that the den.default aspect is nothing more than a router that will include results from its .includes functions according to the current context.

The parametric.withOwn combinator.

The parametric function itself is an alias for parametric.withOwn parametric.atLeast. Meaning that it will behave like parametric.atLeast but it will also include the aspect owned modules and static includes.

Fixed-Context Aspects and Context Adaptation

Context adaptation refers to changing the context of an existing aspect, making it provide a different set of modules.

An aspect calling parametric.fixedTo with an attribute set becomes a fixed or known-context aspect and can be included as if it were a static aspect without having to call it as a function.

The parametric.expands functor allows you to append an attribute set to whatever context is received at the call.

This example adapts an aspect by replacing its inner __functor twice:

let
  a = { x, ... }: { nixos.foo = x; };
  b = { x, y, ... }: { nixos.foo = y; };

  # when original is used, it will only call a since b needs more context
  original = parametric.fixedTo { x = 0; } { 
    includes = [ a b ];
  };

  # replaces the context, but updated will still only invoke a.
  updated = parametric.fixedTo { x = 1; } original;

  # when adapted is used, it will call both a and b, { x = 0, y = 2 }
  adapted = parametric.expands { y = 2; } original;

in adapted

Advanced Context Handling

Note that since all den.lib.parametric combinators are themselves functors, the following two are exactly the same:

# functional style: applying to an aspect.
parametric.atLeast {
  includes = [ a b ];
};

# attribute style: setting __functor attribute.
{
  includes = [ a b ];
  __functor = parametric.atLeast;
}

The first alternative is the recommended use, since it is more idiomatic and does not surfaces knowledge about the internal __functor.

However, for more advanced aspects, you can provide your own __functor attribute. Read the parametric code for how to build your own.

Also, reading the tests for these parametric functors can be of help.

Dependency System

Den implements its dependency system between Hosts, Users, and Homes by using contexts.

An aspect is basically a function that returns a set of configurations. The argument to this function is what Den calls a context; specifically, the argument's attribute names and the required arguments a function can take is what decides if a function is invoked or not.

It is the job of Den's takes.atLeast, takes.exactly, and funk combinator to select and apply the context only to functions from <aspect>.includes that support the context.

Read the following sections to learn about the different context variants that Den uses by default.

How This Relates to den.default.includes or any parametric.atLeast functor.

Remember that den.default is just an aspect with functor parametric.atLeast.

In particular, den.default is the backbone of dependency resolution in Den, since all Hosts, Users, Homes include den.default. Dependency resolving functions are registered in den.default.includes, and you can provide your own. Just take the following in mind:

If you add a function like this to any parametric.atLeast aspect:

den.default.__functor = den.lib.parametric.atLeast;
den.default.includes = [
  ({ host, ... }: { nixos.x = 1; })
];

The function will be invoked whenever a the aspect is applied to an argument that has at least the host attribute. This could happen for several different contexts, like { OS, host } initially, and later { OS, host, user } for each host user, or others like { HM, host, user}, or even your own custom contexts.

If the context is too loose and applies multiple times, it is possible that the resulting value of the function will produce duplicate configuration values. This is more evident on listOf _-type configuration options, where your function will generate duplicated values for each time the function was called.

To prevent this, you need to make the function argument-names more explicit by requesting a more attributes from the context or by using den.lib.takes.exactly if using an smaller context:

den.default.includes = [
  (den.lib.takes.exactly ({ OS, host }: { nixos.x = 1; }))
];

This will only apply to { OS, host } and not to { OS, host, user }. Avoiding duplication for each of the host's users.

With Great Power Comes Great Responsibility

Given the flexibility of den.default, it is recommended to use precise contexts or install parametric aspects specifically at particular host, user, or home aspects instead of abusing den.default.

As an example of creating your own routers, see the default-template's routes.nix

Creating Your Own Intent Context

A context is nothing more than a set of arguments. You can create your own that dispatches and includes differently than our built-in OS and HM contexts.

Obtaining an OS Configuration

The Initial { OS, host } Context

Suppose you have a single hostAspect and a single userAspect. An OS-level configuration -one whose class is nixos or darwin- starts by applying the hostAspect to { OS, host } (the initial, or intent context). Here, host is the den.hosts.<system>.<host> value, and OS is a self-reference to the aspect that is being applied.

The hostAspect must then return the whole OS configuration. This is done, by including the results of calling other aspects with a context, this is the job of Den's dependency system.

Steps that happen to collect the complete OS configuration.

Given that hostAspect is a parametric aspect, the functor applies functions from hostAspect.includes that support at least { OS, host } context.

Since den.default is included in this list, and it has an (exactly { OS, host }: osDependencies) dependency, the function osDependencies is used to return the whole OS configuration by doing the following:

  • First, owned-modules and static-includes are obtained from the den.default aspect.

  • Similarly, we obtain owned-configurations and static-includes from the hostAspect itself.

If the Host has Users, for each user we include the following dependencies:

  • The owned-configurations and static-includes from the userAspect, because it can provide static values for OS-class.

  • The result of applying userAspect with a now expanded { OS, host, user } context. This is where a user can provide parametric configuration for the OS.

The aggregated OS-class modules of all these transitive aspects is the resulting configuration for the host.

OS HomeManaged Configuration Dependencies

When home-manager integration has been enabled on a Host, the following happens to resolve the complete homeManager class configuration for a User.

The { HM, user } Intent Context

The home-manager.nix integration starts by applying the userAspect to the initial context { HM, user, host }.

Similarly to what happened for the OS, the dependency system does the following steps:

  • Obtains owned-configurations and static-includes from den.default.

  • Obtains owned-configurations and static-includes from userAspect.

  • Obtains the result of applying hostAspect to the context { OS, HM, user, host }. This is the oportunity of a Host aspect to provide parametric homeManager-class configuration to its users.

The homeManager modules from these transitive dependencies conform the User's home-manager module.

Standalone HomeManager Configuration Dependencies

Similar to previous cases, obtaining an stand-alone HomeManager configuration happens like this:

The { HM, home } Intent Context

The homeAspect is applied to the context { HM, home }, the dependency system takes the following steps:

  • Obtains owned-configurations and static-includes from den.default.

  • Obtains owned-configurations and static-includes from homeAspect.

The homeManager class of these transitive aspects is the resulting configuration.

Included and Replaceable Batteries

Den includes several opt-in batteries, which are generic aspects that serve as both examples and integrations, such as:

  • home-manager - Integrates Home-Manager into hosts.
  • unfree - Enables unfree packages by name.
  • import-tree - Imports trees of Nix files, which is particularly useful for loading non-dendritic modules and aiding in migration.
  • define-user - Defines a user at the OS and HM levels.
  • primary-user - Makes a user an administrator.
  • user-shell - Sets a user's default shell.

All of them are currently being CI tested, and you can look at examples of their use.

Other, more powerful batteries are available via denful.

Context Data Instead of specialArgs

Since (functional) aspects can take Host, User, and Home data via their context (arguments), there is no need to use specialArgs in configurations.

{ user, host }: {
  nixos = ...; # use host data
  homeManager = ...; # use user data
}

However, at times you may have no other option. For example, if you want a standalone Home-Manager to have access to the osConfig of a particular host, you can achieve it by specifying a custom instantiate attribute on the home object. Be warned that using specialArgs is an anti-pattern in Dendritic and should be avoided unless absolutely necessary.

Bidirectional Dependencies

Den's dependency system allows you to have Hosts that provide configuration to all their users and users that provide configuration to the hosts they live on.

See some examples of this.

Custom Factories for Any Nix Class

Den natively supports nixos, darwin, and homeManager configurations. However, any host can use a custom instantiate function to build its configuration. It is also possible to customize the intoAttr attribute to specify where the configuration should be placed.

Different Input Channels per System

At times, you may need to restrict certain hosts to specific Nixpkgs channels. For example, a WSL system might need to use the stable channel for Nixpkgs, Home-Manager, and NixOS-WSL.

This can be achieved by using the instantiate attribute. See an example here.

Den Angle-Brackets syntax.

Angle brackets is an experimental, opt-in feature.

When Den's __findFile is in scope, you can do:

   <pro/foo/bar> # and it will resolve to:
   den.aspects.pro.provides.foo.provides.bar

   <pro/foo.includes> # resolves to:
   den.aspects.pro.provides.foo.includes

   <den/import-tree/home> # resolves to:
   den.provides.import-tree.provides.home

   <den.default> # resolves to
   den.default

   # When the vix remote namespace is enabled
   <vix/foo/bar> # resolves to
   den.ful.vix.foo.provides.bar

You can bring __findFile into scope in two ways:

# on a lexical scope via a let-binding
den.aspects.my-laptop.includes = 
let
  inherit (den.lib) __findFile;
in [ <den/home-manager> ];

Or, globally on each module scope.

To enable it, have a module with:

{ den, ... }:
{
  _module.args.__findFile = den.lib.__findFile;
}

Then, bring __findFile into scope from module args:

{ __findFile, ... }:
  den.default.includes = [ <den/home-manager> ];
}

Shareable Aspects (Namespaces)

Namespaces are a Den social feature. It allows many flakes to augment a given aspect tree (under the same namespace).

You enable namespaces by having a module like:

{ inputs, ... }: 
{
  imports = [

    # create local `eg` (example!) namespace. not-flake exposed.
    (inputs.den.namespace "eg" false)

    # you can have several namespaces, create yours
    (input.den.namespace "yours" true)

    # you can also mixin other's namespaces
    (input.den.namespace "ours" inputs.theirs) # from remote
    (input.den.namespace "ours" true) # from local
  ];
}

Internally, a namespace is just a provides branch:

# den.ful is the social-convention for namespaces.
den.ful.<namespace>.<aspect>

Having an aspect namespace is not required but helps a lot with organization and conventient access to your aspects.

The following examples use the vix namespace, inspired by github:vic/vix own namespace pattern.

By using an aspect namespace you can:

  • Directly write to aspects in your namespace.
{
    vix.gaming.nixos = ...;

    # instead of:
    # den.ful.vix.gaming.nixos = ...;
}
  • Directly read aspects from your namespace.
# Access the namespace from module args
{ vix, ... }:
{
    den.default.includes = [ vix.security ];

    # instead of:
    # den.default.includes = [ den.ful.vix.security ];
}
  • Share and re-use aspects between Dendritic flakes
imports = [
  # Aspects opt-in exposed as flake.denful.<name>
  ( inputs.den.namespace "vix" true)

  # Many flakes can expose to the same namespace and we
  # can merge them, accessing aspects in a uniform way.
  ( inputs.den.namespace "vix" inputs.dendrix )
];
  • Use Den angle-brackets to access deeply nested trees
{ __findFile, ... }:
  den.aspects.my-laptop.includes = [ 
    <vix/gaming/retro> 
    
    # instead of den.ful.vix.gaming.provides.retro
  ];
}

Community Support (Discussions)

Everyone is welcome to participate in Den discussions. The only rule is to be mindful and respectful of each other. We value people's time as you value yours, and we all deserve the respect you also deserve.

Join us in the discussion, share your thoughts about Den, how you are using it, or ideas on how we can make it better.

Come, get help, and help others along the way.

Quaerendo Invenietis. (By seeking, you will find.)

Contributions Welcome

All kinds of contributions are welcome.

All PRs are checked against the CI. New features should include a test in _example/ci/.

To run tests locally:

nix flake check ./checkmate --override-input target .
nix flake check ./templates/default --override-input den .

Ensure the code is formatted:

nix run ./checkmate#fmt --override-input target .

Hang Out, Say "Hello, Here's a Potato!"

Just share that you are using Den—that will mean a lot to me (vic).

Reporting Bugs

If you have found something that feels odd, be sure to open a discussion.

We use issues for actionable, pending, or planned tasks. Issues are on my (vic's) agenda, so I prefer to have as few as possible; for me, they represent actionable to-dos that I can allocate time to.

Share a bogus Den

We provide a bogus template you can use for sending bug reports. It will mean a lot if you reproduce the bug using our template, because it can save us a lot of time and because your own code can become a test suite in itself.

Use the following commands and share your repository with us:

mkdir bogus && cd bogus
nix flake init -t github:vic/den#bogus
nix flake update den
nix flake check

Become a Patron

Talking about time, I've spent a lot of my own personal time on these libraries because I love Nix (the community—and at times, the technology), and my Dendritic libraries are a way of giving back and trying to achieve something better than what we have now.

Having said that, my time (as is everyone's) is limited on this earth. I love creating tools for people like me—developers, and more specifically, for Nix people.

I have some plans for next year (2026) regarding denful and other Nix-related projects. I'd like to spend more time creating things that are valuable for other people like me.

If you like Den and the effort I'm putting into it and other Dendritic libraries to create high-quality Nix, consider sponsoring vic.