@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
package/docs/http-mcp.md CHANGED
@@ -1,44 +1,55 @@
1
1
  # HTTP MCP
2
2
 
3
- Use HTTP MCP when an app, server-side agent, container, or Python/Node process
4
- needs to connect to a long-running Synapsor Runner service.
3
+ Use HTTP when an application, server-side agent, container, or remote MCP client
4
+ connects to a long-running Synapsor Runner. Prefer stdio when one local desktop
5
+ client can launch Runner directly: stdio opens no network socket and needs no
6
+ HTTP credential, TLS setup, OAuth flow, or MCP HTTP session.
5
7
 
6
- Use stdio MCP when a local MCP client such as Claude Desktop, Cursor, or a
7
- local agent tool can launch Synapsor Runner directly.
8
+ Runner provides:
8
9
 
9
- Synapsor Runner has two HTTP modes:
10
+ - `mcp serve-streamable-http`: standard MCP Streamable HTTP using the official
11
+ MCP SDK. This is the normal HTTP endpoint.
12
+ - `mcp serve-http`: a legacy JSON-RPC bridge with `tools/list`, `tools/call`, and
13
+ `resources/read`. It has the same network-channel hardening but no standard
14
+ MCP session and therefore cannot use per-session `http_claims` identity.
10
15
 
11
- - `mcp serve-streamable-http`: spec-compatible MCP Streamable HTTP. Use this
12
- when an MCP SDK/client expects `initialize`, session IDs, POST/GET/DELETE, and
13
- standard HTTP MCP behavior.
14
- - `mcp serve-http`: a lightweight authenticated JSON-RPC bridge for
15
- `tools/list`, `tools/call`, and `resources/read`. Use this when your app wants
16
- simple POST calls or an explicit wrapper around Runner tools.
16
+ ## Authentication Concepts
17
17
 
18
- ## Start Standard Streamable HTTP MCP
18
+ These are separate controls:
19
19
 
20
- ```bash
21
- export SYNAPSOR_RUNNER_HTTP_TOKEN="dev-local-token"
20
+ | Control | Meaning |
21
+ | --- | --- |
22
+ | TLS | Encrypts the network channel and authenticates the server certificate. |
23
+ | mTLS | Also authenticates a client workload certificate. It supplements Bearer auth in Runner. |
24
+ | HTTP Bearer | The HTTP presentation scheme: `Authorization: Bearer <credential>`. It does not imply that the credential is a JWT. |
25
+ | Opaque endpoint token | One high-entropy service credential for loopback or an explicitly single-tenant service. It is not user or tenant identity. |
26
+ | Signed JWT | A short-lived identity-provider-issued access token whose signature, algorithm, issuer, audience/resource, time, scope, tenant, and principal claims Runner verifies. |
27
+ | MCP session ID | Routes requests to initialized MCP state. It is not authentication. |
28
+ | Trusted context | The tenant and principal Runner binds after authentication. Model arguments, arbitrary headers, query strings, forwarded headers, and MCP metadata are never trusted context. |
22
29
 
23
- synapsor-runner mcp serve-streamable-http \
24
- --host 127.0.0.1 \
25
- --port 8766 \
26
- --config ./synapsor.runner.json \
27
- --store ./.synapsor/local.db \
28
- --auth-token-env SYNAPSOR_RUNNER_HTTP_TOKEN
29
- ```
30
+ Runner does not issue endpoint tokens, JWTs, refresh tokens, or end-user
31
+ passwords. An operator generates and distributes an opaque token out of band.
32
+ For a shared deployment, an external identity provider or authorization server
33
+ issues JWT access tokens; Runner is only the protected resource that verifies
34
+ them.
35
+
36
+ ## Deployment Profiles
30
37
 
31
- For OpenAI Agents SDK, expose OpenAI-safe aliases because OpenAI function names
32
- cannot contain dots:
38
+ ### Local loopback with an opaque token
39
+
40
+ Generate at least 32 random bytes. The same environment variable must be
41
+ available to Runner and the authorized local client; the value is never placed
42
+ in config JSON or a generated client file.
33
43
 
