@xyo-network/event-kit-protocol 1.0.1 → 2.1.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/neutral/admissionCore.d.ts +9 -1
- package/dist/neutral/admissionCore.d.ts.map +1 -1
- package/dist/neutral/admitAndEnqueue.d.ts.map +1 -1
- package/dist/neutral/bestEffortAdmission.d.ts +113 -0
- package/dist/neutral/bestEffortAdmission.d.ts.map +1 -0
- package/dist/neutral/handshakeConstruction.d.ts +5 -1
- package/dist/neutral/handshakeConstruction.d.ts.map +1 -1
- package/dist/neutral/index.d.ts +1 -0
- package/dist/neutral/index.d.ts.map +1 -1
- package/dist/neutral/index.mjs +526 -300
- package/dist/neutral/index.mjs.map +4 -4
- package/dist/neutral/publisherSubscription.d.ts.map +1 -1
- package/dist/neutral/types.d.ts +4 -0
- package/dist/neutral/types.d.ts.map +1 -1
- package/dist/neutral/verifiedAdmissionWake.d.ts +3 -1
- package/dist/neutral/verifiedAdmissionWake.d.ts.map +1 -1
- package/dist/neutral/verifyWakeRequest.d.ts +10 -1
- package/dist/neutral/verifyWakeRequest.d.ts.map +1 -1
- package/dist/neutral/wakeSubscriptionEncoding.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/neutral/index.mjs
CHANGED
|
@@ -12,7 +12,7 @@ async function admitVerifiedWake(input) {
|
|
|
12
12
|
if (!isNonnegativeSafeInteger(input.expectedHead)) {
|
|
13
13
|
return reject("grant-view-invalid", ["expectedHead must be a nonnegative safe integer"]);
|
|
14
14
|
}
|
|
15
|
-
const authorization = await
|
|
15
|
+
const authorization = await authorizeVerifiedWake(input);
|
|
16
16
|
return authorization.ok ? await commitAuthorizedWake(input.inbox, attempt, authorization.grant, wake) : authorization.failure;
|
|
17
17
|
}
|
|
18
18
|
async function resolvePriorAttempt(inbox, attempt, wake) {
|
|
@@ -79,7 +79,7 @@ function createAttempt(verified) {
|
|
|
79
79
|
ok: true
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
|
-
async function
|
|
82
|
+
async function authorizeVerifiedWake(input) {
|
|
83
83
|
const { route } = input.verified;
|
|
84
84
|
let decision;
|
|
85
85
|
try {
|
|
@@ -266,6 +266,7 @@ async function parseAdmittedWake(value) {
|
|
|
266
266
|
// src/admitAndEnqueue.ts
|
|
267
267
|
async function admitAndEnqueue(input) {
|
|
268
268
|
const { verified } = input;
|
|
269
|
+
if (verified.route.deliveryMode !== "durable") throw new TypeError("Durable admission requires explicit durable delivery mode");
|
|
269
270
|
const wake = AdmittedWakeZod.parse({
|
|
270
271
|
audience: verified.audience,
|
|
271
272
|
deploymentId: verified.deploymentId,
|
|
@@ -281,6 +282,53 @@ async function admitAndEnqueue(input) {
|
|
|
281
282
|
return await admitVerifiedWake({ ...input, wake });
|
|
282
283
|
}
|
|
283
284
|
|
|
285
|
+
// src/bestEffortAdmission.ts
|
|
286
|
+
import {
|
|
287
|
+
HashZod as HashZod3,
|
|
288
|
+
NonnegativeJsonSafeIntegerZod as NonnegativeJsonSafeIntegerZod3,
|
|
289
|
+
NormalizedAddressZod as NormalizedAddressZod3,
|
|
290
|
+
WakeIdZod as WakeIdZod2,
|
|
291
|
+
WakeJwtAudienceZod as WakeJwtAudienceZod3,
|
|
292
|
+
WakeJwtIdZod as WakeJwtIdZod2
|
|
293
|
+
} from "@xyo-network/event-kit-schemas";
|
|
294
|
+
import { z as z3 } from "zod";
|
|
295
|
+
|
|
296
|
+
// src/verifyWakeRequest.ts
|
|
297
|
+
import {
|
|
298
|
+
ChainIdZod,
|
|
299
|
+
DeploymentIdZod as DeploymentIdZod2,
|
|
300
|
+
HashZod as HashZod2,
|
|
301
|
+
MAX_POSITIONS_PER_SUBSCRIPTION_WAKE,
|
|
302
|
+
NonnegativeJsonSafeIntegerZod as NonnegativeJsonSafeIntegerZod2,
|
|
303
|
+
NormalizedAddressZod as NormalizedAddressZod2,
|
|
304
|
+
PositiveJsonSafeIntegerZod,
|
|
305
|
+
SOURCE_WAKE_FILTER_PROFILE,
|
|
306
|
+
validWakeDeliveryTerms,
|
|
307
|
+
WakeDeliveryTermsFields,
|
|
308
|
+
WakeEnvelopeZod as WakeEnvelopeZod2,
|
|
309
|
+
WakeJwtAudienceZod as WakeJwtAudienceZod2,
|
|
310
|
+
WakeJwtHeaderZod,
|
|
311
|
+
WakeJwtPayloadZod,
|
|
312
|
+
WakeJwtSchema,
|
|
313
|
+
WakeSourceZod as WakeSourceZod2
|
|
314
|
+
} from "@xyo-network/event-kit-schemas";
|
|
315
|
+
import {
|
|
316
|
+
Account,
|
|
317
|
+
PayloadBuilder as PayloadBuilder3
|
|
318
|
+
} from "@xyo-network/sdk";
|
|
319
|
+
import { z as z2 } from "zod";
|
|
320
|
+
|
|
321
|
+
// src/sourceWakeIdentity.ts
|
|
322
|
+
import {
|
|
323
|
+
SourceWakeSubscriptionFilterZod,
|
|
324
|
+
WakeSourceSchema,
|
|
325
|
+
WakeSourceZod
|
|
326
|
+
} from "@xyo-network/event-kit-schemas";
|
|
327
|
+
import {
|
|
328
|
+
asSchema,
|
|
329
|
+
PayloadBuilder as PayloadBuilder2
|
|
330
|
+
} from "@xyo-network/sdk";
|
|
331
|
+
|
|
284
332
|
// src/canonicalJson.ts
|
|
285
333
|
var textEncoder = new TextEncoder();
|
|
286
334
|
function hasUnpairedSurrogate(value) {
|
|
@@ -330,6 +378,379 @@ function canonicalJsonBytes(value) {
|
|
|
330
378
|
return textEncoder.encode(canonicalize(value));
|
|
331
379
|
}
|
|
332
380
|
|
|
381
|
+
// src/sourceWakeIdentity.ts
|
|
382
|
+
async function wakeSourceHash(source) {
|
|
383
|
+
return await PayloadBuilder2.dataHash({ schema: WakeSourceSchema, ...WakeSourceZod.parse(source) });
|
|
384
|
+
}
|
|
385
|
+
function wakeSourcesEqual(left, right) {
|
|
386
|
+
return canonicalJson(WakeSourceZod.parse(left)) === canonicalJson(WakeSourceZod.parse(right));
|
|
387
|
+
}
|
|
388
|
+
async function sourceWakeFilterHash(filter) {
|
|
389
|
+
const parsed = SourceWakeSubscriptionFilterZod.parse(filter);
|
|
390
|
+
return await PayloadBuilder2.dataHash({ ...parsed, schema: asSchema(parsed.schema, true) });
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// src/verifyWakeRequest.ts
|
|
394
|
+
var EVENT_KIT_MAX_JWT_LIFETIME_SECONDS = 300;
|
|
395
|
+
var EVENT_KIT_MAX_FUTURE_IAT_SECONDS = 60;
|
|
396
|
+
var WakeAdmissionRouteZod = z2.strictObject({
|
|
397
|
+
...WakeDeliveryTermsFields,
|
|
398
|
+
audience: WakeJwtAudienceZod2,
|
|
399
|
+
authorizationChainId: ChainIdZod,
|
|
400
|
+
controller: NormalizedAddressZod2,
|
|
401
|
+
deploymentId: DeploymentIdZod2,
|
|
402
|
+
queueId: z2.string().min(1).max(255).optional(),
|
|
403
|
+
source: WakeSourceZod2,
|
|
404
|
+
filterHash: HashZod2,
|
|
405
|
+
subscriptionHash: HashZod2,
|
|
406
|
+
fromPosition: NonnegativeJsonSafeIntegerZod2,
|
|
407
|
+
maxPositionsPerWake: PositiveJsonSafeIntegerZod.max(MAX_POSITIONS_PER_SUBSCRIPTION_WAKE),
|
|
408
|
+
maxGrantLagBlocks: NonnegativeJsonSafeIntegerZod2
|
|
409
|
+
}).refine((route) => validWakeDeliveryTerms(route) && (route.deliveryMode !== "best-effort" || route.maxPositionsPerWake === 1), "Invalid explicit delivery terms");
|
|
410
|
+
function verifyEventBinding(event, route, nowSeconds) {
|
|
411
|
+
if (event.authorizationChainId !== route.authorizationChainId) {
|
|
412
|
+
return { ok: false, reason: "event-authorization-chain-mismatch" };
|
|
413
|
+
}
|
|
414
|
+
if (!wakeSourcesEqual(event.source, route.source)) return { ok: false, reason: "event-source-mismatch" };
|
|
415
|
+
if (event.filterHash !== route.filterHash) return { ok: false, reason: "event-filter-mismatch" };
|
|
416
|
+
if (event.deploymentId !== route.deploymentId || event.queueId !== route.queueId) {
|
|
417
|
+
return { ok: false, reason: "event-target-mismatch" };
|
|
418
|
+
}
|
|
419
|
+
if (event.deliveryMode !== route.deliveryMode) return { ok: false, reason: "event-mode-mismatch" };
|
|
420
|
+
if (route.deliveryMode === "best-effort") {
|
|
421
|
+
if (event.issuedAt > nowSeconds + EVENT_KIT_MAX_FUTURE_IAT_SECONDS) return { ok: false, reason: "hint-future-issued-at" };
|
|
422
|
+
if (route.bestEffort === void 0 || nowSeconds > event.issuedAt + route.bestEffort.maxHintAgeSeconds) return { ok: false, reason: "hint-expired" };
|
|
423
|
+
}
|
|
424
|
+
if (event.fromPosition < route.fromPosition) return { ok: false, reason: "range-before-floor" };
|
|
425
|
+
if (event.toPosition - event.fromPosition >= route.maxPositionsPerWake) return { ok: false, reason: "range-too-large" };
|
|
426
|
+
}
|
|
427
|
+
async function verifyWakeRequest(input) {
|
|
428
|
+
const routeResult = WakeAdmissionRouteZod.safeParse(input.route);
|
|
429
|
+
if (!routeResult.success || !NonnegativeJsonSafeIntegerZod2.safeParse(input.nowSeconds).success) {
|
|
430
|
+
return { ok: false, reason: "configuration-invalid" };
|
|
431
|
+
}
|
|
432
|
+
const eventResult = WakeEnvelopeZod2.safeParse(input.body);
|
|
433
|
+
if (!eventResult.success) return malformedWake(input.body, eventResult.error.issues);
|
|
434
|
+
const route = routeResult.data;
|
|
435
|
+
if (route.filterHash !== await sourceWakeFilterHash({
|
|
436
|
+
schema: SOURCE_WAKE_FILTER_PROFILE,
|
|
437
|
+
deliveryMode: route.deliveryMode,
|
|
438
|
+
...route.bestEffort === void 0 ? {} : { bestEffort: route.bestEffort },
|
|
439
|
+
source: route.source,
|
|
440
|
+
fromPosition: route.fromPosition,
|
|
441
|
+
maxPositionsPerWake: route.maxPositionsPerWake
|
|
442
|
+
})) return { ok: false, reason: "configuration-invalid" };
|
|
443
|
+
const event = Object.freeze({ ...eventResult.data, source: Object.freeze(eventResult.data.source) });
|
|
444
|
+
const mismatch = verifyEventBinding(event, route, input.nowSeconds);
|
|
445
|
+
if (mismatch !== void 0) return mismatch;
|
|
446
|
+
const jwt = await verifyJwtProfile(input.jwt, route.audience, input.nowSeconds);
|
|
447
|
+
if (!jwt.ok) return jwt;
|
|
448
|
+
let eventHash;
|
|
449
|
+
try {
|
|
450
|
+
eventHash = await PayloadBuilder3.dataHash(event);
|
|
451
|
+
} catch {
|
|
452
|
+
return { ok: false, reason: "event-hash-failed" };
|
|
453
|
+
}
|
|
454
|
+
if (eventHash !== jwt.jwt.eventHash) return { ok: false, reason: "event-hash-mismatch" };
|
|
455
|
+
const sourceHash = await wakeSourceHash(route.source);
|
|
456
|
+
return Object.freeze({
|
|
457
|
+
ok: true,
|
|
458
|
+
verified: Object.freeze({
|
|
459
|
+
audience: route.audience,
|
|
460
|
+
deploymentId: event.deploymentId,
|
|
461
|
+
event,
|
|
462
|
+
eventHash,
|
|
463
|
+
expiresAt: jwt.jwt.expiresAt,
|
|
464
|
+
fromPosition: event.fromPosition,
|
|
465
|
+
issuedAt: event.issuedAt,
|
|
466
|
+
jwtId: jwt.jwt.jwtId,
|
|
467
|
+
publisher: jwt.jwt.publisher,
|
|
468
|
+
...event.queueId === void 0 ? {} : { queueId: event.queueId },
|
|
469
|
+
route: Object.freeze({
|
|
470
|
+
...route,
|
|
471
|
+
source: Object.freeze(route.source),
|
|
472
|
+
sourceHash,
|
|
473
|
+
...route.bestEffort === void 0 ? {} : { bestEffort: Object.freeze(route.bestEffort) }
|
|
474
|
+
}),
|
|
475
|
+
toPosition: event.toPosition,
|
|
476
|
+
tokenIssuedAt: jwt.jwt.issuedAt,
|
|
477
|
+
wakeId: event.wakeId
|
|
478
|
+
})
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
async function verifyJwtProfile(token, audience, nowSeconds) {
|
|
482
|
+
let verified;
|
|
483
|
+
try {
|
|
484
|
+
verified = await Account.verifyJwt(token, { audience, now: nowSeconds });
|
|
485
|
+
} catch (error) {
|
|
486
|
+
return reject2("jwt-malformed", [errorMessage2(error)]);
|
|
487
|
+
}
|
|
488
|
+
if (!verified.ok) {
|
|
489
|
+
return Object.hasOwn(verified.payload ?? {}, "nbf") ? reject2("jwt-nbf-not-allowed") : reject2(classifySdkJwtFailure(verified.reasons), verified.reasons);
|
|
490
|
+
}
|
|
491
|
+
const wireResult = parseStrictJwtWire(verified.header, verified.payload, audience);
|
|
492
|
+
if (!wireResult.ok) return wireResult;
|
|
493
|
+
const { header: strictHeader, payload: strictPayload } = wireResult;
|
|
494
|
+
if (strictHeader.kid !== strictPayload.iss) return reject2("jwt-identity-invalid");
|
|
495
|
+
if (strictPayload.exp <= nowSeconds) return reject2("jwt-expired");
|
|
496
|
+
if (strictPayload.exp - strictPayload.iat > EVENT_KIT_MAX_JWT_LIFETIME_SECONDS) return reject2("jwt-lifetime-invalid");
|
|
497
|
+
if (strictPayload.iat - nowSeconds > EVENT_KIT_MAX_FUTURE_IAT_SECONDS) return reject2("jwt-future-iat");
|
|
498
|
+
const jwt = Object.freeze({
|
|
499
|
+
eventHash: strictPayload.eventHash,
|
|
500
|
+
expiresAt: strictPayload.exp,
|
|
501
|
+
issuedAt: strictPayload.iat,
|
|
502
|
+
jwtId: strictPayload.jti,
|
|
503
|
+
publisher: strictPayload.iss
|
|
504
|
+
});
|
|
505
|
+
return Object.freeze({ jwt, ok: true });
|
|
506
|
+
}
|
|
507
|
+
function parseStrictJwtWire(header, payload, audience) {
|
|
508
|
+
const headerResult = WakeJwtHeaderZod.safeParse(header);
|
|
509
|
+
if (!headerResult.success) {
|
|
510
|
+
return NormalizedAddressZod2.safeParse(header.kid).success ? reject2("jwt-header-invalid", zodDetails(headerResult.error.issues)) : reject2("jwt-identity-invalid");
|
|
511
|
+
}
|
|
512
|
+
if (Object.hasOwn(payload, "nbf")) return reject2("jwt-nbf-not-allowed");
|
|
513
|
+
if (!NormalizedAddressZod2.safeParse(payload.iss).success) return reject2("jwt-identity-invalid");
|
|
514
|
+
if (payload.schema !== WakeJwtSchema) return reject2("jwt-schema-mismatch");
|
|
515
|
+
if (payload.aud !== audience) return reject2("jwt-audience-mismatch");
|
|
516
|
+
if (!isNonnegativeSafeInteger2(payload.iat) || !isNonnegativeSafeInteger2(payload.exp) || payload.exp <= payload.iat) {
|
|
517
|
+
return reject2("jwt-lifetime-invalid");
|
|
518
|
+
}
|
|
519
|
+
const payloadResult = WakeJwtPayloadZod.safeParse(payload);
|
|
520
|
+
if (!payloadResult.success) return reject2("jwt-claims-invalid", zodDetails(payloadResult.error.issues));
|
|
521
|
+
return Object.freeze({
|
|
522
|
+
header: headerResult.data,
|
|
523
|
+
ok: true,
|
|
524
|
+
payload: payloadResult.data
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
function classifySdkJwtFailure(reasons) {
|
|
528
|
+
if (reasons.some((reason) => reason.startsWith("audience mismatch:"))) return "jwt-audience-mismatch";
|
|
529
|
+
if (reasons.some((reason) => reason.startsWith("expired:"))) return "jwt-expired";
|
|
530
|
+
if (reasons.some((reason) => reason.startsWith("unsupported alg:") || reason.startsWith("unsupported typ:"))) {
|
|
531
|
+
return "jwt-header-invalid";
|
|
532
|
+
}
|
|
533
|
+
return reasons.some((reason) => reason.startsWith("invalid public key:") || reason.startsWith("public key does not derive to kid:") || reason === "header.kid does not match payload.iss") ? "jwt-identity-invalid" : "jwt-invalid";
|
|
534
|
+
}
|
|
535
|
+
function malformedWake(body, issues) {
|
|
536
|
+
const details = zodDetails(issues);
|
|
537
|
+
if (hasInvertedSafeRange(body)) return reject2("range-invalid", details);
|
|
538
|
+
return reject2(isRecord2(body) && Object.hasOwn(body, "wakeId") && issues.some((issue) => issue.path[0] === "wakeId") ? "wake-id-invalid" : "event-malformed", details);
|
|
539
|
+
}
|
|
540
|
+
function hasInvertedSafeRange(value) {
|
|
541
|
+
if (!isRecord2(value)) return false;
|
|
542
|
+
const { fromPosition, toPosition } = value;
|
|
543
|
+
return typeof fromPosition === "number" && typeof toPosition === "number" && isNonnegativeSafeInteger2(fromPosition) && isNonnegativeSafeInteger2(toPosition) && fromPosition > toPosition;
|
|
544
|
+
}
|
|
545
|
+
function isNonnegativeSafeInteger2(value) {
|
|
546
|
+
return Number.isSafeInteger(value) && value >= 0;
|
|
547
|
+
}
|
|
548
|
+
function isRecord2(value) {
|
|
549
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
550
|
+
}
|
|
551
|
+
function zodDetails(issues) {
|
|
552
|
+
return issues.map((issue) => {
|
|
553
|
+
const path = issue.path.map(String).join(".") || "<root>";
|
|
554
|
+
return `${path}: ${issue.message}`;
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
function reject2(reason, details) {
|
|
558
|
+
if (details === void 0) return Object.freeze({ ok: false, reason });
|
|
559
|
+
return Object.freeze({
|
|
560
|
+
details: Object.freeze([...details]),
|
|
561
|
+
ok: false,
|
|
562
|
+
reason
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
function errorMessage2(error) {
|
|
566
|
+
return error instanceof Error ? error.message : String(error);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// src/bestEffortAdmission.ts
|
|
570
|
+
function hintRetention(input) {
|
|
571
|
+
const { verified, nowSeconds } = input;
|
|
572
|
+
const policy = verified.route.bestEffort;
|
|
573
|
+
if (verified.route.deliveryMode !== "best-effort" || verified.event.deliveryMode !== "best-effort" || policy === void 0 || verified.fromPosition !== verified.toPosition || !Number.isSafeInteger(nowSeconds) || nowSeconds < 0 || !Number.isSafeInteger(input.expectedHead) || input.expectedHead < 0)
|
|
574
|
+
return { ok: false, reason: "configuration-invalid" };
|
|
575
|
+
if (verified.issuedAt > nowSeconds + EVENT_KIT_MAX_FUTURE_IAT_SECONDS) return { ok: false, reason: "hint-future-issued-at" };
|
|
576
|
+
const bodyDeadline = verified.issuedAt + policy.maxHintAgeSeconds;
|
|
577
|
+
const retainWakeUntil = bodyDeadline + EVENT_KIT_REPLAY_RETENTION_SKEW_SECONDS;
|
|
578
|
+
const retainJwtUntil = verified.expiresAt + EVENT_KIT_REPLAY_RETENTION_SKEW_SECONDS;
|
|
579
|
+
if (!Number.isSafeInteger(retainWakeUntil) || !Number.isSafeInteger(retainJwtUntil)) return { ok: false, reason: "configuration-invalid" };
|
|
580
|
+
return nowSeconds > bodyDeadline ? { ok: false, reason: "hint-expired" } : {
|
|
581
|
+
ok: true,
|
|
582
|
+
retainJwtUntil,
|
|
583
|
+
retainWakeUntil
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
async function admitBestEffortHint(input) {
|
|
587
|
+
const retention = hintRetention(input);
|
|
588
|
+
if (!retention.ok) return retention;
|
|
589
|
+
const { retainJwtUntil, retainWakeUntil } = retention;
|
|
590
|
+
const { verified, nowSeconds } = input;
|
|
591
|
+
const authorization = await authorizeVerifiedWake(input);
|
|
592
|
+
if (!authorization.ok) return authorization.failure;
|
|
593
|
+
const wake = AdmittedWakeZod.parse({
|
|
594
|
+
audience: verified.audience,
|
|
595
|
+
deploymentId: verified.deploymentId,
|
|
596
|
+
event: verified.event,
|
|
597
|
+
eventHash: verified.eventHash,
|
|
598
|
+
fromPosition: verified.fromPosition,
|
|
599
|
+
toPosition: verified.toPosition,
|
|
600
|
+
jti: verified.jwtId,
|
|
601
|
+
publisher: verified.publisher,
|
|
602
|
+
wakeId: verified.wakeId,
|
|
603
|
+
...verified.queueId === void 0 ? {} : { queueId: verified.queueId }
|
|
604
|
+
});
|
|
605
|
+
const attempt = {
|
|
606
|
+
audience: verified.audience,
|
|
607
|
+
eventHash: verified.eventHash,
|
|
608
|
+
jwtId: verified.jwtId,
|
|
609
|
+
publisher: verified.publisher,
|
|
610
|
+
retainJwtUntil,
|
|
611
|
+
retainWakeUntil,
|
|
612
|
+
wakeId: verified.wakeId
|
|
613
|
+
};
|
|
614
|
+
try {
|
|
615
|
+
const committed = await input.inbox.commitAuthorized({
|
|
616
|
+
attempt,
|
|
617
|
+
grant: authorization.grant,
|
|
618
|
+
wake,
|
|
619
|
+
nowSeconds
|
|
620
|
+
});
|
|
621
|
+
if (committed.kind !== "accepted" && committed.kind !== "duplicate") return { ok: false, reason: committed.kind };
|
|
622
|
+
const acknowledgment = committed.acknowledgment;
|
|
623
|
+
return acknowledgmentMatches(acknowledgment, attempt, nowSeconds) ? {
|
|
624
|
+
ok: true,
|
|
625
|
+
disposition: committed.kind,
|
|
626
|
+
acknowledgment,
|
|
627
|
+
wake
|
|
628
|
+
} : { ok: false, reason: "inbox-unavailable" };
|
|
629
|
+
} catch {
|
|
630
|
+
return { ok: false, reason: "inbox-unavailable" };
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
function acknowledgmentMatches(value, attempt, now) {
|
|
634
|
+
return BestEffortAdmissionAcknowledgmentZod.safeParse(value).success && value.eventHash === attempt.eventHash && value.wakeId === attempt.wakeId && value.acceptedAt <= now && value.expiresAt === attempt.retainWakeUntil;
|
|
635
|
+
}
|
|
636
|
+
var BestEffortAdmissionAcknowledgmentZod = z3.strictObject({
|
|
637
|
+
admissionId: z3.string().min(1).max(4096),
|
|
638
|
+
eventHash: HashZod3,
|
|
639
|
+
wakeId: WakeIdZod2,
|
|
640
|
+
acceptedAt: NonnegativeJsonSafeIntegerZod3,
|
|
641
|
+
expiresAt: NonnegativeJsonSafeIntegerZod3
|
|
642
|
+
}).refine((value) => value.expiresAt > value.acceptedAt, "Acknowledgment expiry must follow admission");
|
|
643
|
+
var JwtSecurityRecordZod = z3.strictObject({
|
|
644
|
+
audience: WakeJwtAudienceZod3,
|
|
645
|
+
publisher: NormalizedAddressZod3,
|
|
646
|
+
jwtId: WakeJwtIdZod2,
|
|
647
|
+
wakeId: WakeIdZod2,
|
|
648
|
+
eventHash: HashZod3,
|
|
649
|
+
expiresAt: NonnegativeJsonSafeIntegerZod3
|
|
650
|
+
});
|
|
651
|
+
var WakeSecurityRecordZod = z3.strictObject({
|
|
652
|
+
audience: WakeJwtAudienceZod3,
|
|
653
|
+
publisher: NormalizedAddressZod3,
|
|
654
|
+
wakeId: WakeIdZod2,
|
|
655
|
+
eventHash: HashZod3,
|
|
656
|
+
acknowledgment: BestEffortAdmissionAcknowledgmentZod
|
|
657
|
+
}).refine(
|
|
658
|
+
(value) => value.wakeId === value.acknowledgment.wakeId && value.eventHash === value.acknowledgment.eventHash,
|
|
659
|
+
"Security identity must match acknowledgment"
|
|
660
|
+
);
|
|
661
|
+
var BestEffortSecurityStateZod = z3.strictObject({
|
|
662
|
+
highestNowSeconds: NonnegativeJsonSafeIntegerZod3,
|
|
663
|
+
jwtRecords: z3.array(JwtSecurityRecordZod).max(65536),
|
|
664
|
+
wakeRecords: z3.array(WakeSecurityRecordZod).max(65536)
|
|
665
|
+
}).superRefine((value, context) => {
|
|
666
|
+
const jwtKeys = value.jwtRecords.map((record) => JSON.stringify([record.audience, record.publisher, record.jwtId]));
|
|
667
|
+
if (value.wakeRecords.some((record) => record.acknowledgment.acceptedAt > value.highestNowSeconds))
|
|
668
|
+
context.addIssue({ code: "custom", message: "Acknowledgment cannot precede stored admission clock" });
|
|
669
|
+
const wakeKeys = value.wakeRecords.map((record) => JSON.stringify([record.audience, record.publisher, record.wakeId]));
|
|
670
|
+
const jwtIdentities = new Set(jwtKeys);
|
|
671
|
+
const wakeIdentities = new Set(wakeKeys);
|
|
672
|
+
if (jwtIdentities.size !== jwtKeys.length || wakeIdentities.size !== wakeKeys.length)
|
|
673
|
+
context.addIssue({ code: "custom", message: "Persisted security identities must be unique" });
|
|
674
|
+
});
|
|
675
|
+
function parseBestEffortSecurityState(value) {
|
|
676
|
+
return BestEffortSecurityStateZod.parse(value);
|
|
677
|
+
}
|
|
678
|
+
var BestEffortAdmissionAttemptZod = z3.strictObject({
|
|
679
|
+
audience: WakeJwtAudienceZod3,
|
|
680
|
+
eventHash: HashZod3,
|
|
681
|
+
jwtId: WakeJwtIdZod2,
|
|
682
|
+
publisher: NormalizedAddressZod3,
|
|
683
|
+
retainJwtUntil: NonnegativeJsonSafeIntegerZod3,
|
|
684
|
+
retainWakeUntil: NonnegativeJsonSafeIntegerZod3,
|
|
685
|
+
wakeId: WakeIdZod2
|
|
686
|
+
});
|
|
687
|
+
function createBestEffortSecurityState() {
|
|
688
|
+
return {
|
|
689
|
+
highestNowSeconds: 0,
|
|
690
|
+
jwtRecords: [],
|
|
691
|
+
wakeRecords: []
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
function securityConflict(attempt, priorJwt, priorWake, jwtCount, wakeCount, maxRecords) {
|
|
695
|
+
if (priorJwt !== void 0 && (priorJwt.eventHash !== attempt.eventHash || priorJwt.wakeId !== attempt.wakeId))
|
|
696
|
+
return "jwt-id-collision";
|
|
697
|
+
if (priorWake !== void 0 && priorWake.eventHash !== attempt.eventHash)
|
|
698
|
+
return "wake-id-collision";
|
|
699
|
+
if (priorJwt === void 0 && jwtCount >= maxRecords || priorWake === void 0 && wakeCount >= maxRecords)
|
|
700
|
+
return "capacity-exceeded";
|
|
701
|
+
}
|
|
702
|
+
function validateTransaction(input, maxRecords) {
|
|
703
|
+
const attempt = BestEffortAdmissionAttemptZod.parse(input.attempt);
|
|
704
|
+
const wake = AdmittedWakeZod.parse(input.wake);
|
|
705
|
+
const nowSeconds = NonnegativeJsonSafeIntegerZod3.parse(input.nowSeconds);
|
|
706
|
+
if (attempt.audience !== wake.audience || attempt.publisher !== wake.publisher || attempt.jwtId !== wake.jti || attempt.wakeId !== wake.wakeId || attempt.eventHash !== wake.eventHash || wake.event.deliveryMode !== "best-effort" || attempt.retainJwtUntil <= nowSeconds || attempt.retainWakeUntil <= nowSeconds)
|
|
707
|
+
throw new TypeError("Best Effort transaction attempt does not match admitted hint");
|
|
708
|
+
if (!Number.isSafeInteger(maxRecords) || maxRecords < 1 || maxRecords > 65536) throw new TypeError("Invalid finite security capacity");
|
|
709
|
+
return { attempt, nowSeconds };
|
|
710
|
+
}
|
|
711
|
+
function commitBestEffortSecurity(state, input, maxRecords) {
|
|
712
|
+
state = parseBestEffortSecurityState(state);
|
|
713
|
+
const { attempt, nowSeconds } = validateTransaction(input, maxRecords);
|
|
714
|
+
if (nowSeconds < state.highestNowSeconds) return { state, result: { kind: "clock-regressed" } };
|
|
715
|
+
const jwtRecords = state.jwtRecords.filter((record) => record.expiresAt > nowSeconds).map((record) => ({ ...record }));
|
|
716
|
+
const wakeRecords = state.wakeRecords.filter((record) => record.acknowledgment.expiresAt > nowSeconds);
|
|
717
|
+
const priorJwt = jwtRecords.find((record) => record.audience === attempt.audience && record.publisher === attempt.publisher && record.jwtId === attempt.jwtId);
|
|
718
|
+
const priorWake = wakeRecords.find((record) => record.audience === attempt.audience && record.publisher === attempt.publisher && record.wakeId === attempt.wakeId);
|
|
719
|
+
const conflict = securityConflict(attempt, priorJwt, priorWake, jwtRecords.length, wakeRecords.length, maxRecords);
|
|
720
|
+
if (conflict !== void 0) return { state, result: { kind: conflict } };
|
|
721
|
+
const acknowledgment = priorWake?.acknowledgment ?? {
|
|
722
|
+
admissionId: JSON.stringify([attempt.audience, attempt.publisher, attempt.wakeId, attempt.eventHash]),
|
|
723
|
+
eventHash: attempt.eventHash,
|
|
724
|
+
wakeId: attempt.wakeId,
|
|
725
|
+
acceptedAt: nowSeconds,
|
|
726
|
+
expiresAt: attempt.retainWakeUntil
|
|
727
|
+
};
|
|
728
|
+
if (priorJwt !== void 0) priorJwt.expiresAt = Math.max(priorJwt.expiresAt, attempt.retainJwtUntil);
|
|
729
|
+
if (priorJwt === void 0) jwtRecords.push({
|
|
730
|
+
audience: attempt.audience,
|
|
731
|
+
publisher: attempt.publisher,
|
|
732
|
+
jwtId: attempt.jwtId,
|
|
733
|
+
wakeId: attempt.wakeId,
|
|
734
|
+
eventHash: attempt.eventHash,
|
|
735
|
+
expiresAt: attempt.retainJwtUntil
|
|
736
|
+
});
|
|
737
|
+
if (priorWake === void 0) wakeRecords.push({
|
|
738
|
+
audience: attempt.audience,
|
|
739
|
+
publisher: attempt.publisher,
|
|
740
|
+
wakeId: attempt.wakeId,
|
|
741
|
+
eventHash: attempt.eventHash,
|
|
742
|
+
acknowledgment
|
|
743
|
+
});
|
|
744
|
+
return {
|
|
745
|
+
state: {
|
|
746
|
+
highestNowSeconds: nowSeconds,
|
|
747
|
+
jwtRecords,
|
|
748
|
+
wakeRecords
|
|
749
|
+
},
|
|
750
|
+
result: { kind: priorWake === void 0 ? "accepted" : "duplicate", acknowledgment }
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
|
|
333
754
|
// src/endpointChallenge.ts
|
|
334
755
|
import {
|
|
335
756
|
Base64Url32BytesZod,
|
|
@@ -343,7 +764,7 @@ import {
|
|
|
343
764
|
generateHandshakeId,
|
|
344
765
|
MAX_ENDPOINT_CHALLENGE_LIFETIME_SECONDS
|
|
345
766
|
} from "@xyo-network/event-kit-schemas";
|
|
346
|
-
import { PayloadBuilder as
|
|
767
|
+
import { PayloadBuilder as PayloadBuilder4 } from "@xyo-network/sdk";
|
|
347
768
|
function hashBytes(hash) {
|
|
348
769
|
return Uint8Array.from({ length: 32 }, (_, index) => Number.parseInt(hash.slice(index * 2, index * 2 + 2), 16));
|
|
349
770
|
}
|
|
@@ -370,7 +791,7 @@ function equalProof(left, right) {
|
|
|
370
791
|
}
|
|
371
792
|
async function endpointChallengeHash(challenge) {
|
|
372
793
|
const parsed = EndpointChallengeZod.parse(challenge);
|
|
373
|
-
return await
|
|
794
|
+
return await PayloadBuilder4.dataHash(parsed);
|
|
374
795
|
}
|
|
375
796
|
function createEndpointChallenge(input) {
|
|
376
797
|
const lifetimeSeconds = input.lifetimeSeconds ?? MAX_ENDPOINT_CHALLENGE_LIFETIME_SECONDS;
|
|
@@ -383,7 +804,7 @@ function createEndpointChallenge(input) {
|
|
|
383
804
|
if (input.issuedAt > Number.MAX_SAFE_INTEGER - lifetimeSeconds) {
|
|
384
805
|
throw new RangeError("Challenge expiry exceeds the JSON safe-integer range");
|
|
385
806
|
}
|
|
386
|
-
const builder = new
|
|
807
|
+
const builder = new PayloadBuilder4({ schema: EndpointChallengeSchema });
|
|
387
808
|
return EndpointChallengeZod.parse(builder.fields({
|
|
388
809
|
challengeId: generateHandshakeId(),
|
|
389
810
|
chainId: input.chainId,
|
|
@@ -402,7 +823,7 @@ function createEndpointChallenge(input) {
|
|
|
402
823
|
}
|
|
403
824
|
async function createEndpointChallengeResponse(challenge, secret) {
|
|
404
825
|
const challengeHash = await endpointChallengeHash(challenge);
|
|
405
|
-
const builder = new
|
|
826
|
+
const builder = new PayloadBuilder4({ schema: EndpointChallengeResponseSchema });
|
|
406
827
|
return EndpointChallengeResponseZod.parse(builder.fields({
|
|
407
828
|
challenge: challengeHash,
|
|
408
829
|
proof: await hmacProof(secret, challengeHash)
|
|
@@ -437,7 +858,7 @@ import {
|
|
|
437
858
|
EndpointChallengeZod as EndpointChallengeZod2,
|
|
438
859
|
generateGrantId,
|
|
439
860
|
generateHandshakeId as generateHandshakeId2,
|
|
440
|
-
HashZod as
|
|
861
|
+
HashZod as HashZod5,
|
|
441
862
|
MAX_SUBSCRIPTION_PLAINTEXT_BYTES as MAX_SUBSCRIPTION_PLAINTEXT_BYTES2,
|
|
442
863
|
PublisherEncryptionKeyRevocationSchema,
|
|
443
864
|
PublisherEncryptionKeyRevocationZod,
|
|
@@ -448,7 +869,7 @@ import {
|
|
|
448
869
|
PublisherServiceSchema,
|
|
449
870
|
PublisherServiceZod,
|
|
450
871
|
SOURCE_WAKE_EVENT_KIND,
|
|
451
|
-
SOURCE_WAKE_FILTER_PROFILE,
|
|
872
|
+
SOURCE_WAKE_FILTER_PROFILE as SOURCE_WAKE_FILTER_PROFILE2,
|
|
452
873
|
WAKE_SUBSCRIPTION_HPKE_PROFILE as WAKE_SUBSCRIPTION_HPKE_PROFILE3,
|
|
453
874
|
WAKE_WALLET_JWT_DELIVERY_PROFILE,
|
|
454
875
|
WakeGrantObjectSchema,
|
|
@@ -465,7 +886,7 @@ import {
|
|
|
465
886
|
WakeSubscriptionRevocationSchema,
|
|
466
887
|
WakeSubscriptionRevocationZod
|
|
467
888
|
} from "@xyo-network/event-kit-schemas";
|
|
468
|
-
import { PayloadBuilder as
|
|
889
|
+
import { PayloadBuilder as PayloadBuilder5 } from "@xyo-network/sdk";
|
|
469
890
|
|
|
470
891
|
// src/wakeSubscriptionHpke.ts
|
|
471
892
|
import {
|
|
@@ -480,8 +901,8 @@ import {
|
|
|
480
901
|
canonicalBase64UrlToBytes as canonicalBase64UrlToBytes2,
|
|
481
902
|
EncryptedWakeSubscriptionZod as EncryptedWakeSubscriptionZod2,
|
|
482
903
|
HandshakeIdZod,
|
|
483
|
-
HashZod as
|
|
484
|
-
NormalizedAddressZod as
|
|
904
|
+
HashZod as HashZod4,
|
|
905
|
+
NormalizedAddressZod as NormalizedAddressZod5,
|
|
485
906
|
WAKE_SUBSCRIPTION_HPKE_PROFILE as WAKE_SUBSCRIPTION_HPKE_PROFILE2,
|
|
486
907
|
WakeSubscriptionCleartextZod as WakeSubscriptionCleartextZod2
|
|
487
908
|
} from "@xyo-network/event-kit-schemas";
|
|
@@ -490,7 +911,7 @@ import {
|
|
|
490
911
|
import {
|
|
491
912
|
EncryptedWakeSubscriptionZod,
|
|
492
913
|
MAX_SUBSCRIPTION_PLAINTEXT_BYTES,
|
|
493
|
-
NormalizedAddressZod as
|
|
914
|
+
NormalizedAddressZod as NormalizedAddressZod4,
|
|
494
915
|
WAKE_SUBSCRIPTION_HPKE_PROFILE,
|
|
495
916
|
WakeSubscriptionCleartextZod,
|
|
496
917
|
WakeSubscriptionPlaintextZod
|
|
@@ -507,7 +928,7 @@ function cleartextFromEncrypted(subscription) {
|
|
|
507
928
|
return cleartext;
|
|
508
929
|
}
|
|
509
930
|
function wakeSubscriptionAadBytes(subscription, subscriber) {
|
|
510
|
-
const parsedSubscriber =
|
|
931
|
+
const parsedSubscriber = NormalizedAddressZod4.parse(subscriber);
|
|
511
932
|
const parsedSubscription = "encryption" in subscription ? cleartextFromEncrypted(EncryptedWakeSubscriptionZod.parse(subscription)) : WakeSubscriptionCleartextZod.parse(subscription);
|
|
512
933
|
return canonicalJsonBytes({
|
|
513
934
|
schema: parsedSubscription.schema,
|
|
@@ -522,6 +943,7 @@ function wakeSubscriptionAadBytes(subscription, subscriber) {
|
|
|
522
943
|
revision: parsedSubscription.revision,
|
|
523
944
|
supersedes: parsedSubscription.supersedes ?? null,
|
|
524
945
|
eventKind: parsedSubscription.eventKind,
|
|
946
|
+
deliveryMode: parsedSubscription.deliveryMode,
|
|
525
947
|
expiresAtBlock: parsedSubscription.expiresAtBlock,
|
|
526
948
|
profile: WAKE_SUBSCRIPTION_HPKE_PROFILE
|
|
527
949
|
});
|
|
@@ -571,14 +993,14 @@ var WakeSubscriptionDecryptionError = class extends Error {
|
|
|
571
993
|
};
|
|
572
994
|
function parsePublicKey(value) {
|
|
573
995
|
return {
|
|
574
|
-
encryptionKey:
|
|
996
|
+
encryptionKey: HashZod4.parse(value.encryptionKey),
|
|
575
997
|
keyId: HandshakeIdZod.parse(value.keyId),
|
|
576
998
|
publicKey: Base64Url32BytesZod2.parse(value.publicKey)
|
|
577
999
|
};
|
|
578
1000
|
}
|
|
579
1001
|
function parsePrivateKey(value) {
|
|
580
1002
|
return {
|
|
581
|
-
encryptionKey:
|
|
1003
|
+
encryptionKey: HashZod4.parse(value.encryptionKey),
|
|
582
1004
|
keyId: HandshakeIdZod.parse(value.keyId),
|
|
583
1005
|
privateKey: Base64Url32BytesZod2.parse(value.privateKey)
|
|
584
1006
|
};
|
|
@@ -602,7 +1024,7 @@ async function generateWakeSubscriptionEncryptionKeyPair() {
|
|
|
602
1024
|
}
|
|
603
1025
|
async function encryptWakeSubscription(input) {
|
|
604
1026
|
const subscription = WakeSubscriptionCleartextZod2.parse(input.subscription);
|
|
605
|
-
const subscriber =
|
|
1027
|
+
const subscriber = NormalizedAddressZod5.parse(input.subscriber);
|
|
606
1028
|
const publisherKey = parsePublicKey(input.publisherKey);
|
|
607
1029
|
if (subscription.encryptionKey !== publisherKey.encryptionKey || subscription.keyId !== publisherKey.keyId) {
|
|
608
1030
|
throw new TypeError("Subscription key reference does not match the supplied publisher key");
|
|
@@ -625,7 +1047,7 @@ async function encryptWakeSubscription(input) {
|
|
|
625
1047
|
async function decryptWakeSubscription(input) {
|
|
626
1048
|
try {
|
|
627
1049
|
const subscription = EncryptedWakeSubscriptionZod2.parse(input.subscription);
|
|
628
|
-
const subscriber =
|
|
1050
|
+
const subscriber = NormalizedAddressZod5.parse(input.subscriber);
|
|
629
1051
|
const publisherKey = parsePrivateKey(input.publisherKey);
|
|
630
1052
|
if (subscription.encryptionKey !== publisherKey.encryptionKey || subscription.keyId !== publisherKey.keyId) throw new TypeError("Key reference mismatch");
|
|
631
1053
|
const recipientKey = await suite.kem.deserializePrivateKey(decoded32(publisherKey.privateKey));
|
|
@@ -668,13 +1090,13 @@ function parseHandshakePublicObject(value) {
|
|
|
668
1090
|
throw new TypeError("Expected a strict Event Kit handshake public object");
|
|
669
1091
|
}
|
|
670
1092
|
async function handshakeObjectHash(object) {
|
|
671
|
-
return
|
|
1093
|
+
return HashZod5.parse(await PayloadBuilder5.dataHash(parseHandshakePublicObject(object)));
|
|
672
1094
|
}
|
|
673
1095
|
async function withHandshakeObjectHash(body) {
|
|
674
1096
|
return Object.freeze({ body, objectHash: await handshakeObjectHash(body) });
|
|
675
1097
|
}
|
|
676
1098
|
function createPublisherEncryptionKey(input) {
|
|
677
|
-
const builder = new
|
|
1099
|
+
const builder = new PayloadBuilder5({ schema: PublisherEncryptionKeySchema });
|
|
678
1100
|
return PublisherEncryptionKeyZod.parse(builder.fields({
|
|
679
1101
|
keyId: input.keyId ?? generateHandshakeId2(),
|
|
680
1102
|
profile: WAKE_SUBSCRIPTION_HPKE_PROFILE3,
|
|
@@ -706,7 +1128,7 @@ async function generatePublisherEncryptionKey(input) {
|
|
|
706
1128
|
});
|
|
707
1129
|
}
|
|
708
1130
|
function createPublisherService(input) {
|
|
709
|
-
const builder = new
|
|
1131
|
+
const builder = new PayloadBuilder5({ schema: PublisherServiceSchema });
|
|
710
1132
|
return PublisherServiceZod.parse(builder.fields({
|
|
711
1133
|
authorizationChainId: input.authorizationChainId,
|
|
712
1134
|
serviceId: input.serviceId ?? generateHandshakeId2(),
|
|
@@ -716,9 +1138,11 @@ function createPublisherService(input) {
|
|
|
716
1138
|
...input.expiresAtBlock === void 0 ? {} : { expiresAtBlock: input.expiresAtBlock },
|
|
717
1139
|
events: input.sources.map((source) => ({
|
|
718
1140
|
kind: SOURCE_WAKE_EVENT_KIND,
|
|
719
|
-
filterProfile:
|
|
1141
|
+
filterProfile: SOURCE_WAKE_FILTER_PROFILE2,
|
|
720
1142
|
source
|
|
721
1143
|
})),
|
|
1144
|
+
deliveryMode: input.deliveryMode,
|
|
1145
|
+
...input.bestEffort === void 0 ? {} : { bestEffort: input.bestEffort },
|
|
722
1146
|
deliveryProfile: WAKE_WALLET_JWT_DELIVERY_PROFILE,
|
|
723
1147
|
challengeProfile: ENDPOINT_CHALLENGE_HMAC_SHA256_PROFILE,
|
|
724
1148
|
encryptionProfile: WAKE_SUBSCRIPTION_HPKE_PROFILE3,
|
|
@@ -733,7 +1157,7 @@ function createPublisherService(input) {
|
|
|
733
1157
|
}).build());
|
|
734
1158
|
}
|
|
735
1159
|
function createWakeGrant(input) {
|
|
736
|
-
const builder = new
|
|
1160
|
+
const builder = new PayloadBuilder5({ schema: WakeGrantObjectSchema });
|
|
737
1161
|
return WakeGrantObjectZod.parse(builder.fields({
|
|
738
1162
|
grantId: input.grantId ?? generateGrantId(),
|
|
739
1163
|
deploymentId: input.deploymentId,
|
|
@@ -748,14 +1172,16 @@ function createWakeSubscriptionPlaintext(input) {
|
|
|
748
1172
|
globalThis.crypto.getRandomValues(secretBytes);
|
|
749
1173
|
}
|
|
750
1174
|
const challengeSecret = input.challengeSecret ?? bytesToCanonicalBase64Url3(secretBytes);
|
|
751
|
-
const builder = new
|
|
1175
|
+
const builder = new PayloadBuilder5({ schema: WakeSubscriptionPlaintextSchema });
|
|
752
1176
|
return WakeSubscriptionPlaintextZod2.parse(builder.fields({
|
|
753
1177
|
endpoint: input.endpoint,
|
|
754
1178
|
audience: input.audience,
|
|
755
1179
|
deploymentId: input.deploymentId,
|
|
756
1180
|
queueId: input.queueId ?? null,
|
|
757
1181
|
filter: {
|
|
758
|
-
schema:
|
|
1182
|
+
schema: SOURCE_WAKE_FILTER_PROFILE2,
|
|
1183
|
+
deliveryMode: input.deliveryMode,
|
|
1184
|
+
...input.bestEffort === void 0 ? {} : { bestEffort: input.bestEffort },
|
|
759
1185
|
source: input.source,
|
|
760
1186
|
fromPosition: input.fromPosition,
|
|
761
1187
|
maxPositionsPerWake: input.maxPositionsPerWake
|
|
@@ -767,7 +1193,7 @@ function createWakeSubscriptionPlaintext(input) {
|
|
|
767
1193
|
}).build());
|
|
768
1194
|
}
|
|
769
1195
|
async function createEncryptedWakeSubscription(input) {
|
|
770
|
-
const builder = new
|
|
1196
|
+
const builder = new PayloadBuilder5({ schema: EncryptedWakeSubscriptionSchema });
|
|
771
1197
|
const cleartext = WakeSubscriptionCleartextZod3.parse(builder.fields({
|
|
772
1198
|
subscriptionId: input.subscriptionId ?? generateHandshakeId2(),
|
|
773
1199
|
revision: input.revision ?? 1,
|
|
@@ -779,6 +1205,7 @@ async function createEncryptedWakeSubscription(input) {
|
|
|
779
1205
|
keyId: input.publisherKey.keyId,
|
|
780
1206
|
grant: input.grant,
|
|
781
1207
|
eventKind: SOURCE_WAKE_EVENT_KIND,
|
|
1208
|
+
deliveryMode: input.plaintext.filter.deliveryMode,
|
|
782
1209
|
expiresAtBlock: input.expiresAtBlock
|
|
783
1210
|
}).build());
|
|
784
1211
|
return await encryptWakeSubscription({
|
|
@@ -789,7 +1216,7 @@ async function createEncryptedWakeSubscription(input) {
|
|
|
789
1216
|
});
|
|
790
1217
|
}
|
|
791
1218
|
function createWakeSubscriptionAcceptance(input) {
|
|
792
|
-
const builder = new
|
|
1219
|
+
const builder = new PayloadBuilder5({ schema: WakeSubscriptionAcceptanceSchema });
|
|
793
1220
|
return WakeSubscriptionAcceptanceZod.parse(builder.fields({
|
|
794
1221
|
challenge: input.challenge,
|
|
795
1222
|
encryptionKey: input.encryptionKey,
|
|
@@ -800,45 +1227,45 @@ function createWakeSubscriptionAcceptance(input) {
|
|
|
800
1227
|
}).build());
|
|
801
1228
|
}
|
|
802
1229
|
function createPublisherServiceRevocation(service) {
|
|
803
|
-
const builder = new
|
|
1230
|
+
const builder = new PayloadBuilder5({ schema: PublisherServiceRevocationSchema });
|
|
804
1231
|
return PublisherServiceRevocationZod.parse(builder.fields({ service }).build());
|
|
805
1232
|
}
|
|
806
1233
|
function createPublisherEncryptionKeyRevocation(key) {
|
|
807
|
-
const builder = new
|
|
1234
|
+
const builder = new PayloadBuilder5({ schema: PublisherEncryptionKeyRevocationSchema });
|
|
808
1235
|
return PublisherEncryptionKeyRevocationZod.parse(builder.fields({ key }).build());
|
|
809
1236
|
}
|
|
810
1237
|
function createWakeGrantRevocation(grant) {
|
|
811
|
-
const builder = new
|
|
1238
|
+
const builder = new PayloadBuilder5({ schema: WakeGrantRevocationSchema });
|
|
812
1239
|
return WakeGrantRevocationZod.parse(builder.fields({ grant }).build());
|
|
813
1240
|
}
|
|
814
1241
|
function createWakeSubscriptionRevocation(subscription) {
|
|
815
|
-
const builder = new
|
|
1242
|
+
const builder = new PayloadBuilder5({ schema: WakeSubscriptionRevocationSchema });
|
|
816
1243
|
return WakeSubscriptionRevocationZod.parse(builder.fields({ subscription }).build());
|
|
817
1244
|
}
|
|
818
1245
|
function createWakeSubscriptionAcceptanceRevocation(acceptance) {
|
|
819
|
-
const builder = new
|
|
1246
|
+
const builder = new PayloadBuilder5({ schema: WakeSubscriptionAcceptanceRevocationSchema });
|
|
820
1247
|
return WakeSubscriptionAcceptanceRevocationZod.parse(builder.fields({ acceptance }).build());
|
|
821
1248
|
}
|
|
822
1249
|
|
|
823
1250
|
// src/lifecycleSelection.ts
|
|
824
|
-
function
|
|
1251
|
+
function isNonnegativeSafeInteger3(value) {
|
|
825
1252
|
return Number.isSafeInteger(value) && value >= 0;
|
|
826
1253
|
}
|
|
827
1254
|
function isVisible(claimedAtBlock, finalizedHead) {
|
|
828
|
-
return
|
|
1255
|
+
return isNonnegativeSafeInteger3(claimedAtBlock) && claimedAtBlock <= finalizedHead;
|
|
829
1256
|
}
|
|
830
1257
|
function isRevoked(revokedAtBlock, finalizedHead) {
|
|
831
1258
|
return revokedAtBlock !== void 0 && revokedAtBlock <= finalizedHead;
|
|
832
1259
|
}
|
|
833
1260
|
function isUnrevokedEvidence(claimedAtBlock, revokedAtBlock, finalizedHead) {
|
|
834
|
-
return isVisible(claimedAtBlock, finalizedHead) && (revokedAtBlock === void 0 ||
|
|
1261
|
+
return isVisible(claimedAtBlock, finalizedHead) && (revokedAtBlock === void 0 || isNonnegativeSafeInteger3(revokedAtBlock) && revokedAtBlock >= claimedAtBlock) && !isRevoked(revokedAtBlock, finalizedHead);
|
|
835
1262
|
}
|
|
836
1263
|
function isInsideExclusiveWindow(finalizedHead, validFromBlock, expiresAtBlock) {
|
|
837
|
-
return
|
|
1264
|
+
return isNonnegativeSafeInteger3(validFromBlock) && finalizedHead >= validFromBlock && (expiresAtBlock === void 0 || isNonnegativeSafeInteger3(expiresAtBlock) && expiresAtBlock > validFromBlock && finalizedHead < expiresAtBlock);
|
|
838
1265
|
}
|
|
839
1266
|
function isRevisionShapeValid(revision) {
|
|
840
|
-
if (!Number.isSafeInteger(revision.revision) || revision.revision < 1 || !
|
|
841
|
-
if (revision.revokedAtBlock !== void 0 && (!
|
|
1267
|
+
if (!Number.isSafeInteger(revision.revision) || revision.revision < 1 || !isNonnegativeSafeInteger3(revision.claimedAtBlock)) return false;
|
|
1268
|
+
if (revision.revokedAtBlock !== void 0 && (!isNonnegativeSafeInteger3(revision.revokedAtBlock) || revision.revokedAtBlock < revision.claimedAtBlock)) return false;
|
|
842
1269
|
return revision.revision === 1 ? revision.supersedes === void 0 : revision.supersedes !== void 0;
|
|
843
1270
|
}
|
|
844
1271
|
function resolveLinearChain(revisions, finalizedHead) {
|
|
@@ -933,7 +1360,7 @@ function acceptedSubscription(subscription, input) {
|
|
|
933
1360
|
const acceptances = acceptanceFor(subscription, input);
|
|
934
1361
|
if (acceptances.length > 1) return "ambiguous";
|
|
935
1362
|
const acceptance = acceptances[0];
|
|
936
|
-
if (acceptance === void 0 || !
|
|
1363
|
+
if (acceptance === void 0 || !isNonnegativeSafeInteger3(acceptance.acceptanceBlock)) return void 0;
|
|
937
1364
|
if (!Number.isSafeInteger(subscription.expiresAtBlock) || subscription.expiresAtBlock < 1) return void 0;
|
|
938
1365
|
if (acceptance.acceptanceBlock <= subscription.claimedAtBlock || acceptance.acceptanceBlock >= Number.MAX_SAFE_INTEGER) return void 0;
|
|
939
1366
|
const activeFromBlock = acceptance.acceptanceBlock + 1;
|
|
@@ -1200,43 +1627,20 @@ function createMemoryWakeQueue() {
|
|
|
1200
1627
|
|
|
1201
1628
|
// src/publisherSubscription.ts
|
|
1202
1629
|
import {
|
|
1203
|
-
ChainIdZod,
|
|
1630
|
+
ChainIdZod as ChainIdZod2,
|
|
1204
1631
|
EncryptedWakeSubscriptionZod as EncryptedWakeSubscriptionZod4,
|
|
1205
1632
|
EndpointChallengeZod as EndpointChallengeZod3,
|
|
1206
|
-
HashZod as
|
|
1633
|
+
HashZod as HashZod6,
|
|
1207
1634
|
hasMatchingHandshakeProfile,
|
|
1208
|
-
NormalizedAddressZod as
|
|
1635
|
+
NormalizedAddressZod as NormalizedAddressZod6,
|
|
1209
1636
|
PublisherEncryptionKeyZod as PublisherEncryptionKeyZod2,
|
|
1210
1637
|
PublisherServiceZod as PublisherServiceZod2,
|
|
1211
1638
|
WakeGrantObjectZod as WakeGrantObjectZod2
|
|
1212
1639
|
} from "@xyo-network/event-kit-schemas";
|
|
1213
|
-
|
|
1214
|
-
// src/sourceWakeIdentity.ts
|
|
1215
|
-
import {
|
|
1216
|
-
SourceWakeSubscriptionFilterZod,
|
|
1217
|
-
WakeSourceSchema,
|
|
1218
|
-
WakeSourceZod
|
|
1219
|
-
} from "@xyo-network/event-kit-schemas";
|
|
1220
|
-
import {
|
|
1221
|
-
asSchema,
|
|
1222
|
-
PayloadBuilder as PayloadBuilder4
|
|
1223
|
-
} from "@xyo-network/sdk";
|
|
1224
|
-
async function wakeSourceHash(source) {
|
|
1225
|
-
return await PayloadBuilder4.dataHash({ schema: WakeSourceSchema, ...WakeSourceZod.parse(source) });
|
|
1226
|
-
}
|
|
1227
|
-
function wakeSourcesEqual(left, right) {
|
|
1228
|
-
return canonicalJson(WakeSourceZod.parse(left)) === canonicalJson(WakeSourceZod.parse(right));
|
|
1229
|
-
}
|
|
1230
|
-
async function sourceWakeFilterHash(filter) {
|
|
1231
|
-
const parsed = SourceWakeSubscriptionFilterZod.parse(filter);
|
|
1232
|
-
return await PayloadBuilder4.dataHash({ ...parsed, schema: asSchema(parsed.schema, true) });
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
// src/publisherSubscription.ts
|
|
1236
1640
|
function rejectPublisherSubscription(reason) {
|
|
1237
1641
|
return Object.freeze({ ok: false, reason });
|
|
1238
1642
|
}
|
|
1239
|
-
function
|
|
1643
|
+
function isNonnegativeSafeInteger4(value) {
|
|
1240
1644
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
1241
1645
|
}
|
|
1242
1646
|
function isActiveAt(value, block) {
|
|
@@ -1253,7 +1657,7 @@ function hasExactQueueScope(grant, plaintext) {
|
|
|
1253
1657
|
return plaintext.deploymentId === grant.deploymentId && plaintext.queueId === grantQueue;
|
|
1254
1658
|
}
|
|
1255
1659
|
function hasExactSubscriptionBinding(candidate, subscription) {
|
|
1256
|
-
return subscription.chainId === candidate.chainId && subscription.publisher === candidate.publisher && subscription.service === candidate.service.objectHash && subscription.encryptionKey === candidate.key.objectHash && subscription.keyId === candidate.key.body.keyId && subscription.grant === candidate.grant.objectHash;
|
|
1660
|
+
return subscription.deliveryMode === candidate.service.body.deliveryMode && subscription.chainId === candidate.chainId && subscription.publisher === candidate.publisher && subscription.service === candidate.service.objectHash && subscription.encryptionKey === candidate.key.objectHash && subscription.keyId === candidate.key.body.keyId && subscription.grant === candidate.grant.objectHash;
|
|
1257
1661
|
}
|
|
1258
1662
|
function hasExactKeyBinding(service, key, subscription) {
|
|
1259
1663
|
return service.encryptionKeys.includes(key.objectHash) && service.preferredEncryptionKey === key.objectHash && key.body.keyId === subscription.keyId && key.objectHash === subscription.encryptionKey;
|
|
@@ -1264,22 +1668,22 @@ function expiryWithinServicePolicy(subscription, service, tupleBlock) {
|
|
|
1264
1668
|
function parsePublisherSubscriptionCandidate(candidate) {
|
|
1265
1669
|
try {
|
|
1266
1670
|
return {
|
|
1267
|
-
chainId:
|
|
1671
|
+
chainId: ChainIdZod2.parse(candidate.chainId),
|
|
1268
1672
|
grant: WakeGrantObjectZod2.parse(candidate.grant.body),
|
|
1269
1673
|
key: PublisherEncryptionKeyZod2.parse(candidate.key.body),
|
|
1270
|
-
publisher:
|
|
1674
|
+
publisher: NormalizedAddressZod6.parse(candidate.publisher),
|
|
1271
1675
|
service: PublisherServiceZod2.parse(candidate.service.body),
|
|
1272
|
-
subscriber:
|
|
1676
|
+
subscriber: NormalizedAddressZod6.parse(candidate.subscriber),
|
|
1273
1677
|
subscription: EncryptedWakeSubscriptionZod4.parse(candidate.subscription.body),
|
|
1274
|
-
tupleObjectHashes: candidate.tupleObjectHashes.map((hash) =>
|
|
1275
|
-
tupleSubject:
|
|
1678
|
+
tupleObjectHashes: candidate.tupleObjectHashes.map((hash) => HashZod6.parse(hash)),
|
|
1679
|
+
tupleSubject: NormalizedAddressZod6.parse(candidate.tupleSubject)
|
|
1276
1680
|
};
|
|
1277
1681
|
} catch {
|
|
1278
1682
|
return void 0;
|
|
1279
1683
|
}
|
|
1280
1684
|
}
|
|
1281
1685
|
function tupleBindingIsValid(candidate, parsed) {
|
|
1282
|
-
return
|
|
1686
|
+
return isNonnegativeSafeInteger4(candidate.tupleBlock) && isNonnegativeSafeInteger4(candidate.finalizedHead) && candidate.finalizedHead >= candidate.tupleBlock && parsed.tupleSubject === parsed.publisher && occursExactlyOnce(parsed.tupleObjectHashes, candidate.service.objectHash) && occursExactlyOnce(parsed.tupleObjectHashes, candidate.grant.objectHash) && occursExactlyOnce(parsed.tupleObjectHashes, candidate.subscription.objectHash);
|
|
1283
1687
|
}
|
|
1284
1688
|
async function bodyHashesAreValid(candidate, parsed) {
|
|
1285
1689
|
const canonicalHashes = await Promise.all([
|
|
@@ -1397,10 +1801,10 @@ async function issuePublisherEndpointChallenge(input) {
|
|
|
1397
1801
|
}
|
|
1398
1802
|
}
|
|
1399
1803
|
async function beginPublisherEndpointChallengeAttempt(input) {
|
|
1400
|
-
if (!
|
|
1804
|
+
if (!isNonnegativeSafeInteger4(input.nowSeconds)) return { kind: "store-invalid" };
|
|
1401
1805
|
try {
|
|
1402
1806
|
const result = await input.store.beginAttempt({
|
|
1403
|
-
challengeHash:
|
|
1807
|
+
challengeHash: HashZod6.parse(input.challengeHash),
|
|
1404
1808
|
nowSeconds: input.nowSeconds
|
|
1405
1809
|
});
|
|
1406
1810
|
if (!["already-attempted", "expired", "missing", "started", "verified"].includes(result.kind)) {
|
|
@@ -1409,7 +1813,7 @@ async function beginPublisherEndpointChallengeAttempt(input) {
|
|
|
1409
1813
|
if (result.kind !== "missing" && !await challengeRecordMatches(
|
|
1410
1814
|
result.record,
|
|
1411
1815
|
result.record.challenge,
|
|
1412
|
-
|
|
1816
|
+
HashZod6.parse(input.challengeHash)
|
|
1413
1817
|
)) return { kind: "store-invalid" };
|
|
1414
1818
|
if ((result.kind === "started" || result.kind === "already-attempted") && result.record.state !== "attempted") return { kind: "store-invalid" };
|
|
1415
1819
|
return result.kind === "verified" && result.record.state !== "verified" ? { kind: "store-invalid" } : result;
|
|
@@ -1435,7 +1839,7 @@ async function completePublisherEndpointChallenge(input) {
|
|
|
1435
1839
|
if (!endpointChallengeMatchesPublisherContext(input.challenge, input.subscription.context)) {
|
|
1436
1840
|
return completeFailure("challenge-context-invalid");
|
|
1437
1841
|
}
|
|
1438
|
-
if (!
|
|
1842
|
+
if (!isNonnegativeSafeInteger4(input.nowSeconds)) return completeFailure("challenge-expired");
|
|
1439
1843
|
const insideDecisionWindow = input.challenge.issuedAt <= input.nowSeconds && input.nowSeconds < input.challenge.expiresAt;
|
|
1440
1844
|
const proofInput = {
|
|
1441
1845
|
challenge: input.challenge,
|
|
@@ -1485,33 +1889,33 @@ async function completePublisherEndpointChallenge(input) {
|
|
|
1485
1889
|
}
|
|
1486
1890
|
function filterWithinServicePolicy(service, plaintext) {
|
|
1487
1891
|
const filter = plaintext.filter;
|
|
1488
|
-
return filter.maxPositionsPerWake <= service.limits.maxPositionsPerWake && service.events.some((event) => wakeSourcesEqual(event.source, filter.source));
|
|
1892
|
+
return service.deliveryMode === filter.deliveryMode && JSON.stringify(service.bestEffort) === JSON.stringify(filter.bestEffort) && filter.maxPositionsPerWake <= service.limits.maxPositionsPerWake && service.events.some((event) => wakeSourcesEqual(event.source, filter.source));
|
|
1489
1893
|
}
|
|
1490
1894
|
|
|
1491
1895
|
// src/sourceWakeActivation.ts
|
|
1492
1896
|
import {
|
|
1493
|
-
ChainIdZod as
|
|
1494
|
-
DeploymentIdZod as
|
|
1495
|
-
HashZod as
|
|
1496
|
-
NonnegativeJsonSafeIntegerZod as
|
|
1497
|
-
NormalizedAddressZod as
|
|
1498
|
-
PositiveJsonSafeIntegerZod
|
|
1897
|
+
ChainIdZod as ChainIdZod3,
|
|
1898
|
+
DeploymentIdZod as DeploymentIdZod3,
|
|
1899
|
+
HashZod as HashZod7,
|
|
1900
|
+
NonnegativeJsonSafeIntegerZod as NonnegativeJsonSafeIntegerZod4,
|
|
1901
|
+
NormalizedAddressZod as NormalizedAddressZod7,
|
|
1902
|
+
PositiveJsonSafeIntegerZod as PositiveJsonSafeIntegerZod2
|
|
1499
1903
|
} from "@xyo-network/event-kit-schemas";
|
|
1500
|
-
import { z as
|
|
1501
|
-
var SourceWakeSubscriptionActivationZod =
|
|
1502
|
-
atBlock:
|
|
1503
|
-
activeFromBlock:
|
|
1504
|
-
expiresAtBlock:
|
|
1505
|
-
authorizationChainId:
|
|
1506
|
-
controller:
|
|
1507
|
-
publisher:
|
|
1508
|
-
deploymentId:
|
|
1509
|
-
queueId:
|
|
1510
|
-
sourceHash:
|
|
1511
|
-
filterHash:
|
|
1512
|
-
subscriptionHash:
|
|
1513
|
-
serviceHash:
|
|
1514
|
-
grantHash:
|
|
1904
|
+
import { z as z4 } from "zod";
|
|
1905
|
+
var SourceWakeSubscriptionActivationZod = z4.strictObject({
|
|
1906
|
+
atBlock: NonnegativeJsonSafeIntegerZod4,
|
|
1907
|
+
activeFromBlock: NonnegativeJsonSafeIntegerZod4,
|
|
1908
|
+
expiresAtBlock: PositiveJsonSafeIntegerZod2,
|
|
1909
|
+
authorizationChainId: ChainIdZod3,
|
|
1910
|
+
controller: NormalizedAddressZod7,
|
|
1911
|
+
publisher: NormalizedAddressZod7,
|
|
1912
|
+
deploymentId: DeploymentIdZod3,
|
|
1913
|
+
queueId: z4.string().min(1).max(255).optional(),
|
|
1914
|
+
sourceHash: HashZod7,
|
|
1915
|
+
filterHash: HashZod7,
|
|
1916
|
+
subscriptionHash: HashZod7,
|
|
1917
|
+
serviceHash: HashZod7,
|
|
1918
|
+
grantHash: HashZod7
|
|
1515
1919
|
}).refine(
|
|
1516
1920
|
(value) => value.activeFromBlock <= value.atBlock && value.atBlock < value.expiresAtBlock,
|
|
1517
1921
|
"Subscription must be active at the exact finalized authorization snapshot"
|
|
@@ -1519,17 +1923,17 @@ var SourceWakeSubscriptionActivationZod = z2.strictObject({
|
|
|
1519
1923
|
|
|
1520
1924
|
// src/statementGraphWakeGrantAuthorizer.ts
|
|
1521
1925
|
import {
|
|
1522
|
-
ChainIdZod as
|
|
1523
|
-
WakeSourceZod as
|
|
1926
|
+
ChainIdZod as ChainIdZod4,
|
|
1927
|
+
WakeSourceZod as WakeSourceZod3
|
|
1524
1928
|
} from "@xyo-network/event-kit-schemas";
|
|
1525
1929
|
|
|
1526
1930
|
// src/grantEvidence.ts
|
|
1527
1931
|
import {
|
|
1528
1932
|
asWakeGrantObject,
|
|
1529
1933
|
asWakeGrantRevocation,
|
|
1530
|
-
DeploymentIdZod as
|
|
1531
|
-
HashZod as
|
|
1532
|
-
NormalizedAddressZod as
|
|
1934
|
+
DeploymentIdZod as DeploymentIdZod4,
|
|
1935
|
+
HashZod as HashZod8,
|
|
1936
|
+
NormalizedAddressZod as NormalizedAddressZod8,
|
|
1533
1937
|
WakeGrantObjectSchema as WakeGrantObjectSchema3,
|
|
1534
1938
|
WakeGrantRevocationSchema as WakeGrantRevocationSchema3
|
|
1535
1939
|
} from "@xyo-network/event-kit-schemas";
|
|
@@ -1585,7 +1989,7 @@ async function readRevocations(viewer, controller, subject, atBlock) {
|
|
|
1585
1989
|
for (const claim of claims) {
|
|
1586
1990
|
if (!isLiveClaim(claim, controller, subject, WakeGrantRevocationSchema3, atBlock)) continue;
|
|
1587
1991
|
const object = await viewer.object(claim.objectHash);
|
|
1588
|
-
if (object?.assurance !== "resolved" || object.declaredSchema !== WakeGrantRevocationSchema3 || object.objectHash !== claim.objectHash || !
|
|
1992
|
+
if (object?.assurance !== "resolved" || object.declaredSchema !== WakeGrantRevocationSchema3 || object.objectHash !== claim.objectHash || !isRecord3(object.params) || Object.hasOwn(object.params, "schema")) continue;
|
|
1589
1993
|
const revocation = asWakeGrantRevocation({ ...object.params, schema: object.declaredSchema });
|
|
1590
1994
|
if (revocation !== void 0) revoked.add(revocation.grant);
|
|
1591
1995
|
}
|
|
@@ -1617,55 +2021,55 @@ async function resolveEvidence(viewer, claim, request, atBlock, revokedGrantHash
|
|
|
1617
2021
|
});
|
|
1618
2022
|
}
|
|
1619
2023
|
function isLiveClaim(claim, controller, subject, declaredObjectSchema, atBlock) {
|
|
1620
|
-
return claim.source === controller && claim.subject === subject && claim.declaredObjectSchema === declaredObjectSchema &&
|
|
2024
|
+
return claim.source === controller && claim.subject === subject && claim.declaredObjectSchema === declaredObjectSchema && isNonnegativeSafeInteger5(claim.firstBlock) && claim.firstBlock <= atBlock && isNonnegativeSafeInteger5(claim.lastBlock) && claim.lastBlock >= claim.firstBlock && HashZod8.safeParse(claim.firstEventHash).success && HashZod8.safeParse(claim.objectHash).success;
|
|
1621
2025
|
}
|
|
1622
2026
|
function parseExactGrantObject(object, expectedObjectHash) {
|
|
1623
2027
|
if (object?.assurance !== "resolved") return void 0;
|
|
1624
2028
|
if (object.declaredSchema !== WakeGrantObjectSchema3 || object.objectHash !== expectedObjectHash) return void 0;
|
|
1625
|
-
if (!
|
|
2029
|
+
if (!isRecord3(object.params) || Object.hasOwn(object.params, "schema")) return void 0;
|
|
1626
2030
|
return asWakeGrantObject({
|
|
1627
2031
|
...object.params,
|
|
1628
2032
|
schema: object.declaredSchema
|
|
1629
2033
|
});
|
|
1630
2034
|
}
|
|
1631
2035
|
function assertValidRequest(request) {
|
|
1632
|
-
if (request.exactGrantHash !== void 0 && !
|
|
2036
|
+
if (request.exactGrantHash !== void 0 && !HashZod8.safeParse(request.exactGrantHash).success) {
|
|
1633
2037
|
throw new TypeError("Exact grant hash must be canonical lowercase hexadecimal");
|
|
1634
2038
|
}
|
|
1635
|
-
if (!
|
|
2039
|
+
if (!NormalizedAddressZod8.safeParse(request.controller).success) {
|
|
1636
2040
|
throw new TypeError("Grant controller must be a normalized address");
|
|
1637
2041
|
}
|
|
1638
|
-
if (!
|
|
2042
|
+
if (!NormalizedAddressZod8.safeParse(request.publisher).success) {
|
|
1639
2043
|
throw new TypeError("Grant publisher must be a normalized address");
|
|
1640
2044
|
}
|
|
1641
|
-
if (!
|
|
2045
|
+
if (!DeploymentIdZod4.safeParse(request.deploymentId).success) {
|
|
1642
2046
|
throw new TypeError("Grant deploymentId must contain 1 to 255 characters");
|
|
1643
2047
|
}
|
|
1644
2048
|
if (request.queueId !== void 0 && (request.queueId.length === 0 || request.queueId.length > 255)) {
|
|
1645
2049
|
throw new TypeError("Grant queueId must contain 1 to 255 characters when configured");
|
|
1646
2050
|
}
|
|
1647
|
-
if (!
|
|
2051
|
+
if (!isNonnegativeSafeInteger5(request.expectedHead)) {
|
|
1648
2052
|
throw new RangeError("Grant expectedHead must be a nonnegative safe integer");
|
|
1649
2053
|
}
|
|
1650
2054
|
}
|
|
1651
2055
|
async function readHead(viewer) {
|
|
1652
2056
|
const head = await viewer.head();
|
|
1653
|
-
if (!
|
|
2057
|
+
if (!isNonnegativeSafeInteger5(head)) {
|
|
1654
2058
|
throw new RangeError("Statement Graph head must be a nonnegative safe integer");
|
|
1655
2059
|
}
|
|
1656
2060
|
return head;
|
|
1657
2061
|
}
|
|
1658
|
-
function
|
|
2062
|
+
function isNonnegativeSafeInteger5(value) {
|
|
1659
2063
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
1660
2064
|
}
|
|
1661
|
-
function
|
|
2065
|
+
function isRecord3(value) {
|
|
1662
2066
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1663
2067
|
}
|
|
1664
2068
|
|
|
1665
2069
|
// src/statementGraphWakeGrantAuthorizer.ts
|
|
1666
2070
|
function createStatementGraphWakeGrantAuthorizer(options) {
|
|
1667
|
-
const authorizationChainId =
|
|
1668
|
-
const source =
|
|
2071
|
+
const authorizationChainId = ChainIdZod4.parse(options.authorizationChainId);
|
|
2072
|
+
const source = WakeSourceZod3.parse(options.source);
|
|
1669
2073
|
const grants = createDeploymentGrantAuthorizer({ viewer: options.viewer });
|
|
1670
2074
|
return Object.freeze({
|
|
1671
2075
|
async authorizePublisher(request) {
|
|
@@ -1697,190 +2101,6 @@ function createStatementGraphWakeGrantAuthorizer(options) {
|
|
|
1697
2101
|
});
|
|
1698
2102
|
}
|
|
1699
2103
|
|
|
1700
|
-
// src/verifyWakeRequest.ts
|
|
1701
|
-
import {
|
|
1702
|
-
ChainIdZod as ChainIdZod4,
|
|
1703
|
-
DeploymentIdZod as DeploymentIdZod4,
|
|
1704
|
-
HashZod as HashZod7,
|
|
1705
|
-
MAX_POSITIONS_PER_SUBSCRIPTION_WAKE,
|
|
1706
|
-
NonnegativeJsonSafeIntegerZod as NonnegativeJsonSafeIntegerZod3,
|
|
1707
|
-
NormalizedAddressZod as NormalizedAddressZod7,
|
|
1708
|
-
PositiveJsonSafeIntegerZod as PositiveJsonSafeIntegerZod2,
|
|
1709
|
-
SOURCE_WAKE_FILTER_PROFILE as SOURCE_WAKE_FILTER_PROFILE2,
|
|
1710
|
-
WakeEnvelopeZod as WakeEnvelopeZod2,
|
|
1711
|
-
WakeJwtAudienceZod as WakeJwtAudienceZod2,
|
|
1712
|
-
WakeJwtHeaderZod,
|
|
1713
|
-
WakeJwtPayloadZod,
|
|
1714
|
-
WakeJwtSchema,
|
|
1715
|
-
WakeSourceZod as WakeSourceZod3
|
|
1716
|
-
} from "@xyo-network/event-kit-schemas";
|
|
1717
|
-
import {
|
|
1718
|
-
Account,
|
|
1719
|
-
PayloadBuilder as PayloadBuilder5
|
|
1720
|
-
} from "@xyo-network/sdk";
|
|
1721
|
-
import { z as z3 } from "zod";
|
|
1722
|
-
var EVENT_KIT_MAX_JWT_LIFETIME_SECONDS = 300;
|
|
1723
|
-
var EVENT_KIT_MAX_FUTURE_IAT_SECONDS = 60;
|
|
1724
|
-
var WakeAdmissionRouteZod = z3.strictObject({
|
|
1725
|
-
audience: WakeJwtAudienceZod2,
|
|
1726
|
-
authorizationChainId: ChainIdZod4,
|
|
1727
|
-
controller: NormalizedAddressZod7,
|
|
1728
|
-
deploymentId: DeploymentIdZod4,
|
|
1729
|
-
queueId: z3.string().min(1).max(255).optional(),
|
|
1730
|
-
source: WakeSourceZod3,
|
|
1731
|
-
filterHash: HashZod7,
|
|
1732
|
-
subscriptionHash: HashZod7,
|
|
1733
|
-
fromPosition: NonnegativeJsonSafeIntegerZod3,
|
|
1734
|
-
maxPositionsPerWake: PositiveJsonSafeIntegerZod2.max(MAX_POSITIONS_PER_SUBSCRIPTION_WAKE),
|
|
1735
|
-
maxGrantLagBlocks: NonnegativeJsonSafeIntegerZod3
|
|
1736
|
-
});
|
|
1737
|
-
async function verifyWakeRequest(input) {
|
|
1738
|
-
const routeResult = WakeAdmissionRouteZod.safeParse(input.route);
|
|
1739
|
-
if (!routeResult.success || !NonnegativeJsonSafeIntegerZod3.safeParse(input.nowSeconds).success) {
|
|
1740
|
-
return { ok: false, reason: "configuration-invalid" };
|
|
1741
|
-
}
|
|
1742
|
-
const eventResult = WakeEnvelopeZod2.safeParse(input.body);
|
|
1743
|
-
if (!eventResult.success) return malformedWake(input.body, eventResult.error.issues);
|
|
1744
|
-
const route = routeResult.data;
|
|
1745
|
-
if (route.filterHash !== await sourceWakeFilterHash({
|
|
1746
|
-
schema: SOURCE_WAKE_FILTER_PROFILE2,
|
|
1747
|
-
source: route.source,
|
|
1748
|
-
fromPosition: route.fromPosition,
|
|
1749
|
-
maxPositionsPerWake: route.maxPositionsPerWake
|
|
1750
|
-
})) return { ok: false, reason: "configuration-invalid" };
|
|
1751
|
-
const event = Object.freeze({ ...eventResult.data, source: Object.freeze(eventResult.data.source) });
|
|
1752
|
-
if (event.authorizationChainId !== route.authorizationChainId) {
|
|
1753
|
-
return { ok: false, reason: "event-authorization-chain-mismatch" };
|
|
1754
|
-
}
|
|
1755
|
-
if (!wakeSourcesEqual(event.source, route.source)) return { ok: false, reason: "event-source-mismatch" };
|
|
1756
|
-
if (event.filterHash !== route.filterHash) return { ok: false, reason: "event-filter-mismatch" };
|
|
1757
|
-
if (event.deploymentId !== route.deploymentId || event.queueId !== route.queueId) {
|
|
1758
|
-
return { ok: false, reason: "event-target-mismatch" };
|
|
1759
|
-
}
|
|
1760
|
-
if (event.fromPosition < route.fromPosition) return { ok: false, reason: "range-before-floor" };
|
|
1761
|
-
if (event.toPosition - event.fromPosition >= route.maxPositionsPerWake) return { ok: false, reason: "range-too-large" };
|
|
1762
|
-
const jwt = await verifyJwtProfile(input.jwt, route.audience, input.nowSeconds);
|
|
1763
|
-
if (!jwt.ok) return jwt;
|
|
1764
|
-
let eventHash;
|
|
1765
|
-
try {
|
|
1766
|
-
eventHash = await PayloadBuilder5.dataHash(event);
|
|
1767
|
-
} catch {
|
|
1768
|
-
return { ok: false, reason: "event-hash-failed" };
|
|
1769
|
-
}
|
|
1770
|
-
if (eventHash !== jwt.jwt.eventHash) return { ok: false, reason: "event-hash-mismatch" };
|
|
1771
|
-
const sourceHash = await wakeSourceHash(route.source);
|
|
1772
|
-
return Object.freeze({
|
|
1773
|
-
ok: true,
|
|
1774
|
-
verified: Object.freeze({
|
|
1775
|
-
audience: route.audience,
|
|
1776
|
-
deploymentId: event.deploymentId,
|
|
1777
|
-
event,
|
|
1778
|
-
eventHash,
|
|
1779
|
-
expiresAt: jwt.jwt.expiresAt,
|
|
1780
|
-
fromPosition: event.fromPosition,
|
|
1781
|
-
issuedAt: event.issuedAt,
|
|
1782
|
-
jwtId: jwt.jwt.jwtId,
|
|
1783
|
-
publisher: jwt.jwt.publisher,
|
|
1784
|
-
...event.queueId === void 0 ? {} : { queueId: event.queueId },
|
|
1785
|
-
route: Object.freeze({
|
|
1786
|
-
...route,
|
|
1787
|
-
source: Object.freeze(route.source),
|
|
1788
|
-
sourceHash
|
|
1789
|
-
}),
|
|
1790
|
-
toPosition: event.toPosition,
|
|
1791
|
-
tokenIssuedAt: jwt.jwt.issuedAt,
|
|
1792
|
-
wakeId: event.wakeId
|
|
1793
|
-
})
|
|
1794
|
-
});
|
|
1795
|
-
}
|
|
1796
|
-
async function verifyJwtProfile(token, audience, nowSeconds) {
|
|
1797
|
-
let verified;
|
|
1798
|
-
try {
|
|
1799
|
-
verified = await Account.verifyJwt(token, { audience, now: nowSeconds });
|
|
1800
|
-
} catch (error) {
|
|
1801
|
-
return reject2("jwt-malformed", [errorMessage2(error)]);
|
|
1802
|
-
}
|
|
1803
|
-
if (!verified.ok) {
|
|
1804
|
-
return Object.hasOwn(verified.payload ?? {}, "nbf") ? reject2("jwt-nbf-not-allowed") : reject2(classifySdkJwtFailure(verified.reasons), verified.reasons);
|
|
1805
|
-
}
|
|
1806
|
-
const wireResult = parseStrictJwtWire(verified.header, verified.payload, audience);
|
|
1807
|
-
if (!wireResult.ok) return wireResult;
|
|
1808
|
-
const { header: strictHeader, payload: strictPayload } = wireResult;
|
|
1809
|
-
if (strictHeader.kid !== strictPayload.iss) return reject2("jwt-identity-invalid");
|
|
1810
|
-
if (strictPayload.exp <= nowSeconds) return reject2("jwt-expired");
|
|
1811
|
-
if (strictPayload.exp - strictPayload.iat > EVENT_KIT_MAX_JWT_LIFETIME_SECONDS) return reject2("jwt-lifetime-invalid");
|
|
1812
|
-
if (strictPayload.iat - nowSeconds > EVENT_KIT_MAX_FUTURE_IAT_SECONDS) return reject2("jwt-future-iat");
|
|
1813
|
-
const jwt = Object.freeze({
|
|
1814
|
-
eventHash: strictPayload.eventHash,
|
|
1815
|
-
expiresAt: strictPayload.exp,
|
|
1816
|
-
issuedAt: strictPayload.iat,
|
|
1817
|
-
jwtId: strictPayload.jti,
|
|
1818
|
-
publisher: strictPayload.iss
|
|
1819
|
-
});
|
|
1820
|
-
return Object.freeze({ jwt, ok: true });
|
|
1821
|
-
}
|
|
1822
|
-
function parseStrictJwtWire(header, payload, audience) {
|
|
1823
|
-
const headerResult = WakeJwtHeaderZod.safeParse(header);
|
|
1824
|
-
if (!headerResult.success) {
|
|
1825
|
-
return NormalizedAddressZod7.safeParse(header.kid).success ? reject2("jwt-header-invalid", zodDetails(headerResult.error.issues)) : reject2("jwt-identity-invalid");
|
|
1826
|
-
}
|
|
1827
|
-
if (Object.hasOwn(payload, "nbf")) return reject2("jwt-nbf-not-allowed");
|
|
1828
|
-
if (!NormalizedAddressZod7.safeParse(payload.iss).success) return reject2("jwt-identity-invalid");
|
|
1829
|
-
if (payload.schema !== WakeJwtSchema) return reject2("jwt-schema-mismatch");
|
|
1830
|
-
if (payload.aud !== audience) return reject2("jwt-audience-mismatch");
|
|
1831
|
-
if (!isNonnegativeSafeInteger5(payload.iat) || !isNonnegativeSafeInteger5(payload.exp) || payload.exp <= payload.iat) {
|
|
1832
|
-
return reject2("jwt-lifetime-invalid");
|
|
1833
|
-
}
|
|
1834
|
-
const payloadResult = WakeJwtPayloadZod.safeParse(payload);
|
|
1835
|
-
if (!payloadResult.success) return reject2("jwt-claims-invalid", zodDetails(payloadResult.error.issues));
|
|
1836
|
-
return Object.freeze({
|
|
1837
|
-
header: headerResult.data,
|
|
1838
|
-
ok: true,
|
|
1839
|
-
payload: payloadResult.data
|
|
1840
|
-
});
|
|
1841
|
-
}
|
|
1842
|
-
function classifySdkJwtFailure(reasons) {
|
|
1843
|
-
if (reasons.some((reason) => reason.startsWith("audience mismatch:"))) return "jwt-audience-mismatch";
|
|
1844
|
-
if (reasons.some((reason) => reason.startsWith("expired:"))) return "jwt-expired";
|
|
1845
|
-
if (reasons.some((reason) => reason.startsWith("unsupported alg:") || reason.startsWith("unsupported typ:"))) {
|
|
1846
|
-
return "jwt-header-invalid";
|
|
1847
|
-
}
|
|
1848
|
-
return reasons.some((reason) => reason.startsWith("invalid public key:") || reason.startsWith("public key does not derive to kid:") || reason === "header.kid does not match payload.iss") ? "jwt-identity-invalid" : "jwt-invalid";
|
|
1849
|
-
}
|
|
1850
|
-
function malformedWake(body, issues) {
|
|
1851
|
-
const details = zodDetails(issues);
|
|
1852
|
-
if (hasInvertedSafeRange(body)) return reject2("range-invalid", details);
|
|
1853
|
-
return reject2(isRecord3(body) && Object.hasOwn(body, "wakeId") && issues.some((issue) => issue.path[0] === "wakeId") ? "wake-id-invalid" : "event-malformed", details);
|
|
1854
|
-
}
|
|
1855
|
-
function hasInvertedSafeRange(value) {
|
|
1856
|
-
if (!isRecord3(value)) return false;
|
|
1857
|
-
const { fromPosition, toPosition } = value;
|
|
1858
|
-
return typeof fromPosition === "number" && typeof toPosition === "number" && isNonnegativeSafeInteger5(fromPosition) && isNonnegativeSafeInteger5(toPosition) && fromPosition > toPosition;
|
|
1859
|
-
}
|
|
1860
|
-
function isNonnegativeSafeInteger5(value) {
|
|
1861
|
-
return Number.isSafeInteger(value) && value >= 0;
|
|
1862
|
-
}
|
|
1863
|
-
function isRecord3(value) {
|
|
1864
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1865
|
-
}
|
|
1866
|
-
function zodDetails(issues) {
|
|
1867
|
-
return issues.map((issue) => {
|
|
1868
|
-
const path = issue.path.map(String).join(".") || "<root>";
|
|
1869
|
-
return `${path}: ${issue.message}`;
|
|
1870
|
-
});
|
|
1871
|
-
}
|
|
1872
|
-
function reject2(reason, details) {
|
|
1873
|
-
if (details === void 0) return Object.freeze({ ok: false, reason });
|
|
1874
|
-
return Object.freeze({
|
|
1875
|
-
details: Object.freeze([...details]),
|
|
1876
|
-
ok: false,
|
|
1877
|
-
reason
|
|
1878
|
-
});
|
|
1879
|
-
}
|
|
1880
|
-
function errorMessage2(error) {
|
|
1881
|
-
return error instanceof Error ? error.message : String(error);
|
|
1882
|
-
}
|
|
1883
|
-
|
|
1884
2104
|
// src/wakeQueue.ts
|
|
1885
2105
|
async function drainQueue(queue, handler) {
|
|
1886
2106
|
let handled = 0;
|
|
@@ -1899,6 +2119,8 @@ async function drainQueue(queue, handler) {
|
|
|
1899
2119
|
}
|
|
1900
2120
|
export {
|
|
1901
2121
|
AdmittedWakeZod,
|
|
2122
|
+
BestEffortAdmissionAcknowledgmentZod,
|
|
2123
|
+
BestEffortSecurityStateZod,
|
|
1902
2124
|
EVENT_KIT_MAX_FUTURE_IAT_SECONDS,
|
|
1903
2125
|
EVENT_KIT_MAX_JWT_LIFETIME_SECONDS,
|
|
1904
2126
|
EVENT_KIT_REPLAY_RETENTION_SKEW_SECONDS,
|
|
@@ -1906,10 +2128,13 @@ export {
|
|
|
1906
2128
|
WakeAdmissionRouteZod,
|
|
1907
2129
|
WakeSubscriptionDecryptionError,
|
|
1908
2130
|
admitAndEnqueue,
|
|
2131
|
+
admitBestEffortHint,
|
|
1909
2132
|
beginPublisherEndpointChallengeAttempt,
|
|
1910
2133
|
canonicalJson,
|
|
1911
2134
|
canonicalJsonBytes,
|
|
2135
|
+
commitBestEffortSecurity,
|
|
1912
2136
|
completePublisherEndpointChallenge,
|
|
2137
|
+
createBestEffortSecurityState,
|
|
1913
2138
|
createEncryptedWakeSubscription,
|
|
1914
2139
|
createEndpointChallenge,
|
|
1915
2140
|
createEndpointChallengeResponse,
|
|
@@ -1937,6 +2162,7 @@ export {
|
|
|
1937
2162
|
handshakeObjectHash,
|
|
1938
2163
|
issuePublisherEndpointChallenge,
|
|
1939
2164
|
parseAdmittedWake,
|
|
2165
|
+
parseBestEffortSecurityState,
|
|
1940
2166
|
resolvePreferredEncryptionKey,
|
|
1941
2167
|
selectPublisherService,
|
|
1942
2168
|
selectSubscriptionActivation,
|