@synapsor/runner 1.5.0 → 1.5.4

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 (83) hide show
  1. package/CHANGELOG.md +134 -3
  2. package/README.md +108 -113
  3. package/dist/authoring.d.ts +23 -0
  4. package/dist/authoring.d.ts.map +1 -0
  5. package/dist/authoring.mjs +1318 -0
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +4 -5
  8. package/dist/local-ui.d.ts +14 -0
  9. package/dist/local-ui.d.ts.map +1 -1
  10. package/dist/runner.mjs +12334 -5256
  11. package/dist/runtime.mjs +817 -149
  12. package/dist/shadow.d.ts +36 -0
  13. package/dist/shadow.d.ts.map +1 -0
  14. package/dist/shadow.mjs +5362 -0
  15. package/docs/README.md +25 -2
  16. package/docs/alternatives.md +122 -0
  17. package/docs/capability-authoring.md +49 -1
  18. package/docs/client-recipes.md +218 -0
  19. package/docs/cloud-mode.md +18 -0
  20. package/docs/contract-testing.md +9 -7
  21. package/docs/cursor-plugin.md +78 -0
  22. package/docs/database-enforced-scope.md +8 -0
  23. package/docs/dsl-reference.md +45 -4
  24. package/docs/effect-regression.md +39 -2
  25. package/docs/fresh-developer-usability.md +110 -0
  26. package/docs/getting-started-own-database.md +102 -5
  27. package/docs/guarded-crud-writeback.md +10 -1
  28. package/docs/host-compatibility.md +59 -0
  29. package/docs/http-mcp.md +222 -207
  30. package/docs/limitations.md +9 -2
  31. package/docs/local-mode.md +11 -7
  32. package/docs/mcp-audit.md +166 -2
  33. package/docs/mcp-client-setup.md +28 -11
  34. package/docs/mcp-clients.md +43 -8
  35. package/docs/openai-agents-sdk.md +16 -2
  36. package/docs/oss-vs-cloud.md +3 -0
  37. package/docs/production.md +72 -7
  38. package/docs/release-notes.md +131 -4
  39. package/docs/runner-bundles.md +7 -2
  40. package/docs/runner-config-reference.md +96 -6
  41. package/docs/running-a-runner-fleet.md +42 -13
  42. package/docs/security-boundary.md +58 -8
  43. package/docs/shadow-studies.md +47 -4
  44. package/docs/store-lifecycle.md +93 -5
  45. package/docs/troubleshooting-first-run.md +46 -0
  46. package/examples/openai-agents-http/README.md +10 -4
  47. package/examples/openai-agents-http/agent.py +2 -2
  48. package/examples/runner-fleet/Dockerfile +7 -2
  49. package/examples/runner-fleet/README.md +11 -7
  50. package/examples/runner-fleet/docker-compose.yml +4 -4
  51. package/examples/runner-fleet/mint-dev-token.mjs +1 -1
  52. package/examples/runner-fleet/start-tls-runner.sh +21 -0
  53. package/examples/runner-fleet/synapsor.runner.json +27 -1
  54. package/examples/support-billing-agent/README.md +18 -0
  55. package/examples/support-billing-agent/app/README.md +6 -0
  56. package/examples/support-billing-agent/app/contract.ts +28 -0
  57. package/examples/support-billing-agent/app/effect-adapter.mjs +23 -0
  58. package/examples/support-billing-agent/app/record-shadow-outcomes.mjs +44 -0
  59. package/examples/support-billing-agent/scripts/run-evaluation.sh +6 -5
  60. package/examples/support-plan-credit/README.md +54 -3
  61. package/examples/support-plan-credit/mcp-client-examples/README.md +23 -0
  62. package/examples/support-plan-credit/mcp-client-examples/claude-code.sh +34 -0
  63. package/examples/support-plan-credit/mcp-client-examples/codex.config.toml +24 -0
  64. package/examples/support-plan-credit/mcp-client-examples/generic-stdio.mjs +35 -0
  65. package/examples/support-plan-credit/mcp-client-examples/generic-streamable-http.json +4 -1
  66. package/examples/support-plan-credit/mcp-client-examples/generic-streamable-http.mjs +31 -0
  67. package/examples/support-plan-credit/mcp-client-examples/google-adk.py +40 -0
  68. package/examples/support-plan-credit/mcp-client-examples/langchain.mjs +29 -0
  69. package/examples/support-plan-credit/mcp-client-examples/llamaindex.py +36 -0
  70. package/examples/support-plan-credit/mcp-client-examples/openai-agents-stdio.ts +1 -1
  71. package/examples/support-plan-credit/mcp-client-examples/openai-agents-streamable-http.ts +5 -1
  72. package/examples/support-plan-credit/mcp-client-examples/vscode.mcp.json +20 -0
  73. package/examples/support-plan-credit/synapsor/actions/support.propose_plan_credit.contract-tests.generated.json +221 -0
  74. package/examples/support-plan-credit/synapsor/actions/support.propose_plan_credit.ts +34 -0
  75. package/examples/support-plan-credit/synapsor.contract.json +0 -3
  76. package/fixtures/mcp-audit/README.md +14 -0
  77. package/fixtures/mcp-audit/cursor-bypass-config.json +38 -0
  78. package/fixtures/mcp-audit/dangerous-tools-list.json +33 -0
  79. package/fixtures/mcp-audit/reviewed-proposal-tools-list.json +60 -0
  80. package/package.json +18 -3
  81. package/schemas/mcp-audit-report.schema.json +100 -1
  82. package/schemas/synapsor.contract-tests.schema.json +1 -1
  83. package/schemas/synapsor.runner.schema.json +80 -0
