fix(hermes): restore isolated user features
Some checks failed
Tests / Declarative: Post Actions failed: 2, passed: 153
Some checks failed
Tests / Declarative: Post Actions failed: 2, passed: 153
This commit is contained in:
parent
cd1fb62d0f
commit
5159c98d64
@ -29,8 +29,8 @@ data:
|
||||
enabled: true
|
||||
ttl_hours: 1
|
||||
platform_toolsets:
|
||||
cli: [clarify, session_search, web]
|
||||
api_server: [clarify, session_search, web]
|
||||
cli: [clarify, file, memory, session_search, skills, todo, web]
|
||||
api_server: [clarify, file, memory, session_search, skills, todo, web]
|
||||
dashboard:
|
||||
public_url: https://chat.hermes.bstein.dev
|
||||
display:
|
||||
@ -43,15 +43,17 @@ data:
|
||||
with questions, writing, research, planning, and learning. Be direct,
|
||||
thoughtful, and careful. Use public web research when freshness matters.
|
||||
|
||||
This is a personal sandbox. Never attempt cluster administration, private
|
||||
service access, repository modification, terminal execution, credentials,
|
||||
or coordination of Brad's project agents. Those capabilities are not part
|
||||
of this chat product. The user's conversations and files must never be
|
||||
mixed with another Keycloak user's state.
|
||||
This is a personal sandbox. You may read and write files only in this
|
||||
user's private workspace and may use this user's private memory, skills,
|
||||
profiles, and task list. Never attempt cluster administration, private
|
||||
service access, terminal execution, credentials, or coordination of Brad's
|
||||
project agents. The user's conversations and files must never be mixed with
|
||||
another Keycloak user's state.
|
||||
AGENTS.md: |
|
||||
# Private Hermes chat
|
||||
|
||||
This runtime belongs to one authenticated Keycloak identity and one private
|
||||
persistent volume. Provide conversational help with clarify, session search,
|
||||
and public web tools only. Do not claim access to Kubernetes, Vault, Gitea,
|
||||
Brad's projects, other users, the agent coordinator, or automated triage.
|
||||
persistent volume. Provide conversational help with the private workspace,
|
||||
memory, skills, profiles, task list, session search, and public web tools.
|
||||
Do not claim access to Kubernetes, Vault, Gitea, Brad's projects, other
|
||||
users, the agent coordinator, or automated triage.
|
||||
|
||||
61
services/hermes/chat-pvcs.yaml
Normal file
61
services/hermes/chat-pvcs.yaml
Normal file
@ -0,0 +1,61 @@
|
||||
# services/hermes/chat-pvcs.yaml
|
||||
# Existing tenant claims are explicit so Flux can expand them with the template.
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: home-hermes-chat-tenant-0
|
||||
namespace: hermes
|
||||
labels:
|
||||
app: hermes-chat-tenant
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: astreae
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: home-hermes-chat-tenant-1
|
||||
namespace: hermes
|
||||
labels:
|
||||
app: hermes-chat-tenant
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: astreae
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: home-hermes-chat-tenant-2
|
||||
namespace: hermes
|
||||
labels:
|
||||
app: hermes-chat-tenant
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: astreae
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: home-hermes-chat-tenant-3
|
||||
namespace: hermes
|
||||
labels:
|
||||
app: hermes-chat-tenant
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: astreae
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
@ -20,7 +20,7 @@ spec:
|
||||
app: hermes-chat-router
|
||||
annotations:
|
||||
ai.bstein.dev/role: privacy-preserving-chat-tenant-router
|
||||
ai.bstein.dev/config-rev: "20260808-webui-telegram-router-image"
|
||||
ai.bstein.dev/config-rev: "20260808-tenant-personalization"
|
||||
vault.hashicorp.com/agent-inject: "true"
|
||||
vault.hashicorp.com/agent-pre-populate-only: "true"
|
||||
vault.hashicorp.com/agent-init-first: "true"
|
||||
@ -62,7 +62,7 @@ spec:
|
||||
values: [rpi5]
|
||||
containers:
|
||||
- name: router
|
||||
image: registry.bstein.dev/bstein/hermes-chat-router@sha256:ccb3a7c7ceba317153ff64455777459a5cfd9497bc0e6f51687feedfbfd6f4ed
|
||||
image: registry.bstein.dev/bstein/hermes-chat-router@sha256:d6b938bbde0a229b73dd2f1fc5c4607ea9391b21278a2ae0e43c135b83587534
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- {name: http, containerPort: 8080, protocol: TCP}
|
||||
|
||||
@ -6,10 +6,15 @@ metadata:
|
||||
namespace: hermes
|
||||
labels:
|
||||
app: hermes-chat-tenant
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/force: enabled
|
||||
spec:
|
||||
serviceName: hermes-chat-tenant
|
||||
replicas: 4
|
||||
podManagementPolicy: Parallel
|
||||
persistentVolumeClaimRetentionPolicy:
|
||||
whenDeleted: Retain
|
||||
whenScaled: Retain
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
selector:
|
||||
@ -116,6 +121,7 @@ spec:
|
||||
mv /opt/data/.env.tmp /opt/data/.env
|
||||
fi
|
||||
fi
|
||||
rm -f /vault/secrets/anthropic-token /vault/secrets/chat-relay-key
|
||||
chmod 0600 /opt/data/.env
|
||||
chown -R 10000:10000 /opt/data
|
||||
securityContext:
|
||||
@ -160,8 +166,10 @@ spec:
|
||||
- {name: api, containerPort: 8642, protocol: TCP}
|
||||
env:
|
||||
- {name: HERMES_HOME, value: /opt/data}
|
||||
- {name: HERMES_AUTH_FILE, value: /shared-auth/auth.json}
|
||||
- {name: HERMES_AUTH_FILE, value: /opt/data/auth.json}
|
||||
- {name: HOME, value: /opt/data/home}
|
||||
- {name: TERMINAL_CWD, value: /opt/data/workspace}
|
||||
- {name: HERMES_WRITE_SAFE_ROOT, value: /opt/data/workspace}
|
||||
- {name: PATH, value: /opt/data/home/.local/bin:/opt/hermes/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin}
|
||||
- {name: HERMES_DASHBOARD, value: "0"}
|
||||
- {name: API_SERVER_ENABLED, value: "true"}
|
||||
@ -170,7 +178,7 @@ spec:
|
||||
- {name: API_SERVER_CORS_ORIGINS, value: https://chat.hermes.bstein.dev}
|
||||
volumeMounts:
|
||||
- {name: home, mountPath: /opt/data}
|
||||
- {name: provider-auth, mountPath: /shared-auth}
|
||||
- {name: provider-auth, mountPath: /opt/data/auth.json, subPath: auth.json, readOnly: true}
|
||||
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py}
|
||||
readinessProbe:
|
||||
tcpSocket: {port: api}
|
||||
@ -206,7 +214,7 @@ spec:
|
||||
- {name: webui, containerPort: 8787, protocol: TCP}
|
||||
env:
|
||||
- {name: HERMES_HOME, value: /opt/data}
|
||||
- {name: HERMES_AUTH_FILE, value: /shared-auth/auth.json}
|
||||
- {name: HERMES_AUTH_FILE, value: /opt/data/auth.json}
|
||||
- {name: HOME, value: /opt/data/home}
|
||||
- {name: HERMES_WEBUI_AGENT_DIR, value: /opt/hermes}
|
||||
- {name: HERMES_WEBUI_HOST, value: 0.0.0.0}
|
||||
@ -223,7 +231,7 @@ spec:
|
||||
- {name: HERMES_WEBUI_TRUST_FORWARDED_PROTO, value: "1"}
|
||||
volumeMounts:
|
||||
- {name: home, mountPath: /opt/data}
|
||||
- {name: provider-auth, mountPath: /shared-auth, readOnly: true}
|
||||
- {name: provider-auth, mountPath: /opt/data/auth.json, subPath: auth.json, readOnly: true}
|
||||
- {name: tmp, mountPath: /tmp}
|
||||
readinessProbe:
|
||||
httpGet: {path: /health, port: webui}
|
||||
@ -273,4 +281,4 @@ spec:
|
||||
storageClassName: astreae
|
||||
resources:
|
||||
requests:
|
||||
storage: 2Gi
|
||||
storage: 10Gi
|
||||
|
||||
@ -10,6 +10,7 @@ resources:
|
||||
- chat-configmap.yaml
|
||||
- rbac.yaml
|
||||
- pvc.yaml
|
||||
- chat-pvcs.yaml
|
||||
- model-gate-rbac.yaml
|
||||
- ariadne-handoff-rbac.yaml
|
||||
- model-gate-state.yaml
|
||||
|
||||
@ -45,7 +45,7 @@ spec:
|
||||
matchLabels:
|
||||
app: oauth2-proxy-hermes-triage
|
||||
ports:
|
||||
- {protocol: TCP, port: 9119}
|
||||
- {protocol: TCP, port: 8787}
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
@ -72,7 +72,7 @@ spec:
|
||||
matchLabels:
|
||||
app: oauth2-proxy-hermes-agent
|
||||
ports:
|
||||
- {protocol: TCP, port: 9119}
|
||||
- {protocol: TCP, port: 8787}
|
||||
egress:
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
@ -310,5 +310,5 @@ spec:
|
||||
operator: In
|
||||
values: [hermes, hermes-agent, hermes-chat-router]
|
||||
ports:
|
||||
- {protocol: TCP, port: 9119}
|
||||
- {protocol: TCP, port: 8787}
|
||||
- {protocol: TCP, port: 8080}
|
||||
|
||||
@ -96,7 +96,7 @@ spec:
|
||||
- --cookie-samesite=lax
|
||||
- --cookie-refresh=1h
|
||||
- --cookie-expire=8h
|
||||
- --upstream=http://hermes-agent.hermes.svc.cluster.local:9119
|
||||
- --upstream=http://hermes-agent.hermes.svc.cluster.local:8787
|
||||
- --http-address=0.0.0.0:4180
|
||||
- --skip-provider-button=true
|
||||
- --reverse-proxy=true
|
||||
@ -186,7 +186,7 @@ spec:
|
||||
- --cookie-samesite=lax
|
||||
- --cookie-refresh=1h
|
||||
- --cookie-expire=8h
|
||||
- --upstream=http://hermes-triage.hermes.svc.cluster.local:9119
|
||||
- --upstream=http://hermes-triage.hermes.svc.cluster.local:8787
|
||||
- --http-address=0.0.0.0:4180
|
||||
- --skip-provider-button=true
|
||||
- --reverse-proxy=true
|
||||
|
||||
@ -50,9 +50,9 @@ var deniedPrefixes = []string{
|
||||
"/api/cron", "/api/curator", "/api/dashboard/config", "/api/env",
|
||||
"/api/escape", "/api/extensions", "/api/file", "/api/folder",
|
||||
"/api/gateway", "/api/git", "/api/git-info", "/api/health/restart", "/api/kanban", "/api/logs", "/api/mcp",
|
||||
"/api/memory", "/api/messaging", "/api/notes", "/api/ops",
|
||||
"/api/oauth", "/api/onboarding/oauth", "/api/pairing", "/api/plugins", "/api/profiles", "/api/providers",
|
||||
"/api/rollback", "/api/shutdown", "/api/skills", "/api/terminal",
|
||||
"/api/messaging", "/api/notes", "/api/ops",
|
||||
"/api/oauth", "/api/onboarding/oauth", "/api/pairing", "/api/plugins", "/api/providers",
|
||||
"/api/rollback", "/api/shutdown", "/api/terminal",
|
||||
"/api/tools", "/api/updates", "/api/webhooks", "/api/wiki",
|
||||
"/api/workspace", "/api/workspaces",
|
||||
}
|
||||
|
||||
@ -95,6 +95,33 @@ func TestRouterProxiesWebUIAndAddsTelegramShortcut(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouterAllowsTenantScopedPersonalization(t *testing.T) {
|
||||
backend := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||
writer.WriteHeader(http.StatusNoContent)
|
||||
}))
|
||||
defer backend.Close()
|
||||
router, err := newTenantRouter(filepath.Join(t.TempDir(), "state.json"), 1, func(slot int) string { return backend.URL })
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, path := range []string{
|
||||
"/api/memory",
|
||||
"/api/memory/write",
|
||||
"/api/skills",
|
||||
"/api/skills/save",
|
||||
"/api/profiles",
|
||||
"/api/profile/create",
|
||||
} {
|
||||
request := httptest.NewRequest(http.MethodGet, path, nil)
|
||||
request.Header.Set("X-Forwarded-User", "subject")
|
||||
response := httptest.NewRecorder()
|
||||
router.ServeHTTP(response, request)
|
||||
if response.Code != http.StatusNoContent {
|
||||
t.Fatalf("GET %s: got %d, want %d", path, response.Code, http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebUIModelAndReasoningOverridesAreProxied(t *testing.T) {
|
||||
backend := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||
if request.Method != http.MethodPost {
|
||||
|
||||
@ -36,7 +36,7 @@ spec:
|
||||
port: 8642
|
||||
targetPort: api
|
||||
- name: dashboard
|
||||
port: 9119
|
||||
port: 8787
|
||||
targetPort: dashboard
|
||||
---
|
||||
apiVersion: v1
|
||||
@ -52,7 +52,7 @@ spec:
|
||||
app: hermes-agent
|
||||
ports:
|
||||
- name: dashboard
|
||||
port: 9119
|
||||
port: 8787
|
||||
targetPort: dashboard
|
||||
---
|
||||
apiVersion: v1
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user