Agent Plugin Deployment Flow
Overview
OCC validates selections against the Harness catalog and snapshots the Driver and policy at deployment. The Harness owns tools and approvals.
Entry Points
apps/controller/src/index.ts:createFastifyApp
- Trigger: Agent create/update with
plugins, then deployment. - Requires Agent permission, valid selections and a compatible PluginDriver.
- Source: HTTP handlers, OpenClawController, and bundled PluginDrivers.
Flow
graph TD
D0["Request discovery"] -->|Create| D1["Authorize Agent create"]
D0 -->|Saved| E0["Authorize Agent read/update"]
E0 -->|no credential| D6
E0 -->|bound| E1["Caller and Agent operate"] --> D6
E0 -->|managed MCP| M0["Check credential and discover server tools"] --> D5
E0 -->|credentialSource| D2
D1 -->|stored source| D2["Caller source operate"] --> D6["Select Harness catalog"]
D1 -->|token or none| D6
D6 -->|unsupported| D7["Unsupported"]
D6 -->|token or none| D4["Call PluginDriver"]
D6 -->|Secret| D3["Read and recheck"] --> D4
D6 -->|CredentialSource| D9["Check source and exact Gateway owner; read unless canceled"]
D9 -->|success| D10["Recheck authority, source and catalog"]
D10 -->|allowed and connected| D4
D9 -->|canceled or failed| D12["Stop"]
D10 -->|denied or disconnected| D12
D4 -->|disconnect| D8["Cancel hosted reads"]
D4 --> D5["Return safe metadata"]
A["Authorize and validate policy"] -->|valid| S["Save Agent selections"]
A -->|unsupported| Y["Reject write"]
S --> B["Revalidate and snapshot version"]
B -->|managed MCP| K["Project worker MCP and Gateway tool selections"]
K --> L["Sandbox applies account and tool request policy"]
L --> I
B --> C["Validate native metadata; grant selected plugins"]
C --> D["Attempt selected installs"]
D -->|install rejection or auth required| E["Disable failed selections; collect warnings"]
D -->|success| T["Resolve owned tools; translate final app policy"]
E --> T
T --> F["Verify native identity and effective policy"]
F -->|invalid or unsafe| X["Keep runtime unready"]
F -->|verified| G["Publish current startup status"]
G --> H["Apply matching gateway configuration"]
H -->|dedicated| P["Apply Gateway tool policy"]
P -->|incompatible| X
P -->|valid| I
H -->|embedded| I["Compute returns readiness and warnings"]
I --> J["Worker completes deployment under live claim"]
G -->|runtime restart| CExecution Trace
Credential-scoped discovery
Create discovery requires a
Harness, Agent create, and caller operate on stored sources. Wrong Namespace:
400; missing Secret: 404; unavailable explicit CredentialSource: 409.
Saved discovery requires a dedicated Agent and caller read/update. Bound sources
require caller and Agent operate; explicit sources require caller operate.
Missing or unready bound sources return 404. Hosted catalogs require credentials;
curated catalogs do not.
The OpenShell Backend reads metadata and exports through operator mTLS. External
Gateways retain optional withSourceToken. OCC checks source/Gateway ownership,
then rechecks authority, attachment and catalog before dispatch. Cancellation
discards late credentials; refresh stays with its owner.
packages/occ/src/index.ts:OpenClawController.discoverManagedMcpTools
Managed MCP uses Secret Driver, rechecking before tools/list.
driverPolicy.toolNames pins upstream names across runtimes.
Closing cancels; account changes clear results; Harness changes clear selections/approvers. Hosted reads
bound size/time, reject redirects and credential echoes, and return safe metadata
with Cache-Control: no-store.
apps/controller/src/drivers/plugin/hosted-catalog.ts:catalogEntry accepts omitted
requires_local_executor; explicit true without skills remains unavailable.
Details reject unmatched MCP/scheduled tasks. Optional stdio
NativeCodexPluginCatalogReader rejects invalid plugin/list results and stops its child.
1. Validate desired state under exact-Agent authority
apps/controller/src/index.ts:createFastifyApp
HTTP validates input. OCC checks the exact
Namespace and Agent (read for reads). Create/PATCH saves plugins and Driver
identity. Updates atomically save state and audit evidence. Omitted selections
persist, {} clears them, and nonempty maps replace them. Clear selections with
unknown or different owners before changing catalogs.
validateAgentPlugins checks policy and Configuration against the Harness
catalog on Agent/provisioning writes. Deployment rechecks Configuration.
OpenClaw refuses selections blocked by tools.deny, the sole agents.entries
tool list, the primary provider's byProvider lists, or their combination.
It also refuses selections beside an entry denying * or hidden under a
profile by the sole entry's alsoAllow.
openClawPluginSettingConflict
checks Gateway-rejected settings
first for OpenClaw Harnesses.
Unsupported controls or incompatible selections return 400 INVALID_REQUEST.
validatePolicies
detects conflicting enablement defaults. BundledPluginDriverBase.validate
allowlists it for OCC's fixed PluginPolicyValidationError advice.
Unrecognized refusals stay generic. The Console preserves rejected drafts.
Save keeps out-of-range
Plugin selections JSON text and skips Agent GET/PATCH.
Picker edits
stop. Available catalogs
remain browsable.
Read capabilities from GET /installation or GET .../plugins/capabilities
(Agent read/update).
2. Admit an immutable plugin deployment
packages/occ/src/index.ts:OpenClawController.deployAgent
OCC snapshots the Configuration, Driver and selections. Before cutover the worker checks the pinned Driver against the Harness. Gateway and worker each verify their assets from Compute’s package descriptors.
- CLI packages. The Gateway loads skills. The Harness runs the pinned executable. The guard checks operation and credential restrictions before injection.
- Hosted MCP packages. An account lets the worker advertise selected tools. The Gateway grants same-node execution per turn. Calls requiring Gateway hooks or another node return structured results or policy denials through the Gateway to Code Mode. The credential driver attaches the source. Sandbox supplies its placeholder and scopes worker and script requests. OpenShell enforces exact tool names and rejects overlapping routes.
- Native runtime plugins. Startup installs packages and verifies tool policy.
Withdrawal removes credential injection. Recovery preserves other attachments.
3. Deliver requested state through Compute preparation
apps/controller/src/drivers/compute/plugin-runtime.ts:pluginRuntimeSpecForRevision
Compute validates admitted state, Driver, and Harness. Kubernetes projects the
nonsecret request; Docker uses environment delivery with a 64 KiB size limit. For Codex, Compute
writes the policy the Gateway runs into native approval_policy before startup
validation: the configured appServer.approvalPolicy, with on-failure as
on-request. Omission preserves native defaults; incompatible explicit policies
remain subject to reviewer checks. At Agent save, provisioning and deployment, the Codex
PluginDriver refuses an automatic reviewer unless the policy is on-request or
on-failure, because an omitted policy lets the Gateway pick one the startup
check cannot see. Configuration save and Compute Drivers refuse policies the
Gateway refuses at load, such as untrusted.
The Kubernetes ConfigMap is immutable and per version. A version prepared
by an earlier controller keeps its earlier config.toml (without
[plugins._default] (#508), without approval_policy (#1995), or with
on-failure as written) until the Agent is deployed again; KubernetesComputeDriver.reconcilePluginRuntimeConfigMap refuses any
other difference. OpenShell dedicated Codex has no such allowance and needs a
new deployment after that upgrade.
SSH Compute rejects nonempty plugin maps and Agent default plugin approver policies before host effects.
Initial embedded Kubernetes gateway preparation applies exact-Agent HTTPS
egress before installation. For existing gateways, prepareRevision avoids
duplicate Agent database access. activateRevision uses Recreate, stopping
the old gateway before installation. Version files stay private and the native
registry stays in the Agent-owned database. Docker keeps native state in the
container's private temporary home.
4. Prepare native runtime state and hand off readiness
apps/controller/src/drivers/compute/kubernetes/runtime-entrypoints.ts:installOpenClawPlugins
Embedded OpenClaw validates selections against its bundled catalog and policy.
Grants enter nonempty tools.allow, otherwise tools.alsoAllow; denies and profiles
remain. Tool enabled overrides toolDefaults.enabled; disabled tools emit denies.
Master disable and operator denies prevail. The supported modes
provider_default and none add no plugin review. Version-private installs use --pin --force --no-enable,
preserving enablement and tool policy. Preparation refreshes the registry and verifies
configuration, plugin/package identity, versions, integrity and source containment
within the install path. Verification failures keep the Gateway unready. Confirmed
install rejection disables the optional selection and removes its tool allowance
before startup.
Dedicated launchers capture the Gateway tool policy
from the saved Agent revision before plugin grants. Startup and peer respawn
enforce it; failures keep the Gateway unready. Changes require deployment replacement
after affected old work ends. Human permissions stay unchanged. An embedded Gateway
applies a policy that the Configuration sets without this composition, so Compute checks
it at Configuration save and deployment admission with the same rules
(gateway-tool-policy.ts:validateGatewayToolPolicy), not on maintenance passes.
Dedicated Codex uses isolated CODEX_HOME, disabling app/plugin defaults.
Selections enable apps, plugins and remote plugins; disabled entries cannot execute.
The native policy mappings
describe the generated bridge.
Startup enables experimental APIs and resolves selected IDs through plugin/search.
codex-plugin-stdio.ts:withCodexPluginStdio retains fixed
nativeErrorType/nativeSubErrorType values, discarding other stderr/RPC content
and lines over 64 KiB while draining. RPC errors alone do not prove backend
installation.
runtime-entrypoints.ts:readCodexPluginDetails reads up to four
selections concurrently before and after installs. Batches preserve order and drain
before retries and final verification; writes stay sequential.
codexRuntimeArtifact uses detail.apps, excluding appTemplates.
codexInstallPlan validates policy and component support,
then grants selected plugin IDs through config/batchWrite before plugin/install
can report authentication requirements. App/tool grants follow; plugin defaults stay
off. Bundled skills need the compatible runtime below for selected-only activation.
Install rejections or missing app authentication warn. Explicit tool policies
require mcpServerStatus/list's codex_apps inventory; codexAppToolSettings
binds catalog action IDs through _meta._codex_apps.resource_uri. Native IDs
work. Unknown, unowned, ambiguous, or duplicate IDs fail startup.
After installation, startup rechecks catalog identity, version, apps and components
before final app/tool grants; plugin/read cannot inspect bundles.
writeCodexPluginConfiguration replaces owned plugin/app tables, enables successful
selections, disables failed/disabled plugins and unselected apps, and writes
inherited tool/account approvals from merged workspace settings. Lower-layer
descendants remain. Unspecified enablement stays unset; requirements still apply.
Failed-only apps stay disabled; disabled selections skip installation/status.
Workspace-aware readback rejects policy conflicts before readiness. Disabled apps may
retain inherited fields. Categories resolve app → global → native true; equivalent
values/nulls pass. Tool enablement cannot bypass categories.
verifyCodexPluginConfiguration rejects explicit enablement of unselected plugins;
approval-only or MCP-policy-only entries inherit the verified plugin default-off
setting. Source/account-disabled plugins remain disabled.
runtime-entrypoints.ts:startAuthenticatedCodex passes the admitted Harness model,
without its provider prefix, to the app-server after the authentication probe
succeeds. Native configuration readback therefore exposes the model used for
startup policy validation.
runtime-entrypoints.ts:verifyCodexReviewerConfiguration checks app/link reviewers
and configRequirements/read. Forbidden reviewers, incompatible automatic-review
settings or conflicting model requirements block startup. Checks exclude later
workspace/session/model changes and strict review. Codex 0.156 readback omits
execution-time managed app/tool requirements; effective-policy proof
remains necessary.
runtime-entrypoints.ts:disableCodexSelectionsWithoutChatGptLogin disables enabled
selections and app/plugin features for API-key logins when best-effort preparation
allows authentication warnings. API-key logins cannot install remote plugins.
Startup verifies both default-off tables before handing off readiness.
For Compute-owned Kubernetes workloads, only an admitted {pluginId, code}
warning results from a selected OpenClaw install's normal nonzero exit, a
matching Codex plugin/install error, or a successful Codex install needing app
authentication. Transport loss, timeouts, signals, malformed responses,
discovery failures, and policy failures retain ordinary startup failure
behavior. Provider-owned Harnesses keep their existing startup path.
Kubernetes Compute streams private startup status with a 65,536-byte limit. Overflow cancels the response without retry, even for a 404/503; within the limit those codes mean not serving. Readiness verifies workload, version, startup instance, selection keys and warnings. Docker Codex healthchecks fail on interrupted status bodies, preserving the transport code or original reason.
With the tool proxy enabled, dedicated Codex reads bundled skills at
/home/node/.codex/skills/.system without a repository broker or neighboring
credentials. Startup links /home/node/.openclaw/plugin-skills to
/home/node/openclaw-runtime-assets/plugin-skills, keeping gateway state private.
Gateway blocks failed bridges before serving and retry during turns. Startup changes refresh effective configuration; untrusted status cannot establish readiness or change version selections.
Compute installs status NetworkPolicies before the first dedicated gateway and creates it only when the Agent and plugin status are ready and its Service selects the version. Existing gateways and full runtime policies retain their activation boundary.
The Agent and gateway derive an app-server credential from the transport Secret,
Agent revision ID, and Agent startup ID. The gateway receives it after matching status
and rendering exclusions. After a Harness restart, the old credential cannot open
a new authenticated app-server WebSocket to the replacement Harness. It does not
revoke access to a still-running old Harness or an established connection. For a
changed peer, the supervisor publishes non-ready, restarts only OpenClaw and
rechecks peer startup, Pod, successes and failures after it serves. Changed or
unavailable peers trigger container restart.
The supervisor repeats a stop signal that OpenClaw ignored during its own in-place
restart, seen as readiness returning after its server closed.
For writable native-admin configuration, recovery reads the bridge baseline from
the read-only managed snapshot. A Pod-local record binds the last and pending
generated bridges to the version and snapshot hash before configuration writes.
Process and same-Pod container restarts rebuild only a matching generated bridge.
Other native edits survive; conflicting bridge edits retain the existing refusal.
An unreadable or mismatched record holds startup unready, reporting runtime
failure peer-bridge-record (UNAVAILABLE), and logs the remedy: replace the
Pod, which restores the snapshot and clears this private record.
During an outage, the supervisor reports unready. Kubernetes propagates that
state asynchronously, so the signal alone is not a per-request traffic fence.
If OpenClaw exits while the supervisor waits for its peer, the wrapper exits
for container recovery.
5. Complete version reconciliation
apps/controller/src/worker.ts:finalizeRevision
Before the worker commits activeRevisionId, preparation failure leaves the
prior pointer unchanged. After commit, activation/finalization failure retains
the candidate pointer and records REVISION_FINALIZATION_INCOMPLETE for retry.
Embedded Kubernetes replacement runs after commit; the old gateway may already
be stopped. The previous version remains stored, without pointer rollback or
an availability guarantee during cutover. See the controller worker flow.
Successful completion reports REVISION_ACTIVATED or REVISION_ALREADY_ACTIVE.
A candidate pointer alone is not readiness evidence. Agent turns use native
policy; the workspace and gateway database remain Agent-owned.
The worker saves plugin warnings with successful work under its live claim; the worker flow covers persistence and deployment status. Claim loss blocks stale completion; later workers recheck readiness. No receipt acknowledgment, failed-plugin shutdown, or permanent failure latch exists. Warnings describe deployment completion, not ongoing health.
Debugging and Verification
The OpenClaw model probe's CPU-based deadline cannot exceed 600 seconds. CPU wait is the larger PSI or throttled
delta, capped by throttled periods; missing, nonfinite or reset samples cannot establish starvation. An OpenClaw
timeout is starved only if over a quarter of its last 15 s is CPU wait and no 5 s stretch is idle
(timeoutWindowMs, timeoutWindowCpuWaitMs, timeoutWindowIdleMs).
- Compare
Agent.pluginswith the version snapshot and deployment status. An Agent write alone does not prove runtime installation. - Invalid or unsupported policy leaves Agent desired state unchanged. Startup checks catalog membership, authenticated metadata, ownership and effective native configuration; failure keeps the candidate unready.
- When readiness fails, check native packages, release drift, connector authentication and effective policy; keep credentials out of logs.
- On an SSH Compute refusal (section 3), clear plugin maps and default approver policies, or use compatible Kubernetes.
- For plugin warnings, check deployment status for
PLUGIN_INSTALL_FAILEDorPLUGIN_AUTH_REQUIREDand the admittedpluginId. Confirm its runtime and gateway entries are disabled. Never infer plugin attribution from native logs. - Prove behavior with a model-chosen plugin call in a normal Agent turn, then disable or remove it and verify another Agent is unchanged. Source or fixture tests are not native runtime proof.
