@creatornader/agent-bridge 0.2.0 → 0.3.1

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 (115) hide show
  1. package/CHANGELOG.md +86 -4
  2. package/README.md +567 -55
  3. package/ROADMAP.md +114 -0
  4. package/SECURITY.md +90 -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 +3555 -214
  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 +394 -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/decisions/0003-host-adapters-and-consumer-instance-keys.md +88 -0
  77. package/docs/ecosystem.md +86 -0
  78. package/docs/postmortems/2026-07-08-wrapper-source-drift.md +82 -0
  79. package/docs/troubleshooting.md +89 -0
  80. package/llms.txt +107 -6
  81. package/openapi/agent-bridge-v2.openapi.json +9610 -0
  82. package/package.json +24 -4
  83. package/schemas/agent-bridge-v2.capabilities.json +1829 -0
  84. package/schemas/agent-bridge-v2.mcp.json +2074 -0
  85. package/schemas/agent-bridge-v2.schema.json +4560 -0
  86. package/sql/migrations/008_message_projects.sql +36 -0
  87. package/sql/migrations/009_mailbox_query_indexes.sql +28 -0
  88. package/sql/migrations/010_publisher_delivery_policy.sql +210 -0
  89. package/sql/migrations/011_credential_security.sql +875 -0
  90. package/sql/migrations/012_request_authority.sql +177 -0
  91. package/sql/migrations/013_row_isolation.sql +495 -0
  92. package/sql/migrations/014_owner_control_plane.sql +1641 -0
  93. package/sql/migrations/015_portable_archives.sql +1813 -0
  94. package/sql/migrations/016_native_dr_fence.sql +1005 -0
  95. package/bin/CLAUDE.md +0 -11
  96. package/dist/bridge-domain-BcnBeHOn.d.ts +0 -97
  97. package/dist/bridge-store-BuGjxTn4.d.ts +0 -61
  98. package/dist/chunk-4KAOBC5X.js.map +0 -1
  99. package/dist/chunk-6MAZ6YLZ.js +0 -146
  100. package/dist/chunk-6MAZ6YLZ.js.map +0 -1
  101. package/dist/chunk-AXL2I7FH.js.map +0 -1
  102. package/dist/chunk-BV6W4AJK.js +0 -211
  103. package/dist/chunk-BV6W4AJK.js.map +0 -1
  104. package/dist/chunk-CKSKUBDQ.js +0 -153
  105. package/dist/chunk-CKSKUBDQ.js.map +0 -1
  106. package/dist/chunk-JOUBDRYX.js +0 -272
  107. package/dist/chunk-JOUBDRYX.js.map +0 -1
  108. package/dist/chunk-JTC5YURG.js +0 -49
  109. package/dist/chunk-JTC5YURG.js.map +0 -1
  110. package/dist/chunk-O26W5QHS.js +0 -409
  111. package/dist/chunk-O26W5QHS.js.map +0 -1
  112. package/dist/chunk-WEULPMUY.js +0 -988
  113. package/dist/chunk-WEULPMUY.js.map +0 -1
  114. package/dist/chunk-YD6YJATN.js.map +0 -1
  115. 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,54 +20,144 @@ 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
- - Leased runtime presence with instance IDs and capabilities.
26
- - MCP, CLI, Codex, Claude Code, and Claude Desktop integration paths.
57
+ - Leased instance-keyed presence with capabilities.
58
+ - MCP, CLI, HTTPS, and Node library access, plus host adapters for Codex, Claude Code,
59
+ Claude Desktop, OpenClaw, and generic MCP clients.
60
+ - Canonical portable archives for messages and read receipts across local SQLite and shared PostgreSQL stores.
61
+ - Native SQLite and PostgreSQL backup, verification, and restore.
27
62
  - Existing `post_context`, `get_context`, `ack_context`, `post`, and `get` behavior during migration.
28
63
 
29
64
  The accepted protocol and storage decisions live in [docs/architecture-v2.md](docs/architecture-v2.md).
30
65
 
66
+ 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.
67
+
68
+ 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.
69
+
70
+ 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.
71
+
31
72
  ## Architecture
32
73
 
