@synapsor/runner 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/README.md CHANGED
@@ -6,6 +6,16 @@ no-database demo, wire your database, then read deeper concepts.
6
6
 
7
7
  ## 01 Quickstart
8
8
 
9
+ - Audit a deliberately risky database MCP surface without cloning the repo or
10
+ connecting a database:
11
+
12
+ ```bash
13
+ npx -y @synapsor/runner audit --example dangerous-db-mcp
14
+ ```
15
+
16
+ Then use [MCP Audit](mcp-audit.md) to inspect your own tool manifest, remote
17
+ MCP endpoint, or stdio server. The audit is useful independently of whether
18
+ you adopt Runner.
9
19
  - [README](../README.md): audit-first proof, no-database demo, safety diagram,
10
20
  and the shortest own-database path.
11
21
  - [Troubleshooting First Run](troubleshooting-first-run.md): common first-run
@@ -98,9 +108,12 @@ no-database demo, wire your database, then read deeper concepts.
98
108
 
99
109
  - [Current Scope](current-scope.md): compact v0.1 scope summary.
100
110
  - [Current Limitations](limitations.md): intentional safety limits.
101
- - [Production-Candidate Guide](production.md): single-node OSS deployment
102
- scope, database roles, receipt grants, local ledger backup, restart behavior,
103
- Docker/systemd shapes, TLS, and release-gate expectations.
111
+ - [Production-Candidate Guide](production.md): single-node and bounded-fleet
112
+ OSS deployment scope, database roles, receipt grants, restart
113
+ behavior, Docker/systemd shapes, TLS, and release-gate expectations.
114
+ - [Running A Small Runner Fleet](running-a-runner-fleet.md): tested two-Runner
115
+ topology, claim-bound sessions, pools, fleet rate limits, quorum, metrics,
116
+ dead letters, backup/restore/retention, and kill/recovery evidence.
104
117
  - [Cloud Mode](cloud-mode.md): what stays local and what Cloud-linked mode adds.
105
118
  - [OSS Runner Vs Synapsor Cloud](oss-vs-cloud.md): detailed product and
106
119
  operational boundary.
@@ -136,6 +136,7 @@ reviewed runner JSON capabilities. Current parity:
136
136
  | proposal `transition_guards` | `TRANSITION status ALLOW pending -> approved\|rejected` or `TRANSITION status FROM current_status ALLOW open -> closed` | 0.1.8 | Values are state strings; use `|` for multiple target states. |
137
137
  | proposal `conflict_guard` | `CONFLICT GUARD updated_at` | 0.1 | If omitted, DSL emits an explicit weak-guard acknowledgement. Prefer a real row-version column. |
138
138
  | proposal `approval` | `APPROVAL ROLE billing_lead` | 0.1 | Local mode records the required role; enforcement is still outside the model-facing MCP tool. |
139
+ | proposal `approval.required_approvals` | `REQUIRE 2 APPROVALS` | 1.1 | Optional 1..10 distinct-reviewer quorum; defaults to 1. |
139
140
  | proposal `writeback` | `WRITEBACK DIRECT SQL`, `WRITEBACK APP HANDLER EXECUTOR name`, `WRITEBACK CLOUD WORKER`, `WRITEBACK NONE` | 0.1.7 | Handler URLs/tokens stay in `synapsor.runner.json`; contracts carry only the handler name. |
140
141
  | evidence options | `REQUIRE EVIDENCE` | 0.1 | Detailed evidence sources/handle prefixes are not expressible in DSL yet; use embedded JSON or generated contract JSON for those. |
141
142
 
@@ -285,7 +286,7 @@ surface.
285
286
  "late_fee_cents": { "minimum": 0, "maximum": 10000 }
286
287
  },
287
288
  "conflict_guard": { "column": "updated_at" },
288
- "approval": { "mode": "human", "required_role": "billing_lead" }
289
+ "approval": { "mode": "human", "required_role": "billing_lead", "required_approvals": 2 }
289
290
  }
