Skip to content

Commit 39accd9

Browse files
committed
fix(sdk-core): ensure repository operations route to correct server (PDS/SDS)
When using OAuth to access organization repositories on SDS via repo.repo(orgDid), operations were incorrectly routing to the user's PDS instead of the SDS server. Root cause: Agent was created from OAuth session but only had api.xrpc.uri configured. Without setting agent.service, it continued using session's default PDS URL. Solution: Set both agent.service and agent.api.xrpc.uri in Repository constructor to ensure all operations route to the specified server URL. Affected operations: - HypercertOperationsImpl: list(), listCollections(), get(), create() - RecordOperationsImpl: all CRUD operations - ProfileOperationsImpl: get(), update() - BlobOperationsImpl: upload(), get() Also added comprehensive PDS/SDS orchestration documentation to README covering server types, routing mechanics, and common usage patterns. Fixes: Repository operations on organization repos routing to user's PDS Location: packages/sdk-core/src/repository/Repository.ts:188
1 parent faf4c3f commit 39accd9

File tree

3 files changed

+127
-8
lines changed

3 files changed

+127
-8
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
"@hypercerts-org/sdk-core": patch
3+
---
4+
5+
fix(sdk-core): ensure repository operations route to correct server (PDS/SDS)
6+
7+
**Problem:**
8+
When using OAuth authentication to access organization repositories on SDS via `repo.repo(organizationDid)`, all operations like `hypercerts.list()` and `hypercerts.listCollections()` were incorrectly routing to the user's PDS instead of the SDS server, causing "Could not find repo" errors.
9+
10+
**Root Cause:**
11+
The AT Protocol Agent was created from the OAuth session but only had its `api.xrpc.uri` property configured. Without setting the Agent's `service` property, it continued using the session's default PDS URL for all requests, even when switched to organization repositories.
12+
13+
**Solution:**
14+
Set both `agent.service` and `agent.api.xrpc.uri` to the specified server URL in the Repository constructor. This ensures that:
15+
- Initial repository creation routes to the correct server (PDS or SDS)
16+
- Repository switching via `.repo(did)` maintains the same server routing
17+
- All operation implementations (HypercertOperationsImpl, RecordOperationsImpl, ProfileOperationsImpl, BlobOperationsImpl) now route correctly
18+
19+
**Documentation:**
20+
Added comprehensive PDS/SDS orchestration explanation to README covering:
21+
- Server type comparison and use cases
22+
- How repository routing works internally
23+
- Common patterns for personal vs organization hypercerts
24+
- Key implementation details about Agent configuration

packages/sdk-core/README.md

