REST API service for managing Kubernetes workspaces, built with the Goa framework.
The API provides:
- Workspaces - CRUD, start/stop/reset/reboot, logs, events, pod details
- Volumes - PVC management (create, list, delete)
- Images - List available workspace images with proxy configuration
- SshKeys - CRUD of user SSH public keys (used to seed VM guest access)
- Namespaces - List available Kubernetes namespaces (supports
_allfor cross-namespace listing) - Proxy - Reverse proxy to workspace web UIs with WebSocket support
- Console/exec bridges - WebSocket bridges to workspace terminals, VM serial consoles, VM noVNC displays, and web SSH sessions (single-session with take-over)
- Admin - Raw CRD browsing endpoints
- Health - Health check endpoint
- OpenAPI - Auto-generated OpenAPI 3.0 spec (JSON and YAML)
For spec.type: vm workspaces the API bridges WebSockets to KubeVirt
subresources (and guest sshd), using a session registry so each bridge is
single-session with an idle TTL and take-over consent:
GET /v1/workspaces/{name}/console— serial console (.../virtualmachineinstances/{name}/console)GET /v1/workspaces/{name}/vnc— noVNC display (.../virtualmachineinstances/{name}/vnc)GET /v1/workspaces/{name}/ssh— web SSH into the guest (dials the workspace Service; the browser supplies the private key transiently)GET/POST .../console/status,.../console/takeover,.../ssh/status,.../ssh/takeover— session status + take-overPOST /v1/workspaces/{name}/reboot— delete the VMI (persistent root disk preserved)
Desktop clients cannot read the HttpOnly kw-session cookie, so the API also
supports the loopback + PKCE flow from RFC 8252 ("OAuth 2.0 for Native Apps").
The client never talks to the IdP, so the flow works with whichever
authentication the administrator configured.
-
The client starts a loopback listener on an ephemeral port and opens the system browser at:
GET /auth/login?native_redirect=http://127.0.0.1:<port>/cb &code_challenge=<BASE64URL(SHA256(verifier))> &code_challenge_method=S256 &state=<opaque client nonce>native_redirectmust behttp, on the loopback literal127.0.0.1or[::1](notlocalhost, per RFC 8252 §8.3), with no query or fragment and no userinfo. Anything else is a400. OnlyS256is accepted.stateis optional but recommended: it is the client's own nonce, echoed verbatim in step 3 so the client can perform the RFC 6749 §10.12 check. It must be at most 256 unreserved characters (A-Z a-z 0-9 - . _ ~). The server's internal OIDC CSRF state is never echoed — the client has not seen it and could not check it. -
The API runs its normal OIDC flow. The native parameters ride through the round trip inside the HMAC-signed
kw-auth-statecookie. -
/auth/callbackmints the session token exactly as for a browser login but, instead of setting the cookie, redirects tonative_redirect?code=<code>&state=<client state>. The code is single-use, expires after 60s and is bound to the PKCE challenge. Failures before this point (IdP error, email not allowed) are reported to the same listener as?error=...&error_description=...&state=...so the client fails fast. -
The client exchanges it:
POST /auth/native/token {"code": "...", "code_verifier": "..."} 200 {"token": "...", "expires_at": 1234567890, "email": "...", "role": "..."}Failures return
400with{"error": "...", "message": "..."}whereerrorisinvalid_code,invalid_verifierorinvalid_request. -
The token is then used as
Authorization: Bearer <token>, which/auth/meand/auth/change-passwordaccept in addition to the session cookie.
GET /auth/config advertises support as "nativeAuth": {"enabled": true, "methods": ["loopback-pkce"]}.
Omitting the native parameters leaves the browser flow completely unchanged.
Codes are held in memory on the replica that issued them, so with multiple replicas the exchange must reach that replica; a failed exchange simply restarts the login.
The API is defined using Goa DSL in design/design.go. Running goa gen generates:
- HTTP server and client code
- OpenAPI 2.0 and 3.0 specifications
- Type definitions and encoders/decoders
Additional endpoints (logs, events, pod, proxy, admin) are implemented directly in cmd/kube_workspaces/http.go, along with the WebSocket bridges (exec/console/vnc/ssh) and their session registry.
# Regenerate code from design
go run goa.design/goa/v3/cmd/goa gen github.com/kube-workspaces/api/design
# Build
go build -o bin/kube-workspaces-api ./cmd/kube_workspaces/
# Run locally (uses current kubeconfig)
go run ./cmd/kube_workspaces/ --http-port=8090docker build -t kube-workspaces-api:latest -f Dockerfile .Requires Go 1.26+ (for k8s.io/client-go@v0.36.2 compatibility).
The API includes a built-in reverse proxy at /proxy/{namespace}/{name}/{path...}:
- Full WebSocket passthrough
- Location header rewriting for redirects
- Referer-based catch for escaped absolute-path requests
- No-op ServiceWorker at
/sw.js - Per-image proxy configuration (see
internal/proxy/proxy.go)
When creating workspaces, the API auto-injects default settings for known images:
codercom/code-server:latest- Adds--bind-addr 0.0.0.0:8080 --auth noneargs
| File | Description |
|---|---|
design/design.go |
Goa API design DSL |
cmd/kube_workspaces/http.go |
HTTP server setup, proxy, admin, WebSocket bridge routes |
cmd/kube_workspaces/main.go |
Entry point, client initialization |
internal/k8s/client.go |
CoreClient for pods, logs, events |
internal/k8s/workspace.go |
Workspace CR dynamic client |
internal/k8s/sshkey.go |
SshKey CR client |
internal/k8s/image.go |
Image CR client (workspace types, defaults) |
internal/auth/oidc.go |
OIDC login/callback, /auth/me, /auth/config |
internal/auth/native.go |
RFC 8252 native-app flow (loopback redirect validation, PKCE, code store) |
internal/exec/session.go |
Session registry (single-session, TTL, take-over) |
internal/exec/vmconsole.go |
VM serial console WebSocket bridge |
internal/exec/vmvnc.go |
VM noVNC display WebSocket bridge |
internal/exec/ssh.go |
Web SSH console WebSocket bridge |
internal/proxy/proxy.go |
Reverse proxy handler |
workspaces.go |
Workspace service implementation |
volumes.go |
Volume service implementation |
images.go |
Images service (static registry) |
sshkeys.go |
SshKeys service |
The API uses the current kubeconfig or in-cluster service account for Kubernetes access.
| Env Var | Description | Default |
|---|---|---|
KUBECONFIG |
Path to kubeconfig file | ~/.kube/config |
HTTP_HOST |
Listen address | localhost:8080 |
| Repository | Description |
|---|---|
| kube-workspaces/controller | Kubernetes controller (CRD reconciliation) |
| kube-workspaces/proxy | Workspace reverse proxy |
| kube-workspaces/frontend | Next.js web UI |
| kube-workspaces/deploy | Deployment manifests and documentation |
Apache License 2.0