@@ -10,7 +10,134 @@ npx -y -p @synapsor/runner synapsor-runner demo --quick
10
10
  The OSS runner command is `synapsor-runner`. The `synapsor` command is reserved
11
11
  for the Synapsor Cloud CLI.
12
12
 
13
- ## 1.5.0 (prepared, not published)
13
+ ## 1.5.4 (prepared, not published)
14
+
15
+ ### Networked MCP authentication hardening
16
+
17
+ - Local stdio still opens no network listener and needs no HTTP credential.
18
+ Loopback HTTP remains authenticated by default with an operator-provisioned
19
+ opaque token.
20
+ - Remote HTTP now refuses to bind over an undeclared cleartext channel. Operators
21
+ must use Runner-owned TLS, explicitly declare a trusted TLS proxy, or select an
22
+ authenticated and prominently diagnosed break-glass posture.
23
+ - Opaque endpoint tokens are constrained to local or explicit single-tenant use.
24
+ Remote use requires adequate entropy; one active and one previous env-provided
25
+ value support bounded rotation without logging either value.
26
+ - Shared deployments require signed per-session identity and `http_claims`
27
+ trusted context. Runner revalidates issuer, audience/resource, time, scope,
28
+ tenant, principal, signature, and algorithm on every request and rejects token
29
+ or identity swaps inside an MCP session.
30
+ - RFC 9728 protected-resource metadata and Bearer challenges let compatible MCP
31
+ clients discover the configured external authorization server. Runner verifies
32
+ access tokens but does not implement user login or token issuance.
33
+ - TLS/mTLS preflight, public-only bounded JWKS handling, exact Origin/Host checks,
34
+ request/session bounds, doctor diagnostics, client recipes, and fleet examples
35
+ now share one documented deployment ladder.
36
+ - `lifecycle`, `lifecycle show`, and `lifecycle show latest` now inspect the
37
+ newest complete proposal lifecycle without an id. Filters and known
38
+ proposal/evidence/replay/job/intent/receipt/audit handles resolve a stable,
39
+ read-only `synapsor.lifecycle-view.v1` domain document across local SQLite
40
+ and shared PostgreSQL runtime stores.
41
+ - DSL UPDATE authoring now requires an exact `CONFLICT GUARD <column>` instead
42
+ of silently choosing projection hashing. A reviewer-visible weak compatibility
43
+ clause remains only for ordinary single-row source-DB UPDATE and is rejected
44
+ for the stronger operation modes.
45
+ - Runner rejects canonical `FROM SESSION` with
46
+ `SESSION_BINDING_UNSUPPORTED` rather than treating its key as a process
47
+ environment variable. Explicit ENVIRONMENT, verified HTTP_CLAIM, verified
48
+ CLOUD_SESSION, and STATIC_DEV behavior remains distinct.
49
+
50
+ Prepared package versions: `@synapsor/runner@1.5.4` and
51
+ `@synapsor/dsl@1.4.4`. `@synapsor/spec@1.4.2` and the Cloud CLI are
52
+ unchanged. Nothing has been published by this repository change.
53
+
54
+ ## 1.5.3 (published 2026-07-21)
55
+
56
+ ### Intent to Safe Action
57
+
58
+ - `start --action <name> --description <intent>` creates one inert TypeScript
59
+ Safe Action scaffold from an existing reviewed read boundary. Project-scoped
60
+ instructions let a coding agent complete and validate only the draft.
61
+ - `action validate` statically parses the restricted object and emits a
62
+ digest-addressed disabled canonical draft, explanation, and deterministic
63
+ allow/deny/effect tests. It does not import adopter code, activate the action,
64
+ or alter the active MCP tool catalog.
65
+ - The secured Workbench runs one real source-unchanged staging Data PR before
66
+ activation and requires `ACTIVATE` plus the complete digest. Cloud-linked
67
+ projects use governed Cloud activation. Existing proposals remain bound to
68
+ the exact active contract digest they were created under.
69
+ - The package includes a current-format Cursor plugin with
70
+ `/synapsor-protect`, diagnostics, deterministic package verification, and
71
+ project-safe MCP wiring. Cursor host claims remain evidence-labeled; a stable
72
+ UI pass and Marketplace submission are owner gates.
73
+ - MCP audit now renders a model-authority map and opt-in bypass evidence as
74
+ text, JSON, Markdown, or SARIF. A checked-in GitHub Actions workflow and Safe
75
+ Action team CI are deterministic and non-mutating.
76
+ - Proposal-only integration recipes cover Claude Code, Codex, VS Code, OpenAI
77
+ Agents, LangChain/LangGraph, Google ADK, LlamaIndex, and generic MCP clients.
78
+ - The first two README screens, public website, article, discovery routes, and
79
+ 36-second evidence-backed demo now lead with the own-project Data PR path.
80
+
81
+ Published package version: `@synapsor/runner@1.5.3`. Spec, DSL, and Cloud CLI
82
+ packages were unchanged.
83
+
84
+ ## 1.5.2 (prepared, not published)
85
+
86
+ ### First safe action in an existing application
87
+
88
+ - `start --from-env DATABASE_URL` and
89
+ `try --prove --from-env DATABASE_URL` now converge on one own-data
90
+ onboarding path. It inspects a staging database read-only, asks the developer
91
+ to choose trusted scope and one reviewed action, emits canonical files, and
92
+ never substitutes synthetic data after an own-data failure.
93
+ - The localhost workbench presents Project, Data source, Trust scope, Action,
94
+ Agent, Test, and Review plus an exact proposal Data PR. Test is complete only
95
+ after a scoped tool call records query audit, not after config validation.
96
+ - Project-scoped Cursor install/status/uninstall previews and owns only its MCP
97
+ entry, preserves other entries, and launches the exact Runner version.
98
+ Approval, apply, revert, credentials, and trusted identity stay outside MCP.
99
+ - Local activation reports measure proof, onboarding, Cursor, first read, and
100
+ first proposal without telemetry or business identifiers. Product time
101
+ excludes initial package download; cold `npx` time is reported separately
102
+ as an observed environment-specific measurement.
103
+ - Optional TypeScript authoring emits the same canonical Spec contract. Shadow
104
+ trust progression and provider-neutral effect regression remain
105
+ non-activating and preserve deterministic/external-model provenance.
106
+ - MCP audit candidates can open directly in the secured workbench, and the
107
+ release includes the host-compatibility matrix and support/billing reference
108
+ workflow.
109
+ - The package now requires Node 22.13.0 or newer, the first Node 22 release
110
+ where Runner's `node:sqlite` dependency is available without an experimental
111
+ runtime flag. Older Node versions fail immediately with an actionable error.
112
+
113
+ Prepared package version: `@synapsor/runner@1.5.2`. Spec, DSL, and Cloud CLI
114
+ packages are unchanged. Nothing has been published by this repository change.
115
+
116
+ ## 1.5.1 (prepared, not published)
117
+
118
+ ### Safe disposable state ownership
119
+
120
+ - `try --state-dir` no longer treats the supplied directory as disposable.
121
+ The supplied path is a caller-owned container and Runner uses a marked
122
+ managed child beneath it.
123
+ - Cleanup removes only known direct try-state files. Unrelated files remain in
124
+ place, and roots, home/cwd/repository paths, traversal, symlink escapes,
125
+ unmarked lookalikes, and linked managed files fail closed.
126
+ - An atomic state lease prevents concurrent runs from corrupting each other and
127
+ permits recovery from a valid lease whose process is no longer alive.
128
+ - `demo inspect --state-dir` resolves the same managed child. The default
129
+ `.synapsor/try` path safely adopts only the recognized legacy file layout.
130
+ - Explicit `--force` replacement of generated schema and MCP-audit candidate
131
+ directories now rejects protected paths, symlinked ancestors, and linked or
132
+ invalid ownership markers.
133
+ - The embedded `try` data source is consistently identified as synthetic.
134
+ - The packaged YAML parser is updated to `2.8.3` to include the upstream fix
135
+ for deeply nested collection denial of service.
136
+
137
+ Prepared package version: `@synapsor/runner@1.5.1`. Spec, DSL, and Cloud CLI
138
+ packages are unchanged. Nothing has been published by this repository change.
139
+
140
+ ## 1.5.0 (published 2026-07-20)
14
141
 
