kubernetes/apps/ai/hermes/app/helmrelease.yaml
1
---
2
# yaml-language-server: $schema=https://raw.githubusercontent.com/bjw-s-labs/helm-charts/main/charts/other/app-template/schemas/helmrelease-helm-v2.schema.json
3
apiVersion: helm.toolkit.fluxcd.io/v2
4
kind: HelmRelease
5
metadata:
6
name: hermes
7
spec:
8
chartRef:
9
kind: OCIRepository
10
name: hermes
11
interval: 15m
12
timeout: 20m
13
values:
14
defaultPodOptions:
15
automountServiceAccountToken: true
16
terminationGracePeriodSeconds: 120
17
securityContext:
18
fsGroup: 1000
19
fsGroupChangePolicy: OnRootMismatch
20
runAsGroup: 1000
21
runAsUser: 1000
22
seccompProfile:
23
type: RuntimeDefault
24
controllers:
25
hermes:
26
annotations:
27
reloader.stakater.com/auto: "true"
28
strategy: Recreate
29
type: deployment
30
initContainers:
31
copy-agent-source:
32
image:
33
repository: nousresearch/hermes-agent
34
tag: v2026.8.16.2@sha256:a39fc11620213e3669a327aff5c6cb1eb2b8a238c6044e33e7ef8885833d89a7
35
command:
36
- /bin/sh
37
- -c
38
- |
39
set -e
40
cp -r /opt/hermes/. /agent-src/ 2>/tmp/cp.err || true
41
if grep -v 'Permission denied' /tmp/cp.err; then exit 1; fi
42
test -e /agent-src/.venv
43
securityContext:
44
allowPrivilegeEscalation: false
45
capabilities:
46
drop:
47
- ALL
48
runAsGroup: 1000
49
runAsUser: 1000
50
init:
51
image:
52
repository: docker.io/library/alpine
53
tag: "3.24"
54
command:
55
- sh
56
- -c
57
- |
58
set -euo pipefail
59
mkdir -p /opt/data
60
cp /run/config/config.yaml /opt/data/config.yaml
61
chmod 600 /opt/data/config.yaml
62
mkdir -p /opt/data/hook-scripts
63
cp /run/config/home-ops-guard.py /opt/data/hook-scripts/home-ops-guard.py
64
chmod 644 /opt/data/hook-scripts/home-ops-guard.py
65
mkdir -p /opt/data/.local/bin
66
cp /run/config/matrix-post.py /opt/data/.local/bin/matrix-post
67
chmod 755 /opt/data/.local/bin/matrix-post
68
cp /run/config/weekly-digest.py /opt/data/.local/bin/weekly-digest
69
chmod 755 /opt/data/.local/bin/weekly-digest
70
mkdir -p /opt/data/scripts
71
cp /run/config/weekly-digest-run.sh /opt/data/scripts/weekly-digest-run.sh
72
chmod 755 /opt/data/scripts/weekly-digest-run.sh
73
securityContext:
74
allowPrivilegeEscalation: false
75
capabilities:
76
drop:
77
- ALL
78
runAsGroup: 1000
79
runAsUser: 1000
80
install-tools:
81
image:
82
repository: nousresearch/hermes-agent
83
tag: v2026.8.16.2@sha256:a39fc11620213e3669a327aff5c6cb1eb2b8a238c6044e33e7ef8885833d89a7
84
command:
85
- /bin/sh
86
- -c
87
- |
88
set -e
89
mkdir -p /opt/data/.local/bin /opt/data/.local/go
91
rm -f /opt/data/.local/bin/hermes /opt/data/.local/bin/docker
93
if [ ! -f /opt/data/.local/bin/gh ]; then
94
cd /tmp
95
curl -fsSL https://github.com/cli/cli/releases/download/v2.61.0/gh_2.61.0_linux_amd64.tar.gz -o gh.tar.gz
96
tar xzf gh.tar.gz
97
cp gh_2.61.0_linux_amd64/bin/gh /opt/data/.local/bin/
98
rm -rf gh.tar.gz gh_2.61.0_linux_amd64
99
fi
101
if [ ! -f /opt/data/.local/go/bin/go ]; then
102
cd /tmp
103
curl -fsSL https://go.dev/dl/go1.23.5.linux-amd64.tar.gz -o go.tar.gz
104
tar xzf go.tar.gz
105
mv go /opt/data/.local/
106
rm -rf go.tar.gz
107
fi
109
if [ ! -f /opt/data/.local/homebrew/bin/brew ]; then
110
export HOMEBREW_PREFIX=/opt/data/.local/homebrew
111
mkdir -p $HOMEBREW_PREFIX
112
git clone --depth 1 https://github.com/Homebrew/brew $HOMEBREW_PREFIX
113
fi
115
if [ ! -f /opt/data/plugins/memini/plugin.yaml ]; then
116
mkdir -p /opt/data/plugins
117
cd /tmp
118
rm -rf memini-src
119
git clone --depth 1 --branch v0.7.14 https://github.com/eleboucher/memini memini-src
120
mv memini-src/integrations/hermes/plugin/memini /opt/data/plugins/memini
121
rm -rf memini-src
122
fi
124
if [ ! -f /opt/data/.local/bin/shellcheck ]; then
125
cd /tmp
126
curl -fsSL https://github.com/koalaman/shellcheck/releases/download/v0.10.0/shellcheck-v0.10.0.linux.x86_64.tar.xz -o sc.tar.xz
127
tar xJf sc.tar.xz
128
cp shellcheck-v0.10.0/shellcheck /opt/data/.local/bin/
129
rm -rf sc.tar.xz shellcheck-v0.10.0
130
fi
132
if [ ! -f /opt/data/.local/bin/typescript-language-server ]; then
133
npm install -g --prefix /opt/data/.local \
134
typescript@5.7.2 typescript-language-server@4.3.3
135
fi
137
if [ ! -f /opt/data/.local/bin/yaml-language-server ]; then
138
npm install -g --prefix /opt/data/.local \
139
yaml-language-server@1.15.0
140
fi
142
if [ ! -f /opt/data/.local/bin/gopls ]; then
143
GOBIN=/opt/data/.local/bin \
144
GOPATH=/opt/data/go \
145
GOCACHE=/opt/data/.cache/go-build \
146
/opt/data/.local/go/bin/go install golang.org/x/tools/gopls@v0.17.1
147
fi
149
export HOME=/opt/data
150
export HERMES_HOME=/opt/data
151
HERMES=/opt/hermes/.venv/bin/hermes
153
$HERMES kanban init >/dev/null 2>&1 || true
155
if ! $HERMES kanban boards list 2>/dev/null | grep -qw home-ops; then
156
$HERMES kanban boards create home-ops --name "home-ops" || true
157
fi
159
if ! $HERMES project list 2>/dev/null | grep -qw home-ops; then
160
$HERMES project create "home-ops" \
161
/opt/data/workspace/home-ops \
162
/opt/data/workspace/home-ops-sync \
163
--slug home-ops \
164
--board home-ops \
165
--description "Flux-managed homelab cluster. Primary checkout plus the fork-sync clone." || true
166
fi
168
$HERMES bundles create briefing --force \
169
-s morning-briefing -s weather-forecast -s news-curated \
170
-s file-delivery-to-chat \
171
-d "Daily brief: weather, curated news, delivered to chat" || true
173
$HERMES bundles create cluster --force \
174
-s cluster-resource-pressure-investigation \
175
-s homelab-mcp-debugging -s home-ops-flux-app-template \
176
-s flux-operator-stack-debugging -s hermes-cron-debugging \
177
-d "Homelab cluster debugging: resource pressure, Flux, MCP, cron" || true
179
if ! $HERMES cron list 2>/dev/null | grep -q "weekly-digest"; then
180
$HERMES cron create "0 8 * * 1" \
181
--name weekly-digest \
182
--script weekly-digest-run.sh \
183
--no-agent \
184
--deliver local || true
185
fi
186
securityContext:
187
allowPrivilegeEscalation: false
188
capabilities:
189
drop:
190
- ALL
191
runAsGroup: 1000
192
runAsUser: 1000
193
containers:
194
app:
195
image:
196
repository: nousresearch/hermes-agent
197
tag: v2026.8.16.2@sha256:a39fc11620213e3669a327aff5c6cb1eb2b8a238c6044e33e7ef8885833d89a7
198
args:
199
- gateway
200
- run
201
- --no-supervise
202
env:
203
API_SERVER_ENABLED: "true"
204
API_SERVER_HOST: 0.0.0.0
205
API_SERVER_KEY:
206
valueFrom:
207
secretKeyRef:
208
name: hermes-config
209
key: API_SERVER_KEY
210
GOPATH: /opt/data/go
211
HERMES_GID: "1000"
212
HERMES_HOME: /opt/data
213
HERMES_UID: "1000"
214
HERMES_CRON_TIMEOUT: 0
215
HOME: /opt/data
216
HOMEBREW_CELLAR: /opt/data/.local/homebrew/Cellar
217
HOMEBREW_PREFIX: /opt/data/.local/homebrew
218
HOMEBREW_REPOSITORY: /opt/data/.local/homebrew
219
LITELLM_API_KEY:
220
valueFrom:
221
secretKeyRef:
222
name: hermes-config
223
key: LITELLM_API_KEY
224
MEMINI_BASE_URL: http://memini.ai.svc.cluster.local:8080
225
MEMINI_NAMESPACE: homelab
226
MEMINI_HOME: personal/jan
227
MEMINI_API_KEY:
228
valueFrom:
229
secretKeyRef:
230
name: hermes-config
231
key: MEMINI_API_KEY
232
HOMELAB_GH_TOKEN:
233
valueFrom:
234
secretKeyRef:
235
name: hermes-config
236
key: GH_TOKEN
237
PATH: /opt/hermes/.venv/bin:/opt/data/.local/bin:/opt/data/.local/go/bin:/opt/data/.local/homebrew/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
238
PIP_BREAK_SYSTEM_PACKAGES: "1"
239
SEARXNG_URL: http://searxng.selfhosted.svc.cluster.local:8080
240
SUMMARY_LLM_API_KEY:
241
valueFrom:
242
secretKeyRef:
243
name: hermes-config
244
key: LITELLM_API_KEY
245
SUMMARY_LLM_MODEL: opencode-go/hy3
246
SUMMARY_LLM_URL: http://litellm.ai:4000/v1
247
TERM: xterm-256color
248
TERMINAL_MAX_FOREGROUND_TIMEOUT: "1800"
249
HERMES_DASHBOARD: "1"
250
HERMES_DASHBOARD_PUBLIC_URL: https://hermes.janpuc.com
251
HERMES_DASHBOARD_OIDC_ISSUER: https://id.janpuc.com
252
envFrom:
253
- secretRef:
254
name: "{{ .Release.Name }}-oidc-credentials"
255
- secretRef:
256
name: hermes-matrix-access
257
resources:
258
requests:
259
cpu: 500m
260
memory: 1Gi
261
limits:
262
memory: 4Gi
263
securityContext:
264
allowPrivilegeEscalation: false
265
capabilities:
266
add:
267
- CHOWN
268
- DAC_OVERRIDE
269
- FOWNER
270
- FSETID
271
- SETGID
272
- SETUID
273
drop:
274
- ALL
275
readOnlyRootFilesystem: false
276
runAsGroup: 0
277
runAsNonRoot: false
278
runAsUser: 0
279
codeserver:
280
image:
281
repository: ghcr.io/coder/code-server
282
tag: 4.135.0@sha256:ccd326184d71efc5ebb94155eda7bb30153902342a35dbc8525450d81aa55012
283
args:
284
- --auth
285
- none
286
- --user-data-dir
287
- /opt/data/.vscode
288
- --extensions-dir
289
- /opt/data/.vscode
290
- --port
291
- "12321"
292
- /opt/data
293
env:
294
GH_TOKEN:
295
valueFrom:
296
secretKeyRef:
297
name: hermes-config
298
key: GH_TOKEN
299
resources:
300
requests:
301
cpu: 10m
302
limits:
303
memory: 1Gi
304
securityContext:
305
allowPrivilegeEscalation: false
306
capabilities:
307
drop:
308
- ALL
309
service:
310
app:
311
controller: hermes
312
ipFamilies:
313
- IPv4
314
ports:
315
codeserver:
316
port: 12321
317
gateway:
318
port: 8642
319
dashboard:
320
port: 9119
321
route:
322
app:
323
hostnames:
324
- hermes.janpuc.com
325
parentRefs:
326
- name: envoy-internal
327
namespace: network
328
rules:
329
- backendRefs:
330
- identifier: app
331
port: 9119
332
codeserver:
333
hostnames:
334
- hermes-code.janpuc.com
335
parentRefs:
336
- name: envoy-internal
337
namespace: network
338
rules:
339
- backendRefs:
340
- identifier: app
341
port: 12321
342
persistence:
343
agent-source:
344
type: emptyDir
345
advancedMounts:
346
hermes:
347
copy-agent-source:
348
- path: /agent-src
349
install-tools:
350
- path: /opt/hermes
351
app:
352
- path: /opt/hermes
353
data:
354
existingClaim: hermes
355
advancedMounts:
356
hermes:
357
init:
358
- path: /opt/data
359
install-tools:
360
- path: /opt/data
361
app:
362
- path: /opt/data
363
codeserver:
364
- path: /opt/data
365
config-yaml:
366
type: configMap
367
name: hermes-configmap
368
defaultMode: 0o755
369
advancedMounts:
370
hermes:
371
init:
372
- path: /run/config/config.yaml
373
subPath: config.yaml
374
- path: /run/config/home-ops-guard.py
375
subPath: home-ops-guard.py
376
- path: /run/config/matrix-post.py
377
subPath: matrix-post.py
378
- path: /run/config/weekly-digest.py
379
subPath: weekly-digest.py
380
- path: /run/config/weekly-digest-run.sh
381
subPath: weekly-digest-run.sh
382
config:
383
type: secret
384
name: hermes-config
385
defaultMode: 0o400
386
advancedMounts:
387
hermes:
388
init:
389
- path: /run/config/env
390
subPath: env
391
run:
392
type: emptyDir
393
advancedMounts:
394
hermes:
395
app:
396
- path: /run
397
tmp:
398
type: emptyDir
399
globalMounts:
400
- path: /tmp
401
rbac:
402
roles:
403
hermes-read-all:
404
type: ClusterRole
405
rules:
406
- apiGroups:
407
- ""
408
resources:
409
- pods
410
- pods/log
411
- nodes
412
- services
413
- namespaces
414
- configmaps
415
- persistentvolumes
416
- persistentvolumeclaims
417
- endpoints
418
- resourcequotas
419
- limitranges
420
- serviceaccounts
421
- events
422
verbs:
423
- "get"
424
- "list"
425
- "watch"
426
- apiGroups:
427
- "apps"
428
resources:
429
- deployments
430
- daemonsets
431
- statefulsets
432
- replicasets
433
- controllerrevisions
434
verbs:
435
- "get"
436
- "list"
437
- "watch"
438
- apiGroups:
439
- "batch"
440
resources:
441
- "jobs"
442
- "cronjobs"
443
verbs:
444
- "get"
445
- "list"
446
- "watch"
447
- apiGroups:
448
- "networking.k8s.io"
449
resources:
450
- "ingresses"
451
- "ingressclasses"
452
- "networkpolicies"
453
verbs:
454
- "get"
455
- "list"
456
- "watch"
457
- apiGroups:
458
- "storage.k8s.io"
459
resources:
460
- storageclasses
461
- csinodes
462
- csidrivers
463
- volumeattachments
464
verbs:
465
- "get"
466
- "list"
467
- "watch"
468
- apiGroups:
469
- "helm.toolkit.fluxcd.io"
470
resources:
471
- "helmreleases"
472
verbs:
473
- "get"
474
- "list"
475
- "watch"
476
- apiGroups:
477
- "kustomize.toolkit.fluxcd.io"
478
resources:
479
- "kustomizations"
480
verbs:
481
- "get"
482
- "list"
483
- "watch"
484
- apiGroups:
485
- "source.toolkit.fluxcd.io"
486
resources:
487
- gitrepositories
488
- helmrepositories
489
- ocirepositories
490
- buckets
491
- helmcharts
492
verbs:
493
- "get"
494
- "list"
495
- "watch"
496
- apiGroups:
497
- "external-secrets.io"
498
resources:
499
- externalsecrets
500
- secretstores
501
- clustersecretstores
502
verbs:
503
- "get"
504
- "list"
505
- "watch"
506
- apiGroups:
507
- "miroir.home-operations.com"
508
resources:
509
- miroirvolumes
510
- miroirnodes
511
- miroirnodegroups
512
- miroirsnapshots
513
- miroirsnapshotgroups
514
verbs:
515
- "get"
516
- "list"
517
- "watch"
518
- apiGroups:
519
- "kopiur.home-operations.com"
520
resources:
521
- snapshots
522
- snapshotpolicies
523
- snapshotschedules
524
- restores
525
- repositories
526
- clusterrepositories
527
verbs:
528
- "get"
529
- "list"
530
- "watch"
531
- apiGroups:
532
- "cert-manager.io"
533
resources:
534
- certificates
535
- certificaterequests
536
- issuers
537
- clusterissuers
538
verbs:
539
- "get"
540
- "list"
541
- "watch"
542
- apiGroups:
543
- "metrics.k8s.io"
544
resources:
545
- nodes
546
- pods
547
verbs:
548
- "get"
549
- "list"
550
hermes-pod-delete:
551
type: ClusterRole
552
rules:
553
- apiGroups:
554
- ""
555
resources:
556
- "pods"
557
verbs:
558
- "delete"
559
hermes-exec-deploy:
560
type: Role
561
rules:
562
- apiGroups:
563
- ""
564
resources:
565
- "pods/exec"
566
verbs:
567
- "create"
568
- apiGroups:
569
- "apps"
570
resources:
571
- "deployments"
572
verbs:
573
- "patch"
574
- "update"
575
bindings:
576
hermes-read-all:
577
type: ClusterRoleBinding
578
roleRef:
579
identifier: hermes-read-all
580
subjects:
581
- identifier: hermes
582
serviceAccount: hermes
583
hermes-pod-delete:
584
type: ClusterRoleBinding
585
roleRef:
586
identifier: hermes-pod-delete
587
subjects:
588
- identifier: hermes
589
serviceAccount: hermes
590
hermes-exec-deploy:
591
type: RoleBinding
592
roleRef:
593
identifier: hermes-exec-deploy
594
subjects:
595
- identifier: hermes
596
serviceAccount: hermes
597
namespace: ai
598
serviceAccount:
599
hermes: {}