Integration Tiers — Agent-Only by Default
The stack's home is the app's own k3s (Colima). An existing external cluster is, by default, connected only as a target for observation, diagnostics, and operations — nothing inside that cluster may depend on the Mac's local stack.
| Tier | Scope | Installed in the cluster |
|---|---|---|
| L1 — agent-only (default) | observe / diagnose / operate | kagent CRDs + agents. No bridge |
| L2 — full stack (opt-in) | places the MLOps stack in the external cluster | dedicated kubemetal namespace + host bridge |
L1 is the default because of a measurement: L2's onboarding cost — verifying terminal paths, mirror registries, adjusting ArgoCD boundaries — repeats in proportion to the number of clusters.
L1 — Agent-Only (default path)
make kagent-up CONTEXT=<kubeconfig-context> # installs kagent 0.9.12 via helm (kagent ns)Then use the app's kagent Operations tab for per-context diagnostics and to toggle agents (security / promql / observability). The kagent UI opens on 8090 via make forward.
This path was verified on a live 6-node K3s HA cluster (Narwhal) — measured in-app from preflight through kagent diagnostics using a signed packaged app (2026-07-30).
L2 — Full-Stack Deployment to an Existing Cluster (opt-in)
The advanced path: put the MLOps stack on an existing Kubernetes cluster instead of bringing up Colima. The cluster is configuration, not a constant — the render script owns every per-target substitution (namespace, bridge, StorageClass, image registry), and scripts/k8s/kustomization.yaml is the only manifest list.
External clusters get their own dedicated kubemetal namespace rather than default; default stays Colima-only.
1. Preflight
Verifies reachability, the default StorageClass, any ArgoCD Application owning the target namespace, Kyverno Enforce policies, and host bridge candidates — all by measurement.
make preflight CONTEXT=<context> NAMESPACE=kubemetal2. Render check
Inspect the result without applying it.
make render CONTEXT=<context> BRIDGE_HOST=<host-ip> STORAGE_CLASS=<SC>BRIDGE_HOST must be an address from step 1 whose reachability was actually verified. Omitting it makes the render refuse: without it, a Colima-only address would ship through and the pods would die quietly. An unverified bridge address refuses to render rather than deploying a guess.
3. Apply
make provision CONTEXT=<context> BRIDGE_HOST=<host-ip> STORAGE_CLASS=<SC>This full-stack path was verified on that same 6-node cluster through Kyverno Enforce policies, a private mirror registry (bypassing Docker Hub pull limits), and ArgoCD GitOps with its selfHeal boundary (2026-07-26, terminal path).
Internal Registry / Mirror
For closed networks, or clusters hitting Docker Hub anonymous pull limits, set IMAGE_REGISTRY to redirect Docker Hub images.
make provision CONTEXT=<context> BRIDGE_HOST=<host-ip> IMAGE_REGISTRY=<host[/project]>When ArgoCD Owns the Target Namespace
A direct apply gets reverted by selfHeal. Use the GitOps path instead — KubeMetal writes files only and never pushes to Git.
make export-gitops NARWHAL_DIR=/path/to/narwhal CONTEXT=<context> BRIDGE_HOST=<host-ip>Pushing and syncing stay the GitOps repository owner's call.
Why Code Signing Matters
Reaching a LAN cluster from the packaged app requires stable code signing. Ad-hoc signing changes the identifier on every build, so the macOS local network permission never sticks and LAN kubectl is blocked with no route to host.
- If a valid codesigning identity exists in the keychain,
make appsigns with it automatically. For this Mac alone, a self-signed certificate is sufficient (measured 2026-07-29). - Distribution to others needs a Developer ID.
make app SIGNING_IDENTITY="Developer ID Application: …"Air-Gap Support
The Air-Gap Management tab downloads offline bundles (images, charts, binaries) ahead of time for transfer into a closed network, then performs offline installation and asset version verification. Offline startup itself is verified by a separate gate.
make verify-airgapTroubleshooting
kubectl --context <context> get pods -n kubemetal
kubectl --context <context> get application -A | grep kubemetal # check ArgoCD ownershipRelated: Architecture · Usage