Files
k8s/ops/zipdoc_gen.go
zeekay a7ed7a7c69 Name the thing instead of calling it a door
One word stood for several: the MCP server, one address that accepts requests,
the single way in, and the boundary a credential is validated at. Each site now
names its own sense — endpoint, entry point, the MCP server, the edge, address,
relay — or, where none of those fit, takes the word the surrounding code already
uses.

Prose and comments only, 1:1 line for line. Identifiers keep their names: CSS
classes, DOM ids, struct fields, enum variants, config values and the doc
comments that must open with the identifier they document.
2026-08-23 13:50:00 -07:00

753 lines
84 KiB
Go

// Code generated by zipdoc; DO NOT EDIT.
package ops
import (
"encoding/json"
"github.com/zap-proto/zip"
)
func init() {
zip.Describe("DELETE /v1/k8s/clusters/:cluster", zip.Doc{
Description: "Detaches a cluster from the calling org and destroys its sealed\ncredential.\n\nNothing in the cluster is touched: this removes Hanzo's ability to reach it, not\nthe cluster itself. Deleting a name that is not registered reports removed=false\nrather than failing, so a repeated deregistration is safe.",
Fields: map[string]string{
"ClusterRef.cluster": "Cluster is the registered cluster's name — \"hanzo-k8s\", \"lux-k8s\",\n\"zoo-k8s\", \"bootnode-k8s\", or any name an org registered its own cluster\nunder. It is resolved against the CALLING ORG's registry, so two orgs may\nuse the same name for two different clusters and neither can reach the\nother's.",
},
Response: json.RawMessage(`{"removed":true}`),
})
zip.Describe("DELETE /v1/k8s/namespaces/:namespace", zip.Doc{
Description: "Deletes a namespace and everything in it.\n\nThis is the highest blast radius in the whole API: Kubernetes deletes a namespace\nby garbage-collecting every object inside it, including PersistentVolumeClaims,\nwhich for a Delete-reclaim StorageClass destroys the tenant's data. There is no\nundo and no confirmation this API can offer that would make one.\n\nIt is therefore its own named operation, gated separately from every other write,\nand it is refused outright for a registration bounded to a namespace set: a scoped\ncredential exists precisely because the cluster is shared, and \"delete a namespace\non a shared cluster\" is not an authority a tenant holds.",
Fields: map[string]string{
"NamespaceRef.cluster": "Cluster is the registered cluster the namespace lives on.",
"NamespaceRef.namespace": "Namespace is the namespace's name.",
},
Response: json.RawMessage(`{"removed":true}`),
})
zip.Describe("DELETE /v1/k8s/resources/:kind/:namespace/:name", zip.Doc{
Description: "Deletes one custom resource of a registered kind.\n\nDeleting an object is not deleting its data: a workload's App CR is desired state and\ncan be recreated freely, while the PersistentVolumeClaim it mounted holds the only\ncopy of the tenant's data and is a different lifetime. Nothing here deletes a claim.",
Fields: map[string]string{
"ResourceRef.cluster": "Cluster is the registered cluster the object lives on.",
"ResourceRef.kind": "Kind is the registered kind.",
"ResourceRef.name": "Name is the object's name.",
"ResourceRef.namespace": "Namespace is the object's namespace; empty for a cluster-scoped kind.",
},
Response: json.RawMessage(`{"removed":true}`),
})
zip.Describe("GET /v1/k8s/clusters", zip.Doc{
Description: "Returns every Kubernetes cluster the calling org may reach.\n\nThis is the fleet's ONE cluster list. A row is a real registration — a name, the\nprovider it came from, its apiserver endpoint and the node and accelerator counts\nmeasured when it was attached — and there is no such thing as an unregistered\ncluster this API can see. The credential is never part of a row: it is sealed in\nKMS and resolved only at the moment an op uses it.\n\nThe list is scoped by the caller's org and by nothing the caller can type, so a\ntenant sees its own clusters and no others.",
Fields: map[string]string{
"Registered.namespaces": "Namespaces bounds what this registration may address. Empty means the\nwhole cluster, which is what an org registering its OWN cluster gets.",
"Registered.org": "Org is which org OWNS this cluster. On a reply that is a property of the\nrecord, not a claim by the caller.",
},
Response: json.RawMessage(`{"clusters":[{"name":"hanzo-k8s","org":"hanzo","provider":"doks","endpoint":"https://k8s.example.com","nodes":6,"nvidiaGpu":8,"amdGpu":0,"registered":"2026-07-30T00:00:00Z","default":true}]}`),
})
zip.Describe("GET /v1/k8s/clusters/:cluster", zip.Doc{
Description: "Returns one registered cluster.\n\nA name the calling org has not registered answers 404 — including a name another\norg HAS registered, which is indistinguishable from one that does not exist. That\nis deliberate: a distinguishable refusal would let a tenant enumerate other\ntenants' cluster names one guess at a time.",
Fields: map[string]string{
"ClusterRef.cluster": "Cluster is the registered cluster's name — \"hanzo-k8s\", \"lux-k8s\",\n\"zoo-k8s\", \"bootnode-k8s\", or any name an org registered its own cluster\nunder. It is resolved against the CALLING ORG's registry, so two orgs may\nuse the same name for two different clusters and neither can reach the\nother's.",
"Registered.namespaces": "Namespaces bounds what this registration may address. Empty means the\nwhole cluster, which is what an org registering its OWN cluster gets.",
"Registered.org": "Org is which org OWNS this cluster. On a reply that is a property of the\nrecord, not a claim by the caller.",
},
Response: json.RawMessage(`{"name":"lux-k8s","org":"lux","provider":"byo","endpoint":"https://k8s.lux.example","nodes":12,"nvidiaGpu":32,"amdGpu":0,"registered":"2026-07-30T00:00:00Z","default":false}`),
})
zip.Describe("GET /v1/k8s/clusters/:cluster/status", zip.Doc{
Description: "Reports whether a registered cluster answers right now.\n\nThe probe is the apiserver's version endpoint, because that is the one read every\ncredential can make regardless of what RBAC it was granted: a registration bounded\nto one namespace would fail a namespace list and look dead while being perfectly\nhealthy. So this answers \"is the credential live and the apiserver reachable\",\nwhich is the question a board asks, and the ops that need a permission ask for\nthat permission themselves.\n\nA cluster that does not answer is connected=false with the reason verbatim — never\nan error, because \"this cluster is down\" is a successful answer to this question.",
Fields: map[string]string{
"ClusterRef.cluster": "Cluster is the registered cluster's name — \"hanzo-k8s\", \"lux-k8s\",\n\"zoo-k8s\", \"bootnode-k8s\", or any name an org registered its own cluster\nunder. It is resolved against the CALLING ORG's registry, so two orgs may\nuse the same name for two different clusters and neither can reach the\nother's.",
"Reachable.connected": "Connected is whether the apiserver answered.",
"Reachable.reason": "Reason is why it did not, verbatim. Absent on success.",
"Reachable.version": "Version is the server version string, when it answered.",
},
Response: json.RawMessage(`{"connected":true,"version":"v1.33.1"}`),
})
zip.Describe("GET /v1/k8s/jobs", zip.Doc{
Description: "Returns batch Jobs with a rolled-up phase.\n\n`phase` is one word — running, succeeded or failed — computed here rather than left\nto every caller to derive from three counters. Three consumers deriving it three ways\nis three chances to read a failed job as merely unfinished.",
Fields: map[string]string{
"Job.phase": "Phase is \"running\", \"succeeded\" or \"failed\" — the one word a caller wants,\nrolled here rather than re-derived from three counters by every consumer.",
"Selector.cluster": "Cluster is the registered cluster to read.",
"Selector.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"Selector.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
},
Response: json.RawMessage(`{"jobs":[{"namespace":"hanzo","name":"build-1a2b","active":0,"succeeded":1,"failed":0,"phase":"succeeded","startedAt":1785110400,"endedAt":1785110460}]}`),
})
zip.Describe("GET /v1/k8s/jobs/:namespace/:name", zip.Doc{
Description: "Returns one batch Job.",
Fields: map[string]string{
"Job.phase": "Phase is \"running\", \"succeeded\" or \"failed\" — the one word a caller wants,\nrolled here rather than re-derived from three counters by every consumer.",
"NamedIn.cluster": "Cluster is the registered cluster the object lives on.",
"NamedIn.name": "Name is the object's name.",
"NamedIn.namespace": "Namespace is the object's namespace.",
},
Response: json.RawMessage(`{"namespace":"hanzo","name":"build-1a2b","active":0,"succeeded":1,"failed":0,"phase":"succeeded","startedAt":1785110400,"endedAt":1785110460}`),
})
zip.Describe("GET /v1/k8s/metrics/pods", zip.Doc{
Description: "Returns live CPU and memory use for pods, from metrics.k8s.io.\n\n`available` is the field that carries the honesty. metrics-server is absent from most\nof our clusters, and a cluster without it answers this call with an error — which,\nrendered as zeroes, tells an operator that a busy namespace is idle. So a missing\nmetrics API is reported as available=false with no rows, and a caller must render\n\"unmeasured\" rather than \"0m\".",
Fields: map[string]string{
"PodMetric.cpuMilli": "CPUMilli is CPU use in millicores; MemoryBytes is working-set memory.",
"Selector.cluster": "Cluster is the registered cluster to read.",
"Selector.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"Selector.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","namespace":"acme-prod"}`),
Response: json.RawMessage(`{"pods":[{"namespace":"acme-prod","name":"api-7d9f-2xk","cpuMilli":250,"memoryBytes":536870912}],"available":true}`),
})
zip.Describe("GET /v1/k8s/namespaces", zip.Doc{
Description: "Returns the namespaces the calling org's registration may address.\n\nFor a registration that owns the whole cluster this is every namespace on it. For\none bounded to a namespace set it is that set, read one namespace at a time — so a\nscoped tenant learns nothing about its neighbours, not even how many there are.",
Fields: map[string]string{
"ClusterRef.cluster": "Cluster is the registered cluster's name — \"hanzo-k8s\", \"lux-k8s\",\n\"zoo-k8s\", \"bootnode-k8s\", or any name an org registered its own cluster\nunder. It is resolved against the CALLING ORG's registry, so two orgs may\nuse the same name for two different clusters and neither can reach the\nother's.",
},
Response: json.RawMessage(`{"namespaces":[{"name":"acme-prod","phase":"Active","labels":{"managed-by":"hanzo-cloud"}}]}`),
})
zip.Describe("GET /v1/k8s/namespaces/:namespace", zip.Doc{
Description: "Returns one namespace.",
Fields: map[string]string{
"NamespaceRef.cluster": "Cluster is the registered cluster the namespace lives on.",
"NamespaceRef.namespace": "Namespace is the namespace's name.",
},
Response: json.RawMessage(`{"name":"acme-prod","phase":"Active","labels":{"managed-by":"hanzo-cloud"}}`),
})
zip.Describe("GET /v1/k8s/nodes", zip.Doc{
Description: "Returns a cluster's worker nodes: whether each is up, whether it will\ntake new work, and what accelerators it offers.\n\nThis is the one node inventory in the fleet. The GPU counts come from each node's\nallocatable `nvidia.com/gpu` and `amd.com/gpu`, which is what the scheduler will\nactually honour — not a label somebody typed.\n\nNodes are cluster-scoped, so a registration bounded to a namespace set cannot read\nthem: answering would hand one tenant on a shared cluster the whole cluster's\ncapacity.",
Fields: map[string]string{
"ClusterRef.cluster": "Cluster is the registered cluster's name — \"hanzo-k8s\", \"lux-k8s\",\n\"zoo-k8s\", \"bootnode-k8s\", or any name an org registered its own cluster\nunder. It is resolved against the CALLING ORG's registry, so two orgs may\nuse the same name for two different clusters and neither can reach the\nother's.",
},
Response: json.RawMessage(`{"nodes":[{"name":"pool-gpu-a1","ready":true,"schedulable":true,"region":"nyc3","instanceKind":"gpu-h100x8-640gb","cpu":"64","memory":"755Gi","nvidiaGpu":8,"amdGpu":0,"internalIp":"10.0.0.4"}]}`),
})
zip.Describe("GET /v1/k8s/nodes/volume-stats", zip.Doc{
Description: "Reports how full every mounted volume on a cluster is.\n\nIt is the ONLY source of volume fill we have. Cloud providers do not expose how\nfull a disk is, and metrics-server is absent from most of our clusters — but every\nkubelet serves `stats/summary` unconditionally, so this works everywhere. The read\ngoes through the apiserver's node proxy, which is why this is a SEPARATE named op:\n`nodes/proxy get` is effectively arbitrary kubelet access, and naming it here is\nwhat lets every other operation's grant omit it.\n\nA kubelet that will not answer costs a reading and never the call: fill is\nadvisory, no safety verdict depends on it, and failing the whole sweep over one\nsilent node would block every volume decision on the fleet. `nodesRead` against\n`nodesTotal` is how a caller knows the difference between \"empty\" and\n\"unmeasured\" — a claim with no row is unmeasured, and treating that as empty is\nhow live data gets condemned.",
Fields: map[string]string{
"ClusterRef.cluster": "Cluster is the registered cluster's name — \"hanzo-k8s\", \"lux-k8s\",\n\"zoo-k8s\", \"bootnode-k8s\", or any name an org registered its own cluster\nunder. It is resolved against the CALLING ORG's registry, so two orgs may\nuse the same name for two different clusters and neither can reach the\nother's.",
"VolumeStats.nodesRead": "NodesRead is how many kubelets answered; NodesTotal how many were asked.",
},
Response: json.RawMessage(`{"volumes":[{"namespace":"acme","name":"data","usedBytes":48318382080}],"nodesRead":6,"nodesTotal":6}`),
})
zip.Describe("GET /v1/k8s/pods", zip.Doc{
Description: "Returns pods, narrowed by namespace, labels or node.\n\nOne op covers every way the fleet asks for pods: the whole-cluster sweep an\ninfrastructure board takes, the label-selected set that answers \"is this app's\nworkload up\", and the single-node set a drain needs. They were three call sites\nwith three shapes; the difference between them is which argument is set.\n\nContainer ENV is deliberately absent from the projection. A pod spec carries the\nsecret material injected into it, so a read surface that returned the spec would be\nan exfiltration endpoint wearing the name of a health check.",
Fields: map[string]string{
"PodsIn.cluster": "Cluster is the registered cluster to read.",
"PodsIn.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"PodsIn.namespace": "Namespace narrows to one namespace. Empty means every namespace this\nregistration may address, which for a bounded registration is its own set\nand never the whole cluster.",
"PodsIn.nodeName": "NodeName narrows to the pods on one node — the field-selector variant the\ndrain path needs, named rather than left to a raw selector string.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","namespace":"acme-prod","labelSelector":"app=api"}`),
Response: json.RawMessage(`{"pods":[{"namespace":"acme-prod","name":"api-7d9f-2xk","phase":"Running","node":"pool-a1","controller":"ReplicaSet/api-7d9f","images":["oci.hanzo.ai/acme/api:1.4.2"],"restarts":0}]}`),
})
zip.Describe("GET /v1/k8s/pods/:namespace/:pod/logs", zip.Doc{
Description: "Returns what one container printed.\n\n`tailLines` is the bound a caller should set; without one the read is capped at one\nmebibyte and truncated from the FRONT, so the newest output — the part that\nexplains a crash — is what survives.",
Fields: map[string]string{
"LogsIn.cluster": "Cluster is the registered cluster the pod runs on.",
"LogsIn.container": "Container names the container; empty takes the pod's only one.",
"LogsIn.namespace": "Namespace is the pod's namespace.",
"LogsIn.pod": "Pod is the pod's name.",
"LogsIn.tailLines": "TailLines caps how many lines come back, newest last. Zero takes the\nserver's default. A log read is unbounded by nature, so this is the bound.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","namespace":"acme-prod","pod":"api-7d9f-2xk","tailLines":200}`),
Response: json.RawMessage(`{"log":"2026-07-30T00:00:01Z listening on :8000\n"}`),
})
zip.Describe("GET /v1/k8s/resources", zip.Doc{
Description: "Returns custom resources of one registered kind.",
Fields: map[string]string{
"Resource.version": "Version is the object's resourceVersion — the cursor a Watch resumes from.",
"ResourceIn.cluster": "Cluster is the registered cluster to read.",
"ResourceIn.kind": "Kind is the registered kind: \"App\", \"Application\", \"Datastore\",\n\"Validator\", \"TrainJob\", \"InferenceService\", \"IngressRoute\", \"Middleware\"\nor \"AppProject\".",
"ResourceIn.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"ResourceIn.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address. Ignored for a cluster-scoped kind.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","kind":"App","namespace":"acme-prod"}`),
Response: json.RawMessage(`{"resources":[{"kind":"App","namespace":"acme-prod","name":"api","spec":{"image":"oci.hanzo.ai/acme/api:1.4.2"},"status":{"phase":"Running"},"version":"918273"}],"version":"918273"}`),
})
zip.Describe("GET /v1/k8s/resources/:kind/:namespace/:name", zip.Doc{
Description: "Returns one custom resource.",
Fields: map[string]string{
"Resource.version": "Version is the object's resourceVersion — the cursor a Watch resumes from.",
"ResourceRef.cluster": "Cluster is the registered cluster the object lives on.",
"ResourceRef.kind": "Kind is the registered kind.",
"ResourceRef.name": "Name is the object's name.",
"ResourceRef.namespace": "Namespace is the object's namespace; empty for a cluster-scoped kind.",
},
Response: json.RawMessage(`{"kind":"App","namespace":"acme-prod","name":"api","spec":{"image":"oci.hanzo.ai/acme/api:1.4.2"},"status":{"phase":"Running"},"version":"918273"}`),
})
zip.Describe("GET /v1/k8s/resources/watch", zip.Doc{
Description: "Returns what changed to one kind since a cursor.\n\nIt is a BOUNDED read rather than a stream, and that is a design decision worth\nstating: a stream cannot cross the call plane as a value, and rendering one —\nServer-Sent Events, a websocket — is the EDGE's concern. A consumer builds its own\nstream by calling this in a loop with the cursor it got back, so the streaming\nshape exists once, in the consumer that renders it, instead of once per transport.\n\n`expired` is the field that matters most. When the cursor is older than the\napiserver's history the watch cannot be resumed, and an empty page would look\nexactly like \"nothing changed\" — which is how a watch-backed board silently stops\nupdating for hours. On `expired` a caller must LIST again and take the new cursor.",
Fields: map[string]string{
"Change.type": "Type is \"ADDED\", \"MODIFIED\" or \"DELETED\".",
"Resource.version": "Version is the object's resourceVersion — the cursor a Watch resumes from.",
"WatchIn.cluster": "Cluster is the registered cluster to watch.",
"WatchIn.kind": "Kind is the registered kind.",
"WatchIn.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
"WatchIn.version": "Version is the cursor to resume from — the Version a previous list or\nwatch returned. Empty starts from the current state.",
"WatchIn.waitSeconds": "WaitSeconds is how long to wait for a change before answering empty.\nClamped to a bound this app owns, so a caller cannot pin a watch open.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","kind":"App","namespace":"acme-prod","version":"918273","waitSeconds":30}`),
Response: json.RawMessage(`{"changes":[{"type":"MODIFIED","resource":{"kind":"App","namespace":"acme-prod","name":"api","status":{"phase":"Running"},"version":"918280"}}],"version":"918280"}`),
})
zip.Describe("GET /v1/k8s/volumes/claims", zip.Doc{
Description: "Returns PersistentVolumeClaims.",
Fields: map[string]string{
"Claim.requestGi": "RequestGi is the size the claim asks for, in GiB.",
"Selector.cluster": "Cluster is the registered cluster to read.",
"Selector.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"Selector.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
},
Response: json.RawMessage(`{"claims":[{"namespace":"acme-prod","name":"data","phase":"Bound","volume":"pvc-9f2","requestGi":100,"storageClass":"do-block-storage"}]}`),
})
zip.Describe("GET /v1/k8s/volumes/persistent", zip.Doc{
Description: "Returns the cluster's PersistentVolumes.\n\nA PV is where the link to the real cloud device lives: `handle` is the provider's\nown volume id, read from the CSI source or from the legacy flexVolume shape, and it\nis what lets a reconciliation say whether a disk somebody is paying for is attached\nto anything. The claim reference travels with it because a PV with no claim and a PV\nwhose claim is gone are different situations.\n\nPVs are cluster-scoped, so a namespace-bounded registration cannot read them.",
Fields: map[string]string{
"ClusterRef.cluster": "Cluster is the registered cluster's name — \"hanzo-k8s\", \"lux-k8s\",\n\"zoo-k8s\", \"bootnode-k8s\", or any name an org registered its own cluster\nunder. It is resolved against the CALLING ORG's registry, so two orgs may\nuse the same name for two different clusters and neither can reach the\nother's.",
"Volume.handle": "Handle is the backing device's provider id, when the PV carries one.",
},
Response: json.RawMessage(`{"volumes":[{"name":"pvc-9f2","phase":"Bound","handle":"a1b2c3","claimNamespace":"acme-prod","claimName":"data","capacityGi":100}]}`),
})
zip.Describe("GET /v1/k8s/workloads/configmaps", zip.Doc{
Description: "Returns ConfigMaps, with their data.\n\nThe data travels because a ConfigMap is by definition not secret material. Secrets\nare a different kind and this API does not read one back, at all: an RPC that returns\na tenant's Secret is a credential-exfiltration endpoint however carefully it is\ngated, and material that needs protecting belongs in KMS.",
Fields: map[string]string{
"Selector.cluster": "Cluster is the registered cluster to read.",
"Selector.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"Selector.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
},
Response: json.RawMessage(`{"configMaps":[{"namespace":"hanzo","name":"cron-schedules","data":{"nightly":"0 3 * * *"}}]}`),
})
zip.Describe("GET /v1/k8s/workloads/deployments", zip.Doc{
Description: "Returns Deployments with their live replica counts and the images\nthey are ACTUALLY running.\n\nThe running image is why this op exists rather than a read of the App CR: a CR's\nstatus does not surface the tag the pods are on, so the live Deployment is the only\nplace \"declared v1.4.2, running v1.4.1\" can be seen at all — which is the whole of\ndrift detection.",
Fields: map[string]string{
"Selector.cluster": "Cluster is the registered cluster to read.",
"Selector.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"Selector.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","namespace":"acme-prod"}`),
Response: json.RawMessage(`{"workloads":[{"namespace":"acme-prod","name":"api","kind":"Deployment","replicas":3,"ready":3,"updated":3,"images":["oci.hanzo.ai/acme/api:1.4.2"]}]}`),
})
zip.Describe("GET /v1/k8s/workloads/deployments/:namespace/:name", zip.Doc{
Description: "Returns one Deployment.",
Fields: map[string]string{
"NamedIn.cluster": "Cluster is the registered cluster the object lives on.",
"NamedIn.name": "Name is the object's name.",
"NamedIn.namespace": "Namespace is the object's namespace.",
},
Response: json.RawMessage(`{"namespace":"acme-prod","name":"api","kind":"Deployment","replicas":3,"ready":3,"updated":3,"images":["oci.hanzo.ai/acme/api:1.4.2"]}`),
})
zip.Describe("GET /v1/k8s/workloads/events", zip.Doc{
Description: "Returns cluster events, oldest first.\n\nThis is the only place a failure with no pod behind it can be explained — an\nunschedulable workload, a quota refusal, a volume that never bound — because those\nproduce an event and nothing else. Events are ordered by last-seen so the tail is\nthe current situation.",
Fields: map[string]string{
"Selector.cluster": "Cluster is the registered cluster to read.",
"Selector.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"Selector.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
},
Response: json.RawMessage(`{"events":[{"namespace":"acme-prod","name":"api.17f","type":"Warning","reason":"FailedScheduling","message":"0/6 nodes are available: insufficient nvidia.com/gpu","object":"Pod/api-7d9f-2xk","count":4,"lastSeen":1785110400}]}`),
})
zip.Describe("GET /v1/k8s/workloads/ingresses", zip.Doc{
Description: "Returns Ingresses with the hosts they answer and which of those are\ncovered by TLS.\n\nThe two host lists are separate because a host in `hosts` but not in `tlsHosts` is a\npublic URL served over plain HTTP, which is a finding rather than a detail.",
Fields: map[string]string{
"Selector.cluster": "Cluster is the registered cluster to read.",
"Selector.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"Selector.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
},
Response: json.RawMessage(`{"ingresses":[{"namespace":"acme-prod","name":"api","hosts":["api.acme.com"],"tlsHosts":["api.acme.com"],"addresses":["203.0.113.10"]}]}`),
})
zip.Describe("GET /v1/k8s/workloads/replicasets", zip.Doc{
Description: "Returns ReplicaSets.\n\nA deploy tree needs them because they are the layer between a Deployment and its\npods: during a rollout two exist, and which pods belong to which is the difference\nbetween \"the new version is up\" and \"the old one still is\".",
Fields: map[string]string{
"Selector.cluster": "Cluster is the registered cluster to read.",
"Selector.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"Selector.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
},
Response: json.RawMessage(`{"workloads":[{"namespace":"acme-prod","name":"api-7d9f","kind":"ReplicaSet","replicas":3,"ready":3,"owner":"Deployment/api"}]}`),
})
zip.Describe("GET /v1/k8s/workloads/services", zip.Doc{
Description: "Returns Services with every identity by which one can be matched to the\nload balancer in front of it.\n\nBoth the cloud-controller's own annotation and the assigned addresses travel, and\neither match is enough. That breadth is the safe direction: matching MORE Services to\na balancer means fewer balancers look orphaned, and the consequence of a wrong match\nin the other direction is deleting a live one.",
Fields: map[string]string{
"Selector.cluster": "Cluster is the registered cluster to read.",
"Selector.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"Selector.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
"Service.lbId": "LBID is the provider's load-balancer id, from the cloud-controller's own\nannotation — the strongest link between a Service and a real balancer.",
},
Response: json.RawMessage(`{"services":[{"namespace":"acme-prod","name":"api","type":"LoadBalancer","clusterIp":"10.3.0.7","ips":["203.0.113.10"],"lbId":"a1b2","ports":[443]}]}`),
})
zip.Describe("GET /v1/k8s/workloads/statefulsets", zip.Doc{
Description: "Returns StatefulSets.\n\nThey are read separately from Deployments because they are a different lifetime: a\nStatefulSet's volumeClaimTemplates are immutable, so a storage change means\nrecreating the workload around the claims rather than patching it — and a board that\nfolded the two kinds together would offer an operation that cannot work.",
Fields: map[string]string{
"Selector.cluster": "Cluster is the registered cluster to read.",
"Selector.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"Selector.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
},
Response: json.RawMessage(`{"workloads":[{"namespace":"acme-prod","name":"pg","kind":"StatefulSet","replicas":3,"ready":3,"images":["postgres:17"]}]}`),
})
zip.Describe("POST /k8s/access/can", zip.Doc{
Description: "Asks a cluster whether this app's own credential may perform one act.\n\nIt is a SelfSubjectAccessReview, which means the answer comes from the cluster's\nRBAC rather than from a grant this app asserts about itself. That distinction is the\nreason the op exists: the union ClusterRole this deployment needs is granted OUT of\ntree, so a drifted or half-applied grant is a real and undetectable condition — and\nasking beats asserting, because a refusal a caller can read is a feature it can\ndegrade gracefully on instead of a 403 arriving in the middle of a mutation.",
Fields: map[string]string{
"CanIn.cluster": "Cluster is the registered cluster to ask.",
"CanIn.group": "Group is the API group; empty for the core group.",
"CanIn.namespace": "Namespace scopes the question; empty asks cluster-wide.",
"CanIn.resource": "Resource is the plural resource name (\"pods\", \"resourcequotas\").",
"CanIn.verb": "Verb is the Kubernetes verb: \"get\", \"list\", \"create\", \"patch\", \"delete\", …",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","verb":"get","resource":"resourcequotas","namespace":"acme-prod"}`),
Response: json.RawMessage(`{"allowed":true}`),
})
zip.Describe("POST /k8s/clusters/deregister", zip.Doc{
Description: "Detaches a cluster from the calling org and destroys its sealed\ncredential.\n\nNothing in the cluster is touched: this removes Hanzo's ability to reach it, not\nthe cluster itself. Deleting a name that is not registered reports removed=false\nrather than failing, so a repeated deregistration is safe.",
Fields: map[string]string{
"ClusterRef.cluster": "Cluster is the registered cluster's name — \"hanzo-k8s\", \"lux-k8s\",\n\"zoo-k8s\", \"bootnode-k8s\", or any name an org registered its own cluster\nunder. It is resolved against the CALLING ORG's registry, so two orgs may\nuse the same name for two different clusters and neither can reach the\nother's.",
},
Response: json.RawMessage(`{"removed":true}`),
})
zip.Describe("POST /k8s/clusters/get", zip.Doc{
Description: "Returns one registered cluster.\n\nA name the calling org has not registered answers 404 — including a name another\norg HAS registered, which is indistinguishable from one that does not exist. That\nis deliberate: a distinguishable refusal would let a tenant enumerate other\ntenants' cluster names one guess at a time.",
Fields: map[string]string{
"ClusterRef.cluster": "Cluster is the registered cluster's name — \"hanzo-k8s\", \"lux-k8s\",\n\"zoo-k8s\", \"bootnode-k8s\", or any name an org registered its own cluster\nunder. It is resolved against the CALLING ORG's registry, so two orgs may\nuse the same name for two different clusters and neither can reach the\nother's.",
"Registered.namespaces": "Namespaces bounds what this registration may address. Empty means the\nwhole cluster, which is what an org registering its OWN cluster gets.",
"Registered.org": "Org is which org OWNS this cluster. On a reply that is a property of the\nrecord, not a claim by the caller.",
},
Response: json.RawMessage(`{"name":"lux-k8s","org":"lux","provider":"byo","endpoint":"https://k8s.lux.example","nodes":12,"nvidiaGpu":32,"amdGpu":0,"registered":"2026-07-30T00:00:00Z","default":false}`),
})
zip.Describe("POST /k8s/clusters/list", zip.Doc{
Description: "Returns every Kubernetes cluster the calling org may reach.\n\nThis is the fleet's ONE cluster list. A row is a real registration — a name, the\nprovider it came from, its apiserver endpoint and the node and accelerator counts\nmeasured when it was attached — and there is no such thing as an unregistered\ncluster this API can see. The credential is never part of a row: it is sealed in\nKMS and resolved only at the moment an op uses it.\n\nThe list is scoped by the caller's org and by nothing the caller can type, so a\ntenant sees its own clusters and no others.",
Fields: map[string]string{
"Registered.namespaces": "Namespaces bounds what this registration may address. Empty means the\nwhole cluster, which is what an org registering its OWN cluster gets.",
"Registered.org": "Org is which org OWNS this cluster. On a reply that is a property of the\nrecord, not a claim by the caller.",
},
Response: json.RawMessage(`{"clusters":[{"name":"hanzo-k8s","org":"hanzo","provider":"doks","endpoint":"https://k8s.example.com","nodes":6,"nvidiaGpu":8,"amdGpu":0,"registered":"2026-07-30T00:00:00Z","default":true}]}`),
})
zip.Describe("POST /k8s/clusters/register", zip.Doc{
Description: "Attaches a Kubernetes cluster to the calling org and answers 201.\n\nThis is the cluster-registry primitive and the most important operation in this\nAPI: everything else takes a cluster name, and this is where a name comes to\nexist. It is also the whole of BYO-Kubernetes — an org running its own cluster,\nlux running lux-k8s, a customer bringing their own — because a registered cluster\nis a registered cluster whatever provisioned it.\n\nThe kubeconfig is refused if it carries an exec or auth-provider credential\nplugin (either would run a local binary with this process's environment), and\nrefused if its apiserver is not a routable https host. It is then USED, once, to\nprove the cluster answers and to measure its nodes and accelerators; a cluster\nthat cannot be reached is not registered, because the alternative is a row that\nfails at first use when nobody is holding the credential any more. Finally it is\nsealed in KMS under a ref derived from the owning org and the name, and dropped.\nIt is never written to disk, never logged, and no operation returns it.\n\nRegistration is idempotent on name within the org, so re-registering rotates the\ncredential and refreshes the inventory without a second verb.\n\nSet `namespaces` when the credential is a scoped ServiceAccount on a shared\ncluster: the registration is then bounded to those namespaces and every later op\nis refused outside them, before the apiserver is dialed. Omit it when the org owns\nthe whole cluster.",
Fields: map[string]string{
"RegisterIn.default": "Default marks the cluster an org's workloads target when none is named by\na human — the ONE place a default is legitimate, because it is the org's\nown recorded choice rather than a process's ambient neighbour.",
"RegisterIn.kubeconfig": "Kubeconfig is the credential, in kubeconfig form. It is validated, used\nonce to prove the cluster is reachable, sealed in KMS, and then dropped:\nit is never written to disk, never logged and never returned.",
"RegisterIn.name": "Name is what every later op will call this cluster. Unique within the org.",
"RegisterIn.namespaces": "Namespaces bounds this registration to a namespace set. Omit it when the\norg owns the whole cluster; state it when the credential is a scoped\nServiceAccount on a shared cluster.",
"RegisterIn.provider": "Provider labels where the cluster came from (\"doks\", \"byo\", \"k3s\", …).",
"Registered.namespaces": "Namespaces bounds what this registration may address. Empty means the\nwhole cluster, which is what an org registering its OWN cluster gets.",
"Registered.org": "Org is which org OWNS this cluster. On a reply that is a property of the\nrecord, not a claim by the caller.",
},
Example: json.RawMessage(`{"name":"lux-k8s","kubeconfig":"apiVersion: v1\nkind: Config\n...","provider":"byo","default":true}`),
Response: json.RawMessage(`{"name":"lux-k8s","org":"lux","provider":"byo","endpoint":"https://k8s.lux.example","nodes":12,"nvidiaGpu":32,"amdGpu":0,"registered":"2026-07-30T00:00:00Z","default":true}`),
})
zip.Describe("POST /k8s/clusters/status", zip.Doc{
Description: "Reports whether a registered cluster answers right now.\n\nThe probe is the apiserver's version endpoint, because that is the one read every\ncredential can make regardless of what RBAC it was granted: a registration bounded\nto one namespace would fail a namespace list and look dead while being perfectly\nhealthy. So this answers \"is the credential live and the apiserver reachable\",\nwhich is the question a board asks, and the ops that need a permission ask for\nthat permission themselves.\n\nA cluster that does not answer is connected=false with the reason verbatim — never\nan error, because \"this cluster is down\" is a successful answer to this question.",
Fields: map[string]string{
"ClusterRef.cluster": "Cluster is the registered cluster's name — \"hanzo-k8s\", \"lux-k8s\",\n\"zoo-k8s\", \"bootnode-k8s\", or any name an org registered its own cluster\nunder. It is resolved against the CALLING ORG's registry, so two orgs may\nuse the same name for two different clusters and neither can reach the\nother's.",
"Reachable.connected": "Connected is whether the apiserver answered.",
"Reachable.reason": "Reason is why it did not, verbatim. Absent on success.",
"Reachable.version": "Version is the server version string, when it answered.",
},
Response: json.RawMessage(`{"connected":true,"version":"v1.33.1"}`),
})
zip.Describe("POST /k8s/jobs/create", zip.Doc{
Description: "Runs one batch Job and answers 201.\n\nThe input is a JOB, not a PodSpec: an image, a command, environment, a service\naccount, labels, and claims to mount. That is the whole of what the fleet's job\ncallers need — an image build, a migration, a scheduled task — and it is deliberately\nnot the Kubernetes PodSpec, because accepting a PodSpec would mean accepting\nhostPath mounts, host networking, privileged security contexts and secret volume\nreferences, i.e. everything a bounded surface exists to exclude.\n\n`env` is PLAIN environment. Secret material must not travel here: seal it in KMS and\ngive the job a ServiceAccount that can reach what it needs. `ttlSeconds` is worth\nsetting on anything periodic — a namespace that keeps every finished Job becomes a\nlist nobody can read.",
Fields: map[string]string{
"Job.phase": "Phase is \"running\", \"succeeded\" or \"failed\" — the one word a caller wants,\nrolled here rather than re-derived from three counters by every consumer.",
"JobIn.args": "Args are the arguments passed to the entrypoint.",
"JobIn.backoffLimit": "BackoffLimit caps retries; zero means the Kubernetes default.",
"JobIn.cluster": "Cluster is the registered cluster to run the job on.",
"JobIn.command": "Command replaces the image's entrypoint. Empty keeps it.",
"JobIn.env": "Env is plain environment. Secret material does NOT belong here: seal it in\nKMS and project it with a scoped ServiceAccount instead.",
"JobIn.image": "Image is the container image to run.",
"JobIn.labels": "Labels are stamped on the Job and its pods.",
"JobIn.mounts": "Mounts attaches existing claims by name at a path.",
"JobIn.name": "Name is the job's name.",
"JobIn.namespace": "Namespace is where the job runs.",
"JobIn.serviceAccount": "ServiceAccount runs the pod as a named ServiceAccount.",
"JobIn.ttlSeconds": "TTLSeconds deletes the finished Job after this long. Zero leaves it, which\non a busy namespace is how a Job list becomes unreadable.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","namespace":"hanzo","name":"build-1a2b","image":"gcr.io/kaniko-project/executor:latest","args":["--context=git://…","--destination=oci.hanzo.ai/acme/api:1.4.2"],"ttlSeconds":3600}`),
Response: json.RawMessage(`{"namespace":"hanzo","name":"build-1a2b","active":1,"succeeded":0,"failed":0,"phase":"running"}`),
})
zip.Describe("POST /k8s/jobs/get", zip.Doc{
Description: "Returns one batch Job.",
Fields: map[string]string{
"Job.phase": "Phase is \"running\", \"succeeded\" or \"failed\" — the one word a caller wants,\nrolled here rather than re-derived from three counters by every consumer.",
"NamedIn.cluster": "Cluster is the registered cluster the object lives on.",
"NamedIn.name": "Name is the object's name.",
"NamedIn.namespace": "Namespace is the object's namespace.",
},
Response: json.RawMessage(`{"namespace":"hanzo","name":"build-1a2b","active":0,"succeeded":1,"failed":0,"phase":"succeeded","startedAt":1785110400,"endedAt":1785110460}`),
})
zip.Describe("POST /k8s/jobs/list", zip.Doc{
Description: "Returns batch Jobs with a rolled-up phase.\n\n`phase` is one word — running, succeeded or failed — computed here rather than left\nto every caller to derive from three counters. Three consumers deriving it three ways\nis three chances to read a failed job as merely unfinished.",
Fields: map[string]string{
"Job.phase": "Phase is \"running\", \"succeeded\" or \"failed\" — the one word a caller wants,\nrolled here rather than re-derived from three counters by every consumer.",
"Selector.cluster": "Cluster is the registered cluster to read.",
"Selector.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"Selector.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
},
Response: json.RawMessage(`{"jobs":[{"namespace":"hanzo","name":"build-1a2b","active":0,"succeeded":1,"failed":0,"phase":"succeeded","startedAt":1785110400,"endedAt":1785110460}]}`),
})
zip.Describe("POST /k8s/metrics/pods", zip.Doc{
Description: "Returns live CPU and memory use for pods, from metrics.k8s.io.\n\n`available` is the field that carries the honesty. metrics-server is absent from most\nof our clusters, and a cluster without it answers this call with an error — which,\nrendered as zeroes, tells an operator that a busy namespace is idle. So a missing\nmetrics API is reported as available=false with no rows, and a caller must render\n\"unmeasured\" rather than \"0m\".",
Fields: map[string]string{
"PodMetric.cpuMilli": "CPUMilli is CPU use in millicores; MemoryBytes is working-set memory.",
"Selector.cluster": "Cluster is the registered cluster to read.",
"Selector.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"Selector.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","namespace":"acme-prod"}`),
Response: json.RawMessage(`{"pods":[{"namespace":"acme-prod","name":"api-7d9f-2xk","cpuMilli":250,"memoryBytes":536870912}],"available":true}`),
})
zip.Describe("POST /k8s/namespaces/delete", zip.Doc{
Description: "Deletes a namespace and everything in it.\n\nThis is the highest blast radius in the whole API: Kubernetes deletes a namespace\nby garbage-collecting every object inside it, including PersistentVolumeClaims,\nwhich for a Delete-reclaim StorageClass destroys the tenant's data. There is no\nundo and no confirmation this API can offer that would make one.\n\nIt is therefore its own named operation, gated separately from every other write,\nand it is refused outright for a registration bounded to a namespace set: a scoped\ncredential exists precisely because the cluster is shared, and \"delete a namespace\non a shared cluster\" is not an authority a tenant holds.",
Fields: map[string]string{
"NamespaceRef.cluster": "Cluster is the registered cluster the namespace lives on.",
"NamespaceRef.namespace": "Namespace is the namespace's name.",
},
Response: json.RawMessage(`{"removed":true}`),
})
zip.Describe("POST /k8s/namespaces/ensure", zip.Doc{
Description: "Creates a namespace if it is absent, and reports which it did.\n\nFour separate implementations of get-then-create-if-missing collapse into this one\nname. That matters beyond tidiness: each of those four wrote the ladder itself, and\na ladder is a race — \"get said absent\" and \"create\" are two calls, and two callers\nstarting a tenant at once both take the create branch. Here an AlreadyExists from\nthe apiserver is the successful \"it was already there\" answer, so the race has one\noutcome instead of one winner and one error.\n\n`created` is reported because creating a tenant's namespace and finding it are\ndifferent events, and a caller that cannot tell them apart cannot log the first.",
Fields: map[string]string{
"EnsureNamespaceIn.cluster": "Cluster is the registered cluster to create the namespace on.",
"EnsureNamespaceIn.labels": "Labels are merged onto the namespace when it is created.",
"EnsureNamespaceIn.namespace": "Namespace is the namespace's name.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","namespace":"acme-prod","labels":{"tier":"pro"}}`),
Response: json.RawMessage(`{"name":"acme-prod","created":true}`),
})
zip.Describe("POST /k8s/namespaces/get", zip.Doc{
Description: "Returns one namespace.",
Fields: map[string]string{
"NamespaceRef.cluster": "Cluster is the registered cluster the namespace lives on.",
"NamespaceRef.namespace": "Namespace is the namespace's name.",
},
Response: json.RawMessage(`{"name":"acme-prod","phase":"Active","labels":{"managed-by":"hanzo-cloud"}}`),
})
zip.Describe("POST /k8s/namespaces/list", zip.Doc{
Description: "Returns the namespaces the calling org's registration may address.\n\nFor a registration that owns the whole cluster this is every namespace on it. For\none bounded to a namespace set it is that set, read one namespace at a time — so a\nscoped tenant learns nothing about its neighbours, not even how many there are.",
Fields: map[string]string{
"ClusterRef.cluster": "Cluster is the registered cluster's name — \"hanzo-k8s\", \"lux-k8s\",\n\"zoo-k8s\", \"bootnode-k8s\", or any name an org registered its own cluster\nunder. It is resolved against the CALLING ORG's registry, so two orgs may\nuse the same name for two different clusters and neither can reach the\nother's.",
},
Response: json.RawMessage(`{"namespaces":[{"name":"acme-prod","phase":"Active","labels":{"managed-by":"hanzo-cloud"}}]}`),
})
zip.Describe("POST /k8s/nodes/cordon", zip.Doc{
Description: "Takes a node out of service, or puts it back, and optionally drains it.\n\nThis is ONE named operation over what is really three calls — patch\n`spec.unschedulable`, list the node's pods, evict each one — because the three are\nonly ever wanted together and a generic \"patch a node\" op would let a caller write\nany field of any node's spec. There is deliberately no such op.\n\nThe drain uses the EVICTION api, not delete: an eviction respects\nPodDisruptionBudgets, so a drain that would break a quorum is REFUSED by the\napiserver and reported verbatim, with the node left cordoned so an operator can\nscale and retry. DaemonSet and static (mirror) pods are skipped — the node\nrecreates them immediately, so evicting them is a loop — and pods already\nterminal are skipped too.\n\n`drain` is ignored when `schedulable` is true, because draining a node you just\nreturned to service is incoherent rather than merely useless.",
Fields: map[string]string{
"CordonIn.cluster": "Cluster is the registered cluster the node belongs to.",
"CordonIn.drain": "Drain evicts the node's pods after cordoning. Ignored when Schedulable is\ntrue, because draining a node you just returned to service is incoherent.",
"CordonIn.node": "Node is the node's name.",
"CordonIn.schedulable": "Schedulable is the state to leave the node in: false cordons it, true\nuncordons it.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","node":"pool-gpu-a1","schedulable":false,"drain":true}`),
Response: json.RawMessage(`{"evicted":11}`),
})
zip.Describe("POST /k8s/nodes/list", zip.Doc{
Description: "Returns a cluster's worker nodes: whether each is up, whether it will\ntake new work, and what accelerators it offers.\n\nThis is the one node inventory in the fleet. The GPU counts come from each node's\nallocatable `nvidia.com/gpu` and `amd.com/gpu`, which is what the scheduler will\nactually honour — not a label somebody typed.\n\nNodes are cluster-scoped, so a registration bounded to a namespace set cannot read\nthem: answering would hand one tenant on a shared cluster the whole cluster's\ncapacity.",
Fields: map[string]string{
"ClusterRef.cluster": "Cluster is the registered cluster's name — \"hanzo-k8s\", \"lux-k8s\",\n\"zoo-k8s\", \"bootnode-k8s\", or any name an org registered its own cluster\nunder. It is resolved against the CALLING ORG's registry, so two orgs may\nuse the same name for two different clusters and neither can reach the\nother's.",
},
Response: json.RawMessage(`{"nodes":[{"name":"pool-gpu-a1","ready":true,"schedulable":true,"region":"nyc3","instanceKind":"gpu-h100x8-640gb","cpu":"64","memory":"755Gi","nvidiaGpu":8,"amdGpu":0,"internalIp":"10.0.0.4"}]}`),
})
zip.Describe("POST /k8s/nodes/volume-stats", zip.Doc{
Description: "Reports how full every mounted volume on a cluster is.\n\nIt is the ONLY source of volume fill we have. Cloud providers do not expose how\nfull a disk is, and metrics-server is absent from most of our clusters — but every\nkubelet serves `stats/summary` unconditionally, so this works everywhere. The read\ngoes through the apiserver's node proxy, which is why this is a SEPARATE named op:\n`nodes/proxy get` is effectively arbitrary kubelet access, and naming it here is\nwhat lets every other operation's grant omit it.\n\nA kubelet that will not answer costs a reading and never the call: fill is\nadvisory, no safety verdict depends on it, and failing the whole sweep over one\nsilent node would block every volume decision on the fleet. `nodesRead` against\n`nodesTotal` is how a caller knows the difference between \"empty\" and\n\"unmeasured\" — a claim with no row is unmeasured, and treating that as empty is\nhow live data gets condemned.",
Fields: map[string]string{
"ClusterRef.cluster": "Cluster is the registered cluster's name — \"hanzo-k8s\", \"lux-k8s\",\n\"zoo-k8s\", \"bootnode-k8s\", or any name an org registered its own cluster\nunder. It is resolved against the CALLING ORG's registry, so two orgs may\nuse the same name for two different clusters and neither can reach the\nother's.",
"VolumeStats.nodesRead": "NodesRead is how many kubelets answered; NodesTotal how many were asked.",
},
Response: json.RawMessage(`{"volumes":[{"namespace":"acme","name":"data","usedBytes":48318382080}],"nodesRead":6,"nodesTotal":6}`),
})
zip.Describe("POST /k8s/pods/list", zip.Doc{
Description: "Returns pods, narrowed by namespace, labels or node.\n\nOne op covers every way the fleet asks for pods: the whole-cluster sweep an\ninfrastructure board takes, the label-selected set that answers \"is this app's\nworkload up\", and the single-node set a drain needs. They were three call sites\nwith three shapes; the difference between them is which argument is set.\n\nContainer ENV is deliberately absent from the projection. A pod spec carries the\nsecret material injected into it, so a read surface that returned the spec would be\nan exfiltration endpoint wearing the name of a health check.",
Fields: map[string]string{
"PodsIn.cluster": "Cluster is the registered cluster to read.",
"PodsIn.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"PodsIn.namespace": "Namespace narrows to one namespace. Empty means every namespace this\nregistration may address, which for a bounded registration is its own set\nand never the whole cluster.",
"PodsIn.nodeName": "NodeName narrows to the pods on one node — the field-selector variant the\ndrain path needs, named rather than left to a raw selector string.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","namespace":"acme-prod","labelSelector":"app=api"}`),
Response: json.RawMessage(`{"pods":[{"namespace":"acme-prod","name":"api-7d9f-2xk","phase":"Running","node":"pool-a1","controller":"ReplicaSet/api-7d9f","images":["oci.hanzo.ai/acme/api:1.4.2"],"restarts":0}]}`),
})
zip.Describe("POST /k8s/pods/logs", zip.Doc{
Description: "Returns what one container printed.\n\n`tailLines` is the bound a caller should set; without one the read is capped at one\nmebibyte and truncated from the FRONT, so the newest output — the part that\nexplains a crash — is what survives.",
Fields: map[string]string{
"LogsIn.cluster": "Cluster is the registered cluster the pod runs on.",
"LogsIn.container": "Container names the container; empty takes the pod's only one.",
"LogsIn.namespace": "Namespace is the pod's namespace.",
"LogsIn.pod": "Pod is the pod's name.",
"LogsIn.tailLines": "TailLines caps how many lines come back, newest last. Zero takes the\nserver's default. A log read is unbounded by nature, so this is the bound.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","namespace":"acme-prod","pod":"api-7d9f-2xk","tailLines":200}`),
Response: json.RawMessage(`{"log":"2026-07-30T00:00:01Z listening on :8000\n"}`),
})
zip.Describe("POST /k8s/resources/apply", zip.Doc{
Description: "Creates one custom resource of a registered kind, or patches the one\nalready there.\n\nFive separate get-then-create-else-patch ladders in the fleet collapse into this one\nname. Each of those wrote the ladder itself and each was a race; here the apiserver\nresolves the race, because a create that loses falls through to the patch.\n\nThe `spec` is the caller's domain payload and is applied whole — its SHAPE belongs to\nwhoever authored the CRD, not to this app, which would otherwise have to be edited\nevery time any kind grew a field. What this app owns is the KIND and the VERB, and\nboth are named and typed. That is the line: `kind` is a key in a closed table, so\nthis cannot become \"apply any object the cluster has\".",
Fields: map[string]string{
"ApplyIn.cluster": "Cluster is the registered cluster to apply to.",
"ApplyIn.kind": "Kind is the registered kind.",
"ApplyIn.labels": "Labels are merged onto the object.",
"ApplyIn.name": "Name is the object's name.",
"ApplyIn.namespace": "Namespace is where the object lives; empty for a cluster-scoped kind.",
"ApplyIn.spec": "Spec is the desired spec, applied whole.",
"Resource.version": "Version is the object's resourceVersion — the cursor a Watch resumes from.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","kind":"App","namespace":"acme-prod","name":"api","spec":{"image":"oci.hanzo.ai/acme/api:1.4.2","replicas":3}}`),
Response: json.RawMessage(`{"kind":"App","namespace":"acme-prod","name":"api","spec":{"image":"oci.hanzo.ai/acme/api:1.4.2","replicas":3},"version":"918281"}`),
})
zip.Describe("POST /k8s/resources/delete", zip.Doc{
Description: "Deletes one custom resource of a registered kind.\n\nDeleting an object is not deleting its data: a workload's App CR is desired state and\ncan be recreated freely, while the PersistentVolumeClaim it mounted holds the only\ncopy of the tenant's data and is a different lifetime. Nothing here deletes a claim.",
Fields: map[string]string{
"ResourceRef.cluster": "Cluster is the registered cluster the object lives on.",
"ResourceRef.kind": "Kind is the registered kind.",
"ResourceRef.name": "Name is the object's name.",
"ResourceRef.namespace": "Namespace is the object's namespace; empty for a cluster-scoped kind.",
},
Response: json.RawMessage(`{"removed":true}`),
})
zip.Describe("POST /k8s/resources/get", zip.Doc{
Description: "Returns one custom resource.",
Fields: map[string]string{
"Resource.version": "Version is the object's resourceVersion — the cursor a Watch resumes from.",
"ResourceRef.cluster": "Cluster is the registered cluster the object lives on.",
"ResourceRef.kind": "Kind is the registered kind.",
"ResourceRef.name": "Name is the object's name.",
"ResourceRef.namespace": "Namespace is the object's namespace; empty for a cluster-scoped kind.",
},
Response: json.RawMessage(`{"kind":"App","namespace":"acme-prod","name":"api","spec":{"image":"oci.hanzo.ai/acme/api:1.4.2"},"status":{"phase":"Running"},"version":"918273"}`),
})
zip.Describe("POST /k8s/resources/list", zip.Doc{
Description: "Returns custom resources of one registered kind.",
Fields: map[string]string{
"Resource.version": "Version is the object's resourceVersion — the cursor a Watch resumes from.",
"ResourceIn.cluster": "Cluster is the registered cluster to read.",
"ResourceIn.kind": "Kind is the registered kind: \"App\", \"Application\", \"Datastore\",\n\"Validator\", \"TrainJob\", \"InferenceService\", \"IngressRoute\", \"Middleware\"\nor \"AppProject\".",
"ResourceIn.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"ResourceIn.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address. Ignored for a cluster-scoped kind.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","kind":"App","namespace":"acme-prod"}`),
Response: json.RawMessage(`{"resources":[{"kind":"App","namespace":"acme-prod","name":"api","spec":{"image":"oci.hanzo.ai/acme/api:1.4.2"},"status":{"phase":"Running"},"version":"918273"}],"version":"918273"}`),
})
zip.Describe("POST /k8s/resources/watch", zip.Doc{
Description: "Returns what changed to one kind since a cursor.\n\nIt is a BOUNDED read rather than a stream, and that is a design decision worth\nstating: a stream cannot cross the call plane as a value, and rendering one —\nServer-Sent Events, a websocket — is the EDGE's concern. A consumer builds its own\nstream by calling this in a loop with the cursor it got back, so the streaming\nshape exists once, in the consumer that renders it, instead of once per transport.\n\n`expired` is the field that matters most. When the cursor is older than the\napiserver's history the watch cannot be resumed, and an empty page would look\nexactly like \"nothing changed\" — which is how a watch-backed board silently stops\nupdating for hours. On `expired` a caller must LIST again and take the new cursor.",
Fields: map[string]string{
"Change.type": "Type is \"ADDED\", \"MODIFIED\" or \"DELETED\".",
"Resource.version": "Version is the object's resourceVersion — the cursor a Watch resumes from.",
"WatchIn.cluster": "Cluster is the registered cluster to watch.",
"WatchIn.kind": "Kind is the registered kind.",
"WatchIn.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
"WatchIn.version": "Version is the cursor to resume from — the Version a previous list or\nwatch returned. Empty starts from the current state.",
"WatchIn.waitSeconds": "WaitSeconds is how long to wait for a change before answering empty.\nClamped to a bound this app owns, so a caller cannot pin a watch open.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","kind":"App","namespace":"acme-prod","version":"918273","waitSeconds":30}`),
Response: json.RawMessage(`{"changes":[{"type":"MODIFIED","resource":{"kind":"App","namespace":"acme-prod","name":"api","status":{"phase":"Running"},"version":"918280"}}],"version":"918280"}`),
})
zip.Describe("POST /k8s/volumes/ensure-claim", zip.Doc{
Description: "Creates a PersistentVolumeClaim if it is absent.\n\nIdempotent on name in the namespace, for the reason ensureNamespace is: the\nget-then-create ladder is a race when each caller writes it.",
Fields: map[string]string{
"EnsureClaimIn.accessMode": "AccessMode is \"ReadWriteOnce\" (default) or \"ReadWriteMany\".",
"EnsureClaimIn.cluster": "Cluster is the registered cluster to create the claim on.",
"EnsureClaimIn.gi": "Gi is the storage to request, in GiB.",
"EnsureClaimIn.name": "Name is the claim's name.",
"EnsureClaimIn.namespace": "Namespace is where the claim lives.",
"EnsureClaimIn.storageClass": "StorageClass names the class; empty takes the cluster's default.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","namespace":"acme-prod","name":"data","gi":100,"storageClass":"do-block-storage"}`),
Response: json.RawMessage(`{"name":"data","created":true}`),
})
zip.Describe("POST /k8s/volumes/expand-claim", zip.Doc{
Description: "Grows a PersistentVolumeClaim to a new size.\n\nThis is the ONE correct way to grow a volume Kubernetes manages, and the reason it\nis a named op rather than a patch: the resize controller acts on the CLAIM, growing\nthe backing cloud device and then the filesystem on it, so claim, PersistentVolume,\ndevice and filesystem all end up agreeing. Growing the device through the cloud\nprovider's own API instead leaves the claim and the PV declaring the old capacity\nand the filesystem never grown at all — three sources of truth, two of them wrong.\n\nThe StorageClass must set `allowVolumeExpansion`; when it does not, the apiserver\nrefuses and its message is surfaced verbatim rather than worked around. A shrink is\nrefused by the apiserver too, and there is no op that deletes a claim: the claim\nholds the only copy of the tenant's data, so this app can make one bigger and\nnothing else.",
Fields: map[string]string{
"ExpandClaimIn.cluster": "Cluster is the registered cluster the claim lives on.",
"ExpandClaimIn.gi": "Gi is the new size in GiB. It must exceed the current request; the\napiserver refuses a shrink and its message is surfaced verbatim.",
"ExpandClaimIn.name": "Name is the claim's name.",
"ExpandClaimIn.namespace": "Namespace is the claim's namespace.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","namespace":"acme-prod","name":"data","gi":200}`),
Response: json.RawMessage(`{"gi":200}`),
})
zip.Describe("POST /k8s/volumes/list-claims", zip.Doc{
Description: "Returns PersistentVolumeClaims.",
Fields: map[string]string{
"Claim.requestGi": "RequestGi is the size the claim asks for, in GiB.",
"Selector.cluster": "Cluster is the registered cluster to read.",
"Selector.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"Selector.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
},
Response: json.RawMessage(`{"claims":[{"namespace":"acme-prod","name":"data","phase":"Bound","volume":"pvc-9f2","requestGi":100,"storageClass":"do-block-storage"}]}`),
})
zip.Describe("POST /k8s/volumes/list-persistent", zip.Doc{
Description: "Returns the cluster's PersistentVolumes.\n\nA PV is where the link to the real cloud device lives: `handle` is the provider's\nown volume id, read from the CSI source or from the legacy flexVolume shape, and it\nis what lets a reconciliation say whether a disk somebody is paying for is attached\nto anything. The claim reference travels with it because a PV with no claim and a PV\nwhose claim is gone are different situations.\n\nPVs are cluster-scoped, so a namespace-bounded registration cannot read them.",
Fields: map[string]string{
"ClusterRef.cluster": "Cluster is the registered cluster's name — \"hanzo-k8s\", \"lux-k8s\",\n\"zoo-k8s\", \"bootnode-k8s\", or any name an org registered its own cluster\nunder. It is resolved against the CALLING ORG's registry, so two orgs may\nuse the same name for two different clusters and neither can reach the\nother's.",
"Volume.handle": "Handle is the backing device's provider id, when the PV carries one.",
},
Response: json.RawMessage(`{"volumes":[{"name":"pvc-9f2","phase":"Bound","handle":"a1b2c3","claimNamespace":"acme-prod","claimName":"data","capacityGi":100}]}`),
})
zip.Describe("POST /k8s/workloads/get-deployment", zip.Doc{
Description: "Returns one Deployment.",
Fields: map[string]string{
"NamedIn.cluster": "Cluster is the registered cluster the object lives on.",
"NamedIn.name": "Name is the object's name.",
"NamedIn.namespace": "Namespace is the object's namespace.",
},
Response: json.RawMessage(`{"namespace":"acme-prod","name":"api","kind":"Deployment","replicas":3,"ready":3,"updated":3,"images":["oci.hanzo.ai/acme/api:1.4.2"]}`),
})
zip.Describe("POST /k8s/workloads/list-configmaps", zip.Doc{
Description: "Returns ConfigMaps, with their data.\n\nThe data travels because a ConfigMap is by definition not secret material. Secrets\nare a different kind and this API does not read one back, at all: an RPC that returns\na tenant's Secret is a credential-exfiltration endpoint however carefully it is\ngated, and material that needs protecting belongs in KMS.",
Fields: map[string]string{
"Selector.cluster": "Cluster is the registered cluster to read.",
"Selector.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"Selector.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
},
Response: json.RawMessage(`{"configMaps":[{"namespace":"hanzo","name":"cron-schedules","data":{"nightly":"0 3 * * *"}}]}`),
})
zip.Describe("POST /k8s/workloads/list-deployments", zip.Doc{
Description: "Returns Deployments with their live replica counts and the images\nthey are ACTUALLY running.\n\nThe running image is why this op exists rather than a read of the App CR: a CR's\nstatus does not surface the tag the pods are on, so the live Deployment is the only\nplace \"declared v1.4.2, running v1.4.1\" can be seen at all — which is the whole of\ndrift detection.",
Fields: map[string]string{
"Selector.cluster": "Cluster is the registered cluster to read.",
"Selector.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"Selector.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","namespace":"acme-prod"}`),
Response: json.RawMessage(`{"workloads":[{"namespace":"acme-prod","name":"api","kind":"Deployment","replicas":3,"ready":3,"updated":3,"images":["oci.hanzo.ai/acme/api:1.4.2"]}]}`),
})
zip.Describe("POST /k8s/workloads/list-events", zip.Doc{
Description: "Returns cluster events, oldest first.\n\nThis is the only place a failure with no pod behind it can be explained — an\nunschedulable workload, a quota refusal, a volume that never bound — because those\nproduce an event and nothing else. Events are ordered by last-seen so the tail is\nthe current situation.",
Fields: map[string]string{
"Selector.cluster": "Cluster is the registered cluster to read.",
"Selector.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"Selector.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
},
Response: json.RawMessage(`{"events":[{"namespace":"acme-prod","name":"api.17f","type":"Warning","reason":"FailedScheduling","message":"0/6 nodes are available: insufficient nvidia.com/gpu","object":"Pod/api-7d9f-2xk","count":4,"lastSeen":1785110400}]}`),
})
zip.Describe("POST /k8s/workloads/list-ingresses", zip.Doc{
Description: "Returns Ingresses with the hosts they answer and which of those are\ncovered by TLS.\n\nThe two host lists are separate because a host in `hosts` but not in `tlsHosts` is a\npublic URL served over plain HTTP, which is a finding rather than a detail.",
Fields: map[string]string{
"Selector.cluster": "Cluster is the registered cluster to read.",
"Selector.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"Selector.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
},
Response: json.RawMessage(`{"ingresses":[{"namespace":"acme-prod","name":"api","hosts":["api.acme.com"],"tlsHosts":["api.acme.com"],"addresses":["203.0.113.10"]}]}`),
})
zip.Describe("POST /k8s/workloads/list-replicasets", zip.Doc{
Description: "Returns ReplicaSets.\n\nA deploy tree needs them because they are the layer between a Deployment and its\npods: during a rollout two exist, and which pods belong to which is the difference\nbetween \"the new version is up\" and \"the old one still is\".",
Fields: map[string]string{
"Selector.cluster": "Cluster is the registered cluster to read.",
"Selector.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"Selector.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
},
Response: json.RawMessage(`{"workloads":[{"namespace":"acme-prod","name":"api-7d9f","kind":"ReplicaSet","replicas":3,"ready":3,"owner":"Deployment/api"}]}`),
})
zip.Describe("POST /k8s/workloads/list-services", zip.Doc{
Description: "Returns Services with every identity by which one can be matched to the\nload balancer in front of it.\n\nBoth the cloud-controller's own annotation and the assigned addresses travel, and\neither match is enough. That breadth is the safe direction: matching MORE Services to\na balancer means fewer balancers look orphaned, and the consequence of a wrong match\nin the other direction is deleting a live one.",
Fields: map[string]string{
"Selector.cluster": "Cluster is the registered cluster to read.",
"Selector.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"Selector.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
"Service.lbId": "LBID is the provider's load-balancer id, from the cloud-controller's own\nannotation — the strongest link between a Service and a real balancer.",
},
Response: json.RawMessage(`{"services":[{"namespace":"acme-prod","name":"api","type":"LoadBalancer","clusterIp":"10.3.0.7","ips":["203.0.113.10"],"lbId":"a1b2","ports":[443]}]}`),
})
zip.Describe("POST /k8s/workloads/list-statefulsets", zip.Doc{
Description: "Returns StatefulSets.\n\nThey are read separately from Deployments because they are a different lifetime: a\nStatefulSet's volumeClaimTemplates are immutable, so a storage change means\nrecreating the workload around the claims rather than patching it — and a board that\nfolded the two kinds together would offer an operation that cannot work.",
Fields: map[string]string{
"Selector.cluster": "Cluster is the registered cluster to read.",
"Selector.labelSelector": "LabelSelector is a Kubernetes label selector, verbatim.",
"Selector.namespace": "Namespace narrows to one namespace; empty means every namespace this\nregistration may address.",
},
Response: json.RawMessage(`{"workloads":[{"namespace":"acme-prod","name":"pg","kind":"StatefulSet","replicas":3,"ready":3,"images":["postgres:17"]}]}`),
})
zip.Describe("POST /v1/k8s/access/can", zip.Doc{
Description: "Asks a cluster whether this app's own credential may perform one act.\n\nIt is a SelfSubjectAccessReview, which means the answer comes from the cluster's\nRBAC rather than from a grant this app asserts about itself. That distinction is the\nreason the op exists: the union ClusterRole this deployment needs is granted OUT of\ntree, so a drifted or half-applied grant is a real and undetectable condition — and\nasking beats asserting, because a refusal a caller can read is a feature it can\ndegrade gracefully on instead of a 403 arriving in the middle of a mutation.",
Fields: map[string]string{
"CanIn.cluster": "Cluster is the registered cluster to ask.",
"CanIn.group": "Group is the API group; empty for the core group.",
"CanIn.namespace": "Namespace scopes the question; empty asks cluster-wide.",
"CanIn.resource": "Resource is the plural resource name (\"pods\", \"resourcequotas\").",
"CanIn.verb": "Verb is the Kubernetes verb: \"get\", \"list\", \"create\", \"patch\", \"delete\", …",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","verb":"get","resource":"resourcequotas","namespace":"acme-prod"}`),
Response: json.RawMessage(`{"allowed":true}`),
})
zip.Describe("POST /v1/k8s/clusters", zip.Doc{
Description: "Attaches a Kubernetes cluster to the calling org and answers 201.\n\nThis is the cluster-registry primitive and the most important operation in this\nAPI: everything else takes a cluster name, and this is where a name comes to\nexist. It is also the whole of BYO-Kubernetes — an org running its own cluster,\nlux running lux-k8s, a customer bringing their own — because a registered cluster\nis a registered cluster whatever provisioned it.\n\nThe kubeconfig is refused if it carries an exec or auth-provider credential\nplugin (either would run a local binary with this process's environment), and\nrefused if its apiserver is not a routable https host. It is then USED, once, to\nprove the cluster answers and to measure its nodes and accelerators; a cluster\nthat cannot be reached is not registered, because the alternative is a row that\nfails at first use when nobody is holding the credential any more. Finally it is\nsealed in KMS under a ref derived from the owning org and the name, and dropped.\nIt is never written to disk, never logged, and no operation returns it.\n\nRegistration is idempotent on name within the org, so re-registering rotates the\ncredential and refreshes the inventory without a second verb.\n\nSet `namespaces` when the credential is a scoped ServiceAccount on a shared\ncluster: the registration is then bounded to those namespaces and every later op\nis refused outside them, before the apiserver is dialed. Omit it when the org owns\nthe whole cluster.",
Fields: map[string]string{
"RegisterIn.default": "Default marks the cluster an org's workloads target when none is named by\na human — the ONE place a default is legitimate, because it is the org's\nown recorded choice rather than a process's ambient neighbour.",
"RegisterIn.kubeconfig": "Kubeconfig is the credential, in kubeconfig form. It is validated, used\nonce to prove the cluster is reachable, sealed in KMS, and then dropped:\nit is never written to disk, never logged and never returned.",
"RegisterIn.name": "Name is what every later op will call this cluster. Unique within the org.",
"RegisterIn.namespaces": "Namespaces bounds this registration to a namespace set. Omit it when the\norg owns the whole cluster; state it when the credential is a scoped\nServiceAccount on a shared cluster.",
"RegisterIn.provider": "Provider labels where the cluster came from (\"doks\", \"byo\", \"k3s\", …).",
"Registered.namespaces": "Namespaces bounds what this registration may address. Empty means the\nwhole cluster, which is what an org registering its OWN cluster gets.",
"Registered.org": "Org is which org OWNS this cluster. On a reply that is a property of the\nrecord, not a claim by the caller.",
},
Example: json.RawMessage(`{"name":"lux-k8s","kubeconfig":"apiVersion: v1\nkind: Config\n...","provider":"byo","default":true}`),
Response: json.RawMessage(`{"name":"lux-k8s","org":"lux","provider":"byo","endpoint":"https://k8s.lux.example","nodes":12,"nvidiaGpu":32,"amdGpu":0,"registered":"2026-07-30T00:00:00Z","default":true}`),
})
zip.Describe("POST /v1/k8s/jobs", zip.Doc{
Description: "Runs one batch Job and answers 201.\n\nThe input is a JOB, not a PodSpec: an image, a command, environment, a service\naccount, labels, and claims to mount. That is the whole of what the fleet's job\ncallers need — an image build, a migration, a scheduled task — and it is deliberately\nnot the Kubernetes PodSpec, because accepting a PodSpec would mean accepting\nhostPath mounts, host networking, privileged security contexts and secret volume\nreferences, i.e. everything a bounded surface exists to exclude.\n\n`env` is PLAIN environment. Secret material must not travel here: seal it in KMS and\ngive the job a ServiceAccount that can reach what it needs. `ttlSeconds` is worth\nsetting on anything periodic — a namespace that keeps every finished Job becomes a\nlist nobody can read.",
Fields: map[string]string{
"Job.phase": "Phase is \"running\", \"succeeded\" or \"failed\" — the one word a caller wants,\nrolled here rather than re-derived from three counters by every consumer.",
"JobIn.args": "Args are the arguments passed to the entrypoint.",
"JobIn.backoffLimit": "BackoffLimit caps retries; zero means the Kubernetes default.",
"JobIn.cluster": "Cluster is the registered cluster to run the job on.",
"JobIn.command": "Command replaces the image's entrypoint. Empty keeps it.",
"JobIn.env": "Env is plain environment. Secret material does NOT belong here: seal it in\nKMS and project it with a scoped ServiceAccount instead.",
"JobIn.image": "Image is the container image to run.",
"JobIn.labels": "Labels are stamped on the Job and its pods.",
"JobIn.mounts": "Mounts attaches existing claims by name at a path.",
"JobIn.name": "Name is the job's name.",
"JobIn.namespace": "Namespace is where the job runs.",
"JobIn.serviceAccount": "ServiceAccount runs the pod as a named ServiceAccount.",
"JobIn.ttlSeconds": "TTLSeconds deletes the finished Job after this long. Zero leaves it, which\non a busy namespace is how a Job list becomes unreadable.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","namespace":"hanzo","name":"build-1a2b","image":"gcr.io/kaniko-project/executor:latest","args":["--context=git://…","--destination=oci.hanzo.ai/acme/api:1.4.2"],"ttlSeconds":3600}`),
Response: json.RawMessage(`{"namespace":"hanzo","name":"build-1a2b","active":1,"succeeded":0,"failed":0,"phase":"running"}`),
})
zip.Describe("POST /v1/k8s/namespaces", zip.Doc{
Description: "Creates a namespace if it is absent, and reports which it did.\n\nFour separate implementations of get-then-create-if-missing collapse into this one\nname. That matters beyond tidiness: each of those four wrote the ladder itself, and\na ladder is a race — \"get said absent\" and \"create\" are two calls, and two callers\nstarting a tenant at once both take the create branch. Here an AlreadyExists from\nthe apiserver is the successful \"it was already there\" answer, so the race has one\noutcome instead of one winner and one error.\n\n`created` is reported because creating a tenant's namespace and finding it are\ndifferent events, and a caller that cannot tell them apart cannot log the first.",
Fields: map[string]string{
"EnsureNamespaceIn.cluster": "Cluster is the registered cluster to create the namespace on.",
"EnsureNamespaceIn.labels": "Labels are merged onto the namespace when it is created.",
"EnsureNamespaceIn.namespace": "Namespace is the namespace's name.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","namespace":"acme-prod","labels":{"tier":"pro"}}`),
Response: json.RawMessage(`{"name":"acme-prod","created":true}`),
})
zip.Describe("POST /v1/k8s/nodes/:node/cordon", zip.Doc{
Description: "Takes a node out of service, or puts it back, and optionally drains it.\n\nThis is ONE named operation over what is really three calls — patch\n`spec.unschedulable`, list the node's pods, evict each one — because the three are\nonly ever wanted together and a generic \"patch a node\" op would let a caller write\nany field of any node's spec. There is deliberately no such op.\n\nThe drain uses the EVICTION api, not delete: an eviction respects\nPodDisruptionBudgets, so a drain that would break a quorum is REFUSED by the\napiserver and reported verbatim, with the node left cordoned so an operator can\nscale and retry. DaemonSet and static (mirror) pods are skipped — the node\nrecreates them immediately, so evicting them is a loop — and pods already\nterminal are skipped too.\n\n`drain` is ignored when `schedulable` is true, because draining a node you just\nreturned to service is incoherent rather than merely useless.",
Fields: map[string]string{
"CordonIn.cluster": "Cluster is the registered cluster the node belongs to.",
"CordonIn.drain": "Drain evicts the node's pods after cordoning. Ignored when Schedulable is\ntrue, because draining a node you just returned to service is incoherent.",
"CordonIn.node": "Node is the node's name.",
"CordonIn.schedulable": "Schedulable is the state to leave the node in: false cordons it, true\nuncordons it.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","node":"pool-gpu-a1","schedulable":false,"drain":true}`),
Response: json.RawMessage(`{"evicted":11}`),
})
zip.Describe("POST /v1/k8s/volumes/claims", zip.Doc{
Description: "Creates a PersistentVolumeClaim if it is absent.\n\nIdempotent on name in the namespace, for the reason ensureNamespace is: the\nget-then-create ladder is a race when each caller writes it.",
Fields: map[string]string{
"EnsureClaimIn.accessMode": "AccessMode is \"ReadWriteOnce\" (default) or \"ReadWriteMany\".",
"EnsureClaimIn.cluster": "Cluster is the registered cluster to create the claim on.",
"EnsureClaimIn.gi": "Gi is the storage to request, in GiB.",
"EnsureClaimIn.name": "Name is the claim's name.",
"EnsureClaimIn.namespace": "Namespace is where the claim lives.",
"EnsureClaimIn.storageClass": "StorageClass names the class; empty takes the cluster's default.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","namespace":"acme-prod","name":"data","gi":100,"storageClass":"do-block-storage"}`),
Response: json.RawMessage(`{"name":"data","created":true}`),
})
zip.Describe("POST /v1/k8s/volumes/claims/:namespace/:name/expand", zip.Doc{
Description: "Grows a PersistentVolumeClaim to a new size.\n\nThis is the ONE correct way to grow a volume Kubernetes manages, and the reason it\nis a named op rather than a patch: the resize controller acts on the CLAIM, growing\nthe backing cloud device and then the filesystem on it, so claim, PersistentVolume,\ndevice and filesystem all end up agreeing. Growing the device through the cloud\nprovider's own API instead leaves the claim and the PV declaring the old capacity\nand the filesystem never grown at all — three sources of truth, two of them wrong.\n\nThe StorageClass must set `allowVolumeExpansion`; when it does not, the apiserver\nrefuses and its message is surfaced verbatim rather than worked around. A shrink is\nrefused by the apiserver too, and there is no op that deletes a claim: the claim\nholds the only copy of the tenant's data, so this app can make one bigger and\nnothing else.",
Fields: map[string]string{
"ExpandClaimIn.cluster": "Cluster is the registered cluster the claim lives on.",
"ExpandClaimIn.gi": "Gi is the new size in GiB. It must exceed the current request; the\napiserver refuses a shrink and its message is surfaced verbatim.",
"ExpandClaimIn.name": "Name is the claim's name.",
"ExpandClaimIn.namespace": "Namespace is the claim's namespace.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","namespace":"acme-prod","name":"data","gi":200}`),
Response: json.RawMessage(`{"gi":200}`),
})
zip.Describe("PUT /v1/k8s/resources", zip.Doc{
Description: "Creates one custom resource of a registered kind, or patches the one\nalready there.\n\nFive separate get-then-create-else-patch ladders in the fleet collapse into this one\nname. Each of those wrote the ladder itself and each was a race; here the apiserver\nresolves the race, because a create that loses falls through to the patch.\n\nThe `spec` is the caller's domain payload and is applied whole — its SHAPE belongs to\nwhoever authored the CRD, not to this app, which would otherwise have to be edited\nevery time any kind grew a field. What this app owns is the KIND and the VERB, and\nboth are named and typed. That is the line: `kind` is a key in a closed table, so\nthis cannot become \"apply any object the cluster has\".",
Fields: map[string]string{
"ApplyIn.cluster": "Cluster is the registered cluster to apply to.",
"ApplyIn.kind": "Kind is the registered kind.",
"ApplyIn.labels": "Labels are merged onto the object.",
"ApplyIn.name": "Name is the object's name.",
"ApplyIn.namespace": "Namespace is where the object lives; empty for a cluster-scoped kind.",
"ApplyIn.spec": "Spec is the desired spec, applied whole.",
"Resource.version": "Version is the object's resourceVersion — the cursor a Watch resumes from.",
},
Example: json.RawMessage(`{"cluster":"hanzo-k8s","kind":"App","namespace":"acme-prod","name":"api","spec":{"image":"oci.hanzo.ai/acme/api:1.4.2","replicas":3}}`),
Response: json.RawMessage(`{"kind":"App","namespace":"acme-prod","name":"api","spec":{"image":"oci.hanzo.ai/acme/api:1.4.2","replicas":3},"version":"918281"}`),
})
}