@creatornader/agent-bridge 0.4.0 → 0.5.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/CHANGELOG.md +73 -1
- package/README.md +73 -7
- package/ROADMAP.md +29 -0
- package/SECURITY.md +23 -0
- package/SKILL.md +5 -1
- package/dist/archive.d.ts +2 -2
- package/dist/{bridge-service-YTjGr91A.d.ts → bridge-service-CNBKdYXh.d.ts} +2 -1
- package/dist/{bridge-store-Boaj-ZbO.d.ts → bridge-store-ByJ6CaJk.d.ts} +1 -0
- package/dist/bridge.d.ts +15 -8
- package/dist/bridge.js +10 -8
- package/dist/{chunk-PDIHDBP3.js → chunk-7VJI2MXI.js} +116 -26
- package/dist/chunk-7VJI2MXI.js.map +1 -0
- package/dist/{chunk-4VB44KJU.js → chunk-AWE3DFZV.js} +3 -2
- package/dist/chunk-AWE3DFZV.js.map +1 -0
- package/dist/{chunk-IHZFI7TH.js → chunk-E4OWHGUE.js} +5 -2
- package/dist/chunk-E4OWHGUE.js.map +1 -0
- package/dist/{chunk-SYGONYMM.js → chunk-HUHG7XTB.js} +6 -5
- package/dist/chunk-HUHG7XTB.js.map +1 -0
- package/dist/{chunk-DQOZJJZ2.js → chunk-IVG4XWWS.js} +5 -5
- package/dist/{chunk-DQOZJJZ2.js.map → chunk-IVG4XWWS.js.map} +1 -1
- package/dist/{chunk-3YHVUPU2.js → chunk-P635FAN4.js} +4 -4
- package/dist/chunk-P635FAN4.js.map +1 -0
- package/dist/{chunk-6BTJE6D4.js → chunk-QNXHDOJ5.js} +56 -23
- package/dist/chunk-QNXHDOJ5.js.map +1 -0
- package/dist/{chunk-MCKKVNCE.js → chunk-QPC6JHA3.js} +5 -5
- package/dist/{chunk-SV3SVBYS.js → chunk-SN7PIVDF.js} +2 -2
- package/dist/{chunk-2DFNOLMO.js → chunk-XDNT6ZNZ.js} +9 -6
- package/dist/chunk-XDNT6ZNZ.js.map +1 -0
- package/dist/{chunk-LAHNQZDN.js → chunk-Z6JP6MYD.js} +2 -2
- package/dist/cli.js +9 -9
- package/dist/cli.js.map +1 -1
- package/dist/client-runtime.d.ts +3 -3
- package/dist/client-runtime.js +6 -6
- package/dist/contracts/generate.js +1 -1
- package/dist/dr.js +3 -3
- package/dist/{gateway-BQHpV-MZ.d.ts → gateway-CW-61lvs.d.ts} +2 -2
- package/dist/gateway-main.js +5 -5
- package/dist/gateway.d.ts +3 -3
- package/dist/gateway.js +3 -3
- package/dist/index.js +7 -7
- package/dist/{postgres-bridge-store-WASumj26.d.ts → postgres-bridge-store-C9KG9Z69.d.ts} +1 -1
- package/dist/postgres-production-preflight.d.ts +32 -0
- package/dist/postgres-production-preflight.js +249 -0
- package/dist/postgres-production-preflight.js.map +1 -0
- package/dist/server.js +7 -7
- package/dist/{sqlite-edge-store-Bc15DvpX.d.ts → sqlite-edge-store-CdMIOrho.d.ts} +1 -1
- package/dist/sqlite.d.ts +3 -3
- package/dist/sqlite.js +2 -2
- package/docs/architecture-v2.md +36 -5
- package/docs/deployment.md +144 -0
- package/llms.txt +2 -1
- package/openapi/agent-bridge-v2.openapi.json +5 -1
- package/package.json +5 -1
- package/schemas/agent-bridge-v2.capabilities.json +2 -1
- package/schemas/agent-bridge-v2.mcp.json +5 -1
- package/schemas/agent-bridge-v2.schema.json +4 -0
- package/sql/migrations/007_runtime_role.sql +8 -4
- package/sql/migrations/013_row_isolation.sql +71 -8
- package/sql/migrations/014_owner_control_plane.sql +124 -31
- package/sql/migrations/015_portable_archives.sql +143 -41
- package/sql/migrations/016_native_dr_fence.sql +102 -35
- package/sql/migrations/018_endpoint_migration_challenges.sql +36 -10
- package/sql/migrations/019_endpoint_migration_same_successor.sql +34 -10
- package/sql/migrations/020_managed_authority_compat.sql +698 -0
- package/sql/migrations/021_native_backup_reader.sql +421 -0
- package/sql/setup.sql +1 -3
- package/dist/chunk-2DFNOLMO.js.map +0 -1
- package/dist/chunk-3YHVUPU2.js.map +0 -1
- package/dist/chunk-4VB44KJU.js.map +0 -1
- package/dist/chunk-6BTJE6D4.js.map +0 -1
- package/dist/chunk-IHZFI7TH.js.map +0 -1
- package/dist/chunk-PDIHDBP3.js.map +0 -1
- package/dist/chunk-SYGONYMM.js.map +0 -1
- /package/dist/{chunk-MCKKVNCE.js.map → chunk-QPC6JHA3.js.map} +0 -0
- /package/dist/{chunk-SV3SVBYS.js.map → chunk-SN7PIVDF.js.map} +0 -0
- /package/dist/{chunk-LAHNQZDN.js.map → chunk-Z6JP6MYD.js.map} +0 -0
package/docs/deployment.md
CHANGED
|
@@ -119,6 +119,26 @@ Keep these authorities separate:
|
|
|
119
119
|
| Archive operator | Portable PostgreSQL archive operations | Never |
|
|
120
120
|
| Backup and restore | Native DR backup or restore | Never |
|
|
121
121
|
|
|
122
|
+
The schema owner may be a true PostgreSQL superuser or a managed-provider
|
|
123
|
+
administrator with `CREATEROLE` and the database privileges reported by the production
|
|
124
|
+
preflight. `CREATEROLE` is sufficient for migrations. Native backup requires the
|
|
125
|
+
schema owner to be a true superuser or hold `BYPASSRLS`. A restore target validates the
|
|
126
|
+
intentionally suspended restored schema-owner shell through its superuser session. Migrations create internal
|
|
127
|
+
roles with safe defaults and reject unsafe preexisting roles instead of trying to demote
|
|
128
|
+
them. PostgreSQL 16 and newer may store the schema owner's effective admin, inherit,
|
|
129
|
+
and set authority in two physical grant rows. A native DR restore may recreate it as one
|
|
130
|
+
combined bootstrap-superuser grant. Agent Bridge validates both forms and their
|
|
131
|
+
effective authority. The restricted runtime login must never receive either schema-owner
|
|
132
|
+
authority or `BYPASSRLS`.
|
|
133
|
+
|
|
134
|
+
Migration 021 creates a database-derived `NOLOGIN` backup-reader role. It receives
|
|
135
|
+
`SELECT` on Agent Bridge tables and sequences and is granted only to the schema owner.
|
|
136
|
+
This lets a managed schema owner retain `BYPASSRLS` while producing a complete native
|
|
137
|
+
backup. Do not grant the reader to the runtime login, control members, archive members,
|
|
138
|
+
or another external principal. Native backup and restore require this attestation.
|
|
139
|
+
Gateway runtime readiness does not, so an owner-only backup fault cannot stop normal
|
|
140
|
+
gateway traffic.
|
|
141
|
+
|
|
122
142
|
Use the platform's secret manager or private mounted secret files. Do not put raw
|
|
123
143
|
passwords, tokens, or complete database URLs in image layers, source control, command
|
|
124
144
|
arguments, deployment logs, or shared client config. Rotate each authority on its own
|
|
@@ -127,6 +147,130 @@ schedule and after any suspected exposure.
|
|
|
127
147
|
The runtime database password must contain at least 32 characters. The bootstrap job
|
|
128
148
|
fails before role creation or alteration when the mounted value is shorter.
|
|
129
149
|
|
|
150
|
+
## Fly.io reference hosting
|
|
151
|
+
|
|
152
|
+
[`deploy/fly.toml`](../deploy/fly.toml) is the maintained Fly gateway contract. It has
|
|
153
|
+
no app name, region, or release command. The operator chooses the account, app, region,
|
|
154
|
+
PostgreSQL service, and public hostname. Fly terminates HTTPS and forwards to port
|
|
155
|
+
8787. The service keeps at least one `shared-cpu-1x` machine with 512 MB running, checks
|
|
156
|
+
`GET /readyz`, and gives the Node process 30 seconds after SIGTERM to close listeners
|
|
157
|
+
and database pools.
|
|
158
|
+
|
|
159
|
+
Run the repository check before any Fly action:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
npm run preflight:fly -- --json
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
The local check verifies Node, the Dockerfile, and the static Fly contract. If the app
|
|
166
|
+
already exists, add its name:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
npm run preflight:fly -- --app <app> --json
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
With `--app`, the command invokes only read-only Fly commands. It observes the active
|
|
173
|
+
account, app status, effective config, machines, and secret names. Its JSON includes
|
|
174
|
+
names and check results but never environment-variable values. A passing local check
|
|
175
|
+
does not prove that an external resource exists or that deployment is safe.
|
|
176
|
+
|
|
177
|
+
The long-running Fly machine may receive only the restricted
|
|
178
|
+
`AGENT_BRIDGE_RUNTIME_DATABASE_URL` database authority. Do not give it
|
|
179
|
+
`AGENT_BRIDGE_DATABASE_URL`, `AGENT_BRIDGE_OPERATOR_DATABASE_URL`,
|
|
180
|
+
`AGENT_BRIDGE_RUNTIME_PASSWORD`, a client bearer token, or a Supabase client key. The
|
|
181
|
+
preflight rejects those names. Keep schema-owner, control-operator, archive, and DR
|
|
182
|
+
authority in separate operator jobs.
|
|
183
|
+
|
|
184
|
+
### Operator order and gates
|
|
185
|
+
|
|
186
|
+
The following actions change external state and require an operator gate. The
|
|
187
|
+
repository does not perform them:
|
|
188
|
+
|
|
189
|
+
1. Create or select the Fly app and PostgreSQL authority. Keep PostgreSQL private.
|
|
190
|
+
2. Run the read-only production database preflight before any backup or migration:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
AGENT_BRIDGE_DATABASE_URL="..." npm run preflight:postgres:production -- --json --require-ssl
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The command opens a read-only transaction and reports only nonsecret capability
|
|
197
|
+
and schema facts. `authority.roles` reports migration authority. `authority.native_dr`
|
|
198
|
+
reports whether the source schema owner is a true superuser or has `BYPASSRLS`. A
|
|
199
|
+
`CREATEROLE` role can pass the migration check while failing the native DR check.
|
|
200
|
+
The command also checks supported server majors, migration-ledger drift, derived-role
|
|
201
|
+
collisions, and the legacy import shape. The PostgreSQL 15 through 18 test matrix
|
|
202
|
+
separately applies the full migration plan as both a superuser and a
|
|
203
|
+
managed-provider-style `CREATEROLE` administrator. `--require-ssl` is required for a
|
|
204
|
+
public database endpoint. The preflight cannot prove that later DDL will succeed. For
|
|
205
|
+
a session pooler, it checks the client TLS socket because PostgreSQL reports the
|
|
206
|
+
pooler's separate database connection.
|
|
207
|
+
3. Take and verify a native DR backup when upgrading an existing authority.
|
|
208
|
+
4. Run `agent-bridge migrate` once in an isolated job with
|
|
209
|
+
`AGENT_BRIDGE_DATABASE_URL`. Drain old gateways first when the migration notes
|
|
210
|
+
require it.
|
|
211
|
+
5. Run [`deploy/bootstrap-runtime.sql`](../deploy/bootstrap-runtime.sql) through a
|
|
212
|
+
separate trusted PostgreSQL session. Supply the schema-owner connection and the
|
|
213
|
+
runtime password to that job only. The bootstrap is transactional and changes an
|
|
214
|
+
existing login only when its attributes, ownership, and membership graph already
|
|
215
|
+
match the recorded runtime contract. It never adopts a restored `NOLOGIN` shell.
|
|
216
|
+
6. Construct the restricted runtime URL, set only
|
|
217
|
+
`AGENT_BRIDGE_RUNTIME_DATABASE_URL` on the Fly app, and run the app-aware read-only
|
|
218
|
+
preflight.
|
|
219
|
+
7. Deploy the immutable image with `fly deploy --config deploy/fly.toml --app <app>`.
|
|
220
|
+
Require `/readyz` before routing clients.
|
|
221
|
+
8. Run authenticated capability and status probes, then publish, pull, claim, and
|
|
222
|
+
settle a disposable targeted delivery.
|
|
223
|
+
9. Move clients only after the gateway proof passes. Preserve every source edge store
|
|
224
|
+
until its outbox is empty.
|
|
225
|
+
|
|
226
|
+
App creation, database provisioning, secret writes, migration, bootstrap, deployment,
|
|
227
|
+
traffic changes, and client cutover are all gated external actions. The preflight is
|
|
228
|
+
read-only and cannot authorize any of them.
|
|
229
|
+
|
|
230
|
+
### Fly rollback boundary
|
|
231
|
+
|
|
232
|
+
Before migration, stop a failed deployment and keep the prior image and authority. A
|
|
233
|
+
failed image rollout after a compatible migration can return traffic to the prior
|
|
234
|
+
image only if that image accepts the current schema. Never assume that an older image
|
|
235
|
+
can run against newer migrations.
|
|
236
|
+
|
|
237
|
+
After clients publish through the new gateway, preserve both authorities and every
|
|
238
|
+
edge database. Stop the cutover, drain queued outboxes, and reconcile authoritative
|
|
239
|
+
messages before choosing one authority. If schema or data recovery is required,
|
|
240
|
+
restore the verified native DR bundle into a fresh target and switch authority once.
|
|
241
|
+
Do not run the source and restored database as active authorities at the same time.
|
|
242
|
+
|
|
243
|
+
The manual `gateway production proof` GitHub Actions workflow runs this acceptance
|
|
244
|
+
proof against an existing HTTPS gateway. GitHub restricts it to `main`, and every job
|
|
245
|
+
uses the approval-protected `agent-bridge-production-proof` environment. Configure
|
|
246
|
+
`PROOF_SENDER_TOKEN`, `PROOF_RECEIVER_TOKEN`, `PROOF_HOST_SALT`, and `FLY_API_TOKEN` as
|
|
247
|
+
environment secrets. The sender and receiver credentials must bind the requested
|
|
248
|
+
workspace to the dedicated `proof-sender` and `proof-receiver` principals. The
|
|
249
|
+
sender credential needs `messages:write` and `messages:read`. The receiver needs
|
|
250
|
+
`messages:read`, `deliveries:claim`, `deliveries:settle`, and `deliveries:read`.
|
|
251
|
+
|
|
252
|
+
Run the workflow only when the Fly app has exactly one gateway machine. The cycle job
|
|
253
|
+
restarts that machine and queries the Fly Machines API before and after. It fails
|
|
254
|
+
unless the machine ID stays fixed, the runtime `instance_id` changes, the machine
|
|
255
|
+
returns to `started`, and `/readyz` succeeds. This changes production compute and
|
|
256
|
+
requires environment approval. It does not migrate the database or change secrets.
|
|
257
|
+
|
|
258
|
+
The sender first uses an unreachable loopback origin to force a leased publication
|
|
259
|
+
into its private SQLite outbox. It then synchronizes that row to the supplied gateway
|
|
260
|
+
and repeats the publication with the same idempotency key. A separate host finds the
|
|
261
|
+
exact message through `proof-receiver`, claims its delivery, and acknowledges it.
|
|
262
|
+
After the Fly machine cycle, the verifier uses a fresh instance key, edge database,
|
|
263
|
+
and cursor. It reads the immutable message and confirms that the prior delivery is
|
|
264
|
+
still `acked`.
|
|
265
|
+
|
|
266
|
+
Each phase writes an `agent-bridge-production-proof-v1` receipt. The runner rejects
|
|
267
|
+
unknown fields at the phase boundary and checks every receipt before use. Receipts may
|
|
268
|
+
record IDs, timestamps, principal and workspace labels, the gateway origin, boolean
|
|
269
|
+
checks, and salted SHA-256 host evidence. They never record bearer values, database
|
|
270
|
+
URLs, environment values, message content, lease tokens, cursor files, or edge
|
|
271
|
+
databases. Download all four workflow artifacts for the operator record. A passing
|
|
272
|
+
repository test still does not replace a completed external workflow run.
|
|
273
|
+
|
|
130
274
|
### Persistent data and backup
|
|
131
275
|
|
|
132
276
|
A database volume or managed PostgreSQL disk keeps runtime state. It does not protect
|
package/llms.txt
CHANGED
|
@@ -43,7 +43,7 @@ services can call the CLI or HTTPS API or embed the Node library without a host
|
|
|
43
43
|
- `agent-bridge doctor`: active named checks; exits 0 ok, 2 degraded, 1 failed. Blocked outbox rows cannot be healthy.
|
|
44
44
|
- agent-bridge pending
|
|
45
45
|
- agent-bridge send, inbox, history, acknowledge
|
|
46
|
-
- agent-bridge claim, extend, ack, nack, deliveries, delivery-events, cancel, requeue
|
|
46
|
+
- agent-bridge claim [--message-id <uuid>], extend, ack, nack, deliveries, delivery-events, cancel, requeue
|
|
47
47
|
- agent-bridge sync, watch, join, presence
|
|
48
48
|
- agent-bridge clients install <runtime> --identity <principal>
|
|
49
49
|
- agent-bridge clients inspect <runtime> --identity <principal> --instance <stable-key> --backend-config <absolute path>
|
|
@@ -131,6 +131,7 @@ one logical authority across restore but does not fence a live clone.
|
|
|
131
131
|
|
|
132
132
|
Compatibility: post_context, get_context, ack_context.
|
|
133
133
|
Reliable messaging: send, history, claim, extend, acknowledge, negative_acknowledge, heartbeat, presence, sync.
|
|
134
|
+
Claim selects the next due delivery unless HTTP 2.1, MCP, or CLI supplies one messageId. The exact form atomically selects and maintains only that eligible recipient delivery. HTTP 2.0 rejects the additive field.
|
|
134
135
|
Publisher delivery intent: `deliveryPolicy` mode `mailbox|leased` with `maxAttempts`, `retryBaseDelayMs`, `retryMaxDelayMs`, `retryJitterRatio`, and optional `notBefore`. MCP delivery controls: `list_deliveries`, `list_delivery_events`, `cancel_delivery`, `requeue_delivery`. Consumer `maxAttempts` and `retryPolicy` are validated but ignored for one compatibility release.
|
|
135
136
|
|
|
136
137
|
Gateway MCP clients sync automatically and expose manual sync for bounded outbox replay and inbox cache refresh. Offline unacknowledged results are degraded candidates with unknown acknowledgement state, not proof that messages are unread or that no remote work exists.
|
|
@@ -4776,7 +4776,7 @@
|
|
|
4776
4776
|
"/v2/deliveries/claim": {
|
|
4777
4777
|
"post": {
|
|
4778
4778
|
"operationId": "claim_delivery",
|
|
4779
|
-
"summary": "Atomically claim the next targeted delivery.",
|
|
4779
|
+
"summary": "Atomically claim the next targeted delivery, or one eligible message when requested.",
|
|
4780
4780
|
"security": [
|
|
4781
4781
|
{
|
|
4782
4782
|
"bearerAuth": []
|
|
@@ -4810,6 +4810,10 @@
|
|
|
4810
4810
|
"leaseMs": {
|
|
4811
4811
|
"type": "number"
|
|
4812
4812
|
},
|
|
4813
|
+
"messageId": {
|
|
4814
|
+
"type": "string",
|
|
4815
|
+
"pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
|
|
4816
|
+
},
|
|
4813
4817
|
"maxAttempts": {
|
|
4814
4818
|
"type": "number"
|
|
4815
4819
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@creatornader/agent-bridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Durable, pull-first mailbox and work-delivery control plane for AI agents across runtimes, sessions, and machines.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -90,6 +90,10 @@
|
|
|
90
90
|
"test:postgres:contract": "vitest run test/postgres_integration.test.ts --maxWorkers=1 --hookTimeout=60000",
|
|
91
91
|
"test:postgres:preflight": "node scripts/postgres-preflight.mjs",
|
|
92
92
|
"test:postgres:preflight:full": "node scripts/postgres-preflight.mjs --full",
|
|
93
|
+
"preflight:postgres:production": "npm run build --silent && node dist/postgres-production-preflight.js",
|
|
94
|
+
"preflight:fly": "node scripts/fly-production-preflight.mjs",
|
|
95
|
+
"proof:gateway:production": "node scripts/gateway-production-proof.mjs",
|
|
96
|
+
"test:gateway:production-proof": "vitest run test/gateway-production-proof.test.ts",
|
|
93
97
|
"test:windows:contract": "vitest run test/private-path.test.ts test/windows-native-smoke.test.ts --maxWorkers=1 --testTimeout=180000 -t \"applies and verifies native Windows file and directory ACLs|recovers an (enrollment|client-operation) subsystem lock\" && vitest run test/client-maintenance.test.ts --maxWorkers=1 --testTimeout=180000",
|
|
94
98
|
"typecheck": "tsc --noEmit",
|
|
95
99
|
"contracts:generate": "npm run build --silent && npm run contracts:generate:built",
|
|
@@ -747,7 +747,7 @@
|
|
|
747
747
|
},
|
|
748
748
|
{
|
|
749
749
|
"id": "claim_delivery",
|
|
750
|
-
"summary": "Atomically claim the next targeted delivery.",
|
|
750
|
+
"summary": "Atomically claim the next targeted delivery, or one eligible message when requested.",
|
|
751
751
|
"mcp": {
|
|
752
752
|
"name": "claim"
|
|
753
753
|
},
|
|
@@ -759,6 +759,7 @@
|
|
|
759
759
|
"command": "claim",
|
|
760
760
|
"options": [
|
|
761
761
|
"lease-ms",
|
|
762
|
+
"message-id",
|
|
762
763
|
"max-attempts"
|
|
763
764
|
],
|
|
764
765
|
"outputSchema": {
|
|
@@ -633,13 +633,17 @@
|
|
|
633
633
|
},
|
|
634
634
|
{
|
|
635
635
|
"name": "claim",
|
|
636
|
-
"description": "Atomically claim the next targeted delivery.",
|
|
636
|
+
"description": "Atomically claim the next targeted delivery, or one eligible message when requested.",
|
|
637
637
|
"inputSchema": {
|
|
638
638
|
"type": "object",
|
|
639
639
|
"properties": {
|
|
640
640
|
"leaseMs": {
|
|
641
641
|
"type": "number"
|
|
642
642
|
},
|
|
643
|
+
"messageId": {
|
|
644
|
+
"type": "string",
|
|
645
|
+
"pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
|
|
646
|
+
},
|
|
643
647
|
"maxAttempts": {
|
|
644
648
|
"type": "number"
|
|
645
649
|
}
|
|
@@ -2446,6 +2446,10 @@
|
|
|
2446
2446
|
"leaseMs": {
|
|
2447
2447
|
"type": "number"
|
|
2448
2448
|
},
|
|
2449
|
+
"messageId": {
|
|
2450
|
+
"type": "string",
|
|
2451
|
+
"pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
|
|
2452
|
+
},
|
|
2449
2453
|
"maxAttempts": {
|
|
2450
2454
|
"type": "number"
|
|
2451
2455
|
}
|
|
@@ -12,10 +12,14 @@ begin
|
|
|
12
12
|
role_name
|
|
13
13
|
);
|
|
14
14
|
end if;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
if exists (
|
|
16
|
+
select 1 from pg_roles where rolname=role_name and (
|
|
17
|
+
rolcanlogin or not rolinherit or rolsuper or rolcreatedb or rolcreaterole
|
|
18
|
+
or rolreplication or rolbypassrls or rolconnlimit<>-1
|
|
19
|
+
)
|
|
20
|
+
) then
|
|
21
|
+
raise exception 'Agent Bridge runtime role has unsafe attributes';
|
|
22
|
+
end if;
|
|
19
23
|
execute format('revoke all on schema agent_bridge from %I', role_name);
|
|
20
24
|
execute format('revoke all on all tables in schema agent_bridge from %I', role_name);
|
|
21
25
|
execute format('revoke all on all sequences in schema agent_bridge from %I', role_name);
|
|
@@ -17,11 +17,73 @@ begin
|
|
|
17
17
|
role_name
|
|
18
18
|
);
|
|
19
19
|
end if;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
if exists (
|
|
21
|
+
select 1 from pg_roles where rolname=role_name and (
|
|
22
|
+
rolcanlogin or not rolinherit or rolsuper or rolcreatedb or rolcreaterole
|
|
23
|
+
or rolreplication or rolbypassrls or rolconnlimit<>-1
|
|
24
|
+
)
|
|
25
|
+
) then
|
|
26
|
+
raise exception 'Agent Bridge row-isolation role % has unsafe attributes',role_name;
|
|
27
|
+
end if;
|
|
28
|
+
if current_setting('server_version_num')::integer>=160000 then
|
|
29
|
+
if not exists (
|
|
30
|
+
select 1 from pg_catalog.pg_auth_members membership
|
|
31
|
+
join pg_catalog.pg_roles granted on granted.oid=membership.roleid
|
|
32
|
+
join pg_catalog.pg_roles member on member.oid=membership.member
|
|
33
|
+
where granted.rolname=role_name and member.rolname=current_user
|
|
34
|
+
and membership.admin_option
|
|
35
|
+
) then
|
|
36
|
+
execute format(
|
|
37
|
+
'grant %I to %I with admin true,inherit true,set true',role_name,current_user
|
|
38
|
+
);
|
|
39
|
+
elsif not exists (
|
|
40
|
+
select 1 from pg_catalog.pg_auth_members membership
|
|
41
|
+
join pg_catalog.pg_roles granted on granted.oid=membership.roleid
|
|
42
|
+
join pg_catalog.pg_roles member on member.oid=membership.member
|
|
43
|
+
where granted.rolname=role_name and member.rolname=current_user
|
|
44
|
+
and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
|
|
45
|
+
and coalesce((to_jsonb(membership)->>'set_option')::boolean,true)
|
|
46
|
+
) then
|
|
47
|
+
execute format('grant %I to %I with inherit true,set true',role_name,current_user);
|
|
48
|
+
end if;
|
|
49
|
+
elsif not exists (
|
|
50
|
+
select 1 from pg_catalog.pg_auth_members membership
|
|
51
|
+
join pg_catalog.pg_roles granted on granted.oid=membership.roleid
|
|
52
|
+
join pg_catalog.pg_roles member on member.oid=membership.member
|
|
53
|
+
where granted.rolname=role_name and member.rolname=current_user
|
|
54
|
+
and membership.admin_option
|
|
55
|
+
) then
|
|
56
|
+
execute format('grant %I to %I with admin option',role_name,current_user);
|
|
57
|
+
end if;
|
|
58
|
+
if not exists (
|
|
59
|
+
select 1 from pg_catalog.pg_auth_members membership
|
|
60
|
+
join pg_catalog.pg_roles granted on granted.oid=membership.roleid
|
|
61
|
+
join pg_catalog.pg_roles member on member.oid=membership.member
|
|
62
|
+
where granted.rolname=role_name and member.rolname=current_user
|
|
63
|
+
and membership.admin_option
|
|
64
|
+
) or not exists (
|
|
65
|
+
select 1 from pg_catalog.pg_auth_members membership
|
|
66
|
+
join pg_catalog.pg_roles granted on granted.oid=membership.roleid
|
|
67
|
+
join pg_catalog.pg_roles member on member.oid=membership.member
|
|
68
|
+
where granted.rolname=role_name and member.rolname=current_user
|
|
69
|
+
and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
|
|
70
|
+
and coalesce((to_jsonb(membership)->>'set_option')::boolean,true)
|
|
71
|
+
) or exists (
|
|
72
|
+
select 1 from pg_catalog.pg_auth_members membership
|
|
73
|
+
join pg_catalog.pg_roles granted on granted.oid=membership.roleid
|
|
74
|
+
join pg_catalog.pg_roles member on member.oid=membership.member
|
|
75
|
+
where granted.rolname=role_name and member.rolname=current_user
|
|
76
|
+
and not (
|
|
77
|
+
(membership.grantor=10 and membership.admin_option
|
|
78
|
+
and not coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
|
|
79
|
+
and not coalesce((to_jsonb(membership)->>'set_option')::boolean,true))
|
|
80
|
+
or (membership.grantor=member.oid
|
|
81
|
+
and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
|
|
82
|
+
and coalesce((to_jsonb(membership)->>'set_option')::boolean,true))
|
|
83
|
+
)
|
|
84
|
+
) then
|
|
85
|
+
raise exception 'Agent Bridge row-isolation role % has unsafe owner authority',role_name;
|
|
86
|
+
end if;
|
|
25
87
|
end loop;
|
|
26
88
|
end
|
|
27
89
|
$roles$;
|
|
@@ -292,7 +354,8 @@ declare
|
|
|
292
354
|
event_writer text := 'agent_bridge_event_writer_' || suffix;
|
|
293
355
|
table_name text;
|
|
294
356
|
begin
|
|
295
|
-
execute format('grant usage on schema agent_bridge to %I',
|
|
357
|
+
execute format('grant usage,create on schema agent_bridge to %I,%I,%I',
|
|
358
|
+
data_owner,context_reader,event_writer);
|
|
296
359
|
foreach table_name in array array[
|
|
297
360
|
'messages','receipts','deliveries','delivery_events','agent_instances'
|
|
298
361
|
] loop
|
|
@@ -301,9 +364,9 @@ begin
|
|
|
301
364
|
execute format('alter function agent_bridge.current_request_workspace() owner to %I',context_reader);
|
|
302
365
|
execute format('alter function agent_bridge.current_request_principal() owner to %I',context_reader);
|
|
303
366
|
execute format('alter function agent_bridge.record_delivery_event() owner to %I',event_writer);
|
|
304
|
-
execute format('
|
|
367
|
+
execute format('revoke create on schema agent_bridge from %I,%I,%I',
|
|
368
|
+
data_owner,context_reader,event_writer);
|
|
305
369
|
execute format('grant select on agent_bridge.request_authorities to %I',context_reader);
|
|
306
|
-
execute format('grant usage on schema agent_bridge to %I',event_writer);
|
|
307
370
|
execute format('grant insert on agent_bridge.delivery_events to %I',event_writer);
|
|
308
371
|
execute format('grant usage on sequence agent_bridge.delivery_events_sequence_seq to %I',event_writer);
|
|
309
372
|
end
|
|
@@ -18,11 +18,73 @@ begin
|
|
|
18
18
|
role_name
|
|
19
19
|
);
|
|
20
20
|
end if;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
if exists (
|
|
22
|
+
select 1 from pg_roles where rolname=role_name and (
|
|
23
|
+
rolcanlogin or not rolinherit or rolsuper or rolcreatedb or rolcreaterole
|
|
24
|
+
or rolreplication or rolbypassrls or rolconnlimit<>-1
|
|
25
|
+
)
|
|
26
|
+
) then
|
|
27
|
+
raise exception 'Agent Bridge control role % has unsafe attributes',role_name;
|
|
28
|
+
end if;
|
|
29
|
+
if current_setting('server_version_num')::integer>=160000 then
|
|
30
|
+
if not exists (
|
|
31
|
+
select 1 from pg_catalog.pg_auth_members membership
|
|
32
|
+
join pg_catalog.pg_roles granted on granted.oid=membership.roleid
|
|
33
|
+
join pg_catalog.pg_roles member on member.oid=membership.member
|
|
34
|
+
where granted.rolname=role_name and member.rolname=current_user
|
|
35
|
+
and membership.admin_option
|
|
36
|
+
) then
|
|
37
|
+
execute format(
|
|
38
|
+
'grant %I to %I with admin true,inherit true,set true',role_name,current_user
|
|
39
|
+
);
|
|
40
|
+
elsif not exists (
|
|
41
|
+
select 1 from pg_catalog.pg_auth_members membership
|
|
42
|
+
join pg_catalog.pg_roles granted on granted.oid=membership.roleid
|
|
43
|
+
join pg_catalog.pg_roles member on member.oid=membership.member
|
|
44
|
+
where granted.rolname=role_name and member.rolname=current_user
|
|
45
|
+
and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
|
|
46
|
+
and coalesce((to_jsonb(membership)->>'set_option')::boolean,true)
|
|
47
|
+
) then
|
|
48
|
+
execute format('grant %I to %I with inherit true,set true',role_name,current_user);
|
|
49
|
+
end if;
|
|
50
|
+
elsif not exists (
|
|
51
|
+
select 1 from pg_catalog.pg_auth_members membership
|
|
52
|
+
join pg_catalog.pg_roles granted on granted.oid=membership.roleid
|
|
53
|
+
join pg_catalog.pg_roles member on member.oid=membership.member
|
|
54
|
+
where granted.rolname=role_name and member.rolname=current_user
|
|
55
|
+
and membership.admin_option
|
|
56
|
+
) then
|
|
57
|
+
execute format('grant %I to %I with admin option',role_name,current_user);
|
|
58
|
+
end if;
|
|
59
|
+
if not exists (
|
|
60
|
+
select 1 from pg_catalog.pg_auth_members membership
|
|
61
|
+
join pg_catalog.pg_roles granted on granted.oid=membership.roleid
|
|
62
|
+
join pg_catalog.pg_roles member on member.oid=membership.member
|
|
63
|
+
where granted.rolname=role_name and member.rolname=current_user
|
|
64
|
+
and membership.admin_option
|
|
65
|
+
) or not exists (
|
|
66
|
+
select 1 from pg_catalog.pg_auth_members membership
|
|
67
|
+
join pg_catalog.pg_roles granted on granted.oid=membership.roleid
|
|
68
|
+
join pg_catalog.pg_roles member on member.oid=membership.member
|
|
69
|
+
where granted.rolname=role_name and member.rolname=current_user
|
|
70
|
+
and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
|
|
71
|
+
and coalesce((to_jsonb(membership)->>'set_option')::boolean,true)
|
|
72
|
+
) or exists (
|
|
73
|
+
select 1 from pg_catalog.pg_auth_members membership
|
|
74
|
+
join pg_catalog.pg_roles granted on granted.oid=membership.roleid
|
|
75
|
+
join pg_catalog.pg_roles member on member.oid=membership.member
|
|
76
|
+
where granted.rolname=role_name and member.rolname=current_user
|
|
77
|
+
and not (
|
|
78
|
+
(membership.grantor=10 and membership.admin_option
|
|
79
|
+
and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
|
|
80
|
+
=coalesce((to_jsonb(membership)->>'set_option')::boolean,true))
|
|
81
|
+
or (membership.grantor=member.oid
|
|
82
|
+
and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
|
|
83
|
+
and coalesce((to_jsonb(membership)->>'set_option')::boolean,true))
|
|
84
|
+
)
|
|
85
|
+
) then
|
|
86
|
+
raise exception 'Agent Bridge control role % has unsafe owner authority',role_name;
|
|
87
|
+
end if;
|
|
26
88
|
end loop;
|
|
27
89
|
end
|
|
28
90
|
$roles$;
|
|
@@ -144,27 +206,38 @@ begin
|
|
|
144
206
|
where event.member_role=session_user and event.control_role in ('operator','auditor')
|
|
145
207
|
order by event.control_role,event.sequence desc
|
|
146
208
|
), direct_membership as (
|
|
147
|
-
select granted.rolname control_role,
|
|
148
|
-
|
|
149
|
-
coalesce((to_jsonb(membership)->>'
|
|
150
|
-
|
|
209
|
+
select granted.rolname control_role,
|
|
210
|
+
bool_or(membership.admin_option) admin_option,
|
|
211
|
+
bool_or(coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)) inherit_option,
|
|
212
|
+
bool_or(coalesce((to_jsonb(membership)->>'set_option')::boolean,true)) set_option,
|
|
213
|
+
bool_and(case when member.rolname=schema_owner then
|
|
214
|
+
(membership.grantor=10 and membership.admin_option
|
|
215
|
+
and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
|
|
216
|
+
=coalesce((to_jsonb(membership)->>'set_option')::boolean,true))
|
|
217
|
+
or (membership.grantor=member.oid
|
|
218
|
+
and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
|
|
219
|
+
and coalesce((to_jsonb(membership)->>'set_option')::boolean,true))
|
|
220
|
+
else membership.grantor=(select oid from pg_catalog.pg_roles where rolname=schema_owner)
|
|
221
|
+
and not membership.admin_option
|
|
222
|
+
and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
|
|
223
|
+
and coalesce((to_jsonb(membership)->>'set_option')::boolean,true) end) grants_valid
|
|
151
224
|
from pg_catalog.pg_auth_members membership
|
|
152
225
|
join pg_catalog.pg_roles granted on granted.oid=membership.roleid
|
|
153
226
|
join pg_catalog.pg_roles member on member.oid=membership.member
|
|
154
|
-
join pg_catalog.pg_roles grantor on grantor.oid=membership.grantor
|
|
155
227
|
where member.rolname=session_user and granted.rolname in (operator_role,auditor_role)
|
|
228
|
+
group by granted.rolname
|
|
156
229
|
)
|
|
157
230
|
select
|
|
158
231
|
coalesce(bool_or(membership.control_role=operator_role
|
|
159
232
|
and membership.inherit_option and membership.set_option
|
|
160
|
-
and membership.
|
|
233
|
+
and membership.grants_valid and (
|
|
161
234
|
(session_user=schema_owner and membership.admin_option)
|
|
162
235
|
or (latest.control_role='operator' and latest.action='register'
|
|
163
236
|
and not membership.admin_option)
|
|
164
237
|
)),false),
|
|
165
238
|
coalesce(bool_or(membership.control_role=auditor_role
|
|
166
239
|
and membership.inherit_option and membership.set_option
|
|
167
|
-
and membership.
|
|
240
|
+
and membership.grants_valid and (
|
|
168
241
|
(session_user=schema_owner and membership.admin_option)
|
|
169
242
|
or (latest.control_role='auditor' and latest.action='register'
|
|
170
243
|
and not membership.admin_option)
|
|
@@ -937,15 +1010,15 @@ language sql stable set search_path = '' set timezone = 'UTC' as $$
|
|
|
937
1010
|
from pg_catalog.pg_roles role where role.rolname in (select role_name from control_roles)
|
|
938
1011
|
union all
|
|
939
1012
|
select 'membership',granted.rolname||'->'||member.rolname,
|
|
940
|
-
|
|
941
|
-
coalesce(to_jsonb(membership)->>'inherit_option',
|
|
942
|
-
coalesce(to_jsonb(membership)->>'set_option',
|
|
1013
|
+
bool_or(membership.admin_option)::text||':'||
|
|
1014
|
+
bool_or(coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true))::text||':'||
|
|
1015
|
+
bool_or(coalesce((to_jsonb(membership)->>'set_option')::boolean,true))::text
|
|
943
1016
|
from pg_catalog.pg_auth_members membership
|
|
944
1017
|
join pg_catalog.pg_roles granted on granted.oid=membership.roleid
|
|
945
1018
|
join pg_catalog.pg_roles member on member.oid=membership.member
|
|
946
|
-
join pg_catalog.pg_roles grantor on grantor.oid=membership.grantor
|
|
947
1019
|
where member.rolname in (select role_name from control_roles)
|
|
948
1020
|
or granted.rolname=(select owner_role from names)
|
|
1021
|
+
group by granted.rolname,member.rolname
|
|
949
1022
|
union all
|
|
950
1023
|
select 'schema',namespace.nspname,pg_catalog.pg_get_userbyid(namespace.nspowner)
|
|
951
1024
|
from pg_catalog.pg_namespace namespace where namespace.nspname='agent_bridge'
|
|
@@ -1106,27 +1179,42 @@ returns boolean language sql stable security definer set search_path = '' as $$
|
|
|
1106
1179
|
), active_registry as (
|
|
1107
1180
|
select member_role,control_role from latest_registry where action='register'
|
|
1108
1181
|
), expected_memberships as (
|
|
1109
|
-
select names.owner_role granted_role,names.schema_owner member_role,
|
|
1182
|
+
select names.owner_role granted_role,names.schema_owner member_role,
|
|
1183
|
+
true admin_option,true inherit_option,true set_option
|
|
1110
1184
|
from names
|
|
1111
|
-
union all select names.operator_role,names.schema_owner,true from names
|
|
1112
|
-
union all select names.auditor_role,names.schema_owner,true from names
|
|
1185
|
+
union all select names.operator_role,names.schema_owner,true,true,true from names
|
|
1186
|
+
union all select names.auditor_role,names.schema_owner,true,true,true from names
|
|
1113
1187
|
union all
|
|
1114
1188
|
select case registry.control_role when 'operator' then names.operator_role
|
|
1115
|
-
else names.auditor_role end,registry.member_role,false
|
|
1189
|
+
else names.auditor_role end,registry.member_role,false,true,true
|
|
1116
1190
|
from active_registry registry cross join names
|
|
1117
|
-
),
|
|
1191
|
+
), raw_memberships as (
|
|
1118
1192
|
select granted.rolname::name granted_role,member.rolname::name member_role,
|
|
1119
1193
|
membership.admin_option,
|
|
1120
1194
|
coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true) inherit_option,
|
|
1121
1195
|
coalesce((to_jsonb(membership)->>'set_option')::boolean,true) set_option,
|
|
1122
|
-
grantor.
|
|
1196
|
+
membership.grantor,member.oid member_oid
|
|
1123
1197
|
from pg_catalog.pg_auth_members membership
|
|
1124
1198
|
join pg_catalog.pg_roles granted on granted.oid=membership.roleid
|
|
1125
1199
|
join pg_catalog.pg_roles member on member.oid=membership.member
|
|
1126
|
-
join pg_catalog.pg_roles grantor on grantor.oid=membership.grantor
|
|
1127
1200
|
where granted.rolname in (
|
|
1128
1201
|
(select owner_role from names),(select operator_role from names),(select auditor_role from names)
|
|
1129
1202
|
)
|
|
1203
|
+
), actual_memberships as (
|
|
1204
|
+
select membership.granted_role,membership.member_role,
|
|
1205
|
+
bool_or(membership.admin_option) admin_option,
|
|
1206
|
+
bool_or(membership.inherit_option) inherit_option,
|
|
1207
|
+
bool_or(membership.set_option) set_option,
|
|
1208
|
+
bool_and(case when membership.member_role=names.schema_owner then
|
|
1209
|
+
(membership.grantor=10 and membership.admin_option
|
|
1210
|
+
and membership.inherit_option=membership.set_option)
|
|
1211
|
+
or (membership.grantor=membership.member_oid
|
|
1212
|
+
and membership.inherit_option and membership.set_option)
|
|
1213
|
+
else membership.grantor=(select oid from pg_catalog.pg_roles where rolname=names.schema_owner)
|
|
1214
|
+
and not membership.admin_option
|
|
1215
|
+
and membership.inherit_option and membership.set_option end) grants_valid
|
|
1216
|
+
from raw_memberships membership cross join names
|
|
1217
|
+
group by membership.granted_role,membership.member_role
|
|
1130
1218
|
), authority_closure as (
|
|
1131
1219
|
select control_role.granted_role,candidate.rolname::name member_role
|
|
1132
1220
|
from names
|
|
@@ -1145,16 +1233,14 @@ returns boolean language sql stable security definer set search_path = '' as $$
|
|
|
1145
1233
|
) from agent_bridge.owner_control_attestations attestation
|
|
1146
1234
|
where attestation.name='owner-control-v1')
|
|
1147
1235
|
and not exists (
|
|
1148
|
-
(select granted_role,member_role,admin_option from actual_memberships
|
|
1149
|
-
except select granted_role,member_role,admin_option from expected_memberships)
|
|
1236
|
+
(select granted_role,member_role,admin_option,inherit_option,set_option from actual_memberships
|
|
1237
|
+
except select granted_role,member_role,admin_option,inherit_option,set_option from expected_memberships)
|
|
1150
1238
|
union all
|
|
1151
|
-
(select granted_role,member_role,admin_option from expected_memberships
|
|
1152
|
-
except select granted_role,member_role,admin_option from actual_memberships)
|
|
1239
|
+
(select granted_role,member_role,admin_option,inherit_option,set_option from expected_memberships
|
|
1240
|
+
except select granted_role,member_role,admin_option,inherit_option,set_option from actual_memberships)
|
|
1153
1241
|
)
|
|
1154
1242
|
and not exists (
|
|
1155
|
-
select 1 from actual_memberships membership
|
|
1156
|
-
where not membership.inherit_option or not membership.set_option
|
|
1157
|
-
or membership.grantor_role<>names.schema_owner
|
|
1243
|
+
select 1 from actual_memberships membership where not membership.grants_valid
|
|
1158
1244
|
)
|
|
1159
1245
|
and not exists (
|
|
1160
1246
|
select 1 from active_registry registry
|
|
@@ -1216,6 +1302,7 @@ declare
|
|
|
1216
1302
|
membership_record record;
|
|
1217
1303
|
relation_record record;
|
|
1218
1304
|
begin
|
|
1305
|
+
execute format('grant usage,create on schema agent_bridge to %I',owner_role);
|
|
1219
1306
|
execute format('alter table agent_bridge.control_requests owner to %I',owner_role);
|
|
1220
1307
|
execute format('alter table agent_bridge.control_events owner to %I',owner_role);
|
|
1221
1308
|
execute format('alter table agent_bridge.owner_control_attestations owner to %I',owner_role);
|
|
@@ -1230,6 +1317,7 @@ begin
|
|
|
1230
1317
|
'control_revoke_credential(uuid,uuid,text)','owner_control_catalog_definition()',
|
|
1231
1318
|
'owner_control_plane_ready()'
|
|
1232
1319
|
] loop execute format('alter function agent_bridge.%s owner to %I',role_name,owner_role); end loop;
|
|
1320
|
+
execute format('revoke create on schema agent_bridge from %I',owner_role);
|
|
1233
1321
|
|
|
1234
1322
|
for membership_record in
|
|
1235
1323
|
select granted.rolname granted_name,member.rolname member_name
|
|
@@ -1287,7 +1375,7 @@ begin
|
|
|
1287
1375
|
execute format('grant usage on schema agent_bridge to %I,%I,%I',owner_role,operator_role,auditor_role);
|
|
1288
1376
|
execute format('grant select,insert,update(request_id) on agent_bridge.control_requests to %I',owner_role);
|
|
1289
1377
|
execute format('grant insert on agent_bridge.control_events to %I',owner_role);
|
|
1290
|
-
execute format('grant select on agent_bridge.owner_control_attestations to %I',owner_role);
|
|
1378
|
+
execute format('grant select,insert on agent_bridge.owner_control_attestations to %I',owner_role);
|
|
1291
1379
|
execute format('grant select,insert on agent_bridge.control_membership_events to %I',owner_role);
|
|
1292
1380
|
execute format('grant usage on sequence agent_bridge.control_events_sequence_seq to %I',owner_role);
|
|
1293
1381
|
execute format('grant usage on sequence agent_bridge.control_membership_events_sequence_seq to %I',owner_role);
|
|
@@ -1306,6 +1394,7 @@ begin
|
|
|
1306
1394
|
execute format('grant execute on function agent_bridge.control_revoke_credential(uuid,uuid,text) to %I',operator_role);
|
|
1307
1395
|
execute format('grant execute on function agent_bridge.control_credential_inventory(text,timestamp with time zone,uuid,integer) to %I,%I',operator_role,auditor_role);
|
|
1308
1396
|
execute format('grant execute on function agent_bridge.owner_control_plane_ready() to %I',runtime_role);
|
|
1397
|
+
execute format('grant execute on function agent_bridge.owner_control_plane_ready() to %I',current_user);
|
|
1309
1398
|
execute format('revoke execute on function agent_bridge.replace_credential(uuid,character,text[],text,text,timestamp with time zone,timestamp with time zone,text,uuid) from %I,%I,%I',operator_role,auditor_role,runtime_role);
|
|
1310
1399
|
execute format('revoke execute on function agent_bridge.revoke_credential(uuid,text,text,uuid) from %I,%I,%I',operator_role,auditor_role,runtime_role);
|
|
1311
1400
|
foreach role_name in array array['anon','authenticated'] loop
|
|
@@ -1632,8 +1721,12 @@ begin
|
|
|
1632
1721
|
end
|
|
1633
1722
|
$preflight$;
|
|
1634
1723
|
|
|
1724
|
+
select set_config(
|
|
1725
|
+
'role','agent_bridge_control_owner_'||substr(md5(current_database()),1,16),false
|
|
1726
|
+
);
|
|
1635
1727
|
insert into agent_bridge.owner_control_attestations(name,catalog_definition)
|
|
1636
1728
|
values('owner-control-v1',agent_bridge.owner_control_catalog_definition());
|
|
1729
|
+
reset role;
|
|
1637
1730
|
|
|
1638
1731
|
insert into agent_bridge.schema_migrations(version,name,checksum)
|
|
1639
1732
|
values(14,'owner_control_plane','__AGENT_BRIDGE_MIGRATION_CHECKSUM__');
|