Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ Run `task fixtures-check`, `task test`, and `task lint` before opening a pull re
## Design rules

Start with watch streams and authoritative resource views; add editing as an optional layer.
The [README](README.md) explains current capabilities and the native-fetch direction. Current API
examples must match this branch. The next native slice is read-only LIST/WATCH using the existing
lifecycle and state-input helper; editor cleanup, native editing and larger comparisons are separate.
Native transport and save-progress evaluation remain proposals until implemented.
The [README](README.md) explains current capabilities and how to choose a native or gateway source.
Current API examples must match this branch. Native viewing is read-only LIST/WATCH on the shared
connection lifecycle; native editing, native resume, editor cleanup and larger comparisons are
separate. Save-progress evaluation remains a proposal until implemented.

- Keep the core gateway free of `client-go`; Kubernetes integration belongs in `gateway/kube`.
- Keep the browser client framework-free and free of runtime dependencies.
Expand Down Expand Up @@ -62,9 +62,9 @@ the rule it protects.
|---|---|
| `task test` | Deterministic gateway, adapter, client, and shared-fixture coverage. |
| `task e2e-wire` | Real Go SSE bytes consumed by the TypeScript client over HTTP. |
| `task e2e-browser` | Native `EventSource` and unbundled ESM in Chromium. |
| `task e2e-browser` | Native `EventSource`, unbundled ESM and the native viewer example in Chromium. |
| `task cluster-facts` | Record observed Kubernetes behavior for the supported cluster version. |
| `task test-real-api` | Compose the save path against a real API server: gateway, host endpoint and browser store. |
| `task test-real-api` | Compose the save path and native viewing against a real API server: gateway or host proxy, host endpoint and browser store. |
| `task test-cluster` | Exercise the Kubernetes backend against a real API server. |

The cluster tasks need Docker and take longer; the fixture suites are the per-pull-request baseline.
Expand Down
72 changes: 56 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
Efficient live Kubernetes views for browser applications, with optional editing.

`krm-stream` helps applications watch Kubernetes resources while controllers and other users keep
changing them. Its gateway delivers a defined resource view, withholds selected values, suppresses
irrelevant updates and optionally shares upstream watches. Its headless TypeScript client adds connection lifecycle, recovery and
live state and reconciles incoming changes with local drafts when a page needs editing.
changing them. Its headless TypeScript client adds connection lifecycle, recovery and a live resource
store, reading native Kubernetes collections through a host proxy or projected views from the
gateway, and reconciles incoming changes with local drafts when a page needs editing. Its gateway
delivers a defined resource view, withholds selected values, suppresses irrelevant updates and
optionally shares upstream watches.

