@metalabel/dfos-client 0.50.0 → 0.52.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/README.md +5 -5
- package/dist/api-auth.d.ts +10 -22
- package/dist/api-auth.js +6 -2
- package/dist/index.d.ts +6 -5
- package/dist/index.js +48 -15
- package/dist/{memory-BuTsEPZI.d.ts → memory-B5WMnEUQ.d.ts} +1 -1
- package/dist/siwd.d.ts +32 -30
- package/dist/siwd.js +1 -1
- package/dist/store/index.d.ts +2 -2
- package/dist/{types-BfzEg_gw.d.ts → types-BbIEz7BN.d.ts} +29 -9
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -128,7 +128,7 @@ Three consequences worth knowing before you wire it up:
|
|
|
128
128
|
- **It does not follow redirects** (`redirect: 'manual'`): a 3xx comes back to you as-is, because following it would re-issue the request at coordinates the proof does not cover and carry `X-Credential` to whatever authority the `Location` names.
|
|
129
129
|
- **It buffers the request body before sending.** The proof covers the whole body, so there is nothing to sign until the last octet is in hand — size-bounded requests only. An unbounded or live stream cannot be proof-signed, in any implementation.
|
|
130
130
|
|
|
131
|
-
**A backend that must not proxy uses the decomposed form.** A signing backend fronting a browser describes the one request it is willing to make — never signing coordinates the browser supplies ([
|
|
131
|
+
**A backend that must not proxy uses the decomposed form.** A signing backend fronting a browser describes the one request it is willing to make — never signing coordinates the browser supplies ([INTEGRATIONS § API security notes](https://protocol.dfos.com/integrations#api-security-notes)) — so there is no `Request` for the adapter above to cover:
|
|
132
132
|
|
|
133
133
|
```typescript
|
|
134
134
|
import { buildApiAuthHeaders, signApiRequest } from '@metalabel/dfos-client/api-auth';
|
|
@@ -160,7 +160,7 @@ await verifyApiRequest(client, {
|
|
|
160
160
|
|
|
161
161
|
It throws `ApiRequestVerifyError`, carrying `reason` (`invalid` / `unverifiable` / `config`), `phase`, and the recommended `status` — branch on those, never on message text.
|
|
162
162
|
|
|
163
|
-
`verifyApiIdentityRequest` is the same verifier for the envelope's credential-less sibling, the [identity proof](https://protocol.dfos.com/
|
|
163
|
+
`verifyApiIdentityRequest` is the same verifier for the envelope's credential-less sibling, the [identity proof](https://protocol.dfos.com/integrations#the-identity-proof) — it establishes only which DID is asking, leaving what that DID may do to the resource's own policy.
|
|
164
164
|
|
|
165
165
|
`apiRequestSigningInput(payload)` is the pure byte contract both halves share, and the one place per language the canonical bytes are built.
|
|
166
166
|
|
|
@@ -179,7 +179,7 @@ import {
|
|
|
179
179
|
|
|
180
180
|
Sign In With DFOS. The three verbs above are the relying-party login kit, in the order a login uses them: `createSiwdLoginRequest` mints the challenge and builds the `/authorize` URL to redirect to, `readSiwdCallback` parses what comes back, and `verifySiwd` verifies it — mint → redirect, read → verify. The `expect` object `createSiwdLoginRequest` returns (nonce, domain, and the DID when the challenge is bound to one) is what `verifySiwd` checks against, so the relying party MUST persist it across the redirect: a verifier that takes its expectation from the callback has implemented the check and none of the protection. See [`examples/siwd-demo`](../../examples/siwd-demo) for the reference consumer.
|
|
181
181
|
|
|
182
|
-
The `nonce`/`consumeNonce` pair on the expectation (supply exactly one) maps one field each to the spec's two replay disciplines — which discipline a given scope obliges, and why, is [
|
|
182
|
+
The `nonce`/`consumeNonce` pair on the expectation (supply exactly one) maps one field each to the spec's two replay disciplines — which discipline a given scope obliges, and why, is [INTEGRATIONS § Replay prevention](https://protocol.dfos.com/integrations#replay-prevention)'s argument to make:
|
|
183
183
|
|
|
184
184
|
**`expect.nonce` — flow-bound login.** For a backend granting only a browser session (`scope=identity`), source the expected nonce from state you bound to that browser at mint time — a server-side session, or the nonce sealed under your own key in an `httpOnly` cookie — and compare:
|
|
185
185
|
|
|
@@ -202,7 +202,7 @@ Under either discipline `verifySiwd` checks the nonce at most once, and only aft
|
|
|
202
202
|
|
|
203
203
|
`createSiwdLoginRequest` throws rather than returning an error on the two things that are RP misconfiguration: an `authorizeUrl` or `redirectUri` that is not an absolute URL, and any scope other than `identity` over a loopback redirect that names no client identity.
|
|
204
204
|
|
|
205
|
-
**Loopback redirects** — `http://localhost`, `http://127.0.0.1`, or `http://[::1]`, on any port — come in two shapes: the **anonymous** one (no `client_did`, `scope=identity` only) and the **key-proven** one, the [loopback credential tier](https://protocol.dfos.com/
|
|
205
|
+
**Loopback redirects** — `http://localhost`, `http://127.0.0.1`, or `http://[::1]`, on any port — come in two shapes: the **anonymous** one (no `client_did`, `scope=identity` only) and the **key-proven** one, the [loopback credential tier](https://protocol.dfos.com/integrations#loopback-clients), which is what lets local software receive a credential — the spec defines both. The one integration consequence to know: a credential comes back in the URL **fragment**, which a browser sends to no server — your loopback listener's request line included — so a CLI answers the callback with a small page whose script reads `location.href` and posts the whole URL back, then feeds _that_ to `readSiwdCallback`. A browser relying party just passes `location.href`.
|
|
206
206
|
|
|
207
207
|
```typescript
|
|
208
208
|
import {
|
|
@@ -247,7 +247,7 @@ if (result.kind === 'success') {
|
|
|
247
247
|
}
|
|
248
248
|
```
|
|
249
249
|
|
|
250
|
-
`siwdSigningInput(challenge)` is the pure byte contract both the signer and the verifier share (see [
|
|
250
|
+
`siwdSigningInput(challenge)` is the pure byte contract both the signer and the verifier share (see [INTEGRATIONS.md](../../specs/INTEGRATIONS.md)); `createSiwdChallenge` mints a challenge on its own for a caller building its own redirect; `verifySiwd` is a no-throw verifier that accepts only a current `authKeys` entry of a non-deleted identity.
|
|
251
251
|
|
|
252
252
|
## License
|
|
253
253
|
|
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-BbIEz7BN.js';
|
|
3
3
|
import '@metalabel/dfos-protocol/chain';
|
|
4
4
|
import '@metalabel/dfos-web-relay/peer-client';
|
|
5
5
|
|
|
@@ -36,7 +36,7 @@ interface CreateApiAuthFetchOptions {
|
|
|
36
36
|
*
|
|
37
37
|
* `signApiRequest` stays exported for the backends that must NOT proxy. A
|
|
38
38
|
* signing backend fronting a browser MUST authorize the coordinates it is about
|
|
39
|
-
* to sign against its own session (
|
|
39
|
+
* to sign against its own session (INTEGRATIONS.md, API security notes) — it
|
|
40
40
|
* describes the one request it is willing to make rather than receiving one, so
|
|
41
41
|
* there is no `Request` for this adapter to cover.
|
|
42
42
|
*
|
|
@@ -118,7 +118,8 @@ interface VerifiedRequestProof {
|
|
|
118
118
|
credentialCID: string;
|
|
119
119
|
}
|
|
120
120
|
/**
|
|
121
|
-
* Verify a credential-gated request —
|
|
121
|
+
* Verify a credential-gated request — INTEGRATIONS.md, Verification algorithm's
|
|
122
|
+
* eleven steps, in an order
|
|
122
123
|
* that honors both load-bearing ordering rules: the proof signature gates every
|
|
123
124
|
* credential-chain step, and body hashing runs after the cheaper binding checks.
|
|
124
125
|
*
|
|
@@ -132,24 +133,10 @@ interface VerifiedRequestProof {
|
|
|
132
133
|
* message text. `status` is the recommended HTTP code (401 proof-invalid, 403
|
|
133
134
|
* credential-invalid, 503 unverifiable, 500 config).
|
|
134
135
|
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* ("no revocation found" and "could not reach any relay" both return false), the
|
|
140
|
-
* system-wide v1 stance that "non-revocation is never provable." Likewise a
|
|
141
|
-
* credential-issuer that is unresolvable because relays are down surfaces from the
|
|
142
|
-
* protocol verifier as a `CredentialVerificationError` and is reported here as
|
|
143
|
-
* `invalid` (403), not `unverifiable` (503) — the underlying callback cannot
|
|
144
|
-
* distinguish "genuinely absent" from "transiently unreachable." The PRESENTER
|
|
145
|
-
* side is availability-aware (a resolution failure or unverified/stale tip is
|
|
146
|
-
* `unverifiable`, failing closed unless `allowStale`); the CREDENTIAL side inherits
|
|
147
|
-
* the v1 primitives' limitation. A deployment that needs fail-closed-on-outage for
|
|
148
|
-
* the credential/revocation phase MUST inject an availability-aware `isRevoked`
|
|
149
|
-
* (one that THROWS when it reaches zero sources — the throw is surfaced here as
|
|
150
|
-
* `unverifiable`) via the client config. Tightening the default is a client-level
|
|
151
|
-
* change to the shared revocation/resolution contract (it governs SIWD and relay
|
|
152
|
-
* verification too), tracked outside this kit.
|
|
136
|
+
* Missing issuer dependencies and an unavailable revocation source are
|
|
137
|
+
* unverifiable (503). The default checker throws when no relay answers; an
|
|
138
|
+
* answered negative still cannot prove non-revocation. Custom checkers must
|
|
139
|
+
* likewise throw when status cannot be obtained.
|
|
153
140
|
*/
|
|
154
141
|
declare const verifyApiRequest: (client: Client, input: VerifyApiRequestInput) => Promise<VerifiedRequestProof>;
|
|
155
142
|
interface VerifyApiIdentityRequestInput {
|
|
@@ -212,7 +199,8 @@ interface VerifiedIdentityProof {
|
|
|
212
199
|
rawPayload: Record<string, unknown>;
|
|
213
200
|
}
|
|
214
201
|
/**
|
|
215
|
-
* Verify an identity-proven request —
|
|
202
|
+
* Verify an identity-proven request — INTEGRATIONS.md, Verification algorithm's
|
|
203
|
+
* PROOF PHASE (steps 1–7) with
|
|
216
204
|
* the identity `typ`, and nothing more. Steps 8–11 do not exist for this
|
|
217
205
|
* artifact: there is no credential to walk, so there is no chain, no revocation
|
|
218
206
|
* lookup, and no attenuation coverage.
|
package/dist/api-auth.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/api-auth.ts
|
|
2
|
+
import { isDependencyMissing } from "@metalabel/dfos-protocol";
|
|
2
3
|
import {
|
|
3
4
|
apiIdentitySigningInput,
|
|
4
5
|
apiRequestSigningInput,
|
|
@@ -132,19 +133,22 @@ var verifyApiRequest = async (client, input) => {
|
|
|
132
133
|
let leaf;
|
|
133
134
|
let chain;
|
|
134
135
|
try {
|
|
135
|
-
leaf = await verifyDFOSCredential(input.credential, { resolveIdentity, now });
|
|
136
|
+
leaf = await verifyDFOSCredential(input.credential, { resolveIdentity, nowUnix: now });
|
|
136
137
|
if (await isRevoked(leaf.iss, leaf.credentialCID)) {
|
|
137
138
|
throw new CredentialVerificationError("credential is revoked");
|
|
138
139
|
}
|
|
139
140
|
const verifiedChain = await verifyDelegationChain(leaf, {
|
|
140
141
|
resolveIdentity,
|
|
141
142
|
rootDID,
|
|
142
|
-
now,
|
|
143
|
+
nowUnix: now,
|
|
143
144
|
isRevoked
|
|
144
145
|
});
|
|
145
146
|
chain = verifiedChain.chain;
|
|
146
147
|
} catch (err) {
|
|
147
148
|
if (err instanceof ApiRequestVerifyError) throw err;
|
|
149
|
+
if (isDependencyMissing(err)) {
|
|
150
|
+
throw unverifiableCredential(err instanceof Error ? err.message : String(err));
|
|
151
|
+
}
|
|
148
152
|
if (err instanceof CredentialVerificationError) throw invalidCredential(err.message);
|
|
149
153
|
throw unverifiableCredential(
|
|
150
154
|
`credential verification could not complete: ${err instanceof Error ? err.message : String(err)}`
|
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, G as GlobalLogOptions,
|
|
3
|
-
export { m as memoryStore } from './memory-
|
|
1
|
+
import { C as ClientConfig, a as Client, b as Callbacks, R as RevChecker } from './types-BbIEz7BN.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-BbIEz7BN.js';
|
|
3
|
+
export { m as memoryStore } from './memory-B5WMnEUQ.js';
|
|
4
4
|
import '@metalabel/dfos-protocol/chain';
|
|
5
5
|
import '@metalabel/dfos-protocol/credentials';
|
|
6
6
|
import '@metalabel/dfos-web-relay/peer-client';
|
|
@@ -64,7 +64,8 @@ declare const divergenceErrorFrom: (err: unknown) => DivergenceError | undefined
|
|
|
64
64
|
* (`verifyRevocation`: signature, CID integrity, issuer-only rule) and whose
|
|
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
|
-
* the next relay; false only after the full set has been consulted
|
|
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
69
|
*
|
|
69
70
|
* When the caller supplies `asOfUnix` (the protocol does, on every cold fold, with
|
|
70
71
|
* each operation's own `createdAt`), a verified revocation only counts if its own
|
|
@@ -72,6 +73,6 @@ declare const divergenceErrorFrom: (err: unknown) => DivergenceError | undefined
|
|
|
72
73
|
* verification of history: without it, revoking a credential today would make
|
|
73
74
|
* every already-committed operation it ever authorized fail to verify tomorrow.
|
|
74
75
|
*/
|
|
75
|
-
declare const createRevocationChecker: (relays: string[], fetchImpl: typeof fetch, resolveKey: (kid: string) => Promise<Uint8Array>) => RevChecker;
|
|
76
|
+
declare const createRevocationChecker: (relays: string[], fetchImpl: typeof fetch, resolveKey: (kid: string, basis?: string) => Promise<Uint8Array>) => RevChecker;
|
|
76
77
|
|
|
77
78
|
export { Callbacks, Client, ClientConfig, DivergenceError, RevChecker, createClient, createRevocationChecker, divergenceErrorFrom, resolvers };
|
package/dist/index.js
CHANGED
|
@@ -203,6 +203,7 @@ var createIndexQueries = (relays, fetchImpl) => {
|
|
|
203
203
|
setParam(url, "issuer", params?.issuer);
|
|
204
204
|
setParam(url, "resource", params?.resource);
|
|
205
205
|
setParam(url, "action", params?.action);
|
|
206
|
+
setParam(url, "order", params?.order);
|
|
206
207
|
setParam(url, "after", params?.after);
|
|
207
208
|
setParam(url, "limit", params?.limit);
|
|
208
209
|
return url;
|
|
@@ -276,12 +277,9 @@ var opMeta = (jws) => {
|
|
|
276
277
|
const createdAt = typeof decoded?.payload?.["createdAt"] === "string" ? (decoded?.payload)["createdAt"] : "";
|
|
277
278
|
return { cid, createdAt };
|
|
278
279
|
};
|
|
279
|
-
var provedKeys = (state) => {
|
|
280
|
-
const proved = state.provedKeys ?? state;
|
|
281
|
-
return [...proved.authKeys, ...proved.assertKeys, ...proved.controllerKeys];
|
|
282
|
-
};
|
|
283
280
|
var historicalIdentity = (state) => ({
|
|
284
281
|
...state,
|
|
282
|
+
resolution: "ever-proved",
|
|
285
283
|
...state.provedKeys ?? {
|
|
286
284
|
authKeys: state.authKeys,
|
|
287
285
|
assertKeys: state.assertKeys,
|
|
@@ -289,7 +287,9 @@ var historicalIdentity = (state) => ({
|
|
|
289
287
|
}
|
|
290
288
|
});
|
|
291
289
|
var keyBytesFor = (state, keyId) => {
|
|
292
|
-
const key =
|
|
290
|
+
const key = [...state.authKeys, ...state.assertKeys, ...state.controllerKeys].find(
|
|
291
|
+
(k) => k.id === keyId
|
|
292
|
+
);
|
|
293
293
|
return key ? decodeMultikey(key.publicKeyMultibase).keyBytes : null;
|
|
294
294
|
};
|
|
295
295
|
var createResolvers = (deps) => {
|
|
@@ -371,27 +371,42 @@ var createResolvers = (deps) => {
|
|
|
371
371
|
tipUnverified: cached !== void 0 && candidate.log.length === cached.log.length
|
|
372
372
|
};
|
|
373
373
|
};
|
|
374
|
-
const
|
|
374
|
+
const stateAsOf = async (resolution, basis) => {
|
|
375
|
+
const { state, log } = resolution;
|
|
376
|
+
const last = log[log.length - 1];
|
|
377
|
+
if (basis === void 0 || last === void 0 || opMeta(last).createdAt <= basis) return state;
|
|
378
|
+
const asOf = await verifyIdentityChain({ didPrefix: DID_PREFIX, log, asOf: basis });
|
|
379
|
+
return { ...asOf, isDeleted: state.isDeleted };
|
|
380
|
+
};
|
|
381
|
+
const resolveIdentity = async (did, basis) => {
|
|
375
382
|
try {
|
|
376
|
-
|
|
377
|
-
return historicalIdentity(state);
|
|
383
|
+
return await stateAsOf(await getIdentityChain(did), basis);
|
|
378
384
|
} catch {
|
|
379
385
|
return void 0;
|
|
380
386
|
}
|
|
381
387
|
};
|
|
382
|
-
const resolveKey = async (kid) => {
|
|
388
|
+
const resolveKey = async (kid, basis) => {
|
|
383
389
|
const hashIdx = kid.indexOf("#");
|
|
384
390
|
if (hashIdx < 0) throw new Error(`kid must be a DID URL: ${kid}`);
|
|
385
391
|
const did = kid.substring(0, hashIdx);
|
|
386
392
|
const keyId = kid.substring(hashIdx + 1);
|
|
387
|
-
const
|
|
393
|
+
const state = await stateAsOf(await getIdentityChain(did), basis);
|
|
388
394
|
const bytes = keyBytesFor(state, keyId);
|
|
389
395
|
if (!bytes) throw new Error(`unknown key ${keyId} on identity ${did}`);
|
|
390
396
|
return bytes;
|
|
391
397
|
};
|
|
398
|
+
const resolveClaimantIdentity = async (did) => {
|
|
399
|
+
try {
|
|
400
|
+
const { state } = await getIdentityChain(did);
|
|
401
|
+
return historicalIdentity(state);
|
|
402
|
+
} catch {
|
|
403
|
+
return void 0;
|
|
404
|
+
}
|
|
405
|
+
};
|
|
392
406
|
const callbacks = () => ({
|
|
393
407
|
resolveKey,
|
|
394
408
|
resolveIdentity,
|
|
409
|
+
resolveClaimantIdentity,
|
|
395
410
|
isRevoked: deps.isRevoked
|
|
396
411
|
});
|
|
397
412
|
const getContentChain = async (contentId, options) => {
|
|
@@ -496,6 +511,7 @@ import { REVOCATIONS_BASE_PATH } from "@metalabel/dfos-web-relay/peer-client";
|
|
|
496
511
|
var createRevocationChecker = (relays, fetchImpl, resolveKey) => {
|
|
497
512
|
const relaySet = normalizeRelays(relays);
|
|
498
513
|
return async (issuerDID, credentialCID, asOfUnix) => {
|
|
514
|
+
let answered = false;
|
|
499
515
|
for (const url of relaySet) {
|
|
500
516
|
let body = null;
|
|
501
517
|
try {
|
|
@@ -507,6 +523,10 @@ var createRevocationChecker = (relays, fetchImpl, resolveKey) => {
|
|
|
507
523
|
if (res.status === 501) continue;
|
|
508
524
|
if (!res.ok) continue;
|
|
509
525
|
body = await res.json();
|
|
526
|
+
if (body === null || typeof body !== "object" || typeof body.revoked !== "boolean") {
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
answered = true;
|
|
510
530
|
} catch {
|
|
511
531
|
continue;
|
|
512
532
|
}
|
|
@@ -522,6 +542,7 @@ var createRevocationChecker = (relays, fetchImpl, resolveKey) => {
|
|
|
522
542
|
} catch {
|
|
523
543
|
}
|
|
524
544
|
}
|
|
545
|
+
if (!answered) throw new Error("revocation status unavailable: no relay answered");
|
|
525
546
|
return false;
|
|
526
547
|
};
|
|
527
548
|
};
|
|
@@ -574,7 +595,11 @@ var createClient = (config) => {
|
|
|
574
595
|
let isRevokedImpl = async () => false;
|
|
575
596
|
const isRevoked = (issuerDID, credentialCID, asOfUnix) => isRevokedImpl(issuerDID, credentialCID, asOfUnix);
|
|
576
597
|
const resolvers2 = createResolvers({ relays, quorum, store, peerClient, isRevoked });
|
|
577
|
-
isRevokedImpl = config.isRevoked ?? createRevocationChecker(
|
|
598
|
+
isRevokedImpl = config.isRevoked ?? createRevocationChecker(
|
|
599
|
+
relays,
|
|
600
|
+
fetchImpl,
|
|
601
|
+
(kid, basis) => resolvers2.callbacks().resolveKey(kid, basis)
|
|
602
|
+
);
|
|
578
603
|
const relaysFor = (o) => normalizeRelays(o?.relays ?? relays);
|
|
579
604
|
const identity = async (did, options) => {
|
|
580
605
|
const { state, provenance, tipUnverified } = await resolvers2.getIdentityChain(did, options);
|
|
@@ -600,9 +625,13 @@ var createClient = (config) => {
|
|
|
600
625
|
const issuer = await resolvers2.getIdentityChain(iss, options);
|
|
601
626
|
const verified = await verifyDFOSCredential(jws, {
|
|
602
627
|
resolveIdentity: resolvers2.callbacks().resolveIdentity,
|
|
603
|
-
|
|
628
|
+
nowUnix: Math.floor(nowMs() / 1e3)
|
|
604
629
|
});
|
|
605
|
-
|
|
630
|
+
let revoked = false;
|
|
631
|
+
try {
|
|
632
|
+
revoked = await isRevoked(verified.iss, verified.credentialCID);
|
|
633
|
+
} catch {
|
|
634
|
+
}
|
|
606
635
|
const axes = [];
|
|
607
636
|
if (issuer.tipUnverified) axes.push("tip");
|
|
608
637
|
if (!revoked) axes.push("revocation");
|
|
@@ -646,9 +675,13 @@ var createClient = (config) => {
|
|
|
646
675
|
if (typ === "did:dfos:credential") {
|
|
647
676
|
const verified = await verifyDFOSCredential(jws, {
|
|
648
677
|
resolveIdentity: cb.resolveIdentity,
|
|
649
|
-
|
|
678
|
+
nowUnix: Math.floor(nowMs() / 1e3)
|
|
650
679
|
});
|
|
651
|
-
|
|
680
|
+
let revoked = false;
|
|
681
|
+
try {
|
|
682
|
+
revoked = await isRevoked(verified.iss, verified.credentialCID);
|
|
683
|
+
} catch {
|
|
684
|
+
}
|
|
652
685
|
if (revoked) return { ok: false, error: "credential revoked", value: verified };
|
|
653
686
|
return { ok: true, value: verified, unverifiable: ["revocation"] };
|
|
654
687
|
}
|
package/dist/siwd.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
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-BbIEz7BN.js';
|
|
3
3
|
import '@metalabel/dfos-protocol/credentials';
|
|
4
4
|
import '@metalabel/dfos-web-relay/peer-client';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* The normative JWS header `typ` for a signed SIWD challenge (
|
|
7
|
+
* The normative JWS header `typ` for a signed SIWD challenge (INTEGRATIONS.md,
|
|
8
|
+
* Challenge schema). Signers
|
|
8
9
|
* MUST set it; `verifySiwd` rejects anything else — it is also what lets typ-
|
|
9
10
|
* routing dispatchers tell a SIWD proof apart from credentials and chain ops.
|
|
10
11
|
*/
|
|
@@ -12,7 +13,7 @@ declare const SIWD_JWS_TYP = "did:dfos:siwd";
|
|
|
12
13
|
/**
|
|
13
14
|
* The normative JWS header `typ` for a client ASK PROOF — the artifact a
|
|
14
15
|
* loopback client signs to prove key control at ask-time, registered alongside
|
|
15
|
-
* `SIWD_JWS_TYP` by
|
|
16
|
+
* `SIWD_JWS_TYP` by INTEGRATIONS.md, The ask proof.
|
|
16
17
|
*
|
|
17
18
|
* The two artifacts cover the SAME canonical challenge bytes, so the distinct
|
|
18
19
|
* `typ` is the only thing keeping them from being fungible: without it, an ask
|
|
@@ -89,13 +90,13 @@ interface SiwdLoginRequestInput {
|
|
|
89
90
|
redirectUri: string;
|
|
90
91
|
/**
|
|
91
92
|
* Requested scope: a space-separated SET of scope tokens (the OAuth `scope`
|
|
92
|
-
* convention), each of which must be one specs/
|
|
93
|
+
* convention), each of which must be one specs/INTEGRATIONS.md §Scopes and credentials
|
|
93
94
|
* registers. A request naming an unregistered token is refused WHOLE rather
|
|
94
95
|
* than partially honored — a consent screen that silently dropped a token
|
|
95
96
|
* would describe something other than what was asked for.
|
|
96
97
|
*/
|
|
97
98
|
scope: string;
|
|
98
|
-
/** Consent-screen prose. A host MAY decline to render it; see specs/
|
|
99
|
+
/** Consent-screen prose. A host MAY decline to render it; see specs/INTEGRATIONS.md. */
|
|
99
100
|
statement?: string;
|
|
100
101
|
/**
|
|
101
102
|
* Bind the challenge to ONE identity — "sign in as this DID, or not at all".
|
|
@@ -160,7 +161,7 @@ interface SiwdLoginRequest {
|
|
|
160
161
|
* on a local port used to be refused outright — there is no domain serving a
|
|
161
162
|
* well-known and no registration to check, so nothing backed the DID and a host
|
|
162
163
|
* would not display an identity it could not stand behind. The LOOPBACK
|
|
163
|
-
* CREDENTIAL TIER (specs/
|
|
164
|
+
* CREDENTIAL TIER (specs/INTEGRATIONS.md §Loopback clients) replaces "nothing backs it"
|
|
164
165
|
* with the one thing local software can prove: control of that identity's
|
|
165
166
|
* current keys. So the param now rides through on a loopback redirect instead
|
|
166
167
|
* of being dropped — but it is honored only when the request ALSO carries an
|
|
@@ -171,7 +172,7 @@ interface SiwdLoginRequest {
|
|
|
171
172
|
*
|
|
172
173
|
* The same judgment BOUNDS THE SCOPE. Every scope past `identity` returns a
|
|
173
174
|
* credential issued to a `client_did`, so a loopback request with no client
|
|
174
|
-
* identity at all still has nothing to issue to and specs/
|
|
175
|
+
* identity at all still has nothing to issue to and specs/INTEGRATIONS.md admits it for
|
|
175
176
|
* `scope=identity` only — there is nothing to downgrade, so it throws. With a
|
|
176
177
|
* client identity the tier is open and every scope is available.
|
|
177
178
|
*
|
|
@@ -191,8 +192,8 @@ declare const createSiwdLoginRequest: (input: SiwdLoginRequestInput) => SiwdLogi
|
|
|
191
192
|
/**
|
|
192
193
|
* Sign the ask proof for a loopback authorize request: a JWS over the exact
|
|
193
194
|
* canonical bytes of the request's own challenge, under `SIWD_ASK_JWS_TYP`,
|
|
194
|
-
* signed by a CURRENT auth key of the client identity's chain (
|
|
195
|
-
* proof). It is what makes a `client_did` on a loopback request mean something
|
|
195
|
+
* signed by a CURRENT auth key of the client identity's chain (INTEGRATIONS.md,
|
|
196
|
+
* The ask proof). It is what makes a `client_did` on a loopback request mean something
|
|
196
197
|
* — the host verifies it against the chain's current state before rendering any
|
|
197
198
|
* consent, so key control is established at ask-time, not just at spend-time.
|
|
198
199
|
*
|
|
@@ -209,11 +210,9 @@ declare const createSiwdLoginRequest: (input: SiwdLoginRequestInput) => SiwdLogi
|
|
|
209
210
|
* because a proof that names a key it was not signed with is a lie the wire
|
|
210
211
|
* format has no reason to carry.
|
|
211
212
|
*
|
|
212
|
-
* The `client_proof` param that carries this is the
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
* What is normative is that it arrives WITH the ask and verifies BEFORE any
|
|
216
|
-
* consent is rendered.
|
|
213
|
+
* The `client_proof` param that carries this is the parameter the spec names
|
|
214
|
+
* (INTEGRATIONS.md, The ask proof): the proof arrives WITH the ask and verifies
|
|
215
|
+
* BEFORE any consent is rendered.
|
|
217
216
|
*/
|
|
218
217
|
declare const signSiwdAskProof: (input: {
|
|
219
218
|
challenge: SiwdChallenge;
|
|
@@ -221,7 +220,8 @@ declare const signSiwdAskProof: (input: {
|
|
|
221
220
|
kid: string;
|
|
222
221
|
signer: Signer;
|
|
223
222
|
}) => Promise<string>;
|
|
224
|
-
/**
|
|
223
|
+
/** The carriage cap of INTEGRATIONS.md, `identity_chain`: chain carriage — an
|
|
224
|
+
* identity that has outgrown it has outgrown carriage. */
|
|
225
225
|
declare const MAX_SIWD_CLIENT_CHAIN_OPS = 100;
|
|
226
226
|
/**
|
|
227
227
|
* Encode a client identity chain for carriage on the authorize request: the
|
|
@@ -231,7 +231,7 @@ declare const MAX_SIWD_CLIENT_CHAIN_OPS = 100;
|
|
|
231
231
|
* THIS IS THE LOOPBACK CARRIAGE FORM, and only that. An application that holds a
|
|
232
232
|
* domain encodes nothing: it publishes the very same log as the raw JSON array
|
|
233
233
|
* of the `identity_chain` member of its `/.well-known/dfos-app.json` app
|
|
234
|
-
* description (
|
|
234
|
+
* description (INTEGRATIONS.md, `identity_chain`: chain carriage), where the origin
|
|
235
235
|
* serving the file is what associates the domain with the DID. Same chain, same
|
|
236
236
|
* carriage rules — a URL is simply the carrier available to software that holds
|
|
237
237
|
* no origin to publish from.
|
|
@@ -239,7 +239,7 @@ declare const MAX_SIWD_CLIENT_CHAIN_OPS = 100;
|
|
|
239
239
|
* The DID derived from the genesis operation MUST equal the `client_did` the
|
|
240
240
|
* request names; a request where the two disagree makes no claim at all and the
|
|
241
241
|
* host refuses it WHOLE rather than ingesting the chain and ignoring the
|
|
242
|
-
* mismatch (
|
|
242
|
+
* mismatch (INTEGRATIONS.md, Chain residence). Carriage is only needed when the DID is
|
|
243
243
|
* not already resident on the verifying host.
|
|
244
244
|
*
|
|
245
245
|
* The 100-operation cap is spec-normative and enforced here. Hosts MAY
|
|
@@ -248,10 +248,10 @@ declare const MAX_SIWD_CLIENT_CHAIN_OPS = 100;
|
|
|
248
248
|
* protocol, so it is not a client-side throw; the practical reading is that a
|
|
249
249
|
* chain anywhere near the op cap belongs on relays, not in a URL.
|
|
250
250
|
*
|
|
251
|
-
* As with the ask proof, the `client_chain` param is the
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
251
|
+
* As with the ask proof, the `client_chain` param is the parameter the spec
|
|
252
|
+
* names (INTEGRATIONS.md, Chain residence): base64url of the JSON array of the
|
|
253
|
+
* verbatim operation JWS strings, genesis first, arriving with the ask and
|
|
254
|
+
* verifying before any consent is rendered.
|
|
255
255
|
*/
|
|
256
256
|
declare const encodeSiwdClientChain: (log: string[]) => string;
|
|
257
257
|
interface SiwdClientIdentity {
|
|
@@ -329,7 +329,7 @@ interface SiwdLoopbackLoginRequestInput {
|
|
|
329
329
|
* a credential to.
|
|
330
330
|
*/
|
|
331
331
|
scope: string;
|
|
332
|
-
/** Consent-screen prose. A host MAY decline to render it; see specs/
|
|
332
|
+
/** Consent-screen prose. A host MAY decline to render it; see specs/INTEGRATIONS.md. */
|
|
333
333
|
statement?: string;
|
|
334
334
|
/** Bind the challenge to ONE subject DID (sign in as this DID or not at all). */
|
|
335
335
|
did?: string;
|
|
@@ -345,14 +345,16 @@ interface SiwdLoopbackLoginRequestInput {
|
|
|
345
345
|
* Build a loopback authorize URL under the LOOPBACK CREDENTIAL TIER — the
|
|
346
346
|
* outbound half of what `createSiwdLoginRequest` alone cannot produce. It is
|
|
347
347
|
* that function plus the two things that back the `client_did` it now carries:
|
|
348
|
-
* an ask proof (
|
|
349
|
-
* on the host, the client's identity chain (
|
|
348
|
+
* an ask proof (INTEGRATIONS.md, The ask proof) and, unless the DID is already
|
|
349
|
+
* resident on the host, the client's identity chain (INTEGRATIONS.md, Chain
|
|
350
|
+
* residence).
|
|
350
351
|
*
|
|
351
|
-
* `domain` is DERIVED, not accepted.
|
|
352
|
-
* to the BARE loopback host — the port is
|
|
353
|
-
* local application cannot reserve one — and
|
|
354
|
-
* literally against the redirect's host. Taking a
|
|
355
|
-
* an invitation to a mismatch that fails only
|
|
352
|
+
* `domain` is DERIVED, not accepted. INTEGRATIONS.md, Loopback redirect targets
|
|
353
|
+
* pins a loopback challenge's domain to the BARE loopback host — the port is
|
|
354
|
+
* not part of the binding, because a local application cannot reserve one — and
|
|
355
|
+
* the host compares that value literally against the redirect's host. Taking a
|
|
356
|
+
* `domain` input here would be an invitation to a mismatch that fails only
|
|
357
|
+
* after the redirect.
|
|
356
358
|
*
|
|
357
359
|
* WHAT THIS PROVES IS KEY CONTROL, NOT PROVENANCE. The chain says which keys
|
|
358
360
|
* the asking party holds; nothing about a loopback client's origin or authorship
|
|
@@ -404,7 +406,7 @@ type SiwdCallbackResult = {
|
|
|
404
406
|
* THAT SAME PROPERTY IS A PROBLEM FOR A CLI, and this tier's primary consumer is
|
|
405
407
|
* a CLI. A browser does not send the fragment to the loopback listener either,
|
|
406
408
|
* so the request line a local HTTP server sees carries the query and nothing
|
|
407
|
-
* else. The standard resolution (
|
|
409
|
+
* else. The standard resolution (INTEGRATIONS.md, 4. Callback) is for the listener
|
|
408
410
|
* to answer with a small page whose script reads `location.href` and posts the
|
|
409
411
|
* whole URL back to the local server; feed THAT to this function. A browser RP
|
|
410
412
|
* passes `location.href` directly and needs no relay.
|
package/dist/siwd.js
CHANGED
|
@@ -135,7 +135,7 @@ var createSiwdLoginRequest = (input) => {
|
|
|
135
135
|
const isLoopback = SIWD_LOOPBACK_HOSTS.has(bareHostname(redirect));
|
|
136
136
|
if (isLoopback && input.scope !== "identity" && input.clientDid === void 0) {
|
|
137
137
|
throw new Error(
|
|
138
|
-
"invalid SIWD login request: loopback redirects support scope=identity only without a client identity \u2014 a credential scope needs a client_did proven under the loopback credential tier (specs/
|
|
138
|
+
"invalid SIWD login request: loopback redirects support scope=identity only without a client identity \u2014 a credential scope needs a client_did proven under the loopback credential tier (specs/INTEGRATIONS.md \xA7Loopback clients)"
|
|
139
139
|
);
|
|
140
140
|
}
|
|
141
141
|
const { challenge, encoded, nonce } = createSiwdChallenge({
|
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-B5WMnEUQ.js';
|
|
2
|
+
import { S as Store } from '../types-BbIEz7BN.js';
|
|
3
3
|
import '@metalabel/dfos-protocol/chain';
|
|
4
4
|
import '@metalabel/dfos-protocol/credentials';
|
|
5
5
|
import '@metalabel/dfos-web-relay/peer-client';
|
|
@@ -2,6 +2,14 @@ import { VerifiedIdentity, VerifiedContentChain } from '@metalabel/dfos-protocol
|
|
|
2
2
|
import { VerifiedDFOSCredential, Attenuation } from '@metalabel/dfos-protocol/credentials';
|
|
3
3
|
import { PeerClient } from '@metalabel/dfos-web-relay/peer-client';
|
|
4
4
|
|
|
5
|
+
/** Head/as-of state; excludes the credit-claim projection. */
|
|
6
|
+
type EffectiveIdentity = VerifiedIdentity & {
|
|
7
|
+
readonly resolution?: 'effective';
|
|
8
|
+
};
|
|
9
|
+
/** Every key ever proved, including memberships absent from effective state. */
|
|
10
|
+
type EverProvedIdentity = VerifiedIdentity & {
|
|
11
|
+
readonly resolution: 'ever-proved';
|
|
12
|
+
};
|
|
5
13
|
/**
|
|
6
14
|
* The two axes v1 genuinely cannot check:
|
|
7
15
|
* - `revocation` — non-revocation is never provable (a relay can only attest to
|
|
@@ -48,14 +56,14 @@ interface VerifyResult<T> {
|
|
|
48
56
|
interface ResolvedContent {
|
|
49
57
|
chain: VerifiedContentChain;
|
|
50
58
|
/** The creator identity — resolved as a side effect of key resolution. */
|
|
51
|
-
creator:
|
|
59
|
+
creator: EffectiveIdentity;
|
|
52
60
|
/** The current document blob, when fetched. */
|
|
53
61
|
document?: DocumentBlob;
|
|
54
62
|
}
|
|
55
63
|
interface ResolvedCredential {
|
|
56
64
|
credential: VerifiedDFOSCredential;
|
|
57
65
|
/** The issuer identity, verified. */
|
|
58
|
-
issuer:
|
|
66
|
+
issuer: EffectiveIdentity;
|
|
59
67
|
/** Revocation status per the effective revocation checker (see Trust.unverifiable). */
|
|
60
68
|
revoked: boolean;
|
|
61
69
|
}
|
|
@@ -71,13 +79,13 @@ interface DocumentBlob {
|
|
|
71
79
|
/** Discriminated result of the paste-a-string dispatcher. */
|
|
72
80
|
type Resolution = ({
|
|
73
81
|
kind: 'identity';
|
|
74
|
-
} & Resolved<
|
|
82
|
+
} & Resolved<EffectiveIdentity>) | ({
|
|
75
83
|
kind: 'content';
|
|
76
84
|
} & Resolved<ResolvedContent>) | ({
|
|
77
85
|
kind: 'credential';
|
|
78
86
|
} & Resolved<ResolvedCredential>);
|
|
79
87
|
/**
|
|
80
|
-
* Check whether a credential has been revoked.
|
|
88
|
+
* Check whether a credential has been revoked. The default queries relays and throws when none answers.
|
|
81
89
|
*
|
|
82
90
|
* `asOfUnix` is the protocol's revocation as-of basis (see the protocol's
|
|
83
91
|
* `RevocationChecker`): supplied when folding committed history, so a credential
|
|
@@ -92,8 +100,19 @@ type RevChecker = (issuerDID: string, credentialCID: string, asOfUnix?: number)
|
|
|
92
100
|
* `verifyDFOSCredential`, or any DFOS verifier. This is the trunk product.
|
|
93
101
|
*/
|
|
94
102
|
interface Callbacks {
|
|
95
|
-
|
|
96
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Resolve a kid to key bytes in the signing identity's state as of `basis` —
|
|
105
|
+
* a committed artifact's own `createdAt`, or nothing for an ephemeral
|
|
106
|
+
* presentation, whose basis is now.
|
|
107
|
+
*/
|
|
108
|
+
resolveKey: (kid: string, basis?: string) => Promise<Uint8Array>;
|
|
109
|
+
/** Resolve a DID to its verified identity state as of `basis`. */
|
|
110
|
+
resolveIdentity: (did: string, basis?: string) => Promise<EffectiveIdentity | undefined>;
|
|
111
|
+
/**
|
|
112
|
+
* Resolve a DID to its identity with every key it has ever proved — the
|
|
113
|
+
* credit-claim carve-out, which runs no temporal check and so has no basis.
|
|
114
|
+
*/
|
|
115
|
+
resolveClaimantIdentity: (did: string) => Promise<EverProvedIdentity | undefined>;
|
|
97
116
|
isRevoked: RevChecker;
|
|
98
117
|
}
|
|
99
118
|
/**
|
|
@@ -256,7 +275,7 @@ interface ClientConfig {
|
|
|
256
275
|
store?: Store;
|
|
257
276
|
/** Distinct-digest agreement threshold. Default 1 (first-wins). */
|
|
258
277
|
quorum?: number;
|
|
259
|
-
/** Revocation checker. Default
|
|
278
|
+
/** Revocation checker. Default queries relays; throws when none answers. */
|
|
260
279
|
isRevoked?: RevChecker;
|
|
261
280
|
/** Injected fetch for blob/health/revocation calls. Default `globalThis.fetch`. */
|
|
262
281
|
fetch?: typeof fetch;
|
|
@@ -276,7 +295,7 @@ interface Client {
|
|
|
276
295
|
callbacks(options?: CallOptions): Callbacks;
|
|
277
296
|
/** Paste-a-string dispatcher → a typed, trust-wrapped resolution. */
|
|
278
297
|
resolve(ref: string, options?: CallOptions): Promise<Resolution>;
|
|
279
|
-
identity(did: string, options?: CallOptions): Promise<Resolved<
|
|
298
|
+
identity(did: string, options?: CallOptions): Promise<Resolved<EffectiveIdentity>>;
|
|
280
299
|
content(contentId: string, options?: CallOptions): Promise<Resolved<ResolvedContent>>;
|
|
281
300
|
credential(jws: string, options?: CallOptions): Promise<Resolved<ResolvedCredential>>;
|
|
282
301
|
document(contentId: string, options?: CallOptions): Promise<Resolved<DocumentBlob>>;
|
|
@@ -334,9 +353,10 @@ interface Client {
|
|
|
334
353
|
issuer?: string;
|
|
335
354
|
resource?: string;
|
|
336
355
|
action?: string;
|
|
356
|
+
order?: IndexRecencyOrder;
|
|
337
357
|
after?: string;
|
|
338
358
|
limit?: number;
|
|
339
359
|
}, options?: CallOptions): Promise<IndexCredentialsPage>;
|
|
340
360
|
}
|
|
341
361
|
|
|
342
|
-
export type { ClientConfig as C, DocumentBlob as D, GlobalLogOptions as G, IndexCapabilities as I, LogOp as L, Provenance as P, RevChecker as R, Store as S, Trust as T, UnverifiableAxis as U, VerifyResult as V, Client as a, Callbacks as b, CallOptions as c,
|
|
362
|
+
export type { ClientConfig as C, DocumentBlob as D, EffectiveIdentity as E, GlobalLogOptions as G, IndexCapabilities as I, LogOp as L, Provenance as P, RevChecker as R, Store as S, Trust as T, UnverifiableAxis as U, VerifyResult as V, Client as a, Callbacks as b, CallOptions as c, EverProvedIdentity as d, GlobalLogPage as e, GlobalLogResult as f, IndexContentPage as g, IndexContentRow as h, IndexCountersignatureRow as i, IndexCountersignaturesPage as j, IndexCredentialRow as k, IndexCredentialsPage as l, IndexIdentitiesPage as m, IndexIdentityProfile as n, IndexIdentityRow as o, IndexOrder as p, IndexRecencyOrder as q, RelayHealth as r, RelayResponse as s, Resolution as t, Resolved as u, ResolvedContent as v, ResolvedCredential as w };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metalabel/dfos-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.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.52.0",
|
|
51
|
+
"@metalabel/dfos-web-relay": "^0.52.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-
|
|
59
|
-
"@metalabel/dfos-
|
|
58
|
+
"@metalabel/dfos-protocol": "0.52.0",
|
|
59
|
+
"@metalabel/dfos-web-relay": "0.52.0"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"build": "tsup",
|