fix(nix): let the registry build authenticate to npm.forgegraf.com #512
No reviewers
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
gmackie/ForgeGraph!512
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/nix-registry-npm-auth"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
npm-registry deploys fail with:
The gap is pre-existing; #509 only exposed it. The registry's dependency fetch is a fixed-output derivation, and that sandbox cannot see
/root/.npmrc— which does hold a valid token. It worked for as long as every dependency was public. Once apps/web took a private package, the first lockfile change that invalidated the cached FOD turned it into a hard failure. Nix'''snetrc-filedoes not help: pnpm reads.npmrc, not netrc, and that file has nonpm.forgegraf.comentry anyway.Worth noting the shape:
agent/internal/registry/npmrc.goalready solves exactly this for the CI path. The deploy path had no equivalent.Fix.
impureEnvVarsis nix'''s supported way to pass a credential into a FOD. The fetch writes an.npmrcfromFG_NPM_TOKENwhen one is set. It goes underTMPDIR, never into$out, so the token cannot reach the store. With no token set, behaviour is byte-identical to before.Verified on hetzner-fg before writing this, rather than reasoning about nix semantics:
nix build .#registrynix hash path --srion it gives the hash recorded hereRequires wiring:
FG_NPM_TOKENmust be in the agent'''s environment when it invokesnix build. I am adding it to/etc/forgegraph/agent.envon hetzner-fg (the existingEnvironmentFilethe unit already reads). The durable version is to carry a registry token in the deployment payload the way CI jobs already do —PendingDeploymenthas no registry field today — which is a server+agent change worth doing separately rather than bundling here.Note the hash also moved because the fetch now succeeds and produces a complete dependency set; the neighbouring comment about refreshing it on lockfile changes still applies.
🤖 Generated with Claude Code
Reviewed. The diagnosis is right — a FOD sandbox genuinely cannot see
/root/.npmrc, andimpureEnvVarsis nix's supported escape hatch. Keeping the npmrc under$TMPDIRrather than$outis the correct instinct. Three things before this lands, one of them blocking.1. Blocking: nothing sets
FG_NPM_TOKEN, so this silently no-opsFG_NPM_TOKENappears only insideflake.nix— three references, all consuming it. Nothing exports it, and it is not a repo Actions secret:Because the guard is
if [ -n "${FG_NPM_TOKEN:-}" ], an unset token takes the silent branch and the fetch 401s exactly as before, with nothing in the log saying the credential was absent.I did this to myself earlier today in #499: an auth block that looked right, guarded on the token being present rather than on the write landing, and the only symptom was the same downstream 401. Worth either reusing
FG_REGISTRY_TOKEN(which exists) or adding the new secret, and making the empty case::warning::rather than silent.2.
impureEnvVarson a daemon build reads the DAEMON's environmentEvery deploy path sources
nix-daemon.sh(6 references indeploy.ymlalone), so builds go through the daemon. For daemon builds the values ofimpureEnvVarsare taken from the nix-daemon's environment, not from the shell that invokesnix build. Exporting it in the workflow step will not reach the FOD unless the daemon service itself carries it (systemctl edit nix-daemon/ anEnvironment=drop-in). Worth confirming on the runner before relying on it.3. It widens the token's blast radius, against this repo's own stated threat model
cloudflare_worker.go:665documents the model explicitly:impureEnvVarsis not scoped to this derivation. Any FOD built on that daemon can declareimpureEnvVars = [ "FG_NPM_TOKEN" ]and receive the value — and ForgeGraph exists to build user-supplied flakes on those hosts. That is the same exfiltration path the git-clone code deliberately avoided, reintroduced through a different door.Not necessarily disqualifying — the token is scoped to one private registry, and the alternative is a broken deploy — but it deserves to be a recorded decision rather than a side effect, and the comment should say so.
A narrower option
Pre-fetching the dependency store outside the FOD (where
/root/.npmrcis visible) and passing it in, or vendoring the one private tarball, keeps the credential out of any sandbox. More moving parts; no shared-daemon exposure.Also
The hash moves to
sha256-BCdknkFh1h63QSsZJsOhdeluh8WrDUVWw0C1oRdMOQw=because the lockfile now takes@preflight/runreport@0.1.2. Worth confirming it was computed against this exact tree rather than an earlier one — the value in #491's description differed from the computed hash by a single character (…KPYVfGFW…vs…KPYVfGFQ…), which would have reproduced the identical failure while looking correct.