{"record":{"id":"bc1eb366c55a9a03","repo":"paperclipai/paperclip","slug":"paperclip-cloud-runtime-identity-jwks-is-not-confi","errorCode":null,"errorMessage":"PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS is not configured","messagePattern":"PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS is not configured","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/cloud-runtime-identity.ts","lineNumber":236,"sourceCode":"}\n\nfunction decodeJsonPart(part: string, label: string): Record<string, unknown> {\n  if (!/^[A-Za-z0-9_-]+$/.test(part)) throw new Error(`Cloud runtime identity has an invalid ${label}`);\n  let parsed: unknown;\n  try {\n    parsed = JSON.parse(Buffer.from(part, \"base64url\").toString(\"utf8\"));\n  } catch {\n    throw new Error(`Cloud runtime identity has an invalid ${label}`);\n  }\n  if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n    throw new Error(`Cloud runtime identity has an invalid ${label}`);\n  }\n  return parsed as Record<string, unknown>;\n}\n\nfunction 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) {","sourceCodeStart":218,"sourceCodeEnd":254,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/cloud-runtime-identity.ts#L218-L254","documentation":"Thrown by publicKeyForKid when the PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS environment variable is unset or empty while verifying a cloud runtime identity assertion. The verifier needs the trusted JWKS containing the Ed25519 public key referenced by the assertion's kid; without it no assertion can be verified.","triggerScenarios":"verifyCloudRuntimeIdentityAssertion is called (e.g. during instance identity claim at startup or an auth flow), the JWS header parses fine, but process.env has no non-empty PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS value.","commonSituations":"Self-hosted/off-Cloud deployment where the JWKS env var was never provisioned; the env var was dropped in a Docker/Kubernetes config update or .env file not loaded; typo in the variable name in the deployment manifest.","solutions":["Set PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS in the server environment to the JWKS JSON published by the Cloud control plane","Restart the server after adding the env var so the new environment is picked up","Verify the variable name spelling in your deployment config (docker-compose, k8s secret, .env)","If running off-Cloud intentionally, disable the cloud identity claim path instead of providing an empty value"],"exampleFix":"// before\n// (env missing)\n// after (k8s)\nenv:\n  - name: PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS\n    valueFrom:\n      secretKeyRef: { name: paperclip-cloud, key: runtime-identity-jwks }","handlingStrategy":"validation","validationCode":"if (!process.env.PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS) {\n  throw new Error(\"PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS must be set before verifying cloud identity assertions\");\n}","typeGuard":"function hasJwks(env: NodeJS.ProcessEnv): env is NodeJS.ProcessEnv & { PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS: string } {\n  return typeof env.PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS === \"string\" && env.PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS.length > 0;\n}","tryCatchPattern":"try {\n  verifyCloudRuntimeIdentityAssertion({ compactJws: assertion, expectedPreviousOrigin: prev });\n} catch (e) {\n  if (String((e as Error).message).includes(\"is not configured\")) {\n    logger.error(\"JWKS env var missing; cannot verify cloud runtime identity\", { error: e });\n    // fail startup or fall back to non-cloud identity path\n  } else throw e;\n}","preventionTips":["Provision PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS as part of deployment templates/secrets from day one","Add a startup config check that fails fast with a clear message when the var is missing","Keep the env var name in one constants module to avoid typos across manifests","Document the requirement in deployment docs for off-Cloud vs Cloud installs"],"tags":["env-var","configuration","jwt","security"],"backgroundTag":"missing-env-var","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}