Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .changeset/shaggy-points-sink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
"@cipherstash/protect": major
"next-drizzle-mysql": minor
"@cipherstash/nextjs-clerk-example": minor
"@cipherstash/basic-example": minor
---

Implemented a more configurable pattern for the Protect client.

This release introduces a new `ProtectClientConfig` type that can be used to configure the Protect client.
This is useful if you want to configure the Protect client specific to your application, and will future proof any additional configuration options that are added in the future.

```ts
import { protect, type ProtectClientConfig } from "@cipherstash/protect";

const config: ProtectClientConfig = {
schemas: [users, orders],
workspaceCrn: "your-workspace-crn",
accessKey: "your-access-key",
clientId: "your-client-id",
clientKey: "your-client-key",
}

const protectClient = await protect(config);
```

The now deprecated method of passing your tables to the `protect` client is no longer supported.

```ts
import { protect, type ProtectClientConfig } from "@cipherstash/protect";

// old method (no longer supported)
const protectClient = await protect(users, orders);

// required method
const config: ProtectClientConfig = {
schemas: [users, orders],
}

const protectClient = await protect(config);
```


13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ At the end of `stash setup`, you will have two files in your project:
> Don't commit `cipherstash.secret.toml` to git; it contains sensitive credentials.
> The `stash setup` command will attempt to append to your `.gitignore` file with the `cipherstash.secret.toml` file.

Read more about [configuration via TOML file or environment variables](./docs/reference/configuration.md).
Read more about [configuration via TOML file, environment variables, or the Protect client config object](./docs/reference/configuration.md) to meet your needs.
The method you choose will depend on your use case.

### Basic file structure

Expand Down Expand Up @@ -209,14 +210,18 @@ Read more about [defining your schema](./docs/reference/schema.md).
To import the `protect` function and initialize a client with your defined schema, add the following to `src/protect/index.ts`:

```ts
import { protect } from "@cipherstash/protect";
import { protect, type ProtectClientConfig } from "@cipherstash/protect";
import { users, orders } from "./schema";

const config: ProtectClientConfig = {
schemas: [users, orders],
}

// Pass all your tables to the protect function to initialize the client
export const protectClient = await protect(users, orders);
export const protectClient = await protect(config);
```

The `protect` function requires at least one `csTable` be provided.
The `protect` function requires at least one `csTable` be provided in the `schemas` array.

### Encrypt data

Expand Down
11 changes: 6 additions & 5 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,14 +154,15 @@ Read more about [defining your schema](./docs/reference/schema.md).
To import the `protect` function and initialize a client with your defined schema, add the following to `src/protect/index.ts`:

```ts
import { protect } from "@cipherstash/protect";
import { protect, type ProtectClientConfig } from "@cipherstash/protect";
import { users, orders } from "./schema";

// Pass all your tables to the protect function to initialize the client
export const protectClient = await protect(users, orders);
```
const config: ProtectClientConfig = {
schemas: [users, orders],
}

The `protect` function requires at least one `csTable` to be provided.
export const protectClient = await protect(config);
```

## Step 5: Encrypt data

Expand Down
35 changes: 24 additions & 11 deletions docs/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Environment variables will take precedence over configuration files and it's rec
- [`[auth]` section](#auth-section)
- [cipherstash.secret.toml](#cipherstashsecrettoml)
- [Environment variables](#environment-variables)
- [Configuring the Protect client directly](#configuring-the-protect-client-directly)
- [Deploying to production](#deploying-to-production)
- [Region configuration](#region-configuration)
- [File system write permissions](#file-system-write-permissions)
Expand All @@ -38,7 +39,7 @@ The minimal `cipherstash.toml` file:
client_id = "your-client-id"

[auth]
workspace_id = "your-workspace-id"
workspace_crn = "your-workspace-crn"
```

#### `[encrypt]` section
Expand All @@ -50,7 +51,7 @@ The client key must be stored in `cipherstash.secret.toml` or in an environment

#### `[auth]` section

- `workspace_id` (**required**): A base-32 encoded 10-byte unique identifier.
- `workspace_crn` (**required**): The workspace CRN for your CipherStash account.
- `access_key` (**not allowed**): This is explicitly disallowed and will be rejected at runtime.
The access key must be stored in `cipherstash.secret.toml` or in an environment variable `CS_CLIENT_ACCESS_KEY`.