34
44
  ```bash
45
+ export SYNAPSOR_RUNNER_HTTP_TOKEN="$(node -e 'process.stdout.write(require("node:crypto").randomBytes(32).toString("base64url"))')"
46
+
35
47
  synapsor-runner mcp serve-streamable-http \
36
48
  --host 127.0.0.1 \
37
49
  --port 8766 \
38
50
  --config ./synapsor.runner.json \
39
51
  --store ./.synapsor/local.db \
40
- --auth-token-env SYNAPSOR_RUNNER_HTTP_TOKEN \
41
- --alias-mode openai
52
+ --auth-token-env SYNAPSOR_RUNNER_HTTP_TOKEN
42
53
  ```
43
54
 
44
55
  Equivalent unified command:
@@ -50,34 +61,57 @@ synapsor-runner mcp serve \
50
61
  --port 8766 \
51
62
  --config ./synapsor.runner.json \
52
63
  --store ./.synapsor/local.db \
53
- --auth-token-env SYNAPSOR_RUNNER_HTTP_TOKEN \
54
- --alias-mode openai
64
+ --auth-token-env SYNAPSOR_RUNNER_HTTP_TOKEN
55
65
  ```
56
66
 
57
- The model sees aliases such as `billing__inspect_invoice`. MCP tool metadata
58
- still includes `synapsor.canonical_tool_name`, and Runner maps calls back to
59
- the canonical Synapsor capability such as `billing.inspect_invoice`. Use
60
- `--alias-mode both` only during migrations where some clients still need
61
- canonical dotted names.
67
+ `--dev-no-auth` is accepted only for an explicit loopback development run. It
68
+ is refused for wildcard, private-network, and public binds.
62
69
 
63
- Defaults:
70
+ ### Remote single-tenant service
64
71
 
65
- ```text
66
- host: 127.0.0.1
67
- port: 8766
68
- auth: bearer token required
69
- cors: disabled
70
- sessions: in-memory
72
+ Declare the service and channel in Runner deployment config. This wiring is not
73
+ part of the portable Synapsor contract:
74
+
75
+ ```json
76
+ {
77
+ "http_security": {
78
+ "deployment": "single_tenant",
79
+ "channel": "trusted_tls_proxy",
80
+ "static_token": {
81
+ "active_env": "SYNAPSOR_RUNNER_HTTP_TOKEN",
82
+ "previous_env": "SYNAPSOR_RUNNER_HTTP_TOKEN_PREVIOUS"
83
+ },
84
+ "allowed_hosts": ["runner.internal.example"],
85
+ "allowed_origins": ["https://agent-console.example"]
86
+ }
87
+ }
71
88
  ```
72
89
 
73
- Use `/mcp` as the MCP endpoint. Cheap process liveness is available at
74
- `/healthz`; dependency readiness is available at `/readyz`. When separately
75
- enabled and authorized, OpenMetrics is available at `/metrics`.
90
+ The supported protected channels are:
91
+
92
+ 1. Runner-owned TLS, selected by supplying certificate and key env references.
93
+ 2. `trusted_tls_proxy`, where a trusted proxy terminates TLS and a firewall or
94
+ private network prevents direct client access to Runner.
95
+ 3. `insecure_http_break_glass`, an authenticated but interceptable emergency
96
+ mode. It emits a security warning and is not appropriate for normal use.
76
97
 
77
- ## TLS And mTLS
98
+ A non-loopback listener with no explicit channel refuses to start before it
99
+ binds. Break glass never disables authentication.
78
100
 
79
- For a non-local long-running service, terminate TLS at a trusted proxy or start
80
- Runner with env-backed PEM material:
101
+ Trusted TLS proxy:
102
+
103
+ ```bash
104
+ synapsor-runner mcp serve-streamable-http \
105
+ --host 0.0.0.0 \
106
+ --port 8766 \
107
+ --config ./synapsor.runner.json \
108
+ --store ./.synapsor/local.db \
109
+ --trusted-tls-proxy \
110
+ --auth-token-env SYNAPSOR_RUNNER_HTTP_TOKEN \
111
+ --previous-auth-token-env SYNAPSOR_RUNNER_HTTP_TOKEN_PREVIOUS
112
+ ```
113
+
114
+ Runner-owned TLS:
81
115
 
