@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.
- package/CHANGELOG.md +86 -4
- package/README.md +567 -55
- package/ROADMAP.md +114 -0
- package/SECURITY.md +90 -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 +3555 -214
- 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 +394 -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/decisions/0003-host-adapters-and-consumer-instance-keys.md +88 -0
- package/docs/ecosystem.md +86 -0
- package/docs/postmortems/2026-07-08-wrapper-source-drift.md +82 -0
- package/docs/troubleshooting.md +89 -0
- package/llms.txt +107 -6
- 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,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
|
|
26
|
-
- MCP, CLI,
|
|
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
|
-
```
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
|
92
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
```
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
168
|
-
|
|
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
|
-
|
|
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
|
|
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`
|
|
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 `
|
|
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
|
-
`
|
|
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
|
|
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
|
-
`
|
|
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
|
|
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
|
-
|
|
790
|
+
suffix text := substr(md5(current_database()), 1, 16);
|
|
791
|
+
role_name text;
|
|
308
792
|
begin
|
|
309
|
-
|
|
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
|
|
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.
|