Skip to content

feat(stack): Bigint domains + Supabase support (i64-bounded) — CIP-3291 [gated on protect-ffi release]#557

Draft
freshtonic wants to merge 3 commits into
mainfrom
james/cip-3291-bigint-stack
Draft

feat(stack): Bigint domains + Supabase support (i64-bounded) — CIP-3291 [gated on protect-ffi release]#557
freshtonic wants to merge 3 commits into
mainfrom
james/cip-3291-bigint-stack

Conversation

@freshtonic

Copy link
Copy Markdown
Contributor

Summary

Adds Bigint support to the stack SDK and its Supabase adapter — the Bigint work stream of CIP-3291. This is step 3 of the CIP-3291 merge order. It was cut from #547's branch (james/cip-3300-spike-integrate-eql-v3-into-supabase-orm, the full EQL v3 surface); #547 has since merged to main (merge commit 93b29291 is main's tip), so this PR targets main directly and its diff is exactly the bigint work.

Recorded decisions (from the Linear ticket — not relitigated here)

  • Bounds = full i64 (-2^63 … 2^63 - 1). Enforcement lives at the protect-ffi boundary (a parallel PR adds JS BigInt to protect-ffi's JsPlaintext on the eql_v3 release line). The stack layer's job is types + domains + adapters + surfacing those errors — out-of-range values surface as encryption errors, never silent truncation.
  • bigint columns decrypt to JS bigint, always (never a precision-lossy number).
  • Scope excludes *_ord_ope (CIP-3403): this adds bigint, bigint_eq, bigint_ord, bigint_ord_ore — the same families every other int has.

🚧 Why this is a DRAFT — the gate

The pinned @cipherstash/protect-ffi 0.27.0 does not accept/return JS bigint (JsPlaintext lacks it): encrypting a bigint throws at runtime. The pin is deliberately NOT bumped here. This PR un-drafts when ALL of:

  1. the protect-ffi release with JS BigInt in JsPlaintext ships (parallel FFI PR on the eql_v3 release line);
  2. the protect-ffi pin is bumped in this repo;
  3. live-test verification passes: remove liveGate from the four bigint rows in __tests__/v3-matrix/catalog.ts — the live matrix suites (matrix-live, matrix-live-pg) then automatically pick the bigint domains up (mega-table round-trips at i64::MAX / i64::MIN / 0n / a >2^53 mid value, plus the pg eq/ord proofs) — and run green against live creds + a live DB.

Until then, all runtime coverage is mock-based (the repo's established mock pattern in supabase-v3-builder.test.ts), and the live suites emit explicit, reason-bearing skips for the bigint rows instead of silently omitting them.

What changed

SDK (packages/stack/src):

  • eql/v3/columns.ts: BIGINT/BIGINT_EQ/BIGINT_ORD_ORE/BIGINT_ORD domain consts (eql_v3.bigint*, castAs: 'bigint'), EncryptedBigint*Column classes, AnyEncryptedV3Column membership, PlaintextKind/PlaintextFromKind gain 'bigint'bigint.
  • eql/v3/types.ts: types.Bigint / BigintEq / BigintOrdOre / BigintOrd factories (naming convention preserved); barrel exports in eql/v3/index.ts.
  • types.ts: Plaintext = JsPlaintext | Date | bigint, with the gate documented.
  • Index emission needed no new code: the capability presets already produce the non-text rule — bigint_equnique (hm); bigint_ord/bigint_ord_oreore only (equality answered via ob, like integer_ord). Pinned by regression tests.
  • schema/index.ts already had 'bigint''big_int' in castAsEnum/toEqlCastAs (and the wasm normalizeCastAs path) — no change needed.

Supabase adapter: no runtime code change needed — query-builder-v3.ts is value-generic (filter operands go through encrypt(); only post-encryption envelopes are JSON-encoded, and envelopes never contain a raw bigint). Coverage added instead (below).

Decrypt-path bigint survival (verified): the model decrypt path (encryption/helpers/model-helpers.ts → FFI decryptBulk) and the Supabase decryptResultsdecryptModel/bulkDecryptModelspostprocessDecryptedRow path pass FFI-returned values through with no JSON round-trip — a JS bigint returned through the Neon boundary reaches the caller intact. rowReconstructor needs no bigint arm (comment updated). The only JSON.stringify on the query path operates on post-encryption envelopes (safe).

Tests:

  • v3-matrix/catalog.ts: 4 bigint rows (compile-time coverage gate satisfied); BIGINT_S samples include 9223372036854775807n (i64::MAX), -9223372036854775808n (i64::MIN), 0n, and 4611686018427387904n (> 2^53, proves losslessness); new DomainSpec.liveGate field carries the skip reason naming the gate.
  • matrix-live / matrix-live-pg: live-gated domains are excluded from the shared mega tables / seed INSERT / term encryption — a bigint through FFI 0.27 (including the v2-wire term client) throws at runtime in beforeAll, which would take every other domain's live proof down — and reported as explicit skips.
  • matrix.test-d.ts: bigint_ord infers plaintext bigint + 'equality' | 'orderAndRange'.
  • schema-v3.test.ts: bigint joins the equality-via-ORE and ore-only (no unique) regression lists.
  • supabase-v3-builder.test.ts (mock, wire-encoding): types.BigintOrd column — insert delivers the real bigint to encrypt; eq/gte operands JSON-encode safely as full envelopes; decrypted rows return a JS bigint through decryptResults.
  • supabase-v3.test-d.ts: bigint column plaintext type is bigint; filter/insert value types pin to bigint (number/string rejected).

Docs: docs/reference/supabase-sdk.md + skills/stash-supabase/SKILL.md gain the bigint domains (types/DDL examples), the i64 bounds + boundary-error semantics, and the gate note. Changeset added (minor).

Test evidence

  • pnpm exec tsc --noEmit (packages/stack): 198 errors — byte-identical to the pre-change baseline (all pre-existing in __tests__; src/ at 0). Verified by diffing baseline vs after.
  • pnpm exec vitest run — v3-matrix deterministic suites (matrix now 39 domains), schema-v3, schema-builders, supabase-v3-builder (27 tests incl. 3 new bigint cases), typed-client-v3, schema-v3-client: all green (live suites skip locally as normal).
  • pnpm exec vitest --run --typecheck.only: 7 files, 66 tests, no type errors (incl. the new bigint type assertions).
  • biome on touched files: clean (one pre-existing unused-import warning on the base, unchanged).

Merge order

Step 3 of CIP-3291: (1) protect-ffi JS BigInt PR → (2) base stack PR #547 (merged) → (3) this PR → then pin bump + liveGate removal + live verification un-drafts it.

…plaintext)

Add BIGINT/BIGINT_EQ/BIGINT_ORD_ORE/BIGINT_ORD domain definitions, 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).

