Skip to content

ci: trust niri.cachix.org substituter in flake-check workflow (ADR-028)#59

Merged
dannyfaris merged 1 commit into
mainfrom
desktop-env/ci-niri-cache-trust
May 28, 2026
Merged

ci: trust niri.cachix.org substituter in flake-check workflow (ADR-028)#59
dannyfaris merged 1 commit into
mainfrom
desktop-env/ci-niri-cache-trust

Conversation

@dannyfaris
Copy link
Copy Markdown
Owner

Summary

Companion to slice 3b.5 (#57). Slice 3b.5 added niri.cachix.org to nix.settings in modules/core/nixos/niri.nix — that configures the substituter on deployed systems after nh os switch. CI's nix daemon reads its config from cachix/install-nix-action's extra_nix_config, which (until this commit) didn't include niri.cachix.org.

Without this trust in CI, flake-check source-builds niri on the x86_64-linux matrix entry when any host imports the desktop-env bundle, and the source build hits the in-flight nixpkgs Rust crate fetcher 403 cascade (rust-lang/crates.io#13482, NixOS/nixpkgs#512735). Slice 3d is the first commit that imports the bundle into a host, so this trust must land first.

Changes

  • Adds extra-substituters = https://niri.cachix.org and the matching extra-trusted-public-keys line to the workflow's extra_nix_config.
  • Uses the extra- prefix so it appends to install-nix-action's defaults rather than replacing them (cache.nixos.org preserved).
  • accept-flake-config = false preserved unchanged — transitive flake inputs still cannot silently mutate config; only this operator-owned line adds new trust. The whitelist principle is intact.
  • Updates the comment block above extra_nix_config to record the rationale, source-of-key, and the cross-arch reasoning.

Cross-arch note

niri.cachix.org serves x86_64-linux only. flake-check builds every host's system.build.toplevel including the aarch64 nixos-vm host, but nixos-vm does not import the niri module — so the aarch64 matrix entry lists the substituter but never queries it. Harmless on aarch64.

Why this is a separate PR from #57

PR #57 was opened with auto-merge enabled and the substituter-trust amendment was originally drafted as an amend + force-push on that branch. PR #57 auto-merged before the amend went out, so the CI workflow half lands as its own PR. Same content the peer review approved; just different commit shape.

Peer review

Reviewed against 8 checkpoints:

  1. extra- prefix correctly appends (per nix.conf(5)).
  2. Block-scalar | format is what install-nix-action@v31 expects.
  3. Public key byte-matches modules/core/nixos/niri.nix:51 and niri-flake's flake.nix lines 480–481.
  4. accept-flake-config = false preserved.
  5. YAML indentation consistent (12-space block-scalar content).
  6. actionlint compatibility — pre-commit confirms.
  7. Cross-arch correctness (above).
  8. Concurrency — extra_nix_config applied per-step at runner startup; cancellation safe.

Test plan

  • actionlint pre-commit pass.
  • CI matrix green on aarch64-linux + x86_64-linux. (This PR re-runs CI on the existing flake-check matrix.)
  • Integration test deferred to slice 3d — once metis imports the desktop bundle, CI will pull niri from the cache rather than source-building.

Refs: #33, #57, ADR-028.

🤖 Generated with Claude Code

Companion to slice 3b.5 (#57). Slice 3b.5 added niri.cachix.org to
nix.settings in modules/core/nixos/niri.nix — that configures the
substituter on deployed systems *after* `nh os switch`. CI's nix daemon
reads its own config from cachix/install-nix-action's extra_nix_config,
which (until this commit) did not include niri.cachix.org.

Without this trust in CI, flake-check source-builds niri on the
x86_64-linux matrix entry when any host imports the desktop-env
bundle, and the source build hits the in-flight nixpkgs Rust crate
fetcher 403 cascade (rust-lang/crates.io#13482,
NixOS/nixpkgs#512735). Slice 3d (next) is the first commit that
imports the bundle into a host, so this trust must land first.

Implementation:

- Adds `extra-substituters = https://niri.cachix.org` and the matching
  `extra-trusted-public-keys` line to the workflow's extra_nix_config.
- Uses the `extra-` prefix so it appends to install-nix-action's
  defaults rather than replacing them (cache.nixos.org is preserved).
- `accept-flake-config = false` is preserved unchanged — transitive
  flake inputs still cannot silently mutate config; only this
  operator-owned line adds new trust. The whitelist principle is
  intact.

Cross-arch note: niri.cachix.org serves x86_64-linux only.
flake-check builds every host's system.build.toplevel including
the aarch64 nixos-vm host, but nixos-vm does not import the niri
module, so the aarch64 matrix entry lists the substituter but
never queries it.

Header comment updated to remove the prior "binary-cache config in a
follow-up workflow tweak" placeholder and replace with the rationale
+ source-of-key + cross-arch reasoning.

Peer-reviewed (8 checkpoints: extra- semantics, block-scalar format,
key byte-match against modules/core/nixos/niri.nix, whitelist
preservation, YAML indent, actionlint compatibility, cross-arch
correctness, concurrency).

Per ADR-028 §Implementation slice 3 (amendment).
@dannyfaris dannyfaris enabled auto-merge (squash) May 28, 2026 11:20
@dannyfaris dannyfaris merged commit cf36d92 into main May 28, 2026
4 checks passed
@dannyfaris dannyfaris deleted the desktop-env/ci-niri-cache-trust branch May 28, 2026 11:24
dannyfaris added a commit that referenced this pull request May 28, 2026
First commit that imports the slice-3 desktop bundles into a real host.
Two-line addition to hosts/metis/default.nix:

- modules/core/nixos/bundles/desktop-env.nix added to `imports`
  (alongside bundles/remote-access.nix).
- home/core/nixos/bundles/desktop-env.nix added to
  hostContext.extraHomeModules (after the shared theming bundle,
  before standalones).

This is the integration test for the four merged slice-3 PRs:
  - #42 (3a) — flake inputs
  - #44 (3b) — system bundle scaffold (niri + greetd + fonts + DMS bridge)
  - #55 (3c) — home bundle scaffold (niri binds + foot + DMS)
  - #57 (3b.5) — niri.cachix.org substituter in nix.settings

…plus the CI-side half currently in flight at #59 (extra_nix_config
in flake-check workflow). This branch is stacked on
desktop-env/ci-niri-cache-trust so the CI workflow trust is in place
when CI evaluates this PR.

Closure delta on metis (expected): niri (from niri.cachix.org), greetd
+ tuigreet, Quickshell + Qt6 + matugen + DMS deps,
xdg-desktop-portal-gnome, polkit-kde-agent-1, gnome-keyring, Inter,
Noto Color Emoji, foot. No regression expected on mercury or
nixos-vm — neither imports the bundles.

Local build verification not possible — dbf is not in nix's
trusted-users, so runtime substituter additions are rejected. The
integration test fires in CI where the GH Actions runner is trusted
and the workflow already declares extra-substituters via #59 (which
this branch inherits). No `nh os switch` here; that's slice 5.

Peer-reviewed (8 checkpoints: bundle placement, no auto-discovery,
closure delta sanity, eval-without-cache, CI matrix arch scoping, CI
ordering / branch base, merge-order safety, edge cases including HM
module order, Stylix font conflicts, pre-commit hooks).

Per ADR-028 §Implementation slice 3.
dannyfaris added a commit that referenced this pull request May 28, 2026
First commit that imports the slice-3 desktop bundles into a real host.
Two-line addition to hosts/metis/default.nix:

- modules/core/nixos/bundles/desktop-env.nix added to `imports`
  (alongside bundles/remote-access.nix).
- home/core/nixos/bundles/desktop-env.nix added to
  hostContext.extraHomeModules (after the shared theming bundle,
  before standalones).

This is the integration test for the four merged slice-3 PRs:
  - #42 (3a) — flake inputs
  - #44 (3b) — system bundle scaffold (niri + greetd + fonts + DMS bridge)
  - #55 (3c) — home bundle scaffold (niri binds + foot + DMS)
  - #57 (3b.5) — niri.cachix.org substituter in nix.settings

…plus the CI-side half currently in flight at #59 (extra_nix_config
in flake-check workflow). This branch is stacked on
desktop-env/ci-niri-cache-trust so the CI workflow trust is in place
when CI evaluates this PR.

Closure delta on metis (expected): niri (from niri.cachix.org), greetd
+ tuigreet, Quickshell + Qt6 + matugen + DMS deps,
xdg-desktop-portal-gnome, polkit-kde-agent-1, gnome-keyring, Inter,
Noto Color Emoji, foot. No regression expected on mercury or
nixos-vm — neither imports the bundles.

Local build verification not possible — dbf is not in nix's
trusted-users, so runtime substituter additions are rejected. The
integration test fires in CI where the GH Actions runner is trusted
and the workflow already declares extra-substituters via #59 (which
this branch inherits). No `nh os switch` here; that's slice 5.