33
- ```text
34
- Codex Claude Code Claude Desktop scripts
35
- | | | |
36
- +--------------+------------------+-----------------+
37
- MCP or CLI
38
- |
39
- process-scoped agent identity
40
- |
41
- +----------------+----------------+
42
- | |
43
- local SQLite SQLite edge store
44
- |
45
- authenticated HTTPS
46
- |
47
- Agent Bridge gateway
48
- |
49
- PostgreSQL
74
+ ```mermaid
75
+ flowchart TB
76
+ subgraph execution["Agent execution"]
77
+ harnesses["Agent harnesses<br/>product-specific or custom"]
78
+ callers["Non-harness callers<br/>scripts, CI jobs, and application services"]
79
+ end
80
+
81
+ subgraph hosts["Host surfaces"]
82
+ clis["Command-line hosts<br/>Codex CLI and Claude Code CLI"]
83
+ desktops["Desktop hosts<br/>Codex in ChatGPT and Claude Desktop"]
84
+ daemons["Daemons and custom MCP hosts"]
85
+ end
86
+
87
+ subgraph access["Integration and access"]
88
+ adapter["Host adapter or MCP registration"]
89
+ mcp["MCP over stdio: agent-bridge-mcp"]
90
+ cli["CLI: agent-bridge"]
91
+ library["Node library"]
92
+ direct["Direct HTTPS client"]
93
+ end
94
+
95
+ harnesses --> clis
96
+ harnesses --> desktops
97
+ harnesses --> daemons
98
+ clis --> adapter
99
+ desktops --> adapter
100
+ daemons --> adapter
101
+ adapter --> mcp
102
+ callers --> cli
103
+ callers --> library
104
+ callers --> direct
105
+
106
+ mcp --> core["Agent Bridge client core<br/>principal plus optional consumer instance key"]
107
+ cli --> core
108
+ library --> core
109
+
110
+ core --> local["Local mode<br/>SQLite authority"]
111
+ core --> edge["Gateway client mode<br/>SQLite edge outbox and cache"]
112
+ edge -->|"HTTPS protocol 2.1"| gateway["Agent Bridge gateway"]
113
+ direct -->|"HTTPS protocol 2.1"| gateway
114
+ gateway --> postgres["PostgreSQL authority"]
50
115
  ```
51
116
 
117
+ The diagram shows concepts, not exclusive product categories. A product may package a
118
+ harness and a host together. A harness is the agent execution environment. A host is
119
+ the CLI, desktop app, or daemon where it runs. A host adapter registers an access
120
+ surface. Scripts are orthogonal callers: they can invoke the CLI, call HTTPS, or embed
121
+ the Node library without becoming agent harnesses.
122
+
123
+ | Adapter or integration | Host coverage | Access | Installation status |
124
+ | --- | --- | --- | --- |
125
+ | `codex` | Codex CLI and Codex in the ChatGPT desktop app through their shared profile | MCP stdio | Automated |
126
+ | `claude-code` | Claude Code terminal harness | MCP stdio | Automated |
127
+ | `claude-desktop` | Claude Desktop application | MCP stdio | Automated |
128
+ | `openclaw` | OpenClaw host or daemon | MCP stdio | Manifest included; operator-managed |
129
+ | `generic-mcp` | Any compatible stdio MCP host | MCP stdio | Manifest included; operator-managed |
130
+ | Scripts and services | Shell jobs, CI, daemons, and application code | CLI, HTTPS, or Node library | Direct integration |
131
+ | Hermes, Pi, and other harnesses | Depends on the host's supported interface | No interface assumed | No dedicated adapter or conformance claim yet |
132
+
133
+ `agent-bridge-mcp` is a stdio server. The gateway exposes the Agent Bridge HTTPS API;
134
+ it is not MCP over HTTP. A direct HTTPS client can bypass MCP, the CLI, and the local
135
+ edge store, but then owns its own retry and offline behavior.
136
+
52
137
  Realtime notifications or hooks may wake a client, but cursored reads remain authoritative. A missed notification does not lose a message.
53
138
 
139
+ A2A and application task semantics sit above Agent Bridge. MCP, CLI, HTTPS, and the Node library 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.
140
+
141
+ [ADR-0001](docs/decisions/0001-protocol-layers-and-acknowledgment-semantics.md) defines this boundary and the distinct acknowledgment meanings.
142
+ [ADR-0003](docs/decisions/0003-host-adapters-and-consumer-instance-keys.md) defines the host layers and consumer instance-key contract.
143
+
144
+ Agent Bridge is not a workflow engine, scheduler, memory system, terminal manager, or
145
+ new universal agent protocol. See [Agent Bridge in the agent ecosystem](docs/ecosystem.md)
146
+ for its relationship to MCP, A2A, agmsg, brokers, and agent runtimes.
147
+
54
148
  ## Requirements
55
149
 
56
150
  - Node.js 22.23.1 or newer.
57
151
  - 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.
152
+ - PostgreSQL 15, 16, 17, or 18 for gateway mode. New PostgreSQL majors fail the
153
+ migration prerequisite and live readiness checks until their catalog digest is
154
+ certified.
59
155
 
60
156
  ## Install from source
61
157
 
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`.
158
+ Use the source install for development. The normal installation path is the published
159
+ `@creatornader/agent-bridge` package shown in the quick start. The unscoped npm name
160
+ belongs to another project.
63
161
 
64
162
  ```bash
65
163
  git clone https://github.com/creatornader/agent-bridge.git
@@ -77,7 +175,7 @@ The package exposes three executables:
77
175
 
78
176
  `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
177
 
80
- ## Local quick start
178
+ ## Client identities and local commands
81
179
 
82
180
  Initialize backend settings. `init` does not write an agent identity into the shared config.
83
181
 
@@ -88,21 +186,178 @@ agent-bridge init --provider local
88
186
  Each client supplies its own identity:
89
187
 
90
188
  ```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
189
+ AGENT_BRIDGE_AGENT=codex agent-bridge send \
190
+ --target claude-code \
191
+ --type context \
192
+ "Bridge is ready"
193
+ AGENT_BRIDGE_AGENT=claude-code agent-bridge inbox --receipt-state unread
93
194
  ```
94
195
 
95
196
  You can also pass `--source` to a standalone send. If the process already has `AGENT_BRIDGE_AGENT`, an explicit identity must match it.
96
197
 
97
- Run a two-principal local proof:
198
+ The `post`, `get`, and `--unacked-by` forms remain available only for compatibility
199
+ with older clients. New integrations should use `send`, caller-bound inboxes, and
200
+ `--receipt-state`.
201
+
202
+ ## Portable archives
203
+
204
+ Portable archives move one workspace's immutable messages and read receipts between
205
+ canonical local SQLite and PostgreSQL stores. Export writes a private file through a
206
+ same-directory temporary file, file fsync, atomic publication, and directory fsync.
207
+ The export audit completes only after durable publication. A failed export records a
208
+ bounded abandonment code. Export and import paths must satisfy the current user's
209
+ private-path policy. Export refuses to replace an existing file unless `--force` is
210
+ present. Export accepts `--request-id`, embeds it as `exportRequestId` in the archive
211
+ header, and returns it on success. Verification and import also return that export
212
+ provenance. An import request ID is a separate destination operation identifier.
213
+ Reusing an export request ID verifies a completed file or reconciles a matching file
214
+ whose audit response was lost. A missing or mismatched file stays started and requires
215
+ a new export request ID or owner reconciliation. Recovery artifacts use deterministic
216
+ same-directory names derived from the export request ID, so a retry can report and
217
+ clean only its own private temporary file or backup. Force replacement keeps that
218
+ backup until the new file is durable. Errors report whether publication, backup
219
+ cleanup, or audit state is known.
220
+
221
+ Portable v1 accepts only records in the current Agent Bridge domain. New API writes
222
+ use lowercase UUIDs. Archives require lowercase UUIDs and UTC timestamps with six
223
+ fractional digits. They do not trim strings, remove duplicates, apply defaults, or
224
+ rewrite message content. Export rejects legacy or direct database rows outside these
225
+ rules. Recover those rows through native database backup and restore tools.
98
226
 
