Compare commits

..

4 Commits

Author SHA1 Message Date
jenkins
3fec1cd5dc hermes: isolate Atlas SCM write authority 2026-08-17 02:03:17 -03:00
jenkins
b0812bbe9f hermes: match canonical Forgejo PR URL 2026-08-16 23:12:08 -03:00
jenkins
bf777066fb hermes: harden Forgejo client boundary 2026-08-16 23:12:08 -03:00
jenkins
36031958a4 hermes: bound Atlas pull request client 2026-08-16 23:12:08 -03:00
45 changed files with 5280 additions and 6545 deletions

View File

@ -1,19 +0,0 @@
# clusters/atlas/flux-system/applications/hermes-observer-bindings/kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: hermes-observer-bindings
namespace: flux-system
spec:
interval: 10m
path: ./services/hermes-observer-bindings
prune: true
sourceRef:
kind: GitRepository
name: flux-system
namespace: flux-system
wait: true
timeout: 5m
dependsOn:
- name: hermes
- name: hermes-observer-rbac

View File

@ -31,7 +31,6 @@ resources:
- hermes-scm-broker-code/kustomization.yaml
- hermes/kustomization.yaml
- hermes-observer-rbac/kustomization.yaml
- hermes-observer-bindings/kustomization.yaml
- hermes-scm-broker/kustomization.yaml
- hermes-chat/kustomization.yaml
- hermes-triage-demo/kustomization.yaml

View File

@ -629,125 +629,16 @@ source = source[:runs_start] + runs_source
path.write_text(source)
PY
# ``create_task(initial_status="blocked")`` is an explicit operator park, but
# upstream only treats later block_task() events as sticky. Recognize the
# existing created(status=blocked) event too. This also protects tasks created
# before this image patch without making dependency or circuit-breaker blocks
# depend on a new persisted field.
RUN python - <<'PY'
from pathlib import Path
db_path = Path("/opt/hermes/hermes_cli/kanban_db.py")
db_source = db_path.read_text()
# The rewritten sticky-block gate below classifies a task from the ``created``
# event payload that ``create_task`` writes. That producer is upstream code we
# do not own, so anchor the exact contract the consumer depends on instead of
# trusting it: an ``initial_status="blocked"`` park must resolve ``task_status``
# to ``"blocked"``, every other creation must resolve it to something else, and
# the ``created`` event must carry that same variable under the ``"status"``
# key. If upstream renames the key, drops it, hardcodes it, or stops deriving it
# from ``initial_status``, fail the build here rather than ship a consumer that
# silently mis-classifies every task it reads. These assert only -- they
# deliberately do not rewrite the producer. The regression suite copied in below
# is the second net: it proves the same contract against the real create_task,
# for the drift these textual anchors cannot see.
producer_park_anchor = ''' if initial_status == "blocked":
task_status = "blocked"
'''
if db_source.count(producer_park_anchor) != 1:
raise SystemExit(
"Hermes Kanban create_task park semantics changed: expected 1, "
f"found {db_source.count(producer_park_anchor)}"
)
# The mirror of the park branch: every non-park creation must resolve to a
# status the gate does *not* read as a park. Without this, drift that widened
# the ladder to park ordinary work would leave both other anchors intact.
producer_unparked_anchor = ''' elif triage:
task_status = "triage"
else:
task_status = "ready"
'''
if db_source.count(producer_unparked_anchor) != 1:
raise SystemExit(
"Hermes Kanban create_task non-park status ladder changed: expected 1, "
f"found {db_source.count(producer_unparked_anchor)}"
)
producer_event_anchor = ''' _append_event(
conn,
task_id,
"created",
{
"assignee": assignee,
"status": task_status,
'''
if db_source.count(producer_event_anchor) != 1:
raise SystemExit(
"Hermes Kanban created-event status payload changed: expected 1, "
f"found {db_source.count(producer_event_anchor)}"
)
sticky_doc_before = ''' The cheapest signal that distinguishes the two is the most recent
``"blocked"`` / ``"unblocked"`` event for the task. If the most
recent one is ``"blocked"`` (or there is a ``"blocked"`` event and
no ``"unblocked"`` event has fired since), the task is sticky and
``recompute_ready`` must *not* auto-promote it.
Returns ``False`` when there is no such event at all (e.g. the task
'''
sticky_doc_after = ''' The cheapest signal that distinguishes the two is the most recent
``"created"`` / ``"blocked"`` / ``"unblocked"`` event for the task.
A ``"created"`` event whose payload has ``status="blocked"`` is the
explicit park requested by ``create_task(initial_status="blocked")``.
A later ``"unblocked"`` event clears either kind of sticky block.
Returns ``False`` when there is no such event at all (e.g. the task
'''
if db_source.count(sticky_doc_before) != 1:
raise SystemExit(
"Hermes Kanban sticky-block documentation changed: expected 1, "
f"found {db_source.count(sticky_doc_before)}"
)
db_source = db_source.replace(sticky_doc_before, sticky_doc_after, 1)
sticky_before = ''' row = conn.execute(
"SELECT kind FROM task_events "
"WHERE task_id = ? AND kind IN ('blocked', 'unblocked') "
"ORDER BY id DESC LIMIT 1",
(task_id,),
).fetchone()
return bool(row) and row["kind"] == "blocked"
'''
sticky_after = ''' row = conn.execute(
"SELECT kind, payload FROM task_events "
"WHERE task_id = ? AND kind IN ('created', 'blocked', 'unblocked') "
"ORDER BY id DESC LIMIT 1",
(task_id,),
).fetchone()
if not row or row["kind"] == "unblocked":
return False
if row["kind"] == "blocked":
return True
try:
payload = json.loads(row["payload"] or "{}")
except (TypeError, ValueError):
return False
if not isinstance(payload, dict):
return False
return payload.get("status") == "blocked"
'''
if db_source.count(sticky_before) != 1:
raise SystemExit(
"Hermes Kanban sticky-block gate changed: expected 1, "
f"found {db_source.count(sticky_before)}"
)
db_source = db_source.replace(sticky_before, sticky_after, 1)
# A manual evidence-based close must not expose a parked task to the dispatcher
# between separate `unblock` and `complete` commands. Upstream already permits
# direct completion from `blocked`; extend the same atomic path to `scheduled`
# and make that contract visible in CLI help so agents do not create a ready
# window that can launch redundant work.
RUN python - <<'PY'
from pathlib import Path
db_path = Path("/opt/hermes/hermes_cli/kanban_db.py")
db_source = db_path.read_text()
state_before = ''' WHERE id = ?
AND status IN ('running', 'ready', 'blocked')
'''
@ -780,10 +671,6 @@ if cli_source.count(help_before) != 1:
cli_path.write_text(cli_source.replace(help_before, help_after, 1))
PY
COPY dockerfiles/hermes-kanban-blocked-regression.py /tmp/hermes-kanban-blocked-regression.py
RUN /opt/hermes/.venv/bin/python /tmp/hermes-kanban-blocked-regression.py \
&& rm /tmp/hermes-kanban-blocked-regression.py
COPY dockerfiles/hermes-python-sandbox-tool.py /opt/hermes/tools/python_sandbox_tool.py
COPY dockerfiles/hermes-public-extract/__init__.py /opt/hermes/plugins/web/public_extract/__init__.py
COPY dockerfiles/hermes-public-extract/plugin.yaml /opt/hermes/plugins/web/public_extract/plugin.yaml

View File

