hermes: load tool credentials before s6

This commit is contained in:
jenkins 2026-08-15 03:44:00 -03:00
parent f7492defdd
commit 846c890527
5 changed files with 18 additions and 111 deletions

View File

@ -255,26 +255,6 @@ spec:
resources:
requests: {cpu: 25m, memory: 64Mi}
limits: {cpu: 250m, memory: 256Mi}
- name: patch-main-wrapper
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
imagePullPolicy: IfNotPresent
command:
- /opt/hermes/.venv/bin/python
- /opt/coordinator/patch_main_wrapper.py
- /opt/hermes/docker/main-wrapper.sh
- /patched/main-wrapper.sh
securityContext:
allowPrivilegeEscalation: false
runAsUser: 10000
runAsGroup: 10000
seccompProfile:
type: RuntimeDefault
volumeMounts:
- {name: coordinator, mountPath: /opt/coordinator, readOnly: true}
- {name: main-wrapper-patch, mountPath: /patched}
resources:
requests: {cpu: 25m, memory: 32Mi}
limits: {cpu: 100m, memory: 64Mi}
- name: patch-auth
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
imagePullPolicy: IfNotPresent
@ -476,8 +456,13 @@ spec:
- name: hermes
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
imagePullPolicy: IfNotPresent
command: [/init, /opt/hermes/docker/main-wrapper.sh]
args: [gateway, run]
command: [/bin/sh, -ec]
args:
- |
set -a
. /opt/data/.env
set +a
exec /init /opt/hermes/docker/main-wrapper.sh gateway run
ports:
- {name: api, containerPort: 8642, protocol: TCP}
- {name: dashboard, containerPort: 9119, protocol: TCP}
@ -512,7 +497,6 @@ spec:
- {name: home, mountPath: /opt/data}
- {name: provider-auth, mountPath: /shared-auth}
- {name: coordinator, mountPath: /opt/coordinator, readOnly: true}
- {name: main-wrapper-patch, mountPath: /opt/hermes/docker/main-wrapper.sh, subPath: main-wrapper.sh, readOnly: true}
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py}
- {name: codex-runtime-patch, mountPath: /opt/hermes/hermes_cli/runtime_provider.py, subPath: runtime_provider.py}
- {name: codex-runtime-patch, mountPath: /opt/hermes/agent/transports/codex_app_server_session.py, subPath: codex_app_server_session.py}
@ -962,8 +946,6 @@ spec:
defaultMode: 0444
- name: auth-patch
emptyDir: {}
- name: main-wrapper-patch
emptyDir: {}
- name: tui-gateway-patch
emptyDir: {}
- name: api-server-patch

View File

@ -76,7 +76,6 @@ configMapGenerator:
- migrate_api_session_lineage.py=scripts/migrate_api_session_lineage.py
- patch_api_server_sessions.py=scripts/patch_api_server_sessions.py
- patch_hermes_auth.py=scripts/patch_hermes_auth.py
- patch_main_wrapper.py=scripts/patch_main_wrapper.py
- patch_codex_runtime.py=scripts/patch_codex_runtime.py
- patch_stream_recovery.py=scripts/patch_stream_recovery.py
- patch_tui_gateway.py=scripts/patch_tui_gateway.py

View File

@ -1,48 +0,0 @@
#!/usr/bin/env python3
"""Load the Vault-derived tool environment in Hermes's main gateway."""
from __future__ import annotations
import argparse
import shutil
from pathlib import Path
BEFORE = """# HOME comes through with-contenv as /root (the /init context). Override
"""
AFTER = """# The init container writes Vault-derived tool credentials here. Load them
# after with-contenv restores the container environment so dashboard-spawned
# terminal tools receive the same Git identity as durable CLI workers.
if [ -r /opt/data/.env ]; then
set -a
# shellcheck disable=SC1091
. /opt/data/.env
set +a
fi
# HOME comes through with-contenv as /root (the /init context). Override
"""
def patch(source: Path, destination: Path) -> None:
"""Apply the narrow environment load and fail on upstream drift."""
content = source.read_text(encoding="utf-8")
if BEFORE not in content:
raise RuntimeError("Hermes main-wrapper patch context changed")
destination.parent.mkdir(parents=True, exist_ok=True)
destination.write_text(content.replace(BEFORE, AFTER, 1), encoding="utf-8")
shutil.copymode(source, destination)
def main() -> int:
"""Patch the wrapper path supplied by the deployment init container."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("source", type=Path)
parser.add_argument("destination", type=Path)
args = parser.parse_args()
patch(args.source, args.destination)
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@ -639,8 +639,10 @@ def test_agent_root_is_stock_dashboard_and_terminal_is_a_separate_path():
assert "dashboard" not in containers
hermes = containers["hermes"]
assert hermes["command"] == ["/init", "/opt/hermes/docker/main-wrapper.sh"]
assert hermes["args"] == ["gateway", "run"]
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"

View File

@ -1,9 +1,7 @@
"""Hermes gateway tool-environment patch and manifest tests."""
"""Hermes gateway tool-environment and recovery manifest tests."""
from __future__ import annotations
import importlib.util
import stat
from pathlib import Path
import yaml
@ -11,46 +9,20 @@ import yaml
ROOT = Path(__file__).resolve().parents[2]
HERMES = ROOT / "services/hermes"
SCRIPT = HERMES / "scripts/patch_main_wrapper.py"
def _patch_module():
spec = importlib.util.spec_from_file_location("patch_main_wrapper", SCRIPT)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def _deployment() -> dict:
return yaml.safe_load((HERMES / "agent-deployment.yaml").read_text())
def test_wrapper_loads_tool_environment_after_s6_restore(tmp_path: Path) -> None:
module = _patch_module()
source = tmp_path / "main-wrapper.sh"
destination = tmp_path / "patched.sh"
source.write_text("#!/bin/sh\n" + module.BEFORE + "export HOME=/opt/data\n")
source.chmod(0o755)
module.patch(source, destination)
patched = destination.read_text()
assert ". /opt/data/.env" in patched
assert patched.index(". /opt/data/.env") < patched.index("export HOME=/opt/data")
assert destination.stat().st_mode & stat.S_IXUSR
def test_gateway_retains_stock_entrypoint_with_patched_wrapper() -> None:
def test_gateway_loads_tool_environment_before_s6_entrypoint() -> None:
pod = _deployment()["spec"]["template"]["spec"]
init = {item["name"]: item for item in pod["initContainers"]}
containers = {item["name"]: item for item in pod["containers"]}
assert "repair-cassandra-kanban" in init
assert "patch-main-wrapper" in init
assert containers["hermes"]["command"] == [
"/init",
"/opt/hermes/docker/main-wrapper.sh",
]
mounts = {item["name"]: item for item in containers["hermes"]["volumeMounts"]}
assert mounts["main-wrapper-patch"]["subPath"] == "main-wrapper.sh"
hermes = containers["hermes"]
assert hermes["command"] == ["/bin/sh", "-ec"]
startup = hermes["args"][0]
assert startup.index(". /opt/data/.env") < startup.index("exec /init")
assert "exec /init /opt/hermes/docker/main-wrapper.sh gateway run" in startup