devantler-tech / devantler-tech/ksail
Headlamp-parity audit: capability gap matrix + per-capability parity decisions
- Dominant language
- Go
- Stars
- 165
- Forks
- 12
- Avg merge
- 5h 41m
- Merged PRs (30d)
- 337
Description
> 🤖 Generated by the Daily AI Assistant
Child 1 of the Headlamp-parity epic (#5827). Platform-side retirement counterpart: devantler-tech/platform#2496.
## What this is
The epic's first child: a capability audit of the **KSail web UI** (`ksail open web` / ksail-operator) against **Headlamp** (core UI + the six plugins the platform installs: flux, cert-manager, keda, opencost, kubescape, crossview), producing a gap matrix with a **parity decision per capability** so the retirement (platform#2496) has a concrete, maintainer-visible target.
Audited at ksail `origin/main` (v7.147.0). Backend: `pkg/webui/api/*`; frontend: `web/ui/src/components/*`.
## Headline finding — the KSail web UI is closer than it looks, and its plugin system is Headlamp-compatible
Most Headlamp **core** capabilities are already present (resource browse/detail, safe mutations, logs, exec, multi-cluster, OIDC, overview, events, GitOps/Flux browse). The six **plugin domains** are the visible gaps — but the KSail UI already ships a **Headlamp-compatible plugin system** (`pkg/webui/api/plugins.go`, `web/ui/src/components/PluginsView.tsx`): install-from-URL with Cosign/SHA256/ed25519 verification, an Artifact Hub catalog browser, a read-only kube-apiserver **proxy** (`kubeproxy.go`, powers the plugin *ApiProxy* data layer) and a **WebSocket multiplexer** (`kubewatch.go`) — the two APIs Headlamp plugins use.
**So the biggest strategic question for retirement is:** do the platform's existing Headlamp plugins **load as-is** in the KSail UI? If yes, plugin domains 12–16 collapse from "reimplement each natively (L)" to "verify + wire the existing plugin (S)", and the retirement is mostly a **core-UX parity** exercise. **Validating this is the recommended first parity slice** (child 2) — it de-risks the entire epic before any native plugin work is committed.
## Gap matrix — core Kubernetes-UI capabilities
| # | Capability | KSail today | Gap vs Headlamp | Parity decision | Size |
|---|---|---|---|---|---|
| 1 | Resource browsing | Present — curated allowlist (`resources.go:resourceKindEntries`): pods/workloads/svc/ingress/cm/pvc/events/nodes/ns + GitOps CRs + metrics | No **arbitrary CRD** discovery; Headlamp browses any kind | **Must-have** — dynamic CRD discovery (can ride the existing kube-apiserver proxy's allowlist-bypass) | M |
| 2 | Resource detail views | Present — YAML + conditions + related events (`ResourceDetailPanel.tsx`) | Manifest view is read-only | Met (see #3 for edit) | — |
| 3 | Resource editing | Present — safe mutations: scale/restart/delete/reconcile + multi-doc apply (`resources.go`, `apply.go`) | No **inline YAML edit/patch** of an arbitrary resource | **Must-have** — inline edit/patch in the detail panel | M |
| 4 | Pod logs | Present — SSE stream, follow, tail, container select (`logs.go`) | — | Met | — |
| 5 | Exec / terminal | Present — WebSocket TTY (`exec.go`, `ExecTerminal.tsx`) | — | Met | — |
| 6 | Port-forward | **Absent** | Headlamp has it | **Should-have** (dev convenience; platform access is via Gateway+oauth2-proxy, so lower priority) — maintainer confirms in/out | M |
| 7 | Multi-cluster | Present — cluster switcher + list (`ClusterSwitcher.tsx`) | No cross-cluster federation | Met (federation out of scope) | — |
| 8 | Authentication (OIDC) | Present — confidential-client OIDC, signed HttpOnly sessions (`auth.go`) | — | Met | — |
| 9 | Overview / health | Present — spec/status/conditions, workload counts, CPU/mem from metrics-server (`OverviewView.tsx`) | — | Met | — |
| 10 | Events | Present — type filter, search, pagination (`EventsView.tsx`) | — | Met | — |
## Gap matrix — plugin domains (the platform's six Headlamp plugins)
| # | Domain (plugin) | KSail today | Parity decision | Size |
|---|---|---|---|---|
| 11 | GitOps / Flux (flux 0.6.0) | Present — browse Kustomization/HelmRelease/GitRepository/OCIRepository + Flux/ArgoCD reconcile (`gitOpsKindEntries`) | **Must-have** — enrich toward the flux plugin (suspend/resume, source detail, dependency view); native, GitOps is the platform's core | M |
| 12 | Security posture (kubescape 0.11.2) | **Absent** | **Must-have** (this is the platform's Kubescape-100% program, #2447) — **first try loading the existing kubescape plugin**; native only if it doesn't load | S-if-plugin / L-native |
| 13 | Cost (opencost 0.1.3) | **Absent** | **Should-have** — load the existing opencost plugin; native reimplementation out of scope | S-if-plugin |
| 14 | Autoscaling (keda 0.1.1-beta) | **Absent** | **Should-have** — load the keda plugin, or add KEDA CRDs to the allowlist for basic browse | S |
| 15 | Certificates (cert-manager 0.1.0) | **Absent** | **Should-have** — load the cert-manager plugin, or add Certificate/Issuer CRDs to the allowlist | S |
| 16 | Crossplane graph (crossview 0.1.4) | **Absent** | **Should-have** — load the crossview plugin; native graph out of scope | S-if-plugin |
## KSail-only capabilities (net additions Headlamp lacks — retained through retirement)
Cluster lifecycle create/delete/start/stop · provider-credential management · SOPS age cipher · AI assistant (Copilot) · signed plugin install-from-URL · server-enforced read-only mode · kubeconfig export. These are why the KSail UI is a *superset* target, not just a like-for-like replacement.
## Retirement must-have set (what platform#2496 waits on)
To retire Headlamp, the KSail UI must reach parity on: **dynamic CRD browsing (#1)**, **inline resource edit (#3)**, **enriched Flux views (#11)**, and **security posture (#12)** — plus whichever of #6/#13/#14/#15/#16 the plugin-load validation (below) shows are cheap. Everything else is already met.
## Recommended decomposition (parity slices — children of #5827, oldest/highest-value first)
1. **Validate Headlamp-plugin loadability (child 2, do first — de-risks 12–16).** Load the platform's kubescape/opencost/keda/cert-manager/crossview/flux plugin builds into the KSail UI plugin system; report which load cleanly over the ApiProxy + WS-multiplexer and which need shims. Outcome decides native-vs-load for every plugin domain. Size **S–M**.
2. **Dynamic CRD browsing (#1)** — discover + browse arbitrary CRDs via the kube-apiserver proxy. Size **M**.
3. **Inline resource edit/patch (#3)** — editable YAML in the detail panel with server-side apply + dry-run. Size **M**.
4. **Security-posture view (#12)** — native only if the kubescape plugin doesn't load; wires the Kubescape-100% program (#2447) into the KSail UI. Size **S/L** (per slice 1).
5. **Enriched Flux views (#11)** — suspend/resume, source detail, dependency graph. Size **M**.
6. Then the remaining should-haves (#6 port-forward, #13/#14/#15/#16) per the slice-1 outcome and maintainer priority.
## Parity decision summary (AC#1)
- **Already at parity (7):** resource detail, logs, exec, multi-cluster, OIDC, overview, events.
- **Must-have gaps (4):** dynamic CRD browse, inline edit, enriched Flux, security posture.
- **Should-have gaps (5):** port-forward, cost, autoscaling, certificates, Crossplane — most collapse to "load the existing plugin" if slice 1 succeeds.
- **Out of scope:** cross-cluster federation, native OpenCost/Crossplane reimplementation (prefer loading the plugins).
Part of #5827. Unblocks platform#2496.
Contributor guide
Research direction
Start with pkg/webui/api/plugins.go, web/ui/src/components/PluginsView.tsx, kubeproxy.go, and kubewatch.go to understand the plugin loading, ApiProxy, and WebSocket paths. Load the platform's six Headlamp plugins into the KSail UI and record which work cleanly or need shims. Done means the capability gap matrix has a parity decision for each domain and identifies the remaining retirement blockers.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, kubernetes, typescript
- Domain
- devops, full-stack
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100