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 # Codex uses the distribution bubblewrap package when a bounded sandbox is
# requested. tmux keeps the stock Hermes TUI alive across browser reconnects; # 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 \ RUN apt-get update \
&& apt-get install -y --no-install-recommends bubblewrap tmux \ && apt-get install -y --no-install-recommends bubblewrap tmux \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*

View File

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

View File

@ -16,6 +16,16 @@
"path": "services/bstein-dev-home/migration-jobs", "path": "services/bstein-dev-home/migration-jobs",
"targetNamespace": "bstein-dev-home" "targetNamespace": "bstein-dev-home"
}, },
{
"name": "cassandra",
"path": "services/cassandra",
"targetNamespace": "cassandra"
},
{
"name": "cassandra-auth",
"path": "services/cassandra-auth",
"targetNamespace": "sso"
},
{ {
"name": "cert-manager", "name": "cert-manager",
"path": "infrastructure/cert-manager", "path": "infrastructure/cert-manager",
@ -91,6 +101,16 @@
"path": "services/hermes", "path": "services/hermes",
"targetNamespace": "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", "name": "jellyfin",
"path": "services/jellyfin", "path": "services/jellyfin",
@ -284,7 +304,7 @@
"node-role.kubernetes.io/worker": "true" "node-role.kubernetes.io/worker": "true"
}, },
"images": [ "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" "node-role.kubernetes.io/worker": "true"
}, },
"images": [ "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" "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", "kind": "Deployment",
"namespace": "climate", "namespace": "climate",
@ -825,7 +902,104 @@
"serviceAccountName": "hermes-triage", "serviceAccountName": "hermes-triage",
"nodeSelector": {}, "nodeSelector": {},
"images": [ "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" "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", "kind": "Deployment",
"namespace": "jellyfin", "namespace": "jellyfin",
@ -1188,7 +1445,7 @@
"kubernetes.io/os": "linux" "kubernetes.io/os": "linux"
}, },
"images": [ "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" "kubernetes.io/os": "linux"
}, },
"images": [ "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" "node-role.kubernetes.io/worker": "true"
}, },
"images": [ "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", "serviceAccountName": "metis",
"nodeSelector": {}, "nodeSelector": {},
"images": [ "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" "node-role.kubernetes.io/worker": "true"
}, },
"images": [ "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" "veles.bstein.dev/node-pool": "oceanus"
}, },
"images": [ "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", "serviceAccountName": "veles-frontend",
"nodeSelector": {}, "nodeSelector": {},
"images": [ "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", "namespace": "climate",
"name": "typhon", "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", "namespace": "hermes",
"name": "hermes-ollama", "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", "namespace": "jellyfin",
"name": "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", "namespace": "veles",
"name": "veles-backend", "name": "veles-backend",
@ -3379,22 +3925,22 @@
], ],
"http_endpoints": [ "http_endpoints": [
{ {
"host": "agent.bstein.dev", "host": "agent.hermes.bstein.dev",
"path": "/", "path": "/",
"backend": { "backend": {
"namespace": "hermes", "namespace": "hermes",
"service": "hermes", "service": "oauth2-proxy-hermes-agent",
"port": "dashboard", "port": "http",
"workloads": [ "workloads": [
{ {
"kind": "Deployment", "kind": "Deployment",
"name": "hermes" "name": "oauth2-proxy-hermes-agent"
} }
] ]
}, },
"via": { "via": {
"kind": "Ingress", "kind": "Ingress",
"name": "agent", "name": "hermes-sites",
"source": "hermes" "source": "hermes"
} }
}, },
@ -3558,6 +4104,86 @@
"source": "comms" "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", "host": "chat.ai.bstein.dev",
"path": "/", "path": "/",
@ -3578,6 +4204,26 @@
"source": "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", "host": "ci.bstein.dev",
"path": "/", "path": "/",
@ -4318,6 +4964,26 @@
"source": "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", "host": "vault.bstein.dev",
"path": "/", "path": "/",
@ -4472,7 +5138,8 @@
"mail.bstein.dev" "mail.bstein.dev"
], ],
"monitoring:monitoring/alertmanager": [ "monitoring:monitoring/alertmanager": [
"alerts.bstein.dev" "alerts.bstein.dev",
"bstein.dev"
], ],
"monitoring:monitoring/grafana": [ "monitoring:monitoring/grafana": [
"atlas.bstein.dev", "atlas.bstein.dev",

View File

@ -11,6 +11,12 @@ sources:
- name: bstein-dev-home-migrations - name: bstein-dev-home-migrations
path: services/bstein-dev-home/migration-jobs path: services/bstein-dev-home/migration-jobs
targetNamespace: bstein-dev-home targetNamespace: bstein-dev-home
- name: cassandra
path: services/cassandra
targetNamespace: cassandra
- name: cassandra-auth
path: services/cassandra-auth
targetNamespace: sso
- name: cert-manager - name: cert-manager
path: infrastructure/cert-manager path: infrastructure/cert-manager
targetNamespace: cert-manager targetNamespace: cert-manager
@ -56,6 +62,12 @@ sources:
- name: hermes - name: hermes
path: services/hermes path: services/hermes
targetNamespace: 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 - name: jellyfin
path: services/jellyfin path: services/jellyfin
targetNamespace: jellyfin targetNamespace: jellyfin
@ -175,7 +187,7 @@ workloads:
kubernetes.io/arch: arm64 kubernetes.io/arch: arm64
node-role.kubernetes.io/worker: 'true' node-role.kubernetes.io/worker: 'true'
images: 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 - kind: Deployment
namespace: bstein-dev-home namespace: bstein-dev-home
name: bstein-dev-home-frontend name: bstein-dev-home-frontend
@ -186,7 +198,7 @@ workloads:
kubernetes.io/arch: arm64 kubernetes.io/arch: arm64
node-role.kubernetes.io/worker: 'true' node-role.kubernetes.io/worker: 'true'
images: 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 - kind: Deployment
namespace: bstein-dev-home namespace: bstein-dev-home
name: bstein-dev-home-vault-sync name: bstein-dev-home-vault-sync
@ -207,6 +219,45 @@ workloads:
node-role.kubernetes.io/worker: 'true' node-role.kubernetes.io/worker: 'true'
images: images:
- python:3.11-slim - 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 - kind: Deployment
namespace: climate namespace: climate
name: typhon name: typhon
@ -553,7 +604,76 @@ workloads:
serviceAccountName: hermes-triage serviceAccountName: hermes-triage
nodeSelector: {} nodeSelector: {}
images: 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 - kind: Deployment
namespace: hermes namespace: hermes
name: hermes-ollama name: hermes-ollama
@ -563,6 +683,63 @@ workloads:
nodeSelector: {} nodeSelector: {}
images: images:
- ollama/ollama@sha256:2c9595c555fd70a28363489ac03bd5bf9e7c5bdf2890373c3a830ffd7252ce6d - 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 - kind: Deployment
namespace: jellyfin namespace: jellyfin
name: jellyfin name: jellyfin
@ -801,7 +978,7 @@ workloads:
kubernetes.io/arch: amd64 kubernetes.io/arch: amd64
kubernetes.io/os: linux kubernetes.io/os: linux
images: 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 - kind: DaemonSet
namespace: maintenance namespace: maintenance
name: metis-sentinel-arm64 name: metis-sentinel-arm64
@ -812,7 +989,7 @@ workloads:
kubernetes.io/arch: arm64 kubernetes.io/arch: arm64
kubernetes.io/os: linux kubernetes.io/os: linux
images: 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 - kind: DaemonSet
namespace: maintenance namespace: maintenance
name: node-image-sweeper name: node-image-sweeper
@ -872,7 +1049,7 @@ workloads:
kubernetes.io/arch: arm64 kubernetes.io/arch: arm64
node-role.kubernetes.io/worker: 'true' node-role.kubernetes.io/worker: 'true'
images: images:
- registry.bstein.dev/bstein/ariadne:0.1.0-356 - registry.bstein.dev/bstein/ariadne:0.1.0-448
- kind: Deployment - kind: Deployment
namespace: maintenance namespace: maintenance
name: maintenance-vault-sync name: maintenance-vault-sync
@ -891,7 +1068,7 @@ workloads:
serviceAccountName: metis serviceAccountName: metis
nodeSelector: {} nodeSelector: {}
images: images:
- registry.bstein.dev/bstein/metis:0.1.0-243-arm64 - registry.bstein.dev/bstein/metis:0.1.0-283-arm64
- kind: Deployment - kind: Deployment
namespace: maintenance namespace: maintenance
name: oauth2-proxy-metis name: oauth2-proxy-metis
@ -1166,7 +1343,17 @@ workloads:
kubernetes.io/arch: arm64 kubernetes.io/arch: arm64
node-role.kubernetes.io/worker: 'true' node-role.kubernetes.io/worker: 'true'
images: 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 - kind: Deployment
namespace: veles namespace: veles
name: veles-backend name: veles-backend
@ -1177,7 +1364,7 @@ workloads:
kubernetes.io/arch: amd64 kubernetes.io/arch: amd64
veles.bstein.dev/node-pool: oceanus veles.bstein.dev/node-pool: oceanus
images: images:
- registry.bstein.dev/veles/veles-backend:0.7.56 - registry.bstein.dev/veles/veles-backend:0.7.65
- kind: Deployment - kind: Deployment
namespace: veles namespace: veles
name: veles-frontend name: veles-frontend
@ -1186,7 +1373,18 @@ workloads:
serviceAccountName: veles-frontend serviceAccountName: veles-frontend
nodeSelector: {} nodeSelector: {}
images: 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 - kind: Deployment
namespace: veles namespace: veles
name: veles-vault-sync name: veles-vault-sync
@ -1247,6 +1445,36 @@ services:
port: 80 port: 80
targetPort: 8080 targetPort: 8080
protocol: TCP 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 - namespace: climate
name: typhon name: typhon
type: ClusterIP type: ClusterIP
@ -1785,6 +2013,64 @@ services:
port: 9119 port: 9119
targetPort: dashboard targetPort: dashboard
protocol: TCP 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 - namespace: hermes
name: hermes-ollama name: hermes-ollama
type: ClusterIP type: ClusterIP
@ -1795,6 +2081,70 @@ services:
port: 11434 port: 11434
targetPort: http targetPort: http
protocol: TCP 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 - namespace: jellyfin
name: jellyfin name: jellyfin
type: ClusterIP type: ClusterIP
@ -2191,6 +2541,16 @@ services:
port: 80 port: 80
targetPort: http targetPort: http
protocol: TCP 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 - namespace: veles
name: veles-backend name: veles-backend
type: ClusterIP type: ClusterIP
@ -2222,18 +2582,18 @@ services:
targetPort: 5432 targetPort: 5432
protocol: TCP protocol: TCP
http_endpoints: http_endpoints:
- host: agent.bstein.dev - host: agent.hermes.bstein.dev
path: / path: /
backend: backend:
namespace: hermes namespace: hermes
service: hermes service: oauth2-proxy-hermes-agent
port: dashboard port: http
workloads: workloads:
- kind: Deployment - kind: Deployment
name: hermes name: oauth2-proxy-hermes-agent
via: via:
kind: Ingress kind: Ingress
name: agent name: hermes-sites
source: hermes source: hermes
- host: auth.bstein.dev - host: auth.bstein.dev
path: / path: /
@ -2337,6 +2697,54 @@ http_endpoints:
kind: Ingress kind: Ingress
name: element-call name: element-call
source: comms 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 - host: chat.ai.bstein.dev
path: / path: /
backend: backend:
@ -2350,6 +2758,19 @@ http_endpoints:
kind: Ingress kind: Ingress
name: bstein-dev-home name: bstein-dev-home
source: 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 - host: ci.bstein.dev
path: / path: /
backend: backend:
@ -2454,7 +2875,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-guest-register service: matrix-guest-register
port: 8080 port: 8080
workloads: &id003 workloads: &id004
- kind: Deployment - kind: Deployment
name: matrix-guest-register name: matrix-guest-register
via: via:
@ -2467,7 +2888,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-authentication-service service: matrix-authentication-service
port: 8080 port: 8080
workloads: &id002 workloads: &id003
- kind: Deployment - kind: Deployment
name: matrix-authentication-service name: matrix-authentication-service
via: via:
@ -2480,7 +2901,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-authentication-service service: matrix-authentication-service
port: 8080 port: 8080
workloads: *id002 workloads: *id003
via: via:
kind: Ingress kind: Ingress
name: matrix-routing name: matrix-routing
@ -2491,7 +2912,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-authentication-service service: matrix-authentication-service
port: 8080 port: 8080
workloads: *id002 workloads: *id003
via: via:
kind: Ingress kind: Ingress
name: matrix-routing name: matrix-routing
@ -2502,7 +2923,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-guest-register service: matrix-guest-register
port: 8080 port: 8080
workloads: *id003 workloads: *id004
via: via:
kind: Ingress kind: Ingress
name: matrix-routing name: matrix-routing
@ -2550,7 +2971,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-authentication-service service: matrix-authentication-service
port: 8080 port: 8080
workloads: *id002 workloads: *id003
via: via:
kind: Ingress kind: Ingress
name: matrix-routing name: matrix-routing
@ -2594,7 +3015,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-guest-register service: matrix-guest-register
port: 8080 port: 8080
workloads: *id003 workloads: *id004
via: via:
kind: Ingress kind: Ingress
name: matrix-routing name: matrix-routing
@ -2605,7 +3026,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-authentication-service service: matrix-authentication-service
port: 8080 port: 8080
workloads: *id002 workloads: *id003
via: via:
kind: Ingress kind: Ingress
name: matrix-routing name: matrix-routing
@ -2616,7 +3037,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-authentication-service service: matrix-authentication-service
port: 8080 port: 8080
workloads: *id002 workloads: *id003
via: via:
kind: Ingress kind: Ingress
name: matrix-routing name: matrix-routing
@ -2627,7 +3048,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-authentication-service service: matrix-authentication-service
port: 8080 port: 8080
workloads: *id002 workloads: *id003
via: via:
kind: Ingress kind: Ingress
name: matrix-routing name: matrix-routing
@ -2638,7 +3059,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-guest-register service: matrix-guest-register
port: 8080 port: 8080
workloads: *id003 workloads: *id004
via: via:
kind: Ingress kind: Ingress
name: matrix-routing name: matrix-routing
@ -2810,6 +3231,19 @@ http_endpoints:
kind: Ingress kind: Ingress
name: planka name: planka
source: 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 - host: vault.bstein.dev
path: / path: /
backend: backend:
@ -2842,7 +3276,7 @@ http_endpoints:
namespace: veles namespace: veles
service: veles-backend service: veles-backend
port: 80 port: 80
workloads: &id004 workloads: &id005
- kind: Deployment - kind: Deployment
name: veles-backend name: veles-backend
via: via:
@ -2855,7 +3289,7 @@ http_endpoints:
namespace: veles namespace: veles
service: veles-backend service: veles-backend
port: 80 port: 80
workloads: *id004 workloads: *id005
via: via:
kind: Ingress kind: Ingress
name: veles name: veles
@ -2866,7 +3300,7 @@ http_endpoints:
namespace: veles namespace: veles
service: veles-backend service: veles-backend
port: 80 port: 80
workloads: *id004 workloads: *id005
via: via:
kind: Ingress kind: Ingress
name: veles name: veles
@ -2910,6 +3344,7 @@ helmrelease_host_hints:
- mail.bstein.dev - mail.bstein.dev
monitoring:monitoring/alertmanager: monitoring:monitoring/alertmanager:
- alerts.bstein.dev - alerts.bstein.dev
- bstein.dev
monitoring:monitoring/grafana: monitoring:monitoring/grafana:
- atlas.bstein.dev - atlas.bstein.dev
- 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 flowchart LR
host_agent_bstein_dev["agent.bstein.dev"] host_agent_hermes_bstein_dev["agent.hermes.bstein.dev"]
svc_hermes_hermes["hermes/hermes (Service)"] svc_hermes_oauth2_proxy_hermes_agent["hermes/oauth2-proxy-hermes-agent (Service)"]
host_agent_bstein_dev --> svc_hermes_hermes host_agent_hermes_bstein_dev --> svc_hermes_oauth2_proxy_hermes_agent
wl_hermes_hermes["hermes/hermes (Deployment)"] wl_hermes_oauth2_proxy_hermes_agent["hermes/oauth2-proxy-hermes-agent (Deployment)"]
svc_hermes_hermes --> wl_hermes_hermes svc_hermes_oauth2_proxy_hermes_agent --> wl_hermes_oauth2_proxy_hermes_agent
host_auth_bstein_dev["auth.bstein.dev"] host_auth_bstein_dev["auth.bstein.dev"]
svc_sso_oauth2_proxy["sso/oauth2-proxy (Service)"] svc_sso_oauth2_proxy["sso/oauth2-proxy (Service)"]
host_auth_bstein_dev --> svc_sso_oauth2_proxy host_auth_bstein_dev --> svc_sso_oauth2_proxy
@ -37,11 +37,25 @@ flowchart LR
host_call_live_bstein_dev --> svc_comms_element_call host_call_live_bstein_dev --> svc_comms_element_call
wl_comms_element_call["comms/element-call (Deployment)"] wl_comms_element_call["comms/element-call (Deployment)"]
svc_comms_element_call --> wl_comms_element_call 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"] host_chat_ai_bstein_dev["chat.ai.bstein.dev"]
svc_bstein_dev_home_chat_ai_gateway["bstein-dev-home/chat-ai-gateway (Service)"] 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 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)"] 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 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"] host_ci_bstein_dev["ci.bstein.dev"]
svc_jenkins_jenkins["jenkins/jenkins (Service)"] svc_jenkins_jenkins["jenkins/jenkins (Service)"]
host_ci_bstein_dev --> svc_jenkins_jenkins host_ci_bstein_dev --> svc_jenkins_jenkins
@ -156,6 +170,11 @@ flowchart LR
host_tasks_bstein_dev --> svc_planka_planka host_tasks_bstein_dev --> svc_planka_planka
wl_planka_planka["planka/planka (Deployment)"] wl_planka_planka["planka/planka (Deployment)"]
svc_planka_planka --> wl_planka_planka 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"] host_vault_bstein_dev["vault.bstein.dev"]
svc_vaultwarden_vaultwarden_service["vaultwarden/vaultwarden-service (Service)"] svc_vaultwarden_vaultwarden_service["vaultwarden/vaultwarden-service (Service)"]
host_vault_bstein_dev --> svc_vaultwarden_vaultwarden_service host_vault_bstein_dev --> svc_vaultwarden_vaultwarden_service
@ -184,6 +203,12 @@ flowchart LR
svc_bstein_dev_home_chat_ai_gateway svc_bstein_dev_home_chat_ai_gateway
wl_bstein_dev_home_chat_ai_gateway wl_bstein_dev_home_chat_ai_gateway
end end
subgraph cassandra[cassandra]
svc_cassandra_cassandra_frontend
wl_cassandra_cassandra_frontend
svc_cassandra_cassandra_backend
wl_cassandra_cassandra_backend
end
subgraph comms[comms] subgraph comms[comms]
svc_comms_matrix_wellknown svc_comms_matrix_wellknown
wl_comms_matrix_wellknown wl_comms_matrix_wellknown
@ -222,8 +247,12 @@ flowchart LR
wl_health_wger wl_health_wger
end end
subgraph hermes[hermes] subgraph hermes[hermes]
svc_hermes_hermes svc_hermes_oauth2_proxy_hermes_agent
wl_hermes_hermes 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 end
subgraph jellyfin[jellyfin] subgraph jellyfin[jellyfin]
svc_jellyfin_pegasus 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); `hermes-demo-test-<N>` (failed) and `hermes-demo-repair-<N>` (succeeded);
both TTL-clean after 1 h. both TTL-clean after 1 h.
- Hermes side: the run appears in the dashboard at - Hermes side: the run appears in the dashboard at
`https://agent.bstein.dev` (session/run history). `https://agent.hermes.bstein.dev` (session/run history).
- Escalation path: alert `HermesTriageHumanRequired` in vmalert - Escalation path: alert `HermesTriageHumanRequired` in vmalert
(`vmalert-atlas-availability` deployment, 1 m interval, `for: 2m`) (`vmalert-atlas-availability` deployment, 1 m interval, `for: 2m`)
fires to Alertmanager for any `human_required` incident. Gauges are fires to Alertmanager for any `human_required` incident. Gauges are

