Security-first · spec-driven · Kubernetes-native Website-as-a-Service.
kaddy is an internal developer platform for monitored, TLS-terminated websites. One self-service
claim provisions a site behind Caddy with observability, alerting, and progressive delivery. The
gridscale Platform Engineer exercise (install Caddy, monitor with
Prometheus, alert on thresholds, optional nginx reverse proxy) is satisfied as one tenant of the
platform — clubhouse — not as a one-off VM script.
Local-first: phase-1 development runs on a local kind + Cilium cluster (e1e-kind-local-cluster, landed) — Cilium Gateway API + LB-IPAM/L2, so the edge matches phase 2. Promote to GSK + LBaaS + Upjet Crossplane in phase 2 — see decisions D-025 and ROADMAP. The 3-node Talos driving-range is a deferred optional maturity-contrast spike (D-025).
The platform runs on a real gridscale GSK cloud-edge with publicly-trusted Let's Encrypt certificates (verified live 2026-07-18):
- https://argocd.lab.platformrelay.dev — the ArgoCD app-of-apps UI (the GitOps story).
- https://grafana.lab.platformrelay.dev — anonymous-viewer Grafana (dashboards + Prometheus).
- https://demo.lab.platformrelay.dev — the Caddy website tenant (the served demo).
- https://caddy.lab.platformrelay.dev — the full caddy-mvp Argo Rollouts canary (showcase image
ghcr.io/platformrelay/kaddy-showcase, live stable/canary weight-split).
Served through a Traefik Gateway API edge behind a gridscale LoadBalancer, with
DNS-01 Let's Encrypt prod certs (the TLS chain verifies without -k). The
config is codified as GitOps overlays: the Traefik controller in
deploy/gateway-controller/traefik/ and the
clubhouse Gateway + Certificates + HTTPRoutes in
deploy/gateway/cloud-only/.
On-demand, not 24/7 (DECIDED-B cost governance). This is a reproducible live demo edge brought up around a demo and torn down immediately after (
task e8b:up→ demo →task e8b:down) — the GSK cluster + LoadBalancer cost real money every hour they run. The URLs above are live only while the edge is up; see docs/runbooks/gridscale-live-demo.md.
5 minutes (honest order — skip anything labeled unavailable):
-
Released demo / deck — build the Slidev deck with
task deck:build(sources inslides/; CIdeckworkflow). A published Pages URL for the deck is unavailable until that publish path is enabled. -
Scorecard — live URL
https://platformrelay.github.io/Kaddy/(HTTP 200; workflow.github/workflows/scorecard-pages.yaml). -
Local services + demos — follow docs/getting-started.md (kind bring-up, service catalogue,
task demo:fire/task demo/task demo:chaos). -
Architecture — docs/ARCHITECTURE.md and the exercise traceability matrix; skim docs/ROADMAP.md.
Deep dive — docs/adr/README.md (architecture decisions) →
docs/ARCHITECTURE.md → openspec/changes/ (specs with
Verify: + Test: per requirement).
| Layer | Phase 1 (kind — local) | Phase 2 (gridscale lab) |
|---|---|---|
| Substrate | kind + Cilium (E1e, landed) — single-node | GSK managed k8s |
| Day-0 IaC | hack/cluster/ kind bring-up (E1e) |
Terramate + gridscale/gridscale v2 (E1g) |
| Edge | Cilium Gateway + LB-IPAM/L2 | LBaaS + LE + Cilium/Envoy |
| GitOps | ArgoCD app-of-apps | Same manifests, re-sync on GSK |
| Identity | Dex + GitHub OAuth (PlatformRelay) | Same (update issuer URL for LBaaS domain) |
| Secrets | SOPS + age in git (ADR-0110) | Same |
| Observability | kube-prometheus-stack + Loki + Alloy | Same |
| TLS | cert-manager (staging) | LBaaS LE + cert-manager |
| Self-service | Crossplane Website XRD |
+ Upjet provider-gridscale (E6g) |
| Progressive delivery | Argo Rollouts + Gateway API | Same |
| Policy | Kyverno, default-deny NetworkPolicies | Same |
| Component | Role |
|---|---|
| clubhouse | the sample website tenant (satisfies the brief) |
| marshal | alerting pipeline — PrometheusRules + Alertmanager |
| mulligan | blue/green + canary with automated rollback |
| scorecard | evidence harness — k6 + metrics/logs capture → HTML report |
| driving-range | deferred optional 3-node Talos maturity-contrast spike (sibling repo; D-025) |
Every requirement carries a Test: artifact and a Verify: command. See
docs/development/testing.md and
ADR-0701.
task verify # lint + scrub + openspec + spec-coverage
task test:spec # every REQ has Test + Verify
task test # L0 tofu test · L1 conftest + promtool · L2 Chainsaw| Level | Tool | Proves |
|---|---|---|
| L0 | tofu test |
label module outputs |
| L1 | conftest · promtool | plan labels · alert rules fire |
| L2 | Chainsaw | policies, routes, rollouts, monitors on live cluster |
| L3 / L4 | k6 · scorecard | load/alerting · evidence bundle |
Brief answered end-to-end — serve → scrape → fire, plus progressive delivery and an enforcing
security baseline, all GitOps. Verified live in the phase-1 demo: the phase-1 GitOps app-of-apps
Synced/Healthy; clubhouse served
over verified HTTPS through the Cilium Gateway (E4); observability spine
(Prometheus/Alertmanager/Grafana + Loki/Alloy, E3); the marshal fires a real alert against the
served site and resolves it — task demo:fire (E5) — with a provisioned Grafana dashboard and
data-source-managed alerts; Argo Rollouts shifts live HTTPRoute canary weights and auto-rolls-back
on abort (E7); Kyverno admission (5 Enforce policies), default-deny NetworkPolicies, and restricted
ArgoCD AppProjects are live (E1c). The gateway spike (E2), labels module (E1b), recording-ready
Slidev showcase deck (E12), and the optional Caddy operator (E9, landed on main after v0.1.1) are
also on main. Two releases are tagged and published: v0.1.0 (serve → scrape → fire) and
v0.1.1, which added Crossplane self-service (E6 Website XRD, claim → monitored site), Dex +
GitHub identity (E1d), and CI substrate parity. A data-flow security review is at
docs/security/security-review-2026-07-16.md.
Phase 2 (gridscale) landed as offline-authored IaC — Terramate/OpenTofu stacks for the GSK
day-0 substrate (E1g), the Upjet provider-gridscale consumer (E6g), the on-demand live-demo env
(E8b), and the gridscale Marketplace template (E13) — each gated offline (tofu test/conftest/
kubeconform/promtool) with live provisioning ruthlessly cost-gated (create → verify → tofu destroy). E1g's GSK cluster was live-proven on real gridscale (kubectl get nodes Ready, then
torn down, tenant left clean — see evidence/live/e1g-gsk-2026-07-17.md;
re-provisioned standing 2026-07-18, evidence/live/e1g-gsk-2026-07-18.md).
A 2026-07-18 live standing-demo attempt surfaced that the public cloud edge is not yet built:
task e8b:up is guard-locked to the local kind context, and GSK has no ingress edge out of the box
(Gateway API / Cilium GatewayClass / LB-IPAM are installed only by the kind bring-up). That deferred
work is now decomposed into stories E1g-S05a–h (bootstrap opt-in, GSK Gateway API, LBaaS→node,
network-topology reconcile, real *.platformrelay.dev hostnames, DNS-01 issuer, DNS+LE serve, and a
node-public-IP security spike) in agent-context/BACKLOG.md.
The Backstage self-service portal (E10, cuttable) landed its kaddy-side GitOps wiring
(deploy/portal); the Backstage app source lives in the separate
PlatformRelay/kaddy-portal repo. The remaining
live proofs (E6g provider install, E13 Marketplace deploy → E13-S05, the public E8b edge →
E1g-S05a–h, the running portal) are cost-gated follow-ups. Full plan: ROADMAP.
references/PocketIDP/ is a local, un-vendored copy of
PocketIDP, kept as a reference for the
Gitea + Backstage demo setup we will draw on for E10. It is gitignored — not part of kaddy's
committed tree.
The brief provisions a gridscale lab. Phase 1 develops on a local kind + Cilium cluster ($0, E1e). Phase 2 promotes to gridscale-native PaaS — GSK, LBaaS, Object Storage, Upjet Crossplane — for the employer-facing demo (E8b). Reasoning in decisions D-013 / D-015 / D-016 / D-025.
Lab-sized footprint for the employer demo — estimates from public gridscale list pricing (control plane free; billed per minute). Re-check gridscale pricing before quoting. Phase 1 on kind is €0 / month.
| Resource | Size (typical lab) | Est. EUR / month |
|---|---|---|
| GSK node pool | 2× 2C-4G workers (~€46/node) | ~92 |
| LBaaS | 1 load balancer | ~22.50 |
| Object Storage | evidence / marketplace .gz (per GB) |
~0.06 / GB |
| Total (indicative, excl. storage GB) | ~115 EUR / month |
Exact SKUs live in the gridscale console when phase 2 starts.
