From 5ac7a5ed37303332f1f00ac4e94cf6dbd76b0ec4 Mon Sep 17 00:00:00 2001 From: Dan Draper Date: Sun, 25 Oct 2026 23:44:09 +1100 Subject: [PATCH 1/8] chore(deps): take vitaminc from cipherstash/vitaminc#526 by git reference The Go JSON work adopts the storage profile for opaque whole-struct storage (vcffi.MarshalStorage in Go, vitaminc_aead_frame::storage in Rust), which vitaminc#520 and #526 add and no release carries yet. Every vitaminc crate the root workspace and the two Go guest workspaces resolve at 0.8 is patched to commit c226edf with [patch.crates-io], so no type is duplicated between a crates.io copy and a git copy (the 0.5 line cipherstash-client pulls in is a separate semver line and is left alone). go.mod moves vcffi and vcvalue to the same commit. The EQL subtree's workspace is not patched: it uses no API the release lacks. This is temporary: replace the three [patch] blocks and the go.mod pseudo-versions with the vitaminc release that ships #520/#526. Refs #1347 Claude-Session: https://claude.ai/code/session_01N7bCqa3ZVJMcEE99RzVqEu --- Cargo.lock | 46 ++++++++--------------- Cargo.toml | 27 +++++++++++++ languages/golang/auth/guest/Cargo.lock | 33 ++++++---------- languages/golang/auth/guest/Cargo.toml | 23 ++++++++++++ languages/golang/encrypt/guest/Cargo.lock | 46 ++++++++--------------- languages/golang/encrypt/guest/Cargo.toml | 27 +++++++++++++ languages/golang/go.mod | 7 +++- languages/golang/go.sum | 8 ++-- 8 files changed, 129 insertions(+), 88 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 271420eac..72db085bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4251,8 +4251,7 @@ dependencies = [ [[package]] name = "vitaminc" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea37168a603d605732d0bf0672586139ab91736f767d7b85e173c84dcee19054" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "vitaminc-aead 0.8.0", "vitaminc-context 0.8.0", @@ -4280,8 +4279,7 @@ dependencies = [ [[package]] name = "vitaminc-aead" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9bb97fd543ee6a2b168e257ec69d0d274a775b1eaa3f856b5ac2941b01891c1" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "bytes", "chrono", @@ -4310,8 +4308,7 @@ dependencies = [ [[package]] name = "vitaminc-aead-derive" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0845a44c07ddc882ed47a0e92e04fc83496220f054b589ad0cbd215c5009e5b7" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "proc-macro2", "quote", @@ -4321,11 +4318,11 @@ dependencies = [ [[package]] name = "vitaminc-aead-frame" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0299777255171712a296d59164691dc1e93d10bddaab6e248fede1790dfd2e" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "serde", "vitaminc-aead 0.8.0", + "vitaminc-protected 0.8.0", "zeroize", ] @@ -4342,8 +4339,7 @@ dependencies = [ [[package]] name = "vitaminc-context" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a630079ba2d50680ee5048addd430eda30613559809dd6d383551e6d3d299826" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "mutants", "vitaminc-protected 0.8.0", @@ -4366,8 +4362,7 @@ dependencies = [ [[package]] name = "vitaminc-encrypt" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db45e073f92ae8ca27f8fe14270bc34c75e767559c2347098dacca95180d1ddd" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "aes-gcm", "aws-lc-rs", @@ -4380,8 +4375,7 @@ dependencies = [ [[package]] name = "vitaminc-hmac" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5fa0ae8bbad85ae30fb5c91b0b72b694d59bf2c2c1e7ad799eda66fdbd67e7c" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "hmac", "sha2 0.11.0", @@ -4393,8 +4387,7 @@ dependencies = [ [[package]] name = "vitaminc-normalize" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5a2d8875e0b8b35b7a4f9dd698bbd13c989837cea564b5a4cbb5371b78fa937" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "thiserror 2.0.18", "unicode-general-category", @@ -4406,8 +4399,7 @@ dependencies = [ [[package]] name = "vitaminc-ore" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05518b4d35cb0c592dad59afb11b8e883ea252449b9ae47ff5ddeff0ed49f75" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "thiserror 2.0.18", "unicode-general-category", @@ -4422,8 +4414,7 @@ dependencies = [ [[package]] name = "vitaminc-prf" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "916688478273ca3e5c6805b355534ce9263688391cd4ab23b08bef760039d325" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "mutants", "thiserror 2.0.18", @@ -4453,8 +4444,7 @@ dependencies = [ [[package]] name = "vitaminc-protected" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6edc22562a9bbff0ba9704a2c43670b2c7cb1637524c045c3ba6436ed00917f" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "bitvec", "digest 0.11.3", @@ -4481,8 +4471,7 @@ dependencies = [ [[package]] name = "vitaminc-protected-derive" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cce3a7d0ce6f57c0e82345f20e87f3737cccd3a70aec2a2ab49f68a3a5e24a" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -4508,8 +4497,7 @@ dependencies = [ [[package]] name = "vitaminc-random" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb253f783ce6f31a55a92f5a9f0526109b8caa6a465edb94244fd344a078c11" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "chacha20", "getrandom 0.4.2", @@ -4534,8 +4522,7 @@ dependencies = [ [[package]] name = "vitaminc-random-derives" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0b3615936d6dded4b775856f4b0fa229d745d183f0b3754ac80035f16c27d9" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "proc-macro2", "quote", @@ -4561,8 +4548,7 @@ dependencies = [ [[package]] name = "vitaminc-traits" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9098ee4b13446ef959583ee2f3c384df571b453179104f7b4960ffddf65ee9" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "anyhow", "bytes", diff --git a/Cargo.toml b/Cargo.toml index 340cf5e3b..2d0ec96c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -154,3 +154,30 @@ vitaminc-normalize = "0.8.0" vitaminc-ore = "0.8.0" vitaminc-prf = { version = "0.8.0", features = ["normalize"] } vitaminc-protected = "0.8.0" + +# TEMPORARY: vitaminc from cipherstash/vitaminc#526 (head c226edf, stacked on +# #520), which adds the storage profile for kept values (Rust +# `vitaminc_aead_frame::storage`, Go `vcffi.MarshalStorage`). Both are +# unreleased. Every vitaminc crate this build resolves at 0.8 is patched to +# the one git revision, so no type is duplicated between a crates.io and a +# git copy. The Go guests under languages/golang carry the same block, and +# languages/golang/go.mod pins vcffi and vcvalue to the same commit. Replace +# all three with the vitaminc release that ships #520/#526. +# (The 0.5 copies cipherstash-client pulls in are a separate semver line and +# are not patched.) +[patch.crates-io] +vitaminc = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-aead = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-aead-derive = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-aead-frame = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-context = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-encrypt = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-hmac = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-normalize = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-ore = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-prf = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-protected = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-protected-derive = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-random = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-random-derives = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-traits = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } diff --git a/languages/golang/auth/guest/Cargo.lock b/languages/golang/auth/guest/Cargo.lock index fd1ccb4c5..54fe2b2ea 100644 --- a/languages/golang/auth/guest/Cargo.lock +++ b/languages/golang/auth/guest/Cargo.lock @@ -2300,8 +2300,7 @@ dependencies = [ [[package]] name = "vitaminc" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea37168a603d605732d0bf0672586139ab91736f767d7b85e173c84dcee19054" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "vitaminc-aead 0.8.0", "vitaminc-context 0.8.0", @@ -2329,8 +2328,7 @@ dependencies = [ [[package]] name = "vitaminc-aead" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9bb97fd543ee6a2b168e257ec69d0d274a775b1eaa3f856b5ac2941b01891c1" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "bytes", "futures-util", @@ -2357,8 +2355,7 @@ dependencies = [ [[package]] name = "vitaminc-aead-derive" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0845a44c07ddc882ed47a0e92e04fc83496220f054b589ad0cbd215c5009e5b7" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "proc-macro2", "quote", @@ -2368,8 +2365,7 @@ dependencies = [ [[package]] name = "vitaminc-aead-frame" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0299777255171712a296d59164691dc1e93d10bddaab6e248fede1790dfd2e" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "vitaminc-aead 0.8.0", ] @@ -2387,8 +2383,7 @@ dependencies = [ [[package]] name = "vitaminc-context" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a630079ba2d50680ee5048addd430eda30613559809dd6d383551e6d3d299826" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "mutants", "vitaminc-protected 0.8.0", @@ -2411,8 +2406,7 @@ dependencies = [ [[package]] name = "vitaminc-encrypt" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db45e073f92ae8ca27f8fe14270bc34c75e767559c2347098dacca95180d1ddd" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "aes-gcm", "aws-lc-rs", @@ -2442,8 +2436,7 @@ dependencies = [ [[package]] name = "vitaminc-protected" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6edc22562a9bbff0ba9704a2c43670b2c7cb1637524c045c3ba6436ed00917f" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "bitvec", "digest 0.11.3", @@ -2470,8 +2463,7 @@ dependencies = [ [[package]] name = "vitaminc-protected-derive" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cce3a7d0ce6f57c0e82345f20e87f3737cccd3a70aec2a2ab49f68a3a5e24a" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2497,8 +2489,7 @@ dependencies = [ [[package]] name = "vitaminc-random" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb253f783ce6f31a55a92f5a9f0526109b8caa6a465edb94244fd344a078c11" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "chacha20", "getrandom 0.4.3", @@ -2523,8 +2514,7 @@ dependencies = [ [[package]] name = "vitaminc-random-derives" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0b3615936d6dded4b775856f4b0fa229d745d183f0b3754ac80035f16c27d9" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "proc-macro2", "quote", @@ -2550,8 +2540,7 @@ dependencies = [ [[package]] name = "vitaminc-traits" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9098ee4b13446ef959583ee2f3c384df571b453179104f7b4960ffddf65ee9" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "anyhow", "bytes", diff --git a/languages/golang/auth/guest/Cargo.toml b/languages/golang/auth/guest/Cargo.toml index 8ffa0937d..2db37da47 100644 --- a/languages/golang/auth/guest/Cargo.toml +++ b/languages/golang/auth/guest/Cargo.toml @@ -50,6 +50,29 @@ tempfile = "3" # `Controlled::risky_ref`, to read a codec value back in the tests. vitaminc-protected = "0.8.0" +# TEMPORARY: vitaminc from cipherstash/vitaminc#526 (head c226edf, stacked on +# #520), which adds the storage profile for kept values (Rust +# `vitaminc_aead_frame::storage`, Go `vcffi.MarshalStorage`). Both are +# unreleased. Every vitaminc crate this build resolves at 0.8 is patched to +# the one git revision, so no type is duplicated between a crates.io and a +# git copy. The repository root's Cargo.toml and the other Go guest carry the same +# block, and languages/golang/go.mod pins vcffi and vcvalue to the same +# commit. Replace all of them with the vitaminc release that ships #520/#526. +# (The 0.5 copies cipherstash-client pulls in are a separate semver line and +# are not patched.) +[patch.crates-io] +vitaminc = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-aead = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-aead-derive = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-aead-frame = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-context = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-encrypt = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-protected = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-protected-derive = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-random = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-random-derives = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-traits = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } + [profile.release] # Smaller .wasm; the guest is IO-bound on the FFI copy and the file reads, # not on codegen. diff --git a/languages/golang/encrypt/guest/Cargo.lock b/languages/golang/encrypt/guest/Cargo.lock index 34e8e7007..b066fe766 100644 --- a/languages/golang/encrypt/guest/Cargo.lock +++ b/languages/golang/encrypt/guest/Cargo.lock @@ -2726,8 +2726,7 @@ dependencies = [ [[package]] name = "vitaminc" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea37168a603d605732d0bf0672586139ab91736f767d7b85e173c84dcee19054" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "vitaminc-aead 0.8.0", "vitaminc-context 0.8.0", @@ -2755,8 +2754,7 @@ dependencies = [ [[package]] name = "vitaminc-aead" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9bb97fd543ee6a2b168e257ec69d0d274a775b1eaa3f856b5ac2941b01891c1" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "bytes", "futures-util", @@ -2783,8 +2781,7 @@ dependencies = [ [[package]] name = "vitaminc-aead-derive" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0845a44c07ddc882ed47a0e92e04fc83496220f054b589ad0cbd215c5009e5b7" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "proc-macro2", "quote", @@ -2794,11 +2791,11 @@ dependencies = [ [[package]] name = "vitaminc-aead-frame" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0299777255171712a296d59164691dc1e93d10bddaab6e248fede1790dfd2e" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "serde", "vitaminc-aead 0.8.0", + "vitaminc-protected 0.8.0", "zeroize", ] @@ -2815,8 +2812,7 @@ dependencies = [ [[package]] name = "vitaminc-context" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a630079ba2d50680ee5048addd430eda30613559809dd6d383551e6d3d299826" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "mutants", "vitaminc-protected 0.8.0", @@ -2839,8 +2835,7 @@ dependencies = [ [[package]] name = "vitaminc-encrypt" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db45e073f92ae8ca27f8fe14270bc34c75e767559c2347098dacca95180d1ddd" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "aes-gcm", "aws-lc-rs", @@ -2853,8 +2848,7 @@ dependencies = [ [[package]] name = "vitaminc-hmac" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5fa0ae8bbad85ae30fb5c91b0b72b694d59bf2c2c1e7ad799eda66fdbd67e7c" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "hmac", "sha2 0.11.0", @@ -2866,8 +2860,7 @@ dependencies = [ [[package]] name = "vitaminc-normalize" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5a2d8875e0b8b35b7a4f9dd698bbd13c989837cea564b5a4cbb5371b78fa937" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "thiserror 2.0.20", "unicode-general-category", @@ -2879,8 +2872,7 @@ dependencies = [ [[package]] name = "vitaminc-ore" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05518b4d35cb0c592dad59afb11b8e883ea252449b9ae47ff5ddeff0ed49f75" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "thiserror 2.0.20", "unicode-general-category", @@ -2895,8 +2887,7 @@ dependencies = [ [[package]] name = "vitaminc-prf" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "916688478273ca3e5c6805b355534ce9263688391cd4ab23b08bef760039d325" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "mutants", "thiserror 2.0.20", @@ -2926,8 +2917,7 @@ dependencies = [ [[package]] name = "vitaminc-protected" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6edc22562a9bbff0ba9704a2c43670b2c7cb1637524c045c3ba6436ed00917f" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "bitvec", "digest 0.11.3", @@ -2954,8 +2944,7 @@ dependencies = [ [[package]] name = "vitaminc-protected-derive" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cce3a7d0ce6f57c0e82345f20e87f3737cccd3a70aec2a2ab49f68a3a5e24a" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2981,8 +2970,7 @@ dependencies = [ [[package]] name = "vitaminc-random" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb253f783ce6f31a55a92f5a9f0526109b8caa6a465edb94244fd344a078c11" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "chacha20", "getrandom 0.4.3", @@ -3007,8 +2995,7 @@ dependencies = [ [[package]] name = "vitaminc-random-derives" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0b3615936d6dded4b775856f4b0fa229d745d183f0b3754ac80035f16c27d9" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "proc-macro2", "quote", @@ -3034,8 +3021,7 @@ dependencies = [ [[package]] name = "vitaminc-traits" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9098ee4b13446ef959583ee2f3c384df571b453179104f7b4960ffddf65ee9" +source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" dependencies = [ "anyhow", "bytes", diff --git a/languages/golang/encrypt/guest/Cargo.toml b/languages/golang/encrypt/guest/Cargo.toml index 4df0b70ba..a7fd09045 100644 --- a/languages/golang/encrypt/guest/Cargo.toml +++ b/languages/golang/encrypt/guest/Cargo.toml @@ -82,6 +82,33 @@ recipher = "=0.3.1" base16ct = { version = "0.2.0", features = ["alloc"] } base64ct = { version = "1.7", features = ["alloc"] } +# TEMPORARY: vitaminc from cipherstash/vitaminc#526 (head c226edf, stacked on +# #520), which adds the storage profile for kept values (Rust +# `vitaminc_aead_frame::storage`, Go `vcffi.MarshalStorage`). Both are +# unreleased. Every vitaminc crate this build resolves at 0.8 is patched to +# the one git revision, so no type is duplicated between a crates.io and a +# git copy. The repository root's Cargo.toml and the other Go guest carry the same +# block, and languages/golang/go.mod pins vcffi and vcvalue to the same +# commit. Replace all of them with the vitaminc release that ships #520/#526. +# (The 0.5 copies cipherstash-client pulls in are a separate semver line and +# are not patched.) +[patch.crates-io] +vitaminc = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-aead = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-aead-derive = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-aead-frame = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-context = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-encrypt = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-hmac = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-normalize = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-ore = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-prf = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-protected = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-protected-derive = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-random = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-random-derives = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } +vitaminc-traits = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } + [profile.release] # Smaller .wasm; the guest is IO-bound on the FFI copy and the ZeroKMS round # trip, not on codegen. diff --git a/languages/golang/go.mod b/languages/golang/go.mod index 2df28f1c2..68ef7e5ea 100644 --- a/languages/golang/go.mod +++ b/languages/golang/go.mod @@ -2,9 +2,12 @@ module github.com/cipherstash/stack/languages/golang go 1.26.10 +// TEMPORARY: vcffi and vcvalue at cipherstash/vitaminc#526 (head c226edf), +// unreleased, for the storage profile (vcffi.MarshalStorage). The Rust +// workspaces patch vitaminc to the same commit. Replace with the release. require ( - github.com/cipherstash/vitaminc/bindings/go/vcffi v0.0.0-20261017042657-ca2298d3e7e8 - github.com/cipherstash/vitaminc/bindings/go/vcvalue v0.0.0-20261017042657-ca2298d3e7e8 + github.com/cipherstash/vitaminc/bindings/go/vcffi v0.0.0-20261025114513-c226edfeaf43 + github.com/cipherstash/vitaminc/bindings/go/vcvalue v0.0.0-20261025114513-c226edfeaf43 github.com/tetratelabs/wazero v1.12.0 golang.org/x/oauth2 v0.37.0 ) diff --git a/languages/golang/go.sum b/languages/golang/go.sum index dfafe678f..91db0b468 100644 --- a/languages/golang/go.sum +++ b/languages/golang/go.sum @@ -1,7 +1,7 @@ -github.com/cipherstash/vitaminc/bindings/go/vcffi v0.0.0-20261017042657-ca2298d3e7e8 h1:kXpXvBsE5P35aw8yN8l2k3ARuoPdjQkXvxocg4D2WUc= -github.com/cipherstash/vitaminc/bindings/go/vcffi v0.0.0-20261017042657-ca2298d3e7e8/go.mod h1:6jpaqAo6f7rjWuxoti0Ymoi9DQZyxpnieWVudhXv38Y= -github.com/cipherstash/vitaminc/bindings/go/vcvalue v0.0.0-20261017042657-ca2298d3e7e8 h1:72kmUdUUZENerHctDhFMD31/mWtE80cRbpjLPZOeLCs= -github.com/cipherstash/vitaminc/bindings/go/vcvalue v0.0.0-20261017042657-ca2298d3e7e8/go.mod h1:RJODA1DCSm4H+1pbmCzDdT2pAIkxhinwIu6ewjh5sPs= +github.com/cipherstash/vitaminc/bindings/go/vcffi v0.0.0-20261025114513-c226edfeaf43 h1:lsIsc5mnadNjZNI6DNS3F3Ey1uX9wVR3ctSW/D1r42w= +github.com/cipherstash/vitaminc/bindings/go/vcffi v0.0.0-20261025114513-c226edfeaf43/go.mod h1:7hPdj2tN3YpcPZYdZ+Fe7+Fsc7dnSyH5bsL9y+LbRuM= +github.com/cipherstash/vitaminc/bindings/go/vcvalue v0.0.0-20261025114513-c226edfeaf43 h1:JW0R2y5NzhYhlGg1FO77GYeXGDHDRvRJYOtTMEtANlQ= +github.com/cipherstash/vitaminc/bindings/go/vcvalue v0.0.0-20261025114513-c226edfeaf43/go.mod h1:RJODA1DCSm4H+1pbmCzDdT2pAIkxhinwIu6ewjh5sPs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From 207fa46676731b26b13d2e280b31314d07262c25 Mon Sep 17 00:00:00 2001 From: Dan Draper Date: Sun, 25 Oct 2026 23:46:02 +1100 Subject: [PATCH 2/8] test(stack-encrypt): build the float round-trip test against the renamed read helpers #1366's test landed after #1363 renamed the suite's helpers, so the json_index test binary did not compile on next: `subtree_nodes` became `read_rows`, and an extracted entry opens only through the selector that asked for it (JsonExtracted), not as a bare JsonNode. The test now reads the walk with `read_rows` and opens the element through `extracted`, which is how `->`'s answer is opened everywhere else in the suite. What it asserts is unchanged. Refs #1364 Claude-Session: https://claude.ai/code/session_01N7bCqa3ZVJMcEE99RzVqEu --- packages/stack-encrypt/tests/json_index.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/stack-encrypt/tests/json_index.rs b/packages/stack-encrypt/tests/json_index.rs index c893bc0c9..a89cd9da2 100644 --- a/packages/stack-encrypt/tests/json_index.rs +++ b/packages/stack-encrypt/tests/json_index.rs @@ -2157,8 +2157,8 @@ mod given_a_float_at_full_precision { let key = subtree_key(cipher, "$.list", JsonOptions::default()) .await .unwrap(); - let rows = subtree_nodes(&sealed, key.token_bytes()); - let element = sealed.node(2).unwrap(); + let rows = read_rows(&sealed, &key); + let element = extracted(cipher, "$.list[0]", sealed.node(2).unwrap()).await; vec![ ( "the document, from the sealed root", @@ -2171,7 +2171,7 @@ mod given_a_float_at_full_precision { ( "the extracted element, as `->` returns it", cipher - .decrypt_as::(element, aead()) + .decrypt_as::(element, aead()) .await .unwrap(), ), From 8d36a510cc26bda8a93393779e01d5904b408f10 Mon Sep 17 00:00:00 2001 From: Dan Draper Date: Sun, 25 Oct 2026 23:52:17 +1100 Subject: [PATCH 3/8] test(stack-encrypt): run the registry-path UI case only while vitaminc comes from crates.io With vitaminc patched to a git revision, rustc's diagnostic for plaintext_needs_encrypt names where vitaminc's Encrypt trait is defined by the git checkout's absolute path under the machine's CARGO_HOME. trybuild normalizes a crates.io path to `$CARGO/-$VERSION` and a git checkout's not at all, so no recording matches on every machine. The case moves to tests/ui/registry_vitaminc and runs only when the workspace Cargo.lock resolves vitaminc from crates.io. It is skipped, with a line on stderr, while the temporary [patch.crates-io] block is in place, and runs again the day the block is removed. Its recording is the one next carries, with only the file's new path. Refs #1347 Claude-Session: https://claude.ai/code/session_01N7bCqa3ZVJMcEE99RzVqEu --- packages/stack-encrypt/tests/ui.rs | 23 +++++++++++++++++++ .../plaintext_needs_encrypt.rs | 0 .../plaintext_needs_encrypt.stderr | 4 ++-- 3 files changed, 25 insertions(+), 2 deletions(-) rename packages/stack-encrypt/tests/ui/{ => registry_vitaminc}/plaintext_needs_encrypt.rs (100%) rename packages/stack-encrypt/tests/ui/{ => registry_vitaminc}/plaintext_needs_encrypt.stderr (93%) diff --git a/packages/stack-encrypt/tests/ui.rs b/packages/stack-encrypt/tests/ui.rs index e671d2465..f1311b119 100644 --- a/packages/stack-encrypt/tests/ui.rs +++ b/packages/stack-encrypt/tests/ui.rs @@ -13,7 +13,30 @@ fn derive_diagnostics() { let t = trybuild::TestCases::new(); t.compile_fail("tests/ui/*.rs"); + // These cases' diagnostics name where vitaminc's `Encrypt` trait is + // defined, beside the older vitaminc that cts-common pulls in. trybuild + // writes a crates.io path as `$CARGO/-$VERSION`, and a git + // checkout's as the absolute path under the machine's CARGO_HOME, which + // no recording can match on every machine. So they run only while the + // workspace takes vitaminc from crates.io; while the root Cargo.toml's + // temporary `[patch.crates-io]` block points it at a git revision, they + // are skipped, and they run again the day that block is removed. + if vitaminc_from_crates_io() { + t.compile_fail("tests/ui/registry_vitaminc/*.rs"); + } else { + eprintln!( + "skipping tests/ui/registry_vitaminc: the workspace takes vitaminc from a git revision" + ); + } // And the shapes that must compile, so a diagnostic never grows to // cover a valid call. t.pass("tests/ui/pass/*.rs"); } + +/// Whether the workspace lock resolves `vitaminc-aead` from crates.io, as +/// it does with no `[patch]` pointing it at a git revision. +fn vitaminc_from_crates_io() -> bool { + let lock = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../Cargo.lock"); + let lock = std::fs::read_to_string(lock).expect("the workspace Cargo.lock"); + !lock.contains("git+https://github.com/cipherstash/vitaminc") +} diff --git a/packages/stack-encrypt/tests/ui/plaintext_needs_encrypt.rs b/packages/stack-encrypt/tests/ui/registry_vitaminc/plaintext_needs_encrypt.rs similarity index 100% rename from packages/stack-encrypt/tests/ui/plaintext_needs_encrypt.rs rename to packages/stack-encrypt/tests/ui/registry_vitaminc/plaintext_needs_encrypt.rs diff --git a/packages/stack-encrypt/tests/ui/plaintext_needs_encrypt.stderr b/packages/stack-encrypt/tests/ui/registry_vitaminc/plaintext_needs_encrypt.stderr similarity index 93% rename from packages/stack-encrypt/tests/ui/plaintext_needs_encrypt.stderr rename to packages/stack-encrypt/tests/ui/registry_vitaminc/plaintext_needs_encrypt.stderr index d357fbec3..c94b3ee75 100644 --- a/packages/stack-encrypt/tests/ui/plaintext_needs_encrypt.stderr +++ b/packages/stack-encrypt/tests/ui/registry_vitaminc/plaintext_needs_encrypt.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `SerdeOnly: stack_encrypt::Encrypt` is not satisfied - --> tests/ui/plaintext_needs_encrypt.rs:9:33 + --> tests/ui/registry_vitaminc/plaintext_needs_encrypt.rs:9:33 | 9 | let _=keyset.encrypt_as::<_,Target>(&SerdeOnly {value:"x".into()},nonempty!("column").into()); | ---------- ^^^^^^ unsatisfied trait bound @@ -7,7 +7,7 @@ error[E0277]: the trait bound `SerdeOnly: stack_encrypt::Encrypt` is not satisfi | required by a bound introduced by this call | help: the trait `stack_encrypt::Encrypt` is not implemented for `SerdeOnly` - --> tests/ui/plaintext_needs_encrypt.rs:4:1 + --> tests/ui/registry_vitaminc/plaintext_needs_encrypt.rs:4:1 | 4 | struct SerdeOnly { value: String } | ^^^^^^^^^^^^^^^^ From 2bdc882923e265fb31ce83394de3abc510139d1e Mon Sep 17 00:00:00 2001 From: Dan Draper Date: Mon, 26 Oct 2026 01:58:11 +1100 Subject: [PATCH 4/8] chore(deps): keep the Rust workspaces on vitaminc 0.8.0; only Go takes #526 Review found the Rust [patch.crates-io] blocks unnecessary: nothing in this stack's Rust uses the storage profile, which only Go writes and reads, and the guests' transport is unchanged at c226edf. The blocks also missed the stack-encrypt fuzz and EQL workspaces, put an unreleased git source into every shipped Rust artifact, and cost a UI test case. The root and guest manifests, their lockfiles and tests/ui.rs return to next's. go.mod keeps vcffi and vcvalue at c226edf, and the Go CHANGELOG records the one encoding that moves with them: a nil pointer to a fixed-width value is written as a typed null, which the engine reads as a null. Refs #1347 Claude-Session: https://claude.ai/code/session_01N7bCqa3ZVJMcEE99RzVqEu --- Cargo.lock | 46 ++++++++++++------- Cargo.toml | 27 ----------- languages/golang/CHANGELOG.md | 9 ++++ languages/golang/auth/guest/Cargo.lock | 33 ++++++++----- languages/golang/auth/guest/Cargo.toml | 23 ---------- languages/golang/encrypt/guest/Cargo.lock | 46 ++++++++++++------- languages/golang/encrypt/guest/Cargo.toml | 27 ----------- languages/golang/go.mod | 5 +- packages/stack-encrypt/tests/ui.rs | 23 ---------- .../plaintext_needs_encrypt.rs | 0 .../plaintext_needs_encrypt.stderr | 4 +- 11 files changed, 96 insertions(+), 147 deletions(-) rename packages/stack-encrypt/tests/ui/{registry_vitaminc => }/plaintext_needs_encrypt.rs (100%) rename packages/stack-encrypt/tests/ui/{registry_vitaminc => }/plaintext_needs_encrypt.stderr (93%) diff --git a/Cargo.lock b/Cargo.lock index 72db085bc..271420eac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4251,7 +4251,8 @@ dependencies = [ [[package]] name = "vitaminc" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea37168a603d605732d0bf0672586139ab91736f767d7b85e173c84dcee19054" dependencies = [ "vitaminc-aead 0.8.0", "vitaminc-context 0.8.0", @@ -4279,7 +4280,8 @@ dependencies = [ [[package]] name = "vitaminc-aead" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9bb97fd543ee6a2b168e257ec69d0d274a775b1eaa3f856b5ac2941b01891c1" dependencies = [ "bytes", "chrono", @@ -4308,7 +4310,8 @@ dependencies = [ [[package]] name = "vitaminc-aead-derive" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0845a44c07ddc882ed47a0e92e04fc83496220f054b589ad0cbd215c5009e5b7" dependencies = [ "proc-macro2", "quote", @@ -4318,11 +4321,11 @@ dependencies = [ [[package]] name = "vitaminc-aead-frame" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0299777255171712a296d59164691dc1e93d10bddaab6e248fede1790dfd2e" dependencies = [ "serde", "vitaminc-aead 0.8.0", - "vitaminc-protected 0.8.0", "zeroize", ] @@ -4339,7 +4342,8 @@ dependencies = [ [[package]] name = "vitaminc-context" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a630079ba2d50680ee5048addd430eda30613559809dd6d383551e6d3d299826" dependencies = [ "mutants", "vitaminc-protected 0.8.0", @@ -4362,7 +4366,8 @@ dependencies = [ [[package]] name = "vitaminc-encrypt" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db45e073f92ae8ca27f8fe14270bc34c75e767559c2347098dacca95180d1ddd" dependencies = [ "aes-gcm", "aws-lc-rs", @@ -4375,7 +4380,8 @@ dependencies = [ [[package]] name = "vitaminc-hmac" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5fa0ae8bbad85ae30fb5c91b0b72b694d59bf2c2c1e7ad799eda66fdbd67e7c" dependencies = [ "hmac", "sha2 0.11.0", @@ -4387,7 +4393,8 @@ dependencies = [ [[package]] name = "vitaminc-normalize" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5a2d8875e0b8b35b7a4f9dd698bbd13c989837cea564b5a4cbb5371b78fa937" dependencies = [ "thiserror 2.0.18", "unicode-general-category", @@ -4399,7 +4406,8 @@ dependencies = [ [[package]] name = "vitaminc-ore" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05518b4d35cb0c592dad59afb11b8e883ea252449b9ae47ff5ddeff0ed49f75" dependencies = [ "thiserror 2.0.18", "unicode-general-category", @@ -4414,7 +4422,8 @@ dependencies = [ [[package]] name = "vitaminc-prf" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916688478273ca3e5c6805b355534ce9263688391cd4ab23b08bef760039d325" dependencies = [ "mutants", "thiserror 2.0.18", @@ -4444,7 +4453,8 @@ dependencies = [ [[package]] name = "vitaminc-protected" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6edc22562a9bbff0ba9704a2c43670b2c7cb1637524c045c3ba6436ed00917f" dependencies = [ "bitvec", "digest 0.11.3", @@ -4471,7 +4481,8 @@ dependencies = [ [[package]] name = "vitaminc-protected-derive" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cce3a7d0ce6f57c0e82345f20e87f3737cccd3a70aec2a2ab49f68a3a5e24a" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -4497,7 +4508,8 @@ dependencies = [ [[package]] name = "vitaminc-random" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb253f783ce6f31a55a92f5a9f0526109b8caa6a465edb94244fd344a078c11" dependencies = [ "chacha20", "getrandom 0.4.2", @@ -4522,7 +4534,8 @@ dependencies = [ [[package]] name = "vitaminc-random-derives" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0b3615936d6dded4b775856f4b0fa229d745d183f0b3754ac80035f16c27d9" dependencies = [ "proc-macro2", "quote", @@ -4548,7 +4561,8 @@ dependencies = [ [[package]] name = "vitaminc-traits" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9098ee4b13446ef959583ee2f3c384df571b453179104f7b4960ffddf65ee9" dependencies = [ "anyhow", "bytes", diff --git a/Cargo.toml b/Cargo.toml index 2d0ec96c7..340cf5e3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -154,30 +154,3 @@ vitaminc-normalize = "0.8.0" vitaminc-ore = "0.8.0" vitaminc-prf = { version = "0.8.0", features = ["normalize"] } vitaminc-protected = "0.8.0" - -# TEMPORARY: vitaminc from cipherstash/vitaminc#526 (head c226edf, stacked on -# #520), which adds the storage profile for kept values (Rust -# `vitaminc_aead_frame::storage`, Go `vcffi.MarshalStorage`). Both are -# unreleased. Every vitaminc crate this build resolves at 0.8 is patched to -# the one git revision, so no type is duplicated between a crates.io and a -# git copy. The Go guests under languages/golang carry the same block, and -# languages/golang/go.mod pins vcffi and vcvalue to the same commit. Replace -# all three with the vitaminc release that ships #520/#526. -# (The 0.5 copies cipherstash-client pulls in are a separate semver line and -# are not patched.) -[patch.crates-io] -vitaminc = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-aead = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-aead-derive = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-aead-frame = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-context = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-encrypt = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-hmac = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-normalize = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-ore = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-prf = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-protected = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-protected-derive = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-random = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-random-derives = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-traits = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } diff --git a/languages/golang/CHANGELOG.md b/languages/golang/CHANGELOG.md index 6a2a4f4d2..a92a8160e 100644 --- a/languages/golang/CHANGELOG.md +++ b/languages/golang/CHANGELOG.md @@ -187,6 +187,15 @@ listed under **Breaking**. Releases are tagged `languages/golang/vX.Y.Z`. ### Changed +- **vitaminc's Go bindings from cipherstash/vitaminc#526 (temporary).** + `vcffi` and `vcvalue` move to an unreleased commit, for the storage + profile (`vcffi.MarshalStorage`). One encoding changes with them: a nil + pointer to a fixed-width value (`(*int64)(nil)`) is written as a typed + null where it was written as a null. The engine reads a typed null as a + null, and generated code passes an untyped nil for a nil pointer, so + this only shows in the bytes a hand-built `record.Source` or query input + sends. The pin returns to a vitaminc release before this module is + tagged. - **An EQL v4 block ORE term is one hex string.** The `ob` of a value encrypted into an `_ord_ore` or `text_search_ore` EQL type is `"ob": "0202…"`, where it was a one-element array, and the EQL v4 bundle diff --git a/languages/golang/auth/guest/Cargo.lock b/languages/golang/auth/guest/Cargo.lock index 54fe2b2ea..fd1ccb4c5 100644 --- a/languages/golang/auth/guest/Cargo.lock +++ b/languages/golang/auth/guest/Cargo.lock @@ -2300,7 +2300,8 @@ dependencies = [ [[package]] name = "vitaminc" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea37168a603d605732d0bf0672586139ab91736f767d7b85e173c84dcee19054" dependencies = [ "vitaminc-aead 0.8.0", "vitaminc-context 0.8.0", @@ -2328,7 +2329,8 @@ dependencies = [ [[package]] name = "vitaminc-aead" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9bb97fd543ee6a2b168e257ec69d0d274a775b1eaa3f856b5ac2941b01891c1" dependencies = [ "bytes", "futures-util", @@ -2355,7 +2357,8 @@ dependencies = [ [[package]] name = "vitaminc-aead-derive" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0845a44c07ddc882ed47a0e92e04fc83496220f054b589ad0cbd215c5009e5b7" dependencies = [ "proc-macro2", "quote", @@ -2365,7 +2368,8 @@ dependencies = [ [[package]] name = "vitaminc-aead-frame" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0299777255171712a296d59164691dc1e93d10bddaab6e248fede1790dfd2e" dependencies = [ "vitaminc-aead 0.8.0", ] @@ -2383,7 +2387,8 @@ dependencies = [ [[package]] name = "vitaminc-context" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a630079ba2d50680ee5048addd430eda30613559809dd6d383551e6d3d299826" dependencies = [ "mutants", "vitaminc-protected 0.8.0", @@ -2406,7 +2411,8 @@ dependencies = [ [[package]] name = "vitaminc-encrypt" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db45e073f92ae8ca27f8fe14270bc34c75e767559c2347098dacca95180d1ddd" dependencies = [ "aes-gcm", "aws-lc-rs", @@ -2436,7 +2442,8 @@ dependencies = [ [[package]] name = "vitaminc-protected" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6edc22562a9bbff0ba9704a2c43670b2c7cb1637524c045c3ba6436ed00917f" dependencies = [ "bitvec", "digest 0.11.3", @@ -2463,7 +2470,8 @@ dependencies = [ [[package]] name = "vitaminc-protected-derive" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cce3a7d0ce6f57c0e82345f20e87f3737cccd3a70aec2a2ab49f68a3a5e24a" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2489,7 +2497,8 @@ dependencies = [ [[package]] name = "vitaminc-random" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb253f783ce6f31a55a92f5a9f0526109b8caa6a465edb94244fd344a078c11" dependencies = [ "chacha20", "getrandom 0.4.3", @@ -2514,7 +2523,8 @@ dependencies = [ [[package]] name = "vitaminc-random-derives" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0b3615936d6dded4b775856f4b0fa229d745d183f0b3754ac80035f16c27d9" dependencies = [ "proc-macro2", "quote", @@ -2540,7 +2550,8 @@ dependencies = [ [[package]] name = "vitaminc-traits" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9098ee4b13446ef959583ee2f3c384df571b453179104f7b4960ffddf65ee9" dependencies = [ "anyhow", "bytes", diff --git a/languages/golang/auth/guest/Cargo.toml b/languages/golang/auth/guest/Cargo.toml index 2db37da47..8ffa0937d 100644 --- a/languages/golang/auth/guest/Cargo.toml +++ b/languages/golang/auth/guest/Cargo.toml @@ -50,29 +50,6 @@ tempfile = "3" # `Controlled::risky_ref`, to read a codec value back in the tests. vitaminc-protected = "0.8.0" -# TEMPORARY: vitaminc from cipherstash/vitaminc#526 (head c226edf, stacked on -# #520), which adds the storage profile for kept values (Rust -# `vitaminc_aead_frame::storage`, Go `vcffi.MarshalStorage`). Both are -# unreleased. Every vitaminc crate this build resolves at 0.8 is patched to -# the one git revision, so no type is duplicated between a crates.io and a -# git copy. The repository root's Cargo.toml and the other Go guest carry the same -# block, and languages/golang/go.mod pins vcffi and vcvalue to the same -# commit. Replace all of them with the vitaminc release that ships #520/#526. -# (The 0.5 copies cipherstash-client pulls in are a separate semver line and -# are not patched.) -[patch.crates-io] -vitaminc = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-aead = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-aead-derive = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-aead-frame = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-context = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-encrypt = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-protected = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-protected-derive = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-random = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-random-derives = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-traits = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } - [profile.release] # Smaller .wasm; the guest is IO-bound on the FFI copy and the file reads, # not on codegen. diff --git a/languages/golang/encrypt/guest/Cargo.lock b/languages/golang/encrypt/guest/Cargo.lock index b066fe766..34e8e7007 100644 --- a/languages/golang/encrypt/guest/Cargo.lock +++ b/languages/golang/encrypt/guest/Cargo.lock @@ -2726,7 +2726,8 @@ dependencies = [ [[package]] name = "vitaminc" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea37168a603d605732d0bf0672586139ab91736f767d7b85e173c84dcee19054" dependencies = [ "vitaminc-aead 0.8.0", "vitaminc-context 0.8.0", @@ -2754,7 +2755,8 @@ dependencies = [ [[package]] name = "vitaminc-aead" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9bb97fd543ee6a2b168e257ec69d0d274a775b1eaa3f856b5ac2941b01891c1" dependencies = [ "bytes", "futures-util", @@ -2781,7 +2783,8 @@ dependencies = [ [[package]] name = "vitaminc-aead-derive" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0845a44c07ddc882ed47a0e92e04fc83496220f054b589ad0cbd215c5009e5b7" dependencies = [ "proc-macro2", "quote", @@ -2791,11 +2794,11 @@ dependencies = [ [[package]] name = "vitaminc-aead-frame" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0299777255171712a296d59164691dc1e93d10bddaab6e248fede1790dfd2e" dependencies = [ "serde", "vitaminc-aead 0.8.0", - "vitaminc-protected 0.8.0", "zeroize", ] @@ -2812,7 +2815,8 @@ dependencies = [ [[package]] name = "vitaminc-context" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a630079ba2d50680ee5048addd430eda30613559809dd6d383551e6d3d299826" dependencies = [ "mutants", "vitaminc-protected 0.8.0", @@ -2835,7 +2839,8 @@ dependencies = [ [[package]] name = "vitaminc-encrypt" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db45e073f92ae8ca27f8fe14270bc34c75e767559c2347098dacca95180d1ddd" dependencies = [ "aes-gcm", "aws-lc-rs", @@ -2848,7 +2853,8 @@ dependencies = [ [[package]] name = "vitaminc-hmac" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5fa0ae8bbad85ae30fb5c91b0b72b694d59bf2c2c1e7ad799eda66fdbd67e7c" dependencies = [ "hmac", "sha2 0.11.0", @@ -2860,7 +2866,8 @@ dependencies = [ [[package]] name = "vitaminc-normalize" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5a2d8875e0b8b35b7a4f9dd698bbd13c989837cea564b5a4cbb5371b78fa937" dependencies = [ "thiserror 2.0.20", "unicode-general-category", @@ -2872,7 +2879,8 @@ dependencies = [ [[package]] name = "vitaminc-ore" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05518b4d35cb0c592dad59afb11b8e883ea252449b9ae47ff5ddeff0ed49f75" dependencies = [ "thiserror 2.0.20", "unicode-general-category", @@ -2887,7 +2895,8 @@ dependencies = [ [[package]] name = "vitaminc-prf" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916688478273ca3e5c6805b355534ce9263688391cd4ab23b08bef760039d325" dependencies = [ "mutants", "thiserror 2.0.20", @@ -2917,7 +2926,8 @@ dependencies = [ [[package]] name = "vitaminc-protected" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6edc22562a9bbff0ba9704a2c43670b2c7cb1637524c045c3ba6436ed00917f" dependencies = [ "bitvec", "digest 0.11.3", @@ -2944,7 +2954,8 @@ dependencies = [ [[package]] name = "vitaminc-protected-derive" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cce3a7d0ce6f57c0e82345f20e87f3737cccd3a70aec2a2ab49f68a3a5e24a" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2970,7 +2981,8 @@ dependencies = [ [[package]] name = "vitaminc-random" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb253f783ce6f31a55a92f5a9f0526109b8caa6a465edb94244fd344a078c11" dependencies = [ "chacha20", "getrandom 0.4.3", @@ -2995,7 +3007,8 @@ dependencies = [ [[package]] name = "vitaminc-random-derives" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0b3615936d6dded4b775856f4b0fa229d745d183f0b3754ac80035f16c27d9" dependencies = [ "proc-macro2", "quote", @@ -3021,7 +3034,8 @@ dependencies = [ [[package]] name = "vitaminc-traits" version = "0.8.0" -source = "git+https://github.com/cipherstash/vitaminc?rev=c226edfeaf436883018cbabe3f709860b269eca9#c226edfeaf436883018cbabe3f709860b269eca9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9098ee4b13446ef959583ee2f3c384df571b453179104f7b4960ffddf65ee9" dependencies = [ "anyhow", "bytes", diff --git a/languages/golang/encrypt/guest/Cargo.toml b/languages/golang/encrypt/guest/Cargo.toml index a7fd09045..4df0b70ba 100644 --- a/languages/golang/encrypt/guest/Cargo.toml +++ b/languages/golang/encrypt/guest/Cargo.toml @@ -82,33 +82,6 @@ recipher = "=0.3.1" base16ct = { version = "0.2.0", features = ["alloc"] } base64ct = { version = "1.7", features = ["alloc"] } -# TEMPORARY: vitaminc from cipherstash/vitaminc#526 (head c226edf, stacked on -# #520), which adds the storage profile for kept values (Rust -# `vitaminc_aead_frame::storage`, Go `vcffi.MarshalStorage`). Both are -# unreleased. Every vitaminc crate this build resolves at 0.8 is patched to -# the one git revision, so no type is duplicated between a crates.io and a -# git copy. The repository root's Cargo.toml and the other Go guest carry the same -# block, and languages/golang/go.mod pins vcffi and vcvalue to the same -# commit. Replace all of them with the vitaminc release that ships #520/#526. -# (The 0.5 copies cipherstash-client pulls in are a separate semver line and -# are not patched.) -[patch.crates-io] -vitaminc = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-aead = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-aead-derive = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-aead-frame = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-context = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-encrypt = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-hmac = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-normalize = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-ore = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-prf = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-protected = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-protected-derive = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-random = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-random-derives = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } -vitaminc-traits = { git = "https://github.com/cipherstash/vitaminc", rev = "c226edfeaf436883018cbabe3f709860b269eca9" } - [profile.release] # Smaller .wasm; the guest is IO-bound on the FFI copy and the ZeroKMS round # trip, not on codegen. diff --git a/languages/golang/go.mod b/languages/golang/go.mod index 68ef7e5ea..a1ed756bf 100644 --- a/languages/golang/go.mod +++ b/languages/golang/go.mod @@ -3,8 +3,9 @@ module github.com/cipherstash/stack/languages/golang go 1.26.10 // TEMPORARY: vcffi and vcvalue at cipherstash/vitaminc#526 (head c226edf), -// unreleased, for the storage profile (vcffi.MarshalStorage). The Rust -// workspaces patch vitaminc to the same commit. Replace with the release. +// unreleased, for the storage profile (vcffi.MarshalStorage). Only Go writes +// and reads that profile; the guests stay on vitaminc 0.8.0 from crates.io, +// whose transport is the one this commit writes. Replace with the release. require ( github.com/cipherstash/vitaminc/bindings/go/vcffi v0.0.0-20261025114513-c226edfeaf43 github.com/cipherstash/vitaminc/bindings/go/vcvalue v0.0.0-20261025114513-c226edfeaf43 diff --git a/packages/stack-encrypt/tests/ui.rs b/packages/stack-encrypt/tests/ui.rs index f1311b119..e671d2465 100644 --- a/packages/stack-encrypt/tests/ui.rs +++ b/packages/stack-encrypt/tests/ui.rs @@ -13,30 +13,7 @@ fn derive_diagnostics() { let t = trybuild::TestCases::new(); t.compile_fail("tests/ui/*.rs"); - // These cases' diagnostics name where vitaminc's `Encrypt` trait is - // defined, beside the older vitaminc that cts-common pulls in. trybuild - // writes a crates.io path as `$CARGO/-$VERSION`, and a git - // checkout's as the absolute path under the machine's CARGO_HOME, which - // no recording can match on every machine. So they run only while the - // workspace takes vitaminc from crates.io; while the root Cargo.toml's - // temporary `[patch.crates-io]` block points it at a git revision, they - // are skipped, and they run again the day that block is removed. - if vitaminc_from_crates_io() { - t.compile_fail("tests/ui/registry_vitaminc/*.rs"); - } else { - eprintln!( - "skipping tests/ui/registry_vitaminc: the workspace takes vitaminc from a git revision" - ); - } // And the shapes that must compile, so a diagnostic never grows to // cover a valid call. t.pass("tests/ui/pass/*.rs"); } - -/// Whether the workspace lock resolves `vitaminc-aead` from crates.io, as -/// it does with no `[patch]` pointing it at a git revision. -fn vitaminc_from_crates_io() -> bool { - let lock = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../Cargo.lock"); - let lock = std::fs::read_to_string(lock).expect("the workspace Cargo.lock"); - !lock.contains("git+https://github.com/cipherstash/vitaminc") -} diff --git a/packages/stack-encrypt/tests/ui/registry_vitaminc/plaintext_needs_encrypt.rs b/packages/stack-encrypt/tests/ui/plaintext_needs_encrypt.rs similarity index 100% rename from packages/stack-encrypt/tests/ui/registry_vitaminc/plaintext_needs_encrypt.rs rename to packages/stack-encrypt/tests/ui/plaintext_needs_encrypt.rs diff --git a/packages/stack-encrypt/tests/ui/registry_vitaminc/plaintext_needs_encrypt.stderr b/packages/stack-encrypt/tests/ui/plaintext_needs_encrypt.stderr similarity index 93% rename from packages/stack-encrypt/tests/ui/registry_vitaminc/plaintext_needs_encrypt.stderr rename to packages/stack-encrypt/tests/ui/plaintext_needs_encrypt.stderr index c94b3ee75..d357fbec3 100644 --- a/packages/stack-encrypt/tests/ui/registry_vitaminc/plaintext_needs_encrypt.stderr +++ b/packages/stack-encrypt/tests/ui/plaintext_needs_encrypt.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `SerdeOnly: stack_encrypt::Encrypt` is not satisfied - --> tests/ui/registry_vitaminc/plaintext_needs_encrypt.rs:9:33 + --> tests/ui/plaintext_needs_encrypt.rs:9:33 | 9 | let _=keyset.encrypt_as::<_,Target>(&SerdeOnly {value:"x".into()},nonempty!("column").into()); | ---------- ^^^^^^ unsatisfied trait bound @@ -7,7 +7,7 @@ error[E0277]: the trait bound `SerdeOnly: stack_encrypt::Encrypt` is not satisfi | required by a bound introduced by this call | help: the trait `stack_encrypt::Encrypt` is not implemented for `SerdeOnly` - --> tests/ui/registry_vitaminc/plaintext_needs_encrypt.rs:4:1 + --> tests/ui/plaintext_needs_encrypt.rs:4:1 | 4 | struct SerdeOnly { value: String } | ^^^^^^^^^^^^^^^^ From 65cc466e3472b167f9644aa4ccf43d4e60c44e0c Mon Sep 17 00:00:00 2001 From: Dan Draper Date: Mon, 26 Oct 2026 15:19:05 +1100 Subject: [PATCH 5/8] test(go): pin what a typed nil pointer encodes to on the two raw paths The vitaminc#526 pin changes how vcffi writes a nil pointer to a fixed-width type: (*int64)(nil), (*float64)(nil) and (*time.Time)(nil) marshal as a typed null, padded to the type's width, where they marshalled as the one-byte null. Two public paths send a caller's value as it is, Cipher.Seal with a hand-built record.Source and Cipher.QueryTerm's input, and a typed nil is not == nil in sourceRow, so its bytes reach the engine and change with the pin. The CHANGELOG says the engine reads a typed null as a null; nothing proved it. Three tests now pin it over the deterministic guest. Seal: on a field that seals its nulls, a typed nil is a sealed null as long as a value with the term every nil shares; on a null=sql field it is left out of the record; on a field every row has it is refused as field_type. Each is what the untyped nil gets, and both open as the same row. QueryTerm: eq with a typed nil on a field that seals its nulls derives the stored nils' term, the same as for an untyped nil; anywhere else a typed nil is null_query, as an untyped nil is. A fourth test pins the vcffi encoding itself, so the engine tests are read against the bytes that reach it, and a gensupport test pins that Deref hands generated code an untyped nil, which is why generated code never sends a typed one. Claude-Session: https://claude.ai/code/session_01N7bCqa3ZVJMcEE99RzVqEu --- .../encrypt/gensupport/gensupport_test.go | 29 +++ languages/golang/encrypt/typed_nil_test.go | 205 ++++++++++++++++++ 2 files changed, 234 insertions(+) create mode 100644 languages/golang/encrypt/typed_nil_test.go diff --git a/languages/golang/encrypt/gensupport/gensupport_test.go b/languages/golang/encrypt/gensupport/gensupport_test.go index 68bd64ea0..7d178828e 100644 --- a/languages/golang/encrypt/gensupport/gensupport_test.go +++ b/languages/golang/encrypt/gensupport/gensupport_test.go @@ -5,6 +5,7 @@ import ( "log/slog" "strings" "testing" + "time" ) func TestRedactedHidesSealedFieldsAndSortsShown(t *testing.T) { @@ -73,3 +74,31 @@ func TestVersionConstantIsTyped(t *testing.T) { t.Fatalf("GeneratedVersion1 = %d", GeneratedVersion1) } } + +// Deref is how generated code passes a field declared over a pointer, and +// it is why a nil pointer never reaches the codec typed: the untyped nil it +// gives is == nil, so Seal leaves a null=sql field out and the engine sees a +// plain null elsewhere, while the typed nil itself is not == nil and would be +// sent as a typed null of the pointer's type (see encrypt's typed_nil_test). +func TestDerefGivesAnUntypedNilForANilPointer(t *testing.T) { + // The contrast Deref exists for: the same pointer, as an interface + // value, is not == nil. (Through a call, so the checker does not fold + // the comparison away.) + asAny := func(v any) any { return v } + if asAny((*int64)(nil)) == nil { + t.Fatal("a typed nil pointer is == nil as an interface; this test pins the opposite") + } + if v := Deref((*int64)(nil)); v != nil { + t.Fatalf("Deref((*int64)(nil)) = %#v, want an untyped nil", v) + } + if v := Deref((*time.Time)(nil)); v != nil { + t.Fatalf("Deref((*time.Time)(nil)) = %#v, want an untyped nil", v) + } + n, when := int64(7), time.Unix(1_700_000_000, 0).UTC() + if v := Deref(&n); v != int64(7) { + t.Fatalf("Deref(&7) = %#v, want the int64 7", v) + } + if v := Deref(&when); v != when { + t.Fatalf("Deref(&when) = %#v, want the time", v) + } +} diff --git a/languages/golang/encrypt/typed_nil_test.go b/languages/golang/encrypt/typed_nil_test.go new file mode 100644 index 000000000..0e29b6b25 --- /dev/null +++ b/languages/golang/encrypt/typed_nil_test.go @@ -0,0 +1,205 @@ +package encrypt_test + +import ( + "bytes" + "context" + "errors" + "reflect" + "testing" + "time" + + "github.com/cipherstash/stack/languages/golang/encrypt" + "github.com/cipherstash/stack/languages/golang/internal/record" + "github.com/cipherstash/vitaminc/bindings/go/vcffi" +) + +// A typed nil pointer on the two raw paths. Generated code passes a nil +// pointer through gensupport.Deref, which gives an untyped nil; the two +// paths that take a caller's value as it is are Cipher.Seal with a +// hand-built record.Source and Cipher.QueryTerm's input. There a +// (*int64)(nil) is not == nil, so it is sent, and vcffi (from vitaminc +// e9c3837b on) writes it as a typed null, padded to the width of an int64, +// where it wrote a one-byte null. These tests pin what the engine does with +// it: a typed null is a null to the engine, so each path answers a typed nil +// as it answers an untyped one, on a field of every null mode. + +// typedNilPlan has one int64 field in each null mode and a timestamp field +// that seals its nulls, so both encodings meet every answer the engine has. +var typedNilPlan = &record.Plan{Context: []string{"typed_nil"}, Fields: []record.Field{ + {Name: "every", Kind: record.Int64, Outputs: []record.Output{record.Ciphertext, record.Equality}}, + {Name: "sealed", Kind: record.Int64, Outputs: []record.Output{record.Ciphertext, record.Equality}, Null: record.SealedNull}, + {Name: "sql", Kind: record.Int64, Outputs: []record.Output{record.Ciphertext, record.Ore}, Null: record.SQLNull}, + {Name: "when", Kind: record.Timestamp, Outputs: []record.Output{record.Ciphertext, record.Equality}, Null: record.SealedNull}, +}} + +// The pinned vcffi behaviour itself, so the engine tests below are read +// against the bytes that reach it: a nil pointer to a fixed-width type is a +// typed null (tag 0x15, then the type, padded to the type's width), not the +// one-byte null an untyped nil is, and it decodes as nil like every null. +func TestANilFixedWidthPointerMarshalsAsATypedNull(t *testing.T) { + untyped, err := vcffi.Marshal(nil) + if err != nil || !bytes.Equal(untyped, []byte{0x00}) { + t.Fatalf("an untyped nil marshals as %x, %v; want the one-byte null", untyped, err) + } + for name, tc := range map[string]struct { + nilPointer any + present any + }{ + "*int64": {(*int64)(nil), int64(7)}, + "*float64": {(*float64)(nil), 1.5}, + "*time.Time": {(*time.Time)(nil), time.Unix(1_700_000_000, 0).UTC()}, + } { + got, err := vcffi.Marshal(tc.nilPointer) + if err != nil { + t.Fatalf("%s: %v", name, err) + } + if bytes.Equal(got, untyped) || got[0] != 0x15 { + t.Fatalf("%s: a nil pointer marshals as %x; want a typed null (tag 0x15)", name, got) + } + value, err := vcffi.Marshal(tc.present) + if err != nil { + t.Fatal(err) + } + if len(got) != len(value) { + t.Fatalf("%s: the typed null is %d bytes, a value %d; want them as long", name, len(got), len(value)) + } + back, err := vcffi.Unmarshal(got) + if err != nil || back != nil { + t.Fatalf("%s: the typed null decodes as %#v, %v; want nil", name, back, err) + } + } +} + +// Seal with a hand-built Source: a typed nil seals as the untyped nil does +// on a field of every null mode. Sealed, it is a sealed null, as long as a +// value, with the term every nil shares; sql, it is left out of the record; +// not null, it is refused as a value of the wrong type, the same refusal. +// What it sealed opens as nil, as the untyped nil's does. +func TestSealReadsATypedNilAsANull(t *testing.T) { + c := deterministicClient(t) + ctx := context.Background() + cipher := c.DefaultKeyset() + when := time.Unix(1_700_000_000, 0).UTC() + rows := []record.Source{ + {"every": int64(1), "sealed": int64(2), "sql": int64(3), "when": when}, + {"every": int64(1), "sealed": nil, "sql": nil, "when": nil}, + {"every": int64(1), "sealed": (*int64)(nil), "sql": (*int64)(nil), "when": (*time.Time)(nil)}, + } + sealed, err := cipher.Seal(ctx, typedNilPlan, rows) + if err != nil { + t.Fatal(err) + } + present, untyped, typed := sealed[0], sealed[1], sealed[2] + + // A sealed null: both nils are sealed as long as a value, and they + // share the equality term, which no value has. + for _, field := range []string{"sealed", "when"} { + if len(typed[field].Ciphertext) == 0 || len(typed[field].Ciphertext) != len(untyped[field].Ciphertext) || len(typed[field].Ciphertext) != len(present[field].Ciphertext) { + t.Fatalf("%s: a typed nil seals to %d bytes, an untyped nil to %d, a value to %d; want all three as long", + field, len(typed[field].Ciphertext), len(untyped[field].Ciphertext), len(present[field].Ciphertext)) + } + typedTerm, untypedTerm, presentTerm := typed[field].Terms[record.Equality], untyped[field].Terms[record.Equality], present[field].Terms[record.Equality] + if len(typedTerm) == 0 || !bytes.Equal(typedTerm, untypedTerm) || bytes.Equal(typedTerm, presentTerm) { + t.Fatalf("%s: a typed nil's equality term is %x, an untyped nil's %x, a value's %x; want the nils to share one and the value another", field, typedTerm, untypedTerm, presentTerm) + } + } + // A SQL null: a typed nil stores nothing, as an untyped nil does; the + // host sends the typed one (it is not == nil) and the engine leaves it + // out. + for name, rec := range map[string]record.Sealed{"a typed nil": typed, "an untyped nil": untyped} { + if outputs, ok := rec["sql"]; ok { + t.Fatalf("%s on a null=sql field stored %+v; want no entry", name, outputs) + } + } + if _, ok := present["sql"]; !ok { + t.Fatal("a value on a null=sql field is stored") + } + + // Both open as nil, and the two opened rows are the same row. + opened, err := cipher.Open(ctx, typedNilPlan, sealed) + if err != nil { + t.Fatal(err) + } + for _, field := range []string{"sealed", "sql", "when"} { + v, ok := opened[2][field] + if !ok || v != nil { + t.Fatalf("%s sealed from a typed nil opened as %#v (present %v); want an untyped nil", field, v, ok) + } + } + if !reflect.DeepEqual(opened[1], opened[2]) { + t.Fatalf("a typed nil opened as %#v, an untyped nil as %#v; want the same row", opened[2], opened[1]) + } + if got := opened[0]; got["sealed"] != int64(2) || got["sql"] != int64(3) || got["when"] != when { + t.Fatalf("the values opened as %#v", got) + } + + // A field every row has refuses a typed nil as it refuses an untyped + // one: a value that is not of the field's type, named. + for name, v := range map[string]any{"a typed nil": (*int64)(nil), "an untyped nil": nil} { + _, err := cipher.Seal(ctx, typedNilPlan, []record.Source{{"every": v, "sealed": int64(2), "sql": int64(3), "when": when}}) + var d *encrypt.Diagnostic + if !errors.Is(err, encrypt.ErrEncoding) || !errors.As(err, &d) || d.Field() != "every" || d.Reason() != "field_type" { + t.Fatalf("%s on a field every row has: %v; want ErrEncoding naming \"every\" with reason field_type", name, err) + } + } +} + +// QueryTerm with a typed nil input: the engine reads it as the null it is. +// On a field that seals its nulls, eq derives the term the stored nils +// share, the same term for a typed and an untyped nil; on any other field, +// or operator, a null is no query, and both nils are refused alike. +func TestQueryTermReadsATypedNilAsANull(t *testing.T) { + c := deterministicClient(t) + ctx := context.Background() + cipher := c.DefaultKeyset() + when := time.Unix(1_700_000_000, 0).UTC() + sealed, err := cipher.Seal(ctx, typedNilPlan, []record.Source{{"every": int64(1), "sealed": nil, "sql": nil, "when": nil}}) + if err != nil { + t.Fatal(err) + } + for _, tc := range []struct { + field string + typedNil any + aValue any + valueTerm []byte + }{ + {"sealed", (*int64)(nil), int64(2), nil}, + {"when", (*time.Time)(nil), when, nil}, + } { + typed, err := cipher.QueryTerm(ctx, typedNilPlan, tc.field, "eq", tc.typedNil) + if err != nil { + t.Fatalf("%s eq a typed nil: %v", tc.field, err) + } + untyped, err := cipher.QueryTerm(ctx, typedNilPlan, tc.field, "eq", nil) + if err != nil { + t.Fatalf("%s eq an untyped nil: %v", tc.field, err) + } + if typed.Operator != "eq" || typed.Index != record.Equality || len(typed.Terms) != 1 || !reflect.DeepEqual(typed, untyped) { + t.Fatalf("%s eq a typed nil = %+v, an untyped nil = %+v; want the same eq term", tc.field, typed, untyped) + } + if stored := sealed[0][tc.field].Terms[record.Equality]; !bytes.Equal(typed.Terms[0], stored) { + t.Fatalf("%s eq a typed nil = %x, the stored nil's term is %x; want the query to find the stored nils", tc.field, typed.Terms[0], stored) + } + value, err := cipher.QueryTerm(ctx, typedNilPlan, tc.field, "eq", tc.aValue) + if err != nil || bytes.Equal(value.Terms[0], typed.Terms[0]) { + t.Fatalf("%s eq a value: %v, %x; want a term other than the nils'", tc.field, err, value.Terms) + } + } + // Everywhere else a null is no query: on a field every row has and on a + // field whose nulls are SQL NULL. (A field that seals its nulls answers + // eq alone, so there is no other operator to put a null under.) + for name, tc := range map[string]struct { + field, operator string + input any + }{ + "eq on a field every row has": {"every", "eq", (*int64)(nil)}, + "lt on a null=sql field": {"sql", "lt", (*int64)(nil)}, + "the untyped nil, for comparison": {"every", "eq", nil}, + } { + _, err := cipher.QueryTerm(ctx, typedNilPlan, tc.field, tc.operator, tc.input) + var d *encrypt.Diagnostic + if !errors.Is(err, encrypt.ErrEncoding) || !errors.As(err, &d) || d.Field() != tc.field || d.Reason() != "null_query" { + t.Fatalf("%s: %v; want ErrEncoding naming %q with reason null_query", name, err, tc.field) + } + } +} From 21261e48d493f7f8636ddff0c7d8f4e02d6040a3 Mon Sep 17 00:00:00 2001 From: Dan Draper Date: Mon, 26 Oct 2026 16:13:47 +1100 Subject: [PATCH 6/8] chore(deps): pin vitaminc's Go bindings to d7de535c4, the final #526 merge The previous pin, c226edf, was a pre-rebase head of cipherstash/vitaminc#526: it is on no vitaminc branch and was fetchable only through the Go proxy's cache. d7de535c4 is the merge of #526 into vitaminc's feat/aead-frame-storage-profile branch, the final form of the change. It is not on vitaminc main and in no release, and vitaminc's Go bindings have no tags, so the pin stays a pseudo-version and stays TEMPORARY; it moves to main, or to a tag, when vitaminc provides one. Between the two commits the vcffi change is in the storage profile (MarshalStorage), which nothing in this module calls yet: a pointer to a value that itself starts with null or StorageSome (a nil pointer or interface, Some, or a pointer to one) is now marked at every level, so it reads back as deep as it was written; an unknown `vc` tag option is refused instead of ignored; and ErrMalformed's text names storage blobs too. The transport (Marshal, Unmarshal, MarshalCipherText) is unchanged, so the guests on crates.io vitaminc 0.8.0 still agree with it: go:test passes against them, typed_nil_test.go included, with no assertion moved. The CHANGELOG entry and the test's comment now name the commit rather than the pull request, and say that the typed-null encoding comes from vitaminc e9c3837b, which is on main and in no Go tag yet, not from the storage profile. go.sum was resolved from the repository directly and then verified through proxy.golang.org, so a build with the default GOPROXY finds it. Claude-Session: https://claude.ai/code/session_01N7bCqa3ZVJMcEE99RzVqEu --- languages/golang/CHANGELOG.md | 23 +++++++++++++--------- languages/golang/encrypt/typed_nil_test.go | 11 ++++++----- languages/golang/go.mod | 16 +++++++++------ languages/golang/go.sum | 8 ++++---- 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/languages/golang/CHANGELOG.md b/languages/golang/CHANGELOG.md index a92a8160e..e0e58056f 100644 --- a/languages/golang/CHANGELOG.md +++ b/languages/golang/CHANGELOG.md @@ -187,15 +187,20 @@ listed under **Breaking**. Releases are tagged `languages/golang/vX.Y.Z`. ### Changed -- **vitaminc's Go bindings from cipherstash/vitaminc#526 (temporary).** - `vcffi` and `vcvalue` move to an unreleased commit, for the storage - profile (`vcffi.MarshalStorage`). One encoding changes with them: a nil - pointer to a fixed-width value (`(*int64)(nil)`) is written as a typed - null where it was written as a null. The engine reads a typed null as a - null, and generated code passes an untyped nil for a nil pointer, so - this only shows in the bytes a hand-built `record.Source` or query input - sends. The pin returns to a vitaminc release before this module is - tagged. +- **vitaminc's Go bindings at an unreleased vitaminc commit (temporary).** + `vcffi` and `vcvalue` move to vitaminc `d7de535c4`, the merge of + cipherstash/vitaminc#526 into its `feat/aead-frame-storage-profile` + branch, for the storage profile (`vcffi.MarshalStorage`); vitaminc's Go + bindings have no tags, so the commit is the pin. One encoding changes + with them, and it comes from vitaminc `e9c3837b` (on `main`, in no Go + tag yet), not from the storage profile: a nil pointer to a fixed-width + value (`(*int64)(nil)`) is written as a typed null where it was written + as a null. The engine reads a typed null as a null, and generated code + passes an untyped nil for a nil pointer, so this only shows in the bytes + a hand-built `record.Source` or query input sends. The pin returns to a + vitaminc release before this module is tagged: + `scripts/go-release-version.mjs` refuses a final version while `go.mod` + names a vitaminc pseudo-version. - **An EQL v4 block ORE term is one hex string.** The `ob` of a value encrypted into an `_ord_ore` or `text_search_ore` EQL type is `"ob": "0202…"`, where it was a one-element array, and the EQL v4 bundle diff --git a/languages/golang/encrypt/typed_nil_test.go b/languages/golang/encrypt/typed_nil_test.go index 0e29b6b25..a1020c126 100644 --- a/languages/golang/encrypt/typed_nil_test.go +++ b/languages/golang/encrypt/typed_nil_test.go @@ -17,11 +17,12 @@ import ( // pointer through gensupport.Deref, which gives an untyped nil; the two // paths that take a caller's value as it is are Cipher.Seal with a // hand-built record.Source and Cipher.QueryTerm's input. There a -// (*int64)(nil) is not == nil, so it is sent, and vcffi (from vitaminc -// e9c3837b on) writes it as a typed null, padded to the width of an int64, -// where it wrote a one-byte null. These tests pin what the engine does with -// it: a typed null is a null to the engine, so each path answers a typed nil -// as it answers an untyped one, on a field of every null mode. +// (*int64)(nil) is not == nil, so it is sent, and vcffi, from vitaminc +// e9c3837b on (on main, in no Go tag yet), writes it as a typed null, +// padded to the width of an int64, where it wrote a one-byte null. These +// tests pin what the engine does with it: a typed null is a null to the +// engine, so each path answers a typed nil as it answers an untyped one, on +// a field of every null mode. // typedNilPlan has one int64 field in each null mode and a timestamp field // that seals its nulls, so both encodings meet every answer the engine has. diff --git a/languages/golang/go.mod b/languages/golang/go.mod index a1ed756bf..ec6f78614 100644 --- a/languages/golang/go.mod +++ b/languages/golang/go.mod @@ -2,13 +2,17 @@ module github.com/cipherstash/stack/languages/golang go 1.26.10 -// TEMPORARY: vcffi and vcvalue at cipherstash/vitaminc#526 (head c226edf), -// unreleased, for the storage profile (vcffi.MarshalStorage). Only Go writes -// and reads that profile; the guests stay on vitaminc 0.8.0 from crates.io, -// whose transport is the one this commit writes. Replace with the release. +// TEMPORARY: vcffi and vcvalue at vitaminc d7de535c4, the merge of +// cipherstash/vitaminc#526 (the storage profile, vcffi.MarshalStorage) into +// its branch feat/aead-frame-storage-profile. That commit is not on vitaminc +// main and in no release, and vitaminc's Go bindings have no tags. Only Go +// writes and reads that profile; the guests stay on vitaminc 0.8.0 from +// crates.io, whose transport is the one this commit writes. The pin moves to +// main, or to a tag, when vitaminc provides one; until then +// scripts/go-release-version.mjs refuses a final version of this module. require ( - github.com/cipherstash/vitaminc/bindings/go/vcffi v0.0.0-20261025114513-c226edfeaf43 - github.com/cipherstash/vitaminc/bindings/go/vcvalue v0.0.0-20261025114513-c226edfeaf43 + github.com/cipherstash/vitaminc/bindings/go/vcffi v0.0.0-20261026030854-d7de535c4288 + github.com/cipherstash/vitaminc/bindings/go/vcvalue v0.0.0-20261026030854-d7de535c4288 github.com/tetratelabs/wazero v1.12.0 golang.org/x/oauth2 v0.37.0 ) diff --git a/languages/golang/go.sum b/languages/golang/go.sum index 91db0b468..bb03f7b1a 100644 --- a/languages/golang/go.sum +++ b/languages/golang/go.sum @@ -1,7 +1,7 @@ -github.com/cipherstash/vitaminc/bindings/go/vcffi v0.0.0-20261025114513-c226edfeaf43 h1:lsIsc5mnadNjZNI6DNS3F3Ey1uX9wVR3ctSW/D1r42w= -github.com/cipherstash/vitaminc/bindings/go/vcffi v0.0.0-20261025114513-c226edfeaf43/go.mod h1:7hPdj2tN3YpcPZYdZ+Fe7+Fsc7dnSyH5bsL9y+LbRuM= -github.com/cipherstash/vitaminc/bindings/go/vcvalue v0.0.0-20261025114513-c226edfeaf43 h1:JW0R2y5NzhYhlGg1FO77GYeXGDHDRvRJYOtTMEtANlQ= -github.com/cipherstash/vitaminc/bindings/go/vcvalue v0.0.0-20261025114513-c226edfeaf43/go.mod h1:RJODA1DCSm4H+1pbmCzDdT2pAIkxhinwIu6ewjh5sPs= +github.com/cipherstash/vitaminc/bindings/go/vcffi v0.0.0-20261026030854-d7de535c4288 h1:jX/ouzDlPnEBHTx6E4IdGqdHsl9ImhxRhqK1mQ6CKT8= +github.com/cipherstash/vitaminc/bindings/go/vcffi v0.0.0-20261026030854-d7de535c4288/go.mod h1:7hPdj2tN3YpcPZYdZ+Fe7+Fsc7dnSyH5bsL9y+LbRuM= +github.com/cipherstash/vitaminc/bindings/go/vcvalue v0.0.0-20261026030854-d7de535c4288 h1:8BMb3tOlrztg21QmDMdb5t6gmRc31H/XqlpofOJmB30= +github.com/cipherstash/vitaminc/bindings/go/vcvalue v0.0.0-20261026030854-d7de535c4288/go.mod h1:RJODA1DCSm4H+1pbmCzDdT2pAIkxhinwIu6ewjh5sPs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From 4c5ab21f05dfea7775d1b0aa8214b40e7ca0447d Mon Sep 17 00:00:00 2001 From: Dan Draper Date: Mon, 26 Oct 2026 16:13:48 +1100 Subject: [PATCH 7/8] ci(go-release): refuse a final version while go.mod pins vitaminc at a pseudo-version languages/golang/CHANGELOG.md promises that the TEMPORARY vitaminc pin is gone before the module is tagged, and nothing held that promise: release-golang.yml tags whatever go.mod names, and the earlier vitaminc pins were pseudo-versions too, so a final tag could have shipped a dependency that no vitaminc branch guarantees to keep. scripts/go-release-version.mjs now reads go.mod, strips its comments (the TEMPORARY note quotes the pin), and refuses a final version while any github.com/cipherstash/vitaminc/... module is required or replaced at a pseudo-version, in any of the three pseudo-version forms. A pre-release may carry one, which is how a partner alpha is built ahead of a vitaminc release. The refusal names every pin, so the dispatcher sees what to move without opening go.mod, and it is reported alongside the other reasons rather than instead of them. A pin at a vitaminc tag, once there is one, passes. The test runs the pure function over fixtures for both outcomes, the replace form, a pin quoted only in a comment, and a pseudo-version on another module, and runs the script over the tree's own go.mod: while it carries the pin a final version from main exits 1 naming it, and once the pin is a tag the same dispatch exits 0. Claude-Session: https://claude.ai/code/session_01N7bCqa3ZVJMcEE99RzVqEu --- .github/workflows/release-golang.yml | 3 +- AGENTS.md | 2 +- scripts/__tests__/go-release-version.test.mjs | 94 ++++++++++++++++++- scripts/go-release-version.mjs | 46 ++++++++- 4 files changed, 136 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release-golang.yml b/.github/workflows/release-golang.yml index 0db79e34a..b8b919d59 100644 --- a/.github/workflows/release-golang.yml +++ b/.github/workflows/release-golang.yml @@ -20,7 +20,8 @@ name: Release (Go) # The ref must carry the EQL v4 bundle (packages/eql's v4 build), or the # build job stops at "Build the EQL SQL bundles". A version with a pre-release # suffix may be cut from any branch and is published as a GitHub pre-release; -# a final version is cut from main only (scripts/go-release-version.mjs). +# a final version is cut from main only, and not while go.mod pins vitaminc's +# Go bindings at a pseudo-version (scripts/go-release-version.mjs). # # Three jobs, split by what they may touch: # diff --git a/AGENTS.md b/AGENTS.md index a482387cc..7a795d2e8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -92,7 +92,7 @@ Every npm package except EQL lives under `languages/typescript/`: packages in `l links are provenance only. - `packages/stack-auth`, `packages/stack-profile`, `packages/stack-kms`, `packages/stack-encrypt`, `packages/stack-encrypt-derive`, `packages/stack-guest-abi`: The Rust crates imported from `cipherstash/cipherstash-suite` with their history — `stack-auth` and `stack-profile` (published to crates.io, one version group), `stack-kms` (published to crates.io from 0.1.0, its own version group, re-exported by `stack-encrypt` as `stack_encrypt::kms`), `stack-encrypt` and `stack-encrypt-derive` (published to crates.io from 0.1.0, one version group; `eql-bindings`' `stack-encrypt` feature depends on them from the registry), and `stack-guest-abi` (`publish = false`). They are the members of the **root Cargo workspace**, with the three node binding crates below. See "Working on the Rust crates". - `languages/typescript/packages/auth`, `languages/typescript/packages/profile`, `languages/typescript/packages/stack-auth-wasm`: The node bindings of those crates. `@cipherstash/auth` (napi-rs v2) and its six `platforms/*` packages are published to npm from this repository by `release.yml` (`auth-artifacts`, `publish-auth`); a change to what it ships, the `stack-auth` crate included, needs an `@cipherstash/auth` changeset (`require-auth-npm-changeset.yml`). `@cipherstash/profile` and its platforms are private and never published; `@cipherstash/stack-auth-wasm` is private and builds the wasm that `@cipherstash/auth` ships. Their `build` and `test` scripts never invoke cargo; `build:native`, `build:debug` and `test:cargo` do. -- `languages/golang`: The Go module — the SDK `encrypt` with its generated-code support `encrypt/gensupport` and the policy packages `encrypt/policy` and `encrypt/policy/protosource`; the credential package `auth`; the generator `stashgen` and its command `cmd/stashgen`; and `internal` (the shared guest plumbing and the `record` wire model). A wazero host with no cgo. Its two WASI guests (`encrypt/guest`, `auth/guest`) are detached Cargo workspaces built by `mise run wasm:guest:build` and `mise run wasm:auth-guest:build`; `mise run wasm:guest:build:deterministic` builds the seeded TEST build the hermetic Go tests use; the `.wasm` files they embed are gitignored. The stack-encrypt guest has a second build with the EQL types (`mise run wasm:guest:build:eql`, cargo feature `eql`, linking `eql-bindings` by path; `wasm:guest:build:eql:deterministic` is its test build), embedded by `encrypt/eql` and registered on import, so a program whose generated code names an EQL type runs it (ADR-0007, amended). `encrypt/eql`'s types and `Types` table are generated from the EQL catalog by `eql-codegen` (`eql_gen.go`, written by `mise run types:generate` in `packages/eql` and drift-gated by `types:check` and `cargo test -p eql-codegen`). Generated `*_stash.go` files are committed and CI fails when `go generate ./...` changes one. Releases are cut by hand with `.github/workflows/release-golang.yml` (`gh workflow run release-golang.yml -f version=X.Y.Z[-pre] --ref `): because the guests are gitignored on main and Go fetches a version as the files committed at its tag, the workflow builds them, commits them on a release-only commit no branch points at, tags it `languages/golang/vX.Y.Z` and pushes the tag alone, then attaches the EQL v4 SQL bundle to the GitHub release. The ref must carry the EQL v4 bundle, or the build stops at "Build the EQL SQL bundles". `scripts/go-release-version.mjs` refuses, before anything is built, every version Go would refuse (non-canonical semver, `+build` metadata, a v2+ major on a module path with no `/vN` suffix, a version shaped like a pseudo-version), and a final version dispatched from anywhere but `main`; a pre-release may come from a branch. The build job also refuses a version the Go proxy already lists in `@v/list`, which catches a tag deleted after the proxy fetched it; it never asks the proxy for the version itself before the tag exists, because the proxy would cache "not found" for up to 30 minutes and `verify` would fail after the push. The build checks the `Cargo.lock` of the EQL workspace and both guest workspaces with `--locked` first, since none of those builds passes it. It runs in a job with no write token and hands the guests to a `publish` job that holds `contents: write` and runs only git and gh. `publish` fetches the commit with plain unauthenticated git rather than `actions/checkout`, whose `token` input defaults to the job's writable token, and refuses a release commit whose tree is not the one the build tested. `publish` takes the paths it commits from the workflow's fixed `GUESTS` list, never from the artifact, and refuses a `guests.sha256` that is not exactly one SHA-256 line for each of those three guests: the build job runs third-party code after writing that manifest, so a path read from it could put any file on the tag (`scripts/__tests__/go-release-artifact.test.mjs`). A version is immutable once the Go proxy has fetched it, so a tag is never moved or deleted: if `publish` or `verify` fails after the tag is pushed, use "Re-run failed jobs" on the same run, which keeps a tag that run pushed and creates or completes the release; a new dispatch of the same version is refused. Never tag a main commit by hand: `go get` would fetch a module with no guests. +- `languages/golang`: The Go module — the SDK `encrypt` with its generated-code support `encrypt/gensupport` and the policy packages `encrypt/policy` and `encrypt/policy/protosource`; the credential package `auth`; the generator `stashgen` and its command `cmd/stashgen`; and `internal` (the shared guest plumbing and the `record` wire model). A wazero host with no cgo. Its two WASI guests (`encrypt/guest`, `auth/guest`) are detached Cargo workspaces built by `mise run wasm:guest:build` and `mise run wasm:auth-guest:build`; `mise run wasm:guest:build:deterministic` builds the seeded TEST build the hermetic Go tests use; the `.wasm` files they embed are gitignored. The stack-encrypt guest has a second build with the EQL types (`mise run wasm:guest:build:eql`, cargo feature `eql`, linking `eql-bindings` by path; `wasm:guest:build:eql:deterministic` is its test build), embedded by `encrypt/eql` and registered on import, so a program whose generated code names an EQL type runs it (ADR-0007, amended). `encrypt/eql`'s types and `Types` table are generated from the EQL catalog by `eql-codegen` (`eql_gen.go`, written by `mise run types:generate` in `packages/eql` and drift-gated by `types:check` and `cargo test -p eql-codegen`). Generated `*_stash.go` files are committed and CI fails when `go generate ./...` changes one. Releases are cut by hand with `.github/workflows/release-golang.yml` (`gh workflow run release-golang.yml -f version=X.Y.Z[-pre] --ref `): because the guests are gitignored on main and Go fetches a version as the files committed at its tag, the workflow builds them, commits them on a release-only commit no branch points at, tags it `languages/golang/vX.Y.Z` and pushes the tag alone, then attaches the EQL v4 SQL bundle to the GitHub release. The ref must carry the EQL v4 bundle, or the build stops at "Build the EQL SQL bundles". `scripts/go-release-version.mjs` refuses, before anything is built, every version Go would refuse (non-canonical semver, `+build` metadata, a v2+ major on a module path with no `/vN` suffix, a version shaped like a pseudo-version), a final version dispatched from anywhere but `main`, and a final version while `languages/golang/go.mod` pins a `github.com/cipherstash/vitaminc/…` module at a pseudo-version (vitaminc's Go bindings have no tags, and a TEMPORARY pin on an unreleased commit must be gone before the module is tagged); a pre-release may come from a branch and may carry such a pin. The build job also refuses a version the Go proxy already lists in `@v/list`, which catches a tag deleted after the proxy fetched it; it never asks the proxy for the version itself before the tag exists, because the proxy would cache "not found" for up to 30 minutes and `verify` would fail after the push. The build checks the `Cargo.lock` of the EQL workspace and both guest workspaces with `--locked` first, since none of those builds passes it. It runs in a job with no write token and hands the guests to a `publish` job that holds `contents: write` and runs only git and gh. `publish` fetches the commit with plain unauthenticated git rather than `actions/checkout`, whose `token` input defaults to the job's writable token, and refuses a release commit whose tree is not the one the build tested. `publish` takes the paths it commits from the workflow's fixed `GUESTS` list, never from the artifact, and refuses a `guests.sha256` that is not exactly one SHA-256 line for each of those three guests: the build job runs third-party code after writing that manifest, so a path read from it could put any file on the tag (`scripts/__tests__/go-release-artifact.test.mjs`). A version is immutable once the Go proxy has fetched it, so a tag is never moved or deleted: if `publish` or `verify` fails after the tag is pushed, use "Re-run failed jobs" on the same run, which keeps a tag that run pushed and creates or completes the release; a new dispatch of the same version is refused. Never tag a main commit by hand: `go get` would fetch a module with no guests. - `e2e/*`: Cross-package end-to-end tests (package managers, supply chain, Prisma example README) - `languages/typescript/examples/*`: Working apps (basic, prisma, supabase-worker) - `docs/plans/*`: Internal design plans. User-facing documentation lives at https://cipherstash.com/docs (not in this repo). diff --git a/scripts/__tests__/go-release-version.test.mjs b/scripts/__tests__/go-release-version.test.mjs index 1eca3c37c..ed2639573 100644 --- a/scripts/__tests__/go-release-version.test.mjs +++ b/scripts/__tests__/go-release-version.test.mjs @@ -6,6 +6,7 @@ import { allowedMajors, goReleaseVersionErrors, modulePathOf, + vitamincPseudoVersions, } from '../go-release-version.mjs' import { REPO_ROOT } from './lib/repo-root.mjs' import { readWorkflow } from './lib/workflows.mjs' @@ -19,8 +20,26 @@ const MODULE = 'github.com/cipherstash/stack/languages/golang' const MAIN = 'refs/heads/main' const BRANCH = 'refs/heads/ci/golang-release' -const errorsFor = (version, ref = MAIN, modulePath = MODULE) => - goReleaseVersionErrors(version, { modulePath, ref }) +const errorsFor = (version, ref = MAIN, modulePath = MODULE, goMod = '') => + goReleaseVersionErrors(version, { modulePath, ref, goMod }) + +const VCFFI = 'github.com/cipherstash/vitaminc/bindings/go/vcffi' +const VCVALUE = 'github.com/cipherstash/vitaminc/bindings/go/vcvalue' +const PSEUDO = 'v0.0.0-20261026030854-d7de535c4288' + +// A go.mod whose vitaminc modules are at `version`, with the TEMPORARY +// comment the real one carries when that is a pseudo-version. +const goModPinning = (version) => `module ${MODULE} + +go 1.26.10 + +// TEMPORARY: vcffi and vcvalue at vitaminc d7de535c4 (v0.0.0-20261026030854-d7de535c4288). +require ( + ${VCFFI} ${version} + ${VCVALUE} ${version} + github.com/tetratelabs/wazero v1.12.0 +) +` describe('goReleaseVersionErrors', () => { it.each([ @@ -80,6 +99,62 @@ describe('goReleaseVersionErrors', () => { }) }) +describe('a TEMPORARY vitaminc pin', () => { + const temporary = goModPinning(PSEUDO) + const released = goModPinning('v0.9.0') + + it('is every vitaminc module go.mod names at a pseudo-version', () => { + expect(vitamincPseudoVersions(temporary)).toEqual([ + `${VCFFI} ${PSEUDO}`, + `${VCVALUE} ${PSEUDO}`, + ]) + // The three pseudo-version forms, and a replace directive, count too. + expect( + vitamincPseudoVersions( + `require ${VCFFI} v0.8.1-0.20261026030854-d7de535c4288\n` + + `replace ${VCVALUE} v0.9.0 => ${VCVALUE} v0.9.1-alpha.1.0.20261026030854-d7de535c4288\n`, + ), + ).toEqual([ + `${VCFFI} v0.8.1-0.20261026030854-d7de535c4288`, + `${VCVALUE} v0.9.1-alpha.1.0.20261026030854-d7de535c4288`, + ]) + }) + + it('is not a tag, a pseudo-version quoted in a comment, or another module', () => { + expect(vitamincPseudoVersions(released)).toEqual([]) + expect( + vitamincPseudoVersions( + `// ${VCFFI} ${PSEUDO}\nrequire github.com/tetratelabs/wazero ${PSEUDO}\n`, + ), + ).toEqual([]) + expect(vitamincPseudoVersions('')).toEqual([]) + }) + + it('refuses a final version, naming the pins', () => { + const errors = errorsFor('0.1.0', MAIN, MODULE, temporary) + expect(errors).toHaveLength(1) + expect(errors[0]).toContain('pseudo-version') + expect(errors[0]).toContain(`${VCFFI} ${PSEUDO}`) + expect(errors[0]).toContain(`${VCVALUE} ${PSEUDO}`) + expect(errors[0]).toContain('0.1.0-alpha.1') + }) + + it('lets a pre-release through, from main or a branch', () => { + expect(errorsFor('0.1.0-alpha.1', MAIN, MODULE, temporary)).toEqual([]) + expect(errorsFor('0.1.0-rc.1', BRANCH, MODULE, temporary)).toEqual([]) + }) + + it('does not hold a final version once the pin is a tag', () => { + expect(errorsFor('0.1.0', MAIN, MODULE, released)).toEqual([]) + }) + + it('is reported alongside the other reasons, not instead of them', () => { + const errors = errorsFor('0.1.0', BRANCH, MODULE, temporary).join('\n') + expect(errors).toContain('released from main only') + expect(errors).toContain('pseudo-version') + }) +}) + describe('allowedMajors', () => { it('reads the /vN suffix and nothing else', () => { expect(allowedMajors(MODULE)).toEqual([0, 1]) @@ -125,6 +200,21 @@ describe('the script as the workflow runs it', () => { expect(out).toContain('::error::') }) + it("reads the tree's go.mod, so a final version is refused while it carries a TEMPORARY vitaminc pin", () => { + const goMod = readFileSync( + join(REPO_ROOT, 'languages/golang/go.mod'), + 'utf8', + ) + const pins = vitamincPseudoVersions(goMod) + const { status, out } = run('0.1.0', MAIN) + if (pins.length > 0) { + expect(status).toBe(1) + for (const pin of pins) expect(out).toContain(pin) + } else { + expect(status).toBe(0) + } + }) + it('is what release-golang.yml checks the version with, before any build', () => { // The check is only worth something if it runs before the guests are // built and long before the tag is pushed. diff --git a/scripts/go-release-version.mjs b/scripts/go-release-version.mjs index bb5e739c0..780339ff7 100644 --- a/scripts/go-release-version.mjs +++ b/scripts/go-release-version.mjs @@ -9,7 +9,7 @@ // pushed, rather than by the proxy check at the end of the run, after the tag // exists and the version number is spent. // -// Four rules: +// Five rules: // // 1. CANONICAL SEMVER. Go accepts only `vMAJOR.MINOR.PATCH[-PRERELEASE]` with // no leading zeros in a numeric part or a numeric pre-release identifier, @@ -29,6 +29,14 @@ // the dispatcher picks. A pre-release (a version with a `-suffix`) may be // cut from a branch, which is how a partner alpha is built from an // unmerged stack; a final version may not. +// 5. A FINAL VERSION SHIPS NO TEMPORARY VITAMINC PIN. vitaminc's Go bindings +// (`github.com/cipherstash/vitaminc/bindings/go/…`) have no tags, so +// `go.mod` names them by pseudo-version, and from time to time that is an +// unreleased commit taken ahead of a vitaminc release, marked TEMPORARY in +// `go.mod` and promised in the CHANGELOG to be gone before the module is +// tagged. `release-golang.yml` tags whatever `go.mod` names, so the promise +// is held here: a final version is refused while any vitaminc module is +// pinned to a pseudo-version; a pre-release may carry one. import { readFileSync } from 'node:fs' import { join, resolve } from 'node:path' @@ -43,6 +51,9 @@ const NUMERIC = /^(0|[1-9][0-9]*)$/ const PSEUDO_VERSION = /^v[0-9]+\.(0\.0-|\d+\.\d+-([^+]*\.)?0\.)\d{14}-[A-Za-z0-9]+$/ const IDENTIFIER = /^[0-9A-Za-z-]+$/ +// A module of vitaminc's Go bindings and the version it is required or +// replaced at, in a go.mod stripped of its comments. +const VITAMINC_MODULE = /github\.com\/cipherstash\/vitaminc(?:\/\S+)?\s+(v\S+)/g /** The `module` line of a go.mod. */ export function modulePathOf(goMod) { @@ -51,6 +62,20 @@ export function modulePathOf(goMod) { return match[1] } +/** + * Every `github.com/cipherstash/vitaminc/…` module a go.mod names at a + * pseudo-version, as `path version` strings. Comments are dropped first, so + * the TEMPORARY note explaining a pin does not count as one. + */ +export function vitamincPseudoVersions(goMod) { + const code = goMod.replace(/\/\/[^\n]*/g, '') + const pins = [] + for (const match of code.matchAll(VITAMINC_MODULE)) { + if (PSEUDO_VERSION.test(match[1])) pins.push(match[0].trim()) + } + return pins +} + /** The major version a module path may publish: `/vN` gives N, otherwise 0 or 1. */ export function allowedMajors(modulePath) { const suffix = modulePath.match(/\/v([2-9]|[1-9][0-9]+)$/) @@ -59,9 +84,12 @@ export function allowedMajors(modulePath) { /** * Every reason `version` may not be released from `ref` for the module at - * `modulePath`. Empty when it may. + * `modulePath`, whose go.mod text is `goMod`. Empty when it may. */ -export function goReleaseVersionErrors(version, { modulePath, ref }) { +export function goReleaseVersionErrors( + version, + { modulePath, ref, goMod = '' }, +) { const errors = [] if (version.startsWith('v')) { @@ -125,6 +153,13 @@ export function goReleaseVersionErrors(version, { modulePath, ref }) { ) } + const pins = vitamincPseudoVersions(goMod) + if (pre === null && pins.length > 0) { + errors.push( + `a final version cannot ship vitaminc's Go bindings at a pseudo-version (go.mod names ${pins.join(', ')}); pin a vitaminc tag or a commit on vitaminc main that a release carries first, or give a pre-release version such as ${core}-alpha.1`, + ) + } + return errors } @@ -134,8 +169,9 @@ function main() { console.error('usage: go-release-version.mjs ') process.exit(2) } - const modulePath = modulePathOf(readFileSync(GO_MOD, 'utf8')) - const errors = goReleaseVersionErrors(version, { modulePath, ref }) + const goMod = readFileSync(GO_MOD, 'utf8') + const modulePath = modulePathOf(goMod) + const errors = goReleaseVersionErrors(version, { modulePath, ref, goMod }) for (const error of errors) console.log(`::error::${error}`) if (errors.length > 0) process.exit(1) console.log(`${modulePath} v${version} may be released from ${ref}`) From 02241daea730bcc4ce4bfd51864083dac15d8da1 Mon Sep 17 00:00:00 2001 From: Dan Draper Date: Mon, 26 Oct 2026 23:34:12 +1100 Subject: [PATCH 8/8] chore(golang): pin vitaminc's Go bindings to the storage profile on main vcffi and vcvalue move from d7de535c4, the merge of cipherstash/vitaminc#526 into a feature branch, to 06568ba93, the commit cipherstash/vitaminc#544 put on vitaminc main. The bindings' files are the same at both commits, so no Go behaviour changes; the pin now names a commit a vitaminc release will carry. The bindings still have no tag, so the release guard keeps refusing a final version of this module until one exists. Claude-Session: https://claude.ai/code/session_01N7bCqa3ZVJMcEE99RzVqEu --- languages/golang/CHANGELOG.md | 10 +++++----- languages/golang/go.mod | 20 ++++++++++---------- languages/golang/go.sum | 8 ++++---- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/languages/golang/CHANGELOG.md b/languages/golang/CHANGELOG.md index e0e58056f..ca56396a0 100644 --- a/languages/golang/CHANGELOG.md +++ b/languages/golang/CHANGELOG.md @@ -187,11 +187,11 @@ listed under **Breaking**. Releases are tagged `languages/golang/vX.Y.Z`. ### Changed -- **vitaminc's Go bindings at an unreleased vitaminc commit (temporary).** - `vcffi` and `vcvalue` move to vitaminc `d7de535c4`, the merge of - cipherstash/vitaminc#526 into its `feat/aead-frame-storage-profile` - branch, for the storage profile (`vcffi.MarshalStorage`); vitaminc's Go - bindings have no tags, so the commit is the pin. One encoding changes +- **vitaminc's Go bindings at an untagged vitaminc commit (temporary).** + `vcffi` and `vcvalue` move to vitaminc `06568ba93` on its `main`, which + carries the storage profile (`vcffi.MarshalStorage`, from + cipherstash/vitaminc#526); vitaminc's Go bindings have no tags, so the + commit is the pin. One encoding changes with them, and it comes from vitaminc `e9c3837b` (on `main`, in no Go tag yet), not from the storage profile: a nil pointer to a fixed-width value (`(*int64)(nil)`) is written as a typed null where it was written diff --git a/languages/golang/go.mod b/languages/golang/go.mod index ec6f78614..ac66bd9a5 100644 --- a/languages/golang/go.mod +++ b/languages/golang/go.mod @@ -2,17 +2,17 @@ module github.com/cipherstash/stack/languages/golang go 1.26.10 -// TEMPORARY: vcffi and vcvalue at vitaminc d7de535c4, the merge of -// cipherstash/vitaminc#526 (the storage profile, vcffi.MarshalStorage) into -// its branch feat/aead-frame-storage-profile. That commit is not on vitaminc -// main and in no release, and vitaminc's Go bindings have no tags. Only Go -// writes and reads that profile; the guests stay on vitaminc 0.8.0 from -// crates.io, whose transport is the one this commit writes. The pin moves to -// main, or to a tag, when vitaminc provides one; until then -// scripts/go-release-version.mjs refuses a final version of this module. +// TEMPORARY: vcffi and vcvalue at vitaminc 06568ba93, the commit on vitaminc +// main that carries the storage profile (vcffi.MarshalStorage, from +// cipherstash/vitaminc#526 by way of cipherstash/vitaminc#544). vitaminc's Go +// bindings have no tags, so the commit is the pin. Only Go writes and reads +// that profile; the guests stay on vitaminc 0.8.0 from crates.io, whose +// transport is the one this commit writes. The pin moves to a tag when +// vitaminc provides one; until then scripts/go-release-version.mjs refuses a +// final version of this module. require ( - github.com/cipherstash/vitaminc/bindings/go/vcffi v0.0.0-20261026030854-d7de535c4288 - github.com/cipherstash/vitaminc/bindings/go/vcvalue v0.0.0-20261026030854-d7de535c4288 + github.com/cipherstash/vitaminc/bindings/go/vcffi v0.0.0-20261026122913-06568ba93bd9 + github.com/cipherstash/vitaminc/bindings/go/vcvalue v0.0.0-20261026122913-06568ba93bd9 github.com/tetratelabs/wazero v1.12.0 golang.org/x/oauth2 v0.37.0 ) diff --git a/languages/golang/go.sum b/languages/golang/go.sum index bb03f7b1a..6c30aed4a 100644 --- a/languages/golang/go.sum +++ b/languages/golang/go.sum @@ -1,7 +1,7 @@ -github.com/cipherstash/vitaminc/bindings/go/vcffi v0.0.0-20261026030854-d7de535c4288 h1:jX/ouzDlPnEBHTx6E4IdGqdHsl9ImhxRhqK1mQ6CKT8= -github.com/cipherstash/vitaminc/bindings/go/vcffi v0.0.0-20261026030854-d7de535c4288/go.mod h1:7hPdj2tN3YpcPZYdZ+Fe7+Fsc7dnSyH5bsL9y+LbRuM= -github.com/cipherstash/vitaminc/bindings/go/vcvalue v0.0.0-20261026030854-d7de535c4288 h1:8BMb3tOlrztg21QmDMdb5t6gmRc31H/XqlpofOJmB30= -github.com/cipherstash/vitaminc/bindings/go/vcvalue v0.0.0-20261026030854-d7de535c4288/go.mod h1:RJODA1DCSm4H+1pbmCzDdT2pAIkxhinwIu6ewjh5sPs= +github.com/cipherstash/vitaminc/bindings/go/vcffi v0.0.0-20261026122913-06568ba93bd9 h1:ZF7YOnal03J9uj5ggsqF3vUlPgM3affKpz3DHIyO4UE= +github.com/cipherstash/vitaminc/bindings/go/vcffi v0.0.0-20261026122913-06568ba93bd9/go.mod h1:7hPdj2tN3YpcPZYdZ+Fe7+Fsc7dnSyH5bsL9y+LbRuM= +github.com/cipherstash/vitaminc/bindings/go/vcvalue v0.0.0-20261026122913-06568ba93bd9 h1:y1+c1HV4FeZ+EiX9k3imPIzQYYK3QZD7HoZU8YORa6Q= +github.com/cipherstash/vitaminc/bindings/go/vcvalue v0.0.0-20261026122913-06568ba93bd9/go.mod h1:RJODA1DCSm4H+1pbmCzDdT2pAIkxhinwIu6ewjh5sPs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=