Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
481 commits
Select commit Hold shift + click to select a range
1ad0376
feat(stack-encrypt): let a target sealed under its own context opt in…
coderdan Oct 17, 2026
f41e258
feat(eql-bindings): query EQL v4 types through Stack Encrypt's typed …
coderdan Oct 17, 2026
fd55c20
docs(stack-encrypt,eql-bindings): say Answers needs LabelledTarget, a…
coderdan Oct 17, 2026
d827132
Merge pull request #1244 from cipherstash/feat/eql-v4-typed-queries
coderdan Oct 19, 2026
b6523a1
feat(eql): let an EQL v4 text search query carry only the terms its o…
coderdan Oct 18, 2026
6a1b2c0
feat(stack-encrypt)!: hand a target's query the index that answers it…
coderdan Oct 18, 2026
73583d2
feat(eql-bindings): produce TextSearch, its query carrying only its o…
coderdan Oct 19, 2026
213d998
test(golang): generate and seal a TextSearch field, each query carryi…
coderdan Oct 19, 2026
ba28168
feat(stack-encrypt)!: derive ore-rs 6-bit block ORE terms as a fifth …
coderdan Oct 19, 2026
b0c6cf4
feat(stack-encrypt): the JSON index, with node-local entries, a token…
coderdan Oct 19, 2026
faa3cb7
feat(eql): the json_tree domain family for Stack Encrypt's JSON index…
coderdan Oct 19, 2026
57becce
docs(plans): record where the JSON index implementation departs from …
coderdan Oct 19, 2026
358c0fd
Merge pull request #1274 from cipherstash/feat/eql-v4-text-search-opt…
coderdan Oct 19, 2026
a6619c4
feat(eql): compare ore-rs 6-bit block ORE in EQL v4, as ore_block_v2
coderdan Oct 19, 2026
9fa1312
feat(golang): read the ore_block index the engine reports
coderdan Oct 19, 2026
45a79a0
feat(eql-bindings): produce the block ORE EQL v4 types
coderdan Oct 19, 2026
ea92601
test(eql): let v4 own the JSON tree domains v3 does not in the coexis…
coderdan Oct 19, 2026
a05affa
Merge pull request #1280 from cipherstash/feat/eql-v4-block-ore
coderdan Oct 19, 2026
c6df166
test(stack-encrypt): kill the JSON index mutants the in-diff gate found
coderdan Oct 20, 2026
01c193b
perf(eql, stack-encrypt): store the json_tree projection, walk by par…
coderdan Oct 21, 2026
c98162f
fix(eql): keep json_tree_query out of the planner's inlining so the G…
coderdan Oct 21, 2026
319395d
test(eql, stack-encrypt): pin the stored projection's wire key and th…
coderdan Oct 21, 2026
c45bb79
feat(eql, stack-encrypt): chain json_tree tokens by ordinal and probe…
coderdan Oct 21, 2026
3860ecc
docs(eql, stack-encrypt): record the ordinal-chained walk's measureme…
coderdan Oct 21, 2026
6f74098
test(stack-encrypt): bound the children probe and pin the gated walk'…
coderdan Oct 21, 2026
b8c8ba4
test(stack-encrypt): drop the unreachable depth bound from the engine…
coderdan Oct 21, 2026
f76fe37
feat(stack-encrypt, eql): order json_tree leaves only at the paths th…
coderdan Oct 22, 2026
4f7c4fe
test(stack-encrypt): pin the laid-out plan's specs, Debug form and co…
coderdan Oct 22, 2026
dddb3a3
docs(stack-encrypt): record the storage measured with the ordering te…
coderdan Oct 22, 2026
810b687
test(stack-encrypt): pin that a laid-out plan refuses a clashing open…
coderdan Oct 22, 2026
6fe6128
Merge remote-tracking branch 'origin/next' into feat/json-index-stack…
coderdan Oct 22, 2026
725a27a
refactor(stack-encrypt): derive the JSON header's Debug with OpaqueDebug
coderdan Oct 22, 2026
60c12d2
Merge branch 'feat/json-index-stack-encrypt' into perf/json-tree-walk…
coderdan Oct 22, 2026
d292112
Merge branch 'perf/json-tree-walk-and-projection' into feat/json-tree…
coderdan Oct 22, 2026
37e02ab
feat(json-tree): spell the chain fields in base64, the ciphertext in …
coderdan Oct 23, 2026
9eb4b69
docs(json-tree): record that the derived nonce is rejected
coderdan Oct 23, 2026
55bbd66
docs(plans): rename the JSON index design to the json_tree index record
coderdan Oct 23, 2026
06af208
Merge branch 'docs/json-index-stack-encrypt-spike' into feat/json-ind…
coderdan Oct 23, 2026
b9ac367
docs: point at the renamed json_tree index record
coderdan Oct 23, 2026
cfd5dea
Merge branch 'feat/json-index-stack-encrypt' into perf/json-tree-walk…
coderdan Oct 23, 2026
9657508
Merge branch 'perf/json-tree-walk-and-projection' into feat/json-tree…
coderdan Oct 23, 2026
760e5bd
test(eql): hold the planner to the json_tree indexes on every support…
coderdan Oct 23, 2026
e7f782d
Merge remote-tracking branch 'origin/feat/json-tree-ordinal-chain' in…
coderdan Oct 23, 2026
47235ec
fix(docs): make the containment oracle flatten the stored document it…
coderdan Oct 23, 2026
a93a2f8
fix(docs): bring the token-chain prototype to the derivations the pla…
coderdan Oct 23, 2026
82fccbb
docs(plans): correct the json_tree record where it overstates what it…
coderdan Oct 23, 2026
0dd8a90
Merge branch 'docs/json-index-stack-encrypt-spike' into feat/json-ind…
coderdan Oct 23, 2026
5fb586f
Merge branch 'feat/json-index-stack-encrypt' into perf/json-tree-walk…
coderdan Oct 23, 2026
2b2a663
Merge branch 'perf/json-tree-walk-and-projection' into feat/json-tree…
coderdan Oct 23, 2026
fbb0954
Merge branch 'feat/json-tree-ordinal-chain' into feat/json-tree-gates
coderdan Oct 23, 2026
497c01e
fix(stack-encrypt): refuse a JSON document deeper than its sealed roo…
coderdan Oct 23, 2026
aa90e8b
fix(stack-encrypt): key the json_tree token step and member rank
coderdan Oct 23, 2026
151409c
fix(stack-encrypt, eql): bound what a stored json_tree document can m…
coderdan Oct 23, 2026
71ffa57
fix(stack-encrypt): print a JsonTerms as its node count
coderdan Oct 23, 2026
03a9223
fix(stack-encrypt): make AtPath<Eq> on the JSON index equality, not c…
coderdan Oct 23, 2026
fe7e457
fix(eql): block comparisons between a json_tree entry and plain jsonb
coderdan Oct 23, 2026
fd89e6a
docs(stack-encrypt): record the JSON index in the changelog
coderdan Oct 23, 2026
af54eda
style(stack-encrypt): rustfmt the json_tree review fixes
coderdan Oct 23, 2026
9b740b0
Merge branch 'feat/json-index-stack-encrypt' into perf/json-tree-walk…
coderdan Oct 23, 2026
500d508
Merge branch 'perf/json-tree-walk-and-projection' into feat/json-tree…
coderdan Oct 23, 2026
b91fba7
Merge branch 'feat/json-tree-ordinal-chain' into feat/json-tree-gates
coderdan Oct 23, 2026
c645a98
test(stack-encrypt): line up arrays as well as objects against the re…
coderdan Oct 23, 2026
612d5f5
Merge branch 'feat/json-index-stack-encrypt' into perf/json-tree-walk…
coderdan Oct 23, 2026
c2c6798
Merge branch 'perf/json-tree-walk-and-projection' into feat/json-tree…
coderdan Oct 23, 2026
765107e
Merge branch 'feat/json-tree-ordinal-chain' into feat/json-tree-gates
coderdan Oct 23, 2026
923364d
fix(stack-encrypt, eql-bindings): open a fallen-back walk to what the…
coderdan Oct 23, 2026
c1059d7
fix(eql): bound the json_tree walks by the document's size, not only …
coderdan Oct 23, 2026
014d25d
docs(eql): align the json_tree Phase 3 notes with the report they cit…
coderdan Oct 23, 2026
31d5da4
test(stack-encrypt): pin that the last attachment decides the fallbac…
coderdan Oct 23, 2026
fee616f
Merge branch 'perf/json-tree-walk-and-projection' into feat/json-tree…
coderdan Oct 23, 2026
7bf1d75
docs(stack-encrypt): put each_stored_form_opens_as_a_record_field's d…
coderdan Oct 23, 2026
cd8c060
Merge branch 'feat/json-tree-ordinal-chain' into feat/json-tree-gates
coderdan Oct 23, 2026
67b1f54
fix(eql, stack-encrypt): bind the child's selector into the json_tree…
coderdan Oct 23, 2026
d72d9fe
fix(stack-encrypt): refuse array positions in ordered json paths and …
coderdan Oct 23, 2026
d267735
test(stack-encrypt): fuzz the json wire decoders and refresh the fuzz…
coderdan Oct 23, 2026
d3d7f76
docs(eql): describe json_tree's base85 `c` and eight-byte `p` in type…
coderdan Oct 23, 2026
151fd5d
docs(stack-encrypt, eql-bindings): changelog the layout plan, the gat…
coderdan Oct 23, 2026
6d9ece2
docs(json-tree): count the walk's misses in the eight-byte parent tag…
coderdan Oct 23, 2026
f1f27f2
feat(stack-encrypt, eql-bindings): open a json_tree walk's rows throu…
coderdan Oct 23, 2026
63bec8f
fix(stack-encrypt): hold the json index's plaintext under Protected a…
coderdan Oct 23, 2026
c5d493f
Merge branch 'feat/json-tree-ordinal-chain' into feat/json-tree-gates
coderdan Oct 23, 2026
5dc9136
Merge branch 'next' into docs/json-index-stack-encrypt-spike
coderdan Oct 23, 2026
1a86672
test(stack-encrypt): pin JsonPath::covers on positions
coderdan Oct 23, 2026
0d441a2
Merge branch 'feat/json-tree-ordinal-chain' into feat/json-tree-gates
coderdan Oct 23, 2026
8598446
Merge branch 'docs/json-index-stack-encrypt-spike' into feat/json-ind…
coderdan Oct 23, 2026
53a63fa
Merge branch 'feat/json-index-stack-encrypt' into perf/json-tree-walk…
coderdan Oct 23, 2026
62f2132
Merge branch 'perf/json-tree-walk-and-projection' into feat/json-tree…
coderdan Oct 23, 2026
d3a47b1
Merge branch 'feat/json-tree-ordinal-chain' into feat/json-tree-gates
coderdan Oct 23, 2026
952d0be
Merge branch 'main' into next
coderdan Oct 23, 2026
8f9c9fb
chore(stack-encrypt)!: move to vitaminc 0.8, clww-ore 0.6 and ore-rs …
coderdan Oct 21, 2026
0563857
test(stack-encrypt): exclude KindOf::typed_null's equivalent default …
coderdan Oct 21, 2026
30a89e5
chore(deps): take clww-ore 0.6.0 and ore-rs 0.10.0 from crates.io
coderdan Oct 23, 2026
2ed8e17
chore(eql): build the block ORE timing harness on ore-rs 0.10
coderdan Oct 23, 2026
e88d6b8
docs(stack-encrypt): correct what the ORE upgrade made false
coderdan Oct 23, 2026
4cf7a5b
refactor(golang): take vitaminc only through stack-encrypt's re-exports
coderdan Oct 23, 2026
3c17e96
docs: describe the order indexes and a host's typed null for the reader
coderdan Oct 23, 2026
eb73756
fix(stack-encrypt): address review on the ORE upgrade
coderdan Oct 23, 2026
d830aea
feat(stack-encrypt): a data plan field says how it stores a null
coderdan Oct 21, 2026
3c9084f
feat(eql-bindings): a storage-only EQL type stores a None as long as …
coderdan Oct 21, 2026
089a9d9
feat(golang): an optional sealed field, declared over a pointer
coderdan Oct 21, 2026
06ce1de
test(eql): PostgreSQL stores a sealed null in a storage-only column
coderdan Oct 21, 2026
0a0600d
test(stack-encrypt): a sealed null only in a termless target, and onl…
coderdan Oct 21, 2026
b22be11
feat: a null has an equality term, and a sealed null sits beside one
coderdan Oct 21, 2026
d0ed4a1
test(stack-encrypt): a target query of a null needs a sealed field an…
coderdan Oct 21, 2026
2e23a58
test(stack-encrypt): a sealed null is as long as a value of every fix…
coderdan Oct 23, 2026
f3ade86
docs(stack-encrypt): point from_bytes at the plan null mode that pads…
coderdan Oct 23, 2026
410db32
docs(stack-encrypt): say a null has an equality term
coderdan Oct 23, 2026
c7b1205
fix(eql-bindings): seal no null of text from an Option<String>
coderdan Oct 23, 2026
d6e2a17
feat(golang): generate EncryptNull for a field that seals its nil
coderdan Oct 23, 2026
ab46dcd
Merge pull request #1327 from cipherstash/chore/vitaminc-0-8-ore-0-10
coderdan Oct 23, 2026
1f366a7
docs(eql): state what the json_tree entry order and extraction reveal
coderdan Oct 23, 2026
d01d547
ci(eql): gate ci-required on the per-version encryption suite
coderdan Oct 23, 2026
491b09c
test(eql): pin TOAST storage exactly and hold prepared-statement plans
coderdan Oct 23, 2026
17ac84f
docs(eql): do not claim an extracted value comes from the row it is l…
coderdan Oct 23, 2026
e219a94
Revert "fix(eql-bindings): seal no null of text from an Option<String>"
coderdan Oct 23, 2026
e6dc8f9
docs(eql-bindings): say a text null is not padded
coderdan Oct 23, 2026
bae6ce0
feat(stack-encrypt): order dates, timestamps and decimals
coderdan Oct 23, 2026
8b74c7d
feat(eql-bindings, golang): produce the ordered date, timestamp and n…
coderdan Oct 23, 2026
f8fc092
docs(eql): say the date, timestamp and numeric plaintexts are Stack E…
coderdan Oct 23, 2026
f048ac3
feat(stack-encrypt): serde for the order terms, and an owning from_bytes
coderdan Oct 23, 2026
da78393
feat(eql-bindings, eql): EQL v4 order terms are Stack Encrypt's typed…
coderdan Oct 23, 2026
e7a2fad
docs(eql): an EQL v4 order term is one hex string, checked when written
coderdan Oct 23, 2026
eb415b4
docs(eql): link U-003 from the changelog's directory, and show string…
coderdan Oct 23, 2026
c6e3af1
Merge pull request #1328 from cipherstash/feat/nullable-sealed-fields
coderdan Oct 23, 2026
e81e12c
Merge pull request #1337 from cipherstash/feat/order-dates-timestamps…
coderdan Oct 23, 2026
5a7d19e
Merge pull request #1340 from cipherstash/feat/eql-v4-order-term-types
coderdan Oct 23, 2026
52f9d39
ci: key the FFI cache and its workflow filters to the eql-bindings fi…
coderdan Oct 19, 2026
09d7365
ci(tests): run the live JS jobs only when a pull request can reach them
coderdan Oct 19, 2026
02e49dc
ci(tests): build EQL before the script tests, so the shipped-bundle c…
coderdan Oct 23, 2026
5eb9f2c
test(golang): run the Go SDK's rows and queries through EQL v4 in Pos…
coderdan Oct 19, 2026
0986a92
test(golang): query the block ORE EQL types in the PostgreSQL test
coderdan Oct 23, 2026
9f9db2d
Merge pull request #1290 from cipherstash/ci/path-filter-precision
coderdan Oct 23, 2026
a8f09b4
test(golang): fail the PostgreSQL task when a test did not run, or re…
coderdan Oct 23, 2026
8f2c8f8
test(golang): store a nil row in every EQL type in the PostgreSQL test
coderdan Oct 23, 2026
5d1791c
Merge pull request #1291 from cipherstash/ci/go-eql-postgres
coderdan Oct 23, 2026
1cdfb94
test(golang): check EQL v4 order with random values in PostgreSQL
coderdan Oct 23, 2026
5532dc9
test(golang): run every order edge as a query value
coderdan Oct 23, 2026
ccc7bdc
test(golang): take the order index from the round-trip test's list
coderdan Oct 23, 2026
b8d7042
test(eql): hold json_tree containment to jsonb over generated documents
coderdan Oct 23, 2026
1050ba6
Merge pull request #1344 from cipherstash/test/go-eql-order-properties
coderdan Oct 23, 2026
dd9bc51
test(eql): generate the json_tree containment cases with proptest
coderdan Oct 24, 2026
f2e998c
test(stack-encrypt): restructure the JSON index tests and generate co…
coderdan Oct 24, 2026
8930415
refactor(stack-encrypt): rename Plan::value().encrypt_as to encrypt_w…
coderdan Oct 24, 2026
e83644c
Merge branch 'feat/json-tree-ordinal-chain' into feat/json-tree-gates
coderdan Oct 24, 2026
b8d86cd
docs(eql): say an extracted entry proves its column, not its row
coderdan Oct 24, 2026
c31b0f3
test(eql): hold the json_tree planner test to the generic plan too
coderdan Oct 24, 2026
7d6a19e
fix(stack-encrypt): refuse JSON positions past PostgreSQL's integer
coderdan Oct 24, 2026
021603c
docs(stack-encrypt): show what a target Layout is for, with an example
coderdan Oct 24, 2026
ba0c2a2
docs(stack-encrypt): simplify the one-value plan and encrypt_with_lay…
coderdan Oct 24, 2026
e10c93b
refactor(stack-encrypt): keep a JSON index's ordered paths typed up t…
coderdan Oct 24, 2026
3999ed4
docs(stack-encrypt): record the JSON position bound in the CHANGELOG
coderdan Oct 24, 2026
2f03dfc
Merge branch 'feat/json-tree-gates' into test/json-tree-containment-p…
coderdan Oct 24, 2026
14ccc86
refactor(eql-bindings): name the text encoding of each json_tree wire…
coderdan Oct 24, 2026
63582ee
fix(eql): bound every json_tree walk by the document's entry count
coderdan Oct 24, 2026
5003d08
docs(eql): mention the json_tree encoding newtypes in the changeset
coderdan Oct 24, 2026
99351a5
perf(stack-encrypt): move the fallback's nodes out of the sealed root…
coderdan Oct 24, 2026
fc9d3ef
Merge branch 'feat/json-tree-ordinal-chain' into feat/json-tree-gates
coderdan Oct 24, 2026
7d1f9e5
Merge branch 'feat/json-tree-gates' into test/json-tree-containment-p…
coderdan Oct 24, 2026
6c27ab1
docs(plans): note where the json_tree record is superseded on positio…
coderdan Oct 24, 2026
baa7458
Merge branch 'docs/json-index-stack-encrypt-spike' into feat/json-ind…
coderdan Oct 24, 2026
56264f2
Merge branch 'feat/json-index-stack-encrypt' into perf/json-tree-walk…
coderdan Oct 24, 2026
9cc4c8a
Merge branch 'perf/json-tree-walk-and-projection' into feat/json-tree…
coderdan Oct 24, 2026
4d672e1
Merge branch 'feat/json-tree-ordinal-chain' into feat/json-tree-gates
coderdan Oct 24, 2026
0f80f8c
Merge branch 'feat/json-tree-gates' into test/json-tree-containment-p…
coderdan Oct 24, 2026
7d63656
Merge pull request #1250 from cipherstash/docs/json-index-stack-encry…
coderdan Oct 24, 2026
1260823
Merge branch 'next' into feat/json-index-stack-encrypt
coderdan Oct 24, 2026
ba8a940
Merge branch 'feat/json-index-stack-encrypt' into perf/json-tree-walk…
coderdan Oct 24, 2026
fc462af
Merge branch 'perf/json-tree-walk-and-projection' into feat/json-tree…
coderdan Oct 24, 2026
6127434
Merge branch 'feat/json-tree-ordinal-chain' into feat/json-tree-gates
coderdan Oct 24, 2026
f2af67c
Merge branch 'feat/json-tree-gates' into test/json-tree-containment-p…
coderdan Oct 24, 2026
6a4f1c2
fix(eql-bindings): make Base64Text and Base85Text guarantee their enc…
coderdan Oct 24, 2026
666f407
Merge branch 'feat/json-tree-ordinal-chain' into feat/json-tree-gates
coderdan Oct 24, 2026
338e77b
Merge branch 'feat/json-tree-gates' into test/json-tree-containment-p…
coderdan Oct 24, 2026
8aa177a
test(scripts): count eql-bindings' encoding module among the stack-en…
coderdan Oct 24, 2026
fcf8f0c
Merge branch 'feat/json-tree-ordinal-chain' into feat/json-tree-gates
coderdan Oct 24, 2026
f35ee53
Merge branch 'feat/json-tree-gates' into test/json-tree-containment-p…
coderdan Oct 24, 2026
536d9e5
Merge pull request #1288 from cipherstash/feat/json-index-stack-encrypt
coderdan Oct 24, 2026
1c5e048
Merge pull request #1325 from cipherstash/perf/json-tree-walk-and-pro…
coderdan Oct 24, 2026
5652ec5
Merge pull request #1329 from cipherstash/feat/json-tree-ordinal-chain
coderdan Oct 24, 2026
b104982
Merge pull request #1336 from cipherstash/feat/json-tree-gates
coderdan Oct 24, 2026
9d05f8b
fix(stack-encrypt): check that a json_tree read returns the entry ask…
coderdan Oct 24, 2026
c3d7fee
docs(plans): name the pull request that took the extracted-entry check
coderdan Oct 24, 2026
1586b9a
docs(plans): design json_tree tokens derivable from the path alone
coderdan Oct 24, 2026
edabee6
docs(eql): state what json_tree reveals once tokens are derivable
coderdan Oct 24, 2026
15c952e
feat(stack-encrypt): derive json_tree tokens from the path alone
coderdan Oct 24, 2026
cf82a43
feat(eql): read json_tree subtrees by a list of tokens, without gates
coderdan Oct 24, 2026
6c8cd45
docs(plans): fold the owner's decisions into the derivable-token design
coderdan Oct 24, 2026
effe92b
test(stack-encrypt): check the json_tree leakage page against the scheme
coderdan Oct 24, 2026
683802b
test(stack-encrypt): refuse one json_tree token answered by two rows
coderdan Oct 24, 2026
f94a6eb
perf(eql): inline the json_tree chain primitives and build the read's…
coderdan Oct 25, 2026
f3400a2
fix(stack-encrypt): cap a json_tree read at 8,192 tokens, not 65,536
coderdan Oct 25, 2026
0ec0c73
ci: keep Biome off the json_tree vectors fixture
coderdan Oct 25, 2026
617a156
style(stack-encrypt): rustfmt the token-cap test
coderdan Oct 25, 2026
9ec5c59
fix(stack-encrypt): refuse a json_tree walk cut short as the wrong entry
coderdan Oct 25, 2026
aa591f2
fix(stack-encrypt): refuse a json_tree walk that leaves out an array'…
coderdan Oct 25, 2026
efc6bba
docs(eql): say what a json_tree walk with no gates still does not check
coderdan Oct 25, 2026
790d827
Merge branch 'fix/json-tree-extracted-entry-check' into docs/json-tre…
coderdan Oct 25, 2026
f42496c
Merge branch 'docs/json-tree-derivable-tokens' into feat/json-tree-de…
coderdan Oct 25, 2026
a0261c8
Merge branch 'fix/json-tree-extracted-entry-check' into test/json-tre…
coderdan Oct 25, 2026
241c776
docs(eql): state the ordering term's length, folding and cross-path e…
coderdan Oct 25, 2026
f2331ab
docs(plans): count a wildcard's tokens per array and keep the path ou…
coderdan Oct 25, 2026
febc4b2
fix(stack-encrypt): refuse a repeated or two-document read row as the…
coderdan Oct 25, 2026
516404a
fix(stack-encrypt): keep the last entry carrying a tag in the SQL rea…
coderdan Oct 25, 2026
a76c5fd
docs(stack-encrypt): count probes per expanded array, and drop the ga…
coderdan Oct 25, 2026
c06c5d3
docs(eql): say which json_tree read the entries-times-tokens LIMIT bo…
coderdan Oct 25, 2026
fbdaa23
docs(eql): say a withheld probe hides truncation, and that a read is …
coderdan Oct 25, 2026
d1025be
docs(plans): point section 3 of the derivable-token design at the dec…
coderdan Oct 25, 2026
e0191df
Merge branch 'docs/json-tree-derivable-tokens' into feat/json-tree-de…
coderdan Oct 25, 2026
e4dd59a
Merge pull request #1345 from cipherstash/test/json-tree-containment-…
coderdan Oct 25, 2026
4ce771c
style(stack-encrypt): rustfmt the read model lookup
coderdan Oct 25, 2026
ff287a3
fix(stack-encrypt): open JSON index floats to the exact value sealed
coderdan Oct 25, 2026
fe55bf1
Merge branch 'next' into fix/json-tree-extracted-entry-check
coderdan Oct 25, 2026
9a7ca37
Merge branch 'fix/json-tree-extracted-entry-check' into test/json-tre…
coderdan Oct 25, 2026
2f3a384
Merge branch 'fix/json-tree-extracted-entry-check' into docs/json-tre…
coderdan Oct 25, 2026
9c0ab3b
Merge branch 'docs/json-tree-derivable-tokens' into feat/json-tree-de…
coderdan Oct 25, 2026
997c64b
fix(stack-encrypt): take any wildcard bound on a path that uses none
coderdan Oct 25, 2026
8489400
Merge branch 'feat/json-tree-derivable-tokens' into test/json-tree-le…
coderdan Oct 25, 2026
b992e78
docs(eql): v3 also reveals which leaves share an array element
coderdan Oct 25, 2026
839c0f6
test(stack-encrypt): port the json_tree leakage profile to the #1363 …
coderdan Oct 25, 2026
fa26cb5
test(stack-encrypt): derive child tokens only from tokens that find a…
coderdan Oct 25, 2026
09a9dfc
docs(plans): compare the json_tree leakage profile before and after #…
coderdan Oct 25, 2026
561f273
Merge branch 'feat/json-tree-derivable-tokens' into test/json-tree-le…
coderdan Oct 25, 2026
ae27e40
Merge pull request #1358 from cipherstash/fix/json-tree-extracted-ent…
coderdan Oct 25, 2026
b2a20ab
docs(eql): a probe is distinguishable, and v3 also groups leaves by e…
coderdan Oct 25, 2026
64821b0
docs(plans): lead the leakage before-and-after note with the net
coderdan Oct 25, 2026
0edead7
docs(plans): keep the consecutive-position check, and put the probe e…
coderdan Oct 25, 2026
ba9a87f
Merge branch 'docs/json-tree-derivable-tokens' into feat/json-tree-de…
coderdan Oct 25, 2026
a386b9c
docs(plans): say which kind of absence each (none) in the clause tabl…
coderdan Oct 25, 2026
d6f02a4
docs(plans): an absent old-profile clause reads 'not measured', not '…
coderdan Oct 25, 2026
bdea70a
Merge branch 'feat/json-tree-derivable-tokens' into test/json-tree-le…
coderdan Oct 25, 2026
ee025bd
test(stack-encrypt): pin the one substitution only the position rule …
coderdan Oct 25, 2026
12207f6
Merge pull request #1361 from cipherstash/docs/json-tree-derivable-to…
coderdan Oct 25, 2026
7509600
test(stack-encrypt): hold the ordering terms' shared prefixes in the …
coderdan Oct 25, 2026
076a921
test(stack-encrypt): compare the leakage harness's values across kinds
coderdan Oct 25, 2026
e52fe64
test(stack-encrypt): check the wire field names of every query term a…
coderdan Oct 25, 2026
6b38463
test(stack-encrypt): let token_derivability try the element rule past…
coderdan Oct 25, 2026
7ca5b0d
test(stack-encrypt): give the leakage profile's four unchecked clause…
coderdan Oct 25, 2026
faff233
test(stack-encrypt): widen the leakage corpus to the clauses it never…
coderdan Oct 25, 2026
981360f
test(stack-encrypt): say #1363 corrected the probe comment, and give …
coderdan Oct 25, 2026
5455ae0
docs(plans): correct the leakage note's net, and record the review
coderdan Oct 25, 2026
294f03e
Merge branch 'feat/json-tree-derivable-tokens' into test/json-tree-le…
coderdan Oct 25, 2026
905791d
test(stack-encrypt): state what a path ordered over numbers and strin…
coderdan Oct 25, 2026
7c2f470
test(stack-encrypt): hold the page's "equality is as a 64-bit float" …
coderdan Oct 25, 2026
e50c18b
docs(eql): say the ordering terms' shared prefix is CLLW's known profile
coderdan Oct 25, 2026
fd22900
docs(plans): record the owner's decisions in the leakage note
coderdan Oct 25, 2026
8c843ee
fix(stack-encrypt): refuse a container at the deepest level the index…
coderdan Oct 25, 2026
7f7f5ce
Merge branch 'feat/json-tree-derivable-tokens' into test/json-tree-le…
coderdan Oct 25, 2026
25e9d69
Merge pull request #1363 from cipherstash/feat/json-tree-derivable-to…
coderdan Oct 25, 2026
bf42fc5
Merge pull request #1362 from cipherstash/test/json-tree-leakage-profile
coderdan Oct 25, 2026
ebdb5de
Merge pull request #1366 from cipherstash/fix/json-float-roundtrip
coderdan Oct 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
36 changes: 36 additions & 0 deletions .cargo/mutants.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ exclude_re = [
'stack-encrypt/src/cipher\.rs:\d+:\d+: replace client_key_provider -> EnvKeyProvider with Default::default\(\)$',
'stack-encrypt/src/target/pending\.rs:443:9: replace <impl IntoFuture for Pending<.*>>::into_future -> Self::IntoFuture with Default::default\(\)$',
# stack-encrypt — equivalent.
# An operator and an index are unit structs, whose `Default` is the value
# `clone` and `case_sensitive` already return.
'stack-encrypt/src/op(/mod)?\.rs:\d+:\d+: replace <impl Clone for Matches<C>>::clone -> Self with Default::default\(\)$',
'stack-encrypt/src/op(/mod)?\.rs:\d+:\d+: replace Matches::case_sensitive -> Matches<CaseSensitive> with Default::default\(\)$',
'stack-encrypt/src/target/index\.rs:\d+:\d+: replace <impl Clone for Match<O>>::clone -> Self with Default::default\(\)$',
'stack-encrypt/src/cipher\.rs:\d+:\d+: replace StackCipher<FromEnv>::builder -> StackCipherBuilder with Default::default\(\)$',
'stack-encrypt/src/sem/mod\.rs:\d+:\d+: replace <impl MatchConfig for DefaultMatch>::options -> MatchOptions with Default::default\(\)$',
# Both unit-context conversions explicitly return Self::default().
Expand All @@ -80,6 +85,37 @@ exclude_re = [
# the empty list (`dynamic::target::NoTargets`), which a test asserts on
# and `resolve` turns into `TargetError::NoTargets`; `vec![]` is the body.
'stack-encrypt/src/dynamic/target\.rs:\d+:\d+: replace <impl TargetResolver for NoTargets>::targets -> Vec<TargetDescriptor> with vec!\[\]$',
# A passthrough, and a typed null as any null, has no kind:
# `KindOf::passthrough` and `KindOf::typed_null` return `Ok(None)`, which
# is `Ok(Default::default())`. `a_frame_names_its_kind` asserts the first.
'stack-encrypt/src/dynamic/value_frame\.rs:\d+:\d+: replace <impl Sink for KindOf>::(passthrough|typed_null) -> Result<Self::Ok, DecodeError> with Ok\(Default::default\(\)\)$',
# `model`'s `admits` guard on a date, decimal or undefined value is checked
# again by vitaminc on every path that reaches it: the frame writer behind
# `Modeled` and `ModelOnly` refuses an inadmissible logical value
# (`FrameWriteError::Invalid`, mapped to the same `Expected`), and
# `ValueFrame::check` only walks frames `Frame::parse` has validated. So
# `true` there refuses the same values with the same error. The guard stays
# so the model says what it holds without leaning on the writer behind it.
'stack-encrypt/src/dynamic/value_frame\.rs:\d+:\d+: replace match guard logical\.admits\(&value\) with true in model$',
# A serde visitor's `expecting` writes only the "expected ..." half of a
# serde error's message, and no caller of these three reads that message:
# a context, statistics and a plan's root are each refused with one fixed
# `Reason` (`ContextKind`, `NotStats`, `NotAnObject`), and the plan's
# field-level reasons are read from the error's path, so writing nothing
# refuses the same input with the same error.
'stack-encrypt/src/dynamic/(context|query|record)\.rs:\d+:\d+: replace <impl (Visitor<\x27de> for ContextVisitor|serde::Deserialize<\x27de> for Confidence>::deserialize::<impl serde::de::Visitor<\x27_> for V|Deserialize<\x27de> for PlanWire>::deserialize::<impl Visitor<\x27de> for V)>::expecting -> fmt::Result with Ok\(Default::default\(\)\)$',
# The same for the visitor that reads a refused plan's keys back
# (`entry_key`): its error is discarded (`.ok()`), so its message is never
# read at all.
'stack-encrypt/src/dynamic/record\.rs:\d+:\d+: replace entry_key::<impl Deserialize<\x27de> for Keys>::deserialize::<impl Visitor<\x27de> for V>::expecting -> fmt::Result with Ok\(Default::default\(\)\)$',
# The JSON index. `unhex` joins a high nibble shifted into the top four
# bits with a low nibble below 16: the bits never overlap, so `^` is `|`.
'stack-encrypt/src/json/mod\.rs:\d+:\d+: replace \| with \^ in unhex$',
# `JsonPath::root` is `Self::default()`.
'stack-encrypt/src/json/path\.rs:\d+:\d+: replace JsonPath::root -> Self with Default::default\(\)$',
# A document always has a root, so its terms are never empty: `false` is
# the value `is_empty` returns. The `true` replacement is still caught.
'stack-encrypt/src/json/derive\.rs:\d+:\d+: replace JsonTerms::is_empty -> bool with false$',
]

# Headroom over the measured baseline before a slow-but-correct mutant is
Expand Down
6 changes: 6 additions & 0 deletions .changeset/chacha20-0-10-2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@cipherstash/protect-ffi': patch
'@cipherstash/auth': patch
---

Build the native binaries against `chacha20` 0.10.2. Versions 0.10.0 and 0.10.1 were yanked: on an x86 CPU without AVX2, their SSE2 fallback for the random number generator used an SSE4.1 instruction it had not checked the CPU for. A CPU without SSE4.1 (one from before 2008, or a virtual machine using QEMU's default `qemu64` CPU model) would crash with an illegal-instruction error. Every other CPU produced correct random numbers, so randomness was never weakened. No API change.
5 changes: 5 additions & 0 deletions .changeset/eql-bindings-optional-storage-only.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cipherstash/eql": patch
---

`eql-bindings` (the Rust crate released with this package): an EQL v4 type whose terms a null has, a storage-only type (`Date`, `Integer`, `Text`, ...) or one whose only term is equality's (`DateEq`, `IntegerEq`, `TextEq`, ...), now also implements `EncryptFrom` and `DecryptInto` for an `Option` of its plaintext, and its query twin `EncryptFrom` for one. For a type of fixed width a `None` seals as a typed null as long as a value (vitaminc 0.8), so an optional column does not show which rows are absent. For text it seals as a null that is not padded, since text's ciphertext is as long as the text, so its length may show that the value is absent. Either way it opens as `None`; its `hm` is the one term every null shares, and the query of a `None` finds the stored nulls with `=`. A value seals exactly as before. The by-name dispatch (`encryption::targets`) reads, opens and queries these types through the `Option`; a type with an order or match term still refuses a null. eql-bindings' own `Date`, `Timestamp` and `Decimal` plaintexts declare their fixed width to vitaminc, so their `None` is padded too. The SQL bundle and the TypeScript types do not change.
9 changes: 9 additions & 0 deletions .changeset/eql-json-tree-derivable-tokens.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@cipherstash/eql": minor
---

EQL v4 `json_tree` (unreleased): a subtree read sends the token of exactly the node it reads, and returns that node. Every token is now derivable from its node's path alone: an object member's is `H("t" ‖ t(parent) ‖ s)`, with no ordinal, and an array element's keeps its index. Every stored entry gains a self tag `x`, the first eight bytes of `H("x" ‖ iv ‖ t)`, by which the server finds the node a token names; the entry validator requires it, so a document written by the earlier build of this unreleased family is refused. A read of `$.profile.address.city` used to send `$.profile`'s token, a capability over that whole field in every row, and walk to the leaf through gates; it now sends the leaf's token and nothing that covers any node above it.

**SQL signature changes.** `eql_v4.json_tree_subtree(doc, tokens text[], budget integer DEFAULT 64)` replaces `json_tree_subtree(doc, token, budget)` and `json_tree_subtree_at(doc, token, gates, budget)`, which is removed: for each token in order it returns the entry whose `x` matches at `d = 0`, then its descendants with `d` their depth below it, so `{}`, `[]`, a scalar and a missing node are four answers, as `jsonb` gives them (#1356). The budget counts rows across all the tokens. `json_tree_children(doc, token)` and `json_tree_child_count(doc, token)` lose their `gates` argument and take the token of any node; `json_tree_child_count` of a token that names no node is NULL, as `jsonb_array_length` of a missing path is (it was 0), and so is the sealed root's, whose top-level fields chain from nothing. `eql_v4_internal.json_tree_child_token` is replaced by `json_tree_member_token`, `json_tree_element_token`, `json_tree_self_tag`, `json_tree_hit_token` and `json_tree_index`, which maps every entry's `x`, and every chained entry's `p`, to its position in `sv`; `json_tree_by_parent` is removed. The chain primitives carry the `eql-inline-critical` marker, so the post-install step leaves their `search_path` unpinned and the planner inlines them: pinned, every probe of a walk was a full SQL-function call, which was most of a read's cost.

A wildcard (`$.items[*].price`) is expanded by the client into one token per position below a bound, 64 by default, plus a probe at the bound; a row where the probe finds an element fails the read with the bound rather than return part of the list, walked or fallen back to the sealed root. A read of `$` is legal under the sealed root and returns the root entry, which opens to the whole document. Stored documents grow by about a tenth in characters (+9.8% on a nested document, +9.4% on a flat one). `JsonHeader::FORMAT_VERSION` and the document's `v` are unchanged. In `eql-bindings`, `JsonTreeEntry` gains `x` and `JsonTreeSubtree` opens to `Option<Value>`; in Stack Encrypt, `JsonSubtreeKey` carries the token list (`tokens()`, `probes()`, `bound()`), `KeysetCipher::json_subtree_key_with_bound` sets the bound, `Error::JsonWildcardTruncated` and `TermError::JsonWildcardTooWide` are new, and `TermError::JsonSealedRoot` is removed. What the database learns is in `docs/reference/json-tree-leakage.md`; the design and the decisions behind it are in `docs/plans/2026-10-17-json-tree-index.md`, "Design change (2026-10-24)".
7 changes: 7 additions & 0 deletions .changeset/eql-json-tree-domain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@cipherstash/eql": minor
---

**EQL v4 gains the `json_tree` domain family: `public.eql_v4_json_tree`, an encrypted JSON document stored as one entry per node, with `@>` and `<@` that agree with PostgreSQL's own `jsonb @>` across array elements.** The family is new beside `public.eql_v4_json` and is not a port of the v3 SteVec surface: it is what Stack Encrypt's JSON index writes. A document's entries each carry a path selector `s` and a value selector `vs` (containment is a set comparison over the pair), the node's own ciphertext `c` (its label, kind and scalar value; a document's ciphertext is linear in its size, where a SteVec document repeats every subtree at every level), and a token chain (`p`, `w`, `o`) that lets core PostgreSQL walk the subtree under a node from one client-derived token: `eql_v4.json_tree_subtree(doc, token)`, `json_tree_subtree_at(doc, token, selector)`, `json_tree_children` and `json_tree_child_count`. By default the root is sealed, so no query can hand the server a token for the whole column. Containment projects a stored document into an element-nested shape from its group tags (`n`, `g`), `eql_v4.json_tree_query(doc)`, and compares it with the needle (`eql_v4.query_json_tree`) by native `jsonb @>`, so each needle array element must fit inside one stored element, which closes the cross-element false positives of selector-set containment (#1231) for this family; a GIN index on `(eql_v4.json_tree_query(col)::jsonb) jsonb_path_ops` serves it. `->` by selector returns an entry that opens on its own (`public.eql_v4_json_tree_entry`, with the header and ordinal grafted), and `eql_v4.query_json_tree_leaf` orders an extracted number or string leaf with `<`, `<=`, `>`, `>=`. Every other native jsonb operator on the column is blocked, as on the other EQL domains. EQL v4 only (`-- ONLY EQL VERSION: 4`); the v3 bundle is unchanged. The `eql-bindings` crate carries the hand-written `v4::json_tree` types with Stack Encrypt's `EncryptFrom` / `DecryptInto` for the document and the entry.

Why. Stack Encrypt had no JSON index: the operators (`Contains`, `ContainedBy`, `AtPath`) were declared and answered by nothing, and the v4 JSON wire format was unconstrained by anything shipped. This is Phase 1 and 2 of the `json_tree` index's phased plan (#1249), implemented against `docs/plans/2026-10-17-json-tree-index.md`, whose "Implementation notes" section records where the code deviates from the design.
5 changes: 5 additions & 0 deletions .changeset/eql-json-tree-extracted-entry-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cipherstash/eql": patch
---

EQL v4 `json_tree` (unreleased): the client now checks that what `->` and the subtree walks return is what it asked for. Every entry of a stored document is sealed under its own path selector and position, so any entry of the row opened, and a database could answer `doc -> s($.salary)` with the row's `$.bonus`, or with the sealed root (the whole document), or splice other entries into a walk. In the `eql-bindings` crate an entry `->` returned now opens through the selector that asked for it, `JsonTreeExtracted::from_selector(&selector, entry)`, which refuses an entry carrying any other selector; `JsonTreeEntry` no longer opens on its own. A walk's rows, opened through `JsonTreeSubtree::from_key(&key, rows)` as before, are each checked against the key's path: the node a gated walk reaches must carry the path's selector and position, every other row the selector of its parent's path and its own label, and the rows must follow each other in the document with no gap. A refusal is `stack_encrypt::Error::JsonWrongEntry`. Not checked, by design: which row of the column an entry comes from (ADR 0010). The SQL is unchanged.
13 changes: 13 additions & 0 deletions .changeset/eql-json-tree-ordinal-chain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"@cipherstash/eql": minor
---

EQL v4 `json_tree` (unreleased): the token chain is derived by ordinal and the walk probes to the first miss.

A stored entry now carries four fields, `s`, `c`, `p` and `op`. The value selector `vs` lives only in the document's projection `q`, which the engine now reads back as the projection instead of rebuilding it from the entries; the group tags `n` and `g` are gone with it, since nothing read them once `q` was stored. The wrapped token `w` and the ordinal `o` are gone too: a child's token is `H("t" ‖ t(parent) ‖ s ‖ LE32(o))`, `s` being the child's own selector, which the server derives as it walks, and the ordinals under a node are dense from zero, so the walk probes them in turn and stops at the first miss. A subtree of `N` nodes costs about `2N` probes (a SHA-256 and a lookup each) and `N` token hashes whatever the document's width, where it cost a probe for every ordinal up to the document's widest node at every node visited. The entry validator accepts the four fields only, so a document written by the earlier build of this unreleased family is refused.

The client can derive a token only for a chain head and the array positions that directly follow it, since an object member's ordinal is a rank among its siblings' keys. `JsonSubtreeKey::gates()` gives the rest of a path as one gate per step, a selector, or `#n:<selector>` for an array position with the array elements' selector, and `eql_v4.json_tree_subtree_at(doc, token, gates text[], budget DEFAULT 64)` walks them, replacing the earlier `json_tree_subtree_at(doc, token, selector, budget)`. `json_tree_children` and `json_tree_child_count` take the same `gates`, optional and empty by default. A gated walk returns the node the gates reach with `d = 0` and its descendants beneath it, so a wildcard reaches several nodes and opens as a list; `json_tree_subtree(doc, token, budget)` is now `json_tree_subtree_at` with no gates and still returns the descendants of the token's node from `d = 1`. Past the budget a walk still returns the sealed root alone, with `j = 0` and `d = 0`. `eql_v4_internal.json_tree_unwrap`, `json_tree_xor16` and `json_tree_fanout` are removed; `eql_v4_internal.json_tree_child_token(token, s, o)` is added. Binding the child's selector into its token, and the element selector into a position gate, is what makes a position name an array element only: an ordinal alone is an element's position or an object member's rank, so in a row whose node at the path is an object, `$.a[0]` would otherwise reach the member that ranks first, where `jsonb` has no node.

**Byte encodings.** Every field the SQL compares, derives or decodes is padded standard base64, as PostgreSQL's `encode(x, 'base64')` writes it: the selectors `s`, the `s` and `vs` pairs of `q`, the parent tag `p`, the ordering term `op`, and the tokens and gates `json_tree_subtree_at`, `json_tree_children` and `->` take, where they were hex; the header `h` was base64 already. The ciphertext `c`, which no SQL function decodes, is RFC 1924 base85. The parent tag `p` is the first eight bytes of its hash, since it is a lookup key within one document and nothing authenticates through it. `eql_v4_internal.json_tree_ope_cllw` is added for `eql_v4.ord_term`, because the `ope_cllw(jsonb)` the scalar types share reads hex. A document written by the earlier build of this unreleased family is refused by the engine. In `eql-bindings` each json_tree wire type names its encoding, holding an `encoding::Base64Text` or, for `c`, an `encoding::Base85Text`; the wire form is the same string. The encoding type guarantees its text is in that encoding: a value is made only by `encode`, from bytes, or by deserializing, which decodes the text and refuses text that is not in the encoding, so a payload with a field in the wrong encoding fails to deserialize. Re-encoding 1,000 stored rows of each benchmark corpus: a nested document measures 22% smaller on disk (`pg_column_size` 40,808 to 31,894 bytes) and a flat one 12% (1,596 to 1,398).

Measured on 10,000 and 100,000 rows of a nested corpus (about 213 entries a document), query-only medians: subtree walks 30 to 55% faster (a 122-entry subtree walked out, 23 ms to 11 ms), `json_tree_children` and `json_tree_child_count` 6 to 9%, a stored nested document 25% smaller (heap 810 MB to 621 MB at 10,000 rows), containment and field queries unchanged within the run's noise.
5 changes: 5 additions & 0 deletions .changeset/eql-json-tree-planner-and-leakage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cipherstash/eql": patch
---

EQL v4 `json_tree` (unreleased): the reference docs gain "What an encrypted JSON tree reveals" (`docs/reference/json-tree-leakage.md`), one page on what a stored row shows the database at rest (including the row's shape, which the stored depth-first entry order gives away), what each query type hands it, what is and is not authenticated, and the side-by-side with v3 `ste_vec`. The encryption test suite now holds the planner to the GIN and btree indexes with every setting at its default, on inline and TOASTed documents, for one-shot and prepared statements, and CI runs that suite on every supported PostgreSQL version.
Loading
Loading