paperclipai/paperclip · error

Cloud runtime identity uses an unknown signing key

Error message

Cloud runtime identity uses an unknown signing key

What it means

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.

Source

Thrown at server/src/services/cloud-runtime-identity.ts:252

function publicKeyForKid(env: NodeJS.ProcessEnv, kid: string) {
  const raw = nonEmpty(env.PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS);
  if (!raw) throw new Error("PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS is not configured");
  let parsed: unknown;
  try {
    parsed = JSON.parse(raw);
  } catch {
    throw new Error("PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS is invalid");
  }
  const keys = parsed && typeof parsed === "object" && !Array.isArray(parsed)
    ? (parsed as { keys?: unknown }).keys
    : undefined;
  if (!Array.isArray(keys)) throw new Error("PAPERCLIP_CLOUD_RUNTIME_IDENTITY_JWKS is invalid");
  const matches = keys.filter((candidate): candidate is JsonWebKey & { kid: string } => {
    if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return false;
    const key = candidate as JsonWebKey & { kid?: unknown };
    return key.kid === kid;
  });
  if (matches.length !== 1) throw new Error("Cloud runtime identity uses an unknown signing key");
  const jwk = matches[0];
  if (jwk.kty !== "OKP" || jwk.crv !== "Ed25519" || jwk.use !== "sig" || jwk.alg !== "EdDSA" || !jwk.x || jwk.d) {
    throw new Error("Cloud runtime identity signing key is invalid");
  }
  return createPublicKey({ key: jwk, format: "jwk" });
}

function verifyClaims(input: {
  compactJws: string;
  env: NodeJS.ProcessEnv;
  now: Date;
}): RuntimeIdentityClaims {
  const parts = input.compactJws.split(".");
  if (parts.length !== 3 || parts.some((part) => part.length === 0)) {
    throw new Error("Cloud runtime identity assertion is not a compact JWS");
  }
  const [encodedHeader, encodedPayload, encodedSignature] = parts;
  const header = decodeJsonPart(encodedHeader, "protected header");

View on GitHub (pinned to 01ad858492)

Solutions

  1. Ensure the JWKS contains the Ed25519 public JWK (kty OKP, crv Ed25519, use sig, alg EdDSA) exactly as published by the control plane
  2. Remove any 'd' field — never put the private key in the verifier's JWKS env var
  3. Re-export the key with the correct parameters (e.g. with node:crypto createPublicKey/export({format:'jwk'}))
  4. Regenerate a fresh Ed25519 keypair and reissue assertions if the signing key type is wrong

Example fix

// before: private key in verifier JWKS
{"keys":[{"kty":"OKP","crv":"Ed25519","kid":"k1","x":"...","d":"..."}]}
// after: public-only JWK
{"keys":[{"kty":"OKP","crv":"Ed25519","use":"sig","alg":"EdDSA","kid":"k1","x":"..."}]}
Defensive patterns

Strategy: validation

Validate before calling

function isEd25519SigJwk(jwk: Record<string, unknown>): jwk is JsonWebKey & { kid: string; x: string } {
  return jwk.kty === "OKP" && jwk.crv === "Ed25519" && jwk.use === "sig"
    && jwk.alg === "EdDSA" && typeof jwk.x === "string" && jwk.x.length > 0 && !("d" in jwk);
}
// startup: JSON.parse(jwks).keys.every(isEd25519SigJwk)

Type guard

function isPublicJwk(jwk: Record<string, unknown>): boolean {
  return !("d" in jwk); // public keys must not carry the private scalar
}

Try / catch

try {
  verifyCloudRuntimeIdentityAssertion({ compactJws: assertion, expectedPreviousOrigin: prev });
} catch (e) {
  if (String((e as Error).message).includes("signing key is invalid")) {
    logger.error("JWKS entry is not a public Ed25519 signing key; re-export the public JWK", { error: e });
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/d49a9e8977ff6ce4. Report an issue: GitHub.