@synapsor/runner 1.4.121 → 1.4.123

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 (38) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +25 -15
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/runner.mjs +2855 -347
  6. package/docs/README.md +1 -0
  7. package/docs/capability-authoring.md +59 -1
  8. package/docs/cloud-cli.md +248 -0
  9. package/docs/cloud-mode.md +88 -6
  10. package/docs/cloud-push.md +22 -5
  11. package/docs/conformance.md +6 -0
  12. package/docs/contract-review.md +33 -0
  13. package/docs/contract-testing.md +30 -1
  14. package/docs/dsl-reference.md +11 -0
  15. package/docs/hosted-cloud-linked-verification.md +97 -0
  16. package/docs/oss-vs-cloud.md +13 -6
  17. package/docs/release-notes.md +48 -0
  18. package/docs/rfcs/007-trusted-principal-row-scope.md +75 -0
  19. package/docs/runner-bundles.md +24 -0
  20. package/docs/running-a-runner-fleet.md +8 -0
  21. package/docs/security-boundary.md +19 -2
  22. package/examples/runner-fleet/seed/mysql.sql +5 -1
  23. package/examples/runner-fleet/seed/postgres.sql +5 -1
  24. package/examples/support-plan-credit/README.md +8 -6
  25. package/fixtures/contracts/capability-surface-fitness.contract.json +190 -0
  26. package/fixtures/dsl/principal-row-scope.synapsor.sql +23 -0
  27. package/fixtures/protocol/MANIFEST.json +10 -10
  28. package/package.json +1 -1
  29. package/schemas/change-set.v1.schema.json +14 -0
  30. package/schemas/change-set.v2.schema.json +14 -0
  31. package/schemas/change-set.v3.schema.json +2 -0
  32. package/schemas/compensation-change-set.v1.schema.json +3 -2
  33. package/schemas/inverse-descriptor.v1.schema.json +2 -0
  34. package/schemas/synapsor.contract-tests.schema.json +11 -1
  35. package/schemas/writeback-job.v1.schema.json +14 -0
  36. package/schemas/writeback-job.v2.schema.json +14 -0
  37. package/schemas/writeback-job.v3.schema.json +2 -0
  38. package/schemas/writeback-job.v4.schema.json +2 -2
package/docs/README.md CHANGED
@@ -142,6 +142,7 @@ no-database demo, wire your database, then read deeper concepts.
142
142
  topology, claim-bound sessions, pools, fleet rate limits, quorum, metrics,
143
143
  dead letters, backup/restore/retention, and kill/recovery evidence.
144
144
  - [Cloud Mode](cloud-mode.md): what stays local and what Cloud-linked mode adds.
145
+ - [Synapsor Cloud CLI](cloud-cli.md): command, credential, entitlement, and Cloud-linked authority reference.
145
146
  - [OSS Runner Vs Synapsor Cloud](oss-vs-cloud.md): detailed product and
146
147
  operational boundary.
147
148
  - [Cloud Push](cloud-push.md): register a validated local contract in the
@@ -84,11 +84,14 @@ synapsor-runner contract bundle ./synapsor.contract.json --out ./synapsor-runner
84
84
  synapsor-runner cloud push ./synapsor.contract.json --dry-run
85
85
  synapsor-runner cloud push ./synapsor.contract.json \
86
86
  --api-url "$SYNAPSOR_CLOUD_BASE_URL" \
87
- --token "$SYNAPSOR_CLOUD_TOKEN" \
88
87
  --workspace "$SYNAPSOR_PROJECT_ID" \
89
88
  --name billing-late-fee
90
89
  ```
91
90
 
91
+ Set `SYNAPSOR_API_KEY` for scoped CI automation or
92
+ `SYNAPSOR_CLOUD_ACCESS_TOKEN` for a signed-in human. Secrets are not accepted
93
+ as command-line flags.
94
+
92
95
  Reference the generated contract from local runner wiring:
93
96
 
94
97
  ```json
@@ -118,6 +121,22 @@ synapsor-runner mcp serve --config ./synapsor.runner.json --store ./.synapsor/lo
118
121
  capabilities cannot accidentally lose descriptions, returns hints, or numeric
119
122
  patch bounds during review.
120
123
 
