@pdsjs/spaces 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/package.json +40 -0
- package/src/authority.d.ts +47 -0
- package/src/authority.js +148 -0
- package/src/blake3.d.ts +14 -0
- package/src/blake3.js +272 -0
- package/src/car.d.ts +41 -0
- package/src/car.js +98 -0
- package/src/commit.d.ts +109 -0
- package/src/commit.js +152 -0
- package/src/handlers/auth.d.ts +67 -0
- package/src/handlers/auth.js +436 -0
- package/src/handlers/manage.d.ts +20 -0
- package/src/handlers/manage.js +508 -0
- package/src/handlers/read.d.ts +18 -0
- package/src/handlers/read.js +394 -0
- package/src/handlers/write.d.ts +18 -0
- package/src/handlers/write.js +462 -0
- package/src/index.d.ts +15 -0
- package/src/index.js +56 -0
- package/src/lthash.d.ts +24 -0
- package/src/lthash.js +86 -0
- package/src/mac.d.ts +50 -0
- package/src/mac.js +107 -0
- package/src/memory-storage.d.ts +4 -0
- package/src/memory-storage.js +244 -0
- package/src/path.d.ts +22 -0
- package/src/path.js +33 -0
- package/src/routes.d.ts +23 -0
- package/src/routes.js +42 -0
- package/src/service-auth.d.ts +42 -0
- package/src/service-auth.js +153 -0
- package/src/space-row.d.ts +15 -0
- package/src/space-row.js +33 -0
- package/src/token.d.ts +95 -0
- package/src/token.js +223 -0
- package/src/uri.d.ts +32 -0
- package/src/uri.js +89 -0
- package/src/verifier.d.ts +15 -0
- package/src/verifier.js +74 -0
- package/src/writer.d.ts +77 -0
- package/src/writer.js +195 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
// @pdsjs/spaces/handlers/auth - the space auth chain.
|
|
2
|
+
//
|
|
3
|
+
// app -> PDS.getDelegationToken (OAuth) proves the user delegated
|
|
4
|
+
// app -> authority.getSpaceCredential exchanges it for a credential
|
|
5
|
+
// app -> repo host.<read endpoints> presents the credential
|
|
6
|
+
//
|
|
7
|
+
// The delegation token says only that the user delegated to *someone*; which
|
|
8
|
+
// app is acting is established separately by a client attestation, which the
|
|
9
|
+
// authority verifies itself.
|
|
10
|
+
|
|
11
|
+
import { ScopePermissions } from '@pdsjs/core/scope';
|
|
12
|
+
import {
|
|
13
|
+
didKeyResolver,
|
|
14
|
+
spaceHostAudience,
|
|
15
|
+
spaceHostEndpoint,
|
|
16
|
+
spaceSigningKey,
|
|
17
|
+
} from '../authority.js';
|
|
18
|
+
import { createServiceAuth } from '../service-auth.js';
|
|
19
|
+
import {
|
|
20
|
+
createSpaceToken,
|
|
21
|
+
parseSpaceToken,
|
|
22
|
+
SpaceTokenError,
|
|
23
|
+
verifySpaceToken,
|
|
24
|
+
} from '../token.js';
|
|
25
|
+
import { parseSpaceUri } from '../uri.js';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {string} error
|
|
29
|
+
* @param {string} message
|
|
30
|
+
* @param {number} [status]
|
|
31
|
+
* @returns {Response}
|
|
32
|
+
*/
|
|
33
|
+
function errorResponse(error, message, status = 400) {
|
|
34
|
+
return Response.json({ error, message }, { status });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @param {unknown} err
|
|
39
|
+
* @returns {Response}
|
|
40
|
+
*/
|
|
41
|
+
function tokenErrorResponse(err) {
|
|
42
|
+
if (err instanceof SpaceTokenError) {
|
|
43
|
+
return errorResponse(err.code, err.message, 401);
|
|
44
|
+
}
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {Request} request
|
|
50
|
+
* @returns {Promise<any>}
|
|
51
|
+
*/
|
|
52
|
+
async function readJson(request) {
|
|
53
|
+
try {
|
|
54
|
+
return JSON.parse(await request.text());
|
|
55
|
+
} catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @param {Object} ctx
|
|
62
|
+
* @param {import('@pdsjs/core/ports').SpaceStoragePort} ctx.spaceStorage
|
|
63
|
+
* @param {() => Promise<string|null>} ctx.getDid
|
|
64
|
+
* @param {() => Promise<{sign: (bytes: Uint8Array) => Promise<Uint8Array>}>} ctx.getSigner
|
|
65
|
+
* @param {(did: string) => Promise<any>} ctx.resolveDid
|
|
66
|
+
* @param {import('@pdsjs/core/ports').SignatureVerifierPort} ctx.verifier
|
|
67
|
+
* @param {typeof fetch} [ctx.fetch]
|
|
68
|
+
* @returns {import('@pdsjs/core/pds').Routes}
|
|
69
|
+
*/
|
|
70
|
+
export function createAuthRoutes(ctx) {
|
|
71
|
+
const { spaceStorage, getDid, getSigner, resolveDid, verifier } = ctx;
|
|
72
|
+
const getSigningKey = didKeyResolver(resolveDid);
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
// PDS role: mint a token proving this user delegated to the caller. Signed
|
|
76
|
+
// by the user's own #atproto key and addressed to the authority's space
|
|
77
|
+
// host, so it is useless anywhere else and expires in a minute.
|
|
78
|
+
'/xrpc/com.atproto.space.getDelegationToken': {
|
|
79
|
+
auth: 'required',
|
|
80
|
+
handler: async (_request, url, auth) => {
|
|
81
|
+
const space = url.searchParams.get('space');
|
|
82
|
+
if (!space) return errorResponse('InvalidRequest', 'space is required');
|
|
83
|
+
/** @type {{spaceDid: string, spaceType: string, skey: string}} */
|
|
84
|
+
let parsed;
|
|
85
|
+
try {
|
|
86
|
+
parsed = parseSpaceUri(space);
|
|
87
|
+
} catch {
|
|
88
|
+
return errorResponse('InvalidSpaceUri', `Not a space uri: ${space}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const did = /** @type {{did: string}} */ (auth).did;
|
|
92
|
+
|
|
93
|
+
// `read` grants whole-space access and with it the delegation token;
|
|
94
|
+
// `read_self` deliberately does not, since it only ever covers the
|
|
95
|
+
// holder's own repo and needs no credential.
|
|
96
|
+
const permissions = new ScopePermissions(
|
|
97
|
+
/** @type {{scope?: string}} */ (auth).scope,
|
|
98
|
+
);
|
|
99
|
+
if (
|
|
100
|
+
!permissions.allowsSpace({
|
|
101
|
+
spaceType: parsed.spaceType,
|
|
102
|
+
authority: parsed.spaceDid,
|
|
103
|
+
skey: parsed.skey,
|
|
104
|
+
action: 'read',
|
|
105
|
+
userDid: did,
|
|
106
|
+
})
|
|
107
|
+
) {
|
|
108
|
+
return errorResponse(
|
|
109
|
+
'InvalidToken',
|
|
110
|
+
`Scope does not permit reading ${space}`,
|
|
111
|
+
403,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const hosted = await getDid();
|
|
116
|
+
if (did !== hosted) {
|
|
117
|
+
return errorResponse(
|
|
118
|
+
'Forbidden',
|
|
119
|
+
'Can only mint a delegation token for the hosted account',
|
|
120
|
+
403,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const token = await createSpaceToken(
|
|
125
|
+
'delegation',
|
|
126
|
+
{
|
|
127
|
+
iss: did,
|
|
128
|
+
sub: space,
|
|
129
|
+
aud: spaceHostAudience(parsed.spaceDid),
|
|
130
|
+
},
|
|
131
|
+
await getSigner(),
|
|
132
|
+
);
|
|
133
|
+
return Response.json({ token });
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
// Authority role: exchange a delegation token for a space credential.
|
|
138
|
+
// Unauthenticated at the PDS layer — the delegation token IS the credential
|
|
139
|
+
// for this call, and is verified here.
|
|
140
|
+
'/xrpc/com.atproto.space.getSpaceCredential': {
|
|
141
|
+
method: 'POST',
|
|
142
|
+
handler: async (request) => {
|
|
143
|
+
const body = await readJson(request);
|
|
144
|
+
if (!body) return errorResponse('InvalidRequest', 'Invalid JSON body');
|
|
145
|
+
const { space, clientAttestation } = body;
|
|
146
|
+
if (typeof space !== 'string') {
|
|
147
|
+
return errorResponse('InvalidRequest', 'space is required');
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
parseSpaceUri(space);
|
|
151
|
+
} catch {
|
|
152
|
+
return errorResponse('InvalidSpaceUri', `Not a space uri: ${space}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const header = request.headers.get('authorization') ?? '';
|
|
156
|
+
const match = header.match(/^Bearer\s+(.+)$/i);
|
|
157
|
+
if (!match) {
|
|
158
|
+
return errorResponse(
|
|
159
|
+
'InvalidDelegationToken',
|
|
160
|
+
'A delegation token is required',
|
|
161
|
+
401,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const authorityDid = await getDid();
|
|
166
|
+
if (!authorityDid) {
|
|
167
|
+
return errorResponse('SpaceNotFound', 'Server not initialised', 404);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** @type {string} */
|
|
171
|
+
let userDid;
|
|
172
|
+
try {
|
|
173
|
+
const { payload } = await verifySpaceToken('delegation', match[1], {
|
|
174
|
+
getSigningKey,
|
|
175
|
+
verifier,
|
|
176
|
+
aud: spaceHostAudience(authorityDid),
|
|
177
|
+
sub: space,
|
|
178
|
+
});
|
|
179
|
+
userDid = payload.iss;
|
|
180
|
+
} catch (err) {
|
|
181
|
+
return tokenErrorResponse(err);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Structural validation only. Full verification means resolving the
|
|
185
|
+
// client_id to its client-metadata.json, fetching the published JWKS,
|
|
186
|
+
// and checking the signature against the key named by `kid`. Until then
|
|
187
|
+
// the attested client_id is advisory, so an allowList is only as strong
|
|
188
|
+
// as the caller's honesty. The reference implementation carries the same
|
|
189
|
+
// gap.
|
|
190
|
+
/** @type {string|undefined} */
|
|
191
|
+
let clientId;
|
|
192
|
+
if (clientAttestation) {
|
|
193
|
+
try {
|
|
194
|
+
clientId = parseSpaceToken('clientAttestation', clientAttestation)
|
|
195
|
+
.payload.iss;
|
|
196
|
+
} catch (err) {
|
|
197
|
+
return errorResponse(
|
|
198
|
+
'InvalidClientAttestation',
|
|
199
|
+
err instanceof Error ? err.message : String(err),
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const spaceRow = await spaceStorage.getSpace(space);
|
|
205
|
+
if (!spaceRow || !spaceRow.isOwner) {
|
|
206
|
+
return errorResponse('SpaceNotFound', 'Space not found', 404);
|
|
207
|
+
}
|
|
208
|
+
if (spaceRow.deletedAt) {
|
|
209
|
+
return errorResponse('SpaceDeleted', 'Space has been deleted');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// User perimeter.
|
|
213
|
+
let userAuthorized;
|
|
214
|
+
if (spaceRow.policy === 'public') {
|
|
215
|
+
userAuthorized = true;
|
|
216
|
+
} else if (spaceRow.policy === 'member-list') {
|
|
217
|
+
userAuthorized = await spaceStorage.isMember(space, userDid);
|
|
218
|
+
} else if (spaceRow.policy === 'managing-app') {
|
|
219
|
+
// Delegate the per-user decision to the named service. Any failure —
|
|
220
|
+
// unresolvable DID, unreachable app, malformed answer — denies rather
|
|
221
|
+
// than admits.
|
|
222
|
+
userAuthorized = await checkUserAccess({
|
|
223
|
+
managingApp: spaceRow.managingApp,
|
|
224
|
+
authorityDid,
|
|
225
|
+
space,
|
|
226
|
+
userDid,
|
|
227
|
+
clientId,
|
|
228
|
+
resolveDid,
|
|
229
|
+
getSigner,
|
|
230
|
+
fetch: ctx.fetch,
|
|
231
|
+
});
|
|
232
|
+
} else {
|
|
233
|
+
userAuthorized = false;
|
|
234
|
+
}
|
|
235
|
+
if (!userAuthorized) {
|
|
236
|
+
return errorResponse(
|
|
237
|
+
'UserNotAuthorized',
|
|
238
|
+
'User not authorized for this space',
|
|
239
|
+
403,
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// App perimeter.
|
|
244
|
+
if (spaceRow.appAccessType === 'allowList') {
|
|
245
|
+
if (!clientId || !spaceRow.appAllowed.includes(clientId)) {
|
|
246
|
+
return errorResponse(
|
|
247
|
+
'AppNotAuthorized',
|
|
248
|
+
'Application not authorized for this space',
|
|
249
|
+
403,
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const credential = await createSpaceToken(
|
|
255
|
+
'credential',
|
|
256
|
+
{ iss: authorityDid, sub: space },
|
|
257
|
+
await getSigner(),
|
|
258
|
+
);
|
|
259
|
+
return Response.json({ credential });
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
// Authority role: describe a space.
|
|
264
|
+
'/xrpc/com.atproto.space.getSpace': {
|
|
265
|
+
handler: async (_request, url) => {
|
|
266
|
+
const space = url.searchParams.get('space');
|
|
267
|
+
if (!space) return errorResponse('InvalidRequest', 'space is required');
|
|
268
|
+
const row = await spaceStorage.getSpace(space);
|
|
269
|
+
if (!row || !row.isOwner) {
|
|
270
|
+
return errorResponse('SpaceNotFound', 'Space not found', 404);
|
|
271
|
+
}
|
|
272
|
+
return Response.json({
|
|
273
|
+
uri: row.uri,
|
|
274
|
+
config: {
|
|
275
|
+
$type: 'com.atproto.simplespace.defs#spaceConfig',
|
|
276
|
+
policy: row.policy,
|
|
277
|
+
...(row.managingApp ? { managingApp: row.managingApp } : {}),
|
|
278
|
+
appAccess:
|
|
279
|
+
row.appAccessType === 'allowList'
|
|
280
|
+
? {
|
|
281
|
+
$type: 'com.atproto.simplespace.defs#allowList',
|
|
282
|
+
allowed: row.appAllowed,
|
|
283
|
+
}
|
|
284
|
+
: { $type: 'com.atproto.simplespace.defs#open' },
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
|
|
290
|
+
// Authority role: the writer set, which is the sync boundary. Accounts that
|
|
291
|
+
// have written at least one record — not the broader set allowed to write,
|
|
292
|
+
// which the authority may not even track.
|
|
293
|
+
'/xrpc/com.atproto.space.listRepos': {
|
|
294
|
+
handler: async (_request, url) => {
|
|
295
|
+
const space = url.searchParams.get('space');
|
|
296
|
+
if (!space) return errorResponse('InvalidRequest', 'space is required');
|
|
297
|
+
|
|
298
|
+
const raw = url.searchParams.get('limit');
|
|
299
|
+
const limit = raw === null ? 100 : Number(raw);
|
|
300
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 1000) {
|
|
301
|
+
return errorResponse(
|
|
302
|
+
'InvalidRequest',
|
|
303
|
+
'limit must be an integer between 1 and 1000',
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const row = await spaceStorage.getSpace(space);
|
|
308
|
+
if (!row || !row.isOwner) {
|
|
309
|
+
return errorResponse('SpaceNotFound', 'Space not found', 404);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const { writers, cursor } = await spaceStorage.listSpaceWriters(
|
|
313
|
+
space,
|
|
314
|
+
url.searchParams.get('cursor'),
|
|
315
|
+
limit,
|
|
316
|
+
);
|
|
317
|
+
return Response.json({
|
|
318
|
+
...(cursor ? { cursor } : {}),
|
|
319
|
+
repos: writers.map((w) => ({
|
|
320
|
+
did: w.did,
|
|
321
|
+
rev: w.rev,
|
|
322
|
+
hash: toJsonBytes(w.hash),
|
|
323
|
+
})),
|
|
324
|
+
});
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* @param {Uint8Array} bytes
|
|
332
|
+
* @returns {{$bytes: string}}
|
|
333
|
+
*/
|
|
334
|
+
function toJsonBytes(bytes) {
|
|
335
|
+
let binary = '';
|
|
336
|
+
for (const b of bytes) binary += String.fromCharCode(b);
|
|
337
|
+
return { $bytes: btoa(binary).replace(/=+$/, '') };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Verify a space credential presented to a repo host.
|
|
342
|
+
*
|
|
343
|
+
* @param {Object} opts
|
|
344
|
+
* @param {string} opts.credential - the raw JWT
|
|
345
|
+
* @param {string} opts.space - the space the request targets
|
|
346
|
+
* @param {(did: string) => Promise<any>} opts.resolveDid
|
|
347
|
+
* @param {import('@pdsjs/core/ports').SignatureVerifierPort} opts.verifier
|
|
348
|
+
* @returns {Promise<{iss: string}>}
|
|
349
|
+
*/
|
|
350
|
+
export async function verifySpaceCredential({
|
|
351
|
+
credential,
|
|
352
|
+
space,
|
|
353
|
+
resolveDid,
|
|
354
|
+
verifier,
|
|
355
|
+
}) {
|
|
356
|
+
const { spaceDid } = parseSpaceUri(space);
|
|
357
|
+
const { payload } = await verifySpaceToken('credential', credential, {
|
|
358
|
+
verifier,
|
|
359
|
+
// A credential is signed by the authority's space key, so it is verified
|
|
360
|
+
// without contacting the authority.
|
|
361
|
+
getSigningKey: async (iss) => {
|
|
362
|
+
const didDoc = await resolveDid(iss);
|
|
363
|
+
if (!didDoc) {
|
|
364
|
+
throw new SpaceTokenError(`Could not resolve ${iss}`, 'DidNotFound');
|
|
365
|
+
}
|
|
366
|
+
return spaceSigningKey(didDoc, iss);
|
|
367
|
+
},
|
|
368
|
+
sub: space,
|
|
369
|
+
});
|
|
370
|
+
if (payload.iss !== spaceDid) {
|
|
371
|
+
throw new SpaceTokenError(
|
|
372
|
+
'Credential was not issued by this space authority',
|
|
373
|
+
'BadJwtIss',
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
return { iss: payload.iss };
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Ask a space's managing app whether to authorize a user.
|
|
381
|
+
*
|
|
382
|
+
* Fails closed: anything short of an explicit `authorized: true` is a refusal,
|
|
383
|
+
* so an app being down denies access rather than granting it.
|
|
384
|
+
*
|
|
385
|
+
* @param {Object} opts
|
|
386
|
+
* @param {string|null} opts.managingApp - a `did#serviceId` identifier
|
|
387
|
+
* @param {string} opts.authorityDid
|
|
388
|
+
* @param {string} opts.space
|
|
389
|
+
* @param {string} opts.userDid
|
|
390
|
+
* @param {string|undefined} opts.clientId
|
|
391
|
+
* @param {(did: string) => Promise<any>} opts.resolveDid
|
|
392
|
+
* @param {() => Promise<{sign: (bytes: Uint8Array) => Promise<Uint8Array>}>} opts.getSigner
|
|
393
|
+
* @param {typeof fetch} [opts.fetch]
|
|
394
|
+
* @returns {Promise<boolean>}
|
|
395
|
+
*/
|
|
396
|
+
export async function checkUserAccess({
|
|
397
|
+
managingApp,
|
|
398
|
+
authorityDid,
|
|
399
|
+
space,
|
|
400
|
+
userDid,
|
|
401
|
+
clientId,
|
|
402
|
+
resolveDid,
|
|
403
|
+
getSigner,
|
|
404
|
+
fetch: doFetch,
|
|
405
|
+
}) {
|
|
406
|
+
if (!managingApp) return false;
|
|
407
|
+
try {
|
|
408
|
+
const [appDid] = managingApp.split('#');
|
|
409
|
+
const didDoc = await resolveDid(appDid);
|
|
410
|
+
if (!didDoc) return false;
|
|
411
|
+
const endpoint = spaceHostEndpoint(didDoc, appDid);
|
|
412
|
+
|
|
413
|
+
const token = await createServiceAuth({
|
|
414
|
+
iss: authorityDid,
|
|
415
|
+
aud: managingApp,
|
|
416
|
+
lxm: 'com.atproto.simplespace.checkUserAccess',
|
|
417
|
+
signer: await getSigner(),
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
const url = new URL(
|
|
421
|
+
`${endpoint}/xrpc/com.atproto.simplespace.checkUserAccess`,
|
|
422
|
+
);
|
|
423
|
+
url.searchParams.set('space', space);
|
|
424
|
+
url.searchParams.set('user', userDid);
|
|
425
|
+
if (clientId) url.searchParams.set('clientId', clientId);
|
|
426
|
+
|
|
427
|
+
const res = await (doFetch ?? fetch)(url, {
|
|
428
|
+
headers: { authorization: `Bearer ${token}` },
|
|
429
|
+
});
|
|
430
|
+
if (!res.ok) return false;
|
|
431
|
+
const body = await res.json();
|
|
432
|
+
return body?.authorized === true;
|
|
433
|
+
} catch {
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {Object} ctx
|
|
3
|
+
* @param {import('@pdsjs/core/ports').SpaceStoragePort} ctx.spaceStorage
|
|
4
|
+
* @param {() => Promise<string|null>} ctx.getDid
|
|
5
|
+
* @param {() => Promise<{sign: (bytes: Uint8Array) => Promise<Uint8Array>}>} ctx.getSigner
|
|
6
|
+
* @param {(did: string) => Promise<any>} ctx.resolveDid
|
|
7
|
+
* @param {import('@pdsjs/core/ports').SignatureVerifierPort} ctx.verifier
|
|
8
|
+
* @param {typeof fetch} [ctx.fetch]
|
|
9
|
+
* @returns {import('@pdsjs/core/pds').Routes}
|
|
10
|
+
*/
|
|
11
|
+
export declare function createManageRoutes(ctx: {
|
|
12
|
+
spaceStorage: import('@pdsjs/core/ports').SpaceStoragePort;
|
|
13
|
+
getDid: () => Promise<string | null>;
|
|
14
|
+
getSigner: () => Promise<{
|
|
15
|
+
sign: (bytes: Uint8Array) => Promise<Uint8Array>;
|
|
16
|
+
}>;
|
|
17
|
+
resolveDid: (did: string) => Promise<any>;
|
|
18
|
+
verifier: import('@pdsjs/core/ports').SignatureVerifierPort;
|
|
19
|
+
fetch?: typeof fetch;
|
|
20
|
+
}): import('@pdsjs/core/pds').Routes;
|