{"record":{"id":"5cacf88d649b7b13","repo":"ruvnet/ruflo","slug":"policy-administration-requires-an-authenticated-us","errorCode":null,"errorMessage":"policy administration requires an authenticated user context","messagePattern":"policy administration requires an authenticated user context","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/mcp-tools/policy-tools.ts","lineNumber":24,"sourceCode":"  PolicyState,\n} from '@claude-flow/security';\nimport type { MCPTool } from './types.js';\nimport {\n  evaluatePolicyRequest,\n  issuePolicyApproval,\n  loadPolicyState,\n  revokePolicyApproval,\n  setPolicyBudget,\n  setPolicyMode,\n  upsertPolicyRule,\n  verifyPolicyLedger,\n} from '../services/policy-runtime.js';\n\nfunction requireAuthenticatedAdministrator(\n  context: Record<string, unknown> | undefined,\n): asserts context is Record<string, unknown> & { principalId: string; principalType: 'user' } {\n  if (context?.principalType !== 'user' || typeof context.principalId !== 'string') {\n    throw new Error('policy administration requires an authenticated user context');\n  }\n}\n\nexport const policyTools: MCPTool[] = [\n  {\n    name: 'policy_evaluate',\n    description: 'Evaluate an agent action against ADR-324 policy and persist a tamper-evident decision receipt. Use when a consequential tool, deployment, network, spend, or promotion action needs authorization.',\n    category: 'security',\n    inputSchema: {\n      type: 'object',\n      properties: {\n        request: { type: 'object', description: 'Policy request containing identity, action, and optional evidence/envelope context' },\n      },\n      required: ['request'],\n    },\n    handler: async (input, context) => evaluatePolicyRequest(\n      input.request as PolicyRequest,\n      typeof context?.projectRoot === 'string' ? context.projectRoot : process.cwd(),","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/mcp-tools/policy-tools.ts#L6-L42","documentation":"All mutating policy admin tools (policy_rule_upsert, policy_budget_set, policy_approve, policy_revoke) call requireAuthenticatedAdministrator(context), which asserts the tool-call context has principalType === 'user' AND a string principalId. Anything else — an agent principal, a service identity, or a missing context — throws this error before any policy state is loaded or written. It is an intentional authorization gate: agents may evaluate policy (policy_evaluate) but only authenticated humans may change it.","triggerScenarios":"Invoking any of policy_rule_upsert / policy_budget_set / policy_approve / policy_revoke with context = undefined, context.principalType = 'agent', or principalId missing/non-string. This happens when the MCP client does not forward user identity in the tool-call context, or when an agent-driven automation tries to self-approve a policy change.","commonSituations":"MCP server wiring that never populates the principal context; scripts/CI calling admin policy tools with a service account; an agent loop attempting to escalate its own permissions via policy_approve (correctly blocked); upgrading from a version where these tools were unauthenticated.","solutions":["Route policy administration through a call path that supplies context = { principalType: 'user', principalId: '<user id>' }.","Use policy_evaluate and policy_status for non-human callers — they have no admin requirement.","If you are the MCP host, propagate the authenticated user's identity into the tool-call context before dispatching admin tools.","Do not work around this by faking a user principal from agent code — the gate exists so agents cannot self-authorize (ADR-324)."],"exampleFix":"// before\nawait mcp.call('policy_rule_upsert', { rule: {...} }); // context undefined -> throws\n\n// after\nawait mcp.callWithUser('policy_rule_upsert', { rule: {...} }, userPrincipal); // context = { principalType: 'user', principalId: user.id }","handlingStrategy":"type-guard","validationCode":"function hasUserPrincipal(context: unknown): boolean {\n  return typeof context === 'object' && context !== null &&\n    (context as any).principalType === 'user' &&\n    typeof (context as any).principalId === 'string';\n}\nif (!hasUserPrincipal(toolContext)) {\n  return { error: 'policy administration is reserved for authenticated humans; use policy_evaluate/policy_status instead' };\n}\nawait callPolicyAdminTool(args, toolContext);","typeGuard":"interface UserContext { principalId: string; principalType: 'user' }\nfunction isUserContext(c: unknown): c is UserContext {\n  return typeof c === 'object' && c !== null &&\n    (c as Record<string, unknown>).principalType === 'user' &&\n    typeof (c as Record<string, unknown>).principalId === 'string' &&\n    (c as Record<string, unknown>).principalId.length > 0;\n}","tryCatchPattern":"try {\n  await callTool('policy_rule_upsert', args, context);\n} catch (e) {\n  if (e instanceof Error && e.message === 'policy administration requires an authenticated user context') {\n    return { error: 'Sign in as a user principal to administer policy; agents may only evaluate it.' };\n  }\n  throw e;\n}","preventionTips":["Gate admin policy tools in your UI/automation behind an explicit user-auth check before dispatch.","Give agent/service callers only policy_evaluate and policy_status in their tool allowlist.","Never synthesize a { principalType: 'user' } context from non-human code — it defeats the ADR-324 authorization boundary."],"tags":["policy","authorization","authentication","mcp","security"],"backgroundTag":"authentication-required","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}