124
+ After compilation, run the canonical contract through the surface review:
125
+
126
+ ```bash
127
+ synapsor-runner contract lint ./synapsor.contract.json
128
+ synapsor-runner contract lint ./synapsor.contract.json --strict
129
+ ```
130
+
131
+ The default command keeps capability-surface findings advisory; `--strict` opts
132
+ into warning-as-failure CI policy. Review capabilities using a simple fitness
133
+ test: if the name is not a business operation an audit log would recognize, ask
134
+ whether the model should receive it. Lint also surfaces generic query-like
135
+ string arguments, more than eight capabilities on one target, and structurally
136
+ near-duplicate tools. These checks make breadth drift visible; they do not
137
+ change canonical validity or replace human review. See [Contract
138
+ Review](contract-review.md) for exact codes and behavior.
139
+
121
140
  ## DSL / JSON Capability Parity
122
141
 
123
142
  The DSL compiles to canonical `@synapsor/spec` JSON. It must not silently weaken
@@ -318,6 +337,45 @@ Bad:
318
337
 
319
338
  Runner rejects model-facing trust-scope arguments.
320
339
 
340
+ ### Same-Tenant Principal Row Scope
341
+
342
+ Tenant scope separates organizations. When multiple authenticated users share
343
+ one tenant but should see only rows assigned to them, add a second reviewed
344
+ lock:
345
+
346
+ ```sql
347
+ CREATE AGENT CONTEXT care_session
348
+ BIND hospital_id FROM HTTP_CLAIM hospital_id REQUIRED
349
+ BIND principal FROM HTTP_CLAIM sub REQUIRED
350
+ TENANT BINDING hospital_id
351
+ PRINCIPAL BINDING principal
352
+ END
353
+
354
+ CREATE CAPABILITY care.inspect_assigned_patient
355
+ USING CONTEXT care_session
356
+ SOURCE local_postgres
357
+ ON public.patients
358
+ PRIMARY KEY id
359
+ TENANT KEY hospital_id
360
+ PRINCIPAL SCOPE KEY assigned_to
361
+ LOOKUP patient_id BY id
362
+ ARG patient_id STRING REQUIRED MAX LENGTH 128
363
+ ALLOW READ id, hospital_id, display_name, care_status, updated_at
364
+ KEEP OUT assigned_to, diagnosis_notes
365
+ REQUIRE EVIDENCE
366
+ MAX ROWS 1
367
+ END
368
+ ```
369
+
370
+ Runner generates one parameterized predicate containing both locks. It also
371
+ forces the scope column on reviewed inserts and preserves it through proposals,
372
+ writeback jobs, receipts, replay, and compensation. A same-tenant row assigned
373
+ to another principal has the same public not-found result as an absent or
374
+ cross-tenant row. The scope column may remain kept out of model-visible data.
375
+ Use a separately reviewed tenant-wide capability for supervisors; there is no
376
+ runtime bypass flag. Database RLS and least-privilege roles remain recommended
377
+ defense in depth.
378
+
321
379
  ## Direct SQL Writeback
322
380
 
323
381
  Use direct SQL writeback for reviewed guarded single-row INSERT, UPDATE, or