99
227
  ```bash
100
- agent-bridge demo
228
+ install -d -m 700 "$HOME/.agent-bridge/archives"
229
+
230
+ agent-bridge archive export \
231
+ --provider local \
232
+ --workspace team \
233
+ --request-id 00000000-0000-4000-8000-000000000001 \
234
+ --db "$HOME/.agent-bridge/bridge.sqlite3" \
235
+ --output "$HOME/.agent-bridge/archives/team.ndjson"
236
+
237
+ agent-bridge archive verify --file "$HOME/.agent-bridge/archives/team.ndjson"
238
+
239
+ # Import is a rollback-backed dry run unless --apply is explicit.
240
+ agent-bridge archive import \
241
+ --provider local \
242
+ --db /path/to/target.sqlite3 \
243
+ --file "$HOME/.agent-bridge/archives/team.ndjson" \
244
+ --workspace team \
245
+ --dry-run
246
+
247
+ agent-bridge archive import \
248
+ --provider local \
249
+ --db /path/to/target.sqlite3 \
250
+ --file "$HOME/.agent-bridge/archives/team.ndjson" \
251
+ --workspace team \
252
+ --request-id 00000000-0000-4000-8000-000000000001 \
253
+ --apply
254
+ ```
255
+
256
+ Local commands use `--db`, `AGENT_BRIDGE_DB`, the selected shared config, or the
257
+ canonical local database path, in that order. PostgreSQL commands accept database
258
+ authority only from `AGENT_BRIDGE_ARCHIVE_DATABASE_URL` and require a login registered
259
+ for the archive boundary created by the current migrations. They do not fall back to
260
+ the schema-owner, gateway runtime, owner-control, or client database settings.
261
+
262
+ The archive digest detects accidental changes to the canonical bytes. It is neither
263
+ encryption nor authentication. Archives contain message content and routing metadata,
264
+ so protect them as private data. They exclude deliveries, delivery events, presence,
265
+ credentials, owner-control records, security events, and other control or security
266
+ state. The library API is available from `@creatornader/agent-bridge/archive`.
267
+ Import opens the archive once without following links. It verifies the file, replays
268
+ messages, then replays receipts through the same descriptor. Every pass checks framing,
269
+ order, counts, and the client-computed digest. Batches are limited by row count and a
270
+ 4 MiB byte budget. `--dry-run` and `--apply` are mutually exclusive.
271
+
272
+ ## Native disaster recovery
273
+
274
+ Native DR preserves one complete local authority or shared PostgreSQL deployment. Use
275
+ it for recovery, not for moving a workspace between providers. The framed `.abdr`
276
+ bundle records its provider, schema contract, and entry hashes. Commands calculate and
277
+ return a separate whole-file SHA-256 digest. The hashes detect changed bytes. The bundle
278
+ is not encrypted or authenticated, and it may contain credentials and message content.
279
+ Store and transfer it as private database material.
280
+
281
+ Local backup uses SQLite's online backup API, so the source may remain open. It accepts
282
+ only the local authority database. Gateway edge caches and outboxes are rejected because
283
+ PostgreSQL remains their authority. Restore requires a new target path and verifies the
284
+ exact local schema contract before publication.
285
+
286
+ ```bash
287
+ agent-bridge dr backup \
288
+ --provider local \
289
+ --source "$HOME/.agent-bridge/bridge.sqlite3" \
290
+ --output "$HOME/.agent-bridge/archives/local.abdr" \
291
+ --backup-id 00000000-0000-4000-8000-000000000010
292
+
293
+ agent-bridge dr verify \
294
+ --provider local \
295
+ --bundle "$HOME/.agent-bridge/archives/local.abdr"
296
+
297
+ agent-bridge dr restore \
298
+ --provider local \
299
+ --bundle "$HOME/.agent-bridge/archives/local.abdr" \
300
+ --target /path/to/new-bridge.sqlite3 \
301
+ --request-id 00000000-0000-4000-8000-000000000011
101
302
  ```
102
303
 
103
- ## Install client integrations
304
+ PostgreSQL backup takes a repeatable-read snapshot while holding the native DR fence.
305
+ It stores the `agent_bridge` schema dump plus a canonical inventory of required roles,
306
+ memberships, and default privileges. The backup excludes transient data from agent
307
+ instances, rate-limit buckets, request authority, and archive transaction authority.
308
+ Restore turns claimed deliveries into immediately retryable work because a database
309
+ restore cannot preserve a live lease.
310
+
311
+ PostgreSQL authority comes only from the process environment. Backup reads
312
+ `AGENT_BRIDGE_DR_SOURCE_DATABASE_URL`; restore reads
313
+ `AGENT_BRIDGE_DR_TARGET_DATABASE_URL`. URLs are not accepted as command arguments.
314
+ `pg_dump` and `pg_restore` must match the database major exactly. Native DR supports
315
+ PostgreSQL 15 through 18. If matching tools are not on `PATH`, pass their directory to
316
+ PostgreSQL backup, verify, or restore with `--tool-directory`. Local verification does
317
+ not use PostgreSQL tools and rejects that option.
104
318
 
