Skip to content

Sketch at Varlink interface for #8602 - #13768

Draft
Ericson2314 wants to merge 2 commits into
NixOS:masterfrom
obsidiansystems:varlink
Draft

Sketch at Varlink interface for #8602#13768
Ericson2314 wants to merge 2 commits into
NixOS:masterfrom
obsidiansystems:varlink

Conversation

@Ericson2314

Copy link
Copy Markdown
Member

Motivation

Just a draft PR. Really, just putting this up for ease of liking for conversation at this moment.

#8602

Context


Add 👍 to pull requests you find important.

The Nix maintainer team uses a GitHub project board to schedule and track reviews.

@artemist

artemist commented Jan 8, 2026

Copy link
Copy Markdown
Member

If non-fixed-output derivations can use dynamic derivations to create fixed-output derivations then that can be used to send data to the internet: e.g. derivation A that should be fully sandboxed could create fixed-output derivation B with a curl command sending information to a remote server.

@xokdvium

xokdvium commented Jan 8, 2026

Copy link
Copy Markdown
Contributor

Isn't that an existing issue with recursive-nix?

@artemist

artemist commented Jan 9, 2026

Copy link
Copy Markdown
Member

Yes, I had just noticed the issue now though

@Ericson2314

Copy link
Copy Markdown
Member Author

We could prohibit that.

@artemist
artemist force-pushed the varlink branch 3 times, most recently from f026d02 to 40f4931 Compare April 27, 2026 20:03
@github-actions github-actions Bot added the with-tests Issues related to testing. PRs with tests have some priority label Apr 27, 2026
@github-actions github-actions Bot added the store Issues and pull requests concerning the Nix store label Apr 28, 2026
@artemist
artemist force-pushed the varlink branch 2 times, most recently from 6594ccb to 45c541e Compare April 28, 2026 20:50
# The added files may only reference the runtime closures of the calling derivation's
# inputs or file system objects that have already been created via the Varlink interface.
#
# The file descriptor may be a regular fd referencing a file or a dirfd.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong, the file descriptor is a *socket( over which a NAR and (later) also arbitrary many file descriptors for individual files are sent.

