snix/web/content/docs/components/build/protocol.md
Florian Klink ea90045ddc docs(snix/docs/TODO): drop builder TODOs
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>
2025-05-05 11:56:26 +00:00

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