290
291
  ```
291
292
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  The canonical scope page is [Current Limitations](limitations.md).
4
4
 
5
- Current `1.0` scope:
5
+ Current `1.1` scope:
6
6
 
7
7
  - local semantic MCP tools for Postgres/MySQL-backed business actions;
8
8
  - schema inspection and guided config generation;
@@ -12,6 +12,9 @@ Current `1.0` scope:
12
12
  - app-owned `http_handler` and `command_handler` executors for richer approved
13
13
  business transactions;
14
14
  - stdio MCP, Streamable HTTP MCP, and a small JSON-RPC bridge.
15
+ - a bounded small-fleet shape with asymmetric claim-bound sessions, shared
16
+ Postgres review state, source pools, fleet rate limits, readiness, protected
17
+ metrics, verified quorum review, dead letters, and backup/restore/retention;
15
18
 
16
19
  Stable `1.x` compatibility covers the documented `synapsor-runner` binary,
17
20
  config schema version `1`, result envelope v2 with v1 opt-out, stdio/Streamable
@@ -26,4 +29,5 @@ Out of scope:
26
29
  - generic multi-row SQL writeback;
27
30
  - physical branching of external Postgres/MySQL;
28
31
  - self-hosted Synapsor Cloud;
32
+ - unbounded or multi-region shared-ledger scale and a managed Runner fleet;
29
33
  - production SLA or compliance certification.
@@ -148,6 +148,7 @@ patched column. Values may be identifiers or quoted strings.
148
148
 
149
149
  ```sql
150
150
  APPROVAL ROLE billing_lead
151
+ REQUIRE 2 APPROVALS
151
152
  AUTO APPROVE WHEN amount_cents <= 2500
152
153
  LIMIT 20 PER DAY
153
154
  LIMIT TOTAL 100000 PER DAY
@@ -158,6 +159,12 @@ patched column. Values may be identifiers or quoted strings.
158
159
  approval identity is operator-provided; enterprise identity/RBAC belongs to the
159
160
  Cloud boundary.
160
161
 
162
+ `REQUIRE n APPROVALS` is optional and accepts 1 through 10. It compiles to the
163
+ canonical `approval.required_approvals` field. The default is 1. Each slot
164
+ requires a distinct verified subject; duplicate subjects fail with
165
+ `APPROVER_ALREADY_COUNTED`. Apply/workers remain blocked until `n/N`, rejection
166
+ is terminal, and policy auto-approval is deferred when `n > 1`.
167
+
161
168
  `AUTO APPROVE WHEN field <= non_negative_integer` is supported only for a
162
169
  numeric patched field and must follow `APPROVAL ROLE`. Its maximum cannot exceed
163
170
  the field's `BOUND`. Policy approval still does not apply the write.
package/docs/http-mcp.md CHANGED
@@ -70,7 +70,9 @@ cors: disabled
70
70
  sessions: in-memory
71
71
  ```
72
72
 
73
- Use `/mcp` as the MCP endpoint. Health is available at `/healthz`.
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`.
74
76
 
75
77
  ## TLS And mTLS
76
78
 
@@ -147,6 +149,7 @@ variable name. It does not print token values or database URLs.
147
149
  ```bash
148
150
  curl -i http://127.0.0.1:8766/healthz
149
151
  curl -i http://127.0.0.1:8765/healthz
152
+ curl -i http://127.0.0.1:8766/readyz
150
153
  ```
151
154
 
152
155
  The health endpoint is secret-free:
@@ -1,6 +1,7 @@
1
1
  # Limitations
2
2
 
3
- Synapsor Runner is intentionally narrow in the current alpha.
3
+ Synapsor Runner is intentionally narrow. Version 1.1 adds a bounded small-fleet
4
+ shape; it does not claim Synapsor Cloud scale or an enterprise SLA.
4
5
 
5
6
  ## Supported
6
7
 
@@ -8,7 +9,13 @@ Synapsor Runner is intentionally narrow in the current alpha.
8
9
  - Local read and proposal tools.
9
10
  - Local SQLite evidence/proposal/query-audit/replay store by default.
10
11
  - Optional shared Postgres proposal/evidence/replay runtime store for MCP serving.
