@synapsor/runner 0.1.15 → 1.0.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
@@ -41,6 +41,10 @@ no-database demo, wire your database, then read deeper concepts.
41
41
  - [Capability Authoring](capability-authoring.md): define read/proposal
42
42
  capabilities, model-facing descriptions, result envelopes, trusted context,
43
43
  and writeback guards.
44
+ - [DSL Reference](dsl-reference.md): complete supported grammar, clause order,
45
+ compiled meaning, and constraints.
46
+ - [Runner Config Reference](runner-config-reference.md): every public wiring
47
+ key, default, environment binding, and path-resolution rule.
44
48
  - [Recipes](recipes.md): starter business-capability templates.
45
49
  - [JSON Schema](../schemas/synapsor.runner.schema.json): editor validation for
46
50
  `synapsor.runner.json`.
@@ -12,6 +12,11 @@ Use `.synapsor.sql` as the preferred DSL source extension because editors can
12
12
  provide generic SQL highlighting. Legacy `.synapsor` files remain supported;
13
13
  the suffix does not change DSL semantics or generated canonical JSON.
14
14
 
15
+ For exact clause grammar, ordering, constraints, fixed-string patches, and the
16
+ primary-key-only lookup rule, see the [DSL Reference](dsl-reference.md). For all
17
+ local wiring keys and path rules, see the
18
+ [Runner Config Reference](runner-config-reference.md).
19
+
15
20
  Use `synapsor.runner.json` for local wiring: database env var names, SQLite
16
21
  store path, MCP transport settings, and local development flags. The model sees
17
22
  semantic capabilities such as `billing.inspect_invoice`, not raw SQL, table
@@ -26,10 +26,15 @@ Current fixture groups:
26
26
  - `proposal-capability`
27
27
  - `kept-out-fields`
28
28
  - `manual-approval`
29
+ - `auto-approval`
30
+ - `aggregate-policy-limits`
31
+ - `numeric-bounds`
29
32
 
30
33
  The fixture set is intentionally small in 0.1. It covers the runner-supported
31
34
  semantic surface first: trusted context, scoped reads, evidence handles,
32
35
  proposal boundaries, kept-out fields, manual approval, and replay envelopes.
36
+ The aggregate-policy fixture additionally proves that reviewed daily ceilings
37
+ fall back to human review atomically and record the limit that tripped.
33
38
 
34
39
  Additional 0.1 parity coverage currently lives in tests and verification
35
40
  scripts rather than separate `cloud-push/` or `dsl-json-parity/` conformance
@@ -38,6 +43,9 @@ fixture directories:
38
43
  - `docs/dsl-json-parity.md`, DSL/spec tests, and the
39
44
  `numeric-bounds-transition` C++ export fixture cover richer DSL/JSON parity
40
45
  fields such as `returns_hint`, numeric bounds, and transition guards.
46
+ - `packages/dsl/fixtures/invalid/non-primary-lookup.synapsor.sql` proves the
47
+ DSL rejects a lookup meaning that canonical spec 0.1 cannot represent,
48
+ instead of silently rewriting it to primary-key access.
41
49
  - The main Synapsor repo script `scripts/verify_contract_cloud_push.sh`
42
50
  verifies real Cloud push, retrieval, idempotent versioning, unauthorized
43
51
  rejection, and runner-bundle download against a live local control-plane.
@@ -2,7 +2,7 @@
2
2
 
3
3
  The canonical scope page is [Current Limitations](limitations.md).
4
4
 
5
- Current `0.1.x` scope:
5
+ Current `1.0` scope:
6
6
 
7
7
  - local semantic MCP tools for Postgres/MySQL-backed business actions;
8
8
  - schema inspection and guided config generation;
@@ -13,7 +13,7 @@ Current `0.1.x` scope:
13
13
  business transactions;
14
14
  - stdio MCP, Streamable HTTP MCP, and a small JSON-RPC bridge.
15
15
 
16
- Stable `0.1.x` compatibility covers the documented `synapsor-runner` binary,
16
+ Stable `1.x` compatibility covers the documented `synapsor-runner` binary,
17
17
  config schema version `1`, result envelope v2 with v1 opt-out, stdio/Streamable