Expand Down Expand Up @@ -92,22 +93,34 @@ The following environment variables are supported:
|:----------------------:|:---------------------------------------------------------------:|:--------:|:--------------------------------------------:|
| `CS_CLIENT_ID` | The client ID for your CipherStash account. | Yes | |
| `CS_CLIENT_KEY` | The client key for your CipherStash account. | Yes | |
| `CS_WORKSPACE_ID` | The workspace ID for your CipherStash account. | Yes | |
| `CS_WORKSPACE_CRN` | The workspace CRN for your CipherStash account. | Yes | |
| `CS_CLIENT_ACCESS_KEY` | The access key for your CipherStash account. | Yes | |
| `CS_ZEROKMS_HOST` | The host for the ZeroKMS server. | No | `https://ap-southeast-2.aws.viturhosted.net` |
| `CS_CONFIG_PATH` | A temporary path to store the CipherStash client configuration. | No | `/home/{username}/.cipherstash` |

## Deploying to production
## Configuring the Protect client directly

> [!TIP]
> There are some configuration details you should take note of when deploying `@cipherstash/protect` in your production examples.
You can also configure the Protect client directly by passing a `ProtectClientConfig` object to the `protect` function during initialization.
This is useful if you want to configure the Protect client specific to your application.
An exmaple of this might be if you want to use a secret manager to store your client key and access key rather than relying on environment variables or configuration files.

### Region configuration
```ts
import { protect, type ProtectClientConfig } from "@cipherstash/protect";

If you've created a Workspace in a region other than `ap-southeast-2`, you will need to set the `CS_ZEROKMS_HOST` environment variable to the appropriate region.
const config: ProtectClientConfig = {
schemas: [users, orders],
workspaceCrn: "your-workspace-crn",
accessKey: "your-access-key",
clientId: "your-client-id",
clientKey: "your-client-key",
}

const protectClient = await protect(config);
```

For example, if you are using ZeroKMS in the `eu-central-1` region, you need to set the `CS_ZEROKMS_HOST` variable to `https://eu-central-1.aws.viturhosted.net`.
This is a known usability issue that will be addressed.
## Deploying to production

> [!TIP]
> There are some configuration details you should take note of when deploying `@cipherstash/protect` in your production examples.

### File system write permissions

Expand Down
11 changes: 6 additions & 5 deletions docs/reference/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,15 @@ You will use your defined schemas to initialize the EQL client.
Simply import your schemas and pass them to the `protect` function.

```ts
import { protect } from "@cipherstash/protect";
import { protect, type ProtectClientConfig } from "@cipherstash/protect";
import { protectedUsers } from "./schemas/users";

const protectClient = await protect(protectedUsers, ...);
```

The `protect` function requires at least one `csTable` to be passed in.
const config: ProtectClientConfig = {
schemas: [protectedUsers], // At least one csTable is required
}

const protectClient = await protect(config);
```
---

### Didn't find what you wanted?
Expand Down
28 changes: 24 additions & 4 deletions docs/reference/supabase-sdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,24 @@ Under the hood, the EQL payload is a JSON object that is stored as a composite t
You can insert encrypted data into the table using Protect.js and the Supabase SDK. Since the `eql_v2_encrypted` column is a composite type, you'll need to use the `encryptedToPgComposite` helper to properly format the data:

```typescript
import { protect, csTable, csColumn, encryptedToPgComposite } from '@cipherstash/protect'
import {
protect,
csTable,
csColumn,
encryptedToPgComposite,
type ProtectClientConfig
} from '@cipherstash/protect'

const users = csTable('users', {
name: csColumn('name').freeTextSearch().equality(),
email: csColumn('email').freeTextSearch().equality()
})

const protectClient = await protect(users)
const config: ProtectClientConfig = {
schemas: [users],
}

const protectClient = await protect(config)