82
116
  ```bash
83
117
  export SYNAPSOR_TLS_CERT_PEM="$(cat ./server.crt)"
@@ -93,7 +127,7 @@ synapsor-runner mcp serve-streamable-http \
93
127
  --tls-key-env SYNAPSOR_TLS_KEY_PEM
94
128
  ```
95
129
 
96
- To require client certificates:
130
+ For mTLS, add a protected client CA bundle:
97
131
 
98
132
  ```bash
99
133
  export SYNAPSOR_TLS_CA_PEM="$(cat ./client-ca.crt)"
@@ -110,211 +144,192 @@ synapsor-runner mcp serve-streamable-http \
110
144
  --require-client-cert
111
145
  ```
112
146
 
113
- The CLI reads PEM contents from environment variables and never prints them.
114
- Runner-owned mTLS currently protects the Streamable HTTP MCP boundary. For
115
- app-owned `http_handler` executors, terminate mTLS in your service mesh/proxy
116
- or handler process and keep bearer/signature checks enabled in the handler.
147
+ mTLS authenticates a workload certificate in addition to the Bearer credential.
148
+ It does not turn a shared opaque token into per-user or per-tenant identity.
117
149
 
118
- ## Start The JSON-RPC Bridge
150
+ ### Shared multi-user or multi-tenant service
119
151
 
120
- ```bash
121
- export SYNAPSOR_RUNNER_HTTP_TOKEN="dev-local-token"
152
+ Use verified signed session identity. A static endpoint token is intentionally
153
+ insufficient as trusted tenant/principal authority.
122
154
 
123
- synapsor-runner mcp serve-http \
124
- --host 127.0.0.1 \
125
- --port 8765 \
126
- --config ./synapsor.runner.json \
127
- --store ./.synapsor/local.db \
128
- --auth-token-env SYNAPSOR_RUNNER_HTTP_TOKEN
155
+ ```json
156
+ {
157
+ "trusted_context": {
158
+ "provider": "http_claims",
159
+ "values": {
160
+ "tenant_id_key": "tenant_id",
161
+ "principal_key": "sub"
162
+ }
163
+ },
164
+ "session_auth": {
165
+ "provider": "jwt_asymmetric",
166
+ "algorithms": ["ES256"],
167
+ "jwks_url_env": "SYNAPSOR_SESSION_JWKS_URL",
168
+ "issuer": "https://identity.example",
169
+ "audience": "https://runner.example/mcp",
170
+ "tenant_claim": "tenant_id",
171
+ "principal_claim": "sub",
172
+ "clock_skew_seconds": 30
173
+ },
174
+ "http_security": {
175
+ "deployment": "shared",
176
+ "channel": "trusted_tls_proxy",
177
+ "oauth_resource": {
178
+ "resource": "https://runner.example/mcp",
179
+ "authorization_servers": ["https://identity.example"],
180
+ "scopes_supported": ["synapsor:mcp"],
181
+ "required_scopes": ["synapsor:mcp"],
182
+ "resource_name": "Synapsor Runner"
183
+ },
184
+ "allowed_hosts": ["runner.example"],
185
+ "allowed_origins": ["https://agent-console.example"]
186
+ }
187
+ }
129
188
  ```
130
189
 
131
- Defaults:
190
+ The identity provider publishes the public JWKS URL stored in
191
+ `SYNAPSOR_SESSION_JWKS_URL`. The client obtains a short-lived access token from
192
+ that provider through the provider-supported OAuth/OIDC flow and sends it in the
193
+ Bearer header. Runner then validates, on every request including requests for an
194
+ existing MCP session:
132
195
 
133
- ```text
134
- host: 127.0.0.1
135
- port: 8765
136
- auth: bearer token required
137
- cors: disabled
138
- ```
196
+ - allowed `RS256` or `ES256` algorithm and signature;
197
+ - public `kid` selection and bounded JWKS refresh/cache behavior;
198
+ - exact issuer and audience/resource;
199
+ - expiry, not-before, and configured clock skew;
200
+ - configured required scopes;
201
+ - bounded scalar tenant and principal claims.
139
202
 
140
- Bridge scope: `serve-http` does not implement MCP Streamable HTTP
141
- `initialize`/session behavior. Standard SDK HTTP MCP clients should use
142
- `serve-streamable-http` instead.
203
+ The audience must exactly equal `http_security.oauth_resource.resource`.
204
+ Unverified headers such as `X-Tenant-Id` and all `Forwarded`/`X-Forwarded-*`
205
+ values never become trusted identity.
143
206
 
144
- Startup output prints the URL, config path, store path, and token environment
145
- variable name. It does not print token values or database URLs.
207
+ Runner exposes RFC 9728 protected-resource metadata at the current MCP path:
146
208
 
147
- ## Health Check
148
-
149
- ```bash
150
- curl -i http://127.0.0.1:8766/healthz
151
- curl -i http://127.0.0.1:8765/healthz
152
- curl -i http://127.0.0.1:8766/readyz
209
+ ```text
210
+ /.well-known/oauth-protected-resource/mcp
153
211
  ```