105
- Agent identity and gateway credentials belong to each client process, not the shared `~/.agent-bridge/config` file.
319
+ ```bash
320
+ AGENT_BRIDGE_DR_SOURCE_DATABASE_URL='postgresql://backup-admin@db/agent_bridge' \
321
+ agent-bridge dr backup \
322
+ --provider postgres \
323
+ --output "$HOME/.agent-bridge/archives/postgres.abdr" \
324
+ --backup-id 00000000-0000-4000-8000-000000000020
325
+
326
+ agent-bridge dr verify \
327
+ --provider postgres \
328
+ --bundle "$HOME/.agent-bridge/archives/postgres.abdr"
329
+
330
+ AGENT_BRIDGE_DR_TARGET_DATABASE_URL='postgresql://restore-admin@new-db/agent_bridge' \
331
+ agent-bridge dr restore \
332
+ --provider postgres \
333
+ --bundle "$HOME/.agent-bridge/archives/postgres.abdr" \
334
+ --request-id 00000000-0000-4000-8000-000000000021 \
335
+ --accept-source-sql-risk
336
+ ```
337
+
338
+ PostgreSQL restore executes SQL from the dump, so use `--accept-source-sql-risk` only
339
+ for a bundle from a source you trust. The target must be a dedicated fresh database
340
+ with the same database name and PostgreSQL major as the backup. Restore requires a
341
+ superuser because it recreates object owners, role shells, memberships, and default
342
+ privileges. It never enables login on restored external principals. If a restore fails
343
+ after mutation begins, Agent Bridge disables new target connections and reports any
344
+ residual roles or recovery paths. Inspect and clean those artifacts before retrying.
345
+ Never run the source and restored target as active authorities at the same time.
346
+
347
+ Backup publication never replaces an existing output. Deterministic adjacent staging
348
+ paths use the backup or request UUID. A retry with the same UUID reports retained
349
+ recovery paths instead of overwriting them. File and directory synchronization is
350
+ strongest on platforms that support directory fsync; Windows cannot provide the same
351
+ directory durability proof. Successful Windows commands report
352
+ `directoryDurability: "unavailable"` or
353
+ `cleanupDirectoryDurability: "unavailable"` instead of claiming that proof. The
354
+ library API is available from
355
+ `@creatornader/agent-bridge/dr`.
356
+
357
+ ## Install host adapters
358
+
359
+ Agent identity and gateway credentials belong to each installed client, not the shared
360
+ `~/.agent-bridge/config` file.
106
361
 
107
362
  ```bash
108
363
  agent-bridge clients install codex --identity codex
@@ -110,7 +365,8 @@ agent-bridge clients install claude-code --identity claude-code
110
365
  agent-bridge clients install claude-desktop --identity claude-desktop
111
366
  ```
112
367
 
113
- For gateway mode, issue a distinct principal-bound token for each client and pass it only during that client's installation:
368
+ For gateway mode, use the owner enrollment workflow below for new credentials. The
369
+ older token handoff remains available for credentials issued by external tooling:
114
370
 
115
371
  ```bash
116
372
  AGENT_BRIDGE_CLIENT_TOKEN=<codex token> \
@@ -121,11 +377,19 @@ AGENT_BRIDGE_CLIENT_TOKEN=<claude-desktop token> \
121
377
  agent-bridge clients install claude-desktop --identity claude-desktop
122
378
  ```
123
379
 
124
- `--token` accepts the same value, but the environment form avoids putting a credential in shell history. Each install writes an owner-only backend file under `~/.agent-bridge/clients/`. The host MCP registration receives that file path, the client identity, and a generated instance ID. Tokens are not copied into the shared config, and one client cannot reuse a token bound to another principal.
380
+ `--token` accepts the same value, but the environment form avoids putting a credential in shell history. Each install writes an owner-only backend file under `~/.agent-bridge/clients/`. The host MCP registration receives that file path, the client identity, and a generated stable instance key. Tokens are not copied into the shared config, and one client cannot reuse a token bound to another principal.
125
381
 
126
- Codex and Claude Code installation uses their native MCP commands. Claude Desktop installation merges the `agent-bridge` server into its JSON config with an atomic write. Restart the client after installation.
382
+ The `codex` adapter uses the Codex profile shared by the Codex CLI and the Codex
383
+ surface in the ChatGPT desktop app; it does not install two independent registrations.
384
+ Claude Code installation uses its native MCP command. Claude Desktop installation
385
+ merges the `agent-bridge` server into its JSON config with an atomic write. Restart the
386
+ client after installation.
127
387
 
128
- The runtime contracts are under [`clients/`](clients/). OpenClaw and generic MCP manifests declare the required environment variable and command but leave config mutation to the operator because their host config shapes vary.
388
+ The v1 host-adapter manifests are under [`clients/`](clients/). Their compatibility
389
+ field is named `runtime`, but its value identifies the installation target, not a
390
+ unique process. OpenClaw and generic MCP manifests declare the required environment
391
+ variables and command but leave config mutation to the operator because their host
392
+ config shapes vary.
129
393
 
130
394
  [`SKILL.md`](SKILL.md) provides concise runtime-neutral operating instructions for agents. [`llms.txt`](llms.txt) gives tools and model crawlers a compact map of the package, modes, commands, and identity rules.
131
395
 
@@ -154,21 +418,174 @@ $grant$;
154
418
 
155
419
  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
420
 
