k8boss-admin
● read-only by default

A Kubernetes console that
never lies about what it did.

k8boss-admin browses every resource a cluster serves, and changes them through a path that checks the permission first, dry-runs the write, shows you the diff, waits for a confirmation, and records what happened — including what failed. It's the web UI for the work you'd otherwise do with kubectl across several clusters, built on the assumption that the dangerous part was never reading.

Not a monitoring tool — no metrics storage, no alerting Not a GitOps controller — no reconcile loop, no desired state Not a security posture product — correlates, never claims "enforced" Not a cache — every page is a live read

The safety model

One function. Every write goes through it.

Scale, restart, suspend, rollback, cordon, drain, create, replace, delete — every mutation in the console, no exceptions, in this order:

01
Preflight
A SelfSubjectAccessReview for the exact verb, group, resource, namespace and name. A denial names the missing permission — never a bare "forbidden".
02
Dry-run
dryRun=All on the same call. The projection comes from the API server's own admission chain — webhooks, defaulting, quota — not a simulation this console wrote.
03
Diff
A unified diff with server bookkeeping normalised out. Nothing to change? The UI says so instead of offering a confirm button.
04
Confirm
applied: true appears only when the write actually reached the cluster. A successful dry run is applied: false — always.
05
Audit
Append-only, hash-chained. Every terminal state — including the denials, the conflicts and the failures.
empty is never blind — unavailable[] + partial: true a number never derived is null, never 0 PUT carries the resourceVersion you were looking at drain plans before it acts — evict / skip / blocked, per pod

Product tour

What confirming a write actually looks like

Four moments from the console, redrawn here to show the shape of the interaction — not a chart, a permission check that names itself.

workloads / payments-api / scale
verbpatch
resourcedeployments/scale
dry runAll
spec:
- replicas: 3
+ replicas: 6
status:
observedGeneration: 41
applied: false — dry run
rbac / grant role → alice
roleedit
wildcard verbno
create rolebindingsno
create pods/execyes
impersonateno
create pods/exec reads every Secret mounted into a pod. edit hands over the namespace's credentials without a rule that mentions Secrets at all.
nodes / worker-3 / drain
redis-cache-0evict
billing-worker-2skip · tolerates 300s
postgres-primary-0blocked · PDB
The blocked pod stops the drain before the node is cordoned. "Drained" over a pod that never evicted is the sentence that gets a machine terminated with a database on it.
audit / recent
14:02:11 eren@ops scaled deployments/payments-api to 6 — applied a91f…c02e
14:01:48 ci-bot deleted namespace staging-preview-88 — rbac_denied 7bd2…91aa
13:58:03 eren@ops approved CSR for CN=jenkins-agent — applied 3c40…f118
Every row is hash-chained to the one before it — not tamper-proof, tamper-evident. A denial is recorded exactly like a success.

Under the read/write split

A console that answers "unknown" instead of guessing

A sample of the screens that exist because the obvious version of them would have been confidently wrong.

Reads that refuse to guess

Why is this pod Pending

Quotes the scheduler's own event, with the age of the attempt behind it, and a node table that only ever rules a node out — never claims one fits.

Network policy, as sentences

policyTypes: [Ingress] with no egress section restricts nothing. The page says that outright instead of echoing YAML people misread.

The certificate behind each route

Expiry, and whether the SANs actually cover the hostname — a green row everywhere else in Kubernetes and a browser that refuses to connect.

What a PodDisruptionBudget covers

Counts live coverage from the pods it actually selects. 0 pods is a finding, not a blank.

Will this workload be admitted

Does the ResourceQuota arithmetic before you submit, and names the one bound that's tight — including the compulsory-resource rule a 403 never mentions.

Cluster status, five ways

Leader leases, APIService availability, CRD establishment, webhook backends, kubelet skew — no single verdict, because a rollup that could not read one signal should not report the others as healthy.

Writes that show their work

Approve a CSR

Decodes the PKCS#10 request and shows what it asks to become — an org of system:masters reads as cluster-admin, not as a routine renewal.

Delete a namespace

Reads every namespaced kind first: claims that get destroyed, addresses that stop answering, webhooks left without a backend. The diff shows the wrong object on purpose.

Taint a node

NoExecute names the pods it deletes before you confirm — and says plainly that a PodDisruptionBudget does not stop it, unlike a drain.

Grow a claim

Refuses an arithmetic shrink before sending anything, and separates "the request changed" from "the volume grew" — often two different moments.

Act as the signed-in operator

Per cluster, per session: the API server checks permissions and writes its own audit log as the actual person — closing the gap ADR-0007 spent a document being honest about.

One YAML editor, everywhere

Same manifest editor for every kind, including your CRDs — with a warning, not a block, when a scalar reads differently than kubectl would send it.

What it ships, precisely

Two bundles. That's the number being defended.

k8boss-admin is not a deployment engine — with two stated exceptions, and they are different acts. Both go through the same write funnel as everything else: no reconcile loop, no desired state, no watch. Both are off by default.

§14

A pinned HAProxy ingress controller

An exposure written into a cluster with no controller is an object that routes nothing while looking created. Eight objects, one write funnel, live status like every other page.

ADMIN_ROUTER_MANAGE_ENABLED
§33

Operator Lifecycle Manager itself

Vendored byte-for-byte from upstream's release and pinned by SHA-256. Twenty-six writes in two phases, because §16's operator portal on a cluster with no OLM is empty, correct and useless.

ADMIN_OLM_INSTALL_ENABLED
What creating an OLM Subscription is not: §16's operator portal ships no catalog, pins no image and installs nothing. It writes one object into an API the cluster already serves — exactly what the YAML editor could — and Operator Lifecycle Manager, the cluster's own software, does the installing afterwards. applied: true there means one Subscription exists, and never that an operator is running.

Shape of the thing

FastAPI backend, React/PatternFly SPA

Every write is written once, in one place, and everything else calls it.

app/api/

Thin FastAPI routers — parse, call, envelope. Logic lives below this layer, never in it.

app/resources/ · app/services/

Discovery, generic read/YAML, and the typed read models — workload rows, scheduling, network correlation, TLS coverage, quota arithmetic.

app/admin/

Every write. The single funnel — gate → preflight → apply → diff → audit — plus every feature's apply_fn.

app/audit/

Append-only, hash-chained trail: record(), query(), verify(), export.