18
18
  HTTP MCP surfaces, documented MCP client snippets, proposal/evidence/replay
19
19
  inspection commands, direct SQL writeback, and app-owned executor contracts.
package/docs/doctor.md CHANGED
@@ -63,9 +63,9 @@ The target-table probe uses fixed schema/table/column identifiers from the
63
63
  reviewed config. It does not accept model SQL, user SQL, arbitrary table names,
64
64
  or arbitrary column names. It runs inside a transaction and rolls back.
65
65
 
66
- The receipt-table probe can create `synapsor_writeback_receipts` if the writer
67
- has permission. If your policy does not allow Runner to create tables in the
68
- application schema, pre-create the table and grant access:
66
+ An administrator must pre-create `synapsor_writeback_receipts` and grant the
67
+ steady-state writer access. The doctor probe does not execute DDL or require
68
+ schema `CREATE`:
69
69
 
70
70
  ```bash
71
71
  synapsor-runner writeback migration --engine postgres --schema synapsor
@@ -0,0 +1,215 @@
1
+ # Synapsor DSL Reference
2
+
3
+ The Synapsor DSL is a reviewable SQL-like authoring format that compiles to the
4
+ canonical `@synapsor/spec` contract. Prefer `.synapsor.sql`; legacy `.synapsor`
5
+ files remain valid. The filename does not change semantics.
6
+
7
+ Validate and compile with strict safety warnings enabled:
8
+
9
+ ```bash
10
+ synapsor-runner dsl validate ./contract.synapsor.sql --strict
11
+ synapsor-runner dsl compile ./contract.synapsor.sql \
12
+ --out ./synapsor.contract.json --strict
13
+ ```
14
+
15
+ Keywords are case-insensitive. Blocks start with `CREATE ...` and end with
16
+ `END` or `END;`. Lines may end in semicolons. `--` starts a line comment.
17
+ Unsupported clauses fail explicitly.
18
+
19
+ ## Agent context
20
+
21
+ ```sql
22
+ CREATE AGENT CONTEXT local_operator
23
+ BIND tenant_id FROM ENVIRONMENT SYNAPSOR_TENANT_ID REQUIRED
24
+ BIND principal FROM ENVIRONMENT SYNAPSOR_PRINCIPAL REQUIRED
25
+ TENANT BINDING tenant_id
26
+ PRINCIPAL BINDING principal
27
+ END
28
+ ```
29
+
30
+ | Clause | Meaning |
31
+ | --- | --- |
32
+ | `BIND name FROM source key [REQUIRED]` | Defines trusted context. Sources: `SESSION`, `ENV`/`ENVIRONMENT`, `CLOUD_SESSION`, `STATIC_DEV`, `HTTP_CLAIM`. Model tool arguments cannot set these bindings. |
33
+ | `TENANT BINDING name` | Selects the binding used for tenant scope. Defaults to a binding named `tenant_id`. |
34
+ | `PRINCIPAL BINDING name` | Selects the actor binding. Defaults to a binding named `principal`. |
35
+
36
+ At least one `BIND` is required. The selected tenant/principal bindings must be
37
+ provided by the trusted runtime environment, not by the model.
38
+
39
+ ## Capability identity and target
40
+
41
+ ```sql
42
+ CREATE CAPABILITY billing.inspect_invoice
43
+ DESCRIPTION 'Inspect one invoice in the trusted tenant.'
44
+ RETURNS HINT 'Returns reviewed fields and an evidence handle.'
45
+ USING CONTEXT local_operator
46
+ SOURCE billing_postgres
47
+ ON public.invoices
48
+ PRIMARY KEY id
49
+ TENANT KEY tenant_id
50
+ CONFLICT GUARD updated_at
51
+ ...
52
+ END
53
+ ```
54
+
55
+ | Clause | Requirement and compiled meaning |
56
+ | --- | --- |
57
+ | `CREATE [AGENT] CAPABILITY namespace.name` | Starts a qualified capability. |
58
+ | `DESCRIPTION 'text'` | Model-facing `tools/list` description. Recommended for every tool and required by strict review for proposal quality. |
59
+ | `RETURNS HINT 'text'` | Model-facing result guidance. |
60
+ | `USING CONTEXT name` | Required. References an agent context. |
61
+ | `SOURCE name` | Source key that must match `synapsor.runner.json.sources`. |
62
+ | `ON schema.table` | Required fixed target. Table/schema are never model inputs. |
63
+ | `PRIMARY KEY column` | Fixed single-row target key. Defaults to `id` in DSL 0.1. Declare it explicitly. |
64
+ | `TENANT KEY column` | Required in DSL 0.1. Adds trusted tenant scope to every read/write. |
65
+ | `CONFLICT GUARD column` | Captures the row-version value for exact guarded writeback. Prefer a monotonic version or native-precision timestamp. |
66
+
67
+ ## Arguments and lookup
68
+
69
+ ```sql
70
+ LOOKUP invoice_id BY id
71
+ ARG invoice_id STRING REQUIRED MAX LENGTH 128 DESCRIPTION 'Invoice id.'
72
+ ARG amount_cents NUMBER REQUIRED MIN 1 MAX 2500 DESCRIPTION 'Amount in cents.'
73
+ ARG confirmed BOOLEAN REQUIRED
74
+ ```
75
+
76
+ Argument types are `STRING`/`TEXT`, `NUMBER`, and `BOOLEAN`/`BOOL`.
77
+
78
+ - `REQUIRED` makes the model-facing argument mandatory.
79
+ - `DESCRIPTION 'text'` documents the argument in MCP `tools/list`.
80
+ - `MIN n` and `MAX n` are numeric bounds for `NUMBER`.
81
+ - `MAX LENGTH n` bounds `STRING`/`TEXT`. Legacy text `MAX n` remains accepted.
82
+ - `LOOKUP arg BY column` binds one argument to the target row lookup.
83
+
84
+ In spec 0.1, lookup is primary-key-only. The `BY` column must equal the declared
85
+ `PRIMARY KEY`. A different column fails with `LOOKUP_COLUMN_UNSUPPORTED`; Runner
86
+ never silently rewrites it. List/filter queries require a reviewed view or a
87
+ different capability design.
88
+
89
+ ## Read surface and evidence
90
+
91
+ ```sql
92
+ ALLOW READ id, tenant_id, status, amount_cents, updated_at
93
+ KEEP OUT card_token, private_notes
94
+ REQUIRE EVIDENCE
95
+ MAX ROWS 1
96
+ ```
97
+
98
+ `ALLOW READ` is required and becomes the visible-field allowlist. `KEEP OUT`
99
+ records fields that must remain outside the model/evidence surface. Keep-out
100
+ fields must not also be visible. `REQUIRE EVIDENCE` records the scoped read and
101
+ query audit. `MAX ROWS n` bounds the result; local 0.1 capabilities are designed
102
+ around one primary-key row.
103
+
104
+ ## Proposal and patch
105
+
106
+ Read capabilities end after the read clauses. A proposal capability adds:
107
+
108
+ ```sql
109
+ PROPOSE ACTION waive_late_fee
110
+ ALLOW WRITE late_fee_cents, waiver_reason
111
+ PATCH late_fee_cents = 0
112
+ PATCH waiver_reason = ARG reason
113
+ PATCH status = 'approved'
114
+ PATCH reviewed = TRUE
115
+ PATCH obsolete_note = NULL
116
+ ```
117
+
118
+ `PROPOSE ACTION` must precede proposal-only clauses. `ALLOW WRITE` is the exact
119
+ patch-column allowlist. Each `PATCH` uses one of:
120
+
121
+ - `ARG name`: value comes from a validated model argument;
122
+ - a quoted fixed string;
123
+ - a fixed number;
124
+ - `TRUE`, `FALSE`, or `NULL`.
125
+
126
+ Fixed strings such as `PATCH status = 'approved'` are supported. The patch is
127
+ saved as a proposal; it is not executed by the model-facing tool.
128
+
129
+ ## Bounds and transitions
130
+
131
+ ```sql
132
+ BOUND amount_cents 1..2500
133
+ BOUND discount_cents ..500
134
+ BOUND score 1..
135
+ TRANSITION status ALLOW reported -> approved|rejected
136
+ TRANSITION status FROM current_status ALLOW open -> closed, held -> open
137
+ ```
138
+
139
+ `BOUND column min..max` applies to a patched numeric column. Either side may be
140
+ open, but not both. Strict mode warns when a numeric argument reaches a patch
141
+ without argument bounds or a patch bound.
142
+
143
+ `TRANSITION patched_column [FROM source_column] ALLOW from -> to|to, ...`
144
+ allowlists state changes. Without `FROM`, the current value is read from the
145
+ patched column. Values may be identifiers or quoted strings.
146
+
147
+ ## Approval and writeback
148
+
149
+ ```sql
150
+ APPROVAL ROLE billing_lead
151
+ AUTO APPROVE WHEN amount_cents <= 2500
152
+ LIMIT 20 PER DAY
153
+ LIMIT TOTAL 100000 PER DAY
154
+ WRITEBACK DIRECT SQL
155
+ ```
156
+
157
+ `APPROVAL ROLE role` records the required local reviewer role. Local OSS
158
+ approval identity is operator-provided; enterprise identity/RBAC belongs to the
159
+ Cloud boundary.
160
+
161
+ `AUTO APPROVE WHEN field <= non_negative_integer` is supported only for a
162
+ numeric patched field and must follow `APPROVAL ROLE`. Its maximum cannot exceed
163
+ the field's `BOUND`. Policy approval still does not apply the write.
164
+
165
+ Aggregate limits follow the auto-approval clause:
166
+
167
+ ```sql
168
+ LIMIT 20 PER DAY
169
+ LIMIT TOTAL 100000 PER DAY
170
+ ```
171
+
172
+ `LIMIT n PER DAY` caps policy approvals for the trusted tenant and policy in
173
+ the UTC calendar day. `LIMIT TOTAL n PER DAY` sums the policy's numeric patch
174
+ field over the same scope. Use `PER OBJECT DAY` instead of `PER DAY` to scope a
175
+ limit to one trusted tenant, policy, and business object. The check and approval
176
+ are one atomic ledger transaction. When any ceiling would be exceeded, Runner
177
+ leaves the proposal in `pending_review` and records
178
+ `policy_auto_approval_deferred` with observed, proposed, and projected values.
179
+ It does not reject or auto-apply the proposal.
180
+
181
+ Writeback forms:
182
+
183
+ | Clause | Meaning |
184
+ | --- | --- |
185
+ | `WRITEBACK DIRECT SQL` | Runner performs one guarded single-row update after approval. |
186
+ | `WRITEBACK APP HANDLER EXECUTOR name` | Runner calls a configured app-owned executor after approval. URL/token wiring stays outside the contract. |
187
+ | `WRITEBACK CLOUD WORKER` | Delegates approved execution to Cloud worker infrastructure. |
188
+ | `WRITEBACK NONE` | Proposal-only; local apply is intentionally unavailable. |
189
+
190
+ ## Workflow declarations
191
+
192
+ ```sql
193
+ CREATE AGENT WORKFLOW support.refund_review
194
+ USING CONTEXT local_operator
195
+ ALLOW CAPABILITY support.inspect_order
196
+ ALLOW CAPABILITY support.propose_refund
197
+ REQUIRE EVIDENCE
198
+ APPROVAL REQUIRED ROLE support_lead
199
+ CHECKPOINT PROPOSAL ONLY
200
+ END
201
+ ```
202
+
203
+ `USING CONTEXT` and at least one `ALLOW CAPABILITY` are required. Optional
204
+ clauses are `REQUIRE EVIDENCE`, `APPROVAL REQUIRED ROLE role`, and `CHECKPOINT
205
+ NONE|EVERY STEP|PROPOSAL ONLY`. Contracts can declare workflows; Runner 0.1 does
206
+ not execute arbitrary Cloud workflow DAGs, settlement, branching, or auto-merge.
207
+
208
+ ## Unsupported syntax
209
+
210
+ Unknown clauses fail instead of being ignored. Cloud-generated concepts such as
211
+ `ROOT EXTERNAL`, `JOIN EXTERNAL`, `RETURN ANSWER WITH CITATIONS`, `AUTO BRANCH`,
212
+ and `AUTO MERGE` are not local DSL 0.1 clauses.
213
+
214
+ The canonical JSON output, not parser implementation details, is the portable
215
+ contract. Validate generated JSON with `synapsor-runner contract validate`.
package/docs/http-mcp.md CHANGED
@@ -72,6 +72,47 @@ sessions: in-memory
72
72
 
73
73
  Use `/mcp` as the MCP endpoint. Health is available at `/healthz`.
74
74
 
75
+ ## TLS And mTLS
76
+
77
+ For a non-local long-running service, terminate TLS at a trusted proxy or start
78
+ Runner with env-backed PEM material:
79
+
80
+ ```bash
81
+ export SYNAPSOR_TLS_CERT_PEM="$(cat ./server.crt)"
82
+ export SYNAPSOR_TLS_KEY_PEM="$(cat ./server.key)"
83
+
84
+ synapsor-runner mcp serve-streamable-http \
85
+ --host 0.0.0.0 \
86
+ --port 8766 \
87
+ --config ./synapsor.runner.json \
88
+ --store ./.synapsor/local.db \
89
+ --auth-token-env SYNAPSOR_RUNNER_HTTP_TOKEN \
90
+ --tls-cert-env SYNAPSOR_TLS_CERT_PEM \
91
+ --tls-key-env SYNAPSOR_TLS_KEY_PEM
92
+ ```
93
+
94
+ To require client certificates:
95
+
96
+ ```bash
97
+ export SYNAPSOR_TLS_CA_PEM="$(cat ./client-ca.crt)"
98
+
99
+ synapsor-runner mcp serve-streamable-http \
100
+ --host 0.0.0.0 \
101
+ --port 8766 \
102
+ --config ./synapsor.runner.json \
103
+ --store ./.synapsor/local.db \
104
+ --auth-token-env SYNAPSOR_RUNNER_HTTP_TOKEN \
105
+ --tls-cert-env SYNAPSOR_TLS_CERT_PEM \
106
+ --tls-key-env SYNAPSOR_TLS_KEY_PEM \
107
+ --tls-ca-env SYNAPSOR_TLS_CA_PEM \
108
+ --require-client-cert
109
+ ```
110
+
111
+ The CLI reads PEM contents from environment variables and never prints them.
112
+ Runner-owned mTLS currently protects the Streamable HTTP MCP boundary. For
113
+ app-owned `http_handler` executors, terminate mTLS in your service mesh/proxy
114
+ or handler process and keep bearer/signature checks enabled in the handler.
115
+
75
116
  ## Start The JSON-RPC Bridge
76
117
 
77
118
  ```bash
