Streaming & Subscriptions

Live data — pod logs, events, workload status transitions, helm release progress — streams to the studio over the standard /graphqlws WebSocket.

Overview

Every long-lived stream in the kubernetes subsystem rides the same transport the rest of Bosca uses for subscriptions: GraphQL over WebSocket using the graphql-transport-ws protocol on the studio's existing /graphqlws endpoint. The subscriptions live flat on the GraphQL Subscription root (k8sPodLogs, k8sEvents, k8sWorkloadStatus, k8sHelmReleaseStatus) — graphql-java's SubscriptionExecutionStrategy requires the root subscription field to return a Publisher/Flow, so the kubernetes fields don't nest under a kubernetes sub-root the way queries and mutations do.

Pod Logs — k8sPodLogs

Stream container logs for a specific pod. Supports the standard log toggles operators expect:

  • container — pick a single container in a multi-container pod (optional; defaults to the first).
  • tailLines — backfill the last N lines before live tail begins (default 200).
  • follow — keep the stream open as new lines arrive (true) or close after backfill (false).

The studio's useK8sPodLogs composable buffers lines client-side, supports pause/resume, and re-subscribes automatically when the cluster, namespace, pod, or container changes. Each log line carries a parsed timestamp (RFC-3339 from the kubelet) and a best-effort log level inferred from common log formats — JSON "level":"ERROR", bracketed [WARN], level=error KV pairs, and word-start markers like panic: / fatal:.

Events — k8sEvents

Stream new Kubernetes events as the controller observes them. Optional namespace narrows the watch. The controller maintains a single watch per (cluster, namespace) and fans out to subscribers, so multiple studio sessions watching the same scope don't multiply load on the cluster's API server.

Workload Status — k8sWorkloadStatus

Stream replica-readiness transitions and restart counts for a single workload. Mostly useful during scale/restart operations and rolling deploys — the studio's workload drawer opens this subscription on mount and tears it down on close, so background watches don't accumulate.

Helm Release Status — k8sHelmReleaseStatus

Stream PENDINGDEPLOYED / FAILED / SUPERSEDED / UNINSTALLED transitions for a single helm release. The controller watches the release's sh.helm.release.v1.<release>.v<rev> Secrets (label owner=helm, name=<release>) and emits a new record whenever a revision is written or an existing revision's status changes.

The studio's install/upgrade wizard opens this subscription before firing the mutation, so the first transition arrives within milliseconds of the helm subprocess starting. The composable auto-closes on terminal status to avoid leaving zombie watches behind.

Subscription Lifecycle

All four subscriptions follow the same lifecycle on the controller side:

  1. Subscribe → admin-check on the resolver thread, fail closed.
  2. Open upstream watch (fabric8 informer for k8s events / workload status / helm secrets, or pod log streaming HTTP body from the kubelet).
  3. Stream records as NDJSON over an internal HTTP /stream/... path between bosca-server and the controller.
  4. Bosca-server wraps the NDJSON as GraphQL subscription events and forwards them to the studio over /graphqlws.

Streams have a 5-minute server-side cap on the controller's HTTP body to bound long-lived connections. The studio's composables detect completion and re-subscribe transparently so this is invisible to operators.

Authentication

Subscriptions inherit the same admin-only gating as queries and mutations — see Security Model. The JWT travels in the connection_init payload of the WebSocket handshake; the controller re-verifies admin membership on every upstream HTTP request, not just at subscribe time, so credential revocation between subscribe and the next read closes the stream from the controller side.