Lines changed: 100 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,99 @@ const claim = await repo.hypercerts.create({
4444

4545
## Core Concepts
4646

47-
### 1. Authentication
47+
### 1. PDS vs SDS: Understanding Server Types
48+
49+
The SDK supports two types of AT Protocol servers:
50+
51+
#### Personal Data Server (PDS)
52+
- **Purpose**: User's own data storage (e.g., Bluesky)
53+
- **Use case**: Individual hypercerts, personal records
54+
- **Features**: Profile management, basic CRUD operations
55+
- **Example**: `bsky.social`, any Bluesky PDS
56+
57+
#### Shared Data Server (SDS)
58+
- **Purpose**: Collaborative data storage with access control
59+
- **Use case**: Organization hypercerts, team collaboration
60+
- **Features**: Organizations, multi-user access, role-based permissions
61+
- **Example**: `sds.hypercerts.org`
62+
63+
```typescript
64+
// Connect to user's PDS (default)
65+
const pdsRepo = sdk.repository(session);
66+
await pdsRepo.hypercerts.create({ ... }); // Creates in user's PDS
67+
68+
// Connect to SDS for collaboration features
69+
const sdsRepo = sdk.repository(session, { server: "sds" });
70+
await sdsRepo.organizations.create({ name: "My Org" }); // SDS-only feature
71+
72+
// Switch to organization repository (still on SDS)
73+
const orgs = await sdsRepo.organizations.list();
74+
const orgRepo = sdsRepo.repo(orgs.organizations[0].did);
75+
await orgRepo.hypercerts.list(); // Queries organization's hypercerts on SDS
76+
```
77+
78+
#### How Repository Routing Works
79+
80+
When you create or switch repositories, the SDK ensures requests are routed to the correct server:
81+
82+
1. **Initial Repository Creation**
83+
```typescript
84+
// User authenticates (OAuth session knows user's PDS)
85+
const session = await sdk.callback(params);
86+
87+
// Create PDS repository - routes to user's PDS
88+
const pdsRepo = sdk.repository(session);
89+
90+
// Create SDS repository - routes to SDS server
91+
const sdsRepo = sdk.repository(session, { server: "sds" });
92+
```
93+
94+
2. **Switching Repositories with `.repo()`**
95+
```typescript
96+
// Start with user's SDS repository
97+
const userSdsRepo = sdk.repository(session, { server: "sds" });
98+
99+
// Switch to organization's repository
100+
const orgRepo = userSdsRepo.repo("did:plc:org-did");
101+
102+
// All operations on orgRepo still route to SDS, not user's PDS
103+
await orgRepo.hypercerts.list(); // ✅ Queries SDS
104+
await orgRepo.collaborators.list(); // ✅ Queries SDS
105+
```
106+
107+
3. **Key Implementation Details**
108+
- The SDK configures the AT Protocol Agent's service URL when creating repositories
109+
- When you call `.repo(did)`, a new Repository instance is created that maintains the same server URL
110+
- This ensures that even when querying different DIDs, requests go to the intended server
111+
- User's OAuth session provides authentication, but doesn't determine routing
112+
113+
#### Common Patterns
114+
115+
```typescript
116+
// Pattern 1: Personal hypercerts on PDS
117+
const myRepo = sdk.repository(session);
118+
await myRepo.hypercerts.create({ title: "My Personal Impact" });
119+
120+
// Pattern 2: Organization hypercerts on SDS
121+
const sdsRepo = sdk.repository(session, { server: "sds" });
122+
const orgRepo = sdsRepo.repo(organizationDid);
123+
await orgRepo.hypercerts.create({ title: "Team Impact" });
124+
125+
// Pattern 3: Reading another user's hypercerts
126+
const otherUserRepo = myRepo.repo("did:plc:other-user");
127+
await otherUserRepo.hypercerts.list(); // Read-only access to their PDS
128+
129+
// Pattern 4: Collaborating on organization data
130+
const sdsRepo = sdk.repository(session, { server: "sds" });
131+
await sdsRepo.collaborators.grant({
132+
userDid: "did:plc:teammate",
133+
role: "editor",
134+
});
135+
const orgRepo = sdsRepo.repo(organizationDid);
136+
// Teammate can now access orgRepo and create hypercerts
137+
```
138+
139+
### 2. Authentication
48140

49141
The SDK uses OAuth 2.0 for authentication with support for both PDS (Personal Data Server) and SDS (Shared Data Server).
50142

@@ -67,7 +159,7 @@ const session = await sdk.restoreSession("did:plc:user123");
67159
const repo = sdk.getRepository(session);
68160
```
69161

70-
### 2. Working with Hypercerts
162+
### 3. Working with Hypercerts
71163

72164
#### Creating a Hypercert
73165

@@ -144,7 +236,7 @@ await repo.hypercerts.delete(
144236
);
145237
```
146238

147-
### 3. Contributions and Measurements
239+
### 4. Contributions and Measurements
148240

149241
#### Adding Contributions
150242

@@ -174,7 +266,7 @@ const measurement = await repo.hypercerts.addMeasurement({
174266
});
175267
```
176268

177-
### 4. Blob Operations (Images & Files)
269+
### 5. Blob Operations (Images & Files)
178270

179271
```typescript
180272
// Upload an image or file
@@ -188,7 +280,7 @@ const blobData = await repo.blobs.get(
188280
);
189281
```
190282

191-
### 5. Organizations (SDS only)
283+
### 6. Organizations (SDS only)
192284

193285
Organizations allow multiple users to collaborate on shared repositories.
194286

@@ -216,7 +308,7 @@ const org = await repo.organizations.get("did:plc:org123");
216308
console.log(`${org.name} - ${org.description}`);
217309
```
218310

219-
### 6. Collaborator Management (SDS only)
311+
### 7. Collaborator Management (SDS only)
220312

221313
Manage who has access to your repository and what they can do.
222314

@@ -285,7 +377,7 @@ await repo.collaborators.transferOwnership({
285377
});
286378
```
287379

288-
### 7. Generic Record Operations
380+
### 8. Generic Record Operations
289381

290382
For working with any ATProto record type:
291383

@@ -330,7 +422,7 @@ const { records, cursor } = await repo.records.list({
330422
});
331423
```
332424

333-
### 8. Profile Management (PDS only)
425+
### 9. Profile Management (PDS only)
334426

335427
```typescript
336428
// Get user profile

packages/sdk-core/src/repository/Repository.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,9 @@ export class Repository {
184184

185185
// Configure Agent to use the specified server URL (PDS or SDS)
186186
// This ensures queries are routed to the correct server
187+
// We need to set both the service URL and the XRPC URI to ensure all
188+
// requests are routed to the correct server, not the session's default PDS
189+
this.agent.service = new URL(serverUrl);
187190
this.agent.api.xrpc.uri = new URL(serverUrl);
188191

189192
this.lexiconRegistry.addToAgent(this.agent);

0 commit comments

Comments
 (0)