Skip to content

Commit a872301

Browse files
j-patersonclaude
andcommitted
Fix private spaces docs: salt parameter and private bucket access
- Updated stringToCipherKey to show identity-specific salt parameter instead of hardcoded "salt" string, with usage example - Fixed Load Homebase section to use createSignedUrl() or download() instead of getPublicUrl() which doesn't work for private buckets Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent d1f91a3 commit a872301

File tree

1 file changed

+30
-4
lines changed

1 file changed

+30
-4
lines changed

docs/SYSTEMS/SPACES/PRIVATE_SPACES.md

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,22 @@ Each identity has its own:
9494

9595
### Key Derivation
9696

97-
Encryption keys are derived from the identity's private key:
97+
Encryption keys are derived from the identity's private key and salt:
9898

9999
```typescript
100-
function stringToCipherKey(privateKey: string): Uint8Array {
101-
return hkdf(sha256, privateKey, "salt", "", 32);
100+
function stringToCipherKey(privateKey: string, identitySalt: string): Uint8Array {
101+
return hkdf(sha256, privateKey, identitySalt, "", 32);
102102
}
103+
104+
// Usage with identity
105+
const key = stringToCipherKey(
106+
identity.rootKeys.privateKey,
107+
identity.rootKeys.salt // 32-byte random nonce from identity
108+
);
103109
```
104110

111+
The `identitySalt` comes from the identity's `rootKeys.salt` field - a 32-byte random nonce generated when the identity is created. This ensures each identity derives unique encryption keys even if private keys were somehow similar.
112+
105113
### Two Key Types
106114

107115
1. **Root Keys** - Long-lived identity keys
@@ -213,10 +221,28 @@ Body: SignedFile (encrypted homebase config)
213221

214222
### Load Homebase
215223

224+
Private buckets require authenticated access. Use one of these methods:
225+
226+
**Option 1: Time-limited signed URL**
227+
```typescript
228+
// Creates a temporary URL valid for the specified duration
229+
const { data, error } = await supabase.storage
230+
.from("private")
231+
.createSignedUrl("{identityKey}/homebase", 60); // expires in 60 seconds
232+
233+
const response = await fetch(data.signedUrl);
216234
```
217-
GET Supabase.storage.from("private").getPublicUrl("{identityKey}/homebase")
235+
236+
**Option 2: Authenticated download (requires user JWT)**
237+
```typescript
238+
// Direct download with authenticated client
239+
const { data, error } = await supabase.storage
240+
.from("private")
241+
.download("{identityKey}/homebase");
218242
```
219243

244+
Note: `getPublicUrl()` does not work for private buckets - it only generates URLs for public buckets. Always use `createSignedUrl()` for time-limited access or `download()` with an authenticated Supabase client.
245+
220246
### Manage Tabs
221247

222248
```

0 commit comments

Comments
 (0)