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
- 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
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
- 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
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
- Cloud runtime identity signing key is invalid
- Cloud runtime identity signature is invalid
- Access denied
- [plugin-kubernetes] egressMode=standard cannot enforce FQDN-
- UI parser path escapes package directory — skipping
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/d49a9e8977ff6ce4.
Report an issue: GitHub.