{"record":{"id":"752db7f039339cc5","repo":"astrid-runtime/astrid","slug":"mcp-attach-registration-principal-is-not-the","errorCode":null,"errorMessage":"MCP attach registration principal '{}' is not the authenticated gateway principal '{}'","messagePattern":"MCP attach registration principal '(.+?)' is not the authenticated gateway principal '(.+?)'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/mcp/gateway.rs","lineNumber":946,"sourceCode":"        anyhow::bail!(\"MCP attach registration has an empty host\");\n    }\n    if registration.host_session_id.trim().is_empty() {\n        anyhow::bail!(\"MCP attach registration has an empty host_session_id\");\n    }\n    if registration.hook_token.trim().is_empty() {\n        anyhow::bail!(\"MCP attach registration is missing hook_token\");\n    }\n    validate_workspace(&registration.workspace_abs)?;\n    Ok(())\n}\n\nfn authenticate_registration(\n    registration: &AttachRegistration,\n    state: &GatewayState,\n) -> Result<astrid_core::PrincipalId> {\n    let principal = super::lifecycle::resolve_principal(Some(&registration.principal))?;\n    if principal != state.principal {\n        anyhow::bail!(\n            \"MCP attach registration principal '{}' is not the authenticated gateway principal '{}'\",\n            principal,\n            state.principal\n        );\n    }\n    if registration.hook_token != state.hook_token {\n        anyhow::bail!(\"MCP attach registration hook_token is invalid\");\n    }\n    Ok(principal)\n}\n\nfn mint_hook_token() -> String {\n    format!(\n        \"{}{}\",\n        Uuid::new_v4().as_simple(),\n        Uuid::new_v4().as_simple()\n    )\n}","sourceCodeStart":928,"sourceCodeEnd":964,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/mcp/gateway.rs#L928-L964","documentation":"authenticate_registration resolves the principal embedded in the attach registration and compares it to the principal the gateway itself was started for. If the client names a different principal than the authenticated gateway uplink, the request is treated as a forged-principal attempt and rejected. This is an authorization check tying attach requests to the gateway's own identity.","triggerScenarios":"Calling serve_attach with a registration whose principal field resolves to a PrincipalId different from state.principal; attempting to select another user's/principal's gateway uplink.","commonSituations":"Multiple gateways running for different principals and the client attached to the wrong socket; stale registration payload reused from a previous gateway instance with a different principal; misconfigured client config pointing at another principal's workspace.","solutions":["Attach to the gateway socket belonging to your own principal (check which gateway process/lease you started)","Re-resolve the principal the same way the gateway does (super::lifecycle::resolve_principal) and confirm they match","Restart the gateway for your principal and rebuild the registration from its fresh readiness record","Remove stale client config/cache referencing an old principal"],"exampleFix":"// before\nprincipal: some_other_principal,\n// after\nprincipal: resolve_principal(Some(&my_principal_config))?,","handlingStrategy":"validation","validationCode":"let principal = resolve_principal(Some(&reg.principal))?;\nif principal != gateway_state.principal { return Err(\"attach to your own principal's gateway\"); }","typeGuard":null,"tryCatchPattern":"match authenticate_registration(&reg, &state) {\n    Ok(p) => proceed(p),\n    Err(e) if e.to_string().contains(\"not the authenticated gateway principal\") => reconnect_to_own_socket(),\n    Err(e) => return Err(e),\n}","preventionTips":["Discover the gateway socket from your own startup lease, not global discovery","Rebuild registrations after gateway restarts (principal may change)","Keep per-principal runtime dirs separate"],"tags":["authentication","security","mcp"],"backgroundTag":"permission-denied","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}