From 52cb0522783c27b1cea067f0914eff816bf0af43 Mon Sep 17 00:00:00 2001 From: Dan Draper Date: Fri, 31 Jul 2026 02:21:07 +1000 Subject: [PATCH 1/3] docs(v2): make legacy redirects merge-safe - port Auth, DynamoDB, AI/RAG, and hidden security content - collapse guides and remove placeholder pages - validate every legacy redirect destination --- IA.md | 20 ++- content/docs/concepts/key-management.mdx | 2 +- .../docs/get-started/choose-your-stack.mdx | 2 +- .../docs/get-started/what-is-cipherstash.mdx | 4 +- .../{deployment/index.mdx => deployment.mdx} | 90 ++++++++++- content/docs/guides/deployment/meta.json | 4 - content/docs/guides/development/index.mdx | 9 -- content/docs/guides/development/meta.json | 4 - .../docs/guides/development/schema-design.mdx | 9 -- content/docs/guides/index.mdx | 11 +- content/docs/guides/meta.json | 2 +- ...ncrypt-existing-data.mdx => migration.mdx} | 3 +- content/docs/guides/migration/index.mdx | 19 --- content/docs/guides/migration/meta.json | 4 - content/docs/guides/troubleshooting/index.mdx | 9 -- content/docs/guides/troubleshooting/meta.json | 4 - .../troubleshooting/query-performance.mdx | 9 -- content/docs/index.mdx | 4 +- content/docs/integrations/aws/dynamodb.mdx | 143 ++++++++++++++++++ content/docs/integrations/aws/meta.json | 4 + content/docs/integrations/index.mdx | 2 +- .../docs/integrations/prisma/deployment.mdx | 2 +- content/docs/integrations/supabase/index.mdx | 2 +- .../docs/integrations/supabase/quickstart.mdx | 4 +- content/docs/reference/auth/access-keys.mdx | 63 ++++++++ content/docs/reference/auth/clients.mdx | 48 ++++++ content/docs/reference/auth/index.mdx | 28 +++- content/docs/reference/auth/meta.json | 2 +- .../reference/auth/oidc-configuration.mdx | 73 +++++++++ content/docs/reference/eql/indexes.mdx | 2 +- content/docs/reference/eql/v2/index.mdx | 12 +- content/docs/reference/eql/v2/indexes.mdx | 4 +- content/docs/reference/eql/v2/payload.mdx | 8 +- content/docs/reference/eql/v2/queries.mdx | 2 +- content/docs/reference/glossary.mdx | 12 +- content/docs/reference/index.mdx | 9 +- content/docs/reference/meta.json | 2 +- .../docs/reference/proxy/configuration.mdx | 2 +- content/docs/reference/proxy/multitenant.mdx | 2 +- content/docs/reference/stack/usage.mdx | 21 +++ content/docs/reference/workspace/billing.mdx | 2 +- .../reference/workspace/configuration.mdx | 2 +- content/docs/reference/workspace/index.mdx | 2 +- content/docs/reference/workspace/members.mdx | 2 +- content/docs/security/audit-logging.mdx | 51 ++++++- content/docs/security/compliance/index.mdx | 46 +++++- content/docs/security/cryptography.mdx | 6 +- content/docs/security/cts.mdx | 60 +++++++- content/docs/security/meta.json | 9 +- content/docs/solutions/ai-and-rag.mdx | 84 ++++++++++ content/docs/solutions/data-residency.mdx | 6 +- content/docs/solutions/meta.json | 2 +- content/docs/solutions/provable-access.mdx | 6 +- next.config.mjs | 39 ++++- scripts/validate-v2-redirects.ts | 72 ++++++++- v2-redirects.mjs | 101 ++++++++++--- 56 files changed, 959 insertions(+), 187 deletions(-) rename content/docs/guides/{deployment/index.mdx => deployment.mdx} (60%) delete mode 100644 content/docs/guides/deployment/meta.json delete mode 100644 content/docs/guides/development/index.mdx delete mode 100644 content/docs/guides/development/meta.json delete mode 100644 content/docs/guides/development/schema-design.mdx rename content/docs/guides/{migration/encrypt-existing-data.mdx => migration.mdx} (99%) delete mode 100644 content/docs/guides/migration/index.mdx delete mode 100644 content/docs/guides/migration/meta.json delete mode 100644 content/docs/guides/troubleshooting/index.mdx delete mode 100644 content/docs/guides/troubleshooting/meta.json delete mode 100644 content/docs/guides/troubleshooting/query-performance.mdx create mode 100644 content/docs/integrations/aws/dynamodb.mdx create mode 100644 content/docs/integrations/aws/meta.json create mode 100644 content/docs/reference/auth/access-keys.mdx create mode 100644 content/docs/reference/auth/clients.mdx create mode 100644 content/docs/reference/auth/oidc-configuration.mdx create mode 100644 content/docs/solutions/ai-and-rag.mdx diff --git a/IA.md b/IA.md index 8ba7d33..a3b8254 100644 --- a/IA.md +++ b/IA.md @@ -310,7 +310,7 @@ Two notes: - [x] `/integrations/drizzle` — overview and generated `@cipherstash/stack-drizzle` API reference - [x] `/integrations/prisma` — Prisma ORM 8 RC, EQL v3, Prisma Postgres, Prisma Compute, and generated API reference - [ ] `/integrations/aws/rds-aurora` — Proxy path -- [ ] `/integrations/aws/dynamodb` +- [x] `/integrations/aws/dynamodb` — Stack 1.0 helper, equality lookups, and legacy reads - [ ] `/integrations/clerk` - [ ] `/integrations/auth0` — end-to-end example (Clerk parity) - [ ] `/integrations/okta` — end-to-end example (Clerk parity) @@ -349,13 +349,11 @@ and the old `/compare/*` paths redirect there (`v2-redirects.mjs`). - [ ] `/guides/development/schema-design` — which encrypted type/variant per column (CIP-3327) - [ ] `/guides/development/testing-and-ci` (port deploy/testing) - [ ] `/guides/development/team-onboarding` (port) -- [ ] `/guides/migration/encrypt-existing-data` — the backfill guide, runnable (CIP-3329) +- [x] `/guides/migration` — the backfill guide, runnable (CIP-3329) - [ ] ⛔ `/guides/migration/upgrading-from-eql-v2` — REQUIRED; mechanics pending product answer (CIP-3329) - [ ] `/guides/migration/adopting-incrementally` (CIP-3329) - [ ] `/guides/migration/key-rotation-operations` -- [ ] `/guides/deployment/going-to-production` (port) -- [ ] `/guides/deployment/serverless-and-bundling` (merge bundling + sst) -- [ ] `/guides/deployment/proxy-deployment` (merge proxy Docker + aws-ecs) +- [x] `/guides/deployment` — production rollout, bundling, CI, onboarding, and Proxy deployment consolidated into one page - [ ] `/guides/troubleshooting` index — symptom-based router - [ ] `/guides/troubleshooting/query-performance` — seq-scan diagnosis, typed-operand gotcha - [ ] `/guides/troubleshooting/runtime-errors` @@ -367,14 +365,14 @@ and the old `/compare/*` paths redirect there (`v2-redirects.mjs`). - [x] Section scaffold 🚧 - [x] `/security/cryptography` — ONE reconciled ZeroKMS mechanism story (kills the 3 conflicting accounts) - [ ] `/security/zerokms` -- [ ] `/security/cts` — auth layer architecture (CIP-3330) +- [x] `/security/cts` — auth layer architecture (hidden from nav until the section is expanded) - [ ] `/security/stack-sdk` - [ ] `/security/proxy` - [ ] `/security/threat-scenarios` - [ ] ⛔ `/security/availability-and-continuity` — DR (port) + SLA + exit story; pending SLA answer -- [ ] ⛔ `/security/audit-logging` — pending retention answer +- [x] `/security/audit-logging` — Proxy event mechanics; retention/export remains deployment-specific and the nav stays hidden - [ ] ⛔ `/security/key-ownership` — BYOK/self-hosted; pending product answer -- [ ] `/security/compliance` index — framework mapping (port, good) +- [x] `/security/compliance` index — capability/responsibility mapping (nav remains hidden) - [ ] `/security/compliance/hipaa` — BAA scope, §164.312 mapping (CIP-3332) - [ ] `/security/compliance/soc2` — verify Type II report exists - [ ] `/security/compliance/gdpr` @@ -384,7 +382,7 @@ and the old `/compare/*` paths redirect there (`v2-redirects.mjs`). - [x] Section scaffold 🚧 - [ ] `/solutions/protecting-pii` (new) - [ ] `/solutions/healthcare-hipaa` (new; pairs with compliance/hipaa) -- [ ] `/solutions/ai-and-rag` (port use-cases/ai-rag) +- [x] `/solutions/ai-and-rag` — source protection, retrieval flow, and isolation boundaries - [ ] `/solutions/data-residency` (port) - [ ] `/solutions/provable-access` (port) @@ -419,8 +417,8 @@ and the old `/compare/*` paths redirect there (`v2-redirects.mjs`). - **Auth (CIP-3330):** - [ ] `/reference/auth/lock-contexts` - [ ] `/reference/auth/cts-tokens` -- [ ] `/reference/auth/oidc-configuration` -- [ ] `/reference/auth/access-keys` (+ clients) +- [x] `/reference/auth/oidc-configuration` +- [x] `/reference/auth/access-keys` + `/reference/auth/clients` - **CLI / Proxy / Workspace:** - [ ] `/reference/cli/*` (port 9 pages) - [ ] `/reference/proxy/*` (configuration, message-flow, multitenant, errors) diff --git a/content/docs/concepts/key-management.mdx b/content/docs/concepts/key-management.mdx index 136897f..2216f64 100644 --- a/content/docs/concepts/key-management.mdx +++ b/content/docs/concepts/key-management.mdx @@ -161,7 +161,7 @@ The design deliberately keeps the durable pieces small and separated: | ZeroKMS | Authority keys and access-control state | | Nowhere | Plaintext data keys | -Your encrypted data stays in your database. ZeroKMS handles key material, not application data. This separation also shapes recovery: restoring ZeroKMS restores the ability to reproduce keys; it does not require CipherStash to restore or move your encrypted database. See [Disaster recovery](/stack/cipherstash/kms/disaster-recovery). +Your encrypted data stays in your database. ZeroKMS handles key material, not application data. This separation also shapes recovery: restoring ZeroKMS restores the ability to reproduce keys; it does not require CipherStash to restore or move your encrypted database. Multi-region application patterns are covered in [Data residency](/solutions/data-residency#multi-region-with-regional-key-isolation). ## ZeroKMS and searchable encryption diff --git a/content/docs/get-started/choose-your-stack.mdx b/content/docs/get-started/choose-your-stack.mdx index b6cc077..d083f18 100644 --- a/content/docs/get-started/choose-your-stack.mdx +++ b/content/docs/get-started/choose-your-stack.mdx @@ -33,7 +33,7 @@ EQL installs into any Postgres you can connect to. It needs no extension, no sup | Neon, RDS, Aurora, Cloud SQL | Yes | `stash eql install` detects a non-superuser role and adapts. | | Self-hosted Postgres | Yes | Full operator-class support, so the ORE ordering mechanism is available. | | Postgres behind PgBouncer | Yes | Transaction pooling is fine. Proxy has its own pooling. | -| DynamoDB | Yes, without EQL | [DynamoDB integration](/stack/cipherstash/encryption/dynamodb). Encrypted attributes and HMAC key lookups; no EQL, because there's no Postgres. | +| DynamoDB | Yes, without EQL | [DynamoDB integration](/integrations/aws/dynamodb). Encrypted attributes and HMAC key lookups; no EQL, because there's no Postgres. | | MySQL, MongoDB, others | Not yet | The SDK still encrypts values for you, but nothing is queryable server-side. | diff --git a/content/docs/get-started/what-is-cipherstash.mdx b/content/docs/get-started/what-is-cipherstash.mdx index 032df5d..d93efb1 100644 --- a/content/docs/get-started/what-is-cipherstash.mdx +++ b/content/docs/get-started/what-is-cipherstash.mdx @@ -62,7 +62,7 @@ flowchart TB You rarely need all of them at once. The Stack SDK plus EQL is the common case. Proxy is the alternative to the SDK, not an addition to it. -Note that **EQL is only for Postgres**. The SDK also encrypts values that never touch a database, and non-Postgres stores like [DynamoDB](/stack/cipherstash/encryption/dynamodb), with no EQL involved. +Note that **EQL is only for Postgres**. The SDK also encrypts values that never touch a database, and non-Postgres stores like [DynamoDB](/integrations/aws/dynamodb), with no EQL involved. ## Choosing what each column reveals @@ -99,7 +99,7 @@ Encryption in use is usually assumed to be slow. It is, if you do it with fully | vs fully homomorphic encryption | [410,000x faster](https://github.com/cipherstash/tfhe-ore-bench) on the per-row primitives a database actually executes. | | vs AWS KMS | Up to 14x the throughput, because ZeroKMS derives keys in bulk rather than one call per value. | -The FHE comparison is an open benchmark harness you can run yourself. See [CipherStash vs FHE](/stack/reference/comparisons/fhe) for the methodology, and for the workloads where FHE is genuinely the right tool. +The FHE comparison is an open benchmark harness you can run yourself. See [CipherStash vs FHE](/concepts/compare/fhe) for the methodology, and for the workloads where FHE is genuinely the right tool. ## What it protects against diff --git a/content/docs/guides/deployment/index.mdx b/content/docs/guides/deployment.mdx similarity index 60% rename from content/docs/guides/deployment/index.mdx rename to content/docs/guides/deployment.mdx index 12b5ade..49778ca 100644 --- a/content/docs/guides/deployment/index.mdx +++ b/content/docs/guides/deployment.mdx @@ -1,6 +1,5 @@ --- title: Deployment -navTitle: Overview description: "Deploy CipherStash changes safely across environments, with explicit credential, migration, rollout, monitoring, and rollback gates." type: guide components: [encryption, eql, cli] @@ -9,7 +8,7 @@ audience: [developer] CipherStash deployments coordinate application code, database schema, and credentials. Treat each independently deployable stage as a release with its own verification and rollback gate. -For the detailed dual-write, backfill, and cutover procedure for populated columns, follow [Encrypt existing data](/guides/migration/encrypt-existing-data). This page covers the production rollout around that migration. +For the detailed dual-write, backfill, and cutover procedure for populated columns, follow [Data migration](/guides/migration). This page covers the production rollout around that migration. ## Before the first deployment @@ -96,3 +95,90 @@ Test the correctness of returned values, not only request success rates. An inco Prefer application rollbacks while both plaintext and encrypted columns are being maintained. Before plaintext removal, a read cutover can be reversed without changing data: point reads back to the original columns and leave dual-writes running. After plaintext is dropped, rollback becomes a data-recovery operation. Avoid reaching that stage until operational evidence shows the encrypted path is stable and every supported application version has moved forward. + +## Serverless and bundled applications + +`@cipherstash/stack` must remain a runtime dependency when a build tool creates +a server bundle. Configure the bundler to leave the package external, then make +sure the deployment artifact installs it for the target platform. + +For Next.js, use `serverExternalPackages`: + +```typescript filename="next.config.ts" +const nextConfig = { + serverExternalPackages: ["@cipherstash/stack"], +} + +export default nextConfig +``` + +For esbuild, pass `--external:@cipherstash/stack`; for webpack, declare it as a +CommonJS external. An SST function needs both the esbuild exclusion and a +runtime install: + +```typescript filename="sst.config.ts" +{ + nodejs: { + esbuild: { external: ["@cipherstash/stack"] }, + install: ["@cipherstash/stack"], + }, +} +``` + +Build and start the artifact in a Linux container before release when local +development uses macOS or Windows. This catches a lockfile or deployment bundle +that omitted the target platform's runtime package. + +## Testing and CI + +Use a separate workspace and database for integration tests. Supply the test +workspace's four `CS_*` credentials only to the CI job, install the same EQL +release used by production, and test: + +- encryption followed by decryption returns the original typed value; +- every required equality, range, ordering, token, or JSON query returns the + expected records; +- a client without the required keyset grant cannot decrypt the data; +- schema migrations and application code work in both rolling-deployment + orders. + +Mocks are useful for business logic that merely passes encrypted values +through, but they do not test ciphertext compatibility, key access, query +terms, or EQL behavior. Keep at least one real end-to-end test for every +encrypted query capability used in production. + +## Team onboarding + +Invite each developer to the organization and workspace, then have them run: + +```bash +npx stash init +``` + +Each device receives its own developer identity and client key. Do not share +production credentials with developers or copy one developer's local profile +into CI. Remove a departing developer from the workspace and revoke their +device client keys without changing application credentials. + +See [workspace members](/reference/workspace/members) and +[client keys](/reference/auth/clients) for the complete access model. + +## Deploying Proxy + +Run CipherStash Proxy as a container in the same private network as PostgreSQL. +Supply database credentials and CipherStash application credentials from the +platform's secret manager, expose the PostgreSQL listener only to application +clients, and expose health or metrics endpoints only to the monitoring plane. + +For ECS, Kubernetes, or another orchestrator, keep these concerns separate: + +1. Pull the published Proxy image from the approved registry. +2. Grant the task or pod permission to read only its deployment secrets. +3. Configure security groups or network policies for application → Proxy and + Proxy → database traffic. +4. Roll out at least two instances where connection availability requires it. +5. Verify a plaintext write, encrypted database value, encrypted query, and + decrypted read through the deployed endpoint. + +Use the [Proxy configuration reference](/reference/proxy/configuration) for +environment variables, TOML settings, ports, logging, and metrics. diff --git a/content/docs/guides/deployment/meta.json b/content/docs/guides/deployment/meta.json deleted file mode 100644 index 0bbd244..0000000 --- a/content/docs/guides/deployment/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Deployment", - "pages": ["index", "..."] -} diff --git a/content/docs/guides/development/index.mdx b/content/docs/guides/development/index.mdx deleted file mode 100644 index b1a735e..0000000 --- a/content/docs/guides/development/index.mdx +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: Development -navTitle: Overview -description: "Development documentation — being built as part of the docs V2 overhaul." ---- - -This section is being built as part of the docs V2 overhaul ([CIP-3307](https://linear.app/cipherstash/issue/CIP-3307)). Track progress in [IA.md](https://github.com/cipherstash/docs/blob/v2/IA.md). - -Until it lands, current documentation lives in the [existing docs](/stack). diff --git a/content/docs/guides/development/meta.json b/content/docs/guides/development/meta.json deleted file mode 100644 index 24f8878..0000000 --- a/content/docs/guides/development/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Development", - "pages": ["index", "..."] -} diff --git a/content/docs/guides/development/schema-design.mdx b/content/docs/guides/development/schema-design.mdx deleted file mode 100644 index b11397f..0000000 --- a/content/docs/guides/development/schema-design.mdx +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: Schema design -description: "Choosing the right encrypted type and capability for each column." -type: guide ---- - -This page is being built as part of the docs V2 overhaul ([CIP-3327](https://linear.app/cipherstash/issue/CIP-3327)). Track progress in [IA.md](https://github.com/cipherstash/docs/blob/v2/IA.md). - -Until it lands, [EQL core concepts](/reference/eql/core-concepts) covers the capability model, and the per-type pages ([numbers](/reference/eql/numbers), [dates & times](/reference/eql/dates-and-times), [text](/reference/eql/text), [JSON](/reference/eql/json)) cover choosing variants. diff --git a/content/docs/guides/index.mdx b/content/docs/guides/index.mdx index 32279bf..3f85df0 100644 --- a/content/docs/guides/index.mdx +++ b/content/docs/guides/index.mdx @@ -1,10 +1,11 @@ --- title: Guides -navTitle: Overview -description: "Task-oriented guides: development workflow, data migration, deployment, and troubleshooting." +description: "Deployment and data migration guidance for CipherStash." type: guide --- -This section is being built as part of the docs V2 overhaul ([CIP-3307](https://linear.app/cipherstash/issue/CIP-3307)). Track progress in [IA.md](https://github.com/cipherstash/docs/blob/v2/IA.md). - -Until it lands, current documentation lives in the [existing docs](/stack). +{/* + Fumadocs requires an index document when generating a metadata-backed folder. + meta.json excludes this file from the page tree, and Next redirects its route + to Deployment. +*/} diff --git a/content/docs/guides/meta.json b/content/docs/guides/meta.json index 810cf95..1f31180 100644 --- a/content/docs/guides/meta.json +++ b/content/docs/guides/meta.json @@ -1,5 +1,5 @@ { "title": "Guides", "icon": "Wrench", - "pages": ["index", "..."] + "pages": ["!index", "deployment", "migration", "..."] } diff --git a/content/docs/guides/migration/encrypt-existing-data.mdx b/content/docs/guides/migration.mdx similarity index 99% rename from content/docs/guides/migration/encrypt-existing-data.mdx rename to content/docs/guides/migration.mdx index 69c6b0a..7265075 100644 --- a/content/docs/guides/migration/encrypt-existing-data.mdx +++ b/content/docs/guides/migration.mdx @@ -1,5 +1,6 @@ --- -title: Encrypt existing data +title: Data migration +navTitle: Data migration description: "Move populated plaintext columns to searchable encryption with dual-writes, a resumable backfill, and a reversible read cutover." type: guide components: [encryption, eql, cli] diff --git a/content/docs/guides/migration/index.mdx b/content/docs/guides/migration/index.mdx deleted file mode 100644 index 5820d97..0000000 --- a/content/docs/guides/migration/index.mdx +++ /dev/null @@ -1,19 +0,0 @@ ---- -title: Data migration -navTitle: Overview -description: "Safely introduce CipherStash encryption to existing schemas and populated production data." -type: guide -components: [encryption, eql, cli] -audience: [developer] ---- - -Encryption migrations coordinate schema changes, application writes, historical data, and read cutovers. Keep each stage independently verifiable and reversible until the plaintext data is intentionally removed. - - - - Add encrypted mirrors, deploy dual-writes, backfill historical rows, cut reads over, and remove plaintext safely. - - - Plan environment promotion, credentials, release gates, monitoring, and rollback. - - diff --git a/content/docs/guides/migration/meta.json b/content/docs/guides/migration/meta.json deleted file mode 100644 index db4e68b..0000000 --- a/content/docs/guides/migration/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Data migration", - "pages": ["index", "..."] -} diff --git a/content/docs/guides/troubleshooting/index.mdx b/content/docs/guides/troubleshooting/index.mdx deleted file mode 100644 index b49feca..0000000 --- a/content/docs/guides/troubleshooting/index.mdx +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: Troubleshooting -navTitle: Overview -description: "Troubleshooting documentation — being built as part of the docs V2 overhaul." ---- - -This section is being built as part of the docs V2 overhaul ([CIP-3307](https://linear.app/cipherstash/issue/CIP-3307)). Track progress in [IA.md](https://github.com/cipherstash/docs/blob/v2/IA.md). - -Until it lands, current documentation lives in the [existing docs](/stack). diff --git a/content/docs/guides/troubleshooting/meta.json b/content/docs/guides/troubleshooting/meta.json deleted file mode 100644 index e5715ad..0000000 --- a/content/docs/guides/troubleshooting/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Troubleshooting", - "pages": ["index", "..."] -} diff --git a/content/docs/guides/troubleshooting/query-performance.mdx b/content/docs/guides/troubleshooting/query-performance.mdx deleted file mode 100644 index a128a95..0000000 --- a/content/docs/guides/troubleshooting/query-performance.mdx +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: Query performance -description: "Diagnosing and fixing slow queries on encrypted columns." -type: guide ---- - -This page is being built as part of the docs V2 overhaul ([CIP-3351](https://linear.app/cipherstash/issue/CIP-3351)). Track progress in [IA.md](https://github.com/cipherstash/docs/blob/v2/IA.md). - -Until it lands, the EQL reference covers the essentials: [Indexes](/reference/eql/indexes) walks through `EXPLAIN` verification and large-table index builds, and [Sorting](/reference/eql/sorting) covers extractor-form sort keys. diff --git a/content/docs/index.mdx b/content/docs/index.mdx index 216b22a..8f3f6f7 100644 --- a/content/docs/index.mdx +++ b/content/docs/index.mdx @@ -24,10 +24,10 @@ agent, a curious insider: they all see ciphertext with no key. - + - + ## AI-ready documentation diff --git a/content/docs/integrations/aws/dynamodb.mdx b/content/docs/integrations/aws/dynamodb.mdx new file mode 100644 index 0000000..4a9bb1a --- /dev/null +++ b/content/docs/integrations/aws/dynamodb.mdx @@ -0,0 +1,143 @@ +--- +title: DynamoDB +description: "Encrypt DynamoDB attributes with @cipherstash/stack, including bulk operations and equality lookups over HMAC attributes." +type: guide +components: [encryption, platform] +audience: [developer] +integration: + category: platform + setup: code-only +verifiedAgainst: + stack: "1.0.0" +--- + +CipherStash's DynamoDB helper encrypts and decrypts items without wrapping the +AWS SDK. Your application keeps control of every `PutCommand`, `GetCommand`, +and `QueryCommand`; `encryptedDynamoDB` transforms the item immediately before +a write and after a read. + +DynamoDB does not use EQL. Equality-enabled attributes store an HMAC search +term alongside the ciphertext so they can participate in key conditions. + +## Install + +```bash cta cta-type="install" example-id="install-dynamodb" +npm install @cipherstash/stack @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb +``` + +## Define the encrypted attributes + +Use the v3 schema builder. `TextEq` produces an equality term; `Text` is +storage-only. + +```typescript filename="encryption.ts" +import { Encryption } from "@cipherstash/stack" +import { encryptedDynamoDB } from "@cipherstash/stack/dynamodb" +import { encryptedTable, types } from "@cipherstash/stack/v3" + +export const users = encryptedTable("users", { + email: types.TextEq("email"), + name: types.Text("name"), +}) + +export const encryptionClient = await Encryption({ schemas: [users] }) +export const dynamo = encryptedDynamoDB({ encryptionClient }) +``` + +For an equality-enabled `email`, the encrypted item contains: + +| Attribute | Purpose | +| --- | --- | +| `email__source` | Ciphertext used for decryption | +| `email__hmac` | Equality term used in a partition key, sort key, or GSI | + +Non-encrypted attributes pass through unchanged. Ordering and free-text terms +have no DynamoDB query surface and are not stored. + +## Encrypt and write an item + +```typescript filename="write-user.ts" +import { PutCommand } from "@aws-sdk/lib-dynamodb" +import { docClient } from "./aws" +import { dynamo, users } from "./encryption" + +const encrypted = await dynamo.encryptModel( + { pk: "user#1", email: "alice@example.com", name: "Alice", role: "admin" }, + users, +) + +if (encrypted.failure) throw encrypted.failure + +await docClient.send(new PutCommand({ + TableName: "Users", + Item: encrypted.data, +})) +``` + +The result retains `pk` and `role`, replaces `email` with `email__source` and +`email__hmac`, and replaces `name` with `name__source`. + +## Read and decrypt + +```typescript filename="read-user.ts" +import { GetCommand } from "@aws-sdk/lib-dynamodb" + +const stored = await docClient.send(new GetCommand({ + TableName: "Users", + Key: { pk: "user#1" }, +})) + +const decrypted = await dynamo.decryptModel(stored.Item ?? {}, users) +if (decrypted.failure) throw decrypted.failure + +console.log(decrypted.data.email) +``` + +Use `bulkEncryptModels` and `bulkDecryptModels` before `BatchWriteCommand` and +after `BatchGetCommand` to process many items in one ZeroKMS round trip. + +## Query an equality-enabled attribute + +Encrypt the operand with the same table and column definition, then use its +HMAC as the DynamoDB key value: + +```typescript filename="find-by-email.ts" +import { QueryCommand } from "@aws-sdk/lib-dynamodb" + +const term = await encryptionClient.encryptQuery("alice@example.com", { + table: users, + column: users.email, +}) + +if (term.failure) throw term.failure + +const result = await docClient.send(new QueryCommand({ + TableName: "Users", + IndexName: "EmailIndex", + KeyConditionExpression: "email__hmac = :email", + ExpressionAttributeValues: { ":email": term.data.hm }, +})) + +const decrypted = await dynamo.bulkDecryptModels(result.Items ?? [], users) +if (decrypted.failure) throw decrypted.failure +``` + +An encrypted HMAC attribute can back a table key or GSI. Ciphertext source +attributes do not support ranges, `begins_with`, or `contains`. + +## Read items written by EQL v2 + +Keep the current v3 table declaration and opt into legacy decoding while old +items remain: + +```typescript +const decrypted = await dynamo.decryptModel(storedItem, users, { + storedEqlVersion: 2, +}) +``` + +New writes always use the current format. Remove the option after every legacy +item has been rewritten. + +For every method, option, and return type, see the generated +[DynamoDB API reference](/reference/stack/api-reference/dynamodb). diff --git a/content/docs/integrations/aws/meta.json b/content/docs/integrations/aws/meta.json new file mode 100644 index 0000000..12b54e1 --- /dev/null +++ b/content/docs/integrations/aws/meta.json @@ -0,0 +1,4 @@ +{ + "title": "AWS", + "pages": ["dynamodb"] +} diff --git a/content/docs/integrations/index.mdx b/content/docs/integrations/index.mdx index 04359af..346f52b 100644 --- a/content/docs/integrations/index.mdx +++ b/content/docs/integrations/index.mdx @@ -39,7 +39,7 @@ How your application queries it. -Not on this list? Any ORM works without an integration — encrypt before a write and decrypt after a read with the [Stack SDK](/reference/stack), which is what the integrations do for you. [CipherStash Proxy](/reference/proxy) covers the case where you cannot change the application at all, and the SDK also encrypts values that never reach Postgres, including non-Postgres stores like [DynamoDB](/stack/cipherstash/encryption/dynamodb). +Not on this list? Any ORM works without an integration — encrypt before a write and decrypt after a read with the [Stack SDK](/reference/stack), which is what the integrations do for you. [CipherStash Proxy](/reference/proxy) covers the case where you cannot change the application at all, and the SDK also encrypts values that never reach Postgres, including non-Postgres stores like [DynamoDB](/integrations/aws/dynamodb). ## Still deciding diff --git a/content/docs/integrations/prisma/deployment.mdx b/content/docs/integrations/prisma/deployment.mdx index 8bc63cc..f5d24a9 100644 --- a/content/docs/integrations/prisma/deployment.mdx +++ b/content/docs/integrations/prisma/deployment.mdx @@ -15,7 +15,7 @@ verifiedAgainst: Prisma owns the complete database migration graph for this integration, including the EQL bundle, encrypted columns, and their functional indexes. Application releases still need to be staged carefully when a plaintext column already contains data. -For the reasoning, rollback rules, and adapter-independent sequence, read [Encrypt existing data](/guides/migration/encrypt-existing-data) and the [deployment guide](/guides/deployment). This page maps that process to Prisma ORM 8. +For the reasoning, rollback rules, and adapter-independent sequence, read [Data migration](/guides/migration) and the [deployment guide](/guides/deployment). This page maps that process to Prisma ORM 8. ## New tables and empty columns diff --git a/content/docs/integrations/supabase/index.mdx b/content/docs/integrations/supabase/index.mdx index dba917b..89289a5 100644 --- a/content/docs/integrations/supabase/index.mdx +++ b/content/docs/integrations/supabase/index.mdx @@ -39,7 +39,7 @@ With EQL 3.0.4, the Supabase wrapper supports encrypted equality, range, and ord | Encrypt inside Supabase Edge Functions | [Edge Functions](/integrations/supabase/edge-functions) | | Choose local or deployment credentials | [Workspace credentials](/reference/workspace/configuration#credentials) | | Use an ORM against Supabase Postgres | [Drizzle](/integrations/drizzle) or [Prisma ORM 8](/integrations/prisma) | -| Encrypt columns that already contain data | [Encrypt existing data](/guides/migration/encrypt-existing-data) | +| Encrypt columns that already contain data | [Data migration](/guides/migration) | | Plan a production rollout or rollback | [Deployment guide](/guides/deployment) | ## Supabase, Drizzle, and Prisma diff --git a/content/docs/integrations/supabase/quickstart.mdx b/content/docs/integrations/supabase/quickstart.mdx index 7480ee7..c85343d 100644 --- a/content/docs/integrations/supabase/quickstart.mdx +++ b/content/docs/integrations/supabase/quickstart.mdx @@ -15,7 +15,7 @@ verifiedAgainst: This tutorial creates a Supabase table with one encrypted column, writes a value through the Supabase JavaScript client, and queries it without sending plaintext to Postgres. -It is designed for a new table or an empty column. If the column already contains production data, use [Encrypt existing data](/guides/migration/encrypt-existing-data) instead. +It is designed for a new table or an empty column. If the column already contains production data, use [Data migration](/guides/migration) instead. ## Before you start @@ -161,5 +161,5 @@ The Table Editor cannot decrypt or meaningfully filter the encrypted value becau - Learn the supported mutations, filters, ordering, response types, and errors in the [supabase-js guide](/integrations/supabase/supabase-js). - Use the complete [`encryptedSupabase` API reference](/integrations/supabase/api-reference) when you need method-level detail. - Add functional indexes when the table reaches meaningful row counts by following [EQL indexes](/reference/eql/indexes). -- For a populated table, follow the safe dual-write and backfill process in [Encrypt existing data](/guides/migration/encrypt-existing-data). +- For a populated table, follow the safe dual-write and backfill process in [Data migration](/guides/migration). - To use a direct Postgres ORM connection, continue with [Drizzle](/integrations/drizzle) or [Prisma ORM 8](/integrations/prisma). diff --git a/content/docs/reference/auth/access-keys.mdx b/content/docs/reference/auth/access-keys.mdx new file mode 100644 index 0000000..90cf000 --- /dev/null +++ b/content/docs/reference/auth/access-keys.mdx @@ -0,0 +1,63 @@ +--- +title: Access keys +description: "Create and manage access keys for production applications, CI, and workspace automation." +type: reference +components: [platform] +audience: [developer] +--- + +Access keys authenticate non-interactive requests to CipherStash services such +as ZeroKMS and CTS. A deployed Stack SDK or Proxy client uses an access key +alongside its [client key](/reference/auth/clients). + +Local development normally does not need one. `stash init` creates a +device-backed session and client key for the current developer. Use access +keys for production, CI, or workspace automation where an interactive login is +not available. + +## Create an access key + +Open **Access Keys** for the workspace in the +[CipherStash Dashboard](https://dashboard.cipherstash.com/workspaces/_/access-keys), +select **Create access key**, and choose the least-privileged role that covers +the workload. + +The key value is shown when it is created. Store it in the deployment's secret +manager as `CS_CLIENT_ACCESS_KEY`; do not commit it or share a developer key +with production. + +## Roles + +### Member + +Use `member` for applications performing cryptographic operations. It can list +keysets and generate or retrieve data-key material: + +```text +keyset:list +data_key:generate +data_key:retrieve +``` + +### Control + +Use `control` for workspace automation that manages keysets and client access. +It can create, list, enable, disable, grant, modify, and revoke keysets, and +list clients. It cannot derive data keys. + +### Admin + +`admin` combines service administration and cryptographic permissions. Avoid +it in production applications: use `member` for runtime operations and +`control` for automation so a compromised credential has the smallest useful +scope. + +## Rotate or revoke a key + +Create the replacement first, update every consumer, verify that the new key +is in use, and then delete the old key from the Dashboard. Rotating an access +key changes authentication; it does not re-encrypt stored data. + +See [workspace configuration](/reference/workspace/configuration) for the full +production credential set and [Deployment](/guides/deployment) for rollout +gates. diff --git a/content/docs/reference/auth/clients.mdx b/content/docs/reference/auth/clients.mdx new file mode 100644 index 0000000..0cc78aa --- /dev/null +++ b/content/docs/reference/auth/clients.mdx @@ -0,0 +1,48 @@ +--- +title: Client keys +description: "Understand device-backed and application client keys, keyset grants, revocation, and deployment use." +type: reference +components: [platform, encryption] +audience: [developer] +--- + +A client key is the application-held half of CipherStash's split key +architecture. It identifies a developer device or deployed application and is +granted access to one or more keysets. ZeroKMS never receives the client key. + +## Client key types + +### Device-backed client keys + +`stash init` creates a device-backed client key for the current developer and +grants it access to the workspace's default keyset. + +- Use it for local development only. +- Each developer and device gets a distinct key. +- Revoking one device does not affect other developers or production. +- No `CS_*` environment variables are required while the device session is + active. + +### Application client keys + +Create an application client key for production, CI, background jobs, or +Proxy—any environment without an interactive device login. + +Open **Clients** in the +[CipherStash Dashboard](https://dashboard.cipherstash.com/workspaces/_/clients), +select **Create a new client**, and grant only the keysets that workload needs. +Store its ID and key value as `CS_CLIENT_ID` and `CS_CLIENT_KEY`. + +## Keyset access + +A client can use only the keysets it has been granted. Separate keysets are a +cryptographic isolation boundary: a client without the grant cannot derive the +keys required to decrypt that data. + +Grant access before deploying a workload. To revoke it, remove the client from +the keyset or delete the client. Revocation prevents future key derivation but +does not modify ciphertext already stored in your database. + +See [key management](/concepts/key-management#keysets-define-the-isolation-boundary) +for the isolation model and [workspace configuration](/reference/workspace/configuration) +for production environment variables. diff --git a/content/docs/reference/auth/index.mdx b/content/docs/reference/auth/index.mdx index 220fa84..afd6298 100644 --- a/content/docs/reference/auth/index.mdx +++ b/content/docs/reference/auth/index.mdx @@ -1,9 +1,31 @@ --- title: Auth navTitle: Overview -description: "Auth documentation — being built as part of the docs V2 overhaul." +description: "Authentication and credential reference for local development, production services, and identity-bound encryption." +type: reference +components: [platform] --- -This section is being built as part of the docs V2 overhaul ([CIP-3307](https://linear.app/cipherstash/issue/CIP-3307)). Track progress in [IA.md](https://github.com/cipherstash/docs/blob/v2/IA.md). +CipherStash separates the credentials used by developers, deployed services, +and signed-in end users. Choose the mechanism that matches who is performing +the cryptographic operation. -Until it lands, current documentation lives in the [existing docs](/stack). +| Context | Authentication | Use it for | +| --- | --- | --- | +| Local development | Device session created by `stash init` | Per-developer access without shared environment variables | +| Production and CI | Application client key plus access key | Non-interactive SDK and Proxy deployments | +| Signed-in end user | OIDC federation | Identity-bound encryption and user-attributed key derivation | + +## In this section + +- [Access keys](/reference/auth/access-keys) authenticate programmatic requests + to CipherStash services and define their service-level permissions. +- [Client keys](/reference/auth/clients) supply the client-held half of the + ZeroKMS key split and scope access to keysets. +- [OIDC providers](/reference/auth/oidc-configuration) register an identity + provider for end-user federation. + +For the four environment variables used by deployed applications, see +[workspace configuration](/reference/workspace/configuration). For the +cryptographic effect of binding a value to an identity claim, see +[provable access control](/solutions/provable-access). diff --git a/content/docs/reference/auth/meta.json b/content/docs/reference/auth/meta.json index 943c560..96450d9 100644 --- a/content/docs/reference/auth/meta.json +++ b/content/docs/reference/auth/meta.json @@ -1,4 +1,4 @@ { "title": "Auth", - "pages": ["index", "..."] + "pages": ["index", "access-keys", "clients", "oidc-configuration"] } diff --git a/content/docs/reference/auth/oidc-configuration.mdx b/content/docs/reference/auth/oidc-configuration.mdx new file mode 100644 index 0000000..79a3462 --- /dev/null +++ b/content/docs/reference/auth/oidc-configuration.mdx @@ -0,0 +1,73 @@ +--- +title: OIDC providers +description: "Register an identity provider so cryptographic operations can be authenticated as the signed-in end user." +type: reference +components: [platform, encryption] +audience: [developer] +verifiedAgainst: + auth: "0.42.0" +--- + +Register an OpenID Connect provider when encryption or decryption must be +performed as the signed-in user. CipherStash federates the provider's JWT into +a short-lived service token; the application does not send an OIDC client +secret to CipherStash. + +OIDC is optional. Applications that authenticate only as a deployed service +use an [application client key](/reference/auth/clients) and +[access key](/reference/auth/access-keys) instead. + +## Supported providers + +- Auth0 +- Clerk +- Okta +- Supabase Auth + +## Register a provider + +1. Open **Authentication → OIDC Providers** for the workspace in the + [Dashboard](https://dashboard.cipherstash.com/workspaces/_/oidc-providers). +2. Select **Add OIDC Provider**. +3. Choose the vendor and enter the issuer URL from the provider's OIDC + discovery document. + +| Provider | Example issuer | +| --- | --- | +| Auth0 | `https://your-tenant.auth0.com/` | +| Clerk | `https://your-app.clerk.accounts.dev/` | +| Okta | `https://your-org.okta.com/` | +| Supabase | `https://project-ref.supabase.co/auth/v1` | + +For Supabase, the [Dashboard integration](/integrations/supabase) can register +the project issuer for you. + +CipherStash stores the provider vendor and issuer. Token signatures are +validated using the issuer's published JWKS; no provider client secret is +stored. + +## Use the provider from Stack + +Configure `OidcFederationStrategy` with a callback that returns the current +user's JWT: + +```typescript filename="encryption.ts" +import { Encryption, OidcFederationStrategy } from "@cipherstash/stack" + +const encryption = await Encryption({ + schemas: [patients], + config: { + authStrategy: OidcFederationStrategy.create( + process.env.CS_WORKSPACE_CRN, + () => getCurrentUserJwt(), + ), + }, +}) +``` + +The callback must return the current token rather than a token captured once +at startup, because the strategy invokes it again when federation needs to be +renewed. + +Continue with [provable access control](/solutions/provable-access) to bind +encryption to an identity claim using a lock context. diff --git a/content/docs/reference/eql/indexes.mdx b/content/docs/reference/eql/indexes.mdx index 6313b49..f0fab93 100644 --- a/content/docs/reference/eql/indexes.mdx +++ b/content/docs/reference/eql/indexes.mdx @@ -156,7 +156,7 @@ The first move on a slow query is `EXPLAIN (COSTS OFF)`: - **`Filter:` showing the raw operator** — inlining did not happen. Usual causes: a pinned `search_path` on a customised function, or the planner judging another plan cheaper. - **`Sort` node above an Index Scan** — natural-form `ORDER BY`. Switch the sort key to `eql_v3.ord_term(col)` to eliminate it. -Once the plan looks right, repeat with `EXPLAIN ANALYZE` to measure actual timings. For a full diagnosis walkthrough, see [query performance troubleshooting](/guides/troubleshooting/query-performance). +Once the plan looks right, repeat with `EXPLAIN ANALYZE` to measure actual timings. Compare estimated and actual row counts as well as the chosen scan; a large mismatch usually means the planner needs fresh statistics before the index definition itself is changed. ## Building indexes on large tables diff --git a/content/docs/reference/eql/v2/index.mdx b/content/docs/reference/eql/v2/index.mdx index 228407e..f406579 100644 --- a/content/docs/reference/eql/v2/index.mdx +++ b/content/docs/reference/eql/v2/index.mdx @@ -18,7 +18,7 @@ This section documents **EQL v2** (v2.2), the release existing CipherStash deplo EQL provides the database-side components needed to query encrypted data. Unlike traditional PostgreSQL extensions, EQL is implemented as a collection of types, operators, and functions, making it compatible with managed database providers like AWS RDS that restrict extension installation. -When combined with the [Encryption SDK](/stack/cipherstash/encryption) or [CipherStash Proxy](/stack/cipherstash/proxy), EQL enables: +When combined with the [Stack SDK](/reference/stack) or [CipherStash Proxy](/reference/proxy), EQL enables: - **Exact match queries** using encrypted equality operators - **Range queries** with order-preserving encryption @@ -67,7 +67,7 @@ EQL ships in the `eql_v2` schema, with functions in three groups: - **Index-term extraction** — `eql_v2.hmac_256()` (exact match), `eql_v2.bloom_filter()` (pattern matching), and `eql_v2.ore_block_u64_8_256()` (range) extract a searchable term from an encrypted value. These back the functional indexes — see [Setting up indexes](/reference/eql/v2/indexes). - **Comparison** — operators (`=`, `<`, `LIKE`, `@>`, …) are the query surface over encrypted columns. -For the complete, per-version function reference — every signature, parameter, and return type — see the [EQL API reference](/stack/reference/eql/), generated from each EQL release. +For the complete function reference — every signature, parameter, and return type — see [EQL functions](/reference/eql/functions). ## Index types @@ -77,19 +77,19 @@ EQL supports multiple searchable encryption index types. Each index type enables Enables exact equality queries and unique constraints using HMAC-SHA256. -[Learn more about exact indexes](/stack/cipherstash/encryption/searchable-encryption#exact-matching) +[Learn more about equality queries](/concepts/searchable-encryption#query-capabilities) ### `ore`: Range queries Enables range comparisons (`<`, `>`, `BETWEEN`) and ordering (`ORDER BY`) using Order Revealing Encryption. -[Learn more about range indexes](/stack/cipherstash/encryption/searchable-encryption#sorting-and-range-queries) +[Learn more about range queries](/concepts/searchable-encryption#query-capabilities) ### `match`: Pattern matching Enables substring and full-text search (`LIKE`, `ILIKE`) using encrypted Bloom filters with trigrams. -[Learn more about match indexes](/stack/cipherstash/encryption/searchable-encryption#free-text-search) +[Learn more about token matching](/concepts/searchable-encryption#query-capabilities) ### `ste_vec`: Structured data @@ -120,4 +120,4 @@ Unlike PostgreSQL extensions that require `CREATE EXTENSION`, EQL types and func ## Related documentation - [CipherCell format](/reference/eql/v2/payload): The data structure used by EQL -- [Supported queries](/stack/cipherstash/encryption/searchable-encryption): Available searchable encryption schemes +- [Searchable encryption](/concepts/searchable-encryption): Available encrypted query capabilities diff --git a/content/docs/reference/eql/v2/indexes.mdx b/content/docs/reference/eql/v2/indexes.mdx index 6a84ca4..0bbccab 100644 --- a/content/docs/reference/eql/v2/indexes.mdx +++ b/content/docs/reference/eql/v2/indexes.mdx @@ -149,6 +149,6 @@ CREATE INDEX users_age_range_idx ON users USING btree (age); ## Related - [Searchable encryption queries](/reference/eql/v2/queries): Query patterns for each index type -- [Searchable encryption overview](/stack/cipherstash/encryption/searchable-encryption): How searchable indexes work -- [Supabase integration](/stack/cipherstash/supabase): Supabase-specific setup and limitations +- [Searchable encryption overview](/concepts/searchable-encryption): How searchable indexes work +- [Supabase integration](/integrations/supabase): Supabase-specific setup and limitations - [EQL guide](/reference/eql/v2): Full reference for EQL types and functions diff --git a/content/docs/reference/eql/v2/payload.mdx b/content/docs/reference/eql/v2/payload.mdx index 3e7503a..a086ec0 100644 --- a/content/docs/reference/eql/v2/payload.mdx +++ b/content/docs/reference/eql/v2/payload.mdx @@ -157,7 +157,7 @@ Enables exact match queries using HMAC-SHA256. **Type**: Hex-encoded string (64 characters) -**Index Type**: [Exact](/stack/cipherstash/encryption/searchable-encryption#exact-matching) +**Index Type**: [Equality](/concepts/searchable-encryption#query-capabilities) ```json { @@ -171,7 +171,7 @@ Enables range queries and ordering using Order Revealing Encryption. **Type**: Array of strings -**Index Type**: [Order / Range](/stack/cipherstash/encryption/searchable-encryption#sorting-and-range-queries) +**Index Type**: [Order / Range](/concepts/searchable-encryption#query-capabilities) ```json { @@ -188,7 +188,7 @@ Enables substring and pattern matching queries using encrypted Bloom filters wit **Type**: Array of integers -**Index Type**: [Match](/stack/cipherstash/encryption/searchable-encryption#free-text-search) +**Index Type**: [Match](/concepts/searchable-encryption#query-capabilities) ```json { @@ -306,4 +306,4 @@ The CipherCell format is consistent across all CipherStash SDKs and tools, ensur ## Database storage CipherCells can be stored as JSON in any database that supports JSON data types. -However, for search to be supported using the [Encryption SDK](/stack/cipherstash/encryption) or [CipherStash Proxy](/stack/cipherstash/proxy), the `eql_v2.encrypted` database type must be used which is available when the Encrypt Query Language (EQL) helpers have been installed. +However, for search to be supported using the [Stack SDK](/reference/stack) or [CipherStash Proxy](/reference/proxy), the `eql_v2.encrypted` database type must be used which is available when the Encrypt Query Language (EQL) helpers have been installed. diff --git a/content/docs/reference/eql/v2/queries.mdx b/content/docs/reference/eql/v2/queries.mdx index cbf0f81..8bc39d8 100644 --- a/content/docs/reference/eql/v2/queries.mdx +++ b/content/docs/reference/eql/v2/queries.mdx @@ -15,7 +15,7 @@ This page covers the three query families available for encrypted columns: equal For index creation (the `CREATE INDEX` statements your database needs), see [Setting up indexes](/reference/eql/v2/indexes). -For a conceptual overview of how searchable encryption works, see [Searchable encryption](/stack/cipherstash/encryption/searchable-encryption). +For a conceptual overview of how searchable encryption works, see [Searchable encryption](/concepts/searchable-encryption). ## Equality diff --git a/content/docs/reference/glossary.mdx b/content/docs/reference/glossary.mdx index 3a75a94..616dbf3 100644 --- a/content/docs/reference/glossary.mdx +++ b/content/docs/reference/glossary.mdx @@ -12,7 +12,7 @@ A dynamic security model that makes access decisions based on a variety of attri ### Access key -A persistent authentication credential used for communication with [ZeroKMS](#zerokms) or [CTS](#cts-cipherstash-token-service). See [access keys](/stack/cipherstash/kms/access-keys). +A persistent authentication credential used for communication with [ZeroKMS](#zerokms) or [CTS](#cts-cipherstash-token-service). See [access keys](/reference/auth/access-keys). ### Account management @@ -43,7 +43,7 @@ There are two types: - **Device-backed client keys** are created automatically by `stash init` and tied to a developer's [device](#device). Used for local development. - **Application client keys** are created in the Dashboard for production and CI/CD. Identified by a [client ID](#client-id) and client key value, set via environment variables. -See [client keys](/stack/cipherstash/kms/clients). +See [client keys](/reference/auth/clients). ### Client ID @@ -55,7 +55,7 @@ The secret credential set via `CS_CLIENT_KEY`. Together with the [client ID](#cl ### CTS (CipherStash Token Service) -CTS manages the trust relationships between a workspace and third-party or customer identity providers. It brokers secure access to CipherStash services such as ZeroKMS, ensuring that only authenticated and authorized users gain entry. See [CTS](/stack/cipherstash/kms/cts). +CTS manages the trust relationships between a workspace and third-party or customer identity providers. It brokers secure access to CipherStash services such as ZeroKMS, ensuring that only authenticated and authorized users gain entry. See [CTS](/security/cts). ## D @@ -73,7 +73,7 @@ An event triggered by execution of SQL statements by CipherStash Proxy. Includes A unique identity tied to a developer's device and user account, created by `stash init`. Each device has an associated [client key](#client-key) that is automatically granted access to the default [keyset](#keyset). -Devices are used for local development authentication, with no environment variables required. Production environments use application [client keys](#client-key) instead. See the [quickstart](/stack/quickstart). +Devices are used for local development authentication, with no environment variables required. Production environments use application [client keys](#client-key) instead. See the [quickstart](/get-started/quickstart). ## E @@ -109,7 +109,7 @@ A core CipherStash Cloud primitive for cryptographic isolation, managed by [Zero Use keysets for tenant isolation, environment separation, regional compliance, or any cryptographic boundary your architecture requires. A [client key](#client-key) can access many keysets, and a keyset can be shared by multiple client keys. In the Secrets SDK, environments map directly to keysets. -See [keysets](/stack/cipherstash/kms/keysets). +See [key management](/concepts/key-management#keysets-define-the-isolation-boundary). ## O @@ -153,4 +153,4 @@ See [workspace and account](/reference/workspace). ### ZeroKMS -CipherStash's key management service. ZeroKMS provides high-performance batch encryption and decryption, enabling a unique encryption key per field. See [ZeroKMS](/stack/cipherstash/kms). +CipherStash's key management service. ZeroKMS provides high-performance batch encryption and decryption, enabling a unique encryption key per field. See [key management](/concepts/key-management). diff --git a/content/docs/reference/index.mdx b/content/docs/reference/index.mdx index 2f6f2d3..f734fde 100644 --- a/content/docs/reference/index.mdx +++ b/content/docs/reference/index.mdx @@ -1,10 +1,11 @@ --- title: Reference -navTitle: Overview description: "Precise API documentation for EQL, the Stack SDK, Auth, the CLI, and Proxy." type: reference --- -This section is being built as part of the docs V2 overhaul ([CIP-3307](https://linear.app/cipherstash/issue/CIP-3307)). Track progress in [IA.md](https://github.com/cipherstash/docs/blob/v2/IA.md). - -Until it lands, current documentation lives in the [existing docs](/stack). +{/* + Fumadocs requires an index document when generating a metadata-backed folder. + meta.json excludes this file from the page tree, and Next redirects its route + to the first visible Reference page. +*/} diff --git a/content/docs/reference/meta.json b/content/docs/reference/meta.json index 83f32fd..fc7918c 100644 --- a/content/docs/reference/meta.json +++ b/content/docs/reference/meta.json @@ -2,7 +2,7 @@ "title": "Reference", "icon": "Library", "pages": [ - "index", + "!index", "eql", "stack", "auth", diff --git a/content/docs/reference/proxy/configuration.mdx b/content/docs/reference/proxy/configuration.mdx index 2eac736..d49e9e2 100644 --- a/content/docs/reference/proxy/configuration.mdx +++ b/content/docs/reference/proxy/configuration.mdx @@ -299,7 +299,7 @@ cipherstash-proxy [OPTIONS] [DBNAME] [COMMAND] | `encrypt` | Encrypt existing plaintext data in the database | | `help` | Print help information | -The `encrypt` command encrypts existing plaintext data in place. See [the encrypt tool](/stack/cipherstash/proxy/encrypt-tool). +The `encrypt` command encrypts existing plaintext data in place. Follow the safeguards in [Data migration](/guides/migration) before running it against populated production data. ### Arguments and options diff --git a/content/docs/reference/proxy/multitenant.mdx b/content/docs/reference/proxy/multitenant.mdx index 9a7c2e8..9bc91c4 100644 --- a/content/docs/reference/proxy/multitenant.mdx +++ b/content/docs/reference/proxy/multitenant.mdx @@ -73,5 +73,5 @@ This behaviour is expected. When a client prepares a statement, it sends the SQL ## Related -- [Keysets](/stack/cipherstash/kms/keysets) for the cryptographic isolation model. +- [Key management](/concepts/key-management#keysets-define-the-isolation-boundary) for the cryptographic isolation model. - [Proxy configuration](/reference/proxy/configuration) for `CS_DEFAULT_KEYSET_ID`. diff --git a/content/docs/reference/stack/usage.mdx b/content/docs/reference/stack/usage.mdx index 6e84cfc..51ff988 100644 --- a/content/docs/reference/stack/usage.mdx +++ b/content/docs/reference/stack/usage.mdx @@ -85,3 +85,24 @@ console.log(decrypted.data) ``` For every exported signature and option, use the [generated API reference](/reference/stack/api-reference). For an end-to-end project setup, follow the [quickstart](/get-started/quickstart). + +## Upgrading from `@cipherstash/protect` + +The current package is `@cipherstash/stack`. Replace the retired package and +rename its schema builders: + +| Retired API | Stack API | +| --- | --- | +| `protect(config)` | `Encryption(config)` | +| `csTable(name, columns)` | `encryptedTable(name, columns)` | +| `csColumn(name)` | A typed column from `types`, such as `types.TextEq(name)` | + +```bash +npm uninstall @cipherstash/protect +npm install @cipherstash/stack +``` + +Use the v3 exports shown above for new schemas. Data already written in an +older format needs an explicit migration plan; do not change the package and +database column format in a single irreversible deployment. Follow +[Data migration](/guides/migration) for the staged rollout. diff --git a/content/docs/reference/workspace/billing.mdx b/content/docs/reference/workspace/billing.mdx index 4079457..2ad28ca 100644 --- a/content/docs/reference/workspace/billing.mdx +++ b/content/docs/reference/workspace/billing.mdx @@ -11,7 +11,7 @@ CipherStash uses a **per-workspace billing model**. Each workspace has its own p ## Plans -Each workspace is on a plan. Plans differ in their usage limits and in which features they unlock, such as keyset isolation and [lock contexts](/stack/cipherstash/encryption/identity). See [pricing](https://cipherstash.com/pricing) for the current plans, their prices, and their limits. +Each workspace is on a plan. Plans differ in their usage limits and in which features they unlock, such as keyset isolation and [lock contexts](/solutions/provable-access). See [pricing](https://cipherstash.com/pricing) for the current plans, their prices, and their limits. Enterprise is an organization-level plan with custom pricing and dedicated support. Enterprise organizations bypass per-workspace billing entirely. Contact [sales@cipherstash.com](mailto:sales@cipherstash.com). diff --git a/content/docs/reference/workspace/configuration.mdx b/content/docs/reference/workspace/configuration.mdx index 74fa37d..e7701b7 100644 --- a/content/docs/reference/workspace/configuration.mdx +++ b/content/docs/reference/workspace/configuration.mdx @@ -41,5 +41,5 @@ Proxy scopes keysets per connection instead. See [multitenant operation](/refere ## Related -- [Keysets](/stack/cipherstash/kms/keysets) for the isolation model. +- [Key management](/concepts/key-management#keysets-define-the-isolation-boundary) for the isolation model. - [Billing](/reference/workspace/billing) for the plans and how per-workspace billing works. diff --git a/content/docs/reference/workspace/index.mdx b/content/docs/reference/workspace/index.mdx index e2b4693..1f484f8 100644 --- a/content/docs/reference/workspace/index.mdx +++ b/content/docs/reference/workspace/index.mdx @@ -18,5 +18,5 @@ Use workspaces to separate environments (development from production), to scope ## Related -- [Access keys](/stack/cipherstash/kms/access-keys) for the credentials that authenticate to ZeroKMS and CTS. +- [Access keys](/reference/auth/access-keys) for the credentials that authenticate to ZeroKMS and CTS. - [Glossary](/reference/glossary) for the difference between a workspace, a keyset, a client key, and a device. diff --git a/content/docs/reference/workspace/members.mdx b/content/docs/reference/workspace/members.mdx index 2e11803..afa3a4e 100644 --- a/content/docs/reference/workspace/members.mdx +++ b/content/docs/reference/workspace/members.mdx @@ -29,4 +29,4 @@ npx stash init This creates a unique [device](/reference/glossary#device) and [client key](/reference/glossary#client-key) for that developer, with automatic access to the default [keyset](/reference/glossary#keyset). No environment variables are needed for local development. -See [team onboarding](/stack/deploy/team-onboarding) for the full workflow. +See [team onboarding](/guides/deployment#team-onboarding) for the full workflow. diff --git a/content/docs/security/audit-logging.mdx b/content/docs/security/audit-logging.mdx index ae1bba0..5a8a406 100644 --- a/content/docs/security/audit-logging.mdx +++ b/content/docs/security/audit-logging.mdx @@ -1,9 +1,54 @@ --- title: Audit logging -description: "Decryption-event logging: what's recorded, and how to attach query metadata." +description: "How CipherStash Proxy identifies query patterns, redacts SQL, and associates access events with records and identities." type: concept +components: [proxy, platform] +audience: [ciso, developer] --- -This page is being built as part of the docs V2 overhaul ([CIP-3331](https://linear.app/cipherstash/issue/CIP-3331)). Track progress in [IA.md](https://github.com/cipherstash/docs/blob/v2/IA.md). +CipherStash Proxy can produce data-access events without requiring application +queries to be instrumented. Its audit pipeline identifies the query pattern, +removes literal values, determines which records were accessed, and associates +the event with the authenticated client or identity context. -Until it lands, the current version lives in the [existing docs](/stack/cipherstash/proxy/audit). +This page describes what Proxy contributes to an event. Retention, export, and +SIEM integration depend on the audit service and deployment configuration. + +## Statement fingerprinting + +Proxy fingerprints the parsed SQL statement so semantically equivalent query +shapes can be grouped across parameter values and environments. The fingerprint +identifies a query pattern without using the literal values supplied by the +application. + +## SQL redaction + +Literal values are stripped before SQL is included in an event. Table, column, +and function names remain so the event is useful for investigation. If Proxy +cannot safely parse and redact a statement, it does not transmit the statement +text. + +Parameterized queries sent through PostgreSQL's extended protocol already +keep bound values separate from the SQL text; redaction also covers literals +embedded directly in a statement. + +## Record identification + +Proxy can add missing primary-key columns to an internal form of the query, +then reconcile returned identifiers with their source tables. Injected columns +are removed before the result is returned to the application, so the result +shape remains unchanged. + +## Event pipeline + +1. **Fingerprint** the parsed query shape. +2. **Redact** static values from the SQL representation. +3. **Identify** primary keys for records touched by the query. +4. **Reconcile** identifiers with tables and remove injected fields from the + application result. +5. **Attribute** the event to the client and, when OIDC federation is used, the + authenticated identity. + +For cryptographic identity binding and SDK audit metadata, see +[provable access control](/solutions/provable-access#audit-logging). For Proxy +operation and configuration, see the [Proxy reference](/reference/proxy). diff --git a/content/docs/security/compliance/index.mdx b/content/docs/security/compliance/index.mdx index 8875fd7..98b0846 100644 --- a/content/docs/security/compliance/index.mdx +++ b/content/docs/security/compliance/index.mdx @@ -1,9 +1,49 @@ --- title: Compliance navTitle: Overview -description: "Compliance documentation — being built as part of the docs V2 overhaul." +description: "How CipherStash capabilities support a compliance control design, and which responsibilities remain with the customer." +type: concept +components: [encryption, platform, proxy] +audience: [ciso, cto] +reviewBy: "2027-01-31" --- -This section is being built as part of the docs V2 overhaul ([CIP-3307](https://linear.app/cipherstash/issue/CIP-3307)). Track progress in [IA.md](https://github.com/cipherstash/docs/blob/v2/IA.md). +CipherStash supplies technical controls that can support a compliance program: +field-level encryption, regional key management, cryptographic isolation, +identity-bound decryption, and data-access evidence. A framework outcome still +depends on how those controls are configured and on the surrounding application, +cloud, operational, and organizational controls. -Until it lands, current documentation lives in the [existing docs](/stack). +## Capability map + +| Requirement area | Relevant CipherStash capability | Customer responsibility | +| --- | --- | --- | +| Protect sensitive stored data | Application-level field encryption | Classify fields and ensure every write path encrypts them | +| Restrict key access | Client keys, access-key roles, and keyset grants | Apply least privilege, rotate credentials, and control the deployment secret store | +| Isolate tenants or environments | Separate keysets or workspaces | Define and enforce the boundary consistently in application and database access | +| Attribute access to a user | OIDC federation and lock contexts | Configure the identity provider and choose stable, appropriate claims | +| Produce data-access evidence | ZeroKMS derivation context and Proxy audit events | Configure retention, review, alerting, and SIEM ingestion | +| Keep key material regional | Region-bound workspaces | Deploy applications and databases in the intended region and govern cross-border access | +| Remove access to encrypted data | Revoke client grants or destroy the relevant key boundary | Confirm scope, backups, legal holds, and recovery requirements before destructive action | + +## Common frameworks + +These capabilities can contribute evidence or control implementation for +frameworks such as SOC 2, HIPAA, GDPR, PCI DSS, ISO 27001, and regional privacy +laws. They do not by themselves make a workload compliant, and the exact +mapping depends on the organization's scope and assessor interpretation. + +Use the following pages when building a control narrative: + +- [Cryptography](/security/cryptography) for algorithms, key hierarchy, data + flow, and trust boundaries. +- [Key management](/concepts/key-management) for per-value keys, split control, + keysets, and revocation. +- [Data residency](/solutions/data-residency) for regional deployment patterns. +- [Provable access control](/solutions/provable-access) and + [audit logging](/security/audit-logging) for identity and evidence. +- [Access keys](/reference/auth/access-keys) and + [client keys](/reference/auth/clients) for least-privilege credentials. + +Contact [support@cipherstash.com](mailto:support@cipherstash.com) for current +assurance reports, contractual documents, or framework-specific questions. diff --git a/content/docs/security/cryptography.mdx b/content/docs/security/cryptography.mdx index fdfb922..35c4474 100644 --- a/content/docs/security/cryptography.mdx +++ b/content/docs/security/cryptography.mdx @@ -119,7 +119,7 @@ An attacker must compromise **both** the ZeroKMS authority key and your client k ### Blast radius -[Keysets](/stack/cipherstash/kms/keysets) scope keys. Each keyset is a full cryptographic boundary: one tenant's keyset cannot decrypt another's data. Compromising a client key affects only the keysets that application can reach, and revoking its access key stops further key derivation immediately. +[Keysets](/concepts/key-management#keysets-define-the-isolation-boundary) scope keys. Each keyset is a full cryptographic boundary: one tenant's keyset cannot decrypt another's data. Compromising a client key affects only the keysets that application can reach, and revoking its access key stops further key derivation immediately. ## Data flow @@ -161,7 +161,7 @@ All communication between the SDK and CipherStash services uses TLS 1.2 or later - SDK to CTS, for token exchange during identity-aware encryption. - SDK to your database: your existing connection. CipherStash does not proxy it. -Key material does not leave the region your workspace is configured in. See [regions](/stack/cipherstash/kms/regions). +Key material does not leave the region your workspace is configured in. See [data residency](/solutions/data-residency#regional-zerokms-deployment). ## Open-source components @@ -176,6 +176,6 @@ The core cryptographic implementations are open source and independently auditab ## Related - [Searchable encryption](/concepts/searchable-encryption) for the canonical index-term leakage model. -- [Keysets](/stack/cipherstash/kms/keysets) for the isolation boundary. +- [Key management](/concepts/key-management) for the isolation boundary. - [Proxy configuration](/reference/proxy/configuration) for the cipher cache settings. - [Provable access control](/solutions/provable-access) for binding decryption to an authenticated identity. diff --git a/content/docs/security/cts.mdx b/content/docs/security/cts.mdx index 23e541d..bd5074a 100644 --- a/content/docs/security/cts.mdx +++ b/content/docs/security/cts.mdx @@ -1,9 +1,61 @@ --- -title: CTS -description: "The CipherStash Token Service: exchanging identity-provider JWTs for tokens that gate decryption." +title: CipherStash Token Service +navTitle: CTS +description: "How CTS authenticates client keys and federated identities, then issues short-lived tokens for ZeroKMS." type: concept +components: [platform] +audience: [developer, ciso] --- -This page is being built as part of the docs V2 overhaul ([CIP-3330](https://linear.app/cipherstash/issue/CIP-3330)). Track progress in [IA.md](https://github.com/cipherstash/docs/blob/v2/IA.md). +CipherStash Token Service (CTS) is the authentication and identity-federation +layer in front of ZeroKMS. It converts a persistent application credential or +a trusted identity-provider token into a short-lived token scoped to +cryptographic service access. -Until it lands, the current version lives in the [existing docs](/stack/cipherstash/kms/cts). +CTS plays a role similar to AWS STS: the long-lived credential proves who the +caller is, while the issued token is temporary and is what the caller presents +to ZeroKMS. + +## Request flow + +```mermaid +sequenceDiagram + participant App as Stack SDK or Proxy + participant IdP as Identity provider + participant CTS + participant ZK as ZeroKMS + + alt Application authentication + App->>CTS: Client ID + access key + else End-user federation + App->>IdP: Obtain user JWT + IdP-->>App: Signed JWT + App->>CTS: Federate JWT + end + CTS-->>App: Short-lived CTS token + App->>ZK: Key-seed request + CTS token + ZK-->>App: Key seed +``` + +The Stack SDK refreshes service tokens as required. Application code normally +selects an auth strategy rather than calling CTS directly. + +## Authentication modes + +### Application credentials + +A production service or Proxy instance uses a +[client key](/reference/auth/clients) and +[access key](/reference/auth/access-keys). This authenticates the workload as +an application rather than as a particular end user. + +### OIDC federation + +Register a trusted issuer and use `OidcFederationStrategy` when each request +should carry the signed-in user's identity. CTS validates the provider JWT and +issues the temporary service token. See +[OIDC providers](/reference/auth/oidc-configuration). + +Federated identity can also be used with a lock context so key derivation +depends on a JWT claim. That cryptographic binding is described in +[provable access control](/solutions/provable-access). diff --git a/content/docs/security/meta.json b/content/docs/security/meta.json index ea315bc..6348a13 100644 --- a/content/docs/security/meta.json +++ b/content/docs/security/meta.json @@ -1,5 +1,12 @@ { "title": "Architecture & security", "icon": "Shield", - "pages": ["index", "cryptography", "...", "compliance"] + "pages": [ + "index", + "cryptography", + "!audit-logging", + "!cts", + "!compliance", + "..." + ] } diff --git a/content/docs/solutions/ai-and-rag.mdx b/content/docs/solutions/ai-and-rag.mdx new file mode 100644 index 0000000..745612e --- /dev/null +++ b/content/docs/solutions/ai-and-rag.mdx @@ -0,0 +1,84 @@ +--- +title: AI and RAG +description: "Protect sensitive source text and metadata in AI and retrieval-augmented generation pipelines without giving the database plaintext." +type: concept +components: [encryption, platform] +audience: [developer, cto, ciso] +verifiedAgainst: + stack: "1.0.0" +--- + +Retrieval-augmented generation pipelines often store document chunks, +customer metadata, access labels, and vector embeddings in the same database. +The source text may contain PII, financial records, health information, or +confidential business material that should not become plaintext merely because +an application needs semantic retrieval. + +CipherStash encrypts sensitive fields in the application before they reach the +database. The vector store can continue retrieving candidate rows while the +application decrypts only the records selected for the prompt. + +## What to encrypt + +Treat each part of the RAG record according to how it is used: + +| Field | Typical treatment | +| --- | --- | +| Source chunk | Encrypt; decrypt only after retrieval and authorization | +| Document title or filename | Encrypt if it contains customer or case information | +| Tenant, owner, or policy ID | Encrypt with equality when the database must filter on it | +| Access labels | Encrypt with the narrowest query capability the filter needs | +| Embedding | Keep separate from source text; assess whether the embedding itself is sensitive | + +Embeddings are derived data, not ciphertext. They may reveal properties of the +source and should be covered by the application's data classification and +threat model. Encrypting the source text does not make an exposed embedding +harmless. + +## Retrieval flow + +```mermaid +sequenceDiagram + participant App as Application + participant Vector as Vector store + participant DB as Encrypted record store + participant ZK as ZeroKMS + participant LLM as Model + + App->>Vector: Search with query embedding + Vector-->>App: Candidate record IDs + App->>DB: Fetch authorized encrypted records + DB-->>App: Ciphertext + App->>ZK: Request key seeds for selected values + ZK-->>App: Key seeds + Note over App: Derive keys and decrypt locally + App->>LLM: Minimum required plaintext context +``` + +The database and vector service do not receive the source plaintext. The model +still receives whichever context the application sends, so model-provider +retention, logging, prompt injection, and authorization remain separate design +decisions. + +## Tenant and user isolation + +Use a separate [keyset](/concepts/key-management#keysets-define-the-isolation-boundary) +when tenants or environments require a cryptographic boundary. Use +[identity-bound encryption](/solutions/provable-access) when decryption should +require claims from the signed-in user. + +Apply authorization before decryption and again before assembling the prompt. +Vector similarity alone is not an authorization decision: a candidate from +another tenant must be discarded before its ciphertext is decrypted. + +## Searchable metadata + +If the metadata lives in Postgres, EQL can filter equality, ranges, token +matches, and JSON containment without decrypting every row. Declare only the +capabilities the retrieval pipeline uses; each capability has a documented +leakage profile. See [searchable encryption](/concepts/searchable-encryption) +and [EQL filtering](/reference/eql/filtering). + +For implementation, begin with the [Stack SDK](/reference/stack), then follow +the [Data migration](/guides/migration) rollout when existing records must be +encrypted without downtime. diff --git a/content/docs/solutions/data-residency.mdx b/content/docs/solutions/data-residency.mdx index 6c24689..497328a 100644 --- a/content/docs/solutions/data-residency.mdx +++ b/content/docs/solutions/data-residency.mdx @@ -26,7 +26,7 @@ ZeroKMS is deployed on AWS only. A workspace is tied to one region, and its regi Selecting a ZeroKMS region controls where authority keys are managed. Combined with your application's deployment region, that determines where all key material exists. -Need a region that is not listed? Contact [support@cipherstash.com](mailto:support@cipherstash.com). See the [regions reference](/stack/cipherstash/kms/regions) for more. +Need a region that is not listed? Contact [support@cipherstash.com](mailto:support@cipherstash.com). ## Dual-party key split @@ -128,6 +128,6 @@ This architecture supports compliance with GDPR, regional data protection laws, ## Next steps -- [Configure ZeroKMS regions](/stack/cipherstash/kms/regions) for your workspaces. +- Review [regional ZeroKMS deployment](#regional-zerokms-deployment) for your workspaces. - [Set up provable access control](/solutions/provable-access) to bind decryption to an identity. -- [Review disaster recovery](/stack/cipherstash/kms/disaster-recovery) for multi-region key backup. +- Review [key management](/concepts/key-management) when designing recovery and revocation procedures. diff --git a/content/docs/solutions/meta.json b/content/docs/solutions/meta.json index 866869f..fedb529 100644 --- a/content/docs/solutions/meta.json +++ b/content/docs/solutions/meta.json @@ -1,5 +1,5 @@ { "title": "Solutions", "icon": "Target", - "pages": ["index", "...", "data-residency", "provable-access"] + "pages": ["index", "ai-and-rag", "data-residency", "provable-access", "..."] } diff --git a/content/docs/solutions/provable-access.mdx b/content/docs/solutions/provable-access.mdx index 950b917..16f0c6b 100644 --- a/content/docs/solutions/provable-access.mdx +++ b/content/docs/solutions/provable-access.mdx @@ -126,7 +126,7 @@ Every encryption and decryption through ZeroKMS produces an audit event: ### Combining with Proxy audit -[CipherStash Proxy](/reference/proxy) adds statement-level audit on top: statement fingerprinting to identify unique query patterns against encrypted data, SQL redaction to strip sensitive values from logged queries, primary key injection to track which records were touched, and record reconciliation to map access events back to rows. See [Proxy audit](/stack/cipherstash/proxy/audit). +[CipherStash Proxy](/reference/proxy) adds statement-level audit on top: statement fingerprinting to identify unique query patterns against encrypted data, SQL redaction to strip sensitive values from logged queries, primary key injection to track which records were touched, and record reconciliation to map access events back to rows. See [Audit logging](/security/audit-logging). Together these answer who accessed what data, when, and using which query. @@ -142,7 +142,7 @@ Encrypt audit records under a compliance-team member's identity. Someone on the ### Multi-tenant SaaS: tenant isolation -Bind encryption to a per-user claim so only that user's identity can decrypt their data. For coarser isolation that does not depend on a signed-in end user, separate [keysets](/stack/cipherstash/kms/keysets) usually fit better: a keyset boundary is per-client or per-connection, where a lock context is per-identity. +Bind encryption to a per-user claim so only that user's identity can decrypt their data. For coarser isolation that does not depend on a signed-in end user, separate [keysets](/concepts/key-management#keysets-define-the-isolation-boundary) usually fit better: a keyset boundary is per-client or per-connection, where a lock context is per-identity. ## Compared to application-level access control @@ -156,5 +156,5 @@ Bind encryption to a per-user claim so only that user's identity can decrypt the ## Next steps - [Data residency](/solutions/data-residency) for revoking access per region. -- [Proxy audit](/stack/cipherstash/proxy/audit) for statement-level audit logging. +- [Audit logging](/security/audit-logging) for statement-level access evidence. - [Compliance](/security/compliance) for GDPR, HIPAA, and PCI-DSS mapping. diff --git a/next.config.mjs b/next.config.mjs index 55dc055..a0e4cfc 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -10,6 +10,27 @@ const withMDX = createMDX(); // `bun run validate-redirects` regardless of the flag. const enableV2Redirects = process.env.ENABLE_V2_REDIRECTS === "1"; +// Migrated legacy sections redirect as soon as their replacements are +// complete, without waiting for the root-level v2 IA cutover. The two legacy +// landing pages remain available until ENABLE_V2_REDIRECTS is flipped. +const enabledV2RedirectPrefixes = [ + "/stack/quickstart", + "/stack/cipherstash/postgres", + "/stack/cipherstash/supabase", + "/stack/cipherstash/encryption", + "/stack/cipherstash/kms", + "/stack/cipherstash/proxy", + "/stack/cipherstash/cli", + "/stack/deploy", + "/stack/reference", +]; + +function isEnabledV2Redirect(source) { + return enabledV2RedirectPrefixes.some( + (prefix) => source === prefix || source.startsWith(`${prefix}/`), + ); +} + /** @type {import('next').NextConfig} */ const config = { basePath: "/docs", @@ -40,12 +61,28 @@ const config = { destination: "/concepts/searchable-encryption", permanent: false, }, + // Guides is a non-clickable sidebar group with no landing page. + // Keep its direct URL useful without rendering a synthetic Overview. + { + source: "/guides", + destination: "/guides/deployment", + permanent: false, + }, + // Reference is a non-clickable sidebar group with no landing page. + // Keep its direct URL useful without rendering a synthetic Overview. + { + source: "/reference", + destination: "/reference/eql", + permanent: false, + }, { source: "/integrations/prisma-next", destination: "/integrations/prisma", permanent: true, }, - ...(enableV2Redirects ? v2Redirects : []), + ...v2Redirects.filter( + ({ source }) => enableV2Redirects || isEnabledV2Redirect(source), + ), // === 4-section consolidation: product sections under /cipherstash/ === { source: "/stack/encryption/:path*", diff --git a/scripts/validate-v2-redirects.ts b/scripts/validate-v2-redirects.ts index 97c90d0..da28ad1 100644 --- a/scripts/validate-v2-redirects.ts +++ b/scripts/validate-v2-redirects.ts @@ -3,13 +3,9 @@ * V2 redirect gate (CIP-3325 / CIP-3337 item 7). * * Every page in the legacy tree (content/stack) must be covered by an entry - * in v2-redirects.mjs — exact match or `:path*` wildcard — so that no URL is - * orphaned when the v2 IA ships. Run via `bun run validate-redirects`; wired - * into prebuild so a page added to content/stack without a mapping fails CI. - * - * This checks map *coverage*, not destination existence — destinations are - * stubs until each section's migration ticket lands. CIP-3335 verifies - * destinations resolve before merge. + * in v2-redirects.mjs — exact match or `:path*` wildcard — and every resolved + * destination must be a real v2 page. Run via `bun run validate-redirects`; + * wired into prebuild so an orphaned source or a redirect-to-404 fails CI. */ import fs from "node:fs"; import path from "node:path"; @@ -17,6 +13,15 @@ import path from "node:path"; import { v2Redirects } from "../v2-redirects.mjs"; const LEGACY_DIR = path.join(process.cwd(), "content/stack"); +const V2_DIR = path.join(process.cwd(), "content/docs"); + +// These indexes are generated during prebuild and intentionally ignored by +// git. Their tracked meta files establish the output roots so the redirect +// validator can also run before generation in a clean checkout. +const GENERATED_PAGE_ROOTS = new Set([ + "/integrations/supabase/api-reference", + "/reference/stack/api-reference", +]); function collectSlugs(dir: string, prefix: string[] = []): string[] { const slugs: string[] = []; @@ -42,6 +47,32 @@ function matches(url: string, source: string): boolean { return url === source; } +function resolveDestination( + url: string, + redirect: { source: string; destination: string }, +): string { + if (!redirect.source.endsWith("/:path*")) return redirect.destination; + + const base = redirect.source.slice(0, -"/:path*".length); + const suffix = url === base ? "" : url.slice(base.length + 1); + return redirect.destination.replace(":path*", suffix); +} + +function destinationExists(destination: string): boolean { + const route = destination.split(/[?#]/, 1)[0]; + if (route === "/") return fs.existsSync(path.join(V2_DIR, "index.mdx")); + + const relative = route.replace(/^\//, ""); + const page = path.join(V2_DIR, `${relative}.mdx`); + const index = path.join(V2_DIR, relative, "index.mdx"); + if (fs.existsSync(page) || fs.existsSync(index)) return true; + + return ( + GENERATED_PAGE_ROOTS.has(route) && + fs.existsSync(path.join(V2_DIR, relative, "meta.json")) + ); +} + const urls = collectSlugs(LEGACY_DIR); const unmatched = urls.filter( (url) => !v2Redirects.some((r: { source: string }) => matches(url, r.source)), @@ -58,4 +89,29 @@ if (unmatched.length > 0) { process.exit(1); } -console.log(`✓ all ${urls.length} legacy pages covered by v2-redirects.mjs`); +const broken = urls.flatMap((url) => { + const redirect = v2Redirects.find((candidate: { source: string }) => + matches(url, candidate.source), + ); + if (!redirect) return []; + + const destination = resolveDestination(url, redirect); + return destinationExists(destination) ? [] : [{ url, destination }]; +}); + +if (broken.length > 0) { + console.error( + `✗ ${broken.length} legacy redirect(s) resolve to a missing v2 page:\n`, + ); + for (const { url, destination } of broken) { + console.error(` ${url} → ${destination}`); + } + console.error( + "\nCreate the destination or map the legacy URL to an existing canonical page.", + ); + process.exit(1); +} + +console.log( + `✓ all ${urls.length} legacy pages map to existing v2 destinations`, +); diff --git a/v2-redirects.mjs b/v2-redirects.mjs index 831684e..e08dbcb 100644 --- a/v2-redirects.mjs +++ b/v2-redirects.mjs @@ -81,17 +81,47 @@ export const v2Redirects = [ destination: "/reference/eql/filtering", permanent: false, }, - // configuration, encrypt-decrypt, bulk-operations, models, schema, storing-data + { + source: "/stack/cipherstash/encryption/configuration", + destination: "/reference/workspace/configuration", + permanent: false, + }, + { + source: "/stack/cipherstash/encryption/encrypt-decrypt", + destination: "/reference/stack/usage", + permanent: false, + }, + { + source: "/stack/cipherstash/encryption/storing-data", + destination: "/reference/stack/usage", + permanent: false, + }, + { + source: "/stack/cipherstash/encryption/schema", + destination: "/reference/stack/api-reference/schema", + permanent: false, + }, + { + source: "/stack/cipherstash/encryption/models", + destination: "/reference/stack/api-reference/encryption", + permanent: false, + }, + { + source: "/stack/cipherstash/encryption/bulk-operations", + destination: "/reference/stack/api-reference/encryption", + permanent: false, + }, + // Any retired Encryption SDK leaf falls back to the current usage guide. { source: "/stack/cipherstash/encryption/:path*", - destination: "/reference/stack/:path*", + destination: "/reference/stack/usage", permanent: false, }, // === KMS section → Security + Reference/auth + Concepts === { source: "/stack/cipherstash/kms", - destination: "/security/zerokms", + destination: "/concepts/key-management", permanent: false, }, { @@ -116,7 +146,7 @@ export const v2Redirects = [ }, { source: "/stack/cipherstash/kms/disaster-recovery", - destination: "/security/availability-and-continuity", + destination: "/concepts/key-management", permanent: false, }, { @@ -126,7 +156,7 @@ export const v2Redirects = [ }, { source: "/stack/cipherstash/kms/regions", - destination: "/security/zerokms", + destination: "/solutions/data-residency", permanent: false, }, { @@ -148,12 +178,12 @@ export const v2Redirects = [ }, { source: "/stack/cipherstash/proxy/getting-started", - destination: "/integrations/aws/rds-aurora", + destination: "/reference/proxy", permanent: false, }, { source: "/stack/cipherstash/proxy/encrypt-tool", - destination: "/guides/migration/encrypt-existing-data", + destination: "/guides/migration", permanent: false, }, { @@ -163,7 +193,7 @@ export const v2Redirects = [ }, { source: "/stack/cipherstash/proxy/troubleshooting", - destination: "/guides/troubleshooting/proxy", + destination: "/reference/proxy/errors", permanent: false, }, // configuration, message-flow, multitenant @@ -181,7 +211,27 @@ export const v2Redirects = [ }, { source: "/stack/cipherstash/cli/troubleshooting", - destination: "/guides/troubleshooting/cli", + destination: "/reference/cli/doctor", + permanent: false, + }, + { + source: "/stack/cipherstash/cli/api", + destination: "/reference/cli", + permanent: false, + }, + { + source: "/stack/cipherstash/cli/install", + destination: "/reference/cli/eql", + permanent: false, + }, + { + source: "/stack/cipherstash/cli/push", + destination: "/reference/cli", + permanent: false, + }, + { + source: "/stack/cipherstash/cli/validate", + destination: "/reference/cli/db", permanent: false, }, { @@ -198,42 +248,46 @@ export const v2Redirects = [ }, { source: "/stack/deploy/going-to-production", - destination: "/guides/deployment/going-to-production", + destination: "/guides/deployment", permanent: false, }, { source: "/stack/deploy/aws-ecs", - destination: "/guides/deployment/proxy-deployment", + destination: "/guides/deployment", permanent: false, }, { source: "/stack/deploy/bundling", - destination: "/guides/deployment/serverless-and-bundling", + destination: "/guides/deployment", permanent: false, }, { source: "/stack/deploy/sst", - destination: "/guides/deployment/serverless-and-bundling", + destination: "/guides/deployment", permanent: false, }, { source: "/stack/deploy/testing", - destination: "/guides/development/testing-and-ci", + destination: "/guides/deployment", permanent: false, }, { source: "/stack/deploy/team-onboarding", - destination: "/guides/development/team-onboarding", + destination: "/guides/deployment", permanent: false, }, { source: "/stack/deploy/troubleshooting", - destination: "/guides/troubleshooting", + destination: "/guides/deployment", permanent: false, }, // === Reference section === - { source: "/stack/reference", destination: "/reference", permanent: false }, + { + source: "/stack/reference", + destination: "/reference/eql", + permanent: false, + }, { source: "/stack/reference/what-is-cipherstash", destination: "/get-started/what-is-cipherstash", @@ -269,7 +323,7 @@ export const v2Redirects = [ // it can be republished), so send its legacy URL to the Solutions index // rather than a page that does not exist. source: "/stack/reference/use-cases/ai-rag", - destination: "/solutions", + destination: "/solutions/ai-and-rag", permanent: false, }, { @@ -319,14 +373,14 @@ export const v2Redirects = [ }, { source: "/stack/reference/error-handling", - destination: "/reference/stack/errors", + destination: "/reference/stack/api-reference/errors", permanent: false, }, // NOTE: legacy "migration" page is the @cipherstash/protect→stack package // rename guide, NOT data migration (see IA.md). { source: "/stack/reference/migration", - destination: "/reference/stack/upgrading-from-protect", + destination: "/reference/stack/usage", permanent: false, }, { @@ -375,6 +429,13 @@ export const v2Redirects = [ permanent: false, }, // Generated TypeDoc API reference (scripts/generate-docs.ts output) + // The legacy generator placed the Supabase wrapper beneath Stack. Its v2 + // reference is owned by the Supabase integration instead. + { + source: "/stack/reference/stack/latest/packages/stack-supabase/:path*", + destination: "/integrations/supabase/api-reference", + permanent: false, + }, { source: "/stack/reference/stack/:path*", destination: "/reference/stack/api-reference", From de2ce189611323ab39d7c9721c2826eb3cc64ebe Mon Sep 17 00:00:00 2001 From: Dan Draper Date: Fri, 31 Jul 2026 02:27:20 +1000 Subject: [PATCH 2/3] fix(redirects): route Supabase to overview --- v2-redirects.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2-redirects.mjs b/v2-redirects.mjs index e08dbcb..20dba75 100644 --- a/v2-redirects.mjs +++ b/v2-redirects.mjs @@ -68,7 +68,7 @@ export const v2Redirects = [ }, { source: "/stack/cipherstash/encryption/supabase", - destination: "/integrations/supabase/api-reference", + destination: "/integrations/supabase", permanent: false, }, { From 6badc9f37b5bb4c87cfb35e76f3f545f8194d80a Mon Sep 17 00:00:00 2001 From: Dan Draper Date: Fri, 31 Jul 2026 02:35:30 +1000 Subject: [PATCH 3/3] docs(nav): restore CTS --- content/docs/security/meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/security/meta.json b/content/docs/security/meta.json index 6348a13..30977ec 100644 --- a/content/docs/security/meta.json +++ b/content/docs/security/meta.json @@ -5,7 +5,7 @@ "index", "cryptography", "!audit-logging", - "!cts", + "cts", "!compliance", "..." ]