11
- - Human approval through CLI commands.
12
+ - Asymmetric claim-bound Streamable HTTP sessions and explicit readiness.
13
+ - Native Postgres/MySQL source pools and operational/fleet-wide rate limits.
14
+ - Verified operator approval through CLI, optional distinct-reviewer quorum,
15
+ and shared local review UI reads.
16
+ - Separately protected scrapeable metrics and dead-letter recovery commands.
17
+ - Shared-ledger backup/digest verification, clean restore, and
18
+ archive-before-retention.
12
19
  - Public protocol objects:
13
20
  - `synapsor.change-set.v1`
14
21
  - `synapsor.writeback-job.v1`
@@ -56,7 +63,8 @@ truth for the model-facing tools.
56
63
  - Model-callable approval or commit tools.
57
64
  - Generic MCP firewall behavior.
58
65
  - Prompt-injection prevention.
59
- - High availability, SLA, compliance certification, or production support guarantee.
66
+ - Unbounded/high-throughput or multi-region ledger scale.
67
+ - Managed fleet, SLA, compliance certification, or production support guarantee.
60
68
 
61
69
  ## Important External Database Semantics
62
70
 
@@ -78,9 +86,15 @@ It does not mean external Postgres/MySQL time travel. Runner cannot reconstruct
78
86
  arbitrary historical rows that were never captured as evidence, and it does not
79
87
  provide `AS OF` queries over an external source.
80
88
 
81
- Local search is single-node SQLite search over the local runner store. It is
82
- useful for local/dev/staging usage. It is not a hosted central evidence ledger,
83
- not cross-runner aggregation, not RBAC/SSO, and not compliance retention.
89
+ Local search uses SQLite by default. In `runtime_store` mode, CLI/UI reads can
90
+ inspect one bounded shared Postgres ledger across a small fleet. This is still
91
+ not a hosted central evidence service, organization RBAC/SSO, compliance
92
+ retention system, or unbounded search engine. Each bridge operation serializes
93
+ through an advisory lock and fails above configured `max_entries`.
94
+
95
+ Only homogeneous 1.1 fleet operation is currently verified. Mixed 1.0/1.1
96
+ rolling operation is not claimed. See [Running A Small Runner
97
+ Fleet](running-a-runner-fleet.md).
84
98
 
85
99
  Use this wording:
86
100
 
@@ -127,6 +127,27 @@ Then keep only local wiring in `synapsor.runner.json`.
127
127
  }
128
128
  ```
129
129
 
130
+ ## 1.1 Approval Quorum
131
+
132
+ Existing contracts remain valid and require one approval. To require distinct
133
+ verified reviewers, add the optional canonical field:
134
+
135
+ ```json
136
+ {
137
+ "proposal": {
138
+ "approval": {
139
+ "mode": "human",
140
+ "required_role": "billing_lead",
141
+ "required_approvals": 2
142
+ }
143
+ }
144
+ }
145
+ ```
146
+
147
+ The equivalent DSL is `REQUIRE 2 APPROVALS` after `APPROVAL ROLE
148
+ billing_lead`. Runner 1.0 does not understand this new optional semantic field;
149
+ use Runner/spec/DSL 1.1 together. Omitted fields preserve 1.0 behavior.
150
+
130
151
  ## Validate
131
152
 
132
153
  ```bash
@@ -24,7 +24,7 @@ and investigation surfaces for enabled design-partner deployments.
24
24
  | Writeback | Guarded one-row update or app-owned executor | Cloud-linked jobs with local execution; managed production orchestration is future work |
25
25
  | MCP risk audit | Static local audit | Organization-wide continuous audit is future work |
26
26
  | Identity | Local operator boundary | Workspace RBAC where configured; SAML and SCIM are future work |
27
- | Operations | Customer-operated single node | Design-partner operation; no enterprise SLA in the current beta |
27
+ | Operations | Customer-operated single node or bounded small fleet | Managed fleet remains Cloud work; no enterprise SLA in the current beta |
28
28
 
29
29
  ## What Stays Local
30
30
 
@@ -14,10 +14,36 @@ MCP agent
14
14
  -> receipt/replay
