about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--tvix/Cargo.lock129
-rw-r--r--tvix/Cargo.nix318
-rw-r--r--tvix/cli/Cargo.toml6
-rw-r--r--tvix/cli/src/derivation.rs126
-rw-r--r--tvix/cli/src/errors.rs32
5 files changed, 573 insertions, 38 deletions
diff --git a/tvix/Cargo.lock b/tvix/Cargo.lock
index c0b82eff86..1eb25afbe8 100644
--- a/tvix/Cargo.lock
+++ b/tvix/Cargo.lock
@@ -173,6 +173,15 @@ dependencies = [
 
 [[package]]
 name = "base64"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
+name = "base64"
 version = "0.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
@@ -215,17 +224,38 @@ dependencies = [
  "cc",
  "cfg-if",
  "constant_time_eq",
- "digest",
+ "digest 0.10.6",
  "rayon",
 ]
 
 [[package]]
 name = "block-buffer"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
+dependencies = [
+ "block-padding",
+ "byte-tools",
+ "byteorder",
+ "generic-array 0.12.4",
+]
+
+[[package]]
+name = "block-buffer"
 version = "0.10.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
 dependencies = [
- "generic-array",
+ "generic-array 0.14.6",
+]
+
+[[package]]
+name = "block-padding"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
+dependencies = [
+ "byte-tools",
 ]
 
 [[package]]
@@ -235,6 +265,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
 
 [[package]]
+name = "byte-tools"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
+
+[[package]]
 name = "byteorder"
 version = "1.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -495,7 +531,7 @@ version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
 dependencies = [
- "generic-array",
+ "generic-array 0.14.6",
  "typenum",
 ]
 
@@ -523,11 +559,20 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
 
 [[package]]
 name = "digest"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
+dependencies = [
+ "generic-array 0.12.4",
+]
+
+[[package]]
+name = "digest"
 version = "0.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
 dependencies = [
- "block-buffer",
+ "block-buffer 0.10.3",
  "crypto-common",
  "subtle",
 ]
@@ -617,6 +662,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "fake-simd"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
+
+[[package]]
 name = "fastcdc"
 version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -770,6 +821,15 @@ dependencies = [
 
 [[package]]
 name = "generic-array"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
+dependencies = [
+ "typenum",
+]
+
+[[package]]
+name = "generic-array"
 version = "0.14.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
@@ -857,6 +917,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "hex"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
+
+[[package]]
 name = "http"
 version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1295,6 +1361,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
 
 [[package]]
+name = "opaque-debug"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
+
+[[package]]
 name = "os_str_bytes"
 version = "6.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1912,6 +1984,30 @@ dependencies = [
 ]
 
 [[package]]
+name = "sha-1"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
+dependencies = [
+ "block-buffer 0.7.3",
+ "digest 0.8.1",
+ "fake-simd",
+ "opaque-debug",
+]
+
+[[package]]
+name = "sha2"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69"
+dependencies = [
+ "block-buffer 0.7.3",
+ "digest 0.8.1",
+ "fake-simd",
+ "opaque-debug",
+]
+
+[[package]]
 name = "sha2"
 version = "0.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1919,7 +2015,7 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
 dependencies = [
  "cfg-if",
  "cpufeatures",
- "digest",
+ "digest 0.10.6",
 ]
 
 [[package]]
@@ -1983,6 +2079,21 @@ dependencies = [
 ]
 
 [[package]]
+name = "ssri"
+version = "7.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9cec0d388f39fbe79d7aa600e8d38053bf97b1bc8d350da7c0ba800d0f423f2"
+dependencies = [
+ "base64 0.10.1",
+ "digest 0.8.1",
+ "hex",
+ "serde",
+ "sha-1",
+ "sha2 0.8.2",
+ "thiserror",
+]
+
+[[package]]
 name = "static_assertions"
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2302,7 +2413,7 @@ dependencies = [
  "async-stream",
  "async-trait",
  "axum",
- "base64",
+ "base64 0.13.1",
  "bytes",
  "futures-core",
  "futures-util",
@@ -2498,11 +2609,15 @@ version = "0.1.0"
 dependencies = [
  "aho-corasick",
  "clap 4.0.32",
+ "data-encoding",
  "dirs",
  "rustyline",
  "smol_str",
+ "ssri",
+ "test-case",
  "tvix-derivation",
  "tvix-eval",
+ "tvix-store-bin",
 ]
 
 [[package]]
@@ -2514,7 +2629,7 @@ dependencies = [
  "glob",
  "serde",
  "serde_json",
- "sha2",
+ "sha2 0.10.6",
  "test-case",
  "test-generator",
  "thiserror",
diff --git a/tvix/Cargo.nix b/tvix/Cargo.nix
index 27cc40d2bb..3160d02132 100644
--- a/tvix/Cargo.nix
+++ b/tvix/Cargo.nix
@@ -635,7 +635,24 @@ rec {
         ];
 
       };
-      "base64" = rec {
+      "base64 0.10.1" = rec {
+        crateName = "base64";
+        version = "0.10.1";
+        edition = "2015";
+        sha256 = "13k6bvd3n6dm7jqn9x918w65dd9xhx454bqphbnv0bkd6n9dj98b";
+        authors = [
+          "Alice Maz <alice@alicemaz.com>"
+          "Marshall Pierce <marshall@mpierce.org>"
+        ];
+        dependencies = [
+          {
+            name = "byteorder";
+            packageId = "byteorder";
+          }
+        ];
+
+      };
+      "base64 0.13.1" = rec {
         crateName = "base64";
         version = "0.13.1";
         edition = "2018";
@@ -743,7 +760,7 @@ rec {
           }
           {
             name = "digest";
-            packageId = "digest";
+            packageId = "digest 0.10.6";
             optional = true;
             features = [ "mac" ];
           }
@@ -768,7 +785,7 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" "digest" "rayon" "std" ];
       };
-      "block-buffer" = rec {
+      "block-buffer 0.10.3" = rec {
         crateName = "block-buffer";
         version = "0.10.3";
         edition = "2018";
@@ -779,7 +796,52 @@ rec {
         dependencies = [
           {
             name = "generic-array";
-            packageId = "generic-array";
+            packageId = "generic-array 0.14.6";
+          }
+        ];
+
+      };
+      "block-buffer 0.7.3" = rec {
+        crateName = "block-buffer";
+        version = "0.7.3";
+        edition = "2015";
+        sha256 = "12v8wizynqin0hqf140kmp9s38q223mp1b0hkqk8j5pk8720v560";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "block-padding";
+            packageId = "block-padding";
+          }
+          {
+            name = "byte-tools";
+            packageId = "byte-tools";
+          }
+          {
+            name = "byteorder";
+            packageId = "byteorder";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "generic-array";
+            packageId = "generic-array 0.12.4";
+          }
+        ];
+
+      };
+      "block-padding" = rec {
+        crateName = "block-padding";
+        version = "0.1.5";
+        edition = "2015";
+        sha256 = "1xbkmysiz23vimd17rnsjpw9bgjxipwfslwyygqlkx4in3dxwygs";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "byte-tools";
+            packageId = "byte-tools";
           }
         ];
 
@@ -795,6 +857,16 @@ rec {
         features = { };
         resolvedDefaultFeatures = [ "default" ];
       };
+      "byte-tools" = rec {
+        crateName = "byte-tools";
+        version = "0.3.1";
+        edition = "2015";
+        sha256 = "1mqi29wsm8njpl51pfwr31wmpzs5ahlcb40wsjyd92l90ixcmdg3";
+        authors = [
+          "RustCrypto Developers"
+        ];
+
+      };
       "byteorder" = rec {
         crateName = "byteorder";
         version = "1.4.3";
@@ -1503,7 +1575,7 @@ rec {
         dependencies = [
           {
             name = "generic-array";
-            packageId = "generic-array";
+            packageId = "generic-array 0.14.6";
             features = [ "more_lengths" ];
           }
           {
@@ -1564,7 +1636,7 @@ rec {
         ];
 
       };
-      "digest" = rec {
+      "digest 0.10.6" = rec {
         crateName = "digest";
         version = "0.10.6";
         edition = "2018";
@@ -1575,7 +1647,7 @@ rec {
         dependencies = [
           {
             name = "block-buffer";
-            packageId = "block-buffer";
+            packageId = "block-buffer 0.10.3";
             optional = true;
           }
           {
@@ -1604,6 +1676,26 @@ rec {
         };
         resolvedDefaultFeatures = [ "alloc" "block-buffer" "core-api" "default" "mac" "std" "subtle" ];
       };
+      "digest 0.8.1" = rec {
+        crateName = "digest";
+        version = "0.8.1";
+        edition = "2015";
+        sha256 = "1madjl27f3kj5ql7kwgvb9c8b7yb7bv7yfgx7rqzj4i3fp4cil7k";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "generic-array";
+            packageId = "generic-array 0.12.4";
+          }
+        ];
+        features = {
+          "blobby" = [ "dep:blobby" ];
+          "dev" = [ "blobby" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
       "dirs" = rec {
         crateName = "dirs";
         version = "4.0.0";
@@ -1806,6 +1898,16 @@ rec {
         ];
         features = { };
       };
+      "fake-simd" = rec {
+        crateName = "fake-simd";
+        version = "0.1.2";
+        edition = "2015";
+        sha256 = "1vfylvk4va2ivqx85603lyqqp0zk52cgbs4n5nfbbbqx577qm2p8";
+        authors = [
+          "The Rust-Crypto Project Developers"
+        ];
+
+      };
       "fastcdc" = rec {
         crateName = "fastcdc";
         version = "2.0.0";
@@ -2214,7 +2316,27 @@ rec {
         ];
 
       };
-      "generic-array" = rec {
+      "generic-array 0.12.4" = rec {
+        crateName = "generic-array";
+        version = "0.12.4";
+        edition = "2015";
+        sha256 = "1gfpay78vijl9vrwl1k9v7fbvbhkhcmnrk4kfg9l6x24y4s9zpzz";
+        libName = "generic_array";
+        authors = [
+          "Bartłomiej Kamiński <fizyk20@gmail.com>"
+          "Aaron Trent <novacrazy@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "typenum";
+            packageId = "typenum";
+          }
+        ];
+        features = {
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "generic-array 0.14.6" = rec {
         crateName = "generic-array";
         version = "0.14.6";
         edition = "2015";
@@ -2478,6 +2600,16 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" ];
       };
+      "hex" = rec {
+        crateName = "hex";
+        version = "0.3.2";
+        edition = "2015";
+        sha256 = "0xsdcjiik5j750j67zk42qdnmm4ahirk3gmkmcqgq7qls2jjcl40";
+        authors = [
+          "KokaKiwi <kokakiwi@kokakiwi.net>"
+        ];
+        features = { };
+      };
       "http" = rec {
         crateName = "http";
         version = "0.2.8";
@@ -3749,6 +3881,16 @@ rec {
         ];
 
       };
+      "opaque-debug" = rec {
+        crateName = "opaque-debug";
+        version = "0.2.3";
+        edition = "2015";
+        sha256 = "172j6bs8ndclqxa2m64qc0y1772rr73g4l9fg2svscgicnbfff98";
+        authors = [
+          "RustCrypto Developers"
+        ];
+
+      };
       "os_str_bytes" = rec {
         crateName = "os_str_bytes";
         version = "6.4.1";
@@ -5514,7 +5656,51 @@ rec {
         };
         resolvedDefaultFeatures = [ "serde" ];
       };
-      "sha2" = rec {
+      "sha-1" = rec {
+        crateName = "sha-1";
+        version = "0.8.2";
+        edition = "2015";
+        sha256 = "1pv387q0r7llk2cqzyq0nivzvkgqgzsiygqzlv7b68z9xl5lvngp";
+        libName = "sha1";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "block-buffer";
+            packageId = "block-buffer 0.7.3";
+          }
+          {
+            name = "digest";
+            packageId = "digest 0.8.1";
+          }
+          {
+            name = "fake-simd";
+            packageId = "fake-simd";
+          }
+          {
+            name = "opaque-debug";
+            packageId = "opaque-debug";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "digest";
+            packageId = "digest 0.8.1";
+            features = [ "dev" ];
+          }
+        ];
+        features = {
+          "asm" = [ "sha1-asm" ];
+          "asm-aarch64" = [ "asm" "libc" ];
+          "default" = [ "std" ];
+          "libc" = [ "dep:libc" ];
+          "sha1-asm" = [ "dep:sha1-asm" ];
+          "std" = [ "digest/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "sha2 0.10.6" = rec {
         crateName = "sha2";
         version = "0.10.6";
         edition = "2018";
@@ -5534,13 +5720,13 @@ rec {
           }
           {
             name = "digest";
-            packageId = "digest";
+            packageId = "digest 0.10.6";
           }
         ];
         devDependencies = [
           {
             name = "digest";
-            packageId = "digest";
+            packageId = "digest 0.10.6";
             features = [ "dev" ];
           }
         ];
@@ -5554,6 +5740,49 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" "std" ];
       };
+      "sha2 0.8.2" = rec {
+        crateName = "sha2";
+        version = "0.8.2";
+        edition = "2015";
+        sha256 = "0s9yddvyg6anaikdl86wmwfim25c0d4m0xq0y2ghs34alxpg8mm2";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "block-buffer";
+            packageId = "block-buffer 0.7.3";
+          }
+          {
+            name = "digest";
+            packageId = "digest 0.8.1";
+          }
+          {
+            name = "fake-simd";
+            packageId = "fake-simd";
+          }
+          {
+            name = "opaque-debug";
+            packageId = "opaque-debug";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "digest";
+            packageId = "digest 0.8.1";
+            features = [ "dev" ];
+          }
+        ];
+        features = {
+          "asm" = [ "sha2-asm" ];
+          "asm-aarch64" = [ "asm" "libc" ];
+          "default" = [ "std" ];
+          "libc" = [ "dep:libc" ];
+          "sha2-asm" = [ "dep:sha2-asm" ];
+          "std" = [ "digest/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
       "sharded-slab" = rec {
         crateName = "sharded-slab";
         version = "0.1.4";
@@ -5730,6 +5959,51 @@ rec {
         features = { };
         resolvedDefaultFeatures = [ "all" ];
       };
+      "ssri" = rec {
+        crateName = "ssri";
+        version = "7.0.0";
+        edition = "2018";
+        sha256 = "1wi3yk801a0bgkd51ly83dxzjfq5726hwq5asxwvx7zki39w1km9";
+        authors = [
+          "Kat Marchán <kzm@zkat.tech>"
+        ];
+        dependencies = [
+          {
+            name = "base64";
+            packageId = "base64 0.10.1";
+          }
+          {
+            name = "digest";
+            packageId = "digest 0.8.1";
+          }
+          {
+            name = "hex";
+            packageId = "hex";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+          }
+          {
+            name = "sha-1";
+            packageId = "sha-1";
+          }
+          {
+            name = "sha2";
+            packageId = "sha2 0.8.2";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+        ];
+        features = {
+          "default" = [ "serde" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "serde" ];
+      };
       "static_assertions" = rec {
         crateName = "static_assertions";
         version = "1.1.0";
@@ -6635,7 +6909,7 @@ rec {
           }
           {
             name = "base64";
-            packageId = "base64";
+            packageId = "base64 0.13.1";
           }
           {
             name = "bytes";
@@ -7440,6 +7714,10 @@ rec {
             features = [ "derive" "env" ];
           }
           {
+            name = "data-encoding";
+            packageId = "data-encoding";
+          }
+          {
             name = "dirs";
             packageId = "dirs";
           }
@@ -7452,6 +7730,10 @@ rec {
             packageId = "smol_str";
           }
           {
+            name = "ssri";
+            packageId = "ssri";
+          }
+          {
             name = "tvix-derivation";
             packageId = "tvix-derivation";
           }
@@ -7459,6 +7741,16 @@ rec {
             name = "tvix-eval";
             packageId = "tvix-eval";
           }
+          {
+            name = "tvix-store-bin";
+            packageId = "tvix-store-bin";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "test-case";
+            packageId = "test-case";
+          }
         ];
 
       };
@@ -7492,7 +7784,7 @@ rec {
           }
           {
             name = "sha2";
-            packageId = "sha2";
+            packageId = "sha2 0.10.6";
           }
           {
             name = "thiserror";
diff --git a/tvix/cli/Cargo.toml b/tvix/cli/Cargo.toml
index 45ed05e089..83796855b6 100644
--- a/tvix/cli/Cargo.toml
+++ b/tvix/cli/Cargo.toml
@@ -12,6 +12,12 @@ tvix-eval = { path = "../eval" }
 tvix-derivation = { path = "../derivation" }
 rustyline = "10.0.0"
 clap = { version = "4.0", features = ["derive", "env"] }
+tvix-store-bin = { path = "../store" }
 dirs = "4.0.0"
 smol_str = "0.1"
 aho-corasick = "0.7"
+ssri = "7.0.0"
+data-encoding = "2.3.3"
+
+[dev-dependencies]
+test-case = "2.2.2"
diff --git a/tvix/cli/src/derivation.rs b/tvix/cli/src/derivation.rs
index d57503a696..122330b963 100644
--- a/tvix/cli/src/derivation.rs
+++ b/tvix/cli/src/derivation.rs
@@ -1,5 +1,6 @@
 //! Implements `builtins.derivation`, the core of what makes Nix build packages.
 
+use data_encoding::BASE64;
 use std::cell::RefCell;
 use std::collections::{btree_map, BTreeSet};
 use std::rc::Rc;
@@ -79,6 +80,82 @@ fn populate_inputs<I: IntoIterator<Item = String>>(
     }
 }
 
+/// Due to the support for SRI hashes, and how these are passed along to
+/// builtins.derivation, outputHash and outputHashAlgo can have values which
+/// need to be further modified before constructing the Derivation struct.
+///
+/// If outputHashAlgo is an SRI hash, outputHashAlgo must either be an empty
+/// string, or the hash algorithm as specified in the (single) SRI (entry).
+/// SRI strings with multiple hash algorithms are not supported.
+///
+/// In case an SRI string was used, the (single) fixed output is populated
+/// with the hash algo name, and the hash digest is populated with the
+/// (lowercase) hex encoding of the digest.
+///
+/// These values are only rewritten for the outputs, not what's passed to env.
+fn construct_output_hash(digest: &str, algo: &str, hash_mode: Option<&str>) -> Result<Hash, Error> {
+    let sri_parsed = digest.parse::<ssri::Integrity>();
+    // SRI strings can embed multiple hashes with different algos, but that's probably not supported
+
+    let (digest, algo): (String, String) = match sri_parsed {
+        Err(e) => {
+            // unable to parse as SRI, but algo not set
+            if algo.is_empty() {
+                // InvalidSRIString doesn't implement PartialEq, but our error does
+                return Err(Error::InvalidSRIString(e.to_string()));
+            }
+
+            // algo is set. Assume the digest is set to some nixbase32.
+            // TODO: more validation here
+
+            (digest.to_string(), algo.to_string())
+        }
+        Ok(sri_parsed) => {
+            // We don't support more than one SRI hash
+            if sri_parsed.hashes.len() != 1 {
+                return Err(Error::UnsupportedSRIMultiple(sri_parsed.hashes.len()).into());
+            }
+
+            // grab the first (and only hash)
+            let sri_parsed_hash = &sri_parsed.hashes[0];
+
+            // ensure the algorithm in the SRI is supported
+            if !(sri_parsed_hash.algorithm == ssri::Algorithm::Sha1
+                || sri_parsed_hash.algorithm == ssri::Algorithm::Sha256
+                || sri_parsed_hash.algorithm == ssri::Algorithm::Sha512)
+            {
+                Error::UnsupportedSRIAlgo(sri_parsed_hash.algorithm.to_string());
+            }
+
+            // if algo is set, it needs to match what the SRI says
+            if !algo.is_empty() && algo != sri_parsed_hash.algorithm.to_string() {
+                return Err(Error::ConflictingSRIHashAlgo(
+                    algo.to_string(),
+                    sri_parsed_hash.algorithm.to_string(),
+                ));
+            }
+
+            // the digest comes base64-encoded. We need to decode, and re-encode as hexlower.
+            match BASE64.decode(sri_parsed_hash.digest.as_bytes()) {
+                Err(e) => return Err(Error::InvalidSRIDigest(e).into()),
+                Ok(sri_digest) => (
+                    data_encoding::HEXLOWER.encode(&sri_digest),
+                    sri_parsed_hash.algorithm.to_string(),
+                ),
+            }
+        }
+    };
+
+    // mutate the algo string a bit more, depending on hashMode
+    let algo = match hash_mode {
+        None | Some("flat") => algo,
+        Some("recursive") => format!("r:{}", algo),
+        Some(other) => return Err(Error::InvalidOutputHashMode(other.to_string()).into()),
+    };
+
+    Ok(Hash { algo, digest })
+}
+
 /// Populate the output configuration of a derivation based on the
 /// parameters passed to the call, flipping the required
 /// parameters for a fixed-output derivation if necessary.
@@ -102,6 +179,12 @@ fn populate_output_configuration(
                     .as_str()
                     .to_string();
 
+                let digest_str = hash
+                    .force(vm)?
+                    .coerce_to_string(CoercionKind::Strong, vm)?
+                    .as_str()
+                    .to_string();
+
                 let hash_mode = match hash_mode {
                     None => None,
                     Some(mode) => Some(
@@ -112,23 +195,12 @@ fn populate_output_configuration(
                     ),
                 };
 
-                let algo = match hash_mode.as_deref() {
-                    None | Some("flat") => algo,
-                    Some("recursive") => format!("r:{}", algo),
-                    Some(other) => {
-                        return Err(Error::InvalidOutputHashMode(other.to_string()).into())
-                    }
-                };
-
-                out.hash = Some(Hash {
-                    algo,
-
-                    digest: hash
-                        .force(vm)?
-                        .coerce_to_string(CoercionKind::Strong, vm)?
-                        .as_str()
-                        .to_string(),
-                });
+                // construct out.hash
+                out.hash = Some(construct_output_hash(
+                    &digest_str,
+                    &algo,
+                    hash_mode.as_deref(),
+                )?);
             }
         },
 
@@ -371,6 +443,7 @@ pub use derivation_builtins::builtins as derivation_builtins;
 #[cfg(test)]
 mod tests {
     use super::*;
+    use test_case::test_case;
     use tvix_eval::observer::NoOpObserver;
 
     static mut OBSERVER: NoOpObserver = NoOpObserver {};
@@ -576,4 +649,23 @@ mod tests {
             vec!["--foo".to_string(), "42".to_string(), "--bar".to_string()]
         );
     }
+
+    #[test_case(
+        "sha256-swapHA/ZO8QoDPwumMt6s5gf91oYe+oyk4EfRSyJqMg=", "sha256", Some("flat"),
+        Ok(Hash { algo: "sha256".to_string(), digest: "b306a91c0fd93bc4280cfc2e98cb7ab3981ff75a187bea3293811f452c89a8c8".to_string() });
+        "sha256 and SRI"
+    )]
+    #[test_case(
+        "sha256-s6JN6XqP28g1uYMxaVAQMLiXcDG8tUs7OsE3QPhGqzA=", "", Some("flat"),
+        Ok(Hash { algo: "sha256".to_string(), digest: "b3a24de97a8fdbc835b9833169501030b8977031bcb54b3b3ac13740f846ab30".to_string() });
+        "SRI only"
+    )]
+    fn test_construct_output_hash(
+        digest: &str,
+        algo: &str,
+        hash_mode: Option<&str>,
+        result: Result<Hash, Error>,
+    ) {
+        assert_eq!(construct_output_hash(digest, algo, hash_mode), result);
+    }
 }
diff --git a/tvix/cli/src/errors.rs b/tvix/cli/src/errors.rs
index cbf8ed9457..5791c5332b 100644
--- a/tvix/cli/src/errors.rs
+++ b/tvix/cli/src/errors.rs
@@ -1,7 +1,7 @@
 use std::{error, fmt::Display, rc::Rc};
 use tvix_derivation::DerivationError;
 
-#[derive(Debug)]
+#[derive(Debug, PartialEq)]
 pub enum Error {
     // Errors related to derivation construction
     DuplicateOutput(String),
@@ -10,6 +10,11 @@ pub enum Error {
     ShadowedOutput(String),
     InvalidDerivation(DerivationError),
     InvalidOutputHashMode(String),
+    UnsupportedSRIAlgo(String),
+    UnsupportedSRIMultiple(usize),
+    InvalidSRIDigest(data_encoding::DecodeError),
+    InvalidSRIString(String),
+    ConflictingSRIHashAlgo(String, String),
 }
 
 impl Display for Error {
@@ -38,6 +43,31 @@ impl Display for Error {
                 f,
                 "invalid output hash mode: '{mode}', only 'recursive' and 'flat` are supported"
             ),
+            Error::UnsupportedSRIAlgo(algo) => {
+                write!(
+                    f,
+                    "unsupported sri algorithm: {algo}, only sha1, sha256 or sha512 is supported"
+                )
+            }
+            Error::UnsupportedSRIMultiple(n) => {
+                write!(
+                    f,
+                    "invalid number of sri hashes in string ({n}), only one hash is supported"
+                )
+            }
+            Error::InvalidSRIDigest(err) => {
+                write!(f, "invalid sri digest: {}", err)
+            }
+            Error::InvalidSRIString(err) => {
+                write!(f, "failed to parse SRI string: {}", err)
+            }
+            Error::ConflictingSRIHashAlgo(algo, sri_algo) => {
+                write!(
+                    f,
+                    "outputHashAlgo is set to {}, but outputHash contains SRI with algo {}",
+                    algo, sri_algo
+                )
+            }
         }
     }
 }