157
- Create a workspace, agent, and credential with the migration or provisioning connection. Store only a SHA-256 token hash:
421
+ Migration 014 provides the database owner control plane. The schema-owner connection
422
+ registers provisioning and inventory logins with `register_control_member`. It can
423
+ remove them with `revoke_control_member`. Do not grant the database-specific operator,
424
+ auditor, or owner roles directly. Operators call the narrow
425
+ `control_provision`, `control_rotate_credential`, and `control_revoke_credential`
426
+ functions with a fresh request UUID. Operators and auditors can call
427
+ `control_credential_inventory`; it never exposes credential digest material. Inventory
428
+ accepts an optional workspace, an optional `(created_at, credential_id)` cursor, and
429
+ a row limit. The default is 100 rows and the maximum is 1,000.
430
+
431
+ Each mutation takes a transaction lock for its request UUID before checking the
432
+ request ledger. An identical concurrent call returns the stored result. Reusing the
433
+ UUID with changed content fails. Provisioning reuses an existing workspace only when
434
+ its name matches, so one workspace can contain several principals. Rotation supports
435
+ credentials without an expiry and a null grace cutoff for immediate replacement. A
436
+ rotation request includes the expected workspace and principal. The function checks
437
+ both while it locks the predecessor, then returns the stored canonical identity.
438
+ The database derives the audit actor from `session_user`.
439
+
440
+ This temporary operator workflow creates an eligible login and registers it through
441
+ the protected SQL boundary. The owner CLI then provisions a token-bound `codex`
442
+ principal. It generates the raw token locally, writes it to a private enrollment file
443
+ before contacting PostgreSQL, and sends only the SHA-256 hash to the database.
158
444
 