15
15
  ```
16
16
 
17
- Use this only for bounded database workflows where you can accept a single-node
18
- local ledger and local/operator approval. Use Synapsor Cloud when you need HA,
19
- central audit, RBAC/SSO, multi-reviewer approvals, hosted retention, managed
20
- runners, policy packs, or production support/SLA.
17
+ Use this only for bounded database workflows using either the default
18
+ single-node SQLite ledger or the tested, serialized small-fleet Postgres
19
+ runtime store. Use Synapsor Cloud when you need a managed fleet, hosted central
20
+ audit, organization administration, SSO/SCIM, compliance retention, or a
21
+ production support/SLA. See [Running A Small Runner
22
+ Fleet](running-a-runner-fleet.md) for the exact OSS fleet guarantees and limits.
23
+
24
+ ## Audit The Agent-Facing Surface First
25
+
26
+ Before configuring writeback or connecting a production-like database, audit
27
+ the MCP tools the agent can see. Start with Runner's built-in risky database MCP
28
+ example:
29
+
30
+ ```bash
31
+ npx -y @synapsor/runner audit --example dangerous-db-mcp
32
+ ```
33
+
34
+ Then audit the actual manifest, remote MCP endpoint, or stdio server you intend
35
+ to expose:
36
+
37
+ ```bash
38
+ synapsor-runner audit ./tools-list.json
39
+ synapsor-runner audit https://mcp.example.com --bearer-env MCP_AUDIT_TOKEN
40
+ synapsor-runner audit 'stdio:node ./server.mjs'
41
+ ```
42
+
43
+ Audit is a static risk review, not proof that an MCP server is secure. Treat
44
+ generic SQL/query tools, model-controlled tenant or principal fields, and
45
+ model-facing approval/apply tools as deployment blockers. See [MCP
46
+ Audit](mcp-audit.md) for supported inputs, findings, and machine-readable output.
21
47
 
22
48
  ## Supported Scope
23
49
 
@@ -27,6 +53,11 @@ Production-candidate OSS scope:
27
53
  - trusted context from environment/session values, not model arguments;
28
54
  - local SQLite ledger for evidence, query audit, proposals, receipts, replay,
29
55
  and lifecycle events;
56
+ - bounded shared Postgres runtime ledger for several claim-bound HTTP Runner
57
+ instances and verified reviewers;
58
+ - asymmetric RS256/ES256 session authentication, readiness probes, separately
59
+ protected metrics, source pools, and fleet-wide rate limits;
60
+ - distinct-reviewer approval quorum from the canonical contract;
30
61
  - direct guarded single-row `UPDATE` for simple approved edits;
31
62
  - app-owned `http_handler` or `command_handler` executors for richer approved
32
63
  business transactions;
@@ -37,8 +68,8 @@ Out of scope:
37
68
  - raw `execute_sql` or model-generated SQL;
38
69
  - generic direct `INSERT`, `DELETE`, `UPSERT`, DDL, or multi-row SQL writeback;
39
70
  - physical branching of external Postgres/MySQL;
40
- - workflow DAGs, auto-merge/settlement, RBAC/SSO, HA ledger, or compliance
41
- retention;
71
+ - workflow DAGs, auto-merge/settlement, hosted team administration, SSO/SCIM,
72
+ multi-region ledger replication, or compliance retention;
42
73
  - making prompt injection impossible.
43
74
 
44
75
  ## Database Roles
@@ -213,7 +244,8 @@ Mirror mode config:
213
244
  "mode": "mirror",
214
245
  "url_env": "SYNAPSOR_LEDGER_DATABASE_URL",
215
246
  "schema": "synapsor_runner",
216
- "lock_timeout_ms": 10000
247
+ "lock_timeout_ms": 10000,
248
+ "max_entries": 10000
217
249
  }
218
250
  }
219
251
  }
@@ -241,7 +273,8 @@ synapsor-runner apply --all-approved --yes \
241
273
 
242
274
  When `storage.shared_postgres.mode` is `mirror`, `doctor` checks that the
243
275
  ledger URL environment variable is present and that `ledger_entries`,
244
- `proposal_locks`, and `worker_leases` exist in the configured schema. It reports
276
+ `proposal_locks`, `worker_leases`, and `rate_limit_buckets` exist in the
277
+ configured schema. It reports
245
278
  environment variable names and table readiness only; it does not print database
246
279
  URLs or initialize the schema.
247
280
 
@@ -286,6 +319,13 @@ long-running `worker run --yes` repeats bounded drain cycles under that lock
286
319
  and sleeps between idle polls, so multiple workers can share one Postgres ledger
287
320
  without holding the lock while idle.
288
321
 
322
+ The current bridge loads a bounded snapshot and serializes each mutation. It
323
+ fails closed above `max_entries`; it is not an unbounded scalable database
324
+ engine. `proposals`, `evidence`, `query-audit`, `receipts`, `replay`,
325
+ `activity`, `metrics`, worker status, and the local UI can read the same shared
326
+ queue with `--config`. Back up, verify, restore, and archive-before-retention
327
+ with the commands in [Running A Small Runner Fleet](running-a-runner-fleet.md).
328
+
289
329
  For unattended policy-approved queues, declare reviewed aggregate `LIMIT`
290
330
  clauses first, then use the explicit batch command:
291
331
 
@@ -426,6 +466,8 @@ synapsor-runner config validate --config synapsor.runner.json
426
466
  synapsor-runner doctor --config synapsor.runner.json --report --redact --output synapsor-doctor.md
427
467
  synapsor-runner doctor --config synapsor.runner.json --check-writeback
428
468
  synapsor-runner tools preview --config synapsor.runner.json --store ./.synapsor/local.db
469
+ curl --fail http://127.0.0.1:8766/healthz
470
+ curl --fail http://127.0.0.1:8766/readyz
429
471
  ```