View File

@ -347,7 +347,7 @@ flowchart TB
gitops_domain["cd.bstein.dev"]:::domain gitops_domain["cd.bstein.dev"]:::domain
gitea_domain["scm.bstein.dev"]:::domain gitea_domain["scm.bstein.dev"]:::domain
harbor_domain["registry.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 jenkins_domain["ci.bstein.dev"]:::domain
end 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") JENKINS = os.environ.get("JENKINS_URL", "https://ci.bstein.dev")
GITEA = os.environ.get("GITEA_URL", "https://scm.bstein.dev") GITEA = os.environ.get("GITEA_URL", "https://scm.bstein.dev")
GRAFANA = os.environ.get("GRAFANA_URL", "https://metrics.bstein.dev") GRAFANA = os.environ.get("GRAFANA_URL", "https://metrics.bstein.dev")
HERMES_UI = os.environ.get("HERMES_URL", "https://agent.bstein.dev") HERMES_UI = os.environ.get("HERMES_URL", "https://agent.hermes.bstein.dev")
POLL_SECONDS = 6 POLL_SECONDS = 6
# `--filter <text>` restricts the monitor to incidents whose id contains that # `--filter <text>` restricts the monitor to incidents whose id contains that

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 - limitrange.yaml
- configmap.yaml - configmap.yaml
- rbac.yaml - rbac.yaml
- hermes-agent-rbac.yaml
- artifacts-pvc.yaml - artifacts-pvc.yaml
- postgres-service.yaml - postgres-service.yaml
- postgres-statefulset.yaml - postgres-statefulset.yaml

View File

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

View File

@ -16,6 +16,16 @@
"path": "services/bstein-dev-home/migration-jobs", "path": "services/bstein-dev-home/migration-jobs",
"targetNamespace": "bstein-dev-home" "targetNamespace": "bstein-dev-home"
}, },
{
"name": "cassandra",
"path": "services/cassandra",
"targetNamespace": "cassandra"
},
{
"name": "cassandra-auth",
"path": "services/cassandra-auth",
"targetNamespace": "sso"
},
{ {
"name": "cert-manager", "name": "cert-manager",
"path": "infrastructure/cert-manager", "path": "infrastructure/cert-manager",
@ -91,6 +101,16 @@
"path": "services/hermes", "path": "services/hermes",
"targetNamespace": "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", "name": "jellyfin",
"path": "services/jellyfin", "path": "services/jellyfin",
@ -284,7 +304,7 @@
"node-role.kubernetes.io/worker": "true" "node-role.kubernetes.io/worker": "true"
}, },
"images": [ "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" "node-role.kubernetes.io/worker": "true"
}, },
"images": [ "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" "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", "kind": "Deployment",
"namespace": "climate", "namespace": "climate",
@ -825,7 +902,104 @@
"serviceAccountName": "hermes-triage", "serviceAccountName": "hermes-triage",
"nodeSelector": {}, "nodeSelector": {},
"images": [ "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" "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", "kind": "Deployment",
"namespace": "jellyfin", "namespace": "jellyfin",
@ -1188,7 +1445,7 @@
"kubernetes.io/os": "linux" "kubernetes.io/os": "linux"
}, },
"images": [ "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" "kubernetes.io/os": "linux"
}, },
"images": [ "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" "node-role.kubernetes.io/worker": "true"
}, },
"images": [ "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", "serviceAccountName": "metis",
"nodeSelector": {}, "nodeSelector": {},
"images": [ "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" "node-role.kubernetes.io/worker": "true"
}, },
"images": [ "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" "veles.bstein.dev/node-pool": "oceanus"
}, },
"images": [ "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", "serviceAccountName": "veles-frontend",
"nodeSelector": {}, "nodeSelector": {},
"images": [ "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", "namespace": "climate",
"name": "typhon", "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", "namespace": "hermes",
"name": "hermes-ollama", "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", "namespace": "jellyfin",
"name": "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", "namespace": "veles",
"name": "veles-backend", "name": "veles-backend",
@ -3379,22 +3925,22 @@
], ],
"http_endpoints": [ "http_endpoints": [
{ {
"host": "agent.bstein.dev", "host": "agent.hermes.bstein.dev",
"path": "/", "path": "/",
"backend": { "backend": {
"namespace": "hermes", "namespace": "hermes",
"service": "hermes", "service": "oauth2-proxy-hermes-agent",
"port": "dashboard", "port": "http",
"workloads": [ "workloads": [
{ {
"kind": "Deployment", "kind": "Deployment",
"name": "hermes" "name": "oauth2-proxy-hermes-agent"
} }
] ]
}, },
"via": { "via": {
"kind": "Ingress", "kind": "Ingress",
"name": "agent", "name": "hermes-sites",
"source": "hermes" "source": "hermes"
} }
}, },
@ -3558,6 +4104,86 @@
"source": "comms" "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", "host": "chat.ai.bstein.dev",
"path": "/", "path": "/",
@ -3578,6 +4204,26 @@
"source": "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", "host": "ci.bstein.dev",
"path": "/", "path": "/",
@ -4318,6 +4964,26 @@
"source": "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", "host": "vault.bstein.dev",
"path": "/", "path": "/",
@ -4472,7 +5138,8 @@
"mail.bstein.dev" "mail.bstein.dev"
], ],
"monitoring:monitoring/alertmanager": [ "monitoring:monitoring/alertmanager": [
"alerts.bstein.dev" "alerts.bstein.dev",
"bstein.dev"
], ],
"monitoring:monitoring/grafana": [ "monitoring:monitoring/grafana": [
"atlas.bstein.dev", "atlas.bstein.dev",

View File

@ -11,6 +11,12 @@ sources:
- name: bstein-dev-home-migrations - name: bstein-dev-home-migrations
path: services/bstein-dev-home/migration-jobs path: services/bstein-dev-home/migration-jobs
targetNamespace: bstein-dev-home targetNamespace: bstein-dev-home
- name: cassandra
path: services/cassandra
targetNamespace: cassandra
- name: cassandra-auth
path: services/cassandra-auth
targetNamespace: sso
- name: cert-manager - name: cert-manager
path: infrastructure/cert-manager path: infrastructure/cert-manager
targetNamespace: cert-manager targetNamespace: cert-manager
@ -56,6 +62,12 @@ sources:
- name: hermes - name: hermes
path: services/hermes path: services/hermes
targetNamespace: 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 - name: jellyfin
path: services/jellyfin path: services/jellyfin
targetNamespace: jellyfin targetNamespace: jellyfin
@ -175,7 +187,7 @@ workloads:
kubernetes.io/arch: arm64 kubernetes.io/arch: arm64
node-role.kubernetes.io/worker: 'true' node-role.kubernetes.io/worker: 'true'
images: 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 - kind: Deployment
namespace: bstein-dev-home namespace: bstein-dev-home
name: bstein-dev-home-frontend name: bstein-dev-home-frontend
@ -186,7 +198,7 @@ workloads:
kubernetes.io/arch: arm64 kubernetes.io/arch: arm64
node-role.kubernetes.io/worker: 'true' node-role.kubernetes.io/worker: 'true'
images: 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 - kind: Deployment
namespace: bstein-dev-home namespace: bstein-dev-home
name: bstein-dev-home-vault-sync name: bstein-dev-home-vault-sync
@ -207,6 +219,45 @@ workloads:
node-role.kubernetes.io/worker: 'true' node-role.kubernetes.io/worker: 'true'
images: images:
- python:3.11-slim - 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 - kind: Deployment
namespace: climate namespace: climate
name: typhon name: typhon
@ -553,7 +604,76 @@ workloads:
serviceAccountName: hermes-triage serviceAccountName: hermes-triage
nodeSelector: {} nodeSelector: {}
images: 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 - kind: Deployment
namespace: hermes namespace: hermes
name: hermes-ollama name: hermes-ollama
@ -563,6 +683,63 @@ workloads:
nodeSelector: {} nodeSelector: {}
images: images:
- ollama/ollama@sha256:2c9595c555fd70a28363489ac03bd5bf9e7c5bdf2890373c3a830ffd7252ce6d - 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 - kind: Deployment
namespace: jellyfin namespace: jellyfin
name: jellyfin name: jellyfin
@ -801,7 +978,7 @@ workloads:
kubernetes.io/arch: amd64 kubernetes.io/arch: amd64
kubernetes.io/os: linux kubernetes.io/os: linux
images: 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 - kind: DaemonSet
namespace: maintenance namespace: maintenance
name: metis-sentinel-arm64 name: metis-sentinel-arm64
@ -812,7 +989,7 @@ workloads:
kubernetes.io/arch: arm64 kubernetes.io/arch: arm64
kubernetes.io/os: linux kubernetes.io/os: linux
images: 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 - kind: DaemonSet
namespace: maintenance namespace: maintenance
name: node-image-sweeper name: node-image-sweeper
@ -872,7 +1049,7 @@ workloads:
kubernetes.io/arch: arm64 kubernetes.io/arch: arm64
node-role.kubernetes.io/worker: 'true' node-role.kubernetes.io/worker: 'true'
images: images:
- registry.bstein.dev/bstein/ariadne:0.1.0-356 - registry.bstein.dev/bstein/ariadne:0.1.0-448
- kind: Deployment - kind: Deployment
namespace: maintenance namespace: maintenance
name: maintenance-vault-sync name: maintenance-vault-sync
@ -891,7 +1068,7 @@ workloads:
serviceAccountName: metis serviceAccountName: metis
nodeSelector: {} nodeSelector: {}
images: images:
- registry.bstein.dev/bstein/metis:0.1.0-243-arm64 - registry.bstein.dev/bstein/metis:0.1.0-283-arm64
- kind: Deployment - kind: Deployment
namespace: maintenance namespace: maintenance
name: oauth2-proxy-metis name: oauth2-proxy-metis
@ -1166,7 +1343,17 @@ workloads:
kubernetes.io/arch: arm64 kubernetes.io/arch: arm64
node-role.kubernetes.io/worker: 'true' node-role.kubernetes.io/worker: 'true'
images: 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 - kind: Deployment
namespace: veles namespace: veles
name: veles-backend name: veles-backend
@ -1177,7 +1364,7 @@ workloads:
kubernetes.io/arch: amd64 kubernetes.io/arch: amd64
veles.bstein.dev/node-pool: oceanus veles.bstein.dev/node-pool: oceanus
images: images:
- registry.bstein.dev/veles/veles-backend:0.7.56 - registry.bstein.dev/veles/veles-backend:0.7.65
- kind: Deployment - kind: Deployment
namespace: veles namespace: veles
name: veles-frontend name: veles-frontend
@ -1186,7 +1373,18 @@ workloads:
serviceAccountName: veles-frontend serviceAccountName: veles-frontend
nodeSelector: {} nodeSelector: {}
images: 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 - kind: Deployment
namespace: veles namespace: veles
name: veles-vault-sync name: veles-vault-sync
@ -1247,6 +1445,36 @@ services:
port: 80 port: 80
targetPort: 8080 targetPort: 8080
protocol: TCP 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 - namespace: climate
name: typhon name: typhon
type: ClusterIP type: ClusterIP
@ -1785,6 +2013,64 @@ services:
port: 9119 port: 9119
targetPort: dashboard targetPort: dashboard
protocol: TCP 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 - namespace: hermes
name: hermes-ollama name: hermes-ollama
type: ClusterIP type: ClusterIP
@ -1795,6 +2081,70 @@ services:
port: 11434 port: 11434
targetPort: http targetPort: http
protocol: TCP 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 - namespace: jellyfin
name: jellyfin name: jellyfin
type: ClusterIP type: ClusterIP
@ -2191,6 +2541,16 @@ services:
port: 80 port: 80
targetPort: http targetPort: http
protocol: TCP 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 - namespace: veles
name: veles-backend name: veles-backend
type: ClusterIP type: ClusterIP
@ -2222,18 +2582,18 @@ services:
targetPort: 5432 targetPort: 5432
protocol: TCP protocol: TCP
http_endpoints: http_endpoints:
- host: agent.bstein.dev - host: agent.hermes.bstein.dev
path: / path: /
backend: backend:
namespace: hermes namespace: hermes
service: hermes service: oauth2-proxy-hermes-agent
port: dashboard port: http
workloads: workloads:
- kind: Deployment - kind: Deployment
name: hermes name: oauth2-proxy-hermes-agent
via: via:
kind: Ingress kind: Ingress
name: agent name: hermes-sites
source: hermes source: hermes
- host: auth.bstein.dev - host: auth.bstein.dev
path: / path: /
@ -2337,6 +2697,54 @@ http_endpoints:
kind: Ingress kind: Ingress
name: element-call name: element-call
source: comms 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 - host: chat.ai.bstein.dev
path: / path: /
backend: backend:
@ -2350,6 +2758,19 @@ http_endpoints:
kind: Ingress kind: Ingress
name: bstein-dev-home name: bstein-dev-home
source: 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 - host: ci.bstein.dev
path: / path: /
backend: backend:
@ -2454,7 +2875,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-guest-register service: matrix-guest-register
port: 8080 port: 8080
workloads: &id003 workloads: &id004
- kind: Deployment - kind: Deployment
name: matrix-guest-register name: matrix-guest-register
via: via:
@ -2467,7 +2888,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-authentication-service service: matrix-authentication-service
port: 8080 port: 8080
workloads: &id002 workloads: &id003
- kind: Deployment - kind: Deployment
name: matrix-authentication-service name: matrix-authentication-service
via: via:
@ -2480,7 +2901,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-authentication-service service: matrix-authentication-service
port: 8080 port: 8080
workloads: *id002 workloads: *id003
via: via:
kind: Ingress kind: Ingress
name: matrix-routing name: matrix-routing
@ -2491,7 +2912,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-authentication-service service: matrix-authentication-service
port: 8080 port: 8080
workloads: *id002 workloads: *id003
via: via:
kind: Ingress kind: Ingress
name: matrix-routing name: matrix-routing
@ -2502,7 +2923,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-guest-register service: matrix-guest-register
port: 8080 port: 8080
workloads: *id003 workloads: *id004
via: via:
kind: Ingress kind: Ingress
name: matrix-routing name: matrix-routing
@ -2550,7 +2971,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-authentication-service service: matrix-authentication-service
port: 8080 port: 8080
workloads: *id002 workloads: *id003
via: via:
kind: Ingress kind: Ingress
name: matrix-routing name: matrix-routing
@ -2594,7 +3015,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-guest-register service: matrix-guest-register
port: 8080 port: 8080
workloads: *id003 workloads: *id004
via: via:
kind: Ingress kind: Ingress
name: matrix-routing name: matrix-routing
@ -2605,7 +3026,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-authentication-service service: matrix-authentication-service
port: 8080 port: 8080
workloads: *id002 workloads: *id003
via: via:
kind: Ingress kind: Ingress
name: matrix-routing name: matrix-routing
@ -2616,7 +3037,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-authentication-service service: matrix-authentication-service
port: 8080 port: 8080
workloads: *id002 workloads: *id003
via: via:
kind: Ingress kind: Ingress
name: matrix-routing name: matrix-routing
@ -2627,7 +3048,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-authentication-service service: matrix-authentication-service
port: 8080 port: 8080
workloads: *id002 workloads: *id003
via: via:
kind: Ingress kind: Ingress
name: matrix-routing name: matrix-routing
@ -2638,7 +3059,7 @@ http_endpoints:
namespace: comms namespace: comms
service: matrix-guest-register service: matrix-guest-register
port: 8080 port: 8080
workloads: *id003 workloads: *id004
via: via:
kind: Ingress kind: Ingress
name: matrix-routing name: matrix-routing
@ -2810,6 +3231,19 @@ http_endpoints:
kind: Ingress kind: Ingress
name: planka name: planka
source: 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 - host: vault.bstein.dev
path: / path: /
backend: backend:
@ -2842,7 +3276,7 @@ http_endpoints:
namespace: veles namespace: veles
service: veles-backend service: veles-backend
port: 80 port: 80
workloads: &id004 workloads: &id005
- kind: Deployment - kind: Deployment
name: veles-backend name: veles-backend
via: via:
@ -2855,7 +3289,7 @@ http_endpoints:
namespace: veles namespace: veles
service: veles-backend service: veles-backend
port: 80 port: 80
workloads: *id004 workloads: *id005
via: via:
kind: Ingress kind: Ingress
name: veles name: veles
@ -2866,7 +3300,7 @@ http_endpoints:
namespace: veles namespace: veles
service: veles-backend service: veles-backend
port: 80 port: 80
workloads: *id004 workloads: *id005
via: via:
kind: Ingress kind: Ingress
name: veles name: veles
@ -2910,6 +3344,7 @@ helmrelease_host_hints:
- mail.bstein.dev - mail.bstein.dev
monitoring:monitoring/alertmanager: monitoring:monitoring/alertmanager:
- alerts.bstein.dev - alerts.bstein.dev
- bstein.dev
monitoring:monitoring/grafana: monitoring:monitoring/grafana:
- atlas.bstein.dev - atlas.bstein.dev
- 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 flowchart LR
host_agent_bstein_dev["agent.bstein.dev"] host_agent_hermes_bstein_dev["agent.hermes.bstein.dev"]
svc_hermes_hermes["hermes/hermes (Service)"] svc_hermes_oauth2_proxy_hermes_agent["hermes/oauth2-proxy-hermes-agent (Service)"]
host_agent_bstein_dev --> svc_hermes_hermes host_agent_hermes_bstein_dev --> svc_hermes_oauth2_proxy_hermes_agent
wl_hermes_hermes["hermes/hermes (Deployment)"] wl_hermes_oauth2_proxy_hermes_agent["hermes/oauth2-proxy-hermes-agent (Deployment)"]
svc_hermes_hermes --> wl_hermes_hermes svc_hermes_oauth2_proxy_hermes_agent --> wl_hermes_oauth2_proxy_hermes_agent
host_auth_bstein_dev["auth.bstein.dev"] host_auth_bstein_dev["auth.bstein.dev"]
svc_sso_oauth2_proxy["sso/oauth2-proxy (Service)"] svc_sso_oauth2_proxy["sso/oauth2-proxy (Service)"]
host_auth_bstein_dev --> svc_sso_oauth2_proxy host_auth_bstein_dev --> svc_sso_oauth2_proxy
@ -37,11 +37,25 @@ flowchart LR
host_call_live_bstein_dev --> svc_comms_element_call host_call_live_bstein_dev --> svc_comms_element_call
wl_comms_element_call["comms/element-call (Deployment)"] wl_comms_element_call["comms/element-call (Deployment)"]
svc_comms_element_call --> wl_comms_element_call 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"] host_chat_ai_bstein_dev["chat.ai.bstein.dev"]
svc_bstein_dev_home_chat_ai_gateway["bstein-dev-home/chat-ai-gateway (Service)"] 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 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)"] 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 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"] host_ci_bstein_dev["ci.bstein.dev"]
svc_jenkins_jenkins["jenkins/jenkins (Service)"] svc_jenkins_jenkins["jenkins/jenkins (Service)"]
host_ci_bstein_dev --> svc_jenkins_jenkins host_ci_bstein_dev --> svc_jenkins_jenkins
@ -156,6 +170,11 @@ flowchart LR
host_tasks_bstein_dev --> svc_planka_planka host_tasks_bstein_dev --> svc_planka_planka
wl_planka_planka["planka/planka (Deployment)"] wl_planka_planka["planka/planka (Deployment)"]
svc_planka_planka --> wl_planka_planka 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"] host_vault_bstein_dev["vault.bstein.dev"]
svc_vaultwarden_vaultwarden_service["vaultwarden/vaultwarden-service (Service)"] svc_vaultwarden_vaultwarden_service["vaultwarden/vaultwarden-service (Service)"]
host_vault_bstein_dev --> svc_vaultwarden_vaultwarden_service host_vault_bstein_dev --> svc_vaultwarden_vaultwarden_service
@ -184,6 +203,12 @@ flowchart LR
svc_bstein_dev_home_chat_ai_gateway svc_bstein_dev_home_chat_ai_gateway
wl_bstein_dev_home_chat_ai_gateway wl_bstein_dev_home_chat_ai_gateway
end end
subgraph cassandra[cassandra]
svc_cassandra_cassandra_frontend
wl_cassandra_cassandra_frontend
svc_cassandra_cassandra_backend
wl_cassandra_cassandra_backend
end
subgraph comms[comms] subgraph comms[comms]
svc_comms_matrix_wellknown svc_comms_matrix_wellknown
wl_comms_matrix_wellknown wl_comms_matrix_wellknown
@ -222,8 +247,12 @@ flowchart LR
wl_health_wger wl_health_wger
end end
subgraph hermes[hermes] subgraph hermes[hermes]
svc_hermes_hermes svc_hermes_oauth2_proxy_hermes_agent
wl_hermes_hermes 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 end
subgraph jellyfin[jellyfin] subgraph jellyfin[jellyfin]
svc_jellyfin_pegasus 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 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 upgrade as previously recorded: `junit 1369.v15da_00283f06` runs on core
2.528.3, only the latest release requires 2.533. 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 -> Ariadne calls the Hermes Agent API (/v1/runs) with
$triage-titan-test-failures $triage-titan-test-failures
-> Hermes returns schema-valid diagnosis + requested_action -> Hermes returns schema-valid diagnosis + requested_action
-> Ariadne authorizes (nine gates) and, if remediation is enabled, -> Ariadne authorizes (twelve gates) and, if remediation is enabled,
creates Job hermes-demo-repair-<build> (writes "healthy") patches the fixture ConfigMap back to "healthy" in process
-> Ariadne triggers ONE rebuild with SEED_FAILURE=false -> Ariadne triggers ONE rebuild with SEED_FAILURE=false
-> rebuild passes -> incident resolved -> 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 ## Arming the demo
Jenkins UI: `https://ci.bstein.dev/job/hermes-triage-demo/` → *Build with 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. 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 ## Expected timings (measured live 2026-08-06, Ariadne 0.1.0-402)
if the Jenkins agent capacity of 5 is busy with other CI builds).
- Red → repair Job succeeded: **1m15s** (≤60 s detection tick + ~16 s Time the demo from the moment the build goes **red**, not from arming. Arming
Hermes diagnosis + authorization + repair pod). only queues a Jenkins build, and that leg is at the mercy of the agent pool.
- Repair → rebuild green: **32s**.
- Green → incident resolved: **24s** (next detection tick). - Red → fixture patched back to healthy: **25s** (≤60 s detection tick, ~16 s
- Total armed-to-resolved: **4m00s**. Budgeted Hermes timeout is 420 s; Hermes diagnosis, then a single in-process Kubernetes API call).
observed diagnosis runs were 1521 s on gpt-5.6-terra. The only "silent" - Red → rebuild triggered: **38s**.
phases are the two Jenkins agent-pod waits. - 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 ## 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); `hermes-demo-test-<N>` (failed) and `hermes-demo-repair-<N>` (succeeded);
both TTL-clean after 1 h. both TTL-clean after 1 h.
- Hermes side: the run appears in the dashboard at - Hermes side: the run appears in the dashboard at
`https://agent.bstein.dev` (session/run history). `https://agent.hermes.bstein.dev` (session/run history).
- Escalation path: alert `HermesTriageHumanRequired` in vmalert - Escalation path: alert `HermesTriageHumanRequired` in vmalert
(`vmalert-atlas-availability` deployment, 1 m interval, `for: 2m`) (`vmalert-atlas-availability` deployment, 1 m interval, `for: 2m`)
fires to Alertmanager for any `human_required` incident. Gauges are fires to Alertmanager for any `human_required` incident. Gauges are

