collective-public

The public part of a personal Nix configuration. It holds collective-lib, a Nix library with a type system for Nix values and a test framework; modules for Nix-on-Droid, NixOS and Home Manager; a package for renting GPUs; and scripts that turn a Windows machine into a NixOS-WSL development box.

It is published from a folder inside a private configuration repository, and its own README advises against depending on it directly: it changes whenever the configuration does. nix-reflect, a Nix evaluator written in Nix, is built on it.

collective-lib

A library of plain Nix functions, exposed as the flake's lib output. lib.typed is nixpkgs' lib merged with everything below, 1,022 names in all, so code can switch to it with one with.

The largest of collective-lib's 37 files (16,061 lines in all), counted when the page was built
FileLinesWhat it is
typelib.nix5,388a type system for Nix values, outside the module system
log.nix1,383structured logging and tracing, aware of the type system
functions.nix1,014missing functional basics and variadic functions
strings/strings.nix971string formatting and multi-line indentation
attrsets.nix740attribute-set helpers
tests.nix660the test framework: suites, expectations, runners
script-utils/ansi-utils.nix567terminal colours and styles for generated scripts
colors.nix556colour schemes and hex and RGB formatting for configs
script-utils/log-utils.nix527logging for generated shell scripts
dispatchlib.nix460dispatch on a value's type or shape
wm.nix407window-management intents and key bindings, independent of the window manager
nixlike.el397Emacs: evaluate Nix on the fly in a Nix or Tvix REPL

Types for Nix values

The largest part is a type system that lives outside NixOS's module system. Objects are attribute sets carrying their type and supertype; fields are checked when an object is created and again when it changes, so a value built as a type stays valid for it. Built-in values are wrapped (Int, Bool, …) and the raw value is in .value.

How the type system bootstraps itself The bootstrap of collective-lib's type system, as typelib.nix describes it. A minimal HyperType, built by hand, defines what fields a type has. MetaType is a subtype of HyperType and also an instance of it, and adds methods. ProtoType is a subtype and an instance of MetaType, and binds methods when instantiated. Type is a subtype and instance of ProtoType and is the base of every other type, from Int and Bool to types you declare. HyperTypebuilt by hand MetaTypeadds methods ProtoTypebinds methods Typebase of every typeInt · Bool · yours

Each type is a subtype and an instance of the one before it. Every example below was evaluated against this flake by capture.py when the page was built.

Declare a type, construct it positionally

with lib.typed;
let A = Type "A" { fields = [ { a = "int"; } { b = Int; } { c = Default Int 5; } ]; };
    x = A 1 (Int 2);
in [ x.a x.b.value x.c.value ]
[1,2,5]

A field of the wrong type is refused

with lib.typed;
let A = Type "A" { fields = [ { a = "int"; } { b = Int; } ]; };
in (A 1 "two").b.value
error: Upcast error:

Raw values keep their builtin type

with lib.typed; typeOf true
"bool"

Wrapped values report their type

with lib.typed; typeOf (Bool true) == Bool
true

But Int does not check its argument

with lib.typed; (Int "123").value
"123"

The README is older than the code. Its typed-function example uses defun, which now lives inside the logging layer and is not exported, and its examples use a String type that lib.typed does not export. The eval and parser modules it describes have moved to nix-reflect.

Tests

tests.nix is a small test framework: nested attribute sets of expectations (expect.eq, expect.error, …) gathered into suites, run with lib._tests.run {} or one module at a time. Each library module carries its own suite, and nix-reflect's suites use the same framework.

Each module's own suite, run one module at a time by capture.py: 1093 of 1109 tests pass
ModulePassedTestsResult
attrsets127127all pass
clib1212all pass
collections6262all pass
colors88all pass
data1313all pass
dispatchlib2727all pass
errors1212all pass
fan11all pass
functions134134all pass
lists7070all pass
log5656all pass
script-utils31387 failing
strings85948 failing, 1 skipped
syntax44all pass
tests33all pass
typelib448448all pass

The failures are in two places: seven tests of stripping styled text back to plain text in the terminal-styling helpers, and eight tests of padding strings to a display width when they contain multi-byte characters.

Modules and packages

Nix-on-Droid

  • supervisord: a small service manager that keeps daemons running across app restarts; other modules declare programs for it, and Home Manager modules can too.
  • nod-exec: a TCP command server inside Nix-on-Droid, port 18357 by default, used by NOD Term and X11 Droid.
  • pulseserver: health-checks and restarts the PulseServer app.
  • sshd, termux, session, dbus, fonts: an SSH server under supervision, Termux integration, restarting daemons after Android stops the app, a session bus and fonts.

Everywhere else

  • unexpected-keyboard: a 1,500-line library for building keyboard layouts in Nix, which generates the layouts loaded by the Omni keyboard.
  • handheld-daemon (NixOS): Handheld Daemon for handheld gaming PCs, without letting power-profiles-daemon start again.
  • vastai: the Vast.ai GPU-rental command-line client, with Docker images and provisioning scripts.

Windows development boxes

The windows/ scripts provision a development machine from a fresh Windows host in one command: they install WSL2 and NixOS-WSL, cap WSL at 48 GB of memory and a 1.5 TiB virtual disk (a ceiling, not an allocation), clone the private configuration and apply it, then manage the Windows side declaratively from inside WSL, installing applications with winget and setting power options.

Where it comes from

Where this repository comes from How collective-public is published. It is a folder, collective-public/, inside a private configuration repository. A git pre-push hook there notices pushes that touch the folder and makes a commit on the public repository whose tree is exactly the folder and whose parent is the public repository's current tip, then fast-forwards it. Other projects take it as a flake input: nix-reflect builds on collective-lib and its test framework, and the private configuration uses all of it. private configurationcollective-public/ folder pre-push hooksame tree, fast-forward collective-publicthis repository nix-reflectcollective-lib + tests the configurationmodules · library

Because every push replaces the whole tree, this site is written in the private repository's collective-public/ folder like everything else here.