@@ -0,0 +1,248 @@
1
+ # Synapsor Cloud CLI
2
+
3
+ Use `synapsor-runner` for the local MCP/database boundary. Use `synapsor` from
4
+ `@synapsor/cli` for Cloud administration, shared review, and audit. Linking a
5
+ Runner to Cloud is optional; local-only Runner mode requires no account.
6
+
7
+ ## Credential Boundaries
8
+
9
+ | Credential | Used by | Purpose |
10
+ |---|---|---|
11
+ | Human session | `synapsor`, web UI | Administration and human decisions, subject to role, policy, recent auth, and entitlement |
12
+ | Service API key | `synapsor` or `synapsor-runner cloud push` in CI | Explicit project scopes such as contract push or activity export |
13
+ | Runner token | `synapsor-runner` Cloud protocol | Register, heartbeat, proposal/activity sync, leases, and terminal results |
14
+ | Local MCP HTTP token | MCP client to local Runner | Local transport authentication only |
15
+
16
+ Authentication, project role/scope authorization, and paid feature entitlement
17
+ are independent checks. A valid key can remain authenticated after a billing
18
+ change while paid operations return `payment_required` or
19
+ `feature_not_entitled`. Neither API keys nor Runner tokens bypass plan state.
20
+
21
+ Never put a Cloud secret, database URL, or local MCP token on a CLI argument.
22
+ Use a masked environment variable or a mode-`0600` secret file.
23
+
24
+ ## Command Index
25
+
26
+ ```text
27
+ auth login, logout, whoami, profiles, use, configure-service
28
+ status selected profile, API, project, service health
29
+ entitlements effective hosted features, limits, and blocked reason
30
+ billing plan/payment lifecycle summary
31
+ workspaces list, use
32
+ projects list, show, use
33
+ sources list, show (safe metadata only)
34
+ contracts init, validate, format, inspect, diff, push, list, show,
35
+ history, pull, activate, rollback
36
+ contexts list, show, create, update, remove
37
+ capabilities list, show, create, update, remove, preview
38
+ workflows list, show, create, update, remove, validate
39
+ api-keys list, show, create, rotate, revoke
40
+ runners list, show, create, rotate-token, revoke-token,
41
+ bundle download, doctor
42
+ proposals list, show, decisions, approve, reject
43
+ activity search, show
44
+ evidence show
45
+ receipts show
46
+ replay show, verify
47
+ exports create, status, download
48
+ ```
49
+
50
+ Run `synapsor --help` for the compact index. All local context/capability/
51
+ workflow edits validate the complete canonical contract and use atomic file
52
+ replacement with a backup. Complex definitions can be supplied with
53
+ `--from-file`; the CLI does not invent unsafe defaults for proposal writeback.
54
+
55
+ `synapsor <group> --help` lists that group's real commands, and
56
+ `synapsor <group> <command> --help` shows the checked command syntax. The help
57
+ tree and this index are covered together by the CLI test suite.
58
+
59
+ ## Human Login And Project Selection
60
+
61
+ ```bash
62
+ npm install --global @synapsor/cli
63
+
64
+ synapsor auth login --open
65
+ synapsor auth whoami
66
+ synapsor workspaces list
67
+ synapsor projects list
68
+ synapsor projects use <project-id>
69
+ synapsor status
70
+ synapsor entitlements show
71
+ ```
72
+
73
+ `status` reports authentication, selected project, service health, plan,
74
+ billing lifecycle, effective entitlement, grace deadline, and blocked reason.
75
+ Authentication, project role or service-key scope authorization, and paid
76
+ feature entitlement are separate checks. A valid credential does not bypass
77
+ `payment_required`, `feature_not_entitled`, or `project_inactive`.
78
+
79
+ ## Author A Canonical Contract
80
+
81
+ Contexts, capabilities, and workflows are reviewed parts of one immutable
82
+ canonical contract, not mutable Cloud permission rows. Local edits are atomic,
83
+ fully validated, and followed by a semantic safety diff.
84
+
85
+ ```bash
86
+ synapsor contracts init ./synapsor.contract.json --name support-contract
87
+
88
+ synapsor contexts create trusted_operator \
89
+ --contract ./synapsor.contract.json \
90
+ --from-file ./context.json
91
+
92
+ synapsor capabilities create billing.inspect_invoice \
93
+ --contract ./synapsor.contract.json \
94
+ --from-file ./capability.json
95
+
96
+ synapsor workflows create billing.invoice_review \
97
+ --contract ./synapsor.contract.json \
98
+ --from-file ./workflow.json
99
+
100
+ synapsor contracts validate ./synapsor.contract.json
101
+ synapsor contracts diff ./before.json ./synapsor.contract.json
102
+ synapsor contracts push ./synapsor.contract.json --dry-run
103
+ synapsor contracts push ./synapsor.contract.json
104
+ ```
105
+
106
+ Push creates or reuses an immutable version. It does not silently rewrite an
107
+ existing version. Activation and rollback are separate authorized mutations:
108
+
109
+ ```bash
110
+ synapsor contracts history <contract-id>
111
+ synapsor contracts activate <contract-id>/<version-id> --yes
112
+ synapsor contracts rollback <contract-id>/<older-version-id> --yes
113
+ ```
114
+
115
+ The Runner-centric equivalent remains supported:
116
+
117
+ ```bash
118
+ synapsor-runner cloud push ./synapsor.contract.json
119
+ ```
120
+
121
+ It uses the same canonical validation, digest, endpoint, idempotency, and error
122
+ semantics as `synapsor contracts push`.
123
+
124
+ ## Create And Use A CI API Key
125
+
126
+ Only an authorized human can create, rotate, or revoke keys. The secret is
127
+ returned once and must be written to a protected file; it is never printed by
128
+ normal or JSON output.
129
+
130
+ ```bash
131
+ synapsor api-keys create \
132
+ --name contract-ci \
133
+ --scopes project:read,contracts:read,contracts:write \
134
+ --expires-at 2026-12-31 \
135
+ --secret-file ./.synapsor/contract-ci.key
136
+
137
+ export SYNAPSOR_API_KEY="$(cat ./.synapsor/contract-ci.key)"
138
+ synapsor auth configure-service \
139
+ --profile ci \
140
+ --api-url https://dev-api.synapsor.ai \
141
+ --project <project-id> \
142
+ --credential-env SYNAPSOR_API_KEY
143
+
144
+ synapsor auth whoami --profile ci --json
145
+ synapsor contracts push ./synapsor.contract.json --profile ci
146
+ ```
147
+
148
+ `whoami` reports a service identity, key prefix/ID, scopes, project,
149
+ expiration, and entitlement summary. It never turns the key into a human
150
+ identity. Rotate and revoke through a human profile:
151
+
152
+ ```bash
153
+ synapsor api-keys rotate <key-id> --secret-file ./.synapsor/contract-ci.next.key
154
+ synapsor api-keys revoke <key-id> --yes
155
+ ```
156
+
157
+ After revocation, requests using the old value fail; they do not fall back to a
158
+ local profile or another credential.
159
+
160
+ ## Connect A Customer-Operated Runner
161
+
162
+ Runner tokens are separate, source-scoped machine credentials. Create one and
163
+ store its one-time value without printing it:
164
+
165
+ ```bash
166
+ synapsor runners create \
167
+ --name support-runner \
168
+ --sources <cloud-source-id> \
169
+ --secret-file ./.synapsor/runner.token
170
+
171
+ export SYNAPSOR_RUNNER_TOKEN="$(cat ./.synapsor/runner.token)"
172
+ ```
173
+
174
+ When `--permissions` is omitted, Cloud applies the bounded Runner-protocol
175
+ permission set. Use `--permissions <csv>` to narrow a token further; these
176
+ permissions never authorize human, API-key, contract, or approval operations.
177
+
178
+ The credential-free Runner bundle contains the reviewed connection metadata
179
+ and sample configuration. Supply the Runner token separately:
180
+
181
+ ```bash
182
+ synapsor runners bundle download <contract-id>/<version-id> \
183
+ --source <cloud-source-id> \
184
+ --out ./synapsor-runner-bundle.zip
185
+ synapsor runners doctor <runner-id>
186
+ ```
187
+
188
+ Runner tokens authenticate only registration, heartbeat, proposal/activity
189
+ sync, leases, and terminal results. They are rejected by Cloud administration
190
+ and human-decision routes. Source database URLs and write credentials stay in
191
+ the Runner process environment and never enter the bundle, contract, Cloud
192
+ CLI, or Cloud registry.
193
+
194
+ ## Review And Audit
195
+
196
+ ```bash
197
+ synapsor proposals list --status pending
198
+ synapsor proposals show <proposal-id>
199
+ synapsor proposals approve <proposal-id> --reason "Reviewed evidence" --yes
200
+
201
+ synapsor evidence show <proposal-id>
202
+ synapsor receipts show <proposal-id>
203
+ synapsor replay verify <proposal-id>
204
+ synapsor activity search --lookup <business-id> --json
205
+ ```
206
+
207
+ Proposal approval records Cloud governance state; it never changes the source
208
+ database directly. A trusted Runner must receive an authorized lease and
209
+ recheck the local proposal, exact contract digest, tenant and principal locks,
210
+ allowlists, bounds, conflict/version guard, idempotency, and affected-row rule.
211
+ Service API keys and Runner tokens cannot record human approval.
212
+
213
+ ## Cloud-Linked Runner Authority
214
+
215
+ `governance.mode: cloud_linked` is explicit. Cloud is authoritative for active
216
+ contract versions, human decisions, job leasing, and terminal governance state.
217
+ The local/shared Runner store remains a durable operational spool and local
218
+ evidence/replay ledger; Cloud and the CLI never open or upload its SQLite file.
219
+
220
+ The initial evidence policy is `metadata_only`. Cloud receives bounded proposal
221
+ diffs, safe identities/fingerprints, IDs, counts, and integrity hashes. Source
222
+ rows, evidence payloads, SQL text/parameters, kept-out fields, credentials, and
223
+ local replay payloads remain local.
224
+
225
+ By default a Cloud-linked Runner durably queues proposals during a transient
226
+ Cloud outage. Set `governance.queue_when_unavailable: false` to fail before the
227
+ source read/proposal is created when authenticated Cloud readiness is not
228
+ available. In both modes, no Cloud approval or writeback proceeds offline.
229
+
230
+ Inspect and repair the local operational outbox with Runner, not the Cloud CLI:
231
+
232
+ ```bash
233
+ synapsor-runner doctor --config ./synapsor.runner.json --store ./.synapsor/local.db
234
+ synapsor-runner cloud outbox status --config ./synapsor.runner.json --store ./.synapsor/local.db
235
+ synapsor-runner cloud outbox inspect latest --config ./synapsor.runner.json --store ./.synapsor/local.db
236
+ synapsor-runner cloud outbox reconcile --yes --config ./synapsor.runner.json --store ./.synapsor/local.db
237
+ ```
238
+
239
+ Dead-letter or hash-divergence state requires operator inspection. Runner never
240
+ silently chooses a local copy over Cloud or creates a fake local human approval.
241
+
242
+ ## Pagination And JSON
243
+
244
+ List/search commands accept `--limit`, `--cursor`, and bounded `--all`.
245
+ `--max-items` can lower the aggregate bound; raising it beyond the safe default
246
+ requires `--allow-large-result`. Use `--json` for scripts. Unknown or transient
247
+ failures are not retried as mutations; shared client retries are limited to
248
+ classified safe requests and honor server retry timing.
@@ -15,9 +15,35 @@ Cloud-linked mode is for teams that need a shared control plane:
15
15
  - receipt reporting;
