mutana-heartbeat¶
Metadata¶
| Field | Value |
|---|---|
| Service | mutana-heartbeat |
| Purpose | Publish a privacy-reduced Uptime Kuma status snapshot to mutana.xyz |
| Criticality | Tier 2 |
| Owner | Platform owner |
| Clusters | homelab |
| Namespace | monitoring (shared, pre-existing) |
| Exposure | none; outbound HTTPS/FTP only |
| Stateful | no |
| Backup class | none; output is derived |
| RPO / RTO | 15-minute cadence / next successful run |
| Last reviewed | 2026-08-28 |
1. Service Overview¶
mutana-heartbeat is a scheduled, stdlib-only Python publisher. It reads the explicitly curated public Uptime Kuma status page, optionally verifies https://mutana.xyz, reduces those inputs to display-safe fields, and atomically replaces status.json in WordPress uploads.
If it stops, Mutana keeps serving the last good file and marks it frozen after 45 minutes, then lost after six hours. The publisher does not affect the availability of Uptime Kuma or WordPress.
Dependencies¶
| Dependency | Type | Why it matters |
|---|---|---|
| Uptime Kuma | service in monitoring |
Public status-page metadata and heartbeat source |
| LWS FTP endpoint | external | Receives the generated status.json |
| mutana.xyz | external | Self-check and consumer of the status file |
mutana-heartbeat-secret |
Kubernetes Secret | Kuma and scoped FTP connection values |
2. Architecture Diagram¶
[CronJob / 15 min, homelab:monitoring]
-> GET public Kuma status-page metadata + heartbeat
-> HEAD https://mutana.xyz
-> reduce to display-only status.json
-> STOR status.json.tmp
-> RNFR/RNTO status.json (atomic)
-> browser status page applies staleness rules
3. Deployment Specifications¶
| Item | Value |
|---|---|
| Source path | mutana-heartbeat/ |
| Deployment model | Root Kustomization, manual first release |
| Namespace | existing monitoring namespace |
| Workload kind | CronJob |
| Image | python:3.12-slim@sha256:09f7da3bc104798d0afb40bc08d23ab2da20a76130cec1f2ef170848f5d85217 |
| Schedule | */15 * * * * |
| Concurrency | Forbid |
| Requests | 100m CPU, 64Mi memory |
| RBAC | none; ServiceAccount token automount disabled |
| Storage | none |
| Config | heartbeat.py generated into ConfigMap mutana-heartbeat-script |
Cluster mapping¶
| Cluster | Entry point | Notes |
|---|---|---|
| homelab | mutana-heartbeat/kustomization.yaml |
Reuses the namespace and Kuma instance already used by the homelab monitoring stack |
The root Kustomization is deliberate: the script ConfigMap is generated from the adjacent unit-tested source, avoiding an embedded duplicate. It is not in a Fleet GitRepo path yet.
4. Configuration Guide¶
Public data contract¶
The Kuma public status page is the monitor allowlist. Output contains generated_at, computed overall, and services with only name, category, state, uptime_24h, and uptime_7d. Public monitor names must not contain internal addresses, private hostnames, customer names, or sensitive topology.
Secrets management¶
Secret mutana-heartbeat-secret is created out of band in namespace monitoring. Required keys:
| Key | Purpose |
|---|---|
KUMA_URL |
Reachable Kuma base URL |
SLUG |
Public status-page slug |
FTP_HOST |
TLS-valid publisher hostname |
FTP_USER |
Scoped FTP account |
FTP_PASS |
FTP password |
FTP_DIR |
Existing WordPress uploads directory for that account |
The committed secret.example.yaml contains placeholders only; local secret.yaml is gitignored. Rotate by updating the Secret and triggering a one-off Job. Without the Secret, Jobs fail and the previous public file remains intact.
5. Access Protocols¶
| Path | URL or endpoint | Audience | Auth | TLS terminates at |
|---|---|---|---|---|
| Kuma status API | Secret-defined internal/base URL | publisher pod | public status-page slug | Kuma endpoint |
| Mutana self-check | https://mutana.xyz |
publisher pod | none | LWS edge |
| WordPress uploads | Secret-defined FTP host/directory | publisher pod | scoped FTP credentials | FTP server when FTPS is available |
There is no inbound Service or IngressRoute. The publisher attempts verified FTPS, warns and falls back to plain FTP only because the current hosting account may not expose FTPS on its configured endpoint.
6. Operations and Observability¶
- Primary signals: CronJob last schedule/success time, failed Job count, Job exit code, and logs.
- Public symptom:
/statut/reports frozen data after 45 minutes or a lost heartbeat after six hours. - Logs never print the password or Secret contents; they report fetch/upload failures and the published service count.
- A one-off Job created from the CronJob is the standard post-change smoke test.
See the runbook for exact commands.
7. Backup and Recovery Notes¶
The workload is stateless and needs no PVC backup. Source and manifests live in Git; the Secret is recreated out of band. status.json is derived data and the prior successful version remains on LWS when a run fails.
Recovery requires a valid Secret, a public Kuma status page with reviewed display names, network access, and one successful Job. Do not restore or synthesize old status data as fresh.
8. Release and Change Notes¶
- Initial release: stdlib publisher, deterministic offline fixture path, verified-FTPS-first atomic upload, hardened CronJob, and manual homelab Kustomize entry point.
- Fleet activation is intentionally deferred until the operator reviews the production monitor allowlist and Secret.
- Rollback: suspend/delete the CronJob or revert this workload. Mutana's staleness behavior is the user-visible fallback.