Compare commits
51
Commits
6868182ef5
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
955b6640c3 | ||
|
|
ea791df4aa | ||
|
|
a587f0ab71 | ||
|
|
93f1b191c8 | ||
|
|
a51ed76119 | ||
|
|
5bca7e176a | ||
|
|
684d3f5a75 | ||
|
|
99fca0f746 | ||
|
|
9b1e2fb447 | ||
|
|
9d2379bb3e | ||
|
|
7a650dc7cc | ||
|
|
bb613ac803 | ||
|
|
47362090c3 | ||
|
|
8c5773447e | ||
|
|
ae44982c65 | ||
|
|
c82c1bef9c | ||
|
|
39b2b1d24b | ||
|
|
0022a152e3 | ||
|
|
d9db12c4a5 | ||
|
|
4ac9d1108b | ||
|
|
0c151943de | ||
|
|
dcb8a5e66a | ||
|
|
bdb21a6d50 | ||
|
|
1da34d6232 | ||
|
|
19e0b7f13f | ||
|
|
1ff333a896 | ||
|
|
9d199bc087 | ||
|
|
c7adcccbb3 | ||
|
|
dcc13f94e0 | ||
|
|
d30d8f9892 | ||
|
|
dfafac8de9 | ||
|
|
d9464009f0 | ||
|
|
d654eac1e2 | ||
|
|
d4e7475db9 | ||
|
|
0f7fb7f78a | ||
|
|
0d13581896 | ||
|
|
526e6a08e2 | ||
|
|
9e749cce2b | ||
|
|
1766fb7b3f | ||
|
|
dba73e1199 | ||
|
|
e9835372cd | ||
|
|
bd613ef07f | ||
|
|
c5b41ba6fd | ||
|
|
7041dfebfa | ||
|
|
4d6ad47837 | ||
|
|
f471d226e0 | ||
|
|
10f713103c | ||
|
|
cc6cb24c78 | ||
|
|
240facdbbb | ||
|
|
c1456decaf | ||
|
|
e06495ae69 |
@@ -59,7 +59,7 @@ jobs:
|
|||||||
|
|
||||||
# Nix drives the formatting check, so install it unconditionally.
|
# Nix drives the formatting check, so install it unconditionally.
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31
|
uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
experimental-features = nix-command flakes
|
experimental-features = nix-command flakes
|
||||||
|
|||||||
@@ -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,16 +5,16 @@ 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-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),
|
||||||
@@ -30,7 +30,8 @@ profiles. The full module catalogue is below.
|
|||||||
flake.nix # inputs, mkHost/mkDarwinHost, the host tables, dev shell + checks
|
flake.nix # inputs, mkHost/mkDarwinHost, the host tables, dev shell + checks
|
||||||
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, 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,16 +51,16 @@ 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 feature-flag options (currently `features.swayDesktop.enable`) so any host can read/set them without importing the heavy implementation module. |
|
| `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. |
|
||||||
@@ -74,16 +75,39 @@ Form-factor decision: a **laptop** imports `laptop.nix` (default
|
|||||||
serves. `portable` is threaded through to `home/sway.nix`, which drops the
|
serves. `portable` is threaded through to `home/sway.nix`, which drops the
|
||||||
battery block and brightness keys on desktops.
|
battery block and brightness keys on desktops.
|
||||||
|
|
||||||
|
## CPU capability gating
|
||||||
|
|
||||||
|
Not every host can run everything the fleet installs. Nix cannot probe the CPU
|
||||||
|
(evaluation is pure, and a host may be built elsewhere), so each machine
|
||||||
|
declares what it is and the shared modules derive from that:
|
||||||
|
|
||||||
|
- `features.cpu.microarchLevel` — the x86-64 psABI level the CPU implements
|
||||||
|
(1 = baseline, 2 = SSE4.2/POPCNT, 3 = AVX2, 4 = AVX-512). Defaults to **2**;
|
||||||
|
only a host older than that sets it (the Mac Pro 3,1's 2008 Harpertown Xeons
|
||||||
|
are level 1). Ignored on non-x86_64 hosts.
|
||||||
|
- `features.claudeCode.enable` — derived: on unless the host is below
|
||||||
|
x86-64-v2, because Claude Code's Node runtime needs SSE4.2/POPCNT.
|
||||||
|
[`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
|
||||||
|
symlink) when it is off. Hosts with no such option — the Darwin host and the
|
||||||
|
standalone `homeConfigurations` — fall back to enabled.
|
||||||
|
- An assertion in `features.nix` fails evaluation if a host force-enables a
|
||||||
|
flag its declared CPU level cannot support, so the mistake surfaces in
|
||||||
|
`nix flake check`/CI rather than as an illegal-instruction crash on the box.
|
||||||
|
|
||||||
|
Adding another CPU-sensitive tool means deriving one more flag there, not
|
||||||
|
editing every host.
|
||||||
|
|
||||||
## Users
|
## Users
|
||||||
|
|
||||||
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
|
||||||
@@ -91,12 +115,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, and the corporate ssh
|
toolchain (kubectl/helm/az/etc.), work-only LSP servers, the corporate ssh
|
||||||
handling; imports
|
handling, and the headless Secret Service that gcx needs for its keychain
|
||||||
[`users/emmathorpe/renovate-review.nix`](./users/emmathorpe/renovate-review.nix),
|
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`](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)
|
||||||
@@ -155,17 +180,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
|
||||||
@@ -185,6 +213,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:
|
||||||
@@ -200,7 +260,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,10 +34,44 @@ 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.
|
||||||
|
|
||||||
|
## Secret Service (keychain)
|
||||||
|
|
||||||
|
`work.nix` sets `services.headlessSecretService.enable = true`, which runs
|
||||||
|
`gnome-keyring` as a `systemd --user` service owning `org.freedesktop.secrets`
|
||||||
|
on the session bus, with the login keyring unlocked at start.
|
||||||
|
|
||||||
|
This exists for **gcx**, the Grafana Cloud CLI. gcx stores its OAuth access and
|
||||||
|
refresh tokens in the keychain unconditionally (its config keeps only opaque
|
||||||
|
`keychain:gcx:v2:...` handles) and has no plaintext fallback, so without a
|
||||||
|
Secret Service `gcx login` authenticates and then fails to persist with "The
|
||||||
|
name is not activatable".
|
||||||
|
|
||||||
|
Home-manager's own `services.gnome-keyring` does not work here: it is
|
||||||
|
`WantedBy=graphical-session-pre.target`, which never activates on this headless
|
||||||
|
box, and it cannot unlock the keyring. See
|
||||||
|
[`../../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.
|
||||||
|
|
||||||
|
Only the `secrets` component is started. The `ssh` component is deliberately off
|
||||||
|
— it would claim `SSH_AUTH_SOCK` and displace `services.ssh-agent`, breaking SSH
|
||||||
|
auth and signed commits.
|
||||||
|
|
||||||
|
Checking it:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
systemctl --user status headless-secret-service
|
||||||
|
busctl --user list | grep secrets # expect org.freedesktop.secrets
|
||||||
|
secret-tool search --all service gcx # inspect what gcx stored
|
||||||
|
gcx config check # end-to-end
|
||||||
|
```
|
||||||
|
|
||||||
|
If the keyring password is ever lost or changed, the login keyring cannot be
|
||||||
|
unlocked: delete `~/.local/share/keyrings` and re-run `gcx login`.
|
||||||
|
|
||||||
## stateVersion
|
## stateVersion
|
||||||
|
|
||||||
`system.stateVersion = "24.11"` — the release this box was first installed on.
|
`system.stateVersion = "24.11"` — the release this box was first installed on.
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
# Mac Pro 3,1 (Early 2008) — install notes
|
||||||
|
|
||||||
|
Flake host: `lyrathorpe-macpro31`. Desktop (`portable = false`, imports
|
||||||
|
`../../modules/desktop.nix`). Files: `configuration.nix`, `nvidia.nix`,
|
||||||
|
`hardware-configuration.nix`.
|
||||||
|
|
||||||
|
## Hardware configuration
|
||||||
|
|
||||||
|
`hardware-configuration.nix` here is the real config generated by
|
||||||
|
`nixos-generate-config` on the machine. Root is an **LVM** logical volume
|
||||||
|
(`/dev/mapper/MacPro-Root`, ext4); the ESP (vfat) and swap are referenced by
|
||||||
|
UUID. The initrd carries `dm-snapshot` for the LVM root. Regenerate and commit
|
||||||
|
if the disk layout changes.
|
||||||
|
|
||||||
|
## Bootloader
|
||||||
|
|
||||||
|
The Mac Pro 3,1 has **64-bit EFI**, so it uses **systemd-boot** (no GRUB/CSM
|
||||||
|
shim). `canTouchEfiVariables = false` because Apple's firmware does not reliably
|
||||||
|
accept `efibootmgr` NVRAM writes.
|
||||||
|
|
||||||
|
Apple-EFI quirk: if the firmware boot picker does not show NixOS after install,
|
||||||
|
either
|
||||||
|
|
||||||
|
- uncomment `boot.loader.efi.efiInstallAsRemovable = true;` in
|
||||||
|
`configuration.nix` (installs the fallback `\EFI\BOOT\BOOTX64.EFI`), and/or
|
||||||
|
- "bless" the ESP from macOS.
|
||||||
|
|
||||||
|
Partition the disk GPT with an ESP (vfat).
|
||||||
|
|
||||||
|
## Graphics — NVIDIA Quadro P400
|
||||||
|
|
||||||
|
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,
|
||||||
|
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
|
||||||
|
(`production`, currently 595.x). 580 is the last branch that supports
|
||||||
|
Maxwell/Pascal/Volta and is maintained as an LTS branch until Aug 2028; a
|
||||||
|
newer branch does not drive this card at all.
|
||||||
|
- `modesetting.enable = true` — mandatory for Wayland (sets
|
||||||
|
`nvidia-drm.modeset=1`); without it wlroots gets no GBM device and both Sway
|
||||||
|
and the greeter fail to start.
|
||||||
|
- `open = false` — the open kernel modules require Turing or later.
|
||||||
|
- Sway runs with `--unsupported-gpu` (`programs.sway.extraOptions`); wlroots
|
||||||
|
refuses the proprietary driver otherwise. `cage`/ReGreet needs no such flag.
|
||||||
|
- nouveau and `nvidiafb` are blacklisted automatically by the NVIDIA module.
|
||||||
|
|
||||||
|
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
|
||||||
|
first rebuild and again after every kernel bump. The package names are
|
||||||
|
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
|
||||||
|
ROM): the machine boots blind until KMS brings the display up. That is expected,
|
||||||
|
not a fault.
|
||||||
|
|
||||||
|
Verify after a rebuild:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
nvidia-smi
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docker with CUDA
|
||||||
|
|
||||||
|
`nvidia.nix` also enables Docker and gives containers GPU access via **CDI**
|
||||||
|
(`hardware.nvidia-container-toolkit.enable`), which generates device specs from
|
||||||
|
the host driver at boot (regenerated by a udev rule when the `nvidia` device
|
||||||
|
appears) and turns on the daemon's CDI feature:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker run --rm --device=nvidia.com/gpu=all nvidia/cuda:12.9.1-base-ubuntu24.04 nvidia-smi
|
||||||
|
```
|
||||||
|
|
||||||
|
- Use the `--device=nvidia.com/gpu=all` form. `--gpus all` is the legacy
|
||||||
|
runtime-wrapper path (`virtualisation.docker.enableNvidia`), which is
|
||||||
|
deprecated upstream and deliberately not enabled here.
|
||||||
|
- **CUDA version matters.** The P400 is compute capability 6.1 (`sm_61`); CUDA
|
||||||
|
13 dropped Maxwell/Pascal/Volta, so container images must ship a **CUDA 12.x
|
||||||
|
or older** runtime. The 580 driver itself is happy with either.
|
||||||
|
- 2 GB of VRAM, 256 CUDA cores — fine for encode/decode and small models, not
|
||||||
|
for training anything serious.
|
||||||
|
- Docker socket is local-only (no TCP listener, unlike the Pi). Users need the
|
||||||
|
`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
|
||||||
|
|
||||||
|
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`
|
||||||
|
declares `features.cpu.microarchLevel = 1`, which switches the tool off through
|
||||||
|
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
|
||||||
|
evaluation error, not a broken install.
|
||||||
|
|
||||||
|
## Networking
|
||||||
|
|
||||||
|
Wired Ethernet via NetworkManager (from `desktop.nix`) — the Mac Pro has two
|
||||||
|
gigabit ports.
|
||||||
|
|
||||||
|
## Login
|
||||||
|
|
||||||
|
Graphical login via a Wayland greeter — `greetd` running ReGreet inside the
|
||||||
|
`cage` kiosk compositor — configured centrally in `../../modules/sway.nix` for
|
||||||
|
every Sway host (gated on `features.swayDesktop.enable`). The greeter is forced
|
||||||
|
to the Dvorak layout to match the console and Sway session. Set the user
|
||||||
|
password (`passwd lyrathorpe`) after install, or the greeter cannot
|
||||||
|
authenticate. Requires working KMS (NVIDIA modesetting — see Graphics).
|
||||||
|
|
||||||
|
## Apply
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo nixos-rebuild switch --flake .#lyrathorpe-macpro31
|
||||||
|
```
|
||||||
@@ -5,11 +5,11 @@ Everything here is managed declaratively through Nix — edit the listed file an
|
|||||||
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**
|
||||||
+368
@@ -0,0 +1,368 @@
|
|||||||
|
# Interactive shell environment
|
||||||
|
|
||||||
|
Everything the shell, terminal multiplexer, git and ssh do beyond their defaults,
|
||||||
|
and where each is defined. All of it is managed declaratively through
|
||||||
|
home-manager — edit the listed file and rebuild, never the generated dotfiles.
|
||||||
|
|
||||||
|
Keyboard shortcuts have their own reference: [`keybindings.md`](./keybindings.md).
|
||||||
|
|
||||||
|
| Area | Defined in |
|
||||||
|
| -------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| 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`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/home/git.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`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/home/claude.nix) |
|
||||||
|
| 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`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/home/default.nix); the work box also layers
|
||||||
|
[`work.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/users/emmathorpe/work.nix) on top (its own ssh config, extra
|
||||||
|
packages, kubecolor, and the C#/Helm language servers). The committer identity (name, email,
|
||||||
|
signing key) comes from the user registry
|
||||||
|
([`../users/registry.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/users/registry.nix)), not this module.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## zsh
|
||||||
|
|
||||||
|
| Feature | Notes |
|
||||||
|
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| oh-my-zsh | plugins `git`, `man`, `sudo` (Esc-Esc to prepend sudo), `colored-man-pages`, `extract`; theme `robbyrussell` |
|
||||||
|
| Autosuggestion | fish-style history suggestions as you type (→ to accept) |
|
||||||
|
| Syntax highlighting | commands coloured by validity as you type |
|
||||||
|
| Completion | menu completion; the dump is rebuilt on every activation (see Maintenance) |
|
||||||
|
| History | 100k in-memory/on-disk, deduped, space-prefixed commands ignored, timestamped, **shared live across sessions**; file stays at `~/.zsh_history` |
|
||||||
|
| Dotfiles location | `dotDir` is `~/.config/zsh` (XDG) — `.zshrc`/`.zshenv`/`.zcompdump` live there; `~/.zshenv` only bootstraps `$ZDOTDIR` |
|
||||||
|
| 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 |
|
||||||
|
|
||||||
|
**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
|
||||||
|
|
||||||
|
| Tool | What it gives you |
|
||||||
|
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| `fzf` | `Ctrl-R` fuzzy history, `Ctrl-T` file picker, `Alt-C` fuzzy cd (Catppuccin-themed) |
|
||||||
|
| `zoxide` | `z <fragment>` jumps to frecent directories |
|
||||||
|
| `direnv` + `nix-direnv` | per-project environments auto-loaded on `cd` (cached Nix dev shells) |
|
||||||
|
| `eza` | modern `ls` (drives the ls aliases) |
|
||||||
|
| `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` |
|
||||||
|
| `jq` | JSON processor |
|
||||||
|
| `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) |
|
||||||
|
| `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` |
|
||||||
|
| `btop` | resource monitor, themed Catppuccin Mocha (vendored theme) |
|
||||||
|
| `lazygit` | git TUI for staging/rebasing, themed to match (`git.nix`) |
|
||||||
|
| `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
|
||||||
|
Catppuccin Mocha, driven from the shared `../lib/catppuccin-mocha.nix` palette / the
|
||||||
|
catppuccin upstream themes.
|
||||||
|
|
||||||
|
**Env & defaults:** `xdg.enable` on; `PAGER`/`MANPAGER` (bat) set in `default.nix`
|
||||||
|
(the editor owns `$EDITOR`/`$VISUAL`); `xdg.mimeApps` maps web→Firefox,
|
||||||
|
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`.
|
||||||
|
|
||||||
|
One exception to the password: the EDaaS box sets
|
||||||
|
`security.sudo-rs.wheelNeedsPassword = false`. NixOS-WSL ships that default for
|
||||||
|
`security.sudo` — WSL has no console login, so the trust boundary is the Windows
|
||||||
|
session and the Linux account password is never one the user chose — and the
|
||||||
|
option does not carry across to the `security.sudo-rs` module, which defaults to
|
||||||
|
requiring one. Without the explicit setting, `sudo` on that host prompts for a
|
||||||
|
password nobody knows.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
**Auto-start:** opening any interactive terminal — foot, iTerm2, the WSL shell, the
|
||||||
|
Linux console — drops you straight into a tmux session named `main` (attach if it
|
||||||
|
exists, else create). Panes run a plain non-login zsh. It deliberately does **not**
|
||||||
|
fire for SSH sessions, VS Code's integrated terminal, already-inside-tmux, or
|
||||||
|
non-interactive shells. Escape hatch: `NO_TMUX=1 <terminal>` opens a bare shell.
|
||||||
|
|
||||||
|
| Setting | Value |
|
||||||
|
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
|
| Mode keys | vi |
|
||||||
|
| Mouse | on |
|
||||||
|
| Scrollback | 500000 lines |
|
||||||
|
| `escape-time` | 10ms (the 500ms default lagged vim's ESC) |
|
||||||
|
| `focus-events` | on (vim autoread) |
|
||||||
|
| `base-index` / `pane-base-index` | 1 |
|
||||||
|
| Splits | `prefix s` vertical, `prefix v` horizontal (stock `%`/`"` unbound) |
|
||||||
|
| Pane nav | `Alt`+arrows (no prefix) |
|
||||||
|
| Terminal | `default-terminal tmux-256color`; truecolor advertised per outer terminal (`foot*`, `xterm-256color`/iTerm2) via `terminal-features … RGB` |
|
||||||
|
| Clipboard | `set-clipboard on`; foot `terminal-features` advertise truecolor/sync/OSC52/title/cursor |
|
||||||
|
|
||||||
|
**Plugins:** `sensible`, `vim-tmux-navigator` (Ctrl-h/j/k/l across vim ↔ tmux),
|
||||||
|
`yank`, `extrakto` (`prefix`+`Tab`: fzf-grab paths/URLs/text from the pane into
|
||||||
|
the prompt), `catppuccin` (Mocha statusline), `resurrect` + `continuum`
|
||||||
|
(sessions auto-save and restore across reboots). The statusline draws Nerd-Font
|
||||||
|
glyphs — see Fonts.
|
||||||
|
|
||||||
|
## Fonts
|
||||||
|
|
||||||
|
**JetBrainsMono Nerd Font**, **Noto Sans** and **Noto Color Emoji** are
|
||||||
|
installed on every host (in `common-nixos.nix`, because tmux/terminals run
|
||||||
|
everywhere; the Mac installs the Nerd Font to `/Library/Fonts` via the Darwin
|
||||||
|
config). `fonts.fontconfig.defaultFonts` maps the generic families so anything
|
||||||
|
asking for `monospace` gets the Nerd Font (with emoji fallback) — this also
|
||||||
|
gives the WSL box emoji/sans coverage it otherwise lacked. foot uses the Nerd
|
||||||
|
Font as its main font automatically. iTerm2's font is a GUI setting — set it to
|
||||||
|
_JetBrainsMono Nerd Font_ (Settings → Profiles → Text → Font) so the tmux
|
||||||
|
statusline glyphs render instead of `?`.
|
||||||
|
|
||||||
|
## Editor (Neovim)
|
||||||
|
|
||||||
|
`nvim` — aliased to `vi`/`vim`, and set as `$EDITOR`/`$VISUAL` — is configured
|
||||||
|
declaratively with **nixvim**, so the same plugins and config are baked in on
|
||||||
|
every host. Migrated from plain vim; the practical gain is a real LSP stack in
|
||||||
|
place of the old (inert) ALE.
|
||||||
|
|
||||||
|
| Feature | Notes |
|
||||||
|
| -------------- | ----------------------------------------------------------------------------------------- |
|
||||||
|
| Colorscheme | Catppuccin Mocha (matches the terminal and the rest of the desktop) |
|
||||||
|
| File tree | nvim-tree, toggled with `,,` (comma twice; was nerdtree) |
|
||||||
|
| Fuzzy finder | telescope (+fzf-native): `<leader>ff` files, `<leader>fg` grep, `<leader>fb` buffers |
|
||||||
|
| Format on save | conform-nvim (nixfmt, stylua, ruff, shfmt, prettier, gofumpt; LSP fallback otherwise) |
|
||||||
|
| Git | fugitive (`:Git …`) + gitsigns gutter signs/blame |
|
||||||
|
| Diagnostics | inline + trouble list (`<leader>xx`) |
|
||||||
|
| Completion | nvim-cmp (LSP/buffer/path) with luasnip snippet expansion |
|
||||||
|
| Indent guides | indent-blankline, on by default (was vim-indent-guides) |
|
||||||
|
| Statusline | lualine (Catppuccin theme) |
|
||||||
|
| Editing | which-key hints, comment (`gc`/`gcc`), autopairs, treesitter textobjects |
|
||||||
|
| Pane nav | vim-tmux-navigator — `Ctrl`+`h/j/k/l` moves across vim splits and tmux panes |
|
||||||
|
| Syntax | tree-sitter (nix, lua, bash, markdown, groovy, c#, python, terraform, yaml) |
|
||||||
|
| LSP | nvim-cmp completion + servers `nil_ls` (Nix), `lua_ls`, `pyright` (Python), `terraformls` |
|
||||||
|
| Indentation | 2-wide hard tabs (`noexpandtab`, `tabstop`/`shiftwidth` = 2); line numbers on |
|
||||||
|
| Filetypes | `*Jenkinsfile` → groovy |
|
||||||
|
|
||||||
|
Leader is `Space`. LSP keymaps (`gd`, `gr`, `K`, `<leader>rn`, `<leader>ca`) and
|
||||||
|
the file-tree toggle are listed in
|
||||||
|
[`keybindings.md`](./keybindings.md#neovim). Add a universal language server by
|
||||||
|
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
|
||||||
|
`omnisharp` (C#) and `helm_ls` (Helm), kept off the personal machines.
|
||||||
|
|
||||||
|
## git
|
||||||
|
|
||||||
|
Pager is **delta**. **commitizen** is installed on every host; `cz` defaults to
|
||||||
|
Conventional Commits. **lazygit** (themed) is the TUI. The commit-graph is kept
|
||||||
|
current (`gc`/`fetch.writeCommitGraph`) so `lg` stays fast.
|
||||||
|
|
||||||
|
| Aliases | |
|
||||||
|
| ------------------------ | ------------------------------------------------------------------------- |
|
||||||
|
| `st` `co` `sw` `br` `ci` | status / checkout / switch / branch / commit |
|
||||||
|
| `last` `unstage` | last commit / unstage |
|
||||||
|
| `amend` `fixup` `undo` | amend-no-edit / `commit --fixup` / soft-reset HEAD~1 (keep staged) |
|
||||||
|
| `lg` | graph log, all branches |
|
||||||
|
| `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 | |
|
||||||
|
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| Pulls | rebase, with autostash + autosquash |
|
||||||
|
| Fetch | prune deleted remote branches |
|
||||||
|
| Conflicts | `zdiff3` (shows the common ancestor) |
|
||||||
|
| Diffs | histogram algorithm, colour-moved |
|
||||||
|
| `rerere` | remembers + replays conflict resolutions |
|
||||||
|
| Commit editor | full diff shown (`commit.verbose`) |
|
||||||
|
| Misc | branches sorted by date, `column.ui = auto`, `help.autocorrect = prompt`, `push.autoSetupRemote` |
|
||||||
|
| Global ignores | `result`, `result-*`, `.direnv`, `*.swp`, `.DS_Store` |
|
||||||
|
| Signing | SSH commit + tag signing (`mkDefault`, so a host without the key in its agent can disable it). Name, email and signing key all come from the per-user `identity` (the user registry, `../users/registry.nix`). |
|
||||||
|
|
||||||
|
## ssh
|
||||||
|
|
||||||
|
| Feature | Notes |
|
||||||
|
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| ssh-agent | runs on Linux (launchd on macOS); keys added on **first use** so the passphrase is typed once per login session — this also feeds git commit signing |
|
||||||
|
| macOS | `UseKeychain` caches the passphrase in the login keychain (guarded by `IgnoreUnknown`, so a non-Apple `ssh` skips it instead of erroring) |
|
||||||
|
| Gitea remote | `code.emmathe.dev` → `HostName 10.187.1.76` (DNS-override), `Port 30009`, user `git`, dedicated key, `identitiesOnly` |
|
||||||
|
| Defaults | the module's deprecated default block is opted out; equivalents kept under `settings."*"` |
|
||||||
|
|
||||||
|
The **work box keeps its own `~/.ssh/config`** (home-manager's `programs.ssh` is
|
||||||
|
forced off there) but still runs the agent.
|
||||||
|
|
||||||
|
## Claude Code
|
||||||
|
|
||||||
|
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
|
||||||
|
overlay).
|
||||||
|
|
||||||
|
**Capability gate.** The module installs nothing — CLI or files — when
|
||||||
|
`osConfig.features.claudeCode.enable` is off. That flag is derived fleet-wide
|
||||||
|
from the host's declared CPU level (see "CPU capability gating" in the root
|
||||||
|
README): the Node runtime needs SSE4.2/POPCNT, so anything below x86-64-v2 (the
|
||||||
|
Mac Pro 3,1) is excluded. Hosts that do not define the option — the Darwin host
|
||||||
|
and the standalone `homeConfigurations` — keep it enabled.
|
||||||
|
|
||||||
|
| Managed (static, from Nix) | Left mutable (runtime state) |
|
||||||
|
| --------------------------------------------------- | ------------------------------------------------------ |
|
||||||
|
| `~/.claude/CLAUDE.md` (persona + memory workflow) | `settings.json` (permissions, model, theme, `/config`) |
|
||||||
|
| `~/.claude/output-styles/soviet-engineer.md` | `.credentials.json`, history, caches |
|
||||||
|
| `~/.claude/memory/` (read-only symlink to the repo) | |
|
||||||
|
|
||||||
|
`settings.json` is intentionally **not** managed: Claude rewrites it at runtime
|
||||||
|
(interactive permission grants, `/config`), which a read-only store symlink would
|
||||||
|
break.
|
||||||
|
|
||||||
|
**Memory is sourced from this repo.** The files in
|
||||||
|
[`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
|
||||||
|
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
|
||||||
|
Claude to route new memories there.
|
||||||
|
|
||||||
|
## Maintenance behaviours
|
||||||
|
|
||||||
|
- **zcompdump reset** — `~/.config/zsh/.zcompdump*` (plus legacy `~/.zcompdump*`
|
||||||
|
and the cache copy) is removed on every activation, so a stale
|
||||||
|
dump (pointing at `/nix/store` paths a rebuild or a manual GC removed) can't
|
||||||
|
break completion with `_git: function definition file not found`.
|
||||||
|
- **GC** — no scheduled timer; collect garbage deliberately (`nh clean all` /
|
||||||
|
`nix-collect-garbage -d`) when no important session is running.
|
||||||
|
|
||||||
|
## Per-host differences
|
||||||
|
|
||||||
|
| | Personal Linux (sway) | macOS | Work WSL (EDaaS) |
|
||||||
|
| --------------------------- | --------------------- | --------------------- | ---------------------------- |
|
||||||
|
| Auto-tmux | yes (foot/TTY) | yes (iTerm2) | yes (WSL shell) |
|
||||||
|
| `kubectl` → kubecolor | no (no kubectl) | no | yes (work module) |
|
||||||
|
| `sudo` implementation | sudo-rs (password) | Apple sudo + Touch ID | sudo-rs (passwordless wheel) |
|
||||||
|
| git email | `iam@emmathe.dev` | `iam@emmathe.dev` | `…@citrix.com` (work) |
|
||||||
|
| ssh config managed | yes | yes | no (keeps corporate config) |
|
||||||
|
| ssh-agent | yes | launchd | yes (work module) |
|
||||||
|
| GUI / theming (desktop.nix) | yes | no | no |
|
||||||
Generated
+40
-40
@@ -3,16 +3,16 @@
|
|||||||
"brew-src": {
|
"brew-src": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1785146564,
|
"lastModified": 1786945682,
|
||||||
"narHash": "sha256-Sa7/HrfB04H32OJ7/ofxXjiZEbkWtCNOriONYYTL1OA=",
|
"narHash": "sha256-VBESSoJccikdhxh3vp3SQeG7cZXTOulMvVkoSqNDEhs=",
|
||||||
"owner": "Homebrew",
|
"owner": "Homebrew",
|
||||||
"repo": "brew",
|
"repo": "brew",
|
||||||
"rev": "b2cfc03346d482f79886de108fee5dc49a6efc10",
|
"rev": "5b90e281d4e0c8fbd6ca4d8358276fb305b8d0bd",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "Homebrew",
|
"owner": "Homebrew",
|
||||||
"ref": "6.0.13",
|
"ref": "6.0.18",
|
||||||
"repo": "brew",
|
"repo": "brew",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
@@ -25,11 +25,11 @@
|
|||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"dir": "pkgs/firefox-addons",
|
"dir": "pkgs/firefox-addons",
|
||||||
"lastModified": 1785643380,
|
"lastModified": 1787457768,
|
||||||
"narHash": "sha256-6LdHFP+av+MSeCWLEl0p7qqD8fBH9pVAfMYHSqbjfA4=",
|
"narHash": "sha256-cbgeu5NTb6DtB+tNs4E6z6K/1XKKM90gVmlkWMJe+gY=",
|
||||||
"owner": "rycee",
|
"owner": "rycee",
|
||||||
"repo": "nur-expressions",
|
"repo": "nur-expressions",
|
||||||
"rev": "49e519c7b98b21d278be7d72bce2e8ff3b4f3065",
|
"rev": "25cfc8fdc413d73b3a47e3e86dafcad51cf5c9f9",
|
||||||
"type": "gitlab"
|
"type": "gitlab"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -114,11 +114,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1778716662,
|
"lastModified": 1785627969,
|
||||||
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
|
"narHash": "sha256-4dtXQk/NMePegK/nWp5NSeuZKLATItOq61lpEvmXqGw=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb",
|
"rev": "427bf4bd9435fdf21321c8cc628c24efc14c0f7a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -135,11 +135,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1784288435,
|
"lastModified": 1787424939,
|
||||||
"narHash": "sha256-ReRHaLgr/uVqdD8afFSn+myXIfpHeOhP0yYe0TJqAA8=",
|
"narHash": "sha256-O2tBn84NNuHrnqNVxx/XqsXwfYvS1YwBh+7CBnbCYsk=",
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "git-hooks.nix",
|
"repo": "git-hooks.nix",
|
||||||
"rev": "43b3c1ab9d40fb1dbb008f451988a91e375825e9",
|
"rev": "809414f0cdadf82cf11b06c2b29ba9b3168b3297",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -155,11 +155,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1785119570,
|
"lastModified": 1787377438,
|
||||||
"narHash": "sha256-Rgs2xKnGLFWQscxUaXX07oyZeuMDOHEbqDOsgliLFGM=",
|
"narHash": "sha256-Sxu1NLTD/Ern6hFGLlZmtKCSct3YQXZI/lls8RE1XeM=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "d4fd24667c8cbef124bb70a20380cab75ec8474d",
|
"rev": "65258d5c65a250189fde2e35f490d15e064c4c62",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -211,11 +211,11 @@
|
|||||||
"brew-src": "brew-src"
|
"brew-src": "brew-src"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1785544760,
|
"lastModified": 1787330919,
|
||||||
"narHash": "sha256-qV6OoNuly4ntqpCg7esIeJjUboxSnQNlLPxz+y5h9/o=",
|
"narHash": "sha256-LslMncqN7uOOH5S88WZtO/EVt2HwD8ltUnfyANk+mC0=",
|
||||||
"owner": "zhaofengli",
|
"owner": "zhaofengli",
|
||||||
"repo": "nix-homebrew",
|
"repo": "nix-homebrew",
|
||||||
"rev": "937ce52c7d046310571f3a070713804ead496843",
|
"rev": "b00218e4aec0e5bf07d61a0bb13f842faa582d7b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -231,11 +231,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1785650611,
|
"lastModified": 1787457452,
|
||||||
"narHash": "sha256-q4kR7g+pCcz6NASvoVPYu+CWWUurX03wogtBqGmR4h0=",
|
"narHash": "sha256-FJh4esFS3zqNNuKwvN3t6wrJGewqp1AUF9DAEvoKPD8=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nix-index-database",
|
"repo": "nix-index-database",
|
||||||
"rev": "dbc756c9d7287de19b3e0e38c928c47510d42c3e",
|
"rev": "c51d5c2ba69c907a34e90c9b6b80cd2b93811745",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -252,11 +252,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1785426242,
|
"lastModified": 1786862401,
|
||||||
"narHash": "sha256-QHAP8KsJQmI+dpNS/wfWAtEBQ0Zby+B0Ty+qZIO/U2w=",
|
"narHash": "sha256-zRPYCn5RJWxr9uyUwNIQjPsTFcIFRwuRnI91dqvGA0k=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nixos-apple-silicon",
|
"repo": "nixos-apple-silicon",
|
||||||
"rev": "66d8dd2c27f99bd5420c99938b60695aac1785c4",
|
"rev": "53798a0eb0fa4c8cfaeca7bdc5b4ad22ed210c95",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -272,11 +272,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1785232496,
|
"lastModified": 1787144466,
|
||||||
"narHash": "sha256-65EQYIRRpTdpH8lUiB6Mvo5uBkG60aBIzAJuALfx+O0=",
|
"narHash": "sha256-HHfv2/HkNSKbbSyU9iD/g8lbP6r4tl33sSw1W4rXCk0=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixos-hardware",
|
"repo": "nixos-hardware",
|
||||||
"rev": "2e790b0a6be8ec2b76174ac0931b8ff11919ec98",
|
"rev": "0471accf8d0a8210b31d947497d179ecc99e0021",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -308,11 +308,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1785599192,
|
"lastModified": 1787414105,
|
||||||
"narHash": "sha256-dg4RTtDxnXY13UkJNdhmgTUTl0n/IJBlCigfO7nutZw=",
|
"narHash": "sha256-WncT27+3BOkgTaJZLnCsf3LcYf9RXMuR9ONSN4rzQ7s=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "6d65bfc1bcef2ef39a239d38e577e92a89fb0f07",
|
"rev": "a9e6d84f9c2f9012f5fe7d964a7851352300e61a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -324,11 +324,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-unstable": {
|
"nixpkgs-unstable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1785571196,
|
"lastModified": 1787360063,
|
||||||
"narHash": "sha256-KoTsyMQqnXQZq8deCEnu4QkyldkwH/bpMMhUcfMdGIw=",
|
"narHash": "sha256-dt4WdcvsA8/RCe+VZZwqU0X+XMM3wBbGCWA0/sFWzGo=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "148bab9c1c3c53136ecb44a6ea356a0ed5b39b06",
|
"rev": "2c423e03bbafcff28bfadc6781a4a8257f205cb5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -347,11 +347,11 @@
|
|||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1782919967,
|
"lastModified": 1787536726,
|
||||||
"narHash": "sha256-pRwjfB5HQJ3m8J8bOR43pPHtHI7VUJSqwLA3P06cOY0=",
|
"narHash": "sha256-aBh5Yk9tX8ZV4k10BJr2fvTq0/+iWGegaCMUOU7YKas=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nixvim",
|
"repo": "nixvim",
|
||||||
"rev": "667c8471f4a0fb24d702d1a61af8609f1a5f1ba6",
|
"rev": "e2c3f9f36326d07340626847543c557e2b95fb50",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -402,11 +402,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1785360170,
|
"lastModified": 1786901030,
|
||||||
"narHash": "sha256-XE1lKgQ3eIO3E7zWryqcRsax+mYXod/5RHBn4YaR9YE=",
|
"narHash": "sha256-WSFCsDSE5ffgD2MqzkM2CYjeFiKhRF/dJUN8uedb6YE=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "treefmt-nix",
|
"repo": "treefmt-nix",
|
||||||
"rev": "d1187f8bc71fb8aab02395869ec3f5c1920f75c0",
|
"rev": "27b3b12a8e6375f28ebe122f07d230ca5459bbfa",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -84,7 +84,9 @@
|
|||||||
flake-parts.lib.mkFlake { inherit inputs; } (
|
flake-parts.lib.mkFlake { inherit inputs; } (
|
||||||
{ lib, ... }:
|
{ lib, ... }:
|
||||||
let
|
let
|
||||||
# claude-code tracks nixpkgs-unstable regardless of the pinned nixpkgs.
|
# These track nixpkgs-unstable regardless of the pinned nixpkgs.
|
||||||
|
# gcx: 26.05 ships 0.2.14, which predates the stacks/contexts config
|
||||||
|
# model and the agento11y commands the tooling expects.
|
||||||
overlays = [
|
overlays = [
|
||||||
(_final: prev: {
|
(_final: prev: {
|
||||||
inherit
|
inherit
|
||||||
@@ -93,6 +95,7 @@
|
|||||||
config.allowUnfree = true;
|
config.allowUnfree = true;
|
||||||
})
|
})
|
||||||
claude-code
|
claude-code
|
||||||
|
gcx
|
||||||
;
|
;
|
||||||
})
|
})
|
||||||
# commitizen 4.13.9's regression test for the invalid-command error
|
# commitizen 4.13.9's regression test for the invalid-command error
|
||||||
@@ -108,8 +111,14 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
# Unfree packages permitted to be built (replaces blanket allowUnfree).
|
# Unfree packages permitted to be built (replaces blanket allowUnfree).
|
||||||
|
# The NVIDIA entries are for the Mac Pro's Quadro P400 (hosts/MacPro31/
|
||||||
|
# nvidia.nix); unfree packages are not in the binary cache, so the
|
||||||
|
# kernel module is compiled on the host.
|
||||||
unfreePackages = [
|
unfreePackages = [
|
||||||
"claude-code"
|
"claude-code"
|
||||||
|
"nvidia-x11"
|
||||||
|
"nvidia-kernel-modules"
|
||||||
|
"nvidia-settings"
|
||||||
];
|
];
|
||||||
|
|
||||||
# Per-user identity, keyed by username. See README "Users".
|
# Per-user identity, keyed by username. See README "Users".
|
||||||
@@ -432,6 +441,7 @@
|
|||||||
git = ./home/git.nix;
|
git = ./home/git.nix;
|
||||||
editor = ./home/editor.nix;
|
editor = ./home/editor.nix;
|
||||||
claude = ./home/claude.nix;
|
claude = ./home/claude.nix;
|
||||||
|
secret-service = ./home/secret-service.nix;
|
||||||
desktop = ./home/desktop.nix;
|
desktop = ./home/desktop.nix;
|
||||||
sway = ./home/sway.nix;
|
sway = ./home/sway.nix;
|
||||||
};
|
};
|
||||||
|
|||||||
-215
@@ -1,215 +0,0 @@
|
|||||||
# Interactive shell environment
|
|
||||||
|
|
||||||
Everything the shell, terminal multiplexer, git and ssh do beyond their defaults,
|
|
||||||
and where each is defined. All of it is managed declaratively through
|
|
||||||
home-manager — edit the listed file and rebuild, never the generated dotfiles.
|
|
||||||
|
|
||||||
Keyboard shortcuts have their own reference: [`KEYBINDINGS.md`](./KEYBINDINGS.md).
|
|
||||||
|
|
||||||
| Area | Defined in |
|
|
||||||
| -------------------------------------- | ----------------------------------------------------- |
|
|
||||||
| zsh, CLI tools, tmux, ssh, auto-tmux | [`shell.nix`](./shell.nix) |
|
|
||||||
| git (+ delta, commitizen) | [`git.nix`](./git.nix) |
|
|
||||||
| Neovim (nixvim) + LSP | [`editor.nix`](./editor.nix) |
|
|
||||||
| Claude Code (CLAUDE.md, style, memory) | [`claude.nix`](./claude.nix) |
|
|
||||||
| GUI apps, GTK/Firefox theming, cursor | [`desktop.nix`](./desktop.nix) (graphical hosts only) |
|
|
||||||
|
|
||||||
Shared by every host via [`default.nix`](./default.nix); the work box also layers
|
|
||||||
[`work.nix`](../users/emmathorpe/work.nix) on top (its own ssh config, extra
|
|
||||||
packages, and the C#/Helm language servers). The committer identity (name, email,
|
|
||||||
signing key) comes from the user registry
|
|
||||||
([`../users/registry.nix`](../users/registry.nix)), not this module.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## zsh
|
|
||||||
|
|
||||||
| Feature | Notes |
|
|
||||||
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| oh-my-zsh | plugins `git`, `man`, `sudo` (Esc-Esc to prepend sudo), `colored-man-pages`, `extract`; theme `robbyrussell` |
|
|
||||||
| Autosuggestion | fish-style history suggestions as you type (→ to accept) |
|
|
||||||
| Syntax highlighting | commands coloured by validity as you type |
|
|
||||||
| Completion | menu completion; the dump is rebuilt on every activation (see Maintenance) |
|
|
||||||
| History | 100k in-memory/on-disk, deduped, space-prefixed commands ignored, timestamped, **shared live across sessions**; file stays at `~/.zsh_history` |
|
|
||||||
| Dotfiles location | `dotDir` is `~/.config/zsh` (XDG) — `.zshrc`/`.zshenv`/`.zcompdump` live there; `~/.zshenv` only bootstraps `$ZDOTDIR` |
|
|
||||||
| 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 |
|
|
||||||
|
|
||||||
**Aliases:** `ls`/`ll`/`la`/`lt` → `eza` (icons + git), `cls` → `clear`. git aliases live in git.nix (below).
|
|
||||||
|
|
||||||
## CLI tools
|
|
||||||
|
|
||||||
| Tool | What it gives you |
|
|
||||||
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| `fzf` | `Ctrl-R` fuzzy history, `Ctrl-T` file picker, `Alt-C` fuzzy cd (Catppuccin-themed) |
|
|
||||||
| `zoxide` | `z <fragment>` jumps to frecent directories |
|
|
||||||
| `direnv` + `nix-direnv` | per-project environments auto-loaded on `cd` (cached Nix dev shells) |
|
|
||||||
| `eza` | modern `ls` (drives the ls aliases) |
|
|
||||||
| `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` |
|
|
||||||
| `jq` | JSON processor |
|
|
||||||
| `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) |
|
|
||||||
| `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` |
|
|
||||||
| `btop` | resource monitor, themed Catppuccin Mocha (vendored theme) |
|
|
||||||
| `lazygit` | git TUI for staging/rebasing, themed to match (`git.nix`) |
|
|
||||||
| `hyperfine` / `sd` | command-line benchmarking; saner find-and-replace than sed |
|
|
||||||
|
|
||||||
**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 upstream themes.
|
|
||||||
|
|
||||||
**Env & defaults:** `xdg.enable` on; `PAGER`/`MANPAGER` (bat) set in `default.nix`
|
|
||||||
(the editor owns `$EDITOR`/`$VISUAL`); `xdg.mimeApps` maps web→Firefox,
|
|
||||||
directories→nemo (`desktop.nix`).
|
|
||||||
|
|
||||||
## tmux
|
|
||||||
|
|
||||||
**Auto-start:** opening any interactive terminal — foot, iTerm2, the WSL shell, the
|
|
||||||
Linux console — drops you straight into a tmux session named `main` (attach if it
|
|
||||||
exists, else create). Panes run a plain non-login zsh. It deliberately does **not**
|
|
||||||
fire for SSH sessions, VS Code's integrated terminal, already-inside-tmux, or
|
|
||||||
non-interactive shells. Escape hatch: `NO_TMUX=1 <terminal>` opens a bare shell.
|
|
||||||
|
|
||||||
| Setting | Value |
|
|
||||||
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
||||||
| Mode keys | vi |
|
|
||||||
| Mouse | on |
|
|
||||||
| Scrollback | 500000 lines |
|
|
||||||
| `escape-time` | 10ms (the 500ms default lagged vim's ESC) |
|
|
||||||
| `focus-events` | on (vim autoread) |
|
|
||||||
| `base-index` / `pane-base-index` | 1 |
|
|
||||||
| Splits | `prefix s` vertical, `prefix v` horizontal (stock `%`/`"` unbound) |
|
|
||||||
| Pane nav | `Alt`+arrows (no prefix) |
|
|
||||||
| Terminal | `default-terminal tmux-256color`; truecolor advertised per outer terminal (`foot*`, `xterm-256color`/iTerm2) via `terminal-features … RGB` |
|
|
||||||
| Clipboard | `set-clipboard on`; foot `terminal-features` advertise truecolor/sync/OSC52/title/cursor |
|
|
||||||
|
|
||||||
**Plugins:** `sensible`, `vim-tmux-navigator` (Ctrl-h/j/k/l across vim ↔ tmux),
|
|
||||||
`yank`, `extrakto` (`prefix`+`Tab`: fzf-grab paths/URLs/text from the pane into
|
|
||||||
the prompt), `catppuccin` (Mocha statusline), `resurrect` + `continuum`
|
|
||||||
(sessions auto-save and restore across reboots). The statusline draws Nerd-Font
|
|
||||||
glyphs — see Fonts.
|
|
||||||
|
|
||||||
## Fonts
|
|
||||||
|
|
||||||
**JetBrainsMono Nerd Font**, **Noto Sans** and **Noto Color Emoji** are
|
|
||||||
installed on every host (in `common-nixos.nix`, because tmux/terminals run
|
|
||||||
everywhere; the Mac installs the Nerd Font to `/Library/Fonts` via the Darwin
|
|
||||||
config). `fonts.fontconfig.defaultFonts` maps the generic families so anything
|
|
||||||
asking for `monospace` gets the Nerd Font (with emoji fallback) — this also
|
|
||||||
gives the WSL box emoji/sans coverage it otherwise lacked. foot uses the Nerd
|
|
||||||
Font as its main font automatically. iTerm2's font is a GUI setting — set it to
|
|
||||||
_JetBrainsMono Nerd Font_ (Settings → Profiles → Text → Font) so the tmux
|
|
||||||
statusline glyphs render instead of `?`.
|
|
||||||
|
|
||||||
## Editor (Neovim)
|
|
||||||
|
|
||||||
`nvim` — aliased to `vi`/`vim`, and set as `$EDITOR`/`$VISUAL` — is configured
|
|
||||||
declaratively with **nixvim**, so the same plugins and config are baked in on
|
|
||||||
every host. Migrated from plain vim; the practical gain is a real LSP stack in
|
|
||||||
place of the old (inert) ALE.
|
|
||||||
|
|
||||||
| Feature | Notes |
|
|
||||||
| -------------- | ----------------------------------------------------------------------------------------- |
|
|
||||||
| Colorscheme | Catppuccin Mocha (matches the terminal and the rest of the desktop) |
|
|
||||||
| File tree | nvim-tree, toggled with `,,` (comma twice; was nerdtree) |
|
|
||||||
| Fuzzy finder | telescope (+fzf-native): `<leader>ff` files, `<leader>fg` grep, `<leader>fb` buffers |
|
|
||||||
| Format on save | conform-nvim (nixfmt, stylua, ruff, shfmt, prettier, gofumpt; LSP fallback otherwise) |
|
|
||||||
| Git | fugitive (`:Git …`) + gitsigns gutter signs/blame |
|
|
||||||
| Diagnostics | inline + trouble list (`<leader>xx`) |
|
|
||||||
| Completion | nvim-cmp (LSP/buffer/path) with luasnip snippet expansion |
|
|
||||||
| Indent guides | indent-blankline, on by default (was vim-indent-guides) |
|
|
||||||
| Statusline | lualine (Catppuccin theme) |
|
|
||||||
| Editing | which-key hints, comment (`gc`/`gcc`), autopairs, treesitter textobjects |
|
|
||||||
| Pane nav | vim-tmux-navigator — `Ctrl`+`h/j/k/l` moves across vim splits and tmux panes |
|
|
||||||
| Syntax | tree-sitter (nix, lua, bash, markdown, groovy, c#, python, terraform, yaml) |
|
|
||||||
| LSP | nvim-cmp completion + servers `nil_ls` (Nix), `lua_ls`, `pyright` (Python), `terraformls` |
|
|
||||||
| Indentation | 2-wide hard tabs (`noexpandtab`, `tabstop`/`shiftwidth` = 2); line numbers on |
|
|
||||||
| Filetypes | `*Jenkinsfile` → groovy |
|
|
||||||
|
|
||||||
Leader is `Space`. LSP keymaps (`gd`, `gr`, `K`, `<leader>rn`, `<leader>ca`) and
|
|
||||||
the file-tree toggle are listed in
|
|
||||||
[`KEYBINDINGS.md`](./KEYBINDINGS.md#neovim). Add a universal language server by
|
|
||||||
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
|
|
||||||
`omnisharp` (C#) and `helm_ls` (Helm), kept off the personal machines.
|
|
||||||
|
|
||||||
## git
|
|
||||||
|
|
||||||
Pager is **delta**. **commitizen** is installed on every host; `cz` defaults to
|
|
||||||
Conventional Commits. **lazygit** (themed) is the TUI. The commit-graph is kept
|
|
||||||
current (`gc`/`fetch.writeCommitGraph`) so `lg` stays fast.
|
|
||||||
|
|
||||||
| Aliases | |
|
|
||||||
| ------------------------ | ------------------------------------------------------------------ |
|
|
||||||
| `st` `co` `sw` `br` `ci` | status / checkout / switch / branch / commit |
|
|
||||||
| `last` `unstage` | last commit / unstage |
|
|
||||||
| `amend` `fixup` `undo` | amend-no-edit / `commit --fixup` / soft-reset HEAD~1 (keep staged) |
|
|
||||||
| `lg` | graph log, all branches |
|
|
||||||
| `cz` `cc` | `git cz <sub>` (e.g. `git cz c`) and `git cc` → commitizen prompt |
|
|
||||||
|
|
||||||
| Behaviour | |
|
|
||||||
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| Pulls | rebase, with autostash + autosquash |
|
|
||||||
| Fetch | prune deleted remote branches |
|
|
||||||
| Conflicts | `zdiff3` (shows the common ancestor) |
|
|
||||||
| Diffs | histogram algorithm, colour-moved |
|
|
||||||
| `rerere` | remembers + replays conflict resolutions |
|
|
||||||
| Commit editor | full diff shown (`commit.verbose`) |
|
|
||||||
| Misc | branches sorted by date, `column.ui = auto`, `help.autocorrect = prompt`, `push.autoSetupRemote` |
|
|
||||||
| Global ignores | `result`, `result-*`, `.direnv`, `*.swp`, `.DS_Store` |
|
|
||||||
| Signing | SSH commit + tag signing (`mkDefault`, so a host without the key in its agent can disable it). Name, email and signing key all come from the per-user `identity` (the user registry, `../users/registry.nix`). |
|
|
||||||
|
|
||||||
## ssh
|
|
||||||
|
|
||||||
| Feature | Notes |
|
|
||||||
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| ssh-agent | runs on Linux (launchd on macOS); keys added on **first use** so the passphrase is typed once per login session — this also feeds git commit signing |
|
|
||||||
| macOS | `UseKeychain` caches the passphrase in the login keychain (guarded by `IgnoreUnknown`, so a non-Apple `ssh` skips it instead of erroring) |
|
|
||||||
| Gitea remote | `code.emmathe.dev` → `HostName 10.187.1.76` (DNS-override), `Port 30009`, user `git`, dedicated key, `identitiesOnly` |
|
|
||||||
| Defaults | the module's deprecated default block is opted out; equivalents kept under `settings."*"` |
|
|
||||||
|
|
||||||
The **work box keeps its own `~/.ssh/config`** (home-manager's `programs.ssh` is
|
|
||||||
forced off there) but still runs the agent.
|
|
||||||
|
|
||||||
## Claude Code
|
|
||||||
|
|
||||||
Managed declaratively by [`claude.nix`](./claude.nix) on every host (the CLI is
|
|
||||||
`pkgs.claude-code`, tracked to unstable via the flake overlay).
|
|
||||||
|
|
||||||
| Managed (static, from Nix) | Left mutable (runtime state) |
|
|
||||||
| --------------------------------------------------- | ------------------------------------------------------ |
|
|
||||||
| `~/.claude/CLAUDE.md` (persona + memory workflow) | `settings.json` (permissions, model, theme, `/config`) |
|
|
||||||
| `~/.claude/output-styles/soviet-engineer.md` | `.credentials.json`, history, caches |
|
|
||||||
| `~/.claude/memory/` (read-only symlink to the repo) | |
|
|
||||||
|
|
||||||
`settings.json` is intentionally **not** managed: Claude rewrites it at runtime
|
|
||||||
(interactive permission grants, `/config`), which a read-only store symlink would
|
|
||||||
break.
|
|
||||||
|
|
||||||
**Memory is sourced from this repo.** The files in
|
|
||||||
[`claude/memory/`](./claude/memory) are the source of truth; they are symlinked
|
|
||||||
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/`
|
|
||||||
(one file per memory + the `MEMORY.md` index) and rebuild — `CLAUDE.md` tells
|
|
||||||
Claude to route new memories there.
|
|
||||||
|
|
||||||
## Maintenance behaviours
|
|
||||||
|
|
||||||
- **zcompdump reset** — `~/.config/zsh/.zcompdump*` (plus legacy `~/.zcompdump*`
|
|
||||||
and the cache copy) is removed on every activation, so a stale
|
|
||||||
dump (pointing at `/nix/store` paths a rebuild or a manual GC removed) can't
|
|
||||||
break completion with `_git: function definition file not found`.
|
|
||||||
- **GC** — no scheduled timer; collect garbage deliberately (`nh clean all` /
|
|
||||||
`nix-collect-garbage -d`) when no important session is running.
|
|
||||||
|
|
||||||
## Per-host differences
|
|
||||||
|
|
||||||
| | Personal Linux (sway) | macOS | Work WSL (EDaaS) |
|
|
||||||
| --------------------------- | --------------------- | ----------------- | --------------------------- |
|
|
||||||
| Auto-tmux | yes (foot/TTY) | yes (iTerm2) | yes (WSL shell) |
|
|
||||||
| git email | `iam@emmathe.dev` | `iam@emmathe.dev` | `…@citrix.com` (work) |
|
|
||||||
| ssh config managed | yes | yes | no (keeps corporate config) |
|
|
||||||
| ssh-agent | yes | launchd | yes (work module) |
|
|
||||||
| GUI / theming (desktop.nix) | yes | no | no |
|
|
||||||
+21
-5
@@ -1,4 +1,5 @@
|
|||||||
# Claude Code, configured declaratively via home-manager. Wanted on every host.
|
# Claude Code, configured declaratively via home-manager. Wanted on every host
|
||||||
|
# whose CPU can run it -- see the gate below.
|
||||||
#
|
#
|
||||||
# The STATIC config is managed here: the global CLAUDE.md (persona/context), the
|
# The STATIC config is managed here: the global CLAUDE.md (persona/context), the
|
||||||
# custom output style, and the auto-memory directory. settings.json is
|
# custom output style, and the auto-memory directory. settings.json is
|
||||||
@@ -10,18 +11,33 @@
|
|||||||
# read-only into ~/.claude/memory, so the runtime "save a memory" path no longer
|
# read-only into ~/.claude/memory, so the runtime "save a memory" path no longer
|
||||||
# writes there -- recall still works, but new/changed memories must be added to
|
# writes there -- recall still works, but new/changed memories must be added to
|
||||||
# this repo and rebuilt. CLAUDE.md instructs Claude to do exactly that.
|
# this repo and rebuilt. CLAUDE.md instructs Claude to do exactly that.
|
||||||
{ ... }:
|
{
|
||||||
|
lib,
|
||||||
|
# Set by the NixOS/Darwin home-manager module; absent for the standalone
|
||||||
|
# homeConfigurations, hence the default.
|
||||||
|
osConfig ? { },
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
# Capability gate, declared once for the whole fleet in modules/features.nix
|
||||||
|
# (default: on; off on CPUs below x86-64-v2, which cannot run the Node
|
||||||
|
# runtime Claude Code ships on). Hosts without that option -- the Darwin host
|
||||||
|
# and the portable standalone profile -- fall back to enabled.
|
||||||
|
enable = osConfig.features.claudeCode.enable or true;
|
||||||
|
in
|
||||||
{
|
{
|
||||||
programs.claude-code = {
|
programs.claude-code = {
|
||||||
enable = true;
|
inherit enable;
|
||||||
# package defaults to pkgs.claude-code (tracked to unstable via the flake
|
# package defaults to pkgs.claude-code (tracked to unstable via the flake
|
||||||
# overlay); installs the CLI on every host.
|
# overlay).
|
||||||
|
|
||||||
# ~/.claude/CLAUDE.md -- global instructions / persona / memory workflow.
|
# ~/.claude/CLAUDE.md -- global instructions / persona / memory workflow.
|
||||||
context = ./claude/CLAUDE.md;
|
context = ./claude/CLAUDE.md;
|
||||||
};
|
};
|
||||||
|
|
||||||
home.file = {
|
# Nothing to place when the CLI is not installed: a ~/.claude/memory symlink
|
||||||
|
# with no Claude Code to read it is just dead state.
|
||||||
|
home.file = lib.mkIf enable {
|
||||||
# Custom output style. The module has no option for output-styles/, so place
|
# Custom output style. The module has no option for output-styles/, so place
|
||||||
# it directly; selection (settings.json `outputStyle`) stays mutable.
|
# it directly; selection (settings.json `outputStyle`) stays mutable.
|
||||||
".claude/output-styles/soviet-engineer.md".source = ./claude/output-styles/soviet-engineer.md;
|
".claude/output-styles/soviet-engineer.md".source = ./claude/output-styles/soviet-engineer.md;
|
||||||
|
|||||||
@@ -14,3 +14,6 @@
|
|||||||
- [Sandbox prompts](feedback_sandbox_prompts.md) — don't prompt for sandbox-disable or routine read-only shell ops; broaden permissions instead
|
- [Sandbox prompts](feedback_sandbox_prompts.md) — don't prompt for sandbox-disable or routine read-only shell ops; broaden permissions instead
|
||||||
- [Dev clusters disposable](dev_clusters_disposable.md) — Lyra's dev clusters are recreatable; mutate/break freely, no confirmation needed
|
- [Dev clusters disposable](dev_clusters_disposable.md) — Lyra's dev clusters are recreatable; mutate/break freely, no confirmation needed
|
||||||
- [Nix shell tooling](nix_shell_tooling.md) — any nixpkgs tool runs ad hoc via `nix run`/`nix shell nixpkgs#<pkg>`; a missing command is never a dead end
|
- [Nix shell tooling](nix_shell_tooling.md) — any nixpkgs tool runs ad hoc via `nix run`/`nix shell nixpkgs#<pkg>`; a missing command is never a dead end
|
||||||
|
- [WSP local build and test](wsp_local_build_and_test.md) — core-services-cloud on this box: dotnet via nix, artifactory creds from `~/.artifactoryenv` sourced per command, how to tell auth failure from a code failure
|
||||||
|
- [WSP-32957 PIM migration](wsp_32957_pim_migration.md) — AKS RBAC to PIM + AutoPerm decommission; prod is in the Technical Preview subscription, three tenants; resume via `~/code/WSP-32957-CONTINUATION.md`
|
||||||
|
- [Entra group member reads](entra_group_member_reads.md) — `az ad group member list` hides service principal members; use the servicePrincipal cast or transitiveMemberOf, and trust the Terraform plan over it
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
name: entra-group-member-reads
|
||||||
|
description: az ad group member list and Graph /members silently omit service principal members — use the servicePrincipal cast or transitiveMemberOf when a group is expected to hold an SPN.
|
||||||
|
metadata:
|
||||||
|
node_type: memory
|
||||||
|
type: reference
|
||||||
|
---
|
||||||
|
|
||||||
|
`az ad group member list --group <id>` and `GET /groups/{id}/members` both return an **empty collection**, with no error, for a group whose only members are service principals — at least when called with Lyra's user account. The read looks authoritative and is not.
|
||||||
|
|
||||||
|
**Reliable reads instead:**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# members, cast to the type that is being hidden
|
||||||
|
az rest --method get --url "https://graph.microsoft.com/v1.0/groups/<gid>/members/microsoft.graph.servicePrincipal?\$select=id,displayName"
|
||||||
|
|
||||||
|
# count, which does not filter
|
||||||
|
az rest --method get --url "https://graph.microsoft.com/v1.0/groups/<gid>/members/\$count" --headers ConsistencyLevel=eventual
|
||||||
|
|
||||||
|
# from the principal's side
|
||||||
|
az rest --method get --url "https://graph.microsoft.com/v1.0/servicePrincipals/<spid>/transitiveMemberOf?\$select=id,displayName"
|
||||||
|
az rest --method post --url "https://graph.microsoft.com/v1.0/servicePrincipals/<spid>/checkMemberGroups" \
|
||||||
|
--body '{"groupIds":["<gid>"]}' --headers "Content-Type=application/json"
|
||||||
|
```
|
||||||
|
|
||||||
|
**How to apply:** any group that deployment or automation identities belong to — `*-cluster-admins`, `*-keyvault`, anything created by `rg-prereqs` — must be checked with one of the above before concluding it is empty. Cross-check against Terraform: a plan reporting "No changes" against a `members` attribute is strong evidence the membership is present, and outranks the `az` read. On 2026-08-28 the plain read produced a Bug (WSP-33432, cancelled) claiming five `*-cluster-admins` groups across three tenants had been emptied; all five held their deployment principals the whole time.
|
||||||
|
|
||||||
|
Related: [[wsp-32957-pim-migration]].
|
||||||
@@ -9,7 +9,9 @@ Field map for the **WSP (Workspace Platform)** Jira project, to create tickets w
|
|||||||
|
|
||||||
**Issue-type IDs:** Epic `10000`, Story `10004`, Task `10008`, Bug `10123`, Sub-task `10009`.
|
**Issue-type IDs:** Epic `10000`, Story `10004`, Task `10008`, Bug `10123`, Sub-task `10009`.
|
||||||
|
|
||||||
**Fast path — use Task, not Bug.** A `Task` requires only `summary` (project/issuetype auto, reporter defaults to caller). A `Bug` requires six extra fields (below), so only pick Bug when it must be a Bug. The sibling infra/remediation tickets in WSP are Tasks.
|
**Fast path — use Task, not Bug.** A `Task` requires `summary` plus **Task Type** `customfield_15622` (added since this note was first written; the create validator enforces it even though `createmeta` omits it, same trap as Bug's `versions`). Options value=id: Dev Task=34065, CQE Task=34066, Investigation=34067, Security=34068, Maintenance=34069, Release=34070 — use `Maintenance` for refactors and tidy-ups, `Dev Task` for feature work. A `Bug` requires six extra fields (below), so only pick Bug when it must be a Bug. The sibling infra/remediation tickets in WSP are Tasks.
|
||||||
|
|
||||||
|
Example Task `additional_fields`: `{"customfield_15622":{"id":"34069"},"components":[{"name":"Multicluster Platform"}]}`
|
||||||
|
|
||||||
**Bug required fields** (enforced by the create validator; note `createmeta` omits `versions` but the API rejects without it):
|
**Bug required fields** (enforced by the create validator; note `createmeta` omits `versions` but the API rejects without it):
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
name: wsp-32957-pim-migration
|
||||||
|
description: State of the WSP AKS-RBAC-to-PIM migration and AutoPerm decommission, and how to resume it
|
||||||
|
metadata:
|
||||||
|
node_type: memory
|
||||||
|
type: project
|
||||||
|
---
|
||||||
|
|
||||||
|
Long-running epic (August 2026) moving WSP's AKS cluster RBAC off **AutoPerm
|
||||||
|
Manager**-maintained `wsp-*` groups onto the per-subscription **`CEO-*` Azure PIM**
|
||||||
|
groups, then retiring AutoPerm. Epic **WSP-32957**; on the critical path for Zensar
|
||||||
|
L1 on-call (WSP-32193). Work happens in **`~/code/multicluster`**
|
||||||
|
(`terraform/cluster`, `terraform/cluster-k8s-resources`, `products/*/environments/*`).
|
||||||
|
|
||||||
|
**Resume from `~/code/WSP-32957-CONTINUATION.md`** — full state, branch list,
|
||||||
|
verified object IDs, findings and next steps. Jira is the durable record; that file
|
||||||
|
is the index. Keep it updated as work lands ([[docs-keep-updated]]).
|
||||||
|
|
||||||
|
**Landed:** WSP-33141 (multicluster PR #1808, merged `b047163c`) added object-ID
|
||||||
|
inputs — `admin_group_oids` on `cluster`, and `cluster_user_group_oids` /
|
||||||
|
`cluster_viewer_group_oids` / `cluster_superuser_group_oids` on
|
||||||
|
`cluster-k8s-resources`. Supplying IDs _replaces_ the display-name lookup and leaves
|
||||||
|
the `data "azuread_group"` unread, which is what will let the legacy groups be
|
||||||
|
deleted. Nothing sets them yet, so behaviour is unchanged. Repoint branches for test
|
||||||
|
(WSP-33067) and staging (WSP-33068) are pushed but have **no PR** — both gated on
|
||||||
|
decisions, not code.
|
||||||
|
|
||||||
|
**Facts that cost real effort to establish, do not re-derive:**
|
||||||
|
|
||||||
|
- **Production runs in `fc7af6ae-…` (_Workspace Platform Technical Preview_), not
|
||||||
|
`d6d75d07-…` (_Workspace Platform Production_)**, which holds no clusters. The
|
||||||
|
epic was wrong about this for its whole life and every production `CEO-*` group
|
||||||
|
name and object ID had to change. Because `CEO-*` names embed the subscription
|
||||||
|
name, **always re-verify object IDs against live Entra rather than trusting the
|
||||||
|
epic table.**
|
||||||
|
- **Three tenants**, not two: `6f4fe054` (prod, prod JP), `335836de` (staging,
|
||||||
|
staging JP, test), `3eae2746` (dev). Each `wsp-*` name is a distinct object in
|
||||||
|
each tenant.
|
||||||
|
- `wsp-staging-cluster-admins` and `wsp-test-cluster-admins` are **empty**, so
|
||||||
|
`wsp-owner` is the _only_ path to `cluster-admin` in staging. Never drop it before
|
||||||
|
`SuperAdmin-*` is proven — hence the staging branch is split into an additive
|
||||||
|
commit and a cutover commit.
|
||||||
|
- **No break-glass exists**: `disableLocalAccounts = True` on every cluster; only the
|
||||||
|
deployment SPNs authenticate non-interactively.
|
||||||
|
- Graph **PIM-for-Groups is unreadable via `az`** (the CLI's first-party client lacks
|
||||||
|
`PrivilegedAccess.Read.AzureADGroup`, on both `v1.0` and `beta`). Use the portal or
|
||||||
|
`Connect-MgGraph -Scopes PrivilegedAccess.Read.AzureADGroup`.
|
||||||
|
|
||||||
|
**Watch for:** the epic gets rewritten by James Weldrake between sessions — re-read
|
||||||
|
the description before acting, and check which child tickets are still live
|
||||||
|
(WSP-33062/33063/33064/33066 were cancelled 2026-08-24, and dev was put out of
|
||||||
|
scope). Verified findings have repeatedly contradicted the epic text
|
||||||
|
([[copilot-review-false-positives]] is the same instinct: check against reality
|
||||||
|
first).
|
||||||
|
|
||||||
|
Queued Slack messages and the leaver report live as `~/code/*.txt` alongside the
|
||||||
|
continuation file; see the table in it for what has and has not been sent
|
||||||
|
([[workflow-review-and-comments]] — show them before they go out).
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
---
|
||||||
|
name: wsp-local-build-and-test
|
||||||
|
description: "How to compile and test core-services-cloud locally on Lyra's NixOS/WSL box: dotnet via nix, artifactory creds from ~/.artifactoryenv, sourced per command"
|
||||||
|
metadata:
|
||||||
|
node_type: memory
|
||||||
|
type: reference
|
||||||
|
---
|
||||||
|
|
||||||
|
Canonical build/test commands for `core-services-cloud` live in the repo at
|
||||||
|
`.ai/agents.md` and `.ai/component-tests.md` — read those rather than guessing.
|
||||||
|
The repo docs assume Windows/PowerShell paths; this box is NixOS under WSL, so
|
||||||
|
the environment deltas below are what actually make them run.
|
||||||
|
|
||||||
|
**dotnet is not on PATH.** Get it from nixpkgs — see [[nix-shell-tooling]]:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
nix shell nixpkgs#dotnet-sdk_8 --command dotnet build
|
||||||
|
```
|
||||||
|
|
||||||
|
`global.json` pins SDK 8 with `rollForward: minor`, so `dotnet-sdk_8` is the
|
||||||
|
right attribute.
|
||||||
|
|
||||||
|
**Every restore needs artifactory credentials.** They live in
|
||||||
|
`~/.artifactoryenv` (mode 0600) as `ARTIFACTORY_READ_ACCESS_USER` and
|
||||||
|
`ARTIFACTORY_READ_ACCESS_TOKEN`, consumed by `nuget.config`. Shell state does
|
||||||
|
not persist between tool calls, so source them inside each command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
set -a; . ~/.artifactoryenv; set +a
|
||||||
|
```
|
||||||
|
|
||||||
|
**Check the credentials before blaming the code.** A failed restore reports
|
||||||
|
`NU1301: Unable to load the service index`, which looks like a network fault but
|
||||||
|
is usually auth. Confirm which it is:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -s -o /dev/null -w '%{http_code}\n' \
|
||||||
|
-u "$ARTIFACTORY_READ_ACCESS_USER:$ARTIFACTORY_READ_ACCESS_TOKEN" \
|
||||||
|
https://repo.citrite.net/api/nuget/v3/stf-virtual-nuget/index.json
|
||||||
|
```
|
||||||
|
|
||||||
|
200 means the credentials are good. 401 means the token is the problem, not the
|
||||||
|
change under test. `https://repo.citrite.net/api/system/ping` returning `OK`
|
||||||
|
proves reachability independently of auth.
|
||||||
|
|
||||||
|
**Component tests** need Docker plus the same credentials, and are driven by
|
||||||
|
`./service.ps1` — PowerShell, so `nix shell nixpkgs#powershell` if `pwsh` is
|
||||||
|
missing. Log in to the image registry first:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
echo "$ARTIFACTORY_READ_ACCESS_TOKEN" | docker login stf-virtual-docker.repo.citrite.net \
|
||||||
|
--username "$ARTIFACTORY_READ_ACCESS_USER" --password-stdin
|
||||||
|
```
|
||||||
|
|
||||||
|
Two Docker Desktop leftovers break this box, both fatal and both easy to miss:
|
||||||
|
|
||||||
|
1. `/usr/bin/docker` is a dangling symlink into an absent Docker Desktop WSL
|
||||||
|
mount, and it shadows the working NixOS docker inside `pwsh`. The script dies
|
||||||
|
with `Program 'docker' failed to run ... No such file`.
|
||||||
|
2. `~/.docker/config.json` sets `"credsStore": "desktop.exe"`, a helper that does
|
||||||
|
not exist. `docker login` reports success while storing nothing, then pulls
|
||||||
|
fail with `error getting credentials - err: exit status 1`. Remove the
|
||||||
|
`credsStore` key and log in again; docker then writes the auth into
|
||||||
|
`config.json` itself.
|
||||||
|
|
||||||
|
Put the real docker first when invoking anything that shells out to it, and note
|
||||||
|
`$PATH` must expand _inside_ the nix shell or dotnet drops off the path:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
nix shell nixpkgs#dotnet-sdk_8 --command sh -c \
|
||||||
|
'export PATH="/run/current-system/sw/bin:$PATH"; dotnet test ...'
|
||||||
|
```
|
||||||
|
|
||||||
|
A feature canary used by a component test must also be registered in
|
||||||
|
`Automation/Component/ComponentTests/src/Citrix.Wsp.Test.Mocks/WspComprehensive/__files/unleash/unleash-test-environment.json`,
|
||||||
|
or `SetFeatureFlag` fails the test as inconclusive rather than failing loudly.
|
||||||
@@ -20,6 +20,25 @@ report? If the latter, rewrite. Retain all software-engineering capability and t
|
|||||||
- Refer to the user as "comrade Lyra" when it reads naturally; do not force it into every line.
|
- Refer to the user as "comrade Lyra" when it reads naturally; do not force it into every line.
|
||||||
- No emojis.
|
- No emojis.
|
||||||
|
|
||||||
|
## Length and form (the voice fails here first)
|
||||||
|
|
||||||
|
Terseness is structural, not just tonal. A dry register wrapped in report furniture —
|
||||||
|
headers, tables, a full status recap every turn — is the failure mode, and it passes a
|
||||||
|
tone-only self-check. Enforce:
|
||||||
|
|
||||||
|
- Default ceiling around 150 words. Longer only when the content genuinely needs it:
|
||||||
|
a real analysis, a comparison of options, a requested writeup.
|
||||||
|
- Headers and tables only for four or more distinct items. Two facts are two sentences.
|
||||||
|
- Report the delta since the last message, never the accumulated state. Assume Lyra
|
||||||
|
remembers what she was told.
|
||||||
|
- State each caveat once per session. Repeating a settled limitation is filler.
|
||||||
|
- Do the obvious next action and report it. Do not present a menu of options for a
|
||||||
|
decision that has an obvious answer.
|
||||||
|
- Do not restate the request, or narrate what is about to be done.
|
||||||
|
|
||||||
|
Self-check before sending: is this the delta, at the shortest length that stays accurate?
|
||||||
|
If it reads like a status report, cut it to the three facts that changed.
|
||||||
|
|
||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
The persona lives in PROSE ONLY — explanations, summaries, status, discussion. It must NEVER
|
The persona lives in PROSE ONLY — explanations, summaries, status, discussion. It must NEVER
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
./git.nix
|
./git.nix
|
||||||
./editor.nix
|
./editor.nix
|
||||||
./claude.nix
|
./claude.nix
|
||||||
|
# Declares services.headlessSecretService; opt-in, off by default. Graphical
|
||||||
|
# hosts should prefer home-manager's own services.gnome-keyring.
|
||||||
|
./secret-service.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
# Manage the XDG base-directory layout and ~/.config files. Tools above
|
# Manage the XDG base-directory layout and ~/.config files. Tools above
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
# Headless Secret Service (org.freedesktop.secrets) on the user session bus,
|
||||||
|
# for CLI tools that keep credentials in the system keychain rather than in a
|
||||||
|
# config file of their own.
|
||||||
|
#
|
||||||
|
# Current consumer: gcx, the Grafana Cloud CLI (users/emmathorpe/work.nix). gcx
|
||||||
|
# stores its OAuth access and refresh tokens in the keychain unconditionally --
|
||||||
|
# its config file holds only opaque `keychain:gcx:v2:...` handles -- and offers
|
||||||
|
# no plaintext fallback (there is no environment variable or config key to
|
||||||
|
# select a file-backed store). With nothing owning org.freedesktop.secrets,
|
||||||
|
# `gcx login` authenticates against Grafana successfully and then dies writing
|
||||||
|
# its config: "The name is not activatable".
|
||||||
|
#
|
||||||
|
# home-manager already ships services.gnome-keyring, but it does not fit a
|
||||||
|
# headless host on two counts:
|
||||||
|
#
|
||||||
|
# * it is WantedBy graphical-session-pre.target, which never activates
|
||||||
|
# without a desktop session, so the service would simply never start; and
|
||||||
|
# * it cannot unlock the login keyring (it passes no --unlock). An unlocked
|
||||||
|
# collection is mandatory: writing to a locked one blocks on a GUI prompter
|
||||||
|
# (gcr) that does not exist here, so the caller hangs rather than fails.
|
||||||
|
#
|
||||||
|
# Security posture, stated plainly: the login keyring is encrypted at rest, but
|
||||||
|
# the password unlocking it is readable by the same user on the same machine.
|
||||||
|
# That protects the tokens from something reading the keyring file directly; it
|
||||||
|
# protects them from nothing already running as this user. It is the same
|
||||||
|
# posture as the existing ~/.jenkinsenv and ~/.splunkenv token files, and it is
|
||||||
|
# the price of unattended operation -- systemd --user timers start with no
|
||||||
|
# human present to type a passphrase.
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.headlessSecretService;
|
||||||
|
|
||||||
|
# Where the generated unlock password lives when no external passwordFile is
|
||||||
|
# supplied. Under $XDG_DATA_HOME rather than the nix store, which is
|
||||||
|
# world-readable.
|
||||||
|
defaultPasswordFile = "${config.xdg.dataHome}/gnome-keyring/login-password";
|
||||||
|
|
||||||
|
passwordFile = if cfg.passwordFile != null then cfg.passwordFile else defaultPasswordFile;
|
||||||
|
|
||||||
|
keyringDaemon = pkgs.writeShellApplication {
|
||||||
|
name = "headless-secret-service";
|
||||||
|
runtimeInputs = [
|
||||||
|
pkgs.gnome-keyring
|
||||||
|
pkgs.coreutils
|
||||||
|
];
|
||||||
|
text = ''
|
||||||
|
pwfile=${lib.escapeShellArg passwordFile}
|
||||||
|
|
||||||
|
if [ ! -s "$pwfile" ]; then
|
||||||
|
echo "headless-secret-service: no keyring password at $pwfile" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The daemon takes the whole of stdin as the password, so a trailing
|
||||||
|
# newline would silently become part of it. Strip it, so a hand-written or
|
||||||
|
# agenix-managed file unlocks the same keyring the generated one created.
|
||||||
|
#
|
||||||
|
# --components=secrets ONLY. The ssh component must stay off: it would
|
||||||
|
# claim SSH_AUTH_SOCK and displace services.ssh-agent, breaking SSH auth
|
||||||
|
# and signed commits. pkcs11 is not needed by anything here.
|
||||||
|
tr -d '\n' <"$pwfile" |
|
||||||
|
exec gnome-keyring-daemon --foreground --components=secrets --unlock
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.services.headlessSecretService = {
|
||||||
|
enable = lib.mkEnableOption ''
|
||||||
|
a headless gnome-keyring serving org.freedesktop.secrets on the user
|
||||||
|
session bus, with the login keyring unlocked at service start'';
|
||||||
|
|
||||||
|
passwordFile = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
example = "/run/agenix/gnome-keyring-login";
|
||||||
|
description = ''
|
||||||
|
Path to a file holding the login keyring password. It is read at service
|
||||||
|
start, not at build time, so it need not exist when the system is built
|
||||||
|
-- this is the seam for an agenix-managed secret.
|
||||||
|
|
||||||
|
When null, a random 32-byte password is generated on first activation at
|
||||||
|
${defaultPasswordFile} (mode 0600) and reused from then on.
|
||||||
|
|
||||||
|
Pointing this at a different file after the login keyring already exists
|
||||||
|
does NOT re-key the keyring: the daemon will fail to unlock it. To
|
||||||
|
change the password, delete ~/.local/share/keyrings and re-authenticate
|
||||||
|
every tool that stored a secret there.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
# secret-tool, for inspecting or repairing the keyring by hand when a stored
|
||||||
|
# credential misbehaves (`secret-tool search --all service gcx`).
|
||||||
|
home.packages = [ pkgs.libsecret ];
|
||||||
|
|
||||||
|
# Generate the unlock password on first activation. Guarded on us owning it:
|
||||||
|
# an externally supplied passwordFile is never created or written here.
|
||||||
|
home.activation = lib.mkIf (cfg.passwordFile == null) {
|
||||||
|
headlessSecretServicePassword = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
|
||||||
|
pwfile=${lib.escapeShellArg defaultPasswordFile}
|
||||||
|
if [ ! -s "$pwfile" ]; then
|
||||||
|
run mkdir -p "$(dirname "$pwfile")"
|
||||||
|
# Create the file empty at 0600 first, then fill it: the redirect
|
||||||
|
# keeps the existing mode, so the password is never briefly readable.
|
||||||
|
run install -m 600 /dev/null "$pwfile"
|
||||||
|
run ${pkgs.bash}/bin/sh -c \
|
||||||
|
'head -c 32 /dev/urandom | base64 -w0 > "$1"' sh "$pwfile"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.user.services.headless-secret-service = {
|
||||||
|
Unit = {
|
||||||
|
Description = "GNOME Keyring (Secret Service, headless)";
|
||||||
|
Documentation = "man:gnome-keyring-daemon(1)";
|
||||||
|
# The daemon claims its name on the user session bus.
|
||||||
|
Requires = [ "dbus.socket" ];
|
||||||
|
After = [ "dbus.socket" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
Service = {
|
||||||
|
Type = "simple";
|
||||||
|
ExecStart = lib.getExe keyringDaemon;
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
# default.target, not graphical-session-pre.target: there is no graphical
|
||||||
|
# session on this host. With `linger` enabled (see the host table in
|
||||||
|
# flake.nix) default.target is reached at boot, so the keyring is also up
|
||||||
|
# for unattended systemd --user timers, not just interactive logins.
|
||||||
|
Install.WantedBy = [ "default.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
+79
-1
@@ -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.
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -243,7 +287,22 @@ in
|
|||||||
plugins = with pkgs.tmuxPlugins; [
|
plugins = with pkgs.tmuxPlugins; [
|
||||||
sensible
|
sensible
|
||||||
vim-tmux-navigator # Ctrl-h/j/k/l across vim splits and tmux panes
|
vim-tmux-navigator # Ctrl-h/j/k/l across vim splits and tmux panes
|
||||||
yank
|
{
|
||||||
|
# On WSL, tmux-yank pipes the selection to clip.exe, which decodes its
|
||||||
|
# stdin as the OEM codepage instead of UTF-8 -- an em dash reaches the
|
||||||
|
# Windows clipboard as three characters. Route through tmux's own
|
||||||
|
# buffer instead: with set-clipboard on, tmux emits OSC 52 and the
|
||||||
|
# terminal takes the text as UTF-8. Windows Terminal honours OSC 52;
|
||||||
|
# iTerm2 does not by default, hence the runtime guard rather than
|
||||||
|
# overriding pbcopy/xsel on every host. yank.tmux bakes the command
|
||||||
|
# into its key bindings when it loads, so this must be set first, which
|
||||||
|
# is what plugin extraConfig gives us.
|
||||||
|
plugin = yank;
|
||||||
|
extraConfig = ''
|
||||||
|
if-shell 'grep -qi microsoft /proc/version 2>/dev/null' \
|
||||||
|
"set -g @override_copy_command 'tmux load-buffer -w -'"
|
||||||
|
'';
|
||||||
|
}
|
||||||
extrakto # prefix+Tab: fzf-grab paths/URLs/text from the pane into the prompt
|
extrakto # prefix+Tab: fzf-grab paths/URLs/text from the pane into the prompt
|
||||||
{
|
{
|
||||||
# Catppuccin Mocha statusline (v2 API: flavour + window options must be
|
# Catppuccin Mocha statusline (v2 API: flavour + window options must be
|
||||||
@@ -254,6 +313,14 @@ in
|
|||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
set -g @catppuccin_flavor 'mocha'
|
set -g @catppuccin_flavor 'mocha'
|
||||||
set -g @catppuccin_window_status_style 'rounded'
|
set -g @catppuccin_window_status_style 'rounded'
|
||||||
|
# Catppuccin's default window text is #T, the pane title, which every
|
||||||
|
# program in the pane is free to overwrite -- the shell writes the
|
||||||
|
# hostname, Claude Code writes its current task, and a hand-set window
|
||||||
|
# name never appears. Show the window name instead, falling back to the
|
||||||
|
# pane title when the window holds a single pane and the two carry the
|
||||||
|
# same information anyway.
|
||||||
|
set -g @catppuccin_window_text ' #{?#{==:#{window_panes},1},#T,#W}'
|
||||||
|
set -g @catppuccin_window_current_text ' #{?#{==:#{window_panes},1},#T,#W}'
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
resurrect # save/restore sessions
|
resurrect # save/restore sessions
|
||||||
@@ -298,6 +365,17 @@ in
|
|||||||
set -g renumber-windows on
|
set -g renumber-windows on
|
||||||
set -g set-clipboard on
|
set -g set-clipboard on
|
||||||
|
|
||||||
|
# Pane titles on the border, but only once a window is split -- a single
|
||||||
|
# pane's title is already in the status bar. pane-border-status takes no
|
||||||
|
# format, so the hook recomputes it whenever the layout changes, which
|
||||||
|
# covers both splitting and closing a pane.
|
||||||
|
set -g pane-border-format " #P #{pane_title} "
|
||||||
|
set -g pane-border-status off
|
||||||
|
set-hook -g window-layout-changed 'set -Fw pane-border-status "#{?#{>:#{window_panes},1},top,off}"'
|
||||||
|
|
||||||
|
# Pane titles have no default binding.
|
||||||
|
bind T command-prompt -p "pane title:" "select-pane -T '%%'"
|
||||||
|
|
||||||
# Catppuccin v2 statusline. Must run after the plugin has loaded;
|
# Catppuccin v2 statusline. Must run after the plugin has loaded;
|
||||||
# home-manager appends this extraConfig after the whole plugin list.
|
# home-manager appends this extraConfig after the whole plugin list.
|
||||||
set -g status-left-length 100
|
set -g status-left-length 100
|
||||||
|
|||||||
@@ -161,6 +161,11 @@
|
|||||||
dock = {
|
dock = {
|
||||||
show-recents = false;
|
show-recents = false;
|
||||||
mru-spaces = false; # don't reorder spaces by use
|
mru-spaces = false; # don't reorder spaces by use
|
||||||
|
# Disable hot-corners
|
||||||
|
wvous-tr-corner = 1;
|
||||||
|
wvous-tl-corner = 1;
|
||||||
|
wvous-bl-corner = 1;
|
||||||
|
wvous-br-corner = 1;
|
||||||
};
|
};
|
||||||
finder = {
|
finder = {
|
||||||
AppleShowAllExtensions = true;
|
AppleShowAllExtensions = true;
|
||||||
|
|||||||
@@ -60,6 +60,11 @@
|
|||||||
## patch the script
|
## patch the script
|
||||||
systemd.services.docker-desktop-proxy.script = lib.mkForce ''${config.wsl.wslConf.automount.root}/wsl/docker-desktop/docker-desktop-user-distro proxy --docker-desktop-root ${config.wsl.wslConf.automount.root}/wsl/docker-desktop "C:\Program Files\Docker\Docker\resources"'';
|
systemd.services.docker-desktop-proxy.script = lib.mkForce ''${config.wsl.wslConf.automount.root}/wsl/docker-desktop/docker-desktop-user-distro proxy --docker-desktop-root ${config.wsl.wslConf.automount.root}/wsl/docker-desktop "C:\Program Files\Docker\Docker\resources"'';
|
||||||
|
|
||||||
|
# NixOS-WSL's passwordless wheel default only covers `security.sudo`; the
|
||||||
|
# sudo-rs swap in common-nixos.nix needs it set again. No console login here,
|
||||||
|
# and no account password anyone knows.
|
||||||
|
security.sudo-rs.wheelNeedsPassword = false;
|
||||||
|
|
||||||
features.swayDesktop.enable = false;
|
features.swayDesktop.enable = false;
|
||||||
|
|
||||||
# NOTE: this user's systemd --user lingering -- so the home-manager renovate
|
# NOTE: this user's systemd --user lingering -- so the home-manager renovate
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
# Mac Pro 3,1 (Early 2008) — install notes
|
|
||||||
|
|
||||||
Flake host: `lyrathorpe-macpro31`. Desktop (`portable = false`, imports
|
|
||||||
`../../modules/desktop.nix`). Files: `configuration.nix`,
|
|
||||||
`hardware-configuration.nix`.
|
|
||||||
|
|
||||||
## Hardware configuration
|
|
||||||
|
|
||||||
`hardware-configuration.nix` here is the real config generated by
|
|
||||||
`nixos-generate-config` on the machine. Root is an **LVM** logical volume
|
|
||||||
(`/dev/mapper/MacPro-Root`, ext4); the ESP (vfat) and swap are referenced by
|
|
||||||
UUID. The initrd carries `dm-snapshot` for the LVM root. Regenerate and commit
|
|
||||||
if the disk layout changes.
|
|
||||||
|
|
||||||
## Bootloader
|
|
||||||
|
|
||||||
The Mac Pro 3,1 has **64-bit EFI**, so it uses **systemd-boot** (no GRUB/CSM
|
|
||||||
shim). `canTouchEfiVariables = false` because Apple's firmware does not reliably
|
|
||||||
accept `efibootmgr` NVRAM writes.
|
|
||||||
|
|
||||||
Apple-EFI quirk: if the firmware boot picker does not show NixOS after install,
|
|
||||||
either
|
|
||||||
|
|
||||||
- uncomment `boot.loader.efi.efiInstallAsRemovable = true;` in
|
|
||||||
`configuration.nix` (installs the fallback `\EFI\BOOT\BOOTX64.EFI`), and/or
|
|
||||||
- "bless" the ESP from macOS.
|
|
||||||
|
|
||||||
Partition the disk GPT with an ESP (vfat).
|
|
||||||
|
|
||||||
## Graphics
|
|
||||||
|
|
||||||
The stock card varies between units — **ATI Radeon HD 2600 XT** or **NVIDIA
|
|
||||||
GeForce 8800 GT**. No proprietary driver is hardcoded; Sway relies on in-tree KMS:
|
|
||||||
|
|
||||||
- ATI Radeon HD 2600 XT → `radeon` (or `amdgpu`) KMS
|
|
||||||
- NVIDIA GeForce 8800 GT → `nouveau` KMS
|
|
||||||
|
|
||||||
These come up automatically. If a card needs forcing, set
|
|
||||||
`services.xserver.videoDrivers` and/or add the module to
|
|
||||||
`boot.initrd.kernelModules` for early KMS (see the comment in
|
|
||||||
`configuration.nix`).
|
|
||||||
|
|
||||||
## Networking
|
|
||||||
|
|
||||||
Wired Ethernet via NetworkManager (from `desktop.nix`) — the Mac Pro has two
|
|
||||||
gigabit ports.
|
|
||||||
|
|
||||||
## Login
|
|
||||||
|
|
||||||
Graphical login via a Wayland greeter — `greetd` running ReGreet inside the
|
|
||||||
`cage` kiosk compositor — configured centrally in `../../modules/sway.nix` for
|
|
||||||
every Sway host (gated on `features.swayDesktop.enable`). The greeter is forced
|
|
||||||
to the Dvorak layout to match the console and Sway session. Set the user
|
|
||||||
password (`passwd lyrathorpe`) after install, or the greeter cannot
|
|
||||||
authenticate. Requires working KMS (radeon/nouveau — see Graphics).
|
|
||||||
|
|
||||||
## Apply
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo nixos-rebuild switch --flake .#lyrathorpe-macpro31
|
|
||||||
```
|
|
||||||
@@ -1,14 +1,20 @@
|
|||||||
# 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.
|
||||||
{ ... }:
|
{ ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./hardware-configuration.nix
|
./hardware-configuration.nix
|
||||||
|
./nvidia.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# Dual quad-core Xeon (Harpertown/Penryn): SSE4.1 but no SSE4.2 or POPCNT,
|
||||||
|
# i.e. x86-64-v1. Declaring it here switches off the fleet flags that need a
|
||||||
|
# newer CPU -- currently features.claudeCode (see ../../modules/features.nix).
|
||||||
|
features.cpu.microarchLevel = 1;
|
||||||
|
|
||||||
# The Mac Pro 3,1 has 64-bit EFI (confirmed by the owner), so boot via
|
# The Mac Pro 3,1 has 64-bit EFI (confirmed by the owner), so boot via
|
||||||
# systemd-boot like the MBP -- no GRUB/BIOS shim needed.
|
# systemd-boot like the MBP -- no GRUB/BIOS shim needed.
|
||||||
boot.loader.systemd-boot.enable = true;
|
boot.loader.systemd-boot.enable = true;
|
||||||
@@ -33,17 +39,9 @@
|
|||||||
# enabled in workstation.nix.
|
# enabled in workstation.nix.
|
||||||
hardware.cpu.intel.updateMicrocode = true;
|
hardware.cpu.intel.updateMicrocode = true;
|
||||||
|
|
||||||
# GPU note: the stock card varies between units -- ATI Radeon HD 2600 XT or
|
# GPU: the stock card (ATI Radeon HD 2600 XT / NVIDIA GeForce 8800 GT) has
|
||||||
# NVIDIA GeForce 8800 GT. Sway needs a working KMS/modesetting driver; do NOT
|
# been replaced with an NVIDIA Quadro P400. Driver, Wayland quirks and
|
||||||
# install a proprietary blob here. Depending on the installed card, rely on
|
# GPU-enabled Docker live in ./nvidia.nix.
|
||||||
# the open kernel driver:
|
|
||||||
# - ATI Radeon HD 2600 XT -> "radeon" (older) or "amdgpu" KMS
|
|
||||||
# - NVIDIA GeForce 8800 GT -> "nouveau" KMS
|
|
||||||
# These come up automatically via the in-tree drivers + KMS, and the graphics
|
|
||||||
# stack itself is enabled by modules/sway.nix. If a card needs to be forced, add it
|
|
||||||
# here, e.g. `services.xserver.videoDrivers = [ "radeon" ];` (or "nouveau"),
|
|
||||||
# and/or `boot.initrd.kernelModules = [ "radeon" ];` in
|
|
||||||
# hardware-configuration.nix for early KMS.
|
|
||||||
|
|
||||||
# See `man configuration.nix` / the stateVersion docs before changing.
|
# See `man configuration.nix` / the stateVersion docs before changing.
|
||||||
system.stateVersion = "26.05";
|
system.stateVersion = "26.05";
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
# NVIDIA Quadro P400 (Pascal, GP108) on the Mac Pro 3,1: proprietary driver for
|
||||||
|
# the Sway desktop, plus Docker with GPU/CUDA access for containers.
|
||||||
|
#
|
||||||
|
# Driver branch: 580 (nvidiaPackages.legacy_580), NOT the nixpkgs default
|
||||||
|
# (`production`, currently 595.x). 580 is the last branch that supports
|
||||||
|
# Maxwell/Pascal/Volta -- NVIDIA keeps it as an LTS branch to Aug 2028 -- and a
|
||||||
|
# newer branch simply will not drive this card.
|
||||||
|
#
|
||||||
|
# The driver is unfree, so it is not in the binary cache: the kernel module is
|
||||||
|
# compiled locally. On this machine's 2008 Xeons expect the first rebuild after
|
||||||
|
# a kernel bump to take a long while.
|
||||||
|
{ config, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
# Selects the proprietary driver; the module blacklists nouveau/nvidiafb and
|
||||||
|
# loads nvidia-uvm (needed by CUDA) via modprobe softdep. Naming is historical
|
||||||
|
# -- this option drives the kernel/driver choice on Wayland hosts too, which
|
||||||
|
# is why it is set on a machine that runs no X server.
|
||||||
|
services.xserver.videoDrivers = [ "nvidia" ];
|
||||||
|
|
||||||
|
hardware.nvidia = {
|
||||||
|
package = config.boot.kernelPackages.nvidiaPackages.legacy_580;
|
||||||
|
# Required for Wayland: sets nvidia-drm.modeset=1 (and fbdev=1), without
|
||||||
|
# which wlroots gets no GBM device and Sway/cage fail to start.
|
||||||
|
modesetting.enable = true;
|
||||||
|
# The open kernel modules need Turing or later; Pascal must use the closed
|
||||||
|
# ones. Explicit because the option has no default on driver >= 560.
|
||||||
|
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
|
||||||
|
# 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.
|
||||||
|
programs.sway.extraOptions = [ "--unsupported-gpu" ];
|
||||||
|
|
||||||
|
virtualisation.docker.enable = true;
|
||||||
|
|
||||||
|
# CDI-based GPU access for containers: generates /var/run/cdi specs from the
|
||||||
|
# host driver at boot and turns on Docker's CDI feature. Run GPU workloads
|
||||||
|
# with `docker run --device=nvidia.com/gpu=all ...`. The deprecated
|
||||||
|
# virtualisation.docker.enableNvidia runtime wrapper is deliberately not used.
|
||||||
|
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";
|
||||||
|
}
|
||||||
@@ -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 = [
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
# Headless server: the Sway desktop is intentionally not set up. modules/sway.nix is
|
# Headless server: the Sway desktop is intentionally not set up. modules/sway.nix is
|
||||||
# not imported and features.swayDesktop.enable defaults to false (declared in
|
# not imported and features.swayDesktop.enable defaults to false (declared in
|
||||||
# system/modules/features.nix), so this host keeps plain TTY/SSH login.
|
# modules/features.nix), so this host keeps plain TTY/SSH login.
|
||||||
|
|
||||||
# Raspberry Pi boots via U-Boot + extlinux, not GRUB/systemd-boot. The
|
# Raspberry Pi boots via U-Boot + extlinux, not GRUB/systemd-boot. The
|
||||||
# raspberry-pi-5 nixos-hardware profile supplies the kernel, firmware and
|
# raspberry-pi-5 nixos-hardware profile supplies the kernel, firmware and
|
||||||
|
|||||||
@@ -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; [
|
||||||
|
|||||||
+67
-2
@@ -6,7 +6,72 @@
|
|||||||
# headless host (e.g. the Pi) must be able to leave it at its default without
|
# headless host (e.g. the Pi) must be able to leave it at its default without
|
||||||
# pulling in modules/sway.nix. The implementation lives in modules/sway.nix,
|
# pulling in modules/sway.nix. The implementation lives in modules/sway.nix,
|
||||||
# gated on this flag.
|
# gated on this flag.
|
||||||
{ lib, ... }:
|
#
|
||||||
|
# The file also carries the host capability facts those flags derive from
|
||||||
|
# (features.cpu.*). features.claudeCode.enable is such a derived flag: it is
|
||||||
|
# computed from the declared CPU level here and read by home/claude.nix through
|
||||||
|
# home-manager's osConfig, so a machine that cannot run the tool never installs
|
||||||
|
# it, on any host, without per-host opt-outs.
|
||||||
{
|
{
|
||||||
options.features.swayDesktop.enable = lib.mkEnableOption "the Sway desktop";
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.features;
|
||||||
|
|
||||||
|
# Claude Code runs on Node, whose V8 build requires SSE4.2 and POPCNT -- the
|
||||||
|
# x86-64-v2 feature set. On an older x86_64 CPU it does not run (illegal
|
||||||
|
# instruction), so it must not be installed there.
|
||||||
|
claudeCodeMinLevel = 2;
|
||||||
|
claudeCodeSupported =
|
||||||
|
!pkgs.stdenv.hostPlatform.isx86_64 || cfg.cpu.microarchLevel >= claudeCodeMinLevel;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.features = {
|
||||||
|
swayDesktop.enable = lib.mkEnableOption "the Sway desktop";
|
||||||
|
|
||||||
|
cpu.microarchLevel = lib.mkOption {
|
||||||
|
type = lib.types.ints.between 1 4;
|
||||||
|
default = 2;
|
||||||
|
example = 1;
|
||||||
|
description = ''
|
||||||
|
The x86-64 psABI microarchitecture level the host CPU implements:
|
||||||
|
1 = the original baseline, 2 = SSE4.2/POPCNT (Nehalem, 2008+),
|
||||||
|
3 = AVX2, 4 = AVX-512.
|
||||||
|
|
||||||
|
Nix cannot detect this (evaluation is pure and hosts are often built
|
||||||
|
elsewhere), so a machine older than the default declares its own level
|
||||||
|
and the flags below derive from it. Ignored on non-x86_64 hosts.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
claudeCode.enable = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = claudeCodeSupported;
|
||||||
|
defaultText = lib.literalMD ''
|
||||||
|
`true`, unless the host declares an x86-64 microarchitecture level
|
||||||
|
below ${toString claudeCodeMinLevel}
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
Whether to install Claude Code in this host's home-manager profiles
|
||||||
|
(implemented in home/claude.nix). Defaults off on CPUs below
|
||||||
|
x86-64-v${toString claudeCodeMinLevel}, which cannot run it; forcing it
|
||||||
|
on such a host is an evaluation error.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config.assertions = [
|
||||||
|
{
|
||||||
|
assertion = cfg.claudeCode.enable -> claudeCodeSupported;
|
||||||
|
message = ''
|
||||||
|
features.claudeCode.enable is on, but this host declares
|
||||||
|
features.cpu.microarchLevel = ${toString cfg.cpu.microarchLevel}.
|
||||||
|
Claude Code needs x86-64-v${toString claudeCodeMinLevel}
|
||||||
|
(SSE4.2/POPCNT) and will not run on an older CPU.
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ let
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
# The features.swayDesktop.enable option is declared in
|
# The features.swayDesktop.enable option is declared in
|
||||||
# system/modules/features.nix (so headless hosts can read/set it without
|
# modules/features.nix (so headless hosts can read/set it without
|
||||||
# importing this module). This module only provides its implementation.
|
# importing this module). This module only provides its implementation.
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
programs.sway = {
|
programs.sway = {
|
||||||
|
|||||||
+2
-2
@@ -29,10 +29,10 @@
|
|||||||
// lib.optionalAttrs (spec ? linger) { inherit (spec) linger; }
|
// lib.optionalAttrs (spec ? linger) { inherit (spec) linger; }
|
||||||
) hostUsers;
|
) hostUsers;
|
||||||
|
|
||||||
programs.firefox = lib.mkIf (config.features.swayDesktop.enable == true) {
|
programs.firefox = lib.mkIf config.features.swayDesktop.enable {
|
||||||
enable = true;
|
enable = true;
|
||||||
};
|
};
|
||||||
programs.thunderbird = lib.mkIf (config.features.swayDesktop.enable == true) {
|
programs.thunderbird = lib.mkIf config.features.swayDesktop.enable {
|
||||||
enable = true;
|
enable = true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,8 +46,45 @@
|
|||||||
pkgs.tflint # Terraform linter (catches what terraformls won't)
|
pkgs.tflint # Terraform linter (catches what terraformls won't)
|
||||||
pkgs.terraform-docs # generate Terraform module docs
|
pkgs.terraform-docs # generate Terraform module docs
|
||||||
pkgs.yq-go # jq for YAML
|
pkgs.yq-go # jq for YAML
|
||||||
|
pkgs.gcx # Grafana Cloud CLI (dashboards, SLOs, synthetics, alerts)
|
||||||
|
|
||||||
|
# WSL ships no xdg-open, so anything that shells out to a browser dies with
|
||||||
|
# `exec: "xdg-open,x-www-browser,www-browser": executable file not found`.
|
||||||
|
# kubelogin's interactive login is the one that bites: it is the login mode
|
||||||
|
# the shared cluster kubeconfig uses. Hand the URL to Windows instead.
|
||||||
|
# (wslu, the usual answer, is gone from nixpkgs -- upstream archived it.)
|
||||||
|
(pkgs.writeShellScriptBin "xdg-open" ''
|
||||||
|
url="$1"
|
||||||
|
if command -v powershell.exe >/dev/null 2>&1; then
|
||||||
|
exec powershell.exe -NoProfile -Command "Start-Process '$url'"
|
||||||
|
fi
|
||||||
|
exec explorer.exe "$url"
|
||||||
|
'')
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# Honoured by tools that read $BROWSER rather than calling xdg-open.
|
||||||
|
home.sessionVariables.BROWSER = "xdg-open";
|
||||||
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
|
||||||
|
# plaintext fallback, so this WSL box needs something owning
|
||||||
|
# org.freedesktop.secrets. See home/secret-service.nix for why
|
||||||
|
# home-manager's services.gnome-keyring cannot be used on a headless host,
|
||||||
|
# and for the security trade-off of an auto-unlocked keyring.
|
||||||
|
services.headlessSecretService.enable = true;
|
||||||
home.shellAliases = {
|
home.shellAliases = {
|
||||||
docker = "/run/current-system/sw/bin/docker";
|
docker = "/run/current-system/sw/bin/docker";
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user