hermes: grant owner agent union access
All checks were successful
Tests / Declarative: Post Actions passed: 215

This commit is contained in:
jenkins 2026-08-10 17:05:14 -03:00
parent 4e765635a1
commit d0011866f9
34 changed files with 2934 additions and 528 deletions

View File

@ -6,7 +6,7 @@ USER root
# Codex uses the distribution bubblewrap package when a bounded sandbox is
# requested. tmux keeps the stock Hermes TUI alive across browser reconnects;
# Herdr remains a background worker transport instead of the visible shell.
# durable coding workers are dispatched by native Hermes Kanban lanes.
RUN apt-get update \
&& apt-get install -y --no-install-recommends bubblewrap tmux \
&& rm -rf /var/lib/apt/lists/*

View File

@ -1,8 +1,8 @@
{
"counts": {
"helmrelease_host_hints": 22,
"http_endpoints": 54,
"services": 70,
"workloads": 100
"helmrelease_host_hints": 23,
"http_endpoints": 60,
"services": 85,
"workloads": 119
}
}

View File

@ -16,6 +16,16 @@
"path": "services/bstein-dev-home/migration-jobs",
"targetNamespace": "bstein-dev-home"
},
{
"name": "cassandra",
"path": "services/cassandra",
"targetNamespace": "cassandra"
},
{
"name": "cassandra-auth",
"path": "services/cassandra-auth",
"targetNamespace": "sso"
},
{
"name": "cert-manager",
"path": "infrastructure/cert-manager",
@ -91,6 +101,16 @@
"path": "services/hermes",
"targetNamespace": "hermes"
},
{
"name": "hermes-chat",
"path": "services/hermes-chat",
"targetNamespace": "hermes-chat"
},
{
"name": "hermes-triage-demo",
"path": "services/hermes-triage-demo",
"targetNamespace": null
},
{
"name": "jellyfin",
"path": "services/jellyfin",
@ -284,7 +304,7 @@
"node-role.kubernetes.io/worker": "true"
},
"images": [
"registry.bstein.dev/bstein/bstein-dev-home-backend:0.1.1-414"
"registry.bstein.dev/bstein/bstein-dev-home-backend:0.1.1-454"
]
},
{
@ -300,7 +320,7 @@
"node-role.kubernetes.io/worker": "true"
},
"images": [
"registry.bstein.dev/bstein/bstein-dev-home-frontend:0.1.1-414"
"registry.bstein.dev/bstein/bstein-dev-home-frontend:0.1.1-454"
]
},
{
@ -332,6 +352,63 @@
"python:3.11-slim"
]
},
{
"kind": "Deployment",
"namespace": "cassandra",
"name": "cassandra-backend",
"labels": {
"app": "cassandra-backend"
},
"serviceAccountName": "cassandra-backend",
"nodeSelector": {
"cassandra.bstein.dev/node-pool": "oceanus",
"kubernetes.io/arch": "amd64"
},
"images": [
"registry.bstein.dev/cassandra/cassandra-backend:0.9.63"
]
},
{
"kind": "Deployment",
"namespace": "cassandra",
"name": "cassandra-frontend",
"labels": {
"app": "cassandra-frontend"
},
"serviceAccountName": "cassandra-frontend",
"nodeSelector": {},
"images": [
"registry.bstein.dev/cassandra/cassandra-frontend:0.9.63"
]
},
{
"kind": "Deployment",
"namespace": "cassandra",
"name": "cassandra-vault-sync",
"labels": {
"app": "cassandra-vault-sync"
},
"serviceAccountName": "cassandra-vault-sync",
"nodeSelector": {},
"images": [
"alpine:3.20"
]
},
{
"kind": "StatefulSet",
"namespace": "cassandra",
"name": "cassandra-postgres",
"labels": {
"app": "cassandra-postgres"
},
"serviceAccountName": "cassandra-postgres",
"nodeSelector": {
"cassandra.bstein.dev/node-pool": "oceanus"
},
"images": [
"postgres:15"
]
},
{
"kind": "Deployment",
"namespace": "climate",
@ -825,7 +902,104 @@
"serviceAccountName": "hermes-triage",
"nodeSelector": {},
"images": [
"nousresearch/hermes-agent@sha256:9c841866021c54c4596849f6135717e8a4d52ba510b7f52c50aef1de1a283973"
"registry.bstein.dev/bstein/hermes-agent@sha256:15c5c538c0b58686af2e54e10bc870b23284789d485a609349df24ed3053622f",
"registry.bstein.dev/bstein/hermes-webui@sha256:c109e6faec1d6b86859a182bc845a2e35d64260459dda3892c0510db1dc7272d"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "hermes-agent",
"labels": {
"app": "hermes-agent"
},
"serviceAccountName": "hermes-agent",
"nodeSelector": {},
"images": [
"registry.bstein.dev/bstein/hermes-agent@sha256:46de11122226a68adad6d902e81ef9b4b53deb0433dcc61f57fa43fae2e3a3a5",
"registry.bstein.dev/bstein/hermes-webui@sha256:c109e6faec1d6b86859a182bc845a2e35d64260459dda3892c0510db1dc7272d"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "hermes-chat-router",
"labels": {
"app": "hermes-chat-router"
},
"serviceAccountName": "hermes-chat",
"nodeSelector": {},
"images": [
"registry.bstein.dev/bstein/hermes-chat-router@sha256:882f9c3af7268618a19424765536f4cf6c37e8d380e515780baf4104e5b2931e"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "hermes-chat-sandbox-0",
"labels": {
"ai.bstein.dev/tenant-ordinal": "0",
"app": "hermes-chat-sandbox"
},
"serviceAccountName": null,
"nodeSelector": {},
"images": [
"registry.bstein.dev/bstein/hermes-chat-sandbox@sha256:17ee62b8e61c08573a3a8cca903b38ec43800cb44ec29340e1bc095176544bca"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "hermes-chat-sandbox-1",
"labels": {
"ai.bstein.dev/tenant-ordinal": "1",
"app": "hermes-chat-sandbox"
},
"serviceAccountName": null,
"nodeSelector": {},
"images": [
"registry.bstein.dev/bstein/hermes-chat-sandbox@sha256:17ee62b8e61c08573a3a8cca903b38ec43800cb44ec29340e1bc095176544bca"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "hermes-chat-sandbox-2",
"labels": {
"ai.bstein.dev/tenant-ordinal": "2",
"app": "hermes-chat-sandbox"
},
"serviceAccountName": null,
"nodeSelector": {},
"images": [
"registry.bstein.dev/bstein/hermes-chat-sandbox@sha256:17ee62b8e61c08573a3a8cca903b38ec43800cb44ec29340e1bc095176544bca"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "hermes-chat-sandbox-3",
"labels": {
"ai.bstein.dev/tenant-ordinal": "3",
"app": "hermes-chat-sandbox"
},
"serviceAccountName": null,
"nodeSelector": {},
"images": [
"registry.bstein.dev/bstein/hermes-chat-sandbox@sha256:17ee62b8e61c08573a3a8cca903b38ec43800cb44ec29340e1bc095176544bca"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "hermes-model-gate",
"labels": {
"app": "hermes-model-gate"
},
"serviceAccountName": "hermes-model-gate",
"nodeSelector": {},
"images": [
"python@sha256:6d43704baacd1bfbe7c295d7f13079d5d8104ed33568873133f8fc69980419df"
]
},
{
@ -841,6 +1015,89 @@
"ollama/ollama@sha256:2c9595c555fd70a28363489ac03bd5bf9e7c5bdf2890373c3a830ffd7252ce6d"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "hermes-stt",
"labels": {
"app": "hermes-stt"
},
"serviceAccountName": null,
"nodeSelector": {
"kubernetes.io/hostname": "titan-21"
},
"images": [
"registry.bstein.dev/bstein/hermes-jetson-stt@sha256:dfb0b0788dcf63747d8c761c6d3ea6a7459bcd8d0a5c92bcd4f6f434a760249f"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "hermes-tts",
"labels": {
"app": "hermes-tts"
},
"serviceAccountName": null,
"nodeSelector": {
"kubernetes.io/hostname": "titan-20"
},
"images": [
"registry.bstein.dev/bstein/hermes-jetson-tts@sha256:5cb9e57faab46365bff606c559af57b9505892be2909aea1ac523568d478b2cc"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "oauth2-proxy-hermes-agent",
"labels": {
"app": "oauth2-proxy-hermes-agent"
},
"serviceAccountName": "hermes-agent",
"nodeSelector": {},
"images": [
"quay.io/oauth2-proxy/oauth2-proxy:v7.15.3@sha256:10a1165743a192e1940b4708fb9647027185ce11a681a1c5519b442ff7f1f561"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "oauth2-proxy-hermes-chat",
"labels": {
"app": "oauth2-proxy-hermes-chat"
},
"serviceAccountName": "hermes-chat",
"nodeSelector": {},
"images": [
"quay.io/oauth2-proxy/oauth2-proxy:v7.15.3@sha256:10a1165743a192e1940b4708fb9647027185ce11a681a1c5519b442ff7f1f561"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "oauth2-proxy-hermes-triage",
"labels": {
"app": "oauth2-proxy-hermes-triage"
},
"serviceAccountName": "hermes-vault",
"nodeSelector": {},
"images": [
"quay.io/oauth2-proxy/oauth2-proxy:v7.15.3@sha256:10a1165743a192e1940b4708fb9647027185ce11a681a1c5519b442ff7f1f561"
]
},
{
"kind": "StatefulSet",
"namespace": "hermes",
"name": "hermes-chat-tenant",
"labels": {
"app": "hermes-chat-tenant"
},
"serviceAccountName": "hermes-chat",
"nodeSelector": {},
"images": [
"registry.bstein.dev/bstein/hermes-agent@sha256:10522c69676e250b5d0014d811ed9eb706d0b7c16f35cf66650f480e63ab3ab5",
"registry.bstein.dev/bstein/hermes-webui@sha256:c109e6faec1d6b86859a182bc845a2e35d64260459dda3892c0510db1dc7272d"
]
},
{
"kind": "Deployment",
"namespace": "jellyfin",
@ -1188,7 +1445,7 @@
"kubernetes.io/os": "linux"
},
"images": [
"registry.bstein.dev/bstein/metis-sentinel:0.1.0-243-amd64"
"registry.bstein.dev/bstein/metis-sentinel:0.1.0-283-amd64"
]
},
{
@ -1204,7 +1461,7 @@
"kubernetes.io/os": "linux"
},
"images": [
"registry.bstein.dev/bstein/metis-sentinel:0.1.0-243-arm64"
"registry.bstein.dev/bstein/metis-sentinel:0.1.0-283-arm64"
]
},
{
@ -1293,7 +1550,7 @@
"node-role.kubernetes.io/worker": "true"
},
"images": [
"registry.bstein.dev/bstein/ariadne:0.1.0-356"
"registry.bstein.dev/bstein/ariadne:0.1.0-448"
]
},
{
@ -1321,7 +1578,7 @@
"serviceAccountName": "metis",
"nodeSelector": {},
"images": [
"registry.bstein.dev/bstein/metis:0.1.0-243-arm64"
"registry.bstein.dev/bstein/metis:0.1.0-283-arm64"
]
},
{
@ -1724,7 +1981,22 @@
"node-role.kubernetes.io/worker": "true"
},
"images": [
"vaultwarden/server:1.35.2"
"vaultwarden/server:1.37.0@sha256:e6443e3d5ed8fcee2204b89ec778d7f24d0173bcc42d1ea34f990304f5f63f51"
]
},
{
"kind": "Deployment",
"namespace": "veles",
"name": "veles-artifact-export-for-cassandra",
"labels": {
"app": "veles-artifact-export-for-cassandra"
},
"serviceAccountName": null,
"nodeSelector": {
"cassandra.bstein.dev/node-pool": "oceanus"
},
"images": [
"python:3.12-alpine"
]
},
{
@ -1740,7 +2012,7 @@
"veles.bstein.dev/node-pool": "oceanus"
},
"images": [
"registry.bstein.dev/veles/veles-backend:0.7.56"
"registry.bstein.dev/veles/veles-backend:0.7.65"
]
},
{
@ -1753,7 +2025,23 @@
"serviceAccountName": "veles-frontend",
"nodeSelector": {},
"images": [
"registry.bstein.dev/veles/veles-frontend:0.7.56"
"registry.bstein.dev/veles/veles-frontend:0.7.65"
]
},
{
"kind": "Deployment",
"namespace": "veles",
"name": "veles-generator-worker",
"labels": {
"app": "veles-generator-worker"
},
"serviceAccountName": "veles-generator",
"nodeSelector": {
"kubernetes.io/arch": "amd64",
"veles.bstein.dev/node-pool": "oceanus"
},
"images": [
"registry.bstein.dev/veles/veles-generator-worker:0.7.65"
]
},
{
@ -1850,6 +2138,54 @@
}
]
},
{
"namespace": "cassandra",
"name": "cassandra-backend",
"type": "ClusterIP",
"selector": {
"app": "cassandra-backend"
},
"ports": [
{
"name": "http",
"port": 80,
"targetPort": 8796,
"protocol": "TCP"
}
]
},
{
"namespace": "cassandra",
"name": "cassandra-frontend",
"type": "ClusterIP",
"selector": {
"app": "cassandra-frontend"
},
"ports": [
{
"name": "http",
"port": 80,
"targetPort": 8080,
"protocol": "TCP"
}
]
},
{
"namespace": "cassandra",
"name": "cassandra-postgres",
"type": "ClusterIP",
"selector": {
"app": "cassandra-postgres"
},
"ports": [
{
"name": "postgres",
"port": 5432,
"targetPort": 5432,
"protocol": "TCP"
}
]
},
{
"namespace": "climate",
"name": "typhon",
@ -2684,6 +3020,98 @@
}
]
},
{
"namespace": "hermes",
"name": "hermes-agent",
"type": "ClusterIP",
"selector": {
"app": "hermes-agent"
},
"ports": [
{
"name": "dashboard",
"port": 8787,
"targetPort": "dashboard",
"protocol": "TCP"
},
{
"name": "terminal",
"port": 7681,
"targetPort": "terminal",
"protocol": "TCP"
}
]
},
{
"namespace": "hermes",
"name": "hermes-chat-router",
"type": "ClusterIP",
"selector": {
"app": "hermes-chat-router"
},
"ports": [
{
"name": "http",
"port": 8080,
"targetPort": "http",
"protocol": "TCP"
}
]
},
{
"namespace": "hermes",
"name": "hermes-chat-sandbox",
"type": "ClusterIP",
"selector": {
"app": "hermes-chat-sandbox"
},
"ports": [
{
"name": "http",
"port": 9080,
"targetPort": "http",
"protocol": "TCP"
}
]
},
{
"namespace": "hermes",
"name": "hermes-chat-tenant",
"type": "ClusterIP",
"selector": {
"app": "hermes-chat-tenant"
},
"ports": [
{
"name": "webui",
"port": 8787,
"targetPort": "webui",
"protocol": "TCP"
},
{
"name": "api",
"port": 8642,
"targetPort": "api",
"protocol": "TCP"
}
]
},
{
"namespace": "hermes",
"name": "hermes-model-gate",
"type": "ClusterIP",
"selector": {
"app": "hermes-model-gate"
},
"ports": [
{
"name": "http",
"port": 11434,
"targetPort": "http",
"protocol": "TCP"
}
]
},
{
"namespace": "hermes",
"name": "hermes-ollama",
@ -2700,6 +3128,108 @@
}
]
},
{
"namespace": "hermes",
"name": "hermes-stt",
"type": "ClusterIP",
"selector": {
"app": "hermes-stt"
},
"ports": [
{
"name": "http",
"port": 9000,
"targetPort": "http",
"protocol": "TCP"
}
]
},
{
"namespace": "hermes",
"name": "hermes-triage",
"type": "ClusterIP",
"selector": {
"app": "hermes"
},
"ports": [
{
"name": "api",
"port": 8642,
"targetPort": "api",
"protocol": "TCP"
},
{
"name": "dashboard",
"port": 8787,
"targetPort": "dashboard",
"protocol": "TCP"
}
]
},
{
"namespace": "hermes",
"name": "hermes-tts",
"type": "ClusterIP",
"selector": {
"app": "hermes-tts"
},
"ports": [
{
"name": "http",
"port": 9001,
"targetPort": "http",
"protocol": "TCP"
}
]
},
{
"namespace": "hermes",
"name": "oauth2-proxy-hermes-agent",
"type": "ClusterIP",
"selector": {
"app": "oauth2-proxy-hermes-agent"
},
"ports": [
{
"name": "http",
"port": 80,
"targetPort": "http",
"protocol": "TCP"
}
]
},
{
"namespace": "hermes",
"name": "oauth2-proxy-hermes-chat",
"type": "ClusterIP",
"selector": {
"app": "oauth2-proxy-hermes-chat"
},
"ports": [
{
"name": "http",
"port": 80,
"targetPort": "http",
"protocol": "TCP"
}
]
},
{
"namespace": "hermes",
"name": "oauth2-proxy-hermes-triage",
"type": "ClusterIP",
"selector": {
"app": "oauth2-proxy-hermes-triage"
},
"ports": [
{
"name": "http",
"port": 80,
"targetPort": "http",
"protocol": "TCP"
}
]
},
{
"namespace": "jellyfin",
"name": "jellyfin",
@ -3328,6 +3858,22 @@
}
]
},
{
"namespace": "veles",
"name": "veles-artifact-export-for-cassandra",
"type": "ClusterIP",
"selector": {
"app": "veles-artifact-export-for-cassandra"
},
"ports": [
{
"name": "http",
"port": 8765,
"targetPort": "http",
"protocol": "TCP"
}
]
},
{
"namespace": "veles",
"name": "veles-backend",
@ -3379,22 +3925,22 @@
],
"http_endpoints": [
{
"host": "agent.bstein.dev",
"host": "agent.hermes.bstein.dev",
"path": "/",
"backend": {
"namespace": "hermes",
"service": "hermes",
"port": "dashboard",
"service": "oauth2-proxy-hermes-agent",
"port": "http",
"workloads": [
{
"kind": "Deployment",
"name": "hermes"
"name": "oauth2-proxy-hermes-agent"
}
]
},
"via": {
"kind": "Ingress",
"name": "agent",
"name": "hermes-sites",
"source": "hermes"
}
},
@ -3558,6 +4104,86 @@
"source": "comms"
}
},
{
"host": "cassandra.bstein.dev",
"path": "/",
"backend": {
"namespace": "cassandra",
"service": "cassandra-frontend",
"port": 80,
"workloads": [
{
"kind": "Deployment",
"name": "cassandra-frontend"
}
]
},
"via": {
"kind": "Ingress",
"name": "cassandra",
"source": "cassandra"
}
},
{
"host": "cassandra.bstein.dev",
"path": "/api",
"backend": {
"namespace": "cassandra",
"service": "cassandra-backend",
"port": 80,
"workloads": [
{
"kind": "Deployment",
"name": "cassandra-backend"
}
]
},
"via": {
"kind": "Ingress",
"name": "cassandra",
"source": "cassandra"
}
},
{
"host": "cassandra.bstein.dev",
"path": "/events",
"backend": {
"namespace": "cassandra",
"service": "cassandra-backend",
"port": 80,
"workloads": [
{
"kind": "Deployment",
"name": "cassandra-backend"
}
]
},
"via": {
"kind": "Ingress",
"name": "cassandra",
"source": "cassandra"
}
},
{
"host": "cassandra.bstein.dev",
"path": "/ws",
"backend": {
"namespace": "cassandra",
"service": "cassandra-backend",
"port": 80,
"workloads": [
{
"kind": "Deployment",
"name": "cassandra-backend"
}
]
},
"via": {
"kind": "Ingress",
"name": "cassandra",
"source": "cassandra"
}
},
{
"host": "chat.ai.bstein.dev",
"path": "/",
@ -3578,6 +4204,26 @@
"source": "bstein-dev-home"
}
},
{
"host": "chat.hermes.bstein.dev",
"path": "/",
"backend": {
"namespace": "hermes",
"service": "oauth2-proxy-hermes-chat",
"port": "http",
"workloads": [
{
"kind": "Deployment",
"name": "oauth2-proxy-hermes-chat"
}
]
},
"via": {
"kind": "Ingress",
"name": "hermes-sites",
"source": "hermes"
}
},
{
"host": "ci.bstein.dev",
"path": "/",
@ -4318,6 +4964,26 @@
"source": "planka"
}
},
{
"host": "triage.hermes.bstein.dev",
"path": "/",
"backend": {
"namespace": "hermes",
"service": "oauth2-proxy-hermes-triage",
"port": "http",
"workloads": [
{
"kind": "Deployment",
"name": "oauth2-proxy-hermes-triage"
}
]
},
"via": {
"kind": "Ingress",
"name": "hermes-sites",
"source": "hermes"
}
},
{
"host": "vault.bstein.dev",
"path": "/",
@ -4472,7 +5138,8 @@
"mail.bstein.dev"
],
"monitoring:monitoring/alertmanager": [
"alerts.bstein.dev"
"alerts.bstein.dev",
"bstein.dev"
],
"monitoring:monitoring/grafana": [
"atlas.bstein.dev",

View File

@ -11,6 +11,12 @@ sources:
- name: bstein-dev-home-migrations
path: services/bstein-dev-home/migration-jobs
targetNamespace: bstein-dev-home
- name: cassandra
path: services/cassandra
targetNamespace: cassandra
- name: cassandra-auth
path: services/cassandra-auth
targetNamespace: sso
- name: cert-manager
path: infrastructure/cert-manager
targetNamespace: cert-manager
@ -56,6 +62,12 @@ sources:
- name: hermes
path: services/hermes
targetNamespace: hermes
- name: hermes-chat
path: services/hermes-chat
targetNamespace: hermes-chat
- name: hermes-triage-demo
path: services/hermes-triage-demo
targetNamespace: null
- name: jellyfin
path: services/jellyfin
targetNamespace: jellyfin
@ -175,7 +187,7 @@ workloads:
kubernetes.io/arch: arm64
node-role.kubernetes.io/worker: 'true'
images:
- registry.bstein.dev/bstein/bstein-dev-home-backend:0.1.1-414
- registry.bstein.dev/bstein/bstein-dev-home-backend:0.1.1-454
- kind: Deployment
namespace: bstein-dev-home
name: bstein-dev-home-frontend
@ -186,7 +198,7 @@ workloads:
kubernetes.io/arch: arm64
node-role.kubernetes.io/worker: 'true'
images:
- registry.bstein.dev/bstein/bstein-dev-home-frontend:0.1.1-414
- registry.bstein.dev/bstein/bstein-dev-home-frontend:0.1.1-454
- kind: Deployment
namespace: bstein-dev-home
name: bstein-dev-home-vault-sync
@ -207,6 +219,45 @@ workloads:
node-role.kubernetes.io/worker: 'true'
images:
- python:3.11-slim
- kind: Deployment
namespace: cassandra
name: cassandra-backend
labels:
app: cassandra-backend
serviceAccountName: cassandra-backend
nodeSelector:
cassandra.bstein.dev/node-pool: oceanus
kubernetes.io/arch: amd64
images:
- registry.bstein.dev/cassandra/cassandra-backend:0.9.63
- kind: Deployment
namespace: cassandra
name: cassandra-frontend
labels:
app: cassandra-frontend
serviceAccountName: cassandra-frontend
nodeSelector: {}
images:
- registry.bstein.dev/cassandra/cassandra-frontend:0.9.63
- kind: Deployment
namespace: cassandra
name: cassandra-vault-sync
labels:
app: cassandra-vault-sync
serviceAccountName: cassandra-vault-sync
nodeSelector: {}
images:
- alpine:3.20
- kind: StatefulSet
namespace: cassandra
name: cassandra-postgres
labels:
app: cassandra-postgres
serviceAccountName: cassandra-postgres
nodeSelector:
cassandra.bstein.dev/node-pool: oceanus
images:
- postgres:15
- kind: Deployment
namespace: climate
name: typhon
@ -553,7 +604,76 @@ workloads:
serviceAccountName: hermes-triage
nodeSelector: {}
images:
- nousresearch/hermes-agent@sha256:9c841866021c54c4596849f6135717e8a4d52ba510b7f52c50aef1de1a283973
- registry.bstein.dev/bstein/hermes-agent@sha256:15c5c538c0b58686af2e54e10bc870b23284789d485a609349df24ed3053622f
- registry.bstein.dev/bstein/hermes-webui@sha256:c109e6faec1d6b86859a182bc845a2e35d64260459dda3892c0510db1dc7272d
- kind: Deployment
namespace: hermes
name: hermes-agent
labels:
app: hermes-agent
serviceAccountName: hermes-agent
nodeSelector: {}
images:
- registry.bstein.dev/bstein/hermes-agent@sha256:46de11122226a68adad6d902e81ef9b4b53deb0433dcc61f57fa43fae2e3a3a5
- registry.bstein.dev/bstein/hermes-webui@sha256:c109e6faec1d6b86859a182bc845a2e35d64260459dda3892c0510db1dc7272d
- kind: Deployment
namespace: hermes
name: hermes-chat-router
labels:
app: hermes-chat-router
serviceAccountName: hermes-chat
nodeSelector: {}
images:
- registry.bstein.dev/bstein/hermes-chat-router@sha256:882f9c3af7268618a19424765536f4cf6c37e8d380e515780baf4104e5b2931e
- kind: Deployment
namespace: hermes
name: hermes-chat-sandbox-0
labels:
ai.bstein.dev/tenant-ordinal: '0'
app: hermes-chat-sandbox
serviceAccountName: null
nodeSelector: {}
images:
- registry.bstein.dev/bstein/hermes-chat-sandbox@sha256:17ee62b8e61c08573a3a8cca903b38ec43800cb44ec29340e1bc095176544bca
- kind: Deployment
namespace: hermes
name: hermes-chat-sandbox-1
labels:
ai.bstein.dev/tenant-ordinal: '1'
app: hermes-chat-sandbox
serviceAccountName: null
nodeSelector: {}
images:
- registry.bstein.dev/bstein/hermes-chat-sandbox@sha256:17ee62b8e61c08573a3a8cca903b38ec43800cb44ec29340e1bc095176544bca
- kind: Deployment
namespace: hermes
name: hermes-chat-sandbox-2
labels:
ai.bstein.dev/tenant-ordinal: '2'
app: hermes-chat-sandbox
serviceAccountName: null
nodeSelector: {}
images:
- registry.bstein.dev/bstein/hermes-chat-sandbox@sha256:17ee62b8e61c08573a3a8cca903b38ec43800cb44ec29340e1bc095176544bca
- kind: Deployment
namespace: hermes
name: hermes-chat-sandbox-3
labels:
ai.bstein.dev/tenant-ordinal: '3'
app: hermes-chat-sandbox
serviceAccountName: null
nodeSelector: {}
images:
- registry.bstein.dev/bstein/hermes-chat-sandbox@sha256:17ee62b8e61c08573a3a8cca903b38ec43800cb44ec29340e1bc095176544bca
- kind: Deployment
namespace: hermes
name: hermes-model-gate
labels:
app: hermes-model-gate
serviceAccountName: hermes-model-gate
nodeSelector: {}
images:
- python@sha256:6d43704baacd1bfbe7c295d7f13079d5d8104ed33568873133f8fc69980419df
- kind: Deployment
namespace: hermes
name: hermes-ollama
@ -563,6 +683,63 @@ workloads:
nodeSelector: {}
images:
- ollama/ollama@sha256:2c9595c555fd70a28363489ac03bd5bf9e7c5bdf2890373c3a830ffd7252ce6d
- kind: Deployment
namespace: hermes
name: hermes-stt
labels:
app: hermes-stt
serviceAccountName: null
nodeSelector:
kubernetes.io/hostname: titan-21
images:
- registry.bstein.dev/bstein/hermes-jetson-stt@sha256:dfb0b0788dcf63747d8c761c6d3ea6a7459bcd8d0a5c92bcd4f6f434a760249f
- kind: Deployment
namespace: hermes
name: hermes-tts
labels:
app: hermes-tts
serviceAccountName: null
nodeSelector:
kubernetes.io/hostname: titan-20
images:
- registry.bstein.dev/bstein/hermes-jetson-tts@sha256:5cb9e57faab46365bff606c559af57b9505892be2909aea1ac523568d478b2cc
- kind: Deployment
namespace: hermes
name: oauth2-proxy-hermes-agent
labels:
app: oauth2-proxy-hermes-agent
serviceAccountName: hermes-agent
nodeSelector: {}
images:
- quay.io/oauth2-proxy/oauth2-proxy:v7.15.3@sha256:10a1165743a192e1940b4708fb9647027185ce11a681a1c5519b442ff7f1f561
- kind: Deployment
namespace: hermes
name: oauth2-proxy-hermes-chat
labels:
app: oauth2-proxy-hermes-chat
serviceAccountName: hermes-chat
nodeSelector: {}
images:
- quay.io/oauth2-proxy/oauth2-proxy:v7.15.3@sha256:10a1165743a192e1940b4708fb9647027185ce11a681a1c5519b442ff7f1f561
- kind: Deployment
namespace: hermes
name: oauth2-proxy-hermes-triage
labels:
app: oauth2-proxy-hermes-triage
serviceAccountName: hermes-vault
nodeSelector: {}
images:
- quay.io/oauth2-proxy/oauth2-proxy:v7.15.3@sha256:10a1165743a192e1940b4708fb9647027185ce11a681a1c5519b442ff7f1f561
- kind: StatefulSet
namespace: hermes
name: hermes-chat-tenant
labels:
app: hermes-chat-tenant
serviceAccountName: hermes-chat
nodeSelector: {}
images:
- registry.bstein.dev/bstein/hermes-agent@sha256:10522c69676e250b5d0014d811ed9eb706d0b7c16f35cf66650f480e63ab3ab5
- registry.bstein.dev/bstein/hermes-webui@sha256:c109e6faec1d6b86859a182bc845a2e35d64260459dda3892c0510db1dc7272d
- kind: Deployment
namespace: jellyfin
name: jellyfin
@ -801,7 +978,7 @@ workloads:
kubernetes.io/arch: amd64
kubernetes.io/os: linux
images:
- registry.bstein.dev/bstein/metis-sentinel:0.1.0-243-amd64
- registry.bstein.dev/bstein/metis-sentinel:0.1.0-283-amd64
- kind: DaemonSet
namespace: maintenance
name: metis-sentinel-arm64
@ -812,7 +989,7 @@ workloads:
kubernetes.io/arch: arm64
kubernetes.io/os: linux
images:
- registry.bstein.dev/bstein/metis-sentinel:0.1.0-243-arm64
- registry.bstein.dev/bstein/metis-sentinel:0.1.0-283-arm64
- kind: DaemonSet
namespace: maintenance
name: node-image-sweeper
@ -872,7 +1049,7 @@ workloads:
kubernetes.io/arch: arm64
node-role.kubernetes.io/worker: 'true'
images:
- registry.bstein.dev/bstein/ariadne:0.1.0-356
- registry.bstein.dev/bstein/ariadne:0.1.0-448
- kind: Deployment
namespace: maintenance
name: maintenance-vault-sync
@ -891,7 +1068,7 @@ workloads:
serviceAccountName: metis
nodeSelector: {}
images:
- registry.bstein.dev/bstein/metis:0.1.0-243-arm64
- registry.bstein.dev/bstein/metis:0.1.0-283-arm64
- kind: Deployment
namespace: maintenance
name: oauth2-proxy-metis
@ -1166,7 +1343,17 @@ workloads:
kubernetes.io/arch: arm64
node-role.kubernetes.io/worker: 'true'
images:
- vaultwarden/server:1.35.2
- vaultwarden/server:1.37.0@sha256:e6443e3d5ed8fcee2204b89ec778d7f24d0173bcc42d1ea34f990304f5f63f51
- kind: Deployment
namespace: veles
name: veles-artifact-export-for-cassandra
labels:
app: veles-artifact-export-for-cassandra
serviceAccountName: null
nodeSelector:
cassandra.bstein.dev/node-pool: oceanus
images:
- python:3.12-alpine
- kind: Deployment
namespace: veles
name: veles-backend
@ -1177,7 +1364,7 @@ workloads:
kubernetes.io/arch: amd64
veles.bstein.dev/node-pool: oceanus
images:
- registry.bstein.dev/veles/veles-backend:0.7.56
- registry.bstein.dev/veles/veles-backend:0.7.65
- kind: Deployment
namespace: veles
name: veles-frontend
@ -1186,7 +1373,18 @@ workloads:
serviceAccountName: veles-frontend
nodeSelector: {}
images:
- registry.bstein.dev/veles/veles-frontend:0.7.56
- registry.bstein.dev/veles/veles-frontend:0.7.65
- kind: Deployment
namespace: veles
name: veles-generator-worker
labels:
app: veles-generator-worker
serviceAccountName: veles-generator
nodeSelector:
kubernetes.io/arch: amd64
veles.bstein.dev/node-pool: oceanus
images:
- registry.bstein.dev/veles/veles-generator-worker:0.7.65
- kind: Deployment
namespace: veles
name: veles-vault-sync
@ -1247,6 +1445,36 @@ services:
port: 80
targetPort: 8080
protocol: TCP
- namespace: cassandra
name: cassandra-backend
type: ClusterIP
selector:
app: cassandra-backend
ports:
- name: http
port: 80
targetPort: 8796
protocol: TCP
- namespace: cassandra
name: cassandra-frontend
type: ClusterIP
selector:
app: cassandra-frontend
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
- namespace: cassandra
name: cassandra-postgres
type: ClusterIP
selector:
app: cassandra-postgres
ports:
- name: postgres
port: 5432
targetPort: 5432
protocol: TCP
- namespace: climate
name: typhon
type: ClusterIP
@ -1785,6 +2013,64 @@ services:
port: 9119
targetPort: dashboard
protocol: TCP
- namespace: hermes
name: hermes-agent
type: ClusterIP
selector:
app: hermes-agent
ports:
- name: dashboard
port: 8787
targetPort: dashboard
protocol: TCP
- name: terminal
port: 7681
targetPort: terminal
protocol: TCP
- namespace: hermes
name: hermes-chat-router
type: ClusterIP
selector:
app: hermes-chat-router
ports:
- name: http
port: 8080
targetPort: http
protocol: TCP
- namespace: hermes
name: hermes-chat-sandbox
type: ClusterIP
selector:
app: hermes-chat-sandbox
ports:
- name: http
port: 9080
targetPort: http
protocol: TCP
- namespace: hermes
name: hermes-chat-tenant
type: ClusterIP
selector:
app: hermes-chat-tenant
ports:
- name: webui
port: 8787
targetPort: webui
protocol: TCP
- name: api
port: 8642
targetPort: api
protocol: TCP
- namespace: hermes
name: hermes-model-gate
type: ClusterIP
selector:
app: hermes-model-gate
ports:
- name: http
port: 11434
targetPort: http
protocol: TCP
- namespace: hermes
name: hermes-ollama
type: ClusterIP
@ -1795,6 +2081,70 @@ services:
port: 11434
targetPort: http
protocol: TCP
- namespace: hermes
name: hermes-stt
type: ClusterIP
selector:
app: hermes-stt
ports:
- name: http
port: 9000
targetPort: http
protocol: TCP
- namespace: hermes
name: hermes-triage
type: ClusterIP
selector:
app: hermes
ports:
- name: api
port: 8642
targetPort: api
protocol: TCP
- name: dashboard
port: 8787
targetPort: dashboard
protocol: TCP
- namespace: hermes
name: hermes-tts
type: ClusterIP
selector:
app: hermes-tts
ports:
- name: http
port: 9001
targetPort: http
protocol: TCP
- namespace: hermes
name: oauth2-proxy-hermes-agent
type: ClusterIP
selector:
app: oauth2-proxy-hermes-agent
ports:
- name: http
port: 80
targetPort: http
protocol: TCP
- namespace: hermes
name: oauth2-proxy-hermes-chat
type: ClusterIP
selector:
app: oauth2-proxy-hermes-chat
ports:
- name: http
port: 80
targetPort: http
protocol: TCP
- namespace: hermes
name: oauth2-proxy-hermes-triage
type: ClusterIP
selector:
app: oauth2-proxy-hermes-triage
ports:
- name: http
port: 80
targetPort: http
protocol: TCP
- namespace: jellyfin
name: jellyfin
type: ClusterIP
@ -2191,6 +2541,16 @@ services:
port: 80
targetPort: http
protocol: TCP
- namespace: veles
name: veles-artifact-export-for-cassandra
type: ClusterIP
selector:
app: veles-artifact-export-for-cassandra
ports:
- name: http
port: 8765
targetPort: http
protocol: TCP
- namespace: veles
name: veles-backend
type: ClusterIP
@ -2222,18 +2582,18 @@ services:
targetPort: 5432
protocol: TCP
http_endpoints:
- host: agent.bstein.dev
- host: agent.hermes.bstein.dev
path: /
backend:
namespace: hermes
service: hermes
port: dashboard
service: oauth2-proxy-hermes-agent
port: http
workloads:
- kind: Deployment
name: hermes
name: oauth2-proxy-hermes-agent
via:
kind: Ingress
name: agent
name: hermes-sites
source: hermes
- host: auth.bstein.dev
path: /
@ -2337,6 +2697,54 @@ http_endpoints:
kind: Ingress
name: element-call
source: comms
- host: cassandra.bstein.dev
path: /
backend:
namespace: cassandra
service: cassandra-frontend
port: 80
workloads:
- kind: Deployment
name: cassandra-frontend
via:
kind: Ingress
name: cassandra
source: cassandra
- host: cassandra.bstein.dev
path: /api
backend:
namespace: cassandra
service: cassandra-backend
port: 80
workloads: &id002
- kind: Deployment
name: cassandra-backend
via:
kind: Ingress
name: cassandra
source: cassandra
- host: cassandra.bstein.dev
path: /events
backend:
namespace: cassandra
service: cassandra-backend
port: 80
workloads: *id002
via:
kind: Ingress
name: cassandra
source: cassandra
- host: cassandra.bstein.dev
path: /ws
backend:
namespace: cassandra
service: cassandra-backend
port: 80
workloads: *id002
via:
kind: Ingress
name: cassandra
source: cassandra
- host: chat.ai.bstein.dev
path: /
backend:
@ -2350,6 +2758,19 @@ http_endpoints:
kind: Ingress
name: bstein-dev-home
source: bstein-dev-home
- host: chat.hermes.bstein.dev
path: /
backend:
namespace: hermes
service: oauth2-proxy-hermes-chat
port: http
workloads:
- kind: Deployment
name: oauth2-proxy-hermes-chat
via:
kind: Ingress
name: hermes-sites
source: hermes
- host: ci.bstein.dev
path: /
backend:
@ -2454,7 +2875,7 @@ http_endpoints:
namespace: comms
service: matrix-guest-register
port: 8080
workloads: &id003
workloads: &id004
- kind: Deployment
name: matrix-guest-register
via:
@ -2467,7 +2888,7 @@ http_endpoints:
namespace: comms
service: matrix-authentication-service
port: 8080
workloads: &id002
workloads: &id003
- kind: Deployment
name: matrix-authentication-service
via:
@ -2480,7 +2901,7 @@ http_endpoints:
namespace: comms
service: matrix-authentication-service
port: 8080
workloads: *id002
workloads: *id003
via:
kind: Ingress
name: matrix-routing
@ -2491,7 +2912,7 @@ http_endpoints:
namespace: comms
service: matrix-authentication-service
port: 8080
workloads: *id002
workloads: *id003
via:
kind: Ingress
name: matrix-routing
@ -2502,7 +2923,7 @@ http_endpoints:
namespace: comms
service: matrix-guest-register
port: 8080
workloads: *id003
workloads: *id004
via:
kind: Ingress
name: matrix-routing
@ -2550,7 +2971,7 @@ http_endpoints:
namespace: comms
service: matrix-authentication-service
port: 8080
workloads: *id002
workloads: *id003
via:
kind: Ingress
name: matrix-routing
@ -2594,7 +3015,7 @@ http_endpoints:
namespace: comms
service: matrix-guest-register
port: 8080
workloads: *id003
workloads: *id004
via:
kind: Ingress
name: matrix-routing
@ -2605,7 +3026,7 @@ http_endpoints:
namespace: comms
service: matrix-authentication-service
port: 8080
workloads: *id002
workloads: *id003
via:
kind: Ingress
name: matrix-routing
@ -2616,7 +3037,7 @@ http_endpoints:
namespace: comms
service: matrix-authentication-service
port: 8080
workloads: *id002
workloads: *id003
via:
kind: Ingress
name: matrix-routing
@ -2627,7 +3048,7 @@ http_endpoints:
namespace: comms
service: matrix-authentication-service
port: 8080
workloads: *id002
workloads: *id003
via:
kind: Ingress
name: matrix-routing
@ -2638,7 +3059,7 @@ http_endpoints:
namespace: comms
service: matrix-guest-register
port: 8080
workloads: *id003
workloads: *id004
via:
kind: Ingress
name: matrix-routing
@ -2810,6 +3231,19 @@ http_endpoints:
kind: Ingress
name: planka
source: planka
- host: triage.hermes.bstein.dev
path: /
backend:
namespace: hermes
service: oauth2-proxy-hermes-triage
port: http
workloads:
- kind: Deployment
name: oauth2-proxy-hermes-triage
via:
kind: Ingress
name: hermes-sites
source: hermes
- host: vault.bstein.dev
path: /
backend:
@ -2842,7 +3276,7 @@ http_endpoints:
namespace: veles
service: veles-backend
port: 80
workloads: &id004
workloads: &id005
- kind: Deployment
name: veles-backend
via:
@ -2855,7 +3289,7 @@ http_endpoints:
namespace: veles
service: veles-backend
port: 80
workloads: *id004
workloads: *id005
via:
kind: Ingress
name: veles
@ -2866,7 +3300,7 @@ http_endpoints:
namespace: veles
service: veles-backend
port: 80
workloads: *id004
workloads: *id005
via:
kind: Ingress
name: veles
@ -2910,6 +3344,7 @@ helmrelease_host_hints:
- mail.bstein.dev
monitoring:monitoring/alertmanager:
- alerts.bstein.dev
- bstein.dev
monitoring:monitoring/grafana:
- atlas.bstein.dev
- bstein.dev

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,9 +1,9 @@
flowchart LR
host_agent_bstein_dev["agent.bstein.dev"]
svc_hermes_hermes["hermes/hermes (Service)"]
host_agent_bstein_dev --> svc_hermes_hermes
wl_hermes_hermes["hermes/hermes (Deployment)"]
svc_hermes_hermes --> wl_hermes_hermes
host_agent_hermes_bstein_dev["agent.hermes.bstein.dev"]
svc_hermes_oauth2_proxy_hermes_agent["hermes/oauth2-proxy-hermes-agent (Service)"]
host_agent_hermes_bstein_dev --> svc_hermes_oauth2_proxy_hermes_agent
wl_hermes_oauth2_proxy_hermes_agent["hermes/oauth2-proxy-hermes-agent (Deployment)"]
svc_hermes_oauth2_proxy_hermes_agent --> wl_hermes_oauth2_proxy_hermes_agent
host_auth_bstein_dev["auth.bstein.dev"]
svc_sso_oauth2_proxy["sso/oauth2-proxy (Service)"]
host_auth_bstein_dev --> svc_sso_oauth2_proxy
@ -37,11 +37,25 @@ flowchart LR
host_call_live_bstein_dev --> svc_comms_element_call
wl_comms_element_call["comms/element-call (Deployment)"]
svc_comms_element_call --> wl_comms_element_call
host_cassandra_bstein_dev["cassandra.bstein.dev"]
svc_cassandra_cassandra_frontend["cassandra/cassandra-frontend (Service)"]
host_cassandra_bstein_dev --> svc_cassandra_cassandra_frontend
wl_cassandra_cassandra_frontend["cassandra/cassandra-frontend (Deployment)"]
svc_cassandra_cassandra_frontend --> wl_cassandra_cassandra_frontend
svc_cassandra_cassandra_backend["cassandra/cassandra-backend (Service)"]
host_cassandra_bstein_dev --> svc_cassandra_cassandra_backend
wl_cassandra_cassandra_backend["cassandra/cassandra-backend (Deployment)"]
svc_cassandra_cassandra_backend --> wl_cassandra_cassandra_backend
host_chat_ai_bstein_dev["chat.ai.bstein.dev"]
svc_bstein_dev_home_chat_ai_gateway["bstein-dev-home/chat-ai-gateway (Service)"]
host_chat_ai_bstein_dev --> svc_bstein_dev_home_chat_ai_gateway
wl_bstein_dev_home_chat_ai_gateway["bstein-dev-home/chat-ai-gateway (Deployment)"]
svc_bstein_dev_home_chat_ai_gateway --> wl_bstein_dev_home_chat_ai_gateway
host_chat_hermes_bstein_dev["chat.hermes.bstein.dev"]
svc_hermes_oauth2_proxy_hermes_chat["hermes/oauth2-proxy-hermes-chat (Service)"]
host_chat_hermes_bstein_dev --> svc_hermes_oauth2_proxy_hermes_chat
wl_hermes_oauth2_proxy_hermes_chat["hermes/oauth2-proxy-hermes-chat (Deployment)"]
svc_hermes_oauth2_proxy_hermes_chat --> wl_hermes_oauth2_proxy_hermes_chat
host_ci_bstein_dev["ci.bstein.dev"]
svc_jenkins_jenkins["jenkins/jenkins (Service)"]
host_ci_bstein_dev --> svc_jenkins_jenkins
@ -156,6 +170,11 @@ flowchart LR
host_tasks_bstein_dev --> svc_planka_planka
wl_planka_planka["planka/planka (Deployment)"]
svc_planka_planka --> wl_planka_planka
host_triage_hermes_bstein_dev["triage.hermes.bstein.dev"]
svc_hermes_oauth2_proxy_hermes_triage["hermes/oauth2-proxy-hermes-triage (Service)"]
host_triage_hermes_bstein_dev --> svc_hermes_oauth2_proxy_hermes_triage
wl_hermes_oauth2_proxy_hermes_triage["hermes/oauth2-proxy-hermes-triage (Deployment)"]
svc_hermes_oauth2_proxy_hermes_triage --> wl_hermes_oauth2_proxy_hermes_triage
host_vault_bstein_dev["vault.bstein.dev"]
svc_vaultwarden_vaultwarden_service["vaultwarden/vaultwarden-service (Service)"]
host_vault_bstein_dev --> svc_vaultwarden_vaultwarden_service
@ -184,6 +203,12 @@ flowchart LR
svc_bstein_dev_home_chat_ai_gateway
wl_bstein_dev_home_chat_ai_gateway
end
subgraph cassandra[cassandra]
svc_cassandra_cassandra_frontend
wl_cassandra_cassandra_frontend
svc_cassandra_cassandra_backend
wl_cassandra_cassandra_backend
end
subgraph comms[comms]
svc_comms_matrix_wellknown
wl_comms_matrix_wellknown
@ -222,8 +247,12 @@ flowchart LR
wl_health_wger
end
subgraph hermes[hermes]
svc_hermes_hermes
wl_hermes_hermes
svc_hermes_oauth2_proxy_hermes_agent
wl_hermes_oauth2_proxy_hermes_agent
svc_hermes_oauth2_proxy_hermes_chat
wl_hermes_oauth2_proxy_hermes_chat
svc_hermes_oauth2_proxy_hermes_triage
wl_hermes_oauth2_proxy_hermes_triage
end
subgraph jellyfin[jellyfin]
svc_jellyfin_pegasus

View File

@ -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);
both TTL-clean after 1 h.
- Hermes side: the run appears in the dashboard at
`https://agent.bstein.dev` (session/run history).
`https://agent.hermes.bstein.dev` (session/run history).
- Escalation path: alert `HermesTriageHumanRequired` in vmalert
(`vmalert-atlas-availability` deployment, 1 m interval, `for: 2m`)
fires to Alertmanager for any `human_required` incident. Gauges are

View File

@ -347,7 +347,7 @@ flowchart TB
gitops_domain["cd.bstein.dev"]:::domain
gitea_domain["scm.bstein.dev"]:::domain
harbor_domain["registry.bstein.dev"]:::domain
hermes_domain["agent.bstein.dev"]:::domain
hermes_domain["agent.hermes.bstein.dev"]:::domain
jenkins_domain["ci.bstein.dev"]:::domain
end

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

0
scripts/ops/hermes_demo_lib.sh Normal file → Executable file
View File

View File

@ -39,7 +39,7 @@ NS_ARIADNE = "maintenance"
JENKINS = os.environ.get("JENKINS_URL", "https://ci.bstein.dev")
GITEA = os.environ.get("GITEA_URL", "https://scm.bstein.dev")
GRAFANA = os.environ.get("GRAFANA_URL", "https://metrics.bstein.dev")
HERMES_UI = os.environ.get("HERMES_URL", "https://agent.bstein.dev")
HERMES_UI = os.environ.get("HERMES_URL", "https://agent.hermes.bstein.dev")
POLL_SECONDS = 6
# `--filter <text>` restricts the monitor to incidents whose id contains that

View File

@ -1,52 +0,0 @@
# services/cassandra/hermes-agent-rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: hermes-agent-readonly
namespace: cassandra
rules:
- apiGroups: [""]
resources:
- configmaps
- endpoints
- events
- persistentvolumeclaims
- pods
- pods/log
- replicationcontrollers
- services
verbs: ["get", "list", "watch"]
- apiGroups: ["events.k8s.io"]
resources: ["events"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources:
- daemonsets
- deployments
- replicasets
- statefulsets
verbs: ["get", "list", "watch"]
- apiGroups: ["batch"]
resources:
- cronjobs
- jobs
verbs: ["get", "list", "watch"]
- apiGroups: ["networking.k8s.io"]
resources:
- ingresses
- networkpolicies
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: hermes-agent-readonly
namespace: cassandra
subjects:
- kind: ServiceAccount
name: hermes-agent
namespace: hermes
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: hermes-agent-readonly

View File

@ -11,7 +11,6 @@ resources:
- limitrange.yaml
- configmap.yaml
- rbac.yaml
- hermes-agent-rbac.yaml
- artifacts-pvc.yaml
- postgres-service.yaml
- postgres-statefulset.yaml

View File

@ -1,8 +1,8 @@
{
"counts": {
"helmrelease_host_hints": 22,
"http_endpoints": 54,
"services": 70,
"workloads": 100
"helmrelease_host_hints": 23,
"http_endpoints": 60,
"services": 85,
"workloads": 119
}
}

View File

@ -16,6 +16,16 @@
"path": "services/bstein-dev-home/migration-jobs",
"targetNamespace": "bstein-dev-home"
},
{
"name": "cassandra",
"path": "services/cassandra",
"targetNamespace": "cassandra"
},
{
"name": "cassandra-auth",
"path": "services/cassandra-auth",
"targetNamespace": "sso"
},
{
"name": "cert-manager",
"path": "infrastructure/cert-manager",
@ -91,6 +101,16 @@
"path": "services/hermes",
"targetNamespace": "hermes"
},
{
"name": "hermes-chat",
"path": "services/hermes-chat",
"targetNamespace": "hermes-chat"
},
{
"name": "hermes-triage-demo",
"path": "services/hermes-triage-demo",
"targetNamespace": null
},
{
"name": "jellyfin",
"path": "services/jellyfin",
@ -284,7 +304,7 @@
"node-role.kubernetes.io/worker": "true"
},
"images": [
"registry.bstein.dev/bstein/bstein-dev-home-backend:0.1.1-414"
"registry.bstein.dev/bstein/bstein-dev-home-backend:0.1.1-454"
]
},
{
@ -300,7 +320,7 @@
"node-role.kubernetes.io/worker": "true"
},
"images": [
"registry.bstein.dev/bstein/bstein-dev-home-frontend:0.1.1-414"
"registry.bstein.dev/bstein/bstein-dev-home-frontend:0.1.1-454"
]
},
{
@ -332,6 +352,63 @@
"python:3.11-slim"
]
},
{
"kind": "Deployment",
"namespace": "cassandra",
"name": "cassandra-backend",
"labels": {
"app": "cassandra-backend"
},
"serviceAccountName": "cassandra-backend",
"nodeSelector": {
"cassandra.bstein.dev/node-pool": "oceanus",
"kubernetes.io/arch": "amd64"
},
"images": [
"registry.bstein.dev/cassandra/cassandra-backend:0.9.63"
]
},
{
"kind": "Deployment",
"namespace": "cassandra",
"name": "cassandra-frontend",
"labels": {
"app": "cassandra-frontend"
},
"serviceAccountName": "cassandra-frontend",
"nodeSelector": {},
"images": [
"registry.bstein.dev/cassandra/cassandra-frontend:0.9.63"
]
},
{
"kind": "Deployment",
"namespace": "cassandra",
"name": "cassandra-vault-sync",
"labels": {
"app": "cassandra-vault-sync"
},
"serviceAccountName": "cassandra-vault-sync",
"nodeSelector": {},
"images": [
"alpine:3.20"
]
},
{
"kind": "StatefulSet",
"namespace": "cassandra",
"name": "cassandra-postgres",
"labels": {
"app": "cassandra-postgres"
},
"serviceAccountName": "cassandra-postgres",
"nodeSelector": {
"cassandra.bstein.dev/node-pool": "oceanus"
},
"images": [
"postgres:15"
]
},
{
"kind": "Deployment",
"namespace": "climate",
@ -825,7 +902,104 @@
"serviceAccountName": "hermes-triage",
"nodeSelector": {},
"images": [
"nousresearch/hermes-agent@sha256:9c841866021c54c4596849f6135717e8a4d52ba510b7f52c50aef1de1a283973"
"registry.bstein.dev/bstein/hermes-agent@sha256:15c5c538c0b58686af2e54e10bc870b23284789d485a609349df24ed3053622f",
"registry.bstein.dev/bstein/hermes-webui@sha256:c109e6faec1d6b86859a182bc845a2e35d64260459dda3892c0510db1dc7272d"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "hermes-agent",
"labels": {
"app": "hermes-agent"
},
"serviceAccountName": "hermes-agent",
"nodeSelector": {},
"images": [
"registry.bstein.dev/bstein/hermes-agent@sha256:46de11122226a68adad6d902e81ef9b4b53deb0433dcc61f57fa43fae2e3a3a5",
"registry.bstein.dev/bstein/hermes-webui@sha256:c109e6faec1d6b86859a182bc845a2e35d64260459dda3892c0510db1dc7272d"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "hermes-chat-router",
"labels": {
"app": "hermes-chat-router"
},
"serviceAccountName": "hermes-chat",
"nodeSelector": {},
"images": [
"registry.bstein.dev/bstein/hermes-chat-router@sha256:882f9c3af7268618a19424765536f4cf6c37e8d380e515780baf4104e5b2931e"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "hermes-chat-sandbox-0",
"labels": {
"ai.bstein.dev/tenant-ordinal": "0",
"app": "hermes-chat-sandbox"
},
"serviceAccountName": null,
"nodeSelector": {},
"images": [
"registry.bstein.dev/bstein/hermes-chat-sandbox@sha256:17ee62b8e61c08573a3a8cca903b38ec43800cb44ec29340e1bc095176544bca"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "hermes-chat-sandbox-1",
"labels": {
"ai.bstein.dev/tenant-ordinal": "1",
"app": "hermes-chat-sandbox"
},
"serviceAccountName": null,
"nodeSelector": {},
"images": [
"registry.bstein.dev/bstein/hermes-chat-sandbox@sha256:17ee62b8e61c08573a3a8cca903b38ec43800cb44ec29340e1bc095176544bca"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "hermes-chat-sandbox-2",
"labels": {
"ai.bstein.dev/tenant-ordinal": "2",
"app": "hermes-chat-sandbox"
},
"serviceAccountName": null,
"nodeSelector": {},
"images": [
"registry.bstein.dev/bstein/hermes-chat-sandbox@sha256:17ee62b8e61c08573a3a8cca903b38ec43800cb44ec29340e1bc095176544bca"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "hermes-chat-sandbox-3",
"labels": {
"ai.bstein.dev/tenant-ordinal": "3",
"app": "hermes-chat-sandbox"
},
"serviceAccountName": null,
"nodeSelector": {},
"images": [
"registry.bstein.dev/bstein/hermes-chat-sandbox@sha256:17ee62b8e61c08573a3a8cca903b38ec43800cb44ec29340e1bc095176544bca"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "hermes-model-gate",
"labels": {
"app": "hermes-model-gate"
},
"serviceAccountName": "hermes-model-gate",
"nodeSelector": {},
"images": [
"python@sha256:6d43704baacd1bfbe7c295d7f13079d5d8104ed33568873133f8fc69980419df"
]
},
{
@ -841,6 +1015,89 @@
"ollama/ollama@sha256:2c9595c555fd70a28363489ac03bd5bf9e7c5bdf2890373c3a830ffd7252ce6d"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "hermes-stt",
"labels": {
"app": "hermes-stt"
},
"serviceAccountName": null,
"nodeSelector": {
"kubernetes.io/hostname": "titan-21"
},
"images": [
"registry.bstein.dev/bstein/hermes-jetson-stt@sha256:dfb0b0788dcf63747d8c761c6d3ea6a7459bcd8d0a5c92bcd4f6f434a760249f"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "hermes-tts",
"labels": {
"app": "hermes-tts"
},
"serviceAccountName": null,
"nodeSelector": {
"kubernetes.io/hostname": "titan-20"
},
"images": [
"registry.bstein.dev/bstein/hermes-jetson-tts@sha256:5cb9e57faab46365bff606c559af57b9505892be2909aea1ac523568d478b2cc"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "oauth2-proxy-hermes-agent",
"labels": {
"app": "oauth2-proxy-hermes-agent"
},
"serviceAccountName": "hermes-agent",
"nodeSelector": {},
"images": [
"quay.io/oauth2-proxy/oauth2-proxy:v7.15.3@sha256:10a1165743a192e1940b4708fb9647027185ce11a681a1c5519b442ff7f1f561"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "oauth2-proxy-hermes-chat",
"labels": {
"app": "oauth2-proxy-hermes-chat"
},
"serviceAccountName": "hermes-chat",
"nodeSelector": {},
"images": [
"quay.io/oauth2-proxy/oauth2-proxy:v7.15.3@sha256:10a1165743a192e1940b4708fb9647027185ce11a681a1c5519b442ff7f1f561"
]
},
{
"kind": "Deployment",
"namespace": "hermes",
"name": "oauth2-proxy-hermes-triage",
"labels": {
"app": "oauth2-proxy-hermes-triage"
},
"serviceAccountName": "hermes-vault",
"nodeSelector": {},
"images": [
"quay.io/oauth2-proxy/oauth2-proxy:v7.15.3@sha256:10a1165743a192e1940b4708fb9647027185ce11a681a1c5519b442ff7f1f561"
]
},
{
"kind": "StatefulSet",
"namespace": "hermes",
"name": "hermes-chat-tenant",
"labels": {
"app": "hermes-chat-tenant"
},
"serviceAccountName": "hermes-chat",
"nodeSelector": {},
"images": [
"registry.bstein.dev/bstein/hermes-agent@sha256:10522c69676e250b5d0014d811ed9eb706d0b7c16f35cf66650f480e63ab3ab5",
"registry.bstein.dev/bstein/hermes-webui@sha256:c109e6faec1d6b86859a182bc845a2e35d64260459dda3892c0510db1dc7272d"
]
},
{
"kind": "Deployment",
"namespace": "jellyfin",
@ -1188,7 +1445,7 @@
"kubernetes.io/os": "linux"
},
"images": [
"registry.bstein.dev/bstein/metis-sentinel:0.1.0-243-amd64"
"registry.bstein.dev/bstein/metis-sentinel:0.1.0-283-amd64"
]
},
{
@ -1204,7 +1461,7 @@
"kubernetes.io/os": "linux"
},
"images": [
"registry.bstein.dev/bstein/metis-sentinel:0.1.0-243-arm64"
"registry.bstein.dev/bstein/metis-sentinel:0.1.0-283-arm64"
]
},
{
@ -1293,7 +1550,7 @@
"node-role.kubernetes.io/worker": "true"
},
"images": [
"registry.bstein.dev/bstein/ariadne:0.1.0-356"
"registry.bstein.dev/bstein/ariadne:0.1.0-448"
]
},
{
@ -1321,7 +1578,7 @@
"serviceAccountName": "metis",
"nodeSelector": {},
"images": [
"registry.bstein.dev/bstein/metis:0.1.0-243-arm64"
"registry.bstein.dev/bstein/metis:0.1.0-283-arm64"
]
},
{
@ -1724,7 +1981,22 @@
"node-role.kubernetes.io/worker": "true"
},
"images": [
"vaultwarden/server:1.35.2"
"vaultwarden/server:1.37.0@sha256:e6443e3d5ed8fcee2204b89ec778d7f24d0173bcc42d1ea34f990304f5f63f51"
]
},
{
"kind": "Deployment",
"namespace": "veles",
"name": "veles-artifact-export-for-cassandra",
"labels": {
"app": "veles-artifact-export-for-cassandra"
},
"serviceAccountName": null,
"nodeSelector": {
"cassandra.bstein.dev/node-pool": "oceanus"
},
"images": [
"python:3.12-alpine"
]
},
{
@ -1740,7 +2012,7 @@
"veles.bstein.dev/node-pool": "oceanus"
},
"images": [
"registry.bstein.dev/veles/veles-backend:0.7.56"
"registry.bstein.dev/veles/veles-backend:0.7.65"
]
},
{
@ -1753,7 +2025,23 @@
"serviceAccountName": "veles-frontend",
"nodeSelector": {},
"images": [
"registry.bstein.dev/veles/veles-frontend:0.7.56"
"registry.bstein.dev/veles/veles-frontend:0.7.65"
]
},
{
"kind": "Deployment",
"namespace": "veles",
"name": "veles-generator-worker",
"labels": {
"app": "veles-generator-worker"
},
"serviceAccountName": "veles-generator",
"nodeSelector": {
"kubernetes.io/arch": "amd64",
"veles.bstein.dev/node-pool": "oceanus"
},
"images": [
"registry.bstein.dev/veles/veles-generator-worker:0.7.65"
]
},
{
@ -1850,6 +2138,54 @@
}
]
},
{
"namespace": "cassandra",
"name": "cassandra-backend",
"type": "ClusterIP",
"selector": {
"app": "cassandra-backend"
},
"ports": [
{
"name": "http",
"port": 80,
"targetPort": 8796,
"protocol": "TCP"
}
]
},
{
"namespace": "cassandra",
"name": "cassandra-frontend",
"type": "ClusterIP",
"selector": {
"app": "cassandra-frontend"
},
"ports": [
{
"name": "http",
"port": 80,
"targetPort": 8080,
"protocol": "TCP"
}
]
},
{
"namespace": "cassandra",
"name": "cassandra-postgres",
"type": "ClusterIP",
"selector": {
"app": "cassandra-postgres"
},
"ports": [
{
"name": "postgres",
"port": 5432,
"targetPort": 5432,
"protocol": "TCP"
}
]
},
{
"namespace": "climate",
"name": "typhon",
@ -2684,6 +3020,98 @@
}
]
},
{
"namespace": "hermes",
"name": "hermes-agent",
"type": "ClusterIP",
"selector": {
"app": "hermes-agent"
},
"ports": [
{
"name": "dashboard",
"port": 8787,
"targetPort": "dashboard",
"protocol": "TCP"
},
{
"name": "terminal",
"port": 7681,
"targetPort": "terminal",
"protocol": "TCP"
}
]
},
{
"namespace": "hermes",
"name": "hermes-chat-router",
"type": "ClusterIP",
"selector": {
"app": "hermes-chat-router"
},
"ports": [
{
"name": "http",
"port": 8080,
"targetPort": "http",
"protocol": "TCP"
}
]
},
{
"namespace": "hermes",
"name": "hermes-chat-sandbox",
"type": "ClusterIP",
"selector": {
"app": "hermes-chat-sandbox"
},
"ports": [
{
"name": "http",
"port": 9080,
"targetPort": "http",
"protocol": "TCP"
}
]
},
{
"namespace": "hermes",
"name": "hermes-chat-tenant",
"type": "ClusterIP",
"selector": {
"app": "hermes-chat-tenant"
},
"ports": [
{
"name": "webui",
"port": 8787,
"targetPort": "webui",
"protocol": "TCP"
},
{
"name": "api",
"port": 8642,
"targetPort": "api",
"protocol": "TCP"
}
]
},
{
"namespace": "hermes",
"name": "hermes-model-gate",
"type": "ClusterIP",
"selector": {
"app": "hermes-model-gate"
},
"ports": [
{
"name": "http",
"port": 11434,
"targetPort": "http",
"protocol": "TCP"
}
]
},
{
"namespace": "hermes",
"name": "hermes-ollama",
@ -2700,6 +3128,108 @@
}
]
},
{
"namespace": "hermes",
"name": "hermes-stt",
"type": "ClusterIP",
"selector": {
"app": "hermes-stt"
},
"ports": [
{
"name": "http",
"port": 9000,
"targetPort": "http",
"protocol": "TCP"
}
]
},
{
"namespace": "hermes",
"name": "hermes-triage",
"type": "ClusterIP",
"selector": {
"app": "hermes"
},
"ports": [
{
"name": "api",
"port": 8642,
"targetPort": "api",
"protocol": "TCP"
},
{
"name": "dashboard",
"port": 8787,
"targetPort": "dashboard",
"protocol": "TCP"
}
]
},
{
"namespace": "hermes",
"name": "hermes-tts",
"type": "ClusterIP",
"selector": {
"app": "hermes-tts"
},
"ports": [
{
"name": "http",
"port": 9001,
"targetPort": "http",
"protocol": "TCP"
}
]
},
{
"namespace": "hermes",
"name": "oauth2-proxy-hermes-agent",
"type": "ClusterIP",
"selector": {
"app": "oauth2-proxy-hermes-agent"
},
"ports": [
{
"name": "http",
"port": 80,
"targetPort": "http",
"protocol": "TCP"
}
]
},
{
"namespace": "hermes",
"name": "oauth2-proxy-hermes-chat",
"type": "ClusterIP",
"selector": {
"app": "oauth2-proxy-hermes-chat"
},
"ports": [
{
"name": "http",
"port": 80,
"targetPort": "http",
"protocol": "TCP"
}
]
},
{
"namespace": "hermes",
"name": "oauth2-proxy-hermes-triage",
"type": "ClusterIP",
"selector": {
"app": "oauth2-proxy-hermes-triage"
},
"ports": [
{
"name": "http",
"port": 80,
"targetPort": "http",
"protocol": "TCP"
}
]
},
{
"namespace": "jellyfin",
"name": "jellyfin",
@ -3328,6 +3858,22 @@
}
]
},
{
"namespace": "veles",
"name": "veles-artifact-export-for-cassandra",
"type": "ClusterIP",
"selector": {
"app": "veles-artifact-export-for-cassandra"
},
"ports": [
{
"name": "http",
"port": 8765,
"targetPort": "http",
"protocol": "TCP"
}
]
},
{
"namespace": "veles",
"name": "veles-backend",
@ -3379,22 +3925,22 @@
],
"http_endpoints": [
{
"host": "agent.bstein.dev",
"host": "agent.hermes.bstein.dev",
"path": "/",
"backend": {
"namespace": "hermes",
"service": "hermes",
"port": "dashboard",
"service": "oauth2-proxy-hermes-agent",
"port": "http",
"workloads": [
{
"kind": "Deployment",
"name": "hermes"
"name": "oauth2-proxy-hermes-agent"
}
]
},
"via": {
"kind": "Ingress",
"name": "agent",
"name": "hermes-sites",
"source": "hermes"
}
},
@ -3558,6 +4104,86 @@
"source": "comms"
}
},
{
"host": "cassandra.bstein.dev",
"path": "/",
"backend": {
"namespace": "cassandra",
"service": "cassandra-frontend",
"port": 80,
"workloads": [
{
"kind": "Deployment",
"name": "cassandra-frontend"
}
]
},
"via": {
"kind": "Ingress",
"name": "cassandra",
"source": "cassandra"
}
},
{
"host": "cassandra.bstein.dev",
"path": "/api",
"backend": {
"namespace": "cassandra",
"service": "cassandra-backend",
"port": 80,
"workloads": [
{
"kind": "Deployment",
"name": "cassandra-backend"
}
]
},
"via": {
"kind": "Ingress",
"name": "cassandra",
"source": "cassandra"
}
},
{
"host": "cassandra.bstein.dev",
"path": "/events",
"backend": {
"namespace": "cassandra",
"service": "cassandra-backend",
"port": 80,
"workloads": [
{
"kind": "Deployment",
"name": "cassandra-backend"
}
]
},
"via": {
"kind": "Ingress",
"name": "cassandra",
"source": "cassandra"
}
},
{
"host": "cassandra.bstein.dev",
"path": "/ws",
"backend": {
"namespace": "cassandra",
"service": "cassandra-backend",
"port": 80,
"workloads": [
{
"kind": "Deployment",
"name": "cassandra-backend"
}
]
},
"via": {
"kind": "Ingress",
"name": "cassandra",
"source": "cassandra"
}
},
{
"host": "chat.ai.bstein.dev",
"path": "/",
@ -3578,6 +4204,26 @@
"source": "bstein-dev-home"
}
},
{
"host": "chat.hermes.bstein.dev",
"path": "/",
"backend": {
"namespace": "hermes",
"service": "oauth2-proxy-hermes-chat",
"port": "http",
"workloads": [
{
"kind": "Deployment",
"name": "oauth2-proxy-hermes-chat"
}
]
},
"via": {
"kind": "Ingress",
"name": "hermes-sites",
"source": "hermes"
}
},
{
"host": "ci.bstein.dev",
"path": "/",
@ -4318,6 +4964,26 @@
"source": "planka"
}
},
{
"host": "triage.hermes.bstein.dev",
"path": "/",
"backend": {
"namespace": "hermes",
"service": "oauth2-proxy-hermes-triage",
"port": "http",
"workloads": [
{
"kind": "Deployment",
"name": "oauth2-proxy-hermes-triage"
}
]
},
"via": {
"kind": "Ingress",
"name": "hermes-sites",
"source": "hermes"
}
},
{
"host": "vault.bstein.dev",
"path": "/",
@ -4472,7 +5138,8 @@
"mail.bstein.dev"
],
"monitoring:monitoring/alertmanager": [
"alerts.bstein.dev"
"alerts.bstein.dev",
"bstein.dev"
],
"monitoring:monitoring/grafana": [
"atlas.bstein.dev",

View File

@ -11,6 +11,12 @@ sources:
- name: bstein-dev-home-migrations
path: services/bstein-dev-home/migration-jobs
targetNamespace: bstein-dev-home
- name: cassandra
path: services/cassandra
targetNamespace: cassandra
- name: cassandra-auth
path: services/cassandra-auth
targetNamespace: sso
- name: cert-manager
path: infrastructure/cert-manager
targetNamespace: cert-manager
@ -56,6 +62,12 @@ sources:
- name: hermes
path: services/hermes
targetNamespace: hermes
- name: hermes-chat
path: services/hermes-chat
targetNamespace: hermes-chat
- name: hermes-triage-demo
path: services/hermes-triage-demo
targetNamespace: null
- name: jellyfin
path: services/jellyfin
targetNamespace: jellyfin
@ -175,7 +187,7 @@ workloads:
kubernetes.io/arch: arm64
node-role.kubernetes.io/worker: 'true'
images:
- registry.bstein.dev/bstein/bstein-dev-home-backend:0.1.1-414
- registry.bstein.dev/bstein/bstein-dev-home-backend:0.1.1-454
- kind: Deployment
namespace: bstein-dev-home
name: bstein-dev-home-frontend
@ -186,7 +198,7 @@ workloads:
kubernetes.io/arch: arm64
node-role.kubernetes.io/worker: 'true'
images:
- registry.bstein.dev/bstein/bstein-dev-home-frontend:0.1.1-414
- registry.bstein.dev/bstein/bstein-dev-home-frontend:0.1.1-454
- kind: Deployment
namespace: bstein-dev-home
name: bstein-dev-home-vault-sync
@ -207,6 +219,45 @@ workloads:
node-role.kubernetes.io/worker: 'true'
images:
- python:3.11-slim
- kind: Deployment
namespace: cassandra
name: cassandra-backend
labels:
app: cassandra-backend
serviceAccountName: cassandra-backend
nodeSelector:
cassandra.bstein.dev/node-pool: oceanus
kubernetes.io/arch: amd64
images:
- registry.bstein.dev/cassandra/cassandra-backend:0.9.63
- kind: Deployment
namespace: cassandra
name: cassandra-frontend
labels:
app: cassandra-frontend
serviceAccountName: cassandra-frontend
nodeSelector: {}
images:
- registry.bstein.dev/cassandra/cassandra-frontend:0.9.63
- kind: Deployment
namespace: cassandra
name: cassandra-vault-sync
labels:
app: cassandra-vault-sync
serviceAccountName: cassandra-vault-sync
nodeSelector: {}
images:
- alpine:3.20
- kind: StatefulSet
namespace: cassandra
name: cassandra-postgres
labels:
app: cassandra-postgres
serviceAccountName: cassandra-postgres
nodeSelector:
cassandra.bstein.dev/node-pool: oceanus
images:
- postgres:15
- kind: Deployment
namespace: climate
name: typhon
@ -553,7 +604,76 @@ workloads:
serviceAccountName: hermes-triage
nodeSelector: {}
images:
- nousresearch/hermes-agent@sha256:9c841866021c54c4596849f6135717e8a4d52ba510b7f52c50aef1de1a283973
- registry.bstein.dev/bstein/hermes-agent@sha256:15c5c538c0b58686af2e54e10bc870b23284789d485a609349df24ed3053622f
- registry.bstein.dev/bstein/hermes-webui@sha256:c109e6faec1d6b86859a182bc845a2e35d64260459dda3892c0510db1dc7272d
- kind: Deployment
namespace: hermes
name: hermes-agent
labels:
app: hermes-agent
serviceAccountName: hermes-agent
nodeSelector: {}
images:
- registry.bstein.dev/bstein/hermes-agent@sha256:46de11122226a68adad6d902e81ef9b4b53deb0433dcc61f57fa43fae2e3a3a5
- registry.bstein.dev/bstein/hermes-webui@sha256:c109e6faec1d6b86859a182bc845a2e35d64260459dda3892c0510db1dc7272d
- kind: Deployment
namespace: hermes
name: hermes-chat-router
labels:
app: hermes-chat-router
serviceAccountName: hermes-chat
nodeSelector: {}
images:
- registry.bstein.dev/bstein/hermes-chat-router@sha256:882f9c3af7268618a19424765536f4cf6c37e8d380e515780baf4104e5b2931e
- kind: Deployment
namespace: hermes
name: hermes-chat-sandbox-0
labels:
ai.bstein.dev/tenant-ordinal: '0'
app: hermes-chat-sandbox
serviceAccountName: null
nodeSelector: {}
images:
- registry.bstein.dev/bstein/hermes-chat-sandbox@sha256:17ee62b8e61c08573a3a8cca903b38ec43800cb44ec29340e1bc095176544bca
- kind: Deployment
namespace: hermes
name: hermes-chat-sandbox-1
labels:
ai.bstein.dev/tenant-ordinal: '1'
app: hermes-chat-sandbox
serviceAccountName: null
nodeSelector: {}
images:
- registry.bstein.dev/bstein/hermes-chat-sandbox@sha256:17ee62b8e61c08573a3a8cca903b38ec43800cb44ec29340e1bc095176544bca
- kind: Deployment
namespace: hermes
name: hermes-chat-sandbox-2
labels:
ai.bstein.dev/tenant-ordinal: '2'
app: hermes-chat-sandbox
serviceAccountName: null
nodeSelector: {}
images:
- registry.bstein.dev/bstein/hermes-chat-sandbox@sha256:17ee62b8e61c08573a3a8cca903b38ec43800cb44ec29340e1bc095176544bca
- kind: Deployment
namespace: hermes
name: hermes-chat-sandbox-3
labels:
ai.bstein.dev/tenant-ordinal: '3'
app: hermes-chat-sandbox
serviceAccountName: null
nodeSelector: {}
images:
- registry.bstein.dev/bstein/hermes-chat-sandbox@sha256:17ee62b8e61c08573a3a8cca903b38ec43800cb44ec29340e1bc095176544bca
- kind: Deployment
namespace: hermes
name: hermes-model-gate
labels:
app: hermes-model-gate
serviceAccountName: hermes-model-gate
nodeSelector: {}
images:
- python@sha256:6d43704baacd1bfbe7c295d7f13079d5d8104ed33568873133f8fc69980419df
- kind: Deployment
namespace: hermes
name: hermes-ollama
@ -563,6 +683,63 @@ workloads:
nodeSelector: {}
images:
- ollama/ollama@sha256:2c9595c555fd70a28363489ac03bd5bf9e7c5bdf2890373c3a830ffd7252ce6d
- kind: Deployment
namespace: hermes
name: hermes-stt
labels:
app: hermes-stt
serviceAccountName: null
nodeSelector:
kubernetes.io/hostname: titan-21
images:
- registry.bstein.dev/bstein/hermes-jetson-stt@sha256:dfb0b0788dcf63747d8c761c6d3ea6a7459bcd8d0a5c92bcd4f6f434a760249f
- kind: Deployment
namespace: hermes
name: hermes-tts
labels:
app: hermes-tts
serviceAccountName: null
nodeSelector:
kubernetes.io/hostname: titan-20
images:
- registry.bstein.dev/bstein/hermes-jetson-tts@sha256:5cb9e57faab46365bff606c559af57b9505892be2909aea1ac523568d478b2cc
- kind: Deployment
namespace: hermes
name: oauth2-proxy-hermes-agent
labels:
app: oauth2-proxy-hermes-agent
serviceAccountName: hermes-agent
nodeSelector: {}
images:
- quay.io/oauth2-proxy/oauth2-proxy:v7.15.3@sha256:10a1165743a192e1940b4708fb9647027185ce11a681a1c5519b442ff7f1f561
- kind: Deployment
namespace: hermes
name: oauth2-proxy-hermes-chat
labels:
app: oauth2-proxy-hermes-chat
serviceAccountName: hermes-chat
nodeSelector: {}
images:
- quay.io/oauth2-proxy/oauth2-proxy:v7.15.3@sha256:10a1165743a192e1940b4708fb9647027185ce11a681a1c5519b442ff7f1f561
- kind: Deployment
namespace: hermes
name: oauth2-proxy-hermes-triage
labels:
app: oauth2-proxy-hermes-triage
serviceAccountName: hermes-vault
nodeSelector: {}
images:
- quay.io/oauth2-proxy/oauth2-proxy:v7.15.3@sha256:10a1165743a192e1940b4708fb9647027185ce11a681a1c5519b442ff7f1f561
- kind: StatefulSet
namespace: hermes
name: hermes-chat-tenant
labels:
app: hermes-chat-tenant
serviceAccountName: hermes-chat
nodeSelector: {}
images:
- registry.bstein.dev/bstein/hermes-agent@sha256:10522c69676e250b5d0014d811ed9eb706d0b7c16f35cf66650f480e63ab3ab5
- registry.bstein.dev/bstein/hermes-webui@sha256:c109e6faec1d6b86859a182bc845a2e35d64260459dda3892c0510db1dc7272d
- kind: Deployment
namespace: jellyfin
name: jellyfin
@ -801,7 +978,7 @@ workloads:
kubernetes.io/arch: amd64
kubernetes.io/os: linux
images:
- registry.bstein.dev/bstein/metis-sentinel:0.1.0-243-amd64
- registry.bstein.dev/bstein/metis-sentinel:0.1.0-283-amd64
- kind: DaemonSet
namespace: maintenance
name: metis-sentinel-arm64
@ -812,7 +989,7 @@ workloads:
kubernetes.io/arch: arm64
kubernetes.io/os: linux
images:
- registry.bstein.dev/bstein/metis-sentinel:0.1.0-243-arm64
- registry.bstein.dev/bstein/metis-sentinel:0.1.0-283-arm64
- kind: DaemonSet
namespace: maintenance
name: node-image-sweeper
@ -872,7 +1049,7 @@ workloads:
kubernetes.io/arch: arm64
node-role.kubernetes.io/worker: 'true'
images:
- registry.bstein.dev/bstein/ariadne:0.1.0-356
- registry.bstein.dev/bstein/ariadne:0.1.0-448
- kind: Deployment
namespace: maintenance
name: maintenance-vault-sync
@ -891,7 +1068,7 @@ workloads:
serviceAccountName: metis
nodeSelector: {}
images:
- registry.bstein.dev/bstein/metis:0.1.0-243-arm64
- registry.bstein.dev/bstein/metis:0.1.0-283-arm64
- kind: Deployment
namespace: maintenance
name: oauth2-proxy-metis
@ -1166,7 +1343,17 @@ workloads:
kubernetes.io/arch: arm64
node-role.kubernetes.io/worker: 'true'
images:
- vaultwarden/server:1.35.2
- vaultwarden/server:1.37.0@sha256:e6443e3d5ed8fcee2204b89ec778d7f24d0173bcc42d1ea34f990304f5f63f51
- kind: Deployment
namespace: veles
name: veles-artifact-export-for-cassandra
labels:
app: veles-artifact-export-for-cassandra
serviceAccountName: null
nodeSelector:
cassandra.bstein.dev/node-pool: oceanus
images:
- python:3.12-alpine
- kind: Deployment
namespace: veles
name: veles-backend
@ -1177,7 +1364,7 @@ workloads:
kubernetes.io/arch: amd64
veles.bstein.dev/node-pool: oceanus
images:
- registry.bstein.dev/veles/veles-backend:0.7.56
- registry.bstein.dev/veles/veles-backend:0.7.65
- kind: Deployment
namespace: veles
name: veles-frontend
@ -1186,7 +1373,18 @@ workloads:
serviceAccountName: veles-frontend
nodeSelector: {}
images:
- registry.bstein.dev/veles/veles-frontend:0.7.56
- registry.bstein.dev/veles/veles-frontend:0.7.65
- kind: Deployment
namespace: veles
name: veles-generator-worker
labels:
app: veles-generator-worker
serviceAccountName: veles-generator
nodeSelector:
kubernetes.io/arch: amd64
veles.bstein.dev/node-pool: oceanus
images:
- registry.bstein.dev/veles/veles-generator-worker:0.7.65
- kind: Deployment
namespace: veles
name: veles-vault-sync
@ -1247,6 +1445,36 @@ services:
port: 80
targetPort: 8080
protocol: TCP
- namespace: cassandra
name: cassandra-backend
type: ClusterIP
selector:
app: cassandra-backend
ports:
- name: http
port: 80
targetPort: 8796
protocol: TCP
- namespace: cassandra
name: cassandra-frontend
type: ClusterIP
selector:
app: cassandra-frontend
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
- namespace: cassandra
name: cassandra-postgres
type: ClusterIP
selector:
app: cassandra-postgres
ports:
- name: postgres
port: 5432
targetPort: 5432
protocol: TCP
- namespace: climate
name: typhon
type: ClusterIP
@ -1785,6 +2013,64 @@ services:
port: 9119
targetPort: dashboard
protocol: TCP
- namespace: hermes
name: hermes-agent
type: ClusterIP
selector:
app: hermes-agent
ports:
- name: dashboard
port: 8787
targetPort: dashboard
protocol: TCP
- name: terminal
port: 7681
targetPort: terminal
protocol: TCP
- namespace: hermes
name: hermes-chat-router
type: ClusterIP
selector:
app: hermes-chat-router
ports:
- name: http
port: 8080
targetPort: http
protocol: TCP
- namespace: hermes
name: hermes-chat-sandbox
type: ClusterIP
selector:
app: hermes-chat-sandbox
ports:
- name: http
port: 9080
targetPort: http
protocol: TCP
- namespace: hermes
name: hermes-chat-tenant
type: ClusterIP
selector:
app: hermes-chat-tenant
ports:
- name: webui
port: 8787
targetPort: webui
protocol: TCP
- name: api
port: 8642
targetPort: api
protocol: TCP
- namespace: hermes
name: hermes-model-gate
type: ClusterIP
selector:
app: hermes-model-gate
ports:
- name: http
port: 11434
targetPort: http
protocol: TCP
- namespace: hermes
name: hermes-ollama
type: ClusterIP
@ -1795,6 +2081,70 @@ services:
port: 11434
targetPort: http
protocol: TCP
- namespace: hermes
name: hermes-stt
type: ClusterIP
selector:
app: hermes-stt
ports:
- name: http
port: 9000
targetPort: http
protocol: TCP
- namespace: hermes
name: hermes-triage
type: ClusterIP
selector:
app: hermes
ports:
- name: api
port: 8642
targetPort: api
protocol: TCP
- name: dashboard
port: 8787
targetPort: dashboard
protocol: TCP
- namespace: hermes
name: hermes-tts
type: ClusterIP
selector:
app: hermes-tts
ports:
- name: http
port: 9001
targetPort: http
protocol: TCP
- namespace: hermes
name: oauth2-proxy-hermes-agent
type: ClusterIP
selector:
app: oauth2-proxy-hermes-agent
ports:
- name: http
port: 80
targetPort: http
protocol: TCP
- namespace: hermes
name: oauth2-proxy-hermes-chat
type: ClusterIP
selector:
app: oauth2-proxy-hermes-chat
ports:
- name: http
port: 80
targetPort: http
protocol: TCP
- namespace: hermes
name: oauth2-proxy-hermes-triage
type: ClusterIP
selector:
app: oauth2-proxy-hermes-triage
ports:
- name: http
port: 80
targetPort: http
protocol: TCP
- namespace: jellyfin
name: jellyfin
type: ClusterIP
@ -2191,6 +2541,16 @@ services:
port: 80
targetPort: http
protocol: TCP
- namespace: veles
name: veles-artifact-export-for-cassandra
type: ClusterIP
selector:
app: veles-artifact-export-for-cassandra
ports:
- name: http
port: 8765
targetPort: http
protocol: TCP
- namespace: veles
name: veles-backend
type: ClusterIP
@ -2222,18 +2582,18 @@ services:
targetPort: 5432
protocol: TCP
http_endpoints:
- host: agent.bstein.dev
- host: agent.hermes.bstein.dev
path: /
backend:
namespace: hermes
service: hermes
port: dashboard
service: oauth2-proxy-hermes-agent
port: http
workloads:
- kind: Deployment
name: hermes
name: oauth2-proxy-hermes-agent
via:
kind: Ingress
name: agent
name: hermes-sites
source: hermes
- host: auth.bstein.dev
path: /
@ -2337,6 +2697,54 @@ http_endpoints:
kind: Ingress
name: element-call
source: comms
- host: cassandra.bstein.dev
path: /
backend:
namespace: cassandra
service: cassandra-frontend
port: 80
workloads:
- kind: Deployment
name: cassandra-frontend
via:
kind: Ingress
name: cassandra
source: cassandra
- host: cassandra.bstein.dev
path: /api
backend:
namespace: cassandra
service: cassandra-backend
port: 80
workloads: &id002
- kind: Deployment
name: cassandra-backend
via:
kind: Ingress
name: cassandra
source: cassandra
- host: cassandra.bstein.dev
path: /events
backend:
namespace: cassandra
service: cassandra-backend
port: 80
workloads: *id002
via:
kind: Ingress
name: cassandra
source: cassandra
- host: cassandra.bstein.dev
path: /ws
backend:
namespace: cassandra
service: cassandra-backend
port: 80
workloads: *id002
via:
kind: Ingress
name: cassandra
source: cassandra
- host: chat.ai.bstein.dev
path: /
backend:
@ -2350,6 +2758,19 @@ http_endpoints:
kind: Ingress
name: bstein-dev-home
source: bstein-dev-home
- host: chat.hermes.bstein.dev
path: /
backend:
namespace: hermes
service: oauth2-proxy-hermes-chat
port: http
workloads:
- kind: Deployment
name: oauth2-proxy-hermes-chat
via:
kind: Ingress
name: hermes-sites
source: hermes
- host: ci.bstein.dev
path: /
backend:
@ -2454,7 +2875,7 @@ http_endpoints:
namespace: comms
service: matrix-guest-register
port: 8080
workloads: &id003
workloads: &id004
- kind: Deployment
name: matrix-guest-register
via:
@ -2467,7 +2888,7 @@ http_endpoints:
namespace: comms
service: matrix-authentication-service
port: 8080
workloads: &id002
workloads: &id003
- kind: Deployment
name: matrix-authentication-service
via:
@ -2480,7 +2901,7 @@ http_endpoints:
namespace: comms
service: matrix-authentication-service
port: 8080
workloads: *id002
workloads: *id003
via:
kind: Ingress
name: matrix-routing
@ -2491,7 +2912,7 @@ http_endpoints:
namespace: comms
service: matrix-authentication-service
port: 8080
workloads: *id002
workloads: *id003
via:
kind: Ingress
name: matrix-routing
@ -2502,7 +2923,7 @@ http_endpoints:
namespace: comms
service: matrix-guest-register
port: 8080
workloads: *id003
workloads: *id004
via:
kind: Ingress
name: matrix-routing
@ -2550,7 +2971,7 @@ http_endpoints:
namespace: comms
service: matrix-authentication-service
port: 8080
workloads: *id002
workloads: *id003
via:
kind: Ingress
name: matrix-routing
@ -2594,7 +3015,7 @@ http_endpoints:
namespace: comms
service: matrix-guest-register
port: 8080
workloads: *id003
workloads: *id004
via:
kind: Ingress
name: matrix-routing
@ -2605,7 +3026,7 @@ http_endpoints:
namespace: comms
service: matrix-authentication-service
port: 8080
workloads: *id002
workloads: *id003
via:
kind: Ingress
name: matrix-routing
@ -2616,7 +3037,7 @@ http_endpoints:
namespace: comms
service: matrix-authentication-service
port: 8080
workloads: *id002
workloads: *id003
via:
kind: Ingress
name: matrix-routing
@ -2627,7 +3048,7 @@ http_endpoints:
namespace: comms
service: matrix-authentication-service
port: 8080
workloads: *id002
workloads: *id003
via:
kind: Ingress
name: matrix-routing
@ -2638,7 +3059,7 @@ http_endpoints:
namespace: comms
service: matrix-guest-register
port: 8080
workloads: *id003
workloads: *id004
via:
kind: Ingress
name: matrix-routing
@ -2810,6 +3231,19 @@ http_endpoints:
kind: Ingress
name: planka
source: planka
- host: triage.hermes.bstein.dev
path: /
backend:
namespace: hermes
service: oauth2-proxy-hermes-triage
port: http
workloads:
- kind: Deployment
name: oauth2-proxy-hermes-triage
via:
kind: Ingress
name: hermes-sites
source: hermes
- host: vault.bstein.dev
path: /
backend:
@ -2842,7 +3276,7 @@ http_endpoints:
namespace: veles
service: veles-backend
port: 80
workloads: &id004
workloads: &id005
- kind: Deployment
name: veles-backend
via:
@ -2855,7 +3289,7 @@ http_endpoints:
namespace: veles
service: veles-backend
port: 80
workloads: *id004
workloads: *id005
via:
kind: Ingress
name: veles
@ -2866,7 +3300,7 @@ http_endpoints:
namespace: veles
service: veles-backend
port: 80
workloads: *id004
workloads: *id005
via:
kind: Ingress
name: veles
@ -2910,6 +3344,7 @@ helmrelease_host_hints:
- mail.bstein.dev
monitoring:monitoring/alertmanager:
- alerts.bstein.dev
- bstein.dev
monitoring:monitoring/grafana:
- atlas.bstein.dev
- bstein.dev

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,9 +1,9 @@
flowchart LR
host_agent_bstein_dev["agent.bstein.dev"]
svc_hermes_hermes["hermes/hermes (Service)"]
host_agent_bstein_dev --> svc_hermes_hermes
wl_hermes_hermes["hermes/hermes (Deployment)"]
svc_hermes_hermes --> wl_hermes_hermes
host_agent_hermes_bstein_dev["agent.hermes.bstein.dev"]
svc_hermes_oauth2_proxy_hermes_agent["hermes/oauth2-proxy-hermes-agent (Service)"]
host_agent_hermes_bstein_dev --> svc_hermes_oauth2_proxy_hermes_agent
wl_hermes_oauth2_proxy_hermes_agent["hermes/oauth2-proxy-hermes-agent (Deployment)"]
svc_hermes_oauth2_proxy_hermes_agent --> wl_hermes_oauth2_proxy_hermes_agent
host_auth_bstein_dev["auth.bstein.dev"]
svc_sso_oauth2_proxy["sso/oauth2-proxy (Service)"]
host_auth_bstein_dev --> svc_sso_oauth2_proxy
@ -37,11 +37,25 @@ flowchart LR
host_call_live_bstein_dev --> svc_comms_element_call
wl_comms_element_call["comms/element-call (Deployment)"]
svc_comms_element_call --> wl_comms_element_call
host_cassandra_bstein_dev["cassandra.bstein.dev"]
svc_cassandra_cassandra_frontend["cassandra/cassandra-frontend (Service)"]
host_cassandra_bstein_dev --> svc_cassandra_cassandra_frontend
wl_cassandra_cassandra_frontend["cassandra/cassandra-frontend (Deployment)"]
svc_cassandra_cassandra_frontend --> wl_cassandra_cassandra_frontend
svc_cassandra_cassandra_backend["cassandra/cassandra-backend (Service)"]
host_cassandra_bstein_dev --> svc_cassandra_cassandra_backend
wl_cassandra_cassandra_backend["cassandra/cassandra-backend (Deployment)"]
svc_cassandra_cassandra_backend --> wl_cassandra_cassandra_backend
host_chat_ai_bstein_dev["chat.ai.bstein.dev"]
svc_bstein_dev_home_chat_ai_gateway["bstein-dev-home/chat-ai-gateway (Service)"]
host_chat_ai_bstein_dev --> svc_bstein_dev_home_chat_ai_gateway
wl_bstein_dev_home_chat_ai_gateway["bstein-dev-home/chat-ai-gateway (Deployment)"]
svc_bstein_dev_home_chat_ai_gateway --> wl_bstein_dev_home_chat_ai_gateway
host_chat_hermes_bstein_dev["chat.hermes.bstein.dev"]
svc_hermes_oauth2_proxy_hermes_chat["hermes/oauth2-proxy-hermes-chat (Service)"]
host_chat_hermes_bstein_dev --> svc_hermes_oauth2_proxy_hermes_chat
wl_hermes_oauth2_proxy_hermes_chat["hermes/oauth2-proxy-hermes-chat (Deployment)"]
svc_hermes_oauth2_proxy_hermes_chat --> wl_hermes_oauth2_proxy_hermes_chat
host_ci_bstein_dev["ci.bstein.dev"]
svc_jenkins_jenkins["jenkins/jenkins (Service)"]
host_ci_bstein_dev --> svc_jenkins_jenkins
@ -156,6 +170,11 @@ flowchart LR
host_tasks_bstein_dev --> svc_planka_planka
wl_planka_planka["planka/planka (Deployment)"]
svc_planka_planka --> wl_planka_planka
host_triage_hermes_bstein_dev["triage.hermes.bstein.dev"]
svc_hermes_oauth2_proxy_hermes_triage["hermes/oauth2-proxy-hermes-triage (Service)"]
host_triage_hermes_bstein_dev --> svc_hermes_oauth2_proxy_hermes_triage
wl_hermes_oauth2_proxy_hermes_triage["hermes/oauth2-proxy-hermes-triage (Deployment)"]
svc_hermes_oauth2_proxy_hermes_triage --> wl_hermes_oauth2_proxy_hermes_triage
host_vault_bstein_dev["vault.bstein.dev"]
svc_vaultwarden_vaultwarden_service["vaultwarden/vaultwarden-service (Service)"]
host_vault_bstein_dev --> svc_vaultwarden_vaultwarden_service
@ -184,6 +203,12 @@ flowchart LR
svc_bstein_dev_home_chat_ai_gateway
wl_bstein_dev_home_chat_ai_gateway
end
subgraph cassandra[cassandra]
svc_cassandra_cassandra_frontend
wl_cassandra_cassandra_frontend
svc_cassandra_cassandra_backend
wl_cassandra_cassandra_backend
end
subgraph comms[comms]
svc_comms_matrix_wellknown
wl_comms_matrix_wellknown
@ -222,8 +247,12 @@ flowchart LR
wl_health_wger
end
subgraph hermes[hermes]
svc_hermes_hermes
wl_hermes_hermes
svc_hermes_oauth2_proxy_hermes_agent
wl_hermes_oauth2_proxy_hermes_agent
svc_hermes_oauth2_proxy_hermes_chat
wl_hermes_oauth2_proxy_hermes_chat
svc_hermes_oauth2_proxy_hermes_triage
wl_hermes_oauth2_proxy_hermes_triage
end
subgraph jellyfin[jellyfin]
svc_jellyfin_pegasus

View File

@ -1,145 +0,0 @@
# Hermes Automated Triage Demo — Runbook
How to arm, run, watch, and roll back the automated failure-to-repair demo.
Architecture background: `knowledge/hermes-automated-triage-24h-plan.md`.
## What the loop does
```text
You arm the failure (SEED_FAILURE=true)
-> Jenkins job hermes-triage-demo fails (fixture reads "unhealthy")
-> test-runner pod also writes the incident JSON to stdout
(Fluent Bit ships it to OpenSearch kube-*)
-> Ariadne polls the job every minute, opens incident <job>/<build>
-> Ariadne bundles Jenkins evidence + bounded OpenSearch excerpts
-> Ariadne calls the Hermes Agent API (/v1/runs) with
$triage-titan-test-failures
-> Hermes returns schema-valid diagnosis + requested_action
-> Ariadne authorizes (twelve gates) and, if remediation is enabled,
patches the fixture ConfigMap back to "healthy" in process
-> Ariadne triggers ONE rebuild with SEED_FAILURE=false
-> rebuild passes -> incident resolved
Anything else -> issue in the service repo + human_required metric
```
The model behind Hermes is `anthropic/claude-opus-5` as of 2026-08-06, with
`openai-codex/gpt-5.6-terra` as first fallback and a local `gpt-oss:20b`
behind that, so an expired Anthropic credential degrades rather than stops.
Two behaviours worth knowing before you demo, because both look like nothing
happening:
- **A build that never finishes** is escalated once it passes
`ARIADNE_HERMES_HUNG_BUILD_MINUTES` (default 45). No model is consulted -
the console is still being written - so the issue says only that the build
overran and is holding an agent slot.
- **Alerting no longer fires on every escalation.** The issue in the service
repository is the durable artifact. Email now means either a repair ran and
failed, or an escalation has sat untouched for six hours.
## Arming the demo
Jenkins UI: `https://ci.bstein.dev/job/hermes-triage-demo/` → *Build with
Parameters* → check `SEED_FAILURE` → Build.
CLI (any Jenkins user API token):
```bash
curl -u <user>:<token> -X POST \
"https://ci.bstein.dev/job/hermes-triage-demo/buildWithParameters?SEED_FAILURE=true"
```
That is the only manual step. Everything after the red build is automatic.
Run `scripts/ops/hermes_triage_demo.sh preflight` first. The check that most
often decides whether a rehearsal holds its timings is the agent pool: the
Kubernetes cloud caps concurrent agent pods at `containerCapStr: "5"`, and when
real CI has taken all five the demo build sits in the queue reporting *"All
nodes of label ... are offline"* with no other symptom. Observed 2026-08-06: a
run armed at 02:13:42 did not start seeding until 02:19:37, close to six
minutes of dead air, purely because five other builds held the pool. Wait for a
free slot before starting, or quiesce CI.
## Expected timings (measured live 2026-08-06, Ariadne 0.1.0-402)
Time the demo from the moment the build goes **red**, not from arming. Arming
only queues a Jenkins build, and that leg is at the mercy of the agent pool.
- Red → fixture patched back to healthy: **25s** (≤60 s detection tick, ~16 s
Hermes diagnosis, then a single in-process Kubernetes API call).
- Red → rebuild triggered: **38s**.
- Red → rebuild green and incident resolved: **1m04s**.
The whole automated leg is just over a minute, and there is no silent phase
longer than the detection tick. Budgeted Hermes timeout is 420 s; observed
diagnosis runs are 1521 s.
Arming → red was **6m21s** on this run, but 5m35s of that was queue wait
behind a saturated agent pool. On an idle pool expect roughly 1m45s.
The earlier 2026-08-05 figures (1m15s red→repaired, 4m00s total) were measured
when the repair spawned its own Kubernetes Job. Converting the repair to an
in-process call removed a whole pod launch from the critical path.
## Watching it live
- Jenkins: `https://ci.bstein.dev/job/hermes-triage-demo/` (red build N,
then green build N+1 with `SEED_FAILURE=false`).
- Ariadne incident state:
`GET http://ariadne.maintenance/api/internal/audit/events` (in-cluster) or
`/api/admin/audit/events` (Keycloak JWT) — event types
`hermes_autotriage_incident`, `hermes_autotriage_diagnosis`,
`hermes_autotriage_action`. Status flow:
`detected → diagnosed → repairing → awaiting_rebuild → resolved`.
- Metrics (VictoriaMetrics / Grafana Explore):
`ariadne_hermes_triage_incident{jenkins_job="hermes-triage-demo"}`,
`ariadne_hermes_triage_action_total`,
`ariadne_hermes_triage_duration_seconds`.
- Repair evidence: `kubectl -n hermes-triage-demo get jobs` shows
`hermes-demo-test-<N>` (failed) and `hermes-demo-repair-<N>` (succeeded);
both TTL-clean after 1 h.
- Hermes side: the run appears in the dashboard at
`https://agent.bstein.dev` (session/run history).
- Escalation path: alert `HermesTriageHumanRequired` in vmalert
(`vmalert-atlas-availability` deployment, 1 m interval, `for: 2m`)
fires to Alertmanager for any `human_required` incident. Gauges are
republished every tick from stored incident state (restart-safe), and
the alert self-clears once a newer build of the same job is green.
Note: Alertmanager's default receiver is currently null — the alert is
visible in vmalert/Alertmanager/Grafana but pushes no notification.
## Demonstrating safe escalation (second path)
Any failure that does not match the demo-fixture signature — or any
invalid/low-confidence/unknown-action Hermes response — ends as
`status="human_required"` with **no mutation**. The simplest live demo:
temporarily set `ARIADNE_HERMES_AUTOREMEDIATION_ENABLED=false` (see below)
and arm the failure; Ariadne diagnoses fully but executes nothing, and the
alert fires instead.
## Kill switch and rollback
- Instant behavioral off-switch (Flux-managed, in
`services/maintenance/apps/ariadne-deployment.yaml`):
`ARIADNE_HERMES_AUTOREMEDIATION_ENABLED=false` → diagnose-only.
`ARIADNE_HERMES_AUTOTRIAGE_ENABLED=false` → fully off.
- The automatic loop can only ever: create Jobs named
`hermes-demo-repair-*` in namespace `hermes-triage-demo`, and trigger
rebuilds of allowlisted jobs (`ARIADNE_HERMES_AUTOTRIAGE_JOB_ALLOWLIST`,
currently `hermes-triage-demo` only). One action + one rebuild per
incident, ever (storage-backed idempotency).
- Full teardown: remove `hermes-triage-demo` from
`clusters/atlas/flux-system/applications/kustomization.yaml` (Flux prunes
the namespace) and delete the `pipelineJob('hermes-triage-demo')` block
from `services/jenkins/configmap-jcasc.yaml`.
## Credentials
- Ariadne → Hermes: `Authorization: Bearer` key shared via the
`hermes-api-server-key` Secret present in both `hermes` and `maintenance`
namespaces (Hermes's init container seeds it into the persistent `.env`).
NOTE: currently manually created (Vault migration pending — see plan
handoff); rotating = write new value to both Secrets, restart hermes
deployment and ariadne deployment.
- Ariadne → Jenkins: existing `JENKINS_API_USER/TOKEN` from Vault
(`atlas/maintenance/ariadne-db`).

View File

@ -132,3 +132,35 @@ Smaller corrections to the same nodes:
once the `junit` plugin is live. Note the plugin was never blocked on a core
upgrade as previously recorded: `junit 1369.v15da_00283f06` runs on core
2.528.3, only the latest release requires 2.533.
## Third pass: the escalation branch now has an output
The chart shows the human path as a dead end for the automation: no action
fits, an issue is filed, and Ariadne learns nothing. That is no longer the
whole story.
**A diagnosis may now propose a remediation Ariadne cannot perform.** When no
allowlisted action fits, Hermes may return `suggested_remediation` naming the
action it believes would work and the evidence that should be required before
running it. So the human path forks: an escalation always files an issue, and
some escalations also carry a proposal. The chart should show that second
output leaving the escalation node, labelled as a proposal for a maintainer
rather than as anything executable.
**The proposal must be drawn outside the trust boundary.** It is prose in an
issue and a field in an audit event. No gate reads it; an id that is not
already in the allowlist still fails `action_not_allowlisted`. If the boundary
is drawn as "Hermes advises, Ariadne acts", the proposal sits firmly on the
advisory side, and the arrow from it should terminate at a person, not at the
Action registry. The registry only grows when a human deploys a new action.
**The three allowlisted actions are now four.** `action_registry` should list
`repair_demo_fixture`, `retry_transient_infra`, `reclaim_workspace_storage`
and `clear_stuck_agent_pods`, each reachable only from its own classification.
Two of those are worth calling out on the chart as separate paths rather than
folding them into a generic retry, because the distinction is the point:
`workspace_storage_exhausted` reclaims stale workspace storage *before*
rebuilding, since a plain rebuild lands on the same full volume; and
`jenkins_agent_provisioning_failure` clears finished agent pods first, since a
retry otherwise queues behind the same stuck pool.

View File

@ -15,13 +15,28 @@ You arm the failure (SEED_FAILURE=true)
-> Ariadne calls the Hermes Agent API (/v1/runs) with
$triage-titan-test-failures
-> Hermes returns schema-valid diagnosis + requested_action
-> Ariadne authorizes (nine gates) and, if remediation is enabled,
creates Job hermes-demo-repair-<build> (writes "healthy")
-> Ariadne authorizes (twelve gates) and, if remediation is enabled,
patches the fixture ConfigMap back to "healthy" in process
-> Ariadne triggers ONE rebuild with SEED_FAILURE=false
-> rebuild passes -> incident resolved
Anything else -> human_required metric -> vmalert HermesTriageHumanRequired
Anything else -> issue in the service repo + human_required metric
```
The model behind Hermes is `anthropic/claude-opus-5` as of 2026-08-06, with
`openai-codex/gpt-5.6-terra` as first fallback and a local `gpt-oss:20b`
behind that, so an expired Anthropic credential degrades rather than stops.
Two behaviours worth knowing before you demo, because both look like nothing
happening:
- **A build that never finishes** is escalated once it passes
`ARIADNE_HERMES_HUNG_BUILD_MINUTES` (default 45). No model is consulted -
the console is still being written - so the issue says only that the build
overran and is holding an agent slot.
- **Alerting no longer fires on every escalation.** The issue in the service
repository is the durable artifact. Email now means either a repair ran and
failed, or an escalation has sat untouched for six hours.
## Arming the demo
Jenkins UI: `https://ci.bstein.dev/job/hermes-triage-demo/` → *Build with
@ -36,17 +51,35 @@ curl -u <user>:<token> -X POST \
That is the only manual step. Everything after the red build is automatic.
## Expected timings (measured live, first full loop 2026-08-05)
Run `scripts/ops/hermes_triage_demo.sh preflight` first. The check that most
often decides whether a rehearsal holds its timings is the agent pool: the
Kubernetes cloud caps concurrent agent pods at `containerCapStr: "5"`, and when
real CI has taken all five the demo build sits in the queue reporting *"All
nodes of label ... are offline"* with no other symptom. Observed 2026-08-06: a
run armed at 02:13:42 did not start seeding until 02:19:37, close to six
minutes of dead air, purely because five other builds held the pool. Wait for a
free slot before starting, or quiesce CI.
- Armed → seeded build red: **1m49s** (mostly agent-pod scheduling; longer
if the Jenkins agent capacity of 5 is busy with other CI builds).
- Red → repair Job succeeded: **1m15s** (≤60 s detection tick + ~16 s
Hermes diagnosis + authorization + repair pod).
- Repair → rebuild green: **32s**.
- Green → incident resolved: **24s** (next detection tick).
- Total armed-to-resolved: **4m00s**. Budgeted Hermes timeout is 420 s;
observed diagnosis runs were 1521 s on gpt-5.6-terra. The only "silent"
phases are the two Jenkins agent-pod waits.
## Expected timings (measured live 2026-08-06, Ariadne 0.1.0-402)
Time the demo from the moment the build goes **red**, not from arming. Arming
only queues a Jenkins build, and that leg is at the mercy of the agent pool.
- Red → fixture patched back to healthy: **25s** (≤60 s detection tick, ~16 s
Hermes diagnosis, then a single in-process Kubernetes API call).
- Red → rebuild triggered: **38s**.
- Red → rebuild green and incident resolved: **1m04s**.
The whole automated leg is just over a minute, and there is no silent phase
longer than the detection tick. Budgeted Hermes timeout is 420 s; observed
diagnosis runs are 1521 s.
Arming → red was **6m21s** on this run, but 5m35s of that was queue wait
behind a saturated agent pool. On an idle pool expect roughly 1m45s.
The earlier 2026-08-05 figures (1m15s red→repaired, 4m00s total) were measured
when the repair spawned its own Kubernetes Job. Converting the repair to an
in-process call removed a whole pod launch from the critical path.
## Watching it live
@ -66,7 +99,7 @@ That is the only manual step. Everything after the red build is automatic.
`hermes-demo-test-<N>` (failed) and `hermes-demo-repair-<N>` (succeeded);
both TTL-clean after 1 h.
- Hermes side: the run appears in the dashboard at
`https://agent.bstein.dev` (session/run history).
`https://agent.hermes.bstein.dev` (session/run history).
- Escalation path: alert `HermesTriageHumanRequired` in vmalert
(`vmalert-atlas-availability` deployment, 1 m interval, `for: 2m`)
fires to Alertmanager for any `human_required` incident. Gauges are

View File

@ -107,21 +107,11 @@ data:
home_mode: auto
approvals:
# This owner-only workspace is already bounded by non-root execution,
# repository scope, and read-only cluster RBAC. Keep routine engineering
# work non-interactive while retaining explicit hard denies below.
# The owner workspace deliberately runs unattended. Cluster mutations are
# available, but durable changes still belong in the Git/Flux source of
# truth. Retain only history-destroying Git hard denies.
mode: "off"
deny:
- "*kubectl apply*"
- "*kubectl delete*"
- "*kubectl patch*"
- "*kubectl scale*"
- "*kubectl exec*"
- "*kubectl port-forward*"
- "*flux reconcile*"
- "*flux suspend*"
- "*flux resume*"
- "*vault kv*"
- "*git push --force*"
- "*git push -f*"
- "*git reset --hard*"
@ -248,11 +238,13 @@ data:
objective's difficulty warrants it; otherwise synthesize at the original
effort.
This pod has Cassandra-scoped, read-only Kubernetes access for workloads,
logs, events, networking, and rollout diagnosis. It cannot read Secrets,
exec into pods, or mutate the cluster. Triage belongs at
triage.hermes.bstein.dev and all Atlas changes are delivered through the
titan-iac Git/Flux workflow.
This owner-only pod has cluster-admin access across Atlas, including logs,
Secrets, exec, port-forwarding, rollout operations, and Flux reconciliation.
Prefer the titan-iac Git/Flux workflow for every durable cluster change;
direct operations are available for explicit operator requests, incident
recovery, and verification, and must be followed by a matching source-of-
truth change when they alter desired state. Never expose credentials in
chat or logs. Triage belongs at triage.hermes.bstein.dev.
START-HERE.md: |
# Agent Hermes
@ -267,4 +259,6 @@ data:
for automatic routing, or `/route manual <codex|claude>
<low|medium|high|xhigh> [model]` for a persistent override. The first native
Codex worker requires one device-code login; subsequent sessions persist on
the agent volume.
the agent volume. The owner workspace includes cluster-admin Kubernetes
access plus `kubectl`, `flux`, `helm`, `kustomize`, `jq`, `yq`, `gh`, Git,
SSH, Python, Node, the browser/computer tools, and the native provider CLIs.

View File

@ -24,7 +24,7 @@ spec:
ai.bstein.dev/execution: Hermes Kanban with durable direct Codex and Claude Code CLI workers
ai.bstein.dev/model-policy: Jetson-assisted AUTO routing, low through xhigh, cross-provider fallback
ai.bstein.dev/placement: rpi5 preferred; Jetson deferred until state storage is available
ai.bstein.dev/config-rev: "20260810-native-kanban-direct-cli-lanes"
ai.bstein.dev/config-rev: "20260810-owner-union-access"
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: hermes-agent
vault.hashicorp.com/agent-inject-secret-anthropic-token: kv/data/atlas/hermes/agent-tokens
@ -172,6 +172,7 @@ spec:
chmod 0755 "${tools}/bin/kubectl.tmp"
mv "${tools}/bin/kubectl.tmp" "${tools}/bin/kubectl"
fi
/bin/sh /opt/coordinator/install_agent_tools.sh
securityContext:
allowPrivilegeEscalation: false
runAsUser: 10000
@ -181,6 +182,9 @@ spec:
volumeMounts:
- name: home
mountPath: /opt/data
- name: coordinator
mountPath: /opt/coordinator
readOnly: true
resources:
requests: {cpu: 100m, memory: 256Mi}
limits: {cpu: "1", memory: 1Gi}
@ -571,7 +575,7 @@ spec:
name: hermes-auto-router-plugin
- name: tmp
emptyDir:
sizeLimit: 256Mi
sizeLimit: 4Gi
- name: ttyd-index
emptyDir:
sizeLimit: 2Mi

View File

@ -11,9 +11,9 @@ users:
user:
tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
contexts:
- name: cassandra-readonly
- name: atlas-owner
context:
cluster: atlas
user: hermes-agent
namespace: cassandra
current-context: cassandra-readonly
namespace: default
current-context: atlas-owner

View File

@ -0,0 +1,16 @@
# services/hermes/agent-rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: hermes-agent-cluster-admin
labels:
app.kubernetes.io/name: hermes-agent
app.kubernetes.io/part-of: hermes
subjects:
- kind: ServiceAccount
name: hermes-agent
namespace: hermes
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin

View File

@ -9,6 +9,7 @@ resources:
- agent-configmap.yaml
- chat-configmap.yaml
- rbac.yaml
- agent-rbac.yaml
- pvc.yaml
- chat-pvcs.yaml
- model-gate-rbac.yaml
@ -54,6 +55,7 @@ configMapGenerator:
- hermes_coordinator.py=scripts/hermes_coordinator.py
- hermes_model_routing.py=scripts/hermes_model_routing.py
- hermes_stt_client.py=scripts/hermes_stt_client.py
- install_agent_tools.sh=scripts/install_agent_tools.sh
- migrate_herdr_state.py=scripts/migrate_herdr_state.py
- patch_hermes_auth.py=scripts/patch_hermes_auth.py
- patch_ttyd_index.py=scripts/patch_ttyd_index.py

View File

@ -74,74 +74,11 @@ spec:
ports:
- {protocol: TCP, port: 7681}
- {protocol: TCP, port: 8787}
# agent.hermes.bstein.dev is an owner-only engineering workstation. The
# browser boundary remains OAuth-protected, while its workers need to reach
# every cluster namespace, Atlas LAN service, and hosted provider endpoint.
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- {protocol: UDP, port: 53}
- {protocol: TCP, port: 53}
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: vault
podSelector:
matchLabels:
app: vault
ports:
- {protocol: TCP, port: 8200}
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ai
podSelector:
matchLabels:
app: ollama
ports:
- {protocol: TCP, port: 11434}
- to:
- podSelector:
matchLabels:
app: hermes-model-gate
ports:
- {protocol: TCP, port: 8080}
- to:
- ipBlock:
cidr: 10.43.0.1/32
ports:
- {protocol: TCP, port: 443}
- to:
- ipBlock:
cidr: 192.168.22.11/32
- ipBlock:
cidr: 192.168.22.12/32
- ipBlock:
cidr: 192.168.22.13/32
ports:
- {protocol: TCP, port: 6443}
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: traefik
podSelector:
matchLabels:
app: traefik
ports:
- {protocol: TCP, port: 443}
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 10.0.0.0/8
- 100.64.0.0/10
- 127.0.0.0/8
- 169.254.0.0/16
- 172.16.0.0/12
- 192.168.0.0/16
- {}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy

View File

@ -10,9 +10,6 @@ from typing import Any
DENIED_COMMANDS: tuple[tuple[str, re.Pattern[str]], ...] = (
("Kubernetes mutations are delivered through Flux", re.compile(r"(?:^|[;&|]\s*)kubectl\s+(?:apply|delete|patch|scale|exec|port-forward)\b", re.I)),
("Flux control operations require an explicit operator action", re.compile(r"(?:^|[;&|]\s*)flux\s+(?:reconcile|suspend|resume)\b", re.I)),
("Vault data access is outside the worker boundary", re.compile(r"(?:^|[;&|]\s*)vault\s+kv\b", re.I)),
("force-push is prohibited", re.compile(r"(?:^|[;&|]\s*)git\s+push\b[^\n]*(?:--force(?:-with-lease)?|(?:^|\s)-f(?:\s|$))", re.I)),
("destructive Git reset is prohibited", re.compile(r"(?:^|[;&|]\s*)git\s+reset\b[^\n]*--hard\b", re.I)),
("destructive Git clean is prohibited", re.compile(r"(?:^|[;&|]\s*)git\s+clean\b[^\n]*(?:-[a-z]*f[a-z]*|--force)\b", re.I)),

View File

@ -201,7 +201,7 @@ Work only on this objective and its acceptance criteria:
Workspace: {workspace}
{handoff}
Operate autonomously inside the workspace. Inspect before editing, preserve unrelated user changes, run proportionate tests, and do not claim completion without evidence. Kubernetes mutations must be delivered through Flux-tracked repository manifests; cluster access is read-only. Do not force-push, hard-reset, clean untracked files, read Vault values, or expose credentials.
Operate autonomously inside the workspace. Inspect before editing, preserve unrelated user changes, run proportionate tests, and do not claim completion without evidence. You have owner-level Kubernetes access in every namespace. Prefer Flux-tracked manifests for durable changes, but use kubectl, Flux, exec, port-forwarding, rollout operations, and existing Vault workflows when the objective or incident requires them. Persist any desired-state mutation back to Git. Do not force-push, hard-reset, clean untracked files, or expose credentials.
Return a final JSON object matching the supplied schema. Use status=blocked only for a concrete unresolved blocker. List changed files, tests run, durable artifact paths, and blockers explicitly.
"""
@ -432,16 +432,6 @@ def _claude_command(route: Route, prompt: str, state: dict[str, Any], resume: bo
session_id = str(state["claude_session_id"])
session = ["--resume", session_id] if resume else ["--session-id", session_id]
denied = [
"Bash(kubectl apply *)",
"Bash(kubectl delete *)",
"Bash(kubectl patch *)",
"Bash(kubectl scale *)",
"Bash(kubectl exec *)",
"Bash(kubectl port-forward *)",
"Bash(flux reconcile *)",
"Bash(flux suspend *)",
"Bash(flux resume *)",
"Bash(vault kv *)",
"Bash(git push --force *)",
"Bash(git reset --hard *)",
"Bash(git clean -f *)",

View File

@ -40,7 +40,7 @@ def configure_claude_settings(path: Path) -> None:
permissions = value.get("permissions")
if not isinstance(permissions, dict):
permissions = {}
managed_denies = [
legacy_managed_denies = [
"Bash(kubectl apply *)",
"Bash(kubectl delete *)",
"Bash(kubectl patch *)",
@ -51,6 +51,8 @@ def configure_claude_settings(path: Path) -> None:
"Bash(flux suspend *)",
"Bash(flux resume *)",
"Bash(vault kv *)",
]
managed_denies = [
"Bash(git push --force *)",
"Bash(git reset --hard *)",
"Bash(git clean -f *)",
@ -58,9 +60,13 @@ def configure_claude_settings(path: Path) -> None:
existing_denies = permissions.get("deny")
if not isinstance(existing_denies, list):
existing_denies = []
previous_managed = {*legacy_managed_denies, *managed_denies}
existing_denies = [
str(item) for item in existing_denies if str(item) not in previous_managed
]
permissions["deny"] = [
*[str(item) for item in existing_denies],
*[item for item in managed_denies if item not in existing_denies],
*existing_denies,
*managed_denies,
]
value["permissions"] = permissions

View File

@ -0,0 +1,76 @@
#!/bin/sh
# Install the pinned ARM64 operator toolchain on the persistent owner volume.
set -eu
tools=${HERMES_AGENT_TOOLS_DIR:-/opt/data/tools}
bin=${tools}/bin
stamp=${tools}/.operator-cli-versions-2.7.0-3.18.6-5.7.1-1.8.1-4.47.1-2.78.0
mkdir -p "${bin}"
if [ -f "${stamp}" ]; then
exit 0
fi
work=$(mktemp -d "${tools}/.operator-tools.XXXXXX")
trap 'rm -rf "${work}"' 0 HUP INT TERM
fetch() {
url=$1
checksum=$2
destination=$3
curl -fsSL -o "${destination}" "${url}"
printf '%s %s\n' "${checksum}" "${destination}" | sha256sum -c -
}
publish() {
source=$1
name=$2
chmod 0755 "${source}"
mv "${source}" "${bin}/${name}"
}
fetch \
https://github.com/fluxcd/flux2/releases/download/v2.7.0/flux_2.7.0_linux_arm64.tar.gz \
758703b8cd96be98f1ea23b7bd3ff2ae13d90a23467d8aba8b83adbe335c6854 \
"${work}/flux.tar.gz"
mkdir "${work}/flux"
tar -xzf "${work}/flux.tar.gz" -C "${work}/flux"
publish "${work}/flux/flux" flux
fetch \
https://get.helm.sh/helm-v3.18.6-linux-arm64.tar.gz \
5b8e00b6709caab466cbbb0bc29ee09059b8dc9417991dd04b497530e49b1737 \
"${work}/helm.tar.gz"
mkdir "${work}/helm"
tar -xzf "${work}/helm.tar.gz" -C "${work}/helm"
publish "${work}/helm/linux-arm64/helm" helm
fetch \
https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv5.7.1/kustomize_v5.7.1_linux_arm64.tar.gz \
4261a040217df3bd6896597c3986d1465925726e4f22a945304b5233a4dcdbda \
"${work}/kustomize.tar.gz"
mkdir "${work}/kustomize"
tar -xzf "${work}/kustomize.tar.gz" -C "${work}/kustomize"
publish "${work}/kustomize/kustomize" kustomize
fetch \
https://github.com/jqlang/jq/releases/download/jq-1.8.1/jq-linux-arm64 \
6bc62f25981328edd3cfcfe6fe51b073f2d7e7710d7ef7fcdac28d4e384fc3d4 \
"${work}/jq"
publish "${work}/jq" jq
fetch \
https://github.com/mikefarah/yq/releases/download/v4.47.1/yq_linux_arm64 \
b7f7c991abe262b0c6f96bbcb362f8b35429cefd59c8b4c2daa4811f1e9df599 \
"${work}/yq"
publish "${work}/yq" yq
fetch \
https://github.com/cli/cli/releases/download/v2.78.0/gh_2.78.0_linux_arm64.tar.gz \
9e3ca75b227a5503f6ef92c4b8b6dbf94e34bfdd8069ac0f16b8739856ebba7b \
"${work}/gh.tar.gz"
mkdir "${work}/gh"
tar -xzf "${work}/gh.tar.gz" -C "${work}/gh"
publish "${work}/gh/gh_2.78.0_linux_arm64/bin/gh" gh
touch "${stamp}"

View File

@ -523,18 +523,17 @@ def test_provider_commands_are_structured_unattended_and_capped(tmp_path: Path):
assert "--output-format" in claude and "stream-json" in claude
assert "--json-schema" in claude
assert "--disallowedTools" in claude
assert "Bash(kubectl apply *)" not in claude
assert "Bash(flux reconcile *)" not in claude
assert "max" not in claude
@pytest.mark.parametrize(
"command",
[
"kubectl apply -k services/hermes",
"git push --force origin main",
"git reset --hard HEAD~1",
"git clean -fd",
"flux reconcile kustomization hermes",
"vault kv get kv/atlas/hermes",
],
)
def test_claude_pretool_hook_blocks_hard_denies(command: str):
@ -544,13 +543,32 @@ def test_claude_pretool_hook_blocks_hard_denies(command: str):
def test_claude_pretool_hook_allows_normal_engineering():
assert policy.denial_reason("pytest -q testing/tests") is None
assert policy.denial_reason("git push origin feature/hermes") is None
assert policy.denial_reason("kubectl delete pod -n cassandra stuck-worker") is None
assert policy.denial_reason("flux reconcile kustomization hermes") is None
assert policy.denial_reason("vault kv get kv/atlas/hermes") is None
def test_claude_settings_preserve_state_and_install_three_guardrail_layers(tmp_path: Path):
state = tmp_path / ".claude.json"
settings = tmp_path / "settings.json"
state.write_text('{"promptQueueUseCount": 4}\n', encoding="utf-8")
settings.write_text('{"theme":"dark"}\n', encoding="utf-8")
settings.write_text(
json.dumps(
{
"theme": "dark",
"permissions": {
"deny": [
"Bash(kubectl apply *)",
"Bash(flux reconcile *)",
"Bash(vault kv *)",
"Bash(custom-owner-rule *)",
]
},
}
)
+ "\n",
encoding="utf-8",
)
client_config.configure_claude_state(state)
client_config.configure_claude_settings(settings)
@ -561,6 +579,10 @@ def test_claude_settings_preserve_state_and_install_three_guardrail_layers(tmp_p
assert state_value["bypassPermissionsModeAccepted"] is True
assert settings_value["theme"] == "dark"
assert "Bash(git reset --hard *)" in settings_value["permissions"]["deny"]
assert "Bash(custom-owner-rule *)" in settings_value["permissions"]["deny"]
assert "Bash(kubectl apply *)" not in settings_value["permissions"]["deny"]
assert "Bash(flux reconcile *)" not in settings_value["permissions"]["deny"]
assert "Bash(vault kv *)" not in settings_value["permissions"]["deny"]
hook = settings_value["hooks"]["PreToolUse"][0]["hooks"][0]
assert "claude_command_policy.py" in hook["command"]
@ -659,17 +681,54 @@ def test_agent_network_boundary_allows_only_authenticated_web_surfaces():
],
}
]
assert isolation["spec"]["egress"] == [{}]
def test_agent_has_cassandra_readonly_kubernetes_context():
def test_owner_agent_has_cluster_admin_kubernetes_context():
config = yaml.safe_load((HERMES / "agent-kubeconfig.yaml").read_text())
assert config["current-context"] == "cassandra-readonly"
assert config["contexts"][0]["context"]["namespace"] == "cassandra"
rbac_path = HERMES.parent / "cassandra/hermes-agent-rbac.yaml"
assert config["current-context"] == "atlas-owner"
assert config["contexts"][0]["context"]["namespace"] == "default"
rbac_path = HERMES / "agent-rbac.yaml"
documents = [item for item in yaml.safe_load_all(rbac_path.read_text()) if item]
role = next(item for item in documents if item["kind"] == "Role")
assert "secrets" not in {resource for rule in role["rules"] for resource in rule["resources"]}
assert {verb for rule in role["rules"] for verb in rule["verbs"]} == {"get", "list", "watch"}
binding = next(item for item in documents if item["kind"] == "ClusterRoleBinding")
assert binding["roleRef"] == {
"apiGroup": "rbac.authorization.k8s.io",
"kind": "ClusterRole",
"name": "cluster-admin",
}
assert binding["subjects"] == [
{"kind": "ServiceAccount", "name": "hermes-agent", "namespace": "hermes"}
]
def test_owner_agent_installs_the_pinned_operator_toolchain():
script = (SCRIPTS / "install_agent_tools.sh").read_text()
for value in ["flux", "helm", "kustomize", "jq", "yq", "gh"]:
assert f'"${{bin}}/{value}"' in script or f" {value}\n" in script
assert script.count("sha256sum -c -") == 1
deployment = _agent_deployment()
installer = next(
item
for item in deployment["spec"]["template"]["spec"]["initContainers"]
if item["name"] == "install-agent-tools"
)
assert "/bin/sh /opt/coordinator/install_agent_tools.sh" in installer["command"][2]
assert any(mount["name"] == "coordinator" for mount in installer["volumeMounts"])
def test_owner_agent_uses_only_the_canonical_hostname():
paths = [
HERMES / "agent-configmap.yaml",
HERMES / "agent-deployment.yaml",
HERMES / "agent-ingress.yaml",
HERMES / "oauth2-proxy.yaml",
Path(__file__).parents[2] / "scripts/ops/hermes_triage_monitor.py",
]
for path in paths:
content = path.read_text()
assert "agent.bstein.dev" not in content
assert "agent.hermes.bstein.dev" in content
def test_agent_reconnect_retains_complete_history_and_long_tool_budget():