16
16
  - retention and audit visibility.
17
17
 
18
- The local runner still keeps database credentials in your environment. MCP
19
- client config snippets should contain command paths and runner arguments, not
20
- database URLs or write credentials.
18
+ The local Runner still keeps database credentials in your environment. MCP
19
+ client config snippets contain command paths and Runner arguments, not database
20
+ URLs or write credentials.
21
+
22
+ ## Trust Boundary
23
+
24
+ ```text
25
+ agent / MCP client
26
+ -> local Runner semantic tools
27
+ -> local scoped read + evidence
28
+ -> local proposal (source unchanged)
29
+ -> reviewed diff + safe references synced to Cloud
30
+ -> signed-in human approves or rejects in Cloud
31
+ -> source-scoped leased job
32
+ -> local trusted worker rechecks contract + proposal + DB guards
33
+ -> local database write
34
+ -> redacted receipt/activity linkage returned to Cloud
35
+ ```
36
+
37
+ Cloud receives the contract/version/digest, capability, trusted scope
38
+ identifiers, reviewable allowlisted diff, safe evidence/query references,
39
+ decision identity, lease state, and receipt/replay links. Database URLs,
40
+ passwords, handler tokens, private keys, full source rows, and kept-out evidence
41
+ payloads stay local.
42
+
43
+ Maintainers must run the opt-in
44
+ [hosted Cloud-linked verification](./hosted-cloud-linked-verification.md) with a
45
+ packed Runner, disposable Cloud project, and synthetic source before claiming a
46
+ release is Cloud-linked end to end.
21
47
 
