wire-mesh-core 1.18.0 → 1.19.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.
Files changed (55) hide show
  1. package/dist/adapters/frame-codec.d.cts +1 -1
  2. package/dist/adapters/frame-codec.d.mts +1 -1
  3. package/dist/adapters/node-identity.d.cts +2 -2
  4. package/dist/adapters/node-identity.d.mts +2 -2
  5. package/dist/adapters/tcp-transport.d.cts +1 -1
  6. package/dist/adapters/tcp-transport.d.mts +1 -1
  7. package/dist/adapters/tls-transport.d.cts +1 -1
  8. package/dist/adapters/tls-transport.d.mts +1 -1
  9. package/dist/domain/capability-grant.cjs +3 -3
  10. package/dist/domain/capability-grant.d.cts +3 -3
  11. package/dist/domain/capability-grant.d.mts +3 -3
  12. package/dist/domain/capability-grant.mjs +1 -1
  13. package/dist/domain/capability-request.cjs +2 -2
  14. package/dist/domain/capability-request.d.cts +2 -2
  15. package/dist/domain/capability-request.d.mts +2 -2
  16. package/dist/domain/capability-request.mjs +1 -1
  17. package/dist/domain/device-id.d.cts +1 -1
  18. package/dist/domain/device-id.d.mts +1 -1
  19. package/dist/domain/handshake.d.cts +1 -1
  20. package/dist/domain/handshake.d.mts +1 -1
  21. package/dist/domain/mesh-session.d.cts +3 -3
  22. package/dist/domain/mesh-session.d.mts +3 -3
  23. package/dist/domain/relay-hub.d.cts +1 -1
  24. package/dist/domain/relay-hub.d.mts +1 -1
  25. package/dist/domain/revocation-view.cjs +2 -2
  26. package/dist/domain/revocation-view.d.cts +2 -2
  27. package/dist/domain/revocation-view.d.mts +2 -2
  28. package/dist/domain/revocation-view.mjs +1 -1
  29. package/dist/domain/room-token-verification.cjs +2 -2
  30. package/dist/domain/room-token-verification.d.cts +2 -2
  31. package/dist/domain/room-token-verification.d.mts +2 -2
  32. package/dist/domain/room-token-verification.mjs +1 -1
  33. package/dist/domain/tokens.cjs +6 -315
  34. package/dist/domain/tokens.d.cts +2 -104
  35. package/dist/domain/tokens.d.mts +2 -104
  36. package/dist/domain/tokens.mjs +2 -310
  37. package/dist/generated/protocol.cjs +2 -1
  38. package/dist/generated/protocol.d.cts +1 -1
  39. package/dist/generated/protocol.d.mts +1 -1
  40. package/dist/generated/protocol.mjs +2 -1
  41. package/dist/{identity-B1hBV3YA.d.mts → identity-B7YMBhMf.d.mts} +1 -1
  42. package/dist/{identity-BldV8xFj.d.cts → identity-COdFfaW8.d.cts} +1 -1
  43. package/dist/ports/identity.d.cts +1 -1
  44. package/dist/ports/identity.d.mts +1 -1
  45. package/dist/ports/transport.d.cts +1 -1
  46. package/dist/ports/transport.d.mts +1 -1
  47. package/dist/{protocol-Dhswk7YD.d.cts → protocol-B3U828E7.d.cts} +1 -0
  48. package/dist/{protocol-Dhswk7YD.d.mts → protocol-B3U828E7.d.mts} +1 -0
  49. package/dist/tokens-BxTxBmXt.cjs +499 -0
  50. package/dist/tokens-D2aFwPcZ.d.cts +122 -0
  51. package/dist/tokens-D9I1Pn14.mjs +464 -0
  52. package/dist/tokens-DSwixfDl.d.mts +122 -0
  53. package/dist/{transport-BNNdUBq-.d.mts → transport-B_diuPHT.d.mts} +1 -1
  54. package/dist/{transport-hEUxrdhT.d.cts → transport-BrAqN6I5.d.cts} +1 -1
  55. package/package.json +2 -1
