Skip to content
Pools

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:

  1. 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.
  2. Upgrade the operator, and wait until every replica is running the new version. kobe status reports the version it is talking to.
  3. Only then write a portRange into a SandboxPool.

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.

Available for:
Apple macOS logomacOSMicrosoft Windows logoWindowsLinux logoLinux
Download Kunobi