{"record":{"id":"62c8a211d46e2d53","repo":"paperclipai/paperclip","slug":"invalid-cloud-control-assertion","errorCode":"invalid_cloud_control_assertion","errorMessage":"invalid_cloud_control_assertion","messagePattern":"invalid_cloud_control_assertion","errorType":"http","errorClass":null,"httpStatus":401,"severity":"error","filePath":"server/src/middleware/cloud-control.ts","lineNumber":47,"sourceCode":"  return (req, res, next) => {\n    const assertion = req.get(CLOUD_CONTROL_HEADER)?.trim();\n    if (!assertion) {\n      next();\n      return;\n    }\n    const expectedAction = ACTION_BY_METHOD[req.method];\n    // Express's non-strict routing treats a trailing slash as the same\n    // route; the endpoint check must agree with it.\n    const normalizedPath = req.path.length > 1 && req.path.endsWith(\"/\") ? req.path.slice(0, -1) : req.path;\n    if (normalizedPath !== \"/api/instance/task-drain\" || !expectedAction) {\n      res.status(400).json({ error: \"cloud_control_wrong_endpoint\" });\n      return;\n    }\n    try {\n      verifyCloudControlAssertion({ compactJws: assertion, expectedAction });\n    } catch (error) {\n      logger.warn({ err: error }, \"Rejected Cloud control assertion\");\n      res.status(401).json({ error: \"invalid_cloud_control_assertion\" });\n      return;\n    }\n    req.actor = {\n      type: \"board\",\n      userId: \"paperclip-cloud\",\n      userName: \"Paperclip Cloud\",\n      userEmail: null,\n      isInstanceAdmin: true,\n      source: \"cloud_control\",\n    };\n    next();\n  };\n}\n","sourceCodeStart":29,"sourceCodeEnd":61,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/middleware/cloud-control.ts#L29-L61","documentation":"cloudControlMiddleware verified the request targets the correct endpoint, but verifyCloudControlAssertion rejected the compact JWS assertion — bad signature, expired token, wrong expected action, malformed JWS, or untrusted key. The middleware logs the rejection (without echoing the token) and returns 401 with code invalid_cloud_control_assertion, refusing to grant the 'paperclip-cloud' board actor.","triggerScenarios":"An assertion sent to /api/instance/task-drain fails verifyCloudControlAssertion: signature mismatch, assertion expired, assertion's action claim does not match the expectedAction for the HTTP method, or the token is structurally invalid.","commonSituations":"Clock skew between cloud and instance causing expiry; the cloud signed the assertion for one action but the request used another method; key rotation on the cloud side not yet picked up by the instance; truncated/mangled assertion in a proxy or header.","solutions":["Regenerate a fresh assertion from Paperclip Cloud and retry (rules out expiry)","Ensure the assertion's action claim matches the HTTP method used (per ACTION_BY_METHOD)","Verify instance/cloud clocks are synchronized (NTP) and the cloud signing keys are current on the instance","Inspect server logs for the 'Rejected Cloud control assertion' warning to see the underlying verification error"],"exampleFix":"// before: reusing an old cached assertion\nconst assertion = cachedAssertion;\n// after: mint per-request\nconst assertion = signCloudControlAssertion({ action: expectedAction, ttlSeconds: 60 });","handlingStrategy":"retry","validationCode":"// before sending, mint a fresh, short-lived assertion for the exact action\nconst assertion = signCloudControlAssertion({ action: expectedAction, issuedAt: Date.now(), ttlSeconds: 60 });\nif (decodeJwt(assertion).exp * 1000 < Date.now()) throw new Error(\"assertion already expired\");","typeGuard":null,"tryCatchPattern":"let res = await send(assertion);\nif (res.status === 401 && res.body?.error === \"invalid_cloud_control_assertion\") {\n  res = await send(signCloudControlAssertion({ action: expectedAction })); // one fresh-token retry\n}","preventionTips":["Mint a new assertion per request with a short TTL","Keep clocks synchronized (NTP) between cloud and instance","Ensure the action claim matches the HTTP method used","Re-sync cloud signing keys on the instance after rotation"],"tags":["auth","jwt","jws","cloud-control","middleware"],"backgroundTag":"jwt-token-expired","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}