@@ -6,7 +6,8 @@ Synapsor Runner is intentionally narrow in the current alpha.
6
6
 
7
7
  - Stdio MCP server for semantic database capabilities.
8
8
  - Local read and proposal tools.
9
- - Local SQLite evidence/proposal/query-audit/replay store.
9
+ - Local SQLite evidence/proposal/query-audit/replay store by default.
10
+ - Optional shared Postgres proposal/evidence/replay runtime store for MCP serving.
10
11
  - Human approval through CLI commands.
11
12
  - Public protocol objects:
12
13
  - `synapsor.change-set.v1`
@@ -6,7 +6,8 @@ contract, but they solve different operational problems.
6
6
  Runner is the open-source runtime that stays next to your application and
7
7
  database. It serves reviewed MCP tools, binds trusted context, stores local
8
8
  evidence and proposals, keeps approval outside MCP, applies or routes approved
9
- writeback, and records receipts and replay in a local SQLite ledger.
9
+ writeback, and records receipts and replay in the default local SQLite ledger
10
+ or an opt-in shared Postgres runtime store.
10
11
 
11
12
  Cloud is the team control plane. It stores versioned contracts, produces
12
13
  downloadable Runner bundles, and provides shared activity, evidence, approval,
@@ -18,7 +19,7 @@ and investigation surfaces for enabled design-partner deployments.
18
19
  | Contract source | Local files reviewed in Git | Shared registry with immutable versions and digests |