@artemist
artemist force-pushed the varlink branch 2 times, most recently from f35b599 to a76ae11 Compare April 29, 2026 15:19
amaanq added a commit to obsidiansystems/nix-ninja that referenced this pull request May 11, 2026
NixOS/nix#15793 lets builds with the builder-rpc-v0 system feature
talk to the daemon over the worker protocol's restricted allowlist
plus a new SubmitOutput op, which removes the need for the
recursive-nix subprocess dance. SubmitOutput enforces
outputPathName(callingDrv.name, \"out\") on the submitted path, so the
final drv is re-uploaded under that canonical name and submitted —
its bytes come from Tools.uploaded_drvs (populated at upload time)
because under builder-rpc-v0 the .drv only exists in the daemon's
store, never in the inner sandbox FS. NixOS/nix#12727 tracks
accepting SingleDerivedPath::Built so the rename trick can go away.

mkMesonPackage moves to a raw derivation — stdenv's _assignFirst dies
when submit-output mode skips \$out/\$outputs — so NIX_CFLAGS_COMPILE
suffixed by stdenv.cc.suffixSalt and PKG_CONFIG_PATH are populated by
hand from nativeBuildInputs in place of cc-wrapper's setup hooks.
Parallel to Path A (NixOS/nix#13768); both stay until upstream picks
one.
amaanq added a commit to obsidiansystems/nix-ninja that referenced this pull request May 11, 2026
NixOS/nix#15793 lets builds with the builder-rpc-v0 system feature
talk to the daemon over the worker protocol's restricted allowlist
plus a new SubmitOutput op, which removes the need for the
recursive-nix subprocess dance. SubmitOutput enforces
outputPathName(callingDrv.name, \"out\") on the submitted path, so the
final drv is re-uploaded under that canonical name and submitted —
its bytes come from Tools.uploaded_drvs (populated at upload time)
because under builder-rpc-v0 the .drv only exists in the daemon's
store, never in the inner sandbox FS. NixOS/nix#12727 tracks
accepting SingleDerivedPath::Built so the rename trick can go away.

mkMesonPackage moves to a raw derivation — stdenv's _assignFirst dies
when submit-output mode skips \$out/\$outputs — so NIX_CFLAGS_COMPILE
suffixed by stdenv.cc.suffixSalt and PKG_CONFIG_PATH are populated by
hand from nativeBuildInputs in place of cc-wrapper's setup hooks.
Parallel to Path A (NixOS/nix#13768); both stay until upstream picks
one.
amaanq added a commit to obsidiansystems/nix-ninja that referenced this pull request May 11, 2026
NixOS/nix#15793 lets builds with the builder-rpc-v0 system feature
talk to the daemon over the worker protocol's restricted allowlist
plus a new SubmitOutput op, which removes the need for the
recursive-nix subprocess dance. SubmitOutput enforces
outputPathName(callingDrv.name, \"out\") on the submitted path, so the
final drv is re-uploaded under that canonical name and submitted —
its bytes come from Tools.uploaded_drvs (populated at upload time)
because under builder-rpc-v0 the .drv only exists in the daemon's
store, never in the inner sandbox FS. NixOS/nix#12727 tracks
accepting SingleDerivedPath::Built so the rename trick can go away.

mkMesonPackage moves to a raw derivation — stdenv's _assignFirst dies
when submit-output mode skips \$out/\$outputs — so NIX_CFLAGS_COMPILE
suffixed by stdenv.cc.suffixSalt and PKG_CONFIG_PATH are populated by
hand from nativeBuildInputs in place of cc-wrapper's setup hooks.
Parallel to Path A (NixOS/nix#13768); both stay until upstream picks
one.
amaanq added a commit to obsidiansystems/nix-ninja that referenced this pull request May 11, 2026
NixOS/nix#15793 lets builds with the builder-rpc-v0 system feature
talk to the daemon over the worker protocol's restricted allowlist
plus a new SubmitOutput op, which removes the need for the
recursive-nix subprocess dance. SubmitOutput enforces
outputPathName(callingDrv.name, \"out\") on the submitted path, so the
final drv is re-uploaded under that canonical name and submitted —
its bytes come from Tools.uploaded_drvs (populated at upload time)
because under builder-rpc-v0 the .drv only exists in the daemon's
store, never in the inner sandbox FS. NixOS/nix#12727 tracks
accepting SingleDerivedPath::Built so the rename trick can go away.

mkMesonPackage moves to a raw derivation — stdenv's _assignFirst dies
when submit-output mode skips \$out/\$outputs — so NIX_CFLAGS_COMPILE
suffixed by stdenv.cc.suffixSalt and PKG_CONFIG_PATH are populated by
hand from nativeBuildInputs in place of cc-wrapper's setup hooks.
Parallel to Path A (NixOS/nix#13768); both stay until upstream picks
one.
amaanq added a commit to obsidiansystems/nix-ninja that referenced this pull request May 11, 2026
NixOS/nix#15793 lets builds with the builder-rpc-v0 system feature
talk to the daemon over the worker protocol's restricted allowlist
plus a new SubmitOutput op, which removes the need for the
recursive-nix subprocess dance. SubmitOutput enforces
outputPathName(callingDrv.name, \"out\") on the submitted path, so the
final drv is re-uploaded under that canonical name and submitted —
its bytes come from Tools.uploaded_drvs (populated at upload time)
because under builder-rpc-v0 the .drv only exists in the daemon's
store, never in the inner sandbox FS. NixOS/nix#12727 tracks
accepting SingleDerivedPath::Built so the rename trick can go away.

mkMesonPackage moves to a raw derivation — stdenv's _assignFirst dies
when submit-output mode skips \$out/\$outputs — so NIX_CFLAGS_COMPILE
suffixed by stdenv.cc.suffixSalt and PKG_CONFIG_PATH are populated by
hand from nativeBuildInputs in place of cc-wrapper's setup hooks.
Parallel to Path A (NixOS/nix#13768); both stay until upstream picks
one.
amaanq added a commit to obsidiansystems/nix-ninja that referenced this pull request May 11, 2026
NixOS/nix#15793 lets builds with the builder-rpc-v0 system feature
talk to the daemon over the worker protocol's restricted allowlist
plus a new SubmitOutput op, which removes the need for the
recursive-nix subprocess dance. SubmitOutput enforces
outputPathName(callingDrv.name, \"out\") on the submitted path, so the
final drv is re-uploaded under that canonical name and submitted —
its bytes come from Tools.uploaded_drvs (populated at upload time)
because under builder-rpc-v0 the .drv only exists in the daemon's
store, never in the inner sandbox FS. NixOS/nix#12727 tracks
accepting SingleDerivedPath::Built so the rename trick can go away.

mkMesonPackage moves to a raw derivation — stdenv's _assignFirst dies
when submit-output mode skips \$out/\$outputs — so NIX_CFLAGS_COMPILE
suffixed by stdenv.cc.suffixSalt and PKG_CONFIG_PATH are populated by
hand from nativeBuildInputs in place of cc-wrapper's setup hooks.
Parallel to Path A (NixOS/nix#13768); both stay until upstream picks
one.
amaanq added a commit to obsidiansystems/nix-ninja that referenced this pull request May 12, 2026
NixOS/nix#15793 lets builds with the builder-rpc-v0 system feature
talk to the daemon over the worker protocol's restricted allowlist
plus a new SubmitOutput op, which removes the need for the
recursive-nix subprocess dance. SubmitOutput enforces
outputPathName(callingDrv.name, \"out\") on the submitted path, so the
final drv is re-uploaded under that canonical name and submitted —
its bytes come from Tools.uploaded_drvs (populated at upload time)
because under builder-rpc-v0 the .drv only exists in the daemon's
store, never in the inner sandbox FS. NixOS/nix#12727 tracks
accepting SingleDerivedPath::Built so the rename trick can go away.

mkMesonPackage moves to a raw derivation — stdenv's _assignFirst dies
when submit-output mode skips \$out/\$outputs — so NIX_CFLAGS_COMPILE
suffixed by stdenv.cc.suffixSalt and PKG_CONFIG_PATH are populated by
hand from nativeBuildInputs in place of cc-wrapper's setup hooks.
Parallel to Path A (NixOS/nix#13768); both stay until upstream picks
one.
amaanq added a commit to obsidiansystems/nix-ninja that referenced this pull request May 12, 2026
NixOS/nix#15793 lets builds with the builder-rpc-v0 system feature
talk to the daemon over the worker protocol's restricted allowlist
plus a new SubmitOutput op, which removes the need for the
recursive-nix subprocess dance. SubmitOutput enforces
outputPathName(callingDrv.name, \"out\") on the submitted path, so the
final drv is re-uploaded under that canonical name and submitted —
its bytes come from Tools.uploaded_drvs (populated at upload time)
because under builder-rpc-v0 the .drv only exists in the daemon's
store, never in the inner sandbox FS. NixOS/nix#12727 tracks
accepting SingleDerivedPath::Built so the rename trick can go away.

mkMesonPackage moves to a raw derivation — stdenv's _assignFirst dies
when submit-output mode skips \$out/\$outputs — so NIX_CFLAGS_COMPILE
suffixed by stdenv.cc.suffixSalt and PKG_CONFIG_PATH are populated by
hand from nativeBuildInputs in place of cc-wrapper's setup hooks.
Parallel to Path A (NixOS/nix#13768); both stay until upstream picks
one.
amaanq added a commit to obsidiansystems/nix-ninja that referenced this pull request May 12, 2026
NixOS/nix#15793 lets builds with the builder-rpc-v0 system feature
talk to the daemon over the worker protocol's restricted allowlist
plus a new SubmitOutput op, which removes the need for the
recursive-nix subprocess dance. SubmitOutput enforces
outputPathName(callingDrv.name, \"out\") on the submitted path, so the
final drv is re-uploaded under that canonical name and submitted —
its bytes come from Tools.uploaded_drvs (populated at upload time)
because under builder-rpc-v0 the .drv only exists in the daemon's
store, never in the inner sandbox FS. NixOS/nix#12727 tracks
accepting SingleDerivedPath::Built so the rename trick can go away.

mkMesonPackage moves to a raw derivation — stdenv's _assignFirst dies
when submit-output mode skips \$out/\$outputs — so NIX_CFLAGS_COMPILE
suffixed by stdenv.cc.suffixSalt and PKG_CONFIG_PATH are populated by
hand from nativeBuildInputs in place of cc-wrapper's setup hooks.
Parallel to Path A (NixOS/nix#13768); both stay until upstream picks
one.
amaanq added a commit to obsidiansystems/nix-ninja that referenced this pull request May 12, 2026
NixOS/nix#15793 lets builds with the builder-rpc-v0 system feature
talk to the daemon over the worker protocol's restricted allowlist
plus a new SubmitOutput op, which removes the need for the
recursive-nix subprocess dance. SubmitOutput enforces
outputPathName(callingDrv.name, \"out\") on the submitted path, so the
final drv is re-uploaded under that canonical name and submitted —
its bytes come from Tools.uploaded_drvs (populated at upload time)
because under builder-rpc-v0 the .drv only exists in the daemon's
store, never in the inner sandbox FS. NixOS/nix#12727 tracks
accepting SingleDerivedPath::Built so the rename trick can go away.

mkMesonPackage moves to a raw derivation — stdenv's _assignFirst dies
when submit-output mode skips \$out/\$outputs — so NIX_CFLAGS_COMPILE
suffixed by stdenv.cc.suffixSalt and PKG_CONFIG_PATH are populated by
hand from nativeBuildInputs in place of cc-wrapper's setup hooks.
Parallel to Path A (NixOS/nix#13768); both stay until upstream picks
one.
amaanq added a commit to obsidiansystems/nix-ninja that referenced this pull request May 13, 2026
NixOS/nix#15793 lets builds with the builder-rpc-v0 system feature
talk to the daemon over the worker protocol's restricted allowlist
plus a new SubmitOutput op, which removes the need for the
recursive-nix subprocess dance. SubmitOutput enforces
outputPathName(callingDrv.name, \"out\") on the submitted path, so the
final drv is re-uploaded under that canonical name and submitted —
its bytes come from Tools.uploaded_drvs (populated at upload time)
because under builder-rpc-v0 the .drv only exists in the daemon's
store, never in the inner sandbox FS. NixOS/nix#12727 tracks
accepting SingleDerivedPath::Built so the rename trick can go away.

mkMesonPackage moves to a raw derivation — stdenv's _assignFirst dies
when submit-output mode skips \$out/\$outputs — so NIX_CFLAGS_COMPILE
suffixed by stdenv.cc.suffixSalt and PKG_CONFIG_PATH are populated by
hand from nativeBuildInputs in place of cc-wrapper's setup hooks.
Parallel to Path A (NixOS/nix#13768); both stay until upstream picks
one.
artemist pushed a commit to obsidiansystems/nix-ninja that referenced this pull request May 14, 2026
NixOS/nix#15793 lets builds with the builder-rpc-v0 system feature
talk to the daemon over the worker protocol's restricted allowlist
plus a new SubmitOutput op, which removes the need for the
recursive-nix subprocess dance. SubmitOutput enforces
outputPathName(callingDrv.name, \"out\") on the submitted path, so the
final drv is re-uploaded under that canonical name and submitted —
its bytes come from Tools.uploaded_drvs (populated at upload time)
because under builder-rpc-v0 the .drv only exists in the daemon's
store, never in the inner sandbox FS. NixOS/nix#12727 tracks
accepting SingleDerivedPath::Built so the rename trick can go away.

mkMesonPackage moves to a raw derivation — stdenv's _assignFirst dies
when submit-output mode skips \$out/\$outputs — so NIX_CFLAGS_COMPILE
suffixed by stdenv.cc.suffixSalt and PKG_CONFIG_PATH are populated by
hand from nativeBuildInputs in place of cc-wrapper's setup hooks.
Parallel to Path A (NixOS/nix#13768); both stay until upstream picks
one.
artemist added a commit to obsidiansystems/nix-ninja that referenced this pull request May 14, 2026
NixOS/nix#15793 lets builds with the builder-rpc-v0 system feature
talk to the daemon over the worker protocol's restricted allowlist
plus a new SubmitOutput op, which removes the need for the
recursive-nix subprocess dance. NixOS/nix#12727 tracks
accepting SingleDerivedPath::Built so the rename trick can go away.

mkMesonPackage moves to a raw derivation — stdenv's _assignFirst dies
when submit-output mode skips \$out/\$outputs — so NIX_CFLAGS_COMPILE
suffixed by stdenv.cc.suffixSalt and PKG_CONFIG_PATH are populated by
hand from nativeBuildInputs in place of cc-wrapper's setup hooks.
Parallel to Path A (NixOS/nix#13768); both stay until upstream picks
one.

Co-Authored-By: Artemis Tosini <artemis.tosini@obsidian.systems>
artemist added a commit to obsidiansystems/nix-ninja that referenced this pull request May 15, 2026
NixOS/nix#15793 lets builds with the builder-rpc-v0 system feature
talk to the daemon over the worker protocol's restricted allowlist
plus a new SubmitOutput op, which removes the need for the
recursive-nix subprocess dance. NixOS/nix#12727 tracks
accepting SingleDerivedPath::Built so the rename trick can go away.

mkMesonPackage moves to a raw derivation — stdenv's _assignFirst dies
when submit-output mode skips \$out/\$outputs — so NIX_CFLAGS_COMPILE
suffixed by stdenv.cc.suffixSalt and PKG_CONFIG_PATH are populated by
hand from nativeBuildInputs in place of cc-wrapper's setup hooks.
Parallel to Path A (NixOS/nix#13768); both stay until upstream picks
one.

Co-Authored-By: Artemis Tosini <artemis.tosini@obsidian.systems>
artemist added a commit to obsidiansystems/nix-ninja that referenced this pull request May 15, 2026
NixOS/nix#15793 lets builds with the builder-rpc-v0 system feature
talk to the daemon over the worker protocol's restricted allowlist
plus a new SubmitOutput op, which removes the need for the
recursive-nix subprocess dance. NixOS/nix#12727 tracks
accepting SingleDerivedPath::Built so the rename trick can go away.

mkMesonPackage moves to a raw derivation — stdenv's _assignFirst dies
when submit-output mode skips \$out/\$outputs — so NIX_CFLAGS_COMPILE
suffixed by stdenv.cc.suffixSalt and PKG_CONFIG_PATH are populated by
hand from nativeBuildInputs in place of cc-wrapper's setup hooks.
Parallel to Path A (NixOS/nix#13768); both stay until upstream picks
one.

Co-Authored-By: Artemis Tosini <artemis.tosini@obsidian.systems>
Ericson2314 and others added 2 commits June 22, 2026 14:58
…outputs.

Although the tests use libnix-store, the protocol is simple enough that
it can be implemented by others relatively easily.

Co-Authored-By: John Ericson <John.Ericson@Obsidian.Systems>
Co-Authored-By: Artemis Tosini <artemis.tosini@obsidian.systems>
Ericson2314 pushed a commit to obsidiansystems/nix-ninja that referenced this pull request Jul 22, 2026
NixOS/nix#15793 lets builds with the builder-rpc-v0 system feature
talk to the daemon over the worker protocol's restricted allowlist
plus a new SubmitOutput op, which removes the need for the
recursive-nix subprocess dance. NixOS/nix#12727 tracks
accepting SingleDerivedPath::Built so the rename trick can go away.

mkMesonPackage moves to a raw derivation — stdenv's _assignFirst dies
when submit-output mode skips \$out/\$outputs — so NIX_CFLAGS_COMPILE
suffixed by stdenv.cc.suffixSalt and PKG_CONFIG_PATH are populated by
hand from nativeBuildInputs in place of cc-wrapper's setup hooks.
Parallel to Path A (NixOS/nix#13768); both stay until upstream picks
one.

- Move uploaded_drvs into BuilderRpcClient

- Move rpc_client out of Tools

  They are different things, and are needed in different places

- Remove most instances of shelling out to nix

Co-Authored-By: Artemis Tosini <artemis.tosini@obsidian.systems>
Ericson2314 added a commit to obsidiansystems/nix-ninja that referenced this pull request Jul 23, 2026
NixOS/nix#15793 lets builds with the builder-rpc-v0 system feature
talk to the daemon over the worker protocol's restricted allowlist
plus a new SubmitOutput op, which removes the need for the
recursive-nix subprocess dance. NixOS/nix#12727 tracks
accepting SingleDerivedPath::Built so the rename trick can go away.

mkMesonPackage moves to a raw derivation — stdenv's _assignFirst dies
when submit-output mode skips \$out/\$outputs — so NIX_CFLAGS_COMPILE
suffixed by stdenv.cc.suffixSalt and PKG_CONFIG_PATH are populated by
hand from nativeBuildInputs in place of cc-wrapper's setup hooks.
Parallel to Path A (NixOS/nix#13768); both stay until upstream picks
one.

- Move uploaded_drvs into BuilderRpcClient

- Move rpc_client out of Tools

  They are different things, and are needed in different places

- Remove all instances of shelling out to nix, daemon protocol handles
  everything now.

Co-Authored-By: Artemis Tosini <artemis.tosini@obsidian.systems>
Co-Authored-By: John Ericson <john.ericson@obsidian.systems>
Ericson2314 added a commit to obsidiansystems/nix-ninja that referenced this pull request Jul 23, 2026
NixOS/nix#15793 lets builds with the builder-rpc-v0 system feature
talk to the daemon over the worker protocol's restricted allowlist
plus a new SubmitOutput op, which removes the need for the
recursive-nix subprocess dance. NixOS/nix#12727 tracks
accepting SingleDerivedPath::Built so the rename trick can go away.

mkMesonPackage moves to a raw derivation — stdenv's _assignFirst dies
when submit-output mode skips \$out/\$outputs — so NIX_CFLAGS_COMPILE
suffixed by stdenv.cc.suffixSalt and PKG_CONFIG_PATH are populated by
hand from nativeBuildInputs in place of cc-wrapper's setup hooks.
Parallel to Path A (NixOS/nix#13768); both stay until upstream picks
one.

- Move uploaded_drvs into BuilderRpcClient

- Move rpc_client out of Tools

  They are different things, and are needed in different places

- Remove all instances of shelling out to nix, daemon protocol handles
  everything now.

- Add a test to make sure adding executable files to the store works

Co-Authored-By: Artemis Tosini <artemis.tosini@obsidian.systems>
Co-Authored-By: John Ericson <john.ericson@obsidian.systems>
Ericson2314 added a commit to pdtpartners/nix-ninja that referenced this pull request Jul 23, 2026
NixOS/nix#15793 lets builds with the builder-rpc-v0 system feature
talk to the daemon over the worker protocol's restricted allowlist
plus a new SubmitOutput op, which removes the need for the
recursive-nix subprocess dance. NixOS/nix#12727 tracks
accepting SingleDerivedPath::Built so the rename trick can go away.

mkMesonPackage moves to a raw derivation — stdenv's _assignFirst dies
when submit-output mode skips \$out/\$outputs — so NIX_CFLAGS_COMPILE
suffixed by stdenv.cc.suffixSalt and PKG_CONFIG_PATH are populated by
hand from nativeBuildInputs in place of cc-wrapper's setup hooks.
Parallel to Path A (NixOS/nix#13768); both stay until upstream picks
one.

- Move uploaded_drvs into BuilderRpcClient

- Move rpc_client out of Tools

  They are different things, and are needed in different places

- Remove all instances of shelling out to nix, daemon protocol handles
  everything now.

- Add a test to make sure adding executable files to the store works

Co-Authored-By: Artemis Tosini <artemis.tosini@obsidian.systems>
Co-Authored-By: John Ericson <john.ericson@obsidian.systems>
@domenkozar

domenkozar commented Sep 4, 2026

Copy link
Copy Markdown
Member

I think this draft is the right place to define a dedicated builder-facing API. I would prefer not to stabilize the Nix daemon worker protocol as a derivation-facing ABI.

#15793 has now merged builder-rpc-v0. Its stated reason for reusing the daemon protocol was reducing implementation work, and the current manual describes it as a limited form of the Nix daemon socket. The discussion also characterized v0 as exploratory and possibly not something that will be stabilized. Could we explicitly keep it as a prototype/compatibility mechanism while developing the dedicated interface here?

The three operations in this PR look like the right capability boundary:

  • AddToStore
  • AddDerivation
  • SubmitOutput

I would change how AddToStore transports its NAR. The current design requires SCM_RIGHTS, but file-descriptor passing is deliberately not part of Varlink because it is transport-specific. It also excludes Windows named pipes. The Varlink specification recommends upgrade for large or non-JSON payloads.

One possible API is:

method BeginAddToStore(
    name: string,
    method: string
) -> (uploadId: string)

method FinishAddToStore(
    uploadId: string
) -> (path: string)

BeginAddToStore is called with the standard Varlink upgrade: true header. The server confirms the upgrade and returns uploadId; the upgraded connection then carries exactly one NAR. The client closes the connection after writing it. FinishAddToStore runs on a fresh Varlink connection, waits for ingestion to finish, and returns the resulting store path or a typed error.

This does not introduce another RPC or binary framing format. Control messages remain Varlink, and the only post-upgrade payload is one existing NAR stream. JSON-RPC 2.0 would not solve this part: its base specification defines neither stream framing nor binary transfer, whereas Varlink already provides an IDL, typed errors, introspection, and the upgrade mechanism.

The same interface can be transported over:

  • Unix: an AF_UNIX stream socket.
  • Windows: a local duplex, byte-mode named pipe carrying the same NUL-delimited Varlink records.

Varlink permits any connection-oriented transport, but it does not currently define a standard Windows named-pipe address or guarantee that bindings implement one. We would therefore need a named-pipe transport implementation and address convention, without changing the Varlink call/reply semantics. For concurrent calls, the Windows listener needs multiple pipe instances or overlapped I/O. It should use PIPE_REJECT_REMOTE_CLIENTS and an explicit DACL restricted to the identity assigned to the build.

Independently of transport, the service should remain per-build and capability-limited:

  • no general store/daemon operations;
  • references restricted to the parent’s permitted inputs and objects created through this endpoint;
  • output names restricted to those declared by the parent;
  • aborted uploads cleaned up and subject to resource limits.

There is also an unresolved security rule: reference restrictions alone do not prevent a generated derivation from acquiring capabilities its parent lacked, for example by generating a fixed-output derivation that receives network access. Before stabilization, AddDerivation needs an explicit rule preventing generated derivations from escalating impurity, network, or other build capabilities.

Would maintainers be open to revising this draft along those lines, using #15810 for the bootstrap-friendly client/shim story, and keeping the daemon-protocol implementation confined to experimental builder-rpc-v0?

@Ericson2314

Copy link
Copy Markdown
Member Author

Thanks @domenkozar, that is very much along the line of what I was thinking for next steps, and I am glad to hear that you are interested in this feature and figuring out its details.

Some things I would like to add are:

  • systemd has lots of precedent for sockets and SCM_RIGHTS. varlink vs varlinkctl, and the latter's --push-fd= indicates how systemd is effectively making its own varlink++. On Unix, I do think that is the one to target.
  • On windows we can do WSADuplicateSocket and DuplicateHandle for something a bit similar to SCM_RIGHTS, but just (alas) not tied to the socket.
  • an in-band fallback when neither are available is still fine with me.

@xokdvium and I were interested in the SCM_RIGHTS approach because it has the potential to be much more efficient, thanks to stuff like copy_file_range where data doesn't need to be transferred at all, and also support a rootless Nix daemon securely. (With toot you can receive paths and do copy_file_range on freshly-opened files, but without root you wouldn't be able to open the paths received in general.)

@artemist

artemist commented Sep 4, 2026

Copy link
Copy Markdown
Member

Derivations with network access generated by cargo-dyndrv sound convenient, but I don't think they're needed in the majority of cases.

e.g. before a program can generate dynamic derivations for Rust crates, it needs the full dependency tree crate manifests. By far the easiest way to accomplish this is importCargoLock, which downloads the entire sources of every dependency in the graph, so by the time the generator runs there is no more network access to be done.

@artemist

artemist commented Sep 4, 2026

Copy link
Copy Markdown
Member

I just noticed that JSON derivations are less flexible than ATerm, since ATerm derivations are capable of representing bytes that are not valid Unicode, but JSON is not (the specification describes it as a sequence of Unicode codepoints, not a sequence of bytes. It can then be encoded in any Unicode encoding, though Nix always uses UTF-8 iirc.)

e.g. try:

drv="$(nix eval --raw --expr $(echo -ne '(builtins.derivation { name = "a"; system = "x86_64-linux"; builder = "/bin/sh"; args = [ "-c" "echo -n \xfe > $out" ]; }).drvPath'))"
# succeeds
cat $drv
# succeeds
xxd $(nix build --print-out-paths $drv^out)
# fails
nix derivation show $drv

This would not be an issue with e.g. CBOR, which has native support for binary strings.

@domenkozar

domenkozar commented Sep 5, 2026

Copy link
Copy Markdown
Member

I just noticed that JSON derivations are less flexible than ATerm, since ATerm derivations are capable of representing bytes that are not valid Unicode, but JSON is not (the specification describes it as a sequence of Unicode codepoints, not a sequence of bytes. It can then be encoded in any Unicode encoding, though Nix always uses UTF-8 iirc.)

e.g. try:

drv="$(nix eval --raw --expr $(echo -ne '(builtins.derivation { name = "a"; system = "x86_64-linux"; builder = "/bin/sh"; args = [ "-c" "echo -n \xfe > $out" ]; }).drvPath'))"
# succeeds
cat $drv
# succeeds
xxd $(nix build --print-out-paths $drv^out)
# fails
nix derivation show $drv

This would not be an issue with e.g. CBOR, which has native support for binary strings.

Thanks @artemist

To me that sounds like Varlink has hit a blocker, we should also strive for maximum performance.

I'll start on CBOR representation of Derivation and then look into using CDDL

@domenkozar

Copy link
Copy Markdown
Member

Playing with #16429 I'm thinking we need separate protocol for generating derivations and content.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation store Issues and pull requests concerning the Nix store with-tests Issues related to testing. PRs with tests have some priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants