+6
.letta/settings.json
+6
.letta/settings.json
+3
.letta/settings.local.json
+3
.letta/settings.local.json
-241
CLAUDE.md
-241
CLAUDE.md
···
1
-
# CLAUDE.md
2
-
3
-
This file provides guidance to Claude Code (claude.ai/code) when working with
4
-
code in this repository.
5
-
6
-
## Commands
7
-
8
-
### Testing
9
-
10
-
```bash
11
-
# Run all tests across the monorepo
12
-
deno test --allow-env
13
-
14
-
# Run tests for a specific package
15
-
deno test packages/crypto/
16
-
17
-
# Run E2E tests (requires CISTERN_HANDLE and CISTERN_APP_PASSWORD environment variables)
18
-
deno test --allow-env --allow-net e2e.test.ts
19
-
```
20
-
21
-
### Lexicon Code Generation
22
-
23
-
```bash
24
-
# Generate TypeScript types from JSON lexicon definitions
25
-
cd packages/lexicon
26
-
deno task generate
27
-
```
28
-
29
-
This generates types in `packages/lexicon/src/types/` from the JSON schemas in
30
-
`packages/lexicon/lexicons/`. Run this after modifying any `.json` files in the
31
-
lexicons directory.
32
-
33
-
### Type Checking
34
-
35
-
```bash
36
-
# Deno executes TypeScript directly - no build step needed
37
-
# Check types explicitly with:
38
-
deno check <file.ts>
39
-
```
40
-
41
-
## Architecture Overview
42
-
43
-
Cistern is a **Deno monorepo** implementing a private, encrypted quick-capture
44
-
system on AT Protocol. Items are end-to-end encrypted using post-quantum
45
-
cryptography and stored temporarily in the user's PDS (Personal Data Server).
46
-
47
-
### Monorepo Structure
48
-
49
-
Five packages with clear separation of concerns:
50
-
51
-
- **`@cistern/crypto`** - Core cryptographic primitives
52
-
(encryption/decryption/keys)
53
-
- **`@cistern/lexicon`** - AT Protocol schema definitions (pubkey + item
54
-
records)
55
-
- **`@cistern/shared`** - Authentication utilities and common code
56
-
- **`@cistern/producer`** - Creates and encrypts items for storage
57
-
- **`@cistern/consumer`** - Retrieves, decrypts, and deletes items
58
-
59
-
Internal imports use the `@cistern/*` namespace defined in each package's
60
-
`deno.jsonc`.
61
-
62
-
### Producer/Consumer Pattern
63
-
64
-
**Producer** workflow (packages/producer/mod.ts):
65
-
66
-
1. Select a public key from those registered in the user's PDS
67
-
2. Encrypt plaintext using the public key
68
-
3. Create an `app.cistern.lexicon.item` record with the encrypted payload
69
-
4. Upload to PDS
70
-
71
-
**Consumer** workflow (packages/consumer/mod.ts):
72
-
73
-
1. Generate an X-Wing keypair (post-quantum)
74
-
2. Upload public key to PDS as `app.cistern.lexicon.pubkey` record
75
-
3. Keep private key locally (never uploaded)
76
-
4. Retrieve items via **polling** (`listItems()`) or **streaming**
77
-
(`subscribeToItems()`)
78
-
5. Decrypt items matching the local keypair
79
-
6. Delete items after consumption
80
-
81
-
### Encryption Architecture
82
-
83
-
**Algorithm**: `x_wing-xchacha20_poly1305-sha3_512`
84
-
85
-
The encryption system uses a hybrid approach combining:
86
-
87
-
- **X-Wing KEM** (Key Encapsulation Mechanism) - post-quantum hybrid combining
88
-
ML-KEM-768 and X25519
89
-
- **XChaCha20-Poly1305** - authenticated encryption cipher
90
-
- **SHA3-512** - content integrity verification
91
-
92
-
**Encryption flow** (packages/crypto/src/encrypt.ts):
93
-
94
-
1. X-Wing encapsulation generates a shared secret from the public key
95
-
2. XChaCha20-Poly1305 encrypts the plaintext using the shared secret
96
-
3. SHA3-512 hash computed for integrity verification
97
-
4. Returns `EncryptedPayload` containing ciphertext, nonce, hash, and metadata
98
-
99
-
**Decryption flow** (packages/crypto/src/decrypt.ts):
100
-
101
-
1. X-Wing decapsulation recovers the shared secret using the private key
102
-
2. XChaCha20-Poly1305 decrypts the content
103
-
3. Integrity verification: check content length and SHA3-512 hash match
104
-
4. Returns plaintext or throws error if verification fails
105
-
106
-
### AT Protocol Integration
107
-
108
-
Cistern uses two record types in the user's PDS:
109
-
110
-
**`app.cistern.lexicon.pubkey`**
111
-
(packages/lexicon/lexicons/app/cistern/lexicon/pubkey.json):
112
-
113
-
- Stores public keys with human-readable names
114
-
- Referenced by items via AT-URI
115
-
- Schema: `{name, algorithm, content, createdAt}`
116
-
117
-
**`app.cistern.lexicon.item`**
118
-
(packages/lexicon/lexicons/app/cistern/lexicon/item.json):
119
-
120
-
- Stores encrypted items temporarily
121
-
- Schema:
122
-
`{tid, ciphertext, nonce, algorithm, pubkey, payload, contentLength, contentHash}`
123
-
- The `pubkey` field is an AT-URI reference to the public key record
124
-
125
-
### Real-time Streaming
126
-
127
-
The consumer can subscribe to new items via **Jetstream**
128
-
(packages/consumer/mod.ts:150-190):
129
-
130
-
- Connects to Bluesky's Jetstream WebSocket service
131
-
- Filters for `app.cistern.lexicon.item` creates matching user DID
132
-
- Decrypts items as they arrive in real-time
133
-
- Used for instant delivery (e.g., Obsidian plugin waiting for new memos)
134
-
135
-
### Key Management
136
-
137
-
**Private keys never leave the consumer's device.** The security model depends
138
-
on:
139
-
140
-
- Private key stored off-protocol (e.g., in an Obsidian vault)
141
-
- Public key stored in PDS as a record
142
-
- Items encrypted with public key can only be decrypted by matching private key
143
-
- Each keypair can have a human-readable name (e.g., "Work Laptop", "Phone")
144
-
145
-
### Dependencies
146
-
147
-
**Cryptography** (JSR packages):
148
-
149
-
- `@noble/post-quantum` - X-Wing KEM implementation
150
-
- `@noble/ciphers` - XChaCha20-Poly1305
151
-
- `@noble/hashes` - SHA3-512
152
-
153
-
**AT Protocol** (npm packages):
154
-
155
-
- `@atcute/client` - RPC client for PDS communication
156
-
- `@atcute/jetstream` - Real-time event streaming
157
-
- `@atcute/lexicons` - Schema validation
158
-
- `@atcute/tid` - Timestamp identifiers
159
-
160
-
## Key Files and Locations
161
-
162
-
### Cryptographic Operations
163
-
164
-
- `packages/crypto/src/keys.ts` - Keypair generation (X-Wing)
165
-
- `packages/crypto/src/encrypt.ts` - Encryption logic
166
-
- `packages/crypto/src/decrypt.ts` - Decryption + integrity verification
167
-
- `packages/crypto/src/*.test.ts` - Crypto unit tests
168
-
169
-
### Producer Implementation
170
-
171
-
- `packages/producer/mod.ts` - Main producer class and encryption workflow
172
-
173
-
### Consumer Implementation
174
-
175
-
- `packages/consumer/mod.ts` - Keypair management, item retrieval, Jetstream
176
-
subscription
177
-
178
-
### Authentication
179
-
180
-
- `packages/shared/produce-requirements.ts` - DID resolution and session
181
-
creation
182
-
- Uses Slingshot service for handle → DID resolution
183
-
- Creates authenticated RPC client with app password
184
-
185
-
### Schema Definitions
186
-
187
-
- `packages/lexicon/lexicons/app/cistern/lexicon/*.json` - AT Protocol record
188
-
schemas
189
-
- `packages/lexicon/src/types/` - Generated TypeScript types (run
190
-
`deno task generate` to update)
191
-
- `packages/lexicon/lex.config.ts` - Lexicon generator configuration
192
-
193
-
## Important Patterns
194
-
195
-
### Error Handling in Decryption
196
-
197
-
Decryption can fail for multiple reasons (packages/crypto/src/decrypt.ts):
198
-
199
-
- Wrong private key (decapsulation fails)
200
-
- Corrupted ciphertext (authentication fails)
201
-
- Length mismatch (integrity check fails)
202
-
- Hash mismatch (integrity check fails)
203
-
204
-
Always wrap decrypt calls in try-catch and handle gracefully.
205
-
206
-
### Pagination in Consumer
207
-
208
-
`listItems()` returns an async generator that handles pagination automatically.
209
-
It yields decrypted items and internally manages cursors. Consumers should
210
-
iterate with `for await` loops.
211
-
212
-
### Resource URIs
213
-
214
-
AT Protocol uses AT-URIs to reference records: `at://<did>/<collection>/<rkey>`
215
-
216
-
The consumer caches the public key's AT-URI with the local keypair to filter
217
-
which items it can decrypt.
218
-
219
-
## Testing
220
-
221
-
### Unit Tests
222
-
223
-
Each package contains unit tests following these conventions:
224
-
- Test files use `.test.ts` suffix
225
-
- Use `@std/expect` for assertions
226
-
- Mock external dependencies (RPC clients, credentials)
227
-
- Test both success and error paths
228
-
229
-
**Test locations:**
230
-
- `packages/crypto/src/*.test.ts` - Cryptographic operations
231
-
- `packages/consumer/mod.test.ts` - Consumer functionality
232
-
- `packages/producer/mod.test.ts` - Producer functionality
233
-
234
-
### End-to-End Tests
235
-
236
-
`e2e.test.ts` contains integration tests that use real AT Protocol credentials:
237
-
- Requires `CISTERN_HANDLE` and `CISTERN_APP_PASSWORD` environment variables
238
-
- Tests full workflow: keypair generation, encryption, decryption, deletion
239
-
- Uses Deno test steps to segment each phase
240
-
- Automatically skipped if environment variables are not set
241
-
- Cleans up all test data after execution