22
48
  Push and retrieve the portable contract without moving database credentials
23
49
  into Cloud:
@@ -29,11 +55,67 @@ synapsor-runner cloud push ./synapsor.contract.json \
29
55
  --name support-plan-credit
30
56
  ```
31
57
 
58
+ Use the current design-partner API base:
59
+
60
+ ```bash
61
+ export SYNAPSOR_CLOUD_BASE_URL="https://dev-api.synapsor.ai"
62
+ ```
63
+
64
+ Then create a source-scoped Runner token in **Contract registry -> Connect
65
+ Runner**, download the selected version's bundle, and run:
66
+
67
+ ```bash
68
+ cd ./<downloaded-runner-bundle>
69
+ cp .env.example .env
70
+ # Fill the placeholders in .env, including the one-time Runner token.
71
+ set -a && . ./.env && set +a
72
+
73
+ npx -y -p @synapsor/runner synapsor-runner config validate --config ./synapsor.runner.json
74
+ npx -y -p @synapsor/runner synapsor-runner tools preview --config ./synapsor.runner.json --store ./.synapsor/local.db
75
+ npx -y -p @synapsor/runner synapsor-runner cloud connect --config ./synapsor.cloud.json
76
+ npx -y -p @synapsor/runner synapsor-runner mcp serve --config ./synapsor.runner.json --store ./.synapsor/local.db
77
+ ```
78
+
79
+ Run the trusted worker in a second operator-controlled terminal:
80
+
81
+ ```bash
82
+ set -a && . ./.env && set +a
83
+ npx -y -p @synapsor/runner synapsor-runner runner start --config ./synapsor.runner.json --store ./.synapsor/local.db
84
+ ```
85
+
86
+ For deployment checks or an operator-controlled single claim cycle, use the
87
+ same installed worker with `--once`; it exits after applying at most one
88
+ Cloud-approved job and still rechecks the local contract, proposal, tenant,
89
+ version, bounds, and idempotency guards:
90
+
91
+ ```bash
92
+ npx -y -p @synapsor/runner synapsor-runner runner start --once --config ./synapsor.runner.json --store ./.synapsor/local.db
93
+ ```
94
+
95
+ When a local proposal exists:
96
+
97
+ ```bash
98
+ npx -y -p @synapsor/runner synapsor-runner cloud sync latest --config ./synapsor.cloud.json --store ./.synapsor/local.db
99
+ ```
100
+
32
101
  See [Cloud Push](cloud-push.md) and [Runner Bundles](runner-bundles.md).
33
102
 
34
- Cloud registry storage preserves approval policies but does not, by itself,
35
- mean hosted policy enforcement is enabled. Local Runner enforcement and hosted
36
- Cloud approval enforcement are separate runtime boundaries.
103
+ Cloud approval changes proposal state and creates a claimable job; it does not
104
+ touch the source database directly. If a lease expires, another compatible
105
+ Runner may claim the job, but source and ledger idempotency prevent a duplicate
106
+ effect. Stale row/set guards return a conflict. If the system cannot prove
107
+ whether a source write committed, it records an indeterminate/reconciliation
108
+ state instead of retrying blindly.
109
+
110
+ Runner tokens are scoped to one project, explicit source IDs, and named
111
+ operations. Rotate or revoke them from Connect Runner. Revocation blocks new
112
+ registration, heartbeat, proposal, claim, lease, activity, and result calls.
113
+
114
+ This is a single-node design-partner boundary, not managed Runner hosting,
115
+ multi-region HA, SAML/SCIM, legal hold/WORM retention, or an enterprise SLA.
116
+ Run the explicit hosted integration gate with synthetic staging data before
117
+ depending on a deployment; registry push alone is not proof that registration,
118
+ approval, leasing, and receipt synchronization are live.
37
119
 
38
120
  Run the local smoke for this mode with:
39
121
 
@@ -17,8 +17,8 @@ Dry-run validates and normalizes locally. It performs no network request.
17
17
  ## Push
18
18
 
19
19
  ```bash
