> For the complete documentation index, see [llms.txt](https://docs.enclaive.cloud/sylica/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.enclaive.cloud/sylica/tutorials/reproducing-builds.md).

# Reproducing Builds

This tutorial guides on building the CVMF in a reproducible way, paving the ground for making attestation measurements deterministic and publicly verifable.

## Building reproducible Firmware

Sylica firmware builds are designed to be reproducible. Given the same Sylica revision and pinned build environment, an independent build should produce a firmware image that is byte-for-byte identical to the published artifact.

Reproducibility allows operators and security teams to verify that a published `CVMF.fd` was built from the corresponding Sylica source and declared build environment.

For confidential VM platforms, the reproduced firmware can additionally be used to derive the expected platform-specific launch measurement used during remote attestation.

### What reproducibility verifies

Reproducibility establishes a relationship between:

```
Sylica source
      +
pinned edk2 source
      +
pinned build environment
      ↓
   CVMF.fd
      ↓
firmware digest
```

If the digest of an independently rebuilt `CVMF.fd` matches the digest published with a Sylica release, the two firmware images are byte-for-byte identical.

For confidential VMs, the firmware also contributes to the hardware-backed launch measurement:

```
Reproduced CVMF.fd
        ↓
expected launch measurement
        ↓
remote attestation
        ↓
policy decision
```

The firmware digest and launch measurement serve different purposes:

* **Firmware digest** verifies that the firmware artifact was reproduced exactly.
* **Launch measurement** identifies the firmware as measured by the confidential-computing platform during VM launch.

### Reproducible build inputs

Sylica removes or fixes inputs that would otherwise make builds vary between machines or over time.

| Input             | How it is fixed                               | Purpose                                     |
| ----------------- | --------------------------------------------- | ------------------------------------------- |
| Sylica source     | Git commit or release tag                     | Identifies the source being reproduced      |
| edk2 source       | Pinned Git submodule                          | Prevents upstream source changes            |
| edk2 dependencies | Pinned Git submodules                         | Prevents dependency drift                   |
| Base image        | Container image pinned by digest              | Fixes the base build environment            |
| Toolchain         | Ubuntu snapshot and recorded package versions | Prevents compiler and build-tool drift      |
| Build timestamps  | `SOURCE_DATE_EPOCH`                           | Removes wall-clock time from build outputs  |
| Python hashing    | `PYTHONHASHSEED=1`                            | Removes Python hash randomisation           |
| Stack cookies     | Repository-controlled stack cookie files      | Removes random build-time cookie generation |
| Build network     | Disabled during compilation                   | Prevents undeclared network inputs          |

The exact toolchain package versions used by an isolated build are recorded in `tools-manifest.txt`.

### Reproduce a release

Release reproduction must use the isolated container build. A local build performed with `scripts/build.sh` uses the host toolchain and is intended for development only.

#### 1. Check out the release

Clone Sylica and check out the release that you want to reproduce:

```bash
git clone https://github.com/enclaive/sylica.git
cd sylica

git checkout <release-tag>
```

For example:

```bash
git checkout v1.0.0
```

#### 2. Initialise the source dependencies

Initialise the Sylica and edk2 submodules:

```bash
git submodule update --init --depth 1
git -C edk2 submodule update --init --depth 1
```

The submodule revisions are part of the source revision being reproduced and must not be updated to newer upstream versions.

#### 3. Build the firmware

Choose the platform to reproduce.

For AMD SEV-SNP:

```bash
docker build \
    -f reproduce/Dockerfile \
    --target artifact \
    --build-arg PLATFORM=sylica-sev \
    -o out \
    .
```

For Intel TDX:

```bash
docker build \
    -f reproduce/Dockerfile \
    --target artifact \
    --build-arg PLATFORM=sylica-tdx \
    -o out \
    .
```

The resulting firmware is:

```
out/<platform>/CVMF.fd
```

The output directory also contains the firmware checksums and the toolchain manifest generated during the build.

#### 4. Verify the firmware digest

For AMD SEV-SNP:

```bash
sha256sum out/sylica-sev/CVMF.fd
```

For Intel TDX:

```bash
sha256sum out/sylica-tdx/CVMF.fd
```

Compare the resulting SHA-256 digest with the checksum published with the corresponding Sylica release.

The values must be identical.

A matching digest means that the independently rebuilt firmware is byte-for-byte identical to the published `CVMF.fd`.

The BLAKE2 digest can additionally be checked using:

```bash
b2sum out/<platform>/CVMF.fd
```

### Platform-specific measurements

Firmware reproducibility is independent of the confidential-computing technology: `sylica-sev` and `sylica-tdx` use the same reproducible build infrastructure.

The measurement derived from the firmware is platform specific.

| Platform    | Build        | Firmware  | Launch measurement         |
| ----------- | ------------ | --------- | -------------------------- |
| AMD SEV-SNP | `sylica-sev` | `CVMF.fd` | SEV-SNP launch measurement |
| Intel TDX   | `sylica-tdx` | `CVMF.fd` | MRTD                       |

#### AMD SEV-SNP

For SEV-SNP, the reproduced `CVMF.fd` can be used to calculate the expected SNP firmware launch measurement.

The expected measurement can then be compared with the measurement published with the release and ultimately with evidence returned through SEV-SNP remote attestation.

#### Intel TDX

For Intel TDX, the corresponding firmware identity is represented by the TDX measurement registers, in particular MRTD.

Firmware binary reproducibility for `sylica-tdx` is already verified through the same byte-for-byte comparison used for other Sylica platforms.

{% hint style="info" %}
See [Verification](/sylica/tutorials/verifying-reproducible-builds.md) for the measurement and attestation verification procedure for AMD SEV-SNP and Intel TDX
{% endhint %}

### Automated reproducibility test

Developers and CI can test whether a platform builds deterministically using:

```bash
scripts/verify-reproducibility.sh <platform>
```

For example:

```bash
scripts/verify-reproducibility.sh sylica-sev
```

or:

```bash
scripts/verify-reproducibility.sh sylica-tdx
```

The script performs two isolated container builds and calculates the SHA-256 digest of each resulting firmware image.

Conceptually:

```
same source revision
      │
      ├──────────────┐
      ↓              ↓
   build 1        build 2
      ↓              ↓
   CVMF.fd        CVMF.fd
      ↓              ↓
   SHA-256         SHA-256
      │              │
      └──── compare ─┘
             ↓
       REPRODUCIBLE
```

The build stage is run without using its Docker layer cache. The test fails if the resulting firmware digests differ.

This test answers a different question from release reproduction:

* **Release reproduction:** does my independently built firmware match the published release?
* **Reproducibility test:** do two isolated builds of the same source produce identical firmware?

CI performs the reproducibility test for the supported build platforms.

### Local development builds

For development, Sylica provides:

```bash
scripts/build.sh <platform>
```

For example:

```bash
scripts/build.sh sylica-sev
```

The script:

1. verifies that the required host build tools are installed;
2. initialises the Sylica Git submodules;
3. initialises the edk2 Git submodules; and
4. invokes `reproduce/build.sh` directly on the host.

This avoids the overhead of rebuilding the container and allows edk2 build output to be reused during development.

However, the compiler and other build tools come from the host operating system rather than the pinned Sylica build environment.

Therefore:

> **A successful local build is not evidence that a Sylica release has been reproduced.**

Use the isolated Docker build when reproducing or verifying release firmware.

### Deterministic build environment

The inner build is implemented by `reproduce/build.sh`.

Before invoking the edk2 build system, it establishes deterministic build settings including:

```
SOURCE_DATE_EPOCH
PYTHONHASHSEED=1
PYTHON_COMMAND=python3
WORKSPACE
CONF_PATH
EDK_TOOLS_PATH
```

It also creates the edk2 build configuration from the checked-in BaseTools templates and builds BaseTools from the pinned edk2 source.

The firmware is then built using the platform definition from:

```
platforms/<platform>.json
```

This keeps the reproducibility mechanism common across SEV-SNP, TDX, upstream reference builds, and future platforms.

### Stack cookies

Sylica builds use stack protection. EDK2 BaseTools normally generates random per-module stack cookie values when the corresponding cookie files do not already exist in a fresh build directory.

Random build-time values would cause otherwise identical builds to produce different firmware binaries.

Sylica therefore provides deterministic build-time values in:

```
reproduce/StackCookieValues32.json
reproduce/StackCookieValues64.json
```

`reproduce/build.sh` copies these files into the build directory before compilation.

This does not make the runtime stack cookie deterministic. DXE and UEFI drivers replace the build-time value with a random value during boot using `DynamicStackCookieEntryPointLib` and the available hardware random-number generator.

PEI retains the build-time value, consistent with the underlying edk2 behaviour.

Because released firmware binaries are public, their embedded build-time values are observable regardless of whether those values were generated randomly during the build or fixed to make the build reproducible.

### Troubleshooting

If a reproduced firmware digest does not match the published release, verify the following before investigating the firmware source itself:

1. The correct Sylica release tag or commit is checked out.
2. Git submodules match the revisions recorded by that commit.
3. edk2 submodules are fully initialised.
4. The build uses `reproduce/Dockerfile`, not `scripts/build.sh`.
5. The correct platform was selected.
6. No local source modifications are present.
7. The expected release checksum corresponds to the selected platform.

You can inspect local modifications with:

```bash
git status
git submodule status
git -C edk2 submodule status --recursive
```

For deterministic-build debugging, run:

```bash
scripts/verify-reproducibility.sh <platform>
```

If both isolated builds produce the same digest but that digest differs from the release, investigate differences between the checked-out source/build inputs and the release.

If the two isolated builds produce different digests, investigate a source of build-time nondeterminism.

The `debug-sev` and `debug-tdx` platform builds can be used as upstream edk2 reference builds when isolating whether a reproducibility problem originates in Sylica-specific changes or in the underlying build environment.

### What reproducibility does not prove

A reproducible build demonstrates that the published firmware binary can be recreated from the published source using the declared build environment.

It does not by itself prove that:

* the source code is free from vulnerabilities;
* the compiler or build tools are free from defects or malicious behaviour;
* the source repository itself is trustworthy; or
* a particular VM is currently executing the reproduced firmware.

The final property is established through hardware-backed remote attestation.

Together, source inspection, reproducible builds, published measurements, and remote attestation provide a chain from auditable source code to the firmware identity of a running confidential VM.

See [Verification](/sylica/tutorials/verifying-reproducible-builds.md) for how to verify Sylica firmware artifacts and measurements.
