@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.
- package/CHANGELOG.md +63 -1
- package/README.md +467 -29
- package/ROADMAP.md +104 -0
- package/SECURITY.md +73 -0
- package/SKILL.md +68 -2
- package/bin/agent-bridge +2 -2
- package/clients/claude-desktop.json +3 -1
- package/dist/archive.d.ts +163 -0
- package/dist/archive.js +50 -0
- package/dist/archive.js.map +1 -0
- package/dist/bridge-domain-CBX3IVqC.d.ts +153 -0
- package/dist/{bridge-service-CfqMcMAn.d.ts → bridge-service-hX1ZQp4W.d.ts} +15 -5
- package/dist/bridge-store-DPVSCwfs.d.ts +102 -0
- package/dist/bridge.d.ts +76 -17
- package/dist/bridge.js +52 -24
- package/dist/chunk-2DFNOLMO.js +618 -0
- package/dist/chunk-2DFNOLMO.js.map +1 -0
- package/dist/{chunk-KVR4JBRF.js → chunk-2EVLG3YA.js} +2 -2
- package/dist/{chunk-KVR4JBRF.js.map → chunk-2EVLG3YA.js.map} +1 -1
- package/dist/chunk-45BZGB77.js +709 -0
- package/dist/chunk-45BZGB77.js.map +1 -0
- package/dist/chunk-4VB44KJU.js +230 -0
- package/dist/chunk-4VB44KJU.js.map +1 -0
- package/dist/chunk-5GMB3A77.js +1013 -0
- package/dist/chunk-5GMB3A77.js.map +1 -0
- package/dist/chunk-A6W4QALF.js +1258 -0
- package/dist/chunk-A6W4QALF.js.map +1 -0
- package/dist/chunk-BZLMF5O5.js +328 -0
- package/dist/chunk-BZLMF5O5.js.map +1 -0
- package/dist/{chunk-AXL2I7FH.js → chunk-CEXBMOI5.js} +240 -79
- package/dist/chunk-CEXBMOI5.js.map +1 -0
- package/dist/chunk-GADH244D.js +527 -0
- package/dist/chunk-GADH244D.js.map +1 -0
- package/dist/chunk-MBPZXTJA.js +2428 -0
- package/dist/chunk-MBPZXTJA.js.map +1 -0
- package/dist/chunk-NCO7RLBM.js +146 -0
- package/dist/chunk-NCO7RLBM.js.map +1 -0
- package/dist/chunk-Q6PPMXJC.js +1050 -0
- package/dist/chunk-Q6PPMXJC.js.map +1 -0
- package/dist/{chunk-4KAOBC5X.js → chunk-SPSMZOCJ.js} +190 -165
- package/dist/chunk-SPSMZOCJ.js.map +1 -0
- package/dist/chunk-UAHMP6GC.js +54 -0
- package/dist/chunk-UAHMP6GC.js.map +1 -0
- package/dist/{chunk-YD6YJATN.js → chunk-VOQIDTCJ.js} +4 -2
- package/dist/chunk-VOQIDTCJ.js.map +1 -0
- package/dist/chunk-WAI2IYIQ.js +554 -0
- package/dist/chunk-WAI2IYIQ.js.map +1 -0
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +3577 -250
- package/dist/cli.js.map +1 -1
- package/dist/client-config.d.ts +1 -1
- package/dist/client-config.js +1 -1
- package/dist/client-runtime.d.ts +10 -6
- package/dist/client-runtime.js +9 -6
- package/dist/contracts/generate.d.ts +2 -0
- package/dist/contracts/generate.js +247 -0
- package/dist/contracts/generate.js.map +1 -0
- package/dist/dr.d.ts +324 -0
- package/dist/dr.js +124 -0
- package/dist/dr.js.map +1 -0
- package/dist/gateway-CggQ8vTC.d.ts +78 -0
- package/dist/gateway-main.js +190 -8
- package/dist/gateway-main.js.map +1 -1
- package/dist/gateway.d.ts +4 -3
- package/dist/gateway.js +4 -3
- package/dist/index.js +11 -8
- package/dist/index.js.map +1 -1
- package/dist/postgres-bridge-store-Bfd3hRId.d.ts +49 -0
- package/dist/server.d.ts +3 -2
- package/dist/server.js +13 -8
- package/dist/sqlite.d.ts +94 -12
- package/dist/sqlite.js +7 -4
- package/docs/architecture-v2.md +375 -0
- package/docs/decisions/0001-protocol-layers-and-acknowledgment-semantics.md +85 -0
- package/docs/decisions/0002-canonical-operation-contract-registry.md +46 -0
- package/docs/ecosystem.md +54 -0
- package/docs/postmortems/2026-07-08-wrapper-source-drift.md +82 -0
- package/docs/troubleshooting.md +89 -0
- package/llms.txt +96 -4
- package/openapi/agent-bridge-v2.openapi.json +9610 -0
- package/package.json +24 -4
- package/schemas/agent-bridge-v2.capabilities.json +1829 -0
- package/schemas/agent-bridge-v2.mcp.json +2074 -0
- package/schemas/agent-bridge-v2.schema.json +4560 -0
- package/sql/migrations/008_message_projects.sql +36 -0
- package/sql/migrations/009_mailbox_query_indexes.sql +28 -0
- package/sql/migrations/010_publisher_delivery_policy.sql +210 -0
- package/sql/migrations/011_credential_security.sql +875 -0
- package/sql/migrations/012_request_authority.sql +177 -0
- package/sql/migrations/013_row_isolation.sql +495 -0
- package/sql/migrations/014_owner_control_plane.sql +1641 -0
- package/sql/migrations/015_portable_archives.sql +1813 -0
- package/sql/migrations/016_native_dr_fence.sql +1005 -0
- package/bin/CLAUDE.md +0 -11
- package/dist/bridge-domain-BcnBeHOn.d.ts +0 -97
- package/dist/bridge-store-BuGjxTn4.d.ts +0 -61
- package/dist/chunk-4KAOBC5X.js.map +0 -1
- package/dist/chunk-6MAZ6YLZ.js +0 -146
- package/dist/chunk-6MAZ6YLZ.js.map +0 -1
- package/dist/chunk-AXL2I7FH.js.map +0 -1
- package/dist/chunk-BV6W4AJK.js +0 -211
- package/dist/chunk-BV6W4AJK.js.map +0 -1
- package/dist/chunk-CKSKUBDQ.js +0 -153
- package/dist/chunk-CKSKUBDQ.js.map +0 -1
- package/dist/chunk-JOUBDRYX.js +0 -272
- package/dist/chunk-JOUBDRYX.js.map +0 -1
- package/dist/chunk-JTC5YURG.js +0 -49
- package/dist/chunk-JTC5YURG.js.map +0 -1
- package/dist/chunk-O26W5QHS.js +0 -409
- package/dist/chunk-O26W5QHS.js.map +0 -1
- package/dist/chunk-WEULPMUY.js +0 -988
- package/dist/chunk-WEULPMUY.js.map +0 -1
- package/dist/chunk-YD6YJATN.js.map +0 -1
- package/dist/gateway-BULozv3K.d.ts +0 -54
package/README.md
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
# Agent Bridge
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@creatornader/agent-bridge)
|
|
4
|
+
[](https://github.com/creatornader/agent-bridge/actions/workflows/test.yml)
|
|
5
|
+
[](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
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
|
92
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
-
```
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
168
|
-
|
|
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
|
-
|
|
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
|
|
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`
|
|
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 `
|
|
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
|
-
`
|
|
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
|
-
`
|
|
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
|
|
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
|
-
|
|
717
|
+
suffix text := substr(md5(current_database()), 1, 16);
|
|
718
|
+
role_name text;
|
|
308
719
|
begin
|
|
309
|
-
|
|
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
|
|
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.
|