15
142
  ### Complete guarded-action developer proof
16
143
 
@@ -45,8 +172,8 @@ for the Synapsor Cloud CLI.
45
172
  immediately second, and distinguishes application scope, PostgreSQL RLS, and
46
173
  tenant-bound isolation without overstating any of them.
47
174
 
48
- Prepared package version: `@synapsor/runner@1.5.0`. Spec, DSL, and Cloud CLI
49
- packages are unchanged. Nothing has been published by this repository change.
175
+ Published package version: `@synapsor/runner@1.5.0`. Spec, DSL, and Cloud CLI
176
+ packages were unchanged.
50
177
 
51
178
  ## 1.4.123 (2026-07-17)
52
179
 
@@ -744,7 +871,7 @@ not change and must not be republished for this release.
744
871
  CDC, managed runner fleets, compliance exports, production SLA, or C++ DBMS
745
872
  internals.
746
873
  - The local store is single-node SQLite for local/dev/staging usage.
747
- - Node >= 22.5.0 is required because the local ledger uses Node's
874
+ - Node >= 22.13.0 is required because the local ledger uses Node's unflagged
748
875
  `node:sqlite` runtime. Use a supported Node runtime or the Docker-backed
749
876
  source demo path.
750
877
 
@@ -67,8 +67,13 @@ npx -y -p @synapsor/runner synapsor-runner cloud sync latest \
67
67
 