@@ -1,316 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_generated_protocol = require("../generated/protocol.cjs");
3
- let cbor2 = require("cbor2");
4
- //#region src/domain/tokens.ts
5
- function bytesEqual(a, b) {
6
- if (a.length !== b.length) return false;
7
- for (let i = 0; i < a.length; i += 1) if (a[i] !== b[i]) return false;
8
- return true;
9
- }
10
- /** True when the path contains a "." or ".." segment. Purely lexical prefix comparison would let "/work/../org" pass under "/work" -- a path that normalises outside the parent -- so any relative segment fails the narrowing comparison wholesale: fail-closed rather than reimplementing path normalisation, consistent with how empty, case-different, and non-boundary-prefixed paths already behave. */
11
- function hasRelativeSegment(path) {
12
- return path.split("/").some((segment) => segment === "." || segment === "..");
13
- }
14
- /** True when childPath is parentPath or a descendant of it, compared on "/"-segment boundaries: "/work/sub" narrows "/work", but "/workbook" does NOT narrow "/work" despite the string prefix, because "book" continues the same segment. Paths containing "." or ".." segments never narrow anything (see hasRelativeSegment). */
15
- function pathNarrows(childPath, parentPath) {
16
- if (hasRelativeSegment(childPath) || hasRelativeSegment(parentPath)) return false;
17
- if (childPath === parentPath) return true;
18
- if (!childPath.startsWith(parentPath)) return false;
19
- if (parentPath.endsWith("/")) return true;
20
- return childPath.charAt(parentPath.length) === "/";
21
- }
22
- /**
23
- * True when childScope narrows parentScope per tokens.cddl ("each hop can only narrow authority, never widen it"): the kind must be identical (a different kind is a different kind of authority, not a narrower one), and a parent with a path requires the child to carry an equal-or-descendant path -- an absent child path means the kind's whole-scope root, which is wider than any path-narrowed parent. A parent with no path (whole-scope root) lets any child path under the same kind through. Exported for capability-grant.ts's own obligation 4 (an unsolicited push's embedded token must equal-or-root the enclosing request's own scope), which is exactly this same narrowing relation applied outside a delegation chain.
24
- */
25
- function scopeNarrows(parent, child) {
26
- if (parent.kind !== child.kind) return false;
27
- if (parent.path === void 0) return true;
28
- if (child.path === void 0) return false;
29
- return pathNarrows(child.path, parent.path);
30
- }
31
- /** RFC 9052 §4.4 Sig_structure for a COSE_Sign1 with no external AAD: ["Signature1", protected, external_aad, payload]. */
32
- function sig1ToBeSigned(protectedHeader, payload) {
33
- return (0, cbor2.encode)([
34
- "Signature1",
35
- protectedHeader,
36
- /* @__PURE__ */ new Uint8Array(0),
37
- payload
38
- ], cbor2.cdeEncodeOptions);
39
- }
40
- /** Normalises cbor2's encode() (and any other Uint8Array<ArrayBufferLike>-typed construction) to a fresh, non-shared, whole-buffer Uint8Array<ArrayBuffer> -- what the generated schemas' concrete-typed fields require. */
41
- function buf(bytes) {
42
- return Uint8Array.from(bytes);
43
- }
44
- function encodeBuf(value) {
45
- return buf((0, cbor2.encode)(value, cbor2.cdeEncodeOptions));
46
- }
47
- /** The COSE protected header every capability-token/revocation-entry envelope in this codebase actually signs over: label 1 (alg) and label 4 (kid, the issuer's own device-id) -- matching the frozen conformance vectors, not the empty header a token merely needs to verify against itself. */
48
- function protectedHeaderFor(identity) {
49
- return encodeBuf({
50
- 1: identity.identityKey.alg,
51
- 4: identity.deviceId
52
- });
53
- }
54
- /** Decodes and validates a parent token's own claims from its raw CapabilityToken tuple -- the same decode `verifyTokenChain` performs on `claims.parent`, extracted here so mint can check narrowing against a parent's real claims without duplicating the CBOR/schema plumbing. Returns undefined for anything that doesn't parse; the caller turns that into its own refusal reason since "malformed" means something different at mint time than at verify time. */
55
- function decodeTokenClaims(token) {
56
- const [, , payload] = token;
57
- if (payload === null) return void 0;
58
- let decoded;
59
- try {
60
- decoded = (0, cbor2.decode)(payload, cbor2.cdeDecodeOptions);
61
- } catch {
62
- return;
63
- }
64
- const result = require_generated_protocol.tokenClaimsSchema.safeParse(decoded);
65
- return result.success ? result.data : void 0;
66
- }
67
- /**
68
- * Verifies one capability token per tokens.cddl's own documented rules: the token is a well-formed COSE_Sign1 whose signature actually verifies against its own embedded issuer-key, that issuer-key is self-certifying (sha256(issuer-key.public-key) equals the claimed issuer device-id -- no shared secret needed to check this), the token is currently valid (not expired, not before not-before, not revoked by its own issuer), and -- recursively -- any parent delegation narrows rather than widens across all three axes of authority: the parent's bearer must be this token's issuer (the delegation chain is unbroken), this token's expiry must not exceed its parent's, and this token's scope must narrow its parent's (same kind; equal-or-descendant path when the parent carries one) with an identical capability verb (the capability-verb grammar has no sub-verb relation, so a different verb is a different authority, not a narrower one). Undecodable payload bytes return "malformed" and undecodable parent bytes return "parent_invalid" -- hostile input produces a verdict, never a throw.
69
- */
70
- async function verifyCapabilityToken(token, options) {
71
- const verdict = await verifyTokenChain(token, {
72
- identity: options.identity,
73
- clock: options.clock,
74
- revocation: options.revocation
75
- });
76
- if (!verdict.ok) return verdict;
77
- if (options.expectedBearer !== void 0 && !bytesEqual(verdict.claims.bearer, options.expectedBearer)) return {
78
- ok: false,
79
- reason: "bearer_mismatch"
80
- };
81
- return verdict;
82
- }
83
- async function verifyTokenChain(token, options) {
84
- const [protectedHeader, , payload, signature] = token;
85
- if (payload === null) return {
86
- ok: false,
87
- reason: "malformed"
88
- };
89
- let decodedClaims;
90
- try {
91
- decodedClaims = (0, cbor2.decode)(payload, cbor2.cdeDecodeOptions);
92
- } catch {
93
- return {
94
- ok: false,
95
- reason: "malformed"
96
- };
97
- }
98
- const claimsResult = require_generated_protocol.tokenClaimsSchema.safeParse(decodedClaims);
99
- if (!claimsResult.success) return {
100
- ok: false,
101
- reason: "malformed"
102
- };
103
- const claims = claimsResult.data;
104
- if (!await options.identity.verify(claims["issuer-key"], sig1ToBeSigned(protectedHeader, payload), signature)) return {
105
- ok: false,
106
- reason: "bad_signature"
107
- };
108
- if (!bytesEqual(await options.identity.deriveDeviceId(claims["issuer-key"]["public-key"]), claims.issuer)) return {
109
- ok: false,
110
- reason: "wrong_issuer"
111
- };
112
- const now = options.clock.now();
113
- if (claims.expires <= now) return {
114
- ok: false,
115
- reason: "expired"
116
- };
117
- if (claims["not-before"] !== void 0 && claims["not-before"] > now) return {
118
- ok: false,
119
- reason: "not_yet_valid"
120
- };
121
- const validUntil = claims["valid-until"];
122
- if (validUntil !== void 0 && validUntil <= now) return {
123
- ok: false,
124
- reason: "content_expired"
125
- };
126
- if (await options.revocation.isRevoked(claims["token-id"], claims.issuer)) return {
127
- ok: false,
128
- reason: "revoked"
129
- };
130
- if (claims.parent !== void 0) {
131
- let decodedParent;
132
- try {
133
- decodedParent = (0, cbor2.decode)(claims.parent, cbor2.cdeDecodeOptions);
134
- } catch {
135
- return {
136
- ok: false,
137
- reason: "parent_invalid"
138
- };
139
- }
140
- const parentResult = require_generated_protocol.capabilityTokenSchema.safeParse(decodedParent);
141
- if (!parentResult.success) return {
142
- ok: false,
143
- reason: "parent_invalid"
144
- };
145
- const parentVerdict = await verifyTokenChain(parentResult.data, options);
146
- if (!parentVerdict.ok) return {
147
- ok: false,
148
- reason: "parent_invalid"
149
- };
150
- if (!bytesEqual(parentVerdict.claims.bearer, claims.issuer)) return {
151
- ok: false,
152
- reason: "delegation_exceeds_parent"
153
- };
154
- if (claims.expires > parentVerdict.claims.expires) return {
155
- ok: false,
156
- reason: "delegation_exceeds_parent"
157
- };
158
- if (!scopeNarrows(parentVerdict.claims.scope, claims.scope)) return {
159
- ok: false,
160
- reason: "delegation_exceeds_parent"
161
- };
162
- if (parentVerdict.claims.capability !== claims.capability) return {
163
- ok: false,
164
- reason: "delegation_exceeds_parent"
165
- };
166
- const parentRemaining = parentVerdict.claims["delegations-remaining"];
167
- if (parentRemaining !== void 0 && (claims["delegations-remaining"] === void 0 || claims["delegations-remaining"] >= parentRemaining)) return {
168
- ok: false,
169
- reason: "delegation_exceeds_parent"
170
- };
171
- return {
172
- ok: true,
173
- claims,
174
- rootIssuer: parentVerdict.rootIssuer,
175
- depth: parentVerdict.depth + 1
176
- };
177
- }
178
- return {
179
- ok: true,
180
- claims,
181
- rootIssuer: claims.issuer,
182
- depth: 0
183
- };
184
- }
185
- /**
186
- * Verifies one gossiped revocation-entry (management.cddl): a well-formed COSE_Sign1 whose signature verifies against its own embedded issuer-key, where that issuer-key is self-certifying (sha256(issuer-key.public-key) equals the claimed issuer device-id). A verifier that ingests a revocation-announce frame runs each entry through this before recording it in its revocation view; entries failing here are dropped, not stored. The issuer-match against a specific token's own issuer (only a token's own issuer may revoke it) is deliberately NOT checked here -- it happens at lookup time in RevocationCheck, against whichever token is being verified.
187
- */
188
- async function verifyRevocationEntry(entry, options) {
189
- const [protectedHeader, , payload, signature] = entry;
190
- if (payload === null) return {
191
- ok: false,
192
- reason: "malformed"
193
- };
194
- let decodedClaims;
195
- try {
196
- decodedClaims = (0, cbor2.decode)(payload, cbor2.cdeDecodeOptions);
197
- } catch {
198
- return {
199
- ok: false,
200
- reason: "malformed"
201
- };
202
- }
203
- const claimsResult = require_generated_protocol.revocationClaimsSchema.safeParse(decodedClaims);
204
- if (!claimsResult.success) return {
205
- ok: false,
206
- reason: "malformed"
207
- };
208
- const claims = claimsResult.data;
209
- if (!await options.identity.verify(claims["issuer-key"], sig1ToBeSigned(protectedHeader, payload), signature)) return {
210
- ok: false,
211
- reason: "bad_signature"
212
- };
213
- if (!bytesEqual(await options.identity.deriveDeviceId(claims["issuer-key"]["public-key"]), claims.issuer)) return {
214
- ok: false,
215
- reason: "wrong_issuer"
216
- };
217
- return {
218
- ok: true,
219
- claims
220
- };
221
- }
222
- /** The narrowing arithmetic tokens.cddl's own delegation obligations require (bearer match, expiry within the parent's, scope narrows, same capability, delegations-remaining strictly less than the parent's) -- shared between mintCapabilityToken (which additionally builds and signs the resulting token) and canGrant (a pure query with no minting side effect at all), so the two can never silently drift into two different ideas of what "narrows" means. Returns the specific refusal reason, or undefined when every rule is satisfied. */
223
- function checkNarrowing(parentClaims, granterDeviceId, candidate) {
224
- if (!bytesEqual(parentClaims.bearer, granterDeviceId)) return "parent_bearer_mismatch";
225
- if (candidate.expires > parentClaims.expires) return "expires_exceeds_parent";
226
- if (!scopeNarrows(parentClaims.scope, candidate.scope)) return "scope_does_not_narrow";
227
- if (parentClaims.capability !== candidate.capability) return "capability_mismatch";
228
- const parentRemaining = parentClaims["delegations-remaining"];
229
- if (parentRemaining !== void 0 && (candidate.delegationsRemaining === void 0 || candidate.delegationsRemaining >= parentRemaining)) return "delegation_exceeds_parent";
230
- }
231
- /**
232
- * A pure query: could deviceId, presenting heldToken as its own delegation authority, successfully mint a delegation matching candidate right now -- without attempting (and potentially failing) a real mint just to find out. Reuses mintCapabilityToken's own narrowing arithmetic via checkNarrowing, so the two can never silently drift into different ideas of what "narrows" means.
233
- *
234
- * Deliberately narrower than a full mint attempt in one respect: this checks only the narrowing rules tokens.cddl's own delegation obligations require (bearer match, expiry, scope, capability, delegations-remaining), the same scope mintCapabilityToken itself checks a *parent* against -- it does not verify heldToken's own signature or revocation status, exactly as mintCapabilityToken never re-verifies its own parent's signature either. A caller that also needs heldToken's cryptographic validity confirmed calls verifyCapabilityToken separately.
235
- */
236
- function canGrant(heldToken, deviceId, candidate, now) {
237
- if (candidate.expires <= now) return false;
238
- const heldClaims = decodeTokenClaims(heldToken);
239
- if (heldClaims === void 0) return false;
240
- return checkNarrowing(heldClaims, deviceId, candidate) === void 0;
241
- }
242
- /**
243
- * Mints one capability token: builds token-claims from the given fields, signs it as a COSE_Sign1 under `identity`'s own key, with a protected header matching what the frozen conformance vectors actually encode (`{1: alg, 4: issuer device-id}`, not the empty header a token merely needs to verify against itself).
244
- *
245
- * When `parent` is given, every one of `tokens.cddl`'s own narrowing obligations is enforced here, at issuance, rather than left for the far end to discover minutes or hours later as a bare `delegation_exceeds_parent` from `verifyCapabilityToken` -- the same "fail loudly, fail early" reasoning that governs every other boundary in this codebase. An issuer minting an invalid delegation is a bug in the caller; this function refuses rather than producing a token indistinguishable from a valid one until someone else verifies it.
246
- */
247
- async function mintCapabilityToken(options) {
248
- if (options.expires <= options.clock.now()) return {
249
- ok: false,
250
- reason: "already_expired"
251
- };
252
- let parentBytes;
253
- if (options.parent !== void 0) {
254
- const parentClaims = decodeTokenClaims(options.parent);
255
- if (parentClaims === void 0) return {
256
- ok: false,
257
- reason: "parent_malformed"
258
- };
259
- const refusal = checkNarrowing(parentClaims, options.identity.deviceId, {
260
- capability: options.capability,
261
- scope: options.scope,
262
- expires: options.expires,
263
- ...options.delegationsRemaining !== void 0 ? { delegationsRemaining: options.delegationsRemaining } : {}
264
- });
265
- if (refusal !== void 0) return {
266
- ok: false,
267
- reason: refusal
268
- };
269
- parentBytes = encodeBuf(options.parent);
270
- }
271
- const payload = encodeBuf({
272
- "token-id": options.tokenId,
273
- issuer: options.identity.deviceId,
274
- "issuer-key": options.identity.identityKey,
275
- bearer: options.bearer,
276
- capability: options.capability,
277
- scope: options.scope,
278
- expires: options.expires,
279
- ...options.notBefore !== void 0 ? { "not-before": options.notBefore } : {},
280
- ...parentBytes !== void 0 ? { parent: parentBytes } : {},
281
- ...options.delegationsRemaining !== void 0 ? { "delegations-remaining": options.delegationsRemaining } : {}
282
- });
283
- const protectedHeader = protectedHeaderFor(options.identity);
284
- return {
285
- ok: true,
286
- token: [
287
- protectedHeader,
288
- {},
289
- payload,
290
- await options.identity.sign(sig1ToBeSigned(protectedHeader, payload))
291
- ]
292
- };
293
- }
294
- /** Mints one revocation-entry (management.cddl): a COSE_Sign1 over revocation-claims, signed the same way mintCapabilityToken signs a token. No narrowing chain to check -- a revocation entry has no parent and cannot fail to be issuable the way a delegated token can, so this returns the entry directly rather than a verdict. */
295
- async function mintRevocationEntry(options) {
296
- const payload = encodeBuf({
297
- "token-id": options.tokenId,
298
- issuer: options.identity.deviceId,
299
- "issuer-key": options.identity.identityKey,
300
- "revoked-at": options.revokedAt
301
- });
302
- const protectedHeader = protectedHeaderFor(options.identity);
303
- return [
304
- protectedHeader,
305
- {},
306
- payload,
307
- await options.identity.sign(sig1ToBeSigned(protectedHeader, payload))
308
- ];
309
- }
310
- //#endregion
311
- exports.canGrant = canGrant;
312
- exports.mintCapabilityToken = mintCapabilityToken;
313
- exports.mintRevocationEntry = mintRevocationEntry;
314
- exports.scopeNarrows = scopeNarrows;
315
- exports.verifyCapabilityToken = verifyCapabilityToken;
316
- exports.verifyRevocationEntry = verifyRevocationEntry;
2
+ const require_tokens = require("../tokens-BxTxBmXt.cjs");
3
+ exports.canGrant = require_tokens.canGrant;
4
+ exports.mintCapabilityToken = require_tokens.mintCapabilityToken;
5
+ exports.mintRevocationEntry = require_tokens.mintRevocationEntry;
6
+ exports.verifyCapabilityToken = require_tokens.verifyCapabilityToken;
7
+ exports.verifyRevocationEntry = require_tokens.verifyRevocationEntry;
@@ -1,104 +1,2 @@
1
- import { dt as RevocationEntry, kt as TokenClaims, s as CapabilityToken, ut as RevocationClaims, x as DeviceId } from "../protocol-Dhswk7YD.cjs";
2
- import { t as IdentityPort } from "../identity-BldV8xFj.cjs";
3
- import { t as Clock } from "../clock-DiSx-WKM.cjs";
4
- //#region src/domain/tokens.d.ts
5
- /**
6
- * The revocation view a verifier consults. Contract per management.cddl: an entry counts against a token only when BOTH its token-id and its issuer match the token's own -- only a token's own issuer may revoke it, so a third party's entry for someone else's token-id must be ignored. Implementations ingest gossiped revocation-announce frames via verifyRevocationEntry (which enforces each entry's own signature and self-certification) and key the resulting claims by token-id + issuer.
7
- */
8
- export interface RevocationCheck {
9
- isRevoked: (tokenId: Uint8Array, issuer: DeviceId) => Promise<boolean>;
10
- }
11
- export type TokenVerdictReason = "malformed" | "bad_signature" | "wrong_issuer" | "bearer_mismatch" | "expired" | "not_yet_valid" | "content_expired" | "revoked" | "delegation_exceeds_parent" | "parent_invalid";
12
- export type TokenVerdict = {
13
- ok: true;
14
- claims: TokenClaims;
15
- /** The device-id at the root of this token's delegation chain: its own issuer when it carries no parent, otherwise the root of its parent's chain. Lets a caller (e.g. core/room's obligation that a chain must terminate at the path's own owner, or the verifier itself for a DM) check the chain's root with one equality comparison instead of re-walking the parent chain a second time. */
16
- rootIssuer: DeviceId;
17
- /** How many delegation hops this token is from its own root -- 0 for a root grant. Costs nothing extra once rootIssuer is being tracked, and makes the delegation bound observable for diagnostics. */
18
- depth: number;
19
- } | {
20
- ok: false;
21
- reason: TokenVerdictReason;
22
- };
23
- export interface VerifyCapabilityTokenOptions {
24
- identity: IdentityPort;
25
- clock: Clock;
26
- revocation: RevocationCheck;
27
- /** When given, the token must bear this device -- the caller presenting a token to authorise itself, not someone else. */
28
- expectedBearer?: DeviceId;
29
- }
30
- /**
31
- * True when childScope narrows parentScope per tokens.cddl ("each hop can only narrow authority, never widen it"): the kind must be identical (a different kind is a different kind of authority, not a narrower one), and a parent with a path requires the child to carry an equal-or-descendant path -- an absent child path means the kind's whole-scope root, which is wider than any path-narrowed parent. A parent with no path (whole-scope root) lets any child path under the same kind through. Exported for capability-grant.ts's own obligation 4 (an unsolicited push's embedded token must equal-or-root the enclosing request's own scope), which is exactly this same narrowing relation applied outside a delegation chain.
32
- */
33
- export declare function scopeNarrows(parent: TokenClaims["scope"], child: TokenClaims["scope"]): boolean;
34
- /**
35
- * Verifies one capability token per tokens.cddl's own documented rules: the token is a well-formed COSE_Sign1 whose signature actually verifies against its own embedded issuer-key, that issuer-key is self-certifying (sha256(issuer-key.public-key) equals the claimed issuer device-id -- no shared secret needed to check this), the token is currently valid (not expired, not before not-before, not revoked by its own issuer), and -- recursively -- any parent delegation narrows rather than widens across all three axes of authority: the parent's bearer must be this token's issuer (the delegation chain is unbroken), this token's expiry must not exceed its parent's, and this token's scope must narrow its parent's (same kind; equal-or-descendant path when the parent carries one) with an identical capability verb (the capability-verb grammar has no sub-verb relation, so a different verb is a different authority, not a narrower one). Undecodable payload bytes return "malformed" and undecodable parent bytes return "parent_invalid" -- hostile input produces a verdict, never a throw.
36
- */
37
- export declare function verifyCapabilityToken(token: CapabilityToken, options: VerifyCapabilityTokenOptions): Promise<TokenVerdict>;
38
- export type RevocationEntryVerdictReason = "malformed" | "bad_signature" | "wrong_issuer";
39
- export type RevocationEntryVerdict = {
40
- ok: true;
41
- claims: RevocationClaims;
42
- } | {
43
- ok: false;
44
- reason: RevocationEntryVerdictReason;
45
- };
46
- export interface VerifyRevocationEntryOptions {
47
- /** Crypto primitives only -- any IdentityPort instance can verify any entry, since everything needed to check one travels inside the entry itself. */
48
- identity: IdentityPort;
49
- }
50
- /**
51
- * Verifies one gossiped revocation-entry (management.cddl): a well-formed COSE_Sign1 whose signature verifies against its own embedded issuer-key, where that issuer-key is self-certifying (sha256(issuer-key.public-key) equals the claimed issuer device-id). A verifier that ingests a revocation-announce frame runs each entry through this before recording it in its revocation view; entries failing here are dropped, not stored. The issuer-match against a specific token's own issuer (only a token's own issuer may revoke it) is deliberately NOT checked here -- it happens at lookup time in RevocationCheck, against whichever token is being verified.
52
- */
53
- export declare function verifyRevocationEntry(entry: RevocationEntry, options: VerifyRevocationEntryOptions): Promise<RevocationEntryVerdict>;
54
- export type MintRefusalReason = "already_expired" | "parent_malformed" | "parent_bearer_mismatch" | "expires_exceeds_parent" | "scope_does_not_narrow" | "capability_mismatch" | "delegation_exceeds_parent";
55
- export type MintVerdict = {
56
- ok: true;
57
- token: CapabilityToken;
58
- } | {
59
- ok: false;
60
- reason: MintRefusalReason;
61
- };
62
- /** What a would-be delegation needs, to check it narrows a specific parent -- everything mintCapabilityToken itself checks a delegation against, independent of the tokenId/bearer/notBefore/signing concerns unique to actually minting one. */
63
- interface NarrowingCandidate {
64
- capability: TokenClaims["capability"];
65
- scope: TokenClaims["scope"];
66
- expires: number;
67
- delegationsRemaining?: number;
68
- }
69
- /**
70
- * A pure query: could deviceId, presenting heldToken as its own delegation authority, successfully mint a delegation matching candidate right now -- without attempting (and potentially failing) a real mint just to find out. Reuses mintCapabilityToken's own narrowing arithmetic via checkNarrowing, so the two can never silently drift into different ideas of what "narrows" means.
71
- *
72
- * Deliberately narrower than a full mint attempt in one respect: this checks only the narrowing rules tokens.cddl's own delegation obligations require (bearer match, expiry, scope, capability, delegations-remaining), the same scope mintCapabilityToken itself checks a *parent* against -- it does not verify heldToken's own signature or revocation status, exactly as mintCapabilityToken never re-verifies its own parent's signature either. A caller that also needs heldToken's cryptographic validity confirmed calls verifyCapabilityToken separately.
73
- */
74
- export declare function canGrant(heldToken: CapabilityToken, deviceId: DeviceId, candidate: Readonly<NarrowingCandidate>, now: number): boolean;
75
- export interface MintCapabilityTokenOptions {
76
- /** The issuer -- signs the token, and supplies the self-certifying issuer/issuer-key claims. */
77
- identity: IdentityPort;
78
- clock: Clock;
79
- tokenId: Uint8Array<ArrayBuffer>;
80
- bearer: DeviceId;
81
- capability: TokenClaims["capability"];
82
- scope: TokenClaims["scope"];
83
- expires: number;
84
- notBefore?: number;
85
- /** How many further delegation hops the *resulting* token itself permits below it -- unrelated to, and never a bound on, whether `identity` may mint further tokens of its own at the root level for other bearers. Those are two different facts: a token minted with `delegationsRemaining: 0` genuinely cannot itself be re-delegated (correct -- e.g. a room owner's own self-signed root grant, which should never be handed onward), but that same `0` says nothing about the issuer's own separate, ordinary authority to mint additional independent root-level grants (naming no `parent` at all) for other bearers. Root-level minting for a second bearer is never blocked by any existing token's own `delegationsRemaining`, because it uses no `parent` in the first place -- there is no narrowing check to run. Confirmed live in agent-comms' own room-membership implementation (`ExaDev/agent-comms` PR #72): each member's own join/invite grant is minted as its own independent, parent-less, root-level token precisely because the room owner's `delegationsRemaining: 0` self-grant cannot parent anything -- correct by this same reasoning, not a workaround. */
86
- delegationsRemaining?: number;
87
- /** The issuer's own token, when this is a delegation rather than a root grant. Its claims are checked against every narrowing rule below -- mint refuses rather than producing a token verifyCapabilityToken would reject anyway. Omit entirely for a root-level grant (including a second, independent root-level grant for a different bearer under the same capability/scope this issuer already grants elsewhere) -- there is no bound on how many such root grants an issuer may mint, since none of them narrows any other. */
88
- parent?: CapabilityToken;
89
- }
90
- /**
91
- * Mints one capability token: builds token-claims from the given fields, signs it as a COSE_Sign1 under `identity`'s own key, with a protected header matching what the frozen conformance vectors actually encode (`{1: alg, 4: issuer device-id}`, not the empty header a token merely needs to verify against itself).
92
- *
93
- * When `parent` is given, every one of `tokens.cddl`'s own narrowing obligations is enforced here, at issuance, rather than left for the far end to discover minutes or hours later as a bare `delegation_exceeds_parent` from `verifyCapabilityToken` -- the same "fail loudly, fail early" reasoning that governs every other boundary in this codebase. An issuer minting an invalid delegation is a bug in the caller; this function refuses rather than producing a token indistinguishable from a valid one until someone else verifies it.
94
- */
95
- export declare function mintCapabilityToken(options: MintCapabilityTokenOptions): Promise<MintVerdict>;
96
- export interface MintRevocationEntryOptions {
97
- /** The token's own issuer -- only a token's own issuer may revoke it (management.cddl), so this must be the same identity that minted the token being revoked. */
98
- identity: IdentityPort;
99
- tokenId: Uint8Array<ArrayBuffer>;
100
- revokedAt: number;
101
- }
102
- /** Mints one revocation-entry (management.cddl): a COSE_Sign1 over revocation-claims, signed the same way mintCapabilityToken signs a token. No narrowing chain to check -- a revocation entry has no parent and cannot fail to be issuable the way a delegated token can, so this returns the entry directly rather than a verdict. */
103
- export declare function mintRevocationEntry(options: MintRevocationEntryOptions): Promise<RevocationEntry>;
104
- //#endregion
1
+ import { a as RevocationCheck, c as TokenVerdict, d as VerifyRevocationEntryOptions, f as canGrant, g as verifyRevocationEntry, h as verifyCapabilityToken, i as MintVerdict, l as TokenVerdictReason, m as mintRevocationEntry, n as MintRefusalReason, o as RevocationEntryVerdict, p as mintCapabilityToken, r as MintRevocationEntryOptions, s as RevocationEntryVerdictReason, t as MintCapabilityTokenOptions, u as VerifyCapabilityTokenOptions } from "../tokens-D2aFwPcZ.cjs";
2
+ export { MintCapabilityTokenOptions, MintRefusalReason, MintRevocationEntryOptions, MintVerdict, RevocationCheck, RevocationEntryVerdict, RevocationEntryVerdictReason, TokenVerdict, TokenVerdictReason, VerifyCapabilityTokenOptions, VerifyRevocationEntryOptions, canGrant, mintCapabilityToken, mintRevocationEntry, verifyCapabilityToken, verifyRevocationEntry };
@@ -1,104 +1,2 @@
1
- import { dt as RevocationEntry, kt as TokenClaims, s as CapabilityToken, ut as RevocationClaims, x as DeviceId } from "../protocol-Dhswk7YD.mjs";
2
- import { t as IdentityPort } from "../identity-B1hBV3YA.mjs";
3
- import { t as Clock } from "../clock-DiSx-WKM.mjs";
4
- //#region src/domain/tokens.d.ts
5
- /**
6
- * The revocation view a verifier consults. Contract per management.cddl: an entry counts against a token only when BOTH its token-id and its issuer match the token's own -- only a token's own issuer may revoke it, so a third party's entry for someone else's token-id must be ignored. Implementations ingest gossiped revocation-announce frames via verifyRevocationEntry (which enforces each entry's own signature and self-certification) and key the resulting claims by token-id + issuer.
7
- */
8
- export interface RevocationCheck {
9
- isRevoked: (tokenId: Uint8Array, issuer: DeviceId) => Promise<boolean>;
10
- }
11
- export type TokenVerdictReason = "malformed" | "bad_signature" | "wrong_issuer" | "bearer_mismatch" | "expired" | "not_yet_valid" | "content_expired" | "revoked" | "delegation_exceeds_parent" | "parent_invalid";
12
- export type TokenVerdict = {
13
- ok: true;
14
- claims: TokenClaims;
15
- /** The device-id at the root of this token's delegation chain: its own issuer when it carries no parent, otherwise the root of its parent's chain. Lets a caller (e.g. core/room's obligation that a chain must terminate at the path's own owner, or the verifier itself for a DM) check the chain's root with one equality comparison instead of re-walking the parent chain a second time. */
16
- rootIssuer: DeviceId;
17
- /** How many delegation hops this token is from its own root -- 0 for a root grant. Costs nothing extra once rootIssuer is being tracked, and makes the delegation bound observable for diagnostics. */
18
- depth: number;
19
- } | {
20
- ok: false;
21
- reason: TokenVerdictReason;
22
- };
23
- export interface VerifyCapabilityTokenOptions {
24
- identity: IdentityPort;
25
- clock: Clock;
26
- revocation: RevocationCheck;
27
- /** When given, the token must bear this device -- the caller presenting a token to authorise itself, not someone else. */
28
- expectedBearer?: DeviceId;
29
- }
30
- /**
31
- * True when childScope narrows parentScope per tokens.cddl ("each hop can only narrow authority, never widen it"): the kind must be identical (a different kind is a different kind of authority, not a narrower one), and a parent with a path requires the child to carry an equal-or-descendant path -- an absent child path means the kind's whole-scope root, which is wider than any path-narrowed parent. A parent with no path (whole-scope root) lets any child path under the same kind through. Exported for capability-grant.ts's own obligation 4 (an unsolicited push's embedded token must equal-or-root the enclosing request's own scope), which is exactly this same narrowing relation applied outside a delegation chain.
32
- */
33
- export declare function scopeNarrows(parent: TokenClaims["scope"], child: TokenClaims["scope"]): boolean;
34
- /**
35
- * Verifies one capability token per tokens.cddl's own documented rules: the token is a well-formed COSE_Sign1 whose signature actually verifies against its own embedded issuer-key, that issuer-key is self-certifying (sha256(issuer-key.public-key) equals the claimed issuer device-id -- no shared secret needed to check this), the token is currently valid (not expired, not before not-before, not revoked by its own issuer), and -- recursively -- any parent delegation narrows rather than widens across all three axes of authority: the parent's bearer must be this token's issuer (the delegation chain is unbroken), this token's expiry must not exceed its parent's, and this token's scope must narrow its parent's (same kind; equal-or-descendant path when the parent carries one) with an identical capability verb (the capability-verb grammar has no sub-verb relation, so a different verb is a different authority, not a narrower one). Undecodable payload bytes return "malformed" and undecodable parent bytes return "parent_invalid" -- hostile input produces a verdict, never a throw.
36
- */
37
- export declare function verifyCapabilityToken(token: CapabilityToken, options: VerifyCapabilityTokenOptions): Promise<TokenVerdict>;
38
- export type RevocationEntryVerdictReason = "malformed" | "bad_signature" | "wrong_issuer";
39
- export type RevocationEntryVerdict = {
40
- ok: true;
41
- claims: RevocationClaims;
42
- } | {
43
- ok: false;
44
- reason: RevocationEntryVerdictReason;
45
- };
46
- export interface VerifyRevocationEntryOptions {
47
- /** Crypto primitives only -- any IdentityPort instance can verify any entry, since everything needed to check one travels inside the entry itself. */
48
- identity: IdentityPort;
49
- }
50
- /**
51
- * Verifies one gossiped revocation-entry (management.cddl): a well-formed COSE_Sign1 whose signature verifies against its own embedded issuer-key, where that issuer-key is self-certifying (sha256(issuer-key.public-key) equals the claimed issuer device-id). A verifier that ingests a revocation-announce frame runs each entry through this before recording it in its revocation view; entries failing here are dropped, not stored. The issuer-match against a specific token's own issuer (only a token's own issuer may revoke it) is deliberately NOT checked here -- it happens at lookup time in RevocationCheck, against whichever token is being verified.
52
- */
53
- export declare function verifyRevocationEntry(entry: RevocationEntry, options: VerifyRevocationEntryOptions): Promise<RevocationEntryVerdict>;
54
- export type MintRefusalReason = "already_expired" | "parent_malformed" | "parent_bearer_mismatch" | "expires_exceeds_parent" | "scope_does_not_narrow" | "capability_mismatch" | "delegation_exceeds_parent";
55
- export type MintVerdict = {
56
- ok: true;
57
- token: CapabilityToken;
58
- } | {
59
- ok: false;
60
- reason: MintRefusalReason;
61
- };
62
- /** What a would-be delegation needs, to check it narrows a specific parent -- everything mintCapabilityToken itself checks a delegation against, independent of the tokenId/bearer/notBefore/signing concerns unique to actually minting one. */
63
- interface NarrowingCandidate {
64
- capability: TokenClaims["capability"];
65
- scope: TokenClaims["scope"];
66
- expires: number;
67
- delegationsRemaining?: number;
68
- }
69
- /**
70
- * A pure query: could deviceId, presenting heldToken as its own delegation authority, successfully mint a delegation matching candidate right now -- without attempting (and potentially failing) a real mint just to find out. Reuses mintCapabilityToken's own narrowing arithmetic via checkNarrowing, so the two can never silently drift into different ideas of what "narrows" means.
71
- *
72
- * Deliberately narrower than a full mint attempt in one respect: this checks only the narrowing rules tokens.cddl's own delegation obligations require (bearer match, expiry, scope, capability, delegations-remaining), the same scope mintCapabilityToken itself checks a *parent* against -- it does not verify heldToken's own signature or revocation status, exactly as mintCapabilityToken never re-verifies its own parent's signature either. A caller that also needs heldToken's cryptographic validity confirmed calls verifyCapabilityToken separately.
73
- */
74
- export declare function canGrant(heldToken: CapabilityToken, deviceId: DeviceId, candidate: Readonly<NarrowingCandidate>, now: number): boolean;
75
- export interface MintCapabilityTokenOptions {
76
- /** The issuer -- signs the token, and supplies the self-certifying issuer/issuer-key claims. */
77
- identity: IdentityPort;
78
- clock: Clock;
79
- tokenId: Uint8Array<ArrayBuffer>;
80
- bearer: DeviceId;
81
- capability: TokenClaims["capability"];
82
- scope: TokenClaims["scope"];
83
- expires: number;
84
- notBefore?: number;
85
- /** How many further delegation hops the *resulting* token itself permits below it -- unrelated to, and never a bound on, whether `identity` may mint further tokens of its own at the root level for other bearers. Those are two different facts: a token minted with `delegationsRemaining: 0` genuinely cannot itself be re-delegated (correct -- e.g. a room owner's own self-signed root grant, which should never be handed onward), but that same `0` says nothing about the issuer's own separate, ordinary authority to mint additional independent root-level grants (naming no `parent` at all) for other bearers. Root-level minting for a second bearer is never blocked by any existing token's own `delegationsRemaining`, because it uses no `parent` in the first place -- there is no narrowing check to run. Confirmed live in agent-comms' own room-membership implementation (`ExaDev/agent-comms` PR #72): each member's own join/invite grant is minted as its own independent, parent-less, root-level token precisely because the room owner's `delegationsRemaining: 0` self-grant cannot parent anything -- correct by this same reasoning, not a workaround. */
86
- delegationsRemaining?: number;
87
- /** The issuer's own token, when this is a delegation rather than a root grant. Its claims are checked against every narrowing rule below -- mint refuses rather than producing a token verifyCapabilityToken would reject anyway. Omit entirely for a root-level grant (including a second, independent root-level grant for a different bearer under the same capability/scope this issuer already grants elsewhere) -- there is no bound on how many such root grants an issuer may mint, since none of them narrows any other. */
88
- parent?: CapabilityToken;
89
- }
90
- /**
91
- * Mints one capability token: builds token-claims from the given fields, signs it as a COSE_Sign1 under `identity`'s own key, with a protected header matching what the frozen conformance vectors actually encode (`{1: alg, 4: issuer device-id}`, not the empty header a token merely needs to verify against itself).
92
- *
93
- * When `parent` is given, every one of `tokens.cddl`'s own narrowing obligations is enforced here, at issuance, rather than left for the far end to discover minutes or hours later as a bare `delegation_exceeds_parent` from `verifyCapabilityToken` -- the same "fail loudly, fail early" reasoning that governs every other boundary in this codebase. An issuer minting an invalid delegation is a bug in the caller; this function refuses rather than producing a token indistinguishable from a valid one until someone else verifies it.
94
- */
95
- export declare function mintCapabilityToken(options: MintCapabilityTokenOptions): Promise<MintVerdict>;
96
- export interface MintRevocationEntryOptions {
97
- /** The token's own issuer -- only a token's own issuer may revoke it (management.cddl), so this must be the same identity that minted the token being revoked. */
98
- identity: IdentityPort;
99
- tokenId: Uint8Array<ArrayBuffer>;
100
- revokedAt: number;
101
- }
102
- /** Mints one revocation-entry (management.cddl): a COSE_Sign1 over revocation-claims, signed the same way mintCapabilityToken signs a token. No narrowing chain to check -- a revocation entry has no parent and cannot fail to be issuable the way a delegated token can, so this returns the entry directly rather than a verdict. */
103
- export declare function mintRevocationEntry(options: MintRevocationEntryOptions): Promise<RevocationEntry>;
104
- //#endregion
1
+ import { a as RevocationCheck, c as TokenVerdict, d as VerifyRevocationEntryOptions, f as canGrant, g as verifyRevocationEntry, h as verifyCapabilityToken, i as MintVerdict, l as TokenVerdictReason, m as mintRevocationEntry, n as MintRefusalReason, o as RevocationEntryVerdict, p as mintCapabilityToken, r as MintRevocationEntryOptions, s as RevocationEntryVerdictReason, t as MintCapabilityTokenOptions, u as VerifyCapabilityTokenOptions } from "../tokens-DSwixfDl.mjs";
2
+ export { MintCapabilityTokenOptions, MintRefusalReason, MintRevocationEntryOptions, MintVerdict, RevocationCheck, RevocationEntryVerdict, RevocationEntryVerdictReason, TokenVerdict, TokenVerdictReason, VerifyCapabilityTokenOptions, VerifyRevocationEntryOptions, canGrant, mintCapabilityToken, mintRevocationEntry, verifyCapabilityToken, verifyRevocationEntry };