154
212
 
155
- The health endpoint is secret-free:
213
+ An unauthenticated MCP request receives a `401` Bearer challenge containing the
214
+ `resource_metadata` URL. A valid token missing a required scope receives `403`
215
+ with `insufficient_scope`. Runner does not implement a proprietary login or
216
+ refresh-token service.
156
217
 
157
- ```json
158
- {
159
- "ok": true,
160
- "transport": "streamable-http",
161
- "tools": 1,
162
- "mode": "read_only"
163
- }
164
- ```
218
+ ## Opaque Token Rotation
165
219
 
166
- ## List Tools Through The JSON-RPC Bridge
167
-
168
- Unauthorized requests fail:
220
+ Runner accepts one active token and, only when configured, one previous token:
169
221
 
170
222
  ```bash
171
- curl -i \
172
- -H "Content-Type: application/json" \
173
- -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' \
174
- http://127.0.0.1:8765/mcp
175
- ```
176
-
177
- Authorized requests include the bearer token:
178
-
179
- ```bash
180
- curl -i \
181
- -H "Authorization: Bearer dev-local-token" \
182
- -H "Content-Type: application/json" \
183
- -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' \
184
- http://127.0.0.1:8765/mcp
223
+ export SYNAPSOR_RUNNER_HTTP_TOKEN="$(node -e 'process.stdout.write(require("node:crypto").randomBytes(32).toString("base64url"))')"
185
224
  ```
186
225
 
187
- The tool catalog should contain semantic tools such as:
226
+ 1. Move the old active value to the previous secret slot.
227
+ 2. Have the secret manager inject that old value as
228
+ `SYNAPSOR_RUNNER_HTTP_TOKEN_PREVIOUS`, then put a new random value in the
229
+ active slot.
230
+ 3. Restart/roll Runner instances and update authorized clients.
231
+ 4. Remove `previous_env` and its secret after the bounded rollout window.
188
232
 
189
- ```text
190
- billing.inspect_invoice
191
- billing.propose_late_fee_waiver
192
- ```
233
+ Runner never accepts an unbounded key history. Existing MCP sessions are pinned
234
+ to the exact credential used at initialization, so swapping active and previous
235
+ tokens cannot take over another session.
193
236
 
194
- It should not contain:
237
+ ## Origin, Host, CORS, And Request Bounds
195
238
 