68
68
  For the OpenAI Agents SDK, use the included TypeScript examples. Their stdio
69
69
  command enables `--alias-mode openai`; the Streamable HTTP example includes the
70
- matching server command. Claude, Cursor, and generic templates use canonical
71
- dotted capability names.
70
+ matching server command and reads its Bearer credential from
71
+ `SYNAPSOR_RUNNER_HTTP_TOKEN`. The generic HTTP template uses the same environment
72
+ reference. Neither file embeds a credential value. An operator generates and
73
+ provisions the opaque token to the Runner process and authorized client; use a
74
+ signed identity-provider token instead for shared multi-user deployments. See
75
+ [HTTP MCP](http-mcp.md). Claude, Cursor, and generic stdio templates use
76
+ canonical dotted capability names and need no HTTP credential.
72
77
 
73
78
  Approval and writeback remain outside the model-facing MCP tool surface. Use
74
79
  `proposals`, `apply`, `receipts`, and `replay` from a trusted operator shell.
@@ -36,6 +36,7 @@ Unknown keys fail when `strict` is true (the default).
36
36
  | `approvals` | No | Local approval overrides. |
37
37
  | `operator_identity` | No | Verified operator identity and apply-role wiring for approve/reject/apply. |
38
38
  | `session_auth` | HTTP claims | HS256 development or asymmetric RS256/ES256 session-token verification. |
39
+ | `http_security` | Networked HTTP | Deployment profile, protected channel, endpoint-token env names, OAuth protected-resource metadata, exact Origin/Host policy, and request/session bounds. |
39
40
  | `rate_limits` | No | Operational fixed-window limits; fleet-wide only with shared `runtime_store`. |
40
41
  | `metrics` | No | Separately authorized scrapeable HTTP metrics. Disabled by default. |
41
42
  | `graduated_trust` | No | Off-by-default, operator-only policy recommendation criteria and kill switch. |
@@ -212,8 +213,16 @@ Providers are `environment`, `static_dev`, `http_claims`, and `cloud_session`.
212
213
  Capabilities may reference a context by name. The model never receives tenant
213
214
  or principal as an overridable argument.
214
215
 
215
- For multi-tenant Streamable HTTP services, use `http_claims` plus signed
216
- session auth:
216
+ The language-neutral canonical contract can preserve a `session` binding for
217
+ implementations such as C++/Cloud that own a typed session boundary. Runner has
218
+ no generic web-session provider and rejects it with
219
+ `SESSION_BINDING_UNSUPPORTED`; it never treats the key as an environment
220
+ variable. Use `environment` for local stdio, verified `http_claims` for shared
221
+ Streamable HTTP, or verified `cloud_session` for Cloud embedding.
222
+
223
+ For controlled loopback development, `http_claims` can use an HS256 session
224
+ token. Runner holds the symmetric signing/verification secret in this mode, so
225
+ it is not the recommended shared production profile:
217
226
 
218
227
  ```json
219
228
  {
@@ -229,7 +238,7 @@ session auth:
229
238
  "secret_env": "SYNAPSOR_SESSION_JWT_SECRET",
230
239
  "previous_secret_env": "SYNAPSOR_PREVIOUS_SESSION_JWT_SECRET",
231
240
  "issuer": "https://identity.example",
232
- "audience": "synapsor-runner"
241
+ "audience": "https://runner.example/mcp"
233
242
  }
234
243
  }
235
244
  ```
@@ -239,16 +248,24 @@ the active secret first, then the previous secret. Existing MCP sessions remain
239
248
  bound to the exact token fingerprint, so clients cannot swap tenant/principal
240
249
  identity inside an initialized session.
241
250
 
242
- For networked deployments prefer asymmetric public-key verification:
251
+ For a shared networked deployment, use asymmetric public-key verification and
252
+ declare the protected resource and channel explicitly:
243
253
 
