Publish curated Unraid-AppOps-Gateway source from 2e53f628d284213b90ca2f9c5e121671f5933574

This commit is contained in:
Public Source Publisher committed 2026-10-02 22:44:18 +00:00
1 parent 4f9c5a1106
commit 2facb8de34
13 files changed
+3861 -1742

No files matched your search

+1 -1
View File
@@ -1,3 +1,3 @@
# Public source export # Public source export
This source snapshot was generated from the private canonical repository at revision `f4a918d9dba3663faa1f675dcc335c9ec951f30c`. It contains no private Git history or operational evidence. Changes are published from the canonical repository. This source snapshot was generated from the private canonical repository at revision `2e53f628d284213b90ca2f9c5e121671f5933574`. It contains no private Git history or operational evidence. Changes are published from the canonical repository.
+30 -15
View File
@@ -1,6 +1,6 @@
{ {
"schemaVersion": 1, "schemaVersion": 1,
"sourceRevision": "f4a918d9dba3663faa1f675dcc335c9ec951f30c", "sourceRevision": "2e53f628d284213b90ca2f9c5e121671f5933574",
"files": [ "files": [
{ {
"path": ".dockerignore", "path": ".dockerignore",
@@ -509,8 +509,8 @@
}, },
{ {
"path": "backend/app/services/deployment_control_plane.py", "path": "backend/app/services/deployment_control_plane.py",
"sha256": "bfea241b036111180b42259af770847f15e396284261cb29000e1db4f637867c", "sha256": "4443d9a1581cf24c1cc3eccaaa1e0e67527228e28a873924ab0c3591fd67da99",
"bytes": 28812 "bytes": 25070
}, },
{ {
"path": "backend/app/services/deployment_target_registry.py", "path": "backend/app/services/deployment_target_registry.py",
@@ -589,8 +589,8 @@
}, },
{ {
"path": "backend/app/services/mcp_hub_deployment_reconciliation.py", "path": "backend/app/services/mcp_hub_deployment_reconciliation.py",
"sha256": "d45921d486b7debba7dc7d68a274beb2103c20fc5b2968f9380b5c22dfea6d95", "sha256": "2367cf52e39091bd941b19e12aeccf72f65842cdf5ec74e5eb17bd0f636f48bf",
"bytes": 31417 "bytes": 44919
}, },
{ {
"path": "backend/app/services/mcp_operations.py", "path": "backend/app/services/mcp_operations.py",
@@ -869,13 +869,13 @@
}, },
{ {
"path": "backend/tests/test_deploy_broker.py", "path": "backend/tests/test_deploy_broker.py",
"sha256": "7a59aa36564fb056def3453324f376bb0580fdeee99a32d4a3180a22fd16bebc", "sha256": "6bc23cb5883961316acae07daf12ec261e1c21e229bf5ee8ebc56ff245f6a34d",
"bytes": 305380 "bytes": 309532
}, },
{ {
"path": "backend/tests/test_deployment_auto_rollback.py", "path": "backend/tests/test_deployment_auto_rollback.py",
"sha256": "07e10b748bbfea5277b9b0ca9b2ab38edf90d76a5fe976560e5384ef1e8c90c8", "sha256": "e8eb4fd4be632fcb25e85778adf4174492771c41b7cde0aae6f3746641efcc8a",
"bytes": 13355 "bytes": 5966
}, },
{ {
"path": "backend/tests/test_deployment_contract.py", "path": "backend/tests/test_deployment_contract.py",
@@ -982,21 +982,31 @@
"sha256": "d704cd4ba299ce2fc51d6f4a8a1b26ecdc2dea0e734e63dc6a65e1ba4c3ed37f", "sha256": "d704cd4ba299ce2fc51d6f4a8a1b26ecdc2dea0e734e63dc6a65e1ba4c3ed37f",
"bytes": 692 "bytes": 692
}, },
{
"path": "backend/tests/test_mcp_hub_candidate_preparation.py",
"sha256": "aa5525214e034464ac3586252ba5bad09f59c96c861cb9449a3dc86ae7a91c74",
"bytes": 22525
},
{ {
"path": "backend/tests/test_mcp_hub_deployment_reconciliation.py", "path": "backend/tests/test_mcp_hub_deployment_reconciliation.py",
"sha256": "0400d0b0ca31a62d869166b9368fa0c018c0e44edba98387d12633976988da29", "sha256": "476d02f8c5261e55788b16f8ee490728291b2379395890bbedc3a9be56ae5f58",
"bytes": 14176 "bytes": 28475
}, },
{ {
"path": "backend/tests/test_mcp_hub_governed_topology.py", "path": "backend/tests/test_mcp_hub_governed_topology.py",
"sha256": "f521c7d843f7d5fe9a5ac3d68bde1f072f38991d5e30e8f9bea72b77061c79f8", "sha256": "117c07dfb34e9ab0fac5675dd51585906b33d7bf5ea0a059d2d8f6eb4d2897a7",
"bytes": 106551 "bytes": 118901
}, },
{ {
"path": "backend/tests/test_mcp_hub_policy_reconciliation.py", "path": "backend/tests/test_mcp_hub_policy_reconciliation.py",
"sha256": "50cf920443570240331d8851195bdd3140eeb93397b938c7f78b35749f6b7399", "sha256": "50cf920443570240331d8851195bdd3140eeb93397b938c7f78b35749f6b7399",
"bytes": 5041 "bytes": 5041
}, },
{
"path": "backend/tests/test_mcp_hub_release_candidates.py",
"sha256": "f2add61e18641e4a5075948dc0b3b0e36b2b4bfe389d608078a7c5cf506fad54",
"bytes": 34190
},
{ {
"path": "backend/tests/test_mcp_hub_release_store.py", "path": "backend/tests/test_mcp_hub_release_store.py",
"sha256": "91dbd0da6a131f76a04c51f93e31a77f4acd057f788f4b1a6fce75d37c23525e", "sha256": "91dbd0da6a131f76a04c51f93e31a77f4acd057f788f4b1a6fce75d37c23525e",
@@ -1344,12 +1354,12 @@
}, },
{ {
"path": "pnpm-lock.yaml", "path": "pnpm-lock.yaml",
"sha256": "f699c9ed2c5649b0d41f0e01420ef377161b08ea163c58fd00c5d09cf696fe0d", "sha256": "ca7ed34cc053954acb98b3a704d23f954f4e06eafddfbaf6c27a38ad72245e33",
"bytes": 215991 "bytes": 215991
}, },
{ {
"path": "pnpm-workspace.yaml", "path": "pnpm-workspace.yaml",
"sha256": "bd8c09cbbfdf628ec0c5cce6ceee452d6c8b75373f61da28f347b157fb96a7d4", "sha256": "2dcca254705ad46c71c774714739640b640c11014db4a49171986e5308b30882",
"bytes": 9120 "bytes": 9120
}, },
{ {
@@ -1972,6 +1982,11 @@
"sha256": "b11ff524bb105035e9320691ca80bdb4e8b616d7c28b1f0d0827b13c0f3914f5", "sha256": "b11ff524bb105035e9320691ca80bdb4e8b616d7c28b1f0d0827b13c0f3914f5",
"bytes": 681 "bytes": 681
}, },
{
"path": "src/lib/buildDependencySecurity.test.ts",
"sha256": "a9e011262388ed6d11a173297819166cf0676cc8d68efff9872e0493c05e1ec7",
"bytes": 1369
},
{ {
"path": "src/lib/clipboard.ts", "path": "src/lib/clipboard.ts",
"sha256": "f85b6cfecaa4814d5c6519ba8d42fe0ebb63d8513eff9ab442c4ea286c3bd4a6", "sha256": "f85b6cfecaa4814d5c6519ba8d42fe0ebb63d8513eff9ab442c4ea286c3bd4a6",
+15 -109
View File
@@ -33,8 +33,7 @@ _TERMINAL = {"preflight_failed", "succeeded", "failed", "rolled_back", "rollback
_BROKER_EVIDENCE_FIELDS = {"request", "repository", "revision", "status"} _BROKER_EVIDENCE_FIELDS = {"request", "repository", "revision", "status"}
_OIDC_EVIDENCE_FIELDS = {"success", "status", "applicationId", "providerId", "clientSecretCreated"} _OIDC_EVIDENCE_FIELDS = {"success", "status", "applicationId", "providerId", "clientSecretCreated"}
_PROXY_EVIDENCE_FIELDS = {"success", "status", "hostId", "hostname"} _PROXY_EVIDENCE_FIELDS = {"success", "status", "hostId", "hostname"}
_AUTO_ROLLBACK_REPOSITORY = "jens/itworx_mcp_hub" _CANONICAL_HUB_REPOSITORY = "jens/itworx_mcp_hub"
_AUTO_ROLLBACK_APPLICATION_ID = "itworx-mcp-hub"
class DeploymentControlError(ValueError): class DeploymentControlError(ValueError):
@@ -312,122 +311,22 @@ class DeploymentControlPlane:
**dict(job.evidence_json or {}), **dict(job.evidence_json or {}),
"postApplyVerification": {"status": "failed", "detail": message}, "postApplyVerification": {"status": "failed", "detail": message},
} }
if (
plan.repository.casefold() != _AUTO_ROLLBACK_REPOSITORY
or plan.application_id != _AUTO_ROLLBACK_APPLICATION_ID
):
job.status = "failed" job.status = "failed"
job.stage = "external_integration_failed" job.stage = "external_integration_failed"
job.result_summary = message if plan.repository.casefold() == _CANONICAL_HUB_REPOSITORY:
job.completed_at = utc_now()
return
self._attempt_auto_rollback(session, job, plan, reason=message)
def _attempt_auto_rollback(
self,
session: Session,
job: DeploymentJob,
plan: DeploymentPlanV1,
*,
reason: str,
) -> None:
"""Restore only the controller-retained prior receipt after verification failure."""
policy = evaluate_policy(
session,
action="deployment.rollback",
target=plan.application_id,
source="manual",
)
exact_policies = session.exec(
select(Policy).where(
(Policy.action == "deployment.rollback")
& (Policy.target == plan.application_id)
)
).all()
exact_policy = exact_policies[0] if len(exact_policies) == 1 else None
policy_is_exact_and_strict = (
exact_policy is not None
and exact_policy.manual == "allowed"
and exact_policy.voice == "blocked"
and exact_policy.risk == "high"
and policy.get("policy_id") == exact_policy.id
)
if policy.get("decision") != "allowed" or not policy_is_exact_and_strict:
job.evidence_json = { job.evidence_json = {
**dict(job.evidence_json or {}), **dict(job.evidence_json or {}),
"autoRollback": {"status": "blocked", "reason": "exact-target-policy"}, "recovery": {
} "status": "manual_required",
job.status = "failed" "reason": "hub_data_aware_recovery_required",
job.stage = "external_integration_failed"
job.result_summary = reason
job.completed_at = utc_now()
return
secret = get_secret(session, plan.broker_secret_ref)
if not secret:
job.evidence_json = {
**dict(job.evidence_json or {}),
"autoRollback": {"status": "blocked", "reason": "controller-secret"},
}
job.status = "failed"
job.stage = "external_integration_failed"
job.result_summary = reason
job.completed_at = utc_now()
return
rollback_key = f"auto-rollback:{job.id}"
rollback = self.broker.rollback(plan, secret=secret, idempotency_key=rollback_key)
rollback_status = str(rollback.get("status") or "failed")
rollback_request = str(rollback.get("request") or "") or None
rollback_evidence = {
key: rollback[key]
for key in _BROKER_EVIDENCE_FIELDS
if key in rollback
}
rollback_evidence["status"] = rollback_status
job.evidence_json = {
**dict(job.evidence_json or {}),
"autoRollback": rollback_evidence,
}
if rollback_request:
job.broker_request_id = rollback_request
if rollback_status == "rolled_back":
job.status = "rolled_back"
job.stage = "auto_rollback_verified"
job.result_summary = (
"Post-apply verification failed; the controller restored the prior verified deployment."
)
job.completed_at = utc_now()
elif rollback_status in {"accepted", "already_queued", "running"}:
if not rollback_request:
job.status = "failed"
job.stage = "auto_rollback_failed"
job.evidence_json = {
**dict(job.evidence_json or {}),
"autoRollback": {
**rollback_evidence,
"reason": "missing-controller-request",
}, },
} }
job.result_summary = ( job.result_summary = (
"Post-apply verification failed and automatic rollback " f"{message} Automatic image-only rollback is disabled for MCP Hub; "
"returned no pollable controller request." "data-aware recovery is required."
) )
job.completed_at = utc_now()
return
job.status = "rolling_back"
job.stage = "auto_rollback_queued"
job.result_summary = (
"Post-apply verification failed; automatic rollback is in progress."
)
job.completed_at = None
else: else:
job.status = "failed" job.result_summary = message
job.stage = "auto_rollback_failed"
job.result_summary = (
"Post-apply verification failed and the controller did not verify rollback."
)
job.completed_at = utc_now() job.completed_at = utc_now()
def execute( def execute(
@@ -438,6 +337,13 @@ class DeploymentControlPlane:
idempotency_key: str, idempotency_key: str,
operation: Literal["preflight", "apply", "rollback"], operation: Literal["preflight", "apply", "rollback"],
) -> dict[str, Any]: ) -> dict[str, Any]:
if (
operation in {"apply", "rollback"}
and plan.repository.casefold() == _CANONICAL_HUB_REPOSITORY
):
raise DeploymentControlError(
"MCP Hub apply and rollback require the signed protected push workflow"
)
job = self.create_job(session, plan=plan, idempotency_key=idempotency_key, operation=operation) job = self.create_job(session, plan=plan, idempotency_key=idempotency_key, operation=operation)
if job.status != "queued": if job.status != "queued":
return self.serialize_job(job, replay=True) return self.serialize_job(job, replay=True)
@@ -12,21 +12,21 @@ _REPOSITORY = "jens/itworx_mcp_hub"
_COMPOSE_PROJECT = "itworx-mcp-hub" _COMPOSE_PROJECT = "itworx-mcp-hub"
_TOPOLOGY_CONTRACT = "itworx.mcp-hub-topology/17-v1" _TOPOLOGY_CONTRACT = "itworx.mcp-hub-topology/17-v1"
# One-time fail-closed re-attestation for the existing MCP Hub runtime. The # Read-only re-attestation for an already-successful modern MCP Hub receipt.
# script never builds, restarts, recreates, stops or deploys a container. It # Unknown, failed, legacy and mixed-topology states require the guarded deploy
# repairs only controller state after proving the exact target, Git ancestry, # path. Reconciliation never repairs or writes controller state.
# complete Compose topology and every currently running image identity.
_RECONCILE_SCRIPT = r''' _RECONCILE_SCRIPT = r'''
import fcntl import fcntl
import errno
import hashlib
import json import json
import os import os
from pathlib import Path from pathlib import Path
import re import re
import shutil
import stat import stat
import subprocess import subprocess
import sys import sys
import time from datetime import datetime, timezone
repository, expected_compose_project = sys.argv[1:3] repository, expected_compose_project = sys.argv[1:3]
registry_path = Path("/controller-config/projects.json") registry_path = Path("/controller-config/projects.json")
@@ -80,7 +80,9 @@ if (
or project.get("slug") != expected_compose_project or project.get("slug") != expected_compose_project
or project.get("compose_project") != expected_compose_project or project.get("compose_project") != expected_compose_project
or not isinstance(expected_containers, list) or not isinstance(expected_containers, list)
or set(expected_containers) != expected_fixed or any(not isinstance(item, str) for item in expected_containers)
or len(expected_containers) != len(set(expected_containers))
or frozenset(expected_containers) not in {frozenset(expected_fixed), frozenset(expected_runtime)}
): ):
raise SystemExit(23) raise SystemExit(23)
expected_containers = sorted(expected_runtime) expected_containers = sorted(expected_runtime)
@@ -89,8 +91,6 @@ state_path = state_root / f"{expected_compose_project}.json"
project_lock_path = state_root / "locks" / f"{expected_compose_project}.lock" project_lock_path = state_root / "locks" / f"{expected_compose_project}.lock"
owner, name = repository.split("/", 1) owner, name = repository.split("/", 1)
bare_repo = Path("/gitea-repositories") / owner / f"{name}.git" bare_repo = Path("/gitea-repositories") / owner / f"{name}.git"
canonical_repository_root = Path("/mnt/cache/appdata/gitea/git/repositories")
canonical_bare_repo = canonical_repository_root / owner / f"{name}.git"
if not bare_repo.is_dir() or bare_repo.is_symlink(): if not bare_repo.is_dir() or bare_repo.is_symlink():
raise SystemExit(24) raise SystemExit(24)
@@ -106,44 +106,6 @@ def git_run(*args):
) )
def canonical_git_run(*args):
return subprocess.run(
["git", f"--git-dir={canonical_bare_repo}", *args],
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
timeout=10,
check=False,
)
def canonical_image_ancestry_is_proven(recorded_revision, image_revision, expected_main):
if not canonical_bare_repo.is_dir() or canonical_bare_repo.is_symlink():
return False
resolved_recorded = canonical_git_run("rev-parse", f"{recorded_revision}^{{commit}}")
resolved_image = canonical_git_run("rev-parse", f"{image_revision}^{{commit}}")
resolved_main = canonical_git_run("rev-parse", "refs/heads/main")
if not (
resolved_recorded.returncode == 0
and resolved_recorded.stdout.strip() == recorded_revision
and resolved_image.returncode == 0
and resolved_image.stdout.strip() == image_revision
and resolved_main.returncode == 0
and resolved_main.stdout.strip() == expected_main
):
return False
return (
canonical_git_run(
"merge-base", "--is-ancestor", recorded_revision, image_revision
).returncode
== 0
and canonical_git_run(
"merge-base", "--is-ancestor", image_revision, expected_main
).returncode
== 0
)
main = git_run("rev-parse", "refs/heads/main") main = git_run("rev-parse", "refs/heads/main")
main_revision = main.stdout.strip() main_revision = main.stdout.strip()
if main.returncode != 0 or re.fullmatch(r"[0-9a-f]{40}", main_revision) is None: if main.returncode != 0 or re.fullmatch(r"[0-9a-f]{40}", main_revision) is None:
@@ -219,13 +181,20 @@ def main_declares_sessionless_dataplane_image(container_name, image_ref):
def main_declares_contextforge_compat_image(container_name, image_ref): def main_declares_contextforge_compat_image(container_name, image_ref):
if container_name != "itworx-mcp-hub-contextforge-1": if container_name != "itworx-mcp-hub-contextforge-1":
return False return False
candidate_reference = (
re.fullmatch(r"[0-9a-f]{40}", recorded_live) is not None
and image_ref == f"itworx-mcp-hub/contextforge-schema:{recorded_live}"
)
# The caller already binds recorded_live to the receipt and protected-main
# ancestry. Read a candidate's build contract from that exact source tree.
proof_revision = recorded_live if candidate_reference else main_revision
manifest = git_run( manifest = git_run(
"show", "show",
f"{main_revision}:infrastructure/images/contextforge/compat-image.env", f"{proof_revision}:infrastructure/images/contextforge/compat-image.env",
) )
builder = git_run( builder = git_run(
"show", "show",
f"{main_revision}:scripts/build_contextforge_compat_image.sh", f"{proof_revision}:scripts/build_contextforge_compat_image.sh",
) )
if manifest.returncode != 0 or builder.returncode != 0: if manifest.returncode != 0 or builder.returncode != 0:
return False return False
@@ -257,7 +226,7 @@ def main_declares_contextforge_compat_image(container_name, image_ref):
or re.fullmatch(r"[0-9a-f]{40}", upstream_commit) is None or re.fullmatch(r"[0-9a-f]{40}", upstream_commit) is None
or re.fullmatch(r"[0-9a-f]{12}", alembic_head) is None or re.fullmatch(r"[0-9a-f]{12}", alembic_head) is None
or re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+", mcp_sdk) is None or re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+", mcp_sdk) is None
or local_image != image_ref or (local_image != image_ref and not candidate_reference)
or local_image != f"itworx-mcp-hub/contextforge-schema:{alembic_head}" or local_image != f"itworx-mcp-hub/contextforge-schema:{alembic_head}"
): ):
return False return False
@@ -269,18 +238,370 @@ def main_declares_contextforge_compat_image(container_name, image_ref):
'alembic -c mcpgateway/alembic.ini heads', 'alembic -c mcpgateway/alembic.ini heads',
'docker tag "$STAGED_IMAGE" "$CONTEXTFORGE_LOCAL_IMAGE"', 'docker tag "$STAGED_IMAGE" "$CONTEXTFORGE_LOCAL_IMAGE"',
) )
if candidate_reference:
required_builder_contract += (
'--release-candidate',
'CANDIDATE_IMAGE="itworx-mcp-hub/contextforge-schema:${APPOPS_SOURCE_REVISION}"',
'docker tag "$STAGED_IMAGE" "$CANDIDATE_IMAGE"',
)
return all(fragment in builder.stdout for fragment in required_builder_contract) return all(fragment in builder.stdout for fragment in required_builder_contract)
state_root.mkdir(parents=True, exist_ok=True) def live_attested_mounts(container):
project_lock_path.parent.mkdir(parents=True, exist_ok=True) raw_mounts = container.get("Mounts")
if not isinstance(raw_mounts, list):
raise SystemExit(52)
observed = []
for raw in raw_mounts:
if not isinstance(raw, dict):
raise SystemExit(52)
kind = str(raw.get("Type") or "")
destination = str(raw.get("Destination") or "")
if kind not in {"bind", "volume", "tmpfs"} or not destination:
raise SystemExit(52)
source = str(raw.get("Source") or "") if kind in {"bind", "volume"} else ""
name = str(raw.get("Name") or "") if kind == "volume" else ""
mount = {
"type": kind,
"source": os.path.normpath(source) if source else "",
"destination": destination,
"rw": raw.get("RW") is True,
"name": name,
}
if kind == "volume":
if not name:
raise SystemExit(52)
volume = subprocess.run(
["docker", "volume", "inspect", name],
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
timeout=10,
check=False,
)
if volume.returncode != 0:
raise SystemExit(52)
try: try:
project_lock = project_lock_path.open("a+", encoding="utf-8") volume_items = json.loads(volume.stdout)
global_lock = global_lock_path.open("a+", encoding="utf-8") volume_item = volume_items[0]
options = volume_item.get("Options") or {}
mountpoint = os.path.normpath(str(volume_item.get("Mountpoint") or ""))
created_at = volume_item.get("CreatedAt")
created = datetime.fromisoformat(str(created_at).replace("Z", "+00:00"))
except Exception:
raise SystemExit(52)
if (
not isinstance(options, dict)
or str(volume_item.get("Name") or "") != name
or mountpoint != mount["source"]
or str(volume_item.get("Driver") or "") != str(raw.get("Driver") or "")
or not isinstance(created_at, str)
or created.tzinfo is None
or created.utcoffset() is None
or created.astimezone(timezone.utc) > datetime.now(timezone.utc)
):
raise SystemExit(52)
mount["driver"] = str(volume_item.get("Driver") or "")
mount["options"] = {str(key): str(value) for key, value in options.items()}
mount["created_at"] = created_at
observed.append(mount)
return sorted(observed, key=lambda item: str(item["destination"]))
def fixed_image_reference(service_name, image_ref):
if service_name == "dataplane-router" and image_ref == "itworx-mcp-hub/dataplane:sessionless-final":
return main_declares_sessionless_dataplane_image(
"itworx-mcp-hub-dataplane-router-1", image_ref
)
owned = {
"connector-appops": "gitea.example.com/jens/itworx-mcp-hub/connector-appops",
"connector-gitea": "gitea.example.com/jens/itworx-mcp-hub/connector-gitea",
"connector-ingestion": "gitea.example.com/jens/itworx-mcp-hub/connector-ingestion",
"connector-knowledge": "gitea.example.com/jens/itworx-mcp-hub/connector-knowledge",
"connector-mobilityops": "gitea.example.com/jens/itworx-mcp-hub/connector-mobilityops",
"connector-projectbrain": "gitea.example.com/jens/itworx-mcp-hub/connector-projectbrain",
"connector-stitch": "gitea.example.com/jens/itworx-mcp-hub/connector-stitch",
"connector-unraid": "gitea.example.com/jens/itworx-mcp-hub/connector-unraid",
"control-api": "gitea.example.com/jens/itworx-mcp-hub/control-api",
"dataplane-router": "gitea.example.com/jens/itworx-mcp-hub/dataplane",
"docker-read-broker": "gitea.example.com/jens/itworx-mcp-hub/docker-read-broker",
"web": "gitea.example.com/jens/itworx-mcp-hub/web",
"worker": "gitea.example.com/jens/itworx-mcp-hub/worker",
}
if service_name in owned:
return (
re.fullmatch(r"sha256:[0-9a-f]{64}", image_ref) is not None
or (
image_ref.startswith(owned[service_name] + "@sha256:")
and re.fullmatch(r".+@sha256:[0-9a-f]{64}", image_ref) is not None
)
)
if service_name == "postgres":
return image_ref == "postgres:18.4@sha256:3a82e1f56c8f0f5616a11103ac3d47e632c3938698946a7ad26da0df1334744a" or (
image_ref == "postgres:18.6@sha256:0377e72c5289ed2f98cf61b1a9c2db9eb9d300317fe14244492fbc94343b3d04"
and main_declares_external_image(image_ref)
)
if service_name == "ingestion-clamd":
return image_ref == "clamav/clamav:1.4.6@sha256:a5f03c12a79dbe9f6d8a527b6bb1ea053fa8dd061d3738a26897f055ee2d9303"
if service_name == "redis":
return image_ref == "redis:8.10.0@sha256:c29e49ab2f85760a3827b53882e6dd9f5c6c3f0bb7d724e07bb31cbf275a5236" or (
image_ref == "redis:8.10.2@sha256:7ef5b5cec96495a04ca7feff88a9492efeab8053fb284d24bdd73344c9245a48"
and main_declares_external_image(image_ref)
)
if service_name == "contextforge":
return main_declares_contextforge_compat_image(
"itworx-mcp-hub-contextforge-1", image_ref
)
return False
def image_digest_matches(image, image_ref):
repository, separator, digest = image_ref.partition("@")
if not separator or re.fullmatch(r"sha256:[0-9a-f]{64}", digest) is None:
return False
first, slash, last = repository.partition("/")
if slash:
if ":" in last:
last = last.rsplit(":", 1)[0]
repository = first + slash + last
elif ":" in repository:
repository = repository.rsplit(":", 1)[0]
aliases = {repository}
first_component = repository.split("/", 1)[0]
is_registry = "." in first_component or ":" in first_component or first_component == "localhost"
if not is_registry:
if "/" in repository:
aliases.add("docker.io/" + repository)
else:
aliases.add("docker.io/library/" + repository)
if repository.startswith("docker.io/"):
aliases.add(repository.removeprefix("docker.io/"))
digests = image.get("RepoDigests", [])
return isinstance(digests, list) and any(
"@" in str(item)
and str(item).split("@", 1)[0] in aliases
and str(item).split("@", 1)[1] == digest
for item in digests
)
def receipt_bound_image_reference(launch_image, image_id, recorded, receipt_item):
if not isinstance(recorded, dict) or not isinstance(receipt_item, dict):
raise SystemExit(52)
image_ref = receipt_item.get("image_ref")
if (
re.fullmatch(r"sha256:[0-9a-f]{64}", image_id) is None
or not isinstance(image_ref, str)
or not image_ref
or recorded.get("image_ref") != image_ref
or recorded.get("image_id") != image_id
or receipt_item.get("image_id") != image_id
or launch_image not in {image_ref, image_id}
or (image_ref.startswith("sha256:") and image_ref != image_id)
):
raise SystemExit(52)
return image_ref
def trusted_private_path(path, expected_mode, *, directory):
try:
info = path.lstat()
except OSError:
return False
if (
path.is_symlink()
or (not stat.S_ISDIR(info.st_mode) if directory else not stat.S_ISREG(info.st_mode))
or info.st_uid != 0
or (stat.S_IMODE(info.st_mode) != expected_mode if directory else stat.S_IMODE(info.st_mode) & 0o077 != 0)
):
return False
try:
attributes = os.listxattr(path, follow_symlinks=False)
except OSError as exc:
if exc.errno in {errno.ENOTSUP, getattr(errno, "EOPNOTSUPP", errno.ENOTSUP)}:
attributes = []
else:
return False
return not any(
item in {"system.posix_acl_access", "system.posix_acl_default"}
for item in attributes
)
def validate_migration_intent_state(
state_root, state_bytes, repository, recorded_live, recorded_status,
recorded_live_status, last_attempt, attempt_status, attempt_revision,
):
attempt_metadata = dict(last_attempt.get("metadata") or {})
migration_attempt_id = str(attempt_metadata.get("mcpHubMigrationAttemptId") or "")
migration_root = state_root / "mcp-hub-topology-migrations"
migration_path = migration_root / "active-intent.json"
if migration_root.is_symlink():
raise SystemExit(56)
if migration_root.exists() and not trusted_private_path(migration_root, 0o700, directory=True):
raise SystemExit(56)
if migration_path.is_symlink():
raise SystemExit(56)
if migration_path.exists():
if (
not trusted_private_path(migration_path, 0o600, directory=False)
or stat.S_IMODE(migration_path.lstat().st_mode) != 0o600
):
raise SystemExit(56)
try:
intent_bytes = migration_path.read_bytes()
intent = json.loads(intent_bytes.decode("utf-8"))
except Exception:
raise SystemExit(56)
common_intent_fields = {
"schema", "status", "attemptId", "repository", "candidateRevision",
"priorLiveRevision", "backupManifestSha256", "backupComposeSha256",
"baselineStateSha256", "startedAtEpoch",
}
observed_intent_fields = {"observedBaselinePath", "observedBaselineSha256"}
observed_fields_present = set(intent) & observed_intent_fields if isinstance(intent, dict) else set()
if observed_fields_present not in (set(), observed_intent_fields):
raise SystemExit(56)
required_intent_fields = common_intent_fields | observed_fields_present
if isinstance(intent, dict) and intent.get("status") == "complete":
required_intent_fields |= {"completedAtEpoch", "completedStateSha256"}
if (
not isinstance(intent, dict)
or intent.get("status") != "complete"
or set(intent) != required_intent_fields
or intent.get("schema") != "itworx.mcp-hub-migration-intent/v1"
or intent.get("repository") != repository
or re.fullmatch(r"[0-9a-f]{32}", str(intent.get("attemptId") or "")) is None
or intent.get("attemptId") != migration_attempt_id
or intent.get("candidateRevision") != recorded_live
or re.fullmatch(r"[0-9a-f]{40}", str(intent.get("priorLiveRevision") or "")) is None
or any(
re.fullmatch(r"[0-9a-f]{64}", str(intent.get(field) or "")) is None
for field in ("backupManifestSha256", "backupComposeSha256", "baselineStateSha256", "completedStateSha256")
)
or type(intent.get("startedAtEpoch")) is not int
or type(intent.get("completedAtEpoch")) is not int
or intent.get("completedAtEpoch") < intent.get("startedAtEpoch")
or intent.get("completedStateSha256") != hashlib.sha256(state_bytes).hexdigest()
or recorded_status != "success"
or recorded_live_status != "healthy"
or attempt_status != "success"
or attempt_revision != recorded_live
or not isinstance(attempt_metadata.get("mcpHubBackupReceipt"), dict)
or set(attempt_metadata.get("mcpHubBackupReceipt", {})) != {
"schema", "repository", "revision", "backupDirectory", "manifestSha256",
"composeSha256", "restoreVerified", "createdAtEpoch", "receiptPath",
}
or attempt_metadata.get("mcpHubBackupReceipt", {}).get("schema") != "itworx.mcp-hub-backup-receipt/v1"
or attempt_metadata.get("mcpHubBackupReceipt", {}).get("repository") != repository
or attempt_metadata.get("mcpHubBackupReceipt", {}).get("revision") != recorded_live
or attempt_metadata.get("mcpHubBackupReceipt", {}).get("restoreVerified") is not True
or type(attempt_metadata.get("mcpHubBackupReceipt", {}).get("createdAtEpoch")) is not int
or re.fullmatch(r"[0-9a-f]{64}", str(attempt_metadata.get("mcpHubBackupReceipt", {}).get("manifestSha256") or "")) is None
or re.fullmatch(r"[0-9a-f]{64}", str(attempt_metadata.get("mcpHubBackupReceipt", {}).get("composeSha256") or "")) is None
or attempt_metadata.get("mcpHubBackupReceipt", {}).get("manifestSha256") != intent.get("backupManifestSha256")
or attempt_metadata.get("mcpHubBackupReceipt", {}).get("composeSha256") != intent.get("backupComposeSha256")
):
raise SystemExit(56)
backup_receipt = attempt_metadata.get("mcpHubBackupReceipt", {})
receipt_root = state_root / "mcp-hub-backup-receipts"
backup_path = Path(str(backup_receipt.get("receiptPath") or ""))
if (
backup_path.parent != receipt_root
or re.fullmatch(
rf"[0-9]+-{recorded_live[:12]}-[0-9a-f]{{16}}\.json",
backup_path.name,
) is None
or backup_path.is_symlink()
or not trusted_private_path(receipt_root, 0o700, directory=True)
or not trusted_private_path(backup_path, 0o600, directory=False)
or stat.S_IMODE(backup_path.lstat().st_mode) != 0o600
):
raise SystemExit(56)
try:
stored_backup_receipt = json.loads(backup_path.read_text(encoding="utf-8"))
except Exception:
raise SystemExit(56)
if stored_backup_receipt != {
key: value for key, value in backup_receipt.items() if key != "receiptPath"
}:
raise SystemExit(56)
backup_root = state_root / "mcp-hub-backups"
backup_directory = Path(str(backup_receipt.get("backupDirectory") or ""))
manifest_path = backup_directory / "manifest.json"
if (
backup_directory.parent != backup_root
or re.fullmatch(
rf"[0-9]+-{recorded_live[:12]}-[0-9a-f]{{16}}",
backup_directory.name,
) is None
or not trusted_private_path(backup_root, 0o700, directory=True)
or not trusted_private_path(backup_directory, 0o700, directory=True)
or not trusted_private_path(manifest_path, 0o600, directory=False)
or stat.S_IMODE(manifest_path.lstat().st_mode) != 0o600
):
raise SystemExit(56)
try:
manifest_bytes = manifest_path.read_bytes()
manifest = json.loads(manifest_bytes.decode("utf-8"))
except Exception:
raise SystemExit(56)
if (
hashlib.sha256(manifest_bytes).hexdigest() != intent.get("backupManifestSha256")
or not isinstance(manifest, dict)
or manifest.get("schema") != "itworx.mcp-hub-backup/v1"
or manifest.get("repository") != repository
or manifest.get("revision") != recorded_live
or manifest.get("backupDirectory") != str(backup_directory)
or manifest.get("composeSha256") != intent.get("backupComposeSha256")
or manifest.get("configRestoreVerified") is not True
or dict(manifest.get("restore") or {}).get("status") != "verified"
or set(dict(manifest.get("databases") or {})) != {"itworx_mcp_hub", "contextforge"}
):
raise SystemExit(56)
if observed_fields_present:
observed_path = Path(str(intent.get("observedBaselinePath") or ""))
if (
observed_path.parent != migration_root
or re.fullmatch(
rf"observed-baseline-{recorded_live[:12]}-[0-9a-f]{{20}}\.json",
observed_path.name,
) is None
or observed_path.is_symlink()
or not trusted_private_path(observed_path, 0o600, directory=False)
or stat.S_IMODE(observed_path.lstat().st_mode) != 0o600
):
raise SystemExit(56)
try:
observed_bytes = observed_path.read_bytes()
observed = json.loads(observed_bytes.decode("utf-8"))
except Exception:
raise SystemExit(56)
if (
hashlib.sha256(observed_bytes).hexdigest() != intent.get("observedBaselineSha256")
or not isinstance(observed, dict)
or observed.get("schema") != "itworx.mcp-hub-observed-baseline/v1"
or observed.get("claim") != "observed_mixed_runtime_facts_only"
or observed.get("candidateRevision") != recorded_live
or observed.get("historicalStateSha256") != intent.get("baselineStateSha256")
or observed.get("backupManifestSha256") != intent.get("backupManifestSha256")
or observed.get("backupComposeSha256") != intent.get("backupComposeSha256")
or observed.get("backupRestoreVerified") is not True
or not isinstance(observed.get("postgresVolumeBaseline"), list)
or len(observed.get("postgresVolumeBaseline", [])) != 2
):
raise SystemExit(56)
elif migration_attempt_id:
raise SystemExit(56)
try:
project_lock_fd = os.open(project_lock_path, os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0))
global_lock_fd = os.open(global_lock_path, os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0))
except Exception: except Exception:
raise SystemExit(29) raise SystemExit(29)
with project_lock: with os.fdopen(project_lock_fd, "rb") as project_lock, os.fdopen(global_lock_fd, "rb") as global_lock:
try: try:
fcntl.flock(project_lock, fcntl.LOCK_EX) fcntl.flock(project_lock, fcntl.LOCK_EX)
except Exception: except Exception:
@@ -298,10 +619,17 @@ with project_lock:
if current_projects.get(repository) != project: if current_projects.get(repository) != project:
raise SystemExit(37) raise SystemExit(37)
if state_path.is_symlink() or not state_path.is_file(): if (
not trusted_private_path(state_root, 0o700, directory=True)
or not trusted_private_path(state_root / "locks", 0o700, directory=True)
or not trusted_private_path(project_lock_path, 0o600, directory=False)
or not trusted_private_path(global_lock_path, 0o600, directory=False)
or not trusted_private_path(state_path, 0o600, directory=False)
):
raise SystemExit(42) raise SystemExit(42)
try: try:
state = json.loads(state_path.read_text(encoding="utf-8")) state_bytes = state_path.read_bytes()
state = json.loads(state_bytes.decode("utf-8"))
except Exception: except Exception:
raise SystemExit(42) raise SystemExit(42)
if not isinstance(state, dict) or state.get("schema_version") != 3 or state.get("repository") != repository: if not isinstance(state, dict) or state.get("schema_version") != 3 or state.get("repository") != repository:
@@ -313,9 +641,12 @@ with project_lock:
last_attempt = dict(state.get("last_attempt") or {}) last_attempt = dict(state.get("last_attempt") or {})
attempt_revision = str(last_attempt.get("revision") or "") attempt_revision = str(last_attempt.get("revision") or "")
attempt_status = str(last_attempt.get("status") or "") attempt_status = str(last_attempt.get("status") or "")
last_attempt_message = str(last_attempt.get("message") or "")
receipt = state.get("live_receipt") receipt = state.get("live_receipt")
recorded_artifacts = state.get("live_artifacts") recorded_artifacts = state.get("live_artifacts")
validate_migration_intent_state(
state_root, state_bytes, repository, recorded_live, recorded_status,
recorded_live_status, last_attempt, attempt_status, attempt_revision,
)
registered_live_containers = ( registered_live_containers = (
recorded_artifacts.get("containers") recorded_artifacts.get("containers")
@@ -326,86 +657,73 @@ with project_lock:
print("mcp-hub-topology-migration-required", flush=True) print("mcp-hub-topology-migration-required", flush=True)
raise SystemExit(56) raise SystemExit(56)
if set(expected_containers) != expected_runtime:
raise SystemExit(56)
receipt_payload = receipt if isinstance(receipt, dict) else {}
receipt_tree = str(receipt_payload.get("tree") or "")
receipt_tree_digest = str(receipt_payload.get("treeDigest") or "")
raw_receipt_build = receipt_payload.get("build")
receipt_build = raw_receipt_build if isinstance(raw_receipt_build, dict) else {}
expected_build = {
"id": recorded_live[:12],
"repository": repository,
"revision": recorded_live,
"tree": receipt_tree,
"treeDigest": receipt_tree_digest,
"builtAt": str(receipt_build.get("builtAt") or ""),
}
receipt_identity_valid = (
re.fullmatch(r"[0-9a-f]{40}", receipt_tree) is not None
and receipt_tree_digest == hashlib.sha256(
f"git-tree:{receipt_tree}".encode("utf-8")
).hexdigest()
and receipt_build == expected_build
and re.fullmatch(
r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z",
expected_build["builtAt"],
) is not None
and isinstance(recorded_artifacts, dict)
and set(recorded_artifacts)
== {"containers", "compose_sha256", "topology_contract"}
and re.fullmatch(
r"[0-9a-f]{64}", str(recorded_artifacts.get("compose_sha256") or "")
) is not None
)
already_reconciled = ( already_reconciled = (
recorded_status == "success" recorded_status == "success"
and recorded_live_status == "healthy" and recorded_live_status == "healthy"
and state.get("revision") == recorded_live
and state.get("live_legacy_provenance") is False
and re.fullmatch(r"[0-9a-f]{40}", recorded_live) is not None and re.fullmatch(r"[0-9a-f]{40}", recorded_live) is not None
and isinstance(recorded_artifacts, dict) and isinstance(recorded_artifacts, dict)
and state.get("compose_project") == expected_compose_project and state.get("compose_project") == expected_compose_project
and state.get("slug") == expected_compose_project
and attempt_status == "success"
and attempt_revision == recorded_live
and isinstance(registered_live_containers, dict) and isinstance(registered_live_containers, dict)
and set(registered_live_containers) == expected_runtime and set(registered_live_containers) == expected_runtime
and recorded_artifacts.get("topology_contract") == topology_contract and recorded_artifacts.get("topology_contract") == topology_contract
and ( and state.get("artifacts") == recorded_artifacts
state.get("live_legacy_provenance") is True and isinstance(receipt, dict)
or ( and set(receipt) == {"schema", "repository", "revision", "tree", "treeDigest", "build", "containers", "live"}
isinstance(receipt, dict)
and receipt.get("schema") == "gitea-deploy-receipt.v1" and receipt.get("schema") == "gitea-deploy-receipt.v1"
and receipt.get("repository") == repository and receipt.get("repository") == repository
and receipt.get("revision") == recorded_live and receipt.get("revision") == recorded_live
and receipt_identity_valid
and isinstance(receipt.get("containers"), dict)
and set(dict(receipt.get("containers") or {})) == expected_runtime
and receipt.get("live") is None
) )
) if not already_reconciled:
) raise SystemExit(56 if recorded_live_status in {"unknown", "healthy"} else 52)
if not already_reconciled and (
recorded_status != "failed"
or recorded_live_status not in {"healthy", "unknown"}
or state.get("live_legacy_provenance") is not False
or attempt_status != "failed"
or re.fullmatch(r"[0-9a-f]{40}", recorded_live) is None
or re.fullmatch(r"[0-9a-f]{40}", attempt_revision) is None
or attempt_revision == recorded_live
or not isinstance(receipt, dict)
or receipt.get("schema") != "gitea-deploy-receipt.v1"
or receipt.get("repository") != repository
or receipt.get("revision") != recorded_live
or not isinstance(recorded_artifacts, dict)
):
raise SystemExit(52)
# deploy.py deliberately downgrades the prior live status to unknown
# when capture_compose_rollback() fails before production mutation.
# Accept that state only when the durable attempt message is one of the
# bounded snapshot-stage failure classes and no rollback/source-restore
# error was recorded. The runtime is still re-proven container by
# container below before any controller state is changed.
snapshot_failure_prefixes = (
"Refusing to mutate a Compose project with unrecorded containers:",
"Deployment state claims a live revision but all production containers are missing",
"Cannot capture a complete rollback snapshot; containers are missing:",
"Existing production containers require a valid healthy live deployment state",
"Container ",
"Existing deployment state is not bound to its live revision",
"Recorded deployment receipt ",
"Existing deployment no longer matches its verified live receipt",
"Expected containers are not healthy/running:",
"Compose project container set does not match the allowlisted runtime:",
"Containers are linked to the wrong Compose project:",
"Running container image IDs do not match the verified artifacts:",
"Immutable image provenance does not match the requested deployment:",
)
if (
not already_reconciled
and recorded_live_status == "unknown"
and (
not last_attempt_message.startswith(snapshot_failure_prefixes)
or "; rollback error:" in last_attempt_message
or "; source rollback error:" in last_attempt_message
)
):
raise SystemExit(55)
resolved_live = git_run("rev-parse", f"{recorded_live}^{{commit}}") resolved_live = git_run("rev-parse", f"{recorded_live}^{{commit}}")
if resolved_live.returncode != 0 or resolved_live.stdout.strip() != recorded_live: if resolved_live.returncode != 0 or resolved_live.stdout.strip() != recorded_live:
raise SystemExit(25) raise SystemExit(25)
if git_run("merge-base", "--is-ancestor", recorded_live, main_revision).returncode != 0: if git_run("merge-base", "--is-ancestor", recorded_live, main_revision).returncode != 0:
raise SystemExit(52) raise SystemExit(52)
if not already_reconciled:
resolved_attempt = git_run("rev-parse", f"{attempt_revision}^{{commit}}")
if resolved_attempt.returncode != 0 or resolved_attempt.stdout.strip() != attempt_revision:
raise SystemExit(25)
if git_run("merge-base", "--is-ancestor", recorded_live, attempt_revision).returncode != 0:
raise SystemExit(52)
if git_run("merge-base", "--is-ancestor", attempt_revision, main_revision).returncode != 0:
raise SystemExit(52)
listed = subprocess.run( listed = subprocess.run(
[ [
@@ -428,11 +746,10 @@ with project_lock:
raise SystemExit(47) raise SystemExit(47)
old_containers = recorded_artifacts.get("containers") old_containers = recorded_artifacts.get("containers")
receipt_containers = dict(receipt.get("containers") or {})
if not isinstance(old_containers, dict) or set(old_containers) != set(expected_containers): if not isinstance(old_containers, dict) or set(old_containers) != set(expected_containers):
raise SystemExit(52) raise SystemExit(52)
container_artifacts = {}
drift_found = False
for name in expected_containers: for name in expected_containers:
inspected = subprocess.run( inspected = subprocess.run(
["docker", "inspect", name], ["docker", "inspect", name],
@@ -450,17 +767,31 @@ with project_lock:
raise SystemExit(44) raise SystemExit(44)
runtime_state = dict(container.get("State") or {}) runtime_state = dict(container.get("State") or {})
health = str(dict(runtime_state.get("Health") or {}).get("Status") or "none") health = str(dict(runtime_state.get("Health") or {}).get("Status") or "none")
labels = dict(dict(container.get("Config") or {}).get("Labels") or {})
config = dict(container.get("Config") or {}) config = dict(container.get("Config") or {})
labels = dict(config.get("Labels") or {})
name_service = name.removeprefix("itworx-mcp-hub-").removesuffix("-1")
image_id = str(container.get("Image") or "") image_id = str(container.get("Image") or "")
image_ref = str(config.get("Image") or "") launch_image = str(config.get("Image") or "")
if not runtime_state.get("Running") or health in {"starting", "unhealthy"}: container_id = str(container.get("Id") or "")
if runtime_state.get("Running") is not True or health != "healthy":
raise SystemExit(45) raise SystemExit(45)
if labels.get("com.docker.compose.project") != expected_compose_project: if (
labels.get("com.docker.compose.project") != expected_compose_project
or labels.get("com.docker.compose.service") != name_service
):
raise SystemExit(47) raise SystemExit(47)
if re.fullmatch(r"sha256:[0-9a-f]{64}", image_id) is None or not image_ref: if (
re.fullmatch(r"sha256:[0-9a-f]{64}", image_id) is None
or not launch_image
or not container_id
):
raise SystemExit(46) raise SystemExit(46)
recorded = old_containers.get(name)
receipt_item = receipt_containers.get(name)
image_ref = receipt_bound_image_reference(
launch_image, image_id, recorded, receipt_item
)
image_result = subprocess.run( image_result = subprocess.run(
["docker", "image", "inspect", image_id], ["docker", "image", "inspect", image_id],
text=True, text=True,
@@ -476,149 +807,80 @@ with project_lock:
except Exception: except Exception:
raise SystemExit(50) raise SystemExit(50)
image_labels = dict(dict(image.get("Config") or {}).get("Labels") or {}) image_labels = dict(dict(image.get("Config") or {}).get("Labels") or {})
image_source = str(image_labels.get("org.opencontainers.image.source") or "") artifact = {
image_revision = str(image_labels.get("org.opencontainers.image.revision") or "")
recorded = old_containers.get(name)
if not isinstance(recorded, dict):
raise SystemExit(52)
recorded_image_id = str(recorded.get("image_id") or "")
recorded_container_id = str(recorded.get("container_id") or "")
if re.fullmatch(r"sha256:[0-9a-f]{64}", recorded_image_id) is None:
raise SystemExit(52)
image_changed = image_id != recorded_image_id
if image_changed:
contextforge_compat_image = main_declares_contextforge_compat_image(name, image_ref)
sessionless_dataplane_image = (
not already_reconciled
and main_declares_sessionless_dataplane_image(name, image_ref)
)
if contextforge_compat_image or sessionless_dataplane_image:
pass
elif image_source == owned_image_source:
if re.fullmatch(r"[0-9a-f]{40}", image_revision) is None:
raise SystemExit(51)
resolved_image = git_run("rev-parse", f"{image_revision}^{{commit}}")
mounted_recorded_to_image = git_run("merge-base", "--is-ancestor", recorded_live, image_revision)
mounted_image_to_main = git_run("merge-base", "--is-ancestor", image_revision, main_revision)
mounted_ancestry_proven = (
resolved_image.returncode == 0
and resolved_image.stdout.strip() == image_revision
and mounted_recorded_to_image.returncode == 0
and mounted_image_to_main.returncode == 0
)
if not mounted_ancestry_proven and not canonical_image_ancestry_is_proven(
recorded_live, image_revision, main_revision
):
raise SystemExit(51)
elif not main_declares_external_image(image_ref):
raise SystemExit(51)
container_id = str(container.get("Id") or "")
if not container_id:
raise SystemExit(46)
if image_id != recorded_image_id or container_id != recorded_container_id:
drift_found = True
container_artifacts[name] = {
"container_id": container_id, "container_id": container_id,
"image_id": image_id, "image_id": image_id,
"image_ref": image_ref, "image_ref": image_ref,
"container_revision": str(labels.get("org.opencontainers.image.revision") or ""), "container_revision": str(labels.get("org.opencontainers.image.revision") or ""),
"image_revision": image_revision, "image_revision": str(image_labels.get("org.opencontainers.image.revision") or ""),
"image_source": image_source, "image_source": str(image_labels.get("org.opencontainers.image.source") or ""),
"image_tree": str(image_labels.get("org.opencontainers.image.source-tree") or ""), "image_tree": str(image_labels.get("org.opencontainers.image.source-tree") or ""),
"image_version": str(image_labels.get("org.opencontainers.image.version") or ""), "image_version": str(image_labels.get("org.opencontainers.image.version") or ""),
"image_created": str(image_labels.get("org.opencontainers.image.created") or ""), "image_created": str(image_labels.get("org.opencontainers.image.created") or ""),
} }
if str(image.get("Id") or "") != image_id:
if already_reconciled: raise SystemExit(46)
if drift_found: if (
raise SystemExit(52) set(recorded) != set(artifact) | {"mounts_json"}
print("already-reconciled", flush=True) or set(receipt_item) != set(artifact) | {"mounts_sha256"}
raise SystemExit(0)
if not drift_found:
raise SystemExit(52)
artifacts = {
"containers": container_artifacts,
"compose_sha256": "",
"topology_contract": topology_contract,
}
now = int(time.time())
payload = {
"schema_version": 3,
"repository": repository,
"slug": expected_compose_project,
"compose_project": expected_compose_project,
"revision": recorded_live,
"live_revision": recorded_live,
"live_status": "healthy",
"live_legacy_provenance": True,
"live_artifacts": artifacts,
"live_receipt": {},
"rollback_snapshot": {},
"status": "success",
"message": "Verified mixed MCP Hub runtime reconciled as a legacy provenance baseline",
"updated_at_epoch": now,
"artifacts": artifacts,
"last_attempt": {
"revision": recorded_live,
"status": "success",
"message": "Verified MCP Hub runtime state reconciliation",
"updated_at_epoch": now,
"metadata": {"reconciled_failed_revision": attempt_revision},
},
"previous_revision": recorded_live,
"rollback_restored_revision": "",
}
backup = state_path.with_name(f"{state_path.name}.mcp-hub-reconcile-backup-{now}-{os.getpid()}")
temporary = state_path.with_name(f".{state_path.name}.{os.getpid()}.mcp-hub-reconcile.tmp")
try:
mode = stat.S_IMODE(state_path.stat().st_mode)
shutil.copy2(state_path, backup)
with temporary.open("x", encoding="utf-8") as handle:
handle.write(json.dumps(payload, indent=2, sort_keys=True) + "\n")
handle.flush()
os.fsync(handle.fileno())
os.chmod(temporary, mode)
os.replace(temporary, state_path)
directory_fd = os.open(str(state_root), os.O_RDONLY)
try:
os.fsync(directory_fd)
finally:
os.close(directory_fd)
except Exception:
try:
temporary.unlink(missing_ok=True)
except Exception:
pass
raise SystemExit(53)
try:
verify = json.loads(state_path.read_text(encoding="utf-8"))
except Exception:
raise SystemExit(54)
verified_containers = dict(dict(verify.get("live_artifacts") or {}).get("containers") or {})
if not (
verify.get("repository") == repository
and verify.get("live_revision") == recorded_live
and verify.get("live_status") == "healthy"
and verify.get("live_legacy_provenance") is True
and verify.get("status") == "success"
and set(verified_containers) == set(expected_containers)
and all(str(dict(verified_containers[name]).get("image_id") or "") == str(container_artifacts[name]["image_id"]) for name in expected_containers)
): ):
raise SystemExit(54) raise SystemExit(52)
for key, value in artifact.items():
if recorded.get(key) != value or receipt_item.get(key) != value:
raise SystemExit(52)
print("reconciled", flush=True) raw_mounts = recorded.get("mounts_json")
if not isinstance(raw_mounts, str):
raise SystemExit(52)
try:
recorded_mounts = json.loads(raw_mounts)
except Exception:
raise SystemExit(52)
mount_bytes = json.dumps(
recorded_mounts, sort_keys=True, separators=(",", ":")
).encode("utf-8")
if (
not isinstance(recorded_mounts, list)
or hashlib.sha256(mount_bytes).hexdigest() != receipt_item.get("mounts_sha256")
or live_attested_mounts(container) != recorded_mounts
):
raise SystemExit(52)
if not fixed_image_reference(name_service, image_ref):
raise SystemExit(51)
owned_service = name_service in {
"connector-appops", "connector-gitea", "connector-ingestion",
"connector-knowledge", "connector-mobilityops", "connector-projectbrain",
"connector-stitch", "connector-unraid", "control-api", "dataplane-router",
"docker-read-broker", "web", "worker",
}
if owned_service and image_ref != "itworx-mcp-hub/dataplane:sessionless-final":
if (
artifact["image_source"] != owned_image_source
or artifact["image_revision"] != recorded_live
or artifact["image_tree"] != receipt_tree
or artifact["image_version"] != recorded_live[:12]
or artifact["image_created"] != expected_build["builtAt"]
):
raise SystemExit(51)
if "@sha256:" in image_ref and not image_digest_matches(image, image_ref):
raise SystemExit(51)
try:
if state_path.read_bytes() != state_bytes:
raise SystemExit(52)
current_projects = json.loads(registry_path.read_text(encoding="utf-8"))
except Exception:
raise SystemExit(37)
if current_projects.get(repository) != project:
raise SystemExit(37)
print("already-reconciled", flush=True)
''' '''
class McpHubDeploymentReconciliation: class McpHubDeploymentReconciliation:
"""Re-attest only the exact proven MCP Hub state drift, without touching runtime.""" """Read-only verify only an existing, exact modern MCP Hub success receipt."""
def __init__(self, portainer: PortainerConnector | None = None): def __init__(self, portainer: PortainerConnector | None = None):
self.portainer = portainer or PortainerConnector() self.portainer = portainer or PortainerConnector()
@@ -706,11 +968,8 @@ class McpHubDeploymentReconciliation:
47: "live MCP Hub Compose project differs from the controller target", 47: "live MCP Hub Compose project differs from the controller target",
50: "a live MCP Hub image cannot be inspected", 50: "a live MCP Hub image cannot be inspected",
51: "live MCP Hub image revision or external immutable image is not proven by the recorded-live to main chain", 51: "live MCP Hub image revision or external immutable image is not proven by the recorded-live to main chain",
52: "controller state drift is not the exact known recoverable MCP Hub state", 52: "controller state or live facts do not match the exact modern MCP Hub receipt",
53: "reconciled MCP Hub deployment state could not be written durably", 56: "MCP Hub legacy, unknown, mixed, or unresolved migration state requires guarded deployment or data-aware recovery",
54: "reconciled MCP Hub deployment state did not verify after write",
55: "unknown MCP Hub live status was not produced by a proven recoverable snapshot-stage failure",
56: "MCP Hub 15-to-17 topology migration requires its guarded protected-main deployment",
} }
if exit_code != 0: if exit_code != 0:
if exit_code == 56: if exit_code == 56:
+125
View File
@@ -5718,6 +5718,131 @@ def test_appops_request_is_idempotent_and_runs_only_fixed_deploy(status_module,
assert calls == [["python3", str(status_module.ROOT / "deploy.py"), "deploy", "jens/repo-a", "a" * 40]] assert calls == [["python3", str(status_module.ROOT / "deploy.py"), "deploy", "jens/repo-a", "a" * 40]]
@pytest.mark.parametrize("operation", ["apply", "rollback"])
def test_generic_appops_hub_request_is_rejected_before_inbox_creation(
status_module, tmp_path, monkeypatch, operation
):
inbox = tmp_path / "appops-inbox"
monkeypatch.setattr(status_module, "APPOPS_DEPLOYMENT_INBOX_ROOT", inbox)
status, request_id = status_module._enqueue_appops_request(
repository="Jens/ITWorx_MCP_Hub",
revision="a" * 40,
operation=operation,
idempotency_key=f"appops-hub-{operation}-0001",
plan={},
)
assert (status, request_id) == ("invalid", "")
assert not inbox.exists()
@pytest.mark.parametrize("operation", ["apply", "rollback"])
@pytest.mark.parametrize(
("prior_status", "expected_status"),
[("pending", "failed"), ("running", "outcome_unknown")],
)
def test_direct_appops_hub_record_is_denied_before_project_or_deploy_access(
status_module, tmp_path, monkeypatch, operation, prior_status, expected_status
):
inbox = tmp_path / "appops-inbox"
inbox.mkdir()
request_id = "a" * 64
marker = inbox / f"{request_id}.json"
marker.write_text(
json.dumps(
{
"schema": "appops-deployment-request.v1",
"request": request_id,
"repository": "jens/itworx_mcp_hub",
"revision": "b" * 40,
"operation": operation,
"plan_sha256": "c" * 64,
"status": prior_status,
"received_at": 1,
"updated_at": 1,
}
),
encoding="utf-8",
)
monkeypatch.setattr(
status_module,
"_load_projects",
lambda: pytest.fail("generic Hub records must not inspect deployment projects"),
)
monkeypatch.setattr(
status_module.subprocess,
"run",
lambda *_args, **_kwargs: pytest.fail("generic Hub records must not launch deploy.py"),
)
status_module._process_appops_record(marker)
record = json.loads(marker.read_text(encoding="utf-8"))
assert record["status"] == expected_status
assert record["reason"] == "canonical_hub_push_required"
def test_hub_protected_push_worker_remains_the_fixed_deploy_path(
status_module, tmp_path, monkeypatch
):
repository = "jens/itworx_mcp_hub"
inbox = tmp_path / "webhook-inbox"
projects = tmp_path / "projects.json"
project = {
**_webhook_project(repository),
"slug": "itworx-mcp-hub",
"containers": ["itworx-mcp-hub"],
}
projects.write_text(json.dumps({repository: project}), encoding="utf-8")
monkeypatch.setattr(status_module, "WEBHOOK_INBOX_ROOT", inbox)
monkeypatch.setattr(status_module, "PROJECTS_PATH", projects)
revision = "d" * 40
raw_body = json.dumps(
_push_payload(revision, repository=repository), separators=(",", ":")
).encode()
_, request_id = status_module._enqueue_webhook_delivery(
raw_body,
"145440ab-b7b1-4c74-ae84-996d8d48167e",
77,
"refs/heads/main",
repository,
revision,
status_module._validation_policy(repository, project),
)
marker = inbox / f"{request_id}.json"
calls = []
class Result:
returncode = 0
monkeypatch.setattr(
status_module.subprocess,
"run",
lambda arguments, **_kwargs: calls.append(arguments) or Result(),
)
monkeypatch.setattr(status_module, "_deployment_state", lambda _slug: {})
monkeypatch.setattr(status_module, "_verified_receipt", lambda *_args: {"ok": True})
monkeypatch.setattr(
status_module,
"_fetch_gitea_validation",
lambda *_args: (
"pass",
[{
"context": "Managed validation / full (push)",
"status": "success",
"updated_at": "2026-09-14T00:00:00Z",
}],
),
)
status_module._process_webhook_record(marker)
assert json.loads(marker.read_text(encoding="utf-8"))["status"] == "deployed"
assert calls == [["python3", str(status_module.ROOT / "deploy.py"), "deploy", repository, revision]]
def test_durable_rollback_snapshot_is_receipt_bound_and_not_caller_selected(deploy_module, tmp_path, monkeypatch): def test_durable_rollback_snapshot_is_receipt_bound_and_not_caller_selected(deploy_module, tmp_path, monkeypatch):
previous_revision = "a" * 40 previous_revision = "a" * 40
current_revision = "b" * 40 current_revision = "b" * 40
+58 -261
View File
@@ -1,10 +1,14 @@
from __future__ import annotations from __future__ import annotations
from sqlmodel import Session, SQLModel, create_engine import pytest
from sqlmodel import Session, SQLModel, create_engine, select
from backend.app.db.models import Policy from backend.app.db.models import DeploymentJob, Policy
from backend.app.schemas.deployment import DeploymentPlanV1 from backend.app.schemas.deployment import DeploymentPlanV1
from backend.app.services.deployment_control_plane import DeploymentControlPlane from backend.app.services.deployment_control_plane import (
DeploymentControlError,
DeploymentControlPlane,
)
from backend.app.services.secret_store import set_secret from backend.app.services.secret_store import set_secret
@@ -31,19 +35,22 @@ def _plan() -> DeploymentPlanV1:
class _Broker: class _Broker:
def __init__(self, rollback_status: str = "rolled_back") -> None: def __init__(self) -> None:
self.rollback_status = rollback_status self.preflight_calls = 0
self.apply_calls: list[str] = []
self.rollback_calls: list[str] = [] self.rollback_calls: list[str] = []
def preflight(self, plan, *, secret): def preflight(self, plan, *, secret):
self.preflight_calls += 1
return {"status": "preflight_ok"} return {"status": "preflight_ok"}
def apply(self, plan, *, secret, idempotency_key): def apply(self, plan, *, secret, idempotency_key):
self.apply_calls.append(idempotency_key)
return {"status": "deployed", "request": "a" * 64} return {"status": "deployed", "request": "a" * 64}
def rollback(self, plan, *, secret, idempotency_key): def rollback(self, plan, *, secret, idempotency_key):
self.rollback_calls.append(idempotency_key) self.rollback_calls.append(idempotency_key)
return {"status": self.rollback_status, "request": "b" * 64} return {"status": "rolled_back", "request": "b" * 64}
def status(self, request_id, *, secret): def status(self, request_id, *, secret):
return {"status": "rolled_back", "request": request_id} return {"status": "rolled_back", "request": request_id}
@@ -103,10 +110,9 @@ def _plane(broker: _Broker) -> DeploymentControlPlane:
) )
def _configure(session: Session, *, rollback_policy: bool = True) -> None: def _configure(session: Session) -> None:
set_secret(session, "MCP_HUB_TEST_DEPLOYMENT_BROKER", "x" * 32) set_secret(session, "MCP_HUB_TEST_DEPLOYMENT_BROKER", "x" * 32)
session.add(_policy("deployment.apply")) session.add(_policy("deployment.apply"))
if rollback_policy:
session.add(_policy("deployment.rollback")) session.add(_policy("deployment.rollback"))
session.commit() session.commit()
@@ -117,284 +123,75 @@ def test_default_compose_file_survives_durable_plan_roundtrip():
assert restored.topology.compose_file == ".itworx/deployment.yml" assert restored.topology.compose_file == ".itworx/deployment.yml"
def test_post_apply_verification_failure_auto_rolls_back_with_exact_policy(tmp_path): @pytest.mark.parametrize("operation", ["apply", "rollback"])
broker = _Broker("rolled_back") def test_hub_typed_mutation_is_rejected_before_job_or_broker_queue(
tmp_path, operation
):
broker = _Broker()
plane = _plane(broker)
with _session(tmp_path) as session: with _session(tmp_path) as session:
_configure(session) _configure(session)
result = _plane(broker).execute( with pytest.raises(DeploymentControlError, match="signed protected push"):
plane.execute(
session, session,
plan=_plan(), plan=_plan(),
idempotency_key="mcp-hub-apply-0001", idempotency_key=f"mcp-hub-{operation}-typed-0001",
operation="apply", operation=operation,
) )
assert result["status"] == "rolled_back" assert session.exec(select(DeploymentJob)).all() == []
assert result["stage"] == "auto_rollback_verified"
assert result["evidence"]["postApplyVerification"]["status"] == "failed"
assert result["evidence"]["autoRollback"]["status"] == "rolled_back"
assert broker.rollback_calls == [f"auto-rollback:{result['id']}"]
assert broker.preflight_calls == 0
def test_post_apply_verification_failure_does_not_rollback_without_exact_policy(tmp_path): assert broker.apply_calls == []
broker = _Broker("rolled_back")
with _session(tmp_path) as session:
_configure(session, rollback_policy=False)
result = _plane(broker).execute(
session,
plan=_plan(),
idempotency_key="mcp-hub-apply-0002",
operation="apply",
)
assert result["status"] == "failed"
assert result["stage"] == "external_integration_failed"
assert result["evidence"]["autoRollback"]["status"] == "blocked"
assert broker.rollback_calls == [] assert broker.rollback_calls == []
def test_post_apply_verification_failure_keeps_failure_when_controller_rejects_rollback(tmp_path): def test_hub_read_only_preflight_remains_available(tmp_path):
broker = _Broker("rejected") broker = _Broker()
with _session(tmp_path) as session:
_configure(session)
result = _plane(broker).execute(
session,
plan=_plan(),
idempotency_key="mcp-hub-apply-0003",
operation="apply",
)
assert result["status"] == "failed"
assert result["stage"] == "auto_rollback_failed"
assert result["evidence"]["postApplyVerification"]["status"] == "failed"
assert result["evidence"]["autoRollback"]["status"] == "rejected"
assert len(broker.rollback_calls) == 1
def test_queued_auto_rollback_is_polled_and_finishes_as_auto_rollback(tmp_path):
broker = _Broker("accepted")
plane = _plane(broker) plane = _plane(broker)
with _session(tmp_path) as session:
_configure(session)
initial = plane.execute(
session,
plan=_plan(),
idempotency_key="mcp-hub-apply-0004",
operation="apply",
)
assert initial["status"] == "rolling_back"
assert initial["stage"] == "auto_rollback_queued"
finished = plane.refresh(session, initial["id"])
assert finished["status"] == "rolled_back"
assert finished["stage"] == "auto_rollback_verified"
assert finished["evidence"]["autoRollback"]["status"] == "rolled_back"
def test_successful_post_apply_verification_never_invokes_auto_rollback(tmp_path):
class _HealthyNpm(_FailingNpm):
def ensure_managed_proxy_host(self, **kwargs):
return {"success": True, "status": "created", "hostId": 42}
broker = _Broker("rolled_back")
plane = DeploymentControlPlane(
broker=broker,
portainer=_Portainer(),
npm=_HealthyNpm(),
authentik=_Authentik(),
unraid=_Unraid(),
)
with _session(tmp_path) as session: with _session(tmp_path) as session:
_configure(session) _configure(session)
result = plane.execute( result = plane.execute(
session, session,
plan=_plan(), plan=_plan(),
idempotency_key="mcp-hub-apply-0005", idempotency_key="mcp-hub-preflight-0001",
operation="apply", operation="preflight",
) )
assert result["status"] == "succeeded" assert result["status"] == "preflight_succeeded"
assert result["stage"] == "controller_and_edge_verified" assert broker.preflight_calls == 1
assert broker.apply_calls == []
assert broker.rollback_calls == [] assert broker.rollback_calls == []
def test_auto_rollback_is_scoped_to_exact_mcp_hub_target(tmp_path):
broker = _Broker("rolled_back")
plan = _plan().model_copy(update={"repository": "jens/other-repository"})
with _session(tmp_path) as session:
_configure(session)
result = _plane(broker).execute(
session,
plan=plan,
idempotency_key="mcp-hub-apply-0006",
operation="apply",
)
assert result["status"] == "failed" def test_hub_post_apply_failure_records_data_aware_recovery_without_rollback(tmp_path):
assert result["stage"] == "external_integration_failed" broker = _Broker()
assert result["evidence"]["postApplyVerification"]["status"] == "failed"
assert "autoRollback" not in result["evidence"]
assert broker.rollback_calls == []
def test_auto_rollback_rejects_same_repository_with_different_application(tmp_path):
broker = _Broker("rolled_back")
plan = _plan().model_copy(
update={
"application_id": "itworx-mcp-hub-copy",
"topology": _plan().topology.model_copy(
update={
"compose_project": "itworx-mcp-hub-copy",
"containers": ("itworx-mcp-hub-copy",),
}
),
}
)
with _session(tmp_path) as session:
set_secret(session, "MCP_HUB_TEST_DEPLOYMENT_BROKER", "x" * 32)
session.add(
Policy(
id="allow-copy-apply",
action="deployment.apply",
target="itworx-mcp-hub-copy",
voice="blocked",
manual="allowed",
risk="high",
reason="Test-only exact target.",
)
)
session.add(
Policy(
id="allow-copy-rollback",
action="deployment.rollback",
target="itworx-mcp-hub-copy",
voice="blocked",
manual="allowed",
risk="high",
reason="Test-only exact target.",
)
)
session.commit()
result = _plane(broker).execute(
session,
plan=plan,
idempotency_key="mcp-hub-apply-0007",
operation="apply",
)
assert result["status"] == "failed"
assert result["stage"] == "external_integration_failed"
assert result["evidence"]["postApplyVerification"]["status"] == "failed"
assert "autoRollback" not in result["evidence"]
assert broker.rollback_calls == []
def test_queued_auto_rollback_rejects_deployed_status_as_rollback_proof(tmp_path):
class _AmbiguousRollbackBroker(_Broker):
def status(self, request_id, *, secret):
return {"status": "deployed", "request": request_id}
broker = _AmbiguousRollbackBroker("accepted")
plane = _plane(broker) plane = _plane(broker)
plan = _plan()
job = DeploymentJob(
id="mcp-hub-existing-apply",
application_id=plan.application_id,
repository=plan.repository,
revision=plan.revision,
plan_sha256="a" * 64,
idempotency_sha256="b" * 64,
status="applying",
plan_json=plan.model_dump(mode="json"),
evidence_json={"brokerStatus": "deployed"},
)
with _session(tmp_path) as session: with _session(tmp_path) as session:
_configure(session) _configure(session)
initial = plane.execute( plane._complete_apply_job(session, job, plan)
session,
plan=_plan(),
idempotency_key="mcp-hub-apply-0008",
operation="apply",
)
assert initial["status"] == "rolling_back"
finished = plane.refresh(session, initial["id"]) assert job.status == "failed"
assert job.stage == "external_integration_failed"
assert finished["status"] == "failed" assert job.evidence_json["postApplyVerification"]["status"] == "failed"
assert finished["stage"] == "auto_rollback_failed" assert job.evidence_json["recovery"] == {
assert finished["evidence"]["autoRollback"]["status"] == "accepted" "status": "manual_required",
assert "deployed state" in finished["result"] "reason": "hub_data_aware_recovery_required",
def test_manual_rollback_requires_rolled_back_terminal_proof(tmp_path):
broker = _Broker("deployed")
with _session(tmp_path) as session:
_configure(session)
result = _plane(broker).execute(
session,
plan=_plan(),
idempotency_key="mcp-hub-rollback-0009",
operation="rollback",
)
assert result["status"] == "rollback_failed"
assert result["stage"] == "controller_rejected"
def test_apply_rejects_rolled_back_terminal_status(tmp_path):
class _RolledBackApplyBroker(_Broker):
def apply(self, plan, *, secret, idempotency_key):
return {"status": "rolled_back", "request": "c" * 64}
broker = _RolledBackApplyBroker()
with _session(tmp_path) as session:
_configure(session)
result = _plane(broker).execute(
session,
plan=_plan(),
idempotency_key="mcp-hub-apply-0010",
operation="apply",
)
assert result["status"] == "failed"
assert result["stage"] == "controller_rejected"
assert broker.rollback_calls == []
def test_auto_rollback_refuses_duplicate_exact_rollback_policies(tmp_path):
broker = _Broker("rolled_back")
with _session(tmp_path) as session:
_configure(session)
session.add(
Policy(
id="duplicate-rollback-policy",
action="deployment.rollback",
target="itworx-mcp-hub",
voice="blocked",
manual="allowed",
risk="high",
reason="Duplicate exact rollback policy.",
)
)
session.commit()
result = _plane(broker).execute(
session,
plan=_plan(),
idempotency_key="mcp-hub-apply-0011",
operation="apply",
)
assert result["status"] == "failed"
assert result["stage"] == "external_integration_failed"
assert result["evidence"]["autoRollback"] == {
"status": "blocked",
"reason": "exact-target-policy",
} }
assert "Automatic image-only rollback is disabled" in job.result_summary
assert broker.rollback_calls == [] assert broker.rollback_calls == []
def test_auto_rollback_rejects_unpollable_accepted_response(tmp_path):
class _MissingRequestBroker(_Broker):
def rollback(self, plan, *, secret, idempotency_key):
self.rollback_calls.append(idempotency_key)
return {"status": "accepted"}
broker = _MissingRequestBroker()
with _session(tmp_path) as session:
_configure(session)
result = _plane(broker).execute(
session,
plan=_plan(),
idempotency_key="mcp-hub-apply-0012",
operation="apply",
)
assert result["status"] == "failed"
assert result["stage"] == "auto_rollback_failed"
assert result["evidence"]["autoRollback"] == {
"status": "accepted",
"reason": "missing-controller-request",
}
assert len(broker.rollback_calls) == 1
@@ -0,0 +1,556 @@
from __future__ import annotations
import hashlib
import importlib
import json
import os
import subprocess
import sys
import types
from datetime import datetime, timezone
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
BROKER_DIR = ROOT / "ops" / "gitea-deploy-broker"
@pytest.fixture
def modules(monkeypatch):
if "fcntl" not in sys.modules:
shim = types.ModuleType("fcntl")
shim.LOCK_EX, shim.LOCK_UN, shim.flock = 2, 8, lambda *_args: None
monkeypatch.setitem(sys.modules, "fcntl", shim)
monkeypatch.syspath_prepend(str(BROKER_DIR))
sys.modules.pop("deploy", None)
sys.modules.pop("prepare_mcp_hub_candidate", None)
controller = importlib.import_module("deploy")
utility = importlib.import_module("prepare_mcp_hub_candidate")
monkeypatch.setattr(utility, "_is_host_root", lambda: True)
yield controller, utility
sys.modules.pop("prepare_mcp_hub_candidate", None)
sys.modules.pop("deploy", None)
@pytest.fixture
def harness(modules, tmp_path, monkeypatch):
controller, utility = modules
revision = "a" * 40
epoch = "1790888863"
now = (
datetime.now(timezone.utc)
.isoformat(timespec="milliseconds")
.replace("+00:00", "Z")
)
metadata = {
"id": revision[:12],
"repository": controller.MCP_HUB_REPOSITORY,
"revision": revision,
"tree": "b" * 40,
"treeDigest": hashlib.sha256(("git-tree:" + "b" * 40).encode()).hexdigest(),
"builtAt": now,
}
repo = tmp_path / "hub.git"
project = {
"branch": "main",
"slug": controller.MCP_HUB_PROJECT,
"compose_project": controller.MCP_HUB_PROJECT,
"containers": list(controller.MCP_HUB_CONTAINERS),
"_mcp_hub_contract": controller.MCP_HUB_TOPOLOGY_CONTRACT,
"_repository_path": str(repo),
"repo_name": "itworx_mcp_hub",
}
state = types.SimpleNamespace(
source=tmp_path / "releases" / revision,
compose_marker="baseline",
images={},
mutations=[],
inspect_calls=[],
)
state.source.mkdir(parents=True)
(state.source / "scripts").mkdir()
draft = state.source / "scripts" / "draft_cve_exceptions.py"
draft.write_text(
"import argparse, json\n"
"from datetime import date, timedelta\n"
"from pathlib import Path\n"
"p=argparse.ArgumentParser()\n"
"p.add_argument('--scan-summary', required=True)\n"
"p.add_argument('--image-digests', required=True)\n"
"p.add_argument('--evidence-reference', required=True)\n"
"p.add_argument('--output', required=True)\n"
"a=p.parse_args()\n"
"scan=json.loads(Path(a.scan_summary).read_text())\n"
"digest=json.loads(Path(a.image_digests).read_text())['images']\n"
"exceptions=[]\n"
"for row in scan['unresolvedCriticalOrHigh']:\n"
" refs=set(row['affectedImages'])\n"
" affected={s:v['imageId'] for s,v in digest.items() if v['tag'] in refs}\n"
" for cve in row['cves']:\n"
" exceptions.append({'id':row['package']+'-'+cve,'package':row['package'],'cve':cve,\n"
" 'installedVersion':row['installedVersion'],'severity':row['severity'],\n"
" 'approvalStatus':'pending','riskOwner':'','approver':'','affectedImageDigests':affected,\n"
" 'evidenceReference':a.evidence_reference})\n"
"Path(a.output).write_text(json.dumps({'schemaVersion':'1.0','exceptions':exceptions}))\n"
"",
encoding="utf-8",
)
raw_project = dict(project)
monkeypatch.setattr(controller, "STATE_ROOT", tmp_path)
monkeypatch.setattr(controller, "MCP_HUB_RELEASES_ROOT", tmp_path / "releases")
monkeypatch.setattr(controller, "_mcp_hub_require_trusted_state_root", lambda: None)
monkeypatch.setattr(controller, "_mcp_hub_require_no_posix_acl", lambda _path: None)
monkeypatch.setattr(controller, "_root_owned", lambda _path: True)
monkeypatch.setattr(
controller,
"load_projects",
lambda: {controller.MCP_HUB_REPOSITORY: raw_project},
)
monkeypatch.setattr(
controller, "effective_mcp_hub_project", lambda repository, value: dict(value)
)
monkeypatch.setattr(
controller, "repository_path", lambda _repository, _project: repo
)
monkeypatch.setattr(controller, "output", lambda _args: revision)
monkeypatch.setattr(
controller,
"validate_commit",
lambda _repo, branch, commit: commit if branch == "main" else "",
)
monkeypatch.setattr(
controller, "deployment_metadata", lambda *_args, **_kwargs: dict(metadata)
)
monkeypatch.setattr(controller, "mcp_hub_source_date_epoch", lambda *_args: epoch)
def stage(_project, _repo, commit):
assert commit == revision
return state.source
monkeypatch.setattr(controller, "mcp_hub_stage_release_source", stage)
monkeypatch.setattr(
controller, "exact_source_matches_revision", lambda *_args: True
)
monkeypatch.setattr(
controller, "_mcp_hub_verify_release_tree_metadata", lambda *_args: None
)
monkeypatch.setattr(controller, "mcp_hub_validate_secret_files", lambda _model: 21)
def rendered(project_arg, commit, repository):
assert commit == revision
assert repository == controller.MCP_HUB_REPOSITORY
services = {}
for index, service in enumerate(controller.MCP_HUB_SERVICES, 1):
if service in controller.MCP_HUB_OWNED_IMAGE_REPOSITORIES:
local = controller.MCP_HUB_LOCAL_BUILD_IMAGE_REPOSITORIES.get(service)
reference = (
f"{local}:{revision[:12]}"
if local
else f"{controller.MCP_HUB_OWNED_IMAGE_REPOSITORIES[service]}@sha256:{index:064x}"
)
elif service == "contextforge":
reference = f"itworx-mcp-hub/contextforge-schema:{revision}"
elif service == "ingestion-clamd":
reference = controller.MCP_HUB_INTAKE_CLAMAV_IMAGE
elif service == "postgres":
reference = controller.MCP_HUB_POSTGRES_IMAGE
elif service == "redis":
reference = controller.MCP_HUB_REDIS_IMAGE
else:
reference = f"fixture/{service}@sha256:{index:064x}"
services[service] = {"image": reference}
return {"services": services, "testMarker": state.compose_marker}
monkeypatch.setattr(controller, "mcp_hub_compose_model", rendered)
def inspect(reference):
state.inspect_calls.append(reference)
return state.images.get(reference)
monkeypatch.setattr(controller, "inspect_image", inspect)
monkeypatch.setattr(
controller, "run", lambda *_args, **_kwargs: state.mutations.append("run")
)
monkeypatch.setattr(utility, "_is_host_root", lambda: True)
plan = utility.plan_candidate(controller.MCP_HUB_REPOSITORY, revision)
candidate_dir = tmp_path / "mcp-hub-release-candidates" / revision
plan_doc = json.loads((candidate_dir / "build.json").read_text())
model = rendered(project, revision, controller.MCP_HUB_REPOSITORY)
labels = {
"org.opencontainers.image.source": controller.MCP_HUB_OCI_IMAGE_SOURCE,
"org.opencontainers.image.revision": revision,
"org.opencontainers.image.source-tree": metadata["tree"],
"org.opencontainers.image.version": metadata["id"],
"org.opencontainers.image.created": metadata["builtAt"],
"org.opencontainers.image.source-date-epoch": epoch,
}
for index, service in enumerate(controller.MCP_HUB_SERVICES, 1):
reference = model["services"][service]["image"]
image_id = "sha256:" + f"{index:064x}"
config = {"User": "10001:10001"}
if (
service in controller.MCP_HUB_OWNED_IMAGE_REPOSITORIES
or service == "contextforge"
):
config["Labels"] = dict(labels)
state.images[reference] = {"Id": image_id, "Config": config}
def write_input(name: str, value: object) -> Path:
path = candidate_dir / name
data = (
json.dumps(value, sort_keys=True, separators=(",", ":")) + "\n"
).encode()
descriptor = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
with os.fdopen(descriptor, "wb") as handle:
handle.write(data)
path.chmod(0o600)
return path
for index, service in enumerate(controller.MCP_HUB_SERVICES, 1):
image_id = "sha256:" + f"{index:064x}"
vuln = {
"SchemaVersion": 2,
"Trivy": {"Version": "0.73.0"},
"CreatedAt": now,
"Metadata": {
"ImageID": image_id,
"ImageConfig": {"architecture": "amd64", "os": "linux"},
},
"Results": [
{
"Class": "os-pkgs",
"Packages": [{"Name": "fixture", "Version": "1"}],
"Vulnerabilities": [],
}
],
}
secret = {
"ImageID": image_id,
"CreatedAt": now,
"TrivyVersion": "0.73.0",
"FindingCount": 0,
"SensitiveFindingDetailsPersisted": False,
"Results": [{"FindingCount": 0}],
}
write_input(f"vulnerability-{service}.json", vuln)
write_input(f"secret-{service}.json", secret)
write_input("trivy-db-metadata.json", {"UpdatedAt": now, "DownloadedAt": now})
# The reports must match the IDs in the final inspect inventory. Keep a
# convenient writer for intentional drift cases in each test.
monkeypatch.setattr(utility, "_test_write_input", write_input, raising=False)
yield types.SimpleNamespace(
controller=controller,
utility=utility,
revision=revision,
epoch=epoch,
metadata=metadata,
project=project,
state=state,
candidate_dir=candidate_dir,
model=model,
write_input=write_input,
now=now,
plan=plan,
plan_doc=plan_doc,
)
def test_plan_freezes_protected_metadata_without_candidate_or_approval(harness):
h = harness
document = json.loads((h.candidate_dir / "build.json").read_text())
assert document["schema"] == "itworx.mcp-hub-build-plan/v1"
assert document["repository"] == h.controller.MCP_HUB_REPOSITORY
assert document["revision"] == h.revision
assert document["build"] == h.metadata
assert document["sourceDateEpoch"] == h.epoch
assert len(document["composeSha256"]) == 64
assert not (h.candidate_dir / "candidate.json").exists()
assert not (h.candidate_dir / "approval.json").exists()
assert h.state.mutations == []
def test_wrong_repository_and_nonroot_fail_before_controller_access(
modules, monkeypatch
):
controller, utility = modules
monkeypatch.setattr(utility, "_is_host_root", lambda: True)
monkeypatch.setattr(
controller, "load_projects", lambda: pytest.fail("must reject repo first")
)
with pytest.raises(controller.DeployError, match="fixed MCP Hub repository"):
utility.plan_candidate("someone/else", "a" * 40)
monkeypatch.setattr(utility, "_is_host_root", lambda: False)
with pytest.raises(controller.DeployError, match="root"):
utility.plan_candidate(controller.MCP_HUB_REPOSITORY, "a" * 40)
def test_non_exact_main_head_is_denied_before_staging(modules, monkeypatch, tmp_path):
controller, utility = modules
monkeypatch.setattr(utility, "_is_host_root", lambda: True)
project = {
"branch": "main",
"_mcp_hub_contract": controller.MCP_HUB_TOPOLOGY_CONTRACT,
}
monkeypatch.setattr(
controller, "load_projects", lambda: {controller.MCP_HUB_REPOSITORY: project}
)
monkeypatch.setattr(
controller, "effective_mcp_hub_project", lambda _repo, value: value
)
monkeypatch.setattr(
controller, "repository_path", lambda *_args: tmp_path / "hub.git"
)
monkeypatch.setattr(controller, "output", lambda _args: "c" * 40)
monkeypatch.setattr(
controller,
"mcp_hub_stage_release_source",
lambda *_args: pytest.fail("must not stage"),
)
with pytest.raises(controller.DeployError, match="exact protected main head"):
utility.plan_candidate(controller.MCP_HUB_REPOSITORY, "a" * 40)
def test_capture_emits_candidate_and_pending_only_envelope_without_apply_calls(
harness, monkeypatch
):
h = harness
calls = []
original_run = h.utility.subprocess.run
def observe_draft(command, **kwargs):
calls.append((command, kwargs))
return original_run(command, **kwargs)
monkeypatch.setattr(h.utility.subprocess, "run", observe_draft)
result = h.utility.capture_candidate(h.controller.MCP_HUB_REPOSITORY, h.revision)
candidate = json.loads((h.candidate_dir / "candidate.json").read_text())
exceptions = json.loads((h.candidate_dir / "exceptions.json").read_text())
digests = json.loads((h.candidate_dir / "image-digests.json").read_text())
assert result["status"] == "pending_review"
assert set(candidate["images"]) == set(h.controller.MCP_HUB_SERVICES)
assert digests["images"]["web"]["tag"] == candidate["images"]["web"]["imageRef"]
assert digests["images"]["web"]["imageId"] == candidate["images"]["web"]["imageId"]
candidate_bytes = (h.candidate_dir / "candidate.json").read_bytes()
exceptions_bytes = (h.candidate_dir / "exceptions.json").read_bytes()
assert result["candidateSha256"] == hashlib.sha256(candidate_bytes).hexdigest()
assert result["exceptionsSha256"] == hashlib.sha256(exceptions_bytes).hexdigest()
assert not (h.candidate_dir / "approval.json").exists()
assert all(item["approvalStatus"] == "pending" for item in exceptions["exceptions"])
assert h.state.mutations == []
assert len(h.state.inspect_calls) == 17
assert len(calls) == 1
assert (
Path(calls[0][0][1]) == h.state.source / "scripts" / "draft_cve_exceptions.py"
)
assert calls[0][1]["stdout"] is subprocess.DEVNULL
assert calls[0][1]["stderr"] is subprocess.DEVNULL
assert set(calls[0][1]["env"]) == {
"PATH",
"PYTHONDONTWRITEBYTECODE",
"PYTHONIOENCODING",
}
@pytest.mark.parametrize("mutation", ["label", "root"])
def test_capture_rejects_contextforge_provenance_drift_without_candidate(
harness, mutation
):
h = harness
reference = h.model["services"]["contextforge"]["image"]
config = h.state.images[reference]["Config"]
if mutation == "label":
config["Labels"]["org.opencontainers.image.source-tree"] = "wrong"
else:
config["User"] = "0:10001"
with pytest.raises(h.controller.DeployError, match="provenance|non-root runtime"):
h.utility.capture_candidate(h.controller.MCP_HUB_REPOSITORY, h.revision)
assert not (h.candidate_dir / "candidate.json").exists()
assert not (h.candidate_dir / "approval.json").exists()
assert h.state.mutations == []
def test_pending_high_findings_are_drafted_from_the_exact_staged_source(harness):
h = harness
report_path = h.candidate_dir / "vulnerability-web.json"
report = json.loads(report_path.read_text())
report["Results"][0]["Vulnerabilities"] = [
{
"PkgName": "libfixture",
"VulnerabilityID": "CVE-2026-12345",
"InstalledVersion": "1.2.3",
"Severity": "HIGH",
}
]
report_path.unlink()
h.write_input(report_path.name, report)
result = h.utility.capture_candidate(h.controller.MCP_HUB_REPOSITORY, h.revision)
candidate = json.loads((h.candidate_dir / "candidate.json").read_text())
summary = json.loads((h.candidate_dir / "scan-summary.json").read_text())
exceptions = json.loads((h.candidate_dir / "exceptions.json").read_text())[
"exceptions"
]
row = summary["unresolvedCriticalOrHigh"][0]
assert row["affectedImages"] == [candidate["images"]["web"]["imageRef"]]
assert exceptions[0]["approvalStatus"] == "pending"
assert exceptions[0]["package"] == "libfixture"
assert exceptions[0]["cve"] == "CVE-2026-12345"
assert exceptions[0]["affectedImageDigests"] == {
"web": candidate["images"]["web"]["imageId"]
}
assert result["status"] == "pending_review"
assert not h.state.mutations
def test_findings_for_same_cve_at_distinct_versions_remain_distinct(harness):
h = harness
for service, version in (("web", "1.2.3"), ("worker", "4.5.6")):
path = h.candidate_dir / f"vulnerability-{service}.json"
report = json.loads(path.read_text())
report["Results"][0]["Vulnerabilities"] = [
{
"PkgName": "libfixture",
"VulnerabilityID": "CVE-2026-12345",
"InstalledVersion": version,
"Severity": "HIGH",
}
]
path.unlink()
h.write_input(path.name, report)
h.utility.capture_candidate(h.controller.MCP_HUB_REPOSITORY, h.revision)
summary = json.loads((h.candidate_dir / "scan-summary.json").read_text())
assert {row["installedVersion"] for row in summary["unresolvedCriticalOrHigh"]} == {
"1.2.3",
"4.5.6",
}
def test_same_package_cve_version_with_conflicting_severity_fails_closed(harness):
h = harness
for service, severity in (("web", "HIGH"), ("worker", "CRITICAL")):
path = h.candidate_dir / f"vulnerability-{service}.json"
report = json.loads(path.read_text())
report["Results"][0]["Vulnerabilities"] = [
{
"PkgName": "libfixture",
"VulnerabilityID": "CVE-2026-12345",
"InstalledVersion": "1.2.3",
"Severity": severity,
}
]
path.unlink()
h.write_input(path.name, report)
with pytest.raises(h.controller.DeployError, match="conflicting scan severities"):
h.utility.capture_candidate(h.controller.MCP_HUB_REPOSITORY, h.revision)
assert not (h.candidate_dir / "candidate.json").exists()
def test_draft_for_all_images_is_rejected_when_finding_scope_is_narrower(harness):
h = harness
path = h.candidate_dir / "vulnerability-web.json"
report = json.loads(path.read_text())
report["Results"][0]["Vulnerabilities"] = [
{
"PkgName": "libfixture",
"VulnerabilityID": "CVE-2026-12345",
"InstalledVersion": "1.2.3",
"Severity": "HIGH",
}
]
path.unlink()
h.write_input(path.name, report)
draft_script = h.state.source / "scripts" / "draft_cve_exceptions.py"
draft_script.write_text(
draft_script.read_text().replace("if v['tag'] in refs", "if True"),
encoding="utf-8",
)
with pytest.raises(h.controller.DeployError, match="pending-only document"):
h.utility.capture_candidate(h.controller.MCP_HUB_REPOSITORY, h.revision)
assert not (h.candidate_dir / "candidate.json").exists()
def test_missing_scan_report_fails_without_publishing_candidate(harness):
h = harness
(h.candidate_dir / "secret-web.json").unlink()
with pytest.raises(h.controller.DeployError):
h.utility.capture_candidate(h.controller.MCP_HUB_REPOSITORY, h.revision)
assert not (h.candidate_dir / "candidate.json").exists()
assert not (h.candidate_dir / "approval.json").exists()
assert h.state.mutations == []
def test_frozen_compose_drift_and_image_identity_drift_fail_closed(harness):
h = harness
h.state.compose_marker = "changed"
with pytest.raises(h.controller.DeployError, match="Compose"):
h.utility.capture_candidate(h.controller.MCP_HUB_REPOSITORY, h.revision)
h.state.compose_marker = "baseline"
web_ref = h.model["services"]["web"]["image"]
h.state.images[web_ref]["Id"] = "sha256:" + "f" * 64
with pytest.raises(h.controller.DeployError):
h.utility.capture_candidate(h.controller.MCP_HUB_REPOSITORY, h.revision)
assert not (h.candidate_dir / "candidate.json").exists()
def test_fixable_critical_or_high_finding_is_not_captured(harness):
h = harness
path = h.candidate_dir / "vulnerability-web.json"
report = json.loads(path.read_text())
report["Results"][0]["Vulnerabilities"] = [
{
"PkgName": "libfixture",
"VulnerabilityID": "CVE-2026-12345",
"InstalledVersion": "1.2.3",
"FixedVersion": "1.2.4",
"Severity": "CRITICAL",
}
]
path.unlink()
h.write_input(path.name, report)
with pytest.raises(h.controller.DeployError, match="fixable"):
h.utility.capture_candidate(h.controller.MCP_HUB_REPOSITORY, h.revision)
assert not (h.candidate_dir / "candidate.json").exists()
def test_capture_never_overwrites_existing_records(harness):
h = harness
h.utility.capture_candidate(h.controller.MCP_HUB_REPOSITORY, h.revision)
original = (h.candidate_dir / "candidate.json").read_bytes()
with pytest.raises(h.controller.DeployError):
h.utility.capture_candidate(h.controller.MCP_HUB_REPOSITORY, h.revision)
assert (h.candidate_dir / "candidate.json").read_bytes() == original
assert h.state.mutations == []
def test_capture_preserves_full_supported_trivy_database_metadata(harness):
h = harness
path = h.candidate_dir / "trivy-db-metadata.json"
path.unlink()
metadata = {
"UpdatedAt": h.now,
"DownloadedAt": h.now,
"Version": 2,
"NextUpdate": h.now,
}
h.write_input(path.name, metadata)
h.utility.capture_candidate(h.controller.MCP_HUB_REPOSITORY, h.revision)
candidate = json.loads((h.candidate_dir / "candidate.json").read_text())
assert (
candidate["databaseMetadataSha256"]
== hashlib.sha256(path.read_bytes()).hexdigest()
)
assert json.loads(path.read_text()) == metadata
def test_cli_requires_explicit_fixed_target_and_subcommand(modules, monkeypatch):
_controller, utility = modules
monkeypatch.setattr(utility, "_is_host_root", lambda: False)
with pytest.raises(SystemExit):
utility.main([])
monkeypatch.setattr(utility, "_is_host_root", lambda: True)
assert (
utility.main(["plan", "--repository", "other/repo", "--revision", "a" * 40])
== 2
)
@@ -1,5 +1,16 @@
import ast
import hashlib
import json
import os
import re
import subprocess
import tempfile
from datetime import datetime, timezone
from pathlib import Path
from types import SimpleNamespace from types import SimpleNamespace
import pytest
from backend.app.services.mcp_hub_deployment_reconciliation import ( from backend.app.services.mcp_hub_deployment_reconciliation import (
McpHubDeploymentReconciliation, McpHubDeploymentReconciliation,
) )
@@ -43,259 +54,581 @@ class FakePortainer:
raise AssertionError(path) raise AssertionError(path)
def test_reconciliation_is_fixed_to_mcp_hub_and_reconciles_only_verified_failed_forward_state(): def test_reconciliation_is_fixed_and_read_only_for_mcp_hub():
fake = FakePortainer() fake = FakePortainer()
result = McpHubDeploymentReconciliation(fake).run() result = McpHubDeploymentReconciliation(fake).run()
assert result == { assert result == {
"status": "reconciled", "status": "reconciled",
"repository": "jens/itworx_mcp_hub", "repository": "jens/itworx_mcp_hub",
"composeProject": "itworx-mcp-hub", "composeProject": "itworx-mcp-hub",
} }
create = fake.calls[0] command = fake.calls[0][2]["json"]["Cmd"]
command = create[2]["json"]["Cmd"]
assert command[:2] == ["python3", "-c"] assert command[:2] == ["python3", "-c"]
assert command[3:] == ["jens/itworx_mcp_hub", "itworx-mcp-hub"] assert command[3:] == ["jens/itworx_mcp_hub", "itworx-mcp-hub"]
assert create[2]["json"]["AttachStdout"] is False
assert create[2]["json"]["AttachStderr"] is False
script = command[2] script = command[2]
assert "/controller-config/projects.json" in script assert "/controller-config/projects.json" in script
assert "/controller-hub-releases/controller-config/projects.json" in script assert "/controller-hub-releases/controller-config/projects.json" in script
assert "os.path.samefile(registry_path, host_registry_path)" in script
assert "/mnt/cache/appdata/gitea-deploy-runners/state" in script assert "/mnt/cache/appdata/gitea-deploy-runners/state" in script
assert "/srv/example/appdata/gitea-deploy-runners/deploy.lock" not in script assert "os.path.samefile(registry_path, host_registry_path)" in script
assert "/mnt/cache/appdata/gitea-deploy-runners/deploy.lock" in script assert 'os.open(project_lock_path, os.O_RDONLY' in script
assert 'project.get("branch") != "main"' in script assert 'os.open(global_lock_path, os.O_RDONLY' in script
assert 'project.get("compose_project") != expected_compose_project' in script assert 'state_root.mkdir' not in script
assert 'recorded_status != "failed"' in script assert 'project_lock_path.parent.mkdir' not in script
assert 'recorded_live_status not in {"healthy", "unknown"}' in script assert 'os.replace(' not in script
assert 'attempt_status != "failed"' in script assert 'shutil.copy2(' not in script
assert 'attempt_revision == recorded_live' in script assert 'state_path.write_text(' not in script
assert 'git_run("merge-base", "--is-ancestor", recorded_live, attempt_revision)' in script assert '"status": "success"' not in script
assert 'git_run("merge-base", "--is-ancestor", attempt_revision, main_revision)' in script assert '"live_legacy_provenance": True' not in script
def test_unknown_live_status_is_accepted_only_for_a_proven_snapshot_stage_failure_without_rollback_error(): def test_unknown_or_legacy_state_requires_the_guarded_deployment_path():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
assert 'last_attempt_message = str(last_attempt.get("message") or "")' in script
assert "snapshot_failure_prefixes = (" in script
assert 'recorded_live_status == "unknown"' in script
assert 'last_attempt_message.startswith(snapshot_failure_prefixes)' in script
assert '"; rollback error:" in last_attempt_message' in script
assert '"; source rollback error:" in last_attempt_message' in script
assert "raise SystemExit(55)" in script
def test_reconciliation_proves_complete_live_compose_topology_and_preserves_actual_image_ids():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
assert 'expected_containers = project.get("containers")' in script
assert "set(project_names) != set(expected_containers)" in script
assert 'labels.get("com.docker.compose.project") != expected_compose_project' in script
assert 'not runtime_state.get("Running")' in script
assert 'health in {"starting", "unhealthy"}' in script
assert 'image_id = str(container.get("Image") or "")' in script
assert '"containers": container_artifacts' in script
assert '"compose_sha256": ""' in script
assert '"topology_contract": topology_contract' in script
assert '"live_legacy_provenance": True' in script
assert '"live_artifacts": artifacts' in script
assert '"live_revision": recorded_live' in script
def test_reconciliation_recognizes_only_the_fixed_mcp_hub_oci_source_as_owned():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
assert 'owned_image_source = "https://gitea.example.com/jens/itworx-mcp-hub"' in script
assert "if image_source == owned_image_source:" in script
assert 'git_run("merge-base", "--is-ancestor", recorded_live, image_revision)' in script
assert 'git_run("merge-base", "--is-ancestor", image_revision, main_revision)' in script
assert 'image_source.startswith("https://gitea.example.com/")' not in script
assert 'image_source == repository' not in script
assert '"live_revision": main_revision' not in script
assert '"live_revision": attempt_revision' not in script
def test_reconciliation_requires_the_controller_17_service_topology_migration():
result = McpHubDeploymentReconciliation(FakePortainer(exit_code=56)).run() result = McpHubDeploymentReconciliation(FakePortainer(exit_code=56)).run()
assert result == { assert result == {
"status": "topology_migration_required", "status": "topology_migration_required",
"repository": "jens/itworx_mcp_hub", "repository": "jens/itworx_mcp_hub",
"composeProject": "itworx-mcp-hub", "composeProject": "itworx-mcp-hub",
"reason": "MCP Hub 15-to-17 topology migration requires its guarded protected-main deployment", "reason": "MCP Hub legacy, unknown, mixed, or unresolved migration state requires guarded deployment or data-aware recovery",
} }
fake = FakePortainer() fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run() McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2] script = fake.calls[0][2]["json"]["Cmd"][2]
assert 'topology_contract = "itworx.mcp-hub-topology/17-v1"' in script assert 'set(registered_live_containers) == expected_fixed' in script
assert 'raise SystemExit(56 if recorded_live_status in {"unknown", "healthy"} else 52)' in script
assert 'recorded_status == "success"' in script
assert 'recorded_live_status == "healthy"' in script
assert 'state.get("live_legacy_provenance") is False' in script
assert 'recorded_artifacts.get("topology_contract") == topology_contract' in script assert 'recorded_artifacts.get("topology_contract") == topology_contract' in script
assert '"topology_contract": topology_contract' in script assert 'receipt.get("schema") == "gitea-deploy-receipt.v1"' in script
assert 'receipt.get("live") is None' in script
def test_owned_image_ancestry_can_fail_closed_to_the_canonical_gitea_store(): def test_modern_success_requires_a_complete_exact_receipt_and_protected_ancestry():
fake = FakePortainer() fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run() McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2] script = fake.calls[0][2]["json"]["Cmd"][2]
assert 'canonical_repository_root = Path("/mnt/cache/appdata/gitea/git/repositories")' in script assert 'set(receipt) == {"schema", "repository", "revision", "tree", "treeDigest", "build", "containers", "live"}' in script
assert 'canonical_bare_repo = canonical_repository_root / owner / f"{name}.git"' in script assert 'receipt_identity_valid' in script
assert "def canonical_git_run(*args):" in script assert 'receipt.get("revision") == recorded_live' in script
assert "def canonical_image_ancestry_is_proven(" in script assert 'git_run("merge-base", "--is-ancestor", recorded_live, main_revision)' in script
assert 'resolved_recorded = canonical_git_run("rev-parse", f"{recorded_revision}^{{commit}}")' in script assert 'set(dict(receipt.get("containers") or {})) == expected_runtime' in script
assert 'resolved_image = canonical_git_run("rev-parse", f"{image_revision}^{{commit}}")' in script
assert 'resolved_main = canonical_git_run("rev-parse", "refs/heads/main")' in script
assert 'resolved_main.stdout.strip() == expected_main' in script
assert '"merge-base", "--is-ancestor", recorded_revision, image_revision' in script
assert '"merge-base", "--is-ancestor", image_revision, expected_main' in script
assert "if not mounted_ancestry_proven and not canonical_image_ancestry_is_proven(" in script
assert "canonical_bare_repo.is_symlink()" in script
def test_unchanged_recorded_image_ids_do_not_reinterpret_historical_oci_revision_labels():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
change_probe = "image_changed = image_id != recorded_image_id"
provenance_gate = "if image_changed:"
compat_probe = "contextforge_compat_image = main_declares_contextforge_compat_image(name, image_ref)"
dataplane_probe = "main_declares_sessionless_dataplane_image(name, image_ref)"
owned_probe = "elif image_source == owned_image_source:"
external_probe = "elif not main_declares_external_image(image_ref):"
assert change_probe in script
assert provenance_gate in script
assert compat_probe in script
assert dataplane_probe in script
assert "not already_reconciled" in script
assert owned_probe in script
assert external_probe in script
assert script.index(change_probe) < script.index(provenance_gate)
assert script.index(provenance_gate) < script.index(compat_probe)
assert script.index(compat_probe) < script.index(dataplane_probe)
assert script.index(dataplane_probe) < script.index(owned_probe)
assert script.index(owned_probe) < script.index(external_probe)
assert "if image_id != recorded_image_id or container_id != recorded_container_id:" in script
def test_external_image_drift_requires_an_immutable_digest_declared_in_reviewed_main_compose():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
assert "def main_declares_external_image(image_ref):" in script
assert "@sha256:" in script
assert 'git_run("show", f"{main_revision}:{compose_path}")' in script
assert '"infrastructure/compose/docker-compose.yml"' in script
assert '"infrastructure/compose/docker-compose.prod.yml"' in script
assert 'line.strip() in {f"image: {image_ref}", f\'image: "{image_ref}"\', f"image: \'{image_ref}\'"}' in script
assert "main_declares_external_image(image_ref)" in script
assert "elif not main_declares_external_image(image_ref):" in script
def test_contextforge_compat_image_drift_requires_exact_reviewed_main_contract():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
assert 'container_name != "itworx-mcp-hub-contextforge-1"' in script
assert 'f"{main_revision}:infrastructure/images/contextforge/compat-image.env"' in script
assert 'f"{main_revision}:scripts/build_contextforge_compat_image.sh"' in script
assert '"CONTEXTFORGE_UPSTREAM_COMMIT"' in script
assert '"CONTEXTFORGE_ALEMBIC_HEAD"' in script
assert '"CONTEXTFORGE_MCP_SDK"' in script
assert '"CONTEXTFORGE_LOCAL_IMAGE"' in script
assert 're.fullmatch(r"[0-9a-f]{40}", upstream_commit)' in script
assert 're.fullmatch(r"[0-9a-f]{12}", alembic_head)' in script
assert 'local_image != f"itworx-mcp-hub/contextforge-schema:{alembic_head}"' in script
assert 'git -C "$SRC" fetch --depth 1 origin "$CONTEXTFORGE_UPSTREAM_COMMIT"' in script
assert 'git -C "$SRC" checkout --detach FETCH_HEAD' in script
assert 'MCP_REQUIREMENT="mcp==${CONTEXTFORGE_MCP_SDK}"' in script
assert 'alembic -c mcpgateway/alembic.ini heads' in script
assert 'docker tag "$STAGED_IMAGE" "$CONTEXTFORGE_LOCAL_IMAGE"' in script
assert 'image_ref.startswith("itworx-mcp-hub/contextforge-schema:")' not in script
compat_probe = "contextforge_compat_image = main_declares_contextforge_compat_image(name, image_ref)"
owned_probe = "elif image_source == owned_image_source:"
assert compat_probe in script
assert "if contextforge_compat_image or sessionless_dataplane_image:" in script
assert owned_probe in script
assert script.index(compat_probe) < script.index(owned_probe)
def test_sessionless_dataplane_baseline_is_one_time_and_commit_bound():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
assert 'container_name != "itworx-mcp-hub-dataplane-router-1"' in script
assert 'image_ref != "itworx-mcp-hub/dataplane:sessionless-final"' in script
assert 'sessionless_commit = "b519a8c4cf17cd9df00a575acfe096838aab7789"' in script
assert 'f"{sessionless_commit}:services/dataplane_router/router.py"' in script
assert 'f"{sessionless_commit}:tests/test_dataplane_session_headers.py"' in script
assert 'f"{main_revision}:evidence/mcp-session-boundary-20260916/SUMMARY.md"' in script
assert 'MCP_TRANSPORT_STATE_HEADERS = frozenset({b"mcp-session-id"})' in script
assert 'del response.headers["mcp-session-id"]' in script
assert '"2025-11-25"' in script
assert '"2026-07-28"' in script
assert '"permanently enforce stateless public transport"' in script
assert (
'git_run("merge-base", "--is-ancestor", sessionless_commit, main_revision)'
in script
)
assert "not already_reconciled" in script
def test_already_reconciled_state_is_reverified_before_idempotent_success():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
assert "already_reconciled = (" in script
assert script.index("already_reconciled = (") < script.index("project_names =")
assert script.index("container_artifacts = {}") < script.index("if already_reconciled:")
assert 'print("already-reconciled", flush=True)' in script assert 'print("already-reconciled", flush=True)' in script
assert "if drift_found:" in script assert 'print("reconciled", flush=True)' not in script
assert 'migration_path = migration_root / "active-intent.json"' in script
assert 'intent.get("status") != "complete"' in script
assert 'elif migration_attempt_id:' in script
assert 'intent.get("completedStateSha256") != hashlib.sha256(state_bytes).hexdigest()' in script
assert 'observed.get("historicalStateSha256") != intent.get("baselineStateSha256")' in script
assert 'rf"[0-9]+-{recorded_live[:12]}-[0-9a-f]{{16}}"' in script
def test_reconciliation_refuses_unknown_runtime_topology(): def test_reconciliation_intent_guard_rejects_unknown_and_accepts_exact_complete_intent():
result = McpHubDeploymentReconciliation(FakePortainer(exit_code=47)).run() fake = FakePortainer()
assert result == { McpHubDeploymentReconciliation(fake).run()
"status": "validation_failed", script = fake.calls[0][2]["json"]["Cmd"][2]
"reason": "live MCP Hub Compose project differs from the controller target", parsed = ast.parse(script)
function = next(
node for node in parsed.body
if isinstance(node, ast.FunctionDef) and node.name == "validate_migration_intent_state"
)
namespace = {
"Path": Path,
"hashlib": hashlib,
"json": json,
"os": os,
"re": re,
"stat": SimpleNamespace(S_IMODE=lambda _mode: 0o600),
"trusted_private_path": lambda *_args, **_kwargs: True,
}
exec(compile(ast.Module(body=[function], type_ignores=[]), "intent-test", "exec"), namespace)
validate = namespace["validate_migration_intent_state"]
repository = "jens/itworx_mcp_hub"
candidate = "b" * 40
migration_attempt = "a" * 32
state_bytes = b"exact-success-state-bytes"
for raw in (b'{"status":"started"}', b"not-json"):
with tempfile.TemporaryDirectory() as temporary:
state_root = Path(temporary)
migration_root = state_root / "mcp-hub-topology-migrations"
migration_root.mkdir()
(migration_root / "active-intent.json").write_bytes(raw)
last_attempt = {"metadata": {"mcpHubMigrationAttemptId": migration_attempt}}
with pytest.raises(SystemExit) as exc:
validate(
state_root, state_bytes, repository, candidate, "success", "healthy",
last_attempt, "success", candidate,
)
assert exc.value.code == 56
with tempfile.TemporaryDirectory() as temporary:
last_attempt = {"metadata": {"mcpHubMigrationAttemptId": migration_attempt}}
with pytest.raises(SystemExit) as exc:
validate(
Path(temporary), state_bytes, repository, candidate, "success", "healthy",
last_attempt, "success", candidate,
)
assert exc.value.code == 56
with tempfile.TemporaryDirectory() as temporary:
state_root = Path(temporary)
migration_root = state_root / "mcp-hub-topology-migrations"
migration_root.mkdir()
intent = {
"schema": "itworx.mcp-hub-migration-intent/v1",
"status": "complete",
"attemptId": migration_attempt,
"repository": repository,
"candidateRevision": candidate,
"priorLiveRevision": "c" * 40,
"backupManifestSha256": "d" * 64,
"backupComposeSha256": "e" * 64,
"baselineStateSha256": "f" * 64,
"startedAtEpoch": 10,
"completedAtEpoch": 11,
"completedStateSha256": "0" * 64,
}
active_intent = migration_root / "active-intent.json"
active_intent.write_text(json.dumps(intent), encoding="utf-8")
last_attempt = {
"metadata": {
"mcpHubMigrationAttemptId": migration_attempt,
"mcpHubBackupReceipt": {
"repository": repository,
"revision": candidate,
"restoreVerified": True,
"manifestSha256": "d" * 64,
"composeSha256": "e" * 64,
},
}
}
with pytest.raises(SystemExit) as exc:
validate(
state_root, state_bytes, repository, candidate, "success", "healthy",
last_attempt, "success", candidate,
)
assert exc.value.code == 56
with tempfile.TemporaryDirectory() as temporary:
state_root = Path(temporary)
migration_root = state_root / "mcp-hub-topology-migrations"
receipt_root = state_root / "mcp-hub-backup-receipts"
backup_root = state_root / "mcp-hub-backups"
migration_root.mkdir()
receipt_root.mkdir()
backup_root.mkdir()
backup_directory = backup_root / f"1780400000-{candidate[:12]}-0123456789abcdef"
backup_directory.mkdir()
compose_digest = "e" * 64
manifest = {
"schema": "itworx.mcp-hub-backup/v1",
"repository": repository,
"revision": candidate,
"backupDirectory": str(backup_directory),
"composeSha256": compose_digest,
"configRestoreVerified": True,
"restore": {"status": "verified"},
"databases": {"itworx_mcp_hub": {}, "contextforge": {}},
}
manifest_path = backup_directory / "manifest.json"
manifest_path.write_text(json.dumps(manifest), encoding="utf-8")
manifest_digest = hashlib.sha256(manifest_path.read_bytes()).hexdigest()
receipt_path = receipt_root / f"1780400000-{candidate[:12]}-0123456789abcdef.json"
backup_receipt = {
"schema": "itworx.mcp-hub-backup-receipt/v1",
"repository": repository,
"revision": candidate,
"backupDirectory": str(backup_directory),
"manifestSha256": manifest_digest,
"composeSha256": compose_digest,
"restoreVerified": True,
"createdAtEpoch": 1780400000,
"receiptPath": str(receipt_path),
}
receipt_path.write_text(
json.dumps({key: value for key, value in backup_receipt.items() if key != "receiptPath"}),
encoding="utf-8",
)
observed_path = migration_root / f"observed-baseline-{candidate[:12]}-0123456789abcdef0123.json"
observed = {
"schema": "itworx.mcp-hub-observed-baseline/v1",
"claim": "observed_mixed_runtime_facts_only",
"candidateRevision": candidate,
"historicalStateSha256": "f" * 64,
"backupManifestSha256": manifest_digest,
"backupComposeSha256": compose_digest,
"backupRestoreVerified": True,
"postgresVolumeBaseline": [{}, {}],
}
observed_path.write_text(json.dumps(observed), encoding="utf-8")
observed_digest = hashlib.sha256(observed_path.read_bytes()).hexdigest()
successful_state_sha = hashlib.sha256(state_bytes).hexdigest()
intent = {
"schema": "itworx.mcp-hub-migration-intent/v1",
"status": "complete",
"attemptId": migration_attempt,
"repository": repository,
"candidateRevision": candidate,
"priorLiveRevision": "c" * 40,
"backupManifestSha256": manifest_digest,
"backupComposeSha256": compose_digest,
"baselineStateSha256": "f" * 64,
"startedAtEpoch": 10,
"completedAtEpoch": 11,
"completedStateSha256": successful_state_sha,
"observedBaselinePath": str(observed_path),
"observedBaselineSha256": observed_digest,
}
(migration_root / "active-intent.json").write_text(json.dumps(intent), encoding="utf-8")
last_attempt = {
"metadata": {
"mcpHubMigrationAttemptId": migration_attempt,
"mcpHubBackupReceipt": backup_receipt,
}
} }
validate(
state_root, state_bytes, repository, candidate, "success", "healthy",
last_attempt, "success", candidate,
)
def test_reconciliation_refuses_unproven_runtime_revision_drift():
result = McpHubDeploymentReconciliation(FakePortainer(exit_code=51)).run() def test_container_reverification_requires_exact_health_service_label_ids_and_mounts():
assert result == { fake = FakePortainer()
"status": "validation_failed", McpHubDeploymentReconciliation(fake).run()
"reason": "live MCP Hub image revision or external immutable image is not proven by the recorded-live to main chain", script = fake.calls[0][2]["json"]["Cmd"][2]
assert 'runtime_state.get("Running") is not True or health != "healthy"' in script
assert 'labels.get("com.docker.compose.project") != expected_compose_project' in script
assert 'labels.get("com.docker.compose.service") != name_service' in script
assert 'if recorded.get(key) != value or receipt_item.get(key) != value:' in script
assert 'live_attested_mounts(container) != recorded_mounts' in script
assert 'receipt_item.get("mounts_sha256")' in script
assert 'def live_attested_mounts(container):' in script
assert 'docker", "volume", "inspect", name' in script
assert 'mount["created_at"] = created_at' in script
assert 'created.astimezone(timezone.utc) > datetime.now(timezone.utc)' in script
def test_reconciliation_distinguishes_recreated_postgres_volume_by_created_at():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
parsed = ast.parse(script)
function = next(
node for node in parsed.body
if isinstance(node, ast.FunctionDef) and node.name == "live_attested_mounts"
)
volume = {
"Name": "hub-postgres",
"Driver": "local",
"Mountpoint": "/var/lib/docker/volumes/hub-postgres/_data",
"Options": {},
"CreatedAt": "2020-01-01T10:00:00Z",
}
namespace = {
"json": json,
"os": os,
"subprocess": SimpleNamespace(
PIPE=subprocess.PIPE,
DEVNULL=subprocess.DEVNULL,
run=lambda *_args, **_kwargs: SimpleNamespace(
returncode=0, stdout=json.dumps([volume])
)
),
"datetime": datetime,
"timezone": timezone,
}
exec(compile(ast.Module(body=[function], type_ignores=[]), "mount-test", "exec"), namespace)
attest = namespace["live_attested_mounts"]
container = {
"Mounts": [{
"Type": "volume", "Name": "hub-postgres",
"Source": "/var/lib/docker/volumes/hub-postgres/_data",
"Destination": "/var/lib/postgresql/data", "Driver": "local", "RW": True,
}]
} }
original = attest(container)
assert original[0]["created_at"] == "2020-01-01T10:00:00Z"
volume["CreatedAt"] = "2021-01-01T10:00:00Z"
recreated = attest(container)
assert recreated[0]["name"] == original[0]["name"]
assert recreated[0]["source"] == original[0]["source"]
assert recreated[0]["created_at"] != original[0]["created_at"]
def test_reconciliation_refuses_state_that_is_not_the_known_failed_forward_shape():
result = McpHubDeploymentReconciliation(FakePortainer(exit_code=52)).run() def test_each_service_uses_its_fixed_image_repository_or_reviewed_external_contract():
assert result == { fake = FakePortainer()
"status": "validation_failed", McpHubDeploymentReconciliation(fake).run()
"reason": "controller state drift is not the exact known recoverable MCP Hub state", script = fake.calls[0][2]["json"]["Cmd"][2]
assert 'def fixed_image_reference(service_name, image_ref):' in script
assert '"dataplane-router": "gitea.example.com/jens/itworx-mcp-hub/dataplane"' in script
assert '"docker-read-broker": "gitea.example.com/jens/itworx-mcp-hub/docker-read-broker"' in script
assert 'artifact["image_source"] != owned_image_source' in script
assert 'artifact["image_revision"] != recorded_live' in script
assert 'main_declares_external_image(image_ref)' in script
assert 'main_declares_contextforge_compat_image(' in script
assert 'def image_digest_matches(image, image_ref):' in script
assert 'aliases.add("docker.io/library/" + repository)' in script
def test_external_candidate_pins_are_exact_and_declared_by_protected_main():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
parsed = ast.parse(script)
function = next(
node for node in parsed.body
if isinstance(node, ast.FunctionDef) and node.name == "fixed_image_reference"
)
declared = {
"postgres:18.6@sha256:0377e72c5289ed2f98cf61b1a9c2db9eb9d300317fe14244492fbc94343b3d04",
"redis:8.10.2@sha256:7ef5b5cec96495a04ca7feff88a9492efeab8053fb284d24bdd73344c9245a48",
} }
namespace = {
"re": re,
def test_reconciliation_refuses_unknown_state_without_snapshot_stage_proof(): "main_declares_external_image": lambda image_ref: image_ref in declared,
result = McpHubDeploymentReconciliation(FakePortainer(exit_code=55)).run() "main_declares_sessionless_dataplane_image": lambda *_args: False,
assert result == { "main_declares_contextforge_compat_image": lambda *_args: False,
"status": "validation_failed",
"reason": "unknown MCP Hub live status was not produced by a proven recoverable snapshot-stage failure",
} }
exec(compile(ast.Module(body=[function], type_ignores=[]), "fixed-image-test", "exec"), namespace)
fixed = namespace["fixed_image_reference"]
assert fixed("postgres", "postgres:18.4@sha256:3a82e1f56c8f0f5616a11103ac3d47e632c3938698946a7ad26da0df1334744a")
assert fixed(
"postgres",
"postgres:18.6@sha256:0377e72c5289ed2f98cf61b1a9c2db9eb9d300317fe14244492fbc94343b3d04",
)
assert fixed("redis", "redis:8.10.0@sha256:c29e49ab2f85760a3827b53882e6dd9f5c6c3f0bb7d724e07bb31cbf275a5236")
assert fixed("redis", "redis:8.10.2@sha256:7ef5b5cec96495a04ca7feff88a9492efeab8053fb284d24bdd73344c9245a48")
assert not fixed("postgres", "postgres:18.6@sha256:" + "a" * 64)
assert not fixed("redis", "redis:8.10.3@sha256:" + "b" * 64)
for service in ("web", "control-api", "dataplane-router", "connector-ingestion"):
assert fixed(service, "sha256:" + "a" * 64)
for service in ("postgres", "redis", "contextforge", "ingestion-clamd", "foreign"):
assert not fixed(service, "sha256:" + "a" * 64)
namespace["main_declares_external_image"] = lambda _image_ref: False
assert not fixed("postgres", "postgres:18.6@sha256:0377e72c5289ed2f98cf61b1a9c2db9eb9d300317fe14244492fbc94343b3d04")
assert not fixed("redis", "redis:8.10.2@sha256:7ef5b5cec96495a04ca7feff88a9492efeab8053fb284d24bdd73344c9245a48")
@pytest.fixture
def contextforge_reconciliation_proof():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
parsed = ast.parse(fake.calls[0][2]["json"]["Cmd"][2])
functions = [
node for node in parsed.body
if isinstance(node, ast.FunctionDef)
and node.name in {
"main_declares_contextforge_compat_image", "fixed_image_reference",
"receipt_bound_image_reference",
}
]
revision = "a" * 40
main_revision = "b" * 40
manifest = (
f"CONTEXTFORGE_UPSTREAM_COMMIT={'c' * 40}\n"
"CONTEXTFORGE_ALEMBIC_HEAD=123456789abc\n"
"CONTEXTFORGE_MCP_SDK=1.2.3\n"
"CONTEXTFORGE_LOCAL_IMAGE=itworx-mcp-hub/contextforge-schema:123456789abc\n"
)
builder = (
'git -C "$SRC" fetch --depth 1 origin "$CONTEXTFORGE_UPSTREAM_COMMIT"\n'
'git -C "$SRC" checkout --detach FETCH_HEAD\n'
'MCP_REQUIREMENT="mcp==${CONTEXTFORGE_MCP_SDK}"\n'
'alembic -c mcpgateway/alembic.ini heads\n'
'docker tag "$STAGED_IMAGE" "$CONTEXTFORGE_LOCAL_IMAGE"\n'
'--release-candidate\n'
'CANDIDATE_IMAGE="itworx-mcp-hub/contextforge-schema:${APPOPS_SOURCE_REVISION}"\n'
'docker tag "$STAGED_IMAGE" "$CANDIDATE_IMAGE"\n'
)
files = {
f"{commit}:infrastructure/images/contextforge/compat-image.env": manifest
for commit in (revision, main_revision)
}
files.update({
f"{commit}:scripts/build_contextforge_compat_image.sh": builder
for commit in (revision, main_revision)
})
def git_run(*args):
assert args[0] == "show"
return SimpleNamespace(returncode=0 if args[1] in files else 1,
stdout=files.get(args[1], ""))
namespace = {"re": re, "git_run": git_run, "main_revision": main_revision,
"recorded_live": revision}
exec( # noqa: S102 -- Execute only extracted repository-owned verifier functions.
compile(ast.Module(body=functions, type_ignores=[]), "contextforge-proof", "exec"), namespace
)
return SimpleNamespace(namespace=namespace, files=files, revision=revision)
def test_contextforge_reconciliation_accepts_exact_receipt_revision(contextforge_reconciliation_proof):
proof = contextforge_reconciliation_proof
namespace = proof.namespace
fixed = namespace["fixed_image_reference"]
reference = f"itworx-mcp-hub/contextforge-schema:{proof.revision}"
image_id = "sha256:" + "d" * 64
record = {"image_id": image_id, "image_ref": reference}
bound = namespace["receipt_bound_image_reference"](image_id, image_id, record, dict(record))
assert fixed("contextforge", bound)
assert fixed("contextforge", "itworx-mcp-hub/contextforge-schema:123456789abc")
# A later main may remove the candidate builder; the recorded receipt's
# ancestor revision must supply the source proof for this immutable image.
proof.files[f"{'b' * 40}:scripts/build_contextforge_compat_image.sh"] = ""
assert fixed("contextforge", bound)
@pytest.mark.parametrize("mutation", [
"wrong_revision", "short_revision", "mutable_tag", "wrong_service",
"missing_candidate_flag", "missing_candidate_assignment", "missing_candidate_tag",
"missing_fetch", "bad_manifest", "missing_recorded_source",
])
def test_contextforge_reconciliation_rejects_unbound_candidates(contextforge_reconciliation_proof, mutation):
proof = contextforge_reconciliation_proof
reference = f"itworx-mcp-hub/contextforge-schema:{proof.revision}"
service = "contextforge"
builder_path = f"{proof.revision}:scripts/build_contextforge_compat_image.sh"
fragments = {
"missing_candidate_flag": '--release-candidate',
"missing_candidate_assignment": 'CANDIDATE_IMAGE="itworx-mcp-hub/contextforge-schema:${APPOPS_SOURCE_REVISION}"',
"missing_candidate_tag": 'docker tag "$STAGED_IMAGE" "$CANDIDATE_IMAGE"',
"missing_fetch": 'git -C "$SRC" fetch --depth 1 origin "$CONTEXTFORGE_UPSTREAM_COMMIT"',
}
if mutation == "wrong_revision":
reference = f"itworx-mcp-hub/contextforge-schema:{'b' * 40}"
elif mutation == "short_revision":
reference = f"itworx-mcp-hub/contextforge-schema:{proof.revision[:12]}"
elif mutation == "mutable_tag":
reference = "itworx-mcp-hub/contextforge-schema:latest"
elif mutation == "wrong_service":
service = "foreign"
elif mutation in fragments:
proof.files[builder_path] = proof.files[builder_path].replace(fragments[mutation], "")
elif mutation == "bad_manifest":
path = f"{proof.revision}:infrastructure/images/contextforge/compat-image.env"
proof.files[path] = proof.files[path].replace("CONTEXTFORGE_ALEMBIC_HEAD=123456789abc", "CONTEXTFORGE_ALEMBIC_HEAD=invalid")
else:
proof.files.pop(builder_path)
assert not proof.namespace["fixed_image_reference"](service, reference)
def test_digest_attestation_normalizes_tagged_docker_hub_repo_digests():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
parsed = ast.parse(script)
function = next(
node for node in parsed.body
if isinstance(node, ast.FunctionDef) and node.name == "image_digest_matches"
)
namespace = {"re": re}
exec(compile(ast.Module(body=[function], type_ignores=[]), "reconcile-test", "exec"), namespace)
matches = namespace["image_digest_matches"]
digest = "sha256:" + "a" * 64
assert matches({"RepoDigests": [f"postgres@{digest}"]}, f"postgres:18.6@{digest}")
assert matches({"RepoDigests": [f"docker.io/library/redis@{digest}"]}, f"redis:8.10.2@{digest}")
assert matches({"RepoDigests": [f"clamav/clamav@{digest}"]}, f"clamav/clamav:1.4.6@{digest}")
assert matches(
{"RepoDigests": [f"registry.example:5000/team/web@{digest}"]},
f"registry.example:5000/team/web:release@{digest}",
)
assert not matches(
{"RepoDigests": [f"redis@sha256:{'b' * 64}"]}, f"redis:8.10.2@{digest}"
)
assert not matches({"RepoDigests": [f"foreign/redis@{digest}"]}, f"redis:8.10.2@{digest}")
def test_id_launched_image_uses_exact_state_and_receipt_reference():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
parsed = ast.parse(script)
function = next(
node for node in parsed.body
if isinstance(node, ast.FunctionDef) and node.name == "receipt_bound_image_reference"
)
namespace = {"re": re}
exec( # noqa: S102
compile(ast.Module(body=[function], type_ignores=[]), "receipt-image-test", "exec"),
namespace,
)
bound_reference = namespace["receipt_bound_image_reference"]
image_id = "sha256:" + "a" * 64
image_ref = (
"postgres:18.6@sha256:"
"0377e72c5289ed2f98cf61b1a9c2db9eb9d300317fe14244492fbc94343b3d04"
)
recorded = {"image_id": image_id, "image_ref": image_ref}
receipt_item = dict(recorded)
assert bound_reference(image_id, image_id, recorded, receipt_item) == image_ref
assert bound_reference(image_ref, image_id, recorded, receipt_item) == image_ref
assert 'image_ref = receipt_bound_image_reference(' in script
assert script.index('image_ref = receipt_bound_image_reference(') < script.index(
'if not fixed_image_reference(name_service, image_ref):'
)
assert 'image_digest_matches(image, image_ref)' in script
for launch_image, live_id, state, receipt in (
("sha256:" + "c" * 64, image_id, recorded, receipt_item),
(
image_id, image_id,
{**recorded, "image_id": "sha256:" + "c" * 64}, receipt_item,
),
(
image_id, image_id,
recorded, {**receipt_item, "image_id": "sha256:" + "c" * 64},
),
(
image_id, image_id,
{**recorded, "image_ref": "redis:8.10.2@sha256:" + "b" * 64},
receipt_item,
),
(image_id, image_id, recorded, {**receipt_item, "image_ref": ""}),
(image_id, "arbitrary-image-id", recorded, receipt_item),
(image_id, image_id,
{**recorded, "image_ref": "sha256:" + "b" * 64},
{**receipt_item, "image_ref": "sha256:" + "b" * 64}),
):
with pytest.raises(SystemExit) as exc:
bound_reference(launch_image, live_id, state, receipt)
assert exc.value.code == 52
def test_reconciliation_reports_invalid_state_and_live_evidence_fail_closed():
expected = {
42: ("validation_failed", "MCP Hub deployment state file is unavailable or invalid"),
45: ("validation_failed", "a live MCP Hub container is not running and healthy"),
47: ("validation_failed", "live MCP Hub Compose project differs from the controller target"),
51: ("validation_failed", "live MCP Hub image revision or external immutable image is not proven by the recorded-live to main chain"),
52: ("validation_failed", "controller state or live facts do not match the exact modern MCP Hub receipt"),
56: ("topology_migration_required", "MCP Hub legacy, unknown, mixed, or unresolved migration state requires guarded deployment or data-aware recovery"),
}
for exit_code, (status, reason) in expected.items():
result = McpHubDeploymentReconciliation(FakePortainer(exit_code=exit_code)).run()
assert result["status"] == status
assert result["reason"] == reason
def test_reconciliation_only_allows_fixed_17_topology_and_rejects_registry_drift():
fake = FakePortainer()
McpHubDeploymentReconciliation(fake).run()
script = fake.calls[0][2]["json"]["Cmd"][2]
assert 'project.get("branch") != "main"' in script
assert 'project.get("slug") != expected_compose_project' in script
assert 'project.get("compose_project") != expected_compose_project' in script
assert 'frozenset(expected_containers) not in {frozenset(expected_fixed), frozenset(expected_runtime)}' in script
assert 'set(expected_containers) != expected_runtime' in script
assert 'set(project_names) != set(expected_containers)' in script
assert '"itworx-mcp-hub-connector-ingestion-1"' in script
assert '"itworx-mcp-hub-ingestion-clamd-1"' in script
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,926 @@
from __future__ import annotations
import copy
import hashlib
import importlib.util
import json
import sys
import types
from datetime import datetime, timezone
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
@pytest.fixture
def controller():
if "fcntl" not in sys.modules:
shim = types.ModuleType("fcntl")
shim.LOCK_EX, shim.LOCK_UN, shim.flock = 2, 8, lambda *_args: None
sys.modules["fcntl"] = shim
spec = importlib.util.spec_from_file_location(
"hub_candidate_controller", ROOT / "ops/gitea-deploy-broker/deploy.py"
)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
@pytest.fixture
def prepared(controller, tmp_path, monkeypatch):
monkeypatch.setattr(controller, "STATE_ROOT", tmp_path)
monkeypatch.setattr(controller, "_mcp_hub_require_trusted_state_root", lambda: None)
monkeypatch.setattr(controller, "_mcp_hub_require_no_posix_acl", lambda _path: None)
revision = "a" * 40
directory = tmp_path / "mcp-hub-release-candidates" / revision
directory.mkdir(parents=True, mode=0o700)
directory.parent.chmod(0o700)
now = (
datetime.now(timezone.utc)
.isoformat(timespec="milliseconds")
.replace("+00:00", "Z")
)
metadata = {
"id": revision[:12],
"repository": controller.MCP_HUB_REPOSITORY,
"revision": revision,
"tree": "b" * 40,
"treeDigest": hashlib.sha256(("git-tree:" + "b" * 40).encode()).hexdigest(),
"builtAt": now,
}
project = {
"_mcp_hub_contract": controller.MCP_HUB_TOPOLOGY_CONTRACT,
"_repository_path": str(tmp_path / "hub.git"),
"_deployment_metadata": metadata,
"_mcp_hub_source_date_epoch": "1790888863",
}
monkeypatch.setattr(
controller, "deployment_metadata", lambda *_args, **_kwargs: metadata
)
monkeypatch.setattr(
controller, "mcp_hub_source_date_epoch", lambda *_args: "1790888863"
)
monkeypatch.setattr(
controller, "mcp_hub_stage_release_source", lambda *_args: tmp_path / "release"
)
checks = []
monkeypatch.setattr(
controller,
"_mcp_hub_check_candidate_exceptions",
lambda *_args: checks.append("gate"),
)
images = {}
model = {"services": {}}
def write(name, value):
data = (
json.dumps(value, sort_keys=True, separators=(",", ":")) + "\n"
).encode()
path = directory / name
path.write_bytes(data)
path.chmod(0o600)
return hashlib.sha256(data).hexdigest()
for index, service in enumerate(controller.MCP_HUB_SERVICES, 1):
image_id = "sha256:" + f"{index:064x}"
ref = f"fixture/{service}@sha256:" + f"{index:064x}"
if service == "contextforge":
ref = f"itworx-mcp-hub/contextforge-schema:{revision}"
model["services"][service] = {"image": ref}
vuln = {
"SchemaVersion": 2,
"Trivy": {"Version": "0.73.0"},
"CreatedAt": now,
"Metadata": {
"ImageID": image_id,
"ImageConfig": {"architecture": "amd64", "os": "linux"},
},
"Results": [
{"Class": "os-pkgs", "Packages": [{"Name": "fixture", "Version": "1"}]}
],
}
secret = {
"ImageID": image_id,
"CreatedAt": now,
"TrivyVersion": "0.73.0",
"FindingCount": 0,
"SensitiveFindingDetailsPersisted": False,
"Results": [{"FindingCount": 0}],
}
images[service] = {
"imageRef": ref,
"imageId": image_id,
"vulnerabilitySha256": write(f"vulnerability-{service}.json", vuln),
"secretSha256": write(f"secret-{service}.json", secret),
}
candidate = {
"schema": "itworx.mcp-hub-release-candidate/v1",
"repository": controller.MCP_HUB_REPOSITORY,
"revision": revision,
"build": metadata,
"sourceDateEpoch": "1790888863",
"images": images,
"composeSha256": hashlib.sha256(
json.dumps(model, sort_keys=True, separators=(",", ":")).encode()
).hexdigest(),
"databaseMetadataSha256": write(
"trivy-db-metadata.json", {"UpdatedAt": now, "DownloadedAt": now}
),
"summarySha256": write("scan-summary.json", {"unresolvedCriticalOrHigh": []}),
"digestsSha256": write(
"image-digests.json",
{
"images": {
s: {"imageId": v["imageId"], "tag": v["imageRef"]}
for s, v in images.items()
}
},
),
}
candidate_sha = write("candidate.json", candidate)
exceptions_sha = write("exceptions.json", {"exceptions": []})
write(
"approval.json",
{
"schema": "itworx.mcp-hub-cve-approval/v1",
"candidateSha256": candidate_sha,
"exceptionsSha256": exceptions_sha,
},
)
labels = {
"org.opencontainers.image.source": controller.MCP_HUB_OCI_IMAGE_SOURCE,
"org.opencontainers.image.revision": revision,
"org.opencontainers.image.source-tree": metadata["tree"],
"org.opencontainers.image.version": metadata["id"],
"org.opencontainers.image.created": metadata["builtAt"],
"org.opencontainers.image.source-date-epoch": "1790888863",
}
facts = {
v["imageRef"]: {
"Id": v["imageId"],
"Config": {"User": "10001:10001", "Labels": dict(labels)},
}
for v in images.values()
}
monkeypatch.setattr(controller, "inspect_image", lambda ref: facts.get(ref))
return types.SimpleNamespace(
project=project,
revision=revision,
directory=directory,
candidate=candidate,
model=model,
metadata=metadata,
write=write,
checks=checks,
facts=facts,
)
def test_fresh_complete_candidate_freezes_metadata_and_checks_explicit_gate(
controller, prepared
):
result = controller.mcp_hub_load_release_candidate(
prepared.project, prepared.revision
)
assert result["build"] == prepared.metadata
assert set(result["images"]) == set(controller.MCP_HUB_SERVICES)
assert prepared.checks == ["gate"]
controller.mcp_hub_require_candidate_images(
prepared.project, prepared.model, prepared.revision
)
@pytest.mark.parametrize(
"mutation",
[
"missing",
"extra",
"revision",
"build",
"epoch",
"scan_hash",
"scan_identity",
"scan_empty",
"secret",
"stale",
"db_stale",
"approval",
"summary",
"digests",
],
)
def test_candidate_drift_rejects_before_any_production_action(
controller, prepared, mutation
):
value = copy.deepcopy(prepared.candidate)
if mutation == "missing":
value["images"].pop("web")
elif mutation == "extra":
value["images"]["foreign"] = value["images"]["web"]
elif mutation == "revision":
value["revision"] = "c" * 40
elif mutation == "build":
value["build"]["tree"] = "c" * 40
elif mutation == "epoch":
value["sourceDateEpoch"] = "1"
elif mutation == "scan_hash":
value["images"]["web"]["vulnerabilitySha256"] = "c" * 64
elif mutation in {"scan_identity", "scan_empty", "stale"}:
path = prepared.directory / "vulnerability-web.json"
report = json.loads(path.read_text())
if mutation == "scan_identity":
report["Metadata"]["ImageID"] = "sha256:" + "c" * 64
elif mutation == "scan_empty":
report["Results"] = []
else:
report["CreatedAt"] = "2026-01-01T00:00:00Z"
value["images"]["web"]["vulnerabilitySha256"] = prepared.write(
path.name, report
)
elif mutation == "secret":
report = json.loads((prepared.directory / "secret-web.json").read_text())
report["FindingCount"] = 1
value["images"]["web"]["secretSha256"] = prepared.write(
"secret-web.json", report
)
elif mutation == "db_stale":
value["databaseMetadataSha256"] = prepared.write(
"trivy-db-metadata.json",
{
"UpdatedAt": "2026-01-01T00:00:00Z",
"DownloadedAt": "2026-01-01T00:00:00Z",
},
)
elif mutation == "summary":
value["summarySha256"] = prepared.write(
"scan-summary.json", {"unresolvedCriticalOrHigh": [{"package": "invented"}]}
)
elif mutation == "digests":
value["digestsSha256"] = prepared.write("image-digests.json", {"images": {}})
candidate_sha = prepared.write("candidate.json", value)
exceptions_sha = hashlib.sha256(
(prepared.directory / "exceptions.json").read_bytes()
).hexdigest()
prepared.write(
"approval.json",
{
"schema": "itworx.mcp-hub-cve-approval/v1",
"candidateSha256": "c" * 64 if mutation == "approval" else candidate_sha,
"exceptionsSha256": exceptions_sha,
},
)
with pytest.raises(controller.DeployError):
controller.mcp_hub_load_release_candidate(prepared.project, prepared.revision)
assert prepared.checks == []
def test_pending_or_expired_human_exception_gate_cannot_be_substituted(
controller, prepared, monkeypatch
):
def denied(*_args):
raise controller.DeployError("CVE exception gate did not pass")
monkeypatch.setattr(controller, "_mcp_hub_check_candidate_exceptions", denied)
with pytest.raises(controller.DeployError, match="CVE exception gate"):
controller.mcp_hub_load_release_candidate(prepared.project, prepared.revision)
@pytest.mark.parametrize(
"service", ["web", "contextforge", "postgres", "connector-ingestion"]
)
def test_retagged_or_changed_candidate_image_is_rejected_before_apply(
controller, prepared, service
):
controller.mcp_hub_load_release_candidate(prepared.project, prepared.revision)
prepared.facts[prepared.candidate["images"][service]["imageRef"]]["Id"] = (
"sha256:" + "f" * 64
)
with pytest.raises(controller.DeployError, match="image identity"):
controller.mcp_hub_require_candidate_images(
prepared.project, prepared.model, prepared.revision
)
def test_changed_rendered_model_is_rejected(controller, prepared):
controller.mcp_hub_load_release_candidate(prepared.project, prepared.revision)
prepared.model["services"]["web"]["environment"] = {"unexpected": "change"}
with pytest.raises(controller.DeployError, match="Compose"):
controller.mcp_hub_require_candidate_images(
prepared.project, prepared.model, prepared.revision
)
def test_changed_approval_is_rejected_between_stages(controller, prepared):
controller.mcp_hub_load_release_candidate(prepared.project, prepared.revision)
prepared.write("exceptions.json", {"exceptions": [{"approvalStatus": "pending"}]})
with pytest.raises(controller.DeployError):
controller.mcp_hub_require_candidate_images(
prepared.project, prepared.model, prepared.revision
)
@pytest.mark.parametrize("service", ["web", "contextforge"])
@pytest.mark.parametrize(
"label",
[
"org.opencontainers.image.source",
"org.opencontainers.image.revision",
"org.opencontainers.image.source-tree",
"org.opencontainers.image.source-date-epoch",
"org.opencontainers.image.created",
"org.opencontainers.image.version",
],
)
def test_frozen_image_provenance_is_checked_before_database_intent(
controller, prepared, service, label
):
controller.mcp_hub_load_release_candidate(prepared.project, prepared.revision)
prepared.facts[prepared.candidate["images"][service]["imageRef"]]["Config"][
"Labels"
][label] = "wrong"
with pytest.raises(controller.DeployError, match="provenance"):
controller.mcp_hub_require_candidate_images(
prepared.project, prepared.model, prepared.revision
)
@pytest.mark.parametrize("service", ["web", "contextforge"])
@pytest.mark.parametrize("user", ["", "root", "root:10001", "0", "00:10001"])
def test_frozen_image_root_user_is_rejected_before_database_intent(
controller, prepared, service, user
):
controller.mcp_hub_load_release_candidate(prepared.project, prepared.revision)
prepared.facts[prepared.candidate["images"][service]["imageRef"]]["Config"][
"User"
] = user
with pytest.raises(controller.DeployError, match="non-root runtime"):
controller.mcp_hub_require_candidate_images(
prepared.project, prepared.model, prepared.revision
)
def test_changed_raw_scan_is_rejected_between_stages(controller, prepared):
controller.mcp_hub_load_release_candidate(prepared.project, prepared.revision)
prepared.write("vulnerability-contextforge.json", {"Results": []})
with pytest.raises(controller.DeployError, match="hash"):
controller.mcp_hub_require_candidate_images(
prepared.project, prepared.model, prepared.revision
)
@pytest.mark.parametrize("changed", [False, True])
def test_staged_start_consumes_no_build_no_pull_and_checks_running_ids(
controller, prepared, monkeypatch, changed
):
controller.mcp_hub_load_release_candidate(prepared.project, prepared.revision)
prepared.project["_mcp_hub_rendered_model"] = prepared.model
commands = []
monkeypatch.setattr(
controller,
"compose_command",
lambda _p: (prepared.directory, ["docker", "compose"]),
)
monkeypatch.setattr(controller, "deployment_environment", lambda *_args: {})
monkeypatch.setattr(
controller, "run", lambda command, **_kwargs: commands.append(command)
)
monkeypatch.setattr(controller, "mcp_hub_wait_services", lambda *_args: None)
expected = prepared.candidate["images"]["postgres"]["imageId"]
monkeypatch.setattr(
controller,
"inspect_container",
lambda _name: {"Image": "sha256:" + "f" * 64 if changed else expected},
)
if changed:
with pytest.raises(controller.DeployError, match="exact scanned image"):
controller.mcp_hub_up_services(
prepared.project,
prepared.revision,
controller.MCP_HUB_REPOSITORY,
("postgres",),
)
else:
controller.mcp_hub_up_services(
prepared.project,
prepared.revision,
controller.MCP_HUB_REPOSITORY,
("postgres",),
)
assert commands == [
[
"docker",
"compose",
"up",
"--detach",
"--no-deps",
"--no-build",
"--pull",
"never",
"postgres",
]
]
@pytest.mark.parametrize("operation", ["start", "migrate"])
def test_compose_operations_pin_all_scanned_ids_without_changing_source_model(
controller, prepared, monkeypatch, operation
):
controller.mcp_hub_load_release_candidate(prepared.project, prepared.revision)
prepared.project["_mcp_hub_rendered_model"] = prepared.model
original = copy.deepcopy(prepared.model)
observed = []
def compose(project):
path = Path(project["_mcp_hub_image_override_file"])
assert path.stat().st_mode & 0o777 == 0o600
observed.append((path, json.loads(path.read_text())))
return prepared.directory, ["docker", "compose", "-f", str(path)]
commands = []
monkeypatch.setattr(controller, "compose_command", compose)
monkeypatch.setattr(controller, "deployment_environment", lambda *_args: {})
monkeypatch.setattr(controller, "run", lambda cmd, **_kwargs: commands.append(cmd))
monkeypatch.setattr(controller, "mcp_hub_wait_services", lambda *_args: None)
monkeypatch.setattr(
controller,
"inspect_container",
lambda _name: {"Image": prepared.candidate["images"]["postgres"]["imageId"]},
)
if operation == "start":
controller.mcp_hub_up_services(
prepared.project,
prepared.revision,
controller.MCP_HUB_REPOSITORY,
("postgres",),
)
else:
controller.mcp_hub_run_database_migration(
prepared.project, prepared.revision, controller.MCP_HUB_REPOSITORY
)
assert commands[0][-3:] == [
"-e",
"SERVICE_NAME=database-migrate",
"control-api",
]
assert len(observed) == len(commands) == 1
path, payload = observed[0]
assert payload == {
"services": {
service: {"image": record["imageId"]}
for service, record in prepared.candidate["images"].items()
}
}
assert "--no-deps" in commands[0]
assert commands[0][commands[0].index("--pull") + 1] == "never"
assert not path.exists()
assert "_mcp_hub_image_override_file" not in prepared.project
assert prepared.model == original
def test_migration_rechecks_candidate_before_running(controller, prepared, monkeypatch):
controller.mcp_hub_load_release_candidate(prepared.project, prepared.revision)
prepared.project["_mcp_hub_rendered_model"] = prepared.model
prepared.write("vulnerability-control-api.json", {"Results": []})
monkeypatch.setattr(
controller, "run", lambda *_args, **_kwargs: pytest.fail("must not run")
)
with pytest.raises(controller.DeployError, match="hash"):
controller.mcp_hub_run_database_migration(
prepared.project, prepared.revision, controller.MCP_HUB_REPOSITORY
)
def test_candidate_runtime_verifies_pinned_ids_after_migration(
controller, prepared, monkeypatch
):
controller.mcp_hub_load_release_candidate(prepared.project, prepared.revision)
project = prepared.project
project["compose_project"] = controller.MCP_HUB_PROJECT
project["slug"] = "itworx-mcp-hub"
project["source_dir"] = str(prepared.directory)
project["compose_files"] = []
project["containers"] = list(controller.MCP_HUB_CONTAINERS)
project["_mcp_hub_rendered_model"] = prepared.model
project["_mcp_hub_candidate_image_ids"] = (
controller.mcp_hub_require_candidate_images(
project, prepared.model, prepared.revision
)
)
for service in ("control-api", "worker"):
prepared.model["services"][service]["environment"] = {
"INGESTION_CONNECTOR_ENABLED": "true"
}
containers = {}
for service, record in prepared.candidate["images"].items():
name = f"itworx-mcp-hub-{service}-1"
containers[name] = {
"Id": "c" * 64,
"Image": record["imageId"],
"Config": {
"Image": record["imageId"],
"Env": (
["INGESTION_CONNECTOR_ENABLED=true"]
if service in {"control-api", "worker"}
else []
),
"Labels": {
"com.docker.compose.project": controller.MCP_HUB_PROJECT,
"com.docker.compose.service": service,
},
},
"State": {"Running": True, "Health": {"Status": "healthy"}},
}
monkeypatch.setattr(
controller, "compose_project_container_names", lambda _project: set(containers)
)
monkeypatch.setattr(
controller, "inspect_container", lambda name: containers.get(name)
)
monkeypatch.setattr(
controller,
"container_state",
lambda _name: (True, "healthy", controller.MCP_HUB_PROJECT),
)
labels = {
"org.opencontainers.image.revision": prepared.revision,
"org.opencontainers.image.source": controller.MCP_HUB_OCI_IMAGE_SOURCE,
"org.opencontainers.image.source-tree": prepared.metadata["tree"],
"org.opencontainers.image.version": prepared.metadata["id"],
"org.opencontainers.image.created": prepared.metadata["builtAt"],
}
image_ids_by_ref = {
record["imageRef"]: record["imageId"]
for record in prepared.candidate["images"].values()
}
monkeypatch.setattr(
controller,
"inspect_image",
lambda image_ref: {
"Id": image_ids_by_ref.get(image_ref, image_ref),
"Config": {"Labels": labels},
"RepoDigests": [],
},
)
monkeypatch.setattr(
controller, "mcp_hub_attested_live_mounts", lambda *_args, **_kwargs: []
)
assert set(controller.verify_mcp_hub_live_topology(project, prepared.model)) == set(
containers
)
assert set(
controller.verify_containers(
project, expected_image_ids=project["_mcp_hub_candidate_image_ids"]
)
) == set(containers)
for service in controller.MCP_HUB_APPLICATION_SERVICES:
prepared.model["services"][service]["build"] = {"context": "."}
manifest = (
"CONTEXTFORGE_UPSTREAM_COMMIT=" + "1" * 40 + "\n"
"CONTEXTFORGE_ALEMBIC_HEAD=12d4a0c7789c\n"
"CONTEXTFORGE_MCP_SDK=1.30.0\n"
"CONTEXTFORGE_LOCAL_IMAGE=itworx-mcp-hub/contextforge-schema:12d4a0c7789c\n"
)
builder = "\n".join(
[
"--release-candidate",
'CANDIDATE_IMAGE="itworx-mcp-hub/contextforge-schema:${APPOPS_SOURCE_REVISION}"',
'docker tag "$STAGED_IMAGE" "$CANDIDATE_IMAGE"',
'git -C "$SRC" fetch --depth 1 origin "$CONTEXTFORGE_UPSTREAM_COMMIT"',
'git -C "$SRC" checkout --detach FETCH_HEAD',
"alembic -c mcpgateway/alembic.ini heads",
'docker tag "$STAGED_IMAGE" "$CONTEXTFORGE_LOCAL_IMAGE"',
]
)
monkeypatch.setattr(
controller,
"_mcp_hub_git_output",
lambda _project, _show, path: manifest
if path.endswith("compat-image.env")
else builder,
)
monkeypatch.setattr(
controller, "_mcp_hub_dataplane_compat_proven", lambda *_args: True
)
monkeypatch.setattr(controller, "_mcp_hub_digest_ref_proven", lambda *_args: True)
monkeypatch.setattr(
controller,
"_mcp_hub_live_image_proven",
lambda _project, name, item, source_ref, *_args, **_kwargs: {
"container_id": item["Id"],
"image_id": item["Image"],
"image_ref": source_ref,
"container_revision": "",
"image_revision": prepared.revision,
"image_source": controller.MCP_HUB_OCI_IMAGE_SOURCE,
"image_tree": prepared.metadata["tree"],
"image_version": prepared.metadata["id"],
"image_created": prepared.metadata["builtAt"],
},
)
receipt = controller.verify_deployment(
project,
prepared.metadata,
expected_image_ids=project["_mcp_hub_candidate_image_ids"],
)
for service in controller.MCP_HUB_APPLICATION_SERVICES:
name = f"itworx-mcp-hub-{service}-1"
assert (
receipt["containers"][name]["image_ref"]
== receipt["containers"][name]["image_id"]
)
assert (
controller.validate_recorded_receipt(
project, receipt, controller.MCP_HUB_REPOSITORY, prepared.revision
)
== prepared.metadata
)
state = {}
monkeypatch.setattr(
controller,
"_mcp_hub_read_controller_state_bytes",
lambda **_kwargs: state.get("bytes"),
)
monkeypatch.setattr(
controller,
"_mcp_hub_write_controller_state_bytes",
lambda payload, **_kwargs: state.__setitem__("bytes", payload),
)
controller.write_state(
controller.MCP_HUB_REPOSITORY,
project,
prepared.revision,
"success",
"verified",
live_receipt=receipt,
)
saved = json.loads(state["bytes"])
assert saved["live_status"] == "healthy"
for name in controller.MCP_HUB_CONTAINERS:
assert (
saved["live_artifacts"]["containers"][name]["image_ref"]
== receipt["containers"][name]["image_ref"]
)
project.pop("_mcp_hub_candidate_image_ids")
assert (
controller.verified_current_receipt(
controller.MCP_HUB_REPOSITORY,
project,
prepared.revision,
prepared.metadata["tree"],
)
== receipt
)
assert "_mcp_hub_receipt_image_ids" not in project
project["_repository_name"] = controller.MCP_HUB_REPOSITORY
rollback_commands = []
monkeypatch.setattr(
controller, "run", lambda command, **_kwargs: rollback_commands.append(command)
)
snapshot = controller.capture_compose_rollback(project)
assert len(snapshot["containers"]) == len(rollback_commands) == 17
for item in snapshot["containers"]:
assert item["image_ref"] == receipt["containers"][item["name"]]["image_ref"]
cf = prepared.candidate["images"]["contextforge"]
assert not controller._mcp_hub_contextforge_compat_proven(
project, prepared.revision, cf["imageRef"], "sha256:" + "f" * 64
)
containers["itworx-mcp-hub-web-1"]["Config"]["Image"] = "web:mutable"
with pytest.raises(controller.DeployError, match="identity or health"):
controller.verify_mcp_hub_live_topology(project, prepared.model)
@pytest.mark.parametrize("same_revision", [True, False])
def test_same_revision_receipt_prepares_exact_source_before_verification(
controller, prepared, monkeypatch, same_revision
):
project = prepared.project
project.update({"source_dir": str(prepared.directory), "slug": "itworx-mcp-hub"})
monkeypatch.setattr(
controller,
"read_state",
lambda _project: {
"live_revision": prepared.revision if same_revision else "b" * 40
},
)
events = []
monkeypatch.setattr(
controller,
"sync_source",
lambda *_args, **kwargs: events.append(("sync", kwargs["exact"])),
)
monkeypatch.setattr(
controller, "copy_environment", lambda *_args: events.append("environment")
)
monkeypatch.setattr(
controller,
"mcp_hub_compose_model",
lambda *_args: events.append("model") or prepared.model,
)
controller.mcp_hub_prepare_current_receipt_model(
prepared.directory, project, controller.MCP_HUB_REPOSITORY, prepared.revision
)
assert events == ([("sync", True), "environment", "model"] if same_revision else [])
assert (project.get("_mcp_hub_rendered_model") is prepared.model) is same_revision
@pytest.mark.parametrize("mutation", ["missing", "extra", "tag", "nonmapping"])
def test_candidate_compose_override_rejects_malformed_ids(
controller, prepared, mutation
):
ids = {
f"itworx-mcp-hub-{service}-1": record["imageId"]
for service, record in prepared.candidate["images"].items()
}
if mutation == "missing":
ids.pop("itworx-mcp-hub-web-1")
elif mutation == "extra":
ids["foreign"] = "sha256:" + "a" * 64
elif mutation == "tag":
ids["itworx-mcp-hub-web-1"] = "web:latest"
else:
ids = []
prepared.project["_mcp_hub_rendered_model"] = prepared.model
prepared.project["_mcp_hub_candidate_image_ids"] = ids
with pytest.raises(controller.DeployError, match="candidate image IDs"):
with controller._mcp_hub_compose_image_override(prepared.project):
pytest.fail("must reject invalid image IDs")
def test_actual_high_findings_are_bound_to_current_service_tags(controller, prepared):
report = json.loads((prepared.directory / "vulnerability-web.json").read_text())
report["Results"][0]["Vulnerabilities"] = [
{
"PkgName": "fixture",
"VulnerabilityID": "CVE-2026-12345",
"InstalledVersion": "1",
"Severity": "HIGH",
}
]
prepared.candidate["images"]["web"]["vulnerabilitySha256"] = prepared.write(
"vulnerability-web.json", report
)
summary = {
"unresolvedCriticalOrHigh": [
{
"package": "fixture",
"cves": ["CVE-2026-12345"],
"installedVersion": "1",
"severity": "HIGH",
"affectedImages": [prepared.candidate["images"]["web"]["imageRef"]],
}
]
}
prepared.candidate["summarySha256"] = prepared.write("scan-summary.json", summary)
candidate_sha = prepared.write("candidate.json", prepared.candidate)
prepared.write(
"approval.json",
{
"schema": "itworx.mcp-hub-cve-approval/v1",
"candidateSha256": candidate_sha,
"exceptionsSha256": hashlib.sha256(
(prepared.directory / "exceptions.json").read_bytes()
).hexdigest(),
},
)
controller.mcp_hub_load_release_candidate(prepared.project, prepared.revision)
assert prepared.checks == ["gate"]
def test_fixable_high_findings_cannot_be_approved_around(controller, prepared):
report = json.loads((prepared.directory / "vulnerability-web.json").read_text())
report["Results"][0]["Vulnerabilities"] = [
{
"PkgName": "fixture",
"VulnerabilityID": "CVE-2026-12345",
"InstalledVersion": "1",
"FixedVersion": "2",
"Severity": "HIGH",
}
]
prepared.candidate["images"]["web"]["vulnerabilitySha256"] = prepared.write(
"vulnerability-web.json", report
)
candidate_sha = prepared.write("candidate.json", prepared.candidate)
prepared.write(
"approval.json",
{
"schema": "itworx.mcp-hub-cve-approval/v1",
"candidateSha256": candidate_sha,
"exceptionsSha256": hashlib.sha256(
(prepared.directory / "exceptions.json").read_bytes()
).hexdigest(),
},
)
with pytest.raises(controller.DeployError, match="fixable critical/high"):
controller.mcp_hub_load_release_candidate(prepared.project, prepared.revision)
assert prepared.checks == []
def test_missing_normal_upgrade_volume_baseline_denies_before_intent_or_start(
controller, prepared, monkeypatch
):
controller.mcp_hub_load_release_candidate(prepared.project, prepared.revision)
monkeypatch.setattr(controller, "mcp_hub_require_docker_capacity", lambda: None)
monkeypatch.setattr(
controller, "mcp_hub_stage_release_source", lambda *_args: prepared.directory
)
monkeypatch.setattr(
controller, "mcp_hub_compose_model", lambda *_args: prepared.model
)
monkeypatch.setattr(
controller,
"compose_command",
lambda *_args: (prepared.directory, ["docker", "compose"]),
)
monkeypatch.setattr(controller, "deployment_environment", lambda *_args: {})
monkeypatch.setattr(
controller.subprocess,
"run",
lambda *_args, **_kwargs: types.SimpleNamespace(returncode=0),
)
monkeypatch.setattr(
controller,
"_mcp_hub_begin_migration_intent",
lambda *_args: pytest.fail(
"missing volume identity must precede durable intent"
),
)
monkeypatch.setattr(
controller,
"mcp_hub_up_services",
lambda *_args: pytest.fail(
"missing volume identity must precede Compose mutation"
),
)
with pytest.raises(controller.DeployError, match="volume baseline"):
controller.deploy_mcp_hub_staged(
prepared.project, prepared.revision, controller.MCP_HUB_REPOSITORY
)
@pytest.mark.parametrize(
"mode, candidate_epoch",
[("deploy", "1790888863"), ("deploy", "1"), ("preflight", "1790888863")],
)
def test_main_hub_path_sets_validated_epoch_before_compose_render(
controller, prepared, monkeypatch, mode, candidate_epoch
):
class ReachedValidatedRender(Exception):
pass
target = {
**prepared.project,
"branch": "main",
"slug": controller.MCP_HUB_PROJECT,
"source_dir": str(prepared.directory),
}
target["compose_project"] = controller.MCP_HUB_PROJECT
target.pop("_mcp_hub_source_date_epoch", None)
monkeypatch.setattr(
controller, "load_projects", lambda: {controller.MCP_HUB_REPOSITORY: target}
)
monkeypatch.setattr(
controller,
"effective_mcp_hub_project",
lambda _repository, project: dict(project),
)
monkeypatch.setattr(
controller, "repository_path", lambda *_args: prepared.directory
)
monkeypatch.setattr(controller, "validate_commit", lambda *_args: prepared.revision)
candidate = copy.deepcopy(prepared.candidate)
candidate["sourceDateEpoch"] = candidate_epoch
monkeypatch.setattr(
controller, "mcp_hub_load_release_candidate", lambda *_args: candidate
)
monkeypatch.setenv("SOURCE_DATE_EPOCH", "untrusted-environment-override")
def render(_repo, project, revision):
if mode == "deploy" and candidate_epoch != "1790888863":
pytest.fail(
"mismatched candidate epoch must deny before Compose validation"
)
assert project["_mcp_hub_source_date_epoch"] == "1790888863"
assert (
controller.deployment_environment(
project, revision, controller.MCP_HUB_REPOSITORY
)["SOURCE_DATE_EPOCH"]
== "1790888863"
)
raise ReachedValidatedRender()
monkeypatch.setattr(controller, "validate_project_config", render)
monkeypatch.setattr(
sys,
"argv",
["deploy.py", mode, controller.MCP_HUB_REPOSITORY, prepared.revision],
)
if mode == "deploy" and candidate_epoch != "1790888863":
with pytest.raises(controller.DeployError, match="epoch"):
controller.main()
else:
with pytest.raises(ReachedValidatedRender):
controller.main()
+1 -1
View File
@@ -7,7 +7,7 @@ settings:
overrides: overrides:
'@humanfs/node@>=0.16.6 <0.16.8': 0.16.8 '@humanfs/node@>=0.16.6 <0.16.8': 0.16.8
brace-expansion@>=1.0.0 <1.1.21: 1.1.21 brace-expansion@>=1.0.0 <1.1.21: 1.1.21
brace-expansion@>=2.0.0 <2.0.3: 2.0.3 brace-expansion@>=2.0.0 <2.1.6: 2.1.6
brace-expansion@>=5.0.0 <5.0.12: 5.0.12 brace-expansion@>=5.0.0 <5.0.12: 5.0.12
browserslist@>=4.0.0 <4.28.7: 4.28.7 browserslist@>=4.0.0 <4.28.7: 4.28.7
flatted@<3.4.2: 3.4.2 flatted@<3.4.2: 3.4.2
+1 -1
View File
@@ -373,7 +373,7 @@ minimumReleaseAge: 1440
overrides: overrides:
"@humanfs/node@>=0.16.6 <0.16.8": 0.16.8 "@humanfs/node@>=0.16.6 <0.16.8": 0.16.8
"brace-expansion@>=1.0.0 <1.1.21": 1.1.21 "brace-expansion@>=1.0.0 <1.1.21": 1.1.21
"brace-expansion@>=2.0.0 <2.0.3": 2.0.3 "brace-expansion@>=2.0.0 <2.1.6": 2.1.6
"brace-expansion@>=5.0.0 <5.0.12": 5.0.12 "brace-expansion@>=5.0.0 <5.0.12": 5.0.12
"browserslist@>=4.0.0 <4.28.7": 4.28.7 "browserslist@>=4.0.0 <4.28.7": 4.28.7
"flatted@<3.4.2": 3.4.2 "flatted@<3.4.2": 3.4.2
+37
View File
@@ -0,0 +1,37 @@
import assert from "node:assert/strict";
import { createRequire } from "node:module";
import { describe, it } from "node:test";
// Exercise the dependency actually used by Tailwind's globbing toolchain.
// A direct root import would miss the transitive version selected by pnpm.
let requireFromPackage = createRequire(import.meta.url);
for (const packageName of ["tailwindcss", "sucrase", "glob", "minimatch"]) {
requireFromPackage = createRequire(
requireFromPackage.resolve(`${packageName}/package.json`),
);
}
const { expand } = requireFromPackage("brace-expansion") as {
expand: (pattern: string) => string[];
};
describe("build dependency security", () => {
it("preserves ordinary brace expansion", () => {
assert.deepEqual(expand("assets/{one,two}.css"), [
"assets/one.css",
"assets/two.css",
]);
});
it("bounds deeply nested brace sets without overflowing the stack", () => {
const pattern = "{".repeat(3200) + "a,b" + "}".repeat(3200);
let result: string[] = [];
assert.doesNotThrow(() => { result = expand(pattern); });
assert.ok(Array.isArray(result));
assert.ok(result.length <= 2);
});
it("bounds deeply nested comma alternatives without overflowing the stack", () => {
const pattern = "{a,".repeat(4000) + "z" + "}".repeat(4000);
assert.doesNotThrow(() => expand(pattern));
});
});