fix(controller): production client_instance_id launch path and instance-aware mutation gate

Remediate review 655 blockers on PR #979 (issue #978):

B1 — Production launcher (mcp_application_launcher) mints one trusted
client_instance_id per application launch and propagates it to all five
namespace workers via GITEA_MCP_CLIENT_INSTANCE. launcher_entry and
multi_namespace_launcher_entries use that path. Workers never invent a
trusted ID; missing/malformed/user-supplied values fail closed.

B2 — _check_mcp_runtimes_diagnostics is instance-aware: two legitimate
instances sharing a profile are allowed when each has a distinct trusted
client_instance_id; duplicate workers for the same (instance, profile)
still fail closed. Worker identity/generation exported for peer scans.

Tests cover shared ID across five namespaces, distinct launches, multi-
instance same profile, same-instance duplicates, untrusted attribution,
and the production launcher path.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
2026-07-30 10:12:16 -04:00
co-authored by Claude Opus 4.8
parent 4a1cc63e94
commit 0dbff6dcd5
7 changed files with 1055 additions and 71 deletions
+33 -6
View File
@@ -49,27 +49,54 @@ model (#949 assumption) as the operating rule for multi-instance fleets.
## How five workers join one instance
1. The host starts one application instance (for example one Codex session).
2. The trusted launcher mints `client_instance_id` (see
`mcp_fleet_snapshot.generate_client_instance_id`) and injects:
2. The **production application launcher**
(`mcp_application_launcher.build_application_mcp_servers` /
`gitea_config.multi_namespace_launcher_entries`) mints exactly one
`client_instance_id` via `mcp_fleet_snapshot.generate_client_instance_id`
and injects the same env into every namespace worker:
```text
GITEA_MCP_CLIENT=<client_type>
GITEA_MCP_CLIENT_INSTANCE=<client_instance_id>
GITEA_MCP_INSTANCE_PROVENANCE=trusted_launcher
GITEA_MCP_CLIENT_SESSION=<session_id> # optional but recommended
GITEA_MCP_FLEET_RUN_ID=<enrollment id> # when on an approved canary
GITEA_CLIENT_MANAGED=1
GITEA_MCP_PROFILE=<role-profile>
```
3. The launcher starts the five MCP namespace processes (or attaches five
role profiles) with **that same environment**.
3. The MCP client starts the five namespace processes (`gitea-author`,
`gitea-reviewer`, `gitea-merger`, `gitea-controller`, `gitea-reconciler`)
from that generated `mcpServers` map — each entry carries the **same**
instance ID.
4. Each worker registers once into the worker registry with its own
`worker_identity`, `namespace`, `generation_id`, and PID, but the shared
`client_instance_id`.
`client_instance_id`. Workers never mint a trusted instance ID themselves.
Only IDs matching the launcher format `inst-<client>-<timestamp>-<digest>`
are trusted. Missing, `legacy-pid-*` / `pid-*` placeholders, and ordinary
user-supplied strings fail closed as untrusted and cannot authorize
multi-instance fleet mutation safety.
Worker reconnect (same process, same registration) keeps the instance ID.
Worker restart (new process) mints a new worker identity and generation but
must still receive the same `GITEA_MCP_CLIENT_INSTANCE` from the parent
application if it is the same launch. Full application restart mints a new
`client_instance_id`.
`client_instance_id` (call the launcher without a prior ID).
### Mutation gate (instance-aware)
The capability / runtime diagnostic gate
(`_check_mcp_runtimes_diagnostics`) is **instance-aware**:
* Two legitimate application instances that share a profile (same
`GITEA_MCP_PROFILE`) are allowed when each has a distinct trusted
`client_instance_id`.
* More than one live worker for the same
`(client_instance_id, profile/namespace)` fails closed as a duplicate
namespace worker.
* Multiple processes sharing a profile **without** trusted instance
evidence still fail closed (indistinguishable from a duplicate).
## Approved fleet manifest
+67 -15
View File
@@ -1206,6 +1206,12 @@ RECOGNIZED_GITEA_ENV_KEYS = frozenset({
# #978: operator-approved fleet enrollment identity shared by one launch.
"GITEA_MCP_FLEET_RUN_ID",
"GITEA_MCP_PROCESS_IDENTITY",
# #978 B1/B2: launcher-sealed provenance + peer-visible worker identity so
# the instance-aware mutation gate can distinguish two legitimate
# application instances that share a profile from a true duplicate worker.
"GITEA_MCP_INSTANCE_PROVENANCE",
"GITEA_MCP_WORKER_IDENTITY",
"GITEA_MCP_GENERATION_ID",
# #975 review 652 B1: production also consumes HEARTBEAT_INTERVAL_ENV from
# mcp_worker_identity via gitea_mcp_server._start_worker_heartbeat. Omitting
# it reproduced the same unsupported-env → runtime_reconnect_required
@@ -1239,24 +1245,70 @@ def get_unconsumed_gitea_env_overrides(env=None) -> dict[str, str]:
return unconsumed
def launcher_entry(profile_name, config_path=None):
def launcher_entry(
profile_name,
config_path=None,
*,
client_type=None,
client_instance_id=None,
fleet_run_id=None,
session_id=None,
launch_nonce=None,
):
"""Return a thin MCP launcher entry for *profile_name*.
Contains command/args and the GITEA_MCP_* / GITEA_CLIENT_MANAGED env vars — never a token
or password. Suitable for Claude / Gemini / Codex ``mcpServers`` blocks.
Contains command/args and the GITEA_MCP_* / GITEA_CLIENT_MANAGED env vars —
never a token or password. Suitable for Claude / Gemini / Codex
``mcpServers`` blocks.
#978 B1: production launches mint (or reuse) a trusted
``GITEA_MCP_CLIENT_INSTANCE`` so workers never fall back to the legacy
placeholder identity on the real serve path. Pass *client_instance_id* to
resume the same launch; omit it for a fresh launch (new ID).
"""
command, args = server_command()
return {
"gitea-tools": {
"command": command,
"args": args,
"env": {
"GITEA_MCP_CONFIG": config_path or DEFAULT_CONFIG_PATH,
"GITEA_MCP_PROFILE": profile_name,
"GITEA_CLIENT_MANAGED": "1",
},
}
}
import mcp_application_launcher as app_launcher
built = app_launcher.launcher_entry_for_profile(
profile_name,
client_type=client_type or "unknown",
config_path=config_path or DEFAULT_CONFIG_PATH,
client_instance_id=client_instance_id,
fleet_run_id=fleet_run_id,
session_id=session_id,
launch_nonce=launch_nonce,
server_key="gitea-tools",
)
# Public shape stays {server_key: {command, args, env}} for existing callers.
return {"gitea-tools": built["gitea-tools"]}
def multi_namespace_launcher_entries(
profile_by_namespace,
*,
client_type,
config_path=None,
client_instance_id=None,
fleet_run_id=None,
session_id=None,
launch_nonce=None,
):
"""Build production mcpServers for all five namespaces of one application launch.
One shared trusted ``client_instance_id`` is minted (or reused) and
propagated to every namespace worker env. See
:func:`mcp_application_launcher.build_application_mcp_servers`.
"""
import mcp_application_launcher as app_launcher
return app_launcher.build_application_mcp_servers(
profile_by_namespace,
client_type=client_type,
config_path=config_path or DEFAULT_CONFIG_PATH,
client_instance_id=client_instance_id,
fleet_run_id=fleet_run_id,
session_id=session_id,
launch_nonce=launch_nonce,
)
+130 -33
View File
@@ -15734,6 +15734,9 @@ CLIENT_NAME_ENV = "GITEA_MCP_CLIENT"
CLIENT_INSTANCE_ENV = "GITEA_MCP_CLIENT_INSTANCE"
CLIENT_SESSION_ENV = "GITEA_MCP_CLIENT_SESSION"
FLEET_RUN_ENV = "GITEA_MCP_FLEET_RUN_ID"
INSTANCE_PROVENANCE_ENV = "GITEA_MCP_INSTANCE_PROVENANCE"
WORKER_IDENTITY_ENV = "GITEA_MCP_WORKER_IDENTITY"
GENERATION_ID_ENV = "GITEA_MCP_GENERATION_ID"
def _worker_registry():
@@ -15760,28 +15763,51 @@ def _client_identity_hints() -> dict:
"""What the launcher told us about itself (#948 / #975 / #978).
``client_instance_id`` is established by the trusted application launcher
once per launch and shared by all five namespace workers. When the launcher
key is absent we still register under an explicit *legacy* placeholder so
diagnostic reads work, but the registration is marked incomplete and cannot
authorize multi-instance fleet mutation safety.
once per launch and shared by all five namespace workers. Workers inherit
that value from the production launcher env and never mint a trusted ID
themselves. When the launcher key is absent or untrusted we still register
under an explicit *legacy* placeholder so diagnostic reads work, but the
registration is marked incomplete and cannot authorize multi-instance fleet
mutation safety.
"""
import mcp_application_launcher as app_launcher
import mcp_fleet_snapshot
inherited = app_launcher.inherit_or_refuse_client_instance(os.environ)
client_name = (os.environ.get(CLIENT_NAME_ENV) or "").strip() or None
raw_instance = (os.environ.get(CLIENT_INSTANCE_ENV) or "").strip() or None
instance = mcp_fleet_snapshot.assess_instance_identity(raw_instance)
# Legacy placeholder only when the launcher omitted the key — never invent a
# trusted ID from PID proximity.
instance = {
"client_instance_id": inherited.get("client_instance_id"),
"provenance": inherited.get("provenance"),
"trusted": bool(inherited.get("trusted")),
"complete": bool(inherited.get("complete")),
"launcher_sealed": bool(inherited.get("launcher_sealed")),
}
# Legacy placeholder only when the launcher omitted a usable key — never
# invent a trusted ID from PID proximity or ordinary user env.
if not instance["client_instance_id"] or not instance["trusted"]:
if not instance["client_instance_id"]:
placeholder = f"legacy-pid-{os.getpid()}"
instance = mcp_fleet_snapshot.assess_instance_identity(
fallback = mcp_fleet_snapshot.assess_instance_identity(
placeholder, source="pid_fallback"
)
instance = {
"client_instance_id": fallback["client_instance_id"],
"provenance": fallback["provenance"],
"trusted": False,
"complete": False,
"launcher_sealed": False,
}
else:
# Malformed / untrusted user-supplied value: keep it for diagnosis
# but never mark trusted.
instance["trusted"] = False
instance["launcher_sealed"] = False
return {
"client_name": client_name,
"client_instance_id": instance["client_instance_id"],
"instance_id_provenance": instance["provenance"],
"instance_identity_trusted": instance["trusted"],
"instance_launcher_sealed": instance.get("launcher_sealed", False),
"session_id": (os.environ.get(CLIENT_SESSION_ENV) or "").strip()
or f"proc-{os.getpid()}-{_process_boot_head_sha or 'nohead'}",
"fleet_run_id": (os.environ.get(FLEET_RUN_ENV) or "").strip() or None,
@@ -15857,6 +15883,19 @@ def _active_worker_identity() -> str | None:
if outcome.get("registered"):
_WORKER_IDENTITY = identity
_WORKER_GENERATION = generation
# #978 B2: export worker/generation/instance into this process
# env so peer process scans (and the instance-aware mutation
# gate) can attribute live workers without treating shared
# profiles as duplicates. Never overwrite a launcher-sealed
# client_instance_id with a different value.
os.environ[WORKER_IDENTITY_ENV] = identity
os.environ[GENERATION_ID_ENV] = generation
if hints.get("client_instance_id") and not (
os.environ.get(CLIENT_INSTANCE_ENV) or ""
).strip():
os.environ[CLIENT_INSTANCE_ENV] = str(
hints["client_instance_id"]
)
# #975: registration is the only moment identity and fencing
# epoch are both known, so the heartbeat supervisor is started
# here. Without it ``last_heartbeat_at`` never left
@@ -22375,6 +22414,20 @@ def _check_mcp_runtimes_diagnostics(task: str, matching_profiles: list[str]) ->
)
peer_worker_identity = peer_env.get("GITEA_MCP_WORKER_IDENTITY")
peer_generation = peer_env.get("GITEA_MCP_GENERATION_ID")
# #978 B2: instance attribution for the live fleet gate. Two legitimate
# application instances may share a profile when each has a distinct
# trusted client_instance_id; only same-(instance, profile/namespace)
# duplicates remain blocked.
import mcp_fleet_snapshot as _fleet
peer_instance_raw = peer_env.get("GITEA_MCP_CLIENT_INSTANCE")
peer_instance_assess = _fleet.assess_instance_identity(peer_instance_raw)
peer_client_instance = (
peer_instance_assess["client_instance_id"]
if peer_instance_assess.get("trusted")
else None
)
peer_instance_trusted = bool(peer_instance_assess.get("trusted"))
for env_match in re.finditer(r'\b(GITEA_[A-Z0-9_]+)=([^\s]+)', env_out):
k, v = env_match.group(1), env_match.group(2)
@@ -22392,6 +22445,9 @@ def _check_mcp_runtimes_diagnostics(task: str, matching_profiles: list[str]) ->
"is_client_managed": is_client_managed,
"worker_identity": peer_worker_identity,
"generation_id": peer_generation,
"client_instance_id": peer_client_instance,
"instance_identity_trusted": peer_instance_trusted,
"raw_client_instance_id": peer_instance_raw,
}
if profile not in all_profile_procs:
all_profile_procs[profile] = []
@@ -22399,44 +22455,85 @@ def _check_mcp_runtimes_diagnostics(task: str, matching_profiles: list[str]) ->
running_profiles = {}
for profile, procs in all_profile_procs.items():
# #948 AC40/AC43: sharing a profile is legitimate — profile is a
# reusable capability definition, not a worker identity. What is *not*
# legitimate is reusing one worker identity, or two live sessions
# claiming one generation. Distinctness has to be proven, though:
# processes carrying no identity evidence are indistinguishable, so
# they stay classified as duplicates and keep the #686 wall intact.
identified = [p for p in procs if p.get("worker_identity")]
# #978 B2 / #948 AC40: sharing a profile is legitimate when each live
# process belongs to a distinct trusted application instance. The
# permanent model is instance-aware — not exactly_one_per_profile.
# Fail closed only when:
# * more than one live worker claims the same (client_instance_id,
# profile/namespace), or
# * worker identity / generation is reused, or
# * multiple processes share a profile without trusted instance
# evidence (indistinguishable → treat as duplicate).
# Group by trusted client_instance_id (untrusted/missing → one bucket).
by_instance: dict[str, list[dict]] = {}
for p in procs:
if p.get("instance_identity_trusted") and p.get("client_instance_id"):
key = str(p["client_instance_id"])
else:
key = "__untrusted_or_missing__"
by_instance.setdefault(key, []).append(p)
for instance_key, group in by_instance.items():
if len(group) <= 1:
continue
identified = [p for p in group if p.get("worker_identity")]
distinct_identities = {p["worker_identity"] for p in identified}
all_identified = len(identified) == len(procs)
all_identified = len(identified) == len(group)
duplicate_identity = len(identified) != len(distinct_identities)
contested_generation = any(
len({p["worker_identity"] for p in identified if p.get("generation_id") == gen}) > 1
for gen in {p.get("generation_id") for p in identified if p.get("generation_id")}
len(
{
p["worker_identity"]
for p in identified
if p.get("generation_id") == gen
}
)
if len(procs) > 1 and (
> 1
for gen in {
p.get("generation_id")
for p in identified
if p.get("generation_id")
}
)
# Same trusted (instance, profile) with >1 live process is always a
# duplicate-namespace-worker, even if worker identities differ —
# one application launch gets exactly one worker per namespace.
same_trusted_instance = instance_key != "__untrusted_or_missing__"
if same_trusted_instance or (
not all_identified or duplicate_identity or contested_generation
):
pids_str = ", ".join(str(p["pid"]) for p in procs)
if duplicate_identity or contested_generation:
pids_str = ", ".join(str(p["pid"]) for p in group)
if same_trusted_instance:
detail = (
"The same worker identity or generation is claimed more than once, "
"so these are genuine duplicates rather than independent workers."
f"More than one live worker for profile '{profile}' under "
f"client_instance_id {instance_key!r} (duplicate namespace "
"worker). Two legitimate application instances with "
"distinct trusted client_instance_id values may share a "
"profile; this collision does not."
)
elif duplicate_identity or contested_generation:
detail = (
"The same worker identity or generation is claimed more "
"than once, so these are genuine duplicates rather than "
"independent workers."
)
else:
detail = (
"Manual or duplicate launches defeat staleness detection and cannot "
"receive client stdio."
"Multiple processes share this profile without distinct "
"trusted client_instance_id evidence, so they cannot be "
"told apart from a duplicate MCP process. Relaunch via "
"the production application launcher so each instance "
"receives a trusted GITEA_MCP_CLIENT_INSTANCE."
)
reasons.append(
f"stale-runtime: Duplicate MCP server process(es) detected for profile '{profile}' (PIDs: {pids_str}). "
f"stale-runtime: Duplicate MCP server process(es) detected "
f"for profile '{profile}' (PIDs: {pids_str}). "
+ detail
)
# Otherwise: several independently identified workers share one profile.
# No reason is appended, deliberately. Every reason this function
# returns is raised as a hard RuntimeError by its callers, so recording
# legitimate concurrency here as "informational" would block exactly the
# case #948 exists to permit.
# Multiple trusted instances sharing one profile intentionally produce
# no reason here. Callers raise every reason as a hard RuntimeError, so
# treating legitimate multi-instance concurrency as "informational"
# would re-introduce the profile-only wall #978 removes.
client_procs = [p for p in procs if p["is_client_managed"]]
if client_procs:
client_procs.sort(key=lambda p: p["start_time"], reverse=True)
+393
View File
@@ -0,0 +1,393 @@
"""Production application launcher for multi-namespace MCP fleets (#978 B1).
One real LLM application launch mints exactly one trusted
``client_instance_id`` and propagates it to every Gitea MCP namespace worker
started for that launch. Workers never invent a trusted instance identity from
PID proximity, timestamps, or ordinary untrusted environment values.
This module is the production serve-path authority for instance identity.
Tests and fixtures may call the same functions, but production registration
receives the identity from the env this launcher builds — not from a hand-set
test-only helper that bypasses it.
"""
from __future__ import annotations
import os
import secrets
from typing import Any, Mapping
import mcp_fleet_snapshot as fleet
import mcp_worker_identity as mwi
#: Canonical MCP server names for the five role namespaces.
NAMESPACE_SERVER_NAMES: tuple[str, ...] = (
"gitea-author",
"gitea-reviewer",
"gitea-merger",
"gitea-controller",
"gitea-reconciler",
)
#: Map MCP server name → role/namespace kind.
SERVER_TO_NAMESPACE: dict[str, str] = {
"gitea-author": "author",
"gitea-reviewer": "reviewer",
"gitea-merger": "merger",
"gitea-controller": "controller",
"gitea-reconciler": "reconciler",
}
SANCTIONED_NAMESPACES: tuple[str, ...] = (
"author",
"reviewer",
"merger",
"controller",
"reconciler",
)
# Env the launcher may set on every worker of one application launch.
CLIENT_NAME_ENV = "GITEA_MCP_CLIENT"
CLIENT_INSTANCE_ENV = fleet.CLIENT_INSTANCE_ENV
FLEET_RUN_ENV = fleet.FLEET_RUN_ENV
CLIENT_SESSION_ENV = "GITEA_MCP_CLIENT_SESSION"
CLIENT_MANAGED_ENV = "GITEA_CLIENT_MANAGED"
PROFILE_ENV = "GITEA_MCP_PROFILE"
CONFIG_ENV = "GITEA_MCP_CONFIG"
INSTANCE_PROVENANCE_ENV = "GITEA_MCP_INSTANCE_PROVENANCE"
WORKER_IDENTITY_ENV = "GITEA_MCP_WORKER_IDENTITY"
GENERATION_ID_ENV = "GITEA_MCP_GENERATION_ID"
# Marker the trusted launcher alone writes; ordinary user env without this
# marker is never classified as launcher-trusted provenance.
LAUNCHER_PROVENANCE_VALUE = fleet.INSTANCE_ID_PROVENANCE_TRUSTED
def mint_application_launch(
client_type: str | None,
*,
launch_nonce: str | None = None,
fleet_run_id: str | None = None,
session_id: str | None = None,
now=None,
) -> dict[str, Any]:
"""Mint one trusted application-instance identity for a production launch.
Called exactly once per real application launch. The returned
``client_instance_id`` is injected into every namespace worker environment
for that launch. A second call (separate launch) yields a different ID.
"""
client = mwi.normalize_client_name(client_type)
instance_id = fleet.generate_client_instance_id(
client, launch_nonce=launch_nonce, now=now
)
assessment = fleet.assess_instance_identity(instance_id)
if not assessment["trusted"]:
# generate_client_instance_id always produces a trusted format; fail
# closed if that invariant ever breaks rather than shipping untrusted.
raise RuntimeError(
f"launcher produced untrusted client_instance_id {instance_id!r}: "
f"{assessment.get('reasons')}"
)
session = (session_id or "").strip() or f"launch-{secrets.token_hex(12)}"
return {
"client_type": client,
"client_instance_id": instance_id,
"instance_id_provenance": LAUNCHER_PROVENANCE_VALUE,
"instance_identity_trusted": True,
"fleet_run_id": (fleet_run_id or "").strip() or None,
"session_id": session,
"namespaces": list(SANCTIONED_NAMESPACES),
"namespace_server_names": list(NAMESPACE_SERVER_NAMES),
}
def namespace_worker_env(
*,
profile_name: str,
client_type: str | None,
client_instance_id: str,
config_path: str | None = None,
fleet_run_id: str | None = None,
session_id: str | None = None,
extra_env: Mapping[str, str] | None = None,
) -> dict[str, str]:
"""Build the environment for one namespace worker of a trusted launch.
Never invents a client_instance_id. The caller must supply the launch-minted
identity so all five workers receive the same value.
"""
assessment = fleet.assess_instance_identity(client_instance_id)
if not assessment["trusted"]:
raise ValueError(
"namespace_worker_env refuses untrusted client_instance_id "
f"{client_instance_id!r}: {assessment.get('reasons')}"
)
client = mwi.normalize_client_name(client_type)
env: dict[str, str] = {
PROFILE_ENV: str(profile_name),
CLIENT_MANAGED_ENV: "1",
"GITEA_MCP_CLIENT": client,
CLIENT_INSTANCE_ENV: assessment["client_instance_id"],
INSTANCE_PROVENANCE_ENV: LAUNCHER_PROVENANCE_VALUE,
}
if config_path:
env[CONFIG_ENV] = str(config_path)
if fleet_run_id:
env[FLEET_RUN_ENV] = str(fleet_run_id)
if session_id:
env[CLIENT_SESSION_ENV] = str(session_id)
if extra_env:
# Never let untrusted callers override the trusted instance keys.
protected = {
CLIENT_INSTANCE_ENV,
INSTANCE_PROVENANCE_ENV,
"GITEA_MCP_CLIENT",
CLIENT_MANAGED_ENV,
}
for key, value in extra_env.items():
if key in protected:
continue
env[str(key)] = str(value)
return env
def build_application_mcp_servers(
profile_by_namespace: Mapping[str, str],
*,
client_type: str | None,
config_path: str | None = None,
client_instance_id: str | None = None,
fleet_run_id: str | None = None,
session_id: str | None = None,
launch_nonce: str | None = None,
command: str | None = None,
args: list[str] | None = None,
now=None,
) -> dict[str, Any]:
"""Build a production ``mcpServers`` map for one application launch.
Mints one ``client_instance_id`` when *client_instance_id* is omitted (fresh
launch). When the caller supplies a previously minted trusted ID (resume of
the same launch / config rewrite), that ID is reused so reconnect keeps
attribution. A full new application restart omits the ID and receives a
fresh mint.
Every namespace server entry receives the **same** instance ID. Separate
calls with no supplied ID receive distinct IDs.
"""
missing = [
ns for ns in SANCTIONED_NAMESPACES if not profile_by_namespace.get(ns)
]
if missing:
raise ValueError(
"build_application_mcp_servers requires a profile for every "
f"sanctioned namespace; missing: {missing}"
)
if client_instance_id is None:
launch = mint_application_launch(
client_type,
launch_nonce=launch_nonce,
fleet_run_id=fleet_run_id,
session_id=session_id,
now=now,
)
else:
assessment = fleet.assess_instance_identity(client_instance_id)
if not assessment["trusted"]:
raise ValueError(
"refusing to propagate untrusted client_instance_id "
f"{client_instance_id!r} into production launch envs: "
f"{assessment.get('reasons')}"
)
launch = {
"client_type": mwi.normalize_client_name(client_type),
"client_instance_id": assessment["client_instance_id"],
"instance_id_provenance": LAUNCHER_PROVENANCE_VALUE,
"instance_identity_trusted": True,
"fleet_run_id": (fleet_run_id or "").strip() or None,
"session_id": (session_id or "").strip()
or f"launch-{secrets.token_hex(12)}",
"namespaces": list(SANCTIONED_NAMESPACES),
"namespace_server_names": list(NAMESPACE_SERVER_NAMES),
}
# Resolve command/args from the production server entry when not provided.
if command is None or args is None:
import gitea_config
cmd, cmd_args = gitea_config.server_command()
command = command or cmd
args = args if args is not None else list(cmd_args)
servers: dict[str, Any] = {}
shared_id = launch["client_instance_id"]
for namespace in SANCTIONED_NAMESPACES:
server_name = f"gitea-{namespace}"
profile = profile_by_namespace[namespace]
env = namespace_worker_env(
profile_name=profile,
client_type=launch["client_type"],
client_instance_id=shared_id,
config_path=config_path,
fleet_run_id=launch.get("fleet_run_id"),
session_id=launch.get("session_id"),
)
servers[server_name] = {
"command": command,
"args": list(args),
"env": env,
}
return {
"mcpServers": servers,
"launch": launch,
"client_instance_id": shared_id,
"client_type": launch["client_type"],
"namespaces": list(SANCTIONED_NAMESPACES),
"shared_instance_id_across_namespaces": True,
"namespace_count": len(SANCTIONED_NAMESPACES),
}
def launcher_entry_for_profile(
profile_name: str,
*,
client_type: str | None = None,
config_path: str | None = None,
client_instance_id: str | None = None,
fleet_run_id: str | None = None,
session_id: str | None = None,
server_key: str = "gitea-tools",
launch_nonce: str | None = None,
now=None,
) -> dict[str, Any]:
"""Thin single-server production launcher entry with trusted instance ID.
Used when only one namespace is being configured. Still mints (or reuses)
a trusted ``client_instance_id`` so production never relies on the legacy
placeholder identity for normal launches.
"""
import gitea_config
if client_instance_id is None:
launch = mint_application_launch(
client_type or "unknown",
launch_nonce=launch_nonce,
fleet_run_id=fleet_run_id,
session_id=session_id,
now=now,
)
client_instance_id = launch["client_instance_id"]
client = launch["client_type"]
fleet_run = launch.get("fleet_run_id")
session = launch.get("session_id")
else:
assessment = fleet.assess_instance_identity(client_instance_id)
if not assessment["trusted"]:
raise ValueError(
f"untrusted client_instance_id {client_instance_id!r}"
)
client = mwi.normalize_client_name(client_type)
fleet_run = (fleet_run_id or "").strip() or None
session = (session_id or "").strip() or None
client_instance_id = assessment["client_instance_id"]
command, args = gitea_config.server_command()
env = namespace_worker_env(
profile_name=profile_name,
client_type=client,
client_instance_id=client_instance_id,
config_path=config_path or gitea_config.DEFAULT_CONFIG_PATH,
fleet_run_id=fleet_run,
session_id=session,
)
return {
server_key: {
"command": command,
"args": args,
"env": env,
},
"client_instance_id": client_instance_id,
"client_type": client,
}
def collect_instance_ids_from_mcp_servers(
mcp_servers: Mapping[str, Any],
) -> dict[str, Any]:
"""Inspect a production mcpServers map for shared instance attribution.
Returns the unique set of client_instance_id values across gitea-* servers
and whether all five namespaces share exactly one trusted ID.
"""
ids: list[str] = []
by_server: dict[str, str | None] = {}
for name in NAMESPACE_SERVER_NAMES:
entry = mcp_servers.get(name) or {}
env = entry.get("env") or {}
raw = (env.get(CLIENT_INSTANCE_ENV) or "").strip() or None
by_server[name] = raw
if raw:
ids.append(raw)
unique = sorted(set(ids))
trusted = [
i
for i in unique
if fleet.assess_instance_identity(i)["trusted"]
]
return {
"server_instance_ids": by_server,
"unique_instance_ids": unique,
"trusted_instance_ids": trusted,
"shared_single_trusted_id": (
len(unique) == 1
and len(trusted) == 1
and all(by_server.get(n) == unique[0] for n in NAMESPACE_SERVER_NAMES)
),
"namespace_server_count": sum(
1 for n in NAMESPACE_SERVER_NAMES if n in mcp_servers
),
}
def inherit_or_refuse_client_instance(
env: Mapping[str, str] | None = None,
) -> dict[str, Any]:
"""Resolve instance identity for a worker process at serve time.
Production workers inherit the launcher-issued ID. They never mint a
trusted ID themselves. Missing / legacy / malformed values fail soft into
an untrusted assessment so registration can still record a diagnostic row
without authorizing multi-instance fleet mutation.
"""
source = dict(env if env is not None else os.environ)
raw = (source.get(CLIENT_INSTANCE_ENV) or "").strip() or None
provenance_marker = (source.get(INSTANCE_PROVENANCE_ENV) or "").strip()
assessment = fleet.assess_instance_identity(raw)
# Ordinary user-supplied values without launcher provenance marker are
# still format-checked by assess_instance_identity. When the format is
# trusted but the launcher marker is absent, keep the ID but note that
# provenance is not launcher-sealed (operator hand-set or legacy config).
if assessment["trusted"] and provenance_marker != LAUNCHER_PROVENANCE_VALUE:
assessment = dict(assessment)
assessment["launcher_sealed"] = False
assessment["reasons"] = list(assessment.get("reasons") or []) + [
f"{INSTANCE_PROVENANCE_ENV} is not {LAUNCHER_PROVENANCE_VALUE!r}; "
"identity format is valid but not sealed by the production launcher"
]
else:
assessment = dict(assessment)
assessment["launcher_sealed"] = bool(
assessment["trusted"]
and provenance_marker == LAUNCHER_PROVENANCE_VALUE
)
assessment["fleet_run_id"] = (source.get(FLEET_RUN_ENV) or "").strip() or None
assessment["session_id"] = (
(source.get(CLIENT_SESSION_ENV) or "").strip() or None
)
assessment["client_type"] = mwi.normalize_client_name(
(source.get("GITEA_MCP_CLIENT") or "").strip() or None
)
return assessment
+23 -1
View File
@@ -88,8 +88,13 @@ INSTANCE_ID_PROVENANCE_MISSING = "missing"
CLIENT_INSTANCE_ENV = "GITEA_MCP_CLIENT_INSTANCE"
FLEET_RUN_ENV = "GITEA_MCP_FLEET_RUN_ID"
PROCESS_IDENTITY_ENV = "GITEA_MCP_PROCESS_IDENTITY"
INSTANCE_PROVENANCE_ENV = "GITEA_MCP_INSTANCE_PROVENANCE"
_LEGACY_INSTANCE_PREFIXES = ("pid-", "proc-", "legacy-")
#: Trusted launcher-issued IDs use the reserved ``inst-`` prefix
#: (see :func:`generate_client_instance_id`). Ordinary user-supplied strings
#: without that prefix never count as trusted attribution.
_TRUSTED_INSTANCE_PREFIX = "inst-"
def _utc_now() -> datetime:
@@ -131,7 +136,10 @@ def assess_instance_identity(
) -> dict[str, Any]:
"""Classify whether a client_instance_id is trusted enough for mutation.
Trusted instance IDs are non-empty and not the pre-#978 PID/proc fallbacks.
Trusted instance IDs are non-empty, match the launcher-minted ``inst-…``
format from :func:`generate_client_instance_id`, and are not pre-#978
PID/proc/legacy placeholders. Ordinary user-supplied or malformed values
fail closed as untrusted so they cannot spoof multi-instance attribution.
Incomplete identities remain visible for diagnosis but cannot authorize
unsafe mutation.
"""
@@ -159,6 +167,20 @@ def assess_instance_identity(
"not a trusted launcher-issued instance identity"
],
}
if not text.startswith(_TRUSTED_INSTANCE_PREFIX) or len(text) <= len(
_TRUSTED_INSTANCE_PREFIX
):
return {
"client_instance_id": text,
"complete": False,
"trusted": False,
"provenance": INSTANCE_ID_PROVENANCE_LEGACY,
"reasons": [
f"client_instance_id {text!r} is malformed or user-supplied and "
f"does not use the trusted launcher prefix "
f"{_TRUSTED_INSTANCE_PREFIX!r}; refusing trusted attribution"
],
}
return {
"client_instance_id": text,
"complete": True,
+14 -1
View File
@@ -175,8 +175,21 @@ class TestLauncherSnippets(unittest.TestCase):
def test_only_safe_keys_no_secrets(self):
entry = gitea_config.launcher_entry("prgs", "/cfg/profiles.json")["gitea-tools"]
self.assertEqual(set(entry), {"command", "args", "env"})
self.assertEqual(set(entry["env"]), {"GITEA_MCP_CONFIG", "GITEA_MCP_PROFILE", "GITEA_CLIENT_MANAGED"})
# #978 B1: production launcher also injects trusted client identity.
required = {
"GITEA_MCP_CONFIG",
"GITEA_MCP_PROFILE",
"GITEA_CLIENT_MANAGED",
"GITEA_MCP_CLIENT",
"GITEA_MCP_CLIENT_INSTANCE",
"GITEA_MCP_INSTANCE_PROVENANCE",
}
self.assertTrue(required.issubset(set(entry["env"])), entry["env"])
self.assertEqual(entry["env"]["GITEA_MCP_PROFILE"], "prgs")
self.assertTrue(
entry["env"]["GITEA_MCP_CLIENT_INSTANCE"].startswith("inst-"),
entry["env"]["GITEA_MCP_CLIENT_INSTANCE"],
)
blob = json.dumps(entry).lower()
for word in ("token", "password", "secret"):
self.assertNotIn(word, blob)
@@ -1049,6 +1049,386 @@ class ClientHintsTests(unittest.TestCase):
self.assertEqual(hints["client_instance_id"], inst)
self.assertEqual(hints["fleet_run_id"], "run-1")
def test_malformed_user_supplied_instance_not_trusted(self):
import gitea_mcp_server as server
with mock.patch.dict(
os.environ,
{
"GITEA_MCP_CLIENT": "codex",
"GITEA_MCP_CLIENT_INSTANCE": "user-spoofed-not-launcher",
},
clear=False,
):
hints = server._client_identity_hints()
self.assertFalse(hints["instance_identity_trusted"])
self.assertEqual(hints["client_instance_id"], "user-spoofed-not-launcher")
class ProductionLauncherB1Tests(unittest.TestCase):
"""#978 review 655 B1: production launcher mints + propagates instance ID."""
def test_one_launch_shares_id_across_five_namespaces(self):
import mcp_application_launcher as launcher
profiles = {ns: f"prgs-{ns}" for ns in NAMESPACES}
built = launcher.build_application_mcp_servers(
profiles,
client_type="codex",
config_path="/cfg/profiles.json",
launch_nonce="launch-one",
now=NOW,
command="/venv/bin/python3",
args=["mcp_server.py"],
)
servers = built["mcpServers"]
self.assertEqual(len(servers), 5)
ids = {
servers[f"gitea-{ns}"]["env"]["GITEA_MCP_CLIENT_INSTANCE"]
for ns in NAMESPACES
}
self.assertEqual(len(ids), 1, ids)
shared = next(iter(ids))
self.assertTrue(fleet.assess_instance_identity(shared)["trusted"])
self.assertEqual(built["client_instance_id"], shared)
# No legacy placeholder on any production worker env.
for ns in NAMESPACES:
env = servers[f"gitea-{ns}"]["env"]
self.assertEqual(env["GITEA_MCP_CLIENT_INSTANCE"], shared)
self.assertEqual(
env["GITEA_MCP_INSTANCE_PROVENANCE"],
fleet.INSTANCE_ID_PROVENANCE_TRUSTED,
)
self.assertEqual(env["GITEA_MCP_CLIENT"], "codex")
self.assertEqual(env["GITEA_CLIENT_MANAGED"], "1")
self.assertFalse(env["GITEA_MCP_CLIENT_INSTANCE"].startswith("legacy-"))
proof = launcher.collect_instance_ids_from_mcp_servers(servers)
self.assertTrue(proof["shared_single_trusted_id"], proof)
def test_two_launches_receive_different_ids(self):
import mcp_application_launcher as launcher
profiles = {ns: f"prgs-{ns}" for ns in NAMESPACES}
a = launcher.build_application_mcp_servers(
profiles,
client_type="codex",
launch_nonce="L1",
now=NOW,
command="python3",
args=["mcp_server.py"],
)
b = launcher.build_application_mcp_servers(
profiles,
client_type="codex",
launch_nonce="L2",
now=NOW,
command="python3",
args=["mcp_server.py"],
)
self.assertNotEqual(a["client_instance_id"], b["client_instance_id"])
def test_resume_reuses_supplied_trusted_id(self):
import mcp_application_launcher as launcher
profiles = {ns: f"prgs-{ns}" for ns in NAMESPACES}
first = launcher.build_application_mcp_servers(
profiles,
client_type="claude_code",
launch_nonce="resume-src",
now=NOW,
command="python3",
args=["mcp_server.py"],
)
resumed = launcher.build_application_mcp_servers(
profiles,
client_type="claude_code",
client_instance_id=first["client_instance_id"],
command="python3",
args=["mcp_server.py"],
)
self.assertEqual(first["client_instance_id"], resumed["client_instance_id"])
def test_refuses_untrusted_instance_id_on_production_path(self):
import mcp_application_launcher as launcher
profiles = {ns: f"prgs-{ns}" for ns in NAMESPACES}
with self.assertRaises(ValueError):
launcher.build_application_mcp_servers(
profiles,
client_type="codex",
client_instance_id="pid-999",
command="python3",
args=["mcp_server.py"],
)
with self.assertRaises(ValueError):
launcher.namespace_worker_env(
profile_name="prgs-author",
client_type="codex",
client_instance_id="user-supplied-garbage",
)
def test_extra_env_cannot_override_trusted_instance_keys(self):
import mcp_application_launcher as launcher
trusted = fleet.generate_client_instance_id(
"codex", launch_nonce="seal", now=NOW
)
env = launcher.namespace_worker_env(
profile_name="prgs-author",
client_type="codex",
client_instance_id=trusted,
extra_env={
"GITEA_MCP_CLIENT_INSTANCE": "inst-spoofed-20260730T000000Z-deadbeefdead",
"GITEA_MCP_INSTANCE_PROVENANCE": "trusted_launcher",
"GITEA_CLIENT_MANAGED": "0",
"GITEA_MCP_CLIENT": "gemini",
"GITEA_AUTHOR_WORKTREE": "/ok/extra",
},
)
self.assertEqual(env["GITEA_MCP_CLIENT_INSTANCE"], trusted)
self.assertEqual(env["GITEA_CLIENT_MANAGED"], "1")
self.assertEqual(env["GITEA_MCP_CLIENT"], "codex")
self.assertEqual(env["GITEA_AUTHOR_WORKTREE"], "/ok/extra")
def test_gitea_config_launcher_entry_uses_production_path(self):
"""Production launcher_entry — not a test helper — mints trusted IDs."""
import gitea_config
entry_a = gitea_config.launcher_entry(
"prgs-author",
"/cfg/profiles.json",
client_type="codex",
launch_nonce="cfg-a",
)["gitea-tools"]
entry_b = gitea_config.launcher_entry(
"prgs-author",
"/cfg/profiles.json",
client_type="codex",
launch_nonce="cfg-b",
)["gitea-tools"]
id_a = entry_a["env"]["GITEA_MCP_CLIENT_INSTANCE"]
id_b = entry_b["env"]["GITEA_MCP_CLIENT_INSTANCE"]
self.assertNotEqual(id_a, id_b)
self.assertTrue(fleet.assess_instance_identity(id_a)["trusted"])
self.assertTrue(fleet.assess_instance_identity(id_b)["trusted"])
def test_multi_namespace_launcher_entries_api(self):
import gitea_config
profiles = {ns: f"prgs-{ns}" for ns in NAMESPACES}
built = gitea_config.multi_namespace_launcher_entries(
profiles,
client_type="grok",
config_path="/cfg/profiles.json",
launch_nonce="multi-api",
)
self.assertTrue(built["shared_instance_id_across_namespaces"])
self.assertEqual(built["namespace_count"], 5)
proof = __import__(
"mcp_application_launcher", fromlist=["*"]
).collect_instance_ids_from_mcp_servers(built["mcpServers"])
self.assertTrue(proof["shared_single_trusted_id"], proof)
def test_malformed_and_missing_fail_safely(self):
self.assertFalse(fleet.assess_instance_identity("")["trusted"])
self.assertFalse(fleet.assess_instance_identity(None)["trusted"])
self.assertFalse(fleet.assess_instance_identity("legacy-pid-1")["trusted"])
self.assertFalse(fleet.assess_instance_identity("not-an-inst-id")["trusted"])
self.assertFalse(fleet.assess_instance_identity("inst-")["trusted"])
self.assertFalse(fleet.assess_instance_identity("user-spoofed")["trusted"])
# Reserved inst- prefix from the launcher is trusted.
self.assertTrue(fleet.assess_instance_identity("inst-codex-1")["trusted"])
class InstanceAwareMutationGateB2Tests(unittest.TestCase):
"""#978 review 655 B2: mutation gate is instance-aware, not profile-only."""
def _ps_lines(self, rows):
# rows: list of (pid, lstart, command) — command ignored beyond mcp_server.py
header = " PID LSTART COMMAND\n"
body = "".join(
f"{pid} {lstart} /path/to/python mcp_server.py\n"
for pid, lstart, _env in rows
)
return header + body
def _side_effect(self, rows, *, self_pid=1000):
ps_out = self._ps_lines(rows)
env_by_pid = {str(pid): env for pid, _ls, env in rows}
def side_effect(args, **kwargs):
mock_run = mock.MagicMock()
if args[0] == "ps" and "eww" in args:
mock_run.stdout = env_by_pid.get(str(args[2]), "")
return mock_run
if args[0] == "ps":
mock_run.stdout = ps_out
return mock_run
if args[0] == "git":
mock_run.stdout = "deadbeef"
return mock_run
raise ValueError(args)
return side_effect
def test_two_valid_instances_same_profile_allowed(self):
import gitea_mcp_server as server
inst_a = fleet.generate_client_instance_id(
"codex", launch_nonce="gate-a", now=NOW
)
inst_b = fleet.generate_client_instance_id(
"codex", launch_nonce="gate-b", now=NOW
)
lstart = "Wed Jul 8 15:00:00 2026"
rows = [
(
2001,
lstart,
(
f"GITEA_MCP_PROFILE=prgs-author GITEA_CLIENT_MANAGED=1 "
f"GITEA_MCP_CLIENT_INSTANCE={inst_a} "
f"GITEA_MCP_WORKER_IDENTITY=worker-a "
f"GITEA_MCP_GENERATION_ID=gen-a"
),
),
(
2002,
lstart,
(
f"GITEA_MCP_PROFILE=prgs-author GITEA_CLIENT_MANAGED=1 "
f"GITEA_MCP_CLIENT_INSTANCE={inst_b} "
f"GITEA_MCP_WORKER_IDENTITY=worker-b "
f"GITEA_MCP_GENERATION_ID=gen-b"
),
),
]
with mock.patch("subprocess.run") as mock_run, mock.patch(
"os.path.getmtime", return_value=datetime(2026, 7, 8, 11, 0, 0).timestamp()
), mock.patch("os.path.exists", return_value=True), mock.patch(
"os.getpid", return_value=9999
):
mock_run.side_effect = self._side_effect(rows)
reasons = server._check_mcp_runtimes_diagnostics(
"create_issue", ["prgs-author"]
)
self.assertFalse(
any("Duplicate MCP server process(es) detected" in r for r in reasons),
reasons,
)
def test_duplicate_same_instance_and_namespace_blocked(self):
import gitea_mcp_server as server
inst = fleet.generate_client_instance_id(
"codex", launch_nonce="dup", now=NOW
)
lstart = "Wed Jul 8 15:00:00 2026"
rows = [
(
3001,
lstart,
(
f"GITEA_MCP_PROFILE=prgs-author GITEA_CLIENT_MANAGED=1 "
f"GITEA_MCP_CLIENT_INSTANCE={inst} "
f"GITEA_MCP_WORKER_IDENTITY=worker-1 "
f"GITEA_MCP_GENERATION_ID=gen-1"
),
),
(
3002,
lstart,
(
f"GITEA_MCP_PROFILE=prgs-author GITEA_CLIENT_MANAGED=1 "
f"GITEA_MCP_CLIENT_INSTANCE={inst} "
f"GITEA_MCP_WORKER_IDENTITY=worker-2 "
f"GITEA_MCP_GENERATION_ID=gen-2"
),
),
]
with mock.patch("subprocess.run") as mock_run, mock.patch(
"os.path.getmtime", return_value=datetime(2026, 7, 8, 11, 0, 0).timestamp()
), mock.patch("os.path.exists", return_value=True), mock.patch(
"os.getpid", return_value=9999
):
mock_run.side_effect = self._side_effect(rows)
reasons = server._check_mcp_runtimes_diagnostics(
"create_issue", ["prgs-author"]
)
self.assertTrue(
any("Duplicate MCP server process(es) detected" in r for r in reasons),
reasons,
)
self.assertTrue(
any("duplicate namespace" in r.lower() or "client_instance_id" in r for r in reasons),
reasons,
)
def test_missing_instance_evidence_still_fail_closed(self):
"""Two profile-sharing processes without trusted IDs remain blocked."""
import gitea_mcp_server as server
lstart = "Wed Jul 8 15:00:00 2026"
rows = [
(
4001,
lstart,
"GITEA_MCP_PROFILE=prgs-author GITEA_CLIENT_MANAGED=1",
),
(
4002,
lstart,
"GITEA_MCP_PROFILE=prgs-author GITEA_CLIENT_MANAGED=1",
),
]
with mock.patch("subprocess.run") as mock_run, mock.patch(
"os.path.getmtime", return_value=datetime(2026, 7, 8, 11, 0, 0).timestamp()
), mock.patch("os.path.exists", return_value=True), mock.patch(
"os.getpid", return_value=9999
):
mock_run.side_effect = self._side_effect(rows)
reasons = server._check_mcp_runtimes_diagnostics(
"create_issue", ["prgs-author"]
)
self.assertTrue(
any("Duplicate MCP server process(es) detected" in r for r in reasons),
reasons,
)
def test_single_instance_still_ok(self):
import gitea_mcp_server as server
inst = fleet.generate_client_instance_id(
"codex", launch_nonce="single", now=NOW
)
lstart = "Wed Jul 8 15:00:00 2026"
rows = [
(
5001,
lstart,
(
f"GITEA_MCP_PROFILE=prgs-author GITEA_CLIENT_MANAGED=1 "
f"GITEA_MCP_CLIENT_INSTANCE={inst} "
f"GITEA_MCP_WORKER_IDENTITY=worker-only "
f"GITEA_MCP_GENERATION_ID=gen-only"
),
),
]
with mock.patch("subprocess.run") as mock_run, mock.patch(
"os.path.getmtime", return_value=datetime(2026, 7, 8, 11, 0, 0).timestamp()
), mock.patch("os.path.exists", return_value=True), mock.patch(
"os.getpid", return_value=9999
):
mock_run.side_effect = self._side_effect(rows)
reasons = server._check_mcp_runtimes_diagnostics(
"create_issue", ["prgs-author"]
)
self.assertFalse(
any("Duplicate MCP server process(es) detected" in r for r in reasons),
reasons,
)
if __name__ == "__main__":
unittest.main()