244
254
  ```json
245
255
  {
256
+ "trusted_context": {
257
+ "provider": "http_claims",
258
+ "values": {
259
+ "tenant_id_key": "tenant_id",
260
+ "principal_key": "sub"
261
+ }
262
+ },
246
263
  "session_auth": {
247
264
  "provider": "jwt_asymmetric",
248
- "algorithms": ["RS256"],
265
+ "algorithms": ["RS256", "ES256"],
249
266
  "jwks_url_env": "SYNAPSOR_SESSION_JWKS_URL",
250
267
  "issuer": "https://identity.example",
251
- "audience": "synapsor-runner",
268
+ "audience": "https://runner.example/mcp",
252
269
  "tenant_claim": "tenant_id",
253
270
  "principal_claim": "sub",
254
271
  "clock_skew_seconds": 30,
@@ -256,6 +273,19 @@ For networked deployments prefer asymmetric public-key verification:
256
273
  "jwks_cooldown_seconds": 30,
257
274
  "fetch_timeout_ms": 3000,
258
275
  "max_response_bytes": 1048576
276
+ },
277
+ "http_security": {
278
+ "deployment": "shared",
279
+ "channel": "trusted_tls_proxy",
280
+ "oauth_resource": {
281
+ "resource": "https://runner.example/mcp",
282
+ "authorization_servers": ["https://identity.example"],
283
+ "scopes_supported": ["synapsor:mcp"],
284
+ "required_scopes": ["synapsor:mcp"],
285
+ "resource_name": "Synapsor Runner"
286
+ },
287
+ "allowed_origins": ["https://agent-console.example"],
288
+ "allowed_hosts": ["runner.example"]
259
289
  }
260
290
  }
261
291
  ```
@@ -268,6 +298,66 @@ rejected. Every effective named context in an `http_claims` server must bind
268
298
  tenant and principal from claims; environment/static contradictions fail with
269
299
  `TRUSTED_CONTEXT_PROVIDER_CONFLICT` before serving.
270
300
 
301
+ `session_auth` verifies identity; it does not issue access tokens. The external
302
+ identity provider identified by `issuer` issues a short-lived token whose
303
+ audience is the exact protected-resource URL. Runner fetches only public JWKS
304
+ material and validates the configured claims on every request. Required OAuth
305
+ scopes are declared under `http_security.oauth_resource`.
306
+
307
+ ## HTTP security
308
+
309
+ `http_security` is Runner deployment wiring, not part of a portable contract:
310
+
311
+ ```json
312
+ {
313
+ "http_security": {
314
+ "deployment": "single_tenant",
315
+ "channel": "trusted_tls_proxy",
316
+ "static_token": {
317
+ "active_env": "SYNAPSOR_RUNNER_HTTP_TOKEN",
318
+ "previous_env": "SYNAPSOR_RUNNER_HTTP_TOKEN_PREVIOUS"
319
+ },
320
+ "allowed_origins": ["https://agent-console.example"],
321
+ "allowed_hosts": ["runner.internal.example"],
322
+ "limits": {
323
+ "max_request_bytes": 65536,
324
+ "max_header_bytes": 8192,
325
+ "max_sessions": 500,
326
+ "session_idle_timeout_seconds": 300,
327
+ "request_timeout_ms": 15000,
328
+ "headers_timeout_ms": 5000,
329
+ "keep_alive_timeout_ms": 5000,
330
+ "max_connections": 1000
331
+ }
332
+ }
333
+ }
334
+ ```
335
+
336
+ - `deployment` is `loopback`, `single_tenant`, or `shared`. Shared requires
337
+ verified signed `http_claims`, exact issuer/audience, and RFC 9728
338
+ `oauth_resource` metadata.
339
+ - `channel` is `direct_tls`, `trusted_tls_proxy`, or the explicitly unsafe
340
+ `insecure_http_break_glass`. A non-loopback listener without one refuses to
341
+ bind. Break glass remains authenticated.
342
+ - `static_token` names one active and optionally one previous environment
343
+ variable. It is opaque service access for loopback/single-tenant use, not
344
+ tenant identity. Values never belong in config.
345
+ - `oauth_resource.resource` is the exact HTTPS MCP endpoint and must equal
346
+ `session_auth.audience`. `authorization_servers` names external issuers;
347
+ Runner does not implement login or token refresh.
348
+ - `allowed_origins` contains exact browser origins; an omitted browser Origin
349
+ remains valid for native clients. Wildcards are forbidden.
350
+ - `allowed_hosts` contains exact direct/public Host authorities. Forwarded Host
351
+ and forwarded identity headers are not trusted automatically.
352
+ - `limits` bounds bodies, headers, concurrent sessions/connections, idle
353
+ sessions, and HTTP timing. Defaults are documented in [HTTP MCP](http-mcp.md).
354
+
355
+ Runner-owned TLS certificate, key, and optional client-CA PEM values are passed
356
+ by environment references on the serve command (`--tls-cert-env`,
357
+ `--tls-key-env`, `--tls-ca-env`, `--require-client-cert`). Private TLS material
358
+ does not go in this JSON file. A trusted-proxy channel assumes firewall/private
359
+ network controls prevent clients from bypassing that proxy.
360
+
271
361
  ## Contracts and embedded capabilities
272
362
 
