A mention of these different builders is included in the a footnote in the documentation, and various issues for the different TODOs were created: - #128 Implement bwrap-based Builder - #129 Implement gVisor-based builder - #130 Implement Cloud Hypervisor-based builder - #131 OCI builder: add preflight checks - #132 BuildService: refactor to be more granular Change-Id: I349b799e233ba8bef39a139cf2453d3214bb69b3 Reviewed-on: https://cl.snix.dev/c/snix/+/30474 Autosubmit: Florian Klink <flokli@flokli.de> Tested-by: besadii Reviewed-by: Jonas Chevalier <zimbatm@zimbatm.com>
		
			
				
	
	
		
			104 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			104 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| ---
 | |
| title: "Protocol"
 | |
| slug: protocol
 | |
| description: ""
 | |
| summary: ""
 | |
| date: 2025-03-14T14:14:35+01:00
 | |
| lastmod: 2025-03-14T14:14:35+01:00
 | |
| draft: false
 | |
| weight: 41
 | |
| toc: true
 | |
| ---
 | |
| 
 | |
| ## Standardized Interface
 | |
| One goal is to make the build protocol a standardized interface, allowing to
 | |
| make the sandboxing mechanism used by the build process pluggable.
 | |
| 
 | |
| Nix is currently using a hard-coded [libseccomp][] based sandboxing mechanism
 | |
| and another one based on [sandboxd][] on macOS.
 | |
| These are only separated by [compiler preprocessor macros][ifdef] within the same
 | |
| source files despite having very little in common with each other.
 | |
| 
 | |
| In Snix, the Builders need to implement a trait, and there are multiple
 | |
| implementations. In addition to an [OCI][] builder and plans for
 | |
| more[^other-builders], we currently include a gRPC client (and server adapter),
 | |
| allowing to run the builder both locally or remotely, or plug in your entirely
 | |
| separate Builder, as long as it speaks the same gRPC protocol.
 | |
| 
 | |
| Check `build/protos/build.proto` for a detailed description of the protocol,
 | |
| individual fields, and the tests in `glue/src/tvix_build.rs` for some examples.
 | |
| 
 | |
| While we're somewhat confident about the `BuildRequest`, the RPC method itself
 | |
| will change to a stream of events, so we can stream logs/build telemetry to the
 | |
| requesting client.
 | |
| 
 | |
| ## Unaware of Nix sandbox internals
 | |
| 
 | |
| The environment in which builds currently happen is currently very Nix-specific.
 | |
| In Snix, we don't want to maintain all the intricacies of a Nix-specific
 | |
| sandboxing environment in every builder, and instead only provide a more
 | |
| generic interface, receiving more generic build requests (and translate
 | |
| Derivations into this format). [^reapi]
 | |
| 
 | |
| Another goal of the builder protocol is to not be too tied to the Nix
 | |
| implementation itself, allowing it to be used for other builds/workloads in the
 | |
| future (and experimenting with different hashing schemes etc without having to
 | |
| change builder code).
 | |
| 
 | |
| 
 | |
| 
 | |
| In concrete terms, this means the builder protocol is versatile enough to
 | |
| express the environment a Nix build sets up, while it itself is not aware of
 | |
| "what any of this means".
 | |
| 
 | |
| For example, it is not aware of how certain environment variables are set in a
 | |
| nix build, but provides the necessary infrastructure to specify environment
 | |
| variables that should be set.
 | |
| 
 | |
| It's also not aware of what nix store paths are. Instead, it allows:
 | |
| 
 | |
|  - specifying a list of paths expected to be produced during the build
 | |
|  - specifying a list of castore root nodes to be present in a specified
 | |
|    `inputs_dir`.
 | |
|  - specifying which paths are write-able during build.
 | |
| 
 | |
| In case all specified paths are produced, and the command specified in
 | |
| `command_args` succeeds, the build is considered to be successful.
 | |
| 
 | |
| This happens to be sufficient to *also* express how Nix builds works.
 | |
| 
 | |
| ## More hermetic builds, Build Provenance
 | |
| Nix uses derivations (encoded in ATerm) as nodes in its build graph, but it
 | |
| refers to other store paths used in that build by these store paths only. As
 | |
| mentioned before, store paths only address the inputs - and not the content.
 | |
| 
 | |
| This poses a big problem in Nix as soon as builds are scheduled on remote
 | |
| builders: There is no guarantee that files at the same store path on the remote
 | |
| builder actually have the same contents as on the machine orchestrating the
 | |
| build. If a package is not binary reproducible, this can lead to so-called
 | |
| [frankenbuilds].
 | |
| 
 | |
| This also introduces a dependency on the state that's present on the remote
 | |
| builder machine: Whatever is in its store and matches the paths will be used,
 | |
| even if it was maliciously placed there.
 | |
| 
 | |
| To eliminate this hermiticity problem and increase the integrity of builds,
 | |
| we've decided to use content-addressing in the builder protocol.
 | |
| 
 | |
| In the long run, recording this information is gonna improve our posture
 | |
| regarding [Build Provenance][slsa-provenance].
 | |
| 
 | |
| 
 | |
| [OCI]:             https://github.com/opencontainers/runtime-spec
 | |
| [libseccomp]:      https://github.com/seccomp/libseccomp
 | |
| [sandboxd]:        https://www.unix.com/man-page/mojave/8/sandboxd/
 | |
| [ifdef]:           https://gcc.gnu.org/onlinedocs/cpp/Ifdef.html
 | |
| [frankenbuilds]:   https://blog.layus.be/posts/2021-06-25-frankenbuilds.html
 | |
| [slsa-provenance]: https://slsa.dev/spec/v1.0/provenance
 | |
| 
 | |
| [^other-builders]: With a well-defined builder abstraction, it's also easy to imagine
 | |
|                    other backends such as a Kubernetes-based / bwrap / gVisor /
 | |
|                    Cloud Hypervisor in the future.
 | |
| [^reapi]: There have already been some discussions in the Nix community, to switch
 | |
|   to REAPI:
 | |
|   https://discourse.nixos.org/t/a-proposal-for-replacing-the-nix-worker-protocol/20926/22
 |