openclaw-scheduler 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/AGENTS.md +302 -0
  2. package/BEST-PRACTICES.md +506 -0
  3. package/CHANGELOG.md +82 -0
  4. package/CODE_OF_CONDUCT.md +22 -0
  5. package/CONTEXT.md +26 -0
  6. package/CONTRIBUTING.md +73 -0
  7. package/IMPLEMENTATION_SPEC.md +170 -0
  8. package/INSTALL-ADDITIONAL-HOST.md +333 -0
  9. package/INSTALL-LINUX.md +419 -0
  10. package/INSTALL-WINDOWS.md +305 -0
  11. package/INSTALL.md +364 -0
  12. package/JOB-QUICK-REF.md +222 -0
  13. package/LICENSE +21 -0
  14. package/QUICK-START.md +256 -0
  15. package/README.md +2170 -0
  16. package/SECURITY.md +34 -0
  17. package/UNINSTALL.md +129 -0
  18. package/UPGRADING.md +436 -0
  19. package/agents.js +67 -0
  20. package/approval.js +107 -0
  21. package/backup.js +390 -0
  22. package/bin/openclaw-scheduler.js +138 -0
  23. package/cli.js +1083 -0
  24. package/db.js +122 -0
  25. package/dispatch/529-recovery.mjs +204 -0
  26. package/dispatch/README.md +372 -0
  27. package/dispatch/config.example.json +24 -0
  28. package/dispatch/deliver-watcher.sh +57 -0
  29. package/dispatch/hooks.mjs +171 -0
  30. package/dispatch/index.mjs +1836 -0
  31. package/dispatch/watcher.mjs +1396 -0
  32. package/dispatch-queue.js +112 -0
  33. package/dispatcher-approvals.js +96 -0
  34. package/dispatcher-delivery.js +43 -0
  35. package/dispatcher-maintenance.js +242 -0
  36. package/dispatcher-shell.js +29 -0
  37. package/dispatcher-strategies.js +1280 -0
  38. package/dispatcher-utils.js +81 -0
  39. package/dispatcher.js +855 -0
  40. package/docs/adr-schedule-ownership.md +73 -0
  41. package/docs/gateway-contract.md +904 -0
  42. package/docs/plans/2026-03-09-fix-typescript-types.md +91 -0
  43. package/docs/plans/2026-03-09-test-coverage-gaps.md +83 -0
  44. package/docs/plans/2026-03-10-dispatcher-refactor.md +801 -0
  45. package/docs/trust-architecture.md +266 -0
  46. package/gateway.js +473 -0
  47. package/idempotency.js +119 -0
  48. package/index.d.ts +864 -0
  49. package/index.js +17 -0
  50. package/jobs.js +1224 -0
  51. package/messages.js +357 -0
  52. package/migrate-consolidate.js +694 -0
  53. package/migrate.js +125 -0
  54. package/package.json +130 -0
  55. package/paths.js +79 -0
  56. package/prompt-context.js +94 -0
  57. package/retrieval.js +176 -0
  58. package/runs.js +270 -0
  59. package/scheduler-schema.js +101 -0
  60. package/schema.sql +480 -0
  61. package/scripts/dispatch-cli-utils.mjs +65 -0
  62. package/scripts/inbox-consumer.mjs +288 -0
  63. package/scripts/stuck-detector.sh +18 -0
  64. package/scripts/stuck-run-detector.mjs +333 -0
  65. package/scripts/telegram-webhook-check.mjs +238 -0
  66. package/setup.mjs +724 -0
  67. package/shell-result.js +214 -0
  68. package/task-tracker.js +300 -0
  69. package/team-adapter.js +335 -0
  70. package/v02-runtime.js +599 -0
