Kubernetes API coverage¶
The API server (src/api/) listens on port 6443. It serves TLS and plain HTTP/1.1 on the same port, detected from the first bytes of each connection. Large responses stream with chunked transfer encoding.
Authentication and TLS¶
Bearer token authentication is optional and off by default. Set or disable a token, 16 to 128 characters, over the serial console with K32_CMD:TOKEN:<token> or K32_CMD:TOKEN:off.
- Unauthenticated requests receive
401 Unauthorizedwith a Status body. /healthz,/livez,/readyzand/versiondo not require authentication.- Worker kubelets read the same NVS
tokenkey and send it over plain HTTP on the LAN. SelfSubjectAccessReviewandSelfSubjectRulesReviewalways return allowed for authenticated callers, or for all callers when authentication is off.
On first boot, the control plane generates an ECDSA P-256 key and self-signed certificate in NVS for kube32-apiserver, localhost, the node name and the node IP. Run K32_CMD:CERT on the console to print the PEM certificate.
kubectl only sends bearer tokens over HTTPS. Configure kubeconfig with TLS:
kubectl config set-cluster kube32 --server=https://<control-plane-ip>:6443 --tls-server-name=kube32-apiserver --certificate-authority=<pem file> --embed-certs
kubectl config set-credentials kube32 --token=<token>
Without authentication, use http://<control-plane-ip>:6443 and no credentials.
Discovery¶
Discovery endpoints accept GET and HEAD.
| Path | Answer |
|---|---|
/version |
gitVersion v1.33.0-kube32, git commit, tree state, UTC build time, compiler and platform: darwin/arm64, linux/amd64 or freertos/xtensa. goVersion is empty |
/healthz, /livez, /readyz |
200 OK with body ok |
/api, /api/v1 |
core v1 |
/apis, /apis/<group>, /apis/<group>/<version> |
apps/v1, batch/v1, coordination.k8s.io/v1, discovery.k8s.io/v1, events.k8s.io/v1, networking.k8s.io/v1, authorization.k8s.io/v1, metrics.k8s.io/v1beta1, kube32.io/v1 |
/openapi/v3, /openapi/v3/<group version> |
OpenAPI v3 index and per-group documents listing the fieldValidation parameter without schemas. /openapi/v2 and unknown groups return 404 |
Resources¶
| Group | Kind | Short | Verbs and subresources | Notes |
|---|---|---|---|---|
core v1 |
Pod | po |
get, list, watch, create, update, patch, delete; status, log, binding |
Graceful deletion when bound to a node, 30 s by default or gracePeriodSeconds. DELETE returns the Pod for bound pods and a Status for unbound pods. Create, update and non-status patch add 300 s NoExecute tolerations for node.kubernetes.io/not-ready and node.kubernetes.io/unreachable unless already tolerated |
core v1 |
Node | no |
get, list, watch, create, update, patch, delete; status, proxy |
Registered by kubelets. /api/v1/nodes/<node>/proxy/storage/<path> supports GET, PUT and DELETE; PUT accepts chunked or Content-Length bodies up to 8 MiB by default, writes <path>.part and renames it into place |
core v1 |
Namespace | ns |
get, list, watch, create, update, patch, delete | Seeded at boot: default, kube-system, kube-public, kube-node-lease, kube32-cluster |
core v1 |
Service | svc |
get, list, watch, create, update, patch, delete | Selects Running pods for Ingress, k32_http_call and environment variables |
core v1 |
Endpoints | ep |
get, list | Read-only. Computed on read from Services and their pods |
core v1 |
ConfigMap | cm |
get, list, watch, create, update, patch, delete | Stored in the object store |
core v1 |
Secret | get, list, watch, create, update, patch, delete | Stored in the object store | |
core v1, events.k8s.io/v1 |
Event | ev |
get, list, watch, create, update, patch, delete | Kept in RAM, up to 64 events, and projected to both API groups |
apps/v1 |
Deployment | deploy |
get, list, watch, create, update, patch, delete; status, scale |
Manages pods directly using pod-template-hash |
apps/v1 |
DaemonSet | ds |
get, list, watch, create, update, patch, delete | Template restartPolicy must be Always or unset, 422 otherwise |
batch/v1 |
Job | job |
get, list, watch, create, update, patch, delete | Template restartPolicy must be Never or OnFailure, 422 otherwise |
coordination.k8s.io/v1 |
Lease | get, list, watch, create, update, patch, delete | Kept in RAM for node heartbeats with duration max(15 s, 3 * heartbeat). POST and PUT upsert. When a lease expires, the node is marked Ready=Unknown (NodeStatusUnknown) with node.kubernetes.io/unreachable:NoSchedule; after the eviction timeout (30 s by default) node.kubernetes.io/unreachable:NoExecute is added and pods are unbound and rescheduled once their matching tolerationSeconds elapses |
|
networking.k8s.io/v1 |
Ingress | ing |
get, list, watch, create, update, patch, delete | Routes HTTP traffic on port 80 |
networking.k8s.io/v1 |
NetworkPolicy | netpol |
get, list, watch, create, update, patch, delete | Validated and defaulted as by kube-apiserver. Egress rules restrict k32_http_call on the calling pod's node (NetworkPolicy); ingress rules are stored and not enforced |
discovery.k8s.io/v1 |
EndpointSlice | get, list | Read-only. Computed on read from Services and their pods | |
metrics.k8s.io/v1beta1 |
NodeMetrics, PodMetrics | get, list | Kubelet samples, up to 32 pods with a 30 s TTL | |
authorization.k8s.io/v1 |
SelfSubjectAccessReview, SelfSubjectRulesReview | create | Always returns allowed | |
kube32.io/v1 |
FirmwareRollout | fwr |
get, list, watch, create, update, patch, delete | Cluster-scoped. spec.sha256 names the staged firmware image. The rollout controller updates nodes one at a time and writes status. The API server serves the staged image at /apis/kube32.io/v1/firmware. See Firmware updates. |
POST /apis/kube32.io/v1/egress is the egress gateway: it makes the https:// calls of pods on workers, for nodes only. See Networking.
kubectl and k9s¶
kubectl and k9s work without extra flags. Supported commands include get, describe, apply, create, delete, patch, scale, logs, top, events and auth can-i.
When the client sends Accept: as=Table;v=v1;g=meta.k8s.io, list and single-object GET requests return meta.k8s.io/v1 Table responses for Pod, Node, Deployment, Event, Namespace, Service, ConfigMap, Secret, Lease, Ingress, Endpoints and EndpointSlice. DaemonSets, Jobs and metrics return plain objects for client-side printing.
OpenAPI v3 documents advertise fieldValidation. When kubectl sends fieldValidation=Strict, the server rejects unknown top-level and metadata fields with 400 BadRequest:
$ kubectl apply -f typo.yaml # metadata.lables instead of metadata.labels
Error from server (BadRequest): error when creating "typo.yaml": strict decoding error: unknown field "metadata.lables"
Write requests honour dryRun=All in the query string or in a DeleteOptions request body. The server validates the request and checks preconditions without writing to the store or firing watch events.
application/merge-patch+json and application/strategic-merge-patch+json apply a recursive JSON merge where objects merge and arrays or scalars replace. PUT and PATCH preserve uid, creationTimestamp and status, and increment metadata.generation when spec changes.
fieldSelector supports =, == and != on any dotted path. labelSelector supports =, ==, !=, in, notin, key and !key. List endpoints honour limit and continue and set remainingItemCount in the list metadata. Pod list endpoints accept ?inlineImages=omit (used by the kubelet) to omit kube32.io/wasm-base64 annotation payloads.
GET /api/v1/namespaces/{ns}/pods/{name}/log supports follow, previous, timestamps, tailLines, sinceSeconds, limitBytes and container. Logs live in a 2 KiB RAM ring per pod on the board, 1 KiB for the previous instance and 16 KiB on the simulator. Nodes with storage also write <root>/kube32/logs/<ns>_<pod>/<container>.log, capped at 32 KiB and rotated to .log.1 on restart.
Watches¶
?watch=true works on every stored resource collection. One hub thread in include/api/watch_hub.hpp owns all watch and log-follow streams:
| Setting | Value |
|---|---|
| Concurrent streams | 8 on a board with PSRAM, 4 without, 16 on the simulator; 429 TooManyRequests past the limit |
| Queue per stream | 16 events; overflow sends 410 Expired and closes the stream |
Replay ring for resourceVersion=N |
32 events; older versions get 410 Expired |
| Bookmarks | every 30 s with allowWatchBookmarks=true |
sendInitialEvents=true |
initial ADDED events followed by a BOOKMARK annotated k8s.io/initial-events-end |
| Stream lifetime | 1800 s, or timeoutSeconds |
Limitations¶
- Server-side apply and JSON Patch return
415 UnsupportedMediaType; usekubectl applywithout--server-side. - Request bodies must be JSON, except
ConfigMap,Secret,Namespace,Service,Deployment,Job,SelfSubjectAccessReviewandSelfSubjectRulesReview, which also accept protobuf (kubectl create configmap|secret|namespace|deployment|job|serviceworks); other kinds in protobuf return415 UnsupportedMediaTypewith the reason. - A persisted create or update that exceeds the object store capacity returns
500 InternalError(object store is full: X of Y bytes in use, N more needed), checked before writing; shrinking writes always pass, and a lost create race returns409 AlreadyExists. - There is no RBAC; authenticated callers have full access to all resources.
EndpointsandEndpointSliceare read-only and do not supportwatch.- Deployments manage Pods directly without
ReplicaSetobjects. deletecollection,exec,attach,portforwardandevictionare not supported.fieldValidation=Strictchecks top-level andmetadatafields only, so unknown fields nested insidespecare accepted.- Paginated lists with
limitandcontinueread from the current store state without a snapshot. - Request bodies are capped at 2 MiB, chunked request bodies return
411(except node proxy storage uploads), and headers larger than the 4 KiB board receive buffer return431. - Creating an object in a non-existent namespace succeeds, and deleting a namespace does not delete the objects in it.