Skip to content

Commit 58beea5

Browse files
authored
Merge pull request #294 from cipherstash/stack-package
docs: clean up docs for Stash Stack
2 parents 653c220 + f3ef5d3 commit 58beea5

File tree

13 files changed

+1202
-139
lines changed

13 files changed

+1202
-139
lines changed

docs/README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# CipherStash Encryption documentation
1+
# CipherStash documentation
22

3-
The documentation for CipherStash Encryption is organized into the following sections:
3+
## Getting started
44

55
- [Getting started](./getting-started.md)
66

@@ -10,16 +10,20 @@ The documentation for CipherStash Encryption is organized into the following sec
1010

1111
## Reference
1212

13+
- [Workspace plans and limits](./reference/plans.md)
1314
- [Configuration and production deployment](./reference/configuration.md)
14-
- [Searchable encryption with PostgreSQL](./reference/searchable-encryption-postgres.md) (includes JSONB queries with searchableJson)
1515
- [Encryption schemas](./reference/schema.md)
1616
- [Model operations with bulk crypto functions](./reference/model-operations.md)
17+
- [Searchable encryption with PostgreSQL](./reference/searchable-encryption-postgres.md) (includes JSONB queries with searchableJson)
18+
- [Secrets management](./reference/secrets.md)
19+
- [Identity-aware encryption](./reference/identity.md)
1720

1821
### ORMs and frameworks
1922

2023
- [Supabase SDK](./reference/supabase-sdk.md)
24+
- [DynamoDB integration](./reference/dynamodb.md)
2125

22-
### Drizzle ORM Integration
26+
### Drizzle ORM integration
2327

2428
- [Encryption Operators Pattern](reference/drizzle/drizzle.md) - Recommended approach with auto-encrypting operators
2529
- [Manual Encryption Pattern](reference/drizzle/drizzle-protect.md) - Explicit control over encryption workflow