196
- ```text
197
- execute_sql
198
- raw_sql
199
- approval tools
200
- commit/apply tools
201
- database URLs
202
- write credentials
203
- model-controlled tenant authority
204
- arbitrary table or column names
205
- ```
239
+ - Every present browser `Origin` must exactly match `allowed_origins`; otherwise
240
+ Runner returns `403`. Native MCP clients may omit `Origin`.
241
+ - Wildcard CORS and the `null` origin are rejected.
242
+ - Every request `Host` must match `allowed_hosts` (or the safe loopback default).
243
+ This is independent of tenant identity and limits DNS-rebinding-style access.
244
+ - Forwarded host and identity headers are never trusted automatically.
245
+ - Request body, header, connection, request-time, session-count, and idle-session
246
+ limits have bounded defaults and can be tightened under `http_security.limits`.
206
247
 
207
- ## Call A Tool Through The JSON-RPC Bridge
208
-
209
- ```bash
210
- curl -i \
211
- -H "Authorization: Bearer dev-local-token" \
212
- -H "Content-Type: application/json" \
213
- -d '{
214
- "jsonrpc": "2.0",
215
- "id": 2,
216
- "method": "tools/call",
217
- "params": {
218
- "name": "billing.inspect_invoice",
219
- "arguments": { "invoice_id": "INV-3001" }
248
+ ```json
249
+ {
250
+ "http_security": {
251
+ "limits": {
252
+ "max_request_bytes": 65536,
253
+ "max_header_bytes": 8192,
254
+ "max_sessions": 500,
255
+ "session_idle_timeout_seconds": 300,
256
+ "request_timeout_ms": 15000,
257
+ "headers_timeout_ms": 5000,
258
+ "keep_alive_timeout_ms": 5000,
259
+ "max_connections": 1000
220
260
  }
221
- }' \
222
- http://127.0.0.1:8765/mcp
261
+ }
262
+ }
223
263
  ```
224
264
 
225
- The response includes scoped data, trusted context, evidence handles, and
226
- `source_database_mutated: false`. The agent still does not receive SQL,
227
- database credentials, or approval/commit authority.
265
+ Session and connection limits are per Runner process. Capability rate limits and
266
+ ledger state become fleet-wide only when the shared PostgreSQL runtime store is
267
+ configured.
228
268
 
229
- ## Read Evidence Or Replay Resources Through The JSON-RPC Bridge
269
+ ## Health, Readiness, And Metrics
230
270
 
231
- Use `resources/read` with a `synapsor://...` handle returned by a tool call:
271
+ `/healthz` is unauthenticated and intentionally minimal:
232
272
 
233
- ```bash
234
- curl -i \
235
- -H "Authorization: Bearer dev-local-token" \
236
- -H "Content-Type: application/json" \
237
- -d '{
238
- "jsonrpc": "2.0",
239
- "id": 3,
240
- "method": "resources/read",
241
- "params": { "uri": "synapsor://evidence/ev_..." }
242
- }' \
243
- http://127.0.0.1:8765/mcp
273
+ ```json
274
+ {
275
+ "ok": true,
276
+ "status": "live",
277
+ "transport": "streamable-http"
278
+ }
244
279
  ```
245
280
 
246
- ## CORS
281
+ `/readyz` reports bounded dependency status codes without credentials or raw
282
+ infrastructure errors. `/metrics` is disabled unless separately configured and
283
+ uses its own authorization; the MCP endpoint credential does not implicitly
284
+ grant metrics access.
247
285
 
248
- CORS is disabled by default. If a local browser app needs access during
249
- development, allow one explicit origin:
286
+ ## Legacy JSON-RPC Bridge
250
287
 
251
288
  ```bash
252
289
  synapsor-runner mcp serve-http \
290
+ --host 127.0.0.1 \
291
+ --port 8765 \
253
292
  --config ./synapsor.runner.json \
254
293
  --store ./.synapsor/local.db \