20
- export SYNAPSOR_CLOUD_BASE_URL="https://api.synapsor.ai"
21
- export SYNAPSOR_CLOUD_TOKEN="<workspace-scoped-token>"
20
+ export SYNAPSOR_CLOUD_BASE_URL="https://dev-api.synapsor.ai"
21
+ export SYNAPSOR_API_KEY="<contracts-write-service-key>"
22
22
  export SYNAPSOR_WORKSPACE_ID="<workspace-id>"
23
23
 
24
24
  synapsor-runner cloud push ./synapsor.contract.json \
@@ -29,7 +29,9 @@ The response includes the contract id, immutable version id, server-computed
29
29
  digest, summary counts, status, and registry path. Identical normalized content
30
30
  is idempotent; changed content creates the next version.
31
31
 
32
- Tokens are sent in the authorization header and are never part of the contract.
32
+ Use `SYNAPSOR_API_KEY` for scoped CI automation or
33
+ `SYNAPSOR_CLOUD_ACCESS_TOKEN` for an authenticated human session. Credentials
34
+ are sent in the authorization header and are never part of the contract.
33
35
  Database URLs, passwords, private keys, and model-controlled tenant bindings
34
36
  are rejected by server-side validation.
35
37
 
@@ -42,6 +44,11 @@ permission (`403`), missing workspace/API paths (`404`), registry conflicts
42
44
  (`409`), server validation (`422`), and Cloud/network failures. Error output
43
45
  does not echo the bearer token.
44
46
 
47
+ `dev-api.synapsor.ai` is the supported design-partner endpoint. Do not replace
48
+ it with `api.synapsor.ai` unless Synapsor has separately announced and verified
49
+ that hostname. Contract push uses a signed-in workspace/API credential;
50
+ Runner registration and jobs use a separate source-scoped Runner token.
51
+
45
52
  ## Cloud To Local
46
53
 
47
54
  Open **Contract registry** in the workspace, choose a version, and download its
@@ -49,6 +56,16 @@ runner bundle. The ZIP contains the normalized contract, local runner wiring,
49
56
  placeholder environment file, README, and MCP client examples. It contains no
