omnixient/docs/packs.md
Padreug a31369fea8 docs: point template + install docs at the real Omnixient repo
Replace the <your-org> placeholders with the actual public URL
(git+https://git.atitlan.io/aiolabs/omnixient, https so forkers need no
SSH), and switch the packs standalone-import example from git+ssh to the
same https URL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 11:45:27 +02:00

249 lines
10 KiB
Markdown

# Packs — the core + opt-in goodies model
Omnixient is structured as a **core** that every host gets, plus **opt-in
packs** you switch on for the goodies you want:
```nix
omni.packs.media.enable = true; # gimp, inkscape, mpv, obs, …
omni.packs.development.enable = true; # editors, LSPs, compilers, container CLIs
omni.packs.bitcoin.enable = true; # a real bitcoind + lightning node
```
The same pack definitions are exposed as reusable **`flake.nixosModules`**,
so a *second* Omnixient host (or someone else's NixOS entirely) can consume
them with a different subset — install the core, add the goodies you want.
> **TL;DR**
> - Core = base system + Hyprland desktop + theming (always on).
> - Packs = opt-in bundles of packages/services/config under
> `omni.packs.<name>.enable`.
> - `lnbits` / `aiolabs` / `bitcoin` are **standalone-importable** — usable
> on a non-Omnixient NixOS via `inputs.omni.nixosModules.<pack>`.
> - The legacy `omni.preset` / `omni.features.*` knobs still work —
> they're now a friendly front-end that drives packs (see
> [Compatibility bridge](#compatibility-bridge)).
## What's core vs. what's a pack
| Layer | Contents | Toggle |
|---|---|---|
| **Core** (always) | base packages + fonts, Hyprland desktop, theming/colors, walker/menus, boot, security, services, hardware, users, secrets, `nh`, MCP | — |
| **Packs** (opt-in) | see catalog below | `omni.packs.<name>.enable` |
Theming and the Hyprland desktop are deliberately **core** (Omnixient is a
desktop distro; there's no headless target yet). If a headless/server
target is ever needed, the desktop becomes its own pack — see
[Authoring a pack](#authoring-a-new-pack).
## Pack catalog
| Pack | Provides | Standalone-importable? |
|---|---|---|
| `media` | players (mpv/vlc), image/video editing (gimp, inkscape, krita, kdenlive), screen capture, PDF, `morph-py`, openscad | no (desktop bundle) |
| `development` | editors/IDEs, LSPs, debuggers, build tools, compilers, container CLIs, DB clients, cloud tooling, `git`/`npm`, dev manpages, and the richer `modules/development.nix` dev module | no |
| `gaming` | Steam (+ firewall), Lutris, Wine, performance tools | no |
| `office` | browsers, comms (Signal/Element/Ferdium), office suites, notes, password managers, sync/backup | no |
| `containers` | Docker + Podman (unified daemon config) | no |
| `lnbits` | the LNbits multi-project dev environment (`modules/dev-env`: worktrees, regtest, tmux, pre-commit hook) | **yes** |
| `aiolabs` | the aiolabs project list + deploy targets + tmux sessions, on top of `lnbits` | **yes** |
| `bitcoin` | a real Bitcoin/Lightning node via nix-bitcoin (bitcoind + clightning) | **yes** (opt-in import) |
Each pack's `enable` carries a one-line description — discover them with:
```sh
nix eval .#nixosConfigurations.omni.options.omni.packs --apply builtins.attrNames
man configuration.nix # then search /omni.packs
```
## Enabling packs
### On an Omnixient host
Set them in your host config (`hosts/<name>/default.nix`) or
`configuration.nix`:
```nix
omni.packs = {
development.enable = true;
containers.enable = true;
media.enable = true;
};
```
`gizmo` (a laptop) and `omni` (the desktop) can enable different
subsets from the same definitions — that's the point.
### Via presets / features (the friendly front-end)
The legacy knobs still work and now drive packs through the
[compatibility bridge](#compatibility-bridge):
```nix
omni.preset = "developer"; # → development, containers, media, office packs + theming
omni.features.media = true; # → omni.packs.media.enable
```
Use whichever you prefer; explicit `omni.packs.*.enable` always wins
over a preset/feature default.
### Standalone — on a non-Omnixient NixOS (the reusable path)
`lnbits`, `aiolabs`, and `bitcoin` don't depend on Omnixient core. Add this
flake as an input and import just the pack you want:
```nix
# your own flake.nix
inputs.omni.url = "git+https://git.atitlan.io/aiolabs/omnixient";
# your host's modules
{
imports = [ inputs.omni.nixosModules.lnbits ];
omni.packs.lnbits.enable = true;
dev-env.user = "alice"; # configure via the standard dev-env.* options
dev-env.root = "/home/alice/dev";
}
```
You get the full LNbits dev environment without Hyprland, themes, or any
Omnixient host config. This is what makes the separate `lnbits-sensei`
scaffold obsolete — the dev-env is now a clean importable module.
> `nixosModules.bitcoin` additionally needs `inputs.nix-bitcoin` available
> in your `specialArgs` (Omnixient's own hosts get it via `lib/mksystem.nix`).
> See [The bitcoin pack](#the-bitcoin-pack).
## Compatibility bridge
`modules/packs/compat.nix` maps the legacy `omni.features.*` booleans
onto the new pack toggles with `mkDefault`:
```nix
omni.packs.media.enable = mkDefault (cfg.features.media or false);
omni.packs.development.enable = mkDefault (cfg.features.coding or false);
omni.packs.containers.enable = mkDefault (cfg.features.containers or false);
omni.packs.gaming.enable = mkDefault (cfg.features.gaming or false);
omni.packs.office.enable = mkDefault ((cfg.features.office or false) || (cfg.features.communication or false));
```
So the chain is **preset → features → packs**. `mkDefault` means an
explicit `omni.packs.<x>.enable = true|false` on a host always wins.
The bridge lives in the aggregator (loaded only with full Omnixient), so a
standalone consumer importing a single pack never pulls in the
`omni.features` dependency.
Non-pack features (`customThemes`, `wallpaperEffects`, `virtualization`,
`backup`) remain plain `omni.features.*` — they're core knobs, not
packs.
## The bitcoin pack
`omni.packs.bitcoin` runs a real node via
[nix-bitcoin](https://github.com/fort-nix/nix-bitcoin):
```nix
{
imports = [ inputs.omni.nixosModules.bitcoin ]; # or hosts/<name> imports modules/packs/bitcoin.nix
omni.packs.bitcoin.enable = true;
omni.packs.bitcoin.operatorName = "padreug"; # account granted node CLI access
}
```
It imports only `nix-bitcoin.nixosModules.default` (NOT the
`secure-node`/`hardened` presets, which are server-oriented and hostile to
a daily-driver desktop), enables `bitcoind` + `clightning`, sets
`nix-bitcoin.generateSecrets = true` (auto-generates node secrets at
activation), and grants the operator CLI access.
**Why it's not in the always-loaded aggregator:** nix-bitcoin's default
module fires a secrets assertion merely from being imported — even with no
services enabled. Importing it inert on every host would break eval. So
`bitcoin` is **not** in `modules/packs/default.nix`; it's exposed as
`nixosModules.bitcoin` and a host opts in by importing it explicitly.
(`inputs.nix-bitcoin` is supplied to Omnixient hosts via `lib/mksystem.nix`'s
`specialArgs`.)
**Caveats** (see also `docs/system-map.md`):
- `generateSecrets` is the simplest path; for unified secret management,
wire sops-nix into nix-bitcoin's `secretsDir` (`secretsSetupMethod =
"manual"`) — note LND has an open issue under manual secrets.
- nix-bitcoin follows our `nixpkgs`; it pins its own upstream for *tested*
builds, so if a service fails to build after enabling, consider dropping
the `follows` in `flake.nix`.
## Authoring a new pack
1. Create `modules/packs/<name>.nix` using the canonical template:
```nix
{ config, lib, pkgs, ... }:
let cfg = config.omni.packs.<name>; in {
options.omni.packs.<name>.enable =
lib.mkEnableOption "<one-line description>";
config = lib.mkIf cfg.enable {
# ONLY standard NixOS options + this pack's own bundled modules.
environment.systemPackages = with pkgs; [ … ];
};
}
```
2. **Register it** in `modules/packs/default.nix` (the aggregator) so the
option namespace always exists and the body is inert until enabled.
*Exception:* if the pack imports a module with import-time side effects
(assertions, global `disabledModules` — like nix-bitcoin), do **not**
add it to the aggregator; expose it only as a `nixosModules` output for
explicit per-host import.
3. **Expose it** as a flake output in `flake.nix`:
`nixosModules.<name> = import ./modules/packs/<name>.nix;`.
4. If it should be **standalone-importable**, the module must declare its
own option namespace and touch only standard NixOS options + its own
bundled modules — **never read `config.omni.<core>`**. (This is why
`dev-env` was decoupled from `omni.*`; the `lnbits` pack inherits
that.)
5. *(Optional)* add a bridge line in `modules/packs/compat.nix` if a
legacy `omni.features.*` flag should imply the new pack.
6. Sub-options live alongside `enable`, e.g.
`omni.packs.bitcoin.operatorName`.
## Internals & wiring
- **Aggregator:** `modules/packs/default.nix` imports every (inert) pack +
`compat.nix`.
- **Wired in once:** `lib/mksystem.nix` imports `../modules/packs`, so all
hosts built through `mkSystem` get the pack option namespace.
- **ISO stays pack-free:** the live ISO (`iso.nix`) is a direct
`nixosSystem` call that bypasses `mksystem`, so it never loads packs. Any
core/ISO-shared module that references `config.omni.packs.*` must guard
with `or false`.
- **Reusable outputs:** `nixosModules.{media,development,gaming,office,
containers,lnbits,aiolabs,bitcoin}` in `flake.nix`.
- **dev-env relationship:** the `lnbits` pack `imports = [ ../dev-env ]`
and enables it; configure via the standard `dev-env.*` options (see
`modules/dev-env/README.md`).
## Verifying a pack change
Pack edits should be **closure-neutral** unless you intend a behavior
change. Work in a worktree, build-only (never `switch`), and diff against
a baseline:
```sh
# baseline (once)
nix build .#nixosConfigurations.omni.config.system.build.toplevel -o ~/omni-baseline
# after your change
nix build .#nixosConfigurations.omni.config.system.build.toplevel -o ~/omni-after
nix store diff-closures ~/omni-baseline ~/omni-after # empty = behavior-identical
nix flake check --keep-going # schema + dev-env smoke + VM boot
```
For risky work on this live daily-driver, run Claude under
`scripts/sandbox-claude.sh <worktree>` — it hard-denies `nixos-rebuild
switch|test|boot`, `omni-rebuild`, sudo, push, and network, leaving
build/eval/in-worktree-git allowed. You apply the real `switch` only once
the gates are green.