Generated reference
This page is generated at build time from the crd-schemas JSON Schemas published by the waas repository, vendored at ref v0.2.0.
WorkspaceImage
WorkspaceImage is an admin-approved catalog entry.
Namespaced ยท kubectl get workspaceimages, short name wsi.
Every field of the manifest below carries its API documentation as
YAML comments โ the same text kubectl explain shows. Click a key
to fold or unfold its subtree; # required and defaults are noted
on the field line itself.
specโ
apiVersion: waas.xorhub.io/v1alpha1kind: WorkspaceImagemetadata:name: <name>namespace: <namespace># WorkspaceImageSpec is one admin-approved catalog entry. Only images present in the catalog AND enabled can be referenced (through a WorkspaceTemplate) by a Workspace; everything else is rejected at admission. The catalog is deliberately separate from WorkspaceTemplate: the template says HOW to deploy, this object records WHAT is approved and for WHOM, and disabling it must not tear the template down.spec: {โฆ}
# AllowedGroups restricts this image to members of at least one of these IdP (OIDC) groups. Empty = every authenticated user (still subject to the policy's image subset).allowedGroups:- <string># Architectures the image is published for. The operator turns this into node affinity (ARM64 control-plane vs AMD64 workers). Empty means any node.architectures:- amd64 | arm64# Catalog configures the periodic fetch of a published catalog manifest (format below) listing the images currently under this entry's registry, with display metadata (os/app/version/icon) for the portal catalog picker. Only meaningful when spec.registry is set (ignored on exact spec.image entries โ ENFORCEMENT never reads this field, it is purely cosmetic). Grouped under one struct (rather than flat fields) so a future split into its own CRD, if a real need for K8s-RBAC-level separation ever appears, is a mechanical lift instead of a field-by-field migration โ no such split is planned today, application-level authorization (the same model WorkspacePolicy already uses) covers any future need for a narrower "catalog editor" role. Absent = no automatic catalog; the registry approval itself still works.catalog: {โฆ}
# Auth configures how the live fetch authenticates โ only meaningful when From.URL is set (ignored, and rejected at admission if From points at ConfigMapKeyRef/SecretKeyRef instead, see the XValidation on ImageCatalogSpec above). Nested by method (one field per auth kind) instead of a flat credential reference, so a future method (basic auth, mTLS...) is a pure ADDITION โ a new sibling field on ImageCatalogAuth โ never a rename or a reinterpretation of what an existing field means. Absent = unauthenticated GET, the only mode the two known public catalogs (docker.io/xorhub, docker.io/kasmweb) need.auth:# BearerToken sends "Authorization: Bearer <token>" on the fetch.bearerToken:# SecretRef names an existing Opaque Secret (in the platform workspace namespace, same convention as WorkspaceImageSpec.ImagePullSecretRef) holding the token under the key "token". A missing/unreadable Secret, or one without this key, is a sync failure (status.catalog.lastSyncError), never a crash โ same fail-soft doctrine as the rest of the reconciler.secretRef: <string> # required ยท minLength: 1# From is the catalog manifest source (format below, ยง 1) โ exactly one of URL/ConfigMapKeyRef/SecretKeyRef, mutually exclusive (enforced on ImageCatalogSource below; never more than one set). URL is fetched live over HTTP(S); ConfigMapKeyRef/SecretKeyRef are read directly, no HTTP involved. Both are re-checked on the SAME periodic cadence (apiServer.catalogSyncInterval) โ no dedicated watch on the referenced ConfigMap/Secret (the api-server's CatalogSyncWorker deliberately reads both uncached, without the watch verb) โ a static, GitOps-managed catalog for an admin who prefers not to depend on a live registry endpoint. This is a first-class, permanent choice, not a stopgap-until-network-works: an admin picks ONE of the three and stays on it.from: # required {โฆ}
# ConfigMapKeyRef reads the manifest from a ConfigMap key in the platform workspace namespace instead of fetching it over HTTP โ the common case, since the content isn't secret, just a static admin-provided catalog. Key defaults to "catalog.yaml" when empty. Re-read periodically (apiServer.catalogSyncInterval), not just once โ no dedicated watch on the ConfigMap (uncached reads, no watch verb, by existing design). Not a corev1 ConfigMapKeySelector: that type marks key as REQUIRED in the generated schema, which would kill the default.configMapKeyRef:# Key inside it; empty reads "catalog.yaml".key: <string># Name of the ConfigMap.name: <string> # required ยท minLength: 1# SecretKeyRef reads the manifest from a Secret key instead, in the platform workspace namespace โ for an admin who wants the manifest content itself access-controlled. Key is REQUIRED (no default, unlike ConfigMapKeyRef): no naming convention is assumed for a Secret. Distinct from ImageCatalogAuth.BearerToken below: that one is a fetch CREDENTIAL for a URL source, this one IS the manifest content itself; the two are never the same Secret in practice but nothing in the schema prevents it, and they cannot both apply at once since URL/SecretKeyRef are mutually exclusive.secretKeyRef:# Key inside it โ required: no naming convention is assumed for a Secret.key: <string> # required ยท minLength: 1# Name of the Secret.name: <string> # required ยท minLength: 1# URL is the catalog manifest location, fetched live and periodically (apiServer.catalogSyncInterval).url: <string> # minLength: 1# Description is shown to users when picking an image.description: <string># DisplayName is the human-facing name shown in the portal catalog.displayName: <string> # required ยท minLength: 1# Enabled is the admin kill-switch: false blocks NEW workspaces immediately (existing ones keep running, see grandfathering) while keeping the entry, its history and its group bindings in place.enabled: <boolean> # required ยท default: true# Image is the exact reference approved by the admin. Templates must match it verbatim; pin the digest for immutability (the waas-images pipeline publishes immutable tags precisely for this). Exactly one of image/registry must be set.image: <string> # minLength: 1# ImagePullSecretRef names an existing kubernetes.io/dockerconfigjson Secret (in the platform workspace namespace) holding the pull credentials for this entry's registry. The operator copies it into each workspace's target namespace (imagePullSecrets are read by the kubelet in the POD's namespace) and wires it into the PodSpec. A missing or unreadable source is FAIL-CLOSED: the workspace does not start and its Ready condition says PullSecretMissing.imagePullSecretRef: <string># Protocols the image can serve. A template using this image must pick a port whose protocol is listed here.protocols: # required- vnc | rdp | ssh | kasmvnc# Registry approves every image UNDER this prefix instead of one exact reference โ e.g. "docker.io/kasmweb" approves docker.io/kasmweb/terminal:1.19.0 (path-boundary match: it never approves docker.io/kasmweb-evil/*). An exact image entry always beats a registry entry; among registry entries the longest prefix wins. Combine with tagPolicy: a whole-registry approval with moving tags allowed is the loosest possible gate.registry: <string> # minLength: 1# Resources are the per-workspace sizing hints and hard bounds for this image.resources: {โฆ}
# Default is applied by the portal when the user does not choose.default:cpu: <quantity>memory: <quantity># Max caps a single workspace of this image regardless of policy. The effective per-workspace cap is min(image.max, policy.perWorkspace).max:cpu: <quantity>memory: <quantity># Min rejects undersized workspaces (an IDE image needs real memory).min:cpu: <quantity>memory: <quantity># TagPolicy is the pinning discipline the matched template reference must satisfy: digest (must carry @sha256:โฆ), tag (fixed tag required โ :latest and tag-less references rejected), any (everything allowed, latest included). Unset defaults to "any" on exact image entries (the approval is verbatim) and to "tag" on registry entries (broad approvals stay pinned unless explicitly loosened).tagPolicy: digest | tag | any
statusโ
Reported by the operator โ read it, never write it.
# WorkspaceImageStatus is the observed state of a WorkspaceImage.status: {โฆ}
# Catalog is nil until the first sync attempt of a spec.catalog-configured entry.catalog:# LastSyncError is the most recent fetch/read failure, kept even after a later sync succeeds, so admins can see WHY it once failed.lastSyncError: <string># LastSyncTime is when the catalog was last synced successfully: the real fetch time for "Fetched", the time the ConfigMap/Secret was last read for "Static".lastSyncTime: <timestamp># Source says which From variant last produced entries: "Fetched" (From.URL, a live sync succeeded at least once) or "Static" (From.ConfigMapKeyRef/SecretKeyRef was read successfully at least once). Empty = never synced yet.source: <string>