@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.
- package/CHANGELOG.md +42 -0
- package/README.md +25 -15
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/runner.mjs +2855 -347
- package/docs/README.md +1 -0
- package/docs/capability-authoring.md +59 -1
- package/docs/cloud-cli.md +248 -0
- package/docs/cloud-mode.md +88 -6
- package/docs/cloud-push.md +22 -5
- package/docs/conformance.md +6 -0
- package/docs/contract-review.md +33 -0
- package/docs/contract-testing.md +30 -1
- package/docs/dsl-reference.md +11 -0
- package/docs/hosted-cloud-linked-verification.md +97 -0
- package/docs/oss-vs-cloud.md +13 -6
- package/docs/release-notes.md +48 -0
- package/docs/rfcs/007-trusted-principal-row-scope.md +75 -0
- package/docs/runner-bundles.md +24 -0
- package/docs/running-a-runner-fleet.md +8 -0
- package/docs/security-boundary.md +19 -2
- package/examples/runner-fleet/seed/mysql.sql +5 -1
- package/examples/runner-fleet/seed/postgres.sql +5 -1
- package/examples/support-plan-credit/README.md +8 -6
- package/fixtures/contracts/capability-surface-fitness.contract.json +190 -0
- package/fixtures/dsl/principal-row-scope.synapsor.sql +23 -0
- package/fixtures/protocol/MANIFEST.json +10 -10
- package/package.json +1 -1
- package/schemas/change-set.v1.schema.json +14 -0
- package/schemas/change-set.v2.schema.json +14 -0
- package/schemas/change-set.v3.schema.json +2 -0
- package/schemas/compensation-change-set.v1.schema.json +3 -2
- package/schemas/inverse-descriptor.v1.schema.json +2 -0
- package/schemas/synapsor.contract-tests.schema.json +11 -1
- package/schemas/writeback-job.v1.schema.json +14 -0
- package/schemas/writeback-job.v2.schema.json +14 -0
- package/schemas/writeback-job.v3.schema.json +2 -0
- package/schemas/writeback-job.v4.schema.json +2 -2
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Hosted Cloud-Linked Verification
|
|
2
|
+
|
|
3
|
+
This is the manual release gate for the complete OSS Runner and single-node
|
|
4
|
+
Synapsor Cloud boundary. It is intentionally opt-in because it creates Cloud
|
|
5
|
+
records and mutates a synthetic source row.
|
|
6
|
+
|
|
7
|
+
The verifier installs packed or published Cloud CLI and Runner packages in a
|
|
8
|
+
clean temporary directory and proves:
|
|
9
|
+
|
|
10
|
+
- public protocol discovery;
|
|
11
|
+
- packed CLI human `whoami`, secure one-time service-key output, and immediate
|
|
12
|
+
service-key revocation;
|
|
13
|
+
- canonical contract push parity between `synapsor contracts push` and
|
|
14
|
+
`synapsor-runner cloud push` with one immutable version identity;
|
|
15
|
+
- source-scoped Runner token creation, rotation, registration idempotency, and
|
|
16
|
+
heartbeat;
|
|
17
|
+
- credential-free Runner bundle download;
|
|
18
|
+
- scoped MCP read and cross-tenant denial;
|
|
19
|
+
- proposal creation without a source write and automatic durable-outbox
|
|
20
|
+
delivery without a manual `cloud sync` command;
|
|
21
|
+
- authenticated Cloud approval and guarded local writeback through the packed
|
|
22
|
+
Runner's public worker CLI;
|
|
23
|
+
- two registered Runner processes competing for one exclusive job lease;
|
|
24
|
+
- duplicate-claim idempotency, rejection, and stale-version conflict;
|
|
25
|
+
- linked Cloud activity integrity and canonical export;
|
|
26
|
+
- reviewed activation and rollback of a risk-increasing contract version; and
|
|
27
|
+
- immediate Runner-token revocation across doctor, registration, claim, and
|
|
28
|
+
result submission.
|
|
29
|
+
|
|
30
|
+
It never accepts production data. Use a disposable project and a synthetic
|
|
31
|
+
Postgres or MySQL database that can be reset after the run.
|
|
32
|
+
|
|
33
|
+
## Prerequisites
|
|
34
|
+
|
|
35
|
+
Build the workspace and pack both packages before a pre-publish check:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
corepack pnpm install
|
|
39
|
+
corepack pnpm build
|
|
40
|
+
corepack pnpm --filter @synapsor/runner pack --pack-destination /tmp
|
|
41
|
+
corepack pnpm --filter @synapsor/cli pack --pack-destination /tmp
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Prepare:
|
|
45
|
+
|
|
46
|
+
- a disposable Cloud project and imported synthetic source;
|
|
47
|
+
- an authenticated human session token obtained through the normal browser/
|
|
48
|
+
device login flow;
|
|
49
|
+
- separate least-privilege read and write source URLs;
|
|
50
|
+
- a canonical contract with one scoped read and one proposal capability; and
|
|
51
|
+
- three independent proposal fixture rows: apply, reject, and stale-conflict.
|
|
52
|
+
|
|
53
|
+
Read tokens and database URLs from a secret prompt or secret manager. Do not
|
|
54
|
+
put them in Git, shell history, command-line arguments, or the Runner bundle.
|
|
55
|
+
|
|
56
|
+
## Run
|
|
57
|
+
|
|
58
|
+
Set the non-secret fixture identifiers and JSON assertions for your synthetic
|
|
59
|
+
database. The exact required variables and their meanings are always available
|
|
60
|
+
without credentials:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
corepack pnpm verify:hosted-cloud-linked -- --help
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Then opt in explicitly and run:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
export SYNAPSOR_HOSTED_E2E=1
|
|
70
|
+
export SYNAPSOR_E2E_DISPOSABLE_PROJECT=1
|
|
71
|
+
export SYNAPSOR_CLOUD_BASE_URL="https://dev-api.synapsor.ai"
|
|
72
|
+
export SYNAPSOR_RUNNER_PACKAGE_SPEC="/tmp/synapsor-runner-<version>.tgz"
|
|
73
|
+
export SYNAPSOR_CLI_PACKAGE_SPEC="/tmp/synapsor-cli-<version>.tgz"
|
|
74
|
+
|
|
75
|
+
corepack pnpm verify:hosted-cloud-linked
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The command refuses to run unless both opt-in flags are present. HTTPS is
|
|
79
|
+
required unless `SYNAPSOR_E2E_ALLOW_HTTP=1` is explicitly set for a local
|
|
80
|
+
control-plane test. The generated Runner token is revoked in a `finally` block,
|
|
81
|
+
and the temporary directory is removed unless
|
|
82
|
+
`SYNAPSOR_E2E_KEEP_TEMP=1` is set for debugging.
|
|
83
|
+
|
|
84
|
+
## Passing Result
|
|
85
|
+
|
|
86
|
+
A pass prints only a boolean summary with all checks set to `true`. The
|
|
87
|
+
`token_rotated`, `registration_idempotent`, `two_runner_exclusive_claim`,
|
|
88
|
+
`registration_heartbeat`, `proposal_reviewed_metadata`,
|
|
89
|
+
`two_runner_exclusive_claim`, `receipt_replay_linked`, `activity_exported`,
|
|
90
|
+
`contract_governance`, and `revoked_operations_blocked` fields prove those
|
|
91
|
+
additional boundaries explicitly. It does not print bearer tokens, database
|
|
92
|
+
URLs, source rows, or kept-out values. Confirm the synthetic fixture is reset
|
|
93
|
+
after the run before reusing the project.
|
|
94
|
+
|
|
95
|
+
This gate is evidence for the existing single-node design-partner boundary. It
|
|
96
|
+
does not establish multi-region availability, managed Runner hosting, SSO/SCIM,
|
|
97
|
+
legal hold, formal compliance certification, or an enterprise SLA.
|
package/docs/oss-vs-cloud.md
CHANGED
|
@@ -9,9 +9,12 @@ evidence and proposals, keeps approval outside MCP, applies or routes approved
|
|
|
9
9
|
writeback, and records receipts and replay in the default local SQLite ledger
|
|
10
10
|
or an opt-in shared Postgres runtime store.
|
|
11
11
|
|
|
12
|
-
Cloud is the team control plane.
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
Cloud is the team control plane. In the design-partner deployment it stores
|
|
13
|
+
versioned contracts, issues source-scoped Runner identities, produces
|
|
14
|
+
credential-free bundles, receives reviewed local proposals, records human
|
|
15
|
+
decisions, leases approved jobs to compatible local Runners, and links safe
|
|
16
|
+
activity/receipt/replay metadata. The database connection and enforcement
|
|
17
|
+
runtime stay local.
|
|
15
18
|
|
|
16
19
|
| Need | OSS Runner | Synapsor Cloud |
|
|
17
20
|
| --- | --- | --- |
|
|
@@ -19,9 +22,9 @@ and investigation surfaces for enabled design-partner deployments.
|
|
|
19
22
|
| Contract source | Local files reviewed in Git | Shared registry with immutable versions and digests |
|
|
20
23
|
| Trusted context | Local environment/session bindings | Registered bindings plus deployment-specific Cloud session context |
|
|
21
24
|
| Capabilities | Local semantic MCP tools | Registry, version history, and capability inspection |
|
|
22
|
-
| Evidence and replay | Local SQLite ledger by default; optional shared Postgres runtime store |
|
|
23
|
-
| Approval | Local CLI or localhost UI |
|
|
24
|
-
| Writeback | Guarded one-row CRUD, fixed/frozen bounded sets, or app-owned executor |
|
|
25
|
+
| Evidence and replay | Local SQLite ledger by default; optional shared Postgres runtime store | Redacted shared chronology and references; full evidence payload stays local by default |
|
|
26
|
+
| Approval | Local CLI or localhost UI | Human-authenticated shared approval inbox; unavailable to MCP/Runner tokens |
|
|
27
|
+
| Writeback | Guarded one-row CRUD, fixed/frozen bounded sets, or app-owned executor | Durable approval/job/lease coordination; the local Runner rechecks and executes |
|
|
25
28
|
| MCP risk audit | Static local audit | Organization-wide continuous audit is future work |
|
|
26
29
|
| Identity | Local operator boundary | Workspace RBAC where configured; SAML and SCIM are future work |
|
|
27
30
|
| Operations | Customer-operated single node or bounded small fleet | Managed fleet remains Cloud work; no enterprise SLA in the current beta |
|
|
@@ -45,6 +48,10 @@ environment-variable wiring, and MCP client templates.
|
|
|
45
48
|
3. Keep contracts in Git and exercise proposal, approval, writeback, and replay.
|
|
46
49
|
4. Push the validated contract when the team needs a shared registry and review
|
|
47
50
|
surface.
|
|
51
|
+
5. Create a source-scoped Runner token, download a bundle, and verify the
|
|
52
|
+
registration heartbeat.
|
|
53
|
+
6. Sync one staging proposal, approve it in Cloud, and verify the local guarded
|
|
54
|
+
write plus linked receipt/replay chronology.
|
|
48
55
|
|
|
49
56
|
See [Cloud Push](cloud-push.md), [Runner Bundles](runner-bundles.md), and
|
|
50
57
|
[Cloud Mode](cloud-mode.md) for commands and deployment details. See
|
package/docs/release-notes.md
CHANGED
|
@@ -10,6 +10,54 @@ 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.4.123 (prepared, not published)
|
|
14
|
+
|
|
15
|
+
### Advisory capability-surface fitness lint
|
|
16
|
+
|
|
17
|
+
- `contract lint` now reports high-signal breadth-drift advisories for generic
|
|
18
|
+
query/predicate-style string arguments, capability density above eight on one
|
|
19
|
+
target, operation names that do not read as business actions, and structural
|
|
20
|
+
near-duplicates with identical or loosened arguments.
|
|
21
|
+
- Findings are deterministic across declaration order and share stable codes in
|
|
22
|
+
text, JSON, and SARIF. JSON/SARIF include reviewer-safe metrics and structural
|
|
23
|
+
differences without reading a database or environment values.
|
|
24
|
+
- Advisory-only lint still exits successfully by default. Teams may opt into a
|
|
25
|
+
CI policy gate with `--strict` or `--fail-on warning`; canonical validity and
|
|
26
|
+
runtime enforcement are unchanged.
|
|
27
|
+
|
|
28
|
+
Prepared package version: `@synapsor/runner@1.4.123`. Spec, DSL, and Cloud CLI
|
|
29
|
+
packages are unchanged.
|
|
30
|
+
|
|
31
|
+
## 1.4.122 (2026-07-16)
|
|
32
|
+
|
|
33
|
+
### Trusted principal scope and Cloud-linked authority
|
|
34
|
+
|
|
35
|
+
- Contracts may bind a reviewed target column to a required trusted principal
|
|
36
|
+
in addition to the existing tenant lock. Runner applies both predicates in
|
|
37
|
+
SQL and never accepts the principal value from model arguments.
|
|
38
|
+
- Same-tenant rows owned by another principal and cross-principal evidence,
|
|
39
|
+
proposal, receipt, and replay handles return the same generic miss as absent
|
|
40
|
+
or cross-tenant resources.
|
|
41
|
+
- PostgreSQL/MySQL live tests cover scoped read/propose/insert/update/delete,
|
|
42
|
+
aggregate and bounded-set operations, conflict/idempotency, compensation,
|
|
43
|
+
signed HTTP sessions, and generic denial behavior.
|
|
44
|
+
- In explicit `cloud_linked` mode, Cloud is authoritative for governance while
|
|
45
|
+
the local/shared Runner store remains the durable operational spool. An
|
|
46
|
+
idempotent outbox synchronizes bounded proposal/activity/result metadata;
|
|
47
|
+
full evidence, source rows, SQL details, replay payloads, and credentials
|
|
48
|
+
remain local.
|
|
49
|
+
- Cloud-linked approval and apply cannot fall back to local operator commands.
|
|
50
|
+
The trusted Runner still verifies the exact local contract, proposal hash,
|
|
51
|
+
tenant/principal guards, bounds, conflict checks, and receipt rules before
|
|
52
|
+
source mutation.
|
|
53
|
+
- A separate `@synapsor/cli@0.1.0-beta.1` package manages Cloud contracts,
|
|
54
|
+
projects, scoped credentials, Runner connections, proposal decisions, and
|
|
55
|
+
audit records. Runner keeps the `synapsor-runner` binary and local boundary.
|
|
56
|
+
|
|
57
|
+
Prepared package versions: `@synapsor/spec@1.4.2`,
|
|
58
|
+
`@synapsor/dsl@1.4.3`, `@synapsor/runner@1.4.122`, and
|
|
59
|
+
`@synapsor/cli@0.1.0-beta.1`.
|
|
60
|
+
|
|
13
61
|
## 1.4.121 (prepared, not published)
|
|
14
62
|
|
|
15
63
|
### Contract trust surface and bounded-set parser correctness
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# RFC 007: Trusted Principal Row Scope
|
|
2
|
+
|
|
3
|
+
Status: accepted for implementation
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
Runner already binds a trusted tenant and principal to a context, but the
|
|
8
|
+
principal is currently identity/audit metadata rather than a database row
|
|
9
|
+
predicate. Two users in one tenant can therefore share a tenant-scoped
|
|
10
|
+
capability even when each should see only rows assigned to that user.
|
|
11
|
+
|
|
12
|
+
## Canonical Shape
|
|
13
|
+
|
|
14
|
+
Add one optional subject field:
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"tenant_key": "hospital_id",
|
|
19
|
+
"principal_scope_key": "assigned_to"
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The value for `principal_scope_key` always comes from the capability context's
|
|
24
|
+
required `principal_binding`. The contract does not store a principal value and
|
|
25
|
+
the model does not supply one.
|
|
26
|
+
|
|
27
|
+
The DSL form is:
|
|
28
|
+
|
|
29
|
+
```sql
|
|
30
|
+
TENANT KEY hospital_id
|
|
31
|
+
PRINCIPAL SCOPE KEY assigned_to
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
No second representation is introduced. Existing contracts without the field
|
|
35
|
+
retain their current tenant-scoped semantics.
|
|
36
|
+
|
|
37
|
+
## Invariant
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
effective row authority = tenant predicate
|
|
41
|
+
AND principal predicate
|
|
42
|
+
AND reviewed fixed guards
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Principal scope is invalid without tenant scope or without a required trusted
|
|
46
|
+
principal binding. It cannot replace tenant scope, use a model argument, OR
|
|
47
|
+
with another predicate, default to match-all, or be bypassed dynamically. A
|
|
48
|
+
tenant-wide supervisor uses a separately reviewed capability without principal
|
|
49
|
+
scope.
|
|
50
|
+
|
|
51
|
+
## Enforcement
|
|
52
|
+
|
|
53
|
+
The principal predicate is a bound SQL parameter in every applicable read,
|
|
54
|
+
proposal, aggregate, bounded-set, CRUD, compensation, and apply-time query.
|
|
55
|
+
INSERT forces the reviewed column from trusted context. Executor envelopes carry
|
|
56
|
+
an immutable reviewed scope guard. The principal scope is included in proposal
|
|
57
|
+
integrity and writeback authority.
|
|
58
|
+
|
|
59
|
+
Cloud receives only the reviewed column/binding metadata and a stable safe
|
|
60
|
+
fingerprint. The local proposal retains the trusted value needed for SQL. A
|
|
61
|
+
Cloud job cannot substitute it; Runner verifies the job fingerprint, proposal
|
|
62
|
+
hash, and contract digest before mutation.
|
|
63
|
+
|
|
64
|
+
Evidence, proposal, receipt, activity, and replay lookups recheck tenant and
|
|
65
|
+
principal authority. Opaque handles are not bearer credentials. A same-tenant
|
|
66
|
+
cross-principal miss has the same public result as a cross-tenant or absent row,
|
|
67
|
+
and the query performs no separate existence probe.
|
|
68
|
+
|
|
69
|
+
## Compatibility And Release
|
|
70
|
+
|
|
71
|
+
The field changes `@synapsor/spec`, `@synapsor/dsl`, and Runner behavior. It is
|
|
72
|
+
covered by canonical normalization/digest tests, TypeScript/C++ round-trip
|
|
73
|
+
fixtures, PostgreSQL/MySQL live tests, and packed-package tests. The next Runner
|
|
74
|
+
release remains on the `1.4.x` line and targets `1.4.122` if unused; this RFC
|
|
75
|
+
does not justify a `1.5.0` jump.
|
package/docs/runner-bundles.md
CHANGED
|
@@ -11,6 +11,7 @@ Every bundle contains:
|
|
|
11
11
|
```text
|
|
12
12
|
synapsor.contract.json
|
|
13
13
|
synapsor.runner.json
|
|
14
|
+
synapsor.cloud.json
|
|
14
15
|
.env.example
|
|
15
16
|
README.md
|
|
16
17
|
mcp-client-examples/
|
|
@@ -43,9 +44,27 @@ set -a && . ./.env && set +a
|
|
|
43
44
|
npx -y -p @synapsor/runner synapsor-runner contract validate ./synapsor.contract.json
|
|
44
45
|
npx -y -p @synapsor/runner synapsor-runner config validate --config ./synapsor.runner.json
|
|
45
46
|
npx -y -p @synapsor/runner synapsor-runner tools preview --config ./synapsor.runner.json --store ./.synapsor/local.db
|
|
47
|
+
npx -y -p @synapsor/runner synapsor-runner cloud connect --config ./synapsor.cloud.json
|
|
46
48
|
npx -y -p @synapsor/runner synapsor-runner mcp serve --config ./synapsor.runner.json --store ./.synapsor/local.db
|
|
47
49
|
```
|
|
48
50
|
|
|
51
|
+
For proposal contracts, run the trusted worker in a separate
|
|
52
|
+
operator-controlled terminal:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
set -a && . ./.env && set +a
|
|
56
|
+
npx -y -p @synapsor/runner synapsor-runner start --config ./synapsor.runner.json --store ./.synapsor/local.db
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
After an MCP tool creates a local proposal, sync its reviewed diff and safe
|
|
60
|
+
references to Cloud:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npx -y -p @synapsor/runner synapsor-runner cloud sync latest \
|
|
64
|
+
--config ./synapsor.cloud.json \
|
|
65
|
+
--store ./.synapsor/local.db
|
|
66
|
+
```
|
|
67
|
+
|
|
49
68
|
For the OpenAI Agents SDK, use the included TypeScript examples. Their stdio
|
|
50
69
|
command enables `--alias-mode openai`; the Streamable HTTP example includes the
|
|
51
70
|
matching server command. Claude, Cursor, and generic templates use canonical
|
|
@@ -57,3 +76,8 @@ Approval and writeback remain outside the model-facing MCP tool surface. Use
|
|
|
57
76
|
The bundle's `.env.example` contains names and placeholders only. Fill it for a
|
|
58
77
|
local or staging source, source it into the shell or process that launches your
|
|
59
78
|
MCP client, and keep the resulting `.env` out of source control.
|
|
79
|
+
|
|
80
|
+
The Cloud `source_id` scopes the Runner token and job queue. The contract's
|
|
81
|
+
source alias selects the local database adapter. They are intentionally
|
|
82
|
+
different identifiers. Cloud jobs never contain a database URL or handler
|
|
83
|
+
secret.
|
|
@@ -132,6 +132,14 @@ Every served capability context must bind tenant and principal from
|
|
|
132
132
|
`HTTP_CLAIM`. Runner rejects an environment-bound contract mixed into this
|
|
133
133
|
server with `TRUSTED_CONTEXT_PROVIDER_CONFLICT` before tools are served.
|
|
134
134
|
|
|
135
|
+
For same-tenant per-user rows, declare `PRINCIPAL SCOPE KEY assigned_to` (or
|
|
136
|
+
the corresponding canonical `principal_scope_key`) on the capability. The
|
|
137
|
+
signed `sub` claim then becomes a bound SQL predicate composed with the signed
|
|
138
|
+
tenant claim. Session token fingerprint pinning prevents swapping identities
|
|
139
|
+
within an MCP session. `tools preview` displays both locks, and
|
|
140
|
+
`corepack pnpm test:principal-scope` proves two concurrent principals cannot
|
|
141
|
+
read each other's rows or evidence handles on Postgres and MySQL.
|
|
142
|
+
|
|
135
143
|
Use an HTTPS JWKS URL from an allowlisted identity system. Runner rejects
|
|
136
144
|
redirects, oversized responses, unknown algorithms, `none`, missing/unknown
|
|
137
145
|
`kid`, bad issuer/audience, expired/not-yet-valid tokens, and private JWK
|
|
@@ -50,6 +50,20 @@ Trusted context comes from local configuration, environment bindings, or Cloud
|
|
|
50
50
|
session context in Cloud mode. Tenant, principal, and authorization scope must
|
|
51
51
|
not be accepted from the model as authority.
|
|
52
52
|
|
|
53
|
+
A capability may declare `principal_scope_key` (DSL: `PRINCIPAL SCOPE KEY`) to
|
|
54
|
+
narrow rows inside a tenant. Runner then adds both fixed, parameterized guards:
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
tenant_key = verified tenant AND principal_scope_key = verified principal
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
This applies to single-row reads and writes, aggregates, bounded sets, inserts,
|
|
61
|
+
deletes, executor envelopes, and reviewed compensation. Missing principal
|
|
62
|
+
context fails closed. A principal lock never replaces tenant scope and has no
|
|
63
|
+
dynamic supervisor bypass. Use a separately reviewed tenant-wide capability
|
|
64
|
+
for broader roles. RLS and restricted database roles remain valuable defense in
|
|
65
|
+
depth; the contract does not replace them.
|
|
66
|
+
|
|
53
67
|
Proposal, evidence, and replay handles are references, not bearer credentials.
|
|
54
68
|
Before returning a local MCP resource, Runner resolves the resource's owning
|
|
55
69
|
capability context again and requires both tenant and principal to match the
|
|
@@ -76,7 +90,7 @@ outside the model-facing MCP server and verifies:
|
|
|
76
90
|
- local approval state;
|
|
77
91
|
- proposal and job digests;
|
|
78
92
|
- target schema/table;
|
|
79
|
-
- primary-key and
|
|
93
|
+
- primary-key, tenant, and declared principal-row guards;
|
|
80
94
|
- allowed mutable columns;
|
|
81
95
|
- conflict/version guard;
|
|
82
96
|
- idempotency key;
|
|
@@ -128,7 +142,10 @@ credentials, approval tools, commit tools, or controls that widen reviewed
|
|
|
128
142
|
tables/columns.
|
|
129
143
|
|
|
130
144
|
Contract lint and tests are review aids rather than a proof of complete
|
|
131
|
-
security.
|
|
145
|
+
security. Capability breadth can still drift as narrow tools accumulate;
|
|
146
|
+
surface-fitness lint makes high-signal generic, dense, poorly named, and
|
|
147
|
+
near-duplicate surfaces visible for review but does not enforce good design.
|
|
148
|
+
Scoped report exports omit evidence rows and kept-out values and are
|
|
132
149
|
tamper-evident when their digest/signature verifies; local SQLite is not an
|
|
133
150
|
immutable compliance service. Graduated-trust evaluation is operator-only,
|
|
134
151
|
disabled by default, and can recommend/export a separate artifact but cannot
|
|
@@ -4,6 +4,7 @@ CREATE TABLE invoices (
|
|
|
4
4
|
status varchar(64) NOT NULL,
|
|
5
5
|
late_fee_cents integer NOT NULL,
|
|
6
6
|
waiver_reason varchar(500),
|
|
7
|
+
assigned_to varchar(128) NOT NULL DEFAULT 'case_manager_a',
|
|
7
8
|
updated_at datetime(6) NOT NULL
|
|
8
9
|
);
|
|
9
10
|
|
|
@@ -41,7 +42,10 @@ INSERT INTO guard_crud_items (id, tenant_id, request_id, value_cents, version) V
|
|
|
41
42
|
ALTER TABLE guard_crud_items AUTO_INCREMENT = 2001;
|
|
42
43
|
|
|
43
44
|
INSERT INTO invoices (id, tenant_id, status, late_fee_cents, waiver_reason, updated_at) VALUES
|
|
44
|
-
('MYSQL-ACME', 'acme', 'overdue', 700, NULL, '2026-07-12 12:00:00.000000')
|
|
45
|
+
('MYSQL-ACME', 'acme', 'overdue', 700, NULL, '2026-07-12 12:00:00.000000'),
|
|
46
|
+
('MYSQL-PRINCIPAL-A', 'hospital_a', 'principal_fixture', 100, NULL, '2026-07-12 12:00:00.000000'),
|
|
47
|
+
('MYSQL-PRINCIPAL-B', 'hospital_a', 'principal_fixture', 200, NULL, '2026-07-12 12:00:00.000000');
|
|
48
|
+
UPDATE invoices SET assigned_to = 'case_manager_b' WHERE id = 'MYSQL-PRINCIPAL-B';
|
|
45
49
|
|
|
46
50
|
CREATE VIEW slow_invoices AS
|
|
47
51
|
SELECT invoices.*, SLEEP(0.35) AS synthetic_delay
|
|
@@ -4,6 +4,7 @@ CREATE TABLE public.invoices (
|
|
|
4
4
|
status text NOT NULL,
|
|
5
5
|
late_fee_cents integer NOT NULL,
|
|
6
6
|
waiver_reason text,
|
|
7
|
+
assigned_to text NOT NULL DEFAULT 'case_manager_a',
|
|
7
8
|
updated_at timestamptz NOT NULL
|
|
8
9
|
);
|
|
9
10
|
|
|
@@ -73,7 +74,10 @@ INSERT INTO public.invoices (id, tenant_id, status, late_fee_cents, waiver_reaso
|
|
|
73
74
|
('INV-DEAD-DISCARD', 'acme', 'overdue', 1750, NULL, '2026-07-12T12:00:00Z'),
|
|
74
75
|
('INV-KILL-BEFORE', 'acme', 'overdue', 1200, NULL, '2026-07-12T12:00:00Z'),
|
|
75
76
|
('INV-KILL-DURING', 'acme', 'overdue', 1500, NULL, '2026-07-12T12:00:00Z'),
|
|
76
|
-
('INV-KILL-AFTER', 'acme', 'overdue', 1800, NULL, '2026-07-12T12:00:00Z')
|
|
77
|
+
('INV-KILL-AFTER', 'acme', 'overdue', 1800, NULL, '2026-07-12T12:00:00Z'),
|
|
78
|
+
('INV-PRINCIPAL-A', 'hospital_a', 'principal_fixture', 100, NULL, '2026-07-12T12:00:00Z'),
|
|
79
|
+
('INV-PRINCIPAL-B', 'hospital_a', 'principal_fixture', 200, NULL, '2026-07-12T12:00:00Z');
|
|
80
|
+
UPDATE public.invoices SET assigned_to = 'case_manager_b' WHERE id = 'INV-PRINCIPAL-B';
|
|
77
81
|
|
|
78
82
|
CREATE FUNCTION public.synthetic_pool_delay() RETURNS integer
|
|
79
83
|
LANGUAGE plpgsql VOLATILE AS $$
|
|
@@ -272,10 +272,12 @@ Real push uses your Cloud env vars:
|
|
|
272
272
|
|
|
273
273
|
```bash
|
|
274
274
|
export SYNAPSOR_CLOUD_BASE_URL="https://api.synapsor.ai"
|
|
275
|
-
export
|
|
276
|
-
export
|
|
275
|
+
export SYNAPSOR_API_KEY="<contracts-write-service-key>"
|
|
276
|
+
export SYNAPSOR_PROJECT_ID="<project_id>"
|
|
277
|
+
export SYNAPSOR_SOURCE_ID="<cloud_source_id>"
|
|
277
278
|
|
|
278
279
|
synapsor-runner cloud push examples/support-plan-credit/synapsor.contract.json \
|
|
280
|
+
--workspace "$SYNAPSOR_PROJECT_ID" \
|
|
279
281
|
--name support-plan-credit
|
|
280
282
|
```
|
|
281
283
|
|
|
@@ -283,11 +285,11 @@ After push, Cloud can return the registered version and a runner bundle with
|
|
|
283
285
|
placeholder env names:
|
|
284
286
|
|
|
285
287
|
```bash
|
|
286
|
-
curl -H "Authorization: Bearer $
|
|
287
|
-
"$SYNAPSOR_CLOUD_BASE_URL/v1/control/projects/$
|
|
288
|
+
curl -H "Authorization: Bearer $SYNAPSOR_API_KEY" \
|
|
289
|
+
"$SYNAPSOR_CLOUD_BASE_URL/v1/control/projects/$SYNAPSOR_PROJECT_ID/agent-contracts/<contract_id>/versions/<version_id>"
|
|
288
290
|
|
|
289
|
-
curl -H "Authorization: Bearer $
|
|
290
|
-
"$SYNAPSOR_CLOUD_BASE_URL/v1/control/projects/$
|
|
291
|
+
curl -H "Authorization: Bearer $SYNAPSOR_API_KEY" \
|
|
292
|
+
"$SYNAPSOR_CLOUD_BASE_URL/v1/control/projects/$SYNAPSOR_PROJECT_ID/agent-contracts/<contract_id>/versions/<version_id>/runner-bundle?download=1&source_id=$SYNAPSOR_SOURCE_ID" \
|
|
291
293
|
-o support-plan-credit-runner-bundle.zip
|
|
292
294
|
```
|
|
293
295
|
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
{
|
|
2
|
+
"spec_version": "0.1",
|
|
3
|
+
"kind": "SynapsorContract",
|
|
4
|
+
"metadata": {
|
|
5
|
+
"name": "capability surface fitness fixture",
|
|
6
|
+
"description": "Exercises advisory capability-breadth review without changing contract validity.",
|
|
7
|
+
"version": "1.0.0"
|
|
8
|
+
},
|
|
9
|
+
"resources": [
|
|
10
|
+
{
|
|
11
|
+
"name": "billing_invoices",
|
|
12
|
+
"engine": "postgres",
|
|
13
|
+
"schema": "public",
|
|
14
|
+
"table": "invoices",
|
|
15
|
+
"type": "table",
|
|
16
|
+
"primary_key": "id",
|
|
17
|
+
"tenant_key": "tenant_id",
|
|
18
|
+
"conflict_key": "updated_at"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"contexts": [
|
|
22
|
+
{
|
|
23
|
+
"name": "billing_operator",
|
|
24
|
+
"bindings": [
|
|
25
|
+
{
|
|
26
|
+
"name": "tenant_id",
|
|
27
|
+
"source": "environment",
|
|
28
|
+
"key": "SYNAPSOR_TENANT_ID",
|
|
29
|
+
"required": true
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"tenant_binding": "tenant_id"
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"capabilities": [
|
|
36
|
+
{
|
|
37
|
+
"name": "billing.execute",
|
|
38
|
+
"description": "Look up one reviewed invoice reference.",
|
|
39
|
+
"returns_hint": "One allowlisted invoice row.",
|
|
40
|
+
"kind": "read",
|
|
41
|
+
"context": "billing_operator",
|
|
42
|
+
"source": "local_postgres",
|
|
43
|
+
"subject": { "resource": "billing_invoices" },
|
|
44
|
+
"args": {
|
|
45
|
+
"filter": { "type": "string", "required": true, "max_length": 128 }
|
|
46
|
+
},
|
|
47
|
+
"lookup": { "id_from_arg": "filter" },
|
|
48
|
+
"visible_fields": ["id", "tenant_id", "status"],
|
|
49
|
+
"kept_out_fields": ["card_token", "internal_note"],
|
|
50
|
+
"evidence": { "required": true, "query_audit": true },
|
|
51
|
+
"max_rows": 1
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"name": "billing.inspect_invoice_primary",
|
|
55
|
+
"description": "Inspect one primary invoice.",
|
|
56
|
+
"returns_hint": "One allowlisted invoice row.",
|
|
57
|
+
"kind": "read",
|
|
58
|
+
"context": "billing_operator",
|
|
59
|
+
"source": "local_postgres",
|
|
60
|
+
"subject": { "resource": "billing_invoices" },
|
|
61
|
+
"args": {
|
|
62
|
+
"invoice_id": { "type": "string", "required": true, "max_length": 128 }
|
|
63
|
+
},
|
|
64
|
+
"lookup": { "id_from_arg": "invoice_id" },
|
|
65
|
+
"visible_fields": ["id", "tenant_id", "status"],
|
|
66
|
+
"kept_out_fields": ["card_token", "internal_note"],
|
|
67
|
+
"evidence": { "required": true, "query_audit": true },
|
|
68
|
+
"max_rows": 1
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"name": "billing.inspect_invoice_copy",
|
|
72
|
+
"description": "Inspect the same reviewed invoice shape for duplicate detection.",
|
|
73
|
+
"returns_hint": "One allowlisted invoice row.",
|
|
74
|
+
"kind": "read",
|
|
75
|
+
"context": "billing_operator",
|
|
76
|
+
"source": "local_postgres",
|
|
77
|
+
"subject": { "resource": "billing_invoices" },
|
|
78
|
+
"args": {
|
|
79
|
+
"invoice_id": { "type": "string", "required": true, "max_length": 128 }
|
|
80
|
+
},
|
|
81
|
+
"lookup": { "id_from_arg": "invoice_id" },
|
|
82
|
+
"visible_fields": ["id", "tenant_id", "status"],
|
|
83
|
+
"kept_out_fields": ["card_token", "internal_note"],
|
|
84
|
+
"evidence": { "required": true, "query_audit": true },
|
|
85
|
+
"max_rows": 1
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"name": "billing.inspect_invoice_secondary",
|
|
89
|
+
"description": "Inspect one secondary invoice.",
|
|
90
|
+
"returns_hint": "One allowlisted invoice row.",
|
|
91
|
+
"kind": "read",
|
|
92
|
+
"context": "billing_operator",
|
|
93
|
+
"source": "local_postgres",
|
|
94
|
+
"subject": { "resource": "billing_invoices" },
|
|
95
|
+
"args": {
|
|
96
|
+
"secondary_invoice_id": { "type": "string", "required": true, "max_length": 128 }
|
|
97
|
+
},
|
|
98
|
+
"lookup": { "id_from_arg": "secondary_invoice_id" },
|
|
99
|
+
"visible_fields": ["id", "tenant_id", "status"],
|
|
100
|
+
"kept_out_fields": ["card_token", "internal_note"],
|
|
101
|
+
"evidence": { "required": true, "query_audit": true },
|
|
102
|
+
"max_rows": 1
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"name": "billing.inspect_invoice_archived",
|
|
106
|
+
"description": "Inspect one archived invoice.",
|
|
107
|
+
"returns_hint": "One allowlisted invoice row.",
|
|
108
|
+
"kind": "read",
|
|
109
|
+
"context": "billing_operator",
|
|
110
|
+
"source": "local_postgres",
|
|
111
|
+
"subject": { "resource": "billing_invoices" },
|
|
112
|
+
"args": {
|
|
113
|
+
"archived_invoice_id": { "type": "string", "required": true, "max_length": 128 }
|
|
114
|
+
},
|
|
115
|
+
"lookup": { "id_from_arg": "archived_invoice_id" },
|
|
116
|
+
"visible_fields": ["id", "tenant_id", "status"],
|
|
117
|
+
"kept_out_fields": ["card_token", "internal_note"],
|
|
118
|
+
"evidence": { "required": true, "query_audit": true },
|
|
119
|
+
"max_rows": 1
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"name": "billing.inspect_invoice_disputed",
|
|
123
|
+
"description": "Inspect one disputed invoice.",
|
|
124
|
+
"returns_hint": "One allowlisted invoice row.",
|
|
125
|
+
"kind": "read",
|
|
126
|
+
"context": "billing_operator",
|
|
127
|
+
"source": "local_postgres",
|
|
128
|
+
"subject": { "resource": "billing_invoices" },
|
|
129
|
+
"args": {
|
|
130
|
+
"disputed_invoice_id": { "type": "string", "required": true, "max_length": 128 }
|
|
131
|
+
},
|
|
132
|
+
"lookup": { "id_from_arg": "disputed_invoice_id" },
|
|
133
|
+
"visible_fields": ["id", "tenant_id", "status"],
|
|
134
|
+
"kept_out_fields": ["card_token", "internal_note"],
|
|
135
|
+
"evidence": { "required": true, "query_audit": true },
|
|
136
|
+
"max_rows": 1
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"name": "billing.inspect_invoice_overdue",
|
|
140
|
+
"description": "Inspect one overdue invoice.",
|
|
141
|
+
"returns_hint": "One allowlisted invoice row.",
|
|
142
|
+
"kind": "read",
|
|
143
|
+
"context": "billing_operator",
|
|
144
|
+
"source": "local_postgres",
|
|
145
|
+
"subject": { "resource": "billing_invoices" },
|
|
146
|
+
"args": {
|
|
147
|
+
"overdue_invoice_id": { "type": "string", "required": true, "max_length": 128 }
|
|
148
|
+
},
|
|
149
|
+
"lookup": { "id_from_arg": "overdue_invoice_id" },
|
|
150
|
+
"visible_fields": ["id", "tenant_id", "status"],
|
|
151
|
+
"kept_out_fields": ["card_token", "internal_note"],
|
|
152
|
+
"evidence": { "required": true, "query_audit": true },
|
|
153
|
+
"max_rows": 1
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"name": "billing.inspect_invoice_paid",
|
|
157
|
+
"description": "Inspect one paid invoice.",
|
|
158
|
+
"returns_hint": "One allowlisted invoice row.",
|
|
159
|
+
"kind": "read",
|
|
160
|
+
"context": "billing_operator",
|
|
161
|
+
"source": "local_postgres",
|
|
162
|
+
"subject": { "resource": "billing_invoices" },
|
|
163
|
+
"args": {
|
|
164
|
+
"paid_invoice_id": { "type": "string", "required": true, "max_length": 128 }
|
|
165
|
+
},
|
|
166
|
+
"lookup": { "id_from_arg": "paid_invoice_id" },
|
|
167
|
+
"visible_fields": ["id", "tenant_id", "status"],
|
|
168
|
+
"kept_out_fields": ["card_token", "internal_note"],
|
|
169
|
+
"evidence": { "required": true, "query_audit": true },
|
|
170
|
+
"max_rows": 1
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"name": "billing.inspect_invoice_voided",
|
|
174
|
+
"description": "Inspect one voided invoice.",
|
|
175
|
+
"returns_hint": "One allowlisted invoice row.",
|
|
176
|
+
"kind": "read",
|
|
177
|
+
"context": "billing_operator",
|
|
178
|
+
"source": "local_postgres",
|
|
179
|
+
"subject": { "resource": "billing_invoices" },
|
|
180
|
+
"args": {
|
|
181
|
+
"voided_invoice_id": { "type": "string", "required": true, "max_length": 128 }
|
|
182
|
+
},
|
|
183
|
+
"lookup": { "id_from_arg": "voided_invoice_id" },
|
|
184
|
+
"visible_fields": ["id", "tenant_id", "status"],
|
|
185
|
+
"kept_out_fields": ["card_token", "internal_note"],
|
|
186
|
+
"evidence": { "required": true, "query_audit": true },
|
|
187
|
+
"max_rows": 1
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
CREATE AGENT CONTEXT care_session
|
|
2
|
+
BIND hospital_id FROM HTTP_CLAIM hospital_id REQUIRED
|
|
3
|
+
BIND principal FROM HTTP_CLAIM sub REQUIRED
|
|
4
|
+
TENANT BINDING hospital_id
|
|
5
|
+
PRINCIPAL BINDING principal
|
|
6
|
+
END
|
|
7
|
+
|
|
8
|
+
CREATE CAPABILITY care.inspect_assigned_patient
|
|
9
|
+
DESCRIPTION 'Inspect one patient assigned to the authenticated case manager within the trusted hospital.'
|
|
10
|
+
RETURNS HINT 'Returns reviewed patient fields and evidence only when both tenant and assignee locks match.'
|
|
11
|
+
USING CONTEXT care_session
|
|
12
|
+
SOURCE local_postgres
|
|
13
|
+
ON public.patients
|
|
14
|
+
PRIMARY KEY id
|
|
15
|
+
TENANT KEY hospital_id
|
|
16
|
+
PRINCIPAL SCOPE KEY assigned_to
|
|
17
|
+
LOOKUP patient_id BY id
|
|
18
|
+
ARG patient_id STRING REQUIRED MAX LENGTH 128 DESCRIPTION 'Reviewed patient identifier.'
|
|
19
|
+
ALLOW READ id, hospital_id, display_name, care_status, updated_at
|
|
20
|
+
KEEP OUT assigned_to, diagnosis_notes, insurance_member_id
|
|
21
|
+
REQUIRE EVIDENCE
|
|
22
|
+
MAX ROWS 1
|
|
23
|
+
END
|