Existing Kubernetes Namespace Placement Flow
Overview
An Installation administrator explicitly selects an operator-owned Kubernetes namespace while creating its platform Namespace. The running worker verifies authorization and isolation, binds exact tenant identity, and preserves the namespace after deletion. Other tenants retain deterministic managed placement; existing managed namespaces created with the previous deterministic name remain in place when their tenant markers and OpenClaw manager label are exact.
Entry Points
- Trigger:
POST /namespaceswith optionalexistingNamespace, subsequent worker provisioning or deletion, and Configuration CRUD. packages/occ/src/index.ts:OpenClawController.createNamespaceapps/controller/src/worker.ts:ControllerWorker.authorizeapps/controller/src/drivers/compute/kubernetes/index.ts:KubernetesComputeDriver.ensureNamespace- Assumptions: the operator exclusively dedicates an existing,
Activenamespace to its tenant and prepares its external-lifecycle annotation, restricted Pod Security labels, and tenant-local RoleBindings before the API request. The worker cannot verify foreign-Pod absence. The caller holds both Namespace-create and Installation-administerauthorization.
Before this flow starts, API and worker startup load the Installation YAML and
Kubernetes Compute validates resources.namespace.quota. Counted resources
must pass the native integer-precision rule: pods: "1.5" is refused with a
quota diagnostic before persistence or namespace operations. CPU "500m" and
whole-count suffixes retain their values. See the configuration reference.
Flow
graph TD
A["Create platform Namespace"] --> B{"existingNamespace selected"}
B -->|no| C["Provision deterministic managed namespace"]
C --> J{"Tenant marker already exists"}
J -->|current or previous managed name| H
J -->|wrong name or ownership conflict| K["Fail closed"]
B -->|yes| D["Authorize administer and persist unique selection"]
D --> E["Running worker reauthorizes and verifies exact external namespace"]
E --> F["Bind generated tenant identity while preserving external manager"]
F --> G["Provision owned infrastructure and mark Namespace ready"]
G --> H["Place workloads and ConfigMaps in the bound namespace"]
H --> I["Delete orphans and owned infrastructure; retain external ownership"]Execution Trace
1. Authorize and persist explicit external selection
packages/occ/src/index.ts:OpenClawController.createNamespace
internal/occcli/cli.go:namespaceCommand checks whether the caller supplied
--existing-namespace before creating its HTTP client. An explicitly supplied
value, including an empty string, must be a DNS-1123 label of 1 through 63
characters. Invalid values return a local error without a request. Omitting
the flag retains managed placement; an empty adoption flag cannot select it.
internal/occclient/client.go:Client.CreateNamespace sends an admitted nonempty
selection to the same API operation below.
POST /namespaces accepts { "name": "support", "existingNamespace": "customer-support-prod" }. Omitting existingNamespace keeps ordinary managed
placement and creates the current oce-<hash15> namespace name when no tenant
namespace already exists. Existing-namespace selection requires ordinary
Namespace-create permission plus Installation-level administer and the
selected bundled Kubernetes Compute Driver; Docker or external
Compute selections reject it with 409. OCC persists the exact physical name
with its generated platform Namespace ID before queuing worker provisioning.
Partial database uniqueness prevents simultaneous active claims for the same name.
2. Reauthorize and bind the exact external namespace
apps/controller/src/drivers/compute/kubernetes/index.ts:KubernetesComputeDriver.ensureNamespace
The running worker rechecks Installation administer immediately before
adoption. Compute reads the exact selected Kubernetes namespace and requires
Active status, openclaw.dev/namespace-lifecycle: external, all three
restricted Pod Security labels, and no foreign tenant markers or NetworkPolicies.
It binds its full Namespace-ID label and matching Namespace-ID annotation
together through a resourceVersion-guarded, non-forced patch; concurrent
ownership changes fail safely while preserving the manager and unrelated
metadata. Missing targets, termination, foreign ownership or additive policies,
ambiguous identity, and revoked authorization fail closed without managed
fallback. A missing worker RoleBinding keeps provisioning pending; a missing
API RoleBinding instead makes subsequent Configuration operations return 503.
KubernetesComputeDriver.validateConfiguration checks counted quota precision
through resource-quantities.ts:hasIntegerQuotaPrecision during startup, before
ensureNamespace can copy these values into the ResourceQuota. Invalid counts
therefore do not enter this adoption phase. This does not validate quota capacity
or establish that a cluster is reachable.
Compute reconciles its owned quota, limit range, and isolation policies; no worker pause, restart, or Installation setting is needed.
apps/controller/src/drivers/compute/kubernetes/index.ts:KubernetesComputeDriver.networkPolicies
renders allow-dns with UDP and TCP ports 53 and 5353, scoped to the
configured DNS peer.
For the separate preprovisioned Namespace profile,
KubernetesComputeDriver.verifyExistingNetworkPolicies compares admitted specs
with the declared operator policies before initialization. The operator-owned
OpenShellSandboxDriver.ensureNamespaceWorkspace check uses the same
drivers/kubernetes/network-policy.ts:networkPolicySpecToJson projection on both
observed and configured specs. It restores the SDK ingress alias and accepts
omitted empty rule lists only for explicitly selected policy directions. Other
spec differences still stop preparation; neither check repairs the policy.
3. Colocate Configuration and workload resources
apps/controller/src/drivers/compute/kubernetes/index.ts:KubernetesComputeDriver.reconcileDnsPorts
When preparing an Agent in a ready Namespace, Compute extends the installed tenant and dedicated Gateway DNS policies with missing ports. It verifies exact ownership and the configured DNS peer, then patches with the observed UID and resource version. The patch preserves the installed Pod selector, existing ports, peers, and other rules; policies from before network profiles continue to admit other Agents' unprofiled Pods. Conflicts fail the preparation pass without overwriting concurrent changes. A complete DNS grant causes no write.
apps/controller/src/drivers/configuration/kubernetes/index.ts:KubernetesConfigurationDriver
Configuration discovers the bound backing namespace by tenant label for each
ConfigMap operation. Managed discovery fails closed when multiple Kubernetes
Namespaces claim the tenant, when the claiming object lacks the full
openclaw.dev/namespace label and openclaw.dev/namespace-id annotation, or
when a managed namespace name is neither the current oce-<hash15> form nor the
previous oce-<slug>-<hash12> form. The previous form is accepted only for an
already discovered namespace with app.kubernetes.io/managed-by=openclaw-enterprise;
new managed namespaces still use the current name. Explicitly external Namespace
provisioning rejects Configuration creation with 409 until the worker marks
the Namespace ready. Compute uses the same resolved namespace for workloads,
dedicated Agent-owned shared PersistentVolumeClaims, private gateway state
claims, credentials, and deletion cleanup. Existing exact Namespace, Agent,
version, service-account, and child-resource ownership checks remain unchanged.
Version retirement preserves the current gateway and its owned claims; final
gateway teardown removes the exact-owned private and shared claims by UID.
4. Preserve externally owned namespaces during deletion
apps/controller/src/drivers/compute/kubernetes/index.ts:KubernetesComputeDriver.deleteNamespace
OCC first rejects deletion while Agents, Configurations, Secrets, service
accounts, or other Namespace contents remain. Any object still carrying this Namespace's ownership (the
app.kubernetes.io/managed-by=openclaw-enterprise and openclaw.dev/namespace
labels plus the openclaw.dev/namespace-id annotation) is therefore an orphan,
for example a Configuration ConfigMap whose create applied but whose transaction
then failed. Compute deletes such ConfigMaps and ServiceAccounts by UID, and such
Secrets too when repository credentials are enabled, the only setup in which the
single-cluster chart lets the worker list Secrets. Two-cluster Compute keeps
Secrets and Configurations in the control-plane namespace, which it deletes. A failed list or delete keeps the Namespace deleting.
Compute then removes its owned openclaw-quota,
openclaw-limits, allow-dns, allow-gateway-ingress, and default-deny
objects, preserving default-deny until last. External namespace objects, their
tenant markers, RoleBindings, the operator's Secrets, and unrelated resources
remain untouched.
Deleting a failed selection that was never bound to either tenant marker also
leaves the external namespace untouched; partial or foreign markers fail
closed. An already-missing namespace counts as deleted. After deleting an
unclaimed failed tenant, an administrator can correct preparation and retry.
Previously claimed namespaces cannot be readopted until an operator deliberately
clears the old openclaw.dev/namespace label, openclaw.dev/namespace-id
annotation and, in single-cluster Compute, openclaw.dev/gateway-namespace
label. Deleting a failed selection whose namespace still carries another
tenant's openclaw.dev/namespace label or namespace-id annotation fails
permanently and leaves the Namespace deleting; clear those markers, then have
the initiator repeat DELETE. Managed namespaces retain their existing
complete-deletion lifecycle.
Debugging and Verification
- Before creating the platform Namespace, inspect external lifecycle,
restricted Pod Security labels, tenant-local RoleBindings, NetworkPolicies,
and Installation
administerauthorization. After provisioning, verify exactopenclaw.dev/namespaceidentity and thenamespace-idannotation. - A refused selection reports only
status: "failed"in the API. The worker'sworker.completedline fornamespace.ensurecarriescode: NAMESPACE_INCOMPLETEand areasonnaming the failed check, for example the tenant marker key (openclaw.dev/namespacelabel,openclaw.dev/namespace-idannotation oropenclaw.dev/gateway-namespacelabel) that names a different Namespace. It never logs that marker's value, the other Kubernetes namespace that already claims the tenant, or the name of a NetworkPolicy the Namespace does not own; list those withkubectl. Old tenant markers are usually left by an earlier OCC Namespace on the same Kubernetes namespace. - In single-cluster Compute without repository credentials, an orphaned
Secret outlives deletion. List Secrets labelled
openclaw.dev/namespace=<id>andapp.kubernetes.io/managed-by=openclaw-enterprisewithkubectl, and delete those whoseopenclaw.dev/namespace-idannotation is also<id>. - Run
node --test tests/conformance/kubernetes-compute.test.mjsfor driver contract coverage, including current managed placement, previous managed-name discovery, duplicate-claim rejection, foreign ownership rejection, and cleanup through the resolved namespace. - Run
node --test tests/integration/kubernetes-compute-provisioning-real.test.mjsagainst the explicitly selected disposable cluster documented inAGENTS.md. Missing cluster infrastructure is an explicit verification gap.