255
- --cors-origin http://localhost:3000
294
+ --auth-token-env SYNAPSOR_RUNNER_HTTP_TOKEN
256
295
  ```
257
296
 
258
- Do not use wildcard CORS for a model-facing database tool service.
259
-
260
- ## Network Exposure
297
+ The bridge has equivalent remote channel, TLS/mTLS, static-token rotation,
298
+ Origin/Host, and request-bound enforcement. It rejects `http_claims` because it
299
+ does not implement standard MCP sessions. Use Streamable HTTP for a shared
300
+ identity deployment and for standard MCP SDK clients.
261
301
 
262
- Synapsor Runner binds to `127.0.0.1` by default.
263
-
264
- If you explicitly bind to all interfaces:
302
+ An authorized bridge request references the environment value at runtime:
265
303
 
266
304
  ```bash
267
- synapsor-runner mcp serve-http --host 0.0.0.0
305
+ curl -i \
306
+ -H "Authorization: Bearer ${SYNAPSOR_RUNNER_HTTP_TOKEN}" \
307
+ -H "Content-Type: application/json" \
308
+ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' \
309
+ http://127.0.0.1:8765/mcp
268
310
  ```
269
311
 
270
- the CLI prints a warning. Treat this as a production-like service:
271
-
272
- - keep bearer auth enabled;
273
- - use TLS or a trusted reverse proxy;
274
- - prefer private networking;
275
- - add rate limits and request-size limits at the edge;
276
- - do not log request bodies by default;
277
- - rotate the bearer token if it is exposed.
278
-
279
- `--dev-no-auth` is accepted only on `localhost` or `127.0.0.1`. It fails closed
280
- with `--host 0.0.0.0`.
312
+ ## Diagnose Before Serving
281
313
 
282
- ## Trusted Context
283
-
284
- Tenant and principal values must come from trusted configuration such as
285
- environment variables or a server-side session. HTTP request arguments cannot
286
- override trusted fields such as:
287
-
288
- ```text
289
- tenant_id
290
- principal
291
- principal_id
292
- project_id
293
- source_id
294
- allowed_columns
295
- approval_identity
314
+ ```bash
315
+ synapsor-runner doctor \
316
+ --config ./synapsor.runner.json \
317
+ --transport streamable-http \
318
+ --host 127.0.0.1 \
319
+ --auth-token-env SYNAPSOR_RUNNER_HTTP_TOKEN
296
320
  ```
297
321
 
298
- Use `read_only` mode first. Proposal/review mode should use a separate trusted
299
- write path and a separate write credential. The model-facing HTTP MCP endpoint
300
- must not receive write credentials.
301
-
302
- ## OpenAI Agents SDK
303
-
304
- See:
305
-
306
- ```text
307
- examples/openai-agents-http/
308
- examples/openai-agents-stdio/
309
- ```
322
+ For a remote proxy deployment add `--host 0.0.0.0 --trusted-tls-proxy`; for
323
+ Runner-owned TLS add the same TLS env-name flags used at startup. Doctor reports
324
+ the transport, bind scope, channel, auth/identity mode, issuer/audience/resource,
325
+ key readiness, token strength/rotation, Origin/Host policy, limits, rate-limit
326
+ scope, and database isolation assurance without printing credential values.
310
327
 
311
- Both examples use the MCP client integration from the OpenAI Agents SDK when it
312
- is available. The stdio example launches Runner as a child process. The HTTP
313
- example connects to `synapsor-runner mcp serve-streamable-http` through
314
- `MCPServerStreamableHttp`. Use the JSON-RPC bridge only when you intentionally
315
- want a small app-owned wrapper instead of standard HTTP MCP.
328
+ ## Model-Facing Boundary
316
329
 
317
- The boundary is the same in both modes: the agent calls a semantic Synapsor
318
- tool, not raw SQL. OpenAI-facing examples use `--alias-mode openai` so
319
- the model sees OpenAI-valid aliases while Runner preserves canonical Synapsor
320
- tool names in metadata.
330
+ HTTP transport does not change authority. MCP clients receive reviewed semantic
331
+ capabilities, never raw SQL, database credentials, endpoint credentials,
332
+ approval/apply/reconcile/revert tools, token minting, or token refresh. Keep
333
+ least-privilege DB roles and PostgreSQL RLS or tenant-bound credentials under
334
+ Runner where practical; transport authentication does not replace database or
335
+ operator authorization.
@@ -41,7 +41,9 @@ enterprise SLA.
41
41
  - Primary-key guard.