@ -1,5 +1,4 @@
**
!dockerfiles/hermes-kanban-blocked-regression.py
!dockerfiles/hermes-python-sandbox-tool.py
!dockerfiles/hermes-public-extract/
!dockerfiles/hermes-public-extract/**

View File

@ -1,249 +0,0 @@
"""Regression tests for Hermes Kanban blocked-task scheduling semantics."""
from __future__ import annotations
import json
import os
import tempfile
import unittest
from pathlib import Path
_HERMES_HOME = tempfile.TemporaryDirectory(prefix="hermes-kanban-test-home-")
os.environ["HERMES_HOME"] = _HERMES_HOME.name
from hermes_cli import kanban_db # noqa: E402
class BlockedTaskSchedulingTests(unittest.TestCase):
"""Exercise the real upstream database API against isolated databases."""
def setUp(self) -> None:
self._database_dir = tempfile.TemporaryDirectory(
prefix="hermes-kanban-test-db-"
)
self.connection = kanban_db.connect(
Path(self._database_dir.name) / "kanban.db"
)
def tearDown(self) -> None:
self.connection.close()
self._database_dir.cleanup()
def create_task(self, title: str, **kwargs: object) -> str:
return kanban_db.create_task(self.connection, title=title, **kwargs)
def status(self, task_id: str) -> str:
task = kanban_db.get_task(self.connection, task_id)
self.assertIsNotNone(task)
return task.status
def created_payload(self, task_id: str) -> dict:
"""Return the decoded payload of the task's ``created`` event."""
row = self.connection.execute(
"SELECT payload FROM task_events "
"WHERE task_id = ? AND kind = 'created' ORDER BY id LIMIT 1",
(task_id,),
).fetchone()
self.assertIsNotNone(row, "create_task emitted no 'created' event")
return json.loads(row["payload"] or "{}")
def set_created_payload(self, task_id: str, payload: object) -> None:
"""Overwrite a ``created`` event payload to simulate producer drift."""
with self.connection:
self.connection.execute(
"UPDATE task_events SET payload = ? "
"WHERE task_id = ? AND kind = 'created'",
(payload, task_id),
)
def block_via_circuit_breaker(self, title: str) -> str:
"""Create + claim a normal task, then trip the breaker once on it.
The resulting task is ``blocked`` with exactly one recorded failure and
-- critically -- no ``blocked`` event, because the breaker emits
``gave_up``. Its most recent created/blocked/unblocked event is
therefore its own ``created`` event, which is what makes it the right
probe for the created-event branch of the sticky gate.
"""
task_id = self.create_task(title)
self.assertIsNotNone(kanban_db.claim_task(self.connection, task_id))
self.assertTrue(
kanban_db._record_spawn_failure(
self.connection,
task_id,
"worker failed",
failure_limit=1,
)
)
self.assertEqual(self.status(task_id), "blocked")
return task_id
def test_initial_block_without_parents_is_sticky(self) -> None:
task_id = self.create_task("operator parked", initial_status="blocked")
self.assertEqual(kanban_db.recompute_ready(self.connection), 0)
self.assertEqual(self.status(task_id), "blocked")
def test_initial_block_with_complete_parents_is_sticky(self) -> None:
parent_id = self.create_task("completed prerequisite")
self.assertTrue(kanban_db.complete_task(self.connection, parent_id))
task_id = self.create_task(
"operator parked after prerequisite",
initial_status="blocked",
parents=[parent_id],
)
self.assertEqual(kanban_db.recompute_ready(self.connection), 0)
self.assertEqual(self.status(task_id), "blocked")
def test_initial_block_remains_sticky_when_parent_later_completes(self) -> None:
parent_id = self.create_task("incomplete prerequisite")
task_id = self.create_task(
"operator parked before prerequisite completes",
initial_status="blocked",
parents=[parent_id],
)
self.assertEqual(self.status(task_id), "blocked")
self.assertTrue(kanban_db.complete_task(self.connection, parent_id))
self.assertEqual(self.status(task_id), "blocked")
def test_initial_block_is_released_by_an_explicit_unblock(self) -> None:
task_id = self.create_task("operator parked", initial_status="blocked")
self.assertTrue(kanban_db.unblock_task(self.connection, task_id))
self.assertEqual(self.status(task_id), "ready")
def test_dependency_block_promotes_after_parent_completes(self) -> None:
parent_id = self.create_task("incomplete prerequisite")
task_id = self.create_task("dependency gated", parents=[parent_id])
self.assertEqual(self.status(task_id), "todo")
self.assertTrue(kanban_db.complete_task(self.connection, parent_id))
self.assertEqual(self.status(task_id), "ready")
def test_explicit_block_and_unblock_remain_sticky_and_reversible(self) -> None:
task_id = self.create_task("worker handoff")
self.assertTrue(
kanban_db.block_task(
self.connection,
task_id,
reason="human review required",
)
)
self.assertEqual(kanban_db.recompute_ready(self.connection), 0)
self.assertEqual(self.status(task_id), "blocked")
self.assertTrue(kanban_db.unblock_task(self.connection, task_id))
self.assertEqual(self.status(task_id), "ready")
def test_circuit_breaker_block_is_not_auto_promoted(self) -> None:
task_id = self.create_task("repeated worker failure")
self.assertIsNotNone(kanban_db.claim_task(self.connection, task_id))
# Upstream has no public circuit-breaker entry point; exercise the
# dispatcher helper that owns its persisted failure-limit semantics.
self.assertTrue(
kanban_db._record_spawn_failure(
self.connection,
task_id,
"worker failed",
failure_limit=1,
)
)
self.assertEqual(self.status(task_id), "blocked")
self.assertEqual(
kanban_db.recompute_ready(self.connection, failure_limit=1),
0,
)
self.assertEqual(self.status(task_id), "blocked")
def test_circuit_breaker_block_is_promoted_below_the_failure_limit(self) -> None:
"""Non-sticky oracle for the created-event branch of the sticky gate.
A circuit-breaker block must still auto-recover once the effective
failure limit rises above the recorded failure count -- that is the
pre-existing upstream contract the sticky-block patch must not widen.
This task's most recent created/blocked/unblocked event is its own
``created`` event carrying ``status="ready"``, so it is exactly the
case that a mis-classifying gate strands. It fails if the gate treats
any ``created`` event as a park (over-broad consumer) and it fails if
``create_task`` drifts to labelling every ``created`` event
``"blocked"`` (over-broad producer), while the
circuit-breaker-at-current-limit case above still holds.
"""
task_id = self.block_via_circuit_breaker("recoverable worker failure")
self.assertFalse(
kanban_db._has_sticky_block(self.connection, task_id),
"a circuit-breaker block must never be classified as a park",
)
self.assertEqual(
kanban_db.recompute_ready(self.connection, failure_limit=2),
1,
)
self.assertEqual(self.status(task_id), "ready")
def test_created_event_carries_the_status_the_sticky_gate_reads(self) -> None:
"""Producer contract asserted directly against the real create_task.
The sticky gate reads ``created`` payload ``status``. Pin both poles of
that contract so producer drift is a test failure here as well as a
build failure at the Dockerfile source anchor.
"""
parked_id = self.create_task("operator parked", initial_status="blocked")
normal_id = self.create_task("ordinary work")
self.assertEqual(self.created_payload(parked_id).get("status"), "blocked")
self.assertEqual(self.created_payload(normal_id).get("status"), "ready")
def test_non_blocked_created_payloads_are_not_treated_as_a_park(self) -> None:
"""Only ``status="blocked"`` parks; every other value must promote."""
for payload_status in ("ready", "todo", "running", "triage"):
with self.subTest(payload_status=payload_status):
task_id = self.block_via_circuit_breaker(
f"breaker trip with {payload_status} created payload"
)
self.set_created_payload(
task_id, json.dumps({"status": payload_status})
)
self.assertFalse(
kanban_db._has_sticky_block(self.connection, task_id)
)
self.assertEqual(
kanban_db.recompute_ready(self.connection, failure_limit=2),
1,
)
self.assertEqual(self.status(task_id), "ready")
def test_malformed_created_payloads_fail_closed_to_not_parked(self) -> None:
"""Undecodable or non-object payloads must not park -- or crash.
``recompute_ready`` walks every blocked task in one pass, so an
exception escaping the gate would stall promotion board-wide. Falling
back to "not parked" preserves upstream's pre-patch auto-recover
default for rows the gate cannot interpret.
"""
malformed = (None, "", " ", "{not json", "[]", "null", '"blocked"', "17")
for payload in malformed:
with self.subTest(payload=payload):
task_id = self.block_via_circuit_breaker(
f"breaker trip with payload {payload!r}"
)
self.set_created_payload(task_id, payload)
self.assertFalse(
kanban_db._has_sticky_block(self.connection, task_id)
)
self.assertEqual(
kanban_db.recompute_ready(self.connection, failure_limit=2),
1,
)
self.assertEqual(self.status(task_id), "ready")
if __name__ == "__main__":
unittest.main(verbosity=2)

View File

@ -416,3 +416,101 @@ def test_jobs_dashboard_bar_gauges_use_solid_threshold_colors():
assert {"color": "dark-yellow", "value": 93} in threshold_steps
assert {"color": "dark-blue", "value": 100} in threshold_steps
def test_jobs_dashboard_collapses_heavy_drilldowns_for_light_first_paint():
mod = load_module()
dashboard = mod.build_jobs_dashboard()
panels = dashboard["panels"]
rows = [panel for panel in panels if panel["type"] == "row"]
visible_query_panels = [panel for panel in panels if panel["type"] != "row"]
nested_panels_by_title = {
child["title"]: child
for row in rows
for child in row.get("panels", [])
}
assert len(panels) == 18
assert len(visible_query_panels) == 12
assert sum(len(panel.get("targets", [])) for panel in visible_query_panels) == 12
assert all(
panel["title"] != "Coverage Gap to 95% by Suite"
for panel in visible_query_panels
)
assert [row["title"] for row in rows] == [
"CI Runs And Test Result History",
"Check Failure Rates By Suite",
"Check Healthy Rates By Suite",
"Test Drilldowns And Problem Tests",
"Telemetry Completeness And Branches",
"SonarQube Project Health",
]
assert all(row["collapsed"] for row in rows)
assert "Coverage Failure Rate" in nested_panels_by_title
assert "Supply Chain Healthy Rate" in nested_panels_by_title
assert "Test Category Health History" in nested_panels_by_title
assert "Selected Test Pass Rate History" in nested_panels_by_title
assert "Coverage Metrics Present by Suite" in nested_panels_by_title
assert "SonarQube API Up" in nested_panels_by_title
failure_rate_panel = nested_panels_by_title["Coverage Failure Rate"]
assert failure_rate_panel["type"] == "state-timeline"
assert failure_rate_panel["fieldConfig"]["defaults"]["unit"] == "percent"
assert failure_rate_panel["fieldConfig"]["defaults"]["max"] == 100
assert failure_rate_panel["fieldConfig"]["defaults"]["thresholds"]["steps"][0]["color"] == "dark-blue"
assert "increase(" not in failure_rate_panel["targets"][0]["expr"]
assert "platform_quality:check_failed_flag:present_1h" in failure_rate_panel["targets"][0]["expr"]
assert "platform_quality:check_seen_flag:present_1h" in failure_rate_panel["targets"][0]["expr"]
assert "platform_quality:check_status:present_1h" not in failure_rate_panel["targets"][0]["expr"]
assert '.*_quality_gate_checks_total' not in failure_rate_panel["targets"][0]["expr"]
assert "last_over_time" not in failure_rate_panel["targets"][0]["expr"]
assert 'label_replace' not in failure_rate_panel["targets"][0]["expr"]
assert "0 *" in failure_rate_panel["targets"][0]["expr"]
assert "and on(suite)" not in failure_rate_panel["targets"][0]["expr"]
pass_rate_panel = nested_panels_by_title["Selected Test Pass Rate History"]
assert pass_rate_panel["type"] == "state-timeline"
assert "platform_quality:test_case_pass_rate:percent_1h" in pass_rate_panel["targets"][0]["expr"]
assert "platform_quality_gate_test_case_result" not in pass_rate_panel["targets"][0]["expr"]
pass_fail_panel = nested_panels_by_title["Selected Test Pass/Fail History"]
assert pass_fail_panel["fieldConfig"]["defaults"]["custom"]["drawStyle"] == "bars"
assert all(
"platform_quality:test_case_status:count_1h" in target["expr"]
for target in pass_fail_panel["targets"]
)
problematic_panel = nested_panels_by_title["Problematic Tests Over Time (Top failures)"]
assert problematic_panel["type"] == "state-timeline"
assert problematic_panel["gridPos"]["w"] == 24
assert 'test!=""' in problematic_panel["targets"][0]["expr"]
assert "vector(0)" not in problematic_panel["targets"][0]["expr"]
assert problematic_panel["fieldConfig"]["defaults"]["thresholds"]["steps"] == [
{"color": "dark-blue", "value": None},
{"color": "dark-green", "value": 2},
{"color": "dark-yellow", "value": 3},
{"color": "dark-orange", "value": 5},
{"color": "dark-red", "value": 8},
]
assert "rolling 24h failure count" in problematic_panel["description"]
assert "at least two recent failures" in problematic_panel["description"]
sonar_mix_panel = nested_panels_by_title["Sonar Gate Status Mix (Selected)"]
sonar_health_panel = nested_panels_by_title["Sonar Gate Health by Project"]
assert sonar_mix_panel["gridPos"]["w"] == 4
assert sonar_health_panel["gridPos"]["w"] == 8
assert sonar_health_panel["type"] == "state-timeline"
assert "platform_quality:sonar_gate_health_percent:latest_1h" in sonar_health_panel["targets"][0]["expr"]
assert "sonarqube_project_quality_gate_pass" not in sonar_health_panel["targets"][0]["expr"]
branch_panel = nested_panels_by_title["Primary Branch Clean by Suite (7d)"]
recent_branch_panel = nested_panels_by_title["Recent Branch Evidence by Suite (7d)"]
assert branch_panel["gridPos"]["x"] == 12
assert recent_branch_panel["gridPos"]["x"] == 18
assert "[7d:1h]" in recent_branch_panel["targets"][0]["expr"]
assert "[7d:1h]" in branch_panel["targets"][0]["expr"]
assert branch_panel["fieldConfig"]["defaults"]["unit"] == "percent"
assert "unless on(suite)" in branch_panel["targets"][0]["expr"]
assert "> bool 0" in branch_panel["targets"][0]["expr"]
assert branch_panel["targets"][0]["expr"].startswith("sort(")

View File

@ -1,127 +0,0 @@
"""Detailed Atlas Jobs dashboard collapse and drilldown contracts."""
from scripts.tests.test_dashboards_render_atlas import load_module
def test_jobs_dashboard_collapses_heavy_drilldowns_for_light_first_paint():
mod = load_module()
dashboard = mod.build_jobs_dashboard()
panels = dashboard["panels"]
rows = [panel for panel in panels if panel["type"] == "row"]
visible_query_panels = [panel for panel in panels if panel["type"] != "row"]
nested_panels_by_title = {
child["title"]: child for row in rows for child in row.get("panels", [])
}
assert len(panels) == 18
assert len(visible_query_panels) == 12
assert sum(len(panel.get("targets", [])) for panel in visible_query_panels) == 12
assert all(
panel["title"] != "Coverage Gap to 95% by Suite"
for panel in visible_query_panels
)
assert [row["title"] for row in rows] == [
"CI Runs And Test Result History",
"Check Failure Rates By Suite",
"Check Healthy Rates By Suite",
"Test Drilldowns And Problem Tests",
"Telemetry Completeness And Branches",
"SonarQube Project Health",
]
assert all(row["collapsed"] for row in rows)
assert "Coverage Failure Rate" in nested_panels_by_title
assert "Supply Chain Healthy Rate" in nested_panels_by_title
assert "Test Category Health History" in nested_panels_by_title
assert "Selected Test Pass Rate History" in nested_panels_by_title
assert "Coverage Metrics Present by Suite" in nested_panels_by_title
assert "SonarQube API Up" in nested_panels_by_title
failure_rate_panel = nested_panels_by_title["Coverage Failure Rate"]
assert failure_rate_panel["type"] == "state-timeline"
assert failure_rate_panel["fieldConfig"]["defaults"]["unit"] == "percent"
assert failure_rate_panel["fieldConfig"]["defaults"]["max"] == 100
assert (
failure_rate_panel["fieldConfig"]["defaults"]["thresholds"]["steps"][0]["color"]
== "dark-blue"
)
assert "increase(" not in failure_rate_panel["targets"][0]["expr"]
assert (
"platform_quality:check_failed_flag:present_1h"
in failure_rate_panel["targets"][0]["expr"]
)
assert (
"platform_quality:check_seen_flag:present_1h"
in failure_rate_panel["targets"][0]["expr"]
)
assert (
"platform_quality:check_status:present_1h"
not in failure_rate_panel["targets"][0]["expr"]
)
assert (
".*_quality_gate_checks_total" not in failure_rate_panel["targets"][0]["expr"]
)
assert "last_over_time" not in failure_rate_panel["targets"][0]["expr"]
assert "label_replace" not in failure_rate_panel["targets"][0]["expr"]
assert "0 *" in failure_rate_panel["targets"][0]["expr"]
assert "and on(suite)" not in failure_rate_panel["targets"][0]["expr"]
pass_rate_panel = nested_panels_by_title["Selected Test Pass Rate History"]
assert pass_rate_panel["type"] == "state-timeline"
assert (
"platform_quality:test_case_pass_rate:percent_1h"
in pass_rate_panel["targets"][0]["expr"]
)
assert (
"platform_quality_gate_test_case_result"
not in pass_rate_panel["targets"][0]["expr"]
)
pass_fail_panel = nested_panels_by_title["Selected Test Pass/Fail History"]
assert pass_fail_panel["fieldConfig"]["defaults"]["custom"]["drawStyle"] == "bars"
assert all(
"platform_quality:test_case_status:count_1h" in target["expr"]
for target in pass_fail_panel["targets"]
)
problematic_panel = nested_panels_by_title[
"Problematic Tests Over Time (Top failures)"
]
assert problematic_panel["type"] == "state-timeline"
assert problematic_panel["gridPos"]["w"] == 24
assert 'test!=""' in problematic_panel["targets"][0]["expr"]
assert "vector(0)" not in problematic_panel["targets"][0]["expr"]
assert problematic_panel["fieldConfig"]["defaults"]["thresholds"]["steps"] == [
{"color": "dark-blue", "value": None},
{"color": "dark-green", "value": 2},
{"color": "dark-yellow", "value": 3},
{"color": "dark-orange", "value": 5},
{"color": "dark-red", "value": 8},
]
assert "rolling 24h failure count" in problematic_panel["description"]
assert "at least two recent failures" in problematic_panel["description"]
sonar_mix_panel = nested_panels_by_title["Sonar Gate Status Mix (Selected)"]
sonar_health_panel = nested_panels_by_title["Sonar Gate Health by Project"]
assert sonar_mix_panel["gridPos"]["w"] == 4
assert sonar_health_panel["gridPos"]["w"] == 8
assert sonar_health_panel["type"] == "state-timeline"
assert (
"platform_quality:sonar_gate_health_percent:latest_1h"
in sonar_health_panel["targets"][0]["expr"]
)
assert (
"sonarqube_project_quality_gate_pass"
not in sonar_health_panel["targets"][0]["expr"]
)
branch_panel = nested_panels_by_title["Primary Branch Clean by Suite (7d)"]
recent_branch_panel = nested_panels_by_title["Recent Branch Evidence by Suite (7d)"]
assert branch_panel["gridPos"]["x"] == 12
assert recent_branch_panel["gridPos"]["x"] == 18
assert "[7d:1h]" in recent_branch_panel["targets"][0]["expr"]
assert "[7d:1h]" in branch_panel["targets"][0]["expr"]
assert branch_panel["fieldConfig"]["defaults"]["unit"] == "percent"
assert "unless on(suite)" in branch_panel["targets"][0]["expr"]
assert "> bool 0" in branch_panel["targets"][0]["expr"]
assert branch_panel["targets"][0]["expr"].startswith("sort(")

View File

@ -4,17 +4,15 @@
from __future__ import annotations
import argparse
import datetime
import fnmatch
import json
import os
import re
import stat
from pathlib import Path
MAX_INPUT = 1024 * 1024
MAX_RULES = 100
MAX_RULE_NAME = 255
SPECIAL = frozenset("*?\\[]{}")
class PolicyError(RuntimeError):
@ -35,102 +33,35 @@ def _read_bounded(path: Path) -> bytes:
return value
def _glob_regex(pattern: str, position: int = 0, terminators: str = "") -> tuple[str, int]:
"""Compile the gobwas/glob syntax Gitea v1.23.8 uses."""
pieces: list[str] = []
while position < len(pattern):
character = pattern[position]
if character in terminators:
break
if character == "\\":
position += 1
if position >= len(pattern):
raise ValueError("trailing escape")
pieces.append(re.escape(pattern[position]))
elif character == "*":
if position + 1 < len(pattern) and pattern[position + 1] == "*":
pieces.append(".*")
position += 1
else:
pieces.append("[^/]*")
elif character == "?":
pieces.append("[^/]")
elif character == "[":
end = pattern.find("]", position + 1)
if end < 0:
raise ValueError("unterminated range")
value = pattern[position + 1 : end]
if not value:
raise ValueError("empty range")
negate = value.startswith("!")
value = value[1:] if negate else value
if not value or "[" in value or "\\" in value:
raise ValueError("invalid range")
if "-" in value:
if value.count("-") != 1 or value.startswith("-") or value.endswith("-"):
raise ValueError("invalid range")
low, high = value.split("-")
if len(low) != 1 or len(high) != 1 or ord(high) < ord(low):
raise ValueError("invalid range")
content = re.escape(low) + "-" + re.escape(high)
else:
content = re.escape(value)
pieces.append("[" + ("^" if negate else "") + content + "]")
position = end
elif character == "{":
alternatives: list[str] = []
position += 1
while True:
alternative, position = _glob_regex(pattern, position, ",}")
alternatives.append(alternative)
if position >= len(pattern):
raise ValueError("unterminated alternatives")
if pattern[position] == "}":
break
position += 1
if len(alternatives) < 2 or any(not item for item in alternatives):
raise ValueError("invalid alternatives")
pieces.append("(?:" + "|".join(alternatives) + ")")
elif character in "]}":
# A closing delimiter outside its grammar context is literal in
# gobwas/glob's lexer.
pieces.append(re.escape(character))
else:
pieces.append(re.escape(character))
position += 1
return "".join(pieces), position
def _is_plain(pattern: str) -> bool:
return not any(character in SPECIAL for character in pattern)
def _matches(pattern: str, branch: str) -> bool:
if not pattern.isascii() or not 1 <= len(pattern) <= MAX_RULE_NAME:
raise PolicyError("branch protection rule name is invalid")
if _is_plain(pattern):
return pattern.casefold() == branch.casefold()
try:
expression, position = _glob_regex(pattern)
if position != len(pattern):
raise ValueError("incomplete glob")
except (re.error, ValueError):
# Gitea quotes an invalid special pattern and matches it literally.
# A literal containing a special byte cannot equal main or master.
return False
return re.fullmatch(expression, branch) is not None
def _created(value: object) -> datetime.datetime:
if not isinstance(value, str) or len(value) > 64:
raise PolicyError("branch protection creation time is invalid")
try:
result = datetime.datetime.fromisoformat(value.replace("Z", "+00:00"))
except ValueError as exc:
raise PolicyError("branch protection creation time is invalid") from exc
if result.tzinfo is None:
raise PolicyError("branch protection creation time is invalid")
return result
if pattern.count("[") != pattern.count("]") or "\\" in pattern:
raise PolicyError("branch protection glob is ambiguous")
# Gitea matches ordinary names case-insensitively and compiles wildcard
# names as globs with '/' as a separator. Primary branches have no slash,
# so fnmatchcase is equivalent for the only two targets managed here.
patterns = [pattern]
while any("{" in item or "}" in item for item in patterns):
expanded: list[str] = []
for item in patterns:
if "{" not in item and "}" not in item:
expanded.append(item)
continue
start = item.find("{")
end = item.find("}", start + 1)
if start < 0 or end < 0 or "{" in item[start + 1 : end]:
raise PolicyError("branch protection glob is ambiguous")
choices = item[start + 1 : end].split(",")
if len(choices) < 2 or any(not choice for choice in choices):
raise PolicyError("branch protection glob is ambiguous")
expanded.extend(item[:start] + choice + item[end + 1 :] for choice in choices)
patterns = expanded
if len(patterns) > 32:
raise PolicyError("branch protection glob expands beyond the safe limit")
return any(
fnmatch.fnmatchcase(branch.casefold(), item.casefold()) for item in patterns
)
def _required(reviewer: str) -> dict[str, object]:
@ -159,7 +90,8 @@ def evaluate(value: bytes, branch: str, reviewer: str) -> str:
data = json.loads(value)
if not isinstance(data, list) or len(data) > MAX_RULES:
raise PolicyError("branch protection response is invalid")
ordered: list[tuple[int, bool, datetime.datetime, int, dict[str, object]]] = []
ordered: list[tuple[int, int, dict[str, object]]] = []
priorities: set[int] = set()
for index, raw in enumerate(data):
if not isinstance(raw, dict):
raise PolicyError("branch protection entry is invalid")
@ -169,17 +101,16 @@ def evaluate(value: bytes, branch: str, reviewer: str) -> str:
not isinstance(priority, int)
or isinstance(priority, bool)
or not 1 <= priority <= 1_000_000
or priority in priorities
or not isinstance(rule_name, str)
):
raise PolicyError("branch protection priority is ambiguous")
created = _created(raw.get("created_at"))
priorities.add(priority)
if _matches(rule_name, branch):
# Gitea v1.23.8 sorts by priority, then puts plain names before
# globs, then uses creation time. The API exposes every component.
ordered.append((priority, not _is_plain(rule_name), created, index, raw))
ordered.append((priority, index, raw))
if not ordered:
return "ABSENT"
effective = min(ordered)[4]
effective = min(ordered)[2]
for key, expected in _required(reviewer).items():
if effective.get(key) != expected:
raise PolicyError(

View File

@ -1,5 +0,0 @@
# services/hermes-observer-bindings/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- rolebindings.yaml

View File

@ -3,3 +3,4 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- rbac.yaml
- rolebindings.yaml

View File

@ -1,4 +1,4 @@
# services/hermes-observer-bindings/rolebindings.yaml
# services/hermes-observer-rbac/rolebindings.yaml
apiVersion: v1
kind: List
items:

View File

@ -81,7 +81,6 @@ spec:
vault.hashicorp.com/agent-limits-mem: 128Mi
spec:
serviceAccountName: hermes-agent
enableServiceLinks: false
automountServiceAccountToken: true
securityContext:
fsGroup: 10000
@ -101,7 +100,7 @@ spec:
values: ["true"]
- key: kubernetes.io/hostname
operator: NotIn
values: [titan-04, titan-08, titan-13, titan-14, titan-17, titan-18, titan-19]
values: [titan-08, titan-13, titan-14, titan-17, titan-18]
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
@ -681,7 +680,7 @@ spec:
seccompProfile:
type: RuntimeDefault
resources:
requests: {cpu: 300m, memory: 768Mi}
requests: {cpu: 350m, memory: 768Mi}
limits: {cpu: "3", memory: 6Gi}
- name: oauth2-proxy
image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.3@sha256:10a1165743a192e1940b4708fb9647027185ce11a681a1c5519b442ff7f1f561
@ -867,7 +866,7 @@ spec:
- {name: CLAUDE_CONFIG_DIR, value: /runtime-access/claude}
- {name: KUBECONFIG, value: /opt/data/home/.kube/config}
- {name: PYTHONPATH, value: /opt/hermes}
- {name: HERMES_CLI_LANE_CONCURRENCY, value: "2"}
- {name: HERMES_CLI_LANE_CONCURRENCY, value: "4"}
- {name: HERMES_AUTO_ROUTER_PROFILE, value: agent}
- {name: PATH, value: /opt/coordinator:/opt/data/tools/bin:/opt/data/home/.local/bin:/opt/hermes/.venv/bin:/usr/local/bin:/usr/bin:/bin}
securityContext:
@ -892,7 +891,7 @@ spec:
- {name: tmp, mountPath: /tmp}
resources:
requests: {cpu: 100m, memory: 256Mi}
limits: {cpu: "2", memory: 6Gi}
limits: {cpu: "3", memory: 6Gi}
- name: model-steward
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
imagePullPolicy: IfNotPresent

View File

@ -25,7 +25,7 @@ from gitea_api import (
)
from gitea_api_policy import _reject_forbidden, _validate_ref, _validate_repo
from scm_broker_io import spool_response
from scm_broker_server import AbsoluteHeaderDeadlineMixin, BoundedThreadingHTTPServer
from scm_broker_server import BoundedThreadingHTTPServer
BROKER_PORT = 9081
GIT_USER = "hermes-automation"
@ -319,12 +319,15 @@ def _upstream_git_request(
result.close()
class BrokerHandler(AbsoluteHeaderDeadlineMixin, BaseHTTPRequestHandler):
class BrokerHandler(BaseHTTPRequestHandler):
"""Expose only bounded metadata, draft creation, and smart-HTTP Git."""
server_version = "HermesSCMBroker/1"
sys_version = ""
header_deadline_seconds = INBOUND_HEADER_TIMEOUT
def setup(self) -> None:
super().setup()
self.connection.settimeout(INBOUND_HEADER_TIMEOUT)
def _validate_headers(self) -> None:
items = list(self.headers.items())

View File

@ -4,83 +4,9 @@
from __future__ import annotations
import threading
import time
from http.server import ThreadingHTTPServer
MAX_CONCURRENT_REQUESTS = 3
class _AbsoluteDeadlineReader:
"""Read header lines against one wall-clock deadline, not idle timeouts."""
def __init__(self, stream, connection):
self._stream = stream
self._connection = connection
self._deadline: float | None = None
def begin(self, timeout: float) -> None:
self._deadline = time.monotonic() + timeout
def end(self) -> None:
self._deadline = None
def readline(self, limit: int = -1) -> bytes:
if self._deadline is None:
return self._stream.readline(limit)
value = bytearray()
while limit < 0 or len(value) < limit:
remaining = self._deadline - time.monotonic()
if remaining <= 0:
raise TimeoutError("absolute SCM header deadline exceeded")
self._connection.settimeout(remaining)
character = self._stream.read(1)
if not character:
break
value.extend(character)
if character == b"\n":
break
return bytes(value)
def __getattr__(self, name: str):
return getattr(self._stream, name)
class AbsoluteHeaderDeadlineMixin:
"""Apply an absolute request-line plus header deadline to HTTP handlers."""
header_deadline_seconds = 10.0
def setup(self) -> None:
super().setup()
self._header_reader = _AbsoluteDeadlineReader(self.rfile, self.connection)
self.rfile = self._header_reader
def handle_one_request(self) -> None:
try:
self._header_reader.begin(self.header_deadline_seconds)
try:
self.raw_requestline = self.rfile.readline(65537)
if len(self.raw_requestline) > 65536:
self.requestline = ""
self.request_version = ""
self.command = ""
self.send_error(414)
return
if not self.raw_requestline:
self.close_connection = True
return
if not self.parse_request():
return
finally:
self._header_reader.end()
method_name = "do_" + self.command
if not hasattr(self, method_name):
self.send_error(501, "Unsupported method")
return
getattr(self, method_name)()
self.wfile.flush()
except TimeoutError:
self.close_connection = True
MAX_CONCURRENT_REQUESTS = 2
class BoundedThreadingHTTPServer(ThreadingHTTPServer):

View File

@ -3,7 +3,6 @@
from __future__ import annotations
import re
import stat
import struct
from pathlib import Path
@ -81,33 +80,6 @@ def _audit_policy_metadata(snapshot, root_uid: int, account_uid: int) -> None:
raise HardeningError("sudo/polkit policy ACL grants Hermes write access")
def _active_sudo_policy(text: str) -> str:
"""Remove comments without discarding sudoers' numeric-UID principals."""
active: list[str] = []
for line in text.splitlines():
stripped = line.lstrip()
if not stripped:
continue
include = re.fullmatch(
r"(?i)([#@]include|[#@]includedir)\s+([^\s]+)", stripped
)
if include:
directive, target = include.groups()
if directive.lower().endswith("includedir") and target == "/etc/sudoers.d":
continue
raise HardeningError("sudo policy includes an unaudited authority source")
if stripped.startswith("#") and not re.match(r"#\d+(?:\s|$)", stripped):
continue
active.append(line)
return "\n".join(active)
def _mentions_dedicated_identity(text: str, account: str, account_uid: int) -> bool:
account_pattern = rf"(?<![A-Za-z0-9_.-])%?{re.escape(account)}(?![A-Za-z0-9_.-])"
numeric_pattern = rf"(?<![0-9])#?{account_uid}(?![0-9])"
return bool(re.search(account_pattern, text) or re.search(numeric_pattern, text))
def audit_privilege_policies(
account: str,
account_uid: int,
@ -132,18 +104,12 @@ def audit_privilege_policies(
raise HardeningError("nsswitch policy is not UTF-8") from exc
for line in text.splitlines():
name, separator, sources = line.partition(":")
if name.strip() in {
"passwd",
"group",
"initgroups",
"shadow",
"sudoers",
} and separator:
if name.strip() in {"group", "initgroups", "sudoers"} and separator:
active_sources = [item for item in sources.split() if not item.startswith("[")]
if not active_sources or any(
item not in {"files", "systemd"} for item in active_sources
):
raise HardeningError("external group/account/sudo authority source is unsafe")
raise HardeningError("external group/sudo authority source is unsafe")
total = 0
for path in _policy_files(host_etc, polkit_share, root_uid):
snapshot = read_regular(path, 256 * 1024)
@ -156,23 +122,17 @@ def audit_privilege_policies(
text = value.decode("utf-8")
except UnicodeDecodeError as exc:
raise HardeningError("sudo/polkit policy is not UTF-8") from exc
active = "\n".join(
line for line in text.splitlines() if not line.lstrip().startswith("#")
)
if account in active or f"%{account}" in active:
raise HardeningError("dedicated Hermes account has sudo/polkit authority")
if path.name == "sudoers" or "sudoers.d" in path.parts:
active = _active_sudo_policy(text)
if _mentions_dedicated_identity(active, account, account_uid):
raise HardeningError("dedicated Hermes account has sudo authority")
for line in active.splitlines():
fields = line.split()
if fields and fields[0] in {"ALL", "%ALL"} and "=" in line:
raise HardeningError("broad sudo authority includes Hermes")
continue
active = "\n".join(
line for line in text.splitlines() if not line.lstrip().startswith("#")
)
grants = "polkit.Result.YES" in active or re.search(
r"(?im)^\s*Result(?:Any|Inactive|Active)\s*=\s*yes\s*$", active
)
if grants and _mentions_dedicated_identity(active, account, account_uid):
raise HardeningError("dedicated Hermes account has polkit authority")
broad = "unix-user:*" in active or "Identity=unix-user:*" in active
grants = "polkit.Result.YES" in active or "ResultActive=yes" in active
if broad and grants and any(item in active for item in dangerous_polkit):
raise HardeningError("root-equivalent broad polkit authority includes Hermes")

View File

@ -11,7 +11,7 @@ resources:
- networking
images:
- name: registry.bstein.dev/bstein/ariadne
newTag: 0.1.0-461 # {"$imagepolicy": "maintenance:ariadne:tag"}
newTag: 0.1.0-460 # {"$imagepolicy": "maintenance:ariadne:tag"}
- name: registry.bstein.dev/bstein/metis
newTag: 0.1.0-295-arm64 # {"$imagepolicy": "maintenance:metis-arm64:tag"}
- name: registry.bstein.dev/bstein/soteria

View File

@ -1,282 +0,0 @@
"""Image generation and sandbox-boundary contracts for Hermes chat."""
from __future__ import annotations
import importlib.util
import sys
import time
from types import SimpleNamespace
import yaml
from testing.tests.test_hermes_chat_support import (
HERMES,
VAULT,
_documents,
)
def test_chat_image_generation_uses_private_owner_broker():
"""Family pods get image bytes without receiving the owner's OAuth file."""
configmap = _documents(HERMES / "chat-configmap.yaml")[0]
assert "shared desktop/Wolf lane" in configmap["data"]["SOUL.md"]
assert "local FLUX waits" in configmap["data"]["SOUL.md"]
assert "Use `image_generate_local`" in configmap["data"]["SOUL.md"]
assert "Use `image_generate_hosted`" in configmap["data"]["SOUL.md"]
assert "ComfyUI endpoint" in configmap["data"]["SOUL.md"]
assert "`MEDIA:` path" in configmap["data"]["SOUL.md"]
assert "`image_edit_latest`" in configmap["data"]["SOUL.md"]
assert (
"Preserve the most recently selected image lane" in configmap["data"]["SOUL.md"]
)
config = yaml.safe_load(configmap["data"]["config.yaml"])
assert config["image_gen"] == {
"provider": "atlas-broker",
"model": "atlas-image-auto-high",
}
assert config["plugins"]["enabled"] == ["atlas-broker", "auto-router"]
statefulset = _documents(HERMES / "chat-statefulset.yaml")[0]
pod = statefulset["spec"]["template"]["spec"]
hermes = next(item for item in pod["containers"] if item["name"] == "hermes")
mounts = {item["name"]: item for item in hermes["volumeMounts"]}
assert mounts["image-plugin"]["mountPath"] == (
"/opt/hermes/plugins/image_gen/atlas-broker"
)
assert mounts["runtime-access"]["mountPath"] == "/runtime-access"
assert "provider-auth" not in mounts
assert not any(
mount["name"] == "home" and "agent" in str(mount)
for mount in hermes["volumeMounts"]
)
env = {item["name"]: item["value"] for item in hermes["env"]}
assert env["HERMES_IMAGE_BROKER_URL"].startswith("http://hermes-image-broker.")
plugin = (HERMES / "plugins" / "image-gen-broker" / "__init__.py").read_text()
assert '"local": "flux-2-klein-4b-local"' in plugin
assert '"hosted": "gpt-image-2-high"' in plugin
assert 'name="image_generate_local"' in plugin
assert 'name="image_generate_hosted"' in plugin
assert '"name": "image_edit_latest"' in plugin
assert '"name": "image_edit_latest_local"' in plugin
assert '"name": "image_edit_latest_hosted"' in plugin
assert "def _latest_generated_image" in plugin
assert "newest MEDIA: path from the conversation" in plugin
assert 'candidate.upper().startswith("MEDIA:")' in plugin
assert "override=True" not in plugin
agent = _documents(HERMES / "agent-deployment.yaml")[0]
containers = agent["spec"]["template"]["spec"]["containers"]
broker = next(item for item in containers if item["name"] == "image-broker")
assert broker["ports"] == [
{"name": "image-broker", "containerPort": 9002, "protocol": "TCP"}
]
assert broker["securityContext"]["readOnlyRootFilesystem"] is True
assert broker["securityContext"]["runAsNonRoot"] is True
services = _documents(HERMES / "service.yaml")
service = next(
item for item in services if item["metadata"]["name"] == "hermes-image-broker"
)
assert service["spec"]["selector"] == {"app": "hermes-agent"}
oauth_store = _documents(HERMES / "oauth-session-store.yaml")
redis = next(item for item in oauth_store if item["kind"] == "Deployment")
assert redis["spec"]["strategy"]["type"] == "Recreate"
assert "--appendonly" in redis["spec"]["template"]["spec"]["containers"][0]["args"]
policies = _documents(HERMES / "networkpolicy.yaml")
agent_policy = next(
item
for item in policies
if item["metadata"]["name"] == "hermes-agent-isolation"
)
broker_ingress = next(
rule
for rule in agent_policy["spec"]["ingress"]
if {port["port"] for port in rule["ports"]} == {9002, 9003}
)
assert broker_ingress["from"][0]["podSelector"]["matchLabels"] == {
"app": "hermes-chat-tenant"
}
vault_policy = (VAULT / "scripts" / "vault_k8s_auth_configure.sh").read_text()
agent_role = vault_policy[
vault_policy.index('write_policy_and_role "hermes-agent"') : vault_policy.index(
"write_policy_and_role",
vault_policy.index('write_policy_and_role "hermes-agent"') + 1,
)
]
assert "hermes/developer-gitea" not in agent_role
assert 'write_policy_and_role "hermes-scm-broker" "hermes-scm"' in vault_policy
assert (
'write_policy_and_role "hermes-node-ssh" "hermes" '
'"hermes-node-ssh-access"' in vault_policy
)
def test_compact_image_edit_resolves_latest_tenant_artifact(tmp_path, monkeypatch):
"""Follow-up edits resolve the source server-side and keep tool JSON small."""
provider_module = SimpleNamespace(
DEFAULT_ASPECT_RATIO="square",
ImageGenProvider=object,
error_response=lambda **value: value,
normalize_reference_images=lambda value: value,
resolve_aspect_ratio=lambda value: value,
save_b64_image=lambda *_args, **_kwargs: tmp_path / "saved.png",
success_response=lambda **value: value,
)
monkeypatch.setitem(sys.modules, "agent", SimpleNamespace())
monkeypatch.setitem(sys.modules, "agent.image_gen_provider", provider_module)
spec = importlib.util.spec_from_file_location(
"hermes_image_plugin",
HERMES / "plugins" / "image-gen-broker" / "__init__.py",
)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
image_dir = tmp_path / "cache" / "images"
image_dir.mkdir(parents=True)
older = image_dir / "atlas_flux-old.png"
newest = image_dir / "atlas_gpt-image-new.png"
older.write_bytes(b"older")
newest.write_bytes(b"newest")
older.touch()
time.sleep(0.001)
newest.touch()
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
calls = []
monkeypatch.setattr(
module,
"_handle_image_generate",
lambda args, route: calls.append((args, route)) or "ok",
)
assert module._handle_hosted_edit({"prompt": "make it a clown"}) == "ok"
assert calls == [
(
{
"prompt": "make it a clown",
"image_url": str(newest.resolve()),
},
"hosted",
)
]
def test_chat_reasoning_uses_switchyard_without_owner_credentials():
"""Family pods use AUTO/manual routes without mounting owner credentials."""
configmap = _documents(HERMES / "chat-configmap.yaml")[0]
config = yaml.safe_load(configmap["data"]["config.yaml"])
assert config["model"] == {
"provider": "atlas-switchyard",
"default": "atlas/auto/fast",
"model": "atlas/auto/fast",
}
assert config["providers"]["atlas-switchyard"] == {
"name": "Automatic Router",
"api": "http://hermes-switchyard.hermes.svc.cluster.local:9005/v1",
"api_key": "atlas-switchyard",
"default_model": "atlas/auto/fast",
"transport": "chat_completions",
}
assert config["platforms"]["api_server"]["extra"]["model_routes"] == {
route: {"provider": "atlas-switchyard", "model": route}
for route in [
"atlas/auto/fast",
"atlas/auto/balanced",
"atlas/auto/deep",
"atlas/auto/maximum",
"atlas/manual/codex/luna",
"atlas/manual/codex/terra",
"atlas/manual/codex/sol",
"atlas/manual/claude/haiku",
"atlas/manual/claude/fable",
"atlas/manual/claude/sonnet",
"atlas/manual/claude/opus",
"atlas/manual/local/qwen-14b",
]
}
agent = _documents(HERMES / "agent-deployment.yaml")[0]
containers = agent["spec"]["template"]["spec"]["containers"]
broker = next(item for item in containers if item["name"] == "codex-broker")
assert broker["ports"] == [
{"name": "codex-broker", "containerPort": 9003, "protocol": "TCP"}
]
assert broker["securityContext"]["readOnlyRootFilesystem"] is True
assert broker["securityContext"]["runAsNonRoot"] is True
assert {item["name"]: item["value"] for item in broker["env"]}.items() >= {
"PYTHONPATH": "/opt/hermes",
"HERMES_CODEX_BROKER_LISTEN_PORT": "9003",
"HERMES_ROUTING_CATALOG_PATH": "/routing-catalog/catalog.json",
}.items()
services = _documents(HERMES / "service.yaml")
service = next(
item for item in services if item["metadata"]["name"] == "hermes-codex-broker"
)
assert service["spec"]["selector"] == {"app": "hermes-agent"}
assert service["spec"]["ports"] == [
{
"name": "http",
"port": 9003,
"targetPort": "codex-broker",
"protocol": "TCP",
}
]
statefulset = _documents(HERMES / "chat-statefulset.yaml")[0]
assert (
statefulset["spec"]["template"]["metadata"]["annotations"][
"ai.bstein.dev/config-rev"
]
== "20260816-telegram-topics"
)
pod_spec = statefulset["spec"]["template"]["spec"]
patch_init = next(
item
for item in pod_spec["initContainers"]
if item["name"] == "patch-stream-recovery"
)
assert patch_init["command"][-2:] == [
"/opt/hermes/agent/conversation_loop.py",
"/patched/conversation_loop.py",
]
hermes = next(item for item in pod_spec["containers"] if item["name"] == "hermes")
assert {
"name": "stream-recovery-patch",
"mountPath": "/opt/hermes/agent/conversation_loop.py",
"subPath": "conversation_loop.py",
} in hermes["volumeMounts"]
api_session_init = next(
item
for item in pod_spec["initContainers"]
if item["name"] == "patch-api-server-sessions"
)
assert "patch_api_server_sessions.py" in api_session_init["args"][0]
assert "migrate_telegram_api_sessions.py" in api_session_init["args"][0]
assert {
"name": "api-server-patch",
"mountPath": "/opt/hermes/gateway/platforms/api_server.py",
"subPath": "api_server.py",
} in hermes["volumeMounts"]
assert any(volume["name"] == "api-server-patch" for volume in pod_spec["volumes"])
assert not any(
mount["mountPath"].endswith("/.codex") for mount in hermes["volumeMounts"]
)
policies = _documents(HERMES / "networkpolicy.yaml")
agent_policy = next(
item
for item in policies
if item["metadata"]["name"] == "hermes-agent-isolation"
)
broker_ingress = next(
rule
for rule in agent_policy["spec"]["ingress"]
if {port["port"] for port in rule["ports"]} == {9002, 9003}
)
assert broker_ingress["from"][0]["podSelector"]["matchLabels"] == {
"app": "hermes-chat-tenant"
}

View File

@ -1,172 +0,0 @@
"""Local inference and sandbox execution contracts for Hermes chat."""
from __future__ import annotations
import importlib.util
import json
from pathlib import Path
from types import SimpleNamespace
from testing.tests.test_hermes_chat_support import (
HERMES,
ROOT,
_documents,
)
def test_titan20_serializes_classifier_and_local_chat_model_residency():
"""Classifier and local chat share one serialized resident Qwen weight."""
deployment = _documents(
Path(__file__).parents[2] / "services/ai-llm/deployment.yaml"
)[0]
pod = deployment["spec"]["template"]["spec"]
required = pod["affinity"]["nodeAffinity"][
"requiredDuringSchedulingIgnoredDuringExecution"
]["nodeSelectorTerms"][0]["matchExpressions"][0]
assert required["values"] == ["titan-20"]
container = pod["containers"][0]
env = {item["name"]: item["value"] for item in container["env"]}
assert env["OLLAMA_MAX_LOADED_MODELS"] == "1"
assert env["OLLAMA_NUM_PARALLEL"] == "1"
assert env["OLLAMA_KEEP_ALIVE"] == "-1"
assert env["OLLAMA_CONTEXT_LENGTH"] == "8192"
warm_command = " ".join(container["command"])
assert "--keepalive=-1" not in warm_command
models = next(item for item in pod["volumes"] if item["name"] == "models")
assert models["persistentVolumeClaim"]["claimName"] == ("ollama-models-titan20")
def test_local_image_gpu_guard_distinguishes_background_and_saturated_gpu(monkeypatch):
"""Lease-idle desktop spikes may coexist, but saturation still blocks FLUX."""
source = ROOT / "dockerfiles" / "hermes-local-image-server.py"
spec = importlib.util.spec_from_file_location("hermes_local_image_server", source)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
monkeypatch.setattr(module, "GPU_ACTIVITY_NODE", "titan-24")
monkeypatch.setattr(module, "GPU_ACTIVE_SM_PERCENT", 80.0)
monkeypatch.setattr(module, "GPU_MAX_EXTERNAL_MEMORY_BYTES", 3 << 30)
idle = module._parse_gpu_activity(
"\n".join(
[
'nvidia_process_gpu_sm_util_percent{node="titan-24",namespace="host",process="Xorg"} 0',
'nvidia_process_gpu_memory_used_bytes{node="titan-24",namespace="host",process="Xorg"} 1900000000',
'nvidia_process_gpu_sm_util_percent{node="titan-24",namespace="game-stream",process="wolf"} 3',
'nvidia_process_gpu_memory_used_bytes{node="titan-24",namespace="hermes",process="python"} 9000000000',
]
)
)
assert idle["interactive_active"] is False
assert idle["external_gpu_memory_bytes"] == 1900000000
assert idle["external_gpu_sm_percent"] == 3
background_spike = module._parse_gpu_activity(
'nvidia_process_gpu_sm_util_percent{node="titan-24",namespace="host",process="sway"} 41\n'
)
assert background_spike["interactive_active"] is False
active = module._parse_gpu_activity(
'nvidia_process_gpu_sm_util_percent{node="titan-24",namespace="host",process="steam"} 91\n'
)
assert active["interactive_active"] is True
assert "91%" in active["gpu_guard_reason"]
def test_local_flux_renderer_uses_a_disposable_cuda_worker(monkeypatch):
"""A completed render must not leave its CUDA context in the API process."""
source = ROOT / "dockerfiles" / "hermes-local-image-server.py"
spec = importlib.util.spec_from_file_location("hermes_local_image_worker", source)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
calls = []
def run(command, **kwargs):
calls.append((command, kwargs))
return SimpleNamespace(
returncode=0,
stdout=b'{"success":true,"route":"local","image_b64":"cG5n"}',
stderr=b"",
)
monkeypatch.setattr(module.subprocess, "run", run)
result = module._render({"prompt": "black cat", "aspect_ratio": "square"})
assert result["route"] == "local"
command, options = calls[0]
assert command[-1] == "--render-worker"
assert json.loads(options["input"]) == {
"prompt": "black cat",
"aspect_ratio": "square",
}
assert options["timeout"] == module.RENDER_TIMEOUT_SECONDS
assert options["check"] is False
def test_local_flux_uses_low_vram_offload_without_reducing_resolution():
"""The shared 3080 lane must trade time, not image size, for headroom."""
source = (ROOT / "dockerfiles" / "hermes-local-image-server.py").read_text()
assert "OFFLOAD_MODE = os.environ.get(" in source
assert '"HERMES_LOCAL_IMAGE_OFFLOAD_MODE", "sequential"' in source
assert "pipe.enable_sequential_cpu_offload()" in source
assert '"square": (1024, 1024)' in source
def test_chat_auth_and_relay_are_pod_lifetime_only():
statefulset = _documents(HERMES / "chat-statefulset.yaml")[0]
pod = statefulset["spec"]["template"]["spec"]
containers = statefulset["spec"]["template"]["spec"]["containers"]
for name in ("hermes", "webui"):
container = next(item for item in containers if item["name"] == name)
env = {item["name"]: item["value"] for item in container["env"]}
assert env["HERMES_AUTH_FILE"] == "/runtime-access/hermes-auth.json"
mount = next(
item
for item in container["volumeMounts"]
if item["name"] == "runtime-access"
)
assert mount["mountPath"] == "/runtime-access"
assert "subPath" not in mount
hermes_env = {
item["name"]: item["value"]
for item in next(item for item in containers if item["name"] == "hermes")["env"]
}
runtime = next(item for item in pod["volumes"] if item["name"] == "runtime-access")
assert runtime["emptyDir"] == {"medium": "Memory", "sizeLimit": "2Mi"}
assert not any(item["name"] == "provider-auth" for item in pod["volumes"])
init_command = next(
item for item in pod["initContainers"] if item["name"] == "init-config"
)["command"][2]
for key in (
"ANTHROPIC_API_KEY",
"API_SERVER_KEY",
"CLAUDE_CODE_OAUTH_TOKEN",
"GITEA_TOKEN",
"HERMES_IMAGE_BROKER_KEY",
"OPENAI_API_KEY",
):
assert key in init_command
assert "printf 'API_SERVER_KEY=%s" not in init_command
assert hermes_env["AGENT_BROWSER_EXECUTABLE_PATH"].endswith(
"/chrome-linux/headless_shell"
)
assert "--no-sandbox" in hermes_env["AGENT_BROWSER_ARGS"]
def test_sandbox_executes_python_with_bounded_output(tmp_path: Path, monkeypatch):
source = ROOT / "dockerfiles" / "hermes-chat-sandbox-server.py"
spec = importlib.util.spec_from_file_location("hermes_chat_sandbox_server", source)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
monkeypatch.setattr(module, "WORKSPACE", tmp_path)
result = module._execute("import math\nprint(math.comb(10, 3))")
assert result["success"] is True
assert result["stdout"] == "120\n"
assert result["stderr"] == ""

View File

@ -1,476 +0,0 @@
"""Provider subscription and OAuth contracts for Hermes chat."""
from __future__ import annotations
import base64
import json
import sys
import time
from pathlib import Path
from types import ModuleType
import pytest
from testing.tests.test_hermes_chat_support import (
_load_broker_module,
)
def test_codex_broker_auth_and_request_contract(tmp_path: Path, monkeypatch):
"""The relay is bounded, stateless, and rejects unapproved models."""
module = _load_broker_module("hermes_codex_broker", "codex_broker.py", monkeypatch)
monkeypatch.setattr(module, "TOKEN", "relay-secret")
assert module._authorized("Bearer relay-secret") is True
assert module._authorized("Bearer wrong") is False
assert module._real_model("route/codex/gpt-5.6-sol/xhigh") == "gpt-5.6-sol"
payload = module._validate_payload(
{
"model": "gpt-5.6-terra",
"input": "route this chat turn",
"store": True,
"stream": False,
"max_output_tokens": 96,
"max_completion_tokens": 96,
"max_tokens": 96,
"temperature": 0.7,
"top_p": 0.9,
}
)
assert payload["store"] is False
assert payload["stream"] is True
assert "max_output_tokens" not in payload
assert "max_completion_tokens" not in payload
assert "max_tokens" not in payload
assert "temperature" not in payload
assert "top_p" not in payload
assert payload["input"] == [
{
"type": "message",
"role": "user",
"content": [{"type": "input_text", "text": "route this chat turn"}],
}
]
response_item = {
"type": "message",
"role": "user",
"content": [{"type": "input_text", "text": "keep this item"}],
}
assert module._validate_payload({"model": "gpt-5.6-terra", "input": response_item})[
"input"
] == [response_item]
response_items = [response_item]
assert (
module._validate_payload({"model": "gpt-5.6-terra", "input": response_items})[
"input"
]
is response_items
)
image_items = [
{
"type": "message",
"role": "user",
"content": [
{"type": "input_text", "text": "What color is this?"},
{
"type": "image_url",
"image_url": {
"url": "data:image/png;base64,cHJpdmF0ZQ==",
"detail": "high",
},
},
],
}
]
assert module._validate_payload({"model": "gpt-5.6-terra", "input": image_items})[
"input"
][0]["content"][1] == {
"type": "input_image",
"image_url": "data:image/png;base64,cHJpdmF0ZQ==",
"detail": "high",
}
switchyard_image_items = [
{
"role": "user",
"content": [
{"type": "input_text", "text": "What color is this?"},
{
"type": "image_url",
"image_url": {
"url": "data:image/png;base64,cHJpdmF0ZQ==",
"detail": "auto",
},
},
],
}
]
assert module._validate_payload(
{"model": "gpt-5.6-terra", "input": switchyard_image_items}
)["input"][0]["content"][1] == {
"type": "input_image",
"image_url": "data:image/png;base64,cHJpdmF0ZQ==",
"detail": "auto",
}
switchyard_base64_items = [
{
"role": "user",
"content": [
{"type": "input_text", "text": "What color is this?"},
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": "cHJpdmF0ZQ==",
},
},
],
}
]
normalized_base64 = module._validate_payload(
{"model": "gpt-5.6-terra", "input": switchyard_base64_items}
)["input"][0]["content"][1]
assert normalized_base64 == {
"type": "input_image",
"image_url": "data:image/png;base64,cHJpdmF0ZQ==",
}
switchyard_enum_items = [
{
"role": "user",
"content": [
{
"type": "input_image",
"image_url": {
"type": "url",
"data": {
"url": "data:image/png;base64,cHJpdmF0ZQ==",
"detail": "high",
},
},
}
],
}
]
nested_image = module._validate_payload(
{"model": "gpt-5.6-terra", "input": switchyard_enum_items}
)["input"][0]["content"][0]
assert nested_image["image_url"] == "data:image/png;base64,cHJpdmF0ZQ=="
assert nested_image["detail"] == "high"
with pytest.raises(ValueError, match=r"non-empty Responses image URL.*str\[4\]"):
module._validate_payload(
{
"model": "gpt-5.6-terra",
"input": [
{
"type": "message",
"role": "user",
"content": [
{
"type": "input_image",
"image_url": {"detail": "high"},
}
],
}
],
}
)
routed = module._validate_payload(
{
"model": "route/codex/gpt-5.6-luna/low",
"input": "use the low route",
"stream": False,
}
)
assert routed["model"] == "gpt-5.6-luna"
with pytest.raises(ValueError, match="unsupported Codex model"):
module._validate_payload({"model": "unapproved-model", "input": "hello"})
with pytest.raises(ValueError, match="non-empty Responses input"):
module._validate_payload({"model": "gpt-5.6-terra", "input": ""})
with pytest.raises(ValueError, match="non-empty Responses input list"):
module._validate_payload({"model": "gpt-5.6-terra", "input": []})
completed = {
"id": "resp_test",
"object": "response",
"status": "completed",
"output": [],
}
completed_item = {
"type": "message",
"role": "assistant",
"status": "completed",
"content": [{"type": "output_text", "text": "done"}],
}
assert module._completed_response(
[
"event: response.created",
'data: {"type":"response.created","response":{}}',
"event: response.output_item.done",
"data: "
+ json.dumps(
{
"type": "response.output_item.done",
"output_index": 0,
"item": completed_item,
}
),
"event: response.completed",
"data: "
+ json.dumps({"type": "response.completed", "response": completed}),
"data: [DONE]",
]
)["output"] == [completed_item]
raw_stream = (
"event: response.output_item.done\n"
"data: "
+ json.dumps(
{
"type": "response.output_item.done",
"output_index": 0,
"item": completed_item,
}
)
+ "\n\nevent: response.completed\ndata: "
+ json.dumps({"type": "response.completed", "response": completed})
+ "\n\n"
).encode()
normalized = module._normalized_stream(
raw_stream, {**completed, "output": [completed_item]}
).decode()
terminal_data = next(
line for line in normalized.splitlines() if '"response.completed"' in line
)
assert json.loads(terminal_data.removeprefix("data: "))["response"]["output"] == [
completed_item
]
assert normalized.endswith("\n\n")
streamed_function_item = {
"type": "function_call",
"name": "read_file",
"status": "completed",
"arguments": '{"path":"/tmp"}',
}
streamed_function_body = (
"event: response.function_call_arguments.delta\n"
'data: {"type":"response.function_call_arguments.delta",'
'"item_id":"call_1","delta":"{\\"path\\":\\"/tmp\\"}"}\n\n'
"event: response.function_call_arguments.done\n"
'data: {"type":"response.function_call_arguments.done",'
'"item_id":"call_1","arguments":"{\\"path\\":\\"/tmp\\"}"}\n\n'
"event: response.output_item.done\n"
'data: {"type":"response.output_item.done","output_index":0,'
'"item":{"type":"function_call","name":"read_file",'
'"arguments":"{\\"path\\":\\"/tmp\\"}"}}\n\n'
"event: response.completed\n"
"data: "
+ json.dumps(
{
"type": "response.completed",
"response": {**completed, "output": [streamed_function_item]},
}
)
+ "\n\n"
).encode()
normalized_function_stream = module._normalized_stream(
streamed_function_body, {**completed, "output": [streamed_function_item]}
).decode()
assert "response.function_call_arguments.delta" in normalized_function_stream
assert "response.function_call_arguments.done" not in normalized_function_stream
assert "response.output_item.done" not in normalized_function_stream
normalized_terminal = next(
line
for line in normalized_function_stream.splitlines()
if '"response.completed"' in line
)
assert (
json.loads(normalized_terminal.removeprefix("data: "))["response"]["output"]
== []
)
with pytest.raises(RuntimeError, match="retryable incomplete response"):
module._completed_response(
[
"event: response.incomplete",
'data: {"type":"response.incomplete","response":'
'{"status":"incomplete","incomplete_details":'
'{"reason":"max_output_tokens"}}}',
]
)
with pytest.raises(RuntimeError, match="provider unavailable"):
module._completed_response(
[
"event: error",
'data: {"type":"error","error":{"message":"provider unavailable"}}',
]
)
malformed_tool_item = {
"type": "function_call",
"name": "search_files",
"status": "completed",
"arguments": '{"path":"","offset":',
}
with pytest.raises(RuntimeError, match="malformed function arguments"):
module._completed_response(
[
"event: response.output_item.done",
"data: "
+ json.dumps(
{
"type": "response.output_item.done",
"output_index": 0,
"item": malformed_tool_item,
}
),
"event: response.completed",
"data: "
+ json.dumps({"type": "response.completed", "response": completed}),
]
)
valid_tool_item = {
**malformed_tool_item,
"arguments": '{"path":"","offset":0}',
}
assert module._completed_response(
[
"event: response.output_item.done",
"data: "
+ json.dumps(
{
"type": "response.output_item.done",
"output_index": 0,
"item": valid_tool_item,
}
),
"event: response.completed",
"data: "
+ json.dumps({"type": "response.completed", "response": completed}),
]
)["output"] == [valid_tool_item]
with pytest.raises(RuntimeError, match="malformed function arguments"):
module._completed_response(
[
"event: response.function_call_arguments.delta",
'data: {"type":"response.function_call_arguments.delta",'
'"item_id":"call_1","output_index":0,'
'"delta":"{\\"path\\":\\"/tmp\\",\\"offset\\":"}',
"event: response.completed",
"data: "
+ json.dumps({"type": "response.completed", "response": completed}),
]
)
streamed_tool = module._completed_response(
[
"event: response.function_call_arguments.delta",
'data: {"type":"response.function_call_arguments.delta",'
'"item_id":"call_2","output_index":0,'
'"delta":"{\\"path\\":\\"/tmp\\",\\"offset\\":"}',
"event: response.function_call_arguments.done",
'data: {"type":"response.function_call_arguments.done",'
'"item_id":"call_2","output_index":0,'
'"arguments":"{\\"path\\":\\"/tmp\\",\\"offset\\":0}"}',
"event: response.completed",
"data: "
+ json.dumps({"type": "response.completed", "response": completed}),
]
)
assert streamed_tool["status"] == "completed"
auth_dir = tmp_path / ".codex"
auth_dir.mkdir()
# The token payload need only prove the broker reads CODEX_HOME directly.
encoded = (
base64.urlsafe_b64encode(json.dumps({"exp": time.time() + 3600}).encode())
.decode()
.rstrip("=")
)
(auth_dir / "auth.json").write_text(
json.dumps({"tokens": {"access_token": f"header.{encoded}.signature"}})
)
monkeypatch.setenv("CODEX_HOME", str(auth_dir))
assert module._access_token().startswith("header.")
def test_codex_broker_refreshes_and_persists_first_party_oauth(
tmp_path: Path, monkeypatch
):
"""Expired ChatGPT OAuth refreshes in the canonical Codex CLI store."""
module = _load_broker_module(
"hermes_codex_refresh_broker", "codex_broker.py", monkeypatch
)
auth_dir = tmp_path / ".codex"
auth_dir.mkdir()
def jwt(expires_at: float) -> str:
payload = (
base64.urlsafe_b64encode(json.dumps({"exp": expires_at}).encode())
.decode()
.rstrip("=")
)
return f"header.{payload}.signature"
expired = jwt(time.time() - 60)
live = jwt(time.time() + 3600)
auth_path = auth_dir / "auth.json"
auth_path.write_text(
json.dumps(
{
"auth_mode": "chatgpt",
"tokens": {
"access_token": expired,
"refresh_token": "refresh-old",
},
}
)
)
calls = []
auth_module = ModuleType("hermes_cli.auth")
def refresh(access_token, refresh_token, *, timeout_seconds):
calls.append((access_token, refresh_token, timeout_seconds))
return {
"access_token": live,
"refresh_token": "refresh-new",
"last_refresh": "2026-08-12T20:00:00Z",
}
auth_module.refresh_codex_oauth_pure = refresh
package = ModuleType("hermes_cli")
package.auth = auth_module
monkeypatch.setitem(sys.modules, "hermes_cli", package)
monkeypatch.setitem(sys.modules, "hermes_cli.auth", auth_module)
monkeypatch.setenv("CODEX_HOME", str(auth_dir))
assert module._access_token() == live
persisted = json.loads(auth_path.read_text())
assert persisted["tokens"]["access_token"] == live
assert persisted["tokens"]["refresh_token"] == "refresh-new"
assert persisted["last_refresh"] == "2026-08-12T20:00:00Z"
assert calls == [(expired, "refresh-old", 30.0)]
assert auth_path.stat().st_mode & 0o777 == 0o600
# A healthy token is reused, so repeated routed turns do not spend a
# refresh token or create a second billing/authentication path.
assert module._access_token() == live
assert len(calls) == 1
def test_claude_broker_uses_native_subscription_without_api_billing(monkeypatch):
"""Claude traffic must use the native first-party CLI subscription lane."""
module = _load_broker_module(
"hermes_claude_broker", "claude_oauth_broker.py", monkeypatch
)
monkeypatch.setenv("ANTHROPIC_API_KEY", "must-not-leak")
monkeypatch.setenv("CLAUDE_API_KEY", "must-not-leak")
monkeypatch.setattr(
module,
"resolve_route",
lambda route: "claude-fable-5" if "/fable/" in route else route,
)
model, effort = module._route(
"route/claude/fable/xhigh", {"output_config": {"effort": "xhigh"}}
)
assert (model, effort) == ("claude-fable-5", "xhigh")
assert "ANTHROPIC_API_KEY" not in module._claude_environment()
assert "CLAUDE_API_KEY" not in module._claude_environment()
assert module.CAPACITY_PATTERN.search("weekly usage limit exhausted")

File diff suppressed because it is too large Load Diff

View File

@ -1,486 +0,0 @@
"""Routing and runtime workload contracts for Hermes chat."""
from __future__ import annotations
import importlib.util
import json
import sqlite3
import sys
import tomllib
from pathlib import Path
from types import SimpleNamespace
from testing.tests.test_hermes_chat_support import (
HERMES,
ROOT,
_documents,
)
def test_stale_parent_linked_api_workers_close_on_startup(tmp_path: Path):
"""A previous gateway lifetime cannot leave phantom active workers."""
module_path = HERMES / "scripts" / "migrate_api_session_lineage.py"
spec = importlib.util.spec_from_file_location(
"close_stale_api_workers", module_path
)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
database = tmp_path / "state.db"
with sqlite3.connect(database) as connection:
connection.execute(
"CREATE TABLE sessions (id TEXT PRIMARY KEY, source TEXT, "
"parent_session_id TEXT, title TEXT, started_at REAL, ended_at REAL, "
"end_reason TEXT, archived INTEGER DEFAULT 0)"
)
connection.executemany(
"INSERT INTO sessions "
"(id, source, parent_session_id, started_at, ended_at, end_reason) "
"VALUES (?, ?, ?, 1, ?, ?)",
(
("stale", "api_server", "parent", None, None),
("root", "api_server", None, None, None),
("finished", "api_server", "parent", 2.0, "api_run_completed"),
("interactive", "tui", "parent", None, None),
),
)
assert module.migrate(database) == 1
assert module.migrate(database) == 0
with sqlite3.connect(database) as connection:
rows = connection.execute(
"SELECT id, ended_at, end_reason FROM sessions ORDER BY id"
).fetchall()
by_id = {row[0]: row[1:] for row in rows}
assert by_id["stale"][0] is not None
assert by_id["stale"][1] == "api_run_recovered_stale"
assert by_id["root"] == (None, None)
assert by_id["finished"] == (2.0, "api_run_completed")
assert by_id["interactive"] == (None, None)
def test_switchyard_brokers_and_native_claude_lane_use_the_right_images():
"""Thin brokers stay small while native Claude runs beside owner auth."""
dockerfile = (
ROOT / "dockerfiles" / "Dockerfile.hermes-switchyard-brokers"
).read_text()
assert "httpx==0.28.1" in dockerfile
assert "worker_route_broker.py" in dockerfile
assert "routing_catalog.py" in dockerfile
deployment = _documents(HERMES / "switchyard-deployment.yaml")[0]
containers = {
container["name"]: container
for container in deployment["spec"]["template"]["spec"]["containers"]
}
expected = (
"registry.bstein.dev/bstein/hermes-switchyard-brokers@"
"sha256:ee7e95e060ef8083da505162d7e9030daba15fdd828cc047bbcbe6aa409d2083"
)
assert containers["worker-route-broker"]["image"] == expected
assert containers["classifier-broker"]["image"] == expected
assert "claude-oauth-broker" not in containers
agent = _documents(HERMES / "agent-deployment.yaml")[0]
agent_containers = {
container["name"]: container
for container in agent["spec"]["template"]["spec"]["containers"]
}
for container_name in ("hermes", "terminal"):
container = agent_containers[container_name]
environment = {item["name"]: item["value"] for item in container["env"]}
mounts = {item["name"]: item for item in container["volumeMounts"]}
assert (
environment["HERMES_ROUTING_CATALOG_PATH"]
== "/routing-catalog/catalog.json"
)
assert (
environment["HERMES_CODEX_HEALTH_PATH"]
== "/opt/data/provider-health/codex.json"
)
assert (
environment["HERMES_CLAUDE_HEALTH_PATH"]
== "/opt/data/provider-health/claude.json"
)
assert mounts["routing-catalog"]["mountPath"] == "/routing-catalog"
assert mounts["routing-catalog"]["readOnly"] is True
codex = agent_containers["codex-broker"]
codex_environment = {item["name"]: item["value"] for item in codex["env"]}
assert (
codex_environment["HERMES_CODEX_HEALTH_PATH"]
== "/opt/data/provider-health/codex.json"
)
claude = agent_containers["claude-broker"]
assert claude["image"].startswith("registry.bstein.dev/bstein/hermes-agent@")
assert "unset ANTHROPIC_API_KEY CLAUDE_API_KEY" in claude["args"][0]
assert any(
mount["name"] == "home" and mount["mountPath"] == "/opt/data"
for mount in claude["volumeMounts"]
)
def test_classifier_broker_bounds_history_without_losing_routing_intent(monkeypatch):
"""AUTO classification must fit the Jetson context without losing intent."""
broker_path = HERMES / "scripts" / "classifier_broker.py"
monkeypatch.setitem(sys.modules, "httpx", SimpleNamespace())
spec = importlib.util.spec_from_file_location(
"hermes_classifier_broker", broker_path
)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
payload = {
"model": "qwen2.5:14b-instruct-q4_0",
"messages": [
{
"role": "system",
"content": "routing contract\n" + ("candidate policy " * 1000),
},
{
"role": "user",
"content": "Build and verify the Cassandra release safely.",
},
{
"role": "assistant",
"content": None,
"tool_calls": [
{
"id": "call_large",
"type": "function",
"function": {
"name": "large_tool",
"arguments": '{"command":"' + ("x" * 5000) + '"}',
},
}
],
},
{
"role": "tool",
"content": "unbounded test output " * 10000,
"tool_call_id": "call_large",
},
{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {"url": "data:image/png;base64,private"},
},
{"type": "text", "text": "Turn this cat into a cute clown."},
],
},
],
"tools": [{"type": "function", "function": {"name": "large_tool"}}],
"tool_choice": "auto",
"parallel_tool_calls": True,
"response_format": {"type": "json_object"},
}
compacted = module.compact_payload(payload)
encoded = json.dumps(compacted)
assert compacted["model"] == payload["model"]
assert compacted["response_format"] == payload["response_format"]
assert "tools" not in compacted
assert "tool_choice" not in compacted
assert "parallel_tool_calls" not in compacted
assert all(message.get("role") != "tool" for message in compacted["messages"])
assert all("tool_call_id" not in message for message in compacted["messages"])
assert all("tool_calls" not in message for message in compacted["messages"])
assert "[tool evidence]" in encoded
assert "[assistant requested an external tool]" in encoded
assert "Build and verify the Cassandra release safely." in encoded
assert "Turn this cat into a cute clown." in encoded
assert "image attachment available to the selected worker" in encoded
assert "data:image/png;base64" not in encoded
assert "unbounded test output " * 100 not in encoded
assert len(encoded) < 18_000
def test_switchyard_classifier_is_bounded_and_fails_open_once():
"""A sick local judge must not hold chat through repeated long retries."""
config = tomllib.loads(
_documents(HERMES / "switchyard-configmap.yaml")[0]["data"]["routes.toml"]
)
classifier = config["llm_clients"]["classifier"]
assert classifier["base_url"] == "http://127.0.0.1:9008/v1"
assert classifier["max_retries"] == 0
for route in ("auto_fast", "auto_balanced"):
assert config["routes"][route]["recent_turn_window"] == 4
for route in ("auto_deep", "auto_maximum", "worker_auto_maximum"):
assert config["routes"][route]["recent_turn_window"] == 6
deployment = _documents(HERMES / "switchyard-deployment.yaml")[0]
containers = {
item["name"]: item
for item in deployment["spec"]["template"]["spec"]["containers"]
}
classifier_container = containers["classifier-broker"]
env = {item["name"]: item["value"] for item in classifier_container["env"]}
assert env["HERMES_CLASSIFIER_BROKER_READ_TIMEOUT"] == "60"
assert classifier_container["readinessProbe"]["httpGet"]["port"] == "classifier"
def test_image_broker_returns_bytes_and_removes_owner_cache(
tmp_path: Path, monkeypatch
):
"""The broker must not retain a family user's generated image."""
broker_path = HERMES / "scripts" / "image_broker.py"
spec = importlib.util.spec_from_file_location("hermes_image_broker", broker_path)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
generated = tmp_path / "generated.png"
generated.write_bytes(b"\x89PNG\r\n\x1a\nprivate-image")
class Provider:
def generate(self, prompt, aspect, **kwargs):
assert prompt == "paint a blue sphere"
assert aspect == "square"
return {
"success": True,
"image": str(generated),
"model": "gpt-image-2-high",
"quality": "high",
}
monkeypatch.setattr(module, "_PROVIDER", Provider())
result = module._generate(
{
"prompt": "paint a blue sphere",
"aspect_ratio": "square",
"model": "gpt-image-2-high",
}
)
assert result["success"] is True
assert result["image_b64"]
assert "image" not in result
assert not generated.exists()
def test_image_broker_auto_falls_back_to_local_and_honors_explicit_routes(
monkeypatch,
):
"""AUTO is hosted-first while explicit local never calls the hosted lane."""
broker_path = HERMES / "scripts" / "image_broker.py"
spec = importlib.util.spec_from_file_location("hermes_image_router", broker_path)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
calls = []
def hosted(payload, model, prompt, aspect):
calls.append(("hosted", model, prompt, aspect))
return {"success": False, "error": "hosted refusal"}
def local(payload, timeout=1800.0):
calls.append(("local", payload["model"], timeout))
return {
"success": True,
"image_b64": "aW1hZ2U=",
"model": "flux-2-klein-4b-local",
"route": "local",
}
monkeypatch.setattr(module, "_generate_hosted", hosted)
monkeypatch.setattr(module, "_local_request", local)
auto = module._generate(
{
"prompt": "colorize this family photograph",
"aspect_ratio": "portrait",
"model": "atlas-image-auto-high",
}
)
assert auto["success"] is True
assert auto["route"] == "local"
assert auto["hosted_fallback_reason"] == "hosted refusal"
assert [call[0] for call in calls] == ["hosted", "local"]
calls.clear()
explicit = module._generate(
{
"prompt": "make a local landscape",
"aspect_ratio": "landscape",
"model": "flux-2-klein-4b-local",
}
)
assert explicit["route"] == "local"
assert [call[0] for call in calls] == ["local"]
def test_image_broker_policy_is_narrow_and_operator_extensible(
tmp_path: Path, monkeypatch
):
"""Family-photo restoration stays allowed while the hard boundary remains."""
broker_path = HERMES / "scripts" / "image_broker.py"
spec = importlib.util.spec_from_file_location("hermes_image_policy", broker_path)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
policy = tmp_path / "policy.json"
policy.write_text('{"additional_blocked_phrases":["site-specific block"]}')
monkeypatch.setattr(module, "POLICY_PATH", policy)
assert (
module._policy_error(
"Colorize my baby photograph with a lighter natural skin tone"
)
is None
)
assert "minors" in module._policy_error("Create a sexual image of a child")
assert module._policy_error("A site-specific block request") == (
"request is blocked by the operator image policy"
)
def test_local_flux_runtime_and_gpu_handoff_are_flux_managed():
"""FLUX and Wolf share titan-24 while text stays on titan-20."""
deployment = _documents(HERMES / "local-image-deployment.yaml")[0]
assert deployment["metadata"]["name"] == "hermes-local-image"
pod = deployment["spec"]["template"]["spec"]
assert pod["serviceAccountName"] == "hermes-gpu-runtime"
local = next(item for item in pod["containers"] if item["name"] == "local-image")
assert len(pod["containers"]) == 1
assert local["resources"]["requests"]["nvidia.com/gpu.shared"] == 1
assert local["ports"] == [{"name": "local-image", "containerPort": 9004}]
assert any(mount["mountPath"] == "/models" for mount in local["volumeMounts"])
model_env = {item["name"]: item["value"] for item in local["env"]}
assert model_env["HERMES_LOCAL_IMAGE_LISTEN_PORT"] == "9004"
assert model_env["HERMES_LOCAL_IMAGE_REVISION"] == (
"e7b7dc27f91deacad38e78976d1f2b499d76a294"
)
assert model_env["HERMES_LOCAL_IMAGE_GPU_ACTIVITY_NODE"] == "titan-24"
assert model_env["HERMES_LOCAL_IMAGE_GPU_ACTIVE_SM_PERCENT"] == "80"
assert model_env["HERMES_LOCAL_IMAGE_GPU_MAX_EXTERNAL_MEMORY_BYTES"] == (
"3221225472"
)
assert model_env["HERMES_LOCAL_IMAGE_OFFLOAD_MODE"] == "sequential"
assert (
"nvidia-process-exporter-local.monitoring.svc.cluster.local"
in model_env["HERMES_LOCAL_IMAGE_GPU_ACTIVITY_URL"]
)
models_volume = next(item for item in pod["volumes"] if item["name"] == "models")
assert models_volume["persistentVolumeClaim"]["claimName"] == (
"hermes-image-models"
)
services = _documents(HERMES / "service.yaml")
image_service = next(
item for item in services if item["metadata"]["name"] == "hermes-local-image"
)
assert image_service["spec"]["selector"] == {"app": "hermes-local-image"}
handoff_services = _documents(HERMES / "model-gate-deployment.yaml")
handoff = next(
item
for item in handoff_services
if item["kind"] == "Service"
and item["metadata"]["name"] == "hermes-gpu-handoff"
)
assert handoff["spec"]["ports"][0]["targetPort"] == "handoff"
ariadne = _documents(
Path(__file__).parents[2] / "services/maintenance/apps/ariadne-deployment.yaml"
)[0]
env = {
item["name"]: item["value"]
for item in ariadne["spec"]["template"]["spec"]["containers"][0]["env"]
if "value" in item
}
assert env["GAME_MODE_OLLAMA_URL"] == (
"http://hermes-gpu-handoff.hermes.svc.cluster.local:11434"
)
assert env["GAME_MODE_OLLAMA_MODEL"] == "flux-2-klein-4b-local"
for config_name in (
"configmap.yaml",
"agent-configmap.yaml",
"chat-configmap.yaml",
):
config = _documents(HERMES / config_name)[0]["data"]["config.yaml"]
assert "gpt-oss:20b" not in config
assert "atlas-switchyard" in config
switchyard = _documents(HERMES / "switchyard-configmap.yaml")[0]["data"][
"routes.toml"
]
assert 'id = "qwen2.5:14b-instruct-q4_0"' in switchyard
assert "qwen2.5:3b-instruct-q4_0" not in switchyard
assert "route/local/qwen2.5-14b/medium" in switchyard
assert "Anthropic and Claude name the same provider" in switchyard
assert "OpenAI and Codex name the same provider" in switchyard
assert "Choose across every configured Codex and Claude family" in switchyard
assert "Claude Fable" in switchyard
assert switchyard.count('Treat "think hard"') == 4
assert switchyard.count("Never choose below the") >= 5
switchyard_config = tomllib.loads(switchyard)
routes = switchyard_config["routes"]
configured_targets = switchyard_config["targets"]
for route_name in ("auto_fast", "auto_balanced", "auto_deep", "auto_maximum"):
leading_targets = set(routes[route_name]["targets"][:2])
assert leading_targets == {"codex_sol_xhigh", "claude_opus_xhigh"}
assert "max_output_tokens" not in routes[route_name]
for route_name in ("auto_fast", "auto_balanced", "auto_deep", "auto_maximum"):
targets = routes[route_name]["targets"]
selector_targets = routes[route_name]["response_schema"]
assert not any(target.startswith("local_") for target in targets)
assert any("fable" in target for target in targets)
assert "local_qwen" not in selector_targets
assert "not eligible for foreground" in routes[route_name]["prompt"]
for route_name in ("auto_deep", "auto_maximum"):
targets = routes[route_name]["targets"]
selector_targets = routes[route_name]["response_schema"]
assert not any(target.endswith("_low") for target in targets)
assert "_low" not in selector_targets
maximum_targets = routes["auto_maximum"]["targets"]
maximum_selector_targets = routes["auto_maximum"]["response_schema"]
assert not any(target.endswith("_medium") for target in maximum_targets)
assert "_medium" not in maximum_selector_targets
assert "absolute high effort floor" in routes["auto_maximum"]["prompt"]
assert "quality mark was missed" in routes["auto_balanced"]["prompt"]
assert "raises the next boundary to xhigh" in routes["auto_maximum"]["prompt"]
assert (
"Repeated quality misses require xhigh"
in routes["worker_auto_maximum"]["prompt"]
)
assert any(
target.startswith("local_") for target in routes["manual_local_qwen"]["targets"]
)
for route_name in (
"manual_codex_luna",
"manual_codex_terra",
"manual_codex_sol",
"manual_claude_haiku",
"manual_claude_fable",
"manual_claude_sonnet",
"manual_claude_opus",
):
assert not any(
target.startswith("local_") for target in routes[route_name]["targets"]
)
for provider, families in {
"codex": ("luna", "terra", "sol"),
"claude": ("haiku", "fable", "sonnet", "opus"),
}.items():
for family in families:
for effort in ("low", "medium", "high", "xhigh"):
route = routes[f"manual_{provider}_{family}_{effort}"]
assert route["id"] == f"atlas/manual/{provider}/{family}/{effort}"
assert route["targets"][0] == f"{provider}_{family}_{effort}"
worker_target = f"worker_{provider}_{family}_{effort}"
assert worker_target in routes["worker_auto_maximum"]["targets"]
assert configured_targets[worker_target]["id"] == (
f"worker/{provider}/{family}/{effort}"
)
for route_name in ("auto_fast", "auto_balanced"):
prompt = routes[route_name]["prompt"]
assert "image tool—not the conversational model" in prompt
assert "Do not select a" in prompt
assert "local Qwen or Claude target" in prompt
assert "max_output_tokens" not in routes["worker_auto_maximum"]
model_gate = _documents(HERMES / "model-gate-configmap.yaml")[0]["data"][
"model_gate.py"
]
assert "qwen2.5:14b-instruct-q4_0" in model_gate

View File

@ -1,478 +0,0 @@
"""Session lineage and continuity contracts for Hermes chat."""
from __future__ import annotations
import importlib.util
import json
import sqlite3
from pathlib import Path
import pytest
from testing.tests.test_hermes_chat_support import (
HERMES,
)
def test_codex_native_health_overrides_historical_router_errors(
tmp_path: Path, monkeypatch
):
"""Fresh first-party health is authoritative over old Switchyard probes."""
plugin_path = HERMES / "plugins" / "auto-router" / "provider_status.py"
spec = importlib.util.spec_from_file_location("hermes_provider_status", plugin_path)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
health_path = tmp_path / "codex.json"
health_path.write_text(
json.dumps(
{
"state": "available",
"authenticated": True,
"transport": "codex-chatgpt-subscription",
}
)
)
monkeypatch.setattr(module, "CODEX_HEALTH_PATH", health_path)
monkeypatch.setattr(module, "CLAUDE_HEALTH_PATH", tmp_path / "missing.json")
monkeypatch.setattr(
module,
"_get_json",
lambda url: {"status": "ok"}
if url.endswith("/health")
else {
"models": {
"route/codex/terra/medium": {
"calls": 1,
"errors": 99,
"total_tokens": 12,
}
}
},
)
monkeypatch.setattr(module, "_codex_account", lambda: {})
monkeypatch.setattr(module, "_claude_account", lambda: {})
codex = module.provider_status_payload()["providers"]["codex"]
assert codex["errors"] == 99
assert codex["state"] == "available"
assert codex["native_health"]["transport"] == "codex-chatgpt-subscription"
def test_api_session_patch_accepts_parent_lineage(tmp_path: Path):
"""API-created workers must persist the originating Hermes session."""
module_path = HERMES / "scripts" / "patch_api_server_sessions.py"
spec = importlib.util.spec_from_file_location("patch_api_sessions", module_path)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
source = tmp_path / "api_server.py"
destination = tmp_path / "patched.py"
source.write_text(
"prefix\n"
+ module.BEFORE
+ "middle\n"
+ module.RUNS_BEFORE
+ "run body\n"
+ module.RUN_CLOSE_BEFORE
+ module.RESPONSES_SESSION_BEFORE
+ module.EVENT_CALLBACK_SIGNATURE_BEFORE
+ "callback docstring and push helper\n"
+ module.EVENT_CALLBACK_BODY_BEFORE
+ "tool start body\n"
+ module.EVENT_CALLBACK_END_BEFORE
+ module.EVENT_CALLBACK_CALL_BEFORE
+ module.RUN_SWEEP_BEFORE
+ "suffix\n",
encoding="utf-8",
)
module.patch(source, destination)
patched = destination.read_text(encoding="utf-8")
assert "X-Hermes-Parent-Session-Id" in patched
assert "parent_session_id=parent_session_id" in patched
assert "Parent session not found" in patched
assert "HERMES_API_DEFAULT_PARENT_MATCH_PREFIXES" in patched
assert "user_message.startswith(default_prefixes)" in patched
assert "session_parent_conflict" in patched
assert "X-Hermes-Conversation-Platform" in patched
assert "X-Hermes-Conversation-Title" in patched
assert 'conversation_platform != "telegram"' in patched
assert "db.record_gateway_session_peer(" in patched
assert 'display_name="Telegram"' in patched
assert "db.reopen_session(session_id)" in patched
assert 'db.end_session(session_id, f"api_run_{terminal_status}")' in patched
assert "def _record_run_activity(" in patched
assert '"_thinking": "Hermes is reasoning"' in patched
assert '"run.started": "Worker started"' in patched
assert '"run.completed": "Worker completed"' in patched
assert '"reasoning.available": "Hermes finished a reasoning step"' in patched
assert '"subagent.progress": "Nested worker progress"' in patched
assert "redact_sensitive_text" in patched
assert 'getattr(os, "O_NOFOLLOW", 0)' in patched
assert "os.fchmod(fd, 0o600)" in patched
assert "session_id=session_id" in patched
assert 'self._record_run_activity(session_id, "run.started")' in patched
assert "detail = tool_name if event_type in {" in patched
assert 'if event_type == "subagent.tool"' in patched
assert "_RUN_ACTIVITY_HEARTBEAT_SECONDS = 15.0" in patched
assert "heartbeats.get(session_id, 0.0)" in patched
assert '"subagent.thinking",' in patched
assert "Stream retention and run lifetime are separate" in patched
assert 'terminal_status in {"completed", "failed", "cancelled"}' in patched
assert patched.index("terminal_status = self._run_statuses") < patched.index(
"self._active_run_tasks.pop(run_id, None)"
)
def test_telegram_api_session_migration_is_bounded_and_idempotent(tmp_path: Path):
"""Only named Telegram conversations receive presentation metadata."""
module_path = HERMES / "scripts" / "migrate_telegram_api_sessions.py"
spec = importlib.util.spec_from_file_location(
"migrate_telegram_sessions", module_path
)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
state = tmp_path / "state.db"
responses = tmp_path / "response_store.db"
with sqlite3.connect(state) as connection:
connection.execute(
"""CREATE TABLE sessions (
id TEXT PRIMARY KEY, source TEXT, session_key TEXT, chat_type TEXT,
display_name TEXT, origin_json TEXT, title TEXT
)"""
)
connection.executemany(
"INSERT INTO sessions (id, source) VALUES (?, ?)",
(("telegram-session", "api_server"), ("other-session", "api_server")),
)
with sqlite3.connect(responses) as connection:
connection.execute(
"CREATE TABLE conversations (name TEXT PRIMARY KEY, response_id TEXT NOT NULL)"
)
connection.execute(
"CREATE TABLE responses (response_id TEXT PRIMARY KEY, data TEXT NOT NULL, accessed_at REAL NOT NULL)"
)
connection.executemany(
"INSERT INTO responses VALUES (?, ?, 0)",
(
("telegram-response", json.dumps({"session_id": "telegram-session"})),
("other-response", json.dumps({"session_id": "other-session"})),
),
)
connection.executemany(
"INSERT INTO conversations VALUES (?, ?)",
(("telegram", "telegram-response"), ("unrelated", "other-response")),
)
assert module.migrate(state, responses) == 1
assert module.migrate(state, responses) == 0
with sqlite3.connect(state) as connection:
telegram = connection.execute(
"SELECT session_key, chat_type, display_name, origin_json, title "
"FROM sessions WHERE id = 'telegram-session'"
).fetchone()
other = connection.execute(
"SELECT session_key, title FROM sessions WHERE id = 'other-session'"
).fetchone()
assert telegram[:3] == ("telegram", "private", "Telegram")
assert json.loads(telegram[3]) == {
"platform": "telegram",
"session_key": "telegram",
}
assert telegram[4] == "Telegram · General"
assert other == (None, None)
def test_web_session_activity_patch_projects_bounded_events(tmp_path: Path):
"""The DOM transcript includes events without polluting agent history."""
module_path = HERMES / "scripts" / "patch_web_session_activity.py"
spec = importlib.util.spec_from_file_location("patch_web_activity", module_path)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
source = tmp_path / "web_server.py"
destination = tmp_path / "patched.py"
source.write_text(
"prefix\n"
+ module.LATEST_ROWS_BEFORE
+ module.LATEST_SELECTION_BEFORE
+ module.HELPER_MARKER
+ " db = object()\n"
+ module.MESSAGES_BEFORE
+ "suffix\n",
encoding="utf-8",
)
module.patch(source, destination)
patched = destination.read_text(encoding="utf-8")
assert "def _run_activity_messages(" in patched
assert 'root = get_hermes_home() / "run-activity"' in patched
assert "path.stat().st_size > 600_000" in patched
assert "entries[-1_000:]" in patched
assert "*_run_activity_messages(sid)" in patched
assert "messages.sort(" in patched
assert "limit: Optional[int] = None" in patched
assert "total_messages = len(messages)" in patched
assert "min(int(limit), 10_000)" in patched
assert '"total_messages": total_messages' in patched
assert "SELECT id, parent_session_id, started_at, ended_at" in patched
assert "newest still-open member of the lineage" in patched
assert "immediate objective parent" in patched
assert "mixes unrelated workstreams" in patched
assert "orphaned children" in patched
assert 'item[0].get("ended_at") is None' in patched
def test_web_session_lineage_returns_to_resumed_parent():
"""An ended reviewer must not strand the live view away from its parent."""
module_path = HERMES / "scripts" / "patch_web_session_activity.py"
spec = importlib.util.spec_from_file_location("patch_web_lineage", module_path)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
namespace: dict[str, object] = {}
exec(
"def select_active(sid, db, rows):\n" + module.LATEST_SELECTION_AFTER,
namespace,
)
class FakeDB:
def get_session(self, session_id):
return next((row for row in rows if row["id"] == session_id), None)
rows = [
{
"id": "umbrella",
"parent_session_id": None,
"started_at": 0.0,
"ended_at": None,
},
{
"id": "root",
"parent_session_id": "umbrella",
"started_at": 1.0,
"ended_at": None,
},
{
"id": "review-1",
"parent_session_id": "root",
"started_at": 2.0,
"ended_at": 3.0,
},
{
"id": "review-2",
"parent_session_id": "root",
"started_at": 4.0,
"ended_at": None,
},
]
select_active = namespace["select_active"]
assert select_active("root", FakeDB(), rows) == (
"review-2",
["root", "review-2"],
)
assert select_active("review-1", FakeDB(), rows) == (
"review-2",
["root", "review-2"],
)
rows[2]["ended_at"] = 5.0
rows[3]["ended_at"] = 5.0
assert select_active("root", FakeDB(), rows) == ("root", ["root"])
rows[1]["ended_at"] = 9.0
rows.append(
{
"id": "orphaned-review",
"parent_session_id": "root",
"started_at": 6.0,
"ended_at": None,
}
)
rows.append(
{
"id": "accepted-review",
"parent_session_id": "root",
"started_at": 7.0,
"ended_at": 8.0,
}
)
assert select_active("review-1", FakeDB(), rows) == (
"accepted-review",
["root", "accepted-review"],
)
def test_api_activity_patch_coalesces_streaming_heartbeats(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
):
"""Token callbacks stay bounded while every tool transition is retained."""
module_path = HERMES / "scripts" / "patch_api_server_sessions.py"
spec = importlib.util.spec_from_file_location("patch_api_activity", module_path)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
activity_body = module.EVENT_CALLBACK_SIGNATURE_AFTER.split(
" def _make_run_event_callback(", 1
)[0]
namespace: dict[str, object] = {}
exec(
"import hashlib, json, logging, os, time\n"
"from pathlib import Path\n"
"logger = logging.getLogger(__name__)\n"
"def redact_sensitive_text(value): return value\n"
"class ActivityRecorder:\n" + activity_body,
namespace,
)
recorder = namespace["ActivityRecorder"]()
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
recorder._record_run_activity("session", "_thinking")
recorder._record_run_activity("session", "reasoning.available")
recorder._record_run_activity("session", "subagent.thinking")
recorder._record_run_activity("session", "tool.started", tool_name="terminal")
recorder._record_run_activity("session", "tool.completed", tool_name="terminal")
journals = list((tmp_path / "run-activity").glob("*.jsonl"))
assert len(journals) == 1
entries = [json.loads(line) for line in journals[0].read_text().splitlines()]
assert [entry["activity_event"] for entry in entries] == [
"_thinking",
"tool.started",
"tool.completed",
]
def test_legacy_api_sessions_are_nested_idempotently(tmp_path: Path):
"""Known standalone API workers move under Cassandra without data loss."""
module_path = HERMES / "scripts" / "migrate_api_session_lineage.py"
spec = importlib.util.spec_from_file_location("migrate_api_sessions", module_path)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
database = tmp_path / "state.db"
with sqlite3.connect(database) as connection:
connection.execute(
"CREATE TABLE sessions (id TEXT PRIMARY KEY, source TEXT, "
"parent_session_id TEXT, title TEXT, transcript TEXT, archived INTEGER DEFAULT 0)"
)
connection.execute(
"INSERT INTO sessions (id, source, parent_session_id, title, transcript) "
"VALUES (?, 'tui', NULL, 'Cassandra', 'parent-data')",
(module.LEGACY_CASSANDRA_PARENT,),
)
worker_id = next(iter(module.LEGACY_CASSANDRA_WORKERS))
connection.execute(
"INSERT INTO sessions (id, source, parent_session_id, title, transcript) "
"VALUES (?, 'api_server', NULL, 'old', 'keep-me')",
(worker_id,),
)
connection.executemany(
"INSERT INTO sessions (id, source, parent_session_id, title, transcript) "
"VALUES (?, 'api_server', NULL, 'old smoke', 'keep-smoke')",
((orphan_id,) for orphan_id in module.LEGACY_ORPHANED_SMOKE_SESSIONS),
)
assert module.migrate(database) == 1 + len(module.LEGACY_ORPHANED_SMOKE_SESSIONS)
assert module.migrate(database) == 0
with sqlite3.connect(database) as connection:
row = connection.execute(
"SELECT parent_session_id, title, transcript FROM sessions WHERE id = ?",
(worker_id,),
).fetchone()
assert row == (
module.LEGACY_CASSANDRA_PARENT,
module.LEGACY_CASSANDRA_WORKERS[worker_id],
"keep-me",
)
with sqlite3.connect(database) as connection:
orphans = connection.execute(
"SELECT id, archived, title, transcript FROM sessions "
"WHERE id IN ({}) ORDER BY id".format(
",".join("?" for _ in module.LEGACY_ORPHANED_SMOKE_SESSIONS)
),
tuple(module.LEGACY_ORPHANED_SMOKE_SESSIONS),
).fetchall()
assert orphans == sorted(
(
orphan_id,
1,
module.LEGACY_ORPHANED_SMOKE_SESSIONS[orphan_id],
"keep-smoke",
)
for orphan_id in module.LEGACY_ORPHANED_SMOKE_SESSIONS
)
def test_automated_triage_sessions_are_grouped_without_touching_interactive_runs(
tmp_path: Path,
):
"""Only the stable Ariadne contract moves below the triage parent."""
module_path = HERMES / "scripts" / "migrate_api_session_lineage.py"
spec = importlib.util.spec_from_file_location(
"migrate_triage_sessions", module_path
)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
database = tmp_path / "state.db"
with sqlite3.connect(database) as connection:
connection.execute(
"CREATE TABLE sessions (id TEXT PRIMARY KEY, source TEXT, "
"parent_session_id TEXT, title TEXT, started_at REAL, archived INTEGER DEFAULT 0)"
)
connection.execute(
"CREATE TABLE messages (session_id TEXT, role TEXT, content TEXT)"
)
connection.executemany(
"INSERT INTO sessions (id, source, started_at) VALUES (?, 'api_server', ?)",
(("triage-run", 1.0), ("jenkins-run", 1.5), ("interactive-run", 2.0)),
)
connection.executemany(
"INSERT INTO messages (session_id, role, content) VALUES (?, 'user', ?)",
(
(
"triage-run",
module.TRIAGE_MESSAGE_PREFIXES[0]
+ " Fix for incident sonar/bstein_home/python:S2208/finding-key.",
),
(
"jenkins-run",
module.TRIAGE_MESSAGE_PREFIXES[1]
+ "\nAnalyze incident soteria/291 for the Jenkins job soteria.",
),
("interactive-run", "Please explain this alert to me."),
),
)
assert module.migrate(database, group_triage=True) == 2
assert module.migrate(database, group_triage=True) == 0
with sqlite3.connect(database) as connection:
parent = connection.execute(
"SELECT title FROM sessions WHERE id = ?", (module.TRIAGE_PARENT,)
).fetchone()
triage = connection.execute(
"SELECT parent_session_id, title FROM sessions WHERE id = 'triage-run'"
).fetchone()
interactive = connection.execute(
"SELECT parent_session_id FROM sessions WHERE id = 'interactive-run'"
).fetchone()
jenkins = connection.execute(
"SELECT parent_session_id, title FROM sessions WHERE id = 'jenkins-run'"
).fetchone()
assert parent == (module.TRIAGE_PARENT_TITLE,)
assert triage == (
module.TRIAGE_PARENT,
"Sonar · bstein_home · python:S2208 · iage-run",
)
assert interactive == (None,)
assert jenkins == (
module.TRIAGE_PARENT,
"Sonar · soteria/291 · kins-run",
)

View File

@ -1,38 +0,0 @@
"""Shared paths and loaders for Hermes chat capability contracts."""
from __future__ import annotations
import importlib.util
import sys
from pathlib import Path
from types import SimpleNamespace
import yaml
ROOT = Path(__file__).parents[2]
HERMES = ROOT / "services" / "hermes"
VAULT = ROOT / "services" / "vault"
def _documents(path: Path) -> list[dict]:
return [doc for doc in yaml.safe_load_all(path.read_text()) if doc]
def _load_broker_module(name: str, filename: str, monkeypatch):
"""Load one broker with its mounted routing-catalog dependency."""
catalog_path = HERMES / "scripts" / "routing_catalog.py"
catalog_spec = importlib.util.spec_from_file_location(
"routing_catalog", catalog_path
)
assert catalog_spec and catalog_spec.loader
catalog = importlib.util.module_from_spec(catalog_spec)
catalog_spec.loader.exec_module(catalog)
monkeypatch.setitem(sys.modules, "routing_catalog", catalog)
monkeypatch.setitem(sys.modules, "httpx", SimpleNamespace())
broker_path = HERMES / "scripts" / filename
spec = importlib.util.spec_from_file_location(name, broker_path)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module

File diff suppressed because it is too large Load Diff

View File

@ -1,378 +0,0 @@
"""Network, OAuth, Kubernetes, and SSH access contracts for Hermes CLI lanes."""
from __future__ import annotations
import yaml
from testing.tests.test_hermes_cli_lanes_support import (
FLUX_HERMES,
HERMES,
KEYCLOAK,
ROOT,
_agent_deployment,
_services,
)
def test_broker_services_survive_sibling_container_readiness_loss():
services = _services()
for name in (
"hermes-image-broker",
"hermes-codex-broker",
"hermes-local-image",
"hermes-claude-broker",
):
assert services[name]["spec"]["publishNotReadyAddresses"] is True
def test_agent_dashboard_reconnects_all_transient_websockets():
dockerfile = (HERMES.parents[1] / "dockerfiles/Dockerfile.hermes-agent").read_text(
encoding="utf-8"
)
assert "eventsRetryAttempt.current" in dockerfile
assert "if (!unmounting) setVersion((v) => v + 1);" in dockerfile
assert "events feed rejected (${ev.code}) — reload the page" in dockerfile
assert 'url = await api.buildWsUrl("/api/pty", params);' in dockerfile
assert 'url = await buildWsUrl("/api/events", { channel });' in dockerfile
assert dockerfile.count("' await api.getSessions(1, 0,") == 2
assert ".then(() => gw.connect())" in dockerfile
assert 'api.getSessions(1, 0, profile ?? "")' in dockerfile
assert "dashboard token rotated by a server restart" in dockerfile
def test_agent_refreshes_routes_after_restoring_cli_logins():
deployment = _agent_deployment()
init_containers = {
item["name"]: item
for item in deployment["spec"]["template"]["spec"]["initContainers"]
}
configure = init_containers["configure-agent-clients"]
command = configure["command"][-1]
assert "configure_agent_clients.py" in command
assert command.index("configure_agent_clients.py") < command.index(
"hermes_coordinator.py --once"
)
env = {item["name"]: item["value"] for item in configure["env"]}
assert env["HERMES_AUTH_FILE"] == "/runtime-access/hermes-auth.json"
assert env["PYTHONPATH"] == "/opt/hermes"
for name in ("bootstrap-coordinator", "configure-agent-clients"):
route_env = {
item["name"]: item["value"] for item in init_containers[name]["env"]
}
assert route_env["CODEX_HOME"] == "/runtime-access/codex"
assert route_env["CLAUDE_CONFIG_DIR"] == "/runtime-access/claude"
assert "/opt/data/tools/bin" in route_env["PATH"]
assert "patch-web-session-activity" in init_containers
web_patch = init_containers["patch-web-session-activity"]
assert "/opt/coordinator/patch_web_session_activity.py" in web_patch["command"]
containers = {
item["name"]: item
for item in deployment["spec"]["template"]["spec"]["containers"]
}
steward_env = {
item["name"]: item["value"] for item in containers["model-steward"]["env"]
}
assert steward_env["CODEX_HOME"] == "/runtime-access/codex"
assert steward_env["CLAUDE_CONFIG_DIR"] == "/runtime-access/claude"
assert "/opt/data/tools/bin" in steward_env["PATH"]
hermes_mounts = {
(item["name"], item["mountPath"], item.get("subPath"))
for item in containers["hermes"]["volumeMounts"]
}
assert (
"web-server-patch",
"/opt/hermes/hermes_cli/web_server.py",
"web_server.py",
) in hermes_mounts
def test_flux_health_checks_follow_the_owner_oauth_sidecar():
flux = yaml.safe_load(FLUX_HERMES.read_text())
checks = {(item["kind"], item["name"]) for item in flux["spec"]["healthChecks"]}
assert ("Deployment", "hermes-agent") in checks
assert ("DaemonSet", "hermes-node-ssh-access") in checks
assert ("Deployment", "oauth2-proxy-hermes-agent") not in checks
def test_agent_auth_is_bstein_group_and_email_bounded():
deployment = _agent_deployment()
oauth = next(
item
for item in deployment["spec"]["template"]["spec"]["containers"]
if item["name"] == "oauth2-proxy"
)
args = oauth["args"]
assert "--user-id-claim=sub" in args
assert "--oidc-groups-claim=groups" in args
assert "--allowed-group=/hermes-owner" in args
assert "--authenticated-emails-file=/etc/oauth2-proxy/allowed-emails" in args
script = (KEYCLOAK / "scripts/hermes_access_oidc_ensure.sh").read_text()
assert 'group_name="hermes-owner"' in script
assert "username=bstein&exact=true" in script
assert '"full.path":"true"' in script
def test_agent_network_boundary_allows_only_authenticated_and_metrics_surfaces():
documents = [
item
for item in yaml.safe_load_all((HERMES / "networkpolicy.yaml").read_text())
if item
]
isolation = next(
item
for item in documents
if item.get("metadata", {}).get("name") == "hermes-agent-isolation"
)
assert isolation["spec"]["ingress"] == [
{
"from": [
{
"namespaceSelector": {
"matchLabels": {"kubernetes.io/metadata.name": "traefik"}
},
"podSelector": {
"matchLabels": {"app.kubernetes.io/name": "traefik"}
},
}
],
"ports": [{"protocol": "TCP", "port": 4180}],
},
{
"from": [{"podSelector": {"matchLabels": {"app": "hermes-chat-tenant"}}}],
"ports": [
{"protocol": "TCP", "port": 9002},
{"protocol": "TCP", "port": 9003},
],
},
{
"from": [{"podSelector": {"matchLabels": {"app": "hermes-switchyard"}}}],
"ports": [
{"protocol": "TCP", "port": 9003},
{"protocol": "TCP", "port": 9006},
],
},
{
"from": [
{
"namespaceSelector": {
"matchLabels": {"kubernetes.io/metadata.name": "monitoring"}
},
"podSelector": {"matchLabels": {"app": "server"}},
}
],
"ports": [{"protocol": "TCP", "port": 9010}],
},
]
egress = isolation["spec"]["egress"]
assert any(
rule.get("ports") == [{"protocol": "TCP", "port": 9081}] for rule in egress
)
assert "hermes-scm" in yaml.safe_dump(egress)
assert "gitea" in yaml.safe_dump(egress)
assert egress != [{}]
def test_owner_agent_has_scoped_read_only_kubernetes_context():
config = yaml.safe_load((HERMES / "agent-kubeconfig.yaml").read_text())
assert config["current-context"] == "atlas-observer"
assert config["contexts"][0]["context"]["namespace"] == "default"
rbac_path = ROOT / "services/hermes-observer-rbac/rbac.yaml"
documents = [item for item in yaml.safe_load_all(rbac_path.read_text()) if item]
binding = next(item for item in documents if item["kind"] == "ClusterRoleBinding")
assert binding["roleRef"] == {
"apiGroup": "rbac.authorization.k8s.io",
"kind": "ClusterRole",
"name": "hermes-agent-cluster-observer-v2",
}
assert binding["subjects"] == [
{"kind": "ServiceAccount", "name": "hermes-agent", "namespace": "hermes"}
]
def test_owner_agent_has_pinned_dedicated_node_ssh_access():
deployment = _agent_deployment()
annotations = deployment["spec"]["template"]["metadata"]["annotations"]
assert (
annotations["vault.hashicorp.com/agent-inject-secret-node-ssh-private-key"]
== "kv/data/atlas/hermes/developer-ssh"
)
assert (
annotations["vault.hashicorp.com/agent-inject-secret-node-ssh-config"]
== "kv/data/atlas/hermes/developer-ssh"
)
assert (
annotations["vault.hashicorp.com/agent-inject-secret-node-ssh-known-hosts"]
== "kv/data/atlas/hermes/developer-ssh"
)
init = next(
item
for item in deployment["spec"]["template"]["spec"]["initContainers"]
if item["name"] == "init-config"
)
command = init["command"][2]
assert "ln -s /runtime-access/node-ssh-config /opt/data/home/.ssh/config" in command
assert (
"ln -s /runtime-access/node-ssh-known-hosts /opt/data/home/.ssh/known_hosts"
in command
)
assert "ln -s home/.ssh /opt/data/.ssh" in command
assert "chmod 0700 /opt/data/home/.ssh" in command
assert (
"ln -s /runtime-access/node-ssh-private-key "
"/opt/data/home/.ssh/id_ed25519_atlas_nodes"
) in command
config = yaml.safe_load((HERMES / "agent-configmap.yaml").read_text())["data"]
assert "ssh_config" not in config
assert "ssh_known_hosts" not in config
assert "ssh-ed25519" not in (HERMES / "agent-configmap.yaml").read_text()
resources = yaml.safe_load((HERMES / "kustomization.yaml").read_text())["resources"]
assert "node-ssh-access.yaml" in resources
access = [
item
for item in yaml.safe_load_all((HERMES / "node-ssh-access.yaml").read_text())
if item
]
service_account = next(item for item in access if item["kind"] == "ServiceAccount")
assert service_account["metadata"]["name"] == "hermes-node-ssh-access"
provider = next(item for item in access if item["kind"] == "SecretProviderClass")
assert provider["metadata"]["name"] == "hermes-node-ssh-access"
assert provider["spec"]["provider"] == "vault"
parameters = provider["spec"]["parameters"]
assert parameters["roleName"] == "hermes-node-ssh"
assert 'secretPath: "kv/data/atlas/hermes/developer-ssh"' in parameters["objects"]
assert 'secretKey: "public_key"' in parameters["objects"]
daemonset = next(item for item in access if item["kind"] == "DaemonSet")
pod = daemonset["spec"]["template"]["spec"]
assert pod["serviceAccountName"] == "hermes-node-ssh-access"
assert pod["automountServiceAccountToken"] is True
host_home = next(item for item in pod["volumes"] if item["name"] == "host-home")
assert host_home["hostPath"] == {"path": "/home", "type": "Directory"}
vault_secrets = next(
item for item in pod["volumes"] if item["name"] == "vault-secrets"
)
assert vault_secrets["csi"]["driver"] == "secrets-store.csi.k8s.io"
assert vault_secrets["csi"]["volumeAttributes"] == {
"secretProviderClass": "hermes-node-ssh-access"
}
reconciler = pod["containers"][0]["args"][0]
assert "/opt/node-hardener/node_account_hardening.py" in reconciler
assert "--public-key-file /vault/secrets/node-ssh-public-key" in reconciler
assert "sleep 300" in reconciler
host_etc = next(item for item in pod["volumes"] if item["name"] == "host-etc")
assert host_etc["hostPath"] == {"path": "/etc", "type": "Directory"}
hardener = (HERMES / "scripts/node_account_hardening.py").read_text()
assert 'ACCOUNT = "hermes-agent"' in hardener
assert 'LEGACY_ACCOUNTS = ("atlas", "oceanus")' in hardener
assert "ACCOUNT_UID = 1200" in hardener
assert "ACCOUNT_GID = 1200" in hardener
def test_owner_agent_tracks_no_ssh_identity_or_host_key_material():
"""Vault references may be tracked; SSH identities and trust data may not."""
forbidden = (
"BEGIN OPENSSH PRIVATE KEY",
"ssh-ed25519 AAAA",
"ssh-rsa AAAA",
"IdentityFile ",
"UserKnownHostsFile ",
"StrictHostKeyChecking ",
"ssh_config:",
"ssh_known_hosts:",
)
text_suffixes = {
".conf",
".json",
".md",
".py",
".sh",
".toml",
".yaml",
".yml",
}
tracked = "\n".join(
path.read_text(encoding="utf-8")
for path in HERMES.rglob("*")
if path.is_file() and path.suffix in text_suffixes
)
for marker in forbidden:
assert marker not in tracked
def test_switchyard_has_a_dedicated_non_owner_identity_and_read_only_catalog():
"""Routing must not inherit the owner agent's cluster-admin capability."""
service_accounts = [
item
for item in yaml.safe_load_all(
(HERMES / "vault-serviceaccount.yaml").read_text()
)
if item
]
assert any(
item["kind"] == "ServiceAccount"
and item["metadata"]["name"] == "hermes-switchyard"
for item in service_accounts
)
switchyard = yaml.safe_load((HERMES / "switchyard-deployment.yaml").read_text())
switchyard_pod = switchyard["spec"]["template"]["spec"]
assert switchyard_pod["serviceAccountName"] == "hermes-switchyard"
agent_pod = _agent_deployment()["spec"]["template"]["spec"]
for pod, container_name in (
(switchyard_pod, "worker-route-broker"),
(agent_pod, "claude-broker"),
):
container = next(
item for item in pod["containers"] if item["name"] == container_name
)
catalog = next(
item
for item in container["volumeMounts"]
if item["mountPath"] == "/routing-catalog"
)
assert catalog["readOnly"] is True
rbac = [
item
for item in yaml.safe_load_all(
(ROOT / "services/hermes-observer-rbac/rbac.yaml").read_text()
)
if item
]
binding = next(item for item in rbac if item["kind"] == "ClusterRoleBinding")
assert binding["subjects"] == [
{"kind": "ServiceAccount", "name": "hermes-agent", "namespace": "hermes"}
]
def test_switchyard_active_state_uses_a_relocatable_rwx_claim():
"""A stale node attachment must not strand the routing authority."""
claims = [
item
for item in yaml.safe_load_all((HERMES / "switchyard-pvc.yaml").read_text())
if item
]
active_claim = next(
item
for item in claims
if item["metadata"]["name"] == "hermes-switchyard-state-rwx"
)
assert active_claim["spec"]["accessModes"] == ["ReadWriteMany"]
deployment = yaml.safe_load((HERMES / "switchyard-deployment.yaml").read_text())
strategy = deployment["spec"]["strategy"]
assert strategy == {
"type": "RollingUpdate",
"rollingUpdate": {"maxSurge": 1, "maxUnavailable": 0},
}
pod = deployment["spec"]["template"]["spec"]
state = next(item for item in pod["volumes"] if item["name"] == "state")
assert (
state["persistentVolumeClaim"]["claimName"] == active_claim["metadata"]["name"]
)

View File

@ -1,444 +0,0 @@
"""Agent configuration and dashboard contracts for Hermes CLI lanes."""
from __future__ import annotations
import json
import sys
from contextlib import nullcontext
from pathlib import Path
from types import SimpleNamespace
import pytest
import yaml
from testing.tests.test_hermes_cli_lanes_support import (
HERMES,
_agent_deployment,
client_config,
lanes,
migration,
policy,
)
def test_workspace_preparation_failure_durably_blocks_the_claim(
tmp_path: Path, monkeypatch
):
task = SimpleNamespace(id="t_bad_worktree", current_run_id=7, assignee="cli-auto")
calls = []
class Connection:
def close(self):
return None
fake_db = SimpleNamespace(
scoped_current_board=lambda _board: nullcontext(),
connect=lambda board: Connection(),
get_task=lambda _conn, _task_id: task,
worker_log_path=lambda _task_id, board: tmp_path / "worker.log",
_resolve_worktree_workspace=lambda _task, board: (_ for _ in ()).throw(
ValueError("not a Git repository")
),
block_task=lambda *_args, **kwargs: calls.append(kwargs),
)
monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=fake_db))
monkeypatch.setattr(
lanes, "state_path", lambda _board, _task_id: tmp_path / "state.json"
)
lanes.execute_claim("cassandra", "t_bad_worktree")
assert calls[0]["kind"] == "capability"
assert calls[0]["expected_run_id"] == 7
assert "not a Git repository" in calls[0]["reason"]
def test_artifacts_cannot_escape_the_task_worktree(tmp_path: Path):
workspace = tmp_path / "workspace"
workspace.mkdir()
inside = workspace / "report.json"
outside = tmp_path / "auth.json"
inside.write_text("{}\n", encoding="utf-8")
outside.write_text("secret\n", encoding="utf-8")
assert lanes.workspace_artifacts(
workspace,
["report.json", str(outside), "missing.json"],
) == [str(inside)]
def test_restart_provider_change_includes_explicit_workspace_handoff(
tmp_path: Path, monkeypatch
):
task = SimpleNamespace(
id="t_resume",
current_run_id=9,
assignee="cli-auto",
max_runtime_seconds=60,
)
state_file = tmp_path / "state.json"
state_file.write_text(
json.dumps({"current_route": {"provider": "claude"}}),
encoding="utf-8",
)
(tmp_path / "worker.log").write_text("prior provider evidence", encoding="utf-8")
prompts = []
class Connection:
def close(self):
return None
fake_db = SimpleNamespace(
scoped_current_board=lambda _board: nullcontext(),
connect=lambda board: Connection(),
get_task=lambda _conn, _task_id: task,
worker_log_path=lambda _task_id, board: tmp_path / "worker.log",
_resolve_worktree_workspace=lambda _task, board: (tmp_path, "wt/t_resume"),
set_branch_name=lambda *_args: None,
set_workspace_path=lambda *_args: None,
build_worker_context=lambda *_args: "resume objective",
add_comment=lambda *_args: None,
complete_task=lambda *_args, **_kwargs: None,
block_task=lambda *_args, **_kwargs: None,
)
monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=fake_db))
monkeypatch.setattr(lanes, "state_path", lambda _board, _task_id: state_file)
monkeypatch.setattr(
lanes,
"select_route",
lambda *_args, **_kwargs: lanes.Route(
"codex", "gpt-5.6-sol", "high", "codex-high", "jetson", "vote", 1, ()
),
)
monkeypatch.setattr(
lanes,
"git_handoff",
lambda _workspace, output: f"HANDOFF:{output}",
)
monkeypatch.setattr(
lanes,
"run_provider",
lambda _route, prompt, *_args, **_kwargs: (
prompts.append(prompt)
or lanes.ProcessResult(
0,
"",
{
"status": "completed",
"summary": "done",
"changed_files": [],
"tests_run": [],
"artifacts": [],
"blockers": [],
},
False,
)
),
)
lanes.execute_claim("cassandra", "t_resume")
assert "HANDOFF:prior provider evidence" in prompts[0]
def test_provider_commands_are_structured_unattended_and_capped(tmp_path: Path):
route = lanes.Route(
"codex", "gpt-5.6-sol", "xhigh", "codex-xhigh", "jetson", "vote", 1, ()
)
command = lanes._codex_command(
route, "Work.", tmp_path, {}, tmp_path / "result.json"
)
assert "--dangerously-bypass-approvals-and-sandbox" in command
assert "--json" in command
assert "--output-schema" in command
assert 'model_reasoning_effort="xhigh"' in command
claude_state = {"claude_session_id": "13864642-2985-4f91-bef5-53f145f878e8"}
claude = lanes._claude_command(
lanes.Route(
"claude", "claude-opus-5", "xhigh", "claude-xhigh", "jetson", "vote", 1, ()
),
"Review.",
claude_state,
False,
)
assert "--dangerously-skip-permissions" in claude
assert "--output-format" in claude and "stream-json" in claude
assert "--json-schema" in claude
assert "--disallowedTools" in claude
assert "Bash(kubectl apply *)" not in claude
assert "Bash(flux reconcile *)" not in claude
assert "max" not in claude
def test_worker_contract_separates_review_findings_from_task_blockers(tmp_path: Path):
prompt = lanes.build_prompt("Review the change.", tmp_path)
assert "put defects and risks in findings" in prompt
assert "blockers array must be empty whenever status is completed" in prompt
assert "findings" in lanes.RESULT_SCHEMA["properties"]
assert set(lanes.RESULT_SCHEMA["required"]) == set(
lanes.RESULT_SCHEMA["properties"]
)
assert (
"assigned task itself"
in lanes.RESULT_SCHEMA["properties"]["blockers"]["description"]
)
@pytest.mark.parametrize(
"command",
[
"git push --force origin main",
"git reset --hard HEAD~1",
"git clean -fd",
],
)
def test_claude_pretool_hook_blocks_hard_denies(command: str):
assert policy.denial_reason(command)
def test_claude_pretool_hook_allows_normal_engineering():
assert policy.denial_reason("pytest -q testing/tests") is None
assert policy.denial_reason("git push origin feature/hermes") is None
assert policy.denial_reason("kubectl delete pod -n cassandra stuck-worker") is None
assert policy.denial_reason("flux reconcile kustomization hermes") is None
assert policy.denial_reason("vault kv get kv/atlas/hermes") is None
def test_claude_settings_preserve_state_and_install_three_guardrail_layers(
tmp_path: Path,
):
state = tmp_path / ".claude.json"
settings = tmp_path / "settings.json"
state.write_text('{"promptQueueUseCount": 4}\n', encoding="utf-8")
settings.write_text(
json.dumps(
{
"theme": "dark",
"permissions": {
"deny": [
"Bash(kubectl apply *)",
"Bash(flux reconcile *)",
"Bash(vault kv *)",
"Bash(custom-owner-rule *)",
]
},
}
)
+ "\n",
encoding="utf-8",
)
client_config.configure_claude_state(state)
client_config.configure_claude_settings(settings)
state_value = json.loads(state.read_text())
settings_value = json.loads(settings.read_text())
assert state_value["promptQueueUseCount"] == 4
assert state_value["bypassPermissionsModeAccepted"] is True
assert settings_value["theme"] == "dark"
assert "Bash(git reset --hard *)" in settings_value["permissions"]["deny"]
assert "Bash(custom-owner-rule *)" in settings_value["permissions"]["deny"]
assert "Bash(kubectl apply *)" not in settings_value["permissions"]["deny"]
assert "Bash(flux reconcile *)" not in settings_value["permissions"]["deny"]
assert "Bash(vault kv *)" not in settings_value["permissions"]["deny"]
hook = settings_value["hooks"]["PreToolUse"][0]["hooks"][0]
assert "claude_command_policy.py" in hook["command"]
def test_legacy_state_is_archived_without_removing_provider_transcripts(tmp_path: Path):
session = tmp_path / "home/.config/herdr/session.json"
session.parent.mkdir(parents=True)
session.write_text(
'{"agents":[{"agent":"claude","session_id":"abc"}]}', encoding="utf-8"
)
binary = tmp_path / "tools/bin/herdr"
binary.parent.mkdir(parents=True)
binary.write_text("legacy", encoding="utf-8")
(tmp_path / "home/.claude").mkdir()
(tmp_path / "home/.codex").mkdir()
archive = migration.archive_legacy_state(tmp_path)
value = json.loads(archive.read_text())
assert value["legacy_session"]["agents"][0]["session_id"] == "abc"
assert (tmp_path / "home/.claude").is_dir()
assert (tmp_path / "home/.codex").is_dir()
assert not binary.exists()
assert not (tmp_path / "home/.config/herdr").exists()
def test_agent_uses_one_native_kanban_control_plane():
configmap = yaml.safe_load((HERMES / "agent-configmap.yaml").read_text())
config = yaml.safe_load(configmap["data"]["config.yaml"])
assert config["model"] == {
"provider": "atlas-switchyard",
"default": "atlas/auto/balanced",
"model": "atlas/auto/balanced",
}
assert config["kanban"]["dispatch_in_gateway"] is True
assert config["kanban"]["default_assignee"] == "cli-auto"
assert config["plugins"]["enabled"] == ["auto-router"]
deployment = _agent_deployment()
pod = deployment["spec"]["template"]["spec"]
assert pod["enableServiceLinks"] is False
names = {item["name"] for item in pod["containers"]}
assert "cli-lane-runner" in names
assert "terminal" in names
assert not any("herdr" in name for name in names)
rendered = (HERMES / "agent-deployment.yaml").read_text()
assert "herdr server" not in rendered
assert "herdr-dispatch" not in rendered
def test_cli_lane_reserves_cpu_headroom_for_ui_and_auth():
deployment = _agent_deployment()
containers = {
item["name"]: item
for item in deployment["spec"]["template"]["spec"]["containers"]
}
lane = containers["cli-lane-runner"]
environment = {item["name"]: item["value"] for item in lane["env"]}
assert environment["HERMES_CLI_LANE_CONCURRENCY"] == "2"
assert lane["resources"] == {
"requests": {"cpu": "100m", "memory": "256Mi"},
"limits": {"cpu": "2", "memory": "6Gi"},
}
def test_agent_avoids_unhealthy_nodes_and_fits_its_remaining_capacity():
"""Placement correction: keep the agent off nodes that cannot hold it.
titan-04 is cordoned after repeated kernel undervoltage and kubelet
failure, and titan-19 was probe/Longhorn unstable under worker load, so
both must join the existing hard exclusions. That leaves titan-05 as the
healthy candidate, which is tight enough on requested CPU that the main
container has to give back 50m to schedule there.
"""
pod = _agent_deployment()["spec"]["template"]["spec"]
hostnames = next(
item
for item in pod["affinity"]["nodeAffinity"][
"requiredDuringSchedulingIgnoredDuringExecution"
]["nodeSelectorTerms"][0]["matchExpressions"]
if item["key"] == "kubernetes.io/hostname"
)
assert hostnames["operator"] == "NotIn"
assert set(hostnames["values"]) >= {"titan-04", "titan-19"}
hermes = next(item for item in pod["containers"] if item["name"] == "hermes")
assert hermes["resources"]["requests"]["cpu"] == "300m"
def test_agent_root_is_stock_dashboard_and_terminal_is_a_separate_path():
deployment = _agent_deployment()
pod = deployment["spec"]["template"]["spec"]
containers = {item["name"]: item for item in pod["containers"]}
assert "webui" not in containers
assert "dashboard" not in containers
hermes = containers["hermes"]
hermes_env = {item["name"]: item["value"] for item in hermes["env"]}
assert hermes_env["HERMES_STREAM_STALE_TIMEOUT"] == "600"
assert hermes_env["HERMES_API_CALL_STALE_TIMEOUT"] == "600"
assert hermes["command"] == ["/bin/sh", "-ec"]
startup = hermes["args"][0]
assert ". /opt/data/.env" in startup
assert "exec /init /opt/hermes/docker/main-wrapper.sh gateway run" in startup
hermes_env = {item["name"]: item["value"] for item in hermes["env"]}
assert hermes_env["HERMES_DASHBOARD"] == "1"
assert hermes_env["HERMES_DASHBOARD_HOST"] == "127.0.0.1"
assert hermes_env["HERMES_DASHBOARD_PORT"] == "9119"
assert hermes_env["HERMES_TUI_AGENT_INIT_TIMEOUT_S"] == "180"
assert hermes["securityContext"]["runAsUser"] == 0
assert hermes["securityContext"]["runAsGroup"] == 0
for probe_name in ("startupProbe", "readinessProbe", "livenessProbe"):
probe = hermes[probe_name]
assert probe["exec"]["command"] == [
"curl",
"-fsS",
"http://127.0.0.1:9119/api/status",
]
terminal = containers["terminal"]
command = terminal["args"][0]
assert "--base-path /terminal" in command
assert "--check-origin" not in command
assert "/usr/bin/tmux new-session -A" in command
assert "--continue" in command
assert "--yolo" in command
terminal_env = {item["name"]: item["value"] for item in terminal["env"]}
assert terminal_env["HERMES_TUI_AGENT_INIT_TIMEOUT_S"] == "180"
claude_broker = containers["claude-broker"]
claude_env = {item["name"]: item["value"] for item in claude_broker["env"]}
assert claude_env["HERMES_CLAUDE_BROKER_CONCURRENCY"] == "2"
assert claude_broker["readinessProbe"]["tcpSocket"] == {"port": "claude-broker"}
assert claude_broker["livenessProbe"]["tcpSocket"] == {"port": "claude-broker"}
args = containers["oauth2-proxy"]["args"]
terminal_upstream = "--upstream=http://127.0.0.1:7681/terminal/"
dashboard_upstream = "--upstream=http://127.0.0.1:9119/"
assert terminal_upstream in args
assert dashboard_upstream in args
assert args.index(terminal_upstream) < args.index(dashboard_upstream)
assert "--pass-host-header=false" in args
assert "--cookie-refresh=19m" in args
assert "--session-store-type=redis" in args
assert any(
arg.startswith("--redis-connection-url=redis://hermes-oauth-sessions.")
for arg in args
)
patch_init = next(
item for item in pod["initContainers"] if item["name"] == "patch-tui-gateway"
)
assert patch_init["command"][-1] == "/patched/server.py"
for name in ("hermes", "terminal"):
mounts = containers[name]["volumeMounts"]
assert {
"name": "tui-gateway-patch",
"mountPath": "/opt/hermes/tui_gateway/server.py",
"subPath": "server.py",
} in mounts
ingress_documents = [
item
for item in yaml.safe_load_all((HERMES / "agent-ingress.yaml").read_text())
if item
]
middlewares = {
item["metadata"]["name"]: item
for item in ingress_documents
if item["kind"] == "Middleware"
}
assert middlewares["hermes-agent-terminal-slash"]["spec"]["redirectRegex"][
"replacement"
].endswith("/terminal/")
assert (
middlewares["hermes-agent-stock-dashboard-headers"]["spec"]["headers"][
"customRequestHeaders"
]["Origin"]
== "http://127.0.0.1:9119"
)
ingresses = {
item["metadata"]["name"]: item
for item in ingress_documents
if item["kind"] == "Ingress"
}
assert (
ingresses["hermes-agent-dashboard"]["metadata"]["annotations"][
"traefik.ingress.kubernetes.io/router.middlewares"
]
== "hermes-hermes-agent-stock-dashboard-headers@kubernetescrd"
)
assert (
ingresses["hermes-agent-terminal"]["metadata"]["annotations"][
"traefik.ingress.kubernetes.io/router.middlewares"
]
== "hermes-hermes-agent-terminal-slash@kubernetescrd"
)

View File

@ -1,392 +0,0 @@
"""Kanban claim and lifecycle contracts for Hermes CLI lanes."""
from __future__ import annotations
import sys
from contextlib import nullcontext
from pathlib import Path
from types import SimpleNamespace
import pytest
from testing.tests.test_hermes_cli_lanes_support import (
lanes,
)
def test_unassigned_ready_task_is_persistently_routed_to_auto_lane(monkeypatch):
task = SimpleNamespace(id="t_auto", assignee=None, status="ready")
assigned = []
class Connection:
def close(self):
return None
def assign_task(_conn, task_id, profile):
assigned.append((task_id, profile))
task.assignee = profile
return True
fake_db = SimpleNamespace(
list_boards=lambda include_archived=False: [{"slug": "cassandra"}],
scoped_current_board=lambda _board: nullcontext(),
connect=lambda board: Connection(),
recompute_ready=lambda _conn: None,
list_tasks=lambda _conn: [task],
assign_task=assign_task,
get_task=lambda _conn, _task_id: task,
claim_task=lambda _conn, _task_id, **_kwargs: task,
)
monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=fake_db))
assert lanes.claim_ready(set(), 1) == [("cassandra", "t_auto")]
assert assigned == [("t_auto", "cli-auto")]
def test_corrupt_board_is_quarantined_without_stopping_healthy_lanes(
monkeypatch, capsys
):
class CorruptBoardError(Exception):
pass
task = SimpleNamespace(id="t_healthy", assignee="cli-auto", status="ready")
class Connection:
def close(self):
return None
def connect(*, board):
if board == "cassandra":
raise CorruptBoardError("integrity_check failed")
return Connection()
fake_db = SimpleNamespace(
KanbanDbCorruptError=CorruptBoardError,
list_boards=lambda include_archived=False: [
{"slug": "cassandra"},
{"slug": "healthy"},
],
scoped_current_board=lambda _board: nullcontext(),
connect=connect,
recompute_ready=lambda _conn: None,
list_tasks=lambda _conn: [task],
claim_task=lambda _conn, _task_id, **_kwargs: task,
)
monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=fake_db))
lanes.BOARD_CORRUPTION_ERRORS.clear()
assert lanes.claim_ready(set(), 1) == [("healthy", "t_healthy")]
assert "temporarily skipping Kanban board 'cassandra'" in capsys.readouterr().err
def test_transient_board_scan_failure_does_not_stop_healthy_lanes(monkeypatch, capsys):
task = SimpleNamespace(id="t_healthy", assignee="cli-auto", status="ready")
class Connection:
def __init__(self, board):
self.board = board
def close(self):
return None
def recompute_ready(connection):
if connection.board == "cassandra":
raise lanes.sqlite3.OperationalError("disk I/O error")
fake_db = SimpleNamespace(
list_boards=lambda include_archived=False: [
{"slug": "cassandra"},
{"slug": "healthy"},
],
scoped_current_board=lambda _board: nullcontext(),
connect=lambda board: Connection(board),
recompute_ready=recompute_ready,
list_tasks=lambda _conn: [task],
claim_task=lambda _conn, _task_id, **_kwargs: task,
)
monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=fake_db))
lanes.BOARD_CORRUPTION_ERRORS.clear()
assert lanes.claim_ready(set(), 1) == [("healthy", "t_healthy")]
error = capsys.readouterr().err
assert "temporarily skipping Kanban board 'cassandra'" in error
assert "storage OperationalError: disk I/O error" in error
def test_board_call_retries_storage_faults_on_fresh_connections():
connections = []
class Connection:
def __init__(self):
self.closed = False
def close(self):
self.closed = True
def connect(*, board):
assert board == "cassandra"
connection = Connection()
connections.append(connection)
return connection
attempts = []
def operation(_connection):
attempts.append(1)
if len(attempts) < 3:
raise lanes.sqlite3.OperationalError("disk I/O error")
return "healthy"
fake_db = SimpleNamespace(
scoped_current_board=lambda _board: nullcontext(),
connect=connect,
)
lanes.BOARD_CORRUPTION_ERRORS.clear()
assert lanes._board_call(fake_db, "cassandra", operation) == "healthy"
assert len(connections) == 3
assert all(connection.closed for connection in connections)
@pytest.mark.parametrize(
("result", "expected_action"),
[
(lanes.ProcessResult(0, "plain text only", None, False), "block"),
(
lanes.ProcessResult(
0,
"",
{
"status": "completed",
"summary": "done",
"changed_files": ["src/a.py"],
"tests_run": ["pytest -q"],
"artifacts": ["reports/result.json"],
"blockers": [],
},
False,
),
"complete",
),
(
lanes.ProcessResult(
0,
"",
{
"status": "completed",
"summary": "The full test suite is still running.",
"changed_files": ["src/a.py"],
"tests_run": ["pytest -q — in progress"],
"artifacts": [],
"blockers": [],
},
False,
),
"block",
),
],
)
def test_claim_requires_structured_evidence_and_surfaces_artifacts(
tmp_path: Path,
monkeypatch,
result,
expected_action,
):
task = SimpleNamespace(
id="t_worker",
current_run_id=4,
assignee="cli-auto",
max_runtime_seconds=60,
)
calls = []
heartbeats = []
connections = []
artifact = tmp_path / "reports/result.json"
artifact.parent.mkdir()
artifact.write_text("{}\n", encoding="utf-8")
class Connection:
def __init__(self):
self.closed = False
def close(self):
self.closed = True
def connect(*, board):
assert board == "cassandra"
connection = Connection()
connections.append(connection)
return connection
fake_db = SimpleNamespace(
scoped_current_board=lambda _board: nullcontext(),
connect=connect,
get_task=lambda _conn, _task_id: task,
worker_log_path=lambda _task_id, board: tmp_path / "worker.log",
_resolve_worktree_workspace=lambda _task, board: (tmp_path, "wt/t_worker"),
set_branch_name=lambda *_args: None,
set_workspace_path=lambda *_args: None,
build_worker_context=lambda *_args: "bounded objective",
heartbeat_worker=lambda _conn, _task_id, *, note, expected_run_id: (
heartbeats.append((note, expected_run_id)) or True
),
add_comment=lambda *_args: None,
complete_task=lambda *_args, **kwargs: calls.append(("complete", kwargs)),
block_task=lambda *_args, **kwargs: calls.append(("block", kwargs)),
)
monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=fake_db))
monkeypatch.setattr(
lanes, "state_path", lambda _board, _task_id: tmp_path / "state.json"
)
monkeypatch.setattr(
lanes,
"select_route",
lambda *_args, **_kwargs: lanes.Route(
"codex", "gpt-5.6-sol", "high", "codex-high", "jetson", "vote", 1, ()
),
)
def run_provider(*args, **_kwargs):
assert connections[0].closed
before_heartbeat = len(connections)
assert args[6]("working") is True
assert len(connections) == before_heartbeat + 1
assert connections[-1].closed
return result
monkeypatch.setattr(lanes, "run_provider", run_provider)
lanes.execute_claim("cassandra", "t_worker")
assert calls[0][0] == expected_action
assert heartbeats == [("working", 4)]
if expected_action == "complete":
assert calls[0][1]["metadata"]["artifacts"] == [str(artifact)]
assert calls[0][1]["metadata"]["tests_run"] == ["pytest -q"]
else:
assert calls[0][1]["kind"] == "capability"
assert all(connection.closed for connection in connections)
def test_goal_card_continues_after_local_judge_rejects_progress(
tmp_path: Path,
monkeypatch,
):
task = SimpleNamespace(
id="t_goal",
current_run_id=12,
assignee="cli-auto",
max_runtime_seconds=300,
goal_mode=True,
goal_max_turns=3,
)
calls = []
comments = []
class Connection:
def close(self):
return None
fake_db = SimpleNamespace(
scoped_current_board=lambda _board: nullcontext(),
connect=lambda board: Connection(),
get_task=lambda _conn, _task_id: task,
worker_log_path=lambda _task_id, board: tmp_path / "worker.log",
_resolve_worktree_workspace=lambda _task, board: (tmp_path, "wt/t_goal"),
set_branch_name=lambda *_args: None,
set_workspace_path=lambda *_args: None,
build_worker_context=lambda *_args: "Run tests, commit, push, and verify remote HEAD.",
heartbeat_worker=lambda *_args, **_kwargs: True,
add_comment=lambda _conn, _task_id, _author, body: comments.append(body),
complete_task=lambda *_args, **kwargs: calls.append(("complete", kwargs)),
block_task=lambda *_args, **kwargs: calls.append(("block", kwargs)),
)
monkeypatch.setitem(sys.modules, "hermes_cli", SimpleNamespace(kanban_db=fake_db))
monkeypatch.setattr(
lanes,
"state_path",
lambda _board, _task_id: tmp_path / "state.json",
)
claude_low = lanes.Route(
"claude", "claude-fable-5", "low", "claude-low", "jetson", "vote", 1, ()
)
codex_low = lanes.Route(
"codex", "gpt-5.6-luna", "low", "codex-low", "manual", "fallback", 1, ()
)
codex_xhigh = lanes.Route(
"codex", "gpt-5.6-sol", "xhigh", "codex-xhigh", "jetson", "escalated", 1, ()
)
route_calls = []
def select_route(_prompt, assignee, **kwargs):
route_calls.append((assignee, kwargs))
if assignee == "cli-codex-low":
return codex_low
if len(route_calls) == 1:
return claude_low
return codex_xhigh
monkeypatch.setattr(lanes, "select_route", select_route)
monkeypatch.setattr(lanes, "fresh_unavailable_provider", lambda: None)
reports = [
lanes.ProcessResult(1, "authentication expired", None, True),
lanes.ProcessResult(
0,
"first turn",
{
"status": "completed",
"summary": "Focused tests passed.",
"changed_files": ["src/a.py"],
"tests_run": ["pytest focused: passed"],
"artifacts": [],
"blockers": [],
},
False,
),
lanes.ProcessResult(
0,
"second turn",
{
"status": "completed",
"summary": "Full tests passed; commit pushed and remote HEAD verified.",
"changed_files": ["src/a.py"],
"tests_run": ["pytest full: passed"],
"artifacts": [],
"blockers": [],
},
False,
),
]
monkeypatch.setattr(lanes, "run_provider", lambda *_args, **_kwargs: reports.pop(0))
verdicts = iter(
[
(False, "commit, push, and remote verification are missing"),
(True, "all explicit acceptance criteria have evidence"),
]
)
judge_contexts = []
def judge_goal_completion(objective, *_args, **_kwargs):
judge_contexts.append(objective)
return next(verdicts)
monkeypatch.setattr(
lanes.cli_lane_goal,
"judge_goal_completion",
judge_goal_completion,
)
lanes.execute_claim("cassandra", "t_goal")
assert calls[0][0] == "complete"
assert calls[0][1]["metadata"]["goal_turn"] == 2
assert any(
"Goal completion rejected; continuing turn 2/3" in item for item in comments
)
assert any(
"Goal route 2/3: codex/gpt-5.6-sol at xhigh" in item for item in comments
)
assert route_calls[2][1]["exclude_provider"] == "claude"
assert "prior rejected reports" in judge_contexts[1]
assert "commit, push, and remote verification are missing" in judge_contexts[1]
assert reports == []

View File

@ -1,79 +0,0 @@
"""Shared loaders and manifest helpers for Hermes CLI-lane contracts."""
from __future__ import annotations
import importlib.util
import sys
from pathlib import Path
import yaml
ROOT = Path(__file__).parents[2]
SCRIPTS = ROOT / "services/hermes/scripts"
sys.path.insert(0, str(SCRIPTS))
HERMES = ROOT / "services/hermes"
KEYCLOAK = ROOT / "services/keycloak"
FLUX_HERMES = ROOT / "clusters/atlas/flux-system/applications/hermes/kustomization.yaml"
def _load(name: str):
spec = importlib.util.spec_from_file_location(name, SCRIPTS / f"{name}.py")
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
lanes = _load("cli_lane_runner")
policy = _load("claude_command_policy")
migration = _load("migrate_herdr_state")
auth_patch = _load("patch_hermes_auth")
tui_gateway_patch = _load("patch_tui_gateway")
codex_runtime_patch = _load("patch_codex_runtime")
ttyd_patch = _load("patch_ttyd_index")
client_config = _load("configure_agent_clients")
def _agent_deployment() -> dict:
return yaml.safe_load((HERMES / "agent-deployment.yaml").read_text())
def _services() -> dict[str, dict]:
return {
item["metadata"]["name"]: item
for item in yaml.safe_load_all((HERMES / "service.yaml").read_text())
if item
}
def _oauth_deployment(name: str) -> dict:
documents = [
item
for item in yaml.safe_load_all((HERMES / "oauth2-proxy.yaml").read_text())
if item
]
return next(
item
for item in documents
if item["kind"] == "Deployment" and item["metadata"]["name"] == name
)
class _SwitchyardResponse:
"""Minimal context-managed response used by routing contract tests."""
def __init__(self, selected: str, rationale: str = "local classifier vote"):
self.headers = {
"x-model-router-selected-model": selected,
"x-model-router-rationale": rationale,
}
def __enter__(self):
return self
def __exit__(self, *_args):
return False
def read(self):
return b"{}"

View File

@ -1,386 +0,0 @@
"""Operator toolchain and runtime-patch contracts for Hermes CLI lanes."""
from __future__ import annotations
import json
from pathlib import Path
import pytest
import yaml
from testing.tests.test_hermes_cli_lanes_support import (
HERMES,
SCRIPTS,
_agent_deployment,
auth_patch,
client_config,
codex_runtime_patch,
tui_gateway_patch,
ttyd_patch,
)
def test_worker_route_broker_accepts_pod_network_health_checks():
"""Kubelet probes the pod IP, so the broker cannot bind to loopback only."""
script = (SCRIPTS / "worker_route_broker.py").read_text()
assert 'ThreadingHTTPServer(("0.0.0.0", PORT), Handler)' in script
def test_switchyard_network_boundary_allows_vault_bootstrap():
"""The pre-populate init container must reach Vault before routing starts."""
documents = [
item
for item in yaml.safe_load_all((HERMES / "networkpolicy.yaml").read_text())
if item
]
isolation = next(
item
for item in documents
if item.get("metadata", {}).get("name") == "hermes-switchyard-isolation"
)
assert any(
rule.get("to")
== [
{
"namespaceSelector": {
"matchLabels": {"kubernetes.io/metadata.name": "vault"}
},
"podSelector": {"matchLabels": {"app": "vault"}},
}
]
and rule.get("ports") == [{"protocol": "TCP", "port": 8200}]
for rule in isolation["spec"]["egress"]
)
def test_switchyard_network_boundary_allows_metrics_scraping():
"""VictoriaMetrics may scrape Switchyard without widening its API boundary."""
documents = [
item
for item in yaml.safe_load_all((HERMES / "networkpolicy.yaml").read_text())
if item
]
isolation = next(
item
for item in documents
if item.get("metadata", {}).get("name") == "hermes-switchyard-isolation"
)
assert any(
rule.get("from")
== [
{
"namespaceSelector": {
"matchLabels": {"kubernetes.io/metadata.name": "monitoring"}
},
"podSelector": {"matchLabels": {"app": "server"}},
}
]
and rule.get("ports") == [{"protocol": "TCP", "port": 9005}]
for rule in isolation["spec"]["ingress"]
)
def test_owner_agent_installs_the_pinned_operator_toolchain():
script = (SCRIPTS / "install_agent_tools.sh").read_text()
for value in [
"flux",
"helm",
"kustomize",
"jq",
"yq",
"gh",
"vault",
"sops",
"age",
"age-keygen",
"k9s",
"terraform",
"go",
"gofmt",
]:
assert value in script
assert "go1.26.5.linux-arm64.tar.gz" in script
assert "fe4789e92b1f33358680864bbe8704289e7bb5fc207d80623c308935bd696d49" in script
assert script.count("sha256sum -c -") == 1
deployment = _agent_deployment()
installer = next(
item
for item in deployment["spec"]["template"]["spec"]["initContainers"]
if item["name"] == "install-agent-tools"
)
assert "/bin/sh /opt/coordinator/install_agent_tools.sh" in installer["command"][2]
assert any(mount["name"] == "coordinator" for mount in installer["volumeMounts"])
init_config = next(
item
for item in deployment["spec"]["template"]["spec"]["initContainers"]
if item["name"] == "init-config"
)
init_command = init_config["command"][2]
assert "# Hermes managed operator PATH." in init_command
assert "/opt/coordinator:/opt/data/tools/bin:/opt/hermes/.venv/bin" in init_command
assert 'chmod 0644 "${profile_file}"' in init_command
def test_owner_agent_uses_only_the_canonical_hostname():
paths = [
HERMES / "agent-configmap.yaml",
HERMES / "agent-deployment.yaml",
HERMES / "agent-ingress.yaml",
Path(__file__).parents[2] / "scripts/ops/hermes_triage_monitor.py",
]
for path in paths:
content = path.read_text()
assert "agent.bstein.dev" not in content
assert "agent.hermes.bstein.dev" in content
def test_agent_reconnect_retains_complete_history_and_long_tool_budget():
configmap = yaml.safe_load((HERMES / "agent-configmap.yaml").read_text())
config = yaml.safe_load(configmap["data"]["config.yaml"])
display = config["display"]
assert display["resume_exchanges"] >= 10000
assert display["resume_max_user_chars"] >= 10000000
assert display["resume_max_assistant_chars"] >= 10000000
assert config["agent"]["max_turns"] == 180
assert config["delegation"]["max_iterations"] == 120
def test_auth_patch_honors_explicit_shared_store(tmp_path: Path):
source = tmp_path / "auth.py"
destination = tmp_path / "patched/auth.py"
source.write_text(
'from pathlib import Path\nimport os\n\ndef _auth_file_path() -> Path:\n path = get_hermes_home() / "auth.json"\n return path\n',
encoding="utf-8",
)
auth_patch.patch(source, destination)
content = destination.read_text()
assert 'os.environ.get("HERMES_AUTH_FILE"' in content
def test_auth_patch_fails_closed_on_upstream_drift(tmp_path: Path):
source = tmp_path / "auth.py"
source.write_text("def changed():\n pass\n", encoding="utf-8")
with pytest.raises(RuntimeError, match="context changed"):
auth_patch.patch(source, tmp_path / "patched.py")
def test_codex_runtime_patch_uses_cli_and_forwards_route(tmp_path: Path):
provider = tmp_path / "runtime_provider.py"
provider.write_text(codex_runtime_patch.PROVIDER_BEFORE, encoding="utf-8")
provider_out = tmp_path / "patched/runtime_provider.py"
codex_runtime_patch.patch_provider(provider, provider_out)
assert '"api_mode": "codex_app_server"' in provider_out.read_text()
session = tmp_path / "codex_app_server_session.py"
session.write_text(
codex_runtime_patch.SESSION_SIGNATURE_BEFORE
+ codex_runtime_patch.SESSION_REQUEST_BEFORE,
encoding="utf-8",
)
session_out = tmp_path / "patched/codex_app_server_session.py"
codex_runtime_patch.patch_session(session, session_out)
session_content = session_out.read_text()
assert 'turn_params["model"] = model' in session_content
assert 'turn_params["effort"] = effort' in session_content
assert '"approvalPolicy": "never"' in session_content
assert '"sandboxPolicy": {"type": "dangerFullAccess"}' in session_content
turn = tmp_path / "codex_runtime.py"
turn.write_text(
codex_runtime_patch.FALLBACK_CONTEXT_BEFORE + codex_runtime_patch.TURN_BEFORE,
encoding="utf-8",
)
turn_out = tmp_path / "patched/codex_runtime.py"
codex_runtime_patch.patch_turn(turn, turn_out)
turn_content = turn_out.read_text()
assert "model=str(getattr(agent" in turn_content
assert "build_cross_provider_codex_prompt" in turn_content
fallback = tmp_path / "chat_completion_helpers.py"
fallback.write_text(
codex_runtime_patch.FALLBACK_RESOLUTION_BEFORE,
encoding="utf-8",
)
fallback_out = tmp_path / "patched/chat_completion_helpers.py"
codex_runtime_patch.patch_fallback(fallback, fallback_out)
fallback_content = fallback_out.read_text()
assert 'agent.api_mode = "codex_app_server"' in fallback_content
assert "agent._codex_cross_provider_fallback = True" in fallback_content
loop = tmp_path / "conversation_loop.py"
loop.write_text(
codex_runtime_patch.FALLBACK_DISPATCH_BEFORE
+ codex_runtime_patch.RETRY_FALLBACK_DISPATCH_BEFORE
+ codex_runtime_patch.STREAM_RECOVERY_BEFORE,
encoding="utf-8",
)
loop_out = tmp_path / "patched/conversation_loop.py"
codex_runtime_patch.patch_loop(loop, loop_out)
loop_content = loop_out.read_text()
assert loop_content.count('if agent.api_mode == "codex_app_server"') == 2
assert "build_cross_provider_codex_prompt" in loop_content
retry_dispatch = loop_content.index(
"Fallback activation happens inside this retry loop"
)
api_kwargs = loop_content.find("agent._build_api_kwargs", retry_dispatch)
assert api_kwargs == -1 or retry_dispatch < api_kwargs
assert "Provider stream ended before a complete response" in loop_content
assert "_is_transport_stub" in loop_content
assert "rerouting ({truncated_tool_call_retries}/4)" in loop_content
auxiliary = tmp_path / "auxiliary_client.py"
auxiliary.write_text(
codex_runtime_patch.AUXILIARY_TOKEN_BEFORE,
encoding="utf-8",
)
auxiliary_out = tmp_path / "patched/auxiliary_client.py"
codex_runtime_patch.patch_auxiliary(auxiliary, auxiliary_out)
auxiliary_content = auxiliary_out.read_text()
assert 'os.environ.get("CODEX_HOME"' in auxiliary_content
assert 'Path(codex_home).expanduser() / "auth.json"' in auxiliary_content
assert "never creates a metered API-key lane" in auxiliary_content
def test_agent_mounts_codex_auxiliary_runtime_patch():
deployment = _agent_deployment()
pod = deployment["spec"]["template"]["spec"]
patch_init = next(
item for item in pod["initContainers"] if item["name"] == "patch-codex-runtime"
)
assert patch_init["command"][-2:] == [
"/opt/hermes/agent/auxiliary_client.py",
"/patched/auxiliary_client.py",
]
expected_mount = {
"name": "codex-runtime-patch",
"mountPath": "/opt/hermes/agent/auxiliary_client.py",
"subPath": "auxiliary_client.py",
}
containers = {item["name"]: item for item in pod["containers"]}
for name in ("hermes", "terminal"):
assert expected_mount in containers[name]["volumeMounts"]
def test_codex_auxiliary_patch_reads_cli_token_without_copying_it(
tmp_path: Path,
monkeypatch,
):
codex_home = tmp_path / ".codex"
codex_home.mkdir()
(codex_home / "auth.json").write_text(
json.dumps({"tokens": {"access_token": "cli-access-token"}}),
encoding="utf-8",
)
monkeypatch.setenv("CODEX_HOME", str(codex_home))
source = tmp_path / "auxiliary_client.py"
source.write_text(
"import json, logging, os, time\n"
"from pathlib import Path\n"
"logger = logging.getLogger(__name__)\n"
"def read_token():\n"
" try:\n"
" raise RuntimeError('Hermes provider store intentionally empty')\n"
+ codex_runtime_patch.AUXILIARY_TOKEN_BEFORE,
encoding="utf-8",
)
destination = tmp_path / "patched/auxiliary_client.py"
codex_runtime_patch.patch_auxiliary(source, destination)
namespace: dict = {}
exec(compile(destination.read_text(), str(destination), "exec"), namespace)
assert namespace["read_token"]() == "cli-access-token"
def test_codex_runtime_patch_fails_closed_on_upstream_drift(tmp_path: Path):
source = tmp_path / "runtime_provider.py"
source.write_text("def changed():\n pass\n", encoding="utf-8")
with pytest.raises(RuntimeError, match="context changed"):
codex_runtime_patch.patch_provider(source, tmp_path / "patched.py")
def test_codex_runtime_migration_uses_owner_unsafe_mode(tmp_path: Path):
config = tmp_path / "config.yaml"
config.write_text("model: {}\n", encoding="utf-8")
codex_home = tmp_path / ".codex"
calls = []
class Report:
errors = []
@staticmethod
def summary():
return "configured"
def migrate(value, **kwargs):
calls.append((value, kwargs))
return Report()
client_config.configure_codex_runtime(config, migrate, codex_home)
assert calls[0][1]["default_permission_profile"] is None
assert calls[0][1]["codex_home"] == codex_home
content = (codex_home / "config.toml").read_text(encoding="utf-8")
assert 'approval_policy = "never"' in content
assert 'sandbox_mode = "danger-full-access"' in content
assert "default_permissions" not in content
def test_codex_owner_permissions_replace_stale_profile(tmp_path: Path):
config = tmp_path / "config.toml"
config.write_text(
'default_permissions = ":danger-no-sandbox"\n\n[features]\nhooks = true\n',
encoding="utf-8",
)
client_config.configure_codex_owner_permissions(config)
client_config.configure_codex_owner_permissions(config)
content = config.read_text(encoding="utf-8")
assert content.count(client_config.OWNER_PERMISSIONS_BEGIN) == 1
assert content.count('approval_policy = "never"') == 1
assert content.count('sandbox_mode = "danger-full-access"') == 1
assert "default_permissions" not in content
assert "[features]\nhooks = true" in content
def test_tui_gateway_patch_extends_and_bounds_agent_startup(tmp_path: Path):
source = tmp_path / "server.py"
destination = tmp_path / "patched/server.py"
source.write_text(
"import os\n\n" + tui_gateway_patch.BEFORE + "\ndef unchanged():\n pass\n",
encoding="utf-8",
)
tui_gateway_patch.patch(source, destination)
content = destination.read_text(encoding="utf-8")
assert "HERMES_TUI_AGENT_INIT_TIMEOUT_S" in content
assert "configured = 180.0" in content
assert "return max(30.0, min(configured, 900.0))" in content
assert "timeout: float | None = None" in content
assert "ready.wait(timeout=wait_timeout)" in content
assert "def unchanged():" in content
def test_tui_gateway_patch_fails_closed_on_upstream_drift(tmp_path: Path):
source = tmp_path / "server.py"
source.write_text("def changed():\n pass\n", encoding="utf-8")
with pytest.raises(RuntimeError, match="context changed"):
tui_gateway_patch.patch(source, tmp_path / "patched.py")
def test_ttyd_clipboard_and_reconnect_patch_remain_enabled():
source = '<html><body><script>document.execCommand("copy")</script></body></html>'
content = ttyd_patch.patch_html(source)
assert 'id="atlas-ttyd-clipboard"' in content
assert "navigator.clipboard.writeText(text)" in content
assert "class AtlasRecoveringWebSocket" in content
assert "window.location.reload()" in content
assert "event.stopImmediatePropagation()" in content
def test_ttyd_patch_fails_closed_on_upstream_drift():
with pytest.raises(RuntimeError, match="context changed"):
ttyd_patch.patch_html("<html><body>changed</body></html>")

View File

@ -2,20 +2,44 @@
from __future__ import annotations
import importlib.util
import json
import subprocess
import sys
import tomllib
from pathlib import Path
import pytest
import yaml
from testing.tests.test_hermes_coordinator_support import (
SCRIPT,
_base_config,
catalog_resolver,
coordinator,
routing,
)
SCRIPT = Path(__file__).parents[2] / "services/hermes/scripts/hermes_coordinator.py"
sys.path.insert(0, str(SCRIPT.parent))
routing = importlib.import_module("hermes_model_routing")
catalog_resolver = importlib.import_module("routing_catalog")
SPEC = importlib.util.spec_from_file_location("hermes_coordinator", SCRIPT)
assert SPEC and SPEC.loader
coordinator = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = coordinator
SPEC.loader.exec_module(coordinator)
def _base_config() -> dict:
"""Return the minimal coordinator configuration used by routing tests."""
return {
"model": {
"provider": "openai-codex",
"default": "gpt-5.6-terra",
"model": "gpt-5.6-terra",
},
"fallback_providers": [
{"provider": "anthropic", "model": "claude-opus-5"},
dict(routing.LOCAL_FALLBACK),
],
"toolsets": ["kanban"],
"terminal": {"cwd": "/opt/data/workspace"},
}
def test_model_version_and_quality_selection_handle_new_and_small_models():
assert routing.model_version("claude-3-5-sonnet-20241022") == (3, 5)
@ -310,3 +334,184 @@ def test_refresh_writes_non_secret_routing_status(tmp_path: Path, monkeypatch):
assert status["projects"]["cassandra"]["state"] == "ready"
assert status["projects"]["cassandra"]["board_state"] == {"state": "ready"}
assert "do-not-report" not in status_path.read_text(encoding="utf-8")
def test_corrupt_cassandra_board_is_preserved_without_blocking_refresh(
tmp_path: Path, monkeypatch
):
"""A damaged project board degrades Kanban instead of stopping all services."""
class FakeKanbanDbCorruptError(Exception):
def __init__(self):
super().__init__("integrity check failed")
self.db_path = tmp_path / "kanban.db"
self.backup_path = tmp_path / "kanban.db.corrupt.backup"
self.reason = "row out of order"
def raise_corruption(_root: Path) -> None:
raise FakeKanbanDbCorruptError()
monkeypatch.setattr(coordinator, "bootstrap_cassandra", raise_corruption)
monkeypatch.setattr(
coordinator, "_kanban_corruption_type", lambda: FakeKanbanDbCorruptError
)
status = coordinator.bootstrap_cassandra_state(tmp_path)
assert status == {
"state": "corrupt-preserved",
"database": str(tmp_path / "kanban.db"),
"backup": str(tmp_path / "kanban.db.corrupt.backup"),
"reason": "row out of order",
}
def test_unrelated_cassandra_bootstrap_failure_remains_fatal(
tmp_path: Path, monkeypatch
):
"""Only the known fail-closed corruption state may be degraded."""
class FakeKanbanDbCorruptError(Exception):
pass
def raise_permission_error(_root: Path) -> None:
raise PermissionError("cannot access board")
monkeypatch.setattr(coordinator, "bootstrap_cassandra", raise_permission_error)
monkeypatch.setattr(
coordinator, "_kanban_corruption_type", lambda: FakeKanbanDbCorruptError
)
try:
coordinator.bootstrap_cassandra_state(tmp_path)
except PermissionError as error:
assert str(error) == "cannot access board"
else:
raise AssertionError("unrelated board errors must fail the coordinator refresh")
def test_cassandra_workspace_uses_valid_configured_worktree(
tmp_path: Path, monkeypatch
):
"""The coordinator selects an explicit Git worktree but rejects stale paths."""
active = tmp_path / "cassandra-v69"
active.mkdir()
(active / ".git").write_text("gitdir: elsewhere\n", encoding="utf-8")
monkeypatch.setenv("HERMES_CASSANDRA_ACTIVE_WORKTREE", str(active))
assert coordinator.cassandra_workspace() == active
(active / ".git").unlink()
assert coordinator.cassandra_workspace() == coordinator.CASSANDRA_BASE_PATH
@pytest.mark.parametrize(
("current_remote", "repair_action"),
[
("https://scm.bstein.dev/bstein/cassandra.git", "set-url"),
(None, "add"),
(coordinator.CASSANDRA_REMOTE, None),
],
ids=["old-origin", "missing-origin", "canonical-origin"],
)
def test_cassandra_sync_repairs_existing_worktree(
tmp_path: Path, monkeypatch, current_remote: str | None, repair_action: str | None
):
"""Existing worktrees use the canonical Atlas origin before fetching."""
workspace = tmp_path / "cassandra"
(workspace / ".git").mkdir(parents=True)
monkeypatch.setattr(coordinator, "CASSANDRA_BASE_PATH", workspace)
monkeypatch.setattr(coordinator.shutil, "which", lambda _name: "/usr/bin/git")
token_path = tmp_path / "gitea-token"
token_path.write_text("configured\n", encoding="utf-8")
monkeypatch.setenv("HERMES_GITEA_TOKEN_FILE", str(token_path))
commands: list[list[str]] = []
environments: list[dict[str, str]] = []
def run(command, **kwargs):
commands.append(command)
environments.append(kwargs.get("env", {}))
if command[-2:] == ["get-url", "origin"]:
return subprocess.CompletedProcess(
command, 0 if current_remote else 2, stdout=f"{current_remote or ''}\n"
)
return subprocess.CompletedProcess(command, 0)
monkeypatch.setattr(coordinator.subprocess, "run", run)
assert coordinator.sync_cassandra_repo({"GITEA_TOKEN": "must-not-pass"}) == "ready"
repair_commands = [command for command in commands if repair_action in command]
assert bool(repair_commands) is bool(repair_action)
assert commands[-1][-4:] == ["fetch", "--quiet", "--prune", "origin"]
assert all("GITEA_TOKEN" not in environment for environment in environments)
def test_cassandra_sync_repairs_origin_through_broker_without_token(
tmp_path: Path, monkeypatch
):
"""Repository sync uses the credential-isolated broker remote."""
workspace = tmp_path / "cassandra"
(workspace / ".git").mkdir(parents=True)
monkeypatch.setattr(coordinator, "CASSANDRA_BASE_PATH", workspace)
monkeypatch.setattr(coordinator.shutil, "which", lambda _name: "/usr/bin/git")
commands: list[list[str]] = []
def run(command, **_kwargs):
commands.append(command)
stdout = "old-origin\n" if command[-2:] == ["get-url", "origin"] else None
return subprocess.CompletedProcess(command, 0, stdout=stdout)
monkeypatch.setattr(coordinator.subprocess, "run", run)
state = coordinator.sync_cassandra_repo({})
assert state == "ready"
assert any(
"hermes-scm-broker.hermes-scm.svc.cluster.local" in " ".join(cmd)
for cmd in commands
)
assert all("token" not in " ".join(cmd).lower() for cmd in commands)
assert commands[-1][-4:] == ["fetch", "--quiet", "--prune", "origin"]
def test_migrate_open_cassandra_tasks_preserves_running_and_done_tasks():
"""A project switch moves queued work without relocating active evidence."""
class Task:
def __init__(self, task_id: str, status: str):
self.id = task_id
self.status = status
self.workspace_kind = "dir"
self.workspace_path = str(coordinator.CASSANDRA_BASE_PATH)
class Closing:
def __enter__(self):
return object()
def __exit__(self, *_args):
return None
class FakeKanban:
tasks = [
Task("queued", "todo"),
Task("active", "running"),
Task("done", "done"),
]
moved: list[tuple[str, str]] = []
@staticmethod
def connect_closing(*, board: str):
assert board == "cassandra"
return Closing()
@classmethod
def list_tasks(cls, _connection, *, include_archived: bool):
assert include_archived is False
return cls.tasks
@classmethod
def set_workspace_path(cls, _connection, task_id: str, path: Path):
cls.moved.append((task_id, str(path)))
active = Path("/opt/data/workspace/projects/cassandra-hermes-v69")
coordinator._migrate_open_cassandra_tasks(FakeKanban, active)
assert FakeKanban.moved == [("queued", str(active))]

View File

@ -1,191 +0,0 @@
"""Cassandra workspace and board contracts for the Hermes coordinator."""
from __future__ import annotations
import subprocess
from pathlib import Path
import pytest
from testing.tests.test_hermes_coordinator_support import coordinator
def test_corrupt_cassandra_board_is_preserved_without_blocking_refresh(
tmp_path: Path, monkeypatch
):
"""A damaged project board degrades Kanban instead of stopping all services."""
class FakeKanbanDbCorruptError(Exception):
def __init__(self):
super().__init__("integrity check failed")
self.db_path = tmp_path / "kanban.db"
self.backup_path = tmp_path / "kanban.db.corrupt.backup"
self.reason = "row out of order"
def raise_corruption(_root: Path) -> None:
raise FakeKanbanDbCorruptError()
monkeypatch.setattr(coordinator, "bootstrap_cassandra", raise_corruption)
monkeypatch.setattr(
coordinator, "_kanban_corruption_type", lambda: FakeKanbanDbCorruptError
)
status = coordinator.bootstrap_cassandra_state(tmp_path)
assert status == {
"state": "corrupt-preserved",
"database": str(tmp_path / "kanban.db"),
"backup": str(tmp_path / "kanban.db.corrupt.backup"),
"reason": "row out of order",
}
def test_unrelated_cassandra_bootstrap_failure_remains_fatal(
tmp_path: Path, monkeypatch
):
"""Only the known fail-closed corruption state may be degraded."""
class FakeKanbanDbCorruptError(Exception):
pass
def raise_permission_error(_root: Path) -> None:
raise PermissionError("cannot access board")
monkeypatch.setattr(coordinator, "bootstrap_cassandra", raise_permission_error)
monkeypatch.setattr(
coordinator, "_kanban_corruption_type", lambda: FakeKanbanDbCorruptError
)
try:
coordinator.bootstrap_cassandra_state(tmp_path)
except PermissionError as error:
assert str(error) == "cannot access board"
else:
raise AssertionError("unrelated board errors must fail the coordinator refresh")
def test_cassandra_workspace_uses_valid_configured_worktree(
tmp_path: Path, monkeypatch
):
"""The coordinator selects an explicit Git worktree but rejects stale paths."""
active = tmp_path / "cassandra-v69"
active.mkdir()
(active / ".git").write_text("gitdir: elsewhere\n", encoding="utf-8")
monkeypatch.setenv("HERMES_CASSANDRA_ACTIVE_WORKTREE", str(active))
assert coordinator.cassandra_workspace() == active
(active / ".git").unlink()
assert coordinator.cassandra_workspace() == coordinator.CASSANDRA_BASE_PATH
@pytest.mark.parametrize(
("current_remote", "repair_action"),
[
("https://scm.bstein.dev/bstein/cassandra.git", "set-url"),
(None, "add"),
(coordinator.CASSANDRA_REMOTE, None),
],
ids=["old-origin", "missing-origin", "canonical-origin"],
)
def test_cassandra_sync_repairs_existing_worktree(
tmp_path: Path, monkeypatch, current_remote: str | None, repair_action: str | None
):
"""Existing worktrees use the canonical Atlas origin before fetching."""
workspace = tmp_path / "cassandra"
(workspace / ".git").mkdir(parents=True)
monkeypatch.setattr(coordinator, "CASSANDRA_BASE_PATH", workspace)
monkeypatch.setattr(coordinator.shutil, "which", lambda _name: "/usr/bin/git")
token_path = tmp_path / "gitea-token"
token_path.write_text("configured\n", encoding="utf-8")
monkeypatch.setenv("HERMES_GITEA_TOKEN_FILE", str(token_path))
commands: list[list[str]] = []
environments: list[dict[str, str]] = []
def run(command, **kwargs):
commands.append(command)
environments.append(kwargs.get("env", {}))
if command[-2:] == ["get-url", "origin"]:
return subprocess.CompletedProcess(
command, 0 if current_remote else 2, stdout=f"{current_remote or ''}\n"
)
return subprocess.CompletedProcess(command, 0)
monkeypatch.setattr(coordinator.subprocess, "run", run)
assert coordinator.sync_cassandra_repo({"GITEA_TOKEN": "must-not-pass"}) == "ready"
repair_commands = [command for command in commands if repair_action in command]
assert bool(repair_commands) is bool(repair_action)
assert commands[-1][-4:] == ["fetch", "--quiet", "--prune", "origin"]
assert all("GITEA_TOKEN" not in environment for environment in environments)
def test_cassandra_sync_repairs_origin_through_broker_without_token(
tmp_path: Path, monkeypatch
):
"""Repository sync uses the credential-isolated broker remote."""
workspace = tmp_path / "cassandra"
(workspace / ".git").mkdir(parents=True)
monkeypatch.setattr(coordinator, "CASSANDRA_BASE_PATH", workspace)
monkeypatch.setattr(coordinator.shutil, "which", lambda _name: "/usr/bin/git")
commands: list[list[str]] = []
def run(command, **_kwargs):
commands.append(command)
stdout = "old-origin\n" if command[-2:] == ["get-url", "origin"] else None
return subprocess.CompletedProcess(command, 0, stdout=stdout)
monkeypatch.setattr(coordinator.subprocess, "run", run)
state = coordinator.sync_cassandra_repo({})
assert state == "ready"
assert any(
"hermes-scm-broker.hermes-scm.svc.cluster.local" in " ".join(cmd)
for cmd in commands
)
assert all("token" not in " ".join(cmd).lower() for cmd in commands)
assert commands[-1][-4:] == ["fetch", "--quiet", "--prune", "origin"]
def test_migrate_open_cassandra_tasks_preserves_running_and_done_tasks():
"""A project switch moves queued work without relocating active evidence."""
class Task:
def __init__(self, task_id: str, status: str):
self.id = task_id
self.status = status
self.workspace_kind = "dir"
self.workspace_path = str(coordinator.CASSANDRA_BASE_PATH)
class Closing:
def __enter__(self):
return object()
def __exit__(self, *_args):
return None
class FakeKanban:
tasks = [
Task("queued", "todo"),
Task("active", "running"),
Task("done", "done"),
]
moved: list[tuple[str, str]] = []
@staticmethod
def connect_closing(*, board: str):
assert board == "cassandra"
return Closing()
@classmethod
def list_tasks(cls, _connection, *, include_archived: bool):
assert include_archived is False
return cls.tasks
@classmethod
def set_workspace_path(cls, _connection, task_id: str, path: Path):
cls.moved.append((task_id, str(path)))
active = Path("/opt/data/workspace/projects/cassandra-hermes-v69")
coordinator._migrate_open_cassandra_tasks(FakeKanban, active)
assert FakeKanban.moved == [("queued", str(active))]

View File

@ -1,35 +0,0 @@
"""Shared loader and configuration for Hermes coordinator contracts."""
from __future__ import annotations
import importlib
import importlib.util
import sys
from pathlib import Path
SCRIPT = Path(__file__).parents[2] / "services/hermes/scripts/hermes_coordinator.py"
sys.path.insert(0, str(SCRIPT.parent))
routing = importlib.import_module("hermes_model_routing")
catalog_resolver = importlib.import_module("routing_catalog")
SPEC = importlib.util.spec_from_file_location("hermes_coordinator", SCRIPT)
assert SPEC and SPEC.loader
coordinator = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = coordinator
SPEC.loader.exec_module(coordinator)
def _base_config() -> dict:
"""Return the minimal coordinator configuration used by routing tests."""
return {
"model": {
"provider": "openai-codex",
"default": "gpt-5.6-terra",
"model": "gpt-5.6-terra",
},
"fallback_providers": [
{"provider": "anthropic", "model": "claude-opus-5"},
dict(routing.LOCAL_FALLBACK),
],
"toolsets": ["kanban"],
"terminal": {"cwd": "/opt/data/workspace"},
}

View File

@ -1,12 +1,82 @@
"""Route and request-boundary contracts for the Atlas Gitea client."""
"""Adversarial contracts for the Atlas-only draft pull-request client."""
from __future__ import annotations
import base64
import copy
import importlib.util
import json
import sys
import urllib.request
from email.message import Message
from pathlib import Path
import pytest
from testing.tests.test_hermes_gitea_support import HEAD_SHA, Response, _load
ROOT = Path(__file__).parents[2]
CLIENT_PATH = ROOT / "services/hermes/scm-common/scripts/gitea_api.py"
HEAD_SHA = "465cf9146b05c174a2a8d310aff6c64be58277b6"
if str(CLIENT_PATH.parent) not in sys.path:
sys.path.insert(0, str(CLIENT_PATH.parent))
def _load():
spec = importlib.util.spec_from_file_location("safe_gitea_api", CLIENT_PATH)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
def _draft_payload(**updates):
payload = {
"base": "main",
"body": "Review evidence",
"head": "hermes/review-fix",
"title": "WIP: Repair review findings",
}
payload.update(updates)
return payload
def _draft_response(**updates):
response = {
"number": 3,
"state": "open",
"draft": True,
"merged": False,
"html_url": "https://scm.bstein.dev/atlas/cassandra/pulls/3",
"url": "https://scm.bstein.dev/atlas/cassandra/pulls/3",
"title": "WIP: Focused fix",
"body": "Review evidence",
"base": {"ref": "main", "repo": {"full_name": "atlas/cassandra"}},
"head": {
"ref": "hermes/fix",
"sha": HEAD_SHA,
"repo": {"full_name": "atlas/cassandra"},
},
}
response.update(updates)
return response
class Response:
def __init__(self, body: object, status: int | None = None):
self.body = body if isinstance(body, bytes) else json.dumps(body).encode()
self.status = status if status is not None else (201 if isinstance(body, dict) else 200)
self.headers = Message()
self.headers["Content-Type"] = "application/json"
def __enter__(self):
return self
def __exit__(self, *_args):
return False
def read(self, limit=-1):
return self.body if limit < 0 else self.body[:limit]
def test_redirect_handler_rejects_cross_origin_with_sentinel_authorization():
client = _load()
@ -287,3 +357,682 @@ def test_maximum_bounded_pr_number_is_readable():
== b"{}"
)
assert called is True
@pytest.mark.parametrize(
("method", "path", "data"),
[
("DELETE", "/api/v1/repos/atlas/cassandra/pulls/4", None),
("POST", "/api/v1/repos/atlas/cassandra/pulls/4/merge", {}),
(
"POST",
"/api/v1/repos/atlas/cassandra/pulls/4/reviews",
{"event": "APPROVED"},
),
("PATCH", "/api/v1/repos/atlas/cassandra/pulls/4", {"title": "WIP: x"}),
("PUT", "/api/v1/repos/atlas/cassandra/branches/main", {}),
],
)
def test_merge_approve_close_delete_update_and_other_mutations_are_rejected(
method: str, path: str, data: object
):
client = _load()
with pytest.raises(client.PolicyError):
client.build_request(
method, path, base_url=client.CANONICAL_BASE_URL, token="secret", data=data
)
@pytest.mark.parametrize(
"ref",
[
"foo/.bar",
"foo/bar.lock/baz",
"foo..bar",
"foo@{bar",
"foo//bar",
"-danger",
"danger.",
"danger~one",
"danger^one",
"danger:one",
"danger one",
],
)
def test_complete_git_ref_validation_rejects_invalid_names(ref: str):
client = _load()
with pytest.raises(client.PolicyError):
client._validate_ref(ref, "head")
def test_git_ref_validation_uses_fixed_trusted_binary():
client = _load()
assert client._validate_ref.__globals__["GIT_BIN"] == "/usr/bin/git"
assert client._validate_ref("hermes/valid-fix", "head") == "hermes/valid-fix"
@pytest.mark.parametrize("field", ["base", "head"])
@pytest.mark.parametrize("oversized", ["r" * 100_000, "🧪" * 128])
def test_oversized_ref_never_invokes_git_request_or_opener(
field: str, oversized: str, monkeypatch
):
client = _load()
git_called = False
request_built = False
opener_called = False
original_build_request = client.build_request
def git_run(*_args, **_kwargs):
nonlocal git_called
git_called = True
raise AssertionError("Git must not receive an oversized ref")
def build_request(*args, **kwargs):
nonlocal request_built
request_built = True
return original_build_request(*args, **kwargs)
def opener(*_args, **_kwargs):
nonlocal opener_called
opener_called = True
return Response(_draft_response())
monkeypatch.setattr(client._validate_ref.__globals__["subprocess"], "run", git_run)
client.build_request = build_request
refs = {"base": "main", "head": "hermes/fix"}
refs[field] = oversized
with pytest.raises(client.PolicyError, match="branch-name limit"):
client.create_draft(
"cassandra",
base=refs["base"],
head=refs["head"],
head_sha=HEAD_SHA,
title="Focused fix",
body="Review evidence",
token="runtime",
opener=opener,
)
assert git_called is False
assert request_built is False
assert opener_called is False
@pytest.mark.parametrize(
("field", "oversized"),
[("repo", "r" * 101), ("title", "🧪" * 200), ("body", "🧪" * 9_000)],
)
def test_other_text_bounds_fail_before_git_request_or_opener(
field: str, oversized: str, monkeypatch
):
client = _load()
git_called = False
request_built = False
opener_called = False
original_build_request = client.build_request
def git_run(*_args, **_kwargs):
nonlocal git_called
git_called = True
raise AssertionError("Git must not run before cheap input bounds")
def build_request(*args, **kwargs):
nonlocal request_built
request_built = True
return original_build_request(*args, **kwargs)
def opener(*_args, **_kwargs):
nonlocal opener_called
opener_called = True
return Response(_draft_response())
monkeypatch.setattr(client._validate_ref.__globals__["subprocess"], "run", git_run)
client.build_request = build_request
values = {
"repo": "cassandra",
"title": "Focused fix",
"body": "Review evidence",
}
values[field] = oversized
with pytest.raises(client.PolicyError):
client.create_draft(
values["repo"],
base="main",
head="hermes/fix",
head_sha=HEAD_SHA,
title=values["title"],
body=values["body"],
token="runtime",
opener=opener,
)
assert git_called is False
assert request_built is False
assert opener_called is False
def test_create_forces_draft_title_and_same_repository_branch_names():
client = _load()
assert (
client.authorize_request(
"POST", "/api/v1/repos/atlas/cassandra/pulls", _draft_payload()
)
== "create-draft"
)
with pytest.raises(client.PolicyError, match="draft-title prefix"):
client.authorize_request(
"POST",
"/api/v1/repos/atlas/cassandra/pulls",
_draft_payload(title="Not a draft"),
)
with pytest.raises(client.PolicyError):
client.authorize_request(
"POST",
"/api/v1/repos/atlas/cassandra/pulls",
_draft_payload(head="someone:branch"),
)
def test_runtime_token_is_only_an_authorization_header():
client = _load()
request = client.build_request(
"POST",
"/api/v1/repos/atlas/cassandra/pulls",
base_url=client.CANONICAL_BASE_URL,
token="do-not-leak",
data=_draft_payload(),
)
assert "do-not-leak" not in request.full_url
assert b"do-not-leak" not in request.data
assert request.get_header("Authorization") == "token do-not-leak"
@pytest.mark.parametrize(
"sensitive",
[
"client_" + "secret=not-a-real-value",
'{"client_' + 'secret":\n"synthetic-value"}',
'{"client_' + 'secret"\n:\n"synthetic-value"}',
'{"client\\u005f' + 'secret":"synthetic-value"}',
"client_" + "secret:\n synthetic-value",
'{"client\\q' + 'secret":"synthetic-value"}',
'{"client\n' + 'secret":"synthetic-value"}',
'"client_' + 'secret"\x0b:\n"synthetic-value"',
"ACCESS_" + "TOKEN = 'not-a-real-value'",
'{"refresh_' + 'token": "not-a-real-value"}',
"private_" + "key: not-a-real-value",
"AWS_SECRET_ACCESS_" + "KEY=not-a-real-value",
"aws_access_key_" + "id: not-a-real-value",
"AWS_SESSION_" + "TOKEN = 'not-a-real-value'",
'{"AccessKey' + 'Id":"not-a-real-value"}',
'{"SecretAccess' + 'Key":"not-a-real-value"}',
'{"Session' + 'Token":"not-a-real-value"}',
"aws-security-" + "token: not-a-real-value",
"Account" + "Key=not-a-real-value",
"SharedAccess" + "Signature: not-a-real-value",
"AZURE_STORAGE_CONNECTION_" + "STRING='not-a-real-value'",
"DefaultEndpointsProtocol=https;AccountName=fake;Account"
+ "Key=not-a-real-value;EndpointSuffix=example",
"?sv=2024-11-04&ss=b&srt=sco&sp=rwdlac&se=2099-01-01&sig=" + "not-a-real-value",
"DOCKER_AUTH_" + "CONFIG='not-a-real-value'",
'{"auths":{"registry.example":{"auth":"bm90LXJlYWw="}}}',
'{"identity' + 'token":"not-a-real-value"}',
'{"type":"service_' + 'account","client_email":"fake@example.test"}',
'{"private_key_' + 'id":"not-a-real-value"}',
'{"client_' + 'email":"fake@example.test"}',
"GOOGLE_CREDENTIALS" + "=not-a-real-value",
"personal_access_" + "token: not-a-real-value",
"GITEA_" + "TOKEN=not-a-real-value",
"gitlab-token" + ": not-a-real-value",
"pat" + "=not-a-real-value",
"Authorization: " + "Bearer not-a-real-credential-value",
"authorization = " + '"Basic not-a-real-credential-value"',
"Bearer" + "=not-a-real-credential-value",
"Basic" + ": not-a-real-credential-value",
"pass" + "word=not-a-real-credential",
"ghp_" + "notarealcredentialvalue123456",
"github_pat_" + "notarealcredentialvalue123456",
"glpat-" + "notarealcredentialvalue123456",
"xoxb-" + "not-a-real-credential-value-123456",
"sk-ant-" + "notarealcredentialvalue123456",
"sk-proj-" + "notarealcredentialvalue123456",
"sk_live_" + "notarealcredentialvalue123456",
"ya29." + "notarealcredentialvalue123456",
"gta_" + "notarealcredentialvalue123456",
"whsec_" + "notarealcredentialvalue123456",
"npm_" + "notarealcredentialvalue123456",
"pypi-" + "notarealcredentialvalue123456789012345",
"hf_" + "notarealcredentialvalue123456",
"SG." + "notarealvalue1234" + ".notarealcredentialvalue123456",
"SK" + "a" * 32,
"https://hooks.slack.com/services/" + "T000/B000/notarealvalue123456",
"https://discord.com/api/webhooks/123456789/" + "notarealcredentialvalue123456",
"https://fake.webhook.office.com/" + "notarealcredentialvalue123456",
"webhook_" + "url=https://example.test/not-real",
"FutureCloudSigning" + "Credential=not-a-real-value",
"future-client-signing-" + "key: not-a-real-value",
"future_client_signing_" + "key='not-a-real-value'",
'{"serviceAccountPrivate' + 'Key":"not-a-real-value"}',
"CONTAINER_REGISTRY_" + "CREDENTIAL=not-a-real-value",
"someWebhookSigning" + "Secret: not-a-real-value",
"client" + "Key=not-a-real-value",
"session" + "Key: not-a-real-value",
"access" + "Id=not-a-real-value",
"credentials" + ": {user: fake}",
"private" + "Key: |",
"nuget_api_" + "key=not-a-real-value",
"oy2" + "a" * 44,
"sk_test_" + "notarealcredentialvalue123456",
"A1b2C3d4E5f6G7h8I9j0K_l-M+n/O=pQ2rS3tU4vW5xY6zZ7aB8cC9d",
"AKIA" + "A" * 16,
"AIza" + "a" * 35,
"-----BEGIN OPENSSH " + "PRIVATE KEY-----",
"ssh-ed25519 " + "bm90YXJlYWxjcmVkZW50aWFsdmFsdWU=",
"eyJnotarealheader." + "notarealpayloadvalue." + "notarealsignature",
],
)
@pytest.mark.parametrize("field", ["title", "body"])
def test_create_rejects_expanded_credential_shapes_before_network(
sensitive: str, field: str
):
client = _load()
called = False
request_built = False
original_build_request = client.build_request
def build_request(*args, **kwargs):
nonlocal request_built
request_built = True
return original_build_request(*args, **kwargs)
def opener(*_args, **_kwargs):
nonlocal called
called = True
return Response(_draft_response())
client.build_request = build_request
values = {"title": "Focused fix", "body": "Review evidence"}
values[field] = sensitive
with pytest.raises(client.PolicyError, match="credential material"):
client.create_draft(
"cassandra",
base="main",
head="hermes/fix",
head_sha=HEAD_SHA,
title=values["title"],
body=values["body"],
token="runtime-sentinel",
opener=opener,
)
assert request_built is False
assert called is False
def test_very_long_compact_body_is_rejected_before_request_or_network():
client = _load()
request_built = False
opener_called = False
original_build_request = client.build_request
def build_request(*args, **kwargs):
nonlocal request_built
request_built = True
return original_build_request(*args, **kwargs)
def opener(*_args, **_kwargs):
nonlocal opener_called
opener_called = True
return Response(_draft_response())
client.build_request = build_request
with pytest.raises(client.PolicyError, match="credential material"):
client.create_draft(
"cassandra",
base="main",
head="hermes/fix",
head_sha=HEAD_SHA,
title="Focused fix",
body="a" * 300,
token="runtime-sentinel",
opener=opener,
)
assert request_built is False
assert opener_called is False
@pytest.mark.parametrize(
"safe_text",
[
"AWS_SECRET_ACCESS_KEY is injected at runtime",
"Token: reject empty values",
"Authorization = preserve header behavior",
"Password: add regression",
"Review the Authorization header behavior",
"Bearer authentication is required for this route",
"Document Docker auths payload rejection",
"The client_email field belongs to service accounts",
"AccountKey assignments must be blocked",
"This patch changes token validation without including a value",
"FutureCloudSigningCredential handling needs a regression test",
"The clientKey name is documented without an assigned value",
"A SHA-256 digest 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef is evidence",
],
)
def test_credential_policy_keeps_normal_engineering_prose_usable(safe_text: str):
client = _load()
assert client._validate_body(safe_text) == safe_text
assert client._draft_title(safe_text) == f"WIP: {safe_text}"
@pytest.mark.parametrize("field", ["title", "body"])
def test_create_rejects_exact_runtime_token_before_network(field: str):
client = _load()
called = False
request_built = False
original_build_request = client.build_request
def build_request(*args, **kwargs):
nonlocal request_built
request_built = True
return original_build_request(*args, **kwargs)
def opener(*_args, **_kwargs):
nonlocal called
called = True
return Response(_draft_response())
client.build_request = build_request
runtime_token = "exact-random-runtime-sentinel-7b73ac61"
values = {"title": "Focused fix", "body": "Review evidence"}
values[field] = f"Accidental {runtime_token} value"
with pytest.raises(client.PolicyError, match="runtime credential"):
client.create_draft(
"cassandra",
base="main",
head="hermes/fix",
head_sha=HEAD_SHA,
title=values["title"],
body=values["body"],
token=runtime_token,
opener=opener,
)
assert request_built is False
assert called is False
@pytest.mark.parametrize("field", ["repo", "base", "head", "title", "body"])
def test_every_public_field_rejects_exact_runtime_token_before_git_or_network(
field: str, monkeypatch
):
client = _load()
git_called = False
request_built = False
opener_called = False
runtime_token = "runtime-sentinel"
values = {
"repo": "cassandra",
"base": "main",
"head": "hermes/fix",
"title": "Focused fix",
"body": "Review evidence",
}
values[field] = runtime_token
def git_run(*_args, **_kwargs):
nonlocal git_called
git_called = True
raise AssertionError("Git must not run for a credential-bearing field")
def build_request(*_args, **_kwargs):
nonlocal request_built
request_built = True
raise AssertionError("a request must not be built")
def opener(*_args, **_kwargs):
nonlocal opener_called
opener_called = True
raise AssertionError("the opener must not be called")
monkeypatch.setattr(client._validate_ref.__globals__["subprocess"], "run", git_run)
monkeypatch.setattr(client, "build_request", build_request)
with pytest.raises(client.PolicyError, match="runtime credential"):
client.create_draft(
values["repo"],
base=values["base"],
head=values["head"],
head_sha=HEAD_SHA,
title=values["title"],
body=values["body"],
token=runtime_token,
opener=opener,
)
assert git_called is False
assert request_built is False
assert opener_called is False
@pytest.mark.parametrize(
"sensitive",
[
" ".join(["{}"] * 32) + ' {"client_secret":"synthetic-value"}',
"prefix_" + "ghp_" + "notarealcredentialvalue123456_suffix",
"client_secret: correct horse battery staple",
"'client_secret':\n synthetic-value",
],
)
@pytest.mark.parametrize("field", ["title", "body"])
def test_structured_scanner_closes_bounded_and_wrapped_token_bypasses(
sensitive: str, field: str
):
client = _load()
values = {"title": "Focused fix", "body": "Review evidence"}
values[field] = sensitive
called = False
def opener(*_args, **_kwargs):
nonlocal called
called = True
return Response(_draft_response())
with pytest.raises(client.PolicyError, match="credential material"):
client.create_draft(
"cassandra",
base="main",
head="hermes/fix",
head_sha=HEAD_SHA,
title=values["title"],
body=values["body"],
token="runtime-sentinel",
opener=opener,
)
assert called is False
@pytest.mark.parametrize("status", [200, 202, 204, 206])
def test_create_accepts_only_http_201(status: int):
client = _load()
with pytest.raises(client.PolicyError, match="unexpected HTTP status"):
client.create_draft(
"cassandra",
base="main",
head="hermes/fix",
head_sha=HEAD_SHA,
title="Focused fix",
body="Review evidence",
token="runtime",
opener=lambda *_a, **_k: Response(_draft_response(), status=status),
)
@pytest.mark.parametrize("status", [201, 202, 204, 206])
def test_read_accepts_only_http_200(status: int):
client = _load()
with pytest.raises(client.PolicyError, match="unexpected HTTP status"):
client.read(
"/api/v1/repos/atlas/cassandra",
token="runtime",
opener=lambda *_a, **_k: Response(b"{}", status=status),
)
def test_successful_create_validates_each_ref_once(monkeypatch):
client = _load()
calls = []
def git_run(command, **_kwargs):
calls.append(command)
return type("Result", (), {"returncode": 0})()
monkeypatch.setattr(client._validate_ref.__globals__["subprocess"], "run", git_run)
client.create_draft(
"cassandra",
base="main",
head="hermes/fix",
head_sha=HEAD_SHA,
title="Focused fix",
body="Review evidence",
token="runtime",
opener=lambda *_a, **_k: Response(_draft_response()),
)
assert [command[-1] for command in calls] == [
"refs/heads/main",
"refs/heads/hermes/fix",
]
def test_create_verifies_every_server_postcondition():
client = _load()
calls = []
def opener(request, timeout):
calls.append((request, timeout))
return Response(_draft_response())
result = client.create_draft(
"cassandra",
base="main",
head="hermes/fix",
head_sha=HEAD_SHA,
title="Focused fix",
body="Review evidence",
token="runtime",
opener=opener,
)
assert json.loads(result) == _draft_response()
payload = json.loads(calls[0][0].data)
assert payload == {
"base": "main",
"body": "Review evidence",
"head": "hermes/fix",
"title": "WIP: Focused fix",
}
assert calls[0][1] == 30
def test_create_postcondition_rejects_every_material_mismatch():
client = _load()
mutations = [
("number", 0),
("number", 2_147_483_648),
("state", "closed"),
("draft", False),
("merged", True),
("html_url", "https://evil.example/pulls/3"),
("url", "https://evil.example/api/pulls/3"),
("title", "Focused fix"),
("body", "different"),
]
documents = []
for key, value in mutations:
document = _draft_response()
document[key] = value
documents.append(document)
for path, value in [
(("base", "ref"), "master"),
(("base", "repo", "full_name"), "evil/cassandra"),
(("head", "ref"), "other"),
(("head", "sha"), "0" * 40),
(("head", "repo", "full_name"), "evil/cassandra"),
]:
document = copy.deepcopy(_draft_response())
target = document
for key in path[:-1]:
target = target[key]
target[path[-1]] = value
documents.append(document)
for document in documents:
with pytest.raises(client.PolicyError):
client._require_create_response(
json.dumps(document).encode(),
repo="cassandra",
base="main",
head="hermes/fix",
head_sha=HEAD_SHA,
title="WIP: Focused fix",
body="Review evidence",
)
def test_read_response_is_bounded():
client = _load()
with pytest.raises(client.PolicyError, match="safe size limit"):
client.read(
"/api/v1/repos/atlas/cassandra",
token="runtime",
opener=lambda *_a, **_k: Response(b"x" * (client.MAX_RESPONSE_BYTES + 1)),
)
def test_direct_api_rejects_unexpected_success_content_type():
client = _load()
response = Response(b"{}")
response.headers.replace_header("Content-Type", "text/html")
with pytest.raises(client.PolicyError, match="unexpected response type"):
client.read(
"/api/v1/repos/atlas/cassandra",
token="runtime",
opener=lambda *_a, **_k: response,
)
def test_output_redaction_covers_exact_token_and_authorization_header():
client = _load()
raw = b'{"message":"do-not-leak","debug":"Authorization: token do-not-leak"}'
redacted = client.redact_bytes(raw, "do-not-leak")
assert b"do-not-leak" not in redacted
assert redacted.count(b"[REDACTED]") >= 1
@pytest.mark.parametrize(
"reflected",
[
b"runtime-sentinel",
base64.b64encode(b"runtime-sentinel"),
base64.b64encode(b"hermes-automation:runtime-sentinel"),
],
)
def test_direct_api_rejects_credential_reflection(reflected: bytes):
client = _load()
with pytest.raises(client.PolicyError, match="credential material"):
client.read(
"/api/v1/repos/atlas/cassandra",
token="runtime-sentinel",
opener=lambda *_a, **_k: Response(
b'{"unexpected":"' + reflected + b'"}'
),
)

View File

@ -1,203 +0,0 @@
"""Draft-creation input contracts for the Atlas Gitea client."""
from __future__ import annotations
import pytest
from testing.tests.test_hermes_gitea_support import (
HEAD_SHA,
Response,
_draft_payload,
_draft_response,
_load,
)
@pytest.mark.parametrize(
("method", "path", "data"),
[
("DELETE", "/api/v1/repos/atlas/cassandra/pulls/4", None),
("POST", "/api/v1/repos/atlas/cassandra/pulls/4/merge", {}),
(
"POST",
"/api/v1/repos/atlas/cassandra/pulls/4/reviews",
{"event": "APPROVED"},
),
("PATCH", "/api/v1/repos/atlas/cassandra/pulls/4", {"title": "WIP: x"}),
("PUT", "/api/v1/repos/atlas/cassandra/branches/main", {}),
],
)
def test_merge_approve_close_delete_update_and_other_mutations_are_rejected(
method: str, path: str, data: object
):
client = _load()
with pytest.raises(client.PolicyError):
client.build_request(
method, path, base_url=client.CANONICAL_BASE_URL, token="secret", data=data
)
@pytest.mark.parametrize(
"ref",
[
"foo/.bar",
"foo/bar.lock/baz",
"foo..bar",
"foo@{bar",
"foo//bar",
"-danger",
"danger.",
"danger~one",
"danger^one",
"danger:one",
"danger one",
],
)
def test_complete_git_ref_validation_rejects_invalid_names(ref: str):
client = _load()
with pytest.raises(client.PolicyError):
client._validate_ref(ref, "head")
def test_git_ref_validation_uses_fixed_trusted_binary():
client = _load()
assert client._validate_ref.__globals__["GIT_BIN"] == "/usr/bin/git"
assert client._validate_ref("hermes/valid-fix", "head") == "hermes/valid-fix"
@pytest.mark.parametrize("field", ["base", "head"])
@pytest.mark.parametrize("oversized", ["r" * 100_000, "🧪" * 128])
def test_oversized_ref_never_invokes_git_request_or_opener(
field: str, oversized: str, monkeypatch
):
client = _load()
git_called = False
request_built = False
opener_called = False
original_build_request = client.build_request
def git_run(*_args, **_kwargs):
nonlocal git_called
git_called = True
raise AssertionError("Git must not receive an oversized ref")
def build_request(*args, **kwargs):
nonlocal request_built
request_built = True
return original_build_request(*args, **kwargs)
def opener(*_args, **_kwargs):
nonlocal opener_called
opener_called = True
return Response(_draft_response())
monkeypatch.setattr(client._validate_ref.__globals__["subprocess"], "run", git_run)
client.build_request = build_request
refs = {"base": "main", "head": "hermes/fix"}
refs[field] = oversized
with pytest.raises(client.PolicyError, match="branch-name limit"):
client.create_draft(
"cassandra",
base=refs["base"],
head=refs["head"],
head_sha=HEAD_SHA,
title="Focused fix",
body="Review evidence",
token="runtime",
opener=opener,
)
assert git_called is False
assert request_built is False
assert opener_called is False
@pytest.mark.parametrize(
("field", "oversized"),
[("repo", "r" * 101), ("title", "🧪" * 200), ("body", "🧪" * 9_000)],
)
def test_other_text_bounds_fail_before_git_request_or_opener(
field: str, oversized: str, monkeypatch
):
client = _load()
git_called = False
request_built = False
opener_called = False
original_build_request = client.build_request
def git_run(*_args, **_kwargs):
nonlocal git_called
git_called = True
raise AssertionError("Git must not run before cheap input bounds")
def build_request(*args, **kwargs):
nonlocal request_built
request_built = True
return original_build_request(*args, **kwargs)
def opener(*_args, **_kwargs):
nonlocal opener_called
opener_called = True
return Response(_draft_response())
monkeypatch.setattr(client._validate_ref.__globals__["subprocess"], "run", git_run)
client.build_request = build_request
values = {
"repo": "cassandra",
"title": "Focused fix",
"body": "Review evidence",
}
values[field] = oversized
with pytest.raises(client.PolicyError):
client.create_draft(
values["repo"],
base="main",
head="hermes/fix",
head_sha=HEAD_SHA,
title=values["title"],
body=values["body"],
token="runtime",
opener=opener,
)
assert git_called is False
assert request_built is False
assert opener_called is False
def test_create_forces_draft_title_and_same_repository_branch_names():
client = _load()
assert (
client.authorize_request(
"POST", "/api/v1/repos/atlas/cassandra/pulls", _draft_payload()
)
== "create-draft"
)
with pytest.raises(client.PolicyError, match="draft-title prefix"):
client.authorize_request(
"POST",
"/api/v1/repos/atlas/cassandra/pulls",
_draft_payload(title="Not a draft"),
)
with pytest.raises(client.PolicyError):
client.authorize_request(
"POST",
"/api/v1/repos/atlas/cassandra/pulls",
_draft_payload(head="someone:branch"),
)
def test_runtime_token_is_only_an_authorization_header():
client = _load()
request = client.build_request(
"POST",
"/api/v1/repos/atlas/cassandra/pulls",
base_url=client.CANONICAL_BASE_URL,
token="do-not-leak",
data=_draft_payload(),
)
assert "do-not-leak" not in request.full_url
assert b"do-not-leak" not in request.data
assert request.get_header("Authorization") == "token do-not-leak"

View File

@ -1,317 +0,0 @@
"""Credential-loss-prevention contracts for Atlas draft creation."""
from __future__ import annotations
import pytest
from testing.tests.test_hermes_gitea_support import (
HEAD_SHA,
Response,
_draft_response,
_load,
)
@pytest.mark.parametrize(
"sensitive",
[
"client_" + "secret=not-a-real-value",
'{"client_' + 'secret":\n"synthetic-value"}',
'{"client_' + 'secret"\n:\n"synthetic-value"}',
'{"client\\u005f' + 'secret":"synthetic-value"}',
"client_" + "secret:\n synthetic-value",
'{"client\\q' + 'secret":"synthetic-value"}',
'{"client\n' + 'secret":"synthetic-value"}',
'"client_' + 'secret"\x0b:\n"synthetic-value"',
"ACCESS_" + "TOKEN = 'not-a-real-value'",
'{"refresh_' + 'token": "not-a-real-value"}',
"private_" + "key: not-a-real-value",
"AWS_SECRET_ACCESS_" + "KEY=not-a-real-value",
"aws_access_key_" + "id: not-a-real-value",
"AWS_SESSION_" + "TOKEN = 'not-a-real-value'",
'{"AccessKey' + 'Id":"not-a-real-value"}',
'{"SecretAccess' + 'Key":"not-a-real-value"}',
'{"Session' + 'Token":"not-a-real-value"}',
"aws-security-" + "token: not-a-real-value",
"Account" + "Key=not-a-real-value",
"SharedAccess" + "Signature: not-a-real-value",
"AZURE_STORAGE_CONNECTION_" + "STRING='not-a-real-value'",
"DefaultEndpointsProtocol=https;AccountName=fake;Account"
+ "Key=not-a-real-value;EndpointSuffix=example",
"?sv=2024-11-04&ss=b&srt=sco&sp=rwdlac&se=2099-01-01&sig=" + "not-a-real-value",
"DOCKER_AUTH_" + "CONFIG='not-a-real-value'",
'{"auths":{"registry.example":{"auth":"bm90LXJlYWw="}}}',
'{"identity' + 'token":"not-a-real-value"}',
'{"type":"service_' + 'account","client_email":"fake@example.test"}',
'{"private_key_' + 'id":"not-a-real-value"}',
'{"client_' + 'email":"fake@example.test"}',
"GOOGLE_CREDENTIALS" + "=not-a-real-value",
"personal_access_" + "token: not-a-real-value",
"GITEA_" + "TOKEN=not-a-real-value",
"gitlab-token" + ": not-a-real-value",
"pat" + "=not-a-real-value",
"Authorization: " + "Bearer not-a-real-credential-value",
"authorization = " + '"Basic not-a-real-credential-value"',
"Bearer" + "=not-a-real-credential-value",
"Basic" + ": not-a-real-credential-value",
"pass" + "word=not-a-real-credential",
"ghp_" + "notarealcredentialvalue123456",
"github_pat_" + "notarealcredentialvalue123456",
"glpat-" + "notarealcredentialvalue123456",
"xoxb-" + "not-a-real-credential-value-123456",
"sk-ant-" + "notarealcredentialvalue123456",
"sk-proj-" + "notarealcredentialvalue123456",
"sk_live_" + "notarealcredentialvalue123456",
"ya29." + "notarealcredentialvalue123456",
"gta_" + "notarealcredentialvalue123456",
"whsec_" + "notarealcredentialvalue123456",
"npm_" + "notarealcredentialvalue123456",
"pypi-" + "notarealcredentialvalue123456789012345",
"hf_" + "notarealcredentialvalue123456",
"SG." + "notarealvalue1234" + ".notarealcredentialvalue123456",
"SK" + "a" * 32,
"https://hooks.slack.com/services/" + "T000/B000/notarealvalue123456",
"https://discord.com/api/webhooks/123456789/" + "notarealcredentialvalue123456",
"https://fake.webhook.office.com/" + "notarealcredentialvalue123456",
"webhook_" + "url=https://example.test/not-real",
"FutureCloudSigning" + "Credential=not-a-real-value",
"future-client-signing-" + "key: not-a-real-value",
"future_client_signing_" + "key='not-a-real-value'",
'{"serviceAccountPrivate' + 'Key":"not-a-real-value"}',
"CONTAINER_REGISTRY_" + "CREDENTIAL=not-a-real-value",
"someWebhookSigning" + "Secret: not-a-real-value",
"client" + "Key=not-a-real-value",
"session" + "Key: not-a-real-value",
"access" + "Id=not-a-real-value",
"credentials" + ": {user: fake}",
"private" + "Key: |",
"nuget_api_" + "key=not-a-real-value",
"oy2" + "a" * 44,
"sk_test_" + "notarealcredentialvalue123456",
"A1b2C3d4E5f6G7h8I9j0K_l-M+n/O=pQ2rS3tU4vW5xY6zZ7aB8cC9d",
"AKIA" + "A" * 16,
"AIza" + "a" * 35,
"-----BEGIN OPENSSH " + "PRIVATE KEY-----",
"ssh-ed25519 " + "bm90YXJlYWxjcmVkZW50aWFsdmFsdWU=",
"eyJnotarealheader." + "notarealpayloadvalue." + "notarealsignature",
],
)
@pytest.mark.parametrize("field", ["title", "body"])
def test_create_rejects_expanded_credential_shapes_before_network(
sensitive: str, field: str
):
client = _load()
called = False
request_built = False
original_build_request = client.build_request
def build_request(*args, **kwargs):
nonlocal request_built
request_built = True
return original_build_request(*args, **kwargs)
def opener(*_args, **_kwargs):
nonlocal called
called = True
return Response(_draft_response())
client.build_request = build_request
values = {"title": "Focused fix", "body": "Review evidence"}
values[field] = sensitive
with pytest.raises(client.PolicyError, match="credential material"):
client.create_draft(
"cassandra",
base="main",
head="hermes/fix",
head_sha=HEAD_SHA,
title=values["title"],
body=values["body"],
token="runtime-sentinel",
opener=opener,
)
assert request_built is False
assert called is False
def test_very_long_compact_body_is_rejected_before_request_or_network():
client = _load()
request_built = False
opener_called = False
original_build_request = client.build_request
def build_request(*args, **kwargs):
nonlocal request_built
request_built = True
return original_build_request(*args, **kwargs)
def opener(*_args, **_kwargs):
nonlocal opener_called
opener_called = True
return Response(_draft_response())
client.build_request = build_request
with pytest.raises(client.PolicyError, match="credential material"):
client.create_draft(
"cassandra",
base="main",
head="hermes/fix",
head_sha=HEAD_SHA,
title="Focused fix",
body="a" * 300,
token="runtime-sentinel",
opener=opener,
)
assert request_built is False
assert opener_called is False
@pytest.mark.parametrize(
"safe_text",
[
"AWS_SECRET_ACCESS_KEY is injected at runtime",
"Token: reject empty values",
"Authorization = preserve header behavior",
"Password: add regression",
"Review the Authorization header behavior",
"Bearer authentication is required for this route",
"Document Docker auths payload rejection",
"The client_email field belongs to service accounts",
"AccountKey assignments must be blocked",
"This patch changes token validation without including a value",
"FutureCloudSigningCredential handling needs a regression test",
"The clientKey name is documented without an assigned value",
"A SHA-256 digest 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef is evidence",
],
)
def test_credential_policy_keeps_normal_engineering_prose_usable(safe_text: str):
client = _load()
assert client._validate_body(safe_text) == safe_text
assert client._draft_title(safe_text) == f"WIP: {safe_text}"
@pytest.mark.parametrize("field", ["title", "body"])
def test_create_rejects_exact_runtime_token_before_network(field: str):
client = _load()
called = False
request_built = False
original_build_request = client.build_request
def build_request(*args, **kwargs):
nonlocal request_built
request_built = True
return original_build_request(*args, **kwargs)
def opener(*_args, **_kwargs):
nonlocal called
called = True
return Response(_draft_response())
client.build_request = build_request
runtime_token = "exact-random-runtime-sentinel-7b73ac61"
values = {"title": "Focused fix", "body": "Review evidence"}
values[field] = f"Accidental {runtime_token} value"
with pytest.raises(client.PolicyError, match="runtime credential"):
client.create_draft(
"cassandra",
base="main",
head="hermes/fix",
head_sha=HEAD_SHA,
title=values["title"],
body=values["body"],
token=runtime_token,
opener=opener,
)
assert request_built is False
assert called is False
@pytest.mark.parametrize("field", ["repo", "base", "head", "title", "body"])
def test_every_public_field_rejects_exact_runtime_token_before_git_or_network(
field: str, monkeypatch
):
client = _load()
git_called = False
request_built = False
opener_called = False
runtime_token = "runtime-sentinel"
values = {
"repo": "cassandra",
"base": "main",
"head": "hermes/fix",
"title": "Focused fix",
"body": "Review evidence",
}
values[field] = runtime_token
def git_run(*_args, **_kwargs):
nonlocal git_called
git_called = True
raise AssertionError("Git must not run for a credential-bearing field")
def build_request(*_args, **_kwargs):
nonlocal request_built
request_built = True
raise AssertionError("a request must not be built")
def opener(*_args, **_kwargs):
nonlocal opener_called
opener_called = True
raise AssertionError("the opener must not be called")
monkeypatch.setattr(client._validate_ref.__globals__["subprocess"], "run", git_run)
monkeypatch.setattr(client, "build_request", build_request)
with pytest.raises(client.PolicyError, match="runtime credential"):
client.create_draft(
values["repo"],
base=values["base"],
head=values["head"],
head_sha=HEAD_SHA,
title=values["title"],
body=values["body"],
token=runtime_token,
opener=opener,
)
assert git_called is False
assert request_built is False
assert opener_called is False
@pytest.mark.parametrize(
"sensitive",
[
" ".join(["{}"] * 32) + ' {"client_secret":"synthetic-value"}',
"prefix_" + "ghp_" + "notarealcredentialvalue123456_suffix",
"client_secret: correct horse battery staple",
"'client_secret':\n synthetic-value",
],
)
@pytest.mark.parametrize("field", ["title", "body"])
def test_structured_scanner_closes_bounded_and_wrapped_token_bypasses(
sensitive: str, field: str
):
client = _load()
values = {"title": "Focused fix", "body": "Review evidence"}
values[field] = sensitive
called = False
def opener(*_args, **_kwargs):
nonlocal called
called = True
return Response(_draft_response())
with pytest.raises(client.PolicyError, match="credential material"):
client.create_draft(
"cassandra",
base="main",
head="hermes/fix",
head_sha=HEAD_SHA,
title=values["title"],
body=values["body"],
token="runtime-sentinel",
opener=opener,
)
assert called is False

View File

@ -1,199 +0,0 @@
"""Response and postcondition contracts for Atlas draft creation."""
from __future__ import annotations
import base64
import copy
import json
import pytest
from testing.tests.test_hermes_gitea_support import (
HEAD_SHA,
Response,
_draft_response,
_load,
)
@pytest.mark.parametrize("status", [200, 202, 204, 206])
def test_create_accepts_only_http_201(status: int):
client = _load()
with pytest.raises(client.PolicyError, match="unexpected HTTP status"):
client.create_draft(
"cassandra",
base="main",
head="hermes/fix",
head_sha=HEAD_SHA,
title="Focused fix",
body="Review evidence",
token="runtime",
opener=lambda *_a, **_k: Response(_draft_response(), status=status),
)
@pytest.mark.parametrize("status", [201, 202, 204, 206])
def test_read_accepts_only_http_200(status: int):
client = _load()
with pytest.raises(client.PolicyError, match="unexpected HTTP status"):
client.read(
"/api/v1/repos/atlas/cassandra",
token="runtime",
opener=lambda *_a, **_k: Response(b"{}", status=status),
)
def test_successful_create_validates_each_ref_once(monkeypatch):
client = _load()
calls = []
def git_run(command, **_kwargs):
calls.append(command)
return type("Result", (), {"returncode": 0})()
monkeypatch.setattr(client._validate_ref.__globals__["subprocess"], "run", git_run)
client.create_draft(
"cassandra",
base="main",
head="hermes/fix",
head_sha=HEAD_SHA,
title="Focused fix",
body="Review evidence",
token="runtime",
opener=lambda *_a, **_k: Response(_draft_response()),
)
assert [command[-1] for command in calls] == [
"refs/heads/main",
"refs/heads/hermes/fix",
]
def test_create_verifies_every_server_postcondition():
client = _load()
calls = []
def opener(request, timeout):
calls.append((request, timeout))
return Response(_draft_response())
result = client.create_draft(
"cassandra",
base="main",
head="hermes/fix",
head_sha=HEAD_SHA,
title="Focused fix",
body="Review evidence",
token="runtime",
opener=opener,
)
assert json.loads(result) == _draft_response()
payload = json.loads(calls[0][0].data)
assert payload == {
"base": "main",
"body": "Review evidence",
"head": "hermes/fix",
"title": "WIP: Focused fix",
}
assert calls[0][1] == 30
def test_create_postcondition_rejects_every_material_mismatch():
client = _load()
mutations = [
("number", 0),
("number", 2_147_483_648),
("state", "closed"),
("draft", False),
("merged", True),
("html_url", "https://evil.example/pulls/3"),
("url", "https://evil.example/api/pulls/3"),
("title", "Focused fix"),
("body", "different"),
]
documents = []
for key, value in mutations:
document = _draft_response()
document[key] = value
documents.append(document)
for path, value in [
(("base", "ref"), "master"),
(("base", "repo", "full_name"), "evil/cassandra"),
(("head", "ref"), "other"),
(("head", "sha"), "0" * 40),
(("head", "repo", "full_name"), "evil/cassandra"),
]:
document = copy.deepcopy(_draft_response())
target = document
for key in path[:-1]:
target = target[key]
target[path[-1]] = value
documents.append(document)
for document in documents:
with pytest.raises(client.PolicyError):
client._require_create_response(
json.dumps(document).encode(),
repo="cassandra",
base="main",
head="hermes/fix",
head_sha=HEAD_SHA,
title="WIP: Focused fix",
body="Review evidence",
)
def test_read_response_is_bounded():
client = _load()
with pytest.raises(client.PolicyError, match="safe size limit"):
client.read(
"/api/v1/repos/atlas/cassandra",
token="runtime",
opener=lambda *_a, **_k: Response(b"x" * (client.MAX_RESPONSE_BYTES + 1)),
)
def test_direct_api_rejects_unexpected_success_content_type():
client = _load()
response = Response(b"{}")
response.headers.replace_header("Content-Type", "text/html")
with pytest.raises(client.PolicyError, match="unexpected response type"):
client.read(
"/api/v1/repos/atlas/cassandra",
token="runtime",
opener=lambda *_a, **_k: response,
)
def test_output_redaction_covers_exact_token_and_authorization_header():
client = _load()
raw = b'{"message":"do-not-leak","debug":"Authorization: token do-not-leak"}'
redacted = client.redact_bytes(raw, "do-not-leak")
assert b"do-not-leak" not in redacted
assert redacted.count(b"[REDACTED]") >= 1
@pytest.mark.parametrize(
"reflected",
[
b"runtime-sentinel",
base64.b64encode(b"runtime-sentinel"),
base64.b64encode(b"hermes-automation:runtime-sentinel"),
],
)
def test_direct_api_rejects_credential_reflection(reflected: bytes):
client = _load()
with pytest.raises(client.PolicyError, match="credential material"):
client.read(
"/api/v1/repos/atlas/cassandra",
token="runtime-sentinel",
opener=lambda *_a, **_k: Response(
b'{"unexpected":"' + reflected + b'"}'
),
)

View File

@ -1,75 +0,0 @@
"""Shared fixtures for the Atlas-only Gitea client contracts."""
from __future__ import annotations
import importlib.util
import json
import sys
from email.message import Message
from pathlib import Path
ROOT = Path(__file__).parents[2]
CLIENT_PATH = ROOT / "services/hermes/scm-common/scripts/gitea_api.py"
HEAD_SHA = "465cf9146b05c174a2a8d310aff6c64be58277b6"
if str(CLIENT_PATH.parent) not in sys.path:
sys.path.insert(0, str(CLIENT_PATH.parent))
def _load():
spec = importlib.util.spec_from_file_location("safe_gitea_api", CLIENT_PATH)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
def _draft_payload(**updates):
payload = {
"base": "main",
"body": "Review evidence",
"head": "hermes/review-fix",
"title": "WIP: Repair review findings",
}
payload.update(updates)
return payload
def _draft_response(**updates):
response = {
"number": 3,
"state": "open",
"draft": True,
"merged": False,
"html_url": "https://scm.bstein.dev/atlas/cassandra/pulls/3",
"url": "https://scm.bstein.dev/atlas/cassandra/pulls/3",
"title": "WIP: Focused fix",
"body": "Review evidence",
"base": {"ref": "main", "repo": {"full_name": "atlas/cassandra"}},
"head": {
"ref": "hermes/fix",
"sha": HEAD_SHA,
"repo": {"full_name": "atlas/cassandra"},
},
}
response.update(updates)
return response
class Response:
def __init__(self, body: object, status: int | None = None):
self.body = body if isinstance(body, bytes) else json.dumps(body).encode()
self.status = (
status if status is not None else (201 if isinstance(body, dict) else 200)
)
self.headers = Message()
self.headers["Content-Type"] = "application/json"
def __enter__(self):
return self
def __exit__(self, *_args):
return False
def read(self, limit=-1):
return self.body if limit < 0 else self.body[:limit]

View File

@ -1,20 +1,99 @@
"""Core contracts for the dedicated Hermes node SSH identity."""
"""Contracts for the dedicated, unprivileged Hermes node SSH identity."""
from __future__ import annotations
import base64
import importlib.util
import os
import sys
from pathlib import Path
import pytest
import yaml
from testing.tests.test_hermes_node_account_support import (
ROOT,
SCRIPT,
_fixture,
_key,
_load,
)
ROOT = Path(__file__).parents[2]
SCRIPT = ROOT / "services/hermes/scripts/node_account_hardening.py"
sys.path.insert(0, str(SCRIPT.parent))
def _key(material: bytes, comment: str = "") -> str:
key_type = b"ssh-ed25519"
blob = len(key_type).to_bytes(4, "big") + key_type + material
suffix = f" {comment}" if comment else ""
return "ssh-ed25519 " + base64.b64encode(blob).decode() + suffix
def _typed_key(key_type: str, material: bytes, comment: str = "") -> str:
encoded_type = key_type.encode()
blob = len(encoded_type).to_bytes(4, "big") + encoded_type + material
suffix = f" {comment}" if comment else ""
return f"{key_type} {base64.b64encode(blob).decode()}{suffix}"
def _load():
spec = importlib.util.spec_from_file_location("node_account_hardening_test", SCRIPT)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
def _fixture(tmp_path: Path, monkeypatch):
module = _load()
host_etc = tmp_path / "etc"
host_home = tmp_path / "home"
host_etc.mkdir()
host_home.mkdir()
originals = {
"passwd": (
"root:x:0:0:root:/root:/bin/bash\n"
"atlas:x:2000:2000:Atlas:/home/atlas:/bin/bash\n"
"oceanus:x:2001:2001:Oceanus:/home/oceanus:/bin/bash\n"
),
"group": (
"root:x:0:\n"
"atlas:x:2000:\n"
"oceanus:x:2001:\n"
"disk:x:6:atlas\n"
"sudo:x:27:oceanus\n"
),
"shadow": (
"root:!:1:0:99999:7:::\n"
"atlas:!:1:0:99999:7:::\n"
"oceanus:!:1:0:99999:7:::\n"
),
"gshadow": (
"root:!::\n"
"atlas:!::\n"
"oceanus:!::\n"
"disk:!::atlas\n"
"sudo:!::oceanus\n"
),
}
for name, value in originals.items():
(host_etc / name).write_text(value, encoding="utf-8")
key = _key(b"synthetic-hermes-key")
other = _key(b"human-operator-key")
for user in ("atlas", "oceanus"):
ssh = host_home / user / ".ssh"
ssh.mkdir(parents=True)
(ssh / "authorized_keys").write_text(
f"{other} {user}\n{key}\n", encoding="utf-8"
)
public_key = tmp_path / "public-key"
public_key.write_text(key + "\n", encoding="utf-8")
monkeypatch.setattr(module, "HOST_ETC", host_etc)
monkeypatch.setattr(module, "HOST_HOME", host_home)
monkeypatch.setattr(module, "ACCOUNT_UID", os.getuid())
monkeypatch.setattr(module, "ACCOUNT_GID", os.getgid())
monkeypatch.setattr(module, "HOST_ROOT_UID", os.getuid())
monkeypatch.setattr(module, "HOST_POLKIT_SHARE", tmp_path / "missing-polkit")
# ACL behavior has its own executable test below. These account-database
# tests must not depend on the host running pytest with CAP_FOWNER.
monkeypatch.setattr(module, "_deny_sensitive_roots", lambda: None)
return module, originals, key, other, public_key
def test_reconciler_creates_locked_groupless_account_and_moves_only_exact_key(
tmp_path: Path, monkeypatch
@ -250,3 +329,267 @@ def test_runtime_ssh_config_forces_dedicated_account_for_every_titan():
)
assert "User atlas" not in yaml.safe_dump(agent)
assert "User oceanus" not in yaml.safe_dump(agent)
@pytest.mark.parametrize("database,record", [
("group", "disk:x:6:atlas,hermes-agent\n"),
("gshadow", "disk:!:hermes-agent:atlas\n"),
("gshadow", "disk:!::atlas,hermes-agent\n"),
])
def test_unexpected_group_or_gshadow_membership_fails_before_writes(
tmp_path: Path, monkeypatch, database: str, record: str
):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
path = module.HOST_ETC / database
path.write_text(
"\n".join(line for line in originals[database].splitlines() if not line.startswith("disk:"))
+ "\n"
+ record,
encoding="utf-8",
)
before = {name: (module.HOST_ETC / name).read_bytes() for name in originals}
with pytest.raises(module.HardeningError, match="group access"):
module.reconcile(public_key)
assert {name: (module.HOST_ETC / name).read_bytes() for name in originals} == before
@pytest.mark.parametrize(
"relative,value",
[
("sudoers.d/hermes", "hermes-agent ALL=(ALL:ALL) NOPASSWD: ALL\n"),
("sudoers", "ALL ALL=(ALL:ALL) ALL\n"),
(
"polkit-1/rules.d/90-root.rules",
'if (subject.user == "hermes-agent") return polkit.Result.YES;\n',
),
(
"polkit-1/rules.d/90-root.rules",
'unix-user:* org.freedesktop.policykit.exec polkit.Result.YES\n',
),
],
)
def test_sudo_and_root_equivalent_polkit_authority_fail_closed(
tmp_path: Path, monkeypatch, relative: str, value: str
):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
policy = module.HOST_ETC / relative
policy.parent.mkdir(parents=True, exist_ok=True)
policy.write_text(value, encoding="utf-8")
with pytest.raises(module.HardeningError, match="sudo|polkit"):
module.reconcile(public_key)
assert all((module.HOST_ETC / name).read_text() == original for name, original in originals.items())
def test_external_group_authority_source_is_rejected(tmp_path: Path, monkeypatch):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
(module.HOST_ETC / "nsswitch.conf").write_text("passwd: files\ngroup: files ldap\n")
with pytest.raises(module.HardeningError, match="external group"):
module.reconcile(public_key)
assert all((module.HOST_ETC / name).read_text() == original for name, original in originals.items())
def test_writable_privilege_policy_is_rejected(tmp_path: Path, monkeypatch):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
sudoers = module.HOST_ETC / "sudoers"
sudoers.write_text("root ALL=(ALL:ALL) ALL\n")
sudoers.chmod(0o666)
with pytest.raises(module.HardeningError, match="unsafe mutation"):
module.reconcile(public_key)
assert all((module.HOST_ETC / name).read_text() == original for name, original in originals.items())
def test_key_identity_ignores_options_and_comments_and_removes_legacy_first(
tmp_path: Path, monkeypatch
):
module, _originals, key, other, public_key = _fixture(tmp_path, monkeypatch)
key_fields = key.split()
decorated = f'restrict,command="echo denied" {key_fields[0]} {key_fields[1]} old-comment'
for user in module.LEGACY_ACCOUNTS:
path = module.HOST_HOME / user / ".ssh/authorized_keys"
path.write_text(f"{other} {user}\n{decorated}\n", encoding="utf-8")
writes: list[Path] = []
real_write = module.atomic_write
def recording_write(path, value, metadata):
writes.append(path)
real_write(path, value, metadata)
monkeypatch.setattr(module, "atomic_write", recording_write)
module.reconcile(public_key)
key_writes = [path for path in writes if path.name == "authorized_keys"]
assert key_writes[-1].parts[-3:] == (module.ACCOUNT, ".ssh", "authorized_keys")
assert all(module.ACCOUNT not in path.parts for path in key_writes[:-1])
def test_unrelated_modern_human_key_type_is_preserved(tmp_path: Path, monkeypatch):
module, _originals, key, _other, public_key = _fixture(tmp_path, monkeypatch)
modern = _typed_key("sk-ssh-ed25519@openssh.com", b"human-security-key", "human")
for user in module.LEGACY_ACCOUNTS:
path = module.HOST_HOME / user / ".ssh/authorized_keys"
path.write_text(f"{modern}\n{key}\n", encoding="utf-8")
module.reconcile(public_key)
for user in module.LEGACY_ACCOUNTS:
path = module.HOST_HOME / user / ".ssh/authorized_keys"
assert path.read_text() == modern + "\n"
def test_crash_during_legacy_key_removal_never_installs_duplicate_key(
tmp_path: Path, monkeypatch
):
module, _originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
real_write = module.atomic_write
key_writes = 0
def crash_on_second_key(path, value, metadata):
nonlocal key_writes
if path.name == "authorized_keys":
key_writes += 1
if key_writes == 2:
raise module.HardeningError("synthetic crash")
real_write(path, value, metadata)
monkeypatch.setattr(module, "atomic_write", crash_on_second_key)
with pytest.raises(module.HardeningError, match="synthetic crash"):
module.reconcile(public_key)
assert not (module.HOST_HOME / module.ACCOUNT / ".ssh/authorized_keys").exists()
def test_preexisting_target_key_is_removed_before_legacy_cleanup_and_reinstalled(
tmp_path: Path, monkeypatch
):
module, _originals, key, _other, public_key = _fixture(tmp_path, monkeypatch)
target = module.HOST_HOME / module.ACCOUNT / ".ssh/authorized_keys"
target.parent.mkdir(parents=True)
target.write_text(key + " existing\n")
writes: list[Path] = []
real_write = module.atomic_write
def recording_write(path, value, metadata):
if path.name == "authorized_keys":
writes.append(path)
real_write(path, value, metadata)
monkeypatch.setattr(module, "atomic_write", recording_write)
module._move_key(public_key)
assert writes[0] == target
assert writes[-1] == target
assert all(path != target for path in writes[1:-1])
assert target.read_text() == key + "\n"
def test_account_database_write_preserves_mode_owner_and_xattrs(
tmp_path: Path, monkeypatch
):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
passwd = module.HOST_ETC / "passwd"
passwd.chmod(0o640)
os.setxattr(passwd, "user.hermes-test", b"preserve")
before = passwd.stat()
module.reconcile(public_key)
after = passwd.stat()
assert after.st_mode & 0o777 == 0o640
assert (after.st_uid, after.st_gid) == (before.st_uid, before.st_gid)
assert os.getxattr(passwd, "user.hermes-test") == b"preserve"
backup = module.HOST_ETC / "passwd.hermes-boundary-backup"
assert os.getxattr(backup, "user.hermes-test") == b"preserve"
assert backup.read_text() == originals["passwd"]
def test_atomic_write_restores_acl_and_security_label_xattrs(tmp_path: Path, monkeypatch):
module = _load()
io_module = sys.modules[module.atomic_write.__module__]
target = tmp_path / "passwd"
target.write_bytes(b"old\n")
metadata = io_module.FileSnapshot(
value=b"old\n",
device=target.stat().st_dev,
inode=target.stat().st_ino,
mode=0o600,
uid=os.getuid(),
gid=os.getgid(),
size=4,
mtime_ns=target.stat().st_mtime_ns,
ctime_ns=target.stat().st_ctime_ns,
xattrs=(
("system.posix_acl_access", b"synthetic-acl"),
("security.selinux", b"synthetic-label"),
("user.audit", b"synthetic-xattr"),
),
)
restored: list[tuple[str, bytes]] = []
monkeypatch.setattr(
io_module.os,
"setxattr",
lambda _fd, name, value: restored.append((name, value)),
)
io_module.atomic_write(target, b"new\n", metadata)
assert target.read_bytes() == b"new\n"
assert restored == list(metadata.xattrs)
def test_concurrent_database_change_is_detected_before_any_account_write(
tmp_path: Path, monkeypatch
):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
real_backup = module.backup_once
backup_count = 0
writes: list[Path] = []
def racing_backup(path, snapshot, maximum):
nonlocal backup_count
result = real_backup(path, snapshot, maximum)
backup_count += 1
if backup_count == 4:
group = module.HOST_ETC / "group"
group.write_text(originals["group"] + "race:x:4000:\n")
return result
monkeypatch.setattr(module, "backup_once", racing_backup)
monkeypatch.setattr(module, "atomic_write", lambda path, *_args: writes.append(path))
with pytest.raises(module.HardeningError, match="concurrent host account change"):
module.reconcile(public_key)
assert writes == []
def test_flux_orders_observer_rbac_before_hermes_prunes_old_authority():
observer = yaml.safe_load(
(ROOT / "clusters/atlas/flux-system/applications/hermes-observer-rbac/kustomization.yaml").read_text()
)
hermes = yaml.safe_load(
(ROOT / "clusters/atlas/flux-system/applications/hermes/kustomization.yaml").read_text()
)
assert "dependsOn" not in observer["spec"]
assert {item["name"] for item in hermes["spec"]["dependsOn"]} >= {
"hermes-observer-rbac",
"hermes-scm-broker",
}
def test_sensitive_root_acl_backups_have_unique_host_path_names(monkeypatch):
module = _load()
seen = []
monkeypatch.setattr(
module,
"_deny_sensitive_root",
lambda path, backup_name=None: seen.append((path, backup_name)),
)
module._deny_sensitive_roots()
assert [name for _path, name in seen] == [
"var-lib-rancher-k3s",
"var-lib-kubelet",
"run-k3s",
"run-containerd",
]
assert len({name for _path, name in seen}) == 4

View File

@ -1,222 +0,0 @@
"""Crash-safety and metadata contracts for Hermes node-account writes."""
from __future__ import annotations
import os
import sys
from pathlib import Path
import pytest
import yaml
from testing.tests.test_hermes_node_account_support import (
ROOT,
_fixture,
_load,
_typed_key,
)
def test_key_identity_ignores_options_and_comments_and_removes_legacy_first(
tmp_path: Path, monkeypatch
):
module, _originals, key, other, public_key = _fixture(tmp_path, monkeypatch)
key_fields = key.split()
decorated = f'restrict,command="echo denied" {key_fields[0]} {key_fields[1]} old-comment'
for user in module.LEGACY_ACCOUNTS:
path = module.HOST_HOME / user / ".ssh/authorized_keys"
path.write_text(f"{other} {user}\n{decorated}\n", encoding="utf-8")
writes: list[Path] = []
real_write = module.atomic_write
def recording_write(path, value, metadata):
writes.append(path)
real_write(path, value, metadata)
monkeypatch.setattr(module, "atomic_write", recording_write)
module.reconcile(public_key)
key_writes = [path for path in writes if path.name == "authorized_keys"]
assert key_writes[-1].parts[-3:] == (module.ACCOUNT, ".ssh", "authorized_keys")
assert all(module.ACCOUNT not in path.parts for path in key_writes[:-1])
def test_unrelated_modern_human_key_type_is_preserved(tmp_path: Path, monkeypatch):
module, _originals, key, _other, public_key = _fixture(tmp_path, monkeypatch)
modern = _typed_key("sk-ssh-ed25519@openssh.com", b"human-security-key", "human")
for user in module.LEGACY_ACCOUNTS:
path = module.HOST_HOME / user / ".ssh/authorized_keys"
path.write_text(f"{modern}\n{key}\n", encoding="utf-8")
module.reconcile(public_key)
for user in module.LEGACY_ACCOUNTS:
path = module.HOST_HOME / user / ".ssh/authorized_keys"
assert path.read_text() == modern + "\n"
def test_crash_during_legacy_key_removal_never_installs_duplicate_key(
tmp_path: Path, monkeypatch
):
module, _originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
real_write = module.atomic_write
key_writes = 0
def crash_on_second_key(path, value, metadata):
nonlocal key_writes
if path.name == "authorized_keys":
key_writes += 1
if key_writes == 2:
raise module.HardeningError("synthetic crash")
real_write(path, value, metadata)
monkeypatch.setattr(module, "atomic_write", crash_on_second_key)
with pytest.raises(module.HardeningError, match="synthetic crash"):
module.reconcile(public_key)
assert not (module.HOST_HOME / module.ACCOUNT / ".ssh/authorized_keys").exists()
def test_preexisting_target_key_is_removed_before_legacy_cleanup_and_reinstalled(
tmp_path: Path, monkeypatch
):
module, _originals, key, _other, public_key = _fixture(tmp_path, monkeypatch)
target = module.HOST_HOME / module.ACCOUNT / ".ssh/authorized_keys"
target.parent.mkdir(parents=True)
target.write_text(key + " existing\n")
writes: list[Path] = []
real_write = module.atomic_write
def recording_write(path, value, metadata):
if path.name == "authorized_keys":
writes.append(path)
real_write(path, value, metadata)
monkeypatch.setattr(module, "atomic_write", recording_write)
module._move_key(public_key)
assert writes[0] == target
assert writes[-1] == target
assert all(path != target for path in writes[1:-1])
assert target.read_text() == key + "\n"
def test_account_database_write_preserves_mode_owner_and_xattrs(
tmp_path: Path, monkeypatch
):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
passwd = module.HOST_ETC / "passwd"
passwd.chmod(0o640)
os.setxattr(passwd, "user.hermes-test", b"preserve")
before = passwd.stat()
module.reconcile(public_key)
after = passwd.stat()
assert after.st_mode & 0o777 == 0o640
assert (after.st_uid, after.st_gid) == (before.st_uid, before.st_gid)
assert os.getxattr(passwd, "user.hermes-test") == b"preserve"
backup = module.HOST_ETC / "passwd.hermes-boundary-backup"
assert os.getxattr(backup, "user.hermes-test") == b"preserve"
assert backup.read_text() == originals["passwd"]
def test_atomic_write_restores_acl_and_security_label_xattrs(tmp_path: Path, monkeypatch):
module = _load()
io_module = sys.modules[module.atomic_write.__module__]
target = tmp_path / "passwd"
target.write_bytes(b"old\n")
metadata = io_module.FileSnapshot(
value=b"old\n",
device=target.stat().st_dev,
inode=target.stat().st_ino,
mode=0o600,
uid=os.getuid(),
gid=os.getgid(),
size=4,
mtime_ns=target.stat().st_mtime_ns,
ctime_ns=target.stat().st_ctime_ns,
xattrs=(
("system.posix_acl_access", b"synthetic-acl"),
("security.selinux", b"synthetic-label"),
("user.audit", b"synthetic-xattr"),
),
)
restored: list[tuple[str, bytes]] = []
monkeypatch.setattr(
io_module.os,
"setxattr",
lambda _fd, name, value: restored.append((name, value)),
)
io_module.atomic_write(target, b"new\n", metadata)
assert target.read_bytes() == b"new\n"
assert restored == list(metadata.xattrs)
def test_concurrent_database_change_is_detected_before_any_account_write(
tmp_path: Path, monkeypatch
):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
real_backup = module.backup_once
backup_count = 0
writes: list[Path] = []
def racing_backup(path, snapshot, maximum):
nonlocal backup_count
result = real_backup(path, snapshot, maximum)
backup_count += 1
if backup_count == 4:
group = module.HOST_ETC / "group"
group.write_text(originals["group"] + "race:x:4000:\n")
return result
monkeypatch.setattr(module, "backup_once", racing_backup)
monkeypatch.setattr(module, "atomic_write", lambda path, *_args: writes.append(path))
with pytest.raises(module.HardeningError, match="concurrent host account change"):
module.reconcile(public_key)
assert writes == []
def test_flux_orders_observer_rbac_before_hermes_prunes_old_authority():
observer = yaml.safe_load(
(ROOT / "clusters/atlas/flux-system/applications/hermes-observer-rbac/kustomization.yaml").read_text()
)
hermes = yaml.safe_load(
(ROOT / "clusters/atlas/flux-system/applications/hermes/kustomization.yaml").read_text()
)
bindings = yaml.safe_load(
(
ROOT
/ "clusters/atlas/flux-system/applications/hermes-observer-bindings/kustomization.yaml"
).read_text()
)
assert "dependsOn" not in observer["spec"]
assert {item["name"] for item in hermes["spec"]["dependsOn"]} >= {
"hermes-observer-rbac",
"hermes-scm-broker",
}
assert {item["name"] for item in bindings["spec"]["dependsOn"]} == {
"hermes-observer-rbac",
"hermes",
}
assert "hermes-observer-bindings" not in {
item["name"] for item in hermes["spec"]["dependsOn"]
}
def test_sensitive_root_acl_backups_have_unique_host_path_names(monkeypatch):
module = _load()
seen = []
monkeypatch.setattr(
module,
"_deny_sensitive_root",
lambda path, backup_name=None: seen.append((path, backup_name)),
)
module._deny_sensitive_roots()
assert [name for _path, name in seen] == [
"var-lib-rancher-k3s",
"var-lib-kubelet",
"run-k3s",
"run-containerd",
]
assert len({name for _path, name in seen}) == 4

View File

@ -1,196 +0,0 @@
"""Host privilege-policy audits for the Hermes node SSH identity."""
from __future__ import annotations
import subprocess
from pathlib import Path
import pytest
from testing.tests.test_hermes_node_account_support import _fixture
@pytest.mark.parametrize(
"database,record",
[
("group", "disk:x:6:atlas,hermes-agent\n"),
("gshadow", "disk:!:hermes-agent:atlas\n"),
("gshadow", "disk:!::atlas,hermes-agent\n"),
],
)
def test_unexpected_group_or_gshadow_membership_fails_before_writes(
tmp_path: Path, monkeypatch, database: str, record: str
):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
path = module.HOST_ETC / database
path.write_text(
"\n".join(
line
for line in originals[database].splitlines()
if not line.startswith("disk:")
)
+ "\n"
+ record,
encoding="utf-8",
)
before = {name: (module.HOST_ETC / name).read_bytes() for name in originals}
with pytest.raises(module.HardeningError, match="group access"):
module.reconcile(public_key)
assert {name: (module.HOST_ETC / name).read_bytes() for name in originals} == before
@pytest.mark.parametrize(
"relative,value",
[
("sudoers.d/hermes", "hermes-agent ALL=(ALL:ALL) NOPASSWD: ALL\n"),
("sudoers", "ALL ALL=(ALL:ALL) ALL\n"),
(
"polkit-1/rules.d/90-root.rules",
'if (subject.user == "hermes-agent") return polkit.Result.YES;\n',
),
(
"polkit-1/rules.d/90-root.rules",
"unix-user:* org.freedesktop.policykit.exec polkit.Result.YES\n",
),
],
)
def test_sudo_and_root_equivalent_polkit_authority_fail_closed(
tmp_path: Path, monkeypatch, relative: str, value: str
):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
policy = module.HOST_ETC / relative
policy.parent.mkdir(parents=True, exist_ok=True)
policy.write_text(value, encoding="utf-8")
with pytest.raises(module.HardeningError, match="sudo|polkit"):
module.reconcile(public_key)
assert all(
(module.HOST_ETC / name).read_text() == original
for name, original in originals.items()
)
def test_external_group_authority_source_is_rejected(tmp_path: Path, monkeypatch):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
(module.HOST_ETC / "nsswitch.conf").write_text("passwd: files\ngroup: files ldap\n")
with pytest.raises(module.HardeningError, match="external group"):
module.reconcile(public_key)
assert all(
(module.HOST_ETC / name).read_text() == original
for name, original in originals.items()
)
@pytest.mark.parametrize("database", ["passwd", "group", "initgroups", "shadow"])
def test_external_identity_authority_sources_fail_closed(
tmp_path: Path, monkeypatch, database: str
):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
(module.HOST_ETC / "nsswitch.conf").write_text(
f"passwd: files\n{database}: files ldap\n", encoding="utf-8"
)
with pytest.raises(module.HardeningError, match="external group/account"):
module.reconcile(public_key)
assert all(
(module.HOST_ETC / name).read_text() == original
for name, original in originals.items()
)
@pytest.mark.parametrize(
"value",
[
"#1200 ALL=(ALL:ALL) NOPASSWD: ALL\n",
"User_Alias HERMES_IDS = #1200\nHERMES_IDS ALL=(ALL:ALL) NOPASSWD: ALL\n",
"User_Alias HERMES_GROUP = %#1200\nHERMES_GROUP ALL=(ALL:ALL) NOPASSWD: ALL\n",
],
)
def test_visudo_valid_numeric_and_alias_grants_fail_closed(
tmp_path: Path, monkeypatch, value: str
):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
monkeypatch.setattr(module, "ACCOUNT_UID", 1200)
policy = module.HOST_ETC / "sudoers"
policy.write_text(value, encoding="utf-8")
validation = subprocess.run(
["/usr/bin/visudo", "-c", "-f", str(policy)],
check=False,
capture_output=True,
text=True,
)
assert validation.returncode == 0, validation.stderr
with pytest.raises(module.HardeningError, match="sudo authority"):
module.reconcile(public_key)
assert all(
(module.HOST_ETC / name).read_text() == original
for name, original in originals.items()
)
def test_sudo_include_is_bounded_to_the_audited_standard_directory(
tmp_path: Path, monkeypatch
):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
sudoers = module.HOST_ETC / "sudoers"
sudoers.write_text("#includedir /opt/external-sudoers\n", encoding="utf-8")
with pytest.raises(module.HardeningError, match="unaudited authority source"):
module.reconcile(public_key)
sudoers.write_text("#includedir /etc/sudoers.d\n", encoding="utf-8")
module.reconcile(public_key)
assert all(
(module.HOST_ETC / name).read_text().startswith(original)
for name, original in originals.items()
)
@pytest.mark.parametrize(
"relative,value",
[
(
"polkit-1/rules.d/90-hermes.rules",
"polkit.addRule(function(action, subject) {\n"
" if (subject.uid == 1200) return polkit.Result.YES;\n"
"});\n",
),
(
"polkit-1/localauthority/50-local.d/hermes.pkla",
"[Hermes]\nIdentity=unix-user:1200\n"
"Action=org.freedesktop.policykit.exec\nResultActive=yes\n",
),
],
)
def test_numeric_polkit_grants_fail_closed(
tmp_path: Path, monkeypatch, relative: str, value: str
):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
monkeypatch.setattr(module, "ACCOUNT_UID", 1200)
policy = module.HOST_ETC / relative
policy.parent.mkdir(parents=True, exist_ok=True)
policy.write_text(value, encoding="utf-8")
with pytest.raises(module.HardeningError, match="polkit authority"):
module.reconcile(public_key)
assert all(
(module.HOST_ETC / name).read_text() == original
for name, original in originals.items()
)
def test_writable_privilege_policy_is_rejected(tmp_path: Path, monkeypatch):
module, originals, _key_value, _other, public_key = _fixture(tmp_path, monkeypatch)
sudoers = module.HOST_ETC / "sudoers"
sudoers.write_text("root ALL=(ALL:ALL) ALL\n")
sudoers.chmod(0o666)
with pytest.raises(module.HardeningError, match="unsafe mutation"):
module.reconcile(public_key)
assert all(
(module.HOST_ETC / name).read_text() == original
for name, original in originals.items()
)

View File

@ -1,90 +0,0 @@
"""Shared fixtures for dedicated Hermes node-account contracts."""
from __future__ import annotations
import base64
import importlib.util
import os
import sys
from pathlib import Path
ROOT = Path(__file__).parents[2]
SCRIPT = ROOT / "services/hermes/scripts/node_account_hardening.py"
sys.path.insert(0, str(SCRIPT.parent))
def _key(material: bytes, comment: str = "") -> str:
key_type = b"ssh-ed25519"
blob = len(key_type).to_bytes(4, "big") + key_type + material
suffix = f" {comment}" if comment else ""
return "ssh-ed25519 " + base64.b64encode(blob).decode() + suffix
def _typed_key(key_type: str, material: bytes, comment: str = "") -> str:
encoded_type = key_type.encode()
blob = len(encoded_type).to_bytes(4, "big") + encoded_type + material
suffix = f" {comment}" if comment else ""
return f"{key_type} {base64.b64encode(blob).decode()}{suffix}"
def _load():
spec = importlib.util.spec_from_file_location("node_account_hardening_test", SCRIPT)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
def _fixture(tmp_path: Path, monkeypatch):
module = _load()
host_etc = tmp_path / "etc"
host_home = tmp_path / "home"
host_etc.mkdir()
host_home.mkdir()
originals = {
"passwd": (
"root:x:0:0:root:/root:/bin/bash\n"
"atlas:x:2000:2000:Atlas:/home/atlas:/bin/bash\n"
"oceanus:x:2001:2001:Oceanus:/home/oceanus:/bin/bash\n"
),
"group": (
"root:x:0:\n"
"atlas:x:2000:\n"
"oceanus:x:2001:\n"
"disk:x:6:atlas\n"
"sudo:x:27:oceanus\n"
),
"shadow": (
"root:!:1:0:99999:7:::\n"
"atlas:!:1:0:99999:7:::\n"
"oceanus:!:1:0:99999:7:::\n"
),
"gshadow": (
"root:!::\n"
"atlas:!::\n"
"oceanus:!::\n"
"disk:!::atlas\n"
"sudo:!::oceanus\n"
),
}
for name, value in originals.items():
(host_etc / name).write_text(value, encoding="utf-8")
key = _key(b"synthetic-hermes-key")
other = _key(b"human-operator-key")
for user in ("atlas", "oceanus"):
ssh = host_home / user / ".ssh"
ssh.mkdir(parents=True)
(ssh / "authorized_keys").write_text(
f"{other} {user}\n{key}\n", encoding="utf-8"
)
public_key = tmp_path / "public-key"
public_key.write_text(key + "\n", encoding="utf-8")
monkeypatch.setattr(module, "HOST_ETC", host_etc)
monkeypatch.setattr(module, "HOST_HOME", host_home)
monkeypatch.setattr(module, "ACCOUNT_UID", os.getuid())
monkeypatch.setattr(module, "ACCOUNT_GID", os.getgid())
monkeypatch.setattr(module, "HOST_ROOT_UID", os.getuid())
monkeypatch.setattr(module, "HOST_POLKIT_SHARE", tmp_path / "missing-polkit")
monkeypatch.setattr(module, "_deny_sensitive_roots", lambda: None)
return module, originals, key, other, public_key

View File

@ -1,14 +1,63 @@
"""Atlas route and Git protocol contracts for the Hermes SCM broker."""
"""Executable contracts for the credential-isolated Hermes SCM broker."""
from __future__ import annotations
import pytest
import importlib.util
import io
import json
import sys
from email.message import Message
from pathlib import Path
import pytest
import yaml
ROOT = Path(__file__).parents[2]
SCRIPTS = ROOT / "services/hermes/scm-common/scripts"
sys.path.insert(0, str(SCRIPTS))
def _load(name: str):
path = SCRIPTS / f"{name}.py"
spec = importlib.util.spec_from_file_location(f"test_{name}", path)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
def _load_path(name: str, path: Path):
spec = importlib.util.spec_from_file_location(name, path)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
class Response:
def __init__(self, body: bytes, *, status: int = 200, content_type: str):
self.body = body
self.stream = io.BytesIO(body)
self.status = status
self.headers = Message()
self.headers["Content-Type"] = content_type
def __enter__(self):
return self
def __exit__(self, *_args):
return False
def read(self, limit=-1):
return self.stream.read(limit)
def _receive_command(old: bytes, new: bytes, ref: bytes) -> bytes:
command = old + b" " + new + b" " + ref + b"\x00report-status\n"
return f"{len(command) + 4:04x}".encode() + command + b"0000PACK"
from testing.tests.test_hermes_scm_broker_support import (
Response,
_load,
_receive_command,
)
@pytest.mark.parametrize(
"target",
@ -112,6 +161,7 @@ def test_git_proxy_uses_fixed_origin_and_never_reflects_credential():
content_type="application/x-git-upload-pack-result",
),
)
encoded = broker._credential_forms("runtime-sentinel")[1]
with pytest.raises(broker.PolicyError, match="credential material"):
broker._upstream_git_request(
@ -126,6 +176,8 @@ def test_git_proxy_uses_fixed_origin_and_never_reflects_credential():
content_type="application/x-git-upload-pack-result",
),
)
@pytest.mark.parametrize("status", [201, 202, 204, 206, 301, 302, 307, 308])
def test_git_proxy_requires_exact_http_200(status: int):
broker = _load("scm_broker")
@ -144,3 +196,396 @@ def test_git_proxy_requires_exact_http_200(status: int):
content_type="application/x-git-upload-pack-result",
),
)
def test_agent_broker_client_sends_no_credential_or_authorization_header():
client = _load("scm_broker_client")
seen = []
def opener(request, timeout):
seen.append((request, timeout))
return Response(b"{}", content_type="application/json")
assert client.read(
"/api/v1/repos/atlas/cassandra", opener=opener
) == b"{}"
request = seen[0][0]
assert request.full_url == client.BROKER_ORIGIN + "/v1/metadata"
assert request.get_header("Authorization") is None
assert json.loads(request.data) == {
"path": "/api/v1/repos/atlas/cassandra"
}
@pytest.mark.parametrize("raw", ["", "01", "9" * 4000, "134217729", "12"])
def test_broker_content_length_is_canonical_and_bounded(raw: str):
broker = _load("scm_broker")
headers = Message()
headers["Content-Length"] = raw
with pytest.raises(broker.PolicyError, match="request length"):
broker._content_length(headers, broker.MAX_GIT_REQUEST)
def test_large_git_exchange_rolls_to_disk_and_scans_chunk_boundaries():
broker = _load("scm_broker")
token = "runtime-sentinel"
safe = b"x" * (broker.SPOOL_MEMORY_LIMIT + 1)
inbound_timeouts = []
spool, length = broker._spool_bounded(
io.BytesIO(safe),
broker.MAX_GIT_REQUEST,
len(safe),
token=token,
context="Git request",
deadline=broker.time.monotonic() + 30,
set_timeout=inbound_timeouts.append,
)
try:
assert length == len(safe)
assert spool._rolled is True
assert spool.read(4) == b"xxxx"
assert inbound_timeouts
assert all(0 < value <= 30 for value in inbound_timeouts)
finally:
spool.close()
crossing = b"x" * (broker.STREAM_CHUNK - 5) + token.encode() + b"tail"
with pytest.raises(broker.PolicyError, match="credential material"):
broker._spool_bounded(
io.BytesIO(crossing),
broker.MAX_GIT_REQUEST,
len(crossing),
token=token,
context="Git request",
deadline=broker.time.monotonic() + 30,
)
def test_large_upstream_response_is_streamed_through_bounded_spool():
broker = _load("scm_broker")
body = b"z" * (broker.SPOOL_MEMORY_LIMIT + 1)
seen = []
def opener(request, **_kwargs):
seen.append(request)
assert request.get_header("Content-length") == "7"
assert request.data.read() == b"request"
return Response(body, content_type="application/x-git-upload-pack-result")
streamed = broker._upstream_git_request(
"/atlas/cassandra.git/git-upload-pack",
method="POST",
body=io.BytesIO(b"request"),
body_length=7,
content_type="application/x-git-upload-pack-request",
expected_type="application/x-git-upload-pack-result",
token="runtime-sentinel",
stream_result=True,
opener=opener,
)
spool, length = streamed
try:
assert length == len(body)
assert spool._rolled is True
assert spool.read(3) == b"zzz"
assert len(seen) == 1
finally:
spool.close()
def test_body_deadline_fails_closed_before_reading():
broker = _load("scm_broker")
with pytest.raises(broker.PolicyError, match="deadline"):
broker._spool_bounded(
io.BytesIO(b"body"),
broker.MAX_GIT_REQUEST,
4,
token="runtime-sentinel",
context="Git request",
deadline=broker.time.monotonic() - 1,
)
def test_upstream_stream_applies_absolute_socket_deadline():
broker = _load("scm_broker")
class DeadlineSocket:
def __init__(self):
self.values = []
def settimeout(self, value):
self.values.append(value)
response = Response(b"safe", content_type="application/x-git-upload-pack-result")
sock = DeadlineSocket()
response.fp = type("FP", (), {"raw": type("Raw", (), {"_sock": sock})()})()
spool, length = broker._spool_response(response, broker.MAX_GIT_RESPONSE, "sentinel")
try:
assert length == 4
assert sock.values
assert all(0 < value <= broker.INBOUND_BODY_TIMEOUT for value in sock.values)
finally:
spool.close()
def test_broker_rejects_work_when_concurrency_slots_are_exhausted():
broker = _load("scm_broker")
class FakeSocket:
def __init__(self):
self.value = b""
def sendall(self, value):
self.value += value
def shutdown(self, _how):
return None
def close(self):
return None
server = broker.BoundedThreadingHTTPServer(("127.0.0.1", 0), broker.BrokerHandler)
try:
assert server._slots.acquire(blocking=False)
assert server._slots.acquire(blocking=False)
request = FakeSocket()
server.process_request(request, ("127.0.0.1", 1))
assert b"503 Service Unavailable" in request.value
finally:
server._slots.release()
server._slots.release()
server.server_close()
def test_broker_bounds_header_count_size_and_duplicate_lengths():
broker = _load("scm_broker")
def validate(headers):
handler = object.__new__(broker.BrokerHandler)
handler.headers = headers
handler._validate_headers()
too_many = Message()
for index in range(broker.MAX_HEADERS + 1):
too_many[f"X-Test-{index}"] = "safe"
with pytest.raises(broker.PolicyError, match="too many headers"):
validate(too_many)
too_large = Message()
too_large["X-Test"] = "x" * broker.MAX_HEADER_BYTES
with pytest.raises(broker.PolicyError, match="safe limit"):
validate(too_large)
duplicate = Message()
duplicate["Content-Length"] = "1"
duplicate["Content-Length"] = "1"
with pytest.raises(broker.PolicyError, match="duplicate Content-Length"):
validate(duplicate)
def _can_i(role: dict, api_group: str, resource: str, verb: str) -> bool:
return any(
(api_group in rule["apiGroups"] or "*" in rule["apiGroups"])
and (resource in rule["resources"] or "*" in rule["resources"])
and (verb in rule["verbs"] or "*" in rule["verbs"])
for rule in role["rules"]
)
def test_agent_rbac_preserves_read_diagnostics_without_administrator_authority():
documents = list(
yaml.safe_load_all(
(ROOT / "services/hermes-observer-rbac/rbac.yaml").read_text(
encoding="utf-8"
)
)
)
cluster_role, namespaced_role, binding = documents
assert binding["roleRef"]["name"] == "hermes-agent-cluster-observer-v2"
assert _can_i(cluster_role, "", "nodes", "get")
assert _can_i(namespaced_role, "", "pods", "get")
assert _can_i(namespaced_role, "", "pods/log", "get")
assert _can_i(
namespaced_role,
"kustomize.toolkit.fluxcd.io",
"kustomizations",
"list",
)
denied = (
("", "secrets", "get"),
("", "serviceaccounts/token", "create"),
("", "pods", "create"),
("", "pods/exec", "create"),
("", "pods/attach", "create"),
("", "pods/portforward", "create"),
("rbac.authorization.k8s.io", "clusterrolebindings", "create"),
("authorization.k8s.io", "selfsubjectaccessreviews", "create"),
("apps", "deployments", "patch"),
)
assert all(
not _can_i(role, *request)
for request in denied
for role in (cluster_role, namespaced_role)
)
bindings = yaml.safe_load(
(ROOT / "services/hermes-observer-rbac/rolebindings.yaml").read_text(
encoding="utf-8"
)
)["items"]
namespaces = {item["metadata"]["namespace"] for item in bindings}
assert "hermes-scm" not in namespaces
assert {"cassandra", "flux-system", "hermes", "kube-system"} <= namespaces
assert all(item["roleRef"]["name"].endswith("namespaced-observer-v2") for item in bindings)
def test_flux_boundary_keeps_broker_secret_and_network_separate_from_agent():
agent = yaml.safe_load(
(ROOT / "services/hermes/agent-deployment.yaml").read_text(encoding="utf-8")
)
annotations = agent["spec"]["template"]["metadata"]["annotations"]
assert not any("gitea" in key.lower() for key in annotations)
assert "GIT_ASKPASS" not in json.dumps(agent)
broker = yaml.safe_load(
(ROOT / "services/hermes-scm-broker/deployment.yaml").read_text(
encoding="utf-8"
)
)
assert broker["metadata"]["namespace"] == "hermes-scm"
assert broker["spec"]["template"]["spec"]["serviceAccountName"] == (
"hermes-scm-broker"
)
assert "gitea-token" in json.dumps(broker)
container = broker["spec"]["template"]["spec"]["containers"][0]
assert container["resources"]["limits"]["memory"] == "768Mi"
assert container["livenessProbe"]["failureThreshold"] == 10
tmp = next(
item for item in broker["spec"]["template"]["spec"]["volumes"] if item["name"] == "tmp"
)
assert tmp["emptyDir"]["sizeLimit"] == "1Gi"
policy = yaml.safe_load(
(ROOT / "services/hermes-scm-broker/networkpolicy.yaml").read_text(
encoding="utf-8"
)
)
ingress = policy["spec"]["ingress"]
assert ingress[0]["from"][0]["namespaceSelector"]["matchLabels"] == {
"kubernetes.io/metadata.name": "hermes"
}
assert ingress[0]["from"][0]["podSelector"]["matchLabels"] == {
"app": "hermes-agent"
}
vault = (
ROOT / "services/vault/scripts/vault_k8s_auth_configure.sh"
).read_text(encoding="utf-8")
assert 'write_policy_and_role "hermes-scm-broker" "hermes-scm"' in vault
assert '"hermes/developer-gitea" ""' in vault
agent_start = vault.index('write_policy_and_role "hermes-agent"')
agent_end = vault.index("write_policy_and_role", agent_start + 1)
assert "developer-gitea" not in vault[agent_start:agent_end]
def test_flux_bootstrap_has_no_agent_namespace_dependency_cycle():
hermes_resources = yaml.safe_load(
(ROOT / "services/hermes/kustomization.yaml").read_text(encoding="utf-8")
)["resources"]
assert "namespace.yaml" in hermes_resources
assert "scm-common" in hermes_resources
applications = (
ROOT / "clusters/atlas/flux-system/applications/kustomization.yaml"
).read_text(encoding="utf-8")
assert "hermes-scm-agent-code" not in applications
broker_code = yaml.safe_load(
(
ROOT
/ "clusters/atlas/flux-system/applications/hermes-scm-broker-code/kustomization.yaml"
).read_text(encoding="utf-8")
)
assert broker_code["spec"]["path"] == "./services/hermes/scm-common"
assert broker_code["spec"]["targetNamespace"] == "hermes-scm"
assert broker_code["spec"]["dependsOn"] == [{"name": "hermes-scm-namespace"}]
def test_gitea_bootstrap_enforces_human_review_without_overwriting_drift():
script = (
ROOT / "services/gitea/scripts/gitea_atlas_identity_ensure.sh"
).read_text(encoding="utf-8")
assert 'required_approvals\\\":1' in script
assert 'enable_push_whitelist\\\":true' in script
assert 'push_whitelist_usernames\\\":[\\\"${protected_reviewer}\\\"]' in script
assert 'enable_merge_whitelist\\\":true' in script
assert 'merge_whitelist_usernames\\\":[\\\"${protected_reviewer}\\\"]' in script
assert "protection differs from the human-review policy" in script
assert "api_request PATCH" not in script
job = yaml.safe_load(
(ROOT / "services/gitea/atlas-identity-bootstrap-job.yaml").read_text()
)
init = job["spec"]["template"]["spec"]["initContainers"][0]
assert init["image"] == (
"python@sha256:6d43704baacd1bfbe7c295d7f13079d5d8104ed33568873133f8fc69980419df"
)
assert init["securityContext"]["runAsNonRoot"] is True
assert init["securityContext"]["capabilities"]["drop"] == ["ALL"]
assert "/opt/python/bin/python3" in script
def _protection(rule_name: str, priority: int, helper, **overrides):
value = {"rule_name": rule_name, "priority": priority, **helper._required("bstein")}
value.update(overrides)
return value
def test_branch_protection_uses_first_effective_glob_by_priority():
helper = _load_path(
"branch_protection_test",
ROOT / "services/gitea/scripts/gitea_branch_protection_check.py",
)
earlier_drift = _protection("m*", 1, helper, required_approvals=0)
later_exact = _protection("main", 2, helper)
with pytest.raises(helper.PolicyError, match="effective main protection differs"):
helper.evaluate(json.dumps([later_exact, earlier_drift]).encode(), "main", "bstein")
earlier_drift["required_approvals"] = 1
assert (
helper.evaluate(json.dumps([later_exact, earlier_drift]).encode(), "main", "bstein")
== "PRESENT"
)
brace_drift = _protection("m{ain,aster}", 1, helper, required_approvals=0)
with pytest.raises(helper.PolicyError, match="effective master protection differs"):
helper.evaluate(json.dumps([brace_drift]).encode(), "master", "bstein")
@pytest.mark.parametrize(
"rules",
[
[{"rule_name": "main", "priority": 1}, {"rule_name": "m*", "priority": 1}],
[{"rule_name": "[main", "priority": 1}],
[{"rule_name": "main", "priority": 0}],
[{"rule_name": "main"}],
],
)
def test_branch_protection_rejects_ambiguous_rules_before_creation(rules):
helper = _load_path(
"branch_protection_adversarial_test",
ROOT / "services/gitea/scripts/gitea_branch_protection_check.py",
)
with pytest.raises(helper.PolicyError):
helper.evaluate(json.dumps(rules).encode(), "main", "bstein")
def test_branch_protection_reports_absent_only_when_no_rule_matches():
helper = _load_path(
"branch_protection_absent_test",
ROOT / "services/gitea/scripts/gitea_branch_protection_check.py",
)
rules = [_protection("release/*", 1, helper)]
assert helper.evaluate(json.dumps(rules).encode(), "master", "bstein") == "ABSENT"

View File

@ -1,321 +0,0 @@
"""RBAC, Flux, and human-review contracts for the Hermes SCM broker."""
from __future__ import annotations
import json
import pytest
import yaml
from testing.tests.test_hermes_scm_broker_support import ROOT, _load_path
def _can_i(role: dict, api_group: str, resource: str, verb: str) -> bool:
return any(
(api_group in rule["apiGroups"] or "*" in rule["apiGroups"])
and (resource in rule["resources"] or "*" in rule["resources"])
and (verb in rule["verbs"] or "*" in rule["verbs"])
for rule in role["rules"]
)
def test_agent_rbac_preserves_read_diagnostics_without_administrator_authority():
documents = list(
yaml.safe_load_all(
(ROOT / "services/hermes-observer-rbac/rbac.yaml").read_text(
encoding="utf-8"
)
)
)
cluster_role, namespaced_role, binding = documents
assert binding["roleRef"]["name"] == "hermes-agent-cluster-observer-v2"
assert _can_i(cluster_role, "", "nodes", "get")
assert _can_i(namespaced_role, "", "pods", "get")
assert _can_i(namespaced_role, "", "pods/log", "get")
assert _can_i(
namespaced_role,
"kustomize.toolkit.fluxcd.io",
"kustomizations",
"list",
)
denied = (
("", "secrets", "get"),
("", "serviceaccounts/token", "create"),
("", "pods", "create"),
("", "pods/exec", "create"),
("", "pods/attach", "create"),
("", "pods/portforward", "create"),
("rbac.authorization.k8s.io", "clusterrolebindings", "create"),
("authorization.k8s.io", "selfsubjectaccessreviews", "create"),
("apps", "deployments", "patch"),
)
assert all(
not _can_i(role, *request)
for request in denied
for role in (cluster_role, namespaced_role)
)
bindings = yaml.safe_load(
(ROOT / "services/hermes-observer-bindings/rolebindings.yaml").read_text(
encoding="utf-8"
)
)["items"]
namespaces = {item["metadata"]["namespace"] for item in bindings}
assert "hermes-scm" not in namespaces
assert {"cassandra", "flux-system", "hermes", "kube-system"} <= namespaces
assert all(item["roleRef"]["name"].endswith("namespaced-observer-v2") for item in bindings)
def test_flux_boundary_keeps_broker_secret_and_network_separate_from_agent():
agent = yaml.safe_load(
(ROOT / "services/hermes/agent-deployment.yaml").read_text(encoding="utf-8")
)
annotations = agent["spec"]["template"]["metadata"]["annotations"]
assert not any("gitea" in key.lower() for key in annotations)
assert "GIT_ASKPASS" not in json.dumps(agent)
broker = yaml.safe_load(
(ROOT / "services/hermes-scm-broker/deployment.yaml").read_text(
encoding="utf-8"
)
)
assert broker["metadata"]["namespace"] == "hermes-scm"
assert broker["spec"]["template"]["spec"]["serviceAccountName"] == (
"hermes-scm-broker"
)
assert "gitea-token" in json.dumps(broker)
container = broker["spec"]["template"]["spec"]["containers"][0]
assert container["resources"]["limits"]["memory"] == "768Mi"
assert container["livenessProbe"]["failureThreshold"] == 10
tmp = next(
item for item in broker["spec"]["template"]["spec"]["volumes"] if item["name"] == "tmp"
)
assert tmp["emptyDir"]["sizeLimit"] == "1Gi"
policy = yaml.safe_load(
(ROOT / "services/hermes-scm-broker/networkpolicy.yaml").read_text(
encoding="utf-8"
)
)
ingress = policy["spec"]["ingress"]
assert ingress[0]["from"][0]["namespaceSelector"]["matchLabels"] == {
"kubernetes.io/metadata.name": "hermes"
}
assert ingress[0]["from"][0]["podSelector"]["matchLabels"] == {
"app": "hermes-agent"
}
vault = (
ROOT / "services/vault/scripts/vault_k8s_auth_configure.sh"
).read_text(encoding="utf-8")
assert 'write_policy_and_role "hermes-scm-broker" "hermes-scm"' in vault
assert '"hermes/developer-gitea" ""' in vault
agent_start = vault.index('write_policy_and_role "hermes-agent"')
agent_end = vault.index("write_policy_and_role", agent_start + 1)
assert "developer-gitea" not in vault[agent_start:agent_end]
def test_flux_bootstrap_has_no_agent_namespace_dependency_cycle():
hermes_resources = yaml.safe_load(
(ROOT / "services/hermes/kustomization.yaml").read_text(encoding="utf-8")
)["resources"]
assert "namespace.yaml" in hermes_resources
assert "scm-common" in hermes_resources
observer = yaml.safe_load(
(
ROOT
/ "clusters/atlas/flux-system/applications/hermes-observer-rbac/kustomization.yaml"
).read_text(encoding="utf-8")
)
assert "dependsOn" not in observer["spec"]
observer_objects = [
item
for item in yaml.safe_load_all(
(ROOT / "services/hermes-observer-rbac/rbac.yaml").read_text(
encoding="utf-8"
)
)
if item
]
assert {item["kind"] for item in observer_objects} <= {
"ClusterRole",
"ClusterRoleBinding",
}
hermes_flux = yaml.safe_load(
(
ROOT / "clusters/atlas/flux-system/applications/hermes/kustomization.yaml"
).read_text(encoding="utf-8")
)
assert "hermes-observer-rbac" in {
item["name"] for item in hermes_flux["spec"]["dependsOn"]
}
bindings_flux = yaml.safe_load(
(
ROOT
/ "clusters/atlas/flux-system/applications/hermes-observer-bindings/kustomization.yaml"
).read_text(encoding="utf-8")
)
assert {item["name"] for item in bindings_flux["spec"]["dependsOn"]} == {
"hermes",
"hermes-observer-rbac",
}
assert bindings_flux["metadata"]["name"] not in {
item["name"] for item in hermes_flux["spec"]["dependsOn"]
}
applications = (
ROOT / "clusters/atlas/flux-system/applications/kustomization.yaml"
).read_text(encoding="utf-8")
assert "hermes-scm-agent-code" not in applications
broker_code = yaml.safe_load(
(
ROOT
/ "clusters/atlas/flux-system/applications/hermes-scm-broker-code/kustomization.yaml"
).read_text(encoding="utf-8")
)
assert broker_code["spec"]["path"] == "./services/hermes/scm-common"
assert broker_code["spec"]["targetNamespace"] == "hermes-scm"
assert broker_code["spec"]["dependsOn"] == [{"name": "hermes-scm-namespace"}]
def test_gitea_bootstrap_enforces_human_review_without_overwriting_drift():
script = (
ROOT / "services/gitea/scripts/gitea_atlas_identity_ensure.sh"
).read_text(encoding="utf-8")
assert 'required_approvals\\\":1' in script
assert 'enable_push_whitelist\\\":true' in script
assert 'push_whitelist_usernames\\\":[\\\"${protected_reviewer}\\\"]' in script
assert 'enable_merge_whitelist\\\":true' in script
assert 'merge_whitelist_usernames\\\":[\\\"${protected_reviewer}\\\"]' in script
assert "protection differs from the human-review policy" in script
assert "api_request PATCH" not in script
job = yaml.safe_load(
(ROOT / "services/gitea/atlas-identity-bootstrap-job.yaml").read_text()
)
init = job["spec"]["template"]["spec"]["initContainers"][0]
assert init["image"] == (
"python@sha256:6d43704baacd1bfbe7c295d7f13079d5d8104ed33568873133f8fc69980419df"
)
assert init["securityContext"]["runAsNonRoot"] is True
assert init["securityContext"]["capabilities"]["drop"] == ["ALL"]
assert "/opt/python/bin/python3" in script
def _protection(rule_name: str, priority: int, helper, **overrides):
value = {
"rule_name": rule_name,
"priority": priority,
"created_at": "2026-01-01T00:00:00Z",
**helper._required("bstein"),
}
value.update(overrides)
return value
def test_branch_protection_uses_first_effective_glob_by_priority():
helper = _load_path(
"branch_protection_test",
ROOT / "services/gitea/scripts/gitea_branch_protection_check.py",
)
earlier_drift = _protection("m*", 1, helper, required_approvals=0)
later_exact = _protection("main", 2, helper)
with pytest.raises(helper.PolicyError, match="effective main protection differs"):
helper.evaluate(json.dumps([later_exact, earlier_drift]).encode(), "main", "bstein")
earlier_drift["required_approvals"] = 1
assert (
helper.evaluate(json.dumps([later_exact, earlier_drift]).encode(), "main", "bstein")
== "PRESENT"
)
brace_drift = _protection("m{ain,aster}", 1, helper, required_approvals=0)
with pytest.raises(helper.PolicyError, match="effective master protection differs"):
helper.evaluate(json.dumps([brace_drift]).encode(), "master", "bstein")
upper_glob = _protection("M*", 1, helper, required_approvals=0)
assert (
helper.evaluate(json.dumps([upper_glob, later_exact]).encode(), "main", "bstein")
== "PRESENT"
)
def test_branch_protection_matches_gitea_v1238_tie_order():
helper = _load_path(
"branch_protection_order_test",
ROOT / "services/gitea/scripts/gitea_branch_protection_check.py",
)
older_glob = _protection(
"m*", 1, helper, created_at="2025-01-01T00:00:00Z", required_approvals=0
)
newer_plain = _protection(
"MAIN", 1, helper, created_at="2026-01-01T00:00:00Z"
)
assert (
helper.evaluate(json.dumps([older_glob, newer_plain]).encode(), "main", "bstein")
== "PRESENT"
)
older_glob["created_at"] = "2026-01-02T00:00:00Z"
earlier_glob = _protection(
"m{ain,aster}",
1,
helper,
created_at="2026-01-01T00:00:00Z",
required_approvals=0,
)
with pytest.raises(helper.PolicyError, match="effective main protection differs"):
helper.evaluate(json.dumps([older_glob, earlier_glob]).encode(), "main", "bstein")
@pytest.mark.parametrize(
("rule_name", "branch", "expected"),
[
("release/*", "release/v1.17", True),
("release/**/v1.17", "release/test/1/v1.17", True),
("release/*/v1.17", "release/test/1/v1.17", False),
("*", "release/v1.16", False),
("**", "release/v1.16", True),
("MAIN", "main", True),
("M*", "main", False),
("m{ain,aster}", "master", True),
(r"m\ain", "main", True),
],
)
def test_branch_glob_matches_gitea_v1238_source_corpus(
rule_name: str, branch: str, expected: bool
):
helper = _load_path(
"branch_protection_glob_parity_test",
ROOT / "services/gitea/scripts/gitea_branch_protection_check.py",
)
assert helper._matches(rule_name, branch) is expected
@pytest.mark.parametrize(
"rules",
[
[{"rule_name": "main", "priority": 0, "created_at": "2026-01-01T00:00:00Z"}],
[{"rule_name": "main", "priority": 1, "created_at": "not-a-time"}],
[{"rule_name": "main"}],
],
)
def test_branch_protection_rejects_ambiguous_rules_before_creation(rules):
helper = _load_path(
"branch_protection_adversarial_test",
ROOT / "services/gitea/scripts/gitea_branch_protection_check.py",
)
with pytest.raises(helper.PolicyError):
helper.evaluate(json.dumps(rules).encode(), "main", "bstein")
def test_branch_protection_reports_absent_only_when_no_rule_matches():
helper = _load_path(
"branch_protection_absent_test",
ROOT / "services/gitea/scripts/gitea_branch_protection_check.py",
)
rules = [_protection("release/*", 1, helper)]
assert helper.evaluate(json.dumps(rules).encode(), "master", "bstein") == "ABSENT"

