az ad group member list and GET /groups/{id}/members return an empty
collection, without error, for groups whose members are service principals.
Records the reads that do work and the rule to trust a Terraform plan over
that output.
Creating a WSP Task now fails with "Task Type is required to create a Task
issue". The field is customfield_15622 and the create validator enforces it
even though createmeta does not list it as required - the same trap the note
already records for Bug's versions field.
Long-running epic spanning multiple sessions, with several findings that were
expensive to establish and that contradict the Jira epic text — most notably
that production runs in the subscription named "Workspace Platform Technical
Preview", not the one named "Production".
Points at ~/code/WSP-32957-CONTINUATION.md for the detail rather than carrying
it here, following the pattern used for the SIBO project.
tmux-yank autodetects WSL and pipes the selection to clip.exe, which
decodes its stdin as the console OEM codepage rather than UTF-8. Copying
an em dash out of a pane put "ΓÇö" on the Windows clipboard; the same
applies to every non-ASCII character.
Override the copy command to route through tmux's own buffer. With
set-clipboard on, that emits OSC 52 and the terminal receives UTF-8
directly, with no Windows-side helper in the path. Windows Terminal
honours OSC 52; verified against the running client.
Guarded on /proc/version so only WSL is affected. iTerm2 does not accept
OSC 52 by default, so the Darwin hosts keep pbcopy.
Set in the plugin's extraConfig rather than the shared block because
yank.tmux bakes the copy command into its key bindings at load time, and
home-manager emits plugin extraConfig before the run-shell.
The sudo-rs section claimed the whole fleet runs the stock "wheel, with a
password" policy, which is no longer true for the WSL host. Explain why the
NixOS-WSL default does not survive the sudo-rs swap, and mark the difference in
the per-host table.
NixOS-WSL sets `security.sudo.wheelNeedsPassword = false`, but that option
belongs to the `security.sudo` module and does not carry over to the sudo-rs
swap in modules/common-nixos.nix, whose equivalent option defaults to true.
Since that swap landed, sudo on this host prompts for the account password --
which WSL set during install and nobody knows -- so escalation only worked
through `wsl -u root`.
Set `security.sudo-rs.wheelNeedsPassword = false` on the host to match the
NixOS-WSL default. Other NixOS hosts are unaffected and keep the prompt.
With no entry in the docs-site nav (removed there so awesome-pages can
discover the synced trees), MkDocs derives the section name from the directory
and title-cases it, rendering "Nixfiles". The previous hardcoded nav spelled it
lowercase. Setting title in docs/.pages restores that without reintroducing a
nav entry.
Verified by rebuilding the aggregated site locally with both source trees
synced as the workflow does.
The docs-site build syncs this repo's README.md and docs/ into the site
tree; nothing else is copied. All prose apart from the README therefore lived
outside the sync and never appeared on https://docs.lyrapup.pet/nixfiles/, and
the one page that did publish carried 18 link targets that resolved to nothing.
Moves:
home/README.md -> docs/shell.md
home/KEYBINDINGS.md -> docs/keybindings.md
hosts/<Name>/README.md -> docs/hosts/<name>.md
docs/.pages and docs/hosts/.pages give the awesome-pages plugin an explicit
order; new pages are picked up by the trailing '...' without an edit.
Links are rewritten so a single URL is correct in both Gitea and the published
site: absolute Gitea source URLs for .nix files and directories, relative links
between pages under docs/, and absolute docs.lyrapup.pet URLs from the root
README, which the build republishes at a different depth from the rest of the
tree. In-code comments that pointed at a moved README are updated to the new
path.
The README gains a Documentation section covering the sync contract and the
linking rules, and CLAUDE.md carries the short version so future edits do not
reintroduce unsynced pages or dead links.
Verified by reproducing the docs-site assembly locally against its pinned
toolchain (mkdocs 1.6.1, mkdocs-material 9.7.7, awesome-pages 2.10.1): pages
render at the URLs used above and in the declared order.
security.sudo-rs.enable sets security.sudo.enable = false via mkDefault, so
this is a straight swap; the two modules assert against being on together.
The fleet only uses the stock policy -- wheel may run anything, with a password
-- which sudo-rs implements fully. It does not cover host aliases, LDAP/SSSD
sudoers, sudoreplay or most Defaults settings; needing any of those means
reverting to security.sudo. The macOS host is unaffected and keeps Apple's sudo
with Touch ID.
Recovery from a host that will not escalate is documented in the module and in
home/README.md: get a root shell that does not go through sudo, then roll back
the generation.
Adds Rust/Go replacements for the day-to-day utilities and shadows four of
them with aliases. Only read-only commands are shadowed (cat, du, df, ps), so a
wrong flag costs a retype rather than data; rm, grep, find and sed keep their
originals and the replacements are reached by their own names.
The aliases land in .zshrc, so they apply to interactive zsh only -- scripts,
`sudo <cmd>` and anything exec'd by another program still get the real binary.
New on every host: dust, dysk, procs, trash-cli, doggo, xh, ouch, jnv, hexyl,
fq and tealdeer. dysk is used rather than duf, which is unmaintained upstream.
git gains difftastic behind a `git dft` alias. diff.external is deliberately
left unset so delta remains the renderer for git diff/show and for anything
parsing them.
The work box gains kubecolor, aliased over kubectl; it wraps the real kubectl
and drops colour when stdout is not a terminal, so pipes are unchanged.
home/README.md documents the alias map, the flag incompatibilities (including
the two that fail silently: dust -s is --apparent-size, and procs reads a bare
`aux` as a search keyword) and the rationale for what was left alone.
The CDI generator aborted with "failed to initialize NVML: Driver Not
Loaded", taking docker.service with it (requiredBy) and failing the
switch.
Two causes. The nixpkgs NVIDIA module only adds nvidia/nvidia_modeset/
nvidia_drm to boot.kernelModules when services.xserver.enable is set,
which is false on this Wayland-only host, so load them explicitly.
nvidia_uvm stays out: the module's modprobe softdep loads it once the GPU
device exists.
The generator also runs during activation, when a module rebuilt against a
new kernel cannot be loaded until reboot -- a guaranteed failure after
every kernel bump. Guard it with ConditionPathExists on
/proc/driver/nvidia/version so it skips rather than fails; the toolkit's
udev rule restarts it when the device appears, so the specs are generated
on the next boot.
The stock GPU has been replaced with a Quadro P400 (Pascal, GP108). Add
hosts/MacPro31/nvidia.nix:
- Driver branch 580 (nvidiaPackages.legacy_580), not the nixpkgs default
production branch (595.x). 580 is the last branch supporting
Maxwell/Pascal/Volta and is an LTS branch until Aug 2028; a newer one
does not drive this card.
- modesetting.enable for Wayland (nvidia-drm.modeset=1), open = false
(the open kernel modules need Turing or later), and sway
--unsupported-gpu, which wlroots requires with the proprietary driver.
- Docker with GPU access via CDI (hardware.nvidia-container-toolkit),
rather than the deprecated virtualisation.docker.enableNvidia runtime
wrapper. Containers run with --device=nvidia.com/gpu=all and must ship
a CUDA 12.x or older runtime: CUDA 13 dropped sm_61.
The driver packages are unfree, so allowlist them in unfreePackages; they
are not cached and the kernel module builds on the host.
Also declare features.cpu.microarchLevel = 1 for this machine: the
Harpertown Xeons have SSE4.1 but no SSE4.2/POPCNT, which switches off
Claude Code through the fleet-wide gate.
Claude Code runs on Node, whose V8 build requires SSE4.2 and POPCNT
(x86-64-v2). On an older x86_64 CPU it does not run, so it must not be
installed there in the first place.
Nix cannot detect the CPU (pure evaluation, hosts often built elsewhere),
so add features.cpu.microarchLevel: the psABI level a host declares about
itself, defaulting to 2. features.claudeCode.enable derives from it, and
home/claude.nix reads that through home-manager's osConfig and installs
nothing -- CLI, CLAUDE.md, output style or memory symlink -- when it is
off. Hosts without the option (Darwin, the standalone homeConfigurations)
keep the tool enabled.
An assertion fails evaluation if a host force-enables the flag below the
required level, so the mistake surfaces in nix flake check rather than as
an illegal-instruction crash on the machine.
The style asked for terseness in tonal terms only, so a dry register wrapped in
headers, tables and a full status recap each turn passed its self-check while
being exactly the verbose output the style exists to prevent.
Add explicit limits: a default length ceiling, headers only for four or more
items, report the delta rather than the accumulated state, and state a caveat
once. Replace the self-check with one that tests length and form rather than
tone.
The repo documents its own build and test commands, but assumes Windows and
PowerShell. This captures only the deltas that make them run on this machine:
dotnet from nixpkgs, artifactory credentials sourced per command because shell
state does not persist between tool calls, and a curl check that distinguishes
an auth failure from a code failure, since a rejected token surfaces as a
NuGet error that reads like a network fault.
Also records the two Docker Desktop leftovers that break the component test
environment, and the unleash registration a component test canary needs.
gcx stores its OAuth access and refresh tokens in the system keychain
unconditionally -- its config file keeps only opaque `keychain:gcx:v2:...`
handles -- and exposes no plaintext fallback. With nothing owning
org.freedesktop.secrets on this headless WSL box, `gcx login` authenticates
against Grafana and then dies writing its config: "The name is not activatable".
Add services.headlessSecretService: gnome-keyring as a systemd --user service,
unlocking the login keyring at start. home-manager's own services.gnome-keyring
does not fit here on two counts -- it is WantedBy graphical-session-pre.target,
which never activates without a desktop session, and it passes no --unlock, so
writes would block on a GUI prompter that does not exist.
Only the secrets component is started. The ssh component is deliberately off: it
would claim SSH_AUTH_SOCK and displace services.ssh-agent, breaking SSH auth and
signed commits.
The unlock password defaults to a random one generated on first activation under
$XDG_DATA_HOME. The passwordFile option is the seam for supplying it from an
agenix secret instead, once that lands.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gcx is the Grafana Cloud CLI (dashboards, datasources, SLOs, synthetic
monitoring, alerts), used against the Citrix Grafana stack.
Pull it from nixpkgs-unstable via the existing overlay rather than the pinned
channel: 26.05 ships 0.2.14, which predates the stacks/contexts configuration
model and the agento11y commands, so the current tooling and docs do not apply
to it.
treefmt runs prettier over markdown in this repository and the CI
formatting check failed on the two preceding commits. Prettier prefers
underscores for emphasis and requires blank lines around fenced code
blocks.
No wording changes.
The previous commit required a ticket scope on every commit in every
repository. This repository has no Jira project, so the rule as written
would either block a commit or invite a fabricated WSP number.
Record the exception: in personal repositories the scope is the area of
the change (claude, deps, hosts) and conventional form still applies.
The ticket requirement is scoped to the Jira-backed work repositories
that enforce it in CI.
The git conventions memory said to match the repository's existing log
style. Several repositories (multicluster, core-services-cloud) have
histories dominated by bare "WSP-1234: summary" subjects, so matching
them produced commits that were not in conventional form. A related
failure was scope decay within a session: the first commit was correct
and later ones degraded to bare "test:" or "refactor:" subjects. Both
required commit history to be rebased by hand.
- Make "<type>(<TICKET-ID>): <summary>" mandatory on every commit and
explicitly override repository log style. Style matching now applies
to branch names only.
- Describe how to establish the real ticket ID (named in the request,
extracted from the branch, or taken from existing commits on the
branch) and require asking rather than guessing when none is
available. Replace the literal WSP-1234 examples with <TICKET-ID> so
the placeholder cannot be committed verbatim.
- Record scope decay across a session as a named failure mode.
- Cover merge commits, preferring rebase and requiring an explicit
message when a merge commit is unavoidable.
- Add a pre-push verification grep that must return no output.
- Note that a clean git log does not prove a subject was correct when
written, because rebasing replaces it; compare author and committer
dates instead.
Update the MEMORY.md index entry to match.
commitizen 4.13.9 ships per-Python-minor golden files for its CLI
regression tests. The py_3_13 golden was captured against an early 3.13
whose argparse did not quote invalid choices. CPython later backported
quoting into the 3.13.x line, and nixos-26.05 now ships 3.13.14, so the
golden no longer matches argparse's output:
-cz: error: ... invalid choice: 'x' (choose from init, commit, ...)
+cz: error: ... invalid choice: 'x' (choose from 'init', 'commit', ...)
This fails commitizen's checkPhase and breaks the home-manager closure.
The package itself is unaffected. Deselect just that test via an overlay
until nixpkgs updates the fixture (or the 3.13.x revert lands upstream).
VS Code on macOS resolves the shell environment at startup by running an
interactive login shell with stdout piped and no controlling terminal.
The order-200 auto-tmux block treated that probe as a normal interactive
shell and ran `exec tmux new-session`, which fails without a tty ("open
terminal failed: not a terminal") and exits non-zero. VS Code then reports
"Unable to resolve your shell environment: Unexpected exit code from
spawned shell (code 1)".
Gate the exec on a real terminal (-t 1) and skip it when
VSCODE_RESOLVING_ENVIRONMENT is set. Real terminals still land in tmux;
the integrated terminal was already exempt via TERM_PROGRAM.
Add two memories and correct one existing, from a review of PR comments
across multicluster and unified-helm over the past two months:
- code_comment_style: no Jira/ticket IDs in code comments by default,
keep comments concise and about the non-obvious why, and use # (not
Helm template) comments where they must reach the rendered manifest.
- copilot_review_false_positives: verify Copilot blocking claims against
the spec and live config before acting; records two Terraform FPs.
- workflow_review_and_comments: drop the now-contradicted 'one-liner +
WSP ticket reference' guidance, which reviewers repeatedly strip.
Upstream will stop defaulting hardware.asahi.enable to true and currently emits an evaluation warning to that effect. Set it explicitly on the lyrathorpe-mbp (MBP-Asahi) host to silence the warning and be robust to the future default change. Verified the warning no longer appears in the host toplevel eval.
Add programs.zsh.envExtra to the EDaaS work profile so ~/.jenkinsenv is
sourced from ~/.zshenv on every zsh invocation (login, interactive, and
non-interactive), exporting the JENKINS_UCE_/JENKINS_STF_ tokens the Jenkins
MCP servers read via ${JENKINS_*} expansion. Guarded so a missing file does
not break the shell; the file is kept out of the world-readable nix store
because it holds secrets.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The code.emmathe.dev key is now loaded in the ssh-agent, so git push works with sandbox off and PRs are raised via the tea CLI. Correct the git-network-ops memory that said Gitea always needs hand-off; keep hand-off as the fallback only if the key drops from the agent.
The gpg.ssh.allowedSignersFile error and %G?=N mean git cannot verify locally, not that the commit is unsigned. Add how to confirm via gpgsig header and how to enable local verification.
Record WSP project field map: issue-type IDs, the six required Bug fields with their allowed values/option IDs and JSON shapes, the Task shortcut that avoids them, and platform-relevant component IDs. Cross-links jira-tooling.
Record that any nixpkgs tool can be run on the fly via nix run / nix
shell, so a missing command during development is not a dead end. Takes
effect after a home-manager rebuild.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Document the formatting and lint gates (treefmt/nixfmt/shfmt/prettier,
deadnix, statix, pre-commit) and how to run them, so changes -- docs
included -- are formatted before commit. Notes the CI detect step that
skips heavy checks on docs-only PRs, which can report a false green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The module-catalogue table in README.md was committed without prettier's
alignment, so the treefmt formatting flake check fails. This has left
CI (nix flake check) red on main since the #56 docs merge and blocks
every PR that triggers the full check.
Reformat with the flake's pinned formatter; no content change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The NixOS-WSL module's wslConf.interop submodule declares only `enabled`
and `appendWindowsPath`. `register` and `includePath` are not valid
wsl.conf interop keys, and the freeform INI type does not cover keys
nested under the already-declared `interop` group, so they were rejected
as unknown options. Remove them.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
catppuccin v2.0.0 renamed its lualine theme files; there is no longer a
plain "catppuccin" theme, only per-flavour files (catppuccin-mocha, etc.)
and catppuccin-nvim. The old name no longer resolved, so lualine fell back
to the auto theme and emitted a notice. Pin to catppuccin-mocha to match
the configured colorscheme flavour.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add system/machine/RPi5/README.md (flash/boot, regenerate hardware-config,
Docker-socket security caveat and remote-client usage, how to add a
reverse-proxy vhost). Add lyrathorpe-rpi5 to the README host table and note
that the swayDesktop flag now lives in system/modules/features.nix so headless
hosts keep TTY login.
Add the aarch64-linux Raspberry Pi 5 host to the host table: the RPi5
machine config, the raspberry-pi-5 nixos-hardware profile, and key-only
sshd. Headless, so no swaywm.nix; base home modules only.
lyrathorpe/user.nix reads features.swayDesktop.enable on every host, but the
option was declared inside lyrathorpe/swaywm.nix -- so a host that does not
import swaywm.nix (a headless server) would fail evaluation. Move the option
declaration to a new always-imported system/modules/features.nix and wire it
into baseModules; swaywm.nix keeps only its implementation (config) block.
Headless hosts can now omit swaywm.nix and the flag defaults to false.
Tie the RPi5 submodules together: import hardware-config, docker.nix and
reverse-proxy.nix; pin networking.hostName to the flake attr name so nh
resolves; use U-Boot/extlinux boot (raspberry-pi-5 profile supplies kernel +
firmware); enable key-only sshd and a default-deny firewall opening 22.
Headless -- swaywm.nix is not imported, so swayDesktop stays off.
Enable nginx with the recommended proxy/TLS/optimisation/gzip settings and a
declarative virtualHosts table -- each proxied service is a Nix entry, so the
routing lives in-repo. Ships one HTTP-only example vhost; enableACME/forceSSL
are present but commented, to be flipped per-vhost once a DNS name and cert
exist. Opens 80 and 443.
Enable Docker and expose the daemon over TCP 2375 by extending the systemd
docker.socket ListenStream (avoids the daemon.json hosts vs unit -H fd://
conflict). The port is not added to allowedTCPPorts; instead an nftables
rule accepts it only from the trusted LAN subnet. Plain 2375 is
root-equivalent, so the source restriction is the only safeguard -- mTLS on
2376 is the documented upgrade path.
Committed so the lyrathorpe-rpi5 host evaluates in CI before the Pi is
provisioned. It is a placeholder, not a bootable config: on first install,
regenerate it on the device with nixos-generate-config and replace this file.
Excluded from formatters/linters by the existing hardware-configuration.nix
rules.
This job is a required status check on main. The workflow was path-filtered
to **.nix/flake.lock/ci.yaml, so a PR touching none of those (e.g. a
.renovaterc.json-only change) skipped the workflow entirely, leaving the
required check pending forever and making the PR unmergeable.
Run the workflow on every PR so the check is always reported, but keep a
'detect' step that diffs the PR against its base and runs nix flake check
and the per-host evals only when a .nix file, flake.lock, or this workflow
changed. When nothing Nix-relevant changed the heavy steps skip and the job
still passes, so the required check stays green-reportable without burning a
full evaluation on unrelated changes. Checkout uses fetch-depth: 0 so the
diff has the base history.
The host inherited the stock NixOS default hostname 'nixos', which does not
match the flake's nixosConfigurations attribute 'emmathorpe-edaas'. nh
selects the configuration by the local hostname, so bare 'nh os switch'
failed to resolve. Pin the hostname to the attribute name so it resolves
without an explicit -H/--hostname flag.
git.nix and work.nix both define user.signingkey. They used to hold the
same value, which types.str tolerates, but git.nix now sets the personal
key while work.nix sets the work key, so the two plain definitions
conflict on the EDaaS host.
Mark git.nix's signingkey as mkDefault, mirroring user.email: personal
hosts get the personal key, and work.nix's plain work-key definition wins
on the work host.
home-manager renders ~/.config/gh/config.yml as a read-only /nix/store
symlink whenever programs.gh is enabled (unconditionally, not gated on
settings). gh rewrites that file on 'gh auth login' and 'gh config set',
which then fail with a permission error.
Suppress the managed config.yml via xdg.configFile and drop the
settings.git_protocol declaration that created it; gh now owns the file.
The token lives in hosts.yml, which home-manager never manages. Set the
SSH protocol at runtime with 'gh config set git_protocol ssh'.
programs.gh.enable (in shell.nix) already installs gh, so the explicit
pkgs.gh was redundant. pkgs.tea was also declared in shell.nix; keep the
single declaration there and remove the duplicate here.
Flesh out programs.htop: tree view, sensible highlights, hidden kernel
threads, left CPU/Memory/Swap bar meters and right Tasks/LoadAverage/Uptime
text meters. color_scheme = 0 inherits the terminal's Catppuccin Mocha
palette, as htop has no custom-theme support of its own.
Drop the explicit pkgs.htop: programs.htop.enable already installs it.
The zcompdump reset was declared under home.actiVation (stray capital V),
an unknown option that fails module evaluation, so the activation script
never ran. Restore the correct home.activation attribute.
Extend the daily Renovate review so it triages instead of only advising,
and surface results in the interactive shell.
- Auto-approve: PRs graded low risk (patch/minor bumps to tooling, infra,
test or framework libs; symmetric diff; CI passing; no app logic) that
are not already approved get an APPROVE review via
pull_request_review_write. These repos automerge on approval, so this
merges them with no human in the loop -- intentional. Medium/high risk,
failing/pending CI, stale branches and anything needing judgement are
left untouched for Emma. No merge tool is granted.
- State + reminder: each run records ~/.local/state/renovate-review/
{last-run,needs-review.txt}. A once-a-day interactive zsh reminder
(programs.zsh.initContent) warns if the timer hasn't run, lists the PRs
needing review, or confirms an all-clear.
Verified: nix build (eval + shellcheck) green; triage parsing and the
reminder's run/stale/all-clear/throttle branches exercised against
synthetic state. The first live auto-approval is left for a supervised
scheduled/manual run.
Add a systemd user timer on the EDaaS/WSL host that runs Claude Code
headless once a day (08:47) to review Renovate dependency PRs awaiting
Emma's review. It queries GitHub via the project-scoped github MCP
server, excludes PRs against archived repositories, grades each PR's
risk, and writes a recommendation-only summary to the journal
(journalctl --user -u renovate-review). It never approves or merges.
- lyrathorpe/home/renovate-review.nix: wrapper + service + timer.
Auth is Vertex AI via the inherited project/region/model env; Claude
Code provisions its own network egress, so no proxy is set. The
prompt lives in a store file so its literal backticks/$ don't trip
shellcheck in the wrapper.
- lyrathorpe/home/work.nix: import the module (host-scoped to EDaaS).
- system/machine/EDaaS/configuration.nix: enable user linger so the
timer fires without an attached login session.
Touch ID for sudo failed because pam_tid can't reach the GUI session
from inside tmux (terminals here auto-start tmux); enable sudo_local
reattach (pam_reattach) so the session is re-attached first. Also drop
the dock autohide and tilesize defaults.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
nix-darwin 26.05 forces activation to run as root, and mas cannot reach
the App Store/StoreKit session from root, so homebrew.masApps silently
failed to install. Remove the masApps list; install those apps by hand
with `mas install <id>` from a GUI Terminal. The mas CLI stays in
systemPackages for that.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Vendor the auto-memory directory into the repo (claude/memory/) and
symlink it read-only into ~/.claude/memory. Recall keeps working; the
runtime "save a memory" path no longer writes there. CLAUDE.md instructs
Claude to add/change memories in this repo and rebuild instead, so the
flake stays the single source of truth. README documents the split.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
programs.claude-code on every host: the global CLAUDE.md (persona) and
the Soviet Engineer output style are now declared. settings.json is left
unmanaged on purpose — Claude rewrites it at runtime (permission grants,
/config) and a read-only store symlink would break those writes. Drops
the now-redundant explicit claude-code package from work.nix (the module
installs it).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Update the home README (editor plugins + format-on-save, btop/lazygit/
hyperfine/sd, git aliases, tmux extrakto, fonts/emoji coverage),
KEYBINDINGS (telescope/trouble/comment), and the top README (shared
module layers + nix-flake-check CI). Correct the last work/default.nix
reference in default.nix.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the formatting-only build with `nix flake check`, so deadnix,
statix and the pre-commit hooks are enforced in CI (not just local
hooks). Add the nix-community binary cache to the runner's nix config to
speed up the check closure. The explicit per-host eval pass is kept for
granular output.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Touch ID now authorises sudo (via sudo_local, update-safe). Adds the
standard system.defaults blocks — dock autohide / no-recents, Finder
extensions + path bar + list view, dark mode, fast key repeat, trackpad
tap-to-click — so the Mac's UI is managed declaratively too.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Polkit authentication agent (lxqt-policykit) as a sway-session user
service — programs.sway only enables the daemon, so GUI auth dialogs
(nemo mount, NM/blueman) previously failed silently. Corrected the
header comment that wrongly claimed the agent was handled system-side.
- kanshi for output/display management (safe internal-panel default; a
documented template for docked/Cinema-Display profiles).
- gammastep night-light (manual location; adjust coordinates).
- inhibit_idle on fullscreen so video doesn't get blanked/locked.
- logind lid policy on the laptops: suspend on battery, lock on AC.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
In common-nixos.nix (every NixOS host):
- programs.nix-ld for all hosts, not just WSL — foreign dynamic binaries
(VS Code server, prebuilt toolchains) run on the dev boxes too. Removed
the now-redundant per-host enable from the EDaaS config.
- nix-community.cachix.org substituter (merges with the Asahi cache).
- Noto sans + colour-emoji fonts and fontconfig defaultFonts mapping, so
the WSL box (and anything asking fontconfig for "monospace") stops
rendering tofu.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- thermald on the x86 hosts (guarded; the Asahi MBP self-governs).
- T400 battery charge thresholds (75/80) via tp_smapi; tlp itself comes
from the nixos-hardware profile.
- Bluetooth (bluez + powerOnBoot) and blueman on the laptops — the MBP
already loads Apple BT firmware but bluez was never running.
- earlyoom + fwupd on the physical graphical hosts; zram on the Mac Pro.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New system/modules/ssh.nix disables password and keyboard-interactive
auth and root login, and installs the authorized key for the primary
user. Imported by the two hosts that run sshd; each still enables the
service and opens port 22 in its own config.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
T400 gets the generic lenovo-thinkpad + common-pc-laptop(-ssd) +
common-cpu-intel blocks (no t400-specific profile exists); this also
enables tlp and the tp_smapi/acpi_call battery tooling. Mac Pro 3,1 gets
common-pc-ssd + common-cpu-intel. nixos-hardware follows our nixpkgs to
keep a single nixpkgs in the closure.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Set programs.zsh.dotDir to ~/.config/zsh, adopting the new home-manager
default and silencing the dotDir deprecation warning on rebuild. Pin
history.path to the existing ~/.zsh_history so the move doesn't orphan it,
and extend the zcompdump-reset activation to the new $ZDOTDIR location
(legacy $HOME and cache paths still swept). README updated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rewrite editor.nix on programs.nixvim, keeping every prior feature: file
tree (nvim-tree, toggle ,,), indent guides (indent-blankline), fugitive,
vim-tmux-navigator, Catppuccin Mocha, 2-space hard tabs, and the
*Jenkinsfile=groovy rule. Replace the inert ALE with a real LSP stack —
nvim-lspconfig, nvim-cmp completion, and tree-sitter for highlighting.
Leader is Space; LSP keymaps gd/gr/K/<leader>rn/<leader>ca.
Universal servers: nil (Nix), lua_ls, pyright (Python), terraformls. The
work box (work.nix) additionally enables omnisharp (C#) and helm_ls (Helm),
so the heavy omnisharp closure stays off the personal machines.
default.nix drops VISUAL=vim so nixvim's defaultEditor owns $EDITOR/$VISUAL.
README and KEYBINDINGS updated (and two stale references corrected).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Declarative Neovim, on the nixos-26.05 release branch and following our
nixpkgs to keep a single nixpkgs in the closure. Consumed by
lyrathorpe/home/editor.nix.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- treefmt-nix drives `nix fmt` and the formatting check (nixfmt/shfmt/
prettier; generated files and flake.lock excluded). Replaces the
bespoke find-based check.
- deadnix and statix as flake checks and pre-commit hooks; deadnix
ignores module-arg patterns, statix.toml disables the two house-style
lints (repeated_keys, empty_pattern). Fixed the one real deadnix hit
(unused overlay arg) and statix hit (use inherit for claude-code).
- git-hooks.nix installs the pre-commit gate via the devShell.
- .editorconfig for the base style.
- Move system/modules/work/default.nix -> lyrathorpe/home/work.nix (it is
a home-manager module). README gains a Development section; docs
reformatted by the new formatter.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- common-nixos: nix.settings.auto-optimise-store + larger download buffer.
- workstation: fstrim, boot.tmp.cleanOnBoot, and the shared graphical
options moved here from the per-host configs (pipewire, swaylock PAM
stub, redistributable firmware) -- MBP-Asahi gains audio it lacked.
- T400: zramSwap for the low-RAM host.
- MBP-Asahi: nixos-apple-silicon binary cache substituter.
- MacPro31 README: describe the real (LVM/UUID) hardware config; it is no
longer a placeholder.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Update the interactive-shell README and keybindings reference for changes
made after the initial docs commit: no scheduled GC (manual only),
NO_TMUX escape hatch, default-terminal tmux-256color + truecolor, the
JetBrainsMono Nerd Font (new Fonts section + iTerm2 caveat), the
UseKeychain IgnoreUnknown guard, and the vim-tmux-navigator (Ctrl-hjkl) +
resurrect save/restore tmux bindings.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
nixpkgs' openssh lacks Apple's keychain patch, so `UseKeychain yes` is
rejected as "Bad configuration option" when that ssh is on PATH. Prefix
it with `IgnoreUnknown UseKeychain` (the module emits IgnoreUnknown first)
so a non-Apple ssh skips it while Apple's ssh still honours it. Still
Darwin-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The tmux statusline draws powerline/Nerd glyphs that default fonts lack,
so they render as blank/"?". tmux runs on every host (not just the Sway
ones), so install the font in the shared common-nixos module rather than
swaywm -- a future console-only or non-Sway host gets it too. The Mac
installs it via the Darwin config (/Library/Fonts). foot names it as its
main font (home/sway.nix).
On macOS, iTerm2's font is still a GUI setting: Settings -> Profiles ->
Text -> Font -> "JetBrainsMono Nerd Font".
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
tmux-direct as default-terminal desyncs zsh's line redraw on some
terminals (iTerm2: duplicated characters on Tab, stray newlines). Switch
to the standard tmux-256color and advertise truecolor per outer terminal
via terminal-features (add xterm-256color:RGB alongside the foot ones).
Also add a NO_TMUX escape hatch to the auto-start guard, so
`NO_TMUX=1 <terminal>` opens a bare shell.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The scheduled `nh clean` only reclaimed disk and risked reaping store
paths the current generation still references (notably on nix-darwin).
Keep `programs.nh` (nicer rebuilds + $NH_FLAKE) but remove clean.enable;
GC manually (`nh clean all` / `nix-collect-garbage -d`) when nothing
important is running. The resetZcompdump activation stays as a safety net
for stale completion dumps across rebuilds/manual GC.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add lyrathorpe/home/README.md covering the zsh / CLI tools / tmux / git /
ssh features and nice-to-haves configured across shell.nix and git.nix
(history, fzf/zoxide/direnv/eza/bat, nix-index, nh, tmux plugins +
auto-start, git aliases/settings/signing, ssh agent + Gitea host, the
zcompdump/GC maintenance behaviours, and per-host differences). Link it
from the top-level README alongside the keybindings reference.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Set HostName 10.187.1.76 on the code.emmathe.dev block so the Gitea
remote resolves to the fixed IP without relying on DNS (same user, port
30009 and key).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The home-manager bump deprecated programs.ssh.addKeysToAgent /
matchBlocks / the implicit default block. Move to programs.ssh.settings
with enableDefaultConfig = false, carrying the old defaults under
settings."*" plus AddKeysToAgent, the Darwin UseKeychain, and the
code.emmathe.dev (Port 30009) host. Silences all three ssh warnings.
Also drop ~/.zcompdump on each activation: a stale dump caches /nix/store
paths to completion functions, and once a rebuild or the weekly nh 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.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The flake's origin (ssh://git@code.emmathe.dev) must resolve on every host.
Add a matchBlock for code.emmathe.dev: user git, Port 30009 (Gitea's
non-default SSH port -- the critical bit), the dedicated
~/.ssh/code.emmathe.dev key, and identitiesOnly. The work box keeps its own
ssh config (programs.ssh forced off there) which already has the entry.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Set user.email = iam@emmathe.dev on the personal hosts (mkDefault, so the
work module's address still wins on the work box). Add git aliases for
commitizen -- `git cz <sub>` (e.g. `git cz c`) and `git cc` for the commit
prompt; commitizen is already installed on every host (home.packages) and
defaults to the Conventional Commits ruleset.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move the tmux auto-start out of the graphical-only desktop layer into the
shared shell config so it also covers WSL, iTerm2 and the Linux console
(folded into programs.zsh.initContent via mkMerge alongside the SSH PS1
block). Same guards: interactive, not-already-in-tmux, not-SSH,
not-VS-Code, tmux-present.
ssh: run a user ssh-agent on Linux (macOS uses launchd) and add keys on
first use (addKeysToAgent), so the passphrase is entered once per login
session instead of per commit/push -- which also feeds commit signing.
macOS additionally caches in the login keychain (UseKeychain). The work
box keeps its own ~/.ssh/config (programs.ssh forced off there); its
ssh-agent still runs via the work module.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Opening a terminal (foot) execs `tmux new-session -A -s main`, so every new
terminal lands in the multiplexer; panes run a plain non-login zsh. Guarded
to interactive, not-already-in-tmux, not-SSH, not-VS-Code, tmux-present --
preventing re-exec loops, hijacked scp/ssh shells, and lockout. Lives in the
graphical desktop layer, so the WSL work box keeps a plain shell.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
settings: pull.rebase + rebase autostash/autosquash, fetch.prune,
merge.conflictStyle=zdiff3, diff histogram + colorMoved, rerere,
commit.verbose, branch.sort, column.ui, help.autocorrect, and a small alias
set (st/co/sw/br/ci/last/unstage/lg). Global ignore file (result, .direnv,
*.swp, .DS_Store).
SSH commit/tag signing on personal hosts too, reusing the existing key
(the work module already signs on the work host). gpgsign is mkDefault so a
host lacking the key in its ssh-agent can disable it -- otherwise commits
there would fail. No personal user.email is set (unknown); signing does not
require one, but author email still falls back to user@host until set.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Vim half of the tmux plugin so Ctrl-h/j/k/l moves seamlessly between vim
splits and tmux panes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Prebuilt nix-index database (follows nixpkgs) so command-not-found works
immediately without a manual `nix-index` run. Consumed in shell.nix.
Lock change is purely additive; existing pins are unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The eval steps listed hosts by hand and still referenced lyrathorpe-x1c,
which was removed (replaced by t400/macpro31), so CI errored. Derive the
NixOS and Darwin host lists from attrNames of nixos/darwinConfigurations
instead, so adding or removing a host needs no workflow change.
Verified locally under bash: all current hosts (edaas, macpro31, mbp,
t400, mac) evaluate; no removed host is referenced.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rebuild after the lock-maintenance bump surfaced two home-manager
warnings on the Firefox config added in #17:
- pin programs.firefox.configPath = ".mozilla/firefox" (the legacy
location the system Firefox uses) to silence the stateVersion<26.05
default-change warning,
- address the add-on by pkgs.stdenv.hostPlatform.system (pkgs.system is
deprecated).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Firefox draws its own chrome and ignores the GTK theme, so theme it at the
browser level. Add the rycee firefox-addons flake input and, in the
home-manager desktop layer, manage the Firefox profile (package = null --
the system programs.firefox in user.nix still provides the binary):
- install the Catppuccin Mocha theme add-on (catppuccin-mocha-mauve; only
the mauve accent is packaged upstream, so it differs slightly from the
blue accent used elsewhere),
- autoDisableScopes = 0 so it applies on first launch,
- ui.systemUsesDarkTheme + prefers-color-scheme override for dark chrome
and page content.
Verified the XPI fetches, user.js renders the prefs, finalPackage is null
(no duplicate Firefox), all Sway hosts eval, and EDaaS is unaffected.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Set gtk.gtk4.theme = config.gtk.theme so any GTK4 app added later is
themed too. GTK4 ignores gtk-theme-name, but home-manager renders this as
an `@import` of the theme's gtk-4.0/gtk.css into ~/.config/gtk-4.0/gtk.css
-- which libadwaita honours, since that file overrides the named colours
it uses (window_bg_color/accent_bg_color/view_bg_color, verified present
in catppuccin-gtk's GTK4 stylesheet). Setting it explicitly (to the same
value as the legacy default) also silences the stateVersion<26.05
default-change warning. nemo (GTK3) is unaffected.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Document every configured shortcut in lyrathorpe/home/KEYBINDINGS.md,
compiled from the rendered configs (so it includes the home-manager Sway
module defaults alongside the custom binds and modes), and link it from
the top-level README. Notes the Dvorak keysym caveat and the
laptop-only brightness keys.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
From emmaisadev/dotfiles (tmux.conf): run a non-login shell
(default-command), drop the stock %/" split keys (the s/v vim splits
already come from reverseSplit), declare foot's terminal-features
(RGB/sync/clipboard/title/ccolour/cstyle), and raise the scrollback to
500000. Alt-arrow pane nav and mouse were already present. Also drop a
stale comment referencing the removed tty1 autostart.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adapted from emmaisadev/dotfiles (sway/config.d) into the Nix config:
- Screenshots to swappy: Print = drag a region, Shift+Print = focused
window (a writeShellScript reusing the grimshot.sh tree/jq logic).
Replaces the old plain full-screen grim->file.
- Workspace cycle Mod+z / Mod+x (prev/next).
- Media keys (playerctl) and mic mute (wpctl source).
- Re-home `focus mode_toggle` onto Mod+Alt+space (Mod+Space is the
launcher now).
- Clipboard history: services.clipman stores copies; Mod+c picks one
through a Catppuccin-themed fuzzel (programs.fuzzel).
- Binding modes: a layout submenu (Mod+y -> s/w/e, which also restores
split-toggle that Mod+e gave up to nemo) and a power menu
(Mod+Shift+x -> lock/exit/sleep/reboot/shutdown). Mod+l still locks
immediately.
- Touchpad tap + natural scroll (laptops; inert on desktop).
- Solid Catppuccin base as the wallpaper (output * bg, no image).
- foot: term=xterm-256color and scrollback 100000 (colours unchanged).
- swaywm.nix: add slurp/swappy/jq/playerctl to the session packages.
Skipped from the dotfiles: named workspaces + app auto-assign, the foot
--server/footclient setup, and pactl/MX-Master/lxqt device-specific bits.
All in the shared files, so every Sway host gets it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Install nemo and bind it to Mod+e (mkForce, overriding the module
default `layout toggle split`). Add a home-manager `gtk` block so GTK
apps match the desktop: the Catppuccin Mocha GTK theme
(catppuccin-gtk, mocha/blue), with Adwaita icons + cursor as before.
Under Sway GTK reads ~/.config/gtk-*/settings.ini directly (no XSettings
daemon), so this themes nemo without extra env.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two desktop-config fixes found together:
1. Keybindings: the previous Mod+Space launcher bind wrapped the set in
`lib.mkMerge [ { ... } (mkOptionDefault ...) ]`. A normal-priority
definition there wins over (and discards) the home-manager module's
default keybindings, which are at mkOptionDefault priority -- so every
default bind (terminal Mod+Return, movement, workspaces, kill, ...) and
even the custom swaylock/volume binds silently vanished; only Mod+Space
survived. That is why Super+Enter opened nothing. Restore the single
mkOptionDefault wrapper (so it merges with the module defaults) and
override just Mod+Space via lib.mkForce to beat the module's default
focus-mode_toggle without a same-priority conflict. Generated config
goes from 1 bindsym back to 57.
2. foot 1.27: the bare [colors] section is deprecated in favour of
[colors-dark], and `[cursor] color` is rejected ("not a valid option:
color"). Move the palette to colors-dark and set the cursor colour via
its `cursor` key ("<text> <cursor>"). `foot --check-config` now passes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
historySubstringSearch bound only the default CSI sequences (^[[A/^[[B),
so Up/Down did nothing at the prompt in foot and iTerm2, which send the
SS3 application-mode sequences (^[OA/^[OB). Bind both forms via
searchUpKey/searchDownKey; this covers foot, iTerm2 and the Linux TTY
(CSI).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ReGreet is plain GTK4 (no libadwaita in its closure), so it defaulted to
light Adwaita and my libadwaita-named colour overrides (window_bg_color,
accent_bg_color, ...) were inert -- a light theme with stray accents.
Force the dark Adwaita variant via GTK_THEME=Adwaita:dark in the greeter
wrapper, and override the GTK4 legacy colour names (theme_bg_color,
theme_fg_color, theme_selected_bg_color, borders, ...) that plain GTK4
actually references. The libadwaita names stay as harmless forward-compat.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>