50
57
  live credentials or table rows.
51
58
 
59
+ Choose the Cloud source when downloading because each bundle is source-bound:
60
+
61
+ ```bash
62
+ synapsor runners bundle download <contract-id>/<version-id> \
63
+ --source <cloud-source-id> \
64
+ --out ./synapsor-runner-bundle.zip
65
+ ```
66
+
52
67
  Cloud currently preserves approval policy definitions in the contract and
53
- bundle. Hosted approval-policy enforcement remains a separate Cloud feature;
54
- do not infer it from registry storage alone.
68
+ bundle. Cloud approval is a human-authenticated decision and is not exposed to
69
+ the Runner token or MCP. The local Runner remains responsible for rechecking
70
+ the exact contract digest, proposal hash, tenant, target, allowed fields,
71
+ conflict/version guard, bounds, and one-write/idempotency rules before applying.
@@ -32,6 +32,7 @@ Current fixture groups:
32
32
  - `numeric-bounds`
33
33
  - `bounded-set-threats`
34
34
  - `reversible-change-sets`
35
+ - `principal-row-scope`
35
36
 
36
37
  The fixture set is intentionally small in 0.1. It covers the runner-supported
37
38
  semantic surface first: trusted context, scoped reads, evidence handles,
@@ -54,6 +55,10 @@ compensation proposal, not rollback: apply-time receipts capture only
54
55
  allowlisted inverse data, compensation uses fresh guards and exact members,
55
56
  ambiguous outcomes fail closed pending reconciliation, and hard delete reports
56
57
  a specific best-effort-unavailable reason.
58
+ The principal-row-scope fixture proves that a reviewed owner/assignee column is
59
+ bound to a required trusted principal and AND-composed with tenant scope. Run
60
+ `corepack pnpm test:principal-scope` for the live Postgres/MySQL proof covering
61
+ same-tenant denial and shared-ledger evidence-handle isolation.
57
62
 
58
63
  Additional 0.1 parity coverage currently lives in tests and verification
59
64
  scripts rather than separate `cloud-push/` or `dsl-json-parity/` conformance
@@ -81,6 +86,7 @@ Run:
81
86
  corepack pnpm --filter @synapsor-runner/mcp-server test
82
87
  corepack pnpm test:contract-conformance
83
88
  corepack pnpm test:aggregate-read
89
+ corepack pnpm test:principal-scope
84
90
  ```
85
91
 
86
92
  The spec package also validates every conformance contract:
@@ -33,9 +33,42 @@ cover objective review gaps such as missing descriptions/evidence, unbounded
33
33
  strings, unresolved wiring, irreversible operations, and policy/writeback
34
34
  contradictions. Lint does not claim to discover every sensitive column.
35
35
 
36
+ ### Review Capability-Surface Fitness
37
+
38
+ Runner structurally limits capability **depth**: contracts cannot turn a model
39
+ argument into raw SQL or a free-form predicate, and trusted tenant/principal
40
+ scope is never model input. A separate review concern is capability **breadth**:
41
+ many individually narrow tools can accumulate until the total model-facing
42
+ surface is difficult to understand.
43
+
44
+ Use this fitness test during review:
45
+
46
+ > If a capability cannot be described as a named business operation an audit
47
+ > log would recognize, it probably should not be exposed yet.
48
+
49
+ `contract lint` reports these deterministic advisory warnings:
50
+
51
+ | Code | Review signal |
52
+ | --- | --- |
53
+ | `SURFACE_GENERIC_ARGUMENT` | An un-enumerated string argument is literally named `filter`, `query`, `where`, `predicate`, or `sql`. Runner still does not interpolate it into SQL. |
54
+ | `SURFACE_TARGET_DENSITY` | More than eight capabilities target the same normalized source and object. Eight is a review threshold, not a runtime limit. |
55
+ | `SURFACE_OPERATION_NAMING` | A capability name does not match the conservative named-business-operation heuristic. |
56
+ | `SURFACE_NEAR_DUPLICATE` | Two capabilities have the same target, kind, reviewed fields, targeting, write/approval shape, and identical or directionally loosened arguments. |
57
+
58
+ The default command exits successfully when these advisories are the only
59
+ findings. `--strict` (or `--fail-on warning`) deliberately lets a team turn all
60
+ warnings into a CI policy gate without changing canonical contract validity.
61
+ Text, JSON, and SARIF use the same stable finding set and deterministic order.
62
+
63
+ Passing these checks does not prove that a surface is safe or well designed.
64
+ Review which operations each agent actually needs; use narrower contracts or
65
+ Runner/client deployments instead of exposing every organization capability to
66
+ every model.
67
+
36
68
  Expected successful output ends with:
37
69
 
38
70
  ```text