430
472
 
431
473
  Doctor reports should be redacted by default before sharing. They must not
@@ -10,7 +10,25 @@ 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
- ## Unreleased
13
+ ## 1.1.0 (prepared, not published)
14
+
15
+ ### Bounded Small-Fleet Runtime
16
+
17
+ - Fixes claim/context authority conflicts before serving and keeps
18
+ object-filtered receipts/activity scoped to the requested object.
19
+ - Adds asymmetric session/operator identity, readiness, protected HTTP
20
+ metrics, bounded native source pools, and shared fleet rate limits.
21
+ - Adds portable distinct-reviewer quorum in the OSS canonical spec and DSL.
22
+ Existing 1.0 contracts still default to one approval. Cloud/C++ enforcement
23
+ of this optional field is not claimed until independently verified there.
24
+ - Adds bounded shared-ledger CLI/UI review, startup-safe schema migration,
25
+ dead-letter recovery, backup/restore/retention, and tested worker recovery
26
+ before write and after durable commit.
27
+ - Adds [Running A Small Runner Fleet](running-a-runner-fleet.md) and the
28
+ `corepack pnpm test:fleet` synthetic two-Runner verification.
29
+
30
+ Prepared package versions: `@synapsor/spec@1.1.0`,
31
+ `@synapsor/dsl@1.1.0`, and `@synapsor/runner@1.1.0`.
14
32
 
15
33
  ## 1.0.0
16
34
 
@@ -85,6 +85,27 @@ versioning. Breaking changes require a new major version, except for security
85
85
  fixes that close a vulnerability while preserving the safest possible
86
86
  compatibility path.
87
87
 
88
+ ## 1.1 Fleet Gate
89
+
90
+ Do not publish `1.1.0` until all 1.0 gates remain green and the local synthetic
91
+ fleet verifier proves:
92
+
93
+ - claim/context contradictions fail before serving;
94
+ - two claim-bound Runners preserve tenant isolation and one active proposal;
95
+ - shared fixed-window rate limits reject with `retry_after_ms`;
96
+ - distinct verified reviewers satisfy canonical quorum and apply remains
97
+ blocked before `N/N`;
98
+ - Postgres/MySQL pool acquisition and queue limits fail fast;
99
+ - worker termination before write and after commit recovers without a duplicate
100
+ effect;
101
+ - backup digest, clean restore, and archive-before-retention preserve active
102
+ records;
103
+ - `/healthz`, `/readyz`, and separately authorized `/metrics` expose no secrets
104
+ or business object/principal labels.
105
+
106
+ Run `corepack pnpm test:fleet`. Only homogeneous 1.1 fleet operation is part of
107
+ this gate; mixed-minor operation must be tested before it is claimed.
108
+
88
109
  ## Stable Compatibility Promise
89
110
 
90
111
  Starting with `1.0.0`, Synapsor Runner keeps these public surfaces compatible
