docs(get-started): build the Get started section#65
Conversation
The live quickstart contains six errors, not the two on the ticket. Each was
checked against the shipped stash 0.17.1 and @cipherstash/stack 0.19.0, not
against the prose:
1. `cs_match_v1(email) = $1` — no such function in any EQL version.
Now `WHERE email = $1::public.text_eq`, with the typed-operand rule
explained, since an untyped operand silently skips the encrypted operator.
2. `import { users } from "./encryption/schema"` — init scaffolds
`src/encryption/index.ts` (33 references in the CLI package), and exports
both `users` and `encryptionClient` from it.
3. `decrypt(row.email, { column, table })` — decrypt takes one argument.
4. `.freeTextSearch() // WHERE email LIKE '%alice%'` — LIKE and ILIKE are
unsupported on all six text variants in EQL v3.
5. "Each column gets its own encryption key" — the key is per value.
6. "Every value gets its own key, bound to an identity" — identity binding
needs OidcFederationStrategy plus a lock context, and is not what this page
sets up.
Also drops "The index terms are themselves encrypted, they reveal nothing about
the plaintext", which contradicts the leakage tables on the security and FHE
pages. Replaced with the true, narrower claim: an equality term reveals which
rows share a value.
Restructured as a Diátaxis tutorial with Steps, adds the missing CREATE TABLE
step (the old page never showed the encrypted column type), and links each
mechanism to its canonical page.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
stash 0.17.1 does support EQL v3; it is not the default. Verified against
`npx stash@0.17.1 manifest --json`:
eql install / eql upgrade
--eql-version <2|3> EQL generation to target. [default: 2]
`stash init` runs `eql install` as part of its EQL phase (the CLI's own help
says so) with no way to pass the flag, so init lands EQL v2. `eql install` is
idempotent and skips when EQL is present, so switching afterwards needs
--force.
Adds a step: `npx stash eql install --eql-version 3 --force`, then
`stash eql status` to confirm. Notes that the default becomes 3 in a future
release and the step then goes away, and that v3 is a direct install so
--drizzle, --migration and --latest do not apply to it.
Defers to /reference/eql for the full option set rather than restating it.
|
Pushed
The wrinkle: New step 2: npx stash eql install --eql-version 3 --force
npx stash eql status
I initially wrote that Supabase needs Build passes, all gates green. Rendered text verified for both commands and the callout. |
IA.md assigns /get-started no content: the section is four pages plus the component hubs, and the mental model belongs to what-is-cipherstash. But the landing page links to /get-started, so it needs a real page rather than a dead URL. Written as a link-only router: start with the Quickstart, then branch by intent (integrating, no app changes, understanding, security review). It owns no mechanics. navTitle: Overview keeps the sidebar from repeating the folder name. Landing page fixes: - The Quickstart card pointed at /stack/quickstart and the Agent skills card at /stack/reference/agent-skills. Both have v2 homes now. - Dropped "bound to an identity" from the opening line. Identity binding needs OidcFederationStrategy plus a lock context; it is not what you get by default. Same overclaim removed from the quickstart. - Removed em-dashes. Only links pages that exist. what-is-cipherstash, choose-your-stack, examples, and the hubs get added to the router as they land.
Completes the section. Both are ports with the claims checked rather than
carried across.
what-is-cipherstash: mental model, a mermaid components map, the audience
router, and the threat table. Drops three unverifiable performance claims
("100x faster than AWS KMS", "100,000x faster than FHE", "< 1ms query
overhead") because the corpus disagrees with itself: fhe.mdx says 410,000x,
and the pending benchmarks page says 14x for ZeroKMS vs AWS KMS. Numbers
belong on /reference/benchmarks, sourced once.
Also drops "identity and policy baked into every key", which overstates the
default: identity binding needs OidcFederationStrategy plus a lock context.
Adds what the source never said: the terms stored alongside ciphertext are the
price of a WHERE clause, and an equality term reveals which rows share a value.
And a "what it does not protect against" row, because a page that only lists
wins is not a threat model.
choose-your-stack: merges planning-guide and discovery-session into four
ordered decisions (SDK or Proxy, platform, ORM, identity provider), with the
at-rest / in-transit / in-use table and the dev-to-production credential path.
Notes that SDK and Proxy produce the same ciphertext, so the choice is not a
one-way door.
The section index now routes to all three pages and no longer restates the
component table that what-is-cipherstash owns.
|
Pushed Three performance claims removed
I didn't pick a winner. Performance numbers belong on Also removed "identity and policy baked into every key". That overstates the default in exactly the way the quickstart's "bound to an identity" did: identity binding needs Two things the source never saidThe trade. The old page described searchable encryption as pure upside. The new one says plainly that index terms are the price of a What it does not protect against. A threat-model section that only lists wins isn't one. The page now states that an attacker inside your running application process holds the client key and can call Other notes
Build green: pinned EQL resolves, drift check passes, 4 mermaid diagrams parse, |
Per review: the 100x AWS KMS figure describes unreleased work, so the page uses 14x, which the benches repo supports and which proxy/index.mdx already states. The FHE and sub-millisecond claims are defensible and come back. Each number now names its source rather than floating free: - Sub-millisecond query overhead, explained (operators inline into functional indexes, so Postgres does a normal index scan). - 410,000x vs FHE, linked to the open tfhe-ore-bench harness. The old page said 100,000x; fhe.mdx and searchable-encryption.mdx both say 410,000x, so the outlier was the page being ported. - Up to 14x the throughput of AWS KMS, with the reason (bulk key derivation). Also frames the trade rather than leading with speed: encryption in use is slow if you do it with FHE, and searchable encryption buys its speed with bounded, published leakage.
"The trade" read as an apology. Each scheme's leakage is bounded and published, which makes it something you choose per column rather than something that happens to you. Renamed to "Choosing what each column reveals", with a table mapping declared capability to what an observer could infer, from "nothing" upward. The point is now that the goal is not to eliminate leakage but to pick capabilities whose leakage your threat model already tolerates, and to know exactly what you picked. A column whose frequency distribution is itself the secret gets encrypted without that capability and filtered after decryption. Same reframing on the quickstart's closing bullet, which had the same tone.
- Platform table, Neon/RDS/Aurora/Cloud SQL row: drop "Nothing special." - Section 4: "Identity binding, if you need it" -> "Identity binding". Removing "if you need it" changes the framing from optional to standard, so "Most teams start without this" went with it. Replaced by the reason it matters: identity binding is what shrinks the blast radius of a compromised application process, which is the point raised on the threat-model review.
The front door.
/stack/quickstartand the/quickstartvanity URL both already redirect here.Six errors, not two
The ticket lists two known bugs. I checked every line against the shipped
stash0.17.1 and@cipherstash/stack0.19.0 rather than against the prose, and found six. Anyone who copied this page could not have got it working.WHERE cs_match_v1(email) = $1import { users } from "./encryption/schema"initscaffoldssrc/encryption/index.ts(33 references in the CLI package) and exportsusersandencryptionClientfrom itdecrypt(row.email, { column, table })decrypt(encryptedData: Encrypted)takes one argument.freeTextSearch() // WHERE email LIKE '%alice%'LIKE/ILIKEare ❌ on all six text variants in EQL v3OidcFederationStrategy+ a lock context, which this page never sets up(1) and (3) are the interesting ones.
cs_match_v1would fail loudly.decryptwith a second argument compiles fine in JS and just ignores it, so it appears to work.The false leakage claim
Also removed: "The index terms are themselves encrypted, they reveal nothing about the plaintext."
That contradicts the leakage tables on
security-architecture.mdxandcomparisons/fhe.mdx, both of which enumerate exactly what each term reveals. It is replaced with the true, narrower statement: an equality term reveals which rows share a value, so declare only the capabilities you query on.It remains live in two places on
main, and gets fixed when the canonical leakage page lands.What else changed
CREATE TABLEstep. The old page never showed how an encrypted column is declared, which is the one thing a reader cannot guess. It is nowemail public.text_eq, with the point made that the domain type is the configuration.$1::public.text_eq) rather than leaving it as a mystery cast. An untyped operand doesn't error; it silently falls through tojsonbsemantics and skips the index.Steps, and demotedstash plan/stash implto a callout: they're the agent path, and a quickstart should show the moving parts./security/cryptography,/reference/eql/core-concepts,/reference/eql/text) rather than restating it.Verification
Every API claim traced to a source, not a doc:
decrypt/encryptQuery/encryptedTablesignatures:dist/client-DfCrlHXh.d.tsanddist/index.d.tsof@cipherstash/stack@0.19.0.stash@0.17.1package's own template strings.LIKEsupport: the operator matrix in/reference/eql/text./reference/eql/core-concepts,verifiedAgainst: eql 3.0.0.Page carries
verifiedAgainst: { stack: 0.19.0, cli: 0.17.1, eql: 3.0.0 }.bun run buildpasses (716 pages),validate-content,validate-mermaid,validate-links,validate-redirectsall pass. All internal links resolve. No em-dashes.One thing to confirm
The page assumes a CLI that installs EQL 3.0.0.
stash@0.17.1's bundle still containseql_v2_encryptedcodepaths (1,562 occurrences alongside 39,333 foreql_v3), so a reader runningstash inittoday may get a v2 install and findpublic.text_eqdoesn't exist. That's the EQL 3.0.0 release-alignment issue the branch already tracks, not something this page can fix. Worth confirming before we point the vanity URL at it publicly.