{"record":{"id":"d49a9e8977ff6ce4","repo":"paperclipai/paperclip","slug":"cloud-runtime-identity-uses-an-unknown-signing-key","errorCode":null,"errorMessage":"Cloud runtime identity uses an unknown signing key","messagePattern":"Cloud runtime identity uses an unknown signing key","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/cloud-runtime-identity.ts","lineNumber":252,"sourceCode":"function publicKeyForKid(env: NodeJS.ProcessEnv, kid: string) {\n  const raw = nonEmpty(env.PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS);\n  if (!raw) throw new Error(\"PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS is not configured\");\n  let parsed: unknown;\n  try {\n    parsed = JSON.parse(raw);\n  } catch {\n    throw new Error(\"PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS is invalid\");\n  }\n  const keys = parsed && typeof parsed === \"object\" && !Array.isArray(parsed)\n    ? (parsed as { keys?: unknown }).keys\n    : undefined;\n  if (!Array.isArray(keys)) throw new Error(\"PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS is invalid\");\n  const matches = keys.filter((candidate): candidate is JsonWebKey & { kid: string } => {\n    if (!candidate || typeof candidate !== \"object\" || Array.isArray(candidate)) return false;\n    const key = candidate as JsonWebKey & { kid?: unknown };\n    return key.kid === kid;\n  });\n  if (matches.length !== 1) throw new Error(\"Cloud runtime identity uses an unknown signing key\");\n  const jwk = matches[0];\n  if (jwk.kty !== \"OKP\" || jwk.crv !== \"Ed25519\" || jwk.use !== \"sig\" || jwk.alg !== \"EdDSA\" || !jwk.x || jwk.d) {\n    throw new Error(\"Cloud runtime identity signing key is invalid\");\n  }\n  return createPublicKey({ key: jwk, format: \"jwk\" });\n}\n\nfunction verifyClaims(input: {\n  compactJws: string;\n  env: NodeJS.ProcessEnv;\n  now: Date;\n}): RuntimeIdentityClaims {\n  const parts = input.compactJws.split(\".\");\n  if (parts.length !== 3 || parts.some((part) => part.length === 0)) {\n    throw new Error(\"Cloud runtime identity assertion is not a compact JWS\");\n  }\n  const [encodedHeader, encodedPayload, encodedSignature] = parts;\n  const header = decodeJsonPart(encodedHeader, \"protected header\");","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/cloud-runtime-identity.ts#L234-L270","documentation":"Thrown by publicKeyForKid when the JWKS entry whose kid matches the assertion is not a valid Ed25519 signing public key. The library only accepts OKP/Ed25519 keys with use=sig, alg=EdDSA, a present x (public point), and no d (private scalar) — private keys must never appear in the verifier's JWKS.","triggerScenarios":"The matching JWK has kty != \"OKP\", crv != \"Ed25519\", use != \"sig\", alg != \"EdDSA\", a missing/empty x, or a present d field; raised at line 255 after the kid match succeeded.","commonSituations":"The JWKS was misconfigured with an RSA or EC key instead of Ed25519; the private JWK (with d) was accidentally published into the verifier's env var; key metadata fields (use/alg) were omitted or hand-edited incorrectly.","solutions":["Ensure the JWKS contains the Ed25519 public JWK (kty OKP, crv Ed25519, use sig, alg EdDSA) exactly as published by the control plane","Remove any 'd' field — never put the private key in the verifier's JWKS env var","Re-export the key with the correct parameters (e.g. with node:crypto createPublicKey/export({format:'jwk'}))","Regenerate a fresh Ed25519 keypair and reissue assertions if the signing key type is wrong"],"exampleFix":"// before: private key in verifier JWKS\n{\"keys\":[{\"kty\":\"OKP\",\"crv\":\"Ed25519\",\"kid\":\"k1\",\"x\":\"...\",\"d\":\"...\"}]}\n// after: public-only JWK\n{\"keys\":[{\"kty\":\"OKP\",\"crv\":\"Ed25519\",\"use\":\"sig\",\"alg\":\"EdDSA\",\"kid\":\"k1\",\"x\":\"...\"}]}","handlingStrategy":"validation","validationCode":"function isEd25519SigJwk(jwk: Record<string, unknown>): jwk is JsonWebKey & { kid: string; x: string } {\n  return jwk.kty === \"OKP\" && jwk.crv === \"Ed25519\" && jwk.use === \"sig\"\n    && jwk.alg === \"EdDSA\" && typeof jwk.x === \"string\" && jwk.x.length > 0 && !(\"d\" in jwk);\n}\n// startup: JSON.parse(jwks).keys.every(isEd25519SigJwk)","typeGuard":"function isPublicJwk(jwk: Record<string, unknown>): boolean {\n  return !(\"d\" in jwk); // public keys must not carry the private scalar\n}","tryCatchPattern":"try {\n  verifyCloudRuntimeIdentityAssertion({ compactJws: assertion, expectedPreviousOrigin: prev });\n} catch (e) {\n  if (String((e as Error).message).includes(\"signing key is invalid\")) {\n    logger.error(\"JWKS entry is not a public Ed25519 signing key; re-export the public JWK\", { error: e });\n  } else throw e;\n}","preventionTips":["Only ever place PUBLIC keys in the verifier's JWKS env var; keep d out","Always emit use:\"sig\" and alg:\"EdDSA\" when exporting JWKs for this protocol","Validate each JWKS entry with createPublicKey({key:jwk,format:'jwk'}) in a startup smoke test","Generate keys specifically as Ed25519 for this protocol; don't reuse RSA/EC keys"],"tags":["jwks","ed25519","key-validation","security"],"backgroundTag":"invalid-config-value","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}