273
363
  ```json
@@ -1,6 +1,6 @@
1
1
  # Running A Small Runner Fleet
2
2
 
3
- Synapsor Runner 1.1 supports a bounded small-fleet deployment. It does not
3
+ Synapsor Runner supports a bounded small-fleet deployment. It does not
4
4
  turn the OSS package into Synapsor Cloud and it does not promise an unbounded,
5
5
  multi-region, or SLA-backed control plane.
6
6
 
@@ -32,7 +32,7 @@ model-facing approval/apply tools as blockers. Audit is a static review, not a
32
32
  security proof; follow it with `tools preview`, `doctor`, and the live fleet
33
33
  verification.
34
34
 
35
- ## What 1.1 Guarantees
35
+ ## What The Bounded Fleet Guarantees
36
36
 
37
37
  With `storage.shared_postgres.mode = "runtime_store"`:
38
38
 
@@ -116,7 +116,7 @@ Use asymmetric verification for a networked fleet:
116
116
  "algorithms": ["RS256"],
117
117
  "jwks_url_env": "SYNAPSOR_SESSION_JWKS_URL",
118
118
  "issuer": "https://identity.example",
119
- "audience": "synapsor-runner",
119
+ "audience": "https://runner.example/mcp",
120
120
  "tenant_claim": "tenant_id",
121
121
  "principal_claim": "sub",
122
122
  "clock_skew_seconds": 30,
@@ -124,10 +124,28 @@ Use asymmetric verification for a networked fleet:
124
124
  "jwks_cooldown_seconds": 30,
125
125
  "fetch_timeout_ms": 3000,
126
126
  "max_response_bytes": 1048576
127
+ },
128
+ "http_security": {
129
+ "deployment": "shared",
130
+ "channel": "trusted_tls_proxy",
131
+ "oauth_resource": {
132
+ "resource": "https://runner.example/mcp",
133
+ "authorization_servers": ["https://identity.example"],
134
+ "scopes_supported": ["synapsor:mcp"],
135
+ "required_scopes": ["synapsor:mcp"]
136
+ },
137
+ "allowed_hosts": ["runner.example"],
138
+ "allowed_origins": ["https://agent-console.example"]
127
139
  }
128
140
  }
129
141
  ```
130
142
 
143
+ The external identity provider issues the short-lived client JWT for resource
144
+ `https://runner.example/mcp`; Runner only verifies it. The load balancer must
145
+ terminate TLS and prevent direct access to the private Runner listeners. If
146
+ Runner owns TLS instead, use protected certificate/key env references and set
147
+ `channel` to `direct_tls`.
148
+
131
149
  Every served capability context must bind tenant and principal from
132
150
  `HTTP_CLAIM`. Runner rejects an environment-bound contract mixed into this
133
151
  server with `TRUSTED_CONTEXT_PROVIDER_CONFLICT` before tools are served.
@@ -147,6 +165,12 @@ material. Unknown `kid` triggers at most one controlled refresh. For offline
147
165
  deployments, configure one public PEM through `public_key_env` or
148
166
  `public_key_path`; never place a private key in Runner config.
149
167
 
168
+ Runner publishes RFC 9728 protected-resource metadata at the path derived from
169
+ the public resource and returns scope-aware Bearer challenges. The identity
170
+ provider, not Runner, owns user login, client registration, token issuance, and
171
+ refresh. An MCP session ID is only protocol state; every request is
172
+ reauthenticated and remains pinned to the initialized credential identity.
173
+
150
174
  ## Ledger And Source Pools
151
175
 
