@metalabel/dfos-client 0.52.0 → 0.53.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/dist/api-auth.d.ts +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.js +77 -20
- package/dist/{memory-B5WMnEUQ.d.ts → memory-aYqSas8m.d.ts} +1 -1
- package/dist/siwd.d.ts +1 -1
- package/dist/store/index.d.ts +2 -2
- package/dist/{types-BbIEz7BN.d.ts → types-DClhQw33.d.ts} +6 -0
- package/package.json +5 -5
package/dist/api-auth.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { ApiRequestVerifyError, DEFAULT_PROOF_SKEW_SECONDS, DEFAULT_PROOF_WINDOW_SECONDS, DFOS_AUTH_SCHEME, EMPTY_BODY_SHA256, IDENTITY_PROOF_JWS_TYP, IdentityProofPayload, MAX_BODY_BYTES, MAX_PROOF_FRESHNESS_SPAN_SECONDS, MAX_REQUEST_PROOF_SIZE, ProofExtraMembers, REQUEST_PROOF_JWS_TYP, RequestProofFailurePhase, RequestProofFailureReason, RequestProofPayload, SignApiIdentityRequestInput, SignApiRequestInput, apiIdentitySigningInput, apiRequestSigningInput, assertProofVerifierConfig, buildApiAuthHeaders, buildApiIdentityHeaders, parseDfosAuthorization, sha256BodyHash, signApiIdentityRequest, signApiRequest } from '@metalabel/dfos-protocol/credentials';
|
|
2
|
-
import { a as Client } from './types-
|
|
2
|
+
import { a as Client } from './types-DClhQw33.js';
|
|
3
3
|
import '@metalabel/dfos-protocol/chain';
|
|
4
4
|
import '@metalabel/dfos-web-relay/peer-client';
|
|
5
5
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { C as ClientConfig, a as Client, b as Callbacks, R as RevChecker } from './types-
|
|
2
|
-
export { c as CallOptions, D as DocumentBlob, E as EffectiveIdentity, d as EverProvedIdentity, G as GlobalLogOptions, e as GlobalLogPage, f as GlobalLogResult, I as IndexCapabilities, g as IndexContentPage, h as IndexContentRow, i as IndexCountersignatureRow, j as IndexCountersignaturesPage, k as IndexCredentialRow, l as IndexCredentialsPage, m as IndexIdentitiesPage, n as IndexIdentityProfile, o as IndexIdentityRow, p as IndexOrder, q as IndexRecencyOrder, L as LogOp, P as Provenance, r as RelayHealth, s as RelayResponse, t as Resolution, u as Resolved, v as ResolvedContent, w as ResolvedCredential, S as Store, T as Trust, U as UnverifiableAxis, V as VerifyResult } from './types-
|
|
3
|
-
export { m as memoryStore } from './memory-
|
|
1
|
+
import { C as ClientConfig, a as Client, b as Callbacks, R as RevChecker } from './types-DClhQw33.js';
|
|
2
|
+
export { c as CallOptions, D as DocumentBlob, E as EffectiveIdentity, d as EverProvedIdentity, G as GlobalLogOptions, e as GlobalLogPage, f as GlobalLogResult, I as IndexCapabilities, g as IndexContentPage, h as IndexContentRow, i as IndexCountersignatureRow, j as IndexCountersignaturesPage, k as IndexCredentialRow, l as IndexCredentialsPage, m as IndexIdentitiesPage, n as IndexIdentityProfile, o as IndexIdentityRow, p as IndexOrder, q as IndexRecencyOrder, L as LogOp, P as Provenance, r as RelayHealth, s as RelayResponse, t as Resolution, u as Resolved, v as ResolvedContent, w as ResolvedCredential, S as Store, T as Trust, U as UnverifiableAxis, V as VerifyResult } from './types-DClhQw33.js';
|
|
3
|
+
export { m as memoryStore } from './memory-aYqSas8m.js';
|
|
4
4
|
import '@metalabel/dfos-protocol/chain';
|
|
5
5
|
import '@metalabel/dfos-protocol/credentials';
|
|
6
6
|
import '@metalabel/dfos-web-relay/peer-client';
|
|
@@ -65,7 +65,9 @@ declare const divergenceErrorFrom: (err: unknown) => DivergenceError | undefined
|
|
|
65
65
|
* payload binds exactly the queried (issuerDID, credentialCID). Anything less —
|
|
66
66
|
* unreachable relay, negative answer, forged or mismatched proof — moves on to
|
|
67
67
|
* the next relay; false only after the full set has been consulted and at least
|
|
68
|
-
* one relay answered with a parseable status body. Zero answers throws
|
|
68
|
+
* one relay answered with a parseable status body. Zero answers throws, and so
|
|
69
|
+
* does a positive answer whose signing key this client could not resolve: a
|
|
70
|
+
* dependency failure is never a negative status.
|
|
69
71
|
*
|
|
70
72
|
* When the caller supplies `asOfUnix` (the protocol does, on every cold fold, with
|
|
71
73
|
* each operation's own `createdAt`), a verified revocation only counts if its own
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import { dagCborCanonicalEncode, decodeJwsUnsafe as decodeJwsUnsafe2 } from "@me
|
|
|
12
12
|
import { createHttpPeerClient } from "@metalabel/dfos-web-relay/peer-client";
|
|
13
13
|
|
|
14
14
|
// src/transport.ts
|
|
15
|
+
import { isDependencyMissing, markDependencyMissing } from "@metalabel/dfos-protocol";
|
|
15
16
|
var PAGE_LIMIT = 1e3;
|
|
16
17
|
var MAX_PAGES = 1e4;
|
|
17
18
|
var normalizeRelays = (relays) => {
|
|
@@ -62,6 +63,14 @@ var fanOutLog = async (fetchPage, relays, quorum, verifyCandidate) => {
|
|
|
62
63
|
const byDigest = /* @__PURE__ */ new Map();
|
|
63
64
|
const badDigests = /* @__PURE__ */ new Set();
|
|
64
65
|
let lastVerifyError;
|
|
66
|
+
let lastDependencyError;
|
|
67
|
+
const recordFailure = (err, digest) => {
|
|
68
|
+
if (!(err instanceof StaleAnswerError)) {
|
|
69
|
+
if (isDependencyMissing(err)) lastDependencyError = err;
|
|
70
|
+
else lastVerifyError = err;
|
|
71
|
+
}
|
|
72
|
+
badDigests.add(digest);
|
|
73
|
+
};
|
|
65
74
|
for (const url of relays) {
|
|
66
75
|
let entries = null;
|
|
67
76
|
try {
|
|
@@ -89,8 +98,7 @@ var fanOutLog = async (fetchPage, relays, quorum, verifyCandidate) => {
|
|
|
89
98
|
provenance: { answeredBy: group.url, responses, agreed: true, fromCache: false }
|
|
90
99
|
};
|
|
91
100
|
} catch (err) {
|
|
92
|
-
|
|
93
|
-
badDigests.add(digest);
|
|
101
|
+
recordFailure(err, digest);
|
|
94
102
|
}
|
|
95
103
|
}
|
|
96
104
|
}
|
|
@@ -105,8 +113,7 @@ var fanOutLog = async (fetchPage, relays, quorum, verifyCandidate) => {
|
|
|
105
113
|
provenance: { answeredBy: group.url, responses, agreed: false, fromCache: false }
|
|
106
114
|
};
|
|
107
115
|
} catch (err) {
|
|
108
|
-
|
|
109
|
-
badDigests.add(digest);
|
|
116
|
+
recordFailure(err, digest);
|
|
110
117
|
}
|
|
111
118
|
}
|
|
112
119
|
if (lastVerifyError !== void 0) {
|
|
@@ -115,6 +122,14 @@ var fanOutLog = async (fetchPage, relays, quorum, verifyCandidate) => {
|
|
|
115
122
|
cause: lastVerifyError
|
|
116
123
|
});
|
|
117
124
|
}
|
|
125
|
+
if (lastDependencyError !== void 0) {
|
|
126
|
+
const message = lastDependencyError instanceof Error ? lastDependencyError.message : "unknown error";
|
|
127
|
+
throw markDependencyMissing(
|
|
128
|
+
new Error(`candidate log verification could not complete: ${message}`, {
|
|
129
|
+
cause: lastDependencyError
|
|
130
|
+
})
|
|
131
|
+
);
|
|
132
|
+
}
|
|
118
133
|
return {
|
|
119
134
|
outcome: "unreachable",
|
|
120
135
|
entries: [],
|
|
@@ -234,11 +249,13 @@ var createIndexQueries = (relays, fetchImpl) => {
|
|
|
234
249
|
// src/resolvers.ts
|
|
235
250
|
import {
|
|
236
251
|
decodeMultikey,
|
|
252
|
+
IdentityStateNoSeenKeysError,
|
|
237
253
|
verifyContentChain,
|
|
238
254
|
verifyContentExtensionFromTrustedState,
|
|
239
255
|
verifyIdentityChain,
|
|
240
256
|
verifyIdentityExtensionFromTrustedState
|
|
241
257
|
} from "@metalabel/dfos-protocol/chain";
|
|
258
|
+
import { CredentialVerificationError } from "@metalabel/dfos-protocol/credentials";
|
|
242
259
|
import { decodeJwsUnsafe } from "@metalabel/dfos-protocol/crypto";
|
|
243
260
|
var DID_PREFIX = "did:dfos";
|
|
244
261
|
var cacheKey = (kind, id) => `${kind}:${id}`;
|
|
@@ -292,6 +309,8 @@ var keyBytesFor = (state, keyId) => {
|
|
|
292
309
|
);
|
|
293
310
|
return key ? decodeMultikey(key.publicKeyMultibase).keyBytes : null;
|
|
294
311
|
};
|
|
312
|
+
var NoStateAsOfError = class extends CredentialVerificationError {
|
|
313
|
+
};
|
|
295
314
|
var createResolvers = (deps) => {
|
|
296
315
|
const relaysFor = (o) => normalizeRelays(o?.relays ?? deps.relays);
|
|
297
316
|
const getIdentityChain = async (did, options) => {
|
|
@@ -323,17 +342,28 @@ var createResolvers = (deps) => {
|
|
|
323
342
|
let headCID = cached.headCID;
|
|
324
343
|
let lastCreatedAt = cached.lastCreatedAt;
|
|
325
344
|
const log = [...cached.log];
|
|
326
|
-
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
345
|
+
try {
|
|
346
|
+
for (const entry of entries.slice(cached.log.length)) {
|
|
347
|
+
const r = await verifyIdentityExtensionFromTrustedState({
|
|
348
|
+
currentState: state,
|
|
349
|
+
headCID,
|
|
350
|
+
lastCreatedAt,
|
|
351
|
+
newOp: entry.jwsToken
|
|
352
|
+
});
|
|
353
|
+
state = r.state;
|
|
354
|
+
headCID = r.operationCID;
|
|
355
|
+
lastCreatedAt = r.createdAt;
|
|
356
|
+
log.push(entry.jwsToken);
|
|
357
|
+
}
|
|
358
|
+
} catch (e) {
|
|
359
|
+
if (!(e instanceof IdentityStateNoSeenKeysError)) throw e;
|
|
360
|
+
const full = entries.map((entry) => entry.jwsToken);
|
|
361
|
+
const replayed = await verifyIdentityChain({ didPrefix: DID_PREFIX, log: full });
|
|
362
|
+
if (replayed.did !== did) {
|
|
363
|
+
throw new Error(`relay served a mismatched identity: asked ${did}, got ${replayed.did}`);
|
|
364
|
+
}
|
|
365
|
+
const last = opMeta(full[full.length - 1]);
|
|
366
|
+
return { state: replayed, log: full, headCID: last.cid, lastCreatedAt: last.createdAt };
|
|
337
367
|
}
|
|
338
368
|
return { state, log, headCID, lastCreatedAt };
|
|
339
369
|
};
|
|
@@ -375,13 +405,18 @@ var createResolvers = (deps) => {
|
|
|
375
405
|
const { state, log } = resolution;
|
|
376
406
|
const last = log[log.length - 1];
|
|
377
407
|
if (basis === void 0 || last === void 0 || opMeta(last).createdAt <= basis) return state;
|
|
408
|
+
const first = log[0];
|
|
409
|
+
if (first !== void 0 && basis < opMeta(first).createdAt) {
|
|
410
|
+
throw new NoStateAsOfError(`identity has no state as of ${basis}`);
|
|
411
|
+
}
|
|
378
412
|
const asOf = await verifyIdentityChain({ didPrefix: DID_PREFIX, log, asOf: basis });
|
|
379
|
-
return { ...asOf, isDeleted: state.isDeleted };
|
|
413
|
+
return { ...asOf, isDeleted: state.isDeleted, basisDeterminate: true };
|
|
380
414
|
};
|
|
381
415
|
const resolveIdentity = async (did, basis) => {
|
|
382
416
|
try {
|
|
383
417
|
return await stateAsOf(await getIdentityChain(did), basis);
|
|
384
|
-
} catch {
|
|
418
|
+
} catch (err) {
|
|
419
|
+
if (err instanceof NoStateAsOfError) throw err;
|
|
385
420
|
return void 0;
|
|
386
421
|
}
|
|
387
422
|
};
|
|
@@ -506,12 +541,29 @@ var createResolvers = (deps) => {
|
|
|
506
541
|
};
|
|
507
542
|
|
|
508
543
|
// src/revocation.ts
|
|
544
|
+
import { isDependencyMissing as isDependencyMissing2 } from "@metalabel/dfos-protocol";
|
|
509
545
|
import { parseProtocolTimestampUnix, verifyRevocation } from "@metalabel/dfos-protocol/chain";
|
|
546
|
+
import { CredentialVerificationError as CredentialVerificationError2 } from "@metalabel/dfos-protocol/credentials";
|
|
510
547
|
import { REVOCATIONS_BASE_PATH } from "@metalabel/dfos-web-relay/peer-client";
|
|
548
|
+
var unavailable = (why) => new Error(`revocation status unavailable: ${why}`);
|
|
549
|
+
var KeyUnresolvableError = class extends Error {
|
|
550
|
+
};
|
|
551
|
+
var isResolverVerdict = (err) => err instanceof CredentialVerificationError2 && !isDependencyMissing2(err);
|
|
511
552
|
var createRevocationChecker = (relays, fetchImpl, resolveKey) => {
|
|
512
553
|
const relaySet = normalizeRelays(relays);
|
|
554
|
+
const guardedResolveKey = async (kid, basis) => {
|
|
555
|
+
try {
|
|
556
|
+
return await resolveKey(kid, basis);
|
|
557
|
+
} catch (err) {
|
|
558
|
+
if (isResolverVerdict(err)) throw err;
|
|
559
|
+
throw new KeyUnresolvableError(`could not resolve the revocation signing key ${kid}`, {
|
|
560
|
+
cause: err
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
};
|
|
513
564
|
return async (issuerDID, credentialCID, asOfUnix) => {
|
|
514
565
|
let answered = false;
|
|
566
|
+
let unresolvable;
|
|
515
567
|
for (const url of relaySet) {
|
|
516
568
|
let body = null;
|
|
517
569
|
try {
|
|
@@ -532,17 +584,22 @@ var createRevocationChecker = (relays, fetchImpl, resolveKey) => {
|
|
|
532
584
|
}
|
|
533
585
|
if (!body?.revoked || !body.revocation) continue;
|
|
534
586
|
try {
|
|
535
|
-
const verified = await verifyRevocation({
|
|
587
|
+
const verified = await verifyRevocation({
|
|
588
|
+
jwsToken: body.revocation,
|
|
589
|
+
resolveKey: guardedResolveKey
|
|
590
|
+
});
|
|
536
591
|
if (verified.did === issuerDID && verified.credentialCID === credentialCID) {
|
|
537
592
|
if (asOfUnix === void 0 || asOfUnix <= 0) return true;
|
|
538
593
|
const revokedAtUnix = parseProtocolTimestampUnix(verified.createdAt);
|
|
539
594
|
if (revokedAtUnix === null || revokedAtUnix <= asOfUnix) return true;
|
|
540
595
|
continue;
|
|
541
596
|
}
|
|
542
|
-
} catch {
|
|
597
|
+
} catch (err) {
|
|
598
|
+
if (err instanceof KeyUnresolvableError) unresolvable = err;
|
|
543
599
|
}
|
|
544
600
|
}
|
|
545
|
-
if (!answered) throw
|
|
601
|
+
if (!answered) throw unavailable("no relay answered");
|
|
602
|
+
if (unresolvable) throw unavailable(unresolvable.message);
|
|
546
603
|
return false;
|
|
547
604
|
};
|
|
548
605
|
};
|
package/dist/siwd.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Signer, VerifiedIdentity, VerifiedSignRequest } from '@metalabel/dfos-protocol/chain';
|
|
2
|
-
import { a as Client, V as VerifyResult } from './types-
|
|
2
|
+
import { a as Client, V as VerifyResult } from './types-DClhQw33.js';
|
|
3
3
|
import '@metalabel/dfos-protocol/credentials';
|
|
4
4
|
import '@metalabel/dfos-web-relay/peer-client';
|
|
5
5
|
|
package/dist/store/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { m as memoryStore } from '../memory-
|
|
2
|
-
import { S as Store } from '../types-
|
|
1
|
+
export { m as memoryStore } from '../memory-aYqSas8m.js';
|
|
2
|
+
import { S as Store } from '../types-DClhQw33.js';
|
|
3
3
|
import '@metalabel/dfos-protocol/chain';
|
|
4
4
|
import '@metalabel/dfos-protocol/credentials';
|
|
5
5
|
import '@metalabel/dfos-web-relay/peer-client';
|
|
@@ -5,6 +5,12 @@ import { PeerClient } from '@metalabel/dfos-web-relay/peer-client';
|
|
|
5
5
|
/** Head/as-of state; excludes the credit-claim projection. */
|
|
6
6
|
type EffectiveIdentity = VerifiedIdentity & {
|
|
7
7
|
readonly resolution?: 'effective';
|
|
8
|
+
/**
|
|
9
|
+
* True when no operation dated at or before the basis can still arrive and
|
|
10
|
+
* change this key state — the protocol's `ResolvedIdentity.basisDeterminate`,
|
|
11
|
+
* which decides whether a missing key is a verdict or a retryable miss.
|
|
12
|
+
*/
|
|
13
|
+
basisDeterminate?: boolean;
|
|
8
14
|
};
|
|
9
15
|
/** Every key ever proved, including memberships absent from effective state. */
|
|
10
16
|
type EverProvedIdentity = VerifiedIdentity & {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metalabel/dfos-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.53.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "DFOS Client — the client-side kit for participating in the protocol: resolve, verify, prove. Fetch, resolve, verify-orchestration and cache over untrusted relays, plus the SIWD and API-AUTH proof surfaces. Holds no keys; all crypto truth comes from @metalabel/dfos-protocol",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,16 +47,16 @@
|
|
|
47
47
|
"README.md"
|
|
48
48
|
],
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@metalabel/dfos-protocol": "^0.
|
|
51
|
-
"@metalabel/dfos-web-relay": "^0.
|
|
50
|
+
"@metalabel/dfos-protocol": "^0.53.0",
|
|
51
|
+
"@metalabel/dfos-web-relay": "^0.53.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/node": "^24.10.4",
|
|
55
55
|
"tsup": "^8.5.1",
|
|
56
56
|
"typescript": "^5.9.3",
|
|
57
57
|
"vitest": "^4.1.8",
|
|
58
|
-
"@metalabel/dfos-protocol": "0.
|
|
59
|
-
"@metalabel/dfos-web-relay": "0.
|
|
58
|
+
"@metalabel/dfos-protocol": "0.53.0",
|
|
59
|
+
"@metalabel/dfos-web-relay": "0.53.0"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"build": "tsup",
|