Installation Driver Package Loading Flow
Overview
The API and worker independently load trusted Installation YAML and construct their selected IAM, Compute, Configuration, Secret, and optional Sandbox capabilities. Only the API constructs the optional Backend client and ServiceAccount Driver. This trace follows package resolution through process composition and stops at request serving or worker reconciliation. Development without startup YAML uses the defaults traced in platform startup.
Entry Points
- Trigger: Start
apps/controller/src/server.mjsorapps/controller/src/worker.mjswith trusted Installation configuration. - Source:
apps/controller/src/composition/installation-config.ts:loadInstallationConfiguration,apps/controller/src/composition/driver-packages.ts:loadDriverPackage,apps/controller/src/composition/production.ts:composeProduction. - Assumptions: An operator has installed and selected the reviewed package; Install Driver packages owns installation, package formats, configuration examples, private registries, and deployment.
Flow
graph TD
subgraph Startup["Independent API or worker startup"]
A["Read trusted Installation selections"] --> B{"Bundled or packaged"}
B -->|bundled| C["Resolve built-in implementation"]
B -->|packaged| D["Resolve package identity and import code"]
C --> E["Validate Driver configuration and capability"]
D --> E
E --> F["Construct Configuration, optional Sandbox, Compute, Secret, and IAM factory"]
end
subgraph OCC["Control-plane ownership"]
F --> P{"API with ServiceAccount selection?"}
P -->|yes| Q["Build Backend client and ServiceAccount Driver factory"]
P -->|no| G["Construct IAM with platform state"]
Q --> G
G --> H["Select exact Drivers and attach lifecycle owners"]
H --> V["Freeze Driver-projected<br/>startup values"]
V --> I["Authorize requests and reconcile Namespace lifecycle"]
I -->|Installation administer| R["Return projected values<br/>or values unavailable"]
endExecution Trace
1. Resolve and validate selected Driver implementations
apps/controller/src/composition/installation-config.ts:loadInstallationConfiguration,
apps/controller/src/composition/driver-packages.ts:loadDriverPackage
The API, worker, and Helm Installation check load the bundled OpenShell implementation when validating its Credential Proxy or constructing its Sandbox. An Installation with Kubernetes Compute and neither OpenShell selection skips those imports.
Each process reads the same trusted startup YAML. Configuration, IAM, Compute,
and optional Sandbox selections may name an operator-installed package;
implementation identity comes from its installed metadata. The installed root
package.json accepts one leading UTF-8 byte order mark, as Node does; remaining
text must still be a JSON object with the selected name and exact version. Secret selection is
required in this YAML path and accepts only bundled Kubernetes Secrets.
Optional service_account selection identifies the bundled Backend member;
it has no package-loading path. The
operator installation guide defines the package,
pinning, registry, and configuration contract. TypeBox checks each selected
Driver's closed schema once per admission call before implementation-owned
semantic validation. requireConfiguration gives the callback an isolated frozen
copy, checks its field-error list, and returns the original input. Driver callbacks
cannot supply replacement data; malformed results and throws fail closed. Composition retains each definition with its selection, registers startup
and named contributions in packages/occ/src/configuration-registry.ts, and
rejects missing owner bindings, duplicate identities, or unsupported schemas before
construction. Invalid package exports, identity, capability, or lifecycle wiring reject
startup without fallback.
apps/controller/src/composition/installation-config.ts:selected captures inspection
values immediately after validation, before any constructor or package factory.
packages/contracts/src/configuration-definition.ts:serializeStartupConfiguration
copies actual input values and masks schema writeOnly fields or blocks.
It combines branch annotations conservatively through the existing resolver.
The separate immutable result cannot change when a factory mutates its own input.
Unsupported inspection reports unavailable without raw values or exception text.
The package root export resolver follows Node's
import() resolution: "." selects a subpath only at the top level (nested, it
is an unmatched condition name), conditions are Node's defaults (node,
import, module-sync, node-addons, default) in key order, and mixed
subpath and condition keys or numeric keys are invalid. Invalid targets and
unmatched conditions can select a later array entry; a matched null condition
ends that condition branch. The selected target is resolved as a URL inside the
package and percent-decoded; it must name an existing file exactly, with no
extension, directory index or main lookup, and an encoded separator or
directory is refused. Package containment, the compiled ESM check, and import
must then succeed before Driver construction. Import uses the checked realpath
with the selected URL's query and fragment preserved, so URL-qualified package
profiles retain Node's module identity. Containment rejects only a parent
directory path; an in-package filename that begins with two dots remains valid,
matching Node's export resolution. The entry must be .mjs, or .js
whose nearest package.json (searched from the entry's directory up to the
package root, stopping at a node_modules directory, as Node's import does)
declares "type": "module". The refusal names that package.json, or says the
entry is neither .mjs nor .js. Unlike Node, the check never detects ESM
syntax in a .js file outside a module scope. Missing files and import failures
do not select another target.
For packageless Compute, the exact id compute-ssh selects SshComputeDriver
with implementation occ/ssh. Every other packageless id retains Kubernetes
selection. The SSH reference owns the host
contract. Its helper is read at module import and sent through SSH
operations with a 180-second timeout; bindAgent supplies authoritative Namespace and ServicePrincipal
identity before version operations.
Installed packages run arbitrary, unsandboxed code with controller database, credential, Kubernetes, tenant, and authorization authority. An untrusted or malicious package can violate authorization and tenant isolation; package validation and lockfile integrity do not establish publisher trust.
Local Setup also calls the shared admission path before provisioning:
internal/occdev/sandbox_network_policy.go:loadSandboxNetworkPolicies invokes
scripts/validate-sandbox-network-policy.ts, which uses requireConfiguration
for platform structural validation and Driver semantic checks. A failure stops before creating development state
or invoking a container engine. API and worker startup log
DRIVER_CONFIGURATION_INVALID with field diagnostics that identify the setting
without echoing submitted values.
2. Construct the single authoritative runtime bundle
apps/controller/src/composition/installation-config.ts:loadInstallationConfiguration,
apps/controller/src/composition/driver-packages.ts:createExternalDriver
The loader constructs Configuration, optional Sandbox, Compute, and Secret
Drivers and returns them with the validated Installation and required
createIAMDriver(state) function. A selected Sandbox Driver is passed to bundled
Kubernetes Compute; selecting it with SSH or packaged Compute rejects startup. Bundled and
packaged IAM receive the same controller-owned platform state. The bundled IAM
Driver loads current policy for each identity lookup and authorization decision;
packaged Drivers must do the same, which operator review verifies because the
runtime cannot enforce package internals. Packaged factories must return their exact server-owned capability
and identity. The Helm upgrade hook's installation-check also runs this loader
with only NODE_ENV, OCC_CONFIG_PATH, the API's optional OCC_CONSOLE_THEME_DIR
and the Installation, theme and repository mounts, so factories must defer other
environment and file reads past construction.
Kubernetes-specific image and projected-credential requirements apply only to
bundled Kubernetes Compute. SSH preflight verifies local SSH files and probes
each configured host. Startup enforces the
production version-stage contract
before returning any production runtime. Development requires the five core
operations: ensureNamespace, deleteNamespace, prepareRevision,
stopRevision, and retireRevision. It permits Drivers without
activateRevision and deactivateRevision. The worker still fails closed if a
required stage becomes unavailable.
3. Construct the API-only Backend branch
api-startup.ts:startApi handles a selected
ServiceAccount Driver after loading the common bundle. It requires PostgreSQL,
Compute credential-storage methods, and an owning Backend definition. It reads
that Backend's apiKeyPath, constructs ChatGPTClient, and supplies the
ServiceAccount Driver factory to controller composition. The worker keeps only
nonsecret Backend metadata; it constructs neither the client nor this Driver.
The managed credential flow
continues through account creation, issuance, and deployment checks.
4. Load current policy and hand off lifecycle ownership
apps/controller/src/worker.ts:ControllerWorker.start
API and worker construct separate process-local Driver instances. Production composition validates persisted policy, creates the selected IAM Driver with platform state, and selects the exact common Driver identities with OCC. The API also registers its selected ServiceAccount Driver after composition. Worker startup checks persisted identities, including Secret and optional Sandbox, and attaches selected lifecycle owners once. Both processes pass the retained definitions into OCC; the worker also resolves the current policy contribution before live application.
apps/controller/src/composition/installation-config.ts:installationDriverConfigurations
forwards each selected identity and its retained redacted snapshot to the registry.
The registry keeps no raw startup input. Later startup-file edits do not change
these values; there are no Driver projection callbacks.
packages/occ/src/index.ts:OpenClawController.discoverConfigurations checks
Installation administer before returning these projections as
startupConfiguration. The Console renders them in expandable Driver sections.
Agent discovery retains its resource-owned actions and does not return startup
values. Neither path exposes executable validators.
The bundled IAM Driver loads current policy for identity lookup and
authorization; installed IAM Drivers must honor the same contract. Neither is
rebuilt or replaced after account or policy changes.
OCC owns exact-resource authorization and invokes Compute only during Namespace or Agent revision reconciliation. Embedded OpenClaw and dedicated Codex retain their existing Harness-owned runtime topology.
Debugging and Verification
- Run
node --test tests/integration/driver-plugin-installation.test.tsfor real installed IAM, Compute, and Configuration packages; production admission; IAM allow/deny evidence; Configuration CRUD; and OCC provisioning of an evidence file in a per-run temporary directory. - Startup failures emit
startup-errororworker.startup-error; inspect Driver identity, persisted policy, capability contracts, and lifecycle stages. - The integration uses
InMemoryPlatformState; it does not prove a running PostgreSQL worker, live Kubernetes, private registry, gateway, or model turn.