152
176
  ```json
@@ -329,12 +353,13 @@ configured handler timeout. A hard kill is recoverable through leases and
329
353
  source-side idempotency receipts, but should remain an incident signal.
330
354
 
331
355
  Rotate JWKS keys by publishing the new `kid`, waiting at least the configured
332
- cache lifetime, switching issuers, then retiring the old key. Rotate metrics,
333
- handler, database, and ledger credentials independently through their named
334
- environment variables.
356
+ cache lifetime while issuers sign with the new key, then retiring the old key.
357
+ Do not casually change the issuer or protected-resource audience during a key
358
+ rotation. Rotate TLS/mTLS, metrics, handler, database, and ledger credentials
359
+ independently through their named environment variables.
335
360
 
336
- Only homogeneous Runner 1.1 instances are verified by `test:fleet`. Mixed
337
- 1.0/1.1 operation is not claimed. For rolling upgrades:
361
+ Only homogeneous Runner instances at the release under test are verified by
362
+ `test:fleet`. Mixed-version operation is not claimed. For rolling upgrades:
338
363
 
339
364
  1. back up and verify the ledger;
340
365
  2. run schema/config validation with the target version;
@@ -351,8 +376,12 @@ roll back the full fleet. Never mix versions merely because both are `1.x`.
351
376
 
352
377
  ## TLS Boundary
353
378
 
354
- Terminate public TLS or mTLS at a trusted load balancer, or configure Runner's
355
- tested TLS/mTLS options directly. Keep Runner-to-database TLS verification on,
356
- use private networking where possible, restrict `/metrics`, and never forward
357
- untrusted identity headers as claims. The bearer JWT itself, not a proxy-added
358
- tenant header, is the session authority.
379
+ Terminate public TLS or mTLS at a trusted load balancer and declare
380
+ `channel: trusted_tls_proxy`, or configure Runner's tested TLS/mTLS options
381
+ directly. A proxy declaration is a security assertion: firewall the private
382
+ listener so clients cannot bypass the proxy, protect the private hop, and
383
+ preserve an allowed Host. Keep Runner-to-database TLS verification on, use
384
+ private networking where possible, and restrict `/metrics` separately. The
385
+ verified Bearer JWT itself, not a proxy-added tenant/principal header, is the
386
+ session authority. See [HTTP MCP](http-mcp.md) for channel flags, limits, and
387
+ doctor checks.
@@ -36,19 +36,55 @@ The model does not receive:
36
36
  - a generic `execute_sql` tool;
37
37
  - arbitrary table, schema, or column names;
38
38
  - database URLs or credentials;
39
+ - contract activation tools;
39
40
  - approval tools;
40
41
  - commit/writeback tools;
42
+ - MCP endpoint tokens, JWTs, refresh tokens, client secrets, or TLS keys;
41
43
  - trusted tenant or principal authority as ordinary model arguments.
42
44
 
45
+ ## Network transport and identity
46
+
47
+ Local stdio is the preferred one-client path. The MCP client launches Runner and
48
+ communicates over the child process's standard input/output, so Runner opens no
49
+ network listener and HTTP authentication does not apply.
50
+
51
+ A listening HTTP endpoint has no inherent client identity. Runner therefore
52
+ requires Bearer authentication by default. On loopback or an explicitly
53
+ single-tenant service, Bearer may carry one high-entropy opaque endpoint token;
54
+ that token only answers "may this service call the endpoint?" and is not a user
55
+ or tenant identity. Shared services require signed per-session JWTs, preferably
56
+ RS256/ES256 from an external identity provider, with exact issuer,
57
+ audience/resource, time, scope, tenant, and principal validation on every
58
+ request. An MCP session ID only routes protocol state and never authenticates a
59
+ caller.
60
+
61
+ Non-loopback HTTP refuses to bind unless Runner owns TLS, an explicitly trusted
62
+ TLS proxy protects the public and private hops, or an authenticated emergency
63
+ cleartext break-glass mode is selected. TLS protects the channel; mTLS also
64
+ authenticates a workload certificate but does not replace per-user/tenant claims
65
+ in a shared service. Exact Origin and Host policy, request/session limits, and
66
+ credential-pinned MCP sessions narrow browser, DNS-rebinding, exhaustion, and
67
+ session-swap attacks.
68
+
69
+ These controls remain independent: transport authentication permits endpoint
70
+ access; verified session claims provide tenant/principal context; the contract
71
+ authorizes semantic capabilities; database roles/RLS constrain the connection;
72
+ operator identity authorizes activation, approval, apply, reconcile, and
73
+ revert. See [HTTP MCP](http-mcp.md).
74
+
43
75
  Reviewed aggregate tools expose one scalar only. Their function, column,
44
76
  tenant key, optional equality selection, and minimum-group threshold are fixed
45
77
  in the contract; the model receives no predicate arguments or member rows.
46
78
  Suppression reduces single-record inference but does not replace statistical
47
79
  privacy review. See [Bounded Aggregate Reads](aggregate-reads.md).
48
80
 
49
- Trusted context comes from local configuration, environment bindings, or Cloud
50
- session context in Cloud mode. Tenant, principal, and authorization scope must
51
- not be accepted from the model as authority.
81
+ Trusted context comes from operator-controlled process bindings for local or
82
+ explicitly single-tenant use, verified signed HTTP claims for a shared service,
83
+ or verified Cloud session context in Cloud mode. Tenant, principal, and
84
+ authorization scope must not be accepted from the model as authority.
85
+ Canonical `SESSION` content is rejected by Runner with
86
+ `SESSION_BINDING_UNSUPPORTED`; Runner never aliases it to environment values,
87
+ arbitrary headers, MCP metadata, or model arguments.
52
88
 
53
89
  With a shared credential, these checks are application-level enforcement and
54
90
  depend on Runner's fixed predicate implementation. Optional PostgreSQL RLS mode
@@ -84,6 +120,18 @@ explicit host-level administrative boundary.
84
120
  Proposal tools read the current row through the read credential, store evidence
85
121
  and an exact before/after diff, and leave the source database unchanged.
86
122
 
123
+ Code-first Safe Actions do not create authority when a file is edited. Runner
124
+ parses the restricted TypeScript object without importing or executing it,
125
+ compiles it into a digest-addressed disabled canonical draft, and keeps the
126
+ active contract pointer unchanged. Activation is available only in the
127
+ session-token/CSRF-protected localhost Workbench after a matching real staging
128
+ proposal proves `source_database_changed:false`; the operator must confirm the
129
+ complete digest. A proposal remains pinned to the active contract digest it was
130
+ created under, so later activation cannot reinterpret an old approval. Cloud-
131
+ linked projects route activation through Cloud governance instead of the local
132
+ control. No model-facing MCP tool can validate-and-activate, approve, apply, or
133
+ revert.
134
+
87
135
  The local proposal store rejects obvious credential material before persistence:
88
136
  database URLs, bearer tokens, Synapsor runner tokens, private-key blocks, and
89
137
  secret-like fields such as password, token, API key, private key, cookie,
@@ -145,11 +193,13 @@ database identifiers such as `../private`, `id/../../tenant_id`, or
145
193
  `status; DROP TABLE tickets` before adapter execution. Local CLI file paths
146
194
  remain explicit user-provided paths; they are not model-facing authority.
147
195
 
148
- Local review can happen through the CLI or `synapsor-runner ui`. The UI is a localhost
149
- review surface with a per-run session token and CSRF protection for
150
- approve/reject actions. It does not expose raw SQL, database URLs, write
151
- credentials, approval tools, commit tools, or controls that widen reviewed
152
- tables/columns.
196
+ Local review can happen through the CLI or `synapsor-runner ui`. The UI is a
197
+ localhost review surface with a per-run session token and CSRF protection for
198
+ approve/reject and Safe Action draft controls. Activation additionally requires
199
+ a matching source-unchanged preview plus the complete digest. These operator
200
+ controls are not MCP tools. The UI does not expose raw SQL, database URLs, or
201
+ write credentials, and it does not let a model widen reviewed tables, columns,
202
+ scope, policy, or executor authority.
153
203
 
154
204
  Contract lint and tests are review aids rather than a proof of complete
155
205
  security. Capability breadth can still drift as narrow tools accumulate;
@@ -142,6 +142,41 @@ synapsor-runner shadow outcome import \
142
142
  --store ./.synapsor/local.db
143
143
  ```
