@synapsor/runner 1.5.3 → 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.
- package/CHANGELOG.md +44 -2
- package/README.md +20 -12
- package/dist/cli.d.ts.map +1 -1
- package/dist/runner.mjs +2332 -501
- package/dist/runtime.mjs +788 -148
- package/dist/shadow.mjs +101 -1
- package/docs/capability-authoring.md +6 -1
- package/docs/cloud-mode.md +18 -0
- package/docs/database-enforced-scope.md +8 -0
- package/docs/dsl-reference.md +45 -4
- package/docs/getting-started-own-database.md +6 -3
- package/docs/guarded-crud-writeback.md +10 -1
- package/docs/http-mcp.md +222 -207
- package/docs/limitations.md +9 -2
- package/docs/local-mode.md +5 -2
- package/docs/mcp-client-setup.md +24 -11
- package/docs/mcp-clients.md +10 -4
- package/docs/openai-agents-sdk.md +16 -2
- package/docs/production.md +72 -7
- package/docs/release-notes.md +44 -3
- package/docs/runner-bundles.md +7 -2
- package/docs/runner-config-reference.md +96 -6
- package/docs/running-a-runner-fleet.md +42 -13
- package/docs/security-boundary.md +38 -3
- package/docs/store-lifecycle.md +93 -5
- package/examples/openai-agents-http/README.md +10 -4
- package/examples/openai-agents-http/agent.py +2 -2
- package/examples/runner-fleet/Dockerfile +7 -2
- package/examples/runner-fleet/README.md +11 -7
- package/examples/runner-fleet/docker-compose.yml +4 -4
- package/examples/runner-fleet/mint-dev-token.mjs +1 -1
- package/examples/runner-fleet/start-tls-runner.sh +21 -0
- package/examples/runner-fleet/synapsor.runner.json +27 -1
- package/examples/support-plan-credit/README.md +6 -1
- package/examples/support-plan-credit/mcp-client-examples/generic-streamable-http.json +4 -1
- package/examples/support-plan-credit/mcp-client-examples/openai-agents-streamable-http.ts +4 -0
- package/examples/support-plan-credit/synapsor.contract.json +0 -3
- package/package.json +1 -1
- package/schemas/synapsor.runner.schema.json +80 -0
package/docs/mcp-clients.md
CHANGED
|
@@ -115,19 +115,25 @@ metadata preserves the canonical dotted Synapsor capability name.
|
|
|
115
115
|
## Streamable HTTP
|
|
116
116
|
|
|
117
117
|
```bash
|
|
118
|
+
export SYNAPSOR_RUNNER_HTTP_TOKEN="$(node -e 'process.stdout.write(require("node:crypto").randomBytes(32).toString("base64url"))')"
|
|
119
|
+
|
|
118
120
|
synapsor-runner mcp serve \
|
|
119
121
|
--transport streamable-http \
|
|
120
122
|
--alias-mode openai \
|
|
121
123
|
--host 127.0.0.1 \
|
|
122
124
|
--port 8766 \
|
|
123
125
|
--config ./synapsor.runner.json \
|
|
124
|
-
--store ./.synapsor/local.db
|
|
126
|
+
--store ./.synapsor/local.db \
|
|
127
|
+
--auth-token-env SYNAPSOR_RUNNER_HTTP_TOKEN
|
|
125
128
|
```
|
|
126
129
|
|
|
127
130
|
Connect a standard Streamable HTTP MCP client to
|
|
128
|
-
`http://127.0.0.1:8766/mcp
|
|
129
|
-
|
|
130
|
-
|
|
131
|
+
`http://127.0.0.1:8766/mcp` with `Authorization: Bearer` loaded from the same
|
|
132
|
+
protected environment value. Keep it on loopback for local development. A
|
|
133
|
+
non-loopback listener refuses to bind without direct TLS, an explicitly trusted
|
|
134
|
+
TLS proxy, or authenticated emergency break glass. Shared deployments require
|
|
135
|
+
identity-provider-issued signed claims. Follow [HTTP MCP](http-mcp.md) and the
|
|
136
|
+
[production guide](production.md).
|
|
131
137
|
|
|
132
138
|
Use `--alias-mode openai` for the OpenAI Agents SDK. Omit it for clients that
|
|
133
139
|
accept canonical dotted names, or use `--alias-mode both` only during a planned
|
|
@@ -8,7 +8,7 @@ surface.
|
|
|
8
8
|
## Start Runner
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
export SYNAPSOR_RUNNER_HTTP_TOKEN="
|
|
11
|
+
export SYNAPSOR_RUNNER_HTTP_TOKEN="$(node -e 'process.stdout.write(require("node:crypto").randomBytes(32).toString("base64url"))')"
|
|
12
12
|
|
|
13
13
|
npx -y -p @synapsor/runner synapsor-runner mcp serve-streamable-http \
|
|
14
14
|
--config ./synapsor.runner.json \
|
|
@@ -17,6 +17,11 @@ npx -y -p @synapsor/runner synapsor-runner mcp serve-streamable-http \
|
|
|
17
17
|
--alias-mode openai
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
For this loopback/single-service example, the operator provisions that same
|
|
21
|
+
opaque value to the Runner process and the Python client through protected
|
|
22
|
+
environment injection. HTTP `Bearer` describes how the credential is sent; it
|
|
23
|
+
does not make the opaque value a JWT, and Runner does not issue it.
|
|
24
|
+
|
|
20
25
|
`--alias-mode openai` is important because OpenAI function/tool names cannot
|
|
21
26
|
contain dots. Runner exposes names such as `billing__inspect_invoice` to the
|
|
22
27
|
model and maps calls back to canonical Synapsor capability names such as
|
|
@@ -28,7 +33,8 @@ model and maps calls back to canonical Synapsor capability names such as
|
|
|
28
33
|
npx -y -p @synapsor/runner synapsor-runner mcp client-config \
|
|
29
34
|
--client openai-agents \
|
|
30
35
|
--config ./synapsor.runner.json \
|
|
31
|
-
--store ./.synapsor/local.db
|
|
36
|
+
--store ./.synapsor/local.db \
|
|
37
|
+
--client-access-token-env SYNAPSOR_RUNNER_HTTP_TOKEN
|
|
32
38
|
```
|
|
33
39
|
|
|
34
40
|
The generated output includes:
|
|
@@ -41,6 +47,14 @@ The generated output includes:
|
|
|
41
47
|
It does not include database URLs, passwords, write credentials, API keys, or
|
|
42
48
|
bearer token values.
|
|
43
49
|
|
|
50
|
+
For a shared deployment, configure `http_claims`, asymmetric `session_auth`, and
|
|
51
|
+
`http_security.oauth_resource`, then use a separate env name such as
|
|
52
|
+
`SYNAPSOR_MCP_ACCESS_TOKEN`. Your configured identity provider issues that
|
|
53
|
+
short-lived token for the protected-resource audience; Runner validates it on
|
|
54
|
+
every request but never stores end-user passwords or issues/refreshes tokens.
|
|
55
|
+
The generated client URL is the configured HTTPS protected resource. See
|
|
56
|
+
[HTTP MCP](http-mcp.md) for the complete profile and TLS/proxy requirements.
|
|
57
|
+
|
|
44
58
|
## Sanity Check
|
|
45
59
|
|
|
46
60
|
Before giving the agent a real task, ask it to inspect one known object and
|
package/docs/production.md
CHANGED
|
@@ -421,6 +421,36 @@ synapsor-runner activity search --proposal wrp_... --store ./.synapsor/local.db
|
|
|
421
421
|
|
|
422
422
|
## Deployment Recipes
|
|
423
423
|
|
|
424
|
+
### Networked MCP boundary
|
|
425
|
+
|
|
426
|
+
Prefer stdio when one local client can launch Runner. For Streamable HTTP,
|
|
427
|
+
choose the deployment profile before binding:
|
|
428
|
+
|
|
429
|
+
- loopback: authenticated opaque endpoint token by default;
|
|
430
|
+
- remote single-tenant: direct TLS or an explicitly trusted TLS proxy, plus one
|
|
431
|
+
high-entropy rotated service token;
|
|
432
|
+
- shared multi-user/tenant: direct TLS or trusted proxy, asymmetric signed JWT
|
|
433
|
+
sessions, exact issuer/audience/resource, verified tenant/principal claims,
|
|
434
|
+
and RFC 9728 protected-resource metadata.
|
|
435
|
+
|
|
436
|
+
An opaque token is generated by the operator and provisioned to server and
|
|
437
|
+
authorized client through environment/secret-manager injection. Runner does not
|
|
438
|
+
issue it. In a shared deployment, the configured external identity provider
|
|
439
|
+
issues short-lived access tokens; Runner verifies them but does not store user
|
|
440
|
+
passwords or issue refresh tokens. See [HTTP MCP](http-mcp.md) before exposing a
|
|
441
|
+
listener outside loopback.
|
|
442
|
+
|
|
443
|
+
Run the transport-specific doctor check before serving:
|
|
444
|
+
|
|
445
|
+
```bash
|
|
446
|
+
synapsor-runner doctor \
|
|
447
|
+
--config ./synapsor.runner.json \
|
|
448
|
+
--transport streamable-http \
|
|
449
|
+
--host 0.0.0.0 \
|
|
450
|
+
--trusted-tls-proxy \
|
|
451
|
+
--auth-token-env SYNAPSOR_RUNNER_HTTP_TOKEN
|
|
452
|
+
```
|
|
453
|
+
|
|
424
454
|
### Docker Compose Shape
|
|
425
455
|
|
|
426
456
|
Use Compose to run the MCP server next to your app and database network. Keep
|
|
@@ -436,14 +466,20 @@ export SYNAPSOR_SECRET_MAP='{
|
|
|
436
466
|
}'
|
|
437
467
|
|
|
438
468
|
synapsor-runner mcp serve-streamable-http \
|
|
469
|
+
--host 0.0.0.0 \
|
|
439
470
|
--config ./synapsor.runner.json \
|
|
440
471
|
--store ./.synapsor/local.db \
|
|
472
|
+
--trusted-tls-proxy \
|
|
473
|
+
--auth-token-env SYNAPSOR_RUNNER_HTTP_TOKEN \
|
|
441
474
|
--secrets-provider aws-secretsmanager-cli \
|
|
442
475
|
--secret-map-env SYNAPSOR_SECRET_MAP
|
|
443
476
|
```
|
|
444
477
|
|
|
445
478
|
The AWS provider shells out to `aws secretsmanager get-secret-value`; the runner
|
|
446
|
-
logs only how many target env vars were loaded or skipped.
|
|
479
|
+
logs only how many target env vars were loaded or skipped. This command assumes
|
|
480
|
+
`http_security.deployment: single_tenant`, an exact `allowed_hosts` list, and a
|
|
481
|
+
TLS proxy on a private hop. Use asymmetric claim-bound identity instead for a
|
|
482
|
+
shared user/tenant service.
|
|
447
483
|
|
|
448
484
|
```yaml
|
|
449
485
|
services:
|
|
@@ -453,22 +489,30 @@ services:
|
|
|
453
489
|
command: >
|
|
454
490
|
sh -lc "npm install -g @synapsor/runner &&
|
|
455
491
|
synapsor-runner up --review --config /config/synapsor.runner.json
|
|
456
|
-
--store /data/local.db --host 0.0.0.0 --port 8766
|
|
492
|
+
--store /data/local.db --host 0.0.0.0 --port 8766
|
|
493
|
+
--trusted-tls-proxy --auth-token-env SYNAPSOR_RUNNER_HTTP_TOKEN"
|
|
457
494
|
environment:
|
|
458
495
|
SYNAPSOR_DATABASE_READ_URL: ${SYNAPSOR_DATABASE_READ_URL}
|
|
459
496
|
SYNAPSOR_DATABASE_WRITE_URL: ${SYNAPSOR_DATABASE_WRITE_URL}
|
|
460
497
|
SYNAPSOR_TENANT_ID: ${SYNAPSOR_TENANT_ID}
|
|
461
498
|
SYNAPSOR_PRINCIPAL: ${SYNAPSOR_PRINCIPAL}
|
|
499
|
+
SYNAPSOR_RUNNER_HTTP_TOKEN: ${SYNAPSOR_RUNNER_HTTP_TOKEN}
|
|
462
500
|
volumes:
|
|
463
501
|
- ./synapsor.runner.json:/config/synapsor.runner.json:ro
|
|
464
502
|
- synapsor-runner-data:/data
|
|
465
|
-
|
|
466
|
-
- "8766
|
|
503
|
+
expose:
|
|
504
|
+
- "8766"
|
|
467
505
|
|
|
468
506
|
volumes:
|
|
469
507
|
synapsor-runner-data:
|
|
470
508
|
```
|
|
471
509
|
|
|
510
|
+
This shape deliberately does not publish the Runner port to the host. Put a
|
|
511
|
+
TLS-terminating proxy on the same private container network, publish only that
|
|
512
|
+
proxy, preserve the reviewed external Host, and prevent direct client access to
|
|
513
|
+
Runner. If Runner itself owns TLS, replace `--trusted-tls-proxy` with protected
|
|
514
|
+
`--tls-cert-env`/`--tls-key-env` PEM references.
|
|
515
|
+
|
|
472
516
|
### systemd Shape
|
|
473
517
|
|
|
474
518
|
```ini
|
|
@@ -491,10 +535,20 @@ PrivateTmp=true
|
|
|
491
535
|
WantedBy=multi-user.target
|
|
492
536
|
```
|
|
493
537
|
|
|
494
|
-
Put database credentials
|
|
495
|
-
manager, not in
|
|
538
|
+
Put database credentials and the random loopback endpoint token in
|
|
539
|
+
`/etc/synapsor-runner.env` or your system secret manager, not in
|
|
540
|
+
`synapsor.runner.json`. The systemd command binds loopback, so a local trusted
|
|
541
|
+
proxy may connect to it without exposing cleartext Bearer traffic on the
|
|
542
|
+
network.
|
|
496
543
|
|
|
497
|
-
## TLS And SSL
|
|
544
|
+
## TLS, mTLS, And Database SSL
|
|
545
|
+
|
|
546
|
+
For MCP HTTP, Runner-owned TLS reads certificate/key PEM only from named
|
|
547
|
+
environment variables. Optional mTLS verifies client certificates against an
|
|
548
|
+
explicit CA and supplements Bearer authentication; it does not replace
|
|
549
|
+
per-session tenant/principal identity in a shared service. A trusted proxy must
|
|
550
|
+
be protected from direct bypass and must not inject forwarded tenant/principal
|
|
551
|
+
headers as authority.
|
|
498
552
|
|
|
499
553
|
For staging or production-like databases:
|
|
500
554
|
|
|
@@ -507,6 +561,17 @@ For staging or production-like databases:
|
|
|
507
561
|
For disposable local Docker fixtures, `sslmode=disable` is acceptable when the
|
|
508
562
|
database is bound to loopback and contains only test data.
|
|
509
563
|
|
|
564
|
+
Rotate credentials independently:
|
|
565
|
+
|
|
566
|
+
- opaque endpoint token: active plus at most one previous env during rollout;
|
|
567
|
+
- HS256 session key: active plus one previous key, only for controlled use;
|
|
568
|
+
- asymmetric JWT key: publish a new `kid`, honor bounded JWKS cache/cooldown,
|
|
569
|
+
then retire the old public key after clients rotate;
|
|
570
|
+
- TLS certificate and mTLS client CA: roll protected PEM material and restart
|
|
571
|
+
instances without changing tenant identity;
|
|
572
|
+
- database and handler credentials: rotate their separate env/secret-manager
|
|
573
|
+
entries without reusing an MCP or Cloud credential.
|
|
574
|
+
|
|
510
575
|
## Health And Doctor
|
|
511
576
|
|
|
512
577
|
Run these before exposing tools to an agent:
|
package/docs/release-notes.md
CHANGED
|
@@ -10,7 +10,48 @@ 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.
|
|
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)
|
|
14
55
|
|
|
15
56
|
### Intent to Safe Action
|
|
16
57
|
|
|
@@ -37,8 +78,8 @@ for the Synapsor Cloud CLI.
|
|
|
37
78
|
- The first two README screens, public website, article, discovery routes, and
|
|
38
79
|
36-second evidence-backed demo now lead with the own-project Data PR path.
|
|
39
80
|
|
|
40
|
-
|
|
41
|
-
packages
|
|
81
|
+
Published package version: `@synapsor/runner@1.5.3`. Spec, DSL, and Cloud CLI
|
|
82
|
+
packages were unchanged.
|
|
42
83
|
|
|
43
84
|
## 1.5.2 (prepared, not published)
|
|
44
85
|
|
package/docs/runner-bundles.md
CHANGED
|
@@ -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
|
|
71
|
-
|
|
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
|
-
|
|
216
|
-
session
|
|
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": "
|
|
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
|
|
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": "
|
|
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
|
|
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
|
|
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": "
|
|
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
|
|
333
|
-
|
|
334
|
-
|
|
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
|
|
337
|
-
|
|
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
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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.
|
|
@@ -39,17 +39,52 @@ The model does not receive:
|
|
|
39
39
|
- contract activation tools;
|
|
40
40
|
- approval tools;
|
|
41
41
|
- commit/writeback tools;
|
|
42
|
+
- MCP endpoint tokens, JWTs, refresh tokens, client secrets, or TLS keys;
|
|
42
43
|
- trusted tenant or principal authority as ordinary model arguments.
|
|
43
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
|
+
|
|
44
75
|
Reviewed aggregate tools expose one scalar only. Their function, column,
|
|
45
76
|
tenant key, optional equality selection, and minimum-group threshold are fixed
|
|
46
77
|
in the contract; the model receives no predicate arguments or member rows.
|
|
47
78
|
Suppression reduces single-record inference but does not replace statistical
|
|
48
79
|
privacy review. See [Bounded Aggregate Reads](aggregate-reads.md).
|
|
49
80
|
|
|
50
|
-
Trusted context comes from
|
|
51
|
-
|
|
52
|
-
|
|
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.
|
|
53
88
|
|
|
54
89
|
With a shared credential, these checks are application-level enforcement and
|
|
55
90
|
depend on Runner's fixed predicate implementation. Optional PostgreSQL RLS mode
|