Skip to content

Commit 175eeb7

Browse files
authored
feat(cli): read EQL v3 install SQL from @cipherstash/eql at runtime (CIP-3518) (#692)
The v3 install SQL was vendored into packages/cli/src/sql/cipherstash-encrypt-v3.sql (~44k lines) by a build script (gen:eql-v3-sql), sha256-verified against the pinned @cipherstash/eql. That copy defeats the point of depending on @cipherstash/eql: it drifts from the pin on every bump (re-vendor required) and — as ~44k lines of plpgsql in the tree — made GitHub classify the whole repo as plpgsql (CIP-3518). Read it from the package at runtime instead: - `readV3InstallSql()` calls `@cipherstash/eql/sql` `readInstallSql()` (the same source the stack + prisma-next consume); the v3 branches of `loadBundledEqlSql` and `EQLInstaller` route to it, so `resolveBundledFilename` is now v2-only. - `@cipherstash/eql` moves devDependency → dependency, externalized in tsup so the real package (and its `dist/sql/*.sql`) resolves at runtime. - Delete `cipherstash-encrypt-v3.sql` + `scripts/build-eql-v3-sql.mjs` + the `gen:eql-v3-sql` script and its CI drift-check (tests.yml). No behaviour change: same one-artifact v3 bundle (self-adapts to non-superuser / Supabase via the DO-block privilege catch), v2 path untouched. A `@cipherstash/eql` bump now flows straight through. 557 cli unit green; bundle read verified via the bin from an external cwd. Claude-Session: https://claude.ai/code/session_01MxTTPaPP16m6br7Hoab94w
1 parent b8cb599 commit 175eeb7

8 files changed

Lines changed: 65 additions & 44130 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'stash': patch
3+
---
4+
5+
The EQL **v3** install SQL is now read from the `@cipherstash/eql` package at
6+
runtime instead of a copy vendored into this repo. `@cipherstash/eql` becomes a
7+
runtime dependency of `stash`, and a version bump now flows straight through — no
8+
re-vendor step, no drift between the pin and the shipped bundle.
9+
10+
This removes ~44k lines of generated plpgsql from the repository (which had made
11+
GitHub classify the whole repo as plpgsql — CIP-3518) along with the
12+
`gen:eql-v3-sql` vendor script and its CI drift-check.
13+
14+
No behaviour change: v3 installs the same one-artifact bundle (which self-adapts to
15+
non-superuser environments like Supabase), and the v2 path is unchanged.

.github/workflows/tests.yml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,6 @@ jobs:
133133
- name: Test — lint script self-tests
134134
run: pnpm run test:scripts
135135

136-
# The EQL v3 bundle in packages/cli/src/sql is vendored from the pinned
137-
# @cipherstash/eql package (sha256-verified by gen:eql-v3-sql).
138-
# Regenerate and fail on any diff so the pin and the shipped bundle
139-
# cannot drift silently.
140-
- name: Check — vendored EQL v3 SQL bundle is in sync
141-
run: |
142-
pnpm --filter stash run gen:eql-v3-sql
143-
git diff --exit-code -- packages/cli/src/sql/cipherstash-encrypt-v3.sql
144-
145136
- name: Create .env file in ./packages/protect/
146137
run: |
147138
touch ./packages/protect/.env

packages/cli/package.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "stash",
33
"version": "1.0.0-rc.2",
4-
"description": "CipherStash CLI the one stash command for auth, init, encryption schema, database setup, and secrets.",
4+
"description": "CipherStash CLI \u2014 the one stash command for auth, init, encryption schema, database setup, and secrets.",
55
"repository": {
66
"type": "git",
77
"url": "git+https://github.com/cipherstash/stack.git",
@@ -42,11 +42,11 @@
4242
"dev": "tsup --watch",
4343
"test": "vitest run",
4444
"test:e2e": "vitest run --config vitest.integration.config.ts",
45-
"gen:eql-v3-sql": "node scripts/build-eql-v3-sql.mjs",
4645
"lint": "biome check ."
4746
},
4847
"dependencies": {
4948
"@cipherstash/auth": "catalog:repo",
49+
"@cipherstash/eql": "3.0.0",
5050
"@cipherstash/migrate": "workspace:*",
5151
"@clack/prompts": "1.7.0",
5252
"dotenv": "17.4.2",
@@ -56,7 +56,7 @@
5656
"posthog-node": "^5.40.0",
5757
"zod": "^3.25.76"
5858
},
59-
"//optionalDependencies": "@cipherstash/auth ships per-platform native bindings as optional peerDependencies. pnpm does not auto-install platform-matched optional peer deps, so we declare them here as optionalDependencies pnpm then picks the binary matching the host's os/cpu (from each sub-package's own package.json) and ignores the rest. All seven names share a single catalog entry to keep them in lockstep.",
59+
"//optionalDependencies": "@cipherstash/auth ships per-platform native bindings as optional peerDependencies. pnpm does not auto-install platform-matched optional peer deps, so we declare them here as optionalDependencies \u2014 pnpm then picks the binary matching the host's os/cpu (from each sub-package's own package.json) and ignores the rest. All seven names share a single catalog entry to keep them in lockstep.",
6060
"optionalDependencies": {
6161
"@cipherstash/auth-darwin-arm64": "catalog:repo",
6262
"@cipherstash/auth-darwin-x64": "catalog:repo",
@@ -74,7 +74,6 @@
7474
}
7575
},
7676
"devDependencies": {
77-
"@cipherstash/eql": "3.0.0",
7877
"@cipherstash/stack": "workspace:*",
7978
"@types/pg": "^8.20.0",
8079
"node-pty": "^1.1.0",

packages/cli/scripts/build-eql-v3-sql.mjs

Lines changed: 0 additions & 57 deletions
This file was deleted.

packages/cli/src/installer/index.ts

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { existsSync, readFileSync } from 'node:fs'
22
import { dirname, join, resolve } from 'node:path'
3+
import { readInstallSql } from '@cipherstash/eql/sql'
34
import pg from 'pg'
45
import {
56
EQL_SCHEMA_NAME,
@@ -9,6 +10,29 @@ import {
910
SUPABASE_PERMISSIONS_SQL_V3,
1011
} from './grants.js'
1112

13+
/**
14+
* The EQL **v3** install SQL, read from `@cipherstash/eql` at runtime — the
15+
* single source of truth (the same `readInstallSql()` the stack and prisma-next
16+
* consume). We deliberately do NOT vendor a copy into the repo:
17+
* - a ~44k-line plpgsql artifact in the tree made GitHub classify the whole
18+
* repo as plpgsql (CIP-3518);
19+
* - a vendored copy silently drifts from the pinned package on every bump.
20+
* Reading it here means a `@cipherstash/eql` version bump flows straight through.
21+
* There is no Supabase / no-operator-family variant for v3: the bundle installs
22+
* everywhere from one artifact (its superuser-only operator-class statements run
23+
* inside a `DO` block that catches `insufficient_privilege` and skips).
24+
*/
25+
function readV3InstallSql(): string {
26+
try {
27+
return readInstallSql()
28+
} catch (error) {
29+
throw new Error(
30+
'Failed to read the EQL v3 install SQL from `@cipherstash/eql`. Reinstall dependencies (the package ships the bundle in `dist/sql/`).',
31+
{ cause: error },
32+
)
33+
}
34+
}
35+
1236
// EQL release, pinned to match the EQL payload format this package emits.
1337
// Bump in lockstep with @cipherstash/protect-ffi.
1438
const EQL_VERSION = 'eql-2.3.1'
@@ -320,8 +344,9 @@ export class EQLInstaller {
320344
const excludeOperatorFamily = options?.excludeOperatorFamily || supabase
321345

322346
if (latest && eqlVersion === 3) {
323-
// No public v3 release artifacts exist yet — the v3 bundles are vendored
324-
// from the generated monolith (see scripts/build-eql-v3-sql.mjs).
347+
// The v3 bundle is read from the pinned `@cipherstash/eql` package (see
348+
// `readV3InstallSql`), not fetched from a GitHub release, so `--latest`
349+
// (which downloads a release asset) has no v3 target.
325350
// Gating --latest behind --eql-version 2 is tracked in #585.
326351
throw new Error(
327352
'`--latest` is not supported for EQL v3 yet: no public v3 release artifacts exist. Use the bundled install.',
@@ -394,6 +419,9 @@ export class EQLInstaller {
394419
supabase: boolean
395420
eqlVersion: EqlVersion
396421
}): string {
422+
// v3 is read from `@cipherstash/eql` at runtime; only v2 is vendored.
423+
if (options.eqlVersion === 3) return readV3InstallSql()
424+
397425
const filename = resolveBundledFilename(options)
398426

399427
try {
@@ -455,8 +483,12 @@ function resolveBundledFilename(options: {
455483
supabase: boolean
456484
eqlVersion?: EqlVersion
457485
}): string {
486+
// v3 is not vendored — it's read from `@cipherstash/eql` at runtime (see
487+
// `readV3InstallSql`), so callers route it away before reaching here.
458488
if ((options.eqlVersion ?? DEFAULT_EQL_VERSION) === 3) {
459-
return 'cipherstash-encrypt-v3.sql'
489+
throw new Error(
490+
'resolveBundledFilename is v2-only; v3 SQL comes from `@cipherstash/eql`.',
491+
)
460492
}
461493
if (options.supabase) return 'cipherstash-encrypt-supabase.sql'
462494
if (options.excludeOperatorFamily)
@@ -474,10 +506,14 @@ export function loadBundledEqlSql(
474506
eqlVersion?: EqlVersion
475507
} = {},
476508
): string {
509+
const eqlVersion = options.eqlVersion ?? DEFAULT_EQL_VERSION
510+
// v3 is read from `@cipherstash/eql` at runtime; only v2 is vendored.
511+
if (eqlVersion === 3) return readV3InstallSql()
512+
477513
const filename = resolveBundledFilename({
478514
excludeOperatorFamily: options.excludeOperatorFamily ?? false,
479515
supabase: options.supabase ?? false,
480-
eqlVersion: options.eqlVersion ?? DEFAULT_EQL_VERSION,
516+
eqlVersion,
481517
})
482518

483519
try {

0 commit comments

Comments
 (0)