Your application supplies authentication, authorization policy, Kubernetes credentials, UI and writes.
The browser client has zero runtime dependencies and chooses no UI framework.
Expand Down Expand Up @@ -51,18 +53,52 @@ This is a live state feed: intermediate updates may be coalesced. See [watching

Start with the resource state and guarantees the page needs; framing is an implementation detail.

| Source | Use it for | Current support |
| Source | What the page receives | Connector |
|---|---|---|
| Native through a host proxy | Original Kubernetes resources without adopting the gateway; credentials stay on the host | A focused read-only fetch connector is [next work](docs/field-reports/third-our-identity.md#native-watch-connector) |
| Gateway | Named views, Secret-value redaction, suppression and optional upstream sharing | Supported today through the fetch/SSE connector |
| Native through a host proxy | Original authorized resources, with the shared client lifecycle and store | `connectNativeWatch`, read-only: LIST, then WATCH, and a fresh LIST on every reconnect |
| Gateway | Projected, redacted or suppressed views and optional upstream watch sharing | `connectResourceStream`, delivered over SSE |

Native is the straightforward starting point for hosts that already proxy Kubernetes. Gateway SSE
is the delivery format for projected views; its added capabilities remain useful. Both sources should
reuse connection state, errors, cancellation, bounded recovery and state application.
The first native slice lists then watches and re-lists on reconnect. Native editing, resume and a
larger comparison example follow separately. No source fallback may bypass a refused view.
Native is the straightforward starting point for hosts that already proxy Kubernetes. It delivers
what the proxy returns, Secret values and machinery fields included; it provides no projection,
redaction, suppression or watch sharing. Both connectors share connection state, errors,
cancellation, bounded recovery and state events. Use a separate store per source, scope and login
identity, and never fall back from a refused gateway view to native access.

## Watch a resource view today
## Watch native resources through a host proxy

```ts
import {
LiveResourceStore, readOnlyPolicy, applyStreamEvent,
connectNativeWatch, nativeCollectionURL,
} from "@configbutler/krm-stream";

const store = new LiveResourceStore(readOnlyPolicy);
const stopRendering = store.subscribe(() => renderResources(store));
const connection = connectNativeWatch(
// → /k8s/apis/apps/v1/namespaces/app/deployments?labelSelector=tier%3Dweb
nativeCollectionURL("/k8s", {
group: "apps", version: "v1", resource: "deployments", namespace: "app", labelSelector: "tier=web",
}),
event => applyStreamEvent(store, event),
);
const stopConnection = connection.subscribe(state => renderConnection(state.status));
connection.closed.catch(reportApplicationError);

// On view disposal:
stopRendering();
stopConnection();
connection.close();
```

The host mounts the API server's paths under a base such as `/k8s` and owns credentials, routing and
which collections a user may read. The connector lists the complete collection, delivers it as a
snapshot and becomes `live` once the WATCH from the collection's resourceVersion is accepted. Every
reconnect lists again, and the store prunes only when that snapshot completes. HTTP or in-stream 410
recovers within the bounded retry budget; 401, 403 and other 4xx are terminal. Slice 1 does not
paginate: a LIST that returns a continuation token is refused. The
[native viewer example](examples/native-viewer/README.md) runs this against `kubectl proxy`.

## Watch a gateway view

```ts
import {
Expand All @@ -88,7 +124,7 @@ stopConnection();
connection.close();
```

The current viewer uses `LiveResourceStore(readOnlyPolicy)`; a dedicated read-only store is deferred.
Viewers use `LiveResourceStore(readOnlyPolicy)`; a dedicated read-only store is deferred.
The connector delivers state events independently of editing. It uses same-origin cookies by default,
exposes connection state and bounded retries, and stops on terminal refusals. Apply each event
synchronously. The [client README](packages/krm-stream/README.md) explains lifecycle and errors.
Expand Down Expand Up @@ -134,11 +170,14 @@ arrays, and [saving edits safely](docs/saving.md) for the complete host-owned wr
```mermaid
flowchart LR
api["Kubernetes API"]
proxy["Your native proxy<br/>Credentials and routing"]
gateway["Go gateway<br/>Scopes, views and optional sharing"]
connector["Fetch connector<br/>State events and recovery"]
store["Resource store<br/>Live state and optional drafts"]
ui["Your list, viewer or form"]
save["Your save endpoint<br/>Authorize, validate and conditionally PATCH"]
api -->|"LIST and WATCH"| proxy
proxy -->|"Native watch JSON"| connector
api -->|"Snapshot and watch"| gateway
gateway -->|"SSE"| connector
connector --> store
Expand All @@ -148,7 +187,8 @@ flowchart LR
save --> api
```

The gateway runs inside your Go application. Per-user backends let Kubernetes authorize the caller's
A page uses one source per store: the native proxy or the gateway. The gateway runs inside your
Go application. Per-user backends let Kubernetes authorize the caller's
reads; a shared backend uses a service identity and requires checks for each subscriber. Sharing
reduces duplicate upstream work; access controls and host limits govern who can consume it.
Gateway upstream continuation and improved save progress during suppressed churn are proposed work.
Expand All @@ -168,7 +208,7 @@ such as `spec`, `status` or ConfigMap `data`. Custom resources follow the same c
|---|---|
| `github.com/ConfigButler/krm-stream/gateway` | Dependency-free Go stream gateway and SSE handler |
| `github.com/ConfigButler/krm-stream/gateway/kube` | Optional client-go backend and SubjectAccessReview authorizer |
| `@configbutler/krm-stream` | Dependency-free ESM connector and resource/editor store |
| `@configbutler/krm-stream` | Dependency-free ESM native and gateway connectors, and the resource/editor store |
| [spec/v1.md](spec/v1.md) and [conformance](conformance/README.md) | Shared normative contract and executable fixtures |

## Guides
Expand All @@ -178,7 +218,7 @@ such as `spec`, `status` or ConfigMap `data`. Custom resources follow the same c
- [Editor state model](docs/client-state-model.md): drafts, conflicts, redactions and arrays.
- [Saving](docs/saving.md): conditional writes, recovery and user-facing outcomes.
- [Authorization](docs/auth.md) and [operations](docs/operations.md): identity, revocation and runtime limits.
- [Examples](examples/README.md): browser, conditional editor, recovery recipes and Vue integration.
- [Examples](examples/README.md): native viewer, gateway browser demo, conditional editor, recovery recipes and Vue integration.
- [Delivery plan](docs/proposals/0006-stream-and-save-implementation-plan.md): completed work, open work, ordering and dependencies.
- [Upgrading from 0.7](docs/migrating.md) and [releasing](docs/releasing.md).

Expand Down
2 changes: 1 addition & 1 deletion Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ tasks:
# The save path, composed against a real API server: the real gateway, a real host endpoint and the
# real browser store (run by node).
test-real-api:
desc: "Real-API save composition: status churn, overlapping recovery and the UID race (proposal 0006)."
desc: "Real-API composition: save path (status churn, overlapping recovery, UID race) and native viewing through a host proxy."
deps: [cluster-up]
dir: gateway/kube
env:
Expand Down
5 changes: 3 additions & 2 deletions conformance/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# conformance — the shared contract, executable

These fixtures verify the current gateway SSE contract and browser state application. Editing cases
add local drafts to that live-state contract; viewing does not require edit controls. Requested native
watch support will need recovery tests of its own, not an assumption that these SSE fixtures prove it.
add local drafts to that live-state contract; viewing does not require edit controls. Native watch
support has recovery tests of its own (`packages/krm-stream/test/native.test.ts`); these SSE fixtures
do not prove it.

**One YAML file describes one scenario end to end**: the Kubernetes watch input, the gateway's wire
output, and the client's resulting state after applying events and local edits. The Go and TypeScript
Expand Down
9 changes: 5 additions & 4 deletions docs/adopting.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ and browser reconciliation; your application owns identity, authorization policy
Choose scope, projection and sharing separately using [watching resources](why-a-gateway.md).
Choose the source for the guarantees the page needs: native access retains original resources through
an existing host proxy; the gateway adds projected views, redaction, suppression and optional sharing,
delivered over SSE. Both should reuse a fetch-based frontend lifecycle.
delivered over SSE. Both connectors share one fetch-based frontend lifecycle.

The [first native slice](field-reports/third-our-identity.md#slice-1-a-native-viewer) is a small read-only
LIST/WATCH connector with re-list recovery and a viewer. Native editing is separate and does not block
that slice. Today the supported connector uses the gateway wiring below.
For native access, a host that already proxies Kubernetes needs no gateway: point
`connectNativeWatch` at a collection behind that proxy, as in the [README](../README.md#watch-native-resources-through-a-host-proxy)
and the [native viewer example](../examples/native-viewer/README.md). It is read-only and re-lists on
every reconnect; native editing is separate. The steps below wire the gateway source.

## 1. Mount the stream endpoint

Expand Down
2 changes: 1 addition & 1 deletion docs/alternatives.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ and optional draft reconciliation. Choose tools according to the part your appli

| Need | Relevant approach | Where krm-stream fits |
|---|---|---|
| Native watch through an existing browser proxy | Host proxy plus a native fetch/watch consumer | A read-only native connector with shared lifecycle is next work; gateway SSE delivers the supported projected source today. |
| Native watch through an existing browser proxy | Host proxy plus a native fetch/watch consumer | `connectNativeWatch` lists and watches through the proxy with the shared lifecycle and store, read-only; the gateway adds projected views over SSE. |
| Kubernetes API access from a server | Kubernetes client libraries and raw watches | The Go adapter uses `client-go`; the gateway adds projections and browser snapshot framing. |
| A complete Kubernetes UI | Dashboard applications and their plugin APIs | krm-stream supplies state and transport; the host builds the UI. |
| Review and deliver configuration packages | KRM package and GitOps systems | The host can record accepted writes in its delivery workflow. |
Expand Down
5 changes: 3 additions & 2 deletions docs/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ Prefer a per-user backend: Kubernetes RBAC is then the boundary by construction.
is in [adopting](adopting.md). Watch sharing is an efficiency choice; it never grants a subscriber
the service identity's access. Full/spec Secret redaction also does not replace scope authorization.

These settings describe the current gateway. The requested native fetch connector uses a host proxy
and the same session boundary. Native retains original authorized resources; gateway SSE delivers
These settings describe the current gateway. The native connector reads through a host proxy behind
the same session boundary; the proxy, not the browser, holds Kubernetes credentials and authorizes
each collection. Native retains original authorized resources; gateway SSE delivers
projected views. Neither source may bypass refusal or extend session lifetime. The first native
slice is read-only and must stop on terminal authentication failure, with a new connection after
access is restored; native editing is separate.
Expand Down
9 changes: 4 additions & 5 deletions docs/client-state-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@ The connector consumes and delivers state events independently of the editor.

Do not mutate a `draft()` return value directly: reads are detached copies, and that bypasses edit
policy and notifications. Independent sources/scopes need separate stores. Switching identity or view
must not silently reuse drafts, redactions or snapshot state. Native watch integration is
[requested](field-reports/third-our-identity.md#native-watch-connector). The first native slice uses the
store read-only; this guide's editing and save examples currently use gateway views. Native editing
requires a source-bound policy and host read/write contract in a later slice.
must not silently reuse drafts, redactions or snapshot state. The native connector,
`connectNativeWatch`, uses the store read-only; this guide's editing and save examples use gateway
views. Native editing requires a source-bound policy and host read/write contract in a later slice.

Keep the existing standalone `applyStreamEvent(store, event)` for the native viewer. A bound store
The native viewer uses the existing standalone `applyStreamEvent(store, event)`. A bound store
method is proposed editor cleanup in proposal 0009, not a current API or a prerequisite for native
transport. No new store is needed.

Expand Down
Loading
Loading