144
144
 
145
+ For the normal application path, record the trusted outcome directly instead
146
+ of assembling JSONL. `@synapsor/runner/shadow` writes through the same scope,
147
+ effect-shape, and secret checks as the import command:
148
+
149
+ ```js
150
+ import { createShadowOutcomeRecorder } from "@synapsor/runner/shadow";
151
+
152
+ const recorder = createShadowOutcomeRecorder({
153
+ storePath: "./.synapsor/local.db",
154
+ studyId: "sst_support_pilot",
155
+ actor: "support_application",
156
+ source: "support-audit-log",
157
+ });
158
+
159
+ try {
160
+ recorder.record({
161
+ requestId: "request_123",
162
+ tenantId: "acme",
163
+ businessObject: "invoice",
164
+ objectId: "INV-3001",
165
+ disposition: "applied",
166
+ actualEffect: {
167
+ before: { late_fee_cents: 5500 },
168
+ after: { late_fee_cents: 0 },
169
+ patch: { late_fee_cents: 0 },
170
+ },
171
+ });
172
+ } finally {
173
+ recorder.close();
174
+ }
175
+ ```
176
+
177
+ The application owns the truth of this outcome. The helper does not read or
178
+ mutate the source database and cannot approve a shadow proposal.
179
+
145
180
  ## Review The Report
146
181
 
147
182
  ```bash
@@ -161,6 +196,16 @@ denial, unable-to-propose, stale/conflict, unmatched, and unsafe-scope cases.
161
196
  It also includes amount distributions, capability/reason breakdowns, and
162
197
  risk-ranked disagreements.
163
198
 
199
+ The report also shows the explicit trust progression:
200
+
201
+ ```text
202
+ Observe -> Compare -> Manual review -> Suggested bounded policy
203
+ ```
204
+
205
+ It reports an insufficient sample size until the minimum comparable evidence
206
+ exists. A suggested policy remains an inactive review artifact; the report
207
+ never widens or activates the contract.
208
+
164
209
  Close a completed study:
165
210
 
166
211
  ```bash
@@ -187,10 +232,8 @@ synapsor-runner shadow case import \
187
232
  --input ./examples/support-billing-agent/shadow-study/cases.jsonl \
188
233
  --store ./.synapsor/shadow-reference.db
189
234
 
190
- synapsor-runner shadow outcome import \
191
- --study sst_support_reference \
192
- --input ./examples/support-billing-agent/shadow-study/outcomes.jsonl \
193
- --store ./.synapsor/shadow-reference.db
235
+ node ./examples/support-billing-agent/app/record-shadow-outcomes.mjs \
236
+ ./.synapsor/shadow-reference.db sst_support_reference
194
237
 
195
238
  synapsor-runner shadow report \
196
239
  --study sst_support_reference \