const encryptedResult = await protectClient.encryptModel(
{
Expand Down Expand Up @@ -78,14 +88,24 @@ console.log('Decrypted user:', decryptedResult.data)
When working with models that contain multiple encrypted fields, you can use the `modelToEncryptedPgComposites` helper to handle the conversion to PostgreSQL composite types:

```typescript
import { protect, csTable, csColumn, modelToEncryptedPgComposites } from '@cipherstash/protect'
import {
protect,
csTable,
csColumn,
modelToEncryptedPgComposites,
type ProtectClientConfig
} from '@cipherstash/protect'

const users = csTable('users', {
name: csColumn('name').freeTextSearch().equality(),
email: csColumn('email').freeTextSearch().equality()
})

const protectClient = await protect(users)
const config: ProtectClientConfig = {
schemas: [users],
}

const protectClient = await protect(config)

const model = {
name: 'John Doe',
Expand Down
13 changes: 11 additions & 2 deletions examples/basic/protect.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import 'dotenv/config'
import { protect, csColumn, csTable } from '@cipherstash/protect'
import {
protect,
csColumn,
csTable,
type ProtectClientConfig,
} from '@cipherstash/protect'

export const users = csTable('users', {
name: csColumn('name'),
})

export const protectClient = await protect(users)
const config: ProtectClientConfig = {
schemas: [users],
}

export const protectClient = await protect(config)
13 changes: 11 additions & 2 deletions examples/drizzle/src/protect.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import 'dotenv/config'
import { protect, csColumn, csTable } from '@cipherstash/protect'
import {
protect,
csColumn,
csTable,
type ProtectClientConfig,
} from '@cipherstash/protect'

export const users = csTable('users', {
email_encrypted: csColumn('email_encrypted')
Expand All @@ -8,4 +13,8 @@ export const users = csTable('users', {
.freeTextSearch(),
})

export const protectClient = await protect(users)
const config: ProtectClientConfig = {
schemas: [users],
}

export const protectClient = await protect(config)
13 changes: 11 additions & 2 deletions examples/hono-supabase/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,22 @@ import { createClient } from '@supabase/supabase-js'
import { Hono } from 'hono'

// Consolidated protect and it's schemas into a single file
import { protect, csColumn, csTable } from '@cipherstash/protect'
import {
protect,
csColumn,
csTable,
type ProtectClientConfig,
} from '@cipherstash/protect'

export const users = csTable('users', {
email: csColumn('email'),
})

export const protectClient = await protect(users)
const config: ProtectClientConfig = {
schemas: [users],
}

export const protectClient = await protect(config)

// Create a single supabase client for interacting with the database
const supabaseUrl = process.env.SUPABASE_URL
Expand Down
2 changes: 1 addition & 1 deletion examples/next-drizzle-mysql/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"db:migrate": "drizzle-kit migrate"
},
"dependencies": {
"@cipherstash/protect": "^8.3.0",
"@cipherstash/protect": "workspace:*",
"@hookform/resolvers": "^5.0.1",
"drizzle-orm": "^0.44.0",
"mysql2": "^3.14.1",
Expand Down
8 changes: 6 additions & 2 deletions examples/next-drizzle-mysql/src/protect/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { protect } from '@cipherstash/protect'
import { protect, type ProtectClientConfig } from '@cipherstash/protect'
import { users } from './schema'

export const protectClient = await protect(users)
const config: ProtectClientConfig = {
schemas: [users],
}

export const protectClient = await protect(config)
7 changes: 6 additions & 1 deletion examples/nextjs-clerk/src/core/protect/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@ import {
type CtsToken,
csColumn,
csTable,
type ProtectClientConfig,
} from '@cipherstash/protect'

export const users = csTable('users', {
email: csColumn('email'),
})

export const protectClient = await protect(users)
const config: ProtectClientConfig = {
schemas: [users],
}

export const protectClient = await protect(config)

export const getLockContext = (cts_token?: CtsToken) => {
if (!cts_token) {
Expand Down
13 changes: 9 additions & 4 deletions packages/protect/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ At the end of `stash setup`, you will have two files in your project:
> Don't commit `cipherstash.secret.toml` to git; it contains sensitive credentials.
> The `stash setup` command will attempt to append to your `.gitignore` file with the `cipherstash.secret.toml` file.

Read more about [configuration via TOML file or environment variables](./docs/reference/configuration.md).
Read more about [configuration via TOML file, environment variables, or the Protect client config object](./docs/reference/configuration.md) to meet your needs.
The method you choose will depend on your use case.

### Basic file structure

Expand Down Expand Up @@ -209,14 +210,18 @@ Read more about [defining your schema](./docs/reference/schema.md).
To import the `protect` function and initialize a client with your defined schema, add the following to `src/protect/index.ts`:

```ts
import { protect } from "@cipherstash/protect";
import { protect, type ProtectClientConfig } from "@cipherstash/protect";
import { users, orders } from "./schema";

const config: ProtectClientConfig = {
schemas: [users, orders],
}

// Pass all your tables to the protect function to initialize the client
export const protectClient = await protect(users, orders);
export const protectClient = await protect(config);
```

The `protect` function requires at least one `csTable` be provided.
The `protect` function requires at least one `csTable` be provided in the `schemas` array.

### Encrypt data

Expand Down
Loading