refactor(manifest): remove empty EGRESS_ROLES and related plumbing
test / unit (pull_request) Successful in 36s
test / integration (pull_request) Successful in 53s

EGRESS_ROLES, EGRESS_SINGLETON_ROLES, and PROVIDER_EGRESS_ROLES were
all empty frozensets after the codex_auth and claude_code_oauth roles
were removed. Delete the constants and all validation code that iterated
over them (the singleton-role loop and provider-role check in
_validate_egress_routes, the EGRESS_ROLES membership test in
EgressRoute.from_dict). EgressRoute.from_dict now rejects any role
string unconditionally; _validate_egress_routes loses its
agent_provider_template parameter entirely.

Assisted-by: Claude Code
This commit is contained in:
2026-06-02 01:57:55 +00:00
committed by didericis
parent 938a0e05d6
commit f8fc29ce87
2 changed files with 19 additions and 69 deletions
+2 -3
View File
@@ -69,9 +69,8 @@ class EgressRoute:
under `token_env`. Routes that share a `token_ref` coalesce to
one `token_env` slot.
`roles` carries the manifest route's optional role markers (see
`manifest.EGRESS_ROLES`). The launch step reads these for
side effects like the claude-code OAuth placeholder env.
`roles` carries the manifest route's role tuple (reserved for
future use; always empty today).
`tls_passthrough` signals that pipelock must not TLS-MITM this
host — either because the manifest declared `pipelock.tls_passthrough:
+17 -66
View File
@@ -175,17 +175,6 @@ class GitEntry:
# token-not-Bearer quirk (go-gitea/gitea#16734).
EGRESS_AUTH_SCHEMES = ("Bearer", "token")
# Per-route role markers. Both former roles (claude_code_oauth,
# codex_auth) have been removed — provider auth is now provisioner-owned
# via agent_provider.auth_token / forward_host_credentials. The field
# and validation plumbing remain for future roles.
EGRESS_ROLES: frozenset[str] = frozenset()
EGRESS_SINGLETON_ROLES: frozenset[str] = frozenset()
PROVIDER_EGRESS_ROLES: dict[str, frozenset[str]] = {
"claude": frozenset(),
"codex": frozenset(),
}
@dataclass(frozen=True)
class AgentProvider:
@@ -420,7 +409,8 @@ class EgressRoute:
manifest's `auth` block is omitted both fields are empty strings —
no Authorization is written, no token forwarded.
`Role` is an optional tuple of named markers (see EGRESS_ROLES).
`Role` is reserved for future use; all role strings are currently
rejected by the validator.
Validation rules (enforced in `from_dict`):
- `host` required, non-empty.
@@ -429,10 +419,7 @@ class EgressRoute:
`token_ref` as non-empty strings; an empty `auth: {}` is an
error rather than a synonym for "no auth" (omit `auth` for
that case).
- `role` optional. String or list of strings drawn from
EGRESS_ROLES. Singleton roles (see
EGRESS_SINGLETON_ROLES) may appear on at most one
route per bottle.
- `role` optional, reserved — any non-empty value is rejected.
"""
Host: str
@@ -530,12 +517,11 @@ class EgressRoute:
f"{label} role must be a string or a list of strings "
f"(was {type(role_raw).__name__})"
)
for r in roles:
if r not in EGRESS_ROLES:
raise ManifestError(
f"{label} role {r!r} is not one of "
f"{', '.join(sorted(EGRESS_ROLES))}"
)
if roles:
raise ManifestError(
f"{label} role {roles[0]!r} is not accepted; "
f"the 'role' field is reserved for future use"
)
pipelock = (
PipelockRoutePolicy.from_dict(bottle_name, idx, d["pipelock"])
@@ -570,9 +556,7 @@ class EgressConfig:
routes: tuple[EgressRoute, ...] = ()
@classmethod
def from_dict(
cls, bottle_name: str, raw: object, *, agent_provider_template: str = "claude",
) -> "EgressConfig":
def from_dict(cls, bottle_name: str, raw: object) -> "EgressConfig":
d = _as_json_object(raw, f"bottle '{bottle_name}' egress")
routes_raw = d.get("routes")
routes: tuple[EgressRoute, ...] = ()
@@ -587,9 +571,7 @@ class EgressConfig:
EgressRoute.from_dict(bottle_name, i, entry)
for i, entry in enumerate(routes_list)
)
_validate_egress_routes(
bottle_name, routes, agent_provider_template=agent_provider_template,
)
_validate_egress_routes(bottle_name, routes)
for k in d:
if k != "routes":
raise ManifestError(
@@ -680,10 +662,7 @@ class Bottle:
)
egress = (
EgressConfig.from_dict(
name, d["egress"],
agent_provider_template=agent_provider.template,
)
EgressConfig.from_dict(name, d["egress"])
if "egress" in d
else EgressConfig()
)
@@ -1047,21 +1026,15 @@ def _is_ip_literal(value: str) -> bool:
def _validate_egress_routes(
bottle_name: str,
routes: tuple[EgressRoute, ...],
*,
agent_provider_template: str = "claude",
) -> None:
"""Cross-validation for `bottle.egress.routes`:
"""Cross-validation for `bottle.egress.routes`: hosts must be unique.
- Hosts must be unique within the bottle. The proxy matches by
exact-host (v1, prefix matching is on path_allowlist only);
duplicate hosts leave the route choice ambiguous.
- Singleton roles (see EGRESS_SINGLETON_ROLES) may appear
on at most one route per bottle.
The proxy matches by exact-host (v1); duplicate hosts leave the
route choice ambiguous so we reject them up front.
No cross-validation against `bottle.git` is performed. git-gate
(SSH push/fetch) and egress (HTTPS) broker different
protocols; declaring both for the same host is a legitimate
dev setup."""
(SSH push/fetch) and egress (HTTPS) broker different protocols;
declaring both for the same host is a legitimate dev setup."""
seen_hosts: dict[str, None] = {}
for r in routes:
key = r.Host.lower()
@@ -1071,25 +1044,6 @@ def _validate_egress_routes(
f"{r.Host!r}; each host must be unique on the proxy."
)
seen_hosts[key] = None
for role in EGRESS_SINGLETON_ROLES:
with_role = [r for r in routes if role in r.Role]
if len(with_role) > 1:
hosts = ", ".join(r.Host for r in with_role)
raise ManifestError(
f"bottle '{bottle_name}' egress.routes has {len(with_role)} "
f"routes with role {role!r} (hosts: {hosts}); this role drives a "
f"single launch-step side effect — pick one."
)
allowed_roles = PROVIDER_EGRESS_ROLES[agent_provider_template]
for route in routes:
for role in route.Role:
if role not in allowed_roles:
raise ManifestError(
f"bottle '{bottle_name}' egress route for host "
f"{route.Host!r} has role {role!r}, but provider "
f"{agent_provider_template!r} only accepts roles "
f"{', '.join(sorted(allowed_roles)) or '(none)'}"
)
def _validate_unique_git_names(bottle_name: str, git: tuple[GitEntry, ...]) -> None:
@@ -1300,10 +1254,7 @@ def _merge_bottles(
merged_supervise = (
child.supervise if "supervise" in child_raw else parent.supervise
)
_validate_egress_routes(
name, merged_egress.routes,
agent_provider_template=merged_agent_provider.template,
)
_validate_egress_routes(name, merged_egress.routes)
return Bottle(
env=merged_env,