about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitreview5
-rw-r--r--.mailmap1
-rw-r--r--README.md7
-rw-r--r--corp/rih/backend/Cargo.lock623
-rw-r--r--corp/rih/frontend/Cargo.lock21
-rw-r--r--corp/rih/frontend/Cargo.toml2
-rw-r--r--corp/russian/predlozhnik/Cargo.lock21
-rw-r--r--corp/russian/predlozhnik/Cargo.toml2
-rw-r--r--docs/importing-projects.md91
-rw-r--r--nix/writers/default.nix1
-rw-r--r--ops/besadii/default.nix2
-rw-r--r--ops/besadii/main.go37
-rw-r--r--ops/buildkite/tvl.tf6
-rw-r--r--ops/journaldriver/Cargo.lock270
-rw-r--r--ops/keycloak/main.tf17
-rw-r--r--ops/keycloak/user_sources.tf9
-rw-r--r--ops/kontemplate/util/util.go18
-rw-r--r--ops/kontemplate/util/util_test.go6
-rw-r--r--ops/machines/all-systems.nix2
-rw-r--r--ops/machines/volgasprint-cache/default.nix153
-rw-r--r--ops/machines/whitby/default.nix8
-rw-r--r--ops/modules/cheddar.nix29
-rw-r--r--ops/modules/sourcegraph.nix60
-rw-r--r--ops/modules/www/cs.tvl.fyi.nix49
-rw-r--r--ops/modules/www/tazj.in.nix54
-rw-r--r--ops/nixos.nix1
-rw-r--r--ops/pipelines/static-pipeline.yaml10
-rw-r--r--ops/secrets/tf-keycloak.agebin962 -> 1135 bytes
-rw-r--r--ops/users/default.nix45
-rw-r--r--third_party/chicago95/default.nix47
-rw-r--r--third_party/gitignoreSource/default.nix12
-rw-r--r--third_party/nixpkgs/default.nix3
-rw-r--r--third_party/overlays/patches/cbtemulator-uds.patch140
-rw-r--r--third_party/overlays/tvl.nix7
-rw-r--r--third_party/radicle-explorer/0001-remove-dependency-on-plausible.patch78
-rw-r--r--third_party/radicle-explorer/default.nix66
-rw-r--r--third_party/rust-crates/default.nix126
-rw-r--r--third_party/sources/sources.json48
-rw-r--r--tools/cheddar/Cargo.lock598
-rw-r--r--tools/depot-deps.nix1
-rw-r--r--tools/depotfmt.nix15
-rw-r--r--tools/eaglemode/commands/B.nix23
-rw-r--r--tools/eaglemode/commands/emacsclient.nix26
-rw-r--r--tools/eaglemode/commands/plan9.tgabin0 -> 5926 bytes
-rw-r--r--tools/eaglemode/default.nix146
-rw-r--r--tools/eaglemode/plugins/avif/default.nix10
-rw-r--r--tools/eaglemode/plugins/avif/etc/emCore/FpPlugins/PlAvif.emFpPlugin6
-rw-r--r--tools/eaglemode/plugins/avif/makers/PlAvif.maker.pm64
-rw-r--r--tools/eaglemode/plugins/avif/src/PlAvif.cpp190
-rw-r--r--tools/eaglemode/plugins/example.nix17
-rw-r--r--tools/eaglemode/plugins/qoi/default.nix12
-rw-r--r--tools/eaglemode/plugins/qoi/etc/emCore/FpPlugins/PlQoi.emFpPlugin6
-rw-r--r--tools/eaglemode/plugins/qoi/makers/PlQoi.maker.pm47
-rw-r--r--tools/eaglemode/plugins/qoi/src/PlQoi.cpp273
-rw-r--r--tools/eaglemode/plugins/yatracker/default.nix18
-rw-r--r--tools/eaglemode/plugins/yatracker/etc/emCore/FpPlugins/PlYaTracker.emFpPlugin6
-rw-r--r--tools/eaglemode/plugins/yatracker/logo.webpbin0 -> 13808 bytes
-rw-r--r--tools/eaglemode/plugins/yatracker/makers/PlYaTracker.maker.pm47
-rw-r--r--tools/eaglemode/plugins/yatracker/src/PlYaTracker/PlYaTracker.cpp58
-rw-r--r--tools/eaglemode/wrapper.go156
-rw-r--r--tvix/Cargo.lock1317
-rw-r--r--tvix/Cargo.nix3396
-rw-r--r--tvix/Cargo.toml124
-rw-r--r--tvix/boot/default.nix13
-rw-r--r--tvix/boot/tests/default.nix17
-rw-r--r--tvix/build-go/build.pb.go4
-rw-r--r--tvix/build/Cargo.toml33
-rw-r--r--tvix/build/build.rs2
-rw-r--r--tvix/build/default.nix4
-rw-r--r--tvix/build/protos/build.proto4
-rw-r--r--tvix/build/protos/default.nix21
-rw-r--r--tvix/build/src/bin/tvix-build.rs17
-rw-r--r--tvix/build/src/proto/mod.rs29
-rw-r--r--tvix/castore/Cargo.toml147
-rw-r--r--tvix/castore/build.rs2
-rw-r--r--tvix/castore/protos/default.nix18
-rw-r--r--tvix/castore/src/digests.rs2
-rw-r--r--tvix/castore/src/directoryservice/bigtable.rs24
-rw-r--r--tvix/castore/src/directoryservice/combinators.rs9
-rw-r--r--tvix/castore/src/directoryservice/directory_graph.rs127
-rw-r--r--tvix/castore/src/directoryservice/grpc.rs41
-rw-r--r--tvix/castore/src/directoryservice/memory.rs34
-rw-r--r--tvix/castore/src/directoryservice/mod.rs30
-rw-r--r--tvix/castore/src/directoryservice/object_store.rs25
-rw-r--r--tvix/castore/src/directoryservice/order_validator.rs37
-rw-r--r--tvix/castore/src/directoryservice/redb.rs43
-rw-r--r--tvix/castore/src/directoryservice/simple_putter.rs5
-rw-r--r--tvix/castore/src/directoryservice/sled.rs46
-rw-r--r--tvix/castore/src/directoryservice/tests/mod.rs62
-rw-r--r--tvix/castore/src/directoryservice/traverse.rs56
-rw-r--r--tvix/castore/src/directoryservice/utils.rs34
-rw-r--r--tvix/castore/src/errors.rs48
-rw-r--r--tvix/castore/src/fixtures.rs127
-rw-r--r--tvix/castore/src/fs/fuse/tests.rs111
-rw-r--r--tvix/castore/src/fs/inodes.rs33
-rw-r--r--tvix/castore/src/fs/mod.rs85
-rw-r--r--tvix/castore/src/fs/root_nodes.rs22
-rw-r--r--tvix/castore/src/import/archive.rs2
-rw-r--r--tvix/castore/src/import/fs.rs3
-rw-r--r--tvix/castore/src/import/mod.rs90
-rw-r--r--tvix/castore/src/lib.rs7
-rw-r--r--tvix/castore/src/nodes/directory.rs287
-rw-r--r--tvix/castore/src/nodes/mod.rs48
-rw-r--r--tvix/castore/src/nodes/symlink_target.rs223
-rw-r--r--tvix/castore/src/path/component.rs268
-rw-r--r--tvix/castore/src/path/mod.rs (renamed from tvix/castore/src/path.rs)48
-rw-r--r--tvix/castore/src/proto/grpc_directoryservice_wrapper.rs21
-rw-r--r--tvix/castore/src/proto/mod.rs563
-rw-r--r--tvix/castore/src/proto/tests/directory.rs200
-rw-r--r--tvix/castore/src/proto/tests/directory_nodes_iterator.rs78
-rw-r--r--tvix/castore/src/proto/tests/mod.rs1
-rw-r--r--tvix/castore/src/tests/import.rs32
-rw-r--r--tvix/cli/Cargo.toml32
-rw-r--r--tvix/cli/default.nix64
-rw-r--r--tvix/cli/src/args.rs19
-rw-r--r--tvix/cli/src/lib.rs19
-rw-r--r--tvix/cli/tests/repl.rs15
-rw-r--r--tvix/clippy.toml4
-rw-r--r--tvix/crate-hashes.json1
-rw-r--r--tvix/default.nix68
-rw-r--r--tvix/docs/src/TODO.md30
-rw-r--r--tvix/docs/src/contributing/gerrit.md12
-rw-r--r--tvix/eval/Cargo.toml67
-rw-r--r--tvix/eval/builtin-macros/Cargo.toml6
-rw-r--r--tvix/eval/default.nix4
-rw-r--r--tvix/eval/src/builtins/mod.rs65
-rw-r--r--tvix/eval/src/chunk.rs283
-rw-r--r--tvix/eval/src/compiler/bindings.rs60
-rw-r--r--tvix/eval/src/compiler/mod.rs349
-rw-r--r--tvix/eval/src/compiler/scope.rs1
-rw-r--r--tvix/eval/src/observer.rs14
-rw-r--r--tvix/eval/src/opcode.rs277
-rw-r--r--tvix/eval/src/value/arbitrary.rs12
-rw-r--r--tvix/eval/src/value/attrs.rs190
-rw-r--r--tvix/eval/src/value/attrs/tests.rs8
-rw-r--r--tvix/eval/src/value/list.rs23
-rw-r--r--tvix/eval/src/value/mod.rs35
-rw-r--r--tvix/eval/src/value/string/context.rs161
-rw-r--r--tvix/eval/src/value/string/mod.rs (renamed from tvix/eval/src/value/string.rs)192
-rw-r--r--tvix/eval/src/value/thunk.rs14
-rw-r--r--tvix/eval/src/vm/mod.rs316
-rw-r--r--tvix/glue/Cargo.toml65
-rw-r--r--tvix/glue/benches/eval.rs2
-rw-r--r--tvix/glue/default.nix4
-rw-r--r--tvix/glue/src/builtins/derivation.rs20
-rw-r--r--tvix/glue/src/builtins/import.rs33
-rw-r--r--tvix/glue/src/fetchers/mod.rs33
-rw-r--r--tvix/glue/src/known_paths.rs55
-rw-r--r--tvix/glue/src/refscan.rs268
-rw-r--r--tvix/glue/src/tvix_build.rs47
-rw-r--r--tvix/glue/src/tvix_io.rs6
-rw-r--r--tvix/glue/src/tvix_store_io.rs181
-rw-r--r--tvix/nar-bridge/Cargo.toml50
-rw-r--r--tvix/nar-bridge/default.nix2
-rw-r--r--tvix/nar-bridge/src/lib.rs15
-rw-r--r--tvix/nar-bridge/src/nar.rs85
-rw-r--r--tvix/nar-bridge/src/narinfo.rs104
-rw-r--r--tvix/nix-compat-derive-tests/Cargo.toml24
-rw-r--r--tvix/nix-compat-derive-tests/default.nix5
-rw-r--r--tvix/nix-compat-derive-tests/tests/read_derive.rs417
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui.rs6
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/deserialize_bad_type.rs10
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/deserialize_bad_type.stderr21
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/deserialize_enum_non_exaustive.rs13
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/deserialize_enum_non_exaustive.stderr8
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/deserialize_from_missing.rs7
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/deserialize_from_missing.stderr5
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/deserialize_from_str_error_not_display.rs20
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/deserialize_from_str_error_not_display.stderr13
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/deserialize_from_str_missing.rs7
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/deserialize_from_str_missing.stderr16
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/deserialize_missing_default.rs12
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/deserialize_missing_default.stderr12
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/deserialize_missing_default_path.rs12
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/deserialize_missing_default_path.stderr8
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/deserialize_remote_missing_attr.rs15
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/deserialize_remote_missing_attr.stderr5
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/deserialize_try_from_error_not_display.rs19
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/deserialize_try_from_error_not_display.stderr13
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/deserialize_try_from_missing.rs7
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/deserialize_try_from_missing.stderr8
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/parse_bad_default.rs9
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/parse_bad_default.stderr5
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/parse_bad_default_path.rs9
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/parse_bad_default_path.stderr5
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/parse_bad_nix.rs9
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/parse_bad_nix.stderr5
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/parse_bad_version.rs9
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/parse_bad_version.stderr5
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/parse_mising_version.rs9
-rw-r--r--tvix/nix-compat-derive-tests/tests/ui/parse_mising_version.stderr5
-rw-r--r--tvix/nix-compat-derive/Cargo.toml24
-rw-r--r--tvix/nix-compat-derive/default.nix5
-rw-r--r--tvix/nix-compat-derive/src/de.rs272
-rw-r--r--tvix/nix-compat-derive/src/internal/attrs.rs358
-rw-r--r--tvix/nix-compat-derive/src/internal/ctx.rs50
-rw-r--r--tvix/nix-compat-derive/src/internal/inputs.rs110
-rw-r--r--tvix/nix-compat-derive/src/internal/mod.rs183
-rw-r--r--tvix/nix-compat-derive/src/internal/symbol.rs32
-rw-r--r--tvix/nix-compat-derive/src/lib.rs303
-rw-r--r--tvix/nix-compat/Cargo.toml69
-rw-r--r--tvix/nix-compat/default.nix4
-rw-r--r--tvix/nix-compat/src/aterm/mod.rs4
-rw-r--r--tvix/nix-compat/src/aterm/parser.rs28
-rw-r--r--tvix/nix-compat/src/derivation/mod.rs12
-rw-r--r--tvix/nix-compat/src/derivation/output.rs7
-rw-r--r--tvix/nix-compat/src/derivation/parse_error.rs2
-rw-r--r--tvix/nix-compat/src/derivation/parser.rs75
-rw-r--r--tvix/nix-compat/src/derivation/write.rs16
-rw-r--r--tvix/nix-compat/src/lib.rs5
-rw-r--r--tvix/nix-compat/src/nar/listing/mod.rs128
-rw-r--r--tvix/nix-compat/src/nar/listing/test.rs59
-rw-r--r--tvix/nix-compat/src/nar/mod.rs1
-rw-r--r--tvix/nix-compat/src/nar/reader/mod.rs2
-rw-r--r--tvix/nix-compat/src/nar/tests/nixos-release.ls1
-rw-r--r--tvix/nix-compat/src/nar/writer/sync.rs23
-rw-r--r--tvix/nix-compat/src/narinfo/mod.rs8
-rw-r--r--tvix/nix-compat/src/narinfo/signature.rs117
-rw-r--r--tvix/nix-compat/src/narinfo/signing_keys.rs8
-rw-r--r--tvix/nix-compat/src/narinfo/verifying_keys.rs10
-rw-r--r--tvix/nix-compat/src/nix_daemon/de/bytes.rs70
-rw-r--r--tvix/nix-compat/src/nix_daemon/de/collections.rs105
-rw-r--r--tvix/nix-compat/src/nix_daemon/de/int.rs100
-rw-r--r--tvix/nix-compat/src/nix_daemon/de/mock.rs261
-rw-r--r--tvix/nix-compat/src/nix_daemon/de/mod.rs225
-rw-r--r--tvix/nix-compat/src/nix_daemon/de/reader.rs527
-rw-r--r--tvix/nix-compat/src/nix_daemon/mod.rs2
-rw-r--r--tvix/nix-compat/src/nix_daemon/protocol_version.rs16
-rw-r--r--tvix/nix-compat/src/nix_http/mod.rs115
-rw-r--r--tvix/nix-compat/src/nixhash/ca_hash.rs7
-rw-r--r--tvix/nix-compat/src/store_path/mod.rs323
-rw-r--r--tvix/nix-compat/src/store_path/utils.rs42
-rw-r--r--tvix/nix-compat/src/wire/bytes/mod.rs11
-rw-r--r--tvix/nix-compat/src/wire/bytes/reader/mod.rs2
-rw-r--r--tvix/serde/Cargo.toml4
-rw-r--r--tvix/shell.nix7
-rw-r--r--tvix/store/Cargo.toml104
-rw-r--r--tvix/store/build.rs2
-rw-r--r--tvix/store/default.nix2
-rw-r--r--tvix/store/protos/default.nix21
-rw-r--r--tvix/store/src/bin/tvix-store.rs24
-rw-r--r--tvix/store/src/import.rs49
-rw-r--r--tvix/store/src/nar/import.rs34
-rw-r--r--tvix/store/src/nar/mod.rs16
-rw-r--r--tvix/store/src/nar/renderer.rs77
-rw-r--r--tvix/store/src/pathinfoservice/fs/mod.rs35
-rw-r--r--tvix/store/src/pathinfoservice/grpc.rs15
-rw-r--r--tvix/store/src/pathinfoservice/lru.rs13
-rw-r--r--tvix/store/src/pathinfoservice/nix_http.rs8
-rw-r--r--tvix/store/src/proto/grpc_pathinfoservice_wrapper.rs29
-rw-r--r--tvix/store/src/proto/mod.rs30
-rw-r--r--tvix/store/src/proto/tests/pathinfo.rs54
-rw-r--r--tvix/store/src/tests/nar_renderer.rs51
-rw-r--r--tvix/store/src/utils.rs17
-rw-r--r--tvix/tools/crunch-v2/Cargo.lock69
-rw-r--r--tvix/tools/crunch-v2/Cargo.nix373
-rw-r--r--tvix/tools/crunch-v2/Cargo.toml2
-rw-r--r--tvix/tools/crunch-v2/default.nix4
-rw-r--r--tvix/tools/narinfo2parquet/Cargo.lock68
-rw-r--r--tvix/tools/narinfo2parquet/Cargo.nix359
-rw-r--r--tvix/tools/narinfo2parquet/default.nix4
-rw-r--r--tvix/tools/narinfo2parquet/src/main.rs2
-rw-r--r--tvix/tools/turbofetch/Cargo.lock28
-rw-r--r--tvix/tools/turbofetch/Cargo.nix198
-rw-r--r--tvix/tools/turbofetch/default.nix4
-rw-r--r--tvix/tools/weave/Cargo.lock72
-rw-r--r--tvix/tools/weave/Cargo.nix363
-rw-r--r--tvix/tools/weave/default.nix4
-rw-r--r--tvix/tracing/Cargo.toml34
-rw-r--r--tvix/tracing/default.nix4
-rw-r--r--tvix/utils.nix36
-rw-r--r--tvix/verify-lang-tests/default.nix2
-rw-r--r--users/Profpatsch/.gitignore1
-rw-r--r--users/Profpatsch/README.md2
-rw-r--r--users/Profpatsch/git-db/default.nix10
-rw-r--r--users/Profpatsch/git-db/git-db.rs90
-rw-r--r--users/Profpatsch/lyric.nix54
-rw-r--r--users/Profpatsch/my-prelude/src/Postgres/MonadPostgres.hs66
-rw-r--r--users/Profpatsch/my-prelude/src/Pretty.hs15
-rw-r--r--users/Profpatsch/nix-home/default.nix5
-rw-r--r--users/Profpatsch/openlab-tools/src/OpenlabTools.hs2
-rw-r--r--users/Profpatsch/whatcd-resolver/.gitignore1
-rwxr-xr-xusers/Profpatsch/whatcd-resolver/services/jaeger/run2
-rw-r--r--users/Profpatsch/whatcd-resolver/src/AppT.hs61
-rw-r--r--users/Profpatsch/whatcd-resolver/src/Http.hs4
-rw-r--r--users/Profpatsch/whatcd-resolver/src/Redacted.hs44
-rw-r--r--users/Profpatsch/whatcd-resolver/src/Transmission.hs60
-rw-r--r--users/Profpatsch/whatcd-resolver/src/WhatcdResolver.hs116
-rw-r--r--users/Profpatsch/xdg-cache-home.nix14
-rw-r--r--users/aspen/pkgs/cargo-hakari.nix27
-rw-r--r--users/aspen/pkgs/cargo-nextest.nix27
-rw-r--r--users/aspen/system/home/machines/roswell.nix2
-rw-r--r--users/aspen/system/home/modules/common.nix2
-rw-r--r--users/aspen/system/home/modules/development/rust.nix8
-rw-r--r--users/aspen/system/home/modules/email.nix2
-rw-r--r--users/aspen/system/system/machines/ogopogo.nix5
-rw-r--r--users/aspen/system/system/machines/yeren.nix2
-rw-r--r--users/aspen/system/system/modules/laptop.nix7
-rw-r--r--users/aspen/system/system/modules/sound.nix1
-rw-r--r--users/azahi/OWNERS3
-rw-r--r--users/azahi/pkgs/bruh/default.nix42
-rw-r--r--users/emery/OWNERS3
-rw-r--r--users/emery/eaglemode/default.nix13
-rw-r--r--users/emery/pkgs/syndicate-server.nix34
-rw-r--r--users/emery/workman-cyrillic.xkb118
-rw-r--r--users/flokli/nixos/default.nix1
-rw-r--r--users/kranzes/wasm-hello-world/Cargo.lock21
-rw-r--r--users/kranzes/wasm-hello-world/Cargo.nix109
-rw-r--r--users/picnoir/tvix-daemon/Cargo.lock30
-rw-r--r--users/picnoir/tvix-daemon/Cargo.nix262
-rw-r--r--users/picnoir/tvix-daemon/default.nix10
-rw-r--r--users/sterni/machines/ingeborg/http/code.sterni.lv.nix4
-rw-r--r--users/sterni/modules/backup-minecraft-fabric.nix5
-rw-r--r--users/tazjin/cursed/default.nix9
-rw-r--r--users/tazjin/cursed/responder.nix76
-rw-r--r--users/tazjin/dotfiles/.skip-subtree1
-rw-r--r--users/tazjin/dotfiles/default.nix7
-rw-r--r--users/tazjin/dotfiles/niri.config.kdl132
-rw-r--r--users/tazjin/dotfiles/waybar/config.nix51
-rw-r--r--users/tazjin/dotfiles/waybar/style.css162
-rw-r--r--users/tazjin/eaglemode/default.nix16
-rw-r--r--users/tazjin/emacs/config/init.el8
-rw-r--r--users/tazjin/emacs/default.nix3
-rw-r--r--users/tazjin/german-string/.gitignore1
-rw-r--r--users/tazjin/german-string/Cargo.lock399
-rw-r--r--users/tazjin/german-string/Cargo.toml7
-rw-r--r--users/tazjin/german-string/default.nix5
-rw-r--r--users/tazjin/german-string/src/lib.rs435
-rw-r--r--users/tazjin/home/shared.nix50
-rw-r--r--users/tazjin/home/zamalek.nix2
-rw-r--r--users/tazjin/niri-reap/.gitignore1
-rw-r--r--users/tazjin/niri-reap/Cargo.lock104
-rw-r--r--users/tazjin/niri-reap/Cargo.toml7
-rw-r--r--users/tazjin/niri-reap/default.nix13
-rw-r--r--users/tazjin/niri-reap/src/main.rs76
-rw-r--r--users/tazjin/nixos/arbat/default.nix4
-rw-r--r--users/tazjin/nixos/default.nix4
-rw-r--r--users/tazjin/nixos/frog/default.nix4
-rw-r--r--users/tazjin/nixos/khamovnik/default.nix14
-rw-r--r--users/tazjin/nixos/koptevo/default.nix133
-rw-r--r--users/tazjin/nixos/modules/desktop.nix69
-rw-r--r--users/tazjin/nixos/modules/fonts.nix6
-rw-r--r--users/tazjin/nixos/modules/home-config.nix2
-rw-r--r--users/tazjin/nixos/modules/homepage.nix59
-rw-r--r--users/tazjin/nixos/modules/physical.nix13
-rw-r--r--users/tazjin/nixos/tverskoy/default.nix4
-rw-r--r--users/tazjin/nixos/zamalek/default.nix6
-rw-r--r--users/tazjin/secrets/lego-yandex.agebin0 -> 3886 bytes
-rw-r--r--users/tazjin/secrets/secrets.nix1
-rw-r--r--users/tazjin/wallpapers/alphasoft.webpbin0 -> 176360 bytes
-rw-r--r--users/tazjin/wallpapers/svema_02_big.webpbin0 -> 437844 bytes
-rw-r--r--users/tazjin/wallpapers/svema_07_big.webpbin0 -> 1023978 bytes
-rw-r--r--users/tazjin/wallpapers/svema_09_big.webpbin0 -> 833118 bytes
-rw-r--r--users/tazjin/wallpapers/svema_14_big.webpbin0 -> 861654 bytes
-rw-r--r--users/wpcarro/nixos/ava/default.nix3
-rw-r--r--users/wpcarro/nixos/kyoko/default.nix3
-rw-r--r--users/wpcarro/nixos/marcus/default.nix14
-rw-r--r--users/wpcarro/nixos/tarasco/default.nix3
-rw-r--r--users/yl3dy/OWNERS3
-rw-r--r--users/yl3dy/test.txt1
-rw-r--r--views/README.md5
-rw-r--r--web/atward/indexHtml/default.nix54
-rw-r--r--web/atward/src/main.rs48
-rw-r--r--web/atward/src/tests.rs76
-rw-r--r--web/bubblegum/default.nix1
-rw-r--r--web/cgit-tvl/default.nix2
-rw-r--r--web/pwcrypt/Cargo.lock21
-rw-r--r--web/pwcrypt/Cargo.toml2
-rw-r--r--web/tvixbolt/Cargo.lock120
-rw-r--r--web/tvixbolt/Cargo.nix397
-rw-r--r--web/tvixbolt/default.nix2
-rw-r--r--web/tvl/blog/2024-08-tvix-update.md8
372 files changed, 16856 insertions, 8896 deletions
diff --git a/.gitreview b/.gitreview
new file mode 100644
index 000000000000..6139d7bdadb8
--- /dev/null
+++ b/.gitreview
@@ -0,0 +1,5 @@
+[gerrit]
+host=cl.tvl.fyi
+port=29418
+project=depot
+defaultbranch=canon
diff --git a/.mailmap b/.mailmap
index 06e6c9a44997..a583a1ea96ae 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1 +1,2 @@
 Alyssa Ross <hi@alyssa.is>
+Aspen Smith <root@gws.fyi> <aspen@gws.fyi> <grfn@gws.fyi>
diff --git a/README.md b/README.md
index f58f937cef38..2ef7592688f7 100644
--- a/README.md
+++ b/README.md
@@ -15,10 +15,9 @@ partially see this as [an experiment][] in tooling for monorepos.
 
 ## Services
 
-* Source code is available primarily through Sourcegraph on
-  [cs.tvl.fyi](https://cs.tvl.fyi), where it is searchable and even semantically
-  indexed. A lower-tech view of the repository is also available via cgit-pink
-  on [code.tvl.fyi](https://code.tvl.fyi).
+* Source code can be viewed primarily via `cgit-pink` on
+  [code.tvl.fyi](https://code.tvl.fyi), with code search being available through
+  Livegrep on [grep.tvl.fyi](https://grep.tvl.fyi).
 
   The repository can be cloned using `git` from `https://cl.tvl.fyi/depot`.
 
diff --git a/corp/rih/backend/Cargo.lock b/corp/rih/backend/Cargo.lock
index afbe6fbc0bf4..97b76ca60ca0 100644
--- a/corp/rih/backend/Cargo.lock
+++ b/corp/rih/backend/Cargo.lock
@@ -4,9 +4,9 @@ version = 3
 
 [[package]]
 name = "ahash"
-version = "0.7.6"
+version = "0.7.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
+checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
 dependencies = [
  "getrandom",
  "once_cell",
@@ -30,9 +30,9 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.71"
+version = "1.0.86"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
+checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
 
 [[package]]
 name = "ascii"
@@ -42,13 +42,13 @@ checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"
 
 [[package]]
 name = "async-trait"
-version = "0.1.68"
+version = "0.1.81"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
+checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.18",
+ "syn",
 ]
 
 [[package]]
@@ -69,9 +69,9 @@ dependencies = [
 
 [[package]]
 name = "autocfg"
-version = "1.1.0"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
 
 [[package]]
 name = "aws-creds"
@@ -92,9 +92,9 @@ dependencies = [
 
 [[package]]
 name = "aws-region"
-version = "0.25.3"
+version = "0.25.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "056557a61427d0e5ba29dd931031c8ffed4ee7a550e7cd55692a9d8deb0a9dba"
+checksum = "e9aed3f9c7eac9be28662fdb3b0f4d1951e812f7c64fed4f0327ba702f459b3b"
 dependencies = [
  "thiserror",
 ]
@@ -107,9 +107,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
 
 [[package]]
 name = "bitflags"
-version = "1.3.2"
+version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
 
 [[package]]
 name = "block-buffer"
@@ -132,21 +132,30 @@ dependencies = [
 
 [[package]]
 name = "bumpalo"
-version = "3.13.0"
+version = "3.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
 [[package]]
 name = "bytes"
-version = "1.4.0"
+version = "1.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
 
 [[package]]
 name = "cc"
-version = "1.0.79"
+version = "1.1.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6"
+dependencies = [
+ "shlex",
+]
 
 [[package]]
 name = "cfg-if"
@@ -156,33 +165,33 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "chrono"
-version = "0.4.26"
+version = "0.4.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
+checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
 dependencies = [
  "android-tzdata",
  "iana-time-zone",
  "num-traits",
- "winapi",
+ "windows-targets",
 ]
 
 [[package]]
 name = "chunked_transfer"
-version = "1.4.1"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a"
+checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901"
 
 [[package]]
 name = "core-foundation-sys"
-version = "0.8.4"
+version = "0.8.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
 
 [[package]]
 name = "cpufeatures"
-version = "0.2.7"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58"
+checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad"
 dependencies = [
  "libc",
 ]
@@ -198,6 +207,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "deranged"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+dependencies = [
+ "powerfmt",
+ "serde",
+]
+
+[[package]]
 name = "digest"
 version = "0.10.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -236,44 +255,30 @@ checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257"
 
 [[package]]
 name = "errno"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
-dependencies = [
- "errno-dragonfly",
- "libc",
- "windows-sys 0.48.0",
-]
-
-[[package]]
-name = "errno-dragonfly"
-version = "0.1.2"
+version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
 dependencies = [
- "cc",
  "libc",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "fastrand"
-version = "1.9.0"
+version = "2.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
-dependencies = [
- "instant",
-]
+checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
 
 [[package]]
 name = "filetime"
-version = "0.2.21"
+version = "0.2.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153"
+checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
 dependencies = [
  "cfg-if",
  "libc",
- "redox_syscall 0.2.16",
- "windows-sys 0.48.0",
+ "libredox",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -284,9 +289,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
 [[package]]
 name = "form_urlencoded"
-version = "1.2.0"
+version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
 dependencies = [
  "percent-encoding",
 ]
@@ -303,9 +308,9 @@ dependencies = [
 
 [[package]]
 name = "getrandom"
-version = "0.2.9"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
+checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
 dependencies = [
  "cfg-if",
  "libc",
@@ -323,18 +328,9 @@ dependencies = [
 
 [[package]]
 name = "hermit-abi"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "hermit-abi"
-version = "0.3.1"
+version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
 
 [[package]]
 name = "hex"
@@ -353,9 +349,9 @@ dependencies = [
 
 [[package]]
 name = "http"
-version = "0.2.9"
+version = "0.2.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
+checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
 dependencies = [
  "bytes",
  "fnv",
@@ -364,28 +360,28 @@ dependencies = [
 
 [[package]]
 name = "httparse"
-version = "1.8.0"
+version = "1.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
 
 [[package]]
 name = "httpdate"
-version = "1.0.2"
+version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
 
 [[package]]
 name = "iana-time-zone"
-version = "0.1.56"
+version = "0.1.60"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
 dependencies = [
  "android_system_properties",
  "core-foundation-sys",
  "iana-time-zone-haiku",
  "js-sys",
  "wasm-bindgen",
- "windows",
+ "windows-core",
 ]
 
 [[package]]
@@ -399,76 +395,67 @@ dependencies = [
 
 [[package]]
 name = "idna"
-version = "0.4.0"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
 dependencies = [
  "unicode-bidi",
  "unicode-normalization",
 ]
 
 [[package]]
-name = "instant"
-version = "0.1.12"
+name = "itoa"
+version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
-dependencies = [
- "cfg-if",
-]
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
 
 [[package]]
-name = "io-lifetimes"
-version = "1.0.11"
+name = "js-sys"
+version = "0.3.70"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
+checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
 dependencies = [
- "hermit-abi 0.3.1",
- "libc",
- "windows-sys 0.48.0",
+ "wasm-bindgen",
 ]
 
 [[package]]
-name = "itoa"
-version = "1.0.6"
+name = "libc"
+version = "0.2.158"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
+checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
 
 [[package]]
-name = "js-sys"
-version = "0.3.63"
+name = "libredox"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790"
+checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
 dependencies = [
- "wasm-bindgen",
+ "bitflags",
+ "libc",
+ "redox_syscall",
 ]
 
 [[package]]
-name = "libc"
-version = "0.2.144"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
-
-[[package]]
 name = "linux-raw-sys"
-version = "0.3.8"
+version = "0.4.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
 
 [[package]]
 name = "log"
-version = "0.4.18"
+version = "0.4.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
 
 [[package]]
 name = "maybe-async"
-version = "0.2.7"
+version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f1b8c13cb1f814b634a96b2c725449fe7ed464a7b8781de8688be5ffbd3f305"
+checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 1.0.109",
+ "syn",
 ]
 
 [[package]]
@@ -479,9 +466,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
 
 [[package]]
 name = "memchr"
-version = "2.5.0"
+version = "2.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
 
 [[package]]
 name = "mime"
@@ -491,9 +478,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
 
 [[package]]
 name = "mime_guess"
-version = "2.0.4"
+version = "2.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
+checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
 dependencies = [
  "mime",
  "unicase",
@@ -518,38 +505,44 @@ dependencies = [
 ]
 
 [[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
 name = "num-traits"
-version = "0.2.15"
+version = "0.2.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
 dependencies = [
  "autocfg",
 ]
 
 [[package]]
 name = "num_cpus"
-version = "1.15.0"
+version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
 dependencies = [
- "hermit-abi 0.2.6",
+ "hermit-abi",
  "libc",
 ]
 
 [[package]]
 name = "num_threads"
-version = "0.1.6"
+version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
+checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "once_cell"
-version = "1.18.0"
+version = "1.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
 
 [[package]]
 name = "ordered-multimap"
@@ -563,21 +556,30 @@ dependencies = [
 
 [[package]]
 name = "percent-encoding"
-version = "2.3.0"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
 
 [[package]]
 name = "ppv-lite86"
-version = "0.2.17"
+version = "0.2.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+dependencies = [
+ "zerocopy",
+]
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.59"
+version = "1.0.86"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
 dependencies = [
  "unicode-ident",
 ]
@@ -600,9 +602,9 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.28"
+version = "1.0.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
 dependencies = [
  "proc-macro2",
 ]
@@ -639,30 +641,21 @@ dependencies = [
 
 [[package]]
 name = "redox_syscall"
-version = "0.2.16"
+version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
+checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
 dependencies = [
  "bitflags",
 ]
 
 [[package]]
 name = "redox_users"
-version = "0.4.3"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
+checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
 dependencies = [
  "getrandom",
- "redox_syscall 0.2.16",
+ "libredox",
  "thiserror",
 ]
 
@@ -689,13 +682,28 @@ dependencies = [
  "cc",
  "libc",
  "once_cell",
- "spin",
- "untrusted",
+ "spin 0.5.2",
+ "untrusted 0.7.1",
  "web-sys",
  "winapi",
 ]
 
 [[package]]
+name = "ring"
+version = "0.17.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom",
+ "libc",
+ "spin 0.9.8",
+ "untrusted 0.9.0",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
 name = "rouille"
 version = "3.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -758,35 +766,34 @@ dependencies = [
 
 [[package]]
 name = "rustix"
-version = "0.37.19"
+version = "0.38.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
+checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f"
 dependencies = [
  "bitflags",
  "errno",
- "io-lifetimes",
  "libc",
  "linux-raw-sys",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "rustls"
-version = "0.20.8"
+version = "0.20.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f"
+checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99"
 dependencies = [
  "log",
- "ring",
+ "ring 0.16.20",
  "sct",
  "webpki",
 ]
 
 [[package]]
 name = "ryu"
-version = "1.0.13"
+version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
 
 [[package]]
 name = "safemem"
@@ -796,56 +803,57 @@ checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
 
 [[package]]
 name = "sct"
-version = "0.7.0"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
+checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
 dependencies = [
- "ring",
- "untrusted",
+ "ring 0.17.8",
+ "untrusted 0.9.0",
 ]
 
 [[package]]
 name = "serde"
-version = "1.0.163"
+version = "1.0.209"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
+checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.163"
+version = "1.0.209"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
+checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.18",
+ "syn",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.96"
+version = "1.0.127"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
+checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad"
 dependencies = [
  "itoa",
+ "memchr",
  "ryu",
  "serde",
 ]
 
 [[package]]
 name = "sha1_smol"
-version = "1.0.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
+checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d"
 
 [[package]]
 name = "sha2"
-version = "0.10.6"
+version = "0.10.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
 dependencies = [
  "cfg-if",
  "cpufeatures",
@@ -853,33 +861,34 @@ dependencies = [
 ]
 
 [[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
 name = "spin"
 version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
 
 [[package]]
-name = "subtle"
-version = "2.5.0"
+name = "spin"
+version = "0.9.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
 
 [[package]]
-name = "syn"
-version = "1.0.109"
+name = "subtle"
+version = "2.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
 
 [[package]]
 name = "syn"
-version = "2.0.18"
+version = "2.0.77"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
+checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -888,35 +897,35 @@ dependencies = [
 
 [[package]]
 name = "tempfile"
-version = "3.5.0"
+version = "3.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998"
+checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
 dependencies = [
  "cfg-if",
  "fastrand",
- "redox_syscall 0.3.5",
+ "once_cell",
  "rustix",
- "windows-sys 0.45.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
 name = "thiserror"
-version = "1.0.40"
+version = "1.0.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
+checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.40"
+version = "1.0.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
+checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.18",
+ "syn",
 ]
 
 [[package]]
@@ -930,13 +939,16 @@ dependencies = [
 
 [[package]]
 name = "time"
-version = "0.3.21"
+version = "0.3.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc"
+checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
 dependencies = [
+ "deranged",
  "itoa",
  "libc",
+ "num-conv",
  "num_threads",
+ "powerfmt",
  "serde",
  "time-core",
  "time-macros",
@@ -944,16 +956,17 @@ dependencies = [
 
 [[package]]
 name = "time-core"
-version = "0.1.1"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
 
 [[package]]
 name = "time-macros"
-version = "0.2.9"
+version = "0.2.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b"
+checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
 dependencies = [
+ "num-conv",
  "time-core",
 ]
 
@@ -971,9 +984,9 @@ dependencies = [
 
 [[package]]
 name = "tinyvec"
-version = "1.6.0"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
 dependencies = [
  "tinyvec_macros",
 ]
@@ -995,36 +1008,36 @@ dependencies = [
 
 [[package]]
 name = "typenum"
-version = "1.16.0"
+version = "1.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
 
 [[package]]
 name = "unicase"
-version = "2.6.0"
+version = "2.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
+checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
 dependencies = [
  "version_check",
 ]
 
 [[package]]
 name = "unicode-bidi"
-version = "0.3.13"
+version = "0.3.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
+checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
 
 [[package]]
 name = "unicode-ident"
-version = "1.0.9"
+version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 
 [[package]]
 name = "unicode-normalization"
-version = "0.1.22"
+version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
+checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
 dependencies = [
  "tinyvec",
 ]
@@ -1036,10 +1049,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
 
 [[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
+[[package]]
 name = "url"
-version = "2.4.0"
+version = "2.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb"
+checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
 dependencies = [
  "form_urlencoded",
  "idna",
@@ -1048,9 +1067,9 @@ dependencies = [
 
 [[package]]
 name = "uuid"
-version = "1.3.3"
+version = "1.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2"
+checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
 dependencies = [
  "getrandom",
  "serde",
@@ -1058,9 +1077,9 @@ dependencies = [
 
 [[package]]
 name = "version_check"
-version = "0.9.4"
+version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
 
 [[package]]
 name = "wasi"
@@ -1070,34 +1089,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.86"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73"
+checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
 dependencies = [
  "cfg-if",
+ "once_cell",
  "wasm-bindgen-macro",
 ]
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.86"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb"
+checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
 dependencies = [
  "bumpalo",
  "log",
  "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.18",
+ "syn",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.86"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258"
+checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -1105,28 +1125,28 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.86"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
+checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.18",
+ "syn",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.86"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93"
+checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
 
 [[package]]
 name = "web-sys"
-version = "0.3.63"
+version = "0.3.70"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2"
+checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -1134,12 +1154,12 @@ dependencies = [
 
 [[package]]
 name = "webpki"
-version = "0.22.0"
+version = "0.22.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
+checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53"
 dependencies = [
- "ring",
- "untrusted",
+ "ring 0.17.8",
+ "untrusted 0.9.0",
 ]
 
 [[package]]
@@ -1174,142 +1194,113 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
-name = "windows"
-version = "0.48.0"
+name = "windows-core"
+version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
 dependencies = [
- "windows-targets 0.48.0",
+ "windows-targets",
 ]
 
 [[package]]
 name = "windows-sys"
-version = "0.45.0"
+version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
 dependencies = [
- "windows-targets 0.42.2",
+ "windows-targets",
 ]
 
 [[package]]
 name = "windows-sys"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
-dependencies = [
- "windows-targets 0.48.0",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.42.2"
+version = "0.59.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
 dependencies = [
- "windows_aarch64_gnullvm 0.42.2",
- "windows_aarch64_msvc 0.42.2",
- "windows_i686_gnu 0.42.2",
- "windows_i686_msvc 0.42.2",
- "windows_x86_64_gnu 0.42.2",
- "windows_x86_64_gnullvm 0.42.2",
- "windows_x86_64_msvc 0.42.2",
+ "windows-targets",
 ]
 
 [[package]]
 name = "windows-targets"
-version = "0.48.0"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
 dependencies = [
- "windows_aarch64_gnullvm 0.48.0",
- "windows_aarch64_msvc 0.48.0",
- "windows_i686_gnu 0.48.0",
- "windows_i686_msvc 0.48.0",
- "windows_x86_64_gnu 0.48.0",
- "windows_x86_64_gnullvm 0.48.0",
- "windows_x86_64_msvc 0.48.0",
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
 ]
 
 [[package]]
 name = "windows_aarch64_gnullvm"
-version = "0.42.2"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
-
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
 
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.42.2"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.42.2"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
 
 [[package]]
-name = "windows_i686_gnu"
-version = "0.48.0"
+name = "windows_i686_gnullvm"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.42.2"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.42.2"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
-version = "0.42.2"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
 
 [[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.48.0"
+name = "windows_x86_64_msvc"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 
 [[package]]
-name = "windows_x86_64_msvc"
-version = "0.42.2"
+name = "zerocopy"
+version = "0.7.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+dependencies = [
+ "byteorder",
+ "zerocopy-derive",
+]
 
 [[package]]
-name = "windows_x86_64_msvc"
-version = "0.48.0"
+name = "zerocopy-derive"
+version = "0.7.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
diff --git a/corp/rih/frontend/Cargo.lock b/corp/rih/frontend/Cargo.lock
index 40641d5de343..9ce686ff35d6 100644
--- a/corp/rih/frontend/Cargo.lock
+++ b/corp/rih/frontend/Cargo.lock
@@ -1500,19 +1500,20 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
 dependencies = [
  "cfg-if",
+ "once_cell",
  "wasm-bindgen-macro",
 ]
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
 dependencies = [
  "bumpalo",
  "log",
@@ -1537,9 +1538,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -1547,9 +1548,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1560,9 +1561,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
 
 [[package]]
 name = "web-sys"
diff --git a/corp/rih/frontend/Cargo.toml b/corp/rih/frontend/Cargo.toml
index 1b9d5009791b..193fa919b45e 100644
--- a/corp/rih/frontend/Cargo.toml
+++ b/corp/rih/frontend/Cargo.toml
@@ -19,7 +19,7 @@ yew-router = "0.17"
 wasm-bindgen-futures = "0.4"
 
 # needs to be in sync with nixpkgs
-wasm-bindgen = "= 0.2.92"
+wasm-bindgen = "= 0.2.93"
 uuid = { version = "1.3.3", features = ["v4", "serde"] }
 
 [dependencies.serde]
diff --git a/corp/russian/predlozhnik/Cargo.lock b/corp/russian/predlozhnik/Cargo.lock
index 6874e3554c56..514050e55340 100644
--- a/corp/russian/predlozhnik/Cargo.lock
+++ b/corp/russian/predlozhnik/Cargo.lock
@@ -363,19 +363,20 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
 dependencies = [
  "cfg-if",
+ "once_cell",
  "wasm-bindgen-macro",
 ]
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
 dependencies = [
  "bumpalo",
  "log",
@@ -400,9 +401,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -410,9 +411,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -423,9 +424,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
 
 [[package]]
 name = "web-sys"
diff --git a/corp/russian/predlozhnik/Cargo.toml b/corp/russian/predlozhnik/Cargo.toml
index 0b099ac927a3..63760812470a 100644
--- a/corp/russian/predlozhnik/Cargo.toml
+++ b/corp/russian/predlozhnik/Cargo.toml
@@ -9,4 +9,4 @@ lazy_static = "1.4"
 yew = "0.19"
 
 # needs to be in sync with nixpkgs
-wasm-bindgen = "= 0.2.92"
+wasm-bindgen = "= 0.2.93"
diff --git a/docs/importing-projects.md b/docs/importing-projects.md
new file mode 100644
index 000000000000..c68c8b1906ae
--- /dev/null
+++ b/docs/importing-projects.md
@@ -0,0 +1,91 @@
+Importing projects into depot
+=============================
+
+Before importing an existing `git`-based project into depot, a few questions
+need to be answered:
+
+
+* Is the project licensed under a free software license, or public domain?
+* Do you need to import existing history?
+* Do you need to export new history with hashes that continue on from the old
+  history? (e.g. importing an existing repository, and exporting from depot to
+  the old upstream)
+
+Think about this and then pick an approach below:
+
+## Import with no history (just commit)
+
+Simply copy the files to where you want them to be in depot, and commit. Nothing
+else to do!
+
+## Import without continuous history (subtree merge)
+
+This import approach lets you drop an existing project into depot, keep its
+existing history, but not retain the ability to continue external history.
+
+This means that if you, for example, import a project from a different git host
+using this method, and then continue to commit to it inside of depot, you will
+not be able to export a history consistent with your previous hashes using
+`josh`.
+
+Commit hashes before the import will exist in depot and be valid.
+
+Still, this approach might be viable if a project "moves into" depot, or has
+nothing depending on it externally.
+
+1. Pick a location in depot where you want your project to be (`$loc` from now on).
+2. Fetch your project into the same git store as your depot clone (e.g. by
+   adding it as an upstream and fetching it).
+3. Pick the commit you want to merge (`$commit` from now on).
+4. Run `git subtree add --prefix=$loc $commit`, which will create the correct
+   merge commit.
+5. Ensure Gerrit [knows about your commit](#preparing-merges-in-gerrit) for the
+   parent that is being merged.
+6. Modify the merge commit's message to start with `subtree($project_name):`.
+   Gerrit **will not** allow merge commits that do not follow this format.
+7. Push your subtree commit for review as normal.
+
+## Import with continuous history
+
+This approach imports the history using `josh`, which means that external
+history before/after the import is consistent (you can continue committing in
+`depot`, export the history back out, and from an external perspective nothing
+changes).
+
+This is what we did with repositories like `nix-1p` and `nixery`.
+
+Note: Inside of depot, the pre-import commit hashes will **not make sense**.
+`josh` will rewrite them in such a way that exporting the project will yield the
+same hashes, but this rewriting changes the hashes of your commits inside of
+depot.
+
+1. Pick a location in depot where you want your project to be (`$loc`).
+2. Fetch your project into the same git store as your depot clone (e.g. by
+   adding it as an upstream and fetching it).
+3. Check out the commit you want to merge into depot.
+4. Run `josh-filter ":prefix=$loc"`, and take note of the `FILTERED_HEAD` ref
+   that it produces (`$filtered` from now on).
+5. Ensure Gerrit [knows about the filtered commit](#preparing-merges-in-gerrit).
+6. Merge the filtered commit into depot using a standard merge, but make sure to
+   add the `--allow-unrelated-histories` flag. Your commit message **must**
+   start with `subtree($project_name):`, otherwise Gerrit will not let you push
+   a merge.
+7. Push the merge commit for review as usual.
+
+------------------------------------------------------
+
+## Preparing merges in Gerrit
+
+When pushing a merge to Gerrit, it needs to know about all ancestors of the
+merge, otherwise it will try to interpret commits as new CLs and reject them for
+not having a change ID (or create a huge number of CLs, if they do have one).
+
+To prevent this, we have a special git ref called `subtree-staging` which you
+can push external trees to.
+
+Access to `subtree-staging` has to be granted by a TVL admin, so ping tazjin,
+lukegb, flokli, sterni and so on before proceeding.
+
+1. Determine the commit you want to merge (`$commit`).
+2. Run `git push -f $commit origin/subtree-staging` (or replace `origin` with
+   whatever the TVL Gerrit remote is called in your clone).
diff --git a/nix/writers/default.nix b/nix/writers/default.nix
index 55355913a9f4..e849f1cb0e24 100644
--- a/nix/writers/default.nix
+++ b/nix/writers/default.nix
@@ -23,7 +23,6 @@ let
     { name
     , dependencies ? [ ]
     , doCheck ? true
-    ,
     }: src:
     (if doCheck then testRustSimple else pkgs.lib.id)
       (pkgs.buildRustCrate ({
diff --git a/ops/besadii/default.nix b/ops/besadii/default.nix
index 1199c56cfb94..424fa19b2b44 100644
--- a/ops/besadii/default.nix
+++ b/ops/besadii/default.nix
@@ -1,5 +1,5 @@
 # This program is used as a Gerrit hook to trigger builds on
-# Buildkite, Sourcegraph reindexing and other maintenance tasks.
+# Buildkite and perform other maintenance tasks.
 { depot, ... }:
 
 depot.nix.buildGo.program {
diff --git a/ops/besadii/main.go b/ops/besadii/main.go
index 809acc29e8b4..aacd8f176ecf 100644
--- a/ops/besadii/main.go
+++ b/ops/besadii/main.go
@@ -8,7 +8,6 @@
 //
 // Gerrit (ref-updated) hook:
 // - Trigger Buildkite CI builds
-// - Trigger SourceGraph repository index updates
 //
 // Buildkite (post-command) hook:
 // - Submit CL verification status back to Gerrit
@@ -55,10 +54,6 @@ type config struct {
 	BuildkiteProject string `json:"buildkiteProject"`
 	BuildkiteToken   string `json:"buildkiteToken"`
 	GerritChangeName string `json:"gerritChangeName"`
-
-	// Optional configuration for Sourcegraph trigger updates.
-	SourcegraphUrl   string `json:"sourcegraphUrl"`
-	SourcegraphToken string `json:"sourcegraphToken"`
 }
 
 // buildTrigger represents the information passed to besadii when it
@@ -154,11 +149,6 @@ func loadConfig() (*config, error) {
 		return nil, fmt.Errorf("invalid 'gerritChangeName': %s", cfg.GerritChangeName)
 	}
 
-	// Rudimentary config validation logic
-	if cfg.SourcegraphUrl != "" && cfg.SourcegraphToken == "" {
-		return nil, fmt.Errorf("'SourcegraphToken' must be set if 'SourcegraphUrl' is set")
-	}
-
 	if cfg.Repository == "" || cfg.Branch == "" {
 		return nil, fmt.Errorf("missing repository configuration (required: repository, branch)")
 	}
@@ -299,26 +289,6 @@ func triggerBuild(cfg *config, log *syslog.Writer, trigger *buildTrigger) error
 	return nil
 }
 
-// Trigger a Sourcegraph repository index update.
-//
-// https://docs.sourcegraph.com/admin/repo/webhooks
-func triggerIndexUpdate(cfg *config, log *syslog.Writer) error {
-	req, err := http.NewRequest("POST", cfg.SourcegraphUrl, nil)
-	if err != nil {
-		return err
-	}
-
-	req.Header.Add("Authorization", "token "+cfg.SourcegraphToken)
-
-	_, err = http.DefaultClient.Do(req)
-	if err != nil {
-		return fmt.Errorf("failed to trigger Sourcegraph index update: %w", err)
-	}
-
-	log.Info("triggered sourcegraph index update")
-	return nil
-}
-
 // Gerrit passes more flags than we want, but Rob Pike decided[0] in
 // 2013 that the Go art project will not allow users to ignore flags
 // because he "doesn't like it". This function allows users to ignore
@@ -458,13 +428,6 @@ func gerritHookMain(cfg *config, log *syslog.Writer, trigger *buildTrigger) {
 	if err != nil {
 		log.Err(fmt.Sprintf("failed to trigger Buildkite build: %s", err))
 	}
-
-	if cfg.SourcegraphUrl != "" && trigger.ref == cfg.Branch {
-		err = triggerIndexUpdate(cfg, log)
-		if err != nil {
-			log.Err(fmt.Sprintf("failed to trigger sourcegraph index update: %s", err))
-		}
-	}
 }
 
 func postCommandMain(cfg *config) {
diff --git a/ops/buildkite/tvl.tf b/ops/buildkite/tvl.tf
index 4c45909a0c36..ee032a5143cb 100644
--- a/ops/buildkite/tvl.tf
+++ b/ops/buildkite/tvl.tf
@@ -8,7 +8,9 @@ terraform {
   }
 
   backend "s3" {
-    endpoint = "https://objects.dc-sto1.glesys.net"
+    endpoints = {
+      s3 = "https://objects.dc-sto1.glesys.net"
+    }
     bucket   = "tvl-state"
     key      = "terraform/tvl-buildkite"
     region   = "glesys"
@@ -16,6 +18,8 @@ terraform {
     skip_credentials_validation = true
     skip_region_validation      = true
     skip_metadata_api_check     = true
+    skip_requesting_account_id  = true
+    skip_s3_checksum            = true
   }
 }
 
diff --git a/ops/journaldriver/Cargo.lock b/ops/journaldriver/Cargo.lock
index 97bbe16ceb4c..eaf87cc525e3 100644
--- a/ops/journaldriver/Cargo.lock
+++ b/ops/journaldriver/Cargo.lock
@@ -4,18 +4,18 @@ version = 3
 
 [[package]]
 name = "aho-corasick"
-version = "1.1.2"
+version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
 name = "anyhow"
-version = "1.0.75"
+version = "1.0.86"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
+checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
 
 [[package]]
 name = "base64"
@@ -25,9 +25,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
 
 [[package]]
 name = "bitflags"
-version = "2.4.1"
+version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
 
 [[package]]
 name = "build-env"
@@ -37,11 +37,11 @@ checksum = "e068f31938f954b695423ecaf756179597627d0828c0d3e48c0a722a8b23cf9e"
 
 [[package]]
 name = "cc"
-version = "1.0.84"
+version = "1.1.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f8e7c90afad890484a21653d08b6e209ae34770fb5ee298f9c699fcc1e5c856"
+checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6"
 dependencies = [
- "libc",
+ "shlex",
 ]
 
 [[package]]
@@ -73,9 +73,9 @@ dependencies = [
 
 [[package]]
 name = "curl"
-version = "0.4.44"
+version = "0.4.46"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22"
+checksum = "1e2161dd6eba090ff1594084e95fd67aeccf04382ffea77999ea94ed42ec67b6"
 dependencies = [
  "curl-sys",
  "libc",
@@ -83,14 +83,14 @@ dependencies = [
  "openssl-sys",
  "schannel",
  "socket2",
- "winapi",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "curl-sys"
-version = "0.4.68+curl-8.4.0"
+version = "0.4.74+curl-8.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4a0d18d88360e374b16b2273c832b5e57258ffc1d4aa4f96b108e0738d5752f"
+checksum = "8af10b986114528fcdc4b63b6f5f021b7057618411046a4de2ba0f0149a097bf"
 dependencies = [
  "cc",
  "libc",
@@ -98,14 +98,14 @@ dependencies = [
  "openssl-sys",
  "pkg-config",
  "vcpkg",
- "windows-sys",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "deranged"
-version = "0.3.9"
+version = "0.3.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
 dependencies = [
  "powerfmt",
  "serde",
@@ -113,9 +113,9 @@ dependencies = [
 
 [[package]]
 name = "env_logger"
-version = "0.10.1"
+version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece"
+checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
 dependencies = [
  "humantime",
  "is-terminal",
@@ -125,16 +125,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "errno"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e"
-dependencies = [
- "libc",
- "windows-sys",
-]
-
-[[package]]
 name = "foreign-types"
 version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -178,9 +168,9 @@ checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
 
 [[package]]
 name = "hermit-abi"
-version = "0.3.3"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
+checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
 
 [[package]]
 name = "humantime"
@@ -190,20 +180,20 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
 
 [[package]]
 name = "is-terminal"
-version = "0.4.9"
+version = "0.4.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
+checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
 dependencies = [
  "hermit-abi",
- "rustix",
- "windows-sys",
+ "libc",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "itoa"
-version = "1.0.9"
+version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
 
 [[package]]
 name = "journaldriver"
@@ -224,15 +214,15 @@ dependencies = [
 
 [[package]]
 name = "lazy_static"
-version = "1.4.0"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
 
 [[package]]
 name = "libc"
-version = "0.2.150"
+version = "0.2.158"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
+checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
 
 [[package]]
 name = "libsystemd-sys"
@@ -247,9 +237,9 @@ dependencies = [
 
 [[package]]
 name = "libz-sys"
-version = "1.1.12"
+version = "1.1.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b"
+checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472"
 dependencies = [
  "cc",
  "libc",
@@ -258,16 +248,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "linux-raw-sys"
-version = "0.4.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
-
-[[package]]
 name = "log"
-version = "0.4.20"
+version = "0.4.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
 
 [[package]]
 name = "medallion"
@@ -285,21 +269,27 @@ dependencies = [
 
 [[package]]
 name = "memchr"
-version = "2.6.4"
+version = "2.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
 
 [[package]]
 name = "once_cell"
-version = "1.18.0"
+version = "1.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
 
 [[package]]
 name = "openssl"
-version = "0.10.59"
+version = "0.10.66"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a257ad03cd8fb16ad4172fedf8094451e1af1c4b70097636ef2eac9a5f0cc33"
+checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
 dependencies = [
  "bitflags",
  "cfg-if",
@@ -329,9 +319,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
 
 [[package]]
 name = "openssl-sys"
-version = "0.9.95"
+version = "0.9.103"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9"
+checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
 dependencies = [
  "cc",
  "libc",
@@ -341,9 +331,9 @@ dependencies = [
 
 [[package]]
 name = "pkg-config"
-version = "0.3.27"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
 
 [[package]]
 name = "powerfmt"
@@ -353,27 +343,27 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.69"
+version = "1.0.86"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.33"
+version = "1.0.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
 dependencies = [
  "proc-macro2",
 ]
 
 [[package]]
 name = "regex"
-version = "1.10.2"
+version = "1.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
+checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -383,9 +373,9 @@ dependencies = [
 
 [[package]]
 name = "regex-automata"
-version = "0.4.3"
+version = "0.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -394,52 +384,39 @@ dependencies = [
 
 [[package]]
 name = "regex-syntax"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
-
-[[package]]
-name = "rustix"
-version = "0.38.21"
+version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
-dependencies = [
- "bitflags",
- "errno",
- "libc",
- "linux-raw-sys",
- "windows-sys",
-]
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
 
 [[package]]
 name = "ryu"
-version = "1.0.15"
+version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
 
 [[package]]
 name = "schannel"
-version = "0.1.22"
+version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
+checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
 dependencies = [
- "windows-sys",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "serde"
-version = "1.0.192"
+version = "1.0.209"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
+checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.192"
+version = "1.0.209"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
+checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -448,30 +425,37 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.108"
+version = "1.0.127"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
+checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad"
 dependencies = [
  "itoa",
+ "memchr",
  "ryu",
  "serde",
 ]
 
 [[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
 name = "socket2"
-version = "0.4.10"
+version = "0.5.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
+checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
 dependencies = [
  "libc",
- "winapi",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "syn"
-version = "2.0.39"
+version = "2.0.77"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
+checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -494,21 +478,22 @@ dependencies = [
 
 [[package]]
 name = "termcolor"
-version = "1.3.0"
+version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
 dependencies = [
  "winapi-util",
 ]
 
 [[package]]
 name = "time"
-version = "0.3.30"
+version = "0.3.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5"
+checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
 dependencies = [
  "deranged",
  "itoa",
+ "num-conv",
  "powerfmt",
  "serde",
  "time-core",
@@ -523,10 +508,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
 
 [[package]]
 name = "time-macros"
-version = "0.2.15"
+version = "0.2.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20"
+checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
 dependencies = [
+ "num-conv",
  "time-core",
 ]
 
@@ -549,54 +535,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
 
 [[package]]
-name = "winapi"
-version = "0.3.9"
+name = "winapi-util"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
 dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
-[[package]]
-name = "winapi-util"
-version = "0.1.6"
+name = "windows-sys"
+version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
 dependencies = [
- "winapi",
+ "windows-targets",
 ]
 
 [[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
-
-[[package]]
 name = "windows-sys"
-version = "0.48.0"
+version = "0.59.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
 dependencies = [
  "windows-targets",
 ]
 
 [[package]]
 name = "windows-targets"
-version = "0.48.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
 dependencies = [
  "windows_aarch64_gnullvm",
  "windows_aarch64_msvc",
  "windows_i686_gnu",
+ "windows_i686_gnullvm",
  "windows_i686_msvc",
  "windows_x86_64_gnu",
  "windows_x86_64_gnullvm",
@@ -605,42 +579,48 @@ dependencies = [
 
 [[package]]
 name = "windows_aarch64_gnullvm"
-version = "0.48.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
 
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.48.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.48.5"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.48.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.48.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
-version = "0.48.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.48.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
diff --git a/ops/keycloak/main.tf b/ops/keycloak/main.tf
index 923ac1939712..d5698700ce58 100644
--- a/ops/keycloak/main.tf
+++ b/ops/keycloak/main.tf
@@ -10,20 +10,29 @@ terraform {
   }
 
   backend "s3" {
-    endpoint = "https://objects.dc-sto1.glesys.net"
-    bucket   = "tvl-state"
-    key      = "terraform/tvl-keycloak"
-    region   = "glesys"
+    endpoints = {
+      s3 = "https://objects.dc-sto1.glesys.net"
+    }
+    bucket = "tvl-state"
+    key    = "terraform/tvl-keycloak"
+    region = "glesys"
 
     skip_credentials_validation = true
     skip_region_validation      = true
     skip_metadata_api_check     = true
+    skip_requesting_account_id  = true
+    skip_s3_checksum            = true
   }
 }
 
 provider "keycloak" {
   client_id = "terraform"
   url       = "https://auth.tvl.fyi"
+  # NOTE: Docs mention this applies to "users of the legacy distribution of keycloak".
+  # However, we get a "failed to perform initial login to Keycloak: error
+  # sending POST request to https://auth.tvl.fyi/realms/master/protocol/openid-connect/token: 404 Not Found"
+  # if we don't set this.
+  base_path = "/auth"
 }
 
 resource "keycloak_realm" "tvl" {
diff --git a/ops/keycloak/user_sources.tf b/ops/keycloak/user_sources.tf
index 01307fff8df5..7fa71e36fbb2 100644
--- a/ops/keycloak/user_sources.tf
+++ b/ops/keycloak/user_sources.tf
@@ -22,6 +22,13 @@ resource "keycloak_ldap_user_federation" "tvl_ldap" {
     "inetOrgPerson",
     "organizationalPerson",
   ]
+
+  lifecycle {
+    # Without this, terraform wants to recreate the resource.
+    ignore_changes = [
+      delete_default_mappers
+    ]
+  }
 }
 
 # keycloak_oidc_identity_provider.github will be destroyed
@@ -29,7 +36,7 @@ resource "keycloak_ldap_user_federation" "tvl_ldap" {
 resource "keycloak_oidc_identity_provider" "github" {
   alias                 = "github"
   provider_id           = "github"
-  client_id             = "6d7f8bb2e82bb6739556"
+  client_id             = "Iv23liXfGNIr7InMg5Uo"
   client_secret         = var.github_client_secret
   realm                 = keycloak_realm.tvl.id
   backchannel_supported = false
diff --git a/ops/kontemplate/util/util.go b/ops/kontemplate/util/util.go
index 56fa1e3fc9c5..f96f5c1d7aa4 100644
--- a/ops/kontemplate/util/util.go
+++ b/ops/kontemplate/util/util.go
@@ -29,13 +29,29 @@ func Merge(in1 *map[string]interface{}, in2 *map[string]interface{}) *map[string
 		return in1
 	}
 
+	// The maps are map[string]interface{} with unknown depth.
+	// Loop over both maps into every level and merge them.
 	new := make(map[string]interface{})
+
 	for k, v := range *in1 {
 		new[k] = v
 	}
 
 	for k, v := range *in2 {
-		new[k] = v
+		if existing, ok := new[k]; ok {
+			// If both values are maps, merge them recursively
+			if existingMap, ok := existing.(map[string]interface{}); ok {
+				if newMap, ok := v.(map[string]interface{}); ok {
+					new[k] = *Merge(&existingMap, &newMap)
+				} else {
+					new[k] = v
+				}
+			} else {
+				new[k] = v
+			}
+		} else {
+			new[k] = v
+		}
 	}
 
 	return &new
diff --git a/ops/kontemplate/util/util_test.go b/ops/kontemplate/util/util_test.go
index 53c56081758c..328add3d250f 100644
--- a/ops/kontemplate/util/util_test.go
+++ b/ops/kontemplate/util/util_test.go
@@ -47,6 +47,9 @@ func TestMergeWithNilMap(t *testing.T) {
 func TestMergeMaps(t *testing.T) {
 	map1 := map[string]interface{}{
 		"foo": "bar",
+		"baz": map[string]interface{}{
+			"qux": "quux",
+		},
 	}
 
 	map2 := map[string]interface{}{
@@ -56,6 +59,9 @@ func TestMergeMaps(t *testing.T) {
 	result := Merge(&map1, &map2)
 	expected := map[string]interface{}{
 		"foo": "bar",
+		"baz": map[string]interface{}{
+			"qux": "quux",
+		},
 		"bar": "baz",
 	}
 
diff --git a/ops/machines/all-systems.nix b/ops/machines/all-systems.nix
index c4382fbddb2a..5df09fa0bd30 100644
--- a/ops/machines/all-systems.nix
+++ b/ops/machines/all-systems.nix
@@ -3,6 +3,8 @@
 (with depot.ops.machines; [
   sanduny
   whitby
+  nixery-01
+  volgasprint-cache
 ]) ++
 
 (with depot.users.tazjin.nixos; [
diff --git a/ops/machines/volgasprint-cache/default.nix b/ops/machines/volgasprint-cache/default.nix
new file mode 100644
index 000000000000..88f2f2863dde
--- /dev/null
+++ b/ops/machines/volgasprint-cache/default.nix
@@ -0,0 +1,153 @@
+# temporary machine for local binary cache proxy during VolgaSprint
+
+{ depot, lib, pkgs, ... }: # readTree options
+{ config, ... }: # passed by module system
+
+let
+  mod = name: depot.path.origSrc + ("/ops/modules/" + name);
+in
+{
+  imports = [
+    (mod "tvl-users.nix")
+  ];
+
+  boot = {
+    kernelPackages = pkgs.linuxKernel.packages.linux_rpi4;
+    initrd.availableKernelModules = [ "xhci_pci" "usbhid" "usb_storage" ];
+    loader = {
+      grub.enable = false;
+      generic-extlinux-compatible.enable = true;
+    };
+  };
+
+  depot.auto-deploy = {
+    enable = true;
+    interval = "hourly";
+  };
+
+  fileSystems = {
+    "/" = {
+      device = "/dev/disk/by-label/NIXOS_SD";
+      fsType = "ext4";
+      options = [ "noatime" ];
+    };
+    "/var/public-nix-cache" = {
+      device = "/dev/sda1";
+      fsType = "ext4";
+    };
+  };
+
+  networking = {
+    firewall = {
+      enable = true;
+      allowedTCPPorts = [ 80 443 8098 ];
+    };
+
+    hostName = "volgacache";
+    domain = "volgasprint.org";
+
+    wireless = {
+      enable = true;
+      networks.VolgaSprint.psk = "nixos-unstable";
+      interfaces = [ "wlan0" ];
+    };
+
+    wg-quick.interfaces = {
+      wg0 = {
+        address = [ "10.10.10.2/24" "fd42::1/128" ];
+        dns = [ "1.1.1.1" ];
+        privateKeyFile = "/etc/wireguard_private_key";
+
+        peers = [
+          {
+            publicKey = "2MZzEGJzA3HrwkHf91TaKJEHwCNyVvsTLWoIYHrCxhY=";
+            presharedKeyFile = "/etc/wireguard_preshared_key";
+            allowedIPs = [ "0.0.0.0/0" "::/0" ];
+            endpoint = "195.201.63.240:8098";
+            persistentKeepalive = 15;
+          }
+        ];
+      };
+    };
+  };
+
+  services.openssh.enable = true;
+
+  services.nginx = {
+    enable = true;
+    recommendedGzipSettings = true;
+    recommendedOptimisation = true;
+
+    appendHttpConfig = ''
+      proxy_cache_path /tmp/pkgcache levels=1:2 keys_zone=cachecache:100m max_size=20g inactive=365d use_temp_path=off;
+
+      # Cache only success status codes; in particular we don't want to cache 404s.
+      # See https://serverfault.com/a/690258/128321
+      map $status $cache_header {
+      200     "public";
+      302     "public";
+      default "no-cache";
+      }
+      access_log /var/log/nginx/access.log;
+    '';
+
+    virtualHosts."cache.volgasprint.org" = {
+      sslCertificate = "/etc/ssl/cache.volgasprint.org/key.pem";
+      sslCertificateKey = "/etc/ssl/cache.volgasprint.org/key.pem";
+      sslTrustedCertificate = "/etc/ssl/cache.volgasprint.org/chain.pem";
+
+      locations."/" = {
+        root = "/var/public-nix-cache";
+        extraConfig = ''
+          expires max;
+          add_header Cache-Control $cache_header always;
+          # Ask the upstream server if a file isn't available locally
+          error_page 404 = @fallback;
+        '';
+      };
+
+      extraConfig = ''
+        # Using a variable for the upstream endpoint to ensure that it is
+        # resolved at runtime as opposed to once when the config file is loaded
+        # and then cached forever (we don't want that):
+        # see https://tenzer.dk/nginx-with-dynamic-upstreams/
+        # This fixes errors like
+        #   nginx: [emerg] host not found in upstream "upstream.example.com"
+        # when the upstream host is not reachable for a short time when
+        # nginx is started.
+        resolver 80.67.169.12; # fdn dns
+        set $upstream_endpoint http://cache.nixos.org;
+      '';
+
+      locations."@fallback" = {
+        proxyPass = "$upstream_endpoint";
+        extraConfig = ''
+          proxy_cache cachecache;
+          proxy_cache_valid  200 302  60d;
+          expires max;
+          add_header Cache-Control $cache_header always;
+        '';
+      };
+
+      # We always want to copy cache.nixos.org's nix-cache-info file,
+      # and ignore our own, because `nix-push` by default generates one
+      # without `Priority` field, and thus that file by default has priority
+      # 50 (compared to cache.nixos.org's `Priority: 40`), which will make
+      # download clients prefer `cache.nixos.org` over our binary cache.
+      locations."= /nix-cache-info" = {
+        # Note: This is duplicated with the `@fallback` above,
+        # would be nicer if we could redirect to the @fallback instead.
+        proxyPass = "$upstream_endpoint";
+        extraConfig = ''
+          proxy_cache cachecache;
+          proxy_cache_valid  200 302  60d;
+          expires max;
+          add_header Cache-Control $cache_header always;
+        '';
+      };
+    };
+  };
+
+  hardware.enableRedistributableFirmware = true;
+  system.stateVersion = "23.11";
+}
diff --git a/ops/machines/whitby/default.nix b/ops/machines/whitby/default.nix
index 4c2fd0f2f533..3181ccde451c 100644
--- a/ops/machines/whitby/default.nix
+++ b/ops/machines/whitby/default.nix
@@ -11,6 +11,7 @@ in
   imports = [
     (mod "atward.nix")
     (mod "cgit.nix")
+    (mod "cheddar.nix")
     (mod "clbot.nix")
     (mod "gerrit-autosubmit.nix")
     (mod "irccat.nix")
@@ -24,7 +25,6 @@ in
     (mod "paroxysm.nix")
     (mod "restic.nix")
     (mod "smtprelay.nix")
-    (mod "sourcegraph.nix")
     (mod "teleirc.nix")
     (mod "tvl-buildkite.nix")
     (mod "tvl-slapd/default.nix")
@@ -306,6 +306,9 @@ in
     agentCount = 32;
   };
 
+  # Run Markdown/code renderer
+  services.depot.cheddar.enable = true;
+
   # Start a local SMTP relay to Gmail (used by gerrit)
   services.depot.smtprelay = {
     enable = true;
@@ -374,9 +377,6 @@ in
   };
 
   services.depot = {
-    # Run a SourceGraph code search instance
-    sourcegraph.enable = true;
-
     # Run a livegrep code search instance
     livegrep.enable = true;
 
diff --git a/ops/modules/cheddar.nix b/ops/modules/cheddar.nix
new file mode 100644
index 000000000000..8c3036978988
--- /dev/null
+++ b/ops/modules/cheddar.nix
@@ -0,0 +1,29 @@
+{ depot, config, pkgs, lib, ... }:
+
+let
+  cfg = config.services.depot.cheddar;
+  description = "cheddar - markdown/highlighting server";
+in
+{
+  options.services.depot.cheddar = with lib; {
+    enable = mkEnableOption description;
+    port = mkOption {
+      description = "Port on which cheddar should listen";
+      type = types.int;
+      default = 4238;
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.cheddar-server = {
+      inherit description;
+      wantedBy = [ "multi-user.target" ];
+      script = "${depot.tools.cheddar}/bin/cheddar --listen 0.0.0.0:${toString cfg.port} --sourcegraph-server";
+
+      serviceConfig = {
+        DynamicUser = true;
+        Restart = "always";
+      };
+    };
+  };
+}
diff --git a/ops/modules/sourcegraph.nix b/ops/modules/sourcegraph.nix
deleted file mode 100644
index cbf836ab64d5..000000000000
--- a/ops/modules/sourcegraph.nix
+++ /dev/null
@@ -1,60 +0,0 @@
-# Run sourcegraph, including its entire machinery, in a container.
-# Running it outside of a container is a futile endeavour for now.
-{ depot, config, pkgs, lib, ... }:
-
-let
-  cfg = config.services.depot.sourcegraph;
-in
-{
-  options.services.depot.sourcegraph = with lib; {
-    enable = mkEnableOption "SourceGraph code search engine";
-
-    port = mkOption {
-      description = "Port on which SourceGraph should listen";
-      type = types.int;
-      default = 3463;
-    };
-
-    cheddarPort = mkOption {
-      description = "Port on which cheddar should listen";
-      type = types.int;
-      default = 4238;
-    };
-  };
-
-  config = lib.mkIf cfg.enable {
-    # Run a cheddar syntax highlighting server
-    systemd.services.cheddar-server = {
-      wantedBy = [ "multi-user.target" ];
-      script = "${depot.tools.cheddar}/bin/cheddar --listen 0.0.0.0:${toString cfg.cheddarPort} --sourcegraph-server";
-
-      serviceConfig = {
-        DynamicUser = true;
-        Restart = "always";
-      };
-    };
-
-    virtualisation.oci-containers.containers.sourcegraph = {
-      image = "sourcegraph/server:3.40.0";
-
-      ports = [
-        "127.0.0.1:${toString cfg.port}:7080"
-      ];
-
-      volumes = [
-        "/var/lib/sourcegraph/etc:/etc/sourcegraph"
-        "/var/lib/sourcegraph/data:/var/opt/sourcegraph"
-      ];
-
-      # TODO(tazjin): Figure out what changed in the protocol.
-      # environment.SRC_SYNTECT_SERVER = "http://172.17.0.1:${toString cfg.cheddarPort}";
-
-      # Sourcegraph needs a higher nofile limit, it logs warnings
-      # otherwise (unclear whether it actually affects the service).
-      extraOptions = [
-        "--ulimit"
-        "nofile=10000:10000"
-      ];
-    };
-  };
-}
diff --git a/ops/modules/www/cs.tvl.fyi.nix b/ops/modules/www/cs.tvl.fyi.nix
index fac814baf064..9555acf9ac47 100644
--- a/ops/modules/www/cs.tvl.fyi.nix
+++ b/ops/modules/www/cs.tvl.fyi.nix
@@ -1,3 +1,5 @@
+# This configuration redirects from the previous Sourcegraph instance to
+# livegrep/cgit where appropriate.
 { config, ... }:
 
 {
@@ -13,17 +15,50 @@
       forceSSL = true;
 
       extraConfig = ''
-        location = / {
-          return 301 https://cs.tvl.fyi/depot;
+        set $lineno "";
+
+        # depot root
+        location = /depot {
+            return 301 https://code.tvl.fyi/tree/;
         }
 
-        location / {
-          proxy_set_header X-Sg-Auth "Anonymous";
-          proxy_pass http://localhost:${toString config.services.depot.sourcegraph.port};
+        # folder/file on canon
+        location ~ ^/depot/-/(blob|tree)/([^\s]*)$ {
+            set $path $2;
+            if ($args ~ ^L(\d+)(-\d+)?$) {
+                set $lineno "#n$1";
+            }
+
+            return 302 https://code.tvl.fyi/tree/$path$lineno;
+        }
+
+        # folder/file on specific commit
+        location ~ ^/depot@([a-f0-9]+)/-/(blob|tree)/([^\s]*)$ {
+            set $commit $1;
+            set $path $3;
+
+            if ($args ~ ^L(\d+)(-\d+)?$) {
+                set $lineno "#n$1";
+            }
+
+            return 302 https://code.tvl.fyi/tree/$path?id=$commit$lineno;
+        }
+
+        # commit info
+        location ~ ^/depot/-/commit/([a-f0-9]+)$ {
+            set $commit $1;
+            return 302 https://code.tvl.fyi/commit/?id=$commit;
         }
 
-        location /users/Anonymous/settings {
-          return 301 https://cs.tvl.fyi;
+        # search handler
+        # This only redirects to the new search, it doesn't try to parse and
+        # rewrite the query.
+        location /search {
+            return 302 https://grep.tvl.fyi/search;
+        }
+
+        location / {
+            return 404 "TVL code search has moved to grep.tvl.fyi and we could not figure out how to rewrite your query. Sorry!";
         }
       '';
     };
diff --git a/ops/modules/www/tazj.in.nix b/ops/modules/www/tazj.in.nix
deleted file mode 100644
index 47eefca2a622..000000000000
--- a/ops/modules/www/tazj.in.nix
+++ /dev/null
@@ -1,54 +0,0 @@
-# serve tazjin's website & blog
-{ depot, config, lib, pkgs, ... }:
-
-{
-  imports = [
-    ./base.nix
-  ];
-
-  config = {
-    services.nginx.virtualHosts."tazj.in" = {
-      enableACME = true;
-      forceSSL = true;
-      root = depot.users.tazjin.homepage;
-      serverAliases = [ "www.tazj.in" ];
-
-      extraConfig = ''
-        location = /en/rss.xml {
-          return 301 https://tazj.in/feed.atom;
-        }
-
-        ${depot.users.tazjin.blog.oldRedirects}
-        location /blog/ {
-          alias ${depot.users.tazjin.blog.rendered}/;
-
-          if ($request_uri ~ ^/(.*)\.html$) {
-            return 302 /$1;
-          }
-
-          try_files $uri $uri.html $uri/ =404;
-        }
-
-        location = /predlozhnik {
-          return 302 https://predlozhnik.ru;
-        }
-
-        # redirect for easier entry on a TV
-        location = /tv {
-          return 302 https://tazj.in/blobs/play.html;
-        }
-
-        # Temporary place for serving static files.
-        location /blobs/ {
-          alias /var/lib/tazjins-blobs/;
-        }
-      '';
-    };
-
-    services.nginx.virtualHosts."git.tazj.in" = {
-      enableACME = true;
-      forceSSL = true;
-      extraConfig = "return 301 https://code.tvl.fyi$request_uri;";
-    };
-  };
-}
diff --git a/ops/nixos.nix b/ops/nixos.nix
index 1442d89b30c1..c0410046c626 100644
--- a/ops/nixos.nix
+++ b/ops/nixos.nix
@@ -63,5 +63,6 @@ in rec {
   whitbySystem = (nixosFor depot.ops.machines.whitby).system;
   sandunySystem = (nixosFor depot.ops.machines.sanduny).system;
   nixeryDev01System = (nixosFor depot.ops.machines.nixery-01).system;
+  volgaSprintCacheSystem = (nixosFor depot.ops.machines.volgasprint-cache).system;
   meta.ci.targets = [ "sandunySystem" "whitbySystem" "nixeryDev01System" ];
 }
diff --git a/ops/pipelines/static-pipeline.yaml b/ops/pipelines/static-pipeline.yaml
index af4f9d784e60..a7eea2eb97d8 100644
--- a/ops/pipelines/static-pipeline.yaml
+++ b/ops/pipelines/static-pipeline.yaml
@@ -88,10 +88,12 @@ steps:
     continue_on_failure: true
 
   # Exit with success or failure depending on whether any other steps
-  # failed.
+  # failed (but not retried).
   #
   # This information is checked by querying the Buildkite GraphQL API
-  # and fetching the count of failed steps.
+  # and fetching all failed steps, then filtering out the ones that were
+  # retried (retried jobs create new jobs, which would also show up in the
+  # query).
   #
   # This step must be :duck: (yes, really!) because the post-command
   # hook will inspect this name.
@@ -109,8 +111,8 @@ steps:
       readonly FAILED_JOBS=$(curl 'https://graphql.buildkite.com/v1' \
         --silent \
         -H "Authorization: Bearer $(cat ${BUILDKITE_TOKEN_PATH})" \
-        -d "{\"query\": \"query BuildStatusQuery { build(uuid: \\\"$BUILDKITE_BUILD_ID\\\") { jobs(passed: false) { count } } }\"}" | \
-        jq -r '.data.build.jobs.count')
+        -d "{\"query\": \"query BuildStatusQuery { build(uuid: \\\"$BUILDKITE_BUILD_ID\\\") { jobs(passed: false, first: 500 ) { edges { node { ... on JobTypeCommand { retried } } } } } }\"}" | \
+        jq -r '.data.build.jobs.edges | map(select(.node.retried == false)) | length')
 
       echo "$$FAILED_JOBS build jobs failed."
 
diff --git a/ops/secrets/tf-keycloak.age b/ops/secrets/tf-keycloak.age
index 237b9377bd79..7d470a6a304e 100644
--- a/ops/secrets/tf-keycloak.age
+++ b/ops/secrets/tf-keycloak.age
Binary files differdiff --git a/ops/users/default.nix b/ops/users/default.nix
index 7bd36d9df4ad..a911954fd806 100644
--- a/ops/users/default.nix
+++ b/ops/users/default.nix
@@ -22,6 +22,11 @@
     password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$5NEYPJ19nDITK5sGr4bzhQ$Xzpzth6y4w+HGvioHiYgzqFiwMDx0B7HAh+PVbkRuuk";
   }
   {
+    username = "azahi";
+    email = "azat@bahawi.net";
+    password = "{ARGON2}$argon2id$v=19$m=19456,t=2,p=1$BVRzgfs8YIorOTq62B00CQ$5UXHyG/Ivn5TqB7UNgfjYJMxTjun3NDvAStWFom4oas";
+  }
+  {
     username = "chickadee";
     email = "matthewktromp@gmail.com";
     password = "{ARGON2}$argon2id$v=19$m=19456,t=2,p=1$HoZjVdJ90JmTEJf1MMLuDg$5Pa8kpJdFVsIxgoOTDsH0gv6CLumSIkMqYEn5UVfjwU";
@@ -37,11 +42,21 @@
     password = "{ARGON2}$argon2id$v=19$m=65536,t=4,p=1$TxjbMGenhEmkyYLrg5uGhbr60THB86YeRZg5bPdiTJo$k9gbRlAPjmxwdUwzbavvsAVkckgQZ0jS2oTtvZBPysk";
   }
   {
+    username = "domenkozar";
+    email = "domen@cachix.org";
+    password = "{ARGON2}$argon2id$v=19$m=19456,t=2,p=1$c9WgMrTqPJZenOr5+wlnnQ$XOpRZRTkduzP2+NJBxkg2jhffurg7PDla4/RoAyclwI";
+  }
+  {
     username = "edef";
     email = "edef@edef.eu";
     password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$OORx4ERbkgvTmuYCJA8cIw$i5qaBzHkRVw7Tl+wZsTFTDqJwF0vuZqhW3VpknMYMc0";
   }
   {
+    username = "elle";
+    email = "lnajt4@gmail.com";
+    password = "{ARGON2}$argon2id$v=19$m=19456,t=2,p=1$b5Bfq6u+fEKbtpixOl+yPw$nCyTLbSDYsw30ZiSxhJ6N99IIPQAnS2XRNlpEx9os+0";
+  }
+  {
     username = "ericvolp12";
     email = "ericvolp12@gmail.com";
     password = "{SSHA}pSepaQ+/5KBLfJtRR5rfxGU8goAsXgvk";
@@ -112,6 +127,11 @@
     password = "{SSHA}7a85VNhpFElFw+N5xcjgGmt4HnBsaGp4";
   }
   {
+    username = "mrflos";
+    email = "mrflos@yeswiki.pro";
+    password = "{ARGON2}$argon2id$v=19$m=19456,t=2,p=1$/D1y+6n3+0GigG9mCMqK8A$9PseWm3+QATxN/M3Wu4JM+CnIppLD/LbQaVEKLItv9o";
+  }
+  {
     username = "noteed";
     email = "noteed@gmail.com";
     password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$rcLfF9xXysSx5sahVQLiMA$EgRgAVXn8+r2Csa3XgIHIEBf3hX4Y58pOHf2eDaBUnA";
@@ -154,6 +174,11 @@
     password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$wOPEl9D3kSke//oLtbvqrg$j0npwwXgaXQ/emefKUwL59tH8hdmtzbgH2rQzWSmE2Y";
   }
   {
+    username = "yl3dy";
+    email = "aleksandr.kiselyov@gmail.com";
+    password = "{ARGON2}$argon2id$v=19$m=19456,t=2,p=1$vPvOa0/7RzDLuD/icQuIzQ$IVMSI7hh/frihuL11sNRj6Jz8TTn1wZZHjZZGszz3pI";
+  }
+  {
     username = "implr";
     email = "implr@hackerspace.pl";
     password = "{ARGON2}$argon2id$v=19$m=65536,t=2,p=1$SHRFps5sVgyUXYdmqGPw9g$tEx9DwKK1RjWlw52GLwOZ/iHep+QJboaZE83f1pXSwQ";
@@ -259,4 +284,24 @@
     email = "tvix@sinavir.fr";
     password = "{ARGON2}$argon2id$v=19$m=19456,t=2,p=1$5GXvoN/enVljV97yE/Zasg$OrgY9/ge2LoxNm9OOqxh/kKLxoAvU54MbQa9WWiT0jY";
   }
+  {
+    username = "emery";
+    email = "emery@dmz.rs";
+    password = "{ARGON2}$argon2id$v=19$m=19456,t=2,p=1$b2k5UpTJafqM7yjHfVRjBg$zFGy/ZeI9Hb71TUfJwFp7qDKyUl8tdyFDUK1uNBYfUI";
+  }
+  {
+    username = "aziz";
+    email = "abd.aziz89@gmail.com";
+    password = "{ARGON2}$argon2id$v=19$m=19456,t=2,p=1$xTvdtTF+gavMfF8556CiiQ$IshnauhlEr80skpv5s6ueJLkQxlynzBt6oCp3cQrNCY";
+  }
+  {
+    username = "nikiv";
+    email = "nikita@nikiv.dev";
+    password = "{ARGON2}$argon2id$v=19$m=19456,t=2,p=1$79mMAD2XYa5dg7D9ueWMpw$Edf5WODrFpkNDyWaMdLKcgcErFLx4khmPIk8wzmYGUE";
+  }
+  {
+    username = "ein-shved";
+    email = "mestofel13@gmail.com";
+    password = "{ARGON2}$argon2id$v=19$m=19456,t=2,p=1$D4wzfJoyFye48QNdrC66VA$aBJ/ZaL+rTgXoQa/nFdpHap3G/Oju8WlHaWTii95X8E";
+  }
 ]
diff --git a/third_party/chicago95/default.nix b/third_party/chicago95/default.nix
new file mode 100644
index 000000000000..8703ec3548ad
--- /dev/null
+++ b/third_party/chicago95/default.nix
@@ -0,0 +1,47 @@
+# A rendition of everyone's favourite computer theme.
+{ pkgs, ... }:
+
+let
+  # Chicago95 has no GTK-4 theme (because GTK-4 removed important features that
+  # it needs), but there is a project with an approximation.
+  #
+  # This is a bit of a hack, but I inject that project's GTK-4 theme as if it
+  # was a part of Chicago95.
+  #
+  # This other project is GPL-3.0, under which Chicago95 is also distributed.
+  gtk4ProjectSrc = pkgs.fetchFromGitHub {
+    owner = "B00merang-Project";
+    repo = "Windows-95";
+    rev = "055abd7a3608afdcb2ef021732e07020f2b416b2";
+    hash = "sha256:1li6wzyn3y09d188xki1h96pmn4xcx2lklfc4rkiq2y2r22wx7kz";
+  };
+in
+pkgs.stdenvNoCC.mkDerivation {
+  pname = "Chicago95";
+  version = "master";
+
+  src = pkgs.fetchFromGitHub {
+    owner = "grassmunk";
+    repo = "Chicago95";
+    rev = "bdf5cf36a16102aaac297f3de887c601c2b1146f";
+    hash = "sha256:11fsy3bam1rhp1292zflvzmf1432z1p0ncwy3601wl2f8rnvfdfm";
+  };
+
+  # The project has a Makefile, but it's broken in all sorts of ways, so we just
+  # copy the important stuff manually.
+  dontBuild = true;
+  installPhase = ''
+    mkdir -p $out/share/{icons,fonts,themes,sounds,qt5ct/colors}
+
+    cp -r Theme/Chicago95 $out/share/themes
+    cp -r Icons/* $out/share/icons
+    cp -r Cursors/* $out/share/icons
+    cp -r Fonts/* $out/share/fonts
+    cp Extras/Chicago95_qt.conf $out/share/qt5ct/colors
+
+    cp -r ${gtk4ProjectSrc}/gtk-4.0 $out/share/themes/Chicago95
+  '';
+
+  meta.license = pkgs.lib.licenses.gpl3;
+}
+
diff --git a/third_party/gitignoreSource/default.nix b/third_party/gitignoreSource/default.nix
index 150de7c990e4..78a7414ed3ad 100644
--- a/third_party/gitignoreSource/default.nix
+++ b/third_party/gitignoreSource/default.nix
@@ -1,15 +1,7 @@
-{ pkgs, ... }:
+{ depot, lib, ... }:
 
 let
-  gitignoreNix = import
-    (pkgs.fetchFromGitHub {
-      owner = "hercules-ci";
-      repo = "gitignore";
-      rev = "f9e996052b5af4032fe6150bba4a6fe4f7b9d698";
-      sha256 = "0jrh5ghisaqdd0vldbywags20m2cxpkbbk5jjjmwaw0gr8nhsafv";
-    })
-    { inherit (pkgs) lib; };
-
+  gitignoreNix = import depot.third_party.sources."gitignore.nix" { inherit lib; };
 in
 {
   __functor = _: gitignoreNix.gitignoreSource;
diff --git a/third_party/nixpkgs/default.nix b/third_party/nixpkgs/default.nix
index 03dc7b267c98..3ec49ea084d8 100644
--- a/third_party/nixpkgs/default.nix
+++ b/third_party/nixpkgs/default.nix
@@ -58,6 +58,9 @@ let
 
     # the big lis package change breaks everything in //3p/lisp, undo it for now.
     lispPackages = stableNixpkgs.lispPackages;
+
+    # mypaint is broken on stable (2024-09-05)
+    mypaint = stableNixpkgs.mypaint;
   };
 
   # Overlay to expose the nixpkgs commits we are using to other Nix code.
diff --git a/third_party/overlays/patches/cbtemulator-uds.patch b/third_party/overlays/patches/cbtemulator-uds.patch
deleted file mode 100644
index a19255306f88..000000000000
--- a/third_party/overlays/patches/cbtemulator-uds.patch
+++ /dev/null
@@ -1,140 +0,0 @@
-commit 1397e10225d8c6fd079a86fccd58fb5d0f4200bc
-Author: Florian Klink <flokli@flokli.de>
-Date:   Fri Mar 29 10:06:34 2024 +0100
-
-    feat(bigtable/emulator): allow listening on Unix Domain Sockets
-    
-    cbtemulator listening on unix domain sockets is much easier than trying
-    to allocate free TCP ports, especially if many cbtemulators are run at
-    the same time in integration tests.
-    
-    This adds an additional flag, address, which has priority if it's set,
-    rather than host:port.
-    
-    `NewServer` already takes a `laddr string`, so we simply check for it to
-    contain slashes, and if so, listen on unix, rather than TCP.
-
-diff --git a/bigtable/bttest/inmem.go b/bigtable/bttest/inmem.go
-index 556abc2a85..33e4bf2667 100644
---- a/bttest/inmem.go
-+++ b/bttest/inmem.go
-@@ -40,6 +40,7 @@ import (
- 	"math"
- 	"math/rand"
- 	"net"
-+	"os"
- 	"regexp"
- 	"sort"
- 	"strings"
-@@ -106,7 +107,15 @@ type server struct {
- // The Server will be listening for gRPC connections, without TLS,
- // on the provided address. The resolved address is named by the Addr field.
- func NewServer(laddr string, opt ...grpc.ServerOption) (*Server, error) {
--	l, err := net.Listen("tcp", laddr)
-+	var l net.Listener
-+	var err error
-+
-+	// If the address contains slashes, listen on a unix domain socket instead.
-+	if strings.Contains(laddr, "/") {
-+		l, err = net.Listen("unix", laddr)
-+	} else {
-+		l, err = net.Listen("tcp", laddr)
-+	}
- 	if err != nil {
- 		return nil, err
- 	}
-diff --git a/bigtable/cmd/emulator/cbtemulator.go b/bigtable/cmd/emulator/cbtemulator.go
-index 144c09ffb1..deaf69b717 100644
---- a/cmd/emulator/cbtemulator.go
-+++ b/cmd/emulator/cbtemulator.go
-@@ -27,8 +27,9 @@ import (
- )
- 
- var (
--	host = flag.String("host", "localhost", "the address to bind to on the local machine")
--	port = flag.Int("port", 9000, "the port number to bind to on the local machine")
-+	host    = flag.String("host", "localhost", "the address to bind to on the local machine")
-+	port    = flag.Int("port", 9000, "the port number to bind to on the local machine")
-+	address = flag.String("address", "", "address:port number or unix socket path to listen on. Has priority over host/port")
- )
- 
- const (
-@@ -42,7 +43,15 @@ func main() {
- 		grpc.MaxRecvMsgSize(maxMsgSize),
- 		grpc.MaxSendMsgSize(maxMsgSize),
- 	}
--	srv, err := bttest.NewServer(fmt.Sprintf("%s:%d", *host, *port), opts...)
-+
-+	var laddr string
-+	if *address != "" {
-+		laddr = *address
-+	} else {
-+		laddr = fmt.Sprintf("%s:%d", *host, *port)
-+	}
-+
-+	srv, err := bttest.NewServer(laddr, opts...)
- 	if err != nil {
- 		log.Fatalf("failed to start emulator: %v", err)
- 	}
-commit ce16f843d6c93159d86b3807c6d9ff66e43aac67
-Author: Florian Klink <flokli@flokli.de>
-Date:   Fri Mar 29 11:53:15 2024 +0100
-
-    feat(bigtable): clean up unix socket on close
-    
-    Call srv.Close when receiving an interrupt, and delete the unix domain
-    socket in that function.
-
-diff --git a/bigtable/bttest/inmem.go b/bigtable/bttest/inmem.go
-index 33e4bf2667..0dc96024b1 100644
---- a/bttest/inmem.go
-+++ b/bttest/inmem.go
-@@ -148,6 +148,11 @@ func (s *Server) Close() {
- 
- 	s.srv.Stop()
- 	s.l.Close()
-+
-+	// clean up unix socket
-+	if strings.Contains(s.Addr, "/") {
-+		_ = os.Remove(s.Addr)
-+	}
- }
- 
- func (s *server) CreateTable(ctx context.Context, req *btapb.CreateTableRequest) (*btapb.Table, error) {
-diff --git a/bigtable/cmd/emulator/cbtemulator.go b/bigtable/cmd/emulator/cbtemulator.go
-index deaf69b717..5a9e8f7a8c 100644
---- a/cmd/emulator/cbtemulator.go
-+++ b/cmd/emulator/cbtemulator.go
-@@ -18,9 +18,12 @@ cbtemulator launches the in-memory Cloud Bigtable server on the given address.
- package main
- 
- import (
-+	"context"
- 	"flag"
- 	"fmt"
- 	"log"
-+	"os"
-+	"os/signal"
- 
- 	"cloud.google.com/go/bigtable/bttest"
- 	"google.golang.org/grpc"
-@@ -51,11 +54,18 @@ func main() {
- 		laddr = fmt.Sprintf("%s:%d", *host, *port)
- 	}
- 
-+	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
-+	defer stop()
-+
- 	srv, err := bttest.NewServer(laddr, opts...)
- 	if err != nil {
- 		log.Fatalf("failed to start emulator: %v", err)
- 	}
- 
- 	fmt.Printf("Cloud Bigtable emulator running on %s\n", srv.Addr)
--	select {}
-+	select {
-+	case <-ctx.Done():
-+		srv.Close()
-+		stop()
-+	}
- }
diff --git a/third_party/overlays/tvl.nix b/third_party/overlays/tvl.nix
index 8d541f103da2..a7cc68b2d994 100644
--- a/third_party/overlays/tvl.nix
+++ b/third_party/overlays/tvl.nix
@@ -90,13 +90,6 @@ depot.nix.readTree.drvTargets {
     };
   }));
 
-  # https://github.com/googleapis/google-cloud-go/pull/9665
-  cbtemulator = super.cbtemulator.overrideAttrs (old: {
-    patches = old.patches or [ ] ++ [
-      ./patches/cbtemulator-uds.patch
-    ];
-  });
-
   crate2nix = super.crate2nix.overrideAttrs (old: {
     patches = old.patches or [ ] ++ [
       # TODO(Kranzes): Remove in next release.
diff --git a/third_party/radicle-explorer/0001-remove-dependency-on-plausible.patch b/third_party/radicle-explorer/0001-remove-dependency-on-plausible.patch
new file mode 100644
index 000000000000..0f4a6219b298
--- /dev/null
+++ b/third_party/radicle-explorer/0001-remove-dependency-on-plausible.patch
@@ -0,0 +1,78 @@
+From cc4718cbea1bd70de21a2be515a944802246ffc7 Mon Sep 17 00:00:00 2001
+From: Vincent Ambo <mail@tazj.in>
+Date: Sun, 15 Sep 2024 03:08:28 +0300
+Subject: [PATCH] remove dependency on plausible
+
+We don't need spyware, thanks.
+---
+ package-lock.json | 9 ---------
+ package.json      | 1 -
+ src/App.svelte    | 8 --------
+ 3 files changed, 18 deletions(-)
+
+diff --git a/package-lock.json b/package-lock.json
+index d52de6c0..d96e342f 100644
+--- a/package-lock.json
++++ b/package-lock.json
+@@ -29,7 +29,6 @@
+         "marked-katex-extension": "^5.1.1",
+         "marked-linkify-it": "^3.1.11",
+         "md5": "^2.3.0",
+-        "plausible-tracker": "^0.3.9",
+         "svelte": "^4.2.19",
+         "twemoji": "^14.0.2",
+         "zod": "^3.23.8"
+@@ -3697,14 +3696,6 @@
+         "url": "https://github.com/sponsors/jonschlinkert"
+       }
+     },
+-    "node_modules/plausible-tracker": {
+-      "version": "0.3.9",
+-      "resolved": "https://registry.npmjs.org/plausible-tracker/-/plausible-tracker-0.3.9.tgz",
+-      "integrity": "sha512-hMhneYm3GCPyQon88SZrVJx+LlqhM1kZFQbuAgXPoh/Az2YvO1B6bitT9qlhpiTdJlsT5lsr3gPmzoVjb5CDXA==",
+-      "engines": {
+-        "node": ">=10"
+-      }
+-    },
+     "node_modules/playwright": {
+       "version": "1.46.1",
+       "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.1.tgz",
+diff --git a/package.json b/package.json
+index 6d569ad9..61e8d892 100644
+--- a/package.json
++++ b/package.json
+@@ -73,7 +73,6 @@
+     "marked-katex-extension": "^5.1.1",
+     "marked-linkify-it": "^3.1.11",
+     "md5": "^2.3.0",
+-    "plausible-tracker": "^0.3.9",
+     "svelte": "^4.2.19",
+     "twemoji": "^14.0.2",
+     "zod": "^3.23.8"
+diff --git a/src/App.svelte b/src/App.svelte
+index 8161c390..4281ba61 100644
+--- a/src/App.svelte
++++ b/src/App.svelte
+@@ -1,6 +1,4 @@
+ <script lang="ts">
+-  import Plausible from "plausible-tracker";
+-
+   import * as router from "@app/lib/router";
+   import { unreachable } from "@app/lib/utils";
+ 
+@@ -28,12 +26,6 @@
+ 
+   void router.loadFromLocation();
+ 
+-  if (import.meta.env.PROD) {
+-    const plausible = Plausible({ domain: "app.radicle.xyz" });
+-
+-    plausible.enableAutoPageviews();
+-  }
+-
+   $: document.documentElement.setAttribute("data-codefont", $codeFont);
+   $: document.documentElement.setAttribute("data-theme", $theme);
+ </script>
+-- 
+2.46.0
+
diff --git a/third_party/radicle-explorer/default.nix b/third_party/radicle-explorer/default.nix
new file mode 100644
index 000000000000..7bf7dc3798d9
--- /dev/null
+++ b/third_party/radicle-explorer/default.nix
@@ -0,0 +1,66 @@
+# radicle-explorer is the web UI for Radicle.
+#
+# They have an upstream Nix derivation, but it only works with experimental
+# features Nix and is quite messy, so this is a copy of the relevant parts.
+{ lib, pkgs, ... }:
+
+let
+  twemoji-assets = pkgs.fetchFromGitHub {
+    owner = "twitter";
+    repo = "twemoji";
+    rev = "v14.0.2";
+    hash = "sha256-YoOnZ5uVukzi/6bLi22Y8U5TpplPzB7ji42l+/ys5xI=";
+  };
+
+  httpdSrc = pkgs.radicle-httpd.src;
+in
+lib.fix (self: pkgs.buildNpmPackage rec {
+  pname = "radicle-explorer";
+  version = (builtins.fromJSON (builtins.readFile "${src}/package.json")).version;
+
+  # source should be synced with the httpd, which is already in nixpkgs
+  src = pkgs.fetchgit {
+    inherit (httpdSrc) url rev;
+    hash = "sha256:09m13238h6j7g02r6332ihgyyzbjx90pgz14rz29pgv7936h6il8";
+  };
+
+  # This might change during nixpkgs bumps and will need updating. Need to fix
+  # upstream so that there is a normal, callable derivation.
+  npmDepsHash = "sha256:1hbrzfjkfc0q8qk03yi6qb9zqm57h7hnkn7fl0yxkrzbrljaljaz";
+
+  patches = [
+    ./0001-remove-dependency-on-plausible.patch
+  ];
+
+  postPatch = ''
+    patchShebangs --build ./scripts
+    mkdir -p "public/twemoji"
+    cp -t public/twemoji -r -- ${twemoji-assets}/assets/svg/*
+    : >scripts/install-twemoji-assets
+  '';
+  dontConfigure = true;
+  doCheck = false;
+
+  installPhase = ''
+    runHook preInstall
+    mkdir -p "$out"
+    cp -r -t "$out" build/*
+    runHook postInstall
+  '';
+
+  # Override the build-time configuration with other preferred seeds which are
+  # displayed on the landing page.
+  passthru.withPreferredSeeds = seeds:
+    let
+      originalConfig = builtins.fromJSON (builtins.readFile "${src}/config/default.json");
+      config = originalConfig // {
+        preferredSeeds = seeds;
+      };
+      newConfig = pkgs.writeText "local.json" (builtins.toJSON config);
+    in
+    self.overrideAttrs (_: {
+      preBuild = ''
+        cp ${newConfig} config/local.json
+      '';
+    });
+})
diff --git a/third_party/rust-crates/default.nix b/third_party/rust-crates/default.nix
index 697e47cddefc..a473a8346a6b 100644
--- a/third_party/rust-crates/default.nix
+++ b/third_party/rust-crates/default.nix
@@ -292,130 +292,4 @@ depot.nix.readTree.drvTargets rec{
     sha256 = "1kd047p8jv6mhmfzddjvfa2nwkfrb3l1wml6lfm51n1cr06cc9lz";
   };
 
-  libz-sys = buildRustCrate {
-    pname = "libz-sys";
-    version = "1.1.2";
-    sha256 = "1y7v6bkwr4b6yaf951p1ns7mx47b29ziwdd5wziaic14gs1gwq30";
-    buildDependencies = [
-      cc
-      pkg-config
-    ];
-  };
-
-  libgit2-sys = buildRustCrate {
-    pname = "libgit2-sys";
-    version = "0.16.2+1.7.2";
-    sha256 = "0bs446idbmg8s13jvb0ck6qmrskcdn2mp3d4mn9ggxbmiw4ryd3g";
-    dependencies = [
-      libc
-      libz-sys
-    ];
-    libPath = "lib.rs";
-    libName = "libgit2_sys";
-    # TODO: this should be available via `pkgs.defaultCrateOverrides`,
-    # I thought that was included by default?
-    nativeBuildInputs = [ pkg-config ];
-    buildInputs = [ pkgs.zlib pkgs.libgit2 ];
-    buildDependencies = [
-      cc
-      pkg-config
-    ];
-    env.LIBGIT2_NO_VENDOR = "1";
-  };
-
-  matches = buildRustCrate {
-    pname = "matches";
-    version = "0.1.8";
-    sha256 = "03hl636fg6xggy0a26200xs74amk3k9n0908rga2szn68agyz3cv";
-    libPath = "lib.rs";
-  };
-
-  percent-encoding = buildRustCrate {
-    pname = "percent-encoding";
-    version = "2.1.0";
-    sha256 = "0i838f2nr81585ckmfymf8l1x1vdmx6n8xqvli0lgcy60yl2axy3";
-    libPath = "lib.rs";
-  };
-
-  form_urlencoded = buildRustCrate {
-    pname = "form_urlencoded";
-    version = "1.0.1";
-    sha256 = "0rhv2hfrzk2smdh27walkm66zlvccnnwrbd47fmf8jh6m420dhj8";
-    dependencies = [
-      matches
-      percent-encoding
-    ];
-  };
-
-  tinyvec_macros = buildRustCrate {
-    pname = "tinyvec_macros";
-    version = "0.1.0";
-    sha256 = "0aim73hyq5g8b2hs9gjq2sv0xm4xzfbwp5fdyg1frljqzkapq682";
-  };
-
-  tinyvec = buildRustCrate {
-    pname = "tinyvec";
-    version = "1.2.0";
-    sha256 = "1c95nma20kiyrjwfsk7hzd5ir6yy4bm63fmfbfb4dm9ahnlvdp3y";
-    features = [ "alloc" ];
-    dependencies = [
-      tinyvec_macros
-    ];
-  };
-
-  unicode-normalization = buildRustCrate {
-    pname = "unicode-normalization";
-    version = "0.1.17";
-    sha256 = "0w4s0avzlf7pzcclhhih93aap613398sshm6jrxcwq0f9lhis11c";
-    dependencies = [
-      tinyvec
-    ];
-  };
-
-  unicode-bidi = buildRustCrate {
-    pname = "unicode-bidi";
-    version = "0.3.5";
-    sha256 = "193jzlxj1dfcms2381lyd45zh4ywlicj9lzcfpid1zbkmfarymkz";
-    dependencies = [
-      matches
-    ];
-  };
-
-  idna = buildRustCrate {
-    pname = "idna";
-    version = "0.2.3";
-    sha256 = "0hwypd0fpym9lmd4bbqpwyr5lhrlvmvzhi1vy9asc5wxwkzrh299";
-    dependencies = [
-      matches
-      unicode-normalization
-      unicode-bidi
-    ];
-  };
-
-  url = buildRustCrate {
-    pname = "url";
-    version = "2.2.1";
-    sha256 = "1ci1djafh83qhpzbmxnr9w5gcrjs3ghf8rrxdy4vklqyji6fvn5v";
-    dependencies = [
-      form_urlencoded
-      idna
-      matches
-      percent-encoding
-    ];
-  };
-
-
-  git2 = buildRustCrate {
-    pname = "git2";
-    edition = "2018";
-    version = "0.18.1";
-    sha256 = "1d1wm8cn37svyxgvzfapwilkkc9d2x7fcrgciwn8b2pv9aqz102k";
-    dependencies = [
-      bitflags
-      libc
-      libgit2-sys
-      log
-      url
-    ];
-  };
 }
diff --git a/third_party/sources/sources.json b/third_party/sources/sources.json
index 14186dc4454e..dda541e780bc 100644
--- a/third_party/sources/sources.json
+++ b/third_party/sources/sources.json
@@ -5,10 +5,22 @@
         "homepage": "https://matrix.to/#/#agenix:nixos.org",
         "owner": "ryantm",
         "repo": "agenix",
-        "rev": "de96bd907d5fbc3b14fc33ad37d1b9a3cb15edc6",
-        "sha256": "0rkcx72bq7fm5b1qy1nrzp8v93nv784wh11srgi3pi0m6sr9g03f",
+        "rev": "f6291c5935fdc4e0bef208cfc0dcab7e3f7a1c41",
+        "sha256": "1x8nd8hvsq6mvzig122vprwigsr3z2skanig65haqswn7z7amsvg",
         "type": "tarball",
-        "url": "https://github.com/ryantm/agenix/archive/de96bd907d5fbc3b14fc33ad37d1b9a3cb15edc6.tar.gz",
+        "url": "https://github.com/ryantm/agenix/archive/f6291c5935fdc4e0bef208cfc0dcab7e3f7a1c41.tar.gz",
+        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
+    },
+    "gitignore.nix": {
+        "branch": "master",
+        "description": "Nix functions for filtering local git sources",
+        "homepage": "",
+        "owner": "hercules-ci",
+        "repo": "gitignore.nix",
+        "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
+        "sha256": "02wxkdpbhlm3yk5mhkhsp3kwakc16xpmsf2baw57nz1dg459qv8w",
+        "type": "tarball",
+        "url": "https://github.com/hercules-ci/gitignore.nix/archive/637db329424fd7e46cf4185293b9cc8c88c95394.tar.gz",
         "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
     },
     "home-manager": {
@@ -17,10 +29,10 @@
         "homepage": "https://nix-community.github.io/home-manager/",
         "owner": "nix-community",
         "repo": "home-manager",
-        "rev": "d0240a064db3987eb4d5204cf2400bc4452d9922",
-        "sha256": "0dnb7ph6v4vh30cxhx223xk23kjwfnfc87hz54pyv5ccb393sifs",
+        "rev": "e5fa72bad0c6f533e8d558182529ee2acc9454fe",
+        "sha256": "0sfnsa4sypbq6390jf0la7x6260cvpmml7d0855m9xfhc1x8y0z1",
         "type": "tarball",
-        "url": "https://github.com/nix-community/home-manager/archive/d0240a064db3987eb4d5204cf2400bc4452d9922.tar.gz",
+        "url": "https://github.com/nix-community/home-manager/archive/e5fa72bad0c6f533e8d558182529ee2acc9454fe.tar.gz",
         "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
     },
     "impermanence": {
@@ -29,10 +41,10 @@
         "homepage": "",
         "owner": "nix-community",
         "repo": "impermanence",
-        "rev": "23c1f06316b67cb5dabdfe2973da3785cfe9c34a",
-        "sha256": "1c99hc2mv0f5rjxj97wcypyrpi5i3xmpi3sd2fnw2481jxgqn5h3",
+        "rev": "63f4d0443e32b0dd7189001ee1894066765d18a5",
+        "sha256": "0xnshgwfg834dm9l14p2w3wmhjysjpqpgfk37im0vrk1qgva19g2",
         "type": "tarball",
-        "url": "https://github.com/nix-community/impermanence/archive/23c1f06316b67cb5dabdfe2973da3785cfe9c34a.tar.gz",
+        "url": "https://github.com/nix-community/impermanence/archive/63f4d0443e32b0dd7189001ee1894066765d18a5.tar.gz",
         "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
     },
     "naersk": {
@@ -65,10 +77,10 @@
         "homepage": "",
         "owner": "NixOS",
         "repo": "nixpkgs",
-        "rev": "b73c2221a46c13557b1b3be9c2070cc42cf01eb3",
-        "sha256": "023rghcidsxfwa8hf3g7bi86cnjj03l9j1mmmvdn6lhb875b9r20",
+        "rev": "574d1eac1c200690e27b8eb4e24887f8df7ac27c",
+        "sha256": "0s6h7r9jin9sd8l85hdjwl3jsvzkddn3blggy78w4f21qa3chymz",
         "type": "tarball",
-        "url": "https://github.com/NixOS/nixpkgs/archive/b73c2221a46c13557b1b3be9c2070cc42cf01eb3.tar.gz",
+        "url": "https://github.com/NixOS/nixpkgs/archive/574d1eac1c200690e27b8eb4e24887f8df7ac27c.tar.gz",
         "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
     },
     "nixpkgs-stable": {
@@ -89,10 +101,10 @@
         "homepage": "",
         "owner": "oxalica",
         "repo": "rust-overlay",
-        "rev": "9803f6e04ca37a2c072783e8297d2080f8d0e739",
-        "sha256": "1b566msx04y4s0hvwsza9gcv4djmni4fa6ik7q2m33b6x4vrb92w",
+        "rev": "c3c175c74cd0e8c2c40a0e22bc6e3005c4d28d64",
+        "sha256": "0hd5p42kg8xh1vv7dxlhncg33jfi8hli8w3m96flqpi50dam6nwr",
         "type": "tarball",
-        "url": "https://github.com/oxalica/rust-overlay/archive/9803f6e04ca37a2c072783e8297d2080f8d0e739.tar.gz",
+        "url": "https://github.com/oxalica/rust-overlay/archive/c3c175c74cd0e8c2c40a0e22bc6e3005c4d28d64.tar.gz",
         "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
     },
     "rustsec-advisory-db": {
@@ -101,10 +113,10 @@
         "homepage": "https://rustsec.org",
         "owner": "RustSec",
         "repo": "advisory-db",
-        "rev": "9d024c07ee8c18609b43436bc865abf46636e250",
-        "sha256": "0fyxx52yfaaniqgihhf5zgapgx3s6fsjdrf0rjm8z028svj121pp",
+        "rev": "7fbf1e630ae52b7b364791a107b5bee5ff929496",
+        "sha256": "05qmglwbffxsv18w6rw638gaf7ynldqcw8pf7k5brrgfnwsca9s2",
         "type": "tarball",
-        "url": "https://github.com/RustSec/advisory-db/archive/9d024c07ee8c18609b43436bc865abf46636e250.tar.gz",
+        "url": "https://github.com/RustSec/advisory-db/archive/7fbf1e630ae52b7b364791a107b5bee5ff929496.tar.gz",
         "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
     }
 }
diff --git a/tools/cheddar/Cargo.lock b/tools/cheddar/Cargo.lock
index 41632ea15927..c88cba580a08 100644
--- a/tools/cheddar/Cargo.lock
+++ b/tools/cheddar/Cargo.lock
@@ -3,10 +3,10 @@
 version = 3
 
 [[package]]
-name = "adler"
-version = "1.0.2"
+name = "adler2"
+version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
 
 [[package]]
 name = "adler32"
@@ -16,9 +16,9 @@ checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
 
 [[package]]
 name = "aho-corasick"
-version = "1.1.2"
+version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
 dependencies = [
  "memchr",
 ]
@@ -64,50 +64,51 @@ dependencies = [
 
 [[package]]
 name = "anstream"
-version = "0.6.4"
+version = "0.6.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
+checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
 dependencies = [
  "anstyle",
  "anstyle-parse",
  "anstyle-query",
  "anstyle-wincon",
  "colorchoice",
+ "is_terminal_polyfill",
  "utf8parse",
 ]
 
 [[package]]
 name = "anstyle"
-version = "1.0.4"
+version = "1.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
+checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
 
 [[package]]
 name = "anstyle-parse"
-version = "0.2.2"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140"
+checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
 dependencies = [
  "utf8parse",
 ]
 
 [[package]]
 name = "anstyle-query"
-version = "1.0.0"
+version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
+checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
 dependencies = [
- "windows-sys",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "anstyle-wincon"
-version = "3.0.1"
+version = "3.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
+checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
 dependencies = [
  "anstyle",
- "windows-sys",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
@@ -129,9 +130,9 @@ dependencies = [
 
 [[package]]
 name = "autocfg"
-version = "1.1.0"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
 
 [[package]]
 name = "base64"
@@ -141,9 +142,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
 
 [[package]]
 name = "base64"
-version = "0.21.5"
+version = "0.22.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
 
 [[package]]
 name = "bincode"
@@ -177,9 +178,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "bitflags"
-version = "2.4.1"
+version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
 
 [[package]]
 name = "block-buffer"
@@ -192,9 +193,9 @@ dependencies = [
 
 [[package]]
 name = "brotli"
-version = "3.4.0"
+version = "3.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f"
+checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391"
 dependencies = [
  "alloc-no-stdlib",
  "alloc-stdlib",
@@ -223,17 +224,23 @@ dependencies = [
 
 [[package]]
 name = "bumpalo"
-version = "3.14.0"
+version = "3.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
 [[package]]
 name = "cc"
-version = "1.0.84"
+version = "1.1.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f8e7c90afad890484a21653d08b6e209ae34770fb5ee298f9c699fcc1e5c856"
+checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6"
 dependencies = [
- "libc",
+ "shlex",
 ]
 
 [[package]]
@@ -258,21 +265,21 @@ dependencies = [
 
 [[package]]
 name = "chrono"
-version = "0.4.31"
+version = "0.4.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
+checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
 dependencies = [
  "android-tzdata",
  "iana-time-zone",
  "num-traits",
- "windows-targets",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
 name = "chunked_transfer"
-version = "1.4.1"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a"
+checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901"
 
 [[package]]
 name = "clap"
@@ -291,9 +298,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.4.8"
+version = "4.5.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64"
+checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -301,22 +308,22 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.4.8"
+version = "4.5.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc"
+checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6"
 dependencies = [
  "anstream",
  "anstyle",
  "clap_lex",
- "strsim 0.10.0",
+ "strsim 0.11.1",
  "terminal_size",
 ]
 
 [[package]]
 name = "clap_derive"
-version = "4.4.7"
+version = "4.5.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
+checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
 dependencies = [
  "heck",
  "proc-macro2",
@@ -326,15 +333,15 @@ dependencies = [
 
 [[package]]
 name = "clap_lex"
-version = "0.6.0"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
+checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
 
 [[package]]
 name = "colorchoice"
-version = "1.0.0"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
 
 [[package]]
 name = "comrak"
@@ -342,7 +349,7 @@ version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c11e55664fcff7f4d37cc2adf3a1996913692f037312f4ab0909047fdd2bf962"
 dependencies = [
- "clap 4.4.8",
+ "clap 4.5.16",
  "entities",
  "memchr",
  "once_cell",
@@ -358,24 +365,24 @@ dependencies = [
 
 [[package]]
 name = "core-foundation-sys"
-version = "0.8.4"
+version = "0.8.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
 
 [[package]]
 name = "cpufeatures"
-version = "0.2.11"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
+checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "crc32fast"
-version = "1.3.2"
+version = "1.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
 dependencies = [
  "cfg-if",
 ]
@@ -402,9 +409,9 @@ dependencies = [
 
 [[package]]
 name = "deranged"
-version = "0.3.9"
+version = "0.3.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
 dependencies = [
  "powerfmt",
 ]
@@ -433,12 +440,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
 
 [[package]]
 name = "errno"
-version = "0.3.6"
+version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e"
+checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
 dependencies = [
  "libc",
- "windows-sys",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
@@ -453,27 +460,27 @@ dependencies = [
 
 [[package]]
 name = "fastrand"
-version = "2.0.1"
+version = "2.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
+checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
 
 [[package]]
 name = "filetime"
-version = "0.2.22"
+version = "0.2.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
+checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
 dependencies = [
  "cfg-if",
  "libc",
- "redox_syscall 0.3.5",
- "windows-sys",
+ "libredox",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
 name = "flate2"
-version = "1.0.28"
+version = "1.0.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
+checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253"
 dependencies = [
  "crc32fast",
  "miniz_oxide",
@@ -487,9 +494,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
 [[package]]
 name = "form_urlencoded"
-version = "1.2.0"
+version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
 dependencies = [
  "percent-encoding",
 ]
@@ -506,9 +513,9 @@ dependencies = [
 
 [[package]]
 name = "getrandom"
-version = "0.2.11"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
+checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
 dependencies = [
  "cfg-if",
  "libc",
@@ -526,15 +533,15 @@ dependencies = [
 
 [[package]]
 name = "hashbrown"
-version = "0.14.2"
+version = "0.14.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
 
 [[package]]
 name = "heck"
-version = "0.4.1"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
 
 [[package]]
 name = "hermit-abi"
@@ -547,15 +554,15 @@ dependencies = [
 
 [[package]]
 name = "hermit-abi"
-version = "0.3.3"
+version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
 
 [[package]]
 name = "httparse"
-version = "1.8.0"
+version = "1.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
 
 [[package]]
 name = "httpdate"
@@ -565,9 +572,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
 
 [[package]]
 name = "iana-time-zone"
-version = "0.1.58"
+version = "0.1.60"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
 dependencies = [
  "android_system_properties",
  "core-foundation-sys",
@@ -588,9 +595,9 @@ dependencies = [
 
 [[package]]
 name = "idna"
-version = "0.4.0"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
 dependencies = [
  "unicode-bidi",
  "unicode-normalization",
@@ -598,48 +605,56 @@ dependencies = [
 
 [[package]]
 name = "indexmap"
-version = "2.1.0"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
+checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
 dependencies = [
  "equivalent",
  "hashbrown",
 ]
 
 [[package]]
+name = "is_terminal_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+
+[[package]]
 name = "itoa"
-version = "1.0.9"
+version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
 
 [[package]]
 name = "js-sys"
-version = "0.3.65"
+version = "0.3.70"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8"
+checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
 dependencies = [
  "wasm-bindgen",
 ]
 
 [[package]]
 name = "lazy_static"
-version = "1.4.0"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
 
 [[package]]
 name = "libc"
-version = "0.2.150"
+version = "0.2.158"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
+checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
 
 [[package]]
-name = "line-wrap"
-version = "0.1.1"
+name = "libredox"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9"
+checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
 dependencies = [
- "safemem",
+ "bitflags 2.6.0",
+ "libc",
+ "redox_syscall",
 ]
 
 [[package]]
@@ -650,21 +665,21 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
 
 [[package]]
 name = "linux-raw-sys"
-version = "0.4.11"
+version = "0.4.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
 
 [[package]]
 name = "log"
-version = "0.4.20"
+version = "0.4.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
 
 [[package]]
 name = "memchr"
-version = "2.6.4"
+version = "2.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
 
 [[package]]
 name = "mime"
@@ -674,9 +689,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
 
 [[package]]
 name = "mime_guess"
-version = "2.0.4"
+version = "2.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
+checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
 dependencies = [
  "mime",
  "unicase",
@@ -684,11 +699,11 @@ dependencies = [
 
 [[package]]
 name = "miniz_oxide"
-version = "0.7.1"
+version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
 dependencies = [
- "adler",
+ "adler2",
 ]
 
 [[package]]
@@ -710,10 +725,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
 name = "num-traits"
-version = "0.2.17"
+version = "0.2.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
 dependencies = [
  "autocfg",
 ]
@@ -724,24 +745,24 @@ version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
 dependencies = [
- "hermit-abi 0.3.3",
+ "hermit-abi 0.3.9",
  "libc",
 ]
 
 [[package]]
 name = "num_threads"
-version = "0.1.6"
+version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
+checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "once_cell"
-version = "1.18.0"
+version = "1.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
 
 [[package]]
 name = "onig"
@@ -767,15 +788,15 @@ dependencies = [
 
 [[package]]
 name = "percent-encoding"
-version = "2.3.0"
+version = "2.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
 
 [[package]]
 name = "pest"
-version = "2.7.5"
+version = "2.7.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5"
+checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95"
 dependencies = [
  "memchr",
  "thiserror",
@@ -784,9 +805,9 @@ dependencies = [
 
 [[package]]
 name = "pest_derive"
-version = "2.7.5"
+version = "2.7.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2"
+checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a"
 dependencies = [
  "pest",
  "pest_generator",
@@ -794,9 +815,9 @@ dependencies = [
 
 [[package]]
 name = "pest_generator"
-version = "2.7.5"
+version = "2.7.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227"
+checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183"
 dependencies = [
  "pest",
  "pest_meta",
@@ -807,9 +828,9 @@ dependencies = [
 
 [[package]]
 name = "pest_meta"
-version = "2.7.5"
+version = "2.7.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6"
+checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f"
 dependencies = [
  "once_cell",
  "pest",
@@ -818,19 +839,18 @@ dependencies = [
 
 [[package]]
 name = "pkg-config"
-version = "0.3.27"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
 
 [[package]]
 name = "plist"
-version = "1.6.0"
+version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef"
+checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016"
 dependencies = [
- "base64 0.21.5",
+ "base64 0.22.1",
  "indexmap",
- "line-wrap",
  "quick-xml",
  "serde",
  "time",
@@ -844,15 +864,18 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
 
 [[package]]
 name = "ppv-lite86"
-version = "0.2.17"
+version = "0.2.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+dependencies = [
+ "zerocopy",
+]
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.69"
+version = "1.0.86"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
 dependencies = [
  "unicode-ident",
 ]
@@ -865,18 +888,18 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
 
 [[package]]
 name = "quick-xml"
-version = "0.31.0"
+version = "0.32.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33"
+checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.33"
+version = "1.0.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
 dependencies = [
  "proc-macro2",
 ]
@@ -913,56 +936,41 @@ dependencies = [
 
 [[package]]
 name = "redox_syscall"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
-dependencies = [
- "bitflags 1.3.2",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.4.1"
+version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.6.0",
 ]
 
 [[package]]
 name = "regex"
-version = "1.10.2"
+version = "1.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
+checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
 dependencies = [
  "aho-corasick",
  "memchr",
  "regex-automata",
- "regex-syntax 0.8.2",
+ "regex-syntax",
 ]
 
 [[package]]
 name = "regex-automata"
-version = "0.4.3"
+version = "0.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
 dependencies = [
  "aho-corasick",
  "memchr",
- "regex-syntax 0.8.2",
+ "regex-syntax",
 ]
 
 [[package]]
 name = "regex-syntax"
-version = "0.7.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
-
-[[package]]
-name = "regex-syntax"
-version = "0.8.2"
+version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
 
 [[package]]
 name = "rouille"
@@ -990,22 +998,22 @@ dependencies = [
 
 [[package]]
 name = "rustix"
-version = "0.38.21"
+version = "0.38.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
+checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f"
 dependencies = [
- "bitflags 2.4.1",
+ "bitflags 2.6.0",
  "errno",
  "libc",
  "linux-raw-sys",
- "windows-sys",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "ryu"
-version = "1.0.15"
+version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
 
 [[package]]
 name = "safemem"
@@ -1024,18 +1032,18 @@ dependencies = [
 
 [[package]]
 name = "serde"
-version = "1.0.192"
+version = "1.0.209"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
+checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.192"
+version = "1.0.209"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
+checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1044,20 +1052,21 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.108"
+version = "1.0.127"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
+checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad"
 dependencies = [
  "itoa",
+ "memchr",
  "ryu",
  "serde",
 ]
 
 [[package]]
 name = "sha1_smol"
-version = "1.0.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
+checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d"
 
 [[package]]
 name = "sha2"
@@ -1077,6 +1086,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
 
 [[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
 name = "strsim"
 version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1084,15 +1099,15 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
 
 [[package]]
 name = "strsim"
-version = "0.10.0"
+version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 
 [[package]]
 name = "syn"
-version = "2.0.39"
+version = "2.0.77"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
+checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1101,9 +1116,9 @@ dependencies = [
 
 [[package]]
 name = "syntect"
-version = "5.1.0"
+version = "5.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e02b4b303bf8d08bfeb0445cba5068a3d306b6baece1d5582171a9bf49188f91"
+checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1"
 dependencies = [
  "bincode",
  "bitflags 1.3.2",
@@ -1113,8 +1128,9 @@ dependencies = [
  "once_cell",
  "onig",
  "plist",
- "regex-syntax 0.7.5",
+ "regex-syntax",
  "serde",
+ "serde_derive",
  "serde_json",
  "thiserror",
  "walkdir",
@@ -1123,15 +1139,15 @@ dependencies = [
 
 [[package]]
 name = "tempfile"
-version = "3.8.1"
+version = "3.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
+checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
 dependencies = [
  "cfg-if",
  "fastrand",
- "redox_syscall 0.4.1",
+ "once_cell",
  "rustix",
- "windows-sys",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -1141,7 +1157,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"
 dependencies = [
  "rustix",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -1155,18 +1171,18 @@ dependencies = [
 
 [[package]]
 name = "thiserror"
-version = "1.0.50"
+version = "1.0.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
+checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.50"
+version = "1.0.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
+checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1184,13 +1200,14 @@ dependencies = [
 
 [[package]]
 name = "time"
-version = "0.3.30"
+version = "0.3.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5"
+checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
 dependencies = [
  "deranged",
  "itoa",
  "libc",
+ "num-conv",
  "num_threads",
  "powerfmt",
  "serde",
@@ -1206,10 +1223,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
 
 [[package]]
 name = "time-macros"
-version = "0.2.15"
+version = "0.2.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20"
+checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
 dependencies = [
+ "num-conv",
  "time-core",
 ]
 
@@ -1227,9 +1245,9 @@ dependencies = [
 
 [[package]]
 name = "tinyvec"
-version = "1.6.0"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
 dependencies = [
  "tinyvec_macros",
 ]
@@ -1278,9 +1296,9 @@ dependencies = [
 
 [[package]]
 name = "unicode-bidi"
-version = "0.3.13"
+version = "0.3.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
+checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
 
 [[package]]
 name = "unicode-ident"
@@ -1290,18 +1308,18 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 
 [[package]]
 name = "unicode-normalization"
-version = "0.1.22"
+version = "0.1.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
+checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
 dependencies = [
  "tinyvec",
 ]
 
 [[package]]
 name = "unicode-width"
-version = "0.1.11"
+version = "0.1.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
+checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
 
 [[package]]
 name = "unicode_categories"
@@ -1311,9 +1329,9 @@ checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
 
 [[package]]
 name = "url"
-version = "2.4.1"
+version = "2.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
+checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
 dependencies = [
  "form_urlencoded",
  "idna",
@@ -1322,9 +1340,9 @@ dependencies = [
 
 [[package]]
 name = "utf8parse"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
 
 [[package]]
 name = "vec_map"
@@ -1334,15 +1352,15 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
 
 [[package]]
 name = "version_check"
-version = "0.9.4"
+version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
 
 [[package]]
 name = "walkdir"
-version = "2.4.0"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
 dependencies = [
  "same-file",
  "winapi-util",
@@ -1356,19 +1374,20 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.88"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce"
+checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
 dependencies = [
  "cfg-if",
+ "once_cell",
  "wasm-bindgen-macro",
 ]
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.88"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217"
+checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
 dependencies = [
  "bumpalo",
  "log",
@@ -1381,9 +1400,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.88"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2"
+checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -1391,9 +1410,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.88"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
+checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1404,9 +1423,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.88"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
+checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
 
 [[package]]
 name = "winapi"
@@ -1426,11 +1445,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
 [[package]]
 name = "winapi-util"
-version = "0.1.6"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
 dependencies = [
- "winapi",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -1441,11 +1460,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
 name = "windows-core"
-version = "0.51.1"
+version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
 dependencies = [
- "windows-targets",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -1454,7 +1473,25 @@ version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
 dependencies = [
- "windows-targets",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -1463,13 +1500,29 @@ version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
 dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
 ]
 
 [[package]]
@@ -1479,42 +1532,90 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
 
 [[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
 name = "windows_aarch64_msvc"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
 
 [[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
 name = "windows_i686_gnu"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
 
 [[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
 name = "windows_i686_msvc"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
 
 [[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
 name = "windows_x86_64_gnu"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
 
 [[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
 
 [[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
 name = "windows_x86_64_msvc"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
 
 [[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
 name = "xdg"
 version = "2.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1528,3 +1629,24 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
 dependencies = [
  "linked-hash-map",
 ]
+
+[[package]]
+name = "zerocopy"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+dependencies = [
+ "byteorder",
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
diff --git a/tools/depot-deps.nix b/tools/depot-deps.nix
index c2f1cd302ce8..5fa1fe23be20 100644
--- a/tools/depot-deps.nix
+++ b/tools/depot-deps.nix
@@ -8,6 +8,7 @@ depot.nix.lazy-deps {
   depotfmt.attr = "tools.depotfmt";
   fetch-depot-inbox.attr = "tools.fetch-depot-inbox";
   git-r.attr = "tools.git-r";
+  git-review.attr = "third_party.nixpkgs.git-review";
   gerrit-update.attr = "tools.gerrit-update";
   gerrit.attr = "tools.gerrit-cli";
   hash-password.attr = "tools.hash-password";
diff --git a/tools/depotfmt.nix b/tools/depotfmt.nix
index e40001018c37..7c45f8be44b4 100644
--- a/tools/depotfmt.nix
+++ b/tools/depotfmt.nix
@@ -1,24 +1,14 @@
 # Builds treefmt for depot, with a hardcoded configuration that
 # includes the right paths to formatters.
-{ depot, pkgs, ... }:
+{ pkgs, ... }:
 
 let
-  # terraform fmt can't handle multiple paths at once, but treefmt
-  # expects this
-  terraformat = pkgs.writeShellScript "terraformat" ''
-    echo "$@" | xargs -n1 ${pkgs.terraform}/bin/terraform fmt
-  '';
-
   config = pkgs.writeText "depot-treefmt-config" ''
     [formatter.go]
-    command = "${depot.nix.buildGo.go}/bin/gofmt"
+    command = "${pkgs.go}/bin/gofmt"
     options = [ "-w" ]
     includes = ["*.go"]
 
-    [formatter.tf]
-    command = "${terraformat}"
-    includes = [ "*.tf" ]
-
     [formatter.nix]
     command = "${pkgs.nixpkgs-fmt}/bin/nixpkgs-fmt"
     includes = [ "*.nix" ]
@@ -31,6 +21,7 @@ let
     options = ["--edition", "2021"]
     includes = [ "*.rs" ]
     excludes = [
+      "users/emery/*",
       "users/tazjin/*",
     ]
   '';
diff --git a/tools/eaglemode/commands/B.nix b/tools/eaglemode/commands/B.nix
new file mode 100644
index 000000000000..bca3d3a87a87
--- /dev/null
+++ b/tools/eaglemode/commands/B.nix
@@ -0,0 +1,23 @@
+{ depot, pkgs, ... }:
+
+let
+  em = depot.tools.eaglemode;
+in
+em.mkCommand {
+  name = "9 B";
+  hotkey = "Ctrl+E";
+  icon = "${./plan9.tga}";
+
+  description = ''
+    Plumb target to Sam or Acme
+  '';
+
+  code = ''
+    ErrorIfNotSingleTarget();
+
+    my @tgt=GetTgt();
+    my $dir=$tgt[0];
+
+    ExecOrError('${pkgs.plan9port}/bin/9', 'B', $dir);
+  '';
+}
diff --git a/tools/eaglemode/commands/emacsclient.nix b/tools/eaglemode/commands/emacsclient.nix
new file mode 100644
index 000000000000..bac3674120ee
--- /dev/null
+++ b/tools/eaglemode/commands/emacsclient.nix
@@ -0,0 +1,26 @@
+{ depot, pkgs, ... }:
+
+let
+  em = depot.tools.eaglemode;
+  icon = em.mkTGA "emacs" "${pkgs.emacs}/share/icons/hicolor/128x128/apps/emacs.png";
+in
+em.mkCommand {
+  name = "Emacsclient";
+  hotkey = "Ctrl+E";
+  icon = "${icon}";
+
+  description = ''
+    Open target in Emacsclient.
+
+    Emacs server must be running already for this to have any effect.
+  '';
+
+  code = ''
+    ErrorIfNotSingleTarget();
+
+    my @tgt=GetTgt();
+    my $dir=$tgt[0];
+
+    ExecOrError('${pkgs.emacs}/bin/emacsclient', '-n', $dir);
+  '';
+}
diff --git a/tools/eaglemode/commands/plan9.tga b/tools/eaglemode/commands/plan9.tga
new file mode 100644
index 000000000000..55d002221a9b
--- /dev/null
+++ b/tools/eaglemode/commands/plan9.tga
Binary files differdiff --git a/tools/eaglemode/default.nix b/tools/eaglemode/default.nix
new file mode 100644
index 000000000000..30983f5d5018
--- /dev/null
+++ b/tools/eaglemode/default.nix
@@ -0,0 +1,146 @@
+# Helper functions for extending Eagle Mode with useful stuff.
+#
+# Eagle Mode's customisation usually expects people to copy the entire
+# configuration into their user folder, which we can automate fairly easily
+# using Nix, letting users choose whether to keep upstream config or not.
+{ depot, lib, pkgs, ... }:
+
+let
+  mkDesc = d: lib.concatMapStringsSep "\n"
+    (x: "# Descr =${x}")
+    (builtins.filter (s: s != "") (lib.splitString "\n" d));
+
+  configWrapper = pkgs.runCommand "eaglemode-config-wrapper" { } ''
+    cp ${./wrapper.go} wrapper.go
+    export HOME=$PWD
+    ${pkgs.go}/bin/go build wrapper.go
+    install -Dm755 wrapper $out/bin/wrapper
+  '';
+in
+rec {
+  # mkCommand creates an Eagle Mode command for the file browser.
+  #
+  # Commands are basically little Perl scripts with a command standard library
+  # available. They receive the user's selected target from Eagle Mode.
+  mkCommand = lib.makeOverridable (
+    {
+      # Name of the command.
+      name
+    , # User-facing description, displayed in Eagle Mode UI. Can be multi-line.
+      description
+    , # Verbatim Perl code of the command. Command library is already available.
+      code
+    , # Caption for the UI button (defaults to name).
+      caption ? name
+    , icon ? "terminal.tga"
+    , # TODO: what's a good default?
+      hotkey ? ""
+    , order ? 1.0
+    }: pkgs.writeTextDir "emFileMan/Commands/${name}.pl" (''
+      #!${pkgs.perl}/bin/perl
+      #[[BEGIN PROPERTIES]]
+      # Type = Command
+      # Interpreter = perl
+      # DefaultFor = directory
+      # Caption = ${caption}
+      # Order = ${toString order}
+      # Icon = ${icon}
+    ''
+    + (lib.optionalString (description != "") "${mkDesc description}\n")
+    + (lib.optionalString (hotkey != "") "# Hotkey = ${hotkey}\n")
+    + ''
+      #[[END PROPERTIES]]
+
+      use strict;
+      use warnings;
+      BEGIN { require "$ENV{'EM_DIR'}/res/emFileMan/scripts/cmd-util.pl"; }
+
+      ${if builtins.isString code
+        then code
+        else (if builtins.isPath code
+             then builtins.readFile code
+             else throw "code must be a string (literal code) or path to file")}
+    '')
+  );
+
+  # mkTGA converts the given image to a TGA image.
+  mkTGA = name: path: pkgs.runCommand "${name}.tga" { } ''
+    ${pkgs.imagemagick}/bin/convert ${path} $out
+  '';
+
+  buildPlugin = lib.makeOverridable (
+    { name
+    , src
+    , version
+    , eaglemode ? pkgs.eaglemode
+    , target ? name
+    , extraNativeBuildInputs ? [ ]
+    , extraBuildInputs ? [ ]
+    }:
+    pkgs.stdenv.mkDerivation {
+      pname = "eaglemode-plugin-${name}";
+      inherit src version;
+      # inherit (eaglemode.drvAttrs) dontPatchELF;
+
+      nativeBuildInputs = eaglemode.drvAttrs.nativeBuildInputs ++ extraNativeBuildInputs;
+      buildInputs = eaglemode.drvAttrs.buildInputs ++ extraBuildInputs ++ [ eaglemode ];
+
+      buildPhase = ''
+        runHook preBuild
+
+        # merge eaglemode & plugin folders
+        cp -r ${pkgs.srcOnly eaglemode} merged-src && chmod -R u+rw merged-src
+        cp -r $src/* merged-src && chmod -R u+rw merged-src
+        cd merged-src
+
+        export NIX_LDFLAGS="$NIX_LDFLAGS -lXxf86vm -lXext -lXinerama"
+        perl make.pl build projects=${target} continue=no
+
+        runHook postBuild
+      '';
+
+      installPhase = ''
+        runHook preInstall
+
+        mkdir -p $out/lib
+        cp -r lib/lib${target}.so $out/lib
+
+        if [ -d "$src/etc" ]; then
+          cp -r $src/etc/* $out
+        fi
+
+        runHook postInstall
+      '';
+    }
+  );
+
+  # etcDir creates a directory layout suitable for use in the EM_USER_CONFIG_DIR
+  # environment variable.
+  #
+  # Note that Eagle Mode requires the value of that variable to be mutable at
+  # runtime (it is the same place where it persists all of its user-controlled
+  # state), so the results of this function can not be used directly.
+  etcDir =
+    { eaglemode ? pkgs.eaglemode
+    , extraPaths ? [ ]
+    }: pkgs.runCommand "eaglemode-config" { } ''
+      mkdir $out
+
+      ${
+        lib.concatMapStringsSep "\n" (s: "cp -rT ${s} $out/\nchmod -R u+rw $out/\n") ([ "${eaglemode}/etc"] ++ extraPaths)
+      }
+    '';
+
+  # withConfig creates an Eagle Mode wrapper that runs it with the given
+  # configuration.
+  withConfig = { eaglemode ? pkgs.eaglemode, config }: pkgs.writeShellScriptBin "eaglemode" ''
+    ${configWrapper}/bin/wrapper --em-config "${config}"
+
+    if [ -d "${config}/lib" ]; then
+      export LD_LIBRARY_PATH="${config}/lib:$LD_LIBRARY_PATH"
+      exec ${eaglemode}/bin/eaglemode "$@"
+    fi
+
+    exec ${eaglemode}/bin/eaglemode "$@"
+  '';
+}
diff --git a/tools/eaglemode/plugins/avif/default.nix b/tools/eaglemode/plugins/avif/default.nix
new file mode 100644
index 000000000000..07577c685cc7
--- /dev/null
+++ b/tools/eaglemode/plugins/avif/default.nix
@@ -0,0 +1,10 @@
+{ depot, pkgs, ... }:
+
+depot.tools.eaglemode.buildPlugin {
+  name = "avif";
+  version = "canon";
+  src = ./.;
+  target = "PlAvif";
+  extraBuildInputs = [ pkgs.libavif ];
+  extraNativeBuildInputs = [ pkgs.pkg-config ];
+}
diff --git a/tools/eaglemode/plugins/avif/etc/emCore/FpPlugins/PlAvif.emFpPlugin b/tools/eaglemode/plugins/avif/etc/emCore/FpPlugins/PlAvif.emFpPlugin
new file mode 100644
index 000000000000..61615f9fd391
--- /dev/null
+++ b/tools/eaglemode/plugins/avif/etc/emCore/FpPlugins/PlAvif.emFpPlugin
@@ -0,0 +1,6 @@
+#%rec:emFpPlugin%#
+
+FileTypes = { ".avif" }
+Priority = 1.0
+Library = "PlAvif"
+Function = "PlAvifFpPluginFunc"
diff --git a/tools/eaglemode/plugins/avif/makers/PlAvif.maker.pm b/tools/eaglemode/plugins/avif/makers/PlAvif.maker.pm
new file mode 100644
index 000000000000..00b927805a72
--- /dev/null
+++ b/tools/eaglemode/plugins/avif/makers/PlAvif.maker.pm
@@ -0,0 +1,64 @@
+package PlAvif;
+
+use strict;
+use warnings;
+
+sub GetDependencies
+{
+	return ('emCore');
+}
+
+sub IsEssential
+{
+	return 0;
+}
+
+sub GetFileHandlingrules
+{
+	return ();
+}
+
+sub GetExtraBuildOptions
+{
+	return ();
+}
+
+sub Build
+{
+	shift;
+	my %options=@_;
+
+	my @libAvifFlags=();
+	if ($options{'avif-inc-dir'} eq '' && $options{'avif-lib-dir'} eq '') {
+		@libAvifFlags=split("\n",readpipe(
+			"perl \"".$options{'utils'}."/PkgConfig.pl\" libavif"
+		));
+	}
+	if (!@libAvifFlags) {
+		if ($options{'avif-inc-dir'} ne '') {
+			push(@libAvifFlags, "--inc-search-dir", $options{'avif-inc-dir'});
+		}
+		if ($options{'avif-lib-dir'} ne '') {
+			push(@libAvifFlags, "--lib-search-dir", $options{'avif-lib-dir'});
+		}
+		push(@libAvifFlags, "--link", "avif");
+	}
+
+	system(
+		@{$options{'unicc_call'}},
+		"--math",
+		"--rtti",
+		"--exceptions",
+		"--bin-dir"       , "bin",
+		"--lib-dir"       , "lib",
+		"--obj-dir"       , "obj",
+		"--inc-search-dir", "include",
+		@libAvifFlags,
+		"--link"          , "emCore",
+		"--type"          , "dynlib",
+		"--name"          , "PlAvif",
+		"src/PlAvif.cpp"
+	)==0 or return 0;
+
+	return 1;
+}
diff --git a/tools/eaglemode/plugins/avif/src/PlAvif.cpp b/tools/eaglemode/plugins/avif/src/PlAvif.cpp
new file mode 100644
index 000000000000..e4807bacd49a
--- /dev/null
+++ b/tools/eaglemode/plugins/avif/src/PlAvif.cpp
@@ -0,0 +1,190 @@
+#include <emCore/emFpPlugin.h>
+#include <emCore/emImageFile.h>
+
+#include "avif/avif.h"
+
+class PlAvifImageFileModel : public emImageFileModel
+{
+public:
+
+	static emRef<PlAvifImageFileModel> Acquire(
+		emContext & context, const emString & name, bool common=true
+	);
+
+protected:
+	PlAvifImageFileModel(emContext & context, const emString & name);
+	virtual ~PlAvifImageFileModel();
+	virtual void TryStartLoading();
+	virtual bool TryContinueLoading();
+	virtual void QuitLoading();
+	virtual void TryStartSaving();
+	virtual bool TryContinueSaving();
+	virtual void QuitSaving();
+	virtual emUInt64 CalcMemoryNeed();
+	virtual double CalcFileProgress();
+
+private:
+	struct LoadingState;
+	LoadingState * L = NULL;
+};
+
+
+struct PlAvifImageFileModel::LoadingState {
+	avifRGBImage rgb;
+	avifDecoder * decoder;
+};
+
+
+emRef<PlAvifImageFileModel> PlAvifImageFileModel::Acquire(
+	emContext & context, const emString & name, bool common
+)
+{
+	EM_IMPL_ACQUIRE(PlAvifImageFileModel, context, name, common)
+}
+
+
+PlAvifImageFileModel::PlAvifImageFileModel(
+	emContext & context, const emString & name
+)
+	: emImageFileModel(context, name)
+{
+}
+
+
+PlAvifImageFileModel::~PlAvifImageFileModel()
+{
+	PlAvifImageFileModel::QuitLoading();
+	PlAvifImageFileModel::QuitSaving();
+}
+
+
+void PlAvifImageFileModel::TryStartLoading()
+{
+	avifResult result;
+
+	L = new LoadingState;
+	memset(L, 0, sizeof(LoadingState));
+
+	L->decoder = avifDecoderCreate();
+	if (L->decoder == NULL) {
+		throw emException("failed to create AVIF decoder");
+	}
+
+	result = avifDecoderSetIOFile(L->decoder, GetFilePath());
+	if (result != AVIF_RESULT_OK) {
+		throw emException("%s", avifResultToString(result));
+	}
+
+	result = avifDecoderParse(L->decoder);
+	if (result != AVIF_RESULT_OK) {
+		throw emException("%s", avifResultToString(result));
+	}
+
+	FileFormatInfo = emString::Format(
+		"AVIF %s %ubpc",
+			avifPixelFormatToString(L->decoder->image->yuvFormat),
+			L->decoder->image->depth
+	);
+
+
+	Signal(ChangeSignal);
+}
+
+
+bool PlAvifImageFileModel::TryContinueLoading()
+{
+	avifResult result;
+
+	if (!Image.GetHeight()) {
+		Image.Setup(
+			L->decoder->image->width,
+			L->decoder->image->height,
+			L->decoder->alphaPresent ? 4 : 3
+		);
+	}
+
+	result = avifDecoderNextImage(L->decoder);
+	if (result != AVIF_RESULT_OK) {
+		throw emException("%s", avifResultToString(result));
+	}
+
+	avifRGBImageSetDefaults(&L->rgb, L->decoder->image);
+	L->rgb.format = L->decoder->alphaPresent ?
+		AVIF_RGB_FORMAT_RGBA : AVIF_RGB_FORMAT_RGB;
+	L->rgb.pixels   = Image.GetWritableMap();
+	L->rgb.width    = Image.GetWidth();
+	L->rgb.height   = Image.GetHeight();
+	L->rgb.depth    = 8;
+	L->rgb.rowBytes = Image.GetWidth() * Image.GetChannelCount();
+
+	result = avifImageYUVToRGB(L->decoder->image, &L->rgb);
+	if (result != AVIF_RESULT_OK) {
+		throw emException("%s", avifResultToString(result));
+	}
+
+	Signal(ChangeSignal);
+	return true;
+}
+
+
+void PlAvifImageFileModel::QuitLoading()
+{
+	if (L) {
+		if (L->decoder) avifDecoderDestroy(L->decoder);
+		delete L;
+		L = NULL;
+	}
+}
+
+
+void PlAvifImageFileModel::TryStartSaving()
+{
+	throw emException("PlAvifImageFileModel: Saving not implemented.");
+}
+
+
+bool PlAvifImageFileModel::TryContinueSaving()
+{
+	return false;
+}
+
+
+void PlAvifImageFileModel::QuitSaving()
+{
+}
+
+
+emUInt64 PlAvifImageFileModel::CalcMemoryNeed()
+{
+	return
+		(emUInt64)
+			L->decoder->image->width *
+			L->decoder->image->height *
+			(L->decoder->alphaPresent ? 4 : 3);
+}
+
+
+double PlAvifImageFileModel::CalcFileProgress()
+{
+	return 0.0;
+}
+
+extern "C" {
+	emPanel * PlAvifFpPluginFunc(
+		emPanel::ParentArg parent, const emString & name,
+		const emString & path, emFpPlugin * plugin,
+		emString * errorBuf
+	)
+	{
+		if (plugin->Properties.GetCount()) {
+			*errorBuf="PlAvifFpPlugin: No properties allowed.";
+			return NULL;
+		}
+		return new emImageFilePanel(
+			parent, name,
+			PlAvifImageFileModel::Acquire(
+				parent.GetRootContext(), path
+			)
+		);
+	}
+}
diff --git a/tools/eaglemode/plugins/example.nix b/tools/eaglemode/plugins/example.nix
new file mode 100644
index 000000000000..b361971cc57e
--- /dev/null
+++ b/tools/eaglemode/plugins/example.nix
@@ -0,0 +1,17 @@
+{ depot, pkgs, ... }:
+
+let
+  em = depot.tools.eaglemode;
+  emSrc = with pkgs; srcOnly eaglemode;
+in
+em.buildPlugin {
+  name = "example";
+  version = "canon";
+
+  src = pkgs.runCommand "em-plugin-example-src" { } ''
+    set -ux
+    cp -r ${emSrc}/doc/examples/CppApiExamples/PluginExample $out
+  '';
+
+  target = "PlEx";
+}
diff --git a/tools/eaglemode/plugins/qoi/default.nix b/tools/eaglemode/plugins/qoi/default.nix
new file mode 100644
index 000000000000..8764ac39e88d
--- /dev/null
+++ b/tools/eaglemode/plugins/qoi/default.nix
@@ -0,0 +1,12 @@
+{ depot, pkgs, ... }:
+
+let
+  em = depot.tools.eaglemode;
+  emSrc = pkgs.srcOnly pkgs.em;
+in
+em.buildPlugin {
+  name = "qoi";
+  version = "canon";
+  src = ./.;
+  target = "PlQoi";
+}
diff --git a/tools/eaglemode/plugins/qoi/etc/emCore/FpPlugins/PlQoi.emFpPlugin b/tools/eaglemode/plugins/qoi/etc/emCore/FpPlugins/PlQoi.emFpPlugin
new file mode 100644
index 000000000000..e27f37261d77
--- /dev/null
+++ b/tools/eaglemode/plugins/qoi/etc/emCore/FpPlugins/PlQoi.emFpPlugin
@@ -0,0 +1,6 @@
+#%rec:emFpPlugin%#
+
+FileTypes = { ".qoi" }
+Priority = 1.0
+Library = "PlQoi"
+Function = "PlQoiFpPluginFunc"
diff --git a/tools/eaglemode/plugins/qoi/makers/PlQoi.maker.pm b/tools/eaglemode/plugins/qoi/makers/PlQoi.maker.pm
new file mode 100644
index 000000000000..c68b9bc6324b
--- /dev/null
+++ b/tools/eaglemode/plugins/qoi/makers/PlQoi.maker.pm
@@ -0,0 +1,47 @@
+package PlQoi;
+
+use strict;
+use warnings;
+
+sub GetDependencies
+{
+	return ('emCore');
+}
+
+sub IsEssential
+{
+	return 0;
+}
+
+sub GetFileHandlingrules
+{
+	return ();
+}
+
+sub GetExtraBuildOptions
+{
+	return ();
+}
+
+sub Build
+{
+	shift;
+	my %options=@_;
+
+	system(
+		@{$options{'unicc_call'}},
+		"--math",
+		"--rtti",
+		"--exceptions",
+		"--bin-dir"       , "bin",
+		"--lib-dir"       , "lib",
+		"--obj-dir"       , "obj",
+		"--inc-search-dir", "include",
+		"--link"          , "emCore",
+		"--type"          , "dynlib",
+		"--name"          , "PlQoi",
+		"src/PlQoi.cpp"
+	)==0 or return 0;
+
+	return 1;
+}
diff --git a/tools/eaglemode/plugins/qoi/src/PlQoi.cpp b/tools/eaglemode/plugins/qoi/src/PlQoi.cpp
new file mode 100644
index 000000000000..1455712eff3a
--- /dev/null
+++ b/tools/eaglemode/plugins/qoi/src/PlQoi.cpp
@@ -0,0 +1,273 @@
+#include <emCore/emFpPlugin.h>
+#include <emCore/emImageFile.h>
+
+/*
+QOI Utilities
+
+Copyright (c) 2021, Dominic Szablewski - https://phoboslab.org
+SPDX-License-Identifier: MIT
+*/
+
+#define QOI_OP_INDEX  0x00 /* 00xxxxxx */
+#define QOI_OP_DIFF   0x40 /* 01xxxxxx */
+#define QOI_OP_LUMA   0x80 /* 10xxxxxx */
+#define QOI_OP_RUN    0xc0 /* 11xxxxxx */
+#define QOI_OP_RGB    0xfe /* 11111110 */
+#define QOI_OP_RGBA   0xff /* 11111111 */
+
+#define QOI_MASK_2    0xc0 /* 11000000 */
+
+#define QOI_COLOR_HASH(C) (C.GetRed()*3 + C.GetGreen()*5 + C.GetBlue()*7 + C.GetAlpha()*11)
+
+#define QOI_MAGIC \
+	(((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \
+	 ((unsigned int)'i') <<  8 | ((unsigned int)'f'))
+
+#define QOI_HEADER_SIZE 14
+
+static unsigned int qoi_read_32(const unsigned char *bytes, int *p) {
+	unsigned int a = bytes[(*p)++];
+	unsigned int b = bytes[(*p)++];
+	unsigned int c = bytes[(*p)++];
+	unsigned int d = bytes[(*p)++];
+	return a << 24 | b << 16 | c << 8 | d;
+}
+
+
+class PlQoiImageFileModel : public emImageFileModel
+{
+public:
+
+	static emRef<PlQoiImageFileModel> Acquire(
+		emContext & context, const emString & name, bool common=true
+	);
+
+protected:
+	PlQoiImageFileModel(emContext & context, const emString & name);
+	virtual ~PlQoiImageFileModel();
+	virtual void TryStartLoading();
+	virtual bool TryContinueLoading();
+	virtual void QuitLoading();
+	virtual void TryStartSaving();
+	virtual bool TryContinueSaving();
+	virtual void QuitSaving();
+	virtual emUInt64 CalcMemoryNeed();
+	virtual double CalcFileProgress();
+
+private:
+	struct LoadingState;
+	LoadingState * L = NULL;
+};
+
+
+struct PlQoiImageFileModel::LoadingState {
+	FILE * file;
+	unsigned int width, height, channels;
+	size_t file_len;
+};
+
+
+emRef<PlQoiImageFileModel> PlQoiImageFileModel::Acquire(
+	emContext & context, const emString & name, bool common
+)
+{
+	EM_IMPL_ACQUIRE(PlQoiImageFileModel, context, name, common)
+}
+
+
+PlQoiImageFileModel::PlQoiImageFileModel(
+	emContext & context, const emString & name
+)
+	: emImageFileModel(context, name)
+{
+}
+
+
+PlQoiImageFileModel::~PlQoiImageFileModel()
+{
+	PlQoiImageFileModel::QuitLoading();
+	PlQoiImageFileModel::QuitSaving();
+}
+
+
+void PlQoiImageFileModel::TryStartLoading()
+{
+	unsigned char header[QOI_HEADER_SIZE];
+	unsigned int header_magic, colorspace;
+	int pos = 0;
+
+	L = new LoadingState;
+	memset(L, 0, sizeof(LoadingState));
+	L->file = fopen(GetFilePath(),"rb");
+	if (!L->file) throw emException("%s",emGetErrorText(errno).Get());
+
+	if (fread(header, 1, sizeof(header), L->file) != sizeof(header)) {
+			if (ferror(L->file)) {
+				throw emException("%s",emGetErrorText(errno).Get());
+			}
+			else  {
+				throw emException("QOI header not found");
+			}
+	}
+
+	header_magic = qoi_read_32(header, &pos);
+	L->width = qoi_read_32(header, &pos);
+	L->height = qoi_read_32(header, &pos);
+	L->channels = header[pos++];
+	colorspace = header[pos++];
+
+	if (
+		L->width == 0 || L->height == 0 ||
+		L->channels < 3 || L->channels > 4 ||
+		colorspace > 1 ||
+		header_magic != QOI_MAGIC
+	) {
+		throw emException("QOI header not valid");
+	}
+
+	fseek(L->file, 0, SEEK_END);
+	L->file_len = ftell(L->file);
+
+	if (L->file_len <= QOI_HEADER_SIZE || fseek(L->file, 0, SEEK_SET) != 0) {
+		throw emException("QOI data incomplete");
+	}
+
+	FileFormatInfo = "QOI ";
+	FileFormatInfo += (
+		colorspace ? "all channels linear" : "sRGB with linear alpha"
+	);
+
+	Signal(ChangeSignal);
+}
+
+
+bool PlQoiImageFileModel::TryContinueLoading()
+{
+	emArray<unsigned char> data;
+	emColor index[64];
+	emColor px { 0, 0, 0, 255 };
+	int pos = QOI_HEADER_SIZE;
+	int run = 0;
+
+	if (!Image.GetHeight()) {
+		Image.Setup(L->width, L->height, L->channels);
+	}
+
+	data.SetCount(L->file_len);
+	if (fread(data.GetWritable(), 1, L->file_len, L->file) < L->file_len) {
+		if (ferror(L->file)) {
+			throw emException("%s",emGetErrorText(errno).Get());
+		}
+		else  {
+			throw emException("QOI data incomplete");
+		}
+	}
+
+	memset(index, 0, sizeof(index));
+
+	for (int px_y = 0; px_y < L->height; px_y++) {
+		for (int px_x = 0; px_x < L->width; px_x++) {
+			if (run > 0) {
+				run--;
+			} else if (pos < data.GetCount()) {
+				int b1 = data.Get(pos++);
+
+				if (b1 == QOI_OP_RGB) {
+					px.SetRed(   data.Get(pos++));
+					px.SetGreen( data.Get(pos++));
+					px.SetBlue(  data.Get(pos++));
+				} else if (b1 == QOI_OP_RGBA) {
+					px.SetRed(   data.Get(pos++));
+					px.SetGreen( data.Get(pos++));
+					px.SetBlue(  data.Get(pos++));
+					px.SetAlpha( data.Get(pos++));
+				} else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX) {
+					px = index[b1];
+				} else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF) {
+					px.SetRed(
+						  px.GetRed() + ((b1 >> 4) & 0x03) - 2);
+					px.SetGreen(
+						px.GetGreen() + ((b1 >> 2) & 0x03) - 2);
+					px.SetBlue(
+						 px.GetBlue() + ( b1       & 0x03) - 2);
+				} else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA) {
+					int b2 = data.Get(pos++);
+					int vg = (b1 & 0x3f) - 32;
+					px.SetRed(
+						  px.GetRed() + vg - 8 + ((b2 >> 4) & 0x0f));
+					px.SetGreen(
+						px.GetGreen() + vg);
+					px.SetBlue(
+						 px.GetBlue() + vg - 8 + (b2 & 0x0f));
+				} else if ((b1 & QOI_MASK_2) == QOI_OP_RUN) {
+					run = (b1 & 0x3f);
+				}
+				index[QOI_COLOR_HASH(px) % 64] = px;
+			}
+			Image.SetPixel(px_x, px_y, px);
+		}
+	}
+
+	Signal(ChangeSignal);
+	return true;
+}
+
+
+void PlQoiImageFileModel::QuitLoading()
+{
+	if (L) {
+		if (L->file) fclose(L->file);
+		delete L;
+		L = NULL;
+	}
+}
+
+
+void PlQoiImageFileModel::TryStartSaving()
+{
+	throw emException("PlQoiImageFileModel: Saving not implemented.");
+}
+
+
+bool PlQoiImageFileModel::TryContinueSaving()
+{
+	return false;
+}
+
+
+void PlQoiImageFileModel::QuitSaving()
+{
+}
+
+
+emUInt64 PlQoiImageFileModel::CalcMemoryNeed()
+{
+	return
+		(emUInt64)L->width * L->height * L->channels + L->file_len;
+}
+
+
+double PlQoiImageFileModel::CalcFileProgress()
+{
+	return 0.0;
+}
+
+extern "C" {
+	emPanel * PlQoiFpPluginFunc(
+		emPanel::ParentArg parent, const emString & name,
+		const emString & path, emFpPlugin * plugin,
+		emString * errorBuf
+	)
+	{
+		if (plugin->Properties.GetCount()) {
+			*errorBuf="PlQoiFpPlugin: No properties allowed.";
+			return NULL;
+		}
+		return new emImageFilePanel(
+			parent, name,
+			PlQoiImageFileModel::Acquire(
+				parent.GetRootContext(), path
+			)
+		);
+	}
+}
diff --git a/tools/eaglemode/plugins/yatracker/default.nix b/tools/eaglemode/plugins/yatracker/default.nix
new file mode 100644
index 000000000000..3ffc42029a03
--- /dev/null
+++ b/tools/eaglemode/plugins/yatracker/default.nix
@@ -0,0 +1,18 @@
+{ depot, pkgs, ... }:
+
+let
+  em = depot.tools.eaglemode;
+  emSrc = with pkgs; srcOnly eaglemode;
+in
+(em.buildPlugin {
+  name = "yatracker";
+  version = "canon";
+  src = ./.;
+  target = "PlYaTracker";
+}).overrideAttrs (_: {
+  postInstall = ''
+    mkdir -p $out/icons
+    ${pkgs.imagemagick}/bin/convert $src/logo.webp $out/icons/yandex-tracker.tga
+  '';
+})
+
diff --git a/tools/eaglemode/plugins/yatracker/etc/emCore/FpPlugins/PlYaTracker.emFpPlugin b/tools/eaglemode/plugins/yatracker/etc/emCore/FpPlugins/PlYaTracker.emFpPlugin
new file mode 100644
index 000000000000..637878844709
--- /dev/null
+++ b/tools/eaglemode/plugins/yatracker/etc/emCore/FpPlugins/PlYaTracker.emFpPlugin
@@ -0,0 +1,6 @@
+#%rec:emFpPlugin%#
+
+FileTypes = { ".YaTracker" }
+Priority = 1.0
+Library = "PlYaTracker"
+Function = "PlYaTrackerPluginFunc"
diff --git a/tools/eaglemode/plugins/yatracker/logo.webp b/tools/eaglemode/plugins/yatracker/logo.webp
new file mode 100644
index 000000000000..460d57d72c33
--- /dev/null
+++ b/tools/eaglemode/plugins/yatracker/logo.webp
Binary files differdiff --git a/tools/eaglemode/plugins/yatracker/makers/PlYaTracker.maker.pm b/tools/eaglemode/plugins/yatracker/makers/PlYaTracker.maker.pm
new file mode 100644
index 000000000000..ae954260a2d1
--- /dev/null
+++ b/tools/eaglemode/plugins/yatracker/makers/PlYaTracker.maker.pm
@@ -0,0 +1,47 @@
+package PlYaTracker;
+
+use strict;
+use warnings;
+
+sub GetDependencies
+{
+	return ('emCore');
+}
+
+sub IsEssential
+{
+	return 0;
+}
+
+sub GetFileHandlingRules
+{
+	return ();
+}
+
+sub GetExtraBuildOptions
+{
+	return ();
+}
+
+sub Build
+{
+	shift;
+	my %options=@_;
+
+	system(
+		@{$options{'unicc_call'}},
+		"--math",
+		"--rtti",
+		"--exceptions",
+		"--bin-dir"       , "bin",
+		"--lib-dir"       , "lib",
+		"--obj-dir"       , "obj",
+		"--inc-search-dir", "include",
+		"--link"          , "emCore",
+		"--type"          , "dynlib",
+		"--name"          , "PlYaTracker",
+		"src/PlYaTracker/PlYaTracker.cpp"
+	)==0 or return 0;
+
+	return 1;
+}
diff --git a/tools/eaglemode/plugins/yatracker/src/PlYaTracker/PlYaTracker.cpp b/tools/eaglemode/plugins/yatracker/src/PlYaTracker/PlYaTracker.cpp
new file mode 100644
index 000000000000..9bf05a17179f
--- /dev/null
+++ b/tools/eaglemode/plugins/yatracker/src/PlYaTracker/PlYaTracker.cpp
@@ -0,0 +1,58 @@
+#include <emCore/emFilePanel.h>
+#include <emCore/emFpPlugin.h>
+#include <emCore/emRecFileModel.h>
+#include <emCore/emToolkit.h>
+
+class PlYaTrackerConfig final : public emRecFileModel, public emStructRec {
+ public:
+  static emRef<PlYaTrackerConfig> Acquire(emContext& context,
+                                          const emString& name,
+                                          bool common = true);
+
+  virtual const char* GetFormatName() const;
+
+  emStringRec URL;
+  emStringRec Token;
+
+ protected:
+  PlYaTrackerConfig(emContext& context, const emString& name);
+};
+
+emRef<PlYaTrackerConfig> PlYaTrackerConfig::Acquire(emContext& context,
+                                                    const emString& name,
+                                                    bool common) {
+  EM_IMPL_ACQUIRE(PlYaTrackerConfig, context, name, common)
+}
+
+const char* PlYaTrackerConfig::GetFormatName() const { return "PlYaTracker"; }
+
+PlYaTrackerConfig::PlYaTrackerConfig(emContext& context, const emString& name)
+    : emRecFileModel(context, name),
+      emStructRec(),
+      URL(this, "URL"),
+      Token(this, "Token") {
+  PostConstruct(*this);
+}
+
+class PlYaTrackerFilePanel : public emFilePanel {
+ public:
+  PlYaTrackerFilePanel(ParentArg parent, const emString& name,
+                       emRef<PlYaTrackerConfig> config);
+
+ private:
+  emRef<PlYaTrackerConfig> Config;
+};
+
+PlYaTrackerFilePanel::PlYaTrackerFilePanel(ParentArg parent,
+                                           const emString& name,
+                                           emRef<PlYaTrackerConfig> config)
+    : emFilePanel(parent, name, config), Config(config) {}
+
+extern "C" {
+emPanel* PlYaTrackerPluginFunc(emPanel::ParentArg parent, const emString& name,
+                               const emString& path, emFpPlugin* plugin,
+                               emString* errorBuf) {
+  return new PlYaTrackerFilePanel(
+      parent, name, PlYaTrackerConfig::Acquire(parent.GetRootContext(), path));
+}
+}
diff --git a/tools/eaglemode/wrapper.go b/tools/eaglemode/wrapper.go
new file mode 100644
index 000000000000..841642b6d93f
--- /dev/null
+++ b/tools/eaglemode/wrapper.go
@@ -0,0 +1,156 @@
+// Eagle Mode configuration wrapper that recreates the required directory
+// structure for Eagle Mode based on the output of depot.tools.eaglemode.etcDir
+//
+// This will replace *all* symlinks in the Eagle Mode configuration directory,
+// but it will not touch actual files. Missing folders will be created.
+package main
+
+import (
+	"flag"
+	"fmt"
+	"io/fs"
+	"log"
+	"os"
+	"os/user"
+	"path"
+	"path/filepath"
+	"strings"
+)
+
+func configDir() (string, error) {
+	v := os.Getenv("EM_USER_CONFIG_DIR")
+	if v != "" {
+		return v, nil
+	}
+
+	usr, err := user.Current()
+	if err != nil {
+		return "", fmt.Errorf("failed to get current user: %w", err)
+	}
+
+	return path.Join(usr.HomeDir, ".eaglemode"), nil
+}
+
+// cleanupConfig removes *all* existing symlinks in the configuration which do
+// not point into the right Nix store path.
+func cleanupConfig(conf string, dir string) (map[string]bool, error) {
+	// In case of first launch, we might have to create the directory.
+	_ = os.MkdirAll(dir, 0755)
+	c := 0
+
+	currentFiles := map[string]bool{}
+
+	walker := func(p string, d fs.DirEntry, e error) error {
+		if e != nil {
+			return fmt.Errorf("could not walk %s in config directory: %w", p, e)
+		}
+
+		if d.Type()&fs.ModeSymlink != 0 {
+			target, err := os.Readlink(p)
+			if err != nil {
+				return fmt.Errorf("could not read link for %s: %w", p, err)
+			}
+
+			if !strings.HasPrefix(target, conf) {
+				err = os.Remove(p)
+				c++
+				if err != nil {
+					return fmt.Errorf("could not remove stale link %q: %w", p, err)
+				}
+				log.Printf("removed stale symlink %q", p)
+			} else {
+				currentFiles[p] = false
+			}
+		}
+
+		if d.Type().IsRegular() {
+			currentFiles[p] = true
+		}
+
+		return nil
+	}
+
+	err := filepath.WalkDir(dir, walker)
+	if err != nil {
+		return nil, err
+	}
+
+	if c > 0 {
+		log.Printf("removed %v stale symlinks", c)
+	}
+
+	return currentFiles, nil
+}
+
+// linkConfig traverses the given Eagle Mode configuration and links everything
+// to the expected location in the user's configuration directory.
+//
+// If the user placed actual files in the configuration directory at paths that
+// would be overwritten, they will not be touched.
+func linkConfig(conf string, dir string, existing map[string]bool) error {
+	walker := func(p string, d fs.DirEntry, e error) error {
+		if e != nil {
+			return fmt.Errorf("could not walk %s in config directory: %w", p, e)
+		}
+
+		target := path.Join(dir, strings.TrimPrefix(p, conf))
+
+		if d.Type().IsDir() {
+			err := os.MkdirAll(target, 0755)
+			if err != nil {
+				return fmt.Errorf("could not create directory %q: %w", target, err)
+			}
+
+			return nil
+		}
+
+		if shadow, exists := existing[target]; exists {
+			if shadow {
+				log.Printf("WARN: file %q already exists and shadows a file from configuration", target)
+			}
+
+			return nil
+		}
+
+		err := os.Symlink(p, target)
+		if err != nil {
+			return fmt.Errorf("failed to link %q: %w", target, err)
+		}
+
+		return nil
+	}
+
+	return filepath.WalkDir(conf, walker)
+}
+
+func main() {
+	emConfig := flag.String("em-config", "", "path to em-config dir")
+
+	flag.Parse()
+	log.Println("verifying current Eagle Mode configuration")
+
+	if *emConfig == "" {
+		log.Fatalf("Eagle Mode configuration must be given")
+	}
+
+	if !strings.HasPrefix(*emConfig, "/nix/store/") {
+		log.Fatalf("Eagle Mode configuration must be in Nix store")
+	}
+
+	dir, err := configDir()
+	if err != nil {
+		log.Fatalf("could not determine Eagle Mode config dir: %v", err)
+	}
+
+	currentFiles, err := cleanupConfig(*emConfig, dir)
+	if err != nil {
+		log.Fatalf("failed to remove stale symlinks: %v", err)
+	}
+
+	err = linkConfig(*emConfig, dir, currentFiles)
+	if err != nil {
+		log.Fatalf("failed to link new configuration: %v", err)
+	}
+
+	log.Println("Eagle Mode configuration updated")
+}
diff --git a/tvix/Cargo.lock b/tvix/Cargo.lock
index 9bea027e4de3..7f92e885c3d1 100644
--- a/tvix/Cargo.lock
+++ b/tvix/Cargo.lock
@@ -67,47 +67,48 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
 
 [[package]]
 name = "anstream"
-version = "0.6.11"
+version = "0.6.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5"
+checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
 dependencies = [
  "anstyle",
  "anstyle-parse",
  "anstyle-query",
  "anstyle-wincon",
  "colorchoice",
+ "is_terminal_polyfill",
  "utf8parse",
 ]
 
 [[package]]
 name = "anstyle"
-version = "1.0.4"
+version = "1.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
+checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
 
 [[package]]
 name = "anstyle-parse"
-version = "0.2.3"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
+checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
 dependencies = [
  "utf8parse",
 ]
 
 [[package]]
 name = "anstyle-query"
-version = "1.0.2"
+version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
+checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
 dependencies = [
  "windows-sys 0.52.0",
 ]
 
 [[package]]
 name = "anstyle-wincon"
-version = "3.0.2"
+version = "3.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
+checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
 dependencies = [
  "anstyle",
  "windows-sys 0.52.0",
@@ -115,9 +116,9 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.79"
+version = "1.0.86"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
+checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
 
 [[package]]
 name = "arc-swap"
@@ -152,9 +153,9 @@ dependencies = [
 
 [[package]]
 name = "async-compression"
-version = "0.4.9"
+version = "0.4.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e9eabd7a98fe442131a17c316bd9349c43695e49e730c3c8e12cfb5f4da2693"
+checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa"
 dependencies = [
  "bzip2",
  "flate2",
@@ -208,20 +209,22 @@ dependencies = [
 
 [[package]]
 name = "async-process"
-version = "2.1.0"
+version = "2.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "451e3cf68011bd56771c79db04a9e333095ab6349f7e47592b788e9b98720cc8"
+checksum = "a8a07789659a4d385b79b18b9127fc27e1a59e1e89117c78c5ea3b806f016374"
 dependencies = [
  "async-channel",
  "async-io",
  "async-lock 3.3.0",
  "async-signal",
+ "async-task",
  "blocking",
  "cfg-if",
  "event-listener 5.2.0",
  "futures-lite",
  "rustix",
- "windows-sys 0.52.0",
+ "tracing",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -261,7 +264,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -288,7 +291,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -305,46 +308,18 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "axum"
-version = "0.6.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
-dependencies = [
- "async-trait",
- "axum-core 0.3.4",
- "bitflags 1.3.2",
- "bytes",
- "futures-util",
- "http 0.2.11",
- "http-body 0.4.6",
- "hyper 0.14.28",
- "itoa",
- "matchit 0.7.3",
- "memchr",
- "mime",
- "percent-encoding",
- "pin-project-lite",
- "rustversion",
- "serde",
- "sync_wrapper 0.1.2",
- "tower",
- "tower-layer",
- "tower-service",
-]
-
-[[package]]
-name = "axum"
 version = "0.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf"
 dependencies = [
  "async-trait",
- "axum-core 0.4.3",
+ "axum-core",
  "bytes",
  "futures-util",
- "http 1.1.0",
- "http-body 1.0.0",
+ "http",
+ "http-body",
  "http-body-util",
- "hyper 1.4.1",
+ "hyper",
  "hyper-util",
  "itoa",
  "matchit 0.7.3",
@@ -367,23 +342,6 @@ dependencies = [
 
 [[package]]
 name = "axum-core"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c"
-dependencies = [
- "async-trait",
- "bytes",
- "futures-util",
- "http 0.2.11",
- "http-body 0.4.6",
- "mime",
- "rustversion",
- "tower-layer",
- "tower-service",
-]
-
-[[package]]
-name = "axum-core"
 version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3"
@@ -391,8 +349,8 @@ dependencies = [
  "async-trait",
  "bytes",
  "futures-util",
- "http 1.1.0",
- "http-body 1.0.0",
+ "http",
+ "http-body",
  "http-body-util",
  "mime",
  "pin-project-lite",
@@ -439,43 +397,27 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
 [[package]]
 name = "bigtable_rs"
 version = "0.2.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12ccba0acd0a82afb2b1fe89181b0776ff18aef5e355030631fe534c8ae8ec73"
+source = "git+https://github.com/liufuyang/bigtable_rs?rev=1818355a5373a5bc2c84287e3a4e3807154ac8ef#1818355a5373a5bc2c84287e3a4e3807154ac8ef"
 dependencies = [
  "gcp_auth",
- "http 0.2.11",
+ "http",
+ "hyper-util",
  "log",
- "prost 0.12.3",
- "prost-build 0.12.3",
- "prost-types 0.12.3",
+ "prost",
+ "prost-build",
+ "prost-types",
  "prost-wkt",
- "prost-wkt-build",
  "prost-wkt-types",
  "serde",
  "serde_with",
  "thiserror",
  "tokio",
- "tonic 0.11.0",
- "tonic-build 0.11.0",
+ "tonic",
+ "tonic-build",
  "tower",
 ]
 
 [[package]]
-name = "bit-set"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
-dependencies = [
- "bit-vec",
-]
-
-[[package]]
-name = "bit-vec"
-version = "0.6.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
-
-[[package]]
 name = "bitflags"
 version = "1.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -483,21 +425,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "bitflags"
-version = "2.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
-
-[[package]]
-name = "bitmaps"
-version = "3.2.0"
+version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "703642b98a00b3b90513279a8ede3fcfa479c126c5fb46e78f3051522f021403"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
 
 [[package]]
 name = "blake3"
-version = "1.5.0"
+version = "1.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87"
+checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7"
 dependencies = [
  "arrayref",
  "arrayvec",
@@ -505,7 +441,7 @@ dependencies = [
  "cfg-if",
  "constant_time_eq",
  "digest",
- "rayon",
+ "rayon-core",
 ]
 
 [[package]]
@@ -535,12 +471,12 @@ dependencies = [
 
 [[package]]
 name = "bstr"
-version = "1.9.0"
+version = "1.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc"
+checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c"
 dependencies = [
  "memchr",
- "regex-automata 0.4.3",
+ "regex-automata 0.4.7",
  "serde",
 ]
 
@@ -558,9 +494,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
 [[package]]
 name = "bytes"
-version = "1.5.0"
+version = "1.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
+checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
 
 [[package]]
 name = "bzip2"
@@ -601,12 +537,13 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
 
 [[package]]
 name = "cc"
-version = "1.0.83"
+version = "1.1.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6"
 dependencies = [
  "jobserver",
  "libc",
+ "shlex",
 ]
 
 [[package]]
@@ -627,7 +564,7 @@ dependencies = [
  "num-traits",
  "serde",
  "wasm-bindgen",
- "windows-targets 0.52.5",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -659,9 +596,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.4.18"
+version = "4.5.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c"
+checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -669,33 +606,33 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.4.18"
+version = "4.5.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7"
+checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6"
 dependencies = [
  "anstream",
  "anstyle",
  "clap_lex",
- "strsim",
+ "strsim 0.11.1",
 ]
 
 [[package]]
 name = "clap_derive"
-version = "4.4.7"
+version = "4.5.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
+checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
 dependencies = [
- "heck",
+ "heck 0.5.0",
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.76",
 ]
 
 [[package]]
 name = "clap_lex"
-version = "0.6.0"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
+checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
 
 [[package]]
 name = "clipboard-win"
@@ -726,9 +663,9 @@ dependencies = [
 
 [[package]]
 name = "colorchoice"
-version = "1.0.0"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
 
 [[package]]
 name = "concurrent-queue"
@@ -915,7 +852,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -938,8 +875,8 @@ dependencies = [
  "ident_case",
  "proc-macro2",
  "quote",
- "strsim",
- "syn 2.0.48",
+ "strsim 0.10.0",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -950,7 +887,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
 dependencies = [
  "darling_core",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -1070,9 +1007,9 @@ dependencies = [
 
 [[package]]
 name = "ed25519-dalek"
-version = "2.1.0"
+version = "2.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0"
+checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
 dependencies = [
  "curve25519-dalek",
  "ed25519",
@@ -1108,7 +1045,7 @@ checksum = "ba7795da175654fe16979af73f81f26a8ea27638d8d9823d317016888a63dc4c"
 dependencies = [
  "num-traits",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -1305,7 +1242,7 @@ dependencies = [
  "lazy_static",
  "libc",
  "log",
- "mio",
+ "mio 0.8.11",
  "nix 0.24.3",
  "vhost",
  "virtio-queue",
@@ -1382,7 +1319,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -1432,19 +1369,22 @@ dependencies = [
 
 [[package]]
 name = "gcp_auth"
-version = "0.10.0"
+version = "0.12.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de2c71ea685b88a1aa50e9fb66fe0e1cb29d755f58cca41fb8c91ef604d4f4d4"
+checksum = "536c79e79dde296a800738474691e97031769bed9b54e6dd0401b169d35d693d"
 dependencies = [
  "async-trait",
- "base64 0.21.7",
+ "base64 0.22.1",
+ "bytes",
  "chrono",
  "home",
- "hyper 0.14.28",
- "hyper-rustls 0.24.2",
+ "http",
+ "http-body-util",
+ "hyper",
+ "hyper-rustls",
+ "hyper-util",
  "ring",
- "rustls 0.21.12",
- "rustls-pemfile 1.0.4",
+ "rustls-pemfile",
  "serde",
  "serde_json",
  "thiserror",
@@ -1452,7 +1392,6 @@ dependencies = [
  "tracing",
  "tracing-futures",
  "url",
- "which 5.0.0",
 ]
 
 [[package]]
@@ -1521,25 +1460,6 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
 
 [[package]]
 name = "h2"
-version = "0.3.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
-dependencies = [
- "bytes",
- "fnv",
- "futures-core",
- "futures-sink",
- "futures-util",
- "http 0.2.11",
- "indexmap 2.1.0",
- "slab",
- "tokio",
- "tokio-util",
- "tracing",
-]
-
-[[package]]
-name = "h2"
 version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069"
@@ -1549,7 +1469,7 @@ dependencies = [
  "futures-core",
  "futures-sink",
  "futures-util",
- "http 1.1.0",
+ "http",
  "indexmap 2.1.0",
  "slab",
  "tokio",
@@ -1586,10 +1506,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
 
 [[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
 name = "hermit-abi"
-version = "0.3.4"
+version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
 
 [[package]]
 name = "hex"
@@ -1614,17 +1540,6 @@ dependencies = [
 
 [[package]]
 name = "http"
-version = "0.2.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb"
-dependencies = [
- "bytes",
- "fnv",
- "itoa",
-]
-
-[[package]]
-name = "http"
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
@@ -1636,23 +1551,12 @@ dependencies = [
 
 [[package]]
 name = "http-body"
-version = "0.4.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
-dependencies = [
- "bytes",
- "http 0.2.11",
- "pin-project-lite",
-]
-
-[[package]]
-name = "http-body"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
 dependencies = [
  "bytes",
- "http 1.1.0",
+ "http",
 ]
 
 [[package]]
@@ -1663,8 +1567,8 @@ checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d"
 dependencies = [
  "bytes",
  "futures-core",
- "http 1.1.0",
- "http-body 1.0.0",
+ "http",
+ "http-body",
  "pin-project-lite",
 ]
 
@@ -1688,30 +1592,6 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
 
 [[package]]
 name = "hyper"
-version = "0.14.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
-dependencies = [
- "bytes",
- "futures-channel",
- "futures-core",
- "futures-util",
- "h2 0.3.26",
- "http 0.2.11",
- "http-body 0.4.6",
- "httparse",
- "httpdate",
- "itoa",
- "pin-project-lite",
- "socket2",
- "tokio",
- "tower-service",
- "tracing",
- "want",
-]
-
-[[package]]
-name = "hyper"
 version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
@@ -1719,9 +1599,9 @@ dependencies = [
  "bytes",
  "futures-channel",
  "futures-util",
- "h2 0.4.4",
- "http 1.1.0",
- "http-body 1.0.0",
+ "h2",
+ "http",
+ "http-body",
  "httparse",
  "httpdate",
  "itoa",
@@ -1733,56 +1613,29 @@ dependencies = [
 
 [[package]]
 name = "hyper-rustls"
-version = "0.24.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
-dependencies = [
- "futures-util",
- "http 0.2.11",
- "hyper 0.14.28",
- "rustls 0.21.12",
- "rustls-native-certs 0.6.3",
- "tokio",
- "tokio-rustls 0.24.1",
-]
-
-[[package]]
-name = "hyper-rustls"
 version = "0.27.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155"
 dependencies = [
  "futures-util",
- "http 1.1.0",
- "hyper 1.4.1",
+ "http",
+ "hyper",
  "hyper-util",
- "rustls 0.23.7",
- "rustls-native-certs 0.7.0",
+ "rustls",
+ "rustls-native-certs",
  "rustls-pki-types",
  "tokio",
- "tokio-rustls 0.26.0",
+ "tokio-rustls",
  "tower-service",
 ]
 
 [[package]]
 name = "hyper-timeout"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
-dependencies = [
- "hyper 0.14.28",
- "pin-project-lite",
- "tokio",
- "tokio-io-timeout",
-]
-
-[[package]]
-name = "hyper-timeout"
 version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793"
 dependencies = [
- "hyper 1.4.1",
+ "hyper",
  "hyper-util",
  "pin-project-lite",
  "tokio",
@@ -1791,16 +1644,16 @@ dependencies = [
 
 [[package]]
 name = "hyper-util"
-version = "0.1.6"
+version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956"
+checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9"
 dependencies = [
  "bytes",
  "futures-channel",
  "futures-util",
- "http 1.1.0",
- "http-body 1.0.0",
- "hyper 1.4.1",
+ "http",
+ "http-body",
+ "hyper",
  "pin-project-lite",
  "socket2",
  "tokio",
@@ -1849,30 +1702,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "imbl"
-version = "3.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc3be8d8cd36f33a46b1849f31f837c44d9fa87223baee3b4bd96b8f11df81eb"
-dependencies = [
- "bitmaps",
- "imbl-sized-chunks",
- "proptest",
- "rand_core",
- "rand_xoshiro",
- "serde",
- "version_check",
-]
-
-[[package]]
-name = "imbl-sized-chunks"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "144006fb58ed787dcae3f54575ff4349755b00ccc99f4b4873860b654be1ed63"
-dependencies = [
- "bitmaps",
-]
-
-[[package]]
 name = "indexmap"
 version = "1.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1941,6 +1770,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "is_terminal_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+
+[[package]]
 name = "itertools"
 version = "0.10.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1951,18 +1786,18 @@ dependencies = [
 
 [[package]]
 name = "itertools"
-version = "0.11.0"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
 dependencies = [
  "either",
 ]
 
 [[package]]
 name = "itertools"
-version = "0.12.0"
+version = "0.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
 dependencies = [
  "either",
 ]
@@ -1975,9 +1810,9 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
 
 [[package]]
 name = "jobserver"
-version = "0.1.27"
+version = "0.1.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
+checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
 dependencies = [
  "libc",
 ]
@@ -1993,9 +1828,9 @@ dependencies = [
 
 [[package]]
 name = "lazy_static"
-version = "1.4.0"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
 
 [[package]]
 name = "lexical-core"
@@ -2063,9 +1898,9 @@ dependencies = [
 
 [[package]]
 name = "libc"
-version = "0.2.152"
+version = "0.2.158"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
+checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
 
 [[package]]
 name = "libm"
@@ -2089,16 +1924,16 @@ version = "0.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
 dependencies = [
- "bitflags 2.4.2",
+ "bitflags 2.6.0",
  "libc",
  "redox_syscall 0.4.1",
 ]
 
 [[package]]
 name = "linux-raw-sys"
-version = "0.4.13"
+version = "0.4.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
 
 [[package]]
 name = "litrs"
@@ -2137,9 +1972,9 @@ dependencies = [
 
 [[package]]
 name = "lru"
-version = "0.12.3"
+version = "0.12.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc"
+checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904"
 dependencies = [
  "hashbrown 0.14.3",
 ]
@@ -2161,7 +1996,7 @@ version = "0.16.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a200ae03df8c3dce7a963f6eeaac8feb41bf9001cb7e5ab22e3205aec2f0373d"
 dependencies = [
- "bitflags 2.4.2",
+ "bitflags 2.6.0",
  "libc",
  "magic-sys",
  "thiserror",
@@ -2275,6 +2110,18 @@ dependencies = [
 ]
 
 [[package]]
+name = "mio"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "wasi",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
 name = "multimap"
 version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2284,27 +2131,27 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
 name = "nar-bridge"
 version = "0.1.0"
 dependencies = [
- "axum 0.7.5",
+ "axum",
  "bytes",
  "clap",
  "data-encoding",
  "futures",
  "hex-literal",
- "itertools 0.12.0",
+ "itertools 0.12.1",
  "lru",
  "mimalloc",
  "nix-compat",
  "parking_lot 0.12.3",
- "prost 0.13.1",
- "prost-build 0.13.1",
+ "prost",
+ "prost-build",
  "rstest",
  "serde",
  "thiserror",
  "tokio",
  "tokio-listener",
  "tokio-util",
- "tonic 0.12.1",
- "tonic-build 0.12.1",
+ "tonic",
+ "tonic-build",
  "tower",
  "tower-http",
  "tracing",
@@ -2365,7 +2212,7 @@ version = "0.27.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
 dependencies = [
- "bitflags 2.4.2",
+ "bitflags 2.6.0",
  "cfg-if",
  "libc",
 ]
@@ -2374,8 +2221,9 @@ dependencies = [
 name = "nix-compat"
 version = "0.1.0"
 dependencies = [
- "bitflags 2.4.2",
+ "bitflags 2.6.0",
  "bstr",
+ "bytes",
  "criterion",
  "data-encoding",
  "ed25519",
@@ -2386,6 +2234,7 @@ dependencies = [
  "hex-literal",
  "lazy_static",
  "mimalloc",
+ "nix-compat-derive",
  "nom",
  "num-traits",
  "pin-project-lite",
@@ -2394,13 +2243,50 @@ dependencies = [
  "serde",
  "serde_json",
  "sha2",
+ "smol_str",
  "thiserror",
  "tokio",
  "tokio-test",
+ "tracing",
  "zstd",
 ]
 
 [[package]]
+name = "nix-compat-derive"
+version = "0.1.0"
+dependencies = [
+ "hex-literal",
+ "nix-compat",
+ "pretty_assertions",
+ "proc-macro2",
+ "quote",
+ "rstest",
+ "syn 2.0.76",
+ "tokio",
+ "tokio-test",
+]
+
+[[package]]
+name = "nix-compat-derive-tests"
+version = "0.1.0"
+dependencies = [
+ "hex-literal",
+ "nix-compat",
+ "nix-compat-derive",
+ "pretty_assertions",
+ "rstest",
+ "tokio",
+ "tokio-test",
+ "trybuild",
+]
+
+[[package]]
+name = "nohash-hasher"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
+
+[[package]]
 name = "nom"
 version = "7.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2437,9 +2323,9 @@ checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
 
 [[package]]
 name = "num-traits"
-version = "0.2.18"
+version = "0.2.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
 dependencies = [
  "autocfg",
  "libm",
@@ -2472,9 +2358,9 @@ dependencies = [
 
 [[package]]
 name = "object_store"
-version = "0.10.1"
+version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbebfd32c213ba1907fa7a9c9138015a8de2b43e30c5aa45b18f7deb46786ad6"
+checksum = "e6da452820c715ce78221e8202ccc599b4a52f3e1eb3eedb487b680c81a8e3f3"
 dependencies = [
  "async-trait",
  "base64 0.22.1",
@@ -2482,8 +2368,8 @@ dependencies = [
  "chrono",
  "futures",
  "humantime",
- "hyper 1.4.1",
- "itertools 0.12.0",
+ "hyper",
+ "itertools 0.13.0",
  "md-5",
  "parking_lot 0.12.3",
  "percent-encoding",
@@ -2491,7 +2377,7 @@ dependencies = [
  "rand",
  "reqwest",
  "ring",
- "rustls-pemfile 2.1.0",
+ "rustls-pemfile",
  "serde",
  "serde_json",
  "snafu",
@@ -2556,7 +2442,7 @@ checksum = "ad31e9de44ee3538fb9d64fe3376c1362f406162434609e79aea2a41a0af78ab"
 dependencies = [
  "async-trait",
  "bytes",
- "http 1.1.0",
+ "http",
  "opentelemetry 0.24.0",
 ]
 
@@ -2568,14 +2454,14 @@ checksum = "6b925a602ffb916fb7421276b86756027b37ee708f9dce2dbdcc51739f07e727"
 dependencies = [
  "async-trait",
  "futures-core",
- "http 1.1.0",
+ "http",
  "opentelemetry 0.24.0",
  "opentelemetry-proto",
  "opentelemetry_sdk 0.24.1",
- "prost 0.13.1",
+ "prost",
  "thiserror",
  "tokio",
- "tonic 0.12.1",
+ "tonic",
 ]
 
 [[package]]
@@ -2586,8 +2472,8 @@ checksum = "30ee9f20bff9c984511a02f082dc8ede839e4a9bf15cc2487c8d6fea5ad850d9"
 dependencies = [
  "opentelemetry 0.24.0",
  "opentelemetry_sdk 0.24.1",
- "prost 0.13.1",
- "tonic 0.12.1",
+ "prost",
+ "tonic",
 ]
 
 [[package]]
@@ -2723,9 +2609,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
 
 [[package]]
 name = "petgraph"
-version = "0.6.4"
+version = "0.6.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
+checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
 dependencies = [
  "fixedbitset",
  "indexmap 2.1.0",
@@ -2748,14 +2634,14 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.76",
 ]
 
 [[package]]
 name = "pin-project-lite"
-version = "0.2.13"
+version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
+checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
 
 [[package]]
 name = "pin-utils"
@@ -2873,80 +2759,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5"
 dependencies = [
  "proc-macro2",
- "syn 2.0.48",
+ "syn 2.0.76",
 ]
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.76"
+version = "1.0.86"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
 name = "proptest"
-version = "1.4.0"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf"
+checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d"
 dependencies = [
- "bit-set",
- "bit-vec",
- "bitflags 2.4.2",
+ "bitflags 2.6.0",
  "lazy_static",
  "num-traits",
  "rand",
  "rand_chacha",
  "rand_xorshift",
  "regex-syntax 0.8.2",
- "rusty-fork",
  "tempfile",
  "unarray",
 ]
 
 [[package]]
 name = "prost"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a"
-dependencies = [
- "bytes",
- "prost-derive 0.12.3",
-]
-
-[[package]]
-name = "prost"
 version = "0.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e13db3d3fde688c61e2446b4d843bc27a7e8af269a69440c0308021dc92333cc"
 dependencies = [
  "bytes",
- "prost-derive 0.13.1",
-]
-
-[[package]]
-name = "prost-build"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c55e02e35260070b6f716a2423c2ff1c3bb1642ddca6f99e1f26d06268a0e2d2"
-dependencies = [
- "bytes",
- "heck",
- "itertools 0.11.0",
- "log",
- "multimap",
- "once_cell",
- "petgraph",
- "prettyplease",
- "prost 0.12.3",
- "prost-types 0.12.3",
- "pulldown-cmark",
- "pulldown-cmark-to-cmark",
- "regex",
- "syn 2.0.48",
- "tempfile",
- "which 4.4.2",
+ "prost-derive",
 ]
 
 [[package]]
@@ -2956,53 +2805,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5bb182580f71dd070f88d01ce3de9f4da5021db7115d2e1c3605a754153b77c1"
 dependencies = [
  "bytes",
- "heck",
- "itertools 0.12.0",
+ "heck 0.4.1",
+ "itertools 0.10.5",
  "log",
  "multimap",
  "once_cell",
  "petgraph",
  "prettyplease",
- "prost 0.13.1",
- "prost-types 0.13.1",
+ "prost",
+ "prost-types",
+ "pulldown-cmark",
+ "pulldown-cmark-to-cmark",
  "regex",
- "syn 2.0.48",
+ "syn 2.0.76",
  "tempfile",
 ]
 
 [[package]]
 name = "prost-derive"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e"
-dependencies = [
- "anyhow",
- "itertools 0.11.0",
- "proc-macro2",
- "quote",
- "syn 2.0.48",
-]
-
-[[package]]
-name = "prost-derive"
 version = "0.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "18bec9b0adc4eba778b33684b7ba3e7137789434769ee3ce3930463ef904cfca"
 dependencies = [
  "anyhow",
- "itertools 0.12.0",
+ "itertools 0.10.5",
  "proc-macro2",
  "quote",
- "syn 2.0.48",
-]
-
-[[package]]
-name = "prost-types"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e"
-dependencies = [
- "prost 0.12.3",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -3011,18 +2840,18 @@ version = "0.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cee5168b05f49d4b0ca581206eb14a7b22fafd963efe729ac48eb03266e25cc2"
 dependencies = [
- "prost 0.13.1",
+ "prost",
 ]
 
 [[package]]
 name = "prost-wkt"
-version = "0.5.0"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d8ef9c3f0f1dab910d2b7e2c24a8e4322e122eba6d7a1921eeebcebbc046c40"
+checksum = "a8d84e2bee181b04c2bac339f2bfe818c46a99750488cc6728ce4181d5aa8299"
 dependencies = [
  "chrono",
  "inventory",
- "prost 0.12.3",
+ "prost",
  "serde",
  "serde_derive",
  "serde_json",
@@ -3031,27 +2860,27 @@ dependencies = [
 
 [[package]]
 name = "prost-wkt-build"
-version = "0.5.0"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b31cae9a54ca84fee1504740a82eebf2479532905e106f63ca0c3bc8d780321"
+checksum = "8a669d5acbe719010c6f62a64e6d7d88fdedc1fe46e419747949ecb6312e9b14"
 dependencies = [
- "heck",
- "prost 0.12.3",
- "prost-build 0.12.3",
- "prost-types 0.12.3",
+ "heck 0.4.1",
+ "prost",
+ "prost-build",
+ "prost-types",
  "quote",
 ]
 
 [[package]]
 name = "prost-wkt-types"
-version = "0.5.0"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "435be4a8704091b4c5fb1d79799de7f2dbff53af05edf29385237f8cf7ab37ee"
+checksum = "01ef068e9b82e654614b22e6b13699bd545b6c0e2e721736008b00b38aeb4f64"
 dependencies = [
  "chrono",
- "prost 0.12.3",
- "prost-build 0.12.3",
- "prost-types 0.12.3",
+ "prost",
+ "prost-build",
+ "prost-types",
  "prost-wkt",
  "prost-wkt-build",
  "regex",
@@ -3066,7 +2895,7 @@ version = "0.9.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b"
 dependencies = [
- "bitflags 2.4.2",
+ "bitflags 2.6.0",
  "memchr",
  "unicase",
 ]
@@ -3081,16 +2910,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "quick-error"
-version = "1.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
-
-[[package]]
 name = "quick-xml"
-version = "0.31.0"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33"
+checksum = "96a05e2e8efddfa51a84ca47cec303fac86c8541b686d37cac5efc0e094417bc"
 dependencies = [
  "memchr",
  "serde",
@@ -3107,7 +2930,7 @@ dependencies = [
  "quinn-proto",
  "quinn-udp",
  "rustc-hash 1.1.0",
- "rustls 0.23.7",
+ "rustls",
  "thiserror",
  "tokio",
  "tracing",
@@ -3123,7 +2946,7 @@ dependencies = [
  "rand",
  "ring",
  "rustc-hash 1.1.0",
- "rustls 0.23.7",
+ "rustls",
  "slab",
  "thiserror",
  "tinyvec",
@@ -3145,9 +2968,9 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.35"
+version = "1.0.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
 dependencies = [
  "proc-macro2",
 ]
@@ -3202,15 +3025,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "rand_xoshiro"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
-dependencies = [
- "rand_core",
-]
-
-[[package]]
 name = "rayon"
 version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3232,9 +3046,9 @@ dependencies = [
 
 [[package]]
 name = "redb"
-version = "2.1.1"
+version = "2.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6dd20d3cdeb9c7d2366a0b16b93b35b75aec15309fbeb7ce477138c9f68c8c0"
+checksum = "58323dc32ea52a8ae105ff94bc0460c5d906307533ba3401aa63db3cbe491fe5"
 dependencies = [
  "libc",
 ]
@@ -3279,13 +3093,13 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.10.2"
+version = "1.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
+checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
 dependencies = [
  "aho-corasick",
  "memchr",
- "regex-automata 0.4.3",
+ "regex-automata 0.4.7",
  "regex-syntax 0.8.2",
 ]
 
@@ -3300,9 +3114,9 @@ dependencies = [
 
 [[package]]
 name = "regex-automata"
-version = "0.4.3"
+version = "0.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -3329,20 +3143,20 @@ checksum = "e898588f33fdd5b9420719948f9f2a32c922a246964576f71ba7f24f80610fbc"
 
 [[package]]
 name = "reqwest"
-version = "0.12.5"
+version = "0.12.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37"
+checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63"
 dependencies = [
  "base64 0.22.1",
  "bytes",
  "futures-core",
  "futures-util",
- "h2 0.4.4",
- "http 1.1.0",
- "http-body 1.0.0",
+ "h2",
+ "http",
+ "http-body",
  "http-body-util",
- "hyper 1.4.1",
- "hyper-rustls 0.27.2",
+ "hyper",
+ "hyper-rustls",
  "hyper-util",
  "ipnet",
  "js-sys",
@@ -3352,16 +3166,16 @@ dependencies = [
  "percent-encoding",
  "pin-project-lite",
  "quinn",
- "rustls 0.23.7",
- "rustls-native-certs 0.7.0",
- "rustls-pemfile 2.1.0",
+ "rustls",
+ "rustls-native-certs",
+ "rustls-pemfile",
  "rustls-pki-types",
  "serde",
  "serde_json",
  "serde_urlencoded",
  "sync_wrapper 1.0.1",
  "tokio",
- "tokio-rustls 0.26.0",
+ "tokio-rustls",
  "tokio-util",
  "tower-service",
  "url",
@@ -3369,18 +3183,18 @@ dependencies = [
  "wasm-bindgen-futures",
  "wasm-streams",
  "web-sys",
- "winreg",
+ "windows-registry",
 ]
 
 [[package]]
 name = "reqwest-middleware"
-version = "0.3.2"
+version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39346a33ddfe6be00cbc17a34ce996818b97b230b87229f10114693becca1268"
+checksum = "562ceb5a604d3f7c885a792d42c199fd8af239d0a51b2fa6a78aafa092452b04"
 dependencies = [
  "anyhow",
  "async-trait",
- "http 1.1.0",
+ "http",
  "reqwest",
  "serde",
  "thiserror",
@@ -3389,14 +3203,14 @@ dependencies = [
 
 [[package]]
 name = "reqwest-tracing"
-version = "0.5.2"
+version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e45dcad05dc210fdb0278d62a679eb768730af808f8cb552f810da89bdbe76d"
+checksum = "bfdd9bfa64c72233d8dd99ab7883efcdefe9e16d46488ecb9228b71a2e2ceb45"
 dependencies = [
  "anyhow",
  "async-trait",
  "getrandom",
- "http 1.1.0",
+ "http",
  "matchit 0.8.4",
  "opentelemetry 0.22.0",
  "reqwest",
@@ -3466,7 +3280,7 @@ dependencies = [
  "regex",
  "relative-path",
  "rustc_version",
- "syn 2.0.48",
+ "syn 2.0.76",
  "unicode-ident",
 ]
 
@@ -3479,7 +3293,7 @@ dependencies = [
  "quote",
  "rand",
  "rustc_version",
- "syn 2.0.48",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -3511,11 +3325,11 @@ dependencies = [
 
 [[package]]
 name = "rustix"
-version = "0.38.30"
+version = "0.38.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca"
+checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f"
 dependencies = [
- "bitflags 2.4.2",
+ "bitflags 2.6.0",
  "errno",
  "libc",
  "linux-raw-sys",
@@ -3524,32 +3338,6 @@ dependencies = [
 
 [[package]]
 name = "rustls"
-version = "0.21.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
-dependencies = [
- "log",
- "ring",
- "rustls-webpki 0.101.7",
- "sct",
-]
-
-[[package]]
-name = "rustls"
-version = "0.22.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
-dependencies = [
- "log",
- "ring",
- "rustls-pki-types",
- "rustls-webpki 0.102.2",
- "subtle",
- "zeroize",
-]
-
-[[package]]
-name = "rustls"
 version = "0.23.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ebbbdb961df0ad3f2652da8f3fdc4b36122f568f968f45ad3316f26c025c677b"
@@ -3558,31 +3346,19 @@ dependencies = [
  "once_cell",
  "ring",
  "rustls-pki-types",
- "rustls-webpki 0.102.2",
+ "rustls-webpki",
  "subtle",
  "zeroize",
 ]
 
 [[package]]
 name = "rustls-native-certs"
-version = "0.6.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
-dependencies = [
- "openssl-probe",
- "rustls-pemfile 1.0.4",
- "schannel",
- "security-framework",
-]
-
-[[package]]
-name = "rustls-native-certs"
 version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792"
 dependencies = [
  "openssl-probe",
- "rustls-pemfile 2.1.0",
+ "rustls-pemfile",
  "rustls-pki-types",
  "schannel",
  "security-framework",
@@ -3590,15 +3366,6 @@ dependencies = [
 
 [[package]]
 name = "rustls-pemfile"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
-dependencies = [
- "base64 0.21.7",
-]
-
-[[package]]
-name = "rustls-pemfile"
 version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3c333bb734fcdedcea57de1602543590f545f127dc8b533324318fd492c5c70b"
@@ -3615,16 +3382,6 @@ checksum = "5ede67b28608b4c60685c7d54122d4400d90f62b40caee7700e700380a390fa8"
 
 [[package]]
 name = "rustls-webpki"
-version = "0.101.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
-dependencies = [
- "ring",
- "untrusted",
-]
-
-[[package]]
-name = "rustls-webpki"
 version = "0.102.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610"
@@ -3641,18 +3398,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
 
 [[package]]
-name = "rusty-fork"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
-dependencies = [
- "fnv",
- "quick-error",
- "tempfile",
- "wait-timeout",
-]
-
-[[package]]
 name = "rustyline"
 version = "10.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3712,16 +3457,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 
 [[package]]
-name = "sct"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
-dependencies = [
- "ring",
- "untrusted",
-]
-
-[[package]]
 name = "security-framework"
 version = "2.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3752,22 +3487,22 @@ checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
 
 [[package]]
 name = "serde"
-version = "1.0.204"
+version = "1.0.209"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
+checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.204"
+version = "1.0.209"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
+checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -3804,9 +3539,9 @@ dependencies = [
 
 [[package]]
 name = "serde_spanned"
-version = "0.6.6"
+version = "0.6.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
+checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
 dependencies = [
  "serde",
 ]
@@ -3835,11 +3570,11 @@ dependencies = [
 
 [[package]]
 name = "serde_with"
-version = "3.7.0"
+version = "3.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a"
+checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857"
 dependencies = [
- "base64 0.21.7",
+ "base64 0.22.1",
  "chrono",
  "hex",
  "indexmap 1.9.3",
@@ -3853,14 +3588,14 @@ dependencies = [
 
 [[package]]
 name = "serde_with_macros"
-version = "3.7.0"
+version = "3.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655"
+checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350"
 dependencies = [
  "darling",
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -3895,6 +3630,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
 name = "signal-hook-registry"
 version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3945,9 +3686,9 @@ checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
 
 [[package]]
 name = "smol_str"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6845563ada680337a52d43bb0b29f396f2d911616f6573012645b9e3d048a49"
+checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead"
 dependencies = [
  "serde",
 ]
@@ -3968,7 +3709,7 @@ version = "0.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf"
 dependencies = [
- "heck",
+ "heck 0.4.1",
  "proc-macro2",
  "quote",
  "syn 1.0.109",
@@ -4019,6 +3760,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
 
 [[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
 name = "structmeta"
 version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4060,9 +3807,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.48"
+version = "2.0.76"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
+checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -4080,6 +3827,9 @@ name = "sync_wrapper"
 version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
+dependencies = [
+ "futures-core",
+]
 
 [[package]]
 name = "tabwriter"
@@ -4092,15 +3842,15 @@ dependencies = [
 
 [[package]]
 name = "tempfile"
-version = "3.9.0"
+version = "3.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
+checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
 dependencies = [
  "cfg-if",
  "fastrand",
- "redox_syscall 0.4.1",
+ "once_cell",
  "rustix",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -4132,22 +3882,22 @@ checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233"
 
 [[package]]
 name = "thiserror"
-version = "1.0.56"
+version = "1.0.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
+checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.56"
+version = "1.0.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
+checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -4171,9 +3921,9 @@ dependencies = [
 
 [[package]]
 name = "time"
-version = "0.3.34"
+version = "0.3.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
+checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
 dependencies = [
  "deranged",
  "itoa",
@@ -4192,9 +3942,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
 
 [[package]]
 name = "time-macros"
-version = "0.2.17"
+version = "0.2.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774"
+checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
 dependencies = [
  "num-conv",
  "time-core",
@@ -4227,30 +3977,19 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 
 [[package]]
 name = "tokio"
-version = "1.35.1"
+version = "1.39.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104"
+checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5"
 dependencies = [
  "backtrace",
  "bytes",
  "libc",
- "mio",
- "num_cpus",
+ "mio 1.0.2",
  "pin-project-lite",
  "signal-hook-registry",
  "socket2",
  "tokio-macros",
- "windows-sys 0.48.0",
-]
-
-[[package]]
-name = "tokio-io-timeout"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf"
-dependencies = [
- "pin-project-lite",
- "tokio",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
@@ -4259,19 +3998,19 @@ version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "914a439d123292125bc806649c396d23e1aac5da4052f0d97b23137b38782f46"
 dependencies = [
- "axum 0.7.5",
+ "axum",
  "clap",
  "document-features",
  "futures-core",
  "futures-util",
- "hyper 1.4.1",
+ "hyper",
  "hyper-util",
  "nix 0.26.4",
  "pin-project",
  "socket2",
  "tokio",
  "tokio-util",
- "tonic 0.12.1",
+ "tonic",
  "tower",
  "tower-service",
  "tracing",
@@ -4279,13 +4018,13 @@ dependencies = [
 
 [[package]]
 name = "tokio-macros"
-version = "2.2.0"
+version = "2.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
+checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -4301,41 +4040,20 @@ dependencies = [
 
 [[package]]
 name = "tokio-rustls"
-version = "0.24.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
-dependencies = [
- "rustls 0.21.12",
- "tokio",
-]
-
-[[package]]
-name = "tokio-rustls"
-version = "0.25.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
-dependencies = [
- "rustls 0.22.4",
- "rustls-pki-types",
- "tokio",
-]
-
-[[package]]
-name = "tokio-rustls"
 version = "0.26.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
 dependencies = [
- "rustls 0.23.7",
+ "rustls",
  "rustls-pki-types",
  "tokio",
 ]
 
 [[package]]
 name = "tokio-stream"
-version = "0.1.14"
+version = "0.1.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
+checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
 dependencies = [
  "futures-core",
  "pin-project-lite",
@@ -4359,9 +4077,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-test"
-version = "0.4.3"
+version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e89b3cbabd3ae862100094ae433e1def582cf86451b4e9bf83aa7ac1d8a7d719"
+checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7"
 dependencies = [
  "async-stream",
  "bytes",
@@ -4372,9 +4090,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-util"
-version = "0.7.10"
+version = "0.7.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
+checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
 dependencies = [
  "bytes",
  "futures-core",
@@ -4382,7 +4100,6 @@ dependencies = [
  "futures-sink",
  "pin-project-lite",
  "tokio",
- "tracing",
 ]
 
 [[package]]
@@ -4399,14 +4116,14 @@ dependencies = [
 
 [[package]]
 name = "toml"
-version = "0.8.15"
+version = "0.8.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28"
+checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
 dependencies = [
  "serde",
  "serde_spanned",
- "toml_datetime 0.6.6",
- "toml_edit 0.22.16",
+ "toml_datetime 0.6.8",
+ "toml_edit 0.22.20",
 ]
 
 [[package]]
@@ -4420,9 +4137,9 @@ dependencies = [
 
 [[package]]
 name = "toml_datetime"
-version = "0.6.6"
+version = "0.6.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
+checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
 dependencies = [
  "serde",
 ]
@@ -4442,73 +4159,43 @@ dependencies = [
 
 [[package]]
 name = "toml_edit"
-version = "0.22.16"
+version = "0.22.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788"
+checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
 dependencies = [
  "indexmap 2.1.0",
  "serde",
  "serde_spanned",
- "toml_datetime 0.6.6",
+ "toml_datetime 0.6.8",
  "winnow",
 ]
 
 [[package]]
 name = "tonic"
-version = "0.11.0"
+version = "0.12.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13"
+checksum = "c6f6ba989e4b2c58ae83d862d3a3e27690b6e3ae630d0deb59f3697f32aa88ad"
 dependencies = [
  "async-stream",
  "async-trait",
- "axum 0.6.20",
- "base64 0.21.7",
- "bytes",
- "h2 0.3.26",
- "http 0.2.11",
- "http-body 0.4.6",
- "hyper 0.14.28",
- "hyper-timeout 0.4.1",
- "percent-encoding",
- "pin-project",
- "prost 0.12.3",
- "rustls-pemfile 2.1.0",
- "rustls-pki-types",
- "tokio",
- "tokio-rustls 0.25.0",
- "tokio-stream",
- "tower",
- "tower-layer",
- "tower-service",
- "tracing",
-]
-
-[[package]]
-name = "tonic"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38659f4a91aba8598d27821589f5db7dddd94601e7a01b1e485a50e5484c7401"
-dependencies = [
- "async-stream",
- "async-trait",
- "axum 0.7.5",
+ "axum",
  "base64 0.22.1",
  "bytes",
- "h2 0.4.4",
- "http 1.1.0",
- "http-body 1.0.0",
+ "h2",
+ "http",
+ "http-body",
  "http-body-util",
- "hyper 1.4.1",
- "hyper-timeout 0.5.1",
+ "hyper",
+ "hyper-timeout",
  "hyper-util",
  "percent-encoding",
  "pin-project",
- "prost 0.13.1",
- "rustls-native-certs 0.7.0",
- "rustls-pemfile 2.1.0",
+ "prost",
+ "rustls-native-certs",
+ "rustls-pemfile",
  "socket2",
  "tokio",
- "tokio-rustls 0.26.0",
+ "tokio-rustls",
  "tokio-stream",
  "tower",
  "tower-layer",
@@ -4518,54 +4205,41 @@ dependencies = [
 
 [[package]]
 name = "tonic-build"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be4ef6dd70a610078cb4e338a0f79d06bc759ff1b22d2120c2ff02ae264ba9c2"
-dependencies = [
- "prettyplease",
- "proc-macro2",
- "prost-build 0.12.3",
- "quote",
- "syn 2.0.48",
-]
-
-[[package]]
-name = "tonic-build"
-version = "0.12.1"
+version = "0.12.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "568392c5a2bd0020723e3f387891176aabafe36fd9fcd074ad309dfa0c8eb964"
+checksum = "fe4ee8877250136bd7e3d2331632810a4df4ea5e004656990d8d66d2f5ee8a67"
 dependencies = [
  "prettyplease",
  "proc-macro2",
- "prost-build 0.13.1",
+ "prost-build",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.76",
 ]
 
 [[package]]
 name = "tonic-health"
-version = "0.12.1"
+version = "0.12.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1e10e6a96ee08b6ce443487d4368442d328d0e746f3681f81127f7dc41b4955"
+checksum = "ec0a34e6f706bae26b2b490e1da5c3f6a6ff87cae442bcbc7c881bab9631b5a7"
 dependencies = [
  "async-stream",
- "prost 0.13.1",
+ "prost",
  "tokio",
  "tokio-stream",
- "tonic 0.12.1",
+ "tonic",
 ]
 
 [[package]]
 name = "tonic-reflection"
-version = "0.12.1"
+version = "0.12.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b742c83ad673e9ab5b4ce0981f7b9e8932be9d60e8682cbf9120494764dbc173"
+checksum = "7b56b874eedb04f89907573b408eab1e87c1c1dce43aac6ad63742f57faa99ff"
 dependencies = [
- "prost 0.13.1",
- "prost-types 0.13.1",
+ "prost",
+ "prost-types",
  "tokio",
  "tokio-stream",
- "tonic 0.12.1",
+ "tonic",
 ]
 
 [[package]]
@@ -4594,10 +4268,10 @@ version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
 dependencies = [
- "bitflags 2.4.2",
+ "bitflags 2.6.0",
  "bytes",
- "http 1.1.0",
- "http-body 1.0.0",
+ "http",
+ "http-body",
  "http-body-util",
  "pin-project-lite",
  "tower-layer",
@@ -4637,7 +4311,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -4739,9 +4413,9 @@ dependencies = [
 
 [[package]]
 name = "tracing-tracy"
-version = "0.11.0"
+version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6024d04f84a69fd0d1dc1eee3a2b070bd246530a0582f9982ae487cb6c703614"
+checksum = "c6a90519f16f55e5c62ffd5976349f10744435a919ecff83d918300575dfb69b"
 dependencies = [
  "tracing-core",
  "tracing-subscriber",
@@ -4775,21 +4449,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
 
 [[package]]
+name = "trybuild"
+version = "1.0.99"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "207aa50d36c4be8d8c6ea829478be44a372c6a77669937bb39c698e52f1491e8"
+dependencies = [
+ "glob",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "termcolor",
+ "toml 0.8.19",
+]
+
+[[package]]
 name = "tvix-build"
 version = "0.1.0"
 dependencies = [
  "bytes",
  "clap",
- "itertools 0.12.0",
+ "itertools 0.12.1",
  "mimalloc",
- "prost 0.13.1",
- "prost-build 0.13.1",
+ "prost",
+ "prost-build",
  "rstest",
  "thiserror",
  "tokio",
  "tokio-listener",
- "tonic 0.12.1",
- "tonic-build 0.12.1",
+ "tonic",
+ "tonic-build",
  "tonic-reflection",
  "tracing",
  "tvix-castore",
@@ -4823,8 +4511,8 @@ dependencies = [
  "parking_lot 0.12.3",
  "petgraph",
  "pin-project-lite",
- "prost 0.13.1",
- "prost-build 0.13.1",
+ "prost",
+ "prost-build",
  "redb",
  "rstest",
  "rstest_reuse",
@@ -4842,8 +4530,8 @@ dependencies = [
  "tokio-stream",
  "tokio-tar",
  "tokio-util",
- "tonic 0.12.1",
- "tonic-build 0.12.1",
+ "tonic",
+ "tonic-build",
  "tonic-reflection",
  "tower",
  "tracing",
@@ -4901,12 +4589,12 @@ dependencies = [
  "data-encoding",
  "dirs",
  "genawaiter",
- "imbl",
- "itertools 0.12.0",
+ "itertools 0.12.1",
  "lazy_static",
  "lexical-core",
  "md-5",
  "mimalloc",
+ "nohash-hasher",
  "os_str_bytes",
  "path-clean",
  "pretty_assertions",
@@ -4926,6 +4614,7 @@ dependencies = [
  "test-strategy",
  "toml 0.6.0",
  "tvix-eval-builtin-macros",
+ "vu128",
 ]
 
 [[package]]
@@ -4968,6 +4657,7 @@ dependencies = [
  "thiserror",
  "tokio",
  "tokio-tar",
+ "tokio-test",
  "tokio-util",
  "tracing",
  "tracing-indicatif",
@@ -5013,8 +4703,8 @@ dependencies = [
  "nix-compat",
  "parking_lot 0.12.3",
  "pin-project-lite",
- "prost 0.13.1",
- "prost-build 0.13.1",
+ "prost",
+ "prost-build",
  "redb",
  "reqwest",
  "reqwest-middleware",
@@ -5033,9 +4723,9 @@ dependencies = [
  "tokio-retry",
  "tokio-stream",
  "tokio-util",
- "toml 0.8.15",
- "tonic 0.12.1",
- "tonic-build 0.12.1",
+ "toml 0.8.19",
+ "tonic",
+ "tonic-build",
  "tonic-health",
  "tonic-reflection",
  "tower",
@@ -5052,8 +4742,8 @@ dependencies = [
 name = "tvix-tracing"
 version = "0.1.0"
 dependencies = [
- "axum 0.7.5",
- "http 1.1.0",
+ "axum",
+ "http",
  "indicatif",
  "lazy_static",
  "opentelemetry 0.24.0",
@@ -5063,7 +4753,7 @@ dependencies = [
  "reqwest-tracing",
  "thiserror",
  "tokio",
- "tonic 0.12.1",
+ "tonic",
  "tracing",
  "tracing-indicatif",
  "tracing-opentelemetry 0.25.0",
@@ -5104,7 +4794,7 @@ checksum = "ac73887f47b9312552aa90ef477927ff014d63d1920ca8037c6c1951eab64bb1"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -5163,9 +4853,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
 
 [[package]]
 name = "url"
-version = "2.5.0"
+version = "2.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
+checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
 dependencies = [
  "form_urlencoded",
  "idna",
@@ -5317,19 +5007,16 @@ dependencies = [
 ]
 
 [[package]]
-name = "wait-timeout"
-version = "0.2.0"
+name = "vu128"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
-dependencies = [
- "libc",
-]
+checksum = "b18da3bd753c6f4373511e5f025423986560dfe4a5e7d642cc9a0266847f9fdd"
 
 [[package]]
 name = "walkdir"
-version = "2.4.0"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
 dependencies = [
  "same-file",
  "winapi-util",
@@ -5371,7 +5058,7 @@ dependencies = [
  "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.76",
  "wasm-bindgen-shared",
 ]
 
@@ -5405,7 +5092,7 @@ checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.76",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
@@ -5450,31 +5137,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "which"
-version = "4.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
-dependencies = [
- "either",
- "home",
- "once_cell",
- "rustix",
-]
-
-[[package]]
-name = "which"
-version = "5.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9bf3ea8596f3a0dd5980b46430f2058dfe2c36a27ccfbb1845d6fbfcd9ba6e14"
-dependencies = [
- "either",
- "home",
- "once_cell",
- "rustix",
- "windows-sys 0.48.0",
-]
-
-[[package]]
 name = "winapi"
 version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5512,7 +5174,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49"
 dependencies = [
  "windows-core 0.54.0",
- "windows-targets 0.52.5",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -5521,7 +5183,7 @@ version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
 dependencies = [
- "windows-targets 0.52.5",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -5530,8 +5192,19 @@ version = "0.54.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65"
 dependencies = [
- "windows-result",
- "windows-targets 0.52.5",
+ "windows-result 0.1.2",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-registry"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
+dependencies = [
+ "windows-result 0.2.0",
+ "windows-strings",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -5540,7 +5213,26 @@ version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
 dependencies = [
- "windows-targets 0.52.5",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
+dependencies = [
+ "windows-result 0.2.0",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -5558,7 +5250,16 @@ version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
 dependencies = [
- "windows-targets 0.52.5",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -5578,18 +5279,18 @@ dependencies = [
 
 [[package]]
 name = "windows-targets"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
 dependencies = [
- "windows_aarch64_gnullvm 0.52.5",
- "windows_aarch64_msvc 0.52.5",
- "windows_i686_gnu 0.52.5",
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
  "windows_i686_gnullvm",
- "windows_i686_msvc 0.52.5",
- "windows_x86_64_gnu 0.52.5",
- "windows_x86_64_gnullvm 0.52.5",
- "windows_x86_64_msvc 0.52.5",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
 ]
 
 [[package]]
@@ -5600,9 +5301,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
 
 [[package]]
 name = "windows_aarch64_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
 
 [[package]]
 name = "windows_aarch64_msvc"
@@ -5612,9 +5313,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
 
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
 
 [[package]]
 name = "windows_i686_gnu"
@@ -5624,15 +5325,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
 
 [[package]]
 name = "windows_i686_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
 
 [[package]]
 name = "windows_i686_msvc"
@@ -5642,9 +5343,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
 
 [[package]]
 name = "windows_x86_64_gnu"
@@ -5654,9 +5355,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
@@ -5666,9 +5367,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
 
 [[package]]
 name = "windows_x86_64_msvc"
@@ -5678,30 +5379,20 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.52.5"
+version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 
 [[package]]
 name = "winnow"
-version = "0.6.14"
+version = "0.6.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "374ec40a2d767a3c1b4972d9475ecd557356637be906f2cb3f7fe17a6eb5e22f"
+checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
-name = "winreg"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5"
-dependencies = [
- "cfg-if",
- "windows-sys 0.48.0",
-]
-
-[[package]]
 name = "wu-manber"
 version = "0.1.0"
 source = "git+https://github.com/tvlfyi/wu-manber.git#0d5b22bea136659f7de60b102a7030e0daaa503d"
@@ -5749,7 +5440,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.48",
+ "syn 2.0.76",
 ]
 
 [[package]]
@@ -5760,27 +5451,27 @@ checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
 
 [[package]]
 name = "zstd"
-version = "0.13.0"
+version = "0.13.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110"
+checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9"
 dependencies = [
  "zstd-safe",
 ]
 
 [[package]]
 name = "zstd-safe"
-version = "7.0.0"
+version = "7.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e"
+checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059"
 dependencies = [
  "zstd-sys",
 ]
 
 [[package]]
 name = "zstd-sys"
-version = "2.0.9+zstd.1.5.5"
+version = "2.0.13+zstd.1.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656"
+checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa"
 dependencies = [
  "cc",
  "pkg-config",
diff --git a/tvix/Cargo.nix b/tvix/Cargo.nix
index 9df3c8af8ef5..5671624ceceb 100644
--- a/tvix/Cargo.nix
+++ b/tvix/Cargo.nix
@@ -55,6 +55,26 @@ rec {
       # File a bug if you depend on any for non-debug work!
       debug = internal.debugCrate { inherit packageId; };
     };
+    "nix-compat-derive" = rec {
+      packageId = "nix-compat-derive";
+      build = internal.buildRustCrateWithFeatures {
+        packageId = "nix-compat-derive";
+      };
+
+      # Debug support which might change between releases.
+      # File a bug if you depend on any for non-debug work!
+      debug = internal.debugCrate { inherit packageId; };
+    };
+    "nix-compat-derive-tests" = rec {
+      packageId = "nix-compat-derive-tests";
+      build = internal.buildRustCrateWithFeatures {
+        packageId = "nix-compat-derive-tests";
+      };
+
+      # Debug support which might change between releases.
+      # File a bug if you depend on any for non-debug work!
+      debug = internal.debugCrate { inherit packageId; };
+    };
     "tvix-build" = rec {
       packageId = "tvix-build";
       build = internal.buildRustCrateWithFeatures {
@@ -281,13 +301,14 @@ rec {
           "perf-literal" = [ "dep:memchr" ];
           "std" = [ "memchr?/std" ];
         };
-        resolvedDefaultFeatures = [ "default" "perf-literal" "std" ];
+        resolvedDefaultFeatures = [ "perf-literal" "std" ];
       };
       "allocator-api2" = rec {
         crateName = "allocator-api2";
         version = "0.2.18";
         edition = "2018";
         sha256 = "0kr6lfnxvnj164j1x38g97qjlhb7akppqzvgfs0697140ixbav2w";
+        libName = "allocator_api2";
         authors = [
           "Zakarum <zaq.dev@icloud.com>"
         ];
@@ -303,6 +324,7 @@ rec {
         version = "0.1.1";
         edition = "2018";
         sha256 = "1w7ynjxrfs97xg3qlcdns4kgfpwcdv824g611fq32cag4cdr96g9";
+        libName = "android_tzdata";
         authors = [
           "RumovZ"
         ];
@@ -340,9 +362,9 @@ rec {
       };
       "anstream" = rec {
         crateName = "anstream";
-        version = "0.6.11";
+        version = "0.6.15";
         edition = "2021";
-        sha256 = "19dndamalavhjwp4i74k8hdijcixb7gsfa6ycwyc1r8xn6y1wbkf";
+        sha256 = "09nm4qj34kiwgzczdvj14x7hgsb235g4sqsay3xsz7zqn4d5rqb4";
         dependencies = [
           {
             name = "anstyle";
@@ -368,6 +390,10 @@ rec {
             packageId = "colorchoice";
           }
           {
+            name = "is_terminal_polyfill";
+            packageId = "is_terminal_polyfill";
+          }
+          {
             name = "utf8parse";
             packageId = "utf8parse";
           }
@@ -381,9 +407,9 @@ rec {
       };
       "anstyle" = rec {
         crateName = "anstyle";
-        version = "1.0.4";
+        version = "1.0.8";
         edition = "2021";
-        sha256 = "11yxw02b6parn29s757z96rgiqbn8qy0fk9a3p3bhczm85dhfybh";
+        sha256 = "1cfmkza63xpn1kkz844mgjwm9miaiz4jkyczmwxzivcsypk1vv0v";
         features = {
           "default" = [ "std" ];
         };
@@ -391,9 +417,10 @@ rec {
       };
       "anstyle-parse" = rec {
         crateName = "anstyle-parse";
-        version = "0.2.3";
+        version = "0.2.5";
         edition = "2021";
-        sha256 = "134jhzrz89labrdwxxnjxqjdg06qvaflj1wkfnmyapwyldfwcnn7";
+        sha256 = "1jy12rvgbldflnb2x7mcww9dcffw1mx22nyv6p3n7d62h0gdwizb";
+        libName = "anstyle_parse";
         dependencies = [
           {
             name = "utf8parse";
@@ -410,9 +437,10 @@ rec {
       };
       "anstyle-query" = rec {
         crateName = "anstyle-query";
-        version = "1.0.2";
+        version = "1.1.1";
         edition = "2021";
-        sha256 = "0j3na4b1nma39g4x7cwvj009awxckjf3z2vkwhldgka44hqj72g2";
+        sha256 = "0aj22iy4pzk6mz745sfrm1ym14r0y892jhcrbs8nkj7nqx9gqdkd";
+        libName = "anstyle_query";
         dependencies = [
           {
             name = "windows-sys";
@@ -425,9 +453,10 @@ rec {
       };
       "anstyle-wincon" = rec {
         crateName = "anstyle-wincon";
-        version = "3.0.2";
+        version = "3.0.4";
         edition = "2021";
-        sha256 = "19v0fv400bmp4niqpzxnhg83vz12mmqv7l2l8vi80qcdxj0lpm8w";
+        sha256 = "1y2pkvsrdxbcwircahb4wimans2pzmwwxad7ikdhj5lpdqdlxxsv";
+        libName = "anstyle_wincon";
         dependencies = [
           {
             name = "anstyle";
@@ -444,9 +473,9 @@ rec {
       };
       "anyhow" = rec {
         crateName = "anyhow";
-        version = "1.0.79";
+        version = "1.0.86";
         edition = "2018";
-        sha256 = "1ji5irqiwr8yprgqj8zvnli7zd7fz9kzaiddq44jnrl2l289h3h8";
+        sha256 = "1nk301x8qhpdaks6a9zvcp7yakjqnczjmqndbg7vk4494d3d1ldk";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -461,6 +490,7 @@ rec {
         version = "1.6.0";
         edition = "2018";
         sha256 = "19n9j146bpxs9phyh48gmlh9jjsdijr9p9br04qms0g9ypfsvp5x";
+        libName = "arc_swap";
         authors = [
           "Michal 'vorner' Vaner <vorner@vorner.cz>"
         ];
@@ -497,6 +527,7 @@ rec {
         version = "2.2.0";
         edition = "2021";
         sha256 = "1hzhkbrlmgbrrwb1d5aba5f03p42s6z80g5p38s127c27nj470pj";
+        libName = "async_channel";
         authors = [
           "Stjepan Glavina <stjepang@gmail.com>"
         ];
@@ -534,9 +565,10 @@ rec {
       };
       "async-compression" = rec {
         crateName = "async-compression";
-        version = "0.4.9";
+        version = "0.4.12";
         edition = "2018";
-        sha256 = "14r6vbsbbkqjiqy0qwwywjakdi29jfyidhqp389l5r4gm7bsp7jf";
+        sha256 = "1ap403q889b1gghca7vigvydsnf8vr94xz3d488p9i9b9vv39hgy";
+        libName = "async_compression";
         authors = [
           "Wim Looman <wim@nemo157.com>"
           "Allen Bui <fairingrey@gmail.com>"
@@ -626,6 +658,7 @@ rec {
         version = "2.3.2";
         edition = "2021";
         sha256 = "110847w0ycfhklm3i928avd28x7lf9amblr2wjngi8ngk7sv1k6w";
+        libName = "async_io";
         authors = [
           "Stjepan Glavina <stjepang@gmail.com>"
         ];
@@ -690,6 +723,7 @@ rec {
         version = "2.8.0";
         edition = "2018";
         sha256 = "0asq5xdzgp3d5m82y5rg7a0k9q0g95jy6mgc7ivl334x7qlp4wi8";
+        libName = "async_lock";
         authors = [
           "Stjepan Glavina <stjepang@gmail.com>"
         ];
@@ -706,6 +740,7 @@ rec {
         version = "3.3.0";
         edition = "2021";
         sha256 = "0yxflkfw46rad4lv86f59b5z555dlfmg1riz1n8830rgi0qb8d6h";
+        libName = "async_lock";
         authors = [
           "Stjepan Glavina <stjepang@gmail.com>"
         ];
@@ -733,9 +768,10 @@ rec {
       };
       "async-process" = rec {
         crateName = "async-process";
-        version = "2.1.0";
+        version = "2.2.4";
         edition = "2021";
-        sha256 = "1j0cfac9p3kq5dclfzlz6jv5l29kwflh9nvr3ivmdg8ih3v3q7j5";
+        sha256 = "0x3305pq0fzaqmw7q4c93sgabq97zhkr32xig5dkhkcscn4pg858";
+        libName = "async_process";
         authors = [
           "Stjepan Glavina <stjepang@gmail.com>"
         ];
@@ -743,6 +779,11 @@ rec {
           {
             name = "async-channel";
             packageId = "async-channel";
+            target = { target, features }: ("linux" == target."os" or null);
+          }
+          {
+            name = "async-channel";
+            packageId = "async-channel";
             target = { target, features }: (target."windows" or false);
           }
           {
@@ -760,6 +801,11 @@ rec {
             target = { target, features }: (target."unix" or false);
           }
           {
+            name = "async-task";
+            packageId = "async-task";
+            target = { target, features }: ("linux" == target."os" or null);
+          }
+          {
             name = "blocking";
             packageId = "blocking";
             target = { target, features }: (target."windows" or false);
@@ -780,12 +826,24 @@ rec {
             name = "rustix";
             packageId = "rustix";
             usesDefaultFeatures = false;
+            target = { target, features }: ((target."unix" or false) && (!("linux" == target."os" or null)));
+            features = [ "std" "fs" "process" ];
+          }
+          {
+            name = "rustix";
+            packageId = "rustix";
+            usesDefaultFeatures = false;
             target = { target, features }: (target."unix" or false);
             features = [ "std" "fs" ];
           }
           {
+            name = "tracing";
+            packageId = "tracing";
+            usesDefaultFeatures = false;
+          }
+          {
             name = "windows-sys";
-            packageId = "windows-sys 0.52.0";
+            packageId = "windows-sys 0.59.0";
             usesDefaultFeatures = false;
             target = { target, features }: (target."windows" or false);
             features = [ "Win32_Foundation" "Win32_System_Threading" ];
@@ -804,6 +862,7 @@ rec {
         version = "0.2.5";
         edition = "2018";
         sha256 = "1i9466hiqghhmljjnn83a8vnxi8z013xga03f59c89d2cl7xjiwy";
+        libName = "async_signal";
         authors = [
           "John Nunley <dev@notgull.net>"
         ];
@@ -874,6 +933,7 @@ rec {
         version = "0.3.5";
         edition = "2018";
         sha256 = "0l8sjq1rylkb1ak0pdyjn83b3k6x36j22myngl4sqqgg7whdsmnd";
+        libName = "async_stream";
         authors = [
           "Carl Lerche <me@carllerche.com>"
         ];
@@ -899,6 +959,7 @@ rec {
         edition = "2018";
         sha256 = "14q179j4y8p2z1d0ic6aqgy9fhwz8p9cai1ia8kpw4bw7q12mrhn";
         procMacro = true;
+        libName = "async_stream_impl";
         authors = [
           "Carl Lerche <me@carllerche.com>"
         ];
@@ -913,7 +974,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
             features = [ "full" "visit-mut" ];
           }
         ];
@@ -924,6 +985,7 @@ rec {
         version = "4.7.0";
         edition = "2018";
         sha256 = "16975vx6aqy5yf16fs9xz5vx1zq8mwkzfmykvcilc1j7b6c6xczv";
+        libName = "async_task";
         authors = [
           "Stjepan Glavina <stjepang@gmail.com>"
         ];
@@ -938,6 +1000,7 @@ rec {
         version = "0.4.0";
         edition = "2021";
         sha256 = "16zx4qcwzq94n13pp6xwa4589apm5y8j20jb7lk4yzn42fqlnzdk";
+        libName = "async_tempfile";
         authors = [
           "Markus Mayer"
         ];
@@ -973,6 +1036,7 @@ rec {
         edition = "2021";
         sha256 = "1adf1jh2yg39rkpmqjqyr9xyd6849p0d95425i6imgbhx0syx069";
         procMacro = true;
+        libName = "async_trait";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -987,7 +1051,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
             features = [ "full" "visit-mut" ];
           }
         ];
@@ -998,6 +1062,7 @@ rec {
         version = "1.1.2";
         edition = "2018";
         sha256 = "1h5av1lw56m0jf0fd3bchxq8a30xv0b4wv8s4zkp4s0i7mfvs18m";
+        libName = "atomic_waker";
         authors = [
           "Stjepan Glavina <stjepang@gmail.com>"
           "Contributors to futures-rs"
@@ -1016,135 +1081,7 @@ rec {
         ];
 
       };
-      "axum 0.6.20" = rec {
-        crateName = "axum";
-        version = "0.6.20";
-        edition = "2021";
-        sha256 = "1gynqkg3dcy1zd7il69h8a3zax86v6qq5zpawqyn87mr6979x0iv";
-        dependencies = [
-          {
-            name = "async-trait";
-            packageId = "async-trait";
-          }
-          {
-            name = "axum-core";
-            packageId = "axum-core 0.3.4";
-          }
-          {
-            name = "bitflags";
-            packageId = "bitflags 1.3.2";
-          }
-          {
-            name = "bytes";
-            packageId = "bytes";
-          }
-          {
-            name = "futures-util";
-            packageId = "futures-util";
-            usesDefaultFeatures = false;
-            features = [ "alloc" ];
-          }
-          {
-            name = "http";
-            packageId = "http 0.2.11";
-          }
-          {
-            name = "http-body";
-            packageId = "http-body 0.4.6";
-          }
-          {
-            name = "hyper";
-            packageId = "hyper 0.14.28";
-            features = [ "stream" ];
-          }
-          {
-            name = "itoa";
-            packageId = "itoa";
-          }
-          {
-            name = "matchit";
-            packageId = "matchit 0.7.3";
-          }
-          {
-            name = "memchr";
-            packageId = "memchr";
-          }
-          {
-            name = "mime";
-            packageId = "mime";
-          }
-          {
-            name = "percent-encoding";
-            packageId = "percent-encoding";
-          }
-          {
-            name = "pin-project-lite";
-            packageId = "pin-project-lite";
-          }
-          {
-            name = "serde";
-            packageId = "serde";
-          }
-          {
-            name = "sync_wrapper";
-            packageId = "sync_wrapper 0.1.2";
-          }
-          {
-            name = "tower";
-            packageId = "tower";
-            usesDefaultFeatures = false;
-            features = [ "util" ];
-          }
-          {
-            name = "tower-layer";
-            packageId = "tower-layer";
-          }
-          {
-            name = "tower-service";
-            packageId = "tower-service";
-          }
-        ];
-        buildDependencies = [
-          {
-            name = "rustversion";
-            packageId = "rustversion";
-          }
-        ];
-        devDependencies = [
-          {
-            name = "rustversion";
-            packageId = "rustversion";
-          }
-          {
-            name = "serde";
-            packageId = "serde";
-            features = [ "derive" ];
-          }
-          {
-            name = "tower";
-            packageId = "tower";
-            rename = "tower";
-            features = [ "util" "timeout" "limit" "load-shed" "steer" "filter" ];
-          }
-        ];
-        features = {
-          "__private_docs" = [ "tower/full" "dep:tower-http" ];
-          "default" = [ "form" "http1" "json" "matched-path" "original-uri" "query" "tokio" "tower-log" ];
-          "form" = [ "dep:serde_urlencoded" ];
-          "headers" = [ "dep:headers" ];
-          "http1" = [ "hyper/http1" ];
-          "http2" = [ "hyper/http2" ];
-          "json" = [ "dep:serde_json" "dep:serde_path_to_error" ];
-          "macros" = [ "dep:axum-macros" ];
-          "multipart" = [ "dep:multer" ];
-          "query" = [ "dep:serde_urlencoded" ];
-          "tokio" = [ "dep:tokio" "hyper/server" "hyper/tcp" "hyper/runtime" "tower/make" ];
-          "tower-log" = [ "tower/log" ];
-          "tracing" = [ "dep:tracing" "axum-core/tracing" ];
-          "ws" = [ "tokio" "dep:tokio-tungstenite" "dep:sha1" "dep:base64" ];
-        };
-      };
-      "axum 0.7.5" = rec {
+      "axum" = rec {
         crateName = "axum";
         version = "0.7.5";
         edition = "2021";
@@ -1156,7 +1093,7 @@ rec {
           }
           {
             name = "axum-core";
-            packageId = "axum-core 0.4.3";
+            packageId = "axum-core";
           }
           {
             name = "bytes";
@@ -1170,11 +1107,11 @@ rec {
           }
           {
             name = "http";
-            packageId = "http 1.1.0";
+            packageId = "http";
           }
           {
             name = "http-body";
-            packageId = "http-body 1.0.0";
+            packageId = "http-body";
           }
           {
             name = "http-body-util";
@@ -1182,7 +1119,7 @@ rec {
           }
           {
             name = "hyper";
-            packageId = "hyper 1.4.1";
+            packageId = "hyper";
             optional = true;
           }
           {
@@ -1321,71 +1258,12 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" "form" "http1" "http2" "json" "matched-path" "original-uri" "query" "tokio" "tower-log" "tracing" ];
       };
-      "axum-core 0.3.4" = rec {
-        crateName = "axum-core";
-        version = "0.3.4";
-        edition = "2021";
-        sha256 = "0b1d9nkqb8znaba4qqzxzc968qwj4ybn4vgpyz9lz4a7l9vsb7vm";
-        dependencies = [
-          {
-            name = "async-trait";
-            packageId = "async-trait";
-          }
-          {
-            name = "bytes";
-            packageId = "bytes";
-          }
-          {
-            name = "futures-util";
-            packageId = "futures-util";
-            usesDefaultFeatures = false;
-            features = [ "alloc" ];
-          }
-          {
-            name = "http";
-            packageId = "http 0.2.11";
-          }
-          {
-            name = "http-body";
-            packageId = "http-body 0.4.6";
-          }
-          {
-            name = "mime";
-            packageId = "mime";
-          }
-          {
-            name = "tower-layer";
-            packageId = "tower-layer";
-          }
-          {
-            name = "tower-service";
-            packageId = "tower-service";
-          }
-        ];
-        buildDependencies = [
-          {
-            name = "rustversion";
-            packageId = "rustversion";
-          }
-        ];
-        devDependencies = [
-          {
-            name = "futures-util";
-            packageId = "futures-util";
-            usesDefaultFeatures = false;
-            features = [ "alloc" ];
-          }
-        ];
-        features = {
-          "__private_docs" = [ "dep:tower-http" ];
-          "tracing" = [ "dep:tracing" ];
-        };
-      };
-      "axum-core 0.4.3" = rec {
+      "axum-core" = rec {
         crateName = "axum-core";
         version = "0.4.3";
         edition = "2021";
         sha256 = "1qx28wg4j6qdcdrisqwyaavlzc0zvbsrcwa99zf9456lfbyn6p51";
+        libName = "axum_core";
         dependencies = [
           {
             name = "async-trait";
@@ -1403,11 +1281,11 @@ rec {
           }
           {
             name = "http";
-            packageId = "http 1.1.0";
+            packageId = "http";
           }
           {
             name = "http-body";
-            packageId = "http-body 1.0.0";
+            packageId = "http-body";
           }
           {
             name = "http-body-util";
@@ -1534,7 +1412,7 @@ rec {
           "default" = [ "std" ];
           "std" = [ "alloc" ];
         };
-        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+        resolvedDefaultFeatures = [ "alloc" "std" ];
       };
       "base64 0.22.1" = rec {
         crateName = "base64";
@@ -1567,7 +1445,12 @@ rec {
         crateName = "bigtable_rs";
         version = "0.2.10";
         edition = "2021";
-        sha256 = "0wzcx254qlzy64306mg3ynp1izvn0wdii2gyn6raz0harl5bmk0j";
+        workspace_member = null;
+        src = pkgs.fetchgit {
+          url = "https://github.com/liufuyang/bigtable_rs";
+          rev = "1818355a5373a5bc2c84287e3a4e3807154ac8ef";
+          sha256 = "0mn6iw1z7gdxbarsqiwscbdr25nplwlvzs0rs51vgnnjfsnbgl6q";
+        };
         authors = [
           "Fuyang Liu <liufuyang@users.noreply.github.com>"
         ];
@@ -1578,7 +1461,12 @@ rec {
           }
           {
             name = "http";
-            packageId = "http 0.2.11";
+            packageId = "http";
+          }
+          {
+            name = "hyper-util";
+            packageId = "hyper-util";
+            features = [ "tokio" ];
           }
           {
             name = "log";
@@ -1586,11 +1474,11 @@ rec {
           }
           {
             name = "prost";
-            packageId = "prost 0.12.3";
+            packageId = "prost";
           }
           {
             name = "prost-types";
-            packageId = "prost-types 0.12.3";
+            packageId = "prost-types";
           }
           {
             name = "prost-wkt";
@@ -1621,7 +1509,7 @@ rec {
           }
           {
             name = "tonic";
-            packageId = "tonic 0.11.0";
+            packageId = "tonic";
             features = [ "tls" "transport" ];
           }
           {
@@ -1632,57 +1520,20 @@ rec {
         buildDependencies = [
           {
             name = "prost-build";
-            packageId = "prost-build 0.12.3";
+            packageId = "prost-build";
           }
           {
-            name = "prost-wkt-build";
-            packageId = "prost-wkt-build";
+            name = "prost-wkt-types";
+            packageId = "prost-wkt-types";
           }
           {
             name = "tonic-build";
-            packageId = "tonic-build 0.11.0";
+            packageId = "tonic-build";
             features = [ "cleanup-markdown" ];
           }
         ];
 
       };
-      "bit-set" = rec {
-        crateName = "bit-set";
-        version = "0.5.3";
-        edition = "2015";
-        sha256 = "1wcm9vxi00ma4rcxkl3pzzjli6ihrpn9cfdi0c5b4cvga2mxs007";
-        authors = [
-          "Alexis Beingessner <a.beingessner@gmail.com>"
-        ];
-        dependencies = [
-          {
-            name = "bit-vec";
-            packageId = "bit-vec";
-            usesDefaultFeatures = false;
-          }
-        ];
-        features = {
-          "default" = [ "std" ];
-          "std" = [ "bit-vec/std" ];
-        };
-        resolvedDefaultFeatures = [ "default" "std" ];
-      };
-      "bit-vec" = rec {
-        crateName = "bit-vec";
-        version = "0.6.3";
-        edition = "2015";
-        sha256 = "1ywqjnv60cdh1slhz67psnp422md6jdliji6alq0gmly2xm9p7rl";
-        authors = [
-          "Alexis Beingessner <a.beingessner@gmail.com>"
-        ];
-        features = {
-          "default" = [ "std" ];
-          "serde" = [ "dep:serde" ];
-          "serde_no_std" = [ "serde/alloc" ];
-          "serde_std" = [ "std" "serde/std" ];
-        };
-        resolvedDefaultFeatures = [ "default" "std" ];
-      };
       "bitflags 1.3.2" = rec {
         crateName = "bitflags";
         version = "1.3.2";
@@ -1698,11 +1549,11 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" ];
       };
-      "bitflags 2.4.2" = rec {
+      "bitflags 2.6.0" = rec {
         crateName = "bitflags";
-        version = "2.4.2";
+        version = "2.6.0";
         edition = "2021";
-        sha256 = "1pqd142hyqlzr7p9djxq2ff0jx07a2sb2xp9lhw69cbf80s0jmzd";
+        sha256 = "1pkidwzn3hnxlsl8zizh0bncgbjnw7c41cx7bby26ncbzmiznj5h";
         authors = [
           "The Rust Project Developers"
         ];
@@ -1716,24 +1567,11 @@ rec {
         };
         resolvedDefaultFeatures = [ "std" ];
       };
-      "bitmaps" = rec {
-        crateName = "bitmaps";
-        version = "3.2.0";
-        edition = "2021";
-        sha256 = "00ql08pm4l9hizkldyy54v0pk96g7zg8x6i72c2vkcq0iawl4dkh";
-        authors = [
-          "Bodil Stokke <bodil@bodil.org>"
-        ];
-        features = {
-          "default" = [ "std" ];
-        };
-        resolvedDefaultFeatures = [ "default" "std" ];
-      };
       "blake3" = rec {
         crateName = "blake3";
-        version = "1.5.0";
+        version = "1.5.4";
         edition = "2021";
-        sha256 = "11ysh12zcqq6xkjxh5cbrmnwzalprm3z552i5ff7wm5za9hz0c82";
+        sha256 = "1xy6gp8yfcpvzwrhbx5iksxxwf6hsix403klizgr1s6qgwj3686q";
         authors = [
           "Jack O'Connor <oconnor663@gmail.com>"
           "Samuel Neves"
@@ -1763,8 +1601,8 @@ rec {
             features = [ "mac" ];
           }
           {
-            name = "rayon";
-            packageId = "rayon";
+            name = "rayon-core";
+            packageId = "rayon-core";
             optional = true;
           }
         ];
@@ -1776,20 +1614,20 @@ rec {
         ];
         features = {
           "default" = [ "std" ];
-          "digest" = [ "dep:digest" ];
           "mmap" = [ "std" "dep:memmap2" ];
-          "rayon" = [ "dep:rayon" "std" ];
+          "rayon" = [ "dep:rayon-core" "std" ];
           "serde" = [ "dep:serde" ];
-          "traits-preview" = [ "digest" ];
+          "traits-preview" = [ "dep:digest" ];
           "zeroize" = [ "dep:zeroize" "arrayvec/zeroize" ];
         };
-        resolvedDefaultFeatures = [ "default" "digest" "rayon" "std" "traits-preview" ];
+        resolvedDefaultFeatures = [ "default" "rayon" "std" "traits-preview" ];
       };
       "block-buffer" = rec {
         crateName = "block-buffer";
         version = "0.10.4";
         edition = "2018";
         sha256 = "0w9sa2ypmrsqqvc20nhwr75wbb5cjr4kkyhpjm1z1lv2kdicfy1h";
+        libName = "block_buffer";
         authors = [
           "RustCrypto Developers"
         ];
@@ -1858,9 +1696,9 @@ rec {
       };
       "bstr" = rec {
         crateName = "bstr";
-        version = "1.9.0";
+        version = "1.10.0";
         edition = "2021";
-        sha256 = "1p6hzf3wqwwynv6w4pn17jg21amfafph9kb5sfvf1idlli8h13y4";
+        sha256 = "036wwrchd5gq3q4k6w1j2bfl2bk2ff8c0dsa9y7w7aw7nf7knwj0";
         authors = [
           "Andrew Gallant <jamslam@gmail.com>"
         ];
@@ -1872,7 +1710,7 @@ rec {
           }
           {
             name = "regex-automata";
-            packageId = "regex-automata 0.4.3";
+            packageId = "regex-automata 0.4.7";
             optional = true;
             usesDefaultFeatures = false;
             features = [ "dfa-search" ];
@@ -1921,9 +1759,9 @@ rec {
       };
       "bytes" = rec {
         crateName = "bytes";
-        version = "1.5.0";
+        version = "1.7.1";
         edition = "2018";
-        sha256 = "08w2i8ac912l8vlvkv3q51cd4gr09pwlg3sjsjffcizlrb0i5gd2";
+        sha256 = "0l5sf69avjxcw41cznyzxsnymwmkpmk08q0sm7fgicvvn0ysa643";
         authors = [
           "Carl Lerche <me@carllerche.com>"
           "Sean McArthur <sean@seanmonstar.com>"
@@ -2023,10 +1861,9 @@ rec {
       };
       "cc" = rec {
         crateName = "cc";
-        version = "1.0.83";
+        version = "1.1.15";
         edition = "2018";
-        crateBin = [ ];
-        sha256 = "1l643zidlb5iy1dskc5ggqs4wqa29a02f44piczqc8zcnsq4y5zi";
+        sha256 = "1rn62w58ba1ylqlp3saj4n0vh1h40ii1r83xr06p80r9m9ss5djp";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -2035,25 +1872,31 @@ rec {
             name = "jobserver";
             packageId = "jobserver";
             optional = true;
+            usesDefaultFeatures = false;
           }
           {
             name = "libc";
             packageId = "libc";
+            optional = true;
             usesDefaultFeatures = false;
             target = { target, features }: (target."unix" or false);
           }
+          {
+            name = "shlex";
+            packageId = "shlex";
+          }
         ];
         features = {
-          "jobserver" = [ "dep:jobserver" ];
-          "parallel" = [ "jobserver" ];
+          "parallel" = [ "dep:libc" "dep:jobserver" ];
         };
-        resolvedDefaultFeatures = [ "jobserver" "parallel" ];
+        resolvedDefaultFeatures = [ "parallel" ];
       };
       "cfg-if" = rec {
         crateName = "cfg-if";
         version = "1.0.0";
         edition = "2018";
         sha256 = "1za0vb97n4brpzpv8lsbnzmq5r8f2b0cpqqr0sy8h5bn751xxwds";
+        libName = "cfg_if";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -2107,7 +1950,7 @@ rec {
           }
           {
             name = "windows-targets";
-            packageId = "windows-targets 0.52.5";
+            packageId = "windows-targets 0.52.6";
             optional = true;
             target = { target, features }: (target."windows" or false);
           }
@@ -2173,6 +2016,7 @@ rec {
         version = "0.2.1";
         edition = "2021";
         sha256 = "0mi6ci27lpz3azksxrvgzl9jc4a3dfr20pjx7y2nkcrjalbikyfd";
+        libName = "ciborium_io";
         authors = [
           "Nathaniel McCallum <npmccallum@profian.com>"
         ];
@@ -2186,6 +2030,7 @@ rec {
         version = "0.2.1";
         edition = "2021";
         sha256 = "0az2vabamfk75m74ylgf6nzqgqgma5yf25bc1ripfg09ri7a5yny";
+        libName = "ciborium_ll";
         authors = [
           "Nathaniel McCallum <npmccallum@profian.com>"
         ];
@@ -2205,10 +2050,10 @@ rec {
       };
       "clap" = rec {
         crateName = "clap";
-        version = "4.4.18";
+        version = "4.5.16";
         edition = "2021";
         crateBin = [ ];
-        sha256 = "0p46h346y8nval6gwzh27if3icbi9dwl95fg5ir36ihrqip8smqy";
+        sha256 = "068hjwbrndn4iz4fsc6d52q4ymg1kfsymjnqbxzdil23zbzijrzd";
         dependencies = [
           {
             name = "clap_builder";
@@ -2236,6 +2081,7 @@ rec {
           "suggestions" = [ "clap_builder/suggestions" ];
           "unicode" = [ "clap_builder/unicode" ];
           "unstable-doc" = [ "clap_builder/unstable-doc" "derive" ];
+          "unstable-ext" = [ "clap_builder/unstable-ext" ];
           "unstable-styles" = [ "clap_builder/unstable-styles" ];
           "unstable-v5" = [ "clap_builder/unstable-v5" "clap_derive?/unstable-v5" "deprecated" ];
           "usage" = [ "clap_builder/usage" ];
@@ -2245,9 +2091,9 @@ rec {
       };
       "clap_builder" = rec {
         crateName = "clap_builder";
-        version = "4.4.18";
+        version = "4.5.15";
         edition = "2021";
-        sha256 = "1iyif47075caa4x1p3ygk18b07lb4xl4k48w4c061i2hxi0dzx2d";
+        sha256 = "1dmas5z20yqmlmfhykr38pn1hkcnr4jzxjw4cs2f6lkn2wmyqsi1";
         dependencies = [
           {
             name = "anstream";
@@ -2264,7 +2110,7 @@ rec {
           }
           {
             name = "strsim";
-            packageId = "strsim";
+            packageId = "strsim 0.11.1";
             optional = true;
           }
         ];
@@ -2275,7 +2121,7 @@ rec {
           "std" = [ "anstyle/std" ];
           "suggestions" = [ "dep:strsim" "error-context" ];
           "unicode" = [ "dep:unicode-width" "dep:unicase" ];
-          "unstable-doc" = [ "cargo" "wrap_help" "env" "unicode" "string" ];
+          "unstable-doc" = [ "cargo" "wrap_help" "env" "unicode" "string" "unstable-ext" ];
           "unstable-styles" = [ "color" ];
           "unstable-v5" = [ "deprecated" ];
           "wrap_help" = [ "help" "dep:terminal_size" ];
@@ -2284,14 +2130,14 @@ rec {
       };
       "clap_derive" = rec {
         crateName = "clap_derive";
-        version = "4.4.7";
+        version = "4.5.13";
         edition = "2021";
-        sha256 = "0hk4hcxl56qwqsf4hmf7c0gr19r9fbxk0ah2bgkr36pmmaph966g";
+        sha256 = "1860xq3rbgwsqwcj9rd14cky9iiywwx86j7fvvngdjixbyfka7ah";
         procMacro = true;
         dependencies = [
           {
             name = "heck";
-            packageId = "heck";
+            packageId = "heck 0.5.0";
           }
           {
             name = "proc-macro2";
@@ -2303,7 +2149,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
             features = [ "full" ];
           }
         ];
@@ -2315,9 +2161,9 @@ rec {
       };
       "clap_lex" = rec {
         crateName = "clap_lex";
-        version = "0.6.0";
+        version = "0.7.2";
         edition = "2021";
-        sha256 = "1l8bragdvim7mva9flvd159dskn2bdkpl0jqrr41wnjfn8pcfbvh";
+        sha256 = "15zcrc2fa6ycdzaihxghf48180bnvzsivhf0fmah24bnnaf76qhl";
 
       };
       "clipboard-win" = rec {
@@ -2325,6 +2171,7 @@ rec {
         version = "4.5.0";
         edition = "2018";
         sha256 = "0qh3rypkf1lazniq4nr04hxsck0d55rigb5sjvpvgnap4dyc54bi";
+        libName = "clipboard_win";
         authors = [
           "Douman <douman@gmx.se>"
         ];
@@ -2366,6 +2213,7 @@ rec {
         version = "0.1.2";
         edition = "2015";
         sha256 = "08l1b84bn8r8a72rbvyi2v8a5i0j0kk0a5gr7fb6lmjvw05pf86c";
+        libName = "codemap_diagnostic";
         authors = [
           "Kevin Mehall <km@kevinmehall.net>"
           "The Rust Project Developers"
@@ -2384,9 +2232,9 @@ rec {
       };
       "colorchoice" = rec {
         crateName = "colorchoice";
-        version = "1.0.0";
+        version = "1.0.2";
         edition = "2021";
-        sha256 = "1ix7w85kwvyybwi2jdkl3yva2r2bvdcc3ka2grjfzfgrapqimgxc";
+        sha256 = "1h18ph538y8yjmbpaf8li98l0ifms2xmh3rax9666c5qfjfi3zfk";
 
       };
       "concurrent-queue" = rec {
@@ -2394,6 +2242,7 @@ rec {
         version = "2.4.0";
         edition = "2018";
         sha256 = "0qvk23ynj311adb4z7v89wk3bs65blps4n24q8rgl23vjk6lhq6i";
+        libName = "concurrent_queue";
         authors = [
           "Stjepan Glavina <stjepang@gmail.com>"
           "Taiki Endo <te316e89@gmail.com>"
@@ -2459,6 +2308,7 @@ rec {
         version = "0.9.6";
         edition = "2021";
         sha256 = "1y0jnqaq7p2wvspnx7qj76m7hjcqpz73qzvr9l2p9n2s51vr6if2";
+        libName = "const_oid";
         authors = [
           "RustCrypto Developers"
         ];
@@ -2481,6 +2331,7 @@ rec {
         version = "0.9.4";
         edition = "2018";
         sha256 = "13zvbbj07yk3b61b8fhwfzhy35535a583irf23vlcg59j7h9bqci";
+        libName = "core_foundation";
         authors = [
           "The Servo Project Developers"
         ];
@@ -2512,6 +2363,7 @@ rec {
         version = "0.8.6";
         edition = "2018";
         sha256 = "13w6sdf06r0hn7bx2b45zxsg1mm2phz34jikm6xc5qrbr6djpsh6";
+        libName = "core_foundation_sys";
         authors = [
           "The Servo Project Developers"
         ];
@@ -2525,6 +2377,7 @@ rec {
         version = "0.1.0";
         edition = "2018";
         sha256 = "11bswmgr81s3jagdci1pr6qh9vnz9zsbbf2dqpi260daa2mhgmff";
+        libName = "count_write";
         authors = [
           "SOFe <sofe2038@gmail.com>"
         ];
@@ -2720,6 +2573,7 @@ rec {
         version = "0.5.0";
         edition = "2018";
         sha256 = "1c866xkjqqhzg4cjvg01f8w6xc1j3j7s58rdksl52skq89iq4l3b";
+        libName = "criterion_plot";
         authors = [
           "Jorge Aparicio <japaricious@gmail.com>"
           "Brook Heisler <brookheisler@gmail.com>"
@@ -2741,6 +2595,7 @@ rec {
         version = "0.5.11";
         edition = "2021";
         sha256 = "16v48qdflpw3hgdik70bhsj7hympna79q7ci47rw0mlgnxsw2v8p";
+        libName = "crossbeam_channel";
         dependencies = [
           {
             name = "crossbeam-utils";
@@ -2759,6 +2614,7 @@ rec {
         version = "0.8.5";
         edition = "2021";
         sha256 = "03bp38ljx4wj6vvy4fbhx41q8f585zyqix6pncz1mkz93z08qgv1";
+        libName = "crossbeam_deque";
         dependencies = [
           {
             name = "crossbeam-epoch";
@@ -2782,6 +2638,7 @@ rec {
         version = "0.9.18";
         edition = "2021";
         sha256 = "03j2np8llwf376m3fxqx859mgp9f83hj1w34153c7a9c7i5ar0jv";
+        libName = "crossbeam_epoch";
         dependencies = [
           {
             name = "crossbeam-utils";
@@ -2803,6 +2660,7 @@ rec {
         version = "0.8.19";
         edition = "2021";
         sha256 = "0iakrb1b8fjqrag7wphl94d10irhbh2fw1g444xslsywqyn3p3i4";
+        libName = "crossbeam_utils";
         features = {
           "default" = [ "std" ];
           "loom" = [ "dep:loom" ];
@@ -2814,6 +2672,7 @@ rec {
         version = "0.1.6";
         edition = "2018";
         sha256 = "1cvby95a6xg7kxdz5ln3rl9xh66nz66w46mm3g56ri1z5x815yqv";
+        libName = "crypto_common";
         authors = [
           "RustCrypto Developers"
         ];
@@ -2839,6 +2698,7 @@ rec {
         version = "4.1.1";
         edition = "2021";
         sha256 = "0p7ns5917k6369gajrsbfj24llc5zfm635yh3abla7sb5rm8r6z8";
+        libName = "curve25519_dalek";
         authors = [
           "Isis Lovecruft <isis@patternsinthevoid.net>"
           "Henry de Valence <hdevalence@hdevalence.ca>"
@@ -2911,6 +2771,7 @@ rec {
         edition = "2021";
         sha256 = "1cry71xxrr0mcy5my3fb502cwfxy6822k4pm19cwrilrg7hq4s7l";
         procMacro = true;
+        libName = "curve25519_dalek_derive";
         dependencies = [
           {
             name = "proc-macro2";
@@ -2922,7 +2783,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
             features = [ "full" ];
           }
         ];
@@ -2980,12 +2841,12 @@ rec {
           }
           {
             name = "strsim";
-            packageId = "strsim";
+            packageId = "strsim 0.10.0";
             optional = true;
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
             features = [ "full" "extra-traits" ];
           }
         ];
@@ -3015,7 +2876,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
           }
         ];
 
@@ -3025,6 +2886,7 @@ rec {
         version = "2.6.0";
         edition = "2018";
         sha256 = "1qnn68n4vragxaxlkqcb1r28d3hhj43wch67lm4rpxlw89wnjmp8";
+        libName = "data_encoding";
         authors = [
           "Julien Cretin <git@ia0.eu>"
         ];
@@ -3173,6 +3035,7 @@ rec {
         version = "2.0.0";
         edition = "2018";
         sha256 = "1q9kr151h9681wwp6is18750ssghz6j9j7qm7qi1ngcwy7mzi35r";
+        libName = "dirs_next";
         authors = [
           "The @xdg-rs members"
         ];
@@ -3193,6 +3056,7 @@ rec {
         version = "0.3.7";
         edition = "2015";
         sha256 = "19md1cnkazham8a6kh22v12d8hh3raqahfk6yb043vrjr68is78v";
+        libName = "dirs_sys";
         authors = [
           "Simon Ochsenreither <simon@ochsenreither.de>"
         ];
@@ -3222,6 +3086,7 @@ rec {
         version = "0.1.2";
         edition = "2018";
         sha256 = "0kavhavdxv4phzj4l0psvh55hszwnr0rcz8sxbvx20pyqi2a3gaf";
+        libName = "dirs_sys_next";
         authors = [
           "The @xdg-rs members"
         ];
@@ -3273,6 +3138,7 @@ rec {
         edition = "2018";
         sha256 = "15cvgxqngxslgllz15m8aban6wqfgsi6nlhr0g25yfsnd6nq4lpg";
         procMacro = true;
+        libName = "document_features";
         libPath = "lib.rs";
         authors = [
           "Slint Developers <info@slint-ui.com>"
@@ -3321,9 +3187,10 @@ rec {
       };
       "ed25519-dalek" = rec {
         crateName = "ed25519-dalek";
-        version = "2.1.0";
+        version = "2.1.1";
         edition = "2021";
-        sha256 = "1h13qm789m9gdjl6jazss80hqi8ll37m0afwcnw23zcbqjp8wqhz";
+        sha256 = "0w88cafwglg9hjizldbmlza0ns3hls81zk1bcih3m5m3h67algaa";
+        libName = "ed25519_dalek";
         authors = [
           "isis lovecruft <isis@patternsinthevoid.net>"
           "Tony Arcieri <bascule@gmail.com>"
@@ -3408,7 +3275,7 @@ rec {
           "default" = [ "use_std" ];
           "serde" = [ "dep:serde" ];
         };
-        resolvedDefaultFeatures = [ "default" "use_std" ];
+        resolvedDefaultFeatures = [ "use_std" ];
       };
       "encode_unicode" = rec {
         crateName = "encode_unicode";
@@ -3430,6 +3297,7 @@ rec {
         version = "0.1.2";
         edition = "2015";
         sha256 = "0bbh88zaig1jfqrm7w3gx0pz81kw2jakk3055vbgapw3dmk08ky3";
+        libName = "endian_type";
         authors = [
           "Lolirofle <lolipopple@hotmail.com>"
         ];
@@ -3441,6 +3309,7 @@ rec {
         edition = "2018";
         sha256 = "0k6wcf58h5kh64yq5nfq71va53kaya0kzxwsjwbgwm2n2zd9axxs";
         procMacro = true;
+        libName = "enum_primitive_derive";
         authors = [
           "Doug Goldstein <cardoe@cardoe.com>"
         ];
@@ -3456,7 +3325,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
           }
         ];
 
@@ -3540,6 +3409,7 @@ rec {
         version = "2.3.1";
         edition = "2018";
         sha256 = "08baxlf8qz01lgjsdbfhs193r9y1nlc566s5xvzyf4dzwy8qkwb4";
+        libName = "error_code";
         authors = [
           "Douman <douman@gmx.se>"
         ];
@@ -3561,6 +3431,7 @@ rec {
         version = "2.5.3";
         edition = "2018";
         sha256 = "1q4w3pndc518crld6zsqvvpy9lkzwahp2zgza9kbzmmqh9gif1h2";
+        libName = "event_listener";
         authors = [
           "Stjepan Glavina <stjepang@gmail.com>"
         ];
@@ -3571,6 +3442,7 @@ rec {
         version = "4.0.3";
         edition = "2021";
         sha256 = "0vk4smw1vf871vi76af1zn7w69jg3zmpjddpby2qq91bkg21bck7";
+        libName = "event_listener";
         authors = [
           "Stjepan Glavina <stjepang@gmail.com>"
         ];
@@ -3606,6 +3478,7 @@ rec {
         version = "5.2.0";
         edition = "2021";
         sha256 = "14fcnjgpfl22645nhc3hzkdq3a1v0srqacc3kfassg7sjj8vhprb";
+        libName = "event_listener";
         authors = [
           "Stjepan Glavina <stjepang@gmail.com>"
         ];
@@ -3641,6 +3514,7 @@ rec {
         version = "0.4.0";
         edition = "2018";
         sha256 = "1lwprdjqp2ibbxhgm9khw7s7y7k4xiqj5i5yprqiks6mnrq4v3lm";
+        libName = "event_listener_strategy";
         authors = [
           "John Nunley <dev@notgull.net>"
         ];
@@ -3666,6 +3540,7 @@ rec {
         version = "0.5.0";
         edition = "2021";
         sha256 = "148jflvjrq0zrr3dx3srv88jksj1klm4amy3b9fifjdpm75azvgy";
+        libName = "event_listener_strategy";
         authors = [
           "John Nunley <dev@notgull.net>"
         ];
@@ -3691,6 +3566,7 @@ rec {
         version = "1.5.0";
         edition = "2018";
         sha256 = "1q55nrkgzg345905aqbsdrwlq4sk0gjn4z5bdph1an1kc6jy02wy";
+        libName = "expect_test";
         authors = [
           "rust-analyzer developers"
         ];
@@ -3768,6 +3644,7 @@ rec {
         version = "3.0.13";
         edition = "2018";
         sha256 = "1df1jdncda67g65hrnmd2zsl7q5hdn8cm84chdalxndsx7akw0zg";
+        libName = "fd_lock";
         authors = [
           "Yoshua Wuyts <yoshuawuyts@gmail.com>"
         ];
@@ -3796,6 +3673,7 @@ rec {
         version = "0.2.5";
         edition = "2018";
         sha256 = "1dxn0g50pv0ppal779vi7k40fr55pbhkyv4in7i13pgl4sn3wmr7";
+        libName = "fiat_crypto";
         authors = [
           "Fiat Crypto library authors <jgross@mit.edu>"
         ];
@@ -3958,6 +3836,7 @@ rec {
         version = "0.11.0";
         edition = "2018";
         sha256 = "0jyldvp0kvjk21j5vqga42lkksaf7zg8jkj3l6h2dv20kyl66nif";
+        libName = "fuse_backend_rs";
         authors = [
           "Liu Bo <bo.liu@linux.alibaba.com>"
           "Liu Jiang <gerry@linux.alibaba.com>"
@@ -3998,7 +3877,7 @@ rec {
           }
           {
             name = "mio";
-            packageId = "mio";
+            packageId = "mio 0.8.11";
             features = [ "os-poll" "os-ext" ];
           }
           {
@@ -4126,6 +4005,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "0y6b7xxqdjm9hlcjpakcg41qfl7lihf6gavk8fyqijsxhvbzgj7a";
+        libName = "futures_channel";
         dependencies = [
           {
             name = "futures-core";
@@ -4153,6 +4033,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "07aslayrn3lbggj54kci0ishmd1pr367fp7iks7adia1p05miinz";
+        libName = "futures_core";
         features = {
           "default" = [ "std" ];
           "portable-atomic" = [ "dep:portable-atomic" ];
@@ -4165,6 +4046,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "07dh08gs9vfll2h36kq32q9xd86xm6lyl9xikmmwlkqnmrrgqxm5";
+        libName = "futures_executor";
         dependencies = [
           {
             name = "futures-core";
@@ -4195,6 +4077,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "1hgh25isvsr4ybibywhr4dpys8mjnscw4wfxxwca70cn1gi26im4";
+        libName = "futures_io";
         features = {
           "default" = [ "std" ];
         };
@@ -4205,6 +4088,7 @@ rec {
         version = "2.3.0";
         edition = "2021";
         sha256 = "19gk4my8zhfym6gwnpdjiyv2hw8cc098skkbkhryjdaf0yspwljj";
+        libName = "futures_lite";
         authors = [
           "Stjepan Glavina <stjepang@gmail.com>"
           "Contributors to futures-rs"
@@ -4253,6 +4137,7 @@ rec {
         edition = "2018";
         sha256 = "1b49qh9d402y8nka4q6wvvj0c88qq91wbr192mdn5h54nzs0qxc7";
         procMacro = true;
+        libName = "futures_macro";
         dependencies = [
           {
             name = "proc-macro2";
@@ -4264,7 +4149,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
             features = [ "full" ];
           }
         ];
@@ -4275,6 +4160,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "1dag8xyyaya8n8mh8smx7x6w2dpmafg2din145v973a3hw7f1f4z";
+        libName = "futures_sink";
         features = {
           "default" = [ "std" ];
           "std" = [ "alloc" ];
@@ -4286,6 +4172,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "013h1724454hj8qczp8vvs10qfiqrxr937qsrv6rhii68ahlzn1q";
+        libName = "futures_task";
         features = {
           "default" = [ "std" ];
           "std" = [ "alloc" ];
@@ -4297,6 +4184,7 @@ rec {
         version = "3.0.2";
         edition = "2018";
         sha256 = "0b5v7lk9838ix6jdcrainsyrh7xrf24pwm61dp13907qkn806jz6";
+        libName = "futures_timer";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -4311,6 +4199,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "0j0xqhcir1zf2dcbpd421kgw6wvsk0rpxflylcysn1rlp3g02r1x";
+        libName = "futures_util";
         dependencies = [
           {
             name = "futures-channel";
@@ -4410,9 +4299,9 @@ rec {
       };
       "gcp_auth" = rec {
         crateName = "gcp_auth";
-        version = "0.10.0";
+        version = "0.12.2";
         edition = "2021";
-        sha256 = "1m7lsh2gc7n9p0gs9k2qbxsrvchw1vz6dyz9a2ma322vd3m72b6y";
+        sha256 = "0gb9bp9nkc810kfycm4vxndpccbhx68lcirq0y06lafykpkpjv2k";
         dependencies = [
           {
             name = "async-trait";
@@ -4420,7 +4309,11 @@ rec {
           }
           {
             name = "base64";
-            packageId = "base64 0.21.7";
+            packageId = "base64 0.22.1";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
           }
           {
             name = "chrono";
@@ -4432,27 +4325,37 @@ rec {
             packageId = "home";
           }
           {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "http-body-util";
+            packageId = "http-body-util";
+          }
+          {
             name = "hyper";
-            packageId = "hyper 0.14.28";
-            features = [ "client" "runtime" "http2" ];
+            packageId = "hyper";
+            usesDefaultFeatures = false;
+            features = [ "client" "http1" "http2" ];
           }
           {
             name = "hyper-rustls";
-            packageId = "hyper-rustls 0.24.2";
+            packageId = "hyper-rustls";
             usesDefaultFeatures = false;
-            features = [ "tokio-runtime" "http1" "http2" ];
+            features = [ "http1" "http2" ];
           }
           {
-            name = "ring";
-            packageId = "ring";
+            name = "hyper-util";
+            packageId = "hyper-util";
+            features = [ "client-legacy" ];
           }
           {
-            name = "rustls";
-            packageId = "rustls 0.21.12";
+            name = "ring";
+            packageId = "ring";
           }
           {
             name = "rustls-pemfile";
-            packageId = "rustls-pemfile 1.0.4";
+            packageId = "rustls-pemfile";
           }
           {
             name = "serde";
@@ -4484,10 +4387,6 @@ rec {
             name = "url";
             packageId = "url";
           }
-          {
-            name = "which";
-            packageId = "which 5.0.0";
-          }
         ];
         devDependencies = [
           {
@@ -4497,7 +4396,7 @@ rec {
           }
         ];
         features = {
-          "default" = [ "hyper-rustls/rustls-native-certs" ];
+          "default" = [ "hyper-rustls/rustls-native-certs" "hyper-rustls/ring" ];
           "webpki-roots" = [ "hyper-rustls/webpki-roots" ];
         };
         resolvedDefaultFeatures = [ "default" ];
@@ -4530,6 +4429,7 @@ rec {
         version = "0.99.1";
         edition = "2018";
         sha256 = "1g6zmr88fk48f1ksz9ik1i2mwjsiam9s4p9aybhvs2zwzphxychb";
+        libName = "genawaiter_macro";
         authors = [
           "Devin R <devin.ragotzy@gmail.com>"
         ];
@@ -4680,79 +4580,7 @@ rec {
         ];
 
       };
-      "h2 0.3.26" = rec {
-        crateName = "h2";
-        version = "0.3.26";
-        edition = "2018";
-        sha256 = "1s7msnfv7xprzs6xzfj5sg6p8bjcdpcqcmjjbkd345cyi1x55zl1";
-        authors = [
-          "Carl Lerche <me@carllerche.com>"
-          "Sean McArthur <sean@seanmonstar.com>"
-        ];
-        dependencies = [
-          {
-            name = "bytes";
-            packageId = "bytes";
-          }
-          {
-            name = "fnv";
-            packageId = "fnv";
-          }
-          {
-            name = "futures-core";
-            packageId = "futures-core";
-            usesDefaultFeatures = false;
-          }
-          {
-            name = "futures-sink";
-            packageId = "futures-sink";
-            usesDefaultFeatures = false;
-          }
-          {
-            name = "futures-util";
-            packageId = "futures-util";
-            usesDefaultFeatures = false;
-          }
-          {
-            name = "http";
-            packageId = "http 0.2.11";
-          }
-          {
-            name = "indexmap";
-            packageId = "indexmap 2.1.0";
-            features = [ "std" ];
-          }
-          {
-            name = "slab";
-            packageId = "slab";
-          }
-          {
-            name = "tokio";
-            packageId = "tokio";
-            features = [ "io-util" ];
-          }
-          {
-            name = "tokio-util";
-            packageId = "tokio-util";
-            features = [ "codec" "io" ];
-          }
-          {
-            name = "tracing";
-            packageId = "tracing";
-            usesDefaultFeatures = false;
-            features = [ "std" ];
-          }
-        ];
-        devDependencies = [
-          {
-            name = "tokio";
-            packageId = "tokio";
-            features = [ "rt-multi-thread" "macros" "sync" "net" ];
-          }
-        ];
-        features = { };
-      };
-      "h2 0.4.4" = rec {
+      "h2" = rec {
         crateName = "h2";
         version = "0.4.4";
         edition = "2021";
@@ -4787,7 +4615,7 @@ rec {
           }
           {
             name = "http";
-            packageId = "http 1.1.0";
+            packageId = "http";
           }
           {
             name = "indexmap";
@@ -4902,7 +4730,7 @@ rec {
         };
         resolvedDefaultFeatures = [ "ahash" "allocator-api2" "default" "inline-more" "raw" ];
       };
-      "heck" = rec {
+      "heck 0.4.1" = rec {
         crateName = "heck";
         version = "0.4.1";
         edition = "2018";
@@ -4916,11 +4744,19 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" ];
       };
+      "heck 0.5.0" = rec {
+        crateName = "heck";
+        version = "0.5.0";
+        edition = "2021";
+        sha256 = "1sjmpsdl8czyh9ywl3qcsfsq9a307dg4ni2vnlwgnzzqhc4y0113";
+
+      };
       "hermit-abi" = rec {
         crateName = "hermit-abi";
-        version = "0.3.4";
+        version = "0.3.9";
         edition = "2021";
-        sha256 = "07v5vbwb9kx0yxgdpx15h38ynpzhaqx5ncriryipypi5707hwgax";
+        sha256 = "092hxjbjnq5fmz66grd9plxd0sh6ssg5fhgwwwqbrzgzkjwdycfj";
+        libName = "hermit_abi";
         authors = [
           "Stefan Lankes"
         ];
@@ -4952,6 +4788,7 @@ rec {
         version = "0.4.1";
         edition = "2021";
         sha256 = "0iny5inkixsdr41pm2vkqh3fl66752z5j5c0cdxw16yl9ryjdqkg";
+        libName = "hex_literal";
         authors = [
           "RustCrypto Developers"
         ];
@@ -4975,33 +4812,7 @@ rec {
         ];
 
       };
-      "http 0.2.11" = rec {
-        crateName = "http";
-        version = "0.2.11";
-        edition = "2018";
-        sha256 = "1fwz3mhh86h5kfnr5767jlx9agpdggclq7xsqx930fflzakb2iw9";
-        authors = [
-          "Alex Crichton <alex@alexcrichton.com>"
-          "Carl Lerche <me@carllerche.com>"
-          "Sean McArthur <sean@seanmonstar.com>"
-        ];
-        dependencies = [
-          {
-            name = "bytes";
-            packageId = "bytes";
-          }
-          {
-            name = "fnv";
-            packageId = "fnv";
-          }
-          {
-            name = "itoa";
-            packageId = "itoa";
-          }
-        ];
-
-      };
-      "http 1.1.0" = rec {
+      "http" = rec {
         crateName = "http";
         version = "1.1.0";
         edition = "2018";
@@ -5030,37 +4841,12 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" "std" ];
       };
-      "http-body 0.4.6" = rec {
-        crateName = "http-body";
-        version = "0.4.6";
-        edition = "2018";
-        sha256 = "1lmyjfk6bqk6k9gkn1dxq770sb78pqbqshga241hr5p995bb5skw";
-        authors = [
-          "Carl Lerche <me@carllerche.com>"
-          "Lucio Franco <luciofranco14@gmail.com>"
-          "Sean McArthur <sean@seanmonstar.com>"
-        ];
-        dependencies = [
-          {
-            name = "bytes";
-            packageId = "bytes";
-          }
-          {
-            name = "http";
-            packageId = "http 0.2.11";
-          }
-          {
-            name = "pin-project-lite";
-            packageId = "pin-project-lite";
-          }
-        ];
-
-      };
-      "http-body 1.0.0" = rec {
+      "http-body" = rec {
         crateName = "http-body";
         version = "1.0.0";
         edition = "2018";
         sha256 = "0hyn8n3iadrbwq8y0p1rl1275s4nm49bllw5wji29g4aa3dqbb0w";
+        libName = "http_body";
         authors = [
           "Carl Lerche <me@carllerche.com>"
           "Lucio Franco <luciofranco14@gmail.com>"
@@ -5073,7 +4859,7 @@ rec {
           }
           {
             name = "http";
-            packageId = "http 1.1.0";
+            packageId = "http";
           }
         ];
 
@@ -5083,6 +4869,7 @@ rec {
         version = "0.1.1";
         edition = "2018";
         sha256 = "07agldas2qgcfc05ckiarlmf9vzragbda823nqhrqrc6mjrghx84";
+        libName = "http_body_util";
         authors = [
           "Carl Lerche <me@carllerche.com>"
           "Lucio Franco <luciofranco14@gmail.com>"
@@ -5099,11 +4886,11 @@ rec {
           }
           {
             name = "http";
-            packageId = "http 1.1.0";
+            packageId = "http";
           }
           {
             name = "http-body";
-            packageId = "http-body 1.0.0";
+            packageId = "http-body";
           }
           {
             name = "pin-project-lite";
@@ -5145,114 +4932,7 @@ rec {
         ];
 
       };
-      "hyper 0.14.28" = rec {
-        crateName = "hyper";
-        version = "0.14.28";
-        edition = "2018";
-        sha256 = "107gkvqx4h9bl17d602zkm2dgpfq86l2dr36yzfsi8l3xcsy35mz";
-        authors = [
-          "Sean McArthur <sean@seanmonstar.com>"
-        ];
-        dependencies = [
-          {
-            name = "bytes";
-            packageId = "bytes";
-          }
-          {
-            name = "futures-channel";
-            packageId = "futures-channel";
-          }
-          {
-            name = "futures-core";
-            packageId = "futures-core";
-            usesDefaultFeatures = false;
-          }
-          {
-            name = "futures-util";
-            packageId = "futures-util";
-            usesDefaultFeatures = false;
-          }
-          {
-            name = "h2";
-            packageId = "h2 0.3.26";
-            optional = true;
-          }
-          {
-            name = "http";
-            packageId = "http 0.2.11";
-          }
-          {
-            name = "http-body";
-            packageId = "http-body 0.4.6";
-          }
-          {
-            name = "httparse";
-            packageId = "httparse";
-          }
-          {
-            name = "httpdate";
-            packageId = "httpdate";
-          }
-          {
-            name = "itoa";
-            packageId = "itoa";
-          }
-          {
-            name = "pin-project-lite";
-            packageId = "pin-project-lite";
-          }
-          {
-            name = "socket2";
-            packageId = "socket2";
-            optional = true;
-            features = [ "all" ];
-          }
-          {
-            name = "tokio";
-            packageId = "tokio";
-            features = [ "sync" ];
-          }
-          {
-            name = "tower-service";
-            packageId = "tower-service";
-          }
-          {
-            name = "tracing";
-            packageId = "tracing";
-            usesDefaultFeatures = false;
-            features = [ "std" ];
-          }
-          {
-            name = "want";
-            packageId = "want";
-          }
-        ];
-        devDependencies = [
-          {
-            name = "futures-util";
-            packageId = "futures-util";
-            usesDefaultFeatures = false;
-            features = [ "alloc" ];
-          }
-          {
-            name = "tokio";
-            packageId = "tokio";
-            features = [ "fs" "macros" "io-std" "io-util" "rt" "rt-multi-thread" "sync" "time" "test-util" ];
-          }
-        ];
-        features = {
-          "ffi" = [ "libc" ];
-          "full" = [ "client" "http1" "http2" "server" "stream" "runtime" ];
-          "h2" = [ "dep:h2" ];
-          "http2" = [ "h2" ];
-          "libc" = [ "dep:libc" ];
-          "runtime" = [ "tcp" "tokio/rt" "tokio/time" ];
-          "socket2" = [ "dep:socket2" ];
-          "tcp" = [ "socket2" "tokio/net" "tokio/rt" "tokio/time" ];
-        };
-        resolvedDefaultFeatures = [ "client" "default" "full" "h2" "http1" "http2" "runtime" "server" "socket2" "stream" "tcp" ];
-      };
-      "hyper 1.4.1" = rec {
+      "hyper" = rec {
         crateName = "hyper";
         version = "1.4.1";
         edition = "2021";
@@ -5278,16 +4958,16 @@ rec {
           }
           {
             name = "h2";
-            packageId = "h2 0.4.4";
+            packageId = "h2";
             optional = true;
           }
           {
             name = "http";
-            packageId = "http 1.1.0";
+            packageId = "http";
           }
           {
             name = "http-body";
-            packageId = "http-body 1.0.0";
+            packageId = "http-body";
           }
           {
             name = "httparse";
@@ -5355,86 +5035,12 @@ rec {
         };
         resolvedDefaultFeatures = [ "client" "default" "http1" "http2" "server" ];
       };
-      "hyper-rustls 0.24.2" = rec {
-        crateName = "hyper-rustls";
-        version = "0.24.2";
-        edition = "2021";
-        sha256 = "1475j4a2nczz4aajzzsq3hpwg1zacmzbqg393a14j80ff8izsgpc";
-        dependencies = [
-          {
-            name = "futures-util";
-            packageId = "futures-util";
-            usesDefaultFeatures = false;
-          }
-          {
-            name = "http";
-            packageId = "http 0.2.11";
-          }
-          {
-            name = "hyper";
-            packageId = "hyper 0.14.28";
-            usesDefaultFeatures = false;
-            features = [ "client" ];
-          }
-          {
-            name = "rustls";
-            packageId = "rustls 0.21.12";
-            usesDefaultFeatures = false;
-          }
-          {
-            name = "rustls-native-certs";
-            packageId = "rustls-native-certs 0.6.3";
-            optional = true;
-          }
-          {
-            name = "tokio";
-            packageId = "tokio";
-          }
-          {
-            name = "tokio-rustls";
-            packageId = "tokio-rustls 0.24.1";
-            usesDefaultFeatures = false;
-          }
-        ];
-        devDependencies = [
-          {
-            name = "hyper";
-            packageId = "hyper 0.14.28";
-            features = [ "full" ];
-          }
-          {
-            name = "rustls";
-            packageId = "rustls 0.21.12";
-            usesDefaultFeatures = false;
-            features = [ "tls12" ];
-          }
-          {
-            name = "tokio";
-            packageId = "tokio";
-            features = [ "io-std" "macros" "net" "rt-multi-thread" ];
-          }
-        ];
-        features = {
-          "acceptor" = [ "hyper/server" "tokio-runtime" ];
-          "default" = [ "native-tokio" "http1" "tls12" "logging" "acceptor" ];
-          "http1" = [ "hyper/http1" ];
-          "http2" = [ "hyper/http2" ];
-          "log" = [ "dep:log" ];
-          "logging" = [ "log" "tokio-rustls/logging" "rustls/logging" ];
-          "native-tokio" = [ "tokio-runtime" "rustls-native-certs" ];
-          "rustls-native-certs" = [ "dep:rustls-native-certs" ];
-          "tls12" = [ "tokio-rustls/tls12" "rustls/tls12" ];
-          "tokio-runtime" = [ "hyper/runtime" ];
-          "webpki-roots" = [ "dep:webpki-roots" ];
-          "webpki-tokio" = [ "tokio-runtime" "webpki-roots" ];
-        };
-        resolvedDefaultFeatures = [ "http1" "http2" "rustls-native-certs" "tokio-runtime" ];
-      };
-      "hyper-rustls 0.27.2" = rec {
+      "hyper-rustls" = rec {
         crateName = "hyper-rustls";
         version = "0.27.2";
         edition = "2021";
         sha256 = "0ma1wyfnqnkz7zyr7wpply3xfvlijd0rqqhb6ajs28c9jhnbxr2y";
+        libName = "hyper_rustls";
         dependencies = [
           {
             name = "futures-util";
@@ -5443,11 +5049,11 @@ rec {
           }
           {
             name = "http";
-            packageId = "http 1.1.0";
+            packageId = "http";
           }
           {
             name = "hyper";
-            packageId = "hyper 1.4.1";
+            packageId = "hyper";
             usesDefaultFeatures = false;
           }
           {
@@ -5458,12 +5064,12 @@ rec {
           }
           {
             name = "rustls";
-            packageId = "rustls 0.23.7";
+            packageId = "rustls";
             usesDefaultFeatures = false;
           }
           {
             name = "rustls-native-certs";
-            packageId = "rustls-native-certs 0.7.0";
+            packageId = "rustls-native-certs";
             optional = true;
           }
           {
@@ -5477,7 +5083,7 @@ rec {
           }
           {
             name = "tokio-rustls";
-            packageId = "tokio-rustls 0.26.0";
+            packageId = "tokio-rustls";
             usesDefaultFeatures = false;
           }
           {
@@ -5494,7 +5100,7 @@ rec {
           }
           {
             name = "rustls";
-            packageId = "rustls 0.23.7";
+            packageId = "rustls";
             usesDefaultFeatures = false;
             features = [ "tls12" ];
           }
@@ -5522,59 +5128,19 @@ rec {
         };
         resolvedDefaultFeatures = [ "http1" "http2" "native-tokio" "ring" "rustls-native-certs" "tls12" ];
       };
-      "hyper-timeout 0.4.1" = rec {
-        crateName = "hyper-timeout";
-        version = "0.4.1";
-        edition = "2018";
-        sha256 = "1c8k3g8k2yh1gxvsx9p7amkimgxhl9kafwpj7jyf8ywc5r45ifdv";
-        authors = [
-          "Herman J. Radtke III <herman@hermanradtke.com>"
-        ];
-        dependencies = [
-          {
-            name = "hyper";
-            packageId = "hyper 0.14.28";
-            features = [ "client" ];
-          }
-          {
-            name = "pin-project-lite";
-            packageId = "pin-project-lite";
-          }
-          {
-            name = "tokio";
-            packageId = "tokio";
-          }
-          {
-            name = "tokio-io-timeout";
-            packageId = "tokio-io-timeout";
-          }
-        ];
-        devDependencies = [
-          {
-            name = "hyper";
-            packageId = "hyper 0.14.28";
-            features = [ "client" "http1" "tcp" ];
-          }
-          {
-            name = "tokio";
-            packageId = "tokio";
-            features = [ "io-std" "io-util" "macros" ];
-          }
-        ];
-
-      };
-      "hyper-timeout 0.5.1" = rec {
+      "hyper-timeout" = rec {
         crateName = "hyper-timeout";
         version = "0.5.1";
         edition = "2018";
         sha256 = "14rpyv9zz0ncadn9qgmnjz0hiqk3nav7hglkk1a6yfy8wmhsj0rj";
+        libName = "hyper_timeout";
         authors = [
           "Herman J. Radtke III <herman@hermanradtke.com>"
         ];
         dependencies = [
           {
             name = "hyper";
-            packageId = "hyper 1.4.1";
+            packageId = "hyper";
           }
           {
             name = "hyper-util";
@@ -5597,7 +5163,7 @@ rec {
         devDependencies = [
           {
             name = "hyper";
-            packageId = "hyper 1.4.1";
+            packageId = "hyper";
             features = [ "http1" ];
           }
           {
@@ -5610,9 +5176,9 @@ rec {
       };
       "hyper-util" = rec {
         crateName = "hyper-util";
-        version = "0.1.6";
+        version = "0.1.7";
         edition = "2021";
-        sha256 = "0mm9biwckhwqz23s5dp02410aw7sw2ibg167jzwv8kzf957jzf9s";
+        sha256 = "1fg9h591skksq5zxnffyisj7487jhdcgj6c7bvlkckn535bhbryd";
         libName = "hyper_util";
         authors = [
           "Sean McArthur <sean@seanmonstar.com>"
@@ -5634,15 +5200,15 @@ rec {
           }
           {
             name = "http";
-            packageId = "http 1.1.0";
+            packageId = "http";
           }
           {
             name = "http-body";
-            packageId = "http-body 1.0.0";
+            packageId = "http-body";
           }
           {
             name = "hyper";
-            packageId = "hyper 1.4.1";
+            packageId = "hyper";
           }
           {
             name = "pin-project-lite";
@@ -5687,7 +5253,7 @@ rec {
           }
           {
             name = "hyper";
-            packageId = "hyper 1.4.1";
+            packageId = "hyper";
             features = [ "full" ];
           }
           {
@@ -5715,6 +5281,7 @@ rec {
         version = "0.1.60";
         edition = "2018";
         sha256 = "0hdid5xz3jznm04lysjm3vi93h3c523w0hcc3xba47jl3ddbpzz7";
+        libName = "iana_time_zone";
         authors = [
           "Andrew Straw <strawman@astraw.com>"
           "René Kijewski <rene.kijewski@fu-berlin.de>"
@@ -5760,6 +5327,7 @@ rec {
         version = "0.1.2";
         edition = "2018";
         sha256 = "17r6jmj31chn7xs9698r122mapq85mfnv98bb4pg6spm0si2f67k";
+        libName = "iana_time_zone_haiku";
         authors = [
           "René Kijewski <crates.io@k6i.de>"
         ];
@@ -5808,95 +5376,6 @@ rec {
         };
         resolvedDefaultFeatures = [ "alloc" "default" "std" ];
       };
-      "imbl" = rec {
-        crateName = "imbl";
-        version = "3.0.0";
-        edition = "2018";
-        sha256 = "1sw1vw8qysyr9cxyxfi3fal9ykf46zw337w4n533mwrnrpcfhfxw";
-        authors = [
-          "Bodil Stokke <bodil@bodil.org>"
-          "Joe Neeman <joeneeman@gmail.com>"
-        ];
-        dependencies = [
-          {
-            name = "bitmaps";
-            packageId = "bitmaps";
-          }
-          {
-            name = "imbl-sized-chunks";
-            packageId = "imbl-sized-chunks";
-          }
-          {
-            name = "proptest";
-            packageId = "proptest";
-            optional = true;
-          }
-          {
-            name = "rand_core";
-            packageId = "rand_core";
-          }
-          {
-            name = "rand_xoshiro";
-            packageId = "rand_xoshiro";
-          }
-          {
-            name = "serde";
-            packageId = "serde";
-            optional = true;
-          }
-        ];
-        buildDependencies = [
-          {
-            name = "version_check";
-            packageId = "version_check";
-          }
-        ];
-        devDependencies = [
-          {
-            name = "proptest";
-            packageId = "proptest";
-          }
-          {
-            name = "serde";
-            packageId = "serde";
-          }
-        ];
-        features = {
-          "arbitrary" = [ "dep:arbitrary" ];
-          "proptest" = [ "dep:proptest" ];
-          "quickcheck" = [ "dep:quickcheck" ];
-          "rayon" = [ "dep:rayon" ];
-          "refpool" = [ "dep:refpool" ];
-          "serde" = [ "dep:serde" ];
-          "triomphe" = [ "dep:triomphe" ];
-        };
-        resolvedDefaultFeatures = [ "proptest" "serde" ];
-      };
-      "imbl-sized-chunks" = rec {
-        crateName = "imbl-sized-chunks";
-        version = "0.1.2";
-        edition = "2021";
-        sha256 = "0qzdw55na2w6fd44p7y9rh05nxa98gzpaigmwg57sy7db3xhch0l";
-        authors = [
-          "Bodil Stokke <bodil@bodil.org>"
-          "Joe Neeman <joeneeman@gmail.com>"
-        ];
-        dependencies = [
-          {
-            name = "bitmaps";
-            packageId = "bitmaps";
-            usesDefaultFeatures = false;
-          }
-        ];
-        features = {
-          "arbitrary" = [ "dep:arbitrary" ];
-          "array-ops" = [ "dep:array-ops" ];
-          "default" = [ "std" ];
-          "refpool" = [ "dep:refpool" ];
-          "ringbuffer" = [ "array-ops" ];
-        };
-        resolvedDefaultFeatures = [ "default" "std" ];
-      };
       "indexmap 1.9.3" = rec {
         crateName = "indexmap";
         version = "1.9.3";
@@ -6070,6 +5549,7 @@ rec {
         version = "0.4.10";
         edition = "2018";
         sha256 = "0m9la3f7cs77y85nkbcjsxkb7k861fc6bdhahyfidgh7gljh1b8b";
+        libName = "is_terminal";
         authors = [
           "softprops <d.tangren@gmail.com>"
           "Dan Gohman <dev@sunfishcode.online>"
@@ -6103,6 +5583,14 @@ rec {
         ];
 
       };
+      "is_terminal_polyfill" = rec {
+        crateName = "is_terminal_polyfill";
+        version = "1.70.1";
+        edition = "2021";
+        sha256 = "1kwfgglh91z33kl0w5i338mfpa3zs0hidq5j4ny4rmjwrikchhvr";
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
       "itertools 0.10.5" = rec {
         crateName = "itertools";
         version = "0.10.5";
@@ -6124,11 +5612,11 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" "use_alloc" "use_std" ];
       };
-      "itertools 0.11.0" = rec {
+      "itertools 0.12.1" = rec {
         crateName = "itertools";
-        version = "0.11.0";
+        version = "0.12.1";
         edition = "2018";
-        sha256 = "0mzyqcc59azx9g5cg6fs8k529gvh4463smmka6jvzs3cd2jp7hdi";
+        sha256 = "0s95jbb3ndj1lvfxyq5wanc0fm0r6hg6q4ngb92qlfdxvci10ads";
         authors = [
           "bluss"
         ];
@@ -6143,13 +5631,13 @@ rec {
           "default" = [ "use_std" ];
           "use_std" = [ "use_alloc" "either/use_std" ];
         };
-        resolvedDefaultFeatures = [ "use_alloc" ];
+        resolvedDefaultFeatures = [ "default" "use_alloc" "use_std" ];
       };
-      "itertools 0.12.0" = rec {
+      "itertools 0.13.0" = rec {
         crateName = "itertools";
-        version = "0.12.0";
+        version = "0.13.0";
         edition = "2018";
-        sha256 = "1c07gzdlc6a1c8p8jrvvw3gs52bss3y58cs2s21d9i978l36pnr5";
+        sha256 = "11hiy3qzl643zcigknclh446qb9zlg4dpdzfkjaa9q9fqpgyfgj1";
         authors = [
           "bluss"
         ];
@@ -6180,9 +5668,9 @@ rec {
       };
       "jobserver" = rec {
         crateName = "jobserver";
-        version = "0.1.27";
-        edition = "2018";
-        sha256 = "0z9w6vfqwbr6hfk9yaw7kydlh6f7k39xdlszxlh39in4acwzcdwc";
+        version = "0.1.32";
+        edition = "2021";
+        sha256 = "1l2k50qmj84x9mn39ivjz76alqmx72jhm12rw33zx9xnpv5xpla8";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -6200,6 +5688,7 @@ rec {
         version = "0.3.67";
         edition = "2018";
         sha256 = "1lar78p13w781b4zf44a0sk26i461fczbdrhpan6kjav4gqkc7cs";
+        libName = "js_sys";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -6213,9 +5702,9 @@ rec {
       };
       "lazy_static" = rec {
         crateName = "lazy_static";
-        version = "1.4.0";
+        version = "1.5.0";
         edition = "2015";
-        sha256 = "0in6ikhw8mgl33wjv6q6xfrb5b9jr16q8ygjy803fay4zcisvaz2";
+        sha256 = "1zk6dqqni0193xg6iijh7i3i44sryglwgvx20spdvwk3r6sbrlmv";
         authors = [
           "Marvin Löbel <loebel.marvin@gmail.com>"
         ];
@@ -6229,6 +5718,7 @@ rec {
         version = "0.8.5";
         edition = "2018";
         sha256 = "0ihf0x3vrk25fq3bv9q35m0xax0wmvwkh0j0pjm2yk4ddvh5vpic";
+        libName = "lexical_core";
         authors = [
           "Alex Huszagh <ahuszagh@gmail.com>"
         ];
@@ -6291,6 +5781,7 @@ rec {
         version = "0.8.5";
         edition = "2018";
         sha256 = "0py0gp8hlzcrlvjqmqlpl2v1as65iiqxq2xsabxvhc01pmg3lfv8";
+        libName = "lexical_parse_float";
         authors = [
           "Alex Huszagh <ahuszagh@gmail.com>"
         ];
@@ -6331,6 +5822,7 @@ rec {
         version = "0.8.6";
         edition = "2018";
         sha256 = "1sayji3mpvb2xsjq56qcq3whfz8px9a6fxk5v7v15hyhbr4982bd";
+        libName = "lexical_parse_integer";
         authors = [
           "Alex Huszagh <ahuszagh@gmail.com>"
         ];
@@ -6362,6 +5854,7 @@ rec {
         version = "0.8.5";
         edition = "2018";
         sha256 = "1z73qkv7yxhsbc4aiginn1dqmsj8jarkrdlyxc88g2gz2vzvjmaj";
+        libName = "lexical_util";
         authors = [
           "Alex Huszagh <ahuszagh@gmail.com>"
         ];
@@ -6388,6 +5881,7 @@ rec {
         version = "0.8.5";
         edition = "2018";
         sha256 = "0qk825l0csvnksh9sywb51996cjc2bylq6rxjaiha7sqqjhvmjmc";
+        libName = "lexical_write_float";
         authors = [
           "Alex Huszagh <ahuszagh@gmail.com>"
         ];
@@ -6428,6 +5922,7 @@ rec {
         version = "0.8.5";
         edition = "2018";
         sha256 = "0ii4hmvqrg6pd4j9y1pkhkp0nw2wpivjzmljh6v6ca22yk8z7dp1";
+        libName = "lexical_write_integer";
         authors = [
           "Alex Huszagh <ahuszagh@gmail.com>"
         ];
@@ -6456,9 +5951,9 @@ rec {
       };
       "libc" = rec {
         crateName = "libc";
-        version = "0.2.152";
+        version = "0.2.158";
         edition = "2015";
-        sha256 = "1rsnma7hnw22w7jh9yqg43slddvfbnfzrvm3s7s4kinbj1jvzqqk";
+        sha256 = "0fb4qldw1jrxljrwz6bsjn8lv4rqizlqmab41q3j98q332xw9bfq";
         authors = [
           "The Rust Project Developers"
         ];
@@ -6490,6 +5985,7 @@ rec {
         edition = "2018";
         links = "mimalloc";
         sha256 = "0i3b0dzz7cp0ik7ys66q92r16va78gwlbrnxhj5fnkdxsc8niai3";
+        libName = "libmimalloc_sys";
         authors = [
           "Octavian Oncescu <octavonce@gmail.com>"
         ];
@@ -6521,7 +6017,7 @@ rec {
         dependencies = [
           {
             name = "bitflags";
-            packageId = "bitflags 2.4.2";
+            packageId = "bitflags 2.6.0";
           }
           {
             name = "libc";
@@ -6540,9 +6036,10 @@ rec {
       };
       "linux-raw-sys" = rec {
         crateName = "linux-raw-sys";
-        version = "0.4.13";
+        version = "0.4.14";
         edition = "2021";
-        sha256 = "172k2c6422gsc914ig8rh99mb9yc7siw6ikc3d9xw1k7vx0s3k81";
+        sha256 = "12gsjgbhhjwywpqcrizv80vrp7p7grsz5laqq773i33wphjsxcvq";
+        libName = "linux_raw_sys";
         authors = [
           "Dan Gohman <dev@sunfishcode.online>"
         ];
@@ -6661,9 +6158,9 @@ rec {
       };
       "lru" = rec {
         crateName = "lru";
-        version = "0.12.3";
+        version = "0.12.4";
         edition = "2015";
-        sha256 = "1p5hryc967wdh56q9wzb2x9gdqy3yd0sqmnb2fcf7z28wrsjw9nk";
+        sha256 = "017rzh4kyl3j79sj0qc35wallblsqbnkzxpn6i3xkrv02y4kkvip";
         authors = [
           "Jerome Froelich <jeromefroelic@hotmail.com>"
         ];
@@ -6687,6 +6184,7 @@ rec {
         edition = "2018";
         links = "lzma";
         sha256 = "09sxp20waxyglgn3cjz8qjkspb3ryz2fwx4rigkwvrk46ymh9njz";
+        libName = "lzma_sys";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -6724,7 +6222,7 @@ rec {
         dependencies = [
           {
             name = "bitflags";
-            packageId = "bitflags 2.4.2";
+            packageId = "bitflags 2.6.0";
           }
           {
             name = "libc";
@@ -6748,6 +6246,7 @@ rec {
         edition = "2015";
         links = "magic";
         sha256 = "1g5k9d9igxv4h23nbhp8bqa5gdpkd3ahgm0rh5i0s54mi3h6my7g";
+        libName = "magic_sys";
         authors = [
           "robo9k <robo9k@symlink.io>"
         ];
@@ -6952,6 +6451,7 @@ rec {
         version = "0.2.1";
         edition = "2018";
         sha256 = "16ppc5g84aijpri4jzv14rvcnslvlpphbszc7zzp6vfkddf4qdb8";
+        libName = "minimal_lexical";
         authors = [
           "Alex Huszagh <ahuszagh@gmail.com>"
         ];
@@ -6987,7 +6487,7 @@ rec {
         };
         resolvedDefaultFeatures = [ "with-alloc" ];
       };
-      "mio" = rec {
+      "mio 0.8.11" = rec {
         crateName = "mio";
         version = "0.8.11";
         edition = "2018";
@@ -7030,7 +6530,53 @@ rec {
           "log" = [ "dep:log" ];
           "os-ext" = [ "os-poll" "windows-sys/Win32_System_Pipes" "windows-sys/Win32_Security" ];
         };
-        resolvedDefaultFeatures = [ "default" "log" "net" "os-ext" "os-poll" ];
+        resolvedDefaultFeatures = [ "default" "log" "os-ext" "os-poll" ];
+      };
+      "mio 1.0.2" = rec {
+        crateName = "mio";
+        version = "1.0.2";
+        edition = "2021";
+        sha256 = "1v1cnnn44awxbcfm4zlavwgkvbyg7gp5zzjm8mqf1apkrwflvq40";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "hermit-abi";
+            packageId = "hermit-abi";
+            rename = "libc";
+            target = { target, features }: ("hermit" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "wasi";
+            packageId = "wasi";
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Wdk_Foundation" "Wdk_Storage_FileSystem" "Wdk_System_IO" "Win32_Foundation" "Win32_Networking_WinSock" "Win32_Storage_FileSystem" "Win32_System_IO" "Win32_System_WindowsProgramming" ];
+          }
+        ];
+        features = {
+          "default" = [ "log" ];
+          "log" = [ "dep:log" ];
+          "os-ext" = [ "os-poll" "windows-sys/Win32_System_Pipes" "windows-sys/Win32_Security" ];
+        };
+        resolvedDefaultFeatures = [ "net" "os-ext" "os-poll" ];
       };
       "multimap" = rec {
         crateName = "multimap";
@@ -7058,10 +6604,11 @@ rec {
           }
         ];
         src = lib.cleanSourceWith { filter = sourceFilter; src = ./nar-bridge; };
+        libName = "nar_bridge";
         dependencies = [
           {
             name = "axum";
-            packageId = "axum 0.7.5";
+            packageId = "axum";
             features = [ "http2" ];
           }
           {
@@ -7083,7 +6630,7 @@ rec {
           }
           {
             name = "itertools";
-            packageId = "itertools 0.12.0";
+            packageId = "itertools 0.12.1";
           }
           {
             name = "lru";
@@ -7104,7 +6651,7 @@ rec {
           }
           {
             name = "prost";
-            packageId = "prost 0.13.1";
+            packageId = "prost";
           }
           {
             name = "serde";
@@ -7131,7 +6678,7 @@ rec {
           }
           {
             name = "tonic";
-            packageId = "tonic 0.12.1";
+            packageId = "tonic";
             features = [ "tls" "tls-roots" ];
           }
           {
@@ -7172,11 +6719,11 @@ rec {
         buildDependencies = [
           {
             name = "prost-build";
-            packageId = "prost-build 0.13.1";
+            packageId = "prost-build";
           }
           {
             name = "tonic-build";
-            packageId = "tonic-build 0.12.1";
+            packageId = "tonic-build";
           }
         ];
         devDependencies = [
@@ -7358,7 +6905,7 @@ rec {
         dependencies = [
           {
             name = "bitflags";
-            packageId = "bitflags 2.4.2";
+            packageId = "bitflags 2.6.0";
           }
           {
             name = "cfg-if";
@@ -7400,10 +6947,11 @@ rec {
           }
         ];
         src = lib.cleanSourceWith { filter = sourceFilter; src = ./nix-compat; };
+        libName = "nix_compat";
         dependencies = [
           {
             name = "bitflags";
-            packageId = "bitflags 2.4.2";
+            packageId = "bitflags 2.6.0";
           }
           {
             name = "bstr";
@@ -7411,6 +6959,11 @@ rec {
             features = [ "alloc" "unicode" "serde" ];
           }
           {
+            name = "bytes";
+            packageId = "bytes";
+            optional = true;
+          }
+          {
             name = "data-encoding";
             packageId = "data-encoding";
           }
@@ -7435,6 +6988,11 @@ rec {
             packageId = "mimalloc";
           }
           {
+            name = "nix-compat-derive";
+            packageId = "nix-compat-derive";
+            optional = true;
+          }
+          {
             name = "nom";
             packageId = "nom";
           }
@@ -7470,6 +7028,10 @@ rec {
             optional = true;
             features = [ "io-util" "macros" ];
           }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
         ];
         devDependencies = [
           {
@@ -7480,8 +7042,6 @@ rec {
           {
             name = "futures";
             packageId = "futures";
-            usesDefaultFeatures = false;
-            features = [ "executor" ];
           }
           {
             name = "hex-literal";
@@ -7508,6 +7068,10 @@ rec {
             packageId = "serde_json";
           }
           {
+            name = "smol_str";
+            packageId = "smol_str";
+          }
+          {
             name = "tokio-test";
             packageId = "tokio-test";
           }
@@ -7518,12 +7082,127 @@ rec {
         ];
         features = {
           "async" = [ "tokio" ];
-          "default" = [ "async" "wire" ];
+          "bytes" = [ "dep:bytes" ];
+          "default" = [ "async" "wire" "nix-compat-derive" ];
+          "nix-compat-derive" = [ "dep:nix-compat-derive" ];
           "pin-project-lite" = [ "dep:pin-project-lite" ];
           "tokio" = [ "dep:tokio" ];
-          "wire" = [ "tokio" "pin-project-lite" ];
+          "wire" = [ "tokio" "pin-project-lite" "bytes" ];
         };
-        resolvedDefaultFeatures = [ "async" "default" "pin-project-lite" "tokio" "wire" ];
+        resolvedDefaultFeatures = [ "async" "bytes" "default" "nix-compat-derive" "pin-project-lite" "test" "tokio" "wire" ];
+      };
+      "nix-compat-derive" = rec {
+        crateName = "nix-compat-derive";
+        version = "0.1.0";
+        edition = "2021";
+        src = lib.cleanSourceWith { filter = sourceFilter; src = ./nix-compat-derive; };
+        procMacro = true;
+        libName = "nix_compat_derive";
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            features = [ "proc-macro" ];
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+            features = [ "proc-macro" ];
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.76";
+            features = [ "full" "extra-traits" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "hex-literal";
+            packageId = "hex-literal";
+          }
+          {
+            name = "nix-compat";
+            packageId = "nix-compat";
+            usesDefaultFeatures = false;
+            features = [ "async" "wire" "test" ];
+          }
+          {
+            name = "pretty_assertions";
+            packageId = "pretty_assertions";
+          }
+          {
+            name = "rstest";
+            packageId = "rstest";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "io-util" "macros" ];
+          }
+          {
+            name = "tokio-test";
+            packageId = "tokio-test";
+          }
+        ];
+
+      };
+      "nix-compat-derive-tests" = rec {
+        crateName = "nix-compat-derive-tests";
+        version = "0.1.0";
+        edition = "2021";
+        src = lib.cleanSourceWith { filter = sourceFilter; src = ./nix-compat-derive-tests; };
+        devDependencies = [
+          {
+            name = "hex-literal";
+            packageId = "hex-literal";
+          }
+          {
+            name = "nix-compat";
+            packageId = "nix-compat";
+            features = [ "test" "wire" ];
+          }
+          {
+            name = "nix-compat-derive";
+            packageId = "nix-compat-derive";
+          }
+          {
+            name = "pretty_assertions";
+            packageId = "pretty_assertions";
+          }
+          {
+            name = "rstest";
+            packageId = "rstest";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "io-util" "macros" ];
+          }
+          {
+            name = "tokio-test";
+            packageId = "tokio-test";
+          }
+          {
+            name = "trybuild";
+            packageId = "trybuild";
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "compile-tests" ];
+      };
+      "nohash-hasher" = rec {
+        crateName = "nohash-hasher";
+        version = "0.2.0";
+        edition = "2018";
+        sha256 = "0lf4p6k01w4wm7zn4grnihzj8s7zd5qczjmzng7wviwxawih5x9b";
+        libName = "nohash_hasher";
+        authors = [
+          "Parity Technologies <admin@parity.io>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
       };
       "nom" = rec {
         crateName = "nom";
@@ -7575,6 +7254,7 @@ rec {
         version = "0.46.0";
         edition = "2018";
         sha256 = "115sywxh53p190lyw97alm14nc004qj5jm5lvdj608z84rbida3p";
+        libName = "nu_ansi_term";
         authors = [
           "ogham@bsago.me"
           "Ryan Scheel (Havvy) <ryan.havvy@gmail.com>"
@@ -7603,6 +7283,7 @@ rec {
         version = "0.1.0";
         edition = "2021";
         sha256 = "1ndiyg82q73783jq18isi71a7mjh56wxrk52rlvyx0mi5z9ibmai";
+        libName = "num_conv";
         authors = [
           "Jacob Pratt <jacob@jhpratt.dev>"
         ];
@@ -7610,9 +7291,10 @@ rec {
       };
       "num-traits" = rec {
         crateName = "num-traits";
-        version = "0.2.18";
-        edition = "2018";
-        sha256 = "0yjib8p2p9kzmaz48xwhs69w5dh1wipph9jgnillzd2x33jz03fs";
+        version = "0.2.19";
+        edition = "2021";
+        sha256 = "0h984rhdkkqd4ny9cif7y2azl3xdfb7768hb9irhpsch4q3gq787";
+        libName = "num_traits";
         authors = [
           "The Rust Project Developers"
         ];
@@ -7704,9 +7386,9 @@ rec {
       };
       "object_store" = rec {
         crateName = "object_store";
-        version = "0.10.1";
+        version = "0.10.2";
         edition = "2021";
-        sha256 = "1mkag13fnzcgn52smi9h7ssf53as04w9373sz83ikfhkq8rgvszv";
+        sha256 = "1wz3m20hqs3v93dyxcqy7qpsbd4rqp6050hy49wcw5f740l4bnp6";
         dependencies = [
           {
             name = "async-trait";
@@ -7739,13 +7421,13 @@ rec {
           }
           {
             name = "hyper";
-            packageId = "hyper 1.4.1";
+            packageId = "hyper";
             optional = true;
             usesDefaultFeatures = false;
           }
           {
             name = "itertools";
-            packageId = "itertools 0.12.0";
+            packageId = "itertools 0.13.0";
           }
           {
             name = "md-5";
@@ -7790,7 +7472,7 @@ rec {
           }
           {
             name = "rustls-pemfile";
-            packageId = "rustls-pemfile 2.1.0";
+            packageId = "rustls-pemfile";
             optional = true;
             usesDefaultFeatures = false;
             features = [ "std" ];
@@ -7833,7 +7515,7 @@ rec {
         devDependencies = [
           {
             name = "hyper";
-            packageId = "hyper 1.4.1";
+            packageId = "hyper";
             features = [ "server" ];
           }
           {
@@ -7895,6 +7577,7 @@ rec {
         version = "0.1.5";
         edition = "2015";
         sha256 = "1kq18qm48rvkwgcggfkqq6pm948190czqc94d6bm2sir5hq1l0gz";
+        libName = "openssl_probe";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -7995,6 +7678,7 @@ rec {
         version = "0.13.0";
         edition = "2021";
         sha256 = "1avqmyh42apakbkhjij3c9hl0brnq5v37zk4kpxkhdgf8kgfjcdd";
+        libName = "opentelemetry_http";
         dependencies = [
           {
             name = "async-trait";
@@ -8006,7 +7690,7 @@ rec {
           }
           {
             name = "http";
-            packageId = "http 1.1.0";
+            packageId = "http";
             usesDefaultFeatures = false;
             features = [ "std" ];
           }
@@ -8028,6 +7712,7 @@ rec {
         version = "0.17.0";
         edition = "2021";
         sha256 = "09z70ygp6lfcplnwx7cgf3p3fyq2arkvhxhj8avnz4gv5xh5m4kb";
+        libName = "opentelemetry_otlp";
         dependencies = [
           {
             name = "async-trait";
@@ -8039,7 +7724,7 @@ rec {
           }
           {
             name = "http";
-            packageId = "http 1.1.0";
+            packageId = "http";
             optional = true;
             usesDefaultFeatures = false;
             features = [ "std" ];
@@ -8061,7 +7746,7 @@ rec {
           }
           {
             name = "prost";
-            packageId = "prost 0.13.1";
+            packageId = "prost";
             optional = true;
           }
           {
@@ -8078,7 +7763,7 @@ rec {
           }
           {
             name = "tonic";
-            packageId = "tonic 0.12.1";
+            packageId = "tonic";
             optional = true;
             usesDefaultFeatures = false;
           }
@@ -8125,6 +7810,7 @@ rec {
         version = "0.7.0";
         edition = "2021";
         sha256 = "1nahv1dflvwdgi4c4p7ikd59x0yyivf85w02398q9jgrpwh9zvih";
+        libName = "opentelemetry_proto";
         dependencies = [
           {
             name = "opentelemetry";
@@ -8138,12 +7824,12 @@ rec {
           }
           {
             name = "prost";
-            packageId = "prost 0.13.1";
+            packageId = "prost";
             optional = true;
           }
           {
             name = "tonic";
-            packageId = "tonic 0.12.1";
+            packageId = "tonic";
             optional = true;
             usesDefaultFeatures = false;
             features = [ "codegen" "prost" ];
@@ -8372,6 +8058,7 @@ rec {
         version = "4.2.0";
         edition = "2021";
         sha256 = "0kjqcvvbcsibbx3hnj7ag06bd9gv2zfi5ja6rgyh2kbxbh3zfvd7";
+        libName = "ordered_float";
         authors = [
           "Jonathan Reem <jonathan.reem@gmail.com>"
           "Matt Brubeck <mbrubeck@limpet.net>"
@@ -8600,6 +8287,7 @@ rec {
         version = "0.1.0";
         edition = "2015";
         sha256 = "1pcgqxw0mgg3ha5hi5xkjhyjf488bw5rw1g3qlr9awbq4szh3fpc";
+        libName = "path_clean";
         authors = [
           "Dan Reeves <hey@danreev.es>"
         ];
@@ -8610,6 +8298,7 @@ rec {
         version = "2.3.1";
         edition = "2018";
         sha256 = "0gi8wgx0dcy8rnv1kywdv98lwcx67hz0a0zwpib5v2i08r88y573";
+        libName = "percent_encoding";
         authors = [
           "The rust-url developers"
         ];
@@ -8621,9 +8310,9 @@ rec {
       };
       "petgraph" = rec {
         crateName = "petgraph";
-        version = "0.6.4";
+        version = "0.6.5";
         edition = "2018";
-        sha256 = "1ac6wfq5f5pzcv0nvzzfgjbwg2kwslpnzsw5wcmxlscfcb9azlz1";
+        sha256 = "1ns7mbxidnn2pqahbbjccxkrqkrll2i5rbxx43ns6rh6fn3cridl";
         authors = [
           "bluss"
           "mitchmindtree"
@@ -8640,9 +8329,10 @@ rec {
           }
         ];
         features = {
-          "all" = [ "unstable" "quickcheck" "matrix_graph" "stable_graph" "graphmap" ];
+          "all" = [ "unstable" "quickcheck" "matrix_graph" "stable_graph" "graphmap" "rayon" ];
           "default" = [ "graphmap" "stable_graph" "matrix_graph" ];
           "quickcheck" = [ "dep:quickcheck" ];
+          "rayon" = [ "dep:rayon" "indexmap/rayon" ];
           "serde" = [ "dep:serde" ];
           "serde-1" = [ "serde" "serde_derive" ];
           "serde_derive" = [ "dep:serde_derive" ];
@@ -8655,6 +8345,7 @@ rec {
         version = "1.1.3";
         edition = "2021";
         sha256 = "08k4cpy8q3j93qqgnrbzkcgpn7g0a88l4a9nm33kyghpdhffv97x";
+        libName = "pin_project";
         dependencies = [
           {
             name = "pin-project-internal";
@@ -8669,6 +8360,7 @@ rec {
         edition = "2021";
         sha256 = "01a4l3vb84brv9v7wl71chzxra2kynm6yvcjca66xv3ij6fgsna3";
         procMacro = true;
+        libName = "pin_project_internal";
         dependencies = [
           {
             name = "proc-macro2";
@@ -8680,7 +8372,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
             features = [ "full" "visit-mut" ];
           }
         ];
@@ -8688,9 +8380,10 @@ rec {
       };
       "pin-project-lite" = rec {
         crateName = "pin-project-lite";
-        version = "0.2.13";
+        version = "0.2.14";
         edition = "2018";
-        sha256 = "0n0bwr5qxlf0mhn2xkl36sy55118s9qmvx2yl5f3ixkb007lbywa";
+        sha256 = "00nx3f04agwjlsmd3mc5rx5haibj2v8q9b52b0kwn63wcv4nz9mx";
+        libName = "pin_project_lite";
 
       };
       "pin-utils" = rec {
@@ -8698,6 +8391,7 @@ rec {
         version = "0.1.0";
         edition = "2018";
         sha256 = "117ir7vslsl2z1a7qzhws4pd01cg2d3338c47swjyvqv2n60v1wb";
+        libName = "pin_utils";
         authors = [
           "Josef Brandl <mail@josefbrandl.de>"
         ];
@@ -8777,6 +8471,7 @@ rec {
         version = "0.3.29";
         edition = "2015";
         sha256 = "1jy6158v1316khkpmq2sjj1vgbnbnw51wffx7p0k0l9h9vlys019";
+        libName = "pkg_config";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -8862,6 +8557,7 @@ rec {
         version = "0.3.5";
         edition = "2018";
         sha256 = "02cn98gsj2i1bwrfsymifmyas1wn2gibdm9mk8w82x9s9n5n4xly";
+        libName = "plotters_backend";
         authors = [
           "Hao Hou <haohou302@gmail.com>"
         ];
@@ -8872,6 +8568,7 @@ rec {
         version = "0.3.5";
         edition = "2018";
         sha256 = "1axbw82frs5di4drbyzihr5j35wpy2a75hp3f49p186cjfcd7xiq";
+        libName = "plotters_svg";
         authors = [
           "Hao Hou <haohou302@gmail.com>"
         ];
@@ -8936,6 +8633,7 @@ rec {
         version = "1.6.0";
         edition = "2018";
         sha256 = "1h77x9qx7pns0d66vdrmdbmwpi7586h7ysnkdnhrn5mwi2cyyw3i";
+        libName = "portable_atomic";
         features = {
           "critical-section" = [ "dep:critical-section" ];
           "default" = [ "fallback" ];
@@ -8962,6 +8660,7 @@ rec {
         version = "0.2.17";
         edition = "2018";
         sha256 = "1pp6g52aw970adv3x2310n7glqnji96z0a9wiamzw89ibf0ayh2v";
+        libName = "ppv_lite86";
         authors = [
           "The CryptoCorrosion Contributors"
         ];
@@ -9012,7 +8711,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
             usesDefaultFeatures = false;
             features = [ "full" ];
           }
@@ -9025,7 +8724,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
             usesDefaultFeatures = false;
             features = [ "parsing" ];
           }
@@ -9036,9 +8735,10 @@ rec {
       };
       "proc-macro2" = rec {
         crateName = "proc-macro2";
-        version = "1.0.76";
+        version = "1.0.86";
         edition = "2021";
-        sha256 = "136cp0fgl6rg5ljm3b1xpc0bn0lyvagzzmxvbxgk5hxml36mdz4m";
+        sha256 = "0xrv22p8lqlfdf1w0pj4si8n2ws4aw0kilmziwf0vpv5ys6rwway";
+        libName = "proc_macro2";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
           "Alex Crichton <alex@alexcrichton.com>"
@@ -9056,26 +8756,16 @@ rec {
       };
       "proptest" = rec {
         crateName = "proptest";
-        version = "1.4.0";
+        version = "1.5.0";
         edition = "2018";
-        sha256 = "1gzmw40pgmwzb7x6jsyr88z5w151snv5rp1g0dlcp1iw3h9pdd1i";
+        sha256 = "13gm7mphs95cw4gbgk5qiczkmr68dvcwhp58gmiz33dq2ccm3hml";
         authors = [
           "Jason Lingle"
         ];
         dependencies = [
           {
-            name = "bit-set";
-            packageId = "bit-set";
-            optional = true;
-          }
-          {
-            name = "bit-vec";
-            packageId = "bit-vec";
-            optional = true;
-          }
-          {
             name = "bitflags";
-            packageId = "bitflags 2.4.2";
+            packageId = "bitflags 2.6.0";
           }
           {
             name = "lazy_static";
@@ -9109,12 +8799,6 @@ rec {
             optional = true;
           }
           {
-            name = "rusty-fork";
-            packageId = "rusty-fork";
-            optional = true;
-            usesDefaultFeatures = false;
-          }
-          {
             name = "tempfile";
             packageId = "tempfile";
             optional = true;
@@ -9125,12 +8809,14 @@ rec {
           }
         ];
         features = {
+          "attr-macro" = [ "proptest-macro" ];
           "bit-set" = [ "dep:bit-set" "dep:bit-vec" ];
           "default" = [ "std" "fork" "timeout" "bit-set" ];
           "default-code-coverage" = [ "std" "fork" "timeout" "bit-set" ];
           "fork" = [ "std" "rusty-fork" "tempfile" ];
           "hardware-rng" = [ "x86" ];
           "lazy_static" = [ "dep:lazy_static" ];
+          "proptest-macro" = [ "dep:proptest-macro" ];
           "regex-syntax" = [ "dep:regex-syntax" ];
           "rusty-fork" = [ "dep:rusty-fork" ];
           "std" = [ "rand/std" "lazy_static" "regex-syntax" "num-traits/std" ];
@@ -9138,37 +8824,9 @@ rec {
           "timeout" = [ "fork" "rusty-fork/timeout" ];
           "x86" = [ "dep:x86" ];
         };
-        resolvedDefaultFeatures = [ "alloc" "bit-set" "default" "fork" "lazy_static" "regex-syntax" "rusty-fork" "std" "tempfile" "timeout" ];
-      };
-      "prost 0.12.3" = rec {
-        crateName = "prost";
-        version = "0.12.3";
-        edition = "2021";
-        sha256 = "0jmrhlb4jkiylz72xb14vlkfbmlq0jwv7j20ini9harhvaf2hv0l";
-        authors = [
-          "Dan Burkert <dan@danburkert.com>"
-          "Lucio Franco <luciofranco14@gmail.com"
-          "Tokio Contributors <team@tokio.rs>"
-        ];
-        dependencies = [
-          {
-            name = "bytes";
-            packageId = "bytes";
-            usesDefaultFeatures = false;
-          }
-          {
-            name = "prost-derive";
-            packageId = "prost-derive 0.12.3";
-            optional = true;
-          }
-        ];
-        features = {
-          "default" = [ "prost-derive" "std" ];
-          "prost-derive" = [ "dep:prost-derive" ];
-        };
-        resolvedDefaultFeatures = [ "default" "prost-derive" "std" ];
+        resolvedDefaultFeatures = [ "alloc" "lazy_static" "regex-syntax" "std" "tempfile" ];
       };
-      "prost 0.13.1" = rec {
+      "prost" = rec {
         crateName = "prost";
         version = "0.13.1";
         edition = "2021";
@@ -9187,7 +8845,7 @@ rec {
           }
           {
             name = "prost-derive";
-            packageId = "prost-derive 0.13.1";
+            packageId = "prost-derive";
             optional = true;
           }
         ];
@@ -9198,14 +8856,16 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" "derive" "prost-derive" "std" ];
       };
-      "prost-build 0.12.3" = rec {
+      "prost-build" = rec {
         crateName = "prost-build";
-        version = "0.12.3";
+        version = "0.13.1";
         edition = "2021";
-        sha256 = "1lp2l1l65l163yggk9nw5mjb2fqwzz12693af5phn1v0abih4pn5";
+        sha256 = "1hbp7cam99q56qf2wp8inwfh59adkzgf676hi07hgpbi1xc85cav";
+        libName = "prost_build";
         authors = [
           "Dan Burkert <dan@danburkert.com>"
           "Lucio Franco <luciofranco14@gmail.com>"
+          "Casper Meijn <casper@meijn.net>"
           "Tokio Contributors <team@tokio.rs>"
         ];
         dependencies = [
@@ -9216,11 +8876,11 @@ rec {
           }
           {
             name = "heck";
-            packageId = "heck";
+            packageId = "heck 0.4.1";
           }
           {
             name = "itertools";
-            packageId = "itertools 0.11.0";
+            packageId = "itertools 0.10.5";
             usesDefaultFeatures = false;
             features = [ "use_alloc" ];
           }
@@ -9249,12 +8909,12 @@ rec {
           }
           {
             name = "prost";
-            packageId = "prost 0.12.3";
+            packageId = "prost";
             usesDefaultFeatures = false;
           }
           {
             name = "prost-types";
-            packageId = "prost-types 0.12.3";
+            packageId = "prost-types";
             usesDefaultFeatures = false;
           }
           {
@@ -9276,99 +8936,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
-            optional = true;
-            features = [ "full" ];
-          }
-          {
-            name = "tempfile";
-            packageId = "tempfile";
-          }
-          {
-            name = "which";
-            packageId = "which 4.4.2";
-          }
-        ];
-        features = {
-          "cleanup-markdown" = [ "pulldown-cmark" "pulldown-cmark-to-cmark" ];
-          "default" = [ "format" ];
-          "format" = [ "prettyplease" "syn" ];
-          "prettyplease" = [ "dep:prettyplease" ];
-          "pulldown-cmark" = [ "dep:pulldown-cmark" ];
-          "pulldown-cmark-to-cmark" = [ "dep:pulldown-cmark-to-cmark" ];
-          "syn" = [ "dep:syn" ];
-        };
-        resolvedDefaultFeatures = [ "cleanup-markdown" "default" "format" "prettyplease" "pulldown-cmark" "pulldown-cmark-to-cmark" "syn" ];
-      };
-      "prost-build 0.13.1" = rec {
-        crateName = "prost-build";
-        version = "0.13.1";
-        edition = "2021";
-        sha256 = "1hbp7cam99q56qf2wp8inwfh59adkzgf676hi07hgpbi1xc85cav";
-        authors = [
-          "Dan Burkert <dan@danburkert.com>"
-          "Lucio Franco <luciofranco14@gmail.com>"
-          "Casper Meijn <casper@meijn.net>"
-          "Tokio Contributors <team@tokio.rs>"
-        ];
-        dependencies = [
-          {
-            name = "bytes";
-            packageId = "bytes";
-            usesDefaultFeatures = false;
-          }
-          {
-            name = "heck";
-            packageId = "heck";
-          }
-          {
-            name = "itertools";
-            packageId = "itertools 0.12.0";
-            usesDefaultFeatures = false;
-            features = [ "use_alloc" ];
-          }
-          {
-            name = "log";
-            packageId = "log";
-          }
-          {
-            name = "multimap";
-            packageId = "multimap";
-            usesDefaultFeatures = false;
-          }
-          {
-            name = "once_cell";
-            packageId = "once_cell";
-          }
-          {
-            name = "petgraph";
-            packageId = "petgraph";
-            usesDefaultFeatures = false;
-          }
-          {
-            name = "prettyplease";
-            packageId = "prettyplease";
-            optional = true;
-          }
-          {
-            name = "prost";
-            packageId = "prost 0.13.1";
-            usesDefaultFeatures = false;
-          }
-          {
-            name = "prost-types";
-            packageId = "prost-types 0.13.1";
-            usesDefaultFeatures = false;
-          }
-          {
-            name = "regex";
-            packageId = "regex";
-            usesDefaultFeatures = false;
-            features = [ "std" "unicode-bool" ];
-          }
-          {
-            name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
             optional = true;
             features = [ "full" ];
           }
@@ -9382,52 +8950,15 @@ rec {
           "default" = [ "format" ];
           "format" = [ "dep:prettyplease" "dep:syn" ];
         };
-        resolvedDefaultFeatures = [ "default" "format" ];
-      };
-      "prost-derive 0.12.3" = rec {
-        crateName = "prost-derive";
-        version = "0.12.3";
-        edition = "2021";
-        sha256 = "03l4yf6pdjvc4sgbvln2srq1avzm1ai86zni4hhqxvqxvnhwkdpg";
-        procMacro = true;
-        authors = [
-          "Dan Burkert <dan@danburkert.com>"
-          "Lucio Franco <luciofranco14@gmail.com>"
-          "Tokio Contributors <team@tokio.rs>"
-        ];
-        dependencies = [
-          {
-            name = "anyhow";
-            packageId = "anyhow";
-          }
-          {
-            name = "itertools";
-            packageId = "itertools 0.11.0";
-            usesDefaultFeatures = false;
-            features = [ "use_alloc" ];
-          }
-          {
-            name = "proc-macro2";
-            packageId = "proc-macro2";
-          }
-          {
-            name = "quote";
-            packageId = "quote";
-          }
-          {
-            name = "syn";
-            packageId = "syn 2.0.48";
-            features = [ "extra-traits" ];
-          }
-        ];
-
+        resolvedDefaultFeatures = [ "cleanup-markdown" "default" "format" ];
       };
-      "prost-derive 0.13.1" = rec {
+      "prost-derive" = rec {
         crateName = "prost-derive";
         version = "0.13.1";
         edition = "2021";
         sha256 = "1jng0kwkwiih777f77kn6ja7hdvi7sxbg11nndwagsy4mnqckghq";
         procMacro = true;
+        libName = "prost_derive";
         authors = [
           "Dan Burkert <dan@danburkert.com>"
           "Lucio Franco <luciofranco14@gmail.com>"
@@ -9441,7 +8972,7 @@ rec {
           }
           {
             name = "itertools";
-            packageId = "itertools 0.12.0";
+            packageId = "itertools 0.10.5";
           }
           {
             name = "proc-macro2";
@@ -9453,41 +8984,18 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
             features = [ "extra-traits" ];
           }
         ];
 
       };
-      "prost-types 0.12.3" = rec {
-        crateName = "prost-types";
-        version = "0.12.3";
-        edition = "2021";
-        sha256 = "03j73llzljdxv9cdxp4m3vb9j3gh4y24rkbx48k3rx6wkvsrhf0r";
-        authors = [
-          "Dan Burkert <dan@danburkert.com>"
-          "Lucio Franco <luciofranco14@gmail.com"
-          "Tokio Contributors <team@tokio.rs>"
-        ];
-        dependencies = [
-          {
-            name = "prost";
-            packageId = "prost 0.12.3";
-            usesDefaultFeatures = false;
-            features = [ "prost-derive" ];
-          }
-        ];
-        features = {
-          "default" = [ "std" ];
-          "std" = [ "prost/std" ];
-        };
-        resolvedDefaultFeatures = [ "default" "std" ];
-      };
-      "prost-types 0.13.1" = rec {
+      "prost-types" = rec {
         crateName = "prost-types";
         version = "0.13.1";
         edition = "2021";
         sha256 = "1hjww9k35c4fqjd75ziyjvyzl8kv9aqnw841ll64p7gl0n5idrff";
+        libName = "prost_types";
         authors = [
           "Dan Burkert <dan@danburkert.com>"
           "Lucio Franco <luciofranco14@gmail.com>"
@@ -9497,7 +9005,7 @@ rec {
         dependencies = [
           {
             name = "prost";
-            packageId = "prost 0.13.1";
+            packageId = "prost";
             usesDefaultFeatures = false;
             features = [ "prost-derive" ];
           }
@@ -9510,9 +9018,10 @@ rec {
       };
       "prost-wkt" = rec {
         crateName = "prost-wkt";
-        version = "0.5.0";
+        version = "0.6.0";
         edition = "2021";
-        sha256 = "0h3c0jyfpg7f3s9a3mx6xcif28j3ir5c5qmps88bknpiy31zk3jd";
+        sha256 = "16c2mbaq2hff51kwr204fncnmi0qx2zz4ff3pb1086qqxqmlxn58";
+        libName = "prost_wkt";
         authors = [
           "fdeantoni <fdeantoni@gmail.com>"
         ];
@@ -9529,7 +9038,7 @@ rec {
           }
           {
             name = "prost";
-            packageId = "prost 0.12.3";
+            packageId = "prost";
           }
           {
             name = "serde";
@@ -9552,28 +9061,29 @@ rec {
       };
       "prost-wkt-build" = rec {
         crateName = "prost-wkt-build";
-        version = "0.5.0";
+        version = "0.6.0";
         edition = "2021";
-        sha256 = "0883g26vrhx07kv0dq85559pj95zxs10lx042pp4za2clplwlcav";
+        sha256 = "054v5qqvdv29g5s1kr26zv0yvzc8gmnlx9k2dw6026g7rdd9srla";
+        libName = "prost_wkt_build";
         authors = [
           "fdeantoni <fdeantoni@gmail.com>"
         ];
         dependencies = [
           {
             name = "heck";
-            packageId = "heck";
+            packageId = "heck 0.4.1";
           }
           {
             name = "prost";
-            packageId = "prost 0.12.3";
+            packageId = "prost";
           }
           {
             name = "prost-build";
-            packageId = "prost-build 0.12.3";
+            packageId = "prost-build";
           }
           {
             name = "prost-types";
-            packageId = "prost-types 0.12.3";
+            packageId = "prost-types";
           }
           {
             name = "quote";
@@ -9584,9 +9094,10 @@ rec {
       };
       "prost-wkt-types" = rec {
         crateName = "prost-wkt-types";
-        version = "0.5.0";
+        version = "0.6.0";
         edition = "2021";
-        sha256 = "1vipmgvqqzr3hn9z5v85mx9zznzjwyfpjy8xzg2v94a0f2lf8ns3";
+        sha256 = "0r2gxf5b604b00v1fwif1rn5nm5xk4vb3ri29dhm9rl2kf70dvq1";
+        libName = "prost_wkt_types";
         authors = [
           "fdeantoni <fdeantoni@gmail.com>"
         ];
@@ -9599,7 +9110,7 @@ rec {
           }
           {
             name = "prost";
-            packageId = "prost 0.12.3";
+            packageId = "prost";
           }
           {
             name = "prost-wkt";
@@ -9621,15 +9132,15 @@ rec {
         buildDependencies = [
           {
             name = "prost";
-            packageId = "prost 0.12.3";
+            packageId = "prost";
           }
           {
             name = "prost-build";
-            packageId = "prost-build 0.12.3";
+            packageId = "prost-build";
           }
           {
             name = "prost-types";
-            packageId = "prost-types 0.12.3";
+            packageId = "prost-types";
           }
           {
             name = "prost-wkt-build";
@@ -9655,6 +9166,7 @@ rec {
         edition = "2021";
         crateBin = [ ];
         sha256 = "0av876a31qvqhy7gzdg134zn4s10smlyi744mz9vrllkf906n82p";
+        libName = "pulldown_cmark";
         authors = [
           "Raph Levien <raph.levien@gmail.com>"
           "Marcus Klaas de Vries <mail@marcusklaas.nl>"
@@ -9662,7 +9174,7 @@ rec {
         dependencies = [
           {
             name = "bitflags";
-            packageId = "bitflags 2.4.2";
+            packageId = "bitflags 2.6.0";
           }
           {
             name = "memchr";
@@ -9684,6 +9196,7 @@ rec {
         version = "10.0.4";
         edition = "2018";
         sha256 = "0gc366cmd5jxal9m95l17rvqsm4dn62lywc8v5gwq8vcjvhyd501";
+        libName = "pulldown_cmark_to_cmark";
         authors = [
           "Sebastian Thiel <byronimo@gmail.com>"
           "Dylan Owen <dyltotheo@gmail.com>"
@@ -9699,22 +9212,12 @@ rec {
         ];
 
       };
-      "quick-error" = rec {
-        crateName = "quick-error";
-        version = "1.2.3";
-        edition = "2015";
-        sha256 = "1q6za3v78hsspisc197bg3g7rpc989qycy8ypr8ap8igv10ikl51";
-        authors = [
-          "Paul Colomiets <paul@colomiets.name>"
-          "Colin Kiegel <kiegel@gmx.de>"
-        ];
-
-      };
       "quick-xml" = rec {
         crateName = "quick-xml";
-        version = "0.31.0";
+        version = "0.36.1";
         edition = "2021";
-        sha256 = "0cravqanylzh5cq2v6hzlfqgxcid5nrp2snnb3pf4m0and2a610h";
+        sha256 = "1g0p8h4hxz2ymiyd71mn862nrj7s0g1wwiyahhdabpzxiqp5x84n";
+        libName = "quick_xml";
         dependencies = [
           {
             name = "memchr";
@@ -9771,7 +9274,7 @@ rec {
           }
           {
             name = "rustls";
-            packageId = "rustls 0.23.7";
+            packageId = "rustls";
             optional = true;
             usesDefaultFeatures = false;
             features = [ "ring" "std" ];
@@ -9818,6 +9321,7 @@ rec {
         version = "0.11.3";
         edition = "2021";
         sha256 = "1zhfcj6fffdgkqdhxzlr18hqmpwqshwbx9280h8bi78h7b01gxfx";
+        libName = "quinn_proto";
         dependencies = [
           {
             name = "bytes";
@@ -9838,7 +9342,7 @@ rec {
           }
           {
             name = "rustls";
-            packageId = "rustls 0.23.7";
+            packageId = "rustls";
             optional = true;
             usesDefaultFeatures = false;
             features = [ "ring" "std" ];
@@ -9876,6 +9380,7 @@ rec {
         version = "0.5.2";
         edition = "2021";
         sha256 = "0ilcrwrah36nqxh329p31ghj3rxm4sw5w4iy2kxwf3w68nf655lh";
+        libName = "quinn_udp";
         dependencies = [
           {
             name = "libc";
@@ -9908,9 +9413,9 @@ rec {
       };
       "quote" = rec {
         crateName = "quote";
-        version = "1.0.35";
+        version = "1.0.37";
         edition = "2018";
-        sha256 = "1vv8r2ncaz4pqdr78x7f138ka595sp2ncr1sa2plm4zxbsmwj7i9";
+        sha256 = "1brklraw2g34bxy9y4q1nbrccn7bv36ylihv12c9vlcii55x7fdm";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -10067,25 +9572,6 @@ rec {
           "serde1" = [ "serde" ];
         };
       };
-      "rand_xoshiro" = rec {
-        crateName = "rand_xoshiro";
-        version = "0.6.0";
-        edition = "2018";
-        sha256 = "1ajsic84rzwz5qr0mzlay8vi17swqi684bqvwqyiim3flfrcv5vg";
-        authors = [
-          "The Rand Project Developers"
-        ];
-        dependencies = [
-          {
-            name = "rand_core";
-            packageId = "rand_core";
-          }
-        ];
-        features = {
-          "serde" = [ "dep:serde" ];
-          "serde1" = [ "serde" ];
-        };
-      };
       "rayon" = rec {
         crateName = "rayon";
         version = "1.8.1";
@@ -10116,6 +9602,7 @@ rec {
         edition = "2021";
         links = "rayon-core";
         sha256 = "1qpwim68ai5h0j7axa8ai8z0payaawv3id0lrgkqmapx7lx8fr8l";
+        libName = "rayon_core";
         authors = [
           "Niko Matsakis <niko@alum.mit.edu>"
           "Josh Stone <cuviper@gmail.com>"
@@ -10136,12 +9623,12 @@ rec {
       };
       "redb" = rec {
         crateName = "redb";
-        version = "2.1.1";
+        version = "2.1.2";
         edition = "2021";
-        sha256 = "1h68d2gqq4vpwiyfpyq9ag0swxavnf9npcd0cqipv77brp9j1pd6";
+        sha256 = "1r8z96z3rnv3m80k9fikflq0dnf5c02br57z0phqlam55v1kscjq";
         type = [ "cdylib" "rlib" ];
         authors = [
-          "Christopher Berner <christopherberner@gmail.com>"
+          "Christopher Berner <me@cberner.com>"
         ];
         dependencies = [
           {
@@ -10158,11 +9645,8 @@ rec {
           }
         ];
         features = {
-          "log" = [ "dep:log" ];
-          "logging" = [ "log" ];
-          "pyo3" = [ "dep:pyo3" ];
-          "pyo3-build-config" = [ "dep:pyo3-build-config" ];
-          "python" = [ "pyo3" "pyo3-build-config" ];
+          "logging" = [ "dep:log" ];
+          "python" = [ "dep:pyo3" "dep:pyo3-build-config" ];
         };
       };
       "redox_syscall 0.2.16" = rec {
@@ -10257,9 +9741,9 @@ rec {
       };
       "regex" = rec {
         crateName = "regex";
-        version = "1.10.2";
+        version = "1.10.6";
         edition = "2021";
-        sha256 = "0hxkd814n4irind8im5c9am221ri6bprx49nc7yxv02ykhd9a2rq";
+        sha256 = "06cnlxwzyqfbw1za1i7ks89ns4i2kr0lpg5ykx56b8v7dd6df6a2";
         authors = [
           "The Rust Project Developers"
           "Andrew Gallant <jamslam@gmail.com>"
@@ -10269,15 +9753,17 @@ rec {
             name = "aho-corasick";
             packageId = "aho-corasick";
             optional = true;
+            usesDefaultFeatures = false;
           }
           {
             name = "memchr";
             packageId = "memchr";
             optional = true;
+            usesDefaultFeatures = false;
           }
           {
             name = "regex-automata";
-            packageId = "regex-automata 0.4.3";
+            packageId = "regex-automata 0.4.7";
             usesDefaultFeatures = false;
             features = [ "alloc" "syntax" "meta" "nfa-pikevm" ];
           }
@@ -10316,6 +9802,7 @@ rec {
         version = "0.1.10";
         edition = "2015";
         sha256 = "0ci1hvbzhrfby5fdpf4ganhf7kla58acad9i1ff1p34dzdrhs8vc";
+        libName = "regex_automata";
         authors = [
           "Andrew Gallant <jamslam@gmail.com>"
         ];
@@ -10335,11 +9822,12 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" "regex-syntax" "std" ];
       };
-      "regex-automata 0.4.3" = rec {
+      "regex-automata 0.4.7" = rec {
         crateName = "regex-automata";
-        version = "0.4.3";
+        version = "0.4.7";
         edition = "2021";
-        sha256 = "0gs8q9yhd3kcg4pr00ag4viqxnh5l7jpyb9fsfr8hzh451w4r02z";
+        sha256 = "1pwjdi4jckpbaivpl6x4v5g4crb37zr2wac93wlfsbzgqn6gbjiq";
+        libName = "regex_automata";
         authors = [
           "The Rust Project Developers"
           "Andrew Gallant <jamslam@gmail.com>"
@@ -10400,6 +9888,7 @@ rec {
         version = "0.6.29";
         edition = "2018";
         sha256 = "1qgj49vm6y3zn1hi09x91jvgkl2b1fiaq402skj83280ggfwcqpi";
+        libName = "regex_syntax";
         authors = [
           "The Rust Project Developers"
         ];
@@ -10414,6 +9903,7 @@ rec {
         version = "0.8.2";
         edition = "2021";
         sha256 = "17rd2s8xbiyf6lb4aj2nfi44zqlj98g2ays8zzj2vfs743k79360";
+        libName = "regex_syntax";
         authors = [
           "The Rust Project Developers"
           "Andrew Gallant <jamslam@gmail.com>"
@@ -10430,6 +9920,7 @@ rec {
         version = "1.9.2";
         edition = "2021";
         sha256 = "1g0gc604zwm73gvpcicn8si25j9j5agqz50r0x1bkmgx6f7mi678";
+        libName = "relative_path";
         authors = [
           "John-John Tedro <udoprog@tedro.se>"
         ];
@@ -10440,9 +9931,9 @@ rec {
       };
       "reqwest" = rec {
         crateName = "reqwest";
-        version = "0.12.5";
+        version = "0.12.7";
         edition = "2021";
-        sha256 = "0dva5mx1cli19adc9igc81ix4si7yiri24ayxdw8652pgnid5mn7";
+        sha256 = "0qsymmmgam6whjcymnlpf5kvk3ylc4bs92lygz63hp7g95b9bx7q";
         authors = [
           "Sean McArthur <sean@seanmonstar.com>"
         ];
@@ -10467,17 +9958,17 @@ rec {
           }
           {
             name = "h2";
-            packageId = "h2 0.4.4";
+            packageId = "h2";
             optional = true;
             target = { target, features }: (!("wasm32" == target."arch" or null));
           }
           {
             name = "http";
-            packageId = "http 1.1.0";
+            packageId = "http";
           }
           {
             name = "http-body";
-            packageId = "http-body 1.0.0";
+            packageId = "http-body";
             target = { target, features }: (!("wasm32" == target."arch" or null));
           }
           {
@@ -10487,13 +9978,13 @@ rec {
           }
           {
             name = "hyper";
-            packageId = "hyper 1.4.1";
+            packageId = "hyper";
             target = { target, features }: (!("wasm32" == target."arch" or null));
             features = [ "http1" "client" ];
           }
           {
             name = "hyper-rustls";
-            packageId = "hyper-rustls 0.27.2";
+            packageId = "hyper-rustls";
             optional = true;
             usesDefaultFeatures = false;
             target = { target, features }: (!("wasm32" == target."arch" or null));
@@ -10550,7 +10041,7 @@ rec {
           }
           {
             name = "rustls";
-            packageId = "rustls 0.23.7";
+            packageId = "rustls";
             optional = true;
             usesDefaultFeatures = false;
             target = { target, features }: (!("wasm32" == target."arch" or null));
@@ -10558,13 +10049,13 @@ rec {
           }
           {
             name = "rustls-native-certs";
-            packageId = "rustls-native-certs 0.7.0";
+            packageId = "rustls-native-certs";
             optional = true;
             target = { target, features }: (!("wasm32" == target."arch" or null));
           }
           {
             name = "rustls-pemfile";
-            packageId = "rustls-pemfile 2.1.0";
+            packageId = "rustls-pemfile";
             optional = true;
             target = { target, features }: (!("wasm32" == target."arch" or null));
           }
@@ -10596,6 +10087,7 @@ rec {
           {
             name = "sync_wrapper";
             packageId = "sync_wrapper 1.0.1";
+            features = [ "futures" ];
           }
           {
             name = "tokio";
@@ -10606,7 +10098,7 @@ rec {
           }
           {
             name = "tokio-rustls";
-            packageId = "tokio-rustls 0.26.0";
+            packageId = "tokio-rustls";
             optional = true;
             usesDefaultFeatures = false;
             target = { target, features }: (!("wasm32" == target."arch" or null));
@@ -10651,8 +10143,8 @@ rec {
             features = [ "AbortController" "AbortSignal" "Headers" "Request" "RequestInit" "RequestMode" "Response" "Window" "FormData" "Blob" "BlobPropertyBag" "ServiceWorkerGlobalScope" "RequestCredentials" "File" "ReadableStream" ];
           }
           {
-            name = "winreg";
-            packageId = "winreg";
+            name = "windows-registry";
+            packageId = "windows-registry";
             target = { target, features }: (target."windows" or false);
           }
         ];
@@ -10666,7 +10158,7 @@ rec {
           }
           {
             name = "hyper";
-            packageId = "hyper 1.4.1";
+            packageId = "hyper";
             usesDefaultFeatures = false;
             target = { target, features }: (!("wasm32" == target."arch" or null));
             features = [ "http1" "http2" "client" "server" ];
@@ -10679,7 +10171,7 @@ rec {
           }
           {
             name = "rustls";
-            packageId = "rustls 0.23.7";
+            packageId = "rustls";
             usesDefaultFeatures = false;
             target = { target, features }: (!("wasm32" == target."arch" or null));
             features = [ "ring" ];
@@ -10740,9 +10232,10 @@ rec {
       };
       "reqwest-middleware" = rec {
         crateName = "reqwest-middleware";
-        version = "0.3.2";
+        version = "0.3.3";
         edition = "2018";
-        sha256 = "0s0jrbn3ns8l07qjjwmq62r9g2w1jvllr8qpph6f0szyvlrnld1r";
+        sha256 = "011b8n9a1bwalyk2y6x5s0wz52pxk70l4bbrba47qgsdc1dfnb2n";
+        libName = "reqwest_middleware";
         authors = [
           "Rodrigo Gryzinski <rodrigo.gryzinski@truelayer.com>"
         ];
@@ -10757,7 +10250,7 @@ rec {
           }
           {
             name = "http";
-            packageId = "http 1.1.0";
+            packageId = "http";
           }
           {
             name = "reqwest";
@@ -10777,6 +10270,13 @@ rec {
             packageId = "tower-service";
           }
         ];
+        devDependencies = [
+          {
+            name = "reqwest";
+            packageId = "reqwest";
+            features = [ "rustls-tls" ];
+          }
+        ];
         features = {
           "charset" = [ "reqwest/charset" ];
           "http2" = [ "reqwest/http2" ];
@@ -10787,9 +10287,9 @@ rec {
       };
       "reqwest-tracing" = rec {
         crateName = "reqwest-tracing";
-        version = "0.5.2";
+        version = "0.5.3";
         edition = "2018";
-        sha256 = "0vg7vfdsh3c15xawpy08z0576s5pkrkjmmkq0bdhy8fw0nnxqiaf";
+        sha256 = "0igb5hp1mdr8jb5qwj26dphykvydxy1piawrvpc368n7ckx9ppdz";
         libName = "reqwest_tracing";
         authors = [
           "Rodrigo Gryzinski <rodrigo.gryzinski@truelayer.com>"
@@ -10811,7 +10311,7 @@ rec {
           }
           {
             name = "http";
-            packageId = "http 1.1.0";
+            packageId = "http";
           }
           {
             name = "matchit";
@@ -10859,10 +10359,13 @@ rec {
           "opentelemetry_0_22_pkg" = [ "dep:opentelemetry_0_22_pkg" ];
           "opentelemetry_0_23" = [ "opentelemetry_0_23_pkg" "tracing-opentelemetry_0_24_pkg" ];
           "opentelemetry_0_23_pkg" = [ "dep:opentelemetry_0_23_pkg" ];
+          "opentelemetry_0_24" = [ "opentelemetry_0_24_pkg" "tracing-opentelemetry_0_25_pkg" ];
+          "opentelemetry_0_24_pkg" = [ "dep:opentelemetry_0_24_pkg" ];
           "tracing-opentelemetry_0_21_pkg" = [ "dep:tracing-opentelemetry_0_21_pkg" ];
           "tracing-opentelemetry_0_22_pkg" = [ "dep:tracing-opentelemetry_0_22_pkg" ];
           "tracing-opentelemetry_0_23_pkg" = [ "dep:tracing-opentelemetry_0_23_pkg" ];
           "tracing-opentelemetry_0_24_pkg" = [ "dep:tracing-opentelemetry_0_24_pkg" ];
+          "tracing-opentelemetry_0_25_pkg" = [ "dep:tracing-opentelemetry_0_25_pkg" ];
         };
         resolvedDefaultFeatures = [ "opentelemetry_0_22" "opentelemetry_0_22_pkg" "tracing-opentelemetry_0_23_pkg" ];
       };
@@ -11052,7 +10555,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
             features = [ "full" "parsing" "extra-traits" "visit" "visit-mut" ];
           }
           {
@@ -11091,7 +10594,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
             features = [ "full" "extra-traits" ];
           }
         ];
@@ -11108,6 +10611,7 @@ rec {
         version = "0.1.23";
         edition = "2015";
         sha256 = "0xnbk2bmyzshacjm2g1kd4zzv2y2az14bw3sjccq5qkpmsfvn9nn";
+        libName = "rustc_demangle";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -11122,6 +10626,7 @@ rec {
         version = "1.1.0";
         edition = "2015";
         sha256 = "1qkc5khrmv5pqi5l5ca9p5nl5hs742cagrndhbrlk3dhlrx3zm08";
+        libName = "rustc_hash";
         authors = [
           "The Rust Project Developers"
         ];
@@ -11135,6 +10640,7 @@ rec {
         version = "2.0.0";
         edition = "2021";
         sha256 = "0lni0lf846bzrf3jvci6jaf4142n1mdqxvcpczk5ch9pfgyk8c2q";
+        libName = "rustc_hash";
         authors = [
           "The Rust Project Developers"
         ];
@@ -11163,9 +10669,9 @@ rec {
       };
       "rustix" = rec {
         crateName = "rustix";
-        version = "0.38.30";
+        version = "0.38.35";
         edition = "2021";
-        sha256 = "1jkb6bzrj2w9ffy35aw4q04mqk1yxqw35fz80x0c4cxgi9c988rj";
+        sha256 = "0vy38cpprg64i6kfwz0w5hj2lqgliyimnx6vmplninir499m0pd8";
         authors = [
           "Dan Gohman <dev@sunfishcode.online>"
           "Jakub Konka <kubkon@jakubkonka.com>"
@@ -11173,7 +10679,7 @@ rec {
         dependencies = [
           {
             name = "bitflags";
-            packageId = "bitflags 2.4.2";
+            packageId = "bitflags 2.6.0";
             usesDefaultFeatures = false;
           }
           {
@@ -11204,14 +10710,12 @@ rec {
             optional = true;
             usesDefaultFeatures = false;
             target = { target, features }: ((!(target."rustix_use_libc" or false)) && (!(target."miri" or false)) && ("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null))));
-            features = [ "extra_traits" ];
           }
           {
             name = "libc";
             packageId = "libc";
             usesDefaultFeatures = false;
             target = { target, features }: ((!(target."windows" or false)) && ((target."rustix_use_libc" or false) || (target."miri" or false) || (!(("linux" == target."os" or null) && ("little" == target."endian" or null) && (("arm" == target."arch" or null) || (("aarch64" == target."arch" or null) && ("64" == target."pointer_width" or null)) || ("riscv64" == target."arch" or null) || ((target."rustix_use_experimental_asm" or false) && ("powerpc64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips32r6" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64" == target."arch" or null)) || ((target."rustix_use_experimental_asm" or false) && ("mips64r6" == target."arch" or null)) || ("x86" == target."arch" or null) || (("x86_64" == target."arch" or null) && ("64" == target."pointer_width" or null)))))));
-            features = [ "extra_traits" ];
           }
           {
             name = "linux-raw-sys";
@@ -11248,10 +10752,13 @@ rec {
         ];
         features = {
           "all-apis" = [ "event" "fs" "io_uring" "mm" "mount" "net" "param" "pipe" "process" "procfs" "pty" "rand" "runtime" "shm" "stdio" "system" "termios" "thread" "time" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
           "default" = [ "std" "use-libc-auxv" ];
           "io_uring" = [ "event" "fs" "net" "linux-raw-sys/io_uring" ];
           "itoa" = [ "dep:itoa" ];
           "libc" = [ "dep:libc" ];
+          "libc-extra-traits" = [ "libc?/extra_traits" ];
           "libc_errno" = [ "dep:libc_errno" ];
           "linux_latest" = [ "linux_4_11" ];
           "net" = [ "linux-raw-sys/net" "linux-raw-sys/netlink" "linux-raw-sys/if_ether" "linux-raw-sys/xdp" ];
@@ -11261,113 +10768,17 @@ rec {
           "procfs" = [ "once_cell" "itoa" "fs" ];
           "pty" = [ "itoa" "fs" ];
           "runtime" = [ "linux-raw-sys/prctl" ];
-          "rustc-dep-of-std" = [ "dep:core" "dep:alloc" "dep:compiler_builtins" "linux-raw-sys/rustc-dep-of-std" "bitflags/rustc-dep-of-std" "compiler_builtins?/rustc-dep-of-std" ];
+          "rustc-dep-of-std" = [ "core" "rustc-std-workspace-alloc" "compiler_builtins" "linux-raw-sys/rustc-dep-of-std" "bitflags/rustc-dep-of-std" "compiler_builtins?/rustc-dep-of-std" ];
+          "rustc-std-workspace-alloc" = [ "dep:rustc-std-workspace-alloc" ];
           "shm" = [ "fs" ];
-          "std" = [ "bitflags/std" "alloc" "libc?/std" "libc_errno?/std" ];
+          "std" = [ "bitflags/std" "alloc" "libc?/std" "libc_errno?/std" "libc-extra-traits" ];
           "system" = [ "linux-raw-sys/system" ];
           "thread" = [ "linux-raw-sys/prctl" ];
-          "use-libc" = [ "libc_errno" "libc" ];
+          "use-libc" = [ "libc_errno" "libc" "libc-extra-traits" ];
         };
-        resolvedDefaultFeatures = [ "alloc" "default" "event" "fs" "net" "pipe" "process" "std" "termios" "time" "use-libc-auxv" ];
+        resolvedDefaultFeatures = [ "alloc" "default" "event" "fs" "libc-extra-traits" "net" "pipe" "process" "std" "termios" "time" "use-libc-auxv" ];
       };
-      "rustls 0.21.12" = rec {
-        crateName = "rustls";
-        version = "0.21.12";
-        edition = "2021";
-        sha256 = "0gjdg2a9r81sdwkyw3n5yfbkrr6p9gyk3xr2kcsr3cs83x6s2miz";
-        dependencies = [
-          {
-            name = "log";
-            packageId = "log";
-            optional = true;
-          }
-          {
-            name = "ring";
-            packageId = "ring";
-          }
-          {
-            name = "rustls-webpki";
-            packageId = "rustls-webpki 0.101.7";
-            rename = "webpki";
-            features = [ "alloc" "std" ];
-          }
-          {
-            name = "sct";
-            packageId = "sct";
-          }
-        ];
-        devDependencies = [
-          {
-            name = "log";
-            packageId = "log";
-          }
-        ];
-        features = {
-          "default" = [ "logging" "tls12" ];
-          "log" = [ "dep:log" ];
-          "logging" = [ "log" ];
-          "read_buf" = [ "rustversion" ];
-          "rustversion" = [ "dep:rustversion" ];
-        };
-        resolvedDefaultFeatures = [ "default" "log" "logging" "tls12" ];
-      };
-      "rustls 0.22.4" = rec {
-        crateName = "rustls";
-        version = "0.22.4";
-        edition = "2021";
-        sha256 = "0cl4q6w0x1cl5ldjsgbbiiqhkz6qg5vxl5dkn9wwsyxc44vzfkmz";
-        dependencies = [
-          {
-            name = "log";
-            packageId = "log";
-            optional = true;
-          }
-          {
-            name = "ring";
-            packageId = "ring";
-            optional = true;
-          }
-          {
-            name = "rustls-pki-types";
-            packageId = "rustls-pki-types";
-            rename = "pki-types";
-            features = [ "std" ];
-          }
-          {
-            name = "rustls-webpki";
-            packageId = "rustls-webpki 0.102.2";
-            rename = "webpki";
-            usesDefaultFeatures = false;
-            features = [ "std" ];
-          }
-          {
-            name = "subtle";
-            packageId = "subtle";
-            usesDefaultFeatures = false;
-          }
-          {
-            name = "zeroize";
-            packageId = "zeroize";
-          }
-        ];
-        devDependencies = [
-          {
-            name = "log";
-            packageId = "log";
-          }
-        ];
-        features = {
-          "aws_lc_rs" = [ "dep:aws-lc-rs" "webpki/aws_lc_rs" ];
-          "default" = [ "logging" "ring" "tls12" ];
-          "log" = [ "dep:log" ];
-          "logging" = [ "log" ];
-          "read_buf" = [ "rustversion" ];
-          "ring" = [ "dep:ring" "webpki/ring" ];
-          "rustversion" = [ "dep:rustversion" ];
-        };
-        resolvedDefaultFeatures = [ "log" "logging" "ring" "tls12" ];
-      };
-      "rustls 0.23.7" = rec {
+      "rustls" = rec {
         crateName = "rustls";
         version = "0.23.7";
         edition = "2021";
@@ -11397,7 +10808,7 @@ rec {
           }
           {
             name = "rustls-webpki";
-            packageId = "rustls-webpki 0.102.2";
+            packageId = "rustls-webpki";
             rename = "webpki";
             usesDefaultFeatures = false;
             features = [ "alloc" ];
@@ -11433,39 +10844,12 @@ rec {
         };
         resolvedDefaultFeatures = [ "log" "logging" "ring" "std" "tls12" ];
       };
-      "rustls-native-certs 0.6.3" = rec {
-        crateName = "rustls-native-certs";
-        version = "0.6.3";
-        edition = "2021";
-        sha256 = "007zind70rd5rfsrkdcfm8vn09j8sg02phg9334kark6rdscxam9";
-        dependencies = [
-          {
-            name = "openssl-probe";
-            packageId = "openssl-probe";
-            target = { target, features }: ((target."unix" or false) && (!("macos" == target."os" or null)));
-          }
-          {
-            name = "rustls-pemfile";
-            packageId = "rustls-pemfile 1.0.4";
-          }
-          {
-            name = "schannel";
-            packageId = "schannel";
-            target = { target, features }: (target."windows" or false);
-          }
-          {
-            name = "security-framework";
-            packageId = "security-framework";
-            target = { target, features }: ("macos" == target."os" or null);
-          }
-        ];
-
-      };
-      "rustls-native-certs 0.7.0" = rec {
+      "rustls-native-certs" = rec {
         crateName = "rustls-native-certs";
         version = "0.7.0";
         edition = "2021";
         sha256 = "14ip15dcr6fmjzi12lla9cpln7mmkdid4a7wsp344v4kz9gbh7wg";
+        libName = "rustls_native_certs";
         dependencies = [
           {
             name = "openssl-probe";
@@ -11474,7 +10858,7 @@ rec {
           }
           {
             name = "rustls-pemfile";
-            packageId = "rustls-pemfile 2.1.0";
+            packageId = "rustls-pemfile";
           }
           {
             name = "rustls-pki-types";
@@ -11494,24 +10878,12 @@ rec {
         ];
 
       };
-      "rustls-pemfile 1.0.4" = rec {
-        crateName = "rustls-pemfile";
-        version = "1.0.4";
-        edition = "2018";
-        sha256 = "1324n5bcns0rnw6vywr5agff3rwfvzphi7rmbyzwnv6glkhclx0w";
-        dependencies = [
-          {
-            name = "base64";
-            packageId = "base64 0.21.7";
-          }
-        ];
-
-      };
-      "rustls-pemfile 2.1.0" = rec {
+      "rustls-pemfile" = rec {
         crateName = "rustls-pemfile";
         version = "2.1.0";
         edition = "2018";
         sha256 = "02y7qn9d93ri4hrm72yw4zqlbxch6ma045nyazmdrppw6jvkncrw";
+        libName = "rustls_pemfile";
         dependencies = [
           {
             name = "base64";
@@ -11536,37 +10908,14 @@ rec {
         version = "1.3.1";
         edition = "2021";
         sha256 = "1a0g7453h07701vyxjj05gv903a0shi43mf7hl3cdd08hsr6gpjy";
+        libName = "rustls_pki_types";
         features = {
           "default" = [ "alloc" ];
           "std" = [ "alloc" ];
         };
         resolvedDefaultFeatures = [ "alloc" "default" "std" ];
       };
-      "rustls-webpki 0.101.7" = rec {
-        crateName = "rustls-webpki";
-        version = "0.101.7";
-        edition = "2021";
-        sha256 = "0rapfhpkqp75552i8r0y7f4vq7csb4k7gjjans0df73sxv8paqlb";
-        libName = "webpki";
-        dependencies = [
-          {
-            name = "ring";
-            packageId = "ring";
-            usesDefaultFeatures = false;
-          }
-          {
-            name = "untrusted";
-            packageId = "untrusted";
-          }
-        ];
-        features = {
-          "alloc" = [ "ring/alloc" ];
-          "default" = [ "std" ];
-          "std" = [ "alloc" ];
-        };
-        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
-      };
-      "rustls-webpki 0.102.2" = rec {
+      "rustls-webpki" = rec {
         crateName = "rustls-webpki";
         version = "0.102.2";
         edition = "2021";
@@ -11611,40 +10960,6 @@ rec {
         ];
 
       };
-      "rusty-fork" = rec {
-        crateName = "rusty-fork";
-        version = "0.3.0";
-        edition = "2018";
-        sha256 = "0kxwq5c480gg6q0j3bg4zzyfh2kwmc3v2ba94jw8ncjc8mpcqgfb";
-        authors = [
-          "Jason Lingle"
-        ];
-        dependencies = [
-          {
-            name = "fnv";
-            packageId = "fnv";
-          }
-          {
-            name = "quick-error";
-            packageId = "quick-error";
-          }
-          {
-            name = "tempfile";
-            packageId = "tempfile";
-          }
-          {
-            name = "wait-timeout";
-            packageId = "wait-timeout";
-            optional = true;
-          }
-        ];
-        features = {
-          "default" = [ "timeout" ];
-          "timeout" = [ "wait-timeout" ];
-          "wait-timeout" = [ "dep:wait-timeout" ];
-        };
-        resolvedDefaultFeatures = [ "timeout" "wait-timeout" ];
-      };
       "rustyline" = rec {
         crateName = "rustyline";
         version = "10.1.1";
@@ -11756,6 +11071,7 @@ rec {
         version = "1.0.6";
         edition = "2018";
         sha256 = "00h5j1w87dmhnvbv9l8bic3y7xxsnjmssvifw2ayvgx9mb1ivz4k";
+        libName = "same_file";
         authors = [
           "Andrew Gallant <jamslam@gmail.com>"
         ];
@@ -11798,6 +11114,7 @@ rec {
         version = "1.0.1";
         edition = "2015";
         sha256 = "15524h04mafihcvfpgxd8f4bgc3k95aclz8grjkg9a0rxcvn9kz1";
+        libName = "scoped_tls";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -11816,31 +11133,12 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" "use_std" ];
       };
-      "sct" = rec {
-        crateName = "sct";
-        version = "0.7.1";
-        edition = "2021";
-        sha256 = "056lmi2xkzdg1dbai6ha3n57s18cbip4pnmpdhyljli3m99n216s";
-        authors = [
-          "Joseph Birr-Pixton <jpixton@gmail.com>"
-        ];
-        dependencies = [
-          {
-            name = "ring";
-            packageId = "ring";
-          }
-          {
-            name = "untrusted";
-            packageId = "untrusted";
-          }
-        ];
-
-      };
       "security-framework" = rec {
         crateName = "security-framework";
         version = "2.9.2";
         edition = "2021";
         sha256 = "1pplxk15s5yxvi2m1sz5xfmjibp96cscdcl432w9jzbk0frlzdh5";
+        libName = "security_framework";
         authors = [
           "Steven Fackler <sfackler@gmail.com>"
           "Kornel <kornel@geekhood.net>"
@@ -11887,6 +11185,7 @@ rec {
         version = "2.9.1";
         edition = "2021";
         sha256 = "0yhciwlsy9dh0ps1gw3197kvyqx1bvc4knrhiznhid6kax196cp9";
+        libName = "security_framework_sys";
         authors = [
           "Steven Fackler <sfackler@gmail.com>"
           "Kornel <kornel@geekhood.net>"
@@ -11928,9 +11227,9 @@ rec {
       };
       "serde" = rec {
         crateName = "serde";
-        version = "1.0.204";
+        version = "1.0.209";
         edition = "2018";
-        sha256 = "04kwpwqz559xlhxkggmm8rjxqgywy5swam3kscwsicnbw1cgaxmw";
+        sha256 = "029yqqbb3c8v3gc720fhxn49dhgvb88zbyprdg5621riwzzy1z4r";
         authors = [
           "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
           "David Tolnay <dtolnay@gmail.com>"
@@ -11962,9 +11261,9 @@ rec {
       };
       "serde_derive" = rec {
         crateName = "serde_derive";
-        version = "1.0.204";
+        version = "1.0.209";
         edition = "2015";
-        sha256 = "08p25262mbmhsr2cg0508d5b1wvljj956rvpg0v3qgg6gc8pxkg0";
+        sha256 = "0w114ksg1ymnmqdisd0g1j3g8jgz6pam45xg6yb47dfpkybip0x5";
         procMacro = true;
         authors = [
           "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
@@ -11985,7 +11284,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
             usesDefaultFeatures = false;
             features = [ "clone-impls" "derive" "parsing" "printing" "proc-macro" ];
           }
@@ -12093,9 +11392,9 @@ rec {
       };
       "serde_spanned" = rec {
         crateName = "serde_spanned";
-        version = "0.6.6";
+        version = "0.6.7";
         edition = "2021";
-        sha256 = "1839b6m5p9ijjmcwamiya2r612ks2vg6w2pp95yg76lr3zh79rkr";
+        sha256 = "0v9h2nlg8r7n7dkbgj1aw59g35kl869l652wc6zi2f4zawqinnzb";
         dependencies = [
           {
             name = "serde";
@@ -12170,9 +11469,9 @@ rec {
       };
       "serde_with" = rec {
         crateName = "serde_with";
-        version = "3.7.0";
+        version = "3.9.0";
         edition = "2021";
-        sha256 = "16jn72cij27fxjafcsma1z5p587xkk8wqhp2yv98zy5vc7iv107f";
+        sha256 = "0mxqyh2qzq5xi8pnv9647337pz107yjv3ck7x9b229s892lwzkk9";
         authors = [
           "Jonas Bushart"
           "Marcin Kaźmierczak"
@@ -12180,7 +11479,7 @@ rec {
         dependencies = [
           {
             name = "base64";
-            packageId = "base64 0.21.7";
+            packageId = "base64 0.22.1";
             optional = true;
             usesDefaultFeatures = false;
           }
@@ -12277,9 +11576,9 @@ rec {
       };
       "serde_with_macros" = rec {
         crateName = "serde_with_macros";
-        version = "3.7.0";
+        version = "3.9.0";
         edition = "2021";
-        sha256 = "0mbnika5bw1mvgnl50rs7wfzj7dwxzgwqxnq6656694j38bdqqb5";
+        sha256 = "0l1kfkzj46can1rwfspmnan8shqr0prlmbaig9hp9wpl3scy9zm8";
         procMacro = true;
         authors = [
           "Jonas Bushart"
@@ -12299,7 +11598,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
             features = [ "extra-traits" "full" "parsing" ];
           }
         ];
@@ -12389,6 +11688,7 @@ rec {
         version = "0.1.7";
         edition = "2018";
         sha256 = "1xipjr4nqsgw34k7a2cgj9zaasl2ds6jwn89886kww93d32a637l";
+        libName = "sharded_slab";
         authors = [
           "Eliza Weisman <eliza@buoyant.io>"
         ];
@@ -12402,11 +11702,30 @@ rec {
           "loom" = [ "dep:loom" ];
         };
       };
+      "shlex" = rec {
+        crateName = "shlex";
+        version = "1.3.0";
+        edition = "2015";
+        sha256 = "0r1y6bv26c1scpxvhg2cabimrmwgbp4p3wy6syj9n0c4s3q2znhg";
+        authors = [
+          "comex <comexk@gmail.com>"
+          "Fenhl <fenhl@fenhl.net>"
+          "Adrian Taylor <adetaylor@chromium.org>"
+          "Alex Touchet <alextouchet@outlook.com>"
+          "Daniel Parks <dp+git@oxidized.org>"
+          "Garrett Berg <googberg@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
       "signal-hook-registry" = rec {
         crateName = "signal-hook-registry";
         version = "1.4.1";
         edition = "2015";
         sha256 = "18crkkw5k82bvcx088xlf5g4n3772m24qhzgfan80nda7d3rn8nq";
+        libName = "signal_hook_registry";
         authors = [
           "Michal 'vorner' Vaner <vorner@vorner.cz>"
           "Masaki Hara <ackie.h.gmai@gmail.com>"
@@ -12544,9 +11863,9 @@ rec {
       };
       "smol_str" = rec {
         crateName = "smol_str";
-        version = "0.2.1";
+        version = "0.2.2";
         edition = "2018";
-        sha256 = "0jca0hyrwnv428q5gxhn2s8jsvrrkyrb0fyla9x37056mmimb176";
+        sha256 = "1bfylqf2vnqaglw58930vpxm2rfzji5gjp15a2c0kh8aj6v8ylyx";
         authors = [
           "Aleksey Kladov <aleksey.kladov@gmail.com>"
         ];
@@ -12616,13 +11935,14 @@ rec {
         edition = "2018";
         sha256 = "1gzy9rzggs090zf7hfvgp4lm1glrmg9qzh796686jnq7bxk7j04r";
         procMacro = true;
+        libName = "snafu_derive";
         authors = [
           "Jake Goulding <jake.goulding@gmail.com>"
         ];
         dependencies = [
           {
             name = "heck";
-            packageId = "heck";
+            packageId = "heck 0.4.1";
           }
           {
             name = "proc-macro2";
@@ -12738,6 +12058,7 @@ rec {
         version = "1.0.6";
         edition = "2018";
         sha256 = "1l7q4nha7wpsr0970bfqm773vhmpwr9l6rr8r4gwgrh46wvdh24y";
+        libName = "str_buf";
         authors = [
           "Douman <douman@gmx.se>"
         ];
@@ -12745,7 +12066,7 @@ rec {
           "serde" = [ "dep:serde" ];
         };
       };
-      "strsim" = rec {
+      "strsim 0.10.0" = rec {
         crateName = "strsim";
         version = "0.10.0";
         edition = "2015";
@@ -12755,6 +12076,17 @@ rec {
         ];
 
       };
+      "strsim 0.11.1" = rec {
+        crateName = "strsim";
+        version = "0.11.1";
+        edition = "2015";
+        sha256 = "0kzvqlw8hxqb7y598w1s0hxlnmi84sg5vsipp3yg5na5d1rvba3x";
+        authors = [
+          "Danny Guo <danny@dannyguo.com>"
+          "maxbachmann <oss@maxbachmann.de>"
+        ];
+
+      };
       "structmeta" = rec {
         crateName = "structmeta";
         version = "0.1.6";
@@ -12796,6 +12128,7 @@ rec {
         edition = "2021";
         sha256 = "14vxik2m3dm7bwx016qfz062fwznkbq02fyq8vby545m0pj0nhi4";
         procMacro = true;
+        libName = "structmeta_derive";
         authors = [
           "frozenlib"
         ];
@@ -12869,11 +12202,11 @@ rec {
         };
         resolvedDefaultFeatures = [ "clone-impls" "default" "derive" "extra-traits" "full" "parsing" "printing" "proc-macro" "quote" "visit" "visit-mut" ];
       };
-      "syn 2.0.48" = rec {
+      "syn 2.0.76" = rec {
         crateName = "syn";
-        version = "2.0.48";
+        version = "2.0.76";
         edition = "2021";
-        sha256 = "0gqgfygmrxmp8q32lia9p294kdd501ybn6kn2h4gqza0irik2d8g";
+        sha256 = "09fmdkmqqkkfkg53qnldl10ppwqkqlw22ixhg4rgrkp02hd0i3jp";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -12896,12 +12229,11 @@ rec {
         ];
         features = {
           "default" = [ "derive" "parsing" "printing" "clone-impls" "proc-macro" ];
-          "printing" = [ "quote" ];
-          "proc-macro" = [ "proc-macro2/proc-macro" "quote/proc-macro" ];
-          "quote" = [ "dep:quote" ];
+          "printing" = [ "dep:quote" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" "quote?/proc-macro" ];
           "test" = [ "syn-test-suite/all-features" ];
         };
-        resolvedDefaultFeatures = [ "clone-impls" "default" "derive" "extra-traits" "full" "parsing" "printing" "proc-macro" "quote" "visit" "visit-mut" ];
+        resolvedDefaultFeatures = [ "clone-impls" "default" "derive" "extra-traits" "full" "parsing" "printing" "proc-macro" "visit" "visit-mut" ];
       };
       "sync_wrapper 0.1.2" = rec {
         crateName = "sync_wrapper";
@@ -12924,10 +12256,19 @@ rec {
         authors = [
           "Actyx AG <developer@actyx.io>"
         ];
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
         features = {
           "futures" = [ "futures-core" ];
           "futures-core" = [ "dep:futures-core" ];
         };
+        resolvedDefaultFeatures = [ "futures" "futures-core" ];
       };
       "tabwriter" = rec {
         crateName = "tabwriter";
@@ -12948,9 +12289,9 @@ rec {
       };
       "tempfile" = rec {
         crateName = "tempfile";
-        version = "3.9.0";
-        edition = "2018";
-        sha256 = "1ypkl7rvv57n16q28psxpb61rnyhmfaif12ascdnsyljm90l3kh1";
+        version = "3.12.0";
+        edition = "2021";
+        sha256 = "0r3sm3323crr50ranvask8z4qb3x5zfqxs1mrzab1swlqz8cvjq4";
         authors = [
           "Steven Allen <steven@stebalien.com>"
           "The Rust Project Developers"
@@ -12967,9 +12308,10 @@ rec {
             packageId = "fastrand";
           }
           {
-            name = "redox_syscall";
-            packageId = "redox_syscall 0.4.1";
-            target = { target, features }: ("redox" == target."os" or null);
+            name = "once_cell";
+            packageId = "once_cell";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
           }
           {
             name = "rustix";
@@ -12979,7 +12321,7 @@ rec {
           }
           {
             name = "windows-sys";
-            packageId = "windows-sys 0.52.0";
+            packageId = "windows-sys 0.59.0";
             target = { target, features }: (target."windows" or false);
             features = [ "Win32_Storage_FileSystem" "Win32_Foundation" ];
           }
@@ -13009,6 +12351,7 @@ rec {
         edition = "2021";
         sha256 = "105lxqs0vnqff5821sgns8q1scvrwfx1yw6iz7i7nr862j6l1mk2";
         procMacro = true;
+        libName = "test_strategy";
         authors = [
           "frozenlib"
         ];
@@ -13038,6 +12381,7 @@ rec {
         version = "1.1.1";
         edition = "2018";
         sha256 = "0cwjbkl7w3xc8mnkhg1nwij6p5y2qkcfldgss8ddnawvhf3s32pi";
+        libName = "text_size";
         authors = [
           "Aleksey Kladov <aleksey.kladov@gmail.com>"
           "Christopher Durham (CAD97) <cad97@cad97.com>"
@@ -13048,9 +12392,9 @@ rec {
       };
       "thiserror" = rec {
         crateName = "thiserror";
-        version = "1.0.56";
+        version = "1.0.63";
         edition = "2021";
-        sha256 = "1b9hnzngjan4d89zjs16i01bcpcnvdwklyh73lj16xk28p37hhym";
+        sha256 = "092p83mf4p1vkjb2j6h6z96dan4raq2simhirjv12slbndq26d60";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -13064,10 +12408,11 @@ rec {
       };
       "thiserror-impl" = rec {
         crateName = "thiserror-impl";
-        version = "1.0.56";
+        version = "1.0.63";
         edition = "2021";
-        sha256 = "0w9ldp8fa574ilz4dn7y7scpcq66vdjy59qal8qdpwsh7faal3zs";
+        sha256 = "0qd21l2jjrkvnpr5da3l3b58v4wmrkn6aa0h1z5dg6kb8rc8nmd4";
         procMacro = true;
+        libName = "thiserror_impl";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -13082,7 +12427,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
           }
         ];
 
@@ -13127,9 +12472,9 @@ rec {
       };
       "time" = rec {
         crateName = "time";
-        version = "0.3.34";
+        version = "0.3.36";
         edition = "2021";
-        sha256 = "0jc7wgprzqjhzd0nqkbmdlnjwyddnswmjw86ni2vq55v45jqn968";
+        sha256 = "11g8hdpahgrf1wwl2rpsg5nxq3aj7ri6xr672v4qcij6cgjqizax";
         authors = [
           "Jacob Pratt <open-source@jhpratt.dev>"
           "Time contributors"
@@ -13210,6 +12555,7 @@ rec {
         version = "0.1.2";
         edition = "2021";
         sha256 = "1wx3qizcihw6z151hywfzzyd1y5dl804ydyxci6qm07vbakpr4pg";
+        libName = "time_core";
         authors = [
           "Jacob Pratt <open-source@jhpratt.dev>"
           "Time contributors"
@@ -13218,10 +12564,11 @@ rec {
       };
       "time-macros" = rec {
         crateName = "time-macros";
-        version = "0.2.17";
+        version = "0.2.18";
         edition = "2021";
-        sha256 = "0x3pahhk2751c6kqqq9dk6lz0gydbnxr44q01wpjlrz687ps78vv";
+        sha256 = "1kqwxvfh2jkpg38fy673d6danh1bhcmmbsmffww3mphgail2l99z";
         procMacro = true;
+        libName = "time_macros";
         authors = [
           "Jacob Pratt <open-source@jhpratt.dev>"
           "Time contributors"
@@ -13298,9 +12645,9 @@ rec {
       };
       "tokio" = rec {
         crateName = "tokio";
-        version = "1.35.1";
+        version = "1.39.3";
         edition = "2021";
-        sha256 = "01613rkziqp812a288ga65aqygs254wgajdi57v8brivjkx4x6y8";
+        sha256 = "1xgzhj7bxqqpjaabjkgsx8hi0f600bzj4iyp9f0a9gr3k6dwkawv";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -13323,16 +12670,11 @@ rec {
           }
           {
             name = "mio";
-            packageId = "mio";
+            packageId = "mio 1.0.2";
             optional = true;
             usesDefaultFeatures = false;
           }
           {
-            name = "num_cpus";
-            packageId = "num_cpus";
-            optional = true;
-          }
-          {
             name = "pin-project-lite";
             packageId = "pin-project-lite";
           }
@@ -13356,7 +12698,7 @@ rec {
           }
           {
             name = "windows-sys";
-            packageId = "windows-sys 0.48.0";
+            packageId = "windows-sys 0.52.0";
             optional = true;
             target = { target, features }: (target."windows" or false);
           }
@@ -13374,7 +12716,7 @@ rec {
           }
           {
             name = "windows-sys";
-            packageId = "windows-sys 0.48.0";
+            packageId = "windows-sys 0.52.0";
             target = { target, features }: (target."windows" or false);
             features = [ "Win32_Foundation" "Win32_Security_Authorization" ];
           }
@@ -13387,10 +12729,9 @@ rec {
           "macros" = [ "tokio-macros" ];
           "mio" = [ "dep:mio" ];
           "net" = [ "libc" "mio/os-poll" "mio/os-ext" "mio/net" "socket2" "windows-sys/Win32_Foundation" "windows-sys/Win32_Security" "windows-sys/Win32_Storage_FileSystem" "windows-sys/Win32_System_Pipes" "windows-sys/Win32_System_SystemServices" ];
-          "num_cpus" = [ "dep:num_cpus" ];
           "parking_lot" = [ "dep:parking_lot" ];
           "process" = [ "bytes" "libc" "mio/os-poll" "mio/os-ext" "mio/net" "signal-hook-registry" "windows-sys/Win32_Foundation" "windows-sys/Win32_System_Threading" "windows-sys/Win32_System_WindowsProgramming" ];
-          "rt-multi-thread" = [ "num_cpus" "rt" ];
+          "rt-multi-thread" = [ "rt" ];
           "signal" = [ "libc" "mio/os-poll" "mio/net" "mio/os-ext" "signal-hook-registry" "windows-sys/Win32_Foundation" "windows-sys/Win32_System_Console" ];
           "signal-hook-registry" = [ "dep:signal-hook-registry" ];
           "socket2" = [ "dep:socket2" ];
@@ -13399,35 +12740,7 @@ rec {
           "tracing" = [ "dep:tracing" ];
           "windows-sys" = [ "dep:windows-sys" ];
         };
-        resolvedDefaultFeatures = [ "bytes" "default" "fs" "io-std" "io-util" "libc" "macros" "mio" "net" "num_cpus" "rt" "rt-multi-thread" "signal" "signal-hook-registry" "socket2" "sync" "test-util" "time" "tokio-macros" "windows-sys" ];
-      };
-      "tokio-io-timeout" = rec {
-        crateName = "tokio-io-timeout";
-        version = "1.2.0";
-        edition = "2018";
-        sha256 = "1gx84f92q1491vj4pkn81j8pz1s3pgwnbrsdhfsa2556mli41drh";
-        authors = [
-          "Steven Fackler <sfackler@gmail.com>"
-        ];
-        dependencies = [
-          {
-            name = "pin-project-lite";
-            packageId = "pin-project-lite";
-          }
-          {
-            name = "tokio";
-            packageId = "tokio";
-            features = [ "time" ];
-          }
-        ];
-        devDependencies = [
-          {
-            name = "tokio";
-            packageId = "tokio";
-            features = [ "full" ];
-          }
-        ];
-
+        resolvedDefaultFeatures = [ "bytes" "default" "fs" "io-std" "io-util" "libc" "macros" "mio" "net" "rt" "rt-multi-thread" "signal" "signal-hook-registry" "socket2" "sync" "test-util" "time" "tokio-macros" "windows-sys" ];
       };
       "tokio-listener" = rec {
         crateName = "tokio-listener";
@@ -13438,7 +12751,7 @@ rec {
         dependencies = [
           {
             name = "axum";
-            packageId = "axum 0.7.5";
+            packageId = "axum";
             rename = "axum07";
             optional = true;
           }
@@ -13464,7 +12777,7 @@ rec {
           }
           {
             name = "hyper";
-            packageId = "hyper 1.4.1";
+            packageId = "hyper";
             rename = "hyper1";
             optional = true;
             features = [ "server" ];
@@ -13506,7 +12819,7 @@ rec {
           }
           {
             name = "tonic";
-            packageId = "tonic 0.12.1";
+            packageId = "tonic";
             rename = "tonic_012";
             optional = true;
           }
@@ -13562,10 +12875,11 @@ rec {
       };
       "tokio-macros" = rec {
         crateName = "tokio-macros";
-        version = "2.2.0";
+        version = "2.4.0";
         edition = "2021";
-        sha256 = "0fwjy4vdx1h9pi4g2nml72wi0fr27b5m954p13ji9anyy8l1x2jv";
+        sha256 = "0lnpg14h1v3fh2jvnc8cz7cjf0m7z1xgkwfpcyy632g829imjgb9";
         procMacro = true;
+        libName = "tokio_macros";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -13580,7 +12894,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
             features = [ "full" ];
           }
         ];
@@ -13591,6 +12905,7 @@ rec {
         version = "0.3.0";
         edition = "2018";
         sha256 = "0kr1hnm5dmb9gfkby88yg2xj8g6x4i4gipva0c8ca3xyxhvfnmvz";
+        libName = "tokio_retry";
         authors = [
           "Sam Rijs <srijs@airpost.net>"
         ];
@@ -13618,82 +12933,16 @@ rec {
         ];
 
       };
-      "tokio-rustls 0.24.1" = rec {
-        crateName = "tokio-rustls";
-        version = "0.24.1";
-        edition = "2018";
-        sha256 = "10bhibg57mqir7xjhb2xmf24xgfpx6fzpyw720a4ih8a737jg0y2";
-        dependencies = [
-          {
-            name = "rustls";
-            packageId = "rustls 0.21.12";
-            usesDefaultFeatures = false;
-          }
-          {
-            name = "tokio";
-            packageId = "tokio";
-          }
-        ];
-        devDependencies = [
-          {
-            name = "tokio";
-            packageId = "tokio";
-            features = [ "full" ];
-          }
-        ];
-        features = {
-          "dangerous_configuration" = [ "rustls/dangerous_configuration" ];
-          "default" = [ "logging" "tls12" ];
-          "logging" = [ "rustls/logging" ];
-          "secret_extraction" = [ "rustls/secret_extraction" ];
-          "tls12" = [ "rustls/tls12" ];
-        };
-      };
-      "tokio-rustls 0.25.0" = rec {
-        crateName = "tokio-rustls";
-        version = "0.25.0";
-        edition = "2021";
-        sha256 = "03w6d5aqqf084rmcmrsyq5grhydl53blaiqcl0i2yfnv187hqpkp";
-        dependencies = [
-          {
-            name = "rustls";
-            packageId = "rustls 0.22.4";
-            usesDefaultFeatures = false;
-          }
-          {
-            name = "rustls-pki-types";
-            packageId = "rustls-pki-types";
-            rename = "pki-types";
-          }
-          {
-            name = "tokio";
-            packageId = "tokio";
-          }
-        ];
-        devDependencies = [
-          {
-            name = "tokio";
-            packageId = "tokio";
-            features = [ "full" ];
-          }
-        ];
-        features = {
-          "default" = [ "logging" "tls12" "ring" ];
-          "logging" = [ "rustls/logging" ];
-          "ring" = [ "rustls/ring" ];
-          "tls12" = [ "rustls/tls12" ];
-        };
-        resolvedDefaultFeatures = [ "default" "logging" "ring" "tls12" ];
-      };
-      "tokio-rustls 0.26.0" = rec {
+      "tokio-rustls" = rec {
         crateName = "tokio-rustls";
         version = "0.26.0";
         edition = "2021";
         sha256 = "1m00czrmk8x7pdjnz10a3da3i1d0sdf9j9vfp5dnk5ss1q6w8yqc";
+        libName = "tokio_rustls";
         dependencies = [
           {
             name = "rustls";
-            packageId = "rustls 0.23.7";
+            packageId = "rustls";
             usesDefaultFeatures = false;
             features = [ "std" ];
           }
@@ -13727,9 +12976,10 @@ rec {
       };
       "tokio-stream" = rec {
         crateName = "tokio-stream";
-        version = "0.1.14";
+        version = "0.1.15";
         edition = "2021";
-        sha256 = "0hi8hcwavh5sdi1ivc9qc4yvyr32f153c212dpd7sb366y6rhz1r";
+        sha256 = "1brpbsqyg8yfmfc4y0j9zxvc8xsxjc31d48kb0g6jvpc1fgchyi6";
+        libName = "tokio_stream";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -13773,6 +13023,7 @@ rec {
         version = "0.3.1";
         edition = "2018";
         sha256 = "0xffvap4g7hlswk5daklk3jaqha6s6wxw72c24kmqgna23018mwx";
+        libName = "tokio_tar";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
           "dignifiedquire <me@dignifiequire.com>"
@@ -13829,9 +13080,10 @@ rec {
       };
       "tokio-test" = rec {
         crateName = "tokio-test";
-        version = "0.4.3";
+        version = "0.4.4";
         edition = "2021";
-        sha256 = "06fplzcc2ymahfzykd2ickw2qn7g3lz47bll00865s1spnx3r6z8";
+        sha256 = "1xzri2m3dg8nzdyznm77nymvil9cyh1gfdfrbnska51iqfmvls14";
+        libName = "tokio_test";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -13869,9 +13121,10 @@ rec {
       };
       "tokio-util" = rec {
         crateName = "tokio-util";
-        version = "0.7.10";
+        version = "0.7.11";
         edition = "2021";
-        sha256 = "058y6x4mf0fsqji9rfyb77qbfyc50y4pk2spqgj6xsyr693z66al";
+        sha256 = "1qcz30db6m8lxkl61b3nic4bim1symi636nhbb3rmi3i6xxv9xlw";
+        libName = "tokio_util";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -13902,13 +13155,6 @@ rec {
             packageId = "tokio";
             features = [ "sync" ];
           }
-          {
-            name = "tracing";
-            packageId = "tracing";
-            optional = true;
-            usesDefaultFeatures = false;
-            features = [ "std" ];
-          }
         ];
         devDependencies = [
           {
@@ -13919,7 +13165,6 @@ rec {
         ];
         features = {
           "__docs_rs" = [ "futures-util" ];
-          "codec" = [ "tracing" ];
           "compat" = [ "futures-io" ];
           "full" = [ "codec" "compat" "io-util" "time" "net" "rt" ];
           "futures-io" = [ "dep:futures-io" ];
@@ -13932,7 +13177,7 @@ rec {
           "time" = [ "tokio/time" "slab" ];
           "tracing" = [ "dep:tracing" ];
         };
-        resolvedDefaultFeatures = [ "codec" "compat" "default" "futures-io" "io" "io-util" "net" "tracing" ];
+        resolvedDefaultFeatures = [ "codec" "compat" "default" "futures-io" "io" "io-util" "net" ];
       };
       "toml 0.6.0" = rec {
         crateName = "toml";
@@ -13980,11 +13225,11 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" "display" "parse" ];
       };
-      "toml 0.8.15" = rec {
+      "toml 0.8.19" = rec {
         crateName = "toml";
-        version = "0.8.15";
+        version = "0.8.19";
         edition = "2021";
-        sha256 = "0a2wk4xjdqw59w0jjia3b7p3yinv7wlv68xfq338hwvmpyqalb5c";
+        sha256 = "0knjd3mkxyb87qcs2dark3qkpadidap3frqfj5nqvhpxwfc1zvd1";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -14000,12 +13245,12 @@ rec {
           }
           {
             name = "toml_datetime";
-            packageId = "toml_datetime 0.6.6";
+            packageId = "toml_datetime 0.6.8";
             features = [ "serde" ];
           }
           {
             name = "toml_edit";
-            packageId = "toml_edit 0.22.16";
+            packageId = "toml_edit 0.22.20";
             optional = true;
             usesDefaultFeatures = false;
             features = [ "serde" ];
@@ -14047,11 +13292,11 @@ rec {
         };
         resolvedDefaultFeatures = [ "serde" ];
       };
-      "toml_datetime 0.6.6" = rec {
+      "toml_datetime 0.6.8" = rec {
         crateName = "toml_datetime";
-        version = "0.6.6";
+        version = "0.6.8";
         edition = "2021";
-        sha256 = "1grcrr3gh7id3cy3j700kczwwfbn04p5ncrrj369prjaj9bgvbab";
+        sha256 = "0hgv7v9g35d7y9r2afic58jvlwnf73vgd1mz2k8gihlgrf73bmqd";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -14108,11 +13353,11 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" "serde" ];
       };
-      "toml_edit 0.22.16" = rec {
+      "toml_edit 0.22.20" = rec {
         crateName = "toml_edit";
-        version = "0.22.16";
+        version = "0.22.20";
         edition = "2021";
-        sha256 = "1207pygznx57j9hrkmmfiq8mxjkba5c7g1yfjk4ij8hmir8kv3r7";
+        sha256 = "07ffw4626k6abicjxb2idh12f1p5fn965zk660zhqsyj5b048g2q";
         authors = [
           "Andronik Ordian <write@reusable.software>"
           "Ed Page <eopage@gmail.com>"
@@ -14136,7 +13381,7 @@ rec {
           }
           {
             name = "toml_datetime";
-            packageId = "toml_datetime 0.6.6";
+            packageId = "toml_datetime 0.6.8";
           }
           {
             name = "winnow";
@@ -14152,11 +13397,11 @@ rec {
         };
         resolvedDefaultFeatures = [ "display" "parse" "serde" ];
       };
-      "tonic 0.11.0" = rec {
+      "tonic" = rec {
         crateName = "tonic";
-        version = "0.11.0";
+        version = "0.12.2";
         edition = "2021";
-        sha256 = "04qsr527i256i3dk9dp1g2jr42q7yl91y5h06rvd9ycy9rxfpi3n";
+        sha256 = "1bc8m8r7ysgkb7mhs3b3mvivd43nwaix6qnqhfp5hb2bkscbmxn6";
         authors = [
           "Lucio Franco <luciofranco14@gmail.com>"
         ];
@@ -14173,148 +13418,7 @@ rec {
           }
           {
             name = "axum";
-            packageId = "axum 0.6.20";
-            optional = true;
-            usesDefaultFeatures = false;
-          }
-          {
-            name = "base64";
-            packageId = "base64 0.21.7";
-          }
-          {
-            name = "bytes";
-            packageId = "bytes";
-          }
-          {
-            name = "h2";
-            packageId = "h2 0.3.26";
-            optional = true;
-          }
-          {
-            name = "http";
-            packageId = "http 0.2.11";
-          }
-          {
-            name = "http-body";
-            packageId = "http-body 0.4.6";
-          }
-          {
-            name = "hyper";
-            packageId = "hyper 0.14.28";
-            optional = true;
-            features = [ "full" ];
-          }
-          {
-            name = "hyper-timeout";
-            packageId = "hyper-timeout 0.4.1";
-            optional = true;
-          }
-          {
-            name = "percent-encoding";
-            packageId = "percent-encoding";
-          }
-          {
-            name = "pin-project";
-            packageId = "pin-project";
-          }
-          {
-            name = "prost";
-            packageId = "prost 0.12.3";
-            optional = true;
-            usesDefaultFeatures = false;
-            features = [ "std" ];
-          }
-          {
-            name = "rustls-pemfile";
-            packageId = "rustls-pemfile 2.1.0";
-            optional = true;
-          }
-          {
-            name = "rustls-pki-types";
-            packageId = "rustls-pki-types";
-            optional = true;
-          }
-          {
-            name = "tokio";
-            packageId = "tokio";
-          }
-          {
-            name = "tokio-rustls";
-            packageId = "tokio-rustls 0.25.0";
-            optional = true;
-          }
-          {
-            name = "tokio-stream";
-            packageId = "tokio-stream";
-          }
-          {
-            name = "tower";
-            packageId = "tower";
-            optional = true;
-            usesDefaultFeatures = false;
-            features = [ "balance" "buffer" "discover" "limit" "load" "make" "timeout" "util" ];
-          }
-          {
-            name = "tower-layer";
-            packageId = "tower-layer";
-          }
-          {
-            name = "tower-service";
-            packageId = "tower-service";
-          }
-          {
-            name = "tracing";
-            packageId = "tracing";
-          }
-        ];
-        devDependencies = [
-          {
-            name = "tokio";
-            packageId = "tokio";
-            features = [ "rt" "macros" ];
-          }
-          {
-            name = "tower";
-            packageId = "tower";
-            features = [ "full" ];
-          }
-        ];
-        features = {
-          "codegen" = [ "dep:async-trait" ];
-          "default" = [ "transport" "codegen" "prost" ];
-          "gzip" = [ "dep:flate2" ];
-          "prost" = [ "dep:prost" ];
-          "tls" = [ "dep:rustls-pki-types" "dep:rustls-pemfile" "transport" "dep:tokio-rustls" "tokio/rt" "tokio/macros" ];
-          "tls-roots" = [ "tls-roots-common" "dep:rustls-native-certs" ];
-          "tls-roots-common" = [ "tls" ];
-          "tls-webpki-roots" = [ "tls-roots-common" "dep:webpki-roots" ];
-          "transport" = [ "dep:async-stream" "dep:axum" "channel" "dep:h2" "dep:hyper" "tokio/net" "tokio/time" "dep:tower" "dep:hyper-timeout" ];
-          "zstd" = [ "dep:zstd" ];
-        };
-        resolvedDefaultFeatures = [ "channel" "codegen" "default" "prost" "tls" "transport" ];
-      };
-      "tonic 0.12.1" = rec {
-        crateName = "tonic";
-        version = "0.12.1";
-        edition = "2021";
-        sha256 = "00bl9i4fal2s90g1p877053dkpbxvgsqj5c24y6mka5bj559yr9q";
-        authors = [
-          "Lucio Franco <luciofranco14@gmail.com>"
-        ];
-        dependencies = [
-          {
-            name = "async-stream";
-            packageId = "async-stream";
-            optional = true;
-          }
-          {
-            name = "async-trait";
-            packageId = "async-trait";
-            optional = true;
-          }
-          {
-            name = "axum";
-            packageId = "axum 0.7.5";
+            packageId = "axum";
             optional = true;
             usesDefaultFeatures = false;
           }
@@ -14328,16 +13432,16 @@ rec {
           }
           {
             name = "h2";
-            packageId = "h2 0.4.4";
+            packageId = "h2";
             optional = true;
           }
           {
             name = "http";
-            packageId = "http 1.1.0";
+            packageId = "http";
           }
           {
             name = "http-body";
-            packageId = "http-body 1.0.0";
+            packageId = "http-body";
           }
           {
             name = "http-body-util";
@@ -14345,13 +13449,13 @@ rec {
           }
           {
             name = "hyper";
-            packageId = "hyper 1.4.1";
+            packageId = "hyper";
             optional = true;
             features = [ "http1" "http2" ];
           }
           {
             name = "hyper-timeout";
-            packageId = "hyper-timeout 0.5.1";
+            packageId = "hyper-timeout";
             optional = true;
           }
           {
@@ -14370,19 +13474,19 @@ rec {
           }
           {
             name = "prost";
-            packageId = "prost 0.13.1";
+            packageId = "prost";
             optional = true;
             usesDefaultFeatures = false;
             features = [ "std" ];
           }
           {
             name = "rustls-native-certs";
-            packageId = "rustls-native-certs 0.7.0";
+            packageId = "rustls-native-certs";
             optional = true;
           }
           {
             name = "rustls-pemfile";
-            packageId = "rustls-pemfile 2.1.0";
+            packageId = "rustls-pemfile";
             optional = true;
           }
           {
@@ -14399,7 +13503,7 @@ rec {
           }
           {
             name = "tokio-rustls";
-            packageId = "tokio-rustls 0.26.0";
+            packageId = "tokio-rustls";
             optional = true;
             usesDefaultFeatures = false;
             features = [ "logging" "tls12" "ring" ];
@@ -14449,18 +13553,20 @@ rec {
           "router" = [ "dep:axum" "dep:tower" "tower?/util" ];
           "server" = [ "router" "dep:async-stream" "dep:h2" "dep:hyper" "hyper?/server" "dep:hyper-util" "hyper-util?/service" "hyper-util?/server-auto" "dep:socket2" "dep:tokio" "tokio?/macros" "tokio?/net" "tokio?/time" "tokio-stream/net" "dep:tower" "tower?/util" "tower?/limit" ];
           "tls" = [ "dep:rustls-pemfile" "dep:tokio-rustls" "dep:tokio" "tokio?/rt" "tokio?/macros" ];
-          "tls-roots" = [ "tls" "channel" "dep:rustls-native-certs" ];
+          "tls-native-roots" = [ "tls" "channel" "dep:rustls-native-certs" ];
+          "tls-roots" = [ "tls-native-roots" ];
           "tls-webpki-roots" = [ "tls" "channel" "dep:webpki-roots" ];
           "transport" = [ "server" "channel" ];
           "zstd" = [ "dep:zstd" ];
         };
-        resolvedDefaultFeatures = [ "channel" "codegen" "default" "prost" "router" "server" "tls" "tls-roots" "transport" ];
+        resolvedDefaultFeatures = [ "channel" "codegen" "default" "prost" "router" "server" "tls" "tls-native-roots" "tls-roots" "transport" ];
       };
-      "tonic-build 0.11.0" = rec {
+      "tonic-build" = rec {
         crateName = "tonic-build";
-        version = "0.11.0";
+        version = "0.12.2";
         edition = "2021";
-        sha256 = "1hm99ckaw0pzq8h22bdjy6gpbg06kpvs0f73nj60f456f3fzckmy";
+        sha256 = "0rwaxvsx4rld1ncmcih0bvmg8k8ah4r1ccyjwgbnn4shfa3yhkpy";
+        libName = "tonic_build";
         authors = [
           "Lucio Franco <luciofranco14@gmail.com>"
         ];
@@ -14475,7 +13581,7 @@ rec {
           }
           {
             name = "prost-build";
-            packageId = "prost-build 0.12.3";
+            packageId = "prost-build";
             optional = true;
           }
           {
@@ -14484,7 +13590,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
           }
         ];
         features = {
@@ -14495,50 +13601,12 @@ rec {
         };
         resolvedDefaultFeatures = [ "cleanup-markdown" "default" "prost" "prost-build" "transport" ];
       };
-      "tonic-build 0.12.1" = rec {
-        crateName = "tonic-build";
-        version = "0.12.1";
-        edition = "2021";
-        sha256 = "0r5riq6gm79hmmsd1z6rdziszava2y8phf1z7rr2005xlb2r50sn";
-        authors = [
-          "Lucio Franco <luciofranco14@gmail.com>"
-        ];
-        dependencies = [
-          {
-            name = "prettyplease";
-            packageId = "prettyplease";
-          }
-          {
-            name = "proc-macro2";
-            packageId = "proc-macro2";
-          }
-          {
-            name = "prost-build";
-            packageId = "prost-build 0.13.1";
-            optional = true;
-          }
-          {
-            name = "quote";
-            packageId = "quote";
-          }
-          {
-            name = "syn";
-            packageId = "syn 2.0.48";
-          }
-        ];
-        features = {
-          "cleanup-markdown" = [ "prost" "prost-build/cleanup-markdown" ];
-          "default" = [ "transport" "prost" ];
-          "prost" = [ "prost-build" ];
-          "prost-build" = [ "dep:prost-build" ];
-        };
-        resolvedDefaultFeatures = [ "default" "prost" "prost-build" "transport" ];
-      };
       "tonic-health" = rec {
         crateName = "tonic-health";
-        version = "0.12.1";
+        version = "0.12.2";
         edition = "2021";
-        sha256 = "0ma93g27szqjh4gniws6wz82ils2hhvd91rl8k7bc27fjrm0xqg1";
+        sha256 = "19xm66ban6w8gjybqhp4ra3zz9pnqfjis3j95dmy5fh6yzk382pc";
+        libName = "tonic_health";
         authors = [
           "James Nugent <james@jen20.com>"
         ];
@@ -14549,7 +13617,7 @@ rec {
           }
           {
             name = "prost";
-            packageId = "prost 0.13.1";
+            packageId = "prost";
           }
           {
             name = "tokio";
@@ -14562,7 +13630,7 @@ rec {
           }
           {
             name = "tonic";
-            packageId = "tonic 0.12.1";
+            packageId = "tonic";
             usesDefaultFeatures = false;
             features = [ "codegen" "prost" ];
           }
@@ -14584,9 +13652,10 @@ rec {
       };
       "tonic-reflection" = rec {
         crateName = "tonic-reflection";
-        version = "0.12.1";
+        version = "0.12.2";
         edition = "2021";
-        sha256 = "0wy1vdj4fj90j6zjqs78c2fvwcl9krxiz6709idspsbksqxchhmp";
+        sha256 = "1zwrm9zzahipsrmaqfp4vk0w31qymf740fsp0yczh16vxrsbhmkv";
+        libName = "tonic_reflection";
         authors = [
           "James Nugent <james@jen20.com>"
           "Samani G. Gikandi <samani@gojulas.com>"
@@ -14594,11 +13663,11 @@ rec {
         dependencies = [
           {
             name = "prost";
-            packageId = "prost 0.13.1";
+            packageId = "prost";
           }
           {
             name = "prost-types";
-            packageId = "prost-types 0.13.1";
+            packageId = "prost-types";
             optional = true;
           }
           {
@@ -14615,7 +13684,7 @@ rec {
           }
           {
             name = "tonic";
-            packageId = "tonic 0.12.1";
+            packageId = "tonic";
             usesDefaultFeatures = false;
             features = [ "codegen" "prost" ];
           }
@@ -14623,7 +13692,7 @@ rec {
         devDependencies = [
           {
             name = "tonic";
-            packageId = "tonic 0.12.1";
+            packageId = "tonic";
             usesDefaultFeatures = false;
             features = [ "transport" ];
           }
@@ -14754,20 +13823,21 @@ rec {
           "tracing" = [ "dep:tracing" ];
           "util" = [ "__common" "futures-util" "pin-project" ];
         };
-        resolvedDefaultFeatures = [ "__common" "balance" "buffer" "default" "discover" "futures-core" "futures-util" "indexmap" "limit" "load" "log" "make" "pin-project" "pin-project-lite" "rand" "ready-cache" "slab" "timeout" "tokio" "tokio-util" "tracing" "util" ];
+        resolvedDefaultFeatures = [ "__common" "balance" "buffer" "default" "discover" "futures-core" "futures-util" "indexmap" "limit" "load" "log" "make" "pin-project" "pin-project-lite" "rand" "ready-cache" "slab" "tokio" "tokio-util" "tracing" "util" ];
       };
       "tower-http" = rec {
         crateName = "tower-http";
         version = "0.5.2";
         edition = "2018";
         sha256 = "1xakj3x0anp55gjqibiwvzma5iz0w9pcjsr7qk97sx4qm4sd970y";
+        libName = "tower_http";
         authors = [
           "Tower Maintainers <team@tower-rs.com>"
         ];
         dependencies = [
           {
             name = "bitflags";
-            packageId = "bitflags 2.4.2";
+            packageId = "bitflags 2.6.0";
           }
           {
             name = "bytes";
@@ -14775,11 +13845,11 @@ rec {
           }
           {
             name = "http";
-            packageId = "http 1.1.0";
+            packageId = "http";
           }
           {
             name = "http-body";
-            packageId = "http-body 1.0.0";
+            packageId = "http-body";
           }
           {
             name = "http-body-util";
@@ -14854,6 +13924,7 @@ rec {
         version = "0.3.2";
         edition = "2018";
         sha256 = "1l7i17k9vlssrdg4s3b0ia5jjkmmxsvv8s9y9ih0jfi8ssz8s362";
+        libName = "tower_layer";
         authors = [
           "Tower Maintainers <team@tower-rs.com>"
         ];
@@ -14864,6 +13935,7 @@ rec {
         version = "0.3.2";
         edition = "2018";
         sha256 = "0lmfzmmvid2yp2l36mbavhmqgsvzqf7r2wiwz73ml4xmwaf1rg5n";
+        libName = "tower_service";
         authors = [
           "Tower Maintainers <team@tower-rs.com>"
         ];
@@ -14922,6 +13994,7 @@ rec {
         edition = "2018";
         sha256 = "1rvb5dn9z6d0xdj14r403z0af0bbaqhg02hq4jc97g5wds6lqw1l";
         procMacro = true;
+        libName = "tracing_attributes";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
           "Eliza Weisman <eliza@buoyant.io>"
@@ -14938,7 +14011,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
             usesDefaultFeatures = false;
             features = [ "full" "parsing" "printing" "visit-mut" "clone-impls" "extra-traits" "proc-macro" ];
           }
@@ -14950,6 +14023,7 @@ rec {
         version = "0.1.32";
         edition = "2018";
         sha256 = "0m5aglin3cdwxpvbg6kz0r9r0k31j48n0kcfwsp6l49z26k3svf0";
+        libName = "tracing_core";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -14980,6 +14054,7 @@ rec {
         version = "0.2.5";
         edition = "2018";
         sha256 = "1wimg0iwa2ldq7xv98lvivvf3q9ykfminig8r1bs0ig22np9bl4p";
+        libName = "tracing_futures";
         authors = [
           "Eliza Weisman <eliza@buoyant.io>"
           "Tokio Contributors <team@tokio.rs>"
@@ -15043,6 +14118,7 @@ rec {
         version = "0.2.0";
         edition = "2018";
         sha256 = "1hs77z026k730ij1a9dhahzrl0s073gfa2hm5p0fbl0b80gmz1gf";
+        libName = "tracing_log";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -15074,6 +14150,7 @@ rec {
         version = "0.23.0";
         edition = "2018";
         sha256 = "1112kmckw0qwyckhbwarb230n4ldmfgzixr9jagbfjmy3fx19gm9";
+        libName = "tracing_opentelemetry";
         authors = [
           "Julian Tescher <julian@tescher.me>"
           "Tokio Contributors <team@tokio.rs>"
@@ -15174,6 +14251,7 @@ rec {
         version = "0.25.0";
         edition = "2021";
         sha256 = "0fzzhpcxngnxra56cxmslr5y6k0f1b4ghqv9vz41p4kxvba4wy59";
+        libName = "tracing_opentelemetry";
         dependencies = [
           {
             name = "js-sys";
@@ -15272,6 +14350,7 @@ rec {
         version = "0.3.18";
         edition = "2018";
         sha256 = "12vs1bwk4kig1l2qqjbbn2nm5amwiqmkcmnznylzmnfvjy6083xd";
+        libName = "tracing_subscriber";
         authors = [
           "Eliza Weisman <eliza@buoyant.io>"
           "David Barsky <me@davidbarsky.com>"
@@ -15382,9 +14461,10 @@ rec {
       };
       "tracing-tracy" = rec {
         crateName = "tracing-tracy";
-        version = "0.11.0";
+        version = "0.11.2";
         edition = "2021";
-        sha256 = "051nf1ncp1z45acgk0h5199ldlhb0wmkmvhyvk8x17x6hi7x0930";
+        sha256 = "16xnvxshac0qv61zzv0rm4sl8x0hkws7cngx5z3fambgy4chbaf6";
+        libName = "tracing_tracy";
         authors = [
           "Simonas Kazlauskas <tracing-tracy@kazlauskas.me>"
         ];
@@ -15415,6 +14495,7 @@ rec {
           "context-switch-tracing" = [ "client/context-switch-tracing" ];
           "default" = [ "enable" "system-tracing" "context-switch-tracing" "sampling" "code-transfer" "broadcast" "callstack-inlines" ];
           "delayed-init" = [ "client/delayed-init" ];
+          "demangle" = [ "client/demangle" ];
           "enable" = [ "client/enable" ];
           "fibers" = [ "client/fibers" ];
           "flush-on-exit" = [ "client/flush-on-exit" ];
@@ -15425,6 +14506,7 @@ rec {
           "sampling" = [ "client/sampling" ];
           "system-tracing" = [ "client/system-tracing" ];
           "timer-fallback" = [ "client/timer-fallback" ];
+          "verify" = [ "client/verify" ];
         };
         resolvedDefaultFeatures = [ "broadcast" "callstack-inlines" "code-transfer" "context-switch-tracing" "default" "enable" "flush-on-exit" "sampling" "system-tracing" ];
       };
@@ -15433,6 +14515,7 @@ rec {
         version = "0.17.0";
         edition = "2021";
         sha256 = "07bla4iigg17fl4zil2dwizslhw8mv8vrsfkhr7ri27zchd97ysr";
+        libName = "tracy_client";
         authors = [
           "Simonas Kazlauskas <tracy-client@kazlauskas.me>"
         ];
@@ -15478,6 +14561,7 @@ rec {
         version = "0.22.2";
         edition = "2021";
         sha256 = "10h8msq85b7rhfg2vg22g2iizbk4c6fcq0jiadad37gs1mhls44x";
+        libName = "tracy_client_sys";
         authors = [
           "Simonas Kazlauskas <tracy-client-sys@kazlauskas.me>"
         ];
@@ -15499,11 +14583,51 @@ rec {
         version = "0.2.5";
         edition = "2015";
         sha256 = "0jqijrrvm1pyq34zn1jmy2vihd4jcrjlvsh4alkjahhssjnsn8g4";
+        libName = "try_lock";
         authors = [
           "Sean McArthur <sean@seanmonstar.com>"
         ];
 
       };
+      "trybuild" = rec {
+        crateName = "trybuild";
+        version = "1.0.99";
+        edition = "2021";
+        sha256 = "1s4i2hpyb66676xkg6b6fxm2qdsawj5lfad8ds68vgn46q6sayi0";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "glob";
+            packageId = "glob";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "termcolor";
+            packageId = "termcolor";
+          }
+          {
+            name = "toml";
+            packageId = "toml 0.8.19";
+          }
+        ];
+        features = {
+          "diff" = [ "dissimilar" ];
+          "dissimilar" = [ "dep:dissimilar" ];
+        };
+      };
       "tvix-build" = rec {
         crateName = "tvix-build";
         version = "0.1.0";
@@ -15516,6 +14640,7 @@ rec {
           }
         ];
         src = lib.cleanSourceWith { filter = sourceFilter; src = ./build; };
+        libName = "tvix_build";
         dependencies = [
           {
             name = "bytes";
@@ -15528,7 +14653,7 @@ rec {
           }
           {
             name = "itertools";
-            packageId = "itertools 0.12.0";
+            packageId = "itertools 0.12.1";
           }
           {
             name = "mimalloc";
@@ -15536,7 +14661,7 @@ rec {
           }
           {
             name = "prost";
-            packageId = "prost 0.13.1";
+            packageId = "prost";
           }
           {
             name = "thiserror";
@@ -15553,7 +14678,7 @@ rec {
           }
           {
             name = "tonic";
-            packageId = "tonic 0.12.1";
+            packageId = "tonic";
             features = [ "tls" "tls-roots" ];
           }
           {
@@ -15581,11 +14706,11 @@ rec {
         buildDependencies = [
           {
             name = "prost-build";
-            packageId = "prost-build 0.13.1";
+            packageId = "prost-build";
           }
           {
             name = "tonic-build";
-            packageId = "tonic-build 0.12.1";
+            packageId = "tonic-build";
           }
         ];
         devDependencies = [
@@ -15604,6 +14729,7 @@ rec {
         version = "0.1.0";
         edition = "2021";
         src = lib.cleanSourceWith { filter = sourceFilter; src = ./castore; };
+        libName = "tvix_castore";
         dependencies = [
           {
             name = "async-compression";
@@ -15694,7 +14820,7 @@ rec {
           }
           {
             name = "prost";
-            packageId = "prost 0.13.1";
+            packageId = "prost";
           }
           {
             name = "redb";
@@ -15751,7 +14877,7 @@ rec {
           }
           {
             name = "tonic";
-            packageId = "tonic 0.12.1";
+            packageId = "tonic";
           }
           {
             name = "tonic-reflection";
@@ -15821,11 +14947,11 @@ rec {
         buildDependencies = [
           {
             name = "prost-build";
-            packageId = "prost-build 0.13.1";
+            packageId = "prost-build";
           }
           {
             name = "tonic-build";
-            packageId = "tonic-build 0.12.1";
+            packageId = "tonic-build";
           }
         ];
         devDependencies = [
@@ -15884,6 +15010,7 @@ rec {
           }
         ];
         src = lib.cleanSourceWith { filter = sourceFilter; src = ./cli; };
+        libName = "tvix_cli";
         dependencies = [
           {
             name = "bytes";
@@ -16021,13 +15148,8 @@ rec {
             usesDefaultFeatures = false;
           }
           {
-            name = "imbl";
-            packageId = "imbl";
-            features = [ "serde" ];
-          }
-          {
             name = "itertools";
-            packageId = "itertools 0.12.0";
+            packageId = "itertools 0.12.1";
           }
           {
             name = "lazy_static";
@@ -16043,6 +15165,10 @@ rec {
             packageId = "md-5";
           }
           {
+            name = "nohash-hasher";
+            packageId = "nohash-hasher";
+          }
+          {
             name = "os_str_bytes";
             packageId = "os_str_bytes";
             features = [ "conversions" ];
@@ -16113,6 +15239,10 @@ rec {
             packageId = "tvix-eval-builtin-macros";
             rename = "builtin-macros";
           }
+          {
+            name = "vu128";
+            packageId = "vu128";
+          }
         ];
         devDependencies = [
           {
@@ -16121,7 +15251,7 @@ rec {
           }
           {
             name = "itertools";
-            packageId = "itertools 0.12.0";
+            packageId = "itertools 0.12.1";
           }
           {
             name = "mimalloc";
@@ -16141,7 +15271,7 @@ rec {
           }
         ];
         features = {
-          "arbitrary" = [ "proptest" "test-strategy" "imbl/proptest" ];
+          "arbitrary" = [ "proptest" "test-strategy" ];
           "default" = [ "impure" "arbitrary" "nix_tests" ];
           "proptest" = [ "dep:proptest" ];
           "test-strategy" = [ "dep:test-strategy" ];
@@ -16154,6 +15284,7 @@ rec {
         edition = "2021";
         src = lib.cleanSourceWith { filter = sourceFilter; src = ./eval/builtin-macros; };
         procMacro = true;
+        libName = "tvix_eval_builtin_macros";
         authors = [
           "Griffin Smith <root@gws.fyi>"
         ];
@@ -16185,6 +15316,7 @@ rec {
         version = "0.1.0";
         edition = "2021";
         src = lib.cleanSourceWith { filter = sourceFilter; src = ./glue; };
+        libName = "tvix_glue";
         dependencies = [
           {
             name = "async-compression";
@@ -16202,7 +15334,6 @@ rec {
           {
             name = "clap";
             packageId = "clap";
-            usesDefaultFeatures = false;
           }
           {
             name = "data-encoding";
@@ -16345,6 +15476,10 @@ rec {
             name = "tempfile";
             packageId = "tempfile";
           }
+          {
+            name = "tokio-test";
+            packageId = "tokio-test";
+          }
         ];
         features = {
           "default" = [ "nix_tests" ];
@@ -16356,6 +15491,7 @@ rec {
         version = "0.1.0";
         edition = "2021";
         src = lib.cleanSourceWith { filter = sourceFilter; src = ./serde; };
+        libName = "tvix_serde";
         dependencies = [
           {
             name = "bstr";
@@ -16386,6 +15522,7 @@ rec {
           }
         ];
         src = lib.cleanSourceWith { filter = sourceFilter; src = ./store; };
+        libName = "tvix_store";
         dependencies = [
           {
             name = "anyhow";
@@ -16466,7 +15603,7 @@ rec {
           }
           {
             name = "prost";
-            packageId = "prost 0.13.1";
+            packageId = "prost";
           }
           {
             name = "redb";
@@ -16533,12 +15670,12 @@ rec {
           }
           {
             name = "toml";
-            packageId = "toml 0.8.15";
+            packageId = "toml 0.8.19";
             optional = true;
           }
           {
             name = "tonic";
-            packageId = "tonic 0.12.1";
+            packageId = "tonic";
             features = [ "tls" "tls-roots" ];
           }
           {
@@ -16589,11 +15726,11 @@ rec {
         buildDependencies = [
           {
             name = "prost-build";
-            packageId = "prost-build 0.13.1";
+            packageId = "prost-build";
           }
           {
             name = "tonic-build";
-            packageId = "tonic-build 0.12.1";
+            packageId = "tonic-build";
           }
         ];
         devDependencies = [
@@ -16636,15 +15773,16 @@ rec {
         version = "0.1.0";
         edition = "2021";
         src = lib.cleanSourceWith { filter = sourceFilter; src = ./tracing; };
+        libName = "tvix_tracing";
         dependencies = [
           {
             name = "axum";
-            packageId = "axum 0.7.5";
+            packageId = "axum";
             optional = true;
           }
           {
             name = "http";
-            packageId = "http 1.1.0";
+            packageId = "http";
             optional = true;
           }
           {
@@ -16693,7 +15831,7 @@ rec {
           }
           {
             name = "tonic";
-            packageId = "tonic 0.12.1";
+            packageId = "tonic";
             optional = true;
           }
           {
@@ -16800,6 +15938,7 @@ rec {
         edition = "2021";
         sha256 = "1cabnvm526bcgh1sh34js5ils0gz4xwlgvwhm992acdr8xzqhwxc";
         procMacro = true;
+        libName = "typetag_impl";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -16814,7 +15953,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
             features = [ "full" ];
           }
         ];
@@ -16867,6 +16006,7 @@ rec {
         version = "1.0.12";
         edition = "2018";
         sha256 = "0jzf1znfpb2gx8nr8mvmyqs1crnv79l57nxnbiszc7xf7ynbjm1k";
+        libName = "unicode_ident";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -16877,6 +16017,7 @@ rec {
         version = "0.1.22";
         edition = "2018";
         sha256 = "08d95g7b1irc578b2iyhzv4xhsa4pfvwsqxcl9lbcpabzkq16msw";
+        libName = "unicode_normalization";
         authors = [
           "kwantam <kwantam@gmail.com>"
           "Manish Goregaokar <manishsmail@gmail.com>"
@@ -16898,6 +16039,7 @@ rec {
         version = "1.10.1";
         edition = "2018";
         sha256 = "0dky2hm5k51xy11hc3nk85p533rvghd462b6i0c532b7hl4j9mhx";
+        libName = "unicode_segmentation";
         authors = [
           "kwantam <kwantam@gmail.com>"
           "Manish Goregaokar <manishsmail@gmail.com>"
@@ -16909,6 +16051,7 @@ rec {
         version = "0.1.11";
         edition = "2015";
         sha256 = "11ds4ydhg8g7l06rlmh712q41qsrd0j0h00n1jm74kww3kqk65z5";
+        libName = "unicode_width";
         authors = [
           "kwantam <kwantam@gmail.com>"
           "Manish Goregaokar <manishsmail@gmail.com>"
@@ -16933,9 +16076,9 @@ rec {
       };
       "url" = rec {
         crateName = "url";
-        version = "2.5.0";
+        version = "2.5.2";
         edition = "2018";
-        sha256 = "0cs65961miawncdg2z20171w0vqrmraswv2ihdpd8lxp7cp31rii";
+        sha256 = "0v2dx50mx7xzl9454cl5qmpjnhkbahmn59gd3apyipbgyyylsy12";
         authors = [
           "The rust-url developers"
         ];
@@ -17102,6 +16245,7 @@ rec {
         version = "0.8.0";
         edition = "2018";
         sha256 = "00s33wy8cj2i8b4hlxn7wd8zm1fpaa5kjhzv77b3khsavf8pn8wz";
+        libName = "vhost_user_backend";
         authors = [
           "The Cloud Hypervisor Authors"
         ];
@@ -17156,6 +16300,7 @@ rec {
         version = "0.1.0";
         edition = "2018";
         sha256 = "0sxxhhmz1r4s4q5pd2lykswcv9qk05fmpwc5xlb8aj45h8bi5x9z";
+        libName = "virtio_bindings";
         authors = [
           "Sergio Lopez <slp@redhat.com>"
         ];
@@ -17166,6 +16311,7 @@ rec {
         version = "0.2.2";
         edition = "2021";
         sha256 = "11mfm9brqgwpfg0izsgc4n7xkqwxk5ad03ivslq0r88j50dwp2w7";
+        libName = "virtio_bindings";
         authors = [
           "Sergio Lopez <slp@redhat.com>"
         ];
@@ -17176,6 +16322,7 @@ rec {
         version = "0.7.1";
         edition = "2021";
         sha256 = "1gbppbapj7c0vyca88vl34cx4sp2cy9yg0v6bvyd5h11rhmixa1v";
+        libName = "virtio_queue";
         authors = [
           "The Chromium OS Authors"
         ];
@@ -17211,6 +16358,7 @@ rec {
         version = "0.10.0";
         edition = "2021";
         sha256 = "0z423a8i4s3addq4yjad4ar5l6qwarjwdn94lismbd0mcqv712k8";
+        libName = "vm_memory";
         authors = [
           "Liu Jiang <gerry@linux.alibaba.com>"
         ];
@@ -17242,6 +16390,7 @@ rec {
         version = "0.11.2";
         edition = "2021";
         sha256 = "0a9azxk6wsahwkggshbdga4jdryzfw6j5r21f11gf50j4f2b1ds8";
+        libName = "vmm_sys_util";
         authors = [
           "Intel Virtualization Team <vmm-maintainers@intel.com>"
         ];
@@ -17353,29 +16502,22 @@ rec {
         ];
 
       };
-      "wait-timeout" = rec {
-        crateName = "wait-timeout";
-        version = "0.2.0";
-        edition = "2015";
-        crateBin = [ ];
-        sha256 = "1xpkk0j5l9pfmjfh1pi0i89invlavfrd9av5xp0zhxgb29dhy84z";
+      "vu128" = rec {
+        crateName = "vu128";
+        version = "1.1.0";
+        edition = "2018";
+        sha256 = "1pczgy26c0lsri1ddrx5wkgn0rcq4da04pqya5rl6vrwfnys73di";
+        libPath = "vu128/vu128.rs";
         authors = [
-          "Alex Crichton <alex@alexcrichton.com>"
-        ];
-        dependencies = [
-          {
-            name = "libc";
-            packageId = "libc";
-            target = { target, features }: (target."unix" or false);
-          }
+          "John Millikin <john@john-millikin.com>"
         ];
 
       };
       "walkdir" = rec {
         crateName = "walkdir";
-        version = "2.4.0";
+        version = "2.5.0";
         edition = "2018";
-        sha256 = "1vjl9fmfc4v8k9ald23qrpcbyb8dl1ynyq8d516cm537r1yqa7fp";
+        sha256 = "0jsy7a710qv8gld5957ybrnc07gavppp963gs32xk4ag8130jy99";
         authors = [
           "Andrew Gallant <jamslam@gmail.com>"
         ];
@@ -17430,6 +16572,7 @@ rec {
         version = "0.2.90";
         edition = "2018";
         sha256 = "01jlal3mynqwvqx4acrdnr9bvsdczaz2sy8lmmzmqh81lab348mi";
+        libName = "wasm_bindgen";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -17461,6 +16604,7 @@ rec {
         version = "0.2.90";
         edition = "2018";
         sha256 = "1kcxml9762zjdrn0h0n0qxfg1n7z1f577jcc5yimi3a0cddr7p7w";
+        libName = "wasm_bindgen_backend";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -17487,7 +16631,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
             features = [ "full" ];
           }
           {
@@ -17505,6 +16649,7 @@ rec {
         version = "0.4.40";
         edition = "2018";
         sha256 = "0qf4bzlinyg0s4b38fhzdi1cqdd7rgrywqdjr3ngmgc6xcm07qmx";
+        libName = "wasm_bindgen_futures";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -17539,6 +16684,7 @@ rec {
         edition = "2018";
         sha256 = "16d980bql7y5krfqlmcr8mk1q4mrm0rmb0a99j92im5jc62j6k1y";
         procMacro = true;
+        libName = "wasm_bindgen_macro";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -17563,6 +16709,7 @@ rec {
         version = "0.2.90";
         edition = "2018";
         sha256 = "19r5bsyjw0fvim7dsj8pbwrq8v0ggh845lhfasgavhbdh2vapqds";
+        libName = "wasm_bindgen_macro_support";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -17577,7 +16724,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
             features = [ "visit" "full" ];
           }
           {
@@ -17601,6 +16748,7 @@ rec {
         edition = "2018";
         links = "wasm_bindgen";
         sha256 = "0av0m0shdg1jxhf66ymjbq03m0qb7ypm297glndm7mri3hxl34ad";
+        libName = "wasm_bindgen_shared";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -17611,6 +16759,7 @@ rec {
         version = "0.4.0";
         edition = "2021";
         sha256 = "0ad17c59xb8fffsnbrqbyqz93hb66nzxhizpii31icb31g4w8pdn";
+        libName = "wasm_streams";
         type = [ "cdylib" "rlib" ];
         authors = [
           "Mattias Buelens <mattias@buelens.com>"
@@ -17653,6 +16802,7 @@ rec {
         version = "0.3.67";
         edition = "2018";
         sha256 = "1vfjjj3i49gy8bh8znnqhak1hx7xj9c2a3jzc0wpmgp0nqrj7kaq";
+        libName = "web_sys";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -18122,6 +17272,7 @@ rec {
         version = "1.1.0";
         edition = "2021";
         sha256 = "1fx05yqx83dhx628wb70fyy10yjfq1jpl20qfqhdkymi13rq0ras";
+        libName = "web_time";
         dependencies = [
           {
             name = "js-sys";
@@ -18139,80 +17290,6 @@ rec {
           "serde" = [ "dep:serde" ];
         };
       };
-      "which 4.4.2" = rec {
-        crateName = "which";
-        version = "4.4.2";
-        edition = "2021";
-        sha256 = "1ixzmx3svsv5hbdvd8vdhd3qwvf6ns8jdpif1wmwsy10k90j9fl7";
-        authors = [
-          "Harry Fei <tiziyuanfang@gmail.com>"
-        ];
-        dependencies = [
-          {
-            name = "either";
-            packageId = "either";
-          }
-          {
-            name = "home";
-            packageId = "home";
-            target = { target, features }: ((target."windows" or false) || (target."unix" or false) || ("redox" == target."os" or null));
-          }
-          {
-            name = "once_cell";
-            packageId = "once_cell";
-            target = { target, features }: (target."windows" or false);
-          }
-          {
-            name = "rustix";
-            packageId = "rustix";
-            usesDefaultFeatures = false;
-            features = [ "fs" "std" ];
-          }
-        ];
-        features = {
-          "regex" = [ "dep:regex" ];
-        };
-      };
-      "which 5.0.0" = rec {
-        crateName = "which";
-        version = "5.0.0";
-        edition = "2021";
-        sha256 = "053fpbczryyn8lcbpkvwl8v2rzld0pr30r5lh1cxv87kjs2ymwwv";
-        authors = [
-          "Harry Fei <tiziyuanfang@gmail.com>"
-        ];
-        dependencies = [
-          {
-            name = "either";
-            packageId = "either";
-          }
-          {
-            name = "home";
-            packageId = "home";
-            target = { target, features }: ((target."windows" or false) || (target."unix" or false) || ("redox" == target."os" or null));
-          }
-          {
-            name = "once_cell";
-            packageId = "once_cell";
-            target = { target, features }: (target."windows" or false);
-          }
-          {
-            name = "rustix";
-            packageId = "rustix";
-            usesDefaultFeatures = false;
-            features = [ "fs" "std" ];
-          }
-          {
-            name = "windows-sys";
-            packageId = "windows-sys 0.48.0";
-            target = { target, features }: (target."windows" or false);
-            features = [ "Win32_Storage_FileSystem" "Win32_Foundation" ];
-          }
-        ];
-        features = {
-          "regex" = [ "dep:regex" ];
-        };
-      };
       "winapi" = rec {
         crateName = "winapi";
         version = "0.3.9";
@@ -18243,6 +17320,7 @@ rec {
         version = "0.4.0";
         edition = "2015";
         sha256 = "1dmpa6mvcvzz16zg6d5vrfy4bxgg541wxrcip7cnshi06v38ffxc";
+        libName = "winapi_i686_pc_windows_gnu";
         authors = [
           "Peter Atashian <retep998@gmail.com>"
         ];
@@ -18253,6 +17331,7 @@ rec {
         version = "0.1.6";
         edition = "2021";
         sha256 = "15i5lm39wd44004i9d5qspry2cynkrpvwzghr6s2c3dsk28nz7pj";
+        libName = "winapi_util";
         authors = [
           "Andrew Gallant <jamslam@gmail.com>"
         ];
@@ -18271,6 +17350,7 @@ rec {
         version = "0.4.0";
         edition = "2015";
         sha256 = "0gqq64czqb64kskjryj8isp62m2sgvx25yyj3kpc2myh85w24bki";
+        libName = "winapi_x86_64_pc_windows_gnu";
         authors = [
           "Peter Atashian <retep998@gmail.com>"
         ];
@@ -18291,7 +17371,7 @@ rec {
           }
           {
             name = "windows-targets";
-            packageId = "windows-targets 0.52.5";
+            packageId = "windows-targets 0.52.6";
           }
         ];
         features = {
@@ -18991,13 +18071,14 @@ rec {
         version = "0.52.0";
         edition = "2021";
         sha256 = "1nc3qv7sy24x0nlnb32f7alzpd6f72l4p24vl65vydbyil669ark";
+        libName = "windows_core";
         authors = [
           "Microsoft"
         ];
         dependencies = [
           {
             name = "windows-targets";
-            packageId = "windows-targets 0.52.5";
+            packageId = "windows-targets 0.52.6";
           }
         ];
         features = { };
@@ -19008,23 +18089,49 @@ rec {
         version = "0.54.0";
         edition = "2021";
         sha256 = "0r8x2sgl4qq1h23ldf4z7cj213k0bz7479m8a156h79mi6f1nrhj";
+        libName = "windows_core";
         authors = [
           "Microsoft"
         ];
         dependencies = [
           {
             name = "windows-result";
-            packageId = "windows-result";
+            packageId = "windows-result 0.1.2";
           }
           {
             name = "windows-targets";
-            packageId = "windows-targets 0.52.5";
+            packageId = "windows-targets 0.52.6";
           }
         ];
         features = { };
         resolvedDefaultFeatures = [ "default" ];
       };
-      "windows-result" = rec {
+      "windows-registry" = rec {
+        crateName = "windows-registry";
+        version = "0.2.0";
+        edition = "2021";
+        sha256 = "1c04923fq0rbvl3z0h67xr6rh2fgwkizhclhqv0j79i0nwdh0074";
+        libName = "windows_registry";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-result";
+            packageId = "windows-result 0.2.0";
+          }
+          {
+            name = "windows-strings";
+            packageId = "windows-strings";
+          }
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.52.6";
+          }
+        ];
+
+      };
+      "windows-result 0.1.2" = rec {
         crateName = "windows-result";
         version = "0.1.2";
         edition = "2021";
@@ -19036,7 +18143,52 @@ rec {
         dependencies = [
           {
             name = "windows-targets";
-            packageId = "windows-targets 0.52.5";
+            packageId = "windows-targets 0.52.6";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "windows-result 0.2.0" = rec {
+        crateName = "windows-result";
+        version = "0.2.0";
+        edition = "2021";
+        sha256 = "03mf2z1xcy2slhhsm15z24p76qxgm2m74xdjp8bihyag47c4640x";
+        libName = "windows_result";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.52.6";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "windows-strings" = rec {
+        crateName = "windows-strings";
+        version = "0.1.0";
+        edition = "2021";
+        sha256 = "042dxvi3133f7dyi2pgcvknwkikk47k8bddwxbq5s0l6qhjv3nac";
+        libName = "windows_strings";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-result";
+            packageId = "windows-result 0.2.0";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.52.6";
           }
         ];
         features = {
@@ -19049,6 +18201,7 @@ rec {
         version = "0.48.0";
         edition = "2018";
         sha256 = "1aan23v5gs7gya1lc46hqn9mdh8yph3fhxmhxlw36pn6pqc28zb7";
+        libName = "windows_sys";
         authors = [
           "Microsoft"
         ];
@@ -19335,20 +18488,21 @@ rec {
           "Win32_Web" = [ "Win32" ];
           "Win32_Web_InternetExplorer" = [ "Win32_Web" ];
         };
-        resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_Networking" "Win32_Networking_WinSock" "Win32_Security" "Win32_Storage" "Win32_Storage_FileSystem" "Win32_System" "Win32_System_Console" "Win32_System_Diagnostics" "Win32_System_Diagnostics_Debug" "Win32_System_IO" "Win32_System_Pipes" "Win32_System_Registry" "Win32_System_SystemServices" "Win32_System_Threading" "Win32_System_Time" "Win32_System_WindowsProgramming" "default" ];
+        resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_Networking" "Win32_Networking_WinSock" "Win32_Security" "Win32_Storage" "Win32_Storage_FileSystem" "Win32_System" "Win32_System_Console" "Win32_System_IO" "Win32_System_Pipes" "Win32_System_Threading" "Win32_System_WindowsProgramming" "default" ];
       };
       "windows-sys 0.52.0" = rec {
         crateName = "windows-sys";
         version = "0.52.0";
         edition = "2021";
         sha256 = "0gd3v4ji88490zgb6b5mq5zgbvwv7zx1ibn8v3x83rwcdbryaar8";
+        libName = "windows_sys";
         authors = [
           "Microsoft"
         ];
         dependencies = [
           {
             name = "windows-targets";
-            packageId = "windows-targets 0.52.5";
+            packageId = "windows-targets 0.52.6";
           }
         ];
         features = {
@@ -19582,13 +18736,273 @@ rec {
           "Win32_Web" = [ "Win32" ];
           "Win32_Web_InternetExplorer" = [ "Win32_Web" ];
         };
-        resolvedDefaultFeatures = [ "Wdk" "Wdk_Foundation" "Wdk_Storage" "Wdk_Storage_FileSystem" "Win32" "Win32_Foundation" "Win32_NetworkManagement" "Win32_NetworkManagement_IpHelper" "Win32_Networking" "Win32_Networking_WinSock" "Win32_Security" "Win32_Security_Authentication" "Win32_Security_Authentication_Identity" "Win32_Security_Credentials" "Win32_Security_Cryptography" "Win32_Storage" "Win32_Storage_FileSystem" "Win32_System" "Win32_System_Com" "Win32_System_Console" "Win32_System_Diagnostics" "Win32_System_Diagnostics_Debug" "Win32_System_IO" "Win32_System_LibraryLoader" "Win32_System_Memory" "Win32_System_Threading" "Win32_System_WindowsProgramming" "Win32_UI" "Win32_UI_Input" "Win32_UI_Input_KeyboardAndMouse" "Win32_UI_Shell" "default" ];
+        resolvedDefaultFeatures = [ "Wdk" "Wdk_Foundation" "Wdk_Storage" "Wdk_Storage_FileSystem" "Wdk_System" "Wdk_System_IO" "Win32" "Win32_Foundation" "Win32_NetworkManagement" "Win32_NetworkManagement_IpHelper" "Win32_Networking" "Win32_Networking_WinSock" "Win32_Security" "Win32_Security_Authentication" "Win32_Security_Authentication_Identity" "Win32_Security_Credentials" "Win32_Security_Cryptography" "Win32_Storage" "Win32_Storage_FileSystem" "Win32_System" "Win32_System_Com" "Win32_System_Console" "Win32_System_Diagnostics" "Win32_System_Diagnostics_Debug" "Win32_System_IO" "Win32_System_LibraryLoader" "Win32_System_Memory" "Win32_System_Pipes" "Win32_System_SystemServices" "Win32_System_Threading" "Win32_System_WindowsProgramming" "Win32_UI" "Win32_UI_Input" "Win32_UI_Input_KeyboardAndMouse" "Win32_UI_Shell" "default" ];
+      };
+      "windows-sys 0.59.0" = rec {
+        crateName = "windows-sys";
+        version = "0.59.0";
+        edition = "2021";
+        sha256 = "0fw5672ziw8b3zpmnbp9pdv1famk74f1l9fcbc3zsrzdg56vqf0y";
+        libName = "windows_sys";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.52.6";
+          }
+        ];
+        features = {
+          "Wdk" = [ "Win32_Foundation" ];
+          "Wdk_Devices" = [ "Wdk" ];
+          "Wdk_Devices_Bluetooth" = [ "Wdk_Devices" ];
+          "Wdk_Devices_HumanInterfaceDevice" = [ "Wdk_Devices" ];
+          "Wdk_Foundation" = [ "Wdk" ];
+          "Wdk_Graphics" = [ "Wdk" ];
+          "Wdk_Graphics_Direct3D" = [ "Wdk_Graphics" ];
+          "Wdk_NetworkManagement" = [ "Wdk" ];
+          "Wdk_NetworkManagement_Ndis" = [ "Wdk_NetworkManagement" ];
+          "Wdk_NetworkManagement_WindowsFilteringPlatform" = [ "Wdk_NetworkManagement" ];
+          "Wdk_Storage" = [ "Wdk" ];
+          "Wdk_Storage_FileSystem" = [ "Wdk_Storage" ];
+          "Wdk_Storage_FileSystem_Minifilters" = [ "Wdk_Storage_FileSystem" ];
+          "Wdk_System" = [ "Wdk" ];
+          "Wdk_System_IO" = [ "Wdk_System" ];
+          "Wdk_System_Memory" = [ "Wdk_System" ];
+          "Wdk_System_OfflineRegistry" = [ "Wdk_System" ];
+          "Wdk_System_Registry" = [ "Wdk_System" ];
+          "Wdk_System_SystemInformation" = [ "Wdk_System" ];
+          "Wdk_System_SystemServices" = [ "Wdk_System" ];
+          "Wdk_System_Threading" = [ "Wdk_System" ];
+          "Win32" = [ "Win32_Foundation" ];
+          "Win32_Data" = [ "Win32" ];
+          "Win32_Data_HtmlHelp" = [ "Win32_Data" ];
+          "Win32_Data_RightsManagement" = [ "Win32_Data" ];
+          "Win32_Devices" = [ "Win32" ];
+          "Win32_Devices_AllJoyn" = [ "Win32_Devices" ];
+          "Win32_Devices_BiometricFramework" = [ "Win32_Devices" ];
+          "Win32_Devices_Bluetooth" = [ "Win32_Devices" ];
+          "Win32_Devices_Communication" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAndDriverInstallation" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceQuery" = [ "Win32_Devices" ];
+          "Win32_Devices_Display" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration_Pnp" = [ "Win32_Devices_Enumeration" ];
+          "Win32_Devices_Fax" = [ "Win32_Devices" ];
+          "Win32_Devices_HumanInterfaceDevice" = [ "Win32_Devices" ];
+          "Win32_Devices_PortableDevices" = [ "Win32_Devices" ];
+          "Win32_Devices_Properties" = [ "Win32_Devices" ];
+          "Win32_Devices_Pwm" = [ "Win32_Devices" ];
+          "Win32_Devices_Sensors" = [ "Win32_Devices" ];
+          "Win32_Devices_SerialCommunication" = [ "Win32_Devices" ];
+          "Win32_Devices_Tapi" = [ "Win32_Devices" ];
+          "Win32_Devices_Usb" = [ "Win32_Devices" ];
+          "Win32_Devices_WebServicesOnDevices" = [ "Win32_Devices" ];
+          "Win32_Foundation" = [ "Win32" ];
+          "Win32_Gaming" = [ "Win32" ];
+          "Win32_Globalization" = [ "Win32" ];
+          "Win32_Graphics" = [ "Win32" ];
+          "Win32_Graphics_Dwm" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Gdi" = [ "Win32_Graphics" ];
+          "Win32_Graphics_GdiPlus" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Hlsl" = [ "Win32_Graphics" ];
+          "Win32_Graphics_OpenGL" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing_PrintTicket" = [ "Win32_Graphics_Printing" ];
+          "Win32_Management" = [ "Win32" ];
+          "Win32_Management_MobileDeviceManagementRegistration" = [ "Win32_Management" ];
+          "Win32_Media" = [ "Win32" ];
+          "Win32_Media_Audio" = [ "Win32_Media" ];
+          "Win32_Media_DxMediaObjects" = [ "Win32_Media" ];
+          "Win32_Media_KernelStreaming" = [ "Win32_Media" ];
+          "Win32_Media_Multimedia" = [ "Win32_Media" ];
+          "Win32_Media_Streaming" = [ "Win32_Media" ];
+          "Win32_Media_WindowsMediaFormat" = [ "Win32_Media" ];
+          "Win32_NetworkManagement" = [ "Win32" ];
+          "Win32_NetworkManagement_Dhcp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Dns" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_InternetConnectionWizard" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_IpHelper" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Multicast" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Ndis" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetBios" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetManagement" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetShell" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkDiagnosticsFramework" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_P2P" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_QoS" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Rras" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Snmp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WNet" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WebDav" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WiFi" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectionManager" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFilteringPlatform" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFirewall" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsNetworkVirtualization" = [ "Win32_NetworkManagement" ];
+          "Win32_Networking" = [ "Win32" ];
+          "Win32_Networking_ActiveDirectory" = [ "Win32_Networking" ];
+          "Win32_Networking_Clustering" = [ "Win32_Networking" ];
+          "Win32_Networking_HttpServer" = [ "Win32_Networking" ];
+          "Win32_Networking_Ldap" = [ "Win32_Networking" ];
+          "Win32_Networking_WebSocket" = [ "Win32_Networking" ];
+          "Win32_Networking_WinHttp" = [ "Win32_Networking" ];
+          "Win32_Networking_WinInet" = [ "Win32_Networking" ];
+          "Win32_Networking_WinSock" = [ "Win32_Networking" ];
+          "Win32_Networking_WindowsWebServices" = [ "Win32_Networking" ];
+          "Win32_Security" = [ "Win32" ];
+          "Win32_Security_AppLocker" = [ "Win32_Security" ];
+          "Win32_Security_Authentication" = [ "Win32_Security" ];
+          "Win32_Security_Authentication_Identity" = [ "Win32_Security_Authentication" ];
+          "Win32_Security_Authorization" = [ "Win32_Security" ];
+          "Win32_Security_Credentials" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography_Catalog" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Certificates" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Sip" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_UI" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_DiagnosticDataQuery" = [ "Win32_Security" ];
+          "Win32_Security_DirectoryServices" = [ "Win32_Security" ];
+          "Win32_Security_EnterpriseData" = [ "Win32_Security" ];
+          "Win32_Security_ExtensibleAuthenticationProtocol" = [ "Win32_Security" ];
+          "Win32_Security_Isolation" = [ "Win32_Security" ];
+          "Win32_Security_LicenseProtection" = [ "Win32_Security" ];
+          "Win32_Security_NetworkAccessProtection" = [ "Win32_Security" ];
+          "Win32_Security_WinTrust" = [ "Win32_Security" ];
+          "Win32_Security_WinWlx" = [ "Win32_Security" ];
+          "Win32_Storage" = [ "Win32" ];
+          "Win32_Storage_Cabinets" = [ "Win32_Storage" ];
+          "Win32_Storage_CloudFilters" = [ "Win32_Storage" ];
+          "Win32_Storage_Compression" = [ "Win32_Storage" ];
+          "Win32_Storage_DistributedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_FileHistory" = [ "Win32_Storage" ];
+          "Win32_Storage_FileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_Imapi" = [ "Win32_Storage" ];
+          "Win32_Storage_IndexServer" = [ "Win32_Storage" ];
+          "Win32_Storage_InstallableFileSystems" = [ "Win32_Storage" ];
+          "Win32_Storage_IscsiDisc" = [ "Win32_Storage" ];
+          "Win32_Storage_Jet" = [ "Win32_Storage" ];
+          "Win32_Storage_Nvme" = [ "Win32_Storage" ];
+          "Win32_Storage_OfflineFiles" = [ "Win32_Storage" ];
+          "Win32_Storage_OperationRecorder" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging_Appx" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_ProjectedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_StructuredStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_Vhd" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps" = [ "Win32_Storage" ];
+          "Win32_System" = [ "Win32" ];
+          "Win32_System_AddressBook" = [ "Win32_System" ];
+          "Win32_System_Antimalware" = [ "Win32_System" ];
+          "Win32_System_ApplicationInstallationAndServicing" = [ "Win32_System" ];
+          "Win32_System_ApplicationVerifier" = [ "Win32_System" ];
+          "Win32_System_ClrHosting" = [ "Win32_System" ];
+          "Win32_System_Com" = [ "Win32_System" ];
+          "Win32_System_Com_Marshal" = [ "Win32_System_Com" ];
+          "Win32_System_Com_StructuredStorage" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Urlmon" = [ "Win32_System_Com" ];
+          "Win32_System_ComponentServices" = [ "Win32_System" ];
+          "Win32_System_Console" = [ "Win32_System" ];
+          "Win32_System_CorrelationVector" = [ "Win32_System" ];
+          "Win32_System_DataExchange" = [ "Win32_System" ];
+          "Win32_System_DeploymentServices" = [ "Win32_System" ];
+          "Win32_System_DeveloperLicensing" = [ "Win32_System" ];
+          "Win32_System_Diagnostics" = [ "Win32_System" ];
+          "Win32_System_Diagnostics_Ceip" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug_Extensions" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Etw" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ProcessSnapshotting" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ToolHelp" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_TraceLogging" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_DistributedTransactionCoordinator" = [ "Win32_System" ];
+          "Win32_System_Environment" = [ "Win32_System" ];
+          "Win32_System_ErrorReporting" = [ "Win32_System" ];
+          "Win32_System_EventCollector" = [ "Win32_System" ];
+          "Win32_System_EventLog" = [ "Win32_System" ];
+          "Win32_System_EventNotificationService" = [ "Win32_System" ];
+          "Win32_System_GroupPolicy" = [ "Win32_System" ];
+          "Win32_System_HostCompute" = [ "Win32_System" ];
+          "Win32_System_HostComputeNetwork" = [ "Win32_System" ];
+          "Win32_System_HostComputeSystem" = [ "Win32_System" ];
+          "Win32_System_Hypervisor" = [ "Win32_System" ];
+          "Win32_System_IO" = [ "Win32_System" ];
+          "Win32_System_Iis" = [ "Win32_System" ];
+          "Win32_System_Ioctl" = [ "Win32_System" ];
+          "Win32_System_JobObjects" = [ "Win32_System" ];
+          "Win32_System_Js" = [ "Win32_System" ];
+          "Win32_System_Kernel" = [ "Win32_System" ];
+          "Win32_System_LibraryLoader" = [ "Win32_System" ];
+          "Win32_System_Mailslots" = [ "Win32_System" ];
+          "Win32_System_Mapi" = [ "Win32_System" ];
+          "Win32_System_Memory" = [ "Win32_System" ];
+          "Win32_System_Memory_NonVolatile" = [ "Win32_System_Memory" ];
+          "Win32_System_MessageQueuing" = [ "Win32_System" ];
+          "Win32_System_MixedReality" = [ "Win32_System" ];
+          "Win32_System_Ole" = [ "Win32_System" ];
+          "Win32_System_PasswordManagement" = [ "Win32_System" ];
+          "Win32_System_Performance" = [ "Win32_System" ];
+          "Win32_System_Performance_HardwareCounterProfiling" = [ "Win32_System_Performance" ];
+          "Win32_System_Pipes" = [ "Win32_System" ];
+          "Win32_System_Power" = [ "Win32_System" ];
+          "Win32_System_ProcessStatus" = [ "Win32_System" ];
+          "Win32_System_Recovery" = [ "Win32_System" ];
+          "Win32_System_Registry" = [ "Win32_System" ];
+          "Win32_System_RemoteDesktop" = [ "Win32_System" ];
+          "Win32_System_RemoteManagement" = [ "Win32_System" ];
+          "Win32_System_RestartManager" = [ "Win32_System" ];
+          "Win32_System_Restore" = [ "Win32_System" ];
+          "Win32_System_Rpc" = [ "Win32_System" ];
+          "Win32_System_Search" = [ "Win32_System" ];
+          "Win32_System_Search_Common" = [ "Win32_System_Search" ];
+          "Win32_System_SecurityCenter" = [ "Win32_System" ];
+          "Win32_System_Services" = [ "Win32_System" ];
+          "Win32_System_SetupAndMigration" = [ "Win32_System" ];
+          "Win32_System_Shutdown" = [ "Win32_System" ];
+          "Win32_System_StationsAndDesktops" = [ "Win32_System" ];
+          "Win32_System_SubsystemForLinux" = [ "Win32_System" ];
+          "Win32_System_SystemInformation" = [ "Win32_System" ];
+          "Win32_System_SystemServices" = [ "Win32_System" ];
+          "Win32_System_Threading" = [ "Win32_System" ];
+          "Win32_System_Time" = [ "Win32_System" ];
+          "Win32_System_TpmBaseServices" = [ "Win32_System" ];
+          "Win32_System_UserAccessLogging" = [ "Win32_System" ];
+          "Win32_System_Variant" = [ "Win32_System" ];
+          "Win32_System_VirtualDosMachines" = [ "Win32_System" ];
+          "Win32_System_WindowsProgramming" = [ "Win32_System" ];
+          "Win32_System_Wmi" = [ "Win32_System" ];
+          "Win32_UI" = [ "Win32" ];
+          "Win32_UI_Accessibility" = [ "Win32_UI" ];
+          "Win32_UI_ColorSystem" = [ "Win32_UI" ];
+          "Win32_UI_Controls" = [ "Win32_UI" ];
+          "Win32_UI_Controls_Dialogs" = [ "Win32_UI_Controls" ];
+          "Win32_UI_HiDpi" = [ "Win32_UI" ];
+          "Win32_UI_Input" = [ "Win32_UI" ];
+          "Win32_UI_Input_Ime" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_KeyboardAndMouse" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Pointer" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Touch" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_XboxController" = [ "Win32_UI_Input" ];
+          "Win32_UI_InteractionContext" = [ "Win32_UI" ];
+          "Win32_UI_Magnification" = [ "Win32_UI" ];
+          "Win32_UI_Shell" = [ "Win32_UI" ];
+          "Win32_UI_Shell_Common" = [ "Win32_UI_Shell" ];
+          "Win32_UI_Shell_PropertiesSystem" = [ "Win32_UI_Shell" ];
+          "Win32_UI_TabletPC" = [ "Win32_UI" ];
+          "Win32_UI_TextServices" = [ "Win32_UI" ];
+          "Win32_UI_WindowsAndMessaging" = [ "Win32_UI" ];
+          "Win32_Web" = [ "Win32" ];
+          "Win32_Web_InternetExplorer" = [ "Win32_Web" ];
+        };
+        resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_Storage" "Win32_Storage_FileSystem" "Win32_System" "Win32_System_Threading" "default" ];
       };
       "windows-targets 0.48.5" = rec {
         crateName = "windows-targets";
         version = "0.48.5";
         edition = "2018";
         sha256 = "034ljxqshifs1lan89xwpcy1hp0lhdh4b5n0d2z4fwjx2piacbws";
+        libName = "windows_targets";
         authors = [
           "Microsoft"
         ];
@@ -19631,28 +19045,29 @@ rec {
         ];
 
       };
-      "windows-targets 0.52.5" = rec {
+      "windows-targets 0.52.6" = rec {
         crateName = "windows-targets";
-        version = "0.52.5";
+        version = "0.52.6";
         edition = "2021";
-        sha256 = "1sz7jrnkygmmlj1ia8fk85wbyil450kq5qkh5qh9sh2rcnj161vg";
+        sha256 = "0wwrx625nwlfp7k93r2rra568gad1mwd888h1jwnl0vfg5r4ywlv";
+        libName = "windows_targets";
         authors = [
           "Microsoft"
         ];
         dependencies = [
           {
             name = "windows_aarch64_gnullvm";
-            packageId = "windows_aarch64_gnullvm 0.52.5";
+            packageId = "windows_aarch64_gnullvm 0.52.6";
             target = { target, features }: (stdenv.hostPlatform.rust.rustcTarget == "aarch64-pc-windows-gnullvm");
           }
           {
             name = "windows_aarch64_msvc";
-            packageId = "windows_aarch64_msvc 0.52.5";
+            packageId = "windows_aarch64_msvc 0.52.6";
             target = { target, features }: (("aarch64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
           }
           {
             name = "windows_i686_gnu";
-            packageId = "windows_i686_gnu 0.52.5";
+            packageId = "windows_i686_gnu 0.52.6";
             target = { target, features }: (("x86" == target."arch" or null) && ("gnu" == target."env" or null) && (!("llvm" == target."abi" or null)) && (!(target."windows_raw_dylib" or false)));
           }
           {
@@ -19662,22 +19077,22 @@ rec {
           }
           {
             name = "windows_i686_msvc";
-            packageId = "windows_i686_msvc 0.52.5";
+            packageId = "windows_i686_msvc 0.52.6";
             target = { target, features }: (("x86" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
           }
           {
             name = "windows_x86_64_gnu";
-            packageId = "windows_x86_64_gnu 0.52.5";
+            packageId = "windows_x86_64_gnu 0.52.6";
             target = { target, features }: (("x86_64" == target."arch" or null) && ("gnu" == target."env" or null) && (!("llvm" == target."abi" or null)) && (!(target."windows_raw_dylib" or false)));
           }
           {
             name = "windows_x86_64_gnullvm";
-            packageId = "windows_x86_64_gnullvm 0.52.5";
+            packageId = "windows_x86_64_gnullvm 0.52.6";
             target = { target, features }: (stdenv.hostPlatform.rust.rustcTarget == "x86_64-pc-windows-gnullvm");
           }
           {
             name = "windows_x86_64_msvc";
-            packageId = "windows_x86_64_msvc 0.52.5";
+            packageId = "windows_x86_64_msvc 0.52.6";
             target = { target, features }: ((("x86_64" == target."arch" or null) || ("arm64ec" == target."arch" or null)) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
           }
         ];
@@ -19693,11 +19108,11 @@ rec {
         ];
 
       };
-      "windows_aarch64_gnullvm 0.52.5" = rec {
+      "windows_aarch64_gnullvm 0.52.6" = rec {
         crateName = "windows_aarch64_gnullvm";
-        version = "0.52.5";
+        version = "0.52.6";
         edition = "2021";
-        sha256 = "0qrjimbj67nnyn7zqy15mzzmqg0mn5gsr2yciqjxm3cb3vbyx23h";
+        sha256 = "1lrcq38cr2arvmz19v32qaggvj8bh1640mdm9c2fr877h0hn591j";
         authors = [
           "Microsoft"
         ];
@@ -19713,11 +19128,11 @@ rec {
         ];
 
       };
-      "windows_aarch64_msvc 0.52.5" = rec {
+      "windows_aarch64_msvc 0.52.6" = rec {
         crateName = "windows_aarch64_msvc";
-        version = "0.52.5";
+        version = "0.52.6";
         edition = "2021";
-        sha256 = "1dmga8kqlmln2ibckk6mxc9n59vdg8ziqa2zr8awcl720hazv1cr";
+        sha256 = "0sfl0nysnz32yyfh773hpi49b1q700ah6y7sacmjbqjjn5xjmv09";
         authors = [
           "Microsoft"
         ];
@@ -19733,11 +19148,11 @@ rec {
         ];
 
       };
-      "windows_i686_gnu 0.52.5" = rec {
+      "windows_i686_gnu 0.52.6" = rec {
         crateName = "windows_i686_gnu";
-        version = "0.52.5";
+        version = "0.52.6";
         edition = "2021";
-        sha256 = "0w4np3l6qwlra9s2xpflqrs60qk1pz6ahhn91rr74lvdy4y0gfl8";
+        sha256 = "02zspglbykh1jh9pi7gn8g1f97jh1rrccni9ivmrfbl0mgamm6wf";
         authors = [
           "Microsoft"
         ];
@@ -19745,9 +19160,9 @@ rec {
       };
       "windows_i686_gnullvm" = rec {
         crateName = "windows_i686_gnullvm";
-        version = "0.52.5";
+        version = "0.52.6";
         edition = "2021";
-        sha256 = "1s9f4gff0cixd86mw3n63rpmsm4pmr4ffndl6s7qa2h35492dx47";
+        sha256 = "0rpdx1537mw6slcpqa0rm3qixmsb79nbhqy5fsm3q2q9ik9m5vhf";
         authors = [
           "Microsoft"
         ];
@@ -19763,11 +19178,11 @@ rec {
         ];
 
       };
-      "windows_i686_msvc 0.52.5" = rec {
+      "windows_i686_msvc 0.52.6" = rec {
         crateName = "windows_i686_msvc";
-        version = "0.52.5";
+        version = "0.52.6";
         edition = "2021";
-        sha256 = "1gw7fklxywgpnwbwg43alb4hm0qjmx72hqrlwy5nanrxs7rjng6v";
+        sha256 = "0rkcqmp4zzmfvrrrx01260q3xkpzi6fzi2x2pgdcdry50ny4h294";
         authors = [
           "Microsoft"
         ];
@@ -19783,11 +19198,11 @@ rec {
         ];
 
       };
-      "windows_x86_64_gnu 0.52.5" = rec {
+      "windows_x86_64_gnu 0.52.6" = rec {
         crateName = "windows_x86_64_gnu";
-        version = "0.52.5";
+        version = "0.52.6";
         edition = "2021";
-        sha256 = "1n8p2mcf3lw6300k77a0knksssmgwb9hynl793mhkzyydgvlchjf";
+        sha256 = "0y0sifqcb56a56mvn7xjgs8g43p33mfqkd8wj1yhrgxzma05qyhl";
         authors = [
           "Microsoft"
         ];
@@ -19803,11 +19218,11 @@ rec {
         ];
 
       };
-      "windows_x86_64_gnullvm 0.52.5" = rec {
+      "windows_x86_64_gnullvm 0.52.6" = rec {
         crateName = "windows_x86_64_gnullvm";
-        version = "0.52.5";
+        version = "0.52.6";
         edition = "2021";
-        sha256 = "15n56jrh4s5bz66zimavr1rmcaw6wa306myrvmbc6rydhbj9h8l5";
+        sha256 = "03gda7zjx1qh8k9nnlgb7m3w3s1xkysg55hkd1wjch8pqhyv5m94";
         authors = [
           "Microsoft"
         ];
@@ -19823,11 +19238,11 @@ rec {
         ];
 
       };
-      "windows_x86_64_msvc 0.52.5" = rec {
+      "windows_x86_64_msvc 0.52.6" = rec {
         crateName = "windows_x86_64_msvc";
-        version = "0.52.5";
+        version = "0.52.6";
         edition = "2021";
-        sha256 = "1w1bn24ap8dp9i85s8mlg8cim2bl2368bd6qyvm0xzqvzmdpxi5y";
+        sha256 = "1v7rb5cibyzx8vak29pdrk8nx9hycsjs4w0jgms08qk49jl6v7sq";
         authors = [
           "Microsoft"
         ];
@@ -19835,9 +19250,9 @@ rec {
       };
       "winnow" = rec {
         crateName = "winnow";
-        version = "0.6.14";
+        version = "0.6.18";
         edition = "2021";
-        sha256 = "0bz2nmp7mqbz7z5z41p9gdimcwsmrmg4gnbj94dkqykn5l5c8kip";
+        sha256 = "0vrsrnf2nm9jsk1161x1vacmi3ns4h3h10fib91rs28zd6jbvab8";
         dependencies = [
           {
             name = "memchr";
@@ -19855,31 +19270,6 @@ rec {
         };
         resolvedDefaultFeatures = [ "alloc" "default" "std" ];
       };
-      "winreg" = rec {
-        crateName = "winreg";
-        version = "0.52.0";
-        edition = "2018";
-        sha256 = "19gh9vp7mp1ab84kc3ag48nm9y7xgjhh3xa4vxss1gylk1rsaxx2";
-        authors = [
-          "Igor Shaula <gentoo90@gmail.com>"
-        ];
-        dependencies = [
-          {
-            name = "cfg-if";
-            packageId = "cfg-if";
-          }
-          {
-            name = "windows-sys";
-            packageId = "windows-sys 0.48.0";
-            features = [ "Win32_Foundation" "Win32_System_Time" "Win32_System_Registry" "Win32_Security" "Win32_Storage_FileSystem" "Win32_System_Diagnostics_Debug" ];
-          }
-        ];
-        features = {
-          "chrono" = [ "dep:chrono" ];
-          "serde" = [ "dep:serde" ];
-          "serialization-serde" = [ "transactions" "serde" ];
-        };
-      };
       "wu-manber" = rec {
         crateName = "wu-manber";
         version = "0.1.0";
@@ -19890,6 +19280,7 @@ rec {
           rev = "0d5b22bea136659f7de60b102a7030e0daaa503d";
           sha256 = "1zhk83lbq99xzyjwphv2qrb8f8qgfqwa5bbbvyzm0z0bljsjv0pd";
         };
+        libName = "wu_manber";
         authors = [
           "Joe Neeman <joeneeman@gmail.com>"
         ];
@@ -20009,6 +19400,7 @@ rec {
         edition = "2018";
         sha256 = "0fqvglw01w3hp7xj9gdk1800x9j7v58s9w8ijiyiz2a7krb39s8m";
         procMacro = true;
+        libName = "zerocopy_derive";
         authors = [
           "Joshua Liebow-Feeser <joshlf@google.com>"
         ];
@@ -20023,7 +19415,7 @@ rec {
           }
           {
             name = "syn";
-            packageId = "syn 2.0.48";
+            packageId = "syn 2.0.76";
           }
         ];
 
@@ -20047,9 +19439,9 @@ rec {
       };
       "zstd" = rec {
         crateName = "zstd";
-        version = "0.13.0";
+        version = "0.13.2";
         edition = "2018";
-        sha256 = "0401q54s9r35x2i7m1kwppgkj79g0pb6xz3xpby7qlkdb44k7yxz";
+        sha256 = "1ygkr6wspm9clbp7ykyl0rv69cfsf9q4lic9wcqiwn34lrwbgwpw";
         authors = [
           "Alexandre Bury <alexandre.bury@gmail.com>"
         ];
@@ -20080,9 +19472,10 @@ rec {
       };
       "zstd-safe" = rec {
         crateName = "zstd-safe";
-        version = "7.0.0";
+        version = "7.2.1";
         edition = "2018";
-        sha256 = "0gpav2lcibrpmyslmjkcn3w0w64qif3jjljd2h8lr4p249s7qx23";
+        sha256 = "0nch85m5cr493y26yvndm6a8j6sd9mxpr2awrim3dslcnr6sp8sl";
+        libName = "zstd_safe";
         authors = [
           "Alexandre Bury <alexandre.bury@gmail.com>"
         ];
@@ -20112,10 +19505,11 @@ rec {
       };
       "zstd-sys" = rec {
         crateName = "zstd-sys";
-        version = "2.0.9+zstd.1.5.5";
+        version = "2.0.13+zstd.1.5.6";
         edition = "2018";
         links = "zstd";
-        sha256 = "0mk6a2367swdi22zg03lcackpnvgq96d7120awd4i83lm2lfy5ly";
+        sha256 = "1almbackh06am0d2kc4a089n3al91jg3ahgg9kcrg3zfrwhhzzrq";
+        libName = "zstd_sys";
         authors = [
           "Alexandre Bury <alexandre.bury@gmail.com>"
         ];
diff --git a/tvix/Cargo.toml b/tvix/Cargo.toml
index 53b9134a567b..7387bf2424f2 100644
--- a/tvix/Cargo.toml
+++ b/tvix/Cargo.toml
@@ -27,6 +27,8 @@ members = [
   "glue",
   "nar-bridge",
   "nix-compat",
+  "nix-compat-derive",
+  "nix-compat-derive-tests",
   "serde",
   "store",
   "tracing",
@@ -37,6 +39,128 @@ members = [
 # https://github.com/rust-lang/rust-clippy/issues/12281
 blocks_in_conditions = "allow"
 
+[workspace.dependencies]
+anyhow = "1.0.86"
+async-compression = "0.4.12"
+async-process = "2.2.4"
+async-stream = "0.3.5"
+async-tempfile = "0.4.0"
+axum = "0.7.5"
+# https://github.com/liufuyang/bigtable_rs/pull/86
+bigtable_rs = { git = "https://github.com/liufuyang/bigtable_rs", rev = "1818355a5373a5bc2c84287e3a4e3807154ac8ef" }
+bitflags = "2.6.0"
+blake3 = "1.5.4"
+bstr = "1.10.0"
+bytes = "1.7.1"
+clap = "4.5.16"
+codemap = "0.1.3"
+codemap-diagnostic = "0.1.2"
+count-write = "0.1.0"
+criterion = "0.5"
+data-encoding = "2.6.0"
+digest = "0.10.7"
+dirs = "4.0.0"
+ed25519 = "2.2.3"
+ed25519-dalek = "2.1.1"
+enum-primitive-derive = "0.3.0"
+erased-serde = "0.4.5"
+expect-test = "1.5.0"
+fastcdc = "3.1.0"
+fuse-backend-rs = "0.11.0"
+futures = "0.3.30"
+genawaiter = { version = "0.99.1", default-features = false }
+glob = "0.3.1"
+hex-literal = "0.4.1"
+http = "1.1.0"
+hyper-util = "0.1.7"
+indicatif = "0.17.8"
+itertools = "0.12.1"
+lazy_static = "1.5.0"
+lexical-core = "0.8.5"
+libc = "0.2.158"
+lru = "0.12.4"
+magic = "0.16.2"
+md-5 = "0.10.6"
+mimalloc = "0.1.43"
+nix = "0.27.1"
+nohash-hasher = "0.2.0"
+nom = "7.1.3"
+num-traits = "0.2.19"
+object_store = "0.10.2"
+opentelemetry = "0.24.0"
+opentelemetry-http = "0.13.0"
+opentelemetry-otlp = "0.17.0"
+opentelemetry_sdk = "0.24.1"
+os_str_bytes = "6.6"
+parking_lot = "0.12.3"
+path-clean = "0.1"
+petgraph = "0.6.5"
+pin-project = "1.1"
+pin-project-lite = "0.2.14"
+pretty_assertions = "1.4.0"
+proc-macro2 = "1.0.86"
+proptest = { version = "1.5.0", default-features = false }
+prost = "0.13.1"
+prost-build = "0.13.1"
+quote = "1.0.37"
+redb = "2.1.2"
+regex = "1.10.6"
+reqwest = { version = "0.12.7", default-features = false }
+reqwest-middleware = "0.3.3"
+reqwest-tracing = { version = "0.5.3", default-features = false }
+rnix = "0.11.0"
+rowan = "*"
+rstest = "0.19.0"
+rstest_reuse = "0.6.0"
+rustc-hash = "2.0.0"
+rustyline = "10.1.1"
+serde = "1.0.209"
+serde_json = "1.0"
+serde_qs = "0.12.0"
+serde_tagged = "0.3.0"
+serde_with = "3.9.0"
+sha1 = "0.10.6"
+sha2 = "0.10.8"
+sled = "0.34.7"
+smol_str = "0.2.2"
+tabwriter = "1.4"
+tempfile = "3.12.0"
+test-strategy = "0.2.1"
+thiserror = "1.0.63"
+threadpool = "1.8.1"
+tokio = "1.39.3"
+tokio-listener = "0.4.3"
+tokio-retry = "0.3.0"
+tokio-stream = "0.1.15"
+tokio-tar = "0.3.1"
+tokio-test = "0.4.4"
+tokio-util = "0.7.11"
+tonic = "0.12.2"
+tonic-build = "0.12.2"
+tonic-health = { version = "0.12.2", default-features = false }
+tonic-reflection = "0.12.2"
+tower = "0.4.13"
+tower-http = "0.5.2"
+tracing = "0.1.40"
+tracing-indicatif = "0.3.6"
+tracing-opentelemetry = "0.25.0"
+tracing-subscriber = "0.3.18"
+tracing-tracy = "0.11.2"
+trybuild = "1.0.99"
+url = "2.5.2"
+vhost = "0.6"
+vhost-user-backend = "0.8"
+virtio-bindings = "0.2.2"
+virtio-queue = "0.7"
+vm-memory = "0.10"
+vmm-sys-util = "0.11"
+vu128 = "1.1.0"
+walkdir = "2.5.0"
+# https://github.com/jneem/wu-manber/pull/1
+wu-manber = { git = "https://github.com/tvlfyi/wu-manber.git" }
+xattr = "1.3.1"
+zstd = "0.13.2"
+
 # Add a profile to all targets that enables release optimisations, but
 # retains debug symbols. This is great for use with
 # benchmarking/profiling tools.
diff --git a/tvix/boot/default.nix b/tvix/boot/default.nix
index 7be05c815cdc..60eb2f1b7608 100644
--- a/tvix/boot/default.nix
+++ b/tvix/boot/default.nix
@@ -1,13 +1,16 @@
-{ depot, pkgs, ... }:
+{ lib, pkgs, ... }:
 
 rec {
   # A binary that sets up /nix/store from virtiofs, lists all store paths, and
   # powers off the machine.
-  tvix-init = depot.nix.buildGo.program {
+  tvix-init = pkgs.buildGoModule rec {
     name = "tvix-init";
-    srcs = [
-      ./tvix-init.go
-    ];
+    src = lib.fileset.toSource {
+      root = ./.;
+      fileset = ./tvix-init.go;
+    };
+    vendorHash = null;
+    postPatch = "go mod init ${name}";
   };
 
   # A kernel with virtiofs support baked in
diff --git a/tvix/boot/tests/default.nix b/tvix/boot/tests/default.nix
index 7b9fbbbf8c57..97477572078e 100644
--- a/tvix/boot/tests/default.nix
+++ b/tvix/boot/tests/default.nix
@@ -119,14 +119,20 @@ let
           grep "${assertVMOutput}" output.txt
         '';
         requiredSystemFeatures = [ "kvm" ];
+        # HACK: The boot tests are sometimes flaky, and we don't want them to
+        # periodically fail other build. Have Buildkite auto-retry them 2 times
+        # on failure.
+        # Logs for individual failures are still available, so it won't hinder
+        # flakiness debuggability.
+        meta.ci.buildkiteExtraStepArgs = {
+          retry.automatic = true;
+        };
       } // lib.optionalAttrs (isClosure && !useNarBridge) {
         __structuredAttrs = true;
         exportReferencesGraph.closure = [ path ];
       });
 
-  systemFor = sys: (depot.ops.nixos.nixosFor sys).system;
-
-  testSystem = systemFor ({ modulesPath, pkgs, ... }: {
+  testSystem = (pkgs.nixos {
     # Set some options necessary to evaluate.
     boot.loader.systemd-boot.enable = true;
     # TODO: figure out how to disable this without causing eval to fail
@@ -146,7 +152,10 @@ let
 
     # Don't warn about stateVersion.
     system.stateVersion = "24.05";
-  });
+
+    # Speed-up evaluation and building.
+    documentation.enable = lib.mkForce false;
+  }).config.system.build.toplevel;
 
 in
 depot.nix.readTree.drvTargets
diff --git a/tvix/build-go/build.pb.go b/tvix/build-go/build.pb.go
index 84e866513f6f..c1fbf28b547a 100644
--- a/tvix/build-go/build.pb.go
+++ b/tvix/build-go/build.pb.go
@@ -66,8 +66,8 @@ type BuildRequest struct {
 
 	// The list of all root nodes that should be visible in `inputs_dir` at the
 	// time of the build.
-	// As root nodes are content-addressed, no additional signatures are needed
-	// to substitute / make these available in the build environment.
+	// As all references are content-addressed, no additional signatures are
+	// needed to substitute / make these available in the build environment.
 	// Inputs MUST be sorted by their names.
 	Inputs []*castore_go.Node `protobuf:"bytes,1,rep,name=inputs,proto3" json:"inputs,omitempty"`
 	// The command (and its args) executed as the build script.
diff --git a/tvix/build/Cargo.toml b/tvix/build/Cargo.toml
index 8bb68783f0ea..b9073b7ff61a 100644
--- a/tvix/build/Cargo.toml
+++ b/tvix/build/Cargo.toml
@@ -4,34 +4,31 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-bytes = "1.4.0"
-clap = { version = "4.0", features = ["derive", "env"] }
-itertools = "0.12.0"
-prost = "0.13.1"
-thiserror = "1.0.56"
-tokio = { version = "1.32.0" }
-tokio-listener = { version = "0.4.1", features = [ "tonic012" ] }
-tonic = { version = "0.12.0", features = ["tls", "tls-roots"] }
+bytes = { workspace = true }
+clap = { workspace = true, features = ["derive", "env"] }
+itertools = { workspace = true }
+prost = { workspace = true }
+thiserror = { workspace = true }
+tokio = { workspace = true }
+tokio-listener = { workspace = true, features = ["tonic012"] }
+tonic = { workspace = true, features = ["tls", "tls-roots"] }
 tvix-castore = { path = "../castore" }
 tvix-tracing = { path = "../tracing" }
-tracing = "0.1.40"
-url = "2.4.0"
-mimalloc = "0.1.43"
-
-[dependencies.tonic-reflection]
-optional = true
-version = "0.12.0"
+tracing = { workspace = true }
+url = { workspace = true }
+mimalloc = { workspace = true }
+tonic-reflection = { workspace = true, optional = true }
 
 [build-dependencies]
-prost-build = "0.13.1"
-tonic-build = "0.12.0"
+prost-build = { workspace = true }
+tonic-build = { workspace = true }
 
 [features]
 default = []
 tonic-reflection = ["dep:tonic-reflection", "tvix-castore/tonic-reflection"]
 
 [dev-dependencies]
-rstest = "0.19.0"
+rstest = { workspace = true }
 
 [lints]
 workspace = true
diff --git a/tvix/build/build.rs b/tvix/build/build.rs
index 09604c3e3b71..fe230cbeca8a 100644
--- a/tvix/build/build.rs
+++ b/tvix/build/build.rs
@@ -25,7 +25,7 @@ fn main() -> Result<()> {
             ],
             // If we are in running `cargo build` manually, using `../..` works fine,
             // but in case we run inside a nix build, we need to instead point PROTO_ROOT
-            // to a sparseTree containing that structure.
+            // to a custom tree containing that structure.
             &[match std::env::var_os("PROTO_ROOT") {
                 Some(proto_root) => proto_root.to_str().unwrap().to_owned(),
                 None => "../..".to_string(),
diff --git a/tvix/build/default.nix b/tvix/build/default.nix
index aafab92fd525..17b52354bbeb 100644
--- a/tvix/build/default.nix
+++ b/tvix/build/default.nix
@@ -4,8 +4,8 @@
   runTests = true;
 }).overrideAttrs (old: rec {
   meta.ci.targets = lib.filter (x: lib.hasPrefix "with-features" x || x == "no-features") (lib.attrNames passthru);
-  passthru = depot.tvix.utils.mkFeaturePowerset {
+  passthru = old.passthru // (depot.tvix.utils.mkFeaturePowerset {
     inherit (old) crateName;
     features = [ "tonic-reflection" ];
-  };
+  });
 })
diff --git a/tvix/build/protos/build.proto b/tvix/build/protos/build.proto
index f1f6bf0b05d8..7a3c49db4873 100644
--- a/tvix/build/protos/build.proto
+++ b/tvix/build/protos/build.proto
@@ -47,8 +47,8 @@ option go_package = "code.tvl.fyi/tvix/build-go;buildv1";
 message BuildRequest {
   // The list of all root nodes that should be visible in `inputs_dir` at the
   // time of the build.
-  // As root nodes are content-addressed, no additional signatures are needed
-  // to substitute / make these available in the build environment.
+  // As all references are content-addressed, no additional signatures are
+  // needed to substitute / make these available in the build environment.
   // Inputs MUST be sorted by their names.
   repeated tvix.castore.v1.Node inputs = 1;
 
diff --git a/tvix/build/protos/default.nix b/tvix/build/protos/default.nix
index 790655ac7522..287b513136c9 100644
--- a/tvix/build/protos/default.nix
+++ b/tvix/build/protos/default.nix
@@ -1,17 +1,12 @@
-{ depot, pkgs, ... }:
+{ depot, pkgs, lib, ... }:
 let
-  protos = depot.nix.sparseTree {
-    name = "build-protos";
-    root = depot.path.origSrc;
-    paths = [
-      # We need to include castore.proto (only), as it's referred.
-      ../../castore/protos/castore.proto
-      ./build.proto
-      ./rpc_build.proto
-      ../../../buf.yaml
-      ../../../buf.gen.yaml
-    ];
-  };
+  protos = lib.sourceByRegex depot.path.origSrc [
+    "buf.yaml"
+    "buf.gen.yaml"
+    # We need to include castore.proto (only), as it's referred.
+    "^tvix(/castore(/protos(/castore\.proto)?)?)?$"
+    "^tvix(/build(/protos(/.*\.proto)?)?)?$"
+  ];
 in
 depot.nix.readTree.drvTargets {
   inherit protos;
diff --git a/tvix/build/src/bin/tvix-build.rs b/tvix/build/src/bin/tvix-build.rs
index 93e85871caab..b840e031db80 100644
--- a/tvix/build/src/bin/tvix-build.rs
+++ b/tvix/build/src/bin/tvix-build.rs
@@ -90,11 +90,18 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
 
             #[cfg(feature = "tonic-reflection")]
             {
-                let reflection_svc = tonic_reflection::server::Builder::configure()
-                    .register_encoded_file_descriptor_set(CASTORE_FILE_DESCRIPTOR_SET)
-                    .register_encoded_file_descriptor_set(FILE_DESCRIPTOR_SET)
-                    .build()?;
-                router = router.add_service(reflection_svc);
+                router = router.add_service(
+                    tonic_reflection::server::Builder::configure()
+                        .register_encoded_file_descriptor_set(CASTORE_FILE_DESCRIPTOR_SET)
+                        .register_encoded_file_descriptor_set(FILE_DESCRIPTOR_SET)
+                        .build_v1alpha()?,
+                );
+                router = router.add_service(
+                    tonic_reflection::server::Builder::configure()
+                        .register_encoded_file_descriptor_set(CASTORE_FILE_DESCRIPTOR_SET)
+                        .register_encoded_file_descriptor_set(FILE_DESCRIPTOR_SET)
+                        .build_v1()?,
+                );
             }
 
             info!(listen_address=%listen_address, "listening");
diff --git a/tvix/build/src/proto/mod.rs b/tvix/build/src/proto/mod.rs
index e359b5b5b70e..b36049d05b9d 100644
--- a/tvix/build/src/proto/mod.rs
+++ b/tvix/build/src/proto/mod.rs
@@ -1,7 +1,7 @@
 use std::path::{Path, PathBuf};
 
 use itertools::Itertools;
-use tvix_castore::proto::{NamedNode, ValidateNodeError};
+use tvix_castore::DirectoryError;
 
 mod grpc_buildservice_wrapper;
 
@@ -19,7 +19,7 @@ pub const FILE_DESCRIPTOR_SET: &[u8] = tonic::include_file_descriptor_set!("tvix
 #[derive(Debug, thiserror::Error)]
 pub enum ValidateBuildRequestError {
     #[error("invalid input node at position {0}: {1}")]
-    InvalidInputNode(usize, ValidateNodeError),
+    InvalidInputNode(usize, DirectoryError),
 
     #[error("input nodes are not sorted by name")]
     InputNodesNotSorted,
@@ -123,20 +123,21 @@ impl BuildRequest {
     /// and all restrictions around paths themselves (relative, clean, …) need
     // to be fulfilled.
     pub fn validate(&self) -> Result<(), ValidateBuildRequestError> {
-        // validate all input nodes
-        for (i, n) in self.inputs.iter().enumerate() {
-            // ensure the input node itself is valid
-            n.validate()
+        // validate names. Make sure they're sorted
+
+        let mut last_name: bytes::Bytes = "".into();
+        for (i, node) in self.inputs.iter().enumerate() {
+            // TODO(flokli): store result somewhere
+            let (name, _node) = node
+                .clone()
+                .into_name_and_node()
                 .map_err(|e| ValidateBuildRequestError::InvalidInputNode(i, e))?;
-        }
 
-        // now we can look at the names, and make sure they're sorted.
-        if !is_sorted(
-            self.inputs
-                .iter()
-                .map(|e| e.node.as_ref().unwrap().get_name()),
-        ) {
-            Err(ValidateBuildRequestError::InputNodesNotSorted)?
+            if name.as_ref() <= last_name.as_ref() {
+                return Err(ValidateBuildRequestError::InputNodesNotSorted);
+            } else {
+                last_name = name.into()
+            }
         }
 
         // validate working_dir
diff --git a/tvix/castore/Cargo.toml b/tvix/castore/Cargo.toml
index ded2292db750..b6752ae25af5 100644
--- a/tvix/castore/Cargo.toml
+++ b/tvix/castore/Cargo.toml
@@ -4,101 +4,68 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-async-compression = { version = "0.4.9", features = ["tokio", "zstd"]}
-async-stream = "0.3.5"
-async-tempfile = "0.4.0"
-blake3 = { version = "1.3.1", features = ["rayon", "std", "traits-preview"] }
-bstr = "1.6.0"
-bytes = "1.4.0"
-data-encoding = "2.6.0"
-digest = "0.10.7"
-fastcdc = { version = "3.1.0", features = ["tokio"] }
-futures = "0.3.30"
-lazy_static = "1.4.0"
-object_store = { version = "0.10.1", features = ["http"] }
-parking_lot = "0.12.1"
-pin-project-lite = "0.2.13"
-prost = "0.13.1"
-sled = { version = "0.34.7" }
-thiserror = "1.0.38"
-tokio-stream = { version = "0.1.14", features = ["fs", "net"] }
-tokio-util = { version = "0.7.9", features = ["io", "io-util", "codec"] }
-tokio-tar = "0.3.1"
-tokio = { version = "1.32.0", features = ["fs", "macros", "net", "rt", "rt-multi-thread", "signal"] }
-tonic = "0.12.0"
-tower = "0.4.13"
-tracing = "0.1.37"
-tracing-indicatif = "0.3.6"
+async-compression = { workspace = true, features = ["tokio", "zstd"] }
+async-stream = { workspace = true }
+async-tempfile = { workspace = true }
+blake3 = { workspace = true, features = ["rayon", "std", "traits-preview"] }
+bstr = { workspace = true }
+bytes = { workspace = true }
+data-encoding = { workspace = true }
+digest = { workspace = true }
+fastcdc = { workspace = true, features = ["tokio"] }
+futures = { workspace = true }
+lazy_static = { workspace = true }
+object_store = { workspace = true, features = ["http"] }
+parking_lot = { workspace = true }
+pin-project-lite = { workspace = true }
+prost = { workspace = true }
+sled = { workspace = true }
+thiserror = { workspace = true }
+tokio-stream = { workspace = true, features = ["fs", "net"] }
+tokio-util = { workspace = true, features = ["io", "io-util", "codec"] }
+tokio-tar = { workspace = true }
+tokio = { workspace = true, features = ["fs", "macros", "net", "rt", "rt-multi-thread", "signal"] }
+tonic = { workspace = true }
+tower = { workspace = true }
+tracing = { workspace = true }
+tracing-indicatif = { workspace = true }
 tvix-tracing = { path = "../tracing", features = ["tonic"] }
-url = "2.4.0"
-walkdir = "2.4.0"
-zstd = "0.13.0"
-serde = { version = "1.0.197", features = [ "derive" ] }
-serde_with = "3.7.0"
-serde_qs = "0.12.0"
-petgraph = "0.6.4"
-erased-serde = "0.4.5"
-serde_tagged = "0.3.0"
-hyper-util = "0.1.6"
-redb = "2.1.1"
-
-[dependencies.bigtable_rs]
-optional = true
-version = "0.2.10"
-
-[dependencies.fuse-backend-rs]
-optional = true
-version = "0.11.0"
-
-[dependencies.libc]
-optional = true
-version = "0.2.144"
-
-[dependencies.threadpool]
-version = "1.8.1"
-optional = true
-
-[dependencies.tonic-reflection]
-optional = true
-version = "0.12.0"
-
-[dependencies.vhost]
-optional = true
-version = "0.6"
-
-[dependencies.vhost-user-backend]
-optional = true
-version = "0.8"
-
-[dependencies.virtio-queue]
-optional = true
-version = "0.7"
-
-[dependencies.vm-memory]
-optional = true
-version = "0.10"
-
-[dependencies.vmm-sys-util]
-optional = true
-version = "0.11"
-
-[dependencies.virtio-bindings]
-optional = true
-version = "0.2.1"
+url = { workspace = true }
+walkdir = { workspace = true }
+zstd = { workspace = true }
+serde = { workspace = true, features = ["derive"] }
+serde_with = { workspace = true }
+serde_qs = { workspace = true }
+petgraph = { workspace = true }
+erased-serde = { workspace = true }
+serde_tagged = { workspace = true }
+hyper-util = { workspace = true }
+redb = { workspace = true }
+bigtable_rs = { workspace = true, optional = true }
+fuse-backend-rs = { workspace = true, optional = true }
+libc = { workspace = true, optional = true }
+threadpool = { workspace = true, optional = true }
+tonic-reflection = { workspace = true, optional = true }
+vhost = { workspace = true, optional = true }
+vhost-user-backend = { workspace = true, optional = true }
+virtio-queue = { workspace = true, optional = true }
+vm-memory = { workspace = true, optional = true }
+vmm-sys-util = { workspace = true, optional = true }
+virtio-bindings = { workspace = true, optional = true }
 
 [build-dependencies]
-prost-build = "0.13.1"
-tonic-build = "0.12.0"
+prost-build = { workspace = true }
+tonic-build = { workspace = true }
 
 [dev-dependencies]
-async-process = "2.1.0"
-rstest = "0.19.0"
-tempfile = "3.3.0"
-tokio-retry = "0.3.0"
-hex-literal = "0.4.1"
-rstest_reuse = "0.6.0"
-xattr = "1.3.1"
-serde_json = "*"
+async-process = { workspace = true }
+rstest = { workspace = true }
+tempfile = { workspace = true }
+tokio-retry = { workspace = true }
+hex-literal = { workspace = true }
+rstest_reuse = { workspace = true }
+xattr = { workspace = true }
+serde_json = { workspace = true }
 
 [features]
 default = ["cloud"]
diff --git a/tvix/castore/build.rs b/tvix/castore/build.rs
index 98e2ab348528..a4591c845509 100644
--- a/tvix/castore/build.rs
+++ b/tvix/castore/build.rs
@@ -26,7 +26,7 @@ fn main() -> Result<()> {
             ],
             // If we are in running `cargo build` manually, using `../..` works fine,
             // but in case we run inside a nix build, we need to instead point PROTO_ROOT
-            // to a sparseTree containing that structure.
+            // to a custom tree containing that structure.
             &[match std::env::var_os("PROTO_ROOT") {
                 Some(proto_root) => proto_root.to_str().unwrap().to_owned(),
                 None => "../..".to_string(),
diff --git a/tvix/castore/protos/default.nix b/tvix/castore/protos/default.nix
index feef55690fb9..08bb8fcfeef1 100644
--- a/tvix/castore/protos/default.nix
+++ b/tvix/castore/protos/default.nix
@@ -1,16 +1,10 @@
-{ depot, pkgs, ... }:
+{ depot, pkgs, lib, ... }:
 let
-  protos = depot.nix.sparseTree {
-    name = "castore-protos";
-    root = depot.path.origSrc;
-    paths = [
-      ./castore.proto
-      ./rpc_blobstore.proto
-      ./rpc_directory.proto
-      ../../../buf.yaml
-      ../../../buf.gen.yaml
-    ];
-  };
+  protos = lib.sourceByRegex depot.path.origSrc [
+    "buf.yaml"
+    "buf.gen.yaml"
+    "^tvix(/castore(/protos(/.*\.proto)?)?)?$"
+  ];
 in
 depot.nix.readTree.drvTargets {
   inherit protos;
diff --git a/tvix/castore/src/digests.rs b/tvix/castore/src/digests.rs
index ef9a7326b3fb..4d919ff0d873 100644
--- a/tvix/castore/src/digests.rs
+++ b/tvix/castore/src/digests.rs
@@ -6,7 +6,7 @@ use thiserror::Error;
 pub struct B3Digest(Bytes);
 
 // TODO: allow converting these errors to crate::Error
-#[derive(Error, Debug)]
+#[derive(Error, Debug, PartialEq)]
 pub enum Error {
     #[error("invalid digest length: {0}")]
     InvalidDigestLen(usize),
diff --git a/tvix/castore/src/directoryservice/bigtable.rs b/tvix/castore/src/directoryservice/bigtable.rs
index d10dddaf9f60..73ab4342d832 100644
--- a/tvix/castore/src/directoryservice/bigtable.rs
+++ b/tvix/castore/src/directoryservice/bigtable.rs
@@ -9,7 +9,9 @@ use std::sync::Arc;
 use tonic::async_trait;
 use tracing::{instrument, trace, warn};
 
-use super::{utils::traverse_directory, DirectoryPutter, DirectoryService, SimplePutter};
+use super::{
+    utils::traverse_directory, Directory, DirectoryPutter, DirectoryService, SimplePutter,
+};
 use crate::composition::{CompositionContext, ServiceBuilder};
 use crate::{proto, B3Digest, Error};
 
@@ -149,7 +151,7 @@ fn derive_directory_key(digest: &B3Digest) -> String {
 #[async_trait]
 impl DirectoryService for BigtableDirectoryService {
     #[instrument(skip(self, digest), err, fields(directory.digest = %digest))]
-    async fn get(&self, digest: &B3Digest) -> Result<Option<proto::Directory>, Error> {
+    async fn get(&self, digest: &B3Digest) -> Result<Option<Directory>, Error> {
         let mut client = self.client.clone();
         let directory_key = derive_directory_key(digest);
 
@@ -241,28 +243,20 @@ impl DirectoryService for BigtableDirectoryService {
 
         // Try to parse the value into a Directory message.
         let directory = proto::Directory::decode(Bytes::from(row_cell.value))
-            .map_err(|e| Error::StorageError(format!("unable to decode directory proto: {}", e)))?;
-
-        // validate the Directory.
-        directory
-            .validate()
+            .map_err(|e| Error::StorageError(format!("unable to decode directory proto: {}", e)))?
+            .try_into()
             .map_err(|e| Error::StorageError(format!("invalid Directory message: {}", e)))?;
 
         Ok(Some(directory))
     }
 
     #[instrument(skip(self, directory), err, fields(directory.digest = %directory.digest()))]
-    async fn put(&self, directory: proto::Directory) -> Result<B3Digest, Error> {
+    async fn put(&self, directory: Directory) -> Result<B3Digest, Error> {
         let directory_digest = directory.digest();
         let mut client = self.client.clone();
         let directory_key = derive_directory_key(&directory_digest);
 
-        // Ensure the directory we're trying to upload passes validation
-        directory
-            .validate()
-            .map_err(|e| Error::InvalidRequest(format!("directory is invalid: {}", e)))?;
-
-        let data = directory.encode_to_vec();
+        let data = proto::Directory::from(directory).encode_to_vec();
         if data.len() as u64 > CELL_SIZE_LIMIT {
             return Err(Error::StorageError(
                 "Directory exceeds cell limit on Bigtable".into(),
@@ -310,7 +304,7 @@ impl DirectoryService for BigtableDirectoryService {
     fn get_recursive(
         &self,
         root_directory_digest: &B3Digest,
-    ) -> BoxStream<'static, Result<proto::Directory, Error>> {
+    ) -> BoxStream<'static, Result<Directory, Error>> {
         traverse_directory(self.clone(), root_directory_digest)
     }
 
diff --git a/tvix/castore/src/directoryservice/combinators.rs b/tvix/castore/src/directoryservice/combinators.rs
index 0fdc82c16cb0..4283142231f9 100644
--- a/tvix/castore/src/directoryservice/combinators.rs
+++ b/tvix/castore/src/directoryservice/combinators.rs
@@ -7,10 +7,9 @@ use futures::TryStreamExt;
 use tonic::async_trait;
 use tracing::{instrument, trace};
 
-use super::{DirectoryGraph, DirectoryService, RootToLeavesValidator, SimplePutter};
+use super::{Directory, DirectoryGraph, DirectoryService, RootToLeavesValidator, SimplePutter};
 use crate::composition::{CompositionContext, ServiceBuilder};
 use crate::directoryservice::DirectoryPutter;
-use crate::proto;
 use crate::B3Digest;
 use crate::Error;
 
@@ -40,7 +39,7 @@ where
     DS2: DirectoryService + Clone + 'static,
 {
     #[instrument(skip(self, digest), fields(directory.digest = %digest))]
-    async fn get(&self, digest: &B3Digest) -> Result<Option<proto::Directory>, Error> {
+    async fn get(&self, digest: &B3Digest) -> Result<Option<Directory>, Error> {
         match self.near.get(digest).await? {
             Some(directory) => {
                 trace!("serving from cache");
@@ -82,7 +81,7 @@ where
     }
 
     #[instrument(skip_all)]
-    async fn put(&self, _directory: proto::Directory) -> Result<B3Digest, Error> {
+    async fn put(&self, _directory: Directory) -> Result<B3Digest, Error> {
         Err(Error::StorageError("unimplemented".to_string()))
     }
 
@@ -90,7 +89,7 @@ where
     fn get_recursive(
         &self,
         root_directory_digest: &B3Digest,
-    ) -> BoxStream<'static, Result<proto::Directory, Error>> {
+    ) -> BoxStream<'static, Result<Directory, Error>> {
         let near = self.near.clone();
         let far = self.far.clone();
         let digest = root_directory_digest.clone();
diff --git a/tvix/castore/src/directoryservice/directory_graph.rs b/tvix/castore/src/directoryservice/directory_graph.rs
index e6b9b163370c..017cef024059 100644
--- a/tvix/castore/src/directoryservice/directory_graph.rs
+++ b/tvix/castore/src/directoryservice/directory_graph.rs
@@ -1,7 +1,5 @@
 use std::collections::HashMap;
 
-use bstr::ByteSlice;
-
 use petgraph::{
     graph::{DiGraph, NodeIndex},
     visit::{Bfs, DfsPostOrder, EdgeRef, IntoNodeIdentifiers, Walker},
@@ -10,10 +8,7 @@ use petgraph::{
 use tracing::instrument;
 
 use super::order_validator::{LeavesToRootValidator, OrderValidator, RootToLeavesValidator};
-use crate::{
-    proto::{self, Directory, DirectoryNode},
-    B3Digest,
-};
+use crate::{path::PathComponent, B3Digest, Directory, Node};
 
 #[derive(thiserror::Error, Debug)]
 pub enum Error {
@@ -21,6 +16,11 @@ pub enum Error {
     ValidationError(String),
 }
 
+struct EdgeWeight {
+    name: PathComponent,
+    size: u64,
+}
+
 /// This can be used to validate and/or re-order a Directory closure (DAG of
 /// connected Directories), and their insertion order.
 ///
@@ -58,7 +58,7 @@ pub struct DirectoryGraph<O> {
     //
     // The option in the edge weight tracks the pending validation state of the respective edge, for example if
     // the child has not been added yet.
-    graph: DiGraph<Option<Directory>, Option<DirectoryNode>>,
+    graph: DiGraph<Option<Directory>, Option<EdgeWeight>>,
 
     // A lookup table from directory digest to node index.
     digest_to_node_ix: HashMap<B3Digest, NodeIndex>,
@@ -67,18 +67,18 @@ pub struct DirectoryGraph<O> {
 }
 
 pub struct ValidatedDirectoryGraph {
-    graph: DiGraph<Option<Directory>, Option<DirectoryNode>>,
+    graph: DiGraph<Option<Directory>, Option<EdgeWeight>>,
 
     root: Option<NodeIndex>,
 }
 
-fn check_edge(dir: &DirectoryNode, child: &Directory) -> Result<(), Error> {
+fn check_edge(edge: &EdgeWeight, child: &Directory) -> Result<(), Error> {
     // Ensure the size specified in the child node matches our records.
-    if dir.size != child.size() {
+    if edge.size != child.size() {
         return Err(Error::ValidationError(format!(
             "'{}' has wrong size, specified {}, recorded {}",
-            dir.name.as_bstr(),
-            dir.size,
+            edge.name,
+            edge.size,
             child.size(),
         )));
     }
@@ -88,7 +88,7 @@ fn check_edge(dir: &DirectoryNode, child: &Directory) -> Result<(), Error> {
 impl DirectoryGraph<LeavesToRootValidator> {
     /// Insert a new Directory into the closure
     #[instrument(level = "trace", skip_all, fields(directory.digest=%directory.digest(), directory.size=%directory.size()), err)]
-    pub fn add(&mut self, directory: proto::Directory) -> Result<(), Error> {
+    pub fn add(&mut self, directory: Directory) -> Result<(), Error> {
         if !self.order_validator.add_directory(&directory) {
             return Err(Error::ValidationError(
                 "unknown directory was referenced".into(),
@@ -108,7 +108,7 @@ impl DirectoryGraph<RootToLeavesValidator> {
 
     /// Insert a new Directory into the closure
     #[instrument(level = "trace", skip_all, fields(directory.digest=%directory.digest(), directory.size=%directory.size()), err)]
-    pub fn add(&mut self, directory: proto::Directory) -> Result<(), Error> {
+    pub fn add(&mut self, directory: Directory) -> Result<(), Error> {
         let digest = directory.digest();
         if !self.order_validator.digest_allowed(&digest) {
             return Err(Error::ValidationError("unexpected digest".into()));
@@ -129,12 +129,7 @@ impl<O: OrderValidator> DirectoryGraph<O> {
     }
 
     /// Adds a directory which has already been confirmed to be in-order to the graph
-    pub fn add_order_unchecked(&mut self, directory: proto::Directory) -> Result<(), Error> {
-        // Do some basic validation
-        directory
-            .validate()
-            .map_err(|e| Error::ValidationError(e.to_string()))?;
-
+    pub fn add_order_unchecked(&mut self, directory: Directory) -> Result<(), Error> {
         let digest = directory.digest();
 
         // Teach the graph about the existence of a node with this digest
@@ -149,23 +144,32 @@ impl<O: OrderValidator> DirectoryGraph<O> {
         }
 
         // set up edges to all child directories
-        for subdir in &directory.directories {
-            let subdir_digest: B3Digest = subdir.digest.clone().try_into().unwrap();
-
-            let child_ix = *self
-                .digest_to_node_ix
-                .entry(subdir_digest)
-                .or_insert_with(|| self.graph.add_node(None));
-
-            let pending_edge_check = match &self.graph[child_ix] {
-                Some(child) => {
-                    // child is already available, validate the edge now
-                    check_edge(subdir, child)?;
-                    None
-                }
-                None => Some(subdir.clone()), // pending validation
-            };
-            self.graph.add_edge(ix, child_ix, pending_edge_check);
+        for (name, node) in directory.nodes() {
+            if let Node::Directory { digest, size } = node {
+                let child_ix = *self
+                    .digest_to_node_ix
+                    .entry(digest.clone())
+                    .or_insert_with(|| self.graph.add_node(None));
+
+                let pending_edge_check = match &self.graph[child_ix] {
+                    Some(child) => {
+                        // child is already available, validate the edge now
+                        check_edge(
+                            &EdgeWeight {
+                                name: name.clone(),
+                                size: *size,
+                            },
+                            child,
+                        )?;
+                        None
+                    }
+                    None => Some(EdgeWeight {
+                        name: name.clone(),
+                        size: *size,
+                    }), // pending validation
+                };
+                self.graph.add_edge(ix, child_ix, pending_edge_check);
+            }
         }
 
         // validate the edges from parents to this node
@@ -183,6 +187,7 @@ impl<O: OrderValidator> DirectoryGraph<O> {
                 .expect("edge not found")
                 .take()
                 .expect("edge is already validated");
+
             check_edge(&edge_weight, &directory)?;
         }
 
@@ -266,37 +271,37 @@ impl ValidatedDirectoryGraph {
             .filter_map(move |i| nodes[i.index()].weight.take())
     }
 }
-
-#[cfg(test)]
-mod tests {
-    use crate::{
-        fixtures::{DIRECTORY_A, DIRECTORY_B, DIRECTORY_C},
-        proto::{self, Directory},
-    };
-    use lazy_static::lazy_static;
-    use rstest::rstest;
-
-    lazy_static! {
+/*
         pub static ref BROKEN_DIRECTORY : Directory = Directory {
-            symlinks: vec![proto::SymlinkNode {
+            symlinks: vec![SymlinkNode {
                 name: "".into(), // invalid name!
                 target: "doesntmatter".into(),
             }],
             ..Default::default()
         };
-
-        pub static ref BROKEN_PARENT_DIRECTORY: Directory = Directory {
-            directories: vec![proto::DirectoryNode {
-                name: "foo".into(),
-                digest: DIRECTORY_A.digest().into(),
-                size: DIRECTORY_A.size() + 42, // wrong!
-            }],
-            ..Default::default()
-        };
-    }
+*/
+#[cfg(test)]
+mod tests {
+    use crate::fixtures::{DIRECTORY_A, DIRECTORY_B, DIRECTORY_C};
+    use crate::{Directory, Node};
+    use lazy_static::lazy_static;
+    use rstest::rstest;
 
     use super::{DirectoryGraph, LeavesToRootValidator, RootToLeavesValidator};
 
+    lazy_static! {
+        pub static ref BROKEN_PARENT_DIRECTORY: Directory =
+            Directory::try_from_iter([
+                (
+                    "foo".try_into().unwrap(),
+                    Node::Directory{
+                        digest: DIRECTORY_A.digest(),
+                        size: DIRECTORY_A.size() + 42, // wrong!
+                    }
+                )
+            ]).unwrap();
+    }
+
     #[rstest]
     /// Uploading an empty directory should succeed.
     #[case::empty_directory(&[&*DIRECTORY_A], false, Some(vec![&*DIRECTORY_A]))]
@@ -312,8 +317,6 @@ mod tests {
     #[case::unconnected_node(&[&*DIRECTORY_A, &*DIRECTORY_C, &*DIRECTORY_B], false, None)]
     /// Uploading B (referring to A) should fail immediately, because A was never uploaded.
     #[case::dangling_pointer(&[&*DIRECTORY_B], true, None)]
-    /// Uploading a directory failing validation should fail immediately.
-    #[case::failing_validation(&[&*BROKEN_DIRECTORY], true, None)]
     /// Uploading a directory which refers to another Directory with a wrong size should fail.
     #[case::wrong_size_in_parent(&[&*DIRECTORY_A, &*BROKEN_PARENT_DIRECTORY], true, None)]
     fn test_uploads(
@@ -366,8 +369,6 @@ mod tests {
     #[case::unconnected_node(&*DIRECTORY_C, &[&*DIRECTORY_C, &*DIRECTORY_B], true, None)]
     /// Downloading B (specified as the root) but receiving A instead should fail immediately, because A has no connection to B (the root).
     #[case::dangling_pointer(&*DIRECTORY_B, &[&*DIRECTORY_A], true, None)]
-    /// Downloading a directory failing validation should fail immediately.
-    #[case::failing_validation(&*BROKEN_DIRECTORY, &[&*BROKEN_DIRECTORY], true, None)]
     /// Downloading a directory which refers to another Directory with a wrong size should fail.
     #[case::wrong_size_in_parent(&*BROKEN_PARENT_DIRECTORY, &[&*BROKEN_PARENT_DIRECTORY, &*DIRECTORY_A], true, None)]
     fn test_downloads(
diff --git a/tvix/castore/src/directoryservice/grpc.rs b/tvix/castore/src/directoryservice/grpc.rs
index 4dc3931ed410..9696c5631949 100644
--- a/tvix/castore/src/directoryservice/grpc.rs
+++ b/tvix/castore/src/directoryservice/grpc.rs
@@ -1,9 +1,9 @@
 use std::collections::HashSet;
 
-use super::{DirectoryPutter, DirectoryService};
+use super::{Directory, DirectoryPutter, DirectoryService};
 use crate::composition::{CompositionContext, ServiceBuilder};
 use crate::proto::{self, get_directory_request::ByWhat};
-use crate::{B3Digest, Error};
+use crate::{B3Digest, DirectoryError, Error};
 use async_stream::try_stream;
 use futures::stream::BoxStream;
 use std::sync::Arc;
@@ -41,10 +41,7 @@ where
     T::Future: Send,
 {
     #[instrument(level = "trace", skip_all, fields(directory.digest = %digest))]
-    async fn get(
-        &self,
-        digest: &B3Digest,
-    ) -> Result<Option<crate::proto::Directory>, crate::Error> {
+    async fn get(&self, digest: &B3Digest) -> Result<Option<Directory>, crate::Error> {
         // Get a new handle to the gRPC client, and copy the digest.
         let mut grpc_client = self.grpc_client.clone();
         let digest_cpy = digest.clone();
@@ -72,15 +69,10 @@ where
                         "requested directory with digest {}, but got {}",
                         digest, actual_digest
                     )))
-                } else if let Err(e) = directory.validate() {
-                    // Validate the Directory itself is valid.
-                    warn!("directory failed validation: {}", e.to_string());
-                    Err(crate::Error::StorageError(format!(
-                        "directory {} failed validation: {}",
-                        digest, e,
-                    )))
                 } else {
-                    Ok(Some(directory))
+                    Ok(Some(directory.try_into().map_err(|_| {
+                        Error::StorageError("invalid root digest length in response".to_string())
+                    })?))
                 }
             }
             Ok(None) => Ok(None),
@@ -90,11 +82,11 @@ where
     }
 
     #[instrument(level = "trace", skip_all, fields(directory.digest = %directory.digest()))]
-    async fn put(&self, directory: crate::proto::Directory) -> Result<B3Digest, crate::Error> {
+    async fn put(&self, directory: Directory) -> Result<B3Digest, crate::Error> {
         let resp = self
             .grpc_client
             .clone()
-            .put(tokio_stream::once(directory))
+            .put(tokio_stream::once(proto::Directory::from(directory)))
             .await;
 
         match resp {
@@ -113,7 +105,7 @@ where
     fn get_recursive(
         &self,
         root_directory_digest: &B3Digest,
-    ) -> BoxStream<'static, Result<proto::Directory, Error>> {
+    ) -> BoxStream<'static, Result<Directory, Error>> {
         let mut grpc_client = self.grpc_client.clone();
         let root_directory_digest = root_directory_digest.clone();
 
@@ -135,14 +127,6 @@ where
             loop {
                 match stream.message().await {
                     Ok(Some(directory)) => {
-                        // validate the directory itself.
-                        if let Err(e) = directory.validate() {
-                            Err(crate::Error::StorageError(format!(
-                                "directory {} failed validation: {}",
-                                directory.digest(),
-                                e,
-                            )))?;
-                        }
                         // validate we actually expected that directory, and move it from expected to received.
                         let directory_digest = directory.digest();
                         let was_expected = expected_directory_digests.remove(&directory_digest);
@@ -168,6 +152,9 @@ where
                                 .insert(child_directory_digest);
                         }
 
+                        let directory = directory.try_into()
+                            .map_err(|e: DirectoryError| Error::StorageError(e.to_string()))?;
+
                         yield directory;
                     },
                     Ok(None) if expected_directory_digests.len() == 1 && expected_directory_digests.contains(&root_directory_digest) => {
@@ -279,11 +266,11 @@ pub struct GRPCPutter {
 #[async_trait]
 impl DirectoryPutter for GRPCPutter {
     #[instrument(level = "trace", skip_all, fields(directory.digest=%directory.digest()), err)]
-    async fn put(&mut self, directory: proto::Directory) -> Result<(), crate::Error> {
+    async fn put(&mut self, directory: Directory) -> Result<(), crate::Error> {
         match self.rq {
             // If we're not already closed, send the directory to directory_sender.
             Some((_, ref directory_sender)) => {
-                if directory_sender.send(directory).is_err() {
+                if directory_sender.send(directory.into()).is_err() {
                     // If the channel has been prematurely closed, invoke close (so we can peek at the error code)
                     // That error code is much more helpful, because it
                     // contains the error message from the server.
diff --git a/tvix/castore/src/directoryservice/memory.rs b/tvix/castore/src/directoryservice/memory.rs
index ada4606a5a57..b039d9bc7d84 100644
--- a/tvix/castore/src/directoryservice/memory.rs
+++ b/tvix/castore/src/directoryservice/memory.rs
@@ -1,4 +1,4 @@
-use crate::{proto, B3Digest, Error};
+use crate::{B3Digest, Error};
 use futures::stream::BoxStream;
 use std::collections::HashMap;
 use std::sync::Arc;
@@ -7,8 +7,9 @@ use tonic::async_trait;
 use tracing::{instrument, warn};
 
 use super::utils::traverse_directory;
-use super::{DirectoryPutter, DirectoryService, SimplePutter};
+use super::{Directory, DirectoryPutter, DirectoryService, SimplePutter};
 use crate::composition::{CompositionContext, ServiceBuilder};
+use crate::proto;
 
 #[derive(Clone, Default)]
 pub struct MemoryDirectoryService {
@@ -18,7 +19,7 @@ pub struct MemoryDirectoryService {
 #[async_trait]
 impl DirectoryService for MemoryDirectoryService {
     #[instrument(skip(self, digest), fields(directory.digest = %digest))]
-    async fn get(&self, digest: &B3Digest) -> Result<Option<proto::Directory>, Error> {
+    async fn get(&self, digest: &B3Digest) -> Result<Option<Directory>, Error> {
         let db = self.db.read().await;
 
         match db.get(digest) {
@@ -37,35 +38,20 @@ impl DirectoryService for MemoryDirectoryService {
                     )));
                 }
 
-                // Validate the Directory itself is valid.
-                if let Err(e) = directory.validate() {
-                    warn!("directory failed validation: {}", e.to_string());
-                    return Err(Error::StorageError(format!(
-                        "directory {} failed validation: {}",
-                        actual_digest, e,
-                    )));
-                }
-
-                Ok(Some(directory.clone()))
+                Ok(Some(directory.clone().try_into().map_err(|e| {
+                    crate::Error::StorageError(format!("corrupted directory: {}", e))
+                })?))
             }
         }
     }
 
     #[instrument(skip(self, directory), fields(directory.digest = %directory.digest()))]
-    async fn put(&self, directory: proto::Directory) -> Result<B3Digest, Error> {
+    async fn put(&self, directory: Directory) -> Result<B3Digest, Error> {
         let digest = directory.digest();
 
-        // validate the directory itself.
-        if let Err(e) = directory.validate() {
-            return Err(Error::InvalidRequest(format!(
-                "directory {} failed validation: {}",
-                digest, e,
-            )));
-        }
-
         // store it
         let mut db = self.db.write().await;
-        db.insert(digest.clone(), directory);
+        db.insert(digest.clone(), directory.into());
 
         Ok(digest)
     }
@@ -74,7 +60,7 @@ impl DirectoryService for MemoryDirectoryService {
     fn get_recursive(
         &self,
         root_directory_digest: &B3Digest,
-    ) -> BoxStream<'static, Result<proto::Directory, Error>> {
+    ) -> BoxStream<'static, Result<Directory, Error>> {
         traverse_directory(self.clone(), root_directory_digest)
     }
 
diff --git a/tvix/castore/src/directoryservice/mod.rs b/tvix/castore/src/directoryservice/mod.rs
index 17a78b179349..25162e4de853 100644
--- a/tvix/castore/src/directoryservice/mod.rs
+++ b/tvix/castore/src/directoryservice/mod.rs
@@ -1,5 +1,5 @@
 use crate::composition::{Registry, ServiceBuilder};
-use crate::{proto, B3Digest, Error};
+use crate::{B3Digest, Directory, Error};
 
 use futures::stream::BoxStream;
 use tonic::async_trait;
@@ -19,7 +19,7 @@ mod traverse;
 mod utils;
 
 pub use self::combinators::{Cache, CacheConfig};
-pub use self::directory_graph::DirectoryGraph;
+pub use self::directory_graph::{DirectoryGraph, ValidatedDirectoryGraph};
 pub use self::from_addr::from_addr;
 pub use self::grpc::{GRPCDirectoryService, GRPCDirectoryServiceConfig};
 pub use self::memory::{MemoryDirectoryService, MemoryDirectoryServiceConfig};
@@ -38,7 +38,7 @@ mod bigtable;
 pub use self::bigtable::{BigtableDirectoryService, BigtableParameters};
 
 /// The base trait all Directory services need to implement.
-/// This is a simple get and put of [crate::proto::Directory], returning their
+/// This is a simple get and put of [Directory], returning their
 /// digest.
 #[async_trait]
 pub trait DirectoryService: Send + Sync {
@@ -50,14 +50,14 @@ pub trait DirectoryService: Send + Sync {
     /// Directory digests that are at the "root", aka the last element that's
     /// sent to a DirectoryPutter. This makes sense for implementations bundling
     /// closures of directories together in batches.
-    async fn get(&self, digest: &B3Digest) -> Result<Option<proto::Directory>, Error>;
+    async fn get(&self, digest: &B3Digest) -> Result<Option<Directory>, Error>;
     /// Uploads a single Directory message, and returns the calculated
     /// digest, or an error. An error *must* also be returned if the message is
     /// not valid.
-    async fn put(&self, directory: proto::Directory) -> Result<B3Digest, Error>;
+    async fn put(&self, directory: Directory) -> Result<B3Digest, Error>;
 
-    /// Looks up a closure of [proto::Directory].
-    /// Ideally this would be a `impl Stream<Item = Result<proto::Directory, Error>>`,
+    /// Looks up a closure of [Directory].
+    /// Ideally this would be a `impl Stream<Item = Result<Directory, Error>>`,
     /// and we'd be able to add a default implementation for it here, but
     /// we can't have that yet.
     ///
@@ -75,9 +75,9 @@ pub trait DirectoryService: Send + Sync {
     fn get_recursive(
         &self,
         root_directory_digest: &B3Digest,
-    ) -> BoxStream<'static, Result<proto::Directory, Error>>;
+    ) -> BoxStream<'static, Result<Directory, Error>>;
 
-    /// Allows persisting a closure of [proto::Directory], which is a graph of
+    /// Allows persisting a closure of [Directory], which is a graph of
     /// connected Directory messages.
     fn put_multiple_start(&self) -> Box<dyn DirectoryPutter>;
 }
@@ -87,18 +87,18 @@ impl<A> DirectoryService for A
 where
     A: AsRef<dyn DirectoryService> + Send + Sync,
 {
-    async fn get(&self, digest: &B3Digest) -> Result<Option<proto::Directory>, Error> {
+    async fn get(&self, digest: &B3Digest) -> Result<Option<Directory>, Error> {
         self.as_ref().get(digest).await
     }
 
-    async fn put(&self, directory: proto::Directory) -> Result<B3Digest, Error> {
+    async fn put(&self, directory: Directory) -> Result<B3Digest, Error> {
         self.as_ref().put(directory).await
     }
 
     fn get_recursive(
         &self,
         root_directory_digest: &B3Digest,
-    ) -> BoxStream<'static, Result<proto::Directory, Error>> {
+    ) -> BoxStream<'static, Result<Directory, Error>> {
         self.as_ref().get_recursive(root_directory_digest)
     }
 
@@ -107,7 +107,7 @@ where
     }
 }
 
-/// Provides a handle to put a closure of connected [proto::Directory] elements.
+/// Provides a handle to put a closure of connected [Directory] elements.
 ///
 /// The consumer can periodically call [DirectoryPutter::put], starting from the
 /// leaves. Once the root is reached, [DirectoryPutter::close] can be called to
@@ -119,12 +119,12 @@ where
 /// but a single file or symlink.
 #[async_trait]
 pub trait DirectoryPutter: Send {
-    /// Put a individual [proto::Directory] into the store.
+    /// Put a individual [Directory] into the store.
     /// Error semantics and behaviour is up to the specific implementation of
     /// this trait.
     /// Due to bursting, the returned error might refer to an object previously
     /// sent via `put`.
-    async fn put(&mut self, directory: proto::Directory) -> Result<(), Error>;
+    async fn put(&mut self, directory: Directory) -> Result<(), Error>;
 
     /// Close the stream, and wait for any errors.
     /// If there's been any invalid Directory message uploaded, and error *must*
diff --git a/tvix/castore/src/directoryservice/object_store.rs b/tvix/castore/src/directoryservice/object_store.rs
index a9a2cc8ef5c0..5b5281abcd2f 100644
--- a/tvix/castore/src/directoryservice/object_store.rs
+++ b/tvix/castore/src/directoryservice/object_store.rs
@@ -17,10 +17,11 @@ use tracing::{instrument, trace, warn, Level};
 use url::Url;
 
 use super::{
-    DirectoryGraph, DirectoryPutter, DirectoryService, LeavesToRootValidator, RootToLeavesValidator,
+    Directory, DirectoryGraph, DirectoryPutter, DirectoryService, LeavesToRootValidator,
+    RootToLeavesValidator,
 };
 use crate::composition::{CompositionContext, ServiceBuilder};
-use crate::{proto, B3Digest, Error};
+use crate::{proto, B3Digest, Error, Node};
 
 /// Stores directory closures in an object store.
 /// Notably, this makes use of the option to disallow accessing child directories except when
@@ -78,13 +79,17 @@ impl DirectoryService for ObjectStoreDirectoryService {
     /// This is the same steps as for get_recursive anyways, so we just call get_recursive and
     /// return the first element of the stream and drop the request.
     #[instrument(skip(self, digest), fields(directory.digest = %digest))]
-    async fn get(&self, digest: &B3Digest) -> Result<Option<proto::Directory>, Error> {
+    async fn get(&self, digest: &B3Digest) -> Result<Option<Directory>, Error> {
         self.get_recursive(digest).take(1).next().await.transpose()
     }
 
     #[instrument(skip(self, directory), fields(directory.digest = %directory.digest()))]
-    async fn put(&self, directory: proto::Directory) -> Result<B3Digest, Error> {
-        if !directory.directories.is_empty() {
+    async fn put(&self, directory: Directory) -> Result<B3Digest, Error> {
+        // Ensure the directory doesn't contain other directory children
+        if directory
+            .nodes()
+            .any(|(_, e)| matches!(e, Node::Directory { .. }))
+        {
             return Err(Error::InvalidRequest(
                     "only put_multiple_start is supported by the ObjectStoreDirectoryService for directories with children".into(),
             ));
@@ -99,7 +104,7 @@ impl DirectoryService for ObjectStoreDirectoryService {
     fn get_recursive(
         &self,
         root_directory_digest: &B3Digest,
-    ) -> BoxStream<'static, Result<proto::Directory, Error>> {
+    ) -> BoxStream<'static, Result<Directory, Error>> {
         // Check that we are not passing on bogus from the object store to the client, and that the
         // trust chain from the root digest to the leaves is intact
         let mut order_validator =
@@ -145,6 +150,10 @@ impl DirectoryService for ObjectStoreDirectoryService {
                             warn!("unable to parse directory {}: {}", digest, e);
                             Error::StorageError(e.to_string())
                         })?;
+                        let directory = Directory::try_from(directory).map_err(|e| {
+                            warn!("unable to convert directory {}: {}", digest, e);
+                            Error::StorageError(e.to_string())
+                        })?;
 
                         // Allow the children to appear next
                         order_validator.add_directory_unchecked(&directory);
@@ -244,7 +253,7 @@ impl ObjectStoreDirectoryPutter {
 #[async_trait]
 impl DirectoryPutter for ObjectStoreDirectoryPutter {
     #[instrument(level = "trace", skip_all, fields(directory.digest=%directory.digest()), err)]
-    async fn put(&mut self, directory: proto::Directory) -> Result<(), Error> {
+    async fn put(&mut self, directory: Directory) -> Result<(), Error> {
         match self.directory_validator {
             None => return Err(Error::StorageError("already closed".to_string())),
             Some(ref mut validator) => {
@@ -302,7 +311,7 @@ impl DirectoryPutter for ObjectStoreDirectoryPutter {
 
                 for directory in directories {
                     directories_sink
-                        .send(directory.encode_to_vec().into())
+                        .send(proto::Directory::from(directory).encode_to_vec().into())
                         .await?;
                 }
 
diff --git a/tvix/castore/src/directoryservice/order_validator.rs b/tvix/castore/src/directoryservice/order_validator.rs
index 6045f5d24198..973af92e1294 100644
--- a/tvix/castore/src/directoryservice/order_validator.rs
+++ b/tvix/castore/src/directoryservice/order_validator.rs
@@ -1,7 +1,8 @@
 use std::collections::HashSet;
 use tracing::warn;
 
-use crate::{proto::Directory, B3Digest};
+use super::Directory;
+use crate::{B3Digest, Node};
 
 pub trait OrderValidator {
     /// Update the order validator's state with the directory
@@ -47,10 +48,11 @@ impl RootToLeavesValidator {
             self.expected_digests.insert(directory.digest());
         }
 
-        for subdir in &directory.directories {
-            // Allow the children to appear next
-            let subdir_digest = subdir.digest.clone().try_into().unwrap();
-            self.expected_digests.insert(subdir_digest);
+        // Allow the children to appear next
+        for (_, node) in directory.nodes() {
+            if let Node::Directory { digest, .. } = node {
+                self.expected_digests.insert(digest.clone());
+            }
         }
     }
 }
@@ -79,15 +81,20 @@ impl OrderValidator for LeavesToRootValidator {
     fn add_directory(&mut self, directory: &Directory) -> bool {
         let digest = directory.digest();
 
-        for subdir in &directory.directories {
-            let subdir_digest = subdir.digest.clone().try_into().unwrap(); // this has been validated in validate_directory()
-            if !self.allowed_references.contains(&subdir_digest) {
-                warn!(
-                    directory.digest = %digest,
-                    subdirectory.digest = %subdir_digest,
-                    "unexpected directory reference"
-                );
-                return false;
+        for (_, node) in directory.nodes() {
+            if let Node::Directory {
+                digest: subdir_node_digest,
+                ..
+            } = node
+            {
+                if !self.allowed_references.contains(subdir_node_digest) {
+                    warn!(
+                        directory.digest = %digest,
+                        subdirectory.digest = %subdir_node_digest,
+                        "unexpected directory reference"
+                    );
+                    return false;
+                }
             }
         }
 
@@ -101,8 +108,8 @@ impl OrderValidator for LeavesToRootValidator {
 mod tests {
     use super::{LeavesToRootValidator, RootToLeavesValidator};
     use crate::directoryservice::order_validator::OrderValidator;
+    use crate::directoryservice::Directory;
     use crate::fixtures::{DIRECTORY_A, DIRECTORY_B, DIRECTORY_C};
-    use crate::proto::Directory;
     use rstest::rstest;
 
     #[rstest]
diff --git a/tvix/castore/src/directoryservice/redb.rs b/tvix/castore/src/directoryservice/redb.rs
index 51dc87f92574..d253df503bb3 100644
--- a/tvix/castore/src/directoryservice/redb.rs
+++ b/tvix/castore/src/directoryservice/redb.rs
@@ -5,15 +5,15 @@ use std::{path::PathBuf, sync::Arc};
 use tonic::async_trait;
 use tracing::{instrument, warn};
 
+use super::{
+    traverse_directory, Directory, DirectoryGraph, DirectoryPutter, DirectoryService,
+    LeavesToRootValidator,
+};
 use crate::{
     composition::{CompositionContext, ServiceBuilder},
     digests, proto, B3Digest, Error,
 };
 
-use super::{
-    traverse_directory, DirectoryGraph, DirectoryPutter, DirectoryService, LeavesToRootValidator,
-};
-
 const DIRECTORY_TABLE: TableDefinition<[u8; digests::B3_LEN], Vec<u8>> =
     TableDefinition::new("directory");
 
@@ -69,7 +69,7 @@ fn create_schema(db: &redb::Database) -> Result<(), redb::Error> {
 #[async_trait]
 impl DirectoryService for RedbDirectoryService {
     #[instrument(skip(self, digest), fields(directory.digest = %digest))]
-    async fn get(&self, digest: &B3Digest) -> Result<Option<proto::Directory>, Error> {
+    async fn get(&self, digest: &B3Digest) -> Result<Option<Directory>, Error> {
         let db = self.db.clone();
 
         // Retrieves the protobuf-encoded Directory for the corresponding digest.
@@ -107,13 +107,10 @@ impl DirectoryService for RedbDirectoryService {
         let directory = match proto::Directory::decode(&*directory_data.value()) {
             Ok(dir) => {
                 // The returned Directory must be valid.
-                if let Err(e) = dir.validate() {
+                dir.try_into().map_err(|e| {
                     warn!(err=%e, "Directory failed validation");
-                    return Err(Error::StorageError(
-                        "Directory failed validation".to_string(),
-                    ));
-                }
-                dir
+                    Error::StorageError("Directory failed validation".to_string())
+                })?
             }
             Err(e) => {
                 warn!(err=%e, "failed to parse Directory");
@@ -125,26 +122,21 @@ impl DirectoryService for RedbDirectoryService {
     }
 
     #[instrument(skip(self, directory), fields(directory.digest = %directory.digest()))]
-    async fn put(&self, directory: proto::Directory) -> Result<B3Digest, Error> {
+    async fn put(&self, directory: Directory) -> Result<B3Digest, Error> {
         tokio::task::spawn_blocking({
             let db = self.db.clone();
             move || {
                 let digest = directory.digest();
 
-                // Validate the directory.
-                if let Err(e) = directory.validate() {
-                    warn!(err=%e, "Directory failed validation");
-                    return Err(Error::StorageError(
-                        "Directory failed validation".to_string(),
-                    ));
-                }
-
                 // Store the directory in the table.
                 let txn = db.begin_write()?;
                 {
                     let mut table = txn.open_table(DIRECTORY_TABLE)?;
                     let digest_as_array: [u8; digests::B3_LEN] = digest.clone().into();
-                    table.insert(digest_as_array, directory.encode_to_vec())?;
+                    table.insert(
+                        digest_as_array,
+                        proto::Directory::from(directory).encode_to_vec(),
+                    )?;
                 }
                 txn.commit()?;
 
@@ -158,7 +150,7 @@ impl DirectoryService for RedbDirectoryService {
     fn get_recursive(
         &self,
         root_directory_digest: &B3Digest,
-    ) -> BoxStream<'static, Result<proto::Directory, Error>> {
+    ) -> BoxStream<'static, Result<Directory, Error>> {
         // FUTUREWORK: Ideally we should have all of the directory traversing happen in a single
         // redb transaction to avoid constantly closing and opening new transactions for the
         // database.
@@ -185,7 +177,7 @@ pub struct RedbDirectoryPutter {
 #[async_trait]
 impl DirectoryPutter for RedbDirectoryPutter {
     #[instrument(level = "trace", skip_all, fields(directory.digest=%directory.digest()), err)]
-    async fn put(&mut self, directory: proto::Directory) -> Result<(), Error> {
+    async fn put(&mut self, directory: Directory) -> Result<(), Error> {
         match self.directory_validator {
             None => return Err(Error::StorageError("already closed".to_string())),
             Some(ref mut validator) => {
@@ -228,7 +220,10 @@ impl DirectoryPutter for RedbDirectoryPutter {
                             for directory in directories {
                                 let digest_as_array: [u8; digests::B3_LEN] =
                                     directory.digest().into();
-                                table.insert(digest_as_array, directory.encode_to_vec())?;
+                                table.insert(
+                                    digest_as_array,
+                                    proto::Directory::from(directory).encode_to_vec(),
+                                )?;
                             }
                         }
 
diff --git a/tvix/castore/src/directoryservice/simple_putter.rs b/tvix/castore/src/directoryservice/simple_putter.rs
index dc54e3d11d18..b4daaee61b22 100644
--- a/tvix/castore/src/directoryservice/simple_putter.rs
+++ b/tvix/castore/src/directoryservice/simple_putter.rs
@@ -1,7 +1,6 @@
 use super::DirectoryPutter;
 use super::DirectoryService;
-use super::{DirectoryGraph, LeavesToRootValidator};
-use crate::proto;
+use super::{Directory, DirectoryGraph, LeavesToRootValidator};
 use crate::B3Digest;
 use crate::Error;
 use tonic::async_trait;
@@ -29,7 +28,7 @@ impl<DS: DirectoryService> SimplePutter<DS> {
 #[async_trait]
 impl<DS: DirectoryService + 'static> DirectoryPutter for SimplePutter<DS> {
     #[instrument(level = "trace", skip_all, fields(directory.digest=%directory.digest()), err)]
-    async fn put(&mut self, directory: proto::Directory) -> Result<(), Error> {
+    async fn put(&mut self, directory: Directory) -> Result<(), Error> {
         match self.directory_validator {
             None => return Err(Error::StorageError("already closed".to_string())),
             Some(ref mut validator) => {
diff --git a/tvix/castore/src/directoryservice/sled.rs b/tvix/castore/src/directoryservice/sled.rs
index 5766dec1a5c2..4f3a860d14e4 100644
--- a/tvix/castore/src/directoryservice/sled.rs
+++ b/tvix/castore/src/directoryservice/sled.rs
@@ -1,5 +1,3 @@
-use crate::proto::Directory;
-use crate::{proto, B3Digest, Error};
 use futures::stream::BoxStream;
 use prost::Message;
 use std::ops::Deref;
@@ -9,8 +7,9 @@ use tonic::async_trait;
 use tracing::{instrument, warn};
 
 use super::utils::traverse_directory;
-use super::{DirectoryGraph, DirectoryPutter, DirectoryService, LeavesToRootValidator};
+use super::{Directory, DirectoryGraph, DirectoryPutter, DirectoryService, LeavesToRootValidator};
 use crate::composition::{CompositionContext, ServiceBuilder};
+use crate::{proto, B3Digest, Error};
 
 #[derive(Clone)]
 pub struct SledDirectoryService {
@@ -44,7 +43,7 @@ impl SledDirectoryService {
 #[async_trait]
 impl DirectoryService for SledDirectoryService {
     #[instrument(skip(self, digest), fields(directory.digest = %digest))]
-    async fn get(&self, digest: &B3Digest) -> Result<Option<proto::Directory>, Error> {
+    async fn get(&self, digest: &B3Digest) -> Result<Option<Directory>, Error> {
         let resp = tokio::task::spawn_blocking({
             let db = self.db.clone();
             let digest = digest.clone();
@@ -61,7 +60,7 @@ impl DirectoryService for SledDirectoryService {
             None => Ok(None),
 
             // The directory was found, try to parse the data as Directory message
-            Some(data) => match Directory::decode(&*data) {
+            Some(data) => match proto::Directory::decode(&*data) {
                 Ok(directory) => {
                     // Validate the retrieved Directory indeed has the
                     // digest we expect it to have, to detect corruptions.
@@ -73,14 +72,10 @@ impl DirectoryService for SledDirectoryService {
                         )));
                     }
 
-                    // Validate the Directory itself is valid.
-                    if let Err(e) = directory.validate() {
-                        warn!("directory failed validation: {}", e.to_string());
-                        return Err(Error::StorageError(format!(
-                            "directory {} failed validation: {}",
-                            actual_digest, e,
-                        )));
-                    }
+                    let directory = directory.try_into().map_err(|e| {
+                        warn!("failed to retrieve directory: {}", e);
+                        Error::StorageError(format!("failed to retrieve directory: {}", e))
+                    })?;
 
                     Ok(Some(directory))
                 }
@@ -93,22 +88,18 @@ impl DirectoryService for SledDirectoryService {
     }
 
     #[instrument(skip(self, directory), fields(directory.digest = %directory.digest()))]
-    async fn put(&self, directory: proto::Directory) -> Result<B3Digest, Error> {
+    async fn put(&self, directory: Directory) -> Result<B3Digest, Error> {
         tokio::task::spawn_blocking({
             let db = self.db.clone();
             move || {
                 let digest = directory.digest();
 
-                // validate the directory itself.
-                if let Err(e) = directory.validate() {
-                    return Err(Error::InvalidRequest(format!(
-                        "directory {} failed validation: {}",
-                        digest, e,
-                    )));
-                }
                 // store it
-                db.insert(digest.as_slice(), directory.encode_to_vec())
-                    .map_err(|e| Error::StorageError(e.to_string()))?;
+                db.insert(
+                    digest.as_slice(),
+                    proto::Directory::from(directory).encode_to_vec(),
+                )
+                .map_err(|e| Error::StorageError(e.to_string()))?;
 
                 Ok(digest)
             }
@@ -120,7 +111,7 @@ impl DirectoryService for SledDirectoryService {
     fn get_recursive(
         &self,
         root_directory_digest: &B3Digest,
-    ) -> BoxStream<'static, Result<proto::Directory, Error>> {
+    ) -> BoxStream<'static, Result<Directory, Error>> {
         traverse_directory(self.clone(), root_directory_digest)
     }
 
@@ -215,7 +206,7 @@ pub struct SledDirectoryPutter {
 #[async_trait]
 impl DirectoryPutter for SledDirectoryPutter {
     #[instrument(level = "trace", skip_all, fields(directory.digest=%directory.digest()), err)]
-    async fn put(&mut self, directory: proto::Directory) -> Result<(), Error> {
+    async fn put(&mut self, directory: Directory) -> Result<(), Error> {
         match self.directory_validator {
             None => return Err(Error::StorageError("already closed".to_string())),
             Some(ref mut validator) => {
@@ -252,7 +243,10 @@ impl DirectoryPutter for SledDirectoryPutter {
 
                         let mut batch = sled::Batch::default();
                         for directory in directories {
-                            batch.insert(directory.digest().as_slice(), directory.encode_to_vec());
+                            batch.insert(
+                                directory.digest().as_slice(),
+                                proto::Directory::from(directory).encode_to_vec(),
+                            );
                         }
 
                         tree.apply_batch(batch).map_err(|e| {
diff --git a/tvix/castore/src/directoryservice/tests/mod.rs b/tvix/castore/src/directoryservice/tests/mod.rs
index b698f70ea469..ad189564bfe7 100644
--- a/tvix/castore/src/directoryservice/tests/mod.rs
+++ b/tvix/castore/src/directoryservice/tests/mod.rs
@@ -8,10 +8,8 @@ use rstest_reuse::{self, *};
 
 use super::DirectoryService;
 use crate::directoryservice;
-use crate::{
-    fixtures::{DIRECTORY_A, DIRECTORY_B, DIRECTORY_C, DIRECTORY_D},
-    proto::{self, Directory},
-};
+use crate::fixtures::{DIRECTORY_A, DIRECTORY_B, DIRECTORY_C, DIRECTORY_D};
+use crate::{Directory, Node};
 
 mod utils;
 use self::utils::make_grpc_directory_service_client;
@@ -41,10 +39,10 @@ async fn test_non_exist(directory_service: impl DirectoryService) {
 
     // recursive get
     assert_eq!(
-        Vec::<Result<proto::Directory, crate::Error>>::new(),
+        Vec::<Result<Directory, crate::Error>>::new(),
         directory_service
             .get_recursive(&DIRECTORY_A.digest())
-            .collect::<Vec<Result<proto::Directory, crate::Error>>>()
+            .collect::<Vec<Result<Directory, crate::Error>>>()
             .await
     );
 }
@@ -212,58 +210,20 @@ async fn upload_reject_dangling_pointer(directory_service: impl DirectoryService
     }
 }
 
-/// Try uploading a Directory failing its internal validation, ensure it gets
-/// rejected.
-#[apply(directory_services)]
-#[tokio::test]
-async fn upload_reject_failing_validation(directory_service: impl DirectoryService) {
-    let broken_directory = Directory {
-        symlinks: vec![proto::SymlinkNode {
-            name: "".into(), // wrong!
-            target: "doesntmatter".into(),
-        }],
-        ..Default::default()
-    };
-    assert!(broken_directory.validate().is_err());
-
-    // Try to upload via single upload.
-    assert!(
-        directory_service
-            .put(broken_directory.clone())
-            .await
-            .is_err(),
-        "single upload must fail"
-    );
-
-    // Try to upload via put_multiple. We're a bit more permissive here, the
-    // intermediate .put() might succeed, due to client-side bursting (in the
-    // case of gRPC), but then the close MUST fail.
-    let mut handle = directory_service.put_multiple_start();
-    if handle.put(broken_directory).await.is_ok() {
-        assert!(
-            handle.close().await.is_err(),
-            "when succeeding put, close must fail"
-        )
-    }
-}
-
 /// Try uploading a Directory that refers to a previously-uploaded directory.
 /// Both pass their isolated validation, but the size field in the parent is wrong.
 /// This should be rejected.
 #[apply(directory_services)]
 #[tokio::test]
 async fn upload_reject_wrong_size(directory_service: impl DirectoryService) {
-    let wrong_parent_directory = Directory {
-        directories: vec![proto::DirectoryNode {
-            name: "foo".into(),
-            digest: DIRECTORY_A.digest().into(),
+    let wrong_parent_directory = Directory::try_from_iter([(
+        "foo".try_into().unwrap(),
+        Node::Directory {
+            digest: DIRECTORY_A.digest(),
             size: DIRECTORY_A.size() + 42, // wrong!
-        }],
-        ..Default::default()
-    };
-
-    // Make sure isolated validation itself is ok
-    assert!(wrong_parent_directory.validate().is_ok());
+        },
+    )])
+    .unwrap();
 
     // Now upload both. Ensure it either fails during the second put, or during
     // the close.
diff --git a/tvix/castore/src/directoryservice/traverse.rs b/tvix/castore/src/directoryservice/traverse.rs
index 17a51ae2bbff..0bd67e9bcf1f 100644
--- a/tvix/castore/src/directoryservice/traverse.rs
+++ b/tvix/castore/src/directoryservice/traverse.rs
@@ -1,8 +1,4 @@
-use super::DirectoryService;
-use crate::{
-    proto::{node::Node, NamedNode},
-    B3Digest, Error, Path,
-};
+use crate::{directoryservice::DirectoryService, Error, Node, Path};
 use tracing::{instrument, warn};
 
 /// This descends from a (root) node to the given (sub)path, returning the Node
@@ -17,19 +13,14 @@ where
     DS: AsRef<dyn DirectoryService>,
 {
     let mut parent_node = root_node;
-    for component in path.as_ref().components() {
+    for component in path.as_ref().components_bytes() {
         match parent_node {
-            Node::File(_) | Node::Symlink(_) => {
+            Node::File { .. } | Node::Symlink { .. } => {
                 // There's still some path left, but the parent node is no directory.
                 // This means the path doesn't exist, as we can't reach it.
                 return Ok(None);
             }
-            Node::Directory(directory_node) => {
-                let digest: B3Digest = directory_node
-                    .digest
-                    .try_into()
-                    .map_err(|_e| Error::StorageError("invalid digest length".to_string()))?;
-
+            Node::Directory { digest, .. } => {
                 // fetch the linked node from the directory_service.
                 let directory =
                     directory_service
@@ -44,15 +35,16 @@ where
                         })?;
 
                 // look for the component in the [Directory].
-                // FUTUREWORK: as the nodes() iterator returns in a sorted fashion, we
-                // could stop as soon as e.name is larger than the search string.
-                if let Some(child_node) = directory.nodes().find(|n| n.get_name() == component) {
+                if let Some((_child_name, child_node)) = directory
+                    .into_nodes()
+                    .find(|(name, _node)| name.as_ref() == component)
+                {
                     // child node found, update prev_node to that and continue.
-                    parent_node = child_node;
+                    parent_node = child_node.clone();
                 } else {
                     // child node not found means there's no such element inside the directory.
                     return Ok(None);
-                }
+                };
             }
         }
     }
@@ -65,8 +57,8 @@ where
 mod tests {
     use crate::{
         directoryservice,
-        fixtures::{DIRECTORY_COMPLICATED, DIRECTORY_WITH_KEEP},
-        PathBuf,
+        fixtures::{DIRECTORY_COMPLICATED, DIRECTORY_WITH_KEEP, EMPTY_BLOB_DIGEST},
+        Node, PathBuf,
     };
 
     use super::descend_to;
@@ -88,21 +80,23 @@ mod tests {
         handle.close().await.expect("must upload");
 
         // construct the node for DIRECTORY_COMPLICATED
-        let node_directory_complicated =
-            crate::proto::node::Node::Directory(crate::proto::DirectoryNode {
-                name: "doesntmatter".into(),
-                digest: DIRECTORY_COMPLICATED.digest().into(),
-                size: DIRECTORY_COMPLICATED.size(),
-            });
+        let node_directory_complicated = Node::Directory {
+            digest: DIRECTORY_COMPLICATED.digest(),
+            size: DIRECTORY_COMPLICATED.size(),
+        };
 
         // construct the node for DIRECTORY_COMPLICATED
-        let node_directory_with_keep = crate::proto::node::Node::Directory(
-            DIRECTORY_COMPLICATED.directories.first().unwrap().clone(),
-        );
+        let node_directory_with_keep = Node::Directory {
+            digest: DIRECTORY_WITH_KEEP.digest(),
+            size: DIRECTORY_WITH_KEEP.size(),
+        };
 
         // construct the node for the .keep file
-        let node_file_keep =
-            crate::proto::node::Node::File(DIRECTORY_WITH_KEEP.files.first().unwrap().clone());
+        let node_file_keep = Node::File {
+            digest: EMPTY_BLOB_DIGEST.clone(),
+            size: 0,
+            executable: false,
+        };
 
         // traversal to an empty subpath should return the root node.
         {
diff --git a/tvix/castore/src/directoryservice/utils.rs b/tvix/castore/src/directoryservice/utils.rs
index 726734f55eec..d073c2c3c8ec 100644
--- a/tvix/castore/src/directoryservice/utils.rs
+++ b/tvix/castore/src/directoryservice/utils.rs
@@ -1,21 +1,22 @@
+use super::Directory;
 use super::DirectoryService;
-use crate::proto;
 use crate::B3Digest;
 use crate::Error;
+use crate::Node;
 use async_stream::try_stream;
 use futures::stream::BoxStream;
 use std::collections::{HashSet, VecDeque};
 use tracing::instrument;
 use tracing::warn;
 
-/// Traverses a [proto::Directory] from the root to the children.
+/// Traverses a [Directory] from the root to the children.
 ///
 /// This is mostly BFS, but directories are only returned once.
 #[instrument(skip(directory_service))]
 pub fn traverse_directory<'a, DS: DirectoryService + 'static>(
     directory_service: DS,
     root_directory_digest: &B3Digest,
-) -> BoxStream<'a, Result<proto::Directory, Error>> {
+) -> BoxStream<'a, Result<Directory, Error>> {
     // The list of all directories that still need to be traversed. The next
     // element is picked from the front, new elements are enqueued at the
     // back.
@@ -50,16 +51,6 @@ pub fn traverse_directory<'a, DS: DirectoryService + 'static>(
                 Some(dir) => dir,
             };
 
-
-            // validate, we don't want to send invalid directories.
-            current_directory.validate().map_err(|e| {
-               warn!("directory failed validation: {}", e.to_string());
-               Error::StorageError(format!(
-                   "invalid directory: {}",
-                   current_directory_digest
-               ))
-            })?;
-
             // We're about to send this directory, so let's avoid sending it again if a
             // descendant has it.
             sent_directory_digests.insert(current_directory_digest);
@@ -67,16 +58,15 @@ pub fn traverse_directory<'a, DS: DirectoryService + 'static>(
             // enqueue all child directory digests to the work queue, as
             // long as they're not part of the worklist or already sent.
             // This panics if the digest looks invalid, it's supposed to be checked first.
-            for child_directory_node in &current_directory.directories {
-                // TODO: propagate error
-                let child_digest: B3Digest = child_directory_node.digest.clone().try_into().unwrap();
-
-                if worklist_directory_digests.contains(&child_digest)
-                    || sent_directory_digests.contains(&child_digest)
-                {
-                    continue;
+            for (_, child_directory_node) in current_directory.nodes() {
+                if let Node::Directory{digest: child_digest, ..} = child_directory_node {
+                    if worklist_directory_digests.contains(child_digest)
+                        || sent_directory_digests.contains(child_digest)
+                    {
+                        continue;
+                    }
+                    worklist_directory_digests.push_back(child_digest.clone());
                 }
-                worklist_directory_digests.push_back(child_digest);
             }
 
             yield current_directory;
diff --git a/tvix/castore/src/errors.rs b/tvix/castore/src/errors.rs
index 5bbcd7b04ef1..7b5d1a422c99 100644
--- a/tvix/castore/src/errors.rs
+++ b/tvix/castore/src/errors.rs
@@ -1,7 +1,13 @@
+use bstr::ByteSlice;
 use thiserror::Error;
 use tokio::task::JoinError;
 use tonic::Status;
 
+use crate::{
+    path::{PathComponent, PathComponentError},
+    SymlinkTargetError,
+};
+
 /// Errors related to communication with the store.
 #[derive(Debug, Error, PartialEq)]
 pub enum Error {
@@ -12,6 +18,48 @@ pub enum Error {
     StorageError(String),
 }
 
+/// Errors that occur during construction of [crate::Node]
+#[derive(Debug, thiserror::Error, PartialEq)]
+pub enum ValidateNodeError {
+    /// Invalid digest length encountered
+    #[error("invalid digest length: {0}")]
+    InvalidDigestLen(usize),
+    /// Invalid symlink target
+    #[error("Invalid symlink target: {0}")]
+    InvalidSymlinkTarget(SymlinkTargetError),
+}
+
+impl From<crate::digests::Error> for ValidateNodeError {
+    fn from(e: crate::digests::Error) -> Self {
+        match e {
+            crate::digests::Error::InvalidDigestLen(n) => ValidateNodeError::InvalidDigestLen(n),
+        }
+    }
+}
+
+/// Errors that can occur when populating [crate::Directory] messages,
+/// or parsing [crate::proto::Directory]
+#[derive(Debug, thiserror::Error, PartialEq)]
+pub enum DirectoryError {
+    /// Multiple elements with the same name encountered
+    #[error("{:?} is a duplicate name", .0)]
+    DuplicateName(PathComponent),
+    /// Node failed validation
+    #[error("invalid node with name {}: {:?}", .0, .1.to_string())]
+    InvalidNode(PathComponent, ValidateNodeError),
+    #[error("Total size exceeds u64::MAX")]
+    SizeOverflow,
+    /// Invalid name encountered
+    #[error("Invalid name: {0}")]
+    InvalidName(PathComponentError),
+    /// Elements are not in sorted order. Can only happen on protos
+    #[error("{:?} is not sorted", .0.as_bstr())]
+    WrongSorting(bytes::Bytes),
+    /// This can only happen if there's an unknown node type (on protos)
+    #[error("No node set")]
+    NoNodeSet,
+}
+
 impl From<JoinError> for Error {
     fn from(value: JoinError) -> Self {
         Error::StorageError(value.to_string())
diff --git a/tvix/castore/src/fixtures.rs b/tvix/castore/src/fixtures.rs
index 3ebda64a818a..05bad916d55f 100644
--- a/tvix/castore/src/fixtures.rs
+++ b/tvix/castore/src/fixtures.rs
@@ -1,7 +1,4 @@
-use crate::{
-    proto::{self, Directory, DirectoryNode, FileNode, SymlinkNode},
-    B3Digest,
-};
+use crate::{B3Digest, Directory, Node};
 use lazy_static::lazy_static;
 
 pub const HELLOWORLD_BLOB_CONTENTS: &[u8] = b"Hello World!";
@@ -34,70 +31,74 @@ lazy_static! {
     pub static ref BLOB_B_DIGEST: B3Digest = blake3::hash(&BLOB_B).as_bytes().into();
 
     // Directories
-    pub static ref DIRECTORY_WITH_KEEP: proto::Directory = proto::Directory {
-        directories: vec![],
-        files: vec![FileNode {
-            name: b".keep".to_vec().into(),
-            digest: EMPTY_BLOB_DIGEST.clone().into(),
+    pub static ref DIRECTORY_WITH_KEEP: Directory = Directory::try_from_iter([(
+        ".keep".try_into().unwrap(),
+        Node::File{
+            digest: EMPTY_BLOB_DIGEST.clone(),
             size: 0,
-            executable: false,
-        }],
-        symlinks: vec![],
-    };
-    pub static ref DIRECTORY_COMPLICATED: proto::Directory = proto::Directory {
-        directories: vec![DirectoryNode {
-            name: b"keep".to_vec().into(),
-            digest: DIRECTORY_WITH_KEEP.digest().into(),
-            size: DIRECTORY_WITH_KEEP.size(),
-        }],
-        files: vec![FileNode {
-            name: b".keep".to_vec().into(),
-            digest: EMPTY_BLOB_DIGEST.clone().into(),
-            size: 0,
-            executable: false,
-        }],
-        symlinks: vec![SymlinkNode {
-            name: b"aa".to_vec().into(),
-            target: b"/nix/store/somewhereelse".to_vec().into(),
-        }],
-    };
-    pub static ref DIRECTORY_A: Directory = Directory::default();
-    pub static ref DIRECTORY_B: Directory = Directory {
-        directories: vec![DirectoryNode {
-            name: b"a".to_vec().into(),
-            digest: DIRECTORY_A.digest().into(),
-            size: DIRECTORY_A.size(),
-        }],
-        ..Default::default()
-    };
-    pub static ref DIRECTORY_C: Directory = Directory {
-        directories: vec![
-            DirectoryNode {
-                name: b"a".to_vec().into(),
-                digest: DIRECTORY_A.digest().into(),
+            executable: false
+    })]).unwrap();
+    pub static ref DIRECTORY_COMPLICATED: Directory = Directory::try_from_iter([
+        (
+            "keep".try_into().unwrap(),
+            Node::Directory{
+                digest: DIRECTORY_WITH_KEEP.digest(),
+                size: DIRECTORY_WITH_KEEP.size()
+            }
+        ),
+        (
+            ".keep".try_into().unwrap(),
+            Node::File{
+                digest: EMPTY_BLOB_DIGEST.clone(),
+                size: 0,
+                executable: false
+            }
+        ),
+        (
+            "aa".try_into().unwrap(),
+            Node::Symlink{
+                target: "/nix/store/somewhereelse".try_into().unwrap()
+            }
+        )
+    ]).unwrap();
+    pub static ref DIRECTORY_A: Directory = Directory::new();
+    pub static ref DIRECTORY_B: Directory = Directory::try_from_iter([(
+            "a".try_into().unwrap(),
+            Node::Directory{
+                digest: DIRECTORY_A.digest(),
                 size: DIRECTORY_A.size(),
-            },
-            DirectoryNode {
-                name: b"a'".to_vec().into(),
-                digest: DIRECTORY_A.digest().into(),
+            }
+    )]).unwrap();
+    pub static ref DIRECTORY_C: Directory = Directory::try_from_iter([
+        (
+            "a".try_into().unwrap(),
+            Node::Directory{
+                digest: DIRECTORY_A.digest(),
                 size: DIRECTORY_A.size(),
             }
-        ],
-        ..Default::default()
-    };
-    pub static ref DIRECTORY_D: proto::Directory = proto::Directory {
-        directories: vec![
-            DirectoryNode {
-                name: b"a".to_vec().into(),
-                digest: DIRECTORY_A.digest().into(),
+        ),
+        (
+            "a'".try_into().unwrap(),
+            Node::Directory{
+                digest: DIRECTORY_A.digest(),
+                size: DIRECTORY_A.size(),
+            }
+        )
+    ]).unwrap();
+    pub static ref DIRECTORY_D: Directory = Directory::try_from_iter([
+        (
+            "a".try_into().unwrap(),
+            Node::Directory{
+                digest: DIRECTORY_A.digest(),
                 size: DIRECTORY_A.size(),
-            },
-            DirectoryNode {
-                name: b"b'".to_vec().into(),
-                digest: DIRECTORY_B.digest().into(),
+            }
+        ),
+        (
+            "b".try_into().unwrap(),
+            Node::Directory{
+                digest: DIRECTORY_B.digest(),
                 size: DIRECTORY_B.size(),
             }
-        ],
-        ..Default::default()
-    };
+        )
+    ]).unwrap();
 }
diff --git a/tvix/castore/src/fs/fuse/tests.rs b/tvix/castore/src/fs/fuse/tests.rs
index bcebcf4a7292..9e01204d5da7 100644
--- a/tvix/castore/src/fs/fuse/tests.rs
+++ b/tvix/castore/src/fs/fuse/tests.rs
@@ -1,5 +1,4 @@
 use bstr::ByteSlice;
-use bytes::Bytes;
 use std::{
     collections::BTreeMap,
     ffi::{OsStr, OsString},
@@ -12,13 +11,14 @@ use tempfile::TempDir;
 use tokio_stream::{wrappers::ReadDirStream, StreamExt};
 
 use super::FuseDaemon;
-use crate::fs::{TvixStoreFs, XATTR_NAME_BLOB_DIGEST, XATTR_NAME_DIRECTORY_DIGEST};
-use crate::proto as castorepb;
-use crate::proto::node::Node;
 use crate::{
     blobservice::{BlobService, MemoryBlobService},
     directoryservice::{DirectoryService, MemoryDirectoryService},
-    fixtures,
+    fixtures, Node,
+};
+use crate::{
+    fs::{TvixStoreFs, XATTR_NAME_BLOB_DIGEST, XATTR_NAME_DIRECTORY_DIGEST},
+    PathComponent,
 };
 
 const BLOB_A_NAME: &str = "00000000000000000000000000000000-test";
@@ -39,7 +39,7 @@ fn gen_svcs() -> (Arc<dyn BlobService>, Arc<dyn DirectoryService>) {
 fn do_mount<P: AsRef<Path>, BS, DS>(
     blob_service: BS,
     directory_service: DS,
-    root_nodes: BTreeMap<bytes::Bytes, Node>,
+    root_nodes: BTreeMap<PathComponent, Node>,
     mountpoint: P,
     list_root: bool,
     show_xattr: bool,
@@ -60,7 +60,7 @@ where
 
 async fn populate_blob_a(
     blob_service: &Arc<dyn BlobService>,
-    root_nodes: &mut BTreeMap<Bytes, Node>,
+    root_nodes: &mut BTreeMap<PathComponent, Node>,
 ) {
     let mut bw = blob_service.open_write().await;
     tokio::io::copy(&mut Cursor::new(fixtures::BLOB_A.to_vec()), &mut bw)
@@ -69,19 +69,18 @@ async fn populate_blob_a(
     bw.close().await.expect("must succeed closing");
 
     root_nodes.insert(
-        BLOB_A_NAME.into(),
-        Node::File(castorepb::FileNode {
-            name: BLOB_A_NAME.into(),
-            digest: fixtures::BLOB_A_DIGEST.clone().into(),
+        BLOB_A_NAME.try_into().unwrap(),
+        Node::File {
+            digest: fixtures::BLOB_A_DIGEST.clone(),
             size: fixtures::BLOB_A.len() as u64,
             executable: false,
-        }),
+        },
     );
 }
 
 async fn populate_blob_b(
     blob_service: &Arc<dyn BlobService>,
-    root_nodes: &mut BTreeMap<Bytes, Node>,
+    root_nodes: &mut BTreeMap<PathComponent, Node>,
 ) {
     let mut bw = blob_service.open_write().await;
     tokio::io::copy(&mut Cursor::new(fixtures::BLOB_B.to_vec()), &mut bw)
@@ -90,20 +89,19 @@ async fn populate_blob_b(
     bw.close().await.expect("must succeed closing");
 
     root_nodes.insert(
-        BLOB_B_NAME.into(),
-        Node::File(castorepb::FileNode {
-            name: BLOB_B_NAME.into(),
-            digest: fixtures::BLOB_B_DIGEST.clone().into(),
+        BLOB_B_NAME.try_into().unwrap(),
+        Node::File {
+            digest: fixtures::BLOB_B_DIGEST.clone(),
             size: fixtures::BLOB_B.len() as u64,
             executable: false,
-        }),
+        },
     );
 }
 
 /// adds a blob containing helloworld and marks it as executable
 async fn populate_blob_helloworld(
     blob_service: &Arc<dyn BlobService>,
-    root_nodes: &mut BTreeMap<Bytes, Node>,
+    root_nodes: &mut BTreeMap<PathComponent, Node>,
 ) {
     let mut bw = blob_service.open_write().await;
     tokio::io::copy(
@@ -115,42 +113,39 @@ async fn populate_blob_helloworld(
     bw.close().await.expect("must succeed closing");
 
     root_nodes.insert(
-        HELLOWORLD_BLOB_NAME.into(),
-        Node::File(castorepb::FileNode {
-            name: HELLOWORLD_BLOB_NAME.into(),
-            digest: fixtures::HELLOWORLD_BLOB_DIGEST.clone().into(),
+        HELLOWORLD_BLOB_NAME.try_into().unwrap(),
+        Node::File {
+            digest: fixtures::HELLOWORLD_BLOB_DIGEST.clone(),
             size: fixtures::HELLOWORLD_BLOB_CONTENTS.len() as u64,
             executable: true,
-        }),
+        },
     );
 }
 
-async fn populate_symlink(root_nodes: &mut BTreeMap<Bytes, Node>) {
+async fn populate_symlink(root_nodes: &mut BTreeMap<PathComponent, Node>) {
     root_nodes.insert(
-        SYMLINK_NAME.into(),
-        Node::Symlink(castorepb::SymlinkNode {
-            name: SYMLINK_NAME.into(),
-            target: BLOB_A_NAME.into(),
-        }),
+        SYMLINK_NAME.try_into().unwrap(),
+        Node::Symlink {
+            target: BLOB_A_NAME.try_into().unwrap(),
+        },
     );
 }
 
 /// This writes a symlink pointing to /nix/store/somewhereelse,
 /// which is the same symlink target as "aa" inside DIRECTORY_COMPLICATED.
-async fn populate_symlink2(root_nodes: &mut BTreeMap<Bytes, Node>) {
+async fn populate_symlink2(root_nodes: &mut BTreeMap<PathComponent, Node>) {
     root_nodes.insert(
-        SYMLINK_NAME2.into(),
-        Node::Symlink(castorepb::SymlinkNode {
-            name: SYMLINK_NAME2.into(),
-            target: "/nix/store/somewhereelse".into(),
-        }),
+        SYMLINK_NAME2.try_into().unwrap(),
+        Node::Symlink {
+            target: "/nix/store/somewhereelse".try_into().unwrap(),
+        },
     );
 }
 
 async fn populate_directory_with_keep(
     blob_service: &Arc<dyn BlobService>,
     directory_service: &Arc<dyn DirectoryService>,
-    root_nodes: &mut BTreeMap<Bytes, Node>,
+    root_nodes: &mut BTreeMap<PathComponent, Node>,
 ) {
     // upload empty blob
     let mut bw = blob_service.open_write().await;
@@ -166,45 +161,42 @@ async fn populate_directory_with_keep(
         .expect("must succeed uploading");
 
     root_nodes.insert(
-        DIRECTORY_WITH_KEEP_NAME.into(),
-        castorepb::node::Node::Directory(castorepb::DirectoryNode {
-            name: DIRECTORY_WITH_KEEP_NAME.into(),
-            digest: fixtures::DIRECTORY_WITH_KEEP.digest().into(),
+        DIRECTORY_WITH_KEEP_NAME.try_into().unwrap(),
+        Node::Directory {
+            digest: fixtures::DIRECTORY_WITH_KEEP.digest(),
             size: fixtures::DIRECTORY_WITH_KEEP.size(),
-        }),
+        },
     );
 }
 
 /// Create a root node for DIRECTORY_WITH_KEEP, but don't upload the Directory
 /// itself.
-async fn populate_directorynode_without_directory(root_nodes: &mut BTreeMap<Bytes, Node>) {
+async fn populate_directorynode_without_directory(root_nodes: &mut BTreeMap<PathComponent, Node>) {
     root_nodes.insert(
-        DIRECTORY_WITH_KEEP_NAME.into(),
-        castorepb::node::Node::Directory(castorepb::DirectoryNode {
-            name: DIRECTORY_WITH_KEEP_NAME.into(),
-            digest: fixtures::DIRECTORY_WITH_KEEP.digest().into(),
+        DIRECTORY_WITH_KEEP_NAME.try_into().unwrap(),
+        Node::Directory {
+            digest: fixtures::DIRECTORY_WITH_KEEP.digest(),
             size: fixtures::DIRECTORY_WITH_KEEP.size(),
-        }),
+        },
     );
 }
 
 /// Insert BLOB_A, but don't provide the blob .keep is pointing to.
-async fn populate_filenode_without_blob(root_nodes: &mut BTreeMap<Bytes, Node>) {
+async fn populate_filenode_without_blob(root_nodes: &mut BTreeMap<PathComponent, Node>) {
     root_nodes.insert(
-        BLOB_A_NAME.into(),
-        Node::File(castorepb::FileNode {
-            name: BLOB_A_NAME.into(),
-            digest: fixtures::BLOB_A_DIGEST.clone().into(),
+        BLOB_A_NAME.try_into().unwrap(),
+        Node::File {
+            digest: fixtures::BLOB_A_DIGEST.clone(),
             size: fixtures::BLOB_A.len() as u64,
             executable: false,
-        }),
+        },
     );
 }
 
 async fn populate_directory_complicated(
     blob_service: &Arc<dyn BlobService>,
     directory_service: &Arc<dyn DirectoryService>,
-    root_nodes: &mut BTreeMap<Bytes, Node>,
+    root_nodes: &mut BTreeMap<PathComponent, Node>,
 ) {
     // upload empty blob
     let mut bw = blob_service.open_write().await;
@@ -226,12 +218,11 @@ async fn populate_directory_complicated(
         .expect("must succeed uploading");
 
     root_nodes.insert(
-        DIRECTORY_COMPLICATED_NAME.into(),
-        Node::Directory(castorepb::DirectoryNode {
-            name: DIRECTORY_COMPLICATED_NAME.into(),
-            digest: fixtures::DIRECTORY_COMPLICATED.digest().into(),
+        DIRECTORY_COMPLICATED_NAME.try_into().unwrap(),
+        Node::Directory {
+            digest: fixtures::DIRECTORY_COMPLICATED.digest(),
             size: fixtures::DIRECTORY_COMPLICATED.size(),
-        }),
+        },
     );
 }
 
diff --git a/tvix/castore/src/fs/inodes.rs b/tvix/castore/src/fs/inodes.rs
index bdd459543470..2696fdede378 100644
--- a/tvix/castore/src/fs/inodes.rs
+++ b/tvix/castore/src/fs/inodes.rs
@@ -2,10 +2,7 @@
 //! about inodes, which present tvix-castore nodes in a filesystem.
 use std::time::Duration;
 
-use bytes::Bytes;
-
-use crate::proto as castorepb;
-use crate::B3Digest;
+use crate::{path::PathComponent, B3Digest, Node};
 
 #[derive(Clone, Debug)]
 pub enum InodeData {
@@ -20,27 +17,23 @@ pub enum InodeData {
 /// lookup and did fetch the data.
 #[derive(Clone, Debug)]
 pub enum DirectoryInodeData {
-    Sparse(B3Digest, u64),                                  // digest, size
-    Populated(B3Digest, Vec<(u64, castorepb::node::Node)>), // [(child_inode, node)]
+    Sparse(B3Digest, u64),                                // digest, size
+    Populated(B3Digest, Vec<(u64, PathComponent, Node)>), // [(child_inode, name, node)]
 }
 
 impl InodeData {
     /// Constructs a new InodeData by consuming a [Node].
-    /// It splits off the orginal name, so it can be used later.
-    pub fn from_node(node: castorepb::node::Node) -> (Self, Bytes) {
+    pub fn from_node(node: &Node) -> Self {
         match node {
-            castorepb::node::Node::Directory(n) => (
-                Self::Directory(DirectoryInodeData::Sparse(
-                    n.digest.try_into().unwrap(),
-                    n.size,
-                )),
-                n.name,
-            ),
-            castorepb::node::Node::File(n) => (
-                Self::Regular(n.digest.try_into().unwrap(), n.size, n.executable),
-                n.name,
-            ),
-            castorepb::node::Node::Symlink(n) => (Self::Symlink(n.target), n.name),
+            Node::Directory { digest, size } => {
+                Self::Directory(DirectoryInodeData::Sparse(digest.clone(), *size))
+            }
+            Node::File {
+                digest,
+                size,
+                executable,
+            } => Self::Regular(digest.clone(), *size, *executable),
+            Node::Symlink { target } => Self::Symlink(target.clone().into()),
         }
     }
 
diff --git a/tvix/castore/src/fs/mod.rs b/tvix/castore/src/fs/mod.rs
index b565ed60ac42..e700a25d3966 100644
--- a/tvix/castore/src/fs/mod.rs
+++ b/tvix/castore/src/fs/mod.rs
@@ -15,15 +15,13 @@ use self::{
     inode_tracker::InodeTracker,
     inodes::{DirectoryInodeData, InodeData},
 };
-use crate::proto as castorepb;
 use crate::{
     blobservice::{BlobReader, BlobService},
     directoryservice::DirectoryService,
-    proto::{node::Node, NamedNode},
-    B3Digest,
+    path::PathComponent,
+    B3Digest, Node,
 };
 use bstr::ByteVec;
-use bytes::Bytes;
 use fuse_backend_rs::abi::fuse_abi::{stat64, OpenOptions};
 use fuse_backend_rs::api::filesystem::{
     Context, FileSystem, FsOptions, GetxattrReply, ListxattrReply, ROOT_ID,
@@ -89,7 +87,7 @@ pub struct TvixStoreFs<BS, DS, RN> {
     show_xattr: bool,
 
     /// This maps a given basename in the root to the inode we allocated for the node.
-    root_nodes: RwLock<HashMap<Bytes, u64>>,
+    root_nodes: RwLock<HashMap<PathComponent, u64>>,
 
     /// This keeps track of inodes and data alongside them.
     inode_tracker: RwLock<InodeTracker>,
@@ -105,7 +103,7 @@ pub struct TvixStoreFs<BS, DS, RN> {
             u64,
             (
                 Span,
-                Arc<Mutex<mpsc::Receiver<(usize, Result<Node, crate::Error>)>>>,
+                Arc<Mutex<mpsc::Receiver<(usize, Result<(PathComponent, Node), crate::Error>)>>>,
             ),
         >,
     >,
@@ -156,7 +154,7 @@ where
 
     /// Retrieves the inode for a given root node basename, if present.
     /// This obtains a read lock on self.root_nodes.
-    fn get_inode_for_root_name(&self, name: &[u8]) -> Option<u64> {
+    fn get_inode_for_root_name(&self, name: &PathComponent) -> Option<u64> {
         self.root_nodes.read().get(name).cloned()
     }
 
@@ -167,8 +165,12 @@ where
     /// It is ok if it's a [DirectoryInodeData::Sparse] - in that case, a lookup
     /// in self.directory_service is performed, and self.inode_tracker is updated with the
     /// [DirectoryInodeData::Populated].
+    #[allow(clippy::type_complexity)]
     #[instrument(skip(self), err)]
-    fn get_directory_children(&self, ino: u64) -> io::Result<(B3Digest, Vec<(u64, Node)>)> {
+    fn get_directory_children(
+        &self,
+        ino: u64,
+    ) -> io::Result<(B3Digest, Vec<(u64, PathComponent, Node)>)> {
         let data = self.inode_tracker.read().get(ino).unwrap();
         match *data {
             // if it's populated already, return children.
@@ -198,13 +200,13 @@ where
                 let children = {
                     let mut inode_tracker = self.inode_tracker.write();
 
-                    let children: Vec<(u64, castorepb::node::Node)> = directory
-                        .nodes()
-                        .map(|child_node| {
-                            let (inode_data, _) = InodeData::from_node(child_node.clone());
+                    let children: Vec<(u64, PathComponent, Node)> = directory
+                        .into_nodes()
+                        .map(|(child_name, child_node)| {
+                            let inode_data = InodeData::from_node(&child_node);
 
                             let child_ino = inode_tracker.put(inode_data);
-                            (child_ino, child_node)
+                            (child_ino, child_name, child_node)
                         })
                         .collect();
 
@@ -238,12 +240,12 @@ where
     /// In the case the name can't be found, a libc::ENOENT is returned.
     fn name_in_root_to_ino_and_data(
         &self,
-        name: &std::ffi::CStr,
+        name: &PathComponent,
     ) -> io::Result<(u64, Arc<InodeData>)> {
         // Look up the inode for that root node.
         // If there's one, [self.inode_tracker] MUST also contain the data,
         // which we can then return.
-        if let Some(inode) = self.get_inode_for_root_name(name.to_bytes()) {
+        if let Some(inode) = self.get_inode_for_root_name(name) {
             return Ok((
                 inode,
                 self.inode_tracker
@@ -257,7 +259,8 @@ where
         // We don't have it yet, look it up in [self.root_nodes].
         match self.tokio_handle.block_on({
             let root_nodes_provider = self.root_nodes_provider.clone();
-            async move { root_nodes_provider.get_by_basename(name.to_bytes()).await }
+            let name = name.clone();
+            async move { root_nodes_provider.get_by_basename(&name).await }
         }) {
             // if there was an error looking up the root node, propagate up an IO error.
             Err(_e) => Err(io::Error::from_raw_os_error(libc::EIO)),
@@ -265,15 +268,9 @@ where
             Ok(None) => Err(io::Error::from_raw_os_error(libc::ENOENT)),
             // The root node does exist
             Ok(Some(root_node)) => {
-                // The name must match what's passed in the lookup, otherwise this is also a ENOENT.
-                if root_node.get_name() != name.to_bytes() {
-                    debug!(root_node.name=?root_node.get_name(), found_node.name=%name.to_string_lossy(), "node name mismatch");
-                    return Err(io::Error::from_raw_os_error(libc::ENOENT));
-                }
-
                 // Let's check if someone else beat us to updating the inode tracker and
                 // root_nodes map. This avoids locking inode_tracker for writing.
-                if let Some(ino) = self.root_nodes.read().get(name.to_bytes()) {
+                if let Some(ino) = self.root_nodes.read().get(name) {
                     return Ok((
                         *ino,
                         self.inode_tracker.read().get(*ino).expect("must exist"),
@@ -287,9 +284,9 @@ where
 
                 // insert the (sparse) inode data and register in
                 // self.root_nodes.
-                let (inode_data, name) = InodeData::from_node(root_node);
+                let inode_data = InodeData::from_node(&root_node);
                 let ino = inode_tracker.put(inode_data.clone());
-                root_nodes.insert(name, ino);
+                root_nodes.insert(name.to_owned(), ino);
 
                 Ok((ino, Arc::new(inode_data)))
             }
@@ -345,13 +342,17 @@ where
     ) -> io::Result<fuse_backend_rs::api::filesystem::Entry> {
         debug!("lookup");
 
+        // convert the CStr to a PathComponent
+        // If it can't be converted, we definitely don't have anything here.
+        let name: PathComponent = name.try_into().map_err(|_| std::io::ErrorKind::NotFound)?;
+
         // This goes from a parent inode to a node.
         // - If the parent is [ROOT_ID], we need to check
         //   [self.root_nodes] (fetching from a [RootNode] provider if needed)
         // - Otherwise, lookup the parent in [self.inode_tracker] (which must be
         //   a [InodeData::Directory]), and find the child with that name.
         if parent == ROOT_ID {
-            let (ino, inode_data) = self.name_in_root_to_ino_and_data(name)?;
+            let (ino, inode_data) = self.name_in_root_to_ino_and_data(&name)?;
 
             debug!(inode_data=?&inode_data, ino=ino, "Some");
             return Ok(inode_data.as_fuse_entry(ino));
@@ -364,7 +365,7 @@ where
         // Search for that name in the list of children and return the FileAttrs.
 
         // in the children, find the one with the desired name.
-        if let Some((child_ino, _)) = children.iter().find(|e| e.1.get_name() == name.to_bytes()) {
+        if let Some((child_ino, _, _)) = children.iter().find(|(_, n, _)| n == &name) {
             // lookup the child [InodeData] in [self.inode_tracker].
             // We know the inodes for children have already been allocated.
             let child_inode_data = self.inode_tracker.read().get(*child_ino).unwrap();
@@ -400,8 +401,8 @@ where
             self.tokio_handle.spawn(
                 async move {
                     let mut stream = root_nodes_provider.list().enumerate();
-                    while let Some(node) = stream.next().await {
-                        if tx.send(node).await.is_err() {
+                    while let Some(e) = stream.next().await {
+                        if tx.send(e).await.is_err() {
                             // If we get a send error, it means the sync code
                             // doesn't want any more entries.
                             break;
@@ -463,12 +464,12 @@ where
                 .map_err(|_| crate::Error::StorageError("mutex poisoned".into()))?;
 
             while let Some((i, n)) = rx.blocking_recv() {
-                let root_node = n.map_err(|e| {
+                let (name, node) = n.map_err(|e| {
                     warn!("failed to retrieve root node: {}", e);
                     io::Error::from_raw_os_error(libc::EIO)
                 })?;
 
-                let (inode_data, name) = InodeData::from_node(root_node);
+                let inode_data = InodeData::from_node(&node);
 
                 // obtain the inode, or allocate a new one.
                 let ino = self.get_inode_for_root_name(&name).unwrap_or_else(|| {
@@ -483,7 +484,7 @@ where
                     ino,
                     offset: offset + (i as u64) + 1,
                     type_: inode_data.as_fuse_type(),
-                    name: &name,
+                    name: name.as_ref(),
                 })?;
                 // If the buffer is full, add_entry will return `Ok(0)`.
                 if written == 0 {
@@ -497,15 +498,17 @@ where
         let (parent_digest, children) = self.get_directory_children(inode)?;
         Span::current().record("directory.digest", parent_digest.to_string());
 
-        for (i, (ino, child_node)) in children.into_iter().skip(offset as usize).enumerate() {
-            let (inode_data, name) = InodeData::from_node(child_node);
+        for (i, (ino, child_name, child_node)) in
+            children.into_iter().skip(offset as usize).enumerate()
+        {
+            let inode_data = InodeData::from_node(&child_node);
 
             // the second parameter will become the "offset" parameter on the next call.
             let written = add_entry(fuse_backend_rs::api::filesystem::DirEntry {
                 ino,
                 offset: offset + (i as u64) + 1,
                 type_: inode_data.as_fuse_type(),
-                name: &name,
+                name: child_name.as_ref(),
             })?;
             // If the buffer is full, add_entry will return `Ok(0)`.
             if written == 0 {
@@ -550,12 +553,12 @@ where
                 .map_err(|_| crate::Error::StorageError("mutex poisoned".into()))?;
 
             while let Some((i, n)) = rx.blocking_recv() {
-                let root_node = n.map_err(|e| {
+                let (name, node) = n.map_err(|e| {
                     warn!("failed to retrieve root node: {}", e);
                     io::Error::from_raw_os_error(libc::EPERM)
                 })?;
 
-                let (inode_data, name) = InodeData::from_node(root_node);
+                let inode_data = InodeData::from_node(&node);
 
                 // obtain the inode, or allocate a new one.
                 let ino = self.get_inode_for_root_name(&name).unwrap_or_else(|| {
@@ -571,7 +574,7 @@ where
                         ino,
                         offset: offset + (i as u64) + 1,
                         type_: inode_data.as_fuse_type(),
-                        name: &name,
+                        name: name.as_ref(),
                     },
                     inode_data.as_fuse_entry(ino),
                 )?;
@@ -587,8 +590,8 @@ where
         let (parent_digest, children) = self.get_directory_children(inode)?;
         Span::current().record("directory.digest", parent_digest.to_string());
 
-        for (i, (ino, child_node)) in children.into_iter().skip(offset as usize).enumerate() {
-            let (inode_data, name) = InodeData::from_node(child_node);
+        for (i, (ino, name, child_node)) in children.into_iter().skip(offset as usize).enumerate() {
+            let inode_data = InodeData::from_node(&child_node);
 
             // the second parameter will become the "offset" parameter on the next call.
             let written = add_entry(
@@ -596,7 +599,7 @@ where
                     ino,
                     offset: offset + (i as u64) + 1,
                     type_: inode_data.as_fuse_type(),
-                    name: &name,
+                    name: name.as_ref(),
                 },
                 inode_data.as_fuse_entry(ino),
             )?;
diff --git a/tvix/castore/src/fs/root_nodes.rs b/tvix/castore/src/fs/root_nodes.rs
index 6609e049a1fc..5ed1a4d8d6c0 100644
--- a/tvix/castore/src/fs/root_nodes.rs
+++ b/tvix/castore/src/fs/root_nodes.rs
@@ -1,7 +1,6 @@
 use std::collections::BTreeMap;
 
-use crate::{proto::node::Node, Error};
-use bytes::Bytes;
+use crate::{path::PathComponent, Error, Node};
 use futures::stream::BoxStream;
 use tonic::async_trait;
 
@@ -11,11 +10,12 @@ use tonic::async_trait;
 pub trait RootNodes: Send + Sync {
     /// Looks up a root CA node based on the basename of the node in the root
     /// directory of the filesystem.
-    async fn get_by_basename(&self, name: &[u8]) -> Result<Option<Node>, Error>;
+    async fn get_by_basename(&self, name: &PathComponent) -> Result<Option<Node>, Error>;
 
-    /// Lists all root CA nodes in the filesystem. An error can be returned
-    /// in case listing is not allowed
-    fn list(&self) -> BoxStream<Result<Node, Error>>;
+    /// Lists all root CA nodes in the filesystem, as a tuple of (base)name
+    /// and Node.
+    /// An error can be returned in case listing is not allowed.
+    fn list(&self) -> BoxStream<Result<(PathComponent, Node), Error>>;
 }
 
 #[async_trait]
@@ -23,15 +23,17 @@ pub trait RootNodes: Send + Sync {
 /// the key is the node name.
 impl<T> RootNodes for T
 where
-    T: AsRef<BTreeMap<Bytes, Node>> + Send + Sync,
+    T: AsRef<BTreeMap<PathComponent, Node>> + Send + Sync,
 {
-    async fn get_by_basename(&self, name: &[u8]) -> Result<Option<Node>, Error> {
+    async fn get_by_basename(&self, name: &PathComponent) -> Result<Option<Node>, Error> {
         Ok(self.as_ref().get(name).cloned())
     }
 
-    fn list(&self) -> BoxStream<Result<Node, Error>> {
+    fn list(&self) -> BoxStream<Result<(PathComponent, Node), Error>> {
         Box::pin(tokio_stream::iter(
-            self.as_ref().iter().map(|(_, v)| Ok(v.clone())),
+            self.as_ref()
+                .iter()
+                .map(|(name, node)| Ok((name.to_owned(), node.to_owned()))),
         ))
     }
 }
diff --git a/tvix/castore/src/import/archive.rs b/tvix/castore/src/import/archive.rs
index cd5b1290e031..167f799efa0f 100644
--- a/tvix/castore/src/import/archive.rs
+++ b/tvix/castore/src/import/archive.rs
@@ -13,7 +13,7 @@ use tracing::{instrument, warn, Level};
 use crate::blobservice::BlobService;
 use crate::directoryservice::DirectoryService;
 use crate::import::{ingest_entries, IngestionEntry, IngestionError};
-use crate::proto::node::Node;
+use crate::Node;
 
 use super::blobs::{self, ConcurrentBlobUploader};
 
diff --git a/tvix/castore/src/import/fs.rs b/tvix/castore/src/import/fs.rs
index dc7821b8101e..1332fdfe57b5 100644
--- a/tvix/castore/src/import/fs.rs
+++ b/tvix/castore/src/import/fs.rs
@@ -16,8 +16,7 @@ use walkdir::WalkDir;
 
 use crate::blobservice::BlobService;
 use crate::directoryservice::DirectoryService;
-use crate::proto::node::Node;
-use crate::B3Digest;
+use crate::{B3Digest, Node};
 
 use super::ingest_entries;
 use super::IngestionEntry;
diff --git a/tvix/castore/src/import/mod.rs b/tvix/castore/src/import/mod.rs
index a9ac0be6b064..6e10a64939a4 100644
--- a/tvix/castore/src/import/mod.rs
+++ b/tvix/castore/src/import/mod.rs
@@ -4,15 +4,9 @@
 //! Specific implementations, such as ingesting from the filesystem, live in
 //! child modules.
 
-use crate::directoryservice::DirectoryPutter;
-use crate::directoryservice::DirectoryService;
+use crate::directoryservice::{DirectoryPutter, DirectoryService};
 use crate::path::{Path, PathBuf};
-use crate::proto::node::Node;
-use crate::proto::Directory;
-use crate::proto::DirectoryNode;
-use crate::proto::FileNode;
-use crate::proto::SymlinkNode;
-use crate::B3Digest;
+use crate::{B3Digest, Directory, Node, SymlinkTargetError};
 use futures::{Stream, StreamExt};
 use tracing::Level;
 
@@ -65,14 +59,6 @@ where
             // we break the loop manually.
             .expect("Tvix bug: unexpected end of stream")?;
 
-        let name = entry
-            .path()
-            .file_name()
-            // If this is the root node, it will have an empty name.
-            .unwrap_or_default()
-            .to_owned()
-            .into();
-
         let node = match &mut entry {
             IngestionEntry::Dir { .. } => {
                 // If the entry is a directory, we traversed all its children (and
@@ -98,27 +84,31 @@ where
                         IngestionError::UploadDirectoryError(entry.path().to_owned(), e)
                     })?;
 
-                Node::Directory(DirectoryNode {
-                    name,
-                    digest: directory_digest.into(),
+                Node::Directory {
+                    digest: directory_digest,
                     size: directory_size,
-                })
+                }
             }
-            IngestionEntry::Symlink { ref target, .. } => Node::Symlink(SymlinkNode {
-                name,
-                target: target.to_owned().into(),
-            }),
+            IngestionEntry::Symlink { ref target, .. } => Node::Symlink {
+                target: bytes::Bytes::copy_from_slice(target).try_into().map_err(
+                    |e: SymlinkTargetError| {
+                        IngestionError::UploadDirectoryError(
+                            entry.path().to_owned(),
+                            crate::Error::StorageError(format!("invalid symlink target: {}", e)),
+                        )
+                    },
+                )?,
+            },
             IngestionEntry::Regular {
                 size,
                 executable,
                 digest,
                 ..
-            } => Node::File(FileNode {
-                name,
-                digest: digest.to_owned().into(),
+            } => Node::File {
+                digest: digest.clone(),
                 size: *size,
                 executable: *executable,
-            }),
+            },
         };
 
         let parent = entry
@@ -129,8 +119,24 @@ where
         if parent == crate::Path::ROOT {
             break node;
         } else {
+            let name = entry
+                .path()
+                .file_name()
+                // If this is the root node, it will have an empty name.
+                .unwrap_or_else(|| "".try_into().unwrap())
+                .to_owned();
+
             // record node in parent directory, creating a new [Directory] if not there yet.
-            directories.entry(parent.to_owned()).or_default().add(node);
+            directories
+                .entry(parent.to_owned())
+                .or_default()
+                .add(name, node)
+                .map_err(|e| {
+                    IngestionError::UploadDirectoryError(
+                        entry.path().to_owned(),
+                        crate::Error::StorageError(e.to_string()),
+                    )
+                })?;
         }
     };
 
@@ -155,15 +161,8 @@ where
 
         #[cfg(debug_assertions)]
         {
-            if let Node::Directory(directory_node) = &root_node {
-                debug_assert_eq!(
-                    root_directory_digest,
-                    directory_node
-                        .digest
-                        .to_vec()
-                        .try_into()
-                        .expect("invalid digest len")
-                )
+            if let Node::Directory { digest, .. } = &root_node {
+                debug_assert_eq!(&root_directory_digest, digest);
             } else {
                 unreachable!("Tvix bug: directory putter initialized but no root directory node");
             }
@@ -209,9 +208,8 @@ mod test {
     use rstest::rstest;
 
     use crate::fixtures::{DIRECTORY_COMPLICATED, DIRECTORY_WITH_KEEP, EMPTY_BLOB_DIGEST};
-    use crate::proto::node::Node;
-    use crate::proto::{Directory, DirectoryNode, FileNode, SymlinkNode};
     use crate::{directoryservice::MemoryDirectoryService, fixtures::DUMMY_DIGEST};
+    use crate::{Directory, Node};
 
     use super::ingest_entries;
     use super::IngestionEntry;
@@ -223,18 +221,18 @@ mod test {
         executable: true,
         digest: DUMMY_DIGEST.clone(),
     }],
-        Node::File(FileNode { name: "foo".into(), digest: DUMMY_DIGEST.clone().into(), size: 42, executable: true }
-    ))]
+        Node::File{digest: DUMMY_DIGEST.clone(), size: 42, executable: true}
+    )]
     #[case::single_symlink(vec![IngestionEntry::Symlink {
         path: "foo".parse().unwrap(),
         target: b"blub".into(),
     }],
-        Node::Symlink(SymlinkNode { name: "foo".into(), target: "blub".into()})
+        Node::Symlink{target: "blub".try_into().unwrap()}
     )]
     #[case::single_dir(vec![IngestionEntry::Dir {
         path: "foo".parse().unwrap(),
     }],
-        Node::Directory(DirectoryNode { name: "foo".into(), digest: Directory::default().digest().into(), size: Directory::default().size()})
+        Node::Directory{digest: Directory::default().digest(), size: Directory::default().size()}
     )]
     #[case::dir_with_keep(vec![
         IngestionEntry::Regular {
@@ -247,7 +245,7 @@ mod test {
             path: "foo".parse().unwrap(),
         },
     ],
-        Node::Directory(DirectoryNode { name: "foo".into(), digest: DIRECTORY_WITH_KEEP.digest().into(), size: DIRECTORY_WITH_KEEP.size() })
+        Node::Directory{ digest: DIRECTORY_WITH_KEEP.digest(), size: DIRECTORY_WITH_KEEP.size()}
     )]
     /// This is intentionally a bit unsorted, though it still satisfies all
     /// requirements we have on the order of elements in the stream.
@@ -275,7 +273,7 @@ mod test {
             path: "blub".parse().unwrap(),
         },
     ],
-        Node::Directory(DirectoryNode { name: "blub".into(), digest: DIRECTORY_COMPLICATED.digest().into(), size:DIRECTORY_COMPLICATED.size() })
+    Node::Directory{ digest: DIRECTORY_COMPLICATED.digest(), size: DIRECTORY_COMPLICATED.size() }
     )]
     #[tokio::test]
     async fn test_ingestion(#[case] entries: Vec<IngestionEntry>, #[case] exp_root_node: Node) {
diff --git a/tvix/castore/src/lib.rs b/tvix/castore/src/lib.rs
index 4fca9801c97b..8ac6ca3dd66a 100644
--- a/tvix/castore/src/lib.rs
+++ b/tvix/castore/src/lib.rs
@@ -10,15 +10,18 @@ pub mod fixtures;
 #[cfg(feature = "fs")]
 pub mod fs;
 
+mod nodes;
+pub use nodes::*;
+
 mod path;
-pub use path::{Path, PathBuf};
+pub use path::{Path, PathBuf, PathComponent, PathComponentError};
 
 pub mod import;
 pub mod proto;
 pub mod tonic;
 
 pub use digests::{B3Digest, B3_LEN};
-pub use errors::Error;
+pub use errors::{DirectoryError, Error, ValidateNodeError};
 pub use hashing_reader::{B3HashingReader, HashingReader};
 
 #[cfg(test)]
diff --git a/tvix/castore/src/nodes/directory.rs b/tvix/castore/src/nodes/directory.rs
new file mode 100644
index 000000000000..f80e055dde80
--- /dev/null
+++ b/tvix/castore/src/nodes/directory.rs
@@ -0,0 +1,287 @@
+use std::collections::btree_map::{self, BTreeMap};
+
+use crate::{errors::DirectoryError, path::PathComponent, proto, B3Digest, Node};
+
+/// A Directory contains nodes, which can be Directory, File or Symlink nodes.
+/// It attaches names to these nodes, which is the basename in that directory.
+/// These names:
+///  - MUST not contain slashes or null bytes
+///  - MUST not be '.' or '..'
+///  - MUST be unique across all three lists
+#[derive(Default, Debug, Clone, PartialEq, Eq)]
+pub struct Directory {
+    nodes: BTreeMap<PathComponent, Node>,
+}
+
+impl Directory {
+    /// Constructs a new, empty Directory.
+    pub fn new() -> Self {
+        Directory {
+            nodes: BTreeMap::new(),
+        }
+    }
+
+    /// Construct a [Directory] from tuples of name and [Node].
+    ///
+    /// Inserting multiple elements with the same name will yield an error, as
+    /// well as exceeding the maximum size.
+    pub fn try_from_iter<T: IntoIterator<Item = (PathComponent, Node)>>(
+        iter: T,
+    ) -> Result<Directory, DirectoryError> {
+        let mut nodes = BTreeMap::new();
+
+        iter.into_iter().try_fold(0u64, |size, (name, node)| {
+            check_insert_node(size, &mut nodes, name, node)
+        })?;
+
+        Ok(Self { nodes })
+    }
+
+    /// The size of a directory is the number of all regular and symlink elements,
+    /// the number of directory elements, and their size fields.
+    pub fn size(&self) -> u64 {
+        // It's impossible to create a Directory where the size overflows, because we
+        // check before every add() that the size won't overflow.
+        (self.nodes.len() as u64)
+            + self
+                .nodes()
+                .map(|(_name, n)| match n {
+                    Node::Directory { size, .. } => 1 + size,
+                    Node::File { .. } | Node::Symlink { .. } => 1,
+                })
+                .sum::<u64>()
+    }
+
+    /// Calculates the digest of a Directory, which is the blake3 hash of a
+    /// Directory protobuf message, serialized in protobuf canonical form.
+    pub fn digest(&self) -> B3Digest {
+        proto::Directory::from(self.clone()).digest()
+    }
+
+    /// Allows iterating over all nodes (directories, files and symlinks)
+    /// For each, it returns a tuple of its name and node.
+    /// The elements are sorted by their names.
+    pub fn nodes(&self) -> impl Iterator<Item = (&PathComponent, &Node)> + Send + Sync + '_ {
+        self.nodes.iter()
+    }
+
+    /// Dissolves a Directory into its individual names and nodes.
+    /// The elements are sorted by their names.
+    pub fn into_nodes(self) -> impl Iterator<Item = (PathComponent, Node)> + Send + Sync {
+        self.nodes.into_iter()
+    }
+
+    /// Adds the specified [Node] to the [Directory] with a given name.
+    ///
+    /// Inserting a node that already exists with the same name in the directory
+    /// will yield an error, as well as exceeding the maximum size.
+    ///
+    /// In case you want to construct a [Directory] from multiple elements, use
+    /// [from_iter] instead.
+    pub fn add(&mut self, name: PathComponent, node: Node) -> Result<(), DirectoryError> {
+        check_insert_node(self.size(), &mut self.nodes, name, node)?;
+        Ok(())
+    }
+}
+
+fn checked_sum(iter: impl IntoIterator<Item = u64>) -> Option<u64> {
+    iter.into_iter().try_fold(0u64, |acc, i| acc.checked_add(i))
+}
+
+/// Helper function dealing with inserting nodes into the nodes [BTreeMap],
+/// after ensuring the new size doesn't overlow and the key doesn't exist already.
+///
+/// Returns the new total size, or an error.
+fn check_insert_node(
+    current_size: u64,
+    nodes: &mut BTreeMap<PathComponent, Node>,
+    name: PathComponent,
+    node: Node,
+) -> Result<u64, DirectoryError> {
+    // Check that the even after adding this new directory entry, the size calculation will not
+    // overflow
+    let new_size = checked_sum([
+        current_size,
+        1,
+        match node {
+            Node::Directory { size, .. } => size,
+            _ => 0,
+        },
+    ])
+    .ok_or(DirectoryError::SizeOverflow)?;
+
+    match nodes.entry(name) {
+        btree_map::Entry::Vacant(e) => {
+            e.insert(node);
+        }
+        btree_map::Entry::Occupied(occupied) => {
+            return Err(DirectoryError::DuplicateName(occupied.key().to_owned()))
+        }
+    }
+
+    Ok(new_size)
+}
+
+#[cfg(test)]
+mod test {
+    use super::{Directory, Node};
+    use crate::fixtures::DUMMY_DIGEST;
+    use crate::{DirectoryError, PathComponent};
+
+    #[test]
+    fn from_iter_single() {
+        Directory::try_from_iter([(
+            PathComponent::try_from("b").unwrap(),
+            Node::Directory {
+                digest: DUMMY_DIGEST.clone(),
+                size: 1,
+            },
+        )])
+        .unwrap();
+    }
+
+    #[test]
+    fn from_iter_multiple() {
+        let d = Directory::try_from_iter([
+            (
+                "b".try_into().unwrap(),
+                Node::Directory {
+                    digest: DUMMY_DIGEST.clone(),
+                    size: 1,
+                },
+            ),
+            (
+                "a".try_into().unwrap(),
+                Node::Directory {
+                    digest: DUMMY_DIGEST.clone(),
+                    size: 1,
+                },
+            ),
+            (
+                "z".try_into().unwrap(),
+                Node::Directory {
+                    digest: DUMMY_DIGEST.clone(),
+                    size: 1,
+                },
+            ),
+            (
+                "f".try_into().unwrap(),
+                Node::File {
+                    digest: DUMMY_DIGEST.clone(),
+                    size: 1,
+                    executable: true,
+                },
+            ),
+            (
+                "c".try_into().unwrap(),
+                Node::File {
+                    digest: DUMMY_DIGEST.clone(),
+                    size: 1,
+                    executable: true,
+                },
+            ),
+            (
+                "g".try_into().unwrap(),
+                Node::File {
+                    digest: DUMMY_DIGEST.clone(),
+                    size: 1,
+                    executable: true,
+                },
+            ),
+            (
+                "t".try_into().unwrap(),
+                Node::Symlink {
+                    target: "a".try_into().unwrap(),
+                },
+            ),
+            (
+                "o".try_into().unwrap(),
+                Node::Symlink {
+                    target: "a".try_into().unwrap(),
+                },
+            ),
+            (
+                "e".try_into().unwrap(),
+                Node::Symlink {
+                    target: "a".try_into().unwrap(),
+                },
+            ),
+        ])
+        .unwrap();
+
+        // Convert to proto struct and back to ensure we are not generating any invalid structures
+        crate::Directory::try_from(crate::proto::Directory::from(d))
+            .expect("directory should be valid");
+    }
+
+    #[test]
+    fn add_nodes_to_directory() {
+        let mut d = Directory::new();
+
+        d.add(
+            "b".try_into().unwrap(),
+            Node::Directory {
+                digest: DUMMY_DIGEST.clone(),
+                size: 1,
+            },
+        )
+        .unwrap();
+        d.add(
+            "a".try_into().unwrap(),
+            Node::Directory {
+                digest: DUMMY_DIGEST.clone(),
+                size: 1,
+            },
+        )
+        .unwrap();
+
+        // Convert to proto struct and back to ensure we are not generating any invalid structures
+        crate::Directory::try_from(crate::proto::Directory::from(d))
+            .expect("directory should be valid");
+    }
+
+    #[test]
+    fn validate_overflow() {
+        let mut d = Directory::new();
+
+        assert_eq!(
+            d.add(
+                "foo".try_into().unwrap(),
+                Node::Directory {
+                    digest: DUMMY_DIGEST.clone(),
+                    size: u64::MAX
+                }
+            ),
+            Err(DirectoryError::SizeOverflow)
+        );
+    }
+
+    #[test]
+    fn add_duplicate_node_to_directory() {
+        let mut d = Directory::new();
+
+        d.add(
+            "a".try_into().unwrap(),
+            Node::Directory {
+                digest: DUMMY_DIGEST.clone(),
+                size: 1,
+            },
+        )
+        .unwrap();
+        assert_eq!(
+            format!(
+                "{}",
+                d.add(
+                    "a".try_into().unwrap(),
+                    Node::File {
+                        digest: DUMMY_DIGEST.clone(),
+                        size: 1,
+                        executable: true
+                    }
+                )
+                .expect_err("adding duplicate dir entry must fail")
+            ),
+            "\"a\" is a duplicate name"
+        );
+    }
+}
diff --git a/tvix/castore/src/nodes/mod.rs b/tvix/castore/src/nodes/mod.rs
new file mode 100644
index 000000000000..ac7aa1e666df
--- /dev/null
+++ b/tvix/castore/src/nodes/mod.rs
@@ -0,0 +1,48 @@
+//! This holds types describing nodes in the tvix-castore model.
+mod directory;
+mod symlink_target;
+
+use crate::B3Digest;
+pub use directory::Directory;
+pub use symlink_target::{SymlinkTarget, SymlinkTargetError};
+
+/// A Node is either a [DirectoryNode], [FileNode] or [SymlinkNode].
+/// Nodes themselves don't have names, what gives them names is either them
+/// being inside a [Directory], or a root node with its own name attached to it.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Node {
+    /// A DirectoryNode is a pointer to a [Directory], by its [Directory::digest].
+    /// It also records a`size`.
+    /// Such a node is either an element in the [Directory] it itself is contained in,
+    /// or a standalone root node.
+    Directory {
+        /// The blake3 hash of a Directory message, serialized in protobuf canonical form.
+        digest: B3Digest,
+        /// Number of child elements in the Directory referred to by `digest`.
+        /// Calculated by summing up the numbers of nodes, and for each directory,
+        /// its size field. Can be used for inode allocation.
+        /// This field is precisely as verifiable as any other Merkle tree edge.
+        /// Resolve `digest`, and you can compute it incrementally. Resolve the entire
+        /// tree, and you can fully compute it from scratch.
+        /// A credulous implementation won't reject an excessive size, but this is
+        /// harmless: you'll have some ordinals without nodes. Undersizing is obvious
+        /// and easy to reject: you won't have an ordinal for some nodes.
+        size: u64,
+    },
+    /// A FileNode represents a regular or executable file in a Directory or at the root.
+    File {
+        /// The blake3 digest of the file contents
+        digest: B3Digest,
+
+        /// The file content size
+        size: u64,
+
+        /// Whether the file is executable
+        executable: bool,
+    },
+    /// A SymlinkNode represents a symbolic link in a Directory or at the root.
+    Symlink {
+        /// The target of the symlink.
+        target: SymlinkTarget,
+    },
+}
diff --git a/tvix/castore/src/nodes/symlink_target.rs b/tvix/castore/src/nodes/symlink_target.rs
new file mode 100644
index 000000000000..e9a1a0bd05c2
--- /dev/null
+++ b/tvix/castore/src/nodes/symlink_target.rs
@@ -0,0 +1,223 @@
+use bstr::ByteSlice;
+use std::fmt::{self, Debug, Display};
+
+/// A wrapper type for symlink targets.
+/// Internally uses a [bytes::Bytes], but disallows empty targets and those
+/// containing null bytes.
+#[repr(transparent)]
+#[derive(Clone, PartialEq, Eq)]
+pub struct SymlinkTarget {
+    inner: bytes::Bytes,
+}
+
+/// The maximum length a symlink target can have.
+/// Linux allows 4095 bytes here.
+pub const MAX_TARGET_LEN: usize = 4095;
+
+impl AsRef<[u8]> for SymlinkTarget {
+    fn as_ref(&self) -> &[u8] {
+        self.inner.as_ref()
+    }
+}
+
+impl From<SymlinkTarget> for bytes::Bytes {
+    fn from(value: SymlinkTarget) -> Self {
+        value.inner
+    }
+}
+
+fn validate_symlink_target<B: AsRef<[u8]>>(symlink_target: B) -> Result<B, SymlinkTargetError> {
+    let v = symlink_target.as_ref();
+
+    if v.is_empty() {
+        return Err(SymlinkTargetError::Empty);
+    }
+    if v.len() > MAX_TARGET_LEN {
+        return Err(SymlinkTargetError::TooLong);
+    }
+    if v.contains(&0x00) {
+        return Err(SymlinkTargetError::Null);
+    }
+
+    Ok(symlink_target)
+}
+
+impl TryFrom<bytes::Bytes> for SymlinkTarget {
+    type Error = SymlinkTargetError;
+
+    fn try_from(value: bytes::Bytes) -> Result<Self, Self::Error> {
+        if let Err(e) = validate_symlink_target(&value) {
+            return Err(SymlinkTargetError::Convert(value, Box::new(e)));
+        }
+
+        Ok(Self { inner: value })
+    }
+}
+
+impl TryFrom<&'static [u8]> for SymlinkTarget {
+    type Error = SymlinkTargetError;
+
+    fn try_from(value: &'static [u8]) -> Result<Self, Self::Error> {
+        if let Err(e) = validate_symlink_target(&value) {
+            return Err(SymlinkTargetError::Convert(value.into(), Box::new(e)));
+        }
+
+        Ok(Self {
+            inner: bytes::Bytes::from_static(value),
+        })
+    }
+}
+
+impl TryFrom<&str> for SymlinkTarget {
+    type Error = SymlinkTargetError;
+
+    fn try_from(value: &str) -> Result<Self, Self::Error> {
+        if let Err(e) = validate_symlink_target(value) {
+            return Err(SymlinkTargetError::Convert(
+                value.to_owned().into(),
+                Box::new(e),
+            ));
+        }
+
+        Ok(Self {
+            inner: bytes::Bytes::copy_from_slice(value.as_bytes()),
+        })
+    }
+}
+
+impl Debug for SymlinkTarget {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        Debug::fmt(self.inner.as_bstr(), f)
+    }
+}
+
+impl Display for SymlinkTarget {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        Display::fmt(self.inner.as_bstr(), f)
+    }
+}
+
+/// Errors created when constructing / converting to [SymlinkTarget].
+#[derive(Debug, PartialEq, Eq, thiserror::Error)]
+#[cfg_attr(test, derive(Clone))]
+pub enum SymlinkTargetError {
+    #[error("cannot be empty")]
+    Empty,
+    #[error("cannot contain null bytes")]
+    Null,
+    #[error("cannot be over {} bytes long", MAX_TARGET_LEN)]
+    TooLong,
+    #[error("unable to convert '{:?}", .0.as_bstr())]
+    Convert(bytes::Bytes, Box<Self>),
+}
+
+#[cfg(test)]
+mod tests {
+    use bytes::Bytes;
+    use rstest::rstest;
+
+    use super::validate_symlink_target;
+    use super::{SymlinkTarget, SymlinkTargetError};
+
+    #[rstest]
+    #[case::empty(b"", SymlinkTargetError::Empty)]
+    #[case::null(b"foo\0", SymlinkTargetError::Null)]
+    fn errors(#[case] v: &'static [u8], #[case] err: SymlinkTargetError) {
+        {
+            assert_eq!(
+                Err(err.clone()),
+                validate_symlink_target(v),
+                "validate_symlink_target must fail as expected"
+            );
+        }
+
+        let exp_err_v = Bytes::from_static(v);
+
+        // Bytes
+        {
+            let v = Bytes::from_static(v);
+            assert_eq!(
+                Err(SymlinkTargetError::Convert(
+                    exp_err_v.clone(),
+                    Box::new(err.clone())
+                )),
+                SymlinkTarget::try_from(v),
+                "conversion must fail as expected"
+            );
+        }
+        // &[u8]
+        {
+            assert_eq!(
+                Err(SymlinkTargetError::Convert(
+                    exp_err_v.clone(),
+                    Box::new(err.clone())
+                )),
+                SymlinkTarget::try_from(v),
+                "conversion must fail as expected"
+            );
+        }
+        // &str, if this is valid UTF-8
+        {
+            if let Ok(v) = std::str::from_utf8(v) {
+                assert_eq!(
+                    Err(SymlinkTargetError::Convert(
+                        exp_err_v.clone(),
+                        Box::new(err.clone())
+                    )),
+                    SymlinkTarget::try_from(v),
+                    "conversion must fail as expected"
+                );
+            }
+        }
+    }
+
+    #[test]
+    fn error_toolong() {
+        assert_eq!(
+            Err(SymlinkTargetError::TooLong),
+            validate_symlink_target("X".repeat(5000).into_bytes().as_slice())
+        )
+    }
+
+    #[rstest]
+    #[case::boring(b"aa")]
+    #[case::dot(b".")]
+    #[case::dotsandslashes(b"./..")]
+    #[case::dotdot(b"..")]
+    #[case::slashes(b"a/b")]
+    #[case::slashes_and_absolute(b"/a/b")]
+    #[case::invalid_utf8(b"\xc5\xc4\xd6")]
+    fn success(#[case] v: &'static [u8]) {
+        let exp = SymlinkTarget { inner: v.into() };
+
+        // Bytes
+        {
+            let v: Bytes = v.into();
+            assert_eq!(
+                Ok(exp.clone()),
+                SymlinkTarget::try_from(v),
+                "conversion must succeed"
+            )
+        }
+
+        // &[u8]
+        {
+            assert_eq!(
+                Ok(exp.clone()),
+                SymlinkTarget::try_from(v),
+                "conversion must succeed"
+            )
+        }
+
+        // &str, if this is valid UTF-8
+        {
+            if let Ok(v) = std::str::from_utf8(v) {
+                assert_eq!(
+                    Ok(exp.clone()),
+                    SymlinkTarget::try_from(v),
+                    "conversion must succeed"
+                )
+            }
+        }
+    }
+}
diff --git a/tvix/castore/src/path/component.rs b/tvix/castore/src/path/component.rs
new file mode 100644
index 000000000000..78aca03c50fe
--- /dev/null
+++ b/tvix/castore/src/path/component.rs
@@ -0,0 +1,268 @@
+use bstr::ByteSlice;
+use std::fmt::{self, Debug, Display};
+
+/// A wrapper type for validated path components in the castore model.
+/// Internally uses a [bytes::Bytes], but disallows
+/// slashes, and null bytes to be present, as well as
+/// '.', '..' and the empty string.
+/// It also rejects components that are too long (> 255 bytes).
+#[repr(transparent)]
+#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
+pub struct PathComponent {
+    pub(super) inner: bytes::Bytes,
+}
+
+/// The maximum length an individual path component can have.
+/// Linux allows 255 bytes of actual name, so we pick that.
+pub const MAX_NAME_LEN: usize = 255;
+
+impl AsRef<[u8]> for PathComponent {
+    fn as_ref(&self) -> &[u8] {
+        self.inner.as_ref()
+    }
+}
+
+impl From<PathComponent> for bytes::Bytes {
+    fn from(value: PathComponent) -> Self {
+        value.inner
+    }
+}
+
+pub(super) fn validate_name<B: AsRef<[u8]>>(name: B) -> Result<(), PathComponentError> {
+    match name.as_ref() {
+        b"" => Err(PathComponentError::Empty),
+        b".." => Err(PathComponentError::Parent),
+        b"." => Err(PathComponentError::CurDir),
+        v if v.len() > MAX_NAME_LEN => Err(PathComponentError::TooLong),
+        v if v.contains(&0x00) => Err(PathComponentError::Null),
+        v if v.contains(&b'/') => Err(PathComponentError::Slashes),
+        _ => Ok(()),
+    }
+}
+
+impl TryFrom<bytes::Bytes> for PathComponent {
+    type Error = PathComponentError;
+
+    fn try_from(value: bytes::Bytes) -> Result<Self, Self::Error> {
+        if let Err(e) = validate_name(&value) {
+            return Err(PathComponentError::Convert(value, Box::new(e)));
+        }
+
+        Ok(Self { inner: value })
+    }
+}
+
+impl TryFrom<&'static [u8]> for PathComponent {
+    type Error = PathComponentError;
+
+    fn try_from(value: &'static [u8]) -> Result<Self, Self::Error> {
+        if let Err(e) = validate_name(value) {
+            return Err(PathComponentError::Convert(value.into(), Box::new(e)));
+        }
+
+        Ok(Self {
+            inner: bytes::Bytes::from_static(value),
+        })
+    }
+}
+
+impl TryFrom<&str> for PathComponent {
+    type Error = PathComponentError;
+
+    fn try_from(value: &str) -> Result<Self, Self::Error> {
+        if let Err(e) = validate_name(value) {
+            return Err(PathComponentError::Convert(
+                value.to_owned().into(),
+                Box::new(e),
+            ));
+        }
+
+        Ok(Self {
+            inner: bytes::Bytes::copy_from_slice(value.as_bytes()),
+        })
+    }
+}
+
+impl TryFrom<&std::ffi::CStr> for PathComponent {
+    type Error = PathComponentError;
+
+    fn try_from(value: &std::ffi::CStr) -> Result<Self, Self::Error> {
+        let value = value.to_bytes();
+        if let Err(e) = validate_name(value) {
+            return Err(PathComponentError::Convert(
+                value.to_owned().into(),
+                Box::new(e),
+            ));
+        }
+
+        Ok(Self {
+            inner: bytes::Bytes::copy_from_slice(value),
+        })
+    }
+}
+
+impl Debug for PathComponent {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        Debug::fmt(self.inner.as_bstr(), f)
+    }
+}
+
+impl Display for PathComponent {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        Display::fmt(self.inner.as_bstr(), f)
+    }
+}
+
+/// Errors created when parsing / validating [PathComponent].
+#[derive(Debug, PartialEq, thiserror::Error)]
+#[cfg_attr(test, derive(Clone))]
+pub enum PathComponentError {
+    #[error("cannot be empty")]
+    Empty,
+    #[error("cannot contain null bytes")]
+    Null,
+    #[error("cannot be '.'")]
+    CurDir,
+    #[error("cannot be '..'")]
+    Parent,
+    #[error("cannot contain slashes")]
+    Slashes,
+    #[error("cannot be over {} bytes long", MAX_NAME_LEN)]
+    TooLong,
+    #[error("unable to convert '{:?}'", .0.as_bstr())]
+    Convert(bytes::Bytes, #[source] Box<Self>),
+}
+
+#[cfg(test)]
+mod tests {
+    use std::ffi::CString;
+
+    use bytes::Bytes;
+    use rstest::rstest;
+
+    use super::{validate_name, PathComponent, PathComponentError};
+
+    #[rstest]
+    #[case::empty(b"", PathComponentError::Empty)]
+    #[case::null(b"foo\0", PathComponentError::Null)]
+    #[case::curdir(b".", PathComponentError::CurDir)]
+    #[case::parent(b"..", PathComponentError::Parent)]
+    #[case::slashes1(b"a/b", PathComponentError::Slashes)]
+    #[case::slashes2(b"/", PathComponentError::Slashes)]
+    fn errors(#[case] v: &'static [u8], #[case] err: PathComponentError) {
+        {
+            assert_eq!(
+                Err(err.clone()),
+                validate_name(v),
+                "validate_name must fail as expected"
+            );
+        }
+
+        let exp_err_v = Bytes::from_static(v);
+
+        // Bytes
+        {
+            let v = Bytes::from_static(v);
+            assert_eq!(
+                Err(PathComponentError::Convert(
+                    exp_err_v.clone(),
+                    Box::new(err.clone())
+                )),
+                PathComponent::try_from(v),
+                "conversion must fail as expected"
+            );
+        }
+        // &[u8]
+        {
+            assert_eq!(
+                Err(PathComponentError::Convert(
+                    exp_err_v.clone(),
+                    Box::new(err.clone())
+                )),
+                PathComponent::try_from(v),
+                "conversion must fail as expected"
+            );
+        }
+        // &str, if it is valid UTF-8
+        {
+            if let Ok(v) = std::str::from_utf8(v) {
+                assert_eq!(
+                    Err(PathComponentError::Convert(
+                        exp_err_v.clone(),
+                        Box::new(err.clone())
+                    )),
+                    PathComponent::try_from(v),
+                    "conversion must fail as expected"
+                );
+            }
+        }
+        // &CStr, if it can be constructed (fails if the payload contains null bytes)
+        {
+            if let Ok(v) = CString::new(v) {
+                let v = v.as_ref();
+                assert_eq!(
+                    Err(PathComponentError::Convert(
+                        exp_err_v.clone(),
+                        Box::new(err.clone())
+                    )),
+                    PathComponent::try_from(v),
+                    "conversion must fail as expected"
+                );
+            }
+        }
+    }
+
+    #[test]
+    fn error_toolong() {
+        assert_eq!(
+            Err(PathComponentError::TooLong),
+            validate_name("X".repeat(500).into_bytes().as_slice())
+        )
+    }
+
+    #[test]
+    fn success() {
+        let exp = PathComponent { inner: "aa".into() };
+
+        // Bytes
+        {
+            let v: Bytes = "aa".into();
+            assert_eq!(
+                Ok(exp.clone()),
+                PathComponent::try_from(v),
+                "conversion must succeed"
+            );
+        }
+
+        // &[u8]
+        {
+            let v: &[u8] = b"aa";
+            assert_eq!(
+                Ok(exp.clone()),
+                PathComponent::try_from(v),
+                "conversion must succeed"
+            );
+        }
+
+        // &str
+        {
+            let v: &str = "aa";
+            assert_eq!(
+                Ok(exp.clone()),
+                PathComponent::try_from(v),
+                "conversion must succeed"
+            );
+        }
+
+        // &CStr
+        {
+            let v = CString::new("aa").expect("CString must construct");
+            let v = v.as_c_str();
+            assert_eq!(
+                Ok(exp.clone()),
+                PathComponent::try_from(v),
+                "conversion must succeed"
+            );
+        }
+    }
+}
diff --git a/tvix/castore/src/path.rs b/tvix/castore/src/path/mod.rs
index fcc2bd01fbd6..15f31a570da9 100644
--- a/tvix/castore/src/path.rs
+++ b/tvix/castore/src/path/mod.rs
@@ -1,5 +1,5 @@
 //! Contains data structures to deal with Paths in the tvix-castore model.
-
+use bstr::ByteSlice;
 use std::{
     borrow::Borrow,
     fmt::{self, Debug, Display},
@@ -8,9 +8,8 @@ use std::{
     str::FromStr,
 };
 
-use bstr::ByteSlice;
-
-use crate::proto::validate_node_name;
+mod component;
+pub use component::{PathComponent, PathComponentError};
 
 /// Represents a Path in the castore model.
 /// These are always relative, and platform-independent, which distinguishes
@@ -38,7 +37,9 @@ impl Path {
         if !bytes.is_empty() {
             // Ensure all components are valid castore node names.
             for component in bytes.split_str(b"/") {
-                validate_node_name(component).ok()?;
+                if component::validate_name(component).is_err() {
+                    return None;
+                }
             }
         }
 
@@ -81,10 +82,26 @@ impl Path {
         Ok(v)
     }
 
+    /// Provides an iterator over the components of the path,
+    /// which are invividual [PathComponent].
+    /// In case the path is empty, an empty iterator is returned.
+    pub fn components(&self) -> impl Iterator<Item = PathComponent> + '_ {
+        let mut iter = self.inner.split_str(&b"/");
+
+        // We don't want to return an empty element, consume it if it's the only one.
+        if self.inner.is_empty() {
+            let _ = iter.next();
+        }
+
+        iter.map(|b| PathComponent {
+            inner: bytes::Bytes::copy_from_slice(b),
+        })
+    }
+
     /// Produces an iterator over the components of the path, which are
     /// individual byte slices.
     /// In case the path is empty, an empty iterator is returned.
-    pub fn components(&self) -> impl Iterator<Item = &[u8]> {
+    pub fn components_bytes(&self) -> impl Iterator<Item = &[u8]> {
         let mut iter = self.inner.split_str(&b"/");
 
         // We don't want to return an empty element, consume it if it's the only one.
@@ -95,11 +112,16 @@ impl Path {
         iter
     }
 
-    /// Returns the final component of the Path, if there is one.
-    pub fn file_name(&self) -> Option<&[u8]> {
+    /// Returns the final component of the Path, if there is one, in bytes.
+    pub fn file_name(&self) -> Option<PathComponent> {
         self.components().last()
     }
 
+    /// Returns the final component of the Path, if there is one, in bytes.
+    pub fn file_name_bytes(&self) -> Option<&[u8]> {
+        self.components_bytes().last()
+    }
+
     pub fn as_bytes(&self) -> &[u8] {
         &self.inner
     }
@@ -211,7 +233,9 @@ impl PathBuf {
 
     /// Adjoins `name` to self.
     pub fn try_push(&mut self, name: &[u8]) -> Result<(), std::io::Error> {
-        validate_node_name(name).map_err(|_| std::io::ErrorKind::InvalidData)?;
+        if component::validate_name(name).is_err() {
+            return Err(std::io::ErrorKind::InvalidData.into());
+        }
 
         if !self.inner.is_empty() {
             self.inner.push(b'/');
@@ -329,7 +353,7 @@ mod test {
         assert_eq!(s.as_bytes(), p.as_bytes(), "inner bytes mismatch");
         assert_eq!(
             num_components,
-            p.components().count(),
+            p.components_bytes().count(),
             "number of components mismatch"
         );
     }
@@ -396,10 +420,10 @@ mod test {
     #[case("a", vec!["a"])]
     #[case("a/b", vec!["a", "b"])]
     #[case("a/b/c", vec!["a","b", "c"])]
-    pub fn components(#[case] p: PathBuf, #[case] exp_components: Vec<&str>) {
+    pub fn components_bytes(#[case] p: PathBuf, #[case] exp_components: Vec<&str>) {
         assert_eq!(
             exp_components,
-            p.components()
+            p.components_bytes()
                 .map(|x| x.to_str().unwrap())
                 .collect::<Vec<_>>()
         );
diff --git a/tvix/castore/src/proto/grpc_directoryservice_wrapper.rs b/tvix/castore/src/proto/grpc_directoryservice_wrapper.rs
index ce1d2bcd244a..62fdb34a25a0 100644
--- a/tvix/castore/src/proto/grpc_directoryservice_wrapper.rs
+++ b/tvix/castore/src/proto/grpc_directoryservice_wrapper.rs
@@ -1,7 +1,5 @@
-use crate::directoryservice::DirectoryGraph;
-use crate::directoryservice::LeavesToRootValidator;
-use crate::proto;
-use crate::{directoryservice::DirectoryService, B3Digest};
+use crate::directoryservice::{DirectoryGraph, DirectoryService, LeavesToRootValidator};
+use crate::{proto, B3Digest, DirectoryError};
 use futures::stream::BoxStream;
 use futures::TryStreamExt;
 use std::ops::Deref;
@@ -58,13 +56,16 @@ where
                                 Status::not_found(format!("directory {} not found", digest))
                             })?;
 
-                        Box::pin(once(Ok(directory)))
+                        Box::pin(once(Ok(directory.into())))
                     } else {
                         // If recursive was requested, traverse via get_recursive.
                         Box::pin(
-                            self.directory_service.get_recursive(&digest).map_err(|e| {
-                                tonic::Status::new(tonic::Code::Internal, e.to_string())
-                            }),
+                            self.directory_service
+                                .get_recursive(&digest)
+                                .map_ok(proto::Directory::from)
+                                .map_err(|e| {
+                                    tonic::Status::new(tonic::Code::Internal, e.to_string())
+                                }),
                         )
                     }
                 }))
@@ -83,7 +84,9 @@ where
         let mut validator = DirectoryGraph::<LeavesToRootValidator>::default();
         while let Some(directory) = req_inner.message().await? {
             validator
-                .add(directory)
+                .add(directory.try_into().map_err(|e: DirectoryError| {
+                    tonic::Status::new(tonic::Code::Internal, e.to_string())
+                })?)
                 .map_err(|e| tonic::Status::new(tonic::Code::Internal, e.to_string()))?;
         }
 
diff --git a/tvix/castore/src/proto/mod.rs b/tvix/castore/src/proto/mod.rs
index a0cec896f753..8bc74b412676 100644
--- a/tvix/castore/src/proto/mod.rs
+++ b/tvix/castore/src/proto/mod.rs
@@ -1,18 +1,13 @@
-#![allow(non_snake_case)]
-// https://github.com/hyperium/tonic/issues/1056
-use bstr::ByteSlice;
-use std::{collections::HashSet, iter::Peekable, str};
-
 use prost::Message;
+use std::cmp::Ordering;
 
 mod grpc_blobservice_wrapper;
 mod grpc_directoryservice_wrapper;
 
+use crate::{path::PathComponent, B3Digest, DirectoryError};
 pub use grpc_blobservice_wrapper::GRPCBlobServiceWrapper;
 pub use grpc_directoryservice_wrapper::GRPCDirectoryServiceWrapper;
 
-use crate::{B3Digest, B3_LEN};
-
 tonic::include_proto!("tvix.castore.v1");
 
 #[cfg(feature = "tonic-reflection")]
@@ -24,38 +19,6 @@ pub const FILE_DESCRIPTOR_SET: &[u8] = tonic::include_file_descriptor_set!("tvix
 #[cfg(test)]
 mod tests;
 
-/// Errors that can occur during the validation of [Directory] messages.
-#[derive(Debug, PartialEq, Eq, thiserror::Error)]
-pub enum ValidateDirectoryError {
-    /// Elements are not in sorted order
-    #[error("{:?} is not sorted", .0.as_bstr())]
-    WrongSorting(Vec<u8>),
-    /// Multiple elements with the same name encountered
-    #[error("{:?} is a duplicate name", .0.as_bstr())]
-    DuplicateName(Vec<u8>),
-    /// Invalid node
-    #[error("invalid node with name {:?}: {:?}", .0.as_bstr(), .1.to_string())]
-    InvalidNode(Vec<u8>, ValidateNodeError),
-    #[error("Total size exceeds u32::MAX")]
-    SizeOverflow,
-}
-
-/// Errors that occur during Node validation
-#[derive(Debug, PartialEq, Eq, thiserror::Error)]
-pub enum ValidateNodeError {
-    #[error("No node set")]
-    NoNodeSet,
-    /// Invalid digest length encountered
-    #[error("Invalid Digest length: {0}")]
-    InvalidDigestLen(usize),
-    /// Invalid name encountered
-    #[error("Invalid name: {}", .0.as_bstr())]
-    InvalidName(Vec<u8>),
-    /// Invalid symlink target
-    #[error("Invalid symlink target: {}", .0.as_bstr())]
-    InvalidSymlinkTarget(Vec<u8>),
-}
-
 /// Errors that occur during StatBlobResponse validation
 #[derive(Debug, PartialEq, Eq, thiserror::Error)]
 pub enum ValidateStatBlobResponseError {
@@ -64,186 +27,6 @@ pub enum ValidateStatBlobResponseError {
     InvalidDigestLen(usize, usize),
 }
 
-/// Checks a Node name for validity as an intermediate node.
-/// We disallow slashes, null bytes, '.', '..' and the empty string.
-pub(crate) fn validate_node_name(name: &[u8]) -> Result<(), ValidateNodeError> {
-    if name.is_empty()
-        || name == b".."
-        || name == b"."
-        || name.contains(&0x00)
-        || name.contains(&b'/')
-    {
-        Err(ValidateNodeError::InvalidName(name.to_owned()))
-    } else {
-        Ok(())
-    }
-}
-
-/// NamedNode is implemented for [FileNode], [DirectoryNode] and [SymlinkNode]
-/// and [node::Node], so we can ask all of them for the name easily.
-pub trait NamedNode {
-    fn get_name(&self) -> &[u8];
-}
-
-impl NamedNode for &FileNode {
-    fn get_name(&self) -> &[u8] {
-        &self.name
-    }
-}
-
-impl NamedNode for &DirectoryNode {
-    fn get_name(&self) -> &[u8] {
-        &self.name
-    }
-}
-
-impl NamedNode for &SymlinkNode {
-    fn get_name(&self) -> &[u8] {
-        &self.name
-    }
-}
-
-impl NamedNode for node::Node {
-    fn get_name(&self) -> &[u8] {
-        match self {
-            node::Node::File(node_file) => &node_file.name,
-            node::Node::Directory(node_directory) => &node_directory.name,
-            node::Node::Symlink(node_symlink) => &node_symlink.name,
-        }
-    }
-}
-
-impl Node {
-    /// Ensures the node has a valid enum kind (is Some), and passes its
-    // per-enum validation.
-    // The inner root node is returned for easier consumption.
-    pub fn validate(&self) -> Result<&node::Node, ValidateNodeError> {
-        if let Some(node) = self.node.as_ref() {
-            node.validate()?;
-            Ok(node)
-        } else {
-            Err(ValidateNodeError::NoNodeSet)
-        }
-    }
-}
-
-impl node::Node {
-    /// Returns the node with a new name.
-    pub fn rename(self, name: bytes::Bytes) -> Self {
-        match self {
-            node::Node::Directory(n) => node::Node::Directory(DirectoryNode { name, ..n }),
-            node::Node::File(n) => node::Node::File(FileNode { name, ..n }),
-            node::Node::Symlink(n) => node::Node::Symlink(SymlinkNode { name, ..n }),
-        }
-    }
-
-    /// Ensures the node has a valid name, and checks the type-specific fields too.
-    pub fn validate(&self) -> Result<(), ValidateNodeError> {
-        match self {
-            // for a directory root node, ensure the digest has the appropriate size.
-            node::Node::Directory(directory_node) => {
-                if directory_node.digest.len() != B3_LEN {
-                    Err(ValidateNodeError::InvalidDigestLen(
-                        directory_node.digest.len(),
-                    ))?;
-                }
-                validate_node_name(&directory_node.name)
-            }
-            // for a file root node, ensure the digest has the appropriate size.
-            node::Node::File(file_node) => {
-                if file_node.digest.len() != B3_LEN {
-                    Err(ValidateNodeError::InvalidDigestLen(file_node.digest.len()))?;
-                }
-                validate_node_name(&file_node.name)
-            }
-            // ensure the symlink target is not empty and doesn't contain null bytes.
-            node::Node::Symlink(symlink_node) => {
-                if symlink_node.target.is_empty() || symlink_node.target.contains(&b'\0') {
-                    Err(ValidateNodeError::InvalidSymlinkTarget(
-                        symlink_node.target.to_vec(),
-                    ))?;
-                }
-                validate_node_name(&symlink_node.name)
-            }
-        }
-    }
-}
-
-impl PartialOrd for node::Node {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl Ord for node::Node {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        self.get_name().cmp(other.get_name())
-    }
-}
-
-impl PartialOrd for FileNode {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl Ord for FileNode {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        self.get_name().cmp(other.get_name())
-    }
-}
-
-impl PartialOrd for SymlinkNode {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl Ord for SymlinkNode {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        self.get_name().cmp(other.get_name())
-    }
-}
-
-impl PartialOrd for DirectoryNode {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl Ord for DirectoryNode {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        self.get_name().cmp(other.get_name())
-    }
-}
-
-/// Accepts a name, and a mutable reference to the previous name.
-/// If the passed name is larger than the previous one, the reference is updated.
-/// If it's not, an error is returned.
-fn update_if_lt_prev<'n>(
-    prev_name: &mut &'n [u8],
-    name: &'n [u8],
-) -> Result<(), ValidateDirectoryError> {
-    if *name < **prev_name {
-        return Err(ValidateDirectoryError::WrongSorting(name.to_vec()));
-    }
-    *prev_name = name;
-    Ok(())
-}
-
-/// Inserts the given name into a HashSet if it's not already in there.
-/// If it is, an error is returned.
-fn insert_once<'n>(
-    seen_names: &mut HashSet<&'n [u8]>,
-    name: &'n [u8],
-) -> Result<(), ValidateDirectoryError> {
-    if seen_names.get(name).is_some() {
-        return Err(ValidateDirectoryError::DuplicateName(name.to_vec()));
-    }
-    seen_names.insert(name);
-    Ok(())
-}
-
 fn checked_sum(iter: impl IntoIterator<Item = u64>) -> Option<u64> {
     iter.into_iter().try_fold(0u64, |acc, i| acc.checked_add(i))
 }
@@ -280,117 +63,211 @@ impl Directory {
             .as_bytes()
             .into()
     }
+}
 
-    /// validate checks the directory for invalid data, such as:
-    /// - violations of name restrictions
-    /// - invalid digest lengths
-    /// - not properly sorted lists
-    /// - duplicate names in the three lists
-    pub fn validate(&self) -> Result<(), ValidateDirectoryError> {
-        let mut seen_names: HashSet<&[u8]> = HashSet::new();
-
-        let mut last_directory_name: &[u8] = b"";
-        let mut last_file_name: &[u8] = b"";
-        let mut last_symlink_name: &[u8] = b"";
+impl TryFrom<Directory> for crate::Directory {
+    type Error = DirectoryError;
+
+    fn try_from(value: Directory) -> Result<Self, Self::Error> {
+        // Check directories, files and symlinks are sorted
+        // We'll notice duplicates across all three fields when constructing the Directory.
+        // FUTUREWORK: use is_sorted() once stable, and/or implement the producer for
+        // [crate::Directory::try_from_iter] iterating over all three and doing all checks inline.
+        value
+            .directories
+            .iter()
+            .try_fold(&b""[..], |prev_name, e| {
+                match e.name.as_ref().cmp(prev_name) {
+                    Ordering::Less => Err(DirectoryError::WrongSorting(e.name.to_owned())),
+                    Ordering::Equal => Err(DirectoryError::DuplicateName(
+                        e.name
+                            .to_owned()
+                            .try_into()
+                            .map_err(DirectoryError::InvalidName)?,
+                    )),
+                    Ordering::Greater => Ok(e.name.as_ref()),
+                }
+            })?;
+        value.files.iter().try_fold(&b""[..], |prev_name, e| {
+            match e.name.as_ref().cmp(prev_name) {
+                Ordering::Less => Err(DirectoryError::WrongSorting(e.name.to_owned())),
+                Ordering::Equal => Err(DirectoryError::DuplicateName(
+                    e.name
+                        .to_owned()
+                        .try_into()
+                        .map_err(DirectoryError::InvalidName)?,
+                )),
+                Ordering::Greater => Ok(e.name.as_ref()),
+            }
+        })?;
+        value.symlinks.iter().try_fold(&b""[..], |prev_name, e| {
+            match e.name.as_ref().cmp(prev_name) {
+                Ordering::Less => Err(DirectoryError::WrongSorting(e.name.to_owned())),
+                Ordering::Equal => Err(DirectoryError::DuplicateName(
+                    e.name
+                        .to_owned()
+                        .try_into()
+                        .map_err(DirectoryError::InvalidName)?,
+                )),
+                Ordering::Greater => Ok(e.name.as_ref()),
+            }
+        })?;
 
-        // check directories
-        for directory_node in &self.directories {
-            node::Node::Directory(directory_node.clone())
-                .validate()
-                .map_err(|e| {
-                    ValidateDirectoryError::InvalidNode(directory_node.name.to_vec(), e)
-                })?;
+        // FUTUREWORK: use is_sorted() once stable, and/or implement the producer for
+        // [crate::Directory::try_from_iter] iterating over all three and doing all checks inline.
+        let mut elems: Vec<(PathComponent, crate::Node)> =
+            Vec::with_capacity(value.directories.len() + value.files.len() + value.symlinks.len());
 
-            update_if_lt_prev(&mut last_directory_name, &directory_node.name)?;
-            insert_once(&mut seen_names, &directory_node.name)?;
+        for e in value.directories {
+            elems.push(
+                Node {
+                    node: Some(node::Node::Directory(e)),
+                }
+                .into_name_and_node()?,
+            );
         }
 
-        // check files
-        for file_node in &self.files {
-            node::Node::File(file_node.clone())
-                .validate()
-                .map_err(|e| ValidateDirectoryError::InvalidNode(file_node.name.to_vec(), e))?;
-
-            update_if_lt_prev(&mut last_file_name, &file_node.name)?;
-            insert_once(&mut seen_names, &file_node.name)?;
+        for e in value.files {
+            elems.push(
+                Node {
+                    node: Some(node::Node::File(e)),
+                }
+                .into_name_and_node()?,
+            )
         }
 
-        // check symlinks
-        for symlink_node in &self.symlinks {
-            node::Node::Symlink(symlink_node.clone())
-                .validate()
-                .map_err(|e| ValidateDirectoryError::InvalidNode(symlink_node.name.to_vec(), e))?;
-
-            update_if_lt_prev(&mut last_symlink_name, &symlink_node.name)?;
-            insert_once(&mut seen_names, &symlink_node.name)?;
+        for e in value.symlinks {
+            elems.push(
+                Node {
+                    node: Some(node::Node::Symlink(e)),
+                }
+                .into_name_and_node()?,
+            )
         }
 
-        self.size_checked()
-            .ok_or(ValidateDirectoryError::SizeOverflow)?;
-
-        Ok(())
+        crate::Directory::try_from_iter(elems)
     }
+}
 
-    /// Allows iterating over all three nodes ([DirectoryNode], [FileNode],
-    /// [SymlinkNode]) in an ordered fashion, as long as the individual lists
-    /// are sorted (which can be checked by the [Directory::validate]).
-    pub fn nodes(&self) -> DirectoryNodesIterator {
-        return DirectoryNodesIterator {
-            i_directories: self.directories.iter().peekable(),
-            i_files: self.files.iter().peekable(),
-            i_symlinks: self.symlinks.iter().peekable(),
-        };
-    }
+impl From<crate::Directory> for Directory {
+    fn from(value: crate::Directory) -> Self {
+        let mut directories = vec![];
+        let mut files = vec![];
+        let mut symlinks = vec![];
+
+        for (name, node) in value.into_nodes() {
+            match node {
+                crate::Node::File {
+                    digest,
+                    size,
+                    executable,
+                } => files.push(FileNode {
+                    name: name.into(),
+                    digest: digest.into(),
+                    size,
+                    executable,
+                }),
+                crate::Node::Directory { digest, size } => directories.push(DirectoryNode {
+                    name: name.into(),
+                    digest: digest.into(),
+                    size,
+                }),
+                crate::Node::Symlink { target } => {
+                    symlinks.push(SymlinkNode {
+                        name: name.into(),
+                        target: target.into(),
+                    });
+                }
+            }
+        }
 
-    /// Adds the specified [node::Node] to the [Directory], preserving sorted entries.
-    /// This assumes the [Directory] to be sorted prior to adding the node.
-    ///
-    /// Inserting an element that already exists with the same name in the directory is not
-    /// supported.
-    pub fn add(&mut self, node: node::Node) {
-        debug_assert!(
-            !self.files.iter().any(|x| x.get_name() == node.get_name()),
-            "name already exists in files"
-        );
-        debug_assert!(
-            !self
-                .directories
-                .iter()
-                .any(|x| x.get_name() == node.get_name()),
-            "name already exists in directories"
-        );
-        debug_assert!(
-            !self
-                .symlinks
-                .iter()
-                .any(|x| x.get_name() == node.get_name()),
-            "name already exists in symlinks"
-        );
+        Directory {
+            directories,
+            files,
+            symlinks,
+        }
+    }
+}
 
-        match node {
-            node::Node::File(node) => {
-                let pos = self
-                    .files
-                    .binary_search(&node)
-                    .expect_err("Tvix bug: dir entry with name already exists");
-                self.files.insert(pos, node);
+impl Node {
+    /// Converts a proto [Node] to a [crate::Node], and splits off the name.
+    pub fn into_name_and_node(self) -> Result<(PathComponent, crate::Node), DirectoryError> {
+        match self.node.ok_or_else(|| DirectoryError::NoNodeSet)? {
+            node::Node::Directory(n) => {
+                let name: PathComponent = n.name.try_into().map_err(DirectoryError::InvalidName)?;
+                let digest = B3Digest::try_from(n.digest)
+                    .map_err(|e| DirectoryError::InvalidNode(name.clone(), e.into()))?;
+
+                let node = crate::Node::Directory {
+                    digest,
+                    size: n.size,
+                };
+
+                Ok((name, node))
             }
-            node::Node::Directory(node) => {
-                let pos = self
-                    .directories
-                    .binary_search(&node)
-                    .expect_err("Tvix bug: dir entry with name already exists");
-                self.directories.insert(pos, node);
+            node::Node::File(n) => {
+                let name: PathComponent = n.name.try_into().map_err(DirectoryError::InvalidName)?;
+                let digest = B3Digest::try_from(n.digest)
+                    .map_err(|e| DirectoryError::InvalidNode(name.clone(), e.into()))?;
+
+                let node = crate::Node::File {
+                    digest,
+                    size: n.size,
+                    executable: n.executable,
+                };
+
+                Ok((name, node))
             }
-            node::Node::Symlink(node) => {
-                let pos = self
-                    .symlinks
-                    .binary_search(&node)
-                    .expect_err("Tvix bug: dir entry with name already exists");
-                self.symlinks.insert(pos, node);
+
+            node::Node::Symlink(n) => {
+                let name: PathComponent = n.name.try_into().map_err(DirectoryError::InvalidName)?;
+
+                let node = crate::Node::Symlink {
+                    target: n.target.try_into().map_err(|e| {
+                        DirectoryError::InvalidNode(
+                            name.clone(),
+                            crate::ValidateNodeError::InvalidSymlinkTarget(e),
+                        )
+                    })?,
+                };
+
+                Ok((name, node))
             }
         }
     }
+
+    /// Construsts a [Node] from a name and [crate::Node].
+    /// The name is a [bytes::Bytes], not a [PathComponent], as we have use an
+    /// empty name in some places.
+    pub fn from_name_and_node(name: bytes::Bytes, n: crate::Node) -> Self {
+        match n {
+            crate::Node::Directory { digest, size } => Self {
+                node: Some(node::Node::Directory(DirectoryNode {
+                    name,
+                    digest: digest.into(),
+                    size,
+                })),
+            },
+            crate::Node::File {
+                digest,
+                size,
+                executable,
+            } => Self {
+                node: Some(node::Node::File(FileNode {
+                    name,
+                    digest: digest.into(),
+                    size,
+                    executable,
+                })),
+            },
+            crate::Node::Symlink { target } => Self {
+                node: Some(node::Node::Symlink(SymlinkNode {
+                    name,
+                    target: target.into(),
+                })),
+            },
+        }
+    }
 }
 
 impl StatBlobResponse {
@@ -409,65 +286,3 @@ impl StatBlobResponse {
         Ok(())
     }
 }
-
-/// Struct to hold the state of an iterator over all nodes of a Directory.
-///
-/// Internally, this keeps peekable Iterators over all three lists of a
-/// Directory message.
-pub struct DirectoryNodesIterator<'a> {
-    // directory: &Directory,
-    i_directories: Peekable<std::slice::Iter<'a, DirectoryNode>>,
-    i_files: Peekable<std::slice::Iter<'a, FileNode>>,
-    i_symlinks: Peekable<std::slice::Iter<'a, SymlinkNode>>,
-}
-
-/// looks at two elements implementing NamedNode, and returns true if "left
-/// is smaller / comes first".
-///
-/// Some(_) is preferred over None.
-fn left_name_lt_right<A: NamedNode, B: NamedNode>(left: Option<&A>, right: Option<&B>) -> bool {
-    match left {
-        // if left is None, right always wins
-        None => false,
-        Some(left_inner) => {
-            // left is Some.
-            match right {
-                // left is Some, right is None - left wins.
-                None => true,
-                Some(right_inner) => {
-                    // both are Some - compare the name.
-                    return left_inner.get_name() < right_inner.get_name();
-                }
-            }
-        }
-    }
-}
-
-impl Iterator for DirectoryNodesIterator<'_> {
-    type Item = node::Node;
-
-    // next returns the next node in the Directory.
-    // we peek at all three internal iterators, and pick the one with the
-    // smallest name, to ensure lexicographical ordering.
-    // The individual lists are already known to be sorted.
-    fn next(&mut self) -> Option<Self::Item> {
-        if left_name_lt_right(self.i_directories.peek(), self.i_files.peek()) {
-            // i_directories is still in the game, compare with symlinks
-            if left_name_lt_right(self.i_directories.peek(), self.i_symlinks.peek()) {
-                self.i_directories
-                    .next()
-                    .cloned()
-                    .map(node::Node::Directory)
-            } else {
-                self.i_symlinks.next().cloned().map(node::Node::Symlink)
-            }
-        } else {
-            // i_files is still in the game, compare with symlinks
-            if left_name_lt_right(self.i_files.peek(), self.i_symlinks.peek()) {
-                self.i_files.next().cloned().map(node::Node::File)
-            } else {
-                self.i_symlinks.next().cloned().map(node::Node::Symlink)
-            }
-        }
-    }
-}
diff --git a/tvix/castore/src/proto/tests/directory.rs b/tvix/castore/src/proto/tests/directory.rs
index 81b73a048d52..efbc4e9f2af1 100644
--- a/tvix/castore/src/proto/tests/directory.rs
+++ b/tvix/castore/src/proto/tests/directory.rs
@@ -1,7 +1,5 @@
-use crate::proto::{
-    node, Directory, DirectoryNode, FileNode, SymlinkNode, ValidateDirectoryError,
-    ValidateNodeError,
-};
+use crate::proto::{Directory, DirectoryError, DirectoryNode, FileNode, SymlinkNode};
+use crate::ValidateNodeError;
 
 use hex_literal::hex;
 
@@ -149,7 +147,7 @@ fn digest() {
 #[test]
 fn validate_empty() {
     let d = Directory::default();
-    assert_eq!(d.validate(), Ok(()));
+    assert!(crate::Directory::try_from(d).is_ok());
 }
 
 #[test]
@@ -157,18 +155,15 @@ fn validate_invalid_names() {
     {
         let d = Directory {
             directories: vec![DirectoryNode {
-                name: "".into(),
+                name: b"\0"[..].into(),
                 digest: DUMMY_DIGEST.to_vec().into(),
                 size: 42,
             }],
             ..Default::default()
         };
-        match d.validate().expect_err("must fail") {
-            ValidateDirectoryError::InvalidNode(n, ValidateNodeError::InvalidName(_)) => {
-                assert_eq!(n, b"")
-            }
-            _ => panic!("unexpected error"),
-        };
+
+        let e = crate::Directory::try_from(d).expect_err("must fail");
+        assert!(matches!(e, DirectoryError::InvalidName(_)));
     }
 
     {
@@ -180,12 +175,8 @@ fn validate_invalid_names() {
             }],
             ..Default::default()
         };
-        match d.validate().expect_err("must fail") {
-            ValidateDirectoryError::InvalidNode(n, ValidateNodeError::InvalidName(_)) => {
-                assert_eq!(n, b".")
-            }
-            _ => panic!("unexpected error"),
-        };
+        let e = crate::Directory::try_from(d).expect_err("must fail");
+        assert!(matches!(e, DirectoryError::InvalidName(_)));
     }
 
     {
@@ -198,12 +189,8 @@ fn validate_invalid_names() {
             }],
             ..Default::default()
         };
-        match d.validate().expect_err("must fail") {
-            ValidateDirectoryError::InvalidNode(n, ValidateNodeError::InvalidName(_)) => {
-                assert_eq!(n, b"..")
-            }
-            _ => panic!("unexpected error"),
-        };
+        let e = crate::Directory::try_from(d).expect_err("must fail");
+        assert!(matches!(e, DirectoryError::InvalidName(_)));
     }
 
     {
@@ -214,12 +201,8 @@ fn validate_invalid_names() {
             }],
             ..Default::default()
         };
-        match d.validate().expect_err("must fail") {
-            ValidateDirectoryError::InvalidNode(n, ValidateNodeError::InvalidName(_)) => {
-                assert_eq!(n, b"\x00")
-            }
-            _ => panic!("unexpected error"),
-        };
+        let e = crate::Directory::try_from(d).expect_err("must fail");
+        assert!(matches!(e, DirectoryError::InvalidName(_)));
     }
 
     {
@@ -230,12 +213,20 @@ fn validate_invalid_names() {
             }],
             ..Default::default()
         };
-        match d.validate().expect_err("must fail") {
-            ValidateDirectoryError::InvalidNode(n, ValidateNodeError::InvalidName(_)) => {
-                assert_eq!(n, b"foo/bar")
-            }
-            _ => panic!("unexpected error"),
+        let e = crate::Directory::try_from(d).expect_err("must fail");
+        assert!(matches!(e, DirectoryError::InvalidName(_)));
+    }
+
+    {
+        let d = Directory {
+            symlinks: vec![SymlinkNode {
+                name: bytes::Bytes::copy_from_slice("X".repeat(500).into_bytes().as_slice()),
+                target: "foo".into(),
+            }],
+            ..Default::default()
         };
+        let e = crate::Directory::try_from(d).expect_err("must fail");
+        assert!(matches!(e, DirectoryError::InvalidName(_)));
     }
 }
 
@@ -249,8 +240,8 @@ fn validate_invalid_digest() {
         }],
         ..Default::default()
     };
-    match d.validate().expect_err("must fail") {
-        ValidateDirectoryError::InvalidNode(_, ValidateNodeError::InvalidDigestLen(n)) => {
+    match crate::Directory::try_from(d).expect_err("must fail") {
+        DirectoryError::InvalidNode(_, ValidateNodeError::InvalidDigestLen(n)) => {
             assert_eq!(n, 2)
         }
         _ => panic!("unexpected error"),
@@ -276,15 +267,15 @@ fn validate_sorting() {
             ],
             ..Default::default()
         };
-        match d.validate().expect_err("must fail") {
-            ValidateDirectoryError::WrongSorting(s) => {
-                assert_eq!(s, b"a");
+        match crate::Directory::try_from(d).expect_err("must fail") {
+            DirectoryError::WrongSorting(s) => {
+                assert_eq!(s.as_ref(), b"a");
             }
             _ => panic!("unexpected error"),
         }
     }
 
-    // "a" exists twice, bad.
+    // "a" exists twice (same types), bad.
     {
         let d = Directory {
             directories: vec![
@@ -301,9 +292,31 @@ fn validate_sorting() {
             ],
             ..Default::default()
         };
-        match d.validate().expect_err("must fail") {
-            ValidateDirectoryError::DuplicateName(s) => {
-                assert_eq!(s, b"a");
+        match crate::Directory::try_from(d).expect_err("must fail") {
+            DirectoryError::DuplicateName(s) => {
+                assert_eq!(s.as_ref(), b"a");
+            }
+            _ => panic!("unexpected error"),
+        }
+    }
+
+    // "a" exists twice (different types), bad.
+    {
+        let d = Directory {
+            directories: vec![DirectoryNode {
+                name: "a".into(),
+                digest: DUMMY_DIGEST.to_vec().into(),
+                size: 42,
+            }],
+            symlinks: vec![SymlinkNode {
+                name: "a".into(),
+                target: "b".into(),
+            }],
+            ..Default::default()
+        };
+        match crate::Directory::try_from(d).expect_err("must fail") {
+            DirectoryError::DuplicateName(s) => {
+                assert_eq!(s.as_ref(), b"a");
             }
             _ => panic!("unexpected error"),
         }
@@ -327,7 +340,7 @@ fn validate_sorting() {
             ..Default::default()
         };
 
-        d.validate().expect("validate shouldn't error");
+        crate::Directory::try_from(d).expect("validate shouldn't error");
     }
 
     // [b, c] and [a] are both properly sorted.
@@ -352,101 +365,6 @@ fn validate_sorting() {
             ..Default::default()
         };
 
-        d.validate().expect("validate shouldn't error");
+        crate::Directory::try_from(d).expect("validate shouldn't error");
     }
 }
-
-#[test]
-fn validate_overflow() {
-    let d = Directory {
-        directories: vec![DirectoryNode {
-            name: "foo".into(),
-            digest: DUMMY_DIGEST.to_vec().into(),
-            size: u64::MAX,
-        }],
-        ..Default::default()
-    };
-
-    match d.validate().expect_err("must fail") {
-        ValidateDirectoryError::SizeOverflow => {}
-        _ => panic!("unexpected error"),
-    }
-}
-
-#[test]
-fn add_nodes_to_directory() {
-    let mut d = Directory {
-        ..Default::default()
-    };
-
-    d.add(node::Node::Directory(DirectoryNode {
-        name: "b".into(),
-        digest: DUMMY_DIGEST.to_vec().into(),
-        size: 1,
-    }));
-    d.add(node::Node::Directory(DirectoryNode {
-        name: "a".into(),
-        digest: DUMMY_DIGEST.to_vec().into(),
-        size: 1,
-    }));
-    d.add(node::Node::Directory(DirectoryNode {
-        name: "z".into(),
-        digest: DUMMY_DIGEST.to_vec().into(),
-        size: 1,
-    }));
-
-    d.add(node::Node::File(FileNode {
-        name: "f".into(),
-        digest: DUMMY_DIGEST.to_vec().into(),
-        size: 1,
-        executable: true,
-    }));
-    d.add(node::Node::File(FileNode {
-        name: "c".into(),
-        digest: DUMMY_DIGEST.to_vec().into(),
-        size: 1,
-        executable: true,
-    }));
-    d.add(node::Node::File(FileNode {
-        name: "g".into(),
-        digest: DUMMY_DIGEST.to_vec().into(),
-        size: 1,
-        executable: true,
-    }));
-
-    d.add(node::Node::Symlink(SymlinkNode {
-        name: "t".into(),
-        target: "a".into(),
-    }));
-    d.add(node::Node::Symlink(SymlinkNode {
-        name: "o".into(),
-        target: "a".into(),
-    }));
-    d.add(node::Node::Symlink(SymlinkNode {
-        name: "e".into(),
-        target: "a".into(),
-    }));
-
-    d.validate().expect("directory should be valid");
-}
-
-#[test]
-#[cfg_attr(not(debug_assertions), ignore)]
-#[should_panic = "name already exists in directories"]
-fn add_duplicate_node_to_directory_panics() {
-    let mut d = Directory {
-        ..Default::default()
-    };
-
-    d.add(node::Node::Directory(DirectoryNode {
-        name: "a".into(),
-        digest: DUMMY_DIGEST.to_vec().into(),
-        size: 1,
-    }));
-    d.add(node::Node::File(FileNode {
-        name: "a".into(),
-        digest: DUMMY_DIGEST.to_vec().into(),
-        size: 1,
-        executable: true,
-    }));
-}
diff --git a/tvix/castore/src/proto/tests/directory_nodes_iterator.rs b/tvix/castore/src/proto/tests/directory_nodes_iterator.rs
deleted file mode 100644
index 68f147a33210..000000000000
--- a/tvix/castore/src/proto/tests/directory_nodes_iterator.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-use crate::proto::Directory;
-use crate::proto::DirectoryNode;
-use crate::proto::FileNode;
-use crate::proto::NamedNode;
-use crate::proto::SymlinkNode;
-
-#[test]
-fn iterator() {
-    let d = Directory {
-        directories: vec![
-            DirectoryNode {
-                name: "c".into(),
-                ..DirectoryNode::default()
-            },
-            DirectoryNode {
-                name: "d".into(),
-                ..DirectoryNode::default()
-            },
-            DirectoryNode {
-                name: "h".into(),
-                ..DirectoryNode::default()
-            },
-            DirectoryNode {
-                name: "l".into(),
-                ..DirectoryNode::default()
-            },
-        ],
-        files: vec![
-            FileNode {
-                name: "b".into(),
-                ..FileNode::default()
-            },
-            FileNode {
-                name: "e".into(),
-                ..FileNode::default()
-            },
-            FileNode {
-                name: "g".into(),
-                ..FileNode::default()
-            },
-            FileNode {
-                name: "j".into(),
-                ..FileNode::default()
-            },
-        ],
-        symlinks: vec![
-            SymlinkNode {
-                name: "a".into(),
-                ..SymlinkNode::default()
-            },
-            SymlinkNode {
-                name: "f".into(),
-                ..SymlinkNode::default()
-            },
-            SymlinkNode {
-                name: "i".into(),
-                ..SymlinkNode::default()
-            },
-            SymlinkNode {
-                name: "k".into(),
-                ..SymlinkNode::default()
-            },
-        ],
-    };
-
-    // We keep this strings here and convert to string to make the comparison
-    // less messy.
-    let mut node_names: Vec<String> = vec![];
-
-    for node in d.nodes() {
-        node_names.push(String::from_utf8(node.get_name().to_vec()).unwrap());
-    }
-
-    assert_eq!(
-        vec!["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"],
-        node_names
-    );
-}
diff --git a/tvix/castore/src/proto/tests/mod.rs b/tvix/castore/src/proto/tests/mod.rs
index 8d903bacb6c5..74334029e84c 100644
--- a/tvix/castore/src/proto/tests/mod.rs
+++ b/tvix/castore/src/proto/tests/mod.rs
@@ -1,2 +1 @@
 mod directory;
-mod directory_nodes_iterator;
diff --git a/tvix/castore/src/tests/import.rs b/tvix/castore/src/tests/import.rs
index 72fb06aea877..32c2c363689f 100644
--- a/tvix/castore/src/tests/import.rs
+++ b/tvix/castore/src/tests/import.rs
@@ -2,14 +2,11 @@ use crate::blobservice::{self, BlobService};
 use crate::directoryservice;
 use crate::fixtures::*;
 use crate::import::fs::ingest_path;
-use crate::proto;
+use crate::Node;
 
 use tempfile::TempDir;
 
 #[cfg(target_family = "unix")]
-use std::os::unix::ffi::OsStrExt;
-
-#[cfg(target_family = "unix")]
 #[tokio::test]
 async fn symlink() {
     let blob_service = blobservice::from_addr("memory://").await.unwrap();
@@ -33,10 +30,9 @@ async fn symlink() {
     .expect("must succeed");
 
     assert_eq!(
-        proto::node::Node::Symlink(proto::SymlinkNode {
-            name: "doesntmatter".into(),
-            target: "/nix/store/somewhereelse".into(),
-        }),
+        Node::Symlink {
+            target: "/nix/store/somewhereelse".try_into().unwrap()
+        },
         root_node,
     )
 }
@@ -59,12 +55,11 @@ async fn single_file() {
     .expect("must succeed");
 
     assert_eq!(
-        proto::node::Node::File(proto::FileNode {
-            name: "root".into(),
-            digest: HELLOWORLD_BLOB_DIGEST.clone().into(),
+        Node::File {
+            digest: HELLOWORLD_BLOB_DIGEST.clone(),
             size: HELLOWORLD_BLOB_CONTENTS.len() as u64,
             executable: false,
-        }),
+        },
         root_node,
     );
 
@@ -95,17 +90,10 @@ async fn complicated() {
 
     // ensure root_node matched expectations
     assert_eq!(
-        proto::node::Node::Directory(proto::DirectoryNode {
-            name: tmpdir
-                .path()
-                .file_name()
-                .unwrap()
-                .as_bytes()
-                .to_owned()
-                .into(),
-            digest: DIRECTORY_COMPLICATED.digest().into(),
+        Node::Directory {
+            digest: DIRECTORY_COMPLICATED.digest().clone(),
             size: DIRECTORY_COMPLICATED.size(),
-        }),
+        },
         root_node,
     );
 
diff --git a/tvix/cli/Cargo.toml b/tvix/cli/Cargo.toml
index 2b624917ee87..27cd5a6b395c 100644
--- a/tvix/cli/Cargo.toml
+++ b/tvix/cli/Cargo.toml
@@ -15,25 +15,23 @@ tvix-store = { path = "../store", default-features = false, features = []}
 tvix-eval = { path = "../eval" }
 tvix-glue = { path = "../glue" }
 tvix-tracing = { path = "../tracing" }
-bytes = "1.4.0"
-clap = { version = "4.0", features = ["derive", "env"] }
-dirs = "4.0.0"
-rustyline = "10.0.0"
-rnix = "0.11.0"
-rowan = "*"
-smol_str = "0.2.0"
-thiserror = "1.0.38"
-tokio = "1.28.0"
-tracing = "0.1.40"
-tracing-indicatif = "0.3.6"
-rustc-hash = "2.0.0"
-mimalloc = "0.1.43"
-
-[dependencies.wu-manber]
-git = "https://github.com/tvlfyi/wu-manber.git"
+bytes = { workspace = true }
+clap = { workspace = true, features = ["derive", "env"] }
+dirs = { workspace = true }
+rustyline = { workspace = true }
+rnix = { workspace = true }
+rowan = { workspace = true }
+smol_str = { workspace = true }
+thiserror = { workspace = true }
+tokio = { workspace = true }
+tracing = { workspace = true }
+tracing-indicatif = { workspace = true }
+rustc-hash = { workspace = true }
+mimalloc = { workspace = true }
+wu-manber = { workspace = true }
 
 [dev-dependencies]
-expect-test = "1.5.0"
+expect-test = { workspace = true }
 
 [features]
 default = []
diff --git a/tvix/cli/default.nix b/tvix/cli/default.nix
index 90394f6d28c7..175e088c2dea 100644
--- a/tvix/cli/default.nix
+++ b/tvix/cli/default.nix
@@ -49,24 +49,29 @@ let
 
   # Constructs a Derivation invoking tvix-cli inside a build, ensures the
   # calculated tvix output path matches what's passed in externally.
-  mkNixpkgsEvalTest = attrpath: expectedPath:
-    let
-      name = "tvix-eval-test-${builtins.replaceStrings [".drv"] ["-drv"] attrpath}";
-    in
-    (pkgs.runCommand name { } ''
-      export SSL_CERT_FILE=/dev/null
-      TVIX_OUTPUT=$(${tvix-cli}/bin/tvix -E '(import ${pkgs.path} {}).${attrpath}')
-      EXPECTED='${/* the verbatim expected Tvix output: */ "=> \"${builtins.unsafeDiscardStringContext expectedPath}\" :: string"}'
+  mkNixpkgsEvalTest =
+    { attrPath ? null # An attribute that must already be accessible from `pkgs`. Should evaluate to a store path.
+    , expr ? null # A Nix expression that should evaluate to a store path.
+    , expectedPath # The expected store path that should match one of the above.
+    }:
+      assert lib.assertMsg (attrPath != null || expr != null) "Either 'attrPath' or 'expr' must be set.";
+      let
+        name = "tvix-eval-test-${builtins.replaceStrings [".drv"] ["-drv"] (if expr != null then "custom-expr" else attrPath)}";
+      in
+      (pkgs.runCommand name { } ''
+        export SSL_CERT_FILE=/dev/null
+        TVIX_OUTPUT=$(${tvix-cli}/bin/tvix --no-warnings -E '${if expr != null then expr else "(import ${pkgs.path} {}).${attrPath}"}')
+        EXPECTED='${/* the verbatim expected Tvix output: */ "=> \"${builtins.unsafeDiscardStringContext expectedPath}\" :: string"}'
 
-      echo "Tvix output: ''${TVIX_OUTPUT}"
-      if [ "$TVIX_OUTPUT" != "$EXPECTED" ]; then
-        echo "Correct would have been ''${EXPECTED}"
-        exit 1
-      fi
+        echo "Tvix output: ''${TVIX_OUTPUT}"
+        if [ "$TVIX_OUTPUT" != "$EXPECTED" ]; then
+          echo "Correct would have been ''${EXPECTED}"
+          exit 1
+        fi
 
-      echo "Output was correct."
-      touch $out
-    '');
+        echo "Output was correct."
+        touch $out
+      '');
 
 
   benchmarks = {
@@ -79,13 +84,24 @@ let
   };
 
   evalTests = {
-    eval-nixpkgs-stdenv-drvpath = (mkNixpkgsEvalTest "stdenv.drvPath" pkgs.stdenv.drvPath);
-    eval-nixpkgs-stdenv-outpath = (mkNixpkgsEvalTest "stdenv.outPath" pkgs.stdenv.outPath);
-    eval-nixpkgs-hello-outpath = (mkNixpkgsEvalTest "hello.outPath" pkgs.hello.outPath);
-    eval-nixpkgs-firefox-outpath = (mkNixpkgsEvalTest "firefox.outPath" pkgs.firefox.outPath);
-    eval-nixpkgs-firefox-drvpath = (mkNixpkgsEvalTest "firefox.drvPath" pkgs.firefox.drvPath);
-    eval-nixpkgs-cross-stdenv-outpath = (mkNixpkgsEvalTest "pkgsCross.aarch64-multiplatform.stdenv.outPath" pkgs.pkgsCross.aarch64-multiplatform.stdenv.outPath);
-    eval-nixpkgs-cross-hello-outpath = (mkNixpkgsEvalTest "pkgsCross.aarch64-multiplatform.hello.outPath" pkgs.pkgsCross.aarch64-multiplatform.hello.outPath);
+    eval-nixpkgs-stdenv-drvpath = (mkNixpkgsEvalTest { attrPath = "stdenv.drvPath"; expectedPath = pkgs.stdenv.drvPath; });
+    eval-nixpkgs-stdenv-outpath = (mkNixpkgsEvalTest { attrPath = "stdenv.outPath"; expectedPath = pkgs.stdenv.outPath; });
+    eval-nixpkgs-hello-outpath = (mkNixpkgsEvalTest { attrPath = "hello.outPath"; expectedPath = pkgs.hello.outPath; });
+    eval-nixpkgs-firefox-outpath = (mkNixpkgsEvalTest { attrPath = "firefox.outPath"; expectedPath = pkgs.firefox.outPath; });
+    eval-nixpkgs-firefox-drvpath = (mkNixpkgsEvalTest { attrPath = "firefox.drvPath"; expectedPath = pkgs.firefox.drvPath; });
+    eval-nixpkgs-cross-stdenv-outpath = (mkNixpkgsEvalTest { attrPath = "pkgsCross.aarch64-multiplatform.stdenv.outPath"; expectedPath = pkgs.pkgsCross.aarch64-multiplatform.stdenv.outPath; });
+    eval-nixpkgs-cross-hello-outpath = (mkNixpkgsEvalTest { attrPath = "pkgsCross.aarch64-multiplatform.hello.outPath"; expectedPath = pkgs.pkgsCross.aarch64-multiplatform.hello.outPath; });
+    # Our CI runner currently uses Nix version lower than 2.12, which means it uses the old JSON library.
+    # The NixOS docs generate a JSON file with all the NixOS options, and so output is different between Tvix (and Nix 2.12+) and our CI runner's Nix version,
+    # so we disable the NixOS docs generation for now. TODO(kranzes): Re-enable NixOS docs once the CI runner is using a newer Nix version.
+    eval-nixpkgs-nixos-gnome-installer-drvpath = (mkNixpkgsEvalTest {
+      expr = "(import ${pkgs.path}/nixos/release.nix { configuration = { documentation.nixos.enable = (import ${pkgs.path}/lib).mkForce false; }; }).iso_gnome.${pkgs.system}.drvPath";
+      expectedPath = (import "${pkgs.path}/nixos/release.nix" { configuration.documentation.nixos.enable = lib.mkForce false; }).iso_gnome.${pkgs.system}.drvPath;
+    });
+    eval-nixpkgs-nixos-gnome-installer-outpath = (mkNixpkgsEvalTest {
+      expr = "(import ${pkgs.path}/nixos/release.nix { configuration = { documentation.nixos.enable = (import ${pkgs.path}/lib).mkForce false; }; }).iso_gnome.${pkgs.system}.outPath";
+      expectedPath = (import "${pkgs.path}/nixos/release.nix" { configuration.documentation.nixos.enable = lib.mkForce false; }).iso_gnome.${pkgs.system}.outPath;
+    });
   };
 in
 {
@@ -94,5 +110,5 @@ in
   };
 
   # Expose benchmarks and evalTests as standard CI targets.
-  passthru = benchmarks // evalTests;
+  passthru = previousAttrs.passthru // benchmarks // evalTests;
 })
diff --git a/tvix/cli/src/args.rs b/tvix/cli/src/args.rs
index a4d1bcae157e..36f9a6a262dd 100644
--- a/tvix/cli/src/args.rs
+++ b/tvix/cli/src/args.rs
@@ -4,6 +4,14 @@ use clap::Parser;
 use tracing::Level;
 use tvix_store::utils::ServiceUrlsMemory;
 
+/// Provides a CLI interface to trigger evaluation using tvix-eval.
+///
+/// Uses configured tvix-[ca]store and tvix-build components,
+/// and by default a set of builtins similar to these present in Nix.
+///
+/// None of the stores available add to the local `/nix/store` location.
+///
+/// The CLI interface is not stable and subject to change.
 #[derive(Parser, Clone)]
 pub struct Args {
     /// A global log level to use when printing logs.
@@ -64,4 +72,15 @@ pub struct Args {
 
     #[arg(long, env, default_value = "dummy://")]
     pub build_service_addr: String,
+
+    /// An optional path in which Derivations encountered during evaluation
+    /// are dumped into, after evaluation. If it doesn't exist, the directory is created.
+    ///
+    /// Files dumped there are named like they would show up in `/nix/store`,
+    /// if produced by Nix. Existing files are not overwritten.
+    ///
+    /// This is only for debugging and diffing purposes for post-eval inspection;
+    /// Tvix does not read from these.
+    #[clap(long)]
+    pub drv_dumpdir: Option<PathBuf>,
 }
diff --git a/tvix/cli/src/lib.rs b/tvix/cli/src/lib.rs
index 2351da13a771..beb4c505207c 100644
--- a/tvix/cli/src/lib.rs
+++ b/tvix/cli/src/lib.rs
@@ -112,10 +112,10 @@ pub fn evaluate(
             eval_builder = eval_builder.add_builtins(impure_builtins());
             eval_builder = add_derivation_builtins(eval_builder, Rc::clone(&tvix_store_io));
             eval_builder = add_fetcher_builtins(eval_builder, Rc::clone(&tvix_store_io));
-            eval_builder = add_import_builtins(eval_builder, tvix_store_io);
-            eval_builder = configure_nix_path(eval_builder, &args.nix_search_path);
+            eval_builder = add_import_builtins(eval_builder, Rc::clone(&tvix_store_io));
         }
     };
+    eval_builder = configure_nix_path(eval_builder, &args.nix_search_path);
 
     if let Some(source_map) = source_map {
         eval_builder = eval_builder.with_source_map(source_map);
@@ -173,6 +173,21 @@ pub fn evaluate(
         }
     }
 
+    if let Some(dumpdir) = &args.drv_dumpdir {
+        // Dump all known derivations files to `dumpdir`.
+        std::fs::create_dir_all(dumpdir).expect("failed to create drv dumpdir");
+        tvix_store_io
+            .known_paths
+            .borrow()
+            .get_derivations()
+            // Skip already dumped derivations.
+            .filter(|(drv_path, _)| !dumpdir.join(drv_path.to_string()).exists())
+            .for_each(|(drv_path, drv)| {
+                std::fs::write(dumpdir.join(drv_path.to_string()), drv.to_aterm_bytes())
+                    .expect("failed to write drv to dumpdir");
+            })
+    }
+
     Ok(EvalResult {
         globals,
         value: result.value,
diff --git a/tvix/cli/tests/repl.rs b/tvix/cli/tests/repl.rs
index c6644330976e..7b9b9e34550a 100644
--- a/tvix/cli/tests/repl.rs
+++ b/tvix/cli/tests/repl.rs
@@ -9,7 +9,11 @@ macro_rules! test_repl {
         #[test]
         fn $name() {
             let tokio_runtime = tokio::runtime::Runtime::new().unwrap();
-            let args = tvix_cli::Args::parse_from(Vec::<OsString>::new());
+            let args = tvix_cli::Args::parse_from(vec![
+              OsString::from("tvix"),
+              OsString::from("--nix-search-path"),
+              OsString::from("nixpkgs=/tmp"),
+            ]);
             let mut repl = tvix_cli::Repl::new(init_io_handle(&tokio_runtime, &args), &args);
             $({
                 let result = repl.send($send.into());
@@ -83,3 +87,12 @@ test_repl!(explain() {
         => a 2-item attribute set
     "#]];
 });
+
+test_repl!(reference_nix_path() {
+    "<nixpkgs>" => expect![[r#"
+        => /tmp :: path
+    "#]];
+    "<nixpkgs>" => expect![[r#"
+        => /tmp :: path
+    "#]];
+});
diff --git a/tvix/clippy.toml b/tvix/clippy.toml
index be7709684c6f..31952cc80ad1 100644
--- a/tvix/clippy.toml
+++ b/tvix/clippy.toml
@@ -1,6 +1,8 @@
 # prevents a false-positive lint on our types containing bytes::Bytes
 # https://rust-lang.github.io/rust-clippy/master/index.html#/mutable_key_type
 ignore-interior-mutability = [
+  # make sure to specify the originating type name, not re-exports!
   "bytes::Bytes",
-  "tvix_castore::digests::B3Digest"
+  "tvix_castore::digests::B3Digest",
+  "tvix_castore::path::component::PathComponent"
 ]
diff --git a/tvix/crate-hashes.json b/tvix/crate-hashes.json
index 11679cb2ebdd..42b46c2515d2 100644
--- a/tvix/crate-hashes.json
+++ b/tvix/crate-hashes.json
@@ -1,3 +1,4 @@
 {
+  "git+https://github.com/liufuyang/bigtable_rs?rev=1818355a5373a5bc2c84287e3a4e3807154ac8ef#0.2.10": "0mn6iw1z7gdxbarsqiwscbdr25nplwlvzs0rs51vgnnjfsnbgl6q",
   "git+https://github.com/tvlfyi/wu-manber.git#wu-manber@0.1.0": "1zhk83lbq99xzyjwphv2qrb8f8qgfqwa5bbbvyzm0z0bljsjv0pd"
 }
\ No newline at end of file
diff --git a/tvix/default.nix b/tvix/default.nix
index f8aa47a108c5..8e09c5b88346 100644
--- a/tvix/default.nix
+++ b/tvix/default.nix
@@ -18,6 +18,7 @@ let
         (k:
           (lib.nameValuePair "${crates.internal.crates.${k}.crateName}-${crates.internal.crates.${k}.version}" crates.internal.crates.${k}.src.outputHash)
         ) [
+        "bigtable_rs"
         "wu-manber"
       ]);
   };
@@ -40,59 +41,6 @@ in
 {
   inherit crates protos;
 
-  # Run crate2nix generate, ensure the output doesn't differ afterwards
-  # (and doesn't fail).
-  #
-  # Currently this re-downloads every crate every time
-  # crate2nix-check (but not crate2nix) is built.
-  # TODO(amjoseph): be less wasteful with bandwidth.
-  #
-  crate2nix-check =
-    let
-      outputHashAlgo = "sha256";
-    in
-    pkgs.stdenv.mkDerivation {
-      inherit src;
-
-      # Important: we include the hash of all Cargo related files in the derivation name.
-      # This forces the FOD to be rebuilt/re-verified whenever one of them changes.
-      name = "tvix-crate2nix-check-" + builtins.substring 0 8 (builtins.hashString "sha256"
-        (lib.concatMapStrings (f: builtins.hashFile "sha256" f)
-          ([ ./Cargo.toml ./Cargo.lock ] ++ (map (m: ./. + "/${m}/Cargo.toml") (lib.importTOML ./Cargo.toml).workspace.members))
-        )
-      );
-
-      nativeBuildInputs = with pkgs; [ git cacert cargo ];
-      buildPhase = ''
-        export CARGO_HOME=$(mktemp -d)
-
-        # The following command can be omitted, in which case
-        # crate2nix-generate will run it automatically, but won't show the
-        # output, which makes it look like the build is somehow "stuck" for a
-        # minute or two.
-        cargo metadata > /dev/null
-
-        ${pkgs.crate2nix}/bin/crate2nix generate --all-features
-        ${pkgs.treefmt}/bin/treefmt Cargo.nix \
-          --no-cache \
-          --on-unmatched=debug \
-          --config-file=${depot.tools.depotfmt.config} \
-          --tree-root=.
-
-        # technically unnecessary, but provides more-helpful output in case of error
-        diff -ur Cargo.nix ${src}/Cargo.nix
-
-        # the FOD hash will check that the (re-)generated Cargo.nix matches the committed Cargo.nix
-        cp Cargo.nix $out
-      '';
-
-      # This is an FOD in order to allow `cargo` to perform network access.
-      outputHashMode = "flat";
-      inherit outputHashAlgo;
-      outputHash = builtins.hashFile outputHashAlgo ./Cargo.nix;
-      env.SSL_CERT_FILE = "${pkgs.cacert.out}/etc/ssl/certs/ca-bundle.crt";
-    };
-
   # Provide the Tvix logo in both .webp and .png format.
   logo = pkgs.runCommand "logo"
     {
@@ -158,12 +106,22 @@ in
     buildPhase = "cargo clippy --tests --all-features --benches --examples -- -Dwarnings | tee $out";
   };
 
+  crate2nix-check =
+    let
+      crate2nix-check = depot.tvix.utils.mkCrate2nixCheck ./Cargo.nix;
+    in
+    crate2nix-check.command.overrideAttrs {
+      meta.ci.extraSteps = {
+        inherit crate2nix-check;
+      };
+    };
+
   meta.ci.targets = [
     "clippy"
-    "crate2nix-check"
     "shell"
     "rust-docs"
+    "crate2nix-check"
   ];
 
-  utils = import ./utils.nix { inherit lib depot; };
+  utils = import ./utils.nix { inherit pkgs lib depot; };
 }
diff --git a/tvix/docs/src/TODO.md b/tvix/docs/src/TODO.md
index debad55a4f7c..af558c9580fe 100644
--- a/tvix/docs/src/TODO.md
+++ b/tvix/docs/src/TODO.md
@@ -25,20 +25,6 @@ sure noone is working on this, or has some specific design in mind already.
    with a different level of `--strict`, but the toplevel doc-comment suggests
    its generic?
 
-### castore
-  - The `Directory` struct is currently being generated from the protobuf definitions.
-    We should change this, have `Directory` be our own type (holding a `B3Digest`),
-    rather than a `Vec<u8>`, and move impls to there.
-    The protobuf struct can stay in `castore/proto`, but should only be used
-    when converting to/from Protobuf definitons:
-     - gRPC client/server impls
-     - implementations that use protobuf as a internal data format
-     - `digest()` implementation of our stricter `Directory` struct
-
-    The traits should use our stricter type. This would allow removing a lot of
-    the checks we currently have when converting from `Vec<u8>` to `B3Digest` in
-    various places.
-
 ### Correctness > Performance
 A lot of the Nix behaviour isn't well documented out, and before going too deep
 into performance optimizations, we need to ensure we properly grasped all hidden
@@ -158,8 +144,14 @@ there are ruled out, adding other types of builders might be interesting.
  - gVisor
  - Cloud Hypervisor (using similar technique as `//tvix//boot`).
 
-Long-term, we want to extend traits and gRPC protocol to expose more telemetry,
-logs etc, but this is something requiring a lot of designing.
+Long-term, we want to extend traits and gRPC protocol.
+This requires some more designing. Some goals:
+
+ - use stricter castore types (and maybe stricter build types) instead of
+   proto types, add conversion code where necessary
+ - (more granular) control while a build is happening
+ - expose more telemetry and logs
+
 
 ### Store composition
  - Combinators: list-by-priority, first-come-first-serve, cache
@@ -220,12 +212,6 @@ logs etc, but this is something requiring a lot of designing.
    (https://github.com/apache/arrow-rs/pull/5650), there's no support on the
    local filesystem yet. We'd need to add support to this (through xattrs).
 
-### DirectoryService
- - Add an `object_store` variant, storing a Directory *closure* keyed by the
-   root `Directory` digest. This won't allow indexing intermediate Directory
-   nodes, but once we have `DirectoryService` composition, it shouldn't be an
-   issue.
-
 ### PathInfoService
  - sqlite backend (different schema than the Nix one, we need the root nodes data!)
 
diff --git a/tvix/docs/src/contributing/gerrit.md b/tvix/docs/src/contributing/gerrit.md
index 71877d9d4aab..3644e8cb0200 100644
--- a/tvix/docs/src/contributing/gerrit.md
+++ b/tvix/docs/src/contributing/gerrit.md
@@ -10,14 +10,13 @@ StackOverflow account.
 
 Additionally if you would prefer not to use an SSO option or wish to have a
 backup authentication strategy in the event of downed server or otherwise, we
-recommend setting up a TVL-specific LDAP account. Do note that our IdP
-(Keycloak) sometimes has [problems doing SSO with GitHub][github-problematic],
-so you might get an “unexpected error” while trying to sign in with GitHub;
-that error is not your fault. You can create that account by following these
-instructions:
+recommend setting up a TVL-specific LDAP account.
+
+You can create such an account by following these instructions:
 
 1. Checkout [TVL’s monorepo][check-out-monorepo] if you haven’t already
-2. Be a member of `#tvl` on [hackint][], a communication network.
+2. Be a member of `#tvix-dev` (and/or `#tvl`) on [hackint][], a communication
+   network.
 3. Generate a user entry using [//web/pwcrypt](https://signup.tvl.fyi/).
 4. Commit that generated user entry to our LDAP server configuration in
    [ops/users][ops-users] (for an example, see:
@@ -106,7 +105,6 @@ system, as well as the other [attention set rules][attention-set-rules].
 [ops-users]: https://code.tvl.fyi/tree/ops/users/default.nix
 [hackint]: https://hackint.org
 [github-diff]: https://gerrit.wikimedia.org/r/Documentation/intro-gerrit-walkthrough-github.html
-[github-problematic]: https://b.tvl.fyi/issues/201
 [attention sets]: https://gerrit-review.googlesource.com/Documentation/user-attention-set.html
 [attention-set-rules]: https://gerrit-review.googlesource.com/Documentation/user-attention-set.html#_rules
 [^keycloak]: [^amend]: `git commit --amend`
diff --git a/tvix/eval/Cargo.toml b/tvix/eval/Cargo.toml
index 9637165e8500..c99bea4f7125 100644
--- a/tvix/eval/Cargo.toml
+++ b/tvix/eval/Cargo.toml
@@ -8,41 +8,42 @@ name = "tvix_eval"
 
 [dependencies]
 builtin-macros = { path = "./builtin-macros", package = "tvix-eval-builtin-macros" }
-bytes = "1.4.0"
-bstr = { version = "1.8.0", features = ["serde"] }
-codemap = "0.1.3"
-codemap-diagnostic = "0.1.1"
-dirs = "4.0.0"
-genawaiter = { version = "0.99.1", default-features = false }
-imbl = { version = "3.0", features = [ "serde" ] }
-itertools = "0.12.0"
-lazy_static = "1.4.0"
-lexical-core = { version = "0.8.5", features = ["format", "parse-floats"] }
-os_str_bytes = { version = "6.3", features = ["conversions"] }
-path-clean = "0.1"
-proptest = { version = "1.3.0", default-features = false, features = ["std", "alloc", "tempfile"], optional = true }
-regex = "1.6.0"
-rnix = "0.11.0"
-rowan = "*" # pinned by rnix
-serde = { version = "1.0", features = [ "rc", "derive" ] }
-serde_json = "1.0"
-smol_str = "0.2.0"
-tabwriter = "1.2"
-test-strategy = { version = "0.2.1", optional = true }
+bytes = { workspace = true }
+bstr = { workspace = true, features = ["serde"] }
+codemap = { workspace = true }
+codemap-diagnostic = { workspace = true }
+dirs = { workspace = true }
+genawaiter = { workspace = true }
+itertools = { workspace = true }
+lazy_static = { workspace = true }
+lexical-core = { workspace = true, features = ["format", "parse-floats"] }
+os_str_bytes = { workspace = true, features = ["conversions"] }
+path-clean = { workspace = true }
+proptest = { workspace = true, features = ["std", "alloc", "tempfile"], optional = true }
+regex = { workspace = true }
+rnix = { workspace = true }
+rowan = { workspace = true } # pinned by rnix
+serde = { workspace = true, features = ["rc", "derive"] }
+serde_json = { workspace = true }
+smol_str = { workspace = true }
+tabwriter = { workspace = true }
+test-strategy = { workspace = true, optional = true }
 toml = "0.6.0"
-sha2 = "0.10.8"
-sha1 = "0.10.6"
-md-5 = "0.10.6"
-data-encoding = "2.6.0"
-rustc-hash = "2.0.0"
+sha2 = { workspace = true }
+sha1 = { workspace = true }
+md-5 = { workspace = true }
+data-encoding = { workspace = true }
+rustc-hash = { workspace = true }
+nohash-hasher = { workspace = true }
+vu128 = { workspace = true }
 
 [dev-dependencies]
-criterion = "0.5"
-itertools = "0.12.0"
-mimalloc = "0.1.43"
-pretty_assertions = "1.2.1"
-rstest = "0.19.0"
-tempfile = "3.3.0"
+criterion = { workspace = true }
+itertools = { workspace = true }
+mimalloc = { workspace = true }
+pretty_assertions = { workspace = true }
+rstest = { workspace = true }
+tempfile = { workspace = true }
 
 [features]
 default = ["impure", "arbitrary", "nix_tests"]
@@ -55,7 +56,7 @@ nix_tests = []
 impure = []
 
 # Enables Arbitrary impls for internal types (required to run tests)
-arbitrary = ["proptest", "test-strategy", "imbl/proptest"]
+arbitrary = ["proptest", "test-strategy"]
 
 # Don't leak strings (enable this if you care about peak memory usage of eval)
 #
diff --git a/tvix/eval/builtin-macros/Cargo.toml b/tvix/eval/builtin-macros/Cargo.toml
index 3a35ea12a0c0..0696d742b342 100644
--- a/tvix/eval/builtin-macros/Cargo.toml
+++ b/tvix/eval/builtin-macros/Cargo.toml
@@ -5,9 +5,9 @@ authors = [ "Griffin Smith <root@gws.fyi>" ]
 edition = "2021"
 
 [dependencies]
-syn = { version = "1.0.57", features = ["full", "parsing", "printing", "visit", "visit-mut", "extra-traits"] }
-quote = "1.0.8"
-proc-macro2 = "1"
+syn = { version = "1.0.109", features = ["full", "parsing", "printing", "visit", "visit-mut", "extra-traits"] }
+quote = { workspace = true }
+proc-macro2 = { workspace = true }
 
 [lib]
 proc-macro = true
diff --git a/tvix/eval/default.nix b/tvix/eval/default.nix
index 9dd5875f85a5..9370c81ced1c 100644
--- a/tvix/eval/default.nix
+++ b/tvix/eval/default.nix
@@ -8,9 +8,9 @@
   testInputs = [ pkgs.nix ];
 }).overrideAttrs (old: rec {
   meta.ci.targets = lib.filter (x: lib.hasPrefix "with-features" x || x == "no-features") (lib.attrNames passthru);
-  passthru = depot.tvix.utils.mkFeaturePowerset {
+  passthru = old.passthru // (depot.tvix.utils.mkFeaturePowerset {
     inherit (old) crateName;
     features = [ "nix_tests" ];
     override.testInputs = [ pkgs.nix ];
-  };
+  });
 })
diff --git a/tvix/eval/src/builtins/mod.rs b/tvix/eval/src/builtins/mod.rs
index e961738e3653..f130bbc5b15f 100644
--- a/tvix/eval/src/builtins/mod.rs
+++ b/tvix/eval/src/builtins/mod.rs
@@ -6,7 +6,6 @@
 use bstr::{ByteSlice, ByteVec};
 use builtin_macros::builtins;
 use genawaiter::rc::Gen;
-use imbl::OrdMap;
 use regex::Regex;
 use std::cmp::{self, Ordering};
 use std::collections::BTreeMap;
@@ -85,7 +84,6 @@ mod pure_builtins {
     use std::ffi::OsString;
 
     use bstr::{BString, ByteSlice, B};
-    use imbl::Vector;
     use itertools::Itertools;
     use os_str_bytes::OsStringBytes;
     use rustc_hash::FxHashSet;
@@ -246,7 +244,7 @@ mod pure_builtins {
 
     #[builtin("concatLists")]
     async fn builtin_concat_lists(co: GenCo, lists: Value) -> Result<Value, ErrorKind> {
-        let mut out = imbl::Vector::new();
+        let mut out = Vec::new();
 
         for value in lists.to_list()? {
             let list = try_value!(generators::request_force(&co, value).await).to_list()?;
@@ -259,7 +257,7 @@ mod pure_builtins {
     #[builtin("concatMap")]
     async fn builtin_concat_map(co: GenCo, f: Value, list: Value) -> Result<Value, ErrorKind> {
         let list = list.to_list()?;
-        let mut res = imbl::Vector::new();
+        let mut res = Vec::new();
         for val in list {
             let out = generators::request_call_with(&co, f.clone(), [val]).await;
             let out = try_value!(generators::request_force(&co, out).await);
@@ -387,13 +385,13 @@ mod pure_builtins {
     #[builtin("filter")]
     async fn builtin_filter(co: GenCo, pred: Value, list: Value) -> Result<Value, ErrorKind> {
         let list: NixList = list.to_list()?;
-        let mut out = imbl::Vector::new();
+        let mut out = Vec::new();
 
         for value in list {
             let result = generators::request_call_with(&co, pred.clone(), [value.clone()]).await;
             let verdict = try_value!(generators::request_force(&co, result).await);
             if verdict.as_bool()? {
-                out.push_back(value);
+                out.push(value);
             }
         }
 
@@ -481,7 +479,7 @@ mod pure_builtins {
 
         let operator = attrs.select_required("operator")?;
 
-        let mut res = imbl::Vector::new();
+        let mut res = Vec::new();
         let mut done_keys: Vec<Value> = vec![];
 
         while let Some(val) = work_set.pop_front() {
@@ -499,7 +497,7 @@ mod pure_builtins {
                 continue;
             }
 
-            res.push_back(val.clone());
+            res.push(val.clone());
 
             let op_result = generators::request_force(
                 &co,
@@ -520,14 +518,18 @@ mod pure_builtins {
         #[lazy] generator: Value,
         length: Value,
     ) -> Result<Value, ErrorKind> {
-        let mut out = imbl::Vector::<Value>::new();
         let len = length.as_int()?;
+        let mut out = Vec::with_capacity(
+            len.try_into()
+                .map_err(|_| ErrorKind::Abort(format!("can not create list of size {}", len)))?,
+        );
+
         // the best span we can get…
         let span = generators::request_span(&co).await;
 
         for i in 0..len {
             let val = Value::Thunk(Thunk::new_suspended_call(generator.clone(), i.into(), span));
-            out.push_back(val);
+            out.push(val);
         }
 
         Ok(Value::List(out.into()))
@@ -548,7 +550,7 @@ mod pure_builtins {
 
     #[builtin("groupBy")]
     async fn builtin_group_by(co: GenCo, f: Value, list: Value) -> Result<Value, ErrorKind> {
-        let mut res: BTreeMap<NixString, imbl::Vector<Value>> = BTreeMap::new();
+        let mut res: BTreeMap<NixString, Vec<Value>> = BTreeMap::new();
         for val in list.to_list()? {
             let key = try_value!(
                 generators::request_force(
@@ -559,7 +561,7 @@ mod pure_builtins {
             )
             .to_str()?;
 
-            res.entry(key).or_default().push_back(val);
+            res.entry(key).or_default().push(val);
         }
         Ok(Value::attrs(NixAttrs::from_iter(
             res.into_iter()
@@ -625,7 +627,7 @@ mod pure_builtins {
         let elements = groups
             .into_iter()
             .map(|(key, group)| {
-                let mut outputs: Vector<NixString> = Vector::new();
+                let mut outputs: Vec<NixString> = Vec::new();
                 let mut is_path = false;
                 let mut all_outputs = false;
 
@@ -638,7 +640,7 @@ mod pure_builtins {
 
                         NixContextElement::Single { name, derivation } => {
                             debug_assert!(derivation == key, "Unexpected group containing mixed keys, expected: {:?}, encountered {:?}", key, derivation);
-                            outputs.push_back(name.clone().into());
+                            outputs.push(name.clone().into());
                         }
 
                         NixContextElement::Derivation(drv_path) => {
@@ -665,7 +667,7 @@ mod pure_builtins {
                     vec_attrs.push(("outputs", Value::List(outputs
                                 .into_iter()
                                 .map(|s| s.into())
-                                .collect::<Vector<Value>>()
+                                .collect::<Vec<Value>>()
                                 .into()
                     )));
                 }
@@ -792,7 +794,7 @@ mod pure_builtins {
         }
         let mut right_keys = right_set.keys();
 
-        let mut out: OrdMap<NixString, Value> = OrdMap::new();
+        let mut out: BTreeMap<NixString, Value> = BTreeMap::new();
 
         // Both iterators have at least one entry
         let mut left = left_keys.next().unwrap();
@@ -973,15 +975,16 @@ mod pure_builtins {
     }
 
     #[builtin("map")]
-    async fn builtin_map(co: GenCo, #[lazy] f: Value, list: Value) -> Result<Value, ErrorKind> {
-        let mut out = imbl::Vector::<Value>::new();
+    async fn builtin_map(co: GenCo, #[lazy] f: Value, list_val: Value) -> Result<Value, ErrorKind> {
+        let list = list_val.to_list()?;
+        let mut out = Vec::with_capacity(list.len());
 
         // the best span we can get…
         let span = generators::request_span(&co).await;
 
-        for val in list.to_list()? {
+        for val in list {
             let result = Value::Thunk(Thunk::new_suspended_call(f.clone(), val, span));
-            out.push_back(result)
+            out.push(result)
         }
 
         Ok(Value::List(out.into()))
@@ -994,7 +997,7 @@ mod pure_builtins {
         attrs: Value,
     ) -> Result<Value, ErrorKind> {
         let attrs = attrs.to_attrs()?;
-        let mut out = imbl::OrdMap::new();
+        let mut out: BTreeMap<NixString, Value> = BTreeMap::new();
 
         // the best span we can get…
         let span = generators::request_span(&co).await;
@@ -1040,7 +1043,7 @@ mod pure_builtins {
                         // and then a compressor name will be extracted from that.
                         grp.map(|g| Value::from(g.as_str())).unwrap_or(Value::Null)
                     })
-                    .collect::<imbl::Vector<Value>>()
+                    .collect::<Vec<Value>>()
                     .into(),
             )),
             None => Ok(Value::Null),
@@ -1083,17 +1086,17 @@ mod pure_builtins {
 
     #[builtin("partition")]
     async fn builtin_partition(co: GenCo, pred: Value, list: Value) -> Result<Value, ErrorKind> {
-        let mut right: imbl::Vector<Value> = Default::default();
-        let mut wrong: imbl::Vector<Value> = Default::default();
+        let mut right: Vec<Value> = Default::default();
+        let mut wrong: Vec<Value> = Default::default();
 
         let list: NixList = list.to_list()?;
         for elem in list {
             let result = generators::request_call_with(&co, pred.clone(), [elem.clone()]).await;
 
             if try_value!(generators::request_force(&co, result).await).as_bool()? {
-                right.push_back(elem);
+                right.push(elem);
             } else {
-                wrong.push_back(elem);
+                wrong.push(elem);
             };
         }
 
@@ -1248,12 +1251,12 @@ mod pure_builtins {
         let re = Regex::new(re.to_str()?).unwrap();
         let mut capture_locations = re.capture_locations();
         let num_captures = capture_locations.len();
-        let mut ret = imbl::Vector::new();
+        let mut ret = Vec::new();
         let mut pos = 0;
 
         while let Some(thematch) = re.captures_read_at(&mut capture_locations, text, pos) {
             // push the unmatched characters preceding the match
-            ret.push_back(Value::from(NixString::new_inherit_context_from(
+            ret.push(Value::from(NixString::new_inherit_context_from(
                 &s,
                 &text[pos..thematch.start()],
             )));
@@ -1262,7 +1265,7 @@ mod pure_builtins {
             // group in the regex, containing the characters
             // matched by that capture group, or null if no match.
             // We skip capture 0; it represents the whole match.
-            let v: imbl::Vector<Value> = (1..num_captures)
+            let v: Vec<Value> = (1..num_captures)
                 .map(|i| capture_locations.get(i))
                 .map(|o| {
                     o.map(|(start, end)| {
@@ -1273,7 +1276,7 @@ mod pure_builtins {
                     .unwrap_or(Value::Null)
                 })
                 .collect();
-            ret.push_back(Value::List(NixList::from(v)));
+            ret.push(Value::List(NixList::from(v)));
             if pos == text.len() {
                 break;
             }
@@ -1283,7 +1286,7 @@ mod pure_builtins {
         // push the unmatched characters following the last match
         // Here, a surprising thing happens: we silently discard the original
         // context. This is as intended, Nix does the same.
-        ret.push_back(Value::from(&text[pos..]));
+        ret.push(Value::from(&text[pos..]));
 
         Ok(Value::List(NixList::from(ret)))
     }
diff --git a/tvix/eval/src/chunk.rs b/tvix/eval/src/chunk.rs
index e9c65db72590..2a5446a782ed 100644
--- a/tvix/eval/src/chunk.rs
+++ b/tvix/eval/src/chunk.rs
@@ -1,9 +1,10 @@
+use crate::opcode::{CodeIdx, ConstantIdx, Op, OpArg};
+use crate::value::Value;
+use crate::{CoercionKind, SourceCode};
 use std::io::Write;
-use std::ops::{Index, IndexMut};
 
-use crate::opcode::{CodeIdx, ConstantIdx, OpCode};
-use crate::value::Value;
-use crate::SourceCode;
+/// Maximum size of a u64 encoded in the vu128 varint encoding.
+const U64_VARINT_SIZE: usize = 9;
 
 /// Represents a source location from which one or more operations
 /// were compiled.
@@ -30,39 +31,69 @@ struct SourceSpan {
 /// emitted by the compiler.
 #[derive(Debug, Default)]
 pub struct Chunk {
-    pub code: Vec<OpCode>,
+    pub code: Vec<u8>,
     pub constants: Vec<Value>,
     spans: Vec<SourceSpan>,
+
+    /// Index of the last operation (i.e. not data) written to the code vector.
+    /// Some operations (e.g. jump patching) need to know this.
+    last_op: usize,
 }
 
-impl Index<ConstantIdx> for Chunk {
-    type Output = Value;
+impl Chunk {
+    pub fn push_op(&mut self, op: Op, span: codemap::Span) -> usize {
+        self.last_op = self.code.len();
+        self.code.push(op as u8);
+        self.push_span(span, self.last_op);
+        self.last_op
+    }
 
-    fn index(&self, index: ConstantIdx) -> &Self::Output {
-        &self.constants[index.0]
+    pub fn push_uvarint(&mut self, data: u64) {
+        let mut encoded = [0u8; U64_VARINT_SIZE];
+        let bytes_written = vu128::encode_u64(&mut encoded, data);
+        self.code.extend_from_slice(&encoded[..bytes_written]);
     }
-}
 
-impl Index<CodeIdx> for Chunk {
-    type Output = OpCode;
+    pub fn read_uvarint(&self, idx: usize) -> (u64, usize) {
+        debug_assert!(
+            idx < self.code.len(),
+            "invalid bytecode (missing varint operand)",
+        );
 
-    fn index(&self, index: CodeIdx) -> &Self::Output {
-        &self.code[index.0]
+        if self.code.len() - idx >= U64_VARINT_SIZE {
+            vu128::decode_u64(
+                &self.code[idx..idx + U64_VARINT_SIZE]
+                    .try_into()
+                    .expect("size statically checked"),
+            )
+        } else {
+            let mut tmp = [0u8; U64_VARINT_SIZE];
+            tmp[..self.code.len() - idx].copy_from_slice(&self.code[idx..]);
+            vu128::decode_u64(&tmp)
+        }
     }
-}
 
-impl IndexMut<CodeIdx> for Chunk {
-    fn index_mut(&mut self, index: CodeIdx) -> &mut Self::Output {
-        &mut self.code[index.0]
+    pub fn push_u16(&mut self, data: u16) {
+        self.code.extend_from_slice(&data.to_le_bytes())
     }
-}
 
-impl Chunk {
-    pub fn push_op(&mut self, data: OpCode, span: codemap::Span) -> CodeIdx {
-        let idx = self.code.len();
-        self.code.push(data);
-        self.push_span(span, idx);
-        CodeIdx(idx)
+    /// Patches the argument to the jump operand of the jump at the given index
+    /// to point to the *next* instruction that will be emitted.
+    pub fn patch_jump(&mut self, idx: usize) {
+        let offset = (self.code.len() - idx - /* arg idx = */ 1 - /* jump arg size = */ 2) as u16;
+        self.code[idx + 1..idx + 3].copy_from_slice(&offset.to_le_bytes())
+    }
+
+    pub fn read_u16(&self, idx: usize) -> u16 {
+        if idx + 2 > self.code.len() {
+            panic!("Tvix bug: invalid bytecode (expected u16 operand not found)")
+        }
+
+        let byte_array: &[u8; 2] = &self.code[idx..idx + 2]
+            .try_into()
+            .expect("fixed-size slice can not fail to convert to array");
+
+        u16::from_le_bytes(*byte_array)
     }
 
     /// Get the first span of a chunk, no questions asked.
@@ -70,23 +101,13 @@ impl Chunk {
         self.spans[0].span
     }
 
-    /// Return a reference to the last op in the chunk, if any
-    pub fn last_op(&self) -> Option<&OpCode> {
-        self.code.last()
-    }
-
-    /// Pop the last operation from the chunk and clean up its tracked
-    /// span. Used when the compiler backtracks.
-    pub fn pop_op(&mut self) {
-        // Simply drop the last op.
-        self.code.pop();
-
-        if let Some(span) = self.spans.last() {
-            // If the last span started at this op, drop it.
-            if span.start == self.code.len() {
-                self.spans.pop();
-            }
+    /// Return the last op in the chunk together with its index, if any.
+    pub fn last_op(&self) -> Option<(Op, usize)> {
+        if self.code.is_empty() {
+            return None;
         }
+
+        Some((self.code[self.last_op].into(), self.last_op))
     }
 
     pub fn push_constant(&mut self, data: Value) -> ConstantIdx {
@@ -100,8 +121,6 @@ impl Chunk {
         self.constants.get(constant.0)
     }
 
-    // Span tracking implementation
-
     fn push_span(&mut self, span: codemap::Span, start: usize) {
         match self.spans.last_mut() {
             // We do not need to insert the same span again, as this
@@ -136,76 +155,88 @@ impl Chunk {
     }
 
     /// Write the disassembler representation of the operation at
-    /// `idx` to the specified writer.
+    /// `idx` to the specified writer, and return how many bytes in the code to
+    /// skip for the next op.
     pub fn disassemble_op<W: Write>(
         &self,
         writer: &mut W,
         source: &SourceCode,
         width: usize,
         idx: CodeIdx,
-    ) -> Result<(), std::io::Error> {
+    ) -> Result<usize, std::io::Error> {
         write!(writer, "{:#width$x}\t ", idx.0, width = width)?;
 
         // Print continuation character if the previous operation was at
         // the same line, otherwise print the line.
         let line = source.get_line(self.get_span(idx));
-        if idx.0 > 0 && source.get_line(self.get_span(CodeIdx(idx.0 - 1))) == line {
+        if idx.0 > 0 && source.get_line(self.get_span(idx - 1)) == line {
             write!(writer, "   |\t")?;
         } else {
             write!(writer, "{:4}\t", line)?;
         }
 
-        let a = |idx| match &self[idx] {
+        let _fmt_constant = |idx: ConstantIdx| match &self.constants[idx.0] {
             Value::Thunk(t) => t.debug_repr(),
             Value::Closure(c) => format!("closure({:p})", c.lambda),
             Value::Blueprint(b) => format!("blueprint({:p})", b),
             val => format!("{}", val),
         };
 
-        match self[idx] {
-            OpCode::OpConstant(idx) => {
-                writeln!(writer, "OpConstant({}@{})", a(idx), idx.0)
-            }
-            OpCode::OpClosure(idx) => {
-                writeln!(writer, "OpClosure({}@{})", a(idx), idx.0)
-            }
-            OpCode::OpThunkClosure(idx) => {
-                writeln!(writer, "OpThunkClosure({}@{})", a(idx), idx.0)
+        let op: Op = self.code[idx.0].into();
+
+        match op.arg_type() {
+            OpArg::None => {
+                writeln!(writer, "Op{:?}", op)?;
+                Ok(1)
             }
-            OpCode::OpThunkSuspended(idx) => {
-                writeln!(writer, "OpThunkSuspended({}@{})", a(idx), idx.0)
+
+            OpArg::Fixed => {
+                let arg = self.read_u16(idx.0 + 1);
+                writeln!(writer, "Op{:?}({})", op, arg)?;
+                Ok(3)
             }
-            op => writeln!(writer, "{:?}", op),
-        }?;
 
-        Ok(())
-    }
+            OpArg::Uvarint => {
+                let (arg, size) = self.read_uvarint(idx.0 + 1);
+                writeln!(writer, "Op{:?}({})", op, arg)?;
+                Ok(1 + size)
+            }
 
-    /// Extend this chunk with the content of another, moving out of the other
-    /// in the process.
-    ///
-    /// This is used by the compiler when it detects that it unnecessarily
-    /// thunked a nested expression.
-    pub fn extend(&mut self, other: Self) {
-        // Some operations need to be modified in certain ways before being
-        // valid as part of the new chunk.
-        let const_count = self.constants.len();
-        for (idx, op) in other.code.iter().enumerate() {
-            let span = other.get_span(CodeIdx(idx));
-            match op {
-                // As the constants shift, the index needs to be moved relatively.
-                OpCode::OpConstant(ConstantIdx(idx)) => {
-                    self.push_op(OpCode::OpConstant(ConstantIdx(idx + const_count)), span)
+            _ => match op {
+                Op::CoerceToString => {
+                    let kind: CoercionKind = self.code[idx.0 + 1].into();
+                    writeln!(writer, "Op{:?}({:?})", op, kind)?;
+                    Ok(2)
                 }
 
-                // Other operations either operate on relative offsets, or no
-                // offsets, and are safe to keep as-is.
-                _ => self.push_op(*op, span),
-            };
-        }
+                Op::Closure | Op::ThunkClosure | Op::ThunkSuspended => {
+                    let mut cidx = idx.0 + 1;
 
-        self.constants.extend(other.constants);
-        self.spans.extend(other.spans);
+                    let (bp_idx, size) = self.read_uvarint(cidx);
+                    cidx += size;
+
+                    let (packed_count, size) = self.read_uvarint(cidx);
+                    cidx += size;
+
+                    let captures_with = packed_count & 0b1 == 1;
+                    let count = packed_count >> 1;
+
+                    write!(writer, "Op{:?}(BP @ {}, ", op, bp_idx)?;
+                    if captures_with {
+                        write!(writer, "captures with, ")?;
+                    }
+                    writeln!(writer, "{} upvalues)", count)?;
+
+                    for _ in 0..count {
+                        let (_, size) = self.read_uvarint(cidx);
+                        cidx += size;
+                    }
+
+                    Ok(cidx - idx.0)
+                }
+                _ => panic!("Tvix bug: don't know how to format argument for Op{:?}", op),
+            },
+        }
     }
 }
 
@@ -221,79 +252,49 @@ mod tests {
     #[test]
     fn push_op() {
         let mut chunk = Chunk::default();
-        chunk.push_op(OpCode::OpAdd, dummy_span());
-        assert_eq!(chunk.code.last().unwrap(), &OpCode::OpAdd);
+        let idx = chunk.push_op(Op::Add, dummy_span());
+        assert_eq!(*chunk.code.last().unwrap(), Op::Add as u8);
+        assert_eq!(chunk.code[idx], Op::Add as u8);
     }
 
     #[test]
-    fn extend_empty() {
+    fn push_op_with_arg() {
         let mut chunk = Chunk::default();
-        chunk.push_op(OpCode::OpAdd, dummy_span());
+        let mut idx = chunk.push_op(Op::Constant, dummy_span());
+        chunk.push_uvarint(42);
 
-        let other = Chunk::default();
-        chunk.extend(other);
+        assert_eq!(chunk.code[idx], Op::Constant as u8);
 
-        assert_eq!(
-            chunk.code,
-            vec![OpCode::OpAdd],
-            "code should not have changed"
-        );
+        idx += 1;
+        let (arg, size) = chunk.read_uvarint(idx);
+        assert_eq!(idx + size, chunk.code.len());
+        assert_eq!(arg, 42);
     }
 
     #[test]
-    fn extend_simple() {
-        let span = dummy_span();
+    fn push_jump() {
         let mut chunk = Chunk::default();
-        chunk.push_op(OpCode::OpAdd, span);
 
-        let mut other = Chunk::default();
-        other.push_op(OpCode::OpSub, span);
-        other.push_op(OpCode::OpMul, span);
+        chunk.push_op(Op::Constant, dummy_span());
+        chunk.push_uvarint(0);
 
-        let expected_code = vec![OpCode::OpAdd, OpCode::OpSub, OpCode::OpMul];
+        let idx = chunk.push_op(Op::Jump, dummy_span());
+        chunk.push_u16(0);
 
-        chunk.extend(other);
+        chunk.push_op(Op::Constant, dummy_span());
+        chunk.push_uvarint(1);
 
-        assert_eq!(chunk.code, expected_code, "code should have been extended");
-    }
+        chunk.patch_jump(idx);
+        chunk.push_op(Op::Return, dummy_span());
 
-    #[test]
-    fn extend_with_constant() {
-        let span = dummy_span();
-        let mut chunk = Chunk::default();
-        chunk.push_op(OpCode::OpAdd, span);
-        let cidx = chunk.push_constant(Value::Integer(0));
-        assert_eq!(
-            cidx.0, 0,
-            "first constant in main chunk should have index 0"
-        );
-        chunk.push_op(OpCode::OpConstant(cidx), span);
-
-        let mut other = Chunk::default();
-        other.push_op(OpCode::OpSub, span);
-        let other_cidx = other.push_constant(Value::Integer(1));
-        assert_eq!(
-            other_cidx.0, 0,
-            "first constant in other chunk should have index 0"
-        );
-        other.push_op(OpCode::OpConstant(other_cidx), span);
-
-        chunk.extend(other);
-
-        let expected_code = vec![
-            OpCode::OpAdd,
-            OpCode::OpConstant(ConstantIdx(0)),
-            OpCode::OpSub,
-            OpCode::OpConstant(ConstantIdx(1)), // <- note: this was rewritten
+        #[rustfmt::skip]
+        let expected: Vec<u8> = vec![
+            Op::Constant as u8, 0,
+            Op::Jump as u8, 2, 0,
+            Op::Constant as u8, 1,
+            Op::Return as u8,
         ];
 
-        assert_eq!(
-            chunk.code, expected_code,
-            "code should have been extended and rewritten"
-        );
-
-        assert_eq!(chunk.constants.len(), 2);
-        assert!(matches!(chunk.constants[0], Value::Integer(0)));
-        assert!(matches!(chunk.constants[1], Value::Integer(1)));
+        assert_eq!(chunk.code, expected);
     }
 }
diff --git a/tvix/eval/src/compiler/bindings.rs b/tvix/eval/src/compiler/bindings.rs
index f5c6376eb1b3..6a3ae485936c 100644
--- a/tvix/eval/src/compiler/bindings.rs
+++ b/tvix/eval/src/compiler/bindings.rs
@@ -605,7 +605,7 @@ impl Compiler<'_, '_> {
                         c.emit_force(&namespace);
 
                         c.emit_constant(name.as_str().into(), &span);
-                        c.push_op(OpCode::OpAttrsSelect, &span);
+                        c.push_op(Op::AttrsSelect, &span);
                     })
                 }
 
@@ -632,7 +632,8 @@ impl Compiler<'_, '_> {
             if self.scope()[idx].needs_finaliser {
                 let stack_idx = self.scope().stack_index(idx);
                 let span = self.scope()[idx].span;
-                self.push_op(OpCode::OpFinalise(stack_idx), &OrEntireFile(span));
+                self.push_op(Op::Finalise, &OrEntireFile(span));
+                self.push_uvarint(stack_idx.0 as u64)
             }
         }
     }
@@ -667,11 +668,8 @@ impl Compiler<'_, '_> {
         self.bind_values(bindings);
 
         if kind.is_attrs() {
-            self.push_op(OpCode::OpAttrs(Count(count)), node);
-        }
-
-        if count == 0 {
-            self.unthunk();
+            self.push_op(Op::Attrs, node);
+            self.push_uvarint(count as u64);
         }
     }
 
@@ -697,7 +695,7 @@ impl Compiler<'_, '_> {
         self.scope_mut().end_scope();
 
         self.emit_constant("body".into(), node);
-        self.push_op(OpCode::OpAttrsSelect, node);
+        self.push_op(Op::AttrsSelect, node);
     }
 
     /// Is the given identifier defined *by the user* in any current scope?
@@ -718,8 +716,9 @@ impl Compiler<'_, '_> {
         match self.scope_mut().resolve_local(ident) {
             LocalPosition::Unknown => {
                 // Are we possibly dealing with an upvalue?
-                if let Some(idx) = self.resolve_upvalue(self.contexts.len() - 1, ident, node) {
-                    self.push_op(OpCode::OpGetUpvalue(idx), node);
+                if let Some(idx) = self.resolve_upvalue(self.contexts.len() - 1, ident) {
+                    self.push_op(Op::GetUpvalue, node);
+                    self.push_uvarint(idx.0 as u64);
                     return;
                 }
 
@@ -742,7 +741,7 @@ impl Compiler<'_, '_> {
                     self.thunk(slot, node, |c, _| {
                         c.context_mut().captures_with_stack = true;
                         c.emit_constant(ident.into(), node);
-                        c.push_op(OpCode::OpResolveWith, node);
+                        c.push_op(Op::ResolveWith, node);
                     });
                     return;
                 }
@@ -753,18 +752,17 @@ impl Compiler<'_, '_> {
 
             LocalPosition::Known(idx) => {
                 let stack_idx = self.scope().stack_index(idx);
-                self.push_op(OpCode::OpGetLocal(stack_idx), node);
+                self.push_op(Op::GetLocal, node);
+                self.push_uvarint(stack_idx.0 as u64);
             }
 
             // This identifier is referring to a value from the same scope which
             // is not yet defined. This identifier access must be thunked.
             LocalPosition::Recursive(idx) => self.thunk(slot, node, move |compiler, _| {
-                let upvalue_idx = compiler.add_upvalue(
-                    compiler.contexts.len() - 1,
-                    node,
-                    UpvalueKind::Local(idx),
-                );
-                compiler.push_op(OpCode::OpGetUpvalue(upvalue_idx), node);
+                let upvalue_idx =
+                    compiler.add_upvalue(compiler.contexts.len() - 1, UpvalueKind::Local(idx));
+                compiler.push_op(Op::GetUpvalue, node);
+                compiler.push_uvarint(upvalue_idx.0 as u64);
             }),
         };
     }
@@ -777,12 +775,7 @@ impl Compiler<'_, '_> {
 
 /// Private compiler helpers related to bindings.
 impl Compiler<'_, '_> {
-    fn resolve_upvalue<N: ToSpan>(
-        &mut self,
-        ctx_idx: usize,
-        name: &str,
-        node: &N,
-    ) -> Option<UpvalueIdx> {
+    fn resolve_upvalue(&mut self, ctx_idx: usize, name: &str) -> Option<UpvalueIdx> {
         if ctx_idx == 0 {
             // There can not be any upvalue at the outermost context.
             return None;
@@ -795,7 +788,7 @@ impl Compiler<'_, '_> {
             // stack (i.e. in the right position) *during* their runtime
             // construction
             LocalPosition::Known(idx) | LocalPosition::Recursive(idx) => {
-                return Some(self.add_upvalue(ctx_idx, node, UpvalueKind::Local(idx)))
+                return Some(self.add_upvalue(ctx_idx, UpvalueKind::Local(idx)))
             }
 
             LocalPosition::Unknown => { /* continue below */ }
@@ -803,19 +796,14 @@ impl Compiler<'_, '_> {
 
         // If the upvalue comes from even further up, we need to recurse to make
         // sure that the upvalues are created at each level.
-        if let Some(idx) = self.resolve_upvalue(ctx_idx - 1, name, node) {
-            return Some(self.add_upvalue(ctx_idx, node, UpvalueKind::Upvalue(idx)));
+        if let Some(idx) = self.resolve_upvalue(ctx_idx - 1, name) {
+            return Some(self.add_upvalue(ctx_idx, UpvalueKind::Upvalue(idx)));
         }
 
         None
     }
 
-    fn add_upvalue<N: ToSpan>(
-        &mut self,
-        ctx_idx: usize,
-        node: &N,
-        kind: UpvalueKind,
-    ) -> UpvalueIdx {
+    fn add_upvalue(&mut self, ctx_idx: usize, kind: UpvalueKind) -> UpvalueIdx {
         // If there is already an upvalue closing over the specified index,
         // retrieve that instead.
         for (idx, existing) in self.contexts[ctx_idx].scope.upvalues.iter().enumerate() {
@@ -824,11 +812,7 @@ impl Compiler<'_, '_> {
             }
         }
 
-        let span = self.span_for(node);
-        self.contexts[ctx_idx]
-            .scope
-            .upvalues
-            .push(Upvalue { kind, span });
+        self.contexts[ctx_idx].scope.upvalues.push(Upvalue { kind });
 
         let idx = UpvalueIdx(self.contexts[ctx_idx].lambda.upvalue_count);
         self.contexts[ctx_idx].lambda.upvalue_count += 1;
diff --git a/tvix/eval/src/compiler/mod.rs b/tvix/eval/src/compiler/mod.rs
index 4878db1f1a84..33b70b87ce84 100644
--- a/tvix/eval/src/compiler/mod.rs
+++ b/tvix/eval/src/compiler/mod.rs
@@ -29,7 +29,7 @@ use std::rc::{Rc, Weak};
 use crate::chunk::Chunk;
 use crate::errors::{CatchableErrorKind, Error, ErrorKind, EvalResult};
 use crate::observer::CompilerObserver;
-use crate::opcode::{CodeIdx, ConstantIdx, Count, JumpOffset, OpCode, UpvalueIdx};
+use crate::opcode::{CodeIdx, Op, Position, UpvalueIdx};
 use crate::spans::ToSpan;
 use crate::value::{Closure, Formals, Lambda, NixAttrs, Thunk, Value};
 use crate::warnings::{EvalWarning, WarningKind};
@@ -52,7 +52,6 @@ struct LambdaCtx {
     lambda: Lambda,
     scope: Scope,
     captures_with_stack: bool,
-    unthunk: bool,
 }
 
 impl LambdaCtx {
@@ -61,7 +60,6 @@ impl LambdaCtx {
             lambda: Lambda::default(),
             scope: Default::default(),
             captures_with_stack: false,
-            unthunk: false,
         }
     }
 
@@ -70,7 +68,6 @@ impl LambdaCtx {
             lambda: Lambda::default(),
             scope: self.scope.inherit(),
             captures_with_stack: false,
-            unthunk: false,
         }
     }
 }
@@ -270,13 +267,37 @@ impl Compiler<'_, '_> {
 
     /// Push a single instruction to the current bytecode chunk and
     /// track the source span from which it was compiled.
-    fn push_op<T: ToSpan>(&mut self, data: OpCode, node: &T) -> CodeIdx {
+    fn push_op<T: ToSpan>(&mut self, data: Op, node: &T) -> CodeIdx {
         if self.dead_scope > 0 {
             return CodeIdx(0);
         }
 
         let span = self.span_for(node);
-        self.chunk().push_op(data, span)
+        CodeIdx(self.chunk().push_op(data, span))
+    }
+
+    fn push_u8(&mut self, data: u8) {
+        if self.dead_scope > 0 {
+            return;
+        }
+
+        self.chunk().code.push(data);
+    }
+
+    fn push_uvarint(&mut self, data: u64) {
+        if self.dead_scope > 0 {
+            return;
+        }
+
+        self.chunk().push_uvarint(data);
+    }
+
+    fn push_u16(&mut self, data: u16) {
+        if self.dead_scope > 0 {
+            return;
+        }
+
+        self.chunk().push_u16(data);
     }
 
     /// Emit a single constant to the current bytecode chunk and track
@@ -287,7 +308,8 @@ impl Compiler<'_, '_> {
         }
 
         let idx = self.chunk().push_constant(value);
-        self.push_op(OpCode::OpConstant(idx), node);
+        self.push_op(Op::Constant, node);
+        self.push_uvarint(idx.0 as u64);
     }
 }
 
@@ -400,7 +422,7 @@ impl Compiler<'_, '_> {
                 Value::UnresolvedPath(Box::new(home_relative_path.into())),
                 node,
             );
-            self.push_op(OpCode::OpResolveHomePath, node);
+            self.push_op(Op::ResolveHomePath, node);
             return;
         } else if raw_path.starts_with('<') {
             // TODO: decide what to do with findFile
@@ -416,7 +438,7 @@ impl Compiler<'_, '_> {
             // Make a thunk to resolve the path (without using `findFile`, at least for now?)
             return self.thunk(slot, node, move |c, _| {
                 c.emit_constant(Value::UnresolvedPath(Box::new(path.into())), node);
-                c.push_op(OpCode::OpFindFile, node);
+                c.push_op(Op::FindFile, node);
             });
         } else {
             let mut buf = self.root_dir.clone();
@@ -452,13 +474,15 @@ impl Compiler<'_, '_> {
                 ast::InterpolPart::Interpolation(ipol) => {
                     self.compile(slot, ipol.expr().unwrap());
                     // implicitly forces as well
-                    self.push_op(
-                        OpCode::OpCoerceToString(CoercionKind {
-                            strong: false,
-                            import_paths: true,
-                        }),
-                        ipol,
-                    );
+                    self.push_op(Op::CoerceToString, ipol);
+
+                    let encoded: u8 = CoercionKind {
+                        strong: false,
+                        import_paths: true,
+                    }
+                    .into();
+
+                    self.push_u8(encoded);
                 }
 
                 ast::InterpolPart::Literal(lit) => {
@@ -468,7 +492,8 @@ impl Compiler<'_, '_> {
         }
 
         if parts.len() != 1 {
-            self.push_op(OpCode::OpInterpolate(Count(parts.len())), parent_node);
+            self.push_op(Op::Interpolate, parent_node);
+            self.push_uvarint(parts.len() as u64);
         }
     }
 
@@ -494,8 +519,8 @@ impl Compiler<'_, '_> {
         self.emit_force(op);
 
         let opcode = match op.operator().unwrap() {
-            ast::UnaryOpKind::Invert => OpCode::OpInvert,
-            ast::UnaryOpKind::Negate => OpCode::OpNegate,
+            ast::UnaryOpKind::Invert => Op::Invert,
+            ast::UnaryOpKind::Negate => Op::Negate,
         };
 
         self.push_op(opcode, op);
@@ -526,21 +551,21 @@ impl Compiler<'_, '_> {
         self.emit_force(&op.rhs().unwrap());
 
         match op.operator().unwrap() {
-            BinOpKind::Add => self.push_op(OpCode::OpAdd, op),
-            BinOpKind::Sub => self.push_op(OpCode::OpSub, op),
-            BinOpKind::Mul => self.push_op(OpCode::OpMul, op),
-            BinOpKind::Div => self.push_op(OpCode::OpDiv, op),
-            BinOpKind::Update => self.push_op(OpCode::OpAttrsUpdate, op),
-            BinOpKind::Equal => self.push_op(OpCode::OpEqual, op),
-            BinOpKind::Less => self.push_op(OpCode::OpLess, op),
-            BinOpKind::LessOrEq => self.push_op(OpCode::OpLessOrEq, op),
-            BinOpKind::More => self.push_op(OpCode::OpMore, op),
-            BinOpKind::MoreOrEq => self.push_op(OpCode::OpMoreOrEq, op),
-            BinOpKind::Concat => self.push_op(OpCode::OpConcat, op),
+            BinOpKind::Add => self.push_op(Op::Add, op),
+            BinOpKind::Sub => self.push_op(Op::Sub, op),
+            BinOpKind::Mul => self.push_op(Op::Mul, op),
+            BinOpKind::Div => self.push_op(Op::Div, op),
+            BinOpKind::Update => self.push_op(Op::AttrsUpdate, op),
+            BinOpKind::Equal => self.push_op(Op::Equal, op),
+            BinOpKind::Less => self.push_op(Op::Less, op),
+            BinOpKind::LessOrEq => self.push_op(Op::LessOrEq, op),
+            BinOpKind::More => self.push_op(Op::More, op),
+            BinOpKind::MoreOrEq => self.push_op(Op::MoreOrEq, op),
+            BinOpKind::Concat => self.push_op(Op::Concat, op),
 
             BinOpKind::NotEqual => {
-                self.push_op(OpCode::OpEqual, op);
-                self.push_op(OpCode::OpInvert, op)
+                self.push_op(Op::Equal, op);
+                self.push_op(Op::Invert, op)
             }
 
             // Handled by separate branch above.
@@ -561,20 +586,22 @@ impl Compiler<'_, '_> {
         self.compile(slot, node.lhs().unwrap());
         self.emit_force(&node.lhs().unwrap());
 
-        let throw_idx = self.push_op(OpCode::OpJumpIfCatchable(JumpOffset(0)), node);
+        let throw_idx = self.push_op(Op::JumpIfCatchable, node);
+        self.push_u16(0);
         // If this value is false, jump over the right-hand side - the
         // whole expression is false.
-        let end_idx = self.push_op(OpCode::OpJumpIfFalse(JumpOffset(0)), node);
+        let end_idx = self.push_op(Op::JumpIfFalse, node);
+        self.push_u16(0);
 
         // Otherwise, remove the previous value and leave the
         // right-hand side on the stack. Its result is now the value
         // of the whole expression.
-        self.push_op(OpCode::OpPop, node);
+        self.push_op(Op::Pop, node);
         self.compile(slot, node.rhs().unwrap());
         self.emit_force(&node.rhs().unwrap());
 
         self.patch_jump(end_idx);
-        self.push_op(OpCode::OpAssertBool, node);
+        self.push_op(Op::AssertBool, node);
         self.patch_jump(throw_idx);
     }
 
@@ -589,16 +616,18 @@ impl Compiler<'_, '_> {
         self.compile(slot, node.lhs().unwrap());
         self.emit_force(&node.lhs().unwrap());
 
-        let throw_idx = self.push_op(OpCode::OpJumpIfCatchable(JumpOffset(0)), node);
+        let throw_idx = self.push_op(Op::JumpIfCatchable, node);
+        self.push_u16(0);
         // Opposite of above: If this value is **true**, we can
         // short-circuit the right-hand side.
-        let end_idx = self.push_op(OpCode::OpJumpIfTrue(JumpOffset(0)), node);
-        self.push_op(OpCode::OpPop, node);
+        let end_idx = self.push_op(Op::JumpIfTrue, node);
+        self.push_u16(0);
+        self.push_op(Op::Pop, node);
         self.compile(slot, node.rhs().unwrap());
         self.emit_force(&node.rhs().unwrap());
 
         self.patch_jump(end_idx);
-        self.push_op(OpCode::OpAssertBool, node);
+        self.push_op(Op::AssertBool, node);
         self.patch_jump(throw_idx);
     }
 
@@ -612,17 +641,20 @@ impl Compiler<'_, '_> {
         // Leave left-hand side value on the stack and invert it.
         self.compile(slot, node.lhs().unwrap());
         self.emit_force(&node.lhs().unwrap());
-        let throw_idx = self.push_op(OpCode::OpJumpIfCatchable(JumpOffset(0)), node);
-        self.push_op(OpCode::OpInvert, node);
+        let throw_idx = self.push_op(Op::JumpIfCatchable, node);
+        self.push_u16(0);
+        self.push_op(Op::Invert, node);
 
         // Exactly as `||` (because `a -> b` = `!a || b`).
-        let end_idx = self.push_op(OpCode::OpJumpIfTrue(JumpOffset(0)), node);
-        self.push_op(OpCode::OpPop, node);
+        let end_idx = self.push_op(Op::JumpIfTrue, node);
+        self.push_u16(0);
+
+        self.push_op(Op::Pop, node);
         self.compile(slot, node.rhs().unwrap());
         self.emit_force(&node.rhs().unwrap());
 
         self.patch_jump(end_idx);
-        self.push_op(OpCode::OpAssertBool, node);
+        self.push_op(Op::AssertBool, node);
         self.patch_jump(throw_idx);
     }
 
@@ -657,11 +689,8 @@ impl Compiler<'_, '_> {
             self.scope_mut().mark_initialised(item_slot);
         }
 
-        if count == 0 {
-            self.unthunk();
-        }
-
-        self.push_op(OpCode::OpList(Count(count)), node);
+        self.push_op(Op::List, node);
+        self.push_uvarint(count as u64);
         self.scope_mut().end_scope();
     }
 
@@ -690,7 +719,7 @@ impl Compiler<'_, '_> {
         // next nested element, for all fragments except the last one.
         for (count, fragment) in node.attrpath().unwrap().attrs().enumerate() {
             if count > 0 {
-                self.push_op(OpCode::OpAttrsTrySelect, &fragment);
+                self.push_op(Op::AttrsTrySelect, &fragment);
                 self.emit_force(&fragment);
             }
 
@@ -699,7 +728,7 @@ impl Compiler<'_, '_> {
 
         // After the last fragment, emit the actual instruction that
         // leaves a boolean on the stack.
-        self.push_op(OpCode::OpHasAttr, node);
+        self.push_op(Op::HasAttr, node);
     }
 
     /// When compiling select or select_or expressions, an optimisation is
@@ -723,8 +752,9 @@ impl Compiler<'_, '_> {
         // set that is lacking a key, because that thunk is never
         // evaluated). If anything is missing, just move on. We may
         // want to emit warnings here in the future.
-        if let Some(OpCode::OpConstant(ConstantIdx(idx))) = self.chunk().code.last().cloned() {
-            let constant = &mut self.chunk().constants[idx];
+        if let Some((Op::Constant, op_idx)) = self.chunk().last_op() {
+            let (idx, _) = self.chunk().read_uvarint(op_idx + 1);
+            let constant = &mut self.chunk().constants[idx as usize];
             if let Value::Attrs(attrs) = constant {
                 let mut path_iter = path.attrs();
 
@@ -736,10 +766,6 @@ impl Compiler<'_, '_> {
                     if let Some(ident) = expr_static_attr_str(&attr) {
                         if let Some(selected_value) = attrs.select(ident.as_bytes()) {
                             *constant = selected_value.clone();
-
-                            // If this worked, we can unthunk the current thunk.
-                            self.unthunk();
-
                             return true;
                         }
                     }
@@ -773,7 +799,7 @@ impl Compiler<'_, '_> {
             self.emit_force(&set);
 
             self.compile_attr(slot, &fragment);
-            self.push_op(OpCode::OpAttrsSelect, &fragment);
+            self.push_op(Op::AttrsSelect, &fragment);
         }
     }
 
@@ -823,11 +849,13 @@ impl Compiler<'_, '_> {
         for fragment in path.attrs() {
             self.emit_force(&fragment);
             self.compile_attr(slot, &fragment.clone());
-            self.push_op(OpCode::OpAttrsTrySelect, &fragment);
-            jumps.push(self.push_op(OpCode::OpJumpIfNotFound(JumpOffset(0)), &fragment));
+            self.push_op(Op::AttrsTrySelect, &fragment);
+            jumps.push(self.push_op(Op::JumpIfNotFound, &fragment));
+            self.push_u16(0);
         }
 
-        let final_jump = self.push_op(OpCode::OpJump(JumpOffset(0)), &path);
+        let final_jump = self.push_op(Op::Jump, &path);
+        self.push_u16(0);
 
         for jump in jumps {
             self.patch_jump(jump);
@@ -855,17 +883,22 @@ impl Compiler<'_, '_> {
         // Compile the assertion condition to leave its value on the stack.
         self.compile(slot, node.condition().unwrap());
         self.emit_force(&node.condition().unwrap());
-        let throw_idx = self.push_op(OpCode::OpJumpIfCatchable(JumpOffset(0)), node);
-        let then_idx = self.push_op(OpCode::OpJumpIfFalse(JumpOffset(0)), node);
 
-        self.push_op(OpCode::OpPop, node);
+        let throw_idx = self.push_op(Op::JumpIfCatchable, node);
+        self.push_u16(0);
+
+        let then_idx = self.push_op(Op::JumpIfFalse, node);
+        self.push_u16(0);
+
+        self.push_op(Op::Pop, node);
         self.compile(slot, node.body().unwrap());
 
-        let else_idx = self.push_op(OpCode::OpJump(JumpOffset(0)), node);
+        let else_idx = self.push_op(Op::Jump, node);
+        self.push_u16(0);
 
         self.patch_jump(then_idx);
-        self.push_op(OpCode::OpPop, node);
-        self.push_op(OpCode::OpAssertFail, &node.condition().unwrap());
+        self.push_op(Op::Pop, node);
+        self.push_op(Op::AssertFail, &node.condition().unwrap());
 
         self.patch_jump(else_idx);
         self.patch_jump(throw_idx);
@@ -887,22 +920,20 @@ impl Compiler<'_, '_> {
         self.compile(slot, node.condition().unwrap());
         self.emit_force(&node.condition().unwrap());
 
-        let throw_idx = self.push_op(
-            OpCode::OpJumpIfCatchable(JumpOffset(0)),
-            &node.condition().unwrap(),
-        );
-        let then_idx = self.push_op(
-            OpCode::OpJumpIfFalse(JumpOffset(0)),
-            &node.condition().unwrap(),
-        );
+        let throw_idx = self.push_op(Op::JumpIfCatchable, &node.condition().unwrap());
+        self.push_u16(0);
+
+        let then_idx = self.push_op(Op::JumpIfFalse, &node.condition().unwrap());
+        self.push_u16(0);
 
-        self.push_op(OpCode::OpPop, node); // discard condition value
+        self.push_op(Op::Pop, node); // discard condition value
         self.compile(slot, node.body().unwrap());
 
-        let else_idx = self.push_op(OpCode::OpJump(JumpOffset(0)), node);
+        let else_idx = self.push_op(Op::Jump, node);
+        self.push_u16(0);
 
         self.patch_jump(then_idx); // patch jump *to* else_body
-        self.push_op(OpCode::OpPop, node); // discard condition value
+        self.push_op(Op::Pop, node); // discard condition value
         self.compile(slot, node.else_body().unwrap());
 
         self.patch_jump(else_idx); // patch jump *over* else body
@@ -931,11 +962,12 @@ impl Compiler<'_, '_> {
 
         self.scope_mut().push_with();
 
-        self.push_op(OpCode::OpPushWith(with_idx), &node.namespace().unwrap());
+        self.push_op(Op::PushWith, &node.namespace().unwrap());
+        self.push_uvarint(with_idx.0 as u64);
 
         self.compile(slot, node.body().unwrap());
 
-        self.push_op(OpCode::OpPopWith, node);
+        self.push_op(Op::PopWith, node);
         self.scope_mut().pop_with();
         self.cleanup_scope(node);
     }
@@ -995,13 +1027,15 @@ impl Compiler<'_, '_> {
         // At call time, the attribute set is already at the top of the stack.
         self.scope_mut().mark_initialised(set_idx);
         self.emit_force(pattern);
-        let throw_idx = self.push_op(OpCode::OpJumpIfCatchable(JumpOffset(0)), pattern);
+        let throw_idx = self.push_op(Op::JumpIfCatchable, pattern);
+        self.push_u16(0);
+
         // Evaluation fails on a type error, even if the argument(s) are unused.
-        self.push_op(OpCode::OpAssertAttrs, pattern);
+        self.push_op(Op::AssertAttrs, pattern);
 
         let ellipsis = pattern.ellipsis_token().is_some();
         if !ellipsis {
-            self.push_op(OpCode::OpValidateClosedFormals, pattern);
+            self.push_op(Op::ValidateClosedFormals, pattern);
         }
 
         // Similar to `let ... in ...`, we now do multiple passes over
@@ -1041,7 +1075,8 @@ impl Compiler<'_, '_> {
         // attempt to select from it.
         let stack_idx = self.scope().stack_index(set_idx);
         for tracked_formal in entries.iter() {
-            self.push_op(OpCode::OpGetLocal(stack_idx), pattern);
+            self.push_op(Op::GetLocal, pattern);
+            self.push_uvarint(stack_idx.0 as u64);
             self.emit_literal_ident(&tracked_formal.pattern_entry().ident().unwrap());
 
             let idx = tracked_formal.local_idx();
@@ -1070,14 +1105,14 @@ impl Compiler<'_, '_> {
                     // we only know better after compiling the default expression, so
                     // avoiding unnecessary locals would mean we'd need to modify the chunk
                     // after the fact.
-                    self.push_op(OpCode::OpAttrsTrySelect, &pattern_entry.ident().unwrap());
-                    let jump_to_default =
-                        self.push_op(OpCode::OpJumpIfNotFound(JumpOffset(0)), default_expr);
+                    self.push_op(Op::AttrsTrySelect, &pattern_entry.ident().unwrap());
+                    let jump_to_default = self.push_op(Op::JumpIfNotFound, default_expr);
+                    self.push_u16(0);
 
                     self.emit_constant(Value::FinaliseRequest(false), default_expr);
 
-                    let jump_over_default =
-                        self.push_op(OpCode::OpJump(JumpOffset(0)), default_expr);
+                    let jump_over_default = self.push_op(Op::Jump, default_expr);
+                    self.push_u16(0);
 
                     self.patch_jump(jump_to_default);
 
@@ -1089,7 +1124,7 @@ impl Compiler<'_, '_> {
                     self.patch_jump(jump_over_default);
                 }
                 TrackedFormal::NoDefault { pattern_entry, .. } => {
-                    self.push_op(OpCode::OpAttrsSelect, &pattern_entry.ident().unwrap());
+                    self.push_op(Op::AttrsSelect, &pattern_entry.ident().unwrap());
                 }
             }
 
@@ -1113,23 +1148,16 @@ impl Compiler<'_, '_> {
                         let finalise_request_stack_idx = self.scope().stack_index(*finalise_request_idx);
 
                         // TODO(sterni): better spans
-                        self.push_op(
-                            OpCode::OpGetLocal(finalise_request_stack_idx),
-                            pattern
-                        );
+                        self.push_op(Op::GetLocal, pattern);
+                        self.push_uvarint(finalise_request_stack_idx.0 as u64);
                         let jump_over_finalise =
-                            self.push_op(
-                                OpCode::OpJumpIfNoFinaliseRequest(
-                                    JumpOffset(0)),
-                                pattern
-                            );
-                        self.push_op(
-                            OpCode::OpFinalise(stack_idx),
-                            pattern,
-                        );
+                            self.push_op(Op::JumpIfNoFinaliseRequest, pattern);
+                        self.push_u16(0);
+                        self.push_op(Op::Finalise, pattern);
+                        self.push_uvarint(stack_idx.0 as u64);
                         self.patch_jump(jump_over_finalise);
                         // Get rid of finaliser request value on the stack
-                        self.push_op(OpCode::OpPop, pattern);
+                        self.push_op(Op::Pop, pattern);
                     }
                 }
             }
@@ -1188,12 +1216,6 @@ impl Compiler<'_, '_> {
         })
     }
 
-    /// Mark the current thunk as redundant, i.e. possible to merge directly
-    /// into its parent lambda context without affecting runtime behaviour.
-    fn unthunk(&mut self) {
-        self.context_mut().unthunk = true;
-    }
-
     /// Compile an expression into a runtime closure or thunk
     fn compile_lambda_or_thunk<N, F>(
         &mut self,
@@ -1222,31 +1244,15 @@ impl Compiler<'_, '_> {
             self.patch_jump(throw_idx);
         }
 
-        // TODO: determine and insert enclosing name, if available.
-
         // Pop the lambda context back off, and emit the finished
         // lambda as a constant.
         let mut compiled = self.contexts.pop().unwrap();
 
-        // The compiler might have decided to unthunk, i.e. raise the compiled
-        // code to the parent context. In that case we do so and return right
-        // away.
-        if compiled.unthunk && is_suspended_thunk {
-            self.chunk().extend(compiled.lambda.chunk);
-            return;
-        }
-
         // Emit an instruction to inform the VM that the chunk has ended.
         compiled
             .lambda
             .chunk
-            .push_op(OpCode::OpReturn, self.span_for(node));
-
-        // Capturing the with stack counts as an upvalue, as it is
-        // emitted as an upvalue data instruction.
-        if compiled.captures_with_stack {
-            compiled.lambda.upvalue_count += 1;
-        }
+            .push_op(Op::Return, self.span_for(node));
 
         let lambda = Rc::new(compiled.lambda);
         if is_suspended_thunk {
@@ -1256,7 +1262,7 @@ impl Compiler<'_, '_> {
         }
 
         // If no upvalues are captured, emit directly and move on.
-        if lambda.upvalue_count == 0 {
+        if lambda.upvalue_count == 0 && !compiled.captures_with_stack {
             self.emit_constant(
                 if is_suspended_thunk {
                     Value::Thunk(Thunk::new_suspended(lambda, span))
@@ -1276,12 +1282,13 @@ impl Compiler<'_, '_> {
 
         let code_idx = self.push_op(
             if is_suspended_thunk {
-                OpCode::OpThunkSuspended(blueprint_idx)
+                Op::ThunkSuspended
             } else {
-                OpCode::OpThunkClosure(blueprint_idx)
+                Op::ThunkClosure
             },
             node,
         );
+        self.push_uvarint(blueprint_idx.0 as u64);
 
         self.emit_upvalue_data(
             outer_slot,
@@ -1292,18 +1299,21 @@ impl Compiler<'_, '_> {
 
         if !is_suspended_thunk && !self.scope()[outer_slot].needs_finaliser {
             if !self.scope()[outer_slot].must_thunk {
-                // The closure has upvalues, but is not recursive.  Therefore no thunk is required,
-                // which saves us the overhead of Rc<RefCell<>>
-                self.chunk()[code_idx] = OpCode::OpClosure(blueprint_idx);
+                // The closure has upvalues, but is not recursive. Therefore no
+                // thunk is required, which saves us the overhead of
+                // Rc<RefCell<>>
+                self.chunk().code[code_idx.0] = Op::Closure as u8;
             } else {
-                // This case occurs when a closure has upvalue-references to itself but does not need a
-                // finaliser.  Since no OpFinalise will be emitted later on we synthesize one here.
-                // It is needed here only to set [`Closure::is_finalised`] which is used for sanity checks.
+                // This case occurs when a closure has upvalue-references to
+                // itself but does not need a finaliser. Since no OpFinalise
+                // will be emitted later on we synthesize one here. It is needed
+                // here only to set [`Closure::is_finalised`] which is used for
+                // sanity checks.
                 #[cfg(debug_assertions)]
-                self.push_op(
-                    OpCode::OpFinalise(self.scope().stack_index(outer_slot)),
-                    &self.span_for(node),
-                );
+                {
+                    self.push_op(Op::Finalise, &self.span_for(node));
+                    self.push_uvarint(self.scope().stack_index(outer_slot).0 as u64);
+                }
             }
         }
     }
@@ -1316,7 +1326,7 @@ impl Compiler<'_, '_> {
         self.compile(slot, node.argument().unwrap());
         self.compile(slot, node.lambda().unwrap());
         self.emit_force(&node.lambda().unwrap());
-        self.push_op(OpCode::OpCall, node);
+        self.push_op(Op::Call, node);
     }
 
     /// Emit the data instructions that the runtime needs to correctly
@@ -1324,10 +1334,18 @@ impl Compiler<'_, '_> {
     fn emit_upvalue_data<T: ToSpan>(
         &mut self,
         slot: LocalIdx,
-        node: &T,
+        _: &T, // TODO
         upvalues: Vec<Upvalue>,
         capture_with: bool,
     ) {
+        // Push the count of arguments to be expected, with one bit set to
+        // indicate whether the with stack needs to be captured.
+        let mut count = (upvalues.len() as u64) << 1;
+        if capture_with {
+            count |= 1;
+        }
+        self.push_uvarint(count);
+
         for upvalue in upvalues {
             match upvalue.kind {
                 UpvalueKind::Local(idx) => {
@@ -1337,27 +1355,22 @@ impl Compiler<'_, '_> {
                     // If the target is not yet initialised, we need to defer
                     // the local access
                     if !target.initialised {
-                        self.push_op(OpCode::DataDeferredLocal(stack_idx), &upvalue.span);
+                        self.push_uvarint(Position::deferred_local(stack_idx).0);
                         self.scope_mut().mark_needs_finaliser(slot);
                     } else {
                         // a self-reference
                         if slot == idx {
                             self.scope_mut().mark_must_thunk(slot);
                         }
-                        self.push_op(OpCode::DataStackIdx(stack_idx), &upvalue.span);
+                        self.push_uvarint(Position::stack_index(stack_idx).0);
                     }
                 }
 
                 UpvalueKind::Upvalue(idx) => {
-                    self.push_op(OpCode::DataUpvalueIdx(idx), &upvalue.span);
+                    self.push_uvarint(Position::upvalue_index(idx).0);
                 }
             };
         }
-
-        if capture_with {
-            // TODO(tazjin): probably better to emit span for the ident that caused this
-            self.push_op(OpCode::DataCaptureWith, node);
-        }
     }
 
     /// Emit the literal string value of an identifier. Required for
@@ -1374,20 +1387,7 @@ impl Compiler<'_, '_> {
     /// not known at the time when the jump operation itself is
     /// emitted.
     fn patch_jump(&mut self, idx: CodeIdx) {
-        let offset = JumpOffset(self.chunk().code.len() - 1 - idx.0);
-
-        match &mut self.chunk().code[idx.0] {
-            OpCode::OpJump(n)
-            | OpCode::OpJumpIfFalse(n)
-            | OpCode::OpJumpIfTrue(n)
-            | OpCode::OpJumpIfCatchable(n)
-            | OpCode::OpJumpIfNotFound(n)
-            | OpCode::OpJumpIfNoFinaliseRequest(n) => {
-                *n = offset;
-            }
-
-            op => panic!("attempted to patch unsupported op: {:?}", op),
-        }
+        self.chunk().patch_jump(idx.0);
     }
 
     /// Decrease scope depth of the current function and emit
@@ -1403,7 +1403,8 @@ impl Compiler<'_, '_> {
         }
 
         if popcount > 0 {
-            self.push_op(OpCode::OpCloseScope(Count(popcount)), node);
+            self.push_op(Op::CloseScope, node);
+            self.push_uvarint(popcount as u64);
         }
     }
 
@@ -1461,16 +1462,7 @@ impl Compiler<'_, '_> {
     }
 
     fn emit_force<N: ToSpan>(&mut self, node: &N) {
-        if let Some(&OpCode::OpConstant(c)) = self.chunk().last_op() {
-            if !self.chunk().get_constant(c).unwrap().is_thunk() {
-                // Optimization: Don't emit a force op for non-thunk constants, since they don't
-                // need one!
-                // TODO: this is probably doable for more ops (?)
-                return;
-            }
-        }
-
-        self.push_op(OpCode::OpForce, node);
+        self.push_op(Op::Force, node);
     }
 
     fn emit_warning<N: ToSpan>(&mut self, node: &N, kind: WarningKind) {
@@ -1673,10 +1665,11 @@ pub fn compile(
     c.emit_force(expr);
     if let Some(env) = env {
         if !env.is_empty() {
-            c.push_op(OpCode::OpCloseScope(Count(env.len())), &root_span);
+            c.push_op(Op::CloseScope, &root_span);
+            c.push_uvarint(env.len() as u64);
         }
     }
-    c.push_op(OpCode::OpReturn, &root_span);
+    c.push_op(Op::Return, &root_span);
 
     let lambda = Rc::new(c.contexts.pop().unwrap().lambda);
     c.observer.observe_compiled_toplevel(&lambda);
diff --git a/tvix/eval/src/compiler/scope.rs b/tvix/eval/src/compiler/scope.rs
index 1087e0153e42..bb1784e67b74 100644
--- a/tvix/eval/src/compiler/scope.rs
+++ b/tvix/eval/src/compiler/scope.rs
@@ -105,7 +105,6 @@ pub enum UpvalueKind {
 #[derive(Clone, Debug)]
 pub struct Upvalue {
     pub kind: UpvalueKind,
-    pub span: codemap::Span,
 }
 
 /// The index of a local in the scope's local array at compile time.
diff --git a/tvix/eval/src/observer.rs b/tvix/eval/src/observer.rs
index f5de399315c7..5e6526418b3b 100644
--- a/tvix/eval/src/observer.rs
+++ b/tvix/eval/src/observer.rs
@@ -13,7 +13,7 @@ use tabwriter::TabWriter;
 
 use crate::chunk::Chunk;
 use crate::generators::VMRequest;
-use crate::opcode::{CodeIdx, OpCode};
+use crate::opcode::{CodeIdx, Op};
 use crate::value::Lambda;
 use crate::SourceCode;
 use crate::Value;
@@ -73,7 +73,7 @@ pub trait RuntimeObserver {
 
     /// Called when the runtime *begins* executing an instruction. The
     /// provided stack is the state at the beginning of the operation.
-    fn observe_execute_op(&mut self, _ip: CodeIdx, _: &OpCode, _: &[Value]) {}
+    fn observe_execute_op(&mut self, _ip: CodeIdx, _: &Op, _: &[Value]) {}
 }
 
 #[derive(Default)]
@@ -112,8 +112,12 @@ impl<W: Write> DisassemblingObserver<W> {
         // calculate width of the widest address in the chunk
         let width = format!("{:#x}", chunk.code.len() - 1).len();
 
-        for (idx, _) in chunk.code.iter().enumerate() {
-            let _ = chunk.disassemble_op(&mut self.writer, &self.source, width, CodeIdx(idx));
+        let mut idx = 0;
+        while idx < chunk.code.len() {
+            let size = chunk
+                .disassemble_op(&mut self.writer, &self.source, width, CodeIdx(idx))
+                .expect("writing debug output should work");
+            idx += size;
         }
     }
 }
@@ -304,7 +308,7 @@ impl<W: Write> RuntimeObserver for TracingObserver<W> {
         );
     }
 
-    fn observe_execute_op(&mut self, ip: CodeIdx, op: &OpCode, stack: &[Value]) {
+    fn observe_execute_op(&mut self, ip: CodeIdx, op: &Op, stack: &[Value]) {
         self.maybe_write_time();
         let _ = write!(&mut self.writer, "{:04} {:?}\t", ip.0, op);
         self.write_stack(stack);
diff --git a/tvix/eval/src/opcode.rs b/tvix/eval/src/opcode.rs
index f89c1c12e7fd..ddf1304b3aea 100644
--- a/tvix/eval/src/opcode.rs
+++ b/tvix/eval/src/opcode.rs
@@ -52,8 +52,7 @@ pub struct JumpOffset(pub usize);
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub struct Count(pub usize);
 
-/// All variants of this enum carry a bounded amount of data to
-/// ensure that no heap allocations are needed for an Opcode.
+/// Op represents all instructions in the Tvix abstract machine.
 ///
 /// In documentation comments, stack positions are referred to by
 /// indices written in `{}` as such, where required:
@@ -70,187 +69,182 @@ pub struct Count(pub usize);
 ///
 /// Unless otherwise specified, operations leave their result at the
 /// top of the stack.
-#[warn(variant_size_differences)]
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub enum OpCode {
+#[repr(u8)]
+#[derive(Debug, PartialEq, Eq)]
+pub enum Op {
     /// Push a constant onto the stack.
-    OpConstant(ConstantIdx),
+    Constant,
 
-    // Unary operators
-    /// Discard a value from the stack.
-    OpPop,
+    /// Discard the value on top of the stack.
+    Pop,
 
     /// Invert the boolean at the top of the stack.
-    OpInvert,
+    Invert,
 
-    // Binary operators
     /// Invert the sign of the number at the top of the stack.
-    OpNegate,
+    Negate,
 
     /// Sum up the two numbers at the top of the stack.
-    OpAdd,
+    Add,
 
     /// Subtract the number at {1} from the number at {2}.
-    OpSub,
+    Sub,
 
     /// Multiply the two numbers at the top of the stack.
-    OpMul,
+    Mul,
 
     /// Divide the two numbers at the top of the stack.
-    OpDiv,
+    Div,
 
-    // Comparison operators
     /// Check the two values at the top of the stack for Nix-equality.
-    OpEqual,
+    Equal,
 
     /// Check whether the value at {2} is less than {1}.
-    OpLess,
+    Less,
 
     /// Check whether the value at {2} is less than or equal to {1}.
-    OpLessOrEq,
+    LessOrEq,
 
     /// Check whether the value at {2} is greater than {1}.
-    OpMore,
+    More,
 
     /// Check whether the value at {2} is greater than or equal to {1}.
-    OpMoreOrEq,
+    MoreOrEq,
 
-    // Logical operators & generic jumps
     /// Jump forward in the bytecode specified by the number of
     /// instructions in its usize operand.
-    OpJump(JumpOffset),
+    Jump,
 
     /// Jump forward in the bytecode specified by the number of
     /// instructions in its usize operand, *if* the value at the top
     /// of the stack is `true`.
-    OpJumpIfTrue(JumpOffset),
+    JumpIfTrue,
 
     /// Jump forward in the bytecode specified by the number of
     /// instructions in its usize operand, *if* the value at the top
     /// of the stack is `false`.
-    OpJumpIfFalse(JumpOffset),
+    JumpIfFalse,
 
     /// Pop one stack item and jump forward in the bytecode
     /// specified by the number of instructions in its usize
     /// operand, *if* the value at the top of the stack is a
     /// Value::Catchable.
-    OpJumpIfCatchable(JumpOffset),
+    JumpIfCatchable,
 
     /// Jump forward in the bytecode specified by the number of
     /// instructions in its usize operand, *if* the value at the top
     /// of the stack is the internal value representing a missing
     /// attribute set key.
-    OpJumpIfNotFound(JumpOffset),
+    JumpIfNotFound,
 
     /// Jump forward in the bytecode specified by the number of
     /// instructions in its usize operand, *if* the value at the top
     /// of the stack is *not* the internal value requesting a
     /// stack value finalisation.
-    OpJumpIfNoFinaliseRequest(JumpOffset),
+    JumpIfNoFinaliseRequest,
+
+    /// Construct an attribute set from the given number of key-value pairs on
+    /// the top of the stack. The operand gives the count of *pairs*, not the
+    /// number of *stack values* - the actual number of values popped off the
+    /// stack will be twice the argument to this op.
+    Attrs,
 
-    // Attribute sets
-    /// Construct an attribute set from the given number of key-value pairs on the top of the stack
-    ///
-    /// Note that this takes the count of *pairs*, not the number of *stack values* - the actual
-    /// number of values popped off the stack will be twice the argument to this op
-    OpAttrs(Count),
     /// Merge the attribute set at {2} into the attribute set at {1},
     /// and leave the new set at the top of the stack.
-    OpAttrsUpdate,
+    AttrsUpdate,
 
     /// Select the attribute with the name at {1} from the set at {2}.
-    OpAttrsSelect,
+    AttrsSelect,
 
     /// Select the attribute with the name at {1} from the set at {2}, but leave
     /// a `Value::AttrNotFound` in the stack instead of failing if it is
     /// missing.
-    OpAttrsTrySelect,
+    AttrsTrySelect,
 
     /// Check for the presence of the attribute with the name at {1} in the set
     /// at {2}.
-    OpHasAttr,
+    HasAttr,
 
     /// Throw an error if the attribute set at the top of the stack has any attributes
     /// other than those listed in the formals of the current lambda
     ///
     /// Panics if the current frame is not a lambda with formals
-    OpValidateClosedFormals,
+    ValidateClosedFormals,
 
-    // `with`-handling
     /// Push a value onto the runtime `with`-stack to enable dynamic identifier
     /// resolution. The absolute stack index of the value is supplied as a usize
     /// operand.
-    OpPushWith(StackIdx),
+    PushWith,
 
     /// Pop the last runtime `with`-stack element.
-    OpPopWith,
+    PopWith,
 
     /// Dynamically resolve an identifier with the name at {1} from the runtime
     /// `with`-stack.
-    OpResolveWith,
+    ResolveWith,
 
     // Lists
     /// Construct a list from the given number of values at the top of the
     /// stack.
-    OpList(Count),
+    List,
 
     /// Concatenate the lists at {2} and {1}.
-    OpConcat,
+    Concat,
 
     // Strings
     /// Interpolate the given number of string fragments into a single string.
-    OpInterpolate(Count),
+    Interpolate,
 
     /// Force the Value on the stack and coerce it to a string
-    OpCoerceToString(crate::CoercionKind),
+    CoerceToString,
 
     // Paths
     /// Attempt to resolve the Value on the stack using the configured [`NixSearchPath`][]
     ///
     /// [`NixSearchPath`]: crate::nix_search_path::NixSearchPath
-    OpFindFile,
+    FindFile,
 
     /// Attempt to resolve a path literal relative to the home dir
-    OpResolveHomePath,
+    ResolveHomePath,
 
     // Type assertion operators
     /// Assert that the value at {1} is a boolean, and fail with a runtime error
     /// otherwise.
-    OpAssertBool,
-    OpAssertAttrs,
+    AssertBool,
+    AssertAttrs,
 
     /// Access local identifiers with statically known positions.
-    OpGetLocal(StackIdx),
+    GetLocal,
 
     /// Close scopes while leaving their expression value around.
-    OpCloseScope(Count), // number of locals to pop
+    CloseScope,
 
     /// Return an error indicating that an `assert` failed
-    OpAssertFail,
+    AssertFail,
 
     // Lambdas & closures
     /// Call the value at {1} in a new VM callframe
-    OpCall,
+    Call,
 
     /// Retrieve the upvalue at the given index from the closure or thunk
     /// currently under evaluation.
-    OpGetUpvalue(UpvalueIdx),
+    GetUpvalue,
 
     /// Construct a closure which has upvalues but no self-references
-    OpClosure(ConstantIdx),
+    Closure,
 
     /// Construct a closure which has self-references (direct or via upvalues)
-    OpThunkClosure(ConstantIdx),
+    ThunkClosure,
 
     /// Construct a suspended thunk, used to delay a computation for laziness.
-    OpThunkSuspended(ConstantIdx),
+    ThunkSuspended,
 
     /// Force the value at {1} until it is a `Thunk::Evaluated`.
-    OpForce,
+    Force,
 
     /// Finalise initialisation of the upvalues of the value in the given stack
     /// index (which must be a Value::Thunk) after the scope is fully bound.
-    OpFinalise(StackIdx),
+    Finalise,
 
     /// Final instruction emitted in a chunk. Does not have an
     /// inherent effect, but can simplify VM logic as a marker in some
@@ -258,27 +252,140 @@ pub enum OpCode {
     ///
     /// Can be thought of as "returning" the value to the parent
     /// frame, hence the name.
-    OpReturn,
-
-    // [`OpClosure`], [`OpThunkSuspended`], and [`OpThunkClosure`] have a
-    // variable number of arguments to the instruction, which is
-    // represented here by making their data part of the opcodes.
-    // Each of these two opcodes has a `ConstantIdx`, which must
-    // reference a `Value::Blueprint(Lambda)`.  The `upvalue_count`
-    // field in that `Lambda` indicates the number of arguments it
-    // takes, and the opcode must be followed by exactly this number
-    // of `Data*` opcodes.  The VM skips over these by advancing the
-    // instruction pointer.
-    //
-    // It is illegal for a `Data*` opcode to appear anywhere else.
-    /// Populate a static upvalue by copying from the stack immediately.
-    DataStackIdx(StackIdx),
-    /// Populate a static upvalue of a thunk by copying it the stack, but do
-    /// when the thunk is finalised (by OpFinalise) rather than immediately.
-    DataDeferredLocal(StackIdx),
-    /// Populate a static upvalue by copying it from the upvalues of an
-    /// enclosing scope.
-    DataUpvalueIdx(UpvalueIdx),
-    /// Populate dynamic upvalues by saving a copy of the with-stack.
-    DataCaptureWith,
+    Return,
+
+    /// Sentinel value to signal invalid bytecode. This MUST always be the last
+    /// value in the enum. Do not move it!
+    Invalid,
+}
+
+const _ASSERT_SMALL_OP: () = assert!(std::mem::size_of::<Op>() == 1);
+
+impl From<u8> for Op {
+    fn from(num: u8) -> Self {
+        if num >= Self::Invalid as u8 {
+            return Self::Invalid;
+        }
+
+        // SAFETY: As long as `Invalid` remains the last variant of the enum,
+        // and as long as variant values are not specified manually, this
+        // conversion is safe.
+        unsafe { std::mem::transmute(num) }
+    }
+}
+
+pub enum OpArg {
+    None,
+    Uvarint,
+    Fixed,
+    Custom,
+}
+
+impl Op {
+    pub fn arg_type(&self) -> OpArg {
+        match self {
+            Op::Constant
+            | Op::Attrs
+            | Op::PushWith
+            | Op::List
+            | Op::Interpolate
+            | Op::GetLocal
+            | Op::CloseScope
+            | Op::GetUpvalue
+            | Op::Finalise => OpArg::Uvarint,
+
+            Op::Jump
+            | Op::JumpIfTrue
+            | Op::JumpIfFalse
+            | Op::JumpIfCatchable
+            | Op::JumpIfNotFound
+            | Op::JumpIfNoFinaliseRequest => OpArg::Fixed,
+
+            Op::CoerceToString | Op::Closure | Op::ThunkClosure | Op::ThunkSuspended => {
+                OpArg::Custom
+            }
+            _ => OpArg::None,
+        }
+    }
+}
+
+/// Position is used to represent where to capture an upvalue from.
+#[derive(Clone, Copy)]
+pub struct Position(pub u64);
+
+impl Position {
+    pub fn stack_index(idx: StackIdx) -> Self {
+        Position((idx.0 as u64) << 2)
+    }
+
+    pub fn deferred_local(idx: StackIdx) -> Self {
+        Position(((idx.0 as u64) << 2) | 1)
+    }
+
+    pub fn upvalue_index(idx: UpvalueIdx) -> Self {
+        Position(((idx.0 as u64) << 2) | 2)
+    }
+
+    pub fn runtime_stack_index(&self) -> Option<StackIdx> {
+        if (self.0 & 0b11) == 0 {
+            return Some(StackIdx((self.0 >> 2) as usize));
+        }
+
+        None
+    }
+
+    pub fn runtime_deferred_local(&self) -> Option<StackIdx> {
+        if (self.0 & 0b11) == 1 {
+            return Some(StackIdx((self.0 >> 2) as usize));
+        }
+
+        None
+    }
+
+    pub fn runtime_upvalue_index(&self) -> Option<UpvalueIdx> {
+        if (self.0 & 0b11) == 2 {
+            return Some(UpvalueIdx((self.0 >> 2) as usize));
+        }
+
+        None
+    }
+}
+
+#[cfg(test)]
+mod position_tests {
+    use super::Position; // he-he
+    use super::{StackIdx, UpvalueIdx};
+
+    #[test]
+    fn test_stack_index_position() {
+        let idx = StackIdx(42);
+        let pos = Position::stack_index(idx);
+        let result = pos.runtime_stack_index();
+
+        assert_eq!(result, Some(idx));
+        assert_eq!(pos.runtime_deferred_local(), None);
+        assert_eq!(pos.runtime_upvalue_index(), None);
+    }
+
+    #[test]
+    fn test_deferred_local_position() {
+        let idx = StackIdx(42);
+        let pos = Position::deferred_local(idx);
+        let result = pos.runtime_deferred_local();
+
+        assert_eq!(result, Some(idx));
+        assert_eq!(pos.runtime_stack_index(), None);
+        assert_eq!(pos.runtime_upvalue_index(), None);
+    }
+
+    #[test]
+    fn test_upvalue_index_position() {
+        let idx = UpvalueIdx(42);
+        let pos = Position::upvalue_index(idx);
+        let result = pos.runtime_upvalue_index();
+
+        assert_eq!(result, Some(idx));
+        assert_eq!(pos.runtime_stack_index(), None);
+        assert_eq!(pos.runtime_deferred_local(), None);
+    }
 }
diff --git a/tvix/eval/src/value/arbitrary.rs b/tvix/eval/src/value/arbitrary.rs
index bf53f4fcb28a..49b9f2eea3fb 100644
--- a/tvix/eval/src/value/arbitrary.rs
+++ b/tvix/eval/src/value/arbitrary.rs
@@ -1,6 +1,6 @@
 //! Support for configurable generation of arbitrary nix values
 
-use imbl::proptest::{ord_map, vector};
+use proptest::collection::{btree_map, vec};
 use proptest::{prelude::*, strategy::BoxedStrategy};
 use std::ffi::OsString;
 
@@ -33,16 +33,16 @@ impl Arbitrary for NixAttrs {
     fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
         prop_oneof![
             // Empty attrs representation
-            Just(Self(AttrsRep::Empty)),
+            Just(AttrsRep::Empty.into()),
             // KV representation (name/value pairs)
             (
                 any_with::<Value>(args.clone()),
                 any_with::<Value>(args.clone())
             )
-                .prop_map(|(name, value)| Self(AttrsRep::KV { name, value })),
+                .prop_map(|(name, value)| AttrsRep::KV { name, value }.into()),
             // Map representation
-            ord_map(NixString::arbitrary(), Value::arbitrary_with(args), 0..100)
-                .prop_map(|map| Self(AttrsRep::Im(map)))
+            btree_map(NixString::arbitrary(), Value::arbitrary_with(args), 0..100)
+                .prop_map(|map| AttrsRep::Map(map).into())
         ]
         .boxed()
     }
@@ -53,7 +53,7 @@ impl Arbitrary for NixList {
     type Strategy = BoxedStrategy<Self>;
 
     fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
-        vector(<Value as Arbitrary>::arbitrary_with(args), 0..100)
+        vec(<Value as Arbitrary>::arbitrary_with(args), 0..100)
             .prop_map(NixList::from)
             .boxed()
     }
diff --git a/tvix/eval/src/value/attrs.rs b/tvix/eval/src/value/attrs.rs
index 33259c8058eb..00b913347418 100644
--- a/tvix/eval/src/value/attrs.rs
+++ b/tvix/eval/src/value/attrs.rs
@@ -6,10 +6,11 @@
 //! Due to this, construction and management of attribute sets has
 //! some peculiarities that are encapsulated within this module.
 use std::borrow::Borrow;
+use std::collections::{btree_map, BTreeMap};
 use std::iter::FromIterator;
+use std::rc::Rc;
 
 use bstr::BStr;
-use imbl::{ordmap, OrdMap};
 use lazy_static::lazy_static;
 use serde::de::{Deserializer, Error, Visitor};
 use serde::Deserialize;
@@ -36,7 +37,7 @@ pub(super) enum AttrsRep {
     #[default]
     Empty,
 
-    Im(OrdMap<NixString, Value>),
+    Map(BTreeMap<NixString, Value>),
 
     /// Warning: this represents a **two**-attribute attrset, with
     /// attribute names "name" and "value", like `{name="foo";
@@ -48,28 +49,6 @@ pub(super) enum AttrsRep {
 }
 
 impl AttrsRep {
-    /// Retrieve reference to a mutable map inside of an attrs,
-    /// optionally changing the representation if required.
-    fn map_mut(&mut self) -> &mut OrdMap<NixString, Value> {
-        match self {
-            AttrsRep::Im(m) => m,
-
-            AttrsRep::Empty => {
-                *self = AttrsRep::Im(OrdMap::new());
-                self.map_mut()
-            }
-
-            AttrsRep::KV { name, value } => {
-                *self = AttrsRep::Im(ordmap! {
-                    NAME_S.clone() => name.clone(),
-                    VALUE_S.clone() => value.clone()
-                });
-
-                self.map_mut()
-            }
-        }
-    }
-
     fn select(&self, key: &BStr) -> Option<&Value> {
         match self {
             AttrsRep::Empty => None,
@@ -80,7 +59,7 @@ impl AttrsRep {
                 _ => None,
             },
 
-            AttrsRep::Im(map) => map.get(key),
+            AttrsRep::Map(map) => map.get(key),
         }
     }
 
@@ -88,18 +67,18 @@ impl AttrsRep {
         match self {
             AttrsRep::Empty => false,
             AttrsRep::KV { .. } => key == "name" || key == "value",
-            AttrsRep::Im(map) => map.contains_key(key),
+            AttrsRep::Map(map) => map.contains_key(key),
         }
     }
 }
 
 #[repr(transparent)]
 #[derive(Clone, Debug, Default)]
-pub struct NixAttrs(pub(super) AttrsRep);
+pub struct NixAttrs(pub(super) Rc<AttrsRep>);
 
-impl From<OrdMap<NixString, Value>> for NixAttrs {
-    fn from(map: OrdMap<NixString, Value>) -> Self {
-        NixAttrs(AttrsRep::Im(map))
+impl From<AttrsRep> for NixAttrs {
+    fn from(rep: AttrsRep) -> Self {
+        NixAttrs(Rc::new(rep))
     }
 }
 
@@ -112,34 +91,41 @@ where
     where
         T: IntoIterator<Item = (K, V)>,
     {
-        NixAttrs(AttrsRep::Im(iter.into_iter().collect()))
+        AttrsRep::Map(
+            iter.into_iter()
+                .map(|(k, v)| (k.into(), v.into()))
+                .collect(),
+        )
+        .into()
+    }
+}
+
+impl From<BTreeMap<NixString, Value>> for NixAttrs {
+    fn from(map: BTreeMap<NixString, Value>) -> Self {
+        AttrsRep::Map(map).into()
     }
 }
 
 impl TotalDisplay for NixAttrs {
     fn total_fmt(&self, f: &mut std::fmt::Formatter<'_>, set: &mut ThunkSet) -> std::fmt::Result {
-        f.write_str("{ ")?;
-
-        match &self.0 {
-            AttrsRep::KV { name, value } => {
-                f.write_str("name = ")?;
-                name.total_fmt(f, set)?;
-                f.write_str("; ")?;
-
-                f.write_str("value = ")?;
-                value.total_fmt(f, set)?;
-                f.write_str("; ")?;
-            }
-
-            AttrsRep::Im(map) => {
-                for (name, value) in map {
-                    write!(f, "{} = ", name.ident_str())?;
-                    value.total_fmt(f, set)?;
-                    f.write_str("; ")?;
+        if let Some(Value::String(s)) = self.select("type") {
+            if *s == "derivation" {
+                write!(f, "«derivation ")?;
+                if let Some(p) = self.select("drvPath") {
+                    p.total_fmt(f, set)?;
+                } else {
+                    write!(f, "???")?;
                 }
+                return write!(f, "»");
             }
+        }
+
+        f.write_str("{ ")?;
 
-            AttrsRep::Empty => { /* no values to print! */ }
+        for (name, value) in self.iter_sorted() {
+            write!(f, "{} = ", name.ident_str())?;
+            value.total_fmt(f, set)?;
+            f.write_str("; ")?;
         }
 
         f.write_str("}")
@@ -183,25 +169,22 @@ impl<'de> Deserialize<'de> for NixAttrs {
 
 impl NixAttrs {
     pub fn empty() -> Self {
-        Self(AttrsRep::Empty)
+        AttrsRep::Empty.into()
     }
 
     /// Compare two attribute sets by pointer equality. Only makes
-    /// sense for some attribute set reprsentations, i.e. returning
+    /// sense for some attribute set representations, i.e. returning
     /// `false` does not mean that the two attribute sets do not have
     /// equal *content*.
     pub fn ptr_eq(&self, other: &Self) -> bool {
-        match (&self.0, &other.0) {
-            (AttrsRep::Im(lhs), AttrsRep::Im(rhs)) => lhs.ptr_eq(rhs),
-            _ => false,
-        }
+        Rc::ptr_eq(&self.0, &other.0)
     }
 
     /// Return an attribute set containing the merge of the two
     /// provided sets. Keys from the `other` set have precedence.
     pub fn update(self, other: Self) -> Self {
         // Short-circuit on some optimal cases:
-        match (&self.0, &other.0) {
+        match (self.0.as_ref(), other.0.as_ref()) {
             (AttrsRep::Empty, AttrsRep::Empty) => return self,
             (AttrsRep::Empty, _) => return other,
             (_, AttrsRep::Empty) => return self,
@@ -210,41 +193,44 @@ impl NixAttrs {
             // Explicitly handle all branches instead of falling
             // through, to ensure that we get at least some compiler
             // errors if variants are modified.
-            (AttrsRep::Im(_), AttrsRep::Im(_))
-            | (AttrsRep::Im(_), AttrsRep::KV { .. })
-            | (AttrsRep::KV { .. }, AttrsRep::Im(_)) => {}
+            (AttrsRep::Map(_), AttrsRep::Map(_))
+            | (AttrsRep::Map(_), AttrsRep::KV { .. })
+            | (AttrsRep::KV { .. }, AttrsRep::Map(_)) => {}
         };
 
         // Slightly more advanced, but still optimised updates
-        match (self.0, other.0) {
-            (AttrsRep::Im(mut m), AttrsRep::KV { name, value }) => {
+        match (Rc::unwrap_or_clone(self.0), Rc::unwrap_or_clone(other.0)) {
+            (AttrsRep::Map(mut m), AttrsRep::KV { name, value }) => {
                 m.insert(NAME_S.clone(), name);
                 m.insert(VALUE_S.clone(), value);
-                NixAttrs(AttrsRep::Im(m))
+                AttrsRep::Map(m).into()
             }
 
-            (AttrsRep::KV { name, value }, AttrsRep::Im(mut m)) => {
+            (AttrsRep::KV { name, value }, AttrsRep::Map(mut m)) => {
                 match m.entry(NAME_S.clone()) {
-                    imbl::ordmap::Entry::Vacant(e) => {
+                    btree_map::Entry::Vacant(e) => {
                         e.insert(name);
                     }
 
-                    imbl::ordmap::Entry::Occupied(_) => { /* name from `m` has precedence */ }
+                    btree_map::Entry::Occupied(_) => { /* name from `m` has precedence */ }
                 };
 
                 match m.entry(VALUE_S.clone()) {
-                    imbl::ordmap::Entry::Vacant(e) => {
+                    btree_map::Entry::Vacant(e) => {
                         e.insert(value);
                     }
 
-                    imbl::ordmap::Entry::Occupied(_) => { /* value from `m` has precedence */ }
+                    btree_map::Entry::Occupied(_) => { /* value from `m` has precedence */ }
                 };
 
-                NixAttrs(AttrsRep::Im(m))
+                AttrsRep::Map(m).into()
             }
 
             // Plain merge of maps.
-            (AttrsRep::Im(m1), AttrsRep::Im(m2)) => NixAttrs(AttrsRep::Im(m2.union(m1))),
+            (AttrsRep::Map(mut m1), AttrsRep::Map(m2)) => {
+                m1.extend(m2);
+                AttrsRep::Map(m1).into()
+            }
 
             // Cases handled above by the borrowing match:
             _ => unreachable!(),
@@ -253,16 +239,16 @@ impl NixAttrs {
 
     /// Return the number of key-value entries in an attrset.
     pub fn len(&self) -> usize {
-        match &self.0 {
-            AttrsRep::Im(map) => map.len(),
+        match self.0.as_ref() {
+            AttrsRep::Map(map) => map.len(),
             AttrsRep::Empty => 0,
             AttrsRep::KV { .. } => 2,
         }
     }
 
     pub fn is_empty(&self) -> bool {
-        match &self.0 {
-            AttrsRep::Im(map) => map.is_empty(),
+        match self.0.as_ref() {
+            AttrsRep::Map(map) => map.is_empty(),
             AttrsRep::Empty => true,
             AttrsRep::KV { .. } => false,
         }
@@ -298,8 +284,8 @@ impl NixAttrs {
     /// Construct an iterator over all the key-value pairs in the attribute set.
     #[allow(clippy::needless_lifetimes)]
     pub fn iter<'a>(&'a self) -> Iter<KeyValue<'a>> {
-        Iter(match &self.0 {
-            AttrsRep::Im(map) => KeyValue::Im(map.iter()),
+        Iter(match &self.0.as_ref() {
+            AttrsRep::Map(map) => KeyValue::Map(map.iter()),
             AttrsRep::Empty => KeyValue::Empty,
 
             AttrsRep::KV {
@@ -327,10 +313,12 @@ impl NixAttrs {
 
     /// Construct an iterator over all the keys of the attribute set
     pub fn keys(&self) -> Keys {
-        Keys(match &self.0 {
+        Keys(match self.0.as_ref() {
             AttrsRep::Empty => KeysInner::Empty,
-            AttrsRep::Im(m) => KeysInner::Im(m.keys()),
             AttrsRep::KV { .. } => KeysInner::KV(IterKV::default()),
+
+            // TODO(tazjin): only sort when required, not always.
+            AttrsRep::Map(m) => KeysInner::Map(m.keys()),
         })
     }
 
@@ -349,7 +337,7 @@ impl NixAttrs {
 
         // Optimisation: Empty attribute set
         if count == 0 {
-            return Ok(Ok(NixAttrs(AttrsRep::Empty)));
+            return Ok(Ok(AttrsRep::Empty.into()));
         }
 
         // Optimisation: KV pattern
@@ -359,14 +347,14 @@ impl NixAttrs {
             }
         }
 
-        let mut attrs = NixAttrs(AttrsRep::Im(OrdMap::new()));
+        let mut attrs_map = BTreeMap::new();
 
         for _ in 0..count {
             let value = stack_slice.pop().unwrap();
             let key = stack_slice.pop().unwrap();
 
             match key {
-                Value::String(ks) => set_attr(&mut attrs, ks, value)?,
+                Value::String(ks) => set_attr(&mut attrs_map, ks, value)?,
 
                 Value::Null => {
                     // This is in fact valid, but leads to the value
@@ -381,13 +369,13 @@ impl NixAttrs {
             }
         }
 
-        Ok(Ok(attrs))
+        Ok(Ok(AttrsRep::Map(attrs_map).into()))
     }
 
     /// Construct an optimized "KV"-style attribute set given the value for the
     /// `"name"` key, and the value for the `"value"` key
     pub(crate) fn from_kv(name: Value, value: Value) -> Self {
-        NixAttrs(AttrsRep::KV { name, value })
+        AttrsRep::KV { name, value }.into()
     }
 }
 
@@ -396,12 +384,12 @@ impl IntoIterator for NixAttrs {
     type IntoIter = OwnedAttrsIterator;
 
     fn into_iter(self) -> Self::IntoIter {
-        match self.0 {
+        match Rc::unwrap_or_clone(self.0) {
             AttrsRep::Empty => OwnedAttrsIterator(IntoIterRepr::Empty),
             AttrsRep::KV { name, value } => OwnedAttrsIterator(IntoIterRepr::Finite(
                 vec![(NAME_REF.clone(), name), (VALUE_REF.clone(), value)].into_iter(),
             )),
-            AttrsRep::Im(map) => OwnedAttrsIterator(IntoIterRepr::Im(map.into_iter())),
+            AttrsRep::Map(map) => OwnedAttrsIterator(IntoIterRepr::Map(map.into_iter())),
         }
     }
 }
@@ -440,13 +428,17 @@ fn attempt_optimise_kv(slice: &mut [Value]) -> Option<NixAttrs> {
 
 /// Set an attribute on an in-construction attribute set, while
 /// checking against duplicate keys.
-fn set_attr(attrs: &mut NixAttrs, key: NixString, value: Value) -> Result<(), ErrorKind> {
-    match attrs.0.map_mut().entry(key) {
-        imbl::ordmap::Entry::Occupied(entry) => Err(ErrorKind::DuplicateAttrsKey {
+fn set_attr(
+    map: &mut BTreeMap<NixString, Value>,
+    key: NixString,
+    value: Value,
+) -> Result<(), ErrorKind> {
+    match map.entry(key) {
+        btree_map::Entry::Occupied(entry) => Err(ErrorKind::DuplicateAttrsKey {
             key: entry.key().to_string(),
         }),
 
-        imbl::ordmap::Entry::Vacant(entry) => {
+        btree_map::Entry::Vacant(entry) => {
             entry.insert(value);
             Ok(())
         }
@@ -484,7 +476,7 @@ pub enum KeyValue<'a> {
         at: IterKV,
     },
 
-    Im(imbl::ordmap::Iter<'a, NixString, Value>),
+    Map(btree_map::Iter<'a, NixString, Value>),
 }
 
 /// Iterator over a Nix attribute set.
@@ -498,7 +490,7 @@ impl<'a> Iterator for Iter<KeyValue<'a>> {
 
     fn next(&mut self) -> Option<Self::Item> {
         match &mut self.0 {
-            KeyValue::Im(inner) => inner.next(),
+            KeyValue::Map(inner) => inner.next(),
             KeyValue::Empty => None,
 
             KeyValue::KV { name, value, at } => match at {
@@ -523,7 +515,7 @@ impl<'a> ExactSizeIterator for Iter<KeyValue<'a>> {
         match &self.0 {
             KeyValue::Empty => 0,
             KeyValue::KV { .. } => 2,
-            KeyValue::Im(inner) => inner.len(),
+            KeyValue::Map(inner) => inner.len(),
         }
     }
 }
@@ -531,7 +523,7 @@ impl<'a> ExactSizeIterator for Iter<KeyValue<'a>> {
 enum KeysInner<'a> {
     Empty,
     KV(IterKV),
-    Im(imbl::ordmap::Keys<'a, NixString, Value>),
+    Map(btree_map::Keys<'a, NixString, Value>),
 }
 
 pub struct Keys<'a>(KeysInner<'a>);
@@ -551,7 +543,7 @@ impl<'a> Iterator for Keys<'a> {
                 Some(&VALUE_REF)
             }
             KeysInner::KV(IterKV::Done) => None,
-            KeysInner::Im(m) => m.next(),
+            KeysInner::Map(m) => m.next(),
         }
     }
 }
@@ -571,7 +563,7 @@ impl<'a> ExactSizeIterator for Keys<'a> {
         match &self.0 {
             KeysInner::Empty => 0,
             KeysInner::KV(_) => 2,
-            KeysInner::Im(m) => m.len(),
+            KeysInner::Map(m) => m.len(),
         }
     }
 }
@@ -580,7 +572,7 @@ impl<'a> ExactSizeIterator for Keys<'a> {
 pub enum IntoIterRepr {
     Empty,
     Finite(std::vec::IntoIter<(NixString, Value)>),
-    Im(imbl::ordmap::ConsumingIter<(NixString, Value)>),
+    Map(btree_map::IntoIter<NixString, Value>),
 }
 
 /// Wrapper type which hides the internal implementation details from
@@ -595,7 +587,7 @@ impl Iterator for OwnedAttrsIterator {
         match &mut self.0 {
             IntoIterRepr::Empty => None,
             IntoIterRepr::Finite(inner) => inner.next(),
-            IntoIterRepr::Im(inner) => inner.next(),
+            IntoIterRepr::Map(m) => m.next(),
         }
     }
 }
@@ -605,7 +597,7 @@ impl ExactSizeIterator for OwnedAttrsIterator {
         match &self.0 {
             IntoIterRepr::Empty => 0,
             IntoIterRepr::Finite(inner) => inner.len(),
-            IntoIterRepr::Im(inner) => inner.len(),
+            IntoIterRepr::Map(inner) => inner.len(),
         }
     }
 }
@@ -615,7 +607,7 @@ impl DoubleEndedIterator for OwnedAttrsIterator {
         match &mut self.0 {
             IntoIterRepr::Empty => None,
             IntoIterRepr::Finite(inner) => inner.next_back(),
-            IntoIterRepr::Im(inner) => inner.next_back(),
+            IntoIterRepr::Map(inner) => inner.next_back(),
         }
     }
 }
diff --git a/tvix/eval/src/value/attrs/tests.rs b/tvix/eval/src/value/attrs/tests.rs
index 534b78a00d10..b79f45a71b28 100644
--- a/tvix/eval/src/value/attrs/tests.rs
+++ b/tvix/eval/src/value/attrs/tests.rs
@@ -9,7 +9,7 @@ fn test_empty_attrs() {
         .unwrap();
 
     assert!(
-        matches!(attrs, NixAttrs(AttrsRep::Empty)),
+        matches!(attrs.0.as_ref(), AttrsRep::Empty),
         "empty attribute set should use optimised representation"
     );
 }
@@ -21,7 +21,7 @@ fn test_simple_attrs() {
         .unwrap();
 
     assert!(
-        matches!(attrs, NixAttrs(AttrsRep::Im(_))),
+        matches!(attrs.0.as_ref(), AttrsRep::Map(_)),
         "simple attribute set should use map representation",
     )
 }
@@ -45,8 +45,8 @@ fn test_kv_attrs() {
     .expect("constructing K/V pair attrs should succeed")
     .unwrap();
 
-    match kv_attrs {
-        NixAttrs(AttrsRep::KV { name, value })
+    match kv_attrs.0.as_ref() {
+        AttrsRep::KV { name, value }
             if name.to_str().unwrap() == meaning_val.to_str().unwrap()
                 || value.to_str().unwrap() == forty_two_val.to_str().unwrap() => {}
 
diff --git a/tvix/eval/src/value/list.rs b/tvix/eval/src/value/list.rs
index 2b8b3de28d19..3e4b23a93f42 100644
--- a/tvix/eval/src/value/list.rs
+++ b/tvix/eval/src/value/list.rs
@@ -2,8 +2,6 @@
 use std::ops::Index;
 use std::rc::Rc;
 
-use imbl::{vector, Vector};
-
 use serde::Deserialize;
 
 use super::thunk::ThunkSet;
@@ -12,7 +10,7 @@ use super::Value;
 
 #[repr(transparent)]
 #[derive(Clone, Debug, Deserialize)]
-pub struct NixList(Rc<Vector<Value>>);
+pub struct NixList(Rc<Vec<Value>>);
 
 impl TotalDisplay for NixList {
     fn total_fmt(&self, f: &mut std::fmt::Formatter<'_>, set: &mut ThunkSet) -> std::fmt::Result {
@@ -27,8 +25,8 @@ impl TotalDisplay for NixList {
     }
 }
 
-impl From<Vector<Value>> for NixList {
-    fn from(vs: Vector<Value>) -> Self {
+impl From<Vec<Value>> for NixList {
+    fn from(vs: Vec<Value>) -> Self {
         Self(Rc::new(vs))
     }
 }
@@ -54,10 +52,10 @@ impl NixList {
             stack_slice.len(),
         );
 
-        NixList(Rc::new(Vector::from_iter(stack_slice)))
+        NixList(Rc::new(stack_slice))
     }
 
-    pub fn iter(&self) -> vector::Iter<Value> {
+    pub fn iter(&self) -> std::slice::Iter<Value> {
         self.0.iter()
     }
 
@@ -65,19 +63,14 @@ impl NixList {
         Rc::ptr_eq(&self.0, &other.0)
     }
 
-    pub fn into_inner(self) -> Vector<Value> {
+    pub fn into_inner(self) -> Vec<Value> {
         Rc::try_unwrap(self.0).unwrap_or_else(|rc| (*rc).clone())
     }
-
-    #[deprecated(note = "callers should avoid constructing from Vec")]
-    pub fn from_vec(vs: Vec<Value>) -> Self {
-        Self(Rc::new(Vector::from_iter(vs)))
-    }
 }
 
 impl IntoIterator for NixList {
     type Item = Value;
-    type IntoIter = imbl::vector::ConsumingIter<Value>;
+    type IntoIter = std::vec::IntoIter<Value>;
 
     fn into_iter(self) -> Self::IntoIter {
         self.into_inner().into_iter()
@@ -86,7 +79,7 @@ impl IntoIterator for NixList {
 
 impl<'a> IntoIterator for &'a NixList {
     type Item = &'a Value;
-    type IntoIter = imbl::vector::Iter<'a, Value>;
+    type IntoIter = std::slice::Iter<'a, Value>;
 
     fn into_iter(self) -> Self::IntoIter {
         self.0.iter()
diff --git a/tvix/eval/src/value/mod.rs b/tvix/eval/src/value/mod.rs
index a5bc17c15294..2e78f20b49a0 100644
--- a/tvix/eval/src/value/mod.rs
+++ b/tvix/eval/src/value/mod.rs
@@ -84,7 +84,6 @@ pub enum Value {
     FinaliseRequest(bool),
 
     #[serde(skip)]
-    // TODO(tazjin): why is this in a Box?
     Catchable(Box<CatchableErrorKind>),
 }
 
@@ -187,6 +186,21 @@ pub struct CoercionKind {
     pub import_paths: bool,
 }
 
+impl From<CoercionKind> for u8 {
+    fn from(k: CoercionKind) -> u8 {
+        k.strong as u8 | (k.import_paths as u8) << 1
+    }
+}
+
+impl From<u8> for CoercionKind {
+    fn from(byte: u8) -> Self {
+        CoercionKind {
+            strong: byte & 0x01 != 0,
+            import_paths: byte & 0x02 != 0,
+        }
+    }
+}
+
 impl<T> From<T> for Value
 where
     T: Into<NixString>,
@@ -196,14 +210,6 @@ where
     }
 }
 
-/// Constructors
-impl Value {
-    /// Construct a [`Value::Attrs`] from a [`NixAttrs`].
-    pub fn attrs(attrs: NixAttrs) -> Self {
-        Self::Attrs(Box::new(attrs))
-    }
-}
-
 /// Controls what kind of by-pointer equality comparison is allowed.
 ///
 /// See `//tvix/docs/value-pointer-equality.md` for details.
@@ -220,6 +226,11 @@ pub enum PointerEquality {
 }
 
 impl Value {
+    /// Construct a [`Value::Attrs`] from a [`NixAttrs`].
+    pub fn attrs(attrs: NixAttrs) -> Self {
+        Self::Attrs(Box::new(attrs))
+    }
+
     /// Deeply forces a value, traversing e.g. lists and attribute sets and forcing
     /// their contents, too.
     ///
@@ -877,6 +888,12 @@ impl Value {
             | Value::FinaliseRequest(_) => "an internal Tvix evaluator value".into(),
         }
     }
+
+    /// Constructs a thunk that will be evaluated lazily at runtime. This lets
+    /// users of Tvix implement their own lazy builtins and so on.
+    pub fn suspended_native_thunk(native: Box<dyn Fn() -> Result<Value, ErrorKind>>) -> Self {
+        Value::Thunk(Thunk::new_suspended_native(native))
+    }
 }
 
 trait TotalDisplay {
diff --git a/tvix/eval/src/value/string/context.rs b/tvix/eval/src/value/string/context.rs
new file mode 100644
index 000000000000..e1c04735ddde
--- /dev/null
+++ b/tvix/eval/src/value/string/context.rs
@@ -0,0 +1,161 @@
+use rustc_hash::FxHashSet;
+use serde::Serialize;
+
+use super::NixString;
+
+#[derive(Clone, Debug, Serialize, Hash, PartialEq, Eq)]
+pub enum NixContextElement {
+    /// A plain store path (e.g. source files copied to the store)
+    Plain(String),
+
+    /// Single output of a derivation, represented by its name and its derivation path.
+    Single { name: String, derivation: String },
+
+    /// A reference to a complete derivation
+    /// including its source and its binary closure.
+    /// It is used for the `drvPath` attribute context.
+    /// The referred string is the store path to
+    /// the derivation path.
+    Derivation(String),
+}
+
+/// Nix context strings representation in Tvix. This tracks a set of different kinds of string
+/// dependencies that we can come across during manipulation of our language primitives, mostly
+/// strings. There's some simple algebra of context strings and how they propagate w.r.t. primitive
+/// operations, e.g. concatenation, interpolation and other string operations.
+#[repr(transparent)]
+#[derive(Clone, Debug, Serialize, Default)]
+pub struct NixContext(FxHashSet<NixContextElement>);
+
+impl From<NixContextElement> for NixContext {
+    fn from(value: NixContextElement) -> Self {
+        let mut set = FxHashSet::default();
+        set.insert(value);
+        Self(set)
+    }
+}
+
+impl From<FxHashSet<NixContextElement>> for NixContext {
+    fn from(value: FxHashSet<NixContextElement>) -> Self {
+        Self(value)
+    }
+}
+
+impl<const N: usize> From<[NixContextElement; N]> for NixContext {
+    fn from(value: [NixContextElement; N]) -> Self {
+        let mut set = FxHashSet::default();
+        for elt in value {
+            set.insert(elt);
+        }
+        Self(set)
+    }
+}
+
+impl NixContext {
+    /// Creates an empty context that can be populated
+    /// and passed to form a contextful [NixString], albeit
+    /// if the context is concretly empty, the resulting [NixString]
+    /// will be contextless.
+    pub fn new() -> Self {
+        Self::default()
+    }
+
+    /// For internal consumers, we let people observe
+    /// if the [NixContext] is actually empty or not
+    /// to decide whether they want to skip the allocation
+    /// of a full blown [HashSet].
+    pub(crate) fn is_empty(&self) -> bool {
+        self.0.is_empty()
+    }
+
+    /// Consumes a new [NixContextElement] and add it if not already
+    /// present in this context.
+    pub fn append(mut self, other: NixContextElement) -> Self {
+        self.0.insert(other);
+        self
+    }
+
+    /// Extends the existing context with more context elements.
+    pub fn extend<T>(&mut self, iter: T)
+    where
+        T: IntoIterator<Item = NixContextElement>,
+    {
+        self.0.extend(iter)
+    }
+
+    /// Copies from another [NixString] its context strings
+    /// in this context.
+    pub fn mimic(&mut self, other: &NixString) {
+        if let Some(context) = other.context() {
+            self.extend(context.iter().cloned());
+        }
+    }
+
+    /// Iterates over "plain" context elements, e.g. sources imported
+    /// in the store without more information, i.e. `toFile` or coerced imported paths.
+    /// It yields paths to the store.
+    pub fn iter_plain(&self) -> impl Iterator<Item = &str> {
+        self.iter().filter_map(|elt| {
+            if let NixContextElement::Plain(s) = elt {
+                Some(s.as_str())
+            } else {
+                None
+            }
+        })
+    }
+
+    /// Iterates over "full derivations" context elements, e.g. something
+    /// referring to their `drvPath`, i.e. their full sources and binary closure.
+    /// It yields derivation paths.
+    pub fn iter_derivation(&self) -> impl Iterator<Item = &str> {
+        self.iter().filter_map(|elt| {
+            if let NixContextElement::Derivation(s) = elt {
+                Some(s.as_str())
+            } else {
+                None
+            }
+        })
+    }
+
+    /// Iterates over "single" context elements, e.g. single derived paths,
+    /// or also known as the single output of a given derivation.
+    /// The first element of the tuple is the output name
+    /// and the second element is the derivation path.
+    pub fn iter_single_outputs(&self) -> impl Iterator<Item = (&str, &str)> {
+        self.iter().filter_map(|elt| {
+            if let NixContextElement::Single { name, derivation } = elt {
+                Some((name.as_str(), derivation.as_str()))
+            } else {
+                None
+            }
+        })
+    }
+
+    /// Iterates over any element of the context.
+    pub fn iter(&self) -> impl Iterator<Item = &NixContextElement> {
+        self.0.iter()
+    }
+
+    /// Produces a list of owned references to this current context,
+    /// no matter its type.
+    pub fn to_owned_references(self) -> Vec<String> {
+        self.0
+            .into_iter()
+            .map(|ctx| match ctx {
+                NixContextElement::Derivation(drv_path) => drv_path,
+                NixContextElement::Plain(store_path) => store_path,
+                NixContextElement::Single { derivation, .. } => derivation,
+            })
+            .collect()
+    }
+}
+
+impl IntoIterator for NixContext {
+    type Item = NixContextElement;
+
+    type IntoIter = std::collections::hash_set::IntoIter<NixContextElement>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        self.0.into_iter()
+    }
+}
diff --git a/tvix/eval/src/value/string.rs b/tvix/eval/src/value/string/mod.rs
index 0365f5a9be77..5bcb4786b283 100644
--- a/tvix/eval/src/value/string.rs
+++ b/tvix/eval/src/value/string/mod.rs
@@ -1,181 +1,30 @@
 //! This module implements Nix language strings.
 //!
-//! Nix language strings never need to be modified on the language
-//! level, allowing us to shave off some memory overhead and only
-//! paying the cost when creating new strings.
+//! See [`NixString`] for more information about the internals of string values
+
 use bstr::{BStr, BString, ByteSlice, Chars};
+use nohash_hasher::BuildNoHashHasher;
 use rnix::ast;
-use rustc_hash::{FxHashMap, FxHashSet};
+#[cfg(feature = "no_leak")]
+use rustc_hash::FxHashSet;
+use rustc_hash::FxHasher;
 use std::alloc::dealloc;
 use std::alloc::{alloc, handle_alloc_error, Layout};
 use std::borrow::{Borrow, Cow};
 use std::cell::RefCell;
 use std::ffi::c_void;
 use std::fmt::{self, Debug, Display};
-use std::hash::Hash;
+use std::hash::{Hash, Hasher};
 use std::ops::Deref;
 use std::ptr::{self, NonNull};
-use std::{mem, slice};
+use std::slice;
 
 use serde::de::{Deserializer, Visitor};
-use serde::{Deserialize, Serialize};
-
-#[derive(Clone, Debug, Serialize, Hash, PartialEq, Eq)]
-pub enum NixContextElement {
-    /// A plain store path (e.g. source files copied to the store)
-    Plain(String),
-
-    /// Single output of a derivation, represented by its name and its derivation path.
-    Single { name: String, derivation: String },
-
-    /// A reference to a complete derivation
-    /// including its source and its binary closure.
-    /// It is used for the `drvPath` attribute context.
-    /// The referred string is the store path to
-    /// the derivation path.
-    Derivation(String),
-}
-
-/// Nix context strings representation in Tvix. This tracks a set of different kinds of string
-/// dependencies that we can come across during manipulation of our language primitives, mostly
-/// strings. There's some simple algebra of context strings and how they propagate w.r.t. primitive
-/// operations, e.g. concatenation, interpolation and other string operations.
-#[repr(transparent)]
-#[derive(Clone, Debug, Serialize, Default)]
-pub struct NixContext(FxHashSet<NixContextElement>);
-
-impl From<NixContextElement> for NixContext {
-    fn from(value: NixContextElement) -> Self {
-        let mut set = FxHashSet::default();
-        set.insert(value);
-        Self(set)
-    }
-}
-
-impl From<FxHashSet<NixContextElement>> for NixContext {
-    fn from(value: FxHashSet<NixContextElement>) -> Self {
-        Self(value)
-    }
-}
-
-impl<const N: usize> From<[NixContextElement; N]> for NixContext {
-    fn from(value: [NixContextElement; N]) -> Self {
-        let mut set = FxHashSet::default();
-        for elt in value {
-            set.insert(elt);
-        }
-        Self(set)
-    }
-}
-
-impl NixContext {
-    /// Creates an empty context that can be populated
-    /// and passed to form a contextful [NixString], albeit
-    /// if the context is concretly empty, the resulting [NixString]
-    /// will be contextless.
-    pub fn new() -> Self {
-        Self::default()
-    }
+use serde::Deserialize;
 
-    /// For internal consumers, we let people observe
-    /// if the [NixContext] is actually empty or not
-    /// to decide whether they want to skip the allocation
-    /// of a full blown [HashSet].
-    pub(crate) fn is_empty(&self) -> bool {
-        self.0.is_empty()
-    }
+mod context;
 
-    /// Consumes a new [NixContextElement] and add it if not already
-    /// present in this context.
-    pub fn append(mut self, other: NixContextElement) -> Self {
-        self.0.insert(other);
-        self
-    }
-
-    /// Extends the existing context with more context elements.
-    pub fn extend<T>(&mut self, iter: T)
-    where
-        T: IntoIterator<Item = NixContextElement>,
-    {
-        self.0.extend(iter)
-    }
-
-    /// Copies from another [NixString] its context strings
-    /// in this context.
-    pub fn mimic(&mut self, other: &NixString) {
-        if let Some(context) = other.context() {
-            self.extend(context.iter().cloned());
-        }
-    }
-
-    /// Iterates over "plain" context elements, e.g. sources imported
-    /// in the store without more information, i.e. `toFile` or coerced imported paths.
-    /// It yields paths to the store.
-    pub fn iter_plain(&self) -> impl Iterator<Item = &str> {
-        self.iter().filter_map(|elt| {
-            if let NixContextElement::Plain(s) = elt {
-                Some(s.as_str())
-            } else {
-                None
-            }
-        })
-    }
-
-    /// Iterates over "full derivations" context elements, e.g. something
-    /// referring to their `drvPath`, i.e. their full sources and binary closure.
-    /// It yields derivation paths.
-    pub fn iter_derivation(&self) -> impl Iterator<Item = &str> {
-        self.iter().filter_map(|elt| {
-            if let NixContextElement::Derivation(s) = elt {
-                Some(s.as_str())
-            } else {
-                None
-            }
-        })
-    }
-
-    /// Iterates over "single" context elements, e.g. single derived paths,
-    /// or also known as the single output of a given derivation.
-    /// The first element of the tuple is the output name
-    /// and the second element is the derivation path.
-    pub fn iter_single_outputs(&self) -> impl Iterator<Item = (&str, &str)> {
-        self.iter().filter_map(|elt| {
-            if let NixContextElement::Single { name, derivation } = elt {
-                Some((name.as_str(), derivation.as_str()))
-            } else {
-                None
-            }
-        })
-    }
-
-    /// Iterates over any element of the context.
-    pub fn iter(&self) -> impl Iterator<Item = &NixContextElement> {
-        self.0.iter()
-    }
-
-    /// Produces a list of owned references to this current context,
-    /// no matter its type.
-    pub fn to_owned_references(self) -> Vec<String> {
-        self.0
-            .into_iter()
-            .map(|ctx| match ctx {
-                NixContextElement::Derivation(drv_path) => drv_path,
-                NixContextElement::Plain(store_path) => store_path,
-                NixContextElement::Single { derivation, .. } => derivation,
-            })
-            .collect()
-    }
-}
-
-impl IntoIterator for NixContext {
-    type Item = NixContextElement;
-
-    type IntoIter = std::collections::hash_set::IntoIter<NixContextElement>;
-
-    fn into_iter(self) -> Self::IntoIter {
-        self.0.into_iter()
-    }
-}
+pub use context::{NixContext, NixContextElement};
 
 /// This type is never instantiated, but serves to document the memory layout of the actual heap
 /// allocation for Nix strings.
@@ -399,22 +248,33 @@ impl NixStringInner {
 
 #[derive(Default)]
 struct InternerInner {
-    map: FxHashMap<&'static [u8], NonNull<c_void>>,
+    #[allow(clippy::disallowed_types)] // Not using the default hasher
+    map: std::collections::HashMap<u64, NonNull<c_void>, BuildNoHashHasher<u64>>,
     #[cfg(feature = "no_leak")]
+    #[allow(clippy::disallowed_types)] // Not using the default hasher
     interned_strings: FxHashSet<NonNull<c_void>>,
 }
 
 unsafe impl Send for InternerInner {}
 
+fn hash<T>(s: T) -> u64
+where
+    T: Hash,
+{
+    let mut hasher = FxHasher::default();
+    s.hash(&mut hasher);
+    hasher.finish()
+}
+
 impl InternerInner {
     pub fn intern(&mut self, s: &[u8]) -> NixString {
-        if let Some(s) = self.map.get(s) {
+        let hash = hash(s);
+        if let Some(s) = self.map.get(&hash) {
             return NixString(*s);
         }
 
         let string = NixString::new_inner(s, None);
-        self.map
-            .insert(unsafe { mem::transmute(string.as_bytes()) }, string.0);
+        self.map.insert(hash, string.0);
         #[cfg(feature = "no_leak")]
         self.interned_strings.insert(string.0);
         string
diff --git a/tvix/eval/src/value/thunk.rs b/tvix/eval/src/value/thunk.rs
index 2019e8ab3239..4b915019d47c 100644
--- a/tvix/eval/src/value/thunk.rs
+++ b/tvix/eval/src/value/thunk.rs
@@ -27,7 +27,7 @@ use std::{
 
 use crate::{
     errors::ErrorKind,
-    opcode::OpCode,
+    opcode::Op,
     upvalues::Upvalues,
     value::Closure,
     vm::generators::{self, GenCo},
@@ -170,13 +170,15 @@ impl Thunk {
         // This is basically a recreation of compile_apply():
         // We need to push the argument onto the stack and then the function.
         // The function (not the argument) needs to be forced before calling.
-        lambda.chunk.push_op(OpCode::OpConstant(arg_idx), span);
-        lambda.chunk().push_op(OpCode::OpConstant(f_idx), span);
-        lambda.chunk.push_op(OpCode::OpForce, span);
-        lambda.chunk.push_op(OpCode::OpCall, span);
+        lambda.chunk.push_op(Op::Constant, span);
+        lambda.chunk.push_uvarint(arg_idx.0 as u64);
+        lambda.chunk.push_op(Op::Constant, span);
+        lambda.chunk.push_uvarint(f_idx.0 as u64);
+        lambda.chunk.push_op(Op::Force, span);
+        lambda.chunk.push_op(Op::Call, span);
 
         // Inform the VM that the chunk has ended
-        lambda.chunk.push_op(OpCode::OpReturn, span);
+        lambda.chunk.push_op(Op::Return, span);
 
         Thunk(Rc::new(RefCell::new(ThunkRepr::Suspended {
             upvalues: Rc::new(Upvalues::with_capacity(0)),
diff --git a/tvix/eval/src/vm/mod.rs b/tvix/eval/src/vm/mod.rs
index a6d0941e8d7a..49e9fc5864be 100644
--- a/tvix/eval/src/vm/mod.rs
+++ b/tvix/eval/src/vm/mod.rs
@@ -28,7 +28,7 @@ use crate::{
     lifted_pop,
     nix_search_path::NixSearchPath,
     observer::RuntimeObserver,
-    opcode::{CodeIdx, Count, JumpOffset, OpCode, StackIdx, UpvalueIdx},
+    opcode::{CodeIdx, Op, Position, UpvalueIdx},
     upvalues::Upvalues,
     value::{
         Builtin, BuiltinResult, Closure, CoercionKind, Lambda, NixAttrs, NixContext, NixList,
@@ -146,10 +146,32 @@ impl CallFrame {
 
     /// Increment this frame's instruction pointer and return the operation that
     /// the pointer moved past.
-    fn inc_ip(&mut self) -> OpCode {
-        let op = self.chunk()[self.ip];
+    fn inc_ip(&mut self) -> Op {
+        debug_assert!(
+            self.ip.0 < self.chunk().code.len(),
+            "out of bounds code at IP {} in {:p}",
+            self.ip.0,
+            self.lambda
+        );
+
+        let op = self.chunk().code[self.ip.0];
         self.ip += 1;
-        op
+        op.into()
+    }
+
+    /// Read a varint-encoded operand and return it. The frame pointer is
+    /// incremented internally.
+    fn read_uvarint(&mut self) -> u64 {
+        let (arg, size) = self.chunk().read_uvarint(self.ip.0);
+        self.ip += size;
+        arg
+    }
+
+    /// Read a fixed-size u16 and increment the frame pointer.
+    fn read_u16(&mut self) -> u16 {
+        let arg = self.chunk().read_u16(self.ip.0);
+        self.ip += 2;
+        arg
     }
 
     /// Construct an error result from the given ErrorKind and the source span
@@ -427,8 +449,8 @@ where
     ///    stack. In this case, the frame is not returned to the frame stack.
     ///
     /// 2. The code encounters a generator, in which case the frame in its
-    /// current state is pushed back on the stack, and the generator is left on
-    /// top of it for the outer loop to execute.
+    ///    current state is pushed back on the stack, and the generator is left
+    ///    on top of it for the outer loop to execute.
     ///
     /// 3. An error is encountered.
     ///
@@ -443,17 +465,25 @@ where
             self.observer.observe_execute_op(frame.ip, &op, &self.stack);
 
             match op {
-                OpCode::OpThunkSuspended(idx) | OpCode::OpThunkClosure(idx) => {
-                    let blueprint = match &frame.chunk()[idx] {
+                Op::ThunkSuspended | Op::ThunkClosure => {
+                    let idx = frame.read_uvarint() as usize;
+
+                    let blueprint = match &frame.chunk().constants[idx] {
                         Value::Blueprint(lambda) => lambda.clone(),
                         _ => panic!("compiler bug: non-blueprint in blueprint slot"),
                     };
 
-                    let upvalue_count = blueprint.upvalue_count;
-                    let thunk = if matches!(op, OpCode::OpThunkClosure(_)) {
+                    let upvalue_count = frame.read_uvarint();
+
+                    debug_assert!(
+                        (upvalue_count >> 1) == blueprint.upvalue_count as u64,
+                        "TODO: new upvalue count not correct",
+                    );
+
+                    let thunk = if op == Op::ThunkClosure {
                         debug_assert!(
-                            upvalue_count > 0,
-                            "OpThunkClosure should not be called for plain lambdas"
+                            (((upvalue_count >> 1) > 0) || (upvalue_count & 0b1 == 1)),
+                            "OpThunkClosure should not be called for plain lambdas",
                         );
                         Thunk::new_closure(blueprint)
                     } else {
@@ -470,7 +500,7 @@ where
                     self.populate_upvalues(&mut frame, upvalue_count, upvalues)?;
                 }
 
-                OpCode::OpForce => {
+                Op::Force => {
                     if let Some(Value::Thunk(_)) = self.stack.last() {
                         let thunk = match self.stack_pop() {
                             Value::Thunk(t) => t,
@@ -488,25 +518,35 @@ where
                     }
                 }
 
-                OpCode::OpGetUpvalue(upv_idx) => {
-                    let value = frame.upvalue(upv_idx).clone();
+                Op::GetUpvalue => {
+                    let idx = UpvalueIdx(frame.read_uvarint() as usize);
+                    let value = frame.upvalue(idx).clone();
                     self.stack.push(value);
                 }
 
                 // Discard the current frame.
-                OpCode::OpReturn => {
+                Op::Return => {
                     // TODO(amjoseph): I think this should assert `==` rather
                     // than `<=` but it fails with the stricter condition.
                     debug_assert!(self.stack.len() - 1 <= frame.stack_offset);
                     return Ok(true);
                 }
 
-                OpCode::OpConstant(idx) => {
-                    let c = frame.chunk()[idx].clone();
+                Op::Constant => {
+                    let idx = frame.read_uvarint() as usize;
+
+                    debug_assert!(
+                        idx < frame.chunk().constants.len(),
+                        "out of bounds constant at IP {} in {:p}",
+                        frame.ip.0,
+                        frame.lambda
+                    );
+
+                    let c = frame.chunk().constants[idx].clone();
                     self.stack.push(c);
                 }
 
-                OpCode::OpCall => {
+                Op::Call => {
                     let callable = self.stack_pop();
                     self.call_value(frame.current_span(), Some((span, frame)), callable)?;
 
@@ -516,7 +556,8 @@ where
 
                 // Remove the given number of elements from the stack,
                 // but retain the top value.
-                OpCode::OpCloseScope(Count(count)) => {
+                Op::CloseScope => {
+                    let count = frame.read_uvarint() as usize;
                     // Immediately move the top value into the right
                     // position.
                     let target_idx = self.stack.len() - 1 - count;
@@ -528,15 +569,22 @@ where
                     }
                 }
 
-                OpCode::OpClosure(idx) => {
-                    let blueprint = match &frame.chunk()[idx] {
+                Op::Closure => {
+                    let idx = frame.read_uvarint() as usize;
+                    let blueprint = match &frame.chunk().constants[idx] {
                         Value::Blueprint(lambda) => lambda.clone(),
                         _ => panic!("compiler bug: non-blueprint in blueprint slot"),
                     };
 
-                    let upvalue_count = blueprint.upvalue_count;
+                    let upvalue_count = frame.read_uvarint();
+
+                    debug_assert!(
+                        (upvalue_count >> 1) == blueprint.upvalue_count as u64,
+                        "TODO: new upvalue count not correct in closure",
+                    );
+
                     debug_assert!(
-                        upvalue_count > 0,
+                        ((upvalue_count >> 1) > 0 || (upvalue_count & 0b1 == 1)),
                         "OpClosure should not be called for plain lambdas"
                     );
 
@@ -549,7 +597,7 @@ where
                         ))));
                 }
 
-                OpCode::OpAttrsSelect => lifted_pop! {
+                Op::AttrsSelect => lifted_pop! {
                     self(key, attrs) => {
                         let key = key.to_str().with_span(&frame, self)?;
                         let attrs = attrs.to_attrs().with_span(&frame, self)?;
@@ -569,21 +617,24 @@ where
                     }
                 },
 
-                OpCode::OpJumpIfFalse(JumpOffset(offset)) => {
+                Op::JumpIfFalse => {
+                    let offset = frame.read_u16() as usize;
                     debug_assert!(offset != 0);
                     if !self.stack_peek(0).as_bool().with_span(&frame, self)? {
                         frame.ip += offset;
                     }
                 }
 
-                OpCode::OpJumpIfCatchable(JumpOffset(offset)) => {
+                Op::JumpIfCatchable => {
+                    let offset = frame.read_u16() as usize;
                     debug_assert!(offset != 0);
                     if self.stack_peek(0).is_catchable() {
                         frame.ip += offset;
                     }
                 }
 
-                OpCode::OpJumpIfNoFinaliseRequest(JumpOffset(offset)) => {
+                Op::JumpIfNoFinaliseRequest => {
+                    let offset = frame.read_u16() as usize;
                     debug_assert!(offset != 0);
                     match self.stack_peek(0) {
                         Value::FinaliseRequest(finalise) => {
@@ -595,11 +646,11 @@ where
                     }
                 }
 
-                OpCode::OpPop => {
+                Op::Pop => {
                     self.stack.pop();
                 }
 
-                OpCode::OpAttrsTrySelect => {
+                Op::AttrsTrySelect => {
                     let key = self.stack_pop().to_str().with_span(&frame, self)?;
                     let value = match self.stack_pop() {
                         Value::Attrs(attrs) => match attrs.select(&key) {
@@ -613,12 +664,14 @@ where
                     self.stack.push(value);
                 }
 
-                OpCode::OpGetLocal(StackIdx(local_idx)) => {
+                Op::GetLocal => {
+                    let local_idx = frame.read_uvarint() as usize;
                     let idx = frame.stack_offset + local_idx;
                     self.stack.push(self.stack[idx].clone());
                 }
 
-                OpCode::OpJumpIfNotFound(JumpOffset(offset)) => {
+                Op::JumpIfNotFound => {
+                    let offset = frame.read_u16() as usize;
                     debug_assert!(offset != 0);
                     if matches!(self.stack_peek(0), Value::AttrNotFound) {
                         self.stack_pop();
@@ -626,12 +679,13 @@ where
                     }
                 }
 
-                OpCode::OpJump(JumpOffset(offset)) => {
+                Op::Jump => {
+                    let offset = frame.read_u16() as usize;
                     debug_assert!(offset != 0);
                     frame.ip += offset;
                 }
 
-                OpCode::OpEqual => lifted_pop! {
+                Op::Equal => lifted_pop! {
                     self(b, a) => {
                         let gen_span = frame.current_span();
                         self.push_call_frame(span, frame);
@@ -646,7 +700,7 @@ where
                 // top is not of the expected type. This is necessary
                 // to implement some specific behaviours of Nix
                 // exactly.
-                OpCode::OpAssertBool => {
+                Op::AssertBool => {
                     let val = self.stack_peek(0);
                     // TODO(edef): propagate this into is_bool, since bottom values *are* values of any type
                     if !val.is_catchable() && !val.is_bool() {
@@ -660,7 +714,7 @@ where
                     }
                 }
 
-                OpCode::OpAssertAttrs => {
+                Op::AssertAttrs => {
                     let val = self.stack_peek(0);
                     // TODO(edef): propagate this into is_attrs, since bottom values *are* values of any type
                     if !val.is_catchable() && !val.is_attrs() {
@@ -674,9 +728,9 @@ where
                     }
                 }
 
-                OpCode::OpAttrs(Count(count)) => self.run_attrset(&frame, count)?,
+                Op::Attrs => self.run_attrset(frame.read_uvarint() as usize, &frame)?,
 
-                OpCode::OpAttrsUpdate => lifted_pop! {
+                Op::AttrsUpdate => lifted_pop! {
                     self(rhs, lhs) => {
                         let rhs = rhs.to_attrs().with_span(&frame, self)?;
                         let lhs = lhs.to_attrs().with_span(&frame, self)?;
@@ -684,28 +738,30 @@ where
                     }
                 },
 
-                OpCode::OpInvert => lifted_pop! {
+                Op::Invert => lifted_pop! {
                     self(v) => {
                         let v = v.as_bool().with_span(&frame, self)?;
                         self.stack.push(Value::Bool(!v));
                     }
                 },
 
-                OpCode::OpList(Count(count)) => {
+                Op::List => {
+                    let count = frame.read_uvarint() as usize;
                     let list =
                         NixList::construct(count, self.stack.split_off(self.stack.len() - count));
 
                     self.stack.push(Value::List(list));
                 }
 
-                OpCode::OpJumpIfTrue(JumpOffset(offset)) => {
+                Op::JumpIfTrue => {
+                    let offset = frame.read_u16() as usize;
                     debug_assert!(offset != 0);
                     if self.stack_peek(0).as_bool().with_span(&frame, self)? {
                         frame.ip += offset;
                     }
                 }
 
-                OpCode::OpHasAttr => lifted_pop! {
+                Op::HasAttr => lifted_pop! {
                     self(key, attrs) => {
                         let key = key.to_str().with_span(&frame, self)?;
                         let result = match attrs {
@@ -720,15 +776,16 @@ where
                     }
                 },
 
-                OpCode::OpConcat => lifted_pop! {
+                Op::Concat => lifted_pop! {
                     self(rhs, lhs) => {
                         let rhs = rhs.to_list().with_span(&frame, self)?.into_inner();
-                        let lhs = lhs.to_list().with_span(&frame, self)?.into_inner();
-                        self.stack.push(Value::List(NixList::from(lhs + rhs)))
+                        let mut lhs = lhs.to_list().with_span(&frame, self)?.into_inner();
+                        lhs.extend(rhs.into_iter());
+                        self.stack.push(Value::List(lhs.into()))
                     }
                 },
 
-                OpCode::OpResolveWith => {
+                Op::ResolveWith => {
                     let ident = self.stack_pop().to_str().with_span(&frame, self)?;
 
                     // Re-enqueue this frame.
@@ -755,13 +812,19 @@ where
                     return Ok(false);
                 }
 
-                OpCode::OpFinalise(StackIdx(idx)) => match &self.stack[frame.stack_offset + idx] {
-                    Value::Closure(_) => panic!("attempted to finalise a closure"),
-                    Value::Thunk(thunk) => thunk.finalise(&self.stack[frame.stack_offset..]),
-                    _ => panic!("attempted to finalise a non-thunk"),
-                },
+                Op::Finalise => {
+                    let idx = frame.read_uvarint() as usize;
+                    match &self.stack[frame.stack_offset + idx] {
+                        Value::Closure(_) => panic!("attempted to finalise a closure"),
+                        Value::Thunk(thunk) => thunk.finalise(&self.stack[frame.stack_offset..]),
+                        _ => panic!("attempted to finalise a non-thunk"),
+                    }
+                }
+
+                Op::CoerceToString => {
+                    let kind: CoercionKind = frame.chunk().code[frame.ip.0].into();
+                    frame.ip.0 += 1;
 
-                OpCode::OpCoerceToString(kind) => {
                     let value = self.stack_pop();
                     let gen_span = frame.current_span();
                     self.push_call_frame(span, frame);
@@ -773,9 +836,9 @@ where
                     return Ok(false);
                 }
 
-                OpCode::OpInterpolate(Count(count)) => self.run_interpolate(&frame, count)?,
+                Op::Interpolate => self.run_interpolate(frame.read_uvarint(), &frame)?,
 
-                OpCode::OpValidateClosedFormals => {
+                Op::ValidateClosedFormals => {
                     let formals = frame.lambda.formals.as_ref().expect(
                         "OpValidateClosedFormals called within the frame of a lambda without formals",
                     );
@@ -799,7 +862,7 @@ where
                     }
                 }
 
-                OpCode::OpAdd => lifted_pop! {
+                Op::Add => lifted_pop! {
                     self(b, a) => {
                         let gen_span = frame.current_span();
                         self.push_call_frame(span, frame);
@@ -812,21 +875,21 @@ where
                     }
                 },
 
-                OpCode::OpSub => lifted_pop! {
+                Op::Sub => lifted_pop! {
                     self(b, a) => {
                         let result = arithmetic_op!(&a, &b, -).with_span(&frame, self)?;
                         self.stack.push(result);
                     }
                 },
 
-                OpCode::OpMul => lifted_pop! {
+                Op::Mul => lifted_pop! {
                     self(b, a) => {
                         let result = arithmetic_op!(&a, &b, *).with_span(&frame, self)?;
                         self.stack.push(result);
                     }
                 },
 
-                OpCode::OpDiv => lifted_pop! {
+                Op::Div => lifted_pop! {
                     self(b, a) => {
                         match b {
                             Value::Integer(0) => return frame.error(self, ErrorKind::DivisionByZero),
@@ -841,7 +904,7 @@ where
                     }
                 },
 
-                OpCode::OpNegate => match self.stack_pop() {
+                Op::Negate => match self.stack_pop() {
                     Value::Integer(i) => self.stack.push(Value::Integer(-i)),
                     Value::Float(f) => self.stack.push(Value::Float(-f)),
                     Value::Catchable(cex) => self.stack.push(Value::Catchable(cex)),
@@ -856,12 +919,12 @@ where
                     }
                 },
 
-                OpCode::OpLess => cmp_op!(self, frame, span, <),
-                OpCode::OpLessOrEq => cmp_op!(self, frame, span, <=),
-                OpCode::OpMore => cmp_op!(self, frame, span, >),
-                OpCode::OpMoreOrEq => cmp_op!(self, frame, span, >=),
+                Op::Less => cmp_op!(self, frame, span, <),
+                Op::LessOrEq => cmp_op!(self, frame, span, <=),
+                Op::More => cmp_op!(self, frame, span, >),
+                Op::MoreOrEq => cmp_op!(self, frame, span, >=),
 
-                OpCode::OpFindFile => match self.stack_pop() {
+                Op::FindFile => match self.stack_pop() {
                     Value::UnresolvedPath(path) => {
                         let resolved = self
                             .nix_search_path
@@ -873,7 +936,7 @@ where
                     _ => panic!("tvix compiler bug: OpFindFile called on non-UnresolvedPath"),
                 },
 
-                OpCode::OpResolveHomePath => match self.stack_pop() {
+                Op::ResolveHomePath => match self.stack_pop() {
                     Value::UnresolvedPath(path) => {
                         match dirs::home_dir() {
                             None => {
@@ -896,24 +959,23 @@ where
                     }
                 },
 
-                OpCode::OpPushWith(StackIdx(idx)) => self.with_stack.push(frame.stack_offset + idx),
+                Op::PushWith => self
+                    .with_stack
+                    .push(frame.stack_offset + frame.read_uvarint() as usize),
 
-                OpCode::OpPopWith => {
+                Op::PopWith => {
                     self.with_stack.pop();
                 }
 
-                OpCode::OpAssertFail => {
+                Op::AssertFail => {
                     self.stack
                         .push(Value::from(CatchableErrorKind::AssertionFailed));
                 }
 
-                // Data-carrying operands should never be executed,
-                // that is a critical error in the VM/compiler.
-                OpCode::DataStackIdx(_)
-                | OpCode::DataDeferredLocal(_)
-                | OpCode::DataUpvalueIdx(_)
-                | OpCode::DataCaptureWith => {
-                    panic!("Tvix bug: attempted to execute data-carrying operand")
+                // Encountering an invalid opcode is a critical error in the
+                // VM/compiler.
+                Op::Invalid => {
+                    panic!("Tvix bug: attempted to execute invalid opcode")
                 }
             }
         }
@@ -933,7 +995,7 @@ where
         &self.stack[self.stack.len() - 1 - offset]
     }
 
-    fn run_attrset(&mut self, frame: &CallFrame, count: usize) -> EvalResult<()> {
+    fn run_attrset(&mut self, count: usize, frame: &CallFrame) -> EvalResult<()> {
         let attrs = NixAttrs::construct(count, self.stack.split_off(self.stack.len() - count * 2))
             .with_span(frame, self)?
             .map(Value::attrs)
@@ -971,7 +1033,7 @@ where
     /// Interpolate string fragments by popping the specified number of
     /// fragments of the stack, evaluating them to strings, and pushing
     /// the concatenated result string back on the stack.
-    fn run_interpolate(&mut self, frame: &CallFrame, count: usize) -> EvalResult<()> {
+    fn run_interpolate(&mut self, count: u64, frame: &CallFrame) -> EvalResult<()> {
         let mut out = BString::default();
         // Interpolation propagates the context and union them.
         let mut context: NixContext = NixContext::new();
@@ -1090,64 +1152,74 @@ where
     }
 
     /// Populate the upvalue fields of a thunk or closure under construction.
+    ///
+    /// See the closely tied function `emit_upvalue_data` in the compiler
+    /// implementation for details on the argument processing.
     fn populate_upvalues(
         &mut self,
         frame: &mut CallFrame,
-        count: usize,
+        count: u64,
         mut upvalues: impl DerefMut<Target = Upvalues>,
     ) -> EvalResult<()> {
-        for _ in 0..count {
-            match frame.inc_ip() {
-                OpCode::DataStackIdx(StackIdx(stack_idx)) => {
-                    let idx = frame.stack_offset + stack_idx;
-
-                    let val = match self.stack.get(idx) {
-                        Some(val) => val.clone(),
-                        None => {
-                            return frame.error(
-                                self,
-                                ErrorKind::TvixBug {
-                                    msg: "upvalue to be captured was missing on stack",
-                                    metadata: Some(Rc::new(json!({
-                                        "ip": format!("{:#x}", frame.ip.0 - 1),
-                                        "stack_idx(relative)": stack_idx,
-                                        "stack_idx(absolute)": idx,
-                                    }))),
-                                },
-                            );
-                        }
-                    };
+        // Determine whether to capture the with stack, and then shift the
+        // actual count of upvalues back.
+        let capture_with = count & 0b1 == 1;
+        let count = count >> 1;
+        if capture_with {
+            // Start the captured with_stack off of the
+            // current call frame's captured with_stack, ...
+            let mut captured_with_stack = frame
+                .upvalues
+                .with_stack()
+                .cloned()
+                // ... or make an empty one if there isn't one already.
+                .unwrap_or_else(|| Vec::with_capacity(self.with_stack.len()));
+
+            for idx in &self.with_stack {
+                captured_with_stack.push(self.stack[*idx].clone());
+            }
 
-                    upvalues.deref_mut().push(val);
-                }
+            upvalues.deref_mut().set_with_stack(captured_with_stack);
+        }
 
-                OpCode::DataUpvalueIdx(upv_idx) => {
-                    upvalues.deref_mut().push(frame.upvalue(upv_idx).clone());
-                }
+        for _ in 0..count {
+            let pos = Position(frame.read_uvarint());
 
-                OpCode::DataDeferredLocal(idx) => {
-                    upvalues.deref_mut().push(Value::DeferredUpvalue(idx));
-                }
+            if let Some(stack_idx) = pos.runtime_stack_index() {
+                let idx = frame.stack_offset + stack_idx.0;
 
-                OpCode::DataCaptureWith => {
-                    // Start the captured with_stack off of the
-                    // current call frame's captured with_stack, ...
-                    let mut captured_with_stack = frame
-                        .upvalues
-                        .with_stack()
-                        .cloned()
-                        // ... or make an empty one if there isn't one already.
-                        .unwrap_or_else(|| Vec::with_capacity(self.with_stack.len()));
-
-                    for idx in &self.with_stack {
-                        captured_with_stack.push(self.stack[*idx].clone());
+                let val = match self.stack.get(idx) {
+                    Some(val) => val.clone(),
+                    None => {
+                        return frame.error(
+                            self,
+                            ErrorKind::TvixBug {
+                                msg: "upvalue to be captured was missing on stack",
+                                metadata: Some(Rc::new(json!({
+                                    "ip": format!("{:#x}", frame.ip.0 - 1),
+                                    "stack_idx(relative)": stack_idx.0,
+                                    "stack_idx(absolute)": idx,
+                                }))),
+                            },
+                        );
                     }
+                };
 
-                    upvalues.deref_mut().set_with_stack(captured_with_stack);
-                }
+                upvalues.deref_mut().push(val);
+                continue;
+            }
 
-                _ => panic!("compiler error: missing closure operand"),
+            if let Some(idx) = pos.runtime_deferred_local() {
+                upvalues.deref_mut().push(Value::DeferredUpvalue(idx));
+                continue;
             }
+
+            if let Some(idx) = pos.runtime_upvalue_index() {
+                upvalues.deref_mut().push(frame.upvalue(idx).clone());
+                continue;
+            }
+
+            panic!("Tvix bug: invalid capture position emitted")
         }
 
         Ok(())
diff --git a/tvix/glue/Cargo.toml b/tvix/glue/Cargo.toml
index 6e9f313aa0a9..bb522dceb902 100644
--- a/tvix/glue/Cargo.toml
+++ b/tvix/glue/Cargo.toml
@@ -4,47 +4,46 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-async-compression = { version = "0.4.9", features = ["tokio", "gzip", "bzip2", "xz"]}
-bstr = "1.6.0"
-bytes = "1.4.0"
-data-encoding = "2.6.0"
-futures = "0.3.30"
-magic = "0.16.2"
+async-compression = { workspace = true, features = ["tokio", "gzip", "bzip2", "xz"] }
+bstr = { workspace = true }
+bytes = { workspace = true }
+data-encoding = { workspace = true }
+futures = { workspace = true }
+magic = { workspace = true }
 nix-compat = { path = "../nix-compat" }
-pin-project = "1.1"
-reqwest = { version = "0.12.0", features = ["rustls-tls-native-roots"], default-features = false }
+pin-project = { workspace = true }
+reqwest = { workspace = true, features = ["rustls-tls-native-roots"] }
 tvix-build = { path = "../build", default-features = false, features = []}
 tvix-eval = { path = "../eval" }
 tvix-castore = { path = "../castore" }
 tvix-store = { path = "../store", default-features = false, features = []}
 tvix-tracing = { path = "../tracing" }
-tracing = "0.1.37"
-tracing-indicatif = "0.3.6"
-tokio = "1.28.0"
-tokio-tar = "0.3.1"
-tokio-util = { version = "0.7.9", features = ["io", "io-util", "compat"] }
-thiserror = "1.0.38"
-serde = "1.0.195"
-serde_json = "1.0"
-sha2 = "0.10.8"
-sha1 = "0.10.6"
-md-5 = "0.10.6"
-url = "2.4.0"
-walkdir = "2.4.0"
-clap = { version = "4.4.0", default-features = false }
-
-[dependencies.wu-manber]
-git = "https://github.com/tvlfyi/wu-manber.git"
+tracing = { workspace = true }
+tracing-indicatif = { workspace = true }
+tokio = { workspace = true }
+tokio-tar = { workspace = true }
+tokio-util = { workspace = true, features = ["io", "io-util", "compat"] }
+thiserror = { workspace = true }
+serde = { workspace = true }
+serde_json = { workspace = true }
+sha2 = { workspace = true }
+sha1 = { workspace = true }
+md-5 = { workspace = true }
+url = { workspace = true }
+walkdir = { workspace = true }
+clap = { workspace = true }
+wu-manber = { workspace = true }
 
 [dev-dependencies]
-criterion = { version = "0.5", features = ["html_reports"] }
-hex-literal = "0.4.1"
-lazy_static = "1.4.0"
-mimalloc = "0.1.43"
-nix = { version = "0.27.1", features = [ "fs" ] }
-pretty_assertions = "1.4.0"
-rstest = "0.19.0"
-tempfile = "3.8.1"
+criterion = { workspace = true, features = ["html_reports"] }
+hex-literal = { workspace = true }
+lazy_static = { workspace = true }
+mimalloc = { workspace = true }
+nix = { workspace = true, features = ["fs"] }
+pretty_assertions = { workspace = true }
+rstest = { workspace = true }
+tempfile = { workspace = true }
+tokio-test = { workspace = true }
 
 [features]
 default = ["nix_tests"]
diff --git a/tvix/glue/benches/eval.rs b/tvix/glue/benches/eval.rs
index 0a27d9aa26fe..a0823d2e129b 100644
--- a/tvix/glue/benches/eval.rs
+++ b/tvix/glue/benches/eval.rs
@@ -60,7 +60,7 @@ fn interpret(code: &str) {
     let eval = eval_builder.build();
     let result = eval.evaluate(code, None);
 
-    assert!(result.errors.is_empty());
+    assert!(result.errors.is_empty(), "{:#?}", result.errors);
 }
 
 fn eval_nixpkgs(c: &mut Criterion) {
diff --git a/tvix/glue/default.nix b/tvix/glue/default.nix
index e2b36e94b87f..0ead94a504c3 100644
--- a/tvix/glue/default.nix
+++ b/tvix/glue/default.nix
@@ -7,11 +7,11 @@
   '';
 }).overrideAttrs (old: rec {
   meta.ci.targets = lib.filter (x: lib.hasPrefix "with-features" x || x == "no-features") (lib.attrNames passthru);
-  passthru = depot.tvix.utils.mkFeaturePowerset {
+  passthru = old.passthru // (depot.tvix.utils.mkFeaturePowerset {
     inherit (old) crateName;
     features = [ "nix_tests" ];
     override.testPreRun = ''
       export SSL_CERT_FILE=/dev/null
     '';
-  };
+  });
 })
diff --git a/tvix/glue/src/builtins/derivation.rs b/tvix/glue/src/builtins/derivation.rs
index b17b90466965..5bc9dab71283 100644
--- a/tvix/glue/src/builtins/derivation.rs
+++ b/tvix/glue/src/builtins/derivation.rs
@@ -179,9 +179,7 @@ pub(crate) mod derivation_builtins {
     use nix_compat::nixhash::CAHash;
     use nix_compat::store_path::{build_ca_path, hash_placeholder};
     use sha2::Sha256;
-    use tvix_castore::proto as castorepb;
-    use tvix_castore::proto::node::Node;
-    use tvix_castore::proto::FileNode;
+    use tvix_castore::Node;
     use tvix_eval::generators::Gen;
     use tvix_eval::{NixContext, NixContextElement, NixString};
     use tvix_store::proto::{NarInfo, PathInfo};
@@ -570,7 +568,7 @@ pub(crate) mod derivation_builtins {
             let blob_digest = blob_writer.close().await?;
             let ca_hash = CAHash::Text(Sha256::digest(&content).into());
 
-            let store_path =
+            let store_path: StorePathRef =
                 build_ca_path(name.to_str()?, &ca_hash, content.iter_ctx_plain(), false)
                     .map_err(|_e| {
                         nix_compat::derivation::DerivationError::InvalidOutputName(
@@ -579,12 +577,11 @@ pub(crate) mod derivation_builtins {
                     })
                     .map_err(DerivationError::InvalidDerivation)?;
 
-            let root_node = Node::File(FileNode {
-                name: store_path.to_string().into(),
-                digest: blob_digest.into(),
+            let root_node = Node::File {
+                digest: blob_digest,
                 size: blob_size,
                 executable: false,
-            });
+            };
 
             // calculate the nar hash
             let (nar_size, nar_sha256) = state
@@ -604,9 +601,10 @@ pub(crate) mod derivation_builtins {
             state
                 .path_info_service
                 .put(PathInfo {
-                    node: Some(castorepb::Node {
-                        node: Some(root_node),
-                    }),
+                    node: Some(tvix_castore::proto::Node::from_name_and_node(
+                        store_path.to_string().into(),
+                        root_node,
+                    )),
                     references: reference_paths
                         .iter()
                         .map(|x| bytes::Bytes::copy_from_slice(x.digest()))
diff --git a/tvix/glue/src/builtins/import.rs b/tvix/glue/src/builtins/import.rs
index 273be08ef7b6..34ae2778ecdd 100644
--- a/tvix/glue/src/builtins/import.rs
+++ b/tvix/glue/src/builtins/import.rs
@@ -3,6 +3,7 @@
 use crate::builtins::errors::ImportError;
 use std::path::Path;
 use tvix_castore::import::ingest_entries;
+use tvix_castore::Node;
 use tvix_eval::{
     builtin_macros::builtins,
     generators::{self, GenCo},
@@ -16,7 +17,7 @@ async fn filtered_ingest(
     co: GenCo,
     path: &Path,
     filter: Option<&Value>,
-) -> Result<tvix_castore::proto::node::Node, ErrorKind> {
+) -> Result<Node, ErrorKind> {
     let mut entries: Vec<walkdir::DirEntry> = vec![];
     let mut it = walkdir::WalkDir::new(path)
         .follow_links(false)
@@ -111,11 +112,9 @@ mod import_builtins {
 
     use crate::tvix_store_io::TvixStoreIO;
     use nix_compat::nixhash::{CAHash, NixHash};
-    use nix_compat::store_path::StorePath;
+    use nix_compat::store_path::StorePathRef;
     use sha2::Digest;
     use tokio::io::AsyncWriteExt;
-    use tvix_castore::proto::node::Node;
-    use tvix_castore::proto::FileNode;
     use tvix_eval::builtins::coerce_value_to_path;
     use tvix_eval::generators::Gen;
     use tvix_eval::{generators::GenCo, ErrorKind, Value};
@@ -214,13 +213,11 @@ mod import_builtins {
                     .tokio_handle
                     .block_on(async { blob_writer.close().await })?;
 
-                let root_node = Node::File(FileNode {
-                    // The name gets set further down, while constructing the PathInfo.
-                    name: "".into(),
-                    digest: blob_digest.into(),
+                let root_node = Node::File {
+                    digest: blob_digest,
                     size: blob_size,
                     executable: false,
-                });
+                };
 
                 let ca_hash = if recursive_ingestion {
                     let (_nar_size, nar_sha256) = state
@@ -380,16 +377,16 @@ mod import_builtins {
             }
         })?;
 
-        let path_exists = if let Ok((store_path, sub_path)) = StorePath::from_absolute_path_full(p)
-        {
-            if !sub_path.as_os_str().is_empty() {
-                false
+        let path_exists =
+            if let Ok((store_path, sub_path)) = StorePathRef::from_absolute_path_full(p) {
+                if !sub_path.as_os_str().is_empty() {
+                    false
+                } else {
+                    state.store_path_exists(store_path.as_ref()).await?
+                }
             } else {
-                state.store_path_exists(store_path.as_ref()).await?
-            }
-        } else {
-            false
-        };
+                false
+            };
 
         if !path_exists {
             return Err(ImportError::PathNotInStore(p.into()).into());
diff --git a/tvix/glue/src/fetchers/mod.rs b/tvix/glue/src/fetchers/mod.rs
index eb035a5a905c..065d011361a7 100644
--- a/tvix/glue/src/fetchers/mod.rs
+++ b/tvix/glue/src/fetchers/mod.rs
@@ -10,11 +10,7 @@ use tokio::io::{AsyncBufRead, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader};
 use tokio_util::io::{InspectReader, InspectWriter};
 use tracing::{instrument, warn, Span};
 use tracing_indicatif::span_ext::IndicatifSpanExt;
-use tvix_castore::{
-    blobservice::BlobService,
-    directoryservice::DirectoryService,
-    proto::{node::Node, FileNode},
-};
+use tvix_castore::{blobservice::BlobService, directoryservice::DirectoryService, Node};
 use tvix_store::{nar::NarCalculationService, pathinfoservice::PathInfoService, proto::PathInfo};
 use url::Url;
 
@@ -331,12 +327,11 @@ where
 
                 // Construct and return the FileNode describing the downloaded contents.
                 Ok((
-                    Node::File(FileNode {
-                        name: vec![].into(),
-                        digest: blob_writer.close().await?.into(),
+                    Node::File {
+                        digest: blob_writer.close().await?,
                         size: blob_size,
                         executable: false,
-                    }),
+                    },
                     CAHash::Flat(actual_hash),
                     blob_size,
                 ))
@@ -531,12 +526,11 @@ where
 
                 // Construct and return the FileNode describing the downloaded contents,
                 // make it executable.
-                let root_node = Node::File(FileNode {
-                    name: vec![].into(),
-                    digest: blob_digest.into(),
+                let root_node = Node::File {
+                    digest: blob_digest,
                     size: file_size,
                     executable: true,
-                });
+                };
 
                 Ok((root_node, CAHash::Nar(actual_hash), file_size))
             }
@@ -560,9 +554,6 @@ where
         // Calculate the store path to return, by calculating from ca_hash.
         let store_path = build_ca_path(name, &ca_hash, Vec::<String>::new(), false)?;
 
-        // Rename the node name to match the Store Path.
-        let node = node.rename(store_path.to_string().into());
-
         // If the resulting hash is not a CAHash::Nar, we also need to invoke
         // `calculate_nar` to calculate this representation, as it's required in
         // the [PathInfo].
@@ -580,7 +571,10 @@ where
 
         // Construct the PathInfo and persist it.
         let path_info = PathInfo {
-            node: Some(tvix_castore::proto::Node { node: Some(node) }),
+            node: Some(tvix_castore::proto::Node::from_name_and_node(
+                store_path.to_string().into(),
+                node.clone(),
+            )),
             references: vec![],
             narinfo: Some(tvix_store::proto::NarInfo {
                 nar_size,
@@ -592,13 +586,12 @@ where
             }),
         };
 
-        let path_info = self
-            .path_info_service
+        self.path_info_service
             .put(path_info)
             .await
             .map_err(|e| FetcherError::Io(e.into()))?;
 
-        Ok((store_path, path_info.node.unwrap().node.unwrap()))
+        Ok((store_path, node))
     }
 }
 
diff --git a/tvix/glue/src/known_paths.rs b/tvix/glue/src/known_paths.rs
index edc57c38f247..239acca9829a 100644
--- a/tvix/glue/src/known_paths.rs
+++ b/tvix/glue/src/known_paths.rs
@@ -25,27 +25,27 @@ pub struct KnownPaths {
     ///
     /// Keys are derivation paths, values are a tuple of the "hash derivation
     /// modulo" and the Derivation struct itself.
-    derivations: HashMap<StorePath, ([u8; 32], Derivation)>,
+    derivations: HashMap<StorePath<String>, ([u8; 32], Derivation)>,
 
     /// A map from output path to (one) drv path.
     /// Note that in the case of FODs, multiple drvs can produce the same output
     /// path. We use one of them.
-    outputs_to_drvpath: HashMap<StorePath, StorePath>,
+    outputs_to_drvpath: HashMap<StorePath<String>, StorePath<String>>,
 
     /// A map from output path to fetches (and their names).
-    outputs_to_fetches: HashMap<StorePath, (String, Fetch)>,
+    outputs_to_fetches: HashMap<StorePath<String>, (String, Fetch)>,
 }
 
 impl KnownPaths {
     /// Fetch the opaque "hash derivation modulo" for a given derivation path.
-    pub fn get_hash_derivation_modulo(&self, drv_path: &StorePath) -> Option<&[u8; 32]> {
+    pub fn get_hash_derivation_modulo(&self, drv_path: &StorePath<String>) -> Option<&[u8; 32]> {
         self.derivations
             .get(drv_path)
             .map(|(hash_derivation_modulo, _derivation)| hash_derivation_modulo)
     }
 
     /// Return a reference to the Derivation for a given drv path.
-    pub fn get_drv_by_drvpath(&self, drv_path: &StorePath) -> Option<&Derivation> {
+    pub fn get_drv_by_drvpath(&self, drv_path: &StorePath<String>) -> Option<&Derivation> {
         self.derivations
             .get(drv_path)
             .map(|(_hash_derivation_modulo, derivation)| derivation)
@@ -54,7 +54,10 @@ impl KnownPaths {
     /// Return the drv path of the derivation producing the passed output path.
     /// Note there can be multiple Derivations producing the same output path in
     /// flight; this function will only return one of them.
-    pub fn get_drv_path_for_output_path(&self, output_path: &StorePath) -> Option<&StorePath> {
+    pub fn get_drv_path_for_output_path(
+        &self,
+        output_path: &StorePath<String>,
+    ) -> Option<&StorePath<String>> {
         self.outputs_to_drvpath.get(output_path)
     }
 
@@ -63,7 +66,7 @@ impl KnownPaths {
     /// be fully calculated.
     /// All input derivations this refers to must also be inserted to this
     /// struct.
-    pub fn add_derivation(&mut self, drv_path: StorePath, drv: Derivation) {
+    pub fn add_derivation(&mut self, drv_path: StorePath<String>, drv: Derivation) {
         // check input derivations to have been inserted.
         #[cfg(debug_assertions)]
         {
@@ -124,11 +127,19 @@ impl KnownPaths {
 
     /// Return the name and fetch producing the passed output path.
     /// Note there can also be (multiple) Derivations producing the same output path.
-    pub fn get_fetch_for_output_path(&self, output_path: &StorePath) -> Option<(String, Fetch)> {
+    pub fn get_fetch_for_output_path(
+        &self,
+        output_path: &StorePath<String>,
+    ) -> Option<(String, Fetch)> {
         self.outputs_to_fetches
             .get(output_path)
             .map(|(name, fetch)| (name.to_owned(), fetch.to_owned()))
     }
+
+    /// Returns an iterator over all known derivations and their store path.
+    pub fn get_derivations(&self) -> impl Iterator<Item = (&StorePath<String>, &Derivation)> {
+        self.derivations.iter().map(|(k, v)| (k, &v.1))
+    }
 }
 
 #[cfg(test)]
@@ -151,26 +162,26 @@ mod tests {
             "tests/ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv"
         ))
         .expect("must parse");
-        static ref BAR_DRV_PATH: StorePath =
+        static ref BAR_DRV_PATH: StorePath<String> =
             StorePath::from_bytes(b"ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv").expect("must parse");
-        static ref FOO_DRV_PATH: StorePath =
+        static ref FOO_DRV_PATH: StorePath<String> =
             StorePath::from_bytes(b"ch49594n9avinrf8ip0aslidkc4lxkqv-foo.drv").expect("must parse");
-        static ref BAR_OUT_PATH: StorePath =
+        static ref BAR_OUT_PATH: StorePath<String> =
             StorePath::from_bytes(b"mp57d33657rf34lzvlbpfa1gjfv5gmpg-bar").expect("must parse");
-        static ref FOO_OUT_PATH: StorePath =
+        static ref FOO_OUT_PATH: StorePath<String> =
             StorePath::from_bytes(b"fhaj6gmwns62s6ypkcldbaj2ybvkhx3p-foo").expect("must parse");
 
         static ref FETCH_URL : Fetch = Fetch::URL{
             url: Url::parse("https://raw.githubusercontent.com/aaptel/notmuch-extract-patch/f732a53e12a7c91a06755ebfab2007adc9b3063b/notmuch-extract-patch").unwrap(),
             exp_hash: Some(nixhash::from_sri_str("sha256-Xa1Jbl2Eq5+L0ww+Ph1osA3Z/Dxe/RkN1/dITQCdXFk=").unwrap())
         };
-        static ref FETCH_URL_OUT_PATH: StorePath = StorePath::from_bytes(b"06qi00hylriyfm0nl827crgjvbax84mz-notmuch-extract-patch").unwrap();
+        static ref FETCH_URL_OUT_PATH: StorePath<String> = StorePath::from_bytes(b"06qi00hylriyfm0nl827crgjvbax84mz-notmuch-extract-patch").unwrap();
 
         static ref FETCH_TARBALL : Fetch = Fetch::Tarball{
             url: Url::parse("https://github.com/NixOS/nixpkgs/archive/91050ea1e57e50388fa87a3302ba12d188ef723a.tar.gz").unwrap(),
             exp_nar_sha256: Some(nixbase32::decode_fixed("1hf6cgaci1n186kkkjq106ryf8mmlq9vnwgfwh625wa8hfgdn4dm").unwrap())
         };
-        static ref FETCH_TARBALL_OUT_PATH: StorePath = StorePath::from_bytes(b"7adgvk5zdfq4pwrhsm3n9lzypb12gw0g-source").unwrap();
+        static ref FETCH_TARBALL_OUT_PATH: StorePath<String> = StorePath::from_bytes(b"7adgvk5zdfq4pwrhsm3n9lzypb12gw0g-source").unwrap();
     }
 
     /// ensure we don't allow acdding a Derivation that depends on another,
@@ -269,5 +280,21 @@ mod tests {
         );
     }
 
+    #[test]
+    fn get_derivations_working() {
+        let mut known_paths = KnownPaths::default();
+
+        // Add BAR_DRV
+        known_paths.add_derivation(BAR_DRV_PATH.clone(), BAR_DRV.clone());
+
+        // We should be able to find BAR_DRV_PATH and BAR_DRV as a pair in get_derivations.
+        assert_eq!(
+            Some((&BAR_DRV_PATH.clone(), &BAR_DRV.clone())),
+            known_paths
+                .get_derivations()
+                .find(|(s, d)| (*s, *d) == (&BAR_DRV_PATH, &BAR_DRV))
+        );
+    }
+
     // TODO: add test panicking about missing digest
 }
diff --git a/tvix/glue/src/refscan.rs b/tvix/glue/src/refscan.rs
index 0e0bb6c77828..80a126349746 100644
--- a/tvix/glue/src/refscan.rs
+++ b/tvix/glue/src/refscan.rs
@@ -6,62 +6,250 @@
 //!
 //! The scanner itself is using the Wu-Manber string-matching algorithm, using
 //! our fork of the `wu-mamber` crate.
-
+use pin_project::pin_project;
 use std::collections::BTreeSet;
+use std::pin::Pin;
+use std::sync::Arc;
+use std::task::{ready, Poll};
+use tokio::io::{AsyncBufRead, AsyncRead, ReadBuf};
 use wu_manber::TwoByteWM;
 
-pub const STORE_PATH_LEN: usize = "/nix/store/00000000000000000000000000000000".len();
-
-/// Represents a "primed" reference scanner with an automaton that knows the set
-/// of store paths to scan for.
-pub struct ReferenceScanner<P: Ord + AsRef<[u8]>> {
+/// A searcher that incapsulates the candidates and the Wu-Manber searcher.
+/// This is separate from the scanner because we need to look for the same
+/// pattern in multiple outputs and don't want to pay the price of constructing
+/// the searcher for each build output.
+pub struct ReferencePatternInner<P> {
     candidates: Vec<P>,
+    longest_candidate: usize,
+    // FUTUREWORK: Support overlapping patterns to be compatible with cpp Nix
     searcher: Option<TwoByteWM>,
-    matches: Vec<usize>,
 }
 
-impl<P: Clone + Ord + AsRef<[u8]>> ReferenceScanner<P> {
-    /// Construct a new `ReferenceScanner` that knows how to scan for the given
-    /// candidate store paths.
+#[derive(Clone)]
+pub struct ReferencePattern<P> {
+    inner: Arc<ReferencePatternInner<P>>,
+}
+
+impl<P> ReferencePattern<P> {
+    pub fn candidates(&self) -> &[P] {
+        &self.inner.candidates
+    }
+
+    pub fn longest_candidate(&self) -> usize {
+        self.inner.longest_candidate
+    }
+}
+
+impl<P: AsRef<[u8]>> ReferencePattern<P> {
+    /// Construct a new `ReferencePattern` that knows how to scan for the given
+    /// candidates.
     pub fn new(candidates: Vec<P>) -> Self {
         let searcher = if candidates.is_empty() {
             None
         } else {
             Some(TwoByteWM::new(&candidates))
         };
+        let longest_candidate = candidates.iter().fold(0, |v, c| v.max(c.as_ref().len()));
 
-        ReferenceScanner {
-            searcher,
-            candidates,
-            matches: Default::default(),
+        ReferencePattern {
+            inner: Arc::new(ReferencePatternInner {
+                searcher,
+                candidates,
+                longest_candidate,
+            }),
         }
     }
+}
+
+impl<P> From<Vec<P>> for ReferencePattern<P>
+where
+    P: AsRef<[u8]>,
+{
+    fn from(candidates: Vec<P>) -> Self {
+        Self::new(candidates)
+    }
+}
+
+/// Represents a "primed" reference scanner with an automaton that knows the set
+/// of bytes patterns to scan for.
+pub struct ReferenceScanner<P> {
+    pattern: ReferencePattern<P>,
+    matches: Vec<bool>,
+}
+
+impl<P: AsRef<[u8]>> ReferenceScanner<P> {
+    /// Construct a new `ReferenceScanner` that knows how to scan for the given
+    /// candidate bytes patterns.
+    pub fn new<IP: Into<ReferencePattern<P>>>(pattern: IP) -> Self {
+        let pattern = pattern.into();
+        let matches = vec![false; pattern.candidates().len()];
+        ReferenceScanner { pattern, matches }
+    }
 
-    /// Scan the given str for all non-overlapping matches and collect them
+    /// Scan the given buffer for all non-overlapping matches and collect them
     /// in the scanner.
     pub fn scan<S: AsRef<[u8]>>(&mut self, haystack: S) {
-        if haystack.as_ref().len() < STORE_PATH_LEN {
+        if haystack.as_ref().len() < self.pattern.longest_candidate() {
             return;
         }
 
-        if let Some(searcher) = &self.searcher {
+        if let Some(searcher) = &self.pattern.inner.searcher {
             for m in searcher.find(haystack) {
-                self.matches.push(m.pat_idx);
+                self.matches[m.pat_idx] = true;
             }
         }
     }
 
+    pub fn pattern(&self) -> &ReferencePattern<P> {
+        &self.pattern
+    }
+
+    pub fn matches(&self) -> &[bool] {
+        &self.matches
+    }
+
+    pub fn candidate_matches(&self) -> impl Iterator<Item = &P> {
+        let candidates = self.pattern.candidates();
+        self.matches.iter().enumerate().filter_map(|(idx, found)| {
+            if *found {
+                Some(&candidates[idx])
+            } else {
+                None
+            }
+        })
+    }
+}
+
+impl<P: Clone + Ord + AsRef<[u8]>> ReferenceScanner<P> {
     /// Finalise the reference scanner and return the resulting matches.
     pub fn finalise(self) -> BTreeSet<P> {
-        self.matches
-            .into_iter()
-            .map(|idx| self.candidates[idx].clone())
-            .collect()
+        self.candidate_matches().cloned().collect()
+    }
+}
+
+const DEFAULT_BUF_SIZE: usize = 8 * 1024;
+
+#[pin_project]
+pub struct ReferenceReader<P, R> {
+    scanner: ReferenceScanner<P>,
+    buffer: Vec<u8>,
+    consumed: usize,
+    #[pin]
+    reader: R,
+}
+
+impl<P, R> ReferenceReader<P, R>
+where
+    P: AsRef<[u8]>,
+{
+    pub fn new(pattern: ReferencePattern<P>, reader: R) -> ReferenceReader<P, R> {
+        Self::with_capacity(DEFAULT_BUF_SIZE, pattern, reader)
+    }
+
+    pub fn with_capacity(
+        capacity: usize,
+        pattern: ReferencePattern<P>,
+        reader: R,
+    ) -> ReferenceReader<P, R> {
+        // If capacity is not at least as long as longest_candidate we can't do a scan
+        let capacity = capacity.max(pattern.longest_candidate());
+        ReferenceReader {
+            scanner: ReferenceScanner::new(pattern),
+            buffer: Vec::with_capacity(capacity),
+            consumed: 0,
+            reader,
+        }
+    }
+
+    pub fn scanner(&self) -> &ReferenceScanner<P> {
+        &self.scanner
+    }
+}
+
+impl<P, R> ReferenceReader<P, R>
+where
+    P: Clone + Ord + AsRef<[u8]>,
+{
+    pub fn finalise(self) -> BTreeSet<P> {
+        self.scanner.finalise()
+    }
+}
+
+impl<P, R> AsyncRead for ReferenceReader<P, R>
+where
+    R: AsyncRead,
+    P: AsRef<[u8]>,
+{
+    fn poll_read(
+        mut self: Pin<&mut Self>,
+        cx: &mut std::task::Context<'_>,
+        buf: &mut tokio::io::ReadBuf<'_>,
+    ) -> Poll<std::io::Result<()>> {
+        let internal_buf = ready!(self.as_mut().poll_fill_buf(cx))?;
+        let amt = buf.remaining().min(internal_buf.len());
+        buf.put_slice(&internal_buf[..amt]);
+        self.consume(amt);
+        Poll::Ready(Ok(()))
+    }
+}
+
+impl<P, R> AsyncBufRead for ReferenceReader<P, R>
+where
+    R: AsyncRead,
+    P: AsRef<[u8]>,
+{
+    fn poll_fill_buf(
+        self: Pin<&mut Self>,
+        cx: &mut std::task::Context<'_>,
+    ) -> Poll<std::io::Result<&[u8]>> {
+        let overlap = self.scanner.pattern.longest_candidate() - 1;
+        let mut this = self.project();
+        // Still data in buffer
+        if *this.consumed < this.buffer.len() {
+            return Poll::Ready(Ok(&this.buffer[*this.consumed..]));
+        }
+        // We need to copy last `overlap` bytes to front to deal with references that overlap reads
+        if *this.consumed > overlap {
+            let start = this.buffer.len() - overlap;
+            this.buffer.copy_within(start.., 0);
+            this.buffer.truncate(overlap);
+            *this.consumed = overlap;
+        }
+        // Read at least until self.buffer.len() > overlap so we can do one scan
+        loop {
+            let filled = {
+                let mut buf = ReadBuf::uninit(this.buffer.spare_capacity_mut());
+                ready!(this.reader.as_mut().poll_read(cx, &mut buf))?;
+                buf.filled().len()
+            };
+            // SAFETY: We just read `filled` amount of data above
+            unsafe {
+                this.buffer.set_len(filled + this.buffer.len());
+            }
+            if filled == 0 || this.buffer.len() > overlap {
+                break;
+            }
+        }
+
+        #[allow(clippy::needless_borrows_for_generic_args)] // misfiring lint (breaks code below)
+        this.scanner.scan(&this.buffer);
+
+        Poll::Ready(Ok(&this.buffer[*this.consumed..]))
+    }
+
+    fn consume(self: Pin<&mut Self>, amt: usize) {
+        debug_assert!(self.consumed + amt <= self.buffer.len());
+        let this = self.project();
+        *this.consumed += amt;
     }
 }
 
 #[cfg(test)]
 mod tests {
+    use rstest::rstest;
+    use tokio::io::AsyncReadExt as _;
+    use tokio_test::io::Builder;
+
     use super::*;
 
     // The actual derivation of `nixpkgs.hello`.
@@ -112,4 +300,40 @@ mod tests {
             assert!(result.contains(c));
         }
     }
+
+    #[rstest]
+    #[case::normal(8096, 8096)]
+    #[case::small_capacity(8096, 1)]
+    #[case::small_read(1, 8096)]
+    #[case::all_small(1, 1)]
+    #[tokio::test]
+    async fn test_reference_reader(#[case] chunk_size: usize, #[case] capacity: usize) {
+        let candidates = vec![
+            // these exist in the drv:
+            "33l4p0pn0mybmqzaxfkpppyh7vx1c74p",
+            "pf80kikyxr63wrw56k00i1kw6ba76qik",
+            "cp65c8nk29qq5cl1wyy5qyw103cwmax7",
+            // this doesn't:
+            "fn7zvafq26f0c8b17brs7s95s10ibfzs",
+        ];
+        let pattern = ReferencePattern::new(candidates.clone());
+        let mut mock = Builder::new();
+        for c in HELLO_DRV.as_bytes().chunks(chunk_size) {
+            mock.read(c);
+        }
+        let mock = mock.build();
+        let mut reader = ReferenceReader::with_capacity(capacity, pattern, mock);
+        let mut s = String::new();
+        reader.read_to_string(&mut s).await.unwrap();
+        assert_eq!(s, HELLO_DRV);
+
+        let result = reader.finalise();
+        assert_eq!(result.len(), 3);
+
+        for c in candidates[..3].iter() {
+            assert!(result.contains(c));
+        }
+    }
+
+    // FUTUREWORK: Test with large file
 }
diff --git a/tvix/glue/src/tvix_build.rs b/tvix/glue/src/tvix_build.rs
index e9eb1725ef3e..ae01351503f2 100644
--- a/tvix/glue/src/tvix_build.rs
+++ b/tvix/glue/src/tvix_build.rs
@@ -1,7 +1,7 @@
 //! This module contains glue code translating from
 //! [nix_compat::derivation::Derivation] to [tvix_build::proto::BuildRequest].
 
-use std::collections::{BTreeMap, BTreeSet};
+use std::collections::BTreeMap;
 
 use bytes::Bytes;
 use nix_compat::{derivation::Derivation, nixbase32};
@@ -10,7 +10,7 @@ use tvix_build::proto::{
     build_request::{AdditionalFile, BuildConstraints, EnvVar},
     BuildRequest,
 };
-use tvix_castore::proto::{self, node::Node};
+use tvix_castore::Node;
 
 /// These are the environment variables that Nix sets in its sandbox for every
 /// build.
@@ -36,10 +36,9 @@ const NIX_ENVIRONMENT_VARS: [(&str, &str); 12] = [
 ///   (`fn_input_sources_to_node`)
 /// - one translating a tuple of drv path and (a subset of their) output names to
 ///   castore nodes of the selected outpus (`fn_input_drvs_to_output_nodes`).
-#[allow(clippy::mutable_key_type)]
 pub(crate) fn derivation_to_build_request(
     derivation: &Derivation,
-    inputs: BTreeSet<Node>,
+    inputs: BTreeMap<bytes::Bytes, Node>,
 ) -> std::io::Result<BuildRequest> {
     debug_assert!(derivation.validate(true).is_ok(), "drv must validate");
 
@@ -111,7 +110,7 @@ pub(crate) fn derivation_to_build_request(
             .collect(),
         inputs: inputs
             .into_iter()
-            .map(|n| proto::Node { node: Some(n) })
+            .map(|(name, node)| tvix_castore::proto::Node::from_name_and_node(name, node))
             .collect(),
         inputs_dir: nix_compat::store_path::STORE_DIR[1..].into(),
         constraints,
@@ -192,18 +191,15 @@ fn calculate_pass_as_file_env(k: &str) -> (String, String) {
 
 #[cfg(test)]
 mod test {
-    use std::collections::BTreeSet;
-
     use bytes::Bytes;
     use nix_compat::derivation::Derivation;
+    use std::collections::BTreeMap;
     use tvix_build::proto::{
         build_request::{AdditionalFile, BuildConstraints, EnvVar},
         BuildRequest,
     };
-    use tvix_castore::{
-        fixtures::DUMMY_DIGEST,
-        proto::{self, node::Node, DirectoryNode},
-    };
+    use tvix_castore::fixtures::DUMMY_DIGEST;
+    use tvix_castore::Node;
 
     use crate::tvix_build::NIX_ENVIRONMENT_VARS;
 
@@ -211,11 +207,11 @@ mod test {
     use lazy_static::lazy_static;
 
     lazy_static! {
-        static ref INPUT_NODE_FOO: Node = Node::Directory(DirectoryNode {
-            name: Bytes::from("mp57d33657rf34lzvlbpfa1gjfv5gmpg-bar"),
-            digest: DUMMY_DIGEST.clone().into(),
-            size: 42,
-        });
+        static ref INPUT_NODE_FOO_NAME: Bytes = "mp57d33657rf34lzvlbpfa1gjfv5gmpg-bar".into();
+        static ref INPUT_NODE_FOO: Node = Node::Directory {
+            digest: DUMMY_DIGEST.clone(),
+            size: 42
+        };
     }
 
     #[test]
@@ -224,9 +220,11 @@ mod test {
 
         let derivation = Derivation::from_aterm_bytes(aterm_bytes).expect("must parse");
 
-        let build_request =
-            derivation_to_build_request(&derivation, BTreeSet::from([INPUT_NODE_FOO.clone()]))
-                .expect("must succeed");
+        let build_request = derivation_to_build_request(
+            &derivation,
+            BTreeMap::from([(INPUT_NODE_FOO_NAME.clone(), INPUT_NODE_FOO.clone())]),
+        )
+        .expect("must succeed");
 
         let mut expected_environment_vars = vec![
             EnvVar {
@@ -263,9 +261,10 @@ mod test {
                 command_args: vec![":".into()],
                 outputs: vec!["nix/store/fhaj6gmwns62s6ypkcldbaj2ybvkhx3p-foo".into()],
                 environment_vars: expected_environment_vars,
-                inputs: vec![proto::Node {
-                    node: Some(INPUT_NODE_FOO.clone())
-                }],
+                inputs: vec![tvix_castore::proto::Node::from_name_and_node(
+                    INPUT_NODE_FOO_NAME.clone(),
+                    INPUT_NODE_FOO.clone()
+                )],
                 inputs_dir: "nix/store".into(),
                 constraints: Some(BuildConstraints {
                     system: derivation.system.clone(),
@@ -289,7 +288,7 @@ mod test {
         let derivation = Derivation::from_aterm_bytes(aterm_bytes).expect("must parse");
 
         let build_request =
-            derivation_to_build_request(&derivation, BTreeSet::from([])).expect("must succeed");
+            derivation_to_build_request(&derivation, BTreeMap::from([])).expect("must succeed");
 
         let mut expected_environment_vars = vec![
             EnvVar {
@@ -359,7 +358,7 @@ mod test {
         let derivation = Derivation::from_aterm_bytes(aterm_bytes).expect("must parse");
 
         let build_request =
-            derivation_to_build_request(&derivation, BTreeSet::from([])).expect("must succeed");
+            derivation_to_build_request(&derivation, BTreeMap::from([])).expect("must succeed");
 
         let mut expected_environment_vars = vec![
             // Note how bar and baz are not present in the env anymore,
diff --git a/tvix/glue/src/tvix_io.rs b/tvix/glue/src/tvix_io.rs
index db0c2cef77aa..323fa8d20ccb 100644
--- a/tvix/glue/src/tvix_io.rs
+++ b/tvix/glue/src/tvix_io.rs
@@ -1,12 +1,10 @@
 //! This module implements a wrapper around tvix-eval's [EvalIO] type,
 //! adding functionality which is required by tvix-cli:
 //!
-//! 1. Marking plain paths known to the reference scanner.
-//! 2. Handling the C++ Nix `__corepkgs__`-hack for nixpkgs bootstrapping.
+//! 1. Handling the C++ Nix `__corepkgs__`-hack for nixpkgs bootstrapping.
 //!
 //! All uses of [EvalIO] in tvix-cli must make use of this wrapper,
-//! otherwise fundamental features like nixpkgs bootstrapping and hash
-//! calculation will not work.
+//! otherwise nixpkgs bootstrapping will not work.
 
 use std::io::{self, Cursor};
 use std::path::{Path, PathBuf};
diff --git a/tvix/glue/src/tvix_store_io.rs b/tvix/glue/src/tvix_store_io.rs
index c25194d50fda..93675b438f25 100644
--- a/tvix/glue/src/tvix_store_io.rs
+++ b/tvix/glue/src/tvix_store_io.rs
@@ -4,9 +4,9 @@ use futures::{StreamExt, TryStreamExt};
 use nix_compat::nixhash::NixHash;
 use nix_compat::store_path::StorePathRef;
 use nix_compat::{nixhash::CAHash, store_path::StorePath};
+use std::collections::BTreeMap;
 use std::{
     cell::RefCell,
-    collections::BTreeSet,
     io,
     path::{Path, PathBuf},
     sync::Arc,
@@ -15,15 +15,13 @@ use tokio_util::io::SyncIoBridge;
 use tracing::{error, instrument, warn, Level, Span};
 use tracing_indicatif::span_ext::IndicatifSpanExt;
 use tvix_build::buildservice::BuildService;
-use tvix_castore::proto::node::Node;
 use tvix_eval::{EvalIO, FileType, StdIO};
 use tvix_store::nar::NarCalculationService;
 
 use tvix_castore::{
     blobservice::BlobService,
     directoryservice::{self, DirectoryService},
-    proto::NamedNode,
-    B3Digest,
+    Node,
 };
 use tvix_store::{pathinfoservice::PathInfoService, proto::PathInfo};
 
@@ -67,7 +65,7 @@ pub struct TvixStoreIO {
     >,
 
     // Paths known how to produce, by building or fetching.
-    pub(crate) known_paths: RefCell<KnownPaths>,
+    pub known_paths: RefCell<KnownPaths>,
 }
 
 impl TvixStoreIO {
@@ -107,7 +105,7 @@ impl TvixStoreIO {
     #[instrument(skip(self, store_path), fields(store_path=%store_path, indicatif.pb_show=1), ret(level = Level::TRACE), err)]
     async fn store_path_to_node(
         &self,
-        store_path: &StorePath,
+        store_path: &StorePath<String>,
         sub_path: &Path,
     ) -> io::Result<Option<Node>> {
         // Find the root node for the store_path.
@@ -122,7 +120,22 @@ impl TvixStoreIO {
             .await?
         {
             // if we have a PathInfo, we know there will be a root_node (due to validation)
-            Some(path_info) => path_info.node.expect("no node").node.expect("no node"),
+            // TODO: use stricter typed BuildRequest here.
+            Some(path_info) => {
+                let (name, node) = path_info
+                    .node
+                    .expect("no node")
+                    .into_name_and_node()
+                    .expect("invalid node");
+
+                assert_eq!(
+                    store_path.to_string().as_bytes(),
+                    name.as_ref(),
+                    "returned node basename must match requested store path"
+                );
+
+                node
+            }
             // If there's no PathInfo found, this normally means we have to
             // trigger the build (and insert into PathInfoService, after
             // reference scanning).
@@ -185,8 +198,7 @@ impl TvixStoreIO {
                         // derivation_to_build_request needs castore nodes for all inputs.
                         // Provide them, which means, here is where we recursively build
                         // all dependencies.
-                        #[allow(clippy::mutable_key_type)]
-                        let mut input_nodes: BTreeSet<Node> =
+                        let mut inputs: BTreeMap<Bytes, Node> =
                             futures::stream::iter(drv.input_derivations.iter())
                                 .map(|(input_drv_path, output_names)| {
                                     // look up the derivation object
@@ -201,7 +213,7 @@ impl TvixStoreIO {
                                     };
 
                                     // convert output names to actual paths
-                                    let output_paths: Vec<StorePath> = output_names
+                                    let output_paths: Vec<StorePath<String>> = output_names
                                         .iter()
                                         .map(|output_name| {
                                             input_drv
@@ -214,6 +226,7 @@ impl TvixStoreIO {
                                                 .clone()
                                         })
                                         .collect();
+
                                     // For each output, ask for the castore node.
                                     // We're in a per-derivation context, so if they're
                                     // not built yet they'll all get built together.
@@ -228,7 +241,7 @@ impl TvixStoreIO {
                                                 .await?;
 
                                             if let Some(node) = node {
-                                                Ok(node)
+                                                Ok((output_path.to_string().into(), node))
                                             } else {
                                                 Err(io::Error::other("no node produced"))
                                             }
@@ -242,26 +255,29 @@ impl TvixStoreIO {
                                 .try_collect()
                                 .await?;
 
-                        // add input sources
                         // FUTUREWORK: merge these who things together
-                        #[allow(clippy::mutable_key_type)]
-                        let input_nodes_input_sources: BTreeSet<Node> =
+                        // add input sources
+                        let input_sources: BTreeMap<_, _> =
                             futures::stream::iter(drv.input_sources.iter())
                                 .then(|input_source| {
-                                    Box::pin(async {
-                                        let node = self
-                                            .store_path_to_node(input_source, Path::new(""))
-                                            .await?;
-                                        if let Some(node) = node {
-                                            Ok(node)
-                                        } else {
-                                            Err(io::Error::other("no node produced"))
+                                    Box::pin({
+                                        let input_source = input_source.clone();
+                                        async move {
+                                            let node = self
+                                                .store_path_to_node(&input_source, Path::new(""))
+                                                .await?;
+                                            if let Some(node) = node {
+                                                Ok((input_source.to_string().into(), node))
+                                            } else {
+                                                Err(io::Error::other("no node produced"))
+                                            }
                                         }
                                     })
                                 })
                                 .try_collect()
                                 .await?;
-                        input_nodes.extend(input_nodes_input_sources);
+
+                        inputs.extend(input_sources);
 
                         span.pb_set_message(&format!("🔨Building {}", &store_path));
 
@@ -270,7 +286,7 @@ impl TvixStoreIO {
                         // operations, so dealt with in the Some(…) match arm
 
                         // synthesize the build request.
-                        let build_request = derivation_to_build_request(&drv, input_nodes)?;
+                        let build_request = derivation_to_build_request(&drv, inputs)?;
 
                         // create a build
                         let build_result = self
@@ -284,19 +300,21 @@ impl TvixStoreIO {
 
                         // For each output, insert a PathInfo.
                         for output in &build_result.outputs {
-                            let root_node = output.node.as_ref().expect("invalid root node");
+                            let (output_name, output_node) =
+                                output.clone().into_name_and_node().expect("invalid node");
 
                             // calculate the nar representation
                             let (nar_size, nar_sha256) = self
                                 .nar_calculation_service
-                                .calculate_nar(root_node)
+                                .calculate_nar(&output_node)
                                 .await?;
 
                             // assemble the PathInfo to persist
                             let path_info = PathInfo {
-                                node: Some(tvix_castore::proto::Node {
-                                    node: Some(root_node.clone()),
-                                }),
+                                node: Some(tvix_castore::proto::Node::from_name_and_node(
+                                    output_name.into(),
+                                    output_node,
+                                )),
                                 references: vec![], // TODO: refscan
                                 narinfo: Some(tvix_store::proto::NarInfo {
                                     nar_size,
@@ -329,16 +347,17 @@ impl TvixStoreIO {
                         }
 
                         // find the output for the store path requested
+                        let s = store_path.to_string();
+
                         build_result
                             .outputs
                             .into_iter()
-                            .find(|output_node| {
-                                output_node.node.as_ref().expect("invalid node").get_name()
-                                    == store_path.to_string().as_bytes()
+                            .map(|e| e.into_name_and_node().expect("invalid node"))
+                            .find(|(output_name, _output_node)| {
+                                output_name.as_ref() == s.as_bytes()
                             })
                             .expect("build didn't produce the store path")
-                            .node
-                            .expect("invalid node")
+                            .1
                     }
                 }
             }
@@ -353,13 +372,13 @@ impl TvixStoreIO {
             .map_err(|e| std::io::Error::new(io::ErrorKind::Other, e))
     }
 
-    pub(crate) async fn node_to_path_info(
+    pub(crate) async fn node_to_path_info<'a>(
         &self,
-        name: &str,
+        name: &'a str,
         path: &Path,
         ca: &CAHash,
         root_node: Node,
-    ) -> io::Result<(PathInfo, NixHash, StorePath)> {
+    ) -> io::Result<(PathInfo, NixHash, StorePathRef<'a>)> {
         // Ask the PathInfoService for the NAR size and sha256
         // We always need it no matter what is the actual hash mode
         // because the path info construct a narinfo which *always*
@@ -381,27 +400,27 @@ impl TvixStoreIO {
                 },
             )?;
 
-        // assemble a new root_node with a name that is derived from the nar hash.
-        let root_node = root_node.rename(output_path.to_string().into_bytes().into());
-        tvix_store::import::log_node(&root_node, path);
+        tvix_store::import::log_node(name.as_bytes(), &root_node, path);
 
-        let path_info =
-            tvix_store::import::derive_nar_ca_path_info(nar_size, nar_sha256, Some(ca), root_node);
+        // construct a PathInfo
+        let path_info = tvix_store::import::derive_nar_ca_path_info(
+            nar_size,
+            nar_sha256,
+            Some(ca),
+            output_path.to_string().into(),
+            root_node,
+        );
 
-        Ok((
-            path_info,
-            NixHash::Sha256(nar_sha256),
-            output_path.to_owned(),
-        ))
+        Ok((path_info, NixHash::Sha256(nar_sha256), output_path))
     }
 
-    pub(crate) async fn register_node_in_path_info_service(
+    pub(crate) async fn register_node_in_path_info_service<'a>(
         &self,
-        name: &str,
+        name: &'a str,
         path: &Path,
         ca: &CAHash,
         root_node: Node,
-    ) -> io::Result<StorePath> {
+    ) -> io::Result<StorePathRef<'a>> {
         let (path_info, _, output_path) = self.node_to_path_info(name, path, ca, root_node).await?;
         let _path_info = self.path_info_service.as_ref().put(path_info).await?;
 
@@ -426,7 +445,7 @@ impl EvalIO for TvixStoreIO {
         {
             if self
                 .tokio_handle
-                .block_on(self.store_path_to_node(&store_path, &sub_path))?
+                .block_on(self.store_path_to_node(&store_path, sub_path))?
                 .is_some()
             {
                 Ok(true)
@@ -448,30 +467,18 @@ impl EvalIO for TvixStoreIO {
         {
             if let Some(node) = self
                 .tokio_handle
-                .block_on(async { self.store_path_to_node(&store_path, &sub_path).await })?
+                .block_on(async { self.store_path_to_node(&store_path, sub_path).await })?
             {
                 // depending on the node type, treat open differently
                 match node {
-                    Node::Directory(_) => {
+                    Node::Directory { .. } => {
                         // This would normally be a io::ErrorKind::IsADirectory (still unstable)
                         Err(io::Error::new(
                             io::ErrorKind::Unsupported,
                             format!("tried to open directory at {:?}", path),
                         ))
                     }
-                    Node::File(file_node) => {
-                        let digest: B3Digest =
-                            file_node.digest.clone().try_into().map_err(|_e| {
-                                error!(
-                                    file_node = ?file_node,
-                                    "invalid digest"
-                                );
-                                io::Error::new(
-                                    io::ErrorKind::InvalidData,
-                                    format!("invalid digest length in file node: {:?}", file_node),
-                                )
-                            })?;
-
+                    Node::File { digest, .. } => {
                         self.tokio_handle.block_on(async {
                             let resp = self.blob_service.as_ref().open_read(&digest).await?;
                             match resp {
@@ -493,7 +500,7 @@ impl EvalIO for TvixStoreIO {
                             }
                         })
                     }
-                    Node::Symlink(_symlink_node) => Err(io::Error::new(
+                    Node::Symlink { .. } => Err(io::Error::new(
                         io::ErrorKind::Unsupported,
                         "open for symlinks is unsupported",
                     ))?,
@@ -516,12 +523,12 @@ impl EvalIO for TvixStoreIO {
         {
             if let Some(node) = self
                 .tokio_handle
-                .block_on(async { self.store_path_to_node(&store_path, &sub_path).await })?
+                .block_on(async { self.store_path_to_node(&store_path, sub_path).await })?
             {
                 match node {
-                    Node::Directory(_) => Ok(FileType::Directory),
-                    Node::File(_) => Ok(FileType::Regular),
-                    Node::Symlink(_) => Ok(FileType::Symlink),
+                    Node::Directory { .. } => Ok(FileType::Directory),
+                    Node::File { .. } => Ok(FileType::Regular),
+                    Node::Symlink { .. } => Ok(FileType::Symlink),
                 }
             } else {
                 self.std_io.file_type(path)
@@ -538,31 +545,21 @@ impl EvalIO for TvixStoreIO {
         {
             if let Some(node) = self
                 .tokio_handle
-                .block_on(async { self.store_path_to_node(&store_path, &sub_path).await })?
+                .block_on(async { self.store_path_to_node(&store_path, sub_path).await })?
             {
                 match node {
-                    Node::Directory(directory_node) => {
+                    Node::Directory { digest, .. } => {
                         // fetch the Directory itself.
-                        let digest: B3Digest =
-                            directory_node.digest.clone().try_into().map_err(|_e| {
-                                io::Error::new(
-                                    io::ErrorKind::InvalidData,
-                                    format!(
-                                        "invalid digest length in directory node: {:?}",
-                                        directory_node
-                                    ),
-                                )
-                            })?;
-
-                        if let Some(directory) = self.tokio_handle.block_on(async {
-                            self.directory_service.as_ref().get(&digest).await
+                        if let Some(directory) = self.tokio_handle.block_on({
+                            let digest = digest.clone();
+                            async move { self.directory_service.as_ref().get(&digest).await }
                         })? {
                             let mut children: Vec<(bytes::Bytes, FileType)> = Vec::new();
-                            for node in directory.nodes() {
+                            for (name, node) in directory.into_nodes() {
                                 children.push(match node {
-                                    Node::Directory(e) => (e.name, FileType::Directory),
-                                    Node::File(e) => (e.name, FileType::Regular),
-                                    Node::Symlink(e) => (e.name, FileType::Symlink),
+                                    Node::Directory { .. } => (name.into(), FileType::Directory),
+                                    Node::File { .. } => (name.clone().into(), FileType::Regular),
+                                    Node::Symlink { .. } => (name.into(), FileType::Symlink),
                                 })
                             }
                             Ok(children)
@@ -579,14 +576,14 @@ impl EvalIO for TvixStoreIO {
                             ))?
                         }
                     }
-                    Node::File(_file_node) => {
+                    Node::File { .. } => {
                         // This would normally be a io::ErrorKind::NotADirectory (still unstable)
                         Err(io::Error::new(
                             io::ErrorKind::Unsupported,
                             "tried to readdir path {:?}, which is a file",
                         ))?
                     }
-                    Node::Symlink(_symlink_node) => Err(io::Error::new(
+                    Node::Symlink { .. } => Err(io::Error::new(
                         io::ErrorKind::Unsupported,
                         "read_dir for symlinks is unsupported",
                     ))?,
diff --git a/tvix/nar-bridge/Cargo.toml b/tvix/nar-bridge/Cargo.toml
index ba5f21ee6021..6ca0479a9a81 100644
--- a/tvix/nar-bridge/Cargo.toml
+++ b/tvix/nar-bridge/Cargo.toml
@@ -4,43 +4,43 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-axum = { version = "0.7.5", features = ["http2"] }
-tower = "0.4.13"
-tower-http = { version = "0.5", features = ["trace"] }
-bytes = "1.4.0"
-clap = { version = "4.0", features = ["derive", "env"] }
-data-encoding = "2.3.3"
-futures = "0.3.30"
-itertools = "0.12.0"
-prost = "0.13.1"
+axum = { workspace = true, features = ["http2"] }
+tower = { workspace = true }
+tower-http = { workspace = true, features = ["trace"] }
+bytes = { workspace = true }
+clap = { workspace = true, features = ["derive", "env"] }
+data-encoding = { workspace = true }
+futures = { workspace = true }
+itertools = { workspace = true }
+prost = { workspace = true }
 nix-compat = { path = "../nix-compat", features = ["async"] }
-thiserror = "1.0.56"
-tokio = { version = "1.32.0" }
-tokio-listener = { version = "0.4.2", features = [ "axum07", "clap", "multi-listener", "sd_listen" ] }
-tokio-util = { version = "0.7.9", features = ["io", "io-util", "compat"] }
-tonic = { version = "0.12.0", features = ["tls", "tls-roots"] }
+thiserror = { workspace = true }
+tokio = { workspace = true }
+tokio-listener = { workspace = true, features = ["axum07", "clap", "multi-listener", "sd_listen"] }
+tokio-util = { workspace = true, features = ["io", "io-util", "compat"] }
+tonic = { workspace = true, features = ["tls", "tls-roots"] }
 tvix-castore = { path = "../castore" }
 tvix-store = { path = "../store" }
 tvix-tracing = { path = "../tracing", features = ["tonic", "axum"] }
-tracing = "0.1.37"
-tracing-subscriber = "0.3.16"
-url = "2.4.0"
-serde = { version = "1.0.204", features = ["derive"] }
-lru = "0.12.3"
-parking_lot = "0.12.3"
-mimalloc = "0.1.43"
+tracing = { workspace = true }
+tracing-subscriber = { workspace = true }
+url = { workspace = true }
+serde = { workspace = true, features = ["derive"] }
+lru = { workspace = true }
+parking_lot = { workspace = true }
+mimalloc = { workspace = true }
 
 [build-dependencies]
-prost-build = "0.13.1"
-tonic-build = "0.12.0"
+prost-build = { workspace = true }
+tonic-build = { workspace = true }
 
 [features]
 default = ["otlp"]
 otlp = ["tvix-tracing/otlp"]
 
 [dev-dependencies]
-hex-literal = "0.4.1"
-rstest = "0.19.0"
+hex-literal = { workspace = true }
+rstest = { workspace = true }
 
 [lints]
 workspace = true
diff --git a/tvix/nar-bridge/default.nix b/tvix/nar-bridge/default.nix
index 3e116a1fc02b..2f1384e8211f 100644
--- a/tvix/nar-bridge/default.nix
+++ b/tvix/nar-bridge/default.nix
@@ -4,7 +4,7 @@
   runTests = true;
 }).overrideAttrs (old: rec {
   meta.ci.targets = lib.filter (x: lib.hasPrefix "with-features" x || x == "no-features") (lib.attrNames passthru);
-  passthru = (depot.tvix.utils.mkFeaturePowerset {
+  passthru = old.passthru // (depot.tvix.utils.mkFeaturePowerset {
     inherit (old) crateName;
     features = [ "otlp" ];
   });
diff --git a/tvix/nar-bridge/src/lib.rs b/tvix/nar-bridge/src/lib.rs
index 2f3dd82439b1..79f9b7372245 100644
--- a/tvix/nar-bridge/src/lib.rs
+++ b/tvix/nar-bridge/src/lib.rs
@@ -1,13 +1,15 @@
 use axum::http::StatusCode;
+use axum::response::IntoResponse;
 use axum::routing::{head, put};
 use axum::{routing::get, Router};
 use lru::LruCache;
+use nix_compat::nix_http;
 use parking_lot::RwLock;
 use std::num::NonZeroUsize;
 use std::sync::Arc;
 use tvix_castore::blobservice::BlobService;
 use tvix_castore::directoryservice::DirectoryService;
-use tvix_castore::proto::node::Node;
+use tvix_castore::Node;
 use tvix_store::pathinfoservice::PathInfoService;
 
 mod nar;
@@ -71,9 +73,12 @@ async fn four_o_four() -> Result<(), StatusCode> {
     Err(StatusCode::NOT_FOUND)
 }
 
-async fn nix_cache_info(priority: u64) -> String {
-    format!(
-        "StoreDir: /nix/store\nWantMassQuery: 1\nPriority: {}\n",
-        priority
+async fn nix_cache_info(priority: u64) -> impl IntoResponse {
+    (
+        [("Content-Type", nix_http::MIME_TYPE_CACHE_INFO)],
+        format!(
+            "StoreDir: /nix/store\nWantMassQuery: 1\nPriority: {}\n",
+            priority
+        ),
     )
 }
diff --git a/tvix/nar-bridge/src/nar.rs b/tvix/nar-bridge/src/nar.rs
index 9ee27c7df5ca..70d9d644c26c 100644
--- a/tvix/nar-bridge/src/nar.rs
+++ b/tvix/nar-bridge/src/nar.rs
@@ -5,7 +5,7 @@ use axum::response::Response;
 use bytes::Bytes;
 use data_encoding::BASE64URL_NOPAD;
 use futures::TryStreamExt;
-use nix_compat::nixbase32;
+use nix_compat::{nix_http, nixbase32};
 use serde::Deserialize;
 use std::io;
 use tokio_util::io::ReaderStream;
@@ -46,25 +46,21 @@ pub async fn get(
     }
 
     // parse the proto
-    let mut root_node: tvix_castore::proto::Node = Message::decode(Bytes::from(root_node_proto))
+    let root_node: tvix_castore::proto::Node = Message::decode(Bytes::from(root_node_proto))
         .map_err(|e| {
             warn!(err=%e, "unable to decode root node proto");
             StatusCode::NOT_FOUND
         })?;
 
-    // validate the node, but add a dummy node name, as we only send unnamed
-    // nodes
-    if let Some(rn) = root_node.node {
-        root_node.node = Some(rn.rename("00000000000000000000000000000000-dummy".into()))
-    }
+    let (root_name, root_node) = root_node.into_name_and_node().map_err(|e| {
+        warn!(err=%e, "root node validation failed");
+        StatusCode::BAD_REQUEST
+    })?;
 
-    let root_node = root_node
-        .validate()
-        .map_err(|e| {
-            warn!(err=%e, "root node validation failed");
-            StatusCode::BAD_REQUEST
-        })?
-        .to_owned();
+    if !root_name.as_ref().is_empty() {
+        warn!("root node has name, which it shouldn't");
+        return Err(StatusCode::BAD_REQUEST);
+    }
 
     let (w, r) = tokio::io::duplex(1024 * 8);
 
@@ -81,6 +77,7 @@ pub async fn get(
         .status(StatusCode::OK)
         .header("cache-control", "max-age=31536000, immutable")
         .header("content-length", nar_size)
+        .header("content-type", nix_http::MIME_TYPE_NAR)
         .body(Body::from_stream(ReaderStream::new(r)))
         .unwrap())
 }
@@ -96,7 +93,14 @@ pub async fn put(
     }): axum::extract::State<AppState>,
     request: axum::extract::Request,
 ) -> Result<&'static str, StatusCode> {
-    let nar_hash_expected = parse_nar_str(&nar_str)?;
+    let (nar_hash_expected, compression_suffix) =
+        nix_http::parse_nar_str(&nar_str).ok_or(StatusCode::UNAUTHORIZED)?;
+
+    // No paths with compression suffix are supported.
+    if !compression_suffix.is_empty() {
+        warn!(%compression_suffix, "invalid compression suffix requested");
+        return Err(StatusCode::UNAUTHORIZED);
+    }
 
     let s = request.into_body().into_data_stream();
 
@@ -137,54 +141,3 @@ pub async fn put(
 
 // FUTUREWORK: maybe head by narhash. Though not too critical, as we do
 // implement HEAD for .narinfo.
-
-/// Parses a `14cx20k6z4hq508kqi2lm79qfld5f9mf7kiafpqsjs3zlmycza0k.nar`
-/// string and returns the nixbase32-decoded digest.
-/// No compression is supported.
-fn parse_nar_str(s: &str) -> Result<[u8; 32], StatusCode> {
-    if !s.is_char_boundary(52) {
-        warn!("invalid string, no char boundary at 32");
-        return Err(StatusCode::NOT_FOUND);
-    }
-
-    Ok(match s.split_at(52) {
-        (hash_str, ".nar") => {
-            // we know this is 52 bytes
-            let hash_str_fixed: [u8; 52] = hash_str.as_bytes().try_into().unwrap();
-            nixbase32::decode_fixed(hash_str_fixed).map_err(|e| {
-                warn!(err=%e, "invalid digest");
-                StatusCode::NOT_FOUND
-            })?
-        }
-        _ => {
-            warn!("invalid string");
-            return Err(StatusCode::BAD_REQUEST);
-        }
-    })
-}
-
-#[cfg(test)]
-mod test {
-    use super::parse_nar_str;
-    use hex_literal::hex;
-
-    #[test]
-    fn success() {
-        assert_eq!(
-            hex!("13a8cf7ca57f68a9f1752acee36a72a55187d3a954443c112818926f26109d91"),
-            parse_nar_str("14cx20k6z4hq508kqi2lm79qfld5f9mf7kiafpqsjs3zlmycza0k.nar").unwrap()
-        )
-    }
-
-    #[test]
-    fn failure() {
-        assert!(
-            parse_nar_str("14cx20k6z4hq508kqi2lm79qfld5f9mf7kiafpqsjs3zlmycza0k.nar.x").is_err()
-        );
-        assert!(
-            parse_nar_str("14cx20k6z4hq508kqi2lm79qfld5f9mf7kiafpqsjs3zlmycza0k.nar.xz").is_err()
-        );
-        assert!(parse_nar_str("14cx20k6z4hq508kqi2lm79qfld5f9mf7kiafpqsjs3zlmycza0").is_err());
-        assert!(parse_nar_str("14cx20k6z4hq508kqi2lm79qfld5f9mf7kiafpqsjs3zlmycza0🦊.nar").is_err())
-    }
-}
diff --git a/tvix/nar-bridge/src/narinfo.rs b/tvix/nar-bridge/src/narinfo.rs
index 4706be64d5b4..fc90f0b86629 100644
--- a/tvix/nar-bridge/src/narinfo.rs
+++ b/tvix/nar-bridge/src/narinfo.rs
@@ -1,8 +1,9 @@
-use axum::http::StatusCode;
+use axum::{http::StatusCode, response::IntoResponse};
 use bytes::Bytes;
-use nix_compat::{narinfo::NarInfo, nixbase32};
+use nix_compat::{narinfo::NarInfo, nix_http, nixbase32};
+use prost::Message;
 use tracing::{instrument, warn, Span};
-use tvix_castore::proto::{self as castorepb, node::Node};
+use tvix_castore::proto::{self as castorepb};
 use tvix_store::proto::PathInfo;
 
 use crate::AppState;
@@ -16,8 +17,8 @@ pub async fn head(
     axum::extract::State(AppState {
         path_info_service, ..
     }): axum::extract::State<AppState>,
-) -> Result<&'static str, StatusCode> {
-    let digest = parse_narinfo_str(&narinfo_str)?;
+) -> Result<impl IntoResponse, StatusCode> {
+    let digest = nix_http::parse_narinfo_str(&narinfo_str).ok_or(StatusCode::NOT_FOUND)?;
     Span::current().record("path_info.digest", &narinfo_str[0..32]);
 
     if path_info_service
@@ -29,7 +30,7 @@ pub async fn head(
         })?
         .is_some()
     {
-        Ok("")
+        Ok(([("content-type", nix_http::MIME_TYPE_NARINFO)], ""))
     } else {
         warn!("PathInfo not found");
         Err(StatusCode::NOT_FOUND)
@@ -42,8 +43,8 @@ pub async fn get(
     axum::extract::State(AppState {
         path_info_service, ..
     }): axum::extract::State<AppState>,
-) -> Result<String, StatusCode> {
-    let digest = parse_narinfo_str(&narinfo_str)?;
+) -> Result<impl IntoResponse, StatusCode> {
+    let digest = nix_http::parse_narinfo_str(&narinfo_str).ok_or(StatusCode::NOT_FOUND)?;
     Span::current().record("path_info.digest", &narinfo_str[0..32]);
 
     // fetch the PathInfo
@@ -61,32 +62,36 @@ pub async fn get(
         StatusCode::INTERNAL_SERVER_ERROR
     })?;
 
-    let mut narinfo = path_info.to_narinfo(store_path).ok_or_else(|| {
+    let mut narinfo = path_info.to_narinfo(store_path.as_ref()).ok_or_else(|| {
         warn!(path_info=?path_info, "PathInfo contained no NAR data");
         StatusCode::INTERNAL_SERVER_ERROR
     })?;
 
     // encode the (unnamed) root node in the NAR url itself.
-    let root_node = path_info
+    // We strip the name from the proto node before sending it out.
+    // It's not needed to render the NAR, it'll make the URL shorter, and it
+    // will make caching these requests easier.
+    let (_, root_node) = path_info
         .node
         .as_ref()
-        .and_then(|n| n.node.as_ref())
-        .expect("root node must not be none")
-        .clone()
-        .rename("".into());
-
-    let mut buf = Vec::new();
-    Node::encode(&root_node, &mut buf);
+        .expect("invalid pathinfo")
+        .to_owned()
+        .into_name_and_node()
+        .expect("invalid pathinfo");
 
     let url = format!(
         "nar/tvix-castore/{}?narsize={}",
-        data_encoding::BASE64URL_NOPAD.encode(&buf),
+        data_encoding::BASE64URL_NOPAD
+            .encode(&castorepb::Node::from_name_and_node("".into(), root_node).encode_to_vec()),
         narinfo.nar_size,
     );
 
     narinfo.url = &url;
 
-    Ok(narinfo.to_string())
+    Ok((
+        [("content-type", nix_http::MIME_TYPE_NARINFO)],
+        narinfo.to_string(),
+    ))
 }
 
 #[instrument(skip(path_info_service, root_nodes, request))]
@@ -99,7 +104,7 @@ pub async fn put(
     }): axum::extract::State<AppState>,
     request: axum::extract::Request,
 ) -> Result<&'static str, StatusCode> {
-    let _narinfo_digest = parse_narinfo_str(&narinfo_str)?;
+    let _narinfo_digest = nix_http::parse_narinfo_str(&narinfo_str).ok_or(StatusCode::UNAUTHORIZED);
     Span::current().record("path_info.digest", &narinfo_str[0..32]);
 
     let narinfo_bytes: Bytes = axum::body::to_bytes(request.into_body(), NARINFO_LIMIT)
@@ -128,19 +133,18 @@ pub async fn put(
 
     // Lookup root node with peek, as we don't want to update the LRU list.
     // We need to be careful to not hold the RwLock across the await point.
-    let maybe_root_node = root_nodes
-        .read()
-        .peek(&narinfo.nar_hash)
-        .map(|v| v.to_owned());
+    let maybe_root_node: Option<tvix_castore::Node> =
+        root_nodes.read().peek(&narinfo.nar_hash).cloned();
 
     match maybe_root_node {
         Some(root_node) => {
             // Set the root node from the lookup.
             // We need to rename the node to the narinfo storepath basename, as
             // that's where it's stored in PathInfo.
-            pathinfo.node = Some(castorepb::Node {
-                node: Some(root_node.rename(narinfo.store_path.to_string().into())),
-            });
+            pathinfo.node = Some(castorepb::Node::from_name_and_node(
+                narinfo.store_path.to_string().into(),
+                root_node,
+            ));
 
             // Persist the PathInfo.
             path_info_service.put(pathinfo).await.map_err(|e| {
@@ -156,49 +160,3 @@ pub async fn put(
         }
     }
 }
-
-/// Parses a `3mzh8lvgbynm9daj7c82k2sfsfhrsfsy.narinfo` string and returns the
-/// nixbase32-decoded digest.
-fn parse_narinfo_str(s: &str) -> Result<[u8; 20], StatusCode> {
-    if !s.is_char_boundary(32) {
-        warn!("invalid string, no char boundary at 32");
-        return Err(StatusCode::NOT_FOUND);
-    }
-
-    Ok(match s.split_at(32) {
-        (hash_str, ".narinfo") => {
-            // we know this is 32 bytes
-            let hash_str_fixed: [u8; 32] = hash_str.as_bytes().try_into().unwrap();
-            nixbase32::decode_fixed(hash_str_fixed).map_err(|e| {
-                warn!(err=%e, "invalid digest");
-                StatusCode::NOT_FOUND
-            })?
-        }
-        _ => {
-            warn!("invalid string");
-            return Err(StatusCode::NOT_FOUND);
-        }
-    })
-}
-
-#[cfg(test)]
-mod test {
-    use super::parse_narinfo_str;
-    use hex_literal::hex;
-
-    #[test]
-    fn success() {
-        assert_eq!(
-            hex!("8a12321522fd91efbd60ebb2481af88580f61600"),
-            parse_narinfo_str("00bgd045z0d4icpbc2yyz4gx48ak44la.narinfo").unwrap()
-        );
-    }
-
-    #[test]
-    fn failure() {
-        assert!(parse_narinfo_str("00bgd045z0d4icpbc2yyz4gx48ak44la").is_err());
-        assert!(parse_narinfo_str("/00bgd045z0d4icpbc2yyz4gx48ak44la").is_err());
-        assert!(parse_narinfo_str("000000").is_err());
-        assert!(parse_narinfo_str("00bgd045z0d4icpbc2yyz4gx48ak44l🦊.narinfo").is_err());
-    }
-}
diff --git a/tvix/nix-compat-derive-tests/Cargo.toml b/tvix/nix-compat-derive-tests/Cargo.toml
new file mode 100644
index 000000000000..e69cb10e4fea
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+name = "nix-compat-derive-tests"
+version = "0.1.0"
+edition = "2021"
+
+[features]
+compile-tests = []
+
+[dev-dependencies]
+hex-literal = { workspace = true }
+pretty_assertions = { workspace = true }
+rstest = { workspace = true }
+tokio-test = { workspace = true }
+trybuild = { workspace = true }
+tokio = { workspace = true, features = ["io-util", "macros"] }
+
+[dev-dependencies.nix-compat]
+version = "0.1.0"
+path = "../nix-compat"
+features = ["test", "wire"]
+
+[dev-dependencies.nix-compat-derive]
+version = "0.1.0"
+path = "../nix-compat-derive"
diff --git a/tvix/nix-compat-derive-tests/default.nix b/tvix/nix-compat-derive-tests/default.nix
new file mode 100644
index 000000000000..cabe9ad13780
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/default.nix
@@ -0,0 +1,5 @@
+{ depot, ... }:
+
+depot.tvix.crates.workspaceMembers.nix-compat-derive-tests.build.override {
+  runTests = true;
+}
diff --git a/tvix/nix-compat-derive-tests/tests/read_derive.rs b/tvix/nix-compat-derive-tests/tests/read_derive.rs
new file mode 100644
index 000000000000..055d70cf046e
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/read_derive.rs
@@ -0,0 +1,417 @@
+use std::str::FromStr;
+
+use nix_compat::nix_daemon::de::mock::{Builder, Error};
+use nix_compat::nix_daemon::de::NixRead;
+use nix_compat_derive::NixDeserialize;
+
+#[derive(Debug, PartialEq, Eq, NixDeserialize)]
+pub struct UnitTest;
+
+#[derive(Debug, PartialEq, Eq, NixDeserialize)]
+pub struct EmptyTupleTest();
+
+#[derive(Debug, PartialEq, Eq, NixDeserialize)]
+pub struct StructTest {
+    first: u64,
+    second: String,
+}
+
+#[derive(Debug, PartialEq, Eq, NixDeserialize)]
+pub struct TupleTest(u64, String);
+
+#[derive(Debug, PartialEq, Eq, NixDeserialize)]
+pub struct StructVersionTest {
+    test: u64,
+    #[nix(version = "20..")]
+    hello: String,
+}
+
+fn default_test() -> StructVersionTest {
+    StructVersionTest {
+        test: 89,
+        hello: String::from("klomp"),
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, NixDeserialize)]
+pub struct TupleVersionTest(u64, #[nix(version = "25..")] String);
+
+#[derive(Debug, PartialEq, Eq, NixDeserialize)]
+pub struct TupleVersionDefaultTest(
+    u64,
+    #[nix(version = "..25", default = "default_test")] StructVersionTest,
+);
+
+#[tokio::test]
+async fn read_unit() {
+    let mut mock = Builder::new().build();
+    let v: UnitTest = mock.read_value().await.unwrap();
+    assert_eq!(UnitTest, v);
+}
+
+#[tokio::test]
+async fn read_empty_tuple() {
+    let mut mock = Builder::new().build();
+    let v: EmptyTupleTest = mock.read_value().await.unwrap();
+    assert_eq!(EmptyTupleTest(), v);
+}
+
+#[tokio::test]
+async fn read_struct() {
+    let mut mock = Builder::new().read_number(89).read_slice(b"klomp").build();
+    let v: StructTest = mock.read_value().await.unwrap();
+    assert_eq!(
+        StructTest {
+            first: 89,
+            second: String::from("klomp"),
+        },
+        v
+    );
+}
+
+#[tokio::test]
+async fn read_tuple() {
+    let mut mock = Builder::new().read_number(89).read_slice(b"klomp").build();
+    let v: TupleTest = mock.read_value().await.unwrap();
+    assert_eq!(TupleTest(89, String::from("klomp")), v);
+}
+
+#[tokio::test]
+async fn read_struct_version() {
+    let mut mock = Builder::new()
+        .version((1, 20))
+        .read_number(89)
+        .read_slice(b"klomp")
+        .build();
+    let v: StructVersionTest = mock.read_value().await.unwrap();
+    assert_eq!(default_test(), v);
+}
+
+#[tokio::test]
+async fn read_struct_without_version() {
+    let mut mock = Builder::new().version((1, 19)).read_number(89).build();
+    let v: StructVersionTest = mock.read_value().await.unwrap();
+    assert_eq!(
+        StructVersionTest {
+            test: 89,
+            hello: String::new(),
+        },
+        v
+    );
+}
+
+#[tokio::test]
+async fn read_tuple_version() {
+    let mut mock = Builder::new()
+        .version((1, 26))
+        .read_number(89)
+        .read_slice(b"klomp")
+        .build();
+    let v: TupleVersionTest = mock.read_value().await.unwrap();
+    assert_eq!(TupleVersionTest(89, "klomp".into()), v);
+}
+
+#[tokio::test]
+async fn read_tuple_without_version() {
+    let mut mock = Builder::new().version((1, 19)).read_number(89).build();
+    let v: TupleVersionTest = mock.read_value().await.unwrap();
+    assert_eq!(TupleVersionTest(89, String::new()), v);
+}
+
+#[tokio::test]
+async fn read_complex_1() {
+    let mut mock = Builder::new()
+        .version((1, 19))
+        .read_number(999)
+        .read_number(666)
+        .build();
+    let v: TupleVersionDefaultTest = mock.read_value().await.unwrap();
+    assert_eq!(
+        TupleVersionDefaultTest(
+            999,
+            StructVersionTest {
+                test: 666,
+                hello: String::new()
+            }
+        ),
+        v
+    );
+}
+
+#[tokio::test]
+async fn read_complex_2() {
+    let mut mock = Builder::new()
+        .version((1, 20))
+        .read_number(999)
+        .read_number(666)
+        .read_slice(b"The quick brown \xF0\x9F\xA6\x8A jumps over 13 lazy \xF0\x9F\x90\xB6.")
+        .build();
+    let v: TupleVersionDefaultTest = mock.read_value().await.unwrap();
+    assert_eq!(
+        TupleVersionDefaultTest(
+            999,
+            StructVersionTest {
+                test: 666,
+                hello: String::from("The quick brown 🦊 jumps over 13 lazy 🐶.")
+            }
+        ),
+        v
+    );
+}
+
+#[tokio::test]
+async fn read_complex_3() {
+    let mut mock = Builder::new().version((1, 25)).read_number(999).build();
+    let v: TupleVersionDefaultTest = mock.read_value().await.unwrap();
+    assert_eq!(
+        TupleVersionDefaultTest(
+            999,
+            StructVersionTest {
+                test: 89,
+                hello: String::from("klomp")
+            }
+        ),
+        v
+    );
+}
+
+#[tokio::test]
+async fn read_complex_4() {
+    let mut mock = Builder::new().version((1, 26)).read_number(999).build();
+    let v: TupleVersionDefaultTest = mock.read_value().await.unwrap();
+    assert_eq!(
+        TupleVersionDefaultTest(
+            999,
+            StructVersionTest {
+                test: 89,
+                hello: String::from("klomp")
+            }
+        ),
+        v
+    );
+}
+
+#[tokio::test]
+async fn read_field_invalid_data() {
+    let mut mock = Builder::new()
+        .read_number(666)
+        .read_slice(b"The quick brown \xED\xA0\x80 jumped.")
+        .build();
+    let err = mock.read_value::<StructTest>().await.unwrap_err();
+    assert_eq!(
+        Error::InvalidData("invalid utf-8 sequence of 1 bytes from index 16".into()),
+        err
+    );
+}
+
+#[tokio::test]
+async fn read_field_missing_data() {
+    let mut mock = Builder::new().read_number(666).build();
+    let err = mock.read_value::<StructTest>().await.unwrap_err();
+    assert_eq!(Error::MissingData("unexpected end-of-file".into()), err);
+}
+
+#[tokio::test]
+async fn read_field_no_data() {
+    let mut mock = Builder::new().build();
+    let err = mock.read_value::<StructTest>().await.unwrap_err();
+    assert_eq!(Error::MissingData("unexpected end-of-file".into()), err);
+}
+
+#[tokio::test]
+async fn read_field_reader_error_first() {
+    let mut mock = Builder::new()
+        .read_number_error(Error::InvalidData("Bad reader".into()))
+        .build();
+    let err = mock.read_value::<StructTest>().await.unwrap_err();
+    assert_eq!(Error::InvalidData("Bad reader".into()), err);
+}
+
+#[tokio::test]
+async fn read_field_reader_error_later() {
+    let mut mock = Builder::new()
+        .read_number(999)
+        .read_bytes_error(Error::InvalidData("Bad reader".into()))
+        .build();
+    let err = mock.read_value::<StructTest>().await.unwrap_err();
+    assert_eq!(Error::InvalidData("Bad reader".into()), err);
+}
+
+#[derive(Debug, PartialEq, Eq, NixDeserialize)]
+#[nix(from_str)]
+struct TestFromStr;
+
+impl FromStr for TestFromStr {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        if s == "test" {
+            Ok(TestFromStr)
+        } else {
+            Err(s.into())
+        }
+    }
+}
+
+#[tokio::test]
+async fn read_from_str() {
+    let mut mock = Builder::new().read_slice(b"test").build();
+    let value = mock.read_value::<TestFromStr>().await.unwrap();
+    assert_eq!(TestFromStr, value);
+}
+
+#[tokio::test]
+async fn read_from_str_invalid_data() {
+    let mut mock = Builder::new().read_slice(b"wrong string").build();
+    let err = mock.read_value::<TestFromStr>().await.unwrap_err();
+    assert_eq!(Error::InvalidData("wrong string".into()), err);
+}
+
+#[tokio::test]
+async fn read_from_str_invalid_string() {
+    let mut mock = Builder::new()
+        .read_slice(b"The quick brown \xED\xA0\x80 jumped.")
+        .build();
+    let err = mock.read_value::<TestFromStr>().await.unwrap_err();
+    assert_eq!(
+        Error::InvalidData("invalid utf-8 sequence of 1 bytes from index 16".into()),
+        err
+    );
+}
+
+#[tokio::test]
+async fn read_from_str_reader_error() {
+    let mut mock = Builder::new()
+        .read_bytes_error(Error::InvalidData("Bad reader".into()))
+        .build();
+    let err = mock.read_value::<TestFromStr>().await.unwrap_err();
+    assert_eq!(Error::InvalidData("Bad reader".into()), err);
+}
+
+#[derive(Debug, PartialEq, Eq, NixDeserialize)]
+#[nix(try_from = "u64")]
+struct TestTryFromU64;
+
+impl TryFrom<u64> for TestTryFromU64 {
+    type Error = u64;
+
+    fn try_from(value: u64) -> Result<TestTryFromU64, Self::Error> {
+        if value == 42 {
+            Ok(TestTryFromU64)
+        } else {
+            Err(value)
+        }
+    }
+}
+
+#[tokio::test]
+async fn read_try_from_u64() {
+    let mut mock = Builder::new().read_number(42).build();
+    let value = mock.read_value::<TestTryFromU64>().await.unwrap();
+    assert_eq!(TestTryFromU64, value);
+}
+
+#[tokio::test]
+async fn read_try_from_u64_invalid_data() {
+    let mut mock = Builder::new().read_number(666).build();
+    let err = mock.read_value::<TestTryFromU64>().await.unwrap_err();
+    assert_eq!(Error::InvalidData("666".into()), err);
+}
+
+#[tokio::test]
+async fn read_try_from_u64_reader_error() {
+    let mut mock = Builder::new()
+        .read_number_error(Error::InvalidData("Bad reader".into()))
+        .build();
+    let err = mock.read_value::<TestTryFromU64>().await.unwrap_err();
+    assert_eq!(Error::InvalidData("Bad reader".into()), err);
+}
+
+#[derive(Debug, PartialEq, Eq, NixDeserialize)]
+#[nix(from = "u64")]
+struct TestFromU64;
+
+impl From<u64> for TestFromU64 {
+    fn from(_value: u64) -> TestFromU64 {
+        TestFromU64
+    }
+}
+
+#[tokio::test]
+async fn read_from_u64() {
+    let mut mock = Builder::new().read_number(42).build();
+    let value = mock.read_value::<TestFromU64>().await.unwrap();
+    assert_eq!(TestFromU64, value);
+}
+
+#[tokio::test]
+async fn read_from_u64_reader_error() {
+    let mut mock = Builder::new()
+        .read_number_error(Error::InvalidData("Bad reader".into()))
+        .build();
+    let err = mock.read_value::<TestFromU64>().await.unwrap_err();
+    assert_eq!(Error::InvalidData("Bad reader".into()), err);
+}
+
+#[derive(Debug, PartialEq, Eq, NixDeserialize)]
+enum TestEnum {
+    #[nix(version = "..=19")]
+    Pre20(TestTryFromU64),
+    #[nix(version = "20..")]
+    Post20(StructVersionTest),
+}
+
+#[tokio::test]
+async fn read_enum_19() {
+    let mut mock = Builder::new().version((1, 19)).read_number(42).build();
+    let value = mock.read_value::<TestEnum>().await.unwrap();
+    assert_eq!(TestEnum::Pre20(TestTryFromU64), value);
+}
+
+#[tokio::test]
+async fn read_enum_20() {
+    let mut mock = Builder::new()
+        .version((1, 20))
+        .read_number(42)
+        .read_slice(b"klomp")
+        .build();
+    let value = mock.read_value::<TestEnum>().await.unwrap();
+    assert_eq!(
+        TestEnum::Post20(StructVersionTest {
+            test: 42,
+            hello: "klomp".into(),
+        }),
+        value
+    );
+}
+
+#[tokio::test]
+async fn read_enum_reader_error() {
+    let mut mock = Builder::new()
+        .version((1, 19))
+        .read_number_error(Error::InvalidData("Bad reader".into()))
+        .build();
+    let err = mock.read_value::<TestEnum>().await.unwrap_err();
+    assert_eq!(Error::InvalidData("Bad reader".into()), err);
+}
+
+#[tokio::test]
+async fn read_enum_invalid_data_19() {
+    let mut mock = Builder::new().version((1, 19)).read_number(666).build();
+    let err = mock.read_value::<TestEnum>().await.unwrap_err();
+    assert_eq!(Error::InvalidData("666".into()), err);
+}
+
+#[tokio::test]
+async fn read_enum_invalid_data_20() {
+    let mut mock = Builder::new()
+        .version((1, 20))
+        .read_number(666)
+        .read_slice(b"The quick brown \xED\xA0\x80 jumped.")
+        .build();
+    let err = mock.read_value::<TestEnum>().await.unwrap_err();
+    assert_eq!(
+        Error::InvalidData("invalid utf-8 sequence of 1 bytes from index 16".into()),
+        err
+    );
+}
diff --git a/tvix/nix-compat-derive-tests/tests/ui.rs b/tvix/nix-compat-derive-tests/tests/ui.rs
new file mode 100644
index 000000000000..6a7bffeaf832
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui.rs
@@ -0,0 +1,6 @@
+#[cfg(feature = "compile-tests")]
+#[test]
+fn ui() {
+    let t = trybuild::TestCases::new();
+    t.compile_fail("tests/ui/*.rs");
+}
diff --git a/tvix/nix-compat-derive-tests/tests/ui/deserialize_bad_type.rs b/tvix/nix-compat-derive-tests/tests/ui/deserialize_bad_type.rs
new file mode 100644
index 000000000000..f77469679999
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/deserialize_bad_type.rs
@@ -0,0 +1,10 @@
+use nix_compat_derive::NixDeserialize;
+
+pub struct BadType;
+
+#[derive(NixDeserialize)]
+pub struct Test {
+    version: BadType,
+}
+
+fn main() {}
diff --git a/tvix/nix-compat-derive-tests/tests/ui/deserialize_bad_type.stderr b/tvix/nix-compat-derive-tests/tests/ui/deserialize_bad_type.stderr
new file mode 100644
index 000000000000..12ffdc83c726
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/deserialize_bad_type.stderr
@@ -0,0 +1,21 @@
+error[E0277]: the trait bound `BadType: NixDeserialize` is not satisfied
+ --> tests/ui/deserialize_bad_type.rs:7:14
+  |
+7 |     version: BadType,
+  |              ^^^^^^^ the trait `NixDeserialize` is not implemented for `BadType`
+  |
+  = help: the following other types implement trait `NixDeserialize`:
+            BTreeMap<K, V>
+            String
+            Test
+            Vec<T>
+            bool
+            bytes::bytes::Bytes
+            i64
+            u64
+            usize
+note: required by a bound in `try_read_value`
+ --> $WORKSPACE/nix-compat/src/nix_daemon/de/mod.rs
+  |
+  |     fn try_read_value<V: NixDeserialize>(
+  |                          ^^^^^^^^^^^^^^ required by this bound in `NixRead::try_read_value`
diff --git a/tvix/nix-compat-derive-tests/tests/ui/deserialize_enum_non_exaustive.rs b/tvix/nix-compat-derive-tests/tests/ui/deserialize_enum_non_exaustive.rs
new file mode 100644
index 000000000000..ab559f2b81c8
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/deserialize_enum_non_exaustive.rs
@@ -0,0 +1,13 @@
+use nix_compat_derive::NixDeserialize;
+
+#[derive(NixDeserialize)]
+pub enum Test {
+    #[nix(version = "..=10")]
+    Old,
+    #[nix(version = "15..=17")]
+    Legacy,
+    #[nix(version = "50..")]
+    NewWay,
+}
+
+fn main() {}
diff --git a/tvix/nix-compat-derive-tests/tests/ui/deserialize_enum_non_exaustive.stderr b/tvix/nix-compat-derive-tests/tests/ui/deserialize_enum_non_exaustive.stderr
new file mode 100644
index 000000000000..8a46d9439e35
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/deserialize_enum_non_exaustive.stderr
@@ -0,0 +1,8 @@
+error[E0004]: non-exhaustive patterns: `11_u8..=14_u8` and `18_u8..=49_u8` not covered
+ --> tests/ui/deserialize_enum_non_exaustive.rs:3:10
+  |
+3 | #[derive(NixDeserialize)]
+  |          ^^^^^^^^^^^^^^ patterns `11_u8..=14_u8` and `18_u8..=49_u8` not covered
+  |
+  = note: the matched value is of type `u8`
+  = note: this error originates in the derive macro `NixDeserialize` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tvix/nix-compat-derive-tests/tests/ui/deserialize_from_missing.rs b/tvix/nix-compat-derive-tests/tests/ui/deserialize_from_missing.rs
new file mode 100644
index 000000000000..913b7c4f7e59
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/deserialize_from_missing.rs
@@ -0,0 +1,7 @@
+use nix_compat_derive::NixDeserialize;
+
+#[derive(NixDeserialize)]
+#[nix(from = "u64")]
+pub struct Test;
+
+fn main() {}
diff --git a/tvix/nix-compat-derive-tests/tests/ui/deserialize_from_missing.stderr b/tvix/nix-compat-derive-tests/tests/ui/deserialize_from_missing.stderr
new file mode 100644
index 000000000000..0124010cf10c
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/deserialize_from_missing.stderr
@@ -0,0 +1,5 @@
+error[E0277]: the trait bound `Test: From<u64>` is not satisfied
+ --> tests/ui/deserialize_from_missing.rs:4:14
+  |
+4 | #[nix(from = "u64")]
+  |              ^^^^^ the trait `From<u64>` is not implemented for `Test`
diff --git a/tvix/nix-compat-derive-tests/tests/ui/deserialize_from_str_error_not_display.rs b/tvix/nix-compat-derive-tests/tests/ui/deserialize_from_str_error_not_display.rs
new file mode 100644
index 000000000000..36cd4b153740
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/deserialize_from_str_error_not_display.rs
@@ -0,0 +1,20 @@
+use std::str::FromStr;
+
+use nix_compat_derive::NixDeserialize;
+
+#[derive(NixDeserialize)]
+#[nix(from_str)]
+pub struct Test;
+
+impl FromStr for Test {
+    type Err = ();
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        if s == "test" {
+            Ok(Test)
+        } else {
+            Err(())
+        }
+    }
+}
+
+fn main() {}
diff --git a/tvix/nix-compat-derive-tests/tests/ui/deserialize_from_str_error_not_display.stderr b/tvix/nix-compat-derive-tests/tests/ui/deserialize_from_str_error_not_display.stderr
new file mode 100644
index 000000000000..8283ed5340f3
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/deserialize_from_str_error_not_display.stderr
@@ -0,0 +1,13 @@
+error[E0277]: `()` doesn't implement `std::fmt::Display`
+ --> tests/ui/deserialize_from_str_error_not_display.rs:6:7
+  |
+6 | #[nix(from_str)]
+  |       ^^^^^^^^ `()` cannot be formatted with the default formatter
+  |
+  = help: the trait `std::fmt::Display` is not implemented for `()`
+  = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
+note: required by a bound in `invalid_data`
+ --> $WORKSPACE/nix-compat/src/nix_daemon/de/mod.rs
+  |
+  |     fn invalid_data<T: fmt::Display>(msg: T) -> Self {
+  |                        ^^^^^^^^^^^^ required by this bound in `Error::invalid_data`
diff --git a/tvix/nix-compat-derive-tests/tests/ui/deserialize_from_str_missing.rs b/tvix/nix-compat-derive-tests/tests/ui/deserialize_from_str_missing.rs
new file mode 100644
index 000000000000..a959db57e640
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/deserialize_from_str_missing.rs
@@ -0,0 +1,7 @@
+use nix_compat_derive::NixDeserialize;
+
+#[derive(NixDeserialize)]
+#[nix(from_str)]
+pub struct Test;
+
+fn main() {}
diff --git a/tvix/nix-compat-derive-tests/tests/ui/deserialize_from_str_missing.stderr b/tvix/nix-compat-derive-tests/tests/ui/deserialize_from_str_missing.stderr
new file mode 100644
index 000000000000..f68f588011fc
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/deserialize_from_str_missing.stderr
@@ -0,0 +1,16 @@
+error[E0277]: the trait bound `Test: FromStr` is not satisfied
+ --> tests/ui/deserialize_from_str_missing.rs:4:7
+  |
+4 | #[nix(from_str)]
+  |       ^^^^^^^^ the trait `FromStr` is not implemented for `Test`
+  |
+  = help: the following other types implement trait `FromStr`:
+            IpAddr
+            Ipv4Addr
+            Ipv6Addr
+            NonZero<i128>
+            NonZero<i16>
+            NonZero<i32>
+            NonZero<i64>
+            NonZero<i8>
+          and $N others
diff --git a/tvix/nix-compat-derive-tests/tests/ui/deserialize_missing_default.rs b/tvix/nix-compat-derive-tests/tests/ui/deserialize_missing_default.rs
new file mode 100644
index 000000000000..e9df62845518
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/deserialize_missing_default.rs
@@ -0,0 +1,12 @@
+use nix_compat_derive::NixDeserialize;
+
+#[derive(NixDeserialize)]
+pub struct Value(String);
+
+#[derive(NixDeserialize)]
+pub struct Test {
+    #[nix(version = "20..")]
+    version: Value,
+}
+
+fn main() {}
diff --git a/tvix/nix-compat-derive-tests/tests/ui/deserialize_missing_default.stderr b/tvix/nix-compat-derive-tests/tests/ui/deserialize_missing_default.stderr
new file mode 100644
index 000000000000..5cc2f5974e4c
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/deserialize_missing_default.stderr
@@ -0,0 +1,12 @@
+error[E0277]: the trait bound `Value: Default` is not satisfied
+ --> tests/ui/deserialize_missing_default.rs:6:10
+  |
+6 | #[derive(NixDeserialize)]
+  |          ^^^^^^^^^^^^^^ the trait `Default` is not implemented for `Value`
+  |
+  = note: this error originates in the derive macro `NixDeserialize` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider annotating `Value` with `#[derive(Default)]`
+  |
+4 + #[derive(Default)]
+5 | pub struct Value(String);
+  |
diff --git a/tvix/nix-compat-derive-tests/tests/ui/deserialize_missing_default_path.rs b/tvix/nix-compat-derive-tests/tests/ui/deserialize_missing_default_path.rs
new file mode 100644
index 000000000000..4f319c069dca
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/deserialize_missing_default_path.rs
@@ -0,0 +1,12 @@
+use nix_compat_derive::NixDeserialize;
+
+#[derive(NixDeserialize)]
+pub struct Value(String);
+
+#[derive(NixDeserialize)]
+pub struct Test {
+    #[nix(version = "20..", default = "Value::make_default")]
+    version: Value,
+}
+
+fn main() {}
diff --git a/tvix/nix-compat-derive-tests/tests/ui/deserialize_missing_default_path.stderr b/tvix/nix-compat-derive-tests/tests/ui/deserialize_missing_default_path.stderr
new file mode 100644
index 000000000000..bb9af749128d
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/deserialize_missing_default_path.stderr
@@ -0,0 +1,8 @@
+error[E0599]: no function or associated item named `make_default` found for struct `Value` in the current scope
+ --> tests/ui/deserialize_missing_default_path.rs:8:39
+  |
+4 | pub struct Value(String);
+  | ---------------- function or associated item `make_default` not found for this struct
+...
+8 |     #[nix(version = "20..", default = "Value::make_default")]
+  |                                       ^^^^^^^^^^^^^^^^^^^^^ function or associated item not found in `Value`
diff --git a/tvix/nix-compat-derive-tests/tests/ui/deserialize_remote_missing_attr.rs b/tvix/nix-compat-derive-tests/tests/ui/deserialize_remote_missing_attr.rs
new file mode 100644
index 000000000000..cc2ab5bfbc11
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/deserialize_remote_missing_attr.rs
@@ -0,0 +1,15 @@
+use nix_compat_derive::nix_deserialize_remote;
+
+pub struct Value(String);
+impl From<String> for Value {
+    fn from(s: String) -> Value {
+        Value(s)
+    }
+}
+
+nix_deserialize_remote!(
+    #[nix()]
+    Value
+);
+
+fn main() {}
diff --git a/tvix/nix-compat-derive-tests/tests/ui/deserialize_remote_missing_attr.stderr b/tvix/nix-compat-derive-tests/tests/ui/deserialize_remote_missing_attr.stderr
new file mode 100644
index 000000000000..a1c18adc6e48
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/deserialize_remote_missing_attr.stderr
@@ -0,0 +1,5 @@
+error: Missing from_str, from or try_from attribute
+  --> tests/ui/deserialize_remote_missing_attr.rs:10:25
+   |
+10 | nix_deserialize_remote!(#[nix()] Value);
+   |                         ^^^^^^^^^^^^^^
diff --git a/tvix/nix-compat-derive-tests/tests/ui/deserialize_try_from_error_not_display.rs b/tvix/nix-compat-derive-tests/tests/ui/deserialize_try_from_error_not_display.rs
new file mode 100644
index 000000000000..7f8ad6bbfc4e
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/deserialize_try_from_error_not_display.rs
@@ -0,0 +1,19 @@
+use nix_compat_derive::NixDeserialize;
+
+#[derive(NixDeserialize)]
+#[nix(try_from = "u64")]
+pub struct Test;
+
+impl TryFrom<u64> for Test {
+    type Error = ();
+
+    fn try_from(value: u64) -> Result<Test, Self::Error> {
+        if value == 42 {
+            Ok(Test)
+        } else {
+            Err(())
+        }
+    }
+}
+
+fn main() {}
diff --git a/tvix/nix-compat-derive-tests/tests/ui/deserialize_try_from_error_not_display.stderr b/tvix/nix-compat-derive-tests/tests/ui/deserialize_try_from_error_not_display.stderr
new file mode 100644
index 000000000000..8e55a3c56189
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/deserialize_try_from_error_not_display.stderr
@@ -0,0 +1,13 @@
+error[E0277]: `()` doesn't implement `std::fmt::Display`
+ --> tests/ui/deserialize_try_from_error_not_display.rs:4:18
+  |
+4 | #[nix(try_from = "u64")]
+  |                  ^^^^^ `()` cannot be formatted with the default formatter
+  |
+  = help: the trait `std::fmt::Display` is not implemented for `()`
+  = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
+note: required by a bound in `invalid_data`
+ --> $WORKSPACE/nix-compat/src/nix_daemon/de/mod.rs
+  |
+  |     fn invalid_data<T: fmt::Display>(msg: T) -> Self {
+  |                        ^^^^^^^^^^^^ required by this bound in `Error::invalid_data`
diff --git a/tvix/nix-compat-derive-tests/tests/ui/deserialize_try_from_missing.rs b/tvix/nix-compat-derive-tests/tests/ui/deserialize_try_from_missing.rs
new file mode 100644
index 000000000000..899095ae3542
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/deserialize_try_from_missing.rs
@@ -0,0 +1,7 @@
+use nix_compat_derive::NixDeserialize;
+
+#[derive(NixDeserialize)]
+#[nix(try_from = "u64")]
+pub struct Test;
+
+fn main() {}
diff --git a/tvix/nix-compat-derive-tests/tests/ui/deserialize_try_from_missing.stderr b/tvix/nix-compat-derive-tests/tests/ui/deserialize_try_from_missing.stderr
new file mode 100644
index 000000000000..9605d1f3378f
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/deserialize_try_from_missing.stderr
@@ -0,0 +1,8 @@
+error[E0277]: the trait bound `Test: From<u64>` is not satisfied
+ --> tests/ui/deserialize_try_from_missing.rs:4:18
+  |
+4 | #[nix(try_from = "u64")]
+  |                  ^^^^^ the trait `From<u64>` is not implemented for `Test`, which is required by `Test: TryFrom<u64>`
+  |
+  = note: required for `u64` to implement `Into<Test>`
+  = note: required for `Test` to implement `TryFrom<u64>`
diff --git a/tvix/nix-compat-derive-tests/tests/ui/parse_bad_default.rs b/tvix/nix-compat-derive-tests/tests/ui/parse_bad_default.rs
new file mode 100644
index 000000000000..d87831cecf51
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/parse_bad_default.rs
@@ -0,0 +1,9 @@
+use nix_compat_derive::NixDeserialize;
+
+#[derive(NixDeserialize)]
+pub struct Test {
+    #[nix(default = 12)]
+    version: u8,
+}
+
+fn main() {}
diff --git a/tvix/nix-compat-derive-tests/tests/ui/parse_bad_default.stderr b/tvix/nix-compat-derive-tests/tests/ui/parse_bad_default.stderr
new file mode 100644
index 000000000000..acb1bc2a47bc
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/parse_bad_default.stderr
@@ -0,0 +1,5 @@
+error: expected nix attribute default to be string
+ --> tests/ui/parse_bad_default.rs:5:21
+  |
+5 |     #[nix(default = 12)]
+  |                     ^^
diff --git a/tvix/nix-compat-derive-tests/tests/ui/parse_bad_default_path.rs b/tvix/nix-compat-derive-tests/tests/ui/parse_bad_default_path.rs
new file mode 100644
index 000000000000..fbde8ffbc2b0
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/parse_bad_default_path.rs
@@ -0,0 +1,9 @@
+use nix_compat_derive::NixDeserialize;
+
+#[derive(NixDeserialize)]
+pub struct Test {
+    #[nix(default = "12")]
+    version: u8,
+}
+
+fn main() {}
diff --git a/tvix/nix-compat-derive-tests/tests/ui/parse_bad_default_path.stderr b/tvix/nix-compat-derive-tests/tests/ui/parse_bad_default_path.stderr
new file mode 100644
index 000000000000..7628d4c83bea
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/parse_bad_default_path.stderr
@@ -0,0 +1,5 @@
+error: expected identifier
+ --> tests/ui/parse_bad_default_path.rs:5:21
+  |
+5 |     #[nix(default = "12")]
+  |                     ^^^^
diff --git a/tvix/nix-compat-derive-tests/tests/ui/parse_bad_nix.rs b/tvix/nix-compat-derive-tests/tests/ui/parse_bad_nix.rs
new file mode 100644
index 000000000000..690e76a20fe6
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/parse_bad_nix.rs
@@ -0,0 +1,9 @@
+use nix_compat_derive::NixDeserialize;
+
+#[derive(NixDeserialize)]
+pub struct Test {
+    #[nix]
+    version: u8,
+}
+
+fn main() {}
diff --git a/tvix/nix-compat-derive-tests/tests/ui/parse_bad_nix.stderr b/tvix/nix-compat-derive-tests/tests/ui/parse_bad_nix.stderr
new file mode 100644
index 000000000000..da3d2d9aab47
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/parse_bad_nix.stderr
@@ -0,0 +1,5 @@
+error: expected attribute arguments in parentheses: #[nix(...)]
+ --> tests/ui/parse_bad_nix.rs:5:7
+  |
+5 |     #[nix]
+  |       ^^^
diff --git a/tvix/nix-compat-derive-tests/tests/ui/parse_bad_version.rs b/tvix/nix-compat-derive-tests/tests/ui/parse_bad_version.rs
new file mode 100644
index 000000000000..35b3b05c23e1
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/parse_bad_version.rs
@@ -0,0 +1,9 @@
+use nix_compat_derive::NixDeserialize;
+
+#[derive(NixDeserialize)]
+pub struct Test {
+    #[nix(version = 12)]
+    version: u8,
+}
+
+fn main() {}
diff --git a/tvix/nix-compat-derive-tests/tests/ui/parse_bad_version.stderr b/tvix/nix-compat-derive-tests/tests/ui/parse_bad_version.stderr
new file mode 100644
index 000000000000..48cc817fac9d
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/parse_bad_version.stderr
@@ -0,0 +1,5 @@
+error: expected nix attribute version to be string
+ --> tests/ui/parse_bad_version.rs:5:21
+  |
+5 |     #[nix(version = 12)]
+  |                     ^^
diff --git a/tvix/nix-compat-derive-tests/tests/ui/parse_mising_version.rs b/tvix/nix-compat-derive-tests/tests/ui/parse_mising_version.rs
new file mode 100644
index 000000000000..9eaa743ed2b6
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/parse_mising_version.rs
@@ -0,0 +1,9 @@
+use nix_compat_derive::NixDeserialize;
+
+#[derive(NixDeserialize)]
+pub struct Test {
+    #[nix(version)]
+    version: u8,
+}
+
+fn main() {}
diff --git a/tvix/nix-compat-derive-tests/tests/ui/parse_mising_version.stderr b/tvix/nix-compat-derive-tests/tests/ui/parse_mising_version.stderr
new file mode 100644
index 000000000000..79f048e11198
--- /dev/null
+++ b/tvix/nix-compat-derive-tests/tests/ui/parse_mising_version.stderr
@@ -0,0 +1,5 @@
+error: expected `=`
+ --> tests/ui/parse_mising_version.rs:5:18
+  |
+5 |     #[nix(version)]
+  |                  ^
diff --git a/tvix/nix-compat-derive/Cargo.toml b/tvix/nix-compat-derive/Cargo.toml
new file mode 100644
index 000000000000..da6d6744e650
--- /dev/null
+++ b/tvix/nix-compat-derive/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+name = "nix-compat-derive"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+proc-macro2 = { workspace = true, features = ["proc-macro"] }
+quote = { workspace = true, features = ["proc-macro"] }
+syn = { version = "2.0.76", features = ["full", "extra-traits"] }
+
+[dev-dependencies]
+hex-literal = { workspace = true }
+pretty_assertions = { workspace = true }
+rstest = { workspace = true }
+tokio-test = { workspace = true }
+tokio = { workspace = true, features = ["io-util", "macros"] }
+
+[dev-dependencies.nix-compat]
+path = "../nix-compat"
+default-features = false
+features = ["async", "wire", "test"]
diff --git a/tvix/nix-compat-derive/default.nix b/tvix/nix-compat-derive/default.nix
new file mode 100644
index 000000000000..e6636e7f2510
--- /dev/null
+++ b/tvix/nix-compat-derive/default.nix
@@ -0,0 +1,5 @@
+{ depot, lib, ... }:
+
+depot.tvix.crates.workspaceMembers.nix-compat-derive.build.override {
+  runTests = true;
+}
diff --git a/tvix/nix-compat-derive/src/de.rs b/tvix/nix-compat-derive/src/de.rs
new file mode 100644
index 000000000000..ee79ea9d1012
--- /dev/null
+++ b/tvix/nix-compat-derive/src/de.rs
@@ -0,0 +1,272 @@
+use proc_macro2::{Span, TokenStream};
+use quote::{quote, quote_spanned, ToTokens};
+use syn::spanned::Spanned;
+use syn::{DeriveInput, Generics, Path, Type};
+
+use crate::internal::attrs::Default;
+use crate::internal::inputs::RemoteInput;
+use crate::internal::{attrs, Container, Context, Data, Field, Remote, Style, Variant};
+
+pub fn expand_nix_deserialize(nnixrs: Path, input: &mut DeriveInput) -> syn::Result<TokenStream> {
+    let cx = Context::new();
+    let cont = Container::from_ast(&cx, nnixrs, input);
+    cx.check()?;
+    let cont = cont.unwrap();
+
+    let ty = cont.ident_type();
+    let body = nix_deserialize_body(&cont);
+    let crate_path = cont.crate_path();
+
+    Ok(nix_deserialize_impl(
+        crate_path,
+        &ty,
+        &cont.original.generics,
+        body,
+    ))
+}
+
+pub fn expand_nix_deserialize_remote(
+    crate_path: Path,
+    input: &RemoteInput,
+) -> syn::Result<TokenStream> {
+    let cx = Context::new();
+    let remote = Remote::from_ast(&cx, crate_path, input);
+    cx.check()?;
+    let remote = remote.unwrap();
+
+    let crate_path = remote.crate_path();
+    let body = nix_deserialize_body_from(crate_path, &remote.attrs).expect("From tokenstream");
+    let generics = Generics::default();
+    Ok(nix_deserialize_impl(crate_path, remote.ty, &generics, body))
+}
+
+fn nix_deserialize_impl(
+    crate_path: &Path,
+    ty: &Type,
+    generics: &Generics,
+    body: TokenStream,
+) -> TokenStream {
+    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+    quote! {
+        #[automatically_derived]
+        impl #impl_generics #crate_path::nix_daemon::de::NixDeserialize for #ty #ty_generics
+            #where_clause
+        {
+            #[allow(clippy::manual_async_fn)]
+            fn try_deserialize<R>(reader: &mut R) -> impl ::std::future::Future<Output=Result<Option<Self>, R::Error>> + Send + '_
+                where R: ?Sized + #crate_path::nix_daemon::de::NixRead + Send,
+            {
+                #body
+            }
+        }
+    }
+}
+
+fn nix_deserialize_body_from(
+    crate_path: &syn::Path,
+    attrs: &attrs::Container,
+) -> Option<TokenStream> {
+    if let Some(span) = attrs.from_str.as_ref() {
+        Some(nix_deserialize_from_str(crate_path, span.span()))
+    } else if let Some(type_from) = attrs.type_from.as_ref() {
+        Some(nix_deserialize_from(type_from))
+    } else {
+        attrs
+            .type_try_from
+            .as_ref()
+            .map(|type_try_from| nix_deserialize_try_from(crate_path, type_try_from))
+    }
+}
+
+fn nix_deserialize_body(cont: &Container) -> TokenStream {
+    if let Some(tokens) = nix_deserialize_body_from(cont.crate_path(), &cont.attrs) {
+        tokens
+    } else {
+        match &cont.data {
+            Data::Struct(style, fields) => nix_deserialize_struct(*style, fields),
+            Data::Enum(variants) => nix_deserialize_enum(variants),
+        }
+    }
+}
+
+fn nix_deserialize_struct(style: Style, fields: &[Field<'_>]) -> TokenStream {
+    let read_fields = fields.iter().map(|f| {
+        let field = f.var_ident();
+        let ty = f.ty;
+        let read_value = quote_spanned! {
+            ty.span()=> if first__ {
+                first__ = false;
+                if let Some(v) = reader.try_read_value::<#ty>().await? {
+                    v
+                } else {
+                    return Ok(None);
+                }
+            } else {
+                reader.read_value::<#ty>().await?
+            }
+        };
+        if let Some(version) = f.attrs.version.as_ref() {
+            let default = match &f.attrs.default {
+                Default::Default => quote_spanned!(ty.span()=>::std::default::Default::default),
+                Default::Path(path) => path.to_token_stream(),
+                _ => panic!("No default for versioned field"),
+            };
+            quote! {
+                let #field : #ty = if (#version).contains(&reader.version().minor()) {
+                    #read_value
+                } else {
+                    #default()
+                };
+            }
+        } else {
+            quote! {
+                let #field : #ty = #read_value;
+            }
+        }
+    });
+
+    let field_names = fields.iter().map(|f| f.var_ident());
+    let construct = match style {
+        Style::Struct => {
+            quote! {
+                Self { #(#field_names),* }
+            }
+        }
+        Style::Tuple => {
+            quote! {
+                Self(#(#field_names),*)
+            }
+        }
+        Style::Unit => quote!(Self),
+    };
+    quote! {
+        #[allow(unused_assignments)]
+        async move {
+            let mut first__ = true;
+            #(#read_fields)*
+            Ok(Some(#construct))
+        }
+    }
+}
+
+fn nix_deserialize_variant(variant: &Variant<'_>) -> TokenStream {
+    let ident = variant.ident;
+    let read_fields = variant.fields.iter().map(|f| {
+        let field = f.var_ident();
+        let ty = f.ty;
+        let read_value = quote_spanned! {
+            ty.span()=> if first__ {
+                first__ = false;
+                if let Some(v) = reader.try_read_value::<#ty>().await? {
+                    v
+                } else {
+                    return Ok(None);
+                }
+            } else {
+                reader.read_value::<#ty>().await?
+            }
+        };
+        if let Some(version) = f.attrs.version.as_ref() {
+            let default = match &f.attrs.default {
+                Default::Default => quote_spanned!(ty.span()=>::std::default::Default::default),
+                Default::Path(path) => path.to_token_stream(),
+                _ => panic!("No default for versioned field"),
+            };
+            quote! {
+                let #field : #ty = if (#version).contains(&reader.version().minor()) {
+                    #read_value
+                } else {
+                    #default()
+                };
+            }
+        } else {
+            quote! {
+                let #field : #ty = #read_value;
+            }
+        }
+    });
+    let field_names = variant.fields.iter().map(|f| f.var_ident());
+    let construct = match variant.style {
+        Style::Struct => {
+            quote! {
+                Self::#ident { #(#field_names),* }
+            }
+        }
+        Style::Tuple => {
+            quote! {
+                Self::#ident(#(#field_names),*)
+            }
+        }
+        Style::Unit => quote!(Self::#ident),
+    };
+    let version = &variant.attrs.version;
+    quote! {
+        #version => {
+            #(#read_fields)*
+            Ok(Some(#construct))
+        }
+    }
+}
+
+fn nix_deserialize_enum(variants: &[Variant<'_>]) -> TokenStream {
+    let match_variant = variants
+        .iter()
+        .map(|variant| nix_deserialize_variant(variant));
+    quote! {
+        #[allow(unused_assignments)]
+        async move {
+            let mut first__ = true;
+            match reader.version().minor() {
+                #(#match_variant)*
+            }
+        }
+    }
+}
+
+fn nix_deserialize_from(ty: &Type) -> TokenStream {
+    quote_spanned! {
+        ty.span() =>
+        async move {
+            if let Some(value) = reader.try_read_value::<#ty>().await? {
+                Ok(Some(<Self as ::std::convert::From<#ty>>::from(value)))
+            } else {
+                Ok(None)
+            }
+        }
+    }
+}
+
+fn nix_deserialize_try_from(crate_path: &Path, ty: &Type) -> TokenStream {
+    quote_spanned! {
+        ty.span() =>
+        async move {
+            use #crate_path::nix_daemon::de::Error;
+            if let Some(item) = reader.try_read_value::<#ty>().await? {
+                <Self as ::std::convert::TryFrom<#ty>>::try_from(item)
+                    .map_err(Error::invalid_data)
+                    .map(Some)
+            } else {
+                Ok(None)
+            }
+        }
+    }
+}
+
+fn nix_deserialize_from_str(crate_path: &Path, span: Span) -> TokenStream {
+    quote_spanned! {
+        span =>
+        async move {
+            use #crate_path::nix_daemon::de::Error;
+            if let Some(buf) = reader.try_read_bytes().await? {
+                let s = ::std::str::from_utf8(&buf)
+                    .map_err(Error::invalid_data)?;
+                <Self as ::std::str::FromStr>::from_str(s)
+                    .map_err(Error::invalid_data)
+                    .map(Some)
+            } else {
+                Ok(None)
+            }
+        }
+    }
+}
diff --git a/tvix/nix-compat-derive/src/internal/attrs.rs b/tvix/nix-compat-derive/src/internal/attrs.rs
new file mode 100644
index 000000000000..dbc959d1e917
--- /dev/null
+++ b/tvix/nix-compat-derive/src/internal/attrs.rs
@@ -0,0 +1,358 @@
+use quote::ToTokens;
+use syn::meta::ParseNestedMeta;
+use syn::parse::Parse;
+use syn::{parse_quote, Attribute, Expr, ExprLit, ExprPath, Lit, Token};
+
+use super::symbol::{Symbol, CRATE, DEFAULT, FROM, FROM_STR, NIX, TRY_FROM, VERSION};
+use super::Context;
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum Default {
+    None,
+    #[allow(clippy::enum_variant_names)]
+    Default,
+    Path(ExprPath),
+}
+
+impl Default {
+    pub fn is_none(&self) -> bool {
+        matches!(self, Default::None)
+    }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct Field {
+    pub default: Default,
+    pub version: Option<syn::ExprRange>,
+}
+
+impl Field {
+    pub fn from_ast(ctx: &Context, attrs: &Vec<Attribute>) -> Field {
+        let mut version = None;
+        let mut default = Default::None;
+        for attr in attrs {
+            if attr.path() != NIX {
+                continue;
+            }
+            if let Err(err) = attr.parse_nested_meta(|meta| {
+                if meta.path == VERSION {
+                    version = parse_lit(ctx, &meta, VERSION)?;
+                } else if meta.path == DEFAULT {
+                    if meta.input.peek(Token![=]) {
+                        if let Some(path) = parse_lit(ctx, &meta, DEFAULT)? {
+                            default = Default::Path(path);
+                        }
+                    } else {
+                        default = Default::Default;
+                    }
+                } else {
+                    let path = meta.path.to_token_stream().to_string();
+                    return Err(meta.error(format_args!("unknown nix field attribute '{}'", path)));
+                }
+                Ok(())
+            }) {
+                eprintln!("{:?}", err.span().source_text());
+                ctx.syn_error(err);
+            }
+        }
+        if version.is_some() && default.is_none() {
+            default = Default::Default;
+        }
+
+        Field { default, version }
+    }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct Variant {
+    pub version: syn::ExprRange,
+}
+
+impl Variant {
+    pub fn from_ast(ctx: &Context, attrs: &Vec<Attribute>) -> Variant {
+        let mut version = parse_quote!(..);
+        for attr in attrs {
+            if attr.path() != NIX {
+                continue;
+            }
+            if let Err(err) = attr.parse_nested_meta(|meta| {
+                if meta.path == VERSION {
+                    if let Some(v) = parse_lit(ctx, &meta, VERSION)? {
+                        version = v;
+                    }
+                } else {
+                    let path = meta.path.to_token_stream().to_string();
+                    return Err(
+                        meta.error(format_args!("unknown nix variant attribute '{}'", path))
+                    );
+                }
+                Ok(())
+            }) {
+                eprintln!("{:?}", err.span().source_text());
+                ctx.syn_error(err);
+            }
+        }
+
+        Variant { version }
+    }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct Container {
+    pub from_str: Option<syn::Path>,
+    pub type_from: Option<syn::Type>,
+    pub type_try_from: Option<syn::Type>,
+    pub crate_path: Option<syn::Path>,
+}
+
+impl Container {
+    pub fn from_ast(ctx: &Context, attrs: &Vec<Attribute>) -> Container {
+        let mut type_from = None;
+        let mut type_try_from = None;
+        let mut crate_path = None;
+        let mut from_str = None;
+
+        for attr in attrs {
+            if attr.path() != NIX {
+                continue;
+            }
+            if let Err(err) = attr.parse_nested_meta(|meta| {
+                if meta.path == FROM {
+                    type_from = parse_lit(ctx, &meta, FROM)?;
+                } else if meta.path == TRY_FROM {
+                    type_try_from = parse_lit(ctx, &meta, TRY_FROM)?;
+                } else if meta.path == FROM_STR {
+                    from_str = Some(meta.path);
+                } else if meta.path == CRATE {
+                    crate_path = parse_lit(ctx, &meta, CRATE)?;
+                } else {
+                    let path = meta.path.to_token_stream().to_string();
+                    return Err(
+                        meta.error(format_args!("unknown nix variant attribute '{}'", path))
+                    );
+                }
+                Ok(())
+            }) {
+                eprintln!("{:?}", err.span().source_text());
+                ctx.syn_error(err);
+            }
+        }
+
+        Container {
+            from_str,
+            type_from,
+            type_try_from,
+            crate_path,
+        }
+    }
+}
+
+pub fn get_lit_str(
+    ctx: &Context,
+    meta: &ParseNestedMeta,
+    attr: Symbol,
+) -> syn::Result<Option<syn::LitStr>> {
+    let expr: Expr = meta.value()?.parse()?;
+    let mut value = &expr;
+    while let Expr::Group(e) = value {
+        value = &e.expr;
+    }
+    if let Expr::Lit(ExprLit {
+        lit: Lit::Str(s), ..
+    }) = value
+    {
+        Ok(Some(s.clone()))
+    } else {
+        ctx.error_spanned(
+            expr,
+            format_args!("expected nix attribute {} to be string", attr),
+        );
+        Ok(None)
+    }
+}
+
+pub fn parse_lit<T: Parse>(
+    ctx: &Context,
+    meta: &ParseNestedMeta,
+    attr: Symbol,
+) -> syn::Result<Option<T>> {
+    match get_lit_str(ctx, meta, attr)? {
+        Some(lit) => Ok(Some(lit.parse()?)),
+        None => Ok(None),
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use syn::{parse_quote, Attribute};
+
+    use crate::internal::Context;
+
+    use super::*;
+
+    #[test]
+    fn parse_field_version() {
+        let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(version="..34")])];
+        let ctx = Context::new();
+        let field = Field::from_ast(&ctx, &attrs);
+        ctx.check().unwrap();
+        assert_eq!(
+            field,
+            Field {
+                default: Default::Default,
+                version: Some(parse_quote!(..34)),
+            }
+        );
+    }
+
+    #[test]
+    fn parse_field_default() {
+        let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(default)])];
+        let ctx = Context::new();
+        let field = Field::from_ast(&ctx, &attrs);
+        ctx.check().unwrap();
+        assert_eq!(
+            field,
+            Field {
+                default: Default::Default,
+                version: None,
+            }
+        );
+    }
+
+    #[test]
+    fn parse_field_default_path() {
+        let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(default="Default::default")])];
+        let ctx = Context::new();
+        let field = Field::from_ast(&ctx, &attrs);
+        ctx.check().unwrap();
+        assert_eq!(
+            field,
+            Field {
+                default: Default::Path(parse_quote!(Default::default)),
+                version: None,
+            }
+        );
+    }
+
+    #[test]
+    fn parse_field_both() {
+        let attrs: Vec<Attribute> =
+            vec![parse_quote!(#[nix(version="..", default="Default::default")])];
+        let ctx = Context::new();
+        let field = Field::from_ast(&ctx, &attrs);
+        ctx.check().unwrap();
+        assert_eq!(
+            field,
+            Field {
+                default: Default::Path(parse_quote!(Default::default)),
+                version: Some(parse_quote!(..)),
+            }
+        );
+    }
+
+    #[test]
+    fn parse_field_both_rev() {
+        let attrs: Vec<Attribute> =
+            vec![parse_quote!(#[nix(default="Default::default", version="..")])];
+        let ctx = Context::new();
+        let field = Field::from_ast(&ctx, &attrs);
+        ctx.check().unwrap();
+        assert_eq!(
+            field,
+            Field {
+                default: Default::Path(parse_quote!(Default::default)),
+                version: Some(parse_quote!(..)),
+            }
+        );
+    }
+
+    #[test]
+    fn parse_field_no_attr() {
+        let attrs: Vec<Attribute> = vec![];
+        let ctx = Context::new();
+        let field = Field::from_ast(&ctx, &attrs);
+        ctx.check().unwrap();
+        assert_eq!(
+            field,
+            Field {
+                default: Default::None,
+                version: None,
+            }
+        );
+    }
+
+    #[test]
+    fn parse_field_no_subattrs() {
+        let attrs: Vec<Attribute> = vec![parse_quote!(#[nix()])];
+        let ctx = Context::new();
+        let field = Field::from_ast(&ctx, &attrs);
+        ctx.check().unwrap();
+        assert_eq!(
+            field,
+            Field {
+                default: Default::None,
+                version: None,
+            }
+        );
+    }
+
+    #[test]
+    fn parse_variant_version() {
+        let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(version="..34")])];
+        let ctx = Context::new();
+        let variant = Variant::from_ast(&ctx, &attrs);
+        ctx.check().unwrap();
+        assert_eq!(
+            variant,
+            Variant {
+                version: parse_quote!(..34),
+            }
+        );
+    }
+
+    #[test]
+    fn parse_variant_no_attr() {
+        let attrs: Vec<Attribute> = vec![];
+        let ctx = Context::new();
+        let variant = Variant::from_ast(&ctx, &attrs);
+        ctx.check().unwrap();
+        assert_eq!(
+            variant,
+            Variant {
+                version: parse_quote!(..),
+            }
+        );
+    }
+
+    #[test]
+    fn parse_variant_no_subattrs() {
+        let attrs: Vec<Attribute> = vec![parse_quote!(#[nix()])];
+        let ctx = Context::new();
+        let variant = Variant::from_ast(&ctx, &attrs);
+        ctx.check().unwrap();
+        assert_eq!(
+            variant,
+            Variant {
+                version: parse_quote!(..),
+            }
+        );
+    }
+
+    #[test]
+    fn parse_container_try_from() {
+        let attrs: Vec<Attribute> = vec![parse_quote!(#[nix(try_from="u64")])];
+        let ctx = Context::new();
+        let container = Container::from_ast(&ctx, &attrs);
+        ctx.check().unwrap();
+        assert_eq!(
+            container,
+            Container {
+                from_str: None,
+                type_from: None,
+                type_try_from: Some(parse_quote!(u64)),
+                crate_path: None,
+            }
+        );
+    }
+}
diff --git a/tvix/nix-compat-derive/src/internal/ctx.rs b/tvix/nix-compat-derive/src/internal/ctx.rs
new file mode 100644
index 000000000000..ba770e044bc2
--- /dev/null
+++ b/tvix/nix-compat-derive/src/internal/ctx.rs
@@ -0,0 +1,50 @@
+use std::cell::RefCell;
+use std::fmt;
+use std::thread::panicking;
+
+use quote::ToTokens;
+
+pub struct Context {
+    errors: RefCell<Option<Vec<syn::Error>>>,
+}
+
+impl Context {
+    pub fn new() -> Context {
+        Context {
+            errors: RefCell::new(Some(Vec::new())),
+        }
+    }
+
+    pub fn syn_error(&self, error: syn::Error) {
+        self.errors
+            .borrow_mut()
+            .as_mut()
+            .take()
+            .unwrap()
+            .push(error);
+    }
+
+    pub fn error_spanned<T: ToTokens, D: fmt::Display>(&self, tokens: T, message: D) {
+        self.syn_error(syn::Error::new_spanned(tokens, message));
+    }
+
+    pub fn check(&self) -> syn::Result<()> {
+        let mut iter = self.errors.borrow_mut().take().unwrap().into_iter();
+        let mut err = match iter.next() {
+            None => return Ok(()),
+            Some(err) => err,
+        };
+        for next_err in iter {
+            err.combine(next_err);
+        }
+        Err(err)
+    }
+}
+
+impl Drop for Context {
+    fn drop(&mut self) {
+        if self.errors.borrow().is_some() && !panicking() {
+            panic!("Context dropped without checking errors");
+        }
+    }
+}
diff --git a/tvix/nix-compat-derive/src/internal/inputs.rs b/tvix/nix-compat-derive/src/internal/inputs.rs
new file mode 100644
index 000000000000..097a141a5d7c
--- /dev/null
+++ b/tvix/nix-compat-derive/src/internal/inputs.rs
@@ -0,0 +1,110 @@
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct RemoteInput {
+    pub attrs: Vec<syn::Attribute>,
+    pub ident: syn::Type,
+}
+
+impl syn::parse::Parse for RemoteInput {
+    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
+        let attrs = input.call(syn::Attribute::parse_outer)?;
+
+        let ident = input.parse::<syn::Type>()?;
+        Ok(RemoteInput { attrs, ident })
+    }
+}
+
+impl quote::ToTokens for RemoteInput {
+    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+        fn is_outer(attr: &&syn::Attribute) -> bool {
+            match attr.style {
+                syn::AttrStyle::Outer => true,
+                syn::AttrStyle::Inner(_) => false,
+            }
+        }
+        for attr in self.attrs.iter().filter(is_outer) {
+            attr.to_tokens(tokens);
+        }
+        self.ident.to_tokens(tokens);
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use syn::parse_quote;
+    //use syn::parse::Parse;
+
+    use super::*;
+
+    #[test]
+    fn test_input() {
+        let p: RemoteInput = parse_quote!(u64);
+        assert_eq!(
+            p,
+            RemoteInput {
+                attrs: vec![],
+                ident: parse_quote!(u64),
+            }
+        );
+    }
+
+    #[test]
+    fn test_input_attr() {
+        let p: RemoteInput = parse_quote!(
+            #[nix]
+            u64
+        );
+        assert_eq!(
+            p,
+            RemoteInput {
+                attrs: vec![parse_quote!(#[nix])],
+                ident: parse_quote!(u64),
+            }
+        );
+    }
+
+    #[test]
+    fn test_input_attr_multiple() {
+        let p: RemoteInput = parse_quote!(
+            #[nix]
+            #[hello]
+            u64
+        );
+        assert_eq!(
+            p,
+            RemoteInput {
+                attrs: vec![parse_quote!(#[nix]), parse_quote!(#[hello])],
+                ident: parse_quote!(u64),
+            }
+        );
+    }
+
+    #[test]
+    fn test_input_attr_full() {
+        let p: RemoteInput = parse_quote!(
+            #[nix(try_from = "u64")]
+            usize
+        );
+        assert_eq!(
+            p,
+            RemoteInput {
+                attrs: vec![parse_quote!(#[nix(try_from="u64")])],
+                ident: parse_quote!(usize),
+            }
+        );
+    }
+
+    #[test]
+    fn test_input_attr_other() {
+        let p: RemoteInput = parse_quote!(
+            #[muh]
+            u64
+        );
+        assert_eq!(
+            p,
+            RemoteInput {
+                attrs: vec![parse_quote!(#[muh])],
+                ident: parse_quote!(u64),
+            }
+        );
+    }
+}
diff --git a/tvix/nix-compat-derive/src/internal/mod.rs b/tvix/nix-compat-derive/src/internal/mod.rs
new file mode 100644
index 000000000000..20b243221619
--- /dev/null
+++ b/tvix/nix-compat-derive/src/internal/mod.rs
@@ -0,0 +1,183 @@
+use syn::punctuated::Punctuated;
+use syn::spanned::Spanned;
+use syn::Token;
+
+pub mod attrs;
+mod ctx;
+pub mod inputs;
+mod symbol;
+
+pub use ctx::Context;
+
+pub struct Field<'a> {
+    pub member: syn::Member,
+    pub ty: &'a syn::Type,
+    pub attrs: attrs::Field,
+    pub original: &'a syn::Field,
+}
+
+impl<'a> Field<'a> {
+    pub fn from_ast(ctx: &Context, idx: usize, field: &'a syn::Field) -> Field<'a> {
+        let attrs = attrs::Field::from_ast(ctx, &field.attrs);
+        let member = match &field.ident {
+            Some(id) => syn::Member::Named(id.clone()),
+            None => syn::Member::Unnamed(idx.into()),
+        };
+        Field {
+            member,
+            attrs,
+            ty: &field.ty,
+            original: field,
+        }
+    }
+
+    pub fn var_ident(&self) -> syn::Ident {
+        match &self.member {
+            syn::Member::Named(name) => name.clone(),
+            syn::Member::Unnamed(idx) => {
+                syn::Ident::new(&format!("field{}", idx.index), self.original.span())
+            }
+        }
+    }
+}
+
+pub struct Variant<'a> {
+    pub ident: &'a syn::Ident,
+    pub attrs: attrs::Variant,
+    pub style: Style,
+    pub fields: Vec<Field<'a>>,
+    //pub original: &'a syn::Variant,
+}
+
+impl<'a> Variant<'a> {
+    pub fn from_ast(ctx: &Context, variant: &'a syn::Variant) -> Self {
+        let attrs = attrs::Variant::from_ast(ctx, &variant.attrs);
+        let (style, fields) = match &variant.fields {
+            syn::Fields::Named(fields) => (Style::Struct, fields_ast(ctx, &fields.named)),
+            syn::Fields::Unnamed(fields) => (Style::Tuple, fields_ast(ctx, &fields.unnamed)),
+            syn::Fields::Unit => (Style::Unit, Vec::new()),
+        };
+        Variant {
+            ident: &variant.ident,
+            attrs,
+            style,
+            fields,
+            //original: variant,
+        }
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
+pub enum Style {
+    Struct,
+    Tuple,
+    Unit,
+}
+
+pub enum Data<'a> {
+    Enum(Vec<Variant<'a>>),
+    Struct(Style, Vec<Field<'a>>),
+}
+
+pub struct Container<'a> {
+    pub ident: &'a syn::Ident,
+    pub attrs: attrs::Container,
+    pub data: Data<'a>,
+    pub crate_path: syn::Path,
+    pub original: &'a syn::DeriveInput,
+}
+
+impl<'a> Container<'a> {
+    pub fn from_ast(
+        ctx: &Context,
+        crate_path: syn::Path,
+        input: &'a mut syn::DeriveInput,
+    ) -> Option<Container<'a>> {
+        let attrs = attrs::Container::from_ast(ctx, &input.attrs);
+        let data = match &input.data {
+            syn::Data::Struct(s) => match &s.fields {
+                syn::Fields::Named(fields) => {
+                    Data::Struct(Style::Struct, fields_ast(ctx, &fields.named))
+                }
+                syn::Fields::Unnamed(fields) => {
+                    Data::Struct(Style::Tuple, fields_ast(ctx, &fields.unnamed))
+                }
+                syn::Fields::Unit => Data::Struct(Style::Unit, Vec::new()),
+            },
+            syn::Data::Enum(e) => {
+                let variants = e
+                    .variants
+                    .iter()
+                    .map(|variant| Variant::from_ast(ctx, variant))
+                    .collect();
+                Data::Enum(variants)
+            }
+            syn::Data::Union(u) => {
+                ctx.error_spanned(u.union_token, "Union not supported by nixrs");
+                return None;
+            }
+        };
+        Some(Container {
+            ident: &input.ident,
+            attrs,
+            data,
+            crate_path,
+            original: input,
+        })
+    }
+
+    pub fn crate_path(&self) -> &syn::Path {
+        if let Some(crate_path) = self.attrs.crate_path.as_ref() {
+            crate_path
+        } else {
+            &self.crate_path
+        }
+    }
+
+    pub fn ident_type(&self) -> syn::Type {
+        let path: syn::Path = self.ident.clone().into();
+        let tp = syn::TypePath { qself: None, path };
+        tp.into()
+    }
+}
+
+pub struct Remote<'a> {
+    pub attrs: attrs::Container,
+    pub ty: &'a syn::Type,
+    pub crate_path: syn::Path,
+}
+
+impl<'a> Remote<'a> {
+    pub fn from_ast(
+        ctx: &Context,
+        crate_path: syn::Path,
+        input: &'a inputs::RemoteInput,
+    ) -> Option<Remote<'a>> {
+        let attrs = attrs::Container::from_ast(ctx, &input.attrs);
+        if attrs.from_str.is_none() && attrs.type_from.is_none() && attrs.type_try_from.is_none() {
+            ctx.error_spanned(input, "Missing from_str, from or try_from attribute");
+            return None;
+        }
+        Some(Remote {
+            ty: &input.ident,
+            attrs,
+            crate_path,
+        })
+    }
+
+    pub fn crate_path(&self) -> &syn::Path {
+        if let Some(crate_path) = self.attrs.crate_path.as_ref() {
+            crate_path
+        } else {
+            &self.crate_path
+        }
+    }
+}
+
+fn fields_ast<'a>(ctx: &Context, fields: &'a Punctuated<syn::Field, Token![,]>) -> Vec<Field<'a>> {
+    fields
+        .iter()
+        .enumerate()
+        .map(|(idx, field)| Field::from_ast(ctx, idx, field))
+        .collect()
+}
diff --git a/tvix/nix-compat-derive/src/internal/symbol.rs b/tvix/nix-compat-derive/src/internal/symbol.rs
new file mode 100644
index 000000000000..ed3fe304eb5d
--- /dev/null
+++ b/tvix/nix-compat-derive/src/internal/symbol.rs
@@ -0,0 +1,32 @@
+use std::fmt;
+
+use syn::Path;
+
+#[derive(Copy, Clone)]
+pub struct Symbol(&'static str);
+
+pub const NIX: Symbol = Symbol("nix");
+pub const VERSION: Symbol = Symbol("version");
+pub const DEFAULT: Symbol = Symbol("default");
+pub const FROM: Symbol = Symbol("from");
+pub const TRY_FROM: Symbol = Symbol("try_from");
+pub const FROM_STR: Symbol = Symbol("from_str");
+pub const CRATE: Symbol = Symbol("crate");
+
+impl PartialEq<Symbol> for Path {
+    fn eq(&self, word: &Symbol) -> bool {
+        self.is_ident(word.0)
+    }
+}
+
+impl<'a> PartialEq<Symbol> for &'a Path {
+    fn eq(&self, word: &Symbol) -> bool {
+        self.is_ident(word.0)
+    }
+}
+
+impl fmt::Display for Symbol {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        formatter.write_str(self.0)
+    }
+}
diff --git a/tvix/nix-compat-derive/src/lib.rs b/tvix/nix-compat-derive/src/lib.rs
new file mode 100644
index 000000000000..e3faed02ebab
--- /dev/null
+++ b/tvix/nix-compat-derive/src/lib.rs
@@ -0,0 +1,303 @@
+//! # Using derive
+//!
+//! 1. [Overview](#overview)
+//! 3. [Attributes](#attributes)
+//!     1. [Container attributes](#container-attributes)
+//!         1. [`#[nix(from_str)]`](#nixfrom_str)
+//!         2. [`#[nix(from = "FromType")]`](#nixfrom--fromtype)
+//!         3. [`#[nix(try_from = "FromType")]`](#nixtry_from--fromtype)
+//!         4. [`#[nix(crate = "...")]`](#nixcrate--)
+//!     2. [Variant attributes](#variant-attributes)
+//!         1. [`#[nix(version = "range")]`](#nixversion--range)
+//!     3. [Field attributes](#field-attributes)
+//!         1. [`#[nix(version = "range")]`](#nixversion--range-1)
+//!         2. [`#[nix(default)]`](#nixdefault)
+//!         3. [`#[nix(default = "path")]`](#nixdefault--path)
+//!
+//! ## Overview
+//!
+//! This crate contains derive macros and function-like macros for implementing
+//! `NixDeserialize` with less boilerplate.
+//!
+//! ### Examples
+//! ```rust
+//! # use nix_compat_derive::NixDeserialize;
+//! #
+//! #[derive(NixDeserialize)]
+//! struct Unnamed(u64, String);
+//! ```
+//!
+//! ```rust
+//! # use nix_compat_derive::NixDeserialize;
+//! #
+//! #[derive(NixDeserialize)]
+//! struct Fields {
+//!     number: u64,
+//!     message: String,
+//! };
+//! ```
+//!
+//! ```rust
+//! # use nix_compat_derive::NixDeserialize;
+//! #
+//! #[derive(NixDeserialize)]
+//! struct Ignored;
+//! ```
+//!
+//! ## Attributes
+//!
+//! To customize the derived trait implementations you can add
+//! [attributes](https://doc.rust-lang.org/reference/attributes.html)
+//! to containers, fields and variants.
+//!
+//! ```rust
+//! # use nix_compat_derive::NixDeserialize;
+//! #
+//! #[derive(NixDeserialize)]
+//! #[nix(crate="nix_compat")] // <-- This is a container attribute
+//! struct Fields {
+//!     number: u64,
+//!     #[nix(version="..20")] // <-- This is a field attribute
+//!     message: String,
+//! };
+//!
+//! #[derive(NixDeserialize)]
+//! #[nix(crate="nix_compat")] // <-- This is also a container attribute
+//! enum E {
+//!     #[nix(version="..=9")] // <-- This is a variant attribute
+//!     A(u64),
+//!     #[nix(version="10..")] // <-- This is also a variant attribute
+//!     B(String),
+//! }
+//! ```
+//!
+//! ### Container attributes
+//!
+//! ##### `#[nix(from_str)]`
+//!
+//! When `from_str` is specified the fields are all ignored and instead a
+//! `String` is first deserialized and then `FromStr::from_str` is used
+//! to convert this `String` to the container type.
+//!
+//! This means that the container must implement `FromStr` and the error
+//! returned from the `from_str` must implement `Display`.
+//!
+//! ###### Example
+//!
+//! ```rust
+//! # use nix_compat_derive::NixDeserialize;
+//! #
+//! #[derive(NixDeserialize)]
+//! #[nix(from_str)]
+//! struct MyString(String);
+//! impl std::str::FromStr for MyString {
+//!     type Err = String;
+//!     fn from_str(s: &str) -> Result<Self, Self::Err> {
+//!         if s != "bad string" {
+//!             Ok(MyString(s.to_string()))
+//!         } else {
+//!             Err("Got a bad string".to_string())
+//!         }
+//!     }
+//! }
+//! ```
+//!
+//! ##### `#[nix(from = "FromType")]`
+//!
+//! When `from` is specified the fields are all ignored and instead a
+//! value of `FromType` is first deserialized and then `From::from` is
+//! used to convert from this value to the container type.
+//!
+//! This means that the container must implement `From<FromType>` and
+//! `FromType` must implement `NixDeserialize`.
+//!
+//! ###### Example
+//!
+//! ```rust
+//! # use nix_compat_derive::NixDeserialize;
+//! #
+//! #[derive(NixDeserialize)]
+//! #[nix(from="usize")]
+//! struct MyValue(usize);
+//! impl From<usize> for MyValue {
+//!     fn from(val: usize) -> Self {
+//!         MyValue(val)
+//!     }
+//! }
+//! ```
+//!
+//! ##### `#[nix(try_from = "FromType")]`
+//!
+//! With `try_from` a value of `FromType` is first deserialized and then
+//! `TryFrom::try_from` is used to convert from this value to the container
+//! type.
+//!
+//! This means that the container must implement `TryFrom<FromType>` and
+//! `FromType` must implement `NixDeserialize`.
+//! The error returned from `try_from` also needs to implement `Display`.
+//!
+//! ###### Example
+//!
+//! ```rust
+//! # use nix_compat_derive::NixDeserialize;
+//! #
+//! #[derive(NixDeserialize)]
+//! #[nix(try_from="usize")]
+//! struct WrongAnswer(usize);
+//! impl TryFrom<usize> for WrongAnswer {
+//!     type Error = String;
+//!     fn try_from(val: usize) -> Result<Self, Self::Error> {
+//!         if val != 42 {
+//!             Ok(WrongAnswer(val))
+//!         } else {
+//!             Err("Got the answer to life the universe and everything".to_string())
+//!         }
+//!     }
+//! }
+//! ```
+//!
+//! ##### `#[nix(crate = "...")]`
+//!
+//! Specify the path to the `nix-compat` crate instance to use when referring
+//! to the API in the generated code. This is usually not needed.
+//!
+//! ### Variant attributes
+//!
+//! ##### `#[nix(version = "range")]`
+//!
+//! Specifies the protocol version range where this variant is used.
+//! When deriving an enum the `version` attribute is used to select which
+//! variant of the enum to deserialize. The range is for minor version and
+//! the version ranges of all variants combined must cover all versions
+//! without any overlap or the first variant that matches is selected.
+//!
+//! ###### Example
+//!
+//! ```rust
+//! # use nix_compat_derive::NixDeserialize;
+//! #[derive(NixDeserialize)]
+//! enum Testing {
+//!     #[nix(version="..=18")]
+//!     OldVersion(u64),
+//!     #[nix(version="19..")]
+//!     NewVersion(String),
+//! }
+//! ```
+//!
+//! ### Field attributes
+//!
+//! ##### `#[nix(version = "range")]`
+//!
+//! Specifies the protocol version range where this field is included.
+//! The range is for minor version. For example `version = "..20"`
+//! includes the field in protocol versions `1.0` to `1.19` and skips
+//! it in version `1.20` and above.
+//!
+//! ###### Example
+//!
+//! ```rust
+//! # use nix_compat_derive::NixDeserialize;
+//! #
+//! #[derive(NixDeserialize)]
+//! struct Field {
+//!     number: u64,
+//!     #[nix(version="..20")]
+//!     messsage: String,
+//! }
+//! ```
+//!
+//! ##### `#[nix(default)]`
+//!
+//! When a field is skipped because the active protocol version falls
+//! outside the range specified in [`#[nix(version = "range")]`](#nixversion--range-1)
+//! this attribute indicates that `Default::default()` should be used
+//! to get a value for the field. This is also the default
+//! when you only specify [`#[nix(version = "range")]`](#nixversion--range-1).
+//!
+//! ###### Example
+//!
+//! ```rust
+//! # use nix_compat_derive::NixDeserialize;
+//! #
+//! #[derive(NixDeserialize)]
+//! struct Field {
+//!     number: u64,
+//!     #[nix(version="..20", default)]
+//!     messsage: String,
+//! }
+//! ```
+//!
+//! ##### `#[nix(default = "path")]`
+//!
+//! When a field is skipped because the active protocol version falls
+//! outside the range specified in [`#[nix(version = "range")]`](#nixversion--range-1)
+//! this attribute indicates that the function in `path` should be called to
+//! get a default value for the field. The given function must be callable
+//! as `fn() -> T`.
+//! For example `default = "my_value"` would call `my_value()` and `default =
+//! "AType::empty"` would call `AType::empty()`.
+//!
+//! ###### Example
+//!
+//! ```rust
+//! # use nix_compat_derive::NixDeserialize;
+//! #
+//! #[derive(NixDeserialize)]
+//! struct Field {
+//!     number: u64,
+//!     #[nix(version="..20", default="missing_string")]
+//!     messsage: String,
+//! }
+//!
+//! fn missing_string() -> String {
+//!     "missing string".to_string()
+//! }
+//! ```
+
+use internal::inputs::RemoteInput;
+use proc_macro::TokenStream;
+use syn::{parse_quote, DeriveInput};
+
+mod de;
+mod internal;
+
+#[proc_macro_derive(NixDeserialize, attributes(nix))]
+pub fn derive_nix_deserialize(item: TokenStream) -> TokenStream {
+    let mut input = syn::parse_macro_input!(item as DeriveInput);
+    let nnixrs: syn::Path = parse_quote!(::nix_compat);
+    de::expand_nix_deserialize(nnixrs, &mut input)
+        .unwrap_or_else(syn::Error::into_compile_error)
+        .into()
+}
+
+/// Macro to implement `NixDeserialize` on a type.
+/// Sometimes you can't use the deriver to implement `NixDeserialize`
+/// (like when dealing with types in Rust standard library) but don't want
+/// to implement it yourself. So this macro can be used for those situations
+/// where you would derive using `#[nix(from_str)]`,
+/// `#[nix(from = "FromType")]` or `#[nix(try_from = "FromType")]` if you
+/// could.
+///
+/// #### Example
+///
+/// ```rust
+/// # use nix_compat_derive::nix_deserialize_remote;
+/// #
+/// struct MyU64(u64);
+///
+/// impl From<u64> for MyU64 {
+///     fn from(value: u64) -> Self {
+///         Self(value)
+///     }
+/// }
+///
+/// nix_deserialize_remote!(#[nix(from="u64")] MyU64);
+/// ```
+#[proc_macro]
+pub fn nix_deserialize_remote(item: TokenStream) -> TokenStream {
+    let input = syn::parse_macro_input!(item as RemoteInput);
+    let crate_path = parse_quote!(::nix_compat);
+    de::expand_nix_deserialize_remote(crate_path, &input)
+        .unwrap_or_else(syn::Error::into_compile_error)
+        .into()
+}
diff --git a/tvix/nix-compat/Cargo.toml b/tvix/nix-compat/Cargo.toml
index dc77c5cf0ad1..58137e4de2e1 100644
--- a/tvix/nix-compat/Cargo.toml
+++ b/tvix/nix-compat/Cargo.toml
@@ -7,47 +7,48 @@ edition = "2021"
 # async NAR writer. Also needs the `wire` feature.
 async = ["tokio"]
 # code emitting low-level packets used in the daemon protocol.
-wire = ["tokio", "pin-project-lite"]
+wire = ["tokio", "pin-project-lite", "bytes"]
+test = []
 
 # Enable all features by default.
-default = ["async", "wire"]
+default = ["async", "wire", "nix-compat-derive"]
 
 [dependencies]
-bitflags = "2.4.1"
-bstr = { version = "1.6.0", features = ["alloc", "unicode", "serde"] }
-data-encoding = "2.6.0"
-ed25519 = "2.2.3"
-ed25519-dalek = "2.1.0"
-enum-primitive-derive = "0.3.0"
-glob = "0.3.0"
-mimalloc = "0.1.43"
-nom = "7.1.3"
-num-traits = "0.2.18"
-serde = { version = "1.0", features = ["derive"] }
-serde_json = "1.0"
-sha2 = "0.10.6"
-thiserror = "1.0.38"
-
-[dependencies.tokio]
-optional = true
-version = "1.32.0"
-features = ["io-util", "macros"]
-
-[dependencies.pin-project-lite]
+bitflags = { workspace = true }
+bstr = { workspace = true, features = ["alloc", "unicode", "serde"] }
+data-encoding = { workspace = true }
+ed25519 = { workspace = true }
+ed25519-dalek = { workspace = true }
+enum-primitive-derive = { workspace = true }
+glob = { workspace = true }
+mimalloc = { workspace = true }
+nom = { workspace = true }
+num-traits = { workspace = true }
+serde = { workspace = true, features = ["derive"] }
+serde_json = { workspace = true }
+sha2 = { workspace = true }
+thiserror = { workspace = true }
+tracing = { workspace = true }
+bytes = { workspace = true, optional = true }
+tokio = { workspace = true, features = ["io-util", "macros"], optional = true }
+pin-project-lite = { workspace = true, optional = true }
+
+[dependencies.nix-compat-derive]
+path = "../nix-compat-derive"
 optional = true
-version = "0.2.13"
 
 [dev-dependencies]
-criterion = { version = "0.5", features = ["html_reports"] }
-futures = { version = "0.3.30", default-features = false, features = ["executor"] }
-hex-literal = "0.4.1"
-lazy_static = "1.4.0"
-mimalloc = "0.1.43"
-pretty_assertions = "1.4.0"
-rstest = "0.19.0"
-serde_json = "1.0"
-tokio-test = "0.4.3"
-zstd = "^0.13.0"
+criterion = { workspace = true, features = ["html_reports"] }
+futures = { workspace = true }
+hex-literal = { workspace = true }
+lazy_static = { workspace = true }
+mimalloc = { workspace = true }
+pretty_assertions = { workspace = true }
+rstest = { workspace = true }
+serde_json = { workspace = true }
+smol_str = { workspace = true }
+tokio-test = { workspace = true }
+zstd = { workspace = true }
 
 [[bench]]
 name = "derivation_parse_aterm"
diff --git a/tvix/nix-compat/default.nix b/tvix/nix-compat/default.nix
index 08b053b77dd1..34938e3d6428 100644
--- a/tvix/nix-compat/default.nix
+++ b/tvix/nix-compat/default.nix
@@ -4,8 +4,8 @@
   runTests = true;
 }).overrideAttrs (old: rec {
   meta.ci.targets = lib.filter (x: lib.hasPrefix "with-features" x || x == "no-features") (lib.attrNames passthru);
-  passthru = depot.tvix.utils.mkFeaturePowerset {
+  passthru = old.passthru // (depot.tvix.utils.mkFeaturePowerset {
     inherit (old) crateName;
     features = [ "async" "wire" ];
-  };
+  });
 })
diff --git a/tvix/nix-compat/src/aterm/mod.rs b/tvix/nix-compat/src/aterm/mod.rs
index 8806b6caf2e5..bb3b77bc7399 100644
--- a/tvix/nix-compat/src/aterm/mod.rs
+++ b/tvix/nix-compat/src/aterm/mod.rs
@@ -2,6 +2,6 @@ mod escape;
 mod parser;
 
 pub(crate) use escape::escape_bytes;
-pub(crate) use parser::parse_bstr_field;
-pub(crate) use parser::parse_str_list;
+pub(crate) use parser::parse_bytes_field;
 pub(crate) use parser::parse_string_field;
+pub(crate) use parser::parse_string_list;
diff --git a/tvix/nix-compat/src/aterm/parser.rs b/tvix/nix-compat/src/aterm/parser.rs
index a30cb40ab08d..a570573a8700 100644
--- a/tvix/nix-compat/src/aterm/parser.rs
+++ b/tvix/nix-compat/src/aterm/parser.rs
@@ -11,8 +11,10 @@ use nom::multi::separated_list0;
 use nom::sequence::delimited;
 use nom::IResult;
 
-/// Parse a bstr and undo any escaping.
-fn parse_escaped_bstr(i: &[u8]) -> IResult<&[u8], BString> {
+/// Parse a bstr and undo any escaping (which is why this needs to allocate).
+// FUTUREWORK: have a version for fields that are known to not need escaping
+// (like store paths), and use &str.
+fn parse_escaped_bytes(i: &[u8]) -> IResult<&[u8], BString> {
     escaped_transform(
         is_not("\"\\"),
         '\\',
@@ -29,14 +31,14 @@ fn parse_escaped_bstr(i: &[u8]) -> IResult<&[u8], BString> {
 
 /// Parse a field in double quotes, undo any escaping, and return the unquoted
 /// and decoded `Vec<u8>`.
-pub(crate) fn parse_bstr_field(i: &[u8]) -> IResult<&[u8], BString> {
+pub(crate) fn parse_bytes_field(i: &[u8]) -> IResult<&[u8], BString> {
     // inside double quotes…
     delimited(
         nomchar('\"'),
         // There is
         alt((
             // …either is a bstr after unescaping
-            parse_escaped_bstr,
+            parse_escaped_bytes,
             // …or an empty string.
             map(tag(b""), |_| BString::default()),
         )),
@@ -45,8 +47,8 @@ pub(crate) fn parse_bstr_field(i: &[u8]) -> IResult<&[u8], BString> {
 }
 
 /// Parse a field in double quotes, undo any escaping, and return the unquoted
-/// and decoded string, if it's a valid string. Or fail parsing if the bytes are
-/// no valid UTF-8.
+/// and decoded [String], if it's valid UTF-8.
+/// Or fail parsing if the bytes are no valid UTF-8.
 pub(crate) fn parse_string_field(i: &[u8]) -> IResult<&[u8], String> {
     // inside double quotes…
     delimited(
@@ -54,18 +56,18 @@ pub(crate) fn parse_string_field(i: &[u8]) -> IResult<&[u8], String> {
         // There is
         alt((
             // either is a String after unescaping
-            nom::combinator::map_opt(parse_escaped_bstr, |escaped_bstr| {
-                String::from_utf8(escaped_bstr.into()).ok()
+            nom::combinator::map_opt(parse_escaped_bytes, |escaped_bytes| {
+                String::from_utf8(escaped_bytes.into()).ok()
             }),
             // or an empty string.
-            map(tag(b""), |_| String::new()),
+            map(tag(b""), |_| "".to_string()),
         )),
         nomchar('\"'),
     )(i)
 }
 
-/// Parse a list of of string fields (enclosed in brackets)
-pub(crate) fn parse_str_list(i: &[u8]) -> IResult<&[u8], Vec<String>> {
+/// Parse a list of string fields (enclosed in brackets)
+pub(crate) fn parse_string_list(i: &[u8]) -> IResult<&[u8], Vec<String>> {
     // inside brackets
     delimited(
         nomchar('['),
@@ -89,7 +91,7 @@ mod tests {
         #[case] expected: &[u8],
         #[case] exp_rest: &[u8],
     ) {
-        let (rest, parsed) = super::parse_bstr_field(input).expect("must parse");
+        let (rest, parsed) = super::parse_bytes_field(input).expect("must parse");
         assert_eq!(exp_rest, rest, "expected remainder");
         assert_eq!(expected, parsed);
     }
@@ -118,7 +120,7 @@ mod tests {
     #[case::empty_list(b"[]", vec![], b"")]
     #[case::empty_list_with_rest(b"[]blub", vec![], b"blub")]
     fn parse_list(#[case] input: &[u8], #[case] expected: Vec<String>, #[case] exp_rest: &[u8]) {
-        let (rest, parsed) = super::parse_str_list(input).expect("must parse");
+        let (rest, parsed) = super::parse_string_list(input).expect("must parse");
         assert_eq!(exp_rest, rest, "expected remainder");
         assert_eq!(expected, parsed);
     }
diff --git a/tvix/nix-compat/src/derivation/mod.rs b/tvix/nix-compat/src/derivation/mod.rs
index 6e12e3ea86e6..6baeaba38299 100644
--- a/tvix/nix-compat/src/derivation/mod.rs
+++ b/tvix/nix-compat/src/derivation/mod.rs
@@ -36,11 +36,11 @@ pub struct Derivation {
 
     /// Map from drv path to output names used from this derivation.
     #[serde(rename = "inputDrvs")]
-    pub input_derivations: BTreeMap<StorePath, BTreeSet<String>>,
+    pub input_derivations: BTreeMap<StorePath<String>, BTreeSet<String>>,
 
     /// Plain store paths of additional inputs.
     #[serde(rename = "inputSrcs")]
-    pub input_sources: BTreeSet<StorePath>,
+    pub input_sources: BTreeSet<StorePath<String>>,
 
     /// Maps output names to Output.
     pub outputs: BTreeMap<String, Output>,
@@ -127,7 +127,10 @@ impl Derivation {
     /// the `name` with a `.drv` suffix as name, all [Derivation::input_sources] and
     /// keys of [Derivation::input_derivations] as references, and the ATerm string of
     /// the [Derivation] as content.
-    pub fn calculate_derivation_path(&self, name: &str) -> Result<StorePath, DerivationError> {
+    pub fn calculate_derivation_path(
+        &self,
+        name: &str,
+    ) -> Result<StorePath<String>, DerivationError> {
         // append .drv to the name
         let name = &format!("{}.drv", name);
 
@@ -141,7 +144,6 @@ impl Derivation {
             .collect();
 
         build_text_path(name, self.to_aterm_bytes(), references)
-            .map(|s| s.to_owned())
             .map_err(|_e| DerivationError::InvalidOutputName(name.to_string()))
     }
 
@@ -210,7 +212,7 @@ impl Derivation {
                 self.input_derivations
                     .iter()
                     .map(|(drv_path, output_names)| {
-                        let hash = fn_lookup_hash_derivation_modulo(&drv_path.into());
+                        let hash = fn_lookup_hash_derivation_modulo(&drv_path.as_ref());
 
                         (hash, output_names.to_owned())
                     }),
diff --git a/tvix/nix-compat/src/derivation/output.rs b/tvix/nix-compat/src/derivation/output.rs
index 266617f587f8..0b81ef3c3155 100644
--- a/tvix/nix-compat/src/derivation/output.rs
+++ b/tvix/nix-compat/src/derivation/output.rs
@@ -1,5 +1,4 @@
 use crate::nixhash::CAHash;
-use crate::store_path::StorePathRef;
 use crate::{derivation::OutputError, store_path::StorePath};
 use serde::de::Unexpected;
 use serde::{Deserialize, Serialize};
@@ -10,7 +9,7 @@ use std::borrow::Cow;
 #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)]
 pub struct Output {
     /// Store path of build result.
-    pub path: Option<StorePath>,
+    pub path: Option<StorePath<String>>,
 
     #[serde(flatten)]
     pub ca_hash: Option<CAHash>, // we can only represent a subset here.
@@ -33,10 +32,10 @@ impl<'de> Deserialize<'de> for Output {
                 &"a string",
             ))?;
 
-        let path = StorePathRef::from_absolute_path(path.as_bytes())
+        let path = StorePath::from_absolute_path(path.as_bytes())
             .map_err(|_| serde::de::Error::invalid_value(Unexpected::Str(path), &"StorePath"))?;
         Ok(Self {
-            path: Some(path.to_owned()),
+            path: Some(path),
             ca_hash: CAHash::from_map::<D>(&fields)?,
         })
     }
diff --git a/tvix/nix-compat/src/derivation/parse_error.rs b/tvix/nix-compat/src/derivation/parse_error.rs
index fc97f1a9883b..f625d9aeb724 100644
--- a/tvix/nix-compat/src/derivation/parse_error.rs
+++ b/tvix/nix-compat/src/derivation/parse_error.rs
@@ -20,7 +20,7 @@ pub enum ErrorKind {
     DuplicateInputDerivationOutputName(String, String),
 
     #[error("duplicate input source: {0}")]
-    DuplicateInputSource(StorePath),
+    DuplicateInputSource(StorePath<String>),
 
     #[error("nix hash error: {0}")]
     NixHashError(nixhash::Error),
diff --git a/tvix/nix-compat/src/derivation/parser.rs b/tvix/nix-compat/src/derivation/parser.rs
index 2775294960fe..4fff7181ba40 100644
--- a/tvix/nix-compat/src/derivation/parser.rs
+++ b/tvix/nix-compat/src/derivation/parser.rs
@@ -3,7 +3,6 @@
 //!
 //! [ATerm]: http://program-transformation.org/Tools/ATermFormat.html
 
-use bstr::BString;
 use nom::bytes::complete::tag;
 use nom::character::complete::char as nomchar;
 use nom::combinator::{all_consuming, map_res};
@@ -14,7 +13,7 @@ use thiserror;
 
 use crate::derivation::parse_error::{into_nomerror, ErrorKind, NomError, NomResult};
 use crate::derivation::{write, CAHash, Derivation, Output};
-use crate::store_path::{self, StorePath, StorePathRef};
+use crate::store_path::{self, StorePath};
 use crate::{aterm, nixhash};
 
 #[derive(Debug, thiserror::Error)]
@@ -73,7 +72,7 @@ fn parse_output(i: &[u8]) -> NomResult<&[u8], (String, Output)> {
                     terminated(aterm::parse_string_field, nomchar(',')),
                     terminated(aterm::parse_string_field, nomchar(',')),
                     terminated(aterm::parse_string_field, nomchar(',')),
-                    aterm::parse_bstr_field,
+                    aterm::parse_bytes_field,
                 ))(i)
                 .map_err(into_nomerror)
             },
@@ -102,7 +101,7 @@ fn parse_output(i: &[u8]) -> NomResult<&[u8], (String, Output)> {
                             path: if output_path.is_empty() {
                                 None
                             } else {
-                                Some(string_to_store_path(i, output_path)?)
+                                Some(string_to_store_path(i, &output_path)?)
                             },
                             ca_hash: hash_with_mode,
                         },
@@ -132,12 +131,12 @@ fn parse_outputs(i: &[u8]) -> NomResult<&[u8], BTreeMap<String, Output>> {
 
     match res {
         Ok((rst, outputs_lst)) => {
-            let mut outputs: BTreeMap<String, Output> = BTreeMap::default();
+            let mut outputs = BTreeMap::default();
             for (output_name, output) in outputs_lst.into_iter() {
                 if outputs.contains_key(&output_name) {
                     return Err(nom::Err::Failure(NomError {
                         input: i,
-                        code: ErrorKind::DuplicateMapKey(output_name),
+                        code: ErrorKind::DuplicateMapKey(output_name.to_string()),
                     }));
                 }
                 outputs.insert(output_name, output);
@@ -149,11 +148,13 @@ fn parse_outputs(i: &[u8]) -> NomResult<&[u8], BTreeMap<String, Output>> {
     }
 }
 
-fn parse_input_derivations(i: &[u8]) -> NomResult<&[u8], BTreeMap<StorePath, BTreeSet<String>>> {
-    let (i, input_derivations_list) = parse_kv::<Vec<String>, _>(aterm::parse_str_list)(i)?;
+fn parse_input_derivations(
+    i: &[u8],
+) -> NomResult<&[u8], BTreeMap<StorePath<String>, BTreeSet<String>>> {
+    let (i, input_derivations_list) = parse_kv(aterm::parse_string_list)(i)?;
 
     // This is a HashMap of drv paths to a list of output names.
-    let mut input_derivations: BTreeMap<StorePath, BTreeSet<String>> = BTreeMap::new();
+    let mut input_derivations: BTreeMap<StorePath<String>, BTreeSet<_>> = BTreeMap::new();
 
     for (input_derivation, output_names) in input_derivations_list {
         let mut new_output_names = BTreeSet::new();
@@ -170,7 +171,7 @@ fn parse_input_derivations(i: &[u8]) -> NomResult<&[u8], BTreeMap<StorePath, BTr
             new_output_names.insert(output_name);
         }
 
-        let input_derivation: StorePath = string_to_store_path(i, input_derivation)?;
+        let input_derivation = string_to_store_path(i, input_derivation.as_str())?;
 
         input_derivations.insert(input_derivation, new_output_names);
     }
@@ -178,16 +179,16 @@ fn parse_input_derivations(i: &[u8]) -> NomResult<&[u8], BTreeMap<StorePath, BTr
     Ok((i, input_derivations))
 }
 
-fn parse_input_sources(i: &[u8]) -> NomResult<&[u8], BTreeSet<StorePath>> {
-    let (i, input_sources_lst) = aterm::parse_str_list(i).map_err(into_nomerror)?;
+fn parse_input_sources(i: &[u8]) -> NomResult<&[u8], BTreeSet<StorePath<String>>> {
+    let (i, input_sources_lst) = aterm::parse_string_list(i).map_err(into_nomerror)?;
 
     let mut input_sources: BTreeSet<_> = BTreeSet::new();
     for input_source in input_sources_lst.into_iter() {
-        let input_source: StorePath = string_to_store_path(i, input_source)?;
+        let input_source = string_to_store_path(i, input_source.as_str())?;
         if input_sources.contains(&input_source) {
             return Err(nom::Err::Failure(NomError {
                 input: i,
-                code: ErrorKind::DuplicateInputSource(input_source),
+                code: ErrorKind::DuplicateInputSource(input_source.to_owned()),
             }));
         } else {
             input_sources.insert(input_source);
@@ -197,24 +198,27 @@ fn parse_input_sources(i: &[u8]) -> NomResult<&[u8], BTreeSet<StorePath>> {
     Ok((i, input_sources))
 }
 
-fn string_to_store_path(
-    i: &[u8],
-    path_str: String,
-) -> Result<StorePath, nom::Err<NomError<&[u8]>>> {
-    #[cfg(debug_assertions)]
-    let path_str2 = path_str.clone();
-
-    let path: StorePath = StorePathRef::from_absolute_path(path_str.as_bytes())
-        .map_err(|e: store_path::Error| {
+fn string_to_store_path<'a, 'i, S>(
+    i: &'i [u8],
+    path_str: &'a str,
+) -> Result<StorePath<S>, nom::Err<NomError<&'i [u8]>>>
+where
+    S: std::cmp::Eq
+        + std::fmt::Display
+        + std::clone::Clone
+        + std::ops::Deref<Target = str>
+        + std::convert::From<&'a str>,
+{
+    let path =
+        StorePath::from_absolute_path(path_str.as_bytes()).map_err(|e: store_path::Error| {
             nom::Err::Failure(NomError {
                 input: i,
                 code: e.into(),
             })
-        })?
-        .to_owned();
+        })?;
 
     #[cfg(debug_assertions)]
-    assert_eq!(path_str2, path.to_absolute_path());
+    assert_eq!(path_str, path.to_absolute_path());
 
     Ok(path)
 }
@@ -240,9 +244,9 @@ pub fn parse_derivation(i: &[u8]) -> NomResult<&[u8], Derivation> {
                 // // parse builder
                 |i| terminated(aterm::parse_string_field, nomchar(','))(i).map_err(into_nomerror),
                 // // parse arguments
-                |i| terminated(aterm::parse_str_list, nomchar(','))(i).map_err(into_nomerror),
+                |i| terminated(aterm::parse_string_list, nomchar(','))(i).map_err(into_nomerror),
                 // parse environment
-                parse_kv::<BString, _>(aterm::parse_bstr_field),
+                parse_kv(aterm::parse_bytes_field),
             )),
             nomchar(')'),
         )
@@ -376,11 +380,11 @@ mod tests {
         };
         static ref EXP_AB_MAP: BTreeMap<String, BString> = {
             let mut b = BTreeMap::new();
-            b.insert("a".to_string(), b"1".as_bstr().to_owned());
-            b.insert("b".to_string(), b"2".as_bstr().to_owned());
+            b.insert("a".to_string(), b"1".into());
+            b.insert("b".to_string(), b"2".into());
             b
         };
-        static ref EXP_INPUT_DERIVATIONS_SIMPLE: BTreeMap<StorePath, BTreeSet<String>> = {
+        static ref EXP_INPUT_DERIVATIONS_SIMPLE: BTreeMap<StorePath<String>, BTreeSet<String>> = {
             let mut b = BTreeMap::new();
             b.insert(
                 StorePath::from_bytes(b"8bjm87p310sb7r2r0sg4xrynlvg86j8k-hello-2.12.1.tar.gz.drv")
@@ -427,8 +431,8 @@ mod tests {
         #[case] expected: &BTreeMap<String, BString>,
         #[case] exp_rest: &[u8],
     ) {
-        let (rest, parsed) = super::parse_kv::<BString, _>(crate::aterm::parse_bstr_field)(input)
-            .expect("must parse");
+        let (rest, parsed) =
+            super::parse_kv(crate::aterm::parse_bytes_field)(input).expect("must parse");
         assert_eq!(exp_rest, rest, "expected remainder");
         assert_eq!(*expected, parsed);
     }
@@ -437,8 +441,7 @@ mod tests {
     #[test]
     fn parse_kv_fail_dup_keys() {
         let input: &'static [u8] = b"[(\"a\",\"1\"),(\"a\",\"2\")]";
-        let e = super::parse_kv::<BString, _>(crate::aterm::parse_bstr_field)(input)
-            .expect_err("must fail");
+        let e = super::parse_kv(crate::aterm::parse_bytes_field)(input).expect_err("must fail");
 
         match e {
             nom::Err::Failure(e) => {
@@ -454,7 +457,7 @@ mod tests {
     #[case::simple(EXP_INPUT_DERIVATIONS_SIMPLE_ATERM.as_bytes(), &EXP_INPUT_DERIVATIONS_SIMPLE)]
     fn parse_input_derivations(
         #[case] input: &'static [u8],
-        #[case] expected: &BTreeMap<StorePath, BTreeSet<String>>,
+        #[case] expected: &BTreeMap<StorePath<String>, BTreeSet<String>>,
     ) {
         let (rest, parsed) = super::parse_input_derivations(input).expect("must parse");
 
diff --git a/tvix/nix-compat/src/derivation/write.rs b/tvix/nix-compat/src/derivation/write.rs
index 2ff68b6edba8..42dadcd76064 100644
--- a/tvix/nix-compat/src/derivation/write.rs
+++ b/tvix/nix-compat/src/derivation/write.rs
@@ -6,7 +6,7 @@
 use crate::aterm::escape_bytes;
 use crate::derivation::{ca_kind_prefix, output::Output};
 use crate::nixbase32;
-use crate::store_path::{StorePath, StorePathRef, STORE_DIR_WITH_SLASH};
+use crate::store_path::{StorePath, STORE_DIR_WITH_SLASH};
 use bstr::BString;
 use data_encoding::HEXLOWER;
 
@@ -34,7 +34,10 @@ pub(crate) trait AtermWriteable {
     fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()>;
 }
 
-impl AtermWriteable for StorePathRef<'_> {
+impl<S> AtermWriteable for StorePath<S>
+where
+    S: std::cmp::Eq + std::ops::Deref<Target = str>,
+{
     fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
         write_char(writer, QUOTE)?;
         writer.write_all(STORE_DIR_WITH_SLASH.as_bytes())?;
@@ -46,13 +49,6 @@ impl AtermWriteable for StorePathRef<'_> {
     }
 }
 
-impl AtermWriteable for StorePath {
-    fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
-        let r: StorePathRef = self.into();
-        r.aterm_write(writer)
-    }
-}
-
 impl AtermWriteable for String {
     fn aterm_write(&self, writer: &mut impl Write) -> std::io::Result<()> {
         write_field(writer, self, true)
@@ -179,7 +175,7 @@ pub(crate) fn write_input_derivations(
 
 pub(crate) fn write_input_sources(
     writer: &mut impl Write,
-    input_sources: &BTreeSet<StorePath>,
+    input_sources: &BTreeSet<StorePath<String>>,
 ) -> Result<(), io::Error> {
     write_char(writer, BRACKET_OPEN)?;
     write_array_elements(
diff --git a/tvix/nix-compat/src/lib.rs b/tvix/nix-compat/src/lib.rs
index cc1ee082be38..f30c557889a8 100644
--- a/tvix/nix-compat/src/lib.rs
+++ b/tvix/nix-compat/src/lib.rs
@@ -1,7 +1,10 @@
+extern crate self as nix_compat;
+
 pub(crate) mod aterm;
 pub mod derivation;
 pub mod nar;
 pub mod narinfo;
+pub mod nix_http;
 pub mod nixbase32;
 pub mod nixcpp;
 pub mod nixhash;
@@ -12,7 +15,7 @@ pub mod store_path;
 pub mod wire;
 
 #[cfg(feature = "wire")]
-mod nix_daemon;
+pub mod nix_daemon;
 #[cfg(feature = "wire")]
 pub use nix_daemon::worker_protocol;
 #[cfg(feature = "wire")]
diff --git a/tvix/nix-compat/src/nar/listing/mod.rs b/tvix/nix-compat/src/nar/listing/mod.rs
new file mode 100644
index 000000000000..5a9a3b4d3613
--- /dev/null
+++ b/tvix/nix-compat/src/nar/listing/mod.rs
@@ -0,0 +1,128 @@
+//! Parser for the Nix archive listing format, aka .ls.
+//!
+//! LS files are produced by the C++ Nix implementation via `write-nar-listing=1` query parameter
+//! passed to a store implementation when transferring store paths.
+//!
+//! Listing files contains metadata about a file and its offset in the corresponding NAR.
+//!
+//! NOTE: LS entries does not offer any integrity field to validate the retrieved file at the provided
+//! offset. Validating the contents is the caller's responsibility.
+
+use std::{
+    collections::HashMap,
+    path::{Component, Path},
+};
+
+use serde::Deserialize;
+
+#[cfg(test)]
+mod test;
+
+#[derive(Debug, thiserror::Error)]
+pub enum ListingError {
+    // TODO: add an enum of what component was problematic
+    // reusing `std::path::Component` is not possible as it contains a lifetime.
+    /// An unsupported path component can be:
+    /// - either a Windows prefix (`C:\\`, `\\share\\`)
+    /// - either a parent directory (`..`)
+    /// - either a root directory (`/`)
+    #[error("unsupported path component")]
+    UnsupportedPathComponent,
+    #[error("invalid encoding for entry component")]
+    InvalidEncoding,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(tag = "type", rename_all = "lowercase")]
+pub enum ListingEntry {
+    Regular {
+        size: u64,
+        #[serde(default)]
+        executable: bool,
+        #[serde(rename = "narOffset")]
+        nar_offset: u64,
+    },
+    Directory {
+        // It's tempting to think that the key should be a `Vec<u8>`
+        // but Nix does not support that and will fail to emit a listing version 1 for any non-UTF8
+        // encodeable string.
+        entries: HashMap<String, ListingEntry>,
+    },
+    Symlink {
+        target: String,
+    },
+}
+
+impl ListingEntry {
+    /// Given a relative path without `..` component, this will locate, relative to this entry, a
+    /// deeper entry.
+    ///
+    /// If the path is invalid, a listing error [`ListingError`] will be returned.
+    /// If the entry cannot be found, `None` will be returned.
+    pub fn locate<P: AsRef<Path>>(&self, path: P) -> Result<Option<&ListingEntry>, ListingError> {
+        // We perform a simple DFS on the components of the path
+        // while rejecting dangerous components, e.g. `..` or `/`
+        // Files and symlinks are *leaves*, i.e. we return them
+        let mut cur = self;
+        for component in path.as_ref().components() {
+            match component {
+                Component::CurDir => continue,
+                Component::RootDir | Component::Prefix(_) | Component::ParentDir => {
+                    return Err(ListingError::UnsupportedPathComponent)
+                }
+                Component::Normal(file_or_dir_name) => {
+                    if let Self::Directory { entries } = cur {
+                        // As Nix cannot encode non-UTF8 components in the listing (see comment on
+                        // the `Directory` enum variant), invalid encodings path components are
+                        // errors.
+                        let entry_name = file_or_dir_name
+                            .to_str()
+                            .ok_or(ListingError::InvalidEncoding)?;
+
+                        if let Some(new_entry) = entries.get(entry_name) {
+                            cur = new_entry;
+                        } else {
+                            return Ok(None);
+                        }
+                    } else {
+                        return Ok(None);
+                    }
+                }
+            }
+        }
+
+        // By construction, we found the node that corresponds to the path traversal.
+        Ok(Some(cur))
+    }
+}
+
+#[derive(Debug)]
+pub struct ListingVersion<const V: u8>;
+
+#[derive(Debug, thiserror::Error)]
+#[error("Invalid version: {0}")]
+struct ListingVersionError(u8);
+
+impl<'de, const V: u8> Deserialize<'de> for ListingVersion<V> {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        let value = u8::deserialize(deserializer)?;
+        if value == V {
+            Ok(ListingVersion::<V>)
+        } else {
+            Err(serde::de::Error::custom(ListingVersionError(value)))
+        }
+    }
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(untagged)]
+#[non_exhaustive]
+pub enum Listing {
+    V1 {
+        root: ListingEntry,
+        version: ListingVersion<1>,
+    },
+}
diff --git a/tvix/nix-compat/src/nar/listing/test.rs b/tvix/nix-compat/src/nar/listing/test.rs
new file mode 100644
index 000000000000..5b2ac3f166fe
--- /dev/null
+++ b/tvix/nix-compat/src/nar/listing/test.rs
@@ -0,0 +1,59 @@
+use std::{collections::HashMap, path::PathBuf, str::FromStr};
+
+use crate::nar;
+
+#[test]
+fn weird_paths() {
+    let root = nar::listing::ListingEntry::Directory {
+        entries: HashMap::new(),
+    };
+
+    root.locate("../../../../etc/passwd")
+        .expect_err("Failed to reject `../` fragment in a path during traversal");
+
+    // Gated on Windows as C:\\ is parsed as `Component::Normal(_)` on Linux.
+    #[cfg(target_os = "windows")]
+    root.locate("C:\\\\Windows\\System32")
+        .expect_err("Failed to reject Windows-style prefixes");
+
+    root.locate("/etc/passwd")
+        .expect_err("Failed to reject absolute UNIX paths");
+}
+
+#[test]
+fn nixos_release() {
+    let listing_bytes = include_bytes!("../tests/nixos-release.ls");
+    let listing: nar::listing::Listing = serde_json::from_slice(listing_bytes).unwrap();
+
+    let nar::listing::Listing::V1 { root, .. } = listing;
+    assert!(matches!(root, nar::listing::ListingEntry::Directory { .. }));
+
+    let build_products = root
+        .locate(PathBuf::from_str("nix-support/hydra-build-products").unwrap())
+        .expect("Failed to locate a known file in a directory")
+        .expect("File was unexpectedly not found in the listing");
+
+    assert!(matches!(
+        build_products,
+        nar::listing::ListingEntry::Regular { .. }
+    ));
+
+    let nonexisting_file = root
+        .locate(PathBuf::from_str("nix-support/does-not-exist").unwrap())
+        .expect("Failed to locate an unknown file in a directory");
+
+    assert!(
+        nonexisting_file.is_none(),
+        "Non-existing file was unexpectedly found in the listing"
+    );
+
+    let existing_dir = root
+        .locate(PathBuf::from_str("nix-support").unwrap())
+        .expect("Failed to locate a known directory in a directory")
+        .expect("Directory was expectedly found in the listing");
+
+    assert!(matches!(
+        existing_dir,
+        nar::listing::ListingEntry::Directory { .. }
+    ));
+}
diff --git a/tvix/nix-compat/src/nar/mod.rs b/tvix/nix-compat/src/nar/mod.rs
index c678d26ffb38..d0e8ee8a412f 100644
--- a/tvix/nix-compat/src/nar/mod.rs
+++ b/tvix/nix-compat/src/nar/mod.rs
@@ -1,4 +1,5 @@
 pub(crate) mod wire;
 
+pub mod listing;
 pub mod reader;
 pub mod writer;
diff --git a/tvix/nix-compat/src/nar/reader/mod.rs b/tvix/nix-compat/src/nar/reader/mod.rs
index 7e9143c8f35f..eef3b10f3c28 100644
--- a/tvix/nix-compat/src/nar/reader/mod.rs
+++ b/tvix/nix-compat/src/nar/reader/mod.rs
@@ -29,9 +29,11 @@ struct ArchiveReader<'a, 'r> {
     inner: &'a mut Reader<'r>,
 
     /// In debug mode, also track when we need to abandon this archive reader.
+    ///
     /// The archive reader must be abandoned when:
     ///   * An error is encountered at any point
     ///   * A file or directory reader is dropped before being read entirely.
+    ///
     /// All of these checks vanish in release mode.
     status: ArchiveReaderStatus<'a>,
 }
diff --git a/tvix/nix-compat/src/nar/tests/nixos-release.ls b/tvix/nix-compat/src/nar/tests/nixos-release.ls
new file mode 100644
index 000000000000..9dd350b7cf86
--- /dev/null
+++ b/tvix/nix-compat/src/nar/tests/nixos-release.ls
@@ -0,0 +1 @@
+{"root":{"entries":{"iso":{"entries":{"nixos-minimal-new-kernel-no-zfs-24.11pre660688.bee6b69aad74-x86_64-linux.iso":{"narOffset":440,"size":1051721728,"type":"regular"}},"type":"directory"},"nix-support":{"entries":{"hydra-build-products":{"narOffset":1051722544,"size":211,"type":"regular"},"system":{"narOffset":1051722944,"size":13,"type":"regular"}},"type":"directory"}},"type":"directory"},"version":1}
\ No newline at end of file
diff --git a/tvix/nix-compat/src/nar/writer/sync.rs b/tvix/nix-compat/src/nar/writer/sync.rs
index 6270129028fa..584b5a7192e5 100644
--- a/tvix/nix-compat/src/nar/writer/sync.rs
+++ b/tvix/nix-compat/src/nar/writer/sync.rs
@@ -35,11 +35,8 @@ use std::io::{
     Write,
 };
 
-/// Convenience type alias for types implementing [`Write`].
-pub type Writer<'a> = dyn Write + Send + 'a;
-
 /// Create a new NAR, writing the output to the specified writer.
-pub fn open<'a, 'w: 'a>(writer: &'a mut Writer<'w>) -> io::Result<Node<'a, 'w>> {
+pub fn open<W: Write>(writer: &mut W) -> io::Result<Node<W>> {
     let mut node = Node { writer };
     node.write(&wire::TOK_NAR)?;
     Ok(node)
@@ -49,11 +46,11 @@ pub fn open<'a, 'w: 'a>(writer: &'a mut Writer<'w>) -> io::Result<Node<'a, 'w>>
 ///
 /// A NAR can be thought of as a tree of nodes represented by this type. Each
 /// node can be a file, a symlink or a directory containing other nodes.
-pub struct Node<'a, 'w: 'a> {
-    writer: &'a mut Writer<'w>,
+pub struct Node<'a, W: Write> {
+    writer: &'a mut W,
 }
 
-impl<'a, 'w> Node<'a, 'w> {
+impl<'a, W: Write> Node<'a, W> {
     fn write(&mut self, data: &[u8]) -> io::Result<()> {
         self.writer.write_all(data)
     }
@@ -128,7 +125,7 @@ impl<'a, 'w> Node<'a, 'w> {
     ///
     /// It is the caller's responsibility to invoke [`Directory::close`],
     /// or invalid archives will be produced silently.
-    pub fn directory(mut self) -> io::Result<Directory<'a, 'w>> {
+    pub fn directory(mut self) -> io::Result<Directory<'a, W>> {
         self.write(&wire::TOK_DIR)?;
         Ok(Directory::new(self))
     }
@@ -145,13 +142,13 @@ fn into_name(_name: &[u8]) -> Name {
 }
 
 /// Content of a NAR node that represents a directory.
-pub struct Directory<'a, 'w> {
-    node: Node<'a, 'w>,
+pub struct Directory<'a, W: Write> {
+    node: Node<'a, W>,
     prev_name: Option<Name>,
 }
 
-impl<'a, 'w> Directory<'a, 'w> {
-    fn new(node: Node<'a, 'w>) -> Self {
+impl<'a, W: Write> Directory<'a, W> {
+    fn new(node: Node<'a, W>) -> Self {
         Self {
             node,
             prev_name: None,
@@ -166,7 +163,7 @@ impl<'a, 'w> Directory<'a, 'w> {
     /// It is the caller's responsibility to ensure that directory entries are
     /// written in order of ascending name. If this is not ensured, this method
     /// may panic or silently produce invalid archives.
-    pub fn entry(&mut self, name: &[u8]) -> io::Result<Node<'_, 'w>> {
+    pub fn entry(&mut self, name: &[u8]) -> io::Result<Node<'_, W>> {
         debug_assert!(
             name.len() <= wire::MAX_NAME_LEN,
             "name.len() > {}",
diff --git a/tvix/nix-compat/src/narinfo/mod.rs b/tvix/nix-compat/src/narinfo/mod.rs
index a77eba200f8d..21aecf80b5a2 100644
--- a/tvix/nix-compat/src/narinfo/mod.rs
+++ b/tvix/nix-compat/src/narinfo/mod.rs
@@ -32,7 +32,7 @@ mod signing_keys;
 mod verifying_keys;
 
 pub use fingerprint::fingerprint;
-pub use signature::{Error as SignatureError, Signature};
+pub use signature::{Error as SignatureError, Signature, SignatureRef};
 pub use signing_keys::parse_keypair;
 pub use signing_keys::{Error as SigningKeyError, SigningKey};
 pub use verifying_keys::{Error as VerifyingKeyError, VerifyingKey};
@@ -51,7 +51,7 @@ pub struct NarInfo<'a> {
     pub references: Vec<StorePathRef<'a>>,
     // authenticity
     /// Ed25519 signature over the path fingerprint
-    pub signatures: Vec<Signature<'a>>,
+    pub signatures: Vec<SignatureRef<'a>>,
     /// Content address (for content-defined paths)
     pub ca: Option<CAHash>,
     // derivation metadata
@@ -246,7 +246,7 @@ impl<'a> NarInfo<'a> {
                     };
                 }
                 "Sig" => {
-                    let val = Signature::parse(val)
+                    let val = SignatureRef::parse(val)
                         .map_err(|e| Error::UnableToParseSignature(signatures.len(), e))?;
 
                     signatures.push(val);
@@ -578,7 +578,7 @@ CA: fixed:r:sha1:1ak1ymbmsfx7z8kh09jzkr3a4dvkrfjw
 
         // ensure the signature is added
         let new_sig = narinfo.signatures.last().unwrap();
-        assert_eq!(signing_key.name(), new_sig.name());
+        assert_eq!(signing_key.name(), *new_sig.name());
 
         // verify the new signature against the verifying key
         let verifying_key = super::VerifyingKey::parse(super::DUMMY_VERIFYING_KEY)
diff --git a/tvix/nix-compat/src/narinfo/signature.rs b/tvix/nix-compat/src/narinfo/signature.rs
index fd197e771d98..33c49128c2d5 100644
--- a/tvix/nix-compat/src/narinfo/signature.rs
+++ b/tvix/nix-compat/src/narinfo/signature.rs
@@ -1,21 +1,44 @@
-use std::fmt::{self, Display};
+use std::{
+    fmt::{self, Display},
+    ops::Deref,
+};
 
 use data_encoding::BASE64;
-use ed25519_dalek::SIGNATURE_LENGTH;
 use serde::{Deserialize, Serialize};
 
+const SIGNATURE_LENGTH: usize = std::mem::size_of::<ed25519::SignatureBytes>();
+
 #[derive(Clone, Debug, Eq, PartialEq)]
-pub struct Signature<'a> {
-    name: &'a str,
-    bytes: [u8; SIGNATURE_LENGTH],
+pub struct Signature<S> {
+    name: S,
+    bytes: ed25519::SignatureBytes,
 }
 
-impl<'a> Signature<'a> {
-    pub fn new(name: &'a str, bytes: [u8; SIGNATURE_LENGTH]) -> Self {
+/// Type alias of a [Signature] using a `&str` as `name` field.
+pub type SignatureRef<'a> = Signature<&'a str>;
+
+/// Represents the signatures that Nix emits.
+/// It consists of a name (an identifier for a public key), and an ed25519
+/// signature (64 bytes).
+/// It is generic over the string type that's used for the name, and there's
+/// [SignatureRef] as a type alias for one containing &str.
+impl<S> Signature<S>
+where
+    S: Deref<Target = str>,
+{
+    /// Constructs a new [Signature] from a name and public key.
+    pub fn new(name: S, bytes: ed25519::SignatureBytes) -> Self {
         Self { name, bytes }
     }
 
-    pub fn parse(input: &'a str) -> Result<Self, Error> {
+    /// Parses a [Signature] from a string containing the name, a colon, and 64
+    /// base64-encoded bytes (plus padding).
+    /// These strings are commonly seen in the `Signature:` field of a NARInfo
+    /// file.
+    pub fn parse<'a>(input: &'a str) -> Result<Self, Error>
+    where
+        S: From<&'a str>,
+    {
         let (name, bytes64) = input.split_once(':').ok_or(Error::MissingSeparator)?;
 
         if name.is_empty()
@@ -39,14 +62,19 @@ impl<'a> Signature<'a> {
             Err(_) => return Err(Error::DecodeError(input.to_string())),
         }
 
-        Ok(Signature { name, bytes })
+        Ok(Self {
+            name: name.into(),
+            bytes,
+        })
     }
 
-    pub fn name(&self) -> &'a str {
-        self.name
+    /// Returns the name field of the signature.
+    pub fn name(&self) -> &S {
+        &self.name
     }
 
-    pub fn bytes(&self) -> &[u8; SIGNATURE_LENGTH] {
+    /// Returns the 64 bytes of signatures.
+    pub fn bytes(&self) -> &ed25519::SignatureBytes {
         &self.bytes
     }
 
@@ -56,9 +84,21 @@ impl<'a> Signature<'a> {
 
         verifying_key.verify_strict(fingerprint, &signature).is_ok()
     }
+
+    /// Constructs a [SignatureRef] from this signature.
+    pub fn as_ref(&self) -> SignatureRef<'_> {
+        SignatureRef {
+            name: self.name.deref(),
+            bytes: self.bytes,
+        }
+    }
 }
 
-impl<'de: 'a, 'a> Deserialize<'de> for Signature<'a> {
+impl<'a, 'de, S> Deserialize<'de> for Signature<S>
+where
+    S: Deref<Target = str> + From<&'a str>,
+    'de: 'a,
+{
     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     where
         D: serde::Deserializer<'de>,
@@ -70,10 +110,13 @@ impl<'de: 'a, 'a> Deserialize<'de> for Signature<'a> {
     }
 }
 
-impl<'a> Serialize for Signature<'a> {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+impl<S: Display> Serialize for Signature<S>
+where
+    S: Deref<Target = str>,
+{
+    fn serialize<SR>(&self, serializer: SR) -> Result<SR::Ok, SR::Error>
     where
-        S: serde::Serializer,
+        SR: serde::Serializer,
     {
         let string: String = self.to_string();
 
@@ -81,6 +124,15 @@ impl<'a> Serialize for Signature<'a> {
     }
 }
 
+impl<S> Display for Signature<S>
+where
+    S: Display,
+{
+    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
+        write!(w, "{}:{}", self.name, BASE64.encode(&self.bytes))
+    }
+}
+
 #[derive(Debug, thiserror::Error)]
 pub enum Error {
     #[error("Invalid name: {0}")]
@@ -93,12 +145,6 @@ pub enum Error {
     DecodeError(String),
 }
 
-impl Display for Signature<'_> {
-    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
-        write!(w, "{}:{}", self.name, BASE64.encode(&self.bytes))
-    }
-}
-
 #[cfg(test)]
 mod test {
     use data_encoding::BASE64;
@@ -143,7 +189,7 @@ mod test {
         #[case] fp: &str,
         #[case] expect_valid: bool,
     ) {
-        let sig = Signature::parse(sig_str).expect("must parse");
+        let sig = Signature::<&str>::parse(sig_str).expect("must parse");
         assert_eq!(expect_valid, sig.verify(fp.as_bytes(), verifying_key));
     }
 
@@ -158,7 +204,7 @@ mod test {
         "u01BybwQhyI5H1bW1EIWXssMDhDDIvXOG5uh8Qzgdyjz6U1qg6DHhMAvXZOUStIj6X5t4/ufFgR8i3fjf0bMAw=="
     )]
     fn parse_fail(#[case] input: &'static str) {
-        Signature::parse(input).expect_err("must fail");
+        Signature::<&str>::parse(input).expect_err("must fail");
     }
 
     #[test]
@@ -177,8 +223,29 @@ mod test {
         let serialized = serde_json::to_string(&signature_actual).expect("must serialize");
         assert_eq!(signature_str_json, &serialized);
 
-        let deserialized: Signature<'_> =
+        let deserialized: Signature<&str> =
             serde_json::from_str(signature_str_json).expect("must deserialize");
         assert_eq!(&signature_actual, &deserialized);
     }
+
+    /// Construct a [Signature], using different String types for the name field.
+    #[test]
+    fn signature_owned() {
+        let signature1 = Signature::<String>::parse("cache.nixos.org-1:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==").expect("must parse");
+        let signature2 = Signature::<smol_str::SmolStr>::parse("cache.nixos.org-1:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==").expect("must parse");
+        let signature3 = Signature::<&str>::parse("cache.nixos.org-1:TsTTb3WGTZKphvYdBHXwo6weVILmTytUjLB+vcX89fOjjRicCHmKA4RCPMVLkj6TMJ4GMX3HPVWRdD1hkeKZBQ==").expect("must parse");
+
+        assert!(
+            signature1.verify(FINGERPRINT.as_bytes(), &PUB_CACHE_NIXOS_ORG_1),
+            "must verify"
+        );
+        assert!(
+            signature2.verify(FINGERPRINT.as_bytes(), &PUB_CACHE_NIXOS_ORG_1),
+            "must verify"
+        );
+        assert!(
+            signature3.verify(FINGERPRINT.as_bytes(), &PUB_CACHE_NIXOS_ORG_1),
+            "must verify"
+        );
+    }
 }
diff --git a/tvix/nix-compat/src/narinfo/signing_keys.rs b/tvix/nix-compat/src/narinfo/signing_keys.rs
index e33687bc88f8..cf513b7ba475 100644
--- a/tvix/nix-compat/src/narinfo/signing_keys.rs
+++ b/tvix/nix-compat/src/narinfo/signing_keys.rs
@@ -7,7 +7,7 @@
 use data_encoding::BASE64;
 use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH};
 
-use super::{Signature, VerifyingKey};
+use super::{SignatureRef, VerifyingKey};
 
 pub struct SigningKey<S> {
     name: String,
@@ -23,9 +23,9 @@ where
         Self { name, signing_key }
     }
 
-    /// Signs a fingerprint using the internal signing key, returns the [Signature]
-    pub(crate) fn sign<'a>(&'a self, fp: &[u8]) -> Signature<'a> {
-        Signature::new(&self.name, self.signing_key.sign(fp).to_bytes())
+    /// Signs a fingerprint using the internal signing key, returns the [SignatureRef]
+    pub(crate) fn sign<'a>(&'a self, fp: &[u8]) -> SignatureRef<'a> {
+        SignatureRef::new(&self.name, self.signing_key.sign(fp).to_bytes())
     }
 
     pub fn name(&self) -> &str {
diff --git a/tvix/nix-compat/src/narinfo/verifying_keys.rs b/tvix/nix-compat/src/narinfo/verifying_keys.rs
index b8ed2b9531c1..67ef2e3a459c 100644
--- a/tvix/nix-compat/src/narinfo/verifying_keys.rs
+++ b/tvix/nix-compat/src/narinfo/verifying_keys.rs
@@ -6,7 +6,7 @@ use std::fmt::Display;
 use data_encoding::BASE64;
 use ed25519_dalek::PUBLIC_KEY_LENGTH;
 
-use super::Signature;
+use super::SignatureRef;
 
 /// This represents a ed25519 public key and "name".
 /// These are normally passed in the `trusted-public-keys` Nix config option,
@@ -69,8 +69,8 @@ impl VerifyingKey {
     /// which means the name in the signature has to match,
     /// and the signature bytes themselves need to be a valid signature made by
     /// the signing key identified by [Self::verifying key].
-    pub fn verify(&self, fingerprint: &str, signature: &Signature) -> bool {
-        if self.name() != signature.name() {
+    pub fn verify(&self, fingerprint: &str, signature: &SignatureRef<'_>) -> bool {
+        if self.name() != *signature.name() {
             return false;
         }
 
@@ -109,7 +109,7 @@ mod test {
     use ed25519_dalek::PUBLIC_KEY_LENGTH;
     use rstest::rstest;
 
-    use crate::narinfo::Signature;
+    use crate::narinfo::SignatureRef;
 
     use super::VerifyingKey;
     const FINGERPRINT: &str = "1;/nix/store/syd87l2rxw8cbsxmxl853h0r6pdwhwjr-curl-7.82.0-bin;sha256:1b4sb93wp679q4zx9k1ignby1yna3z7c4c2ri3wphylbc2dwsys0;196040;/nix/store/0jqd0rlxzra1rs38rdxl43yh6rxchgc6-curl-7.82.0,/nix/store/6w8g7njm4mck5dmjxws0z1xnrxvl81xa-glibc-2.34-115,/nix/store/j5jxw3iy7bbz4a57fh9g2xm2gxmyal8h-zlib-1.2.12,/nix/store/yxvjs9drzsphm9pcf42a4byzj1kb9m7k-openssl-1.1.1n";
@@ -146,7 +146,7 @@ mod test {
         #[case] expected: bool,
     ) {
         let pubkey = VerifyingKey::parse(pubkey_str).expect("must parse");
-        let signature = Signature::parse(signature_str).expect("must parse");
+        let signature = SignatureRef::parse(signature_str).expect("must parse");
 
         assert_eq!(expected, pubkey.verify(fingerprint, &signature));
     }
diff --git a/tvix/nix-compat/src/nix_daemon/de/bytes.rs b/tvix/nix-compat/src/nix_daemon/de/bytes.rs
new file mode 100644
index 000000000000..7daced54eef7
--- /dev/null
+++ b/tvix/nix-compat/src/nix_daemon/de/bytes.rs
@@ -0,0 +1,70 @@
+use bytes::Bytes;
+
+use super::{Error, NixDeserialize, NixRead};
+
+impl NixDeserialize for Bytes {
+    async fn try_deserialize<R>(reader: &mut R) -> Result<Option<Self>, R::Error>
+    where
+        R: ?Sized + NixRead + Send,
+    {
+        reader.try_read_bytes().await
+    }
+}
+
+impl NixDeserialize for String {
+    async fn try_deserialize<R>(reader: &mut R) -> Result<Option<Self>, R::Error>
+    where
+        R: ?Sized + NixRead + Send,
+    {
+        if let Some(buf) = reader.try_read_bytes().await? {
+            String::from_utf8(buf.to_vec())
+                .map_err(R::Error::invalid_data)
+                .map(Some)
+        } else {
+            Ok(None)
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use std::io;
+
+    use hex_literal::hex;
+    use rstest::rstest;
+    use tokio_test::io::Builder;
+
+    use crate::nix_daemon::de::{NixRead, NixReader};
+
+    #[rstest]
+    #[case::empty("", &hex!("0000 0000 0000 0000"))]
+    #[case::one(")", &hex!("0100 0000 0000 0000 2900 0000 0000 0000"))]
+    #[case::two("it", &hex!("0200 0000 0000 0000 6974 0000 0000 0000"))]
+    #[case::three("tea", &hex!("0300 0000 0000 0000 7465 6100 0000 0000"))]
+    #[case::four("were", &hex!("0400 0000 0000 0000 7765 7265 0000 0000"))]
+    #[case::five("where", &hex!("0500 0000 0000 0000 7768 6572 6500 0000"))]
+    #[case::six("unwrap", &hex!("0600 0000 0000 0000 756E 7772 6170 0000"))]
+    #[case::seven("where's", &hex!("0700 0000 0000 0000 7768 6572 6527 7300"))]
+    #[case::aligned("read_tea", &hex!("0800 0000 0000 0000 7265 6164 5F74 6561"))]
+    #[case::more_bytes("read_tess", &hex!("0900 0000 0000 0000 7265 6164 5F74 6573 7300 0000 0000 0000"))]
+    #[case::utf8("The quick brown 🦊 jumps over 13 lazy 🐶.", &hex!("2D00 0000 0000 0000  5468 6520 7175 6963  6b20 6272 6f77 6e20  f09f a68a 206a 756d  7073 206f 7665 7220  3133 206c 617a 7920  f09f 90b6 2e00 0000"))]
+    #[tokio::test]
+    async fn test_read_string(#[case] expected: &str, #[case] data: &[u8]) {
+        let mock = Builder::new().read(data).build();
+        let mut reader = NixReader::new(mock);
+        let actual: String = reader.read_value().await.unwrap();
+        assert_eq!(actual, expected);
+    }
+
+    #[tokio::test]
+    async fn test_read_string_invalid() {
+        let mock = Builder::new()
+            .read(&hex!("0300 0000 0000 0000 EDA0 8000 0000 0000"))
+            .build();
+        let mut reader = NixReader::new(mock);
+        assert_eq!(
+            io::ErrorKind::InvalidData,
+            reader.read_value::<String>().await.unwrap_err().kind()
+        );
+    }
+}
diff --git a/tvix/nix-compat/src/nix_daemon/de/collections.rs b/tvix/nix-compat/src/nix_daemon/de/collections.rs
new file mode 100644
index 000000000000..cf79f584506a
--- /dev/null
+++ b/tvix/nix-compat/src/nix_daemon/de/collections.rs
@@ -0,0 +1,105 @@
+use std::{collections::BTreeMap, future::Future};
+
+use super::{NixDeserialize, NixRead};
+
+#[allow(clippy::manual_async_fn)]
+impl<T> NixDeserialize for Vec<T>
+where
+    T: NixDeserialize + Send,
+{
+    fn try_deserialize<R>(
+        reader: &mut R,
+    ) -> impl Future<Output = Result<Option<Self>, R::Error>> + Send + '_
+    where
+        R: ?Sized + NixRead + Send,
+    {
+        async move {
+            if let Some(len) = reader.try_read_value::<usize>().await? {
+                let mut ret = Vec::with_capacity(len);
+                for _ in 0..len {
+                    ret.push(reader.read_value().await?);
+                }
+                Ok(Some(ret))
+            } else {
+                Ok(None)
+            }
+        }
+    }
+}
+
+#[allow(clippy::manual_async_fn)]
+impl<K, V> NixDeserialize for BTreeMap<K, V>
+where
+    K: NixDeserialize + Ord + Send,
+    V: NixDeserialize + Send,
+{
+    fn try_deserialize<R>(
+        reader: &mut R,
+    ) -> impl Future<Output = Result<Option<Self>, R::Error>> + Send + '_
+    where
+        R: ?Sized + NixRead + Send,
+    {
+        async move {
+            if let Some(len) = reader.try_read_value::<usize>().await? {
+                let mut ret = BTreeMap::new();
+                for _ in 0..len {
+                    let key = reader.read_value().await?;
+                    let value = reader.read_value().await?;
+                    ret.insert(key, value);
+                }
+                Ok(Some(ret))
+            } else {
+                Ok(None)
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use std::collections::BTreeMap;
+    use std::fmt;
+
+    use hex_literal::hex;
+    use rstest::rstest;
+    use tokio_test::io::Builder;
+
+    use crate::nix_daemon::de::{NixDeserialize, NixRead, NixReader};
+
+    #[rstest]
+    #[case::empty(vec![], &hex!("0000 0000 0000 0000"))]
+    #[case::one(vec![0x29], &hex!("0100 0000 0000 0000 2900 0000 0000 0000"))]
+    #[case::two(vec![0x7469, 10], &hex!("0200 0000 0000 0000 6974 0000 0000 0000 0A00 0000 0000 0000"))]
+    #[tokio::test]
+    async fn test_read_small_vec(#[case] expected: Vec<usize>, #[case] data: &[u8]) {
+        let mock = Builder::new().read(data).build();
+        let mut reader = NixReader::new(mock);
+        let actual: Vec<usize> = reader.read_value().await.unwrap();
+        assert_eq!(actual, expected);
+    }
+
+    fn empty_map() -> BTreeMap<usize, u64> {
+        BTreeMap::new()
+    }
+    macro_rules! map {
+        ($($key:expr => $value:expr),*) => {{
+            let mut ret = BTreeMap::new();
+            $(ret.insert($key, $value);)*
+            ret
+        }};
+    }
+
+    #[rstest]
+    #[case::empty(empty_map(), &hex!("0000 0000 0000 0000"))]
+    #[case::one(map![0x7469usize => 10u64], &hex!("0100 0000 0000 0000 6974 0000 0000 0000 0A00 0000 0000 0000"))]
+    #[tokio::test]
+    async fn test_read_small_btree_map<E>(#[case] expected: E, #[case] data: &[u8])
+    where
+        E: NixDeserialize + PartialEq + fmt::Debug,
+    {
+        let mock = Builder::new().read(data).build();
+        let mut reader = NixReader::new(mock);
+        let actual: E = reader.read_value().await.unwrap();
+        assert_eq!(actual, expected);
+    }
+}
diff --git a/tvix/nix-compat/src/nix_daemon/de/int.rs b/tvix/nix-compat/src/nix_daemon/de/int.rs
new file mode 100644
index 000000000000..eecf641cfe99
--- /dev/null
+++ b/tvix/nix-compat/src/nix_daemon/de/int.rs
@@ -0,0 +1,100 @@
+use super::{Error, NixDeserialize, NixRead};
+
+impl NixDeserialize for u64 {
+    async fn try_deserialize<R>(reader: &mut R) -> Result<Option<Self>, R::Error>
+    where
+        R: ?Sized + NixRead + Send,
+    {
+        reader.try_read_number().await
+    }
+}
+
+impl NixDeserialize for usize {
+    async fn try_deserialize<R>(reader: &mut R) -> Result<Option<Self>, R::Error>
+    where
+        R: ?Sized + NixRead + Send,
+    {
+        if let Some(value) = reader.try_read_number().await? {
+            value.try_into().map_err(R::Error::invalid_data).map(Some)
+        } else {
+            Ok(None)
+        }
+    }
+}
+
+impl NixDeserialize for bool {
+    async fn try_deserialize<R>(reader: &mut R) -> Result<Option<Self>, R::Error>
+    where
+        R: ?Sized + NixRead + Send,
+    {
+        Ok(reader.try_read_number().await?.map(|v| v != 0))
+    }
+}
+impl NixDeserialize for i64 {
+    async fn try_deserialize<R>(reader: &mut R) -> Result<Option<Self>, R::Error>
+    where
+        R: ?Sized + NixRead + Send,
+    {
+        Ok(reader.try_read_number().await?.map(|v| v as i64))
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use hex_literal::hex;
+    use rstest::rstest;
+    use tokio_test::io::Builder;
+
+    use crate::nix_daemon::de::{NixRead, NixReader};
+
+    #[rstest]
+    #[case::simple_false(false, &hex!("0000 0000 0000 0000"))]
+    #[case::simple_true(true, &hex!("0100 0000 0000 0000"))]
+    #[case::other_true(true, &hex!("1234 5600 0000 0000"))]
+    #[case::max_true(true, &hex!("FFFF FFFF FFFF FFFF"))]
+    #[tokio::test]
+    async fn test_read_bool(#[case] expected: bool, #[case] data: &[u8]) {
+        let mock = Builder::new().read(data).build();
+        let mut reader = NixReader::new(mock);
+        let actual: bool = reader.read_value().await.unwrap();
+        assert_eq!(actual, expected);
+    }
+
+    #[rstest]
+    #[case::zero(0, &hex!("0000 0000 0000 0000"))]
+    #[case::one(1, &hex!("0100 0000 0000 0000"))]
+    #[case::other(0x563412, &hex!("1234 5600 0000 0000"))]
+    #[case::max_value(u64::MAX, &hex!("FFFF FFFF FFFF FFFF"))]
+    #[tokio::test]
+    async fn test_read_u64(#[case] expected: u64, #[case] data: &[u8]) {
+        let mock = Builder::new().read(data).build();
+        let mut reader = NixReader::new(mock);
+        let actual: u64 = reader.read_value().await.unwrap();
+        assert_eq!(actual, expected);
+    }
+
+    #[rstest]
+    #[case::zero(0, &hex!("0000 0000 0000 0000"))]
+    #[case::one(1, &hex!("0100 0000 0000 0000"))]
+    #[case::other(0x563412, &hex!("1234 5600 0000 0000"))]
+    #[case::max_value(usize::MAX, &usize::MAX.to_le_bytes())]
+    #[tokio::test]
+    async fn test_read_usize(#[case] expected: usize, #[case] data: &[u8]) {
+        let mock = Builder::new().read(data).build();
+        let mut reader = NixReader::new(mock);
+        let actual: usize = reader.read_value().await.unwrap();
+        assert_eq!(actual, expected);
+    }
+
+    // FUTUREWORK: Test this on supported hardware
+    #[tokio::test]
+    #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
+    async fn test_read_usize_overflow() {
+        let mock = Builder::new().read(&u64::MAX.to_le_bytes()).build();
+        let mut reader = NixReader::new(mock);
+        assert_eq!(
+            std::io::ErrorKind::InvalidData,
+            reader.read_value::<usize>().await.unwrap_err().kind()
+        );
+    }
+}
diff --git a/tvix/nix-compat/src/nix_daemon/de/mock.rs b/tvix/nix-compat/src/nix_daemon/de/mock.rs
new file mode 100644
index 000000000000..31cc3a4897ba
--- /dev/null
+++ b/tvix/nix-compat/src/nix_daemon/de/mock.rs
@@ -0,0 +1,261 @@
+use std::collections::VecDeque;
+use std::fmt;
+use std::io;
+use std::thread;
+
+use bytes::Bytes;
+use thiserror::Error;
+
+use crate::nix_daemon::ProtocolVersion;
+
+use super::NixRead;
+
+#[derive(Debug, Error, PartialEq, Eq, Clone)]
+pub enum Error {
+    #[error("custom error '{0}'")]
+    Custom(String),
+    #[error("invalid data '{0}'")]
+    InvalidData(String),
+    #[error("missing data '{0}'")]
+    MissingData(String),
+    #[error("IO error {0} '{1}'")]
+    IO(io::ErrorKind, String),
+    #[error("wrong read: expected {0} got {1}")]
+    WrongRead(OperationType, OperationType),
+}
+
+impl Error {
+    pub fn expected_read_number() -> Error {
+        Error::WrongRead(OperationType::ReadNumber, OperationType::ReadBytes)
+    }
+
+    pub fn expected_read_bytes() -> Error {
+        Error::WrongRead(OperationType::ReadBytes, OperationType::ReadNumber)
+    }
+}
+
+impl super::Error for Error {
+    fn custom<T: fmt::Display>(msg: T) -> Self {
+        Self::Custom(msg.to_string())
+    }
+
+    fn io_error(err: std::io::Error) -> Self {
+        Self::IO(err.kind(), err.to_string())
+    }
+
+    fn invalid_data<T: fmt::Display>(msg: T) -> Self {
+        Self::InvalidData(msg.to_string())
+    }
+
+    fn missing_data<T: fmt::Display>(msg: T) -> Self {
+        Self::MissingData(msg.to_string())
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum OperationType {
+    ReadNumber,
+    ReadBytes,
+}
+
+impl fmt::Display for OperationType {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::ReadNumber => write!(f, "read_number"),
+            Self::ReadBytes => write!(f, "read_bytess"),
+        }
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+enum Operation {
+    ReadNumber(Result<u64, Error>),
+    ReadBytes(Result<Bytes, Error>),
+}
+
+impl From<Operation> for OperationType {
+    fn from(value: Operation) -> Self {
+        match value {
+            Operation::ReadNumber(_) => OperationType::ReadNumber,
+            Operation::ReadBytes(_) => OperationType::ReadBytes,
+        }
+    }
+}
+
+pub struct Builder {
+    version: ProtocolVersion,
+    ops: VecDeque<Operation>,
+}
+
+impl Builder {
+    pub fn new() -> Builder {
+        Builder {
+            version: Default::default(),
+            ops: VecDeque::new(),
+        }
+    }
+
+    pub fn version<V: Into<ProtocolVersion>>(&mut self, version: V) -> &mut Self {
+        self.version = version.into();
+        self
+    }
+
+    pub fn read_number(&mut self, value: u64) -> &mut Self {
+        self.ops.push_back(Operation::ReadNumber(Ok(value)));
+        self
+    }
+
+    pub fn read_number_error(&mut self, err: Error) -> &mut Self {
+        self.ops.push_back(Operation::ReadNumber(Err(err)));
+        self
+    }
+
+    pub fn read_bytes(&mut self, value: Bytes) -> &mut Self {
+        self.ops.push_back(Operation::ReadBytes(Ok(value)));
+        self
+    }
+
+    pub fn read_slice(&mut self, data: &[u8]) -> &mut Self {
+        let value = Bytes::copy_from_slice(data);
+        self.ops.push_back(Operation::ReadBytes(Ok(value)));
+        self
+    }
+
+    pub fn read_bytes_error(&mut self, err: Error) -> &mut Self {
+        self.ops.push_back(Operation::ReadBytes(Err(err)));
+        self
+    }
+
+    pub fn build(&mut self) -> Mock {
+        Mock {
+            version: self.version,
+            ops: self.ops.clone(),
+        }
+    }
+}
+
+impl Default for Builder {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+pub struct Mock {
+    version: ProtocolVersion,
+    ops: VecDeque<Operation>,
+}
+
+impl NixRead for Mock {
+    type Error = Error;
+
+    fn version(&self) -> ProtocolVersion {
+        self.version
+    }
+
+    async fn try_read_number(&mut self) -> Result<Option<u64>, Self::Error> {
+        match self.ops.pop_front() {
+            Some(Operation::ReadNumber(ret)) => ret.map(Some),
+            Some(Operation::ReadBytes(_)) => Err(Error::expected_read_bytes()),
+            None => Ok(None),
+        }
+    }
+
+    async fn try_read_bytes_limited(
+        &mut self,
+        _limit: std::ops::RangeInclusive<usize>,
+    ) -> Result<Option<Bytes>, Self::Error> {
+        match self.ops.pop_front() {
+            Some(Operation::ReadBytes(ret)) => ret.map(Some),
+            Some(Operation::ReadNumber(_)) => Err(Error::expected_read_number()),
+            None => Ok(None),
+        }
+    }
+}
+
+impl Drop for Mock {
+    fn drop(&mut self) {
+        // No need to panic again
+        if thread::panicking() {
+            return;
+        }
+        if let Some(op) = self.ops.front() {
+            panic!("reader dropped with {op:?} operation still unread")
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use bytes::Bytes;
+    use hex_literal::hex;
+
+    use crate::nix_daemon::de::NixRead;
+
+    use super::{Builder, Error};
+
+    #[tokio::test]
+    async fn read_slice() {
+        let mut mock = Builder::new()
+            .read_number(10)
+            .read_slice(&[])
+            .read_slice(&hex!("0000 1234 5678 9ABC DEFF"))
+            .build();
+        assert_eq!(10, mock.read_number().await.unwrap());
+        assert_eq!(&[] as &[u8], &mock.read_bytes().await.unwrap()[..]);
+        assert_eq!(
+            &hex!("0000 1234 5678 9ABC DEFF"),
+            &mock.read_bytes().await.unwrap()[..]
+        );
+        assert_eq!(None, mock.try_read_number().await.unwrap());
+        assert_eq!(None, mock.try_read_bytes().await.unwrap());
+    }
+
+    #[tokio::test]
+    async fn read_bytes() {
+        let mut mock = Builder::new()
+            .read_number(10)
+            .read_bytes(Bytes::from_static(&[]))
+            .read_bytes(Bytes::from_static(&hex!("0000 1234 5678 9ABC DEFF")))
+            .build();
+        assert_eq!(10, mock.read_number().await.unwrap());
+        assert_eq!(&[] as &[u8], &mock.read_bytes().await.unwrap()[..]);
+        assert_eq!(
+            &hex!("0000 1234 5678 9ABC DEFF"),
+            &mock.read_bytes().await.unwrap()[..]
+        );
+        assert_eq!(None, mock.try_read_number().await.unwrap());
+        assert_eq!(None, mock.try_read_bytes().await.unwrap());
+    }
+
+    #[tokio::test]
+    async fn read_number() {
+        let mut mock = Builder::new().read_number(10).build();
+        assert_eq!(10, mock.read_number().await.unwrap());
+        assert_eq!(None, mock.try_read_number().await.unwrap());
+        assert_eq!(None, mock.try_read_bytes().await.unwrap());
+    }
+
+    #[tokio::test]
+    async fn expect_number() {
+        let mut mock = Builder::new().read_number(10).build();
+        assert_eq!(
+            Error::expected_read_number(),
+            mock.read_bytes().await.unwrap_err()
+        );
+    }
+
+    #[tokio::test]
+    async fn expect_bytes() {
+        let mut mock = Builder::new().read_slice(&[]).build();
+        assert_eq!(
+            Error::expected_read_bytes(),
+            mock.read_number().await.unwrap_err()
+        );
+    }
+
+    #[test]
+    #[should_panic]
+    fn operations_left() {
+        let _ = Builder::new().read_number(10).build();
+    }
+}
diff --git a/tvix/nix-compat/src/nix_daemon/de/mod.rs b/tvix/nix-compat/src/nix_daemon/de/mod.rs
new file mode 100644
index 000000000000..f85ccd8fea0e
--- /dev/null
+++ b/tvix/nix-compat/src/nix_daemon/de/mod.rs
@@ -0,0 +1,225 @@
+use std::error::Error as StdError;
+use std::future::Future;
+use std::ops::RangeInclusive;
+use std::{fmt, io};
+
+use ::bytes::Bytes;
+
+use super::ProtocolVersion;
+
+mod bytes;
+mod collections;
+mod int;
+#[cfg(any(test, feature = "test"))]
+pub mod mock;
+mod reader;
+
+pub use reader::{NixReader, NixReaderBuilder};
+
+/// Like serde the `Error` trait allows `NixRead` implementations to add
+/// custom error handling for `NixDeserialize`.
+pub trait Error: Sized + StdError {
+    /// A totally custom non-specific error.
+    fn custom<T: fmt::Display>(msg: T) -> Self;
+
+    /// Some kind of std::io::Error occured.
+    fn io_error(err: std::io::Error) -> Self {
+        Self::custom(format_args!("There was an I/O error {}", err))
+    }
+
+    /// The data read from `NixRead` is invalid.
+    /// This could be that some bytes were supposed to be valid UFT-8 but weren't.
+    fn invalid_data<T: fmt::Display>(msg: T) -> Self {
+        Self::custom(msg)
+    }
+
+    /// Required data is missing. This is mostly like an EOF
+    fn missing_data<T: fmt::Display>(msg: T) -> Self {
+        Self::custom(msg)
+    }
+}
+
+impl Error for io::Error {
+    fn custom<T: fmt::Display>(msg: T) -> Self {
+        io::Error::new(io::ErrorKind::Other, msg.to_string())
+    }
+
+    fn io_error(err: std::io::Error) -> Self {
+        err
+    }
+
+    fn invalid_data<T: fmt::Display>(msg: T) -> Self {
+        io::Error::new(io::ErrorKind::InvalidData, msg.to_string())
+    }
+
+    fn missing_data<T: fmt::Display>(msg: T) -> Self {
+        io::Error::new(io::ErrorKind::UnexpectedEof, msg.to_string())
+    }
+}
+
+/// A reader of data from the Nix daemon protocol.
+/// Basically there are two basic types in the Nix daemon protocol
+/// u64 and a bytes buffer. Everything else is more or less built on
+/// top of these two types.
+pub trait NixRead: Send {
+    type Error: Error + Send;
+
+    /// Some types are serialized differently depending on the version
+    /// of the protocol and so this can be used for implementing that.
+    fn version(&self) -> ProtocolVersion;
+
+    /// Read a single u64 from the protocol.
+    /// This returns an Option to support graceful shutdown.
+    fn try_read_number(
+        &mut self,
+    ) -> impl Future<Output = Result<Option<u64>, Self::Error>> + Send + '_;
+
+    /// Read bytes from the protocol.
+    /// A size limit on the returned bytes has to be specified.
+    /// This returns an Option to support graceful shutdown.
+    fn try_read_bytes_limited(
+        &mut self,
+        limit: RangeInclusive<usize>,
+    ) -> impl Future<Output = Result<Option<Bytes>, Self::Error>> + Send + '_;
+
+    /// Read bytes from the protocol without a limit.
+    /// The default implementation just calls `try_read_bytes_limited` with a
+    /// limit of `0..=usize::MAX` but other implementations are free to have a
+    /// reader wide limit.
+    /// This returns an Option to support graceful shutdown.
+    fn try_read_bytes(
+        &mut self,
+    ) -> impl Future<Output = Result<Option<Bytes>, Self::Error>> + Send + '_ {
+        self.try_read_bytes_limited(0..=usize::MAX)
+    }
+
+    /// Read a single u64 from the protocol.
+    /// This will return an error if the number could not be read.
+    fn read_number(&mut self) -> impl Future<Output = Result<u64, Self::Error>> + Send + '_ {
+        async move {
+            match self.try_read_number().await? {
+                Some(v) => Ok(v),
+                None => Err(Self::Error::missing_data("unexpected end-of-file")),
+            }
+        }
+    }
+
+    /// Read bytes from the protocol.
+    /// A size limit on the returned bytes has to be specified.
+    /// This will return an error if the number could not be read.
+    fn read_bytes_limited(
+        &mut self,
+        limit: RangeInclusive<usize>,
+    ) -> impl Future<Output = Result<Bytes, Self::Error>> + Send + '_ {
+        async move {
+            match self.try_read_bytes_limited(limit).await? {
+                Some(v) => Ok(v),
+                None => Err(Self::Error::missing_data("unexpected end-of-file")),
+            }
+        }
+    }
+
+    /// Read bytes from the protocol.
+    /// The default implementation just calls `read_bytes_limited` with a
+    /// limit of `0..=usize::MAX` but other implementations are free to have a
+    /// reader wide limit.
+    /// This will return an error if the bytes could not be read.
+    fn read_bytes(&mut self) -> impl Future<Output = Result<Bytes, Self::Error>> + Send + '_ {
+        self.read_bytes_limited(0..=usize::MAX)
+    }
+
+    /// Read a value from the protocol.
+    /// Uses `NixDeserialize::deserialize` to read a value.
+    fn read_value<V: NixDeserialize>(
+        &mut self,
+    ) -> impl Future<Output = Result<V, Self::Error>> + Send + '_ {
+        V::deserialize(self)
+    }
+
+    /// Read a value from the protocol.
+    /// Uses `NixDeserialize::try_deserialize` to read a value.
+    /// This returns an Option to support graceful shutdown.
+    fn try_read_value<V: NixDeserialize>(
+        &mut self,
+    ) -> impl Future<Output = Result<Option<V>, Self::Error>> + Send + '_ {
+        V::try_deserialize(self)
+    }
+}
+
+impl<T: ?Sized + NixRead> NixRead for &mut T {
+    type Error = T::Error;
+
+    fn version(&self) -> ProtocolVersion {
+        (**self).version()
+    }
+
+    fn try_read_number(
+        &mut self,
+    ) -> impl Future<Output = Result<Option<u64>, Self::Error>> + Send + '_ {
+        (**self).try_read_number()
+    }
+
+    fn try_read_bytes_limited(
+        &mut self,
+        limit: RangeInclusive<usize>,
+    ) -> impl Future<Output = Result<Option<Bytes>, Self::Error>> + Send + '_ {
+        (**self).try_read_bytes_limited(limit)
+    }
+
+    fn try_read_bytes(
+        &mut self,
+    ) -> impl Future<Output = Result<Option<Bytes>, Self::Error>> + Send + '_ {
+        (**self).try_read_bytes()
+    }
+
+    fn read_number(&mut self) -> impl Future<Output = Result<u64, Self::Error>> + Send + '_ {
+        (**self).read_number()
+    }
+
+    fn read_bytes_limited(
+        &mut self,
+        limit: RangeInclusive<usize>,
+    ) -> impl Future<Output = Result<Bytes, Self::Error>> + Send + '_ {
+        (**self).read_bytes_limited(limit)
+    }
+
+    fn read_bytes(&mut self) -> impl Future<Output = Result<Bytes, Self::Error>> + Send + '_ {
+        (**self).read_bytes()
+    }
+
+    fn try_read_value<V: NixDeserialize>(
+        &mut self,
+    ) -> impl Future<Output = Result<Option<V>, Self::Error>> + Send + '_ {
+        (**self).try_read_value()
+    }
+
+    fn read_value<V: NixDeserialize>(
+        &mut self,
+    ) -> impl Future<Output = Result<V, Self::Error>> + Send + '_ {
+        (**self).read_value()
+    }
+}
+
+/// A data structure that can be deserialized from the Nix daemon
+/// worker protocol.
+pub trait NixDeserialize: Sized {
+    /// Read a value from the reader.
+    /// This returns an Option to support gracefull shutdown.
+    fn try_deserialize<R>(
+        reader: &mut R,
+    ) -> impl Future<Output = Result<Option<Self>, R::Error>> + Send + '_
+    where
+        R: ?Sized + NixRead + Send;
+
+    fn deserialize<R>(reader: &mut R) -> impl Future<Output = Result<Self, R::Error>> + Send + '_
+    where
+        R: ?Sized + NixRead + Send,
+    {
+        async move {
+            match Self::try_deserialize(reader).await? {
+                Some(v) => Ok(v),
+                None => Err(R::Error::missing_data("unexpected end-of-file")),
+            }
+        }
+    }
+}
diff --git a/tvix/nix-compat/src/nix_daemon/de/reader.rs b/tvix/nix-compat/src/nix_daemon/de/reader.rs
new file mode 100644
index 000000000000..87c623b2220c
--- /dev/null
+++ b/tvix/nix-compat/src/nix_daemon/de/reader.rs
@@ -0,0 +1,527 @@
+use std::future::poll_fn;
+use std::io::{self, Cursor};
+use std::ops::RangeInclusive;
+use std::pin::Pin;
+use std::task::{ready, Context, Poll};
+
+use bytes::{Buf, BufMut, Bytes, BytesMut};
+use pin_project_lite::pin_project;
+use tokio::io::{AsyncBufRead, AsyncRead, AsyncReadExt, ReadBuf};
+
+use crate::nix_daemon::ProtocolVersion;
+use crate::wire::EMPTY_BYTES;
+
+use super::{Error, NixRead};
+
+pub struct NixReaderBuilder {
+    buf: Option<BytesMut>,
+    reserved_buf_size: usize,
+    max_buf_size: usize,
+    version: ProtocolVersion,
+}
+
+impl Default for NixReaderBuilder {
+    fn default() -> Self {
+        Self {
+            buf: Default::default(),
+            reserved_buf_size: 8192,
+            max_buf_size: 8192,
+            version: Default::default(),
+        }
+    }
+}
+
+impl NixReaderBuilder {
+    pub fn set_buffer(mut self, buf: BytesMut) -> Self {
+        self.buf = Some(buf);
+        self
+    }
+
+    pub fn set_reserved_buf_size(mut self, size: usize) -> Self {
+        self.reserved_buf_size = size;
+        self
+    }
+
+    pub fn set_max_buf_size(mut self, size: usize) -> Self {
+        self.max_buf_size = size;
+        self
+    }
+
+    pub fn set_version(mut self, version: ProtocolVersion) -> Self {
+        self.version = version;
+        self
+    }
+
+    pub fn build<R>(self, reader: R) -> NixReader<R> {
+        let buf = self.buf.unwrap_or_else(|| BytesMut::with_capacity(0));
+        NixReader {
+            buf,
+            inner: reader,
+            reserved_buf_size: self.reserved_buf_size,
+            max_buf_size: self.max_buf_size,
+            version: self.version,
+        }
+    }
+}
+
+pin_project! {
+    pub struct NixReader<R> {
+        #[pin]
+        inner: R,
+        buf: BytesMut,
+        reserved_buf_size: usize,
+        max_buf_size: usize,
+        version: ProtocolVersion,
+    }
+}
+
+impl NixReader<Cursor<Vec<u8>>> {
+    pub fn builder() -> NixReaderBuilder {
+        NixReaderBuilder::default()
+    }
+}
+
+impl<R> NixReader<R>
+where
+    R: AsyncReadExt,
+{
+    pub fn new(reader: R) -> NixReader<R> {
+        NixReader::builder().build(reader)
+    }
+
+    pub fn buffer(&self) -> &[u8] {
+        &self.buf[..]
+    }
+
+    #[cfg(test)]
+    pub(crate) fn buffer_mut(&mut self) -> &mut BytesMut {
+        &mut self.buf
+    }
+
+    /// Remaining capacity in internal buffer
+    pub fn remaining_mut(&self) -> usize {
+        self.buf.capacity() - self.buf.len()
+    }
+
+    fn poll_force_fill_buf(
+        mut self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+    ) -> Poll<io::Result<usize>> {
+        // Ensure that buffer has space for at least reserved_buf_size bytes
+        if self.remaining_mut() < self.reserved_buf_size {
+            let me = self.as_mut().project();
+            me.buf.reserve(*me.reserved_buf_size);
+        }
+        let me = self.project();
+        let n = {
+            let dst = me.buf.spare_capacity_mut();
+            let mut buf = ReadBuf::uninit(dst);
+            let ptr = buf.filled().as_ptr();
+            ready!(me.inner.poll_read(cx, &mut buf)?);
+
+            // Ensure the pointer does not change from under us
+            assert_eq!(ptr, buf.filled().as_ptr());
+            buf.filled().len()
+        };
+
+        // SAFETY: This is guaranteed to be the number of initialized (and read)
+        // bytes due to the invariants provided by `ReadBuf::filled`.
+        unsafe {
+            me.buf.advance_mut(n);
+        }
+        Poll::Ready(Ok(n))
+    }
+}
+
+impl<R> NixReader<R>
+where
+    R: AsyncReadExt + Unpin,
+{
+    async fn force_fill(&mut self) -> io::Result<usize> {
+        let mut p = Pin::new(self);
+        let read = poll_fn(|cx| p.as_mut().poll_force_fill_buf(cx)).await?;
+        Ok(read)
+    }
+}
+
+impl<R> NixRead for NixReader<R>
+where
+    R: AsyncReadExt + Send + Unpin,
+{
+    type Error = io::Error;
+
+    fn version(&self) -> ProtocolVersion {
+        self.version
+    }
+
+    async fn try_read_number(&mut self) -> Result<Option<u64>, Self::Error> {
+        let mut buf = [0u8; 8];
+        let read = self.read_buf(&mut &mut buf[..]).await?;
+        if read == 0 {
+            return Ok(None);
+        }
+        if read < 8 {
+            self.read_exact(&mut buf[read..]).await?;
+        }
+        let num = Buf::get_u64_le(&mut &buf[..]);
+        Ok(Some(num))
+    }
+
+    async fn try_read_bytes_limited(
+        &mut self,
+        limit: RangeInclusive<usize>,
+    ) -> Result<Option<Bytes>, Self::Error> {
+        assert!(
+            *limit.end() <= self.max_buf_size,
+            "The limit must be smaller than {}",
+            self.max_buf_size
+        );
+        match self.try_read_number().await? {
+            Some(raw_len) => {
+                // Check that length is in range and convert to usize
+                let len = raw_len
+                    .try_into()
+                    .ok()
+                    .filter(|v| limit.contains(v))
+                    .ok_or_else(|| Self::Error::invalid_data("bytes length out of range"))?;
+
+                // Calculate 64bit aligned length and convert to usize
+                let aligned: usize = raw_len
+                    .checked_add(7)
+                    .map(|v| v & !7)
+                    .ok_or_else(|| Self::Error::invalid_data("bytes length out of range"))?
+                    .try_into()
+                    .map_err(Self::Error::invalid_data)?;
+
+                // Ensure that there is enough space in buffer for contents
+                if self.buf.len() + self.remaining_mut() < aligned {
+                    self.buf.reserve(aligned - self.buf.len());
+                }
+                while self.buf.len() < aligned {
+                    if self.force_fill().await? == 0 {
+                        return Err(Self::Error::missing_data(
+                            "unexpected end-of-file reading bytes",
+                        ));
+                    }
+                }
+                let mut contents = self.buf.split_to(aligned);
+
+                let padding = aligned - len;
+                // Ensure padding is all zeros
+                if contents[len..] != EMPTY_BYTES[..padding] {
+                    return Err(Self::Error::invalid_data("non-zero padding"));
+                }
+
+                contents.truncate(len);
+                Ok(Some(contents.freeze()))
+            }
+            None => Ok(None),
+        }
+    }
+
+    fn try_read_bytes(
+        &mut self,
+    ) -> impl std::future::Future<Output = Result<Option<Bytes>, Self::Error>> + Send + '_ {
+        self.try_read_bytes_limited(0..=self.max_buf_size)
+    }
+
+    fn read_bytes(
+        &mut self,
+    ) -> impl std::future::Future<Output = Result<Bytes, Self::Error>> + Send + '_ {
+        self.read_bytes_limited(0..=self.max_buf_size)
+    }
+}
+
+impl<R: AsyncRead> AsyncRead for NixReader<R> {
+    fn poll_read(
+        mut self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+        buf: &mut ReadBuf<'_>,
+    ) -> Poll<io::Result<()>> {
+        let rem = ready!(self.as_mut().poll_fill_buf(cx))?;
+        let amt = std::cmp::min(rem.len(), buf.remaining());
+        buf.put_slice(&rem[0..amt]);
+        self.consume(amt);
+        Poll::Ready(Ok(()))
+    }
+}
+
+impl<R: AsyncRead> AsyncBufRead for NixReader<R> {
+    fn poll_fill_buf(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
+        if self.as_ref().project_ref().buf.is_empty() {
+            ready!(self.as_mut().poll_force_fill_buf(cx))?;
+        }
+        let me = self.project();
+        Poll::Ready(Ok(&me.buf[..]))
+    }
+
+    fn consume(self: Pin<&mut Self>, amt: usize) {
+        let me = self.project();
+        me.buf.advance(amt)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use std::time::Duration;
+
+    use hex_literal::hex;
+    use rstest::rstest;
+    use tokio_test::io::Builder;
+
+    use super::*;
+    use crate::nix_daemon::de::NixRead;
+
+    #[tokio::test]
+    async fn test_read_u64() {
+        let mock = Builder::new().read(&hex!("0100 0000 0000 0000")).build();
+        let mut reader = NixReader::new(mock);
+
+        assert_eq!(1, reader.read_number().await.unwrap());
+        assert_eq!(hex!(""), reader.buffer());
+
+        let mut buf = Vec::new();
+        reader.read_to_end(&mut buf).await.unwrap();
+        assert_eq!(hex!(""), &buf[..]);
+    }
+
+    #[tokio::test]
+    async fn test_read_u64_rest() {
+        let mock = Builder::new()
+            .read(&hex!("0100 0000 0000 0000 0123 4567 89AB CDEF"))
+            .build();
+        let mut reader = NixReader::new(mock);
+
+        assert_eq!(1, reader.read_number().await.unwrap());
+        assert_eq!(hex!("0123 4567 89AB CDEF"), reader.buffer());
+
+        let mut buf = Vec::new();
+        reader.read_to_end(&mut buf).await.unwrap();
+        assert_eq!(hex!("0123 4567 89AB CDEF"), &buf[..]);
+    }
+
+    #[tokio::test]
+    async fn test_read_u64_partial() {
+        let mock = Builder::new()
+            .read(&hex!("0100 0000"))
+            .wait(Duration::ZERO)
+            .read(&hex!("0000 0000 0123 4567 89AB CDEF"))
+            .wait(Duration::ZERO)
+            .read(&hex!("0100 0000"))
+            .build();
+        let mut reader = NixReader::new(mock);
+
+        assert_eq!(1, reader.read_number().await.unwrap());
+        assert_eq!(hex!("0123 4567 89AB CDEF"), reader.buffer());
+
+        let mut buf = Vec::new();
+        reader.read_to_end(&mut buf).await.unwrap();
+        assert_eq!(hex!("0123 4567 89AB CDEF 0100 0000"), &buf[..]);
+    }
+
+    #[tokio::test]
+    async fn test_read_u64_eof() {
+        let mock = Builder::new().build();
+        let mut reader = NixReader::new(mock);
+
+        assert_eq!(
+            io::ErrorKind::UnexpectedEof,
+            reader.read_number().await.unwrap_err().kind()
+        );
+    }
+
+    #[tokio::test]
+    async fn test_try_read_u64_none() {
+        let mock = Builder::new().build();
+        let mut reader = NixReader::new(mock);
+
+        assert_eq!(None, reader.try_read_number().await.unwrap());
+    }
+
+    #[tokio::test]
+    async fn test_try_read_u64_eof() {
+        let mock = Builder::new().read(&hex!("0100 0000 0000")).build();
+        let mut reader = NixReader::new(mock);
+
+        assert_eq!(
+            io::ErrorKind::UnexpectedEof,
+            reader.try_read_number().await.unwrap_err().kind()
+        );
+    }
+
+    #[tokio::test]
+    async fn test_try_read_u64_eof2() {
+        let mock = Builder::new()
+            .read(&hex!("0100"))
+            .wait(Duration::ZERO)
+            .read(&hex!("0000 0000"))
+            .build();
+        let mut reader = NixReader::new(mock);
+
+        assert_eq!(
+            io::ErrorKind::UnexpectedEof,
+            reader.try_read_number().await.unwrap_err().kind()
+        );
+    }
+
+    #[rstest]
+    #[case::empty(b"", &hex!("0000 0000 0000 0000"))]
+    #[case::one(b")", &hex!("0100 0000 0000 0000 2900 0000 0000 0000"))]
+    #[case::two(b"it", &hex!("0200 0000 0000 0000 6974 0000 0000 0000"))]
+    #[case::three(b"tea", &hex!("0300 0000 0000 0000 7465 6100 0000 0000"))]
+    #[case::four(b"were", &hex!("0400 0000 0000 0000 7765 7265 0000 0000"))]
+    #[case::five(b"where", &hex!("0500 0000 0000 0000 7768 6572 6500 0000"))]
+    #[case::six(b"unwrap", &hex!("0600 0000 0000 0000 756E 7772 6170 0000"))]
+    #[case::seven(b"where's", &hex!("0700 0000 0000 0000 7768 6572 6527 7300"))]
+    #[case::aligned(b"read_tea", &hex!("0800 0000 0000 0000 7265 6164 5F74 6561"))]
+    #[case::more_bytes(b"read_tess", &hex!("0900 0000 0000 0000 7265 6164 5F74 6573 7300 0000 0000 0000"))]
+    #[tokio::test]
+    async fn test_read_bytes(#[case] expected: &[u8], #[case] data: &[u8]) {
+        let mock = Builder::new().read(data).build();
+        let mut reader = NixReader::new(mock);
+        let actual = reader.read_bytes().await.unwrap();
+        assert_eq!(&actual[..], expected);
+    }
+
+    #[tokio::test]
+    async fn test_read_bytes_empty() {
+        let mock = Builder::new().build();
+        let mut reader = NixReader::new(mock);
+
+        assert_eq!(
+            io::ErrorKind::UnexpectedEof,
+            reader.read_bytes().await.unwrap_err().kind()
+        );
+    }
+
+    #[tokio::test]
+    async fn test_try_read_bytes_none() {
+        let mock = Builder::new().build();
+        let mut reader = NixReader::new(mock);
+
+        assert_eq!(None, reader.try_read_bytes().await.unwrap());
+    }
+
+    #[tokio::test]
+    async fn test_try_read_bytes_missing_data() {
+        let mock = Builder::new()
+            .read(&hex!("0500"))
+            .wait(Duration::ZERO)
+            .read(&hex!("0000 0000"))
+            .build();
+        let mut reader = NixReader::new(mock);
+
+        assert_eq!(
+            io::ErrorKind::UnexpectedEof,
+            reader.try_read_bytes().await.unwrap_err().kind()
+        );
+    }
+
+    #[tokio::test]
+    async fn test_try_read_bytes_missing_padding() {
+        let mock = Builder::new()
+            .read(&hex!("0200 0000 0000 0000"))
+            .wait(Duration::ZERO)
+            .read(&hex!("1234"))
+            .build();
+        let mut reader = NixReader::new(mock);
+
+        assert_eq!(
+            io::ErrorKind::UnexpectedEof,
+            reader.try_read_bytes().await.unwrap_err().kind()
+        );
+    }
+
+    #[tokio::test]
+    async fn test_read_bytes_bad_padding() {
+        let mock = Builder::new()
+            .read(&hex!("0200 0000 0000 0000"))
+            .wait(Duration::ZERO)
+            .read(&hex!("1234 0100 0000 0000"))
+            .build();
+        let mut reader = NixReader::new(mock);
+
+        assert_eq!(
+            io::ErrorKind::InvalidData,
+            reader.read_bytes().await.unwrap_err().kind()
+        );
+    }
+
+    #[tokio::test]
+    async fn test_read_bytes_limited_out_of_range() {
+        let mock = Builder::new().read(&hex!("FFFF 0000 0000 0000")).build();
+        let mut reader = NixReader::new(mock);
+
+        assert_eq!(
+            io::ErrorKind::InvalidData,
+            reader.read_bytes_limited(0..=50).await.unwrap_err().kind()
+        );
+    }
+
+    #[tokio::test]
+    async fn test_read_bytes_length_overflow() {
+        let mock = Builder::new().read(&hex!("F9FF FFFF FFFF FFFF")).build();
+        let mut reader = NixReader::builder()
+            .set_max_buf_size(usize::MAX)
+            .build(mock);
+
+        assert_eq!(
+            io::ErrorKind::InvalidData,
+            reader
+                .read_bytes_limited(0..=usize::MAX)
+                .await
+                .unwrap_err()
+                .kind()
+        );
+    }
+
+    // FUTUREWORK: Test this on supported hardware
+    #[tokio::test]
+    #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
+    async fn test_bytes_length_conversion_overflow() {
+        let len = (usize::MAX as u64) + 1;
+        let mock = Builder::new().read(&len.to_le_bytes()).build();
+        let mut reader = NixReader::new(mock);
+        assert_eq!(
+            std::io::ErrorKind::InvalidData,
+            reader.read_value::<usize>().await.unwrap_err().kind()
+        );
+    }
+
+    // FUTUREWORK: Test this on supported hardware
+    #[tokio::test]
+    #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
+    async fn test_bytes_aligned_length_conversion_overflow() {
+        let len = (usize::MAX - 6) as u64;
+        let mock = Builder::new().read(&len.to_le_bytes()).build();
+        let mut reader = NixReader::new(mock);
+        assert_eq!(
+            std::io::ErrorKind::InvalidData,
+            reader.read_value::<usize>().await.unwrap_err().kind()
+        );
+    }
+
+    #[tokio::test]
+    async fn test_buffer_resize() {
+        let mock = Builder::new()
+            .read(&hex!("0100"))
+            .read(&hex!("0000 0000 0000"))
+            .build();
+        let mut reader = NixReader::builder().set_reserved_buf_size(8).build(mock);
+        // buffer has no capacity initially
+        assert_eq!(0, reader.buffer_mut().capacity());
+
+        assert_eq!(2, reader.force_fill().await.unwrap());
+
+        // After first read buffer should have capacity we chose
+        assert_eq!(8, reader.buffer_mut().capacity());
+
+        // Because there was only 6 bytes remaining in buffer,
+        // which is enough to read the last 6 bytes, but we require
+        // capacity for 8 bytes, it doubled the capacity
+        assert_eq!(6, reader.force_fill().await.unwrap());
+        assert_eq!(16, reader.buffer_mut().capacity());
+
+        assert_eq!(1, reader.read_number().await.unwrap());
+    }
+}
diff --git a/tvix/nix-compat/src/nix_daemon/mod.rs b/tvix/nix-compat/src/nix_daemon/mod.rs
index fe652377d1b4..11413e85fd1b 100644
--- a/tvix/nix-compat/src/nix_daemon/mod.rs
+++ b/tvix/nix-compat/src/nix_daemon/mod.rs
@@ -2,3 +2,5 @@ pub mod worker_protocol;
 
 mod protocol_version;
 pub use protocol_version::ProtocolVersion;
+
+pub mod de;
diff --git a/tvix/nix-compat/src/nix_daemon/protocol_version.rs b/tvix/nix-compat/src/nix_daemon/protocol_version.rs
index 8fd2b085c962..19da28d484dd 100644
--- a/tvix/nix-compat/src/nix_daemon/protocol_version.rs
+++ b/tvix/nix-compat/src/nix_daemon/protocol_version.rs
@@ -1,3 +1,6 @@
+/// The latest version that is currently supported by nix-compat.
+static DEFAULT_PROTOCOL_VERSION: ProtocolVersion = ProtocolVersion::from_parts(1, 37);
+
 /// Protocol versions are represented as a u16.
 /// The upper 8 bits are the major version, the lower bits the minor.
 /// This is not aware of any endianness, use [crate::wire::read_u64] to get an
@@ -20,6 +23,12 @@ impl ProtocolVersion {
     }
 }
 
+impl Default for ProtocolVersion {
+    fn default() -> Self {
+        DEFAULT_PROTOCOL_VERSION
+    }
+}
+
 impl PartialOrd for ProtocolVersion {
     fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
         Some(self.cmp(other))
@@ -45,6 +54,13 @@ impl From<u16> for ProtocolVersion {
     }
 }
 
+#[cfg(any(test, feature = "test"))]
+impl From<(u8, u8)> for ProtocolVersion {
+    fn from((major, minor): (u8, u8)) -> Self {
+        Self::from_parts(major, minor)
+    }
+}
+
 impl TryFrom<u64> for ProtocolVersion {
     type Error = &'static str;
 
diff --git a/tvix/nix-compat/src/nix_http/mod.rs b/tvix/nix-compat/src/nix_http/mod.rs
new file mode 100644
index 000000000000..89ba147b8071
--- /dev/null
+++ b/tvix/nix-compat/src/nix_http/mod.rs
@@ -0,0 +1,115 @@
+use tracing::trace;
+
+use crate::nixbase32;
+
+/// The mime type used for NAR files, both compressed and uncompressed
+pub const MIME_TYPE_NAR: &str = "application/x-nix-nar";
+/// The mime type used for NARInfo files
+pub const MIME_TYPE_NARINFO: &str = "text/x-nix-narinfo";
+/// The mime type used for the `nix-cache-info` file
+pub const MIME_TYPE_CACHE_INFO: &str = "text/x-nix-cache-info";
+
+/// Parses a `14cx20k6z4hq508kqi2lm79qfld5f9mf7kiafpqsjs3zlmycza0k.nar`
+/// string and returns the nixbase32-decoded digest, as well as the compression
+/// suffix (which might be empty).
+pub fn parse_nar_str(s: &str) -> Option<([u8; 32], &str)> {
+    if !s.is_char_boundary(52) {
+        trace!("invalid string, no char boundary at 52");
+        return None;
+    }
+
+    let (hash_str, suffix) = s.split_at(52);
+
+    // we know hash_str is 52 bytes, so it's ok to unwrap here.
+    let hash_str_fixed: [u8; 52] = hash_str.as_bytes().try_into().unwrap();
+
+    match suffix.strip_prefix(".nar") {
+        Some(compression_suffix) => match nixbase32::decode_fixed(hash_str_fixed) {
+            Err(e) => {
+                trace!(err=%e, "invalid nixbase32 encoding");
+                None
+            }
+            Ok(digest) => Some((digest, compression_suffix)),
+        },
+        None => {
+            trace!("no .nar suffix");
+            None
+        }
+    }
+}
+
+/// Parses a `3mzh8lvgbynm9daj7c82k2sfsfhrsfsy.narinfo` string and returns the
+/// nixbase32-decoded digest.
+pub fn parse_narinfo_str(s: &str) -> Option<[u8; 20]> {
+    if !s.is_char_boundary(32) {
+        trace!("invalid string, no char boundary at 32");
+        return None;
+    }
+
+    match s.split_at(32) {
+        (hash_str, ".narinfo") => {
+            // we know this is 32 bytes, so it's ok to unwrap here.
+            let hash_str_fixed: [u8; 32] = hash_str.as_bytes().try_into().unwrap();
+
+            match nixbase32::decode_fixed(hash_str_fixed) {
+                Err(e) => {
+                    trace!(err=%e, "invalid nixbase32 encoding");
+                    None
+                }
+                Ok(digest) => Some(digest),
+            }
+        }
+        _ => {
+            trace!("invalid string, no .narinfo suffix");
+            None
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::{parse_nar_str, parse_narinfo_str};
+    use hex_literal::hex;
+
+    #[test]
+    fn parse_nar_str_success() {
+        assert_eq!(
+            (
+                hex!("13a8cf7ca57f68a9f1752acee36a72a55187d3a954443c112818926f26109d91"),
+                ""
+            ),
+            parse_nar_str("14cx20k6z4hq508kqi2lm79qfld5f9mf7kiafpqsjs3zlmycza0k.nar").unwrap()
+        );
+
+        assert_eq!(
+            (
+                hex!("13a8cf7ca57f68a9f1752acee36a72a55187d3a954443c112818926f26109d91"),
+                ".xz"
+            ),
+            parse_nar_str("14cx20k6z4hq508kqi2lm79qfld5f9mf7kiafpqsjs3zlmycza0k.nar.xz").unwrap()
+        )
+    }
+
+    #[test]
+    fn parse_nar_str_failure() {
+        assert!(parse_nar_str("14cx20k6z4hq508kqi2lm79qfld5f9mf7kiafpqsjs3zlmycza0").is_none());
+        assert!(
+            parse_nar_str("14cx20k6z4hq508kqi2lm79qfld5f9mf7kiafpqsjs3zlmycza0🦊.nar").is_none()
+        )
+    }
+    #[test]
+    fn parse_narinfo_str_success() {
+        assert_eq!(
+            hex!("8a12321522fd91efbd60ebb2481af88580f61600"),
+            parse_narinfo_str("00bgd045z0d4icpbc2yyz4gx48ak44la.narinfo").unwrap()
+        );
+    }
+
+    #[test]
+    fn parse_narinfo_str_failure() {
+        assert!(parse_narinfo_str("00bgd045z0d4icpbc2yyz4gx48ak44la").is_none());
+        assert!(parse_narinfo_str("/00bgd045z0d4icpbc2yyz4gx48ak44la").is_none());
+        assert!(parse_narinfo_str("000000").is_none());
+        assert!(parse_narinfo_str("00bgd045z0d4icpbc2yyz4gx48ak44l🦊.narinfo").is_none());
+    }
+}
diff --git a/tvix/nix-compat/src/nixhash/ca_hash.rs b/tvix/nix-compat/src/nixhash/ca_hash.rs
index 423531696afe..e6cbaf5b710a 100644
--- a/tvix/nix-compat/src/nixhash/ca_hash.rs
+++ b/tvix/nix-compat/src/nixhash/ca_hash.rs
@@ -72,7 +72,8 @@ impl CAHash {
     /// - `text:sha256:$nixbase32sha256digest`
     /// - `fixed:r:$algo:$nixbase32digest`
     /// - `fixed:$algo:$nixbase32digest`
-    /// which is the format that's used in the NARInfo for example.
+    ///
+    /// These formats are used in NARInfo, for example.
     pub fn from_nix_hex_str(s: &str) -> Option<Self> {
         let (tag, s) = s.split_once(':')?;
 
@@ -108,11 +109,13 @@ impl CAHash {
     /// know whether we have a invalid or a missing NixHashWithMode structure in another structure,
     /// e.g. Output.
     /// This means we have this combinatorial situation:
+    ///
     /// - no hash, no hashAlgo: no [CAHash] so we return Ok(None).
     /// - present hash, missing hashAlgo: invalid, we will return missing_field
     /// - missing hash, present hashAlgo: same
     /// - present hash, present hashAlgo: either we return ourselves or a type/value validation
-    /// error.
+    ///   error.
+    ///
     /// This function is for internal consumption regarding those needs until we have a better
     /// solution. Now this is said, let's explain how this works.
     ///
diff --git a/tvix/nix-compat/src/store_path/mod.rs b/tvix/nix-compat/src/store_path/mod.rs
index 707c41a92d74..177cc96ce20f 100644
--- a/tvix/nix-compat/src/store_path/mod.rs
+++ b/tvix/nix-compat/src/store_path/mod.rs
@@ -2,14 +2,15 @@ use crate::nixbase32;
 use data_encoding::{DecodeError, BASE64};
 use serde::{Deserialize, Serialize};
 use std::{
-    fmt,
-    path::PathBuf,
+    fmt::{self, Display},
+    ops::Deref,
+    path::Path,
     str::{self, FromStr},
 };
 use thiserror;
 
 #[cfg(target_family = "unix")]
-use std::os::unix::ffi::OsStringExt;
+use std::os::unix::ffi::OsStrExt;
 
 mod utils;
 
@@ -54,17 +55,26 @@ pub enum Error {
 /// A [StorePath] does not encode any additional subpath "inside" the store
 /// path.
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
-pub struct StorePath {
+pub struct StorePath<S>
+where
+    S: std::cmp::Eq + std::cmp::PartialEq,
+{
     digest: [u8; DIGEST_SIZE],
-    name: Box<str>,
+    name: S,
 }
+/// Like [StorePath], but without a heap allocation for the name.
+/// Used by [StorePath] for parsing.
+pub type StorePathRef<'a> = StorePath<&'a str>;
 
-impl StorePath {
+impl<S> StorePath<S>
+where
+    S: std::cmp::Eq + Deref<Target = str>,
+{
     pub fn digest(&self) -> &[u8; DIGEST_SIZE] {
         &self.digest
     }
 
-    pub fn name(&self) -> &str {
+    pub fn name(&self) -> &S {
         &self.name
     }
 
@@ -74,60 +84,101 @@ impl StorePath {
             name: &self.name,
         }
     }
-}
 
-impl PartialOrd for StorePath {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        Some(self.cmp(other))
+    pub fn to_owned(&self) -> StorePath<String> {
+        StorePath {
+            digest: self.digest,
+            name: self.name.to_string(),
+        }
     }
-}
 
-/// `StorePath`s are sorted by their reverse digest to match the sorting order
-/// of the nixbase32-encoded string.
-impl Ord for StorePath {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        self.as_ref().cmp(&other.as_ref())
+    /// Construct a [StorePath] by passing the `$digest-$name` string
+    /// that comes after [STORE_DIR_WITH_SLASH].
+    pub fn from_bytes<'a>(s: &'a [u8]) -> Result<Self, Error>
+    where
+        S: From<&'a str>,
+    {
+        // the whole string needs to be at least:
+        //
+        // - 32 characters (encoded hash)
+        // - 1 dash
+        // - 1 character for the name
+        if s.len() < ENCODED_DIGEST_SIZE + 2 {
+            Err(Error::InvalidLength)?
+        }
+
+        let digest = nixbase32::decode_fixed(&s[..ENCODED_DIGEST_SIZE])?;
+
+        if s[ENCODED_DIGEST_SIZE] != b'-' {
+            return Err(Error::MissingDash);
+        }
+
+        Ok(StorePath {
+            digest,
+            name: validate_name(&s[ENCODED_DIGEST_SIZE + 1..])?.into(),
+        })
     }
-}
 
-impl FromStr for StorePath {
-    type Err = Error;
+    /// Construct a [StorePathRef] from a name and digest.
+    /// The name is validated, and the digest checked for size.
+    pub fn from_name_and_digest<'a>(name: &'a str, digest: &[u8]) -> Result<Self, Error>
+    where
+        S: From<&'a str>,
+    {
+        let digest_fixed = digest.try_into().map_err(|_| Error::InvalidLength)?;
+        Self::from_name_and_digest_fixed(name, digest_fixed)
+    }
 
-    /// Construct a [StorePath] by passing the `$digest-$name` string
-    /// that comes after [STORE_DIR_WITH_SLASH].
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        Self::from_bytes(s.as_bytes())
+    /// Construct a [StorePathRef] from a name and digest of correct length.
+    /// The name is validated.
+    pub fn from_name_and_digest_fixed<'a>(
+        name: &'a str,
+        digest: [u8; DIGEST_SIZE],
+    ) -> Result<Self, Error>
+    where
+        S: From<&'a str>,
+    {
+        Ok(Self {
+            name: validate_name(name.as_bytes())?.into(),
+            digest,
+        })
     }
-}
 
-impl StorePath {
-    /// Construct a [StorePath] by passing the `$digest-$name` string
-    /// that comes after [STORE_DIR_WITH_SLASH].
-    pub fn from_bytes(s: &[u8]) -> Result<StorePath, Error> {
-        Ok(StorePathRef::from_bytes(s)?.to_owned())
+    /// Construct a [StorePathRef] from an absolute store path string.
+    /// This is equivalent to calling [StorePathRef::from_bytes], but stripping
+    /// the [STORE_DIR_WITH_SLASH] prefix before.
+    pub fn from_absolute_path<'a>(s: &'a [u8]) -> Result<Self, Error>
+    where
+        S: From<&'a str>,
+    {
+        match s.strip_prefix(STORE_DIR_WITH_SLASH.as_bytes()) {
+            Some(s_stripped) => Self::from_bytes(s_stripped),
+            None => Err(Error::MissingStoreDir),
+        }
     }
 
     /// Decompose a string into a [StorePath] and a [PathBuf] containing the
     /// rest of the path, or an error.
     #[cfg(target_family = "unix")]
-    pub fn from_absolute_path_full(s: &str) -> Result<(StorePath, PathBuf), Error> {
+    pub fn from_absolute_path_full<'a>(s: &'a str) -> Result<(Self, &'a Path), Error>
+    where
+        S: From<&'a str>,
+    {
         // strip [STORE_DIR_WITH_SLASH] from s
+
         match s.strip_prefix(STORE_DIR_WITH_SLASH) {
             None => Err(Error::MissingStoreDir),
             Some(rest) => {
-                // put rest in a PathBuf
-                let mut p = PathBuf::new();
-                p.push(rest);
-
-                let mut it = p.components();
+                let mut it = Path::new(rest).components();
 
                 // The first component of the rest must be parse-able as a [StorePath]
                 if let Some(first_component) = it.next() {
                     // convert first component to StorePath
-                    let first_component_bytes = first_component.as_os_str().to_owned().into_vec();
-                    let store_path = StorePath::from_bytes(&first_component_bytes)?;
+                    let store_path = StorePath::from_bytes(first_component.as_os_str().as_bytes())?;
+
                     // collect rest
-                    let rest_buf: PathBuf = it.collect();
+                    let rest_buf = it.as_path();
+
                     Ok((store_path, rest_buf))
                 } else {
                     Err(Error::InvalidLength) // Well, or missing "/"?
@@ -139,139 +190,48 @@ impl StorePath {
     /// Returns an absolute store path string.
     /// That is just the string representation, prefixed with the store prefix
     /// ([STORE_DIR_WITH_SLASH]),
-    pub fn to_absolute_path(&self) -> String {
-        let sp_ref: StorePathRef = self.into();
-        sp_ref.to_absolute_path()
-    }
-}
-
-impl<'de> Deserialize<'de> for StorePath {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        let r = <StorePathRef<'de> as Deserialize<'de>>::deserialize(deserializer)?;
-        Ok(r.to_owned())
-    }
-}
-
-impl Serialize for StorePath {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    pub fn to_absolute_path(&self) -> String
     where
-        S: serde::Serializer,
+        S: Display,
     {
-        let r: StorePathRef = self.into();
-        r.serialize(serializer)
-    }
-}
-
-/// Like [StorePath], but without a heap allocation for the name.
-/// Used by [StorePath] for parsing.
-///
-#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
-pub struct StorePathRef<'a> {
-    digest: [u8; DIGEST_SIZE],
-    name: &'a str,
-}
-
-impl<'a> From<&'a StorePath> for StorePathRef<'a> {
-    fn from(&StorePath { digest, ref name }: &'a StorePath) -> Self {
-        StorePathRef { digest, name }
+        format!("{}{}", STORE_DIR_WITH_SLASH, self)
     }
 }
 
-impl<'a> PartialOrd for StorePathRef<'a> {
+impl<S> PartialOrd for StorePath<S>
+where
+    S: std::cmp::PartialEq + std::cmp::Eq,
+{
     fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
         Some(self.cmp(other))
     }
 }
 
-/// `StorePathRef`s are sorted by their reverse digest to match the sorting order
+/// `StorePath`s are sorted by their reverse digest to match the sorting order
 /// of the nixbase32-encoded string.
-impl<'a> Ord for StorePathRef<'a> {
+impl<S> Ord for StorePath<S>
+where
+    S: std::cmp::PartialEq + std::cmp::Eq,
+{
     fn cmp(&self, other: &Self) -> std::cmp::Ordering {
         self.digest.iter().rev().cmp(other.digest.iter().rev())
     }
 }
 
-impl<'a> StorePathRef<'a> {
-    pub fn digest(&self) -> &[u8; DIGEST_SIZE] {
-        &self.digest
-    }
-
-    pub fn name(&self) -> &'a str {
-        self.name
-    }
-
-    pub fn to_owned(&self) -> StorePath {
-        StorePath {
-            digest: self.digest,
-            name: self.name.into(),
-        }
-    }
-
-    /// Construct a [StorePathRef] from a name and digest.
-    /// The name is validated, and the digest checked for size.
-    pub fn from_name_and_digest(name: &'a str, digest: &[u8]) -> Result<Self, Error> {
-        let digest_fixed = digest.try_into().map_err(|_| Error::InvalidLength)?;
-        Self::from_name_and_digest_fixed(name, digest_fixed)
-    }
-
-    /// Construct a [StorePathRef] from a name and digest of correct length.
-    /// The name is validated.
-    pub fn from_name_and_digest_fixed(
-        name: &'a str,
-        digest: [u8; DIGEST_SIZE],
-    ) -> Result<Self, Error> {
-        Ok(Self {
-            name: validate_name(name.as_bytes())?,
-            digest,
-        })
-    }
-
-    /// Construct a [StorePathRef] from an absolute store path string.
-    /// This is equivalent to calling [StorePathRef::from_bytes], but stripping
-    /// the [STORE_DIR_WITH_SLASH] prefix before.
-    pub fn from_absolute_path(s: &'a [u8]) -> Result<Self, Error> {
-        match s.strip_prefix(STORE_DIR_WITH_SLASH.as_bytes()) {
-            Some(s_stripped) => Self::from_bytes(s_stripped),
-            None => Err(Error::MissingStoreDir),
-        }
-    }
+impl<'a, 'b: 'a> FromStr for StorePath<String> {
+    type Err = Error;
 
-    /// Construct a [StorePathRef] by passing the `$digest-$name` string
+    /// Construct a [StorePath] by passing the `$digest-$name` string
     /// that comes after [STORE_DIR_WITH_SLASH].
-    pub fn from_bytes(s: &'a [u8]) -> Result<Self, Error> {
-        // the whole string needs to be at least:
-        //
-        // - 32 characters (encoded hash)
-        // - 1 dash
-        // - 1 character for the name
-        if s.len() < ENCODED_DIGEST_SIZE + 2 {
-            Err(Error::InvalidLength)?
-        }
-
-        let digest = nixbase32::decode_fixed(&s[..ENCODED_DIGEST_SIZE])?;
-
-        if s[ENCODED_DIGEST_SIZE] != b'-' {
-            return Err(Error::MissingDash);
-        }
-
-        Ok(StorePathRef {
-            digest,
-            name: validate_name(&s[ENCODED_DIGEST_SIZE + 1..])?,
-        })
-    }
-
-    /// Returns an absolute store path string.
-    /// That is just the string representation, prefixed with the store prefix
-    /// ([STORE_DIR_WITH_SLASH]),
-    pub fn to_absolute_path(&self) -> String {
-        format!("{}{}", STORE_DIR_WITH_SLASH, self)
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        StorePath::<String>::from_bytes(s.as_bytes())
     }
 }
 
-impl<'de: 'a, 'a> Deserialize<'de> for StorePathRef<'a> {
+impl<'a, 'de: 'a, S> Deserialize<'de> for StorePath<S>
+where
+    S: std::cmp::Eq + Deref<Target = str> + From<&'a str>,
+{
     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     where
         D: serde::Deserializer<'de>,
@@ -284,16 +244,19 @@ impl<'de: 'a, 'a> Deserialize<'de> for StorePathRef<'a> {
                 &"store path prefix",
             )
         })?;
-        StorePathRef::from_bytes(stripped.as_bytes()).map_err(|_| {
+        StorePath::from_bytes(stripped.as_bytes()).map_err(|_| {
             serde::de::Error::invalid_value(serde::de::Unexpected::Str(string), &"StorePath")
         })
     }
 }
 
-impl Serialize for StorePathRef<'_> {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+impl<S> Serialize for StorePath<S>
+where
+    S: std::cmp::Eq + Deref<Target = str> + Display,
+{
+    fn serialize<SR>(&self, serializer: SR) -> Result<SR::Ok, SR::Error>
     where
-        S: serde::Serializer,
+        SR: serde::Serializer,
     {
         let string: String = self.to_absolute_path();
         string.serialize(serializer)
@@ -347,13 +310,10 @@ pub(crate) fn validate_name(s: &(impl AsRef<[u8]> + ?Sized)) -> Result<&str, Err
     Ok(unsafe { str::from_utf8_unchecked(s) })
 }
 
-impl fmt::Display for StorePath {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        StorePathRef::from(self).fmt(f)
-    }
-}
-
-impl fmt::Display for StorePathRef<'_> {
+impl<S> fmt::Display for StorePath<S>
+where
+    S: fmt::Display + std::cmp::Eq,
+{
     /// The string representation of a store path starts with a digest (20
     /// bytes), [crate::nixbase32]-encoded, followed by a `-`,
     /// and ends with the name.
@@ -386,12 +346,12 @@ mod tests {
     fn happy_path() {
         let example_nix_path_str =
             "00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432";
-        let nixpath = StorePath::from_bytes(example_nix_path_str.as_bytes())
+        let nixpath = StorePathRef::from_bytes(example_nix_path_str.as_bytes())
             .expect("Error parsing example string");
 
         let expected_digest: [u8; DIGEST_SIZE] = hex!("8a12321522fd91efbd60ebb2481af88580f61600");
 
-        assert_eq!("net-tools-1.60_p20170221182432", nixpath.name());
+        assert_eq!("net-tools-1.60_p20170221182432", *nixpath.name());
         assert_eq!(nixpath.digest, expected_digest);
 
         assert_eq!(example_nix_path_str, nixpath.to_string())
@@ -426,8 +386,8 @@ mod tests {
             if w.len() < 2 {
                 continue;
             }
-            let (pa, _) = StorePath::from_absolute_path_full(w[0]).expect("parseable");
-            let (pb, _) = StorePath::from_absolute_path_full(w[1]).expect("parseable");
+            let (pa, _) = StorePathRef::from_absolute_path_full(w[0]).expect("parseable");
+            let (pb, _) = StorePathRef::from_absolute_path_full(w[1]).expect("parseable");
             assert_eq!(
                 Ordering::Less,
                 pa.cmp(&pb),
@@ -448,36 +408,38 @@ mod tests {
     /// https://github.com/NixOS/nix/pull/9867 (revert-of-revert)
     #[test]
     fn starts_with_dot() {
-        StorePath::from_bytes(b"fli4bwscgna7lpm7v5xgnjxrxh0yc7ra-.gitignore")
+        StorePathRef::from_bytes(b"fli4bwscgna7lpm7v5xgnjxrxh0yc7ra-.gitignore")
             .expect("must succeed");
     }
 
     #[test]
     fn empty_name() {
-        StorePath::from_bytes(b"00bgd045z0d4icpbc2yy-").expect_err("must fail");
+        StorePathRef::from_bytes(b"00bgd045z0d4icpbc2yy-").expect_err("must fail");
     }
 
     #[test]
     fn excessive_length() {
-        StorePath::from_bytes(b"00bgd045z0d4icpbc2yy-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
+        StorePathRef::from_bytes(b"00bgd045z0d4icpbc2yy-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
             .expect_err("must fail");
     }
 
     #[test]
     fn invalid_hash_length() {
-        StorePath::from_bytes(b"00bgd045z0d4icpbc2yy-net-tools-1.60_p20170221182432")
+        StorePathRef::from_bytes(b"00bgd045z0d4icpbc2yy-net-tools-1.60_p20170221182432")
             .expect_err("must fail");
     }
 
     #[test]
     fn invalid_encoding_hash() {
-        StorePath::from_bytes(b"00bgd045z0d4icpbc2yyz4gx48aku4la-net-tools-1.60_p20170221182432")
-            .expect_err("must fail");
+        StorePathRef::from_bytes(
+            b"00bgd045z0d4icpbc2yyz4gx48aku4la-net-tools-1.60_p20170221182432",
+        )
+        .expect_err("must fail");
     }
 
     #[test]
     fn more_than_just_the_bare_nix_store_path() {
-        StorePath::from_bytes(
+        StorePathRef::from_bytes(
             b"00bgd045z0d4icpbc2yyz4gx48aku4la-net-tools-1.60_p20170221182432/bin/arp",
         )
         .expect_err("must fail");
@@ -485,7 +447,7 @@ mod tests {
 
     #[test]
     fn no_dash_between_hash_and_name() {
-        StorePath::from_bytes(b"00bgd045z0d4icpbc2yyz4gx48ak44lanet-tools-1.60_p20170221182432")
+        StorePathRef::from_bytes(b"00bgd045z0d4icpbc2yyz4gx48ak44lanet-tools-1.60_p20170221182432")
             .expect_err("must fail");
     }
 
@@ -534,7 +496,7 @@ mod tests {
 
     #[test]
     fn serialize_owned() {
-        let nixpath_actual = StorePath::from_bytes(
+        let nixpath_actual = StorePathRef::from_bytes(
             b"00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432",
         )
         .expect("can parse");
@@ -578,7 +540,8 @@ mod tests {
         let store_path_str_json =
             "\"/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432\"";
 
-        let store_path: StorePath = serde_json::from_str(store_path_str_json).expect("valid json");
+        let store_path: StorePath<String> =
+            serde_json::from_str(store_path_str_json).expect("valid json");
 
         assert_eq!(
             "/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432",
@@ -601,7 +564,7 @@ mod tests {
         StorePath::from_bytes(b"00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432").unwrap(), PathBuf::from("bin/arp/"))]
     fn from_absolute_path_full(
         #[case] s: &str,
-        #[case] exp_store_path: StorePath,
+        #[case] exp_store_path: StorePath<&str>,
         #[case] exp_path: PathBuf,
     ) {
         let (actual_store_path, actual_path) =
@@ -615,15 +578,15 @@ mod tests {
     fn from_absolute_path_errors() {
         assert_eq!(
             Error::InvalidLength,
-            StorePath::from_absolute_path_full("/nix/store/").expect_err("must fail")
+            StorePathRef::from_absolute_path_full("/nix/store/").expect_err("must fail")
         );
         assert_eq!(
             Error::InvalidLength,
-            StorePath::from_absolute_path_full("/nix/store/foo").expect_err("must fail")
+            StorePathRef::from_absolute_path_full("/nix/store/foo").expect_err("must fail")
         );
         assert_eq!(
             Error::MissingStoreDir,
-            StorePath::from_absolute_path_full(
+            StorePathRef::from_absolute_path_full(
                 "00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432"
             )
             .expect_err("must fail")
diff --git a/tvix/nix-compat/src/store_path/utils.rs b/tvix/nix-compat/src/store_path/utils.rs
index d6f390db85c2..4bfbb72fcdde 100644
--- a/tvix/nix-compat/src/store_path/utils.rs
+++ b/tvix/nix-compat/src/store_path/utils.rs
@@ -1,6 +1,6 @@
 use crate::nixbase32;
 use crate::nixhash::{CAHash, NixHash};
-use crate::store_path::{Error, StorePathRef, STORE_DIR};
+use crate::store_path::{Error, StorePath, StorePathRef, STORE_DIR};
 use data_encoding::HEXLOWER;
 use sha2::{Digest, Sha256};
 use thiserror;
@@ -43,11 +43,17 @@ pub fn compress_hash<const OUTPUT_SIZE: usize>(input: &[u8]) -> [u8; OUTPUT_SIZE
 /// derivation or a literal text file that may contain references.
 /// If you don't want to have to pass the entire contents, you might want to use
 /// [build_ca_path] instead.
-pub fn build_text_path<S: AsRef<str>, I: IntoIterator<Item = S>, C: AsRef<[u8]>>(
-    name: &str,
+pub fn build_text_path<'a, S, SP, I, C>(
+    name: &'a str,
     content: C,
     references: I,
-) -> Result<StorePathRef<'_>, BuildStorePathError> {
+) -> Result<StorePath<SP>, BuildStorePathError>
+where
+    S: AsRef<str>,
+    SP: std::cmp::Eq + std::ops::Deref<Target = str> + std::convert::From<&'a str>,
+    I: IntoIterator<Item = S>,
+    C: AsRef<[u8]>,
+{
     // produce the sha256 digest of the contents
     let content_digest = Sha256::new_with_prefix(content).finalize().into();
 
@@ -55,12 +61,17 @@ pub fn build_text_path<S: AsRef<str>, I: IntoIterator<Item = S>, C: AsRef<[u8]>>
 }
 
 /// This builds a store path from a [CAHash] and a list of references.
-pub fn build_ca_path<'a, S: AsRef<str>, I: IntoIterator<Item = S>>(
+pub fn build_ca_path<'a, S, SP, I>(
     name: &'a str,
     ca_hash: &CAHash,
     references: I,
     self_reference: bool,
-) -> Result<StorePathRef<'a>, BuildStorePathError> {
+) -> Result<StorePath<SP>, BuildStorePathError>
+where
+    S: AsRef<str>,
+    SP: std::cmp::Eq + std::ops::Deref<Target = str> + std::convert::From<&'a str>,
+    I: IntoIterator<Item = S>,
+{
     // self references are only allowed for CAHash::Nar(NixHash::Sha256(_)).
     if self_reference && matches!(ca_hash, CAHash::Nar(NixHash::Sha256(_))) {
         return Err(BuildStorePathError::InvalidReference());
@@ -145,17 +156,20 @@ pub fn build_output_path<'a>(
 /// bytes.
 /// Inside a StorePath, that digest is printed nixbase32-encoded
 /// (32 characters).
-fn build_store_path_from_fingerprint_parts<'a>(
+fn build_store_path_from_fingerprint_parts<'a, S>(
     ty: &str,
     inner_digest: &[u8; 32],
     name: &'a str,
-) -> Result<StorePathRef<'a>, Error> {
+) -> Result<StorePath<S>, Error>
+where
+    S: std::cmp::Eq + std::ops::Deref<Target = str> + std::convert::From<&'a str>,
+{
     let fingerprint = format!(
         "{ty}:sha256:{}:{STORE_DIR}:{name}",
         HEXLOWER.encode(inner_digest)
     );
     // name validation happens in here.
-    StorePathRef::from_name_and_digest_fixed(
+    StorePath::from_name_and_digest_fixed(
         name,
         compress_hash(&Sha256::new_with_prefix(fingerprint).finalize()),
     )
@@ -216,7 +230,7 @@ mod test {
         // nix-repl> builtins.toFile "foo" "bar"
         // "/nix/store/vxjiwkjkn7x4079qvh1jkl5pn05j2aw0-foo"
 
-        let store_path = build_text_path("foo", "bar", Vec::<String>::new())
+        let store_path: StorePathRef = build_text_path("foo", "bar", Vec::<String>::new())
             .expect("build_store_path() should succeed");
 
         assert_eq!(
@@ -232,11 +246,11 @@ mod test {
         // nix-repl> builtins.toFile "baz" "${builtins.toFile "foo" "bar"}"
         // "/nix/store/5xd714cbfnkz02h2vbsj4fm03x3f15nf-baz"
 
-        let inner = build_text_path("foo", "bar", Vec::<String>::new())
+        let inner: StorePathRef = build_text_path("foo", "bar", Vec::<String>::new())
             .expect("path_with_references() should succeed");
         let inner_path = inner.to_absolute_path();
 
-        let outer = build_text_path("baz", &inner_path, vec![inner_path.as_str()])
+        let outer: StorePathRef = build_text_path("baz", &inner_path, vec![inner_path.as_str()])
             .expect("path_with_references() should succeed");
 
         assert_eq!(
@@ -247,7 +261,7 @@ mod test {
 
     #[test]
     fn build_sha1_path() {
-        let outer = build_ca_path(
+        let outer: StorePathRef = build_ca_path(
             "bar",
             &CAHash::Nar(NixHash::Sha1(hex!(
                 "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
@@ -272,7 +286,7 @@ mod test {
         //
         // $ nix store make-content-addressed /nix/store/5xd714cbfnkz02h2vbsj4fm03x3f15nf-baz
         // rewrote '/nix/store/5xd714cbfnkz02h2vbsj4fm03x3f15nf-baz' to '/nix/store/s89y431zzhmdn3k8r96rvakryddkpv2v-baz'
-        let outer = build_ca_path(
+        let outer: StorePathRef = build_ca_path(
             "baz",
             &CAHash::Nar(NixHash::Sha256(
                 nixbase32::decode(b"1xqkzcb3909fp07qngljr4wcdnrh1gdam1m2n29i6hhrxlmkgkv1")
diff --git a/tvix/nix-compat/src/wire/bytes/mod.rs b/tvix/nix-compat/src/wire/bytes/mod.rs
index 47bfb5eabacf..74adfb49b6a4 100644
--- a/tvix/nix-compat/src/wire/bytes/mod.rs
+++ b/tvix/nix-compat/src/wire/bytes/mod.rs
@@ -1,9 +1,12 @@
+#[cfg(feature = "async")]
+use std::mem::MaybeUninit;
 use std::{
     io::{Error, ErrorKind},
-    mem::MaybeUninit,
     ops::RangeInclusive,
 };
-use tokio::io::{self, AsyncReadExt, AsyncWriteExt, ReadBuf};
+#[cfg(feature = "async")]
+use tokio::io::ReadBuf;
+use tokio::io::{self, AsyncReadExt, AsyncWriteExt};
 
 pub(crate) mod reader;
 pub use reader::BytesReader;
@@ -11,7 +14,7 @@ mod writer;
 pub use writer::BytesWriter;
 
 /// 8 null bytes, used to write out padding.
-const EMPTY_BYTES: &[u8; 8] = &[0u8; 8];
+pub(crate) const EMPTY_BYTES: &[u8; 8] = &[0u8; 8];
 
 /// The length of the size field, in bytes is always 8.
 const LEN_SIZE: usize = 8;
@@ -79,6 +82,7 @@ where
     Ok(buf)
 }
 
+#[cfg(feature = "async")]
 pub(crate) async fn read_bytes_buf<'a, const N: usize, R>(
     reader: &mut R,
     buf: &'a mut [MaybeUninit<u8>; N],
@@ -132,6 +136,7 @@ where
 }
 
 /// SAFETY: The bytes have to actually be initialized.
+#[cfg(feature = "async")]
 unsafe fn assume_init_bytes(slice: &[MaybeUninit<u8>]) -> &[u8] {
     &*(slice as *const [MaybeUninit<u8>] as *const [u8])
 }
diff --git a/tvix/nix-compat/src/wire/bytes/reader/mod.rs b/tvix/nix-compat/src/wire/bytes/reader/mod.rs
index 81474df70657..77950496ed6b 100644
--- a/tvix/nix-compat/src/wire/bytes/reader/mod.rs
+++ b/tvix/nix-compat/src/wire/bytes/reader/mod.rs
@@ -109,8 +109,6 @@ where
     }
 
     /// Remaining data length, ie not including data already read.
-    ///
-    /// If the size has not been read yet, this is [None].
     pub fn len(&self) -> u64 {
         match self.state {
             State::Body {
diff --git a/tvix/serde/Cargo.toml b/tvix/serde/Cargo.toml
index 5652126ada1d..fc5f08a2ddcd 100644
--- a/tvix/serde/Cargo.toml
+++ b/tvix/serde/Cargo.toml
@@ -5,5 +5,5 @@ edition = "2021"
 
 [dependencies]
 tvix-eval = { path = "../eval" }
-serde = { version = "1.0", features = ["derive"] }
-bstr = { version = "1.8.0", features = ["serde"] }
+serde = { workspace = true, features = ["derive"] }
+bstr = { workspace = true, features = ["serde"] }
diff --git a/tvix/shell.nix b/tvix/shell.nix
index 947cda269b2f..d6b4bb7d3458 100644
--- a/tvix/shell.nix
+++ b/tvix/shell.nix
@@ -10,13 +10,6 @@
     depot.third_party.sources = import ./sources { };
     additionalOverlays = [
       (self: super: {
-        # https://github.com/googleapis/google-cloud-go/pull/9665
-        cbtemulator = super.cbtemulator.overrideAttrs (old: {
-          patches = old.patches or [ ] ++ [
-            ./nixpkgs/cbtemulator-uds.patch
-          ];
-        });
-
         # macFUSE bump containing fix for https://github.com/osxfuse/osxfuse/issues/974
         # https://github.com/NixOS/nixpkgs/pull/320197
         fuse =
diff --git a/tvix/store/Cargo.toml b/tvix/store/Cargo.toml
index 0cbaa102f051..9d54ad1ea760 100644
--- a/tvix/store/Cargo.toml
+++ b/tvix/store/Cargo.toml
@@ -4,68 +4,62 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-anyhow = "1.0.68"
-async-compression = { version = "0.4.9", features = ["tokio", "bzip2", "gzip", "xz", "zstd"]}
-async-stream = "0.3.5"
-blake3 = { version = "1.3.1", features = ["rayon", "std"] }
-bstr = "1.6.0"
-bytes = "1.4.0"
-clap = { version = "4.0", features = ["derive", "env"] }
-count-write = "0.1.0"
-data-encoding = "2.6.0"
-futures = "0.3.30"
-lazy_static = "1.4.0"
+anyhow = { workspace = true }
+async-compression = { workspace = true, features = ["tokio", "bzip2", "gzip", "xz", "zstd"] }
+async-stream = { workspace = true }
+blake3 = { workspace = true, features = ["rayon", "std"] }
+bstr = { workspace = true }
+bytes = { workspace = true }
+clap = { workspace = true, features = ["derive", "env"] }
+count-write = { workspace = true }
+data-encoding = { workspace = true }
+futures = { workspace = true }
+lazy_static = { workspace = true }
 nix-compat = { path = "../nix-compat", features = ["async"] }
-pin-project-lite = "0.2.13"
-prost = "0.13.1"
-serde = { version = "1.0.197", features = [ "derive" ] }
-serde_json = "1.0"
-serde_with = "3.7.0"
-serde_qs = "0.12.0"
-sha2 = "0.10.6"
-sled = { version = "0.34.7" }
-thiserror = "1.0.38"
-tokio = { version = "1.32.0", features = ["fs", "macros", "net", "rt", "rt-multi-thread", "signal"] }
-tokio-listener = { version = "0.4.2", features = [ "clap", "multi-listener", "sd_listen", "tonic012" ] }
-tokio-stream = { version = "0.1.14", features = ["fs"] }
-tokio-util = { version = "0.7.9", features = ["io", "io-util", "compat"] }
-tonic = { version = "0.12.0", features = ["tls", "tls-roots"] }
-tower = "0.4.13"
-tower-http = { version = "0.5.0", features = ["trace"] }
+pin-project-lite = { workspace = true }
+prost = { workspace = true }
+serde = { workspace = true, features = ["derive"] }
+serde_json = { workspace = true }
+serde_with = { workspace = true }
+serde_qs = { workspace = true }
+sha2 = { workspace = true }
+sled = { workspace = true }
+thiserror = { workspace = true }
+tokio = { workspace = true, features = ["fs", "macros", "net", "rt", "rt-multi-thread", "signal"] }
+tokio-listener = { workspace = true, features = ["clap", "multi-listener", "sd_listen", "tonic012"] }
+tokio-stream = { workspace = true, features = ["fs"] }
+tokio-util = { workspace = true, features = ["io", "io-util", "compat"] }
+tonic = { workspace = true, features = ["tls", "tls-roots"] }
+tower = { workspace = true }
+tower-http = { workspace = true, features = ["trace"] }
 tvix-castore = { path = "../castore" }
-url = "2.4.0"
-walkdir = "2.4.0"
-reqwest = { version = "0.12.0", features = ["rustls-tls-native-roots", "stream"], default-features = false }
-reqwest-middleware = "0.3.0"
-lru = "0.12.3"
-parking_lot = "0.12.2"
+url = { workspace = true }
+walkdir = { workspace = true }
+reqwest = { workspace = true, features = ["rustls-tls-native-roots", "stream"] }
+reqwest-middleware = { workspace = true }
+lru = { workspace = true }
+parking_lot = { workspace = true }
 tvix-tracing = { path = "../tracing", features = ["tonic", "reqwest"] }
-tracing = "0.1.40"
-tracing-indicatif = "0.3.6"
-hyper-util = "0.1.6"
-toml = { version = "0.8.15", optional = true }
-tonic-health = { version = "0.12.1", default-features = false }
-redb = "2.1.1"
-mimalloc = "0.1.43"
-
-[dependencies.tonic-reflection]
-optional = true
-version = "0.12.0"
-
-[dependencies.bigtable_rs]
-optional = true
-version = "0.2.10"
+tracing = { workspace = true }
+tracing-indicatif = { workspace = true }
+hyper-util = { workspace = true }
+toml = { version = "0.8.19", optional = true }
+tonic-health = { workspace = true }
+redb = { workspace = true }
+mimalloc = { workspace = true }
+tonic-reflection = { workspace = true, optional = true }
+bigtable_rs = { workspace = true, optional = true }
 
 [build-dependencies]
-prost-build = "0.13.1"
-tonic-build = "0.12.0"
+prost-build = { workspace = true }
+tonic-build = { workspace = true }
 
 [dev-dependencies]
-async-process = "2.1.0"
-rstest = "0.19.0"
-rstest_reuse = "0.6.0"
-tempfile = "3.3.0"
-tokio-retry = "0.3.0"
+async-process = { workspace = true }
+rstest = { workspace = true }
+rstest_reuse = { workspace = true }
+tempfile = { workspace = true }
+tokio-retry = { workspace = true }
 
 [features]
 default = ["cloud", "fuse", "otlp", "tonic-reflection"]
diff --git a/tvix/store/build.rs b/tvix/store/build.rs
index 8a92ab26fd64..4e0371311220 100644
--- a/tvix/store/build.rs
+++ b/tvix/store/build.rs
@@ -25,7 +25,7 @@ fn main() -> Result<()> {
             ],
             // If we are in running `cargo build` manually, using `../..` works fine,
             // but in case we run inside a nix build, we need to instead point PROTO_ROOT
-            // to a sparseTree containing that structure.
+            // to a custom tree containing that structure.
             &[match std::env::var_os("PROTO_ROOT") {
                 Some(proto_root) => proto_root.to_str().unwrap().to_owned(),
                 None => "../..".to_string(),
diff --git a/tvix/store/default.nix b/tvix/store/default.nix
index b8541d072224..863ddb6de23f 100644
--- a/tvix/store/default.nix
+++ b/tvix/store/default.nix
@@ -35,7 +35,7 @@ in
     targets = [ "integration-tests" ] ++ lib.filter (x: lib.hasPrefix "with-features" x || x == "no-features") (lib.attrNames passthru);
     extraSteps.import-docs = (mkImportCheck "tvix/docs/src/store" ../docs/src/store);
   };
-  passthru = (depot.tvix.utils.mkFeaturePowerset {
+  passthru = old.passthru // (depot.tvix.utils.mkFeaturePowerset {
     inherit (old) crateName;
     features = ([ "cloud" "fuse" "otlp" "tonic-reflection" "xp-store-composition" ]
       # virtiofs feature currently fails to build on Darwin
diff --git a/tvix/store/protos/default.nix b/tvix/store/protos/default.nix
index 56345d9338b2..005a2697e5e9 100644
--- a/tvix/store/protos/default.nix
+++ b/tvix/store/protos/default.nix
@@ -1,17 +1,12 @@
-{ depot, pkgs, ... }:
+{ depot, pkgs, lib, ... }:
 let
-  protos = depot.nix.sparseTree {
-    name = "store-protos";
-    root = depot.path.origSrc;
-    paths = [
-      # We need to include castore.proto (only), as it's referred.
-      ../../castore/protos/castore.proto
-      ./pathinfo.proto
-      ./rpc_pathinfo.proto
-      ../../../buf.yaml
-      ../../../buf.gen.yaml
-    ];
-  };
+  protos = lib.sourceByRegex depot.path.origSrc [
+    "buf.yaml"
+    "buf.gen.yaml"
+    # We need to include castore.proto (only), as it's referred.
+    "^tvix(/castore(/protos(/castore\.proto)?)?)?$"
+    "^tvix(/store(/protos(/.*\.proto)?)?)?$"
+  ];
 in
 depot.nix.readTree.drvTargets {
   inherit protos;
diff --git a/tvix/store/src/bin/tvix-store.rs b/tvix/store/src/bin/tvix-store.rs
index d639a6e9d78d..6da239a8fee3 100644
--- a/tvix/store/src/bin/tvix-store.rs
+++ b/tvix/store/src/bin/tvix-store.rs
@@ -201,11 +201,18 @@ async fn run_cli(cli: Cli) -> Result<(), Box<dyn std::error::Error + Send + Sync
 
             #[cfg(feature = "tonic-reflection")]
             {
-                let reflection_svc = tonic_reflection::server::Builder::configure()
-                    .register_encoded_file_descriptor_set(CASTORE_FILE_DESCRIPTOR_SET)
-                    .register_encoded_file_descriptor_set(FILE_DESCRIPTOR_SET)
-                    .build()?;
-                router = router.add_service(reflection_svc);
+                router = router.add_service(
+                    tonic_reflection::server::Builder::configure()
+                        .register_encoded_file_descriptor_set(CASTORE_FILE_DESCRIPTOR_SET)
+                        .register_encoded_file_descriptor_set(FILE_DESCRIPTOR_SET)
+                        .build_v1alpha()?,
+                );
+                router = router.add_service(
+                    tonic_reflection::server::Builder::configure()
+                        .register_encoded_file_descriptor_set(CASTORE_FILE_DESCRIPTOR_SET)
+                        .register_encoded_file_descriptor_set(FILE_DESCRIPTOR_SET)
+                        .build_v1()?,
+                );
             }
 
             let listen_address = &listen_args.listen_address.unwrap_or_else(|| {
@@ -351,9 +358,10 @@ async fn run_cli(cli: Cli) -> Result<(), Box<dyn std::error::Error + Send + Sync
                 // Create and upload a PathInfo pointing to the root_node,
                 // annotated with information we have from the reference graph.
                 let path_info = PathInfo {
-                    node: Some(tvix_castore::proto::Node {
-                        node: Some(root_node),
-                    }),
+                    node: Some(tvix_castore::proto::Node::from_name_and_node(
+                        elem.path.to_string().into(),
+                        root_node,
+                    )),
                     references: Vec::from_iter(
                         elem.references.iter().map(|e| e.digest().to_vec().into()),
                     ),
diff --git a/tvix/store/src/import.rs b/tvix/store/src/import.rs
index 70a97982e642..1719669a4285 100644
--- a/tvix/store/src/import.rs
+++ b/tvix/store/src/import.rs
@@ -1,13 +1,14 @@
+use bstr::ByteSlice;
 use std::path::Path;
 use tracing::{debug, instrument};
 use tvix_castore::{
-    blobservice::BlobService, directoryservice::DirectoryService, import::fs::ingest_path,
-    proto::node::Node, B3Digest,
+    blobservice::BlobService, directoryservice::DirectoryService, import::fs::ingest_path, Node,
+    PathComponent,
 };
 
 use nix_compat::{
     nixhash::{CAHash, NixHash},
-    store_path::{self, StorePath},
+    store_path::{self, StorePathRef},
 };
 
 use crate::{
@@ -27,29 +28,29 @@ impl From<CAHash> for nar_info::Ca {
     }
 }
 
-pub fn log_node(node: &Node, path: &Path) {
+pub fn log_node(name: &[u8], node: &Node, path: &Path) {
     match node {
-        Node::Directory(directory_node) => {
+        Node::Directory { digest, .. } => {
             debug!(
                 path = ?path,
-                name = ?directory_node.name,
-                digest = %B3Digest::try_from(directory_node.digest.clone()).unwrap(),
+                name = %name.as_bstr(),
+                digest = %digest,
                 "import successful",
             )
         }
-        Node::File(file_node) => {
+        Node::File { digest, .. } => {
             debug!(
                 path = ?path,
-                name = ?file_node.name,
-                digest = %B3Digest::try_from(file_node.digest.clone()).unwrap(),
+                name = %name.as_bstr(),
+                digest = %digest,
                 "import successful"
             )
         }
-        Node::Symlink(symlink_node) => {
+        Node::Symlink { target } => {
             debug!(
                 path = ?path,
-                name = ?symlink_node.name,
-                target = ?symlink_node.target,
+                name = %name.as_bstr(),
+                target = ?target,
                 "import successful"
             )
         }
@@ -82,13 +83,14 @@ pub fn derive_nar_ca_path_info(
     nar_size: u64,
     nar_sha256: [u8; 32],
     ca: Option<&CAHash>,
+    name: bytes::Bytes,
     root_node: Node,
 ) -> PathInfo {
     // assemble the [crate::proto::PathInfo] object.
     PathInfo {
-        node: Some(tvix_castore::proto::Node {
-            node: Some(root_node),
-        }),
+        node: Some(tvix_castore::proto::Node::from_name_and_node(
+            name, root_node,
+        )),
         // There's no reference scanning on path contents ingested like this.
         references: vec![],
         narinfo: Some(NarInfo {
@@ -113,7 +115,7 @@ pub async fn import_path_as_nar_ca<BS, DS, PS, NS, P>(
     directory_service: DS,
     path_info_service: PS,
     nar_calculation_service: NS,
-) -> Result<StorePath, std::io::Error>
+) -> Result<StorePathRef, std::io::Error>
 where
     P: AsRef<Path> + std::fmt::Debug,
     BS: BlobService + Clone,
@@ -138,14 +140,19 @@ where
         )
     })?;
 
-    // rename the root node to match the calculated output path.
-    let root_node = root_node.rename(output_path.to_string().into_bytes().into());
-    log_node(&root_node, path.as_ref());
+    let name: PathComponent = output_path
+        .to_string()
+        .as_str()
+        .try_into()
+        .expect("Tvix bug: StorePath must be PathComponent");
+
+    log_node(name.as_ref(), &root_node, path.as_ref());
 
     let path_info = derive_nar_ca_path_info(
         nar_size,
         nar_sha256,
         Some(&CAHash::Nar(NixHash::Sha256(nar_sha256))),
+        name.into(),
         root_node,
     );
 
@@ -154,7 +161,7 @@ where
     // callers don't really need it.
     let _path_info = path_info_service.as_ref().put(path_info).await?;
 
-    Ok(output_path.to_owned())
+    Ok(output_path)
 }
 
 #[cfg(test)]
diff --git a/tvix/store/src/nar/import.rs b/tvix/store/src/nar/import.rs
index 32c2f4e58061..b9a15fe71384 100644
--- a/tvix/store/src/nar/import.rs
+++ b/tvix/store/src/nar/import.rs
@@ -12,8 +12,7 @@ use tvix_castore::{
         blobs::{self, ConcurrentBlobUploader},
         ingest_entries, IngestionEntry, IngestionError,
     },
-    proto::{node::Node, NamedNode},
-    PathBuf,
+    Node, PathBuf,
 };
 
 /// Ingests the contents from a [AsyncRead] providing NAR into the tvix store,
@@ -98,9 +97,7 @@ where
 
     let (_, node) = try_join!(produce, consume)?;
 
-    // remove the fake "root" name again
-    debug_assert_eq!(&node.get_name(), b"root");
-    Ok(node.rename("".into()))
+    Ok(node)
 }
 
 async fn produce_nar_inner<BS>(
@@ -177,7 +174,7 @@ mod test {
         DIRECTORY_COMPLICATED, DIRECTORY_WITH_KEEP, EMPTY_BLOB_DIGEST, HELLOWORLD_BLOB_CONTENTS,
         HELLOWORLD_BLOB_DIGEST,
     };
-    use tvix_castore::proto as castorepb;
+    use tvix_castore::{Directory, Node};
 
     use crate::tests::fixtures::{
         blob_service, directory_service, NAR_CONTENTS_COMPLICATED, NAR_CONTENTS_HELLOWORLD,
@@ -199,10 +196,9 @@ mod test {
         .expect("must parse");
 
         assert_eq!(
-            castorepb::node::Node::Symlink(castorepb::SymlinkNode {
-                name: "".into(), // name must be empty
-                target: "/nix/store/somewhereelse".into(),
-            }),
+            Node::Symlink {
+                target: "/nix/store/somewhereelse".try_into().unwrap()
+            },
             root_node
         );
     }
@@ -222,12 +218,11 @@ mod test {
         .expect("must parse");
 
         assert_eq!(
-            castorepb::node::Node::File(castorepb::FileNode {
-                name: "".into(), // name must be empty
-                digest: HELLOWORLD_BLOB_DIGEST.clone().into(),
+            Node::File {
+                digest: HELLOWORLD_BLOB_DIGEST.clone(),
                 size: HELLOWORLD_BLOB_CONTENTS.len() as u64,
                 executable: false,
-            }),
+            },
             root_node
         );
 
@@ -250,11 +245,10 @@ mod test {
         .expect("must parse");
 
         assert_eq!(
-            castorepb::node::Node::Directory(castorepb::DirectoryNode {
-                name: "".into(), // name must be empty
-                digest: DIRECTORY_COMPLICATED.digest().into(),
-                size: DIRECTORY_COMPLICATED.size(),
-            }),
+            Node::Directory {
+                digest: DIRECTORY_COMPLICATED.digest(),
+                size: DIRECTORY_COMPLICATED.size()
+            },
             root_node,
         );
 
@@ -262,7 +256,7 @@ mod test {
         assert!(blob_service.has(&EMPTY_BLOB_DIGEST).await.unwrap());
 
         // directoryservice must contain the directories, at least with get_recursive.
-        let resp: Result<Vec<castorepb::Directory>, _> = directory_service
+        let resp: Result<Vec<Directory>, _> = directory_service
             .get_recursive(&DIRECTORY_COMPLICATED.digest())
             .collect()
             .await;
diff --git a/tvix/store/src/nar/mod.rs b/tvix/store/src/nar/mod.rs
index 8cbb091f1aab..da798bbf3a3c 100644
--- a/tvix/store/src/nar/mod.rs
+++ b/tvix/store/src/nar/mod.rs
@@ -8,16 +8,14 @@ pub use import::ingest_nar_and_hash;
 pub use renderer::calculate_size_and_sha256;
 pub use renderer::write_nar;
 pub use renderer::SimpleRenderer;
-use tvix_castore::proto as castorepb;
+use tvix_castore::Node;
 
 #[async_trait]
 pub trait NarCalculationService: Send + Sync {
     /// Return the nar size and nar sha256 digest for a given root node.
     /// This can be used to calculate NAR-based output paths.
-    async fn calculate_nar(
-        &self,
-        root_node: &castorepb::node::Node,
-    ) -> Result<(u64, [u8; 32]), tvix_castore::Error>;
+    async fn calculate_nar(&self, root_node: &Node)
+        -> Result<(u64, [u8; 32]), tvix_castore::Error>;
 }
 
 #[async_trait]
@@ -27,7 +25,7 @@ where
 {
     async fn calculate_nar(
         &self,
-        root_node: &castorepb::node::Node,
+        root_node: &Node,
     ) -> Result<(u64, [u8; 32]), tvix_castore::Error> {
         self.as_ref().calculate_nar(root_node).await
     }
@@ -39,13 +37,13 @@ pub enum RenderError {
     #[error("failure talking to a backing store client: {0}")]
     StoreError(#[source] std::io::Error),
 
-    #[error("unable to find directory {}, referred from {:?}", .0, .1)]
+    #[error("unable to find directory {0}, referred from {1:?}")]
     DirectoryNotFound(B3Digest, bytes::Bytes),
 
-    #[error("unable to find blob {}, referred from {:?}", .0, .1)]
+    #[error("unable to find blob {0}, referred from {1:?}")]
     BlobNotFound(B3Digest, bytes::Bytes),
 
-    #[error("unexpected size in metadata for blob {}, referred from {:?} returned, expected {}, got {}", .0, .1, .2, .3)]
+    #[error("unexpected size in metadata for blob {0}, referred from {1:?} returned, expected {2}, got {3}")]
     UnexpectedBlobMeta(B3Digest, bytes::Bytes, u32, u32),
 
     #[error("failure using the NAR writer: {0}")]
diff --git a/tvix/store/src/nar/renderer.rs b/tvix/store/src/nar/renderer.rs
index e3cb54dd229f..afd85f267c6c 100644
--- a/tvix/store/src/nar/renderer.rs
+++ b/tvix/store/src/nar/renderer.rs
@@ -8,11 +8,7 @@ use tokio::io::{self, AsyncWrite, BufReader};
 use tonic::async_trait;
 use tracing::{instrument, Span};
 use tracing_indicatif::span_ext::IndicatifSpanExt;
-use tvix_castore::{
-    blobservice::BlobService,
-    directoryservice::DirectoryService,
-    proto::{self as castorepb, NamedNode},
-};
+use tvix_castore::{blobservice::BlobService, directoryservice::DirectoryService, Node};
 
 pub struct SimpleRenderer<BS, DS> {
     blob_service: BS,
@@ -36,7 +32,7 @@ where
 {
     async fn calculate_nar(
         &self,
-        root_node: &castorepb::node::Node,
+        root_node: &Node,
     ) -> Result<(u64, [u8; 32]), tvix_castore::Error> {
         calculate_size_and_sha256(
             root_node,
@@ -52,7 +48,7 @@ where
 /// NAR output.
 #[instrument(skip_all, fields(indicatif.pb_show=1))]
 pub async fn calculate_size_and_sha256<BS, DS>(
-    root_node: &castorepb::node::Node,
+    root_node: &Node,
     blob_service: BS,
     directory_service: DS,
 ) -> Result<(u64, [u8; 32]), RenderError>
@@ -80,13 +76,13 @@ where
     Ok((cw.count(), h.finalize().into()))
 }
 
-/// Accepts a [castorepb::node::Node] pointing to the root of a (store) path,
+/// Accepts a [Node] pointing to the root of a (store) path,
 /// and uses the passed blob_service and directory_service to perform the
 /// necessary lookups as it traverses the structure.
 /// The contents in NAR serialization are writen to the passed [AsyncWrite].
 pub async fn write_nar<W, BS, DS>(
     mut w: W,
-    proto_root_node: &castorepb::node::Node,
+    root_node: &Node,
     blob_service: BS,
     directory_service: DS,
 ) -> Result<(), RenderError>
@@ -102,7 +98,8 @@ where
 
     walk_node(
         nar_root_node,
-        proto_root_node,
+        root_node,
+        b"",
         blob_service,
         directory_service,
     )
@@ -115,7 +112,8 @@ where
 /// This consumes the node.
 async fn walk_node<BS, DS>(
     nar_node: nar_writer::Node<'_, '_>,
-    proto_node: &castorepb::node::Node,
+    castore_node: &Node,
+    name: &[u8],
     blob_service: BS,
     directory_service: DS,
 ) -> Result<(BS, DS), RenderError>
@@ -123,24 +121,20 @@ where
     BS: BlobService + Send,
     DS: DirectoryService + Send,
 {
-    match proto_node {
-        castorepb::node::Node::Symlink(proto_symlink_node) => {
+    match castore_node {
+        Node::Symlink { target, .. } => {
             nar_node
-                .symlink(&proto_symlink_node.target)
+                .symlink(target.as_ref())
                 .await
                 .map_err(RenderError::NARWriterError)?;
         }
-        castorepb::node::Node::File(proto_file_node) => {
-            let digest_len = proto_file_node.digest.len();
-            let digest = proto_file_node.digest.clone().try_into().map_err(|_| {
-                RenderError::StoreError(io::Error::new(
-                    io::ErrorKind::Other,
-                    format!("invalid digest len {} in file node", digest_len),
-                ))
-            })?;
-
+        Node::File {
+            digest,
+            size,
+            executable,
+        } => {
             let mut blob_reader = match blob_service
-                .open_read(&digest)
+                .open_read(digest)
                 .await
                 .map_err(RenderError::StoreError)?
             {
@@ -152,39 +146,23 @@ where
             }?;
 
             nar_node
-                .file(
-                    proto_file_node.executable,
-                    proto_file_node.size,
-                    &mut blob_reader,
-                )
+                .file(*executable, *size, &mut blob_reader)
                 .await
                 .map_err(RenderError::NARWriterError)?;
         }
-        castorepb::node::Node::Directory(proto_directory_node) => {
-            let digest_len = proto_directory_node.digest.len();
-            let digest = proto_directory_node
-                .digest
-                .clone()
-                .try_into()
-                .map_err(|_| {
-                    RenderError::StoreError(io::Error::new(
-                        io::ErrorKind::InvalidData,
-                        format!("invalid digest len {} in directory node", digest_len),
-                    ))
-                })?;
-
+        Node::Directory { digest, .. } => {
             // look it up with the directory service
             match directory_service
-                .get(&digest)
+                .get(digest)
                 .await
                 .map_err(|e| RenderError::StoreError(e.into()))?
             {
                 // if it's None, that's an error!
                 None => Err(RenderError::DirectoryNotFound(
-                    digest,
-                    proto_directory_node.name.clone(),
+                    digest.clone(),
+                    bytes::Bytes::copy_from_slice(name),
                 ))?,
-                Some(proto_directory) => {
+                Some(directory) => {
                     // start a directory node
                     let mut nar_node_directory = nar_node
                         .directory()
@@ -198,15 +176,16 @@ where
 
                     // for each node in the directory, create a new entry with its name,
                     // and then recurse on that entry.
-                    for proto_node in proto_directory.nodes() {
+                    for (name, node) in directory.nodes() {
                         let child_node = nar_node_directory
-                            .entry(proto_node.get_name())
+                            .entry(name.as_ref())
                             .await
                             .map_err(RenderError::NARWriterError)?;
 
                         (blob_service, directory_service) = Box::pin(walk_node(
                             child_node,
-                            &proto_node,
+                            node,
+                            name.as_ref(),
                             blob_service,
                             directory_service,
                         ))
diff --git a/tvix/store/src/pathinfoservice/fs/mod.rs b/tvix/store/src/pathinfoservice/fs/mod.rs
index aa64b1c01f16..1f7fa8a8afce 100644
--- a/tvix/store/src/pathinfoservice/fs/mod.rs
+++ b/tvix/store/src/pathinfoservice/fs/mod.rs
@@ -1,10 +1,10 @@
 use futures::stream::BoxStream;
 use futures::StreamExt;
+use nix_compat::store_path::StorePathRef;
 use tonic::async_trait;
 use tvix_castore::fs::{RootNodes, TvixStoreFs};
-use tvix_castore::proto as castorepb;
-use tvix_castore::Error;
 use tvix_castore::{blobservice::BlobService, directoryservice::DirectoryService};
+use tvix_castore::{Error, Node, PathComponent};
 
 use super::PathInfoService;
 
@@ -48,8 +48,8 @@ impl<T> RootNodes for RootNodesWrapper<T>
 where
     T: AsRef<dyn PathInfoService> + Send + Sync,
 {
-    async fn get_by_basename(&self, name: &[u8]) -> Result<Option<castorepb::node::Node>, Error> {
-        let Ok(store_path) = nix_compat::store_path::StorePath::from_bytes(name) else {
+    async fn get_by_basename(&self, name: &PathComponent) -> Result<Option<Node>, Error> {
+        let Ok(store_path) = StorePathRef::from_bytes(name.as_ref()) else {
             return Ok(None);
         };
 
@@ -59,22 +59,31 @@ where
             .get(*store_path.digest())
             .await?
             .map(|path_info| {
-                path_info
+                let node = path_info
                     .node
+                    .as_ref()
                     .expect("missing root node")
-                    .node
-                    .expect("empty node")
-            }))
+                    .to_owned();
+
+                match node.into_name_and_node() {
+                    Ok((_name, node)) => Ok(node),
+                    Err(e) => Err(Error::StorageError(e.to_string())),
+                }
+            })
+            .transpose()?)
     }
 
-    fn list(&self) -> BoxStream<Result<castorepb::node::Node, Error>> {
+    fn list(&self) -> BoxStream<Result<(PathComponent, Node), Error>> {
         Box::pin(self.0.as_ref().list().map(|result| {
-            result.map(|path_info| {
-                path_info
+            result.and_then(|path_info| {
+                let node = path_info
                     .node
+                    .as_ref()
                     .expect("missing root node")
-                    .node
-                    .expect("empty node")
+                    .to_owned();
+
+                node.into_name_and_node()
+                    .map_err(|e| Error::StorageError(e.to_string()))
             })
         }))
     }
diff --git a/tvix/store/src/pathinfoservice/grpc.rs b/tvix/store/src/pathinfoservice/grpc.rs
index 2ac0e43303cb..7510ccd911f0 100644
--- a/tvix/store/src/pathinfoservice/grpc.rs
+++ b/tvix/store/src/pathinfoservice/grpc.rs
@@ -11,7 +11,8 @@ use tonic::{async_trait, Code};
 use tracing::{instrument, Span};
 use tracing_indicatif::span_ext::IndicatifSpanExt;
 use tvix_castore::composition::{CompositionContext, ServiceBuilder};
-use tvix_castore::{proto as castorepb, Error};
+use tvix_castore::Error;
+use tvix_castore::Node;
 
 /// Connects to a (remote) tvix-store PathInfoService over gRPC.
 #[derive(Clone)]
@@ -123,10 +124,7 @@ where
     T::Future: Send,
 {
     #[instrument(level = "trace", skip_all, fields(root_node = ?root_node, indicatif.pb_show=1))]
-    async fn calculate_nar(
-        &self,
-        root_node: &castorepb::node::Node,
-    ) -> Result<(u64, [u8; 32]), Error> {
+    async fn calculate_nar(&self, root_node: &Node) -> Result<(u64, [u8; 32]), Error> {
         let span = Span::current();
         span.pb_set_message("Waiting for NAR calculation");
         span.pb_start();
@@ -134,9 +132,10 @@ where
         let path_info = self
             .grpc_client
             .clone()
-            .calculate_nar(castorepb::Node {
-                node: Some(root_node.clone()),
-            })
+            .calculate_nar(tvix_castore::proto::Node::from_name_and_node(
+                "".into(),
+                root_node.to_owned(),
+            ))
             .await
             .map_err(|e| Error::StorageError(e.to_string()))?
             .into_inner();
diff --git a/tvix/store/src/pathinfoservice/lru.rs b/tvix/store/src/pathinfoservice/lru.rs
index 39c592bc96fb..695c04636089 100644
--- a/tvix/store/src/pathinfoservice/lru.rs
+++ b/tvix/store/src/pathinfoservice/lru.rs
@@ -108,8 +108,17 @@ mod test {
             let mut p = PATHINFO_1.clone();
             let root_node = p.node.as_mut().unwrap();
             if let castorepb::Node { node: Some(node) } = root_node {
-                let n = node.to_owned();
-                *node = n.rename("11111111111111111111111111111111-dummy2".into());
+                match node {
+                    castorepb::node::Node::Directory(n) => {
+                        n.name = "11111111111111111111111111111111-dummy2".into()
+                    }
+                    castorepb::node::Node::File(n) => {
+                        n.name = "11111111111111111111111111111111-dummy2".into()
+                    }
+                    castorepb::node::Node::Symlink(n) => {
+                        n.name = "11111111111111111111111111111111-dummy2".into()
+                    }
+                }
             } else {
                 unreachable!()
             }
diff --git a/tvix/store/src/pathinfoservice/nix_http.rs b/tvix/store/src/pathinfoservice/nix_http.rs
index a2756033172f..5f1eed1a0a9f 100644
--- a/tvix/store/src/pathinfoservice/nix_http.rs
+++ b/tvix/store/src/pathinfoservice/nix_http.rs
@@ -228,10 +228,10 @@ where
         }
 
         Ok(Some(PathInfo {
-            node: Some(castorepb::Node {
-                // set the name of the root node to the digest-name of the store path.
-                node: Some(root_node.rename(narinfo.store_path.to_string().to_owned().into())),
-            }),
+            node: Some(castorepb::Node::from_name_and_node(
+                narinfo.store_path.to_string().into(),
+                root_node,
+            )),
             references: pathinfo.references,
             narinfo: pathinfo.narinfo,
         }))
diff --git a/tvix/store/src/proto/grpc_pathinfoservice_wrapper.rs b/tvix/store/src/proto/grpc_pathinfoservice_wrapper.rs
index 68f557567629..60da73012df7 100644
--- a/tvix/store/src/proto/grpc_pathinfoservice_wrapper.rs
+++ b/tvix/store/src/proto/grpc_pathinfoservice_wrapper.rs
@@ -74,24 +74,19 @@ where
         &self,
         request: Request<castorepb::Node>,
     ) -> Result<Response<proto::CalculateNarResponse>> {
-        match request.into_inner().node {
-            None => Err(Status::invalid_argument("no root node sent")),
-            Some(root_node) => {
-                if let Err(e) = root_node.validate() {
-                    warn!(err = %e, "invalid root node");
-                    Err(Status::invalid_argument("invalid root node"))?
-                }
+        let (_, root_node) = request.into_inner().into_name_and_node().map_err(|e| {
+            warn!(err = %e, "invalid root node");
+            Status::invalid_argument("invalid root node")
+        })?;
 
-                match self.nar_calculation_service.calculate_nar(&root_node).await {
-                    Ok((nar_size, nar_sha256)) => Ok(Response::new(proto::CalculateNarResponse {
-                        nar_size,
-                        nar_sha256: nar_sha256.to_vec().into(),
-                    })),
-                    Err(e) => {
-                        warn!(err = %e, "error during NAR calculation");
-                        Err(e.into())
-                    }
-                }
+        match self.nar_calculation_service.calculate_nar(&root_node).await {
+            Ok((nar_size, nar_sha256)) => Ok(Response::new(proto::CalculateNarResponse {
+                nar_size,
+                nar_sha256: nar_sha256.to_vec().into(),
+            })),
+            Err(e) => {
+                warn!(err = %e, "error during NAR calculation");
+                Err(e.into())
             }
         }
     }
diff --git a/tvix/store/src/proto/mod.rs b/tvix/store/src/proto/mod.rs
index b45e6fda46fe..f3ea4b196946 100644
--- a/tvix/store/src/proto/mod.rs
+++ b/tvix/store/src/proto/mod.rs
@@ -9,7 +9,7 @@ use nix_compat::{
     store_path::{self, StorePathRef},
 };
 use thiserror::Error;
-use tvix_castore::proto::{self as castorepb, NamedNode, ValidateNodeError};
+use tvix_castore::DirectoryError;
 
 mod grpc_pathinfoservice_wrapper;
 
@@ -39,7 +39,7 @@ pub enum ValidatePathInfoError {
 
     /// Node fails validation
     #[error("Invalid root node: {:?}", .0.to_string())]
-    InvalidRootNode(ValidateNodeError),
+    InvalidRootNode(DirectoryError),
 
     /// Invalid node name encountered. Root nodes in PathInfos have more strict name requirements
     #[error("Failed to parse {} as StorePath: {1}", .0.to_str_lossy())]
@@ -87,7 +87,7 @@ impl PathInfo {
     /// validate performs some checks on the PathInfo struct,
     /// Returning either a [store_path::StorePath] of the root node, or a
     /// [ValidatePathInfoError].
-    pub fn validate(&self) -> Result<store_path::StorePathRef<'_>, ValidatePathInfoError> {
+    pub fn validate(&self) -> Result<store_path::StorePath<String>, ValidatePathInfoError> {
         // ensure the references have the right number of bytes.
         for (i, reference) in self.references.iter().enumerate() {
             if reference.len() != store_path::DIGEST_SIZE {
@@ -118,7 +118,7 @@ impl PathInfo {
             // parse references in reference_names.
             for (i, reference_name_str) in narinfo.reference_names.iter().enumerate() {
                 // ensure thy parse as (non-absolute) store path
-                let reference_names_store_path = store_path::StorePath::from_bytes(
+                let reference_names_store_path = store_path::StorePathRef::from_bytes(
                     reference_name_str.as_bytes(),
                 )
                 .map_err(|_| {
@@ -158,14 +158,20 @@ impl PathInfo {
 
         // Ensure there is a (root) node present, and it properly parses to a [store_path::StorePath].
         let root_nix_path = match &self.node {
-            None | Some(castorepb::Node { node: None }) => {
-                Err(ValidatePathInfoError::NoNodePresent)?
-            }
-            Some(castorepb::Node { node: Some(node) }) => {
-                node.validate()
+            None => Err(ValidatePathInfoError::NoNodePresent)?,
+            Some(node) => {
+                // NOTE: We could have some PathComponent not allocating here,
+                // so this can return StorePathRef.
+                // However, as this will get refactored away to stricter types
+                // soon anyways, there's no point.
+                let (name, _node) = node
+                    .clone()
+                    .into_name_and_node()
                     .map_err(ValidatePathInfoError::InvalidRootNode)?;
+
                 // parse the name of the node itself and return
-                parse_node_name_root(node.get_name(), ValidatePathInfoError::InvalidNodeName)?
+                parse_node_name_root(name.as_ref(), ValidatePathInfoError::InvalidNodeName)?
+                    .to_owned()
             }
         };
 
@@ -219,7 +225,7 @@ impl PathInfo {
                 .signatures
                 .iter()
                 .map(|sig| {
-                    nix_compat::narinfo::Signature::new(
+                    nix_compat::narinfo::SignatureRef::new(
                         &sig.name,
                         // This shouldn't pass validation
                         sig.data[..].try_into().expect("invalid signature len"),
@@ -354,7 +360,7 @@ impl From<&nix_compat::narinfo::NarInfo<'_>> for NarInfo {
             signatures,
             reference_names: value.references.iter().map(|r| r.to_string()).collect(),
             deriver: value.deriver.as_ref().map(|sp| StorePath {
-                name: sp.name().to_owned(),
+                name: (*sp.name()).to_owned(),
                 digest: Bytes::copy_from_slice(sp.digest()),
             }),
             ca: value.ca.as_ref().map(|ca| ca.into()),
diff --git a/tvix/store/src/proto/tests/pathinfo.rs b/tvix/store/src/proto/tests/pathinfo.rs
index 4d0834878d7c..eb47e592be8c 100644
--- a/tvix/store/src/proto/tests/pathinfo.rs
+++ b/tvix/store/src/proto/tests/pathinfo.rs
@@ -3,17 +3,18 @@ use crate::tests::fixtures::*;
 use bytes::Bytes;
 use data_encoding::BASE64;
 use nix_compat::nixbase32;
-use nix_compat::store_path::{self, StorePathRef};
+use nix_compat::store_path::{self, StorePath, StorePathRef};
 use rstest::rstest;
 use tvix_castore::proto as castorepb;
+use tvix_castore::{DirectoryError, ValidateNodeError};
 
 #[rstest]
 #[case::no_node(None, Err(ValidatePathInfoError::NoNodePresent))]
-#[case::no_node_2(Some(castorepb::Node { node: None}), Err(ValidatePathInfoError::NoNodePresent))]
+#[case::no_node_2(Some(castorepb::Node { node: None}), Err(ValidatePathInfoError::InvalidRootNode(DirectoryError::NoNodeSet)))]
 
 fn validate_pathinfo(
     #[case] node: Option<castorepb::Node>,
-    #[case] exp_result: Result<StorePathRef, ValidatePathInfoError>,
+    #[case] exp_result: Result<StorePath<String>, ValidatePathInfoError>,
 ) {
     // construct the PathInfo object
     let p = PathInfo {
@@ -22,9 +23,6 @@ fn validate_pathinfo(
     };
 
     assert_eq!(exp_result, p.validate());
-
-    let err = p.validate().expect_err("validation should fail");
-    assert!(matches!(err, ValidatePathInfoError::NoNodePresent));
 }
 
 #[rstest]
@@ -32,12 +30,12 @@ fn validate_pathinfo(
         name: DUMMY_PATH.into(),
         digest: DUMMY_DIGEST.clone().into(),
         size: 0,
-}, Ok(StorePathRef::from_bytes(DUMMY_PATH.as_bytes()).unwrap()))]
+}, Ok(StorePath::from_bytes(DUMMY_PATH.as_bytes()).unwrap()))]
 #[case::invalid_digest_length(castorepb::DirectoryNode {
         name: DUMMY_PATH.into(),
         digest: Bytes::new(),
         size: 0,
-}, Err(ValidatePathInfoError::InvalidRootNode(castorepb::ValidateNodeError::InvalidDigestLen(0))))]
+}, Err(ValidatePathInfoError::InvalidRootNode(DirectoryError::InvalidNode(DUMMY_PATH.try_into().unwrap(), ValidateNodeError::InvalidDigestLen(0)))))]
 #[case::invalid_node_name_no_storepath(castorepb::DirectoryNode {
         name: "invalid".into(),
         digest: DUMMY_DIGEST.clone().into(),
@@ -48,7 +46,7 @@ fn validate_pathinfo(
 )))]
 fn validate_directory(
     #[case] directory_node: castorepb::DirectoryNode,
-    #[case] exp_result: Result<StorePathRef, ValidatePathInfoError>,
+    #[case] exp_result: Result<StorePath<String>, ValidatePathInfoError>,
 ) {
     // construct the PathInfo object
     let p = PathInfo {
@@ -68,7 +66,7 @@ fn validate_directory(
         size: 0,
         executable: false,
     },
-    Ok(StorePathRef::from_bytes(DUMMY_PATH.as_bytes()).unwrap())
+    Ok(StorePath::from_bytes(DUMMY_PATH.as_bytes()).unwrap())
 )]
 #[case::invalid_digest_len(
     castorepb::FileNode {
@@ -76,7 +74,7 @@ fn validate_directory(
         digest: Bytes::new(),
         ..Default::default()
     },
-    Err(ValidatePathInfoError::InvalidRootNode(castorepb::ValidateNodeError::InvalidDigestLen(0)))
+    Err(ValidatePathInfoError::InvalidRootNode(DirectoryError::InvalidNode(DUMMY_PATH.try_into().unwrap(), ValidateNodeError::InvalidDigestLen(0))))
 )]
 #[case::invalid_node_name(
     castorepb::FileNode {
@@ -91,7 +89,7 @@ fn validate_directory(
 )]
 fn validate_file(
     #[case] file_node: castorepb::FileNode,
-    #[case] exp_result: Result<StorePathRef, ValidatePathInfoError>,
+    #[case] exp_result: Result<StorePath<String>, ValidatePathInfoError>,
 ) {
     // construct the PathInfo object
     let p = PathInfo {
@@ -109,7 +107,7 @@ fn validate_file(
         name: DUMMY_PATH.into(),
         target: "foo".into(),
     },
-    Ok(StorePathRef::from_bytes(DUMMY_PATH.as_bytes()).unwrap())
+    Ok(StorePath::from_bytes(DUMMY_PATH.as_bytes()).unwrap())
 )]
 #[case::invalid_node_name(
     castorepb::SymlinkNode {
@@ -123,7 +121,7 @@ fn validate_file(
 )]
 fn validate_symlink(
     #[case] symlink_node: castorepb::SymlinkNode,
-    #[case] exp_result: Result<StorePathRef, ValidatePathInfoError>,
+    #[case] exp_result: Result<StorePath<String>, ValidatePathInfoError>,
 ) {
     // construct the PathInfo object
     let p = PathInfo {
@@ -228,24 +226,28 @@ fn validate_inconsistent_narinfo_reference_name_digest() {
 /// Create a node with an empty symlink target, and ensure it fails validation.
 #[test]
 fn validate_symlink_empty_target_invalid() {
-    let node = castorepb::node::Node::Symlink(castorepb::SymlinkNode {
-        name: "foo".into(),
-        target: "".into(),
-    });
-
-    node.validate().expect_err("must fail validation");
+    castorepb::Node {
+        node: Some(castorepb::node::Node::Symlink(castorepb::SymlinkNode {
+            name: "foo".into(),
+            target: "".into(),
+        })),
+    }
+    .into_name_and_node()
+    .expect_err("must fail validation");
 }
 
 /// Create a node with a symlink target including null bytes, and ensure it
 /// fails validation.
 #[test]
 fn validate_symlink_target_null_byte_invalid() {
-    let node = castorepb::node::Node::Symlink(castorepb::SymlinkNode {
-        name: "foo".into(),
-        target: "foo\0".into(),
-    });
-
-    node.validate().expect_err("must fail validation");
+    castorepb::Node {
+        node: Some(castorepb::node::Node::Symlink(castorepb::SymlinkNode {
+            name: "foo".into(),
+            target: "foo\0".into(),
+        })),
+    }
+    .into_name_and_node()
+    .expect_err("must fail validation");
 }
 
 /// Create a PathInfo with a correct deriver field and ensure it succeeds.
diff --git a/tvix/store/src/tests/nar_renderer.rs b/tvix/store/src/tests/nar_renderer.rs
index 8bfb5a72bb2f..03eaa28aaac8 100644
--- a/tvix/store/src/tests/nar_renderer.rs
+++ b/tvix/store/src/tests/nar_renderer.rs
@@ -10,7 +10,7 @@ use std::sync::Arc;
 use tokio::io::sink;
 use tvix_castore::blobservice::BlobService;
 use tvix_castore::directoryservice::DirectoryService;
-use tvix_castore::proto as castorepb;
+use tvix_castore::Node;
 
 #[rstest]
 #[tokio::test]
@@ -22,10 +22,9 @@ async fn single_symlink(
 
     write_nar(
         &mut buf,
-        &castorepb::node::Node::Symlink(castorepb::SymlinkNode {
-            name: "doesntmatter".into(),
-            target: "/nix/store/somewhereelse".into(),
-        }),
+        &Node::Symlink {
+            target: "/nix/store/somewhereelse".try_into().unwrap(),
+        },
         // don't put anything in the stores, as we don't actually do any requests.
         blob_service,
         directory_service,
@@ -45,12 +44,11 @@ async fn single_file_missing_blob(
 ) {
     let e = write_nar(
         sink(),
-        &castorepb::node::Node::File(castorepb::FileNode {
-            name: "doesntmatter".into(),
-            digest: HELLOWORLD_BLOB_DIGEST.clone().into(),
+        &Node::File {
+            digest: HELLOWORLD_BLOB_DIGEST.clone(),
             size: HELLOWORLD_BLOB_CONTENTS.len() as u64,
             executable: false,
-        }),
+        },
         // the blobservice is empty intentionally, to provoke the error.
         blob_service,
         directory_service,
@@ -90,12 +88,11 @@ async fn single_file_wrong_blob_size(
     // Test with a root FileNode of a too big size
     let e = write_nar(
         sink(),
-        &castorepb::node::Node::File(castorepb::FileNode {
-            name: "doesntmatter".into(),
-            digest: HELLOWORLD_BLOB_DIGEST.clone().into(),
+        &Node::File {
+            digest: HELLOWORLD_BLOB_DIGEST.clone(),
             size: 42, // <- note the wrong size here!
             executable: false,
-        }),
+        },
         blob_service.clone(),
         directory_service.clone(),
     )
@@ -112,12 +109,11 @@ async fn single_file_wrong_blob_size(
     // Test with a root FileNode of a too small size
     let e = write_nar(
         sink(),
-        &castorepb::node::Node::File(castorepb::FileNode {
-            name: "doesntmatter".into(),
-            digest: HELLOWORLD_BLOB_DIGEST.clone().into(),
+        &Node::File {
+            digest: HELLOWORLD_BLOB_DIGEST.clone(),
             size: 2, // <- note the wrong size here!
             executable: false,
-        }),
+        },
         blob_service,
         directory_service,
     )
@@ -153,12 +149,11 @@ async fn single_file(
 
     write_nar(
         &mut buf,
-        &castorepb::node::Node::File(castorepb::FileNode {
-            name: "doesntmatter".into(),
-            digest: HELLOWORLD_BLOB_DIGEST.clone().into(),
+        &Node::File {
+            digest: HELLOWORLD_BLOB_DIGEST.clone(),
             size: HELLOWORLD_BLOB_CONTENTS.len() as u64,
             executable: false,
-        }),
+        },
         blob_service,
         directory_service,
     )
@@ -196,11 +191,10 @@ async fn test_complicated(
 
     write_nar(
         &mut buf,
-        &castorepb::node::Node::Directory(castorepb::DirectoryNode {
-            name: "doesntmatter".into(),
-            digest: DIRECTORY_COMPLICATED.digest().into(),
+        &Node::Directory {
+            digest: DIRECTORY_COMPLICATED.digest(),
             size: DIRECTORY_COMPLICATED.size(),
-        }),
+        },
         blob_service.clone(),
         directory_service.clone(),
     )
@@ -211,11 +205,10 @@ async fn test_complicated(
 
     // ensure calculate_nar does return the correct sha256 digest and sum.
     let (nar_size, nar_digest) = calculate_size_and_sha256(
-        &castorepb::node::Node::Directory(castorepb::DirectoryNode {
-            name: "doesntmatter".into(),
-            digest: DIRECTORY_COMPLICATED.digest().into(),
+        &Node::Directory {
+            digest: DIRECTORY_COMPLICATED.digest(),
             size: DIRECTORY_COMPLICATED.size(),
-        }),
+        },
         blob_service,
         directory_service,
     )
diff --git a/tvix/store/src/utils.rs b/tvix/store/src/utils.rs
index c550ad365ed7..1385ece39f8a 100644
--- a/tvix/store/src/utils.rs
+++ b/tvix/store/src/utils.rs
@@ -29,6 +29,10 @@ pub struct CompositionConfigs {
     >,
 }
 
+/// Provides a set clap arguments to configure tvix-[ca]store services.
+///
+/// This particular variant has defaults tailored for usecases accessing data
+/// directly locally, like the `tvix-store daemon` command.
 #[derive(clap::Parser, Clone)]
 pub struct ServiceUrls {
     #[arg(
@@ -56,7 +60,10 @@ pub struct ServiceUrls {
     experimental_store_composition: Option<String>,
 }
 
-/// like ServiceUrls, but with different clap defaults
+/// Provides a set clap arguments to configure tvix-[ca]store services.
+///
+/// This particular variant has defaults tailored for usecases accessing data
+/// from another running tvix daemon, via gRPC.
 #[derive(clap::Parser, Clone)]
 pub struct ServiceUrlsGrpc {
     #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
@@ -73,7 +80,13 @@ pub struct ServiceUrlsGrpc {
     experimental_store_composition: Option<String>,
 }
 
-/// like ServiceUrls, but with different clap defaults
+/// Provides a set clap arguments to configure tvix-[ca]store services.
+///
+/// This particular variant has defaults tailored for usecases keeping all data
+/// in memory.
+/// It's currently used in tvix-cli, as we don't really care about persistency
+/// there yet, and using something else here might make some perf output harder
+/// to interpret.
 #[derive(clap::Parser, Clone)]
 pub struct ServiceUrlsMemory {
     #[arg(long, env, default_value = "memory://")]
diff --git a/tvix/tools/crunch-v2/Cargo.lock b/tvix/tools/crunch-v2/Cargo.lock
index e1d25dc1d8f6..71f0516c844d 100644
--- a/tvix/tools/crunch-v2/Cargo.lock
+++ b/tvix/tools/crunch-v2/Cargo.lock
@@ -365,9 +365,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
 [[package]]
 name = "bytes"
-version = "1.5.0"
+version = "1.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
+checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
 
 [[package]]
 name = "bzip2"
@@ -627,16 +627,15 @@ dependencies = [
 
 [[package]]
 name = "curve25519-dalek"
-version = "4.1.1"
+version = "4.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c"
+checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
 dependencies = [
  "cfg-if",
  "cpufeatures",
  "curve25519-dalek-derive",
  "digest 0.10.7",
  "fiat-crypto",
- "platforms",
  "rustc_version",
  "subtle",
  "zeroize",
@@ -999,9 +998,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
 
 [[package]]
 name = "h2"
-version = "0.3.22"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178"
+checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
 dependencies = [
  "bytes",
  "fnv",
@@ -1244,6 +1243,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
 
 [[package]]
+name = "libmimalloc-sys"
+version = "0.1.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23aa6811d3bd4deb8a84dde645f943476d13b248d818edcf8ce0b2f37f036b44"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
 name = "libredox"
 version = "0.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1343,6 +1352,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "mimalloc"
+version = "0.1.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68914350ae34959d83f732418d51e2427a794055d0b9529f48259ac07af65633"
+dependencies = [
+ "libmimalloc-sys",
+]
+
+[[package]]
 name = "minimal-lexical"
 version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1359,9 +1377,9 @@ dependencies = [
 
 [[package]]
 name = "mio"
-version = "0.8.9"
+version = "0.8.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
+checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
 dependencies = [
  "libc",
  "wasi",
@@ -1402,11 +1420,13 @@ version = "0.1.0"
 dependencies = [
  "bitflags 2.4.1",
  "bstr",
+ "bytes",
  "data-encoding",
  "ed25519",
  "ed25519-dalek",
  "enum-primitive-derive",
  "glob",
+ "mimalloc",
  "nom",
  "num-traits",
  "pin-project-lite",
@@ -1415,6 +1435,7 @@ dependencies = [
  "sha2 0.10.8",
  "thiserror",
  "tokio",
+ "tracing",
 ]
 
 [[package]]
@@ -1610,12 +1631,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "platforms"
-version = "3.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0"
-
-[[package]]
 name = "polars"
 version = "0.35.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2441,9 +2456,9 @@ dependencies = [
 
 [[package]]
 name = "shlex"
-version = "1.2.0"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
 
 [[package]]
 name = "signal-hook-registry"
@@ -2763,10 +2778,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
 dependencies = [
  "pin-project-lite",
+ "tracing-attributes",
  "tracing-core",
 ]
 
 [[package]]
+name = "tracing-attributes"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
 name = "tracing-core"
 version = "0.1.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3176,18 +3203,18 @@ dependencies = [
 
 [[package]]
 name = "zerocopy"
-version = "0.7.26"
+version = "0.7.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0"
+checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
 dependencies = [
  "zerocopy-derive",
 ]
 
 [[package]]
 name = "zerocopy-derive"
-version = "0.7.26"
+version = "0.7.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f"
+checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
 dependencies = [
  "proc-macro2",
  "quote",
diff --git a/tvix/tools/crunch-v2/Cargo.nix b/tvix/tools/crunch-v2/Cargo.nix
index 811fd4a5a5cc..5af8289a6720 100644
--- a/tvix/tools/crunch-v2/Cargo.nix
+++ b/tvix/tools/crunch-v2/Cargo.nix
@@ -207,6 +207,7 @@ rec {
         edition = "2015";
         crateBin = [ ];
         sha256 = "1cy6r2sfv5y5cigv86vms7n5nlwhx1rbyxwcraqnmm1rxiib2yyc";
+        libName = "alloc_no_stdlib";
         authors = [
           "Daniel Reiter Horn <danielrh@dropbox.com>"
         ];
@@ -218,6 +219,7 @@ rec {
         edition = "2015";
         crateBin = [ ];
         sha256 = "1kkfbld20ab4165p29v172h8g0wvq8i06z8vnng14whw0isq5ywl";
+        libName = "alloc_stdlib";
         authors = [
           "Daniel Reiter Horn <danielrh@dropbox.com>"
         ];
@@ -234,6 +236,7 @@ rec {
         version = "0.2.16";
         edition = "2018";
         sha256 = "1iayppgq4wqbfbfcqmsbwgamj0s65012sskfvyx07pxavk3gyhh9";
+        libName = "allocator_api2";
         authors = [
           "Zakarum <zaq.dev@icloud.com>"
         ];
@@ -249,6 +252,7 @@ rec {
         version = "0.1.1";
         edition = "2018";
         sha256 = "1w7ynjxrfs97xg3qlcdns4kgfpwcdv824g611fq32cag4cdr96g9";
+        libName = "android_tzdata";
         authors = [
           "RumovZ"
         ];
@@ -326,6 +330,7 @@ rec {
         version = "0.2.3";
         edition = "2021";
         sha256 = "134jhzrz89labrdwxxnjxqjdg06qvaflj1wkfnmyapwyldfwcnn7";
+        libName = "anstyle_parse";
         dependencies = [
           {
             name = "utf8parse";
@@ -345,6 +350,7 @@ rec {
         version = "1.0.2";
         edition = "2021";
         sha256 = "0j3na4b1nma39g4x7cwvj009awxckjf3z2vkwhldgka44hqj72g2";
+        libName = "anstyle_query";
         dependencies = [
           {
             name = "windows-sys";
@@ -360,6 +366,7 @@ rec {
         version = "3.0.2";
         edition = "2021";
         sha256 = "19v0fv400bmp4niqpzxnhg83vz12mmqv7l2l8vi80qcdxj0lpm8w";
+        libName = "anstyle_wincon";
         dependencies = [
           {
             name = "anstyle";
@@ -424,6 +431,7 @@ rec {
         version = "0.2.0";
         edition = "2021";
         sha256 = "0xpbqf7qkvzplpjd7f0wbcf2n1v9vygdccwxkd1amxp4il0hlzdz";
+        libName = "array_init_cursor";
 
       };
       "arrayref" = rec {
@@ -455,6 +463,7 @@ rec {
         version = "0.8.1";
         edition = "2018";
         sha256 = "1irj67p6c224dzw86jr7j3z9r5zfid52gy6ml8rdqk4r2si4x207";
+        libName = "arrow_format";
         authors = [
           "Jorge C. Leitao <jorgecarleitao@gmail.com>"
         ];
@@ -490,6 +499,7 @@ rec {
         version = "0.3.5";
         edition = "2018";
         sha256 = "0l8sjq1rylkb1ak0pdyjn83b3k6x36j22myngl4sqqgg7whdsmnd";
+        libName = "async_stream";
         authors = [
           "Carl Lerche <me@carllerche.com>"
         ];
@@ -515,6 +525,7 @@ rec {
         edition = "2018";
         sha256 = "14q179j4y8p2z1d0ic6aqgy9fhwz8p9cai1ia8kpw4bw7q12mrhn";
         procMacro = true;
+        libName = "async_stream_impl";
         authors = [
           "Carl Lerche <me@carllerche.com>"
         ];
@@ -541,6 +552,7 @@ rec {
         edition = "2021";
         sha256 = "1ydhbsqjqqa6bxbv0kgys2wq2vi3jpwjy57dk162ajwppgqkfrd6";
         procMacro = true;
+        libName = "async_trait";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -792,6 +804,7 @@ rec {
         version = "0.10.4";
         edition = "2018";
         sha256 = "0w9sa2ypmrsqqvc20nhwr75wbb5cjr4kkyhpjm1z1lv2kdicfy1h";
+        libName = "block_buffer";
         authors = [
           "RustCrypto Developers"
         ];
@@ -808,6 +821,7 @@ rec {
         version = "0.9.0";
         edition = "2018";
         sha256 = "1r4pf90s7d7lj1wdjhlnqa26vvbm6pnc33z138lxpnp9srpi2lj1";
+        libName = "block_buffer";
         authors = [
           "RustCrypto Developers"
         ];
@@ -865,6 +879,7 @@ rec {
         edition = "2015";
         crateBin = [ ];
         sha256 = "0kyyh9701dwqzwvn2frff4ww0zibikqd1s1xvl7n1pfpc3z4lbjf";
+        libName = "brotli_decompressor";
         authors = [
           "Daniel Reiter Horn <danielrh@dropbox.com>"
           "The Brotli Authors"
@@ -1000,9 +1015,9 @@ rec {
       };
       "bytes" = rec {
         crateName = "bytes";
-        version = "1.5.0";
+        version = "1.6.1";
         edition = "2018";
-        sha256 = "08w2i8ac912l8vlvkv3q51cd4gr09pwlg3sjsjffcizlrb0i5gd2";
+        sha256 = "0lnryqfiymbq5mfflfmbsqvfnw80kkh36nk5kpiscgxb9ac1cad1";
         authors = [
           "Carl Lerche <me@carllerche.com>"
           "Sean McArthur <sean@seanmonstar.com>"
@@ -1100,6 +1115,7 @@ rec {
         version = "1.0.0";
         edition = "2018";
         sha256 = "1za0vb97n4brpzpv8lsbnzmq5r8f2b0cpqqr0sy8h5bn751xxwds";
+        libName = "cfg_if";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -1335,6 +1351,7 @@ rec {
         version = "0.9.5";
         edition = "2021";
         sha256 = "0vxb4d25mgk8y0phay7j078limx2553716ixsr1x5605k31j5h98";
+        libName = "const_oid";
         authors = [
           "RustCrypto Developers"
         ];
@@ -1357,6 +1374,7 @@ rec {
         version = "0.9.3";
         edition = "2015";
         sha256 = "0ii1ihpjb30fk38gdikm5wqlkmyr8k46fh4k2r8sagz5dng7ljhr";
+        libName = "core_foundation";
         authors = [
           "The Servo Project Developers"
         ];
@@ -1384,6 +1402,7 @@ rec {
         version = "0.8.4";
         edition = "2015";
         sha256 = "1yhf471qj6snnm2mcswai47vsbc9w30y4abmdp4crb4av87sb5p4";
+        libName = "core_foundation_sys";
         authors = [
           "The Servo Project Developers"
         ];
@@ -1446,6 +1465,7 @@ rec {
         version = "0.5.8";
         edition = "2018";
         sha256 = "004jz4wxp9k26z657i7rsh9s7586dklx2c5aqf1n3w1dgzvjng53";
+        libName = "crossbeam_channel";
         dependencies = [
           {
             name = "cfg-if";
@@ -1470,6 +1490,7 @@ rec {
         version = "0.8.3";
         edition = "2018";
         sha256 = "1vqczbcild7nczh5z116w8w46z991kpjyw7qxkf24c14apwdcvyf";
+        libName = "crossbeam_deque";
         dependencies = [
           {
             name = "cfg-if";
@@ -1501,6 +1522,7 @@ rec {
         version = "0.9.15";
         edition = "2018";
         sha256 = "1ixwc3cq816wb8rlh3ix4jnybqbyyq4l61nwlx0mfm3ck0s148df";
+        libName = "crossbeam_epoch";
         dependencies = [
           {
             name = "cfg-if";
@@ -1541,6 +1563,7 @@ rec {
         version = "0.3.8";
         edition = "2018";
         sha256 = "1p9s6n4ckwdgxkb7a8ay9zjzmgc8ppfbxix2vr07rwskibmb7kyi";
+        libName = "crossbeam_queue";
         dependencies = [
           {
             name = "cfg-if";
@@ -1564,6 +1587,7 @@ rec {
         version = "0.8.16";
         edition = "2018";
         sha256 = "153j0gikblz7n7qdvdi8pslhi008s1yp9cmny6vw07ad7pbb48js";
+        libName = "crossbeam_utils";
         dependencies = [
           {
             name = "cfg-if";
@@ -1593,6 +1617,7 @@ rec {
           }
         ];
         src = lib.cleanSourceWith { filter = sourceFilter; src = ./.; };
+        libName = "crunch_v2";
         dependencies = [
           {
             name = "anyhow";
@@ -1702,6 +1727,7 @@ rec {
         version = "0.1.6";
         edition = "2018";
         sha256 = "1cvby95a6xg7kxdz5ln3rl9xh66nz66w46mm3g56ri1z5x815yqv";
+        libName = "crypto_common";
         authors = [
           "RustCrypto Developers"
         ];
@@ -1727,6 +1753,7 @@ rec {
         version = "0.11.1";
         edition = "2018";
         sha256 = "05672ncc54h66vph42s0a42ljl69bwnqjh0x4xgj2v1395psildi";
+        libName = "crypto_mac";
         authors = [
           "RustCrypto Developers"
         ];
@@ -1749,9 +1776,10 @@ rec {
       };
       "curve25519-dalek" = rec {
         crateName = "curve25519-dalek";
-        version = "4.1.1";
+        version = "4.1.3";
         edition = "2021";
-        sha256 = "0p7ns5917k6369gajrsbfj24llc5zfm635yh3abla7sb5rm8r6z8";
+        sha256 = "1gmjb9dsknrr8lypmhkyjd67p1arb8mbfamlwxm7vph38my8pywp";
+        libName = "curve25519_dalek";
         authors = [
           "Isis Lovecruft <isis@patternsinthevoid.net>"
           "Henry de Valence <hdevalence@hdevalence.ca>"
@@ -1797,10 +1825,6 @@ rec {
         ];
         buildDependencies = [
           {
-            name = "platforms";
-            packageId = "platforms";
-          }
-          {
             name = "rustc_version";
             packageId = "rustc_version";
           }
@@ -1824,6 +1848,7 @@ rec {
         edition = "2021";
         sha256 = "1cry71xxrr0mcy5my3fb502cwfxy6822k4pm19cwrilrg7hq4s7l";
         procMacro = true;
+        libName = "curve25519_dalek_derive";
         dependencies = [
           {
             name = "proc-macro2";
@@ -1846,6 +1871,7 @@ rec {
         version = "2.6.0";
         edition = "2018";
         sha256 = "1qnn68n4vragxaxlkqcb1r28d3hhj43wch67lm4rpxlw89wnjmp8";
+        libName = "data_encoding";
         authors = [
           "Julien Cretin <git@ia0.eu>"
         ];
@@ -1950,6 +1976,7 @@ rec {
         version = "2.0.0";
         edition = "2018";
         sha256 = "1q9kr151h9681wwp6is18750ssghz6j9j7qm7qi1ngcwy7mzi35r";
+        libName = "dirs_next";
         authors = [
           "The @xdg-rs members"
         ];
@@ -1970,6 +1997,7 @@ rec {
         version = "0.1.2";
         edition = "2018";
         sha256 = "0kavhavdxv4phzj4l0psvh55hszwnr0rcz8sxbvx20pyqi2a3gaf";
+        libName = "dirs_sys_next";
         authors = [
           "The @xdg-rs members"
         ];
@@ -1999,6 +2027,7 @@ rec {
         version = "1.0.16";
         edition = "2018";
         sha256 = "0pa9kas6a241pbx0q82ipwi4f7m7wwyzkkc725caky24gl4j4nsl";
+        libName = "dyn_clone";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -2041,6 +2070,7 @@ rec {
         version = "2.1.0";
         edition = "2021";
         sha256 = "1h13qm789m9gdjl6jazss80hqi8ll37m0afwcnw23zcbqjp8wqhz";
+        libName = "ed25519_dalek";
         authors = [
           "isis lovecruft <isis@patternsinthevoid.net>"
           "Tony Arcieri <bascule@gmail.com>"
@@ -2148,6 +2178,7 @@ rec {
         edition = "2018";
         sha256 = "0k6wcf58h5kh64yq5nfq71va53kaya0kzxwsjwbgwm2n2zd9axxs";
         procMacro = true;
+        libName = "enum_primitive_derive";
         authors = [
           "Doug Goldstein <cardoe@cardoe.com>"
         ];
@@ -2264,6 +2295,7 @@ rec {
         version = "0.1.9";
         edition = "2015";
         sha256 = "0nj6j26p71bjy8h42x6jahx1hn0ng6mc2miwpgwnp8vnwqf4jq3k";
+        libName = "fallible_streaming_iterator";
         authors = [
           "Steven Fackler <sfackler@gmail.com>"
         ];
@@ -2274,6 +2306,7 @@ rec {
         version = "0.2.0";
         edition = "2018";
         sha256 = "0g7kfll3xyh99kc7r352lhljnwvgayxxa6saifb6725inikmyxlm";
+        libName = "fast_float";
         authors = [
           "Ivan Smirnov <i.s.smirnov@gmail.com>"
         ];
@@ -2319,6 +2352,7 @@ rec {
         version = "0.2.5";
         edition = "2018";
         sha256 = "1dxn0g50pv0ppal779vi7k40fr55pbhkyv4in7i13pgl4sn3wmr7";
+        libName = "fiat_crypto";
         authors = [
           "Fiat Crypto library authors <jgross@mit.edu>"
         ];
@@ -2498,6 +2532,7 @@ rec {
         version = "0.3.29";
         edition = "2018";
         sha256 = "1jxsifvrbqzdadk0svbax71cba5d3qg3wgjq8i160mxmd1kdckgz";
+        libName = "futures_channel";
         dependencies = [
           {
             name = "futures-core";
@@ -2525,6 +2560,7 @@ rec {
         version = "0.3.29";
         edition = "2018";
         sha256 = "1308bpj0g36nhx2y6bl4mm6f1gnh9xyvvw2q2wpdgnb6dv3247gb";
+        libName = "futures_core";
         features = {
           "default" = [ "std" ];
           "portable-atomic" = [ "dep:portable-atomic" ];
@@ -2537,6 +2573,7 @@ rec {
         version = "0.3.29";
         edition = "2018";
         sha256 = "1g4pjni0sw28djx6mlcfz584abm2lpifz86cmng0kkxh7mlvhkqg";
+        libName = "futures_executor";
         dependencies = [
           {
             name = "futures-core";
@@ -2567,6 +2604,7 @@ rec {
         version = "0.3.29";
         edition = "2018";
         sha256 = "1ajsljgny3zfxwahba9byjzclrgvm1ypakca8z854k2w7cb4mwwb";
+        libName = "futures_io";
         features = {
           "default" = [ "std" ];
         };
@@ -2578,6 +2616,7 @@ rec {
         edition = "2018";
         sha256 = "1nwd18i8kvpkdfwm045hddjli0n96zi7pn6f99zi9c74j7ym7cak";
         procMacro = true;
+        libName = "futures_macro";
         dependencies = [
           {
             name = "proc-macro2";
@@ -2600,6 +2639,7 @@ rec {
         version = "0.3.29";
         edition = "2018";
         sha256 = "05q8jykqddxzp8nwf00wjk5m5mqi546d7i8hsxma7hiqxrw36vg3";
+        libName = "futures_sink";
         features = {
           "default" = [ "std" ];
           "std" = [ "alloc" ];
@@ -2611,6 +2651,7 @@ rec {
         version = "0.3.29";
         edition = "2018";
         sha256 = "1qmsss8rb5ppql4qvd4r70h9gpfcpd0bg2b3qilxrnhdkc397lgg";
+        libName = "futures_task";
         features = {
           "default" = [ "std" ];
           "std" = [ "alloc" ];
@@ -2622,6 +2663,7 @@ rec {
         version = "0.3.29";
         edition = "2018";
         sha256 = "0141rkqh0psj4h8x8lgsl1p29dhqr7z2wcixkcbs60z74kb2d5d1";
+        libName = "futures_util";
         dependencies = [
           {
             name = "futures-channel";
@@ -2825,9 +2867,9 @@ rec {
       };
       "h2" = rec {
         crateName = "h2";
-        version = "0.3.22";
+        version = "0.3.26";
         edition = "2018";
-        sha256 = "0y41jlflvw8niifdirgng67zdmic62cjf5m2z69hzrpn5qr50qjd";
+        sha256 = "1s7msnfv7xprzs6xzfj5sg6p8bjcdpcqcmjjbkd345cyi1x55zl1";
         authors = [
           "Carl Lerche <me@carllerche.com>"
           "Sean McArthur <sean@seanmonstar.com>"
@@ -2964,6 +3006,7 @@ rec {
         version = "0.3.3";
         edition = "2021";
         sha256 = "1dyc8qsjh876n74a3rcz8h43s27nj1sypdhsn2ms61bd3b47wzyp";
+        libName = "hermit_abi";
         authors = [
           "Stefan Lankes"
         ];
@@ -3068,6 +3111,7 @@ rec {
         version = "0.4.5";
         edition = "2018";
         sha256 = "1l967qwwlvhp198xdrnc0p5d7jwfcp6q2lm510j6zqw4s4b8zwym";
+        libName = "http_body";
         authors = [
           "Carl Lerche <me@carllerche.com>"
           "Lucio Franco <luciofranco14@gmail.com>"
@@ -3224,6 +3268,7 @@ rec {
         version = "0.23.2";
         edition = "2018";
         sha256 = "0736s6a32dqr107f943xaz1n05flbinq6l19lq1wsrxkc5g9d20p";
+        libName = "hyper_rustls";
         dependencies = [
           {
             name = "http";
@@ -3298,6 +3343,7 @@ rec {
         version = "0.1.58";
         edition = "2018";
         sha256 = "081vcr8z8ddhl5r1ywif6grnswk01b2ac4nks2bhn8zzdimvh9l3";
+        libName = "iana_time_zone";
         authors = [
           "Andrew Straw <strawman@astraw.com>"
           "René Kijewski <rene.kijewski@fu-berlin.de>"
@@ -3343,6 +3389,7 @@ rec {
         version = "0.1.2";
         edition = "2018";
         sha256 = "17r6jmj31chn7xs9698r122mapq85mfnv98bb4pg6spm0si2f67k";
+        libName = "iana_time_zone_haiku";
         authors = [
           "René Kijewski <crates.io@k6i.de>"
         ];
@@ -3503,6 +3550,7 @@ rec {
         version = "0.3.65";
         edition = "2018";
         sha256 = "1s1gaxgzpqfyygc7f2pwp9y128rh5f8zvsc4nm5yazgna9cw7h2l";
+        libName = "js_sys";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -3557,6 +3605,33 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" ];
       };
+      "libmimalloc-sys" = rec {
+        crateName = "libmimalloc-sys";
+        version = "0.1.39";
+        edition = "2018";
+        links = "mimalloc";
+        sha256 = "0i3b0dzz7cp0ik7ys66q92r16va78gwlbrnxhj5fnkdxsc8niai3";
+        libName = "libmimalloc_sys";
+        authors = [
+          "Octavian Oncescu <octavonce@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+        features = {
+          "cty" = [ "dep:cty" ];
+          "extended" = [ "cty" ];
+        };
+      };
       "libredox" = rec {
         crateName = "libredox";
         version = "0.0.1";
@@ -3590,6 +3665,7 @@ rec {
         version = "0.4.11";
         edition = "2021";
         sha256 = "0adqqaya81s7k5r323g65pw6q85pxd1x4prz9whh5i4abysqi54n";
+        libName = "linux_raw_sys";
         authors = [
           "Dan Gohman <dev@sunfishcode.online>"
         ];
@@ -3677,6 +3753,7 @@ rec {
         edition = "2015";
         links = "lz4";
         sha256 = "0059ik4xlvnss5qfh6l691psk4g3350ljxaykzv10yr0gqqppljp";
+        libName = "lz4_sys";
         authors = [
           "Jens Heyens <jens.heyens@ewetel.net>"
           "Artem V. Navrotskiy <bozaro@buzzsoft.ru>"
@@ -3702,6 +3779,7 @@ rec {
         edition = "2018";
         links = "lzma";
         sha256 = "09sxp20waxyglgn3cjz8qjkspb3ryz2fwx4rigkwvrk46ymh9njz";
+        libName = "lzma_sys";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -3818,11 +3896,40 @@ rec {
         features = { };
         resolvedDefaultFeatures = [ "default" ];
       };
+      "mimalloc" = rec {
+        crateName = "mimalloc";
+        version = "0.1.43";
+        edition = "2018";
+        sha256 = "0csnyrxc16i592gm5ffham07jyj2w98qsh9jyy1rv59lmr8474b8";
+        authors = [
+          "Octavian Oncescu <octavonce@gmail.com>"
+          "Vincent Rouillé <vincent@speedy37.fr>"
+          "Thom Chiovoloni <chiovolonit@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libmimalloc-sys";
+            packageId = "libmimalloc-sys";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "debug" = [ "libmimalloc-sys/debug" ];
+          "debug_in_debug" = [ "libmimalloc-sys/debug_in_debug" ];
+          "extended" = [ "libmimalloc-sys/extended" ];
+          "local_dynamic_tls" = [ "libmimalloc-sys/local_dynamic_tls" ];
+          "no_thp" = [ "libmimalloc-sys/no_thp" ];
+          "override" = [ "libmimalloc-sys/override" ];
+          "secure" = [ "libmimalloc-sys/secure" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
       "minimal-lexical" = rec {
         crateName = "minimal-lexical";
         version = "0.2.1";
         edition = "2018";
         sha256 = "16ppc5g84aijpri4jzv14rvcnslvlpphbszc7zzp6vfkddf4qdb8";
+        libName = "minimal_lexical";
         authors = [
           "Alex Huszagh <ahuszagh@gmail.com>"
         ];
@@ -3860,9 +3967,9 @@ rec {
       };
       "mio" = rec {
         crateName = "mio";
-        version = "0.8.9";
+        version = "0.8.11";
         edition = "2018";
-        sha256 = "1l23hg513c23nhcdzvk25caaj28mic6qgqadbn8axgj6bqf2ikix";
+        sha256 = "034byyl0ardml5yliy1hmvx8arkmn9rv479pid794sm07ia519m4";
         authors = [
           "Carl Lerche <me@carllerche.com>"
           "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
@@ -3943,6 +4050,7 @@ rec {
         edition = "2021";
         sha256 = "1j1avbxw7jscyi7dmnywhlwbiny1fvg1vpp9fy4dc1pd022kva16";
         procMacro = true;
+        libName = "multiversion_macros";
         authors = [
           "Caleb Zulawski <caleb.zulawski@gmail.com>"
         ];
@@ -3976,6 +4084,7 @@ rec {
         edition = "2021";
         crateBin = [ ];
         src = lib.cleanSourceWith { filter = sourceFilter; src = ../../nix-compat; };
+        libName = "nix_compat";
         dependencies = [
           {
             name = "bitflags";
@@ -3987,6 +4096,11 @@ rec {
             features = [ "alloc" "unicode" "serde" ];
           }
           {
+            name = "bytes";
+            packageId = "bytes";
+            optional = true;
+          }
+          {
             name = "data-encoding";
             packageId = "data-encoding";
           }
@@ -4007,6 +4121,10 @@ rec {
             packageId = "glob";
           }
           {
+            name = "mimalloc";
+            packageId = "mimalloc";
+          }
+          {
             name = "nom";
             packageId = "nom";
           }
@@ -4042,21 +4160,30 @@ rec {
             optional = true;
             features = [ "io-util" "macros" ];
           }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
         ];
         devDependencies = [
           {
+            name = "mimalloc";
+            packageId = "mimalloc";
+          }
+          {
             name = "serde_json";
             packageId = "serde_json";
           }
         ];
         features = {
           "async" = [ "tokio" ];
+          "bytes" = [ "dep:bytes" ];
           "default" = [ "async" "wire" ];
           "pin-project-lite" = [ "dep:pin-project-lite" ];
           "tokio" = [ "dep:tokio" ];
-          "wire" = [ "tokio" "pin-project-lite" ];
+          "wire" = [ "tokio" "pin-project-lite" "bytes" ];
         };
-        resolvedDefaultFeatures = [ "async" "default" "pin-project-lite" "tokio" "wire" ];
+        resolvedDefaultFeatures = [ "async" "bytes" "default" "pin-project-lite" "tokio" "wire" ];
       };
       "nom" = rec {
         crateName = "nom";
@@ -4128,6 +4255,7 @@ rec {
         version = "0.2.19";
         edition = "2021";
         sha256 = "0h984rhdkkqd4ny9cif7y2azl3xdfb7768hb9irhpsch4q3gq787";
+        libName = "num_traits";
         authors = [
           "The Rust Project Developers"
         ];
@@ -4240,6 +4368,7 @@ rec {
         version = "0.3.0";
         edition = "2018";
         sha256 = "1m8kzi4nd6shdqimn0mgb24f0hxslhnqd1whakyq06wcqd086jk2";
+        libName = "opaque_debug";
         authors = [
           "RustCrypto Developers"
         ];
@@ -4250,6 +4379,7 @@ rec {
         version = "0.1.5";
         edition = "2015";
         sha256 = "1kq18qm48rvkwgcggfkqq6pm948190czqc94d6bm2sir5hq1l0gz";
+        libName = "openssl_probe";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -4405,6 +4535,7 @@ rec {
         version = "0.2.4";
         edition = "2021";
         sha256 = "07wf6wf4jrxlq5p3xldxsnabp7jl06my2qp7kiwy9m3x2r5wac8i";
+        libName = "parquet_format_safe";
         authors = [
           "Apache Thrift contributors <dev@thrift.apache.org>"
           "Jorge Leitao <jorgecarleitao@gmail.com>"
@@ -4434,6 +4565,7 @@ rec {
         version = "2.3.0";
         edition = "2018";
         sha256 = "152slflmparkh27hprw62sph8rv77wckzhwl2dhqk6bf563lfalv";
+        libName = "percent_encoding";
         authors = [
           "The rust-url developers"
         ];
@@ -4478,6 +4610,7 @@ rec {
         version = "0.2.13";
         edition = "2018";
         sha256 = "0n0bwr5qxlf0mhn2xkl36sy55118s9qmvx2yl5f3ixkb007lbywa";
+        libName = "pin_project_lite";
 
       };
       "pin-utils" = rec {
@@ -4485,6 +4618,7 @@ rec {
         version = "0.1.0";
         edition = "2018";
         sha256 = "117ir7vslsl2z1a7qzhws4pd01cg2d3338c47swjyvqv2n60v1wb";
+        libName = "pin_utils";
         authors = [
           "Josef Brandl <mail@josefbrandl.de>"
         ];
@@ -4529,6 +4663,7 @@ rec {
         version = "0.3.27";
         edition = "2015";
         sha256 = "0r39ryh1magcq4cz5g9x88jllsnxnhcqr753islvyk4jp9h2h1r6";
+        libName = "pkg_config";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -4550,21 +4685,6 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" "std" ];
       };
-      "platforms" = rec {
-        crateName = "platforms";
-        version = "3.2.0";
-        edition = "2018";
-        sha256 = "1c6bzwn877aqdbbmyqsl753ycbciwvbdh4lpzijb8vrfb4zsprhl";
-        authors = [
-          "Tony Arcieri <bascule@gmail.com>"
-          "Sergey \"Shnatsel\" Davidoff <shnatsel@gmail.com>"
-        ];
-        features = {
-          "default" = [ "std" ];
-          "serde" = [ "dep:serde" ];
-        };
-        resolvedDefaultFeatures = [ "default" "std" ];
-      };
       "polars" = rec {
         crateName = "polars";
         version = "0.35.4";
@@ -4759,6 +4879,7 @@ rec {
         version = "0.35.4";
         edition = "2021";
         sha256 = "1rxa9dfsqy7mh4w9gy7y7kpig0wrzrjqi1axj43rnxyrlqq38l6x";
+        libName = "polars_arrow";
         authors = [
           "Jorge C. Leitao <jorgecarleitao@gmail.com>"
           "Apache Arrow <dev@arrow.apache.org>"
@@ -4937,6 +5058,7 @@ rec {
         version = "0.35.4";
         edition = "2021";
         sha256 = "1mnginlmgmlp167ij0r5lywvy50zns1cr8db1ikxxv2xwnwdawxf";
+        libName = "polars_core";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -5086,6 +5208,7 @@ rec {
         version = "0.35.4";
         edition = "2021";
         sha256 = "127l5hazh5hn73j767cfyjwgbvvbab8hj53l1jp976daivb201gb";
+        libName = "polars_error";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -5122,6 +5245,7 @@ rec {
         version = "0.35.4";
         edition = "2021";
         sha256 = "01mwcdikw7y92xjhlsmj4wf0p9r3kvmhrvsbnsfh1mmc8l3hmqcn";
+        libName = "polars_io";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -5280,6 +5404,7 @@ rec {
         version = "0.35.4";
         edition = "2021";
         sha256 = "084gp9qa1w9b7dglcmrvlx6xrcl7hw5nmlb26w6xvrjvf1czfm9m";
+        libName = "polars_lazy";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -5469,6 +5594,7 @@ rec {
         version = "0.35.4";
         edition = "2021";
         sha256 = "0dpnxcgc57k8xvp4jmjhz8jwz8rclf62h22zjiwpzaka54cb4zhs";
+        libName = "polars_ops";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -5609,6 +5735,7 @@ rec {
         version = "0.35.4";
         edition = "2021";
         sha256 = "1pb79wb1f31pk48lfm2w72hdfxqz6vv65iyxb072skfxnzj10q0l";
+        libName = "polars_parquet";
         authors = [
           "Jorge C. Leitao <jorgecarleitao@gmail.com>"
           "Apache Arrow <dev@arrow.apache.org>"
@@ -5729,6 +5856,7 @@ rec {
         version = "0.35.4";
         edition = "2021";
         sha256 = "1v5xniw8yxx9cnksc4bf169b3p7gcl6dzryzgfd2m4scyrylw2b6";
+        libName = "polars_pipe";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -5836,6 +5964,7 @@ rec {
         version = "0.35.4";
         edition = "2021";
         sha256 = "1g2z0g0hpg05jp4n9s6m6m32adrjrdlq6zxd5c9lp1ggb04jmqqh";
+        libName = "polars_plan";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -6019,6 +6148,7 @@ rec {
         version = "0.35.4";
         edition = "2021";
         sha256 = "1b3d8pdxz12dzg75nqb7b2p83l15c1z4r6589sknp462ra0sndfi";
+        libName = "polars_row";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -6048,6 +6178,7 @@ rec {
         version = "0.35.4";
         edition = "2021";
         sha256 = "17jiflfmyymz1fdk2mrsdri2yqs1h7rqn66y3yny79a9d1wdgnxq";
+        libName = "polars_sql";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -6112,6 +6243,7 @@ rec {
         version = "0.35.4";
         edition = "2021";
         sha256 = "0lgyk3fpp7krbyfra51pb4fqn6m3hk5gbj61fdvn3pffx5wnzrda";
+        libName = "polars_time";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -6190,6 +6322,7 @@ rec {
         version = "0.35.4";
         edition = "2021";
         sha256 = "1z7v7h54p883ww64mqpn4cphzwv0fd2bgsn8b1lx8qgyd60ycv6s";
+        libName = "polars_utils";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -6257,6 +6390,7 @@ rec {
         version = "1.5.1";
         edition = "2018";
         sha256 = "0fxg0i7n3wmffbfn95nwi062srdg40bwkj5143w1kk6pgw7apk1v";
+        libName = "portable_atomic";
         features = {
           "critical-section" = [ "dep:critical-section" ];
           "default" = [ "fallback" ];
@@ -6269,6 +6403,7 @@ rec {
         version = "0.2.17";
         edition = "2018";
         sha256 = "1pp6g52aw970adv3x2310n7glqnji96z0a9wiamzw89ibf0ayh2v";
+        libName = "ppv_lite86";
         authors = [
           "The CryptoCorrosion Contributors"
         ];
@@ -6316,6 +6451,7 @@ rec {
         version = "1.0.69";
         edition = "2021";
         sha256 = "1nljgyllbm3yr3pa081bf83gxh6l4zvjqzaldw7v4mj9xfgihk0k";
+        libName = "proc_macro2";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
           "Alex Crichton <alex@alexcrichton.com>"
@@ -6364,6 +6500,7 @@ rec {
         version = "0.12.2";
         edition = "2021";
         sha256 = "0f57mmf6cg7f4401x9s3fgdc1idnz7i1nxxjxyzi2jbhr22d18qz";
+        libName = "prost_build";
         authors = [
           "Dan Burkert <dan@danburkert.com>"
           "Lucio Franco <luciofranco14@gmail.com>"
@@ -6456,6 +6593,7 @@ rec {
         edition = "2021";
         sha256 = "1g268fzmswaf6rx1sm8000h6a4rigd4b6zg55wysi95cvyjifmq6";
         procMacro = true;
+        libName = "prost_derive";
         authors = [
           "Dan Burkert <dan@danburkert.com>"
           "Lucio Franco <luciofranco14@gmail.com>"
@@ -6493,6 +6631,7 @@ rec {
         version = "0.12.2";
         edition = "2021";
         sha256 = "0lls5w7yh2jxi764kd9k44dwskrr85j2fs335wg2i47m6qig6fc3";
+        libName = "prost_types";
         authors = [
           "Dan Burkert <dan@danburkert.com>"
           "Lucio Franco <luciofranco14@gmail.com"
@@ -6697,6 +6836,7 @@ rec {
         edition = "2021";
         links = "rayon-core";
         sha256 = "1vaq0q71yfvcwlmia0iqf6ixj2fibjcf2xjy92n1m1izv1mgpqsw";
+        libName = "rayon_core";
         authors = [
           "Niko Matsakis <niko@alum.mit.edu>"
           "Josh Stone <cuviper@gmail.com>"
@@ -6844,6 +6984,7 @@ rec {
         version = "0.4.3";
         edition = "2021";
         sha256 = "0gs8q9yhd3kcg4pr00ag4viqxnh5l7jpyb9fsfr8hzh451w4r02z";
+        libName = "regex_automata";
         authors = [
           "The Rust Project Developers"
           "Andrew Gallant <jamslam@gmail.com>"
@@ -6904,6 +7045,7 @@ rec {
         version = "0.8.2";
         edition = "2021";
         sha256 = "17rd2s8xbiyf6lb4aj2nfi44zqlj98g2ays8zzj2vfs743k79360";
+        libName = "regex_syntax";
         authors = [
           "The Rust Project Developers"
           "Andrew Gallant <jamslam@gmail.com>"
@@ -7384,6 +7526,7 @@ rec {
         version = "0.1.23";
         edition = "2015";
         sha256 = "0xnbk2bmyzshacjm2g1kd4zzv2y2az14bw3sjccq5qkpmsfvn9nn";
+        libName = "rustc_demangle";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -7564,6 +7707,7 @@ rec {
         version = "0.6.3";
         edition = "2021";
         sha256 = "007zind70rd5rfsrkdcfm8vn09j8sg02phg9334kark6rdscxam9";
+        libName = "rustls_native_certs";
         dependencies = [
           {
             name = "openssl-probe";
@@ -7592,6 +7736,7 @@ rec {
         version = "1.0.4";
         edition = "2018";
         sha256 = "1324n5bcns0rnw6vywr5agff3rwfvzphi7rmbyzwnv6glkhclx0w";
+        libName = "rustls_pemfile";
         dependencies = [
           {
             name = "base64";
@@ -7686,6 +7831,7 @@ rec {
         version = "2.9.2";
         edition = "2021";
         sha256 = "1pplxk15s5yxvi2m1sz5xfmjibp96cscdcl432w9jzbk0frlzdh5";
+        libName = "security_framework";
         authors = [
           "Steven Fackler <sfackler@gmail.com>"
           "Kornel <kornel@geekhood.net>"
@@ -7732,6 +7878,7 @@ rec {
         version = "2.9.1";
         edition = "2021";
         sha256 = "0yhciwlsy9dh0ps1gw3197kvyqx1bvc4knrhiznhid6kax196cp9";
+        libName = "security_framework_sys";
         authors = [
           "Steven Fackler <sfackler@gmail.com>"
           "Kornel <kornel@geekhood.net>"
@@ -7777,6 +7924,7 @@ rec {
         edition = "2018";
         sha256 = "1d50kbaslrrd0374ivx15jg57f03y5xzil1wd2ajlvajzlkbzw53";
         procMacro = true;
+        libName = "seq_macro";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -7981,6 +8129,7 @@ rec {
         version = "0.6.3";
         edition = "2018";
         sha256 = "0kp480744vkwg3fqx98379nsdw1lzzzimd88v0qgpqqic03afyzj";
+        libName = "sha2_asm";
         authors = [
           "RustCrypto Developers"
         ];
@@ -7994,12 +8143,16 @@ rec {
       };
       "shlex" = rec {
         crateName = "shlex";
-        version = "1.2.0";
+        version = "1.3.0";
         edition = "2015";
-        sha256 = "1033pj9dyb76nm5yv597nnvj3zpvr2aw9rm5wy0gah3dk99f1km7";
+        sha256 = "0r1y6bv26c1scpxvhg2cabimrmwgbp4p3wy6syj9n0c4s3q2znhg";
         authors = [
           "comex <comexk@gmail.com>"
           "Fenhl <fenhl@fenhl.net>"
+          "Adrian Taylor <adetaylor@chromium.org>"
+          "Alex Touchet <alextouchet@outlook.com>"
+          "Daniel Parks <dp+git@oxidized.org>"
+          "Garrett Berg <googberg@gmail.com>"
         ];
         features = {
           "default" = [ "std" ];
@@ -8011,6 +8164,7 @@ rec {
         version = "1.4.1";
         edition = "2015";
         sha256 = "18crkkw5k82bvcx088xlf5g4n3772m24qhzgfan80nda7d3rn8nq";
+        libName = "signal_hook_registry";
         authors = [
           "Michal 'vorner' Vaner <vorner@vorner.cz>"
           "Masaki Hara <ackie.h.gmai@gmail.com>"
@@ -8359,6 +8513,7 @@ rec {
         version = "0.1.2";
         edition = "2018";
         sha256 = "1wscqj3s30qknda778wf7z99mknk65p0h9hhs658l4pvkfqw6v5z";
+        libName = "streaming_decompression";
         dependencies = [
           {
             name = "fallible-streaming-iterator";
@@ -8372,6 +8527,7 @@ rec {
         version = "0.1.9";
         edition = "2021";
         sha256 = "0845zdv8qb7zwqzglpqc0830i43xh3fb6vqms155wz85qfvk28ib";
+        libName = "streaming_iterator";
         authors = [
           "Steven Fackler <sfackler@gmail.com>"
         ];
@@ -8568,6 +8724,7 @@ rec {
         version = "0.1.5";
         edition = "2021";
         sha256 = "1gb974chm9aj8ifkyibylxkyb5an4bf5y8dxb18pqmck698gmdfg";
+        libName = "target_features";
         authors = [
           "Caleb Zulawski <caleb.zulawski@gmail.com>"
         ];
@@ -8635,6 +8792,7 @@ rec {
         edition = "2021";
         sha256 = "1f0lmam4765sfnwr4b1n00y14vxh10g0311mkk0adr80pi02wsr6";
         procMacro = true;
+        libName = "thiserror_impl";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -8770,6 +8928,7 @@ rec {
         edition = "2021";
         sha256 = "0fwjy4vdx1h9pi4g2nml72wi0fr27b5m954p13ji9anyy8l1x2jv";
         procMacro = true;
+        libName = "tokio_macros";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -8795,6 +8954,7 @@ rec {
         version = "0.23.4";
         edition = "2018";
         sha256 = "0nfsmmi8l1lgpbfy6079d5i13984djzcxrdr9jc06ghi0cwyhgn4";
+        libName = "tokio_rustls";
         authors = [
           "quininer kel <quininer@live.com>"
         ];
@@ -8833,6 +8993,7 @@ rec {
         version = "0.7.10";
         edition = "2021";
         sha256 = "058y6x4mf0fsqji9rfyb77qbfyc50y4pk2spqgj6xsyr693z66al";
+        libName = "tokio_util";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -8895,6 +9056,7 @@ rec {
         version = "0.3.2";
         edition = "2018";
         sha256 = "0lmfzmmvid2yp2l36mbavhmqgsvzqf7r2wiwz73ml4xmwaf1rg5n";
+        libName = "tower_service";
         authors = [
           "Tower Maintainers <team@tower-rs.com>"
         ];
@@ -8915,6 +9077,11 @@ rec {
             packageId = "pin-project-lite";
           }
           {
+            name = "tracing-attributes";
+            packageId = "tracing-attributes";
+            optional = true;
+          }
+          {
             name = "tracing-core";
             packageId = "tracing-core";
             usesDefaultFeatures = false;
@@ -8929,13 +9096,44 @@ rec {
           "tracing-attributes" = [ "dep:tracing-attributes" ];
           "valuable" = [ "tracing-core/valuable" ];
         };
-        resolvedDefaultFeatures = [ "std" ];
+        resolvedDefaultFeatures = [ "attributes" "default" "std" "tracing-attributes" ];
+      };
+      "tracing-attributes" = rec {
+        crateName = "tracing-attributes";
+        version = "0.1.27";
+        edition = "2018";
+        sha256 = "1rvb5dn9z6d0xdj14r403z0af0bbaqhg02hq4jc97g5wds6lqw1l";
+        procMacro = true;
+        libName = "tracing_attributes";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+          "Eliza Weisman <eliza@buoyant.io>"
+          "David Barsky <dbarsky@amazon.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            usesDefaultFeatures = false;
+            features = [ "full" "parsing" "printing" "visit-mut" "clone-impls" "extra-traits" "proc-macro" ];
+          }
+        ];
+        features = { };
       };
       "tracing-core" = rec {
         crateName = "tracing-core";
         version = "0.1.32";
         edition = "2018";
         sha256 = "0m5aglin3cdwxpvbg6kz0r9r0k31j48n0kcfwsp6l49z26k3svf0";
+        libName = "tracing_core";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -8959,6 +9157,7 @@ rec {
         version = "0.2.4";
         edition = "2015";
         sha256 = "1vc15paa4zi06ixsxihwbvfn24d708nsyg1ncgqwcrn42byyqa1m";
+        libName = "try_lock";
         authors = [
           "Sean McArthur <sean@seanmonstar.com>"
         ];
@@ -8984,6 +9183,7 @@ rec {
         version = "1.0.12";
         edition = "2018";
         sha256 = "0jzf1znfpb2gx8nr8mvmyqs1crnv79l57nxnbiszc7xf7ynbjm1k";
+        libName = "unicode_ident";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -8994,6 +9194,7 @@ rec {
         version = "0.1.11";
         edition = "2015";
         sha256 = "11ds4ydhg8g7l06rlmh712q41qsrd0j0h00n1jm74kww3kqk65z5";
+        libName = "unicode_width";
         authors = [
           "kwantam <kwantam@gmail.com>"
           "Manish Goregaokar <manishsmail@gmail.com>"
@@ -9087,6 +9288,7 @@ rec {
         version = "0.2.88";
         edition = "2018";
         sha256 = "1khgsh4z9bga35mjhg41dl7523i69ffc5m8ckhqaw6ssyabc5bkx";
+        libName = "wasm_bindgen";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -9118,6 +9320,7 @@ rec {
         version = "0.2.88";
         edition = "2018";
         sha256 = "05zj8yl243rvs87rhicq2l1d6443lnm6k90khf744khf9ikg95z3";
+        libName = "wasm_bindgen_backend";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -9163,6 +9366,7 @@ rec {
         edition = "2018";
         sha256 = "1chn3wgw9awmvs0fpmazbqyc5rwfgy3pj7lzwczmzb887dxh2qar";
         procMacro = true;
+        libName = "wasm_bindgen_macro";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -9187,6 +9391,7 @@ rec {
         version = "0.2.88";
         edition = "2018";
         sha256 = "01rrzg3y1apqygsjz1jg0n7p831nm4kdyxmxyl85x7v6mf6kndf5";
+        libName = "wasm_bindgen_macro_support";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -9225,6 +9430,7 @@ rec {
         edition = "2018";
         links = "wasm_bindgen";
         sha256 = "02vmw2rzsla1qm0zgfng4kqz52xn8k54v8ads4g1macv09fnq10d";
+        libName = "wasm_bindgen_shared";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -9235,6 +9441,7 @@ rec {
         version = "0.3.65";
         edition = "2018";
         sha256 = "11ba406ca9qssc21c37v49sn2y2gsdn6c3nva4hjf8v3yv2rkd2x";
+        libName = "web_sys";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -9788,6 +9995,7 @@ rec {
         version = "0.4.0";
         edition = "2015";
         sha256 = "1dmpa6mvcvzz16zg6d5vrfy4bxgg541wxrcip7cnshi06v38ffxc";
+        libName = "winapi_i686_pc_windows_gnu";
         authors = [
           "Peter Atashian <retep998@gmail.com>"
         ];
@@ -9798,6 +10006,7 @@ rec {
         version = "0.4.0";
         edition = "2015";
         sha256 = "0gqq64czqb64kskjryj8isp62m2sgvx25yyj3kpc2myh85w24bki";
+        libName = "winapi_x86_64_pc_windows_gnu";
         authors = [
           "Peter Atashian <retep998@gmail.com>"
         ];
@@ -9808,6 +10017,7 @@ rec {
         version = "0.51.1";
         edition = "2021";
         sha256 = "0r1f57hsshsghjyc7ypp2s0i78f7b1vr93w68sdb8baxyf2czy7i";
+        libName = "windows_core";
         authors = [
           "Microsoft"
         ];
@@ -9825,6 +10035,7 @@ rec {
         version = "0.45.0";
         edition = "2018";
         sha256 = "1l36bcqm4g89pknfp8r9rl1w4bn017q6a8qlx8viv0xjxzjkna3m";
+        libName = "windows_sys";
         authors = [
           "Microsoft"
         ];
@@ -10111,6 +10322,7 @@ rec {
         version = "0.48.0";
         edition = "2018";
         sha256 = "1aan23v5gs7gya1lc46hqn9mdh8yph3fhxmhxlw36pn6pqc28zb7";
+        libName = "windows_sys";
         authors = [
           "Microsoft"
         ];
@@ -10404,6 +10616,7 @@ rec {
         version = "0.52.0";
         edition = "2021";
         sha256 = "0gd3v4ji88490zgb6b5mq5zgbvwv7zx1ibn8v3x83rwcdbryaar8";
+        libName = "windows_sys";
         authors = [
           "Microsoft"
         ];
@@ -10651,6 +10864,7 @@ rec {
         version = "0.42.2";
         edition = "2018";
         sha256 = "0wfhnib2fisxlx8c507dbmh97kgij4r6kcxdi0f9nk6l1k080lcf";
+        libName = "windows_targets";
         authors = [
           "Microsoft"
         ];
@@ -10723,6 +10937,7 @@ rec {
         version = "0.48.5";
         edition = "2018";
         sha256 = "034ljxqshifs1lan89xwpcy1hp0lhdh4b5n0d2z4fwjx2piacbws";
+        libName = "windows_targets";
         authors = [
           "Microsoft"
         ];
@@ -10770,6 +10985,7 @@ rec {
         version = "0.52.0";
         edition = "2021";
         sha256 = "1kg7a27ynzw8zz3krdgy6w5gbqcji27j1sz4p7xk2j5j8082064a";
+        libName = "windows_targets";
         authors = [
           "Microsoft"
         ];
@@ -11039,6 +11255,7 @@ rec {
         version = "0.8.7";
         edition = "2018";
         sha256 = "0yz037yrkn0qa0g0r6733ynd1xbw7zvx58v6qylhyi2kv9wb2a4q";
+        libName = "xxhash_rust";
         authors = [
           "Douman <douman@gmx.se>"
         ];
@@ -11068,9 +11285,9 @@ rec {
       };
       "zerocopy" = rec {
         crateName = "zerocopy";
-        version = "0.7.26";
+        version = "0.7.35";
         edition = "2018";
-        sha256 = "184s51bzn8mpx3p9h6klrlppv9b7ja1b8y9998jr36jmj1a42zp9";
+        sha256 = "1w36q7b9il2flg0qskapgi9ymgg7p985vniqd09vi0mwib8lz6qv";
         authors = [
           "Joshua Liebow-Feeser <joshlf@google.com>"
         ];
@@ -11104,10 +11321,11 @@ rec {
       };
       "zerocopy-derive" = rec {
         crateName = "zerocopy-derive";
-        version = "0.7.26";
+        version = "0.7.35";
         edition = "2018";
-        sha256 = "17v8yshfa23z5az2xhclpwrlzih26nj7imwbra12i5b6y764hznx";
+        sha256 = "0gnf2ap2y92nwdalzz3x7142f2b83sni66l39vxp2ijd6j080kzs";
         procMacro = true;
+        libName = "zerocopy_derive";
         authors = [
           "Joshua Liebow-Feeser <joshlf@google.com>"
         ];
@@ -11182,6 +11400,7 @@ rec {
         version = "7.0.0";
         edition = "2018";
         sha256 = "0gpav2lcibrpmyslmjkcn3w0w64qif3jjljd2h8lr4p249s7qx23";
+        libName = "zstd_safe";
         authors = [
           "Alexandre Bury <alexandre.bury@gmail.com>"
         ];
@@ -11215,6 +11434,7 @@ rec {
         edition = "2018";
         links = "zstd";
         sha256 = "0mk6a2367swdi22zg03lcackpnvgq96d7120awd4i83lm2lfy5ly";
+        libName = "zstd_sys";
         authors = [
           "Alexandre Bury <alexandre.bury@gmail.com>"
         ];
@@ -11344,52 +11564,41 @@ rec {
                 testPostRun
               ]);
           in
-          pkgs.runCommand "run-tests-${testCrate.name}"
-            {
-              inherit testCrateFlags;
-              buildInputs = testInputs;
-            } ''
-            set -e
-
-            export RUST_BACKTRACE=1
-
-            # recreate a file hierarchy as when running tests with cargo
-
-            # the source for test data
-            # It's necessary to locate the source in $NIX_BUILD_TOP/source/
-            # instead of $NIX_BUILD_TOP/
-            # because we compiled those test binaries in the former and not the latter.
-            # So all paths will expect source tree to be there and not in the build top directly.
-            # For example: $NIX_BUILD_TOP := /build in general, if you ask yourself.
-            # NOTE: There could be edge cases if `crate.sourceRoot` does exist but
-            # it's very hard to reason about them.
-            # Open a bug if you run into this!
-            mkdir -p source/
-            cd source/
-
-            ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${crate.src}
-
-            # build outputs
-            testRoot=target/debug
-            mkdir -p $testRoot
-
-            # executables of the crate
-            # we copy to prevent std::env::current_exe() to resolve to a store location
-            for i in ${crate}/bin/*; do
-              cp "$i" "$testRoot"
-            done
-            chmod +w -R .
-
-            # test harness executables are suffixed with a hash, like cargo does
-            # this allows to prevent name collision with the main
-            # executables of the crate
-            hash=$(basename $out)
-            for file in ${drv}/tests/*; do
-              f=$testRoot/$(basename $file)-$hash
-              cp $file $f
-              ${testCommand}
-            done
-          '';
+          pkgs.stdenvNoCC.mkDerivation {
+            name = "run-tests-${testCrate.name}";
+
+            inherit (crate) src;
+
+            inherit testCrateFlags;
+
+            buildInputs = testInputs;
+
+            buildPhase = ''
+              set -e
+              export RUST_BACKTRACE=1
+
+              # build outputs
+              testRoot=target/debug
+              mkdir -p $testRoot
+
+              # executables of the crate
+              # we copy to prevent std::env::current_exe() to resolve to a store location
+              for i in ${crate}/bin/*; do
+                cp "$i" "$testRoot"
+              done
+              chmod +w -R .
+
+              # test harness executables are suffixed with a hash, like cargo does
+              # this allows to prevent name collision with the main
+              # executables of the crate
+              hash=$(basename $out)
+              for file in ${drv}/tests/*; do
+                f=$testRoot/$(basename $file)-$hash
+                cp $file $f
+                ${testCommand}
+              done
+            '';
+          };
       in
       pkgs.runCommand "${crate.name}-linked"
         {
diff --git a/tvix/tools/crunch-v2/Cargo.toml b/tvix/tools/crunch-v2/Cargo.toml
index d2b7126bd241..4421c8b9ab34 100644
--- a/tvix/tools/crunch-v2/Cargo.toml
+++ b/tvix/tools/crunch-v2/Cargo.toml
@@ -11,7 +11,7 @@ anyhow = { version = "1.0.75", features = ["backtrace"] }
 lazy_static = "1.4.0"
 
 bstr = "1.8.0"
-bytes = "1.5.0"
+bytes = "1.6.1"
 
 futures = "0.3.29"
 tokio = { version = "1.37.0", features = ["full"] }
diff --git a/tvix/tools/crunch-v2/default.nix b/tvix/tools/crunch-v2/default.nix
index a3eb25eb9ad6..8014aa9443c8 100644
--- a/tvix/tools/crunch-v2/default.nix
+++ b/tvix/tools/crunch-v2/default.nix
@@ -10,4 +10,6 @@
       nativeBuildInputs = [ pkgs.protobuf ];
     };
   };
-}).rootCrate.build
+}).rootCrate.build.overrideAttrs {
+  meta.ci.extraSteps.crate2nix-check = depot.tvix.utils.mkCrate2nixCheck ./Cargo.nix;
+}
diff --git a/tvix/tools/narinfo2parquet/Cargo.lock b/tvix/tools/narinfo2parquet/Cargo.lock
index 88809eb57cf2..151ce0b47f44 100644
--- a/tvix/tools/narinfo2parquet/Cargo.lock
+++ b/tvix/tools/narinfo2parquet/Cargo.lock
@@ -271,9 +271,9 @@ dependencies = [
 
 [[package]]
 name = "bytes"
-version = "1.5.0"
+version = "1.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
+checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
 
 [[package]]
 name = "cc"
@@ -398,16 +398,15 @@ dependencies = [
 
 [[package]]
 name = "curve25519-dalek"
-version = "4.1.1"
+version = "4.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c"
+checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
 dependencies = [
  "cfg-if",
  "cpufeatures",
  "curve25519-dalek-derive",
  "digest",
  "fiat-crypto",
- "platforms",
  "rustc_version",
  "subtle",
  "zeroize",
@@ -817,6 +816,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
 
 [[package]]
+name = "libmimalloc-sys"
+version = "0.1.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23aa6811d3bd4deb8a84dde645f943476d13b248d818edcf8ce0b2f37f036b44"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
 name = "linux-raw-sys"
 version = "0.4.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -873,6 +882,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "mimalloc"
+version = "0.1.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68914350ae34959d83f732418d51e2427a794055d0b9529f48259ac07af65633"
+dependencies = [
+ "libmimalloc-sys",
+]
+
+[[package]]
 name = "minimal-lexical"
 version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -938,11 +956,13 @@ version = "0.1.0"
 dependencies = [
  "bitflags 2.4.1",
  "bstr",
+ "bytes",
  "data-encoding",
  "ed25519",
  "ed25519-dalek",
  "enum-primitive-derive",
  "glob",
+ "mimalloc",
  "nom",
  "num-traits",
  "pin-project-lite",
@@ -951,6 +971,7 @@ dependencies = [
  "sha2",
  "thiserror",
  "tokio",
+ "tracing",
 ]
 
 [[package]]
@@ -1070,12 +1091,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "platforms"
-version = "3.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0"
-
-[[package]]
 name = "polars"
 version = "0.36.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1860,6 +1875,37 @@ dependencies = [
 ]
 
 [[package]]
+name = "tracing"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
 name = "typenum"
 version = "1.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/tvix/tools/narinfo2parquet/Cargo.nix b/tvix/tools/narinfo2parquet/Cargo.nix
index d00ceaf4f2e3..9804b92a5e76 100644
--- a/tvix/tools/narinfo2parquet/Cargo.nix
+++ b/tvix/tools/narinfo2parquet/Cargo.nix
@@ -207,6 +207,7 @@ rec {
         edition = "2015";
         crateBin = [ ];
         sha256 = "1cy6r2sfv5y5cigv86vms7n5nlwhx1rbyxwcraqnmm1rxiib2yyc";
+        libName = "alloc_no_stdlib";
         authors = [
           "Daniel Reiter Horn <danielrh@dropbox.com>"
         ];
@@ -218,6 +219,7 @@ rec {
         edition = "2015";
         crateBin = [ ];
         sha256 = "1kkfbld20ab4165p29v172h8g0wvq8i06z8vnng14whw0isq5ywl";
+        libName = "alloc_stdlib";
         authors = [
           "Daniel Reiter Horn <danielrh@dropbox.com>"
         ];
@@ -234,6 +236,7 @@ rec {
         version = "0.2.16";
         edition = "2018";
         sha256 = "1iayppgq4wqbfbfcqmsbwgamj0s65012sskfvyx07pxavk3gyhh9";
+        libName = "allocator_api2";
         authors = [
           "Zakarum <zaq.dev@icloud.com>"
         ];
@@ -249,6 +252,7 @@ rec {
         version = "0.1.1";
         edition = "2018";
         sha256 = "1w7ynjxrfs97xg3qlcdns4kgfpwcdv824g611fq32cag4cdr96g9";
+        libName = "android_tzdata";
         authors = [
           "RumovZ"
         ];
@@ -320,6 +324,7 @@ rec {
         version = "0.2.0";
         edition = "2021";
         sha256 = "0xpbqf7qkvzplpjd7f0wbcf2n1v9vygdccwxkd1amxp4il0hlzdz";
+        libName = "array_init_cursor";
 
       };
       "arrow-format" = rec {
@@ -327,6 +332,7 @@ rec {
         version = "0.8.1";
         edition = "2018";
         sha256 = "1irj67p6c224dzw86jr7j3z9r5zfid52gy6ml8rdqk4r2si4x207";
+        libName = "arrow_format";
         authors = [
           "Jorge C. Leitao <jorgecarleitao@gmail.com>"
         ];
@@ -362,6 +368,7 @@ rec {
         version = "0.3.5";
         edition = "2018";
         sha256 = "0l8sjq1rylkb1ak0pdyjn83b3k6x36j22myngl4sqqgg7whdsmnd";
+        libName = "async_stream";
         authors = [
           "Carl Lerche <me@carllerche.com>"
         ];
@@ -387,6 +394,7 @@ rec {
         edition = "2018";
         sha256 = "14q179j4y8p2z1d0ic6aqgy9fhwz8p9cai1ia8kpw4bw7q12mrhn";
         procMacro = true;
+        libName = "async_stream_impl";
         authors = [
           "Carl Lerche <me@carllerche.com>"
         ];
@@ -413,6 +421,7 @@ rec {
         edition = "2021";
         sha256 = "1ydhbsqjqqa6bxbv0kgys2wq2vi3jpwjy57dk162ajwppgqkfrd6";
         procMacro = true;
+        libName = "async_trait";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -605,6 +614,7 @@ rec {
         version = "0.10.4";
         edition = "2018";
         sha256 = "0w9sa2ypmrsqqvc20nhwr75wbb5cjr4kkyhpjm1z1lv2kdicfy1h";
+        libName = "block_buffer";
         authors = [
           "RustCrypto Developers"
         ];
@@ -660,6 +670,7 @@ rec {
         edition = "2015";
         crateBin = [ ];
         sha256 = "0kyyh9701dwqzwvn2frff4ww0zibikqd1s1xvl7n1pfpc3z4lbjf";
+        libName = "brotli_decompressor";
         authors = [
           "Daniel Reiter Horn <danielrh@dropbox.com>"
           "The Brotli Authors"
@@ -782,9 +793,9 @@ rec {
       };
       "bytes" = rec {
         crateName = "bytes";
-        version = "1.5.0";
+        version = "1.6.1";
         edition = "2018";
-        sha256 = "08w2i8ac912l8vlvkv3q51cd4gr09pwlg3sjsjffcizlrb0i5gd2";
+        sha256 = "0lnryqfiymbq5mfflfmbsqvfnw80kkh36nk5kpiscgxb9ac1cad1";
         authors = [
           "Carl Lerche <me@carllerche.com>"
           "Sean McArthur <sean@seanmonstar.com>"
@@ -828,6 +839,7 @@ rec {
         version = "1.0.0";
         edition = "2018";
         sha256 = "1za0vb97n4brpzpv8lsbnzmq5r8f2b0cpqqr0sy8h5bn751xxwds";
+        libName = "cfg_if";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -892,6 +904,7 @@ rec {
         version = "0.9.5";
         edition = "2021";
         sha256 = "0vxb4d25mgk8y0phay7j078limx2553716ixsr1x5605k31j5h98";
+        libName = "const_oid";
         authors = [
           "RustCrypto Developers"
         ];
@@ -904,6 +917,7 @@ rec {
         version = "0.8.4";
         edition = "2015";
         sha256 = "1yhf471qj6snnm2mcswai47vsbc9w30y4abmdp4crb4av87sb5p4";
+        libName = "core_foundation_sys";
         authors = [
           "The Servo Project Developers"
         ];
@@ -966,6 +980,7 @@ rec {
         version = "0.5.8";
         edition = "2018";
         sha256 = "004jz4wxp9k26z657i7rsh9s7586dklx2c5aqf1n3w1dgzvjng53";
+        libName = "crossbeam_channel";
         dependencies = [
           {
             name = "cfg-if";
@@ -990,6 +1005,7 @@ rec {
         version = "0.8.3";
         edition = "2018";
         sha256 = "1vqczbcild7nczh5z116w8w46z991kpjyw7qxkf24c14apwdcvyf";
+        libName = "crossbeam_deque";
         dependencies = [
           {
             name = "cfg-if";
@@ -1021,6 +1037,7 @@ rec {
         version = "0.9.15";
         edition = "2018";
         sha256 = "1ixwc3cq816wb8rlh3ix4jnybqbyyq4l61nwlx0mfm3ck0s148df";
+        libName = "crossbeam_epoch";
         dependencies = [
           {
             name = "cfg-if";
@@ -1061,6 +1078,7 @@ rec {
         version = "0.3.8";
         edition = "2018";
         sha256 = "1p9s6n4ckwdgxkb7a8ay9zjzmgc8ppfbxix2vr07rwskibmb7kyi";
+        libName = "crossbeam_queue";
         dependencies = [
           {
             name = "cfg-if";
@@ -1084,6 +1102,7 @@ rec {
         version = "0.8.16";
         edition = "2018";
         sha256 = "153j0gikblz7n7qdvdi8pslhi008s1yp9cmny6vw07ad7pbb48js";
+        libName = "crossbeam_utils";
         dependencies = [
           {
             name = "cfg-if";
@@ -1101,6 +1120,7 @@ rec {
         version = "0.1.6";
         edition = "2018";
         sha256 = "1cvby95a6xg7kxdz5ln3rl9xh66nz66w46mm3g56ri1z5x815yqv";
+        libName = "crypto_common";
         authors = [
           "RustCrypto Developers"
         ];
@@ -1123,9 +1143,10 @@ rec {
       };
       "curve25519-dalek" = rec {
         crateName = "curve25519-dalek";
-        version = "4.1.1";
+        version = "4.1.3";
         edition = "2021";
-        sha256 = "0p7ns5917k6369gajrsbfj24llc5zfm635yh3abla7sb5rm8r6z8";
+        sha256 = "1gmjb9dsknrr8lypmhkyjd67p1arb8mbfamlwxm7vph38my8pywp";
+        libName = "curve25519_dalek";
         authors = [
           "Isis Lovecruft <isis@patternsinthevoid.net>"
           "Henry de Valence <hdevalence@hdevalence.ca>"
@@ -1171,10 +1192,6 @@ rec {
         ];
         buildDependencies = [
           {
-            name = "platforms";
-            packageId = "platforms";
-          }
-          {
             name = "rustc_version";
             packageId = "rustc_version";
           }
@@ -1198,6 +1215,7 @@ rec {
         edition = "2021";
         sha256 = "1cry71xxrr0mcy5my3fb502cwfxy6822k4pm19cwrilrg7hq4s7l";
         procMacro = true;
+        libName = "curve25519_dalek_derive";
         dependencies = [
           {
             name = "proc-macro2";
@@ -1220,6 +1238,7 @@ rec {
         version = "2.6.0";
         edition = "2018";
         sha256 = "1qnn68n4vragxaxlkqcb1r28d3hhj43wch67lm4rpxlw89wnjmp8";
+        libName = "data_encoding";
         authors = [
           "Julien Cretin <git@ia0.eu>"
         ];
@@ -1303,6 +1322,7 @@ rec {
         version = "1.0.16";
         edition = "2018";
         sha256 = "0pa9kas6a241pbx0q82ipwi4f7m7wwyzkkc725caky24gl4j4nsl";
+        libName = "dyn_clone";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -1345,6 +1365,7 @@ rec {
         version = "2.1.0";
         edition = "2021";
         sha256 = "1h13qm789m9gdjl6jazss80hqi8ll37m0afwcnw23zcbqjp8wqhz";
+        libName = "ed25519_dalek";
         authors = [
           "isis lovecruft <isis@patternsinthevoid.net>"
           "Tony Arcieri <bascule@gmail.com>"
@@ -1437,6 +1458,7 @@ rec {
         edition = "2018";
         sha256 = "0k6wcf58h5kh64yq5nfq71va53kaya0kzxwsjwbgwm2n2zd9axxs";
         procMacro = true;
+        libName = "enum_primitive_derive";
         authors = [
           "Doug Goldstein <cardoe@cardoe.com>"
         ];
@@ -1553,6 +1575,7 @@ rec {
         version = "0.1.9";
         edition = "2015";
         sha256 = "0nj6j26p71bjy8h42x6jahx1hn0ng6mc2miwpgwnp8vnwqf4jq3k";
+        libName = "fallible_streaming_iterator";
         authors = [
           "Steven Fackler <sfackler@gmail.com>"
         ];
@@ -1563,6 +1586,7 @@ rec {
         version = "0.2.0";
         edition = "2018";
         sha256 = "0g7kfll3xyh99kc7r352lhljnwvgayxxa6saifb6725inikmyxlm";
+        libName = "fast_float";
         authors = [
           "Ivan Smirnov <i.s.smirnov@gmail.com>"
         ];
@@ -1592,6 +1616,7 @@ rec {
         version = "0.2.5";
         edition = "2018";
         sha256 = "1dxn0g50pv0ppal779vi7k40fr55pbhkyv4in7i13pgl4sn3wmr7";
+        libName = "fiat_crypto";
         authors = [
           "Fiat Crypto library authors <jgross@mit.edu>"
         ];
@@ -1721,6 +1746,7 @@ rec {
         version = "0.3.29";
         edition = "2018";
         sha256 = "1jxsifvrbqzdadk0svbax71cba5d3qg3wgjq8i160mxmd1kdckgz";
+        libName = "futures_channel";
         dependencies = [
           {
             name = "futures-core";
@@ -1748,6 +1774,7 @@ rec {
         version = "0.3.29";
         edition = "2018";
         sha256 = "1308bpj0g36nhx2y6bl4mm6f1gnh9xyvvw2q2wpdgnb6dv3247gb";
+        libName = "futures_core";
         features = {
           "default" = [ "std" ];
           "portable-atomic" = [ "dep:portable-atomic" ];
@@ -1760,6 +1787,7 @@ rec {
         version = "0.3.29";
         edition = "2018";
         sha256 = "1g4pjni0sw28djx6mlcfz584abm2lpifz86cmng0kkxh7mlvhkqg";
+        libName = "futures_executor";
         dependencies = [
           {
             name = "futures-core";
@@ -1790,6 +1818,7 @@ rec {
         version = "0.3.29";
         edition = "2018";
         sha256 = "1ajsljgny3zfxwahba9byjzclrgvm1ypakca8z854k2w7cb4mwwb";
+        libName = "futures_io";
         features = {
           "default" = [ "std" ];
         };
@@ -1801,6 +1830,7 @@ rec {
         edition = "2018";
         sha256 = "1nwd18i8kvpkdfwm045hddjli0n96zi7pn6f99zi9c74j7ym7cak";
         procMacro = true;
+        libName = "futures_macro";
         dependencies = [
           {
             name = "proc-macro2";
@@ -1823,6 +1853,7 @@ rec {
         version = "0.3.29";
         edition = "2018";
         sha256 = "05q8jykqddxzp8nwf00wjk5m5mqi546d7i8hsxma7hiqxrw36vg3";
+        libName = "futures_sink";
         features = {
           "default" = [ "std" ];
           "std" = [ "alloc" ];
@@ -1834,6 +1865,7 @@ rec {
         version = "0.3.29";
         edition = "2018";
         sha256 = "1qmsss8rb5ppql4qvd4r70h9gpfcpd0bg2b3qilxrnhdkc397lgg";
+        libName = "futures_task";
         features = {
           "default" = [ "std" ];
           "std" = [ "alloc" ];
@@ -1845,6 +1877,7 @@ rec {
         version = "0.3.29";
         edition = "2018";
         sha256 = "0141rkqh0psj4h8x8lgsl1p29dhqr7z2wcixkcbs60z74kb2d5d1";
+        libName = "futures_util";
         dependencies = [
           {
             name = "futures-channel";
@@ -2098,6 +2131,7 @@ rec {
         version = "0.3.3";
         edition = "2021";
         sha256 = "1dyc8qsjh876n74a3rcz8h43s27nj1sypdhsn2ms61bd3b47wzyp";
+        libName = "hermit_abi";
         authors = [
           "Stefan Lankes"
         ];
@@ -2132,6 +2166,7 @@ rec {
         version = "0.1.58";
         edition = "2018";
         sha256 = "081vcr8z8ddhl5r1ywif6grnswk01b2ac4nks2bhn8zzdimvh9l3";
+        libName = "iana_time_zone";
         authors = [
           "Andrew Straw <strawman@astraw.com>"
           "René Kijewski <rene.kijewski@fu-berlin.de>"
@@ -2177,6 +2212,7 @@ rec {
         version = "0.1.2";
         edition = "2018";
         sha256 = "17r6jmj31chn7xs9698r122mapq85mfnv98bb4pg6spm0si2f67k";
+        libName = "iana_time_zone_haiku";
         authors = [
           "René Kijewski <crates.io@k6i.de>"
         ];
@@ -2234,6 +2270,7 @@ rec {
         edition = "2018";
         links = "jemalloc";
         sha256 = "1wpbpwhfs6wd484cdfpl0zdf441ann9wj0fypy67i8ffw531jv5c";
+        libName = "jemalloc_sys";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
           "Gonzalo Brito Gadeschi <gonzalobg88@gmail.com>"
@@ -2316,6 +2353,7 @@ rec {
         version = "0.3.65";
         edition = "2018";
         sha256 = "1s1gaxgzpqfyygc7f2pwp9y128rh5f8zvsc4nm5yazgna9cw7h2l";
+        libName = "js_sys";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -2357,11 +2395,39 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" ];
       };
+      "libmimalloc-sys" = rec {
+        crateName = "libmimalloc-sys";
+        version = "0.1.39";
+        edition = "2018";
+        links = "mimalloc";
+        sha256 = "0i3b0dzz7cp0ik7ys66q92r16va78gwlbrnxhj5fnkdxsc8niai3";
+        libName = "libmimalloc_sys";
+        authors = [
+          "Octavian Oncescu <octavonce@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+        features = {
+          "cty" = [ "dep:cty" ];
+          "extended" = [ "cty" ];
+        };
+      };
       "linux-raw-sys" = rec {
         crateName = "linux-raw-sys";
         version = "0.4.10";
         edition = "2021";
         sha256 = "0gz0671d4hgrdngrryaajxl962ny4g40pykg0vq0pr32q3l7j96s";
+        libName = "linux_raw_sys";
         authors = [
           "Dan Gohman <dev@sunfishcode.online>"
         ];
@@ -2421,6 +2487,7 @@ rec {
         edition = "2015";
         links = "lz4";
         sha256 = "0059ik4xlvnss5qfh6l691psk4g3350ljxaykzv10yr0gqqppljp";
+        libName = "lz4_sys";
         authors = [
           "Jens Heyens <jens.heyens@ewetel.net>"
           "Artem V. Navrotskiy <bozaro@buzzsoft.ru>"
@@ -2497,11 +2564,40 @@ rec {
         features = { };
         resolvedDefaultFeatures = [ "default" ];
       };
+      "mimalloc" = rec {
+        crateName = "mimalloc";
+        version = "0.1.43";
+        edition = "2018";
+        sha256 = "0csnyrxc16i592gm5ffham07jyj2w98qsh9jyy1rv59lmr8474b8";
+        authors = [
+          "Octavian Oncescu <octavonce@gmail.com>"
+          "Vincent Rouillé <vincent@speedy37.fr>"
+          "Thom Chiovoloni <chiovolonit@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libmimalloc-sys";
+            packageId = "libmimalloc-sys";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "debug" = [ "libmimalloc-sys/debug" ];
+          "debug_in_debug" = [ "libmimalloc-sys/debug_in_debug" ];
+          "extended" = [ "libmimalloc-sys/extended" ];
+          "local_dynamic_tls" = [ "libmimalloc-sys/local_dynamic_tls" ];
+          "no_thp" = [ "libmimalloc-sys/no_thp" ];
+          "override" = [ "libmimalloc-sys/override" ];
+          "secure" = [ "libmimalloc-sys/secure" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
       "minimal-lexical" = rec {
         crateName = "minimal-lexical";
         version = "0.2.1";
         edition = "2018";
         sha256 = "16ppc5g84aijpri4jzv14rvcnslvlpphbszc7zzp6vfkddf4qdb8";
+        libName = "minimal_lexical";
         authors = [
           "Alex Huszagh <ahuszagh@gmail.com>"
         ];
@@ -2608,6 +2704,7 @@ rec {
         edition = "2021";
         sha256 = "1j1avbxw7jscyi7dmnywhlwbiny1fvg1vpp9fy4dc1pd022kva16";
         procMacro = true;
+        libName = "multiversion_macros";
         authors = [
           "Caleb Zulawski <caleb.zulawski@gmail.com>"
         ];
@@ -2684,6 +2781,7 @@ rec {
         edition = "2021";
         crateBin = [ ];
         src = lib.cleanSourceWith { filter = sourceFilter; src = ../../nix-compat; };
+        libName = "nix_compat";
         dependencies = [
           {
             name = "bitflags";
@@ -2695,6 +2793,11 @@ rec {
             features = [ "alloc" "unicode" "serde" ];
           }
           {
+            name = "bytes";
+            packageId = "bytes";
+            optional = true;
+          }
+          {
             name = "data-encoding";
             packageId = "data-encoding";
           }
@@ -2715,6 +2818,10 @@ rec {
             packageId = "glob";
           }
           {
+            name = "mimalloc";
+            packageId = "mimalloc";
+          }
+          {
             name = "nom";
             packageId = "nom";
           }
@@ -2750,21 +2857,30 @@ rec {
             optional = true;
             features = [ "io-util" "macros" ];
           }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
         ];
         devDependencies = [
           {
+            name = "mimalloc";
+            packageId = "mimalloc";
+          }
+          {
             name = "serde_json";
             packageId = "serde_json";
           }
         ];
         features = {
           "async" = [ "tokio" ];
+          "bytes" = [ "dep:bytes" ];
           "default" = [ "async" "wire" ];
           "pin-project-lite" = [ "dep:pin-project-lite" ];
           "tokio" = [ "dep:tokio" ];
-          "wire" = [ "tokio" "pin-project-lite" ];
+          "wire" = [ "tokio" "pin-project-lite" "bytes" ];
         };
-        resolvedDefaultFeatures = [ "async" "default" "pin-project-lite" "tokio" "wire" ];
+        resolvedDefaultFeatures = [ "async" "bytes" "default" "pin-project-lite" "tokio" "wire" ];
       };
       "nom" = rec {
         crateName = "nom";
@@ -2836,6 +2952,7 @@ rec {
         version = "0.2.19";
         edition = "2021";
         sha256 = "0h984rhdkkqd4ny9cif7y2azl3xdfb7768hb9irhpsch4q3gq787";
+        libName = "num_traits";
         authors = [
           "The Rust Project Developers"
         ];
@@ -2935,6 +3052,7 @@ rec {
         version = "0.2.4";
         edition = "2021";
         sha256 = "07wf6wf4jrxlq5p3xldxsnabp7jl06my2qp7kiwy9m3x2r5wac8i";
+        libName = "parquet_format_safe";
         authors = [
           "Apache Thrift contributors <dev@thrift.apache.org>"
           "Jorge Leitao <jorgecarleitao@gmail.com>"
@@ -2964,6 +3082,7 @@ rec {
         version = "2.3.0";
         edition = "2018";
         sha256 = "152slflmparkh27hprw62sph8rv77wckzhwl2dhqk6bf563lfalv";
+        libName = "percent_encoding";
         authors = [
           "The rust-url developers"
         ];
@@ -2978,6 +3097,7 @@ rec {
         version = "0.2.13";
         edition = "2018";
         sha256 = "0n0bwr5qxlf0mhn2xkl36sy55118s9qmvx2yl5f3ixkb007lbywa";
+        libName = "pin_project_lite";
 
       };
       "pin-utils" = rec {
@@ -2985,6 +3105,7 @@ rec {
         version = "0.1.0";
         edition = "2018";
         sha256 = "117ir7vslsl2z1a7qzhws4pd01cg2d3338c47swjyvqv2n60v1wb";
+        libName = "pin_utils";
         authors = [
           "Josef Brandl <mail@josefbrandl.de>"
         ];
@@ -3029,6 +3150,7 @@ rec {
         version = "0.3.27";
         edition = "2015";
         sha256 = "0r39ryh1magcq4cz5g9x88jllsnxnhcqr753islvyk4jp9h2h1r6";
+        libName = "pkg_config";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -3050,21 +3172,6 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" "std" ];
       };
-      "platforms" = rec {
-        crateName = "platforms";
-        version = "3.2.0";
-        edition = "2018";
-        sha256 = "1c6bzwn877aqdbbmyqsl753ycbciwvbdh4lpzijb8vrfb4zsprhl";
-        authors = [
-          "Tony Arcieri <bascule@gmail.com>"
-          "Sergey \"Shnatsel\" Davidoff <shnatsel@gmail.com>"
-        ];
-        features = {
-          "default" = [ "std" ];
-          "serde" = [ "dep:serde" ];
-        };
-        resolvedDefaultFeatures = [ "default" "std" ];
-      };
       "polars" = rec {
         crateName = "polars";
         version = "0.36.2";
@@ -3265,6 +3372,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "0vxql6amvwyp6qj0w87vm21y33qa0i61pshs4ry7ixwgd4ps0s6f";
+        libName = "polars_arrow";
         authors = [
           "Jorge C. Leitao <jorgecarleitao@gmail.com>"
           "Apache Arrow <dev@arrow.apache.org>"
@@ -3443,6 +3551,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "1pa4l1w41gdzdff81ih1z05z3gz568k81i6flibbca8v2igvqkxi";
+        libName = "polars_compute";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -3482,6 +3591,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "08sar9h97znpb8ziyvrrvvx28lyzc21vwsd7gvwybjxn6kkyzxfh";
+        libName = "polars_core";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -3636,6 +3746,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "11m80a899kp45b3dz9jbvrn5001hw8izbdp7d2czrswrixwdx5k3";
+        libName = "polars_error";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -3672,6 +3783,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "1smamd34ghlxyxdd4aqdplk7k8xm1l626brmzlc4fvwlx3pmh13x";
+        libName = "polars_io";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -3830,6 +3942,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "1mdml4hs574njb23mb9gl6x92x2bb4vdfzsazkl3ifq516s0awcx";
+        libName = "polars_lazy";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -4024,6 +4137,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "13m7dh4vpdmcah04a7kql933l7y7045f0hybbmgff4dbav2ay29f";
+        libName = "polars_ops";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -4175,6 +4289,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "0gay037sw5hcg2q93pxcfh7krcchl1px8g838d8vhjpnn5klv8kv";
+        libName = "polars_parquet";
         authors = [
           "Jorge C. Leitao <jorgecarleitao@gmail.com>"
           "Apache Arrow <dev@arrow.apache.org>"
@@ -4295,6 +4410,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "00217q51mnq7i57k3sax293xnwghm5hridbpgl11fffcfg8fmdyr";
+        libName = "polars_pipe";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -4408,6 +4524,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "1l5ca38v7ksq4595wdr6wsd74pdgszwivq9y8wfc6l6h4ib1fjiq";
+        libName = "polars_plan";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -4596,6 +4713,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "0by7x6jlj5dwr3f1gj49v8w65l3kpqhwhzb9qzlv6gdqrdx2ycij";
+        libName = "polars_row";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -4625,6 +4743,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "1dcmm993gycw75a6c5hxcr6h2cy6fa2r3hsbx19h9zgxvxnlq2wz";
+        libName = "polars_sql";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -4689,6 +4808,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "1l24fmpv5v1qshxfd4592z8x6bnjlwy4njhf9rcbdlbbr6gn9qny";
+        libName = "polars_time";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -4767,6 +4887,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "1nmvfqwyzbaxcw457amspz479y5vcnpalq043awxfixdfx5clx5i";
+        libName = "polars_utils";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -4834,6 +4955,7 @@ rec {
         version = "0.2.17";
         edition = "2018";
         sha256 = "1pp6g52aw970adv3x2310n7glqnji96z0a9wiamzw89ibf0ayh2v";
+        libName = "ppv_lite86";
         authors = [
           "The CryptoCorrosion Contributors"
         ];
@@ -4847,6 +4969,7 @@ rec {
         version = "1.0.69";
         edition = "2021";
         sha256 = "1nljgyllbm3yr3pa081bf83gxh6l4zvjqzaldw7v4mj9xfgihk0k";
+        libName = "proc_macro2";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
           "Alex Crichton <alex@alexcrichton.com>"
@@ -5048,6 +5171,7 @@ rec {
         edition = "2021";
         links = "rayon-core";
         sha256 = "1vaq0q71yfvcwlmia0iqf6ixj2fibjcf2xjy92n1m1izv1mgpqsw";
+        libName = "rayon_core";
         authors = [
           "Niko Matsakis <niko@alum.mit.edu>"
           "Josh Stone <cuviper@gmail.com>"
@@ -5145,6 +5269,7 @@ rec {
         version = "0.4.3";
         edition = "2021";
         sha256 = "0gs8q9yhd3kcg4pr00ag4viqxnh5l7jpyb9fsfr8hzh451w4r02z";
+        libName = "regex_automata";
         authors = [
           "The Rust Project Developers"
           "Andrew Gallant <jamslam@gmail.com>"
@@ -5205,6 +5330,7 @@ rec {
         version = "0.8.2";
         edition = "2021";
         sha256 = "17rd2s8xbiyf6lb4aj2nfi44zqlj98g2ays8zzj2vfs743k79360";
+        libName = "regex_syntax";
         authors = [
           "The Rust Project Developers"
           "Andrew Gallant <jamslam@gmail.com>"
@@ -5221,6 +5347,7 @@ rec {
         version = "0.1.23";
         edition = "2015";
         sha256 = "0xnbk2bmyzshacjm2g1kd4zzv2y2az14bw3sjccq5qkpmsfvn9nn";
+        libName = "rustc_demangle";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -5412,6 +5539,7 @@ rec {
         edition = "2018";
         sha256 = "1d50kbaslrrd0374ivx15jg57f03y5xzil1wd2ajlvajzlkbzw53";
         procMacro = true;
+        libName = "seq_macro";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -5755,6 +5883,7 @@ rec {
         version = "0.1.2";
         edition = "2018";
         sha256 = "1wscqj3s30qknda778wf7z99mknk65p0h9hhs658l4pvkfqw6v5z";
+        libName = "streaming_decompression";
         dependencies = [
           {
             name = "fallible-streaming-iterator";
@@ -5768,6 +5897,7 @@ rec {
         version = "0.1.9";
         edition = "2021";
         sha256 = "0845zdv8qb7zwqzglpqc0830i43xh3fb6vqms155wz85qfvk28ib";
+        libName = "streaming_iterator";
         authors = [
           "Steven Fackler <sfackler@gmail.com>"
         ];
@@ -5954,6 +6084,7 @@ rec {
         version = "0.1.5";
         edition = "2021";
         sha256 = "1gb974chm9aj8ifkyibylxkyb5an4bf5y8dxb18pqmck698gmdfg";
+        libName = "target_features";
         authors = [
           "Caleb Zulawski <caleb.zulawski@gmail.com>"
         ];
@@ -6004,6 +6135,7 @@ rec {
         version = "0.3.4";
         edition = "2018";
         sha256 = "1xksx1l1019k9q0az9mhqsgb14w0vm88yax30iq6178s3d9yhjx7";
+        libName = "tempfile_fast";
         authors = [
           "Chris West (Faux) <git@goeswhere.com>"
         ];
@@ -6046,6 +6178,7 @@ rec {
         edition = "2021";
         sha256 = "1f0lmam4765sfnwr4b1n00y14vxh10g0311mkk0adr80pi02wsr6";
         procMacro = true;
+        libName = "thiserror_impl";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -6170,6 +6303,7 @@ rec {
         edition = "2021";
         sha256 = "0fwjy4vdx1h9pi4g2nml72wi0fr27b5m954p13ji9anyy8l1x2jv";
         procMacro = true;
+        libName = "tokio_macros";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -6195,6 +6329,7 @@ rec {
         version = "0.7.10";
         edition = "2021";
         sha256 = "058y6x4mf0fsqji9rfyb77qbfyc50y4pk2spqgj6xsyr693z66al";
+        libName = "tokio_util";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -6245,6 +6380,96 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" "io" "io-util" ];
       };
+      "tracing" = rec {
+        crateName = "tracing";
+        version = "0.1.40";
+        edition = "2018";
+        sha256 = "1vv48dac9zgj9650pg2b4d0j3w6f3x9gbggf43scq5hrlysklln3";
+        authors = [
+          "Eliza Weisman <eliza@buoyant.io>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tracing-attributes";
+            packageId = "tracing-attributes";
+            optional = true;
+          }
+          {
+            name = "tracing-core";
+            packageId = "tracing-core";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "attributes" = [ "tracing-attributes" ];
+          "default" = [ "std" "attributes" ];
+          "log" = [ "dep:log" ];
+          "log-always" = [ "log" ];
+          "std" = [ "tracing-core/std" ];
+          "tracing-attributes" = [ "dep:tracing-attributes" ];
+          "valuable" = [ "tracing-core/valuable" ];
+        };
+        resolvedDefaultFeatures = [ "attributes" "default" "std" "tracing-attributes" ];
+      };
+      "tracing-attributes" = rec {
+        crateName = "tracing-attributes";
+        version = "0.1.27";
+        edition = "2018";
+        sha256 = "1rvb5dn9z6d0xdj14r403z0af0bbaqhg02hq4jc97g5wds6lqw1l";
+        procMacro = true;
+        libName = "tracing_attributes";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+          "Eliza Weisman <eliza@buoyant.io>"
+          "David Barsky <dbarsky@amazon.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.39";
+            usesDefaultFeatures = false;
+            features = [ "full" "parsing" "printing" "visit-mut" "clone-impls" "extra-traits" "proc-macro" ];
+          }
+        ];
+        features = { };
+      };
+      "tracing-core" = rec {
+        crateName = "tracing-core";
+        version = "0.1.32";
+        edition = "2018";
+        sha256 = "0m5aglin3cdwxpvbg6kz0r9r0k31j48n0kcfwsp6l49z26k3svf0";
+        libName = "tracing_core";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            optional = true;
+          }
+        ];
+        features = {
+          "default" = [ "std" "valuable/std" ];
+          "once_cell" = [ "dep:once_cell" ];
+          "std" = [ "once_cell" ];
+          "valuable" = [ "dep:valuable" ];
+        };
+        resolvedDefaultFeatures = [ "once_cell" "std" ];
+      };
       "typenum" = rec {
         crateName = "typenum";
         version = "1.17.0";
@@ -6265,6 +6490,7 @@ rec {
         version = "1.0.12";
         edition = "2018";
         sha256 = "0jzf1znfpb2gx8nr8mvmyqs1crnv79l57nxnbiszc7xf7ynbjm1k";
+        libName = "unicode_ident";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -6302,6 +6528,7 @@ rec {
         version = "0.2.88";
         edition = "2018";
         sha256 = "1khgsh4z9bga35mjhg41dl7523i69ffc5m8ckhqaw6ssyabc5bkx";
+        libName = "wasm_bindgen";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -6333,6 +6560,7 @@ rec {
         version = "0.2.88";
         edition = "2018";
         sha256 = "05zj8yl243rvs87rhicq2l1d6443lnm6k90khf744khf9ikg95z3";
+        libName = "wasm_bindgen_backend";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -6378,6 +6606,7 @@ rec {
         edition = "2018";
         sha256 = "1chn3wgw9awmvs0fpmazbqyc5rwfgy3pj7lzwczmzb887dxh2qar";
         procMacro = true;
+        libName = "wasm_bindgen_macro";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -6402,6 +6631,7 @@ rec {
         version = "0.2.88";
         edition = "2018";
         sha256 = "01rrzg3y1apqygsjz1jg0n7p831nm4kdyxmxyl85x7v6mf6kndf5";
+        libName = "wasm_bindgen_macro_support";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -6440,6 +6670,7 @@ rec {
         edition = "2018";
         links = "wasm_bindgen";
         sha256 = "02vmw2rzsla1qm0zgfng4kqz52xn8k54v8ads4g1macv09fnq10d";
+        libName = "wasm_bindgen_shared";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -6475,6 +6706,7 @@ rec {
         version = "0.4.0";
         edition = "2015";
         sha256 = "1dmpa6mvcvzz16zg6d5vrfy4bxgg541wxrcip7cnshi06v38ffxc";
+        libName = "winapi_i686_pc_windows_gnu";
         authors = [
           "Peter Atashian <retep998@gmail.com>"
         ];
@@ -6485,6 +6717,7 @@ rec {
         version = "0.4.0";
         edition = "2015";
         sha256 = "0gqq64czqb64kskjryj8isp62m2sgvx25yyj3kpc2myh85w24bki";
+        libName = "winapi_x86_64_pc_windows_gnu";
         authors = [
           "Peter Atashian <retep998@gmail.com>"
         ];
@@ -7176,6 +7409,7 @@ rec {
         version = "0.51.1";
         edition = "2021";
         sha256 = "0r1f57hsshsghjyc7ypp2s0i78f7b1vr93w68sdb8baxyf2czy7i";
+        libName = "windows_core";
         authors = [
           "Microsoft"
         ];
@@ -7193,6 +7427,7 @@ rec {
         version = "0.52.0";
         edition = "2021";
         sha256 = "1nc3qv7sy24x0nlnb32f7alzpd6f72l4p24vl65vydbyil669ark";
+        libName = "windows_core";
         authors = [
           "Microsoft"
         ];
@@ -7210,6 +7445,7 @@ rec {
         version = "0.48.0";
         edition = "2018";
         sha256 = "1aan23v5gs7gya1lc46hqn9mdh8yph3fhxmhxlw36pn6pqc28zb7";
+        libName = "windows_sys";
         authors = [
           "Microsoft"
         ];
@@ -7503,6 +7739,7 @@ rec {
         version = "0.48.5";
         edition = "2018";
         sha256 = "034ljxqshifs1lan89xwpcy1hp0lhdh4b5n0d2z4fwjx2piacbws";
+        libName = "windows_targets";
         authors = [
           "Microsoft"
         ];
@@ -7550,6 +7787,7 @@ rec {
         version = "0.52.0";
         edition = "2021";
         sha256 = "1kg7a27ynzw8zz3krdgy6w5gbqcji27j1sz4p7xk2j5j8082064a";
+        libName = "windows_targets";
         authors = [
           "Microsoft"
         ];
@@ -7737,6 +7975,7 @@ rec {
         version = "0.8.7";
         edition = "2018";
         sha256 = "0yz037yrkn0qa0g0r6733ynd1xbw7zvx58v6qylhyi2kv9wb2a4q";
+        libName = "xxhash_rust";
         authors = [
           "Douman <douman@gmx.se>"
         ];
@@ -7785,6 +8024,7 @@ rec {
         edition = "2018";
         sha256 = "0fqvglw01w3hp7xj9gdk1800x9j7v58s9w8ijiyiz2a7krb39s8m";
         procMacro = true;
+        libName = "zerocopy_derive";
         authors = [
           "Joshua Liebow-Feeser <joshlf@google.com>"
         ];
@@ -7859,6 +8099,7 @@ rec {
         version = "7.0.0";
         edition = "2018";
         sha256 = "0gpav2lcibrpmyslmjkcn3w0w64qif3jjljd2h8lr4p249s7qx23";
+        libName = "zstd_safe";
         authors = [
           "Alexandre Bury <alexandre.bury@gmail.com>"
         ];
@@ -7892,6 +8133,7 @@ rec {
         edition = "2018";
         links = "zstd";
         sha256 = "0mk6a2367swdi22zg03lcackpnvgq96d7120awd4i83lm2lfy5ly";
+        libName = "zstd_sys";
         authors = [
           "Alexandre Bury <alexandre.bury@gmail.com>"
         ];
@@ -8021,52 +8263,41 @@ rec {
                 testPostRun
               ]);
           in
-          pkgs.runCommand "run-tests-${testCrate.name}"
-            {
-              inherit testCrateFlags;
-              buildInputs = testInputs;
-            } ''
-            set -e
+          pkgs.stdenvNoCC.mkDerivation {
+            name = "run-tests-${testCrate.name}";
 
-            export RUST_BACKTRACE=1
+            inherit (crate) src;
 
-            # recreate a file hierarchy as when running tests with cargo
+            inherit testCrateFlags;
 
-            # the source for test data
-            # It's necessary to locate the source in $NIX_BUILD_TOP/source/
-            # instead of $NIX_BUILD_TOP/
-            # because we compiled those test binaries in the former and not the latter.
-            # So all paths will expect source tree to be there and not in the build top directly.
-            # For example: $NIX_BUILD_TOP := /build in general, if you ask yourself.
-            # NOTE: There could be edge cases if `crate.sourceRoot` does exist but
-            # it's very hard to reason about them.
-            # Open a bug if you run into this!
-            mkdir -p source/
-            cd source/
+            buildInputs = testInputs;
 
-            ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${crate.src}
+            buildPhase = ''
+              set -e
+              export RUST_BACKTRACE=1
 
-            # build outputs
-            testRoot=target/debug
-            mkdir -p $testRoot
+              # build outputs
+              testRoot=target/debug
+              mkdir -p $testRoot
 
-            # executables of the crate
-            # we copy to prevent std::env::current_exe() to resolve to a store location
-            for i in ${crate}/bin/*; do
-              cp "$i" "$testRoot"
-            done
-            chmod +w -R .
+              # executables of the crate
+              # we copy to prevent std::env::current_exe() to resolve to a store location
+              for i in ${crate}/bin/*; do
+                cp "$i" "$testRoot"
+              done
+              chmod +w -R .
 
-            # test harness executables are suffixed with a hash, like cargo does
-            # this allows to prevent name collision with the main
-            # executables of the crate
-            hash=$(basename $out)
-            for file in ${drv}/tests/*; do
-              f=$testRoot/$(basename $file)-$hash
-              cp $file $f
-              ${testCommand}
-            done
-          '';
+              # test harness executables are suffixed with a hash, like cargo does
+              # this allows to prevent name collision with the main
+              # executables of the crate
+              hash=$(basename $out)
+              for file in ${drv}/tests/*; do
+                f=$testRoot/$(basename $file)-$hash
+                cp $file $f
+                ${testCommand}
+              done
+            '';
+          };
       in
       pkgs.runCommand "${crate.name}-linked"
         {
diff --git a/tvix/tools/narinfo2parquet/default.nix b/tvix/tools/narinfo2parquet/default.nix
index 0b486f08849a..dfe2b66655c8 100644
--- a/tvix/tools/narinfo2parquet/default.nix
+++ b/tvix/tools/narinfo2parquet/default.nix
@@ -6,4 +6,6 @@
       src = depot.tvix.utils.filterRustCrateSrc { root = prev.src.origSrc; };
     };
   };
-}).rootCrate.build
+}).rootCrate.build.overrideAttrs {
+  meta.ci.extraSteps.crate2nix = depot.tvix.utils.mkCrate2nixCheck ./Cargo.nix;
+}
diff --git a/tvix/tools/narinfo2parquet/src/main.rs b/tvix/tools/narinfo2parquet/src/main.rs
index b96b134f103a..ea3d39af5503 100644
--- a/tvix/tools/narinfo2parquet/src/main.rs
+++ b/tvix/tools/narinfo2parquet/src/main.rs
@@ -187,7 +187,7 @@ impl FrameBuilder {
         assert!(entry.signatures.len() <= 1);
         self.signature
             .append_option(entry.signatures.get(0).map(|sig| {
-                assert_eq!(sig.name(), "cache.nixos.org-1");
+                assert_eq!(sig.name(), &"cache.nixos.org-1");
                 sig.bytes()
             }));
 
diff --git a/tvix/tools/turbofetch/Cargo.lock b/tvix/tools/turbofetch/Cargo.lock
index b2aaf1cccb67..c865efb47466 100644
--- a/tvix/tools/turbofetch/Cargo.lock
+++ b/tvix/tools/turbofetch/Cargo.lock
@@ -265,6 +265,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
 name = "fnv"
 version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -388,9 +394,9 @@ checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
 
 [[package]]
 name = "h2"
-version = "0.3.21"
+version = "0.3.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833"
+checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
 dependencies = [
  "bytes",
  "fnv",
@@ -407,9 +413,9 @@ dependencies = [
 
 [[package]]
 name = "hashbrown"
-version = "0.12.3"
+version = "0.14.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
 
 [[package]]
 name = "hermit-abi"
@@ -541,11 +547,11 @@ dependencies = [
 
 [[package]]
 name = "indexmap"
-version = "1.9.3"
+version = "2.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
 dependencies = [
- "autocfg",
+ "equivalent",
  "hashbrown",
 ]
 
@@ -697,9 +703,9 @@ dependencies = [
 
 [[package]]
 name = "mio"
-version = "0.8.9"
+version = "0.8.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
+checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
 dependencies = [
  "libc",
  "wasi",
@@ -1151,9 +1157,9 @@ dependencies = [
 
 [[package]]
 name = "shlex"
-version = "1.2.0"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
 
 [[package]]
 name = "signal-hook-registry"
diff --git a/tvix/tools/turbofetch/Cargo.nix b/tvix/tools/turbofetch/Cargo.nix
index 72fcac307de6..09c9493430d5 100644
--- a/tvix/tools/turbofetch/Cargo.nix
+++ b/tvix/tools/turbofetch/Cargo.nix
@@ -132,6 +132,7 @@ rec {
         version = "0.1.1";
         edition = "2018";
         sha256 = "1w7ynjxrfs97xg3qlcdns4kgfpwcdv824g611fq32cag4cdr96g9";
+        libName = "android_tzdata";
         authors = [
           "RumovZ"
         ];
@@ -158,6 +159,7 @@ rec {
         version = "0.3.5";
         edition = "2018";
         sha256 = "0l8sjq1rylkb1ak0pdyjn83b3k6x36j22myngl4sqqgg7whdsmnd";
+        libName = "async_stream";
         authors = [
           "Carl Lerche <me@carllerche.com>"
         ];
@@ -183,6 +185,7 @@ rec {
         edition = "2018";
         sha256 = "14q179j4y8p2z1d0ic6aqgy9fhwz8p9cai1ia8kpw4bw7q12mrhn";
         procMacro = true;
+        libName = "async_stream_impl";
         authors = [
           "Carl Lerche <me@carllerche.com>"
         ];
@@ -209,6 +212,7 @@ rec {
         edition = "2021";
         sha256 = "1ydhbsqjqqa6bxbv0kgys2wq2vi3jpwjy57dk162ajwppgqkfrd6";
         procMacro = true;
+        libName = "async_trait";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -463,6 +467,7 @@ rec {
         version = "0.9.0";
         edition = "2018";
         sha256 = "1r4pf90s7d7lj1wdjhlnqa26vvbm6pnc33z138lxpnp9srpi2lj1";
+        libName = "block_buffer";
         authors = [
           "RustCrypto Developers"
         ];
@@ -546,6 +551,7 @@ rec {
         version = "1.0.0";
         edition = "2018";
         sha256 = "1za0vb97n4brpzpv8lsbnzmq5r8f2b0cpqqr0sy8h5bn751xxwds";
+        libName = "cfg_if";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -616,6 +622,7 @@ rec {
         version = "0.9.3";
         edition = "2015";
         sha256 = "0ii1ihpjb30fk38gdikm5wqlkmyr8k46fh4k2r8sagz5dng7ljhr";
+        libName = "core_foundation";
         authors = [
           "The Servo Project Developers"
         ];
@@ -643,6 +650,7 @@ rec {
         version = "0.8.4";
         edition = "2015";
         sha256 = "1yhf471qj6snnm2mcswai47vsbc9w30y4abmdp4crb4av87sb5p4";
+        libName = "core_foundation_sys";
         authors = [
           "The Servo Project Developers"
         ];
@@ -705,6 +713,7 @@ rec {
         version = "0.11.1";
         edition = "2018";
         sha256 = "05672ncc54h66vph42s0a42ljl69bwnqjh0x4xgj2v1395psildi";
+        libName = "crypto_mac";
         authors = [
           "RustCrypto Developers"
         ];
@@ -730,6 +739,7 @@ rec {
         version = "2.6.0";
         edition = "2018";
         sha256 = "1qnn68n4vragxaxlkqcb1r28d3hhj43wch67lm4rpxlw89wnjmp8";
+        libName = "data_encoding";
         authors = [
           "Julien Cretin <git@ia0.eu>"
         ];
@@ -765,6 +775,7 @@ rec {
         version = "2.0.0";
         edition = "2018";
         sha256 = "1q9kr151h9681wwp6is18750ssghz6j9j7qm7qi1ngcwy7mzi35r";
+        libName = "dirs_next";
         authors = [
           "The @xdg-rs members"
         ];
@@ -785,6 +796,7 @@ rec {
         version = "0.1.2";
         edition = "2018";
         sha256 = "0kavhavdxv4phzj4l0psvh55hszwnr0rcz8sxbvx20pyqi2a3gaf";
+        libName = "dirs_sys_next";
         authors = [
           "The @xdg-rs members"
         ];
@@ -809,6 +821,13 @@ rec {
         ];
 
       };
+      "equivalent" = rec {
+        crateName = "equivalent";
+        version = "1.0.1";
+        edition = "2015";
+        sha256 = "1malmx5f4lkfvqasz319lq6gb3ddg19yzf9s8cykfsgzdmyq0hsl";
+
+      };
       "fnv" = rec {
         crateName = "fnv";
         version = "1.0.7";
@@ -889,6 +908,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "0y6b7xxqdjm9hlcjpakcg41qfl7lihf6gavk8fyqijsxhvbzgj7a";
+        libName = "futures_channel";
         dependencies = [
           {
             name = "futures-core";
@@ -916,6 +936,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "07aslayrn3lbggj54kci0ishmd1pr367fp7iks7adia1p05miinz";
+        libName = "futures_core";
         features = {
           "default" = [ "std" ];
           "portable-atomic" = [ "dep:portable-atomic" ];
@@ -928,6 +949,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "07dh08gs9vfll2h36kq32q9xd86xm6lyl9xikmmwlkqnmrrgqxm5";
+        libName = "futures_executor";
         dependencies = [
           {
             name = "futures-core";
@@ -958,6 +980,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "1hgh25isvsr4ybibywhr4dpys8mjnscw4wfxxwca70cn1gi26im4";
+        libName = "futures_io";
         features = {
           "default" = [ "std" ];
         };
@@ -969,6 +992,7 @@ rec {
         edition = "2018";
         sha256 = "1b49qh9d402y8nka4q6wvvj0c88qq91wbr192mdn5h54nzs0qxc7";
         procMacro = true;
+        libName = "futures_macro";
         dependencies = [
           {
             name = "proc-macro2";
@@ -991,6 +1015,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "1dag8xyyaya8n8mh8smx7x6w2dpmafg2din145v973a3hw7f1f4z";
+        libName = "futures_sink";
         features = {
           "default" = [ "std" ];
           "std" = [ "alloc" ];
@@ -1002,6 +1027,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "013h1724454hj8qczp8vvs10qfiqrxr937qsrv6rhii68ahlzn1q";
+        libName = "futures_task";
         features = {
           "default" = [ "std" ];
           "std" = [ "alloc" ];
@@ -1013,6 +1039,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "0j0xqhcir1zf2dcbpd421kgw6wvsk0rpxflylcysn1rlp3g02r1x";
+        libName = "futures_util";
         dependencies = [
           {
             name = "futures-channel";
@@ -1175,9 +1202,9 @@ rec {
       };
       "h2" = rec {
         crateName = "h2";
-        version = "0.3.21";
+        version = "0.3.26";
         edition = "2018";
-        sha256 = "0cq8g5bgk3fihnqicy3g8gc3dpsalzqjg4bjyip9g4my26m27z4i";
+        sha256 = "1s7msnfv7xprzs6xzfj5sg6p8bjcdpcqcmjjbkd345cyi1x55zl1";
         authors = [
           "Carl Lerche <me@carllerche.com>"
           "Sean McArthur <sean@seanmonstar.com>"
@@ -1227,7 +1254,7 @@ rec {
           {
             name = "tokio-util";
             packageId = "tokio-util";
-            features = [ "codec" ];
+            features = [ "codec" "io" ];
           }
           {
             name = "tracing";
@@ -1247,21 +1274,23 @@ rec {
       };
       "hashbrown" = rec {
         crateName = "hashbrown";
-        version = "0.12.3";
+        version = "0.14.5";
         edition = "2021";
-        sha256 = "1268ka4750pyg2pbgsr43f0289l5zah4arir2k4igx5a8c6fg7la";
+        sha256 = "1wa1vy1xs3mp11bn3z9dv0jricgr6a2j0zkf1g19yz3vw4il89z5";
         authors = [
           "Amanieu d'Antras <amanieu@gmail.com>"
         ];
         features = {
           "ahash" = [ "dep:ahash" ];
-          "ahash-compile-time-rng" = [ "ahash/compile-time-rng" ];
           "alloc" = [ "dep:alloc" ];
-          "bumpalo" = [ "dep:bumpalo" ];
+          "allocator-api2" = [ "dep:allocator-api2" ];
           "compiler_builtins" = [ "dep:compiler_builtins" ];
           "core" = [ "dep:core" ];
-          "default" = [ "ahash" "inline-more" ];
+          "default" = [ "ahash" "inline-more" "allocator-api2" ];
+          "equivalent" = [ "dep:equivalent" ];
+          "nightly" = [ "allocator-api2?/nightly" "bumpalo/allocator_api" ];
           "rayon" = [ "dep:rayon" ];
+          "rkyv" = [ "dep:rkyv" ];
           "rustc-dep-of-std" = [ "nightly" "core" "compiler_builtins" "alloc" "rustc-internal-api" ];
           "serde" = [ "dep:serde" ];
         };
@@ -1272,6 +1301,7 @@ rec {
         version = "0.3.3";
         edition = "2021";
         sha256 = "1dyc8qsjh876n74a3rcz8h43s27nj1sypdhsn2ms61bd3b47wzyp";
+        libName = "hermit_abi";
         authors = [
           "Stefan Lankes"
         ];
@@ -1358,6 +1388,7 @@ rec {
         version = "0.4.5";
         edition = "2018";
         sha256 = "1l967qwwlvhp198xdrnc0p5d7jwfcp6q2lm510j6zqw4s4b8zwym";
+        libName = "http_body";
         authors = [
           "Carl Lerche <me@carllerche.com>"
           "Lucio Franco <luciofranco14@gmail.com>"
@@ -1384,6 +1415,7 @@ rec {
         version = "1.1.3";
         edition = "2021";
         sha256 = "1vnald3g10gxj15dc5jjjk7aff23p1zly0xgzhn5gwfrb9k0nmkg";
+        libName = "http_serde";
         authors = [
           "Kornel <kornel@geekhood.net>"
         ];
@@ -1535,6 +1567,7 @@ rec {
         version = "0.23.2";
         edition = "2018";
         sha256 = "0736s6a32dqr107f943xaz1n05flbinq6l19lq1wsrxkc5g9d20p";
+        libName = "hyper_rustls";
         dependencies = [
           {
             name = "http";
@@ -1609,6 +1642,7 @@ rec {
         version = "0.1.58";
         edition = "2018";
         sha256 = "081vcr8z8ddhl5r1ywif6grnswk01b2ac4nks2bhn8zzdimvh9l3";
+        libName = "iana_time_zone";
         authors = [
           "Andrew Straw <strawman@astraw.com>"
           "René Kijewski <rene.kijewski@fu-berlin.de>"
@@ -1654,6 +1688,7 @@ rec {
         version = "0.1.2";
         edition = "2018";
         sha256 = "17r6jmj31chn7xs9698r122mapq85mfnv98bb4pg6spm0si2f67k";
+        libName = "iana_time_zone_haiku";
         authors = [
           "René Kijewski <crates.io@k6i.de>"
         ];
@@ -1667,32 +1702,32 @@ rec {
       };
       "indexmap" = rec {
         crateName = "indexmap";
-        version = "1.9.3";
+        version = "2.4.0";
         edition = "2021";
-        sha256 = "16dxmy7yvk51wvnih3a3im6fp5lmx0wx76i03n06wyak6cwhw1xx";
+        sha256 = "0p2hwvmir50qcl5q6lib8fjq5dzv4f0gqy8czcyfva3yzhzdbslk";
         dependencies = [
           {
+            name = "equivalent";
+            packageId = "equivalent";
+            usesDefaultFeatures = false;
+          }
+          {
             name = "hashbrown";
             packageId = "hashbrown";
             usesDefaultFeatures = false;
             features = [ "raw" ];
           }
         ];
-        buildDependencies = [
-          {
-            name = "autocfg";
-            packageId = "autocfg";
-          }
-        ];
         features = {
           "arbitrary" = [ "dep:arbitrary" ];
+          "borsh" = [ "dep:borsh" ];
+          "default" = [ "std" ];
           "quickcheck" = [ "dep:quickcheck" ];
           "rayon" = [ "dep:rayon" ];
           "rustc-rayon" = [ "dep:rustc-rayon" ];
           "serde" = [ "dep:serde" ];
-          "serde-1" = [ "serde" ];
         };
-        resolvedDefaultFeatures = [ "std" ];
+        resolvedDefaultFeatures = [ "default" "std" ];
       };
       "itoa" = rec {
         crateName = "itoa";
@@ -1728,6 +1763,7 @@ rec {
         version = "0.3.65";
         edition = "2018";
         sha256 = "1s1gaxgzpqfyygc7f2pwp9y128rh5f8zvsc4nm5yazgna9cw7h2l";
+        libName = "js_sys";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -1981,6 +2017,7 @@ rec {
         version = "0.1.1";
         edition = "2021";
         sha256 = "0bpzcrwq89cc5q8mgkmsyx39vjsqaxvaxs29qp8k04rndc7ysfh0";
+        libName = "magic_buffer";
         authors = [
           "Sebastian Klose <mail@sklose.com>"
         ];
@@ -2094,9 +2131,9 @@ rec {
       };
       "mio" = rec {
         crateName = "mio";
-        version = "0.8.9";
+        version = "0.8.11";
         edition = "2018";
-        sha256 = "1l23hg513c23nhcdzvk25caaj28mic6qgqadbn8axgj6bqf2ikix";
+        sha256 = "034byyl0ardml5yliy1hmvx8arkmn9rv479pid794sm07ia519m4";
         authors = [
           "Carl Lerche <me@carllerche.com>"
           "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
@@ -2137,6 +2174,7 @@ rec {
         version = "0.46.0";
         edition = "2018";
         sha256 = "115sywxh53p190lyw97alm14nc004qj5jm5lvdj608z84rbida3p";
+        libName = "nu_ansi_term";
         authors = [
           "ogham@bsago.me"
           "Ryan Scheel (Havvy) <ryan.havvy@gmail.com>"
@@ -2165,6 +2203,7 @@ rec {
         version = "0.2.17";
         edition = "2018";
         sha256 = "0z16bi5zwgfysz6765v3rd6whfbjpihx3mhsn4dg8dzj2c221qrr";
+        libName = "num_traits";
         authors = [
           "The Rust Project Developers"
         ];
@@ -2256,6 +2295,7 @@ rec {
         version = "0.3.0";
         edition = "2018";
         sha256 = "1m8kzi4nd6shdqimn0mgb24f0hxslhnqd1whakyq06wcqd086jk2";
+        libName = "opaque_debug";
         authors = [
           "RustCrypto Developers"
         ];
@@ -2266,6 +2306,7 @@ rec {
         version = "0.1.5";
         edition = "2015";
         sha256 = "1kq18qm48rvkwgcggfkqq6pm948190czqc94d6bm2sir5hq1l0gz";
+        libName = "openssl_probe";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -2353,6 +2394,7 @@ rec {
         version = "2.3.0";
         edition = "2018";
         sha256 = "152slflmparkh27hprw62sph8rv77wckzhwl2dhqk6bf563lfalv";
+        libName = "percent_encoding";
         authors = [
           "The rust-url developers"
         ];
@@ -2367,6 +2409,7 @@ rec {
         version = "1.1.3";
         edition = "2021";
         sha256 = "08k4cpy8q3j93qqgnrbzkcgpn7g0a88l4a9nm33kyghpdhffv97x";
+        libName = "pin_project";
         dependencies = [
           {
             name = "pin-project-internal";
@@ -2381,6 +2424,7 @@ rec {
         edition = "2021";
         sha256 = "01a4l3vb84brv9v7wl71chzxra2kynm6yvcjca66xv3ij6fgsna3";
         procMacro = true;
+        libName = "pin_project_internal";
         dependencies = [
           {
             name = "proc-macro2";
@@ -2403,6 +2447,7 @@ rec {
         version = "0.2.13";
         edition = "2018";
         sha256 = "0n0bwr5qxlf0mhn2xkl36sy55118s9qmvx2yl5f3ixkb007lbywa";
+        libName = "pin_project_lite";
 
       };
       "pin-utils" = rec {
@@ -2410,6 +2455,7 @@ rec {
         version = "0.1.0";
         edition = "2018";
         sha256 = "117ir7vslsl2z1a7qzhws4pd01cg2d3338c47swjyvqv2n60v1wb";
+        libName = "pin_utils";
         authors = [
           "Josef Brandl <mail@josefbrandl.de>"
         ];
@@ -2420,6 +2466,7 @@ rec {
         version = "1.0.69";
         edition = "2021";
         sha256 = "1nljgyllbm3yr3pa081bf83gxh6l4zvjqzaldw7v4mj9xfgihk0k";
+        libName = "proc_macro2";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
           "Alex Crichton <alex@alexcrichton.com>"
@@ -2978,6 +3025,7 @@ rec {
         version = "0.1.23";
         edition = "2015";
         sha256 = "0xnbk2bmyzshacjm2g1kd4zzv2y2az14bw3sjccq5qkpmsfvn9nn";
+        libName = "rustc_demangle";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -3049,6 +3097,7 @@ rec {
         version = "0.6.3";
         edition = "2021";
         sha256 = "007zind70rd5rfsrkdcfm8vn09j8sg02phg9334kark6rdscxam9";
+        libName = "rustls_native_certs";
         dependencies = [
           {
             name = "openssl-probe";
@@ -3077,6 +3126,7 @@ rec {
         version = "1.0.4";
         edition = "2018";
         sha256 = "1324n5bcns0rnw6vywr5agff3rwfvzphi7rmbyzwnv6glkhclx0w";
+        libName = "rustls_pemfile";
         dependencies = [
           {
             name = "base64";
@@ -3159,6 +3209,7 @@ rec {
         version = "2.9.2";
         edition = "2021";
         sha256 = "1pplxk15s5yxvi2m1sz5xfmjibp96cscdcl432w9jzbk0frlzdh5";
+        libName = "security_framework";
         authors = [
           "Steven Fackler <sfackler@gmail.com>"
           "Kornel <kornel@geekhood.net>"
@@ -3205,6 +3256,7 @@ rec {
         version = "2.9.1";
         edition = "2021";
         sha256 = "0yhciwlsy9dh0ps1gw3197kvyqx1bvc4knrhiznhid6kax196cp9";
+        libName = "security_framework_sys";
         authors = [
           "Steven Fackler <sfackler@gmail.com>"
           "Kornel <kornel@geekhood.net>"
@@ -3417,6 +3469,7 @@ rec {
         version = "0.1.7";
         edition = "2018";
         sha256 = "1xipjr4nqsgw34k7a2cgj9zaasl2ds6jwn89886kww93d32a637l";
+        libName = "sharded_slab";
         authors = [
           "Eliza Weisman <eliza@buoyant.io>"
         ];
@@ -3432,12 +3485,16 @@ rec {
       };
       "shlex" = rec {
         crateName = "shlex";
-        version = "1.2.0";
+        version = "1.3.0";
         edition = "2015";
-        sha256 = "1033pj9dyb76nm5yv597nnvj3zpvr2aw9rm5wy0gah3dk99f1km7";
+        sha256 = "0r1y6bv26c1scpxvhg2cabimrmwgbp4p3wy6syj9n0c4s3q2znhg";
         authors = [
           "comex <comexk@gmail.com>"
           "Fenhl <fenhl@fenhl.net>"
+          "Adrian Taylor <adetaylor@chromium.org>"
+          "Alex Touchet <alextouchet@outlook.com>"
+          "Daniel Parks <dp+git@oxidized.org>"
+          "Garrett Berg <googberg@gmail.com>"
         ];
         features = {
           "default" = [ "std" ];
@@ -3449,6 +3506,7 @@ rec {
         version = "1.4.1";
         edition = "2015";
         sha256 = "18crkkw5k82bvcx088xlf5g4n3772m24qhzgfan80nda7d3rn8nq";
+        libName = "signal_hook_registry";
         authors = [
           "Michal 'vorner' Vaner <vorner@vorner.cz>"
           "Masaki Hara <ackie.h.gmai@gmail.com>"
@@ -3651,6 +3709,7 @@ rec {
         edition = "2021";
         sha256 = "1f0lmam4765sfnwr4b1n00y14vxh10g0311mkk0adr80pi02wsr6";
         procMacro = true;
+        libName = "thiserror_impl";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -3806,6 +3865,7 @@ rec {
         edition = "2021";
         sha256 = "0fwjy4vdx1h9pi4g2nml72wi0fr27b5m954p13ji9anyy8l1x2jv";
         procMacro = true;
+        libName = "tokio_macros";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -3831,6 +3891,7 @@ rec {
         version = "0.23.4";
         edition = "2018";
         sha256 = "0nfsmmi8l1lgpbfy6079d5i13984djzcxrdr9jc06ghi0cwyhgn4";
+        libName = "tokio_rustls";
         authors = [
           "quininer kel <quininer@live.com>"
         ];
@@ -3869,6 +3930,7 @@ rec {
         version = "0.1.14";
         edition = "2021";
         sha256 = "0hi8hcwavh5sdi1ivc9qc4yvyr32f153c212dpd7sb366y6rhz1r";
+        libName = "tokio_stream";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -3912,6 +3974,7 @@ rec {
         version = "0.7.10";
         edition = "2021";
         sha256 = "058y6x4mf0fsqji9rfyb77qbfyc50y4pk2spqgj6xsyr693z66al";
+        libName = "tokio_util";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -3967,7 +4030,7 @@ rec {
           "time" = [ "tokio/time" "slab" ];
           "tracing" = [ "dep:tracing" ];
         };
-        resolvedDefaultFeatures = [ "codec" "default" "tracing" ];
+        resolvedDefaultFeatures = [ "codec" "default" "io" "tracing" ];
       };
       "tower" = rec {
         crateName = "tower";
@@ -4062,6 +4125,7 @@ rec {
         version = "0.3.2";
         edition = "2018";
         sha256 = "1l7i17k9vlssrdg4s3b0ia5jjkmmxsvv8s9y9ih0jfi8ssz8s362";
+        libName = "tower_layer";
         authors = [
           "Tower Maintainers <team@tower-rs.com>"
         ];
@@ -4072,6 +4136,7 @@ rec {
         version = "0.3.2";
         edition = "2018";
         sha256 = "0lmfzmmvid2yp2l36mbavhmqgsvzqf7r2wiwz73ml4xmwaf1rg5n";
+        libName = "tower_service";
         authors = [
           "Tower Maintainers <team@tower-rs.com>"
         ];
@@ -4130,6 +4195,7 @@ rec {
         edition = "2018";
         sha256 = "1rvb5dn9z6d0xdj14r403z0af0bbaqhg02hq4jc97g5wds6lqw1l";
         procMacro = true;
+        libName = "tracing_attributes";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
           "Eliza Weisman <eliza@buoyant.io>"
@@ -4158,6 +4224,7 @@ rec {
         version = "0.1.32";
         edition = "2018";
         sha256 = "0m5aglin3cdwxpvbg6kz0r9r0k31j48n0kcfwsp6l49z26k3svf0";
+        libName = "tracing_core";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -4188,6 +4255,7 @@ rec {
         version = "0.1.4";
         edition = "2018";
         sha256 = "1wmxawaz94sk52i4vs2wg5d5clyks972rqskrvc93rxl14ki2lgp";
+        libName = "tracing_log";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -4220,6 +4288,7 @@ rec {
         version = "0.1.3";
         edition = "2018";
         sha256 = "1qfr0va69djvxqvjrx4vqq7p6myy414lx4w1f6amcn0hfwqj2sxw";
+        libName = "tracing_serde";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -4244,6 +4313,7 @@ rec {
         version = "0.3.17";
         edition = "2018";
         sha256 = "0xvwfpmb943hdy4gzyn7a2azgigf30mfd1kx10gyh5gr6yy539ih";
+        libName = "tracing_subscriber";
         authors = [
           "Eliza Weisman <eliza@buoyant.io>"
           "David Barsky <me@davidbarsky.com>"
@@ -4338,6 +4408,7 @@ rec {
         version = "0.2.4";
         edition = "2015";
         sha256 = "1vc15paa4zi06ixsxihwbvfn24d708nsyg1ncgqwcrn42byyqa1m";
+        libName = "try_lock";
         authors = [
           "Sean McArthur <sean@seanmonstar.com>"
         ];
@@ -4459,6 +4530,7 @@ rec {
         version = "1.0.12";
         edition = "2018";
         sha256 = "0jzf1znfpb2gx8nr8mvmyqs1crnv79l57nxnbiszc7xf7ynbjm1k";
+        libName = "unicode_ident";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -4546,6 +4618,7 @@ rec {
         version = "0.2.88";
         edition = "2018";
         sha256 = "1khgsh4z9bga35mjhg41dl7523i69ffc5m8ckhqaw6ssyabc5bkx";
+        libName = "wasm_bindgen";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -4577,6 +4650,7 @@ rec {
         version = "0.2.88";
         edition = "2018";
         sha256 = "05zj8yl243rvs87rhicq2l1d6443lnm6k90khf744khf9ikg95z3";
+        libName = "wasm_bindgen_backend";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -4622,6 +4696,7 @@ rec {
         edition = "2018";
         sha256 = "1chn3wgw9awmvs0fpmazbqyc5rwfgy3pj7lzwczmzb887dxh2qar";
         procMacro = true;
+        libName = "wasm_bindgen_macro";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -4646,6 +4721,7 @@ rec {
         version = "0.2.88";
         edition = "2018";
         sha256 = "01rrzg3y1apqygsjz1jg0n7p831nm4kdyxmxyl85x7v6mf6kndf5";
+        libName = "wasm_bindgen_macro_support";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -4684,6 +4760,7 @@ rec {
         edition = "2018";
         links = "wasm_bindgen";
         sha256 = "02vmw2rzsla1qm0zgfng4kqz52xn8k54v8ads4g1macv09fnq10d";
+        libName = "wasm_bindgen_shared";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -4694,6 +4771,7 @@ rec {
         version = "0.3.65";
         edition = "2018";
         sha256 = "11ba406ca9qssc21c37v49sn2y2gsdn6c3nva4hjf8v3yv2rkd2x";
+        libName = "web_sys";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -5213,6 +5291,7 @@ rec {
         version = "0.4.0";
         edition = "2015";
         sha256 = "1dmpa6mvcvzz16zg6d5vrfy4bxgg541wxrcip7cnshi06v38ffxc";
+        libName = "winapi_i686_pc_windows_gnu";
         authors = [
           "Peter Atashian <retep998@gmail.com>"
         ];
@@ -5223,6 +5302,7 @@ rec {
         version = "0.4.0";
         edition = "2015";
         sha256 = "0gqq64czqb64kskjryj8isp62m2sgvx25yyj3kpc2myh85w24bki";
+        libName = "winapi_x86_64_pc_windows_gnu";
         authors = [
           "Peter Atashian <retep998@gmail.com>"
         ];
@@ -5233,6 +5313,7 @@ rec {
         version = "0.51.1";
         edition = "2021";
         sha256 = "0r1f57hsshsghjyc7ypp2s0i78f7b1vr93w68sdb8baxyf2czy7i";
+        libName = "windows_core";
         authors = [
           "Microsoft"
         ];
@@ -5250,6 +5331,7 @@ rec {
         version = "0.48.0";
         edition = "2018";
         sha256 = "1aan23v5gs7gya1lc46hqn9mdh8yph3fhxmhxlw36pn6pqc28zb7";
+        libName = "windows_sys";
         authors = [
           "Microsoft"
         ];
@@ -5543,6 +5625,7 @@ rec {
         version = "0.48.5";
         edition = "2018";
         sha256 = "034ljxqshifs1lan89xwpcy1hp0lhdh4b5n0d2z4fwjx2piacbws";
+        libName = "windows_targets";
         authors = [
           "Microsoft"
         ];
@@ -5719,6 +5802,7 @@ rec {
         version = "4.1.3+zstd.1.5.1";
         edition = "2018";
         sha256 = "0yfvqzzkbj871f2vaikal5rm2gf60p1mdzp3jk3w5hmkkywq37g9";
+        libName = "zstd_safe";
         authors = [
           "Alexandre Bury <alexandre.bury@gmail.com>"
         ];
@@ -5753,6 +5837,7 @@ rec {
         edition = "2018";
         links = "zstd";
         sha256 = "17xcr0mw8ps9hlc8m0dzj7yd52lb9r9ic9fbpxa4994yilj2zbrd";
+        libName = "zstd_sys";
         authors = [
           "Alexandre Bury <alexandre.bury@gmail.com>"
         ];
@@ -5885,52 +5970,41 @@ rec {
                 testPostRun
               ]);
           in
-          pkgs.runCommand "run-tests-${testCrate.name}"
-            {
-              inherit testCrateFlags;
-              buildInputs = testInputs;
-            } ''
-            set -e
+          pkgs.stdenvNoCC.mkDerivation {
+            name = "run-tests-${testCrate.name}";
 
-            export RUST_BACKTRACE=1
+            inherit (crate) src;
 
-            # recreate a file hierarchy as when running tests with cargo
+            inherit testCrateFlags;
 
-            # the source for test data
-            # It's necessary to locate the source in $NIX_BUILD_TOP/source/
-            # instead of $NIX_BUILD_TOP/
-            # because we compiled those test binaries in the former and not the latter.
-            # So all paths will expect source tree to be there and not in the build top directly.
-            # For example: $NIX_BUILD_TOP := /build in general, if you ask yourself.
-            # NOTE: There could be edge cases if `crate.sourceRoot` does exist but
-            # it's very hard to reason about them.
-            # Open a bug if you run into this!
-            mkdir -p source/
-            cd source/
+            buildInputs = testInputs;
 
-            ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${crate.src}
+            buildPhase = ''
+              set -e
+              export RUST_BACKTRACE=1
 
-            # build outputs
-            testRoot=target/debug
-            mkdir -p $testRoot
+              # build outputs
+              testRoot=target/debug
+              mkdir -p $testRoot
 
-            # executables of the crate
-            # we copy to prevent std::env::current_exe() to resolve to a store location
-            for i in ${crate}/bin/*; do
-              cp "$i" "$testRoot"
-            done
-            chmod +w -R .
+              # executables of the crate
+              # we copy to prevent std::env::current_exe() to resolve to a store location
+              for i in ${crate}/bin/*; do
+                cp "$i" "$testRoot"
+              done
+              chmod +w -R .
 
-            # test harness executables are suffixed with a hash, like cargo does
-            # this allows to prevent name collision with the main
-            # executables of the crate
-            hash=$(basename $out)
-            for file in ${drv}/tests/*; do
-              f=$testRoot/$(basename $file)-$hash
-              cp $file $f
-              ${testCommand}
-            done
-          '';
+              # test harness executables are suffixed with a hash, like cargo does
+              # this allows to prevent name collision with the main
+              # executables of the crate
+              hash=$(basename $out)
+              for file in ${drv}/tests/*; do
+                f=$testRoot/$(basename $file)-$hash
+                cp $file $f
+                ${testCommand}
+              done
+            '';
+          };
       in
       pkgs.runCommand "${crate.name}-linked"
         {
diff --git a/tvix/tools/turbofetch/default.nix b/tvix/tools/turbofetch/default.nix
index 10f3d134138e..a98b2f0b4330 100644
--- a/tvix/tools/turbofetch/default.nix
+++ b/tvix/tools/turbofetch/default.nix
@@ -6,4 +6,6 @@
       src = depot.tvix.utils.filterRustCrateSrc { root = prev.src.origSrc; };
     };
   };
-}).rootCrate.build
+}).rootCrate.build.overrideAttrs {
+  meta.ci.extraSteps.crate2nix-check = depot.tvix.utils.mkCrate2nixCheck ./Cargo.nix;
+}
diff --git a/tvix/tools/weave/Cargo.lock b/tvix/tools/weave/Cargo.lock
index 5fb04a522b21..bb571af1a20d 100644
--- a/tvix/tools/weave/Cargo.lock
+++ b/tvix/tools/weave/Cargo.lock
@@ -271,9 +271,9 @@ dependencies = [
 
 [[package]]
 name = "bytes"
-version = "1.5.0"
+version = "1.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
+checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
 
 [[package]]
 name = "cc"
@@ -422,16 +422,15 @@ dependencies = [
 
 [[package]]
 name = "curve25519-dalek"
-version = "4.1.2"
+version = "4.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348"
+checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
 dependencies = [
  "cfg-if",
  "cpufeatures",
  "curve25519-dalek-derive",
  "digest",
  "fiat-crypto",
- "platforms",
  "rustc_version",
  "subtle",
  "zeroize",
@@ -805,6 +804,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
 
 [[package]]
+name = "libmimalloc-sys"
+version = "0.1.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23aa6811d3bd4deb8a84dde645f943476d13b248d818edcf8ce0b2f37f036b44"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
 name = "lock_api"
 version = "0.4.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -856,6 +865,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "mimalloc"
+version = "0.1.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68914350ae34959d83f732418d51e2427a794055d0b9529f48259ac07af65633"
+dependencies = [
+ "libmimalloc-sys",
+]
+
+[[package]]
 name = "minimal-lexical"
 version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -872,9 +890,9 @@ dependencies = [
 
 [[package]]
 name = "mio"
-version = "0.8.10"
+version = "0.8.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
+checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
 dependencies = [
  "libc",
  "wasi",
@@ -909,11 +927,13 @@ version = "0.1.0"
 dependencies = [
  "bitflags 2.4.2",
  "bstr",
+ "bytes",
  "data-encoding",
  "ed25519",
  "ed25519-dalek",
  "enum-primitive-derive",
  "glob",
+ "mimalloc",
  "nom",
  "num-traits",
  "pin-project-lite",
@@ -922,6 +942,7 @@ dependencies = [
  "sha2",
  "thiserror",
  "tokio",
+ "tracing",
 ]
 
 [[package]]
@@ -1073,12 +1094,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "platforms"
-version = "3.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c"
-
-[[package]]
 name = "polars"
 version = "0.36.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1864,6 +1879,37 @@ dependencies = [
 ]
 
 [[package]]
+name = "tracing"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
 name = "typenum"
 version = "1.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/tvix/tools/weave/Cargo.nix b/tvix/tools/weave/Cargo.nix
index e485ecd28089..25b2c94a3f11 100644
--- a/tvix/tools/weave/Cargo.nix
+++ b/tvix/tools/weave/Cargo.nix
@@ -207,6 +207,7 @@ rec {
         edition = "2015";
         crateBin = [ ];
         sha256 = "1cy6r2sfv5y5cigv86vms7n5nlwhx1rbyxwcraqnmm1rxiib2yyc";
+        libName = "alloc_no_stdlib";
         authors = [
           "Daniel Reiter Horn <danielrh@dropbox.com>"
         ];
@@ -218,6 +219,7 @@ rec {
         edition = "2015";
         crateBin = [ ];
         sha256 = "1kkfbld20ab4165p29v172h8g0wvq8i06z8vnng14whw0isq5ywl";
+        libName = "alloc_stdlib";
         authors = [
           "Daniel Reiter Horn <danielrh@dropbox.com>"
         ];
@@ -234,6 +236,7 @@ rec {
         version = "0.2.16";
         edition = "2018";
         sha256 = "1iayppgq4wqbfbfcqmsbwgamj0s65012sskfvyx07pxavk3gyhh9";
+        libName = "allocator_api2";
         authors = [
           "Zakarum <zaq.dev@icloud.com>"
         ];
@@ -249,6 +252,7 @@ rec {
         version = "0.1.1";
         edition = "2018";
         sha256 = "1w7ynjxrfs97xg3qlcdns4kgfpwcdv824g611fq32cag4cdr96g9";
+        libName = "android_tzdata";
         authors = [
           "RumovZ"
         ];
@@ -320,6 +324,7 @@ rec {
         version = "0.2.0";
         edition = "2021";
         sha256 = "0xpbqf7qkvzplpjd7f0wbcf2n1v9vygdccwxkd1amxp4il0hlzdz";
+        libName = "array_init_cursor";
 
       };
       "arrow-format" = rec {
@@ -327,6 +332,7 @@ rec {
         version = "0.8.1";
         edition = "2018";
         sha256 = "1irj67p6c224dzw86jr7j3z9r5zfid52gy6ml8rdqk4r2si4x207";
+        libName = "arrow_format";
         authors = [
           "Jorge C. Leitao <jorgecarleitao@gmail.com>"
         ];
@@ -362,6 +368,7 @@ rec {
         version = "0.3.5";
         edition = "2018";
         sha256 = "0l8sjq1rylkb1ak0pdyjn83b3k6x36j22myngl4sqqgg7whdsmnd";
+        libName = "async_stream";
         authors = [
           "Carl Lerche <me@carllerche.com>"
         ];
@@ -387,6 +394,7 @@ rec {
         edition = "2018";
         sha256 = "14q179j4y8p2z1d0ic6aqgy9fhwz8p9cai1ia8kpw4bw7q12mrhn";
         procMacro = true;
+        libName = "async_stream_impl";
         authors = [
           "Carl Lerche <me@carllerche.com>"
         ];
@@ -413,6 +421,7 @@ rec {
         edition = "2021";
         sha256 = "1adf1jh2yg39rkpmqjqyr9xyd6849p0d95425i6imgbhx0syx069";
         procMacro = true;
+        libName = "async_trait";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -604,6 +613,7 @@ rec {
         version = "0.10.4";
         edition = "2018";
         sha256 = "0w9sa2ypmrsqqvc20nhwr75wbb5cjr4kkyhpjm1z1lv2kdicfy1h";
+        libName = "block_buffer";
         authors = [
           "RustCrypto Developers"
         ];
@@ -659,6 +669,7 @@ rec {
         edition = "2015";
         crateBin = [ ];
         sha256 = "0kyyh9701dwqzwvn2frff4ww0zibikqd1s1xvl7n1pfpc3z4lbjf";
+        libName = "brotli_decompressor";
         authors = [
           "Daniel Reiter Horn <danielrh@dropbox.com>"
           "The Brotli Authors"
@@ -781,9 +792,9 @@ rec {
       };
       "bytes" = rec {
         crateName = "bytes";
-        version = "1.5.0";
+        version = "1.6.1";
         edition = "2018";
-        sha256 = "08w2i8ac912l8vlvkv3q51cd4gr09pwlg3sjsjffcizlrb0i5gd2";
+        sha256 = "0lnryqfiymbq5mfflfmbsqvfnw80kkh36nk5kpiscgxb9ac1cad1";
         authors = [
           "Carl Lerche <me@carllerche.com>"
           "Sean McArthur <sean@seanmonstar.com>"
@@ -827,6 +838,7 @@ rec {
         version = "1.0.0";
         edition = "2018";
         sha256 = "1za0vb97n4brpzpv8lsbnzmq5r8f2b0cpqqr0sy8h5bn751xxwds";
+        libName = "cfg_if";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -897,6 +909,7 @@ rec {
         version = "7.1.0";
         edition = "2021";
         sha256 = "11i6sm6vznv9982hqpbrba43vfd7vv7zqzlywdc4qykvdhyh8r3w";
+        libName = "comfy_table";
         authors = [
           "Arne Beer <contact@arne.beer>"
         ];
@@ -944,6 +957,7 @@ rec {
         version = "0.9.6";
         edition = "2021";
         sha256 = "1y0jnqaq7p2wvspnx7qj76m7hjcqpz73qzvr9l2p9n2s51vr6if2";
+        libName = "const_oid";
         authors = [
           "RustCrypto Developers"
         ];
@@ -956,6 +970,7 @@ rec {
         version = "0.8.6";
         edition = "2018";
         sha256 = "13w6sdf06r0hn7bx2b45zxsg1mm2phz34jikm6xc5qrbr6djpsh6";
+        libName = "core_foundation_sys";
         authors = [
           "The Servo Project Developers"
         ];
@@ -1021,6 +1036,7 @@ rec {
         version = "0.5.11";
         edition = "2021";
         sha256 = "16v48qdflpw3hgdik70bhsj7hympna79q7ci47rw0mlgnxsw2v8p";
+        libName = "crossbeam_channel";
         dependencies = [
           {
             name = "crossbeam-utils";
@@ -1039,6 +1055,7 @@ rec {
         version = "0.8.5";
         edition = "2021";
         sha256 = "03bp38ljx4wj6vvy4fbhx41q8f585zyqix6pncz1mkz93z08qgv1";
+        libName = "crossbeam_deque";
         dependencies = [
           {
             name = "crossbeam-epoch";
@@ -1062,6 +1079,7 @@ rec {
         version = "0.9.18";
         edition = "2021";
         sha256 = "03j2np8llwf376m3fxqx859mgp9f83hj1w34153c7a9c7i5ar0jv";
+        libName = "crossbeam_epoch";
         dependencies = [
           {
             name = "crossbeam-utils";
@@ -1083,6 +1101,7 @@ rec {
         version = "0.3.11";
         edition = "2021";
         sha256 = "0d8y8y3z48r9javzj67v3p2yfswd278myz1j9vzc4sp7snslc0yz";
+        libName = "crossbeam_queue";
         dependencies = [
           {
             name = "crossbeam-utils";
@@ -1102,6 +1121,7 @@ rec {
         version = "0.8.19";
         edition = "2021";
         sha256 = "0iakrb1b8fjqrag7wphl94d10irhbh2fw1g444xslsywqyn3p3i4";
+        libName = "crossbeam_utils";
         features = {
           "default" = [ "std" ];
           "loom" = [ "dep:loom" ];
@@ -1178,6 +1198,7 @@ rec {
         version = "0.1.6";
         edition = "2018";
         sha256 = "1cvby95a6xg7kxdz5ln3rl9xh66nz66w46mm3g56ri1z5x815yqv";
+        libName = "crypto_common";
         authors = [
           "RustCrypto Developers"
         ];
@@ -1200,9 +1221,10 @@ rec {
       };
       "curve25519-dalek" = rec {
         crateName = "curve25519-dalek";
-        version = "4.1.2";
+        version = "4.1.3";
         edition = "2021";
-        sha256 = "0j7kqchcgycs4a11gvlda93h9w2jr05nn4hjpfyh2kn94a4pnrqa";
+        sha256 = "1gmjb9dsknrr8lypmhkyjd67p1arb8mbfamlwxm7vph38my8pywp";
+        libName = "curve25519_dalek";
         authors = [
           "Isis Lovecruft <isis@patternsinthevoid.net>"
           "Henry de Valence <hdevalence@hdevalence.ca>"
@@ -1248,10 +1270,6 @@ rec {
         ];
         buildDependencies = [
           {
-            name = "platforms";
-            packageId = "platforms";
-          }
-          {
             name = "rustc_version";
             packageId = "rustc_version";
           }
@@ -1275,6 +1293,7 @@ rec {
         edition = "2021";
         sha256 = "1cry71xxrr0mcy5my3fb502cwfxy6822k4pm19cwrilrg7hq4s7l";
         procMacro = true;
+        libName = "curve25519_dalek_derive";
         dependencies = [
           {
             name = "proc-macro2";
@@ -1297,6 +1316,7 @@ rec {
         version = "2.6.0";
         edition = "2018";
         sha256 = "1qnn68n4vragxaxlkqcb1r28d3hhj43wch67lm4rpxlw89wnjmp8";
+        libName = "data_encoding";
         authors = [
           "Julien Cretin <git@ia0.eu>"
         ];
@@ -1380,6 +1400,7 @@ rec {
         version = "1.0.16";
         edition = "2018";
         sha256 = "0pa9kas6a241pbx0q82ipwi4f7m7wwyzkkc725caky24gl4j4nsl";
+        libName = "dyn_clone";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -1422,6 +1443,7 @@ rec {
         version = "2.1.1";
         edition = "2021";
         sha256 = "0w88cafwglg9hjizldbmlza0ns3hls81zk1bcih3m5m3h67algaa";
+        libName = "ed25519_dalek";
         authors = [
           "isis lovecruft <isis@patternsinthevoid.net>"
           "Tony Arcieri <bascule@gmail.com>"
@@ -1514,6 +1536,7 @@ rec {
         edition = "2018";
         sha256 = "0k6wcf58h5kh64yq5nfq71va53kaya0kzxwsjwbgwm2n2zd9axxs";
         procMacro = true;
+        libName = "enum_primitive_derive";
         authors = [
           "Doug Goldstein <cardoe@cardoe.com>"
         ];
@@ -1590,6 +1613,7 @@ rec {
         version = "0.1.9";
         edition = "2015";
         sha256 = "0nj6j26p71bjy8h42x6jahx1hn0ng6mc2miwpgwnp8vnwqf4jq3k";
+        libName = "fallible_streaming_iterator";
         authors = [
           "Steven Fackler <sfackler@gmail.com>"
         ];
@@ -1600,6 +1624,7 @@ rec {
         version = "0.2.0";
         edition = "2018";
         sha256 = "0g7kfll3xyh99kc7r352lhljnwvgayxxa6saifb6725inikmyxlm";
+        libName = "fast_float";
         authors = [
           "Ivan Smirnov <i.s.smirnov@gmail.com>"
         ];
@@ -1613,6 +1638,7 @@ rec {
         version = "0.2.6";
         edition = "2018";
         sha256 = "10hkkkjynhibvchznkxx81gwxqarn9i5sgz40d6xxb8xzhsz8xhn";
+        libName = "fiat_crypto";
         authors = [
           "Fiat Crypto library authors <jgross@mit.edu>"
         ];
@@ -1742,6 +1768,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "0y6b7xxqdjm9hlcjpakcg41qfl7lihf6gavk8fyqijsxhvbzgj7a";
+        libName = "futures_channel";
         dependencies = [
           {
             name = "futures-core";
@@ -1769,6 +1796,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "07aslayrn3lbggj54kci0ishmd1pr367fp7iks7adia1p05miinz";
+        libName = "futures_core";
         features = {
           "default" = [ "std" ];
           "portable-atomic" = [ "dep:portable-atomic" ];
@@ -1781,6 +1809,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "07dh08gs9vfll2h36kq32q9xd86xm6lyl9xikmmwlkqnmrrgqxm5";
+        libName = "futures_executor";
         dependencies = [
           {
             name = "futures-core";
@@ -1811,6 +1840,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "1hgh25isvsr4ybibywhr4dpys8mjnscw4wfxxwca70cn1gi26im4";
+        libName = "futures_io";
         features = {
           "default" = [ "std" ];
         };
@@ -1822,6 +1852,7 @@ rec {
         edition = "2018";
         sha256 = "1b49qh9d402y8nka4q6wvvj0c88qq91wbr192mdn5h54nzs0qxc7";
         procMacro = true;
+        libName = "futures_macro";
         dependencies = [
           {
             name = "proc-macro2";
@@ -1844,6 +1875,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "1dag8xyyaya8n8mh8smx7x6w2dpmafg2din145v973a3hw7f1f4z";
+        libName = "futures_sink";
         features = {
           "default" = [ "std" ];
           "std" = [ "alloc" ];
@@ -1855,6 +1887,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "013h1724454hj8qczp8vvs10qfiqrxr937qsrv6rhii68ahlzn1q";
+        libName = "futures_task";
         features = {
           "default" = [ "std" ];
           "std" = [ "alloc" ];
@@ -1866,6 +1899,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "0j0xqhcir1zf2dcbpd421kgw6wvsk0rpxflylcysn1rlp3g02r1x";
+        libName = "futures_util";
         dependencies = [
           {
             name = "futures-channel";
@@ -2119,6 +2153,7 @@ rec {
         version = "0.3.5";
         edition = "2021";
         sha256 = "1hw2bxkzyvr0rbnpj0lkasi8h8qf3lyb63hp760cn22fjqaj3inh";
+        libName = "hermit_abi";
         authors = [
           "Stefan Lankes"
         ];
@@ -2153,6 +2188,7 @@ rec {
         version = "0.1.60";
         edition = "2018";
         sha256 = "0hdid5xz3jznm04lysjm3vi93h3c523w0hcc3xba47jl3ddbpzz7";
+        libName = "iana_time_zone";
         authors = [
           "Andrew Straw <strawman@astraw.com>"
           "René Kijewski <rene.kijewski@fu-berlin.de>"
@@ -2198,6 +2234,7 @@ rec {
         version = "0.1.2";
         edition = "2018";
         sha256 = "17r6jmj31chn7xs9698r122mapq85mfnv98bb4pg6spm0si2f67k";
+        libName = "iana_time_zone_haiku";
         authors = [
           "René Kijewski <crates.io@k6i.de>"
         ];
@@ -2271,6 +2308,7 @@ rec {
         version = "0.3.68";
         edition = "2018";
         sha256 = "1vm98fhnhs4w6yakchi9ip7ar95900k9vkr24a21qlwd6r5xlv20";
+        libName = "js_sys";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -2312,6 +2350,33 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" ];
       };
+      "libmimalloc-sys" = rec {
+        crateName = "libmimalloc-sys";
+        version = "0.1.39";
+        edition = "2018";
+        links = "mimalloc";
+        sha256 = "0i3b0dzz7cp0ik7ys66q92r16va78gwlbrnxhj5fnkdxsc8niai3";
+        libName = "libmimalloc_sys";
+        authors = [
+          "Octavian Oncescu <octavonce@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+        features = {
+          "cty" = [ "dep:cty" ];
+          "extended" = [ "cty" ];
+        };
+      };
       "lock_api" = rec {
         crateName = "lock_api";
         version = "0.4.11";
@@ -2388,6 +2453,7 @@ rec {
         edition = "2015";
         links = "lz4";
         sha256 = "0059ik4xlvnss5qfh6l691psk4g3350ljxaykzv10yr0gqqppljp";
+        libName = "lz4_sys";
         authors = [
           "Jens Heyens <jens.heyens@ewetel.net>"
           "Artem V. Navrotskiy <bozaro@buzzsoft.ru>"
@@ -2447,11 +2513,40 @@ rec {
           "stable_deref_trait" = [ "dep:stable_deref_trait" ];
         };
       };
+      "mimalloc" = rec {
+        crateName = "mimalloc";
+        version = "0.1.43";
+        edition = "2018";
+        sha256 = "0csnyrxc16i592gm5ffham07jyj2w98qsh9jyy1rv59lmr8474b8";
+        authors = [
+          "Octavian Oncescu <octavonce@gmail.com>"
+          "Vincent Rouillé <vincent@speedy37.fr>"
+          "Thom Chiovoloni <chiovolonit@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libmimalloc-sys";
+            packageId = "libmimalloc-sys";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "debug" = [ "libmimalloc-sys/debug" ];
+          "debug_in_debug" = [ "libmimalloc-sys/debug_in_debug" ];
+          "extended" = [ "libmimalloc-sys/extended" ];
+          "local_dynamic_tls" = [ "libmimalloc-sys/local_dynamic_tls" ];
+          "no_thp" = [ "libmimalloc-sys/no_thp" ];
+          "override" = [ "libmimalloc-sys/override" ];
+          "secure" = [ "libmimalloc-sys/secure" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
       "minimal-lexical" = rec {
         crateName = "minimal-lexical";
         version = "0.2.1";
         edition = "2018";
         sha256 = "16ppc5g84aijpri4jzv14rvcnslvlpphbszc7zzp6vfkddf4qdb8";
+        libName = "minimal_lexical";
         authors = [
           "Alex Huszagh <ahuszagh@gmail.com>"
         ];
@@ -2489,9 +2584,9 @@ rec {
       };
       "mio" = rec {
         crateName = "mio";
-        version = "0.8.10";
+        version = "0.8.11";
         edition = "2018";
-        sha256 = "02gyaxvaia9zzi4drrw59k9s0j6pa5d1y2kv7iplwjipdqlhngcg";
+        sha256 = "034byyl0ardml5yliy1hmvx8arkmn9rv479pid794sm07ia519m4";
         authors = [
           "Carl Lerche <me@carllerche.com>"
           "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
@@ -2558,6 +2653,7 @@ rec {
         edition = "2021";
         sha256 = "1j1avbxw7jscyi7dmnywhlwbiny1fvg1vpp9fy4dc1pd022kva16";
         procMacro = true;
+        libName = "multiversion_macros";
         authors = [
           "Caleb Zulawski <caleb.zulawski@gmail.com>"
         ];
@@ -2591,6 +2687,7 @@ rec {
         edition = "2021";
         crateBin = [ ];
         src = lib.cleanSourceWith { filter = sourceFilter; src = ../../nix-compat; };
+        libName = "nix_compat";
         dependencies = [
           {
             name = "bitflags";
@@ -2602,6 +2699,11 @@ rec {
             features = [ "alloc" "unicode" "serde" ];
           }
           {
+            name = "bytes";
+            packageId = "bytes";
+            optional = true;
+          }
+          {
             name = "data-encoding";
             packageId = "data-encoding";
           }
@@ -2622,6 +2724,10 @@ rec {
             packageId = "glob";
           }
           {
+            name = "mimalloc";
+            packageId = "mimalloc";
+          }
+          {
             name = "nom";
             packageId = "nom";
           }
@@ -2657,21 +2763,30 @@ rec {
             optional = true;
             features = [ "io-util" "macros" ];
           }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
         ];
         devDependencies = [
           {
+            name = "mimalloc";
+            packageId = "mimalloc";
+          }
+          {
             name = "serde_json";
             packageId = "serde_json";
           }
         ];
         features = {
           "async" = [ "tokio" ];
+          "bytes" = [ "dep:bytes" ];
           "default" = [ "async" "wire" ];
           "pin-project-lite" = [ "dep:pin-project-lite" ];
           "tokio" = [ "dep:tokio" ];
-          "wire" = [ "tokio" "pin-project-lite" ];
+          "wire" = [ "tokio" "pin-project-lite" "bytes" ];
         };
-        resolvedDefaultFeatures = [ "async" "default" "pin-project-lite" "tokio" "wire" ];
+        resolvedDefaultFeatures = [ "async" "bytes" "default" "pin-project-lite" "tokio" "wire" ];
       };
       "nom" = rec {
         crateName = "nom";
@@ -2743,6 +2858,7 @@ rec {
         version = "0.2.18";
         edition = "2018";
         sha256 = "0yjib8p2p9kzmaz48xwhs69w5dh1wipph9jgnillzd2x33jz03fs";
+        libName = "num_traits";
         authors = [
           "The Rust Project Developers"
         ];
@@ -2926,6 +3042,7 @@ rec {
         version = "0.2.4";
         edition = "2021";
         sha256 = "07wf6wf4jrxlq5p3xldxsnabp7jl06my2qp7kiwy9m3x2r5wac8i";
+        libName = "parquet_format_safe";
         authors = [
           "Apache Thrift contributors <dev@thrift.apache.org>"
           "Jorge Leitao <jorgecarleitao@gmail.com>"
@@ -2955,6 +3072,7 @@ rec {
         version = "2.3.1";
         edition = "2018";
         sha256 = "0gi8wgx0dcy8rnv1kywdv98lwcx67hz0a0zwpib5v2i08r88y573";
+        libName = "percent_encoding";
         authors = [
           "The rust-url developers"
         ];
@@ -2969,6 +3087,7 @@ rec {
         version = "0.2.13";
         edition = "2018";
         sha256 = "0n0bwr5qxlf0mhn2xkl36sy55118s9qmvx2yl5f3ixkb007lbywa";
+        libName = "pin_project_lite";
 
       };
       "pin-utils" = rec {
@@ -2976,6 +3095,7 @@ rec {
         version = "0.1.0";
         edition = "2018";
         sha256 = "117ir7vslsl2z1a7qzhws4pd01cg2d3338c47swjyvqv2n60v1wb";
+        libName = "pin_utils";
         authors = [
           "Josef Brandl <mail@josefbrandl.de>"
         ];
@@ -3020,6 +3140,7 @@ rec {
         version = "0.3.29";
         edition = "2015";
         sha256 = "1jy6158v1316khkpmq2sjj1vgbnbnw51wffx7p0k0l9h9vlys019";
+        libName = "pkg_config";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -3041,21 +3162,6 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" "std" ];
       };
-      "platforms" = rec {
-        crateName = "platforms";
-        version = "3.3.0";
-        edition = "2018";
-        sha256 = "0k7q6pigmnvgpfasvssb12m2pv3pc94zrhrfg9by3h3wmhyfqvb2";
-        authors = [
-          "Tony Arcieri <bascule@gmail.com>"
-          "Sergey \"Shnatsel\" Davidoff <shnatsel@gmail.com>"
-        ];
-        features = {
-          "default" = [ "std" ];
-          "serde" = [ "dep:serde" ];
-        };
-        resolvedDefaultFeatures = [ "default" "std" ];
-      };
       "polars" = rec {
         crateName = "polars";
         version = "0.36.2";
@@ -3262,6 +3368,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "0vxql6amvwyp6qj0w87vm21y33qa0i61pshs4ry7ixwgd4ps0s6f";
+        libName = "polars_arrow";
         authors = [
           "Jorge C. Leitao <jorgecarleitao@gmail.com>"
           "Apache Arrow <dev@arrow.apache.org>"
@@ -3440,6 +3547,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "1pa4l1w41gdzdff81ih1z05z3gz568k81i6flibbca8v2igvqkxi";
+        libName = "polars_compute";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -3479,6 +3587,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "08sar9h97znpb8ziyvrrvvx28lyzc21vwsd7gvwybjxn6kkyzxfh";
+        libName = "polars_core";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -3639,6 +3748,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "11m80a899kp45b3dz9jbvrn5001hw8izbdp7d2czrswrixwdx5k3";
+        libName = "polars_error";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -3675,6 +3785,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "1smamd34ghlxyxdd4aqdplk7k8xm1l626brmzlc4fvwlx3pmh13x";
+        libName = "polars_io";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -3871,6 +3982,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "1mdml4hs574njb23mb9gl6x92x2bb4vdfzsazkl3ifq516s0awcx";
+        libName = "polars_lazy";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -4065,6 +4177,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "13m7dh4vpdmcah04a7kql933l7y7045f0hybbmgff4dbav2ay29f";
+        libName = "polars_ops";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -4216,6 +4329,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "0gay037sw5hcg2q93pxcfh7krcchl1px8g838d8vhjpnn5klv8kv";
+        libName = "polars_parquet";
         authors = [
           "Jorge C. Leitao <jorgecarleitao@gmail.com>"
           "Apache Arrow <dev@arrow.apache.org>"
@@ -4336,6 +4450,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "00217q51mnq7i57k3sax293xnwghm5hridbpgl11fffcfg8fmdyr";
+        libName = "polars_pipe";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -4449,6 +4564,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "1l5ca38v7ksq4595wdr6wsd74pdgszwivq9y8wfc6l6h4ib1fjiq";
+        libName = "polars_plan";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -4637,6 +4753,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "0by7x6jlj5dwr3f1gj49v8w65l3kpqhwhzb9qzlv6gdqrdx2ycij";
+        libName = "polars_row";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -4666,6 +4783,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "1dcmm993gycw75a6c5hxcr6h2cy6fa2r3hsbx19h9zgxvxnlq2wz";
+        libName = "polars_sql";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -4730,6 +4848,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "1l24fmpv5v1qshxfd4592z8x6bnjlwy4njhf9rcbdlbbr6gn9qny";
+        libName = "polars_time";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -4808,6 +4927,7 @@ rec {
         version = "0.36.2";
         edition = "2021";
         sha256 = "1nmvfqwyzbaxcw457amspz479y5vcnpalq043awxfixdfx5clx5i";
+        libName = "polars_utils";
         authors = [
           "Ritchie Vink <ritchie46@gmail.com>"
         ];
@@ -4875,6 +4995,7 @@ rec {
         version = "0.2.17";
         edition = "2018";
         sha256 = "1pp6g52aw970adv3x2310n7glqnji96z0a9wiamzw89ibf0ayh2v";
+        libName = "ppv_lite86";
         authors = [
           "The CryptoCorrosion Contributors"
         ];
@@ -4888,6 +5009,7 @@ rec {
         version = "1.0.78";
         edition = "2021";
         sha256 = "1bjak27pqdn4f4ih1c9nr3manzyavsgqmf76ygw9k76q8pb2lhp2";
+        libName = "proc_macro2";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
           "Alex Crichton <alex@alexcrichton.com>"
@@ -5091,6 +5213,7 @@ rec {
         edition = "2021";
         links = "rayon-core";
         sha256 = "1qpwim68ai5h0j7axa8ai8z0payaawv3id0lrgkqmapx7lx8fr8l";
+        libName = "rayon_core";
         authors = [
           "Niko Matsakis <niko@alum.mit.edu>"
           "Josh Stone <cuviper@gmail.com>"
@@ -5192,6 +5315,7 @@ rec {
         version = "0.4.5";
         edition = "2021";
         sha256 = "1karc80mx15z435rm1jg3sqylnc58nxi15gqypcd1inkzzpqgfav";
+        libName = "regex_automata";
         authors = [
           "The Rust Project Developers"
           "Andrew Gallant <jamslam@gmail.com>"
@@ -5252,6 +5376,7 @@ rec {
         version = "0.8.2";
         edition = "2021";
         sha256 = "17rd2s8xbiyf6lb4aj2nfi44zqlj98g2ays8zzj2vfs743k79360";
+        libName = "regex_syntax";
         authors = [
           "The Rust Project Developers"
           "Andrew Gallant <jamslam@gmail.com>"
@@ -5268,6 +5393,7 @@ rec {
         version = "0.1.23";
         edition = "2015";
         sha256 = "0xnbk2bmyzshacjm2g1kd4zzv2y2az14bw3sjccq5qkpmsfvn9nn";
+        libName = "rustc_demangle";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -5350,6 +5476,7 @@ rec {
         edition = "2018";
         sha256 = "1d50kbaslrrd0374ivx15jg57f03y5xzil1wd2ajlvajzlkbzw53";
         procMacro = true;
+        libName = "seq_macro";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -5507,6 +5634,7 @@ rec {
         version = "1.4.1";
         edition = "2015";
         sha256 = "18crkkw5k82bvcx088xlf5g4n3772m24qhzgfan80nda7d3rn8nq";
+        libName = "signal_hook_registry";
         authors = [
           "Michal 'vorner' Vaner <vorner@vorner.cz>"
           "Masaki Hara <ackie.h.gmai@gmail.com>"
@@ -5745,6 +5873,7 @@ rec {
         version = "0.1.2";
         edition = "2018";
         sha256 = "1wscqj3s30qknda778wf7z99mknk65p0h9hhs658l4pvkfqw6v5z";
+        libName = "streaming_decompression";
         dependencies = [
           {
             name = "fallible-streaming-iterator";
@@ -5758,6 +5887,7 @@ rec {
         version = "0.1.9";
         edition = "2021";
         sha256 = "0845zdv8qb7zwqzglpqc0830i43xh3fb6vqms155wz85qfvk28ib";
+        libName = "streaming_iterator";
         authors = [
           "Steven Fackler <sfackler@gmail.com>"
         ];
@@ -5960,6 +6090,7 @@ rec {
         version = "0.1.5";
         edition = "2021";
         sha256 = "1gb974chm9aj8ifkyibylxkyb5an4bf5y8dxb18pqmck698gmdfg";
+        libName = "target_features";
         authors = [
           "Caleb Zulawski <caleb.zulawski@gmail.com>"
         ];
@@ -5987,6 +6118,7 @@ rec {
         edition = "2021";
         sha256 = "0w9ldp8fa574ilz4dn7y7scpcq66vdjy59qal8qdpwsh7faal3zs";
         procMacro = true;
+        libName = "thiserror_impl";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -6122,6 +6254,7 @@ rec {
         edition = "2021";
         sha256 = "0fwjy4vdx1h9pi4g2nml72wi0fr27b5m954p13ji9anyy8l1x2jv";
         procMacro = true;
+        libName = "tokio_macros";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -6147,6 +6280,7 @@ rec {
         version = "0.7.10";
         edition = "2021";
         sha256 = "058y6x4mf0fsqji9rfyb77qbfyc50y4pk2spqgj6xsyr693z66al";
+        libName = "tokio_util";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -6197,6 +6331,96 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" "io" "io-util" ];
       };
+      "tracing" = rec {
+        crateName = "tracing";
+        version = "0.1.40";
+        edition = "2018";
+        sha256 = "1vv48dac9zgj9650pg2b4d0j3w6f3x9gbggf43scq5hrlysklln3";
+        authors = [
+          "Eliza Weisman <eliza@buoyant.io>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tracing-attributes";
+            packageId = "tracing-attributes";
+            optional = true;
+          }
+          {
+            name = "tracing-core";
+            packageId = "tracing-core";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "attributes" = [ "tracing-attributes" ];
+          "default" = [ "std" "attributes" ];
+          "log" = [ "dep:log" ];
+          "log-always" = [ "log" ];
+          "std" = [ "tracing-core/std" ];
+          "tracing-attributes" = [ "dep:tracing-attributes" ];
+          "valuable" = [ "tracing-core/valuable" ];
+        };
+        resolvedDefaultFeatures = [ "attributes" "default" "std" "tracing-attributes" ];
+      };
+      "tracing-attributes" = rec {
+        crateName = "tracing-attributes";
+        version = "0.1.27";
+        edition = "2018";
+        sha256 = "1rvb5dn9z6d0xdj14r403z0af0bbaqhg02hq4jc97g5wds6lqw1l";
+        procMacro = true;
+        libName = "tracing_attributes";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+          "Eliza Weisman <eliza@buoyant.io>"
+          "David Barsky <dbarsky@amazon.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn 2.0.48";
+            usesDefaultFeatures = false;
+            features = [ "full" "parsing" "printing" "visit-mut" "clone-impls" "extra-traits" "proc-macro" ];
+          }
+        ];
+        features = { };
+      };
+      "tracing-core" = rec {
+        crateName = "tracing-core";
+        version = "0.1.32";
+        edition = "2018";
+        sha256 = "0m5aglin3cdwxpvbg6kz0r9r0k31j48n0kcfwsp6l49z26k3svf0";
+        libName = "tracing_core";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            optional = true;
+          }
+        ];
+        features = {
+          "default" = [ "std" "valuable/std" ];
+          "once_cell" = [ "dep:once_cell" ];
+          "std" = [ "once_cell" ];
+          "valuable" = [ "dep:valuable" ];
+        };
+        resolvedDefaultFeatures = [ "once_cell" "std" ];
+      };
       "typenum" = rec {
         crateName = "typenum";
         version = "1.17.0";
@@ -6217,6 +6441,7 @@ rec {
         version = "1.0.12";
         edition = "2018";
         sha256 = "0jzf1znfpb2gx8nr8mvmyqs1crnv79l57nxnbiszc7xf7ynbjm1k";
+        libName = "unicode_ident";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -6227,6 +6452,7 @@ rec {
         version = "0.1.11";
         edition = "2015";
         sha256 = "11ds4ydhg8g7l06rlmh712q41qsrd0j0h00n1jm74kww3kqk65z5";
+        libName = "unicode_width";
         authors = [
           "kwantam <kwantam@gmail.com>"
           "Manish Goregaokar <manishsmail@gmail.com>"
@@ -6271,6 +6497,7 @@ rec {
         version = "0.2.91";
         edition = "2018";
         sha256 = "0zwbb07ln4m5hh6axamc701nnj090nd66syxbf6bagzf189j9qf1";
+        libName = "wasm_bindgen";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -6302,6 +6529,7 @@ rec {
         version = "0.2.91";
         edition = "2018";
         sha256 = "02zpi9sjzhd8kfv1yj9m1bs4a41ik9ii5bc8hjf60arm1j8f3ry9";
+        libName = "wasm_bindgen_backend";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -6347,6 +6575,7 @@ rec {
         edition = "2018";
         sha256 = "1va6dilw9kcnvsg5043h5b9mwc5sgq0lyhj9fif2n62qsgigj2mk";
         procMacro = true;
+        libName = "wasm_bindgen_macro";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -6371,6 +6600,7 @@ rec {
         version = "0.2.91";
         edition = "2018";
         sha256 = "0rlyl3yzwbcnc691mvx78m1wbqf1qs52mlc3g88bh7ihwrdk4bv4";
+        libName = "wasm_bindgen_macro_support";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -6409,6 +6639,7 @@ rec {
         edition = "2018";
         links = "wasm_bindgen";
         sha256 = "0f4qmjv57ppwi4xpdxgcd77vz9vmvlrnybg8dj430hzhvk96n62g";
+        libName = "wasm_bindgen_shared";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -6496,6 +6727,7 @@ rec {
         version = "0.4.0";
         edition = "2015";
         sha256 = "1dmpa6mvcvzz16zg6d5vrfy4bxgg541wxrcip7cnshi06v38ffxc";
+        libName = "winapi_i686_pc_windows_gnu";
         authors = [
           "Peter Atashian <retep998@gmail.com>"
         ];
@@ -6506,6 +6738,7 @@ rec {
         version = "0.4.0";
         edition = "2015";
         sha256 = "0gqq64czqb64kskjryj8isp62m2sgvx25yyj3kpc2myh85w24bki";
+        libName = "winapi_x86_64_pc_windows_gnu";
         authors = [
           "Peter Atashian <retep998@gmail.com>"
         ];
@@ -7197,6 +7430,7 @@ rec {
         version = "0.52.0";
         edition = "2021";
         sha256 = "1nc3qv7sy24x0nlnb32f7alzpd6f72l4p24vl65vydbyil669ark";
+        libName = "windows_core";
         authors = [
           "Microsoft"
         ];
@@ -7214,6 +7448,7 @@ rec {
         version = "0.48.0";
         edition = "2018";
         sha256 = "1aan23v5gs7gya1lc46hqn9mdh8yph3fhxmhxlw36pn6pqc28zb7";
+        libName = "windows_sys";
         authors = [
           "Microsoft"
         ];
@@ -7507,6 +7742,7 @@ rec {
         version = "0.52.0";
         edition = "2021";
         sha256 = "0gd3v4ji88490zgb6b5mq5zgbvwv7zx1ibn8v3x83rwcdbryaar8";
+        libName = "windows_sys";
         authors = [
           "Microsoft"
         ];
@@ -7754,6 +7990,7 @@ rec {
         version = "0.48.5";
         edition = "2018";
         sha256 = "034ljxqshifs1lan89xwpcy1hp0lhdh4b5n0d2z4fwjx2piacbws";
+        libName = "windows_targets";
         authors = [
           "Microsoft"
         ];
@@ -7801,6 +8038,7 @@ rec {
         version = "0.52.0";
         edition = "2021";
         sha256 = "1kg7a27ynzw8zz3krdgy6w5gbqcji27j1sz4p7xk2j5j8082064a";
+        libName = "windows_targets";
         authors = [
           "Microsoft"
         ];
@@ -7988,6 +8226,7 @@ rec {
         version = "0.8.8";
         edition = "2018";
         sha256 = "0q9xl4kxibh61631lw9m7if7pkdvq3pp5ss52zdkxs6rirkhdgjk";
+        libName = "xxhash_rust";
         authors = [
           "Douman <douman@gmx.se>"
         ];
@@ -8036,6 +8275,7 @@ rec {
         edition = "2018";
         sha256 = "19nj11md42aijyqnfx8pa647fjzhz537xyc624rajwwfrn6b3qcw";
         procMacro = true;
+        libName = "zerocopy_derive";
         authors = [
           "Joshua Liebow-Feeser <joshlf@google.com>"
         ];
@@ -8110,6 +8350,7 @@ rec {
         version = "7.0.0";
         edition = "2018";
         sha256 = "0gpav2lcibrpmyslmjkcn3w0w64qif3jjljd2h8lr4p249s7qx23";
+        libName = "zstd_safe";
         authors = [
           "Alexandre Bury <alexandre.bury@gmail.com>"
         ];
@@ -8143,6 +8384,7 @@ rec {
         edition = "2018";
         links = "zstd";
         sha256 = "0mk6a2367swdi22zg03lcackpnvgq96d7120awd4i83lm2lfy5ly";
+        libName = "zstd_sys";
         authors = [
           "Alexandre Bury <alexandre.bury@gmail.com>"
         ];
@@ -8272,52 +8514,41 @@ rec {
                 testPostRun
               ]);
           in
-          pkgs.runCommand "run-tests-${testCrate.name}"
-            {
-              inherit testCrateFlags;
-              buildInputs = testInputs;
-            } ''
-            set -e
+          pkgs.stdenvNoCC.mkDerivation {
+            name = "run-tests-${testCrate.name}";
 
-            export RUST_BACKTRACE=1
+            inherit (crate) src;
 
-            # recreate a file hierarchy as when running tests with cargo
+            inherit testCrateFlags;
 
-            # the source for test data
-            # It's necessary to locate the source in $NIX_BUILD_TOP/source/
-            # instead of $NIX_BUILD_TOP/
-            # because we compiled those test binaries in the former and not the latter.
-            # So all paths will expect source tree to be there and not in the build top directly.
-            # For example: $NIX_BUILD_TOP := /build in general, if you ask yourself.
-            # NOTE: There could be edge cases if `crate.sourceRoot` does exist but
-            # it's very hard to reason about them.
-            # Open a bug if you run into this!
-            mkdir -p source/
-            cd source/
+            buildInputs = testInputs;
 
-            ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${crate.src}
+            buildPhase = ''
+              set -e
+              export RUST_BACKTRACE=1
 
-            # build outputs
-            testRoot=target/debug
-            mkdir -p $testRoot
+              # build outputs
+              testRoot=target/debug
+              mkdir -p $testRoot
 
-            # executables of the crate
-            # we copy to prevent std::env::current_exe() to resolve to a store location
-            for i in ${crate}/bin/*; do
-              cp "$i" "$testRoot"
-            done
-            chmod +w -R .
+              # executables of the crate
+              # we copy to prevent std::env::current_exe() to resolve to a store location
+              for i in ${crate}/bin/*; do
+                cp "$i" "$testRoot"
+              done
+              chmod +w -R .
 
-            # test harness executables are suffixed with a hash, like cargo does
-            # this allows to prevent name collision with the main
-            # executables of the crate
-            hash=$(basename $out)
-            for file in ${drv}/tests/*; do
-              f=$testRoot/$(basename $file)-$hash
-              cp $file $f
-              ${testCommand}
-            done
-          '';
+              # test harness executables are suffixed with a hash, like cargo does
+              # this allows to prevent name collision with the main
+              # executables of the crate
+              hash=$(basename $out)
+              for file in ${drv}/tests/*; do
+                f=$testRoot/$(basename $file)-$hash
+                cp $file $f
+                ${testCommand}
+              done
+            '';
+          };
       in
       pkgs.runCommand "${crate.name}-linked"
         {
diff --git a/tvix/tools/weave/default.nix b/tvix/tools/weave/default.nix
index 8c5e1be928d1..3d979b6d3327 100644
--- a/tvix/tools/weave/default.nix
+++ b/tvix/tools/weave/default.nix
@@ -6,4 +6,6 @@
       src = depot.tvix.utils.filterRustCrateSrc { root = prev.src.origSrc; };
     };
   };
-}).rootCrate.build
+}).rootCrate.build.overrideAttrs {
+  meta.ci.extraSteps.crate2nix-check = depot.tvix.utils.mkCrate2nixCheck ./Cargo.nix;
+}
diff --git a/tvix/tracing/Cargo.toml b/tvix/tracing/Cargo.toml
index 3f3e5330c621..acd968eb231f 100644
--- a/tvix/tracing/Cargo.toml
+++ b/tvix/tracing/Cargo.toml
@@ -4,27 +4,27 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-lazy_static = "1.4.0"
-tracing = { version = "0.1.40", features = ["max_level_trace", "release_max_level_debug"] }
-tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
-indicatif = "0.17.8"
-tracing-indicatif = "0.3.6"
-tokio = { version = "1.32.0" , features = ["sync", "rt"] }
-thiserror = "1.0.38"
+lazy_static = { workspace = true }
+tracing = { workspace = true, features = ["max_level_trace", "release_max_level_debug"] }
+tracing-subscriber = { workspace = true, features = ["env-filter"] }
+indicatif = { workspace = true }
+tracing-indicatif = { workspace = true }
+tokio = { workspace = true, features = ["sync", "rt"] }
+thiserror = { workspace = true }
 
-tracing-opentelemetry = { version = "0.25.0", optional = true }
-opentelemetry = { version = "0.24.0", optional = true }
-opentelemetry-otlp = { version = "0.17.0", optional = true }
-opentelemetry_sdk = { version = "0.24.1", features = ["rt-tokio"], optional = true }
-tracing-tracy = { version = "0.11.0", features = ["flush-on-exit"], optional = true }
-opentelemetry-http = { version = "0.13.0", optional = true }
+tracing-opentelemetry = { workspace = true, optional = true }
+opentelemetry = { workspace = true, optional = true }
+opentelemetry-otlp = { workspace = true, optional = true }
+opentelemetry_sdk = { workspace = true, features = ["rt-tokio"], optional = true }
+tracing-tracy = { workspace = true, features = ["flush-on-exit"], optional = true }
+opentelemetry-http = { workspace = true, optional = true }
 
-tonic = { version = "0.12.0", optional = true }
-http  = { version = "1.1.0", optional = true }
+tonic = { workspace = true, optional = true }
+http  = { workspace = true, optional = true }
 
-reqwest-tracing = { version = "0.5.0", default-features = false, optional = true }
+reqwest-tracing = { workspace = true, optional = true }
 
-axum = { version = "0.7.5", optional = true }
+axum = { workspace = true, optional = true }
 
 [features]
 default = []
diff --git a/tvix/tracing/default.nix b/tvix/tracing/default.nix
index ef1985cb47fb..b519d0ffc0b3 100644
--- a/tvix/tracing/default.nix
+++ b/tvix/tracing/default.nix
@@ -4,8 +4,8 @@
   runTests = true;
 }).overrideAttrs (old: rec {
   meta.ci.targets = lib.filter (x: lib.hasPrefix "with-features" x || x == "no-features") (lib.attrNames passthru);
-  passthru = depot.tvix.utils.mkFeaturePowerset {
+  passthru = old.passthru // (depot.tvix.utils.mkFeaturePowerset {
     inherit (old) crateName;
     features = [ "otlp" "tracy" "tonic" "reqwest" "axum" ];
-  };
+  });
 })
diff --git a/tvix/utils.nix b/tvix/utils.nix
index dc40df5007ea..6fa99e63eb05 100644
--- a/tvix/utils.nix
+++ b/tvix/utils.nix
@@ -1,4 +1,4 @@
-{ lib, depot, ... }:
+{ pkgs, lib, depot, ... }:
 
 {
   mkFeaturePowerset = { crateName, features, override ? { } }:
@@ -34,7 +34,7 @@
       fileset = lib.fileset.intersection
         (lib.fileset.fromSource root) # We build our final fileset from the original src
         (lib.fileset.unions ([
-          (root + "/src")
+          (lib.fileset.maybeMissing (root + "/src")) # src may be missing if the crate just has tests for example
           (lib.fileset.fileFilter (f: f.hasExt "rs") root)
         ] ++ lib.optionals cargoSupport [
           (lib.fileset.fileFilter (f: f.name == "Cargo.toml") root)
@@ -62,6 +62,15 @@
           extraFileset = root + "/testdata";
         };
       };
+
+      nix-compat-derive = prev: {
+        src = depot.tvix.utils.filterRustCrateSrc { root = prev.src.origSrc; };
+      };
+
+      nix-compat-derive-tests = prev: {
+        src = depot.tvix.utils.filterRustCrateSrc { root = prev.src.origSrc; };
+      };
+
       tvix-build = prev: {
         src = depot.tvix.utils.filterRustCrateSrc rec {
           root = prev.src.origSrc;
@@ -125,4 +134,27 @@
         src = depot.tvix.utils.filterRustCrateSrc { root = prev.src.origSrc; };
       };
     };
+
+  # This creates an extraStep in CI to check whether the Cargo.nix file is up-to-date.
+  mkCrate2nixCheck =
+    path: # The path to the Cargo.nix to be checked.
+    let
+      relCrateRoot = lib.removePrefix "./" (builtins.dirOf (lib.path.removePrefix depot.path.origSrc path));
+    in
+    {
+      label = "crate2nix check for ${relCrateRoot}";
+      needsOutput = true;
+      alwaysRun = true;
+      command = pkgs.writeShellScript "crate2nix-check-for-${lib.replaceStrings [ "/" ] ["-"] relCrateRoot}" ''
+        (cd $(git rev-parse --show-toplevel)/${relCrateRoot} &&
+          ${depot.tools.crate2nix-generate}/bin/crate2nix-generate &&
+          if [[ -n "$(git status --porcelain -unormal Cargo.nix)" ]]; then
+              echo "----------------------------------------------------------------------------------------------------"
+              echo "Cargo.nix needs to be updated, run 'mg run //tools/crate2nix-generate' in ${relCrateRoot}"
+              echo "----------------------------------------------------------------------------------------------------"
+              exit 1
+          fi
+        )
+      '';
+    };
 }
diff --git a/tvix/verify-lang-tests/default.nix b/tvix/verify-lang-tests/default.nix
index 29d963065ea3..e6de1c1f455c 100644
--- a/tvix/verify-lang-tests/default.nix
+++ b/tvix/verify-lang-tests/default.nix
@@ -11,7 +11,7 @@ let
 
   inherit (pkgs.nixVersions) nix_2_3;
   # The latest Nix version we've verified to work for our testing suite.
-  nix_latest_verified = pkgs.nixVersions.nix_2_23;
+  nix_latest_verified = pkgs.nixVersions.nix_2_24;
 
   parseTest = dir: baseName:
     let
diff --git a/users/Profpatsch/.gitignore b/users/Profpatsch/.gitignore
index c33954f53a06..95941cd52dbe 100644
--- a/users/Profpatsch/.gitignore
+++ b/users/Profpatsch/.gitignore
@@ -1 +1,2 @@
 dist-newstyle/
+result-*
diff --git a/users/Profpatsch/README.md b/users/Profpatsch/README.md
index 5bb74cd7580f..0f2147e9afc1 100644
--- a/users/Profpatsch/README.md
+++ b/users/Profpatsch/README.md
@@ -2,7 +2,7 @@
 
 Welcome, Welcome.
 
-Welcome to my user dir, where we optimize f*** around, in order to optimize finding out.
+Welcome to my user dir, where we optimize f\*\*\*ing around, in order to optimize finding out.
 
 ![fafo graph](./fafo.jpg)
 
diff --git a/users/Profpatsch/git-db/default.nix b/users/Profpatsch/git-db/default.nix
deleted file mode 100644
index ad5d927677bf..000000000000
--- a/users/Profpatsch/git-db/default.nix
+++ /dev/null
@@ -1,10 +0,0 @@
-{ depot, pkgs, lib, ... }:
-
-depot.nix.writers.rustSimple
-{
-  name = "git-db";
-  dependencies = [
-    depot.third_party.rust-crates.git2
-  ];
-}
-  (builtins.readFile ./git-db.rs)
diff --git a/users/Profpatsch/git-db/git-db.rs b/users/Profpatsch/git-db/git-db.rs
deleted file mode 100644
index c8019bf03661..000000000000
--- a/users/Profpatsch/git-db/git-db.rs
+++ /dev/null
@@ -1,90 +0,0 @@
-extern crate git2;
-use std::os::unix::ffi::OsStrExt;
-use std::path::PathBuf;
-
-const DEFAULT_BRANCH: &str = "refs/heads/main";
-
-fn main() {
-    let git_db_dir = std::env::var_os("GIT_DB_DIR").expect("set GIT_DB_DIR");
-    let git_db = PathBuf::from(git_db_dir).join("git");
-
-    std::fs::create_dir_all(&git_db).unwrap();
-
-    let repo = git2::Repository::init_opts(
-        &git_db,
-        git2::RepositoryInitOptions::new()
-            .bare(true)
-            .mkpath(true)
-            .description("git-db database")
-            .initial_head(DEFAULT_BRANCH),
-    )
-    .expect(&format!(
-        "unable to create or open bare git repo at {}",
-        &git_db.display()
-    ));
-
-    let mut index = repo.index().expect("cannot get the git index file");
-    eprintln!("{:#?}", index.version());
-    index.clear().expect("could not clean the index");
-
-    let now = std::time::SystemTime::now()
-        .duration_since(std::time::SystemTime::UNIX_EPOCH)
-        .expect("unable to get system time");
-
-    let now_git_time = git2::IndexTime::new(
-        now.as_secs() as i32, // lol
-        u32::from(now.subsec_nanos()),
-    );
-
-    let data = "hi, it’s me".as_bytes();
-
-    index
-        .add_frombuffer(
-            &git2::IndexEntry {
-            mtime: now_git_time,
-            ctime: now_git_time,
-            // don’t make sense
-            dev: 0,
-            ino: 0,
-            mode: /*libc::S_ISREG*/ 0b1000 << (3+9) | /* read write for owner */ 0o644,
-            uid: 0,
-            gid: 0,
-            file_size: data.len() as u32, // lol again
-            id: git2::Oid::zero(),
-            flags: 0,
-            flags_extended: 0,
-            path: "hi.txt".as_bytes().to_owned(),
-        },
-            data,
-        )
-        .expect("could not add data to index");
-
-    let oid = index.write_tree().expect("could not write index tree");
-
-    let to_add_tree = repo
-        .find_tree(oid)
-        .expect("we just created this tree, where did it go?");
-
-    let parent_commits = match repo.find_reference(DEFAULT_BRANCH) {
-        Ok(ref_) => vec![ref_.peel_to_commit().expect(&format!(
-            "reference {} does not point to a commit",
-            DEFAULT_BRANCH
-        ))],
-        Err(err) => match err.code() {
-            // no commit exists yet
-            git2::ErrorCode::NotFound => vec![],
-            _ => panic!("could not read latest commit from {}", DEFAULT_BRANCH),
-        },
-    };
-    repo.commit(
-        Some(DEFAULT_BRANCH),
-        &git2::Signature::now("Mr. Authorboy", "author@example.com").unwrap(),
-        &git2::Signature::now("Mr. Commiterboy", "committer@example.com").unwrap(),
-        "This is my first commit!\n\
-         \n\
-         I wonder if it supports extended commit descriptions?\n",
-        &to_add_tree,
-        &parent_commits.iter().collect::<Vec<_>>()[..],
-    )
-    .expect("could not commit the index we just wrote");
-}
diff --git a/users/Profpatsch/lyric.nix b/users/Profpatsch/lyric.nix
new file mode 100644
index 000000000000..b3914d195e1f
--- /dev/null
+++ b/users/Profpatsch/lyric.nix
@@ -0,0 +1,54 @@
+# Display lyrics for the given search string;
+# search string can contain a substring of band name, album name, song title
+#
+# Use the database dump from https://lrclib.net/db-dumps and place it in ~/.cache/lyric/lrclib-db-dump.sqlite3
+
+{ depot, pkgs, lib, ... }:
+
+let
+  bins = depot.nix.getBins pkgs.sqlite-utils [ "sqlite-utils" ]
+    // depot.nix.getBins pkgs.jq [ "jq" ];
+
+in
+depot.nix.writeExecline "lyric" { readNArgs = 1; } [
+  "backtick"
+  "-E"
+  "cache"
+  [ depot.users.Profpatsch.xdg-cache-home ]
+  "pipeline"
+  [
+    bins.sqlite-utils
+    "query"
+    "\${cache}/lyric/lrclib-db-dump.sqlite3"
+    ''
+      select
+          synced_lyrics,
+          has_synced_lyrics,
+          plain_lyrics
+      from
+          tracks_fts(:searchstring) tf
+          join tracks t on t.rowid = tf.rowid
+          join lyrics l on t.rowid = l.track_id
+      order by
+          t.id
+      limit
+          1
+    ''
+    "--param"
+    "searchstring"
+    "$1"
+  ]
+  bins.jq
+  "-r"
+  ''
+    if .[0] == null
+    then ""
+    else
+      .[0]
+        | if .has_synced_lyrics == 1
+          then .synced_lyrics
+          else .plain_lyrics
+          end
+    end
+  ''
+]
diff --git a/users/Profpatsch/my-prelude/src/Postgres/MonadPostgres.hs b/users/Profpatsch/my-prelude/src/Postgres/MonadPostgres.hs
index 6ccc45faad0a..87928678a052 100644
--- a/users/Profpatsch/my-prelude/src/Postgres/MonadPostgres.hs
+++ b/users/Profpatsch/my-prelude/src/Postgres/MonadPostgres.hs
@@ -34,7 +34,7 @@ import Database.PostgreSQL.Simple qualified as Postgres
 import Database.PostgreSQL.Simple.FromRow qualified as PG
 import Database.PostgreSQL.Simple.ToField (ToField)
 import Database.PostgreSQL.Simple.ToRow (ToRow (toRow))
-import Database.PostgreSQL.Simple.Types (Query (..))
+import Database.PostgreSQL.Simple.Types (PGArray (PGArray), Query (..))
 import GHC.IO.Handle (Handle)
 import GHC.Records (getField)
 import Label
@@ -930,6 +930,70 @@ withEvent span start end act = do
     )
   pure res
 
+unzipPGArray ::
+  forall l1 t1 l2 t2 r.
+  ( HasField l1 r t1,
+    HasField l2 r t2
+  ) =>
+  [r] ->
+  (PGArray t1, PGArray t2)
+{-# INLINEABLE unzipPGArray #-}
+unzipPGArray xs =
+  ( PGArray $ getField @l1 <$> xs,
+    PGArray $ getField @l2 <$> xs
+  )
+
+unzip3PGArray ::
+  forall l1 t1 l2 t2 l3 t3 r.
+  ( HasField l1 r t1,
+    HasField l2 r t2,
+    HasField l3 r t3
+  ) =>
+  [r] ->
+  (PGArray t1, PGArray t2, PGArray t3)
+{-# INLINEABLE unzip3PGArray #-}
+unzip3PGArray xs =
+  ( PGArray $ getField @l1 <$> xs,
+    PGArray $ getField @l2 <$> xs,
+    PGArray $ getField @l3 <$> xs
+  )
+
+unzip4PGArray ::
+  forall l1 t1 l2 t2 l3 t3 l4 t4 r.
+  ( HasField l1 r t1,
+    HasField l2 r t2,
+    HasField l3 r t3,
+    HasField l4 r t4
+  ) =>
+  [r] ->
+  (PGArray t1, PGArray t2, PGArray t3, PGArray t4)
+{-# INLINEABLE unzip4PGArray #-}
+unzip4PGArray xs =
+  ( PGArray $ getField @l1 <$> xs,
+    PGArray $ getField @l2 <$> xs,
+    PGArray $ getField @l3 <$> xs,
+    PGArray $ getField @l4 <$> xs
+  )
+
+unzip5PGArray ::
+  forall l1 t1 l2 t2 l3 t3 l4 t4 l5 t5 r.
+  ( HasField l1 r t1,
+    HasField l2 r t2,
+    HasField l3 r t3,
+    HasField l4 r t4,
+    HasField l5 r t5
+  ) =>
+  [r] ->
+  (PGArray t1, PGArray t2, PGArray t3, PGArray t4, PGArray t5)
+{-# INLINEABLE unzip5PGArray #-}
+unzip5PGArray xs =
+  ( PGArray $ getField @l1 <$> xs,
+    PGArray $ getField @l2 <$> xs,
+    PGArray $ getField @l3 <$> xs,
+    PGArray $ getField @l4 <$> xs,
+    PGArray $ getField @l5 <$> xs
+  )
+
 instance (ToField t1) => ToRow (Label l1 t1) where
   toRow t2 = toRow $ PG.Only $ getField @l1 t2
 
diff --git a/users/Profpatsch/my-prelude/src/Pretty.hs b/users/Profpatsch/my-prelude/src/Pretty.hs
index d9d4ce132b11..6711ea951a48 100644
--- a/users/Profpatsch/my-prelude/src/Pretty.hs
+++ b/users/Profpatsch/my-prelude/src/Pretty.hs
@@ -8,6 +8,7 @@ module Pretty
     printShowedStringPretty,
     -- constructors hidden
     prettyErrs,
+    prettyErrsNoColor,
     message,
     messageString,
     pretty,
@@ -19,6 +20,7 @@ where
 import Data.Aeson qualified as Json
 import Data.Aeson.Encode.Pretty qualified as Aeson.Pretty
 import Data.List qualified as List
+import Data.String (IsString (fromString))
 import Data.Text.Lazy.Builder qualified as Text.Builder
 import Language.Haskell.HsColour
   ( Output (TTYg),
@@ -62,7 +64,6 @@ showPrettyJson val =
     & toStrict
 
 -- | Display a list of 'Err's as a colored error message
--- and abort the test.
 prettyErrs :: [Err] -> String
 prettyErrs errs = res
   where
@@ -74,6 +75,15 @@ prettyErrs errs = res
     prettyShowString :: String -> String
     prettyShowString = hscolour' . nicify
 
+-- | Display a list of 'Err's as a plain-colored error message
+prettyErrsNoColor :: [Err] -> String
+prettyErrsNoColor errs = res
+  where
+    res = List.intercalate "\n" $ map one errs
+    one = \case
+      ErrMsg s -> s
+      ErrPrettyString s -> nicify s
+
 -- | Small DSL for pretty-printing errors
 data Err
   = -- | Message to display in the error
@@ -81,6 +91,9 @@ data Err
   | -- | Pretty print a String that was produced by 'show'
     ErrPrettyString String
 
+instance IsString Err where
+  fromString s = ErrMsg s
+
 -- | Plain message to display, as 'Text'
 message :: Text -> Err
 message = ErrMsg . textToString
diff --git a/users/Profpatsch/nix-home/default.nix b/users/Profpatsch/nix-home/default.nix
index ee154c549a6b..72c77122fc9b 100644
--- a/users/Profpatsch/nix-home/default.nix
+++ b/users/Profpatsch/nix-home/default.nix
@@ -158,7 +158,10 @@ let
               name = "scripts/lw";
               path = depot.users.Profpatsch.lorri-wait-for-eval;
             }
-
+            {
+              name = "scripts/lyric";
+              path = depot.users.Profpatsch.lyric;
+            }
           ]
           ++
           (lib.pipe depot.users.Profpatsch.aliases [
diff --git a/users/Profpatsch/openlab-tools/src/OpenlabTools.hs b/users/Profpatsch/openlab-tools/src/OpenlabTools.hs
index 16f1b626ac10..7ba52c30229d 100644
--- a/users/Profpatsch/openlab-tools/src/OpenlabTools.hs
+++ b/users/Profpatsch/openlab-tools/src/OpenlabTools.hs
@@ -389,7 +389,7 @@ httpJson opts span parser req = do
             | statusCode == 200,
               Nothing <- contentType ->
                 Left [fmt|Server returned a body with unspecified content type|]
-            | code <- statusCode -> Left [fmt|Server returned an non-200 error code, code {code}: {resp & showPretty}|]
+            | code <- statusCode -> Left $ singleError [fmt|Server returned an non-200 error code, code {code}: {[pretty resp] & prettyErrsNoColor}|]
       )
     >>= assertM
       span
diff --git a/users/Profpatsch/whatcd-resolver/.gitignore b/users/Profpatsch/whatcd-resolver/.gitignore
new file mode 100644
index 000000000000..f9c4bdf8b808
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/.gitignore
@@ -0,0 +1 @@
+/.ninja/
diff --git a/users/Profpatsch/whatcd-resolver/services/jaeger/run b/users/Profpatsch/whatcd-resolver/services/jaeger/run
index 41332f8bb61b..7800d88287d5 100755
--- a/users/Profpatsch/whatcd-resolver/services/jaeger/run
+++ b/users/Profpatsch/whatcd-resolver/services/jaeger/run
@@ -1,3 +1,3 @@
 #!/usr/bin/env execlineb
 importas -i DEPOT_ROOT DEPOT_ROOT
-nix-run { $DEPOT_ROOT -A users.Profpatsch.jaeger -kK --builders '' }
+nix-run { $DEPOT_ROOT -A users.Profpatsch.jaeger -kK }
diff --git a/users/Profpatsch/whatcd-resolver/src/AppT.hs b/users/Profpatsch/whatcd-resolver/src/AppT.hs
index 6a8637bb1660..8550d4aa713e 100644
--- a/users/Profpatsch/whatcd-resolver/src/AppT.hs
+++ b/users/Profpatsch/whatcd-resolver/src/AppT.hs
@@ -9,8 +9,11 @@ import Data.Error.Tree
 import Data.HashMap.Strict (HashMap)
 import Data.HashMap.Strict qualified as HashMap
 import Data.Pool (Pool)
+import Data.String (IsString (fromString))
 import Data.Text qualified as Text
 import Database.PostgreSQL.Simple qualified as Postgres
+import FieldParser (FieldParser)
+import FieldParser qualified as Field
 import GHC.Stack qualified
 import Json.Enc
 import Json.Enc qualified as Enc
@@ -20,6 +23,7 @@ import OpenTelemetry.Trace.Core qualified as Otel hiding (inSpan, inSpan')
 import OpenTelemetry.Trace.Monad qualified as Otel
 import PossehlAnalyticsPrelude
 import Postgres.MonadPostgres
+import Pretty qualified
 import System.IO qualified as IO
 import UnliftIO
 import Prelude hiding (span)
@@ -40,13 +44,17 @@ data Context = Context
 newtype AppT m a = AppT {unAppT :: ReaderT Context m a}
   deriving newtype (Functor, Applicative, Monad, MonadIO, MonadUnliftIO, MonadThrow)
 
-newtype AppException = AppException Text
+data AppException
+  = AppExceptionTree ErrorTree
+  | AppExceptionPretty [Pretty.Err]
   deriving anyclass (Exception)
 
-instance Show AppException where
-  showsPrec _ (AppException t) = ("AppException: " ++) . (textToString t ++)
+instance IsString AppException where
+  fromString s = AppExceptionTree (fromString s)
 
--- *  Logging & Opentelemetry
+instance Show AppException where
+  showsPrec _ (AppExceptionTree t) = ("AppException: " ++) . ((textToString $ prettyErrorTree t) ++)
+  showsPrec _ (AppExceptionPretty t) = ("AppException: " ++) . ((Pretty.prettyErrsNoColor t) ++)
 
 instance (MonadIO m) => MonadLogger (AppT m) where
   monadLoggerLog loc src lvl msg = liftIO $ Logger.defaultOutput IO.stderr loc src lvl (Logger.toLogStr msg)
@@ -88,47 +96,58 @@ addEventSimple span name =
 jsonAttribute :: Enc -> Otel.Attribute
 jsonAttribute e = e & Enc.encToTextPretty & Otel.toAttribute
 
-orThrowAppErrorNewSpan :: (MonadThrow m, MonadOtel m) => Text -> Either ErrorTree a -> m a
+parseOrThrow :: (MonadThrow m, MonadIO m) => Otel.Span -> FieldParser from to -> from -> m to
+parseOrThrow span fp f =
+  f & Field.runFieldParser fp & \case
+    Left err -> appThrow span (AppExceptionTree $ singleError err)
+    Right a -> pure a
+
+orThrowAppErrorNewSpan :: (MonadThrow m, MonadOtel m) => Text -> Either AppException a -> m a
 orThrowAppErrorNewSpan msg = \case
-  Left err -> appThrowTreeNewSpan msg err
+  Left err -> appThrowNewSpan msg err
   Right a -> pure a
 
-appThrowTreeNewSpan :: (MonadThrow m, MonadOtel m) => Text -> ErrorTree -> m a
-appThrowTreeNewSpan spanName exc = inSpan' spanName $ \span -> do
-  let msg = prettyErrorTree exc
+appThrowNewSpan :: (MonadThrow m, MonadOtel m) => Text -> AppException -> m a
+appThrowNewSpan spanName exc = inSpan' spanName $ \span -> do
+  let msg = case exc of
+        AppExceptionTree e -> prettyErrorTree e
+        AppExceptionPretty p -> Pretty.prettyErrsNoColor p & stringToText
   recordException
     span
     ( T2
         (label @"type_" "AppException")
         (label @"message" msg)
     )
-  throwM $ AppException msg
+  throwM $ exc
 
-appThrowTree :: (MonadThrow m, MonadIO m) => Otel.Span -> ErrorTree -> m a
-appThrowTree span exc = do
-  let msg = prettyErrorTree exc
+appThrow :: (MonadThrow m, MonadIO m) => Otel.Span -> AppException -> m a
+appThrow span exc = do
+  let msg = case exc of
+        AppExceptionTree e -> prettyErrorTree e
+        AppExceptionPretty p -> Pretty.prettyErrsNoColor p & stringToText
   recordException
     span
     ( T2
         (label @"type_" "AppException")
         (label @"message" msg)
     )
-  throwM $ AppException msg
+  throwM $ exc
 
-orAppThrowTree :: (MonadThrow m, MonadIO m) => Otel.Span -> Either ErrorTree a -> m a
-orAppThrowTree span = \case
-  Left err -> appThrowTree span err
+orAppThrow :: (MonadThrow m, MonadIO m) => Otel.Span -> Either AppException a -> m a
+orAppThrow span = \case
+  Left err -> appThrow span err
   Right a -> pure a
 
-assertM :: (MonadThrow f, MonadIO f) => Otel.Span -> (t -> Either ErrorTree a) -> t -> f a
+-- | If action returns a Left, throw an AppException
+assertM :: (MonadThrow f, MonadIO f) => Otel.Span -> (t -> Either AppException a) -> t -> f a
 assertM span f v = case f v of
   Right a -> pure a
-  Left err -> appThrowTree span err
+  Left err -> appThrow span err
 
-assertMNewSpan :: (MonadThrow f, MonadOtel f) => Text -> (t -> Either ErrorTree a) -> t -> f a
+assertMNewSpan :: (MonadThrow f, MonadOtel f) => Text -> (t -> Either AppException a) -> t -> f a
 assertMNewSpan spanName f v = case f v of
   Right a -> pure a
-  Left err -> appThrowTreeNewSpan spanName err
+  Left err -> appThrowNewSpan spanName err
 
 -- | A specialized variant of @addEvent@ that records attributes conforming to
 -- the OpenTelemetry specification's
diff --git a/users/Profpatsch/whatcd-resolver/src/Http.hs b/users/Profpatsch/whatcd-resolver/src/Http.hs
index f0f635e7d837..14ce191d520e 100644
--- a/users/Profpatsch/whatcd-resolver/src/Http.hs
+++ b/users/Profpatsch/whatcd-resolver/src/Http.hs
@@ -91,13 +91,13 @@ httpJson opts parser req = inSpan' "HTTP Request (JSON)" $ \span -> do
             | statusCode == 200,
               Nothing <- contentType ->
                 Left [fmt|Server returned a body with unspecified content type|]
-            | code <- statusCode -> Left [fmt|Server returned an non-200 error code, code {code}: {resp & showPretty}|]
+            | code <- statusCode -> Left $ AppExceptionPretty [[fmt|Server returned an non-200 error code, code {code}:|], pretty resp]
       )
     >>= assertM
       span
       ( \body ->
           Json.parseStrict parser body
-            & first (Json.parseErrorTree "could not parse redacted response")
+            & first (AppExceptionTree . Json.parseErrorTree "could not parse HTTP response")
       )
 
 doRequestJson ::
diff --git a/users/Profpatsch/whatcd-resolver/src/Redacted.hs b/users/Profpatsch/whatcd-resolver/src/Redacted.hs
index 5b6751346b18..7bf9e8c2ce27 100644
--- a/users/Profpatsch/whatcd-resolver/src/Redacted.hs
+++ b/users/Profpatsch/whatcd-resolver/src/Redacted.hs
@@ -275,20 +275,23 @@ redactedSearchAndInsert extraArguments = do
             , torrent_id
             , full_json_result)
           |]
-        ( [ ( dat.torrentGroupIdPg :: Int,
-              group.torrentId :: Int,
-              group.fullJsonResult :: Json.Value
-            )
+        ( [ T3
+              (getLabel @"torrentGroupIdPg" dat)
+              (getLabel @"torrentId" group)
+              (getLabel @"fullJsonResult" group)
             | dat <- dats,
               group <- dat.torrents
           ]
             & unzip3PGArray
+              @"torrentGroupIdPg"
+              @Int
+              @"torrentId"
+              @Int
+              @"fullJsonResult"
+              @Json.Value
         )
       pure ()
 
-unzip3PGArray :: [(a1, a2, a3)] -> (PGArray a1, PGArray a2, PGArray a3)
-unzip3PGArray xs = xs & unzip3 & \(a, b, c) -> (PGArray a, PGArray b, PGArray c)
-
 redactedGetTorrentFileAndInsert ::
   ( HasField "torrentId" r Int,
     MonadPostgres m,
@@ -357,7 +360,7 @@ assertOneUpdated ::
   m ()
 assertOneUpdated span name x = case x.numberOfRowsAffected of
   1 -> pure ()
-  n -> appThrowTree span ([fmt|{name :: Text}: Expected to update exactly one row, but updated {n :: Natural} row(s)|])
+  n -> appThrow span ([fmt|{name :: Text}: Expected to update exactly one row, but updated {n :: Natural} row(s)|])
 
 data TorrentData transmissionInfo = TorrentData
   { groupId :: Int,
@@ -365,7 +368,8 @@ data TorrentData transmissionInfo = TorrentData
     seedingWeight :: Int,
     artists :: [T2 "artistId" Int "artistName" Text],
     torrentGroupJson :: TorrentGroupJson,
-    torrentStatus :: TorrentStatus transmissionInfo
+    torrentStatus :: TorrentStatus transmissionInfo,
+    torrentFormat :: Text
   }
 
 data TorrentGroupJson = TorrentGroupJson
@@ -413,7 +417,11 @@ getBestTorrents opts = do
           -- filter by artist id
           AND
           (?::bool OR (to_jsonb(?::int) <@ (jsonb_path_query_array(full_json_result, '$.artists[*].id'))))
-        ORDER BY torrent_group, seeding_weight DESC
+        ORDER BY
+          torrent_group,
+          -- prefer torrents which we already downloaded
+          torrent_file,
+          seeding_weight DESC
       )
       SELECT
         tg.group_id,
@@ -423,7 +431,8 @@ getBestTorrents opts = do
         tg.full_json_result->>'groupName' AS group_name,
         tg.full_json_result->>'groupYear' AS group_year,
         t.torrent_file IS NOT NULL AS has_torrent_file,
-        t.transmission_torrent_hash
+        t.transmission_torrent_hash,
+        t.full_json_result->>'encoding' AS torrent_format
       FROM filtered_torrents f
       JOIN redacted.torrents t ON t.id = f.id
       JOIN redacted.torrent_groups tg ON tg.id = t.torrent_group
@@ -452,8 +461,8 @@ getBestTorrents opts = do
           groupYear <- Dec.textParse Field.decimalNatural
           pure $ TorrentGroupJson {..}
         hasTorrentFile <- Dec.fromField @Bool
-        transmissionTorrentHash <-
-          Dec.fromField @(Maybe Text)
+        transmissionTorrentHash <- Dec.fromField @(Maybe Text)
+        torrentFormat <- Dec.text
         pure $
           TorrentData
             { torrentStatus =
@@ -463,6 +472,13 @@ getBestTorrents opts = do
                   | Just hash <- transmissionTorrentHash ->
                       InTransmission $
                         T2 (label @"torrentHash" hash) (label @"transmissionInfo" ()),
+              torrentFormat = case torrentFormat of
+                "Lossless" -> "flac"
+                "V0 (VBR)" -> "V0"
+                "V2 (VBR)" -> "V2"
+                "320" -> "320"
+                "256" -> "256"
+                o -> o,
               ..
             }
     )
@@ -513,7 +529,7 @@ httpTorrent span req =
             | statusCode == 200,
               Nothing <- contentType ->
                 Left [fmt|Redacted returned a body with unspecified content type|]
-            | code <- statusCode -> Left [fmt|Redacted returned an non-200 error code, code {code}: {resp & showPretty}|]
+            | code <- statusCode -> Left $ AppExceptionPretty [[fmt|Redacted returned an non-200 error code, code {code}|], pretty resp]
       )
 
 redactedApiRequestJson ::
diff --git a/users/Profpatsch/whatcd-resolver/src/Transmission.hs b/users/Profpatsch/whatcd-resolver/src/Transmission.hs
index acbab001621c..3238780af70f 100644
--- a/users/Profpatsch/whatcd-resolver/src/Transmission.hs
+++ b/users/Profpatsch/whatcd-resolver/src/Transmission.hs
@@ -49,18 +49,20 @@ scientificPercentage =
               | otherwise -> Right $ Percentage $ ceiling (f * 100)
         )
 
--- | Fetch the current status from transmission, and remove the tranmission hash from our database
--- iff it does not exist in transmission anymore
+-- | Fetch the current status from transmission,
+--  and remove the transmission hash and torrent file from our database iff it does not exist in transmission anymore
 getAndUpdateTransmissionTorrentsStatus ::
   ( MonadTransmission m,
     MonadThrow m,
     MonadLogger m,
     MonadPostgres m,
-    MonadOtel m
+    MonadOtel m,
+    HasField "groupId" info Int,
+    HasField "torrentId" info Int
   ) =>
-  Map (Label "torrentHash" Text) () ->
-  (Transaction m (Map (Label "torrentHash" Text) (Label "percentDone" Percentage)))
-getAndUpdateTransmissionTorrentsStatus knownTorrents = do
+  Map (Label "torrentHash" Text) info ->
+  (Transaction m (Label "knownTorrentsStale" Bool, (Map (Label "torrentHash" Text) (Label "percentDone" Percentage))))
+getAndUpdateTransmissionTorrentsStatus knownTorrents = inSpan' "getAndUpdateTransmissionTorrentsStatus" $ \span -> do
   let fields = ["hashString", "percentDone"]
   actualTorrents <-
     lift @Transaction $
@@ -77,14 +79,36 @@ getAndUpdateTransmissionTorrentsStatus knownTorrents = do
         )
         <&> Map.fromList
   let toDelete = Map.difference knownTorrents actualTorrents
-  execute
-    [fmt|
-    UPDATE redacted.torrents_json
-    SET transmission_torrent_hash = NULL
-    WHERE transmission_torrent_hash = ANY (?::text[])
-  |]
-    $ Only (toDelete & Map.keys <&> (.torrentHash) & PGArray :: PGArray Text)
-  pure actualTorrents
+  if
+    | Map.null toDelete -> do
+        addEventSimple span "We know about all transmission hashes."
+        pure (label @"knownTorrentsStale" False, actualTorrents)
+    | otherwise -> inSpan' "Delete outdated transmission hashes" $ \span' -> do
+        addAttribute
+          span'
+          "db.delete-transmission-hashes"
+          ( toDelete
+              & Map.toList
+              & Enc.list
+                ( \(k, v) ->
+                    Enc.object
+                      [ ("torrentHash", Enc.text k.torrentHash),
+                        ("groupId", Enc.int v.groupId),
+                        ("torrentId", Enc.int v.torrentId)
+                      ]
+                )
+              & jsonAttribute
+          )
+        _ <-
+          execute
+            [fmt|
+          UPDATE redacted.torrents_json
+          SET transmission_torrent_hash = NULL,
+              torrent_file = NULL
+          WHERE transmission_torrent_hash = ANY (?::text[])
+        |]
+            $ Only (toDelete & Map.keys <&> (.torrentHash) & PGArray :: PGArray Text)
+        pure (label @"knownTorrentsStale" True, actualTorrents)
 
 getTransmissionTorrentsTable ::
   (MonadTransmission m, MonadThrow m, MonadLogger m, MonadOtel m) => m Html
@@ -205,9 +229,9 @@ doTransmissionRequest' req = inSpan' "Transmission Request" $ \span -> do
       transmissionConnectionConfig
       req
   case resp.result of
-    TransmissionResponseFailure err -> appThrowTree span (nestedError "Transmission RPC error" $ singleError $ newError err)
+    TransmissionResponseFailure err -> appThrow span (AppExceptionTree $ nestedError "Transmission RPC error" $ singleError $ newError err)
     TransmissionResponseSuccess -> case resp.arguments of
-      Nothing -> appThrowTree span "Transmission RPC error: No `arguments` field in response"
+      Nothing -> appThrow span "Transmission RPC error: No `arguments` field in response"
       Just out -> pure out
 
 -- | Contact the transmission RPC, and do the CSRF protection dance.
@@ -305,8 +329,8 @@ doTransmissionRequest span dat (req, parser) = do
             case Json.eitherDecodeStrict' @Json.Value (resp & Http.getResponseBody) of
               Left _err -> pure ()
               Right val -> logInfo [fmt|failing transmission response: {showPrettyJson val}|]
-            appThrowTree span err
-    _ -> liftIO $ unwrapIOError $ Left [fmt|Non-200 response: {showPretty resp}|]
+            appThrow span (AppExceptionTree err)
+    _ -> appThrow span $ AppExceptionPretty [[fmt|Non-200 response:|], pretty resp]
 
 class MonadTransmission m where
   getCurrentTransmissionSessionId :: m (Maybe ByteString)
diff --git a/users/Profpatsch/whatcd-resolver/src/WhatcdResolver.hs b/users/Profpatsch/whatcd-resolver/src/WhatcdResolver.hs
index ad363090ffdd..c8850e70a121 100644
--- a/users/Profpatsch/whatcd-resolver/src/WhatcdResolver.hs
+++ b/users/Profpatsch/whatcd-resolver/src/WhatcdResolver.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE DeriveAnyClass #-}
 {-# LANGUAGE QuasiQuotes #-}
 
 module WhatcdResolver where
@@ -41,7 +42,6 @@ import Network.URI qualified
 import Network.Wai (ResponseReceived)
 import Network.Wai qualified as Wai
 import Network.Wai.Handler.Warp qualified as Warp
-import Network.Wai.Parse qualified as Wai
 import OpenTelemetry.Attributes qualified as Otel
 import OpenTelemetry.Trace qualified as Otel hiding (getTracer, inSpan, inSpan')
 import OpenTelemetry.Trace.Monad qualified as Otel
@@ -91,14 +91,17 @@ htmlUi = do
     let catchAppException act =
           try act >>= \case
             Right a -> pure a
-            Left (AppException err) -> do
-              runInIO (logError err)
+            Left (AppExceptionTree err) -> do
+              runInIO (logError (prettyErrorTree err))
+              respondOrig (Wai.responseLBS Http.status500 [] "")
+            Left (AppExceptionPretty err) -> do
+              runInIO (logError (err & Pretty.prettyErrsNoColor & stringToText))
               respondOrig (Wai.responseLBS Http.status500 [] "")
 
     catchAppException $ do
       let mp span parser =
             Multipart.parseMultipartOrThrow
-              (appThrowTree span)
+              (appThrow span . AppExceptionTree)
               parser
               req
 
@@ -111,7 +114,7 @@ htmlUi = do
 
       let parseQueryArgsNewSpan spanName parser =
             Parse.runParse "Unable to find the right request query arguments" (lmap Wai.queryString parser) req
-              & assertMNewSpan spanName id
+              & assertMNewSpan spanName (first AppExceptionTree)
 
       let handlers :: Handlers (AppT IO)
           handlers respond =
@@ -160,7 +163,7 @@ htmlUi = do
                       file <-
                         getTorrentFileById dat
                           <&> annotate [fmt|No torrent file for torrentId "{dat.torrentId}"|]
-                          >>= orAppThrowTree span
+                          >>= orAppThrow span
 
                       running <-
                         lift @Transaction $
@@ -311,6 +314,12 @@ htmlPageChrome title body =
         <title>{title}</title>
         <meta charset="utf-8">
         <meta name="viewport" content="width=device-width, initial-scale=1">
+        <!--
+          prevent favicon request, based on answers in
+          https://stackoverflow.com/questions/1321878/how-to-prevent-favicon-ico-requests
+          TODO: create favicon
+        -->
+        <link rel="icon" href="data:,">
         <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
         <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
         <script src="https://unpkg.com/htmx.org@1.9.2" integrity="sha384-L6OqL9pRWyyFU3+/bjdSri+iIphTN/bvYyM37tICVyOJkWZLpP2vGn6VUEXgzg6h" crossorigin="anonymous"></script>
@@ -551,25 +560,36 @@ getBestTorrentsData ::
   Transaction m [TorrentData (Label "percentDone" Percentage)]
 getBestTorrentsData artistFilter = inSpan' "get torrents table data" $ \span -> do
   artistFilter & doIfJust (\a -> addAttribute span "artist-filter.redacted-id" (a.artistRedactedId & showToText & Otel.toAttribute))
-  bestStale :: [TorrentData ()] <- getBestTorrents GetBestTorrentsFilter {onlyArtist = artistFilter, onlyDownloaded = False}
-  actual <-
+  let getBest = getBestTorrents GetBestTorrentsFilter {onlyArtist = artistFilter, onlyDownloaded = False}
+  bestStale :: [TorrentData ()] <- getBest
+  (statusInfo, transmissionStatus) <-
     getAndUpdateTransmissionTorrentsStatus
       ( bestStale
           & mapMaybe
             ( \td -> case td.torrentStatus of
-                InTransmission h -> Just h
+                InTransmission h -> Just (getLabel @"torrentHash" h, td)
                 _ -> Nothing
             )
-          <&> (\t -> (getLabel @"torrentHash" t, t.transmissionInfo))
           & Map.fromList
       )
+  bestBest <-
+    -- Instead of serving a stale table when a torrent gets deleted, fetch
+    -- the whole view again. This is a little wasteful, but torrents
+    -- shouldn’t get deleted very often, so it’s fine.
+    -- Re-evaluate invariant if this happens too often.
+    if statusInfo.knownTorrentsStale
+      then inSpan' "Fetch torrents table data again" $
+        \span' -> do
+          addEventSimple span' "The transmission torrent list was out of date, refetching torrent list."
+          getBest
+      else pure bestStale
   pure $
-    bestStale
+    bestBest
       --  we have to update the status of every torrent that’s not in tranmission anymore
       -- TODO I feel like it’s easier (& more correct?) to just do the database request again …
       <&> ( \td -> case td.torrentStatus of
               InTransmission info ->
-                case actual & Map.lookup (getLabel @"torrentHash" info) of
+                case transmissionStatus & Map.lookup (getLabel @"torrentHash" info) of
                   -- TODO this is also pretty dumb, cause it assumes that we have the torrent file if it was in transmission before,
                   -- which is an internal factum that is established in getBestTorrents (and might change later)
                   Nothing -> td {torrentStatus = NotInTransmissionYet}
@@ -621,6 +641,7 @@ mkBestTorrentsTable fresh = do
                   </td>
                   <td>{Html.toHtml @Natural b.torrentGroupJson.groupYear}</td>
                   <td>{Html.toHtml @Int b.seedingWeight}</td>
+                  <td>{Html.toHtml @Text b.torrentFormat}</td>
                   <td><details hx-trigger="toggle once" hx-post="snips/redacted/torrentDataJson" hx-vals={Enc.encToBytesUtf8 $ Enc.object [("torrent-id", Enc.int b.torrentId)]}></details></td>
                   </tr>
                 |]
@@ -635,8 +656,8 @@ mkBestTorrentsTable fresh = do
               <th>Name</th>
               <th>Year</th>
               <th>Weight</th>
+              <th>Format</th>
               <th>Torrent</th>
-              <th>Torrent Group</th>
             </tr>
           </thead>
           <tbody>
@@ -689,7 +710,7 @@ assertOneUpdated ::
   m ()
 assertOneUpdated span name x = case x.numberOfRowsAffected of
   1 -> pure ()
-  n -> appThrowTree span ([fmt|{name :: Text}: Expected to update exactly one row, but updated {n :: Natural} row(s)|])
+  n -> appThrow span ([fmt|{name :: Text}: Expected to update exactly one row, but updated {n :: Natural} row(s)|])
 
 migrate ::
   ( MonadPostgres m,
@@ -730,7 +751,8 @@ migrate = inSpan "Database Migration" $ do
     CREATE OR REPLACE FUNCTION calc_seeding_weight(full_json_result jsonb) RETURNS int AS $$
     BEGIN
       RETURN
-        ((full_json_result->'seeders')::integer*3
+        -- three times seeders plus one times snatches
+        (3 * (full_json_result->'seeders')::integer
         + (full_json_result->'snatches')::integer
         )
         -- prefer remasters by multiplying them with 3
@@ -738,7 +760,26 @@ migrate = inSpan "Database Migration" $ do
             WHEN full_json_result->>'remasterTitle' ILIKE '%remaster%'
             THEN 3
             ELSE 1
-          END);
+          END)
+        -- slightly push mp3 V0, to make sure it’s preferred over 320 CBR
+        * (CASE
+            WHEN full_json_result->>'encoding' ILIKE '%v0%'
+            THEN 2
+            ELSE 1
+          END)
+        -- remove 24bit torrents from the result (wayyy too big)
+        * (CASE
+            WHEN full_json_result->>'encoding' ILIKE '%24bit%'
+            THEN 0
+            ELSE 1
+          END)
+        -- discount FLACS, so we only use them when there’s no mp3 alternative (to save space)
+        / (CASE
+            WHEN full_json_result->>'encoding' ILIKE '%lossless%'
+            THEN 5
+            ELSE 1
+          END)
+        ;
     END;
     $$ LANGUAGE plpgsql IMMUTABLE;
 
@@ -764,38 +805,6 @@ migrate = inSpan "Database Migration" $ do
   |]
     ()
 
-httpTorrent ::
-  ( MonadIO m,
-    MonadThrow m
-  ) =>
-  Otel.Span ->
-  Http.Request ->
-  m ByteString
-httpTorrent span req =
-  Http.httpBS req
-    >>= assertM
-      span
-      ( \resp -> do
-          let statusCode = resp & Http.responseStatus & (.statusCode)
-              contentType =
-                resp
-                  & Http.responseHeaders
-                  & List.lookup "content-type"
-                  <&> Wai.parseContentType
-                  <&> (\(ct, _mimeAttributes) -> ct)
-          if
-            | statusCode == 200,
-              Just "application/x-bittorrent" <- contentType ->
-                Right $ (resp & Http.responseBody)
-            | statusCode == 200,
-              Just otherType <- contentType ->
-                Left [fmt|Redacted returned a non-torrent body, with content-type "{otherType}"|]
-            | statusCode == 200,
-              Nothing <- contentType ->
-                Left [fmt|Redacted returned a body with unspecified content type|]
-            | code <- statusCode -> Left [fmt|Redacted returned an non-200 error code, code {code}: {resp & showPretty}|]
-      )
-
 runAppWith :: AppT IO a -> IO (Either TmpPg.StartError a)
 runAppWith appT = withTracer $ \tracer -> withDb $ \db -> do
   tool <- readTools (label @"toolsEnvVar" "WHATCD_RESOLVER_TOOLS") (readTool "pg_format")
@@ -828,6 +837,17 @@ runAppWith appT = withTracer $ \tracer -> withDb $ \db -> do
         logInfo [fmt|Connected to database at {db & TmpPg.toDataDirectory} on socket {db & TmpPg.toConnectionString}|]
         appT
   runReaderT newAppT.unAppT Context {..}
+    `catch` ( \case
+                AppExceptionPretty p -> throwM $ EscapedException (p & Pretty.prettyErrs)
+                AppExceptionTree t -> throwM $ EscapedException (t & prettyErrorTree & textToString)
+            )
+
+-- | Just a silly wrapper so that correctly format any 'AppException' that would escape the runAppWith scope.
+newtype EscapedException = EscapedException String
+  deriving anyclass (Exception)
+
+instance Show EscapedException where
+  show (EscapedException s) = s
 
 withTracer :: (Otel.Tracer -> IO c) -> IO c
 withTracer f = do
diff --git a/users/Profpatsch/xdg-cache-home.nix b/users/Profpatsch/xdg-cache-home.nix
new file mode 100644
index 000000000000..6dc02b7f1d66
--- /dev/null
+++ b/users/Profpatsch/xdg-cache-home.nix
@@ -0,0 +1,14 @@
+{ depot, pkgs, lib, ... }:
+depot.nix.writeExecline "xdg-cache-home" { } [
+  "if"
+  "-n"
+  [
+    "printenv"
+    "XDG_CACHE_HOME"
+  ]
+  "importas"
+  "HOME"
+  "HOME"
+  "echo"
+  "\${HOME}/.cache"
+]
diff --git a/users/aspen/pkgs/cargo-hakari.nix b/users/aspen/pkgs/cargo-hakari.nix
deleted file mode 100644
index b6f4e7e40007..000000000000
--- a/users/aspen/pkgs/cargo-hakari.nix
+++ /dev/null
@@ -1,27 +0,0 @@
-{ pkgs, ... }:
-
-with pkgs;
-
-rustPlatform.buildRustPackage rec {
-  pname = "cargo-hakari";
-  version = "0.9.13";
-
-  src = fetchFromGitHub {
-    owner = "facebookincubator";
-    repo = "cargo-guppy";
-    rev = "cargo-hakari-${version}";
-    sha256 = "11ds2zryxdd6rvszkpphb0xnfg7rqisg6kixrwyiydjrm5rdjg9d";
-  };
-
-  cargoSha256 = "0b2hjyak5v4m3g5zjk2q8bdb4iv3015qw1rmhpclv4cv48lcmdbb";
-
-  buildAndTestSubdir = "tools/cargo-hakari";
-
-  nativeBuildInputs = [
-    pkg-config
-  ];
-
-  buildInputs = [
-    openssl
-  ];
-}
diff --git a/users/aspen/pkgs/cargo-nextest.nix b/users/aspen/pkgs/cargo-nextest.nix
deleted file mode 100644
index dbf3bd7eef19..000000000000
--- a/users/aspen/pkgs/cargo-nextest.nix
+++ /dev/null
@@ -1,27 +0,0 @@
-{ pkgs, ... }:
-
-with pkgs;
-
-rustPlatform.buildRustPackage rec {
-  pname = "cargo-nextest";
-  version = "0.9.36";
-
-  src = fetchFromGitHub {
-    owner = "nextest-rs";
-    repo = "nextest";
-    rev = "cargo-nextest-${version}";
-    sha256 = "1g40r38bqmdhc0dy07pj27vkc64d3fw6v5z2vwn82xld2h9dg7w2";
-  };
-
-  cargoSha256 = "1g862azgkn3xk3v3chs8hv1b1prj1pq2vfzbhcx6ir9l00kv6gcv";
-
-  cargoTestFlags = [
-    "--"
-    "--skip"
-    "tests_integration::test_relocated_run"
-    "--skip"
-    "tests_integration::test_run"
-    "--skip"
-    "tests_integration::test_run_after_build"
-  ];
-}
diff --git a/users/aspen/system/home/machines/roswell.nix b/users/aspen/system/home/machines/roswell.nix
index 135477b12ddf..600abb052b56 100644
--- a/users/aspen/system/home/machines/roswell.nix
+++ b/users/aspen/system/home/machines/roswell.nix
@@ -34,7 +34,7 @@ in
     openssl
 
     # Nix things
-    nixfmt
+    nixfmt-classic
     nix-prefetch-github
     nixpkgs-review
     cachix
diff --git a/users/aspen/system/home/modules/common.nix b/users/aspen/system/home/modules/common.nix
index b51ae1c7db7e..5117187d6b98 100644
--- a/users/aspen/system/home/modules/common.nix
+++ b/users/aspen/system/home/modules/common.nix
@@ -43,7 +43,7 @@
     openssl
 
     # Nix things
-    nixfmt
+    nixfmt-classic
     nix-prefetch-github
     nixpkgs-review
     cachix
diff --git a/users/aspen/system/home/modules/development/rust.nix b/users/aspen/system/home/modules/development/rust.nix
index c4b20f231546..3c81e2398010 100644
--- a/users/aspen/system/home/modules/development/rust.nix
+++ b/users/aspen/system/home/modules/development/rust.nix
@@ -10,16 +10,16 @@ with lib;
 
   home.packages = with pkgs; [
     rustup
+
+    cargo-bloat
     cargo-edit
     cargo-expand
+    cargo-hakari
+    cargo-nextest
     cargo-udeps
-    cargo-bloat
     sccache
     evcxr
 
-    depot.users.aspen.pkgs.cargo-hakari
-    depot.users.aspen.pkgs.cargo-nextest
-
     # benchmarking+profiling
     cargo-criterion
     cargo-flamegraph
diff --git a/users/aspen/system/home/modules/email.nix b/users/aspen/system/home/modules/email.nix
index cb92c40cee89..a43e3ab5a68d 100644
--- a/users/aspen/system/home/modules/email.nix
+++ b/users/aspen/system/home/modules/email.nix
@@ -16,7 +16,7 @@ let
     personal = {
       primary = true;
       address = "root@gws.fyi";
-      aliases = [ "aspen@gws.fyi" "aspen@gws.fyi" ];
+      aliases = [ "aspen@gws.fyi" ];
       passEntry = "root-gws-msmtp";
     };
   };
diff --git a/users/aspen/system/system/machines/ogopogo.nix b/users/aspen/system/system/machines/ogopogo.nix
index e69424118d8c..4b425246034d 100644
--- a/users/aspen/system/system/machines/ogopogo.nix
+++ b/users/aspen/system/system/machines/ogopogo.nix
@@ -77,12 +77,13 @@
     videoDrivers = [ "nvidia" ];
     dpi = 100;
   };
-  hardware.opengl.enable = true;
+  hardware.graphics.enable = true;
   services.picom = {
     enable = true;
     vSync = true;
   };
   hardware.graphics.enable32Bit = true;
+  hardware.nvidia.open = true;
 
   services.postgresql = {
     enable = true;
@@ -90,8 +91,8 @@
     authentication = "host all all 0.0.0.0/0 md5";
     dataDir = "/data/postgresql";
     package = pkgs.postgresql_15;
-    port = 5431;
     settings = {
+      port = 5431;
       wal_level = "logical";
     };
   };
diff --git a/users/aspen/system/system/machines/yeren.nix b/users/aspen/system/system/machines/yeren.nix
index acab0127376d..4b563df635aa 100644
--- a/users/aspen/system/system/machines/yeren.nix
+++ b/users/aspen/system/system/machines/yeren.nix
@@ -93,7 +93,7 @@
     sof-firmware
   ];
 
-  hardware.opengl.extraPackages = with pkgs; [
+  hardware.graphics.extraPackages = with pkgs; [
     vaapiIntel
     vaapiVdpau
     libvdpau-va-gl
diff --git a/users/aspen/system/system/modules/laptop.nix b/users/aspen/system/system/modules/laptop.nix
index b9bf38a4f1fa..57b2bc5a45a9 100644
--- a/users/aspen/system/system/modules/laptop.nix
+++ b/users/aspen/system/system/modules/laptop.nix
@@ -21,10 +21,5 @@
     percentageAction = 3;
   };
 
-  services.libinput = {
-    touchpad = {
-      naturalScrolling = true;
-      disableWhileTyping = true;
-    };
-  };
+  services.libinput.touchpad.naturalScrolling = true;
 }
diff --git a/users/aspen/system/system/modules/sound.nix b/users/aspen/system/system/modules/sound.nix
index 2081cbe8476a..c97e19f9b2f8 100644
--- a/users/aspen/system/system/modules/sound.nix
+++ b/users/aspen/system/system/modules/sound.nix
@@ -3,6 +3,7 @@
 {
   # Enable sound.
   hardware.pulseaudio.enable = true;
+  services.pipewire.enable = false;
 
   environment.systemPackages = with pkgs; [
     pulseaudio-ctl
diff --git a/users/azahi/OWNERS b/users/azahi/OWNERS
new file mode 100644
index 000000000000..73bc1ebcc3af
--- /dev/null
+++ b/users/azahi/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+azahi
diff --git a/users/azahi/pkgs/bruh/default.nix b/users/azahi/pkgs/bruh/default.nix
new file mode 100644
index 000000000000..5eecf94b6203
--- /dev/null
+++ b/users/azahi/pkgs/bruh/default.nix
@@ -0,0 +1,42 @@
+{ pkgs
+, lib
+, ...
+}:
+
+let
+  inherit (pkgs)
+    alsa-utils
+    fetchFromGitHub
+    stdenv
+    ;
+in
+
+stdenv.mkDerivation (finalAttrs: {
+  pname = "bruh";
+  version = "2.1";
+
+  src =
+    with finalAttrs;
+    fetchFromGitHub {
+      owner = "kejpies";
+      repo = pname;
+      rev = version;
+      hash = "sha256-Uw6Qes0IZkkfBchFnvnX9l1ZG5T5pyExmV7yUJLPOJ0=";
+    };
+
+  postPatch = ''
+    substituteInPlace bruh.c \
+      --replace-fail "aplay" "${alsa-utils}/bin/aplay"
+  '';
+
+  makeFlags = [ "PREFIX=$(out)" ];
+
+  meta = with lib; {
+    description = "Bruh sound, but as a program";
+    inherit (finalAttrs.src.meta) homepage;
+    license = licenses.gpl3Only;
+    platforms = platforms.linux;
+    maintainers = with maintainers; [ azahi ];
+    mainProgram = "bruh";
+  };
+})
diff --git a/users/emery/OWNERS b/users/emery/OWNERS
new file mode 100644
index 000000000000..9e052507d2da
--- /dev/null
+++ b/users/emery/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+emery
diff --git a/users/emery/eaglemode/default.nix b/users/emery/eaglemode/default.nix
new file mode 100644
index 000000000000..471e8eda67f7
--- /dev/null
+++ b/users/emery/eaglemode/default.nix
@@ -0,0 +1,13 @@
+# Derivation for my fully configured Eagle Mode.
+{ depot, ... }:
+
+let
+  config = depot.tools.eaglemode.etcDir {
+    extraPaths = [
+      depot.tools.eaglemode.commands.B
+      depot.tools.eaglemode.plugins.avif
+      depot.tools.eaglemode.plugins.qoi
+    ];
+  };
+in
+depot.tools.eaglemode.withConfig { inherit config; }
diff --git a/users/emery/pkgs/syndicate-server.nix b/users/emery/pkgs/syndicate-server.nix
new file mode 100644
index 000000000000..e8bf73836f24
--- /dev/null
+++ b/users/emery/pkgs/syndicate-server.nix
@@ -0,0 +1,34 @@
+{ pkgs, ... }:
+
+let
+  inherit (pkgs)
+    rustPlatform
+    rust-bin
+    fetchFromGitea
+    pkg-config
+    openssl
+    ;
+in
+
+rustPlatform.buildRustPackage rec {
+  pname = "syndicate-server";
+  version = "0.46.0";
+  src = fetchFromGitea {
+    domain = "git.syndicate-lang.org";
+    owner = "syndicate-lang";
+    repo = "syndicate-rs";
+    rev = "${pname}-v${version}";
+    sha256 = "sha256-bTteZIlBSoQ1o5shgd9NeKVvEhZTyG3i2zbeVojWiO8=";
+  };
+  cargoHash = "sha256-SIpdFXTk6MC/drjCLaaa49BbGsvCMNbPGCfTxAlCo9c=";
+  nativeBuildInputs = [ pkg-config ];
+  buildInputs = [ openssl ];
+
+  RUSTC_BOOTSTRAP = 1;
+
+  meta = {
+    description = "Syndicate broker server";
+    homepage = "https://git.syndicate-lang.org/syndicate-lang/syndicate-rs/";
+    mainProgram = "syndicate-server";
+  };
+}
diff --git a/users/emery/workman-cyrillic.xkb b/users/emery/workman-cyrillic.xkb
new file mode 100644
index 000000000000..f6783bab7b81
--- /dev/null
+++ b/users/emery/workman-cyrillic.xkb
@@ -0,0 +1,118 @@
+# Workman with phonetic transliteration layer for Cyrllic.
+# Switch layers using Shift+CapsLock.
+
+partial alphanumeric_keys
+xkb_symbols "workman-emery" {
+
+    name[Group1]= "Emery";
+
+    include "us(euro)"
+
+    // Alphanumeric section
+    key <AE01> {  [ 1,       exclam ] };
+    key <AE02> {  [ 2,           at ] };
+    key <AE03> {  [ 3,   numbersign ] };
+    key <AE04> {  [ 4,       dollar ] };
+    key <AE05> {  [ 5,      percent ] };
+    key <AE06> {  [ 6,  asciicircum ] };
+    key <AE07> {  [ 7,    ampersand ] };
+    key <AE08> {  [ 8,     asterisk ] };
+    key <AE09> {  [ 9,    parenleft ] };
+    key <AE10> {  [ 0,   parenright ] };
+
+    key <AD01> {
+        [     q,     Q,    adiaeresis,       Adiaeresis ],
+        [ U044B, U042B, U044c, U042C ] }; # ы Ы ь Ь
+    key <AD02> {
+        [     d,     D, U0111, U0111 ], # d D đ đ
+        [ U0434, U0414, U0452, U0402 ] }; # д Д ђ Ђ
+    key <AD03> {
+        [     r,          R,         U20AC,            U20AC ],
+        [ U0440, U0420, U20AC, U20AC ] }; # р Р
+    key <AD04> {
+      [     w,          W,         aring,            Aring ],
+      [ U0447, U0427, U045B, U040B ] }; # ч Ч ћ Ћ
+    key <AD05> {
+      [     b,          B,             b,                B ],
+      [ U0431, U0411 ]}; # б Б
+    key <AD06> {
+      [     j,     J, U0135, U0134 ], # j J ĵ Ĵ
+      [ U0458, U0408 ]}; # ј Ј
+    key <AD07> {
+      [ f, F,  0x1002200, F ], # f F ∀ F
+      [ U0444, U0424, U0473, U0472 ]}; # ф Ф ѳ Ѳ
+    key <AD08> {
+      [     u,          U, U016D, U016C ],
+      [ U0443, U0423, U045E, U040E ] }; # у У ў Ў
+    key <AD09> {
+      [     p,     P, sterling, sterling ],
+      [ U043F, U041F, sterling, sterling ] }; # п П
+    key <AD10> {
+      [ semicolon,  colon,     paragraph,           degree ] };
+
+    key <AC01> {
+        [     a,          A,    adiaeresis,       Adiaeresis ],
+        [ U0430, U0410, U2248, U00B6 ] # а А ≈ ¶
+      };
+    key <AC02> {
+        [     s,          S, U0161, U0160 ],
+        [ U0441, U0421,  U0448,  U0428 ] # с С ш Ш
+      };
+    key <AC03> {
+      [     h,          H, U010D, U010C ],
+      [ U0445, U0425 , U044B, U042B ] }; # х Х
+    key <AC04> {
+      [     t,          T,         thorn,            THORN ],
+      [ U0442, U0422] }; # т Т
+    key <AC05> {
+      [     g,          G, U011D, U011C ],
+      [ U0433, U0413  ] }; # г Г
+    key <AC06> {
+      [     y,          Y,    udiaeresis,       Udiaeresis ],
+      [ U044f, U042f, U044D, U042D ] }; # я Я э Э
+    key <AC07> {
+      [     n,          N,        ntilde,           Ntilde ],
+      [ U043D, U041D, U045A, U040A ] }; # н Н њ Њ
+    key <AC08> {
+      [     e,          E,    ediaeresis,       Ediaeresis ],
+      [ U0435, U0415, U0451, U0401 ] }; # е Е ё Ё
+    key <AC09> {
+      [     o,          O,    odiaeresis,       Odiaeresis ],
+      [ U043E, U041E, U044E, U042E  ] }; # о О ю Ю
+    key <AC10> {
+      [     i,          I,    idiaeresis,       Idiaeresis ],
+      [ U0438, U0418, U0439, U0419 ] }; # и И й
+    key <AC11> {
+      [],
+      [ U0447, U0427, U045B, U040B ] }; # ч Ч ћ Ћ
+
+    key <AB01> {
+      [     z,          Z,            U017E,              U017D ],
+      [ U0437, U0417, U0436, U0416 ] }; # з З ж Ж
+    key <AB02> {
+      [     x,          X,             x,                X ],
+      [ U045F, U040F ] }; # џ Џ
+    key <AB03> {
+      [     m,          M,            mu,               mu ],
+      [ U043C, U041C, mu,               mu ] }; # м М
+    key <AB04> {
+      [     c,          C, U0107, U0106 ],
+      [ U0446, U0426, U00A9, U2103 ] }; # ц Ц © ℃
+    key <AB05> {
+      [     v,          V,             v,                V ],
+      [ U0432, U0412 ]}; # в В
+    key <AB06> {
+      [     k,          K,            oe,               OE ],
+      [ U043A, U041A ] }; # к К
+    key <AB07> {
+      [     l,          L,  U01C9,     U01C8 ],
+      [ U043B, U041B,  U0459, U0409 ] }; # л Л љ Љ
+    key <AB08> { [   comma,    less,  ellipsis,  guillemotleft ] };
+    key <AB09> { [  period, greater,  ellipsis, guillemotright ] };
+    // End alphanumeric section
+
+    key <CAPS> { [ BackSpace, ISO_Next_Group ] };
+
+    key <SPCE> { [ space, minus, space, space ] };
+    include "level3(ralt_switch)"
+};
diff --git a/users/flokli/nixos/default.nix b/users/flokli/nixos/default.nix
index 9ed223a90896..cce4801f45bd 100644
--- a/users/flokli/nixos/default.nix
+++ b/users/flokli/nixos/default.nix
@@ -23,6 +23,7 @@ depot.nix.readTree.drvTargets rec {
 
   deps = (depot.nix.lazy-deps {
     deploy-archeology-ec2.attr = "users.flokli.nixos.deploy-archeology-ec2";
+    aws.attr = "third_party.nixpkgs.awscli";
   });
 
   shell = pkgs.mkShell {
diff --git a/users/kranzes/wasm-hello-world/Cargo.lock b/users/kranzes/wasm-hello-world/Cargo.lock
index d0ad9e38d78c..7a9956d8e2d1 100644
--- a/users/kranzes/wasm-hello-world/Cargo.lock
+++ b/users/kranzes/wasm-hello-world/Cargo.lock
@@ -63,19 +63,20 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
 dependencies = [
  "cfg-if",
+ "once_cell",
  "wasm-bindgen-macro",
 ]
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
 dependencies = [
  "bumpalo",
  "log",
@@ -88,9 +89,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -98,9 +99,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -111,9 +112,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
 
 [[package]]
 name = "wasm_hello_world"
diff --git a/users/kranzes/wasm-hello-world/Cargo.nix b/users/kranzes/wasm-hello-world/Cargo.nix
index 2f629a04d6e7..d590411e11eb 100644
--- a/users/kranzes/wasm-hello-world/Cargo.nix
+++ b/users/kranzes/wasm-hello-world/Cargo.nix
@@ -102,6 +102,7 @@ rec {
         version = "1.0.0";
         edition = "2018";
         sha256 = "1za0vb97n4brpzpv8lsbnzmq5r8f2b0cpqqr0sy8h5bn751xxwds";
+        libName = "cfg_if";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -232,6 +233,7 @@ rec {
         version = "1.0.12";
         edition = "2018";
         sha256 = "0jzf1znfpb2gx8nr8mvmyqs1crnv79l57nxnbiszc7xf7ynbjm1k";
+        libName = "unicode_ident";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -239,9 +241,10 @@ rec {
       };
       "wasm-bindgen" = rec {
         crateName = "wasm-bindgen";
-        version = "0.2.92";
-        edition = "2018";
-        sha256 = "1a4mcw13nsk3fr8fxjzf9kk1wj88xkfsmnm0pjraw01ryqfm7qjb";
+        version = "0.2.93";
+        edition = "2021";
+        sha256 = "1dfr7pka5kwvky2fx82m9d060p842hc5fyyw8igryikcdb0xybm8";
+        libName = "wasm_bindgen";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -251,6 +254,10 @@ rec {
             packageId = "cfg-if";
           }
           {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
             name = "wasm-bindgen-macro";
             packageId = "wasm-bindgen-macro";
           }
@@ -258,7 +265,6 @@ rec {
         features = {
           "default" = [ "spans" "std" ];
           "enable-interning" = [ "std" ];
-          "gg-alloc" = [ "wasm-bindgen-test/gg-alloc" ];
           "serde" = [ "dep:serde" ];
           "serde-serialize" = [ "serde" "serde_json" "std" ];
           "serde_json" = [ "dep:serde_json" ];
@@ -270,9 +276,10 @@ rec {
       };
       "wasm-bindgen-backend" = rec {
         crateName = "wasm-bindgen-backend";
-        version = "0.2.92";
-        edition = "2018";
-        sha256 = "1nj7wxbi49f0rw9d44rjzms26xlw6r76b2mrggx8jfbdjrxphkb1";
+        version = "0.2.93";
+        edition = "2021";
+        sha256 = "0yypblaf94rdgqs5xw97499xfwgs1096yx026d6h88v563d9dqwx";
+        libName = "wasm_bindgen_backend";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -314,10 +321,11 @@ rec {
       };
       "wasm-bindgen-macro" = rec {
         crateName = "wasm-bindgen-macro";
-        version = "0.2.92";
-        edition = "2018";
-        sha256 = "09npa1srjjabd6nfph5yc03jb26sycjlxhy0c2a1pdrpx4yq5y51";
+        version = "0.2.93";
+        edition = "2021";
+        sha256 = "1kycd1xfx4d9xzqknvzbiqhwb5fzvjqrrn88x692q1vblj8lqp2q";
         procMacro = true;
+        libName = "wasm_bindgen_macro";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -339,9 +347,10 @@ rec {
       };
       "wasm-bindgen-macro-support" = rec {
         crateName = "wasm-bindgen-macro-support";
-        version = "0.2.92";
-        edition = "2018";
-        sha256 = "1dqv2xs8zcyw4kjgzj84bknp2h76phmsb3n7j6hn396h4ssifkz9";
+        version = "0.2.93";
+        edition = "2021";
+        sha256 = "0dp8w6jmw44srym6l752nkr3hkplyw38a2fxz5f3j1ch9p3l1hxg";
+        libName = "wasm_bindgen_macro_support";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -376,10 +385,11 @@ rec {
       };
       "wasm-bindgen-shared" = rec {
         crateName = "wasm-bindgen-shared";
-        version = "0.2.92";
-        edition = "2018";
+        version = "0.2.93";
+        edition = "2021";
         links = "wasm_bindgen";
-        sha256 = "15kyavsrna2cvy30kg03va257fraf9x00ny554vxngvpyaa0q6dg";
+        sha256 = "1104bny0hv40jfap3hp8jhs0q4ya244qcrvql39i38xlghq0lan6";
+        libName = "wasm_bindgen_shared";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -508,52 +518,41 @@ rec {
                 testPostRun
               ]);
           in
-          pkgs.runCommand "run-tests-${testCrate.name}"
-            {
-              inherit testCrateFlags;
-              buildInputs = testInputs;
-            } ''
-            set -e
+          pkgs.stdenvNoCC.mkDerivation {
+            name = "run-tests-${testCrate.name}";
 
-            export RUST_BACKTRACE=1
+            inherit (crate) src;
 
-            # recreate a file hierarchy as when running tests with cargo
+            inherit testCrateFlags;
 
-            # the source for test data
-            # It's necessary to locate the source in $NIX_BUILD_TOP/source/
-            # instead of $NIX_BUILD_TOP/
-            # because we compiled those test binaries in the former and not the latter.
-            # So all paths will expect source tree to be there and not in the build top directly.
-            # For example: $NIX_BUILD_TOP := /build in general, if you ask yourself.
-            # NOTE: There could be edge cases if `crate.sourceRoot` does exist but
-            # it's very hard to reason about them.
-            # Open a bug if you run into this!
-            mkdir -p source/
-            cd source/
+            buildInputs = testInputs;
 
-            ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${crate.src}
+            buildPhase = ''
+              set -e
+              export RUST_BACKTRACE=1
 
-            # build outputs
-            testRoot=target/debug
-            mkdir -p $testRoot
+              # build outputs
+              testRoot=target/debug
+              mkdir -p $testRoot
 
-            # executables of the crate
-            # we copy to prevent std::env::current_exe() to resolve to a store location
-            for i in ${crate}/bin/*; do
-              cp "$i" "$testRoot"
-            done
-            chmod +w -R .
+              # executables of the crate
+              # we copy to prevent std::env::current_exe() to resolve to a store location
+              for i in ${crate}/bin/*; do
+                cp "$i" "$testRoot"
+              done
+              chmod +w -R .
 
-            # test harness executables are suffixed with a hash, like cargo does
-            # this allows to prevent name collision with the main
-            # executables of the crate
-            hash=$(basename $out)
-            for file in ${drv}/tests/*; do
-              f=$testRoot/$(basename $file)-$hash
-              cp $file $f
-              ${testCommand}
-            done
-          '';
+              # test harness executables are suffixed with a hash, like cargo does
+              # this allows to prevent name collision with the main
+              # executables of the crate
+              hash=$(basename $out)
+              for file in ${drv}/tests/*; do
+                f=$testRoot/$(basename $file)-$hash
+                cp $file $f
+                ${testCommand}
+              done
+            '';
+          };
       in
       pkgs.runCommand "${crate.name}-linked"
         {
diff --git a/users/picnoir/tvix-daemon/Cargo.lock b/users/picnoir/tvix-daemon/Cargo.lock
index 683203f5ca1e..a7be63acd093 100644
--- a/users/picnoir/tvix-daemon/Cargo.lock
+++ b/users/picnoir/tvix-daemon/Cargo.lock
@@ -214,9 +214,9 @@ dependencies = [
 
 [[package]]
 name = "bytes"
-version = "1.5.0"
+version = "1.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
+checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
 
 [[package]]
 name = "cc"
@@ -331,9 +331,9 @@ dependencies = [
 
 [[package]]
 name = "data-encoding"
-version = "2.5.0"
+version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
+checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
 
 [[package]]
 name = "der"
@@ -659,6 +659,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
 
 [[package]]
+name = "libmimalloc-sys"
+version = "0.1.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23aa6811d3bd4deb8a84dde645f943476d13b248d818edcf8ce0b2f37f036b44"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
 name = "litrs"
 version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -693,6 +703,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
 
 [[package]]
+name = "mimalloc"
+version = "0.1.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68914350ae34959d83f732418d51e2427a794055d0b9529f48259ac07af65633"
+dependencies = [
+ "libmimalloc-sys",
+]
+
+[[package]]
 name = "mime"
 version = "0.3.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -741,11 +760,13 @@ version = "0.1.0"
 dependencies = [
  "bitflags 2.4.2",
  "bstr",
+ "bytes",
  "data-encoding",
  "ed25519",
  "ed25519-dalek",
  "enum-primitive-derive",
  "glob",
+ "mimalloc",
  "nom",
  "num-traits",
  "pin-project-lite",
@@ -754,6 +775,7 @@ dependencies = [
  "sha2",
  "thiserror",
  "tokio",
+ "tracing",
 ]
 
 [[package]]
diff --git a/users/picnoir/tvix-daemon/Cargo.nix b/users/picnoir/tvix-daemon/Cargo.nix
index 2382027f9b13..b80fa0271b5f 100644
--- a/users/picnoir/tvix-daemon/Cargo.nix
+++ b/users/picnoir/tvix-daemon/Cargo.nix
@@ -1,4 +1,4 @@
-# This file was @generated by crate2nix 0.13.0 with the command:
+# This file was @generated by crate2nix 0.14.1 with the command:
 #   "generate" "--all-features"
 # See https://github.com/kolloch/crate2nix for more info.
 
@@ -13,6 +13,8 @@
 , rootFeatures ? [ "default" ]
   # If true, throw errors instead of issueing deprecation warnings.
 , strictDeprecation ? false
+  # Elements to add to the `-C target-feature=` argument passed to `rustc`
+  # (separated by `,`, prefixed with `+`).
   # Used for conditional compilation based on CPU feature detection.
 , targetFeatures ? [ ]
   # Whether to perform release builds: longer compile times, faster binaries.
@@ -181,6 +183,7 @@ rec {
         version = "0.2.3";
         edition = "2021";
         sha256 = "134jhzrz89labrdwxxnjxqjdg06qvaflj1wkfnmyapwyldfwcnn7";
+        libName = "anstyle_parse";
         dependencies = [
           {
             name = "utf8parse";
@@ -200,6 +203,7 @@ rec {
         version = "1.0.2";
         edition = "2021";
         sha256 = "0j3na4b1nma39g4x7cwvj009awxckjf3z2vkwhldgka44hqj72g2";
+        libName = "anstyle_query";
         dependencies = [
           {
             name = "windows-sys";
@@ -215,6 +219,7 @@ rec {
         version = "3.0.2";
         edition = "2021";
         sha256 = "19v0fv400bmp4niqpzxnhg83vz12mmqv7l2l8vi80qcdxj0lpm8w";
+        libName = "anstyle_wincon";
         dependencies = [
           {
             name = "anstyle";
@@ -234,6 +239,7 @@ rec {
         version = "0.3.5";
         edition = "2018";
         sha256 = "0l8sjq1rylkb1ak0pdyjn83b3k6x36j22myngl4sqqgg7whdsmnd";
+        libName = "async_stream";
         authors = [
           "Carl Lerche <me@carllerche.com>"
         ];
@@ -259,6 +265,7 @@ rec {
         edition = "2018";
         sha256 = "14q179j4y8p2z1d0ic6aqgy9fhwz8p9cai1ia8kpw4bw7q12mrhn";
         procMacro = true;
+        libName = "async_stream_impl";
         authors = [
           "Carl Lerche <me@carllerche.com>"
         ];
@@ -285,6 +292,7 @@ rec {
         edition = "2021";
         sha256 = "1adf1jh2yg39rkpmqjqyr9xyd6849p0d95425i6imgbhx0syx069";
         procMacro = true;
+        libName = "async_trait";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -497,6 +505,7 @@ rec {
         version = "0.4.3";
         edition = "2021";
         sha256 = "1qx28wg4j6qdcdrisqwyaavlzc0zvbsrcwa99zf9456lfbyn6p51";
+        libName = "axum_core";
         dependencies = [
           {
             name = "async-trait";
@@ -682,6 +691,7 @@ rec {
         version = "0.10.4";
         edition = "2018";
         sha256 = "0w9sa2ypmrsqqvc20nhwr75wbb5cjr4kkyhpjm1z1lv2kdicfy1h";
+        libName = "block_buffer";
         authors = [
           "RustCrypto Developers"
         ];
@@ -732,9 +742,9 @@ rec {
       };
       "bytes" = rec {
         crateName = "bytes";
-        version = "1.5.0";
+        version = "1.6.1";
         edition = "2018";
-        sha256 = "08w2i8ac912l8vlvkv3q51cd4gr09pwlg3sjsjffcizlrb0i5gd2";
+        sha256 = "0lnryqfiymbq5mfflfmbsqvfnw80kkh36nk5kpiscgxb9ac1cad1";
         authors = [
           "Carl Lerche <me@carllerche.com>"
           "Sean McArthur <sean@seanmonstar.com>"
@@ -764,6 +774,7 @@ rec {
         version = "1.0.0";
         edition = "2018";
         sha256 = "1za0vb97n4brpzpv8lsbnzmq5r8f2b0cpqqr0sy8h5bn751xxwds";
+        libName = "cfg_if";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -902,6 +913,7 @@ rec {
         version = "0.9.6";
         edition = "2021";
         sha256 = "1y0jnqaq7p2wvspnx7qj76m7hjcqpz73qzvr9l2p9n2s51vr6if2";
+        libName = "const_oid";
         authors = [
           "RustCrypto Developers"
         ];
@@ -921,7 +933,7 @@ rec {
           {
             name = "libc";
             packageId = "libc";
-            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-linux-android");
+            target = { target, features }: (stdenv.hostPlatform.rust.rustcTarget == "aarch64-linux-android");
           }
           {
             name = "libc";
@@ -946,6 +958,7 @@ rec {
         version = "0.1.6";
         edition = "2018";
         sha256 = "1cvby95a6xg7kxdz5ln3rl9xh66nz66w46mm3g56ri1z5x815yqv";
+        libName = "crypto_common";
         authors = [
           "RustCrypto Developers"
         ];
@@ -971,6 +984,7 @@ rec {
         version = "4.1.2";
         edition = "2021";
         sha256 = "0j7kqchcgycs4a11gvlda93h9w2jr05nn4hjpfyh2kn94a4pnrqa";
+        libName = "curve25519_dalek";
         authors = [
           "Isis Lovecruft <isis@patternsinthevoid.net>"
           "Henry de Valence <hdevalence@hdevalence.ca>"
@@ -1043,6 +1057,7 @@ rec {
         edition = "2021";
         sha256 = "1cry71xxrr0mcy5my3fb502cwfxy6822k4pm19cwrilrg7hq4s7l";
         procMacro = true;
+        libName = "curve25519_dalek_derive";
         dependencies = [
           {
             name = "proc-macro2";
@@ -1062,9 +1077,10 @@ rec {
       };
       "data-encoding" = rec {
         crateName = "data-encoding";
-        version = "2.5.0";
+        version = "2.6.0";
         edition = "2018";
-        sha256 = "1rcbnwfmfxhlshzbn3r7srm3azqha3mn33yxyqxkzz2wpqcjm5ky";
+        sha256 = "1qnn68n4vragxaxlkqcb1r28d3hhj43wch67lm4rpxlw89wnjmp8";
+        libName = "data_encoding";
         authors = [
           "Julien Cretin <git@ia0.eu>"
         ];
@@ -1149,6 +1165,7 @@ rec {
         edition = "2018";
         sha256 = "15cvgxqngxslgllz15m8aban6wqfgsi6nlhr0g25yfsnd6nq4lpg";
         procMacro = true;
+        libName = "document_features";
         libPath = "lib.rs";
         authors = [
           "Slint Developers <info@slint-ui.com>"
@@ -1200,6 +1217,7 @@ rec {
         version = "2.1.1";
         edition = "2021";
         sha256 = "0w88cafwglg9hjizldbmlza0ns3hls81zk1bcih3m5m3h67algaa";
+        libName = "ed25519_dalek";
         authors = [
           "isis lovecruft <isis@patternsinthevoid.net>"
           "Tony Arcieri <bascule@gmail.com>"
@@ -1278,6 +1296,7 @@ rec {
         edition = "2018";
         sha256 = "0k6wcf58h5kh64yq5nfq71va53kaya0kzxwsjwbgwm2n2zd9axxs";
         procMacro = true;
+        libName = "enum_primitive_derive";
         authors = [
           "Doug Goldstein <cardoe@cardoe.com>"
         ];
@@ -1310,6 +1329,7 @@ rec {
         version = "0.2.6";
         edition = "2018";
         sha256 = "10hkkkjynhibvchznkxx81gwxqarn9i5sgz40d6xxb8xzhsz8xhn";
+        libName = "fiat_crypto";
         authors = [
           "Fiat Crypto library authors <jgross@mit.edu>"
         ];
@@ -1358,6 +1378,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "0y6b7xxqdjm9hlcjpakcg41qfl7lihf6gavk8fyqijsxhvbzgj7a";
+        libName = "futures_channel";
         dependencies = [
           {
             name = "futures-core";
@@ -1379,6 +1400,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "07aslayrn3lbggj54kci0ishmd1pr367fp7iks7adia1p05miinz";
+        libName = "futures_core";
         features = {
           "default" = [ "std" ];
           "portable-atomic" = [ "dep:portable-atomic" ];
@@ -1392,6 +1414,7 @@ rec {
         edition = "2018";
         sha256 = "1b49qh9d402y8nka4q6wvvj0c88qq91wbr192mdn5h54nzs0qxc7";
         procMacro = true;
+        libName = "futures_macro";
         dependencies = [
           {
             name = "proc-macro2";
@@ -1414,6 +1437,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "1dag8xyyaya8n8mh8smx7x6w2dpmafg2din145v973a3hw7f1f4z";
+        libName = "futures_sink";
         features = {
           "default" = [ "std" ];
           "std" = [ "alloc" ];
@@ -1425,6 +1449,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "013h1724454hj8qczp8vvs10qfiqrxr937qsrv6rhii68ahlzn1q";
+        libName = "futures_task";
         features = {
           "default" = [ "std" ];
           "std" = [ "alloc" ];
@@ -1436,6 +1461,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "0j0xqhcir1zf2dcbpd421kgw6wvsk0rpxflylcysn1rlp3g02r1x";
+        libName = "futures_util";
         dependencies = [
           {
             name = "futures-core";
@@ -1690,6 +1716,7 @@ rec {
         version = "0.3.9";
         edition = "2021";
         sha256 = "092hxjbjnq5fmz66grd9plxd0sh6ssg5fhgwwwqbrzgzkjwdycfj";
+        libName = "hermit_abi";
         authors = [
           "Stefan Lankes"
         ];
@@ -1735,6 +1762,7 @@ rec {
         version = "1.0.0";
         edition = "2018";
         sha256 = "0hyn8n3iadrbwq8y0p1rl1275s4nm49bllw5wji29g4aa3dqbb0w";
+        libName = "http_body";
         authors = [
           "Carl Lerche <me@carllerche.com>"
           "Lucio Franco <luciofranco14@gmail.com>"
@@ -1757,6 +1785,7 @@ rec {
         version = "0.1.1";
         edition = "2018";
         sha256 = "07agldas2qgcfc05ckiarlmf9vzragbda823nqhrqrc6mjrghx84";
+        libName = "http_body_util";
         authors = [
           "Carl Lerche <me@carllerche.com>"
           "Lucio Franco <luciofranco14@gmail.com>"
@@ -1912,6 +1941,7 @@ rec {
         version = "0.1.3";
         edition = "2021";
         sha256 = "1akngan7j0n2n0wd25c6952mvqbkj9gp1lcwzyxjc0d37l8yyf6a";
+        libName = "hyper_util";
         authors = [
           "Sean McArthur <sean@seanmonstar.com>"
         ];
@@ -2053,6 +2083,33 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" "extra_traits" "std" ];
       };
+      "libmimalloc-sys" = rec {
+        crateName = "libmimalloc-sys";
+        version = "0.1.39";
+        edition = "2018";
+        links = "mimalloc";
+        sha256 = "0i3b0dzz7cp0ik7ys66q92r16va78gwlbrnxhj5fnkdxsc8niai3";
+        libName = "libmimalloc_sys";
+        authors = [
+          "Octavian Oncescu <octavonce@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+        features = {
+          "cty" = [ "dep:cty" ];
+          "extended" = [ "cty" ];
+        };
+      };
       "litrs" = rec {
         crateName = "litrs";
         version = "0.4.1";
@@ -2150,6 +2207,34 @@ rec {
         };
         resolvedDefaultFeatures = [ "alloc" "default" "std" ];
       };
+      "mimalloc" = rec {
+        crateName = "mimalloc";
+        version = "0.1.43";
+        edition = "2018";
+        sha256 = "0csnyrxc16i592gm5ffham07jyj2w98qsh9jyy1rv59lmr8474b8";
+        authors = [
+          "Octavian Oncescu <octavonce@gmail.com>"
+          "Vincent Rouillé <vincent@speedy37.fr>"
+          "Thom Chiovoloni <chiovolonit@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libmimalloc-sys";
+            packageId = "libmimalloc-sys";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "debug" = [ "libmimalloc-sys/debug" ];
+          "debug_in_debug" = [ "libmimalloc-sys/debug_in_debug" ];
+          "extended" = [ "libmimalloc-sys/extended" ];
+          "local_dynamic_tls" = [ "libmimalloc-sys/local_dynamic_tls" ];
+          "no_thp" = [ "libmimalloc-sys/no_thp" ];
+          "override" = [ "libmimalloc-sys/override" ];
+          "secure" = [ "libmimalloc-sys/secure" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
       "mime" = rec {
         crateName = "mime";
         version = "0.3.17";
@@ -2165,6 +2250,7 @@ rec {
         version = "0.2.1";
         edition = "2018";
         sha256 = "16ppc5g84aijpri4jzv14rvcnslvlpphbszc7zzp6vfkddf4qdb8";
+        libName = "minimal_lexical";
         authors = [
           "Alex Huszagh <ahuszagh@gmail.com>"
         ];
@@ -2286,12 +2372,8 @@ rec {
         version = "0.1.0";
         edition = "2021";
         crateBin = [ ];
-        # We can't filter paths with references in Nix 2.4
-        # See https://github.com/NixOS/nix/issues/5410
-        src =
-          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
-          then lib.cleanSourceWith { filter = sourceFilter; src = ../../../tvix/nix-compat; }
-          else ../../../tvix/nix-compat;
+        src = lib.cleanSourceWith { filter = sourceFilter; src = ../../../tvix/nix-compat; };
+        libName = "nix_compat";
         dependencies = [
           {
             name = "bitflags";
@@ -2303,6 +2385,11 @@ rec {
             features = [ "alloc" "unicode" "serde" ];
           }
           {
+            name = "bytes";
+            packageId = "bytes";
+            optional = true;
+          }
+          {
             name = "data-encoding";
             packageId = "data-encoding";
           }
@@ -2323,6 +2410,10 @@ rec {
             packageId = "glob";
           }
           {
+            name = "mimalloc";
+            packageId = "mimalloc";
+          }
+          {
             name = "nom";
             packageId = "nom";
           }
@@ -2358,20 +2449,30 @@ rec {
             optional = true;
             features = [ "io-util" "macros" ];
           }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
         ];
         devDependencies = [
           {
+            name = "mimalloc";
+            packageId = "mimalloc";
+          }
+          {
             name = "serde_json";
             packageId = "serde_json";
           }
         ];
         features = {
           "async" = [ "tokio" ];
+          "bytes" = [ "dep:bytes" ];
+          "default" = [ "async" "wire" ];
           "pin-project-lite" = [ "dep:pin-project-lite" ];
           "tokio" = [ "dep:tokio" ];
-          "wire" = [ "tokio" "pin-project-lite" ];
+          "wire" = [ "tokio" "pin-project-lite" "bytes" ];
         };
-        resolvedDefaultFeatures = [ "pin-project-lite" "tokio" "wire" ];
+        resolvedDefaultFeatures = [ "async" "bytes" "default" "pin-project-lite" "tokio" "wire" ];
       };
       "nom" = rec {
         crateName = "nom";
@@ -2404,6 +2505,7 @@ rec {
         version = "0.46.0";
         edition = "2018";
         sha256 = "115sywxh53p190lyw97alm14nc004qj5jm5lvdj608z84rbida3p";
+        libName = "nu_ansi_term";
         authors = [
           "ogham@bsago.me"
           "Ryan Scheel (Havvy) <ryan.havvy@gmail.com>"
@@ -2432,6 +2534,7 @@ rec {
         version = "0.2.18";
         edition = "2018";
         sha256 = "0yjib8p2p9kzmaz48xwhs69w5dh1wipph9jgnillzd2x33jz03fs";
+        libName = "num_traits";
         authors = [
           "The Rust Project Developers"
         ];
@@ -2602,6 +2705,7 @@ rec {
         version = "2.3.1";
         edition = "2018";
         sha256 = "0gi8wgx0dcy8rnv1kywdv98lwcx67hz0a0zwpib5v2i08r88y573";
+        libName = "percent_encoding";
         authors = [
           "The rust-url developers"
         ];
@@ -2616,6 +2720,7 @@ rec {
         version = "1.1.5";
         edition = "2021";
         sha256 = "1cxl146x0q7lawp0m1826wsgj8mmmfs6ja8q7m6f7ff5j6vl7gxn";
+        libName = "pin_project";
         dependencies = [
           {
             name = "pin-project-internal";
@@ -2630,6 +2735,7 @@ rec {
         edition = "2021";
         sha256 = "0r9r4ivwiyqf45sv6b30l1dx282lxaax2f6gl84jwa3q590s8f1g";
         procMacro = true;
+        libName = "pin_project_internal";
         dependencies = [
           {
             name = "proc-macro2";
@@ -2652,6 +2758,7 @@ rec {
         version = "0.2.13";
         edition = "2018";
         sha256 = "0n0bwr5qxlf0mhn2xkl36sy55118s9qmvx2yl5f3ixkb007lbywa";
+        libName = "pin_project_lite";
 
       };
       "pin-utils" = rec {
@@ -2659,6 +2766,7 @@ rec {
         version = "0.1.0";
         edition = "2018";
         sha256 = "117ir7vslsl2z1a7qzhws4pd01cg2d3338c47swjyvqv2n60v1wb";
+        libName = "pin_utils";
         authors = [
           "Josef Brandl <mail@josefbrandl.de>"
         ];
@@ -2718,6 +2826,7 @@ rec {
         version = "1.0.79";
         edition = "2021";
         sha256 = "0bn004ybzdqid81cqppr5c9jrvqsxv50x60sxc41cwpmk0igydg8";
+        libName = "proc_macro2";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
           "Alex Crichton <alex@alexcrichton.com>"
@@ -2803,6 +2912,7 @@ rec {
         version = "0.4.6";
         edition = "2021";
         sha256 = "1spaq7y4im7s56d1gxa2hi4hzf6dwswb1bv8xyavzya7k25kpf46";
+        libName = "regex_automata";
         authors = [
           "The Rust Project Developers"
           "Andrew Gallant <jamslam@gmail.com>"
@@ -2843,6 +2953,7 @@ rec {
         version = "0.1.23";
         edition = "2015";
         sha256 = "0xnbk2bmyzshacjm2g1kd4zzv2y2az14bw3sjccq5qkpmsfvn9nn";
+        libName = "rustc_demangle";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -3119,6 +3230,7 @@ rec {
         version = "0.1.7";
         edition = "2018";
         sha256 = "1xipjr4nqsgw34k7a2cgj9zaasl2ds6jwn89886kww93d32a637l";
+        libName = "sharded_slab";
         authors = [
           "Eliza Weisman <eliza@buoyant.io>"
         ];
@@ -3137,6 +3249,7 @@ rec {
         version = "1.4.1";
         edition = "2015";
         sha256 = "18crkkw5k82bvcx088xlf5g4n3772m24qhzgfan80nda7d3rn8nq";
+        libName = "signal_hook_registry";
         authors = [
           "Michal 'vorner' Vaner <vorner@vorner.cz>"
           "Masaki Hara <ackie.h.gmai@gmail.com>"
@@ -3359,6 +3472,7 @@ rec {
         edition = "2021";
         sha256 = "1xylyqcb8rv5yh2yf97hg4n4kg27qccc0ijafr1zqklrhahkn7y6";
         procMacro = true;
+        libName = "thiserror_impl";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -3513,6 +3627,7 @@ rec {
         version = "0.3.2";
         edition = "2021";
         sha256 = "00vkr1cywd2agn8jbkzwwf7y4ps3cfjm8l9ab697px2cgc97wdln";
+        libName = "tokio_listener";
         dependencies = [
           {
             name = "axum";
@@ -3598,6 +3713,7 @@ rec {
         edition = "2021";
         sha256 = "0fwjy4vdx1h9pi4g2nml72wi0fr27b5m954p13ji9anyy8l1x2jv";
         procMacro = true;
+        libName = "tokio_macros";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -3623,6 +3739,7 @@ rec {
         version = "0.1.14";
         edition = "2021";
         sha256 = "0hi8hcwavh5sdi1ivc9qc4yvyr32f153c212dpd7sb366y6rhz1r";
+        libName = "tokio_stream";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -3666,6 +3783,7 @@ rec {
         version = "0.4.4";
         edition = "2021";
         sha256 = "1xzri2m3dg8nzdyznm77nymvil9cyh1gfdfrbnska51iqfmvls14";
+        libName = "tokio_test";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -3706,6 +3824,7 @@ rec {
         version = "0.7.10";
         edition = "2021";
         sha256 = "058y6x4mf0fsqji9rfyb77qbfyc50y4pk2spqgj6xsyr693z66al";
+        libName = "tokio_util";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -3867,6 +3986,7 @@ rec {
         version = "0.3.2";
         edition = "2018";
         sha256 = "1l7i17k9vlssrdg4s3b0ia5jjkmmxsvv8s9y9ih0jfi8ssz8s362";
+        libName = "tower_layer";
         authors = [
           "Tower Maintainers <team@tower-rs.com>"
         ];
@@ -3877,6 +3997,7 @@ rec {
         version = "0.3.2";
         edition = "2018";
         sha256 = "0lmfzmmvid2yp2l36mbavhmqgsvzqf7r2wiwz73ml4xmwaf1rg5n";
+        libName = "tower_service";
         authors = [
           "Tower Maintainers <team@tower-rs.com>"
         ];
@@ -3935,6 +4056,7 @@ rec {
         edition = "2018";
         sha256 = "1rvb5dn9z6d0xdj14r403z0af0bbaqhg02hq4jc97g5wds6lqw1l";
         procMacro = true;
+        libName = "tracing_attributes";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
           "Eliza Weisman <eliza@buoyant.io>"
@@ -3963,6 +4085,7 @@ rec {
         version = "0.1.32";
         edition = "2018";
         sha256 = "0m5aglin3cdwxpvbg6kz0r9r0k31j48n0kcfwsp6l49z26k3svf0";
+        libName = "tracing_core";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -3993,6 +4116,7 @@ rec {
         version = "0.2.0";
         edition = "2018";
         sha256 = "1hs77z026k730ij1a9dhahzrl0s073gfa2hm5p0fbl0b80gmz1gf";
+        libName = "tracing_log";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -4024,6 +4148,7 @@ rec {
         version = "0.3.18";
         edition = "2018";
         sha256 = "12vs1bwk4kig1l2qqjbbn2nm5amwiqmkcmnznylzmnfvjy6083xd";
+        libName = "tracing_subscriber";
         authors = [
           "Eliza Weisman <eliza@buoyant.io>"
           "David Barsky <me@davidbarsky.com>"
@@ -4110,12 +4235,7 @@ rec {
             requiredFeatures = [ ];
           }
         ];
-        # We can't filter paths with references in Nix 2.4
-        # See https://github.com/NixOS/nix/issues/5410
-        src =
-          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
-          then lib.cleanSourceWith { filter = sourceFilter; src = ./.; }
-          else ./.;
+        src = lib.cleanSourceWith { filter = sourceFilter; src = ./.; };
         dependencies = [
           {
             name = "clap";
@@ -4173,6 +4293,7 @@ rec {
         version = "1.0.12";
         edition = "2018";
         sha256 = "0jzf1znfpb2gx8nr8mvmyqs1crnv79l57nxnbiszc7xf7ynbjm1k";
+        libName = "unicode_ident";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -4242,12 +4363,12 @@ rec {
           {
             name = "winapi-i686-pc-windows-gnu";
             packageId = "winapi-i686-pc-windows-gnu";
-            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "i686-pc-windows-gnu");
+            target = { target, features }: (stdenv.hostPlatform.rust.rustcTarget == "i686-pc-windows-gnu");
           }
           {
             name = "winapi-x86_64-pc-windows-gnu";
             packageId = "winapi-x86_64-pc-windows-gnu";
-            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnu");
+            target = { target, features }: (stdenv.hostPlatform.rust.rustcTarget == "x86_64-pc-windows-gnu");
           }
         ];
         features = {
@@ -4260,6 +4381,7 @@ rec {
         version = "0.4.0";
         edition = "2015";
         sha256 = "1dmpa6mvcvzz16zg6d5vrfy4bxgg541wxrcip7cnshi06v38ffxc";
+        libName = "winapi_i686_pc_windows_gnu";
         authors = [
           "Peter Atashian <retep998@gmail.com>"
         ];
@@ -4270,6 +4392,7 @@ rec {
         version = "0.4.0";
         edition = "2015";
         sha256 = "0gqq64czqb64kskjryj8isp62m2sgvx25yyj3kpc2myh85w24bki";
+        libName = "winapi_x86_64_pc_windows_gnu";
         authors = [
           "Peter Atashian <retep998@gmail.com>"
         ];
@@ -4280,6 +4403,7 @@ rec {
         version = "0.48.0";
         edition = "2018";
         sha256 = "1aan23v5gs7gya1lc46hqn9mdh8yph3fhxmhxlw36pn6pqc28zb7";
+        libName = "windows_sys";
         authors = [
           "Microsoft"
         ];
@@ -4573,6 +4697,7 @@ rec {
         version = "0.52.0";
         edition = "2021";
         sha256 = "0gd3v4ji88490zgb6b5mq5zgbvwv7zx1ibn8v3x83rwcdbryaar8";
+        libName = "windows_sys";
         authors = [
           "Microsoft"
         ];
@@ -4820,6 +4945,7 @@ rec {
         version = "0.48.5";
         edition = "2018";
         sha256 = "034ljxqshifs1lan89xwpcy1hp0lhdh4b5n0d2z4fwjx2piacbws";
+        libName = "windows_targets";
         authors = [
           "Microsoft"
         ];
@@ -4827,7 +4953,7 @@ rec {
           {
             name = "windows_aarch64_gnullvm";
             packageId = "windows_aarch64_gnullvm 0.48.5";
-            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-pc-windows-gnullvm");
+            target = { target, features }: (stdenv.hostPlatform.rust.rustcTarget == "aarch64-pc-windows-gnullvm");
           }
           {
             name = "windows_aarch64_msvc";
@@ -4852,7 +4978,7 @@ rec {
           {
             name = "windows_x86_64_gnullvm";
             packageId = "windows_x86_64_gnullvm 0.48.5";
-            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnullvm");
+            target = { target, features }: (stdenv.hostPlatform.rust.rustcTarget == "x86_64-pc-windows-gnullvm");
           }
           {
             name = "windows_x86_64_msvc";
@@ -4867,6 +4993,7 @@ rec {
         version = "0.52.4";
         edition = "2021";
         sha256 = "06sdd7fin3dj9cmlg6n1dw0n1l10jhn9b8ckz1cqf0drb9z7plvx";
+        libName = "windows_targets";
         authors = [
           "Microsoft"
         ];
@@ -4874,7 +5001,7 @@ rec {
           {
             name = "windows_aarch64_gnullvm";
             packageId = "windows_aarch64_gnullvm 0.52.4";
-            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-pc-windows-gnullvm");
+            target = { target, features }: (stdenv.hostPlatform.rust.rustcTarget == "aarch64-pc-windows-gnullvm");
           }
           {
             name = "windows_aarch64_msvc";
@@ -4899,7 +5026,7 @@ rec {
           {
             name = "windows_x86_64_gnullvm";
             packageId = "windows_x86_64_gnullvm 0.52.4";
-            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnullvm");
+            target = { target, features }: (stdenv.hostPlatform.rust.rustcTarget == "x86_64-pc-windows-gnullvm");
           }
           {
             name = "windows_x86_64_msvc";
@@ -5081,14 +5208,11 @@ rec {
       fuchsia = true;
       test = false;
 
-      /* We are choosing an arbitrary rust version to grab `lib` from,
-      which is unfortunate, but `lib` has been version-agnostic the
-      whole time so this is good enough for now.
-      */
-      os = pkgs.rust.lib.toTargetOs platform;
-      arch = pkgs.rust.lib.toTargetArch platform;
-      family = pkgs.rust.lib.toTargetFamily platform;
-      vendor = pkgs.rust.lib.toTargetVendor platform;
+      inherit (platform.rust.platform)
+        arch
+        os
+        vendor;
+      family = platform.rust.platform.target-family;
       env = "gnu";
       endian =
         if platform.parsed.cpu.significantByte.name == "littleEndian"
@@ -5178,51 +5302,41 @@ rec {
                 testPostRun
               ]);
           in
-          pkgs.runCommand "run-tests-${testCrate.name}"
-            {
-              inherit testCrateFlags;
-              buildInputs = testInputs;
-            } ''
-            set -e
+          pkgs.stdenvNoCC.mkDerivation {
+            name = "run-tests-${testCrate.name}";
 
-            export RUST_BACKTRACE=1
+            inherit (crate) src;
 
-            # recreate a file hierarchy as when running tests with cargo
+            inherit testCrateFlags;
 
-            # the source for test data
-            # It's necessary to locate the source in $NIX_BUILD_TOP/source/
-            # instead of $NIX_BUILD_TOP/
-            # because we compiled those test binaries in the former and not the latter.
-            # So all paths will expect source tree to be there and not in the build top directly.
-            # For example: $NIX_BUILD_TOP := /build in general, if you ask yourself.
-            # TODO(raitobezarius): I believe there could be more edge cases if `crate.sourceRoot`
-            # do exist but it's very hard to reason about them, so let's wait until the first bug report.
-            mkdir -p source/
-            cd source/
+            buildInputs = testInputs;
 
-            ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${crate.src}
+            buildPhase = ''
+              set -e
+              export RUST_BACKTRACE=1
 
-            # build outputs
-            testRoot=target/debug
-            mkdir -p $testRoot
+              # build outputs
+              testRoot=target/debug
+              mkdir -p $testRoot
 
-            # executables of the crate
-            # we copy to prevent std::env::current_exe() to resolve to a store location
-            for i in ${crate}/bin/*; do
-              cp "$i" "$testRoot"
-            done
-            chmod +w -R .
+              # executables of the crate
+              # we copy to prevent std::env::current_exe() to resolve to a store location
+              for i in ${crate}/bin/*; do
+                cp "$i" "$testRoot"
+              done
+              chmod +w -R .
 
-            # test harness executables are suffixed with a hash, like cargo does
-            # this allows to prevent name collision with the main
-            # executables of the crate
-            hash=$(basename $out)
-            for file in ${drv}/tests/*; do
-              f=$testRoot/$(basename $file)-$hash
-              cp $file $f
-              ${testCommand}
-            done
-          '';
+              # test harness executables are suffixed with a hash, like cargo does
+              # this allows to prevent name collision with the main
+              # executables of the crate
+              hash=$(basename $out)
+              for file in ${drv}/tests/*; do
+                f=$testRoot/$(basename $file)-$hash
+                cp $file $f
+                ${testCommand}
+              done
+            '';
+          };
       in
       pkgs.runCommand "${crate.name}-linked"
         {
@@ -5331,7 +5445,7 @@ rec {
             let
               self = {
                 crates = lib.mapAttrs (packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId) crateConfigs;
-                target = makeTarget pkgs.stdenv.hostPlatform;
+                target = makeTarget stdenv.hostPlatform;
                 build = mkBuiltByPackageIdByPkgs pkgs.buildPackages;
               };
             in
@@ -5406,8 +5520,6 @@ rec {
             buildRustCrateForPkgsFunc pkgs
               (
                 crateConfig // {
-                  # https://github.com/NixOS/nixpkgs/issues/218712
-                  dontStrip = stdenv.hostPlatform.isDarwin;
                   src = crateConfig.src or (
                     pkgs.fetchurl rec {
                       name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz";
diff --git a/users/picnoir/tvix-daemon/default.nix b/users/picnoir/tvix-daemon/default.nix
index 78b9aa9a1d1c..d970ac3608f3 100644
--- a/users/picnoir/tvix-daemon/default.nix
+++ b/users/picnoir/tvix-daemon/default.nix
@@ -36,8 +36,18 @@ in
 
     buildPhase = "cargo clippy --tests --all-features --benches --examples | tee $out";
   };
+  crate2nix-check =
+    let
+      crate2nix-check = depot.tvix.utils.mkCrate2nixCheck ./Cargo.nix;
+    in
+    crate2nix-check.command.overrideAttrs {
+      meta.ci.extraSteps = {
+        inherit crate2nix-check;
+      };
+    };
   meta.ci.targets = [
     "tvix-daemon"
     "shell"
+    "crate2nix-check"
   ];
 }
diff --git a/users/sterni/machines/ingeborg/http/code.sterni.lv.nix b/users/sterni/machines/ingeborg/http/code.sterni.lv.nix
index 3b080b9dc8b5..24ce218d48d6 100644
--- a/users/sterni/machines/ingeborg/http/code.sterni.lv.nix
+++ b/users/sterni/machines/ingeborg/http/code.sterni.lv.nix
@@ -170,7 +170,7 @@ in
   ];
 
   config = {
-    services.fcgiwrap.cgit = {
+    services.fcgiwrap.instances.cgit = {
       process = {
         user = "http";
         group = "http";
@@ -197,7 +197,7 @@ in
           fastcgi_param    QUERY_STRING    $args;
           fastcgi_param    HTTP_HOST       $server_name;
           fastcgi_param    CGIT_CONFIG     ${cgitConfig};
-          fastcgi_pass     unix:${toString config.services.fcgiwrap.cgit.socket.address};
+          fastcgi_pass     unix:${toString config.services.fcgiwrap.instances.cgit.socket.address};
         }
       '';
     };
diff --git a/users/sterni/modules/backup-minecraft-fabric.nix b/users/sterni/modules/backup-minecraft-fabric.nix
index a80a7f51a9ef..5dad2b8825c2 100644
--- a/users/sterni/modules/backup-minecraft-fabric.nix
+++ b/users/sterni/modules/backup-minecraft-fabric.nix
@@ -10,7 +10,8 @@ let
   inherit (depot.nix) getBins;
 
   bins = getBins pkgs.borgbackup [ "borg" ]
-    // getBins pkgs.mcrcon [ "mcrcon" ];
+    // getBins pkgs.mcrcon [ "mcrcon" ]
+    // getBins pkgs.systemd [ "systemd-creds" ];
 
   unvaried = ls: builtins.all (l: l == builtins.head ls) ls;
 
@@ -29,7 +30,7 @@ let
       export MCRCON_HOST="localhost"
       export MCRCON_PORT="${toString instanceCfg.serverProperties."rcon.port"}"
       # Unfortunately, mcrcon can't read the password from a file
-      export MCRCON_PASS="$(cat "''${CREDENTIALS_DIRECTORY}/${instanceName}-rcon-password")"
+      export MCRCON_PASS="$(${bins.systemd-creds} cat "${instanceName}-rcon-password")"
 
       ${bins.mcrcon} save-all
       unset MCRCON_PASS
diff --git a/users/tazjin/cursed/default.nix b/users/tazjin/cursed/default.nix
new file mode 100644
index 000000000000..fa1ae9271fee
--- /dev/null
+++ b/users/tazjin/cursed/default.nix
@@ -0,0 +1,9 @@
+{ depot, ... }:
+
+let
+  inherit (depot.web) bubblegum;
+in
+bubblegum.writeCGI
+{
+  name = "cursed";
+} ./responder.nix
diff --git a/users/tazjin/cursed/responder.nix b/users/tazjin/cursed/responder.nix
new file mode 100644
index 000000000000..9aa6a2d55807
--- /dev/null
+++ b/users/tazjin/cursed/responder.nix
@@ -0,0 +1,76 @@
+{ depot, ... }:
+
+let
+  inherit (depot.users.sterni.nix.html)
+    __findFile
+    esc
+    withDoctype
+    ;
+
+  # CGI envvars: https://www.instanet.com/cgi/env.html
+  method = builtins.getEnv "REQUEST_METHOD";
+  path = builtins.getEnv "PATH_INFO";
+
+  rawQuery = builtins.getEnv "QUERY_STRING";
+  query = with builtins; let
+    pairs = (filter (s: isString s && s != "") (split "&" rawQuery));
+    tuples = filter (l: length l > 0) (map (p: filter (s: isString s) (split "=" p)) pairs);
+    mkAttr = t: {
+      name = elemAt t 0;
+      value = elemAt t 1;
+    };
+  in
+  listToAttrs (map mkAttr tuples);
+
+  default = let {
+  hasQuery = if builtins.length (builtins.attrNames query) > 0 then "?" else "";
+  body = (withDoctype (<html> { lang = "en"; } [
+    (<head> { } [
+      (<title> { } "some cursed nix")
+    ])
+    (<body> { } [
+      (<p> { } "hello volgasprint")
+      (<p> { } [ method " " path hasQuery rawQuery ])
+      (<p> { } (builtins.toJSON query))
+    ])
+  ]));
+  };
+
+  greeter = withDoctype (<html> { lang = "en"; } [
+    (<head> { } [
+      (<title> { } "hello there")
+    ])
+    (<body> { } [
+      (<p> { } "hello ${query.name or "unknown"}")
+    ])
+  ]);
+
+  weather = let {
+  town = query.town or "Kazan";
+  w = builtins.fetchurl "https://wttr.in/${town}?";
+  rendered = with depot.third_party.nixpkgs; runCommand "weather-${town}" { } ''
+    cat ${w} | ${ansi2html}/bin/ansi2html > $out
+  '';
+
+  body = builtins.readFile "${rendered}";
+  };
+
+  routes = {
+    "/other" = (withDoctype (<html> { lang = "en"; } [
+      (<head> { } [
+        (<title> { } "other endpoint")
+      ])
+      (<body> { } [
+        (<p> { } "this is another route")
+      ])
+    ]));
+    "/greeter" = greeter;
+    "/weather" = weather;
+  }."${path}" or default;
+
+in
+depot.web.bubblegum.respond "OK"
+{
+  "Content-Type" = "text/html";
+}
+  routes
diff --git a/users/tazjin/dotfiles/.skip-subtree b/users/tazjin/dotfiles/.skip-subtree
new file mode 100644
index 000000000000..954981f436ee
--- /dev/null
+++ b/users/tazjin/dotfiles/.skip-subtree
@@ -0,0 +1 @@
+Stuff below here is managed manually, without readTree.
diff --git a/users/tazjin/dotfiles/default.nix b/users/tazjin/dotfiles/default.nix
index 9b783a9c857c..d536d3a494cd 100644
--- a/users/tazjin/dotfiles/default.nix
+++ b/users/tazjin/dotfiles/default.nix
@@ -1,3 +1,8 @@
-_: {
+args: {
   dunstrc = ./dunstrc;
+  niri = ./niri.config.kdl;
+  waybar = {
+    config = import ./waybar/config.nix args;
+    style = ./waybar/style.css;
+  };
 }
diff --git a/users/tazjin/dotfiles/niri.config.kdl b/users/tazjin/dotfiles/niri.config.kdl
new file mode 100644
index 000000000000..2ccc56413655
--- /dev/null
+++ b/users/tazjin/dotfiles/niri.config.kdl
@@ -0,0 +1,132 @@
+// https://github.com/YaLTeR/niri/wiki/Configuration:-Overview
+
+input {
+    keyboard {
+        xkb {
+            layout "us,ru"
+            variant "hyper"
+            options "grp:win_space_toggle,compose:ralt,caps:hyper"
+        }
+    }
+
+    touchpad {
+        tap
+    }
+}
+
+layout {
+    gaps 14
+    center-focused-column "never"
+
+    preset-column-widths {
+        proportion 0.33333
+        proportion 0.5
+        proportion 0.66667
+    }
+
+    default-column-width {}
+
+    focus-ring {
+        off
+    }
+
+    border {
+        off
+    }
+}
+
+spawn-at-startup "xwayland-satellite"
+spawn-at-startup "xrandr --output eDP-1 --primary"
+spawn-at-startup "wpaperd" "-d"
+spawn-at-startup "systemctl --user start xss-lock"
+
+environment {
+  QT_QPA_PLATFORM "wayland"
+  DISPLAY ":0"
+  EDITOR "emacsclient"
+}
+
+hotkey-overlay {
+  skip-at-startup
+}
+
+screenshot-path "~/screenshots/screenshot-%Y-%m-%d_%H-%M-%S.png"
+
+animations {
+    slowdown 0.3
+}
+
+binds {
+    Mod+Shift+Slash { show-hotkey-overlay; }
+
+    Mod+T { spawn "emacsclient" "--no-wait" "--create-frame" "--eval" "(vterm)"; }
+    Mod+Shift+T { spawn "alacritty"; } // fallback terminal
+    Mod+D { spawn "xfce4-appfinder" "--disable-server"; }
+    Super+Alt+L { spawn "swaylock" "-fFkl" "-c" "#008080"; }
+
+    // Volume control
+    XF86AudioRaiseVolume allow-when-locked=true { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1+"; }
+    XF86AudioLowerVolume allow-when-locked=true { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1-"; }
+    XF86AudioMute        allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"; }
+    XF86AudioMicMute     allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle"; }
+
+    // Brightness control
+    XF86MonBrightnessUp allow-when-locked=true { spawn "light" "-A" "5"; }
+    Shift+XF86MonBrightnessUp allow-when-locked=true { spawn "light" "-A" "1"; }
+    XF86MonBrightnessDown allow-when-locked=true { spawn "light" "-U" "5"; }
+    Shift+XF86MonBrightnessDown allow-when-locked=true { spawn "light" "-U" "1"; }
+
+    Mod+Q { close-window; }
+
+    Mod+Left      { focus-column-or-monitor-left; }
+    Mod+Right     { focus-column-or-monitor-right; }
+    Mod+Down      { focus-column-or-monitor-right; }
+    Mod+Up        { focus-column-or-monitor-left; }
+    Mod+J         { focus-column-or-monitor-left; }
+    Mod+K         { focus-column-or-monitor-right; }
+    Mod+L         { focus-window-up; }
+    Mod+Semicolon { focus-window-down; }
+
+    Mod+Ctrl+Left  { move-column-left-or-to-monitor-left; }
+    Mod+Ctrl+Right { move-column-right-or-to-monitor-right; }
+    Mod+Ctrl+J     { move-column-left-or-to-monitor-left; }
+    Mod+Ctrl+K     { move-column-right-or-to-monitor-right; }
+
+    Mod+Home { focus-column-first; }
+    Mod+End  { focus-column-last; }
+
+    Mod+Ctrl+Home { move-column-to-first; }
+    Mod+Ctrl+End  { move-column-to-last; }
+
+    // Scroll (or move windows) between columns when holding the modifier down.
+    Mod+WheelScrollDown      cooldown-ms=150 { focus-column-or-monitor-right; }
+    Mod+WheelScrollUp        cooldown-ms=150 { focus-column-or-monitor-left; }
+    Mod+Ctrl+WheelScrollDown cooldown-ms=150 { move-column-right-or-to-monitor-right; }
+    Mod+Ctrl+WheelScrollUp   cooldown-ms=150 { move-column-left-or-to-monitor-left; }
+
+    Mod+Comma  { consume-window-into-column; }
+    Mod+Period { expel-window-from-column; }
+
+    // There are also commands that consume or expel a single window to the side.
+    // Mod+BracketLeft  { consume-or-expel-window-left; }
+    // Mod+BracketRight { consume-or-expel-window-right; }
+
+    Mod+R { switch-preset-column-width; }
+    Mod+Shift+R { reset-window-height; }
+    Mod+F { maximize-column; }
+    Mod+Shift+F { fullscreen-window; }
+    Mod+C { center-column; }
+
+    Mod+Minus { set-column-width "-10%"; }
+    Mod+Equal { set-column-width "+10%"; }
+
+    // Finer height adjustments when in column with other windows.
+    Mod+Shift+Minus { set-window-height "-2%"; }
+    Mod+Shift+Equal { set-window-height "+2%"; }
+
+    Print { screenshot; }
+    Ctrl+Print { screenshot-screen; }
+    Alt+Print { screenshot-window; }
+
+    Mod+Shift+E { quit; }
+}
diff --git a/users/tazjin/dotfiles/waybar/config.nix b/users/tazjin/dotfiles/waybar/config.nix
new file mode 100644
index 000000000000..e65edabe7d19
--- /dev/null
+++ b/users/tazjin/dotfiles/waybar/config.nix
@@ -0,0 +1,51 @@
+{ depot, pkgs, ... }:
+
+let
+  launcher = "${pkgs.xfce4-appfinder}/bin/xfce4-appfinder --disable-server";
+in
+{
+  mainBar = {
+    layer = "top";
+    position = "bottom";
+    modules-left = [ "image#start" "custom/start" ];
+
+    "image#start" = {
+      path = "${depot.third_party.chicago95}/share/icons/Chicago95/panel/24/start-here.png";
+      size = 24;
+      on-click = "xfce4-appfinder --disable-server";
+    };
+
+    "custom/start" = {
+      format = " Start";
+      on-click = "xfce4-appfinder --disable-server";
+    };
+
+    modules-right = [ "pulseaudio" "backlight" "battery" "tray" "clock" ];
+
+    pulseaudio = {
+      on-click = "pavucontrol";
+      # Font "Awesome" speaker icons can't be made to render, I don't care why, emoji time.
+      format = "{volume}% 🎧";
+      format-muted = "{volume}% ";
+    };
+
+    battery = {
+      format = "{capacity}% {icon}";
+      format-icons = [ "" "" "" "" "" ];
+    };
+
+    backlight = {
+      format = "{percent}% {icon}";
+      format-icons = [ "" ];
+      on-scroll-up = "light -A 1";
+      on-scroll-down = "light -U 1";
+    };
+
+    clock.format-alt = "{:%a, %d. %b  %H:%M}";
+
+    tray = {
+      icon-size = 20;
+      spacing = 10;
+    };
+  };
+}
diff --git a/users/tazjin/dotfiles/waybar/style.css b/users/tazjin/dotfiles/waybar/style.css
new file mode 100644
index 000000000000..319ef695e23e
--- /dev/null
+++ b/users/tazjin/dotfiles/waybar/style.css
@@ -0,0 +1,162 @@
+* {
+    /* `otf-font-awesome` is required to be installed for icons */
+    font-family: FontAwesome, Unifont;
+    font-size: 16px;
+}
+
+window#waybar {
+    background-color: #c0c0c0;
+    border-top: 3px solid #dfdfdf;
+    color: #000000;
+    transition-property: background-color;
+    transition-duration: .5s;
+}
+
+window#waybar.hidden {
+    opacity: 0.2;
+}
+
+window#waybar.termite {
+    background-color: #3F3F3F;
+}
+
+window#waybar.chromium {
+    background-color: #000000;
+    border: none;
+}
+
+button {
+    /* Use box-shadow instead of border so the text isn't offset */
+    box-shadow: inset 0 -3px transparent;
+    /* Avoid rounded borders under each button name */
+    border: none;
+    border-radius: 0;
+}
+
+/* https://github.com/Alexays/Waybar/wiki/FAQ#the-workspace-buttons-have-a-strange-hover-effect */
+button:hover {
+    background: inherit;
+    box-shadow: inset 0 -3px #ffffff;
+}
+
+/* you can set a style on hover for any module like this */
+#pulseaudio:hover {
+    background-color: #a37800;
+}
+
+#mode {
+    background-color: #64727D;
+    box-shadow: inset 0 -3px #ffffff;
+}
+
+#clock,
+#battery,
+#cpu,
+#memory,
+#disk,
+#temperature,
+#backlight,
+#network,
+#pulseaudio,
+#wireplumber,
+#custom-media,
+#tray,
+#mode,
+#idle_inhibitor,
+#scratchpad,
+#power-profiles-daemon,
+#mpd {
+    padding: 0 5px;
+    padding-top: 0px;
+    padding-bottom: 0px;
+    /* color: #ffffff; */
+}
+
+#window,
+#workspaces {
+    margin: 0 4px;
+}
+
+#custom-start {
+    padding-bottom: 1px;
+    margin-right: 1px;
+}
+
+/* pop out left "modules" (start button) */
+.modules-left {
+    margin: 3px;
+    border-right: 1px solid gray;
+    border-bottom: 1px solid gray;
+    border-left: 1px solid white;
+    border-top: 1px solid white;
+    font-weight: bold;
+}
+
+/* indent right modules like in the original */
+.modules-right {
+    margin: 3px;
+    border-top: 1px solid gray;
+    border-left: 1px solid gray;
+    border-right: 1px solid white;
+    border-bottom: 1px solid white;
+}
+
+/* If workspaces is the leftmost module, omit left margin */
+.modules-left > widget:first-child > #workspaces {
+    margin-left: 0;
+}
+
+/* If workspaces is the rightmost module, omit right margin */
+.modules-right > widget:last-child > #workspaces {
+    margin-right: 0;
+}
+
+#battery.charging, #battery.plugged {
+    color: #ffffff;
+    background-color: #208C71;
+}
+
+@keyframes blink {
+    to {
+        background-color: #ffffff;
+        color: #000000;
+    }
+}
+
+/* Using steps() instead of linear as a timing function to limit cpu usage */
+#battery.critical:not(.charging) {
+    background-color: #e35f5f;
+    color: #ffffff;
+    animation-name: blink;
+    animation-duration: 0.5s;
+    animation-timing-function: steps(12);
+    animation-iteration-count: infinite;
+    animation-direction: alternate;
+}
+
+label:focus {
+    background-color: #000000;
+}
+
+#pulseaudio.muted {
+    background-color: #808080;
+    color: #000000;
+}
+
+#tray > .passive {
+    -gtk-icon-effect: dim;
+}
+
+#tray > .needs-attention {
+    -gtk-icon-effect: highlight;
+    background-color: #e35f5f;
+}
+
+#idle_inhibitor {
+    background-color: #2d3436;
+}
+
+#idle_inhibitor.activated {
+    background-color: #ecf0f1;
+    color: #2d3436;
+}
diff --git a/users/tazjin/eaglemode/default.nix b/users/tazjin/eaglemode/default.nix
new file mode 100644
index 000000000000..9f59695a40b8
--- /dev/null
+++ b/users/tazjin/eaglemode/default.nix
@@ -0,0 +1,16 @@
+# Derivation for my fully configured Eagle Mode.
+{ depot, ... }:
+
+with depot.tools.eaglemode;
+
+withConfig {
+  config = etcDir {
+    extraPaths = [
+      commands.emacsclient
+      plugins.example
+      plugins.yatracker
+      plugins.qoi
+      plugins.avif
+    ];
+  };
+}
diff --git a/users/tazjin/emacs/config/init.el b/users/tazjin/emacs/config/init.el
index 2af68a9f746e..a0adcf6cfaec 100644
--- a/users/tazjin/emacs/config/init.el
+++ b/users/tazjin/emacs/config/init.el
@@ -72,7 +72,8 @@
 (use-package rainbow-mode)
 (use-package s)
 (use-package string-edit-at-point)
-(use-package term-switcher)
+(use-package term-switcher
+  :bind (:map global-map ("<f5>" . #'ts/switch-to-terminal)))
 
 (use-package undo-tree
   :config (global-undo-tree-mode)
@@ -166,7 +167,7 @@
   (setq common-lisp-hyperspec-root "file:///home/tazjin/docs/lisp/"))
 
 (use-package telega
-  :bind (:map global-map ("s-c" . (lambda (p) (interactive "P")
+  :bind (:map global-map ("C-x c" . (lambda (p) (interactive "P")
                                     (if p (call-interactively #'telega-chat-with)
                                       (telega))))
          :map telega-chat-button-map ("a" . ignore))
@@ -238,8 +239,7 @@
 
 ;; Load all other Emacs configuration. These configurations are
 ;; added to `load-path' by Nix.
-(mapc 'require '(desktop
-                 mail-setup
+(mapc 'require '(mail-setup
                  look-and-feel
                  functions
                  settings
diff --git a/users/tazjin/emacs/default.nix b/users/tazjin/emacs/default.nix
index 17973b8b4869..80db7610a107 100644
--- a/users/tazjin/emacs/default.nix
+++ b/users/tazjin/emacs/default.nix
@@ -3,7 +3,7 @@
 { depot, lib, pkgs, ... }:
 
 pkgs.makeOverridable
-  ({ emacs ? pkgs.emacs29 }:
+  ({ emacs ? pkgs.emacs29-pgtk }:
   let
     emacsPackages = (pkgs.emacsPackagesFor emacs);
     emacsWithPackages = emacsPackages.emacsWithPackages;
@@ -41,6 +41,7 @@ pkgs.makeOverridable
       tree-sitter-rust
       tree-sitter-sql
       tree-sitter-toml
+      tree-sitter-typescript
       tree-sitter-yaml
     ]);
 
diff --git a/users/tazjin/german-string/.gitignore b/users/tazjin/german-string/.gitignore
new file mode 100644
index 000000000000..2f7896d1d136
--- /dev/null
+++ b/users/tazjin/german-string/.gitignore
@@ -0,0 +1 @@
+target/
diff --git a/users/tazjin/german-string/Cargo.lock b/users/tazjin/german-string/Cargo.lock
new file mode 100644
index 000000000000..ffd73ea32472
--- /dev/null
+++ b/users/tazjin/german-string/Cargo.lock
@@ -0,0 +1,399 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "autocfg"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+
+[[package]]
+name = "bit-set"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[package]]
+name = "bitflags"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "errno"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "german-string"
+version = "0.1.0"
+dependencies = [
+ "proptest",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
+[[package]]
+name = "libc"
+version = "0.2.156"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a"
+
+[[package]]
+name = "libm"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "proptest"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d"
+dependencies = [
+ "bit-set",
+ "bit-vec",
+ "bitflags",
+ "lazy_static",
+ "num-traits",
+ "rand",
+ "rand_chacha",
+ "rand_xorshift",
+ "regex-syntax",
+ "rusty-fork",
+ "tempfile",
+ "unarray",
+]
+
+[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
+[[package]]
+name = "quote"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_xorshift"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+
+[[package]]
+name = "rustix"
+version = "0.38.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rusty-fork"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
+dependencies = [
+ "fnv",
+ "quick-error",
+ "tempfile",
+ "wait-timeout",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.74"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "unarray"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "wait-timeout"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "zerocopy"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+dependencies = [
+ "byteorder",
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
diff --git a/users/tazjin/german-string/Cargo.toml b/users/tazjin/german-string/Cargo.toml
new file mode 100644
index 000000000000..8eec963f071a
--- /dev/null
+++ b/users/tazjin/german-string/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "german-string"
+version = "0.1.0"
+edition = "2021"
+
+[dev-dependencies]
+proptest = "1.5.0"
diff --git a/users/tazjin/german-string/default.nix b/users/tazjin/german-string/default.nix
new file mode 100644
index 000000000000..c6cbc8c3c2d0
--- /dev/null
+++ b/users/tazjin/german-string/default.nix
@@ -0,0 +1,5 @@
+{ depot, pkgs, ... }:
+
+depot.third_party.naersk.buildPackage {
+  src = ./.;
+}
diff --git a/users/tazjin/german-string/src/lib.rs b/users/tazjin/german-string/src/lib.rs
new file mode 100644
index 000000000000..328eca309f38
--- /dev/null
+++ b/users/tazjin/german-string/src/lib.rs
@@ -0,0 +1,435 @@
+use std::alloc::Layout;
+use std::cmp::Ordering;
+use std::fmt::{Debug, Formatter};
+
+#[derive(Clone, Copy)]
+#[repr(C)]
+struct GSSmall {
+    len: u32,
+    data: [u8; 12],
+}
+
+#[derive(Clone, Copy)]
+#[repr(transparent)]
+struct StorageClassPtr(usize);
+
+impl StorageClassPtr {
+    fn transient(ptr: *const u8) -> Self {
+        debug_assert!(
+            (ptr as usize & 0b1) == 0,
+            "pointer must be at least 2-byte aligned"
+        );
+        Self(ptr as usize)
+    }
+
+    fn persistent(ptr: *const u8) -> Self {
+        debug_assert!(
+            (ptr as usize & 0b1) == 0,
+            "pointer must be at least 2-byte aligned"
+        );
+        Self((ptr as usize) | 0b1)
+    }
+
+    fn as_ptr(&self) -> *const u8 {
+        (self.0 & !0b1) as *const u8
+    }
+
+    unsafe fn as_mut_ptr(&self) -> *mut u8 {
+        (self.0 & !0b1) as *mut u8
+    }
+
+    fn is_transient(&self) -> bool {
+        (self.0 & 0b1) == 0
+    }
+}
+
+#[derive(Clone, Copy)]
+#[repr(C)]
+struct GSLarge {
+    len: u32,
+    prefix: [u8; 4],
+    data: StorageClassPtr,
+}
+
+const _ASSERT_VARIANTS_SIZE: () = assert!(
+    std::mem::size_of::<GSSmall>() == std::mem::size_of::<GSLarge>(),
+    "German String variants must have the same size"
+);
+
+union GSRepr {
+    small: GSSmall,
+    large: GSLarge,
+}
+
+#[repr(transparent)]
+pub struct GermanString(GSRepr);
+
+const _ASSERT_GSTRING_SIZE: () = assert!(
+    std::mem::size_of::<GermanString>() == 16,
+    "German String should be 16 bytes in size",
+);
+
+impl GermanString {
+    /// Creates a new transient German String from the given slice, copying the
+    /// data in the process.
+    pub fn transient(bytes: &[u8]) -> GermanString {
+        if bytes.len() > u32::MAX as usize {
+            panic!("GermanString maximum length is {} bytes", u32::MAX);
+        }
+
+        if bytes.len() <= 12 {
+            let mut s = GSSmall {
+                len: bytes.len() as u32,
+                data: [0u8; 12],
+            };
+            s.data[..bytes.len()].copy_from_slice(bytes);
+            GermanString(GSRepr { small: s })
+        } else {
+            let layout = Layout::array::<u8>(bytes.len()).unwrap();
+            let mut large = GSLarge {
+                len: bytes.len() as u32,
+                prefix: [0u8; 4],
+                data: unsafe {
+                    let ptr = std::alloc::alloc(layout);
+                    if ptr.is_null() {
+                        std::alloc::handle_alloc_error(layout);
+                    }
+                    std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, bytes.len());
+                    StorageClassPtr::transient(ptr)
+                },
+            };
+
+            large.prefix.copy_from_slice(&bytes[..4]);
+
+            GermanString(GSRepr { large })
+        }
+    }
+
+    /// Creates a new transient German String from the given owned bytes. Short
+    /// strings will be copied into the string representation, long strings will
+    /// be moved out of the given vector without additional allocations.
+    pub fn transient_from_owned(bytes: Vec<u8>) -> GermanString {
+        if bytes.len() > u32::MAX as usize {
+            panic!("GermanString maximum length is {} bytes", u32::MAX);
+        }
+
+        if bytes.len() <= 12 {
+            let mut s = GSSmall {
+                len: bytes.len() as u32,
+                data: [0u8; 12],
+            };
+
+            s.data[..bytes.len()].copy_from_slice(&bytes);
+            GermanString(GSRepr { small: s })
+        } else {
+            let md = std::mem::ManuallyDrop::new(bytes);
+            let mut large = GSLarge {
+                len: md.len() as u32,
+                prefix: [0u8; 4],
+                data: StorageClassPtr::transient(md.as_ptr()),
+            };
+
+            large.prefix.copy_from_slice(&md[..4]);
+            GermanString(GSRepr { large })
+        }
+    }
+
+    /// Creates a persistent German String from a static data buffer.
+    pub fn persistent(bytes: &'static [u8]) -> GermanString {
+        if bytes.len() > u32::MAX as usize {
+            panic!("GermanString maximum length is {} bytes", u32::MAX);
+        }
+
+        if bytes.len() <= 12 {
+            let mut s = GSSmall {
+                len: bytes.len() as u32,
+                data: [0u8; 12],
+            };
+
+            s.data[..bytes.len()].copy_from_slice(&bytes);
+            GermanString(GSRepr { small: s })
+        } else {
+            let mut large = GSLarge {
+                len: bytes.len() as u32,
+                prefix: [0u8; 4],
+                data: StorageClassPtr::persistent(bytes.as_ptr()),
+            };
+
+            large.prefix.copy_from_slice(&bytes[..4]);
+            GermanString(GSRepr { large })
+        }
+    }
+
+    /// Creates a persistent German String by leaking the provided data.
+    pub fn persistent_leak(bytes: Vec<u8>) -> GermanString {
+        if bytes.len() > u32::MAX as usize {
+            panic!("GermanString maximum length is {} bytes", u32::MAX);
+        }
+
+        if bytes.len() <= 12 {
+            let mut s = GSSmall {
+                len: bytes.len() as u32,
+                data: [0u8; 12],
+            };
+
+            s.data[..bytes.len()].copy_from_slice(&bytes);
+            GermanString(GSRepr { small: s })
+        } else {
+            let md = std::mem::ManuallyDrop::new(bytes);
+            let mut large = GSLarge {
+                len: md.len() as u32,
+                prefix: [0u8; 4],
+                data: StorageClassPtr::persistent(md.as_ptr()),
+            };
+
+            large.prefix.copy_from_slice(&md[..4]);
+            GermanString(GSRepr { large })
+        }
+    }
+
+    /// Creates a persistent German String from a static data buffer.
+    pub fn persistent_from_str(s: &'static str) -> GermanString {
+        GermanString::persistent(s.as_bytes())
+    }
+
+    pub fn len(&self) -> usize {
+        // SAFETY: The length field is located in the same location for both
+        // variants, reading it from either is safe.
+        unsafe { self.0.small.len as usize }
+    }
+
+    pub fn as_bytes(&self) -> &[u8] {
+        if self.len() > 12 {
+            unsafe { std::slice::from_raw_parts(self.0.large.data.as_ptr(), self.len()) }
+        } else {
+            unsafe { &self.0.small.data.as_ref()[..self.len()] }
+        }
+    }
+
+    pub fn as_str(&self) -> Result<&str, std::str::Utf8Error> {
+        std::str::from_utf8(self.as_bytes())
+    }
+}
+
+impl Drop for GermanString {
+    fn drop(&mut self) {
+        unsafe {
+            if self.len() > 12 && self.0.large.data.is_transient() {
+                let layout = Layout::array::<u8>(self.len()).unwrap();
+                std::alloc::dealloc(self.0.large.data.as_mut_ptr(), layout);
+            }
+        }
+    }
+}
+
+impl PartialEq for GermanString {
+    fn eq(&self, other: &GermanString) -> bool {
+        if self.len() != other.len() {
+            return false;
+        }
+
+        unsafe {
+            if self.len() <= 12 {
+                return self.0.small.data[..self.len()] == other.0.small.data[..other.len()];
+            }
+            return self.0.large.data.as_ptr() == other.0.large.data.as_ptr()
+                || (self.0.large.prefix == other.0.large.prefix
+                    && self.as_bytes() == other.as_bytes());
+        }
+    }
+}
+
+impl Eq for GermanString {}
+
+impl Ord for GermanString {
+    fn cmp(&self, other: &GermanString) -> Ordering {
+        match (self.len().cmp(&12), other.len().cmp(&12)) {
+            // two small strings
+            (Ordering::Less | Ordering::Equal, Ordering::Less | Ordering::Equal) => unsafe {
+                self.0.small.data[..self.len()].cmp(&other.0.small.data[..other.len()])
+            },
+            // two large strings
+            (Ordering::Greater, Ordering::Greater) => unsafe {
+                match self.0.large.prefix.cmp(&other.0.large.prefix) {
+                    Ordering::Equal => self.as_bytes().cmp(other.as_bytes()),
+                    ordering => ordering,
+                }
+            },
+
+            // LHS large, RHS small
+            (Ordering::Greater, _) => {
+                let prefix_ordering =
+                    unsafe { self.0.large.prefix.as_slice().cmp(&other.0.small.data[..4]) };
+
+                if prefix_ordering != Ordering::Equal {
+                    return prefix_ordering;
+                }
+
+                self.as_bytes().cmp(other.as_bytes())
+            }
+
+            // LHS small, RHS large
+            (_, Ordering::Greater) => {
+                let prefix_ordering =
+                    unsafe { self.0.small.data[..4].cmp(other.0.large.prefix.as_slice()) };
+
+                if prefix_ordering != Ordering::Equal {
+                    return prefix_ordering;
+                }
+
+                self.as_bytes().cmp(other.as_bytes())
+            }
+        }
+    }
+}
+
+impl PartialOrd for GermanString {
+    fn partial_cmp(&self, other: &GermanString) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Debug for GermanString {
+    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
+        String::from_utf8_lossy(self.as_bytes()).fmt(f)
+    }
+}
+
+impl Clone for GermanString {
+    fn clone(&self) -> Self {
+        unsafe {
+            if self.len() <= 12 {
+                return GermanString(GSRepr {
+                    small: self.0.small.clone(),
+                });
+            }
+
+            if self.0.large.data.is_transient() {
+                return GermanString::transient(self.as_bytes());
+            }
+
+            return GermanString(GSRepr {
+                large: self.0.large.clone(),
+            });
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use proptest::prelude::*;
+
+    impl Arbitrary for GermanString {
+        type Parameters = <String as Arbitrary>::Parameters;
+        type Strategy = BoxedStrategy<Self>;
+
+        fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
+            any_with::<String>(args)
+                .prop_map(|s| GermanString::transient(s.as_bytes()))
+                .boxed()
+        }
+    }
+
+    #[test]
+    fn test_empty_string() {
+        let empty = GermanString::transient(b"");
+
+        assert_eq!(empty.len(), 0, "empty string should be empty");
+        assert_eq!(empty.as_bytes(), b"", "empty string should contain nothing");
+        assert_eq!(
+            empty.as_str().expect("empty string is valid UTF-8"),
+            "",
+            "empty string should contain empty string"
+        );
+    }
+
+    #[test]
+    fn test_short_string() {
+        let short = GermanString::transient(b"meow");
+
+        assert_eq!(short.len(), 4, "'meow' is four characters");
+        assert_eq!(
+            short.as_bytes(),
+            b"meow",
+            "short string returns correct bytes"
+        );
+        assert_eq!(
+            short.as_str().expect("'meow' is valid UTF-8"),
+            "meow",
+            "short string returns correct string"
+        );
+    }
+
+    #[test]
+    fn test_long_string() {
+        let input: &str = "This code was written at https://signal.live";
+        let long = GermanString::transient(input.as_bytes());
+
+        assert_eq!(long.len(), 44, "long string has correct length");
+        assert_eq!(
+            long.as_bytes(),
+            input.as_bytes(),
+            "long string returns correct bytes"
+        );
+
+        assert_eq!(
+            long.as_str().expect("input is valid UTF-8"),
+            input,
+            "long string returns correct string"
+        );
+    }
+
+    proptest! {
+        #[test]
+        fn test_roundtrip_vec(input: Vec<u8>) {
+            let gs = GermanString::transient_from_owned(input.clone());
+            assert_eq!(input.len(), gs.len(), "length should match");
+
+            let out = gs.as_bytes().to_owned();
+            assert_eq!(input, out, "roundtrip should yield same bytes");
+        }
+
+        #[test]
+        fn test_roundtrip_string(input: String) {
+            let gs = GermanString::transient_from_owned(input.clone().into_bytes());
+            assert_eq!(input.len(), gs.len(), "length should match");
+
+            let out = String::from_utf8(gs.as_bytes().to_owned())
+              .expect("string should be valid after roundtrip");
+
+            assert_eq!(input, out, "roundtrip should yield same string");
+        }
+
+        // Test [`Eq`] implementation.
+        #[test]
+        fn test_eq(lhs: Vec<u8>, rhs: Vec<u8>) {
+            let lhs_gs = GermanString::transient(lhs.as_slice());
+            let rhs_gs = GermanString::transient(rhs.as_slice());
+
+            assert_eq!(
+                (lhs == rhs),
+                (lhs_gs == rhs_gs),
+                "Eq should match between std::String and GermanString ({:?} == {:?})",
+                lhs, rhs,
+            );
+        }
+
+        #[test]
+        fn test_reflexivity(x: GermanString) {
+            prop_assert!(x == x);
+        }
+
+        #[test]
+        fn test_symmetry(x: GermanString, y: GermanString) {
+            prop_assert_eq!(x == y, y == x);
+        }
+
+        #[test]
+        fn test_transitivity(x: GermanString, y: GermanString, z: GermanString) {
+            if x == y && y == z {
+                assert!(x == z);
+            }
+        }
+    }
+}
diff --git a/users/tazjin/home/shared.nix b/users/tazjin/home/shared.nix
index 38d8add4ac2f..a5aa5296a132 100644
--- a/users/tazjin/home/shared.nix
+++ b/users/tazjin/home/shared.nix
@@ -5,6 +5,8 @@
 
 
 let
+  inherit (depot.third_party) chicago95;
+
   # URL handler to open `tg://` URLs in telega.el
   telega-launcher = pkgs.writeShellScriptBin "telega-launcher" ''
     echo "Opening ''${1} in telega.el ..."
@@ -38,12 +40,6 @@ in
     '';
   };
 
-  services.screen-locker = {
-    enable = true;
-    inactiveInterval = 10; # minutes
-    lockCmd = "${depot.users.tazjin.screenLock}/bin/tazjin-screen-lock";
-  };
-
   home.packages = [ telega-launcher ];
 
   xdg.desktopEntries.telega-launcher = {
@@ -65,13 +61,35 @@ in
     };
   };
 
-  services.picom = {
+  programs.wpaperd = {
+    enable = true;
+    settings = {
+      default = {
+        duration = "1d";
+        mode = "center";
+        sorting = "random";
+      };
+
+      any.path = ../wallpapers;
+    };
+  };
+
+  programs.waybar = {
     enable = true;
-    vSync = true;
-    backend = "glx";
+    settings = depot.users.tazjin.dotfiles.waybar.config;
+    style = depot.users.tazjin.dotfiles.waybar.style;
+    systemd.enable = true;
   };
+  systemd.user.services.waybar.Unit.After = lib.mkForce [ "niri.service" ];
 
-  services.syncthing.enable = true;
+
+  services.swayidle = let cmd = "${pkgs.swaylock}/bin/swaylock -fFkl -c 008080"; in {
+    enable = true;
+    events = [
+      { event = "before-sleep"; command = cmd; }
+      { event = "lock"; command = cmd; }
+    ];
+  };
 
   # Enable the dunst notification daemon, but force the
   # configuration file separately instead of going via the strange
@@ -84,6 +102,18 @@ in
     '';
   };
 
+  gtk = {
+    enable = true;
+    theme.name = "Chicago95";
+    theme.package = chicago95;
+
+    iconTheme.name = "Chicago95-tux";
+    iconTheme.package = chicago95;
+
+    cursorTheme.name = lib.mkDefault "Chicago95_Animated_Hourglass_Cursors";
+    cursorTheme.package = chicago95;
+  };
+
   systemd.user.startServices = true;
 
   # Previous default version, see https://github.com/nix-community/home-manager/blob/master/docs/release-notes/rl-2211.adoc
diff --git a/users/tazjin/home/zamalek.nix b/users/tazjin/home/zamalek.nix
index d24de945bb28..98da5e6b233e 100644
--- a/users/tazjin/home/zamalek.nix
+++ b/users/tazjin/home/zamalek.nix
@@ -8,4 +8,6 @@
     depot.users.tazjin.home.shared
     depot.users.tazjin.home.persistence
   ];
+
+  gtk.cursorTheme.name = lib.mkForce "Chicago95_Animated_Hourglass_Cursors_HiDPI";
 }
diff --git a/users/tazjin/niri-reap/.gitignore b/users/tazjin/niri-reap/.gitignore
new file mode 100644
index 000000000000..2f7896d1d136
--- /dev/null
+++ b/users/tazjin/niri-reap/.gitignore
@@ -0,0 +1 @@
+target/
diff --git a/users/tazjin/niri-reap/Cargo.lock b/users/tazjin/niri-reap/Cargo.lock
new file mode 100644
index 000000000000..d2f5c53075f2
--- /dev/null
+++ b/users/tazjin/niri-reap/Cargo.lock
@@ -0,0 +1,104 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "niri-ipc"
+version = "0.1.8"
+source = "git+https://github.com/YaLTeR/niri.git#370fd4e172ec3daf9dc9c75dc0555fe91182f731"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "niri-reap"
+version = "0.1.0"
+dependencies = [
+ "niri-ipc",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+
+[[package]]
+name = "serde"
+version = "1.0.210"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.210"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.128"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
+dependencies = [
+ "itoa",
+ "memchr",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
diff --git a/users/tazjin/niri-reap/Cargo.toml b/users/tazjin/niri-reap/Cargo.toml
new file mode 100644
index 000000000000..20512685b48d
--- /dev/null
+++ b/users/tazjin/niri-reap/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "niri-reap"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+niri-ipc = { git = "https://github.com/YaLTeR/niri.git", version = "0.1.8" }
diff --git a/users/tazjin/niri-reap/default.nix b/users/tazjin/niri-reap/default.nix
new file mode 100644
index 000000000000..b2a0594b7748
--- /dev/null
+++ b/users/tazjin/niri-reap/default.nix
@@ -0,0 +1,13 @@
+{ depot, pkgs, ... }:
+
+pkgs.rustPlatform.buildRustPackage {
+  name = "niri-reap";
+  src = depot.third_party.gitignoreSource ./.;
+
+  cargoLock = {
+    lockFile = ./Cargo.lock;
+    outputHashes = {
+      "niri-ipc-0.1.8" = "sha256:0wyl0mpk9hg67bvj7q120wanrdqn3ls9zv9vjv9yxp11kan5pi1q";
+    };
+  };
+}
diff --git a/users/tazjin/niri-reap/src/main.rs b/users/tazjin/niri-reap/src/main.rs
new file mode 100644
index 000000000000..d89b18fc57cf
--- /dev/null
+++ b/users/tazjin/niri-reap/src/main.rs
@@ -0,0 +1,76 @@
+use niri_ipc::socket::Socket;
+use niri_ipc::{Action, Reply, Request, Response, Window, Workspace};
+
+fn sock() -> Socket {
+    Socket::connect().expect("could not connect to Niri socket")
+}
+
+fn list_workspaces() -> Vec<Workspace> {
+    let (reply, _) = sock()
+        .send(Request::Workspaces)
+        .expect("failed to send workspace request");
+
+    match reply {
+        Reply::Err(err) => panic!("failed to list workspaces: {}", err),
+        Reply::Ok(Response::Workspaces(w)) => w,
+        Reply::Ok(other) => panic!("unexpected reply from Niri: {:#?}", other),
+    }
+}
+
+fn list_windows() -> Vec<Window> {
+    let (reply, _) = sock()
+        .send(Request::Windows)
+        .expect("failed to send window request");
+
+    match reply {
+        Reply::Err(err) => panic!("failed to list windows: {}", err),
+        Reply::Ok(Response::Windows(w)) => w,
+        Reply::Ok(other) => panic!("unexpected reply from Niri: {:#?}", other),
+    }
+}
+
+fn reap_window(window: u64, workspace: u64) {
+    let (reply, _) = sock()
+        .send(Request::Action(Action::MoveWindowToWorkspace {
+            window_id: Some(window),
+            reference: niri_ipc::WorkspaceReferenceArg::Id(workspace),
+        }))
+        .expect("failed to send window move request");
+
+    reply.expect("failed to move window to workspace");
+}
+
+fn main() {
+    let workspaces = list_workspaces();
+
+    let active_workspace = workspaces
+        .iter()
+        .filter(|w| w.is_focused)
+        .next()
+        .expect("expected an active workspace");
+
+    let orphan_workspaces = workspaces
+        .iter()
+        .filter(|w| w.output == active_workspace.output)
+        // Only select workspaces that are further down, to avoid issues with
+        // indices changing during the operation.
+        .filter(|w| w.idx > active_workspace.idx)
+        .map(|w| w.id)
+        .collect::<Vec<_>>();
+
+    if orphan_workspaces.is_empty() {
+        return;
+    }
+
+    let reapable = list_windows()
+        .into_iter()
+        .filter(|w| match w.workspace_id {
+            Some(id) => orphan_workspaces.contains(&id),
+            None => true,
+        })
+        .collect::<Vec<_>>();
+
+    for window in reapable.iter().rev() {
+        reap_window(window.id, active_workspace.id);
+    }
+}
diff --git a/users/tazjin/nixos/arbat/default.nix b/users/tazjin/nixos/arbat/default.nix
index 73bed4573ac1..c87aa445c29c 100644
--- a/users/tazjin/nixos/arbat/default.nix
+++ b/users/tazjin/nixos/arbat/default.nix
@@ -55,7 +55,7 @@ in
 
   hardware = {
     enableRedistributableFirmware = true;
-    opengl.enable = true;
+    graphics.enable = true;
     bluetooth.enable = true;
   };
 
@@ -66,8 +66,6 @@ in
     mouse.naturalScrolling = false;
     mouse.disableWhileTyping = true;
   };
-  # services.xserver.libinput.touchpad.clickMethod = "clickfinger";
-  # services.xserver.libinput.touchpad.tapping = false;
 
   nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
   system.stateVersion = "24.11";
diff --git a/users/tazjin/nixos/default.nix b/users/tazjin/nixos/default.nix
index 29b6a0e83b10..6bca09d8f129 100644
--- a/users/tazjin/nixos/default.nix
+++ b/users/tazjin/nixos/default.nix
@@ -4,10 +4,12 @@ let systemFor = sys: (depot.ops.nixos.nixosFor sys).system;
 in depot.nix.readTree.drvTargets {
   arbatSystem = systemFor depot.users.tazjin.nixos.arbat;
   camdenSystem = systemFor depot.users.tazjin.nixos.camden;
-  frogSystem = systemFor depot.users.tazjin.nixos.frog;
   tverskoySystem = systemFor depot.users.tazjin.nixos.tverskoy;
   zamalekSystem = systemFor depot.users.tazjin.nixos.zamalek;
   koptevoRaw = depot.ops.nixos.nixosFor depot.users.tazjin.nixos.koptevo;
   koptevoSystem = systemFor depot.users.tazjin.nixos.koptevo;
   khamovnikSystem = systemFor depot.users.tazjin.nixos.khamovnik;
+
+  # no need to build this while the machine is in storage
+  # frogSystem = systemFor depot.users.tazjin.nixos.frog;
 }
diff --git a/users/tazjin/nixos/frog/default.nix b/users/tazjin/nixos/frog/default.nix
index 349c5eb6d81a..ce82d749f408 100644
--- a/users/tazjin/nixos/frog/default.nix
+++ b/users/tazjin/nixos/frog/default.nix
@@ -41,9 +41,9 @@ lib.fix (self: {
   hardware = {
     cpu.amd.updateMicrocode = true;
     enableRedistributableFirmware = true;
-    opengl = {
+    graphics = {
       enable = true;
-      driSupport32Bit = true;
+      enable32Bit = true;
     };
 
     pulseaudio = {
diff --git a/users/tazjin/nixos/khamovnik/default.nix b/users/tazjin/nixos/khamovnik/default.nix
index 19d7f5822b4a..dcb19be2ae9f 100644
--- a/users/tazjin/nixos/khamovnik/default.nix
+++ b/users/tazjin/nixos/khamovnik/default.nix
@@ -43,6 +43,8 @@ in
       "rtsx_pci_sdmmc"
     ];
     kernelModules = [ "kvm-intel" ];
+
+    tmp.cleanOnBoot = true;
   };
 
   fileSystems = {
@@ -64,12 +66,13 @@ in
   tvl.cache.enable = true;
 
   networking.hostName = "khamovnik";
+  networking.networkmanager.enable = true;
 
   nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
   powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
   hardware.cpu.intel.updateMicrocode = true;
   hardware.enableRedistributableFirmware = true;
-  hardware.opengl.extraPackages = with pkgs; [
+  hardware.graphics.extraPackages = with pkgs; [
     intel-compute-runtime
     intel-media-driver
     intel-vaapi-driver
@@ -116,6 +119,13 @@ in
   # Try to work around Intel CPU throttling bugs
   services.throttled.enable = true;
 
+  # Try to get suspend to work more reliably
+  services.logind = {
+    lidSwitch = "suspend";
+    lidSwitchDocked = "suspend";
+    lidSwitchExternalPower = "suspend";
+  };
+
   virtualisation.docker.enable = true;
 
   hardware.bluetooth.enable = true;
@@ -128,5 +138,7 @@ in
     protobuf
   ];
 
+  programs.adb.enable = true;
+
   system.stateVersion = "23.05"; # Did you read the comment?
 }
diff --git a/users/tazjin/nixos/koptevo/default.nix b/users/tazjin/nixos/koptevo/default.nix
index ea8dfd4bd809..d1fe793915a7 100644
--- a/users/tazjin/nixos/koptevo/default.nix
+++ b/users/tazjin/nixos/koptevo/default.nix
@@ -11,12 +11,12 @@ in
   imports = [
     (mod "quassel.nix")
     (mod "www/base.nix")
-    (mod "www/tazj.in.nix")
     (usermod "airsonic.nix")
     (usermod "geesefs.nix")
+    (usermod "homepage.nix")
+    (usermod "miniflux.nix")
     (usermod "predlozhnik.nix")
     (usermod "tgsa.nix")
-    (usermod "miniflux.nix")
     (depot.third_party.agenix.src + "/modules/age.nix")
   ];
 
@@ -62,7 +62,7 @@ in
     domain = "tazj.in";
     useDHCP = true;
     firewall.enable = true;
-    firewall.allowedTCPPorts = [ 22 80 443 ];
+    firewall.allowedTCPPorts = [ 22 80 443 8776 9443 ];
 
     wireless.enable = true;
     wireless.networks."How do I computer fast?" = {
@@ -72,8 +72,22 @@ in
 
   time.timeZone = "UTC";
 
-  security.acme.acceptTerms = true;
-  security.acme.defaults.email = lib.mkForce "acme@tazj.in";
+  security.acme = {
+    acceptTerms = true;
+    defaults.email = lib.mkForce "acme@tazj.in";
+
+    # wildcard cert for usage with Yggdrasil services
+    certs."y.tazj.in" = {
+      dnsProvider = "yandexcloud";
+      credentialFiles.YANDEX_CLOUD_IAM_TOKEN_FILE = "/run/agenix/lego-yandex";
+      extraDomainNames = [ "*.y.tazj.in" ];
+
+      # folder tvl/tazjin-private/default
+      environmentFile = builtins.toFile "lego-yandex-env" ''
+        YANDEX_CLOUD_FOLDER_ID=b1gq41rsbggeum4qafnh
+      '';
+    };
+  };
 
   programs.fish.enable = true;
 
@@ -84,11 +98,14 @@ in
     openssh.authorizedKeys.keys = depot.users.tazjin.keys.all;
   };
 
+  users.users.nginx.extraGroups = [ "acme" ];
+
   age.secrets =
     let
       secretFile = name: depot.users.tazjin.secrets."${name}.age";
     in
     {
+      lego-yandex.file = secretFile "lego-yandex";
       tgsa-yandex.file = secretFile "tgsa-yandex";
     };
 
@@ -169,16 +186,116 @@ in
   # List packages installed in system profile. To search, run:
   # $ nix search wget
   environment.systemPackages = with pkgs; [
+    bat
     curl
+    emacs-nox
     htop
     jq
-    nmap
-    bat
-    emacs-nox
     nano
+    nmap
+    radicle-node
     wget
   ];
 
+  # configure Yggdrasil network
+  services.yggdrasil = {
+    enable = true;
+    persistentKeys = true;
+    openMulticastPort = true;
+
+    settings = {
+      Listen = [ "tls://[::]:9443" ]; # yggd
+      IfName = "ygg0";
+      Peers = [
+        "quic://ygg-msk-1.averyan.ru:8364"
+        "tls://ekb.itrus.su:7992"
+        "tls://s-mow-1.sergeysedoy97.ru:65534"
+      ];
+
+      MulticastInterfaces = [{
+        Regex = "enp.*";
+        Beacon = true;
+        Listen = true;
+        Port = 0;
+      }];
+
+      AllowedPublicKeys = [
+        "573fd89392e2741ead4edd85034c91c88f1e560d991bbdbf1fccb6233db4d325" # khamovnik
+      ];
+    };
+  };
+
+  # TODO(tazjin): move this to a module for radicle stuff
+  services.radicle = {
+    enable = true;
+    publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILHs6jSvMdtu9oJCt48etEs8ExjfGY5PmWQsRzFleogS";
+    privateKeyFile = "/etc/secrets/radicle"; # TODO: to manage, or not to manage ...
+
+    settings = {
+      web.pinned.repositories = [
+        "rad:z3r5zMi9U3az3i4cPKxMcA3K7xx9L" # depot
+        "rad:z2mdnBK1tX6pibdBfRct3ThCgheHu" # tvix-go
+      ];
+
+      node = {
+        alias = "rad.tazj.in";
+        seedingPolicy.default = "block";
+      };
+    };
+
+    node = {
+      openFirewall = true;
+      listenAddress = "[::]";
+    };
+
+    httpd = {
+      enable = true;
+      listenAddress = "127.0.0.1";
+      listenPort = 7235; # radl
+    };
+  };
+
+  services.nginx.virtualHosts."rad.tazj.in" = {
+    enableACME = true;
+    forceSSL = true;
+    locations."/".proxyPass = "http://127.0.0.1:7235";
+  };
+
+  services.nginx.virtualHosts."rad.y.tazj.in" = {
+    enableSSL = true;
+    useACMEHost = "y.tazj.in";
+    locations = config.services.nginx.virtualHosts."rad.tazj.in".locations;
+  };
+
+  services.nginx.virtualHosts."src.tazj.in" = {
+    enableACME = true;
+    forceSSL = true;
+    root = depot.third_party.radicle-explorer.withPreferredSeeds [{
+      hostname = "rad.tazj.in";
+      port = 443;
+      scheme = "https";
+    }];
+
+    locations."/" = {
+      index = "index.html";
+      extraConfig = ''
+        try_files $uri $uri/ /index.html;
+      '';
+    };
+  };
+
+  services.nginx.virtualHosts."src.y.tazj.in" = {
+    enableSSL = true;
+    useACMEHost = "y.tazj.in";
+    root = depot.third_party.radicle-explorer.withPreferredSeeds [{
+      hostname = "rad.y.tazj.in";
+      port = 443;
+      scheme = "https";
+    }];
+
+    locations = config.services.nginx.virtualHosts."src.tazj.in".locations;
+  };
+
   programs.mtr.enable = true;
   programs.mosh.enable = true;
   zramSwap.enable = true;
diff --git a/users/tazjin/nixos/modules/desktop.nix b/users/tazjin/nixos/modules/desktop.nix
index 296960a44345..dfbc12dfd8e9 100644
--- a/users/tazjin/nixos/modules/desktop.nix
+++ b/users/tazjin/nixos/modules/desktop.nix
@@ -10,43 +10,62 @@
       pulse.enable = true;
     };
 
-    redshift.enable = true;
     blueman.enable = true;
     libinput.enable = true;
 
     xserver = {
-      enable = true;
-      xkb.layout = "us";
-      xkb.options = "caps:super";
-
-      displayManager = {
-        # Give EXWM permission to control the session.
-        sessionCommands = "${pkgs.xorg.xhost}/bin/xhost +SI:localuser:$USER";
-        lightdm.enable = true;
-        # lightdm.greeters.gtk.clock-format = "%H:%M"; # TODO(tazjin): TZ?
-      };
-
-      windowManager.session = lib.singleton {
-        name = "exwm";
-        start = "${config.tazjin.emacs}/bin/tazjins-emacs --internal-border=0 --border-width=0";
+      enable = true; # wayland doesn't work otherwise ...?!
+      displayManager.gdm = {
+        enable = true;
+        wayland = true;
       };
-      desktopManager.xfce.enable = true;
     };
   };
 
-  # Set variables to enable EXWM-XIM and other Emacs features.
-  environment.sessionVariables = {
-    XMODIFIERS = "@im=exwm-xim";
-    GTK_IM_MODULE = "xim";
-    QT_IM_MODULE = "xim";
-    CLUTTER_IM_MODULE = "xim";
-    EDITOR = "emacsclient";
-    _JAVA_AWT_WM_NONREPARENTING = "1";
-  };
+  services.displayManager.sessionPackages = [ pkgs.niri ];
+
+  programs.xwayland.enable = true;
+
+  environment.systemPackages = with pkgs; [
+    # core packages
+    niri
+    xwayland-satellite
+    swaylock
+
+    # support tooling
+    alacritty
+    qt5.qtwayland
+    swayidle
+    waybar
+    wdisplays
+    wl-mirror
+    xfce.xfce4-appfinder
+  ];
 
   # Do not restart the display manager automatically
   systemd.services.display-manager.restartIfChanged = lib.mkForce false;
 
+  # pipewire MUST start before niri, otherwise screen sharing doesn't work
+  systemd.user.services.pipewire.wantedBy = [ "niri.service" ];
+  systemd.user.services.pipewire.before = [ "niri.service" ];
+
+  # enable "desktop portals", which are important somehow
+  xdg.portal = {
+    enable = true;
+    extraPortals = with pkgs; [
+      xdg-desktop-portal-gtk
+      xdg-desktop-portal-gnome
+    ];
+    config.common.default = "*";
+  };
+
+  # swaylock needs an empty PAM configuration, otherwise it locks the user out
+  security.pam.services.swaylock = { };
+
+  # enable theming support for Qt that is compatible with Chicago95 theme
+  qt.enable = true;
+  qt.platformTheme = "qt5ct";
+
   # If something needs more than 10s to stop it should probably be
   # killed.
   systemd.extraConfig = ''
diff --git a/users/tazjin/nixos/modules/fonts.nix b/users/tazjin/nixos/modules/fonts.nix
index ee1b84e581f1..36b4cbe969ce 100644
--- a/users/tazjin/nixos/modules/fonts.nix
+++ b/users/tazjin/nixos/modules/fonts.nix
@@ -1,15 +1,17 @@
 # Attempt at configuring reasonable font-rendering.
 
-{ pkgs, ... }:
+{ depot, pkgs, ... }:
 
 {
   fonts = {
     packages = with pkgs; [
       corefonts
       dejavu_fonts
+      font-awesome
       jetbrains-mono
       noto-fonts-cjk
-      noto-fonts-emoji
+      noto-fonts-color-emoji
+      noto-fonts-monochrome-emoji
     ];
 
     fontconfig = {
diff --git a/users/tazjin/nixos/modules/home-config.nix b/users/tazjin/nixos/modules/home-config.nix
index 77fe3f69bcd2..9aa1cab46d66 100644
--- a/users/tazjin/nixos/modules/home-config.nix
+++ b/users/tazjin/nixos/modules/home-config.nix
@@ -6,7 +6,7 @@
   users.users.tazjin = {
     isNormalUser = true;
     createHome = true;
-    extraGroups = [ "wheel" "networkmanager" "video" "adbusers" ];
+    extraGroups = [ "wheel" "networkmanager" "video" "adbusers" "yggdrasil" ];
     uid = 1000;
     shell = pkgs.fish;
     initialHashedPassword = "$2b$05$1eBPdoIgan/C/L8JFqIHBuVscQyTKw1L/4VBlzlLvLBEf6CXS3EW6";
diff --git a/users/tazjin/nixos/modules/homepage.nix b/users/tazjin/nixos/modules/homepage.nix
new file mode 100644
index 000000000000..65191d6e7087
--- /dev/null
+++ b/users/tazjin/nixos/modules/homepage.nix
@@ -0,0 +1,59 @@
+# serve tazjin's website & blog
+{ depot, config, lib, pkgs, ... }:
+
+let
+  extraConfig = ''
+    location = /en/rss.xml {
+      return 301 https://tazj.in/feed.atom;
+    }
+
+    ${depot.users.tazjin.blog.oldRedirects}
+    location /blog/ {
+      alias ${depot.users.tazjin.blog.rendered}/;
+
+      if ($request_uri ~ ^/(.*)\.html$) {
+        return 302 /$1;
+      }
+
+      try_files $uri $uri.html $uri/ =404;
+    }
+
+    location = /predlozhnik {
+      return 302 https://predlozhnik.ru;
+    }
+
+    # redirect for easier entry on a TV
+    location = /tv {
+      return 302 https://tazj.in/blobs/play.html;
+    }
+
+    # Temporary place for serving static files.
+    location /blobs/ {
+      alias /var/lib/tazjins-blobs/;
+    }
+  '';
+in
+{
+  config = {
+    services.nginx.virtualHosts."tazj.in" = {
+      enableACME = true;
+      forceSSL = true;
+      root = depot.users.tazjin.homepage;
+      serverAliases = [ "www.tazj.in" ];
+      inherit extraConfig;
+    };
+
+    services.nginx.virtualHosts."y.tazj.in" = {
+      enableSSL = true;
+      useACMEHost = "y.tazj.in";
+      root = depot.users.tazjin.homepage;
+      inherit extraConfig;
+    };
+
+    services.nginx.virtualHosts."git.tazj.in" = {
+      enableACME = true;
+      forceSSL = true;
+      extraConfig = "return 301 https://code.tvl.fyi$request_uri;";
+    };
+  };
+}
diff --git a/users/tazjin/nixos/modules/physical.nix b/users/tazjin/nixos/modules/physical.nix
index 5ec527fa7cf7..92b22112d4f4 100644
--- a/users/tazjin/nixos/modules/physical.nix
+++ b/users/tazjin/nixos/modules/physical.nix
@@ -20,11 +20,12 @@ in
     environment.systemPackages =
       # programs from the depot
       (with depot; [
-        users.tazjin.screenLock
-        users.tazjin.chase-geese
         config.tazjin.emacs
         third_party.agenix.cli
         tools.when
+        users.tazjin.chase-geese
+        users.tazjin.eaglemode
+        users.tazjin.screenLock
       ]) ++
 
       # programs from nixpkgs
@@ -74,6 +75,7 @@ in
         pulseaudio # for pactl
         pwgen
         quasselClient
+        radicle-node
         rink
         ripgrep
         rustup
@@ -100,6 +102,13 @@ in
     # run manually patchelfed binaries
     environment.stub-ld.enable = false;
 
+    # Enable yggdrasil network.
+    services.yggdrasil = {
+      enable = true;
+      persistentKeys = true;
+      settings.IfName = "ygg0";
+    };
+
     programs = {
       fish.enable = true;
       mosh.enable = true;
diff --git a/users/tazjin/nixos/tverskoy/default.nix b/users/tazjin/nixos/tverskoy/default.nix
index 733929219a3a..c074fd9c5d70 100644
--- a/users/tazjin/nixos/tverskoy/default.nix
+++ b/users/tazjin/nixos/tverskoy/default.nix
@@ -93,9 +93,9 @@ lib.fix (self: {
     enableRedistributableFirmware = true;
     bluetooth.enable = true;
 
-    opengl = {
+    graphics = {
       enable = true;
-      driSupport32Bit = true;
+      enable32Bit = true;
 
       extraPackages = with pkgs; [
         vaapiVdpau
diff --git a/users/tazjin/nixos/zamalek/default.nix b/users/tazjin/nixos/zamalek/default.nix
index 29effaa9bdd7..ebaf2d2cfa6d 100644
--- a/users/tazjin/nixos/zamalek/default.nix
+++ b/users/tazjin/nixos/zamalek/default.nix
@@ -71,11 +71,11 @@ in
     cpu.intel.updateMicrocode = true;
     bluetooth.enable = true;
     enableRedistributableFirmware = true;
-    opengl.enable = true;
+    graphics.enable = true;
   };
 
-  services.xserver.libinput.touchpad.clickMethod = "clickfinger";
-  services.xserver.libinput.touchpad.tapping = false;
+  services.libinput.touchpad.clickMethod = "clickfinger";
+  services.libinput.touchpad.tapping = false;
   services.avahi.enable = true;
   services.tailscale.enable = true;
 
diff --git a/users/tazjin/secrets/lego-yandex.age b/users/tazjin/secrets/lego-yandex.age
new file mode 100644
index 000000000000..10524a9577c2
--- /dev/null
+++ b/users/tazjin/secrets/lego-yandex.age
Binary files differdiff --git a/users/tazjin/secrets/secrets.nix b/users/tazjin/secrets/secrets.nix
index 12f12f721c6c..a29bd30b7766 100644
--- a/users/tazjin/secrets/secrets.nix
+++ b/users/tazjin/secrets/secrets.nix
@@ -13,4 +13,5 @@ in
   "geesefs-tazjins-files.age".publicKeys = allKeys;
   "miniflux.age".publicKeys = allKeys;
   "tgsa-yandex.age".publicKeys = allKeys;
+  "lego-yandex.age".publicKeys = allKeys;
 }
diff --git a/users/tazjin/wallpapers/alphasoft.webp b/users/tazjin/wallpapers/alphasoft.webp
new file mode 100644
index 000000000000..10c404eff0ab
--- /dev/null
+++ b/users/tazjin/wallpapers/alphasoft.webp
Binary files differdiff --git a/users/tazjin/wallpapers/svema_02_big.webp b/users/tazjin/wallpapers/svema_02_big.webp
new file mode 100644
index 000000000000..5b7f18715c9d
--- /dev/null
+++ b/users/tazjin/wallpapers/svema_02_big.webp
Binary files differdiff --git a/users/tazjin/wallpapers/svema_07_big.webp b/users/tazjin/wallpapers/svema_07_big.webp
new file mode 100644
index 000000000000..0706543473e5
--- /dev/null
+++ b/users/tazjin/wallpapers/svema_07_big.webp
Binary files differdiff --git a/users/tazjin/wallpapers/svema_09_big.webp b/users/tazjin/wallpapers/svema_09_big.webp
new file mode 100644
index 000000000000..4983efef29b8
--- /dev/null
+++ b/users/tazjin/wallpapers/svema_09_big.webp
Binary files differdiff --git a/users/tazjin/wallpapers/svema_14_big.webp b/users/tazjin/wallpapers/svema_14_big.webp
new file mode 100644
index 000000000000..c74542807c29
--- /dev/null
+++ b/users/tazjin/wallpapers/svema_14_big.webp
Binary files differdiff --git a/users/wpcarro/nixos/ava/default.nix b/users/wpcarro/nixos/ava/default.nix
index d2f743503da4..457947607e7e 100644
--- a/users/wpcarro/nixos/ava/default.nix
+++ b/users/wpcarro/nixos/ava/default.nix
@@ -76,9 +76,6 @@ in
     };
   };
 
-  # Enable sound.
-  hardware.pulseaudio.enable = true;
-
   users.mutableUsers = true;
   users.users.root.openssh.authorizedKeys.keys = with wpcarro.keys; [
     iphone
diff --git a/users/wpcarro/nixos/kyoko/default.nix b/users/wpcarro/nixos/kyoko/default.nix
index 323f57eeb210..024276afddaf 100644
--- a/users/wpcarro/nixos/kyoko/default.nix
+++ b/users/wpcarro/nixos/kyoko/default.nix
@@ -79,9 +79,6 @@ in
     };
   };
 
-  # Enable sound.
-  hardware.pulseaudio.enable = true;
-
   users.mutableUsers = true;
   users.users.root.openssh.authorizedKeys.keys = with wpcarro.keys; [
     iphone
diff --git a/users/wpcarro/nixos/marcus/default.nix b/users/wpcarro/nixos/marcus/default.nix
index 22bc1ca5e2ce..491c010ac871 100644
--- a/users/wpcarro/nixos/marcus/default.nix
+++ b/users/wpcarro/nixos/marcus/default.nix
@@ -50,13 +50,14 @@ in
       interval = "1d";
     };
 
+    libinput = {
+      enable = true;
+      touchpad.naturalScrolling = false;
+      touchpad.tapping = false;
+    };
+
     xserver = {
       enable = true;
-      libinput = {
-        enable = true;
-        touchpad.naturalScrolling = false;
-        touchpad.tapping = false;
-      };
       xkb.layout = "us";
       xkb.options = "caps:escape";
       displayManager = {
@@ -78,9 +79,6 @@ in
     };
   };
 
-  # Enable sound.
-  hardware.pulseaudio.enable = true;
-
   users.mutableUsers = true;
   users.users.wpcarro = {
     isNormalUser = true;
diff --git a/users/wpcarro/nixos/tarasco/default.nix b/users/wpcarro/nixos/tarasco/default.nix
index 257201a6c6db..75f19aa6e3d6 100644
--- a/users/wpcarro/nixos/tarasco/default.nix
+++ b/users/wpcarro/nixos/tarasco/default.nix
@@ -72,9 +72,6 @@ in
     };
   };
 
-  # Enable sound.
-  hardware.pulseaudio.enable = true;
-
   users.mutableUsers = true;
   users.users.root.openssh.authorizedKeys.keys = with wpcarro.keys; [
     ava
diff --git a/users/yl3dy/OWNERS b/users/yl3dy/OWNERS
new file mode 100644
index 000000000000..686f0179a665
--- /dev/null
+++ b/users/yl3dy/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+yl3dy
diff --git a/users/yl3dy/test.txt b/users/yl3dy/test.txt
new file mode 100644
index 000000000000..57a1fac55ce4
--- /dev/null
+++ b/users/yl3dy/test.txt
@@ -0,0 +1 @@
+Some gpg-signed text
diff --git a/views/README.md b/views/README.md
index a6ebd93a7788..57fcbb476e38 100644
--- a/views/README.md
+++ b/views/README.md
@@ -8,9 +8,8 @@ individual content and usage information.
 Testing changes locally
 -----------------------
 
-Generally, when iterating on these files, it's best to locally invoke `josh-
-filter` (from `//third_party//josh`) locally to inspect how the workspace would
-look like:
+Generally, when iterating on these files, it's best to invoke `josh-filter`
+(from the `josh` package) locally to inspect how the workspace would look like:
 
   - Commit your changes. This is required, as `josh-filter` operates on your
     `HEAD`, not working directory state.
diff --git a/web/atward/indexHtml/default.nix b/web/atward/indexHtml/default.nix
index 3af808b89831..801faf57dcf2 100644
--- a/web/atward/indexHtml/default.nix
+++ b/web/atward/indexHtml/default.nix
@@ -35,65 +35,13 @@ depot.web.tvl.template {
     <kbd>cl</kbd>), atward will redirect to the appropriate `tvl.fyi`
     domain.
 
-    ### Configuration
-
-    Some behaviour of atward can be configured by adding query
-    parameters to the search string:
-
-    * <kbd>cs=true</kbd> - use Sourcegraph instead of cgit to view code
-
-
-    In some browsers (like Firefox) users can not edit query
-    parameters for search engines. As an alternative configuration can
-    be supplied via cookies with the same names as the configuration
-    parameters.
-
-    The form below can set this configuration:
-    <form class="cheddar-callout cheddar-todo">
-      <input type="checkbox"
-             id="cs-setting"
-             name="cs-setting"
-             onchange="saveSetting(this, 'cs');">
-      <label for="cs-setting">Use Sourcegraph instead of cgit</label>
-    </form>
-
-    <noscript>
-      <p class="cheddar-callout cheddar-warning">
-        The form above only works with Javascript enabled. Only a few
-        lines of Javascript are used, and they are licensed under a
-        free-software license (MIT).
-      </p>
-    </noscript>
-
     ### Source code
 
     atward's source code lives at
     [//web/atward](https://at.tvl.fyi/?q=%2F%2Fweb%2Fatward).
   '';
-  extraHead = ''
-    <script>
-      /* Initialise the state of all settings. */
-      function loadSettings() {
-          loadSetting(document.getElementById('cs-setting'), 'cs');
-      }
-
-      /* Initialise the state of a setting from a cookie. */
-      function loadSetting(checkbox, name) {
-          if (document.cookie.split(';').some(function(cookie) {
-              return cookie.indexOf(`''${name}=true`) >= 0;
-          })) {
-              checkbox.checked = true;
-          }
-      }
 
-      /* Persist the state of a checkbox in a cookie */
-      function saveSetting(checkbox, name) {
-          console.log(`setting atward parameter '''''${name}' to ''${checkbox.checked.toString()}`);
-          document.cookie = `''${name}=''${checkbox.checked.toString()};`;
-      }
-
-      document.addEventListener('DOMContentLoaded', loadSettings);
-    </script>
+  extraHead = ''
     <link rel="search" type="application/opensearchdescription+xml" title="TVL Search" href="https://at.tvl.fyi/opensearch.xml">
   '';
 }
diff --git a/web/atward/src/main.rs b/web/atward/src/main.rs
index eb2603a226c6..13aeff0e27d8 100644
--- a/web/atward/src/main.rs
+++ b/web/atward/src/main.rs
@@ -5,7 +5,6 @@
 //! browsers and attempts to send users to useful locations based on
 //! their search query (falling back to another search engine).
 use regex::Regex;
-use rouille::input::cookies;
 use rouille::{Request, Response};
 
 #[cfg(test)]
@@ -31,43 +30,14 @@ struct Handler {
 struct Query {
     /// Query string itself.
     query: String,
-
-    /// Should Sourcegraph be used instead of cgit?
-    cs: bool,
-}
-
-/// Helper function for setting a parameter based on a query
-/// parameter.
-fn query_setting(req: &Request, config: &mut bool, param: &str) {
-    match req.get_param(param) {
-        Some(s) if s == "true" => *config = true,
-        Some(s) if s == "false" => *config = false,
-        _ => {}
-    }
 }
 
 impl Query {
     fn from_request(req: &Request) -> Option<Query> {
-        // First extract the actual search query ...
-        let mut query = match req.get_param("q") {
-            Some(query) => Query { query, cs: false },
+        match req.get_param("q") {
+            Some(query) => Some(Query { query }),
             None => return None,
-        };
-
-        // ... then apply settings to it. Settings in query parameters
-        // take precedence over cookies.
-        for cookie in cookies(req) {
-            match cookie {
-                ("cs", "true") => {
-                    query.cs = true;
-                }
-                _ => {}
-            }
         }
-
-        query_setting(req, &mut query.cs, "cs");
-
-        Some(query)
     }
 }
 
@@ -76,7 +46,6 @@ impl From<&str> for Query {
     fn from(query: &str) -> Query {
         Query {
             query: query.to_string(),
-            cs: false,
         }
     }
 }
@@ -90,10 +59,6 @@ fn cgit_url(path: &str) -> String {
     }
 }
 
-/// Create a URL to a path in Sourcegraph.
-fn sourcegraph_path_url(path: &str) -> String {
-    format!("https://cs.tvl.fyi/depot/-/tree/{}", path)
-}
 /// Definition of all supported query handlers in atward.
 fn handlers() -> Vec<Handler> {
     vec![
@@ -126,16 +91,11 @@ fn handlers() -> Vec<Handler> {
         // TODO(tazjin): Add support for specifying lines in a query parameter
         Handler {
             pattern: Regex::new("^//(?P<path>[a-zA-Z].*)?$").unwrap(),
-            target: |query, captures| {
+            target: |_, captures| {
                 // Pass an empty string if the path is missing, to
                 // redirect to the depot root.
                 let path = captures.name("path").map(|m| m.as_str()).unwrap_or("");
-
-                if query.cs {
-                    Some(sourcegraph_path_url(path))
-                } else {
-                    Some(cgit_url(path))
-                }
+                Some(cgit_url(path))
             },
         },
     ]
diff --git a/web/atward/src/tests.rs b/web/atward/src/tests.rs
index a23f96ee9a74..eb205fdf9810 100644
--- a/web/atward/src/tests.rs
+++ b/web/atward/src/tests.rs
@@ -44,38 +44,12 @@ fn depot_path_cgit_query() {
 }
 
 #[test]
-fn depot_path_sourcegraph_query() {
-    assert_eq!(
-        dispatch(
-            &handlers(),
-            &Query {
-                query: "//web/atward/default.nix".to_string(),
-                cs: true,
-            }
-        ),
-        Some("https://cs.tvl.fyi/depot/-/tree/web/atward/default.nix".to_string()),
-    );
-
-    assert_eq!(
-        dispatch(
-            &handlers(),
-            &Query {
-                query: "/not/a/depot/path".to_string(),
-                cs: true,
-            }
-        ),
-        None
-    );
-}
-
-#[test]
 fn depot_root_cgit_query() {
     assert_eq!(
         dispatch(
             &handlers(),
             &Query {
                 query: "//".to_string(),
-                cs: false,
             }
         ),
         Some("https://code.tvl.fyi/tree/".to_string()),
@@ -112,7 +86,6 @@ fn request_to_query() {
             .expect("request should parse to a query"),
         Query {
             query: "b/42".to_string(),
-            cs: false,
         },
     );
 
@@ -123,55 +96,6 @@ fn request_to_query() {
 }
 
 #[test]
-fn settings_from_cookie() {
-    assert_eq!(
-        Query::from_request(&Request::fake_http(
-            "GET",
-            "/?q=b%2F42",
-            vec![("Cookie".to_string(), "cs=true;".to_string())],
-            vec![]
-        ))
-        .expect("request should parse to a query"),
-        Query {
-            query: "b/42".to_string(),
-            cs: true,
-        },
-    );
-}
-
-#[test]
-fn settings_from_query_parameter() {
-    assert_eq!(
-        Query::from_request(&Request::fake_http(
-            "GET",
-            "/?q=b%2F42&cs=true",
-            vec![],
-            vec![]
-        ))
-        .expect("request should parse to a query"),
-        Query {
-            query: "b/42".to_string(),
-            cs: true,
-        },
-    );
-
-    // Query parameter should override cookie
-    assert_eq!(
-        Query::from_request(&Request::fake_http(
-            "GET",
-            "/?q=b%2F42&cs=false",
-            vec![("Cookie".to_string(), "cs=true;".to_string())],
-            vec![]
-        ))
-        .expect("request should parse to a query"),
-        Query {
-            query: "b/42".to_string(),
-            cs: false,
-        },
-    );
-}
-
-#[test]
 fn depot_revision_query() {
     assert_eq!(
         dispatch(&handlers(), &"r/3002".into()),
diff --git a/web/bubblegum/default.nix b/web/bubblegum/default.nix
index ed9ab616804d..b7e1ce81ebd0 100644
--- a/web/bubblegum/default.nix
+++ b/web/bubblegum/default.nix
@@ -21,6 +21,7 @@ let
       # nixpkgs for lib and packages
       "third_party/nixpkgs"
       "third_party/overlays"
+      "third_party/sources"
       # bubblegum and its dependencies
       "web/bubblegum"
       "nix/runExecline"
diff --git a/web/cgit-tvl/default.nix b/web/cgit-tvl/default.nix
index d26de9a5ebe7..1be2f29b81f0 100644
--- a/web/cgit-tvl/default.nix
+++ b/web/cgit-tvl/default.nix
@@ -15,7 +15,7 @@ let
     source-filter=${depot.tools.cheddar}/bin/cheddar
     enable-log-filecount=1
     enable-log-linecount=1
-    enable-follow-links=1
+    enable-follow-links=0
     enable-blame=1
     mimetype-file=${pkgs.mime-types}/etc/mime.types
     logo=https://static.tvl.fyi/${depot.web.static.drvHash}/logo-animated.svg
diff --git a/web/pwcrypt/Cargo.lock b/web/pwcrypt/Cargo.lock
index 2a88ec59d5a6..75b2d8c9c69f 100644
--- a/web/pwcrypt/Cargo.lock
+++ b/web/pwcrypt/Cargo.lock
@@ -818,19 +818,20 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
 dependencies = [
  "cfg-if",
+ "once_cell",
  "wasm-bindgen-macro",
 ]
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
 dependencies = [
  "bumpalo",
  "log",
@@ -855,9 +856,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -865,9 +866,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -878,9 +879,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
 
 [[package]]
 name = "web-sys"
diff --git a/web/pwcrypt/Cargo.toml b/web/pwcrypt/Cargo.toml
index 488dcad19e15..48c73081f1ee 100644
--- a/web/pwcrypt/Cargo.toml
+++ b/web/pwcrypt/Cargo.toml
@@ -8,6 +8,6 @@ argon2 = "0.5.0"
 getrandom = { version = "0.2.10", features = ["js"] }
 gloo = "0.8.0"
 rand_core = { version = "0.6.4", features = ["getrandom"] }
-wasm-bindgen = "= 0.2.92"
+wasm-bindgen = "= 0.2.93"
 web-sys = "0.3"
 yew = { version = "0.20.0", features = [ "csr" ]}
diff --git a/web/tvixbolt/Cargo.lock b/web/tvixbolt/Cargo.lock
index 82162469dee9..f67dbb9f9846 100644
--- a/web/tvixbolt/Cargo.lock
+++ b/web/tvixbolt/Cargo.lock
@@ -75,12 +75,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
 
 [[package]]
-name = "bitmaps"
-version = "3.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "703642b98a00b3b90513279a8ede3fcfa479c126c5fb46e78f3051522f021403"
-
-[[package]]
 name = "block-buffer"
 version = "0.10.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -97,9 +91,9 @@ checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9"
 
 [[package]]
 name = "bstr"
-version = "1.9.0"
+version = "1.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc"
+checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c"
 dependencies = [
  "memchr",
  "regex-automata",
@@ -114,9 +108,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
 
 [[package]]
 name = "bytes"
-version = "1.5.0"
+version = "1.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
+checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
 
 [[package]]
 name = "cc"
@@ -745,29 +739,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "imbl"
-version = "3.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc3be8d8cd36f33a46b1849f31f837c44d9fa87223baee3b4bd96b8f11df81eb"
-dependencies = [
- "bitmaps",
- "imbl-sized-chunks",
- "rand_core",
- "rand_xoshiro",
- "serde",
- "version_check",
-]
-
-[[package]]
-name = "imbl-sized-chunks"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6957ea0b2541c5ca561d3ef4538044af79f8a05a1eb3a3b148936aaceaa1076"
-dependencies = [
- "bitmaps",
-]
-
-[[package]]
 name = "implicit-clone"
 version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -809,9 +780,9 @@ dependencies = [
 
 [[package]]
 name = "itertools"
-version = "0.12.0"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
 dependencies = [
  "either",
 ]
@@ -833,9 +804,9 @@ dependencies = [
 
 [[package]]
 name = "lazy_static"
-version = "1.4.0"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
 
 [[package]]
 name = "lexical-core"
@@ -959,6 +930,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "nohash-hasher"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
+
+[[package]]
 name = "nom8"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1128,29 +1105,14 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.36"
+version = "1.0.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
 dependencies = [
  "proc-macro2",
 ]
 
 [[package]]
-name = "rand_core"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
-
-[[package]]
-name = "rand_xoshiro"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
-dependencies = [
- "rand_core",
-]
-
-[[package]]
 name = "redox_syscall"
 version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1172,9 +1134,9 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.10.2"
+version = "1.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
+checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -1184,9 +1146,9 @@ dependencies = [
 
 [[package]]
 name = "regex-automata"
-version = "0.4.3"
+version = "0.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -1259,9 +1221,9 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
 
 [[package]]
 name = "serde"
-version = "1.0.204"
+version = "1.0.210"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
+checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
 dependencies = [
  "serde_derive",
 ]
@@ -1290,9 +1252,9 @@ dependencies = [
 
 [[package]]
 name = "serde_derive"
-version = "1.0.204"
+version = "1.0.210"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
+checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1364,9 +1326,9 @@ dependencies = [
 
 [[package]]
 name = "smol_str"
-version = "0.2.0"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c"
+checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead"
 dependencies = [
  "serde",
 ]
@@ -1557,11 +1519,11 @@ dependencies = [
  "data-encoding",
  "dirs",
  "genawaiter",
- "imbl",
  "itertools",
  "lazy_static",
  "lexical-core",
  "md-5",
+ "nohash-hasher",
  "os_str_bytes",
  "path-clean",
  "regex",
@@ -1576,6 +1538,7 @@ dependencies = [
  "tabwriter",
  "toml",
  "tvix-eval-builtin-macros",
+ "vu128",
 ]
 
 [[package]]
@@ -1630,6 +1593,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 
 [[package]]
+name = "vu128"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b18da3bd753c6f4373511e5f025423986560dfe4a5e7d642cc9a0266847f9fdd"
+
+[[package]]
 name = "wasi"
 version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1637,19 +1606,20 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
 dependencies = [
  "cfg-if",
+ "once_cell",
  "wasm-bindgen-macro",
 ]
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
 dependencies = [
  "bumpalo",
  "log",
@@ -1674,9 +1644,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -1684,9 +1654,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1697,9 +1667,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.92"
+version = "0.2.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
 
 [[package]]
 name = "web-sys"
diff --git a/web/tvixbolt/Cargo.nix b/web/tvixbolt/Cargo.nix
index a2ad6cc33e80..ceec2d582cca 100644
--- a/web/tvixbolt/Cargo.nix
+++ b/web/tvixbolt/Cargo.nix
@@ -150,7 +150,7 @@ rec {
           "perf-literal" = [ "dep:memchr" ];
           "std" = [ "memchr?/std" ];
         };
-        resolvedDefaultFeatures = [ "default" "perf-literal" "std" ];
+        resolvedDefaultFeatures = [ "perf-literal" "std" ];
       };
       "anymap2" = rec {
         crateName = "anymap2";
@@ -283,24 +283,12 @@ rec {
           "serde" = [ "dep:serde" ];
         };
       };
-      "bitmaps" = rec {
-        crateName = "bitmaps";
-        version = "3.2.0";
-        edition = "2021";
-        sha256 = "00ql08pm4l9hizkldyy54v0pk96g7zg8x6i72c2vkcq0iawl4dkh";
-        authors = [
-          "Bodil Stokke <bodil@bodil.org>"
-        ];
-        features = {
-          "default" = [ "std" ];
-        };
-        resolvedDefaultFeatures = [ "default" "std" ];
-      };
       "block-buffer" = rec {
         crateName = "block-buffer";
         version = "0.10.4";
         edition = "2018";
         sha256 = "0w9sa2ypmrsqqvc20nhwr75wbb5cjr4kkyhpjm1z1lv2kdicfy1h";
+        libName = "block_buffer";
         authors = [
           "RustCrypto Developers"
         ];
@@ -324,9 +312,9 @@ rec {
       };
       "bstr" = rec {
         crateName = "bstr";
-        version = "1.9.0";
+        version = "1.10.0";
         edition = "2021";
-        sha256 = "1p6hzf3wqwwynv6w4pn17jg21amfafph9kb5sfvf1idlli8h13y4";
+        sha256 = "036wwrchd5gq3q4k6w1j2bfl2bk2ff8c0dsa9y7w7aw7nf7knwj0";
         authors = [
           "Andrew Gallant <jamslam@gmail.com>"
         ];
@@ -375,9 +363,9 @@ rec {
       };
       "bytes" = rec {
         crateName = "bytes";
-        version = "1.5.0";
+        version = "1.7.1";
         edition = "2018";
-        sha256 = "08w2i8ac912l8vlvkv3q51cd4gr09pwlg3sjsjffcizlrb0i5gd2";
+        sha256 = "0l5sf69avjxcw41cznyzxsnymwmkpmk08q0sm7fgicvvn0ysa643";
         authors = [
           "Carl Lerche <me@carllerche.com>"
           "Sean McArthur <sean@seanmonstar.com>"
@@ -405,6 +393,7 @@ rec {
         version = "1.0.0";
         edition = "2018";
         sha256 = "1za0vb97n4brpzpv8lsbnzmq5r8f2b0cpqqr0sy8h5bn751xxwds";
+        libName = "cfg_if";
         authors = [
           "Alex Crichton <alex@alexcrichton.com>"
         ];
@@ -429,6 +418,7 @@ rec {
         version = "0.1.2";
         edition = "2015";
         sha256 = "08l1b84bn8r8a72rbvyi2v8a5i0j0kk0a5gr7fb6lmjvw05pf86c";
+        libName = "codemap_diagnostic";
         authors = [
           "Kevin Mehall <km@kevinmehall.net>"
           "The Rust Project Developers"
@@ -518,6 +508,7 @@ rec {
         version = "0.1.6";
         edition = "2018";
         sha256 = "1cvby95a6xg7kxdz5ln3rl9xh66nz66w46mm3g56ri1z5x815yqv";
+        libName = "crypto_common";
         authors = [
           "RustCrypto Developers"
         ];
@@ -543,6 +534,7 @@ rec {
         version = "2.6.0";
         edition = "2018";
         sha256 = "1qnn68n4vragxaxlkqcb1r28d3hhj43wch67lm4rpxlw89wnjmp8";
+        libName = "data_encoding";
         authors = [
           "Julien Cretin <git@ia0.eu>"
         ];
@@ -607,6 +599,7 @@ rec {
         version = "0.3.7";
         edition = "2015";
         sha256 = "19md1cnkazham8a6kh22v12d8hh3raqahfk6yb043vrjr68is78v";
+        libName = "dirs_sys";
         authors = [
           "Simon Ochsenreither <simon@ochsenreither.de>"
         ];
@@ -748,6 +741,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "0y6b7xxqdjm9hlcjpakcg41qfl7lihf6gavk8fyqijsxhvbzgj7a";
+        libName = "futures_channel";
         dependencies = [
           {
             name = "futures-core";
@@ -775,6 +769,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "07aslayrn3lbggj54kci0ishmd1pr367fp7iks7adia1p05miinz";
+        libName = "futures_core";
         features = {
           "default" = [ "std" ];
           "portable-atomic" = [ "dep:portable-atomic" ];
@@ -787,6 +782,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "1hgh25isvsr4ybibywhr4dpys8mjnscw4wfxxwca70cn1gi26im4";
+        libName = "futures_io";
         features = {
           "default" = [ "std" ];
         };
@@ -798,6 +794,7 @@ rec {
         edition = "2018";
         sha256 = "1b49qh9d402y8nka4q6wvvj0c88qq91wbr192mdn5h54nzs0qxc7";
         procMacro = true;
+        libName = "futures_macro";
         dependencies = [
           {
             name = "proc-macro2";
@@ -820,6 +817,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "1dag8xyyaya8n8mh8smx7x6w2dpmafg2din145v973a3hw7f1f4z";
+        libName = "futures_sink";
         features = {
           "default" = [ "std" ];
           "std" = [ "alloc" ];
@@ -831,6 +829,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "013h1724454hj8qczp8vvs10qfiqrxr937qsrv6rhii68ahlzn1q";
+        libName = "futures_task";
         features = {
           "default" = [ "std" ];
           "std" = [ "alloc" ];
@@ -842,6 +841,7 @@ rec {
         version = "0.3.30";
         edition = "2018";
         sha256 = "0j0xqhcir1zf2dcbpd421kgw6wvsk0rpxflylcysn1rlp3g02r1x";
+        libName = "futures_util";
         dependencies = [
           {
             name = "futures-channel";
@@ -950,6 +950,7 @@ rec {
         version = "0.99.1";
         edition = "2018";
         sha256 = "1g6zmr88fk48f1ksz9ik1i2mwjsiam9s4p9aybhvs2zwzphxychb";
+        libName = "genawaiter_macro";
         authors = [
           "Devin R <devin.ragotzy@gmail.com>"
         ];
@@ -1240,6 +1241,7 @@ rec {
         version = "0.2.3";
         edition = "2018";
         sha256 = "0gqd35vn0i5y6hzfrsb2i032p1j832c08sar6dr19gny0lycxdw2";
+        libName = "gloo_console";
         authors = [
           "Rust and WebAssembly Working Group"
         ];
@@ -1275,6 +1277,7 @@ rec {
         version = "0.3.0";
         edition = "2021";
         sha256 = "0qg24wbvql0bsr980hbrm0pi11c3jmlwpj0pgdklz8mlas7qc5ra";
+        libName = "gloo_console";
         authors = [
           "Rust and WebAssembly Working Group"
         ];
@@ -1310,6 +1313,7 @@ rec {
         version = "0.1.1";
         edition = "2018";
         sha256 = "1rh2j0l8rbj8pbypxqy99qi2x3hq52sclijs8h47zlkjmij261k7";
+        libName = "gloo_dialogs";
         authors = [
           "Rust and WebAssembly Working Group"
         ];
@@ -1331,6 +1335,7 @@ rec {
         version = "0.2.0";
         edition = "2021";
         sha256 = "1pqmg2z3x4c3id25jd0p8rjwy5qjbc4k1x8gflsi9c1207hlhixz";
+        libName = "gloo_dialogs";
         authors = [
           "Rust and WebAssembly Working Group"
         ];
@@ -1352,6 +1357,7 @@ rec {
         version = "0.1.2";
         edition = "2018";
         sha256 = "1z4j14r2lim77s0jm1dpk306jyycmx2kirid33j0b0gdmgw0gcb8";
+        libName = "gloo_events";
         authors = [
           "Rust and WebAssembly Working Group"
         ];
@@ -1380,6 +1386,7 @@ rec {
         version = "0.2.0";
         edition = "2021";
         sha256 = "0h8yr4n1pvwp4rr87835w14kjdkycyn8gypmh2lmnf3wbys6zhi7";
+        libName = "gloo_events";
         authors = [
           "Rust and WebAssembly Working Group"
         ];
@@ -1408,6 +1415,7 @@ rec {
         version = "0.2.3";
         edition = "2018";
         sha256 = "1mxnd7l8gglv5yqhah6ny329hc0c98vn7h5xg0yv8f0aax75dmd8";
+        libName = "gloo_file";
         authors = [
           "Rust and WebAssembly Working Group"
         ];
@@ -1449,6 +1457,7 @@ rec {
         version = "0.3.0";
         edition = "2021";
         sha256 = "07xxainnnrg6l3ccw2bvqiz4m76ih557aklp5r5q5cizhrqksmlp";
+        libName = "gloo_file";
         authors = [
           "Rust and WebAssembly Working Group"
         ];
@@ -1495,6 +1504,7 @@ rec {
         version = "0.1.5";
         edition = "2018";
         sha256 = "0zsy3m5bgah8hyd95sc9b68afn1nhs7g43lkndip1m0fpy85swl5";
+        libName = "gloo_history";
         authors = [
           "Rust and WebAssembly Working Group"
         ];
@@ -1549,6 +1559,7 @@ rec {
         version = "0.2.2";
         edition = "2021";
         sha256 = "1mhphqywgbqj4agpi4zyc4hah12nys7085jymiz44d5swlml6gwh";
+        libName = "gloo_history";
         authors = [
           "Rust and WebAssembly Working Group"
         ];
@@ -1609,6 +1620,7 @@ rec {
         version = "0.3.1";
         edition = "2018";
         sha256 = "0866ih3bff7dwxdfc813pk5nwz2ayyqwi5vbzlax7n4ygly4wsx6";
+        libName = "gloo_net";
         authors = [
           "Rust and WebAssembly Working Group"
           "Muhammad Hamza <muhammadhamza1311@gmail.com>"
@@ -1701,6 +1713,7 @@ rec {
         version = "0.4.0";
         edition = "2021";
         sha256 = "1i3x5fvp07valrxjsa25ycq1b2p3pxqaqmw6kzx35ip2i8lfijca";
+        libName = "gloo_net";
         authors = [
           "Rust and WebAssembly Working Group"
           "Muhammad Hamza <muhammadhamza1311@gmail.com>"
@@ -1793,6 +1806,7 @@ rec {
         version = "0.1.1";
         edition = "2018";
         sha256 = "0r3pxj22l489ldakj6521a0f0n1r9v8xrai3k12d9kv7xxm31n9g";
+        libName = "gloo_render";
         authors = [
           "Rust and WebAssembly Working Group"
         ];
@@ -1814,6 +1828,7 @@ rec {
         version = "0.2.0";
         edition = "2021";
         sha256 = "0cwqcka7l5p29idq174c6mi5cgal0rywngdck26qwfki8ikqn02n";
+        libName = "gloo_render";
         authors = [
           "Rust and WebAssembly Working Group"
         ];
@@ -1835,6 +1850,7 @@ rec {
         version = "0.2.2";
         edition = "2018";
         sha256 = "1074j754a6c21sbmqws5qwaha0a13fikv17ps476zzfvyl5vcsjx";
+        libName = "gloo_storage";
         authors = [
           "Rust and WebAssembly Working Group"
         ];
@@ -1883,6 +1899,7 @@ rec {
         version = "0.3.0";
         edition = "2021";
         sha256 = "0yi7740iza6nyg6n8sxzzhy6yg6xpbxhig7r2bwqlxcjihg07j7v";
+        libName = "gloo_storage";
         authors = [
           "Rust and WebAssembly Working Group"
         ];
@@ -1931,6 +1948,7 @@ rec {
         version = "0.2.6";
         edition = "2018";
         sha256 = "0p2yqcxw0q9kclhwpgshq1r4ijns07nmmagll3lvrgl7pdk5m6cv";
+        libName = "gloo_timers";
         authors = [
           "Rust and WebAssembly Working Group"
         ];
@@ -1956,6 +1974,7 @@ rec {
         version = "0.3.0";
         edition = "2021";
         sha256 = "1519157n7xppkk6pdw5w52vy1llzn5iljkqd7q1h5609jv7l7cdv";
+        libName = "gloo_timers";
         authors = [
           "Rust and WebAssembly Working Group"
         ];
@@ -1991,6 +2010,7 @@ rec {
         version = "0.1.7";
         edition = "2018";
         sha256 = "13m59g36spynspvhx0xsaahbkdshn1v03gcjf87s7cvc443wnzq3";
+        libName = "gloo_utils";
         authors = [
           "Rust and WebAssembly Working Group"
         ];
@@ -2030,6 +2050,7 @@ rec {
         version = "0.2.0";
         edition = "2021";
         sha256 = "1am31cd6889shb7158bg9zzsjcpvyzxrhfhxgia8rc8k84smam8b";
+        libName = "gloo_utils";
         authors = [
           "Rust and WebAssembly Working Group"
         ];
@@ -2069,6 +2090,7 @@ rec {
         version = "0.2.1";
         edition = "2018";
         sha256 = "0sjiw13069i7bpiyb03w3kyddn3q07fmj4vd60l1l1kqva21aiqk";
+        libName = "gloo_worker";
         authors = [
           "Rust and WebAssembly Working Group"
         ];
@@ -2120,6 +2142,7 @@ rec {
         version = "0.4.0";
         edition = "2021";
         sha256 = "00744js1jcwdndzf22c8gzml7aqql4ymjfpsd2i1vrbxv0ymsjbn";
+        libName = "gloo_worker";
         authors = [
           "Rust and WebAssembly Working Group"
         ];
@@ -2182,6 +2205,7 @@ rec {
         edition = "2021";
         sha256 = "1rs0f6b34mkhlmpmhqi747c34000sd5mxma92yacjyw5sicalv4m";
         procMacro = true;
+        libName = "gloo_worker_macros";
         authors = [
           "Rust and WebAssembly Working Group"
         ];
@@ -2257,6 +2281,7 @@ rec {
         version = "0.3.9";
         edition = "2021";
         sha256 = "092hxjbjnq5fmz66grd9plxd0sh6ssg5fhgwwwqbrzgzkjwdycfj";
+        libName = "hermit_abi";
         authors = [
           "Stefan Lankes"
         ];
@@ -2294,91 +2319,12 @@ rec {
         ];
 
       };
-      "imbl" = rec {
-        crateName = "imbl";
-        version = "3.0.0";
-        edition = "2018";
-        sha256 = "1sw1vw8qysyr9cxyxfi3fal9ykf46zw337w4n533mwrnrpcfhfxw";
-        authors = [
-          "Bodil Stokke <bodil@bodil.org>"
-          "Joe Neeman <joeneeman@gmail.com>"
-        ];
-        dependencies = [
-          {
-            name = "bitmaps";
-            packageId = "bitmaps";
-          }
-          {
-            name = "imbl-sized-chunks";
-            packageId = "imbl-sized-chunks";
-          }
-          {
-            name = "rand_core";
-            packageId = "rand_core";
-          }
-          {
-            name = "rand_xoshiro";
-            packageId = "rand_xoshiro";
-          }
-          {
-            name = "serde";
-            packageId = "serde";
-            optional = true;
-          }
-        ];
-        buildDependencies = [
-          {
-            name = "version_check";
-            packageId = "version_check";
-          }
-        ];
-        devDependencies = [
-          {
-            name = "serde";
-            packageId = "serde";
-          }
-        ];
-        features = {
-          "arbitrary" = [ "dep:arbitrary" ];
-          "proptest" = [ "dep:proptest" ];
-          "quickcheck" = [ "dep:quickcheck" ];
-          "rayon" = [ "dep:rayon" ];
-          "refpool" = [ "dep:refpool" ];
-          "serde" = [ "dep:serde" ];
-          "triomphe" = [ "dep:triomphe" ];
-        };
-        resolvedDefaultFeatures = [ "serde" ];
-      };
-      "imbl-sized-chunks" = rec {
-        crateName = "imbl-sized-chunks";
-        version = "0.1.1";
-        edition = "2021";
-        sha256 = "0xhhmb7aldl92hxkmsx10n59zxsa0hw4bvykc6jmq72lnah7x5g6";
-        authors = [
-          "Bodil Stokke <bodil@bodil.org>"
-          "Joe Neeman <joeneeman@gmail.com>"
-        ];
-        dependencies = [
-          {
-            name = "bitmaps";
-            packageId = "bitmaps";
-            usesDefaultFeatures = false;
-          }
-        ];
-        features = {
-          "arbitrary" = [ "dep:arbitrary" ];
-          "array-ops" = [ "dep:array-ops" ];
-          "default" = [ "std" ];
-          "refpool" = [ "dep:refpool" ];
-          "ringbuffer" = [ "array-ops" ];
-        };
-        resolvedDefaultFeatures = [ "default" "std" ];
-      };
       "implicit-clone" = rec {
         crateName = "implicit-clone";
         version = "0.4.9";
         edition = "2021";
         sha256 = "113agd9bqk7c0s2rqgarzkfp3wgbzl3q59mp6sv72nkv3iwsmagq";
+        libName = "implicit_clone";
         authors = [
           "Cecile Tonglet <cecile.tonglet@cecton.com>"
         ];
@@ -2410,6 +2356,7 @@ rec {
         edition = "2021";
         sha256 = "0fsfj6n56mg92f3899gcdck1dqlsmgyd52k0n2xhhj53p5g6h4ck";
         procMacro = true;
+        libName = "implicit_clone_derive";
         authors = [
           "Cecile Tonglet <cecile.tonglet@cecton.com>"
         ];
@@ -2485,9 +2432,9 @@ rec {
       };
       "itertools" = rec {
         crateName = "itertools";
-        version = "0.12.0";
+        version = "0.12.1";
         edition = "2018";
-        sha256 = "1c07gzdlc6a1c8p8jrvvw3gs52bss3y58cs2s21d9i978l36pnr5";
+        sha256 = "0s95jbb3ndj1lvfxyq5wanc0fm0r6hg6q4ngb92qlfdxvci10ads";
         authors = [
           "bluss"
         ];
@@ -2521,6 +2468,7 @@ rec {
         version = "0.3.69";
         edition = "2018";
         sha256 = "0v99rz97asnzapb0jsc3jjhvxpfxr7h7qd97yqyrf9i7viimbh99";
+        libName = "js_sys";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -2534,9 +2482,9 @@ rec {
       };
       "lazy_static" = rec {
         crateName = "lazy_static";
-        version = "1.4.0";
+        version = "1.5.0";
         edition = "2015";
-        sha256 = "0in6ikhw8mgl33wjv6q6xfrb5b9jr16q8ygjy803fay4zcisvaz2";
+        sha256 = "1zk6dqqni0193xg6iijh7i3i44sryglwgvx20spdvwk3r6sbrlmv";
         authors = [
           "Marvin Löbel <loebel.marvin@gmail.com>"
         ];
@@ -2550,6 +2498,7 @@ rec {
         version = "0.8.5";
         edition = "2018";
         sha256 = "0ihf0x3vrk25fq3bv9q35m0xax0wmvwkh0j0pjm2yk4ddvh5vpic";
+        libName = "lexical_core";
         authors = [
           "Alex Huszagh <ahuszagh@gmail.com>"
         ];
@@ -2612,6 +2561,7 @@ rec {
         version = "0.8.5";
         edition = "2018";
         sha256 = "0py0gp8hlzcrlvjqmqlpl2v1as65iiqxq2xsabxvhc01pmg3lfv8";
+        libName = "lexical_parse_float";
         authors = [
           "Alex Huszagh <ahuszagh@gmail.com>"
         ];
@@ -2652,6 +2602,7 @@ rec {
         version = "0.8.6";
         edition = "2018";
         sha256 = "1sayji3mpvb2xsjq56qcq3whfz8px9a6fxk5v7v15hyhbr4982bd";
+        libName = "lexical_parse_integer";
         authors = [
           "Alex Huszagh <ahuszagh@gmail.com>"
         ];
@@ -2683,6 +2634,7 @@ rec {
         version = "0.8.5";
         edition = "2018";
         sha256 = "1z73qkv7yxhsbc4aiginn1dqmsj8jarkrdlyxc88g2gz2vzvjmaj";
+        libName = "lexical_util";
         authors = [
           "Alex Huszagh <ahuszagh@gmail.com>"
         ];
@@ -2709,6 +2661,7 @@ rec {
         version = "0.8.5";
         edition = "2018";
         sha256 = "0qk825l0csvnksh9sywb51996cjc2bylq6rxjaiha7sqqjhvmjmc";
+        libName = "lexical_write_float";
         authors = [
           "Alex Huszagh <ahuszagh@gmail.com>"
         ];
@@ -2749,6 +2702,7 @@ rec {
         version = "0.8.5";
         edition = "2018";
         sha256 = "0ii4hmvqrg6pd4j9y1pkhkp0nw2wpivjzmljh6v6ca22yk8z7dp1";
+        libName = "lexical_write_integer";
         authors = [
           "Alex Huszagh <ahuszagh@gmail.com>"
         ];
@@ -2939,6 +2893,20 @@ rec {
           "simd-adler32" = [ "dep:simd-adler32" ];
         };
       };
+      "nohash-hasher" = rec {
+        crateName = "nohash-hasher";
+        version = "0.2.0";
+        edition = "2018";
+        sha256 = "0lf4p6k01w4wm7zn4grnihzj8s7zd5qczjmzng7wviwxawih5x9b";
+        libName = "nohash_hasher";
+        authors = [
+          "Parity Technologies <admin@parity.io>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
       "nom8" = rec {
         crateName = "nom8";
         version = "0.2.0";
@@ -3062,6 +3030,7 @@ rec {
         version = "0.1.0";
         edition = "2015";
         sha256 = "1pcgqxw0mgg3ha5hi5xkjhyjf488bw5rw1g3qlr9awbq4szh3fpc";
+        libName = "path_clean";
         authors = [
           "Dan Reeves <hey@danreev.es>"
         ];
@@ -3072,6 +3041,7 @@ rec {
         version = "2.3.1";
         edition = "2018";
         sha256 = "0gi8wgx0dcy8rnv1kywdv98lwcx67hz0a0zwpib5v2i08r88y573";
+        libName = "percent_encoding";
         authors = [
           "The rust-url developers"
         ];
@@ -3086,6 +3056,7 @@ rec {
         version = "1.1.5";
         edition = "2021";
         sha256 = "1cxl146x0q7lawp0m1826wsgj8mmmfs6ja8q7m6f7ff5j6vl7gxn";
+        libName = "pin_project";
         dependencies = [
           {
             name = "pin-project-internal";
@@ -3100,6 +3071,7 @@ rec {
         edition = "2021";
         sha256 = "0r9r4ivwiyqf45sv6b30l1dx282lxaax2f6gl84jwa3q590s8f1g";
         procMacro = true;
+        libName = "pin_project_internal";
         dependencies = [
           {
             name = "proc-macro2";
@@ -3122,6 +3094,7 @@ rec {
         version = "0.2.14";
         edition = "2018";
         sha256 = "00nx3f04agwjlsmd3mc5rx5haibj2v8q9b52b0kwn63wcv4nz9mx";
+        libName = "pin_project_lite";
 
       };
       "pin-utils" = rec {
@@ -3129,6 +3102,7 @@ rec {
         version = "0.1.0";
         edition = "2018";
         sha256 = "117ir7vslsl2z1a7qzhws4pd01cg2d3338c47swjyvqv2n60v1wb";
+        libName = "pin_utils";
         authors = [
           "Josef Brandl <mail@josefbrandl.de>"
         ];
@@ -3204,6 +3178,7 @@ rec {
         version = "1.3.1";
         edition = "2021";
         sha256 = "069r1k56bvgk0f58dm5swlssfcp79im230affwk6d9ck20g04k3z";
+        libName = "proc_macro_crate";
         authors = [
           "Bastian Köcher <git@kchr.de>"
         ];
@@ -3224,6 +3199,7 @@ rec {
         version = "1.0.4";
         edition = "2018";
         sha256 = "1373bhxaf0pagd8zkyd03kkx6bchzf6g0dkwrwzsnal9z47lj9fs";
+        libName = "proc_macro_error";
         authors = [
           "CreepySkeleton <creepy-skeleton@yandex.ru>"
         ];
@@ -3266,6 +3242,7 @@ rec {
         edition = "2018";
         sha256 = "0sgq6m5jfmasmwwy8x4mjygx5l7kp8s4j60bv25ckv2j1qc41gm1";
         procMacro = true;
+        libName = "proc_macro_error_attr";
         authors = [
           "CreepySkeleton <creepy-skeleton@yandex.ru>"
         ];
@@ -3377,9 +3354,9 @@ rec {
       };
       "quote" = rec {
         crateName = "quote";
-        version = "1.0.36";
+        version = "1.0.37";
         edition = "2018";
-        sha256 = "19xcmh445bg6simirnnd4fvkmp6v2qiwxh5f6rw4a70h76pnm9qg";
+        sha256 = "1brklraw2g34bxy9y4q1nbrccn7bv36ylihv12c9vlcii55x7fdm";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -3396,41 +3373,6 @@ rec {
         };
         resolvedDefaultFeatures = [ "default" "proc-macro" ];
       };
-      "rand_core" = rec {
-        crateName = "rand_core";
-        version = "0.6.4";
-        edition = "2018";
-        sha256 = "0b4j2v4cb5krak1pv6kakv4sz6xcwbrmy2zckc32hsigbrwy82zc";
-        authors = [
-          "The Rand Project Developers"
-          "The Rust Project Developers"
-        ];
-        features = {
-          "getrandom" = [ "dep:getrandom" ];
-          "serde" = [ "dep:serde" ];
-          "serde1" = [ "serde" ];
-          "std" = [ "alloc" "getrandom" "getrandom/std" ];
-        };
-      };
-      "rand_xoshiro" = rec {
-        crateName = "rand_xoshiro";
-        version = "0.6.0";
-        edition = "2018";
-        sha256 = "1ajsic84rzwz5qr0mzlay8vi17swqi684bqvwqyiim3flfrcv5vg";
-        authors = [
-          "The Rand Project Developers"
-        ];
-        dependencies = [
-          {
-            name = "rand_core";
-            packageId = "rand_core";
-          }
-        ];
-        features = {
-          "serde" = [ "dep:serde" ];
-          "serde1" = [ "serde" ];
-        };
-      };
       "redox_syscall" = rec {
         crateName = "redox_syscall";
         version = "0.4.1";
@@ -3486,9 +3428,9 @@ rec {
       };
       "regex" = rec {
         crateName = "regex";
-        version = "1.10.2";
+        version = "1.10.6";
         edition = "2021";
-        sha256 = "0hxkd814n4irind8im5c9am221ri6bprx49nc7yxv02ykhd9a2rq";
+        sha256 = "06cnlxwzyqfbw1za1i7ks89ns4i2kr0lpg5ykx56b8v7dd6df6a2";
         authors = [
           "The Rust Project Developers"
           "Andrew Gallant <jamslam@gmail.com>"
@@ -3498,11 +3440,13 @@ rec {
             name = "aho-corasick";
             packageId = "aho-corasick";
             optional = true;
+            usesDefaultFeatures = false;
           }
           {
             name = "memchr";
             packageId = "memchr";
             optional = true;
+            usesDefaultFeatures = false;
           }
           {
             name = "regex-automata";
@@ -3542,9 +3486,10 @@ rec {
       };
       "regex-automata" = rec {
         crateName = "regex-automata";
-        version = "0.4.3";
+        version = "0.4.7";
         edition = "2021";
-        sha256 = "0gs8q9yhd3kcg4pr00ag4viqxnh5l7jpyb9fsfr8hzh451w4r02z";
+        sha256 = "1pwjdi4jckpbaivpl6x4v5g4crb37zr2wac93wlfsbzgqn6gbjiq";
+        libName = "regex_automata";
         authors = [
           "The Rust Project Developers"
           "Andrew Gallant <jamslam@gmail.com>"
@@ -3605,6 +3550,7 @@ rec {
         version = "0.8.2";
         edition = "2021";
         sha256 = "17rd2s8xbiyf6lb4aj2nfi44zqlj98g2ays8zzj2vfs743k79360";
+        libName = "regex_syntax";
         authors = [
           "The Rust Project Developers"
           "Andrew Gallant <jamslam@gmail.com>"
@@ -3637,6 +3583,7 @@ rec {
         version = "0.3.1";
         edition = "2018";
         sha256 = "0ikp3blbina00jdbifxw1c9whg6mljli24lq5pv82iar53xr9axg";
+        libName = "route_recognizer";
         authors = [
           "wycats"
           "rustasync"
@@ -3700,6 +3647,7 @@ rec {
         version = "1.1.0";
         edition = "2015";
         sha256 = "1qkc5khrmv5pqi5l5ca9p5nl5hs742cagrndhbrlk3dhlrx3zm08";
+        libName = "rustc_hash";
         authors = [
           "The Rust Project Developers"
         ];
@@ -3713,6 +3661,7 @@ rec {
         version = "2.0.0";
         edition = "2021";
         sha256 = "0lni0lf846bzrf3jvci6jaf4142n1mdqxvcpczk5ch9pfgyk8c2q";
+        libName = "rustc_hash";
         authors = [
           "The Rust Project Developers"
         ];
@@ -3748,9 +3697,9 @@ rec {
       };
       "serde" = rec {
         crateName = "serde";
-        version = "1.0.204";
+        version = "1.0.210";
         edition = "2018";
-        sha256 = "04kwpwqz559xlhxkggmm8rjxqgywy5swam3kscwsicnbw1cgaxmw";
+        sha256 = "0flc0z8wgax1k4j5bf2zyq48bgzyv425jkd5w0i6wbh7f8j5kqy8";
         authors = [
           "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
           "David Tolnay <dtolnay@gmail.com>"
@@ -3785,6 +3734,7 @@ rec {
         version = "0.5.0";
         edition = "2018";
         sha256 = "03m01y4l2kqz63pb1bip52j8bqilzlhhsa7asfdanmrwhgi47cgk";
+        libName = "serde_wasm_bindgen";
         authors = [
           "Ingvar Stepanyan <me@rreverser.com>"
         ];
@@ -3816,6 +3766,7 @@ rec {
         version = "0.6.5";
         edition = "2018";
         sha256 = "0sz1l4v8059hiizf5z7r2spm6ws6sqcrs4qgqwww3p7dy1ly20l3";
+        libName = "serde_wasm_bindgen";
         authors = [
           "Ingvar Stepanyan <me@rreverser.com>"
         ];
@@ -3838,9 +3789,9 @@ rec {
       };
       "serde_derive" = rec {
         crateName = "serde_derive";
-        version = "1.0.204";
+        version = "1.0.210";
         edition = "2015";
-        sha256 = "08p25262mbmhsr2cg0508d5b1wvljj956rvpg0v3qgg6gc8pxkg0";
+        sha256 = "07yzy4wafk79ps0hmbqmsqh5xjna4pm4q57wc847bb8gl3nh4f94";
         procMacro = true;
         authors = [
           "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
@@ -4061,9 +4012,9 @@ rec {
       };
       "smol_str" = rec {
         crateName = "smol_str";
-        version = "0.2.0";
+        version = "0.2.2";
         edition = "2018";
-        sha256 = "1779hpx5ipbcvkdj5zw8zqk3ynn160qvls1gkcr54hwsprmjw8bl";
+        sha256 = "1bfylqf2vnqaglw58930vpxm2rfzji5gjp15a2c0kh8aj6v8ylyx";
         authors = [
           "Aleksey Kladov <aleksey.kladov@gmail.com>"
         ];
@@ -4206,6 +4157,7 @@ rec {
         version = "1.1.1";
         edition = "2018";
         sha256 = "0cwjbkl7w3xc8mnkhg1nwij6p5y2qkcfldgss8ddnawvhf3s32pi";
+        libName = "text_size";
         authors = [
           "Aleksey Kladov <aleksey.kladov@gmail.com>"
           "Christopher Durham (CAD97) <cad97@cad97.com>"
@@ -4301,6 +4253,7 @@ rec {
         version = "0.1.15";
         edition = "2021";
         sha256 = "1brpbsqyg8yfmfc4y0j9zxvc8xsxjc31d48kb0g6jvpc1fgchyi6";
+        libName = "tokio_stream";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -4530,6 +4483,7 @@ rec {
         edition = "2018";
         sha256 = "1rvb5dn9z6d0xdj14r403z0af0bbaqhg02hq4jc97g5wds6lqw1l";
         procMacro = true;
+        libName = "tracing_attributes";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
           "Eliza Weisman <eliza@buoyant.io>"
@@ -4558,6 +4512,7 @@ rec {
         version = "0.1.32";
         edition = "2018";
         sha256 = "0m5aglin3cdwxpvbg6kz0r9r0k31j48n0kcfwsp6l49z26k3svf0";
+        libName = "tracing_core";
         authors = [
           "Tokio Contributors <team@tokio.rs>"
         ];
@@ -4614,11 +4569,6 @@ rec {
             usesDefaultFeatures = false;
           }
           {
-            name = "imbl";
-            packageId = "imbl";
-            features = [ "serde" ];
-          }
-          {
             name = "itertools";
             packageId = "itertools";
           }
@@ -4636,6 +4586,10 @@ rec {
             packageId = "md-5";
           }
           {
+            name = "nohash-hasher";
+            packageId = "nohash-hasher";
+          }
+          {
             name = "os_str_bytes";
             packageId = "os_str_bytes";
             features = [ "conversions" ];
@@ -4694,6 +4648,10 @@ rec {
             packageId = "tvix-eval-builtin-macros";
             rename = "builtin-macros";
           }
+          {
+            name = "vu128";
+            packageId = "vu128";
+          }
         ];
         devDependencies = [
           {
@@ -4702,7 +4660,7 @@ rec {
           }
         ];
         features = {
-          "arbitrary" = [ "proptest" "test-strategy" "imbl/proptest" ];
+          "arbitrary" = [ "proptest" "test-strategy" ];
           "default" = [ "impure" "arbitrary" "nix_tests" ];
           "proptest" = [ "dep:proptest" ];
           "test-strategy" = [ "dep:test-strategy" ];
@@ -4714,6 +4672,7 @@ rec {
         edition = "2021";
         src = lib.cleanSourceWith { filter = sourceFilter; src = ../../tvix/eval/builtin-macros; };
         procMacro = true;
+        libName = "tvix_eval_builtin_macros";
         authors = [
           "Griffin Smith <root@gws.fyi>"
         ];
@@ -4792,6 +4751,7 @@ rec {
         version = "1.0.12";
         edition = "2018";
         sha256 = "0jzf1znfpb2gx8nr8mvmyqs1crnv79l57nxnbiszc7xf7ynbjm1k";
+        libName = "unicode_ident";
         authors = [
           "David Tolnay <dtolnay@gmail.com>"
         ];
@@ -4802,6 +4762,7 @@ rec {
         version = "0.1.11";
         edition = "2015";
         sha256 = "11ds4ydhg8g7l06rlmh712q41qsrd0j0h00n1jm74kww3kqk65z5";
+        libName = "unicode_width";
         authors = [
           "kwantam <kwantam@gmail.com>"
           "Manish Goregaokar <manishsmail@gmail.com>"
@@ -4835,6 +4796,17 @@ rec {
         ];
 
       };
+      "vu128" = rec {
+        crateName = "vu128";
+        version = "1.1.0";
+        edition = "2018";
+        sha256 = "1pczgy26c0lsri1ddrx5wkgn0rcq4da04pqya5rl6vrwfnys73di";
+        libPath = "vu128/vu128.rs";
+        authors = [
+          "John Millikin <john@john-millikin.com>"
+        ];
+
+      };
       "wasi" = rec {
         crateName = "wasi";
         version = "0.11.0+wasi-snapshot-preview1";
@@ -4853,9 +4825,10 @@ rec {
       };
       "wasm-bindgen" = rec {
         crateName = "wasm-bindgen";
-        version = "0.2.92";
-        edition = "2018";
-        sha256 = "1a4mcw13nsk3fr8fxjzf9kk1wj88xkfsmnm0pjraw01ryqfm7qjb";
+        version = "0.2.93";
+        edition = "2021";
+        sha256 = "1dfr7pka5kwvky2fx82m9d060p842hc5fyyw8igryikcdb0xybm8";
+        libName = "wasm_bindgen";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -4865,6 +4838,10 @@ rec {
             packageId = "cfg-if";
           }
           {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
             name = "wasm-bindgen-macro";
             packageId = "wasm-bindgen-macro";
           }
@@ -4872,7 +4849,6 @@ rec {
         features = {
           "default" = [ "spans" "std" ];
           "enable-interning" = [ "std" ];
-          "gg-alloc" = [ "wasm-bindgen-test/gg-alloc" ];
           "serde" = [ "dep:serde" ];
           "serde-serialize" = [ "serde" "serde_json" "std" ];
           "serde_json" = [ "dep:serde_json" ];
@@ -4884,9 +4860,10 @@ rec {
       };
       "wasm-bindgen-backend" = rec {
         crateName = "wasm-bindgen-backend";
-        version = "0.2.92";
-        edition = "2018";
-        sha256 = "1nj7wxbi49f0rw9d44rjzms26xlw6r76b2mrggx8jfbdjrxphkb1";
+        version = "0.2.93";
+        edition = "2021";
+        sha256 = "0yypblaf94rdgqs5xw97499xfwgs1096yx026d6h88v563d9dqwx";
+        libName = "wasm_bindgen_backend";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -4931,6 +4908,7 @@ rec {
         version = "0.4.42";
         edition = "2018";
         sha256 = "1h322zjvpjllcpj7dahfxjsv6inkr6y0baw7nkdwivr1c4v19g3n";
+        libName = "wasm_bindgen_futures";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -4961,10 +4939,11 @@ rec {
       };
       "wasm-bindgen-macro" = rec {
         crateName = "wasm-bindgen-macro";
-        version = "0.2.92";
-        edition = "2018";
-        sha256 = "09npa1srjjabd6nfph5yc03jb26sycjlxhy0c2a1pdrpx4yq5y51";
+        version = "0.2.93";
+        edition = "2021";
+        sha256 = "1kycd1xfx4d9xzqknvzbiqhwb5fzvjqrrn88x692q1vblj8lqp2q";
         procMacro = true;
+        libName = "wasm_bindgen_macro";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -4986,9 +4965,10 @@ rec {
       };
       "wasm-bindgen-macro-support" = rec {
         crateName = "wasm-bindgen-macro-support";
-        version = "0.2.92";
-        edition = "2018";
-        sha256 = "1dqv2xs8zcyw4kjgzj84bknp2h76phmsb3n7j6hn396h4ssifkz9";
+        version = "0.2.93";
+        edition = "2021";
+        sha256 = "0dp8w6jmw44srym6l752nkr3hkplyw38a2fxz5f3j1ch9p3l1hxg";
+        libName = "wasm_bindgen_macro_support";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -5023,10 +5003,11 @@ rec {
       };
       "wasm-bindgen-shared" = rec {
         crateName = "wasm-bindgen-shared";
-        version = "0.2.92";
-        edition = "2018";
+        version = "0.2.93";
+        edition = "2021";
         links = "wasm_bindgen";
-        sha256 = "15kyavsrna2cvy30kg03va257fraf9x00ny554vxngvpyaa0q6dg";
+        sha256 = "1104bny0hv40jfap3hp8jhs0q4ya244qcrvql39i38xlghq0lan6";
+        libName = "wasm_bindgen_shared";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -5037,6 +5018,7 @@ rec {
         version = "0.3.69";
         edition = "2018";
         sha256 = "1vqkxk935xa8zcnsi4bd88sb267ly2i24xl1yiq26d1n32hskbvp";
+        libName = "web_sys";
         authors = [
           "The wasm-bindgen Developers"
         ];
@@ -5532,6 +5514,7 @@ rec {
         version = "0.4.0";
         edition = "2015";
         sha256 = "1dmpa6mvcvzz16zg6d5vrfy4bxgg541wxrcip7cnshi06v38ffxc";
+        libName = "winapi_i686_pc_windows_gnu";
         authors = [
           "Peter Atashian <retep998@gmail.com>"
         ];
@@ -5542,6 +5525,7 @@ rec {
         version = "0.1.6";
         edition = "2021";
         sha256 = "15i5lm39wd44004i9d5qspry2cynkrpvwzghr6s2c3dsk28nz7pj";
+        libName = "winapi_util";
         authors = [
           "Andrew Gallant <jamslam@gmail.com>"
         ];
@@ -5560,6 +5544,7 @@ rec {
         version = "0.4.0";
         edition = "2015";
         sha256 = "0gqq64czqb64kskjryj8isp62m2sgvx25yyj3kpc2myh85w24bki";
+        libName = "winapi_x86_64_pc_windows_gnu";
         authors = [
           "Peter Atashian <retep998@gmail.com>"
         ];
@@ -5709,6 +5694,7 @@ rec {
         edition = "2021";
         sha256 = "1qix6k8f8gzxb750icxvxknm3xrg8g7a4035g6gyasbd2sjqrz82";
         procMacro = true;
+        libName = "yew_macro";
         authors = [
           "Justin Starry <justin@yew.rs>"
         ];
@@ -5750,6 +5736,7 @@ rec {
         version = "0.18.0";
         edition = "2021";
         sha256 = "1ip071apn35gbycpki768ba5hxsrxnnsh2b29lmpdrln5h2xb8ac";
+        libName = "yew_router";
         authors = [
           "Hamza <muhammadhamza1311@gmail.com>"
         ];
@@ -5827,6 +5814,7 @@ rec {
         edition = "2021";
         sha256 = "134pcmiyi5x6v8s8rnr3fg03v033qhx2piflgkgcza3wl28d3gs2";
         procMacro = true;
+        libName = "yew_router_macro";
         authors = [
           "Hamza <muhammadhamza1311@gmail.com>"
         ];
@@ -5956,52 +5944,41 @@ rec {
                 testPostRun
               ]);
           in
-          pkgs.runCommand "run-tests-${testCrate.name}"
-            {
-              inherit testCrateFlags;
-              buildInputs = testInputs;
-            } ''
-            set -e
+          pkgs.stdenvNoCC.mkDerivation {
+            name = "run-tests-${testCrate.name}";
 
-            export RUST_BACKTRACE=1
+            inherit (crate) src;
 
-            # recreate a file hierarchy as when running tests with cargo
+            inherit testCrateFlags;
 
-            # the source for test data
-            # It's necessary to locate the source in $NIX_BUILD_TOP/source/
-            # instead of $NIX_BUILD_TOP/
-            # because we compiled those test binaries in the former and not the latter.
-            # So all paths will expect source tree to be there and not in the build top directly.
-            # For example: $NIX_BUILD_TOP := /build in general, if you ask yourself.
-            # NOTE: There could be edge cases if `crate.sourceRoot` does exist but
-            # it's very hard to reason about them.
-            # Open a bug if you run into this!
-            mkdir -p source/
-            cd source/
+            buildInputs = testInputs;
 
-            ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${crate.src}
+            buildPhase = ''
+              set -e
+              export RUST_BACKTRACE=1
 
-            # build outputs
-            testRoot=target/debug
-            mkdir -p $testRoot
+              # build outputs
+              testRoot=target/debug
+              mkdir -p $testRoot
 
-            # executables of the crate
-            # we copy to prevent std::env::current_exe() to resolve to a store location
-            for i in ${crate}/bin/*; do
-              cp "$i" "$testRoot"
-            done
-            chmod +w -R .
+              # executables of the crate
+              # we copy to prevent std::env::current_exe() to resolve to a store location
+              for i in ${crate}/bin/*; do
+                cp "$i" "$testRoot"
+              done
+              chmod +w -R .
 
-            # test harness executables are suffixed with a hash, like cargo does
-            # this allows to prevent name collision with the main
-            # executables of the crate
-            hash=$(basename $out)
-            for file in ${drv}/tests/*; do
-              f=$testRoot/$(basename $file)-$hash
-              cp $file $f
-              ${testCommand}
-            done
-          '';
+              # test harness executables are suffixed with a hash, like cargo does
+              # this allows to prevent name collision with the main
+              # executables of the crate
+              hash=$(basename $out)
+              for file in ${drv}/tests/*; do
+                f=$testRoot/$(basename $file)-$hash
+                cp $file $f
+                ${testCommand}
+              done
+            '';
+          };
       in
       pkgs.runCommand "${crate.name}-linked"
         {
diff --git a/web/tvixbolt/default.nix b/web/tvixbolt/default.nix
index 33c6622717d5..a3ce1662ff52 100644
--- a/web/tvixbolt/default.nix
+++ b/web/tvixbolt/default.nix
@@ -27,4 +27,6 @@ in
         "$@" \
         ${depot.web.tvixbolt}
   '';
+
+  meta.ci.extraSteps.crate2nix-check = depot.tvix.utils.mkCrate2nixCheck ./Cargo.nix;
 })
diff --git a/web/tvl/blog/2024-08-tvix-update.md b/web/tvl/blog/2024-08-tvix-update.md
index 7307c3927a2f..5fc15c02d164 100644
--- a/web/tvl/blog/2024-08-tvix-update.md
+++ b/web/tvl/blog/2024-08-tvix-update.md
@@ -45,7 +45,7 @@ file from the internet, clone of a git repo). These needed to be implemented
 for completeness. We implemented pretty much all downloads of Tarballs, NARs and
 plain files, except git repositories, which are left for later.
 
-Instead of doing these fetches immediately, we added a generic `Fetch` struct
+Instead of doing these fetches immediately, we added a generic `Fetch` type
 that allows describing such fetches *before actually doing them*, similar to
 being able to describe builds, and use the same "Drive builds on IO" machinery
 to delay these fetches to the point where it's needed. We also show progress
@@ -53,7 +53,7 @@ bars when doing fetches.
 
 Very early, during bootstrapping, nixpkgs relies on some `builtin:fetchurl`
 "fake" Derivation, which has some special handling logic in Nix. We implemented
-these quirks, by converting it to our generic `Fetch` struct and dealing with
+these quirks, by converting it to instances of our `Fetch` type and dealing with
 it there in a consistent fashion.
 
 ### More fixes, Refscan
@@ -159,8 +159,8 @@ store backends as the rest of Tvix.
 We landed some fixes in [crate2nix][crate2nix], the tool we're using to for
 per-crate incremental builds of Tvix.
 
-It now supports the corner cases needed to build WASM - so now [Tvixbolt]
-[tvixbolt] is built with it, too.
+It now supports the corner cases needed to build WASM - so now
+[Tvixbolt][tvixbolt] is built with it, too.
 
 We also fixed some bugs in how test directories are prepared, which unlocked
 running some more tests for filesystem related builtins such as `readDir` in our test suite.