{"version":3,"sources":["../src/federation.ts"],"names":[],"mappings":"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","file":"federation.cjs","sourcesContent":["/**\n * Small, runtime-neutral request-signing primitives for app-to-app federation.\n *\n * Federation peers authenticate one bounded HTTP request with a shared vault\n * secret. They never exchange an end-user session or an odla-db application\n * key. The signature binds the sender, method, path/query, timestamp, nonce,\n * and exact body bytes. Callers remain responsible for authorizing the named\n * sender and, for non-idempotent operations, consuming the nonce exactly once.\n */\n\n/** Header names used by the version-1 federation signature envelope. */\nexport const FEDERATION_HEADERS = {\n  version: \"x-odla-federation\",\n  sender: \"x-odla-federation-sender\",\n  timestamp: \"x-odla-federation-timestamp\",\n  nonce: \"x-odla-federation-nonce\",\n  signature: \"x-odla-federation-signature\",\n} as const;\n\n/** Current wire version for signed federation requests. */\nexport const FEDERATION_VERSION = \"1\";\n\nconst DEFAULT_MAX_SKEW_MS = 5 * 60_000;\nconst SENDER = /^[a-z0-9][a-z0-9-]{1,62}$/;\nconst NONCE = /^[A-Za-z0-9_-]{16,128}$/;\nconst BASE64URL = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\";\n\n/** Input for {@link signFederatedRequest}. */\nexport interface SignFederatedRequestOptions {\n  /** Vaulted edge secret shared only by the two server runtimes. */\n  secret: string;\n  /** Stable app/site slug asserted by the sender. */\n  sender: string;\n  method: string;\n  url: string | URL;\n  /** Exact request body text. Omit for a bodyless request. */\n  body?: string;\n  /** Epoch milliseconds. Defaults to `Date.now()`. */\n  timestamp?: number;\n  /** Base64url-safe request nonce. Defaults to 128 random bits. */\n  nonce?: string;\n}\n\n/** Options for {@link verifyFederatedRequest}. */\nexport interface VerifyFederatedRequestOptions {\n  /** Vaulted edge secret expected for this route. */\n  secret: string;\n  /** Optional sender allowlist. An omitted list accepts any valid sender slug. */\n  senders?: readonly string[];\n  /** Epoch-millisecond clock injection. */\n  now?: () => number;\n  /** Maximum past/future clock skew. Defaults to five minutes. */\n  maxSkewMs?: number;\n  /**\n   * Optional replay gate. Return true only when this sender+nonce was consumed\n   * for the first time. Idempotent read routes can omit it.\n   */\n  consumeNonce?: (sender: string, nonce: string, timestamp: number) => boolean | Promise<boolean>;\n}\n\n/** Successful verification, including the cloned request body. */\nexport interface VerifiedFederatedRequest {\n  ok: true;\n  sender: string;\n  timestamp: number;\n  nonce: string;\n  body: string;\n}\n\n/** Stable failure reasons suitable for a route's audit log. */\nexport type FederationVerificationFailure =\n  | \"missing_headers\"\n  | \"unsupported_version\"\n  | \"invalid_sender\"\n  | \"sender_not_allowed\"\n  | \"invalid_timestamp\"\n  | \"expired\"\n  | \"invalid_nonce\"\n  | \"invalid_signature\"\n  | \"replayed\";\n\n/** Failed verification. No secret or submitted signature is reflected. */\nexport interface RejectedFederatedRequest {\n  ok: false;\n  reason: FederationVerificationFailure;\n}\n\n/** Result returned by {@link verifyFederatedRequest}. */\nexport type FederationVerification = VerifiedFederatedRequest | RejectedFederatedRequest;\n\nconst encoder = new TextEncoder();\n\nfunction bytesToBase64url(bytes: Uint8Array): string {\n  let output = \"\";\n  for (let offset = 0; offset < bytes.length; offset += 3) {\n    const a = bytes[offset] ?? 0;\n    const b = bytes[offset + 1] ?? 0;\n    const c = bytes[offset + 2] ?? 0;\n    const block = (a << 16) | (b << 8) | c;\n    output += BASE64URL[(block >>> 18) & 63];\n    output += BASE64URL[(block >>> 12) & 63];\n    if (offset + 1 < bytes.length) output += BASE64URL[(block >>> 6) & 63];\n    if (offset + 2 < bytes.length) output += BASE64URL[block & 63];\n  }\n  return output;\n}\n\nfunction randomNonce(): string {\n  const bytes = new Uint8Array(16);\n  crypto.getRandomValues(bytes);\n  return bytesToBase64url(bytes);\n}\n\nasync function sha256(value: string): Promise<string> {\n  const digest = await crypto.subtle.digest(\"SHA-256\", encoder.encode(value));\n  return bytesToBase64url(new Uint8Array(digest));\n}\n\n/** The key type as this runtime's Web Crypto reports it.\n *\n * Not the ambient `CryptoKey`: that name only exists with the DOM lib, and this\n * module is typechecked under more than one config — @odla-ai/cli maps\n * `@odla-ai/db` to this source and compiles it with Node libs only, where the\n * ambient name is absent. Deriving the type from the call keeps the module\n * correct under every consumer instead of requiring each to opt into DOM. */\ntype SubtleCryptoKey = Awaited<ReturnType<typeof crypto.subtle.importKey>>;\n\nasync function hmacKey(secret: string): Promise<SubtleCryptoKey> {\n  if (secret.length < 16) throw new Error(\"federation secret must contain at least 16 characters\");\n  return crypto.subtle.importKey(\n    \"raw\",\n    encoder.encode(secret),\n    { name: \"HMAC\", hash: \"SHA-256\" },\n    false,\n    [\"sign\", \"verify\"],\n  );\n}\n\nfunction requestTarget(url: string | URL): string {\n  const parsed = typeof url === \"string\" ? new URL(url) : url;\n  return `${parsed.pathname}${parsed.search}`;\n}\n\nasync function canonicalRequest(input: {\n  sender: string;\n  timestamp: number;\n  nonce: string;\n  method: string;\n  url: string | URL;\n  body: string;\n}): Promise<string> {\n  return [\n    FEDERATION_VERSION,\n    input.sender,\n    String(input.timestamp),\n    input.nonce,\n    input.method.toUpperCase(),\n    requestTarget(input.url),\n    await sha256(input.body),\n  ].join(\"\\n\");\n}\n\n/**\n * Sign one app-to-app HTTP request. Add the returned headers to the exact\n * method, URL path/query, and body passed here.\n */\nexport async function signFederatedRequest(\n  options: SignFederatedRequestOptions,\n): Promise<Record<string, string>> {\n  if (!SENDER.test(options.sender)) throw new Error(\"federation sender must be a lowercase app slug\");\n  const timestamp = options.timestamp ?? Date.now();\n  if (!Number.isSafeInteger(timestamp) || timestamp <= 0) throw new Error(\"federation timestamp must be epoch milliseconds\");\n  const nonce = options.nonce ?? randomNonce();\n  if (!NONCE.test(nonce)) throw new Error(\"federation nonce must be a 16–128 character base64url value\");\n  const body = options.body ?? \"\";\n  const canonical = await canonicalRequest({\n    sender: options.sender,\n    timestamp,\n    nonce,\n    method: options.method,\n    url: options.url,\n    body,\n  });\n  const signature = await crypto.subtle.sign(\"HMAC\", await hmacKey(options.secret), encoder.encode(canonical));\n  return {\n    [FEDERATION_HEADERS.version]: FEDERATION_VERSION,\n    [FEDERATION_HEADERS.sender]: options.sender,\n    [FEDERATION_HEADERS.timestamp]: String(timestamp),\n    [FEDERATION_HEADERS.nonce]: nonce,\n    [FEDERATION_HEADERS.signature]: bytesToBase64url(new Uint8Array(signature)),\n  };\n}\n\n/**\n * Verify one signed federation request. Verification reads a clone, leaving the\n * original request body available; the verified clone body is also returned so\n * route code can parse exactly the bytes covered by the signature.\n */\nexport async function verifyFederatedRequest(\n  request: Request,\n  options: VerifyFederatedRequestOptions,\n): Promise<FederationVerification> {\n  const version = request.headers.get(FEDERATION_HEADERS.version);\n  const sender = request.headers.get(FEDERATION_HEADERS.sender);\n  const timestampText = request.headers.get(FEDERATION_HEADERS.timestamp);\n  const nonce = request.headers.get(FEDERATION_HEADERS.nonce);\n  const signature = request.headers.get(FEDERATION_HEADERS.signature);\n  if (!version || !sender || !timestampText || !nonce || !signature) return { ok: false, reason: \"missing_headers\" };\n  if (version !== FEDERATION_VERSION) return { ok: false, reason: \"unsupported_version\" };\n  if (!SENDER.test(sender)) return { ok: false, reason: \"invalid_sender\" };\n  if (options.senders && !options.senders.includes(sender)) return { ok: false, reason: \"sender_not_allowed\" };\n\n  const timestamp = Number(timestampText);\n  if (!Number.isSafeInteger(timestamp) || timestamp <= 0) return { ok: false, reason: \"invalid_timestamp\" };\n  const maxSkewMs = options.maxSkewMs ?? DEFAULT_MAX_SKEW_MS;\n  if (!Number.isFinite(maxSkewMs) || maxSkewMs < 0) throw new Error(\"maxSkewMs must be a non-negative number\");\n  if (Math.abs((options.now?.() ?? Date.now()) - timestamp) > maxSkewMs) return { ok: false, reason: \"expired\" };\n  if (!NONCE.test(nonce)) return { ok: false, reason: \"invalid_nonce\" };\n\n  const body = await request.clone().text();\n  const canonical = await canonicalRequest({\n    sender,\n    timestamp,\n    nonce,\n    method: request.method,\n    url: request.url,\n    body,\n  });\n  let valid = false;\n  try {\n    const submitted = signature.split(\"\").map((char) => BASE64URL.indexOf(char));\n    if (submitted.some((value) => value < 0)) return { ok: false, reason: \"invalid_signature\" };\n    let accumulator = 0;\n    let bits = 0;\n    const bytes: number[] = [];\n    for (const value of submitted) {\n      accumulator = (accumulator << 6) | value;\n      bits += 6;\n      if (bits >= 8) {\n        bits -= 8;\n        bytes.push((accumulator >>> bits) & 255);\n      }\n    }\n    valid = await crypto.subtle.verify(\n      \"HMAC\",\n      await hmacKey(options.secret),\n      new Uint8Array(bytes),\n      encoder.encode(canonical),\n    );\n  } catch {\n    valid = false;\n  }\n  if (!valid) return { ok: false, reason: \"invalid_signature\" };\n  if (options.consumeNonce && !(await options.consumeNonce(sender, nonce, timestamp))) {\n    return { ok: false, reason: \"replayed\" };\n  }\n  return { ok: true, sender, timestamp, nonce, body };\n}\n"]}