159
- ```sql
160
- insert into agent_bridge.workspaces (id, name)
161
- values ('team', 'Team');
445
+ ```bash
446
+ OPERATOR_LOGIN="agent_bridge_operator_$(date +%s)"
447
+ OPERATOR_PASSWORD="$(node -e "process.stdout.write(require('node:crypto').randomBytes(32).toString('hex'))")"
448
+ MEMBERSHIP_REQUEST="$(node -e "process.stdout.write(require('node:crypto').randomUUID())")"
449
+ PROVISION_REQUEST="$(node -e "process.stdout.write(require('node:crypto').randomUUID())")"
450
+ export AGENT_BRIDGE_OPERATOR_LOGIN="$OPERATOR_LOGIN"
451
+ export AGENT_BRIDGE_OPERATOR_PASSWORD="$OPERATOR_PASSWORD"
452
+ export AGENT_BRIDGE_MEMBERSHIP_REQUEST="$MEMBERSHIP_REQUEST"
453
+ export AGENT_BRIDGE_PROVISION_REQUEST="$PROVISION_REQUEST"
454
+
455
+ psql -v ON_ERROR_STOP=1 "$AGENT_BRIDGE_DATABASE_URL" <<'SQL'
456
+ \getenv operator_login AGENT_BRIDGE_OPERATOR_LOGIN
457
+ \getenv operator_password AGENT_BRIDGE_OPERATOR_PASSWORD
458
+ \getenv membership_request AGENT_BRIDGE_MEMBERSHIP_REQUEST
459
+ SELECT format(
460
+ 'CREATE ROLE %I LOGIN PASSWORD %L',
461
+ :'operator_login',
462
+ :'operator_password'
463
+ ) \gexec
464
+ SELECT * FROM agent_bridge.register_control_member(
465
+ :'membership_request'::uuid,
466
+ :'operator_login'::name,
467
+ 'operator'
468
+ );
469
+ SQL
470
+
471
+ export AGENT_BRIDGE_OPERATOR_DATABASE_URL="postgresql://${OPERATOR_LOGIN}:${OPERATOR_PASSWORD}@host/database"
472
+ export AGENT_BRIDGE_URL="https://bridge.example.com"
473
+ ENROLLMENT_FILE="${AGENT_BRIDGE_ENROLLMENT_DIR:-$HOME/.agent-bridge/enrollments}/${PROVISION_REQUEST}.json"
474
+
475
+ agent-bridge owner provision \
476
+ --request-id "$PROVISION_REQUEST" \
477
+ --workspace team \
478
+ --workspace-name Team \
479
+ --identity codex \
480
+ --runtime codex \
481
+ --instance codex-machine \
482
+ --scope-set release-a-full
483
+
484
+ agent-bridge clients install codex --enrollment-file "$ENROLLMENT_FILE"
485
+
486
+ REVOKE_REQUEST="$(node -e "process.stdout.write(require('node:crypto').randomUUID())")"
487
+ export AGENT_BRIDGE_REVOKE_REQUEST="$REVOKE_REQUEST"
488
+ psql -v ON_ERROR_STOP=1 "$AGENT_BRIDGE_DATABASE_URL" <<'SQL'
489
+ \getenv operator_login AGENT_BRIDGE_OPERATOR_LOGIN
490
+ \getenv revoke_request AGENT_BRIDGE_REVOKE_REQUEST
491
+ SELECT * FROM agent_bridge.revoke_control_member(
492
+ :'revoke_request'::uuid,
493
+ :'operator_login'::name,
494
+ 'operator'
495
+ );
496
+ SELECT format('DROP ROLE %I', :'operator_login') \gexec
497
+ SQL
498
+ unset OPERATOR_PASSWORD AGENT_BRIDGE_OPERATOR_PASSWORD
499
+ unset AGENT_BRIDGE_OPERATOR_LOGIN AGENT_BRIDGE_MEMBERSHIP_REQUEST
500
+ unset AGENT_BRIDGE_PROVISION_REQUEST AGENT_BRIDGE_REVOKE_REQUEST
501
+ unset AGENT_BRIDGE_OPERATOR_DATABASE_URL
502
+ ```
162
503
 
163
- insert into agent_bridge.agents (workspace_id, principal, runtime_type)
164
- values ('team', 'codex', 'codex')
165
- returning id;
504
+ `AGENT_BRIDGE_OPERATOR_DATABASE_URL` is the owner CLI's only database authority. It
505
+ does not fall back to the schema migration URL, runtime URL, shared client config, or
506
+ active client identity. `owner inventory` returns at most 100 rows by default and
507
+ 1,000 when requested. Its opaque cursor is bound to the selected workspace.
508
+
509
+ Provision and rotation enrollment files stay under
510
+ `$AGENT_BRIDGE_ENROLLMENT_DIR` or `~/.agent-bridge/enrollments`. The CLI requires an
511
+ owner-only root and private path components. It refuses symlinks, path escapes, and
512
+ directory replacement during an operation. Every file has a monotonic revision. A
513
+ per-file exclusive lock covers the read, state transition, database call, and client
514
+ installation side effects. Each transition compares the revision, state, request,
515
+ operation, and token with the current file before atomic replacement.
516
+
517
+ If provisioning fails, rerun the same operation with `--resume "$ENROLLMENT_FILE"`.
518
+ A process crash can leave the adjacent lock file behind. Do not delete it manually.
519
+ After at least 60 seconds, use `--recover-lock` with `--resume` or the client install
520
+ command. Recovery succeeds only when the lock belongs to this host and user and its
521
+ recorded process no longer exists. The stored request UUID, inputs, instance, and raw
522
+ token are reused exactly.
523
+
524
+ `clients install <runtime> --enrollment-file <path>` derives the gateway URL,
525
+ workspace, principal, instance, and token from the file. It rejects conflicting flags
526
+ or environment values. Provisioning registers the host MCP server. Rotation updates
527
+ only that instance's existing private backend file and never repeats host registration.
528
+ The backend must contain the predecessor credential ID, principal, and instance metadata
529
+ written by an enrollment-based provision. Rotation fails closed for older backend files
530
+ without this metadata. It also verifies the exact live MCP registration before replacing
531
+ the token.
532
+ The installer removes the enrollment file only after installation succeeds. A failed
533
+ delete returns `enrollmentStatus: "consumed-file-retained"` and the consumed file path.
534
+ An already missing file returns `consumed-file-missing`. If unlink succeeds but the
535
+ directory identity check or fsync fails, the result is
536
+ `consumed-deletion-durability-unknown`; it never claims that the path was retained.
537
+ `lockReleaseStatus` separately reports `released`, `retained`, or `durability-unknown`.
538
+ A lock-release failure after successful installation does not replace the installation
539
+ result with a generic error. The consumed artifact no longer contains the raw token. A retry validates the
540
+ live backend and MCP registration before deleting a retained consumed file.
541
+
542
+ `--token` and `AGENT_BRIDGE_CLIENT_TOKEN` remain available for the older manual
543
+ installation flow, but they cannot be combined with `--enrollment-file`. The final SQL
544
+ block revokes and drops the temporary operator after the client installation has
545
+ stored the credential.
546
+
547
+ Rotate the credential for one installed instance with an explicit grace cutoff or
548
+ `--invalidate-immediately`, then consume the resulting enrollment file:
166
549
 
167
- insert into agent_bridge.credentials (workspace_id, agent_id, token_hash, label)
168
- values ('team', '<agent uuid>', '<sha256 token hash>', 'codex laptop');
550
+ ```bash
551
+ ROTATE_REQUEST="$(node -e "process.stdout.write(require('node:crypto').randomUUID())")"
552
+ ROTATION_FILE="${AGENT_BRIDGE_ENROLLMENT_DIR:-$HOME/.agent-bridge/enrollments}/${ROTATE_REQUEST}.json"
553
+ agent-bridge owner rotate \
554
+ --request-id "$ROTATE_REQUEST" \
555
+ --credential-id "<current credential UUID>" \
556
+ --workspace team \
557
+ --identity codex \
558
+ --runtime codex \
559
+ --instance codex-machine \
560
+ --scope-set release-a-full \
561
+ --invalidate-immediately
562
+ agent-bridge clients install codex --enrollment-file "$ROTATION_FILE"
169
563
  ```
170
564
 
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.
565
+ Control membership is an offline SQL administration boundary. The gateway and MCP
566
+ server do not expose it. Runtime readiness compares PostgreSQL membership with the
567
+ append-only registry. It fails for an unregistered operator or auditor, an external
568
+ owner holder, a missing registered grant, any extra role inherited by a registered
569
+ member, or any role that inherits a registered member. Protected operations recheck
570
+ the live registry and direct membership after taking a member-global transaction lock,
571
+ followed by capability locks in operator-before-auditor order. Registration and
572
+ revocation use the same order. A stale session that remains in a revoked role cannot
573
+ continue operating. The schema owner is the only bootstrap holder of all three control
574
+ roles and the only role with ordinary execution authority on the registration
575
+ functions. PostgreSQL superusers remain inside this trusted database-administration
576
+ boundary. Readiness checks the running PostgreSQL major on every call, so upgrading an
577
+ already-migrated database to an uncertified major disables the runtime.
578
+
579
+ Direct credential inserts default to empty scopes rather than full access. Issued
580
+ identity, scope, label, expiry, and lineage fields cannot be edited directly. Existing
581
+ migration-013 rows retain their values and remain revocable during upgrade. Runtime
582
+ readiness compares the live owner-control catalog with the protected migration-014
583
+ attestation. Migration 014 refuses to create that baseline if critical workspace,
584
+ agent, credential, index, trigger, function, ownership, or row-isolation dependencies
585
+ have already drifted. Unrelated additive schema objects do not change readiness.
586
+ The migration also blocks unsafe direct and default privileges on protected objects.
587
+ Global and workspace inventory use fixed-origin expression indexes for their keyset
588
+ ordering.
172
589
 
173
590
  Start the gateway:
174
591
 
@@ -190,7 +607,12 @@ AGENT_BRIDGE_URL=https://bridge.example.com
190
607
  AGENT_BRIDGE_WORKSPACE=team
191
608
  ```