19
20
  | Trusted context | Local environment/session bindings | Registered bindings plus deployment-specific Cloud session context |
20
21
  | Capabilities | Local semantic MCP tools | Registry, version history, and capability inspection |
21
- | Evidence and replay | Local SQLite ledger | Shared activity and evidence surfaces where enabled |
22
+ | Evidence and replay | Local SQLite ledger by default; optional shared Postgres runtime store | Shared activity and evidence surfaces where enabled |
22
23
  | Approval | Local CLI or localhost UI | Team approval surfaces where enabled |
23
24
  | Writeback | Guarded one-row update or app-owned executor | Cloud-linked jobs with local execution; managed production orchestration is future work |
24
25
  | MCP risk audit | Static local audit | Organization-wide continuous audit is future work |
@@ -56,8 +56,9 @@ Write credential for direct `sql_update`:
56
56
 
57
57
  - can update only the allowed business columns;
58
58
  - cannot modify primary-key or tenant columns;
59
- - can create/write the `synapsor_writeback_receipts` table, or the table is
60
- pre-created and granted by an administrator;
59
+ - can `SELECT`/`INSERT`/`UPDATE` the administrator-created
60
+ `synapsor_writeback_receipts` table;
61
+ - does not need schema `CREATE` during doctor or apply;
61
62
  - is never exposed to MCP clients.