42
42
  - Tenant guard.
43
43
  - Allowed-column validation.
44
- - Version-column or explicit weak row-hash conflict guard.
44
+ - Exact version-column conflict guards, plus an explicit legacy
45
+ `CONFLICT GUARD WEAK ROW HASH ACKNOWLEDGED` escape hatch for ordinary
46
+ single-row source-DB UPDATE only.
45
47
  - Idempotency receipts.
46
48
  - Named local trusted contexts for capability configs.
47
49
  - Capability recipes that generate reviewed starter configs.
@@ -134,7 +136,12 @@ Do not describe external approval as merge.
134
136
 
135
137
  ## Weak Conflict Guards
136
138
 
137
- A version/timestamp column is the preferred conflict guard. A weak row-hash guard can be acknowledged for local/dev use, but it should not be presented as equivalent to a durable version column.
139
+ A version/timestamp column is the preferred conflict guard. UPDATE authoring
140
+ fails if no guard is declared. A weak row-hash guard can be selected only with
141
+ the reviewer-visible `CONFLICT GUARD WEAK ROW HASH ACKNOWLEDGED` clause for a
142
+ narrow ordinary single-row source-DB UPDATE. It hashes the captured projection,
143
+ may miss concurrent changes outside that projection, and must not be presented
144
+ as equivalent to a durable version column.
138
145
 
139
146
  Runner-ledger UPDATE and DELETE require an exact guard; UPDATE must advance it
140
147
  inside the source transaction. INSERT requires a reviewed source-unique dedup
@@ -32,11 +32,12 @@ Current local-mode foundation:
32
32
 
33
33
  Runtime requirement:
34
34
 
35
- The stable package requires Node >= 22.5.0 because the local evidence/replay
36
- ledger uses Node's `node:sqlite` runtime. The published package declares that
37
- engine requirement and the CLI exits early with a clear message on older Node
38
- versions. The Docker-backed demo remains the recommended path when you do not
39
- want to change your host Node version.
35
+ The stable package requires Node >= 22.13.0 because the local evidence/replay
36
+ ledger uses Node's unflagged `node:sqlite` runtime. Earlier Node 22 releases
37
+ require an experimental flag and are not supported. The published package
38
+ declares that engine requirement and the CLI exits early with a clear message
39
+ on older Node versions. The Docker-backed demo remains the recommended path
40
+ when you do not want to change your host Node version.
40
41
 
41
42
  ## Initialize a config
42
43
 
@@ -84,7 +85,7 @@ Use Streamable HTTP when your app/server agent connects through a standard HTTP
84
85
  MCP client:
85
86
 
86
87
  ```bash
87
- export SYNAPSOR_RUNNER_HTTP_TOKEN="dev-local-token"
88
+ export SYNAPSOR_RUNNER_HTTP_TOKEN="$(node -e 'process.stdout.write(require("node:crypto").randomBytes(32).toString("base64url"))')"
88
89
 
89
90
  npx -y -p @synapsor/runner synapsor-runner mcp serve-streamable-http \
90
91
  --config ./synapsor.runner.json \
@@ -93,7 +94,10 @@ npx -y -p @synapsor/runner synapsor-runner mcp serve-streamable-http \
93
94
  ```
94
95
 
95
96
  Streamable HTTP defaults to `127.0.0.1:8766`, requires bearer auth by default,
96
- and should run behind private networking/TLS before production-like exposure.
97
+ and uses an opaque endpoint token only as shared service access, not user or
98
+ tenant identity. Non-loopback serving requires direct TLS, an explicitly
99
+ trusted TLS proxy, or authenticated emergency break glass; shared deployments
100
+ require verified signed claims.
97
101
  Use `synapsor-runner mcp serve-http` only when you explicitly want the smaller
98
102
  JSON-RPC bridge. Details: [HTTP MCP](http-mcp.md).
99
103