Keep the Minikube corp-ui available on this Mac at http://127.0.0.1:3000 without requiring a manually started terminal command. Access remains workstation-local and is not exposed to the LAN or public internet.
The active Minikube profile uses the Docker driver on macOS. A control connection to the existing user-service NodePort timed out from the host, confirming that a Kubernetes NodePort alone is not host-reachable in this profile. Docker port mappings cannot be added to the running Minikube container, so making NodePort host-reachable would require recreating the cluster and its persistent volumes.
The current listener on port 3000 is a manual process:
kubectl --cluster=minikube -n aegida-services port-forward service/ai-control-chat-ui 3000:3000
The persistent configuration will replace this process without changing the Minikube topology.
Keep ai-control-chat-ui as a ClusterIP Service and manage a fixed kubectl port-forward with a per-user macOS LaunchAgent. This avoids cluster recreation, binds only to loopback, and lets launchd restore access after login, pod replacement, or a transient Minikube outage.
The LaunchAgent will run the equivalent of:
/usr/local/bin/kubectl \
--context=minikube \
--namespace=aegida-services \
port-forward \
--address=127.0.0.1 \
service/ai-control-chat-ui \
3000:3000
The fixed executable path matches this Mac. The explicit context, namespace, address, Service, and port prevent the agent from silently targeting a different cluster or exposing the listener beyond localhost.
Add ops/macos/com.aegida.corp-ui-port-forward.plist with:
com.aegida.corp-ui-port-forward;RunAtLoad enabled;KeepAlive enabled so a lost pod connection or unavailable cluster is retried;/tmp that contain only kubectl port-forward lifecycle messages.Update k8s/README.md with installation, status, verification, log, restart, and uninstall commands. The checked-in plist is copied to ~/Library/LaunchAgents/com.aegida.corp-ui-port-forward.plist and loaded into the current GUI domain with launchctl bootstrap gui/501.
No Kubernetes Service or Deployment change is required. Keeping the Service private preserves the existing security boundary.
Before changing process state, resolve the listener on TCP port 3000 and inspect its complete command. Continue only when exactly one listener exists and it matches the known manual kubectl port-forward for ai-control-chat-ui. If the listener is absent, installation continues. If the port belongs to another process or the command differs, stop and request user direction.
For the matching manual listener, send SIGTERM to that exact PID and confirm port 3000 is released. Validate the plist with plutil -lint, copy it to the per-user LaunchAgents directory, bootstrap it, and use launchctl kickstart to start it immediately. Do not use broad process matching or force-kill signals.
The LaunchAgent depends on Minikube being running and on the ai-control-chat-ui Service having a ready endpoint. When either dependency is unavailable, kubectl exits and launchd retries after the throttle interval. When the pod is replaced, the active port-forward exits and is re-established automatically against the Service.
If port 3000 is occupied by an unrelated process after installation, the agent remains failed/retrying and records the bind error. Operators resolve the conflicting listener and kickstart the agent; the configuration never chooses a different port silently.
Installation is successful only when all of the following hold:
plutil -lint accepts both the repository and installed plist;launchctl print gui/501/com.aegida.corp-ui-port-forward reports a running job;127.0.0.1:3000 and its command belongs to the LaunchAgent-managed kubectl;curl --fail http://127.0.0.1:3000/api/health returns {"status":"ok"} with HTTP 200;corp-ui Deployment remains 1/1 ready with zero container restarts;Unload the job with launchctl bootout gui/501 ~/Library/LaunchAgents/com.aegida.corp-ui-port-forward.plist, then remove that exact installed plist. This stops permanent localhost exposure without changing or deleting any Kubernetes resource. A one-off manual port-forward remains available as a fallback.