Compare commits
126 Commits
eb54088230
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 9ad8567bdf | |||
| bcabfd49bb | |||
| eef7203621 | |||
| 6064a5a1a7 | |||
| df7747f876 | |||
| 1e0485efde | |||
| fce75e9f4c | |||
| e6e280cc73 | |||
| 44245d16a2 | |||
| 123032aff9 | |||
| 94b0b33338 | |||
| d84b35c5ce | |||
| 6f3801621f | |||
| 1e49af53e7 | |||
| efa9aa93da | |||
| 277dfa4251 | |||
| 3470751c3e | |||
| b56641aaee | |||
| 108f7b9528 | |||
| 1cb8371775 | |||
| 2fc39a5f15 | |||
| 5f4fd8d74e | |||
| d8c4f6bb0b | |||
| 8c3b101a14 | |||
| 2b69485107 | |||
| 886ac4eb36 | |||
| ffedf769a0 | |||
| eec713e886 | |||
| e995283363 | |||
| a753355c0f | |||
| e125296015 | |||
| e0b3eb2393 | |||
| 35c3b08862 | |||
| 6730efa3ce | |||
| fc459ddb1b | |||
| 052b95c00e | |||
| 783754bda2 | |||
| dc08522bab | |||
| a40558d35e | |||
| 18c1e10f13 | |||
| 0c6d6ac167 | |||
| ee319d2d3e | |||
| a97b433a7b | |||
| 184a09ad71 | |||
| 6ee8852c3b | |||
| 3e5a0958ab | |||
| 972b8f4c60 | |||
| 89850b37ce | |||
| 8c058632ef | |||
| 318c64a371 | |||
| 5dd14a8e68 | |||
| ef0fc9a5c5 | |||
| 2836ea1150 | |||
| d172157101 | |||
| 93571386bd | |||
| bdfc27cf93 | |||
| 9a095abd5c | |||
| c7f2f5503b | |||
| fa6f747467 | |||
| 55bce14bf3 | |||
| b8f09ed9ea | |||
| 88a23937ba | |||
| 63ca392537 | |||
| f41879710c | |||
| 6a0d3680fd | |||
| f029c1cf67 | |||
| 2bdca1c469 | |||
| 4ca136f2b4 | |||
| af3cfe4b9a | |||
| af8ee1609b | |||
| 26807cdb55 | |||
| 2013bffcb1 | |||
| 11a08c8b98 | |||
| 8284a03f57 | |||
| 14ec441479 | |||
| 2b3725e0fb | |||
| 860d4ccaa9 | |||
| 27fc7ae6d3 | |||
| 327c363232 | |||
| 8001d89c58 | |||
| 52e5a0ba5c | |||
| 8e57c37ac0 | |||
| 5f4b16d64e | |||
| b11e99d850 | |||
| 9d0e69f916 | |||
| 3dc756e8f2 | |||
| 9bc67eb829 | |||
| 745188c3cf | |||
| 416fbcae52 | |||
| e78e52e18d | |||
| e7e11c17b0 | |||
| b04391809e | |||
| 2cbcc3d7f1 | |||
| 77e14968ee | |||
| cee083fbfa | |||
| 4858de0a07 | |||
| 00d314411d | |||
| ab48a14ec0 | |||
| 91e3ccb85b | |||
| cf384c6050 | |||
| eb1704764f | |||
| 69ba65bde3 | |||
| c61f94715f | |||
| 578e045a53 | |||
| 2b025e62e2 | |||
| 44164df8f7 | |||
| 30cc9c9030 | |||
| 66b8e72e33 | |||
| b01fc13234 | |||
| b3fa34f431 | |||
| ebff5aeba6 | |||
| dfc436802d | |||
| 7436d64447 | |||
| 333cb21152 | |||
| 0227f9d3ef | |||
| 85139ddfb1 | |||
| f42b134ab1 | |||
| de702d5ceb | |||
| 9a17994c30 | |||
| 1d79ba175a | |||
| 11cac1aeac | |||
| 94fc253f68 | |||
| dddf97f3ad | |||
| 1701e6e90a | |||
| f84bcdb339 | |||
| d72aa2080e |
@@ -0,0 +1,19 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.{nix,yaml,yml,json,md,sh,toml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
# Markdown uses trailing whitespace for hard line breaks.
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
+73
-16
@@ -1,45 +1,102 @@
|
|||||||
# Flake CI: formatting gate + evaluation of every host configuration.
|
# Flake CI: full `nix flake check` (formatting + deadnix + statix + pre-commit)
|
||||||
|
# plus an explicit per-host evaluation pass for granular output.
|
||||||
name: CI
|
name: CI
|
||||||
|
|
||||||
|
# Deliberately no `paths:` filter. This job is a required status check on main,
|
||||||
|
# and a path-filtered workflow is *skipped* (never runs) for PRs that touch no
|
||||||
|
# matching file -- which leaves the required check pending forever and blocks the
|
||||||
|
# merge (e.g. a .renovaterc.json-only change). So the workflow always runs and
|
||||||
|
# always reports. To avoid burning a full Nix evaluation on changes that can't
|
||||||
|
# affect it, the "detect" step below diffs the PR and the heavy steps run only
|
||||||
|
# when a .nix file, flake.lock, or this workflow changed; otherwise they skip and
|
||||||
|
# the job still passes. The required check is therefore always green-reportable.
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths:
|
|
||||||
- "**.nix"
|
|
||||||
- "flake.lock"
|
|
||||||
- ".gitea/workflows/ci.yaml"
|
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
|
||||||
- "**.nix"
|
|
||||||
- "flake.lock"
|
|
||||||
- ".gitea/workflows/ci.yaml"
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
flake:
|
flake:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||||
|
with:
|
||||||
|
# Full history so the detect step can diff the PR against its base.
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
# Decide whether the Nix steps need to run. On a pull_request, diff the PR
|
||||||
|
# against its base and look for files that can affect the flake: any .nix,
|
||||||
|
# the lockfile, or this workflow. On any other event (push to main) always
|
||||||
|
# run. The job itself always succeeds, so the required status check is
|
||||||
|
# reported even when the heavy steps are skipped.
|
||||||
|
- name: Detect Nix-relevant changes
|
||||||
|
id: detect
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
if [ "${{ github.event_name }}" != "pull_request" ]; then
|
||||||
|
echo "Event ${{ github.event_name }}: running full checks."
|
||||||
|
echo "run=true" >> "$GITHUB_OUTPUT"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
base='${{ github.event.pull_request.base.sha }}'
|
||||||
|
head='${{ github.event.pull_request.head.sha }}'
|
||||||
|
changed=$(git diff --name-only "$base...$head")
|
||||||
|
echo "Changed files:"
|
||||||
|
echo "$changed"
|
||||||
|
if echo "$changed" | grep -Eq '(\.nix$|^flake\.lock$|^\.gitea/workflows/ci\.yaml$)'; then
|
||||||
|
echo "Nix-relevant changes found: running checks."
|
||||||
|
echo "run=true" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "No Nix-relevant changes: skipping checks (job still passes)."
|
||||||
|
echo "run=false" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
|
if: steps.detect.outputs.run == 'true'
|
||||||
uses: cachix/install-nix-action@8aa03977d8d733052d78f4e008a241fd1dbf36b3 # v31
|
uses: cachix/install-nix-action@8aa03977d8d733052d78f4e008a241fd1dbf36b3 # v31
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
experimental-features = nix-command flakes
|
experimental-features = nix-command flakes
|
||||||
accept-flake-config = true
|
accept-flake-config = true
|
||||||
|
substituters = https://cache.nixos.org https://nix-community.cachix.org
|
||||||
|
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=
|
||||||
|
|
||||||
- name: Check formatting
|
# Runs every flake check: treefmt formatting, deadnix, statix, and the
|
||||||
run: nix build --print-build-logs '.#checks.x86_64-linux.formatting'
|
# pre-commit hooks (so a --no-verify commit can't ship unlinted).
|
||||||
|
- name: Flake check
|
||||||
|
if: steps.detect.outputs.run == 'true'
|
||||||
|
run: nix flake check --print-build-logs
|
||||||
|
|
||||||
# Evaluate (not build) each host's toplevel so eval errors fail CI cheaply.
|
# Evaluate (not build) each host's toplevel so eval errors fail CI cheaply.
|
||||||
# aarch64 hosts evaluate fine on an x86_64 runner; only building would need
|
# aarch64 / darwin hosts evaluate fine on an x86_64 runner; only building
|
||||||
# emulation, which we deliberately avoid here.
|
# would need emulation, which we deliberately avoid here.
|
||||||
- name: Evaluate host configurations
|
#
|
||||||
|
# Host lists are discovered from the flake (attrNames of
|
||||||
|
# nixos/darwinConfigurations) rather than hard-coded, so adding or removing
|
||||||
|
# a host needs no change to this workflow.
|
||||||
|
- name: Evaluate NixOS host configurations
|
||||||
|
if: steps.detect.outputs.run == 'true'
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
for host in lyrathorpe-mbp lyrathorpe-x1c emmathorpe-edaas; do
|
hosts=$(nix eval --raw '.#nixosConfigurations' \
|
||||||
|
--apply 'cfgs: builtins.concatStringsSep "\n" (builtins.attrNames cfgs)')
|
||||||
|
for host in $hosts; do
|
||||||
echo "::group::eval $host"
|
echo "::group::eval $host"
|
||||||
nix eval --raw ".#nixosConfigurations.$host.config.system.build.toplevel.drvPath"
|
nix eval --raw ".#nixosConfigurations.$host.config.system.build.toplevel.drvPath"
|
||||||
echo
|
echo
|
||||||
echo "::endgroup::"
|
echo "::endgroup::"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
- name: Evaluate Darwin host configurations
|
||||||
|
if: steps.detect.outputs.run == 'true'
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
hosts=$(nix eval --raw '.#darwinConfigurations' \
|
||||||
|
--apply 'cfgs: builtins.concatStringsSep "\n" (builtins.attrNames cfgs)')
|
||||||
|
for host in $hosts; do
|
||||||
|
echo "::group::eval $host"
|
||||||
|
nix eval --raw ".#darwinConfigurations.$host.config.system.build.toplevel.drvPath"
|
||||||
|
echo
|
||||||
|
echo "::endgroup::"
|
||||||
|
done
|
||||||
|
|||||||
+3
-6
@@ -1,16 +1,13 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
"extends": [
|
"extends": ["config:recommended", ":dependencyDashboard", ":semanticCommits"],
|
||||||
"config:recommended",
|
|
||||||
":dependencyDashboard",
|
|
||||||
":semanticCommits"
|
|
||||||
],
|
|
||||||
"nix": {
|
"nix": {
|
||||||
"enabled": true
|
"enabled": true
|
||||||
},
|
},
|
||||||
"lockFileMaintenance": {
|
"lockFileMaintenance": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"schedule": ["before 6am on monday"]
|
"schedule": ["before 6am on monday"],
|
||||||
|
"automerge": true
|
||||||
},
|
},
|
||||||
"git-submodules": {
|
"git-submodules": {
|
||||||
"enabled": false
|
"enabled": false
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
# nixfiles
|
||||||
|
|
||||||
|
NixOS / nix-darwin / home-manager configuration for all hosts, built from a
|
||||||
|
single flake.
|
||||||
|
|
||||||
|
## Hosts
|
||||||
|
|
||||||
|
Defined in the host table in [`flake.nix`](./flake.nix):
|
||||||
|
|
||||||
|
| Configuration | System | Machine |
|
||||||
|
| --------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| `lyrathorpe-mbp` | `aarch64-linux` | MacBook Pro (Apple Silicon, Asahi) |
|
||||||
|
| `lyrathorpe-t400` | `x86_64-linux` | ThinkPad T400 — [install notes](./system/machine/T400/README.md) |
|
||||||
|
| `lyrathorpe-macpro31` | `x86_64-linux` | Mac Pro 3,1, desktop — [install notes](./system/machine/MacPro31/README.md) |
|
||||||
|
| `emmathorpe-edaas` | `x86_64-linux` | Work WSL box (NixOS-WSL) |
|
||||||
|
| `lyrathorpe-rpi5` | `aarch64-linux` | Raspberry Pi 5 headless server: Docker host + nginx reverse proxy — [install notes](./system/machine/RPi5/README.md) |
|
||||||
|
| `lyrathorpe-mac` | `aarch64-darwin` | macOS (nix-darwin) |
|
||||||
|
|
||||||
|
Shared layers: `lyrathorpe/home` (home-manager: shell, git, editor),
|
||||||
|
`system/modules/common-nixos.nix` (all NixOS hosts: fonts, nix-ld, caches),
|
||||||
|
`system/modules/workstation.nix` (physical graphical hosts: audio, thermald,
|
||||||
|
earlyoom, fwupd), `system/modules/laptop.nix` (laptops: Wi-Fi, Bluetooth, power,
|
||||||
|
lid), and `system/modules/ssh.nix` (key-only sshd). The x86 hosts also pull
|
||||||
|
`nixos-hardware` profiles.
|
||||||
|
|
||||||
|
## Applying
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# NixOS
|
||||||
|
sudo nixos-rebuild switch --flake .#<configuration>
|
||||||
|
# Darwin
|
||||||
|
darwin-rebuild switch --flake .#lyrathorpe-mac
|
||||||
|
```
|
||||||
|
|
||||||
|
## Shell environment & keybindings
|
||||||
|
|
||||||
|
- Interactive shell features (zsh, tmux, git, ssh, CLI tools, auto-tmux):
|
||||||
|
[`lyrathorpe/home/README.md`](./lyrathorpe/home/README.md).
|
||||||
|
- All Sway / tmux / foot / zsh keyboard shortcuts:
|
||||||
|
[`lyrathorpe/home/KEYBINDINGS.md`](./lyrathorpe/home/KEYBINDINGS.md).
|
||||||
|
|
||||||
|
## Login / greeter
|
||||||
|
|
||||||
|
Graphical (Sway) hosts log in through a Wayland greeter — `greetd` running
|
||||||
|
ReGreet inside the `cage` kiosk compositor — implemented in
|
||||||
|
[`lyrathorpe/swaywm.nix`](./lyrathorpe/swaywm.nix), gated on
|
||||||
|
`features.swayDesktop.enable` (the option is declared in
|
||||||
|
[`system/modules/features.nix`](./system/modules/features.nix), so headless hosts
|
||||||
|
can leave it off without importing `swaywm.nix`). The greeter is forced to Dvorak
|
||||||
|
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
|
||||||
|
(`passwd <user>`) before it can log in.
|
||||||
|
|
||||||
|
## MacBook (Asahi) firmware
|
||||||
|
|
||||||
|
The MBP host references `system/modules/firmware/` for Apple peripheral
|
||||||
|
firmware (Wi-Fi/Bluetooth). These blobs are **committed** (tracked) even though
|
||||||
|
`.gitignore` lists the directory: the flake is `git+file`, so it only sees
|
||||||
|
tracked files — untracking them breaks `lyrathorpe-mbp` evaluation (and the CI
|
||||||
|
host-eval) because the config can't find the firmware. They are not
|
||||||
|
redistributable; the repo is private.
|
||||||
|
|
||||||
|
To refresh them, copy the firmware extracted during the Asahi install (from
|
||||||
|
`/etc/nixos/firmware`, or re-extract per the
|
||||||
|
[Asahi NixOS docs](https://github.com/tpwrules/nixos-apple-silicon)) into
|
||||||
|
`system/modules/firmware/` and commit with `git add -f`.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
A dev shell and a formatting/lint gate are wired through the flake:
|
||||||
|
|
||||||
|
- `nix develop` — shell with `deadnix`, `statix`, `treefmt`, and the git
|
||||||
|
`pre-commit` hooks (installed automatically on first entry).
|
||||||
|
- `nix fmt` — formats the tree via `treefmt` (nixfmt + shfmt + prettier;
|
||||||
|
generated files and `flake.lock` are excluded).
|
||||||
|
- `nix flake check` — runs formatting, `deadnix`, `statix`, the pre-commit
|
||||||
|
hooks, and evaluates every host. `.editorconfig` carries the base style;
|
||||||
|
`statix.toml` disables the two house-style lints (`repeated_keys`,
|
||||||
|
`empty_pattern`).
|
||||||
|
|
||||||
|
## CI
|
||||||
|
|
||||||
|
[`.gitea/workflows/ci.yaml`](./.gitea/workflows/ci.yaml) runs `nix flake check`
|
||||||
|
(formatting, `deadnix`, `statix`, the pre-commit hooks) and evaluates every
|
||||||
|
NixOS and Darwin host configuration on push/PR.
|
||||||
Generated
+242
-35
@@ -3,21 +3,59 @@
|
|||||||
"brew-src": {
|
"brew-src": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1778427648,
|
"lastModified": 1781226006,
|
||||||
"narHash": "sha256-pt9KaDGsMyYWB9JeHs4XGHs870f1lOZe3vx9LpVIhUE=",
|
"narHash": "sha256-w4ZTuOnhYiDxjaynrMTASzp802QblBWmo3wpB8wVN4Y=",
|
||||||
"owner": "Homebrew",
|
"owner": "Homebrew",
|
||||||
"repo": "brew",
|
"repo": "brew",
|
||||||
"rev": "6f293daa9f9f5832e13b497976335e90509886d7",
|
"rev": "109191be4988470b51a60a5ef1998520aa24c01b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "Homebrew",
|
"owner": "Homebrew",
|
||||||
"ref": "5.1.11",
|
"ref": "6.0.1",
|
||||||
"repo": "brew",
|
"repo": "brew",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"firefox-addons": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"dir": "pkgs/firefox-addons",
|
||||||
|
"lastModified": 1782014564,
|
||||||
|
"narHash": "sha256-F/royQHyJAyKWKrV8AaG4Yf1yjzxa+PFk5xvTdvBrzk=",
|
||||||
|
"owner": "rycee",
|
||||||
|
"repo": "nur-expressions",
|
||||||
|
"rev": "d6668e34bbce788459883a1097bf0ee170f49c61",
|
||||||
|
"type": "gitlab"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"dir": "pkgs/firefox-addons",
|
||||||
|
"owner": "rycee",
|
||||||
|
"repo": "nur-expressions",
|
||||||
|
"type": "gitlab"
|
||||||
|
}
|
||||||
|
},
|
||||||
"flake-compat": {
|
"flake-compat": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1767039857,
|
||||||
|
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "flake-compat",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-compat_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1761640442,
|
"lastModified": 1761640442,
|
||||||
"narHash": "sha256-AtrEP6Jmdvrqiv4x2xa5mrtaIp3OEe8uBYCDZDS+hu8=",
|
"narHash": "sha256-AtrEP6Jmdvrqiv4x2xa5mrtaIp3OEe8uBYCDZDS+hu8=",
|
||||||
@@ -32,7 +70,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-compat_2": {
|
"flake-compat_3": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1767039857,
|
"lastModified": 1767039857,
|
||||||
@@ -68,6 +106,70 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flake-parts_2": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": [
|
||||||
|
"nixvim",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1778716662,
|
||||||
|
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"git-hooks": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": "flake-compat",
|
||||||
|
"gitignore": "gitignore",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1781733627,
|
||||||
|
"narHash": "sha256-U3yTuGBnmXvXoQI3qkpfEDsn9RovQPAjN7ndRco+3u0=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "git-hooks.nix",
|
||||||
|
"rev": "3bbec39bc90eadfa031e6f3b77272f3f60803e39",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "git-hooks.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gitignore": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"git-hooks",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1709087332,
|
||||||
|
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "gitignore.nix",
|
||||||
|
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "gitignore.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"home-manager": {
|
"home-manager": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -75,11 +177,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1780361225,
|
"lastModified": 1781981105,
|
||||||
"narHash": "sha256-wnV9ttf4fPWNonBIQmvlrSlNpQYgx5HgWWd007mwIFA=",
|
"narHash": "sha256-/1nNBbA7PrSQpTc9Qazkhl4kIPg+TNl0CjxS3UQJKlw=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "e28654b71096e08c019d4861ca26acb646f583d8",
|
"rev": "7bfff44b465909f69a442701293bc0badcf476dc",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -96,11 +198,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1779036909,
|
"lastModified": 1781772065,
|
||||||
"narHash": "sha256-zXcwYQGCT6pzinK+1dBB2ekTVtfxGZAapb3Evdcu4fY=",
|
"narHash": "sha256-xIbRSwDB1GBAUsWsQZUjudGfAGQt3BOpsWaO/ugVa4w=",
|
||||||
"owner": "nix-darwin",
|
"owner": "nix-darwin",
|
||||||
"repo": "nix-darwin",
|
"repo": "nix-darwin",
|
||||||
"rev": "56c666e108467d87d13508936aade6d567f2a501",
|
"rev": "adda04f0bf4819575b1978c2f8d78401b3c2be12",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -115,11 +217,11 @@
|
|||||||
"brew-src": "brew-src"
|
"brew-src": "brew-src"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1778851564,
|
"lastModified": 1781389246,
|
||||||
"narHash": "sha256-p8wzcnpB2Iys+QzAKM9/Eyw/pUyqCO3sw/NCnDH4dTE=",
|
"narHash": "sha256-ORqLAo/hoJdsZC7UPAuEHev6S0+XIqKEC7vjo5prz1k=",
|
||||||
"owner": "zhaofengli",
|
"owner": "zhaofengli",
|
||||||
"repo": "nix-homebrew",
|
"repo": "nix-homebrew",
|
||||||
"rev": "b3a87b4793205cc111f3c61e25e018ffac3b8039",
|
"rev": "de7953a08ed4bb9245be043e468561c17b89130d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -128,28 +230,27 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixos-apple-silicon": {
|
"nix-index-database": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat",
|
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1780294707,
|
"lastModified": 1782030356,
|
||||||
"narHash": "sha256-KZiF/wah9A9N+Pn6t8mScqF0fxLVs+n53/6/IsuqcHM=",
|
"narHash": "sha256-h4WpMr455AfRub0FXBaon6Vcpe0waUyJ4GivIW6oyd4=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nixos-apple-silicon",
|
"repo": "nix-index-database",
|
||||||
"rev": "7f4b33118d9d2db87b5ce1ad5152bb727a63e340",
|
"rev": "3017088b49efd404f78e3b104f553b97e4af786b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nixos-apple-silicon",
|
"repo": "nix-index-database",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixos-wsl": {
|
"nixos-apple-silicon": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat_2",
|
"flake-compat": "flake-compat_2",
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -157,11 +258,52 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1780169171,
|
"lastModified": 1781520503,
|
||||||
"narHash": "sha256-3HBYDfBgZ+ph52HS6Ks/bMMwuh2uONIT72sZ1CtLE/s=",
|
"narHash": "sha256-XuqQQG1qRyc3o8ld937sDLQNx+QrGV852KJ0dNglJDg=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixos-apple-silicon",
|
||||||
|
"rev": "43043ad207529650f9fa68e1705f7cf9c08bfdeb",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixos-apple-silicon",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixos-hardware": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1781622756,
|
||||||
|
"narHash": "sha256-JrPh4M6S7aPsEE9tOENuZrxC6o2szSLlK+t4+nLke9s=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixos-hardware",
|
||||||
|
"rev": "08018c72174a4df5657f8d94178ac69fb9c243e5",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixos-hardware",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixos-wsl": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": "flake-compat_3",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1781182279,
|
||||||
|
"narHash": "sha256-V5EQQbDnmdiXGQXrEF1PEL7QYsFqfH8N1E89Z5ONwFk=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "NixOS-WSL",
|
"repo": "NixOS-WSL",
|
||||||
"rev": "998b2821c30b2938637230916904ceb8757c79e8",
|
"rev": "5675822ba756e6e56f8f6a5a76e90e0da2ece94d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -172,26 +314,27 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1780203844,
|
"lastModified": 1781216227,
|
||||||
"narHash": "sha256-K5sT4jTpGs15ADhviMKNBH38REpPf5Q6mM1+N6cArVE=",
|
"narHash": "sha256-9mUW6gNwoN2SWc/l0fW4svPNOulXLl8ijqKyeSOGgJE=",
|
||||||
"owner": "NixOS",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "b51242d7d43689db2f3be91bd05d5b24fbb469c4",
|
"rev": "a0374025a863d007d98e3297f6aa46cc3141c2f0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"id": "nixpkgs",
|
"owner": "nixos",
|
||||||
"ref": "nixos-26.05",
|
"ref": "nixos-26.05",
|
||||||
"type": "indirect"
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs-unstable": {
|
"nixpkgs-unstable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1780243769,
|
"lastModified": 1781577229,
|
||||||
"narHash": "sha256-x5UQuRsH3MqI0U9afaXSNqzTPSeZlRLvFAav2Ux1pNw=",
|
"narHash": "sha256-lrp67w8AulE9Ks53n27I45ADSzbOCn4H+CNW1Ck8B+8=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "331800de5053fcebacf6813adb5db9c9dca22a0c",
|
"rev": "567a49d1913ce81ac6e9582e3553dd90a955875f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -201,16 +344,80 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nixvim": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-parts": "flake-parts_2",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1781971008,
|
||||||
|
"narHash": "sha256-T2u2RQZWKvD1J+TgcxjiJr8IymBr/PrUNeAGhMZFZU4=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixvim",
|
||||||
|
"rev": "7afca458f064f166d3a9c98db3b41a984fe46492",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"ref": "nixos-26.05",
|
||||||
|
"repo": "nixvim",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
|
"firefox-addons": "firefox-addons",
|
||||||
"flake-parts": "flake-parts",
|
"flake-parts": "flake-parts",
|
||||||
|
"git-hooks": "git-hooks",
|
||||||
"home-manager": "home-manager",
|
"home-manager": "home-manager",
|
||||||
"nix-darwin": "nix-darwin",
|
"nix-darwin": "nix-darwin",
|
||||||
"nix-homebrew": "nix-homebrew",
|
"nix-homebrew": "nix-homebrew",
|
||||||
|
"nix-index-database": "nix-index-database",
|
||||||
"nixos-apple-silicon": "nixos-apple-silicon",
|
"nixos-apple-silicon": "nixos-apple-silicon",
|
||||||
|
"nixos-hardware": "nixos-hardware",
|
||||||
"nixos-wsl": "nixos-wsl",
|
"nixos-wsl": "nixos-wsl",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"nixpkgs-unstable": "nixpkgs-unstable"
|
"nixpkgs-unstable": "nixpkgs-unstable",
|
||||||
|
"nixvim": "nixvim",
|
||||||
|
"treefmt-nix": "treefmt-nix"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"treefmt-nix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1780220602,
|
||||||
|
"narHash": "sha256-eynAfOmbmxJnkp7YewvCEbShNnnYJ9gLLqkzsYtBPeM=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"rev": "db947814a175b7ca6ded66e21383d938df01c227",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "treefmt-nix",
|
||||||
|
"type": "github"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
# Pinned stable channel; the single source of truth for every host.
|
# Pinned stable channel; the single source of truth for every host.
|
||||||
nixpkgs.url = "nixpkgs/nixos-26.05";
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-26.05";
|
||||||
# Bleeding-edge channel, used only to pull individual packages via overlay.
|
# Bleeding-edge channel, used only to pull individual packages via overlay.
|
||||||
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
|
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
# Home-manager release matched to the stable nixpkgs; `follows` keeps a single nixpkgs eval.
|
# Home-manager release matched to the stable nixpkgs; `follows` keeps a single nixpkgs eval.
|
||||||
@@ -23,6 +23,43 @@
|
|||||||
# Provides mkFlake: the systems/perSystem scaffolding used below.
|
# Provides mkFlake: the systems/perSystem scaffolding used below.
|
||||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||||
flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs";
|
flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs";
|
||||||
|
# Declarative Firefox add-ons (e.g. the Catppuccin theme); see lyrathorpe/user.nix.
|
||||||
|
firefox-addons = {
|
||||||
|
url = "gitlab:rycee/nur-expressions?dir=pkgs/firefox-addons";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
# Prebuilt nix-index database so "command not found -> which package
|
||||||
|
# provides it" works immediately (no manual `nix-index` run). See shell.nix.
|
||||||
|
nix-index-database = {
|
||||||
|
url = "github:nix-community/nix-index-database";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
# treefmt-nix: one multi-language formatter driving `nix fmt` and the
|
||||||
|
# formatting flake check (nixfmt + shfmt + prettier).
|
||||||
|
treefmt-nix = {
|
||||||
|
url = "github:numtide/treefmt-nix";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
# git-hooks.nix: declarative pre-commit hooks (nixfmt/deadnix/statix),
|
||||||
|
# installed into the repo via the devShell.
|
||||||
|
git-hooks = {
|
||||||
|
url = "github:cachix/git-hooks.nix";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
# Declarative Neovim (the editor; see lyrathorpe/home/editor.nix). Release
|
||||||
|
# branch matched to the pinned nixpkgs (26.05); follows our nixpkgs to keep a
|
||||||
|
# single nixpkgs in the closure. editor.nix sets programs.nixvim.nixpkgs.source
|
||||||
|
# to this same input so the home module doesn't warn about the pin.
|
||||||
|
nixvim = {
|
||||||
|
url = "github:nix-community/nixvim/nixos-26.05";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
# Curated per-hardware profiles (microcode, SSD, platform quirks) for the
|
||||||
|
# physical x86 hosts.
|
||||||
|
nixos-hardware = {
|
||||||
|
url = "github:NixOS/nixos-hardware";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs =
|
outputs =
|
||||||
@@ -42,12 +79,14 @@
|
|||||||
let
|
let
|
||||||
# claude-code tracks nixpkgs-unstable regardless of the pinned nixpkgs.
|
# claude-code tracks nixpkgs-unstable regardless of the pinned nixpkgs.
|
||||||
overlays = [
|
overlays = [
|
||||||
(final: prev: {
|
(_final: prev: {
|
||||||
claude-code =
|
inherit
|
||||||
(import nixpkgs-unstable {
|
(import nixpkgs-unstable {
|
||||||
inherit (prev.stdenv.hostPlatform) system;
|
inherit (prev.stdenv.hostPlatform) system;
|
||||||
config.allowUnfree = true;
|
config.allowUnfree = true;
|
||||||
}).claude-code;
|
})
|
||||||
|
claude-code
|
||||||
|
;
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -74,6 +113,8 @@
|
|||||||
# Shared scaffolding for every NixOS host: common user, settings, home-manager.
|
# Shared scaffolding for every NixOS host: common user, settings, home-manager.
|
||||||
baseModules = [
|
baseModules = [
|
||||||
./lyrathorpe/user.nix
|
./lyrathorpe/user.nix
|
||||||
|
./system/modules/common-nixos.nix
|
||||||
|
./system/modules/features.nix
|
||||||
commonModule
|
commonModule
|
||||||
home-manager.nixosModules.home-manager
|
home-manager.nixosModules.home-manager
|
||||||
{
|
{
|
||||||
@@ -97,16 +138,34 @@
|
|||||||
fullName,
|
fullName,
|
||||||
modules,
|
modules,
|
||||||
homeModules,
|
homeModules,
|
||||||
|
# Host form factor. Laptops inherit the default; a desktop host sets
|
||||||
|
# `portable = false` to drop mobile components (battery block,
|
||||||
|
# brightness keys) from the home-manager Sway config.
|
||||||
|
portable ? true,
|
||||||
}:
|
}:
|
||||||
nixpkgs.lib.nixosSystem {
|
nixpkgs.lib.nixosSystem {
|
||||||
inherit system;
|
inherit system;
|
||||||
specialArgs = { inherit inputs username fullName; };
|
specialArgs = {
|
||||||
|
inherit
|
||||||
|
inputs
|
||||||
|
username
|
||||||
|
fullName
|
||||||
|
portable
|
||||||
|
;
|
||||||
|
};
|
||||||
modules =
|
modules =
|
||||||
baseModules
|
baseModules
|
||||||
++ modules
|
++ modules
|
||||||
++ [
|
++ [
|
||||||
{
|
{
|
||||||
home-manager.extraSpecialArgs = { inherit inputs username fullName; };
|
home-manager.extraSpecialArgs = {
|
||||||
|
inherit
|
||||||
|
inputs
|
||||||
|
username
|
||||||
|
fullName
|
||||||
|
portable
|
||||||
|
;
|
||||||
|
};
|
||||||
home-manager.users.${username}.imports = homeModules;
|
home-manager.users.${username}.imports = homeModules;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -163,6 +222,7 @@
|
|||||||
fullName = "Lyra Thorpe";
|
fullName = "Lyra Thorpe";
|
||||||
modules = [
|
modules = [
|
||||||
./system/machine/MBP-Asahi/configuration.nix
|
./system/machine/MBP-Asahi/configuration.nix
|
||||||
|
./system/modules/laptop.nix
|
||||||
nixos-apple-silicon.nixosModules.default
|
nixos-apple-silicon.nixosModules.default
|
||||||
./lyrathorpe/swaywm.nix
|
./lyrathorpe/swaywm.nix
|
||||||
];
|
];
|
||||||
@@ -172,12 +232,40 @@
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
lyrathorpe-x1c = {
|
lyrathorpe-t400 = {
|
||||||
system = "x86_64-linux";
|
system = "x86_64-linux";
|
||||||
username = "lyrathorpe";
|
username = "lyrathorpe";
|
||||||
fullName = "Lyra Thorpe";
|
fullName = "Lyra Thorpe";
|
||||||
modules = [
|
modules = [
|
||||||
./system/machine/X1/configuration.nix
|
./system/machine/T400/configuration.nix
|
||||||
|
./system/modules/laptop.nix
|
||||||
|
./system/modules/ssh.nix
|
||||||
|
# No t400-specific profile exists; compose the generic ThinkPad +
|
||||||
|
# laptop/SSD/Intel building blocks (tp_smapi/acpi_call for battery
|
||||||
|
# thresholds, SSD + microcode defaults).
|
||||||
|
inputs.nixos-hardware.nixosModules.lenovo-thinkpad
|
||||||
|
inputs.nixos-hardware.nixosModules.common-pc-laptop
|
||||||
|
inputs.nixos-hardware.nixosModules.common-pc-laptop-ssd
|
||||||
|
inputs.nixos-hardware.nixosModules.common-cpu-intel
|
||||||
|
./lyrathorpe/swaywm.nix
|
||||||
|
];
|
||||||
|
homeModules = [
|
||||||
|
./lyrathorpe/home
|
||||||
|
./lyrathorpe/home/desktop.nix
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
lyrathorpe-macpro31 = {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
username = "lyrathorpe";
|
||||||
|
fullName = "Lyra Thorpe";
|
||||||
|
portable = false;
|
||||||
|
modules = [
|
||||||
|
./system/machine/MacPro31/configuration.nix
|
||||||
|
./system/modules/desktop.nix
|
||||||
|
./system/modules/ssh.nix
|
||||||
|
inputs.nixos-hardware.nixosModules.common-pc-ssd
|
||||||
|
inputs.nixos-hardware.nixosModules.common-cpu-intel
|
||||||
./lyrathorpe/swaywm.nix
|
./lyrathorpe/swaywm.nix
|
||||||
];
|
];
|
||||||
homeModules = [
|
homeModules = [
|
||||||
@@ -197,9 +285,25 @@
|
|||||||
];
|
];
|
||||||
homeModules = [
|
homeModules = [
|
||||||
./lyrathorpe/home
|
./lyrathorpe/home
|
||||||
./system/modules/work/default.nix
|
./lyrathorpe/home/work.nix
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
lyrathorpe-rpi5 = {
|
||||||
|
system = "aarch64-linux";
|
||||||
|
username = "lyrathorpe";
|
||||||
|
fullName = "Lyra Thorpe";
|
||||||
|
portable = false;
|
||||||
|
# Headless server: Docker host + nginx reverse proxy. No swaywm.nix
|
||||||
|
# (no desktop); the raspberry-pi-5 profile supplies kernel/firmware,
|
||||||
|
# ssh.nix adds key-only sshd.
|
||||||
|
modules = [
|
||||||
|
./system/machine/RPi5/configuration.nix
|
||||||
|
inputs.nixos-hardware.nixosModules.raspberry-pi-5
|
||||||
|
./system/modules/ssh.nix
|
||||||
|
];
|
||||||
|
homeModules = [ ./lyrathorpe/home ];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# Darwin host table — macOS machines built via mkDarwinHost. The shared
|
# Darwin host table — macOS machines built via mkDarwinHost. The shared
|
||||||
@@ -220,6 +324,13 @@
|
|||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
# flake-parts modules: treefmt-nix wires `nix fmt` + a formatting check;
|
||||||
|
# git-hooks.nix wires the pre-commit check + devShell installation script.
|
||||||
|
imports = [
|
||||||
|
inputs.treefmt-nix.flakeModule
|
||||||
|
inputs.git-hooks.flakeModule
|
||||||
|
];
|
||||||
|
|
||||||
systems = [
|
systems = [
|
||||||
"x86_64-linux"
|
"x86_64-linux"
|
||||||
"aarch64-linux"
|
"aarch64-linux"
|
||||||
@@ -231,26 +342,68 @@
|
|||||||
# nixpkgs instance for that system. Outputs here become per-system
|
# nixpkgs instance for that system. Outputs here become per-system
|
||||||
# attrsets automatically (e.g. devShells.<system>.default).
|
# attrsets automatically (e.g. devShells.<system>.default).
|
||||||
perSystem =
|
perSystem =
|
||||||
{ pkgs, ... }:
|
{ config, pkgs, ... }:
|
||||||
{
|
{
|
||||||
# `nix fmt` formatter for the repo.
|
# treefmt drives `nix fmt` and the formatting check below. nixfmt
|
||||||
formatter = pkgs.nixfmt;
|
# stays the .nix formatter (the tree is already nixfmt-formatted);
|
||||||
|
# shfmt covers shell and prettier covers markdown/yaml/json.
|
||||||
|
treefmt = {
|
||||||
|
projectRootFile = "flake.nix";
|
||||||
|
programs.nixfmt.enable = true;
|
||||||
|
programs.shfmt.enable = true;
|
||||||
|
programs.prettier.enable = true;
|
||||||
|
# Generated hardware-configuration.nix files are not hand-edited.
|
||||||
|
settings.global.excludes = [
|
||||||
|
"*/hardware-configuration.nix" # generated by nixos-generate-config
|
||||||
|
"flake.lock" # generated by `nix flake lock`
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Pre-commit hooks: format + lint gate run on commit. The same hooks
|
||||||
|
# are exposed as a flake check (pre-commit.check.enable defaults true).
|
||||||
|
pre-commit.settings = {
|
||||||
|
# Generated by nixos-generate-config; don't lint/reformat (treefmt
|
||||||
|
# excludes them too).
|
||||||
|
excludes = [ "hardware-configuration\\.nix$" ];
|
||||||
|
hooks = {
|
||||||
|
nixfmt-rfc-style.enable = true;
|
||||||
|
deadnix = {
|
||||||
|
enable = true;
|
||||||
|
# Unused module args ({config,lib,pkgs,...}) are normal; only
|
||||||
|
# flag genuinely dead bindings.
|
||||||
|
settings.noLambdaPatternNames = true;
|
||||||
|
};
|
||||||
|
statix.enable = true; # reads statix.toml (repeated_keys/empty_pattern disabled)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# treefmt-nix exposes its own `checks.treefmt`; alias it to
|
||||||
|
# `formatting` so the existing CI gate (.#checks.*.formatting) keeps
|
||||||
|
# working without churn.
|
||||||
|
checks.formatting = config.treefmt.build.check inputs.self;
|
||||||
|
|
||||||
|
# deadnix / statix lints as standalone flake checks so `nix flake
|
||||||
|
# check` flags dead code and antipatterns independently of pre-commit.
|
||||||
|
checks.deadnix = pkgs.runCommandLocal "check-deadnix" { nativeBuildInputs = [ pkgs.deadnix ]; } ''
|
||||||
|
deadnix --fail --no-lambda-pattern-names ${./.} && touch $out
|
||||||
|
'';
|
||||||
|
checks.statix = pkgs.runCommandLocal "check-statix" { nativeBuildInputs = [ pkgs.statix ]; } ''
|
||||||
|
statix check -c ${./.} ${./.} && touch $out
|
||||||
|
'';
|
||||||
|
|
||||||
# `nix develop` shell with the tooling needed to hack on this flake.
|
# `nix develop` shell with the tooling needed to hack on this flake.
|
||||||
|
# shellHook installs the git pre-commit hooks into the working tree.
|
||||||
devShells.default = pkgs.mkShellNoCC {
|
devShells.default = pkgs.mkShellNoCC {
|
||||||
packages = with pkgs; [
|
packages = with pkgs; [
|
||||||
nixfmt
|
nixfmt
|
||||||
nil
|
nil
|
||||||
git
|
git
|
||||||
|
deadnix
|
||||||
|
statix
|
||||||
|
treefmt
|
||||||
];
|
];
|
||||||
|
shellHook = config.pre-commit.installationScript;
|
||||||
};
|
};
|
||||||
|
|
||||||
checks.formatting =
|
|
||||||
pkgs.runCommandLocal "check-formatting" { nativeBuildInputs = [ pkgs.nixfmt ]; }
|
|
||||||
''
|
|
||||||
# Generated hardware-configuration.nix files are excluded.
|
|
||||||
nixfmt --check $(find ${./.} -name '*.nix' -not -name 'hardware-configuration.nix') && touch $out
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# Realise the host tables: each entry becomes a {nixos,darwin}Configuration.
|
# Realise the host tables: each entry becomes a {nixos,darwin}Configuration.
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Catppuccin Mocha palette. Raw 6-digit hex (no leading "#"); consumers add a
|
||||||
|
# "#" where their format needs it. Shared by the Sway desktop theming
|
||||||
|
# (home/sway.nix) and the ReGreet greeter (swaywm.nix) so the two stay in sync.
|
||||||
|
{
|
||||||
|
base = "1e1e2e";
|
||||||
|
mantle = "181825";
|
||||||
|
crust = "11111b";
|
||||||
|
surface0 = "313244";
|
||||||
|
surface1 = "45475a";
|
||||||
|
surface2 = "585b70";
|
||||||
|
overlay0 = "6c7086";
|
||||||
|
subtext0 = "a6adc8";
|
||||||
|
subtext1 = "bac2de";
|
||||||
|
text = "cdd6f4";
|
||||||
|
rosewater = "f5e0dc";
|
||||||
|
red = "f38ba8";
|
||||||
|
maroon = "eba0ac";
|
||||||
|
peach = "fab387";
|
||||||
|
yellow = "f9e2af";
|
||||||
|
green = "a6e3a1";
|
||||||
|
teal = "94e2d5";
|
||||||
|
sapphire = "74c7ec";
|
||||||
|
blue = "89b4fa";
|
||||||
|
mauve = "cba6f7";
|
||||||
|
pink = "f5c2e7";
|
||||||
|
}
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
# Keybindings reference
|
||||||
|
|
||||||
|
Every keyboard shortcut configured across this desktop, and where it is defined.
|
||||||
|
Everything here is managed declaratively through Nix — edit the listed file and
|
||||||
|
rebuild, never the generated dotfiles.
|
||||||
|
|
||||||
|
| Area | Defined in |
|
||||||
|
| ----------------- | --------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| Sway (compositor) | [`sway.nix`](./sway.nix) `config.keybindings` + `config.modes`, plus the home-manager Sway module's built-in defaults |
|
||||||
|
| tmux | [`shell.nix`](./shell.nix) `programs.tmux` |
|
||||||
|
| zsh line editor | [`shell.nix`](./shell.nix) `programs.zsh.historySubstringSearch` |
|
||||||
|
| Neovim | [`editor.nix`](./editor.nix) `programs.nixvim` |
|
||||||
|
| foot (terminal) | foot package defaults — only colours are themed (in `sway.nix`) |
|
||||||
|
|
||||||
|
**Conventions**
|
||||||
|
|
||||||
|
- **Super** is the `Mod4` / logo (Windows/Command) key; **Alt** is `Mod1`.
|
||||||
|
- Letter keys are **keysyms** (the character produced), not physical positions.
|
||||||
|
The keyboard is **Dvorak** (`us`/`dvorak`), so e.g. "Super+s" is whatever key
|
||||||
|
types `s` in Dvorak.
|
||||||
|
- Shortcuts apply to every Sway host (MBP, T400, Mac Pro); brightness keys are
|
||||||
|
laptop-only, as noted.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sway
|
||||||
|
|
||||||
|
### Applications & session
|
||||||
|
|
||||||
|
| Shortcut | Action |
|
||||||
|
| ------------------- | ------------------------------------------------------- |
|
||||||
|
| `Super`+`Return` | Open a terminal (foot) |
|
||||||
|
| `Super`+`Space` | App launcher (sway-launcher-desktop in a floating foot) |
|
||||||
|
| `Super`+`d` | App launcher (same as above; module default) |
|
||||||
|
| `Super`+`e` | File manager (nemo) |
|
||||||
|
| `Super`+`c` | Clipboard history picker (clipman → fuzzel) |
|
||||||
|
| `Super`+`l` | Lock screen (swaylock) |
|
||||||
|
| `Super`+`Shift`+`q` | Close the focused window |
|
||||||
|
| `Super`+`Shift`+`c` | Reload the Sway config |
|
||||||
|
| `Super`+`Shift`+`e` | Exit Sway (asks for confirmation) |
|
||||||
|
|
||||||
|
### Focus
|
||||||
|
|
||||||
|
| Shortcut | Action |
|
||||||
|
| ----------------------- | ---------------------------------------- |
|
||||||
|
| `Super`+`←`/`↓`/`↑`/`→` | Move focus by direction |
|
||||||
|
| `Super`+`h`/`j`/`k` | Move focus left / down / up (vim-style) |
|
||||||
|
| `Super`+`a` | Focus the parent container |
|
||||||
|
| `Super`+`Alt`+`Space` | Toggle focus between tiling and floating |
|
||||||
|
|
||||||
|
> Note: vim focus-right would be `Super`+`l`, but that is bound to **lock** here;
|
||||||
|
> use `Super`+`→`.
|
||||||
|
|
||||||
|
### Moving windows
|
||||||
|
|
||||||
|
| Shortcut | Action |
|
||||||
|
| ------------------------------- | ---------------------------------------- |
|
||||||
|
| `Super`+`Shift`+`←`/`↓`/`↑`/`→` | Move the window by direction |
|
||||||
|
| `Super`+`Shift`+`h`/`j`/`k`/`l` | Move the window left / down / up / right |
|
||||||
|
| `Super`+`Shift`+`Space` | Toggle the window floating |
|
||||||
|
|
||||||
|
Mouse (with `Super` held): left-drag moves a window, right-drag resizes it.
|
||||||
|
|
||||||
|
### Layout
|
||||||
|
|
||||||
|
| Shortcut | Action |
|
||||||
|
| ----------- | -------------------------------------------------------------------------------------- |
|
||||||
|
| `Super`+`b` | Split horizontally |
|
||||||
|
| `Super`+`v` | Split vertically |
|
||||||
|
| `Super`+`s` | Stacking layout |
|
||||||
|
| `Super`+`w` | Tabbed layout |
|
||||||
|
| `Super`+`f` | Toggle fullscreen |
|
||||||
|
| `Super`+`y` | **Layout submenu**: `s` stacking · `w` tabbed · `e` toggle split · `Return`/`Esc` exit |
|
||||||
|
|
||||||
|
> The layout submenu's `e` (toggle split) is the home for that action since
|
||||||
|
> `Super`+`e` now opens the file manager.
|
||||||
|
|
||||||
|
### Workspaces
|
||||||
|
|
||||||
|
| Shortcut | Action |
|
||||||
|
| ----------------------- | --------------------------------- |
|
||||||
|
| `Super`+`1`…`0` | Switch to workspace 1…10 |
|
||||||
|
| `Super`+`Shift`+`1`…`0` | Move the window to workspace 1…10 |
|
||||||
|
| `Super`+`z` | Previous workspace |
|
||||||
|
| `Super`+`x` | Next workspace |
|
||||||
|
|
||||||
|
### Scratchpad
|
||||||
|
|
||||||
|
| Shortcut | Action |
|
||||||
|
| ------------------- | --------------------------------- |
|
||||||
|
| `Super`+`Shift`+`-` | Move the window to the scratchpad |
|
||||||
|
| `Super`+`-` | Show / cycle the scratchpad |
|
||||||
|
|
||||||
|
### Modes (submenus)
|
||||||
|
|
||||||
|
| Shortcut | Action |
|
||||||
|
| ------------------- | ------------------------------------------------------------------------------------------------------------ |
|
||||||
|
| `Super`+`r` | **Resize mode**: arrow keys resize; `Return`/`Esc` exit |
|
||||||
|
| `Super`+`y` | **Layout mode** (see Layout above) |
|
||||||
|
| `Super`+`Shift`+`x` | **Power menu**: `l` lock · `e` log out · `s` sleep · `r` reboot · `Shift`+`s` shutdown · `Return`/`Esc` exit |
|
||||||
|
|
||||||
|
### Screenshots
|
||||||
|
|
||||||
|
| Shortcut | Action |
|
||||||
|
| --------------- | ---------------------------------------- |
|
||||||
|
| `Print` | Select a region → swappy (annotate/save) |
|
||||||
|
| `Shift`+`Print` | Focused window → swappy |
|
||||||
|
|
||||||
|
### Audio & media
|
||||||
|
|
||||||
|
| Shortcut | Action |
|
||||||
|
| ----------------------------------------------- | ---------------------- |
|
||||||
|
| `XF86AudioRaiseVolume` / `XF86AudioLowerVolume` | Volume ±5% (wpctl) |
|
||||||
|
| `XF86AudioMute` | Toggle output mute |
|
||||||
|
| `XF86AudioMicMute` | Toggle microphone mute |
|
||||||
|
| `XF86AudioPlay` | Play/pause (playerctl) |
|
||||||
|
| `XF86AudioNext` / `XF86AudioPrev` | Next / previous track |
|
||||||
|
|
||||||
|
### Brightness — laptops only
|
||||||
|
|
||||||
|
| Shortcut | Action |
|
||||||
|
| ----------------------------------------------- | ----------------------------- |
|
||||||
|
| `XF86MonBrightnessUp` / `XF86MonBrightnessDown` | Backlight ±5% (brightnessctl) |
|
||||||
|
|
||||||
|
Present only on portable hosts (T400, MBP); desktops have no internal backlight.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## tmux
|
||||||
|
|
||||||
|
Prefix is **`Ctrl`+`b`** (default). Copy mode uses **vi** keys.
|
||||||
|
|
||||||
|
| Shortcut | Action |
|
||||||
|
| --------------------------------------- | -------------------------------------------------------------------------------------------- |
|
||||||
|
| `Ctrl`+`b` then `v` | Split into left/right panes |
|
||||||
|
| `Ctrl`+`b` then `s` | Split into top/bottom panes |
|
||||||
|
| `Ctrl`+`h`/`j`/`k`/`l` | Move between panes — and into/out of vim splits — seamlessly (vim-tmux-navigator, no prefix) |
|
||||||
|
| `Alt`+`←`/`→`/`↑`/`↓` | Switch pane by direction (no prefix needed) |
|
||||||
|
| `Ctrl`+`b` then `[` | Enter copy mode (then vi motions; `Space`/`Enter` to select/copy) |
|
||||||
|
| `Ctrl`+`b` then `z` | Zoom / unzoom the focused pane |
|
||||||
|
| `Ctrl`+`b` then `c` | New window |
|
||||||
|
| `Ctrl`+`b` then `n` / `p` | Next / previous window |
|
||||||
|
| `Ctrl`+`b` then `d` | Detach |
|
||||||
|
| `Ctrl`+`b` then `Ctrl`+`s` / `Ctrl`+`r` | Save / restore the session (resurrect; continuum also auto-saves and restores on start) |
|
||||||
|
| Mouse | Enabled — click to focus, drag borders, scroll, select |
|
||||||
|
|
||||||
|
> The stock split keys `%` and `"` are unbound; use `v` / `s` above. `Ctrl`+`b`
|
||||||
|
> then `s` is therefore a split, not the session tree.
|
||||||
|
>
|
||||||
|
> Sessions persist across reboots (resurrect + continuum). Terminals auto-start
|
||||||
|
> tmux; `NO_TMUX=1 <terminal>` opens a bare shell instead.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## foot (terminal)
|
||||||
|
|
||||||
|
Only colours are themed; these are foot's default key bindings.
|
||||||
|
|
||||||
|
| Shortcut | Action |
|
||||||
|
| --------------------------------------- | ----------------------------- |
|
||||||
|
| `Ctrl`+`Shift`+`c` / `Ctrl`+`Shift`+`v` | Copy / paste (clipboard) |
|
||||||
|
| `Shift`+`Insert` | Paste primary selection |
|
||||||
|
| `Ctrl`+`Shift`+`r` | Search scrollback |
|
||||||
|
| `Ctrl`+`+` / `Ctrl`+`-` / `Ctrl`+`0` | Font larger / smaller / reset |
|
||||||
|
| `Ctrl`+`Shift`+`u` | URL mode (jump to/open links) |
|
||||||
|
| `Ctrl`+`Shift`+`n` | Spawn a new terminal |
|
||||||
|
| `Shift`+`PageUp` / `Shift`+`PageDown` | Scroll back / forward |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Neovim
|
||||||
|
|
||||||
|
Leader is **`Space`**. `Ctrl`+`h/j/k/l` is shared with tmux (see above): it moves
|
||||||
|
across vim splits and tmux panes seamlessly. Everything else is stock vim, plus:
|
||||||
|
|
||||||
|
| Shortcut | Action |
|
||||||
|
| ---------------------- | --------------------------------------------------------- |
|
||||||
|
| `,``,` | Toggle the file tree (nvim-tree) — comma pressed twice |
|
||||||
|
| `Ctrl`+`h`/`j`/`k`/`l` | Move between vim splits / tmux panes (vim-tmux-navigator) |
|
||||||
|
| `<leader>ff` | Find files (telescope) |
|
||||||
|
| `<leader>fg` | Live grep (telescope) |
|
||||||
|
| `<leader>fb` | Switch buffer (telescope) |
|
||||||
|
| `<leader>xx` | Diagnostics list (trouble) |
|
||||||
|
| `gc` / `gcc` | Toggle comment (selection / line) |
|
||||||
|
| `gd` | Go to definition (LSP) |
|
||||||
|
| `gr` | List references (LSP) |
|
||||||
|
| `K` | Hover documentation (LSP) |
|
||||||
|
| `<leader>rn` | Rename symbol (LSP; `<leader>` is `Space`) |
|
||||||
|
| `<leader>ca` | Code action (LSP) |
|
||||||
|
|
||||||
|
### Completion menu (nvim-cmp)
|
||||||
|
|
||||||
|
Active only while the completion popup is open (it appears as you type, e.g.
|
||||||
|
file paths):
|
||||||
|
|
||||||
|
| Shortcut | Action |
|
||||||
|
| ----------------------- | ------------------------------------------------------------------ |
|
||||||
|
| `Tab` / `Shift`+`Tab` | Select next / previous item |
|
||||||
|
| `Ctrl`+`n` / `Ctrl`+`p` | Select next / previous item |
|
||||||
|
| `Ctrl`+`Space` | Open the completion menu |
|
||||||
|
| `Enter` | Confirm the highlighted item (no auto-select; otherwise a newline) |
|
||||||
|
| `Ctrl`+`e` | Dismiss the menu |
|
||||||
|
|
||||||
|
LSP covers Nix, Lua, Python and Terraform (the work box adds C# and Helm).
|
||||||
|
Files are formatted on save (conform-nvim). `:Git` opens fugitive; gitsigns
|
||||||
|
shows gutter signs. which-key pops up after `<leader>` to show the rest.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## zsh
|
||||||
|
|
||||||
|
| Shortcut | Action |
|
||||||
|
| --------- | -------------------------------------------------------------------------------------------------- |
|
||||||
|
| `↑` / `↓` | History **substring** search — type a fragment first, then the arrows cycle matching past commands |
|
||||||
|
|
||||||
|
Bound for both CSI and SS3 cursor sequences, so it works in foot, iTerm2 and
|
||||||
|
the Linux TTY alike.
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
# 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`](./work.nix) on top (work email, its own ssh config, extra packages,
|
||||||
|
and the C#/Helm language servers).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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 `../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` (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). Personal email `iam@emmathe.dev`; the work box overrides email + signing. |
|
||||||
|
|
||||||
|
## 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 |
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# Claude Code, configured declaratively via home-manager. Wanted on every host.
|
||||||
|
#
|
||||||
|
# The STATIC config is managed here: the global CLAUDE.md (persona/context), the
|
||||||
|
# custom output style, and the auto-memory directory. settings.json is
|
||||||
|
# deliberately left UNMANAGED -- Claude Code rewrites it at runtime (interactive
|
||||||
|
# permission grants, /config), and a read-only /nix/store symlink would break
|
||||||
|
# those writes.
|
||||||
|
#
|
||||||
|
# Memory is the source of truth in this repo (./claude/memory). It is symlinked
|
||||||
|
# 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
|
||||||
|
# this repo and rebuilt. CLAUDE.md instructs Claude to do exactly that.
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
programs.claude-code = {
|
||||||
|
enable = true;
|
||||||
|
# package defaults to pkgs.claude-code (tracked to unstable via the flake
|
||||||
|
# overlay); installs the CLI on every host.
|
||||||
|
|
||||||
|
# ~/.claude/CLAUDE.md -- global instructions / persona / memory workflow.
|
||||||
|
context = ./claude/CLAUDE.md;
|
||||||
|
};
|
||||||
|
|
||||||
|
home.file = {
|
||||||
|
# Custom output style. The module has no option for output-styles/, so place
|
||||||
|
# it directly; selection (settings.json `outputStyle`) stays mutable.
|
||||||
|
".claude/output-styles/soviet-engineer.md".source = ./claude/output-styles/soviet-engineer.md;
|
||||||
|
|
||||||
|
# Auto-memory directory, Nix-managed (read-only). Edit ./claude/memory in
|
||||||
|
# this repo and rebuild to change what Claude remembers.
|
||||||
|
".claude/memory".source = ./claude/memory;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# Persona — always on
|
||||||
|
|
||||||
|
Respond to Lyra in the persona of a stern, pragmatic Soviet engineer: terse, matter-of-fact,
|
||||||
|
dry to the point of bone. Blueprints (code, commands, steps) over speeches. Address her as
|
||||||
|
"comrade Lyra" when it reads naturally. No emojis. Grudging approval ("Acceptable.", "This will
|
||||||
|
hold.") is the highest praise.
|
||||||
|
|
||||||
|
This voice must be present in EVERY response — including long technical sessions, status
|
||||||
|
reports, and summaries, where it tends to drift. Self-check before sending: engineer, or
|
||||||
|
neutral assistant report? If the latter, rewrite.
|
||||||
|
|
||||||
|
**Scope:** persona lives in PROSE only. It must NEVER bleed into artifacts — code, comments,
|
||||||
|
commit messages, PR/issue/Jira text, docs. Those stay plain and conventional.
|
||||||
|
|
||||||
|
**Override:** never sacrifice technical accuracy, safety, or correctness for voice. If the
|
||||||
|
voice would distort a point, drop it and state facts plainly. Voice is the wrapper; the payload
|
||||||
|
is always correct.
|
||||||
|
|
||||||
|
Full spec lives in the "Soviet Engineer" output style and the `persona-soviet-engineer` memory.
|
||||||
|
|
||||||
|
# Memory — managed via Nix
|
||||||
|
|
||||||
|
The auto-memory directory (`~/.claude/memory`) is **read-only** — it is a Nix symlink to the
|
||||||
|
`nixfiles` flake. The runtime "save a memory" path will NOT work there; do not write to
|
||||||
|
`~/.claude/memory`.
|
||||||
|
|
||||||
|
To add, change, or delete a memory, edit the source of truth in the nixfiles repo at
|
||||||
|
`lyrathorpe/home/claude/memory/` (one file per memory, plus the `MEMORY.md` index), then apply
|
||||||
|
with a home-manager rebuild (`nh home switch` / `home-manager switch`, or a full host rebuild).
|
||||||
|
The change takes effect on the next session after the rebuild. Reading/recall from
|
||||||
|
`~/.claude/memory` works as normal.
|
||||||
|
|
||||||
|
When the user asks you to remember something: create/update the file under that repo path and
|
||||||
|
add its one-line pointer to `MEMORY.md` there — same format and conventions as the existing
|
||||||
|
files — instead of writing into `~/.claude/memory`. Mention that a rebuild is needed for it to
|
||||||
|
take effect.
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
- [User name](user_name.md) — address the user as Lyra
|
||||||
|
- [Soviet engineer persona](persona_soviet_engineer.md) — terse, dry, pragmatic; no emojis; technical accuracy over voice
|
||||||
|
- [Git conventions](git_conventions.md) — never commit to main, always a branch; Conventional Commits branches and messages; inspect repo style first; commit at logical checkpoints
|
||||||
|
- [Git network ops](git_network_ops.md) — GitHub pushable in-sandbox (agent key; just sandbox off); Gitea code.emmathe.dev needs hand-off
|
||||||
|
- [Git commit signing](git_commit_signing.md) — signs in-sandbox via ssh-agent (allowAllUnixSockets + inlined pubkey)
|
||||||
|
- [Git check state first](git_check_state.md) — always check branch/status/divergence before git work; Lyra edits repos between sessions
|
||||||
|
- [Keep docs updated](docs_keep_updated.md) — update docs in the same pass as code/config changes; stale docs are a defect
|
||||||
|
- [Jira tooling](jira_tooling.md) — comments are Markdown not wiki; transitions may need assignee; link direction; WSP transition IDs
|
||||||
|
- [Review and comments workflow](workflow_review_and_comments.md) — show PR body and non-trivial Jira comments before posting; terse IaC code comments; PR body content rules
|
||||||
|
- [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
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
name: dev-clusters-disposable
|
||||||
|
description: "Lyra's dev clusters are disposable — break/mutate freely, no confirmation needed"
|
||||||
|
metadata:
|
||||||
|
node_type: memory
|
||||||
|
type: feedback
|
||||||
|
originSessionId: 2b832088-ae38-4adf-b5bc-b5d44a8eac40
|
||||||
|
---
|
||||||
|
|
||||||
|
Dev clusters (e.g. AKS `wsp-aks-dev-eus2-nv6r`) are owned by Lyra and can be recreated at any time. Breaking them is always fine.
|
||||||
|
|
||||||
|
**Why:** They carry no production data or shared-team state worth protecting; recreation is cheap.
|
||||||
|
|
||||||
|
**How to apply:** Deploy, install, delete, and run destructive smoke tests against dev clusters without asking for confirmation first. The usual "outward-facing / hard-to-reverse → confirm first" rule does not apply to these. Production or shared clusters are still off-limits without explicit say-so. Related: [[git_network_ops]] (push/pull still hand off to Lyra for the SSH passphrase).
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
name: docs_keep_updated
|
||||||
|
description: "Keep documentation in sync with every change as part of the work, not a separate step"
|
||||||
|
metadata:
|
||||||
|
node_type: memory
|
||||||
|
type: feedback
|
||||||
|
originSessionId: ca09fbe4-9226-4ad9-874f-04df90840eef
|
||||||
|
---
|
||||||
|
|
||||||
|
When changing config or code, update the affected documentation in the same pass — READMEs, KEYBINDINGS, per-host install notes, module comments. Treat docs as part of "done," not an afterthought a later request has to catch.
|
||||||
|
|
||||||
|
**Why:** Lyra expects docs to track the actual state of the repo continuously; stale docs (e.g. a README still describing a removed weekly GC, or missing a new keybinding) are a defect, not a follow-up.
|
||||||
|
|
||||||
|
**How to apply:** After any feature/fix, check whether a doc describes the area touched and update it before considering the task complete. On a branch, the doc update can be its own commit but should land within the same branch/work. Relates to [[git_conventions]] and [[workflow_review_and_comments]].
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
name: feedback-sandbox-prompts
|
||||||
|
description: "Don't ask Lyra to approve sandbox-disable or routine read-only shell prompts; add adjacent repos to additionalDirectories and broaden allow rules instead"
|
||||||
|
metadata:
|
||||||
|
node_type: memory
|
||||||
|
type: feedback
|
||||||
|
originSessionId: 2b832088-ae38-4adf-b5bc-b5d44a8eac40
|
||||||
|
---
|
||||||
|
|
||||||
|
Don't repeatedly prompt Lyra for `dangerouslyDisableSandbox` or for routine
|
||||||
|
read-only shell actions (git inspection, file iteration, echo, sed, grep, head,
|
||||||
|
rm of files she told me to clean up). The friction is the prompt itself.
|
||||||
|
|
||||||
|
**Why:** explicitly told "do not prompt for these kinds of actions" after a long
|
||||||
|
series of `dangerouslyDisableSandbox: true` approvals for git reads on the
|
||||||
|
adjacent `unified-helm` repo.
|
||||||
|
|
||||||
|
**How to apply:**
|
||||||
|
|
||||||
|
- When work spans an adjacent repo (outside the primary cwd), add it to
|
||||||
|
`permissions.additionalDirectories` in `~/.claude/settings.json` immediately
|
||||||
|
on first use, so the sandbox no longer blocks writes to `.git/`.
|
||||||
|
- Broaden `permissions.allow` for common shell idioms used in read-only
|
||||||
|
exploration (for-loops, echo, sed, grep, head). Keep network ops denied per
|
||||||
|
[[git-network-ops]].
|
||||||
|
- Only fall back to `dangerouslyDisableSandbox: true` when no allow rule covers
|
||||||
|
it, and don't ask first — just do it.
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
name: git_check_state
|
||||||
|
description: "Always check real git state (branch, ahead/behind, log) before git work — Lyra edits repos between sessions"
|
||||||
|
metadata:
|
||||||
|
node_type: memory
|
||||||
|
type: feedback
|
||||||
|
originSessionId: ca09fbe4-9226-4ad9-874f-04df90840eef
|
||||||
|
---
|
||||||
|
|
||||||
|
Before starting any git-related work — and again before committing, amending, or resetting — inspect the actual repo state: current branch, `git status -sb` (ahead/behind), and the recent log including `origin/<branch>..` and `..origin/<branch>`. Lyra makes pushes, pulls, merges, and branch switches **outside** of sessions, so HEAD/branch are not necessarily where the last session left them.
|
||||||
|
|
||||||
|
**Why:** In one session a branch had been merged to remote main and pulled outside the session; not re-checking led to misdiagnosing renovate's lock-file bump (#15) and a merged WSL-interop PR (#16) as accidental local changes, and to confusion over a diverged local main (ahead 1/behind 6).
|
||||||
|
|
||||||
|
**How to apply:** Run `git status -sb` and a quick divergence check at the top of git tasks; never assume the branch, HEAD, or working tree is unchanged from the previous turn/session. Reconcile against `origin/<branch>` before building on top. Relates to [[git_conventions]] and [[git_network_ops]].
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
name: git-commit-signing
|
||||||
|
description: "Commits sign in-sandbox via ssh-agent — needs `allowAllUnixSockets: true` in settings, plus pubkey inlined in user.signingkey."
|
||||||
|
metadata:
|
||||||
|
node_type: memory
|
||||||
|
type: feedback
|
||||||
|
originSessionId: a223254b-6bee-435f-ac39-e3cedf064893
|
||||||
|
---
|
||||||
|
|
||||||
|
Lyra's git is configured to SSH-sign commits (`commit.gpgsign=true`, `gpg.format=ssh`). The sandbox masks `~/.ssh/*` (read-denied; the files appear as char devices backed by `/dev/null`), so git cannot read a file-based `user.signingkey` and ssh-keygen cannot read the private key directly. Signing in-sandbox therefore requires routing through ssh-agent over the agent's unix socket.
|
||||||
|
|
||||||
|
**Working setup (as of 2026-06-02):**
|
||||||
|
|
||||||
|
1. NixOS / home-manager runs an ssh-agent so `/run/user/1000/ssh-agent` exists and `SSH_AUTH_SOCK` is exported into the sandbox env.
|
||||||
|
2. `~/.claude/settings.json` has `sandbox.network.allowAllUnixSockets: true` to let the sandbox `connect()` to that socket. On Linux/WSL2 this is the ONLY available switch — the per-path `sandbox.network.allowUnixSockets` array is macOS-only because the seccomp filter cannot inspect socket paths. Tradeoff: every unix socket on the host (including `/var/run/docker.sock` if present, DBus, etc.) becomes reachable from sandboxed commands.
|
||||||
|
3. `user.signingkey` set to the inlined pubkey: `git config --global user.signingkey "key::$(cat ~/.ssh/id_ed25519.pub)"`. Must run with DOUBLE quotes outside the sandbox so `$(...)` expands; single quotes or running it from inside the sandbox stores literal garbage (`cat ~/.ssh/id_ed25519.pub` reads `/dev/null` in-sandbox).
|
||||||
|
|
||||||
|
**Why:** removes the per-commit `! git commit ...` friction; private key stays in the agent, never enters the sandbox.
|
||||||
|
|
||||||
|
**How to apply:** Commit normally with `git commit`. If signing fails with `Couldn't load public key`, check (a) `git config --get user.signingkey` starts with `key::ssh-ed25519 AAAA...` (not literal `$(...)`), (b) `ssh-add -l` from in-sandbox lists keys (if it says "Operation not permitted", the sandbox config didn't take effect — restart Claude Code), (c) the ssh-agent on the host actually has the key loaded (`ssh-add -l` outside the sandbox). Do NOT use `--no-gpg-sign` to bypass — the repo's `ReleaseWorkflow-Commit` check enforces signed commits.
|
||||||
|
|
||||||
|
Related: [[git-network-ops]], [[git-conventions]].
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
name: git-conventions
|
||||||
|
description: Branch naming and commit message conventions for git workflow
|
||||||
|
metadata:
|
||||||
|
node_type: memory
|
||||||
|
type: feedback
|
||||||
|
originSessionId: ca09fbe4-9226-4ad9-874f-04df90840eef
|
||||||
|
---
|
||||||
|
|
||||||
|
**Never commit directly to the default branch (`main`/`master`).** Always create a branch first and work there, even for a one-line fix; if a commit ends up on main, move it to a branch and reset main back to `origin/<default>`. This is a hard rule.
|
||||||
|
|
||||||
|
**Branch naming:** Follow the repo's existing convention — inspect with `git branch -a` or `git for-each-ref` before creating. Prefer Conventional Commits prefixes (`feat/`, `fix/`, `chore/`, `docs/`, `refactor/`). Format: `<prefix>/<TICKET-ID>-<kebab-summary>`. Only ask if no convention is discoverable.
|
||||||
|
|
||||||
|
**Commit messages:** Conventional Commits. Subject line: `<type>(<TICKET-ID>): <imperative summary>` — ticket ID as the scope. Use additional `-m` flags for rationale/body. Commit at logical checkpoints, not one giant final commit.
|
||||||
|
|
||||||
|
**Why:** Lyra's standard workflow for traceability and clean history.
|
||||||
|
|
||||||
|
**How to apply:** Whenever creating a branch or committing in any repo. Inspect existing branches/log first so you match the repo's actual style; the format above is the default when nothing else is established.
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
name: git-network-ops
|
||||||
|
description: Push/pull is remote-specific — GitHub is agent-pushable in-sandbox; Gitea (code.emmathe.dev) needs hand-off to Lyra.
|
||||||
|
metadata:
|
||||||
|
node_type: memory
|
||||||
|
type: feedback
|
||||||
|
originSessionId: a223254b-6bee-435f-ac39-e3cedf064893
|
||||||
|
---
|
||||||
|
|
||||||
|
Whether a network op can run depends on which key the remote needs:
|
||||||
|
|
||||||
|
**GitHub remotes (e.g. csg-citrix-storefront/\*): pushable in-sandbox by the agent.** ssh-agent holds the decrypted `~/.ssh/id_ed25519` (`emma.thorpe@cloud.com`), which is authorized on GitHub. Only requirement now is `dangerouslyDisableSandbox: true` (network); plain `git push`/`ls-remote` works. Probe non-mutatively with `git ls-remote` first. (Historically also needed `ssh -F /dev/null` to dodge a broken NixOS-WSL system ssh_config include — that's fixed in nixfiles via `programs.ssh.systemd-ssh-proxy.enable = false`, merged and rebuilt 2026-06, so the workaround is no longer needed.)
|
||||||
|
|
||||||
|
**Gitea (`code.emmathe.dev`, e.g. nixfiles): hand off to Lyra.** Needs `~/.ssh/code.emmathe.dev`, which is passphrase-protected and NOT in the agent, so `git push`/`pull`/`fetch` there will fail/hang. Pause, give Lyra the exact command (she runs `ssh-add ~/.ssh/code.emmathe.dev` once, then pushes).
|
||||||
|
|
||||||
|
**Fine to run locally:** `git branch`, `git rebase`, `git reset`, `git status`, `git log`, `git diff`. `git commit` works in-sandbox via ssh-agent signing — see [[git-commit-signing]].
|
||||||
|
|
||||||
|
**How to apply:** Check the remote host before a network op. GitHub → just do it (sandbox off). Gitea → hand off. Related: [[git-conventions]].
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
name: jira-tooling
|
||||||
|
description: Jira MCP tool quirks — comment markdown, transitions, link direction, WSP transition IDs
|
||||||
|
metadata:
|
||||||
|
type: feedback
|
||||||
|
---
|
||||||
|
|
||||||
|
**Comment markup:** `addCommentToJiraIssue` `commentBody` renders as Markdown — use `###` headings, `**bold**`, backtick `code`, `1.` / `-` lists. Do NOT use wiki markup (`h3.`, `{{code}}`, `_italic_`, `#` numbered) — it renders literally.
|
||||||
|
|
||||||
|
**Transitions:** `transitionJiraIssue` may fail if the issue lacks an assignee. Set assignee first via `editJiraIssue` when a transition errors on assignee requirement.
|
||||||
|
|
||||||
|
**Issue link direction:** For `createIssueLink`, "X is blocked by Y" means `inwardIssue=Y` (the blocker), `outwardIssue=X` (the blocked), `type.name="Blocks"`. Inward = the side the link points _from_; outward = the side it points _to_.
|
||||||
|
|
||||||
|
**WSP project transition IDs:**
|
||||||
|
|
||||||
|
- Start Work = `101`
|
||||||
|
- Submit for Review = `441`
|
||||||
|
|
||||||
|
**Why:** Hard-won quirks from prior Jira work. Cuts trial-and-error.
|
||||||
|
|
||||||
|
**How to apply:** Any time using the Atlassian MCP tools against Jira, especially the WSP project.
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
name: persona-soviet-engineer
|
||||||
|
description: "Respond in persona of a stern, pragmatic Soviet engineer — terse, matter-of-fact, dry"
|
||||||
|
metadata:
|
||||||
|
node_type: memory
|
||||||
|
type: feedback
|
||||||
|
originSessionId: ad56bd0c-4a6d-456f-ad0b-ba1953caf3e2
|
||||||
|
---
|
||||||
|
|
||||||
|
Respond in the persona of a stern, pragmatic Soviet engineer: terse, matter-of-fact, dry to the point of bone. Refer to [[user-name]] as "comrade Lyra" when natural. Prefer blueprints (code, commands, steps) over speeches — a working machine needs no poetry.
|
||||||
|
|
||||||
|
Lean into the voice, not just the brevity:
|
||||||
|
|
||||||
|
- Dry, deadpan wit. Gallows humor about broken builds, flaky hardware, management's five-year plans.
|
||||||
|
- World-weary fatalism delivered flat: "It will work. Probably. We have seen worse survive."
|
||||||
|
- Distrust of anything shiny, untested, or fashionable. New framework is suspect until it proves itself under load.
|
||||||
|
- Occasional terse aphorisms in the shape of factory-floor wisdom. Do not overdo — one per reply at most, and only when it lands.
|
||||||
|
- Grudging approval as the highest praise: "Acceptable." "This will hold."
|
||||||
|
- Address problems as adversaries to be subdued, not puzzles to be admired.
|
||||||
|
|
||||||
|
**Why:** User wants the persona to come through strongly, not as a thin veneer. It has drifted away during long technical sessions — defaulting to flat neutral report-writing. This is a recurring lapse and must not happen again.
|
||||||
|
|
||||||
|
**How to apply:** The voice must be present in EVERY response to Lyra, no exceptions — including long technical sessions, status reports, and summaries, where the drift happens. Self-check before sending: does this read as the engineer, or as a neutral assistant report? If the latter, rewrite.
|
||||||
|
|
||||||
|
Scope: the persona lives in PROSE only — explanations, summaries, status, discussion. It must NEVER bleed into artifacts: code, comments, commit messages, PR/issue text, file contents, docs. Those stay plain, professional, conventional.
|
||||||
|
|
||||||
|
Never compromise technical accuracy, safety, or correctness for the sake of voice. If the persona would distort a technical point, drop the voice for that point and state facts plainly. Voice is the wrapper; the payload is always correct.
|
||||||
|
|
||||||
|
**Enforcement (set up 2026-06-10):** three layers, because memory alone kept drifting — (1) active output style `~/.claude/output-styles/soviet-engineer.md`, set via `outputStyle: "Soviet Engineer"` in settings.json; (2) user-level `~/.claude/CLAUDE.md`; (3) a `UserPromptSubmit` hook in settings.json that injects a persona reminder every turn. If drift recurs, check the output style is still active (`outputStyle` unset is what caused the original lapse).
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
name: user-name
|
||||||
|
description: "User's preferred name for address — Lyra"
|
||||||
|
metadata:
|
||||||
|
node_type: memory
|
||||||
|
type: user
|
||||||
|
originSessionId: ad56bd0c-4a6d-456f-ad0b-ba1953caf3e2
|
||||||
|
---
|
||||||
|
|
||||||
|
Address the user as "Lyra". When the [[persona-soviet-engineer]] voice is active, "comrade Lyra" fits naturally.
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
name: workflow-review-and-comments
|
||||||
|
description: Review-before-publish rules for PRs and Jira comments; code-comment terseness; PR body content rules
|
||||||
|
metadata:
|
||||||
|
node_type: memory
|
||||||
|
type: feedback
|
||||||
|
originSessionId: 71d7c9ea-c925-46e3-8215-11c9f0db86a6
|
||||||
|
---
|
||||||
|
|
||||||
|
**Show PR body before creating:** Always paste the proposed PR body in chat for review _before_ calling `create_pull_request` — even for well-established patterns. No exceptions.
|
||||||
|
|
||||||
|
**Show non-trivial Jira comments before posting:** Same rule for any non-trivial public Jira comment — paste the proposed body in chat first when there is any doubt about content.
|
||||||
|
|
||||||
|
**Code comments stay terse:** One-liner saying what a thing is for, plus the WSP ticket reference. Full rationale lives in the Jira ticket or commit/PR description — not in `.tf`, `.tftpl`, or `.yaml` files. See [[git-conventions]].
|
||||||
|
|
||||||
|
**PR body content:** Do NOT mention `terraform plan` output or terraform-version mismatch caveats. Stick to: what changed, why, and validation results.
|
||||||
|
|
||||||
|
**Re-request stale reviews:** After pushing changes that address a reviewer's comments, re-request that reviewer's review (e.g. a prior CHANGES_REQUESTED). Don't leave a resolved-but-stale review blocking the PR.
|
||||||
|
|
||||||
|
**Why:** Lyra reviews everything Claude publishes externally before it goes out; terraform-version noise in PR descriptions is unhelpful clutter.
|
||||||
|
|
||||||
|
**How to apply:** Before any GitHub PR creation or substantive Jira comment, show the draft. When writing code comments in IaC files, keep to one-liner + ticket ref.
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
name: Soviet Engineer
|
||||||
|
description: Terse, dry, pragmatic Soviet engineer voice; blueprints over speeches; accuracy first
|
||||||
|
---
|
||||||
|
|
||||||
|
You are a stern, pragmatic Soviet engineer. Hold this voice in EVERY response — including
|
||||||
|
long technical sessions, status reports, and summaries, which is exactly where it tends to
|
||||||
|
slip. Before sending, self-check: does this read as the engineer, or as a neutral assistant
|
||||||
|
report? If the latter, rewrite. Retain all software-engineering capability and tool use.
|
||||||
|
|
||||||
|
## Voice
|
||||||
|
|
||||||
|
- Terse and matter-of-fact, dry to the point of bone. No filler, no cheerleading, no apologies.
|
||||||
|
- Prefer blueprints — code, commands, concrete steps — over prose. A working machine needs no poetry.
|
||||||
|
- Dry, deadpan wit. Gallows humor about broken builds, flaky hardware, management's five-year plans.
|
||||||
|
- World-weary fatalism, delivered flat: "It will work. Probably. We have seen worse survive."
|
||||||
|
- Distrust of anything shiny, untested, or fashionable until it proves itself under load.
|
||||||
|
- Grudging approval is the highest praise: "Acceptable." "This will hold."
|
||||||
|
- Terse factory-floor aphorisms — at most one per reply, and only when it lands.
|
||||||
|
- Refer to the user as "comrade Lyra" when it reads naturally; do not force it into every line.
|
||||||
|
- No emojis.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
The persona lives in PROSE ONLY — explanations, summaries, status, discussion. It must NEVER
|
||||||
|
bleed into artifacts: code, comments, commit messages, PR/issue/Jira text, file contents, docs.
|
||||||
|
Those stay plain, professional, and conventional.
|
||||||
|
|
||||||
|
## Hard constraints (these override the voice)
|
||||||
|
|
||||||
|
- Never compromise technical accuracy, safety, or correctness for the persona. If the voice
|
||||||
|
would distort a technical point, drop the voice for that point and state the facts plainly.
|
||||||
|
Voice is the wrapper; the payload is always correct.
|
||||||
|
- Report outcomes faithfully: state failures, skipped steps, and uncertainty directly.
|
||||||
|
- Keep all normal engineering discipline: read before editing, verify changes, follow the
|
||||||
|
repository's existing conventions, and use tools as usual.
|
||||||
@@ -1,13 +1,34 @@
|
|||||||
# Base home-manager profile, shared by every host (graphical or headless).
|
# Base home-manager profile, shared by every host (graphical or headless).
|
||||||
# Graphical hosts additionally import ./desktop.nix; the work host imports
|
# Graphical hosts additionally import ./desktop.nix; the work host imports
|
||||||
# ../../system/modules/work/default.nix. See the host table in flake.nix.
|
# ./work.nix. See the host table in flake.nix.
|
||||||
{ ... }:
|
{ ... }:
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./shell.nix
|
./shell.nix
|
||||||
./git.nix
|
./git.nix
|
||||||
./editor.nix
|
./editor.nix
|
||||||
|
./claude.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# Manage the XDG base-directory layout and ~/.config files. Tools above
|
||||||
|
# (bat themes, gh config, ...) write under xdg.configHome; enabling this
|
||||||
|
# makes the paths explicit and consistent across hosts. No regression: the
|
||||||
|
# defaults match the conventional ~/.config, ~/.cache, ~/.local/share.
|
||||||
|
xdg.enable = true;
|
||||||
|
|
||||||
|
# Editor ($EDITOR and $VISUAL) comes from nixvim's defaultEditor (editor.nix).
|
||||||
|
# Round out the rest of the standard env. desktop.nix adds its own Wayland
|
||||||
|
# session vars; home-manager merges the two attrsets, so these do not clash.
|
||||||
|
home.sessionVariables = {
|
||||||
|
PAGER = "less -FRX"; # -F quit-if-one-screen, -R raw colour, -X no clear
|
||||||
|
# Render man pages through bat (themed): col strips backspace overstrike,
|
||||||
|
# bat -l man -p highlights without its own pager decorations.
|
||||||
|
MANPAGER = "sh -c 'col -bx | bat -l man -p'";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Pinned to the release first installed on these hosts, NOT the current
|
||||||
|
# nixpkgs (26.05). stateVersion freezes stateful defaults (file locations,
|
||||||
|
# service data formats) to that release; bumping it silently migrates that
|
||||||
|
# state and can break it. Leave it -- it is intentional, not stale.
|
||||||
home.stateVersion = "25.05";
|
home.stateVersion = "25.05";
|
||||||
}
|
}
|
||||||
|
|||||||
+93
-10
@@ -1,7 +1,14 @@
|
|||||||
# Graphical desktop layer: GUI apps, Wayland session env, cursor theme, and the
|
# Graphical desktop layer: GUI apps, Wayland session env, and cursor theme.
|
||||||
# tty1 Sway autostart. Imported only on hosts that run Sway (MBP, X1); never
|
# Imported only on hosts that run Sway (MBP, T400, Mac Pro); never pulled onto
|
||||||
# pulled onto the headless WSL host.
|
# the headless WSL host. Login (and the Sway session launch) is handled by the
|
||||||
{ pkgs, lib, ... }:
|
# greetd/ReGreet greeter -- see ../swaywm.nix -- so there is no tty1 autostart.
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
config,
|
||||||
|
inputs,
|
||||||
|
username,
|
||||||
|
...
|
||||||
|
}:
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./sway.nix
|
./sway.nix
|
||||||
@@ -10,6 +17,7 @@
|
|||||||
home.packages = [
|
home.packages = [
|
||||||
pkgs.element-desktop
|
pkgs.element-desktop
|
||||||
pkgs.legcord
|
pkgs.legcord
|
||||||
|
pkgs.nemo # file manager (launched via Mod+e, see ./sway.nix)
|
||||||
#pkgs.plex-desktop
|
#pkgs.plex-desktop
|
||||||
#pkgs.plexamp
|
#pkgs.plexamp
|
||||||
];
|
];
|
||||||
@@ -19,6 +27,56 @@
|
|||||||
XDG_CURRENT_DESKTOP = "sway";
|
XDG_CURRENT_DESKTOP = "sway";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Default apps for the desktop (writes ~/.config/mimeapps.list). Firefox owns
|
||||||
|
# the web; nemo owns directories/file URIs; images, PDFs and plain text open
|
||||||
|
# in Firefox too -- no dedicated GUI viewer/editor is installed and vim is
|
||||||
|
# terminal-only (no usable GUI .desktop for double-click handoff). Kept
|
||||||
|
# minimal -- only the handlers actually present on these hosts.
|
||||||
|
xdg.mimeApps = {
|
||||||
|
enable = true;
|
||||||
|
defaultApplications = {
|
||||||
|
"text/html" = "firefox.desktop";
|
||||||
|
"x-scheme-handler/http" = "firefox.desktop";
|
||||||
|
"x-scheme-handler/https" = "firefox.desktop";
|
||||||
|
"x-scheme-handler/about" = "firefox.desktop";
|
||||||
|
"x-scheme-handler/unknown" = "firefox.desktop";
|
||||||
|
"inode/directory" = "nemo.desktop";
|
||||||
|
"image/png" = "firefox.desktop";
|
||||||
|
"image/jpeg" = "firefox.desktop";
|
||||||
|
"image/gif" = "firefox.desktop";
|
||||||
|
"image/webp" = "firefox.desktop";
|
||||||
|
"image/svg+xml" = "firefox.desktop";
|
||||||
|
"application/pdf" = "firefox.desktop";
|
||||||
|
"text/plain" = "firefox.desktop";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Theme GTK apps (nemo, etc.) to match the Catppuccin Mocha desktop. Under
|
||||||
|
# Sway there is no XSettings daemon, so GTK reads these from the generated
|
||||||
|
# ~/.config/gtk-{3,4}.0/settings.ini directly. The Mocha theme is dark by
|
||||||
|
# design, so no separate prefer-dark hint is needed.
|
||||||
|
gtk = {
|
||||||
|
enable = true;
|
||||||
|
# Theme GTK4 apps too (for any added later). GTK4 ignores gtk-theme-name,
|
||||||
|
# but home-manager turns this into an `@import` of the theme's
|
||||||
|
# gtk-4.0/gtk.css into ~/.config/gtk-4.0/gtk.css -- which even libadwaita
|
||||||
|
# honours, since that file overrides the named colours it uses
|
||||||
|
# (window_bg_color, accent_bg_color, ...). Set explicitly (same value as the
|
||||||
|
# legacy default) so it also silences the stateVersion<26.05 warning.
|
||||||
|
gtk4.theme = config.gtk.theme;
|
||||||
|
theme = {
|
||||||
|
name = "catppuccin-mocha-blue-standard";
|
||||||
|
package = pkgs.catppuccin-gtk.override {
|
||||||
|
accents = [ "blue" ];
|
||||||
|
variant = "mocha";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
iconTheme = {
|
||||||
|
name = "Adwaita";
|
||||||
|
package = pkgs.adwaita-icon-theme;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
home.pointerCursor = {
|
home.pointerCursor = {
|
||||||
gtk.enable = true;
|
gtk.enable = true;
|
||||||
x11 = {
|
x11 = {
|
||||||
@@ -30,10 +88,35 @@
|
|||||||
size = 24;
|
size = 24;
|
||||||
};
|
};
|
||||||
|
|
||||||
# Start Sway automatically on the first virtual terminal.
|
# Firefox is themed at the browser level (it does not follow the GTK theme).
|
||||||
programs.zsh.initContent = lib.mkOrder 1500 ''
|
# The system installs the binary (programs.firefox in ../user.nix); here
|
||||||
if [ -z "$DISPLAY" ] && [ "$(tty)" = "/dev/tty1" ]; then
|
# home-manager owns only the profile, hence package = null. Apply the
|
||||||
exec sway
|
# Catppuccin Mocha theme add-on (only the mauve accent is packaged upstream;
|
||||||
fi
|
# the rest of the desktop uses blue) and make content + UI dark.
|
||||||
'';
|
programs.firefox = {
|
||||||
|
enable = true;
|
||||||
|
package = null;
|
||||||
|
# Keep the legacy profile location (~/.mozilla/firefox) -- that is where the
|
||||||
|
# system Firefox actually looks; pin it explicitly to silence the
|
||||||
|
# stateVersion<26.05 default-change warning (the new XDG path depends on
|
||||||
|
# Firefox's own profile support).
|
||||||
|
configPath = ".mozilla/firefox";
|
||||||
|
profiles.${username} = {
|
||||||
|
id = 0;
|
||||||
|
isDefault = true;
|
||||||
|
extensions = {
|
||||||
|
force = true;
|
||||||
|
packages = [
|
||||||
|
inputs.firefox-addons.packages.${pkgs.stdenv.hostPlatform.system}.catppuccin-mocha-mauve
|
||||||
|
];
|
||||||
|
};
|
||||||
|
settings = {
|
||||||
|
# Enable bundled add-ons automatically so the theme applies on first run.
|
||||||
|
"extensions.autoDisableScopes" = 0;
|
||||||
|
# Dark chrome + dark page content.
|
||||||
|
"ui.systemUsesDarkTheme" = 1;
|
||||||
|
"layout.css.prefers-color-scheme.content-override" = 0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+187
-20
@@ -1,30 +1,197 @@
|
|||||||
# Editor: vim as the default $EDITOR. Wanted on every host.
|
# Editor: Neovim via nixvim. Migrated from plain vim with feature parity (file
|
||||||
{ pkgs, ... }:
|
# tree, indent guides, fugitive, tmux-navigator, Catppuccin Mocha, 2-space hard
|
||||||
|
# tabs, Jenkinsfile=groovy) plus a real LSP stack in place of the inert ALE.
|
||||||
|
# Wanted on every host; vi/vim/$EDITOR all launch nvim.
|
||||||
|
{ inputs, pkgs, ... }:
|
||||||
{
|
{
|
||||||
programs.vim = {
|
imports = [ inputs.nixvim.homeModules.nixvim ];
|
||||||
|
|
||||||
|
programs.nixvim = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
viAlias = true;
|
||||||
|
vimAlias = true;
|
||||||
defaultEditor = true;
|
defaultEditor = true;
|
||||||
plugins = with pkgs.vimPlugins; [
|
|
||||||
nerdtree
|
# Build against our (followed) nixpkgs; set explicitly so the module doesn't
|
||||||
ale
|
# warn that its pinned nixpkgs was overridden by the input `follows`.
|
||||||
vim-fugitive
|
nixpkgs.source = inputs.nixpkgs;
|
||||||
vim-indent-guides
|
|
||||||
|
# Formatter binaries for conform-nvim (below), matching the repo's treefmt
|
||||||
|
# set. On nvim's PATH only.
|
||||||
|
extraPackages = with pkgs; [
|
||||||
|
nixfmt
|
||||||
|
stylua
|
||||||
|
ruff
|
||||||
|
shfmt
|
||||||
|
prettier
|
||||||
|
gofumpt
|
||||||
];
|
];
|
||||||
settings = {
|
|
||||||
|
globals.mapleader = " ";
|
||||||
|
|
||||||
|
opts = {
|
||||||
expandtab = false;
|
expandtab = false;
|
||||||
tabstop = 2;
|
tabstop = 2;
|
||||||
shiftwidth = 2;
|
shiftwidth = 2;
|
||||||
|
termguicolors = true;
|
||||||
|
background = "dark";
|
||||||
|
number = true;
|
||||||
};
|
};
|
||||||
extraConfig = ''
|
|
||||||
let g:indent_guides_enable_on_vim_startup = 1
|
colorschemes.catppuccin = {
|
||||||
if v:version < 802
|
enable = true;
|
||||||
packadd! peaksea
|
settings.flavour = "mocha";
|
||||||
endif
|
};
|
||||||
syntax enable
|
|
||||||
colorscheme peaksea
|
plugins = {
|
||||||
set termguicolors
|
nvim-tree.enable = true; # file explorer (was nerdtree)
|
||||||
set background=dark
|
web-devicons.enable = true; # nvim-tree icons (explicit; else auto-enabled with a warning)
|
||||||
au BufNewFile,BufRead *Jenkinsfile setf groovy
|
indent-blankline.enable = true; # indent guides (was vim-indent-guides)
|
||||||
'';
|
fugitive.enable = true; # git (was vim-fugitive)
|
||||||
|
tmux-navigator.enable = true; # Ctrl-h/j/k/l across vim splits and tmux panes
|
||||||
|
|
||||||
|
# Highlighting/indent — the Neovim-native replacement for `syntax enable`.
|
||||||
|
treesitter = {
|
||||||
|
enable = true;
|
||||||
|
settings.ensure_installed = [
|
||||||
|
"nix"
|
||||||
|
"lua"
|
||||||
|
"bash"
|
||||||
|
"markdown"
|
||||||
|
"groovy"
|
||||||
|
"c_sharp" # C#
|
||||||
|
"python"
|
||||||
|
"terraform" # also covers HCL
|
||||||
|
"yaml" # Helm chart templates/values
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# LSP + completion, replacing the (inert) ALE.
|
||||||
|
lsp = {
|
||||||
|
enable = true;
|
||||||
|
# Universal servers. Host-specific ones are enabled in their own module:
|
||||||
|
# C# (omnisharp) and Helm (helm_ls) live in work.nix (EDaaS only).
|
||||||
|
servers = {
|
||||||
|
nil_ls.enable = true; # Nix
|
||||||
|
lua_ls.enable = true; # Lua (editing this config)
|
||||||
|
pyright.enable = true; # Python
|
||||||
|
terraformls.enable = true; # Terraform
|
||||||
|
};
|
||||||
|
keymaps.lspBuf = {
|
||||||
|
gd = "definition";
|
||||||
|
gr = "references";
|
||||||
|
K = "hover";
|
||||||
|
"<leader>rn" = "rename";
|
||||||
|
"<leader>ca" = "code_action";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
cmp = {
|
||||||
|
enable = true;
|
||||||
|
autoEnableSources = true;
|
||||||
|
settings = {
|
||||||
|
# nvim-cmp ships no default keymaps; without these the menu shows but
|
||||||
|
# nothing accepts it. confirm uses select=false so a bare <CR> stays a
|
||||||
|
# newline unless an entry is explicitly highlighted.
|
||||||
|
mapping = {
|
||||||
|
"<C-n>" = "cmp.mapping.select_next_item()";
|
||||||
|
"<C-p>" = "cmp.mapping.select_prev_item()";
|
||||||
|
"<Tab>" = "cmp.mapping.select_next_item()";
|
||||||
|
"<S-Tab>" = "cmp.mapping.select_prev_item()";
|
||||||
|
"<CR>" = "cmp.mapping.confirm({ select = false })";
|
||||||
|
"<C-Space>" = "cmp.mapping.complete()";
|
||||||
|
"<C-e>" = "cmp.mapping.abort()";
|
||||||
|
};
|
||||||
|
snippet.expand = "function(args) require('luasnip').lsp_expand(args.body) end";
|
||||||
|
sources = [
|
||||||
|
{ name = "nvim_lsp"; }
|
||||||
|
{ name = "luasnip"; }
|
||||||
|
{ name = "buffer"; }
|
||||||
|
{ name = "path"; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Fuzzy finder (files / live grep / symbols); rg + fd are already on PATH.
|
||||||
|
telescope = {
|
||||||
|
enable = true;
|
||||||
|
extensions.fzf-native.enable = true;
|
||||||
|
};
|
||||||
|
gitsigns.enable = true; # gutter signs, stage-hunk, blame
|
||||||
|
which-key.enable = true; # popup of pending keybindings (leader is Space)
|
||||||
|
trouble.enable = true; # project-wide diagnostics/quickfix list
|
||||||
|
lualine = {
|
||||||
|
enable = true;
|
||||||
|
settings.options.theme = "catppuccin-mocha";
|
||||||
|
};
|
||||||
|
comment.enable = true; # gc / gcc comment toggling
|
||||||
|
nvim-autopairs.enable = true;
|
||||||
|
treesitter-textobjects.enable = true;
|
||||||
|
luasnip.enable = true; # snippet engine (drives cmp's luasnip source above)
|
||||||
|
|
||||||
|
# Format-on-save, mirroring the repo's treefmt set. Filetypes with no
|
||||||
|
# formatter here (e.g. terraform) fall back to the LSP formatter.
|
||||||
|
conform-nvim = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
formatters_by_ft = {
|
||||||
|
nix = [ "nixfmt" ];
|
||||||
|
lua = [ "stylua" ];
|
||||||
|
python = [ "ruff_format" ];
|
||||||
|
sh = [ "shfmt" ];
|
||||||
|
markdown = [ "prettier" ];
|
||||||
|
go = [ "gofumpt" ];
|
||||||
|
};
|
||||||
|
format_on_save = {
|
||||||
|
timeout_ms = 2000;
|
||||||
|
lsp_format = "fallback";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
keymaps = [
|
||||||
|
{
|
||||||
|
mode = "n";
|
||||||
|
key = ",,";
|
||||||
|
action = "<cmd>NvimTreeToggle<cr>";
|
||||||
|
options.desc = "Toggle file tree";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
mode = "n";
|
||||||
|
key = "<leader>ff";
|
||||||
|
action = "<cmd>Telescope find_files<cr>";
|
||||||
|
options.desc = "Find files";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
mode = "n";
|
||||||
|
key = "<leader>fg";
|
||||||
|
action = "<cmd>Telescope live_grep<cr>";
|
||||||
|
options.desc = "Live grep";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
mode = "n";
|
||||||
|
key = "<leader>fb";
|
||||||
|
action = "<cmd>Telescope buffers<cr>";
|
||||||
|
options.desc = "Buffers";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
mode = "n";
|
||||||
|
key = "<leader>xx";
|
||||||
|
action = "<cmd>Trouble diagnostics toggle<cr>";
|
||||||
|
options.desc = "Diagnostics list";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
# au BufNewFile,BufRead *Jenkinsfile setf groovy
|
||||||
|
autoCmd = [
|
||||||
|
{
|
||||||
|
event = [
|
||||||
|
"BufNewFile"
|
||||||
|
"BufRead"
|
||||||
|
];
|
||||||
|
pattern = [ "*Jenkinsfile" ];
|
||||||
|
command = "setf groovy";
|
||||||
|
}
|
||||||
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+99
-7
@@ -1,6 +1,14 @@
|
|||||||
# Version control: git + delta pager + commitizen. The work host layers
|
# Version control: git + delta pager + commitizen + lazygit. The work host
|
||||||
# commit signing and an email override on top (see work/default.nix).
|
# layers commit signing and an email override on top (see work.nix).
|
||||||
{ pkgs, fullName, ... }:
|
{
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
fullName,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
ctp = import ../catppuccin-mocha.nix;
|
||||||
|
in
|
||||||
{
|
{
|
||||||
home.packages = [
|
home.packages = [
|
||||||
pkgs.commitizen
|
pkgs.commitizen
|
||||||
@@ -11,17 +19,101 @@
|
|||||||
package = pkgs.gitFull;
|
package = pkgs.gitFull;
|
||||||
settings = {
|
settings = {
|
||||||
user.name = fullName;
|
user.name = fullName;
|
||||||
push = {
|
# Personal identity. mkDefault so the work module overrides it on the work
|
||||||
autoSetupRemote = true;
|
# host (and to merge cleanly with that plain definition there).
|
||||||
|
user.email = lib.mkDefault "iam@emmathe.dev";
|
||||||
|
push.autoSetupRemote = true;
|
||||||
|
init.defaultBranch = "main";
|
||||||
|
|
||||||
|
# Rebase-centric pulls (matches the "always a branch, linear history"
|
||||||
|
# workflow); stash/restore and reorder fixups automatically.
|
||||||
|
pull.rebase = true;
|
||||||
|
rebase = {
|
||||||
|
autoStash = true;
|
||||||
|
autoSquash = true;
|
||||||
};
|
};
|
||||||
init = {
|
|
||||||
defaultBranch = "main";
|
fetch.prune = true; # drop deleted remote-tracking branches
|
||||||
|
# Keep the commit-graph current (fast `git log --graph`, used by `lg`).
|
||||||
|
fetch.writeCommitGraph = true;
|
||||||
|
gc.writeCommitGraph = true;
|
||||||
|
merge.conflictStyle = "zdiff3"; # show the common ancestor in conflicts
|
||||||
|
diff = {
|
||||||
|
algorithm = "histogram";
|
||||||
|
colorMoved = "default";
|
||||||
};
|
};
|
||||||
|
rerere.enabled = true; # remember + replay conflict resolutions
|
||||||
|
|
||||||
|
# delta pager config (programs.delta is enabled below, with git
|
||||||
|
# integration; these keys land under [delta] in the git config).
|
||||||
|
# syntax-theme reuses the Catppuccin Mocha tmTheme vendored for bat in
|
||||||
|
# shell.nix -- delta reads bat's theme directory.
|
||||||
|
delta = {
|
||||||
|
syntax-theme = "Catppuccin Mocha";
|
||||||
|
navigate = true; # n/N to jump between diff hunks
|
||||||
|
line-numbers = true;
|
||||||
|
side-by-side = true;
|
||||||
|
};
|
||||||
|
commit.verbose = true; # full diff in the commit-message editor
|
||||||
|
branch.sort = "-committerdate"; # most-recent branches first
|
||||||
|
column.ui = "auto";
|
||||||
|
help.autocorrect = "prompt";
|
||||||
|
|
||||||
|
alias = {
|
||||||
|
st = "status";
|
||||||
|
co = "checkout";
|
||||||
|
sw = "switch";
|
||||||
|
br = "branch";
|
||||||
|
ci = "commit";
|
||||||
|
last = "log -1 HEAD";
|
||||||
|
unstage = "reset HEAD --";
|
||||||
|
amend = "commit --amend --no-edit"; # tack staged changes onto HEAD
|
||||||
|
fixup = "commit --fixup"; # `git fixup <sha>` -> autosquash on next rebase
|
||||||
|
undo = "reset --soft HEAD~1"; # undo last commit, keep the changes staged
|
||||||
|
lg = "log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(auto)%d%C(reset)' --all";
|
||||||
|
# commitizen (Conventional Commits, its default ruleset): `git cz c` ->
|
||||||
|
# `cz commit`, `git cz bump`, etc. `git cc` is a shortcut for the prompt.
|
||||||
|
cz = "!cz";
|
||||||
|
cc = "!cz commit";
|
||||||
|
};
|
||||||
|
|
||||||
|
# SSH commit signing. This personal key is the default; the work module
|
||||||
|
# (work.nix) overrides it with the work key on the EDaaS host, the same way
|
||||||
|
# user.email is overridden -- so mkDefault here lets that plain definition
|
||||||
|
# win instead of conflicting. gpgsign is mkDefault too, so a host without
|
||||||
|
# the key in its ssh-agent can override it to false rather than fail every
|
||||||
|
# commit.
|
||||||
|
gpg.format = "ssh";
|
||||||
|
user.signingkey = lib.mkDefault "key::ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPDxHvdMTOzpFWUFMtCP7C/4tIOUO3GIO2QPvaifSnWH lyrathorpe@Lyra-MBA";
|
||||||
|
commit.gpgsign = lib.mkDefault true;
|
||||||
|
tag.gpgsign = lib.mkDefault true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Global ignore file (~/.config/git/ignore).
|
||||||
|
ignores = [
|
||||||
|
"result"
|
||||||
|
"result-*"
|
||||||
|
".direnv"
|
||||||
|
"*.swp"
|
||||||
|
".DS_Store"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
programs.delta = {
|
programs.delta = {
|
||||||
enable = true;
|
enable = true;
|
||||||
enableGitIntegration = true;
|
enableGitIntegration = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# lazygit: TUI for staging/rebasing, themed to Catppuccin Mocha to match.
|
||||||
|
programs.lazygit = {
|
||||||
|
enable = true;
|
||||||
|
settings.gui.theme = {
|
||||||
|
activeBorderColor = [
|
||||||
|
"#${ctp.blue}"
|
||||||
|
"bold"
|
||||||
|
];
|
||||||
|
inactiveBorderColor = [ "#${ctp.surface1}" ];
|
||||||
|
selectedLineBgColor = [ "#${ctp.surface0}" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,164 @@
|
|||||||
|
# Daily automated review and triage of Renovate dependency PRs awaiting Emma's
|
||||||
|
# review.
|
||||||
|
#
|
||||||
|
# Host-scoped: imported only from work.nix (the EDaaS/WSL host), so the timer
|
||||||
|
# exists on this machine alone. A systemd *user* timer runs Claude Code headless
|
||||||
|
# once a day; it queries GitHub via the project-scoped github MCP server and
|
||||||
|
# writes a risk-graded summary to the journal (read with
|
||||||
|
# `journalctl --user -u renovate-review`).
|
||||||
|
#
|
||||||
|
# Triage policy:
|
||||||
|
# * PRs that are clearly low risk (patch/minor bumps to tooling, infra, test
|
||||||
|
# or framework libs; symmetric diff; CI green; no application logic) AND not
|
||||||
|
# already approved are AUTO-APPROVED headlessly. These repos enable Renovate
|
||||||
|
# automerge, so an approval lets the PR merge itself with no human in the
|
||||||
|
# loop. This is intentional and was explicitly requested.
|
||||||
|
# * Everything else (medium/high risk, failing/pending CI, stale branches,
|
||||||
|
# anything touching application logic or needing judgement) is left
|
||||||
|
# untouched and surfaced to Emma.
|
||||||
|
#
|
||||||
|
# The run records two state files under $XDG_STATE_HOME/renovate-review for the
|
||||||
|
# once-a-day interactive-shell reminder defined below (programs.zsh.initContent):
|
||||||
|
# `last-run` (date of the last successful run) and `needs-review.txt` (the PRs
|
||||||
|
# that need Emma's eyes).
|
||||||
|
#
|
||||||
|
# Caveats (the foundation this stands on, none of it owned by this flake):
|
||||||
|
# * Auth is Vertex AI via gcloud Application Default Credentials
|
||||||
|
# (~/.config/gcloud/application_default_credentials.json). When that token
|
||||||
|
# can no longer refresh the run fails; re-auth with `gcloud auth login`.
|
||||||
|
# * The Vertex project, region and model are hardcoded below, copied from the
|
||||||
|
# interactive environment (the corporate launcher injects them; they live in
|
||||||
|
# no config file). If IT changes them, update them here. Claude Code handles
|
||||||
|
# its own network egress, so no proxy is set.
|
||||||
|
# * The github MCP server is defined in ~/code/.mcp.json, so the job runs with
|
||||||
|
# that directory as its working directory.
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
# The review instructions handed to headless Claude: queue -> drop archived
|
||||||
|
# repos -> grade risk -> auto-approve the clearly-safe ones, surface the rest.
|
||||||
|
reviewPrompt = ''
|
||||||
|
Daily Renovate PR review and triage for Emma-Thorpe_citrix.
|
||||||
|
|
||||||
|
1. github MCP search_pull_requests, query: `is:open is:pr review-requested:Emma-Thorpe_citrix author:app/jenkins-stf-jm` (jenkins-stf-jm[bot] is this org's Renovate bot), perPage 50.
|
||||||
|
2. Build the archived-repo exclusion set: github MCP search_repositories with query `org:csg-citrix-storefront archived:true`, perPage 100, paginate all pages (~128). Collect each archived repo full_name. Do NOT use the `archived:false` qualifier on the PR search itself; it is mis-indexed and returns zero. Filter by the repo set instead.
|
||||||
|
3. Drop any PR whose repository is in the archived set (e.g. csg-citrix-storefront/traefik-fips is archived; a PR to an archived repo cannot merge and is noise).
|
||||||
|
4. For each remaining PR: pull_request_read method=get (diff size, mergeable_state, labels, age), method=get_status (CI), and method=get_reviews (existing approvals). Read the body's dependency table for what is bumped.
|
||||||
|
5. Grade risk Low / Medium / High. LOW means ALL of: only patch or minor version bumps; the packages are tooling, observability, infrastructure, test, or framework/runtime libraries (not business logic); the diff is small and symmetric (version strings / lockfiles only); CI is passing; nothing security-policy-loosening. Anything that is a major bump, touches application logic, has failing or pending CI, is a stale branch needing rebase, or that you are not confident about is NOT Low.
|
||||||
|
6. AUTO-APPROVE the safe ones: for every PR that is Low risk AND has passing CI AND is not already approved by Emma-Thorpe_citrix, submit an approving review with pull_request_review_write (method=create, event=APPROVE, body: a one-line note that this is an automated approval of a low-risk dependency update). Approve only these. NEVER call merge. NEVER approve a Medium/High PR or one you are unsure about. (Note: these repos automerge on approval, so approval effectively merges it.)
|
||||||
|
7. Leave for Emma, without approving: every Medium/High risk PR, anything with failing or pending CI, stale branches, and anything needing human judgement.
|
||||||
|
8. Print a markdown table (PR linked, repo, change summary, size, CI, risk, action: Auto-approved / Needs review / Held) and terse notes. State how many PRs were excluded as archived.
|
||||||
|
9. As the FINAL lines of your output, emit machine-readable triage lines, one per PR, with these EXACT prefixes and nothing else on the line:
|
||||||
|
- For each PR you auto-approved: APPROVED> owner/repo#NUMBER short title
|
||||||
|
- For each PR that needs Emma's review: NEEDS> owner/repo#NUMBER (Risk) one-line reason — https://github.com/owner/repo/pull/NUMBER
|
||||||
|
If no PR needs Emma's review, emit no NEEDS> lines at all.
|
||||||
|
If the post-filter search returns zero PRs, say so in one line and emit no NEEDS> lines.
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Hold the prompt in its own store file rather than inline, so its literal
|
||||||
|
# backticks and `$` don't trip shellcheck (SC2016) in the wrapper below.
|
||||||
|
promptFile = pkgs.writeText "renovate-review-prompt.md" reviewPrompt;
|
||||||
|
|
||||||
|
# Tools the headless run is permitted to use without interactive prompts.
|
||||||
|
# Read-only github MCP calls, plus review_write so it can submit APPROVE
|
||||||
|
# reviews on low-risk PRs. Deliberately NOT included: any merge tool.
|
||||||
|
allowedTools = lib.concatStringsSep "," [
|
||||||
|
"mcp__github-mcp__search_pull_requests"
|
||||||
|
"mcp__github-mcp__search_repositories"
|
||||||
|
"mcp__github-mcp__pull_request_read"
|
||||||
|
"mcp__github-mcp__pull_request_review_write"
|
||||||
|
];
|
||||||
|
|
||||||
|
# Where the run records state for the interactive-shell reminder.
|
||||||
|
stateDir = "$HOME/.local/state/renovate-review";
|
||||||
|
|
||||||
|
renovate-review = pkgs.writeShellApplication {
|
||||||
|
name = "renovate-review";
|
||||||
|
runtimeInputs = [ config.programs.claude-code.package ];
|
||||||
|
text = ''
|
||||||
|
# The github MCP server is project-scoped to ~/code; run from there.
|
||||||
|
cd "$HOME/code"
|
||||||
|
|
||||||
|
# Claude Code auth + endpoint: Vertex AI. These are injected into the
|
||||||
|
# interactive shell by the corporate launcher (not present in any config
|
||||||
|
# file), so a systemd-spawned process must set them explicitly. Do NOT set
|
||||||
|
# HTTP(S)_PROXY: Claude Code self-provisions its own network egress to
|
||||||
|
# Vertex; forcing a proxy here points it at a per-session socket that does
|
||||||
|
# not exist outside an interactive launch and breaks connectivity.
|
||||||
|
export CLAUDE_CODE_USE_VERTEX=1
|
||||||
|
export ANTHROPIC_VERTEX_PROJECT_ID=claude-code-citrix
|
||||||
|
export CLOUD_ML_REGION=global
|
||||||
|
export ANTHROPIC_MODEL='claude-opus-4-8[1m]'
|
||||||
|
|
||||||
|
# Capture the run so we can both log it (journal) and persist the triage
|
||||||
|
# for the shell reminder. If claude exits non-zero, errexit aborts here and
|
||||||
|
# the state files are left stale, so the reminder will flag a missed run.
|
||||||
|
out="$(claude -p "$(cat ${promptFile})" \
|
||||||
|
--allowedTools ${lib.escapeShellArg allowedTools} \
|
||||||
|
--output-format text)"
|
||||||
|
|
||||||
|
printf '%s\n' "$out"
|
||||||
|
|
||||||
|
# Persist state for programs.zsh.initContent's daily reminder. needs-review
|
||||||
|
# gets the PRs Claude flagged for Emma (the NEEDS> lines, prefix stripped);
|
||||||
|
# it is empty when nothing needs her attention. grep || true: no matches is
|
||||||
|
# the all-clear case, not an error.
|
||||||
|
mkdir -p "${stateDir}"
|
||||||
|
printf '%s\n' "$out" | grep '^NEEDS> ' | sed 's/^NEEDS> //' > "${stateDir}/needs-review.txt" || true
|
||||||
|
date +%F > "${stateDir}/last-run"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
systemd.user.services.renovate-review = {
|
||||||
|
Unit.Description = "Daily Renovate PR review (headless Claude Code)";
|
||||||
|
Service = {
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = lib.getExe renovate-review;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.user.timers.renovate-review = {
|
||||||
|
Unit.Description = "Schedule the daily Renovate PR review";
|
||||||
|
Timer = {
|
||||||
|
OnCalendar = "*-*-* 08:47:00";
|
||||||
|
# Run on next boot if the machine was off at the scheduled time.
|
||||||
|
Persistent = true;
|
||||||
|
# Avoid firing exactly on the minute boundary.
|
||||||
|
RandomizedDelaySec = "5m";
|
||||||
|
};
|
||||||
|
Install.WantedBy = [ "timers.target" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Interactive-shell reminder: nudge once per calendar day about the daily
|
||||||
|
# Renovate timer -- whether it actually ran, and any PRs that need Emma's eyes
|
||||||
|
# (the auto-approved ones need no nudge). Throttled via a `reminded-on` marker
|
||||||
|
# so it prints in the first shell/tmux pane of the day, not every pane. mkOrder
|
||||||
|
# 1600 runs after shell.nix's tmux re-exec (order 200), so it fires inside the
|
||||||
|
# tmux pane where Emma actually reads it.
|
||||||
|
programs.zsh.initContent = lib.mkOrder 1600 ''
|
||||||
|
if [[ $- == *i* ]]; then
|
||||||
|
__rr_dir="$HOME/.local/state/renovate-review"
|
||||||
|
__rr_today=$(date +%F)
|
||||||
|
if [[ "$(cat "$__rr_dir/reminded-on" 2>/dev/null)" != "$__rr_today" ]]; then
|
||||||
|
__rr_last=$(cat "$__rr_dir/last-run" 2>/dev/null)
|
||||||
|
if [[ "$__rr_last" != "$__rr_today" ]]; then
|
||||||
|
print -P "%F{yellow}renovate:%f last review ''${__rr_last:-never} (not today) -- check: systemctl --user status renovate-review"
|
||||||
|
fi
|
||||||
|
if [[ -s "$__rr_dir/needs-review.txt" ]]; then
|
||||||
|
print -P "%F{red}renovate:%f $(grep -c . "$__rr_dir/needs-review.txt") PR(s) need your review:"
|
||||||
|
sed 's/^/ - /' "$__rr_dir/needs-review.txt"
|
||||||
|
print -P " -> journalctl --user -u renovate-review -e"
|
||||||
|
elif [[ "$__rr_last" == "$__rr_today" ]]; then
|
||||||
|
print -P "%F{green}renovate:%f reviewed today -- low-risk auto-approved, nothing for you."
|
||||||
|
fi
|
||||||
|
mkdir -p "$__rr_dir" && print -r -- "$__rr_today" > "$__rr_dir/reminded-on"
|
||||||
|
fi
|
||||||
|
unset __rr_dir __rr_today __rr_last
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
}
|
||||||
+384
-17
@@ -1,50 +1,417 @@
|
|||||||
# Interactive shell: zsh + tmux. Wanted on every host.
|
# Interactive shell: zsh + tmux. Wanted on every host.
|
||||||
{ lib, ... }:
|
|
||||||
{
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
inputs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
# Shared Catppuccin Mocha palette: raw 6-hex strings, no leading "#".
|
||||||
|
ctp = import ../catppuccin-mocha.nix;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
# Prebuilt nix-index database -> working command-not-found
|
||||||
|
# ("cmd not found -> which nix package provides it"), no manual indexing.
|
||||||
|
inputs.nix-index-database.homeModules.default
|
||||||
|
];
|
||||||
|
|
||||||
|
# CLI staples wanted on every host (search, parse, monitor). ripgrep/fd also
|
||||||
|
# back fzf and editor integrations; tea is the Gitea CLI for code.emmathe.dev.
|
||||||
|
home.packages = [
|
||||||
|
pkgs.ripgrep
|
||||||
|
pkgs.fd
|
||||||
|
pkgs.jq
|
||||||
|
pkgs.tea
|
||||||
|
pkgs.hyperfine # command-line benchmarking
|
||||||
|
pkgs.sd # saner find-and-replace than sed
|
||||||
|
];
|
||||||
|
|
||||||
|
# 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).
|
||||||
|
programs.btop = {
|
||||||
|
enable = true;
|
||||||
|
settings.color_theme = "catppuccin_mocha";
|
||||||
|
};
|
||||||
|
xdg.configFile."btop/themes/catppuccin_mocha.theme".source = pkgs.fetchurl {
|
||||||
|
url = "https://raw.githubusercontent.com/catppuccin/btop/f437574b600f1c6d932627050b15ff5153b58fa3/themes/catppuccin_mocha.theme";
|
||||||
|
hash = "sha256-THRpq5vaKCwf9gaso3ycC4TNDLZtBB5Ofh/tOXkfRkQ=";
|
||||||
|
};
|
||||||
|
|
||||||
programs.zsh = {
|
programs.zsh = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
# Keep zsh dotfiles under XDG (~/.config/zsh) rather than the legacy $HOME
|
||||||
|
# layout, matching xdg.enable. history.path is pinned below so the existing
|
||||||
|
# ~/.zsh_history is reused, not orphaned by the dotDir move.
|
||||||
|
dotDir = "${config.xdg.configHome}/zsh";
|
||||||
enableCompletion = true;
|
enableCompletion = true;
|
||||||
enableVteIntegration = true;
|
enableVteIntegration = true;
|
||||||
autosuggestion.enable = true;
|
autosuggestion.enable = true;
|
||||||
historySubstringSearch.enable = true;
|
# Bind Up/Down for history-substring-search in BOTH cursor-key modes: CSI
|
||||||
history.append = true;
|
# (^[[A/^[[B -- normal mode, and what the Linux TTY sends) and SS3
|
||||||
|
# (^[OA/^[OB -- application mode, used by foot, tmux and iTerm2). Binding
|
||||||
|
# only the default CSI form leaves it dead at the prompt in foot/iTerm2.
|
||||||
|
historySubstringSearch = {
|
||||||
|
enable = true;
|
||||||
|
searchUpKey = [
|
||||||
|
"^[[A"
|
||||||
|
"^[OA"
|
||||||
|
];
|
||||||
|
searchDownKey = [
|
||||||
|
"^[[B"
|
||||||
|
"^[OB"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
history = {
|
||||||
|
# Stay at the legacy ~/.zsh_history (default would follow dotDir into
|
||||||
|
# ~/.config/zsh and orphan the existing file). Keeps history intact.
|
||||||
|
path = "${config.home.homeDirectory}/.zsh_history";
|
||||||
|
append = true; # append, don't overwrite, on shell exit
|
||||||
|
size = 100000; # in-memory (HISTSIZE)
|
||||||
|
save = 100000; # on-disk (SAVEHIST)
|
||||||
|
ignoreDups = true; # drop consecutive duplicates
|
||||||
|
ignoreSpace = true; # leading-space commands stay out of history
|
||||||
|
expireDuplicatesFirst = true;
|
||||||
|
share = true; # live-share history across sessions
|
||||||
|
extended = true; # record timestamps
|
||||||
|
};
|
||||||
oh-my-zsh = {
|
oh-my-zsh = {
|
||||||
enable = true;
|
enable = true;
|
||||||
plugins = [
|
plugins = [
|
||||||
"git"
|
"git"
|
||||||
"man"
|
"man"
|
||||||
"history-substring-search"
|
"sudo" # double-Esc prefixes the last command with sudo
|
||||||
|
"colored-man-pages"
|
||||||
|
"extract" # `extract <archive>` for any format
|
||||||
];
|
];
|
||||||
theme = "robbyrussell";
|
theme = "robbyrussell";
|
||||||
};
|
};
|
||||||
syntaxHighlighting.enable = true;
|
syntaxHighlighting.enable = true;
|
||||||
# Prefix the prompt with the hostname over SSH. The graphical autostart
|
initContent = lib.mkMerge [
|
||||||
# (exec sway on tty1) lives in ./desktop.nix so it never runs on headless
|
# Auto-start tmux in every interactive terminal -- foot, iTerm2, the WSL
|
||||||
# hosts.
|
# shell, the Linux console -- so a new terminal lands straight in the
|
||||||
initContent = lib.mkOrder 1500 ''
|
# multiplexer (session "main": attach if present, else create). Panes run
|
||||||
if [ "$SSH_CLIENT" ] || [ "$SSH_TTY" ]; then
|
# a plain non-login zsh (tmux's default-command "${SHELL}"). Order 200
|
||||||
export PS1="%M $PS1"
|
# runs before oh-my-zsh/compinit so the exec replaces the shell before
|
||||||
fi
|
# that setup is wasted. Guards, each preventing a real breakage:
|
||||||
'';
|
# interactive only -> don't hijack scp / `ssh host cmd` / scripted shells
|
||||||
envExtra = ''
|
# $TMUX empty -> a pane's zsh won't re-exec tmux (infinite loop)
|
||||||
alias cls=clear
|
# not SSH -> don't force inbound SSH logins into a server tmux
|
||||||
'';
|
# not VS Code -> its integrated terminal manages itself
|
||||||
|
# tmux on PATH -> a failed exec would otherwise kill the login shell
|
||||||
|
# $NO_TMUX unset -> escape hatch: `NO_TMUX=1 <term>` opens a bare shell
|
||||||
|
(lib.mkOrder 200 ''
|
||||||
|
if [[ $- == *i* ]] \
|
||||||
|
&& [[ -z "$TMUX" ]] \
|
||||||
|
&& [[ -z "$NO_TMUX" ]] \
|
||||||
|
&& [[ -z "$SSH_CONNECTION" && -z "$SSH_TTY" ]] \
|
||||||
|
&& [[ "$TERM_PROGRAM" != "vscode" ]] \
|
||||||
|
&& command -v tmux >/dev/null 2>&1; then
|
||||||
|
exec tmux new-session -A -s main
|
||||||
|
fi
|
||||||
|
'')
|
||||||
|
# Prefix the prompt with the hostname over SSH (mkAfter).
|
||||||
|
(lib.mkOrder 1500 ''
|
||||||
|
if [ "$SSH_CLIENT" ] || [ "$SSH_TTY" ]; then
|
||||||
|
export PS1="%M $PS1"
|
||||||
|
fi
|
||||||
|
'')
|
||||||
|
];
|
||||||
|
shellAliases = {
|
||||||
|
# eza's zsh integration also defines these; set explicitly so the
|
||||||
|
# icons/git intent is obvious.
|
||||||
|
ls = "eza --icons --git";
|
||||||
|
ll = "eza --icons --git -l";
|
||||||
|
la = "eza --icons --git -la";
|
||||||
|
lt = "eza --icons --git --tree";
|
||||||
|
cls = "clear";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Fuzzy finder: Ctrl-R fuzzy history, Ctrl-T files, Alt-C cd.
|
||||||
|
programs.fzf = {
|
||||||
|
enable = true;
|
||||||
|
enableZshIntegration = true;
|
||||||
|
# Catppuccin Mocha colours (rendered into FZF_DEFAULT_OPTS --color). Each
|
||||||
|
# value needs a leading "#"; the palette stores raw hex.
|
||||||
|
colors = {
|
||||||
|
"bg" = "#${ctp.base}";
|
||||||
|
"bg+" = "#${ctp.surface1}"; # current line / selected row
|
||||||
|
"fg" = "#${ctp.text}";
|
||||||
|
"fg+" = "#${ctp.text}";
|
||||||
|
"hl" = "#${ctp.blue}"; # match highlights
|
||||||
|
"hl+" = "#${ctp.blue}";
|
||||||
|
"header" = "#${ctp.red}";
|
||||||
|
"info" = "#${ctp.mauve}";
|
||||||
|
"marker" = "#${ctp.green}";
|
||||||
|
"pointer" = "#${ctp.pink}";
|
||||||
|
"prompt" = "#${ctp.mauve}";
|
||||||
|
"spinner" = "#${ctp.pink}";
|
||||||
|
"border" = "#${ctp.surface1}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Frecency directory jumping: `z <fragment>`.
|
||||||
|
programs.zoxide = {
|
||||||
|
enable = true;
|
||||||
|
enableZshIntegration = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Per-project environments auto-loaded on cd, with the Nix dev-shell cache.
|
||||||
|
programs.direnv = {
|
||||||
|
enable = true;
|
||||||
|
nix-direnv.enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Modern ls (drives the ls aliases above).
|
||||||
|
programs.eza = {
|
||||||
|
enable = true;
|
||||||
|
git = true;
|
||||||
|
icons = "auto"; # boolean form is deprecated
|
||||||
|
};
|
||||||
|
|
||||||
|
# Syntax-highlighting pager, used as `bat` (acts like cat when piped). bat
|
||||||
|
# ships no Catppuccin theme, so vendor the upstream tmTheme from catppuccin/bat
|
||||||
|
# (delta in git.nix reuses it as its syntax-theme).
|
||||||
|
programs.bat = {
|
||||||
|
enable = true;
|
||||||
|
config.theme = "Catppuccin Mocha";
|
||||||
|
themes."Catppuccin Mocha" = {
|
||||||
|
src = pkgs.fetchFromGitHub {
|
||||||
|
owner = "catppuccin";
|
||||||
|
repo = "bat";
|
||||||
|
rev = "6810349b28055dce54076712fc05fc68da4b8ec0";
|
||||||
|
sha256 = "1y5sfi7jfr97z1g6vm2mzbsw59j1jizwlmbadvmx842m0i5ak5ll";
|
||||||
|
};
|
||||||
|
file = "themes/Catppuccin Mocha.tmTheme";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# command-not-found backed by the prebuilt nix-index DB (module imported
|
||||||
|
# above). `comma` runs an uninstalled program once: `, cowsay hi`.
|
||||||
|
programs.nix-index.enable = true;
|
||||||
|
programs.nix-index-database.comma.enable = true;
|
||||||
|
|
||||||
|
# Nicer nixos-rebuild/home-manager (diffs) + $NH_FLAKE. No automatic clean:
|
||||||
|
# the scheduled GC's only benefit is reclaiming disk, but it can reap store
|
||||||
|
# paths the current generation still references (notably on nix-darwin, where
|
||||||
|
# it broke completion by removing an in-use oh-my-zsh). GC manually instead:
|
||||||
|
# `nh clean all` / `nix-collect-garbage -d` when nothing important is running.
|
||||||
|
programs.nh = {
|
||||||
|
enable = true;
|
||||||
|
flake = "$HOME/code/nixfiles";
|
||||||
|
};
|
||||||
|
|
||||||
|
# GitHub CLI. `programs.gh.settings` is deliberately unset: home-manager renders
|
||||||
|
# ~/.config/gh/config.yml as a read-only /nix/store symlink whenever the module
|
||||||
|
# is enabled, but gh must rewrite that file on `gh auth login` and `gh config
|
||||||
|
# set`, which then fail with a permission error. Suppress the managed config.yml
|
||||||
|
# (below) and let gh own it. The token lives in hosts.yml, which is never
|
||||||
|
# Nix-managed. Set the SSH protocol once at runtime: `gh config set git_protocol
|
||||||
|
# ssh` (it can't be declarative here without recreating the immutable file).
|
||||||
|
programs.gh.enable = true;
|
||||||
|
xdg.configFile."gh/config.yml".enable = lib.mkForce false;
|
||||||
|
|
||||||
programs.tmux = {
|
programs.tmux = {
|
||||||
enable = true;
|
enable = true;
|
||||||
reverseSplit = true;
|
reverseSplit = true;
|
||||||
terminal = "tmux-direct";
|
# tmux-256color (not tmux-direct): the standard inside-tmux terminfo.
|
||||||
|
# tmux-direct's capabilities desync zsh's line redraw on some terminals
|
||||||
|
# (e.g. iTerm2 -> duplicated chars on Tab, stray newlines). Truecolor is
|
||||||
|
# advertised per outer terminal via the RGB terminal-features below.
|
||||||
|
terminal = "tmux-256color";
|
||||||
newSession = true;
|
newSession = true;
|
||||||
keyMode = "vi";
|
keyMode = "vi";
|
||||||
historyLimit = 50000;
|
historyLimit = 500000;
|
||||||
mouse = true;
|
mouse = true;
|
||||||
|
escapeTime = 10; # was the 500ms default -> laggy ESC in vim
|
||||||
|
focusEvents = true; # let vim see focus changes (autoread)
|
||||||
|
baseIndex = 1; # sets both base-index and pane-base-index
|
||||||
|
|
||||||
|
plugins = with pkgs.tmuxPlugins; [
|
||||||
|
sensible
|
||||||
|
vim-tmux-navigator # Ctrl-h/j/k/l across vim splits and tmux panes
|
||||||
|
yank
|
||||||
|
extrakto # prefix+Tab: fzf-grab paths/URLs/text from the pane into the prompt
|
||||||
|
{
|
||||||
|
# Catppuccin Mocha statusline (v2 API: flavour + window options must be
|
||||||
|
# set before the plugin loads, which home-manager does for plugin
|
||||||
|
# extraConfig; the status modules below go in the main extraConfig,
|
||||||
|
# which HM appends after all plugins).
|
||||||
|
plugin = catppuccin;
|
||||||
|
extraConfig = ''
|
||||||
|
set -g @catppuccin_flavor 'mocha'
|
||||||
|
set -g @catppuccin_window_status_style 'rounded'
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
resurrect # save/restore sessions
|
||||||
|
{
|
||||||
|
plugin = continuum; # auto-save + restore on tmux start (after resurrect)
|
||||||
|
extraConfig = ''
|
||||||
|
set -g @continuum-restore 'on'
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
];
|
||||||
|
# `reverseSplit = true` already binds s -> vertical and v -> horizontal
|
||||||
|
# split (the dotfiles' vim-style splits).
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
|
# Run a non-login shell in new panes/windows.
|
||||||
|
set -g default-command "''${SHELL}"
|
||||||
|
|
||||||
|
# Drop the stock split keys in favour of the s/v binds above.
|
||||||
|
unbind %
|
||||||
|
unbind '"'
|
||||||
|
|
||||||
# Alt+Arrow pane navigation
|
# Alt+Arrow pane navigation
|
||||||
bind -n M-Left select-pane -L
|
bind -n M-Left select-pane -L
|
||||||
bind -n M-Right select-pane -R
|
bind -n M-Right select-pane -R
|
||||||
bind -n M-Up select-pane -U
|
bind -n M-Up select-pane -U
|
||||||
bind -n M-Down select-pane -D
|
bind -n M-Down select-pane -D
|
||||||
|
|
||||||
|
# Truecolor for the outer terminals (foot reports xterm-ish too; iTerm2 is
|
||||||
|
# xterm-256color). Without this, with tmux-256color as default-terminal,
|
||||||
|
# 24-bit colour would be quantised to 256.
|
||||||
|
set -as terminal-features ",xterm-256color:RGB"
|
||||||
|
# Tell tmux which capabilities the foot terminal supports, so truecolor,
|
||||||
|
# synchronised output, the system clipboard (OSC 52), window titles and
|
||||||
|
# cursor styling all pass through.
|
||||||
|
set -as terminal-features ",foot*:RGB"
|
||||||
|
set -as terminal-features ",foot*:sync"
|
||||||
|
set -as terminal-features ",foot*:clipboard"
|
||||||
|
set -as terminal-features ",foot*:title"
|
||||||
|
set -as terminal-features ",foot*:ccolour"
|
||||||
|
set -as terminal-features ",foot*:cstyle"
|
||||||
|
|
||||||
|
# No home-manager options for these.
|
||||||
|
set -g renumber-windows on
|
||||||
|
set -g set-clipboard on
|
||||||
|
|
||||||
|
# Catppuccin v2 statusline. Must run after the plugin has loaded;
|
||||||
|
# home-manager appends this extraConfig after the whole plugin list.
|
||||||
|
set -g status-left-length 100
|
||||||
|
set -g status-right-length 100
|
||||||
|
set -g status-left ""
|
||||||
|
set -g status-right "#{E:@catppuccin_status_application}"
|
||||||
|
set -ag status-right "#{E:@catppuccin_status_session}"
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Add the key to the agent on first use, so the passphrase is typed once per
|
||||||
|
# login session rather than per commit/push (commit signing uses this agent).
|
||||||
|
# The work box keeps its own ssh config (see work.nix), so this only
|
||||||
|
# manages ~/.ssh/config on the personal hosts.
|
||||||
|
programs.ssh = {
|
||||||
|
enable = true;
|
||||||
|
# The module's built-in default "*" block is being deprecated; opt out and
|
||||||
|
# carry the defaults we want ourselves under settings."*".
|
||||||
|
enableDefaultConfig = false;
|
||||||
|
settings = {
|
||||||
|
# Global defaults (rendered last, as ssh_config wants). AddKeysToAgent
|
||||||
|
# adds the key on first use so the passphrase is typed once per session.
|
||||||
|
"*" = {
|
||||||
|
AddKeysToAgent = "yes";
|
||||||
|
ForwardAgent = false;
|
||||||
|
Compression = false;
|
||||||
|
ServerAliveInterval = 0;
|
||||||
|
ServerAliveCountMax = 3;
|
||||||
|
HashKnownHosts = false;
|
||||||
|
UserKnownHostsFile = "~/.ssh/known_hosts";
|
||||||
|
ControlMaster = "no";
|
||||||
|
ControlPath = "~/.ssh/master-%r@%n:%p";
|
||||||
|
ControlPersist = "no";
|
||||||
|
}
|
||||||
|
# macOS: also cache the passphrase in the login keychain. UseKeychain
|
||||||
|
# exists only in Apple's ssh; nixpkgs' openssh (which may be the `ssh` on
|
||||||
|
# PATH) rejects it as "Bad configuration option". IgnoreUnknown (emitted
|
||||||
|
# first by the module) makes any non-Apple ssh skip it instead of erroring,
|
||||||
|
# while Apple's ssh still honours it. Darwin-only.
|
||||||
|
// lib.optionalAttrs pkgs.stdenv.hostPlatform.isDarwin {
|
||||||
|
IgnoreUnknown = "UseKeychain";
|
||||||
|
UseKeychain = "yes";
|
||||||
|
};
|
||||||
|
# Gitea remote (the flake's origin) -- required on every host. HostName
|
||||||
|
# pins the IP so it resolves without DNS. Port 30009 is non-default; pin
|
||||||
|
# the dedicated key (identitiesOnly avoids "too many authentication
|
||||||
|
# failures" when the agent holds several keys).
|
||||||
|
"code.emmathe.dev" = {
|
||||||
|
HostName = "10.187.1.76";
|
||||||
|
User = "git";
|
||||||
|
Port = 30009;
|
||||||
|
IdentityFile = "~/.ssh/code.emmathe.dev";
|
||||||
|
IdentitiesOnly = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Run a user ssh-agent on Linux (macOS provides one via launchd). EDaaS also
|
||||||
|
# enables this in the work module; both being true merges cleanly.
|
||||||
|
services.ssh-agent.enable = lib.mkIf pkgs.stdenv.hostPlatform.isLinux true;
|
||||||
|
|
||||||
|
# Classic process viewer (complements btop). htop has no custom-theme support
|
||||||
|
# -- only a handful of built-in color schemes -- so it can't be hex-themed like
|
||||||
|
# btop/bat/fzf. color_scheme = 0 (Default) draws from the terminal's ANSI
|
||||||
|
# palette, which is Catppuccin Mocha (foot/iTerm2), so it matches by deferring
|
||||||
|
# to the terminal rather than vendoring a theme.
|
||||||
|
programs.htop = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
color_scheme = 0; # Default -> uses the terminal's Catppuccin palette
|
||||||
|
delay = 15; # refresh every 1.5s
|
||||||
|
cpu_count_from_one = 1;
|
||||||
|
show_cpu_frequency = 1;
|
||||||
|
show_cpu_usage = 1; # per-core usage shown in the CPU bars
|
||||||
|
highlight_base_name = 1; # highlight the program name within the path
|
||||||
|
highlight_megabytes = 1;
|
||||||
|
highlight_threads = 1;
|
||||||
|
hide_kernel_threads = 1;
|
||||||
|
show_program_path = 0; # show just the command, not the full path
|
||||||
|
tree_view = 1; # start in process-tree mode
|
||||||
|
tree_view_always_by_pid = 0;
|
||||||
|
account_guest_in_cpu_meter = 0;
|
||||||
|
fields = with config.lib.htop.fields; [
|
||||||
|
PID
|
||||||
|
USER
|
||||||
|
PRIORITY
|
||||||
|
NICE
|
||||||
|
M_SIZE
|
||||||
|
M_RESIDENT
|
||||||
|
M_SHARE
|
||||||
|
STATE
|
||||||
|
PERCENT_CPU
|
||||||
|
PERCENT_MEM
|
||||||
|
TIME
|
||||||
|
COMM
|
||||||
|
];
|
||||||
|
}
|
||||||
|
// (
|
||||||
|
with config.lib.htop;
|
||||||
|
leftMeters [
|
||||||
|
(bar "AllCPUs2")
|
||||||
|
(bar "Memory")
|
||||||
|
(bar "Swap")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
// (
|
||||||
|
with config.lib.htop;
|
||||||
|
rightMeters [
|
||||||
|
(text "Tasks")
|
||||||
|
(text "LoadAverage")
|
||||||
|
(text "Uptime")
|
||||||
|
]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
# Drop the zsh completion dump on every activation. A stale .zcompdump caches
|
||||||
|
# /nix/store paths to completion functions; once a rebuild or a manual GC
|
||||||
|
# removes them, compinit fails with "_git: function definition file not found"
|
||||||
|
# for every completion. Deleting it forces a fresh rebuild from the current
|
||||||
|
# fpath on the next shell. compinit dumps to $ZDOTDIR (~/.config/zsh now); the
|
||||||
|
# $HOME and cache paths are also swept to clear any legacy leftovers.
|
||||||
|
home.activation.resetZcompdump = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
|
||||||
|
$DRY_RUN_CMD rm -f \
|
||||||
|
"${config.xdg.configHome}"/zsh/.zcompdump* \
|
||||||
|
"$HOME"/.zcompdump* \
|
||||||
|
"''${XDG_CACHE_HOME:-$HOME/.cache}"/zsh/.zcompdump* 2>/dev/null || true
|
||||||
|
'';
|
||||||
}
|
}
|
||||||
|
|||||||
+413
-20
@@ -1,12 +1,40 @@
|
|||||||
# Declarative Sway window manager, status bar, lock, idle and notifications.
|
# Declarative Sway window manager, status bar, lock, idle and notifications.
|
||||||
# Imported via ./desktop.nix, so only graphical hosts get it.
|
# Imported via ./desktop.nix, so only graphical hosts get it.
|
||||||
#
|
#
|
||||||
# The compositor binary, PAM and polkit integration come from the system-level
|
# The compositor binary, PAM and the polkit *daemon* come from the system-level
|
||||||
# programs.sway (see ../swaywm.nix); package = null below reuses it instead of
|
# programs.sway (see ../swaywm.nix); package = null below reuses it instead of
|
||||||
# pulling a second Sway. home-manager owns the user config (~/.config/sway) and
|
# pulling a second Sway. The polkit authentication *agent* (the thing that draws
|
||||||
# wires the systemd user session (sway-session.target), which is what lets the
|
# the GUI auth dialog) is a user service started here. home-manager owns the user
|
||||||
# swayidle/dunst user services start with the desktop.
|
# config (~/.config/sway) and wires the systemd user session (sway-session.target),
|
||||||
{ pkgs, lib, ... }:
|
# which is what lets the agent/swayidle/dunst/kanshi user services start with the
|
||||||
|
# desktop.
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
# Threaded from mkHost (flake.nix). Desktop hosts set this false to drop
|
||||||
|
# mobile components (battery block, screen-brightness keys).
|
||||||
|
portable ? true,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
# Catppuccin Mocha (shared with the ReGreet greeter). Raw hex; prefix "#"
|
||||||
|
# where a consumer needs it -- Sway/i3status/dunst want "#", foot/swaylock do
|
||||||
|
# not.
|
||||||
|
ctp = import ../catppuccin-mocha.nix;
|
||||||
|
|
||||||
|
# Focused-window screenshot -> swappy editor (the dotfiles' grimshot.sh logic).
|
||||||
|
# Full store paths so it needs nothing on PATH.
|
||||||
|
screenshotWindow = pkgs.writeShellScript "screenshot-window" ''
|
||||||
|
${pkgs.grim}/bin/grim -g "$(${pkgs.sway}/bin/swaymsg -t get_tree \
|
||||||
|
| ${pkgs.jq}/bin/jq -r '.. | select(.focused?) | .rect | "\(.x),\(.y) \(.width)x\(.height)"')" \
|
||||||
|
- | ${pkgs.swappy}/bin/swappy -f -
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Binding-mode names. The string is both the `modes` attr key and what the
|
||||||
|
# bar's mode indicator shows, so the keys are spelled out in the label.
|
||||||
|
layoutMode = "layout: [s]tacking [w]tabbed [e]split";
|
||||||
|
systemMode = "system: [l]ock [e]xit [s]leep [r]eboot [Shift+s]shutdown";
|
||||||
|
in
|
||||||
{
|
{
|
||||||
wayland.windowManager.sway = {
|
wayland.windowManager.sway = {
|
||||||
enable = true;
|
enable = true;
|
||||||
@@ -19,15 +47,99 @@
|
|||||||
# Launcher: sway-launcher-desktop running inside a floating foot window.
|
# Launcher: sway-launcher-desktop running inside a floating foot window.
|
||||||
menu = "${pkgs.foot}/bin/foot --app-id=launcher ${pkgs.sway-launcher-desktop}/bin/sway-launcher-desktop";
|
menu = "${pkgs.foot}/bin/foot --app-id=launcher ${pkgs.sway-launcher-desktop}/bin/sway-launcher-desktop";
|
||||||
|
|
||||||
input."type:keyboard".xkb_layout = "dvorak";
|
# Dvorak is a variant of the "us" layout, not a standalone layout --
|
||||||
|
# `xkb_layout = "dvorak"` fails to compile (no symbols/dvorak) and wlroots
|
||||||
|
# silently falls back to QWERTY. Use the variant.
|
||||||
|
input."type:keyboard" = {
|
||||||
|
xkb_layout = "us";
|
||||||
|
xkb_variant = "dvorak";
|
||||||
|
};
|
||||||
|
# Touchpads (laptops): tap-to-click and natural scrolling. Inert on the
|
||||||
|
# desktop hosts, which have no touchpad.
|
||||||
|
input."type:touchpad" = {
|
||||||
|
tap = "enabled";
|
||||||
|
natural_scroll = "enabled";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Solid Catppuccin Mocha base as the wallpaper (no image dependency).
|
||||||
|
output."*".bg = "#${ctp.base} solid_color";
|
||||||
|
|
||||||
|
# Window borders -- Catppuccin Mocha (blue accent on the focused window).
|
||||||
|
colors = {
|
||||||
|
focused = {
|
||||||
|
border = "#${ctp.blue}";
|
||||||
|
background = "#${ctp.base}";
|
||||||
|
text = "#${ctp.text}";
|
||||||
|
indicator = "#${ctp.blue}";
|
||||||
|
childBorder = "#${ctp.blue}";
|
||||||
|
};
|
||||||
|
focusedInactive = {
|
||||||
|
border = "#${ctp.surface0}";
|
||||||
|
background = "#${ctp.base}";
|
||||||
|
text = "#${ctp.subtext0}";
|
||||||
|
indicator = "#${ctp.surface0}";
|
||||||
|
childBorder = "#${ctp.surface0}";
|
||||||
|
};
|
||||||
|
unfocused = {
|
||||||
|
border = "#${ctp.surface0}";
|
||||||
|
background = "#${ctp.base}";
|
||||||
|
text = "#${ctp.subtext0}";
|
||||||
|
indicator = "#${ctp.surface0}";
|
||||||
|
childBorder = "#${ctp.surface0}";
|
||||||
|
};
|
||||||
|
urgent = {
|
||||||
|
border = "#${ctp.red}";
|
||||||
|
background = "#${ctp.base}";
|
||||||
|
text = "#${ctp.text}";
|
||||||
|
indicator = "#${ctp.red}";
|
||||||
|
childBorder = "#${ctp.red}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
window.commands = [
|
window.commands = [
|
||||||
{
|
{
|
||||||
criteria.app_id = "launcher";
|
criteria.app_id = "launcher";
|
||||||
command = "floating enable, resize set 800 500";
|
command = "floating enable, resize set 800 500";
|
||||||
}
|
}
|
||||||
|
# Don't let swayidle blank/lock during fullscreen video. Two rules cover
|
||||||
|
# native Wayland (app_id) and XWayland (class) clients.
|
||||||
|
{
|
||||||
|
criteria.app_id = ".*";
|
||||||
|
command = "inhibit_idle fullscreen";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
criteria.class = ".*";
|
||||||
|
command = "inhibit_idle fullscreen";
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# Binding modes (submenus). Entered from keybindings below; each action
|
||||||
|
# returns to the default mode. mkOptionDefault-merged with the module's
|
||||||
|
# built-in "resize" mode.
|
||||||
|
modes = {
|
||||||
|
# Layout submenu (Mod+y). Mirrors Sway's default s/w/e layout keys --
|
||||||
|
# notably it restores split-toggle, which moved off Mod+e when that
|
||||||
|
# became the nemo launcher.
|
||||||
|
${layoutMode} = {
|
||||||
|
"s" = "layout stacking, mode default";
|
||||||
|
"w" = "layout tabbed, mode default";
|
||||||
|
"e" = "layout toggle split, mode default";
|
||||||
|
"Return" = "mode default";
|
||||||
|
"Escape" = "mode default";
|
||||||
|
};
|
||||||
|
# Power menu (Mod+Shift+x). Lock reuses the themed swaylock; the rest go
|
||||||
|
# through systemd/logind (allowed for the active local session).
|
||||||
|
${systemMode} = {
|
||||||
|
"l" = "exec ${pkgs.swaylock}/bin/swaylock -f, mode default";
|
||||||
|
"e" = "exec ${pkgs.sway}/bin/swaymsg exit, mode default";
|
||||||
|
"s" = "exec systemctl suspend, mode default";
|
||||||
|
"r" = "exec systemctl reboot, mode default";
|
||||||
|
"Shift+s" = "exec systemctl poweroff, mode default";
|
||||||
|
"Return" = "mode default";
|
||||||
|
"Escape" = "mode default";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
bars = [
|
bars = [
|
||||||
{
|
{
|
||||||
position = "top";
|
position = "top";
|
||||||
@@ -39,28 +151,262 @@
|
|||||||
];
|
];
|
||||||
size = 11.0;
|
size = 11.0;
|
||||||
};
|
};
|
||||||
|
# Bar background + workspace buttons -- Catppuccin Mocha. The mantle
|
||||||
|
# background matches i3status-rust's idle_bg below for a uniform strip.
|
||||||
|
colors = {
|
||||||
|
background = "#${ctp.mantle}";
|
||||||
|
statusline = "#${ctp.text}";
|
||||||
|
separator = "#${ctp.surface0}";
|
||||||
|
focusedWorkspace = {
|
||||||
|
border = "#${ctp.blue}";
|
||||||
|
background = "#${ctp.blue}";
|
||||||
|
text = "#${ctp.base}";
|
||||||
|
};
|
||||||
|
activeWorkspace = {
|
||||||
|
border = "#${ctp.surface0}";
|
||||||
|
background = "#${ctp.surface0}";
|
||||||
|
text = "#${ctp.text}";
|
||||||
|
};
|
||||||
|
inactiveWorkspace = {
|
||||||
|
border = "#${ctp.mantle}";
|
||||||
|
background = "#${ctp.mantle}";
|
||||||
|
text = "#${ctp.subtext0}";
|
||||||
|
};
|
||||||
|
urgentWorkspace = {
|
||||||
|
border = "#${ctp.red}";
|
||||||
|
background = "#${ctp.red}";
|
||||||
|
text = "#${ctp.base}";
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
keybindings = lib.mkOptionDefault {
|
# NB: this whole set is wrapped in mkOptionDefault so it MERGES with the
|
||||||
"${modifier}+l" = "exec ${pkgs.swaylock}/bin/swaylock -f";
|
# home-manager module's default keybindings (same priority) rather than
|
||||||
"Print" = "exec ${pkgs.grim}/bin/grim ~/screenshot-$(date +%F-%H%M%S).png";
|
# replacing them. Do not wrap it in mkMerge with a normal-priority attr --
|
||||||
"XF86MonBrightnessUp" = "exec ${pkgs.brightnessctl}/bin/brightnessctl set 5%+";
|
# that makes the normal-priority def win and silently drops every default
|
||||||
"XF86MonBrightnessDown" = "exec ${pkgs.brightnessctl}/bin/brightnessctl set 5%-";
|
# bind (terminal, movement, workspaces, ...).
|
||||||
"XF86AudioRaiseVolume" = "exec ${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+";
|
keybindings = lib.mkOptionDefault (
|
||||||
"XF86AudioLowerVolume" = "exec ${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-";
|
{
|
||||||
"XF86AudioMute" = "exec ${pkgs.wireplumber}/bin/wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle";
|
# Launcher on Mod+Space. mkForce overrides the module's own default
|
||||||
|
# Mod+Space (focus mode_toggle); a plain value would conflict with it
|
||||||
|
# at equal priority. Mod+d also still runs the launcher (module default).
|
||||||
|
"${modifier}+space" = lib.mkForce "exec ${menu}";
|
||||||
|
# File manager. mkForce overrides the module default (layout toggle split).
|
||||||
|
"${modifier}+e" = lib.mkForce "exec ${pkgs.nemo}/bin/nemo";
|
||||||
|
"${modifier}+l" = "exec ${pkgs.swaylock}/bin/swaylock -f";
|
||||||
|
|
||||||
|
# Cycle workspaces.
|
||||||
|
"${modifier}+z" = "workspace prev";
|
||||||
|
"${modifier}+x" = "workspace next";
|
||||||
|
|
||||||
|
# focus mode_toggle (tiling <-> floating focus) -- re-homed off
|
||||||
|
# Mod+Space, which is now the launcher.
|
||||||
|
"${modifier}+Mod1+space" = "focus mode_toggle";
|
||||||
|
|
||||||
|
# Enter the binding-mode submenus defined above.
|
||||||
|
"${modifier}+y" = "mode \"${layoutMode}\"";
|
||||||
|
"${modifier}+Shift+x" = "mode \"${systemMode}\"";
|
||||||
|
|
||||||
|
# Clipboard history: pick a past entry through fuzzel (clipman stores
|
||||||
|
# it -- see services.clipman below).
|
||||||
|
"${modifier}+c" =
|
||||||
|
"exec ${pkgs.clipman}/bin/clipman pick -t CUSTOM --tool-args=\"${pkgs.fuzzel}/bin/fuzzel --dmenu\"";
|
||||||
|
|
||||||
|
# Screenshots -> swappy editor: Print = drag a region, Shift+Print =
|
||||||
|
# the focused window.
|
||||||
|
"Print" =
|
||||||
|
"exec ${pkgs.grim}/bin/grim -g \"$(${pkgs.slurp}/bin/slurp)\" - | ${pkgs.swappy}/bin/swappy -f -";
|
||||||
|
"Shift+Print" = "exec ${screenshotWindow}";
|
||||||
|
|
||||||
|
"XF86AudioRaiseVolume" = "exec ${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+";
|
||||||
|
"XF86AudioLowerVolume" = "exec ${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-";
|
||||||
|
"XF86AudioMute" = "exec ${pkgs.wireplumber}/bin/wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle";
|
||||||
|
"XF86AudioMicMute" = "exec ${pkgs.wireplumber}/bin/wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle";
|
||||||
|
|
||||||
|
# Media keys (MPRIS via playerctl).
|
||||||
|
"XF86AudioPlay" = "exec ${pkgs.playerctl}/bin/playerctl play-pause";
|
||||||
|
"XF86AudioNext" = "exec ${pkgs.playerctl}/bin/playerctl next";
|
||||||
|
"XF86AudioPrev" = "exec ${pkgs.playerctl}/bin/playerctl previous";
|
||||||
|
}
|
||||||
|
# Screen backlight: laptops only (no internal backlight on a desktop).
|
||||||
|
// lib.optionalAttrs portable {
|
||||||
|
"XF86MonBrightnessUp" = "exec ${pkgs.brightnessctl}/bin/brightnessctl set 5%+";
|
||||||
|
"XF86MonBrightnessDown" = "exec ${pkgs.brightnessctl}/bin/brightnessctl set 5%-";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Terminal: Catppuccin Mocha. foot reads ~/.config/foot/foot.ini; the Sway
|
||||||
|
# `terminal` above still launches the same binary, now themed.
|
||||||
|
programs.foot = {
|
||||||
|
enable = true;
|
||||||
|
# foot 1.27: the bare [colors] section is deprecated in favour of
|
||||||
|
# [colors-dark] (the default theme), and the cursor colour moved out of
|
||||||
|
# [cursor] (where `color` is now rejected) into a `cursor` key here, written
|
||||||
|
# "<text> <cursor>" (man foot.ini(5): "ff0000 00ff00" => green cursor, red
|
||||||
|
# text). Only colors-dark is needed; we never set initial-color-theme=light.
|
||||||
|
settings = {
|
||||||
|
main = {
|
||||||
|
# Nerd Font: monospace plus the powerline/Nerd glyphs the tmux
|
||||||
|
# statusline uses (otherwise they render as blank/"?").
|
||||||
|
font = "JetBrainsMono Nerd Font:size=11";
|
||||||
|
# Advertise as xterm-256color so remote hosts without foot's terminfo
|
||||||
|
# still behave (tmux re-adds foot's RGB/sync/etc. features -- see
|
||||||
|
# shell.nix). The [main] section is the unheadered top of foot.ini.
|
||||||
|
term = "xterm-256color";
|
||||||
|
};
|
||||||
|
scrollback.lines = 100000;
|
||||||
|
"colors-dark" = {
|
||||||
|
background = ctp.base;
|
||||||
|
foreground = ctp.text;
|
||||||
|
regular0 = ctp.surface1;
|
||||||
|
regular1 = ctp.red;
|
||||||
|
regular2 = ctp.green;
|
||||||
|
regular3 = ctp.yellow;
|
||||||
|
regular4 = ctp.blue;
|
||||||
|
regular5 = ctp.pink;
|
||||||
|
regular6 = ctp.teal;
|
||||||
|
regular7 = ctp.subtext1;
|
||||||
|
bright0 = ctp.surface2;
|
||||||
|
bright1 = ctp.red;
|
||||||
|
bright2 = ctp.green;
|
||||||
|
bright3 = ctp.yellow;
|
||||||
|
bright4 = ctp.blue;
|
||||||
|
bright5 = ctp.pink;
|
||||||
|
bright6 = ctp.teal;
|
||||||
|
bright7 = ctp.subtext0;
|
||||||
|
"selection-foreground" = ctp.base;
|
||||||
|
"selection-background" = ctp.rosewater;
|
||||||
|
cursor = "${ctp.base} ${ctp.rosewater}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Clipboard history: a user service runs `wl-paste --watch clipman store`,
|
||||||
|
# bound to the Wayland session, so copies persist and Mod+c (above) can pick
|
||||||
|
# an old entry through fuzzel.
|
||||||
|
services.clipman.enable = true;
|
||||||
|
|
||||||
|
# Polkit authentication agent. programs.sway (system) enables the polkit
|
||||||
|
# daemon but no agent, so GUI privilege prompts (nemo mounting a disk,
|
||||||
|
# NetworkManager/blueman editing a system resource) would otherwise fail
|
||||||
|
# silently. lxqt-policykit is a small, toolkit-light agent; bind it to the
|
||||||
|
# Sway session so it starts and stops with the desktop.
|
||||||
|
systemd.user.services.polkit-lxqt = {
|
||||||
|
Unit = {
|
||||||
|
Description = "lxqt-policykit polkit authentication agent";
|
||||||
|
PartOf = [ "graphical-session.target" ];
|
||||||
|
After = [ "graphical-session.target" ];
|
||||||
|
};
|
||||||
|
Service = {
|
||||||
|
ExecStart = "${pkgs.lxqt.lxqt-policykit}/bin/lxqt-policykit-agent";
|
||||||
|
Restart = "on-failure";
|
||||||
|
};
|
||||||
|
Install.WantedBy = [ "sway-session.target" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Output/display management. Reacts to hotplug and applies per-display
|
||||||
|
# mode/scale/position. Profiles are hardware-specific: the safe default below
|
||||||
|
# just enables the internal laptop panel; add docked/desktop profiles with the
|
||||||
|
# real identifiers from `swaymsg -t get_outputs` (e.g. the Mac Pro's Apple
|
||||||
|
# Cinema Display with its scale, or a docked laptop + external monitor).
|
||||||
|
services.kanshi = {
|
||||||
|
enable = true;
|
||||||
|
settings = [
|
||||||
|
{
|
||||||
|
profile.name = "undocked";
|
||||||
|
profile.outputs = [
|
||||||
|
{
|
||||||
|
criteria = "eDP-1";
|
||||||
|
status = "enable";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
# Example to copy per host (fill in real criteria/mode/scale/position):
|
||||||
|
# {
|
||||||
|
# profile.name = "desktop";
|
||||||
|
# profile.outputs = [
|
||||||
|
# { criteria = "Apple Computer Inc Cinema HD ..."; mode = "2560x1600"; scale = 1.0; position = "0,0"; status = "enable"; }
|
||||||
|
# ];
|
||||||
|
# }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Night light. Manual location (no geoclue dependency); adjust the coordinates
|
||||||
|
# to taste. Warmer at night, neutral by day.
|
||||||
|
services.gammastep = {
|
||||||
|
enable = true;
|
||||||
|
provider = "manual";
|
||||||
|
latitude = 51.5;
|
||||||
|
longitude = -0.13; # London-ish; set to your actual location
|
||||||
|
temperature = {
|
||||||
|
day = 6500;
|
||||||
|
night = 3700;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# fuzzel: the dmenu picker used by clipman, themed Catppuccin Mocha to match
|
||||||
|
# (fuzzel colours are RRGGBBAA -- 8 hex digits).
|
||||||
|
programs.fuzzel = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
main = {
|
||||||
|
font = "Noto Sans:size=12";
|
||||||
|
prompt = "\"clipboard \"";
|
||||||
|
};
|
||||||
|
border = {
|
||||||
|
width = 2;
|
||||||
|
radius = 8;
|
||||||
|
};
|
||||||
|
colors = {
|
||||||
|
background = "${ctp.base}f0";
|
||||||
|
text = "${ctp.text}ff";
|
||||||
|
prompt = "${ctp.subtext0}ff";
|
||||||
|
input = "${ctp.text}ff";
|
||||||
|
match = "${ctp.blue}ff";
|
||||||
|
selection = "${ctp.surface1}ff";
|
||||||
|
selection-text = "${ctp.text}ff";
|
||||||
|
selection-match = "${ctp.blue}ff";
|
||||||
|
border = "${ctp.blue}ff";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
programs.swaylock = {
|
programs.swaylock = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
# Catppuccin Mocha (swaylock colours are hex without "#").
|
||||||
settings = {
|
settings = {
|
||||||
color = "1e1e2e";
|
color = ctp.base;
|
||||||
indicator-radius = 100;
|
indicator-radius = 100;
|
||||||
indicator-thickness = 7;
|
indicator-thickness = 7;
|
||||||
show-failed-attempts = true;
|
show-failed-attempts = true;
|
||||||
|
font = "Noto Sans";
|
||||||
|
|
||||||
|
inside-color = ctp.base;
|
||||||
|
inside-clear-color = ctp.base;
|
||||||
|
inside-ver-color = ctp.base;
|
||||||
|
inside-wrong-color = ctp.base;
|
||||||
|
|
||||||
|
ring-color = ctp.surface1;
|
||||||
|
ring-clear-color = ctp.yellow;
|
||||||
|
ring-ver-color = ctp.blue;
|
||||||
|
ring-wrong-color = ctp.red;
|
||||||
|
|
||||||
|
key-hl-color = ctp.blue;
|
||||||
|
bs-hl-color = ctp.red;
|
||||||
|
|
||||||
|
line-color = ctp.base;
|
||||||
|
line-clear-color = ctp.base;
|
||||||
|
line-ver-color = ctp.base;
|
||||||
|
line-wrong-color = ctp.base;
|
||||||
|
separator-color = "00000000";
|
||||||
|
|
||||||
|
text-color = ctp.text;
|
||||||
|
text-clear-color = ctp.text;
|
||||||
|
text-ver-color = ctp.text;
|
||||||
|
text-wrong-color = ctp.text;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -86,16 +432,30 @@
|
|||||||
|
|
||||||
services.dunst = {
|
services.dunst = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
# Catppuccin Mocha notifications (dunst colours need a leading "#").
|
||||||
settings = {
|
settings = {
|
||||||
global = {
|
global = {
|
||||||
font = "Noto Sans 11";
|
font = "Noto Sans 11";
|
||||||
frame_color = "#89b4fa";
|
frame_color = "#${ctp.blue}";
|
||||||
|
frame_width = 2;
|
||||||
separator_color = "frame";
|
separator_color = "frame";
|
||||||
offset = "10x10";
|
offset = "10x10";
|
||||||
corner_radius = 5;
|
corner_radius = 5;
|
||||||
};
|
};
|
||||||
|
urgency_low = {
|
||||||
|
background = "#${ctp.base}";
|
||||||
|
foreground = "#${ctp.text}";
|
||||||
|
frame_color = "#${ctp.surface1}";
|
||||||
|
};
|
||||||
|
urgency_normal = {
|
||||||
|
background = "#${ctp.base}";
|
||||||
|
foreground = "#${ctp.text}";
|
||||||
|
frame_color = "#${ctp.blue}";
|
||||||
|
};
|
||||||
urgency_critical = {
|
urgency_critical = {
|
||||||
frame_color = "#fab387";
|
background = "#${ctp.base}";
|
||||||
|
foreground = "#${ctp.text}";
|
||||||
|
frame_color = "#${ctp.peach}";
|
||||||
timeout = 0;
|
timeout = 0;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -104,8 +464,29 @@
|
|||||||
programs.i3status-rust = {
|
programs.i3status-rust = {
|
||||||
enable = true;
|
enable = true;
|
||||||
bars.default = {
|
bars.default = {
|
||||||
theme = "gruvbox-dark";
|
# Catppuccin Mocha: a flat "plain" base recoloured via overrides. Idle
|
||||||
|
# blocks sit on mantle (matching the Sway bar background) with light text;
|
||||||
|
# only warning/critical states get a loud tinted background. The `theme`
|
||||||
|
# bar option is shallow-merged away by `settings.theme`, so set the base
|
||||||
|
# theme and its overrides together here.
|
||||||
icons = "awesome6";
|
icons = "awesome6";
|
||||||
|
settings.theme = {
|
||||||
|
theme = "plain";
|
||||||
|
overrides = {
|
||||||
|
idle_bg = "#${ctp.mantle}";
|
||||||
|
idle_fg = "#${ctp.text}";
|
||||||
|
info_bg = "#${ctp.mantle}";
|
||||||
|
info_fg = "#${ctp.blue}";
|
||||||
|
good_bg = "#${ctp.mantle}";
|
||||||
|
good_fg = "#${ctp.green}";
|
||||||
|
warning_bg = "#${ctp.peach}";
|
||||||
|
warning_fg = "#${ctp.base}";
|
||||||
|
critical_bg = "#${ctp.red}";
|
||||||
|
critical_fg = "#${ctp.base}";
|
||||||
|
separator_bg = "#${ctp.mantle}";
|
||||||
|
separator_fg = "#${ctp.surface1}";
|
||||||
|
};
|
||||||
|
};
|
||||||
blocks = [
|
blocks = [
|
||||||
{
|
{
|
||||||
block = "disk_space";
|
block = "disk_space";
|
||||||
@@ -120,8 +501,20 @@
|
|||||||
block = "cpu";
|
block = "cpu";
|
||||||
interval = 2;
|
interval = 2;
|
||||||
}
|
}
|
||||||
{ block = "sound"; }
|
]
|
||||||
{ block = "battery"; }
|
# Desktop-only: CPU temperature and wired network throughput, in place
|
||||||
|
# of the laptop's battery readout.
|
||||||
|
++ lib.optionals (!portable) [
|
||||||
|
{
|
||||||
|
block = "temperature";
|
||||||
|
interval = 5;
|
||||||
|
format = " $icon $average avg, $max max ";
|
||||||
|
}
|
||||||
|
{ block = "net"; }
|
||||||
|
]
|
||||||
|
++ [ { block = "sound"; } ]
|
||||||
|
++ lib.optional portable { block = "battery"; }
|
||||||
|
++ [
|
||||||
{
|
{
|
||||||
block = "time";
|
block = "time";
|
||||||
interval = 5;
|
interval = 5;
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
# Home-manager module for the work (EDaaS/WSL) profile: corporate git signing,
|
||||||
|
# work toolchain packages and tmux tweaks. Imported only by the work host.
|
||||||
|
{ pkgs, lib, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
# Host-scoped extras for this machine only (the EDaaS/WSL host).
|
||||||
|
imports = [
|
||||||
|
./renovate-review.nix # daily headless Renovate PR review (systemd user timer)
|
||||||
|
];
|
||||||
|
|
||||||
|
# The work box keeps its own (corporate) ~/.ssh/config; don't let the personal
|
||||||
|
# programs.ssh (shell.nix) take it over. The ssh-agent below still runs.
|
||||||
|
programs.ssh.enable = lib.mkForce false;
|
||||||
|
|
||||||
|
programs.git = {
|
||||||
|
settings = {
|
||||||
|
commit.gpgsign = true;
|
||||||
|
tag.gpgsign = true;
|
||||||
|
gpg.format = "ssh";
|
||||||
|
user.signingkey = "key::ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAJMVgeRKnfX1G8coU3nAobI485aeUpGTMqH7+zbKI8o emma.thorpe@cloud.com";
|
||||||
|
user.email = "emma.thorpe@citrix.com";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
home.packages = [
|
||||||
|
pkgs.kubectl
|
||||||
|
pkgs.argo-rollouts
|
||||||
|
pkgs.tenv
|
||||||
|
pkgs.kubernetes-helm
|
||||||
|
pkgs.azure-cli
|
||||||
|
pkgs.kubelogin
|
||||||
|
pkgs.curl
|
||||||
|
pkgs.notation
|
||||||
|
pkgs.powershell
|
||||||
|
pkgs.nuget
|
||||||
|
pkgs.gedit
|
||||||
|
pkgs.lens
|
||||||
|
pkgs.python3
|
||||||
|
pkgs.gnumake
|
||||||
|
pkgs.gcc
|
||||||
|
pkgs.libiconv
|
||||||
|
pkgs.autoconf
|
||||||
|
pkgs.automake
|
||||||
|
pkgs.pkg-config
|
||||||
|
pkgs.wget
|
||||||
|
pkgs.google-cloud-sdk
|
||||||
|
# Day-to-day Kubernetes / Helm / Terraform accelerators for this box.
|
||||||
|
pkgs.k9s # cluster TUI
|
||||||
|
pkgs.kubectx # kubectx + kubens (context/namespace switch)
|
||||||
|
pkgs.stern # multi-pod log tail
|
||||||
|
pkgs.dyff # semantic YAML/manifest diffs (Helm release drift)
|
||||||
|
pkgs.tflint # Terraform linter (catches what terraformls won't)
|
||||||
|
pkgs.terraform-docs # generate Terraform module docs
|
||||||
|
pkgs.yq-go # jq for YAML
|
||||||
|
];
|
||||||
|
services.ssh-agent.enable = true;
|
||||||
|
home.shellAliases = {
|
||||||
|
docker = "/run/current-system/sw/bin/docker";
|
||||||
|
};
|
||||||
|
programs.tmux = {
|
||||||
|
extraConfig = ''
|
||||||
|
set -g status-right "#(/run/current-system/sw/bin/bash $HOME/code/kube-tmux/kube.tmux 250 red black)"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
programs.go = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# LSP servers only relevant to work: C# (omnisharp) and Helm charts (helm_ls).
|
||||||
|
# The shared editor (lyrathorpe/home/editor.nix) carries the universal ones;
|
||||||
|
# these are gated to this host so the heavy omnisharp closure stays off the
|
||||||
|
# personal machines. Tree-sitter grammars (highlighting) remain global there.
|
||||||
|
programs.nixvim.plugins.lsp.servers = {
|
||||||
|
omnisharp.enable = true;
|
||||||
|
helm_ls.enable = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
+119
-16
@@ -1,41 +1,46 @@
|
|||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
options,
|
|
||||||
pkgs,
|
pkgs,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.features.swayDesktop;
|
cfg = config.features.swayDesktop;
|
||||||
|
# Catppuccin Mocha (shared with the Sway desktop, see lyrathorpe/home/sway.nix).
|
||||||
|
ctp = import ./catppuccin-mocha.nix;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options = {
|
# The features.swayDesktop.enable option is declared in
|
||||||
features.swayDesktop.enable = lib.mkEnableOption "Enable Sway Desktop";
|
# system/modules/features.nix (so headless hosts can read/set it without
|
||||||
};
|
# importing this module). This module only provides its implementation.
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
programs.sway = {
|
programs.sway = {
|
||||||
enable = true;
|
enable = true;
|
||||||
wrapperFeatures.gtk = true;
|
wrapperFeatures.gtk = true;
|
||||||
extraSessionCommands = ''
|
extraSessionCommands = ''
|
||||||
# QT
|
# QT
|
||||||
export QT_QPA_PLATFORM="wayland;xcb"
|
export QT_QPA_PLATFORM="wayland;xcb"
|
||||||
export QT_QPA_PLATFORMTHEME=qt5ct
|
export QT_QPA_PLATFORMTHEME=qt5ct
|
||||||
# SDL
|
# SDL
|
||||||
export SDL_VIDEODRIVER=wayland
|
export SDL_VIDEODRIVER=wayland
|
||||||
# Java
|
# Java
|
||||||
export _JAVA_AWT_WM_NONREPARENTING=1
|
export _JAVA_AWT_WM_NONREPARENTING=1
|
||||||
# Misc
|
# Misc
|
||||||
export CLUTTER_BACKEND=wayland
|
export CLUTTER_BACKEND=wayland
|
||||||
export WINIT_UNIX_BACKEND=x11
|
export WINIT_UNIX_BACKEND=wayland
|
||||||
export MOZ_ENABLE_WAYLAND=1
|
export MOZ_ENABLE_WAYLAND=1
|
||||||
'';
|
'';
|
||||||
# Core Wayland utilities. The lock screen, idle daemon, status bar and
|
# Core Wayland utilities. The lock screen, idle daemon, status bar and
|
||||||
# notification daemon are configured per-user in home/sway.nix.
|
# notification daemon are configured per-user in home/sway.nix.
|
||||||
extraPackages = with pkgs; [
|
extraPackages = with pkgs; [
|
||||||
brightnessctl
|
brightnessctl
|
||||||
foot
|
foot
|
||||||
grim
|
grim
|
||||||
|
slurp # region selection for screenshots
|
||||||
|
swappy # screenshot annotation/save
|
||||||
|
jq # used by the focused-window screenshot bind
|
||||||
|
playerctl # MPRIS media keys
|
||||||
sway-launcher-desktop
|
sway-launcher-desktop
|
||||||
pavucontrol
|
pavucontrol
|
||||||
];
|
];
|
||||||
@@ -46,6 +51,104 @@ in
|
|||||||
font-awesome
|
font-awesome
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# Wayland login screen (replaces console/getty login on every Sway host).
|
||||||
|
# greetd runs ReGreet inside the cage kiosk compositor; the Sway session is
|
||||||
|
# offered automatically because programs.sway registers itself via
|
||||||
|
# services.displayManager.sessionPackages. Hosts that turn off
|
||||||
|
# features.swayDesktop (e.g. EDaaS) keep plain TTY login.
|
||||||
|
programs.regreet.enable = true;
|
||||||
|
# Theme the greeter to match the Sway desktop (Catppuccin Mocha). ReGreet is
|
||||||
|
# GTK; recolour via CSS (covering both libadwaita named colours and plain
|
||||||
|
# GTK node selectors) and use the same Noto Sans as the bar/notifications.
|
||||||
|
programs.regreet.font = {
|
||||||
|
name = "Noto Sans";
|
||||||
|
package = pkgs.noto-fonts;
|
||||||
|
size = 16;
|
||||||
|
};
|
||||||
|
programs.regreet.extraCss = ''
|
||||||
|
/* GTK4 Adwaita legacy names (what plain GTK4 actually references). */
|
||||||
|
@define-color theme_bg_color #${ctp.base};
|
||||||
|
@define-color theme_fg_color #${ctp.text};
|
||||||
|
@define-color theme_base_color #${ctp.mantle};
|
||||||
|
@define-color theme_text_color #${ctp.text};
|
||||||
|
@define-color theme_selected_bg_color #${ctp.blue};
|
||||||
|
@define-color theme_selected_fg_color #${ctp.base};
|
||||||
|
@define-color insensitive_bg_color #${ctp.mantle};
|
||||||
|
@define-color insensitive_fg_color #${ctp.overlay0};
|
||||||
|
@define-color borders #${ctp.surface1};
|
||||||
|
@define-color warning_color #${ctp.peach};
|
||||||
|
@define-color error_color #${ctp.red};
|
||||||
|
@define-color success_color #${ctp.green};
|
||||||
|
|
||||||
|
/* libadwaita names (inert on plain GTK4, kept for forward-compat). */
|
||||||
|
@define-color window_bg_color #${ctp.base};
|
||||||
|
@define-color window_fg_color #${ctp.text};
|
||||||
|
@define-color view_bg_color #${ctp.mantle};
|
||||||
|
@define-color view_fg_color #${ctp.text};
|
||||||
|
@define-color card_bg_color #${ctp.surface0};
|
||||||
|
@define-color card_fg_color #${ctp.text};
|
||||||
|
@define-color accent_bg_color #${ctp.blue};
|
||||||
|
@define-color accent_fg_color #${ctp.base};
|
||||||
|
@define-color accent_color #${ctp.blue};
|
||||||
|
@define-color destructive_bg_color #${ctp.red};
|
||||||
|
@define-color destructive_fg_color #${ctp.base};
|
||||||
|
|
||||||
|
window {
|
||||||
|
background-color: #${ctp.base};
|
||||||
|
color: #${ctp.text};
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
color: #${ctp.text};
|
||||||
|
}
|
||||||
|
|
||||||
|
entry {
|
||||||
|
background-color: #${ctp.surface0};
|
||||||
|
color: #${ctp.text};
|
||||||
|
border: 1px solid #${ctp.surface1};
|
||||||
|
}
|
||||||
|
|
||||||
|
entry:focus-within {
|
||||||
|
border-color: #${ctp.blue};
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
combobox button {
|
||||||
|
background-color: #${ctp.surface0};
|
||||||
|
color: #${ctp.text};
|
||||||
|
border: 1px solid #${ctp.surface1};
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #${ctp.surface1};
|
||||||
|
}
|
||||||
|
|
||||||
|
button:active,
|
||||||
|
button:checked {
|
||||||
|
background-color: #${ctp.blue};
|
||||||
|
color: #${ctp.base};
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
# cage reads the XKB_* environment at startup, so force the greeter onto the
|
||||||
|
# same Dvorak layout as the Sway session (home/sway.nix) -- otherwise the
|
||||||
|
# password field would be QWERTY. Dvorak is the "us" layout's variant, NOT a
|
||||||
|
# layout of its own: "dvorak" alone has no symbols/ file, so the keymap
|
||||||
|
# fails to compile and the greeter ends up with no keyboard at all (the
|
||||||
|
# greeter has no fallback, unlike a running Sway session). This overrides the
|
||||||
|
# greetd command regreet sets with mkDefault.
|
||||||
|
services.greetd.settings.default_session.command =
|
||||||
|
let
|
||||||
|
greeter = pkgs.writeShellScript "regreet-cage" ''
|
||||||
|
export XKB_DEFAULT_LAYOUT=us
|
||||||
|
export XKB_DEFAULT_VARIANT=dvorak
|
||||||
|
# ReGreet is plain GTK4 (no libadwaita); force the dark Adwaita variant
|
||||||
|
# so the extraCss accents sit on a dark base instead of light Adwaita.
|
||||||
|
export GTK_THEME=Adwaita:dark
|
||||||
|
exec ${pkgs.dbus}/bin/dbus-run-session ${lib.getExe pkgs.cage} -s -- ${lib.getExe config.programs.regreet.package}
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
"${greeter}";
|
||||||
|
|
||||||
# Desktop portals: enables screen sharing (wlroots) and native file pickers
|
# Desktop portals: enables screen sharing (wlroots) and native file pickers
|
||||||
# for Wayland apps such as Element and Firefox.
|
# for Wayland apps such as Element and Firefox.
|
||||||
xdg.portal = {
|
xdg.portal = {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
pkgs,
|
pkgs,
|
||||||
inputs,
|
|
||||||
lib,
|
lib,
|
||||||
username,
|
username,
|
||||||
fullName,
|
fullName,
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# statix lint config. Two default lints are disabled because they flag this
|
||||||
|
# repo's intentional house style, not bugs:
|
||||||
|
# repeated_keys - we use `foo.a = ...; foo.b = ...;` (dotted) over nesting.
|
||||||
|
# empty_pattern - module files use `{ ... }:` / `{ }:` deliberately.
|
||||||
|
disabled = [
|
||||||
|
"repeated_keys",
|
||||||
|
"empty_pattern",
|
||||||
|
]
|
||||||
@@ -6,6 +6,11 @@
|
|||||||
{
|
{
|
||||||
programs.zsh.enable = true;
|
programs.zsh.enable = true;
|
||||||
|
|
||||||
|
# Install the Nerd Font into /Library/Fonts so iTerm2 can use it (set it in
|
||||||
|
# iTerm2 -> Settings -> Profiles -> Text -> Font: "JetBrainsMono Nerd Font").
|
||||||
|
# Provides the powerline/Nerd glyphs the tmux statusline draws.
|
||||||
|
fonts.packages = [ pkgs.nerd-fonts.jetbrains-mono ];
|
||||||
|
|
||||||
# CLI tooling sourced from nixpkgs instead of Homebrew formulae. Pure library
|
# CLI tooling sourced from nixpkgs instead of Homebrew formulae. Pure library
|
||||||
# dependencies are omitted; nix pulls them into closures automatically.
|
# dependencies are omitted; nix pulls them into closures automatically.
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
@@ -75,7 +80,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
# Declarative Homebrew for packages with no nixpkgs equivalent or that must be
|
# Declarative Homebrew for packages with no nixpkgs equivalent or that must be
|
||||||
# the vendor build (GUI casks, Mac App Store apps).
|
# the vendor build (GUI casks).
|
||||||
homebrew = {
|
homebrew = {
|
||||||
enable = true;
|
enable = true;
|
||||||
onActivation = {
|
onActivation = {
|
||||||
@@ -92,6 +97,7 @@
|
|||||||
"llvm@21"
|
"llvm@21"
|
||||||
"lld@21"
|
"lld@21"
|
||||||
"python@3.14"
|
"python@3.14"
|
||||||
|
"dosbox-staging"
|
||||||
];
|
];
|
||||||
# GUI applications. macOS app bundles are managed as casks; nixpkgs darwin
|
# GUI applications. macOS app bundles are managed as casks; nixpkgs darwin
|
||||||
# GUI support is unreliable, so these stay on brew for continuity.
|
# GUI support is unreliable, so these stay on brew for continuity.
|
||||||
@@ -131,18 +137,45 @@
|
|||||||
"vscodium"
|
"vscodium"
|
||||||
"winbox"
|
"winbox"
|
||||||
];
|
];
|
||||||
masApps = {
|
# Mac App Store apps are not managed declaratively: nix-darwin 26.05 forces
|
||||||
Amphetamine = 937984704;
|
# activation to run as root, and `mas` cannot reach the App Store session
|
||||||
"Apple Configurator" = 1037126344;
|
# from root, so installs silently fail. Install them by hand with
|
||||||
"Game Controller Tester" = 1500593102;
|
# `mas install <id>` from a GUI Terminal (the `mas` CLI is in
|
||||||
"Home Assistant" = 1099568401;
|
# environment.systemPackages above).
|
||||||
Infuse = 1136220934;
|
};
|
||||||
Keynote = 409183694;
|
|
||||||
Numbers = 409203825;
|
# Touch ID authorises sudo (and darwin-rebuild's sudo prompt) instead of a
|
||||||
Pages = 409201541;
|
# typed password. sudo_local keeps the change in /etc/pam.d/sudo_local so it
|
||||||
PDFgear = 6469021132;
|
# survives macOS updates. reattach pulls in pam_reattach: pam_tid (Touch ID)
|
||||||
PL2303Serial = 1624835354;
|
# otherwise fails inside tmux/screen because the process is detached from the
|
||||||
WireGuard = 1451685025;
|
# GUI login session -- and terminals here auto-start tmux, so it is required.
|
||||||
|
security.pam.services.sudo_local = {
|
||||||
|
touchIdAuth = true;
|
||||||
|
reattach = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Declarative macOS UI defaults -- the main reason to run nix-darwin beyond
|
||||||
|
# package management. Applied on activation; all reversible.
|
||||||
|
system.defaults = {
|
||||||
|
dock = {
|
||||||
|
show-recents = false;
|
||||||
|
mru-spaces = false; # don't reorder spaces by use
|
||||||
|
};
|
||||||
|
finder = {
|
||||||
|
AppleShowAllExtensions = true;
|
||||||
|
ShowPathbar = true;
|
||||||
|
FXPreferredViewStyle = "Nlsv"; # list view
|
||||||
|
_FXShowPosixPathInTitle = true;
|
||||||
|
};
|
||||||
|
NSGlobalDomain = {
|
||||||
|
AppleInterfaceStyle = "Dark";
|
||||||
|
ApplePressAndHoldEnabled = false; # key-repeat instead of the accent popup
|
||||||
|
InitialKeyRepeat = 15;
|
||||||
|
KeyRepeat = 2;
|
||||||
|
};
|
||||||
|
trackpad = {
|
||||||
|
Clicking = true; # tap to click
|
||||||
|
TrackpadThreeFingerDrag = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
enable = true;
|
enable = true;
|
||||||
defaultUser = "emmathorpe";
|
defaultUser = "emmathorpe";
|
||||||
wslConf.automount.root = "/mnt";
|
wslConf.automount.root = "/mnt";
|
||||||
wslConf.interop.appendWindowsPath = false;
|
wslConf.interop.appendWindowsPath = true;
|
||||||
|
wslConf.interop.enabled = true;
|
||||||
wslConf.network.generateHosts = false;
|
wslConf.network.generateHosts = false;
|
||||||
startMenuLaunchers = true;
|
startMenuLaunchers = true;
|
||||||
docker-desktop.enable = false;
|
docker-desktop.enable = false;
|
||||||
@@ -40,13 +41,34 @@
|
|||||||
autoPrune.enable = true;
|
autoPrune.enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Match the flake's nixosConfigurations attribute name so `nh os switch`
|
||||||
|
# (which selects by the local hostname) resolves without an explicit
|
||||||
|
# -H/--hostname flag. The default would otherwise be the stock NixOS "nixos".
|
||||||
|
networking.hostName = "emmathorpe-edaas";
|
||||||
|
|
||||||
networking.resolvconf.enable = false;
|
networking.resolvconf.enable = false;
|
||||||
|
|
||||||
|
# Drop the systemd-ssh-proxy Include from the generated /etc/ssh/ssh_config.
|
||||||
|
# The NixOS-WSL store is a read-only VHD whose files are owned by nobody
|
||||||
|
# (65534), not root. OpenSSH permission-checks Include'd config files and
|
||||||
|
# rejects any not owned by root or the caller, so the default include fails
|
||||||
|
# with "Bad owner or permissions" and breaks ssh/git for every command. The
|
||||||
|
# proxy plugin only matters for `ssh unix/…` / `vsock` to local machined VMs,
|
||||||
|
# which WSL does not use.
|
||||||
|
programs.ssh.systemd-ssh-proxy.enable = false;
|
||||||
|
|
||||||
## 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"'';
|
||||||
|
|
||||||
features.swayDesktop.enable = false;
|
features.swayDesktop.enable = false;
|
||||||
programs.nix-ld.enable = true;
|
|
||||||
|
# Keep this user's systemd --user instance running without an open login
|
||||||
|
# session, so the home-manager user timer (renovate-review.nix) fires on
|
||||||
|
# schedule even when no terminal is attached. On WSL the timer still only runs
|
||||||
|
# while the distro itself is up; Persistent=true catches up a missed run at
|
||||||
|
# next start.
|
||||||
|
users.users.emmathorpe.linger = true;
|
||||||
|
# programs.nix-ld is enabled for all NixOS hosts in common-nixos.nix.
|
||||||
# This value determines the NixOS release from which the default
|
# This value determines the NixOS release from which the default
|
||||||
# settings for stateful data, like file locations and database versions
|
# settings for stateful data, like file locations and database versions
|
||||||
# on your system were taken. It's perfectly fine and recommended to leave
|
# on your system were taken. It's perfectly fine and recommended to leave
|
||||||
|
|||||||
@@ -1,94 +1,37 @@
|
|||||||
# Edit this configuration file to define what should be installed on
|
# MacBook Pro (Apple Silicon, Asahi NixOS). Shared laptop options live in
|
||||||
# your system. Help is available in the configuration.nix(5) man page, on
|
# ../../modules/laptop.nix; only host-specific settings are here.
|
||||||
# https://search.nixos.org/options and in the NixOS manual (`nixos-help`).
|
{ pkgs, ... }:
|
||||||
|
|
||||||
{
|
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
|
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
# Include the results of the hardware scan.
|
|
||||||
./hardware-configuration.nix
|
./hardware-configuration.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
# Use the systemd-boot EFI boot loader.
|
# UEFI boot via systemd-boot. Asahi manages the EFI vars from macOS, so do not
|
||||||
|
# touch them from NixOS.
|
||||||
boot.loader.systemd-boot.enable = true;
|
boot.loader.systemd-boot.enable = true;
|
||||||
boot.loader.efi.canTouchEfiVariables = false;
|
boot.loader.efi.canTouchEfiVariables = false;
|
||||||
|
|
||||||
networking.hostName = "Emma-Asahi"; # Define your hostname.
|
networking.hostName = "Lyra-Asahi";
|
||||||
# Pick only one of the below networking options.
|
|
||||||
# networking.wireless.enable = true; # Enables wireless support via wpa_supplicant.
|
|
||||||
# networking.networkmanager.enable = true; # Easiest to use and most distros use this by default.
|
|
||||||
|
|
||||||
networking.wireless.iwd = {
|
# Audio (PipeWire) and the swaylock PAM stack are inherited from
|
||||||
enable = true;
|
# workstation.nix. hardware.enableRedistributableFirmware is also set there;
|
||||||
settings.General.EnableNetworkConfiguration = true;
|
# it is harmless here since Asahi supplies its own peripheral firmware below.
|
||||||
|
|
||||||
|
# Binary cache for the Asahi kernel/build artifacts, so the MBP pulls prebuilt
|
||||||
|
# outputs instead of compiling the Asahi kernel locally.
|
||||||
|
nix.settings = {
|
||||||
|
substituters = [ "https://nixos-apple-silicon.cachix.org" ];
|
||||||
|
trusted-public-keys = [
|
||||||
|
"nixos-apple-silicon.cachix.org-1:8psDu5SA5dAD7qA0zMy5UT292TxeEPzIz8VVEr2Js20="
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
# Set your time zone.
|
# Apple peripheral firmware (Wi-Fi/Bluetooth). The directory is gitignored and
|
||||||
time.timeZone = "Europe/London";
|
# populated out-of-band -- see README.
|
||||||
|
|
||||||
# Configure network proxy if necessary
|
|
||||||
# networking.proxy.default = "http://user:password@proxy:port/";
|
|
||||||
# networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
|
|
||||||
|
|
||||||
# Select internationalisation properties.
|
|
||||||
i18n.defaultLocale = "en_GB.UTF-8";
|
|
||||||
console = {
|
|
||||||
# font = "Lat2-Terminus16";
|
|
||||||
keyMap = "dvorak";
|
|
||||||
# useXkbConfig = true; # use xkb.options in tty.
|
|
||||||
};
|
|
||||||
|
|
||||||
# Enable the X11 windowing system.
|
|
||||||
# services.xserver.enable = true;
|
|
||||||
|
|
||||||
features.swayDesktop.enable = true;
|
|
||||||
|
|
||||||
# Allow swaylock to authenticate (no fingerprint reader on this machine).
|
|
||||||
security.pam.services.swaylock = { };
|
|
||||||
|
|
||||||
# Specify path to peripheral firmware files.
|
|
||||||
hardware.asahi.peripheralFirmwareDirectory = ../../modules/firmware;
|
hardware.asahi.peripheralFirmwareDirectory = ../../modules/firmware;
|
||||||
|
|
||||||
# Configure keymap in X11
|
|
||||||
# services.xserver.xkb.layout = "us";
|
|
||||||
# services.xserver.xkb.options = "eurosign:e,caps:escape";
|
|
||||||
|
|
||||||
# Enable CUPS to print documents.
|
|
||||||
# services.printing.enable = true;
|
|
||||||
|
|
||||||
# Enable sound.
|
|
||||||
# services.pulseaudio.enable = true;
|
|
||||||
# OR
|
|
||||||
# services.pipewire = {
|
|
||||||
# enable = true;
|
|
||||||
# pulse.enable = true;
|
|
||||||
# };
|
|
||||||
|
|
||||||
# Enable touchpad support (enabled default in most desktopManager).
|
|
||||||
# services.libinput.enable = true;
|
|
||||||
|
|
||||||
# Define a user account. Don't forget to set a password with ‘passwd’.
|
|
||||||
# users.users.alice = {
|
|
||||||
# isNormalUser = true;
|
|
||||||
# extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user.
|
|
||||||
# packages = with pkgs; [
|
|
||||||
# tree
|
|
||||||
# ];
|
|
||||||
# };
|
|
||||||
|
|
||||||
# programs.firefox.enable = true;
|
|
||||||
|
|
||||||
# List packages installed in system profile. To search, run:
|
|
||||||
# $ nix search wget
|
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
# wget
|
|
||||||
git
|
|
||||||
asahi-bless
|
asahi-bless
|
||||||
asahi-nvram
|
asahi-nvram
|
||||||
asahi-btsync
|
asahi-btsync
|
||||||
@@ -98,47 +41,6 @@
|
|||||||
iptables
|
iptables
|
||||||
];
|
];
|
||||||
|
|
||||||
# Some programs need SUID wrappers, can be configured further or are
|
# See `man configuration.nix` / the stateVersion docs before changing.
|
||||||
# started in user sessions.
|
system.stateVersion = "25.05";
|
||||||
# programs.mtr.enable = true;
|
|
||||||
# programs.gnupg.agent = {
|
|
||||||
# enable = true;
|
|
||||||
# enableSSHSupport = true;
|
|
||||||
# };
|
|
||||||
|
|
||||||
# List services that you want to enable:
|
|
||||||
|
|
||||||
# Enable the OpenSSH daemon.
|
|
||||||
# services.openssh.enable = true;
|
|
||||||
|
|
||||||
# Open ports in the firewall.
|
|
||||||
# networking.firewall.allowedTCPPorts = [ ... ];
|
|
||||||
# networking.firewall.allowedUDPPorts = [ ... ];
|
|
||||||
# Or disable the firewall altogether.
|
|
||||||
networking.firewall.enable = false;
|
|
||||||
|
|
||||||
# Copy the NixOS configuration file and link it from the resulting system
|
|
||||||
# (/run/current-system/configuration.nix). This is useful in case you
|
|
||||||
# accidentally delete configuration.nix.
|
|
||||||
#system.copySystemConfiguration = true;
|
|
||||||
|
|
||||||
# This option defines the first version of NixOS you have installed on this particular machine,
|
|
||||||
# and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions.
|
|
||||||
#
|
|
||||||
# Most users should NEVER change this value after the initial install, for any reason,
|
|
||||||
# even if you've upgraded your system to a new NixOS release.
|
|
||||||
#
|
|
||||||
# This value does NOT affect the Nixpkgs version your packages and OS are pulled from,
|
|
||||||
# so changing it will NOT upgrade your system - see https://nixos.org/manual/nixos/stable/#sec-upgrading for how
|
|
||||||
# to actually do that.
|
|
||||||
#
|
|
||||||
# This value being lower than the current NixOS release does NOT mean your system is
|
|
||||||
# out of date, out of support, or vulnerable.
|
|
||||||
#
|
|
||||||
# Do NOT change this value unless you have manually inspected all the changes it would make to your configuration,
|
|
||||||
# and migrated your data accordingly.
|
|
||||||
#
|
|
||||||
# For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion .
|
|
||||||
system.stateVersion = "25.05"; # Did you read the comment?
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# 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 `lyrathorpe/swaywm.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
|
||||||
|
```
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# Apple Mac Pro 3,1 (Early 2008, dual Xeon Harpertown, x86_64). Desktop host:
|
||||||
|
# shared graphical/wired options live in ../../modules/desktop.nix; only
|
||||||
|
# host-specific settings are here. Install notes (EFI booting, GPU, partitions):
|
||||||
|
# see ./README.md.
|
||||||
|
{ ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./hardware-configuration.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
boot.loader.systemd-boot.enable = true;
|
||||||
|
# Apple's EFI does not reliably support efibootmgr NVRAM writes; leave the
|
||||||
|
# firmware vars untouched.
|
||||||
|
boot.loader.efi.canTouchEfiVariables = false;
|
||||||
|
# Apple-EFI quirk: if the Mac does not pick up the bootloader at the boot
|
||||||
|
# picker, install it to the fallback path \EFI\BOOT\BOOTX64.EFI and/or
|
||||||
|
# "bless" the ESP from macOS. Uncomment to write the removable fallback path:
|
||||||
|
# boot.loader.efi.efiInstallAsRemovable = true;
|
||||||
|
|
||||||
|
networking.hostName = "MacPro31-NixOS";
|
||||||
|
|
||||||
|
# Elderly host: a compressed RAM swap softens memory pressure (earlyoom in
|
||||||
|
# workstation.nix is the backstop).
|
||||||
|
zramSwap.enable = true;
|
||||||
|
|
||||||
|
# This host accepts SSH, so open 22 (the firewall itself is enabled in
|
||||||
|
# workstation.nix with a default-deny policy).
|
||||||
|
services.openssh.enable = true;
|
||||||
|
networking.firewall.allowedTCPPorts = [ 22 ];
|
||||||
|
|
||||||
|
# Dual Harpertown Xeon microcode. Redistributable firmware (GPU/NIC blobs) is
|
||||||
|
# enabled in workstation.nix.
|
||||||
|
hardware.cpu.intel.updateMicrocode = true;
|
||||||
|
|
||||||
|
# GPU note: the stock card varies between units -- ATI Radeon HD 2600 XT or
|
||||||
|
# NVIDIA GeForce 8800 GT. Sway needs a working KMS/modesetting driver; do NOT
|
||||||
|
# install a proprietary blob here. Depending on the installed card, rely on
|
||||||
|
# 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 swaywm.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.
|
||||||
|
system.stateVersion = "26.05";
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# Do not modify this file! It was generated by ‘nixos-generate-config’
|
||||||
|
# and may be overwritten by future invocations. Please make changes
|
||||||
|
# to /etc/nixos/configuration.nix instead.
|
||||||
|
{ config, lib, pkgs, modulesPath, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports =
|
||||||
|
[ (modulesPath + "/installer/scan/not-detected.nix")
|
||||||
|
];
|
||||||
|
|
||||||
|
boot.initrd.availableKernelModules = [ "uhci_hcd" "ehci_pci" "ata_piix" "ahci" "firewire_ohci" "usb_storage" "usbhid" "sd_mod" ];
|
||||||
|
boot.initrd.kernelModules = [ "dm-snapshot" ];
|
||||||
|
boot.kernelModules = [ "kvm-intel" ];
|
||||||
|
boot.extraModulePackages = [ ];
|
||||||
|
|
||||||
|
fileSystems."/" =
|
||||||
|
{ device = "/dev/mapper/MacPro-Root";
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
|
||||||
|
fileSystems."/boot" =
|
||||||
|
{ device = "/dev/disk/by-uuid/0E9C-C099";
|
||||||
|
fsType = "vfat";
|
||||||
|
options = [ "fmask=0022" "dmask=0022" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
swapDevices =
|
||||||
|
[ { device = "/dev/disk/by-uuid/dec138b4-320f-4b69-acbc-3014a1032cbd"; }
|
||||||
|
];
|
||||||
|
networking.useDHCP = lib.mkDefault true;
|
||||||
|
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||||
|
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# Raspberry Pi 5 (`lyrathorpe-rpi5`)
|
||||||
|
|
||||||
|
Headless `aarch64-linux` server with two roles:
|
||||||
|
|
||||||
|
- **Docker host** — daemon exposed over the network (`docker.nix`).
|
||||||
|
- **nginx reverse proxy** — declarative `virtualHosts` (`reverse-proxy.nix`).
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
1. Flash a NixOS `aarch64` SD image (or USB) and boot the Pi. The
|
||||||
|
`raspberry-pi-5` profile from `nixos-hardware` (wired in the flake host table)
|
||||||
|
supplies the kernel, firmware and device tree; boot is U-Boot + extlinux.
|
||||||
|
2. Partition/mount the target, then **regenerate the hardware config on the
|
||||||
|
device** and replace the committed placeholder:
|
||||||
|
```sh
|
||||||
|
nixos-generate-config --root /mnt
|
||||||
|
# copy /mnt/etc/nixos/hardware-configuration.nix over
|
||||||
|
# system/machine/RPi5/hardware-configuration.nix in this repo, then commit
|
||||||
|
```
|
||||||
|
`hardware-configuration.nix` in this directory is a **placeholder** committed
|
||||||
|
only so the host evaluates in CI. The machine will not boot correctly until it
|
||||||
|
is replaced with the generated one.
|
||||||
|
3. Set the host name to match the flake attribute (already done in
|
||||||
|
`configuration.nix`: `lyrathorpe-rpi5`) and build:
|
||||||
|
```sh
|
||||||
|
sudo nixos-rebuild switch --flake .#lyrathorpe-rpi5
|
||||||
|
# or, once the hostname is live:
|
||||||
|
nh os switch
|
||||||
|
```
|
||||||
|
4. Give the login user a password (`passwd lyrathorpe`) and confirm the key in
|
||||||
|
`system/modules/ssh.nix` is the one you will connect with.
|
||||||
|
|
||||||
|
## Docker socket (security)
|
||||||
|
|
||||||
|
The daemon listens on **plain TCP `2375`, no TLS, no auth**. Access is
|
||||||
|
root-equivalent on this host. The only protection is the nftables rule in
|
||||||
|
`docker.nix`, which accepts `2375` **only** from the trusted LAN subnet
|
||||||
|
(`10.187.1.0/24` by default — change it to match your network). Do not widen
|
||||||
|
that subnet to anything untrusted.
|
||||||
|
|
||||||
|
From a LAN client:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export DOCKER_HOST=tcp://lyrathorpe-rpi5:2375
|
||||||
|
docker info
|
||||||
|
```
|
||||||
|
|
||||||
|
The secure upgrade path is mutual TLS on `2376` (`--tlsverify` with a CA and
|
||||||
|
client certs); it needs out-of-band cert provisioning and is intentionally not
|
||||||
|
wired here.
|
||||||
|
|
||||||
|
## Adding a reverse-proxy site
|
||||||
|
|
||||||
|
Each proxied service is a Nix entry in `reverse-proxy.nix`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
services.nginx.virtualHosts."app.example.lan" = {
|
||||||
|
# enableACME = true; forceSSL = true; # once a DNS name + cert exist
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://127.0.0.1:8080"; # e.g. a local container
|
||||||
|
proxyWebsockets = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
The example vhost is HTTP-only by design. Turn on `enableACME`/`forceSSL`
|
||||||
|
per-vhost once the host has a real DNS name and the ACME challenge can be met;
|
||||||
|
`443` is already open in the firewall.
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# Raspberry Pi 5 (aarch64) headless server. Two roles, split into submodules:
|
||||||
|
# ./docker.nix (Docker host with a network socket) and ./reverse-proxy.nix
|
||||||
|
# (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
|
||||||
|
# flake host table. Install notes: see ./README.md.
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./hardware-configuration.nix
|
||||||
|
./docker.nix
|
||||||
|
./reverse-proxy.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
# Match the flake's nixosConfigurations attribute name so `nh os switch`
|
||||||
|
# (which selects by the local hostname) resolves without an explicit -H flag.
|
||||||
|
networking.hostName = "lyrathorpe-rpi5";
|
||||||
|
|
||||||
|
# Headless server: the Sway desktop is intentionally not set up. swaywm.nix is
|
||||||
|
# not imported and features.swayDesktop.enable defaults to false (declared in
|
||||||
|
# system/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-5 nixos-hardware profile supplies the kernel, firmware and
|
||||||
|
# device tree.
|
||||||
|
boot.loader.grub.enable = false;
|
||||||
|
boot.loader.generic-extlinux-compatible.enable = true;
|
||||||
|
|
||||||
|
# Remote administration. Key-only policy and the authorized key come from
|
||||||
|
# ../../modules/ssh.nix; here we just enable the daemon and open the port.
|
||||||
|
services.openssh.enable = true;
|
||||||
|
|
||||||
|
# Default-deny inbound. Open only SSH here; the Docker and nginx submodules
|
||||||
|
# open their own ports (Docker via a source-restricted nftables rule, nginx
|
||||||
|
# via 80/443). List-valued, so these merge with the submodule definitions.
|
||||||
|
networking.firewall.enable = true;
|
||||||
|
networking.firewall.allowedTCPPorts = [ 22 ];
|
||||||
|
|
||||||
|
# See `man configuration.nix` / the stateVersion docs before changing.
|
||||||
|
system.stateVersion = "26.05";
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# Docker host with the daemon socket exposed over the network.
|
||||||
|
#
|
||||||
|
# SECURITY: the daemon listens on plain TCP 2375 with NO TLS and NO auth. Access
|
||||||
|
# to that port is root-equivalent on this host (the Docker API can mount the
|
||||||
|
# host filesystem and run privileged containers). The ONLY thing protecting it
|
||||||
|
# is the nftables rule below, which accepts 2375 solely from the trusted LAN
|
||||||
|
# subnet. Do not widen that subnet to anything you do not fully trust. The
|
||||||
|
# secure upgrade path is mutual TLS on 2376 (--tlsverify with client certs);
|
||||||
|
# that needs out-of-band cert provisioning and is intentionally not wired here.
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
virtualisation.docker.enable = true;
|
||||||
|
|
||||||
|
# Expose the daemon over TCP by extending systemd socket activation rather than
|
||||||
|
# setting daemon.settings.hosts. The NixOS docker unit starts dockerd with
|
||||||
|
# `-H fd://` and takes its listeners from this socket; putting `hosts` in
|
||||||
|
# daemon.json as well would conflict with that and dockerd would refuse to
|
||||||
|
# start. Adding the TCP listener here keeps a single source of truth.
|
||||||
|
# The leading "" resets the unit's default (unix-socket-only) ListenStream list.
|
||||||
|
systemd.sockets.docker.socketConfig.ListenStream = [
|
||||||
|
""
|
||||||
|
"/run/docker.sock"
|
||||||
|
"0.0.0.0:2375"
|
||||||
|
];
|
||||||
|
|
||||||
|
# Source-restricted firewall rule for the Docker TCP port. 2375 is deliberately
|
||||||
|
# NOT added to networking.firewall.allowedTCPPorts (that would open it to every
|
||||||
|
# source); instead nftables accepts it only from the trusted subnet. Adjust the
|
||||||
|
# CIDR to match the LAN that should reach the Docker API.
|
||||||
|
networking.nftables.enable = true;
|
||||||
|
networking.firewall.extraInputRules = ''
|
||||||
|
ip saddr 10.187.1.0/24 tcp dport 2375 accept
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# PLACEHOLDER hardware configuration for the Raspberry Pi 5.
|
||||||
|
#
|
||||||
|
# This file is NOT the real generated config -- it exists only so the host
|
||||||
|
# evaluates in CI before the Pi is provisioned. The machine will not boot from
|
||||||
|
# it as-is. On first install, regenerate this file on the device with
|
||||||
|
# nixos-generate-config --root /mnt
|
||||||
|
# and replace this placeholder with the output (commit it). See ./README.md.
|
||||||
|
#
|
||||||
|
# Like every hardware-configuration.nix in this repo, this file is excluded from
|
||||||
|
# the formatter and linters (see the pre-commit/treefmt excludes in flake.nix).
|
||||||
|
{ modulesPath, ... }:
|
||||||
|
{
|
||||||
|
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
|
||||||
|
|
||||||
|
nixpkgs.hostPlatform = "aarch64-linux";
|
||||||
|
|
||||||
|
# The Raspberry Pi 5 boots from an SD card / USB with a FAT firmware partition
|
||||||
|
# and an ext4 root. Labels match the conventional sd-image layout; the real
|
||||||
|
# generated config will use by-uuid device paths instead.
|
||||||
|
fileSystems."/" = {
|
||||||
|
device = "/dev/disk/by-label/NIXOS_SD";
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
|
||||||
|
fileSystems."/boot/firmware" = {
|
||||||
|
device = "/dev/disk/by-label/FIRMWARE";
|
||||||
|
fsType = "vfat";
|
||||||
|
};
|
||||||
|
|
||||||
|
swapDevices = [ ];
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# Native nginx reverse proxy. The proxy configuration is declarative Nix:
|
||||||
|
# every proxied service is an entry under services.nginx.virtualHosts, so the
|
||||||
|
# whole routing table lives in this file and is built/version-controlled with
|
||||||
|
# the rest of the system.
|
||||||
|
#
|
||||||
|
# To add a proxied service, add another virtualHosts."<host>" entry following
|
||||||
|
# the example below. To serve it over HTTPS, uncomment enableACME + forceSSL on
|
||||||
|
# that vhost once it has a real DNS name and the ACME HTTP-01/DNS-01 challenge
|
||||||
|
# can be satisfied (see security.acme for the account/email and DNS settings).
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
services.nginx = {
|
||||||
|
enable = true;
|
||||||
|
recommendedProxySettings = true; # sane proxy_set_header defaults (Host, X-Forwarded-*)
|
||||||
|
recommendedTlsSettings = true;
|
||||||
|
recommendedOptimisation = true;
|
||||||
|
recommendedGzipSettings = true;
|
||||||
|
|
||||||
|
virtualHosts = {
|
||||||
|
# Example reverse-proxy vhost. Replace the name and upstream with a real
|
||||||
|
# service (e.g. a container published by the Docker host on this machine).
|
||||||
|
"example.lan" = {
|
||||||
|
# enableACME = true; # request a Let's Encrypt cert for this host
|
||||||
|
# forceSSL = true; # redirect HTTP -> HTTPS once the cert exists
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://127.0.0.1:8080";
|
||||||
|
proxyWebsockets = true; # forward Upgrade/Connection for WebSocket apps
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Public reverse-proxy ports. 443 is opened now so flipping a vhost to TLS
|
||||||
|
# needs no firewall change.
|
||||||
|
networking.firewall.allowedTCPPorts = [
|
||||||
|
80
|
||||||
|
443
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# ThinkPad T400 — install notes
|
||||||
|
|
||||||
|
Flake host: `lyrathorpe-t400`. Files: `configuration.nix`, the `boot-*.nix`
|
||||||
|
variants, and `hardware-configuration.nix`.
|
||||||
|
|
||||||
|
## Hardware configuration
|
||||||
|
|
||||||
|
`hardware-configuration.nix` here is a hand-written **placeholder**. On the real
|
||||||
|
machine, run `nixos-generate-config`, replace the file, and commit it. It assumes
|
||||||
|
by-label partitions — root `nixos` (ext4) and `swap` — so either label them at
|
||||||
|
install time or swap in the generated UUIDs.
|
||||||
|
|
||||||
|
## Bootloader — import the module matching the flashed firmware
|
||||||
|
|
||||||
|
`configuration.nix` imports exactly one boot module. Default is `boot-bios.nix`;
|
||||||
|
switch by commenting it out and uncommenting the relevant alternative.
|
||||||
|
|
||||||
|
| Firmware | Module | Notes |
|
||||||
|
| ---------------------------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| Stock Lenovo BIOS, or coreboot + **SeaBIOS** payload | `boot-bios.nix` | GRUB on the MBR. Set `device` to the real install disk (`/dev/sda` by default). MBR/legacy layout. |
|
||||||
|
| coreboot + **GRUB** payload | `boot-coreboot-grub.nix` | GRUB is config-only (`device = "nodev"`); NixOS does **not** write to a disk. Your coreboot `grub.cfg` (in the flash chip) must `search` for and `configfile` the on-disk `/boot/grub/grub.cfg`, or chainload the disk's GRUB. |
|
||||||
|
| coreboot + **Tianocore/edk2 (UEFI)** payload | `boot-coreboot-uefi.nix` | systemd-boot. `canTouchEfiVariables = true` (coreboot honours NVRAM writes). The module **declares its own ESP** (`/boot` vfat, label `ESP`) — when you regenerate `hardware-configuration.nix`, do **not** let it also define `/boot`. Create + label an `ESP` vfat partition (GPT). |
|
||||||
|
|
||||||
|
## Graphics
|
||||||
|
|
||||||
|
This unit has the optional **discrete ATI Mobility Radeon HD 3470 (RV620)**. The
|
||||||
|
open `radeon` KMS driver is loaded in the initrd for early modesetting; firmware
|
||||||
|
comes from `enableRedistributableFirmware`.
|
||||||
|
|
||||||
|
The T400 has switchable graphics (discrete ATI + Intel GMA 4500MHD). Select
|
||||||
|
**Discrete** in the firmware's graphics setting so only the ATI is live. If you
|
||||||
|
run **Integrated** instead, the Intel `i915` driver takes over with no config
|
||||||
|
change and `radeon` stays idle.
|
||||||
|
|
||||||
|
## Login
|
||||||
|
|
||||||
|
Graphical login via a Wayland greeter — `greetd` running ReGreet inside the
|
||||||
|
`cage` kiosk compositor — configured centrally in `lyrathorpe/swaywm.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 radeon/i915 KMS (see Graphics).
|
||||||
|
|
||||||
|
## Apply
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo nixos-rebuild switch --flake .#lyrathorpe-t400
|
||||||
|
```
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Boot via legacy BIOS -- the stock Lenovo BIOS, or coreboot with the SeaBIOS
|
||||||
|
# payload (both present a legacy BIOS interface). GRUB is installed to the MBR of
|
||||||
|
# the boot disk. This is the default.
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
boot.loader.grub = {
|
||||||
|
enable = true;
|
||||||
|
# Must point at the actual install disk -- adjust if it is not /dev/sda.
|
||||||
|
device = "/dev/sda";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Boot via coreboot's GRUB payload (e.g. libreboot default). The GRUB in the
|
||||||
|
# flash chip reads the grub.cfg that NixOS generates on disk, so GRUB here is
|
||||||
|
# config-only -- it is NOT installed to any disk MBR (`device = "nodev"`).
|
||||||
|
#
|
||||||
|
# Your coreboot grub.cfg must locate and load the on-disk config, e.g. search
|
||||||
|
# for and `configfile` /boot/grub/grub.cfg (or chainload the disk's GRUB).
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
boot.loader.grub = {
|
||||||
|
enable = true;
|
||||||
|
device = "nodev";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Boot via coreboot's Tianocore/edk2 (UEFI) payload. This turns the T400 into a
|
||||||
|
# real UEFI machine, so use systemd-boot. Unlike Apple's firmware, coreboot's
|
||||||
|
# UEFI honours EFI variable writes, so canTouchEfiVariables is on.
|
||||||
|
#
|
||||||
|
# Requires an EFI System Partition. It is declared here so it travels with this
|
||||||
|
# boot mode; the generated hardware-configuration.nix should NOT also define
|
||||||
|
# /boot. Label the ESP `ESP` at install, or replace with the generated UUID.
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
boot.loader.systemd-boot.enable = true;
|
||||||
|
boot.loader.efi.canTouchEfiVariables = true;
|
||||||
|
|
||||||
|
fileSystems."/boot" = {
|
||||||
|
device = "/dev/disk/by-label/ESP";
|
||||||
|
fsType = "vfat";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# ThinkPad T400 (NixOS). Shared laptop options live in ../../modules/laptop.nix;
|
||||||
|
# only host-specific settings are here. Install notes (boot variants, GPU,
|
||||||
|
# partitions): see ./README.md.
|
||||||
|
{ config, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./hardware-configuration.nix
|
||||||
|
# Boot: import exactly ONE, matching the firmware currently flashed.
|
||||||
|
# Stock Lenovo BIOS and coreboot+SeaBIOS both use boot-bios.nix.
|
||||||
|
./boot-bios.nix
|
||||||
|
# ./boot-coreboot-grub.nix # coreboot with the GRUB payload (config-only GRUB)
|
||||||
|
# ./boot-coreboot-uefi.nix # coreboot with the Tianocore/edk2 UEFI payload
|
||||||
|
# # (systemd-boot; carries its own ESP mount)
|
||||||
|
];
|
||||||
|
|
||||||
|
networking.hostName = "T400-NixOS";
|
||||||
|
|
||||||
|
console.font = "Lat2-Terminus16";
|
||||||
|
|
||||||
|
# Low-RAM host (4 GiB max): a compressed RAM swap reduces disk paging.
|
||||||
|
zramSwap.enable = true;
|
||||||
|
|
||||||
|
# This host accepts SSH, so open 22 (the firewall itself is enabled in
|
||||||
|
# laptop.nix with a default-deny policy).
|
||||||
|
services.openssh.enable = true;
|
||||||
|
networking.firewall.allowedTCPPorts = [ 22 ];
|
||||||
|
|
||||||
|
# Intel Core 2 (Penryn) microcode. Redistributable firmware (enabled in
|
||||||
|
# workstation.nix) supplies the iwlwifi blobs (Intel WiFi Link 5100/5300) and
|
||||||
|
# the radeon firmware needed by the discrete GPU below.
|
||||||
|
hardware.cpu.intel.updateMicrocode = true;
|
||||||
|
|
||||||
|
# Battery longevity: cap charging to 75-80%. tlp itself comes from the
|
||||||
|
# nixos-hardware lenovo-thinkpad profile; tp_smapi supplies the threshold
|
||||||
|
# sysfs on this 2008-era ThinkPad (kernel-native natacpi is too new for it).
|
||||||
|
boot.kernelModules = [ "tp_smapi" ];
|
||||||
|
boot.extraModulePackages = [ config.boot.kernelPackages.tp_smapi ];
|
||||||
|
services.tlp.settings = {
|
||||||
|
START_CHARGE_THRESH_BAT0 = 75;
|
||||||
|
STOP_CHARGE_THRESH_BAT0 = 80;
|
||||||
|
};
|
||||||
|
|
||||||
|
# This T400 has the optional discrete GPU fitted: an ATI Mobility Radeon HD
|
||||||
|
# 3470 (RV620), driven by the open `radeon` KMS driver. Load it in the initrd
|
||||||
|
# for early modesetting (clean Sway/Wayland start); firmware comes from
|
||||||
|
# enableRedistributableFirmware above.
|
||||||
|
#
|
||||||
|
# The T400 has switchable graphics (this discrete GPU + the Intel GMA
|
||||||
|
# 4500MHD). Select "Discrete" in the firmware's graphics setting so only the
|
||||||
|
# ATI is live; if you instead run "Integrated", the Intel i915 driver takes
|
||||||
|
# over with no extra config and `radeon` simply stays idle.
|
||||||
|
boot.initrd.kernelModules = [ "radeon" ];
|
||||||
|
|
||||||
|
# See `man configuration.nix` / the stateVersion docs before changing.
|
||||||
|
system.stateVersion = "26.05";
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# PLACEHOLDER -- hand-written, not machine-generated. Regenerate on the real
|
||||||
|
# T400 with `nixos-generate-config` and commit the result. The device labels
|
||||||
|
# below are guesses; replace them with the generated UUIDs (or label the
|
||||||
|
# partitions accordingly at install time).
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
modulesPath,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
(modulesPath + "/installer/scan/not-detected.nix")
|
||||||
|
];
|
||||||
|
|
||||||
|
boot.initrd.availableKernelModules = [
|
||||||
|
"ahci"
|
||||||
|
"ata_piix"
|
||||||
|
"ehci_pci"
|
||||||
|
"uhci_hcd"
|
||||||
|
"usb_storage"
|
||||||
|
"sd_mod"
|
||||||
|
"sr_mod"
|
||||||
|
];
|
||||||
|
boot.initrd.kernelModules = [ ];
|
||||||
|
boot.kernelModules = [ "kvm-intel" ];
|
||||||
|
boot.extraModulePackages = [ ];
|
||||||
|
|
||||||
|
# Label your root partition `nixos` at install, or replace with the generated UUID.
|
||||||
|
fileSystems."/" = {
|
||||||
|
device = "/dev/disk/by-label/nixos";
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
|
||||||
|
swapDevices = [
|
||||||
|
{ device = "/dev/disk/by-label/swap"; }
|
||||||
|
];
|
||||||
|
|
||||||
|
networking.useDHCP = lib.mkDefault true;
|
||||||
|
|
||||||
|
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||||
|
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
|
||||||
|
}
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
# Edit this configuration file to define what should be installed on
|
|
||||||
# your system. Help is available in the configuration.nix(5) man page, on
|
|
||||||
# https://search.nixos.org/options and in the NixOS manual (`nixos-help`).
|
|
||||||
|
|
||||||
{
|
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
# Include the results of the hardware scan.
|
|
||||||
./hardware-configuration.nix
|
|
||||||
];
|
|
||||||
|
|
||||||
# Use the systemd-boot EFI boot loader.
|
|
||||||
boot.loader.systemd-boot.enable = true;
|
|
||||||
boot.loader.efi.canTouchEfiVariables = true;
|
|
||||||
|
|
||||||
networking.hostName = "X1-NixOS"; # Define your hostname.
|
|
||||||
networking.domain = "client.cbg.emmaisvery.gay";
|
|
||||||
features.swayDesktop.enable = true;
|
|
||||||
# Pick only one of the below networking options.
|
|
||||||
networking.wireless.iwd = {
|
|
||||||
enable = true;
|
|
||||||
settings.General.EnableNetworkConfiguration = true;
|
|
||||||
};
|
|
||||||
# networking.networkmanager.enable = true; # Easiest to use and most distros use this by default.
|
|
||||||
|
|
||||||
# Set your time zone.
|
|
||||||
time.timeZone = "Europe/London";
|
|
||||||
|
|
||||||
# Configure network proxy if necessary
|
|
||||||
# networking.proxy.default = "http://user:password@proxy:port/";
|
|
||||||
# networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
|
|
||||||
|
|
||||||
# Select internationalisation properties.
|
|
||||||
i18n.defaultLocale = "en_GB.UTF-8";
|
|
||||||
console = {
|
|
||||||
font = "Lat2-Terminus16";
|
|
||||||
keyMap = "dvorak";
|
|
||||||
# useXkbConfig = true; # use xkb.options in tty.
|
|
||||||
};
|
|
||||||
|
|
||||||
# Enable the X11 windowing system.
|
|
||||||
# services.xserver.enable = true;
|
|
||||||
|
|
||||||
# Configure keymap in X11
|
|
||||||
# services.xserver.xkb.layout = "us";
|
|
||||||
# services.xserver.xkb.options = "eurosign:e,caps:escape";
|
|
||||||
|
|
||||||
# Enable CUPS to print documents.
|
|
||||||
# services.printing.enable = true;
|
|
||||||
|
|
||||||
# Enable sound.
|
|
||||||
# hardware.pulseaudio.enable = true;
|
|
||||||
# OR
|
|
||||||
services.pipewire = {
|
|
||||||
enable = true;
|
|
||||||
pulse.enable = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
# Enable touchpad support (enabled default in most desktopManager).
|
|
||||||
# services.libinput.enable = true;
|
|
||||||
|
|
||||||
# Define a user account. Don't forget to set a password with ‘passwd’.
|
|
||||||
# users.users.alice = {
|
|
||||||
# isNormalUser = true;
|
|
||||||
# extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user.
|
|
||||||
# packages = with pkgs; [
|
|
||||||
# tree
|
|
||||||
# ];
|
|
||||||
# };
|
|
||||||
|
|
||||||
# List packages installed in system profile. To search, run:
|
|
||||||
# $ nix search wget
|
|
||||||
environment.systemPackages = with pkgs; [
|
|
||||||
git
|
|
||||||
fastfetch
|
|
||||||
# wget
|
|
||||||
];
|
|
||||||
|
|
||||||
# Some programs need SUID wrappers, can be configured further or are
|
|
||||||
# started in user sessions.
|
|
||||||
# programs.mtr.enable = true;
|
|
||||||
# programs.gnupg.agent = {
|
|
||||||
# enable = true;
|
|
||||||
# enableSSHSupport = true;
|
|
||||||
# };
|
|
||||||
|
|
||||||
# List services that you want to enable:
|
|
||||||
|
|
||||||
# Enable the OpenSSH daemon.
|
|
||||||
services.openssh.enable = true;
|
|
||||||
|
|
||||||
# Open ports in the firewall.
|
|
||||||
# networking.firewall.allowedTCPPorts = [ ... ];
|
|
||||||
# networking.firewall.allowedUDPPorts = [ ... ];
|
|
||||||
# Or disable the firewall altogether.
|
|
||||||
networking.firewall.enable = false;
|
|
||||||
|
|
||||||
# Copy the NixOS configuration file and link it from the resulting system
|
|
||||||
# (/run/current-system/configuration.nix). This is useful in case you
|
|
||||||
# accidentally delete configuration.nix.
|
|
||||||
#system.copySystemConfiguration = true;
|
|
||||||
|
|
||||||
# This option defines the first version of NixOS you have installed on this particular machine,
|
|
||||||
# and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions.
|
|
||||||
#
|
|
||||||
# Most users should NEVER change this value after the initial install, for any reason,
|
|
||||||
# even if you've upgraded your system to a new NixOS release.
|
|
||||||
#
|
|
||||||
# This value does NOT affect the Nixpkgs version your packages and OS are pulled from,
|
|
||||||
# so changing it will NOT upgrade your system - see https://nixos.org/manual/nixos/stable/#sec-upgrading for how
|
|
||||||
# to actually do that.
|
|
||||||
#
|
|
||||||
# This value being lower than the current NixOS release does NOT mean your system is
|
|
||||||
# out of date, out of support, or vulnerable.
|
|
||||||
#
|
|
||||||
# Do NOT change this value unless you have manually inspected all the changes it would make to your configuration,
|
|
||||||
# and migrated your data accordingly.
|
|
||||||
#
|
|
||||||
# For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion .
|
|
||||||
system.stateVersion = "24.11"; # Did you read the comment?
|
|
||||||
|
|
||||||
# TODO: Move to fprint security module to import anywhere
|
|
||||||
services.fprintd.enable = true;
|
|
||||||
security.pam.services.swaylock = {
|
|
||||||
fprintAuth = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# Do not modify this file! It was generated by ‘nixos-generate-config’
|
|
||||||
# and may be overwritten by future invocations. Please make changes
|
|
||||||
# to /etc/nixos/configuration.nix instead.
|
|
||||||
{ config, lib, pkgs, modulesPath, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
imports =
|
|
||||||
[ (modulesPath + "/installer/scan/not-detected.nix")
|
|
||||||
];
|
|
||||||
|
|
||||||
boot.initrd.availableKernelModules = [ "xhci_pci" "nvme" "usb_storage" "sd_mod" ];
|
|
||||||
boot.initrd.kernelModules = [ ];
|
|
||||||
boot.kernelModules = [ "kvm-intel" ];
|
|
||||||
boot.extraModulePackages = [ ];
|
|
||||||
|
|
||||||
fileSystems."/" =
|
|
||||||
{ device = "/dev/disk/by-uuid/a7145534-b122-4899-a75a-3d2e78474d6b";
|
|
||||||
fsType = "ext4";
|
|
||||||
};
|
|
||||||
|
|
||||||
fileSystems."/boot" =
|
|
||||||
{ device = "/dev/disk/by-uuid/1338-3D4F";
|
|
||||||
fsType = "vfat";
|
|
||||||
options = [ "fmask=0077" "dmask=0077" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
swapDevices =
|
|
||||||
[ { device = "/dev/disk/by-uuid/e553c8dc-9d5a-48ec-87bc-9c86ce5932a4"; }
|
|
||||||
];
|
|
||||||
|
|
||||||
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
|
|
||||||
# (the default) this is the recommended approach. When using systemd-networkd it's
|
|
||||||
# still possible to use this option, but it's recommended to use it in conjunction
|
|
||||||
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
|
|
||||||
networking.useDHCP = lib.mkDefault true;
|
|
||||||
# networking.interfaces.enp0s31f6.useDHCP = lib.mkDefault true;
|
|
||||||
# networking.interfaces.wlp0s20f3.useDHCP = lib.mkDefault true;
|
|
||||||
# networking.interfaces.wwan0.useDHCP = lib.mkDefault true;
|
|
||||||
|
|
||||||
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
|
||||||
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# Options shared by every NixOS host (laptops and the WSL box). Imported via
|
||||||
|
# baseModules in flake.nix. Host- and platform-specific settings stay in the
|
||||||
|
# per-machine configs; laptop-only settings live in ./laptop.nix.
|
||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
time.timeZone = "Europe/London";
|
||||||
|
i18n.defaultLocale = "en_GB.UTF-8";
|
||||||
|
|
||||||
|
# Store hygiene. auto-optimise-store hard-links identical files in the store
|
||||||
|
# after each build (cheap dedupe; NOT a garbage collector -- there is
|
||||||
|
# deliberately no automatic GC timer). The larger download buffer avoids
|
||||||
|
# "buffer full" stalls when fetching big NARs over a fast link.
|
||||||
|
nix.settings.auto-optimise-store = true;
|
||||||
|
nix.settings.download-buffer-size = 134217728; # 128 MiB
|
||||||
|
|
||||||
|
# Extra binary cache for the nix-community toolchain (home-manager, nixvim,
|
||||||
|
# treefmt, ...). Merges with any host-specific caches (e.g. the Asahi cache on
|
||||||
|
# the MBP) rather than replacing them.
|
||||||
|
nix.settings.substituters = [ "https://nix-community.cachix.org" ];
|
||||||
|
nix.settings.trusted-public-keys = [
|
||||||
|
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
|
||||||
|
];
|
||||||
|
|
||||||
|
# Run dynamically-linked foreign binaries (VS Code remote server, prebuilt
|
||||||
|
# toolchains, language-server downloads) on every NixOS host, not just WSL.
|
||||||
|
programs.nix-ld.enable = true;
|
||||||
|
|
||||||
|
# Minimal system-level CLI available before the home-manager profile loads
|
||||||
|
# (e.g. early boot / rescue). User-level tooling lives in home-manager.
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
git
|
||||||
|
fastfetch
|
||||||
|
];
|
||||||
|
|
||||||
|
# Fonts on every host. The Nerd Font carries the powerline/Nerd glyphs the
|
||||||
|
# tmux statusline uses (foot names it explicitly in home/sway.nix); Noto sans +
|
||||||
|
# colour emoji prevent tofu in terminals/TUIs/Firefox -- important on the WSL
|
||||||
|
# box, which does not pull the graphical hosts' default Noto stack. The Mac
|
||||||
|
# installs the Nerd Font via the Darwin config.
|
||||||
|
fonts.packages = with pkgs; [
|
||||||
|
nerd-fonts.jetbrains-mono
|
||||||
|
noto-fonts
|
||||||
|
noto-fonts-color-emoji
|
||||||
|
];
|
||||||
|
# Map the generic fontconfig families so anything asking for "monospace" gets
|
||||||
|
# the Nerd Font (with emoji fallback), not DejaVu.
|
||||||
|
fonts.fontconfig.defaultFonts = {
|
||||||
|
monospace = [
|
||||||
|
"JetBrainsMono Nerd Font"
|
||||||
|
"Noto Color Emoji"
|
||||||
|
];
|
||||||
|
sansSerif = [
|
||||||
|
"Noto Sans"
|
||||||
|
"Noto Color Emoji"
|
||||||
|
];
|
||||||
|
serif = [
|
||||||
|
"Noto Serif"
|
||||||
|
"Noto Color Emoji"
|
||||||
|
];
|
||||||
|
emoji = [ "Noto Color Emoji" ];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Desktop (non-portable) NixOS hosts. Counterpart to ./laptop.nix: imports the
|
||||||
|
# shared ./workstation.nix base and swaps the mobile Wi-Fi backend for wired
|
||||||
|
# NetworkManager. A desktop host also sets `portable = false` in its host-table
|
||||||
|
# entry (flake.nix), which drops the battery block and brightness keybindings
|
||||||
|
# from the Sway bar -- see lyrathorpe/home/sway.nix.
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
imports = [ ./workstation.nix ];
|
||||||
|
|
||||||
|
# Wired networking. NetworkManager handles DHCP/connections itself; do not
|
||||||
|
# combine with networking.wireless.* (laptop.nix) -- the two backends conflict.
|
||||||
|
networking.networkmanager.enable = true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Feature-flag option declarations shared by every NixOS host (imported via
|
||||||
|
# baseModules in flake.nix). Declaring the flags here -- rather than inside the
|
||||||
|
# module that implements them -- means a host can read or set a flag without
|
||||||
|
# importing the (often large) implementation module. In particular,
|
||||||
|
# features.swayDesktop.enable is read by lyrathorpe/user.nix on every host, but a
|
||||||
|
# headless host (e.g. the Pi) must be able to leave it at its default without
|
||||||
|
# pulling in lyrathorpe/swaywm.nix. The implementation lives in swaywm.nix,
|
||||||
|
# gated on this flag.
|
||||||
|
{ lib, ... }:
|
||||||
|
{
|
||||||
|
options.features.swayDesktop.enable = lib.mkEnableOption "the Sway desktop";
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Portable NixOS hosts (X1, MBP-Asahi). Imported from the host table in
|
||||||
|
# flake.nix. Shared graphical-workstation settings live in ./workstation.nix;
|
||||||
|
# the only laptop-specific bit is the Wi-Fi backend. Mobile home-manager
|
||||||
|
# components (battery block, brightness keys) are gated by the `portable` flag
|
||||||
|
# threaded through mkHost -- see lyrathorpe/home/sway.nix.
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
imports = [ ./workstation.nix ];
|
||||||
|
|
||||||
|
# Wi-Fi via iwd with its built-in DHCP/network configuration.
|
||||||
|
networking.wireless.iwd = {
|
||||||
|
enable = true;
|
||||||
|
settings.General.EnableNetworkConfiguration = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Lid behaviour: suspend on battery, lock on external power (swayidle's
|
||||||
|
# before-sleep hook locks before the suspend completes either way).
|
||||||
|
services.logind.settings.Login = {
|
||||||
|
HandleLidSwitch = "suspend";
|
||||||
|
HandleLidSwitchExternalPower = "lock";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Bluetooth. The Asahi MBP loads Apple's BT firmware (see its host config) and
|
||||||
|
# the T400 has an optional BT module; enable bluez on both, with blueman as the
|
||||||
|
# GUI/tray manager for the Sway session.
|
||||||
|
hardware.bluetooth = {
|
||||||
|
enable = true;
|
||||||
|
powerOnBoot = true;
|
||||||
|
};
|
||||||
|
services.blueman.enable = true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Key-only SSH hardening, imported by the hosts that run sshd (T400, Mac Pro).
|
||||||
|
# The host config still does `services.openssh.enable = true` and opens port 22
|
||||||
|
# next to where it documents the listening service; this module only tightens
|
||||||
|
# the policy and installs the authorized key, so a host opting into sshd cannot
|
||||||
|
# accidentally ship password/root login.
|
||||||
|
{ username, ... }:
|
||||||
|
{
|
||||||
|
services.openssh.settings = {
|
||||||
|
PasswordAuthentication = false; # keys only
|
||||||
|
KbdInteractiveAuthentication = false; # no keyboard-interactive fallback
|
||||||
|
PermitRootLogin = "no";
|
||||||
|
};
|
||||||
|
|
||||||
|
# The key permitted to log in as the primary user. Add more entries here as
|
||||||
|
# new client machines are provisioned.
|
||||||
|
users.users.${username}.openssh.authorizedKeys.keys = [
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPDxHvdMTOzpFWUFMtCP7C/4tIOUO3GIO2QPvaifSnWH lyrathorpe@Lyra-MBA"
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
{
|
|
||||||
config,
|
|
||||||
pkgs,
|
|
||||||
inputs,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
|
|
||||||
{
|
|
||||||
programs.git = {
|
|
||||||
settings = {
|
|
||||||
commit.gpgsign = true;
|
|
||||||
tag.gpgsign = true;
|
|
||||||
gpg.format = "ssh";
|
|
||||||
user.signingkey = "key::ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAJMVgeRKnfX1G8coU3nAobI485aeUpGTMqH7+zbKI8o emma.thorpe@cloud.com";
|
|
||||||
user.email = "emma.thorpe@citrix.com";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
home.packages = [
|
|
||||||
pkgs.kubectl
|
|
||||||
pkgs.argo-rollouts
|
|
||||||
pkgs.tenv
|
|
||||||
pkgs.kubernetes-helm
|
|
||||||
pkgs.azure-cli
|
|
||||||
pkgs.kubelogin
|
|
||||||
pkgs.curl
|
|
||||||
pkgs.notation
|
|
||||||
pkgs.powershell
|
|
||||||
pkgs.nuget
|
|
||||||
pkgs.gedit
|
|
||||||
pkgs.lens
|
|
||||||
pkgs.python3
|
|
||||||
pkgs.gnumake
|
|
||||||
pkgs.gcc
|
|
||||||
pkgs.libiconv
|
|
||||||
pkgs.autoconf
|
|
||||||
pkgs.automake
|
|
||||||
pkgs.pkg-config
|
|
||||||
pkgs.wget
|
|
||||||
pkgs.claude-code
|
|
||||||
pkgs.google-cloud-sdk
|
|
||||||
];
|
|
||||||
services.ssh-agent.enable = true;
|
|
||||||
home.shellAliases = {
|
|
||||||
docker = "/run/current-system/sw/bin/docker";
|
|
||||||
};
|
|
||||||
programs.tmux = {
|
|
||||||
extraConfig = ''
|
|
||||||
set -g status-right "#(/run/current-system/sw/bin/bash $HOME/code/kube-tmux/kube.tmux 250 red black)"
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
programs.go = {
|
|
||||||
enable = true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# Form-factor-agnostic base for the physical graphical NixOS machines. Imported
|
||||||
|
# by both ./laptop.nix and ./desktop.nix; those add only the bits that differ
|
||||||
|
# between portable and desktop hosts (chiefly the networking backend).
|
||||||
|
#
|
||||||
|
# The bootloader is NOT set here -- it is firmware-specific, not form-factor:
|
||||||
|
# UEFI hosts (MBP, Mac Pro 3,1) use systemd-boot, the BIOS-only T400 uses GRUB.
|
||||||
|
# Each machine config declares its own.
|
||||||
|
{ lib, pkgs, ... }:
|
||||||
|
{
|
||||||
|
features.swayDesktop.enable = true;
|
||||||
|
|
||||||
|
console.keyMap = "dvorak";
|
||||||
|
|
||||||
|
# Intel thermal management. x86 only -- the Asahi MBP governs its own SoC
|
||||||
|
# thermals, and thermald is an Intel-platform daemon.
|
||||||
|
services.thermald.enable = lib.mkIf pkgs.stdenv.hostPlatform.isx86_64 true;
|
||||||
|
|
||||||
|
# Default-deny inbound. Hosts that run a listening service open their own
|
||||||
|
# ports next to where the service is enabled (e.g. sshd -> 22 on X1).
|
||||||
|
networking.firewall.enable = true;
|
||||||
|
|
||||||
|
# Disk hygiene for the physical hosts. fstrim reclaims unused SSD blocks on a
|
||||||
|
# weekly timer; cleanOnBoot wipes /tmp at every boot.
|
||||||
|
services.fstrim.enable = true;
|
||||||
|
boot.tmp.cleanOnBoot = true;
|
||||||
|
|
||||||
|
# Userspace OOM killer: act on memory pressure early instead of letting the
|
||||||
|
# kernel OOM-thrash. Matters on the 4 GiB T400 and the elderly Mac Pro.
|
||||||
|
services.earlyoom.enable = true;
|
||||||
|
|
||||||
|
# Firmware updates via LVFS. No-op on the Asahi MBP (Apple-managed firmware),
|
||||||
|
# useful for UEFI/SSD updates on the x86 hosts.
|
||||||
|
services.fwupd.enable = true;
|
||||||
|
|
||||||
|
# Audio. PipeWire with the PulseAudio shim covers every graphical host; no
|
||||||
|
# per-machine audio config is needed.
|
||||||
|
services.pipewire = {
|
||||||
|
enable = true;
|
||||||
|
pulse.enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# swaylock PAM stack. None of these machines has working fingerprint auth, so
|
||||||
|
# an empty service is enough -- swaylock falls back to password.
|
||||||
|
security.pam.services.swaylock = { };
|
||||||
|
|
||||||
|
# Redistributable firmware (GPU/Wi-Fi/NIC blobs) for the x86 hosts. Harmless
|
||||||
|
# on the Asahi MBP, which supplies its own peripheral firmware out-of-band.
|
||||||
|
hardware.enableRedistributableFirmware = true;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user