Attestation is a powerful feature of TEEs that allow the end user to be confident of the exact software and OS running on an enclave before sending sensitive data to it.

When building a Nitro Enclave, a set of hashes called Platform Configuration Registers (PCRS) are returned that describe what will be running on the enclave. They are useful for version tracking but are more valuable when it's possible to reproduce them on different machines to prove trust to external parties.

In order for an EIF to be reproducible across different machines, the underlying Dockerfile must be reproducible. One of the biggest issues with build reproducibility is that time stamps are added in Docker layers and by package managers so rebuilding from the same source will produce different hashes even if they are built moments apart.

A lot of work has been done to overcome this, with more left to do but we highly recommend having a look at Akihiro Suda's blog post explaining how it's possible with Buildkit as it's the approach we took for Cages.


Buildx version v0.10.0(Buildkit version v0.11.0) is the minimum version needed for reproducible Cage builds. Earlier versions will still build Cages successfully but reproducible PCRs won’t be guaranteed.

You can check your Buildx version by running:

docker buildx version

If it’s lower than v0.10.0 upgrading your Docker desktop/engine version will pull in a compatible version of Buildx.


First you'll need an app with a Dockerfile. We have a hello world one to get you started.

Start by initializing the Cage with the CLI:

ev-cage init --name test-repro-builds --egress

Next, build the Cage:

ev-cage build --reproducible

The build step will take your Dockerfile and create a new one with the required Evervault related directives. These directives include installing the data plane, required packages such as Runit and also including Cage specific environments variables that make running your app on an enclave possible. The intermediate enclave.Dockerfile is then converted to an EIF so that it can be run on an enclave.

Cage deployment process

You should see a file named enclave.Dockerfile in your folder at this point and the cage.toml should have PCRs like this:

HashAlgorithm = "Sha384 { ... }"
PCR0 = "9fe8e1525219b65a265f0ccb234f66fececfc5c761fe50bcbc3b1ac0e48f84dc4b0434a9a29f27d8aef57399e78b4124"
PCR1 = "bcdf05fefccaa8e55bf2c8d6dee9e79bbff31e34bf28a99aa19e6b29c37ee80b214a414b7607236edf26fcb78654e63f"
PCR2 = "48ffa10dec685c5e43c2e37f7800d103d42b5d9cc2fe36fc7419f35950a089d0b251a3ee36a311c9718159f8e62f0cc9"
PCR8 = "eae227ef31b9249c03691dd215d83cdd4cdc687fa38a8b78448a01b73ea38d942b3114e17631b182afbf63a1d2346bf3"

These measurements will be reproducible by any third party on their machine. To test it out trying running the rebuild process in CI.

Reproduce Build

You can rebuild an enclave with the following command:

ev-cage build --config cage.toml --from-existing enclave.Dockerfile

If your Dockerfile is pulling in source code during build you'll need to run the command from the same commit as it was initally generated. We store the git hash associated with the deployment along with the PCRs so you can easily access them in the dashboard under Cage versions if you need to cross reference PCRs with commits.

The enclave.Dockerfile must be used for build reproducibility instead of the original Dockerfile. Different versions of the Cage CLI will inject different scripts/values that would affect the PCRs if the other party uses a different version to you.

Specify Timestamp

The default behaviour is to use Unix epoch 0 as the timestamp to build the Dockerfile with. If you wish to set it to a specific time such as the time of the commit, you can export SOURCE_DATE_EPOCH with the time in milliseconds and the CLI will use that during the build process.

export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)

Installing Packages

In order to ensure that your Cage is reproducible, all the packages that are installed in your Dockerfile must always use the same binary. Package managers don’t guarantee reproducibility so if you are using one you’ll need to work around this.

We created an installer where dependencies are compiled and packed into a binary that can be pinned by version in the enclave.Dockerfile, the source code is here. Additionally, repro-get can also be used to achieve the same thing depending on your Dockerfile’s distro.

What’s Next?

Now that you've built an enclave that can be build bit for bit by multiple parties, its time to deploy