Peer-reviewed (8 checkpoints: bundle placement, no auto-discovery,
closure delta sanity, eval-without-cache, CI matrix arch scoping, CI
ordering / branch base, merge-order safety, edge cases including HM
module order, Stylix font conflicts, pre-commit hooks).

Per ADR-028 §Implementation slice 3.
dannyfaris added a commit that referenced this pull request May 28, 2026
… font size, DMS dynamic-theming off) (#63)

Three pre-activation hardening edits across the desktop-env module set.
Co-motivated: each is needed before slice 5 (first activation on metis)
can run cleanly, but none belongs in the slice 5 PR itself (that one
records the activation outcome). Single commit, three files.

modules/core/nixos/niri.nix
  Add `systemd.user.services.niri.enableDefaultPath = false;`. niri-session
  imports the user-manager PATH at session start; the NixOS default
  would otherwise inject a stripped PATH on niri.service that shadows
  it, breaking binary resolution for niri-spawned helpers. nixpkgs's
  own niri module sets this verbatim at
  nixos/modules/programs/wayland/niri.nix:51; niri-flake's nixosModule
  does not. Required companion when launching niri-session via greetd.

modules/core/nixos/desktop-fonts.nix
  Add `stylix.fonts.sizes.terminal = 11;`. foot 1.15.0 changed
  `dpi-aware` from `auto` to `no`, which Stylix's foot target adopts
  verbatim. Under that default, `:size=N` (points) is multiplied by
  the compositor scale rather than the monitor DPI; on a scale-1
  output the historical sizing reads smaller. 11pt approximates the
  prior visual size; operator may retune in slice 5.

home/core/nixos/dms.nix
  Add `programs.dank-material-shell.enableDynamicTheming = false;` at
  the top level (NOT under `.settings.` — verified against DMS upstream
  `distro/nix/options.nix:58`). Load-bearing for the Stylix-decoupling
  decision (ADR-028 §History 2026-05-29): this is the single gate in
  DMS's `distro/nix/common.nix:19` (`++ lib.optional cfg.enableDynamicTheming
  pkgs.matugen`) that pulls matugen into the closure. With it false,
  matugen is absent from build and runtime and cannot stomp the GTK/Qt
  files Stylix's gtk/qt targets already manage.

  Header comment rewritten: drop the "slice 4 (issue #34) owns the
  customThemeFile" paragraph (slice 4 deferred indefinitely); add a
  decoupling note pointing to ADR-028 §History 2026-05-29; reframe the
  Q10 TODO (enableKeybinds + includes.enable overlap warning) to
  "next iteration on the niri/DMS interface" rather than "revisit at
  slice 4".

Verified:
  - All three files parse + treefmt clean + nix flake check --no-build
    succeeds.
  - Slice 3b.5's substituter whitelist (niri-flake.cache.enable = false
    + nix.settings entries in niri.nix) survives the niri.nix edit.
  - All four DMS option-path references use the correct top-level
    `programs.dank-material-shell.enableDynamicTheming` (peer review
    caught the path during planning; this commit applies the fix).
  - No matugen reference remains in the nix sources outside the dms.nix
    comment that documents the gate.
  - Bundles + hosts/metis/default.nix untouched.

Local build (`nix build .#nixosConfigurations.metis...`) source-builds
niri locally and hits the in-flight crates.io 403 cascade because the
operator's user is not in nix's trusted-users — same constraint hit
during slice 3d. CI is the real integration test (the workflow has
extra-substituters for niri.cachix.org per #59 on main).

Peer-reviewed; three non-blocking nits surfaced + agreed with operator
(comment phrasing fixed; attribute order + TODO cross-reference left
as-is).

Per ADR-028 §Implementation slice 5 prep (post-§History 2026-05-29).
dannyfaris added a commit that referenced this pull request May 28, 2026
… cold runs (#61)

Inserts a cache-nix-action@v7 step between cachix/install-nix-action
and nix flake check. Per-arch cache key keyed on hashFiles('flake.lock')
so lockfile bumps invalidate cleanly; per-arch restore-prefix lets PR
branches restore from default-branch entries (the squash-auto-merge
flow seeds each new entry).

Amortises the non-niri portion of metis's desktop closure across cold
CI runs: Quickshell + Qt6 + DMS (dms-shell) + xwayland-satellite + foot
+ greetd transitive deps. niri itself comes from niri.cachix.org via
the extra-substituters trust added in #59 and doesn't need this;
matugen is suppressed via DMS's enableDynamicTheming = false
(ADR-028 §History 2026-05-29 / PR #63) so it also doesn't enter the
closure.

Configuration choices (all per #61's spec verbatim):
- @v7 — current major tag (released 2026-01-08); inputs unchanged from
  v6; matches the floating-major-tag convention used by the other
  steps in this workflow.
- primary-key on hashFiles('flake.lock'); restore-prefixes-first-match
  per-arch so PR branches fall back to the latest default-branch entry.
- gc-max-store-size-linux: 5G — two arch entries fit exactly under
  GH's 10 GB/repo default cap. Follow-up tracked on #61: revisit
  after the first two runs if entries land smaller.
- 7-day last-accessed purge with primary-key pinned.

Hardening edits folded into this commit after PR #66's first CI
attempt failed with HTTP 404 on api.github.com tarball endpoints
(both arches, ~12s each):

1. Explicit `github_access_token: ${{ secrets.GITHUB_TOKEN }}` on the
   cachix/install-nix-action step. v31's docs claim
   GITHUB_TOKEN-from-env auto-pickup, but the symptom (HTTP 404 on
   unauthenticated api.github.com calls — what GitHub returns to
   anonymous clients past the 60 req/hr limit) suggests the
   auto-pickup didn't apply in our environment. The empty-restore on
   cache-nix-action's first run exposed this: every flake input had
   to fetch from GitHub. Setting the token explicitly makes the auth
   deterministic.

2. Top-level `permissions: contents: read` block. Defense-in-depth
   scope-pin on the workflow's token. The workflow only needs to
   read repo contents (checkout clones; Nix fetches public flake
   inputs); explicit-over-implicit per CLAUDE.md's whitelist > blanket
   stance applied to the token surface. No interaction with the
   `github_access_token` input — different layer.

Why cache-nix-action and not Cachix-hosted / FlakeHub / Attic:
one-operator project; matched-to-scale tooling; no extra trust
delegations beyond GitHub itself. Triggers to revisit (hit rate
<60%, second source-built dep, routine aarch64 source builds) are
documented in #61's body.

Peer-reviewed twice — once for the original cache-nix-action step (12
checkpoints; clean), once for the hardening amend (8 checkpoints; clean
with two non-blocking nits surfaced + agreed with operator: comment
phrasing left as-is, permissions block folded into this amend).

Closes #61.
dannyfaris added a commit that referenced this pull request May 31, 2026
#151)

Amortise the non-niri half of the desktop closure (Quickshell, Qt6,
matugen, DMS, xwayland-satellite, foot and transitive deps) that a
fresh GH Actions runner otherwise rebuilds every cold run.

Output-cache storage (actions/cache-shaped), orthogonal to the
niri.cachix.org substituter trust lines (#59). Keyed on os × arch ×
flake.lock hash; PR branches restore from the os×arch prefix seeded
on main. GC to 5G before save keeps both arch entries under the 10 GB
per-repo limit; a 7-day last-accessed purge sweeps dead prefixes
without evicting the run's own primary key.

cache-nix-action chosen over Cachix/FlakeHub/Attic per the issue's
trust-delegation reasoning; revisit only if hit rate stays below ~60%
over a month or a second major source-built dep arrives.

https://claude.ai/code/session_01R9nbBuTjy2rkiMYJ6NJ1G1

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant