| .onedev | Loading last commit info... | |
| docs | ||
| hosts | ||
| lib | ||
| modules | ||
| .dir-locals.el | ||
| .gitignore | ||
| .onedev-buildspec.yml.bak | ||
| .sops.yaml | ||
| README.org | ||
| flake.lock | ||
| flake.nix | ||
| homeModules.nix | ||
| outputs.nix |
Architecture
Everything runs through flake-parts with easy-hosts for host management and import-tree for automatic module discovery.
Modules under ./modules/ are imported automatically. No manual registration needed. Drop a .nix file in the right directory and it gets picked up.
Shared configuration values (SSH keys, IPs, domains) live in lib/default.nix as sportLib and are injected into all NixOS modules via specialArgs and into home-manager modules via extraSpecialArgs. No relative imports needed.
sport.*
All configuration is unified under the sport namespace. Hosts declare what they are and what they need. Modules react to those declarations.
sport = {
profiles.graphical.enable = true;
profiles.workstation.enable = true;
device = {
cpu = "intel";
gpu = "intel";
hasBluetooth = true;
};
system.boot.loader = "grub";
programs.wm = "mango";
services = {
navidrome.enable = true;
rkvm.enable = true;
};
};
Profiles
Role flags that modules gate their configuration on.
| Profile | Purpose |
|---|---|
graphical |
Has a display. Enables fonts, PipeWire, portals. |
headless |
No display. Server hardening. |
server |
Runs services. |
laptop |
Portable. Power management. |
workstation |
Heavy desktop. Audiophile PipeWire, JACK. |
Device
Hardware declarations. Modules activate based on these values.
| Option | Values | Effect |
|---|---|---|
cpu |
"intel", "amd", null |
Microcode, kernel params |
gpu |
"nvidia", "amd", "intel", null |
GPU drivers, VAAPI |
keyboard |
"us", "de", "piccolo" |
Keymap, keyd for piccolo |
hasTouch |
bool | Touchscreen support |
hasBluetooth |
bool | Bluetooth + blueman |
hasPrinter |
bool | CUPS printing |
hasNitrokey |
bool | Nitrokey U2F, smartcards |
hasLogitech |
bool | Logitech wireless |
Services
Every service has a standardized interface: enable, host, port, domain.
Enable a service and it configures itself, including the Caddy reverse proxy where applicable. Override port or domain if the defaults don't work for your setup.
Programs
Desktop environment and program selection.
sport.programs = {
wm = "mango"; # sway | mango | xfce | exwm
greeter = "tuigreet"; # tuigreet | sddm
terminal = "foot"; # foot | kitty
shell = "fish"; # fish | bash
browser = "brave"; # brave | qutebrowser | firefox
};
Hosts
| Host | Arch | Role | Location |
|---|---|---|---|
md |
x8664 | Primary desktop workstation | Local |
sportmacher |
x8664 | Piccolo Series 71 tablet | Local |
chiefsosa |
aarch64 | Production server | Hetzner VPS |
moneyspread |
x8664 | Secondary server | Hetzner VPS |
md
Primary workstation. Intel CPU + Intel GPU, Mango window manager, ZFS on LUKS with encrypted disk, QMK keyboard support (Keychron), audiophile PipeWire. Runs Navidrome, OBS Studio, rkvm (server), VMs locally. ZFS snapshots via sanoid/syncoid to a secondary encrypted drive.
sportmacher
X-Plus Piccolo Series 71 tablet. 7-inch with Intel N-series, DSI panel needing rotation, touchscreen, trackpoint. Mango window manager, tuigreet as greeter, keyd for keyboard remapping. Connected to md via rkvm (client) over Tinc mesh VPN.
chiefsosa
Hetzner aarch64 VPS running the full service stack. See the services inventory below. All services toggled via sport.services.<name>.enable = true. SSH access via sshpiper on port 22 (routes to git, pb, or the main SSH on 23230). Box86/Box64 for running TeamSpeak3 on ARM.
moneyspread
Hetzner x8664 VPS at moneyspread.st. Runs the "wired" IRC network via Sable, plus Photoflow photo gallery, Soft Serve git hosting, snips pastebin, senpai IRC client, TeamSpeak6 server, step-ca SSH certificate authority, Coder remote dev environments, and a wishlist SSH directory. Uses server-base with disko ZFS, HPN SSH on port 23230, sshpiper on port 22 routing to git/pb/senpai/shell. Nginx handles TLS for all moneyspread.st subdomains.
Home-manager is configured for the cashmere user via cashmere-moneyspread.nix, providing fish shell, starship prompt, atuin history sync, tmux (with noctalia theming), pueue background task manager, and standard CLI tools (eza, bat, btop, zoxide, yazi).
Remote Operations Workflow
Kitty's SSH kitten (kitten ssh) replaces plain ssh for all interactive sessions. It automatically copies shell integration, terminfo, fish config, starship prompt, and bat config to the remote host. Connections are shared within a kitty instance for instant subsequent logins.
Per-host visual identity
Each host gets a distinct terminal color shift via ~/.config/kitty/ssh.conf:
moneyspread— cold blue tint (you're on the secondary server)chiefsosa— warm amber (primary server)
The KITTY_SSH_HOST and KITTY_HOST_ICON environment variables are set on the remote, available to prompts and scripts.
Commands
| Command | Effect |
|---|---|
kssh moneyspread |
SSH via kitten (stays in current terminal) |
kssh moneyspread --dash |
3-pane kitty dashboard: shell + htop + journalctl -f |
kssh --pick |
Interactive host picker via gum |
beam moneyspread:~/logs/app.log ./ |
Download file from remote (kitten transfer) |
beam ./config.nix chiefsosa:~/nix/ |
Upload file to remote |
beam -d moneyspread:~/big.db ./ |
Transfer with rsync delta mode (large files) |
rrun moneyspread nix-collect-garbage |
Queue remote command in pueue (desktop notification on done) |
ops |
Interactive server operations menu (gum-powered) |
ops menu
The ops command opens an interactive gum-powered menu for server management:
- dashboard — spawns a new kitty window with 3-pane SSH layout
- shell — plain SSH session
- services —
systemctl list-unitsoverview - logs — pick a service unit, follow its journal
- deploy — triggers
deploy-rsfor the selected host - gc — queues
nix-collect-garbage -dvia pueue - beam — interactive file upload/download
- disk — remote
ncdusession - irc — opens senpai IRC client on the remote
Accessible from mango via Alt+Super → l → m.
Kitty keybindings
| Keybind | Action |
|---|---|
Ctrl+Space → s |
SSH to moneyspread (new kitty tab) |
Ctrl+Space → Shift+S |
SSH to chiefsosa (new kitty tab) |
Ctrl+Space → Shift+V |
SSH to moneyspread in vertical split |
Ctrl+Space → Shift+X |
Close all shared SSH connections |
Background tasks (pueue)
The pueued daemon runs as a systemd user service. Completed tasks trigger desktop notifications via notify-send.
| Abbreviation | Expands to |
|---|---|
pq |
pueue add -- |
pqs |
pueue status |
pql |
pueue log |
pqf |
pueue follow |
pqk |
pueue kill |
pqc |
pueue clean |
The pueue status scratchpad is available in mango via Alt+Super → l → q.
Services Inventory
All services run on chiefsosa unless noted. Domain defaults use cashmere.rs.
| Service | Port | Domain | Description |
|---|---|---|---|
caddy |
443 | (all) | Reverse proxy + TLS via ACME |
sshpiper |
22 | (direct) | SSH multiplexer (git, pb, shell) |
ergochat |
6697 | irc.cashmere.rs |
IRCv3 server (TLS) |
soju |
6699 | bouncer.cashmere.rs |
IRC bouncer |
catchmeonirc |
8787 | (internal) | IRC bot (sysinfo, webhooks, RSS, mesh) |
kiwiirc |
8669 | chat.cashmere.rs |
Web IRC client |
convos |
8668 | irc.chat.cashmere.rs |
Web IRC client (alt) |
sharkey |
8487 | social.cashmere.rs |
Fediverse (Misskey fork) |
matrix |
6167 | matrix.cashmere.rs |
Matrix homeserver |
mautrix |
29318 | (internal) | Matrix bridge |
bitlbee |
6667 | bridge.cashmere.rs |
IM-to-IRC gateway |
git (Soft Serve) |
23232 | git.cashmere.rs |
Git hosting (SSH on 6611 via sshpiper) |
onedev |
6610 | dev.cashmere.rs |
CI/CD + Git forge |
snips |
8580 | pb.cashmere.rs |
Pastebin (SSH paste via sshpiper) |
grafana |
3000 | grafana.cashmere.rs |
Monitoring dashboards |
umami |
5932 | umami.cashmere.rs |
Web analytics |
screego |
5050 | screen.cashmere.rs |
Screen sharing |
send |
4594 | send.cashmere.rs |
Encrypted file sharing |
atuin |
8887 | atuin.cashmere.rs |
Shell history sync |
tt-rss |
rss.cashmere.rs |
RSS reader | |
qbittorrent |
8938 | qb.cashmere.rs |
Torrent client |
wishlist |
2200 | (SSH) | SSH directory / menu |
pgs |
2281 | pgs.cashmere.rs |
Static site hosting via SSH |
nix-cache |
5000 | cache.cashmere.rs |
Binary cache |
stayem |
8080 | (internal) | Custom application |
strapi |
1338 | dashboard.atelier-mai.art |
CMS backend |
atelier-frontend |
19997 | atelier-mai.art |
Portfolio frontend |
catchmyorg |
8930 | cashmere.rs |
Org-mode site generator |
teamspeak3-arm |
9987 | ts.cashmere.rs |
TeamSpeak3 (ARM via Box86/Box64) |
storagebox |
(mount) | Hetzner Storage Box CIFS mount | |
borgbackup |
(scheduled) | Borg backups to storagebox | |
navidrome |
4533 | (local) | Music server (runs on md) |
rkvm |
5258 | (mesh) | KVM switch (server on md) |
teamspeak3 |
9987 | (local) | TeamSpeak3 (runs on md) |
lastfm-playlist |
(local) | Last.fm playlist creator (runs on md) |
|
| moneyspread | |||
sable |
6697 | irc.moneyspread.st |
Sable IRC server ("wired" network) |
nginx |
443 | (all moneyspread.st) |
Reverse proxy + TLS via ACME |
photoflow |
moneyspread.st |
Photo gallery | |
sshpiper |
22 | (direct) | SSH multiplexer (git, pb, senpai, shell) |
git (Soft Serve) |
23232 | git.moneyspread.st |
Git hosting (SSH on 6622 via sshpiper) |
snips |
8580 | pb.moneyspread.st |
SSH pastebin |
senpai |
2266 | (SSH via wishlist) | TUI IRC client on wired |
wishlist |
2200 | (SSH) | SSH directory / menu |
Caddy
Central reverse proxy and TLS terminator for the entire infrastructure. Nearly every service registers a virtual host with Caddy. ACME certificates are provisioned automatically via Let's Encrypt.
Services that need direct TLS access (ergochat, soju) read Caddy's certificates from disk. sportLib.mkCaddyCertFixService creates a helper systemd unit that fixes file permissions so the service can read the cert, and a dummy Caddy vhost that triggers certificate provisioning for the domain.
The Caddy module also defines several "infrastructure" vhosts directly: Matrix .well-known delegation on the bare domain, Grafana, Screego, Atuin, and the musicbot web UI.
IRC Stack (ergochat + soju + catchmeonirc)
Three services form the IRC infrastructure. See IRC Infrastructure Reference for full details.
- ergochat — IRCv3 server. Network name
cashmerehq. Plaintext on:6668(used internally by borgbackup notifications and deploy-notify), TLS on:6697. Shares Caddy's Let's Encrypt certs. Multiclient and history enabled. - soju — IRC bouncer. TLS-only on
:6699, same cert-sharing pattern. Depends on ergochat. - catchmeonirc — Multi-purpose IRC bot. Connects to ergochat via SASL as
catchmebot. Joins#dev,#rss,#sysinfo,#notify. Features: RSS feed aggregation (NixOS news, HM news, wiki changes), webhook receiver for CI notifications, URL tracking, system info monitoring across hosts via mesh VPN SSH. Has 4 SOPS secrets.
Other internal services also post to IRC: borgbackup sends backup reports to #sysinfo, and a deploy-notify service diffs NixOS profile changes on boot and posts to #sysinfo.
OneDev + CI
OneDev is a self-hosted DevOps platform (Git hosting + CI/CD) at dev.cashmere.rs. Uses an embedded HSQLDB database. The ExecStartPre script auto-backs up the database before version upgrades (keeping 3 snapshots). See OneDev Operations Reference for upgrade procedures.
The onedev-org companion module injects a custom plugin (from the onedev-org flake input) that adds Org-mode rendering via pandoc.
The ci module sets up the CI/CD pipeline:
- Creates a
ciuser on chiefsosa with a SOPS-managed SSH key (also deployed to theonedevuser) - Both
ciandonedevare Nix trusted-users with doas rules fornix,nix-env, andswitch-to-configuration - On all deploy targets (chiefsosa, md, sportmacher), the CI SSH pubkey is authorized for the
cashmereuser, enabling deploy-rs to SSH in
SSHPiper
SSH reverse proxy sitting on port 22 — the main entry point for all SSH traffic. Routes connections based on username:
git-> Soft Serve (127.0.0.1:6611)pb-> snips pastebin (127.0.0.1:2223).*(everything else) -> system SSH (127.0.0.1:23230)
Auto-injects authorized keys and generates an upstream ed25519 key for transparent proxying.
Git (Soft Serve)
Git hosting via Soft Serve. SSH listener on port 22 (routed through sshpiper), HTTP reverse-proxied by Caddy at git.cashmere.rs. Initial admin key set from config.sport.system.sshKeys.
Nix Binary Cache
Harmonia-based binary cache at cache.cashmere.rs. The signing key (SOPS-managed) is shared with the onedev group so CI builds can sign store paths. A daily systemd timer runs nix store sign --recursive /run/current-system to ensure the current system closure is signed and cached.
Backups (borgbackup + storagebox)
Daily encrypted Borg backups to a Hetzner Storage Box. Backs up state directories for: soft-serve, matrix, stayem, sharkey, mautrix, caddy, ergochat, atuin, pgs, bitlbee, grafana, umami, onedev, and /srv/www. Encryption uses repokey-blake2 with a SOPS-managed passphrase. Retention: 7 daily, 4 weekly, 6 monthly.
Post-backup, an IRC notification script connects to ergochat's plaintext port and posts stats (duration, files changed, deduplicated size) to #sysinfo.
The storagebox module mounts the same Hetzner Storage Box via SSHFS at /mnt/storagebox using the same SSH key.
TeamSpeak3
Three modules handle TeamSpeak across hosts:
server-teamspeak3— Native TS3 server + TS3AudioBot in Podman onmd. Bot mounts/home/cashmere/Musicread-only.server-teamspeak3-arm— ARM64 variant onchiefsosausing Box86/Box64 emulation (ghcr.io/sobc/ts3server-for-arm). AudioBot streams music from Navidrome onmdvia Tinc mesh.server-teamspeak3-proxy— Socat port forwarder onchiefsosathat proxies TS3 UDP voice (9987) and TCP file transfer (30033) tomdover Tinc mesh.
catchMyorg
Org-mode notes publisher served at the bare domain cashmere.rs. Has a SOPS-managed SSH key for git operations. Manages a "notes" vault at /var/lib/catchmyorg/notes with git-backed auto-commit.
Networking
Tinc VPN
All hosts are connected via a Tinc mesh VPN overlay network. chiefsosa acts as the hub node.
| Host | Mesh IP | Role |
|---|---|---|
chiefsosa |
10.13.37.1 |
Hub |
md |
10.13.37.2 |
Workstation |
sportmacher |
10.13.37.3 |
Workstation |
moneyspread |
10.13.37.4 |
Server |
Tinc enables:
- rkvm keyboard/mouse sharing between
mdandsportmacher - TeamSpeak3 proxy from
chiefsosatomd(voice over UDP, file transfer over TCP) - catchmeonirc remote system monitoring over SSH
- Direct inter-host access without exposing services to the internet
Per-host Ed25519 keys are managed via SOPS. The module is at modules/nixosModules/base/tinc.nix. See Tinc VPN Reference for full details.
Yggdrasil
All hosts also run Yggdrasil, an IPv6 mesh overlay network. Peers through German public nodes via TLS. Public-IP servers (chiefsosa, moneyspread) also peer directly with each other for resilience. The ygg0 interface is trusted. See Yggdrasil VPN Reference for details.
| Host | Yggdrasil IPv6 |
|---|---|
chiefsosa |
201:6904:7fcb:6fa6:b869:48cc:a863:e9a6 |
md |
202:5e97:a5ba:a207:ce38:ac4a:ca3:8f86 |
sportmacher |
200:aa5e:2bcd:b7b3:7852:a02f:77bc:658a |
moneyspread |
200:1c8c:1f5:f25d:1a9f:c191:e656:9636 |
Hostnames resolve as <host>.ygg within the mesh (e.g., chiefsosa.ygg).
Firewall
NixOS firewall is enabled on all hosts. Tinc's tinc.cashmere and Yggdrasil's ygg0 interfaces are trusted (all mesh traffic bypasses firewall rules). Only explicitly listed TCP/UDP ports are opened on public interfaces.
Secrets Management
Secrets are managed with sops-nix using age encryption.
- Each host has its own
secrets/*.yamlfiles underhosts/<hostname>/secrets/ - Age keys are stored at
/keys.txt(md) or/var/lib/sops-nix/key.txt(servers) - Key recipients are defined in
.sops.yamlat the repo root - Secrets are decrypted at activation time and placed under
/run/secrets/ - SOPS templates are used for env files that mix secrets and non-secrets (e.g., catchmeonirc)
To add a secret to a host:
- Add the secret to the host's YAML file:
sops hosts/<hostname>/secrets/secrets.yaml - Reference it in NixOS config:
sops.secrets."my-secret" = { }; - Use the path:
config.sops.secrets."my-secret".path
Nixpkgs Configuration
Nixpkgs is configured identically for both NixOS and home-manager via sportLib.sharedNixpkgsConfig. Both receive the same overlays:
| Overlay | Purpose |
|---|---|
workarounds |
Temporary fixes for broken upstream packages |
custom-packages |
Packages built from modules/packages/ |
NUR |
Nix User Repository |
emacs-overlay |
Latest Emacs builds and MELPA packages |
The workarounds overlay pins or patches packages with upstream issues. Each override has a comment explaining the issue and a link to the fix so it can be removed after a flake update.
Flake Inputs
| Input | Purpose |
|---|---|
flake-parts |
Flake structure framework |
import-tree |
Automatic module discovery |
easy-hosts |
Host registry for NixOS configs |
nixpkgs |
Main package set (unstable) |
nixpkgs-stable |
Stable channel (25.11) |
home-manager |
User environment management |
sops-nix |
Secrets management with age encryption |
deploy-rs |
Remote NixOS deployment |
disko |
Declarative disk partitioning |
nixos-facter-modules |
Hardware detection |
stylix |
System-wide theming |
catppuccin |
Catppuccin theme for home-manager |
noctalia |
Noctalia shell |
mango |
Mango window manager |
ewm |
Emacs window manager |
nixgl |
OpenGL wrapper for non-NixOS |
nix-flatpak |
Declarative Flatpak management |
emacs-overlay |
Latest Emacs + MELPA |
nur |
Nix User Repository |
nix-index-database |
Prebuilt nix-index database |
stayem |
Custom application (private) |
auralis |
Custom tool (private) |
catchmeonirc |
IRC bot (private) |
catchmyorg |
Org-mode site generator (private) |
lastfm-playlist-creator |
Last.fm playlist creator (private) |
onedev-org |
OneDev Org-mode plugin (private) |
ncs |
Custom package repository (private) |
Module Structure
modules/
easy-hosts.nix # Host registry + shared specialArgs
deploy.nix # deploy-rs configuration
hpi.nix # HPI (Human Programming Interface) packages
homeModules.nix # flake-parts option declarations for HM
nixosModules/
base.nix # Aggregator: all base modules
base/ # Core system (boot, users, locale, nix, ...)
tinc.nix # Tinc VPN mesh (IPv4, 10.13.37.x)
yggdrasil.nix # Yggdrasil IPv6 mesh VPN
nixpkgs-config.nix # Shared nixpkgs overlays + config
rkvm.nix # KVM switch (rkvm server/client)
desktop.nix # Aggregator: all desktop modules
desktop/ # Desktop (GPU, steam, xfce, keyd, ...)
options/ # sport.* option declarations
device.nix # sport.device.* definitions
profiles.nix # sport.profiles.* definitions
programs.nix # sport.programs.* definitions
services.nix # sport.services.* definitions
system.nix # sport.system.* definitions
server/
base.nix # Server aggregator (disko, SSH, users)
disk.nix # Configurable disko ZFS
ssh.nix # Configurable SSH (HPN support)
users.nix # Configurable server users
services/ # 40+ self-hosted service modules
homeConfigurations/
cashmere.nix # Standalone + base home-manager config
cashmere-md.nix # md workstation HM overrides
cashmere-moneyspread.nix # moneyspread server HM config
cashmere-x12.nix # x12 tablet HM overrides
wwp.nix # wwp HM config
homeModules/
packages.nix # Package aggregator
packages/ # Categorized packages
core.nix # Essential CLI tools
dev.nix # Development tools
gui.nix # GUI desktop apps
media.nix # Media/audio/video
wayland.nix # Wayland-specific tools
gaming.nix # Gaming (wine, etc.)
ssh.nix # SSH client config + step-ca cert management
tmux.nix # tmux with vi mode, noctalia theming
pueue.nix # Background task queue with notifications
sops.nix # User-level secrets
emacs.nix # Emacs config
kitty/ # kitty terminal + SSH kitten config
mango/ # Mango WM config (per-host)
noctalia/ # Runtime color scheme daemon
... # ~40 more program modules
overlays/
custom-packages.nix # Packages from ncs flake input
workarounds.nix # Temporary upstream fixes
packages/ # Custom package definitions
Centralized Values (sportLib)
All shared values live in lib/default.nix and are available as sportLib in every NixOS module (via specialArgs) and every home-manager module (via extraSpecialArgs).
sportLib.defaultUser # "cashmere"
sportLib.defaultDomain # "cashmere.rs"
sportLib.defaultFlakePath # "/home/cashmere/nix"
sportLib.defaultTimezone # "Europe/Berlin"
sportLib.defaultLocale # "en_US.UTF-8"
sportLib.sshKeys.nitrokey # primary hardware key
sportLib.serverSSHKeys # all keys authorized on servers
sportLib.allSSHKeys # every known key
sportLib.hosts.chiefsosa.ipv4 # "157.180.75.233"
sportLib.hosts.chiefsosa.ipv6 # "2a01:4f9:c013:2fa::1"
sportLib.hosts.mesh.md # "10.13.37.2"
sportLib.mkServiceOption # creates standardized service options
sportLib.mkCaddyProxy # creates Caddy reverse proxy config
sportLib.mkCaddyCertFixService # fixes cert perms for non-Caddy services
sportLib.sharedNixpkgsConfig # nixpkgs config shared across NixOS + HM
Adding a New Host
- Create
hosts/<hostname>/default.nix - Set
sport.*options for profiles, device, services - Add the host to
modules/easy-hosts.nix - Pick which module aggregators to include (
base,desktop,server-base) - Build:
nix build .#nixosConfigurations.<hostname>.config.system.build.toplevel
Minimal example:
# hosts/newhost/default.nix
{ pkgs, sportLib, ... }:
{
sport = {
profiles.graphical.enable = true;
device.cpu = "intel";
device.gpu = "intel";
programs.wm = "mango";
programs.greeter = "tuigreet";
};
networking.hostName = "newhost";
system.stateVersion = "25.05";
}
# in modules/easy-hosts.nix
hosts.newhost = {
path = ../hosts/newhost;
arch = "x86_64";
class = "nixos";
modules = [
self.nixosModules.base
self.nixosModules.desktop
];
};
Adding a New Service (NixOS)
- Create
modules/nixosModules/server/services/<name>.nix - Add
sport.services.<name>option inmodules/nixosModules/options/services.nixusingsportLib.mkServiceOption - Gate the module config on
lib.mkIf config.sport.services.<name>.enable - Enable it in any host:
sport.services.<name>.enable = true - Add the module to easy-hosts for the target host
# modules/nixosModules/options/services.nix (add to options.sport.services)
myapp = mkSvc "myapp" {
port = 8888;
domain = "myapp.${d}";
};
# modules/nixosModules/server/services/myapp.nix
{ ... }:
{
flake.nixosModules.server-myapp =
{
pkgs,
lib,
config,
sportLib,
...
}:
let
cfg = config.sport.services.myapp;
in
{
config = lib.mkIf cfg.enable {
# Caddy reverse proxy (sportLib helper)
services.caddy.virtualHosts.${cfg.domain}.extraConfig = ''
reverse_proxy localhost:${toString cfg.port}
'';
# The actual service
systemd.services.myapp = {
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${pkgs.myapp}/bin/myapp --port ${toString cfg.port}";
DynamicUser = true;
Restart = "always";
};
};
};
};
}
# in modules/easy-hosts.nix, add to the target host's modules list
self.nixosModules.server-myapp
# in hosts/chiefsosa/default.nix (or whichever host)
sport.services.myapp.enable = true;
Adding a New Module (home-manager)
Home-manager modules follow the flake-parts pattern. The outer function receives flake-parts args, the inner function receives home-manager args (including sportLib via extraSpecialArgs). import-tree picks up the file automatically — no manual registration.
- Create
modules/homeModules/<name>.nix - Import it in
modules/homeConfigurations/cashmere.nix(addself.homeModules.<name>)
# modules/homeModules/myapp.nix
{ ... }:
{
flake.homeModules.myapp =
{
pkgs,
config,
lib,
sportLib,
...
}:
{
home.packages = [ pkgs.myapp ];
xdg.configFile."myapp/config.toml".text = ''
user = "${sportLib.defaultUser}"
server = "${sportLib.hosts.chiefsosa.ipv4}"
'';
};
}
# in modules/homeConfigurations/cashmere.nix, add to imports list
self.homeModules.myapp
Server Base
The server-base aggregator (modules/nixosModules/server/base.nix) includes:
disk.nix— Configurable disko ZFS partitioningssh.nix— OpenSSH with optional HPN (High Performance Networking) supportusers.nix— Server user creation with SSH key authorization
modules.disk = {
enable = true;
device = "/dev/sda";
};
modules.users = {
enable = true;
sshKeys = [ sportLib.sshKeys.nitrokey ];
};
modules.ssh = {
enable = true;
useHpn = true;
};
Home Manager Packages
Packages are split into categories. The aggregator homeModules.packages imports all groups. To exclude a group, import individual categories instead of the aggregator.
| Group | What's in it |
|---|---|
core |
CLI essentials, nix tools, file utils |
dev |
Language servers, compilers, build tools |
gui |
Desktop apps, browsers, chat clients |
media |
Audio/video tools, media players |
wayland |
Wayland compositing tools, clipboard, bars |
gaming |
Wine, winetricks, game-related |
Build Commands
# Evaluate all outputs (check for warnings/errors)
nix flake check --no-build
# Build a specific host
nix build .#nixosConfigurations.chiefsosa.config.system.build.toplevel
nix build .#nixosConfigurations.md.config.system.build.toplevel
# Build standalone home-manager config
nix build .#homeConfigurations.cashmere.activationPackage
Deploy
Remote deployments use deploy-rs. All deploy targets use remoteBuild = true and doas for privilege escalation.
| Target | Hostname | SSH Port | Notes |
|---|---|---|---|
chiefsosa |
157.180.75.233 |
23230 | Production server |
moneyspread |
152.53.151.56 |
23230 | Root access |
sportmacher |
10.13.37.3 (Tinc) |
22 | Via Tinc mesh |
md |
10.13.37.2 (Tinc) |
22 | Via Tinc mesh |
deploy .#chiefsosa
deploy .#moneyspread
deploy .#sportmacher
deploy .#md
Documentation
Detailed documentation lives in docs/:
- Tinc VPN Reference — Mesh topology, key management, adding new hosts, services using the mesh
- Yggdrasil VPN Reference — IPv6 mesh overlay, peer config, direct peering, hostnames, relationship to Tinc
- Home Manager Reference — Module architecture, extraSpecialArgs flow, package categories, cashmere vs cashmere-workstation split
- Desktop Environment Reference — Mango/Sway WM setup, keyd piccolo remapping, greeters, Noctalia + Stylix theming, PipeWire audio
- Services Architecture Reference — mkServiceOption pattern, Caddy cert sharing, IRC notification hooks, cross-service dependencies
- Deployment Reference — deploy-rs setup, CI pipeline, doas rules, ephemeral build VPS, end-to-end flow
- OneDev Operations Reference — Upgrade procedures, backup/restore, database management
- IRC Infrastructure Reference — ergochat + soju + catchmeonirc architecture, TLS cert sharing
- Custom Packages Reference — ncs package repository, overlay structure, package categories
- Mango Keybinds — Complete keybind cheatsheet for the Mango window manager