@@ -0,0 +1,266 @@
1
+ # Trust Architecture
2
+
3
+ Date: 2026-03-30
4
+ Status: Accepted
5
+
6
+ ## Purpose
7
+
8
+ This document describes the trust architecture of the scheduler/sub-agent
9
+ execution model: what the boundary guarantees, what it does not, and how
10
+ operators reason about the security properties of scheduled workflows.
11
+
12
+ ## The Core Design: Scheduler as Broker, Child as Bounded Actor
13
+
14
+ The scheduler is a control-plane broker. It owns the dispatch queue,
15
+ credential resolution, trust evaluation, authorization gates, and run
16
+ lifecycle. It authenticates to the gateway with a single operator-provisioned
17
+ bearer token and holds whatever master/scoped keys the operator has loaded
18
+ into its environment or provider plugins.
19
+
20
+ Child tasks are bounded execution principals. They receive only what the
21
+ scheduler gives them and cannot escalate their own authority. A child's
22
+ credentials are resolved by an identity provider, narrowed via
23
+ `prepareHandoff` when the policy requires it, and materialized as scoped
24
+ environment variables (shell tasks) or auth-profile headers (agent tasks).
25
+ The child never sees the scheduler's own bearer token or master keys.
26
+
27
+ This is the broker/orchestrator + bounded actor pattern. The scheduler
28
+ decides what to run, with what credentials, under what trust constraints.
29
+ The child executes within those constraints.
30
+
31
+ ## When the Boundary Is a Real Security Boundary
32
+
33
+ The scheduler/child separation is a meaningful security boundary when the
34
+ child is narrower than the parent in at least one of these dimensions:
35
+
36
+ - **Identity** -- different principal, different subject kind, or
37
+ provider-resolved session with narrower scope.
38
+ - **Credentials** -- downscoped key, narrower OAuth scope, shorter-lived
39
+ token. With dynamic RAK minting, each child gets a per-task restricted
40
+ API key that is revoked on cleanup.
41
+ - **Tools** -- different tool set, different sandbox mode, spawn depth cap.
42
+ - **State** -- isolated session with no access to parent's memory or
43
+ conversation history.
44
+ - **Network/filesystem scope** -- different `allowed_paths`, different
45
+ network policy (`unrestricted` / `restricted` / `none`).
46
+
47
+ When the child is meaningfully narrower, the boundary limits blast radius:
48
+ a compromised or misbehaving child cannot access the parent's full
49
+ credential set, cannot read or write the parent's session state, and
50
+ cannot escalate to the parent's trust level.
51
+
52
+ A concrete example: a workflow like
53
+ `check balance (read-only) -> process payment (payments:write) -> send receipt (email:send)`
54
+ gives each step a per-task restricted key with exactly the scope it needs.
55
+ The payments step cannot read customer PII, the receipt step cannot make
56
+ charges, and if any step is compromised, the blast radius is one
57
+ short-lived restricted key -- not the master.
58
+
59
+ ## When the Boundary Is an Operational Boundary
60
+
61
+ If you cannot make the child meaningfully narrower in identity, tools,
62
+ state, or network/filesystem scope, then the sub-agent boundary is mostly
63
+ an execution/lifecycle boundary, not a strong security boundary. This
64
+ happens when:
65
+
66
+ - The child inherits the parent's full credentials
67
+ (`child_credential_policy: inherit`) without further narrowing.
68
+ - The child runs with the same tool set and no additional sandbox
69
+ constraints.
70
+ - The child's identity profile is identical to the parent's.
71
+
72
+ In this case the boundary still provides:
73
+
74
+ - **Lifecycle isolation.** The child can be timed out, retried, or
75
+ cancelled independently of the parent.
76
+ - **Attribution.** Each child has its own run record, execution ID, and
77
+ audit trail.
78
+ - **Context isolation.** The child session cannot read the parent's
79
+ conversation history or tool state.
80
+ - **Crash containment.** A child crash does not crash the parent or
81
+ sibling tasks.
82
+ - **Observability.** Independent run status, duration tracking, and
83
+ delivery.
84
+
85
+ These are real operational benefits, but they are not security guarantees
86
+ in the credential-scoping or access-control sense.
87
+
88
+ ## What the Model Guarantees
89
+
90
+ **Does guarantee:**
91
+
92
+ - Credential narrowing when `child_credential_policy` is `downscope` and
93
+ the provider implements scope hierarchy. The provider mints a restricted
94
+ key scoped to exactly the permissions the child declared, with a lifetime
95
+ tied to the task's timeout. The key is revoked in cleanup.
96
+ - Trust level enforcement: a child cannot run if its resolved trust level
97
+ is below the contract's `required_trust_level` (when
98
+ `contract_trust_enforcement` is `strict` or `block`).
99
+ - Trust level ceiling: `independent` and `downscope` policies both enforce
100
+ that the child's trust level cannot exceed the parent's. Violations
101
+ abort dispatch.
102
+ - Fail-closed behavior: missing providers, unresolvable credentials,
103
+ invalid delegation chains, and failed proof verification all abort the
104
+ run rather than proceeding with degraded security.
105
+ - Session isolation: child sessions have independent memory, history, and
106
+ tool scope from the parent.
107
+ - Audit attribution: every run is attributed to a resolved identity with
108
+ full trust/authorization/evidence chain.
109
+ - Security aborts do not fire triggered children: a parent that fails a
110
+ security gate (identity, trust, authorization, proof) does not dispatch
111
+ downstream work.
112
+
113
+ **Does not guarantee:**
114
+
115
+ - Network isolation between parent and child. They run on the same host,
116
+ same gateway, same network unless the contract's `network` policy adds
117
+ OS-level restrictions.
118
+ - Filesystem isolation beyond what the contract declares in
119
+ `allowed_paths`. There is no container or namespace boundary.
120
+ - That an inherited credential is narrower than the parent's. `inherit`
121
+ passes through verbatim.
122
+ - That a child cannot observe side effects of the parent's execution
123
+ through shared filesystem state.
124
+ - That the gateway itself enforces credential boundaries. Credential
125
+ enforcement happens at dispatch time in the scheduler; the gateway
126
+ trusts the auth-profile header it receives.
127
+
128
+ ## Credential Flow
129
+
130
+ The complete credential flow from operator to child execution:
131
+
132
+ ### 1. Operator provisions
133
+
134
+ Credentials enter the system via environment variables, Vault, managed
135
+ identity, or files. The operator controls `SCHEDULER_PROVIDER_PATH` and
136
+ the scheduler's execution environment. The operator may pre-provision
137
+ scoped keys (e.g. `STRIPE_KEY_FULL`, `STRIPE_KEY_PAYMENTS`,
138
+ `STRIPE_KEY_READONLY`) or a single master key that the provider uses to
139
+ mint restricted keys dynamically.
140
+
141
+ ### 2. Scheduler loads providers at startup
142
+
143
+ Every `*.js` file in `SCHEDULER_PROVIDER_PATH` is dynamically imported
144
+ and registered by type (identity, authorization, proof-verifier). The
145
+ directory must not be world-writable. This is the root of trust for the
146
+ provider plugin system.
147
+
148
+ ### 3. Scheduler resolves credentials at dispatch time
149
+
150
+ For each dispatched job, the scheduler runs the v0.2 evaluation chain:
151
+
152
+ 1. `resolveIdentity()` -- provider resolves a credential session or
153
+ structural fallback extracts identity from the job declaration.
154
+ 2. Child credential policy enforcement -- `none` strips credentials,
155
+ `inherit` forwards the parent's auth profile, `downscope` calls
156
+ `prepareHandoff()` to create a narrower session, `independent` uses
157
+ the child's own credentials (trust-capped at parent's level).
158
+ 3. `evaluateTrust()` -- compares effective trust level against the
159
+ contract floor. Blocks on `deny`, warns on `warn`.
160
+ 4. `verifyAuthorizationProof()` -- validates proof if declared. Blocks
161
+ if verification fails or verifier is missing.
162
+ 5. `evaluateAuthorization()` -- evaluates inline policy or invokes
163
+ authorization provider. Blocks on `deny`, aborts on `escalate`.
164
+
165
+ ### 4. Provider narrows credentials
166
+
167
+ When `child_credential_policy` is `downscope`, the provider's
168
+ `prepareHandoff()` creates a derivative credential with reduced scope.
169
+ With dynamic RAK minting, this means the provider calls the credential
170
+ issuer's API (e.g. Stripe Restricted Keys API) to mint a per-task key
171
+ scoped to exactly the permissions the child declared. The key's lifetime
172
+ is tied to the task's timeout plus a cleanup buffer.
173
+
174
+ Scope hierarchy validation ensures the child's requested scope is
175
+ reachable from the parent's scope via the declared hierarchy.
176
+ Unreachable scopes abort dispatch. If the handoff session's trust level
177
+ would exceed the parent's, dispatch is aborted.
178
+
179
+ ### 5. Child receives scoped credentials
180
+
181
+ For shell tasks, credentials are injected as environment variables via
182
+ `provider.materialize()`. For agent tasks, the scheduler either forwards a
183
+ resolved auth profile or, when materialization yields env vars, sends an
184
+ `x-openclaw-env-inject` header that the gateway can apply to the child
185
+ session. Gateway receiver support is still required for env injection, so
186
+ profile forwarding remains the compatibility path until that support is
187
+ present. The child never sees the master key.
188
+
189
+ ### 6. Cleanup
190
+
191
+ On task completion (success or failure), `provider.cleanup()` revokes
192
+ dynamically minted keys and removes temporary materialization artifacts.
193
+ Cleanup runs even on error paths.
194
+
195
+ ## Trust Boundary Definition
196
+
197
+ The operator controls:
198
+
199
+ - The scheduler's execution environment (host, env vars, process).
200
+ - The provider plugin directory (`SCHEDULER_PROVIDER_PATH`).
201
+ - The gateway connection (`OPENCLAW_GATEWAY_URL`,
202
+ `OPENCLAW_GATEWAY_TOKEN`).
203
+ - The manifest content (via `agentcli compile` + `agentcli apply`).
204
+
205
+ Everything downstream of the operator's control surface narrows only:
206
+
207
+ - A child task MUST NOT receive broader credentials than its parent.
208
+ - A child task MUST NOT run at a higher trust level than its parent.
209
+ - Provider plugins MUST NOT widen scope during handoff.
210
+ - The scheduler MUST NOT auto-escalate trust on retry or timeout.
211
+
212
+ If the provider directory is compromised, the trust model is broken.
213
+ If the scheduler's environment variables are compromised, the trust model
214
+ is broken. These are root-of-trust assumptions, not runtime invariants.
215
+
216
+ ## Benefits by Dimension
217
+
218
+ | Benefit | Always present | Only with narrowing |
219
+ |---------|---------------|---------------------|
220
+ | Blast radius (credential) | No | Yes (`downscope` / `independent`) |
221
+ | Blast radius (crash) | Yes | Yes |
222
+ | Attribution | Yes | Yes |
223
+ | Context isolation | Yes | Yes |
224
+ | Lifecycle independence | Yes | Yes |
225
+ | Least privilege (credentials) | No | Yes (`downscope` + dynamic RAK) |
226
+ | Least privilege (trust level) | Partial (contract floor) | Yes |
227
+ | Audit traceability | Yes | Yes |
228
+ | Independent timeout/retry | Yes | Yes |
229
+
230
+ ## Credential Strategies
231
+
232
+ ### Precreated keys (available now)
233
+
234
+ The operator creates restricted API keys ahead of time and stores them in
235
+ environment variables or Vault. The provider resolves the correct key by
236
+ scope name at dispatch time. No runtime API calls to the credential
237
+ issuer.
238
+
239
+ Trade-offs: simpler setup, works today, but keys are static and rotation
240
+ is operator-managed. Key count grows with the number of distinct scopes.
241
+
242
+ ### Dynamic key minting (available now)
243
+
244
+ The provider uses an operator-provisioned master key to mint a per-task
245
+ restricted key via the credential issuer's API at dispatch time. The
246
+ minted key has:
247
+
248
+ - Scope limited to exactly the permissions the task declared.
249
+ - Lifetime tied to the task's timeout plus a cleanup buffer.
250
+ - Automatic revocation in the cleanup phase.
251
+
252
+ The master key itself is operator-provisioned and the minted keys are
253
+ always narrower than the master, never wider. This gives true per-task
254
+ credential lifecycle without operator-managed key inventories.
255
+
256
+ Both strategies use the same manifest syntax. The provider's
257
+ `key_strategy` configuration determines which path runs.
258
+
259
+ ## Cross-References
260
+
261
+ - Execution Identity Architecture: `agentcli/docs/execution-identity.md`
262
+ - Gateway contract (session isolation, auth-profile forwarding):
263
+ `docs/gateway-contract.md`
264
+ - Provider plugin system: `provider-registry.js`
265
+ - v0.2 runtime evaluation: `v02-runtime.js`
266
+ - ADR on schedule ownership: `docs/adr-schedule-ownership.md`