62
63
 
63
64
  Example config:
@@ -81,7 +82,7 @@ worker flows that do not pass a local config.
81
82
  ## Receipt Table
82
83
 
83
84
  Direct SQL writeback stores idempotency receipts in the source database. Runner
84
- creates this table if allowed:
85
+ expects an administrator to create this table before steady-state operation:
85
86
 
86
87
  ```sql
87
88
  CREATE TABLE IF NOT EXISTS synapsor_writeback_receipts (...);
@@ -97,10 +98,9 @@ synapsor-runner doctor --config synapsor.runner.json --check-writeback
97
98
 
98
99
  For MySQL, replace `postgres` with `mysql`.
99
100
 
100
- If the writer should not create application-schema tables, pre-create the
101
- receipt table with an administrator role, grant only the needed receipt-table
102
- permissions to the writer, or use an app-owned executor that stores receipts in
103
- your application boundary.
101
+ Grant only receipt-table `SELECT`/`INSERT`/`UPDATE` and schema usage to the
102
+ writer; schema `CREATE` is not required by doctor or apply. Use an app-owned
103
+ executor when receipt storage belongs inside your application boundary.
104
104
 
105
105
  ## Direct Writeback Vs App-Owned Executor
106
106
 
@@ -150,12 +150,163 @@ Useful commands:
150
150
  ```bash