@@ -35,6 +35,9 @@ Unknown keys fail when `strict` is true (the default).
35
35
  | `policies` | No | Embedded reviewed policies; contract policies merge into the same catalog. |
36
36
  | `approvals` | No | Local approval overrides. |
37
37
  | `operator_identity` | No | Verified operator identity and apply-role wiring for approve/reject/apply. |
38
+ | `session_auth` | HTTP claims | HS256 development or asymmetric RS256/ES256 session-token verification. |
39
+ | `rate_limits` | No | Operational fixed-window limits; fleet-wide only with shared `runtime_store`. |
40
+ | `metrics` | No | Separately authorized scrapeable HTTP metrics. Disabled by default. |
38
41
  | `executors` | No | App-owned writeback wiring. |
39
42
  | `cloud` | Cloud mode | Scoped Cloud adapter configuration. |
40
43
 
@@ -58,7 +61,8 @@ Unknown keys fail when `strict` is true (the default).
58
61
  "mode": "mirror",
59
62
  "url_env": "SYNAPSOR_LEDGER_DATABASE_URL",
60
63
  "schema": "synapsor_runner",
61
- "lock_timeout_ms": 10000
64
+ "lock_timeout_ms": 10000,
65
+ "max_entries": 10000
62
66
  }
63
67
  }
64
68
  }
@@ -85,10 +89,17 @@ ledger by restoring into a temporary local store under the Postgres advisory
85
89
  lock, running the existing mutation, and syncing back. Long-running worker
86
90
  processes repeat bounded drain cycles and release the advisory lock while idle.
87
91
 
92
+ The bridge loads at most `max_entries` ledger records per operation. The default
93
+ is 10,000; valid values are 100 through 100,000. It fails closed with
94
+ `POSTGRES_RUNTIME_STORE_CAPACITY_EXCEEDED` above the bound. Reviewer CLI reads
95
+ and the local UI use the same shared queue. This is a serialized small-fleet
96
+ design, not an unbounded high-throughput primary store.
97
+
88
98
  `synapsor-runner doctor --config ./synapsor.runner.json` checks shared Postgres
89
99
  wiring when this block is present: the URL env var must be set, and the
90
- configured schema must already contain `ledger_entries`, `proposal_locks`, and
91
- `worker_leases`. Doctor reports env var names and table readiness only; it does
100
+ configured schema must already contain `ledger_entries`, `proposal_locks`,
101
+ `worker_leases`, and `rate_limit_buckets`. Doctor reports env var names and
102
+ table readiness only; it does
92
103
  not print database URLs or create the schema.
93
104
 
94
105
  ## Sources
@@ -115,6 +126,13 @@ timeout. `ssl` carries adapter-specific reviewed SSL options when used.
115
126
 
116
127
  A contract's `SOURCE billing_postgres` must exactly match a `sources` key.
117
128
 
129
+ Long-running servers reuse native driver pools. Optional `pool` keys are
130
+ `max_connections` (default 10), `connection_timeout_ms` (3000),
131
+ `idle_timeout_ms` (30000), `queue_timeout_ms` (5000), and `queue_limit`
132
+ (default `max(10, max_connections * 4)`). Queue overflow returns
133
+ `SOURCE_POOL_QUEUE_FULL`; acquisition timeout returns `SOURCE_POOL_TIMEOUT`.
134
+ One-shot CLI commands close their pools.
135
+
118
136
  ## Trusted context
119
137
 
