Skip to content

feat(cli): add the stash-sql and stash-edge skills — raw-SQL predicates and the WASM entry#777

Open
coderdan wants to merge 1 commit into
remove-v2from
feat/stash-sql-edge-skills
Open

feat(cli): add the stash-sql and stash-edge skills — raw-SQL predicates and the WASM entry#777
coderdan wants to merge 1 commit into
remove-v2from
feat/stash-sql-edge-skills

Conversation

@coderdan

@coderdan coderdan commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Closes #754.

Targets remove-v2 (#772), not main — so nothing here references the EQL v2 surface. Rebased onto a16302fc; see Alignment with the v2 removal for what that changed.

Problem

There is a stash-drizzle, a stash-supabase, and a stash-dynamodb. Nothing covered the integrations that don't use an ORM — hand-written SQL over pg / postgres-js, and the WASM entry on Deno / Supabase Edge Functions / Workers.

Grepping the skills stash init installs for postgres-js|::jsonb::eql|sql.json|query_text_search|query_numeric_ord|eql_v3.matches( returns one hit across all of them, in an unrelated code comment. An integration on that path had to recover the binding surface from dist/*.d.ts JSDoc, the Postgres catalog, and experiment.

Two skills, not one

Per the scope question in the issue: the raw-SQL predicate cookbook serves a supported plain-Node path (hono-pg — no Deno, no WASM, same binding problem), so scoping it under an edge-named skill would leave that surface uncovered. Same reasoning that split stash-indexing out rather than folding it into stash-encryption.

skills/stash-sql/SKILL.md (367 lines) — the column-domain × query-domain operator matrix, storage-vs-query payload shapes, per-driver parameter binding, and recipes for equality / free-text / range / ORDER BY / JSON containment / JSON field selectors, plus the information_schema drift check.

skills/stash-edge/SKILL.md (398 lines) — import specifier per runtime, the four mandatory CS_* variables and minting them with stash env, how the WASM client surface differs from the native one, and why a schema module can't be shared across entries.

Both are single self-sufficient files, required for the Codex/editor-agent inline path.

Verified against a live install, not transcribed

I had a Supabase stack with EQL v3 3.0.2 running, so the predicate matrix is extracted from pg_operator rather than inferred, and the binding rules were tested. Three claims came out differently than the issue recorded:

  • The binding rule is driver-specific, not universal. On postgres-js a bare object fails to INSERT (invalid input syntax for type json) and JSON.stringify(...)::jsonb fails the domain CHECK; only sql.json() works in both positions. On pg, all three forms work. "Bind as an object" is postgres-js guidance, so the skill splits it per driver.
  • A bare-jsonb operand does not fall through to native jsonb semantics. EQL defines jsonb overloads that coerce to the storage domain — eql_v3.eq_term(b::public.eql_v3_text_search) — which requires the ciphertext key c. A ciphertext-free query term without the domain cast therefore raises a CHECK violation rather than silently matching nothing. Corrected in three places where I'd first written the issue's version.
  • The schema type incompatibility reproduces in both directions, not just one. The column classes carry private fields, so TypeScript compares them nominally: Types have separate declarations of a private property 'columnName'. That rules out "author against wasm-inline and share" as a fix — the guidance is to author against exactly one entry. Re-verified on this branch after feat(stack)!: EQL v3 audit-on-decrypt + collapse EncryptionV3 into Encryption (EQL v2 removal PR 3) #768 collapsed the client factories.

The double-encoding failure also gets a one-line diagnosis it didn't have: SELECT jsonb_typeof($1::jsonb) returns 'string', and every EQL domain CHECK opens with jsonb_typeof(VALUE) = 'object' — which is why the error names neither JSON nor encoding.

Two bugs found along the way

  • packages/stack/src/wasm-inline.ts — the module JSDoc passed OidcFederationStrategy.create(...)'s Result straight to config.authStrategy without unwrapping. That is the same JSDoc the issue names as the reverse-engineering source, so it was actively teaching the mistake. Fixed there and written correctly in the skill.
  • The npm:@cipherstash/stack@^1.0.0/wasm-inline form I first wrote would not resolve — semver ranges exclude prereleases, and the published version is 1.0.0-rc.4. The skill pins exactly and explains why.

Alignment with the v2 removal

Rebased from main onto remove-v2 so the new skills describe only the post-removal surface. What that changed:

  • EncryptionV3 is now a deprecated alias of Encryption (feat(stack)!: EQL v3 audit-on-decrypt + collapse EncryptionV3 into Encryption (EQL v2 removal PR 3) #768), so stash-edge's "use Encryption, not EncryptionV3" contrast was stale. Replaced with the distinction that actually survives: both entries name the factory Encryption, so the import path is the only thing separating them — which is a better warning anyway, since the two take different config and different bulk shapes.
  • Schema-authoring references point at @cipherstash/stack/v3, matching stash-encryption on this branch.
  • Conflict in stash-encryption's subpath table resolved in favour of remove-v2's rows (@cipherstash/stack-drizzle at root, encryptedSupabase, the v3-only DynamoDB note), keeping only my wasm-inline addition.
  • Audited every added line: no eql_v2, EncryptionV3, encryptedColumn, encryptedSupabaseV3, or stack-drizzle/v3 reference is introduced.

Folded into existing skills

  • stash-cli — the credential-identity rule under env and under encrypt backfill, where it actually bites (backfill from a laptop, query from an Edge Function → every row decrypts, no search ever matches). Backfill is version-aware, not v2-only, so this applies to the v3 columns these skills are about.
  • stash-supabase — same rule, plus pointers to both new skills.
  • stash-encryption — the entry table said the WASM entry has "the v3 authoring surface", which reads as though a schema can be shared. Corrected, with an [!IMPORTANT] explaining the nominal-typing mechanism and why as any is the wrong fix.
  • stash-drizzle, stash-prisma-next — cross-links for the raw-SQL escape hatch (db.execute(sql\…`)` bypasses the operators those integrations emit).
  • stash-indexing — its "Raw SQL / plain PostgreSQL" bullet now points somewhere.

Wiring

SKILL_MAP (CLI + wizard) installs both for postgresql and supabase — Supabase because Edge Functions are the flagship WASM-entry use and its migrations/RPC are hand-written SQL. Drizzle and Prisma Next get cross-links instead, since they emit correctly-typed operands themselves. SKILL_PURPOSES entries added (covered by the existing "purpose line for every installable skill" test), plus new SKILL_MAP assertions.

Verification

On the rebased base:

  • pnpm run code:check — 0 errors, 196 warnings, matching the remove-v2 baseline in feat!: remove EQL v2 repo-wide (umbrella, #707) #772.
  • pnpm --filter stash test — 841 passed; pnpm --filter @cipherstash/wizard test — 265 passed.
  • Both skills confirmed present in packages/cli/dist/skills/ after build.
  • Every command and flag referenced resolves against stash manifest --json.

Not done

The schema non-interchange is documented here rather than fixed, per the issue's scoping ("worth stating explicitly either way").

Correction to an earlier version of this section: it suggested dropping private on columnName / definition to restore structural compatibility across the two .d.ts bundles. That is the wrong fix, and the code says so directly (packages/stack/src/eql/v3/columns.ts:426-433):

the private definition field carries the literal eqlType/castAs/capabilities, so two otherwise-empty subclasses (e.g. EncryptedBooleanColumn and EncryptedDateColumn, both storage-only) are NOT mutually assignable. This nominality is what keeps plaintext inference precise.

Those privates are load-bearing. Removing them would make storage-only domains mutually assignable and degrade the very inference the v3 surface exists to provide — trading a papercut for a real regression.

It is also not an isolated papercut. #780's Not in this PR section defers a second symptom of the same root cause: encryptQuery in the published .d.ts collapses its plaintext parameter to never under private-field type erasure. Reproduced against a fresh dist/ build:

TS2345: Argument of type '"alice@example.com"' is not assignable to parameter of type 'never'.

Both symptoms share a cause — the v3 column classes' type identity does not survive .d.ts emission intact — and both are invisible to CI for the same reason: the type tests import source, not dist/. Whatever the fix turns out to be, a dist/-consuming type test is the thing that would have caught either one. Worth scoping the two together rather than separately.

@coderdan
coderdan requested a review from a team as a code owner July 24, 2026 07:24
@changeset-bot

changeset-bot Bot commented Jul 24, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3895d94

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 11 packages
Name Type
stash Minor
@cipherstash/wizard Minor
@cipherstash/stack Minor
@cipherstash/basic-example Patch
@cipherstash/e2e Patch
@cipherstash/bench Patch
@cipherstash/prisma-next Minor
@cipherstash/stack-drizzle Minor
@cipherstash/stack-supabase Minor
@cipherstash/test-kit Patch
@cipherstash/prisma-next-example Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds stash-sql and stash-edge skill documentation, expands CLI and wizard installation mappings, documents credential and schema constraints, updates related skill references, and corrects the WASM authentication example.

Changes

SQL and edge integration guidance

Layer / File(s) Summary
Raw SQL predicate and binding guidance
skills/stash-sql/SKILL.md, skills/stash-drizzle/SKILL.md, skills/stash-prisma-next/SKILL.md, skills/stash-indexing/SKILL.md, skills/stash-encryption/SKILL.md
Documents EQL v3 domains, operators, driver-specific JSON binding, SQL recipes, decryption, indexing, and troubleshooting.
WASM edge runtime and credential contract
skills/stash-edge/SKILL.md, skills/stash-cli/SKILL.md, skills/stash-supabase/SKILL.md, packages/stack/src/wasm-inline.ts
Documents WASM runtime setup, explicit credentials, credential identity, client differences, schema isolation, edge querying, and Result unwrapping.
Skill installation and repository guidance
packages/cli/src/commands/init/lib/install-skills.ts, packages/wizard/src/lib/install-skills.ts, packages/cli/src/commands/init/lib/setup-prompt.ts, packages/cli/src/commands/init/lib/__tests__/install-skills.test.ts, .changeset/stash-sql-edge-skills.md, AGENTS.md
Adds the skills to integration mappings, prompt descriptions, regression tests, release metadata, and repository cross-references.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related issues

  • cipherstash/stack issue 601 — Adds the raw-Postgres guidance and driver-specific binding rules covered here.
  • cipherstash/stack issue 664 — Covers the new stash-sql skill’s EQL v3 casting and encryptQuery guidance.
  • cipherstash/stack issue 756 — Overlaps the raw-SQL documentation updates in stash-prisma-next.
  • cipherstash/stack issue 764 — Covers the new edge skill, credential identity, and indexing guidance.
  • cipherstash/stack issue 757 — Addresses credential mismatches that cause silent query failures.

Possibly related PRs

Suggested reviewers: calvinbrewer

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR implements the requested stash-edge and raw-SQL guidance, credential rules, and related skill mappings described in #754.
Out of Scope Changes check ✅ Passed The remaining doc, test, and JSDoc updates all support the new skills and cross-links, with no clear unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly reflects the main change: adding the stash-sql and stash-edge skills and their raw-SQL/WASM focus.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/stash-sql-edge-skills

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/stash-sql-edge-skills.md:
- Around line 48-51: Update the change description to include repository
examples covering the revised stash init/impl handoffs, wizard skill
installation, and raw-SQL/WASM guidance, and explicitly note that the separately
maintained documentation site requires corresponding updates. Preserve the
existing integration-specific guidance and cross-links.

In `@skills/stash-drizzle/SKILL.md`:
- Around line 420-421: Update the raw SQL example in the surrounding
documentation to remove the nested backticks from the inline Markdown code span.
Use a safe placeholder such as db.execute(sqlTemplate), or convert the example
to a fenced TypeScript block while preserving the guidance about operand casts
and driver-specific parameter binding.

In `@skills/stash-edge/SKILL.md`:
- Around line 291-294: Update the fenced code block containing the
EncryptedTextSearchColumn error in SKILL.md to specify the text language tag,
such as ```text, while leaving the documented error content unchanged.

In `@skills/stash-sql/SKILL.md`:
- Around line 68-73: Update the fenced code blocks containing the eql_v3
mappings in SKILL.md, including the additionally referenced block, by adding the
text language identifier to each opening fence while preserving their contents.
- Around line 182-188: Update every recipe using encryptQuery, including the
snippets around term and their corresponding examples, to guard the Result
before dereferencing .data: check .failure and handle or return the failure,
then execute the SQL only on success. Apply the same pattern consistently to all
referenced recipes, or explicitly state that success/error handling is omitted
for brevity.
- Line 3: Update the description metadata in SKILL.md to include the missing <=
and > operators alongside the existing operator list, keeping it consistent with
the predicate matrix and preserving all other description content.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 17c1183c-c043-47b6-878b-9830d357e011

📥 Commits

Reviewing files that changed from the base of the PR and between f6b82c6 and e6498d4.

📒 Files selected for processing (15)
  • .changeset/stash-sql-edge-skills.md
  • AGENTS.md
  • packages/cli/src/commands/init/lib/__tests__/install-skills.test.ts
  • packages/cli/src/commands/init/lib/install-skills.ts
  • packages/cli/src/commands/init/lib/setup-prompt.ts
  • packages/stack/src/wasm-inline.ts
  • packages/wizard/src/lib/install-skills.ts
  • skills/stash-cli/SKILL.md
  • skills/stash-drizzle/SKILL.md
  • skills/stash-edge/SKILL.md
  • skills/stash-encryption/SKILL.md
  • skills/stash-indexing/SKILL.md
  • skills/stash-prisma-next/SKILL.md
  • skills/stash-sql/SKILL.md
  • skills/stash-supabase/SKILL.md

Comment on lines +48 to +51
`stash init` / `stash impl` handoffs and the `@cipherstash/wizard` skills
prompt install both skills for the `postgresql` and `supabase` integrations.
Drizzle and Prisma Next get cross-links from their own skills instead, since
those integrations emit correctly-typed operands themselves.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Add example coverage and a documentation-site follow-up.

These changes alter the public stash init / wizard handoff workflow and introduce raw-SQL/WASM usage guidance. Add or update repository examples and explicitly track corresponding updates for the separately maintained documentation site.

As per coding guidelines, “When public APIs or user-facing workflows change, update repository examples and flag that the separately maintained documentation site needs corresponding updates.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.changeset/stash-sql-edge-skills.md around lines 48 - 51, Update the change
description to include repository examples covering the revised stash init/impl
handoffs, wizard skill installation, and raw-SQL/WASM guidance, and explicitly
note that the separately maintained documentation site requires corresponding
updates. Preserve the existing integration-specific guidance and cross-links.

Source: Coding guidelines

Comment on lines +420 to +421
> **Dropping to raw SQL?** `db.execute(sql\`…\`)` bypasses the operators this integration emits, so you own the operand casts yourself — an encrypted predicate needs its needle cast to the column's `eql_v3.query_*` domain, and the driver's parameter-binding rules differ between `pg` and `postgres-js`. The `stash-sql` skill is the reference for both.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Avoid nested backticks inside this inline code span.

Line 420 embeds a template literal inside an inline Markdown code span, triggering MD038 and risking incorrect rendering. Use a fenced TypeScript example or a placeholder such as db.execute(sqlTemplate).

🧰 Tools
🪛 markdownlint-cli2 (0.23.0)

[warning] 420-420: Spaces inside code span elements

(MD038, no-space-in-code)

🪛 SkillSpector (2.3.11)

[warning] 27: [RP1] null: npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Remediation: Pin the version: npx @scope/server@1.2.3

(MCP Rug Pull (RP1))

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/stash-drizzle/SKILL.md` around lines 420 - 421, Update the raw SQL
example in the surrounding documentation to remove the nested backticks from the
inline Markdown code span. Use a safe placeholder such as
db.execute(sqlTemplate), or convert the example to a fenced TypeScript block
while preserving the guidance about operand casts and driver-specific parameter
binding.

Source: Linters/SAST tools

Comment on lines +291 to +294
```
Type 'EncryptedTextSearchColumn' is not assignable to type 'AnyEncryptedV3Column'.
Types have separate declarations of a private property 'columnName'.
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a language tag to this fenced block.

Line 291 should use ```text (or another appropriate language) so the skill passes MD040 and renders the TypeScript error example consistently.

🧰 Tools
🪛 markdownlint-cli2 (0.23.0)

[warning] 291-291: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/stash-edge/SKILL.md` around lines 291 - 294, Update the fenced code
block containing the EncryptedTextSearchColumn error in SKILL.md to specify the
text language tag, such as ```text, while leaving the documented error content
unchanged.

Source: Linters/SAST tools

Comment thread skills/stash-sql/SKILL.md
@@ -0,0 +1,365 @@
---
name: stash-sql
description: Query EQL v3 encrypted columns from hand-written SQL over `pg` (node-postgres) or `postgres` (postgres-js) — no ORM. Covers the column-domain-to-query-domain operator matrix (which of `=`, `<>`, `<`, `>=`, `@@`, `@>` each encrypted domain accepts), minting search needles with `encryptQuery`, the per-driver parameter-binding rules for encrypted payloads, and the double-encoding failure that trips the domain CHECK with a message naming neither JSON nor encoding. Use when writing INSERT/SELECT against an encrypted column without an ORM, when a predicate returns zero rows or raises "operator does not exist", or when a domain CHECK constraint rejects an encrypted value on write.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep the operator list consistent with the predicate matrix.

The description omits <= and > even though the matrix documents both. Include every operator covered by the skill so users do not incorrectly conclude those predicates are unsupported.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/stash-sql/SKILL.md` at line 3, Update the description metadata in
SKILL.md to include the missing <= and > operators alongside the existing
operator list, keeping it consistent with the predicate matrix and preserving
all other description content.

Comment thread skills/stash-sql/SKILL.md
Comment on lines +68 to +73
```
public.eql_v3_text_eq → eql_v3.query_text_eq
public.eql_v3_text_search → eql_v3.query_text_search
public.eql_v3_bigint_ord → eql_v3.query_bigint_ord
public.eql_v3_timestamp_ord → eql_v3.query_timestamp_ord
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add language identifiers to fenced code blocks.

The bare fences trigger Markdownlint MD040. Mark these blocks as text (or another appropriate language) so the published skill passes documentation checks.

Also applies to: 159-161

🧰 Tools
🪛 markdownlint-cli2 (0.23.0)

[warning] 68-68: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/stash-sql/SKILL.md` around lines 68 - 73, Update the fenced code
blocks containing the eql_v3 mappings in SKILL.md, including the additionally
referenced block, by adding the text language identifier to each opening fence
while preserving their contents.

Source: Linters/SAST tools

Comment thread skills/stash-sql/SKILL.md
Comment on lines +182 to +188
```ts
const term = await client.encryptQuery(email, {
table: users, column: users.email, queryType: 'equality',
})
await sql`SELECT * FROM users
WHERE email = ${sql.json(term.data)}::jsonb::eql_v3.query_text_eq`
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Handle encryptQuery failures in every recipe.

These snippets dereference .data without checking .failure, unlike the earlier examples. Add the same Result guard to each recipe, or explicitly state that success handling is omitted for brevity.

Also applies to: 195-203, 212-219, 229-235, 247-251, 257-260

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/stash-sql/SKILL.md` around lines 182 - 188, Update every recipe using
encryptQuery, including the snippets around term and their corresponding
examples, to guard the Result before dereferencing .data: check .failure and
handle or return the failure, then execute the SQL only on success. Apply the
same pattern consistently to all referenced recipes, or explicitly state that
success/error handling is omitted for brevity.

…es and the WASM entry

Closes #754.

No shipped skill covered the integrations that don't use an ORM: hand-written
SQL over `pg` / `postgres-js`, and the WASM entry on Deno / Supabase Edge
Functions / Workers. Grepping the skills `stash init` installs for
`postgres-js|::jsonb::eql|sql.json|query_text_search` returned one hit, in an
unrelated code comment — so an integration on that path had to recover the
binding surface from `dist/*.d.ts`, the Postgres catalog, and experiment.

Two skills rather than one, per the scope question in the issue: the raw-SQL
predicate cookbook serves a supported plain-Node path (`hono-pg`) with no edge
or WASM involvement, so scoping it under an edge-named skill would leave that
surface uncovered.

skills/stash-sql — the predicate matrix (which of `=`, `<>`, `<`, `>=`, `@@`,
`@>` each column domain accepts, against which `eql_v3.query_*` operand),
storage-vs-query payload shapes, per-driver parameter binding, and recipes for
equality / free-text / range / ORDER BY / JSON containment / field selectors.

skills/stash-edge — import specifier per runtime, the four mandatory `CS_*`
variables and minting them with `stash env`, how the WASM client surface
differs from the native typed client, and why a schema module can't be shared
across the two entries.

Both carry the credential-identity rule, also folded into stash-cli (under
`env` and `encrypt backfill`, where it bites) and stash-supabase: EQL index
terms derive from the ZeroKMS client key, so rows written under one credential
and queried under another decrypt correctly and never match a query.

Three claims were corrected against a live EQL v3 3.0.2 install rather than
carried over from the issue:

- The binding rule is driver-specific. On postgres-js a bare object fails to
  INSERT and `JSON.stringify(...)::jsonb` fails the domain CHECK; only
  `sql.json()` works in both positions. On `pg` all three forms work.
- A bare-`jsonb` operand does not fall through to native jsonb semantics — EQL
  defines `jsonb` overloads that coerce to the *storage* domain, which requires
  the ciphertext key `c`, so a query term without the domain cast raises a
  CHECK violation rather than silently matching nothing.
- The schema type incompatibility reproduces in both directions, not one.

Also fixes the wasm-inline module JSDoc, which passed
`OidcFederationStrategy.create(...)`'s Result straight to
`config.authStrategy` without unwrapping — the same JSDoc the raw-SQL surface
was being reverse-engineered from.

SKILL_MAP (CLI + wizard) installs both for `postgresql` and `supabase`;
Drizzle and Prisma Next get cross-links from their own skills instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a stash-edge skill: WASM entry, credentials, and the raw-SQL predicate surface

1 participant