Ports
exposedPorts on a SandboxPool is the complete answer to "what may a caller
of this pool reach". Anything not in it stays unauthorized. Without that list,
a forward is a general tunnel into the sandbox's network namespace.
Each entry declares either one port or one contiguous range.
exposedPorts:
- name: http # one published port
container: workspace
port: 8080
- name: dev # a band, authorized for forwarding only
container: workspace
portRange:
start: 3000
end: 9999
The two shapes are not interchangeable.
port publishes one port. It becomes a named ContainerPort on the Pod
and a named port on the Sandbox's Service, and callers may address it by that
name or by its number.
portRange authorizes a band and publishes nothing — no ContainerPort,
no Service port. Callers reach a ranged port by number only:
kobe port-forward dev 8080:5173 # 5173 is inside 3000-9999
kobe port-forward dev 8080:dev # refused: one name cannot pick one port
A range's name exists for the manifest and the audit trail, not for callers.
Asking for it returns 400 with reason port_name_covers_range.
Which shape to use depends on the caller. An agent pool enumerates each
port its workload serves. A workstation pool for a human can authorize
3000-9999 once so a developer can reach a dev server they started without
editing GitOps. Undeclared is still unauthorized.
A single portRange is capped at 8192 ports. exposedPorts takes up to
64 entries, so eight adjacent bands still cover the whole port space. The cap
refuses a 1-65535 written because it was easier than choosing a range.
Kobe refuses a bad declaration when the pool is admitted, not when a forward
is attempted: an entry that sets both fields or neither, a range whose start
is after its end, a bound outside 1-65535, or a band wider than the cap.
Overlapping declarations union — a published 5173 inside a 3000-9999 band
is fine, and the port resolves either way.
A pool that declares only ranges gets no Service, because nothing is published. Port-forward does not need one; it addresses the Pod directly.
SSH through kobe ssh-proxy does not require a declared
port. The workspace image's kobe-sshd is reached over attach.
Rollout order
portRange is a new field on a schema that rejects unknown ones, so an
operator older than the release that introduced it will refuse to read a pool
that uses it. Roll it out in order:
- Apply the new chart's CRD manifests. Helm does not upgrade objects from a
chart's
crds/directory, so this step is explicit. See Deploy. - Upgrade the operator, and wait until every replica is running the new
version.
kobe statusreports the version it is talking to. - Only then write a
portRangeinto aSandboxPool.
Do not write a range between steps 1 and 2. An old operator that cannot
decode one pool does not simply skip it: it lists SandboxPool objects as a
typed collection, and one undecodable item fails the whole list. During that
window a single ranged pool can make the operator's pool watch fail, and can
turn pool listing into a 500 for every pool in the namespace. Finish step 2
first.
Applying a range before step 1 is the harmless failure: the older schema
requires port on every entry, the unknown portRange is dropped, and the
write is rejected for the missing port.
Apply pool manifests with strict field validation
kubectl apply sends fieldValidation=Strict by default, and you should keep
it that way for SandboxPool. Under the API server's default (Warn), an
unknown field is pruned and the write succeeds.
The case that matters is a portRange written alongside a port against the
older CRD:
exposedPorts:
- name: dev
container: workspace
port: 8080
portRange: { start: 3000, end: 4000 } # dropped by an older CRD
The new CRD rejects that entry — exactly one of the two fields. The old CRD
does not know portRange, so a non-strict apply prunes it and stores
port: 8080. Nothing is over-authorized, but 3000-4000 is silently absent.
Strict validation turns that into an error at apply time.
A misspelled portrange needs strict validation for the same reason. Without
it the unknown key is dropped: if the entry also set port, it is accepted as
a plain single-port declaration.