120
138
  ```json
@@ -161,6 +179,35 @@ the active secret first, then the previous secret. Existing MCP sessions remain
161
179
  bound to the exact token fingerprint, so clients cannot swap tenant/principal
162
180
  identity inside an initialized session.
163
181
 
182
+ For networked deployments prefer asymmetric public-key verification:
183
+
184
+ ```json
185
+ {
186
+ "session_auth": {
187
+ "provider": "jwt_asymmetric",
188
+ "algorithms": ["RS256"],
189
+ "jwks_url_env": "SYNAPSOR_SESSION_JWKS_URL",
190
+ "issuer": "https://identity.example",
191
+ "audience": "synapsor-runner",
192
+ "tenant_claim": "tenant_id",
193
+ "principal_claim": "sub",
194
+ "clock_skew_seconds": 30,
195
+ "jwks_cache_seconds": 600,
196
+ "jwks_cooldown_seconds": 30,
197
+ "fetch_timeout_ms": 3000,
198
+ "max_response_bytes": 1048576
199
+ }
200
+ }
201
+ ```
202
+
203
+ `jwt_asymmetric` requires an explicit `RS256`/`ES256` allowlist and exactly one
204
+ public-key source: `jwks_url_env`, `public_key_env`, or `public_key_path`.
205
+ JWKS selection uses `kid`; unknown keys get one controlled refresh. Fetches are
206
+ timeout/size bounded and do not follow redirects. Private JWK fields are
207
+ rejected. Every effective named context in an `http_claims` server must bind
208
+ tenant and principal from claims; environment/static contradictions fail with
209
+ `TRUSTED_CONTEXT_PROVIDER_CONFLICT` before serving.
210
+
164
211
  ## Contracts and embedded capabilities
165
212
 
166
213
  ```json
@@ -215,6 +262,48 @@ from reviewed DSL `LIMIT` clauses, not an unreviewed local override. A tripped
215
262
  limit falls back to human review and is recorded in the ledger.
216
263
  `approvals.disable_auto_approval: true` disables local policy auto-approval
217
264
  without changing the reviewed contract. Approval never becomes an MCP tool.
265
+ Portable `approval.required_approvals` defaults to `1` and accepts `1..10`.
266
+ Distinct verified subjects fill quorum slots; one subject cannot count twice.
267
+ A rejection is terminal. Policy auto-approval is deferred when quorum is
268
+ greater than one.
269
+
270
+ ## Rate limits
271
+
272
+ ```json
273
+ {
274
+ "rate_limits": {
275
+ "enabled": true,
276
+ "default": { "requests": 60, "window_seconds": 60 },
277
+ "capabilities": {
278
+ "billing.propose_refund": { "requests": 10, "window_seconds": 60 }
279
+ }
280
+ }
281
+ }
282
+ ```
283
+
284
+ Rules are fixed-window deployment controls keyed by trusted tenant plus
285
+ reviewed capability. There is no separate burst allowance: all configured
286
+ requests may arrive immediately, but the next request is rejected until the
287
+ aligned window resets. They are process-local with SQLite and fleet-wide/atomic
288
+ with shared `runtime_store`. `RATE_LIMITED` includes `retry_after_ms` to that
289
+ reset boundary, records a counter, and creates no proposal. Rate limits are
290
+ runtime wiring and never portable contract fields.
291
+
292
+ ## HTTP metrics
293
+
294
+ ```json
295
+ {
296
+ "metrics": {
297
+ "enabled": true,
298
+ "token_env": "SYNAPSOR_METRICS_TOKEN"
299
+ }
300
+ }
301
+ ```
302
+
303
+ `/metrics` is disabled by default. Non-loopback exposure requires a separate
304
+ metrics token; the MCP bearer does not authorize metrics. Labels are bounded to
305
+ trusted tenant, capability, source, engine, and readiness component. Object
306
+ IDs, principals, URLs, tokens, and raw errors are never labels.
218
307
 
219
308
  ## Operator identity
220
309
 
@@ -257,6 +346,15 @@ subject, timestamp, roles, signature, and integrity hash into the approval
257
346
  ledger. `apply_roles` independently gates writeback. The local browser UI
258
347
  refuses approval/rejection in signed-key mode so it cannot bypass this check.
259
348
 
349
+ For OIDC-style operator identity, use `provider: "jwt_oidc"` with the same
350
+ asymmetric public-key options as session auth, plus one token source:
351
+ `token_env`, `token_file_env`, or `token_stdin: true`. With stdin, pipe or paste
352
+ one token and close stdin; combine it with `--yes` so confirmation does not
353
+ compete for the stream. Also configure `roles_claim`, `subject_claim`, and an
354
+ `attestation_secret_env` containing at least 32 bytes. Runner persists verified
355
+ subject/roles/issuer/key ID and an attested decision proof, never the bearer
356
+ token. Tokens must not be passed as command-line arguments.
357
+
260
358
  ## Cloud mode
261
359
 
262
360
  Cloud mode omits local sources/capabilities and requires: