@seams/wallet-server 0.10.2 → 0.12.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 (106) hide show
  1. package/THIRD_PARTY_NOTICES.md +33 -1812
  2. package/artifact-manifest.json +28 -28
  3. package/cloudflare-router-ab/build/deriver-a/build-stamp.json +1 -1
  4. package/cloudflare-router-ab/build/deriver-a/index.js +2 -2
  5. package/cloudflare-router-ab/build/deriver-a/index_bg.wasm +0 -0
  6. package/cloudflare-router-ab/build/deriver-b/build-stamp.json +1 -1
  7. package/cloudflare-router-ab/build/deriver-b/index.js +2 -2
  8. package/cloudflare-router-ab/build/deriver-b/index_bg.wasm +0 -0
  9. package/cloudflare-router-ab/build/local-tools/darwin-arm64/router_ab_local_init +0 -0
  10. package/cloudflare-router-ab/build/local-tools/darwin-x64/router_ab_local_init +0 -0
  11. package/cloudflare-router-ab/build/local-tools/linux-x64/router_ab_local_init +0 -0
  12. package/cloudflare-router-ab/build/router/build-stamp.json +1 -1
  13. package/cloudflare-router-ab/build/router/index.js +2 -2
  14. package/cloudflare-router-ab/build/router/index_bg.wasm +0 -0
  15. package/cloudflare-router-ab/build/signing-worker/build-stamp.json +1 -1
  16. package/cloudflare-router-ab/build/signing-worker/index.js +2 -2
  17. package/cloudflare-router-ab/build/signing-worker/index_bg.wasm +0 -0
  18. package/cloudflare-router-ab/build/tenant-root-control-plane/build-stamp.json +1 -1
  19. package/cloudflare-router-ab/build/tenant-root-control-plane/index.js +2 -2
  20. package/cloudflare-router-ab/build/tenant-root-control-plane/index_bg.wasm +0 -0
  21. package/cloudflare-router-ab/scripts/start-local-role-workers.mjs +3 -0
  22. package/cloudflare-router-ab/scripts/start-local-wallet-system.mjs +3 -0
  23. package/dist/esm/cloud-host.js +7 -7
  24. package/dist/esm/core/ThresholdService/kv.js +1 -218
  25. package/dist/esm/hosted-wallet-gateway.js +1 -1
  26. package/dist/esm/index.js +3 -4
  27. package/dist/esm/localHostedWalletGatewayHandler.js +17 -11
  28. package/dist/esm/localIntendedYaoFault.js +34 -3
  29. package/dist/esm/localIntendedYaoRecoveryFault.js +19 -16
  30. package/dist/esm/localIntendedYaoSigningFault.js +31 -16
  31. package/dist/esm/router/auth/routerApiKeyAuth.js +1 -13
  32. package/dist/esm/router/cloudflare/d1/auth/d1RouterApiAuthService.js +3 -3
  33. package/dist/esm/router/cloudflare/d1/deviceLinking/d1LinkedDeviceOwnerAuthorizationProvider.js +1 -1
  34. package/dist/esm/router/cloudflare/d1/emailOtp/d1EmailOtpRecords.js +1 -1
  35. package/dist/esm/router/cloudflare/d1/emailOtp/d1EmailOtpRecoveryService.js +1 -1
  36. package/dist/esm/router/cloudflare/d1/emailOtp/d1GoogleEmailOtpSessionResolver.js +1 -1
  37. package/dist/esm/router/cloudflare/d1/registration/d1RegistrationCeremonyRecordStore.js +19 -0
  38. package/dist/esm/router/cloudflare/d1/registration/d1RegistrationCeremonyRecords.js +5 -5
  39. package/dist/esm/router/cloudflare/d1/registration/d1RegistrationCeremonyStore.js +5 -2
  40. package/dist/esm/router/cloudflare/d1/registration/d1RegistrationSetupSupport.js +7 -1
  41. package/dist/esm/router/cloudflare/d1/registration/d1WalletRegistrationCommitStore.js +29 -4
  42. package/dist/esm/router/cloudflare/d1/registration/d1WalletRegistrationRead.js +6 -3
  43. package/dist/esm/router/cloudflare/d1/registration/d1WalletRegistrationService.js +51 -29
  44. package/dist/esm/router/cloudflare/d1/registration/d1WalletRegistrationSetupExecution.js +23 -7
  45. package/dist/esm/router/cloudflare/d1/wallet/d1WalletAddSignerFinalizeRecord.js +1 -1
  46. package/dist/esm/router/cloudflare/d1/wallet/d1WalletAddSignerService.js +2 -2
  47. package/dist/esm/router/cloudflare/d1/wallet/d1WalletAuthMethodService.js +3 -3
  48. package/dist/esm/router/cloudflare/d1/webauthn/d1WebAuthnAuthService.js +1 -1
  49. package/dist/esm/router/cloudflare/d1/webauthn/d1WebAuthnRecords.js +1 -1
  50. package/dist/esm/router/cloudflare-adaptor.js +2 -3
  51. package/dist/esm/router/cloudflare.js +2470 -2515
  52. package/dist/esm/router/domains/ed25519Yao/capabilityLifecycle/routerAbEd25519YaoProductRegistration.js +1 -1
  53. package/dist/esm/router/domains/walletRegistration/walletRegistrationRoutes.js +39 -32
  54. package/dist/esm/router/framework/registrationSetupDispatch.js +11 -3
  55. package/dist/esm/router/framework/routeResponses.js +1 -5
  56. package/dist/esm/router/transport/fetch/routes/deviceLinkingOwnerAuthorization.js +1 -1
  57. package/dist/esm/router/transport/fetch/routes/passkeyCustody.js +2 -2
  58. package/dist/esm/router/transport/fetch/routes/thresholdEd25519.js +51 -24
  59. package/dist/esm/shared-ts/src/utils/validation.js +5 -1
  60. package/dist/esm/threshold/session/signingSessionSeal/guards/backends.js +2 -98
  61. package/dist/esm/threshold/session/signingSessionSeal/idempotencyBackends.js +2 -69
  62. package/dist/esm/threshold/session/signingSessionSeal/index.js +3 -4
  63. package/dist/esm/wasm/evm_crypto/pkg/evm_crypto_bg.wasm +0 -0
  64. package/dist/esm/wasm/near_signer/pkg/wasm_signer_worker_bg.wasm +0 -0
  65. package/dist/esm/wasm/shamir3pass_runtime/pkg/shamir3pass_runtime_bg.wasm +0 -0
  66. package/dist/types/wallet-server/src/cloud-host.d.ts +1 -0
  67. package/dist/types/wallet-server/src/core/ThresholdService/kv.d.ts +0 -29
  68. package/dist/types/wallet-server/src/localHostedWalletGatewayHandler.d.ts +1 -1
  69. package/dist/types/wallet-server/src/localIntendedYaoFault.d.ts +13 -3
  70. package/dist/types/wallet-server/src/localIntendedYaoRecoveryFault.d.ts +4 -2
  71. package/dist/types/wallet-server/src/localIntendedYaoSigningFault.d.ts +4 -2
  72. package/dist/types/wallet-server/src/router/auth/routerApiKeyAuth.d.ts +0 -8
  73. package/dist/types/wallet-server/src/router/cloudflare/d1/registration/d1RegistrationCeremonyRecordStore.d.ts +2 -0
  74. package/dist/types/wallet-server/src/router/cloudflare/d1/registration/d1RegistrationCeremonyStore.d.ts +1 -0
  75. package/dist/types/wallet-server/src/router/cloudflare/d1/registration/d1RegistrationSetupSupport.d.ts +2 -0
  76. package/dist/types/wallet-server/src/router/cloudflare/d1/registration/d1WalletRegistrationCommitStore.d.ts +25 -5
  77. package/dist/types/wallet-server/src/router/cloudflare/d1/registration/d1WalletRegistrationRead.d.ts +15 -0
  78. package/dist/types/wallet-server/src/router/cloudflare-adaptor.d.ts +1 -3
  79. package/dist/types/wallet-server/src/router/domains/walletRegistration/walletRegistrationInputs.d.ts +2 -0
  80. package/dist/types/wallet-server/src/router/framework/routeResponses.d.ts +0 -2
  81. package/dist/types/wallet-server/src/threshold/session/signingSessionSeal/guards/backends.d.ts +1 -16
  82. package/dist/types/wallet-server/src/threshold/session/signingSessionSeal/idempotencyBackends.d.ts +1 -16
  83. package/dist/types/wallet-server/src/threshold/session/signingSessionSeal/index.d.ts +5 -6
  84. package/package.json +2 -9
  85. package/src/README.md +5 -3
  86. package/dist/esm/router/cloudflare/runtime/createSelfHostedCloudflareSigningWorker.js +0 -93
  87. package/dist/esm/router/express-adaptor.js +0 -84
  88. package/dist/esm/router/express.js +0 -26400
  89. package/dist/esm/router/node/nodeHostedWalletGateway.js +0 -64
  90. package/dist/esm/router/node/nodeHostedWalletGatewayCheck.js +0 -163
  91. package/dist/esm/router/node/nodeHostedWalletGatewayMain.js +0 -120
  92. package/dist/esm/router/node/nodeHttp.js +0 -79
  93. package/dist/esm/router/node/nodeSqlite.js +0 -43
  94. package/dist/esm/router/node/signerSqlMigrations.js +0 -48
  95. package/dist/esm/storage/syncSqlite.js +0 -79
  96. package/dist/esm/threshold/session/signingSessionSeal/transport/express.js +0 -75
  97. package/dist/types/wallet-server/src/router/cloudflare/runtime/createSelfHostedCloudflareSigningWorker.d.ts +0 -23
  98. package/dist/types/wallet-server/src/router/express-adaptor.d.ts +0 -22
  99. package/dist/types/wallet-server/src/router/node/nodeHostedWalletGateway.d.ts +0 -34
  100. package/dist/types/wallet-server/src/router/node/nodeHostedWalletGatewayCheck.d.ts +0 -21
  101. package/dist/types/wallet-server/src/router/node/nodeHostedWalletGatewayMain.d.ts +0 -2
  102. package/dist/types/wallet-server/src/router/node/nodeHttp.d.ts +0 -13
  103. package/dist/types/wallet-server/src/router/node/nodeSqlite.d.ts +0 -22
  104. package/dist/types/wallet-server/src/router/node/signerSqlMigrations.d.ts +0 -20
  105. package/dist/types/wallet-server/src/storage/syncSqlite.d.ts +0 -28
  106. package/dist/types/wallet-server/src/threshold/session/signingSessionSeal/transport/express.d.ts +0 -12
@@ -7,8 +7,8 @@ import { secureRandomBase64Url } from "../../../../shared-ts/src/utils/secureRan
7
7
  import { ROUTER_AB_ED25519_NORMAL_SIGNING_STATE_KIND } from "../../../../shared-ts/src/utils/signingSessionSeal.js";
8
8
  import { computeAddSignerNearEd25519SigningKeyId, computeRegistrationNearEd25519SigningKeyId } from "../../../../shared-ts/src/utils/registrationIds.js";
9
9
  import { registrationNearEd25519BranchKey } from "../../../../shared-ts/src/utils/registrationSignerPlan.js";
10
- import "../../../../shared-ts/src/threshold/sessionPolicy.js";
11
10
  import { thresholdEd25519AuthorityScopeFromWalletAuthAuthority } from "../../../../core/ThresholdService/validation.js";
11
+ import "../../../../shared-ts/src/threshold/sessionPolicy.js";
12
12
  import { createRouterApiModule } from "../../../framework/modules.js";
13
13
  import { InMemoryRouterAbEd25519YaoRegistrationStateV1, createRouterAbEd25519YaoRegistrationModule } from "../registration/routerAbEd25519YaoRegistration.js";
14
14
  import { InMemoryRouterAbEd25519YaoRegistrationIntentAuthorizationStateV1 } from "../registration/routerAbEd25519YaoRegistrationIntentAuthorization.js";
@@ -14,6 +14,13 @@ import { parseHostedWalletSessionOperationCredentialToken, parsePrimaryWalletSes
14
14
  import { normalizeCorsOrigin } from "../../../core/SessionService.js";
15
15
  import { thresholdEcdsaChainTargetFromValue } from "../../../core/thresholdEcdsaChainTarget.js";
16
16
  import { walletIdFromString } from "../../../shared-ts/src/utils/registrationIds.js";
17
+ import { deriveImplicitNearAccountIdFromEd25519PublicKey, parseImplicitNearAccountId } from "../../../shared-ts/src/utils/near.js";
18
+ import { normalizeRegistrationSignerPlan, registrationSignerSetSelectionFromPlan } from "../../../shared-ts/src/utils/registrationSignerPlan.js";
19
+ import { addAuthMethodIntentGrantFromString, addSignerIntentGrantFromString, computeAddAuthMethodIntentDigestB64u, computeAddSignerIntentDigestB64u, normalizeAddAuthMethodIntentCaller } from "../../../shared-ts/src/utils/registrationIntent.js";
20
+ import { parsePasskeyCustodyEnvelopeRecord } from "../../../shared-ts/src/passkey-custody/custodyEnvelope.js";
21
+ import "../../../shared-ts/src/passkey-custody/index.js";
22
+ import { parseRouterAbTraceContextV1 } from "../../../shared-ts/src/utils/routerAbTraceContext.js";
23
+ import { recordRegistrationServerTiming, registrationServerTimingHeaders } from "../../cloudflare/d1/registration/d1RegistrationSetupSupport.js";
17
24
  import { parseRegistrationSetupOperationId } from "../../../shared-ts/src/utils/registrationSetupOperation.js";
18
25
  import { extractBearerCredential, extractRouterApiEnvironmentId } from "../../auth/routerApiKeyAuth.js";
19
26
  import { publishableKeyRoutePrincipal, resolvePublishableKeyApiCredentialAuth, resolveWalletRegistrationContext } from "../../auth/routerApiCredentialAuth.js";
@@ -21,13 +28,7 @@ import { resolveActiveRuntimePolicyScopeForEnvironment, resolveWalletSessionAdmi
21
28
  import { enforceRoutePolicy } from "../../framework/enforceRoutePolicy.js";
22
29
  import { parseRegistrationPreparedSessionSeal } from "./registrationSessionSeal.js";
23
30
  import { routeError, routeJson } from "../../framework/routeResponses.js";
24
- import { parsePasskeyCustodyEnvelopeRecord } from "../../../shared-ts/src/passkey-custody/custodyEnvelope.js";
25
- import "../../../shared-ts/src/passkey-custody/index.js";
26
31
  import { computeWalletEcdsaKeyFactsInventoryChallengeDigestB64u } from "../../../shared-ts/src/utils/ecdsaKeyFactsInventory.js";
27
- import { deriveImplicitNearAccountIdFromEd25519PublicKey, parseImplicitNearAccountId } from "../../../shared-ts/src/utils/near.js";
28
- import { normalizeRegistrationSignerPlan, registrationSignerSetSelectionFromPlan } from "../../../shared-ts/src/utils/registrationSignerPlan.js";
29
- import { addAuthMethodIntentGrantFromString, addSignerIntentGrantFromString, computeAddAuthMethodIntentDigestB64u, computeAddSignerIntentDigestB64u, normalizeAddAuthMethodIntentCaller } from "../../../shared-ts/src/utils/registrationIntent.js";
30
- import { parseRouterAbTraceContextV1 } from "../../../shared-ts/src/utils/routerAbTraceContext.js";
31
32
  //#region src/router/domains/walletRegistration/walletRegistrationRoutes.ts
32
33
  function parseRegistrationTraceContext(headers) {
33
34
  const parsed = parseRouterAbTraceContextV1(headers["x-seams-trace-id"] ?? headers["X-Seams-Trace-Id"]);
@@ -1573,6 +1574,8 @@ async function handleRouterApiWalletRegistrationSetup(input) {
1573
1574
  if (!registrationContext) return routeError(500, "route_auth_not_configured", "Wallet registration context is not configured");
1574
1575
  const request = parseWalletRegistrationSetupRequest(input.body);
1575
1576
  if (!request.ok) return routeError(400, request.code, request.message);
1577
+ const serverTiming = [];
1578
+ const contextStartedAtMs = Date.now();
1576
1579
  const context = await resolveWalletRegistrationContext({
1577
1580
  registration: request.value,
1578
1581
  environmentId: extractRouterApiEnvironmentId(input.headers) || void 0,
@@ -1585,11 +1588,13 @@ async function handleRouterApiWalletRegistrationSetup(input) {
1585
1588
  route: input.route,
1586
1589
  routeAuthNotConfiguredMessage: "Wallet registration setup requires API credential auth policy"
1587
1590
  });
1591
+ recordRegistrationServerTiming(serverTiming, "setup_context", contextStartedAtMs);
1588
1592
  if (!context.ok) {
1589
1593
  if (context.status === 401) return routeError(401, "unauthorized", `${context.code}: ${context.message}`);
1590
1594
  if (context.status === 403) return routeError(403, "forbidden", `${context.code}: ${context.message}`);
1591
1595
  return routeError(context.status, "internal", `${context.code}: ${context.message}`);
1592
1596
  }
1597
+ const policyStartedAtMs = Date.now();
1593
1598
  const resolved = await enforceRoutePolicy({
1594
1599
  headers: input.headers,
1595
1600
  logger: input.logger,
@@ -1602,23 +1607,29 @@ async function handleRouterApiWalletRegistrationSetup(input) {
1602
1607
  sourceIp: input.sourceIp,
1603
1608
  resolvers: { apiCredentials: publishableKeyRoutePrincipal.bind(void 0, context.principal) }
1604
1609
  });
1610
+ recordRegistrationServerTiming(serverTiming, "setup_route_policy", policyStartedAtMs);
1605
1611
  if (!resolved.ok) return routeJson(resolved.status, resolved.body);
1606
1612
  const principal = context.principal;
1607
- if (input.services.registrationSetupRateLimit) try {
1608
- if (!(await input.services.registrationSetupRateLimit({
1609
- apiKeyId: principal.apiKeyId,
1610
- orgId: principal.orgId,
1611
- projectId: context.environment.projectId,
1612
- envId: context.environment.key,
1613
- sourceIp: input.sourceIp || "unknown"
1614
- })).allowed) return routeJson(429, {
1615
- ok: false,
1616
- code: "rate_limited",
1617
- message: "Too many registration attempts. Wait one minute and try again.",
1618
- retryAfterMs: 6e4
1619
- });
1620
- } catch {
1621
- return routeError(503, "internal", "Registration setup rate limit is unavailable");
1613
+ if (input.services.registrationSetupRateLimit) {
1614
+ const limitStartedAtMs = Date.now();
1615
+ try {
1616
+ const limit = await input.services.registrationSetupRateLimit({
1617
+ apiKeyId: principal.apiKeyId,
1618
+ orgId: principal.orgId,
1619
+ projectId: context.environment.projectId,
1620
+ envId: context.environment.key,
1621
+ sourceIp: input.sourceIp || "unknown"
1622
+ });
1623
+ recordRegistrationServerTiming(serverTiming, "setup_rate_limit", limitStartedAtMs);
1624
+ if (!limit.allowed) return routeJson(429, {
1625
+ ok: false,
1626
+ code: "rate_limited",
1627
+ message: "Too many registration attempts. Wait one minute and try again.",
1628
+ retryAfterMs: 6e4
1629
+ });
1630
+ } catch {
1631
+ return routeError(503, "internal", "Registration setup rate limit is unavailable");
1632
+ }
1622
1633
  }
1623
1634
  const runtimePolicyScope = {
1624
1635
  orgId: principal.orgId,
@@ -1635,7 +1646,8 @@ async function handleRouterApiWalletRegistrationSetup(input) {
1635
1646
  signer: session,
1636
1647
  runtimePolicyScope,
1637
1648
  signingRootId: `${runtimePolicyScope.projectId}:${runtimePolicyScope.envId}`,
1638
- signingRootVersion: runtimePolicyScope.signingRootVersion
1649
+ signingRootVersion: runtimePolicyScope.signingRootVersion,
1650
+ serverTiming
1639
1651
  });
1640
1652
  }
1641
1653
  /**
@@ -1659,7 +1671,7 @@ async function handleRouterApiWalletRegistrationRespond(input) {
1659
1671
  verifier: session,
1660
1672
  ...registrationUserAgentFromHeaders(input.headers) ? { userAgent: registrationUserAgentFromHeaders(input.headers) } : {}
1661
1673
  }, traceContext.value ?? void 0, serverTiming, input.waitUntil);
1662
- return routeJson(result.ok ? 200 : result.code === "storage_unavailable" ? 503 : 400, result, { headers: ecdsaGatewayServerTimingHeaders(serverTiming) });
1674
+ return routeJson(result.ok ? 200 : result.code === "storage_unavailable" ? 503 : 400, result, { headers: registrationServerTimingHeaders(serverTiming) });
1663
1675
  }
1664
1676
  async function handleRouterApiWalletRegistrationNearAdmission(input) {
1665
1677
  const traceContext = parseRegistrationTraceContext(input.headers);
@@ -1882,7 +1894,7 @@ async function handleRouterApiWalletRegistrationActivate(input) {
1882
1894
  }, traceContext.value ?? void 0, serverTiming);
1883
1895
  if (!result.ok) return routeJson(400, result);
1884
1896
  if (!isEmailOtpWalletRegistrationActivateSuccessV2(result) && !isPasskeyWalletRegistrationActivateSuccessV2(result)) return routeError(500, "internal", "Wallet registration activation returned an invalid authority");
1885
- return routeJson(200, result, { headers: ecdsaGatewayServerTimingHeaders(serverTiming) });
1897
+ return routeJson(200, result, { headers: registrationServerTimingHeaders(serverTiming) });
1886
1898
  }
1887
1899
  /**
1888
1900
  * Commits a NEAR-only wallet or completes a mixed wallet's deferred signer.
@@ -1922,18 +1934,13 @@ async function handleRouterApiWalletRegistrationNearProvisioning(input) {
1922
1934
  ok: false,
1923
1935
  code: sealed.code,
1924
1936
  message: sealed.message
1925
- }, { headers: ecdsaGatewayServerTimingHeaders(serverTiming) });
1937
+ }, { headers: registrationServerTimingHeaders(serverTiming) });
1926
1938
  return routeJson(200, {
1927
1939
  ...result,
1928
1940
  sessionSeal: sealed.value
1929
- }, { headers: ecdsaGatewayServerTimingHeaders(serverTiming) });
1941
+ }, { headers: registrationServerTimingHeaders(serverTiming) });
1930
1942
  }
1931
- return routeJson(200, result, { headers: ecdsaGatewayServerTimingHeaders(serverTiming) });
1932
- }
1933
- function ecdsaGatewayServerTimingHeaders(serverTiming) {
1934
- if (serverTiming.length === 0) return void 0;
1935
- const header = serverTiming.map(([name, durationMs]) => `${name};dur=${Math.max(0, durationMs).toFixed(1)}`).join(", ");
1936
- return { "Server-Timing": header };
1943
+ return routeJson(200, result, { headers: registrationServerTimingHeaders(serverTiming) });
1937
1944
  }
1938
1945
  async function handleRouterApiWalletAddSignerStart(input) {
1939
1946
  if (!isPlainObject(input.body)) return routeError(400, "invalid_body", "JSON body required");
@@ -1,3 +1,4 @@
1
+ import { recordRegistrationServerTiming, registrationServerTimingHeaders } from "../cloudflare/d1/registration/d1RegistrationSetupSupport.js";
1
2
  //#region src/router/framework/registrationSetupDispatch.ts
2
3
  /** A transient storage fault outlived its retries. The same request can succeed later. */
3
4
  const SETUP_STORAGE_UNAVAILABLE = {
@@ -9,17 +10,23 @@ const SETUP_INTERNAL_FAILURE = {
9
10
  message: "Wallet registration setup failed"
10
11
  };
11
12
  async function dispatchWalletRegistrationSetup(context, input) {
13
+ const serverTiming = input.serverTiming ?? [];
14
+ const dispatchStartedAtMs = Date.now();
12
15
  const forwarded = await context.dispatcher?.dispatch(input);
13
16
  if (forwarded) return forwarded;
17
+ recordRegistrationServerTiming(serverTiming, "setup_dispatch", dispatchStartedAtMs);
14
18
  const result = await context.local.setupWalletRegistration(input);
19
+ const headers = registrationServerTimingHeaders(serverTiming);
15
20
  if (result.ok) return {
16
21
  status: 200,
17
- body: result
22
+ body: result,
23
+ ...headers ? { headers } : {}
18
24
  };
19
25
  const serverFailure = result.code === "storage_unavailable" ? SETUP_STORAGE_UNAVAILABLE : result.code === "internal" ? SETUP_INTERNAL_FAILURE : null;
20
26
  if (!serverFailure) return {
21
27
  status: 400,
22
- body: result
28
+ body: result,
29
+ ...headers ? { headers } : {}
23
30
  };
24
31
  console.error("[wallet-registration] setup failed", {
25
32
  code: result.code,
@@ -31,7 +38,8 @@ async function dispatchWalletRegistrationSetup(context, input) {
31
38
  ok: false,
32
39
  code: result.code,
33
40
  message: serverFailure.message
34
- }
41
+ },
42
+ ...headers ? { headers } : {}
35
43
  };
36
44
  }
37
45
  //#endregion
@@ -14,10 +14,6 @@ function routeError(status, code, message) {
14
14
  message
15
15
  });
16
16
  }
17
- function sendExpressRouteResponse(res, response) {
18
- for (const [name, value] of Object.entries(response.headers || {})) res.set(name, value);
19
- res.status(response.status).json(response.body);
20
- }
21
17
  function toFetchRouteResponse(response) {
22
18
  return new Response(JSON.stringify(response.body), {
23
19
  status: response.status,
@@ -28,6 +24,6 @@ function toFetchRouteResponse(response) {
28
24
  });
29
25
  }
30
26
  //#endregion
31
- export { routeError, routeJson, sendExpressRouteResponse, toFetchRouteResponse };
27
+ export { routeError, routeJson, toFetchRouteResponse };
32
28
 
33
29
  //# sourceMappingURL=routeResponses.js.map
@@ -4,9 +4,9 @@ import { sha256DigestB64u } from "../../../../shared-ts/src/utils/canonicalPrimi
4
4
  import { buildEmailOtpWalletAuthAuthority, buildPasskeyWalletAuthAuthority, walletAuthAuthorityRef } from "../../../../shared-ts/src/utils/walletAuthAuthority.js";
5
5
  import { parseLinkedDeviceOwnerAuthorizationRequestV1 } from "../../../../shared-ts/src/device-linking/parsers.js";
6
6
  import { buildWalletSessionCapabilitySubjectsV1, walletSessionCapabilitySubjectsV1Equal } from "../../../../authorization/domain.js";
7
+ import { thresholdEd25519AuthorityScopeFromWalletAuthAuthority } from "../../../../core/ThresholdService/validation.js";
7
8
  import { extractBearerCredential } from "../../../auth/routerApiKeyAuth.js";
8
9
  import { json, jsonFailure, readJson } from "../../../framework/http.js";
9
- import { thresholdEd25519AuthorityScopeFromWalletAuthAuthority } from "../../../../core/ThresholdService/validation.js";
10
10
  //#region src/router/transport/fetch/routes/deviceLinkingOwnerAuthorization.ts
11
11
  const LINKED_DEVICE_OWNER_AUTHORIZATION_PATH_V1 = "/wallet/device-linking/v1/owner-authorization";
12
12
  /**
@@ -10,10 +10,10 @@ import { buildPasskeyWalletAuthAuthority, walletAuthAuthorityRef } from "../../.
10
10
  import { parseSessionOrigin, parseVerifiedOwnerProofId } from "../../../../authorization/domain.js";
11
11
  import { buildVerifiedWalletOperationEmailOtpFactorResult, buildVerifiedWalletOperationPasskeyFactorResult } from "../../../../authorization/factorEvidence.js";
12
12
  import { findRouteDefinitionById, matchesRouteDefinitionRequest } from "../../../framework/routeDefinitions.js";
13
- import { extractBearerCredential, resolveSourceIpFromFetchHeaders } from "../../../auth/routerApiKeyAuth.js";
14
- import { toFetchRouteResponse } from "../../../framework/routeResponses.js";
15
13
  import { parsePasskeyCustodyEnvelopeRecord } from "../../../../shared-ts/src/passkey-custody/custodyEnvelope.js";
16
14
  import "../../../../shared-ts/src/passkey-custody/index.js";
15
+ import { extractBearerCredential, resolveSourceIpFromFetchHeaders } from "../../../auth/routerApiKeyAuth.js";
16
+ import { toFetchRouteResponse } from "../../../framework/routeResponses.js";
17
17
  import { readJson } from "../../../framework/http.js";
18
18
  import { isHostWithinRpId, originHostnameOrEmpty } from "../../../../core/authService/webauthnOidcHelpers.js";
19
19
  import { parseRecoveryCodeReservationId } from "../../../../shared-ts/src/wallet-recovery/recoveryCodeReservation.js";
@@ -890,7 +890,34 @@ async function proxyEd25519OwnerLaneExecution(input) {
890
890
  });
891
891
  }
892
892
  async function handleRouterAbEd25519NormalSigningRoute(input) {
893
- const authorization = await authorizeRouterAbEd25519NormalSigningRoute({
893
+ const startedAt = performance.now();
894
+ const timing = {
895
+ authorize: null,
896
+ admit: null,
897
+ proxy: null,
898
+ complete: null,
899
+ total: null
900
+ };
901
+ const response = await executeRouterAbEd25519NormalSigningRoute(input, timing);
902
+ timing.total = performance.now() - startedAt;
903
+ const headers = new Headers(response.headers);
904
+ for (const [stage, duration] of Object.entries(timing)) if (duration !== null) headers.append("Server-Timing", `ed25519_sign_${stage};dur=${Math.max(0, duration).toFixed(1)}`);
905
+ return new Response(response.body, {
906
+ status: response.status,
907
+ statusText: response.statusText,
908
+ headers
909
+ });
910
+ }
911
+ async function timeEd25519SigningStage(timing, stage, pending) {
912
+ const startedAt = performance.now();
913
+ try {
914
+ return await pending;
915
+ } finally {
916
+ timing[stage] = performance.now() - startedAt;
917
+ }
918
+ }
919
+ async function executeRouterAbEd25519NormalSigningRoute(input, timing) {
920
+ const authorization = await timeEd25519SigningStage(timing, "authorize", authorizeRouterAbEd25519NormalSigningRoute({
894
921
  body: input.body,
895
922
  rawBody: input.body,
896
923
  headers: Object.fromEntries(input.ctx.request.headers.entries()),
@@ -900,7 +927,7 @@ async function handleRouterAbEd25519NormalSigningRoute(input) {
900
927
  admissionAdapter: input.ctx.opts.routerAbNormalSigningAdmission,
901
928
  resolveEd25519MaterialActivation: input.ctx.service.walletRegistration.resolveEd25519MaterialActivation.bind(input.ctx.service.walletRegistration),
902
929
  phase: input.phase
903
- });
930
+ }));
904
931
  if (!authorization.ok) return json(authorization.result.body, { status: authorization.result.status });
905
932
  if (authorization.kind === "operation_step_up") {
906
933
  if (input.phase === "prepare") {
@@ -910,7 +937,7 @@ async function handleRouterAbEd25519NormalSigningRoute(input) {
910
937
  operation: authorization.operation
911
938
  });
912
939
  if (execution.kind !== "execute") return execution.response;
913
- const upstream = await proxyEd25519OwnerLaneExecution({
940
+ const upstream = await timeEd25519SigningStage(timing, "proxy", proxyEd25519OwnerLaneExecution({
914
941
  ctx: input.ctx,
915
942
  authorization,
916
943
  authorizedOperation: execution.operation,
@@ -921,7 +948,7 @@ async function handleRouterAbEd25519NormalSigningRoute(input) {
921
948
  binding: await ed25519OperationStepUpBinding(input.ctx, authorization.session, execution.operation)
922
949
  })
923
950
  }
924
- });
951
+ }));
925
952
  const upstreamBodyText = await upstream.clone().text().catch(() => "");
926
953
  let upstreamBody = null;
927
954
  try {
@@ -933,7 +960,7 @@ async function handleRouterAbEd25519NormalSigningRoute(input) {
933
960
  status: upstream.status,
934
961
  bodyText: upstreamBodyText
935
962
  })) return upstream;
936
- if (!upstream.ok || !isPlainObject(upstreamBody)) return requireCompletedEd25519OperationResponse(await completeEd25519Operation({
963
+ if (!upstream.ok || !isPlainObject(upstreamBody)) return requireCompletedEd25519OperationResponse(await timeEd25519SigningStage(timing, "complete", completeEd25519Operation({
937
964
  ctx: input.ctx,
938
965
  operation: execution.operation,
939
966
  result: upstream.status < 500 ? "failed_before_side_effect" : "failed_after_side_effect",
@@ -941,7 +968,7 @@ async function handleRouterAbEd25519NormalSigningRoute(input) {
941
968
  response: upstream,
942
969
  bodyText: upstreamBodyText
943
970
  })
944
- }));
971
+ })));
945
972
  const headers = new Headers(upstream.headers);
946
973
  headers.delete("content-length");
947
974
  return new Response(JSON.stringify(signingPrepareSessionResponse({
@@ -954,15 +981,15 @@ async function handleRouterAbEd25519NormalSigningRoute(input) {
954
981
  });
955
982
  }
956
983
  if (authorization.phase !== "finalize") throw new Error("Operation step-up finalize authorization phase changed");
957
- const validatedAuthorization = await validateEd25519VerifiedStepUpAuthorizedOperation({
984
+ const validatedAuthorization = await timeEd25519SigningStage(timing, "admit", validateEd25519VerifiedStepUpAuthorizedOperation({
958
985
  ctx: input.ctx,
959
986
  body: input.body,
960
987
  authorization
961
- });
988
+ }));
962
989
  if (!validatedAuthorization.ok) return validatedAuthorization.response;
963
990
  const replay = replayCompletedEd25519Operation(validatedAuthorization.operation);
964
991
  if (replay) return replay;
965
- const upstream = await proxyEd25519OwnerLaneExecution({
992
+ const upstream = await timeEd25519SigningStage(timing, "proxy", proxyEd25519OwnerLaneExecution({
966
993
  ctx: input.ctx,
967
994
  authorization,
968
995
  authorizedOperation: validatedAuthorization.operation,
@@ -973,13 +1000,13 @@ async function handleRouterAbEd25519NormalSigningRoute(input) {
973
1000
  binding: await ed25519OperationStepUpBinding(input.ctx, authorization.session, validatedAuthorization.operation)
974
1001
  })
975
1002
  }
976
- });
1003
+ }));
977
1004
  const upstreamBodyText = await upstream.clone().text().catch(() => "");
978
1005
  if (isRouterAbEd25519OperationInProgressResponse({
979
1006
  status: upstream.status,
980
1007
  bodyText: upstreamBodyText
981
1008
  })) return upstream;
982
- return requireCompletedEd25519OperationResponse(await completeEd25519Operation({
1009
+ return requireCompletedEd25519OperationResponse(await timeEd25519SigningStage(timing, "complete", completeEd25519Operation({
983
1010
  ctx: input.ctx,
984
1011
  operation: validatedAuthorization.operation,
985
1012
  result: upstream.ok ? "succeeded" : upstream.status < 500 ? "failed_before_side_effect" : "failed_after_side_effect",
@@ -987,14 +1014,14 @@ async function handleRouterAbEd25519NormalSigningRoute(input) {
987
1014
  response: upstream,
988
1015
  bodyText: upstreamBodyText
989
1016
  })
990
- }));
1017
+ })));
991
1018
  }
992
1019
  if (input.phase === "prepare") {
993
- const authorized = await authorizeEd25519ReusableWalletSessionOperation({
1020
+ const authorized = await timeEd25519SigningStage(timing, "admit", authorizeEd25519ReusableWalletSessionOperation({
994
1021
  ctx: input.ctx,
995
1022
  body: input.body,
996
1023
  authorization
997
- });
1024
+ }));
998
1025
  if (!authorized.ok) return authorized.response;
999
1026
  const execution = decideEd25519NormalSigningExecution({
1000
1027
  phase: "prepare",
@@ -1016,7 +1043,7 @@ async function handleRouterAbEd25519NormalSigningRoute(input) {
1016
1043
  }
1017
1044
  }
1018
1045
  }, { status: 403 });
1019
- const upstream = await proxyEd25519OwnerLaneExecution({
1046
+ const upstream = await timeEd25519SigningStage(timing, "proxy", proxyEd25519OwnerLaneExecution({
1020
1047
  ctx: input.ctx,
1021
1048
  authorization,
1022
1049
  authorizedOperation: execution.operation,
@@ -1027,7 +1054,7 @@ async function handleRouterAbEd25519NormalSigningRoute(input) {
1027
1054
  binding: buildEd25519GatewayOwnerWalletSessionBinding(authorization, pinnedEd25519WalletScope(authorization, authorized.admission.ownerScope))
1028
1055
  })
1029
1056
  }
1030
- });
1057
+ }));
1031
1058
  const upstreamBodyText = await upstream.clone().text().catch(() => "");
1032
1059
  let upstreamBody = null;
1033
1060
  try {
@@ -1039,7 +1066,7 @@ async function handleRouterAbEd25519NormalSigningRoute(input) {
1039
1066
  status: upstream.status,
1040
1067
  bodyText: upstreamBodyText
1041
1068
  })) return upstream;
1042
- if (!upstream.ok || !isPlainObject(upstreamBody)) return requireCompletedEd25519OperationResponse(await completeEd25519Operation({
1069
+ if (!upstream.ok || !isPlainObject(upstreamBody)) return requireCompletedEd25519OperationResponse(await timeEd25519SigningStage(timing, "complete", completeEd25519Operation({
1043
1070
  ctx: input.ctx,
1044
1071
  operation: execution.operation,
1045
1072
  result: upstream.status < 500 ? "failed_before_side_effect" : "failed_after_side_effect",
@@ -1047,7 +1074,7 @@ async function handleRouterAbEd25519NormalSigningRoute(input) {
1047
1074
  response: upstream,
1048
1075
  bodyText: upstreamBodyText
1049
1076
  })
1050
- }));
1077
+ })));
1051
1078
  const headers = new Headers(upstream.headers);
1052
1079
  headers.delete("content-length");
1053
1080
  return new Response(JSON.stringify(signingPrepareSessionResponse({
@@ -1059,16 +1086,16 @@ async function handleRouterAbEd25519NormalSigningRoute(input) {
1059
1086
  headers
1060
1087
  });
1061
1088
  }
1062
- const validatedAuthorization = await validateEd25519ReusableAuthorizedOperation({
1089
+ const validatedAuthorization = await timeEd25519SigningStage(timing, "admit", validateEd25519ReusableAuthorizedOperation({
1063
1090
  ctx: input.ctx,
1064
1091
  body: input.body,
1065
1092
  authorization
1066
- });
1093
+ }));
1067
1094
  if (!validatedAuthorization.ok) return validatedAuthorization.response;
1068
1095
  const replay = replayCompletedEd25519Operation(validatedAuthorization.operation);
1069
1096
  if (replay) return replay;
1070
1097
  if (!validatedAuthorization.ownerScope) throw new Error("Pinned owner Wallet Session scope is unavailable");
1071
- const upstream = await proxyEd25519OwnerLaneExecution({
1098
+ const upstream = await timeEd25519SigningStage(timing, "proxy", proxyEd25519OwnerLaneExecution({
1072
1099
  ctx: input.ctx,
1073
1100
  authorization,
1074
1101
  authorizedOperation: validatedAuthorization.operation,
@@ -1079,13 +1106,13 @@ async function handleRouterAbEd25519NormalSigningRoute(input) {
1079
1106
  binding: buildEd25519GatewayOwnerWalletSessionBinding(authorization, pinnedEd25519WalletScope(authorization, validatedAuthorization.ownerScope))
1080
1107
  })
1081
1108
  }
1082
- });
1109
+ }));
1083
1110
  const upstreamBodyText = await upstream.clone().text().catch(() => "");
1084
1111
  if (isRouterAbEd25519OperationInProgressResponse({
1085
1112
  status: upstream.status,
1086
1113
  bodyText: upstreamBodyText
1087
1114
  })) return upstream;
1088
- return requireCompletedEd25519OperationResponse(await completeEd25519Operation({
1115
+ return requireCompletedEd25519OperationResponse(await timeEd25519SigningStage(timing, "complete", completeEd25519Operation({
1089
1116
  ctx: input.ctx,
1090
1117
  operation: validatedAuthorization.operation,
1091
1118
  result: upstream.ok ? "succeeded" : upstream.status < 500 ? "failed_before_side_effect" : "failed_after_side_effect",
@@ -1093,7 +1120,7 @@ async function handleRouterAbEd25519NormalSigningRoute(input) {
1093
1120
  response: upstream,
1094
1121
  bodyText: upstreamBodyText
1095
1122
  })
1096
- }));
1123
+ })));
1097
1124
  }
1098
1125
  async function handleThresholdEd25519(ctx) {
1099
1126
  if (ctx.method === "GET" && ctx.pathname === "/router-ab/ed25519/healthz") {
@@ -44,7 +44,11 @@ function requireArray(value, label) {
44
44
  if (!Array.isArray(value)) throw new Error(`${label} must be an array`);
45
45
  return value;
46
46
  }
47
+ function assertString(val, name = "value") {
48
+ if (typeof val !== "string") throw new Error(`Invalid ${name}: expected string`);
49
+ return val;
50
+ }
47
51
  //#endregion
48
- export { asRecord, coerceNonEmptyString, isFunction, isObject, isPlainObject, requireArray, requireCanonicalString, requireNonEmptyString, requireRecord, requireRecordCopy, requireRecordOrArray, requireTrimmedString };
52
+ export { asRecord, assertString, coerceNonEmptyString, isFunction, isObject, isPlainObject, requireArray, requireCanonicalString, requireNonEmptyString, requireRecord, requireRecordCopy, requireRecordOrArray, requireTrimmedString };
49
53
 
50
54
  //# sourceMappingURL=validation.js.map
@@ -1,6 +1,5 @@
1
1
  import { normalizeOptionalTrimmedString } from "../../../../shared-ts/src/utils/normalize.js";
2
- import { createInMemorySigningSessionSealRateLimiter } from "./index.js";
3
- import { RedisTcpClient, UpstashRedisRestClient } from "../../../../core/ThresholdService/kv.js";
2
+ import { UpstashRedisRestClient } from "../../../../core/ThresholdService/kv.js";
4
3
  //#region src/threshold/session/signingSessionSeal/guards/backends.ts
5
4
  const WINDOW_COUNTER_LUA = `
6
5
  local key = KEYS[1]
@@ -87,105 +86,10 @@ var UpstashSigningSessionSealRateLimiter = class {
87
86
  }
88
87
  }
89
88
  };
90
- function redisRespToString(resp) {
91
- if (resp.type === "error") return null;
92
- if (resp.type === "bulk") return typeof resp.value === "string" ? resp.value : null;
93
- if (resp.type === "integer") return String(resp.value);
94
- if (resp.type === "simple") return resp.value;
95
- return null;
96
- }
97
- var RedisTcpSigningSessionSealRateLimiter = class {
98
- constructor(input) {
99
- this.client = new RedisTcpClient(input.redisUrl);
100
- this.keyPrefix = normalizeOptionalTrimmedString(input.keyPrefix);
101
- }
102
- withPrefix(key) {
103
- return this.keyPrefix ? `${this.keyPrefix}${key}` : key;
104
- }
105
- async consume(input) {
106
- const key = this.withPrefix(normalizeOptionalTrimmedString(input.key));
107
- if (!key) return {
108
- ok: false,
109
- code: "invalid_rate_limit_key",
110
- message: "Rate-limit key is required"
111
- };
112
- try {
113
- const resp = await this.client.send([
114
- "EVAL",
115
- WINDOW_COUNTER_LUA,
116
- "1",
117
- key,
118
- String(Math.max(1, Math.floor(Number(input.windowMs) || 0)))
119
- ]);
120
- if (resp.type === "error") return {
121
- ok: false,
122
- code: "internal",
123
- message: `Redis EVAL error: ${resp.value}`
124
- };
125
- const parsed = parseCounterWindow(redisRespToString(resp));
126
- if (!parsed) return {
127
- ok: false,
128
- code: "internal",
129
- message: "Invalid Redis rate-limit response"
130
- };
131
- return finalizeConsume({
132
- count: parsed.count,
133
- ttlMs: parsed.ttlMs,
134
- limit: Math.max(1, Math.floor(Number(input.limit) || 0)),
135
- nowMs: Math.floor(Number(input.nowMs) || 0)
136
- });
137
- } catch (error) {
138
- return {
139
- ok: false,
140
- code: "internal",
141
- message: error instanceof Error ? error.message : String(error || "Redis rate-limit failed")
142
- };
143
- }
144
- }
145
- };
146
89
  function createUpstashSigningSessionSealRateLimiter(options) {
147
90
  return new UpstashSigningSessionSealRateLimiter(options);
148
91
  }
149
- function createRedisTcpSigningSessionSealRateLimiter(options) {
150
- return new RedisTcpSigningSessionSealRateLimiter(options);
151
- }
152
- function resolveSigningSessionSealRateLimitFromEnv(input) {
153
- const limiterKind = normalizeOptionalTrimmedString(input.limiterKind || "").toLowerCase();
154
- const upstashUrl = normalizeOptionalTrimmedString(input.upstashUrl || "");
155
- const upstashToken = normalizeOptionalTrimmedString(input.upstashToken || "");
156
- const redisUrl = normalizeOptionalTrimmedString(input.redisUrl || "");
157
- const keyPrefix = normalizeOptionalTrimmedString(input.keyPrefix || "") || void 0;
158
- const selectedKind = limiterKind || (upstashUrl || upstashToken ? "upstash-redis-rest" : redisUrl ? "redis-tcp" : "in-memory");
159
- if (selectedKind === "upstash-redis-rest") {
160
- if (!upstashUrl || !upstashToken) throw new Error("Upstash Signing-session seal rate limiter requires both upstashUrl and upstashToken");
161
- return {
162
- limiter: createUpstashSigningSessionSealRateLimiter({
163
- url: upstashUrl,
164
- token: upstashToken,
165
- keyPrefix
166
- }),
167
- limit: input.limit,
168
- windowMs: input.windowMs
169
- };
170
- }
171
- if (selectedKind === "redis-tcp") {
172
- if (!redisUrl) throw new Error("Redis TCP Signing-session seal rate limiter requires redisUrl");
173
- return {
174
- limiter: createRedisTcpSigningSessionSealRateLimiter({
175
- redisUrl,
176
- keyPrefix
177
- }),
178
- limit: input.limit,
179
- windowMs: input.windowMs
180
- };
181
- }
182
- return {
183
- limiter: createInMemorySigningSessionSealRateLimiter(),
184
- limit: input.limit,
185
- windowMs: input.windowMs
186
- };
187
- }
188
92
  //#endregion
189
- export { createRedisTcpSigningSessionSealRateLimiter, createUpstashSigningSessionSealRateLimiter, resolveSigningSessionSealRateLimitFromEnv };
93
+ export { createUpstashSigningSessionSealRateLimiter };
190
94
 
191
95
  //# sourceMappingURL=backends.js.map