Agent Workspace Files Flow
Overview
OCC privately stages authenticated creation inputs: AGENTS.md, SOUL.md,
IDENTITY.md, and USER.md. Compute prepares storage; the execution provider
initializes it before activation.
File operations authorize the exact active Agent through its Compute-resolved private Envoy endpoint.
Dedicated execution initializes the Harness workspace for Codex and native OpenClaw; see workspace and launcher boundaries.
See two-cluster transport for CP/DP routing.
Entry Points
apps/controller/src/index.ts:createFastifyAppaccepts initial contents throughPOST /namespaces/:namespaceId/agentsand handles liveGETandPUT /namespaces/:namespaceId/agents/:agentId/workspace/files/:name.packages/occ/src/index.ts:createAgentauthorizes creation and persists private setup state;deployAgentWithAuthorizationrechecks it, completed too, before each revision.
Live access requires the Agent's private HTTPRoute and native gateway. Configure Envoy Gateway, trust, and network restrictions through deployment; the routing contract covers credentials, hostnames, and CAs.
Flow
graph TD
subgraph Initial["Creation and first deployment"]
S["Create Agent with initial files"] --> T["Authorize and stage exact-Agent input"]
T --> U["Separate deploy request"]
U --> V["Initialize through Compute or selected Sandbox"]
V --> W{"Setup complete?"}
W -->|no| X["Block execution; retain pending input"]
W -->|yes| Y["Start runtime; activate Agent revision"]
Y --> Z["Clear staged bytes; retain completion metadata"]
end
A["GET or PUT Agent workspace file"] --> B["OCC authenticates and validates request"]
B --> C["Authorize exact Agent and resolve selected version"]
C --> D["Compute derives private Agent URL"]
D --> E["OCC reads service key and opens WSS"]
E --> F{"Envoy authenticates OCC?"}
F -->|no| G["503 dependency unavailable"]
F -->|yes| H["Overwrite identity and real IP; route to Agent Service"]
H --> I["Native gateway authorizes service identity"]
I --> J["Native file get or set"]
J --> K{"Result"}
K -->|read| L["Return name and content"]
K -->|write| M["Audit metadata and return name and size"]
K -->|missing| N["404 NOT_FOUND"]
K -->|unavailable| G
K -->|write uncertain| O["Audit UNKNOWN_OUTCOME; never replay"]Execution Trace
1. Creation validates and privately stages the inputs
apps/controller/src/console/agents/create.mts prefills four textareas from
/console/workspace-defaults.mjs (packages/contracts/src/workspace-defaults.ts).
It submits their values with WORKSPACE_DEFAULTS_ID.
apps/controller/src/index.ts:createFastifyApp rejects a stale defaults identity;
packages/contracts/src/workspace-setup.ts:normalizeInitialWorkspaceFiles
rejects unknown names, invalid Unicode, NUL, and values above 16 KiB UTF-8.
An absent or empty map follows ordinary startup without setup state; see
the create body limit.
packages/occ/src/index.ts:createAgent checks Namespace-scoped Agent creation,
exact Configuration read, and existing binding permissions. Its transaction
creates a stopped Agent and, when keys were supplied, a private workspaceSetups
record keyed by exact Namespace/Agent. No AgentRevision is created. Inputs do
not enter the Agent, Configuration, Agent revision snapshot, public response, or
metadata-only create audit. API strings are preserved; Console textareas use LF newlines.
packages/occ/src/index.ts:updateAgent keeps an omitted Configuration ID.
Completed setup is rechecked when the resolved Configuration ID or execution
mode changes. Saving Gateway managed mode alone keeps both. Pending setup is
checked on each update.
2. Deployment initializes storage before execution
apps/controller/src/worker.ts reads private setup state into
ComputeRevisionContext. A Driver without supportsWorkspaceSetup returns
WORKSPACE_SETUP_UNSUPPORTED. The worker serializes Agent startup and passes workspaceSetup to Compute.
Bundled Drivers use
apps/controller/src/drivers/compute/workspace-setup-runtime.ts:WORKSPACE_SETUP_RUNTIME.
Kubernetes delivers an Agent-owned Secret to the workspace owner's init container
(Gateway when embedded, Harness when dedicated); Docker uses a setup container
and durable volumes; SSH uses the protected Agent directory. Provider-owned
Harnesses require the Sandbox's validateWorkspaceSetup(harness, configuration):
Compute delegates validation of the relocated workspace, or returns 409 before
staging inputs. Preparation repeats this check.
OpenShell separately prepares native OpenClaw's private writable PVC subpaths, preserving retained entries. Ownership/mode checks, UID-fenced deletion and writer termination precede Sandbox creation. This permission setup carries no documents; see OpenShell preparation.
apps/controller/src/drivers/compute/kubernetes/index.ts:harnessRequirementsFromDeployment
hands Sandbox the setup identity and exact Secret reference.
After Sandbox readiness, apps/controller/src/drivers/sandbox/openshell.ts:initializeWorkspace
verifies the Secret's Namespace, Agent and setup identity, then calls authenticated
ExecSandbox with a fixed initializer and JSON stdin. Contents stay out of provider
configuration, arguments and environment; remote output is discarded. The launcher
waits for completion. A workspace flock serializes attempts after lost
acknowledgments. The verifier uses OPENCLAW_WORKSPACE_DIR after relocation.
The runner validates setup identity and workspace paths, rejecting symlinks and
conflicting contents. First setup requires the installed OpenClaw version and
template digest to match WORKSPACE_DEFAULTS_VERSION and WORKSPACE_DEFAULTS_ID
in packages/contracts/src/workspace-defaults.ts.
Native setup initializes workspace/Git without starting Gateway. Kubernetes init
containers use Gateway resource budgets. Supplied files, including empty strings,
atomically replace only absent, stock, or already-submitted content. The runner
reruns setup for BOOTSTRAP.md, verifies results, and atomically writes
.oce-workspace-setup.json.
Matching markers skip replay; incomplete writes revalidate. After recorded
completion, missing/mismatched markers block startup, preserving edits.
Kubernetes initializer logs can include allowlisted codes such as EROFS.
Temporary-directory cleanup preserves an earlier setup error.
apps/controller/src/drivers/compute/kubernetes/index.ts:safeRuntimeFailureObservation
reports exit 2 as WORKSPACE_SETUP_REFUSED; other failures retry
(WORKSPACE_SETUP_RETRYING). OpenShell raises SandboxWorkspaceSetupRefusedError on refusal,
reporting RPC status or exit/timeout only. Docker retries failures without
interrupting setup, staying unready. checkedAt records termination/observation time.
3. Activation clears staged contents and keeps completion metadata
apps/controller/src/worker.ts completes setup in the activation-completion
transaction only after checking the selected Agent revision ID and work claim.
workspaceSetups.complete removes document bytes and retains identity and
completion metadata. Drivers remove or replace private delivery bytes with
metadata.
Failed or never-deployed Agents retain pending inputs. Agent deletion removes
the setup record through packages/occ/src/index.ts:deleteAgent; Driver
cleanup owns runtime storage. No public endpoint reads or updates setup, and
live edits after activation do not update it.
4. Composition configures private access
apps/controller/src/api-startup.ts:startApi validates the optional absolute
OCC_GATEWAY_API_KEY_PATH before opening the database. Production and
PostgreSQL development composition bind the selected Compute Driver to
createWorkspaceFilesAccess. The worker uses that key for node enrollment.
API and worker Pods wait for the root Secret and load only public CA trust through NODE_EXTRA_CA_CERTS at
startup, never the CA signing key; the
TLS lifecycle
covers the automatic CA and explicit issuers.
Kubernetes derives endpoints from admitted IDs and Installation routing; Drivers without endpoint support cannot serve workspace files.
5. OCC admits one exact-Agent file operation
apps/controller/src/index.ts:createFastifyApp requires a valid user
session or scoped service API key. Native Agent credentials cannot invoke this
administration surface. GET needs Agent read; PUT needs Agent operate
and, for session callers, passes the browser CSRF boundary. OCC resolves the
selected AgentRevision before Compute endpoint resolution. After that grant, an
Agent with no running version (stopped, never deployed, or still activating)
gets 409 RESOURCE_CONFLICT naming the Agent and the next step.
Names, the PUT body, and content and body limits follow the
live file contract. The deadline and
disconnect signal cover admission and native access.
6. Compute resolves a route and OCC loads the current key
apps/controller/src/composition/workspace-files.ts:createWorkspaceFilesAccess
uses ComputeDriver.getGatewayEndpoint(revision) to resolve
wss://<hostname>[:<endpointPort>]/namespaces/<namespaceId>/agents/<agentId>.
Hostname defaults to Helm's Service DNS, port to 443; resolution does not
prove readiness.
kubernetes/index.ts:reconcileGatewayRoute provisions operator routing plus an
exact /node HTTPRoute and SecurityPolicy for dedicated runtimes.
Only operator routes allow admin UI subpaths; both strip authorization and cookies.
The node endpoint contract
owns route ownership across preparation, activation, stop and retirement.
prepareWorkspaceNode calls
gateway/node-enrollment-client.ts:createGatewayNodeEnrollment after Gateway
readiness. Each Harness kind uses an Agent-owned setup/identity Secret.
confirmWorkspaceNodeSetup retains setupReceiptDeviceId before node.describe.
packages/gateway-client/src/client.ts saves and reads back the token,
reconnecting token-only. src/gateway/server-methods/nodes.read.ts reports
deviceTokenAuthenticated from src/gateway/node-registry.ts, bound to the
current session and pairing generation. Promotion requires true; live
approval invalidates proof and forces reauthentication.
After OCC retains a receipt, expired status cannot trigger fresh credentials. First reconciliation after Gateway's 20-minute retention can renew setup without recovering its receipt (readiness TODO). Harness storage owns recovery. Native provisioning requires Sandbox; expired unpaired setup awaits Gateway.
- Gateway admits the seven file and workspace commands before pairing, preserving explicit denies.
addNodeEnrollmentStateretains Agent-scoped identity until Agent deletion in the Harness PVC's/home/node/.openclaw-node(0700, nonroot initializer).AGENT_WITH_NODE_ENTRYPOINTrunssetup --baselinewith admitted options, preserving edits, then supervises Codex and node undertini. Setup failure holds; stop waits for setup. Codex starts on managed PATH; node awaits complete setup. Neither receives OCC's key.- Both supervisors poll setup files. Validated changes replace only the node group after exit; malformed inputs preserve it. Operator stop cancels renewal. Harness storage defines timing and validation.
- The native renewal bridge returns bounded
NODE_NOT_READYresponses for invocations received before credential verification, permitting Gateway retry without premature execution. - With the status proxy, a Codex Gateway hot-loads
file-transferfrom an Agent-owned ConfigMap, replacing Codex's plugin runtime. Activation requires OpenClaw's report and an active plugin. The wrapper pollsplugins.listthrough the Gateway SDK, cancelling timed-out connections after eight seconds. Fixed auth refusal reportsGATEWAY_UNAUTHORIZEDand fails activation withAGENT_GATEWAY_UNAUTHORIZED; rate limits and pairing still wait. Without the proxy, the ID is set at Gateway start; losing it fails. - The Gateway's own
/home/node/workspacestays empty. ThroughcodexDynamicToolsExclude(keeping owner entries) Codex loses OpenClaw tools acting in the Gateway Pod (ls,read,write,edit,apply_patch,exec,process,gateway_exec,gateway_process,terminal,portal) or change the Gateway with operator authority (openclaw,plugins,gatewayself-update). Stream schedules and trigger scripts run in the Gateway, socron.triggers.enabledisfalse; timed automations still run Codex turns. The memory flush run gets Gateway-localreadandwrite, soagents.defaults.compaction.memoryFlush.enabledisfalse. Codex's native tools act in the Harness; file-transfer reaches it through the node. - For a Codex workspace-node Gateway’s
codexandopenairows,pinCodexProviderTransportdrops request overrides and replaces authored transports (always forcodex) withhttp://127.0.0.1:9, blocking Gateway-local model access. Modelreasoning,thinkingLevelMap, and valid nonemptycompat.supportedReasoningEffortsremain. All othercompatfields are stripped; specifically,thinkingLevelswould trigger native fallback. Capabilities are authored, not inferred. - At startup Gateway emits
runtime.gateway_settings_overriddennaming, never valuing, replaced or dropped settings.occ agent logsexposes names; the Collector exports only the event. Admission rejects non-listcodexDynamicToolsExcludeand non-object Codex pluginconfig,cron,cron.triggers,models,models.providers, or components ofagents.defaults.compaction.memoryFlush. It returns400 INVALID_REQUESTnaming the path without creating a revision. OCC's earlier model check returns the same error for non-object provider rows ormodelsthat are not Agent-model catalog lists. - Default reads cover the enrolled Agent's Harness workspace and managed skill roots for previews, browsing, bootstrap and outputs. Symlinks are not followed; explicit policies remain authoritative.
runtime-entrypoints.ts:configureWorkspaceNodePluginsdefaults writes to owner documents, memory,skills/**, the two ClawHub lockfiles,.openclaw/skill-installs/**, and staged inbound files. It leaves explicit node and wildcard policies unchanged.file.createpreserves existing files. Reads above 16 MiB retain caller and node limits; command admission does not replace path authorization.
The chart supplies worker credentials/public trust; Compute installs Envoy access. Memory uses node duplex and native file workers; index/embedding configuration stays on Gateway. Skills uses remote discovery, reads and policy-checked dependency installation. Hosts initialize their own image assets. Gateway Skill sources stay local; native turns can receive temporary resource copies. See the ownership table. Remote channel menus remain deferred to #241.
Only Harness mounts the dedicated workspace, generated images and Codex rollouts.
Rollouts retain history without guaranteeing restart recovery; see
restart limits.
The rest of CODEX_HOME stays Pod-local. External OAuth uses ephemeral placeholders
and metadata; the token service owns refresh. Codex's remote-media reader retrieves
reply artifacts before cleanup. Embedded storage is unchanged.
KubernetesComputeDriver.verifyPersistentVolumeClaim rejects RWX claims without
mutating them. The worker stops predecessors and suppresses their maintenance
before dedicated preparation. The storage contract
owns RWO, downtime and recovery limits. These contracts require matching runtime
images; local checks do not prove deployed acceptance.
The API reads the mounted key per operation, so new connections pick up Secret rotation without a restart. Missing routing or keys, invalid keys, expired deadlines, and unavailable targets fail closed. No URL or credential comes from the caller.
7. Envoy authenticates and routes the native connection
apps/controller/src/gateway/workspace-files-client.ts:requestNativeWorkspaceFile opens WSS with only the
service key in x-api-key. The client verifies the server hostname and CA;
there is no leaf pin, device enrollment, native token, or client-certificate
option. It connects as a backend operator with deviceIdentity: null and no
self-asserted scopes.
The Gateway-level Envoy SecurityPolicy verifies and strips the key. The exact
Agent HTTPRoute overwrites x-occ-identity, removes forwarded and native-scope
headers, and sets X-Real-IP to Envoy's direct downstream socket address. It
rewrites the upgrade path to / for the same-namespace Agent gateway Service. Namespace attachment labels, route ownership checks, and
restricted Kubernetes RBAC protect this mapping.
Kubernetes Compute renders
native trust
from Installation network.gatewayTrustedProxyCidrs; conflicting tenant trust
settings fail deployment. allowRealIpFallback accepts the genuine nonloopback
OCC connection address even within a shared Pod CIDR. NetworkPolicy admits only
Envoy to the native gateway; the CIDR is not an independent authentication
boundary. Native
hello grants operator.admin; reads also accept operator.read.
8. Native file access limits reads to 16 KiB
apps/controller/src/gateway/workspace-files-client.ts:requestNativeWorkspaceFile uses Hello's
sessionDefaults.defaultAgentId when embedded OpenClaw composition opts in and native ownership
is sole with selectionRequired: false. Other rosters retain the explicit
main target; other callers retain their targets. Dedicated Codex
binds only main to its workspace node, so deployment refuses a roster without
it (requireDedicatedMainAgentDefault). No roster RPC or
additional scope is required. The client invokes agents.files.get or agents.files.set. Reads enforce the response content limit and return
{ name, content }; writes return { name, size }. There is no list, delete,
compare-and-swap, generic RPC, chat bridge, or PostgreSQL file copy.
Certificate renewal under the same CA reaches new WSS connections without an OCC restart. Root-CA replacement follows the trust rotation requirements.
Writes audit only the Agent resource, authorization action, outcome, reason
when present, and file name. If a dispatched write has an unknown outcome,
OCC returns 503 UNKNOWN_OUTCOME, attempts the corresponding audit, and never
replays it. Cleanup closes the native client.
Debugging and Verification
- Check revision/work status, Driver support, native release/defaults identity and durable workspace placement. Missing initialized storage needs operator recovery, not creation payload replay or completion-marker deletion.
- A stale
workspaceDefaultsIdrejects creation with409 RESOURCE_CONFLICT; reload the Console create form and resubmit. Creation does not prove runtime initialization; verify the selected version and live content. - First use, retry, and redeploy require real-workflow proof.
- For
503 DEPENDENCY_UNAVAILABLE, check the Compute routing settings and key mount, then the Gateway, Certificate, SecurityPolicy, and HTTPRoute status. Check DNS/CA trust and exact NetworkPolicy peers before changing native auth. 403 FORBIDDENcan indicate missing exact-Agent IAM or session PUT CSRF rejection. Granting a native service scope does not change human IAM.- An authenticated native upgrade failure can indicate missing trusted-proxy configuration, a simultaneous token, a loopback real IP, or absent native identity scopes. Do not fix it by inventing a forwarded address.
- Testing distinguishes fixture and runtime proof: a calculated URL, ready proxy or rendered chart does not prove file writes or model use.
