Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a94a749f29 | ||
|
|
1ff333a896 | ||
|
|
9d199bc087 | ||
|
|
c7adcccbb3 | ||
|
|
dcc13f94e0 | ||
|
|
d30d8f9892 | ||
|
|
dfafac8de9 | ||
|
|
d9464009f0 | ||
|
|
d654eac1e2 | ||
|
|
d4e7475db9 |
@@ -42,6 +42,17 @@ prettier formats `*.md`, so **documentation edits must be run through `nix fmt`*
|
|||||||
exactly like code. prettier re-aligns Markdown tables in particular; hand-editing
|
exactly like code. prettier re-aligns Markdown tables in particular; hand-editing
|
||||||
a table almost always leaves it non-conformant and fails the `formatting` check.
|
a table almost always leaves it non-conformant and fails the `formatting` check.
|
||||||
|
|
||||||
|
Prose documentation lives in `docs/` and is **published** to
|
||||||
|
<https://docs.lyrapup.pet/nixfiles/> by the separate `docs-site` repo, which
|
||||||
|
clones this one at build time. Two consequences when editing docs:
|
||||||
|
|
||||||
|
- A markdown file outside `docs/` (other than the root `README.md`) is not
|
||||||
|
synced and will never appear on the site. Put new prose in `docs/`.
|
||||||
|
- Links must follow the rules in the README's "Documentation" section: absolute
|
||||||
|
Gitea URLs to source files, relative links between `docs/` pages, and
|
||||||
|
absolute `docs.lyrapup.pet` URLs from the root README into `docs/`. The site
|
||||||
|
builds non-strict, so a broken link is silent.
|
||||||
|
|
||||||
The CI `formatting` step runs on **every** PR — including docs- and config-only
|
The CI `formatting` step runs on **every** PR — including docs- and config-only
|
||||||
changes — so a Markdown/YAML/JSON edit is format-checked before merge, not just
|
changes — so a Markdown/YAML/JSON edit is format-checked before merge, not just
|
||||||
after it lands on `main`. (The heavier `deadnix`/`statix`/`pre-commit` lints and
|
after it lands on `main`. (The heavier `deadnix`/`statix`/`pre-commit` lints and
|
||||||
|
|||||||
@@ -5,24 +5,25 @@ single flake.
|
|||||||
|
|
||||||
## Hosts
|
## Hosts
|
||||||
|
|
||||||
Defined in the host table in [`flake.nix`](./flake.nix):
|
Defined in the host table in [`flake.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/flake.nix):
|
||||||
|
|
||||||
| Configuration | System | Machine |
|
| Configuration | System | Machine |
|
||||||
| --------------------- | ---------------- | ----------------------------------------------------------------------------------------------------------- |
|
| --------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `lyrathorpe-mbp` | `aarch64-linux` | MacBook Pro (Apple Silicon, Asahi) |
|
| `lyrathorpe-mbp` | `aarch64-linux` | MacBook Pro (Apple Silicon, Asahi) |
|
||||||
| `lyrathorpe-t400` | `x86_64-linux` | ThinkPad T400 — [install notes](./hosts/T400/README.md) |
|
| `lyrathorpe-t400` | `x86_64-linux` | ThinkPad T400 — [install notes](https://docs.lyrapup.pet/nixfiles/hosts/t400/) |
|
||||||
| `lyrathorpe-macpro31` | `x86_64-linux` | Mac Pro 3,1, desktop — [install notes](./hosts/MacPro31/README.md) |
|
| `lyrathorpe-macpro31` | `x86_64-linux` | Mac Pro 3,1, desktop — [install notes](https://docs.lyrapup.pet/nixfiles/hosts/macpro31/) |
|
||||||
| `emmathorpe-edaas` | `x86_64-linux` | Work WSL box (NixOS-WSL) — [notes](./hosts/EDaaS/README.md) |
|
| `emmathorpe-edaas` | `x86_64-linux` | Work WSL box (NixOS-WSL) — [notes](https://docs.lyrapup.pet/nixfiles/hosts/edaas/) |
|
||||||
| `lyrathorpe-rpi5` | `aarch64-linux` | Raspberry Pi 5 headless server: Docker host + nginx reverse proxy — [install notes](./hosts/RPi5/README.md) |
|
| `lyrathorpe-rpi5` | `aarch64-linux` | Raspberry Pi 5 headless server: Docker host + nginx reverse proxy — [install notes](https://docs.lyrapup.pet/nixfiles/hosts/rpi5/) |
|
||||||
| `lyrathorpe-mac` | `aarch64-darwin` | macOS (nix-darwin) — [notes](./hosts/Darwin/README.md) |
|
| `lyrathorpe-zero2w` | `aarch64-linux` | Raspberry Pi Zero 2 W "Psion sidecar": PPP over RS232 + legacy mail proxy — [install notes](https://docs.lyrapup.pet/nixfiles/hosts/pizero2w/) |
|
||||||
|
| `lyrathorpe-mac` | `aarch64-darwin` | macOS (nix-darwin) — [notes](https://docs.lyrapup.pet/nixfiles/hosts/darwin/) |
|
||||||
|
|
||||||
Shared layers: `home` (home-manager: shell, git, editor),
|
Shared layers: `home` (home-manager: shell, git, editor),
|
||||||
`modules/common-nixos.nix` (all NixOS hosts: fonts, nix-ld, caches),
|
`modules/common-nixos.nix` (all NixOS hosts: fonts, nix-ld, caches),
|
||||||
`modules/workstation.nix` (physical graphical hosts: audio, thermald,
|
`modules/workstation.nix` (physical graphical hosts: audio, thermald,
|
||||||
earlyoom, fwupd), `modules/laptop.nix` (laptops: Wi-Fi, Bluetooth, power,
|
earlyoom, fwupd), `modules/laptop.nix` (laptops: Wi-Fi, Bluetooth, power,
|
||||||
lid), `modules/desktop.nix` (wired desktops: NetworkManager), and
|
lid), `modules/desktop.nix` (wired desktops: NetworkManager), and
|
||||||
`modules/ssh.nix` (key-only sshd). The x86 hosts also pull `nixos-hardware`
|
`modules/ssh.nix` (key-only sshd). The x86 hosts and both Raspberry Pis also
|
||||||
profiles. The full module catalogue is below.
|
pull `nixos-hardware` profiles. The full module catalogue is below.
|
||||||
|
|
||||||
## Repository layout
|
## Repository layout
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ flake.nix # inputs, mkHost/mkDarwinHost, the host tables, dev shell
|
|||||||
flake.lock # pinned input revisions (Renovate keeps this fresh)
|
flake.lock # pinned input revisions (Renovate keeps this fresh)
|
||||||
modules/ # reusable NixOS system modules (see "Module catalogue")
|
modules/ # reusable NixOS system modules (see "Module catalogue")
|
||||||
home/ # home-manager profile: shell, git, editor, claude, secret-service, desktop, sway
|
home/ # home-manager profile: shell, git, editor, claude, secret-service, desktop, sway
|
||||||
|
docs/ # all prose documentation; published to docs.lyrapup.pet (see "Documentation")
|
||||||
users/ # identity registry + per-user home extras (see "Users")
|
users/ # identity registry + per-user home extras (see "Users")
|
||||||
hosts/<Name>/ # per-machine config: configuration.nix + hardware-configuration.nix
|
hosts/<Name>/ # per-machine config: configuration.nix + hardware-configuration.nix
|
||||||
lib/ # small pure helpers (currently the Catppuccin Mocha palette)
|
lib/ # small pure helpers (currently the Catppuccin Mocha palette)
|
||||||
@@ -50,22 +52,22 @@ host's table entry.
|
|||||||
|
|
||||||
## Module catalogue
|
## Module catalogue
|
||||||
|
|
||||||
Reusable NixOS modules under [`modules/`](./modules). "Imported by" says how a
|
Reusable NixOS modules under [`modules/`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/modules). "Imported by" says how a
|
||||||
module reaches a host: **baseModules** (every NixOS host, via `flake.nix`),
|
module reaches a host: **baseModules** (every NixOS host, via `flake.nix`),
|
||||||
**host table** (listed explicitly per host in `flake.nix`), or **transitively**
|
**host table** (listed explicitly per host in `flake.nix`), or **transitively**
|
||||||
(pulled in by another module's `imports`).
|
(pulled in by another module's `imports`).
|
||||||
|
|
||||||
| Module | Imported by | What it does / when to use it |
|
| Module | Imported by | What it does / when to use it |
|
||||||
| ------------------ | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------------ | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `common-nixos.nix` | baseModules (all NixOS) | Timezone/locale, store hygiene (auto-optimise, big download buffer, **no** auto-GC), the nix-community binary cache, `nix-ld`, base CLI (`git`, `fastfetch`), and the fleet-wide font stack. |
|
| `common-nixos.nix` | baseModules (all NixOS) | Timezone/locale, store hygiene (auto-optimise, big download buffer, **no** auto-GC), the nix-community binary cache, `nix-ld`, **sudo-rs** in place of sudo, base CLI (`git`, `fastfetch`), and the fleet-wide font stack. |
|
||||||
| `users.nix` | baseModules (all NixOS) | Builds `users.users` from the registry for the host's `hostUsers`; enables zsh; enables Firefox + Thunderbird **only** when `features.swayDesktop.enable` is on. Applies per-user `linger`. |
|
| `users.nix` | baseModules (all NixOS) | Builds `users.users` from the registry for the host's `hostUsers`; enables zsh; enables Firefox + Thunderbird **only** when `features.swayDesktop.enable` is on. Applies per-user `linger`. |
|
||||||
| `features.nix` | baseModules (all NixOS) | Declares the feature-flag options (`features.swayDesktop.enable`, `features.claudeCode.enable`) so any host can read/set them without importing the heavy implementation module, plus the CPU capability fact they derive from (`features.cpu.microarchLevel`) and the assertion that guards it. See "CPU capability gating". |
|
| `features.nix` | baseModules (all NixOS) | Declares the feature-flag options (`features.swayDesktop.enable`, `features.claudeCode.enable`) so any host can read/set them without importing the heavy implementation module, plus the CPU capability fact they derive from (`features.cpu.microarchLevel`) and the assertion that guards it. See "CPU capability gating". |
|
||||||
| `workstation.nix` | transitively (via laptop/desktop) | Form-factor-agnostic base for physical graphical hosts: turns on `swayDesktop`, Dvorak console, PipeWire, firewall (default-deny), fstrim, earlyoom, fwupd, thermald (x86), redistributable fw. |
|
| `workstation.nix` | transitively (via laptop/desktop) | Form-factor-agnostic base for physical graphical hosts: turns on `swayDesktop`, Dvorak console, PipeWire, firewall (default-deny), fstrim, earlyoom, fwupd, thermald (x86), redistributable fw. |
|
||||||
| `laptop.nix` | host table (MBP, T400) | `imports` workstation.nix, then adds the portable bits: iwd Wi-Fi, lid suspend/lock, Bluetooth + blueman. |
|
| `laptop.nix` | host table (MBP, T400) | `imports` workstation.nix, then adds the portable bits: iwd Wi-Fi, lid suspend/lock, Bluetooth + blueman. |
|
||||||
| `desktop.nix` | host table (Mac Pro) | `imports` workstation.nix, then swaps Wi-Fi for wired NetworkManager. Pair with `portable = false` in the host table. |
|
| `desktop.nix` | host table (Mac Pro) | `imports` workstation.nix, then swaps Wi-Fi for wired NetworkManager. Pair with `portable = false` in the host table. |
|
||||||
| `sway.nix` | host table (graphical hosts) | Implementation of `features.swayDesktop`: the system Sway package, the greetd/ReGreet (cage) greeter forced to Dvorak, xdg-portal, Wayland utility packages. Home-side Sway config is in `home/sway.nix`. |
|
| `sway.nix` | host table (graphical hosts) | Implementation of `features.swayDesktop`: the system Sway package, the greetd/ReGreet (cage) greeter forced to Dvorak, xdg-portal, Wayland utility packages. Home-side Sway config is in `home/sway.nix`. |
|
||||||
| `ssh.nix` | host table (T400, Mac Pro, RPi5) | Enables sshd, opens port 22, enforces a key-only policy (no password / keyboard-interactive, no root). Authorized keys come from the registry via `users.nix`. |
|
| `ssh.nix` | host table (T400, Mac Pro, both Pis) | Enables sshd, opens port 22, enforces a key-only policy (no password / keyboard-interactive, no root). Authorized keys come from the registry via `users.nix`. |
|
||||||
| `firmware/` | referenced by MBP host config | Committed Apple peripheral firmware blobs for the Asahi MBP (see "MacBook (Asahi) firmware"). |
|
| `firmware/` | referenced by MBP host config | Committed Apple peripheral firmware blobs for the Asahi MBP (see "MacBook (Asahi) firmware"). |
|
||||||
|
|
||||||
Form-factor decision: a **laptop** imports `laptop.nix` (default
|
Form-factor decision: a **laptop** imports `laptop.nix` (default
|
||||||
`portable = true`); a **wired desktop** imports `desktop.nix` and sets
|
`portable = true`); a **wired desktop** imports `desktop.nix` and sets
|
||||||
@@ -86,7 +88,7 @@ declares what it is and the shared modules derive from that:
|
|||||||
are level 1). Ignored on non-x86_64 hosts.
|
are level 1). Ignored on non-x86_64 hosts.
|
||||||
- `features.claudeCode.enable` — derived: on unless the host is below
|
- `features.claudeCode.enable` — derived: on unless the host is below
|
||||||
x86-64-v2, because Claude Code's Node runtime needs SSE4.2/POPCNT.
|
x86-64-v2, because Claude Code's Node runtime needs SSE4.2/POPCNT.
|
||||||
[`home/claude.nix`](./home/claude.nix) reads it through home-manager's
|
[`home/claude.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/home/claude.nix) reads it through home-manager's
|
||||||
`osConfig` and installs nothing (CLI, `CLAUDE.md`, output style, memory
|
`osConfig` and installs nothing (CLI, `CLAUDE.md`, output style, memory
|
||||||
symlink) when it is off. Hosts with no such option — the Darwin host and the
|
symlink) when it is off. Hosts with no such option — the Darwin host and the
|
||||||
standalone `homeConfigurations` — fall back to enabled.
|
standalone `homeConfigurations` — fall back to enabled.
|
||||||
@@ -101,12 +103,12 @@ editing every host.
|
|||||||
|
|
||||||
Identity is data, kept separate from the reusable modules:
|
Identity is data, kept separate from the reusable modules:
|
||||||
|
|
||||||
- [`users/registry.nix`](./users/registry.nix) — one entry per user (display
|
- [`users/registry.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/users/registry.nix) — one entry per user (display
|
||||||
name, email, supplementary groups, authorized + signing keys). This is the
|
name, email, supplementary groups, authorized + signing keys). This is the
|
||||||
single source of identity; no user data is hardcoded in the modules.
|
single source of identity; no user data is hardcoded in the modules.
|
||||||
- Each host's table entry declares a `users` set keyed by username; every entry
|
- Each host's table entry declares a `users` set keyed by username; every entry
|
||||||
lists that user's home-module composition (the shared `./home` bundle plus any
|
lists that user's home-module composition (the shared `./home` bundle plus any
|
||||||
per-user modules, e.g. [`users/emmathorpe/work.nix`](./users/emmathorpe/work.nix))
|
per-user modules, e.g. [`users/emmathorpe/work.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/users/emmathorpe/work.nix))
|
||||||
and optional per-host-user system bits such as `linger`.
|
and optional per-host-user system bits such as `linger`.
|
||||||
- `mkHost` builds each account from the registry and injects the matching
|
- `mkHost` builds each account from the registry and injects the matching
|
||||||
identity into that user's home config as the `identity` module arg. A host can
|
identity into that user's home config as the `identity` module arg. A host can
|
||||||
@@ -114,13 +116,13 @@ Identity is data, kept separate from the reusable modules:
|
|||||||
|
|
||||||
Per-user home extras live under `users/<name>/`:
|
Per-user home extras live under `users/<name>/`:
|
||||||
|
|
||||||
- [`users/lyrathorpe/home.nix`](./users/lyrathorpe/home.nix) — personal extras
|
- [`users/lyrathorpe/home.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/users/lyrathorpe/home.nix) — personal extras
|
||||||
(an ssh host shortcut, gammastep coordinates); imported on Lyra's hosts.
|
(an ssh host shortcut, gammastep coordinates); imported on Lyra's hosts.
|
||||||
- [`users/emmathorpe/work.nix`](./users/emmathorpe/work.nix) — the work
|
- [`users/emmathorpe/work.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/users/emmathorpe/work.nix) — the work
|
||||||
toolchain (kubectl/helm/az/etc.), work-only LSP servers, the corporate ssh
|
toolchain (kubectl/helm/az/etc.), work-only LSP servers, the corporate ssh
|
||||||
handling, and the headless Secret Service that gcx needs for its keychain
|
handling, and the headless Secret Service that gcx needs for its keychain
|
||||||
tokens (see [`home/secret-service.nix`](./home/secret-service.nix)); imports
|
tokens (see [`home/secret-service.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/home/secret-service.nix)); imports
|
||||||
[`users/emmathorpe/renovate-review.nix`](./users/emmathorpe/renovate-review.nix),
|
[`users/emmathorpe/renovate-review.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/users/emmathorpe/renovate-review.nix),
|
||||||
the daily headless Renovate-PR review timer (EDaaS only).
|
the daily headless Renovate-PR review timer (EDaaS only).
|
||||||
|
|
||||||
### Portable home (off-NixOS / external consumers)
|
### Portable home (off-NixOS / external consumers)
|
||||||
@@ -179,17 +181,20 @@ automatically.
|
|||||||
## Shell environment & keybindings
|
## Shell environment & keybindings
|
||||||
|
|
||||||
- Interactive shell features (zsh, tmux, git, ssh, CLI tools, auto-tmux):
|
- Interactive shell features (zsh, tmux, git, ssh, CLI tools, auto-tmux):
|
||||||
[`home/README.md`](./home/README.md).
|
[`docs/shell.md`](https://docs.lyrapup.pet/nixfiles/shell/).
|
||||||
|
- Which classic utilities are shadowed by modern replacements, and the flag
|
||||||
|
differences that will bite:
|
||||||
|
[`docs/shell.md` → "Replacing the classics"](https://docs.lyrapup.pet/nixfiles/shell/#replacing-the-classics).
|
||||||
- All Sway / tmux / foot / zsh keyboard shortcuts:
|
- All Sway / tmux / foot / zsh keyboard shortcuts:
|
||||||
[`home/KEYBINDINGS.md`](./home/KEYBINDINGS.md).
|
[`docs/keybindings.md`](https://docs.lyrapup.pet/nixfiles/keybindings/).
|
||||||
|
|
||||||
## Login / greeter
|
## Login / greeter
|
||||||
|
|
||||||
Graphical (Sway) hosts log in through a Wayland greeter — `greetd` running
|
Graphical (Sway) hosts log in through a Wayland greeter — `greetd` running
|
||||||
ReGreet inside the `cage` kiosk compositor — implemented in
|
ReGreet inside the `cage` kiosk compositor — implemented in
|
||||||
[`modules/sway.nix`](./modules/sway.nix), gated on
|
[`modules/sway.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/modules/sway.nix), gated on
|
||||||
`features.swayDesktop.enable` (the option is declared in
|
`features.swayDesktop.enable` (the option is declared in
|
||||||
[`modules/features.nix`](./modules/features.nix), so headless hosts
|
[`modules/features.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/modules/features.nix), so headless hosts
|
||||||
can leave it off without importing `modules/sway.nix`). The greeter is forced to Dvorak
|
can leave it off without importing `modules/sway.nix`). The greeter is forced to Dvorak
|
||||||
to match the console and Sway session. Headless hosts (the WSL work box and the
|
to match the console and Sway session. Headless hosts (the WSL work box and the
|
||||||
Raspberry Pi server) keep plain TTY login. The target account needs a password
|
Raspberry Pi server) keep plain TTY login. The target account needs a password
|
||||||
@@ -209,6 +214,38 @@ To refresh them, copy the firmware extracted during the Asahi install (from
|
|||||||
[Asahi NixOS docs](https://github.com/tpwrules/nixos-apple-silicon)) into
|
[Asahi NixOS docs](https://github.com/tpwrules/nixos-apple-silicon)) into
|
||||||
`modules/firmware/` and commit with `git add -f`.
|
`modules/firmware/` and commit with `git add -f`.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
All prose documentation lives in [`docs/`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/docs); this README is the overview. The pages are
|
||||||
|
published to **<https://docs.lyrapup.pet/nixfiles/>** by the
|
||||||
|
[`docs-site`](https://code.emmathe.dev/lyrathorpe/docs-site) repository, which clones this repo on
|
||||||
|
every build (on its own push, nightly, or on demand) and assembles the tree:
|
||||||
|
|
||||||
|
```
|
||||||
|
README.md -> docs/nixfiles/index.md # this file becomes the section landing page
|
||||||
|
docs/ -> docs/nixfiles/ # everything here, ordering from docs/.pages
|
||||||
|
```
|
||||||
|
|
||||||
|
Nothing is pushed from this side and there is no build step here — editing a
|
||||||
|
page and merging is all that is required. Files outside `docs/` (bar this
|
||||||
|
README) are **not** synced, so a doc kept next to the code it describes will
|
||||||
|
never appear on the site.
|
||||||
|
|
||||||
|
### Linking rules
|
||||||
|
|
||||||
|
The site has no copy of the source tree, and this README is republished at a
|
||||||
|
different depth from the rest of `docs/`. Both facts break naive relative
|
||||||
|
links, so:
|
||||||
|
|
||||||
|
| Link from | To | Use |
|
||||||
|
| ----------------- | ----------------------- | ---------------------------------------------------------------- |
|
||||||
|
| anywhere | a source file or dir | absolute `https://code.emmathe.dev/.../src/branch/main/…` |
|
||||||
|
| a page in `docs/` | another page in `docs/` | relative (`./keybindings.md`) — correct in Gitea and on the site |
|
||||||
|
| this README | a page in `docs/` | absolute `https://docs.lyrapup.pet/nixfiles/…` |
|
||||||
|
|
||||||
|
`mkdocs build` runs non-strict on the docs-site side, so a broken link fails
|
||||||
|
silently rather than failing the build. Check links by hand when moving a page.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
A dev shell and a formatting/lint gate are wired through the flake:
|
A dev shell and a formatting/lint gate are wired through the flake:
|
||||||
@@ -224,7 +261,7 @@ A dev shell and a formatting/lint gate are wired through the flake:
|
|||||||
|
|
||||||
## CI
|
## CI
|
||||||
|
|
||||||
[`.gitea/workflows/ci.yaml`](./.gitea/workflows/ci.yaml) runs `nix flake check`
|
[`.gitea/workflows/ci.yaml`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/.gitea/workflows/ci.yaml) runs `nix flake check`
|
||||||
(formatting, `deadnix`, `statix`, the pre-commit hooks) and evaluates every
|
(formatting, `deadnix`, `statix`, the pre-commit hooks) and evaluates every
|
||||||
NixOS and Darwin host configuration on push/PR. It always runs (no `paths:`
|
NixOS and Darwin host configuration on push/PR. It always runs (no `paths:`
|
||||||
filter) so the required check never hangs pending; the heavy Nix steps are
|
filter) so the required check never hangs pending; the heavy Nix steps are
|
||||||
|
|||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
# Section title and ordering for the MkDocs awesome-pages plugin on
|
||||||
|
# docs.lyrapup.pet.
|
||||||
|
#
|
||||||
|
# The title is set explicitly: with no entry in the site's nav, MkDocs derives
|
||||||
|
# the section name from the directory and renders it title-cased as "Nixfiles".
|
||||||
|
title: nixfiles
|
||||||
|
|
||||||
|
# `index.md` is this repository's root README, copied in by the docs-site build
|
||||||
|
# before this directory is synced over the top. The trailing `...` picks up any
|
||||||
|
# page added later, so a new file needs no edit here.
|
||||||
|
nav:
|
||||||
|
- index.md
|
||||||
|
- shell.md
|
||||||
|
- keybindings.md
|
||||||
|
- hosts
|
||||||
|
- ...
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
title: Hosts
|
||||||
@@ -11,7 +11,7 @@ The day-to-day work environment. It layers the corporate Kubernetes / Helm /
|
|||||||
Terraform / cloud toolchain and a couple of work-only editor language servers on
|
Terraform / cloud toolchain and a couple of work-only editor language servers on
|
||||||
top of the shared home profile. The system config here is thin — it is mostly
|
top of the shared home profile. The system config here is thin — it is mostly
|
||||||
WSL plumbing; the user-facing tooling lives in
|
WSL plumbing; the user-facing tooling lives in
|
||||||
[`../../users/emmathorpe/work.nix`](../../users/emmathorpe/work.nix).
|
[`../../users/emmathorpe/work.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/users/emmathorpe/work.nix).
|
||||||
|
|
||||||
## WSL specifics
|
## WSL specifics
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ WSL plumbing; the user-facing tooling lives in
|
|||||||
The host-table entry sets `users.emmathorpe.linger = true` so the user's
|
The host-table entry sets `users.emmathorpe.linger = true` so the user's
|
||||||
`systemd --user` instance stays alive without an open login session. That keeps
|
`systemd --user` instance stays alive without an open login session. That keeps
|
||||||
the daily headless **Renovate PR review** timer firing — defined in
|
the daily headless **Renovate PR review** timer firing — defined in
|
||||||
[`../../users/emmathorpe/renovate-review.nix`](../../users/emmathorpe/renovate-review.nix)
|
[`../../users/emmathorpe/renovate-review.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/users/emmathorpe/renovate-review.nix)
|
||||||
(imported only from `work.nix`, so it exists on this machine alone). See that
|
(imported only from `work.nix`, so it exists on this machine alone). See that
|
||||||
file's header for the auth (Vertex AI ADC), triage policy, and caveats.
|
file's header for the auth (Vertex AI ADC), triage policy, and caveats.
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ name is not activatable".
|
|||||||
Home-manager's own `services.gnome-keyring` does not work here: it is
|
Home-manager's own `services.gnome-keyring` does not work here: it is
|
||||||
`WantedBy=graphical-session-pre.target`, which never activates on this headless
|
`WantedBy=graphical-session-pre.target`, which never activates on this headless
|
||||||
box, and it cannot unlock the keyring. See
|
box, and it cannot unlock the keyring. See
|
||||||
[`../../home/secret-service.nix`](../../home/secret-service.nix) for the full
|
[`../../home/secret-service.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/home/secret-service.nix) for the full
|
||||||
rationale and the security trade-off of an auto-unlocked keyring.
|
rationale and the security trade-off of an auto-unlocked keyring.
|
||||||
|
|
||||||
Only the `secrets` component is started. The `ssh` component is deliberately off
|
Only the `secrets` component is started. The `ssh` component is deliberately off
|
||||||
@@ -31,7 +31,7 @@ Partition the disk GPT with an ESP (vfat).
|
|||||||
|
|
||||||
The stock card (**ATI Radeon HD 2600 XT** or **NVIDIA GeForce 8800 GT**,
|
The stock card (**ATI Radeon HD 2600 XT** or **NVIDIA GeForce 8800 GT**,
|
||||||
depending on the unit) has been replaced with an **NVIDIA Quadro P400** (Pascal,
|
depending on the unit) has been replaced with an **NVIDIA Quadro P400** (Pascal,
|
||||||
GP108). Everything driver-related lives in [`nvidia.nix`](./nvidia.nix):
|
GP108). Everything driver-related lives in [`nvidia.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/hosts/MacPro31/nvidia.nix):
|
||||||
|
|
||||||
- **Driver branch 580** (`nvidiaPackages.legacy_580`), _not_ the nixpkgs default
|
- **Driver branch 580** (`nvidiaPackages.legacy_580`), _not_ the nixpkgs default
|
||||||
(`production`, currently 595.x). 580 is the last branch that supports
|
(`production`, currently 595.x). 580 is the last branch that supports
|
||||||
@@ -48,7 +48,7 @@ GP108). Everything driver-related lives in [`nvidia.nix`](./nvidia.nix):
|
|||||||
The driver is unfree, so it is **not in the binary cache**: the kernel module is
|
The driver is unfree, so it is **not in the binary cache**: the kernel module is
|
||||||
compiled on the machine, which on these 2008 Xeons is slow — budget for a long
|
compiled on the machine, which on these 2008 Xeons is slow — budget for a long
|
||||||
first rebuild and again after every kernel bump. The package names are
|
first rebuild and again after every kernel bump. The package names are
|
||||||
allowlisted in `unfreePackages` in [`flake.nix`](../../flake.nix).
|
allowlisted in `unfreePackages` in [`flake.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/flake.nix).
|
||||||
|
|
||||||
Note the Mac Pro shows no EFI boot screen with a stock PC card (no Apple EFI
|
Note the Mac Pro shows no EFI boot screen with a stock PC card (no Apple EFI
|
||||||
ROM): the machine boots blind until KMS brings the display up. That is expected,
|
ROM): the machine boots blind until KMS brings the display up. That is expected,
|
||||||
@@ -82,12 +82,38 @@ docker run --rm --device=nvidia.com/gpu=all nvidia/cuda:12.9.1-base-ubuntu24.04
|
|||||||
- Docker socket is local-only (no TCP listener, unlike the Pi). Users need the
|
- Docker socket is local-only (no TCP listener, unlike the Pi). Users need the
|
||||||
`docker` group; the registry already grants it.
|
`docker` group; the registry already grants it.
|
||||||
|
|
||||||
|
### "Driver Not Loaded" from the CDI generator
|
||||||
|
|
||||||
|
`nvidia-container-toolkit-cdi-generator.service` fails with
|
||||||
|
`failed to initialize NVML: Driver Not Loaded` whenever the `nvidia` kernel
|
||||||
|
module is not loaded in the **running** kernel. After a kernel bump that is
|
||||||
|
unavoidable — the rebuilt module cannot load until reboot — so the unit is
|
||||||
|
guarded with `ConditionPathExists=/proc/driver/nvidia/version` and skips
|
||||||
|
instead of failing. Without that guard it also takes `docker.service`
|
||||||
|
(`requiredBy`) with it and makes `nixos-rebuild switch` exit non-zero.
|
||||||
|
|
||||||
|
**Reboot after a rebuild that touches the driver or the kernel.** The toolkit's
|
||||||
|
udev rule restarts the generator when the GPU device appears, so the CDI specs
|
||||||
|
are written on the next boot. To check the state:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
lsmod | grep nvidia # nvidia, nvidia_modeset, nvidia_drm, nvidia_uvm
|
||||||
|
cat /proc/driver/nvidia/version
|
||||||
|
nvidia-smi
|
||||||
|
systemctl status nvidia-container-toolkit-cdi-generator.service
|
||||||
|
ls /var/run/cdi # the generated spec
|
||||||
|
```
|
||||||
|
|
||||||
|
If the module is genuinely absent after a reboot, check `dmesg | grep -i
|
||||||
|
nvidia` (build/version mismatch, or nouveau still bound — the module blacklists
|
||||||
|
it, so that should not happen).
|
||||||
|
|
||||||
## Claude Code — not installed here
|
## Claude Code — not installed here
|
||||||
|
|
||||||
The dual Harpertown Xeons are **x86-64-v1** (SSE4.1, but no SSE4.2/POPCNT) and
|
The dual Harpertown Xeons are **x86-64-v1** (SSE4.1, but no SSE4.2/POPCNT) and
|
||||||
the Node runtime Claude Code ships on requires x86-64-v2. `configuration.nix`
|
the Node runtime Claude Code ships on requires x86-64-v2. `configuration.nix`
|
||||||
declares `features.cpu.microarchLevel = 1`, which switches the tool off through
|
declares `features.cpu.microarchLevel = 1`, which switches the tool off through
|
||||||
the fleet-wide gate in [`../../modules/features.nix`](../../modules/features.nix)
|
the fleet-wide gate in [`../../modules/features.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/modules/features.nix)
|
||||||
— see the root README. Forcing `features.claudeCode.enable` on here is an
|
— see the root README. Forcing `features.claudeCode.enable` on here is an
|
||||||
evaluation error, not a broken install.
|
evaluation error, not a broken install.
|
||||||
|
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
# Raspberry Pi Zero 2 W (`lyrathorpe-zero2w`)
|
||||||
|
|
||||||
|
Headless `aarch64-linux` "Psion sidecar": an RS232 companion for a Psion 5MX,
|
||||||
|
after [Kian Ryan's PPP modem and terminal
|
||||||
|
write-up](https://www.kianryan.co.uk/2022-11-28-psion-sidecar-ppp-modem-and-terminal/).
|
||||||
|
Two roles, split into submodules:
|
||||||
|
|
||||||
|
- **PPP link + telnet** (`serial-ppp.nix`) — `pppd` on `/dev/ttyAMA0`, the Psion
|
||||||
|
on the far end of a null-modem cable, NAT out to Wi-Fi, and a telnet login for
|
||||||
|
the Psion's terminal client.
|
||||||
|
- **Legacy mail proxy** (`email-proxy.nix`) — cleartext POP3/SMTP for the
|
||||||
|
Psion's built-in mail client, forwarded to authenticated IMAPS/SMTPS by
|
||||||
|
[legacy-email-proxy](https://code.emmathe.dev/lyrathorpe/legacy-email-proxy).
|
||||||
|
That project ships its own package and NixOS module, so `email-proxy.nix`
|
||||||
|
here is only `services.legacy-email-proxy.enable` plus a path to the
|
||||||
|
credentials — nothing about the proxy is vendored into this flake.
|
||||||
|
|
||||||
|
`sd-image.nix` in the same directory is not part of the running system: it is
|
||||||
|
the one-shot install card, built as `packages.aarch64-linux.zero2w-sd-image`.
|
||||||
|
See "Install".
|
||||||
|
|
||||||
|
## Hardware and boot
|
||||||
|
|
||||||
|
The Zero 2 W is a BCM2837 — the Pi 3's SoC — so the host table uses
|
||||||
|
`nixos-hardware`'s `raspberry-pi-3` profile for the kernel, firmware and device
|
||||||
|
tree. Boot is the same U-Boot + extlinux path as the other Pi.
|
||||||
|
|
||||||
|
Unlike the Pi 5, this host owns the firmware partition declaratively
|
||||||
|
(`hardware.raspberry-pi.firmware.enable`): every `switch` rewrites
|
||||||
|
`/boot/firmware`, including `config.txt`. Two settings there matter:
|
||||||
|
|
||||||
|
| `config.txt` | Why |
|
||||||
|
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| `dtoverlay=disable-bt` | Moves the PL011 UART off Bluetooth onto GPIO 14/15, so `/dev/ttyAMA0` is the RS232 header. The mini UART (`ttyS0`) drifts at 115200. |
|
||||||
|
| `dtoverlay=uart0,ctsrts` | RTS/CTS on GPIO 16/17. Both `pppd` and the Psion's modem profile use hardware flow control. |
|
||||||
|
| `kernel=u-boot.bin` | `hardware.raspberry-pi.firmware.uboot.enable`. Without it the rewritten `config.txt` would have no `kernel=` line and the board would stop booting. |
|
||||||
|
|
||||||
|
`gpu_mem=16`, `start_x=0`, `camera_auto_detect=0` and `display_auto_detect=0`
|
||||||
|
hand the VideoCore the minimum: the board has 512 MB total and no display.
|
||||||
|
|
||||||
|
## Never build on the Pi
|
||||||
|
|
||||||
|
512 MB of RAM and an SD card. It cannot compile its own system, and there is
|
||||||
|
deliberately no swap partition (SD cards wear out under swap writes) — zram
|
||||||
|
takes its place. Build somewhere else and push the result:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# from a workstation, using another aarch64 machine as the builder
|
||||||
|
nixos-rebuild switch --flake .#lyrathorpe-zero2w \
|
||||||
|
--build-host lyrathorpe@lyrathorpe-rpi5 \
|
||||||
|
--target-host lyrathorpe@<pi-address> --use-remote-sudo
|
||||||
|
```
|
||||||
|
|
||||||
|
The `raspberry-pi-3` profile builds the vendor kernel from source and it is not
|
||||||
|
in the binary cache, so the first build is long (hours on the Pi 5, less on the
|
||||||
|
MacBook). Later builds reuse it. The same applies to the SD image below: it
|
||||||
|
contains that kernel, so it needs an `aarch64-linux` builder too. From an
|
||||||
|
`x86_64` box or a Mac, that means a remote builder (`nix.buildMachines`) or, on
|
||||||
|
Darwin, `nix.linux-builder.enable`.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
The card is built from this flake, not downloaded. A generic NixOS image would
|
||||||
|
boot, but there would be no way into the machine afterwards: it has no Ethernet,
|
||||||
|
no wifi credentials, and this configuration hands the serial port to `pppd`, so
|
||||||
|
there is no console either. Building the host's own image sidesteps all three —
|
||||||
|
the first boot is already the real system, with the SSH key from the registry
|
||||||
|
in place.
|
||||||
|
|
||||||
|
1. **Set the SSID.** `networking.wireless.networks` in `configuration.nix` still
|
||||||
|
says `CHANGE-ME-SSID`. It is baked into the image at build time; only the PSK
|
||||||
|
is read at runtime.
|
||||||
|
2. **Build and write the card.** On an `aarch64-linux` machine (or with one
|
||||||
|
configured as a builder):
|
||||||
|
```sh
|
||||||
|
nix build .#packages.aarch64-linux.zero2w-sd-image
|
||||||
|
sudo dd if=result/sd-image/nixos-zero2w.img of=/dev/sdX bs=4M conv=fsync status=progress
|
||||||
|
```
|
||||||
|
Check `/dev/sdX` twice. `dd` does not ask.
|
||||||
|
3. **Seed the secrets before first boot.** They are not in the image. Mount the
|
||||||
|
card's second partition (the ext4 root) and write both files described under
|
||||||
|
"Secrets" below:
|
||||||
|
```sh
|
||||||
|
sudo mount /dev/sdX2 /mnt
|
||||||
|
sudo mkdir -p /mnt/var/lib/wpa_supplicant /mnt/var/lib/legacy-email-proxy
|
||||||
|
printf 'psk_home=%s\n' 'the-pre-shared-key' \
|
||||||
|
| sudo tee /mnt/var/lib/wpa_supplicant/secrets.conf > /dev/null
|
||||||
|
sudo chmod 600 /mnt/var/lib/wpa_supplicant/secrets.conf
|
||||||
|
# ... and /mnt/var/lib/legacy-email-proxy/backend.env, same permissions
|
||||||
|
sudo umount /mnt
|
||||||
|
```
|
||||||
|
Skip the PSK and the board boots with no network at all.
|
||||||
|
4. **Boot it.** Give it a few minutes on first boot — it resizes the root
|
||||||
|
partition and generates host keys on a slow card. Then:
|
||||||
|
```sh
|
||||||
|
ssh lyrathorpe@lyrathorpe-zero2w.local # mDNS; services.avahi publishes it
|
||||||
|
```
|
||||||
|
5. **Give the login user a password** (`passwd lyrathorpe`) if you want console
|
||||||
|
or telnet login; the SSH key from
|
||||||
|
[`users/registry.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/users/registry.nix)
|
||||||
|
already works without one.
|
||||||
|
6. Thereafter, rebuild from another machine as in the previous section.
|
||||||
|
|
||||||
|
`hosts/PiZero2W/hardware-configuration.nix` is a **placeholder** — but its
|
||||||
|
layout (`/` on label `NIXOS_SD`, `/boot/firmware` on label `FIRMWARE`) is
|
||||||
|
exactly what the SD image produces, so there is nothing to regenerate for a card
|
||||||
|
install. Run `nixos-generate-config` and replace it only if you deviate from
|
||||||
|
that layout.
|
||||||
|
|
||||||
|
If the board never appears on the network, it is almost always the PSK file.
|
||||||
|
Re-mount the card and check it. Failing that, a mini-HDMI monitor and a
|
||||||
|
micro-USB keyboard get you a console on `tty1` — the serial port will not,
|
||||||
|
because `pppd` holds it.
|
||||||
|
|
||||||
|
## Secrets (not in the Nix store)
|
||||||
|
|
||||||
|
Both files are created on the device, owned by root, mode `0600`. Neither is
|
||||||
|
managed by this flake; the units that read them fail loudly if they are absent.
|
||||||
|
|
||||||
|
**Wi-Fi PSK** — `/var/lib/wpa_supplicant/secrets.conf`:
|
||||||
|
|
||||||
|
```
|
||||||
|
psk_home=<the pre-shared key>
|
||||||
|
```
|
||||||
|
|
||||||
|
The SSID itself _is_ in `configuration.nix` and is currently the placeholder
|
||||||
|
`CHANGE-ME-SSID`; set it to the real network. `wpa_supplicant` resolves
|
||||||
|
`pskRaw = "ext:psk_home"` against this file at runtime.
|
||||||
|
|
||||||
|
**Mail backend** — `/var/lib/legacy-email-proxy/backend.env`, a systemd
|
||||||
|
`EnvironmentFile`:
|
||||||
|
|
||||||
|
```
|
||||||
|
BACKEND_IMAP_HOST=imap.example.com
|
||||||
|
BACKEND_IMAP_USER=someone@example.com
|
||||||
|
BACKEND_IMAP_PASS=<app password>
|
||||||
|
BACKEND_SMTP_HOST=smtp.example.com
|
||||||
|
BACKEND_SMTP_USER=someone@example.com
|
||||||
|
BACKEND_SMTP_PASS=<app password>
|
||||||
|
```
|
||||||
|
|
||||||
|
Ports and TLS default sensibly (IMAPS 993, SMTPS 465); the full variable list is
|
||||||
|
in the proxy's README.
|
||||||
|
|
||||||
|
### Why POP3 and not IMAP
|
||||||
|
|
||||||
|
The Psion's built-in mail client speaks POP only, so POP3 is what the proxy
|
||||||
|
exposes. If a third-party IMAP client is ever installed on the device, the
|
||||||
|
answer is **not** to add an IMAP frontend to the proxy: the backend is already
|
||||||
|
IMAP, so there is no protocol to translate, only TLS to remove. An `stunnel`
|
||||||
|
client (plaintext 143 on the PPP link, IMAPS 993 outbound) does that in a few
|
||||||
|
lines with no code, and credentials pass straight through — IMAP clients always
|
||||||
|
authenticate.
|
||||||
|
|
||||||
|
SMTP stays on the proxy either way. A client of this vintage cannot do SMTP
|
||||||
|
AUTH, which is exactly why the proxy injects the backend credentials.
|
||||||
|
|
||||||
|
## Psion configuration
|
||||||
|
|
||||||
|
Matches the addressing in `serial-ppp.nix` (`10.0.0.1` the Pi, `10.0.0.2` the
|
||||||
|
Psion):
|
||||||
|
|
||||||
|
- **Modem** control panel, a "Direct Cable Connection" profile: 115200 baud,
|
||||||
|
Hardware (RTS/CTS) flow control; on the Advanced tab, Terminal Detect and
|
||||||
|
Carrier Detect both **off**.
|
||||||
|
- **Internet** control panel, a new profile: Connection Type **Direct**, Manual
|
||||||
|
Login **True**. Addresses: get IP from server **False**, static **10.0.0.2**.
|
||||||
|
Get DNS from server **True** — `pppd` sends resolvers over the link
|
||||||
|
(`ms-dns`), so nothing is hard-coded on the Psion.
|
||||||
|
- Advanced: PPP extensions **False**, plain-text authentication **True**.
|
||||||
|
- Terminal client: telnet to **10.0.0.1 port 23**. It renders non-ANSI output
|
||||||
|
far better than the raw serial console does.
|
||||||
|
- Mail client: POP3 and SMTP server **10.0.0.1**, no encryption, no
|
||||||
|
authentication.
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
Everything on this host that the Psion talks to is unauthenticated and
|
||||||
|
unencrypted, because a 1999 palmtop speaks no TLS:
|
||||||
|
|
||||||
|
- **telnet on 23** — cleartext login, including the password.
|
||||||
|
- **POP3 on 110 / SMTP on 25** — full mailbox access and an open relay to anyone
|
||||||
|
who reaches them.
|
||||||
|
|
||||||
|
The confinement is the firewall, and it is the only thing standing there:
|
||||||
|
`ppp0` is a trusted interface, `wlan0` is not, and those ports are never opened
|
||||||
|
on it. The proxy binds `0.0.0.0` rather than `10.0.0.1` on purpose — the PPP
|
||||||
|
address only exists while the Psion is plugged in, and a bind-time dependency on
|
||||||
|
a serial cable is a restart loop waiting to happen. Do not add these ports to
|
||||||
|
`networking.firewall.allowedTCPPorts`, and do not put this board on an untrusted
|
||||||
|
network.
|
||||||
|
|
||||||
|
Only sshd (port 22, key-only, via
|
||||||
|
[`modules/ssh.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/modules/ssh.nix))
|
||||||
|
is reachable over Wi-Fi.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Symptom | Check |
|
||||||
|
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| No PPP at all | `systemctl status pppd-psion`, then `journalctl -u pppd-psion -f` while the Psion dials. `passive`/`persist` mean it waits, not fails. |
|
||||||
|
| PPP negotiates, then hangs | Flow control. Confirm `dtoverlay=uart0,ctsrts` is in `/boot/firmware/config.txt` and that the Psion's modem profile is set to Hardware. |
|
||||||
|
| `/dev/ttyAMA0` missing or is a Bluetooth device | `disable-bt` did not apply — the firmware partition was not rewritten. Confirm `/boot/firmware` is a mounted partition; the activation script skips with a warning if it is not. |
|
||||||
|
| Something else holds the port | `systemctl status serial-getty@ttyAMA0` — it is disabled in `serial-ppp.nix`, and must stay that way. |
|
||||||
|
| Mail proxy dead | `systemctl status legacy-email-proxy`. A missing `backend.env` fails the unit before it starts. |
|
||||||
@@ -4,13 +4,13 @@ Every keyboard shortcut configured across this desktop, and where it is defined.
|
|||||||
Everything here is managed declaratively through Nix — edit the listed file and
|
Everything here is managed declaratively through Nix — edit the listed file and
|
||||||
rebuild, never the generated dotfiles.
|
rebuild, never the generated dotfiles.
|
||||||
|
|
||||||
| Area | Defined in |
|
| Area | Defined in |
|
||||||
| ----------------- | --------------------------------------------------------------------------------------------------------------------- |
|
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| Sway (compositor) | [`sway.nix`](./sway.nix) `config.keybindings` + `config.modes`, plus the home-manager Sway module's built-in defaults |
|
| Sway (compositor) | [`sway.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/home/sway.nix) `config.keybindings` + `config.modes`, plus the home-manager Sway module's built-in defaults |
|
||||||
| tmux | [`shell.nix`](./shell.nix) `programs.tmux` |
|
| tmux | [`shell.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/home/shell.nix) `programs.tmux` |
|
||||||
| zsh line editor | [`shell.nix`](./shell.nix) `programs.zsh.historySubstringSearch` |
|
| zsh line editor | [`shell.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/home/shell.nix) `programs.zsh.historySubstringSearch` |
|
||||||
| Neovim | [`editor.nix`](./editor.nix) `programs.nixvim` |
|
| Neovim | [`editor.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/home/editor.nix) `programs.nixvim` |
|
||||||
| foot (terminal) | foot package defaults — only colours are themed (in `sway.nix`) |
|
| foot (terminal) | foot package defaults — only colours are themed (in `sway.nix`) |
|
||||||
|
|
||||||
**Conventions**
|
**Conventions**
|
||||||
|
|
||||||
+183
-46
@@ -4,21 +4,21 @@ Everything the shell, terminal multiplexer, git and ssh do beyond their defaults
|
|||||||
and where each is defined. All of it is managed declaratively through
|
and where each is defined. All of it is managed declaratively through
|
||||||
home-manager — edit the listed file and rebuild, never the generated dotfiles.
|
home-manager — edit the listed file and rebuild, never the generated dotfiles.
|
||||||
|
|
||||||
Keyboard shortcuts have their own reference: [`KEYBINDINGS.md`](./KEYBINDINGS.md).
|
Keyboard shortcuts have their own reference: [`keybindings.md`](./keybindings.md).
|
||||||
|
|
||||||
| Area | Defined in |
|
| Area | Defined in |
|
||||||
| -------------------------------------- | ----------------------------------------------------- |
|
| -------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
|
||||||
| zsh, CLI tools, tmux, ssh, auto-tmux | [`shell.nix`](./shell.nix) |
|
| zsh, CLI tools, tmux, ssh, auto-tmux | [`shell.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/home/shell.nix) |
|
||||||
| git (+ delta, commitizen) | [`git.nix`](./git.nix) |
|
| git (+ delta, commitizen) | [`git.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/home/git.nix) |
|
||||||
| Neovim (nixvim) + LSP | [`editor.nix`](./editor.nix) |
|
| Neovim (nixvim) + LSP | [`editor.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/home/editor.nix) |
|
||||||
| Claude Code (CLAUDE.md, style, memory) | [`claude.nix`](./claude.nix) |
|
| Claude Code (CLAUDE.md, style, memory) | [`claude.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/home/claude.nix) |
|
||||||
| GUI apps, GTK/Firefox theming, cursor | [`desktop.nix`](./desktop.nix) (graphical hosts only) |
|
| GUI apps, GTK/Firefox theming, cursor | [`desktop.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/home/desktop.nix) (graphical hosts only) |
|
||||||
|
|
||||||
Shared by every host via [`default.nix`](./default.nix); the work box also layers
|
Shared by every host via [`default.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/home/default.nix); the work box also layers
|
||||||
[`work.nix`](../users/emmathorpe/work.nix) on top (its own ssh config, extra
|
[`work.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/users/emmathorpe/work.nix) on top (its own ssh config, extra
|
||||||
packages, and the C#/Helm language servers). The committer identity (name, email,
|
packages, kubecolor, and the C#/Helm language servers). The committer identity (name, email,
|
||||||
signing key) comes from the user registry
|
signing key) comes from the user registry
|
||||||
([`../users/registry.nix`](../users/registry.nix)), not this module.
|
([`../users/registry.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/users/registry.nix)), not this module.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -35,26 +35,34 @@ signing key) comes from the user registry
|
|||||||
| History substring search | type a fragment, then ↑/↓ cycles matching past commands — works in foot, iTerm2 and the Linux TTY (both CSI and SS3 arrow encodings bound) |
|
| History substring search | type a fragment, then ↑/↓ cycles matching past commands — works in foot, iTerm2 and the Linux TTY (both CSI and SS3 arrow encodings bound) |
|
||||||
| Prompt | hostname is prefixed when over SSH |
|
| Prompt | hostname is prefixed when over SSH |
|
||||||
|
|
||||||
**Aliases:** `ls`/`ll`/`la`/`lt` → `eza` (icons + git), `cls` → `clear`. git aliases live in git.nix (below).
|
**Aliases:** `ls`/`ll`/`la`/`lt` → `eza` (icons + git), `cls` → `clear`,
|
||||||
|
`cat`/`du`/`df`/`ps` → their modern equivalents (see "Replacing the classics").
|
||||||
|
git aliases live in git.nix (below).
|
||||||
|
|
||||||
## CLI tools
|
## CLI tools
|
||||||
|
|
||||||
| Tool | What it gives you |
|
| Tool | What it gives you |
|
||||||
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `fzf` | `Ctrl-R` fuzzy history, `Ctrl-T` file picker, `Alt-C` fuzzy cd (Catppuccin-themed) |
|
| `fzf` | `Ctrl-R` fuzzy history, `Ctrl-T` file picker, `Alt-C` fuzzy cd (Catppuccin-themed) |
|
||||||
| `zoxide` | `z <fragment>` jumps to frecent directories |
|
| `zoxide` | `z <fragment>` jumps to frecent directories |
|
||||||
| `direnv` + `nix-direnv` | per-project environments auto-loaded on `cd` (cached Nix dev shells) |
|
| `direnv` + `nix-direnv` | per-project environments auto-loaded on `cd` (cached Nix dev shells) |
|
||||||
| `eza` | modern `ls` (drives the ls aliases) |
|
| `eza` | modern `ls` (drives the ls aliases) |
|
||||||
| `bat` | syntax-highlighting pager (Catppuccin Mocha theme); behaves like `cat` when piped; also the `MANPAGER` |
|
| `bat` | syntax-highlighting pager (Catppuccin Mocha theme); behaves like `cat` when piped; also the `MANPAGER` |
|
||||||
| `ripgrep` / `fd` | fast search (`rg`) and find (`fd`); also back `fzf` |
|
| `ripgrep` / `fd` | fast search (`rg`) and find (`fd`); also back `fzf` |
|
||||||
| `jq` | JSON processor |
|
| `jq` | JSON processor |
|
||||||
| `gh` / `tea` | GitHub and Gitea (`code.emmathe.dev`) CLIs; `gh` uses SSH |
|
| `gh` / `tea` | GitHub and Gitea (`code.emmathe.dev`) CLIs; `gh` uses SSH |
|
||||||
| `nix-index` | `command-not-found`: an unknown command tells you which Nix package provides it (prebuilt DB, no manual indexing) |
|
| `nix-index` | `command-not-found`: an unknown command tells you which Nix package provides it (prebuilt DB, no manual indexing) |
|
||||||
| `comma` (`,`) | run an uninstalled program once: `, cowsay hi` |
|
| `comma` (`,`) | run an uninstalled program once: `, cowsay hi` |
|
||||||
| `nh` | nicer `nixos-rebuild`/`home-manager` with diffs; `$NH_FLAKE` set to the repo. No scheduled GC (it could reap paths a running generation still references) — collect garbage manually with `nh clean all` / `nix-collect-garbage -d` |
|
| `nh` | nicer `nixos-rebuild`/`home-manager` with diffs; `$NH_FLAKE` set to the repo. No scheduled GC (it could reap paths a running generation still references) — collect garbage manually with `nh clean all` / `nix-collect-garbage -d` |
|
||||||
| `btop` | resource monitor, themed Catppuccin Mocha (vendored theme) |
|
| `btop` | resource monitor, themed Catppuccin Mocha (vendored theme) |
|
||||||
| `lazygit` | git TUI for staging/rebasing, themed to match (`git.nix`) |
|
| `lazygit` | git TUI for staging/rebasing, themed to match (`git.nix`) |
|
||||||
| `hyperfine` / `sd` | command-line benchmarking; saner find-and-replace than sed |
|
| `hyperfine` / `sd` | command-line benchmarking; saner find-and-replace than sed |
|
||||||
|
| `tldr` (tealdeer) | worked examples for a command, alongside `man`; the page cache is refreshed by a `tldr-update` user timer |
|
||||||
|
| `jnv` / `fq` | interactive jq-filter builder for JSON; jq syntax over binary formats (ELF, PNG, gzip, mp4…) |
|
||||||
|
| `hexyl` | hex viewer, coloured by byte class |
|
||||||
|
| `ouch` | one command for every archive format (`ouch d`/`c`/`l`) |
|
||||||
|
| `dust` `dysk` `procs` | `du` / `df` / `ps` replacements — aliased over the originals, see below |
|
||||||
|
| `trash-cli` `doggo` `xh` | `rm` (to the XDG trash) / `dig` / `curl` replacements — **not** aliased, see below |
|
||||||
|
|
||||||
**Theming:** `fzf`, `bat`, `btop`, `lazygit` and `git`'s `delta` pager are all
|
**Theming:** `fzf`, `bat`, `btop`, `lazygit` and `git`'s `delta` pager are all
|
||||||
Catppuccin Mocha, driven from the shared `../lib/catppuccin-mocha.nix` palette / the
|
Catppuccin Mocha, driven from the shared `../lib/catppuccin-mocha.nix` palette / the
|
||||||
@@ -64,6 +72,125 @@ catppuccin upstream themes.
|
|||||||
(the editor owns `$EDITOR`/`$VISUAL`); `xdg.mimeApps` maps web→Firefox,
|
(the editor owns `$EDITOR`/`$VISUAL`); `xdg.mimeApps` maps web→Firefox,
|
||||||
directories→nemo (`desktop.nix`).
|
directories→nemo (`desktop.nix`).
|
||||||
|
|
||||||
|
## Replacing the classics
|
||||||
|
|
||||||
|
Muscle memory is the expensive part of this, not the packages. Four commands are
|
||||||
|
**shadowed** — the old name now runs a new tool. Everything else keeps a new
|
||||||
|
name, so the original is never displaced.
|
||||||
|
|
||||||
|
### Shadowed by an alias
|
||||||
|
|
||||||
|
| You type | You now run | The original is still `command <name>` / `\<name>` |
|
||||||
|
| -------- | -------------------- | -------------------------------------------------- |
|
||||||
|
| `cat` | `bat --paging=never` | `command cat` |
|
||||||
|
| `du` | `dust` | `command du` |
|
||||||
|
| `df` | `dysk` | `command df` |
|
||||||
|
| `ps` | `procs` | `command ps` |
|
||||||
|
|
||||||
|
Only read-only commands are shadowed, so the worst case of a wrong flag is a
|
||||||
|
retype rather than lost data. `rm`, `grep`, `curl` and `find` are deliberately
|
||||||
|
left alone — see "Left alone on purpose" below.
|
||||||
|
|
||||||
|
**Where the aliases apply.** They are written into `~/.config/zsh/.zshrc`, so
|
||||||
|
they exist only in an **interactive zsh**:
|
||||||
|
|
||||||
|
- shell scripts, `Makefile` recipes and anything another program `exec`s get the
|
||||||
|
real coreutils binary — nothing that parses output can break;
|
||||||
|
- `sudo du -sh /var` runs the real `du`: zsh does not expand an alias after
|
||||||
|
`sudo`;
|
||||||
|
- `KUBECONFIG=… kubectl …` **does** expand — zsh expands aliases after a
|
||||||
|
variable-assignment prefix. That is what makes the kubecolor alias on the work
|
||||||
|
box (below) useful rather than a special case you have to remember.
|
||||||
|
|
||||||
|
### Flag gotchas
|
||||||
|
|
||||||
|
These replacements are not drop-in. The two marked **silent** are the dangerous
|
||||||
|
ones — they succeed and answer a different question than the one you asked.
|
||||||
|
Everything else fails loudly.
|
||||||
|
|
||||||
|
| Old habit | What happens now | Do this instead |
|
||||||
|
| ------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
|
||||||
|
| `du -sh dir` | dust prints its usage and exits non-zero — `-h` is not a dust flag | `dust dir` (units are human by default; the total is the last row) |
|
||||||
|
| `du -s dir` | **silent**: dust's `-s` is `--apparent-size`, not `--summarize` | `dust -d 0 dir` for a single total line |
|
||||||
|
| `du --max-depth=2` | not recognised | `dust -d 2` |
|
||||||
|
| `df -h` | dysk rejects `-h` | `dysk` (SI units by default; `-u binary` for 1024-based) |
|
||||||
|
| `df -i` | not recognised | `dysk -c +inodes` |
|
||||||
|
| `df -a` | works, same meaning (all mount points) | — |
|
||||||
|
| `df /some/path` | works, same meaning (the device holding that path) | — |
|
||||||
|
| `ps aux` | **silent**: `aux` is read as a search keyword, so you get only processes whose command line contains the string "aux" | `procs` lists everything; `procs <pattern>` filters |
|
||||||
|
| `ps -ef` | `error: unexpected argument '-e'` | `procs` |
|
||||||
|
| `ps -p 1234` | not recognised | `procs 1234` |
|
||||||
|
| `procs -a` | **silent**: `-a` is `--and` (combine search keywords), not "all" | drop it — `procs` already shows everything |
|
||||||
|
| `cat -v` / `cat -e` | `error: unexpected argument` | `cat -A` does work (bat implements show-all); else `command cat -v` |
|
||||||
|
| `cat -n` | works, but bat's number column, not coreutils' layout | fine to read; `command cat -n` when the exact layout matters |
|
||||||
|
| `cat <binary>` | prints `<BINARY>` to a terminal instead of dumping the bytes | `hexyl <file>`, or `command cat` to dump |
|
||||||
|
|
||||||
|
Useful new capabilities in the same tools: `procs --tree`, `procs --watch`,
|
||||||
|
`dust -r` (largest at the top), `dysk -s size`, `dysk -f 'type=ext4'`.
|
||||||
|
|
||||||
|
**Piping is safe for `cat`.** bat drops all decoration and colour when stdout is
|
||||||
|
not a terminal, so `cat f | sha256sum` is byte-for-byte what coreutils `cat`
|
||||||
|
would have given. The others are TUI-shaped tables with no stable format — if
|
||||||
|
something needs to parse them, use `dysk --json`/`--csv`, `procs --json`, or the
|
||||||
|
original binary.
|
||||||
|
|
||||||
|
### Renamed, not shadowed
|
||||||
|
|
||||||
|
| Instead of | Use | Notes |
|
||||||
|
| --------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| `rm` | `trash` | Moves to the XDG trash. `trash-list`, `trash-restore` (interactive picker), `trash-empty [days]`. It never deletes in place: if it cannot create a trash directory on that filesystem it errors out. |
|
||||||
|
| `dig` / `nslookup` | `doggo` | `doggo example.com MX @1.1.1.1`; `--json` for scripting. Not aliased — `dig` (from the `bind` closure that other modules pull in) stays where scripts expect it. |
|
||||||
|
| `curl` (interactive poking) | `xh` | HTTPie syntax: `xh POST api.example/x name=lyra`. `xhs` is `xh --https`. **curl stays installed and unaliased** — it is what scripts and CI use. |
|
||||||
|
| `tar` / `unzip` / `7z` | `ouch` | `ouch d file.<anything>`, `ouch c out.tar.zst src/`, `ouch l archive`. Format is inferred from the extension. The oh-my-zsh `extract` function still works too. |
|
||||||
|
| `jq` (exploring a payload) | `jnv` | Interactive filter builder over a JSON file; it prints the jq expression you built. `jq` remains the scripting tool. |
|
||||||
|
| `hexdump -C` / `xxd` | `hexyl` | `hexyl -n 256 -s 0x40 file` for a window into a large file. |
|
||||||
|
| `strings` on a known format | `fq` | jq syntax over binary formats: `fq -d elf '.sections[].name' ./bin`. |
|
||||||
|
| skimming a man page | `tldr` | Worked examples. `man` is untouched (and still rendered through bat). |
|
||||||
|
|
||||||
|
### Left alone on purpose
|
||||||
|
|
||||||
|
- **`grep`** is not aliased to `rg`. ripgrep is recursive by default, skips
|
||||||
|
gitignored and hidden files, and uses a different regex dialect (no
|
||||||
|
backreferences, no POSIX classes in the same form). A `grep` habit silently
|
||||||
|
producing fewer matches is a worse failure than typing three characters. Type
|
||||||
|
`rg`.
|
||||||
|
- **`rm`** is not aliased to `trash-put`. Retraining `rm` to mean "recoverable"
|
||||||
|
is a habit that follows you onto every machine where it is not — remote hosts,
|
||||||
|
root shells, containers, CI. Type `trash`.
|
||||||
|
- **`find`** is not aliased to `fd`; the `-exec`/`-print0` vocabulary has no
|
||||||
|
equivalent and scripts lean on it. Type `fd`.
|
||||||
|
- **`sed`** is not aliased to `sd`; `sd` takes real regex and literal
|
||||||
|
replacements, not sed's expression language. Type `sd`.
|
||||||
|
- **coreutils itself** is not swapped for `uutils-coreutils`. It is packaged and
|
||||||
|
tempting, but every Nix builder and shell script on these hosts is written
|
||||||
|
against GNU behaviour, including its forty-year-old edge cases.
|
||||||
|
|
||||||
|
### Work box only: kubectl → kubecolor
|
||||||
|
|
||||||
|
On EDaaS (`work.nix`) `kubectl` is aliased to **kubecolor**, which runs the real
|
||||||
|
kubectl underneath and colourises what comes back. Nothing to relearn: every
|
||||||
|
flag, subcommand and plugin passes straight through, unrecognised output is
|
||||||
|
printed verbatim, and colour is dropped automatically when stdout is not a
|
||||||
|
terminal — so `kubectl get -o json … | jq` is unchanged. The alias also applies
|
||||||
|
to `KUBECONFIG=prodconfig kubectl …`, per the alias-expansion note above.
|
||||||
|
Completions are kubectl's own (`compdef kubecolor=kubectl`). Escape hatch as
|
||||||
|
ever: `command kubectl`.
|
||||||
|
|
||||||
|
### sudo → sudo-rs
|
||||||
|
|
||||||
|
Every NixOS host now uses **sudo-rs**, the memory-safe reimplementation, in
|
||||||
|
place of `sudo` (`modules/common-nixos.nix`; the macOS host keeps Apple's sudo
|
||||||
|
with Touch ID). Day to day there is nothing to learn — `sudo`, `sudo -i`,
|
||||||
|
`sudo -u`, `sudo -l`, `sudoedit` and `visudo` all behave as before against this
|
||||||
|
fleet's stock "wheel, with a password" policy. What it does **not** implement:
|
||||||
|
host aliases, LDAP/SSSD sudoers, `sudoreplay`, and most `Defaults` settings.
|
||||||
|
Needing any of those means reverting to `security.sudo`.
|
||||||
|
|
||||||
|
If a host ever refuses to escalate, get a root shell that does not go through
|
||||||
|
sudo (`wsl -u root -d NixOS` on the work box; the console or a serial/HDMI login
|
||||||
|
elsewhere) and roll back with `nixos-rebuild switch --rollback`, or pick the
|
||||||
|
previous generation from the boot menu.
|
||||||
|
|
||||||
## tmux
|
## tmux
|
||||||
|
|
||||||
**Auto-start:** opening any interactive terminal — foot, iTerm2, the WSL shell, the
|
**Auto-start:** opening any interactive terminal — foot, iTerm2, the WSL shell, the
|
||||||
@@ -130,7 +257,7 @@ place of the old (inert) ALE.
|
|||||||
|
|
||||||
Leader is `Space`. LSP keymaps (`gd`, `gr`, `K`, `<leader>rn`, `<leader>ca`) and
|
Leader is `Space`. LSP keymaps (`gd`, `gr`, `K`, `<leader>rn`, `<leader>ca`) and
|
||||||
the file-tree toggle are listed in
|
the file-tree toggle are listed in
|
||||||
[`KEYBINDINGS.md`](./KEYBINDINGS.md#neovim). Add a universal language server by
|
[`keybindings.md`](./keybindings.md#neovim). Add a universal language server by
|
||||||
enabling it under `programs.nixvim.plugins.lsp.servers` in `editor.nix`;
|
enabling it under `programs.nixvim.plugins.lsp.servers` in `editor.nix`;
|
||||||
host-specific ones go in that host's module — the work box (`work.nix`) adds
|
host-specific ones go in that host's module — the work box (`work.nix`) adds
|
||||||
`omnisharp` (C#) and `helm_ls` (Helm), kept off the personal machines.
|
`omnisharp` (C#) and `helm_ls` (Helm), kept off the personal machines.
|
||||||
@@ -141,13 +268,21 @@ Pager is **delta**. **commitizen** is installed on every host; `cz` defaults to
|
|||||||
Conventional Commits. **lazygit** (themed) is the TUI. The commit-graph is kept
|
Conventional Commits. **lazygit** (themed) is the TUI. The commit-graph is kept
|
||||||
current (`gc`/`fetch.writeCommitGraph`) so `lg` stays fast.
|
current (`gc`/`fetch.writeCommitGraph`) so `lg` stays fast.
|
||||||
|
|
||||||
| Aliases | |
|
| Aliases | |
|
||||||
| ------------------------ | ------------------------------------------------------------------ |
|
| ------------------------ | ------------------------------------------------------------------------- |
|
||||||
| `st` `co` `sw` `br` `ci` | status / checkout / switch / branch / commit |
|
| `st` `co` `sw` `br` `ci` | status / checkout / switch / branch / commit |
|
||||||
| `last` `unstage` | last commit / unstage |
|
| `last` `unstage` | last commit / unstage |
|
||||||
| `amend` `fixup` `undo` | amend-no-edit / `commit --fixup` / soft-reset HEAD~1 (keep staged) |
|
| `amend` `fixup` `undo` | amend-no-edit / `commit --fixup` / soft-reset HEAD~1 (keep staged) |
|
||||||
| `lg` | graph log, all branches |
|
| `lg` | graph log, all branches |
|
||||||
| `cz` `cc` | `git cz <sub>` (e.g. `git cz c`) and `git cc` → commitizen prompt |
|
| `cz` `cc` | `git cz <sub>` (e.g. `git cz c`) and `git cc` → commitizen prompt |
|
||||||
|
| `dft` | structural (syntax-aware) diff via difftastic; takes `git diff` arguments |
|
||||||
|
|
||||||
|
**`git dft` vs `git diff`.** delta stays the default renderer for everything;
|
||||||
|
`diff.external` is deliberately **not** set, so `git diff`, `git show` and
|
||||||
|
anything parsing their output are unchanged. Reach for `dft` when a refactor
|
||||||
|
moved code around and a line-based diff is noise. One wrinkle: `dft` is a
|
||||||
|
`!`-shell alias, and git runs those from the repository root — pass pathspecs
|
||||||
|
relative to the root, not to your current directory.
|
||||||
|
|
||||||
| Behaviour | |
|
| Behaviour | |
|
||||||
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
@@ -175,7 +310,7 @@ forced off there) but still runs the agent.
|
|||||||
|
|
||||||
## Claude Code
|
## Claude Code
|
||||||
|
|
||||||
Managed declaratively by [`claude.nix`](./claude.nix) on every host whose CPU
|
Managed declaratively by [`claude.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/home/claude.nix) on every host whose CPU
|
||||||
can run it (the CLI is `pkgs.claude-code`, tracked to unstable via the flake
|
can run it (the CLI is `pkgs.claude-code`, tracked to unstable via the flake
|
||||||
overlay).
|
overlay).
|
||||||
|
|
||||||
@@ -197,7 +332,7 @@ and the standalone `homeConfigurations` — keep it enabled.
|
|||||||
break.
|
break.
|
||||||
|
|
||||||
**Memory is sourced from this repo.** The files in
|
**Memory is sourced from this repo.** The files in
|
||||||
[`claude/memory/`](./claude/memory) are the source of truth; they are symlinked
|
[`claude/memory/`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/home/claude/memory) are the source of truth; they are symlinked
|
||||||
read-only into `~/.claude/memory`, so recall works but the runtime "save a
|
read-only into `~/.claude/memory`, so recall works but the runtime "save a
|
||||||
memory" path does not. To add/change/remove a memory, edit `claude/memory/`
|
memory" path does not. To add/change/remove a memory, edit `claude/memory/`
|
||||||
(one file per memory + the `MEMORY.md` index) and rebuild — `CLAUDE.md` tells
|
(one file per memory + the `MEMORY.md` index) and rebuild — `CLAUDE.md` tells
|
||||||
@@ -214,10 +349,12 @@ Claude to route new memories there.
|
|||||||
|
|
||||||
## Per-host differences
|
## Per-host differences
|
||||||
|
|
||||||
| | Personal Linux (sway) | macOS | Work WSL (EDaaS) |
|
| | Personal Linux (sway) | macOS | Work WSL (EDaaS) |
|
||||||
| --------------------------- | --------------------- | ----------------- | --------------------------- |
|
| --------------------------- | --------------------- | --------------------- | --------------------------- |
|
||||||
| Auto-tmux | yes (foot/TTY) | yes (iTerm2) | yes (WSL shell) |
|
| Auto-tmux | yes (foot/TTY) | yes (iTerm2) | yes (WSL shell) |
|
||||||
| git email | `iam@emmathe.dev` | `iam@emmathe.dev` | `…@citrix.com` (work) |
|
| `kubectl` → kubecolor | no (no kubectl) | no | yes (work module) |
|
||||||
| ssh config managed | yes | yes | no (keeps corporate config) |
|
| `sudo` implementation | sudo-rs | Apple sudo + Touch ID | sudo-rs |
|
||||||
| ssh-agent | yes | launchd | yes (work module) |
|
| git email | `iam@emmathe.dev` | `iam@emmathe.dev` | `…@citrix.com` (work) |
|
||||||
| GUI / theming (desktop.nix) | yes | no | no |
|
| ssh config managed | yes | yes | no (keeps corporate config) |
|
||||||
|
| ssh-agent | yes | launchd | yes (work module) |
|
||||||
|
| GUI / theming (desktop.nix) | yes | no | no |
|
||||||
Generated
+21
@@ -185,6 +185,26 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"legacy-email-proxy": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1787315211,
|
||||||
|
"narHash": "sha256-FuZ9nXMRtnMPO/wbjYkpsKn6K/FFc64P5XDmCyfyxGs=",
|
||||||
|
"ref": "refs/heads/main",
|
||||||
|
"rev": "f1e1373fd350fd77f1848eddfa67ed9e00724c25",
|
||||||
|
"revCount": 13,
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://code.emmathe.dev/lyrathorpe/legacy-email-proxy"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://code.emmathe.dev/lyrathorpe/legacy-email-proxy"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nix-darwin": {
|
"nix-darwin": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -368,6 +388,7 @@
|
|||||||
"git-hooks": "git-hooks",
|
"git-hooks": "git-hooks",
|
||||||
"home-manager": "home-manager",
|
"home-manager": "home-manager",
|
||||||
"kube-tmux": "kube-tmux",
|
"kube-tmux": "kube-tmux",
|
||||||
|
"legacy-email-proxy": "legacy-email-proxy",
|
||||||
"nix-darwin": "nix-darwin",
|
"nix-darwin": "nix-darwin",
|
||||||
"nix-homebrew": "nix-homebrew",
|
"nix-homebrew": "nix-homebrew",
|
||||||
"nix-index-database": "nix-index-database",
|
"nix-index-database": "nix-index-database",
|
||||||
|
|||||||
@@ -67,6 +67,13 @@
|
|||||||
url = "github:jonmosco/kube-tmux";
|
url = "github:jonmosco/kube-tmux";
|
||||||
flake = false;
|
flake = false;
|
||||||
};
|
};
|
||||||
|
# legacy-email-proxy: cleartext POP3/SMTP front end for the Psion's mail
|
||||||
|
# client, proxied to authenticated IMAPS/SMTPS. Ships its own package and
|
||||||
|
# NixOS module; the Pi Zero 2 W host just enables the service.
|
||||||
|
legacy-email-proxy = {
|
||||||
|
url = "git+https://code.emmathe.dev/lyrathorpe/legacy-email-proxy";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs =
|
outputs =
|
||||||
@@ -327,6 +334,26 @@
|
|||||||
./users/lyrathorpe/home.nix
|
./users/lyrathorpe/home.nix
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
lyrathorpe-zero2w = {
|
||||||
|
system = "aarch64-linux";
|
||||||
|
portable = false;
|
||||||
|
# Headless "Psion sidecar": PPP over RS232 plus a legacy mail proxy
|
||||||
|
# (hosts/PiZero2W/). No sway.nix; the raspberry-pi-3 profile carries
|
||||||
|
# the kernel/firmware/device tree (the Zero 2 W is the Pi 3's
|
||||||
|
# BCM2837 SoC) and ssh.nix adds key-only sshd. This board has 512 MB
|
||||||
|
# of RAM and never builds its own system -- see
|
||||||
|
# docs/hosts/pizero2w.md.
|
||||||
|
modules = [
|
||||||
|
./hosts/PiZero2W/configuration.nix
|
||||||
|
inputs.nixos-hardware.nixosModules.raspberry-pi-3
|
||||||
|
./modules/ssh.nix
|
||||||
|
];
|
||||||
|
users.lyrathorpe.homeModules = [
|
||||||
|
./home
|
||||||
|
./users/lyrathorpe/home.nix
|
||||||
|
];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# Darwin host table — macOS machines built via mkDarwinHost. The shared
|
# Darwin host table — macOS machines built via mkDarwinHost. The shared
|
||||||
@@ -365,8 +392,24 @@
|
|||||||
# nixpkgs instance for that system. Outputs here become per-system
|
# nixpkgs instance for that system. Outputs here become per-system
|
||||||
# attrsets automatically (e.g. devShells.<system>.default).
|
# attrsets automatically (e.g. devShells.<system>.default).
|
||||||
perSystem =
|
perSystem =
|
||||||
{ config, pkgs, ... }:
|
|
||||||
{
|
{
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
system,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
# One-shot SD card for bringing the Pi Zero 2 W up: that host's own
|
||||||
|
# configuration plus the sd-image module, so the first boot is
|
||||||
|
# already the real system. aarch64-linux only -- building it needs
|
||||||
|
# an aarch64 Linux builder. See docs/hosts/pizero2w.md.
|
||||||
|
packages = lib.optionalAttrs (system == "aarch64-linux") {
|
||||||
|
zero2w-sd-image =
|
||||||
|
((mkHost hosts.lyrathorpe-zero2w).extendModules {
|
||||||
|
modules = [ ./hosts/PiZero2W/sd-image.nix ];
|
||||||
|
}).config.system.build.sdImage;
|
||||||
|
};
|
||||||
|
|
||||||
# treefmt drives `nix fmt` and the formatting check below. nixfmt
|
# treefmt drives `nix fmt` and the formatting check below. nixfmt
|
||||||
# stays the .nix formatter (the tree is already nixfmt-formatted);
|
# stays the .nix formatter (the tree is already nixfmt-formatted);
|
||||||
# shfmt covers shell and prettier covers markdown/yaml/json.
|
# shfmt covers shell and prettier covers markdown/yaml/json.
|
||||||
|
|||||||
@@ -74,6 +74,11 @@ in
|
|||||||
# `cz commit`, `git cz bump`, etc. `git cc` is a shortcut for the prompt.
|
# `cz commit`, `git cz bump`, etc. `git cc` is a shortcut for the prompt.
|
||||||
cz = "!cz";
|
cz = "!cz";
|
||||||
cc = "!cz commit";
|
cc = "!cz commit";
|
||||||
|
# Structural (syntax-aware) diff, on demand. Set per-invocation via the
|
||||||
|
# environment rather than `diff.external`, which would also change what
|
||||||
|
# `git show` and `git log -p --ext-diff` emit for every caller.
|
||||||
|
# Takes the same arguments as `git diff`: `git dft HEAD~3 -- file`.
|
||||||
|
dft = "!GIT_EXTERNAL_DIFF=difft git diff";
|
||||||
};
|
};
|
||||||
|
|
||||||
# SSH signing, key from the registry. mkDefault so a host lacking the key
|
# SSH signing, key from the registry. mkDefault so a host lacking the key
|
||||||
@@ -99,6 +104,14 @@ in
|
|||||||
enableGitIntegration = true;
|
enableGitIntegration = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# difftastic backs the `dft` alias above. git.enable stays off on purpose:
|
||||||
|
# the module's git integration sets `diff.external`, which would displace
|
||||||
|
# delta as the diff renderer everywhere instead of only where asked.
|
||||||
|
programs.difftastic = {
|
||||||
|
enable = true;
|
||||||
|
git.enable = false;
|
||||||
|
};
|
||||||
|
|
||||||
# lazygit: TUI for staging/rebasing, themed to Catppuccin Mocha to match.
|
# lazygit: TUI for staging/rebasing, themed to Catppuccin Mocha to match.
|
||||||
programs.lazygit = {
|
programs.lazygit = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|||||||
@@ -26,8 +26,32 @@ in
|
|||||||
pkgs.tea
|
pkgs.tea
|
||||||
pkgs.hyperfine # command-line benchmarking
|
pkgs.hyperfine # command-line benchmarking
|
||||||
pkgs.sd # saner find-and-replace than sed
|
pkgs.sd # saner find-and-replace than sed
|
||||||
|
|
||||||
|
# Replacements for the classic coreutils/BSD tools. Only the read-only ones
|
||||||
|
# are aliased over the original name (see shellAliases below); the rest keep
|
||||||
|
# their own name so nothing changes shape under a script's feet. The alias
|
||||||
|
# map and the flag-compatibility differences are documented in
|
||||||
|
# ../docs/shell.md, "Replacing the classics".
|
||||||
|
pkgs.dust # du: tree-shaped, size-sorted disk usage
|
||||||
|
pkgs.dysk # df: mounted filesystems (duf is unmaintained upstream)
|
||||||
|
pkgs.procs # ps: process list with tree, ports and container columns
|
||||||
|
pkgs.trash-cli # rm: XDG trash; `trash` / `trash-list` / `trash-restore`
|
||||||
|
pkgs.doggo # dig: DNS lookups
|
||||||
|
pkgs.xh # curl, for interactive HTTP poking (curl stays for scripts)
|
||||||
|
pkgs.ouch # tar/unzip/7z/zstd: one command for every archive format
|
||||||
|
pkgs.jnv # interactive jq filter builder (jq itself stays for scripts)
|
||||||
|
pkgs.hexyl # hex viewer
|
||||||
|
pkgs.fq # jq for binary formats
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# tldr pages: worked examples for a command, next to (not instead of) man.
|
||||||
|
# enableAutoUpdates defaults on and installs a tldr-update user timer, which
|
||||||
|
# keeps the page cache fresh -- without it `tldr` fails until first `--update`.
|
||||||
|
programs.tealdeer = {
|
||||||
|
enable = true;
|
||||||
|
settings.display.compact = true;
|
||||||
|
};
|
||||||
|
|
||||||
# Resource monitor, themed Catppuccin Mocha to match the rest of the desktop.
|
# Resource monitor, themed Catppuccin Mocha to match the rest of the desktop.
|
||||||
# btop does not bundle the theme, so vendor it from catppuccin/btop (pinned).
|
# btop does not bundle the theme, so vendor it from catppuccin/btop (pinned).
|
||||||
programs.btop = {
|
programs.btop = {
|
||||||
@@ -137,6 +161,26 @@ in
|
|||||||
la = "eza --icons --git -la";
|
la = "eza --icons --git -la";
|
||||||
lt = "eza --icons --git --tree";
|
lt = "eza --icons --git --tree";
|
||||||
cls = "clear";
|
cls = "clear";
|
||||||
|
|
||||||
|
# Shadow the classics with their modern equivalents. Only read-only
|
||||||
|
# commands are shadowed: a wrong flag costs a retype, never data. The
|
||||||
|
# flag vocabularies are NOT compatible (`du -sh`, `df -h`, `ps aux` all
|
||||||
|
# fail here) -- see ../docs/shell.md, "Replacing the classics".
|
||||||
|
#
|
||||||
|
# Blast radius is bounded by where these live: shellAliases lands in
|
||||||
|
# .zshrc, so only interactive zsh sees them. Scripts, `sudo <cmd>` and
|
||||||
|
# anything exec'd by another program still get the real binary. To reach
|
||||||
|
# the original in an interactive shell: `command du` or `\du`.
|
||||||
|
cat = "bat --paging=never"; # bat is already the PAGER/MANPAGER
|
||||||
|
du = "dust";
|
||||||
|
df = "dysk";
|
||||||
|
ps = "procs";
|
||||||
|
|
||||||
|
# `rm` is deliberately NOT aliased to trash-put. Retraining `rm` to mean
|
||||||
|
# "recoverable" is a habit that follows you onto machines where it does
|
||||||
|
# not (every remote host, every root shell, every container), and trash
|
||||||
|
# semantics break down anyway on a different filesystem or on
|
||||||
|
# root-owned paths. Type `trash` when you want a trash can.
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Apple Mac Pro 3,1 (Early 2008, dual Xeon Harpertown, x86_64). Desktop host:
|
# Apple Mac Pro 3,1 (Early 2008, dual Xeon Harpertown, x86_64). Desktop host:
|
||||||
# shared graphical/wired options live in ../../modules/desktop.nix; only
|
# shared graphical/wired options live in ../../modules/desktop.nix; only
|
||||||
# host-specific settings are here. Install notes (EFI booting, GPU, partitions):
|
# host-specific settings are here. Install notes (EFI booting, GPU, partitions):
|
||||||
# see ./README.md.
|
# see ../../docs/hosts/macpro31.md.
|
||||||
{ ... }:
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,6 +28,18 @@
|
|||||||
open = false;
|
open = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# The NVIDIA module only puts these in boot.kernelModules when
|
||||||
|
# services.xserver.enable is true, which is false on this Wayland-only host --
|
||||||
|
# so load them explicitly rather than relying on udev modalias autoloading.
|
||||||
|
# nvidia_uvm (needed by CUDA) is deliberately absent: the module's modprobe
|
||||||
|
# softdep pulls it in after the GPU device exists, which is the supported
|
||||||
|
# ordering.
|
||||||
|
boot.kernelModules = [
|
||||||
|
"nvidia"
|
||||||
|
"nvidia_modeset"
|
||||||
|
"nvidia_drm"
|
||||||
|
];
|
||||||
|
|
||||||
# wlroots refuses the proprietary NVIDIA driver unless told to proceed. The
|
# wlroots refuses the proprietary NVIDIA driver unless told to proceed. The
|
||||||
# greeter's compositor (cage) has no such check; only Sway needs the flag,
|
# greeter's compositor (cage) has no such check; only Sway needs the flag,
|
||||||
# which the module bakes into the wrapper the session's .desktop file runs.
|
# which the module bakes into the wrapper the session's .desktop file runs.
|
||||||
@@ -40,4 +52,15 @@
|
|||||||
# with `docker run --device=nvidia.com/gpu=all ...`. The deprecated
|
# with `docker run --device=nvidia.com/gpu=all ...`. The deprecated
|
||||||
# virtualisation.docker.enableNvidia runtime wrapper is deliberately not used.
|
# virtualisation.docker.enableNvidia runtime wrapper is deliberately not used.
|
||||||
hardware.nvidia-container-toolkit.enable = true;
|
hardware.nvidia-container-toolkit.enable = true;
|
||||||
|
|
||||||
|
# The generator needs a loaded kernel module: without one it aborts with
|
||||||
|
# "failed to initialize NVML: Driver Not Loaded". That is guaranteed after a
|
||||||
|
# kernel bump, where the rebuilt module cannot load until reboot -- and since
|
||||||
|
# the unit is requiredBy docker.service and wantedBy multi-user.target, the
|
||||||
|
# failure takes Docker down and makes `nixos-rebuild switch` exit non-zero.
|
||||||
|
# Skip the run instead when no driver is loaded; the toolkit's udev rule
|
||||||
|
# restarts the unit as soon as the nvidia device appears, so the CDI specs are
|
||||||
|
# still generated on the next boot.
|
||||||
|
systemd.services.nvidia-container-toolkit-cdi-generator.unitConfig.ConditionPathExists =
|
||||||
|
"/proc/driver/nvidia/version";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
# Raspberry Pi Zero 2 W (aarch64) "Psion sidecar": an RS232 companion for a
|
||||||
|
# Psion 5MX. Two roles, split into submodules: ./serial-ppp.nix (PPP over the
|
||||||
|
# serial line, NAT out to wifi, telnet login) and ./email-proxy.nix (cleartext
|
||||||
|
# POP3/SMTP front end for the Psion's mail client). The raspberry-pi-3
|
||||||
|
# nixos-hardware profile (the Zero 2 W is the same BCM2837 SoC as the Pi 3) and
|
||||||
|
# key-only sshd (../../modules/ssh.nix) are layered on in the flake host table.
|
||||||
|
# Install notes: see ../../docs/hosts/pizero2w.md.
|
||||||
|
{ lib, ... }:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./hardware-configuration.nix
|
||||||
|
./serial-ppp.nix
|
||||||
|
./email-proxy.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
# Match the flake's nixosConfigurations attribute name so `nh os switch`
|
||||||
|
# (which selects by the local hostname) resolves without an explicit -H flag.
|
||||||
|
networking.hostName = "lyrathorpe-zero2w";
|
||||||
|
|
||||||
|
# Headless server: modules/sway.nix is not imported and
|
||||||
|
# features.swayDesktop.enable defaults to false, so this host keeps plain
|
||||||
|
# TTY/SSH login.
|
||||||
|
|
||||||
|
# Claude Code is a Node application. It runs on aarch64, but not usefully in
|
||||||
|
# 512 MB of RAM, and its closure is unwelcome on an SD card.
|
||||||
|
features.claudeCode.enable = false;
|
||||||
|
|
||||||
|
# 512 MB total and no swap partition -- SD cards wear out under swap writes.
|
||||||
|
# Compressed RAM swap instead; zstd is the best ratio-per-cycle the SoC can
|
||||||
|
# sustain.
|
||||||
|
zramSwap = {
|
||||||
|
enable = true;
|
||||||
|
algorithm = "zstd";
|
||||||
|
};
|
||||||
|
|
||||||
|
# The NixOS manual and man page index cost build time and a chunk of the card
|
||||||
|
# for a box that is administered over SSH from elsewhere.
|
||||||
|
documentation.nixos.enable = false;
|
||||||
|
|
||||||
|
# Own the firmware partition declaratively: every switch rewrites config.txt,
|
||||||
|
# the vendor device trees and the overlays below. Without this the card keeps
|
||||||
|
# whatever config.txt the flashed image wrote and the UART overlays never
|
||||||
|
# load. uboot.enable keeps the GPU firmware chainloading U-Boot -> extlinux,
|
||||||
|
# which is how the NixOS aarch64 SD image boots; leaving it off would rewrite
|
||||||
|
# config.txt without a `kernel=` line and the board would stop booting.
|
||||||
|
hardware.raspberry-pi.firmware = {
|
||||||
|
enable = true;
|
||||||
|
uboot.enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
hardware.raspberry-pi.configtxt = {
|
||||||
|
settings.all = {
|
||||||
|
# Headless: hand the VideoCore the minimum and leave the rest to Linux.
|
||||||
|
# start_x/camera_auto_detect otherwise reserve VRAM for a camera stack
|
||||||
|
# this board does not have.
|
||||||
|
gpu_mem = 16;
|
||||||
|
start_x = 0;
|
||||||
|
camera_auto_detect = false;
|
||||||
|
# Left on, the firmware auto-loads the KMS display overlay, which wants
|
||||||
|
# more VRAM than this board can spare for a monitor it will never have.
|
||||||
|
display_auto_detect = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Replaces the profile's default (vc4-kms-v3d), which is display hardware
|
||||||
|
# this host never uses.
|
||||||
|
deviceTreeOverlays.all = [
|
||||||
|
# Move the PL011 UART off Bluetooth and onto GPIO 14/15, so /dev/ttyAMA0
|
||||||
|
# is the RS232 header. The mini UART (ttyS0) derives its baud rate from
|
||||||
|
# the core clock and drifts at 115200.
|
||||||
|
{ disable-bt = { }; }
|
||||||
|
# RTS/CTS on GPIO 16/17: the Psion's modem profile uses hardware flow
|
||||||
|
# control, and so does pppd in ./serial-ppp.nix.
|
||||||
|
{ uart0.ctsrts = true; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Wifi is the Pi's uplink and the route the Psion reaches the internet over
|
||||||
|
# (./serial-ppp.nix masquerades onto it).
|
||||||
|
networking.interfaces.wlan0.useDHCP = true;
|
||||||
|
networking.wireless = {
|
||||||
|
enable = true;
|
||||||
|
interfaces = [ "wlan0" ];
|
||||||
|
# PSKs stay out of the Nix store: wpa_supplicant reads them at runtime from
|
||||||
|
# this file, which is created on the device (root-owned, 0600) and contains
|
||||||
|
# psk_home=<the pre-shared key>
|
||||||
|
# See ../../docs/hosts/pizero2w.md.
|
||||||
|
secretsFile = "/var/lib/wpa_supplicant/secrets.conf";
|
||||||
|
networks."CHANGE-ME-SSID".pskRaw = "ext:psk_home";
|
||||||
|
};
|
||||||
|
|
||||||
|
# The board takes a DHCP lease over wifi, so its address moves. mDNS makes it
|
||||||
|
# findable as lyrathorpe-zero2w.local instead of hunting through the router's
|
||||||
|
# lease table -- which matters most on first boot, when it is the only way in.
|
||||||
|
services.avahi = {
|
||||||
|
enable = true;
|
||||||
|
openFirewall = true;
|
||||||
|
publish = {
|
||||||
|
enable = true;
|
||||||
|
addresses = true;
|
||||||
|
workstation = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Default-deny inbound. sshd opens 22 (../../modules/ssh.nix); everything the
|
||||||
|
# Psion talks to is reached over the PPP link, which ./serial-ppp.nix marks
|
||||||
|
# trusted.
|
||||||
|
networking.firewall.enable = true;
|
||||||
|
|
||||||
|
# See `man configuration.nix` / the stateVersion docs before changing.
|
||||||
|
system.stateVersion = "26.05";
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# legacy-email-proxy: a cleartext POP3 (110) and SMTP (25) front end for the
|
||||||
|
# Psion's built-in mail client, forwarded to authenticated IMAPS/SMTPS.
|
||||||
|
#
|
||||||
|
# The package, the systemd unit and its hardening all live upstream
|
||||||
|
# (https://code.emmathe.dev/lyrathorpe/legacy-email-proxy); this host only
|
||||||
|
# enables the service and points it at the credentials.
|
||||||
|
{ inputs, ... }:
|
||||||
|
{
|
||||||
|
imports = [ inputs.legacy-email-proxy.nixosModules.default ];
|
||||||
|
|
||||||
|
services.legacy-email-proxy = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
# The listeners are unauthenticated and unencrypted by design, so the
|
||||||
|
# firewall is what confines them: ppp0 is trusted, wlan0 is not, and 110/25
|
||||||
|
# are never opened there (./serial-ppp.nix). They stay on the default
|
||||||
|
# 0.0.0.0 rather than the PPP address because 10.0.0.1 exists only while
|
||||||
|
# the Psion is plugged in, and a bind-time dependency on a serial cable is
|
||||||
|
# a restart loop waiting to happen.
|
||||||
|
|
||||||
|
# Backend hostnames and credentials. Kept out of the Nix store: created on
|
||||||
|
# the device, root-owned 0600. See ../../docs/hosts/pizero2w.md.
|
||||||
|
environmentFile = "/var/lib/legacy-email-proxy/backend.env";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# PLACEHOLDER hardware configuration for the Raspberry Pi Zero 2 W.
|
||||||
|
#
|
||||||
|
# This file is NOT the real generated config -- it exists only so the host
|
||||||
|
# evaluates in CI before the Pi is provisioned. The machine will not boot from
|
||||||
|
# it as-is. On first install, regenerate this file on the device with
|
||||||
|
# nixos-generate-config --root /mnt
|
||||||
|
# and replace this placeholder with the output (commit it). See ../../docs/hosts/pizero2w.md.
|
||||||
|
#
|
||||||
|
# Like every hardware-configuration.nix in this repo, this file is excluded from
|
||||||
|
# the formatter and linters (see the pre-commit/treefmt excludes in flake.nix).
|
||||||
|
{ modulesPath, ... }:
|
||||||
|
{
|
||||||
|
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
|
||||||
|
|
||||||
|
nixpkgs.hostPlatform = "aarch64-linux";
|
||||||
|
|
||||||
|
# The Zero 2 W boots from an SD card with a FAT firmware partition and an ext4
|
||||||
|
# root. Labels match the conventional sd-image layout; the real generated
|
||||||
|
# config will use by-uuid device paths instead.
|
||||||
|
fileSystems."/" = {
|
||||||
|
device = "/dev/disk/by-label/NIXOS_SD";
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
|
||||||
|
fileSystems."/boot/firmware" = {
|
||||||
|
device = "/dev/disk/by-label/FIRMWARE";
|
||||||
|
fsType = "vfat";
|
||||||
|
};
|
||||||
|
|
||||||
|
# 512 MB of RAM and an SD card: no swap partition (SD cards wear out under
|
||||||
|
# swap writes). zram takes its place; see ../../hosts/PiZero2W/configuration.nix.
|
||||||
|
swapDevices = [ ];
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# SD-card image of this host, used exactly once: to bring the board up.
|
||||||
|
#
|
||||||
|
# Deliberately NOT imported by ./configuration.nix. The flake extends the host
|
||||||
|
# with it (see packages.aarch64-linux.zero2w-sd-image in ../../flake.nix), so
|
||||||
|
# the card carries the host's own kernel, config.txt and SSH keys rather than a
|
||||||
|
# generic installer that then has to be reconfigured over a console this host
|
||||||
|
# does not have -- pppd owns the serial port (./serial-ppp.nix).
|
||||||
|
#
|
||||||
|
# It does not carry the runtime secrets. Seed those into the card's root
|
||||||
|
# partition before first boot; see ../../docs/hosts/pizero2w.md.
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
modulesPath,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
imports = [ "${modulesPath}/installer/sd-card/sd-image.nix" ];
|
||||||
|
|
||||||
|
# sd-image.nix pulls in profiles/all-hardware.nix, which is every driver and
|
||||||
|
# firmware blob NixOS knows about. The raspberry-pi-3 profile already carries
|
||||||
|
# what this board has, and the card is small.
|
||||||
|
hardware.enableAllHardware = lib.mkForce false;
|
||||||
|
|
||||||
|
image.baseName = "nixos-zero2w";
|
||||||
|
|
||||||
|
sdImage = {
|
||||||
|
# Compressing costs a long single-threaded pass and buys nothing: the image
|
||||||
|
# is written straight to a card with dd.
|
||||||
|
compressImage = false;
|
||||||
|
|
||||||
|
# The default 30 MiB does not hold the vendor GPU firmware, U-Boot and the
|
||||||
|
# BCM2837 device trees and overlays that nixos-hardware installs here.
|
||||||
|
firmwareSize = 128;
|
||||||
|
|
||||||
|
# The firmware partition is populated by nixos-hardware's firmware module
|
||||||
|
# (it takes over sdImage.populateFirmwareCommands); the root side is the
|
||||||
|
# stock extlinux install, which no longer arrives with it.
|
||||||
|
populateRootCommands = ''
|
||||||
|
mkdir -p ./files/boot
|
||||||
|
${config.boot.loader.generic-extlinux-compatible.populateCmd} -c ${config.system.build.toplevel} -d ./files/boot
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
# The serial half of the Psion sidecar: a PPP link to a Psion 5MX over
|
||||||
|
# /dev/ttyAMA0 (RS232 level shifter on the GPIO header, 115200 8N1 with
|
||||||
|
# RTS/CTS), masqueraded out of wifi, plus a telnet login for the Psion's
|
||||||
|
# terminal client.
|
||||||
|
#
|
||||||
|
# Cleartext telnet and unauthenticated PPP are safe *only* because the link is
|
||||||
|
# a two-node cable: the peer is a machine from 1999 that speaks no TLS. Nothing
|
||||||
|
# here is exposed to wlan0.
|
||||||
|
{ pkgs, ... }:
|
||||||
|
let
|
||||||
|
# Point-to-point addresses for the serial link; nothing else routes here.
|
||||||
|
piAddress = "10.0.0.1";
|
||||||
|
psionAddress = "10.0.0.2";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# pppd needs exclusive use of the port. NixOS starts a getty on any serial
|
||||||
|
# console named in boot.kernelParams; ttyAMA0 is not one today, but disable it
|
||||||
|
# explicitly so a later kernel-param change cannot silently steal the line.
|
||||||
|
systemd.services."serial-getty@ttyAMA0".enable = false;
|
||||||
|
|
||||||
|
services.pppd = {
|
||||||
|
enable = true;
|
||||||
|
peers.psion.config = ''
|
||||||
|
/dev/ttyAMA0
|
||||||
|
115200
|
||||||
|
${piAddress}:${psionAddress}
|
||||||
|
|
||||||
|
# Hardware flow control, matching the Psion's modem profile.
|
||||||
|
crtscts
|
||||||
|
|
||||||
|
# A null-modem cable has no carrier detect and no peer to authenticate.
|
||||||
|
local
|
||||||
|
noauth
|
||||||
|
|
||||||
|
# The systemd unit is Type=notify, so pppd must stay in the foreground.
|
||||||
|
nodetach
|
||||||
|
lock
|
||||||
|
|
||||||
|
# Wait for the Psion rather than failing when it is unplugged, and keep
|
||||||
|
# waiting for the next time it is plugged back in.
|
||||||
|
passive
|
||||||
|
persist
|
||||||
|
maxfail 0
|
||||||
|
holdoff 1
|
||||||
|
|
||||||
|
# Hand the Psion resolvers over the link, so its Internet profile can set
|
||||||
|
# "get DNS from server = True" instead of hard-coding them.
|
||||||
|
ms-dns 1.1.1.1
|
||||||
|
ms-dns 8.8.8.8
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# The Psion's route to the internet. The original write-up used pppd's
|
||||||
|
# proxyarp instead; NAT keeps the Psion out of the LAN broadcast domain and
|
||||||
|
# does not depend on what the wifi router tolerates.
|
||||||
|
networking.nat = {
|
||||||
|
enable = true;
|
||||||
|
externalInterface = "wlan0";
|
||||||
|
internalIPs = [ "${psionAddress}/32" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Everything the Psion connects to (telnet here, POP3/SMTP in
|
||||||
|
# ./email-proxy.nix) is reachable over the PPP link and nowhere else.
|
||||||
|
networking.firewall.trustedInterfaces = [ "ppp0" ];
|
||||||
|
|
||||||
|
# The Psion's terminal client speaks telnet over TCP, which it renders far
|
||||||
|
# better than the raw serial console. Socket-activated, one process per
|
||||||
|
# connection; busybox's telnetd in inetd mode hands straight over to login.
|
||||||
|
systemd.sockets.telnetd = {
|
||||||
|
description = "Telnet login socket for the Psion";
|
||||||
|
wantedBy = [ "sockets.target" ];
|
||||||
|
listenStreams = [ "${piAddress}:23" ];
|
||||||
|
socketConfig = {
|
||||||
|
Accept = true;
|
||||||
|
# ppp0 (and with it 10.0.0.1) only exists while the Psion is connected;
|
||||||
|
# FreeBind lets the socket be listening before that.
|
||||||
|
FreeBind = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."telnetd@" = {
|
||||||
|
description = "Telnet login for the Psion";
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = "-${pkgs.busybox}/bin/busybox telnetd -i -l ${pkgs.shadow}/bin/login";
|
||||||
|
StandardInput = "socket";
|
||||||
|
StandardError = "journal";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
# ./docker.nix (Docker host with a network socket) and ./reverse-proxy.nix
|
# ./docker.nix (Docker host with a network socket) and ./reverse-proxy.nix
|
||||||
# (native nginx). The raspberry-pi-5 nixos-hardware profile (kernel, firmware,
|
# (native nginx). The raspberry-pi-5 nixos-hardware profile (kernel, firmware,
|
||||||
# device tree) and key-only sshd (../../modules/ssh.nix) are layered on in the
|
# device tree) and key-only sshd (../../modules/ssh.nix) are layered on in the
|
||||||
# flake host table. Install notes: see ./README.md.
|
# flake host table. Install notes: see ../../docs/hosts/rpi5.md.
|
||||||
{ ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
# evaluates in CI before the Pi is provisioned. The machine will not boot from
|
# evaluates in CI before the Pi is provisioned. The machine will not boot from
|
||||||
# it as-is. On first install, regenerate this file on the device with
|
# it as-is. On first install, regenerate this file on the device with
|
||||||
# nixos-generate-config --root /mnt
|
# nixos-generate-config --root /mnt
|
||||||
# and replace this placeholder with the output (commit it). See ./README.md.
|
# and replace this placeholder with the output (commit it). See ../../docs/hosts/rpi5.md.
|
||||||
#
|
#
|
||||||
# Like every hardware-configuration.nix in this repo, this file is excluded from
|
# Like every hardware-configuration.nix in this repo, this file is excluded from
|
||||||
# the formatter and linters (see the pre-commit/treefmt excludes in flake.nix).
|
# the formatter and linters (see the pre-commit/treefmt excludes in flake.nix).
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# ThinkPad T400 (NixOS). Shared laptop options live in ../../modules/laptop.nix;
|
# ThinkPad T400 (NixOS). Shared laptop options live in ../../modules/laptop.nix;
|
||||||
# only host-specific settings are here. Install notes (boot variants, GPU,
|
# only host-specific settings are here. Install notes (boot variants, GPU,
|
||||||
# partitions): see ./README.md.
|
# partitions): see ../../docs/hosts/t400.md.
|
||||||
{ config, ... }:
|
{ config, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,6 +25,22 @@
|
|||||||
# toolchains, language-server downloads) on every NixOS host, not just WSL.
|
# toolchains, language-server downloads) on every NixOS host, not just WSL.
|
||||||
programs.nix-ld.enable = true;
|
programs.nix-ld.enable = true;
|
||||||
|
|
||||||
|
# Memory-safe sudo. The two modules assert against being enabled together;
|
||||||
|
# this one sets `security.sudo.enable = false` via mkDefault, so it is a
|
||||||
|
# straight swap and not an addition.
|
||||||
|
#
|
||||||
|
# Safe here because this fleet only ever uses the stock policy -- wheel may
|
||||||
|
# run anything, with a password -- which sudo-rs implements completely. It
|
||||||
|
# does not cover the more exotic sudoers surface (host aliases, LDAP/SSSD
|
||||||
|
# sudoers, most `Defaults` settings, `sudoreplay`); adding any of those means
|
||||||
|
# going back to `security.sudo`.
|
||||||
|
#
|
||||||
|
# Recovery if a host ever refuses to escalate: get a root shell without sudo
|
||||||
|
# (`wsl -u root -d NixOS` on the WSL box, the console or a serial/HDMI login
|
||||||
|
# elsewhere) and roll back -- `nixos-rebuild switch --rollback`, or pick the
|
||||||
|
# previous generation from the boot menu.
|
||||||
|
security.sudo-rs.enable = true;
|
||||||
|
|
||||||
# Minimal system-level CLI available before the home-manager profile loads
|
# Minimal system-level CLI available before the home-manager profile loads
|
||||||
# (e.g. early boot / rescue). User-level tooling lives in home-manager.
|
# (e.g. early boot / rescue). User-level tooling lives in home-manager.
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
|
|||||||
@@ -50,6 +50,19 @@
|
|||||||
];
|
];
|
||||||
services.ssh-agent.enable = true;
|
services.ssh-agent.enable = true;
|
||||||
|
|
||||||
|
# Colourised kubectl. enableAlias points `kubectl` at kubecolor, which parses
|
||||||
|
# the output of the real kubectl underneath and passes anything it does not
|
||||||
|
# recognise straight through, so every flag and subcommand still works. It
|
||||||
|
# drops colour automatically when stdout is not a terminal, leaving pipes into
|
||||||
|
# grep/jq/yq byte-identical. zsh integration reuses kubectl's own completions.
|
||||||
|
# Note the alias does apply to `KUBECONFIG=... kubectl ...`: zsh expands
|
||||||
|
# aliases after a variable-assignment prefix.
|
||||||
|
programs.kubecolor = {
|
||||||
|
enable = true;
|
||||||
|
enableAlias = true;
|
||||||
|
enableZshIntegration = true;
|
||||||
|
};
|
||||||
|
|
||||||
# gcx (above) keeps its OAuth tokens in the system keychain and has no
|
# gcx (above) keeps its OAuth tokens in the system keychain and has no
|
||||||
# plaintext fallback, so this WSL box needs something owning
|
# plaintext fallback, so this WSL box needs something owning
|
||||||
# org.freedesktop.secrets. See home/secret-service.nix for why
|
# org.freedesktop.secrets. See home/secret-service.nix for why
|
||||||
|
|||||||
Reference in New Issue
Block a user