Repository navigation
Go and Rust plans cannot target EQL domains, so Go writes rows EQL cannot query — make domains plan field targets #1062
Description
Updated 2026-10-05 (PDT). The Go SDK design is PR cipherstash/stack#1070:
docs/plans/2026-10-04-plan-builder.md, "The Go SDK", and the principles indocs/sdk-design-principles.md(ADR-0008). Dan's "EQL Plans in Go" proposal is taken. Item 2 keeps this issue's shape:eql-codegenemits a dispatch table besideinventory.rs, the plan grammar gains atargetfield form, and the guest build that holds the EQL types runs the Rust-derived plan for the named type. The Go packageencrypt/eqlholds generated structs that store the EQL value the guest returns, and it embeds that guest build. Item 3: the envelope is decided. A Stack Encrypt EQL value has the EQL v3 envelope (v: 3,eql_v3domains) with a ciphertext prefixedstack-encrypt:1:; "EQL v4" names that form. Item 5: the record fixture guards the bytes (each side opens the other's records, same term bytes); there is no Go encoder to fixture. The engine producesTextEqonly today;stashgenrefuses the other types until the engine can produce them. The size of the guest build with the EQL types is measured before the SDK ships two builds or one.
Background
EQL (Encrypt Query Language) is our PostgreSQL library: SQL we install into a customer database that stores and queries encrypted payloads. Each EQL domain (TextEq, IntegerOrd, Json, …) fixes the JSON shape a column stores: a version, the column identifier i ({"t": table, "c": column}), the ciphertext, and the search terms that domain needs. packages/eql/crates/eql-bindings/ holds the Rust types for those domains; PR cipherstash/stack#971 added text encryption and queries there through stack-encrypt, our Rust field-level encryption library.
The plan builder (docs/plans/2026-10-04-plan-builder.md, PR cipherstash/stack#1052) lets a field's target be any EncryptFrom type. So an EQL domain can be a plan field target with no builder work.
Problem
- Rust: there is no way to say "this plan field is stored as EQL
TextEq" inside a plan. - Go: the binding's record output is its own map (
{"c","eq","match","ore","ope"}), which is not an EQL payload, so Go cannot write rows EQL can query. There are no Go domain types. - Name lookup: a binding sends a domain name as data. Nothing maps that name to the Rust type, and inferring the domain from a payload's keys would be unsafe (two domains can share keys).
Proposal
From the plan doc's "EQL v3 domains as field targets" section. Not yet verified by implementation.
- Rust:
encrypt_into::<Domain>(name)on the fields chain, for anyEncryptFromtype, using<Domain as EncryptFrom<S>>::encryption()as that field's description. - A name-to-type table in
eql-bindings: an explicit lookup from domain name to type, never inferred from payload keys. It lives ineql-bindingsso every binding reuses it. - Go: a package generated by
eql-codegenintolanguages/golang, inside the one Go module and the one guest, with every domain, and constructors only for domains that have Rust derives. Module size is measured and reported in the PR. - A domain wraps an index's output for EQL's wire format. The index stays in stack-encrypt. For example the JSON domain frames the
Jsonindex's document (#1060) into EQL's JSON envelope the wayTextEqframes an equality term. - Remove the Go record output map (
c/eq/match/ore/ope) once domains are field targets.
Naming note: the plan doc calls the generated Go package eqlv3 and the section "EQL v3 domains". Payloads emitted by stack-encrypt are intended to be EQL v4. The generated package name and the envelope version must follow the EQL v4 envelope work, and the plan doc's eqlv3 naming is to be corrected.
Relationship to other work
- Blocked by #1057 (the plan builder), #1046 (the Go chain), and the EQL v4 envelope work (the version and shape of the payload stack-encrypt emits).
- Blocks #1065 (TypeScript on stack-encrypt).
- Supersedes the "EQL v3 outputs as
EncryptAstargets" section previously in #1046. - Design:
docs/plans/2026-10-04-plan-builder.mdon PR cipherstash/stack#1052.
Activity
coderdan commentedon Oct 5, 2026
Updated direction (recorded in the plan on #1052): the EQL typed verb,
encrypt_into::<TextEq>("email"), is Rust-only. It works through the EQL type's ownEncryptFrom, so there is no registry and no domain or target name in the data grammar, and the WASI guest stays EQL-free. Go assembles EQL types from the engine's standard outputs (ciphertext, terms) in code that the generator in #1070 writes. A cross-language fixture guards the encoding, which then exists in both Rust and Go.docs(plans): apply the SDK principles to the Go SDK design
docs(plans): apply the SDK principles to the Go SDK design
docs(plans): apply the SDK principles to the Go SDK design
feat(eql-bindings): EQL types as plan field targets, dispatched by name
feat(golang): encrypt_into=TextEq through the generated API, and encr…
feat(eql-bindings): EQL types as plan field targets, dispatched by name
feat(golang): encrypt_into=TextEq through the generated API, and encr…
feat(eql-bindings): EQL types as plan field targets, dispatched by name
feat(golang): encrypt_into=TextEq through the generated API, and encr…
coderdan commentedon Oct 7, 2026
Follow-ups for the EQL types this issue left unproducible (only
TextEqis producible today): #1130 (non-text families: plaintext encoding not specified), #1131 (TextMatch,TextSearch,TextOrd,TextOrdOpe), #1132 (TextOrdOre,TextSearchOre: block ORE vs CLLW ORE) and #1133 (storage-onlyText).SteVecDocumentwaits on #1060.