Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 15 additions & 0 deletions .changeset/eql-v3-source-from-package.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'stash': patch
---

The EQL **v3** install SQL is now read from the `@cipherstash/eql` package at
runtime instead of a copy vendored into this repo. `@cipherstash/eql` becomes a
runtime dependency of `stash`, and a version bump now flows straight through — no
re-vendor step, no drift between the pin and the shipped bundle.

This removes ~44k lines of generated plpgsql from the repository (which had made
GitHub classify the whole repo as plpgsql — CIP-3518) along with the
`gen:eql-v3-sql` vendor script and its CI drift-check.

No behaviour change: v3 installs the same one-artifact bundle (which self-adapts to
non-superuser environments like Supabase), and the v2 path is unchanged.
9 changes: 0 additions & 9 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,15 +133,6 @@ jobs:
- name: Test — lint script self-tests
run: pnpm run test:scripts

# The EQL v3 bundle in packages/cli/src/sql is vendored from the pinned
# @cipherstash/eql package (sha256-verified by gen:eql-v3-sql).
# Regenerate and fail on any diff so the pin and the shipped bundle
# cannot drift silently.
- name: Check — vendored EQL v3 SQL bundle is in sync
run: |
pnpm --filter stash run gen:eql-v3-sql
git diff --exit-code -- packages/cli/src/sql/cipherstash-encrypt-v3.sql

- name: Create .env file in ./packages/protect/
run: |
touch ./packages/protect/.env
Expand Down
7 changes: 3 additions & 4 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "stash",
"version": "1.0.0-rc.2",
"description": "CipherStash CLI the one stash command for auth, init, encryption schema, database setup, and secrets.",
"description": "CipherStash CLI \u2014 the one stash command for auth, init, encryption schema, database setup, and secrets.",
"repository": {
"type": "git",
"url": "git+https://github.com/cipherstash/stack.git",
Expand Down Expand Up @@ -42,11 +42,11 @@
"dev": "tsup --watch",
"test": "vitest run",
"test:e2e": "vitest run --config vitest.integration.config.ts",
"gen:eql-v3-sql": "node scripts/build-eql-v3-sql.mjs",
"lint": "biome check ."
},
"dependencies": {
"@cipherstash/auth": "catalog:repo",
"@cipherstash/eql": "3.0.0",
"@cipherstash/migrate": "workspace:*",
"@clack/prompts": "1.7.0",
"dotenv": "17.4.2",
Expand All @@ -56,7 +56,7 @@
"posthog-node": "^5.40.0",
"zod": "^3.25.76"
},
"//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.",
"//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.",
"optionalDependencies": {
"@cipherstash/auth-darwin-arm64": "catalog:repo",
"@cipherstash/auth-darwin-x64": "catalog:repo",
Expand All @@ -74,7 +74,6 @@
}
},
"devDependencies": {
"@cipherstash/eql": "3.0.0",
"@cipherstash/stack": "workspace:*",
"@types/pg": "^8.20.0",
"node-pty": "^1.1.0",
Expand Down
57 changes: 0 additions & 57 deletions packages/cli/scripts/build-eql-v3-sql.mjs

This file was deleted.

44 changes: 40 additions & 4 deletions packages/cli/src/installer/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { existsSync, readFileSync } from 'node:fs'
import { dirname, join, resolve } from 'node:path'
import { readInstallSql } from '@cipherstash/eql/sql'
import pg from 'pg'
import {
EQL_SCHEMA_NAME,
Expand All @@ -9,6 +10,29 @@ import {
SUPABASE_PERMISSIONS_SQL_V3,
} from './grants.js'

/**
* The EQL **v3** install SQL, read from `@cipherstash/eql` at runtime — the
* single source of truth (the same `readInstallSql()` the stack and prisma-next
* consume). We deliberately do NOT vendor a copy into the repo:
* - a ~44k-line plpgsql artifact in the tree made GitHub classify the whole
* repo as plpgsql (CIP-3518);
* - a vendored copy silently drifts from the pinned package on every bump.
* Reading it here means a `@cipherstash/eql` version bump flows straight through.
* There is no Supabase / no-operator-family variant for v3: the bundle installs
* everywhere from one artifact (its superuser-only operator-class statements run
* inside a `DO` block that catches `insufficient_privilege` and skips).
*/
function readV3InstallSql(): string {
try {
return readInstallSql()
} catch (error) {
throw new Error(
'Failed to read the EQL v3 install SQL from `@cipherstash/eql`. Reinstall dependencies (the package ships the bundle in `dist/sql/`).',
{ cause: error },
)
}
}

// EQL release, pinned to match the EQL payload format this package emits.
// Bump in lockstep with @cipherstash/protect-ffi.
const EQL_VERSION = 'eql-2.3.1'
Expand Down Expand Up @@ -320,8 +344,9 @@ export class EQLInstaller {
const excludeOperatorFamily = options?.excludeOperatorFamily || supabase

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).
// The v3 bundle is read from the pinned `@cipherstash/eql` package (see
// `readV3InstallSql`), not fetched from a GitHub release, so `--latest`
// (which downloads a release asset) has no v3 target.
// Gating --latest behind --eql-version 2 is tracked in #585.
throw new Error(
'`--latest` is not supported for EQL v3 yet: no public v3 release artifacts exist. Use the bundled install.',
Expand Down Expand Up @@ -394,6 +419,9 @@ export class EQLInstaller {
supabase: boolean
eqlVersion: EqlVersion
}): string {
// v3 is read from `@cipherstash/eql` at runtime; only v2 is vendored.
if (options.eqlVersion === 3) return readV3InstallSql()

const filename = resolveBundledFilename(options)

try {
Expand Down Expand Up @@ -455,8 +483,12 @@ function resolveBundledFilename(options: {
supabase: boolean
eqlVersion?: EqlVersion
}): string {
// v3 is not vendored — it's read from `@cipherstash/eql` at runtime (see
// `readV3InstallSql`), so callers route it away before reaching here.
if ((options.eqlVersion ?? DEFAULT_EQL_VERSION) === 3) {
return 'cipherstash-encrypt-v3.sql'
throw new Error(
'resolveBundledFilename is v2-only; v3 SQL comes from `@cipherstash/eql`.',
)
}
if (options.supabase) return 'cipherstash-encrypt-supabase.sql'
if (options.excludeOperatorFamily)
Expand All @@ -474,10 +506,14 @@ export function loadBundledEqlSql(
eqlVersion?: EqlVersion
} = {},
): string {
const eqlVersion = options.eqlVersion ?? DEFAULT_EQL_VERSION
// v3 is read from `@cipherstash/eql` at runtime; only v2 is vendored.
if (eqlVersion === 3) return readV3InstallSql()

const filename = resolveBundledFilename({
excludeOperatorFamily: options.excludeOperatorFamily ?? false,
supabase: options.supabase ?? false,
eqlVersion: options.eqlVersion ?? DEFAULT_EQL_VERSION,
eqlVersion,
})

try {
Expand Down
Loading
Loading