R4 (partial): isolated-engine-only forwarder guard (gate item 5)

Land only the containment-refusal half of R4: the guard every SOR forwarder
must pass before it does anything — assert engine != local, or refuse. It moves
no traffic, opens no socket, spawns no engine, and builds no SSH chain.

- forwarder.py: assert_isolated (raises on local and on any engine off the
  manifest allow-list), isolation_prefix (docker/multipass exec argv, no host
  path), ForwarderPlan (construction IS the gate — no plan for local/unknown/
  no-container). Allow-list imported from provenance so forwarder and manifest
  cannot disagree about what counts as isolated.
- There is no code path in this module that returns an exec prefix for the host;
  containment is structural, not a runtime flag.

The traffic-moving half of R4 (gate item 1: 3-hop self-traffic e2e delivery +
per-hop pcaps across the grid) is deliberately NOT implemented — HELD for a live
isolated engine + grid. This guard is fully verifiable offline.

Acceptance (gate item 5) green: local/unknown/no-container refused; isolated
engine accepted with the correct isolation prefix. Python forwarder suite 12
passed; full SOR Python suite 49 passed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
leetcrypt
2026-07-19 16:43:10 -07:00
parent 7de0f57fa6
commit dfc2e60e1b
2 changed files with 176 additions and 0 deletions
+100
View File
@@ -0,0 +1,100 @@
"""R4 (partial) — containment guard for the SOR nested-SSH forwarder.
This module lands **only instrument-validation gate item 5**: forwarders are
*isolated-engine-only*. It is the guard that every forwarder MUST pass before it
does anything — ``assert engine != local`` or refuse. It moves no traffic, opens
no socket, spawns no engine, and builds no SSH chain.
The traffic-moving half of R4 (gate item 1: a 3-hop self-traffic circuit across
the grid delivering a known payload e2e, with per-hop pcaps) is intentionally
**NOT** implemented here — it is HELD for a live isolated engine + grid (see
OVERSEER-STATUS.md). Committing an unrunnable forwarder would violate the build
discipline; this guard, by contrast, is fully verifiable offline: constructing a
forwarder plan for a ``local`` (or unknown) engine refuses, for an isolated
engine it yields the isolation exec prefix.
Containment law (CLAUDE.md §Containment #1) is encoded structurally: there is no
code path in this module that returns an exec prefix for the host. ``local`` —
the explicit warned exception the *chat* sandbox allows (``bridge.py:540``) — is
never valid for a SOR run. The allow-list is imported from ``provenance`` so the
forwarder and the run manifest can never disagree about what counts as isolated.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import List
from cmd_chat.sor.provenance import ISOLATED_ENGINES
# The host. Never a valid engine for a SOR forwarder — its whole purpose is to
# keep every forwarder/circuit process off the host (blast-radius containment).
LOCAL_ENGINE = "local"
class ContainmentError(RuntimeError):
"""A forwarder was asked to run on a non-isolated engine (or with no
container/VM to exec into). Raised instead of running — the code refuses."""
def assert_isolated(engine: str) -> None:
"""Raise :class:`ContainmentError` unless ``engine`` is an isolated engine.
This is the ``assert engine != local`` half of the R4 acceptance check
(instrument-validation gate item 5), hardened to also reject any engine not
on the manifest allow-list (unknown engines are refused, not assumed safe)."""
if engine == LOCAL_ENGINE:
raise ContainmentError(
"containment: SOR forwarders never run on the host "
f"({LOCAL_ENGINE!r}); isolated engine required (one of {ISOLATED_ENGINES})"
)
if engine not in ISOLATED_ENGINES:
raise ContainmentError(
f"containment: engine {engine!r} is not an isolated engine "
f"(allowed: {ISOLATED_ENGINES})"
)
def isolation_prefix(engine: str, container: str) -> List[str]:
"""The argv prefix that runs a command *inside* the named isolated engine —
the same shape the chat sandbox uses (``bridge.py:_exec_prefix``), minus the
``local`` host exception, which is refused here. The caller appends the actual
program + args as separate argv items (never a shell-interpolated string), so
circuit descriptors can't inject shell metacharacters.
Refuses (raises) on a non-isolated engine or a missing container name."""
assert_isolated(engine)
if not container:
raise ContainmentError(
f"containment: no container/VM name to exec into for engine {engine!r}"
)
if engine == "docker":
return ["docker", "exec", "-i", container]
if engine == "multipass":
return ["multipass", "exec", container, "--"]
# Unreachable: assert_isolated already constrained `engine` to ISOLATED_ENGINES
# and both members are handled above. Refuse rather than fall through silently.
raise ContainmentError(f"containment: no isolation prefix for engine {engine!r}")
@dataclass(frozen=True)
class ForwarderPlan:
"""A validated, inert description of *where* a hop forwarder would run. Its
construction is the containment gate: a plan for a ``local``/unknown engine,
or with no container, cannot be built. It carries no credentials, opens no
connection, and moves no bytes — the traffic-moving forwarder that would
consume it (gate item 1) is HELD for the human + a live grid."""
engine: str
container: str
def __post_init__(self) -> None:
assert_isolated(self.engine)
if not self.container:
raise ContainmentError(
f"containment: no container/VM name for engine {self.engine!r}"
)
def exec_prefix(self) -> List[str]:
"""The isolated exec argv prefix for this plan (validated at build time)."""
return isolation_prefix(self.engine, self.container)