Captain — a ship's wheel with seven handles and a gold anchor hub

Take the helm of your cluster.
From your iPhone.

Captain is a Kubernetes client for iOS that dynamically discovers every resource type in your cluster — CRDs included — and lets you browse, edit and delete any of them. With native auth for EKS, GKE and AKS. No backend, no agent, no compromise.

iOS · React Native (Expo) TypeScript + Swift Works with any conformant cluster 100% on-device

Everything the API server knows, in your pocket

Captain doesn't ship a hard-coded list of resource kinds. It asks your cluster what it can do — and renders all of it.

🔍

Dynamic resource discovery

All resource types are discovered live via /api/v1 and /apis/… — Pods, Deployments and every CRD your cluster defines. If the API server knows it, Captain shows it.

🗂️

Curated overview

A Lens-style browser with collapsible categories: Cluster, Workloads, Config, Network, Storage, Access Control — and Custom Resources grouped automatically by API group.

✏️

Read, edit, delete

Lists with namespace filter, pagination and search. Full YAML detail view, an in-app YAML editor (PUT/replace) and delete with confirmation.

🖥️

Exec terminal

Run one-shot commands in any container over the kubectl exec WebSocket protocol (v4.channel.k8s.io) — natively, with full cluster-CA trust and quick-command chips.

🔌

Port-forwarding

A local TCP listener (Network.framework) bridges onto the cluster's portforward WebSocket endpoint. Active forwards show up in the Browse tab and can be stopped individually.

📈

Live metrics

Node and pod usage via the metrics-server API (metrics.k8s.io): CPU/memory bars in the node list, a CPU column in the pod list. Hides itself automatically when metrics-server isn't installed.

🔥

Prometheus integration

Captain auto-discovers Prometheus in the cluster and queries it through the API-server proxy — no extra networking or auth. One-hour CPU/memory sparklines and currently firing alerts, sorted by severity.

📲

Kubeconfig & QR onboarding

Paste a kubeconfig, pick contexts, done — or scan it as a QR code. Exec plugins (aws, gke-gcloud-auth-plugin, kubelogin) are detected and mapped to the matching native auth method.

🎨

Soft Bridge design

A dark indigo theme with soft gradient cards, iOS-Settings-style squircle icons, a health-ring dashboard, a floating tab bar and bottom sheets for cluster and namespace switching.

Native auth for every major cloud

No CLI, no exec plugins, no jump host. Captain implements each provider's token flow directly on the device.

AWS EKS
SigV4-presigned STS tokens

Captain produces k8s-aws-v1.… tokens on-device — the exact equivalent of aws eks get-token — and refreshes them automatically. Supports temporary STS credentials with a session token.

Google GKE
OAuth 2.0 with PKCE

Sign in with Google using an iOS OAuth client. Refresh tokens are stored securely and renewed automatically.

Azure AKS
Microsoft Entra ID OAuth 2.0 (PKCE)

Authenticates against the AKS AAD server app for clusters with managed Entra ID integration.

Bearer
Bearer tokens

Use ServiceAccount tokens or any other bearer token for self-hosted and on-prem clusters.

mTLS
Client certificates

PKCS#12 client certificates, handled natively via URLSession. The native KubeHttp module also validates server certificates against your cluster's own CA — essential for EKS, GKE and AKS endpoints.

Getting started

Requirements: macOS with Xcode 16+, Node 20+, CocoaPods.

1Clone and build

# clone the repository
git clone https://github.com/dpfaffenbauer/captain.git
cd captain

npm install
npx expo run:ios   # builds the iOS app incl. the native KubeHttp module

2Create a token (any cluster)

kubectl create serviceaccount captain -n kube-system
kubectl create clusterrolebinding captain \
  --clusterrole=cluster-admin \
  --serviceaccount=kube-system:captain
kubectl create token captain -n kube-system --duration=8760h

3Onboard in seconds with QR

kubectl config view --minify --raw | qrencode -t png # scan it in Captain
Note: Custom cluster CAs and client certificates require a development build (npx expo run:ios). In Expo Go, Captain falls back to fetch(), which only works against API servers with a publicly trusted certificate. For a physical device, set a signing team in Xcode or run npx expo run:ios --device.

Architecture

A TypeScript app on Expo Router, with a small native Swift module where iOS networking needs to go beyond what JavaScript can do.

app/                          Screens (expo-router)
  index.tsx                   Cluster overview
  cluster-form.tsx            Add/edit clusters incl. cloud auth
  kubeconfig-import.tsx       Kubeconfig import
  cluster/[id]/index.tsx      Resource types (discovery, grouped, search)
  cluster/[id]/list.tsx       Resource lists (namespace, pagination, search)
  cluster/[id]/item.tsx       YAML detail, editor, delete
src/
  auth/                       EKS SigV4, Google/Azure OAuth, token cache
  kube/                       Transport, discovery, CRUD, kubeconfig parser,
                              metrics-server + Prometheus
  state/, storage/, ui/, util/
modules/kube-http/            Native iOS module (Swift): TLS with custom CA,
                              insecure-skip-verify, mTLS via PKCS#12

Security by architecture

There is no backend. Captain talks directly to your API server — nothing else ever sees your cluster.

🔐

Keychain only. All credentials are stored exclusively in the device's iOS Keychain via expo-secure-store.

📜

Real TLS validation. Server certificates are checked against the cluster CA through a native URLSession delegate. Skipping TLS is possible — but clearly flagged as insecure.

🛰️

Direct connection. No proxy, no relay, no telemetry. The app speaks to the Kubernetes API server and nothing in between.