View File

@ -107,21 +107,11 @@ data:
home_mode: auto home_mode: auto
approvals: approvals:
# This owner-only workspace is already bounded by non-root execution, # The owner workspace deliberately runs unattended. Cluster mutations are
# repository scope, and read-only cluster RBAC. Keep routine engineering # available, but durable changes still belong in the Git/Flux source of
# work non-interactive while retaining explicit hard denies below. # truth. Retain only history-destroying Git hard denies.
mode: "off" mode: "off"
deny: 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 --force*"
- "*git push -f*" - "*git push -f*"
- "*git reset --hard*" - "*git reset --hard*"
@ -248,11 +238,13 @@ data:
objective's difficulty warrants it; otherwise synthesize at the original objective's difficulty warrants it; otherwise synthesize at the original
effort. effort.
This pod has Cassandra-scoped, read-only Kubernetes access for workloads, This owner-only pod has cluster-admin access across Atlas, including logs,
logs, events, networking, and rollout diagnosis. It cannot read Secrets, Secrets, exec, port-forwarding, rollout operations, and Flux reconciliation.
exec into pods, or mutate the cluster. Triage belongs at Prefer the titan-iac Git/Flux workflow for every durable cluster change;
triage.hermes.bstein.dev and all Atlas changes are delivered through the direct operations are available for explicit operator requests, incident
titan-iac Git/Flux workflow. 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: | START-HERE.md: |
# Agent Hermes # Agent Hermes
@ -267,4 +259,6 @@ data:
for automatic routing, or `/route manual <codex|claude> for automatic routing, or `/route manual <codex|claude>
<low|medium|high|xhigh> [model]` for a persistent override. The first native <low|medium|high|xhigh> [model]` for a persistent override. The first native
Codex worker requires one device-code login; subsequent sessions persist on 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/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/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/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/agent-inject: "true"
vault.hashicorp.com/role: hermes-agent vault.hashicorp.com/role: hermes-agent
vault.hashicorp.com/agent-inject-secret-anthropic-token: kv/data/atlas/hermes/agent-tokens 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" chmod 0755 "${tools}/bin/kubectl.tmp"
mv "${tools}/bin/kubectl.tmp" "${tools}/bin/kubectl" mv "${tools}/bin/kubectl.tmp" "${tools}/bin/kubectl"
fi fi
/bin/sh /opt/coordinator/install_agent_tools.sh
securityContext: securityContext:
allowPrivilegeEscalation: false allowPrivilegeEscalation: false
runAsUser: 10000 runAsUser: 10000
@ -181,6 +182,9 @@ spec:
volumeMounts: volumeMounts:
- name: home - name: home
mountPath: /opt/data mountPath: /opt/data
- name: coordinator
mountPath: /opt/coordinator
readOnly: true
resources: resources:
requests: {cpu: 100m, memory: 256Mi} requests: {cpu: 100m, memory: 256Mi}
limits: {cpu: "1", memory: 1Gi} limits: {cpu: "1", memory: 1Gi}
@ -571,7 +575,7 @@ spec:
name: hermes-auto-router-plugin name: hermes-auto-router-plugin
- name: tmp - name: tmp
emptyDir: emptyDir:
sizeLimit: 256Mi sizeLimit: 4Gi
- name: ttyd-index - name: ttyd-index
emptyDir: emptyDir:
sizeLimit: 2Mi sizeLimit: 2Mi