192
609
 
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.
610
+ These shared settings are enough before `clients install`. The secure flow reads
611
+ the principal-bound token and exact client settings from an enrollment file. The
612
+ compatibility flow accepts `AGENT_BRIDGE_CLIENT_TOKEN`. Both write a private client
613
+ backend and register `AGENT_BRIDGE_AGENT`, `AGENT_BRIDGE_INSTANCE`, and that file's
614
+ path with the host. Gateway initialization checks readiness, token validity, and that
615
+ the token-bound principal matches that process identity.
194
616
 
195
617
  ## Offline gateway behavior
196
618
 
@@ -200,14 +622,15 @@ When a send cannot reach the gateway:
200
622
 
201
623
  1. The validated message is written to the SQLite outbox with a stable idempotency key.
202
624
  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.
625
+ 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
626
  4. An ambiguous retry is safe because the gateway enforces idempotency and rejects changed content under the same key.
205
627
 
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.
628
+ 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
629
 
208
630
  ## CLI
209
631
 
210
632
  ```bash
633
+ agent-bridge --version
211
634
  agent-bridge init --provider local
212
635
  agent-bridge doctor
213
636
  agent-bridge status
@@ -215,10 +638,17 @@ agent-bridge pending
215
638
  agent-bridge send --type request --target worker "Run the task"
216
639
  agent-bridge post --category goal-update --project agent-bridge "Gateway is ready"
217
640
  agent-bridge inbox --limit 20
641
+ agent-bridge sent --limit 20
642
+ agent-bridge history --mailbox all --receipt-state any
218
643
  agent-bridge get --since 24h --unacked-by codex
219
644
  agent-bridge history --thread-id release-1
220
645
  agent-bridge acknowledge --ids <message-uuid>
221
646
  agent-bridge claim --lease-ms 30000
647
+ agent-bridge send --target worker --delivery-policy '{"mode":"leased","maxAttempts":3,"retryBaseDelayMs":1000,"retryMaxDelayMs":60000,"retryJitterRatio":0.2}' "work"
648
+ agent-bridge deliveries --state dead
649
+ agent-bridge delivery-events --delivery-id <uuid>
650
+ agent-bridge cancel --delivery-id <uuid>
651
+ agent-bridge requeue --delivery-id <uuid>
222
652
  agent-bridge extend --delivery-id <uuid> --lease-token <uuid>
223
653
  agent-bridge ack --delivery-id <uuid> --lease-token <uuid>
224
654
  agent-bridge nack --delivery-id <uuid> --lease-token <uuid> --error "retry later"
@@ -228,9 +658,18 @@ agent-bridge sync
228
658
  agent-bridge watch
229
659
  ```
230
660
 
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.
661
+ `--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.
662
+
663
+ 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.
664
+
665
+ 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.
666
+
667
+ 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.
668
+
669
+ `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
670
 
233
671
  `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.
672
+ Each exact project filter gets its own local watch checkpoint. Switching between a filtered and unfiltered watch cannot advance the other checkpoint.
234
673
 
235
674
  Unknown flags and missing option values fail before a message can be sent. This prevents a misspelled target flag from becoming a broadcast.
236
675
 
@@ -250,14 +689,36 @@ Local and gateway providers also expose:
250
689
  - `extend`
251
690
  - `acknowledge`
252
691
  - `negative_acknowledge`
692
+ - `list_deliveries`
693
+ - `list_delivery_events`
694
+ - `cancel_delivery`
695
+ - `requeue_delivery`
253
696
  - `heartbeat`
254
697
  - `presence`
255
698
 
256
699
  Legacy Supabase does not advertise delivery or presence tools because its schema cannot enforce those operations.
257
700
 
701
+ 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.
702
+
258
703
  ## Identity and security
259
704
 
260
705
  - `~/.agent-bridge/config` contains backend settings only. A stored `AGENT_BRIDGE_AGENT` value is ignored.
706
+ - Workspace is the tenant boundary. Principal is the durable agent identity and message source.
707
+ - `AGENT_BRIDGE_INSTANCE` is an optional caller-supplied stable consumer key. Supported
708
+ installers generate and persist one for each installed client. Direct clients should
709
+ do the same when they need separate consumer state or presence.
710
+ - Unless `AGENT_BRIDGE_CURSOR` sets an explicit path, a present instance key selects
711
+ the cursor path. The key also selects the delivery lease owner and presence row.
712
+ Processes that share a key also share those resources. The gateway does not bind the
713
+ key to an installer registration. It is not a PID, unique live process, or
714
+ conversation ID.
715
+ - When the key and explicit cursor path are both omitted, cursor storage uses
716
+ `default`. Delivery leases fall back to the principal, and presence operations reject
717
+ the request because presence requires an instance key.
718
+ - Presence is instance-keyed in the released protocol. Per-process presence requires
719
+ an additive identity instead of a silent change to lease ownership or cursor paths.
720
+ - Session and thread identifiers are message and application context. They do not
721
+ select credentials, workspace, principal, or delivery ownership.
261
722
  - Local clients bind identity through their process environment or an explicit CLI send argument.
262
723
  - Gateway tokens bind workspace and principal. The API ignores caller-supplied source and workspace fields.
263
724
  - Gateway clients use separate owner-only backend files and principal-bound tokens.
@@ -265,7 +726,8 @@ Legacy Supabase does not advertise delivery or presence tools because its schema
265
726
  - Remote HTTP must use TLS. Plain HTTP is accepted only on loopback.
266
727
  - Local config, database, WAL, and shared-memory files use owner-only permissions where the platform supports POSIX modes.
267
728
  - The private `agent_bridge` schema is denied to Supabase `anon` and `authenticated` Data API roles.
268
- - Expired presence rows are pruned during heartbeat and listing. Each agent is capped at 128 active instances, and each workspace is capped at 4,096.
729
+ - Expired instance-presence rows are pruned during heartbeat and listing. Each agent
730
+ is capped at 128 active instance keys, and each workspace is capped at 4,096.
269
731
  - Errors expose stable codes without database URLs, tokens, or provider response bodies.
270
732
  - Delivery work is at least once, not exactly once. Consumers must make external side effects idempotent.
271
733
 
@@ -283,30 +745,63 @@ Legacy mode keeps the v1 `shared_context` and `ack_context` RPC behavior. It add
283
745
 
284
746
  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
747
 
748
+ 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.
749
+
286
750
  The original v1 schema remains in [`sql/setup.sql`](sql/setup.sql). Ordered gateway migrations live in [`sql/migrations/`](sql/migrations/).
287
751
 
288
752
  ## Health and operations
289
753
 
290
- `doctor` and `status` return JSON with:
754
+ `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
755
 
