π€ Generated by the Daily AI Assistant
Problem
ksail only "sees" clusters that it discovers through its infrastructure providers (Docker / Hetzner / Omni / AWS-EKS / nested-Kubernetes) plus in-flight lifecycle jobs β Service.List() in pkg/cli/clusterapi/local_service.go, which backs both the CLI (ksail cluster list) and the web UI / desktop app. A cluster that already exists in the user's kubeconfig but was not provisioned by ksail (a managed EKS/GKE/AKS cluster, a kubeadm cluster, a colleague's cluster, etc.) is invisible everywhere β even though most of ksail's read/operate surface already works against any context via client-go.
Goal
ksail should be able to see and operate on any kubeconfig cluster, across both the CLI and the desktop app / web UI, within the limitations of what it can do for a cluster it does not manage. Unmanaged (kubeconfig-only) clusters are shown clearly and visibly marked as unmanaged / unsupported β never hidden, never presented as a normal managed cluster, and never silently failing when a ksail-only action is attempted.
What already works generically (any kubeconfig context, via client-go)
Resource browsing, pod logs & exec, kubectl-style access, server-side manifest apply, kubeconfig export, apiserver proxy/watch, and secrets cipher are all client-go / local-key backed and need no per-cluster ksail metadata β so they should "just work" against an imported cluster once ksail can select it.
Proposed direction
- One cluster model, marked. Extend cluster enumeration (
Service.List() / enumerate) to also read the user's kubeconfig (clientcmd) and synthesize a v1alpha1.Cluster for each context not already discovered via a provider β keyed by context name and flagged unmanaged / kubeconfig-only. Distribution/provider are best-effort hints (context-name / server-URL heuristics) and left empty when unknown. This one model feeds both the CLI and the UI, so behaviour is identical across surfaces. The existing injectable kubeconfigPath seam makes it unit-testable with a fixture kubeconfig.
- CLI surfaces them, marked. See the CLI parity section below.
- UI surfaces them, marked. An "Add / import from kubeconfig" affordance in
ClusterFormDialog.tsx; render unmanaged clusters in ClustersTable.tsx / ClusterSwitcher.tsx with a clear unmanaged badge and a graceful β for unknown distribution/provider.
- Gate ksail-only actions with the existing capability flags (no silent failures) β reusing the flags the local backend already emits (
clusterUpdate, component-install, start/stop, β¦); no new flags expected.
CLI parity β ksail cluster and ksail workload
The same "see + operate within limits, clearly marked unmanaged" model applies to the CLI. ksail cluster list and ksail cluster switch are the headline cases; the rest of the cluster and workload groups follow the same rule:
- See / select (work on any kubeconfig context) β surface with an explicit
unmanaged / unsupported marker:
ksail cluster list β include kubeconfig contexts not managed by ksail, with an explicit unmanaged status column (never hidden, never shown as a normal managed cluster).
ksail cluster switch β allow switching the active context to an unmanaged cluster (it is just a kubeconfig context switch), noting it is unmanaged.
ksail cluster info / ksail cluster connect β show/emit what client-go can (endpoint, kubeconfig access), with unknown ksail-only fields shown as β.
- Operate within limits (client-go-backed β work on the selected context):
ksail workload {kubectl, watch, network, edit, images, push, render, validate, scan, debug} and ksail cluster diagnose should work against the selected unmanaged context. GitOps-driven commands (ksail workload reconcile, ksail workload install) depend on ksail/GitOps config and should clearly report "requires a ksail-managed / GitOps-configured cluster" when that config is absent β not a stack trace.
- Manage / lifecycle (ksail-only β refuse clearly on unmanaged):
ksail cluster {create, delete, update, start, stop, backup, restore, repair, ttl} must fail fast with a documented message (e.g. cluster '<name>' is not managed by ksail; lifecycle operations are unavailable for imported kubeconfig clusters) β a clean, documented exit, never a stack trace or a silent no-op.
Consistency: the CLI unmanaged marker and the UI "unmanaged" badge are the same concept driven by the one marked cluster model, so a cluster behaves identically whether the user is on the CLI or in the UI.
Limitations to make clearly & visibly unavailable on unmanaged (kubeconfig-only) clusters
These require ksail-managed provisioning / spec.cluster.* metadata, so they are shown as explicitly disabled/refused with a reason (UI: disabled control + tooltip; CLI: documented error):
- Lifecycle operations (create / delete / start / stop) β ksail has no provider or distribution to drive them.
- Component installation (CNI, CSI, metrics-server, GitOps/Flux, policy engine) β all driven from
spec.cluster.*.
- Reconfigure / re-provision & provider credentials β no provider spec to act on.
- GitOps operations (reconcile / install) β need ksail/GitOps config, not just a kubeconfig.
- Distribution / provider shown as best-effort or
β (no ksail spec to read).
- Live status reconciliation (phase / endpoint / nodesReady) β limited to what client-go can directly observe, without the operator's reconciler.
Acceptance criteria
ksail cluster list and the UI cluster list both show kubeconfig contexts not managed by ksail, clearly marked unmanaged / imported (CLI status column + UI badge).
ksail cluster switch can select an unmanaged cluster; the client-go-backed read/operate commands (and their UI equivalents β resource browsing, logs/exec, manifest apply, proxy/watch) work against it.
- Every ksail-only action (lifecycle, component install, GitOps, reprovision) is visibly disabled/refused with an explanation on unmanaged clusters β never a silent failure, a bare 501, or an unhandled stack trace.
- No regression for ksail-managed clusters (they keep full capability across CLI + UI).
- Docs (CLI reference for
cluster/workload, plus web UI / desktop) document unmanaged-cluster tracking and its visible-limitations model.
Suggested decomposition (independently-shippable children)
- Backend enumeration + marked model (natural first slice): kubeconfig-context enumeration in
Service.List() β synthetic v1alpha1.Cluster with an unmanaged marker + a distribution-detection heuristic + tests via the kubeconfigPath fixture seam. Audit spec.cluster.distribution / provider optionality in pkg/apis/cluster/v1alpha1/types.go (omitempty / schema). This model is shared by all downstream children.
- CLI: see / select β
ksail cluster list shows the unmanaged marker; ksail cluster switch / info / connect operate on unmanaged contexts; ksail cluster diagnose + the client-go workload commands work against the selected context.
- CLI: refuse lifecycle clearly β
ksail cluster {create,delete,update,start,stop,backup,restore,repair,ttl} and GitOps workload {reconcile,install} return the documented "not managed by ksail" error for unmanaged clusters.
- Capability gating & API contract β imported clusters report the right capability flags so ksail-only actions are disabled + labelled; confirm the
pkg/webui/api contract stays cluster-agnostic.
- Web UI β import affordance in
ClusterFormDialog.tsx + unmanaged badge + graceful degraded rendering in ClustersTable.tsx / ClusterSwitcher.tsx (the SSE cluster-list stream already carries generic cluster objects, so useClusterStream.ts / api.ts likely need no change).
- Docs β document the unmanaged-cluster tracking capability and the visible-limitations model across CLI + UI.
Size
Large (epic) β decomposes into the small children above; the backend-enumeration + marked-model child (1) is the natural first increment and unblocks both the CLI (2/3) and UI (5) work.
Change surface (grounded on main)
- Backend / model:
pkg/cli/clusterapi/local_service.go (List / enumerate / newCluster), pkg/apis/cluster/v1alpha1/types.go (ClusterSpec optionality), pkg/webui/api (contract).
- CLI:
pkg/cli/cmd/cluster/{list,switch,info,connect,diagnose}.go (surface + select), pkg/cli/cmd/cluster/{create,delete,update,startstop,backup,restore,repair,ttl}.go (refuse cleanly), pkg/cli/cmd/workload/{reconcile,install}.go (GitOps guard).
- UI:
web/ui/src/components/{ClustersTable,ClusterSwitcher,ClusterFormDialog}.tsx, web/ui/src/api.ts (likely no change).
Part of the KSail-as-a-Kubernetes-UI direction (see roadmap #4988).
Problem
ksail only "sees" clusters that it discovers through its infrastructure providers (Docker / Hetzner / Omni / AWS-EKS / nested-Kubernetes) plus in-flight lifecycle jobs β
Service.List()inpkg/cli/clusterapi/local_service.go, which backs both the CLI (ksail cluster list) and the web UI / desktop app. A cluster that already exists in the user's kubeconfig but was not provisioned by ksail (a managed EKS/GKE/AKS cluster, a kubeadm cluster, a colleague's cluster, etc.) is invisible everywhere β even though most of ksail's read/operate surface already works against any context via client-go.Goal
ksail should be able to see and operate on any kubeconfig cluster, across both the CLI and the desktop app / web UI, within the limitations of what it can do for a cluster it does not manage. Unmanaged (kubeconfig-only) clusters are shown clearly and visibly marked as unmanaged / unsupported β never hidden, never presented as a normal managed cluster, and never silently failing when a ksail-only action is attempted.
What already works generically (any kubeconfig context, via client-go)
Resource browsing, pod logs & exec,
kubectl-style access, server-side manifest apply, kubeconfig export, apiserver proxy/watch, and secrets cipher are all client-go / local-key backed and need no per-cluster ksail metadata β so they should "just work" against an imported cluster once ksail can select it.Proposed direction
Service.List()/enumerate) to also read the user's kubeconfig (clientcmd) and synthesize av1alpha1.Clusterfor each context not already discovered via a provider β keyed by context name and flagged unmanaged / kubeconfig-only. Distribution/provider are best-effort hints (context-name / server-URL heuristics) and left empty when unknown. This one model feeds both the CLI and the UI, so behaviour is identical across surfaces. The existing injectablekubeconfigPathseam makes it unit-testable with a fixture kubeconfig.ClusterFormDialog.tsx; render unmanaged clusters inClustersTable.tsx/ClusterSwitcher.tsxwith a clear unmanaged badge and a gracefulβfor unknown distribution/provider.clusterUpdate, component-install, start/stop, β¦); no new flags expected.CLI parity β
ksail clusterandksail workloadThe same "see + operate within limits, clearly marked unmanaged" model applies to the CLI.
ksail cluster listandksail cluster switchare the headline cases; the rest of theclusterandworkloadgroups follow the same rule:unmanaged/unsupportedmarker:ksail cluster listβ include kubeconfig contexts not managed by ksail, with an explicitunmanagedstatus column (never hidden, never shown as a normal managed cluster).ksail cluster switchβ allow switching the active context to an unmanaged cluster (it is just a kubeconfig context switch), noting it is unmanaged.ksail cluster info/ksail cluster connectβ show/emit what client-go can (endpoint, kubeconfig access), with unknown ksail-only fields shown asβ.ksail workload {kubectl, watch, network, edit, images, push, render, validate, scan, debug}andksail cluster diagnoseshould work against the selected unmanaged context. GitOps-driven commands (ksail workload reconcile,ksail workload install) depend on ksail/GitOps config and should clearly report "requires a ksail-managed / GitOps-configured cluster" when that config is absent β not a stack trace.ksail cluster {create, delete, update, start, stop, backup, restore, repair, ttl}must fail fast with a documented message (e.g.cluster '<name>' is not managed by ksail; lifecycle operations are unavailable for imported kubeconfig clusters) β a clean, documented exit, never a stack trace or a silent no-op.Consistency: the CLI
unmanagedmarker and the UI "unmanaged" badge are the same concept driven by the one marked cluster model, so a cluster behaves identically whether the user is on the CLI or in the UI.Limitations to make clearly & visibly unavailable on unmanaged (kubeconfig-only) clusters
These require ksail-managed provisioning /
spec.cluster.*metadata, so they are shown as explicitly disabled/refused with a reason (UI: disabled control + tooltip; CLI: documented error):spec.cluster.*.β(no ksail spec to read).Acceptance criteria
ksail cluster listand the UI cluster list both show kubeconfig contexts not managed by ksail, clearly markedunmanaged/ imported (CLI status column + UI badge).ksail cluster switchcan select an unmanaged cluster; the client-go-backed read/operate commands (and their UI equivalents β resource browsing, logs/exec, manifest apply, proxy/watch) work against it.cluster/workload, plus web UI / desktop) document unmanaged-cluster tracking and its visible-limitations model.Suggested decomposition (independently-shippable children)
Service.List()β syntheticv1alpha1.Clusterwith an unmanaged marker + a distribution-detection heuristic + tests via thekubeconfigPathfixture seam. Auditspec.cluster.distribution/provideroptionality inpkg/apis/cluster/v1alpha1/types.go(omitempty/ schema). This model is shared by all downstream children.ksail cluster listshows theunmanagedmarker;ksail cluster switch/info/connectoperate on unmanaged contexts;ksail cluster diagnose+ the client-goworkloadcommands work against the selected context.ksail cluster {create,delete,update,start,stop,backup,restore,repair,ttl}and GitOpsworkload {reconcile,install}return the documented "not managed by ksail" error for unmanaged clusters.pkg/webui/apicontract stays cluster-agnostic.ClusterFormDialog.tsx+ unmanaged badge + graceful degraded rendering inClustersTable.tsx/ClusterSwitcher.tsx(the SSE cluster-list stream already carries generic cluster objects, souseClusterStream.ts/api.tslikely need no change).Size
Large (epic) β decomposes into the small children above; the backend-enumeration + marked-model child (1) is the natural first increment and unblocks both the CLI (2/3) and UI (5) work.
Change surface (grounded on
main)pkg/cli/clusterapi/local_service.go(List/enumerate/newCluster),pkg/apis/cluster/v1alpha1/types.go(ClusterSpecoptionality),pkg/webui/api(contract).pkg/cli/cmd/cluster/{list,switch,info,connect,diagnose}.go(surface + select),pkg/cli/cmd/cluster/{create,delete,update,startstop,backup,restore,repair,ttl}.go(refuse cleanly),pkg/cli/cmd/workload/{reconcile,install}.go(GitOps guard).web/ui/src/components/{ClustersTable,ClusterSwitcher,ClusterFormDialog}.tsx,web/ui/src/api.ts(likely no change).Part of the KSail-as-a-Kubernetes-UI direction (see roadmap #4988).