Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7a9ef7c
feat(eql-bindings): EQL types as plan field targets, dispatched by name
coderdan Oct 6, 2026
2480dc3
docs(eql-bindings): document EQL types as plan field targets
coderdan Oct 6, 2026
ba6a32d
feat(stack-encrypt)!: a data plan field may name an EQL type as its t…
coderdan Oct 6, 2026
fae8568
test(stack-encrypt): the adapter tests pass the target slot list
coderdan Oct 6, 2026
7987a2f
feat(golang): the stack-encrypt guest's eql build, and se_query
coderdan Oct 6, 2026
e235774
feat(golang): encrypt_into=TextEq through the generated API, and encr…
coderdan Oct 6, 2026
0e94d76
docs: EQL types as plan field targets, across the Go SDK and the plan
coderdan Oct 6, 2026
9c8f440
docs(golang): the guest targets module links resolve in both feature …
coderdan Oct 6, 2026
e9993b6
style(eql): rustfmt the EQL workspace
coderdan Oct 6, 2026
31d2013
test(stack-encrypt): pin with_target's segment bound from both sides
coderdan Oct 6, 2026
b3d22ca
fix(eql-bindings): open a target through decrypt_as, which crates.io …
coderdan Oct 6, 2026
3459f07
test(scripts): the eql guest's Cargo.lock now records eql-bindings
coderdan Oct 6, 2026
8e67d88
test(stack-encrypt): pin TargetDescriptor's Display; exclude NoTarget…
coderdan Oct 6, 2026
7005882
fix(stack-encrypt)!: a target field's label is refused when it is not…
coderdan Oct 6, 2026
25d3cee
test(stack-encrypt): a keyset scope refuses a target value from anoth…
coderdan Oct 6, 2026
1e5d524
test(eql-bindings): a keyset opener refuses a value from another keyset
coderdan Oct 6, 2026
3795827
fix(eql-bindings): a target's plaintext copy is wiped when it is dropped
coderdan Oct 6, 2026
06b526f
test(golang): the status table pins both Error::Target arms
coderdan Oct 6, 2026
f3a3c6f
fix(eql-codegen): a producible type without a query twin gets no quer…
coderdan Oct 6, 2026
a8798fe
test(stack-encrypt): the eql node's shape, and a target slot the reso…
coderdan Oct 6, 2026
165c390
test(stack-encrypt): fuzz check_record under a resolver that holds Te…
coderdan Oct 6, 2026
afbccc0
fix(golang): a program that links encrypt/eql never falls back to the…
coderdan Oct 6, 2026
e007c94
test(golang): host refusals around a target field, and a tampered EQL…
coderdan Oct 6, 2026
0f0e55d
test(golang): the generator's kind check and Go-name rule against the…
coderdan Oct 6, 2026
a223e1f
chore(golang): adopt #1094's third-review changes in the EQL build
coderdan Oct 7, 2026
687b36c
docs(golang): eql names the interfaces its types implement, not libra…
coderdan Oct 7, 2026
0049376
chore(golang): regenerate contact_stash.go for the request-count comment
coderdan Oct 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .cargo/mutants.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ exclude_re = [
'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().
'stack-encrypt/src/target/context\.rs:\d+:\d+: replace <impl From<\(\)> for (DeclaredContext|ExpectedContext<T>)>::from -> Self with Default::default\(\)$',
# The resolver of a build without EQL types holds none: its `targets` IS
# 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!\[\]$',
]

# Headroom over the measured baseline before a slow-but-correct mutant is
Expand Down
5 changes: 5 additions & 0 deletions .changeset/eql-plan-field-targets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@cipherstash/eql': minor
---