View File

@ -1,237 +0,0 @@
"""Bounded I/O and concurrency contracts for the Hermes SCM broker."""
from __future__ import annotations
import io
import json
from email.message import Message
import pytest
from testing.tests.test_hermes_scm_broker_support import Response, _load
def test_agent_broker_client_sends_no_credential_or_authorization_header():
client = _load("scm_broker_client")
seen = []
def opener(request, timeout):
seen.append((request, timeout))
return Response(b"{}", content_type="application/json")
assert client.read("/api/v1/repos/atlas/cassandra", opener=opener) == b"{}"
request = seen[0][0]
assert request.full_url == client.BROKER_ORIGIN + "/v1/metadata"
assert request.get_header("Authorization") is None
assert json.loads(request.data) == {"path": "/api/v1/repos/atlas/cassandra"}
@pytest.mark.parametrize("raw", ["", "01", "9" * 4000, "134217729", "12"])
def test_broker_content_length_is_canonical_and_bounded(raw: str):
broker = _load("scm_broker")
headers = Message()
headers["Content-Length"] = raw
with pytest.raises(broker.PolicyError, match="request length"):
broker._content_length(headers, broker.MAX_GIT_REQUEST)
def test_large_git_exchange_rolls_to_disk_and_scans_chunk_boundaries():
broker = _load("scm_broker")
token = "runtime-sentinel"
safe = b"x" * (broker.SPOOL_MEMORY_LIMIT + 1)
inbound_timeouts = []
spool, length = broker._spool_bounded(
io.BytesIO(safe),
broker.MAX_GIT_REQUEST,
len(safe),
token=token,
context="Git request",
deadline=broker.time.monotonic() + 30,
set_timeout=inbound_timeouts.append,
)
try:
assert length == len(safe)
assert spool._rolled is True
assert spool.read(4) == b"xxxx"
assert inbound_timeouts
assert all(0 < value <= 30 for value in inbound_timeouts)
finally:
spool.close()
crossing = b"x" * (broker.STREAM_CHUNK - 5) + token.encode() + b"tail"
with pytest.raises(broker.PolicyError, match="credential material"):
broker._spool_bounded(
io.BytesIO(crossing),
broker.MAX_GIT_REQUEST,
len(crossing),
token=token,
context="Git request",
deadline=broker.time.monotonic() + 30,
)
def test_large_upstream_response_is_streamed_through_bounded_spool():
broker = _load("scm_broker")
body = b"z" * (broker.SPOOL_MEMORY_LIMIT + 1)
seen = []
def opener(request, **_kwargs):
seen.append(request)
assert request.get_header("Content-length") == "7"
assert request.data.read() == b"request"
return Response(body, content_type="application/x-git-upload-pack-result")
streamed = broker._upstream_git_request(
"/atlas/cassandra.git/git-upload-pack",
method="POST",
body=io.BytesIO(b"request"),
body_length=7,
content_type="application/x-git-upload-pack-request",
expected_type="application/x-git-upload-pack-result",
token="runtime-sentinel",
stream_result=True,
opener=opener,
)
spool, length = streamed
try:
assert length == len(body)
assert spool._rolled is True
assert spool.read(3) == b"zzz"
assert len(seen) == 1
finally:
spool.close()
def test_body_deadline_fails_closed_before_reading():
broker = _load("scm_broker")
with pytest.raises(broker.PolicyError, match="deadline"):
broker._spool_bounded(
io.BytesIO(b"body"),
broker.MAX_GIT_REQUEST,
4,
token="runtime-sentinel",
context="Git request",
deadline=broker.time.monotonic() - 1,
)
def test_upstream_stream_applies_absolute_socket_deadline():
broker = _load("scm_broker")
class DeadlineSocket:
def __init__(self):
self.values = []
def settimeout(self, value):
self.values.append(value)
response = Response(b"safe", content_type="application/x-git-upload-pack-result")
sock = DeadlineSocket()
response.fp = type("FP", (), {"raw": type("Raw", (), {"_sock": sock})()})()
spool, length = broker._spool_response(
response, broker.MAX_GIT_RESPONSE, "sentinel"
)
try:
assert length == 4
assert sock.values
assert all(0 < value <= broker.INBOUND_BODY_TIMEOUT for value in sock.values)
finally:
spool.close()
def test_broker_rejects_work_when_concurrency_slots_are_exhausted():
broker = _load("scm_broker")
class FakeSocket:
def __init__(self):
self.value = b""
def sendall(self, value):
self.value += value
def shutdown(self, _how):
return None
def close(self):
return None
server = broker.BoundedThreadingHTTPServer(("127.0.0.1", 0), broker.BrokerHandler)
try:
assert broker.BoundedThreadingHTTPServer.__module__ == "scm_broker_server"
assert server._slots.acquire(blocking=False)
assert server._slots.acquire(blocking=False)
assert server._slots.acquire(blocking=False)
request = FakeSocket()
server.process_request(request, ("127.0.0.1", 1))
assert b"503 Service Unavailable" in request.value
finally:
server._slots.release()
server._slots.release()
server._slots.release()
server.server_close()
def test_three_simultaneous_broker_calls_have_bounded_capacity():
server_module = _load("scm_broker_server")
semaphore = server_module.threading.BoundedSemaphore(
server_module.MAX_CONCURRENT_REQUESTS
)
assert server_module.MAX_CONCURRENT_REQUESTS >= 3
assert [semaphore.acquire(blocking=False) for _ in range(3)] == [True, True, True]
assert semaphore.acquire(blocking=False) is False
def test_header_deadline_is_absolute_against_slow_trickle(monkeypatch):
server_module = _load("scm_broker_server")
class Stream:
def __init__(self):
self.value = io.BytesIO(b"GET /healthz HTTP/1.1\r\n")
def read(self, length):
return self.value.read(length)
class Connection:
def __init__(self):
self.timeouts = []
def settimeout(self, value):
self.timeouts.append(value)
ticks = iter((0.0, 0.02, 0.04, 0.06, 0.08, 0.11))
monkeypatch.setattr(server_module.time, "monotonic", lambda: next(ticks))
connection = Connection()
reader = server_module._AbsoluteDeadlineReader(Stream(), connection)
reader.begin(0.1)
with pytest.raises(TimeoutError, match="absolute SCM header deadline"):
reader.readline(65537)
assert connection.timeouts == pytest.approx([0.08, 0.06, 0.04, 0.02])
def test_broker_bounds_header_count_size_and_duplicate_lengths():
broker = _load("scm_broker")
def validate(headers):
handler = object.__new__(broker.BrokerHandler)
handler.headers = headers
handler._validate_headers()
too_many = Message()
for index in range(broker.MAX_HEADERS + 1):
too_many[f"X-Test-{index}"] = "safe"
with pytest.raises(broker.PolicyError, match="too many headers"):
validate(too_many)
too_large = Message()
too_large["X-Test"] = "x" * broker.MAX_HEADER_BYTES
with pytest.raises(broker.PolicyError, match="safe limit"):
validate(too_large)
duplicate = Message()
duplicate["Content-Length"] = "1"
duplicate["Content-Length"] = "1"
with pytest.raises(broker.PolicyError, match="duplicate Content-Length"):
validate(duplicate)

View File

@ -1,55 +0,0 @@
"""Shared fixtures for credential-isolated Hermes SCM broker tests."""
from __future__ import annotations
import importlib.util
import io
import sys
from email.message import Message
from pathlib import Path
ROOT = Path(__file__).parents[2]
SCRIPTS = ROOT / "services/hermes/scm-common/scripts"
sys.path.insert(0, str(SCRIPTS))
def _load(name: str):
path = SCRIPTS / f"{name}.py"
spec = importlib.util.spec_from_file_location(f"test_{name}", path)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
def _load_path(name: str, path: Path):
spec = importlib.util.spec_from_file_location(name, path)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
class Response:
def __init__(self, body: bytes, *, status: int = 200, content_type: str):
self.body = body
self.stream = io.BytesIO(body)
self.status = status
self.headers = Message()
self.headers["Content-Type"] = content_type
def __enter__(self):
return self
def __exit__(self, *_args):
return False
def read(self, limit=-1):
return self.stream.read(limit)
def _receive_command(old: bytes, new: bytes, ref: bytes) -> bytes:
command = old + b" " + new + b" " + ref + b"\x00report-status\n"
return f"{len(command) + 4:04x}".encode() + command + b"0000PACK"