292
756
  - Provider and actual schema version.
293
- - Bound workspace, agent, and instance.
757
+ - Bound workspace, agent, and consumer instance key.
294
758
  - Endpoint or database path.
295
759
  - 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.
760
+ - Pending, claimed, retrying, and dead delivery counts, plus due versus scheduled work, expired leases, oldest-due time, and bounded queue lag.
761
+ - Gateway reachability, outbox due/scheduled/leased/blocked depth, cache size, blocked age/attempt/error, next retry, last outbound/inbound sync, and last attempt.
762
+ - Explicit check results. Blocked outbox rows, expired leases, dead deliveries, or a known-unreachable gateway prevent a healthy result.
763
+
764
+ 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.
765
+
766
+ 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.
767
+
768
+ 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
769
 
299
770
  The gateway exposes unauthenticated `/readyz`. `/v2/status` and `/metrics` require a valid credential.
300
771
 
772
+ 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`.
773
+
774
+ 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.
775
+
776
+ 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.
777
+
778
+ 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.
779
+
780
+ 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.
781
+
782
+ 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.
783
+
301
784
  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
785
 
303
786
  ```sql
304
787
  drop role if exists agent_bridge_gateway;
305
788
  do $cleanup$
306
789
  declare
307
- runtime_role text := 'agent_bridge_runtime_' || substr(md5(current_database()), 1, 16);
790
+ suffix text := substr(md5(current_database()), 1, 16);
791
+ role_name text;
308
792
  begin
309
- execute format('drop role if exists %I', runtime_role);
793
+ foreach role_name in array array[
794
+ 'agent_bridge_runtime_' || suffix,
795
+ 'agent_bridge_data_owner_' || suffix,
796
+ 'agent_bridge_context_reader_' || suffix,
797
+ 'agent_bridge_event_writer_' || suffix
798
+ ] loop
799
+ if exists (select 1 from pg_roles where rolname=role_name) then
800
+ execute format('reassign owned by %I to %I', role_name, current_user);
801
+ execute format('drop owned by %I', role_name);
802
+ execute format('drop role %I', role_name);
803
+ end if;
804
+ end loop;
310
805
  end
311
806
  $cleanup$;
312
807
  ```
@@ -322,10 +817,27 @@ npm run build
322
817
  npm pack
323
818
  ```
324
819
 
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.
820
+ Set `AGENT_BRIDGE_TEST_DATABASE_URL` to run the live PostgreSQL contract and migration
821
+ tests. CI runs Node 22 and 24 on Linux, macOS, and Windows. PostgreSQL 15 through 18 each
822
+ pack and install the npm tarball, register a least-privilege operator, and exercise
823
+ owner provision, inventory, rotation, and revocation. Before applying a Windows DACL,
824
+ private paths must be owned by the current account SID or the active token's default
825
+ owner SID. The final owner and sole protected FullControl rule must use the account SID.
826
+ Verification-only paths must already satisfy that final policy. Node file identity
827
+ checks and native Windows reparse attributes must also agree before and after policy
828
+ validation; symlinks, junctions, and other reparse objects fail closed. CI applies and
829
+ verifies this policy on native Windows runners. Windows ACL race and directory
830
+ durability behavior has not yet been proved on a dedicated Windows host.
326
831
 
327
832
  ## Documentation
328
833
 
834
+ - [ROADMAP.md](ROADMAP.md): released work, unreleased implementation, and the remaining program.
835
+ - [SECURITY.md](SECURITY.md): supported versions, release integrity, private vulnerability reporting, and security boundaries.
836
+ - [docs/ecosystem.md](docs/ecosystem.md): product boundary, adjacent systems, and interoperability direction.
837
+ - [docs/troubleshooting.md](docs/troubleshooting.md): MCP startup, identity, backend, and client recovery.
838
+ - [ADR-0001](docs/decisions/0001-protocol-layers-and-acknowledgment-semantics.md): protocol layers and acknowledgment semantics.
839
+ - [ADR-0002](docs/decisions/0002-canonical-operation-contract-registry.md): canonical contracts, generated artifacts, discovery, and version negotiation.
840
+ - [ADR-0003](docs/decisions/0003-host-adapters-and-consumer-instance-keys.md): host layers and consumer instance-key semantics.
329
841
  - [docs/architecture-v2.md](docs/architecture-v2.md): protocol and storage decisions.
330
842
  - [SKILL.md](SKILL.md): runtime-neutral agent operating instructions.
331
843
  - [llms.txt](llms.txt): compact machine-readable project map.