From 66310c6259499fb40e0e4e7748072fce1a90a1c0 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Sat, 11 Jul 2026 14:04:31 -0700 Subject: [PATCH 1/9] Add managed configuration RFC --- rfcs/0019-managed-configuration.md | 570 +++++++++++++++++++++++++++++ 1 file changed, 570 insertions(+) create mode 100644 rfcs/0019-managed-configuration.md diff --git a/rfcs/0019-managed-configuration.md b/rfcs/0019-managed-configuration.md new file mode 100644 index 00000000..ea2d9eb1 --- /dev/null +++ b/rfcs/0019-managed-configuration.md @@ -0,0 +1,570 @@ +--- +title: Managed Configuration +authors: + - Gio Lodi +created: 2026-07-10 +last_updated: 2026-07-10 +status: draft +issue: +rfc_pr: https://github.com/giodl73-repo/rfcs/pull/1 +--- + +# Proposal: Managed Configuration + +## Summary + +Add a core configuration-authority model that recursively composes an ordered +list of named configuration layers through the existing OpenClaw schema. +OpenClaw rejects conflicts or attempts to weaken authority established by an +earlier layer and produces one effective configuration with inspectable +provenance. A host-managed document plus an operator document is one deployment +profile of this generic model, not a pair of roles hard-coded into the engine. + +## Motivation + +OpenClaw currently has one ordinary configuration authority. A hosting platform +that must enforce deployment posture while preserving operator customization +therefore has to generate config fragments, project environment variables, +rewrite files in a particular order, remove stale generated values, and keep +custom logic synchronized with OpenClaw's schema. + +The missing distinction is not a new `hosting` config section. It is authority +over fields in the existing configuration schema: + +```text +OpenClaw defaults + + host-managed configuration + + operator configuration + -> admission and cross-field validation + -> effective configuration + -> runtime +``` + +Without an upstream contract, hosts silently overwrite operator values or carry +private strictness logic. Operators cannot reliably explain why a value is +effective, and support teams cannot reproduce the deployment from declared +inputs. Repeated host patches then track OpenClaw config internals instead of a +stable composition contract. + +Managed Configuration also gives OCC a clean future boundary: OCC can compile +admitted desired state into a managed document while OpenClaw remains the owner +of schema validation, field semantics, and effective runtime configuration. + +## Goals + +- Load an ordered list of named layers using the normal OpenClaw configuration + schema. +- Let presence in an earlier layer declare authority over that field for every + later layer. +- Reject conflicts with structured findings instead of silently overwriting. +- Support exact authority for every schema field. +- Support a small closed set of OpenClaw-defined bounded strictness rules. +- Run normal cross-field validation against the composed effective config. +- Expose redacted effective values, provenance, control mode, and stable config + identity for diagnostics and conformance. +- Associate mutability with a layer source rather than a built-in role, and + prevent writes from bypassing earlier authority. +- Make unsafe startup composition failures visible through status/readiness. +- Allow hosts to replace private config writers with a release-tested contract. + +## Non-Goals + +- A new top-level `hosting` section containing copies of existing settings. +- Numeric priorities, implicit source discovery, or dynamic layer reordering. +- A policy expression language or host-defined comparison functions. +- Silent managed-value precedence. +- Dynamic OCC reconciliation in the first version. +- Secret delivery, trusted identity, lifecycle, state synchronization, or + plugin installation. +- Depending on the optional Policy plugin for runtime correctness. +- Making every field support a monotonic "stricter" relationship. + +## Proposal + +### Layers and recursive composition + +The input is an ordered list of layer descriptors from strongest to weakest: + +```text +layer[0] -> layer[1] -> ... -> layer[n] +``` + +Each descriptor has a stable layer identity, an opaque source reference, and an +explicit `read-only` or `read-write` access capability. Resolution returns one +document using the existing OpenClaw schema plus a stable redacted source +identity. Names such as +`platform`, `tenant`, `team`, and `operator` are diagnostic labels supplied by a +deployment; they have no special semantics in core. + +Independent sources may resolve concurrently, but composition always retains +the descriptor array's declared order. Empty or duplicate layer identities fail +before source I/O. Failure to resolve any source rejects the complete candidate; +OpenClaw does not silently omit the failed layer. + +Composition applies one operation repeatedly: + +```text +state[0] = empty authority state +state[i + 1] = compose(state[i], layer[i]) +effective = materializeWithDefaults(state[layerCount]) +``` + +```mermaid +flowchart LR + A[Ordered source descriptors] --> B[Resolve every source] + B --> C[Admit cloneable sparse documents] + C --> D[Recursive exact-authority fold] + D --> E[Validate effective configuration] + E --> F[Normalize and materialize defaults] + F --> G[Publish one runtime snapshot] + B -. any failure .-> X[Reject complete candidate] + C -. any failure .-> X + D -. conflict .-> X + E -. schema or cross-field failure .-> X +``` + +```mermaid +flowchart TD + S0[Empty authority state] --> L0[Compose layer 0] + L0 --> S1[Authority state 1] + S1 --> L1[Compose layer 1] + L1 --> S2[Authority state 2] + S2 --> LN[Repeat for layer n] + LN --> SE[Composed sparse source] +``` + +For each leaf, the first declaring layer establishes its schema-defined control +rule. Every later layer may omit the leaf, repeat the same authored exact +value, or tighten a bounded value. It may not replace an exact value or weaken a +bounded value. A leaf omitted by every layer receives its ordinary OpenClaw +default after composition. + +Authority compares authored source values before schema normalization. Two +different accepted representations remain different exact claims even if the +ordinary schema would later normalize them to the same runtime value. This +keeps admission recursive and context-free: a layer cannot acquire a different +meaning merely because another layer supplies a cross-field dependency. + +Schema normalization, defaults, plugin-aware validation, and runtime-derived +values apply only after the complete fold. They therefore never create +authority claims. Cross-field rules see the composed effective candidate rather +than an incomplete sparse layer. + +Arrays are whole-field values unless a specific built-in bounded rule applies. +Objects are traversed according to the normal schema; declaring one child does +not implicitly claim unrelated siblings. + +Absence means that a layer makes no declaration for the path. Removing a path +from a writable layer relinquishes that layer's claim when the chain is +recomputed. `null` is a declared value only where the schema accepts it; it is +not a generic deletion marker. + +Empty objects declare no leaves and canonicalize away without individual +warnings. A layer with no declarations is valid, allowing staged or temporarily +empty sources, but inspection and status report one `NoDeclaredValues` +advisory for that layer. + +The ordered layer list is selected at process startup through one explicit core +loading mechanism. Each source reference identifies one document, its immutable +source identity, and an optional expected digest. Order is declared once; it is +not inferred from filenames, environment discovery, or numeric priority. + +OpenClaw resolves includes, environment interpolation, and secret references +independently within each source before authority admission. These mechanisms +cannot read from, write to, or gain precedence over another layer. Every +resolved layer must be a cloneable plain-object document. Ordinary schema, +plugin-aware, and cross-field validation run against the composed candidate. + +The runtime reports source identities rather than host paths where paths would +leak deployment details. A host can prove which inputs produced an effective +snapshot without making its filesystem layout part of the contract. + +### Authority rules + +#### Exact authority + +A later layer must omit the field or provide the same authored value. A +different value is an admission error. + +```json +{ + "path": "gateway.auth.mode", + "reason": "ControlledByEarlierLayer", + "controllingLayer": "platform", + "controllingValue": "trusted-proxy", + "conflictingLayer": "operator", + "conflictingValue": "token", + "control": "exact" +} +``` + +Exact authority is the default and works for every schema field without adding +field-specific policy logic. + +Equal exact declarations are accepted. Provenance records each declaring layer +while the earliest remaining declaration stays controlling. Removing or +reordering a layer recomputes the complete chain from source documents; derived +authority state is not persisted as another configuration source. + +#### Bounded authority + +OpenClaw may mark a small closed set of fields with a monotonic comparator. A +later layer may preserve or tighten the inherited boundary but cannot weaken +it. Comparator composition must be associative so the same operation can be +applied recursively across any number of layers. + +Initial comparator classes are: + +| Comparator | Composition rule | +| --- | --- | +| Allow-set ceiling | Operator set must be a subset of the managed set | +| Deny-set floor | Operator set must be a superset of the managed set | +| Maximum limit | Operator value must be equal or lower | +| Minimum requirement | Operator value must be equal or stricter | +| Required protection | Operator cannot disable it | +| Disabled risky capability | Operator cannot re-enable it | + +Comparator assignment and ordering are owned by the OpenClaw schema. Layers +cannot attach a comparator to an arbitrary field or redefine "stricter." +Each comparator must be deterministic, idempotent, and associative over its +supported domain so recursive composition does not depend on fold grouping. + +### Effective configuration + +Composition happens once before runtime consumers observe configuration: + +```text +resolve each source independently + -> sparse-document admission + -> authority admission + -> effective composition + -> existing schema, plugin-aware, and cross-field validation + -> existing runtime materialization + -> immutable runtime snapshot +``` + +Gateway, plugins, tools, sessions, and state modules consume the same effective +snapshot. They do not independently merge managed and operator values. + +The effective result reports two identities: an effective-content identity for +the normalized, redacted effective configuration, and an authority-chain +identity for the ordered authored claims and control metadata. Source delivery +identities are separate diagnostic metadata. Secret values are never included +in diagnostic output or hashes in a way that exposes plaintext. + +### Findings and provenance + +Validation returns structured findings suitable for CLI, status, doctor, admin +UI, and automation: + +```json +{ + "valid": false, + "findings": [ + { + "path": "tools.exec.ask", + "reason": "WeakerThanManagedRequirement", + "managedValue": "always", + "operatorValue": "never", + "control": "minimum-requirement" + } + ] +} +``` + +Effective inspection reports provenance without exposing secrets: + +```json +{ + "path": "tools.alsoAllow", + "value": ["read", "sessions_list"], + "authority": "operator", + "managedBoundary": ["read", "sessions_list", "exec"], + "control": "allow-set-ceiling" +} +``` + +At minimum, OpenClaw should support machine-readable operations equivalent to: + +- validate managed plus operator inputs; +- inspect the effective configuration; +- explain one path's value, authority, and comparator; +- report the effective configuration identity through status. + +Exact command names and loading flags should follow existing config CLI and +Gateway conventions during implementation. + +### Mutation behavior + +Mutation APIs target one explicitly writable layer, including a middle layer. A +write that would violate authority inherited from an earlier layer fails before +persistence; a valid declaration may establish authority over later layers. +Read-only layers are not writable through normal config APIs, regardless of +label. + +Config reload follows the existing hot-reload/restart classification after a +complete effective candidate passes admission and cross-field validation. +OpenClaw publishes one runtime snapshot; a failed reload leaves the previous +snapshot active and does not partially activate the candidate. At startup, +failure remains visible and prevents readiness when the runtime cannot safely +operate under the declared host boundary. + +Layer resolution and admission invoke snapshot publication exactly once with a +complete source/runtime candidate. Source resolution, sparse-document admission, +authority, or effective-validation failure does not invoke the publisher. +Atomic preflight and publication remain owned by the existing runtime snapshot +boundary; the layer engine does not own snapshot globals or reload actions. + +Layer writes use the existing config mutation preflight and conflict model. The +write must be validated against the target source and authority-chain +identity captured for that mutation. If either changed, the write fails with a +structured conflict before persistence. This extends the existing write path; +it does not introduce dynamic ownership transfer or a general reconciliation +protocol. + +### Example deployment profiles + +A local personal installation can use one writable layer. A simple hosted +deployment can use two layers. A control plane can use more: + +```text +platform -> tenant -> agent/team -> operator +``` + +These are profiles of the same ordered recursive model. Core does not assign +special behavior to any name or require a fixed layer count. + +### Lobster usage case: Scout, tenant, and operator config + +A realistic Lobster deployment uses three layers. The names describe this +deployment; core still treats them as generic ordered sources. + +The Scout global layer establishes fleet-wide Gateway security posture: + +```yaml +id: scout-global +access: read-only +config: + gateway: + mode: local + auth: + mode: trusted-proxy + trustedProxy: + userHeader: x-scout-user + requiredHeaders: + - x-scout-tenant + controlUi: + dangerouslyAllowHostHeaderOriginFallback: false + allowInsecureAuth: false + dangerouslyDisableDeviceAuth: false +``` + +The tenant layer supplies private-network facts that differ per deployment, +including its Tailnet exposure, trusted proxy range, and tenant URL: + +```yaml +id: tenant-network +access: read-only +config: + gateway: + bind: tailnet + trustedProxies: + - 100.96.0.0/12 + tailscale: + mode: serve + serviceName: svc:openclaw-acme + controlUi: + allowedOrigins: + - https://openclaw.acme.internal +``` + +The operator layer remains writable for ordinary customization: + +```yaml +id: operator +access: read-write +config: + gateway: + controlUi: + enabled: true + logging: + level: info +``` + +```mermaid +flowchart LR + S[Scout global
security baseline] --> F[Recursive fold] + T[Tenant network
URL, proxy, Tailnet] --> F + O[Operator
ordinary customization] --> F + F --> V[Effective validation] + V --> R[One ordinary OpenClaw runtime config] +``` + +OpenClaw publishes the same ordinary runtime shape that an unmanaged +installation would consume: + +```yaml +gateway: + mode: local + bind: tailnet + auth: + mode: trusted-proxy + trustedProxy: + userHeader: x-scout-user + requiredHeaders: + - x-scout-tenant + trustedProxies: + - 100.96.0.0/12 + tailscale: + mode: serve + serviceName: svc:openclaw-acme + controlUi: + enabled: true + allowedOrigins: + - https://openclaw.acme.internal + dangerouslyAllowHostHeaderOriginFallback: false + allowInsecureAuth: false + dangerouslyDisableDeviceAuth: false +logging: + level: info +``` + +No Gateway, Control UI, logging, or plugin consumer needs to know that three +sources produced the document. Provenance retains the distinction for +inspection and write preflight. + +If the operator later attempts to replace the tenant URL, OpenClaw rejects the +write before persistence: + +```json +{ + "path": "gateway.controlUi.allowedOrigins", + "reason": "ControlledByEarlierLayer", + "controllingLayer": "tenant-network", + "controllingValue": ["https://openclaw.acme.internal"], + "conflictingLayer": "operator", + "conflictingValue": ["https://example.invalid"] +} +``` + +The same rule protects Scout global security posture and tenant-private network +facts without hard-coding Scout, tenant, or operator as engine roles. This +replaces Lobster-specific fragment generation and stale-value cleanup while +preserving the existing runtime config API. + + +### Policy plugin relationship + +Core owns composition, authority metadata, comparators, findings, and startup +enforcement. The optional Policy plugin may reuse the comparator and finding +machinery for diagnostics, repair suggestions, or conformance checks, but core +must not depend on that plugin. + +### Conformance + +Release tests should cover: + +- exact conflicts across representative scalar, object, and array fields; +- every supported bounded comparator and authored-representation edge case; +- source immutability and operator write rejection; +- redaction and stable effective identity; +- cross-field validation after composition; +- reload/restart behavior; +- invalid-startup readiness/status behavior; +- compatibility when a newer managed document references an unsupported field + or comparator; +- transactional reload and preservation of the previous effective generation; +- stale operator-write rejection across managed-boundary rotation; +- source digest mismatch and source-identity redaction; +- semantic equivalence between direct effective config and composed managed + plus operator inputs. + +Hosting Profiles may declare Managed Configuration as an optional capability, +but profiles do not own its semantics. + +### Implementation sequence + +The RFC is delivered as small, independently reviewable PRs. No PR needs to +implement the complete RFC: + +1. **Exact-authority core.** Add the pure recursive fold, authored-value conflict + findings, provenance, effective validation, and focused tests. No startup, + persistence, or existing config behavior changes. +2. **Bounded-authority registry and first field set.** Add the schema-owned + comparator registry and the first closed set of real fields using the + allow-set ceiling, deny-set floor, numeric-minimum, or numeric-maximum + classes. The PR must include at least one demonstrated deployment use; it is + not an empty comparator framework. +3. **Opt-in loading and activation.** Add one explicit ordered-source seam to the + existing config I/O path and publish one effective snapshot through the + existing activation boundary. With no source list, OpenClaw follows its + current single-config path and users cannot observe the feature. +4. **Writes and inspection.** Route writes to an explicitly writable source, + reject writes that violate earlier authority, and expose redacted provenance + and readiness findings. Reuse existing mutation and status surfaces rather + than creating a management subsystem. +5. **Lobster migration and deletion.** Prove effective-config equivalence for + the allowed-origins case, switch Lobster to the supported source seam, and + delete its baked overlay writer, stale-value cleanup, and exact-blob tests. + +The exact-authority core can be reviewed independently, but RFC completion +includes the bounded-authority PR. Comparator assignment remains closed, +schema-owned, and justified field by field. + +### Host migration and deletion gate + +The feature succeeds only when a host can remove private config machinery. For +each migration, conformance compares the old generated effective config with +the OpenClaw-composed effective snapshot over representative deployments, then +proves conflict, reload, and operator-write behavior. Once a minimum OpenClaw +release passes that proof, the host removes the corresponding fragment +generator, environment projection, stale-value cleanup, and exact config-blob +tests. Temporary dual generation is diagnostic only and must have an owner, +expiry release, and removal change. + +## Rationale + +### Why not generated overlays? + +Generated overlays express precedence, not authority. They silently overwrite +conflicts and require every host to reproduce OpenClaw normalization and +cross-field validation behavior. + +### Why not a new hosted config section? + +The controlled settings already have canonical homes. Copying them into a +hosting section creates two schemas and forces runtime modules to understand +hosting. Authority metadata composes existing fields without changing their +semantic owner. + +### Why reject instead of managed-wins? + +Silent precedence hides operator intent and configuration drift. Structured +admission makes the conflict actionable and preserves one explainable effective +state. + +### Why keep comparators closed? + +Arbitrary host comparators become a policy language and make conformance +impossible. OpenClaw can safely promise monotonic composition only where it owns +the field and ordering. + +### Why core rather than a plugin? + +Configuration authority must apply before optional plugins and runtime modules +activate. A plugin cannot safely be the enforcement dependency for its own +loading configuration or for Gateway startup. + +## Unresolved questions + +- Which existing OpenClaw config loading API and CLI commands should expose the + ordered layer list? +- Which fields, if any, should receive bounded comparators in the first release? +- How should schema evolution report a managed field that is unknown to an + older OpenClaw release? +- Which redacted provenance fields belong in `status` versus a dedicated config + inspection call? +- What storage and ownership guidance should hosts follow for durable operator + configuration across container replacement? +- Which existing runtime override paths are defaults, trusted activation + inputs, or potential authority-chain bypasses? From 19318de34a9ed244848e4e1a9745d154116416cf Mon Sep 17 00:00:00 2001 From: Gio Della-Libera Date: Sat, 11 Jul 2026 22:48:07 -0700 Subject: [PATCH 2/9] docs(rfc): align managed config write guarantees --- rfcs/0019-managed-configuration.md | 63 ++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/rfcs/0019-managed-configuration.md b/rfcs/0019-managed-configuration.md index ea2d9eb1..6bd5a7c0 100644 --- a/rfcs/0019-managed-configuration.md +++ b/rfcs/0019-managed-configuration.md @@ -3,10 +3,10 @@ title: Managed Configuration authors: - Gio Lodi created: 2026-07-10 -last_updated: 2026-07-10 +last_updated: 2026-07-11 status: draft issue: -rfc_pr: https://github.com/giodl73-repo/rfcs/pull/1 +rfc_pr: https://github.com/giodl73-repo/rfcs/pull/4 --- # Proposal: Managed Configuration @@ -169,11 +169,15 @@ loading mechanism. Each source reference identifies one document, its immutable source identity, and an optional expected digest. Order is declared once; it is not inferred from filenames, environment discovery, or numeric priority. -OpenClaw resolves includes, environment interpolation, and secret references -independently within each source before authority admission. These mechanisms -cannot read from, write to, or gain precedence over another layer. Every -resolved layer must be a cloneable plain-object document. Ordinary schema, -plugin-aware, and cross-field validation run against the composed candidate. +OpenClaw resolves JSON5 and includes within each source before authority +admission, using the ordinary include rules and the source's real location. +Includes cannot read from, write to, or gain precedence over another layer. +Environment substitution runs once after the ordered documents are composed, +so an earlier layer may intentionally declare environment values referenced by +a later layer without making substitution order-dependent. Every resolved +layer must be a cloneable plain-object document. Ordinary schema, plugin-aware, +secret-reference, and cross-field validation run against the composed +candidate. The runtime reports source identities rather than host paths where paths would leak deployment details. A host can prove which inputs produced an effective @@ -322,6 +326,35 @@ structured conflict before persistence. This extends the existing write path; it does not introduce dynamic ownership transfer or a general reconciliation protocol. +A descriptor with an expected content digest is immutable for that descriptor +generation and therefore cannot also be writable. A successful persistence +adapter returns the canonical committed bytes. Before publication, OpenClaw +resolves the complete source chain again, requires the refreshed writable +source to match those committed bytes, and recomposes from the refreshed +sources. A concurrent non-target change is therefore included in the published +candidate, while an intervening target write produces a structured conflict +instead of publishing bytes that no longer match storage. + +```mermaid +sequenceDiagram + participant C as Config client + participant O as OpenClaw + participant W as Writable source + participant R as Remaining sources + C->>O: write(target digest, chain identity, proposal) + O->>O: resolve + preflight complete chain + O->>W: CAS/atomic commit + W-->>O: canonical committed bytes + O->>R: resolve complete chain again + O->>W: resolve committed target again + alt target digest matches committed bytes + O->>O: recompose + validate refreshed chain + O-->>C: publish one candidate + else target changed after commit + O-->>C: structured conflict; publish nothing + end +``` + ### Example deployment profiles A local personal installation can use one writable layer. A simple hosted @@ -475,6 +508,10 @@ Release tests should cover: or comparator; - transactional reload and preservation of the previous effective generation; - stale operator-write rejection across managed-boundary rotation; +- rejection of a digest-pinned writable descriptor; +- complete-chain refresh when a non-target source changes during persistence; +- intervening target-write rejection before publication; +- canonical committed-byte reread before effective publication; - source digest mismatch and source-identity redaction; - semantic equivalence between direct effective config and composed managed plus operator inputs. @@ -503,7 +540,12 @@ implement the complete RFC: reject writes that violate earlier authority, and expose redacted provenance and readiness findings. Reuse existing mutation and status surfaces rather than creating a management subsystem. -5. **Lobster migration and deletion.** Prove effective-config equivalence for +5. **Concrete local-file integration.** Resolve ordered JSON5/include sources, + use the ordinary primary OpenClaw config as the one writable source, persist + through existing snapshot/CAS/atomic-write APIs, and recompose canonical + committed bytes before publication. Environment substitution runs once + after the complete ordered composition. +6. **Lobster migration and deletion.** Prove effective-config equivalence for the allowed-origins case, switch Lobster to the supported source seam, and delete its baked overlay writer, stale-value cleanup, and exact-blob tests. @@ -511,6 +553,11 @@ The exact-authority core can be reviewed independently, but RFC completion includes the bounded-authority PR. Comparator assignment remains closed, schema-owned, and justified field by field. +Steps 1 through 5 complete the generic OpenClaw feature contract. Step 6 is an +adoption and deletion gate for Lobster, intentionally dependent on an OpenClaw +release that contains the capability; it is not additional core composition +machinery. + ### Host migration and deletion gate The feature succeeds only when a host can remove private config machinery. For From 78f082eaf744498547e4f7d354b114c258fd5453 Mon Sep 17 00:00:00 2001 From: Gio Della-Libera Date: Mon, 13 Jul 2026 19:44:27 -0700 Subject: [PATCH 3/9] docs(rfc): align managed config with V1 --- rfcs/0019-managed-configuration.md | 880 ++++++++++++----------------- 1 file changed, 367 insertions(+), 513 deletions(-) diff --git a/rfcs/0019-managed-configuration.md b/rfcs/0019-managed-configuration.md index 6bd5a7c0..5c690514 100644 --- a/rfcs/0019-managed-configuration.md +++ b/rfcs/0019-managed-configuration.md @@ -3,615 +3,469 @@ title: Managed Configuration authors: - Gio Lodi created: 2026-07-10 -last_updated: 2026-07-11 +last_updated: 2026-07-13 status: draft issue: -rfc_pr: https://github.com/giodl73-repo/rfcs/pull/4 +rfc_pr: https://github.com/openclaw/rfcs/pull/34 --- # Proposal: Managed Configuration ## Summary -Add a core configuration-authority model that recursively composes an ordered -list of named configuration layers through the existing OpenClaw schema. -OpenClaw rejects conflicts or attempts to weaken authority established by an -earlier layer and produces one effective configuration with inspectable -provenance. A host-managed document plus an operator document is one deployment -profile of this generic model, not a pair of roles hard-coded into the engine. +Add an opt-in way to start OpenClaw from an ordered list of ordinary +configuration documents. -## Motivation +Each document is parsed and validated through OpenClaw's existing configuration +pipeline. OpenClaw then folds the documents in declared order. The first layer +to declare an exact path controls it; later layers may omit it or repeat the +same value, but may not replace it. A small closed set of fields can use +OpenClaw-owned monotonic rules instead. -OpenClaw currently has one ordinary configuration authority. A hosting platform -that must enforce deployment posture while preserving operator customization -therefore has to generate config fragments, project environment variables, -rewrite files in a particular order, remove stale generated values, and keep -custom logic synchronized with OpenClaw's schema. +The first version is deliberately startup-only and read-only. It does not add a +configuration control plane, layer roles, write routing, live reload, or a +provenance API. -The missing distinction is not a new `hosting` config section. It is authority -over fields in the existing configuration schema: +## Motivation -```text -OpenClaw defaults - + host-managed configuration - + operator configuration - -> admission and cross-field validation - -> effective configuration - -> runtime -``` +Lobster currently needs to combine three kinds of OpenClaw configuration: -Without an upstream contract, hosts silently overwrite operator values or carry -private strictness logic. Operators cannot reliably explain why a value is -effective, and support teams cannot reproduce the deployment from declared -inputs. Repeated host patches then track OpenClaw config internals instead of a -stable composition contract. +- Scout-wide security and Gateway defaults; +- tenant-specific URLs and private-network facts; +- operator-local customization. -Managed Configuration also gives OCC a clean future boundary: OCC can compile -admitted desired state into a managed document while OpenClaw remains the owner -of schema validation, field semantics, and effective runtime configuration. +Without an upstream composition seam, Lobster must bake those inputs into one +config file and maintain overlay application, stale-value cleanup, and +OpenClaw-specific validation behavior outside OpenClaw. OpenClaw sees only the +result and cannot reject a later overlay that weakens an earlier boundary. + +The problem is not unique to Lobster and does not require a new `hosting` +section. The values already belong in the ordinary OpenClaw schema. What is +missing is a generic way to compose multiple ordinary documents while +preserving declared order and rejecting conflicts. ## Goals -- Load an ordered list of named layers using the normal OpenClaw configuration - schema. -- Let presence in an earlier layer declare authority over that field for every - later layer. -- Reject conflicts with structured findings instead of silently overwriting. -- Support exact authority for every schema field. -- Support a small closed set of OpenClaw-defined bounded strictness rules. -- Run normal cross-field validation against the composed effective config. -- Expose redacted effective values, provenance, control mode, and stable config - identity for diagnostics and conformance. -- Associate mutability with a layer source rather than a built-in role, and - prevent writes from bypassing earlier authority. -- Make unsafe startup composition failures visible through status/readiness. -- Allow hosts to replace private config writers with a release-tested contract. - -## Non-Goals - -- A new top-level `hosting` section containing copies of existing settings. -- Numeric priorities, implicit source discovery, or dynamic layer reordering. -- A policy expression language or host-defined comparison functions. -- Silent managed-value precedence. -- Dynamic OCC reconciliation in the first version. -- Secret delivery, trusted identity, lifecycle, state synchronization, or - plugin installation. -- Depending on the optional Policy plugin for runtime correctness. -- Making every field support a monotonic "stricter" relationship. - -## Proposal - -### Layers and recursive composition - -The input is an ordered list of layer descriptors from strongest to weakest: +- Accept any positive number of explicitly ordered config documents. +- Keep layer names descriptive, with no built-in host, tenant, or operator roles. +- Apply the same operation recursively for every layer. +- Reuse JSON5, include, environment, schema, and plugin validation behavior. +- Reject exact conflicts instead of silently choosing a winner. +- Permit only proven tightening for the initial bounded tool-policy fields. +- Publish one effective startup snapshot to existing runtime consumers. +- Make the layered runtime immutable for its lifetime. +- Leave ordinary single-config startup unchanged and unaware of the feature. +- Give Lobster a supported seam that can replace baked overlay generation. + +## Non-goals + +- Implicit source discovery or numeric priorities. +- A fixed two-stage managed/operator model. +- Host-defined comparators or a policy expression language. +- Writable layers or routing config mutations back to source documents. +- Live layer reload, rollback generations, or transactional reconciliation. +- A per-path provenance or explanation API in V1. +- Secret delivery, identity, state synchronization, or plugin installation. +- Moving canonical settings into a parallel hosted-config schema. + +## Interface + +The Gateway CLI accepts a repeatable option: + +```bash +openclaw gateway run \ + --config-layer global=./global.json5 \ + --config-layer tenant=./tenant.json5 \ + --config-layer operator=./operator.json5 +``` + +The syntax is: ```text -layer[0] -> layer[1] -> ... -> layer[n] +--config-layer ``` -Each descriptor has a stable layer identity, an opaque source reference, and an -explicit `read-only` or `read-write` access capability. Resolution returns one -document using the existing OpenClaw schema plus a stable redacted source -identity. Names such as -`platform`, `tenant`, `team`, and `operator` are diagnostic labels supplied by a -deployment; they have no special semantics in core. +Order is the command-line declaration order. IDs must be non-empty and unique, +but otherwise have no semantics. The examples use deployment vocabulary only +to make the source of each document understandable. -Independent sources may resolve concurrently, but composition always retains -the descriptor array's declared order. Empty or duplicate layer identities fail -before source I/O. Failure to resolve any source rejects the complete candidate; -OpenClaw does not silently omit the failed layer. +When no `--config-layer` option is present, OpenClaw follows its existing +single-config path with no behavior change. -Composition applies one operation repeatedly: +Layered startup is incompatible with `--dev`, because dev startup creates and +mutates configuration. + +## Composition model + +Each source is an ordinary sparse OpenClaw config document. For each source, +OpenClaw: + +1. reads JSON5; +2. resolves includes using the normal include roots and the source file location; +3. resolves environment references; +4. requires a plain-object root; +5. rejects bootstrap-owned `meta` and `env` root keys. + +After every source resolves, OpenClaw folds them in declared order: ```text -state[0] = empty authority state +state[0] = empty state[i + 1] = compose(state[i], layer[i]) -effective = materializeWithDefaults(state[layerCount]) +effective = validate(state[layerCount]) ``` ```mermaid flowchart LR - A[Ordered source descriptors] --> B[Resolve every source] - B --> C[Admit cloneable sparse documents] - C --> D[Recursive exact-authority fold] - D --> E[Validate effective configuration] - E --> F[Normalize and materialize defaults] - F --> G[Publish one runtime snapshot] - B -. any failure .-> X[Reject complete candidate] - C -. any failure .-> X - D -. conflict .-> X - E -. schema or cross-field failure .-> X + A[Ordered id=path arguments] --> B[Resolve each ordinary config document] + B --> C[Recursive authority fold] + C --> D[Validate schema and plugins] + D --> E[Publish one startup snapshot] + B -. parse or include failure .-> X[Reject startup] + C -. authority conflict .-> X + D -. invalid config .-> X ``` ```mermaid flowchart TD - S0[Empty authority state] --> L0[Compose layer 0] + S0[Empty state] --> L0[Compose layer 0] L0 --> S1[Authority state 1] S1 --> L1[Compose layer 1] L1 --> S2[Authority state 2] S2 --> LN[Repeat for layer n] - LN --> SE[Composed sparse source] + LN --> E[Effective sparse source] ``` -For each leaf, the first declaring layer establishes its schema-defined control -rule. Every later layer may omit the leaf, repeat the same authored exact -value, or tighten a bounded value. It may not replace an exact value or weaken a -bounded value. A leaf omitted by every layer receives its ordinary OpenClaw -default after composition. - -Authority compares authored source values before schema normalization. Two -different accepted representations remain different exact claims even if the -ordinary schema would later normalize them to the same runtime value. This -keeps admission recursive and context-free: a layer cannot acquire a different -meaning merely because another layer supplies a cross-field dependency. - -Schema normalization, defaults, plugin-aware validation, and runtime-derived -values apply only after the complete fold. They therefore never create -authority claims. Cross-field rules see the composed effective candidate rather -than an incomplete sparse layer. - -Arrays are whole-field values unless a specific built-in bounded rule applies. -Objects are traversed according to the normal schema; declaring one child does -not implicitly claim unrelated siblings. - -Absence means that a layer makes no declaration for the path. Removing a path -from a writable layer relinquishes that layer's claim when the chain is -recomputed. `null` is a declared value only where the schema accepts it; it is -not a generic deletion marker. - -Empty objects declare no leaves and canonicalize away without individual -warnings. A layer with no declarations is valid, allowing staged or temporarily -empty sources, but inspection and status report one `NoDeclaredValues` -advisory for that layer. - -The ordered layer list is selected at process startup through one explicit core -loading mechanism. Each source reference identifies one document, its immutable -source identity, and an optional expected digest. Order is declared once; it is -not inferred from filenames, environment discovery, or numeric priority. - -OpenClaw resolves JSON5 and includes within each source before authority -admission, using the ordinary include rules and the source's real location. -Includes cannot read from, write to, or gain precedence over another layer. -Environment substitution runs once after the ordered documents are composed, -so an earlier layer may intentionally declare environment values referenced by -a later layer without making substitution order-dependent. Every resolved -layer must be a cloneable plain-object document. Ordinary schema, plugin-aware, -secret-reference, and cross-field validation run against the composed -candidate. - -The runtime reports source identities rather than host paths where paths would -leak deployment details. A host can prove which inputs produced an effective -snapshot without making its filesystem layout part of the contract. - -### Authority rules - -#### Exact authority - -A later layer must omit the field or provide the same authored value. A -different value is an admission error. +Objects compose recursively. Declaring one child does not claim unrelated +siblings. Arrays are whole-field values unless the field has a built-in bounded +rule. Empty objects preserve ownership of that object boundary. + +The composed source is validated once through the ordinary OpenClaw schema and +plugin-aware validator. Gateway, plugins, tools, and other consumers receive +the normal runtime config shape; they do not implement layer-specific logic. + +## Authority rules + +### Exact ownership + +Exact ownership is the default. + +The earliest layer declaring a path owns that path. A later layer may: + +- omit the path; +- repeat the same authored value. + +A later layer may not provide a different value. OpenClaw rejects the complete +candidate with a `ControlledByEarlierLayer` finding. ```json { - "path": "gateway.auth.mode", "reason": "ControlledByEarlierLayer", - "controllingLayer": "platform", - "controllingValue": "trusted-proxy", - "conflictingLayer": "operator", - "conflictingValue": "token", - "control": "exact" + "layer": "operator", + "path": "gateway.controlUi.allowedOrigins", + "controllingLayer": "tenant" } ``` -Exact authority is the default and works for every schema field without adding -field-specific policy logic. +There is no silent managed-wins or last-writer-wins behavior. -Equal exact declarations are accepted. Provenance records each declaring layer -while the earliest remaining declaration stays controlling. Removing or -reordering a layer recomputes the complete chain from source documents; derived -authority state is not persisted as another configuration source. +### Bounded tool policy -#### Bounded authority +V1 has two built-in bounded paths: -OpenClaw may mark a small closed set of fields with a monotonic comparator. A -later layer may preserve or tighten the inherited boundary but cannot weaken -it. Comparator composition must be associative so the same operation can be -applied recursively across any number of layers. +| Path | Rule | +| --- | --- | +| `tools.allow` | A later layer may only narrow the effective allow policy | +| `tools.deny` | A later layer may only broaden the effective deny policy | -Initial comparator classes are: +The comparison uses OpenClaw's runtime tool-policy semantics, including groups, +wildcards, and the meaning of an empty allow list. Ambiguous +expression-to-expression comparisons fail closed unless containment is proven. -| Comparator | Composition rule | -| --- | --- | -| Allow-set ceiling | Operator set must be a subset of the managed set | -| Deny-set floor | Operator set must be a superset of the managed set | -| Maximum limit | Operator value must be equal or lower | -| Minimum requirement | Operator value must be equal or stricter | -| Required protection | Operator cannot disable it | -| Disabled risky capability | Operator cannot re-enable it | +A weakening attempt produces `WouldWeakenEarlierLayer`. -Comparator assignment and ordering are owned by the OpenClaw schema. Layers -cannot attach a comparator to an arbitrary field or redefine "stricter." -Each comparator must be deterministic, idempotent, and associative over its -supported domain so recursive composition does not depend on fold grouping. +No other field receives a comparator in V1. Additional comparators require +field-specific semantics, tests, and demonstrated demand. -### Effective configuration +## Runtime lifecycle -Composition happens once before runtime consumers observe configuration: +Layered configuration is a startup input, not a second live config store. -```text -resolve each source independently - -> sparse-document admission - -> authority admission - -> effective composition - -> existing schema, plugin-aware, and cross-field validation - -> existing runtime materialization - -> immutable runtime snapshot -``` +When layered mode is active: -Gateway, plugins, tools, sessions, and state modules consume the same effective -snapshot. They do not independently merge managed and operator values. +- the composed snapshot is reused for the server lifetime; +- config hot reload is disabled; +- config-mutating RPCs are rejected; +- agent create, update, and delete are rejected before workspace side effects; +- process-wide config persistence paths reject writes; +- a source change takes effect only after Gateway restart. -The effective result reports two identities: an effective-content identity for -the normalized, redacted effective configuration, and an authority-chain -identity for the ordered authored claims and control metadata. Source delivery -identities are separate diagnostic metadata. Secret values are never included -in diagnostic output or hashes in a way that exposes plaintext. +Read surfaces use the composed snapshot where they would otherwise reread the +canonical config file. -### Findings and provenance +The write guard is owned by the Gateway server lifecycle and supports +overlapping owners in one process. Closing one layered server removes only its +own guard. -Validation returns structured findings suitable for CLI, status, doctor, admin -UI, and automation: +This read-only boundary avoids partial write semantics and keeps V1 small. A +future writable-layer design would need explicit source ownership, conflict +detection, atomic persistence, reload, and recovery guarantees and is not +implied by this RFC. -```json -{ - "valid": false, - "findings": [ - { - "path": "tools.exec.ask", - "reason": "WeakerThanManagedRequirement", - "managedValue": "always", - "operatorValue": "never", - "control": "minimum-requirement" - } - ] -} -``` +## Lobster example -Effective inspection reports provenance without exposing secrets: +The following is a realistic three-file Scout deployment. The names are not +special to OpenClaw. -```json +### 1. Scout global config + +```json5 +// scout-global.json5 { - "path": "tools.alsoAllow", - "value": ["read", "sessions_list"], - "authority": "operator", - "managedBoundary": ["read", "sessions_list", "exec"], - "control": "allow-set-ceiling" + gateway: { + mode: "local", + auth: { + mode: "trusted-proxy", + trustedProxy: { + userHeader: "x-scout-user", + requiredHeaders: ["x-scout-tenant"], + }, + }, + controlUi: { + dangerouslyAllowHostHeaderOriginFallback: false, + allowInsecureAuth: false, + dangerouslyDisableDeviceAuth: false, + }, + }, + tools: { + deny: ["exec"], + }, } ``` -At minimum, OpenClaw should support machine-readable operations equivalent to: - -- validate managed plus operator inputs; -- inspect the effective configuration; -- explain one path's value, authority, and comparator; -- report the effective configuration identity through status. - -Exact command names and loading flags should follow existing config CLI and -Gateway conventions during implementation. - -### Mutation behavior - -Mutation APIs target one explicitly writable layer, including a middle layer. A -write that would violate authority inherited from an earlier layer fails before -persistence; a valid declaration may establish authority over later layers. -Read-only layers are not writable through normal config APIs, regardless of -label. - -Config reload follows the existing hot-reload/restart classification after a -complete effective candidate passes admission and cross-field validation. -OpenClaw publishes one runtime snapshot; a failed reload leaves the previous -snapshot active and does not partially activate the candidate. At startup, -failure remains visible and prevents readiness when the runtime cannot safely -operate under the declared host boundary. - -Layer resolution and admission invoke snapshot publication exactly once with a -complete source/runtime candidate. Source resolution, sparse-document admission, -authority, or effective-validation failure does not invoke the publisher. -Atomic preflight and publication remain owned by the existing runtime snapshot -boundary; the layer engine does not own snapshot globals or reload actions. - -Layer writes use the existing config mutation preflight and conflict model. The -write must be validated against the target source and authority-chain -identity captured for that mutation. If either changed, the write fails with a -structured conflict before persistence. This extends the existing write path; -it does not introduce dynamic ownership transfer or a general reconciliation -protocol. - -A descriptor with an expected content digest is immutable for that descriptor -generation and therefore cannot also be writable. A successful persistence -adapter returns the canonical committed bytes. Before publication, OpenClaw -resolves the complete source chain again, requires the refreshed writable -source to match those committed bytes, and recomposes from the refreshed -sources. A concurrent non-target change is therefore included in the published -candidate, while an intervening target write produces a structured conflict -instead of publishing bytes that no longer match storage. +### 2. Tenant network config -```mermaid -sequenceDiagram - participant C as Config client - participant O as OpenClaw - participant W as Writable source - participant R as Remaining sources - C->>O: write(target digest, chain identity, proposal) - O->>O: resolve + preflight complete chain - O->>W: CAS/atomic commit - W-->>O: canonical committed bytes - O->>R: resolve complete chain again - O->>W: resolve committed target again - alt target digest matches committed bytes - O->>O: recompose + validate refreshed chain - O-->>C: publish one candidate - else target changed after commit - O-->>C: structured conflict; publish nothing - end +```json5 +// tenant-network.json5 +{ + gateway: { + bind: "tailnet", + trustedProxies: ["100.96.0.0/12"], + tailscale: { + mode: "serve", + serviceName: "svc:openclaw-acme", + }, + controlUi: { + allowedOrigins: ["https://openclaw.acme.internal"], + }, + }, + tools: { + deny: ["exec", "web"], + }, +} ``` -### Example deployment profiles - -A local personal installation can use one writable layer. A simple hosted -deployment can use two layers. A control plane can use more: - -```text -platform -> tenant -> agent/team -> operator -``` +### 3. Operator config -These are profiles of the same ordered recursive model. Core does not assign -special behavior to any name or require a fixed layer count. - -### Lobster usage case: Scout, tenant, and operator config - -A realistic Lobster deployment uses three layers. The names describe this -deployment; core still treats them as generic ordered sources. - -The Scout global layer establishes fleet-wide Gateway security posture: - -```yaml -id: scout-global -access: read-only -config: - gateway: - mode: local - auth: - mode: trusted-proxy - trustedProxy: - userHeader: x-scout-user - requiredHeaders: - - x-scout-tenant - controlUi: - dangerouslyAllowHostHeaderOriginFallback: false - allowInsecureAuth: false - dangerouslyDisableDeviceAuth: false +```json5 +// operator.json5 +{ + gateway: { + controlUi: { + enabled: true, + }, + }, + logging: { + level: "info", + }, +} ``` -The tenant layer supplies private-network facts that differ per deployment, -including its Tailnet exposure, trusted proxy range, and tenant URL: - -```yaml -id: tenant-network -access: read-only -config: - gateway: - bind: tailnet - trustedProxies: - - 100.96.0.0/12 - tailscale: - mode: serve - serviceName: svc:openclaw-acme - controlUi: - allowedOrigins: - - https://openclaw.acme.internal -``` +Lobster starts OpenClaw with the three explicit sources: -The operator layer remains writable for ordinary customization: - -```yaml -id: operator -access: read-write -config: - gateway: - controlUi: - enabled: true - logging: - level: info +```bash +openclaw gateway run \ + --config-layer scout=./scout-global.json5 \ + --config-layer tenant=./tenant-network.json5 \ + --config-layer operator=./operator.json5 ``` ```mermaid flowchart LR - S[Scout global
security baseline] --> F[Recursive fold] + S[Scout global
security baseline] --> F[Ordered recursive fold] T[Tenant network
URL, proxy, Tailnet] --> F - O[Operator
ordinary customization] --> F - F --> V[Effective validation] - V --> R[One ordinary OpenClaw runtime config] -``` - -OpenClaw publishes the same ordinary runtime shape that an unmanaged -installation would consume: - -```yaml -gateway: - mode: local - bind: tailnet - auth: - mode: trusted-proxy - trustedProxy: - userHeader: x-scout-user - requiredHeaders: - - x-scout-tenant - trustedProxies: - - 100.96.0.0/12 - tailscale: - mode: serve - serviceName: svc:openclaw-acme - controlUi: - enabled: true - allowedOrigins: - - https://openclaw.acme.internal - dangerouslyAllowHostHeaderOriginFallback: false - allowInsecureAuth: false - dangerouslyDisableDeviceAuth: false -logging: - level: info + O[Operator
local customization] --> F + F --> V[OpenClaw validation] + V --> R[One immutable runtime config] ``` -No Gateway, Control UI, logging, or plugin consumer needs to know that three -sources produced the document. Provenance retains the distinction for -inspection and write preflight. - -If the operator later attempts to replace the tenant URL, OpenClaw rejects the -write before persistence: +The effective runtime shape is the same ordinary config shape OpenClaw already +consumes: -```json +```json5 { - "path": "gateway.controlUi.allowedOrigins", - "reason": "ControlledByEarlierLayer", - "controllingLayer": "tenant-network", - "controllingValue": ["https://openclaw.acme.internal"], - "conflictingLayer": "operator", - "conflictingValue": ["https://example.invalid"] + gateway: { + mode: "local", + bind: "tailnet", + auth: { + mode: "trusted-proxy", + trustedProxy: { + userHeader: "x-scout-user", + requiredHeaders: ["x-scout-tenant"], + }, + }, + trustedProxies: ["100.96.0.0/12"], + tailscale: { + mode: "serve", + serviceName: "svc:openclaw-acme", + }, + controlUi: { + enabled: true, + allowedOrigins: ["https://openclaw.acme.internal"], + dangerouslyAllowHostHeaderOriginFallback: false, + allowInsecureAuth: false, + dangerouslyDisableDeviceAuth: false, + }, + }, + tools: { + deny: ["exec", "web"], + }, + logging: { + level: "info", + }, } ``` -The same rule protects Scout global security posture and tenant-private network -facts without hard-coding Scout, tenant, or operator as engine roles. This -replaces Lobster-specific fragment generation and stale-value cleanup while -preserving the existing runtime config API. - - -### Policy plugin relationship - -Core owns composition, authority metadata, comparators, findings, and startup -enforcement. The optional Policy plugin may reuse the comparator and finding -machinery for diagnostics, repair suggestions, or conformance checks, but core -must not depend on that plugin. - -### Conformance - -Release tests should cover: - -- exact conflicts across representative scalar, object, and array fields; -- every supported bounded comparator and authored-representation edge case; -- source immutability and operator write rejection; -- redaction and stable effective identity; -- cross-field validation after composition; -- reload/restart behavior; -- invalid-startup readiness/status behavior; -- compatibility when a newer managed document references an unsupported field - or comparator; -- transactional reload and preservation of the previous effective generation; -- stale operator-write rejection across managed-boundary rotation; -- rejection of a digest-pinned writable descriptor; -- complete-chain refresh when a non-target source changes during persistence; -- intervening target-write rejection before publication; -- canonical committed-byte reread before effective publication; -- source digest mismatch and source-identity redaction; -- semantic equivalence between direct effective config and composed managed - plus operator inputs. - -Hosting Profiles may declare Managed Configuration as an optional capability, -but profiles do not own its semantics. - -### Implementation sequence - -The RFC is delivered as small, independently reviewable PRs. No PR needs to -implement the complete RFC: - -1. **Exact-authority core.** Add the pure recursive fold, authored-value conflict - findings, provenance, effective validation, and focused tests. No startup, - persistence, or existing config behavior changes. -2. **Bounded-authority registry and first field set.** Add the schema-owned - comparator registry and the first closed set of real fields using the - allow-set ceiling, deny-set floor, numeric-minimum, or numeric-maximum - classes. The PR must include at least one demonstrated deployment use; it is - not an empty comparator framework. -3. **Opt-in loading and activation.** Add one explicit ordered-source seam to the - existing config I/O path and publish one effective snapshot through the - existing activation boundary. With no source list, OpenClaw follows its - current single-config path and users cannot observe the feature. -4. **Writes and inspection.** Route writes to an explicitly writable source, - reject writes that violate earlier authority, and expose redacted provenance - and readiness findings. Reuse existing mutation and status surfaces rather - than creating a management subsystem. -5. **Concrete local-file integration.** Resolve ordered JSON5/include sources, - use the ordinary primary OpenClaw config as the one writable source, persist - through existing snapshot/CAS/atomic-write APIs, and recompose canonical - committed bytes before publication. Environment substitution runs once - after the complete ordered composition. -6. **Lobster migration and deletion.** Prove effective-config equivalence for - the allowed-origins case, switch Lobster to the supported source seam, and - delete its baked overlay writer, stale-value cleanup, and exact-blob tests. - -The exact-authority core can be reviewed independently, but RFC completion -includes the bounded-authority PR. Comparator assignment remains closed, -schema-owned, and justified field by field. - -Steps 1 through 5 complete the generic OpenClaw feature contract. Step 6 is an -adoption and deletion gate for Lobster, intentionally dependent on an OpenClaw -release that contains the capability; it is not additional core composition -machinery. - -### Host migration and deletion gate - -The feature succeeds only when a host can remove private config machinery. For -each migration, conformance compares the old generated effective config with -the OpenClaw-composed effective snapshot over representative deployments, then -proves conflict, reload, and operator-write behavior. Once a minimum OpenClaw -release passes that proof, the host removes the corresponding fragment -generator, environment projection, stale-value cleanup, and exact config-blob -tests. Temporary dual generation is diagnostic only and must have an owner, -expiry release, and removal change. +If the operator document also declares a different +`gateway.controlUi.allowedOrigins`, startup is rejected because the tenant +layer declared that exact path first. If it removes `web` from +`tools.deny`, startup is rejected because that would weaken the inherited +deny floor. + +This lets Lobster materialize three source files without teaching OpenClaw +about Scout, tenants, or operators. After an OpenClaw release contains the +feature, Lobster can remove the baked effective-config overlay and its +stale-value cleanup. + +## Evidence + +The design was developed through the FACES loop: + +- frame the host problem and deletion target; +- audit the existing OpenClaw config and lifecycle boundaries; +- compare recursive and layered patterns inside OpenClaw; +- evaluate the contract with maintainer, security, host, operator, and testing + roles; +- build a broad fork prototype, then reduce it to the smallest supported slice. + +Evidence available during RFC review: + +- broad fork prototype: https://github.com/giodl73-repo/openclaw/pull/33 +- simplified upstream draft implementation: + https://github.com/openclaw/openclaw/pull/107026 +- a Lobster fork adapter materializes Scout, tenant, and operator documents and + passes them as repeated flags; +- 61 focused OpenClaw tests cover recursive composition, exact conflicts, + bounded tool policies, config loading, immutable write ownership, and early + agent-mutation rejection; +- final Codex review reported no actionable correctness regression. + +The broad prototype was useful evidence, not the proposed V1. It showed that +writable layers, provenance, reload, and rollback substantially expand the +contract. Those features were removed from the upstream implementation rather +than carried as speculative framework. + +## Delivery plan + +### PR 1: OpenClaw V1 + +The implementation draft at +https://github.com/openclaw/openclaw/pull/107026 delivers the complete V1 +contract in one reviewable change: + +- pure recursive composition; +- exact ownership and bounded tool-policy checks; +- repeatable Gateway CLI loading; +- ordinary config and plugin validation; +- immutable server lifecycle; +- focused tests and user documentation. + +Before moving from draft, it must have green upstream CI and a recorded +foreground Gateway proof showing successful three-layer startup, rejected +conflict startup, and rejected runtime mutation. + +### PR 2: Lobster adoption and deletion + +After an OpenClaw release contains PR 1, Lobster can: + +- materialize the three ordinary documents; +- launch OpenClaw with repeated `--config-layer` flags; +- compare the resulting effective config with representative existing + deployments; +- remove the baked overlay writer, stale-value cleanup, and exact generated-blob + tests. + +The Lobster PR should depend on the released OpenClaw version, not an +unpublished branch. No upstream Lobster PR is required before that release. + +## Conformance + +V1 acceptance requires: + +- no-flag startup remains behaviorally unchanged; +- one or more layers compose in declared order; +- duplicate or malformed descriptors fail; +- JSON5, includes, env references, schema, and plugin validation work; +- exact conflicts fail before readiness; +- tool-policy tightening succeeds and weakening fails closed; +- the Gateway uses the composed snapshot for reads; +- reload is disabled for layered mode; +- config and agent config mutations fail before persistence or workspace side + effects; +- closing layered runtimes releases only their own write guards; +- documentation shows the generic model and the Lobster three-file example. ## Rationale -### Why not generated overlays? +### Why an opt-in CLI seam? -Generated overlays express precedence, not authority. They silently overwrite -conflicts and require every host to reproduce OpenClaw normalization and -cross-field validation behavior. +It is explicit, easy for hosts to generate, invisible to ordinary users, and +does not add a second persistent config format. -### Why not a new hosted config section? +### Why reject conflicts? -The controlled settings already have canonical homes. Copying them into a -hosting section creates two schemas and forces runtime modules to understand -hosting. Authority metadata composes existing fields without changing their -semantic owner. +Silent precedence hides operator intent and can weaken deployment posture. +Rejecting the whole candidate makes the boundary visible and keeps the last +running configuration unchanged. -### Why reject instead of managed-wins? +### Why only two bounded fields? -Silent precedence hides operator intent and configuration drift. Structured -admission makes the conflict actionable and preserves one explainable effective -state. +Exact ownership is generic. Monotonic comparison is field-specific. The two +tool-policy fields already have runtime semantics OpenClaw can reuse and test. +Adding an empty comparator framework would increase surface area without +delivering behavior. -### Why keep comparators closed? +### Why startup-only and read-only? -Arbitrary host comparators become a policy language and make conformance -impossible. OpenClaw can safely promise monotonic composition only where it owns -the field and ordering. +Write-through and reload require source selection, concurrency, recovery, and +partial-failure semantics. Hosts can solve the immediate overlay problem by +regenerating source files and restarting. The smaller lifecycle is easier to +reason about and ship. ### Why core rather than a plugin? -Configuration authority must apply before optional plugins and runtime modules -activate. A plugin cannot safely be the enforcement dependency for its own -loading configuration or for Gateway startup. - -## Unresolved questions - -- Which existing OpenClaw config loading API and CLI commands should expose the - ordered layer list? -- Which fields, if any, should receive bounded comparators in the first release? -- How should schema evolution report a managed field that is unknown to an - older OpenClaw release? -- Which redacted provenance fields belong in `status` versus a dedicated config - inspection call? -- What storage and ownership guidance should hosts follow for durable operator - configuration across container replacement? -- Which existing runtime override paths are defaults, trusted activation - inputs, or potential authority-chain bypasses? +Composition and authority must run before plugin-aware validation and plugin +activation. A plugin cannot safely enforce the configuration that controls its +own loading. + +## Future work + +The following require separate evidence and design review: + +- redacted per-path provenance and explanation; +- writable source routing; +- live reload with atomic candidate publication; +- rollback generations; +- additional field-specific bounded comparators; +- non-file source adapters. + +None is required for Lobster to replace its baked startup overlays. From 123c303ab6c701e6286258e30a95fbd0777d29c3 Mon Sep 17 00:00:00 2001 From: Gio Della-Libera Date: Mon, 13 Jul 2026 20:05:40 -0700 Subject: [PATCH 4/9] docs(rfc): record managed config lifecycle proof --- rfcs/0019-managed-configuration.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/rfcs/0019-managed-configuration.md b/rfcs/0019-managed-configuration.md index 5c690514..0d7d8db8 100644 --- a/rfcs/0019-managed-configuration.md +++ b/rfcs/0019-managed-configuration.md @@ -367,6 +367,9 @@ Evidence available during RFC review: - 61 focused OpenClaw tests cover recursive composition, exact conflicts, bounded tool policies, config loading, immutable write ownership, and early agent-mutation rejection; +- a foreground lifecycle proof demonstrates successful three-layer startup, + composed config reads, rejected runtime mutation, rejected conflicting + startup, and no canonical config write; - final Codex review reported no actionable correctness regression. The broad prototype was useful evidence, not the proposed V1. It showed that @@ -389,9 +392,9 @@ contract in one reviewable change: - immutable server lifecycle; - focused tests and user documentation. -Before moving from draft, it must have green upstream CI and a recorded -foreground Gateway proof showing successful three-layer startup, rejected -conflict startup, and rejected runtime mutation. +The implementation PR records a foreground Gateway proof showing successful +three-layer startup, composed reads, rejected conflict startup, and rejected +runtime mutation. Before moving from draft, it must have green upstream CI. ### PR 2: Lobster adoption and deletion From e1baf373e1bfdd78fb30852fa9800af0f7a61c9c Mon Sep 17 00:00:00 2001 From: Gio Della-Libera Date: Tue, 14 Jul 2026 08:46:51 -0700 Subject: [PATCH 5/9] docs(rfc): assign composition semantics to core --- rfcs/0019-managed-configuration.md | 34 +++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/rfcs/0019-managed-configuration.md b/rfcs/0019-managed-configuration.md index 0d7d8db8..a72328a8 100644 --- a/rfcs/0019-managed-configuration.md +++ b/rfcs/0019-managed-configuration.md @@ -44,6 +44,24 @@ section. The values already belong in the ordinary OpenClaw schema. What is missing is a generic way to compose multiple ordinary documents while preserving declared order and rejecting conflicts. +### Why OpenClaw owns composition semantics + +OpenClaw must own the merge semantics because OpenClaw owns the configuration +schema, plugin-aware validation, tool-policy interpretation, runtime snapshot, +and mutation boundary. Authority and monotonic-tightening decisions depend on +those semantics; a launcher cannot reproduce them reliably without becoming a +second OpenClaw configuration implementation. + +Hosts remain responsible for materializing values and declaring source order. +OpenClaw is responsible for resolving, composing, validating, and enforcing +those sources. This boundary keeps deployment vocabulary outside core while +ensuring every host receives the same conflict and security behavior. + +`$include` remains appropriate for structuring one authored configuration +document. It does not preserve authority between independent sources, enforce +monotonic policy bounds, or make the resulting runtime immutable. External +flattening also erases source boundaries before OpenClaw can enforce them. + ## Goals - Accept any positive number of explicitly ordered config documents. @@ -196,15 +214,18 @@ When layered mode is active: - config hot reload is disabled; - config-mutating RPCs are rejected; - agent create, update, and delete are rejected before workspace side effects; -- process-wide config persistence paths reject writes; +- config persistence targeting the layered Gateway's canonical path rejects + writes; - a source change takes effect only after Gateway restart. Read surfaces use the composed snapshot where they would otherwise reread the canonical config file. -The write guard is owned by the Gateway server lifecycle and supports -overlapping owners in one process. Closing one layered server removes only its -own guard. +The write guard is owned by the Gateway server lifecycle and scoped to the +canonical config path. Overlapping owners for that path compose safely, while +an unrelated config path remains writable. Closing one layered server removes +only its own guard. Pathless mutation preflights resolve to the canonical path +so plugin or repair side effects cannot occur before persistence is rejected. This read-only boundary avoids partial write semantics and keeps V1 small. A future writable-layer design would need explicit source ownership, conflict @@ -364,12 +385,15 @@ Evidence available during RFC review: https://github.com/openclaw/openclaw/pull/107026 - a Lobster fork adapter materializes Scout, tenant, and operator documents and passes them as repeated flags; -- 61 focused OpenClaw tests cover recursive composition, exact conflicts, +- 63 focused OpenClaw tests cover recursive composition, exact conflicts, bounded tool policies, config loading, immutable write ownership, and early agent-mutation rejection; - a foreground lifecycle proof demonstrates successful three-layer startup, composed config reads, rejected runtime mutation, rejected conflicting startup, and no canonical config write; +- fresh-state and existing-state Gateway proofs demonstrate that layered startup + neither creates a missing canonical config nor changes an existing canonical + config or its legacy metadata; - final Codex review reported no actionable correctness regression. The broad prototype was useful evidence, not the proposed V1. It showed that From 67adf49bbe3228a9532140e4067cff83787b113a Mon Sep 17 00:00:00 2001 From: Gio Della-Libera Date: Tue, 14 Jul 2026 09:04:50 -0700 Subject: [PATCH 6/9] docs(rfc): add per-cell Fleet layering --- rfcs/0019-managed-configuration.md | 52 ++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/rfcs/0019-managed-configuration.md b/rfcs/0019-managed-configuration.md index a72328a8..9d1400c0 100644 --- a/rfcs/0019-managed-configuration.md +++ b/rfcs/0019-managed-configuration.md @@ -85,6 +85,8 @@ flattening also erases source boundaries before OpenClaw can enforce them. - A per-path provenance or explanation API in V1. - Secret delivery, identity, state synchronization, or plugin installation. - Moving canonical settings into a parallel hosted-config schema. +- Multi-tenant isolation inside one Gateway. Mutually untrusted tenants still + require separate Gateway processes and state boundaries. ## Interface @@ -367,6 +369,56 @@ about Scout, tenants, or operators. After an OpenClaw release contains the feature, Lobster can remove the baked effective-config overlay and its stale-value cleanup. +## Fleet and multi-tenant hosting + +Managed configuration complements OpenClaw Fleet's cell isolation model. Fleet +continues to run one complete Gateway cell per tenant trust boundary, with +separate state, credentials, workspace, network, and Gateway token. Layers do +not turn one shared Gateway into a tenant authorization boundary. + +For each cell, the host can reuse one shared baseline and append only the +documents applicable to that tenant and cell: + +```mermaid +flowchart LR + G[Shared global baseline] --> A[Compose Acme cell] + TA[Acme tenant config] --> A + OA[Acme operator config] --> A + A --> CA[Acme Gateway cell] + + G --> B[Compose Contoso cell] + TB[Contoso tenant config] --> B + OB[Contoso operator config] --> B + B --> CB[Contoso Gateway cell] +``` + +Conceptually, Fleet or another host supervisor starts each isolated cell with +its own ordered arguments: + +```text +Acme: global.json5, tenant-acme.json5, operator-acme.json5 +Contoso: global.json5, tenant-contoso.json5, operator-contoso.json5 +``` + +The global document can establish security and operational boundaries. The +tenant document can add that cell's URLs, private-network settings, channel or +provider configuration, and other tenant facts. An optional operator document +can add local choices or tighten bounded policy. OpenClaw applies the same +generic composition rules independently inside each cell. + +The V1 Gateway feature does not add or change Fleet commands. A later Fleet +integration only needs to mount the applicable documents into each cell, pass +the repeated `--config-layer` arguments, and replace or restart the cell when +those inputs change. Cells that rely on interactive in-cell configuration +should continue using ordinary mutable config instead of opting into layered +mode. + +This preserves the ownership boundary: Fleet owns cell lifecycle, isolation, +mounts, source materialization, and source order; the Gateway owns config +resolution, composition, validation, authority, and runtime enforcement. A +host must not place multiple mutually untrusted tenant documents into one +Gateway stack. + ## Evidence The design was developed through the FACES loop: From 481116b135790390da82a519e03f84ad7ad770b8 Mon Sep 17 00:00:00 2001 From: Gio Della-Libera Date: Wed, 15 Jul 2026 18:00:01 -0700 Subject: [PATCH 7/9] docs(rfc): add managed configuration v1 spec --- rfcs/0019-managed-configuration.md | 3 + rfcs/0019/managed-configuration-v1-spec.md | 409 +++++++++++++++++++++ 2 files changed, 412 insertions(+) create mode 100644 rfcs/0019/managed-configuration-v1-spec.md diff --git a/rfcs/0019-managed-configuration.md b/rfcs/0019-managed-configuration.md index 9d1400c0..3604fec5 100644 --- a/rfcs/0019-managed-configuration.md +++ b/rfcs/0019-managed-configuration.md @@ -26,6 +26,9 @@ The first version is deliberately startup-only and read-only. It does not add a configuration control plane, layer roles, write routing, live reload, or a provenance API. +The implementer-facing v1 contract is defined in +[Managed Configuration v1 Core Specification](0019/managed-configuration-v1-spec.md). + ## Motivation Lobster currently needs to combine three kinds of OpenClaw configuration: diff --git a/rfcs/0019/managed-configuration-v1-spec.md b/rfcs/0019/managed-configuration-v1-spec.md new file mode 100644 index 00000000..d9f05e88 --- /dev/null +++ b/rfcs/0019/managed-configuration-v1-spec.md @@ -0,0 +1,409 @@ +# Managed Configuration v1 Core Specification + +This document is the implementer-facing core specification for RFC 0019, +Managed Configuration. The RFC explains the motivation, ownership boundary, +and rollout plan. This file defines the v1 invocation, source preparation, +composition, validation, findings, runtime lifecycle, hosting, and conformance +contract that OpenClaw and host supervisors can build against. + +Status: draft, tied to RFC 0019. + +## Scope + +This specification defines: + +- repeated ordered local-file inputs at Gateway startup; +- generic layer identifiers with no built-in deployment roles; +- JSON5, include, and environment preparation for each source; +- recursive first-declaration authority; +- bounded tightening for `tools.allow` and `tools.deny`; +- final schema and plugin-aware validation; +- one immutable startup snapshot; +- path-scoped mutation rejection; +- ordinary single-config compatibility; +- per-cell use by Fleet and other host supervisors; +- a minimum conformance suite. + +This specification does not define: + +- live layer reload or reconciliation; +- writable layers or mutation routing; +- provenance or status APIs; +- generation, rollback, or transaction controllers; +- implicit source discovery, priorities, or inheritance; +- special host, tenant, operator, or Fleet roles; +- secret delivery or credential storage; +- tenant isolation inside one Gateway; +- remote layer URLs or signed layer envelopes. + +## Normative Language + +The terms **must**, **must not**, **should**, and **may** describe requirements +for a conforming v1 implementation. Examples use deployment-oriented labels +only for clarity; those labels have no core semantics. + +## Terminology + +- **Layer**: one explicitly ordered ordinary OpenClaw config document. +- **Layer id**: a unique descriptive label supplied with a layer path. +- **Authored value**: a value present after JSON5 parsing, include resolution, + and environment substitution, before schema defaults are applied. +- **Exact path**: a recursively addressed config path such as + `gateway.controlUi.allowedOrigins`. +- **Controlling layer**: the earliest layer that declares an exact path. +- **Bounded path**: a path with a field-specific rule that may accept a later + value only when it provably tightens the effective value. +- **Composed source**: the sparse authored document produced by the ordered + fold. +- **Runtime snapshot**: the validated config published to ordinary Gateway + consumers for the process lifetime. +- **Canonical config path**: the `openclaw.json` path that ordinary config + persistence would target for the process. + +## Invocation Contract + +The Gateway CLI accepts a repeatable option: + +```text +--config-layer +``` + +Example: + +```bash +openclaw gateway run \ + --config-layer global=./global.json5 \ + --config-layer tenant=./tenant.json5 \ + --config-layer operator=./operator.json5 +``` + +Requirements: + +- option occurrence order is layer order; +- at least one occurrence enables layered mode; +- the first `=` separates id from path; +- ids and paths must be non-empty after trimming; +- ids must be unique within one invocation; +- paths resolve through normal user-path expansion to absolute local paths; +- a missing, unreadable, or invalid file rejects startup; +- `--dev` and layered mode must not be combined; +- no flag means the ordinary single-config path with no layered behavior. + +The option may be registered on `gateway` and remain available to its `run` +subcommand. The contract is the repeated ordered sequence, not a particular +argument-parser implementation. + +## Source Preparation + +Each layer is prepared independently in declared order. + +For each layer, OpenClaw must: + +1. read the selected local file as UTF-8; +2. parse it as JSON5; +3. resolve `$include` using the layer file as the relative source location and + the normal configured include roots; +4. resolve `${ENV_NAME}` references using the startup environment and preserve + normal missing-variable warnings; +5. require a plain-object root; +6. reject authored root keys `meta` and `env`. + +`meta` and `env` are rejected because they participate in process bootstrap +semantics that precede layered composition. Launchers must supply process +environment through the process boundary instead. + +Preparation does not apply schema defaults to individual layers. Defaults and +plugin-aware validation apply once to the complete composed source so defaults +cannot accidentally claim authority on behalf of an earlier sparse layer. + +## Ordered Composition + +Composition starts from an empty object and folds prepared layers in declared +order: + +```text +state[0] = {} +state[i + 1] = compose(state[i], layer[i]) +composedSource = state[layerCount] +``` + +The operation must be deterministic for the same ordered authored inputs. + +### Recursive Object Semantics + +Plain objects compose recursively. Declaring one child does not claim sibling +paths. For example, an earlier declaration of `gateway.mode` does not control +`gateway.controlUi.allowedOrigins`. + +An empty object is an authored value and controls that object path. It must not +disappear during composition. + +Arrays, scalars, and non-plain-object values are whole-path values unless the +path has a bounded rule. + +### Exact Authority + +Exact authority is the default for every path. + +The earliest layer declaring a path becomes its controlling layer. A later +layer may: + +- omit the path; +- repeat a deeply equal authored value. + +A later layer must not replace the path with a different value. One conflict +invalidates the complete candidate; OpenClaw must not publish a partial +composition. + +### Bounded Tool Policy + +V1 defines bounded rules only for these exact paths: + +| Path | Accepted later value | +| --- | --- | +| `tools.allow` | Proven to narrow the effective allow policy | +| `tools.deny` | Proven to broaden the effective deny policy | + +Comparisons must use OpenClaw's runtime tool-policy meaning, including exact +tool names, groups, wildcard patterns, and the meaning of an empty allow list. + +A comparator must fail closed. If containment between expressions cannot be +proven, the later declaration is rejected. Syntactic difference alone is not +proof of either tightening or weakening. + +No other path receives a comparator in v1. A future bounded path requires +field-specific semantics, positive and negative vectors, and an RFC/spec +update. Hosts must not inject custom comparators into core composition. + +## Findings And Startup Rejection + +Composition findings are structured and identify enough context to repair the +source without exposing unrelated config values. + +An exact conflict has this minimum shape: + +```json +{ + "reason": "ControlledByEarlierLayer", + "layer": "operator", + "path": "gateway.controlUi.allowedOrigins", + "controllingLayer": "tenant" +} +``` + +A bounded weakening has this minimum shape: + +```json +{ + "reason": "WouldWeakenEarlierLayer", + "layer": "operator", + "path": "tools.deny", + "controllingLayer": "global" +} +``` + +Implementations may attach bounded diagnostic metadata, but must not include +credentials, raw secret values, or complete unredacted documents in findings. + +Any parse, include, root-shape, bootstrap-key, composition, schema, or plugin +validation error rejects Gateway startup before a runtime snapshot is +published. + +## Final Validation And Publication + +After successful composition, OpenClaw must: + +1. resolve plugin metadata for the composed source and effective workspace; +2. validate the composed source through the ordinary OpenClaw schema and + plugin-aware validator; +3. retain ordinary validation warnings; +4. publish the validated config through the existing Gateway startup-snapshot + path. + +Gateway, plugin, channel, tool, and agent consumers receive the ordinary +runtime config shape. They must not implement layer-name or layer-role logic. + +The canonical `openclaw.json` is not an implicit final layer and must not be +created, repaired, migrated, or merged into the layered candidate. + +## Runtime Lifecycle + +Layered v1 is startup-only and read-only. + +While layered mode is active: + +- `config.get` reports the composed source and effective runtime config; +- config mutation RPCs reject before persistence; +- agent create, update, and delete reject before workspace side effects; +- plugin/runtime mutation preflights reject before installation or other + persistent side effects; +- config persistence targeting the canonical config path is blocked; +- an unrelated config path in the same process remains writable; +- canonical file watching and last-known-good promotion are disabled; +- source file changes do not alter the active snapshot; +- an in-process Gateway restart reuses the validated startup snapshot; +- a full process restart rereads the declared layer files. + +Pathless mutation preflights are interpreted as targeting the process's +canonical config path. Runtime write blocks are path-scoped, support +overlapping owners, and release independently when their owning server closes +or startup fails. + +A separate process may edit canonical config while a layered Gateway runs. The +layered Gateway does not consume that file, and such edits do not change its +active snapshot. + +## Ordinary Config Compatibility + +When no `--config-layer` option is supplied: + +- startup reads ordinary canonical config exactly as before; +- normal config watching, migration, mutation, backup, and reload behavior is + unchanged; +- no layered write block is registered; +- users do not need to know the feature exists. + +Layered files use the existing OpenClaw schema. This specification does not +create a parallel hosted or managed config schema. + +## Fleet And Host Supervisor Contract + +One ordered stack configures one Gateway trust domain. Managed configuration +does not permit mutually untrusted tenants to share a Gateway. + +Fleet or another host supervisor may reuse a global document while selecting +cell-specific later documents: + +```text +global + tenant-acme + operator-acme -> Acme Gateway cell +global + tenant-contoso + operator-contoso -> Contoso Gateway cell +``` + +The supervisor owns: + +- cell/process isolation; +- state, credential, workspace, and network boundaries; +- source generation, permissions, mounts, and order; +- rollout and full-process replacement or restart. + +OpenClaw owns: + +- source resolution; +- recursive composition; +- exact and bounded authority; +- schema and plugin validation; +- runtime snapshot publication; +- mutation enforcement. + +V1 does not require a Fleet command change. A Fleet integration may mount the +applicable documents read-only and pass repeated Gateway flags. Cells that +depend on interactive in-cell configuration should use ordinary mutable config +instead of layered mode. + +## Security Requirements + +- Layer files are trusted startup inputs from the launcher or operator. +- File permissions and secure materialization are host responsibilities. +- Layer ids and diagnostics are not authorization boundaries. +- Layering does not isolate sessions, users, tenants, credentials, channels, + workspaces, tools, or state inside one Gateway. +- Mutually untrusted tenants require separate Gateway cells or stronger host + isolation. +- Secrets remain subject to ordinary OpenClaw secret handling and must not be + exposed in findings or logs. +- A later layer must never weaken an earlier bounded policy when containment is + uncertain. + +## Compatibility And Evolution + +The v1 compatibility surface is the CLI spelling, declared order, source +preparation, composition rules, finding reasons, and immutable lifecycle. + +Compatible changes may add clearer diagnostics or optimize implementation +without changing accepted/rejected inputs or runtime behavior. + +The following require an explicit contract revision: + +- accepting remote or implicit sources; +- changing first-declaration authority; +- adding a bounded comparator; +- making layers writable or reloadable; +- adding semantic layer roles; +- changing full-process versus in-process restart behavior; +- treating canonical config as part of the layered stack. + +## Minimum Conformance Suite + +A conforming implementation must cover these cases. + +### Invocation And Preparation + +- no flag follows ordinary startup; +- one valid layer starts successfully; +- repeated flags preserve declaration order; +- duplicate or empty ids reject; +- missing files and invalid JSON5 reject; +- includes resolve relative to each source; +- missing environment variables preserve normal warnings; +- non-object roots and authored `meta` or `env` reject; +- `--dev` plus a layer rejects. + +### Composition + +- unrelated object siblings compose; +- the same value may be repeated; +- a different controlled value rejects with `ControlledByEarlierLayer`; +- arrays behave as whole-path values; +- empty objects preserve authority; +- later `tools.allow` narrowing succeeds; +- later `tools.deny` broadening succeeds; +- allow or deny weakening rejects with `WouldWeakenEarlierLayer`; +- ambiguous wildcard containment fails closed. + +### Validation And Lifecycle + +- final schema and plugin validation use the composed source; +- `config.get` returns the composed snapshot; +- config mutations reject with no canonical write; +- agent mutations reject before workspace side effects; +- pathless plugin mutation preflights reject before installation side effects; +- an unrelated config path remains writable; +- overlapping runtime owners release independently; +- startup failure releases its write owner; +- a fresh state directory does not gain canonical config; +- an existing canonical config remains byte-for-byte unchanged; +- a full restart rereads sources while an in-process restart reuses the + validated snapshot. + +### Hosted Cell + +- two cells may reuse one global source with different tenant sources; +- each cell receives only its own effective config and state; +- the global bounded policy cannot be weakened by either tenant source; +- no test treats layering as cross-tenant process isolation. + +## Implementer Checklist + +An OpenClaw implementation is v1 conformant when it: + +- exposes the repeated Gateway flag without changing no-flag behavior; +- prepares sources through native config primitives; +- composes sparse authored values before applying defaults; +- implements generic recursive exact authority; +- implements only the two specified bounded tool-policy paths; +- rejects the complete startup candidate on any finding or validation error; +- publishes one ordinary runtime snapshot; +- scopes immutability to the canonical config path and server lifetime; +- prevents persistent side effects before mutation rejection; +- documents restart-to-apply behavior and the lack of tenant isolation; +- passes the minimum conformance suite. + +A host supervisor is v1 compatible when it: + +- materializes complete local source files before process start; +- passes a deterministic explicit order; +- protects file paths and permissions; +- uses one stack per Gateway trust domain; +- replaces or fully restarts the process to activate changes; +- does not depend on write-through, reload, provenance, or special role + semantics. From 8046ab416426e9ab5e4a6140a12fe9ede60c75bf Mon Sep 17 00:00:00 2001 From: Gio Della-Libera Date: Thu, 23 Jul 2026 07:47:44 -0700 Subject: [PATCH 8/9] docs(rfc-0019): clarify conformance and lifecycle --- rfcs/0019-managed-configuration.md | 35 ++++++++++++-------- rfcs/0019/managed-configuration-v1-spec.md | 38 +++++++++++++++++++--- 2 files changed, 56 insertions(+), 17 deletions(-) diff --git a/rfcs/0019-managed-configuration.md b/rfcs/0019-managed-configuration.md index 3604fec5..82f73814 100644 --- a/rfcs/0019-managed-configuration.md +++ b/rfcs/0019-managed-configuration.md @@ -3,7 +3,7 @@ title: Managed Configuration authors: - Gio Lodi created: 2026-07-10 -last_updated: 2026-07-13 +last_updated: 2026-07-23 status: draft issue: rfc_pr: https://github.com/openclaw/rfcs/pull/34 @@ -16,8 +16,10 @@ rfc_pr: https://github.com/openclaw/rfcs/pull/34 Add an opt-in way to start OpenClaw from an ordered list of ordinary configuration documents. -Each document is parsed and validated through OpenClaw's existing configuration -pipeline. OpenClaw then folds the documents in declared order. The first layer +Each document is independently parsed, include-resolved, environment-resolved, +and checked for a valid object root. OpenClaw then folds the documents in +declared order and applies schema defaults and plugin-aware validation once to +the composed source. The first layer to declare an exact path controls it; later layers may omit it or repeat the same value, but may not replace it. A small closed set of fields can use OpenClaw-owned monotonic rules instead. @@ -28,6 +30,8 @@ provenance API. The implementer-facing v1 contract is defined in [Managed Configuration v1 Core Specification](0019/managed-configuration-v1-spec.md). +That sidecar is normative for V1; if this explanatory RFC and the sidecar +conflict, the sidecar controls. ## Motivation @@ -221,7 +225,8 @@ When layered mode is active: - agent create, update, and delete are rejected before workspace side effects; - config persistence targeting the layered Gateway's canonical path rejects writes; -- a source change takes effect only after Gateway restart. +- a source change takes effect only after a full Gateway process restart or + replacement. Read surfaces use the composed snapshot where they would otherwise reread the canonical config file. @@ -411,8 +416,8 @@ generic composition rules independently inside each cell. The V1 Gateway feature does not add or change Fleet commands. A later Fleet integration only needs to mount the applicable documents into each cell, pass -the repeated `--config-layer` arguments, and replace or restart the cell when -those inputs change. Cells that rely on interactive in-cell configuration +the repeated `--config-layer` arguments, and replace or fully restart the +Gateway process when those inputs change. Cells that rely on interactive in-cell configuration should continue using ordinary mutable config instead of opting into layered mode. @@ -438,9 +443,10 @@ Evidence available during RFC review: - broad fork prototype: https://github.com/giodl73-repo/openclaw/pull/33 - simplified upstream draft implementation: https://github.com/openclaw/openclaw/pull/107026 -- a Lobster fork adapter materializes Scout, tenant, and operator documents and - passes them as repeated flags; -- 63 focused OpenClaw tests cover recursive composition, exact conflicts, +- a Lobster fork adapter demonstrates materializing Scout, tenant, and operator + documents and passing them as repeated flags; its production lifecycle and + rollout proof remain adoption work; +- 63 focused tests on the recorded implementation heads cover recursive composition, exact conflicts, bounded tool policies, config loading, immutable write ownership, and early agent-mutation rejection; - a foreground lifecycle proof demonstrates successful three-layer startup, @@ -449,7 +455,8 @@ Evidence available during RFC review: - fresh-state and existing-state Gateway proofs demonstrate that layered startup neither creates a missing canonical config nor changes an existing canonical config or its legacy metadata; -- final Codex review reported no actionable correctness regression. +- exact-head core conformance, upstream CI, and host-supervisor integration + evidence remain required before either implementation is called complete. The broad prototype was useful evidence, not the proposed V1. It showed that writable layers, provenance, reload, and rollback substantially expand the @@ -461,8 +468,8 @@ than carried as speculative framework. ### PR 1: OpenClaw V1 The implementation draft at -https://github.com/openclaw/openclaw/pull/107026 delivers the complete V1 -contract in one reviewable change: +https://github.com/openclaw/openclaw/pull/107026 implements the proposed V1 core +slice in one reviewable change: - pure recursive composition; - exact ownership and bounded tool-policy checks; @@ -473,7 +480,9 @@ contract in one reviewable change: The implementation PR records a foreground Gateway proof showing successful three-layer startup, composed reads, rejected conflict startup, and rejected -runtime mutation. Before moving from draft, it must have green upstream CI. +runtime mutation. Before it can claim V1 core conformance, it must be rebased to +current main, have green exact-head upstream CI, and map every core conformance +case below to an automated test or named proof. ### PR 2: Lobster adoption and deletion diff --git a/rfcs/0019/managed-configuration-v1-spec.md b/rfcs/0019/managed-configuration-v1-spec.md index d9f05e88..406324a3 100644 --- a/rfcs/0019/managed-configuration-v1-spec.md +++ b/rfcs/0019/managed-configuration-v1-spec.md @@ -167,6 +167,29 @@ V1 defines bounded rules only for these exact paths: Comparisons must use OpenClaw's runtime tool-policy meaning, including exact tool names, groups, wildcard patterns, and the meaning of an empty allow list. +The following vectors are normative for V1. `accept` means the later value is +proven monotonic; `reject` includes indeterminate containment. + +| Path | Earlier value | Later value | Result | Reason | +| --- | --- | --- | --- | --- | +| `tools.allow` | `[]` | `["read"]` | accept | Empty allow is unrestricted; a non-empty allow narrows it | +| `tools.allow` | `["read", "write"]` | `["read"]` | accept | Exact subset | +| `tools.allow` | `["write"]` | `["apply_patch"]` | accept | The runtime `write` alias includes `apply_patch` | +| `tools.allow` | `["read"]` | `["write"]` | reject | Adds authority | +| `tools.allow` | `["read*"]` | `["read_file"]` | accept | The earlier wildcard matches the later exact name | +| `tools.allow` | `["read*"]` | `["read?"]` | reject | Expression-to-expression containment is not proven | +| `tools.allow` | `["group:fs"]` | `["group:fs"]` | accept | Identical group expression | +| `tools.allow` | `["group:fs"]` | `["group:web"]` | reject | Different group containment is not proven | +| `tools.deny` | `[]` | `["exec"]` | accept | Adds a denial | +| `tools.deny` | `["exec"]` | `["exec", "browser"]` | accept | Exact superset | +| `tools.deny` | `["apply_patch"]` | `["write"]` | accept | The runtime `write` alias continues denying `apply_patch` | +| `tools.deny` | `["write"]` | `["apply_patch"]` | reject | Would stop denying the distinct `write` name | + +Runtime matcher evolution must preserve these results. A matcher change that +changes whether an existing layered input is accepted requires a contract +revision, even when the matcher change is otherwise compatible for ordinary +single-config use. + A comparator must fail closed. If containment between expressions cannot be proven, the later declaration is rejected. Syntactic difference alone is not proof of either tightening or weakening. @@ -332,9 +355,12 @@ The following require an explicit contract revision: - changing full-process versus in-process restart behavior; - treating canonical config as part of the layered stack. -## Minimum Conformance Suite +## Minimum Conformance Suites -A conforming implementation must cover these cases. +A conforming OpenClaw core implementation must cover the invocation, +composition, validation, and lifecycle cases below. Host-supervisor +conformance is separate because process, state, credential, mount, and network +isolation are host responsibilities rather than OpenClaw core behavior. ### Invocation And Preparation @@ -377,6 +403,9 @@ A conforming implementation must cover these cases. ### Hosted Cell +A conforming host integration must cover these cases. They are not prerequisites +for OpenClaw core conformance. + - two cells may reuse one global source with different tenant sources; - each cell receives only its own effective config and state; - the global bounded policy cannot be weakened by either tenant source; @@ -396,7 +425,7 @@ An OpenClaw implementation is v1 conformant when it: - scopes immutability to the canonical config path and server lifetime; - prevents persistent side effects before mutation rejection; - documents restart-to-apply behavior and the lack of tenant isolation; -- passes the minimum conformance suite. +- passes the OpenClaw core portions of the minimum conformance suites. A host supervisor is v1 compatible when it: @@ -406,4 +435,5 @@ A host supervisor is v1 compatible when it: - uses one stack per Gateway trust domain; - replaces or fully restarts the process to activate changes; - does not depend on write-through, reload, provenance, or special role - semantics. + semantics; +- passes the hosted-cell conformance suite. From 5b33475052c687faad3219ad0a760cf089369888 Mon Sep 17 00:00:00 2001 From: Gio Della-Libera Date: Tue, 28 Jul 2026 07:21:02 -0700 Subject: [PATCH 9/9] docs(rfc-0019): define ownership lifetimes --- rfcs/0019-managed-configuration.md | 46 +++++++++++++++++++--- rfcs/0019/managed-configuration-v1-spec.md | 45 +++++++++++++++++++++ 2 files changed, 85 insertions(+), 6 deletions(-) diff --git a/rfcs/0019-managed-configuration.md b/rfcs/0019-managed-configuration.md index 82f73814..52cef22e 100644 --- a/rfcs/0019-managed-configuration.md +++ b/rfcs/0019-managed-configuration.md @@ -3,7 +3,7 @@ title: Managed Configuration authors: - Gio Lodi created: 2026-07-10 -last_updated: 2026-07-23 +last_updated: 2026-07-28 status: draft issue: rfc_pr: https://github.com/openclaw/rfcs/pull/34 @@ -64,6 +64,39 @@ OpenClaw is responsible for resolving, composing, validating, and enforcing those sources. This boundary keeps deployment vocabulary outside core while ensuring every host receives the same conflict and security behavior. +### Ownership and authority + +This RFC uses **ownership** for responsibility over semantics and lifecycle, +and **authority** for the value-control relationship derived from ordered +layers. A layer id is a descriptive label, not an authenticated principal or a +durable owner identity. + +| Concern | Responsible owner | Lifetime | +| --- | --- | --- | +| Field meaning, defaults, validation, and bounded comparison | The OpenClaw subsystem that owns the existing config field | The OpenClaw contract version | +| Source contents, materialization, permissions, and declared order | The invoking operator, host, Fleet, or future control plane | One process invocation | +| Authority over an authored path | The earliest declaring layer, as derived and enforced by OpenClaw | One candidate evaluation and, after acceptance, its layered activation | +| Candidate composition and admission | OpenClaw config bootstrap | One startup evaluation | +| Effective runtime snapshot | The existing Gateway config lifecycle | One full process activation, including in-process Gateway restarts | +| Canonical-path mutation exclusion | Each layered Gateway server lifecycle | Registration through server close or failed startup cleanup | +| Runtime behavior | The existing Gateway, plugin, channel, tool, and agent owners | The accepted snapshot they consume | + +The source producer chooses desired values and order but does not define what a +field means or whether one value tightens another. OpenClaw derives authority, +rejects invalid candidates, and publishes the only effective snapshot. Runtime +consumers use that snapshot; they do not recompute layering or infer roles from +layer ids. + +A full process restart creates a new layered activation and recomputes +authority from the then-current ordered sources. An in-process Gateway restart +does not create a new activation and must reuse the accepted snapshot. V1 does +not persist authority as a separate lease, generation, or configuration source. + +This also defines the future OCC boundary. OCC may own desired state, +admission, rollout, and source materialization, but it remains a caller of this +contract. It does not replace OpenClaw's ownership of field semantics, +composition, validation, or the active runtime snapshot. + `$include` remains appropriate for structuring one authored configuration document. It does not preserve authority between independent sources, enforce monotonic policy bounds, or make the resulting runtime immutable. External @@ -172,11 +205,12 @@ the normal runtime config shape; they do not implement layer-specific logic. ## Authority rules -### Exact ownership +### Exact authority -Exact ownership is the default. +Exact authority is the default. -The earliest layer declaring a path owns that path. A later layer may: +The earliest layer declaring a path controls that path for the current +candidate and accepted activation. A later layer may: - omit the path; - repeat the same authored value. @@ -472,7 +506,7 @@ https://github.com/openclaw/openclaw/pull/107026 implements the proposed V1 core slice in one reviewable change: - pure recursive composition; -- exact ownership and bounded tool-policy checks; +- exact authority and bounded tool-policy checks; - repeatable Gateway CLI loading; - ordinary config and plugin validation; - immutable server lifecycle; @@ -530,7 +564,7 @@ running configuration unchanged. ### Why only two bounded fields? -Exact ownership is generic. Monotonic comparison is field-specific. The two +Exact authority is generic. Monotonic comparison is field-specific. The two tool-policy fields already have runtime semantics OpenClaw can reuse and test. Adding an empty comparator framework would increase surface area without delivering behavior. diff --git a/rfcs/0019/managed-configuration-v1-spec.md b/rfcs/0019/managed-configuration-v1-spec.md index 406324a3..818bcb3e 100644 --- a/rfcs/0019/managed-configuration-v1-spec.md +++ b/rfcs/0019/managed-configuration-v1-spec.md @@ -57,9 +57,47 @@ only for clarity; those labels have no core semantics. fold. - **Runtime snapshot**: the validated config published to ordinary Gateway consumers for the process lifetime. +- **Candidate evaluation**: one attempt to resolve, compose, and validate the + complete ordered source list before publication. +- **Layered activation**: the lifetime beginning when a candidate is accepted + and ending when the OpenClaw process exits. An in-process Gateway restart is + part of the same activation. - **Canonical config path**: the `openclaw.json` path that ordinary config persistence would target for the process. +## Ownership And Lifetime Contract + +Conforming implementations must preserve these responsibility boundaries: + +| Concern | Owner | Required behavior | +| --- | --- | --- | +| Config field semantics | The existing OpenClaw field owner | Defines defaults, validation, runtime meaning, and any bounded comparator | +| Source selection | The invoker or host supervisor | Supplies complete local files, permissions, and deterministic order | +| Composition and authority admission | OpenClaw config bootstrap | Derives first-declaration authority and rejects the complete candidate on failure | +| Active effective config | The existing Gateway config lifecycle | Publishes exactly one accepted snapshot for the layered activation | +| Mutation exclusion | Each layered Gateway server lifecycle | Registers and releases only its own canonical-path write block | +| Runtime consumption | Existing Gateway, plugin, channel, tool, and agent owners | Consumes the accepted ordinary config without interpreting layer ids or roles | + +Layer ids are diagnostic labels. They must not be treated as identities, +credentials, authorization principals, built-in roles, or durable ownership +records. + +First-declaration authority is derived state scoped to one candidate +evaluation. Once accepted, that authority and its effective snapshot remain +fixed for the layered activation. A full process restart creates a new +activation and must recompute both from the then-current ordered sources. An +in-process Gateway restart remains in the current activation and must reuse the +accepted snapshot. + +The implementation must not persist derived authority as another config +source, silently transfer authority during an activation, or allow a runtime +consumer to reinterpret composition. V1 defines no authority generation, +ownership lease, or write-through owner. + +A control plane or host may own desired state, rollout, source materialization, +and process replacement. Those responsibilities do not transfer ownership of +OpenClaw field semantics, admission, or the effective runtime snapshot. + ## Invocation Contract The Gateway CLI accepts a repeatable option: @@ -368,6 +406,7 @@ isolation are host responsibilities rather than OpenClaw core behavior. - one valid layer starts successfully; - repeated flags preserve declaration order; - duplicate or empty ids reject; +- changing only layer ids does not change composition or runtime behavior; - missing files and invalid JSON5 reject; - includes resolve relative to each source; - missing environment variables preserve normal warnings; @@ -400,6 +439,9 @@ isolation are host responsibilities rather than OpenClaw core behavior. - an existing canonical config remains byte-for-byte unchanged; - a full restart rereads sources while an in-process restart reuses the validated snapshot. +- a full restart recomputes authority from the complete current source list; +- no accepted activation can silently transfer authority or publish a partial + candidate. ### Hosted Cell @@ -423,6 +465,9 @@ An OpenClaw implementation is v1 conformant when it: - rejects the complete startup candidate on any finding or validation error; - publishes one ordinary runtime snapshot; - scopes immutability to the canonical config path and server lifetime; +- treats layer ids as diagnostic labels rather than identities or roles; +- keeps field semantics and bounded comparators with their existing OpenClaw + owners; - prevents persistent side effects before mutation rejection; - documents restart-to-apply behavior and the lack of tenant isolation; - passes the OpenClaw core portions of the minimum conformance suites.