{"record":{"id":"e80a494491c60e36","repo":"paperclipai/paperclip","slug":"description-must-be-owned-by-the-paperclip-proc","errorCode":null,"errorMessage":"${description} must be owned by the Paperclip process user","messagePattern":"(.+?) must be owned by the Paperclip process user","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/src/services/decision-signing.ts","lineNumber":18,"sourceCode":"import { createHmac, randomBytes, timingSafeEqual } from \"node:crypto\";\nimport { chmodSync, linkSync, lstatSync, mkdirSync, readFileSync, type Stats, unlinkSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { resolveDefaultSecretsKeyFilePath } from \"../home-paths.js\";\n\nconst VERSION = \"decision-spec-v1\";\nconst MIN_SECRET_LENGTH = 32;\n\nfunction resolveGeneratedSecretFilePath() {\n  return path.join(path.dirname(resolveDefaultSecretsKeyFilePath()), \"decision-signing.key\");\n}\n\nfunction assertOwnedByCurrentUser(stats: Stats, description: string) {\n  if (process.platform === \"win32\") return;\n\n  const currentUserId = process.getuid?.();\n  if (currentUserId !== undefined && stats.uid !== currentUserId) {\n    throw new Error(`${description} must be owned by the Paperclip process user`);\n  }\n}\n\nfunction enforceKeyFilePermissions(keyPath: string) {\n  let stats = lstatSync(keyPath);\n  if (!stats.isFile()) {\n    throw new Error(`Decision signing key at ${keyPath} must be a regular file`);\n  }\n  assertOwnedByCurrentUser(stats, `Decision signing key at ${keyPath}`);\n  if (process.platform === \"win32\") return;\n\n  const mode = stats.mode & 0o777;\n  if ((mode & 0o077) !== 0) {\n    chmodSync(keyPath, 0o600);\n    stats = lstatSync(keyPath);\n    if (!stats.isFile()) {\n      throw new Error(`Decision signing key at ${keyPath} must be a regular file`);\n    }","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/paperclipai/paperclip/blob/120ae5428fa29bee300bcf806491cd4d965fbb7c/server/src/services/decision-signing.ts#L1-L36","documentation":"Ownership guard in decision-signing (assertOwnedByCurrentUser, skipped on Windows): the key file or secrets directory's uid does not match the Paperclip process's uid. File-based secret material must be owned by the running user to prevent cross-user tampering with decision signing.","triggerScenarios":"Thrown at server/src/services/decision-signing.ts:18 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Change ownership of the named file/directory to the Paperclip process user (chown)."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"120ae5428fa29bee300bcf806491cd4d965fbb7c","analyzedAt":"2026-08-18T22:49:45.177Z","contentChangedAt":"2026-08-18T22:49:45.177Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}