Files
nixfiles/hosts/EDaaS
Emma ThorpeandClaude Opus 5 10f713103c
CI / flake (push) Skipped
CI / flake (pull_request) Successful in 3m57s
feat(work): headless Secret Service for gcx keychain tokens
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>
2026-08-11 15:03:18 +01:00
..

Work WSL box — emmathorpe-edaas

Flake host: emmathorpe-edaas (x86_64-linux). NixOS running under NixOS-WSL on the corporate Windows machine. Headless: no Sway desktop (features.swayDesktop.enable = false), plain WSL shell login. Files: configuration.nix.

What this host is

The day-to-day work environment. It layers the corporate Kubernetes / Helm / Terraform / cloud toolchain and a couple of work-only editor language servers on top of the shared home profile. The system config here is thin — it is mostly WSL plumbing; the user-facing tooling lives in ../../users/emmathorpe/work.nix.

WSL specifics

  • wsl.enable, default user emmathorpe, Windows PATH interop and start-menu launchers on. /etc/hosts generation is off (generateHosts = false).
  • Docker Desktop integration, not the native daemon as the primary path: wsl.extraBin shims the coreutils/groupadd/usermod binaries Docker Desktop's wsl-distro-proxy expects, and docker-desktop-proxy.script is patched to the real proxy path. The native virtualisation.docker is also enabled (with enableOnBoot + autoPrune).
  • programs.ssh.systemd-ssh-proxy.enable = false — the NixOS-WSL store is a read-only VHD owned by nobody, and OpenSSH rejects the generated ssh-proxy Include as "Bad owner or permissions", which would break ssh/git for every command. The vsock proxy it provides is unused under WSL.
  • networking.hostName = "emmathorpe-edaas" matches the flake attribute so nh os switch resolves without -H.

Renovate review timer

The host-table entry sets users.emmathorpe.linger = true so the user's systemd --user instance stays alive without an open login session. That keeps the daily headless Renovate PR review timer firing — defined in ../../users/emmathorpe/renovate-review.nix (imported only from work.nix, so it exists on this machine alone). See that file's header for the auth (Vertex AI ADC), triage policy, and caveats.

Secret Service (keychain)

work.nix sets services.headlessSecretService.enable = true, which runs gnome-keyring as a systemd --user service owning org.freedesktop.secrets on the session bus, with the login keyring unlocked at start.

This exists for gcx, the Grafana Cloud CLI. gcx stores its OAuth access and refresh tokens in the keychain unconditionally (its config keeps only opaque keychain:gcx:v2:... handles) and has no plaintext fallback, so without a Secret Service gcx login authenticates and then fails to persist with "The name is not activatable".

Home-manager's own services.gnome-keyring does not work here: it is WantedBy=graphical-session-pre.target, which never activates on this headless box, and it cannot unlock the keyring. See ../../home/secret-service.nix for the full rationale and the security trade-off of an auto-unlocked keyring.

Only the secrets component is started. The ssh component is deliberately off — it would claim SSH_AUTH_SOCK and displace services.ssh-agent, breaking SSH auth and signed commits.

Checking it:

systemctl --user status headless-secret-service
busctl --user list | grep secrets     # expect org.freedesktop.secrets
secret-tool search --all service gcx  # inspect what gcx stored
gcx config check                      # end-to-end

If the keyring password is ever lost or changed, the login keyring cannot be unlocked: delete ~/.local/share/keyrings and re-run gcx login.

stateVersion

system.stateVersion = "24.11" — the release this box was first installed on. Leave it; it freezes stateful defaults and is not meant to track the current nixpkgs.

Apply

sudo nixos-rebuild switch --flake .#emmathorpe-edaas
# or, since the hostname matches the attribute:
nh os switch