View File

@ -11,9 +11,9 @@ users:
user: user:
tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
contexts: contexts:
- name: cassandra-readonly - name: atlas-owner
context: context:
cluster: atlas cluster: atlas
user: hermes-agent user: hermes-agent
namespace: cassandra namespace: default
current-context: cassandra-readonly 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 - agent-configmap.yaml
- chat-configmap.yaml - chat-configmap.yaml
- rbac.yaml - rbac.yaml
- agent-rbac.yaml
- pvc.yaml - pvc.yaml
- chat-pvcs.yaml - chat-pvcs.yaml
- model-gate-rbac.yaml - model-gate-rbac.yaml
@ -54,6 +55,7 @@ configMapGenerator:
- hermes_coordinator.py=scripts/hermes_coordinator.py - hermes_coordinator.py=scripts/hermes_coordinator.py
- hermes_model_routing.py=scripts/hermes_model_routing.py - hermes_model_routing.py=scripts/hermes_model_routing.py
- hermes_stt_client.py=scripts/hermes_stt_client.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 - migrate_herdr_state.py=scripts/migrate_herdr_state.py
- patch_hermes_auth.py=scripts/patch_hermes_auth.py - patch_hermes_auth.py=scripts/patch_hermes_auth.py
- patch_ttyd_index.py=scripts/patch_ttyd_index.py - patch_ttyd_index.py=scripts/patch_ttyd_index.py