151
151
  synapsor-runner store stats --store ./.synapsor/local.db
152
152
  synapsor-runner events tail --store ./.synapsor/local.db --follow
153
+ synapsor-runner metrics show --store ./.synapsor/local.db
153
154
  synapsor-runner store prune --store ./.synapsor/local.db --older-than 30d --dry-run
154
155
  synapsor-runner store vacuum --store ./.synapsor/local.db
155
156
  ```
156
157
 
157
158
  Details: [Store Lifecycle](store-lifecycle.md).
158
159
 
160
+ ## Shared Postgres Ledger Setup
161
+
162
+ The default runtime ledger is SQLite. For shared deployments, Runner now ships a
163
+ Postgres ledger setup surface that creates the schema used for shared audit
164
+ entries, proposal locks, and worker leases:
165
+
166
+ ```bash
167
+ export SYNAPSOR_LEDGER_DATABASE_URL="postgresql://ledger_writer:..."
168
+
169
+ synapsor-runner store shared-postgres migration --schema synapsor_runner
170
+ synapsor-runner store shared-postgres apply-migration \
171
+ --url-env SYNAPSOR_LEDGER_DATABASE_URL \
172
+ --schema synapsor_runner \
173
+ --yes
174
+ synapsor-runner store shared-postgres status \
175
+ --url-env SYNAPSOR_LEDGER_DATABASE_URL \
176
+ --schema synapsor_runner
177
+ synapsor-runner store shared-postgres sync \
178
+ --store ./.synapsor/local.db \
179
+ --url-env SYNAPSOR_LEDGER_DATABASE_URL \
180
+ --schema synapsor_runner \
181
+ --yes
182
+ synapsor-runner store shared-postgres restore \
183
+ --store ./.synapsor/restored.db \
184
+ --url-env SYNAPSOR_LEDGER_DATABASE_URL \
185
+ --schema synapsor_runner \
186
+ --yes
187
+ ```
188
+
189
+ These commands do not print the database URL. `sync` upserts a stable snapshot
190
+ of the local proposal/evidence/replay ledger into Postgres for shared audit,
191
+ backup, and retention. `restore` rebuilds a local SQLite store from those
192
+ shared ledger entries for recovery or offline investigation.
193
+
194
+ Runner supports two shared Postgres modes:
195
+
196
+ - `mirror`: bounded CLI mutations restore from Postgres into local SQLite,
197
+ mutate locally, then sync back under a schema-scoped advisory lock.
198
+ - `runtime_store`: MCP serving uses Postgres as the primary
199
+ proposal/evidence/replay store instead of opening a local SQLite store.
200
+
201
+ Local SQLite remains the default. Use `runtime_store` when several MCP sessions
202
+ or runner processes need to share proposal/evidence/replay state through one
203
+ ledger database. Use `mirror` when you want bounded operator handoff while still
204
+ running CLI mutations against a local SQLite file.
205
+
206
+ Mirror mode config:
207
+
208
+ ```json
209
+ {
210
+ "storage": {
211
+ "sqlite_path": "./.synapsor/local.db",
212
+ "shared_postgres": {
213
+ "mode": "mirror",
214
+ "url_env": "SYNAPSOR_LEDGER_DATABASE_URL",
215
+ "schema": "synapsor_runner",
216
+ "lock_timeout_ms": 10000
217
+ }
218
+ }
219
+ }
220
+ ```
221
+
222
+ ```bash
223
+ export SYNAPSOR_LEDGER_DATABASE_URL="postgresql://ledger_writer:..."
224
+
225
+ synapsor-runner doctor \
226
+ --config ./synapsor.runner.json \
227
+ --store ./.synapsor/local.db
228
+
229
+ synapsor-runner propose support.propose_plan_credit --sample \
230
+ --config ./synapsor.runner.json \
231
+ --store ./.synapsor/local.db
232
+
233
+ synapsor-runner proposals approve latest --yes \
234
+ --config ./synapsor.runner.json \
235
+ --store ./.synapsor/local.db
236
+
237
+ synapsor-runner apply --all-approved --yes \
238
+ --config ./synapsor.runner.json \
239
+ --store ./.synapsor/local.db
240
+ ```
241
+
242
+ When `storage.shared_postgres.mode` is `mirror`, `doctor` checks that the
243
+ ledger URL environment variable is present and that `ledger_entries`,
244
+ `proposal_locks`, and `worker_leases` exist in the configured schema. It reports
245
+ environment variable names and table readiness only; it does not print database
246
+ URLs or initialize the schema.
247
+
248
+ Mirror mode restores the Postgres ledger into the local store before a mutation
249
+ and syncs the local store back after the command, including failure events when
250
+ they were recorded locally. While it runs, Runner holds a schema-scoped
251
+ Postgres advisory lock so concurrent mirror-mode operators do not restore,
252
+ mutate, and sync over one another. The default lock wait is 10 seconds; tune it
253
+ with `--shared-ledger-lock-timeout-ms` or
254
+ `SYNAPSOR_SHARED_LEDGER_LOCK_TIMEOUT_MS`.
255
+
256
+ Mirror mode is explicit because the local SQLite store still executes the CLI
257
+ mutation. It is useful for bounded operator workflows and finite workers
258
+ (`worker run --once` or `--drain`); use `runtime_store` for long-lived MCP
259
+ serving and shared worker loops that need one proposal/evidence/replay ledger.
260
+
261
+ Runtime-store mode config:
262
+
263
+ ```json
264
+ {
265
+ "storage": {
266
+ "shared_postgres": {
267
+ "mode": "runtime_store",
268
+ "url_env": "SYNAPSOR_LEDGER_DATABASE_URL",
269
+ "schema": "synapsor_runner",
270
+ "lock_timeout_ms": 10000
271
+ }
272
+ }
273
+ }
274
+ ```
275
+
276
+ When MCP serving starts in `runtime_store` mode, Runner opens the Postgres URL
277
+ from `url_env`, auto-runs the shared-ledger migration, and serializes runtime
278
+ mutations with a transaction-scoped advisory lock. The MCP tools still expose no
279
+ database URLs or write credentials to the model.
280
+
281
+ `runtime_store` covers MCP serving, CLI approval/apply, and supervised worker
282
+ runs. For CLI mutations, Runner restores the shared ledger into a temporary
283
+ local store while holding the Postgres advisory lock, runs the existing local
284
+ mutation, then syncs the resulting ledger entries back to Postgres. A
285
+ long-running `worker run --yes` repeats bounded drain cycles under that lock
286
+ and sleeps between idle polls, so multiple workers can share one Postgres ledger
287
+ without holding the lock while idle.
288
+
289
+ For unattended policy-approved queues, declare reviewed aggregate `LIMIT`
290
+ clauses first, then use the explicit batch command:
291
+
292
+ ```bash
293
+ synapsor-runner apply --all-approved --yes \
294
+ --config ./synapsor.runner.json --store ./.synapsor/local.db \
295
+ --capability support.propose_plan_credit --tenant acme --max 20
296
+ ```
297
+
298
+ Each proposal is independent: a stale-row conflict does not abort later jobs.
299
+ The final summary reports applied, conflict, and skipped IDs. Re-running is
300
+ idempotent through durable receipts. Do not schedule batch apply for a policy
301
+ that has no reviewed aggregate limits.
302
+
303
+ Runner writes newline-delimited JSON events to stderr for model-facing tool
304
+ rejections, operator decisions, and terminal writeback outcomes. These events
305
+ contain safe codes and identifiers, never tool arguments, row values, database
306
+ URLs, tokens, private keys, or free-form driver errors. Prometheus/OpenMetrics
307
+ counters are available with `metrics show` and are grouped by trusted tenant
308
+ and reviewed capability.
309
+
159
310
  ## Restart And Recovery
160
311
 
161
312
  Runner stores proposal/evidence/replay state before writeback.
@@ -183,7 +334,26 @@ synapsor-runner activity search --proposal wrp_... --store ./.synapsor/local.db
183
334
  ### Docker Compose Shape
184
335
 
185
336
  Use Compose to run the MCP server next to your app and database network. Keep
186
- secrets in environment variables or your platform secret manager.
337
+ secrets in environment variables or your platform secret manager. To hydrate
338
+ Runner env vars from AWS Secrets Manager at startup, pass a JSON map through
339
+ `SYNAPSOR_SECRET_MAP`:
340
+
341
+ ```bash
342
+ export SYNAPSOR_SECRET_MAP='{
343
+ "SYNAPSOR_DATABASE_READ_URL": "prod/synapsor/runner#read_url",
344
+ "SYNAPSOR_DATABASE_WRITE_URL": "prod/synapsor/runner#write_url",
345
+ "SYNAPSOR_RUNNER_HTTP_TOKEN": "prod/synapsor/runner#http_token"
346
+ }'
347
+
348
+ synapsor-runner mcp serve-streamable-http \
349
+ --config ./synapsor.runner.json \
350
+ --store ./.synapsor/local.db \
351
+ --secrets-provider aws-secretsmanager-cli \
352
+ --secret-map-env SYNAPSOR_SECRET_MAP
353
+ ```
354
+
355
+ The AWS provider shells out to `aws secretsmanager get-secret-value`; the runner
356
+ logs only how many target env vars were loaded or skipped.
187
357
 
188
358
  ```yaml
189
359
  services:
@@ -286,4 +456,3 @@ Before promoting a package or calling a build production-candidate:
286
456
  The release gate should cover typecheck, focused tests, packed-package install,
287
457
  quick demo, own-db fixture, MCP stdio/HTTP checks, direct writeback,
288
458
  app-owned executor paths, package dry-run, and docs/package consistency.
289
-