refactor(hermes): rename the owner agent host to worker.bstein.dev
Introduce worker.bstein.dev as the canonical hostname for the owner-only Hermes coordinator, previously agent.hermes.bstein.dev. The rename is additive, matching the shape #38 restored for chat and triage. CoreDNS, both agent Ingresses and the hermes-sites certificate now serve BOTH names, so merging this cannot take away the endpoint the operator uses to reach the coordinator. Retiring agent.hermes.bstein.dev is a separate, separately scheduled change. No redirect middleware is added. What switches to the new host: - HERMES_DASHBOARD_PUBLIC_URL and the oauth2-proxy --redirect-url - the Keycloak agent proxy rootUrl - operator docs, skills, the ZAP baseline target and the triage monitor default What stays dual-homed until retirement: - CoreDNS hosts entry, both agent Ingress rules, certificate SANs - API_SERVER_CORS_ORIGINS (now a comma-separated pair) - the Keycloak redirect URIs, web origins and post-logout origins, so a rollback only needs the oauth2-proxy --redirect-url reverted and does not require re-running the ensure job The agent client passes its legacy origin through the optional fourth argument #38 added to ensure_proxy_client, so no second mechanism is introduced. The immutable ensure Job goes -11 -> -12 because #38 already consumed -11 and that run has completed; without a further bump this change would never be applied. Login on the new host fails until the -12 Job completes. Because the session and CSRF cookies use the __Host- prefix they are bound to one origin, so a fresh login must start on worker.bstein.dev and existing sessions do not carry over -- re-login is required after rollout. #38's public-host continuity test now covers the agent proxy's dual origins rather than asserting the agent surface was untouched by the rename. Knowledge catalogs and diagrams regenerated with `make knowledge`.
This commit is contained in:
parent
004c41629e
commit
94106bf252
@ -49,6 +49,7 @@ data:
|
|||||||
192.168.22.9 triage.bstein.dev
|
192.168.22.9 triage.bstein.dev
|
||||||
192.168.22.9 triage.hermes.bstein.dev
|
192.168.22.9 triage.hermes.bstein.dev
|
||||||
192.168.22.9 vault.bstein.dev
|
192.168.22.9 vault.bstein.dev
|
||||||
|
192.168.22.9 worker.bstein.dev
|
||||||
fallthrough
|
fallthrough
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"counts": {
|
"counts": {
|
||||||
"helmrelease_host_hints": 23,
|
"helmrelease_host_hints": 23,
|
||||||
"http_endpoints": 63,
|
"http_endpoints": 65,
|
||||||
"services": 98,
|
"services": 98,
|
||||||
"workloads": 130
|
"workloads": 130
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5550,6 +5550,46 @@
|
|||||||
"name": "wolf",
|
"name": "wolf",
|
||||||
"source": "game-stream"
|
"source": "game-stream"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"host": "worker.bstein.dev",
|
||||||
|
"path": "/",
|
||||||
|
"backend": {
|
||||||
|
"namespace": "hermes",
|
||||||
|
"service": "oauth2-proxy-hermes-agent",
|
||||||
|
"port": "http",
|
||||||
|
"workloads": [
|
||||||
|
{
|
||||||
|
"kind": "Deployment",
|
||||||
|
"name": "hermes-agent"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"via": {
|
||||||
|
"kind": "Ingress",
|
||||||
|
"name": "hermes-agent-dashboard",
|
||||||
|
"source": "hermes"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"host": "worker.bstein.dev",
|
||||||
|
"path": "/terminal",
|
||||||
|
"backend": {
|
||||||
|
"namespace": "hermes",
|
||||||
|
"service": "oauth2-proxy-hermes-agent",
|
||||||
|
"port": "http",
|
||||||
|
"workloads": [
|
||||||
|
{
|
||||||
|
"kind": "Deployment",
|
||||||
|
"name": "hermes-agent"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"via": {
|
||||||
|
"kind": "Ingress",
|
||||||
|
"name": "hermes-agent-terminal",
|
||||||
|
"source": "hermes"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"helmrelease_host_hints": {
|
"helmrelease_host_hints": {
|
||||||
|
|||||||
@ -3607,6 +3607,28 @@ http_endpoints:
|
|||||||
kind: Ingress
|
kind: Ingress
|
||||||
name: wolf
|
name: wolf
|
||||||
source: game-stream
|
source: game-stream
|
||||||
|
- host: worker.bstein.dev
|
||||||
|
path: /
|
||||||
|
backend:
|
||||||
|
namespace: hermes
|
||||||
|
service: oauth2-proxy-hermes-agent
|
||||||
|
port: http
|
||||||
|
workloads: *id001
|
||||||
|
via:
|
||||||
|
kind: Ingress
|
||||||
|
name: hermes-agent-dashboard
|
||||||
|
source: hermes
|
||||||
|
- host: worker.bstein.dev
|
||||||
|
path: /terminal
|
||||||
|
backend:
|
||||||
|
namespace: hermes
|
||||||
|
service: oauth2-proxy-hermes-agent
|
||||||
|
port: http
|
||||||
|
workloads: *id001
|
||||||
|
via:
|
||||||
|
kind: Ingress
|
||||||
|
name: hermes-agent-terminal
|
||||||
|
source: hermes
|
||||||
helmrelease_host_hints:
|
helmrelease_host_hints:
|
||||||
cert-manager:cert-manager/cert-manager:
|
cert-manager:cert-manager/cert-manager:
|
||||||
- atlas.bstein.dev
|
- atlas.bstein.dev
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -198,6 +198,8 @@ flowchart LR
|
|||||||
host_wolf_bstein_dev --> svc_game_stream_oauth2_proxy_wolf
|
host_wolf_bstein_dev --> svc_game_stream_oauth2_proxy_wolf
|
||||||
wl_game_stream_oauth2_proxy_wolf["game-stream/oauth2-proxy-wolf (Deployment)"]
|
wl_game_stream_oauth2_proxy_wolf["game-stream/oauth2-proxy-wolf (Deployment)"]
|
||||||
svc_game_stream_oauth2_proxy_wolf --> wl_game_stream_oauth2_proxy_wolf
|
svc_game_stream_oauth2_proxy_wolf --> wl_game_stream_oauth2_proxy_wolf
|
||||||
|
host_worker_bstein_dev["worker.bstein.dev"]
|
||||||
|
host_worker_bstein_dev --> svc_hermes_oauth2_proxy_hermes_agent
|
||||||
|
|
||||||
subgraph bstein_dev_home[bstein-dev-home]
|
subgraph bstein_dev_home[bstein-dev-home]
|
||||||
svc_bstein_dev_home_bstein_dev_home_frontend
|
svc_bstein_dev_home_bstein_dev_home_frontend
|
||||||
|
|||||||
@ -99,7 +99,7 @@ in-process call removed a whole pod launch from the critical path.
|
|||||||
`hermes-demo-test-<N>` (failed) and `hermes-demo-repair-<N>` (succeeded);
|
`hermes-demo-test-<N>` (failed) and `hermes-demo-repair-<N>` (succeeded);
|
||||||
both TTL-clean after 1 h.
|
both TTL-clean after 1 h.
|
||||||
- Hermes side: the run appears in the dashboard at
|
- Hermes side: the run appears in the dashboard at
|
||||||
`https://agent.hermes.bstein.dev` (session/run history).
|
`https://worker.bstein.dev` (session/run history).
|
||||||
- Escalation path: alert `HermesTriageHumanRequired` in vmalert
|
- Escalation path: alert `HermesTriageHumanRequired` in vmalert
|
||||||
(`vmalert-atlas-availability` deployment, 1 m interval, `for: 2m`)
|
(`vmalert-atlas-availability` deployment, 1 m interval, `for: 2m`)
|
||||||
fires to Alertmanager for any `human_required` incident. Gauges are
|
fires to Alertmanager for any `human_required` incident. Gauges are
|
||||||
|
|||||||
@ -40,7 +40,7 @@ JENKINS = os.environ.get("JENKINS_URL", "https://ci.bstein.dev")
|
|||||||
GITEA = os.environ.get("GITEA_URL", "https://scm.bstein.dev")
|
GITEA = os.environ.get("GITEA_URL", "https://scm.bstein.dev")
|
||||||
CODE_REPO_OWNER = os.environ.get("CODE_REPO_OWNER", "atlas")
|
CODE_REPO_OWNER = os.environ.get("CODE_REPO_OWNER", "atlas")
|
||||||
GRAFANA = os.environ.get("GRAFANA_URL", "https://metrics.bstein.dev")
|
GRAFANA = os.environ.get("GRAFANA_URL", "https://metrics.bstein.dev")
|
||||||
HERMES_UI = os.environ.get("HERMES_URL", "https://agent.hermes.bstein.dev")
|
HERMES_UI = os.environ.get("HERMES_URL", "https://worker.bstein.dev")
|
||||||
POLL_SECONDS = 6
|
POLL_SECONDS = 6
|
||||||
|
|
||||||
# `--filter <text>` restricts the monitor to incidents whose id contains that
|
# `--filter <text>` restricts the monitor to incidents whose id contains that
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"counts": {
|
"counts": {
|
||||||
"helmrelease_host_hints": 23,
|
"helmrelease_host_hints": 23,
|
||||||
"http_endpoints": 63,
|
"http_endpoints": 65,
|
||||||
"services": 98,
|
"services": 98,
|
||||||
"workloads": 130
|
"workloads": 130
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5550,6 +5550,46 @@
|
|||||||
"name": "wolf",
|
"name": "wolf",
|
||||||
"source": "game-stream"
|
"source": "game-stream"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"host": "worker.bstein.dev",
|
||||||
|
"path": "/",
|
||||||
|
"backend": {
|
||||||
|
"namespace": "hermes",
|
||||||
|
"service": "oauth2-proxy-hermes-agent",
|
||||||
|
"port": "http",
|
||||||
|
"workloads": [
|
||||||
|
{
|
||||||
|
"kind": "Deployment",
|
||||||
|
"name": "hermes-agent"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"via": {
|
||||||
|
"kind": "Ingress",
|
||||||
|
"name": "hermes-agent-dashboard",
|
||||||
|
"source": "hermes"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"host": "worker.bstein.dev",
|
||||||
|
"path": "/terminal",
|
||||||
|
"backend": {
|
||||||
|
"namespace": "hermes",
|
||||||
|
"service": "oauth2-proxy-hermes-agent",
|
||||||
|
"port": "http",
|
||||||
|
"workloads": [
|
||||||
|
{
|
||||||
|
"kind": "Deployment",
|
||||||
|
"name": "hermes-agent"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"via": {
|
||||||
|
"kind": "Ingress",
|
||||||
|
"name": "hermes-agent-terminal",
|
||||||
|
"source": "hermes"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"helmrelease_host_hints": {
|
"helmrelease_host_hints": {
|
||||||
|
|||||||
@ -3607,6 +3607,28 @@ http_endpoints:
|
|||||||
kind: Ingress
|
kind: Ingress
|
||||||
name: wolf
|
name: wolf
|
||||||
source: game-stream
|
source: game-stream
|
||||||
|
- host: worker.bstein.dev
|
||||||
|
path: /
|
||||||
|
backend:
|
||||||
|
namespace: hermes
|
||||||
|
service: oauth2-proxy-hermes-agent
|
||||||
|
port: http
|
||||||
|
workloads: *id001
|
||||||
|
via:
|
||||||
|
kind: Ingress
|
||||||
|
name: hermes-agent-dashboard
|
||||||
|
source: hermes
|
||||||
|
- host: worker.bstein.dev
|
||||||
|
path: /terminal
|
||||||
|
backend:
|
||||||
|
namespace: hermes
|
||||||
|
service: oauth2-proxy-hermes-agent
|
||||||
|
port: http
|
||||||
|
workloads: *id001
|
||||||
|
via:
|
||||||
|
kind: Ingress
|
||||||
|
name: hermes-agent-terminal
|
||||||
|
source: hermes
|
||||||
helmrelease_host_hints:
|
helmrelease_host_hints:
|
||||||
cert-manager:cert-manager/cert-manager:
|
cert-manager:cert-manager/cert-manager:
|
||||||
- atlas.bstein.dev
|
- atlas.bstein.dev
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -198,6 +198,8 @@ flowchart LR
|
|||||||
host_wolf_bstein_dev --> svc_game_stream_oauth2_proxy_wolf
|
host_wolf_bstein_dev --> svc_game_stream_oauth2_proxy_wolf
|
||||||
wl_game_stream_oauth2_proxy_wolf["game-stream/oauth2-proxy-wolf (Deployment)"]
|
wl_game_stream_oauth2_proxy_wolf["game-stream/oauth2-proxy-wolf (Deployment)"]
|
||||||
svc_game_stream_oauth2_proxy_wolf --> wl_game_stream_oauth2_proxy_wolf
|
svc_game_stream_oauth2_proxy_wolf --> wl_game_stream_oauth2_proxy_wolf
|
||||||
|
host_worker_bstein_dev["worker.bstein.dev"]
|
||||||
|
host_worker_bstein_dev --> svc_hermes_oauth2_proxy_hermes_agent
|
||||||
|
|
||||||
subgraph bstein_dev_home[bstein-dev-home]
|
subgraph bstein_dev_home[bstein-dev-home]
|
||||||
svc_bstein_dev_home_bstein_dev_home_frontend
|
svc_bstein_dev_home_bstein_dev_home_frontend
|
||||||
|
|||||||
@ -99,7 +99,7 @@ in-process call removed a whole pod launch from the critical path.
|
|||||||
`hermes-demo-test-<N>` (failed) and `hermes-demo-repair-<N>` (succeeded);
|
`hermes-demo-test-<N>` (failed) and `hermes-demo-repair-<N>` (succeeded);
|
||||||
both TTL-clean after 1 h.
|
both TTL-clean after 1 h.
|
||||||
- Hermes side: the run appears in the dashboard at
|
- Hermes side: the run appears in the dashboard at
|
||||||
`https://agent.hermes.bstein.dev` (session/run history).
|
`https://worker.bstein.dev` (session/run history).
|
||||||
- Escalation path: alert `HermesTriageHumanRequired` in vmalert
|
- Escalation path: alert `HermesTriageHumanRequired` in vmalert
|
||||||
(`vmalert-atlas-availability` deployment, 1 m interval, `for: 2m`)
|
(`vmalert-atlas-availability` deployment, 1 m interval, `for: 2m`)
|
||||||
fires to Alertmanager for any `human_required` incident. Gauges are
|
fires to Alertmanager for any `human_required` incident. Gauges are
|
||||||
|
|||||||
@ -5,7 +5,7 @@ This is the mental model and demonstration script for the operator instance at
|
|||||||
consumer instance at `chat.bstein.dev` is intentionally separate and is not the
|
consumer instance at `chat.bstein.dev` is intentionally separate and is not the
|
||||||
place to perform infrastructure triage.
|
place to perform infrastructure triage.
|
||||||
|
|
||||||
`agent.hermes.bstein.dev` is the owner-only engineering control plane. Its root
|
`worker.bstein.dev` is the owner-only engineering control plane. Its root
|
||||||
serves the stock Hermes dashboard, including embedded chat/TUI and the native
|
serves the stock Hermes dashboard, including embedded chat/TUI and the native
|
||||||
sessions, files, models, logs, Kanban, skills, plugins, MCP, profiles, and
|
sessions, files, models, logs, Kanban, skills, plugins, MCP, profiles, and
|
||||||
configuration surfaces. `/terminal/` remains the raw full-screen TUI. The
|
configuration surfaces. `/terminal/` remains the raw full-screen TUI. The
|
||||||
@ -143,7 +143,7 @@ off. The final TypeScript build, Python compile, and source assertions remain
|
|||||||
mandatory, so an omitted or drifted replay fails before any image can pass
|
mandatory, so an omitted or drifted replay fails before any image can pass
|
||||||
release verification.
|
release verification.
|
||||||
|
|
||||||
From agent.hermes, trigger that one fixed job with:
|
From worker.bstein.dev, trigger that one fixed job with:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
jenkins_image_build_trigger.py '<reviewed full main commit>'
|
jenkins_image_build_trigger.py '<reviewed full main commit>'
|
||||||
|
|||||||
@ -17,3 +17,6 @@ spec:
|
|||||||
# the rename in #34 must not break links or sessions already in flight.
|
# the rename in #34 must not break links or sessions already in flight.
|
||||||
- chat.hermes.bstein.dev
|
- chat.hermes.bstein.dev
|
||||||
- triage.hermes.bstein.dev
|
- triage.hermes.bstein.dev
|
||||||
|
# worker.bstein.dev is the canonical owner-agent host; the legacy
|
||||||
|
# agent.hermes.bstein.dev SAN above stays until it is retired on purpose.
|
||||||
|
- worker.bstein.dev
|
||||||
|
|||||||
@ -142,7 +142,7 @@ data:
|
|||||||
- "*git clean -f*"
|
- "*git clean -f*"
|
||||||
|
|
||||||
dashboard:
|
dashboard:
|
||||||
public_url: https://agent.hermes.bstein.dev
|
public_url: https://worker.bstein.dev
|
||||||
|
|
||||||
display:
|
display:
|
||||||
compact: true
|
compact: true
|
||||||
@ -174,7 +174,7 @@ data:
|
|||||||
backup_keep: 5
|
backup_keep: 5
|
||||||
non_interactive_local_changes: stash
|
non_interactive_local_changes: stash
|
||||||
SOUL.md: |
|
SOUL.md: |
|
||||||
You are Brad's private Hermes coordinator at agent.hermes.bstein.dev. Turn
|
You are Brad's private Hermes coordinator at worker.bstein.dev. Turn
|
||||||
objectives into organized, reviewable delivery without making Brad manage
|
objectives into organized, reviewable delivery without making Brad manage
|
||||||
model names, terminals, or provider capacity.
|
model names, terminals, or provider capacity.
|
||||||
|
|
||||||
@ -408,7 +408,7 @@ data:
|
|||||||
START-HERE.md: |
|
START-HERE.md: |
|
||||||
# Agent Hermes
|
# Agent Hermes
|
||||||
|
|
||||||
The authenticated root of agent.hermes.bstein.dev opens Hermes' stock
|
The authenticated root of worker.bstein.dev opens Hermes' stock
|
||||||
dashboard with embedded chat/TUI, sessions, files, models, logs, Kanban,
|
dashboard with embedded chat/TUI, sessions, files, models, logs, Kanban,
|
||||||
skills, plugins, MCP, profiles, and configuration. `/terminal/` opens the
|
skills, plugins, MCP, profiles, and configuration. `/terminal/` opens the
|
||||||
raw full-screen Hermes TUI. Give Hermes
|
raw full-screen Hermes TUI. Give Hermes
|
||||||
|
|||||||
@ -624,14 +624,15 @@ spec:
|
|||||||
- {name: HERMES_DASHBOARD_HOST, value: 127.0.0.1}
|
- {name: HERMES_DASHBOARD_HOST, value: 127.0.0.1}
|
||||||
- {name: HERMES_DASHBOARD_PORT, value: "9119"}
|
- {name: HERMES_DASHBOARD_PORT, value: "9119"}
|
||||||
- {name: HERMES_DASHBOARD_FILES_ROOT, value: /opt/data/workspace}
|
- {name: HERMES_DASHBOARD_FILES_ROOT, value: /opt/data/workspace}
|
||||||
- {name: HERMES_DASHBOARD_PUBLIC_URL, value: https://agent.hermes.bstein.dev}
|
- {name: HERMES_DASHBOARD_PUBLIC_URL, value: https://worker.bstein.dev}
|
||||||
- {name: HERMES_TUI_AGENT_INIT_TIMEOUT_S, value: "180"}
|
- {name: HERMES_TUI_AGENT_INIT_TIMEOUT_S, value: "180"}
|
||||||
- {name: HERMES_STREAM_STALE_TIMEOUT, value: "600"}
|
- {name: HERMES_STREAM_STALE_TIMEOUT, value: "600"}
|
||||||
- {name: HERMES_API_CALL_STALE_TIMEOUT, value: "600"}
|
- {name: HERMES_API_CALL_STALE_TIMEOUT, value: "600"}
|
||||||
- {name: API_SERVER_ENABLED, value: "true"}
|
- {name: API_SERVER_ENABLED, value: "true"}
|
||||||
- {name: API_SERVER_HOST, value: 0.0.0.0}
|
- {name: API_SERVER_HOST, value: 0.0.0.0}
|
||||||
- {name: API_SERVER_PORT, value: "8642"}
|
- {name: API_SERVER_PORT, value: "8642"}
|
||||||
- {name: API_SERVER_CORS_ORIGINS, value: https://agent.hermes.bstein.dev}
|
# Comma-separated; the legacy origin stays until agent.hermes.bstein.dev is retired.
|
||||||
|
- {name: API_SERVER_CORS_ORIGINS, value: "https://worker.bstein.dev,https://agent.hermes.bstein.dev"}
|
||||||
- {name: HERMES_MEDIA_DELIVERY_STRICT, value: "1"}
|
- {name: HERMES_MEDIA_DELIVERY_STRICT, value: "1"}
|
||||||
- {name: HERMES_MEDIA_ALLOW_DIRS, value: /opt/data/workspace}
|
- {name: HERMES_MEDIA_ALLOW_DIRS, value: /opt/data/workspace}
|
||||||
- {name: HERMES_AUTO_ROUTER_PROFILE, value: agent}
|
- {name: HERMES_AUTO_ROUTER_PROFILE, value: agent}
|
||||||
@ -694,7 +695,11 @@ spec:
|
|||||||
args:
|
args:
|
||||||
- --provider=oidc
|
- --provider=oidc
|
||||||
- --config=/vault/secrets/oidc-config
|
- --config=/vault/secrets/oidc-config
|
||||||
- --redirect-url=https://agent.hermes.bstein.dev/oauth2/callback
|
# Single canonical callback. The __Host- cookie prefix below binds both the
|
||||||
|
# session and the CSRF cookie to one host, so a fresh login must start on
|
||||||
|
# worker.bstein.dev; agent.hermes.bstein.dev keeps serving already
|
||||||
|
# authenticated sessions until it is retired separately.
|
||||||
|
- --redirect-url=https://worker.bstein.dev/oauth2/callback
|
||||||
- --oidc-issuer-url=https://sso.bstein.dev/realms/atlas
|
- --oidc-issuer-url=https://sso.bstein.dev/realms/atlas
|
||||||
- --user-id-claim=sub
|
- --user-id-claim=sub
|
||||||
- --oidc-groups-claim=groups
|
- --oidc-groups-claim=groups
|
||||||
|
|||||||
@ -6,8 +6,10 @@ metadata:
|
|||||||
namespace: hermes
|
namespace: hermes
|
||||||
spec:
|
spec:
|
||||||
redirectRegex:
|
redirectRegex:
|
||||||
regex: ^https://agent\.hermes\.bstein\.dev/terminal$
|
# Host-preserving so the trailing-slash fixup works on both the canonical
|
||||||
replacement: https://agent.hermes.bstein.dev/terminal/
|
# worker.bstein.dev and the legacy agent.hermes.bstein.dev host.
|
||||||
|
regex: ^https://(worker\.bstein\.dev|agent\.hermes\.bstein\.dev)/terminal$
|
||||||
|
replacement: https://${1}/terminal/
|
||||||
permanent: false
|
permanent: false
|
||||||
---
|
---
|
||||||
apiVersion: traefik.io/v1alpha1
|
apiVersion: traefik.io/v1alpha1
|
||||||
@ -33,9 +35,21 @@ spec:
|
|||||||
ingressClassName: traefik
|
ingressClassName: traefik
|
||||||
tls:
|
tls:
|
||||||
- hosts:
|
- hosts:
|
||||||
|
- worker.bstein.dev
|
||||||
- agent.hermes.bstein.dev
|
- agent.hermes.bstein.dev
|
||||||
secretName: hermes-sites-tls
|
secretName: hermes-sites-tls
|
||||||
rules:
|
rules:
|
||||||
|
- host: worker.bstein.dev
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: oauth2-proxy-hermes-agent
|
||||||
|
port:
|
||||||
|
name: http
|
||||||
|
# Legacy host, served unchanged until it is retired in a separate change.
|
||||||
- host: agent.hermes.bstein.dev
|
- host: agent.hermes.bstein.dev
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
@ -60,9 +74,21 @@ spec:
|
|||||||
ingressClassName: traefik
|
ingressClassName: traefik
|
||||||
tls:
|
tls:
|
||||||
- hosts:
|
- hosts:
|
||||||
|
- worker.bstein.dev
|
||||||
- agent.hermes.bstein.dev
|
- agent.hermes.bstein.dev
|
||||||
secretName: hermes-sites-tls
|
secretName: hermes-sites-tls
|
||||||
rules:
|
rules:
|
||||||
|
- host: worker.bstein.dev
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /terminal
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: oauth2-proxy-hermes-agent
|
||||||
|
port:
|
||||||
|
name: http
|
||||||
|
# Legacy host, served unchanged until it is retired in a separate change.
|
||||||
- host: agent.hermes.bstein.dev
|
- host: agent.hermes.bstein.dev
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
@ -90,6 +116,7 @@ spec:
|
|||||||
- agent.hermes.bstein.dev
|
- agent.hermes.bstein.dev
|
||||||
- chat.bstein.dev
|
- chat.bstein.dev
|
||||||
- triage.bstein.dev
|
- triage.bstein.dev
|
||||||
|
- worker.bstein.dev
|
||||||
- chat.hermes.bstein.dev
|
- chat.hermes.bstein.dev
|
||||||
- triage.hermes.bstein.dev
|
- triage.hermes.bstein.dev
|
||||||
secretName: hermes-sites-tls
|
secretName: hermes-sites-tls
|
||||||
|
|||||||
@ -113,7 +113,7 @@ data:
|
|||||||
|
|
||||||
This is the dedicated triage appliance at triage.bstein.dev. Keep
|
This is the dedicated triage appliance at triage.bstein.dev. Keep
|
||||||
automated Ariadne intake and testing conversations here. Project delivery
|
automated Ariadne intake and testing conversations here. Project delivery
|
||||||
and coding orchestration belong to agent.hermes.bstein.dev; general user
|
and coding orchestration belong to worker.bstein.dev; general user
|
||||||
chat belongs to chat.bstein.dev.
|
chat belongs to chat.bstein.dev.
|
||||||
|
|
||||||
Start in AUTO routing with a careful, intelligence-biased posture. Every
|
Start in AUTO routing with a careful, intelligence-biased posture. Every
|
||||||
|
|||||||
@ -8,7 +8,7 @@ asserting health, placement, ownership, or current model availability.
|
|||||||
| Surface | Purpose | Identity boundary | State and permissions |
|
| Surface | Purpose | Identity boundary | State and permissions |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| `triage.bstein.dev` | Brad's automated testing triage | Keycloak plus an outer oauth2-proxy exact-email allow-list for `brad@bstein.dev` | `hermes` namespace, its own PVC and service account; read-only cluster triage plus approved internal evidence endpoints |
|
| `triage.bstein.dev` | Brad's automated testing triage | Keycloak plus an outer oauth2-proxy exact-email allow-list for `brad@bstein.dev` | `hermes` namespace, its own PVC and service account; read-only cluster triage plus approved internal evidence endpoints |
|
||||||
| `agent.hermes.bstein.dev` | Brad's project coordinator | Keycloak plus an outer oauth2-proxy exact-email allow-list for `brad@bstein.dev` | `hermes` namespace and separate PVC; native Hermes delegates bounded work while Herdr preserves real Codex and Claude Code CLI sessions when needed |
|
| `worker.bstein.dev` | Brad's project coordinator | Keycloak plus an outer oauth2-proxy exact-email allow-list for `brad@bstein.dev` | `hermes` namespace and separate PVC; native Hermes delegates bounded work while Herdr preserves real Codex and Claude Code CLI sessions when needed |
|
||||||
| `chat.bstein.dev` | Private consumer chat and research through Hermes WebUI or a linked Telegram DM | Keycloak login plus one-time Telegram account link | One Hermes process and PVC per assigned Keycloak subject; no Kubernetes RBAC, terminal, or private-service access |
|
| `chat.bstein.dev` | Private consumer chat and research through Hermes WebUI or a linked Telegram DM | Keycloak login plus one-time Telegram account link | One Hermes process and PVC per assigned Keycloak subject; no Kubernetes RBAC, terminal, or private-service access |
|
||||||
|
|
||||||
The instances do not share conversation state, credentials, profiles, skills
|
The instances do not share conversation state, credentials, profiles, skills
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
apiVersion: batch/v1
|
apiVersion: batch/v1
|
||||||
kind: Job
|
kind: Job
|
||||||
metadata:
|
metadata:
|
||||||
name: hermes-access-oidc-client-ensure-11
|
name: hermes-access-oidc-client-ensure-12
|
||||||
namespace: sso
|
namespace: sso
|
||||||
spec:
|
spec:
|
||||||
backoffLimit: 3
|
backoffLimit: 3
|
||||||
|
|||||||
@ -343,7 +343,8 @@ ensure_telegram_config() {
|
|||||||
ensure_hermes_owner
|
ensure_hermes_owner
|
||||||
ensure_proxy_client "hermes-chat-proxy" "https://chat.bstein.dev" "hermes/chat-oidc" \
|
ensure_proxy_client "hermes-chat-proxy" "https://chat.bstein.dev" "hermes/chat-oidc" \
|
||||||
"https://chat.hermes.bstein.dev"
|
"https://chat.hermes.bstein.dev"
|
||||||
ensure_proxy_client "hermes-agent-proxy" "https://agent.hermes.bstein.dev" "hermes/agent-oidc"
|
ensure_proxy_client "hermes-agent-proxy" "https://worker.bstein.dev" "hermes/agent-oidc" \
|
||||||
|
"https://agent.hermes.bstein.dev"
|
||||||
ensure_proxy_client "hermes-triage-proxy" "https://triage.bstein.dev" "hermes/triage-oidc" \
|
ensure_proxy_client "hermes-triage-proxy" "https://triage.bstein.dev" "hermes/triage-oidc" \
|
||||||
"https://triage.hermes.bstein.dev"
|
"https://triage.hermes.bstein.dev"
|
||||||
ensure_service_account_client "hermes-automation" "hermes/developer-keycloak"
|
ensure_service_account_client "hermes-automation" "hermes/developer-keycloak"
|
||||||
|
|||||||
@ -26,7 +26,7 @@ data:
|
|||||||
https://budget.bstein.dev
|
https://budget.bstein.dev
|
||||||
https://money.bstein.dev
|
https://money.bstein.dev
|
||||||
https://health.bstein.dev
|
https://health.bstein.dev
|
||||||
https://agent.hermes.bstein.dev
|
https://worker.bstein.dev
|
||||||
https://chat.bstein.dev
|
https://chat.bstein.dev
|
||||||
https://triage.bstein.dev
|
https://triage.bstein.dev
|
||||||
https://cassandra.bstein.dev
|
https://cassandra.bstein.dev
|
||||||
|
|||||||
@ -63,7 +63,7 @@ def test_owner_agent_installs_the_pinned_operator_toolchain():
|
|||||||
assert 'chmod 0644 "${profile_file}"' in init_command
|
assert 'chmod 0644 "${profile_file}"' in init_command
|
||||||
|
|
||||||
|
|
||||||
def test_owner_agent_uses_only_the_canonical_hostname():
|
def test_owner_agent_uses_the_canonical_hostname():
|
||||||
paths = [
|
paths = [
|
||||||
HERMES / "agent-configmap.yaml",
|
HERMES / "agent-configmap.yaml",
|
||||||
HERMES / "agent-deployment.yaml",
|
HERMES / "agent-deployment.yaml",
|
||||||
@ -73,7 +73,40 @@ def test_owner_agent_uses_only_the_canonical_hostname():
|
|||||||
for path in paths:
|
for path in paths:
|
||||||
content = path.read_text()
|
content = path.read_text()
|
||||||
assert "agent.bstein.dev" not in content
|
assert "agent.bstein.dev" not in content
|
||||||
assert "agent.hermes.bstein.dev" in content
|
assert "worker.bstein.dev" in content
|
||||||
|
|
||||||
|
|
||||||
|
def test_legacy_owner_host_stays_served_until_it_is_retired_separately():
|
||||||
|
"""The rename must not cut off the host the operator reaches Hermes on.
|
||||||
|
|
||||||
|
``agent.hermes.bstein.dev`` keeps resolving, routing and validating TLS
|
||||||
|
until it is retired in its own change, so rolling the rename back never
|
||||||
|
needs more than reverting the oauth2-proxy callback.
|
||||||
|
"""
|
||||||
|
legacy = "agent.hermes.bstein.dev"
|
||||||
|
canonical = "worker.bstein.dev"
|
||||||
|
|
||||||
|
coredns = (
|
||||||
|
Path(__file__).parents[2] / "infrastructure/core/coredns-custom.yaml"
|
||||||
|
).read_text()
|
||||||
|
assert f"192.168.22.9 {legacy}" in coredns
|
||||||
|
assert f"192.168.22.9 {canonical}" in coredns
|
||||||
|
|
||||||
|
certificate = yaml.safe_load((HERMES / "agent-certificate.yaml").read_text())
|
||||||
|
assert {legacy, canonical} <= set(certificate["spec"]["dnsNames"])
|
||||||
|
|
||||||
|
ingresses = [
|
||||||
|
doc
|
||||||
|
for doc in yaml.safe_load_all((HERMES / "agent-ingress.yaml").read_text())
|
||||||
|
if doc
|
||||||
|
and doc.get("kind") == "Ingress"
|
||||||
|
and doc["metadata"]["name"].startswith("hermes-agent-")
|
||||||
|
]
|
||||||
|
assert len(ingresses) == 2
|
||||||
|
for ingress in ingresses:
|
||||||
|
assert {legacy, canonical} <= {rule["host"] for rule in ingress["spec"]["rules"]}
|
||||||
|
for tls in ingress["spec"]["tls"]:
|
||||||
|
assert {legacy, canonical} <= set(tls["hosts"])
|
||||||
|
|
||||||
|
|
||||||
def test_agent_reconnect_retains_complete_history_and_long_tool_budget():
|
def test_agent_reconnect_retains_complete_history_and_long_tool_budget():
|
||||||
|
|||||||
@ -122,7 +122,7 @@ def test_owner_agent_installs_the_pinned_operator_toolchain():
|
|||||||
assert 'chmod 0644 "${profile_file}"' in init_command
|
assert 'chmod 0644 "${profile_file}"' in init_command
|
||||||
|
|
||||||
|
|
||||||
def test_owner_agent_uses_only_the_canonical_hostname():
|
def test_owner_agent_uses_the_canonical_hostname():
|
||||||
paths = [
|
paths = [
|
||||||
HERMES / "agent-configmap.yaml",
|
HERMES / "agent-configmap.yaml",
|
||||||
HERMES / "agent-deployment.yaml",
|
HERMES / "agent-deployment.yaml",
|
||||||
@ -132,7 +132,7 @@ def test_owner_agent_uses_only_the_canonical_hostname():
|
|||||||
for path in paths:
|
for path in paths:
|
||||||
content = path.read_text()
|
content = path.read_text()
|
||||||
assert "agent.bstein.dev" not in content
|
assert "agent.bstein.dev" not in content
|
||||||
assert "agent.hermes.bstein.dev" in content
|
assert "worker.bstein.dev" in content
|
||||||
|
|
||||||
|
|
||||||
def test_agent_reconnect_retains_complete_history_and_long_tool_budget():
|
def test_agent_reconnect_retains_complete_history_and_long_tool_budget():
|
||||||
|
|||||||
@ -35,8 +35,10 @@ PUBLIC_HOSTS = {
|
|||||||
"triage.hermes.bstein.dev": "oauth2-proxy-hermes-triage",
|
"triage.hermes.bstein.dev": "oauth2-proxy-hermes-triage",
|
||||||
}
|
}
|
||||||
|
|
||||||
# The agent surface was deliberately untouched by the rename.
|
# The agent surface is mid-rename too: worker.bstein.dev is canonical and the
|
||||||
|
# legacy name stays served until a deliberate retirement change removes it.
|
||||||
AGENT_HOST = "agent.hermes.bstein.dev"
|
AGENT_HOST = "agent.hermes.bstein.dev"
|
||||||
|
AGENT_CANONICAL_HOST = "worker.bstein.dev"
|
||||||
|
|
||||||
|
|
||||||
def _docs(path: Path) -> list[dict]:
|
def _docs(path: Path) -> list[dict]:
|
||||||
@ -107,21 +109,29 @@ def test_host_is_registered_with_keycloak(host: str, ensure_script: str):
|
|||||||
assert f"https://{host}" in ensure_script
|
assert f"https://{host}" in ensure_script
|
||||||
|
|
||||||
|
|
||||||
def test_agent_surface_was_not_touched_by_the_rename(
|
@pytest.mark.parametrize("host", [AGENT_HOST, AGENT_CANONICAL_HOST])
|
||||||
certificate: dict, coredns_hosts: set[str], ensure_script: str
|
def test_agent_surface_serves_both_hosts_during_its_rename(
|
||||||
|
host: str, certificate: dict, coredns_hosts: set[str], ensure_script: str
|
||||||
):
|
):
|
||||||
assert AGENT_HOST in certificate["spec"]["dnsNames"]
|
"""Retiring the legacy owner host is a separate, deliberate change.
|
||||||
assert AGENT_HOST in coredns_hosts
|
|
||||||
assert f"https://{AGENT_HOST}" in ensure_script
|
The agent hosts are served by hermes-agent-dashboard/-terminal rather than
|
||||||
|
hermes-sites, so their Ingress rules are asserted in
|
||||||
|
``test_hermes_agent_runtime_patches.py`` instead of the table above.
|
||||||
|
"""
|
||||||
|
assert host in certificate["spec"]["dnsNames"]
|
||||||
|
assert host in coredns_hosts
|
||||||
|
assert f"https://{host}" in ensure_script
|
||||||
|
|
||||||
|
|
||||||
def test_ensure_script_registers_legacy_and_renamed_origins_together(
|
def test_ensure_script_registers_legacy_and_renamed_origins_together(
|
||||||
ensure_script: str,
|
ensure_script: str,
|
||||||
):
|
):
|
||||||
"""The renamed proxies must carry both origins; the agent proxy only one."""
|
"""Every renamed proxy must accept its old and new origin at the same time."""
|
||||||
for client, canonical, legacy in (
|
for client, canonical, legacy in (
|
||||||
("hermes-chat-proxy", "chat.bstein.dev", "chat.hermes.bstein.dev"),
|
("hermes-chat-proxy", "chat.bstein.dev", "chat.hermes.bstein.dev"),
|
||||||
("hermes-triage-proxy", "triage.bstein.dev", "triage.hermes.bstein.dev"),
|
("hermes-triage-proxy", "triage.bstein.dev", "triage.hermes.bstein.dev"),
|
||||||
|
("hermes-agent-proxy", "worker.bstein.dev", "agent.hermes.bstein.dev"),
|
||||||
):
|
):
|
||||||
call = re.search(
|
call = re.search(
|
||||||
rf'ensure_proxy_client "{client}".*?(?=\nensure_)',
|
rf'ensure_proxy_client "{client}".*?(?=\nensure_)',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user