Add secret resolver plumbing - #16339
Open
domenkozar wants to merge 10 commits into
Open
Conversation
domenkozar
force-pushed
the
secret-resolver-interface
branch
from
August 21, 2026 16:27
0acbf04 to
f2242a4
Compare
domenkozar
force-pushed
the
secret-resolver-interface
branch
3 times, most recently
from
August 22, 2026 15:21
11d8955 to
0745db1
Compare
Introduce SecretResolver, SecretRequest, and SecretContext for resolving named secrets within a single operation. Requests specify the required representation and describe the consumer, operation, host, and path. Treat an unconfigured secret as an ordinary result distinct from a resolver failure. Require resolvers to support concurrent calls because one operation may run several transfers on different threads. Return materialised files as SecretFile leases. Consumers keep the lease alive while using its path, allowing the resolver to release the secret and remove temporary material once the last consumer finishes. Add callback-based test implementations for resolvers and leased files. Assisted-by: Codex (GPT-5) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xod3FXfS6PFpWYLqmZLME
Fetcher APIs currently receive only global settings, so callers cannot attach dependencies that belong to one fetch operation. Add FetchContext to pair the existing settings with an optional secret resolver. Pass it from evaluation and command entry points through flake registries, input schemes, and the Git, GitHub, Mercurial, and tarball fetchers. This establishes the operation-scoped path for secret resolution without changing fetch behavior yet. Assisted-by: Codex (GPT-5)
Add FileTransferContext and context-aware variants of the enqueue, download, upload, and delete operations. Keep the existing entry points as convenience wrappers that use an empty context. Pass each fetch operation's resolver into HTTP and tarball downloads, registry fetches, Git operations, and Git LFS smudging. This carries the resolver to the transfer item where credentials can be selected while the owning operation is still alive. Assisted-by: Codex (GPT-5)
Add a SecretContext overload of Store::getBuilder() and retain that context through LocalBuilder, Worker, and DerivationBuilderParams. This gives local builds access to the resolver owned by the build operation. Keep the existing builder entry point as an empty-context wrapper. Remote and legacy SSH builders explicitly discard the resolver because its authority is local to the current process and must not cross a remote store boundary. Assisted-by: Codex (GPT-5)
Let openStore() accept a SecretContext and retain it in the resulting Store. HTTP and S3 binary caches pass the resolver to their file transfers, and local builds propagate it to the substituter stores they open. Continue caching resolver-free substituters process-wide. Do not cache stores opened with a resolver, so operation-scoped authority cannot leak to unrelated callers or outlive its operation. Update every store implementation to accept the context. Remote builders still keep resolution on the side of the process performing the work. Assisted-by: Codex (GPT-5)
File transfers currently read the global `netrc-file` setting directly, even when the owning operation provides a secret resolver. Resolve a materialised `netrc` secret from the transfer context first and fall back to `netrc-file` when no resolver or secret is available. Include the destination host in the request so a resolver can return only the credentials relevant to that connection. Perform resolution before enqueueing the transfer and retain the SecretFile lease on the transfer item. This keeps the file available for curl retries and releases it when the transfer finishes. Add tests for resolver precedence, fallback behavior, request scoping, and lease lifetime. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xod3FXfS6PFpWYLqmZLME
builtin:fetchurl writes the host's netrc data into its sandbox and then points the process-wide `netrc-file` setting at that file. This exposes a per-fetch credential through global state and leaves its lifetime tied to the sandbox directory. Serve the sandboxed netrc through a resolver owned by the fetch instead. The resolver supports only materialised `netrc` requests, shares the file between that fetch's transfers, and removes it after the last lease is released. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xod3FXfS6PFpWYLqmZLME
The builtin:fetchurl child currently reads `netrc-file` itself. That bypasses the build's resolver, may run after sandbox setup makes the file unavailable, and would require a forked child to contact the secret broker. Resolve the netrc in the parent and pass its contents to the child in RunChildArgs. Use one makeRunChildArgs() path for all startChild() implementations so parent-side preparation remains consistent. Ask the resolver for inline data and fall back to `netrc-file`. Leave the request unscoped by host because one fetch may try several hashed mirrors before its primary URL. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xod3FXfS6PFpWYLqmZLME
builtin:fetchurl currently points the process-wide `ssl-cert-file` setting at the CA bundle written into its sandbox. A choice made for one fetch can therefore affect unrelated transfers in the same process. Add caFile to FileTransferRequest and use it as an override for curl's CA bundle. Requests without an override continue to use the global setting. Pass the sandbox CA bundle directly on each builtin:fetchurl request. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xod3FXfS6PFpWYLqmZLME
InstallableFlake::getCursors() ignores its AutoCall argument, so commands such as `nix run`, `nix search`, `nix bundle`, and `nix repl` receive a function-valued flake output without applying its default arguments. Auto-call the cursor when requested. Store the result under an `<auto-call>` child in the evaluation cache so a later AutoCall::No lookup cannot incorrectly reuse the called value. Keep the call lazy so cached attributes do not trigger unnecessary evaluation. Add regression coverage for the affected commands and cache behavior. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Curu4eZVEzFJZnA7ZADPz
domenkozar
force-pushed
the
secret-resolver-interface
branch
from
August 24, 2026 01:55
0745db1 to
9cf59cf
Compare
|
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Split out of #16300
We need a way to resolve secrets from external providers without relying on global state.
Secret resolution represents authority: different commands, evaluations, or daemon clients may have access to different secrets.
Passing that authority explicitly also makes it easier to prevent secrets from crossing remote-store and build trust boundaries accidentally.
Some providers materialize secrets as temporary files, so consumers must retain a lease for as long as they use the path.
Implementation
This adds a generic
SecretResolverinterface supporting:Resolver ownership is propagated explicitly through fetch, file-transfer, store, substituter, and build operations.
Stores retain the context with which they were opened and use it when opening related substituters or creating local builders. Remote and restricted stores deliberately do not forward process-local resolver authority across trust boundaries.
Compatibility overloads use an empty context where callers have no resolver authority.
Scope
This PR establishes the interface, ownership model, and propagation paths. It does not yet provide a concrete resolver implementation
or user-facing configuration, and there are currently no production calls to
SecretResolver::resolve().Those will be introduced by follow-up work.
Relationship to #16087
#16087 implements concrete HTTP username/password discovery using credential helpers.
This branch addresses a broader layer: operation-scoped authority for named inline or file-based secrets, including fetches, stores, and builds. The approaches could be combined by supplying HTTP authentication through an explicit operation context instead of a process-global authenticator.