**The `eql-bindings` crate resolves an EQL type named as a string to its own Stack Encrypt plan** (`stack-encrypt` feature). `eql_bindings::encryption::targets` carries a catalog-generated table of every EQL type a data plan may name as a field target — its name across languages, family and suffix, the plaintext `ValueKind` it takes, the indexes it carries, its query twin, and whether the engine can produce it today with the reason when not — and `encrypt` / `decrypt` / `query` entry points that dispatch on the name and run the type's derived `EncryptFrom` / `DecryptInto`, resolving to the EQL value's JSON bytes through the engine's `Pending` so a guest batches it with the rest of a plan. This is the EQL half of EQL types as plan field targets (cipherstash/stack#1062): a Go data plan names `TextEq` and the guest returns the finished EQL value instead of assembling one. `TextEq` is the only producible type; every other name is refused with the plan's reason. The SQL surface and the TypeScript package are unchanged.
33 changes: 28 additions & 5 deletions .github/workflows/tests-golang.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ name: Tests (Go)
# wasi-check the stack crates build for wasm32-wasip1 with no JS-host or
# native-HTTP dependencies, the no-http shape passes its tests
# and docs, both guests pass lint and tests and are built with
# their import surfaces checked (the stack-encrypt guest twice:
# the real build and the deterministic-kms test build), their
# their import surfaces checked (the stack-encrypt guest four
# times: the real build, the build with the EQL types, and the
# deterministic-kms test build of each), their
# sha256 is recorded, `go:test` runs against them, and
# `go generate` leaves the tree unchanged.
# go-lint golangci-lint, Linux only.
Expand All @@ -32,6 +33,9 @@ on:
- packages/stack-encrypt/**
- packages/stack-encrypt-derive/**
- packages/stack-guest-abi/**
# The eql guest build links eql-bindings.
- packages/eql/crates/eql-bindings/**
- packages/eql/crates/eql-domains/**
- languages/golang/**
- scripts/check-wasm-imports.py
- scripts/go-binding-test.sh
Expand All @@ -54,6 +58,9 @@ on:
- packages/stack-encrypt/**
- packages/stack-encrypt-derive/**
- packages/stack-guest-abi/**
# The eql guest build links eql-bindings.
- packages/eql/crates/eql-bindings/**
- packages/eql/crates/eql-domains/**
- languages/golang/**
- scripts/check-wasm-imports.py
- scripts/go-binding-test.sh
Expand Down Expand Up @@ -156,6 +163,18 @@ jobs:
- name: stack-encrypt guest deterministic test build
run: mise run wasm:guest:build:deterministic

# The build with the EQL types (ADR-0007, amended 2026-10-06), which
# package encrypt/eql embeds and registers on import, and its
# deterministic test build, which the hermetic tests of a TextEq field
# load. Same import-surface gate. The build task prints both builds'
# sizes: the plan asks for that measurement before the SDK settles on
# one build or two.
- name: stack-encrypt guest eql build and import-surface gate
run: mise run wasm:guest:build:eql

- name: stack-encrypt guest eql deterministic test build
run: mise run wasm:guest:build:eql:deterministic

- name: Credential guest lint and tests
run: mise run wasm:auth-guest:test

Expand All @@ -167,7 +186,7 @@ jobs:
# same bytes rather than a stale or rebuilt guest.
- name: Record the guests' checksums
run: |
for guest in encrypt/wasm/stack_encrypt_guest.wasm encrypt/testdata/stack_encrypt_guest_deterministic.wasm auth/wasm/stack_auth_guest.wasm; do
for guest in encrypt/wasm/stack_encrypt_guest.wasm encrypt/testdata/stack_encrypt_guest_deterministic.wasm encrypt/eql/wasm/stack_encrypt_guest_eql.wasm encrypt/testdata/stack_encrypt_guest_eql_deterministic.wasm auth/wasm/stack_auth_guest.wasm; do
(cd languages/golang && openssl dgst -sha256 "$guest" | awk '{print $NF}' > "$guest.sha256" && echo "$guest sha256 $(cat "$guest.sha256")")
done

Expand All @@ -180,6 +199,10 @@ jobs:
languages/golang/encrypt/wasm/stack_encrypt_guest.wasm.sha256
languages/golang/encrypt/testdata/stack_encrypt_guest_deterministic.wasm
languages/golang/encrypt/testdata/stack_encrypt_guest_deterministic.wasm.sha256
languages/golang/encrypt/eql/wasm/stack_encrypt_guest_eql.wasm
languages/golang/encrypt/eql/wasm/stack_encrypt_guest_eql.wasm.sha256
languages/golang/encrypt/testdata/stack_encrypt_guest_eql_deterministic.wasm
languages/golang/encrypt/testdata/stack_encrypt_guest_eql_deterministic.wasm.sha256
languages/golang/auth/wasm/stack_auth_guest.wasm
languages/golang/auth/wasm/stack_auth_guest.wasm.sha256
if-no-files-found: error
Expand Down Expand Up @@ -290,7 +313,7 @@ jobs:
- name: The guests are the ones Linux built and checked
run: |
cd languages/golang
for guest in encrypt/wasm/stack_encrypt_guest.wasm encrypt/testdata/stack_encrypt_guest_deterministic.wasm auth/wasm/stack_auth_guest.wasm; do
for guest in encrypt/wasm/stack_encrypt_guest.wasm encrypt/testdata/stack_encrypt_guest_deterministic.wasm encrypt/eql/wasm/stack_encrypt_guest_eql.wasm encrypt/testdata/stack_encrypt_guest_eql_deterministic.wasm auth/wasm/stack_auth_guest.wasm; do
want=$(cat "$guest.sha256")
got=$(openssl dgst -sha256 "$guest" | awk '{print $NF}')
if [ "$want" != "$got" ]; then
Expand Down Expand Up @@ -356,7 +379,7 @@ jobs:
- name: The guests are the ones the wasi-check job built and checked
run: |
cd languages/golang
for guest in encrypt/wasm/stack_encrypt_guest.wasm encrypt/testdata/stack_encrypt_guest_deterministic.wasm auth/wasm/stack_auth_guest.wasm; do
for guest in encrypt/wasm/stack_encrypt_guest.wasm encrypt/testdata/stack_encrypt_guest_deterministic.wasm encrypt/eql/wasm/stack_encrypt_guest_eql.wasm encrypt/testdata/stack_encrypt_guest_eql_deterministic.wasm auth/wasm/stack_auth_guest.wasm; do
want=$(cat "$guest.sha256")
got=$(openssl dgst -sha256 "$guest" | awk '{print $NF}')
if [ "$want" != "$got" ]; then
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,10 @@ mutants.out/
# The Go module's embedded WASI guests: build outputs of `mise run
# wasm:guest:build` and `mise run wasm:auth-guest:build`.
languages/golang/encrypt/wasm/*.wasm
languages/golang/encrypt/eql/wasm/*.wasm
languages/golang/auth/wasm/*.wasm
languages/golang/encrypt/wasm/*.sha256
languages/golang/encrypt/eql/wasm/*.sha256
languages/golang/auth/wasm/*.sha256
# The deterministic-kms TEST build of the stack-encrypt guest, from `mise run
# wasm:guest:build:deterministic`; under testdata so no binary embeds it.
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. Generated `*_stash.go` files are committed and CI fails when `go generate ./...` changes one. There is no Go release process yet.
- `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. There is no Go release process yet.
- `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).
Expand Down
3 changes: 3 additions & 0 deletions docs/plans/2026-10-04-plan-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,9 @@ The ciphertext inside it is a Stack Encrypt ciphertext, which starts with `stack
"EQL v4" in this plan is the name of that form, and not a new envelope.

The engine produces one EQL type today: `TextEq`.
Status (2026-10-06, #1062): `TextEq` is producible through the data plan's target form.
`stashgen` accepts `encrypt_into=TextEq`, the guest build with the EQL types returns the finished value, and `encrypt/eql` holds the generated Go types; every other type is listed by `se_targets` with the reason it is not producible, and `stashgen` refuses it.
Open question for Dan: an EQL value is stored under a table and a column, so a target field's label must be exactly `<table>/<column>`; a cipher extended with a tenant part has no column for the extended label, and a plan with a target field refuses the extension rather than dropping it.
The other types wait for work in the engine:

- **Every family but `Text`:** how the family encodes a plaintext is not specified.
Expand Down
19 changes: 11 additions & 8 deletions languages/golang/cmd/stashgen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ Your program calls those functions, and it never builds or names a plan.
type User struct {
_ struct{} `stash:"context=users"`
ID int64 `stash:"id,passthrough"`
Email string `stash:"email,encrypt,index=equality;match"`
Name string `stash:"name,encrypt"`
Email string `stash:"email,encrypt_into=TextEq"`
Name string `stash:"name,encrypt_into=TextEq"`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change before merge: this example gives Name an equality index, so a developer who copies it reveals which rows share a name.

Impact: Before this PR, this line was stash:"name,encrypt", with no index. The paper found that developers copy the first example as it is. With TextEq, each row stores an equality term for Name, and rows with the same name have the same term. The example never searches by name, so the index adds exposure and no feature.

Evidence: The TextEq doc says only "the text family with the eq index" (encrypt/eql/eql_gen.go:1361). The engine's docs say "equal values are visibly equal" (packages/stack-encrypt/src/sem/mod.rs:71). No text at the point of choice says what TextEq reveals. One struct can hold both kinds of field: testusers.Contact has an encrypt_into=TextEq field and an encrypt field.

Fix: Keep Name without an index:

	Email string   `stash:"email,encrypt_into=TextEq"`
	Name  string   `stash:"name,encrypt"`

Then add one line about TextEq below the tag table:

`TextEq` stores an equality term beside the ciphertext, so rows with the same value have the same term. Use it only for a field that a query searches by equality.

Found by 1 model: claude

}
```

Expand All @@ -40,12 +40,11 @@ Your program calls those functions, and it never builds or names a plan.
```go
encrypted, err := users.Encrypt(ctx, cipher, people)
opened, err := users.Decrypt(ctx, cipher, encrypted)
term, err := users.Fields.Email.Equality(ctx, cipher, "bob@example.com")
query, err := users.Fields.Email.Query(ctx, cipher, "bob@example.com")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional: one equality search has two method names, Equality for separate columns and Query for an EQL field.

Impact: A developer who moves a field from index=equality to encrypt_into=TextEq must rename every call. The two names also suggest two operations, but both make an equality search value for one field. The paper found that names which match what they do help a developer write correct code.

Evidence: Step 5 of encrypt/README.md calls users.Fields.Email.Equality. This step calls users.Fields.Email.Query. "What stashgen writes" says that an entry has Query for encrypt_into, and Equality, Match, Ore or Ope for index=.

Fix: Name the EQL method after the search it makes, for example Equality, which returns an eql.TextEqQuery. Or keep Query, and say in "What stashgen writes" why the two names are different.

Found by 1 model: claude

```

6. Store the encrypted type.
Each sealed field is one or more byte columns: `Email.Ciphertext`, `Email.Equality`, `Email.Match`.
`encrypt.Ciphertext` and each term type implement `driver.Valuer` and `sql.Scanner`, so a database library binds and scans each one as bytes; map each one to its own column.
Each `encrypt_into` field is one EQL column: `eql.TextEq` implements `driver.Valuer` and `sql.Scanner`, so a database library binds and scans it as the JSON a `public.eql_v3_text_eq` column holds.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional: the docs do not say how to move a field from separate columns to one EQL column.

Impact: Before this PR, this step stored Email.Ciphertext, Email.Equality and Email.Match as separate byte columns. A developer who changes the tag to encrypt_into=TextEq gets a different generated type and a different column type. The new code cannot read the rows that the old code stored, and no text says what to do with them.

Evidence: This PR replaces the separate-column text of this step with the EQL text. EncryptedUser.Email becomes an eql.TextEq, and public.eql_v3_text_eq is a different column type from bytea.

Fix: Add a short section that says how to move stored rows to the new column. For example: decrypt each row with the old generated code, encrypt it with the new code, and write the new column.

Found by 1 model: claude


7. Run the generator again after each change to the struct or to a tag.
A change to the fields of the struct stops the build until you do.
Expand Down Expand Up @@ -140,10 +139,14 @@ It ignores its own output file when it loads the package, so a stale file does n
The same input always gives the same file: fields keep their declared order, and the file carries no version and no time.

`stashgen` checks each declaration with the engine, and holds no copy of the engine's rules: it runs the WASI guest the SDK embeds and asks it, one field at a time, so the error names the field.
It asks the engine for the EQL types it holds: each name, its plaintext type, its indexes and its query form.
This build of the engine produces no EQL type, so `encrypt_into` is refused with "EQL types are not available yet"; the next release adds `TextEq` and `encrypt/eql`.
It asks the engine for the EQL types it holds: each name, its plaintext type, its indexes, its query form, and whether the engine produces it today.
The command links the build of the engine that holds the EQL types (`encrypt/eql`), so it can answer for every type; a generated file imports `encrypt/eql` only when it names one.
The engine produces `TextEq` today; `encrypt_into` with any other type is refused with the type's name.
Separate columns work today for four indexes: `equality`, `match`, `ore` and `ope`.

A field with `encrypt_into` is stored under its table and column, which is what an EQL value records in its `i`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix in a follow-up: this line names the column that i records, but not that nothing binds the value to its row.

Impact: A developer can read i as an identity that ties the value to its row. decrypt checks i against the expected column, so a value moved to another column fails. A value moved to another row of the same column decrypts there with no error. The "Take care" note in "The cipher" in encrypt/README.md says this for all fields, but this section does not point to it.

Evidence: A reviewer swapped the Email values of two testusers.Contact rows at 0049376. DecryptContact returned each row with the email of the other row, with no error.

Fix: Add this sentence after this line:

`i` does not name the row, so a value copied to another row of the same column decrypts there with no error. See "The cipher" in `encrypt/README.md`.

Found by 1 model: claude

A cipher extended with a tenant part (`cipher.Extend(...)`) has no column for the extended label, so it refuses a struct with an `encrypt_into` field; use `index=` columns for a tenant-extended struct until that rule is settled.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change before merge: tenants that share a keyset share each equality term, and this line does not say so.

Impact: This line tells a developer with tenant-extended data to use index= columns. A developer can instead remove Extend to use encrypt_into. Then each tenant on the same keyset derives the same hm for the same value. A database reader can link one email across tenants, and match one tenant's query value against another tenant's rows. With index= columns, Extend(tenant) keeps those terms apart.

Evidence: A reviewer ran this at 0049376 with NewDeterministicEQLClient, the deterministic test build. Two TextEq values of bob@example.com, encrypted through DefaultKeyset() for two tenants, had the same hm and a different c. Query values under the keysets tenant-a and tenant-b had different hm.

Fix: Add this note after this line:

> **Take care**
>
> Without `Extend`, every tenant on one keyset derives the same equality term for the same value. A person who can read the database can then see that two tenants hold the same value. To keep tenants apart with `encrypt_into`, give each tenant its own keyset: `client.Keyset(encrypt.KeysetName(tenant))`.

Found by 1 model: claude


## When stashgen stops

`stashgen` stops with an error, and writes no file, for each of these.
Expand Down Expand Up @@ -216,5 +219,5 @@ A protobuf message with a `oneof` cannot be generated from a policy: protoc-gen-

## Status

The command runs the WASI guest the SDK embeds, so it needs the guest built: `mise run wasm:guest:build`.
The command runs the WASI guest the SDK embeds, so it needs the guests built: `mise run wasm:guest:build wasm:guest:build:eql`.
The library, `github.com/cipherstash/stack/languages/golang/stashgen`, takes any `Engine`; `stashgen.Generate` takes one with `WithEngine`, and `stashgen/enginetest` has a static one for tests.
5 changes: 5 additions & 0 deletions languages/golang/cmd/stashgen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ import (
"os"
"strings"

// The build of the engine that holds the EQL types: the generator
// asks the embedded guest which EQL types it produces, so it links the
// build that has them. Generated code imports encrypt/eql only when it
// names one.
_ "github.com/cipherstash/stack/languages/golang/encrypt/eql"
"github.com/cipherstash/stack/languages/golang/stashgen"
)

Expand Down
Loading
Loading