StewOS
Source

Architecture

The organising rule is that nothing is discovered by scanning a directory. Adding a module means adding a line to that platform's default.nix; adding a package means adding a line to pkgs/default.nix. Every import is greppable, and the complete list of what exists is always a file you can read.

flake.nix          Inputs and every output, declared explicitly.
                   The only place flake inputs are captured.
overlays/          The StewOS overlay, which adds pkgs.stewos.*
pkgs/              Package definitions; plain callPackage derivations
lib/               Pure helpers: takes a nixpkgs lib, returns functions
modules/
  common/          Options shared by NixOS and Home-Manager
  nixos/           NixOS system modules
  home-manager/    Home-Manager user modules
  nix-darwin/      macOS system modules
hosts/             Machine-specific configuration only
templates/         Flake templates for new systems

Modules

Modules are ordinary module files, imported by path. inputs arrives through specialArgs, which flake.nix sets once:

{ inputs, lib, config, pkgs, ... }:
let
  cfg = config.stewos.moduleName;
in
{
  options.stewos.moduleName = {
    enable = lib.mkEnableOption "feature description";
  };

  config = lib.mkIf cfg.enable {
    # configuration
  };
}

Take inputs only if you actually use it. Because modules are paths, the module system deduplicates them, so importing the same one twice is harmless.

A module that needs several files gets a directory with a default.nix; everything else is a single {name}.nix.

Packages and the overlay

Packages under pkgs/ never reference flake inputs. Anything that must come from an input — a flake = false source tree, a colour scheme — is injected into the scope by overlays/default.nix and resolved by argument name. That is what keeps pkgs/ buildable against a plain nixpkgs.

The modules expect two overlays on pkgs: stewos.overlays.default and NUR.

Hosts

hosts/{hostname}/ holds configuration only; the outputs are declared in flake.nix, so the full set of configurations is visible in one file. hosts/common/workstation.nix carries the policy the two Framework machines share.

system.stateVersion deliberately stays per-host and must never move into shared configuration. So does hibernation: the desktop cannot survive an aborted S4 on its iGPU, and the laptop can.

The desktop option surface names no compositor

stewos.desktop is deliberately platform-neutral. A binding is { key, modifiers, useModifier, platforms, action | command }, where key and action are neutral names. Each backend — Hyprland on Linux, Aerospace on macOS — owns three translation tables (modifiers, keys, actions) and asserts on anything it does not implement.

Adding an action means adding it to modules/home-manager/desktop/vocabulary.nix plus at least one backend's actions table. Hyprland and Rofi vocabulary stays out of options.nix.

Each backend contributes its default keymap through stewos.desktop.bindings with per-field mkDefault, which is what lets a host retarget or disable a StewOS-provided binding by name.