raboof / sbt-reproducible-builds

Make your published artifacts bit-by-bit reproducible, and compare with other certifications

Version Matrix


sbt plugin to make sbt builds more reproducible.

For general information on 'Reproducible Builds', see https://reproducible-builds.org/

This plugin provides a number of features:

  • Strip 'accidental' sources of nondeterminism (e.g. timestamps) from the packaged jar
  • Produce a 'buildinfo' file describing the build environment used and containing a cryptographic hash of the resulting artifacts.
  • Upload this (signed) certification when publishing
  • Upload this (signed) certification to a reproducible-builds-certification-repository instance
  • Check your local build against already-uploaded certifications (WiP)


Stripping nondeterminism from artifacts

Then add to your project/plugins.sbt:

addSbtPlugin("net.bzzt" % "sbt-reproducible-builds" % "0.24")

And to build.sbt:


After sbt package, the stripped artifact can be found under target/scala-${scalaBinaryVersion}/stripped/${reproducibleBuildsPackageName}-${version}.jar. The generated buildinfo that will be published along with your release will look something like this:


Interoperability with other sbt plugins


As this plugin as well as the sbt-osgi plugin redefine the packageBin task, it is necessary to re-apply the plugin settings after the application of the osgi settings. You can find an example under src/sbt-test/sbt-reproducible-builds/osgi/build.sbt.

Describe your build as a 'buildinfo' certification

You can now generate a description of the build environment with the sbt task reproducibleBuildsCertification. This certification will also be included when publishing your project. If you want to disable this, you can set publishCertification := false.

To sign the certification, configure sbt-gpg and either simply publishLocal, or, for example if you have publishCertification := false, reproducible-builds:publishLocal.

Sharing certifications

If you are a (3rd-party or 'official') rebuilder, you can use the reproducible-builds:publish task to publish the buildinfo to a reproducible-builds-certification-repository instance.

Uploading certifications from Travis

Especially if you're already deploying from Travis, it can be a great start to publish certifications from Travis as well. For this, you should give Travis its own gpg key pair to sign those certifications.

An even better way might be to use the key generated by travis for this repository itself following https://docs.travis-ci.com/user/encryption-keys/

If you want to use your own key, however, start by generating a keypair with gpg --gen-key, naming it something like "Arnout Engelen (via Travis) arnout@bzzt.net". Then export public and private key with gpg --export -a "Arnout Engelen (via Travis)" > public.key and gpg --export-secret-key -a "Arnout Engelen (via Travis)" > private.key. Now encrypt the private key so only Travis can read it with travis encrypt-file .travis/private.key and follow the instructions to unencrypt. Finally, gpg --import private.key public.key and sbt reproducible-builds:publish to sign and upload the certification from Travis.

Checking certifications

You can check your certification against other uploaded certifications with reproducibleBuildsCheckCertification

Checking your certification with the 'official' published certification is currently not yet implemented.

Drinking our own champagne

From version 0.3 onwards, sbt-reproducible-builds should itself be reproducible in the sense that building the same git tag should produce the exact same binary.

When this is not the case, this is to be considered a bug and a bug report with the binary, the buildinfo and any additional information about any peculiarities of the build system would be greatly appreciated!


ivy.xml is not included

When using Ivy-style publishing (which is notably common for sbt plugins), the ivy.xml is not currently part of the published buildinfo. This is a problem because this file contains the transitive dependencies for the artifact, so a backdoor can be introduced by adding a rogue transitive dependency to this file. This issue is tracked under #84.

Further recommendations

Some further recommendations to make your builds more reproducible:

  • Specify scalaVersion (and if applicable crossScalaVersions) in your build configuration
  • Use sbt-strict-scala-versions to ensure always using those Scala versions (addSbtPlugin("net.bzzt" % "sbt-strict-scala-versions" % "0.0.1"))