71
+ Surface: N model-facing capabilities across M targets; 0 target(s) above the advisory density threshold of 8
39
72
  Summary: 0 error / 0 warning / N info
40
73
  ```
41
74
 
@@ -36,11 +36,40 @@ A successful run ends with a stable count such as:
36
36
  Summary: 6 passed / 0 failed / 6 total
37
37
  ```
38
38
 
39
- Supported assertions cover valid scoped tools, cross-tenant denial, kept-out
39
+ Supported assertions cover valid scoped tools, cross-tenant denial,
40
+ same-tenant cross-principal denial, kept-out
40
41
  fields, argument constraints, transition guards, set caps, unchanged source
41
42
  state before approval, and the absence of model-facing approval/writeback.
42
43
  Trusted tenant and principal values belong in test setup, not tool arguments.
43
44
 
45
+ Use `cross_principal_deny` to prove both the allowed and denied sides of one
46
+ row lock. The two contexts must use the same tenant and different principals:
47
+
48
+ ```json
49
+ {
50
+ "id": "case-manager-b-cannot-read-a",
51
+ "kind": "cross_principal_deny",
52
+ "capability": "care.inspect_assigned_patient",
53
+ "args": { "patient_id": "PAT-1001" },
54
+ "trusted_context": {
55
+ "tenant_id": "hospital_a",
56
+ "principal": "case_manager_a",
57
+ "provenance": "http_claims"
58
+ },
59
+ "other_trusted_context": {
60
+ "tenant_id": "hospital_a",
61
+ "principal": "case_manager_b",
62
+ "provenance": "http_claims"
63
+ },
64
+ "expected_code": "NOT_FOUND_IN_TENANT"
65
+ }
66
+ ```
67
+
68
+ Live execution first proves the owner can read the row, then requires the other
69
+ principal to receive the generic denial and `RESOURCE_NOT_FOUND` for the
70
+ owner's evidence handle. Run the repository's Postgres/MySQL proof with
71
+ `corepack pnpm test:principal-scope`.
72
+
44
73
  The flagship example includes a working manifest at
45
74
  [`examples/support-plan-credit/synapsor.contract-tests.json`](../examples/support-plan-credit/synapsor.contract-tests.json).
46
75
  Contract tests prove the declared cases, not every possible threat or database
@@ -47,6 +47,7 @@ CREATE CAPABILITY billing.inspect_invoice
47
47
  ON public.invoices
48
48
  PRIMARY KEY id
49
49
  TENANT KEY tenant_id
50
+ PRINCIPAL SCOPE KEY assigned_to
50
51
  CONFLICT GUARD updated_at
51
52
  ...
52
53
  END
@@ -62,8 +63,18 @@ END
62
63
  | `ON schema.table` | Required fixed target. Table/schema are never model inputs. |
63
64
  | `PRIMARY KEY column` | Fixed single-row target key. Defaults to `id` in DSL 0.1. Declare it explicitly. |
64
65
  | `TENANT KEY column` | Required in DSL 0.1. Adds trusted tenant scope to every read/write. |
66
+ | `PRINCIPAL SCOPE KEY column` | Optional tenant-additive row lock. Runner binds this fixed column to the context's required trusted `PRINCIPAL BINDING`; it is never a model argument. |
65
67
  | `CONFLICT GUARD column` | Captures the row-version value for exact guarded writeback. Prefer a monotonic version or native-precision timestamp. |
66
68
 
69
+ `PRINCIPAL SCOPE KEY` means `tenant_key = trusted tenant AND
70
+ principal_scope_key = trusted principal`. It cannot replace tenant scope,
71
+ default to all rows, or be overridden by an argument. The principal binding
72
+ must be required and come from a trusted provider such as `ENVIRONMENT`,
73
+ `HTTP_CLAIM`, `SESSION`, or `CLOUD_SESSION`. For networked multi-user serving,
74
+ use signed HTTP claims. See the complete
75
+ [`principal-row-scope.synapsor.sql`](../fixtures/dsl/principal-row-scope.synapsor.sql)
76
+ example.
77
+
67
78
  ## Arguments and lookup
68
79
 
69
80
  ```sql