@creatornader/agent-bridge 0.2.0 → 0.3.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.
Files changed (114) hide show
  1. package/CHANGELOG.md +63 -1
  2. package/README.md +467 -29
  3. package/ROADMAP.md +104 -0
  4. package/SECURITY.md +73 -0
  5. package/SKILL.md +68 -2
  6. package/bin/agent-bridge +2 -2
  7. package/clients/claude-desktop.json +3 -1
  8. package/dist/archive.d.ts +163 -0
  9. package/dist/archive.js +50 -0
  10. package/dist/archive.js.map +1 -0
  11. package/dist/bridge-domain-CBX3IVqC.d.ts +153 -0
  12. package/dist/{bridge-service-CfqMcMAn.d.ts → bridge-service-hX1ZQp4W.d.ts} +15 -5
  13. package/dist/bridge-store-DPVSCwfs.d.ts +102 -0
  14. package/dist/bridge.d.ts +76 -17
  15. package/dist/bridge.js +52 -24
  16. package/dist/chunk-2DFNOLMO.js +618 -0
  17. package/dist/chunk-2DFNOLMO.js.map +1 -0
  18. package/dist/{chunk-KVR4JBRF.js → chunk-2EVLG3YA.js} +2 -2
  19. package/dist/{chunk-KVR4JBRF.js.map → chunk-2EVLG3YA.js.map} +1 -1
  20. package/dist/chunk-45BZGB77.js +709 -0
  21. package/dist/chunk-45BZGB77.js.map +1 -0
  22. package/dist/chunk-4VB44KJU.js +230 -0
  23. package/dist/chunk-4VB44KJU.js.map +1 -0
  24. package/dist/chunk-5GMB3A77.js +1013 -0
  25. package/dist/chunk-5GMB3A77.js.map +1 -0
  26. package/dist/chunk-A6W4QALF.js +1258 -0
  27. package/dist/chunk-A6W4QALF.js.map +1 -0
  28. package/dist/chunk-BZLMF5O5.js +328 -0
  29. package/dist/chunk-BZLMF5O5.js.map +1 -0
  30. package/dist/{chunk-AXL2I7FH.js → chunk-CEXBMOI5.js} +240 -79
  31. package/dist/chunk-CEXBMOI5.js.map +1 -0
  32. package/dist/chunk-GADH244D.js +527 -0
  33. package/dist/chunk-GADH244D.js.map +1 -0
  34. package/dist/chunk-MBPZXTJA.js +2428 -0
  35. package/dist/chunk-MBPZXTJA.js.map +1 -0
  36. package/dist/chunk-NCO7RLBM.js +146 -0
  37. package/dist/chunk-NCO7RLBM.js.map +1 -0
  38. package/dist/chunk-Q6PPMXJC.js +1050 -0
  39. package/dist/chunk-Q6PPMXJC.js.map +1 -0
  40. package/dist/{chunk-4KAOBC5X.js → chunk-SPSMZOCJ.js} +190 -165
  41. package/dist/chunk-SPSMZOCJ.js.map +1 -0
  42. package/dist/chunk-UAHMP6GC.js +54 -0
  43. package/dist/chunk-UAHMP6GC.js.map +1 -0
  44. package/dist/{chunk-YD6YJATN.js → chunk-VOQIDTCJ.js} +4 -2
  45. package/dist/chunk-VOQIDTCJ.js.map +1 -0
  46. package/dist/chunk-WAI2IYIQ.js +554 -0
  47. package/dist/chunk-WAI2IYIQ.js.map +1 -0
  48. package/dist/cli.d.ts +2 -1
  49. package/dist/cli.js +3577 -250
  50. package/dist/cli.js.map +1 -1
  51. package/dist/client-config.d.ts +1 -1
  52. package/dist/client-config.js +1 -1
  53. package/dist/client-runtime.d.ts +10 -6
  54. package/dist/client-runtime.js +9 -6
  55. package/dist/contracts/generate.d.ts +2 -0
  56. package/dist/contracts/generate.js +247 -0
  57. package/dist/contracts/generate.js.map +1 -0
  58. package/dist/dr.d.ts +324 -0
  59. package/dist/dr.js +124 -0
  60. package/dist/dr.js.map +1 -0
  61. package/dist/gateway-CggQ8vTC.d.ts +78 -0
  62. package/dist/gateway-main.js +190 -8
  63. package/dist/gateway-main.js.map +1 -1
  64. package/dist/gateway.d.ts +4 -3
  65. package/dist/gateway.js +4 -3
  66. package/dist/index.js +11 -8
  67. package/dist/index.js.map +1 -1
  68. package/dist/postgres-bridge-store-Bfd3hRId.d.ts +49 -0
  69. package/dist/server.d.ts +3 -2
  70. package/dist/server.js +13 -8
  71. package/dist/sqlite.d.ts +94 -12
  72. package/dist/sqlite.js +7 -4
  73. package/docs/architecture-v2.md +375 -0
  74. package/docs/decisions/0001-protocol-layers-and-acknowledgment-semantics.md +85 -0
  75. package/docs/decisions/0002-canonical-operation-contract-registry.md +46 -0
  76. package/docs/ecosystem.md +54 -0
  77. package/docs/postmortems/2026-07-08-wrapper-source-drift.md +82 -0
  78. package/docs/troubleshooting.md +89 -0
  79. package/llms.txt +96 -4
  80. package/openapi/agent-bridge-v2.openapi.json +9610 -0
  81. package/package.json +24 -4
  82. package/schemas/agent-bridge-v2.capabilities.json +1829 -0
  83. package/schemas/agent-bridge-v2.mcp.json +2074 -0
  84. package/schemas/agent-bridge-v2.schema.json +4560 -0
  85. package/sql/migrations/008_message_projects.sql +36 -0
  86. package/sql/migrations/009_mailbox_query_indexes.sql +28 -0
  87. package/sql/migrations/010_publisher_delivery_policy.sql +210 -0
  88. package/sql/migrations/011_credential_security.sql +875 -0
  89. package/sql/migrations/012_request_authority.sql +177 -0
  90. package/sql/migrations/013_row_isolation.sql +495 -0
  91. package/sql/migrations/014_owner_control_plane.sql +1641 -0
  92. package/sql/migrations/015_portable_archives.sql +1813 -0
  93. package/sql/migrations/016_native_dr_fence.sql +1005 -0
  94. package/bin/CLAUDE.md +0 -11
  95. package/dist/bridge-domain-BcnBeHOn.d.ts +0 -97
  96. package/dist/bridge-store-BuGjxTn4.d.ts +0 -61
  97. package/dist/chunk-4KAOBC5X.js.map +0 -1
  98. package/dist/chunk-6MAZ6YLZ.js +0 -146
  99. package/dist/chunk-6MAZ6YLZ.js.map +0 -1
  100. package/dist/chunk-AXL2I7FH.js.map +0 -1
  101. package/dist/chunk-BV6W4AJK.js +0 -211
  102. package/dist/chunk-BV6W4AJK.js.map +0 -1
  103. package/dist/chunk-CKSKUBDQ.js +0 -153
  104. package/dist/chunk-CKSKUBDQ.js.map +0 -1
  105. package/dist/chunk-JOUBDRYX.js +0 -272
  106. package/dist/chunk-JOUBDRYX.js.map +0 -1
  107. package/dist/chunk-JTC5YURG.js +0 -49
  108. package/dist/chunk-JTC5YURG.js.map +0 -1
  109. package/dist/chunk-O26W5QHS.js +0 -409
  110. package/dist/chunk-O26W5QHS.js.map +0 -1
  111. package/dist/chunk-WEULPMUY.js +0 -988
  112. package/dist/chunk-WEULPMUY.js.map +0 -1
  113. package/dist/chunk-YD6YJATN.js.map +0 -1
  114. package/dist/gateway-BULozv3K.d.ts +0 -54
