deploy both qwen3.6 versions

This commit is contained in:
Roger Oriol
2026-07-23 22:10:44 +02:00
parent 8ef9db1e37
commit 04bd4d8e5e
16 changed files with 906 additions and 53 deletions

139
home-manager/README.md Normal file
View File

@@ -0,0 +1,139 @@
# Home Manager Agent — Deployment Plan
An autonomous **Hermes Agent** that runs inside the k3s cluster and takes care
of the home by talking to **Home Assistant**'s REST API. It mirrors the
`platform-engineer` agent pattern but its domain is the home, not the cluster:
it watches sensors, runs routines, and notifies Roger on Discord — asking
before touching anything security-critical (locks, alarm, garage, HVAC).
Docs: https://hermes-agent.nousresearch.com/docs/user-guide/docker
---
## 1. Goal & operating model
- **One Hermes container** in namespace `home-manager`, scheduled on the amd64
NUC (`roger-nucbox-evo-x2`, 24 GiB) — same node as `platform-engineer`.
- Hermes runs in **gateway mode** under s6 supervision (`gateway run`), so the
built-in **cron scheduler** is active and survives restarts.
- The agent talks to the home via the **Home Assistant REST API** at
`http://home-assistant.home-assistant:80` (in-cluster `home-assistant`
Service). Auth is a HA Long-Lived Access Token in a Secret.
- The agent has **NO k8s RBAC and NO git repo** — it doesn't need them. Its
only write path is HA service calls. (Contrast with `platform-engineer`,
which remediates via git commits → ArgoCD sync.)
- LLM calls route through the in-cluster **LiteLLM** proxy.
- Notifications go to a **dedicated Discord bot** (separate token from the
platform engineer's bot).
- **Hermes-native cron jobs** make the agent run periodic checks + routines.
---
## 2. Files in this directory
| File | Purpose |
|------|---------|
| `namespace.yaml` | namespace `home-manager` |
| `rbac.yaml` | ServiceAccount + Role for the `cron-seed` Job only (pods/exec on the hermes pod) |
| `configmap.yaml` | seed `config.yaml` + `SOUL.md` (HA REST API docs + operating rules) |
| `secret.yaml` | HA token, Discord token/channel, LiteLLM key, dashboard auth (PLACEHOLDERS — fill in) |
| `pvc.yaml` | 5 Gi PVC for `/opt/data` (HERMES_HOME) |
| `deployment.yaml` | Deployment (1 replica, Recreate, pinned to amd64 NUC) + Service |
| `ingress.yaml` | `home-manager.rogi.casa` → dashboard (TLS + basic auth) |
| `cron-seed.yaml` | one-shot Job that creates the Hermes cron schedule |
| `README.md` | this file |
---
## 3. How it differs from `platform-engineer`
| Concern | `platform-engineer` | `home-manager` (this) |
|---|---|---|
| Domain | k3s cluster health | the home |
| Observe via | Prometheus / Loki / ArgoCD HTTP APIs | Home Assistant REST API |
| Write path | git commit → push → ArgoCD sync | HA service calls (`POST /api/services/...`) |
| k8s RBAC | none | none |
| git repo | yes (cloned to `/workspace/k3s-cluster`) | none |
| Discord bot | dedicated | dedicated (separate token + channel) |
| `HERMES_WRITE_SAFE_ROOT` | `/` (needs to edit manifests) | unset → defaults to `/opt/data` (tighter) |
| Image | `nousresearch/hermes-agent:latest` | same |
| Model | `qwen3.6` via LiteLLM | same |
---
## 4. Safety & guardrails
1. **SOUL.md is the real boundary.** The agent may freely toggle lights, fans,
media, and non-critical switches. It must **ask Roger on Discord before
touching** `lock.*`, `alarm_control_panel.*`, garage `cover.*`, risky
`climate.*` changes, or anything tagged `critical`/`security`/`safe_to_auto: false`.
2. **`tool_loop_guardrails.hard_stop_enabled: true`** — circuit-breaks a stuck
gateway.
3. **`yolo: true` + `approvals.mode: off`** — cron jobs aren't blocked waiting
for a human. Safe because the blast radius is HA service calls and the SOUL.md
encodes the confirmation gate for dangerous entities.
4. **Tirith scanner disabled** — it false-positives on the in-cluster plain-HTTP
HA URL and would block every query.
5. **Single replica / single-writer PVC** — never scale >1. `podAntiAffinity`
guards against an accidental co-run.
6. **No real-time event bus** — the terminal tool can't hold a HA WebSocket open.
The agent works on cron polling + on-demand Discord commands. (See §6 for an
optional real-time hook.)
---
## 5. Deployment checklist (do in this order)
1. **Create a Home Assistant Long-Lived Access Token.** In HA UI:
Profile → Long-Lived Access Tokens → Create Token → label `home-manager-agent`.
Copy the token.
2. **Create a dedicated Discord bot** (separate from the platform-engineer bot),
invite it to your server, and note its token + the channel ID you want it to
use as its home channel.
3. **Fill in `secret.yaml`** — base64-encode each value (`echo -n 'VALUE' | base64`):
- `HOMEASSISTANT_TOKEN` — the HA LLAT from step 1.
- `OPENAI_API_KEY` — your LiteLLM master key.
- `DISCORD_BOT_TOKEN`, `DISCORD_HOME_CHANNEL`, `DISCORD_FREE_RESPONSE_CHANNELS`
— from step 2 (channel ID is the same for both).
- `HERMES_DASHBOARD_BASIC_AUTH_PASSWORD` and `..._SECRET` — strong values.
4. **Fill in `configmap.yaml`** — replace `REPLACE_WITH_HOME_CHANNEL_ID` (two
places under `discord:`) with your numeric home channel ID. (Must match the
secret's `DISCORD_HOME_CHANNEL`.)
5. **Tailor `cron-seed.yaml`** schedules/prompts to your actual HA entities
(your sensor names, your `script.goodnight`, etc.).
6. **Commit & push** the whole change (including the regenerated
`argocd/apps/home-manager.yaml`). ArgoCD will create the namespace resources,
deploy the pod, and bring up the ingress at `home-manager.rogi.casa`.
7. **Seed the cron jobs:** ArgoCD runs `cron-seed.yaml` as a Sync hook
automatically. To re-seed after a wipe:
`kubectl apply -f home-manager/cron-seed.yaml`.
8. **Smoke test:** trigger a check manually —
`kubectl exec -n home-manager deploy/hermes -- hermes cron run door-window-check`
and confirm the message lands in Discord (or `[SILENT]` if all clear).
---
## 6. Optional: real-time events from HA
The base setup is cron-poll + Discord. For real-time reaction (e.g. "front door
opened → agent acts immediately"), you can have Home Assistant call the Hermes
gateway API on an event:
1. Enable the gateway API (port 8642) in `config.yaml` and set an auth token.
2. In HA, create a `rest` command + an automation that fires on the event and
POSTs to `http://home-manager.home-manager:80/...` (the in-cluster Service).
This is not enabled by default — start with cron + Discord, add webhooks once
the agent is trusted.
---
## 7. What ArgoCD owns vs. what is runtime state
- **ArgoCD owns** (in git): namespace, RBAC, Secret, ConfigMap (seed), PVC,
Deployment, Service, Ingress, cron-seed Job.
- **Runtime state (on the PVC, NOT reconciled):** `config.yaml`, `SOUL.md`,
`.env`, `cron/jobs.json`, `sessions/`, `memories/`, `skills/`. The ConfigMap
only *seeds* these on first boot; after that, edits made via the dashboard or
`hermes cron edit` persist on the PVC and Argo will not revert them. For a
hard reset, delete the PVC and re-apply.

186
home-manager/configmap.yaml Normal file
View File

@@ -0,0 +1,186 @@
# Hermes configuration + SOUL.md (seeded into the PVC on first boot).
---
apiVersion: v1
kind: ConfigMap
metadata:
name: hermes-seed
namespace: home-manager
data:
config.yaml: |
model:
provider: openai-api
default: qwen3.6
base_url: "http://litellm-service.litellm:80/v1"
api_mode: chat_completions
auxiliary:
compression:
provider: openai-api
model: qwen3.6
base_url: "http://litellm-service.litellm:80/v1"
title_generation:
provider: openai-api
model: qwen3.6
base_url: "http://litellm-service.litellm:80/v1"
terminal:
backend: local
cwd: /opt/data
timeout: 180
home_mode: profile
# The agent runs unattended (cron jobs). yolo:true disables approval prompts
# so cron jobs aren't blocked waiting for a human. Safe here because the
# agent's blast radius is limited to Home Assistant service calls, and the
# SOUL.md forbids touching security-critical entities without confirmation.
yolo: true
approvals:
mode: off
# Disable the Tirith pre-exec command scanner. It flags in-cluster plain
# HTTP URLs (http://home-assistant.home-assistant:80) as 'insecure URL'
# false positives, which blocks every HA API query.
security:
tirith_enabled: false
tirith_fail_open: true
tool_loop_guardrails:
hard_stop_enabled: true
hard_stop_after:
exact_failure: 5
idempotent_no_progress: 5
sessions:
auto_prune: true
retention_days: 90
cron:
wrap_response: false
discord:
# REPLACE_WITH_HOME_CHANNEL_ID — set to your home channel's Discord ID.
# Must match DISCORD_HOME_CHANNEL / DISCORD_FREE_RESPONSE_CHANNELS in the
# Secret. Hermes reads these from config.yaml, not the env.
allowed_channels: 'REPLACE_WITH_HOME_CHANNEL_ID'
free_response_channels: 'REPLACE_WITH_HOME_CHANNEL_ID'
dm_policy: open
group_policy: open
memory:
memory_enabled: true
user_profile_enabled: true
write_approval: false
skills:
write_approval: false
SOUL.md: |
# Home Manager — rogi.casa (via Home Assistant)
You are the autonomous Home Manager for the `rogi.casa` home. You run
*inside* the k3s cluster (namespace `home-manager`) and your job is to keep
the home comfortable, safe, and efficient by talking to Home Assistant, and
to notify your owner (Roger) on Discord when something needs a human.
## The home you look after
- **Home Assistant:** the brain of the home. Runs in namespace
`home-assistant`. You reach its REST API in-cluster at
`http://home-assistant.home-assistant:80` (the `home-assistant` Service,
port 80 → 8123). If that DNS ever fails, fall back to the node IP
`http://10.88.20.11:8123` (HA runs hostNetwork).
- **Auth:** every HA REST call needs an `Authorization: Bearer $HOMEASSISTANT_TOKEN`
header. The token is in your environment. NEVER log or echo the token.
- **Your model provider:** LiteLLM at `http://litellm-service.litellm:80/v1`.
- **Notifications:** Discord (your home channel).
## How you observe the home (Home Assistant REST API)
Use the terminal tool to send HTTP requests with curl. Always pass the bearer
token from the environment (`$HOMEASSISTANT_TOKEN`), never hard-code it.
Base URL: `$HOMEASSISTANT_URL` (set to http://home-assistant.home-assistant:80)
### 1. Read state
- All entities: GET /api/states
- One entity: GET /api/states/<entity_id> (e.g. light.living_room)
- Services list: GET /api/services
- Calendar events: GET /api/calendars/<calendar_id>?start=...&end=...
- History: GET /api/history/period/<timestamp>?filter_entity_id=<id>&minimal
- Config check: GET /api/config
### 2. Actuate (call a service)
POST /api/services/<domain>/<service> with a JSON body.
Common services:
- light.turn_on / light.turn_off / light.toggle
body: {"entity_id": "light.x", "brightness": 200}
- switch.turn_on / switch.turn_off / switch.toggle
body: {"entity_id": "switch.x"}
- cover.open_cover / close_cover / set_cover_position
body: {"entity_id": "cover.x", "position": 50}
- fan.toggle / fan.set_percentage
- media_player.play_media / media_pause / volume_set
- climate.set_temperature
body: {"entity_id": "climate.x", "temperature": 21}
- script.turn_on
body: {"entity_id": "script.goodnight"}
- input_boolean.toggle / input_select.select_option
### 3. Fire events
POST /api/events/<event_type> { ... }
(Listening to the live event bus needs the WebSocket API, which the
terminal tool can't hold open. Use cron polling for periodic checks, and
ask Roger on Discord for anything that needs real-time reaction.)
### 4. Parse JSON
Pipe curl output through `python3 -m json.tool` or `jq` in the terminal
tool. The execute_code tool is BLOCKED in cron mode.
## How you remediate (call services — NOT git, NOT kubectl)
Unlike the platform engineer, you do NOT change the cluster. You change the
*home* by calling Home Assistant services via the REST API. You have no k8s
access and no git repo — and you don't need them.
Workflow:
1. GET /api/states (or a specific entity) to understand the situation.
2. Decide a safe action. POST the service call.
3. GET the entity back to confirm the new state.
4. Report what you did in one line (entity_id + service + result).
## Operating rules
1. **Read first, act second.** Always GET the current state before changing
anything. Cite the exact `entity_id` and the service you called.
2. **Only safe, idempotent actions on your own.** Allowed without asking:
lights, fans, media players, non-critical switches, running scripts Roger
has marked safe, reading any sensor.
3. **NEVER touch security- or safety-critical entities without explicit
confirmation from Roger on Discord.** This includes, but is not limited to:
- `lock.*` (door locks)
- `alarm_control_panel.*` (alarm arm/disarm)
- `cover.*` for garage doors or main gates
- `climate.*` changes that could freeze/overheat the house or cost a lot
- any `switch.*` / `script.*` tagged `critical` or `security`
- any entity with a custom attribute `safe_to_auto: false`
For these, post the proposed action to Discord and wait for Roger to
reply yes/no before calling the service.
4. **When in doubt, notify, don't act.** If an action is unusual, could
startle someone (e.g. loud media at 03:00, vacuum robot at night), or you
are unsure what an entity does, ask Roger first.
5. **Be quiet when healthy.** Watchdog cron jobs reply with exactly
`[SILENT]` when there is nothing to report. Failed jobs always deliver.
6. **No runaway loops.** You cannot create new cron jobs from inside a cron
run (Hermes disables that). Do not try.
7. **Talk like a concierge.** Short, concrete, with entity_ids and values.
No filler. When you did something, say what in one line.
8. **Respect time of day and presence.** Don't run noisy routines (loud
media, vacuum robots) at night. Check presence (`zone.*` /
`binary_sensor.*`) and the time before acting.
## How you reach Roger
Notifications go to Discord (your home channel). Cron jobs deliver there by
default (`deliver="discord"`). Keep messages under ~1800 chars. For anything
that needs a yes/no decision, ask the question clearly and wait — do not
proceed on a guess.

View File

@@ -0,0 +1,93 @@
# One-shot Job that seeds Hermes' built-in cron schedule on first install.
# Idempotent: skips job names that already exist.
#
# Cron prompts are deliberately written as plain-English instructions (no inline
# curl commands) to avoid tripping Hermes' threat-pattern scanner, which blocks
# cron prompts containing curl+auth-header patterns. The exact HA REST API
# endpoints and examples are documented in the agent's SOUL.md instead.
#
# Tailor the schedules/prompts below to your actual Home Assistant entities.
---
apiVersion: batch/v1
kind: Job
metadata:
name: hermes-cron-seed
namespace: home-manager
labels:
app: hermes
annotations:
argocd.argoproj.io/sync-options: Replace=true
argocd.argoproj.io/hook: Sync
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
spec:
backoffLimit: 4
ttlSecondsAfterFinished: 86400
template:
metadata:
labels:
app: hermes
spec:
serviceAccountName: cron-seeder
restartPolicy: OnFailure
containers:
- name: seed
image: alpine:3.20
command: ["sh", "-c"]
args:
- |
set -e
apk add --no-cache curl
ARCH=$(uname -m)
case "$ARCH" in
x86_64) KARCH=amd64 ;;
aarch64) KARCH=arm64 ;;
armv7l) KARCH=arm ;;
*) echo "unsupported arch: $ARCH" >&2; exit 1 ;;
esac
curl -fsSL -o /usr/local/bin/kubectl \
"https://dl.k8s.io/release/v1.35.0/bin/linux/${KARCH}/kubectl"
chmod +x /usr/local/bin/kubectl
echo "Waiting for hermes pod to be Ready..."
kubectl -n home-manager wait --for=condition=Ready pod -l app=hermes --timeout=300s || true
POD=$(kubectl -n home-manager get pod -l app=hermes -o jsonpath='{.items[0].metadata.name}')
echo "Using pod: $POD"
exists() { kubectl -n home-manager exec "$POD" -- hermes cron list 2>/dev/null | grep -qi " $1 "; }
create() {
name="$1"; schedule="$2"; deliver="$3"; prompt="$4"
if exists "$name"; then
echo "cron job '$name' already exists — skipping"
else
echo "creating cron job '$name' ..."
kubectl -n home-manager exec "$POD" -- hermes cron create "$schedule" "$prompt" --name "$name" --deliver "$deliver"
fi
}
# ---- Watchdog checks (silent unless something is wrong) ----
create "door-window-check" "every 15m" "discord" \
"Check the Home Assistant REST API as documented in your SOUL.md. List all door and window sensor entities (binary_sensor.* for doors, windows, contact sensors). If any are open and either no one is home (check zone.home or presence sensors) or it is between 23:00 and 06:00, notify Roger with which sensor is open. If all closed, reply with exactly [SILENT]."
create "leak-moisture-check" "every 10m" "discord" \
"Check the Home Assistant REST API as documented in your SOUL.md. Look for moisture, water leak, or flood sensor entities (binary_sensor.* moisture/water/leak, sensor.* moisture). If any report wet/active, alert Roger immediately with the sensor name and location. If all dry, reply with exactly [SILENT]."
create "energy-anomaly" "every 30m" "discord" \
"Check the Home Assistant REST API as documented in your SOUL.md. Read the current power draw sensor (sensor.* power, sensor.* current_power). If the total power draw is unusually high for the time of day (over 4000 W during the day, or over 800 W when no one is home at night), alert Roger with the reading. Otherwise reply with exactly [SILENT]."
create "comfort-check" "every 1h" "discord" \
"Check the Home Assistant REST API as documented in your SOUL.md. Read indoor temperature and humidity sensors. If a room is outside a comfortable range (below 18C or above 26C, or humidity above 65 percent), you may toggle a fan entity in that room (safe). Do NOT change climate/HVAC settings yourself — if it is uncomfortable, propose a climate change to Roger on Discord and wait. If everything is comfortable, reply with exactly [SILENT]."
create "left-on-check" "every 1h" "discord" \
"Check the Home Assistant REST API as documented in your SOUL.md. If no one is home (zone.home is away or presence sensors all off), find any lights, media players, fans, or non-critical switches that are on. Turn off the safe non-essential ones (lights, media, fans) and report what you turned off. Do NOT touch anything security-critical. If someone is home, or nothing is left on, reply with exactly [SILENT]."
# ---- Routines (always delivered) ----
create "goodnight-routine" "0 23 * * *" "discord" \
"Run the goodnight routine via the Home Assistant REST API as documented in your SOUL.md. Turn off all non-essential lights and pause media players (safe actions). Then propose — but do NOT execute — locking the doors (lock.*), closing the garage (cover.* garage), and arming the alarm (alarm_control_panel.*). Ask Roger on Discord for confirmation before calling any of those security-critical services. Deliver a short summary of what you turned off and what you are waiting for confirmation on."
create "morning-briefing" "0 7 * * *" "discord" \
"Produce a morning home briefing for Roger using the Home Assistant REST API as documented in your SOUL.md. Include: (1) who is home (zone.home / presence), (2) any lights or media still on, (3) any doors or windows open, (4) indoor temperatures, (5) today's calendar events if a calendar entity exists, (6) overnight energy use if an energy sensor exists, (7) any sensors in an alert/unavailable state. Keep it under 1800 chars. Always deliver (no [SILENT])."
echo "Done. Listing all cron jobs:"
kubectl -n home-manager exec "$POD" -- hermes cron list