Index emission follows the non-text numeric rule automatically:
bigint_eq -> unique (hm); bigint_ord/bigint_ord_ore -> ore only (equality
answered via ob).

Bounds are the full i64 range, enforced at the protect-ffi boundary.
GATED: live encrypt/decrypt requires the next protect-ffi release —
the pinned 0.27.0 JsPlaintext cannot marshal a JS bigint across the Neon
boundary (throws at runtime). No reconstruction is needed on the decrypt
paths: the model decrypt helpers pass FFI-returned values through with no
JSON round-trip.

Part of CIP-3291 (bigint work stream); *_ord_ope is out of scope (CIP-3403).
- catalog: add the four bigint domain rows with BIGINT_S samples covering
  i64::MAX (9223372036854775807n), i64::MIN (-9223372036854775808n), 0n,
  and a mid value beyond Number.MAX_SAFE_INTEGER; widen DomainSpec.samples
  to include bigint; add an optional liveGate field naming the skip reason.
- matrix-live / matrix-live-pg: exclude liveGate'd domains from the mega
  tables, seed rows, and term encryption (a bigint through protect-ffi
  0.27.0 throws at RUNTIME, which would fail the shared beforeAll and take
  every other domain's proof down); emit explicit reason-bearing skips
  instead. Gating lifts by removing liveGate when the protect-ffi pin bumps.
- matrix.test-d: bigint_ord column infers plaintext bigint and the
  equality|orderAndRange queryType union.
- schema-v3: bigint_ord/bigint_ord_ore join the equality-via-ORE and
  ore-only (no unique) regression lists.
- supabase-v3-builder (mock-based): bigint insert reaches encrypt as a real
  bigint, eq/gte filter operands JSON-encode safely (envelopes never carry
  raw bigint), and decrypted rows return a JS bigint through decryptResults.
  The mock envelope encodes bigint as a tagged string to preserve the
  real-world invariant that post-encryption envelopes are JSON-safe.
- supabase-v3.test-d: bigint column plaintext is bigint; filter and insert
  value types pin to bigint (number/string rejected).

Part of CIP-3291.
… gate

Add the bigint domain family to the Supabase SDK reference and the
stash-supabase skill (types table, DDL example, JS bigint in/out
semantics, i64 boundary-error behaviour), plus a changeset (minor) noting
the FFI-release gate and that bigint columns always decrypt to JS bigint.

Part of CIP-3291.
@changeset-bot

changeset-bot Bot commented Jul 5, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9dd1a06

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

This PR includes changesets to release 6 packages
Name Type
@cipherstash/stack Minor
@cipherstash/bench Patch
@cipherstash/prisma-next Patch
@cipherstash/basic-example Patch
@cipherstash/prisma-next-example Patch
@cipherstash/e2e 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 5, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2cc3a17d-6a61-41ea-9b51-b92d41237821

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch james/cip-3291-bigint-stack

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.

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.

1 participant