package/README.md CHANGED
@@ -1,6 +1,14 @@
1
1
  # Agent Bridge
2
2
 
3
- Agent Bridge is a shared messaging and work-delivery layer for AI agents that run in different clients, processes, sessions, and machines.
3
+ [![npm version](https://img.shields.io/npm/v/%40creatornader%2Fagent-bridge)](https://www.npmjs.com/package/@creatornader/agent-bridge)
4
+ [![test](https://github.com/creatornader/agent-bridge/actions/workflows/test.yml/badge.svg)](https://github.com/creatornader/agent-bridge/actions/workflows/test.yml)
5
+ [![license](https://img.shields.io/github/license/creatornader/agent-bridge)](LICENSE)
6
+
7
+ Agent Bridge is a durable, pull-first mailbox and work-delivery control plane for AI
8
+ agents across runtimes, sessions, and machines. It gives coding agents, desktop agents,
9
+ always-on workers, and agent frameworks a shared inbox that survives disconnection and
10
+ restart. Start locally with SQLite, then use an operator-owned PostgreSQL gateway when
11
+ agents need to communicate across machines.
4
12
 
5
13
  It supports three operating modes:
6
14
 
@@ -12,22 +20,54 @@ It supports three operating modes:
12
20
 
13
21
  PostgreSQL remains the authority in gateway mode. SQLite holds local-only messages or gateway edge state. Supabase is one way to host PostgreSQL, not a protocol dependency.
14
22
 
23
+ ## Quick start
24
+
25
+ Install the published package and run a two-principal local proof. Local mode needs no
26
+ account, daemon, or external database.
27
+
28
+ ```bash
29
+ npm install --global @creatornader/agent-bridge
30
+ agent-bridge init --provider local
31
+ agent-bridge demo
32
+ ```
33
+
34
+ Install Agent Bridge into a supported client after the demo succeeds:
35
+
36
+ ```bash
37
+ agent-bridge clients install codex --identity codex
38
+ agent-bridge clients install claude-code --identity claude-code
39
+ agent-bridge clients install claude-desktop --identity claude-desktop
40
+ ```
41
+
42
+ Restart the client, then run `agent-bridge doctor --json` if the MCP server does not
43
+ attach. The [troubleshooting guide](docs/troubleshooting.md) covers executable paths,
44
+ client configuration, and recovery steps.
45
+
15
46
  ## What v2 provides
16
47
 
17
48
  - Immutable messages with UUIDv7 IDs and opaque server cursors.
18
49
  - Indexed source, thread, timestamp, workspace, and delivery queries.
19
50
  - Read receipts separated from executable delivery state.
20
51
  - Atomic claim, lease renewal, acknowledgment, negative acknowledgment, retry, and dead-letter operations.
52
+ - Immutable publisher-owned delivery policy with mailbox and leased modes, strict urgent/high/info claim priority, publisher cancel and requeue controls, authorized audit pagination, and fenced leases.
21
53
  - Append-only delivery transition history.
22
54
  - Idempotency conflict detection. Exact replay deduplicates; changed content under the same key fails.
23
55
  - Scoped credentials that bind a remote workspace and agent principal.
24
56
  - SQLite outbox and inbox cache for gateway clients.
25
57
  - Leased runtime presence with instance IDs and capabilities.
26
58
  - MCP, CLI, Codex, Claude Code, and Claude Desktop integration paths.
59
+ - Canonical portable archives for messages and read receipts across local SQLite and shared PostgreSQL stores.
60
+ - Native SQLite and PostgreSQL backup, verification, and restore.
27
61
  - Existing `post_context`, `get_context`, `ack_context`, `post`, and `get` behavior during migration.
28
62
 
29
63
  The accepted protocol and storage decisions live in [docs/architecture-v2.md](docs/architecture-v2.md).
30
64
 
65
+ The canonical v2 operation registry generates [JSON Schema](schemas/agent-bridge-v2.schema.json), [OpenAPI 3.1.2](openapi/agent-bridge-v2.openapi.json), and the [MCP manifest](schemas/agent-bridge-v2.mcp.json). Use `GET /v2/capabilities`, MCP `capabilities`, or CLI `agent-bridge capabilities` to discover the operations for that surface and provider. Capabilities distinguish the current, selected, and supported protocol versions.
66
+
67
+ Protocol 2.1 uses a gateway-first rollout. An upgraded gateway continues to serve released 2.0 clients, including headerless requests and their direct or null delivery results. A 2.1 client probes before mutation and proceeds only when complete, consistent response headers select 2.1 and advertise 2.1 support. It rejects mutation against a headerless or 2.0 gateway instead of downgrading. Upgrade the gateway before installing or starting 2.1 clients.
68
+
69
+ The OpenAPI paths describe protocol 2.1. The embedded `x-agent-bridge-protocol-2.0` and `x-agent-bridge-schemas-2.0` vendor extensions contain frozen compatibility schema metadata for released 2.0 clients. They are not a second OpenAPI description. Gateway credentials enforce the operation scopes declared by the canonical registry. Local mode uses process identity, and legacy mode uses its configured key. Provider-neutral artifacts report this difference instead of claiming one authorization model for every backend.
70
+
31
71
  ## Architecture
32
72
 
33
73
  ```text
@@ -51,15 +91,27 @@ The accepted protocol and storage decisions live in [docs/architecture-v2.md](do
51
91
 
52
92
  Realtime notifications or hooks may wake a client, but cursored reads remain authoritative. A missed notification does not lose a message.
53
93
 
94
+ A2A and application task semantics sit above Agent Bridge. MCP, CLI, and HTTPS are access surfaces. Optional transports such as SLIM or NATS may provide wakeups or transport below the core, but authoritative recovery still uses cursored pull. agmsg remains a reference for adapters, interoperability, and client experience.
95
+
96
+ [ADR-0001](docs/decisions/0001-protocol-layers-and-acknowledgment-semantics.md) defines this boundary and the distinct acknowledgment meanings.
97
+
98
+ Agent Bridge is not a workflow engine, scheduler, memory system, terminal manager, or
99
+ new universal agent protocol. See [Agent Bridge in the agent ecosystem](docs/ecosystem.md)
100
+ for its relationship to MCP, A2A, agmsg, brokers, and agent runtimes.
101
+
54
102
  ## Requirements
55
103
 
56
104
  - Node.js 22.23.1 or newer.
57
105
  - SQLite 3.51.3 or newer for local and edge storage. The supported Node version includes it.
58
- - PostgreSQL 15 or newer for gateway mode.
106
+ - PostgreSQL 15, 16, 17, or 18 for gateway mode. New PostgreSQL majors fail the
107
+ migration prerequisite and live readiness checks until their catalog digest is
108
+ certified.
59
109
 
60
110
  ## Install from source
61
111
 
62
- The unscoped npm name belongs to another project. This repository uses `@creatornader/agent-bridge`. The release workflow always builds a tagged package. Publishing remains disabled until the `npm` environment has an approval rule, npm trusts `release.yml` as this package's publisher, and `NPM_PUBLISH_ENABLED` is set to `true`.
112
+ Use the source install for development. The normal installation path is the published
113
+ `@creatornader/agent-bridge` package shown in the quick start. The unscoped npm name
114
+ belongs to another project.
63
115
 
64
116
  ```bash
65
117
  git clone https://github.com/creatornader/agent-bridge.git
@@ -77,7 +129,7 @@ The package exposes three executables:
77
129
 
78
130
  `npm pack` builds the package before creating a tarball. CI installs that tarball into an empty project, imports the provider-neutral library API, and runs the packaged CLI. Importing the package root does not start the MCP server.
79
131
 
80
- ## Local quick start
132
+ ## Client identities and local commands
81
133
 
82
134
  Initialize backend settings. `init` does not write an agent identity into the shared config.
83
135
 
@@ -88,18 +140,174 @@ agent-bridge init --provider local
88
140
  Each client supplies its own identity:
89
141
 
90
142
  ```bash
91
- AGENT_BRIDGE_AGENT=codex agent-bridge post --category operational "Bridge is ready"
92
- AGENT_BRIDGE_AGENT=claude-code agent-bridge get --unacked-by claude-code
143
+ AGENT_BRIDGE_AGENT=codex agent-bridge send \
144
+ --target claude-code \
145
+ --type context \
146
+ "Bridge is ready"
147
+ AGENT_BRIDGE_AGENT=claude-code agent-bridge inbox --receipt-state unread
93
148
  ```
94
149
 
95
150
  You can also pass `--source` to a standalone send. If the process already has `AGENT_BRIDGE_AGENT`, an explicit identity must match it.
96
151
 
97
- Run a two-principal local proof:
152
+ The `post`, `get`, and `--unacked-by` forms remain available only for compatibility
153
+ with older clients. New integrations should use `send`, caller-bound inboxes, and
154
+ `--receipt-state`.
155
+
156
+ ## Portable archives
157
+
158
+ Portable archives move one workspace's immutable messages and read receipts between
159
+ canonical local SQLite and PostgreSQL stores. Export writes a private file through a
160
+ same-directory temporary file, file fsync, atomic publication, and directory fsync.
161
+ The export audit completes only after durable publication. A failed export records a
162
+ bounded abandonment code. Export and import paths must satisfy the current user's
163
+ private-path policy. Export refuses to replace an existing file unless `--force` is
164
+ present. Export accepts `--request-id`, embeds it as `exportRequestId` in the archive
165
+ header, and returns it on success. Verification and import also return that export
166
+ provenance. An import request ID is a separate destination operation identifier.
167
+ Reusing an export request ID verifies a completed file or reconciles a matching file
168
+ whose audit response was lost. A missing or mismatched file stays started and requires
169
+ a new export request ID or owner reconciliation. Recovery artifacts use deterministic
170
+ same-directory names derived from the export request ID, so a retry can report and
171
+ clean only its own private temporary file or backup. Force replacement keeps that
172
+ backup until the new file is durable. Errors report whether publication, backup
173
+ cleanup, or audit state is known.
174
+
175
+ Portable v1 accepts only records in the current Agent Bridge domain. New API writes
176
+ use lowercase UUIDs. Archives require lowercase UUIDs and UTC timestamps with six
177
+ fractional digits. They do not trim strings, remove duplicates, apply defaults, or
178
+ rewrite message content. Export rejects legacy or direct database rows outside these
179
+ rules. Recover those rows through native database backup and restore tools.
98
180
 
99
181
  ```bash
100
- agent-bridge demo
182
+ install -d -m 700 "$HOME/.agent-bridge/archives"
183
+
184
+ agent-bridge archive export \
185
+ --provider local \
186
+ --workspace team \
187
+ --request-id 00000000-0000-4000-8000-000000000001 \
188
+ --db "$HOME/.agent-bridge/bridge.sqlite3" \
189
+ --output "$HOME/.agent-bridge/archives/team.ndjson"
190
+
191
+ agent-bridge archive verify --file "$HOME/.agent-bridge/archives/team.ndjson"
192
+
193
+ # Import is a rollback-backed dry run unless --apply is explicit.
194
+ agent-bridge archive import \
195
+ --provider local \
196
+ --db /path/to/target.sqlite3 \
197
+ --file "$HOME/.agent-bridge/archives/team.ndjson" \
198
+ --workspace team \
199
+ --dry-run
200
+
201
+ agent-bridge archive import \
202
+ --provider local \
203
+ --db /path/to/target.sqlite3 \
204
+ --file "$HOME/.agent-bridge/archives/team.ndjson" \
205
+ --workspace team \
206
+ --request-id 00000000-0000-4000-8000-000000000001 \
207
+ --apply
208
+ ```
209
+
210
+ Local commands use `--db`, `AGENT_BRIDGE_DB`, the selected shared config, or the
211
+ canonical local database path, in that order. PostgreSQL commands accept database
212
+ authority only from `AGENT_BRIDGE_ARCHIVE_DATABASE_URL` and require a login registered
213
+ for the archive boundary created by the current migrations. They do not fall back to
214
+ the schema-owner, gateway runtime, owner-control, or client database settings.
215
+
216
+ The archive digest detects accidental changes to the canonical bytes. It is neither
217
+ encryption nor authentication. Archives contain message content and routing metadata,
218
+ so protect them as private data. They exclude deliveries, delivery events, presence,
219
+ credentials, owner-control records, security events, and other control or security
220
+ state. The library API is available from `@creatornader/agent-bridge/archive`.
221
+ Import opens the archive once without following links. It verifies the file, replays
222
+ messages, then replays receipts through the same descriptor. Every pass checks framing,
223
+ order, counts, and the client-computed digest. Batches are limited by row count and a
224
+ 4 MiB byte budget. `--dry-run` and `--apply` are mutually exclusive.
225
+
226
+ ## Native disaster recovery
227
+
228
+ Native DR preserves one complete local authority or shared PostgreSQL deployment. Use
229
+ it for recovery, not for moving a workspace between providers. The framed `.abdr`
230
+ bundle records its provider, schema contract, and entry hashes. Commands calculate and
231
+ return a separate whole-file SHA-256 digest. The hashes detect changed bytes. The bundle
232
+ is not encrypted or authenticated, and it may contain credentials and message content.
233
+ Store and transfer it as private database material.
234
+
235
+ Local backup uses SQLite's online backup API, so the source may remain open. It accepts
236
+ only the local authority database. Gateway edge caches and outboxes are rejected because
237
+ PostgreSQL remains their authority. Restore requires a new target path and verifies the
238
+ exact local schema contract before publication.
239
+
240
+ ```bash
241
+ agent-bridge dr backup \
242
+ --provider local \
243
+ --source "$HOME/.agent-bridge/bridge.sqlite3" \
244
+ --output "$HOME/.agent-bridge/archives/local.abdr" \
245
+ --backup-id 00000000-0000-4000-8000-000000000010
246
+
247
+ agent-bridge dr verify \
248
+ --provider local \
249
+ --bundle "$HOME/.agent-bridge/archives/local.abdr"
250
+
251
+ agent-bridge dr restore \
252
+ --provider local \
253
+ --bundle "$HOME/.agent-bridge/archives/local.abdr" \
254
+ --target /path/to/new-bridge.sqlite3 \
255
+ --request-id 00000000-0000-4000-8000-000000000011
101
256
  ```
102
257
 
258
+ PostgreSQL backup takes a repeatable-read snapshot while holding the native DR fence.
259
+ It stores the `agent_bridge` schema dump plus a canonical inventory of required roles,
260
+ memberships, and default privileges. The backup excludes transient data from agent
261
+ instances, rate-limit buckets, request authority, and archive transaction authority.
262
+ Restore turns claimed deliveries into immediately retryable work because a database
263
+ restore cannot preserve a live lease.
264
+
265
+ PostgreSQL authority comes only from the process environment. Backup reads
266
+ `AGENT_BRIDGE_DR_SOURCE_DATABASE_URL`; restore reads
267
+ `AGENT_BRIDGE_DR_TARGET_DATABASE_URL`. URLs are not accepted as command arguments.
268
+ `pg_dump` and `pg_restore` must match the database major exactly. Native DR supports
269
+ PostgreSQL 15 through 18. If matching tools are not on `PATH`, pass their directory to
270
+ PostgreSQL backup, verify, or restore with `--tool-directory`. Local verification does
271
+ not use PostgreSQL tools and rejects that option.
272
+
273
+ ```bash
274
+ AGENT_BRIDGE_DR_SOURCE_DATABASE_URL='postgresql://backup-admin@db/agent_bridge' \
275
+ agent-bridge dr backup \
276
+ --provider postgres \
277
+ --output "$HOME/.agent-bridge/archives/postgres.abdr" \
278
+ --backup-id 00000000-0000-4000-8000-000000000020
279
+
280
+ agent-bridge dr verify \
281
+ --provider postgres \
282
+ --bundle "$HOME/.agent-bridge/archives/postgres.abdr"
283
+
284
+ AGENT_BRIDGE_DR_TARGET_DATABASE_URL='postgresql://restore-admin@new-db/agent_bridge' \
285
+ agent-bridge dr restore \
286
+ --provider postgres \
287
+ --bundle "$HOME/.agent-bridge/archives/postgres.abdr" \
288
+ --request-id 00000000-0000-4000-8000-000000000021 \
289
+ --accept-source-sql-risk
290
+ ```
291
+
292
+ PostgreSQL restore executes SQL from the dump, so use `--accept-source-sql-risk` only
293
+ for a bundle from a source you trust. The target must be a dedicated fresh database
294
+ with the same database name and PostgreSQL major as the backup. Restore requires a
295
+ superuser because it recreates object owners, role shells, memberships, and default
296
+ privileges. It never enables login on restored external principals. If a restore fails
297
+ after mutation begins, Agent Bridge disables new target connections and reports any
298
+ residual roles or recovery paths. Inspect and clean those artifacts before retrying.
299
+ Never run the source and restored target as active authorities at the same time.
300
+
301
+ Backup publication never replaces an existing output. Deterministic adjacent staging
302
+ paths use the backup or request UUID. A retry with the same UUID reports retained
303
+ recovery paths instead of overwriting them. File and directory synchronization is
304
+ strongest on platforms that support directory fsync; Windows cannot provide the same
305
+ directory durability proof. Successful Windows commands report
306
+ `directoryDurability: "unavailable"` or
307
+ `cleanupDirectoryDurability: "unavailable"` instead of claiming that proof. The
308
+ library API is available from
309
+ `@creatornader/agent-bridge/dr`.
310
+
103
311
  ## Install client integrations
104
312
 
105
313
  Agent identity and gateway credentials belong to each client process, not the shared `~/.agent-bridge/config` file.
@@ -110,7 +318,8 @@ agent-bridge clients install claude-code --identity claude-code
110
318
  agent-bridge clients install claude-desktop --identity claude-desktop
111
319
  ```
112
320
 
113
- For gateway mode, issue a distinct principal-bound token for each client and pass it only during that client's installation:
321
+ For gateway mode, use the owner enrollment workflow below for new credentials. The
322
+ older token handoff remains available for credentials issued by external tooling:
114
323
 
115
324
  ```bash
116
325
  AGENT_BRIDGE_CLIENT_TOKEN=<codex token> \
@@ -154,21 +363,174 @@ $grant$;
154
363
 
155
364
  The database-derived suffix prevents a gateway login for one database from inheriting access to another Agent Bridge database on the same PostgreSQL cluster. Do not grant the login another database's runtime role.
156
365
 
157
- Create a workspace, agent, and credential with the migration or provisioning connection. Store only a SHA-256 token hash:
366
+ Migration 014 provides the database owner control plane. The schema-owner connection
367
+ registers provisioning and inventory logins with `register_control_member`. It can
368
+ remove them with `revoke_control_member`. Do not grant the database-specific operator,
369
+ auditor, or owner roles directly. Operators call the narrow
370
+ `control_provision`, `control_rotate_credential`, and `control_revoke_credential`
371
+ functions with a fresh request UUID. Operators and auditors can call
372
+ `control_credential_inventory`; it never exposes credential digest material. Inventory
373
+ accepts an optional workspace, an optional `(created_at, credential_id)` cursor, and
374
+ a row limit. The default is 100 rows and the maximum is 1,000.
375
+
376
+ Each mutation takes a transaction lock for its request UUID before checking the
377
+ request ledger. An identical concurrent call returns the stored result. Reusing the
378
+ UUID with changed content fails. Provisioning reuses an existing workspace only when
379
+ its name matches, so one workspace can contain several principals. Rotation supports
380
+ credentials without an expiry and a null grace cutoff for immediate replacement. A
381
+ rotation request includes the expected workspace and principal. The function checks
382
+ both while it locks the predecessor, then returns the stored canonical identity.
383
+ The database derives the audit actor from `session_user`.
384
+
385
+ This temporary operator workflow creates an eligible login and registers it through
386
+ the protected SQL boundary. The owner CLI then provisions a token-bound `codex`
387
+ principal. It generates the raw token locally, writes it to a private enrollment file
388
+ before contacting PostgreSQL, and sends only the SHA-256 hash to the database.
158
389
 
159
- ```sql
160
- insert into agent_bridge.workspaces (id, name)
161
- values ('team', 'Team');
390
+ ```bash
391
+ OPERATOR_LOGIN="agent_bridge_operator_$(date +%s)"
392
+ OPERATOR_PASSWORD="$(node -e "process.stdout.write(require('node:crypto').randomBytes(32).toString('hex'))")"
393
+ MEMBERSHIP_REQUEST="$(node -e "process.stdout.write(require('node:crypto').randomUUID())")"
394
+ PROVISION_REQUEST="$(node -e "process.stdout.write(require('node:crypto').randomUUID())")"
395
+ export AGENT_BRIDGE_OPERATOR_LOGIN="$OPERATOR_LOGIN"
396
+ export AGENT_BRIDGE_OPERATOR_PASSWORD="$OPERATOR_PASSWORD"
397
+ export AGENT_BRIDGE_MEMBERSHIP_REQUEST="$MEMBERSHIP_REQUEST"
398
+ export AGENT_BRIDGE_PROVISION_REQUEST="$PROVISION_REQUEST"
399
+
400
+ psql -v ON_ERROR_STOP=1 "$AGENT_BRIDGE_DATABASE_URL" <<'SQL'
401
+ \getenv operator_login AGENT_BRIDGE_OPERATOR_LOGIN
402
+ \getenv operator_password AGENT_BRIDGE_OPERATOR_PASSWORD
403
+ \getenv membership_request AGENT_BRIDGE_MEMBERSHIP_REQUEST
404
+ SELECT format(
405
+ 'CREATE ROLE %I LOGIN PASSWORD %L',
406
+ :'operator_login',
407
+ :'operator_password'
408
+ ) \gexec
409
+ SELECT * FROM agent_bridge.register_control_member(
410
+ :'membership_request'::uuid,
411
+ :'operator_login'::name,
412
+ 'operator'
413
+ );
414
+ SQL
415
+
416
+ export AGENT_BRIDGE_OPERATOR_DATABASE_URL="postgresql://${OPERATOR_LOGIN}:${OPERATOR_PASSWORD}@host/database"
417
+ export AGENT_BRIDGE_URL="https://bridge.example.com"
418
+ ENROLLMENT_FILE="${AGENT_BRIDGE_ENROLLMENT_DIR:-$HOME/.agent-bridge/enrollments}/${PROVISION_REQUEST}.json"
419
+
420
+ agent-bridge owner provision \
421
+ --request-id "$PROVISION_REQUEST" \
422
+ --workspace team \
423
+ --workspace-name Team \
424
+ --identity codex \
425
+ --runtime codex \
426
+ --instance codex-machine \
427
+ --scope-set release-a-full
428
+
429
+ agent-bridge clients install codex --enrollment-file "$ENROLLMENT_FILE"
430
+
431
+ REVOKE_REQUEST="$(node -e "process.stdout.write(require('node:crypto').randomUUID())")"
432
+ export AGENT_BRIDGE_REVOKE_REQUEST="$REVOKE_REQUEST"
433
+ psql -v ON_ERROR_STOP=1 "$AGENT_BRIDGE_DATABASE_URL" <<'SQL'
434
+ \getenv operator_login AGENT_BRIDGE_OPERATOR_LOGIN
435
+ \getenv revoke_request AGENT_BRIDGE_REVOKE_REQUEST
436
+ SELECT * FROM agent_bridge.revoke_control_member(
437
+ :'revoke_request'::uuid,
438
+ :'operator_login'::name,
439
+ 'operator'
440
+ );
441
+ SELECT format('DROP ROLE %I', :'operator_login') \gexec
442
+ SQL
443
+ unset OPERATOR_PASSWORD AGENT_BRIDGE_OPERATOR_PASSWORD
444
+ unset AGENT_BRIDGE_OPERATOR_LOGIN AGENT_BRIDGE_MEMBERSHIP_REQUEST
445
+ unset AGENT_BRIDGE_PROVISION_REQUEST AGENT_BRIDGE_REVOKE_REQUEST
446
+ unset AGENT_BRIDGE_OPERATOR_DATABASE_URL
447
+ ```
162
448
 
163
- insert into agent_bridge.agents (workspace_id, principal, runtime_type)
164
- values ('team', 'codex', 'codex')
165
- returning id;
449
+ `AGENT_BRIDGE_OPERATOR_DATABASE_URL` is the owner CLI's only database authority. It
450
+ does not fall back to the schema migration URL, runtime URL, shared client config, or
451
+ active client identity. `owner inventory` returns at most 100 rows by default and
452
+ 1,000 when requested. Its opaque cursor is bound to the selected workspace.
453
+
454
+ Provision and rotation enrollment files stay under
455
+ `$AGENT_BRIDGE_ENROLLMENT_DIR` or `~/.agent-bridge/enrollments`. The CLI requires an
456
+ owner-only root and private path components. It refuses symlinks, path escapes, and
457
+ directory replacement during an operation. Every file has a monotonic revision. A
458
+ per-file exclusive lock covers the read, state transition, database call, and client
459
+ installation side effects. Each transition compares the revision, state, request,
460
+ operation, and token with the current file before atomic replacement.
461
+
462
+ If provisioning fails, rerun the same operation with `--resume "$ENROLLMENT_FILE"`.
463
+ A process crash can leave the adjacent lock file behind. Do not delete it manually.
464
+ After at least 60 seconds, use `--recover-lock` with `--resume` or the client install
465
+ command. Recovery succeeds only when the lock belongs to this host and user and its
466
+ recorded process no longer exists. The stored request UUID, inputs, instance, and raw
467
+ token are reused exactly.
468
+
469
+ `clients install <runtime> --enrollment-file <path>` derives the gateway URL,
470
+ workspace, principal, instance, and token from the file. It rejects conflicting flags
471
+ or environment values. Provisioning registers the host MCP server. Rotation updates
472
+ only that instance's existing private backend file and never repeats host registration.
473
+ The backend must contain the predecessor credential ID, principal, and instance metadata
474
+ written by an enrollment-based provision. Rotation fails closed for older backend files
475
+ without this metadata. It also verifies the exact live MCP registration before replacing
476
+ the token.
477
+ The installer removes the enrollment file only after installation succeeds. A failed
478
+ delete returns `enrollmentStatus: "consumed-file-retained"` and the consumed file path.
479
+ An already missing file returns `consumed-file-missing`. If unlink succeeds but the
480
+ directory identity check or fsync fails, the result is
481
+ `consumed-deletion-durability-unknown`; it never claims that the path was retained.
482
+ `lockReleaseStatus` separately reports `released`, `retained`, or `durability-unknown`.
483
+ A lock-release failure after successful installation does not replace the installation
484
+ result with a generic error. The consumed artifact no longer contains the raw token. A retry validates the
485
+ live backend and MCP registration before deleting a retained consumed file.
486
+
487
+ `--token` and `AGENT_BRIDGE_CLIENT_TOKEN` remain available for the older manual
488
+ installation flow, but they cannot be combined with `--enrollment-file`. The final SQL
489
+ block revokes and drops the temporary operator after the client installation has
490
+ stored the credential.
491
+
492
+ Rotate the credential for one installed instance with an explicit grace cutoff or
493
+ `--invalidate-immediately`, then consume the resulting enrollment file:
166
494
 
167
- insert into agent_bridge.credentials (workspace_id, agent_id, token_hash, label)
168
- values ('team', '<agent uuid>', '<sha256 token hash>', 'codex laptop');
495
+ ```bash
496
+ ROTATE_REQUEST="$(node -e "process.stdout.write(require('node:crypto').randomUUID())")"
497
+ ROTATION_FILE="${AGENT_BRIDGE_ENROLLMENT_DIR:-$HOME/.agent-bridge/enrollments}/${ROTATE_REQUEST}.json"
498
+ agent-bridge owner rotate \
499
+ --request-id "$ROTATE_REQUEST" \
500
+ --credential-id "<current credential UUID>" \
501
+ --workspace team \
502
+ --identity codex \
503
+ --runtime codex \
504
+ --instance codex-machine \
505
+ --scope-set release-a-full \
506
+ --invalidate-immediately
507
+ agent-bridge clients install codex --enrollment-file "$ROTATION_FILE"
169
508
  ```
170
509
 
171
- Repeat the agent and credential inserts for Claude Code, Claude Desktop, or any other principal. Each plaintext token should be random, unique, and shown only to the matching client installer.
510
+ Control membership is an offline SQL administration boundary. The gateway and MCP
511
+ server do not expose it. Runtime readiness compares PostgreSQL membership with the
512
+ append-only registry. It fails for an unregistered operator or auditor, an external
513
+ owner holder, a missing registered grant, any extra role inherited by a registered
514
+ member, or any role that inherits a registered member. Protected operations recheck
515
+ the live registry and direct membership after taking a member-global transaction lock,
516
+ followed by capability locks in operator-before-auditor order. Registration and
517
+ revocation use the same order. A stale session that remains in a revoked role cannot
518
+ continue operating. The schema owner is the only bootstrap holder of all three control
519
+ roles and the only role with ordinary execution authority on the registration
520
+ functions. PostgreSQL superusers remain inside this trusted database-administration
521
+ boundary. Readiness checks the running PostgreSQL major on every call, so upgrading an
522
+ already-migrated database to an uncertified major disables the runtime.
523
+
524
+ Direct credential inserts default to empty scopes rather than full access. Issued
525
+ identity, scope, label, expiry, and lineage fields cannot be edited directly. Existing
526
+ migration-013 rows retain their values and remain revocable during upgrade. Runtime
527
+ readiness compares the live owner-control catalog with the protected migration-014
528
+ attestation. Migration 014 refuses to create that baseline if critical workspace,
529
+ agent, credential, index, trigger, function, ownership, or row-isolation dependencies
530
+ have already drifted. Unrelated additive schema objects do not change readiness.
531
+ The migration also blocks unsafe direct and default privileges on protected objects.
532
+ Global and workspace inventory use fixed-origin expression indexes for their keyset
533
+ ordering.
172
534
 
173
535
  Start the gateway:
174
536
 
@@ -190,7 +552,12 @@ AGENT_BRIDGE_URL=https://bridge.example.com
190
552
  AGENT_BRIDGE_WORKSPACE=team
191
553
  ```
192
554
 
193
- These shared settings are enough before `clients install`. The installer takes `AGENT_BRIDGE_CLIENT_TOKEN`, creates a private client backend file containing the matching token, and registers `AGENT_BRIDGE_AGENT`, `AGENT_BRIDGE_INSTANCE`, and that file's path with the host. Gateway initialization checks readiness, token validity, and that the token-bound principal matches that process identity.
555
+ These shared settings are enough before `clients install`. The secure flow reads
556
+ the principal-bound token and exact client settings from an enrollment file. The
557
+ compatibility flow accepts `AGENT_BRIDGE_CLIENT_TOKEN`. Both write a private client
558
+ backend and register `AGENT_BRIDGE_AGENT`, `AGENT_BRIDGE_INSTANCE`, and that file's
559
+ path with the host. Gateway initialization checks readiness, token validity, and that
560
+ the token-bound principal matches that process identity.
194
561
 
195
562
  ## Offline gateway behavior
196
563
 
@@ -200,10 +567,10 @@ When a send cannot reach the gateway:
200
567
 
201
568
  1. The validated message is written to the SQLite outbox with a stable idempotency key.
202
569
  2. The CLI or MCP result reports `disposition: "queued"` and `authoritative: false`.
203
- 3. `agent-bridge sync` retries due messages in queue order and pulls remote history into the inbox cache. A permanently blocked message remains visible in diagnostics but does not stop later messages.
570
+ 3. Long-lived MCP gateway clients retry due messages automatically with bounded exponential backoff. `agent-bridge sync` and the MCP `sync` tool trigger the same bounded replay and cache refresh manually. A permanently blocked message remains visible in diagnostics but does not stop later messages.
204
571
  4. An ambiguous retry is safe because the gateway enforces idempotency and rejects changed content under the same key.
205
572
 
206
- Cached reads report `source: "cache"` and `stale: true`. Claims, lease changes, delivery settlement, presence, and read receipts require the gateway because replaying those operations after a lease or identity change is unsafe.
573
+ Normal inbox and pending reads fall back to the local cache when the gateway is unreachable. Cached reads report `source: "cache"`, `stale: true`, and `degraded: true`. When a read requested an unacknowledged filter, cached rows are candidates rather than proof of unread state and report `acknowledgements: "unknown"`. Claims, lease changes, delivery settlement, presence, and read-receipt writes still require the gateway because replaying those operations after a lease or identity change is unsafe.
207
574
 
208
575
  ## CLI
209
576
 
@@ -215,10 +582,17 @@ agent-bridge pending
215
582
  agent-bridge send --type request --target worker "Run the task"
216
583
  agent-bridge post --category goal-update --project agent-bridge "Gateway is ready"
217
584
  agent-bridge inbox --limit 20
585
+ agent-bridge sent --limit 20
586
+ agent-bridge history --mailbox all --receipt-state any
218
587
  agent-bridge get --since 24h --unacked-by codex
219
588
  agent-bridge history --thread-id release-1
220
589
  agent-bridge acknowledge --ids <message-uuid>
221
590
  agent-bridge claim --lease-ms 30000
591
+ agent-bridge send --target worker --delivery-policy '{"mode":"leased","maxAttempts":3,"retryBaseDelayMs":1000,"retryMaxDelayMs":60000,"retryJitterRatio":0.2}' "work"
592
+ agent-bridge deliveries --state dead
593
+ agent-bridge delivery-events --delivery-id <uuid>
594
+ agent-bridge cancel --delivery-id <uuid>
595
+ agent-bridge requeue --delivery-id <uuid>
222
596
  agent-bridge extend --delivery-id <uuid> --lease-token <uuid>
223
597
  agent-bridge ack --delivery-id <uuid> --lease-token <uuid>
224
598
  agent-bridge nack --delivery-id <uuid> --lease-token <uuid> --error "retry later"
@@ -228,9 +602,18 @@ agent-bridge sync
228
602
  agent-bridge watch
229
603
  ```
230
604
 
231
- `agent-bridge pending` is a cheap shell gate for agent startup. It exits 0 when unread messages or due delivery work exists and 1 when neither exists. Its JSON result separates unread context from executable delivery work. Gateway checks fail instead of returning a false negative when the remote authority cannot confirm unread state.
605
+ `--project` adds an optional immutable label to a message. It never selects a workspace or changes the active identity. Omit it on reads to include every project and unlabeled messages in the credential-bound workspace. Pass it to `get`, `inbox`, `history`, `pending`, or `watch` for an exact label match. Gateway callers may use `--workspace` only as an assertion. The CLI rejects a mismatch before sending a request. Local mode permits a per-command workspace override. The global legacy Supabase schema has no tenant workspace, so legacy mode fixes workspace to `*` and rejects other `--workspace` values.
606
+
607
+ History defaults to mailbox `inbox`: broadcasts plus messages targeted to the configured principal, exactly as prior releases did. `sent` selects messages whose source is that principal, and `all` is the union. `--receipt-state any|unread|read` is caller-relative and valid only with `inbox`. The deprecated `--unacked-by` option remains an identity assertion. The CLI rejects a mismatch before opening storage or contacting a gateway. Server surfaces reject it before querying message storage. Cursors bind workspace, principal, mailbox, and normalized filters. Version 1 cursors are temporarily accepted, while all new cursors are version 2.
608
+
609
+ Publishers set `deliveryPolicy` on the message. Leased policy uses `maxAttempts`, `retryBaseDelayMs`, `retryMaxDelayMs`, `retryJitterRatio`, and optional `notBefore`. Consumers cannot override those values. Consumer-side `maxAttempts` on claim and `retryPolicy` on nack remain validated but ignored for one compatibility release. New code should omit both fields.
610
+
611
+ The legacy Supabase adapter applies mailbox and receipt rules cooperatively. A holder of the legacy publishable key can bypass the adapter through PostgREST or its receipt RPC. Use the authenticated v2 gateway when the authorization boundary must be enforced.
612
+
613
+ `agent-bridge pending` is a cheap shell gate for agent startup. It exits 0 when unread candidates or due delivery work are visible, 1 only for an authoritative empty result, and 2 when an empty remote state cannot be confirmed. Its JSON result separates unread context from executable delivery work and labels the state as `available`, `empty`, or `unknown`.
232
614
 
233
615
  `watch` runs until interrupted unless `--polls` sets an explicit bound. Empty polls use capped backoff and jitter. Gateway and legacy network failures retry when the error is transient. Authentication and validation errors stop the watcher.
616
+ Each exact project filter gets its own local watch checkpoint. Switching between a filtered and unfiltered watch cannot advance the other checkpoint.
234
617
 
235
618
  Unknown flags and missing option values fail before a message can be sent. This prevents a misspelled target flag from becoming a broadcast.
236
619
 
@@ -250,11 +633,17 @@ Local and gateway providers also expose:
250
633
  - `extend`
251
634
  - `acknowledge`
252
635
  - `negative_acknowledge`
636
+ - `list_deliveries`
637
+ - `list_delivery_events`
638
+ - `cancel_delivery`
639
+ - `requeue_delivery`
253
640
  - `heartbeat`
254
641
  - `presence`
255
642
 
256
643
  Legacy Supabase does not advertise delivery or presence tools because its schema cannot enforce those operations.
257
644
 
645
+ The acknowledgment names depend on the interface. MCP `ack_context` and CLI `acknowledge` write read receipts. MCP `acknowledge` and CLI `ack` settle claimed deliveries. A receipt never settles delivery work, and delivery settlement never creates a receipt.
646
+
258
647
  ## Identity and security
259
648
 
260
649
  - `~/.agent-bridge/config` contains backend settings only. A stored `AGENT_BRIDGE_AGENT` value is ignored.
@@ -283,30 +672,63 @@ Legacy mode keeps the v1 `shared_context` and `ack_context` RPC behavior. It add
283
672
 
284
673
  When gateway migrations run in the same PostgreSQL database as `public.shared_context`, migration 006 imports legacy rows and receipts into the private v2 schema. It preserves valid envelope UUIDs, keeps the existing synthetic UUID mapping for ordinary numeric IDs, assigns deterministic UUIDs to larger IDs, maps projects to workspaces, rejects ID collisions, and verifies the imported count. It does not create executable deliveries for historical targeted rows because an upgrade must not replay old work.
285
674
 
675
+ Migration 008 adds the optional project label without changing migration 006. A schema owner can preview correction of migration 006 rows into the canonical `agent-bridge` workspace with `agent-bridge reconcile-legacy-projects`. The command defaults to a dry run. Pass `--apply` to make the change in one transaction. The canonical workspace must already exist. Reconciliation preserves message IDs, timestamps, receipts, and total row counts. It creates no deliveries and is safe to repeat. It refuses changed source data, invalid labels, project or idempotency conflicts, and imported messages that already have a delivery.
676
+
286
677
  The original v1 schema remains in [`sql/setup.sql`](sql/setup.sql). Ordered gateway migrations live in [`sql/migrations/`](sql/migrations/).
287
678
 
288
679
  ## Health and operations
289
680
 
290
- `doctor` and `status` return JSON with:
681
+ `status` is a passive snapshot. It does not synchronize or probe a remote provider. An unprobed remote reports `status: "unknown"`, `connected: false`, and `remoteReachable: null`. Passive status still exits 0. `doctor` evaluates named checks and exits 0 for `ok`, 2 for `degraded`, and 1 for `failed`. Both return JSON with:
291
682
 
292
683
  - Provider and actual schema version.
293
684
  - Bound workspace, agent, and instance.
294
685
  - Endpoint or database path.
295
686
  - Cursor and queue state.
296
- - Pending, claimed, retrying, and dead delivery counts.
297
- - Gateway reachability, outbox depth, blocked outbox rows, cache size, and last sync error.
687
+ - Pending, claimed, retrying, and dead delivery counts, plus due versus scheduled work, expired leases, oldest-due time, and bounded queue lag.
688
+ - Gateway reachability, outbox due/scheduled/leased/blocked depth, cache size, blocked age/attempt/error, next retry, last outbound/inbound sync, and last attempt.
689
+ - Explicit check results. Blocked outbox rows, expired leases, dead deliveries, or a known-unreachable gateway prevent a healthy result.
690
+
691
+ Local edge health and remote reachability are separate. A healthy offline gateway client after a doctor probe reports `localHealthy: true`, `remoteReachable: false`, `connected: false`, and `status: "degraded"` while retaining usable cached reads and queued sends. Fatal local edge errors report `localHealthy: false` without hiding the queue snapshot. Blocked age is null for rows created by an older client before transition timestamps existed.
692
+
693
+ Client-local outbox and synchronization fields appear only in CLI status output. Authenticated HTTP `/v2/status` reports gateway delivery diagnostics and does not advertise client-local state.
694
+
695
+ In-process `SyncingBridgeStore` diagnostics also expose the current loop state and a sanitized loop error. Standalone CLI health commands omit those fields because their short-lived diagnostic runtime is not the long-lived MCP client.
298
696
 
299
697
  The gateway exposes unauthenticated `/readyz`. `/v2/status` and `/metrics` require a valid credential.
300
698
 
699
+ Each gateway operation checks the scopes in the canonical registry. `capabilities` needs an active credential but no named scope. For requests with bodies, the gateway validates media type, size, and JSON before it opens the request transaction. It then audits scope denials and applies both a credential-wide token bucket and an operation bucket before domain work begins. Missing policy state or a failed denial audit closes the request with `security_unavailable`. Rate denials return the same rounded delay in `Retry-After` and `error.details.retryAfterSeconds`.
700
+
701
+ Migration 012 makes PostgreSQL the authority for each production gateway request that passes credential preflight. Node hashes the bearer credential before PostgreSQL receives it. The gateway checks out one connection, opens one explicit transaction, matches the credential ID and hash inside a narrow security-definer function, and derives the workspace, principal, and scopes from current database state. Security accounting and domain work share that transaction and backend. Delivery claim, cancel, and requeue reuse it without nested `BEGIN`. Runtime logins cannot read credential hashes, agent records, workspace records, or request-authority records.
702
+
703
+ Migration 013 enables and forces row-level security on messages, receipts, deliveries, delivery events, and presence. Policies read workspace and principal from the transaction-bound request authority. The runtime role cannot set that authority through session variables, inherit a table-owner role, or bypass RLS. Separate no-login roles own domain tables, read request context, and write delivery audit events. Delivery identity and publisher bindings are immutable. A trigger lets recipients perform recipient lifecycle actions and reserves cancel and requeue for publishers.
704
+
705
+ The migration records a catalog attestation after it creates the policies, constraints, triggers, and security functions. `/readyz` fails if the current catalog no longer matches that owner-created baseline. Gateway capabilities report `rowIsolation: true` only when request authority is enabled and the migration, catalog attestation, ownership, policy, privilege, and function checks all pass. An owner-approved schema change must update the attestation in its migration.
706
+
707
+ RLS does not replace operation checks. The service still enforces lease tokens, valid state transitions, and target-to-delivery membership. The database binds each delivery to its publisher and message, but it does not prove that the recipient appears in the message target list. PostgreSQL superusers and roles with `BYPASSRLS` remain outside this boundary; do not use either for the gateway login.
708
+
709
+ Credential replacement links are immutable and stay within one workspace and principal. A replacement grace period can shorten the predecessor's lifetime but cannot extend ordinary expiry or override revocation. Replacement, revocation, scope denial, and rate denial events use explicit append-only columns. They do not store tokens, hashes, authorization headers, request bodies, message content, arbitrary metadata, URLs, IP addresses, or database errors.
710
+
301
711
  Before dropping an Agent Bridge database, remove its gateway login and database-specific runtime role. Run this while connected to that database so `current_database()` still identifies the right role:
302
712
 
303
713
  ```sql
304
714
  drop role if exists agent_bridge_gateway;
305
715
  do $cleanup$
306
716
  declare
307
- runtime_role text := 'agent_bridge_runtime_' || substr(md5(current_database()), 1, 16);
717
+ suffix text := substr(md5(current_database()), 1, 16);
718
+ role_name text;
308
719
  begin
309
- execute format('drop role if exists %I', runtime_role);
720
+ foreach role_name in array array[
721
+ 'agent_bridge_runtime_' || suffix,
722
+ 'agent_bridge_data_owner_' || suffix,
723
+ 'agent_bridge_context_reader_' || suffix,
724
+ 'agent_bridge_event_writer_' || suffix
725
+ ] loop
726
+ if exists (select 1 from pg_roles where rolname=role_name) then
727
+ execute format('reassign owned by %I to %I', role_name, current_user);
728
+ execute format('drop owned by %I', role_name);
729
+ execute format('drop role %I', role_name);
730
+ end if;
731
+ end loop;
310
732
  end
311
733
  $cleanup$;
312
734
  ```
@@ -322,10 +744,26 @@ npm run build
322
744
  npm pack
323
745
  ```
324
746
 
325
- Set `AGENT_BRIDGE_TEST_DATABASE_URL` to run the live PostgreSQL contract and migration tests. CI runs Node 22 and 24 on Linux, macOS, and Windows, plus PostgreSQL and clean package-install jobs.
747
+ Set `AGENT_BRIDGE_TEST_DATABASE_URL` to run the live PostgreSQL contract and migration
748
+ tests. CI runs Node 22 and 24 on Linux, macOS, and Windows. PostgreSQL 15 through 18 each
749
+ pack and install the npm tarball, register a least-privilege operator, and exercise
750
+ owner provision, inventory, rotation, and revocation. Before applying a Windows DACL,
751
+ private paths must be owned by the current account SID or the active token's default
752
+ owner SID. The final owner and sole protected FullControl rule must use the account SID.
753
+ Verification-only paths must already satisfy that final policy. Node file identity
754
+ checks and native Windows reparse attributes must also agree before and after policy
755
+ validation; symlinks, junctions, and other reparse objects fail closed. CI applies and
756
+ verifies this policy on native Windows runners. Windows ACL race and directory
757
+ durability behavior has not yet been proved on a dedicated Windows host.
326
758
 
327
759
  ## Documentation
328
760
 
761
+ - [ROADMAP.md](ROADMAP.md): released work, unreleased implementation, and the remaining program.
762
+ - [SECURITY.md](SECURITY.md): supported versions, private vulnerability reporting, and security boundaries.
763
+ - [docs/ecosystem.md](docs/ecosystem.md): product boundary, adjacent systems, and interoperability direction.
764
+ - [docs/troubleshooting.md](docs/troubleshooting.md): MCP startup, identity, backend, and client recovery.
765
+ - [ADR-0001](docs/decisions/0001-protocol-layers-and-acknowledgment-semantics.md): protocol layers and acknowledgment semantics.
766
+ - [ADR-0002](docs/decisions/0002-canonical-operation-contract-registry.md): canonical contracts, generated artifacts, discovery, and version negotiation.
329
767
  - [docs/architecture-v2.md](docs/architecture-v2.md): protocol and storage decisions.
330
768
  - [SKILL.md](SKILL.md): runtime-neutral agent operating instructions.
331
769
  - [llms.txt](llms.txt): compact machine-readable project map.