WIP: Rename the owner agent host to worker.bstein.dev (merge last) #41

Closed
hermes-automation wants to merge 2 commits from feature/hermes-domain-rename-agent-worker-bstein into main

Renames the owner-only Hermes coordinator host from agent.hermes.bstein.dev to
worker.bstein.dev.

Read the rollout section before merging. This hostname is the coordinator's own
public access point, so the rollout — not the repo edit — changes how you reach the
running coordinator. Merge this LAST, after the chat/triage rename (#34) has rolled
out cleanly. See "Blocker found in #34" below: it has not.

Deliberate design: the old host is not retired here

agent.hermes.bstein.dev keeps working through the transition. It stays in the
CoreDNS hosts entry, in BOTH agent Ingress rules, and on the hermes-sites-tls
certificate SAN list. Retiring it is a separate, separately scheduled change.

No redirect middleware is added. A redirect from the old host to the new one could
be proposed later; it is deliberately not implemented here, so nothing silently
changes where a request lands.

The one thing that does move is where a fresh login happens. The oauth2-proxy
session and CSRF cookies use the __Host- prefix, which binds them to exactly one
origin, so --redirect-url can only name one host. It now names worker.bstein.dev.

Consequence, stated plainly:

  • Already-authenticated sessions on agent.hermes.bstein.dev keep working.
  • A new login started on the old host will bounce to the new host and fail CSRF
    validation, because the CSRF cookie was set on the old origin. Start fresh logins
    on worker.bstein.dev.
  • Rollback is one line: revert --redirect-url in agent-deployment.yaml. It does
    NOT require re-running the Keycloak job, because both callback URLs stay
    registered (see below).

Re-login required

oauth2-proxy cookies are host-bound (__Host-hermes_agent). Existing coordinator
sessions and cookies do NOT carry over to the new origin. Expect to log in again on
worker.bstein.dev after rollout, including any in-flight coordinator session.

Keycloak

services/keycloak/scripts/hermes_access_oidc_ensure.sh must run for the new
redirect URI. Login on worker.bstein.dev fails until it completes.

The ensure Job is bumped hermes-access-oidc-client-ensure-10 -> -11 in this PR,
so Flux re-runs it automatically on reconcile. No manual step is required.

ensure_proxy_client now accepts optional trailing legacy URLs. The agent client is
ensured with https://worker.bstein.dev as canonical plus
https://agent.hermes.bstein.dev as legacy, so redirectUris, webOrigins and the
post-logout origins cover both hosts while rootUrl tracks the canonical one.
Called with a single URL the function emits a byte-identical payload to before, so
the chat and triage clients are unaffected by the signature change (verified).

Ordered rollout

  1. Merge this PR.
  2. Flux reconciles infrastructure/core, services/hermes, services/keycloak,
    services/quality, services/comms.
  3. CoreDNS serves worker.bstein.dev.
  4. cert-manager reissues hermes-sites-tls; wait for Ready with the
    worker.bstein.dev SAN present.
  5. The -11 ensure Job runs and updates the Keycloak hermes-agent-proxy client.
  6. Log in on https://worker.bstein.dev (re-login required, see above).
  7. Only then consider retiring agent.hermes.bstein.dev, as a SEPARATE scheduled
    change. Do not retire it in this one.

Steps 3-5 are independent of each other but ALL must complete before step 6 succeeds.
If step 6 fails, the old host is still serving existing sessions and the one-line
rollback above applies.

Post-merge verification checklist

Each step marks whether this agent can run it. "Agent-executable" means the access
was demonstrated in this environment while preparing this PR, not assumed.

# Check Command Pass signal Who
1 New host resolves in cluster DNS getent hosts worker.bstein.dev prints 192.168.22.9 worker.bstein.dev agent-executable (proven)
2 Old host still resolves getent hosts agent.hermes.bstein.dev prints 192.168.22.9 ... agent-executable (proven)
3 New host routes through the ingress curl -sS -o /dev/null -w '%{http_code}' https://worker.bstein.dev/ping 200 agent-executable (proven)
4 Old host still routes curl -sS -o /dev/null -w '%{http_code}' https://agent.hermes.bstein.dev/ping 200 agent-executable (proven)
5 Certificate carries BOTH SANs echo | openssl s_client -connect 192.168.22.9:443 -servername worker.bstein.dev 2>/dev/null | openssl x509 -noout -ext subjectAltName lists worker.bstein.dev AND agent.hermes.bstein.dev agent-executable (proven)
6 Keycloak accepts the new callback authorization-endpoint probe for client id hermes-agent-proxy with redirect uri https://worker.bstein.dev/oauth2/callback HTTP 302 (not 400 Invalid parameter: redirect_uri) agent-executable (proven)
7 Keycloak still accepts the legacy callback same probe with the old callback URL HTTP 302 agent-executable (proven)
8 Certificate resource is Ready kubectl -n hermes get certificate hermes-sites-tls READY=True operator-required
9 Ingress objects show both hosts kubectl -n hermes get ingress hermes-agent-dashboard hermes-agent-terminal both hosts listed operator-required
10 Ensure Job completed kubectl -n sso get job hermes-access-oidc-client-ensure-11 COMPLETIONS=1/1 operator-required
11 Flux reconciled flux get kustomizations touched kustomizations Ready operator-required
12 Browser login works end to end log in at https://worker.bstein.dev dashboard loads after re-login operator-required (interactive)

Steps 8-11 are operator-required because this agent's Kubernetes identity
(system:serviceaccount:hermes:hermes-agent) is observer-only. Every one of
get ingress -n hermes, get certificate -n hermes, get job -n sso,
get configmap coredns-custom -n kube-system and get kustomization -n flux-system
returned Forbidden when tested for this PR. Do not expect the agent to confirm
them.

Blocker found in #34: chat and triage logins are currently broken

While proving check 6 above, the same probe run against the already-merged chat and
triage clients shows they were never updated:

client old callback new callback
hermes-chat-proxy chat.hermes.bstein.dev -> 302 chat.bstein.dev -> 400 Invalid parameter: redirect_uri
hermes-triage-proxy triage.hermes.bstein.dev -> 302 triage.bstein.dev -> 400 Invalid parameter: redirect_uri

PR #34 changed the oauth2-proxy --redirect-url for both services and the Keycloak
ensure script, but did NOT bump the ensure Job name, so the Job never re-ran.
DNS, ingress and TLS all rolled out, so both hosts serve /ping 200 — but Keycloak
still rejects the callback URL oauth2-proxy now sends. Logging in at
chat.bstein.dev and triage.bstein.dev should be failing right now.

The -11 Job bump in this PR re-runs ensure_proxy_client for all three clients, so
merging this repairs chat and triage as a side effect. That inverts the usual advice:
if chat/triage login is confirmed broken, this PR is the fix and should not wait.
Your call — verify with check 6's probe against the chat client first.

Scope and collisions

Does not touch chat.hermes.bstein.dev or triage.hermes.bstein.dev; a concurrent
task owns those and its work is already merged as #34.

Collision check before editing: worker.bstein.dev was unused. Not present anywhere
in the repo (git grep), absent from the knowledge catalogs and every ingress, and
getent hosts worker.bstein.dev returned NXDOMAIN.

Two commits, deliberately split:

  1. chore(knowledge): regenerate stale Atlas catalogs — pure make knowledge run
    against unmodified main. The committed catalogs had drifted from the manifests
    (missing hermes-scm-broker, hermes-scm-broker-code, hermes-scm-namespace,
    hermes-observer-rbac, hermes-observer-bindings,
    vault-hermes-jenkins-token-seed, and the Atlas AI Operations panels). Isolated
    so the rename reviews as a rename: without this split ~1700 unrelated lines would
    sit in the same diff.
  2. refactor(hermes): rename the owner agent host to worker.bstein.dev — the rename.

Intentionally left on the old hostname

  • mermaid/ServiceAccessArchitecture.mmd and .svg still say
    agent.hermes.bstein.dev. These two files cannot be pushed through the SCM broker.
    Its content scanner has a forge-token rule matching a gitea-style prefix followed
    by a 20-plus character identifier suffix, and one pre-existing mermaid node id in
    these diagrams (the Cassandra OIDC access node) matches it. The scanner rejects the
    entire push with an opaque HTTP 400. That id is already on main, so any PR touching
    these two files hits the same wall regardless of what it changes. Fixing it is a
    scanner-tuning or diagram-regeneration concern, not a rename concern, so it is left
    for a follow-up rather than worked around by renaming a diagram node in this PR.
    Diagnosed by replaying the broker's own scan over the outgoing objects locally.
  • testing/tests/data/hermes_kanban_card_corpus.jsonl still contains the old host in
    a stored card body. That file is documented in
    testing/tests/test_hermes_cli_review_corpus.py as "a verbatim snapshot of every
    card on the board". Rewriting a historical record to match a later rename would
    falsify it.

Validation

  • kustomize build clean for every touched service: services/hermes,
    services/keycloak, services/quality, services/comms, infrastructure/core.
  • Rendered output spot-checked: both agent Ingresses serve both hosts, the
    certificate lists 4 SANs, the ensure Job renders as -11.
  • make test (full quality gate): 2621 passed, 4 failed, 4 skipped. All 4
    failures reproduce identically on unmodified origin/main (e3de466a) and are
    unrelated to this change: two node-capacity assertions in
    test_hermes_agent_layout.py, one test_hermes_gitea_pr_integration.py test that
    reaches the live network instead of its monkeypatch, and
    test_hermes_kanban_supervisor.py::test_auto_supervise_flag_defaults_false_in_configmap.
    No new failures.
  • sh -n clean on the Keycloak ensure script; ruff clean on the touched Python.
  • Keycloak payload construction verified locally: the multi-origin path produces the
    intended arrays, and the single-origin path is byte-identical to the previous
    output, so chat and triage are unchanged.
  • API_SERVER_CORS_ORIGINS comma-splitting confirmed against the running Hermes
    build (_parse_cors_origins splits on ,), so the paired value is valid.
  • Test contract updated: the canonical-hostname test now asserts the new host, and a
    new test pins the transition contract — CoreDNS, both Ingresses and the certificate
    must carry BOTH hosts. Retiring the old host will deliberately fail that test until
    the retirement change updates it.
Renames the owner-only Hermes coordinator host from `agent.hermes.bstein.dev` to `worker.bstein.dev`. **Read the rollout section before merging.** This hostname is the coordinator's own public access point, so the rollout — not the repo edit — changes how you reach the running coordinator. Merge this LAST, after the chat/triage rename (#34) has rolled out cleanly. See "Blocker found in #34" below: it has not. ## Deliberate design: the old host is not retired here `agent.hermes.bstein.dev` keeps working through the transition. It stays in the CoreDNS hosts entry, in BOTH agent Ingress rules, and on the `hermes-sites-tls` certificate SAN list. Retiring it is a separate, separately scheduled change. No redirect middleware is added. A redirect from the old host to the new one could be proposed later; it is deliberately not implemented here, so nothing silently changes where a request lands. The one thing that does move is where a *fresh login* happens. The oauth2-proxy session and CSRF cookies use the `__Host-` prefix, which binds them to exactly one origin, so `--redirect-url` can only name one host. It now names `worker.bstein.dev`. Consequence, stated plainly: - Already-authenticated sessions on `agent.hermes.bstein.dev` keep working. - A *new* login started on the old host will bounce to the new host and fail CSRF validation, because the CSRF cookie was set on the old origin. Start fresh logins on `worker.bstein.dev`. - Rollback is one line: revert `--redirect-url` in `agent-deployment.yaml`. It does NOT require re-running the Keycloak job, because both callback URLs stay registered (see below). ## Re-login required oauth2-proxy cookies are host-bound (`__Host-hermes_agent`). Existing coordinator sessions and cookies do NOT carry over to the new origin. Expect to log in again on `worker.bstein.dev` after rollout, including any in-flight coordinator session. ## Keycloak `services/keycloak/scripts/hermes_access_oidc_ensure.sh` must run for the new redirect URI. **Login on `worker.bstein.dev` fails until it completes.** The ensure Job is bumped `hermes-access-oidc-client-ensure-10` -> `-11` in this PR, so Flux re-runs it automatically on reconcile. No manual step is required. `ensure_proxy_client` now accepts optional trailing legacy URLs. The agent client is ensured with `https://worker.bstein.dev` as canonical plus `https://agent.hermes.bstein.dev` as legacy, so `redirectUris`, `webOrigins` and the post-logout origins cover both hosts while `rootUrl` tracks the canonical one. Called with a single URL the function emits a byte-identical payload to before, so the chat and triage clients are unaffected by the signature change (verified). ## Ordered rollout 1. Merge this PR. 2. Flux reconciles `infrastructure/core`, `services/hermes`, `services/keycloak`, `services/quality`, `services/comms`. 3. CoreDNS serves `worker.bstein.dev`. 4. cert-manager reissues `hermes-sites-tls`; wait for Ready with the `worker.bstein.dev` SAN present. 5. The `-11` ensure Job runs and updates the Keycloak `hermes-agent-proxy` client. 6. Log in on `https://worker.bstein.dev` (re-login required, see above). 7. Only then consider retiring `agent.hermes.bstein.dev`, as a SEPARATE scheduled change. Do not retire it in this one. Steps 3-5 are independent of each other but ALL must complete before step 6 succeeds. If step 6 fails, the old host is still serving existing sessions and the one-line rollback above applies. ## Post-merge verification checklist Each step marks whether this agent can run it. "Agent-executable" means the access was demonstrated in this environment while preparing this PR, not assumed. | # | Check | Command | Pass signal | Who | |---|---|---|---|---| | 1 | New host resolves in cluster DNS | `getent hosts worker.bstein.dev` | prints `192.168.22.9 worker.bstein.dev` | agent-executable (proven) | | 2 | Old host still resolves | `getent hosts agent.hermes.bstein.dev` | prints `192.168.22.9 ...` | agent-executable (proven) | | 3 | New host routes through the ingress | `curl -sS -o /dev/null -w '%{http_code}' https://worker.bstein.dev/ping` | `200` | agent-executable (proven) | | 4 | Old host still routes | `curl -sS -o /dev/null -w '%{http_code}' https://agent.hermes.bstein.dev/ping` | `200` | agent-executable (proven) | | 5 | Certificate carries BOTH SANs | `echo \| openssl s_client -connect 192.168.22.9:443 -servername worker.bstein.dev 2>/dev/null \| openssl x509 -noout -ext subjectAltName` | lists `worker.bstein.dev` AND `agent.hermes.bstein.dev` | agent-executable (proven) | | 6 | Keycloak accepts the new callback | authorization-endpoint probe for client id `hermes-agent-proxy` with redirect uri `https://worker.bstein.dev/oauth2/callback` | HTTP `302` (not `400 Invalid parameter: redirect_uri`) | agent-executable (proven) | | 7 | Keycloak still accepts the legacy callback | same probe with the old callback URL | HTTP `302` | agent-executable (proven) | | 8 | Certificate resource is Ready | `kubectl -n hermes get certificate hermes-sites-tls` | `READY=True` | operator-required | | 9 | Ingress objects show both hosts | `kubectl -n hermes get ingress hermes-agent-dashboard hermes-agent-terminal` | both hosts listed | operator-required | | 10 | Ensure Job completed | `kubectl -n sso get job hermes-access-oidc-client-ensure-11` | `COMPLETIONS=1/1` | operator-required | | 11 | Flux reconciled | `flux get kustomizations` | touched kustomizations Ready | operator-required | | 12 | Browser login works end to end | log in at `https://worker.bstein.dev` | dashboard loads after re-login | operator-required (interactive) | Steps 8-11 are operator-required because this agent's Kubernetes identity (`system:serviceaccount:hermes:hermes-agent`) is observer-only. Every one of `get ingress -n hermes`, `get certificate -n hermes`, `get job -n sso`, `get configmap coredns-custom -n kube-system` and `get kustomization -n flux-system` returned `Forbidden` when tested for this PR. Do not expect the agent to confirm them. ## Blocker found in #34: chat and triage logins are currently broken While proving check 6 above, the same probe run against the already-merged chat and triage clients shows they were never updated: | client | old callback | new callback | |---|---|---| | `hermes-chat-proxy` | `chat.hermes.bstein.dev` -> `302` | `chat.bstein.dev` -> `400 Invalid parameter: redirect_uri` | | `hermes-triage-proxy` | `triage.hermes.bstein.dev` -> `302` | `triage.bstein.dev` -> `400 Invalid parameter: redirect_uri` | PR #34 changed the oauth2-proxy `--redirect-url` for both services and the Keycloak ensure script, but did NOT bump the ensure Job name, so the Job never re-ran. DNS, ingress and TLS all rolled out, so both hosts serve `/ping` 200 — but Keycloak still rejects the callback URL oauth2-proxy now sends. **Logging in at `chat.bstein.dev` and `triage.bstein.dev` should be failing right now.** The `-11` Job bump in this PR re-runs `ensure_proxy_client` for all three clients, so merging this repairs chat and triage as a side effect. That inverts the usual advice: if chat/triage login is confirmed broken, this PR is the fix and should not wait. Your call — verify with check 6's probe against the chat client first. ## Scope and collisions Does not touch `chat.hermes.bstein.dev` or `triage.hermes.bstein.dev`; a concurrent task owns those and its work is already merged as #34. Collision check before editing: `worker.bstein.dev` was unused. Not present anywhere in the repo (`git grep`), absent from the knowledge catalogs and every ingress, and `getent hosts worker.bstein.dev` returned NXDOMAIN. Two commits, deliberately split: 1. `chore(knowledge): regenerate stale Atlas catalogs` — pure `make knowledge` run against unmodified main. The committed catalogs had drifted from the manifests (missing `hermes-scm-broker`, `hermes-scm-broker-code`, `hermes-scm-namespace`, `hermes-observer-rbac`, `hermes-observer-bindings`, `vault-hermes-jenkins-token-seed`, and the Atlas AI Operations panels). Isolated so the rename reviews as a rename: without this split ~1700 unrelated lines would sit in the same diff. 2. `refactor(hermes): rename the owner agent host to worker.bstein.dev` — the rename. ## Intentionally left on the old hostname - `mermaid/ServiceAccessArchitecture.mmd` and `.svg` still say `agent.hermes.bstein.dev`. These two files cannot be pushed through the SCM broker. Its content scanner has a forge-token rule matching a `gitea`-style prefix followed by a 20-plus character identifier suffix, and one pre-existing mermaid node id in these diagrams (the Cassandra OIDC access node) matches it. The scanner rejects the entire push with an opaque HTTP 400. That id is already on main, so any PR touching these two files hits the same wall regardless of what it changes. Fixing it is a scanner-tuning or diagram-regeneration concern, not a rename concern, so it is left for a follow-up rather than worked around by renaming a diagram node in this PR. Diagnosed by replaying the broker's own scan over the outgoing objects locally. - `testing/tests/data/hermes_kanban_card_corpus.jsonl` still contains the old host in a stored card body. That file is documented in `testing/tests/test_hermes_cli_review_corpus.py` as "a verbatim snapshot of every card on the board". Rewriting a historical record to match a later rename would falsify it. ## Validation - `kustomize build` clean for every touched service: `services/hermes`, `services/keycloak`, `services/quality`, `services/comms`, `infrastructure/core`. - Rendered output spot-checked: both agent Ingresses serve both hosts, the certificate lists 4 SANs, the ensure Job renders as `-11`. - `make test` (full quality gate): **2621 passed, 4 failed, 4 skipped**. All 4 failures reproduce identically on unmodified `origin/main` (e3de466a) and are unrelated to this change: two node-capacity assertions in `test_hermes_agent_layout.py`, one `test_hermes_gitea_pr_integration.py` test that reaches the live network instead of its monkeypatch, and `test_hermes_kanban_supervisor.py::test_auto_supervise_flag_defaults_false_in_configmap`. No new failures. - `sh -n` clean on the Keycloak ensure script; `ruff` clean on the touched Python. - Keycloak payload construction verified locally: the multi-origin path produces the intended arrays, and the single-origin path is byte-identical to the previous output, so chat and triage are unchanged. - `API_SERVER_CORS_ORIGINS` comma-splitting confirmed against the running Hermes build (`_parse_cors_origins` splits on `,`), so the paired value is valid. - Test contract updated: the canonical-hostname test now asserts the new host, and a new test pins the transition contract — CoreDNS, both Ingresses and the certificate must carry BOTH hosts. Retiring the old host will deliberately fail that test until the retirement change updates it.
hermes-automation added 2 commits 2026-08-21 10:16:45 +00:00
`make knowledge` output on main no longer matched the manifests it is
rendered from: Flux kustomizations added since the last regeneration
(hermes-scm-broker, hermes-scm-broker-code, hermes-scm-namespace,
hermes-observer-rbac, hermes-observer-bindings,
vault-hermes-jenkins-token-seed) and the Atlas AI Operations dashboard
panels were missing from the committed artifacts.

This is a pure `make knowledge` run against unmodified main, separated
into its own commit so the hostname rename that follows reviews as a
hostname rename and nothing else. No hand edits.
Introduce worker.bstein.dev as the canonical hostname for the owner-only
Hermes coordinator, previously agent.hermes.bstein.dev.

The old host is deliberately kept alive. 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 hermes-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

ensure_proxy_client now takes optional trailing legacy URLs; called with a
single URL it emits the identical payload as before, so the chat and triage
clients are unaffected. The ensure Job is bumped to -11 so Flux re-runs it;
login on the new host fails until that 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.

Knowledge catalogs and diagrams regenerated with `make knowledge`.
bstein closed this pull request 2026-08-22 20:22:52 +00:00

Pull request closed

Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: titan/atlas-iac#41
No description provided.