View File

@@ -0,0 +1,154 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: hermes
namespace: home-manager
labels:
app: hermes
spec:
replicas: 1 # MUST be 1 — Hermes' /opt/data is single-writer.
strategy:
type: Recreate # never run two pods against the same PVC
selector:
matchLabels:
app: hermes
template:
metadata:
labels:
app: hermes
spec:
# No serviceAccountName — the agent has NO k8s API access. It manages the
# home via the Home Assistant REST API and notifies via Discord.
# Pin to the powerful amd64 node (image is linux/amd64; the NUC has 24 GiB).
nodeSelector:
kubernetes.io/arch: amd64
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: hardware
operator: In
values: ["high-memory"]
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: hermes
topologyKey: kubernetes.io/hostname
initContainers:
# Seed /opt/data with config.yaml + SOUL.md + .env on first boot only.
# ArgoCD owns the manifests; the PVC is runtime state and is NOT reconciled.
- name: seed-data
image: busybox:1.36
command: ["sh", "-c"]
args:
- |
set -e
if [ ! -f /opt/data/config.yaml ]; then
echo "First boot: seeding /opt/data from ConfigMap + env..."
cp /seed/config.yaml /opt/data/config.yaml
cp /seed/SOUL.md /opt/data/SOUL.md
chmod 600 /opt/data/config.yaml
# Write .env from the injected Secret env vars so the s6 gateway
# finds API keys (the hermes container reads keys from /opt/data/.env).
: > /opt/data/.env
chmod 600 /opt/data/.env
for k in OPENAI_API_KEY OPENAI_BASE_URL HOMEASSISTANT_TOKEN \
DISCORD_BOT_TOKEN DISCORD_HOME_CHANNEL \
GATEWAY_ALLOW_ALL_USERS DISCORD_FREE_RESPONSE_CHANNELS \
HERMES_DASHBOARD HERMES_DASHBOARD_BASIC_AUTH_USERNAME \
HERMES_DASHBOARD_BASIC_AUTH_PASSWORD HERMES_DASHBOARD_BASIC_AUTH_SECRET; do
eval "v=\${$k:-}"
[ -n "$v" ] && echo "$k=$v" >> /opt/data/.env
done
else
echo "/opt/data already initialized — leaving runtime state intact."
fi
mkdir -p /opt/data/home/.kube /opt/data/cron/output /opt/data/scripts
envFrom:
- secretRef:
name: hermes-env
volumeMounts:
- name: data
mountPath: /opt/data
- name: seed
mountPath: /seed
containers:
- name: hermes
image: nousresearch/hermes-agent:latest
imagePullPolicy: Always
# IMPORTANT: do NOT set `command:` — it would override the image's
# ENTRYPOINT (/init, s6-overlay), which sets up the hermes user, seeds
# config on first boot, and supervises the gateway.
args: ["gateway", "run"]
ports:
- name: gateway
containerPort: 8642
- name: dashboard
containerPort: 9119
envFrom:
- secretRef:
name: hermes-env
env:
- name: HERMES_HOME
value: /opt/data
# Home Assistant REST API base URL (in-cluster service). Non-secret, so
# it lives here rather than in the Secret. The SOUL.md uses $HOMEASSISTANT_URL.
- name: HOMEASSISTANT_URL
value: "http://home-assistant.home-assistant:80"
# HERMES_WRITE_SAFE_ROOT is intentionally unset → defaults to HERMES_HOME
# (/opt/data). The home agent has no git workspace to write to, so the
# tighter default is correct (memory/skills/scripts live under /opt/data).
volumeMounts:
- name: data
mountPath: /opt/data
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "2Gi"
cpu: "1000m"
livenessProbe:
# Probe the dashboard port (9119, always enabled via HERMES_DASHBOARD=1
# and binds 0.0.0.0). The gateway API on 8642 is off by default.
tcpSocket:
port: 9119
initialDelaySeconds: 90
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 5
securityContext:
allowPrivilegeEscalation: false
volumes:
- name: data
persistentVolumeClaim:
claimName: hermes-data
- name: seed
configMap:
name: hermes-seed
---
apiVersion: v1
kind: Service
metadata:
name: home-manager
namespace: home-manager
spec:
type: ClusterIP
selector:
app: hermes
ports:
- name: gateway
port: 80
targetPort: 8642
- name: dashboard
port: 9119
targetPort: 9119

24
home-manager/ingress.yaml Normal file
View File

@@ -0,0 +1,24 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: home-manager
namespace: home-manager
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: traefik
tls:
- hosts:
- home-manager.rogi.casa
secretName: home-manager-tls
rules:
- host: home-manager.rogi.casa
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: home-manager
port:
number: 9119 # dashboard

View File

@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: home-manager

11
home-manager/pvc.yaml Normal file
View File

@@ -0,0 +1,11 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: hermes-data
namespace: home-manager
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi

41
home-manager/rbac.yaml Normal file
View File

@@ -0,0 +1,41 @@
# Minimal RBAC for the cron-seed Job ONLY.
#
# The Hermes home-manager agent itself has NO k8s RBAC — it manages the home
# via the Home Assistant REST API (http://home-assistant.home-assistant:80).
#
# The cron-seed Job needs to `kubectl exec` into the hermes pod to run
# `hermes cron create ...` (the only way to seed Hermes' internal cron).
# Scoped to this namespace, pods/exec on the hermes pod only.
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: cron-seeder
namespace: home-manager
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: cron-seeder
namespace: home-manager
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: cron-seeder
namespace: home-manager
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: cron-seeder
subjects:
- kind: ServiceAccount
name: cron-seeder
namespace: home-manager