View File

@ -74,74 +74,11 @@ spec:
ports: ports:
- {protocol: TCP, port: 7681} - {protocol: TCP, port: 7681}
- {protocol: TCP, port: 8787} - {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: 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 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy

View File

@ -10,9 +10,6 @@ from typing import Any
DENIED_COMMANDS: tuple[tuple[str, re.Pattern[str]], ...] = ( 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)), ("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 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)), ("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} Workspace: {workspace}
{handoff} {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. 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_id = str(state["claude_session_id"])
session = ["--resume", session_id] if resume else ["--session-id", session_id] session = ["--resume", session_id] if resume else ["--session-id", session_id]
denied = [ 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 push --force *)",
"Bash(git reset --hard *)", "Bash(git reset --hard *)",
"Bash(git clean -f *)", "Bash(git clean -f *)",

View File

@ -40,7 +40,7 @@ def configure_claude_settings(path: Path) -> None:
permissions = value.get("permissions") permissions = value.get("permissions")
if not isinstance(permissions, dict): if not isinstance(permissions, dict):
permissions = {} permissions = {}
managed_denies = [ legacy_managed_denies = [
"Bash(kubectl apply *)", "Bash(kubectl apply *)",
"Bash(kubectl delete *)", "Bash(kubectl delete *)",
"Bash(kubectl patch *)", "Bash(kubectl patch *)",
@ -51,6 +51,8 @@ def configure_claude_settings(path: Path) -> None:
"Bash(flux suspend *)", "Bash(flux suspend *)",
"Bash(flux resume *)", "Bash(flux resume *)",
"Bash(vault kv *)", "Bash(vault kv *)",
]
managed_denies = [
"Bash(git push --force *)", "Bash(git push --force *)",
"Bash(git reset --hard *)", "Bash(git reset --hard *)",
"Bash(git clean -f *)", "Bash(git clean -f *)",
@ -58,9 +60,13 @@ def configure_claude_settings(path: Path) -> None:
existing_denies = permissions.get("deny") existing_denies = permissions.get("deny")
if not isinstance(existing_denies, list): if not isinstance(existing_denies, list):
existing_denies = [] 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"] = [ permissions["deny"] = [
*[str(item) for item in existing_denies], *existing_denies,
*[item for item in managed_denies if item not in existing_denies], *managed_denies,
] ]
value["permissions"] = permissions 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 "--output-format" in claude and "stream-json" in claude
assert "--json-schema" in claude assert "--json-schema" in claude
assert "--disallowedTools" 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 assert "max" not in claude
@pytest.mark.parametrize( @pytest.mark.parametrize(
"command", "command",
[ [
"kubectl apply -k services/hermes",
"git push --force origin main", "git push --force origin main",
"git reset --hard HEAD~1", "git reset --hard HEAD~1",
"git clean -fd", "git clean -fd",
"flux reconcile kustomization hermes",
"vault kv get kv/atlas/hermes",
], ],
) )
def test_claude_pretool_hook_blocks_hard_denies(command: str): 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(): def test_claude_pretool_hook_allows_normal_engineering():
assert policy.denial_reason("pytest -q testing/tests") is None 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("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): def test_claude_settings_preserve_state_and_install_three_guardrail_layers(tmp_path: Path):
state = tmp_path / ".claude.json" state = tmp_path / ".claude.json"
settings = tmp_path / "settings.json" settings = tmp_path / "settings.json"
state.write_text('{"promptQueueUseCount": 4}\n', encoding="utf-8") 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_state(state)
client_config.configure_claude_settings(settings) 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 state_value["bypassPermissionsModeAccepted"] is True
assert settings_value["theme"] == "dark" assert settings_value["theme"] == "dark"
assert "Bash(git reset --hard *)" in settings_value["permissions"]["deny"] 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] hook = settings_value["hooks"]["PreToolUse"][0]["hooks"][0]
assert "claude_command_policy.py" in hook["command"] 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()) config = yaml.safe_load((HERMES / "agent-kubeconfig.yaml").read_text())
assert config["current-context"] == "cassandra-readonly" assert config["current-context"] == "atlas-owner"
assert config["contexts"][0]["context"]["namespace"] == "cassandra" assert config["contexts"][0]["context"]["namespace"] == "default"
rbac_path = HERMES.parent / "cassandra/hermes-agent-rbac.yaml" rbac_path = HERMES / "agent-rbac.yaml"
documents = [item for item in yaml.safe_load_all(rbac_path.read_text()) if item] 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") binding = next(item for item in documents if item["kind"] == "ClusterRoleBinding")
assert "secrets" not in {resource for rule in role["rules"] for resource in rule["resources"]} assert binding["roleRef"] == {
assert {verb for rule in role["rules"] for verb in rule["verbs"]} == {"get", "list", "watch"} "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(): def test_agent_reconnect_retains_complete_history_and_long_tool_budget():