docs/reference/configuration.md

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
CipherStash Encryption is configured with [toml](https://toml.io/en/) files or environment variables, and is used to initialize the Encryption client.
44

5-
Environment variables will take precedence over configuration files and it's recommented to use them for sensitive values.
5+
Environment variables will take precedence over configuration files and it's recommended to use them for sensitive values.
66

77
## Table of contents
88

@@ -99,9 +99,8 @@ The following environment variables are supported:
9999

100100
## Configuring the Encryption client directly
101101

102-
You can also configure the Encryption client directly by passing a `EncryptionClientConfig` object to the `Encryption` function during initialization.
103-
This is useful if you want to configure the Encryption client specific to your application.
104-
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.
102+
You can also configure the Encryption client directly by passing an `EncryptionClientConfig` object to the `Encryption` function during initialization.
103+
This is useful 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.
105104

106105
```ts
107106
import { Encryption, type EncryptionClientConfig } from "@cipherstash/stack";
@@ -119,6 +118,52 @@ const config: EncryptionClientConfig = {
119118
const client = await Encryption(config);
120119
```
121120

121+
### Multi-tenant keyset configuration
122+
123+
For multi-tenant applications, you can specify a keyset to isolate encryption keys per tenant.
124+
A keyset can be identified by name or by UUID:
125+
126+
```ts
127+
const client = await Encryption({
128+
schemas: [users],
129+
config: {
130+
workspaceCrn: "your-workspace-crn",
131+
accessKey: "your-access-key",
132+
clientId: "your-client-id",
133+
clientKey: "your-client-key",
134+
keyset: { name: "tenant-acme" },
135+
// or by UUID:
136+
// keyset: { id: "550e8400-e29b-41d4-a716-446655440000" },
137+
},
138+
});
139+
```
140+
141+
Each keyset provides an isolated set of encryption keys, so data encrypted under one keyset cannot be decrypted with another.
142+
143+
### Logging configuration
144+
145+
You can configure structured logging for the Encryption client:
146+
147+
```ts
148+
const client = await Encryption({
149+
schemas: [users],
150+
logging: {
151+
enabled: true,
152+
pretty: true, // Pretty-print log output (auto-detected in development)
153+
drain: (ctx) => {
154+
// Forward logs to your logging service
155+
myLogger.log(ctx)
156+
},
157+
},
158+
});
159+
```
160+
161+
| Option | Type | Default | Description |
162+
|--------|------|---------|-------------|
163+
| `enabled` | `boolean` | `true` | Toggle logging on or off. |
164+
| `pretty` | `boolean` | Auto-detected | Enable pretty-printed log format. |
165+
| `drain` | `(ctx) => void` | `undefined` | Callback for forwarding log events to an external platform. |
166+
122167
## Deploying to production
123168

124169
> [!TIP]

docs/reference/dynamodb.md

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
# DynamoDB integration
2+
3+
CipherStash Encryption provides a DynamoDB helper that transparently encrypts and decrypts items according to your encryption schema.
4+
The helper wraps your existing DynamoDB workflow — you handle the DynamoDB client calls, and the helper handles encryption.
5+
6+
## Table of contents
7+
8+
- [Overview](#overview)
9+
- [Installation](#installation)
10+
- [Setting up encryptedDynamoDB](#setting-up-encrypteddynamodb)
11+
- [Encrypting a model](#encrypting-a-model)
12+
- [Decrypting a model](#decrypting-a-model)
13+
- [Bulk operations](#bulk-operations)
14+
- [Bulk encryption](#bulk-encryption)
15+
- [Bulk decryption](#bulk-decryption)
16+
- [Using nested objects](#using-nested-objects)
17+
- [Error handling](#error-handling)
18+
19+
## Overview
20+
21+
The `encryptedDynamoDB` function creates a helper bound to your `EncryptionClient`.
22+
It provides `encryptModel`, `decryptModel`, `bulkEncryptModels`, and `bulkDecryptModels` methods that work with your DynamoDB items.
23+
24+
Unlike the Supabase and Drizzle integrations, the DynamoDB helper does not wrap a database client.
25+
You use it to encrypt items before sending them to DynamoDB and decrypt items after retrieving them.
26+
27+
## Installation
28+
29+
The DynamoDB helper is included in the `@cipherstash/stack` package:
30+
31+
```bash
32+
npm install @cipherstash/stack
33+
```
34+
35+
Import from the `@cipherstash/stack/dynamodb` subpath:
36+
37+
```typescript
38+
import { encryptedDynamoDB } from '@cipherstash/stack/dynamodb'
39+
```
40+
41+
## Setting up encryptedDynamoDB
42+
43+
Create an encryption schema and initialize the helper:
44+
45+
```typescript
46+
import { Encryption } from '@cipherstash/stack'
47+
import { encryptedDynamoDB } from '@cipherstash/stack/dynamodb'
48+
import { encryptedTable, encryptedColumn } from '@cipherstash/stack/schema'
49+
50+
// 1. Define your encryption schema
51+
const users = encryptedTable('users', {
52+
email: encryptedColumn('email').equality(),
53+
name: encryptedColumn('name'),
54+
})
55+
56+
// 2. Initialize the encryption client
57+
const client = await Encryption({ schemas: [users] })
58+
59+
// 3. Create the DynamoDB helper
60+
const dynamo = encryptedDynamoDB({ encryptionClient: client })
61+
```
62+
63+
### Configuration options
64+
65+
The `encryptedDynamoDB` function accepts an `EncryptedDynamoDBConfig` object:
66+
67+
| Option | Type | Required | Description |
68+
|--------|------|----------|-------------|
69+
| `encryptionClient` | `EncryptionClient` | Yes | The initialized encryption client. |
70+
| `options.logger` | `{ error: (message, error) => void }` | No | Custom logger for error reporting. |
71+
| `options.errorHandler` | `(error: EncryptedDynamoDBError) => void` | No | Custom error handler callback. |
72+
73+
## Encrypting a model
74+
75+
Use `encryptModel` to encrypt an item before writing it to DynamoDB:
76+
77+
```typescript
78+
import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb'
79+
import { marshall } from '@aws-sdk/util-dynamodb'
80+
81+
const user = {
82+
id: '1',
83+
email: 'user@example.com',
84+
name: 'Alice Johnson',
85+
role: 'admin', // Not in schema — will remain unchanged
86+
}
87+
88+
const encryptedResult = await dynamo.encryptModel(user, users)
89+
90+
if (encryptedResult.failure) {
91+
console.error('Encryption failed:', encryptedResult.failure.message)
92+
return
93+
}
94+
95+
// Write the encrypted item to DynamoDB
96+
const dynamoClient = new DynamoDBClient({})
97+
await dynamoClient.send(
98+
new PutItemCommand({
99+
TableName: 'users',
100+
Item: marshall(encryptedResult.data),
101+
})
102+
)
103+
```
104+
105+
Fields defined in the encryption schema (`email`, `name`) are encrypted.
106+
Fields not in the schema (`id`, `role`) pass through unchanged.
107+
108+
## Decrypting a model
109+
110+
Use `decryptModel` to decrypt an item after reading it from DynamoDB:
111+
112+
```typescript
113+
import { GetItemCommand } from '@aws-sdk/client-dynamodb'
114+
import { unmarshall } from '@aws-sdk/util-dynamodb'
115+
116+
const response = await dynamoClient.send(
117+
new GetItemCommand({
118+
TableName: 'users',
119+
Key: marshall({ id: '1' }),
120+
})
121+
)
122+
123+
const item = unmarshall(response.Item!)
124+
125+
const decryptedResult = await dynamo.decryptModel(item, users)
126+
127+
if (decryptedResult.failure) {
128+
console.error('Decryption failed:', decryptedResult.failure.message)
129+
return
130+
}
131+
132+
console.log(decryptedResult.data.email) // 'user@example.com'
133+
```
134+
135+
## Bulk operations
136+
137+
### Bulk encryption
138+
139+
Use `bulkEncryptModels` for encrypting multiple items:
140+
141+
```typescript
142+
const items = [
143+
{ id: '1', email: 'alice@example.com', name: 'Alice' },
144+
{ id: '2', email: 'bob@example.com', name: 'Bob' },
145+
]
146+
147+
const encryptedResult = await dynamo.bulkEncryptModels(items, users)
148+
149+
if (encryptedResult.failure) {
150+
console.error('Bulk encryption failed:', encryptedResult.failure.message)
151+
return
152+
}
153+
154+
// Write encrypted items to DynamoDB using BatchWriteItem
155+
```
156+
157+
### Bulk decryption
158+
159+
Use `bulkDecryptModels` for decrypting multiple items:
160+
161+
```typescript
162+
const decryptedResult = await dynamo.bulkDecryptModels(encryptedItems, users)
163+
164+
if (decryptedResult.failure) {
165+
console.error('Bulk decryption failed:', decryptedResult.failure.message)
166+
return
167+
}
168+
169+
const decryptedUsers = decryptedResult.data
170+
```
171+
172+
## Using nested objects
173+
174+
The DynamoDB helper supports nested object encryption using `encryptedValue`:
175+
176+
```typescript
177+
import { encryptedTable, encryptedColumn, encryptedValue } from '@cipherstash/stack/schema'
178+
179+
const users = encryptedTable('users', {
180+
email: encryptedColumn('email'),
181+
profile: {
182+
name: encryptedValue('profile.name'),
183+
address: {
184+
street: encryptedValue('profile.address.street'),
185+
},
186+
},
187+
})
188+
189+
const dynamo = encryptedDynamoDB({ encryptionClient: client })
190+
191+
const user = {
192+
id: '1',
193+
email: 'user@example.com',
194+
profile: {
195+
name: 'Alice Johnson',
196+
address: {
197+
street: '123 Main St',
198+
city: 'Sydney', // Not in schema — unchanged
199+
},
200+
},
201+
}
202+
203+
const encryptedResult = await dynamo.encryptModel(user, users)
204+
```
205+
206+
> [!NOTE]
207+
> Nested objects support encryption up to 3 levels deep. Searchable encryption is not supported on nested fields.
208+
209+
## Error handling
210+
211+
All DynamoDB helper methods return a `Result` type:
212+
213+
```typescript
214+
const result = await dynamo.encryptModel(item, users)
215+
216+
if (result.failure) {
217+
console.error('Error:', result.failure.type, result.failure.message)
218+
return
219+
}
220+
221+
const encryptedItem = result.data
222+
```
223+
224+
You can also provide a custom error handler in the configuration:
225+
226+
```typescript
227+
const dynamo = encryptedDynamoDB({
228+
encryptionClient: client,
229+
options: {
230+
errorHandler: (error) => {
231+
console.error(`DynamoDB encryption error [${error.code}]:`, error.message)
232+
},
233+
logger: {
234+
error: (message, error) => {
235+
// Send to your logging service
236+
},
237+
},
238+
},
239+
})
240+
```
241+
242+
---
243+
244+
### Didn't find what you wanted?
245+
246+
[Click here to let us know what was missing from our docs.](https://github.com/cipherstash/protectjs/issues/new?template=docs-feedback.yml&title=[Docs:]%20Feedback%20on%20dynamodb.md)

0 commit comments

Comments
 (0)