Two surfaces reported different provenance for one process.
`gitea_get_runtime_context` read the live environment and reported
`client_managed`; `mcp_namespace_health.classify_namespace_probe` derived
provenance from `_safe_env_summary()`, whose `SAFE_ENV_KEYS` allowlist never
contained `GITEA_CLIENT_MANAGED`, `GITEA_MCP_CLIENT_MANAGED`, or
`GITEA_SERVER_PROVENANCE`. That lookup could only ever miss, so the health
surface was structurally incapable of returning anything but `manual_launch`.
Neither model could name which client or which session owned a runtime, so a
healthy daemon serving a second client was indistinguishable from a duplicate,
and the profile-wide duplicate scan walled the whole fleet.
Introduce `mcp_worker_identity` as the one authority, splitting two claims the
old code ran together:
* launch provenance — was this hand-launched from a terminal? Answered from the
environment, which is legitimate because the launcher sets it. Preserves the
#686 wall unchanged.
* session ownership — which live client session owns this runtime now? Answered
only from a live attachment record; no environment flag can establish it.
The module provides collision-resistant worker identities
(`<llm-name>-<UTC-timestamp>-<short-sha>`), an atomic SQLite registry with
fencing epochs, heartbeat-based liveness, generation takeover that supersedes
only a non-live claimant, cohort classification, and failure scoping.
Behaviour changes:
* Registering an existing worker identity fails closed; it is never replaced,
adopted, or merged with. The caller mints a different identity instead.
* A generation held by a live session cannot be claimed by a second one. A
generation whose claimant is not live is taken over with a higher fencing
epoch, so stale ownership cannot permanently strand a healthy daemon.
* A superseded session presenting an old epoch is refused and performs no write.
* Liveness comes from heartbeat freshness; a live PID cannot resurrect an
expired record, and a dead PID withdraws liveness.
* Workers sharing a role or profile no longer trigger a profile-wide duplicate
block, provided each carries a distinct identity. Processes with no identity
evidence remain classified as duplicates, so the #686 wall still holds.
* Runtime failures are scoped to a worker identity or generation, never to a
profile or the fleet.
* Reconnect guidance no longer defaults to Codex. An unidentified client gets
host-agnostic steps; `gitea_request_mcp_reconnect(client=...)` defaults to
resolving the client from the live attachment record.
* `resolve_bound_remote` keeps a bound namespace on its remote instead of
falling through to the `dadeschools` library default.
Absence of proof is now reported as `unproven` rather than asserted as
`manual_launch`. Both still fail closed — `is_client_managed` is unchanged, so
nothing previously refused is now permitted — but remediation names the proof
that is actually missing instead of describing a terminal launch it cannot
evidence. The #686 test is updated for that vocabulary and keeps every
wall-preserving assertion.
Threat-model anchors and their citations in docs/remote-mcp/threat-model.md are
restamped for the line movement in gitea_mcp_server.py.
Tests: tests/test_issue_948_client_session_provenance.py adds 43 cases covering
Codex/Gemini/Antigravity/Claude attachment, same-client new session, cross-client
takeover after a session ends, two live conflicting sessions, stale records,
missing attachment proof, environment flags without attachment, mixed
generations, duplicate cohorts, the hardcoded-client regression, explicit PRGS
selection, default-remote host drift, cross-surface agreement, and fail-closed
handling without false reconnect loops. Synthetic identifiers throughout.
Full suite from a branches/ worktree: 28F/5953P/6S at head vs 28F/5910P/6S at
merge base 8eada1fb, identical failing ID sets.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01F6Vomtndpq2gSBa88Tfcwy
247 lines
6.3 KiB
JSON
247 lines
6.3 KiB
JSON
{
|
|
"_comment": [
|
|
"Machine-checkable anchor table for docs/remote-mcp/threat-model.md (#956).",
|
|
"Every file:line anchor cited in the threat model must appear here, and the",
|
|
"source line at that anchor must contain the 'expect' substring.",
|
|
"tests/test_issue_956_threat_model.py enforces both directions, so a refactor",
|
|
"that shifts a line number fails the suite instead of silently rotting the",
|
|
"document. #930's inventory had no such guard and its gitea_mcp_server.py",
|
|
"anchors drifted between 7bf4f125 and aad5c8b4."
|
|
],
|
|
"generated_against_commit": "ca5f078d8a575ea3e2991771f8b4ea85e3dcaaa0",
|
|
"anchors": [
|
|
{
|
|
"anchor": "gitea_mcp_server.py:25087",
|
|
"expect": "mcp_daemon_guard.bind_native_mcp_transport()"
|
|
},
|
|
{
|
|
"anchor": "mcp_daemon_guard.py:49",
|
|
"expect": "_PRODUCTION_TRANSPORTS = mcp_transport_config.SUPPORTED_TRANSPORTS"
|
|
},
|
|
{
|
|
"anchor": "mcp_daemon_guard.py:195",
|
|
"expect": "def bind_native_mcp_transport"
|
|
},
|
|
{
|
|
"anchor": "irrecoverable_provenance.py:497",
|
|
"expect": "def assess_transport_for_auth_mint"
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:9192",
|
|
"expect": "assess_transport_for_auth_mint()"
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:9441",
|
|
"expect": "assess_transport_for_auth_mint()"
|
|
},
|
|
{
|
|
"anchor": "mcp_server.py:4",
|
|
"expect": "The transport is selected by deployment configuration"
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:15630",
|
|
"expect": "def _is_client_managed_process"
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:15644",
|
|
"expect": "def _provenance_mutation_block"
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:15652",
|
|
"expect": "unsupported_manual_launch"
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:19217",
|
|
"expect": "server_provenance"
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:21741",
|
|
"expect": "def _check_mcp_runtimes_diagnostics"
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:21761",
|
|
"expect": "\"ps\", \"-o\", \"pid,lstart,command\""
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:21805",
|
|
"expect": "\"ps\", \"eww\""
|
|
},
|
|
{
|
|
"anchor": "gitea_config.py:1172",
|
|
"expect": "RECOGNIZED_GITEA_ENV_KEYS"
|
|
},
|
|
{
|
|
"anchor": "gitea_config.py:1233",
|
|
"expect": "GITEA_CLIENT_MANAGED"
|
|
},
|
|
{
|
|
"anchor": "gitea_config.py:54",
|
|
"expect": "ENV_PROFILE = \"GITEA_MCP_PROFILE\""
|
|
},
|
|
{
|
|
"anchor": "gitea_config.py:97",
|
|
"expect": "_REVIEW_MERGE_OPS"
|
|
},
|
|
{
|
|
"anchor": "gitea_config.py:499",
|
|
"expect": "repository authorization scope"
|
|
},
|
|
{
|
|
"anchor": "gitea_config.py:956",
|
|
"expect": "def _keychain_token"
|
|
},
|
|
{
|
|
"anchor": "gitea_config.py:974",
|
|
"expect": "def resolve_token"
|
|
},
|
|
{
|
|
"anchor": "gitea_config.py:1015",
|
|
"expect": "def keychain_auth"
|
|
},
|
|
{
|
|
"anchor": "gitea_config.py:294",
|
|
"expect": "def _validate_identity_auth"
|
|
},
|
|
{
|
|
"anchor": "mcp_daemon_guard.py:583",
|
|
"expect": "def assert_keychain_access_allowed"
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:19487",
|
|
"expect": "def gitea_list_profiles"
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:19538",
|
|
"expect": "gitea_config.resolve_token(p)"
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:19851",
|
|
"expect": "def gitea_audit_config"
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:19873",
|
|
"expect": "service_summaries(config)"
|
|
},
|
|
{
|
|
"anchor": "gitea_config.py:704",
|
|
"expect": "def resolve_service"
|
|
},
|
|
{
|
|
"anchor": "gitea_config.py:837",
|
|
"expect": "def service_summaries"
|
|
},
|
|
{
|
|
"anchor": "gitea_config.py:851",
|
|
"expect": "_keychain_token(auth.get(\"id\"))"
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:17909",
|
|
"expect": "\"jenkins-mcp\""
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:17915",
|
|
"expect": "external-mcp"
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:17936",
|
|
"expect": "\"glitchtip-mcp\""
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:17941",
|
|
"expect": "external-mcp"
|
|
},
|
|
{
|
|
"anchor": "mcp_discoverability.py:9",
|
|
"expect": "EXPECTED_JENKINS_TOOLS"
|
|
},
|
|
{
|
|
"anchor": "mcp_discoverability.py:17",
|
|
"expect": "EXPECTED_GLITCHTIP_TOOLS"
|
|
},
|
|
{
|
|
"anchor": "sentry_incident_bridge.py:36",
|
|
"expect": "SENTRY_AUTH_TOKEN"
|
|
},
|
|
{
|
|
"anchor": "sentry_incident_bridge.py:190",
|
|
"expect": "def resolve_token"
|
|
},
|
|
{
|
|
"anchor": "sentry_incident_bridge.py:289",
|
|
"expect": "Authorization"
|
|
},
|
|
{
|
|
"anchor": "sentry_observability.py:55",
|
|
"expect": "SENTRY_DSN"
|
|
},
|
|
{
|
|
"anchor": "master_parity_gate.py:168",
|
|
"expect": "def capture_startup_parity"
|
|
},
|
|
{
|
|
"anchor": "master_parity_gate.py:255",
|
|
"expect": "mutation_safe"
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:19331",
|
|
"expect": "def gitea_assess_master_parity"
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:193",
|
|
"expect": "ACTIVE_WORKTREE_ENV"
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:194",
|
|
"expect": "AUTHOR_WORKTREE_ENV"
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:2352",
|
|
"expect": "/tmp/gitea_issue_lock.json"
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:10957",
|
|
"expect": "def gitea_bootstrap_author_issue_worktree"
|
|
},
|
|
{
|
|
"anchor": "mcp_server.py:13",
|
|
"expect": "/tmp/mcp_server_stderr.log"
|
|
},
|
|
{
|
|
"anchor": "issue_lock_store.py:26",
|
|
"expect": "DEFAULT_LOCK_DIR"
|
|
},
|
|
{
|
|
"anchor": "issue_lock_store.py:83",
|
|
"expect": "def session_pointer_path"
|
|
},
|
|
{
|
|
"anchor": "issue_lock_store.py:98",
|
|
"expect": "def is_process_alive"
|
|
},
|
|
{
|
|
"anchor": "mcp_session_state.py:27",
|
|
"expect": "DEFAULT_STATE_DIR"
|
|
},
|
|
{
|
|
"anchor": "control_plane_db.py:47",
|
|
"expect": "DEFAULT_DB_PATH"
|
|
},
|
|
{
|
|
"anchor": "control_plane_db.py:380",
|
|
"expect": "mode=0o700"
|
|
},
|
|
{
|
|
"anchor": "control_plane_db.py:386",
|
|
"expect": "sqlite3.connect"
|
|
},
|
|
{
|
|
"anchor": "control_plane_db.py:1145",
|
|
"expect": "os.getpid()"
|
|
},
|
|
{
|
|
"anchor": "gitea_mcp_server.py:12871",
|
|
"expect": "owner_pid_alive"
|
|
}
|
|
]
|
|
}
|