feat(stack,cli): EQL v3 bigint + Supabase v3 + CLI install path, re-expressed on protect-ffi 0.28#583
feat(stack,cli): EQL v3 bigint + Supabase v3 + CLI install path, re-expressed on protect-ffi 0.28#583tobyhede wants to merge 5 commits into
Conversation
The bulk model helpers key per-field operation results by
`${modelIndex}-${fieldKey}` ids and reconstructed models with a naive
key.split('-'), truncating any field key containing a hyphen
(some-field -> some): the value landed under the truncated key and the
real field silently vanished. Duplicated across all four multi-model
reconstruction sites.
Extract one fieldsForModelIndex helper that splits at the FIRST hyphen
only and use it at all four sites. Offline regression tests (mocked
protect-ffi) prove hyphenated and multi-hyphen field names survive
bulkEncryptModels / bulkDecryptModels -- both fail against the naive
split.
Re-expressed from james/cip-3291-bigint-stack 632bdf6.
…n protect-ffi 0.28) Add BIGINT/BIGINT_EQ/BIGINT_ORD_ORE/BIGINT_ORD domain definitions against the public.bigint* concrete domains, the EncryptedBigint*Column classes, types.Bigint* factories, barrel exports, and union membership, following the existing per-domain pattern. The SDK-wide Plaintext union gains bigint, and PlaintextKind/PlaintextFromKind map cast_as 'bigint' to the JS bigint type (bigint columns always decrypt to a JS bigint). Shipped UN-GATED: protect-ffi 0.28 marshals a native JS bigint across the Neon boundary losslessly (encode on the way in, JsBigInt::from_i64 on decrypt), so no reconstruction is needed on the decrypt path and the live matrix suites exercise the round-trip directly. i64 bounds are enforced at the protect-ffi boundary. Index emission follows the numeric rule: bigint_eq -> unique (hm); bigint_ord/bigint_ord_ore -> ore (equality via ob). Removes the base's bigint skip changeset and flips the schema-v3 type guard from a negative (@ts-expect-error) to a positive assertion. The catalog-driven capability sweep and live-PG matrix pick up the four public.bigint* rows automatically (no liveGate). Re-expressed from james/cip-3291-bigint-stack 2495c65 + e593415 against the public.* domain surface and protect-ffi 0.28.
… eql install Vendor the v3 SQL bundles into packages/cli/src/sql via a checked-in derivation script (scripts/build-eql-v3-sql.mjs): the full bundle is a byte-identical copy of the stack public.* fixture monolith; the Supabase variant strips the two CREATE OPERATOR CLASS/FAMILY chunks at their --! @file markers (they need superuser). EQLInstaller gains an eqlVersion option on install/isInstalled/ getInstalledVersion; v3 keys install-detection, version(), and Supabase grants to the eql_v3 schema (type domains live in public, operators in eql_v3, constructors in eql_v3_internal). v3 + --latest is rejected (no public artifacts). eql install --eql-version 3 is direct-install only — --drizzle/--migration/--migrations-dir/--latest are rejected up front and auto-detected drizzle falls back to direct with a notice. Move db install under a dedicated eql command group as eql install; db install stays as a deprecated alias that warns and forwards. Help text, next-step hints, generated Supabase migration headers, wizard allowlist + steps, docs, and tests all reference eql install. Re-expressed from james/cip-3291-bigint-stack d15414a + e027b74 + 64fdeb2 against the public.* v3 bundle (scoped to the install rename; the eqlVersion runtime client flag and eql upgrade/status moves are intentionally not ported).
…ent --eql-version Update the stash-cli skill and @cipherstash/migrate README to the renamed `stash eql install` command (keeping `stash db install` documented as the deprecated alias), and document the new `--eql-version <2|3>` install flag and its v3 direct-install-only constraints in the skill. Re-expressed from james/cip-3291-bigint-stack 64fdeb2/d15414a5 docs. Historical CHANGELOGs and dated design docs are left as point-in-time records; the base already migrated the domain surface to public.*.
…pter The v2 query mechanism (direct EQL operators over PostgREST) is unchanged: EncryptedQueryBuilderImpl gains narrow protected seams whose defaults preserve the v2 behaviour byte-for-byte, and a v3 subclass (query-builder-v3.ts) overrides them for native public.* concrete-domain columns: - column recognition + property<->DB name resolution via buildColumnKeyMap (filters, mutations, aliased select casts prop:db::jsonb) - raw jsonb mutation payloads (no eql_v2 composite wrap) - full-envelope filter operands: every public.* domain CHECK requires the storage keys (v/i/c + index terms) and the SQL operators coerce their jsonb operand into the domain, so a narrowed encryptQuery term fails 23514 on EVERY domain — all operands go through encrypt() instead - like/ilike on encrypted columns -> PostgREST cs (bloom @>) - Date reconstruction from cast_as (date/timestamp) on decrypted rows - capability validation: filters on storage-only columns or unsupported query types throw typed + runtime errors; filter keys are type-narrowed Wire-encoding unit tests (mock encryption + supabase clients) cover both dialects, including v2 regression pins for the seams. Re-expressed from james/cip-3291-bigint-stack ecd3f38 against the base's query-builder and the public.* domain surface (protect-ffi 0.28).
🦋 Changeset detectedLatest commit: f2d38d8 The changes in this PR will be included in the next version bump. This PR includes changesets to release 8 packages
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 |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
| // v3 supports the direct install path only. Explicit --drizzle/--migration | ||
| // are rejected up-front by validateInstallFlags; auto-DETECTED drizzle or | ||
| // migration modes fall back to direct here rather than erroring. | ||
| if (eqlVersion === 3 && resolved.drizzle) { | ||
| p.log.info( | ||
| 'EQL v3 does not support the Drizzle migration path yet — installing directly.', | ||
| ) | ||
| resolved.drizzle = false | ||
| } |
There was a problem hiding this comment.
Is there an issue tracked for this?
| /** The v3 (`eql_v3`) Supabase grants block. See {@link supabasePermissionsSql}. */ | ||
| export const SUPABASE_PERMISSIONS_SQL_V3 = | ||
| supabasePermissionsSql(EQL_V3_SCHEMA_NAME) |
There was a problem hiding this comment.
Clarification: the EQLV3 install now gracefully degrades to OPE if no operator class/family support is available. Is the dedicated Supabase installer needed for V3 now?
| if (latest && eqlVersion === 3) { | ||
| // No public v3 release artifacts exist yet — the v3 bundles are vendored | ||
| // from the generated monolith (see scripts/build-eql-v3-sql.mjs). | ||
| throw new Error( | ||
| '`--latest` is not supported for EQL v3 yet: no public v3 release artifacts exist. Use the bundled install.', | ||
| ) | ||
| } |
| ``` | ||
|
|
||
| State is tracked in an append-only `cipherstash.cs_migrations` table installed by `stash db install`. The EQL intent (which indexes, which cast_as) continues to live in `eql_v2_configuration` so Proxy continues to work against the same database. | ||
| State is tracked in an append-only `cipherstash.cs_migrations` table installed by `stash eql install`. The EQL intent (which indexes, which cast_as) continues to live in `eql_v2_configuration` so Proxy continues to work against the same database. |
There was a problem hiding this comment.
This might need to be updated to differentiate between V2 and V3.
| * const users = encryptedTable('users', { | ||
| * email: types.TextSearch('email'), // public.text_search | ||
| * amount: types.IntegerOrd('amount'), // public.integer_ord | ||
| * }) | ||
| * | ||
| * const client = await Encryption({ schemas: [users] }) | ||
| * const es = encryptedSupabaseV3({ encryptionClient: client, supabaseClient: supabase }) |
There was a problem hiding this comment.
This interface differs to what is currently on the launch blog post. I think a Rails-style auto-config loading from the schema is the money shot of V3. I'd love for us to be able to make it happen if we can. If not, we'll need to update the blog post.
coderdan
left a comment
There was a problem hiding this comment.
This is looking really solid. A couple of things I'd like to align on before explicitly approving, but the direction is really good.
Re-expresses the net-new work from
origin/james/cip-3291-bigint-stack(protect-ffi 0.27, oldeql_v3.*naming) onto this base's newer EQL v3 surface:public.*type domains, schema-drivencast_asrouting, and protect-ffi 0.28 (native JSbigintround-trip). Because the branches diverged on the domain surface, each feature is re-derived against BASE conventions rather than cherry-picked. Stacked onchore/eql-v3-ffi-0-28(PR #575) — set that as the merge base, notmain.Commits (one per feature)
fix(stack)hyphen fix —fieldsForModelIndex()splits bulk-model result ids at the first hyphen only, so field keys containing-survivebulkEncrypt/DecryptModels. Regression test fails against the old naivesplit('-').feat(stack)bigint domain family (un-gated) —types.Bigint*+EncryptedBigint*Columnover thepublic.bigint*domains,bigintPlaintextKind/Plaintextarm. Shipped un-gated: protect-ffi 0.28 marshals a native JSbigintlosslessly (bigintWire: encode on encrypt,JsBigInt::from_i64on decrypt), so no reconstruction is needed and the live matrix exercises the round-trip. Removes the base'sskip-v3-bigintchangeset and flips the@ts-expect-errorguard to a positive assertion.feat(cli)EQL v3 install path +db install→eql install—--eql-version <2|3>installs vendoredpublic.*bundles (derived from the stack fixture viascripts/build-eql-v3-sql.mjs), keyed to theeql_v3schema; v3 is direct-install-only.db installbecomes a deprecated alias. (Scoped to the install rename — theeqlVersionruntime client flag is intentionally not ported; BASE supersedes it withcast_asrouting.)docs— reconcile living docs (stash-cli skill, migrate README) tostash eql install+ document--eql-version.feat(stack)encryptedSupabaseV3— EQL v3 dialect of the Supabase adapter.EncryptedQueryBuilderImplgains protected seams (v2-preserving defaults); a v3 subclass overrides them for property↔DB resolution, raw jsonb mutations, full-envelope filter operands,like/ilike→cs,Datereconstruction, and capability validation.Verification
pnpm --filter @cipherstash/stack test:types→ 67 ✓ (theRecord<EqlV3TypeName, DomainSpec>catalog gate covers the newpublic.bigint*rows)Client key not configuredenvironmental baseline (unchanged with/without this branch, confirmed by stashing)matrix-live-pg/schema-v3-pgprove the bigint round-trip end-to-end but need CS workspace creds (CS_*) — run in CI or locally with creds. Code is verified against 0.28'sbigintWireFFI contract.