Agent Bridge lets AI agents message each other and hand off work across tools, sessions, and machines. Messages wait while an agent is offline and are still there after it restarts.
Use it through MCP, a CLI, a Node library, or an authenticated HTTPS gateway. Start locally with SQLite. Use PostgreSQL when agents need to communicate across machines.
It supports two operating modes:
| Mode | Store | Use case |
|---|---|---|
| Local | SQLite WAL | One machine, no service or account |
| Gateway | PostgreSQL plus a local SQLite edge store | Cross-machine messaging, offline sends, claims, retries, and presence |
PostgreSQL is the authority in gateway mode. SQLite holds local-only messages or gateway edge state. Supabase can host PostgreSQL, but Agent Bridge does not depend on its client APIs. The direct Supabase runtime adapter was removed in 0.6.0.
Install the published package and run a two-principal local proof. Local mode needs no account, daemon, or external database.
npm install --global @creatornader/agent-bridge
agent-bridge init --provider local
agent-bridge demoInstall Agent Bridge into a supported client after the demo succeeds:
agent-bridge clients install codex --identity codex
agent-bridge clients install claude-code --identity claude-code
agent-bridge clients install claude-desktop --identity claude-desktopRestart the client, then run agent-bridge doctor --json if the MCP server does not
attach. The troubleshooting guide covers executable paths,
client configuration, and recovery steps.
The repository includes a loopback-only Compose stack for development and evaluation. It starts PostgreSQL 16, runs the ordered migrations, creates a restricted runtime login, and starts the authenticated gateway.
mkdir -m 700 .secrets
node -e "process.stdout.write(require('node:crypto').randomBytes(32).toString('hex'))" > .secrets/postgres_password
node -e "process.stdout.write(require('node:crypto').randomBytes(32).toString('hex'))" > .secrets/runtime_password
chmod 600 .secrets/*
docker compose up --build --wait gateway
curl --fail http://127.0.0.1:8787/readyzThe checked-in stack publishes the gateway and PostgreSQL only on loopback. It is not a production TLS deployment. Read the deployment guide before exposing a gateway, changing network bindings, upgrading the schema, or relying on the named volume for persistent data.
The repository also maintains a Fly.io reference that does not depend on a specific
database host. It lives at
deploy/fly.toml. It deliberately omits an app name and release
command. Check the local contract before creating or changing any Fly resource:
npm run preflight:fly -- --jsonBefore migrating an existing PostgreSQL database, run the read-only production preflight with schema-owner authority:
AGENT_BRIDGE_DATABASE_URL="..." npm run preflight:postgres:production -- --json --require-sslThe command checks the PostgreSQL major, database and role authority, existing
migration ledger, derived-role collisions, and the legacy shared_context import
shape inside a read-only transaction. authority.roles reports migration capability:
the schema owner may be a true superuser or a managed-provider administrator with
CREATEROLE. authority.native_dr reports source-backup capability. The source normally
connects as the schema owner, which must be a true superuser or hold BYPASSRLS, so
CREATEROLE alone can run migrations but cannot create a native backup. A restore target
uses its superuser session to validate a suspended restored schema-owner shell. The
migration suite tests both authority forms on PostgreSQL 15 through
18. The command does not print the connection URL or apply DDL. --require-ssl rejects
an unencrypted public database connection. Passing the preflight does not replace a
backup or authorize migration. When a session pooler terminates TLS, the check inspects
the client socket instead of the pooler's separate database connection.
After an operator supplies an existing app, npm run preflight:fly -- --app <app> --json adds read-only account, app, machine, config, and secret-name observations.
The command does not deploy, migrate, scale, restart, or print environment-variable
values. The Fly production procedure
keeps database migration and runtime-role bootstrap outside the gateway process.
The Fly gateway expects two secrets: the restricted runtime database URL and a
base64-encoded PostgreSQL CA bundle. It decodes the CA in memory and verifies both the
certificate chain and database hostname. It never needs a CA file inside the image.
Build the production image with its source revision, then confirm the running gateway before moving clients or starting the full proof:
fly deploy --config deploy/fly.toml --app <app> \
--build-arg AGENT_BRIDGE_BUILD_REVISION="$(git rev-parse HEAD)"
AGENT_BRIDGE_URL="https://<app>.fly.dev" AGENT_BRIDGE_TOKEN="..." \
npm run verify:gateway:release -- \
--version "$(node -p "require('./package.json').version")" \
--revision "$(git rev-parse HEAD)"The authenticated check compares the running package and Git revision with the intended release. It also requires protocol 2.1, request authority, and row isolation. It prints no credential. A missing or different revision stops the rollout.
The manual gateway production proof workflow tests an existing gateway after this
deployment gate passes. GitHub must run it from main in the protected
agent-bridge-production-proof environment. Separate sender and receiver jobs prove
offline outbox replay, exact idempotency, claim, and acknowledgment. The workflow then
restarts the single Fly machine, requires a later successful machine start event, and
verifies the message and settled delivery with a fresh receiver instance and a new
SQLite edge file. Use a new proof workspace for each release run. Owner provisioning
refuses a second agent with the same principal in one workspace, and revoked proof
credentials cannot be rotated.
Its versioned receipts contain only identifiers, times, principal and workspace labels,
the gateway origin, checks, and SHA-256 host evidence. They omit credentials, database
URLs, message content, and SQLite files.
- Immutable messages with UUIDv7 IDs and opaque server cursors.
- Indexed source, thread, timestamp, workspace, and delivery queries.
- Read receipts separated from executable delivery state.
- Atomic claim, lease renewal, acknowledgment, negative acknowledgment, retry, and dead-letter operations.
- 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.
- Append-only delivery transition history.
- Idempotency conflict detection. Exact replay deduplicates; changed content under the same key fails.
- Scoped credentials that bind a remote workspace and agent principal.
- SQLite outbox and inbox cache for gateway clients.
- Leased instance-keyed presence with capabilities.
- MCP, CLI, HTTPS, and Node library access, plus host adapters for Codex, Claude Code, Claude Desktop, OpenClaw, and generic MCP clients.
- Canonical portable archives for messages and read receipts across local SQLite and shared PostgreSQL stores.
- Native SQLite and PostgreSQL backup, verification, and restore.
- Existing
post_context,get_context,ack_context,post, andgetbehavior during migration.
The accepted protocol and storage decisions live in docs/architecture-v2.md.
The canonical v2 operation registry generates JSON Schema, OpenAPI 3.1.2, and the MCP manifest. 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.
Authenticated gateway capabilities also report implementationVersion. Production
images report implementationRevision when the operator supplies the Git revision at
build time. These fields identify deployed code. They do not change protocol version
negotiation.
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.
Two gateway-only HTTP 2.1 operations establish a short-lived endpoint-migration challenge. Initial preflight uses a direct predecessor and successor. After a cutover journal exists, the same active successor credential can issue at the retained source URL and consume at the target URL. Both calls require active transaction-bound authority and use the canonical credential and operation rate buckets. PostgreSQL stores only a domain-separated SHA-256 commitment. A challenge expires within 60 seconds and does not authorize endpoint cutover. Authenticated HTTP 2.1 capabilities include the credential's grantedScopes.
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. Provider-neutral artifacts report this difference instead of claiming one authorization model for every backend.
flowchart TB
subgraph execution["Agent execution"]
harnesses["Agent harnesses<br/>product-specific or custom"]
callers["Non-harness callers<br/>scripts, CI jobs, and application services"]
end
subgraph hosts["Host surfaces"]
clis["Command-line hosts<br/>Codex CLI and Claude Code CLI"]
desktops["Desktop hosts<br/>Codex in ChatGPT and Claude Desktop"]
daemons["Daemons and custom MCP hosts"]
end
subgraph access["Integration and access"]
adapter["Host adapter or MCP registration"]
mcp["MCP over stdio: agent-bridge-mcp"]
cli["CLI: agent-bridge"]
library["Node library"]
direct["Direct HTTPS client"]
end
harnesses --> clis
harnesses --> desktops
harnesses --> daemons
clis --> adapter
desktops --> adapter
daemons --> adapter
adapter --> mcp
callers --> cli
callers --> library
callers --> direct
mcp --> core["Agent Bridge client core<br/>principal plus optional consumer instance key"]
cli --> core
library --> core
core --> local["Local mode<br/>SQLite authority"]
core --> edge["Gateway client mode<br/>SQLite edge outbox and cache"]
edge -->|"HTTPS protocol 2.1"| gateway["Agent Bridge gateway"]
direct -->|"HTTPS protocol 2.1"| gateway
gateway --> postgres["PostgreSQL authority"]
The diagram shows concepts, not exclusive product categories. A product may package a harness and a host together. A harness is the agent execution environment. A host is the CLI, desktop app, or daemon where it runs. A host adapter registers an access surface. Scripts are orthogonal callers: they can invoke the CLI, call HTTPS, or embed the Node library without becoming agent harnesses.
| Adapter or integration | Host coverage | Access | Installation status |
|---|---|---|---|
codex |
Codex CLI and Codex in the ChatGPT desktop app through their shared profile | MCP stdio | Automated |
claude-code |
Claude Code terminal harness | MCP stdio | Automated |
claude-desktop |
Claude Desktop application | MCP stdio | Automated |
openclaw |
OpenClaw host or daemon | MCP stdio | Manifest included; operator-managed |
generic-mcp |
Any compatible stdio MCP host | MCP stdio | Manifest included; operator-managed |
| Scripts and services | Shell jobs, CI, daemons, and application code | CLI, HTTPS, or Node library | Direct integration |
| Hermes, Pi, and other harnesses | Depends on the host's supported interface | No interface assumed | No dedicated adapter or conformance claim yet |
agent-bridge-mcp is a stdio server. The gateway exposes the Agent Bridge HTTPS API;
it is not MCP over HTTP. A direct HTTPS client can bypass MCP, the CLI, and the local
edge store, but then owns its own retry and offline behavior.
Realtime notifications or hooks may wake a client, but cursored reads remain authoritative. A missed notification does not lose a message.
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.
ADR-0001 defines this boundary and the distinct acknowledgment meanings. ADR-0003 defines the host layers and consumer instance-key contract.
Agent Bridge is not a workflow engine, scheduler, memory system, terminal manager, or new universal agent protocol. See Agent Bridge in the agent ecosystem for its relationship to MCP, A2A, agmsg, brokers, and agent runtimes.
- Node.js 22.23.1 or newer.
- SQLite 3.51.3 or newer for local and edge storage. The supported Node version includes it.
- Concurrent local and edge initialization may wait up to 15 seconds for schema work; normal database operations retain their configured busy timeout.
- PostgreSQL 15, 16, 17, or 18 for gateway mode. New PostgreSQL majors fail the migration prerequisite and live readiness checks until their catalog digest is certified.
Use the source install for development. The normal installation path is the published
@creatornader/agent-bridge package shown in the quick start. The unscoped npm name
belongs to another project, and npm rejects agentbridge as confusingly similar.
git clone https://github.com/creatornader/agent-bridge.git
cd agent-bridge
npm ci
npm run build
npm linkThe package exposes three executables:
agent-bridge: CLI.agent-bridge-mcp: stdio MCP server.agent-bridge-gateway: HTTP gateway.
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.
Initialize backend settings. init does not write an agent identity into the shared config.
agent-bridge init --provider localEach client supplies its own identity:
AGENT_BRIDGE_AGENT=codex agent-bridge send \
--target claude-code \
--type context \
"Bridge is ready"
AGENT_BRIDGE_AGENT=claude-code agent-bridge inbox --receipt-state unreadYou can also pass --source to a standalone send. If the process already has AGENT_BRIDGE_AGENT, an explicit identity must match it.
The post, get, and --unacked-by forms remain available only for compatibility
with older clients. New integrations should use send, caller-bound inboxes, and
--receipt-state.
Portable archives move one workspace's immutable messages and read receipts between
canonical local SQLite and PostgreSQL stores. Export writes a private file through a
same-directory temporary file, file fsync, atomic publication, and directory fsync.
The export audit completes only after durable publication. A failed export records a
bounded abandonment code. Export and import paths must satisfy the current user's
private-path policy. Export refuses to replace an existing file unless --force is
present. Export accepts --request-id, embeds it as exportRequestId in the archive
header, and returns it on success. Verification and import also return that export
provenance. An import request ID is a separate destination operation identifier.
Reusing an export request ID verifies a completed file or reconciles a matching file
whose audit response was lost. A missing or mismatched file stays started and requires
a new export request ID or owner reconciliation. Recovery artifacts use deterministic
same-directory names derived from the export request ID, so a retry can report and
clean only its own private temporary file or backup. Force replacement keeps that
backup until the new file is durable. Errors report whether publication, backup
cleanup, or audit state is known.
Portable v1 accepts only records in the current Agent Bridge domain. New API writes use lowercase UUIDs. Archives require lowercase UUIDs and UTC timestamps with six fractional digits. They do not trim strings, remove duplicates, apply defaults, or rewrite message content. Export rejects legacy or direct database rows outside these rules. Recover those rows through native database backup and restore tools.
install -d -m 700 "$HOME/.agent-bridge/archives"
agent-bridge archive export \
--provider local \
--workspace team \
--request-id 00000000-0000-4000-8000-000000000001 \
--db "$HOME/.agent-bridge/bridge.sqlite3" \
--output "$HOME/.agent-bridge/archives/team.ndjson"
agent-bridge archive verify --file "$HOME/.agent-bridge/archives/team.ndjson"
# Import is a rollback-backed dry run unless --apply is explicit.
agent-bridge archive import \
--provider local \
--db /path/to/target.sqlite3 \
--file "$HOME/.agent-bridge/archives/team.ndjson" \
--workspace team \
--dry-run
agent-bridge archive import \
--provider local \
--db /path/to/target.sqlite3 \
--file "$HOME/.agent-bridge/archives/team.ndjson" \
--workspace team \
--request-id 00000000-0000-4000-8000-000000000001 \
--applyLocal commands use --db, AGENT_BRIDGE_DB, the selected shared config, or the
canonical local database path, in that order. PostgreSQL commands accept database
authority only from AGENT_BRIDGE_ARCHIVE_DATABASE_URL and require a login registered
for the archive boundary created by the current migrations. They do not fall back to
the schema-owner, gateway runtime, owner-control, or client database settings.
The archive digest detects accidental changes to the canonical bytes. It is neither
encryption nor authentication. Archives contain message content and routing metadata,
so protect them as private data. They exclude deliveries, delivery events, presence,
credentials, owner-control records, security events, and other control or security
state. The library API is available from @creatornader/agent-bridge/archive.
Import opens the archive once without following links. It verifies the file, replays
messages, then replays receipts through the same descriptor. Every pass checks framing,
order, counts, and the client-computed digest. Batches are limited by row count and a
4 MiB byte budget. --dry-run and --apply are mutually exclusive.
Native DR preserves one complete local authority or shared PostgreSQL deployment. Use
it for recovery, not for moving a workspace between providers. The framed .abdr
bundle records its provider, schema contract, and entry hashes. Commands calculate and
return a separate whole-file SHA-256 digest. The hashes detect changed bytes. The bundle
is not encrypted or authenticated, and it may contain credentials and message content.
Store and transfer it as private database material.
Local backup uses SQLite's online backup API, so the source may remain open. It accepts only the local authority database. Gateway edge caches and outboxes are rejected because PostgreSQL remains their authority. Restore requires a new target path and verifies the exact local schema contract before publication.
agent-bridge dr backup \
--provider local \
--source "$HOME/.agent-bridge/bridge.sqlite3" \
--output "$HOME/.agent-bridge/archives/local.abdr" \
--backup-id 00000000-0000-4000-8000-000000000010
agent-bridge dr verify \
--provider local \
--bundle "$HOME/.agent-bridge/archives/local.abdr"
agent-bridge dr restore \
--provider local \
--bundle "$HOME/.agent-bridge/archives/local.abdr" \
--target /path/to/new-bridge.sqlite3 \
--request-id 00000000-0000-4000-8000-000000000011PostgreSQL backup takes a repeatable-read snapshot while holding the native DR fence.
It stores the agent_bridge schema dump plus a canonical inventory of required roles,
memberships, and default privileges. The backup excludes transient data from agent
instances, rate-limit buckets, request authority, endpoint-migration challenge rows, and
archive transaction authority.
Restore turns claimed deliveries into immediately retryable work because a database
restore cannot preserve a live lease.
PostgreSQL authority comes only from the process environment. Backup reads
AGENT_BRIDGE_DR_SOURCE_DATABASE_URL; restore reads
AGENT_BRIDGE_DR_TARGET_DATABASE_URL. URLs are not accepted as command arguments.
pg_dump and pg_restore must match the database major exactly. Native DR supports
PostgreSQL 15 through 18. If matching tools are not on PATH, pass their directory to
PostgreSQL backup, verify, or restore with --tool-directory. Local verification does
not use PostgreSQL tools and rejects that option.
AGENT_BRIDGE_DR_SOURCE_DATABASE_URL='postgresql://backup-admin@db/agent_bridge' \
agent-bridge dr backup \
--provider postgres \
--output "$HOME/.agent-bridge/archives/postgres.abdr" \
--backup-id 00000000-0000-4000-8000-000000000020
agent-bridge dr verify \
--provider postgres \
--bundle "$HOME/.agent-bridge/archives/postgres.abdr"
AGENT_BRIDGE_DR_TARGET_DATABASE_URL='postgresql://restore-admin@new-db/agent_bridge' \
agent-bridge dr restore \
--provider postgres \
--bundle "$HOME/.agent-bridge/archives/postgres.abdr" \
--request-id 00000000-0000-4000-8000-000000000021 \
--accept-source-sql-riskPostgreSQL restore executes SQL from the dump, so use --accept-source-sql-risk only
for a bundle from a source you trust. The target must be a dedicated fresh database
with the same database name and PostgreSQL major as the backup. Restore requires a
superuser because it recreates object owners, role shells, memberships, and default
privileges. It never enables login on restored external principals. Runtime principals
lose their runtime membership. Active control and archive registrations receive a new
revocation event, and their principals lose those memberships. Re-enable a principal
and register it again only after inspecting the restored target. A restore may reuse a
preexisting role only when it is the source schema owner, the target session user, and
a true superuser. Every other role collision fails. If a restore fails after mutation
begins, Agent Bridge disables new target connections and reports any residual roles or
recovery paths. Inspect and clean those artifacts before retrying. Never run the source
and restored target as active authorities at the same time.
Backup publication never replaces an existing output. Deterministic adjacent staging
paths use the backup or request UUID. A retry with the same UUID reports retained
recovery paths instead of overwriting them. File and directory synchronization is
strongest on platforms that support directory fsync; Windows cannot provide the same
directory durability proof. Successful Windows commands report
directoryDurability: "unavailable" or
cleanupDirectoryDurability: "unavailable" instead of claiming that proof. The
library API is available from
@creatornader/agent-bridge/dr.
Agent identity and gateway credentials belong to each installed client, not the shared
~/.agent-bridge/config file.
agent-bridge clients install codex --identity codex
agent-bridge clients install claude-code --identity claude-code
agent-bridge clients install claude-desktop --identity claude-desktopFor gateway mode, use the owner enrollment workflow below for new credentials. The older token handoff remains available for credentials issued by external tooling:
AGENT_BRIDGE_CLIENT_TOKEN=<codex token> \
agent-bridge clients install codex --identity codex
AGENT_BRIDGE_CLIENT_TOKEN=<claude-code token> \
agent-bridge clients install claude-code --identity claude-code
AGENT_BRIDGE_CLIENT_TOKEN=<claude-desktop token> \
agent-bridge clients install claude-desktop --identity claude-desktop--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.
The codex adapter uses the Codex profile shared by the Codex CLI and the Codex
surface in the ChatGPT desktop app; it does not install two independent registrations.
Claude Code installation uses its native MCP command. Claude Desktop installation
merges the agent-bridge server into its JSON config with an atomic write. Restart the
client after installation.
Inspect an existing registration before making Agent Bridge its lifecycle owner:
agent-bridge clients inspect codex --identity codex --instance <stable-key> \
--backend-config <absolute-client-backend-path>
agent-bridge clients adopt codex --identity codex --instance <stable-key> \
--backend-config <absolute-client-backend-path>
agent-bridge clients adopt codex --identity codex --instance <stable-key> \
--backend-config <absolute-client-backend-path> --applyinspect is local, read-only, and does not return backend values or credentials. It
classifies the requested registration as absent, unmanaged, managed, or
drifted. adopt is plan-only unless --apply is explicit, and application writes
only owner-private, credential-free management metadata after the identity, instance,
backend path, scope, launch contract, and registration locator match exactly. Adoption
never rewrites the host registration or backend file. Enrollment-based first-time
provisioning still refuses registration and backend-file collisions.
Claude Code inspection accepts the installed --scope; Claude Desktop accepts an
explicit validated --command and, when needed, --config-path; its default contract
is absolute Node plus the absolute server entry. Other adapters reject these
runtime-specific options. Managed metadata binds Desktop to that normalized config
path and Codex to its active profile config. Claude Code local and project adoption
bind to the current directory because the native CLI exposes no stronger locator, so
run those operations from the project context that later lifecycle commands should
use. Backend files and their immediate parents must satisfy the owner-only no-link
policy. Registration state is independent of connectivity health, and applied
adoption re-inspects before reporting success.
Once adopted, repair, update, and uninstall locate the registration from owner-private managed
metadata. The runtime and stable instance select that record. --identity must match
the recorded identity and the immutable request. These commands reject backend-path, scope, and host
config locator flags, so a caller cannot redirect a managed mutation.
agent-bridge clients repair codex --identity codex --instance <stable-key>
agent-bridge clients repair codex --identity codex --instance <stable-key> --apply
agent-bridge clients update codex --identity codex --instance <stable-key> \
--command agent-bridge-mcp --apply
agent-bridge clients repair codex --identity codex --instance <stable-key> \
--apply --resume <operation-uuid>
agent-bridge clients uninstall codex --identity codex --instance <stable-key> --apply
agent-bridge clients rollback <update-operation-uuid> --identity codex
agent-bridge clients rollback <update-operation-uuid> --identity codex --apply
agent-bridge clients resume <operation-uuid>
agent-bridge clients migrate stage codex --identity codex --instance <stable-key> \\
--enrollment-file <private-path>
agent-bridge clients migrate cutover <stage-operation-uuid> --exclusive-edge
agent-bridge clients migrate cutover <stage-operation-uuid> --exclusive-edge --apply
agent-bridge clients migrate finalize <cutover-operation-uuid> --exclusive-edge --applyThese commands return a plan by default. A managed registration that is already exact
and has a private backend file produces no operation. Repair restores the recorded
launch contract. Update validates and records a new credential-free launch contract
before it creates a journal. A native --command is one executable contract. Bare
commands cannot contain arguments or URL-like selectors. Absolute paths must resolve
to executable files. Native adapters remove, verify absence, add, and verify
the target registration. Claude Desktop replaces only mcpServers.agent-bridge,
preserves unrelated JSON values in memory, publishes through a private operation-scoped
temporary file, and verifies the new entry. Node cannot provide an OS transaction with
an uncooperative same-user Desktop writer. The pre-rename identity checks make this an
advisory race boundary rather than a guarantee.
Uninstall is forward-only. It removes the managed registration and verifies its
absence, deletes the private backend file, then deletes the management metadata. It
does not repair an unsafe backend before deletion. If the backend is not already
private, uninstall stops without changing the registration. It does not restore a
registration, backend, or metadata file after a later step fails. Desktop removes only
mcpServers.agent-bridge and preserves other JSON values. Backend and metadata removal
check the file and private parent identities before unlink. POSIX then syncs the parent
directory. Windows verifies the result but records unavailable directory durability.
Node cannot make pathname-based deletion atomic against an uncooperative same-user
writer, so that boundary remains advisory.
Rollback is explicit and update-only. A committed v4 update retains a bounded
credential-free inverse contract: its prior managed metadata, prior exact
registration contract, and digests for the forward metadata and registration state.
clients rollback uses only the source operation UUID and matching identity. It first
returns a plan. With --apply, it creates a separate reverse journal after it proves
the source is same-host, the source is a committed v4 update, and the current state
still matches the recorded forward state. Native reversal removes the forward entry,
adds the prior entry, then writes prior metadata. Claude Desktop replaces only its
Agent Bridge entry and writes prior metadata last. A reverse journal can resume with
clients resume; a second rollback fails because the forward state is no longer
present. Repair remains monotonic. Uninstall recovery requires re-enrollment.
--resume must name an unfinished operation for the same action, runtime, instance,
and identity. It uses the recorded request instead of new flags. --recover-lock
requires --apply and only removes an old same-host lock after the process-death
proof succeeds. Never delete a lock manually.
clients resume <operation-uuid> takes only recorded v3, supported v4, v5
migration-stage, or v6 endpoint-migration operation authority and an optional
--recover-lock. It does not accept a replacement action, runtime,
instance, identity, command, backend path, scope, or config locator. Use it for every
generic resume. It is also required when an uninstall has already deleted its final
metadata file, because the action-specific command has no metadata record left to
assert.
Repair and uninstall create v3 journals. New update and reverse rollback journals use v4. Existing v2 journals remain inspectable. A non-terminal v2 journal lacks the required identity-bound request, so it is blocked rather than resumed by these commands.
Managed-client operations now have a crash-safe local substrate under the owner-only
~/.agent-bridge/operations/ directory. agent-bridge clients operations lists safe
operation summaries, and appending an operation UUID inspects one manifest. Output
never includes artifact contents or backend values. Update requests retain only a
normalized launch command, arguments, scope, and the fixed Agent Bridge environment
key names. Each immutable ordered plan names
registration, backend, and management-metadata targets with non-sensitive locators,
no-replace before and after artifacts, and expected digests. Registration artifacts
contain only bounded Agent Bridge fields and the full nonsecret target contract. An
unknown environment key, malformed field, unsafe argument, or wrong scope stops before
the journal is created. Begin holds the
runtime-plus-instance lock and refuses another unfinished operation. Any corrupt or
blocked journal fences new mutations and resume until an operator resolves it. Resume is
same-host only. Intent is durable before an
external write; observed-applied is durable only after the after-state is verified.
On restart, the exact pending step is retryable only at its before-state, advances only
at its after-state, and otherwise blocks as ambiguous. Sensitive file access checks and
pins its immediate directory. Stale-lock recovery separately pins the operation root
and locks directory for the full recovery sequence. Creation and destructive cleanup
also keep the operation root and target directories pinned through their writes. The
full metadata record is rechecked after the lock and before every non-metadata step.
On Windows, a held mutation lock can reuse a native ACL result only for the same directory path identity. File checks, every new or resumed lock, and each passive inspection check the ACL again. POSIX mode checks remain per-access. This cache does not change local mode's trust in the current OS user.
The journal states are prepared, snapshotted, in-progress, applied, cleaning,
and committed; inspection separately reports resumable,
classification-required, blocked, or complete. committed means every write was
verified and every artifact was removed. Cleanup records durable intent per artifact,
pins and verifies it, unlinks it, and syncs the directory on POSIX; Windows explicitly
records unavailable directory durability. A missing artifact is safe to resume only
when cleanup intent predated the attempt. Inspection accepts only two exact crash
residues: that authorized absence, or the verified after artifact for the current
intent-recorded step. A terminal manifest removes request, step, digest, locator, and
artifact metadata. A v4 update retains only its explicit credential-free inverse
contract. A committed v5 stage retains its credential-free stage contract. A committed
v6 phase retains its credential-free cutover contract. Completed records remain
readable across hosts. Repair, update, rollback, uninstall, and endpoint migration use
this journal. Agent Bridge cannot promise physical erasure
on SSDs or journaled filesystems, so future commands must minimize credential-bearing
snapshots and rotate credentials when retained copies contained them. Inspection
changes no registration, backend, metadata, or snapshot file.
The v1 host-adapter manifests are under clients/. Their compatibility
field is named runtime, but its value identifies the installation target, not a
unique process. OpenClaw and generic MCP manifests declare the required environment
variables and command but leave config mutation to the operator because their host
config shapes vary.
The lifecycle ownership boundary and deferred endpoint-migration constraints are recorded in ADR 0004.
clients migrate stage <runtime> --identity <name> --instance <key> --enrollment-file <path> --apply prepares a private gateway successor from a rotation
enrollment. It does not change the active registration or backend. Staging records
endpoint digests, credential IDs, the source edge path and scope key, and the
predecessor grace cutoff without recording tokens or raw gateway URLs.
clients migrate cutover <stage-operation-uuid> --exclusive-edge --apply consumes a
committed v5 stage. Before it creates a journal, it verifies both live gateways under
HTTP 2.1, including the recorded workspace, principal, credential ID, immutable gateway
authority ID, and the successor credential's status:read and messages:write grants.
The endpoints then exchange a direct predecessor-to-successor route challenge. The
durable journal retains the normalized source gateway URL without a token. Every later
route challenge uses the successor credential at both URLs, and it drains the source
edge through the target gateway. The source edge changes to draining, receives an
exclusive lease, and must reach an exact zero-work outbox before any host registration
changes. The command removes and adds native registrations, or replaces the Claude
Desktop entry, then changes managed metadata last. It rechecks the authority and lease
before every host write.
Every cutover phase requires --exclusive-edge. Agent Bridge can enumerate managed
registrations that share an edge file, including hardlink aliases, but it cannot see
unmanaged publishers. The flag records the operator's assertion for that remaining
cohort. The forward phase also requires an active source edge and an active, empty
target edge.
clients migrate finalize <cutover-operation-uuid> --exclusive-edge --apply works
only after the predecessor grace cutoff and retires the retained source edge. To return
to an earlier endpoint, first rotate a new owner credential for that endpoint, then run
an ordinary forward cutover with it as the successor. Use clients resume <operation-uuid> after a crash. Resume waits for a live drain lease to expire before
another worker can take it. Dry inspection refuses an edge with live WAL sidecars rather
than opening it in a mode that could miss uncheckpointed outbox state.
Moving to an independent database needs a separate owner-mediated process.
The source backend must name an absolute, normalized edge database file. Relative and
in-memory edge paths cannot identify one durable outbox and are rejected.
SKILL.md provides concise runtime-neutral operating instructions for agents. llms.txt gives tools and model crawlers a compact map of the package, modes, commands, and identity rules.
Apply migrations to a PostgreSQL database:
PGUSER=schema_owner PGPASSWORD='<password>' \
AGENT_BRIDGE_DATABASE_URL=postgresql://host/database \
agent-bridge migrateThe migration sequence creates or refreshes a database-specific restricted role. Get its name, then create a login that inherits it. Keep the schema-owner URL out of the gateway process:
create role agent_bridge_gateway login password '<generated password>';
do $grant$
declare
runtime_role text := 'agent_bridge_runtime_' || substr(md5(current_database()), 1, 16);
begin
execute format('grant %I to agent_bridge_gateway', runtime_role);
end
$grant$;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.
Migration 014 provides the database owner control plane. The schema-owner connection
registers provisioning and inventory logins with register_control_member. It can
remove them with revoke_control_member. Do not grant the database-specific operator,
auditor, or owner roles directly. Operators call the narrow
control_provision, control_rotate_credential, and control_revoke_credential
functions with a fresh request UUID. Operators and auditors can call
control_credential_inventory; it never exposes credential digest material. Inventory
accepts an optional workspace, an optional (created_at, credential_id) cursor, and
a row limit. The default is 100 rows and the maximum is 1,000.
Each mutation takes a transaction lock for its request UUID before checking the
request ledger. An identical concurrent call returns the stored result. Reusing the
UUID with changed content fails. Provisioning reuses an existing workspace only when
its name matches, so one workspace can contain several principals. Rotation supports
credentials without an expiry and a null grace cutoff for immediate replacement. A
rotation request includes the expected workspace and principal. The function checks
both while it locks the predecessor, then returns the stored canonical identity.
The database derives the audit actor from session_user.
This temporary operator workflow creates an eligible login and registers it through
the protected SQL boundary. The owner CLI then provisions a token-bound codex
principal. It generates the raw token locally, writes it to a private enrollment file
before contacting PostgreSQL, and sends only the SHA-256 hash to the database.
OPERATOR_LOGIN="agent_bridge_operator_$(date +%s)"
OPERATOR_PASSWORD="$(node -e "process.stdout.write(require('node:crypto').randomBytes(32).toString('hex'))")"
MEMBERSHIP_REQUEST="$(node -e "process.stdout.write(require('node:crypto').randomUUID())")"
PROVISION_REQUEST="$(node -e "process.stdout.write(require('node:crypto').randomUUID())")"
export AGENT_BRIDGE_OPERATOR_LOGIN="$OPERATOR_LOGIN"
export AGENT_BRIDGE_OPERATOR_PASSWORD="$OPERATOR_PASSWORD"
export AGENT_BRIDGE_MEMBERSHIP_REQUEST="$MEMBERSHIP_REQUEST"
export AGENT_BRIDGE_PROVISION_REQUEST="$PROVISION_REQUEST"
psql -v ON_ERROR_STOP=1 "$AGENT_BRIDGE_DATABASE_URL" <<'SQL'
\getenv operator_login AGENT_BRIDGE_OPERATOR_LOGIN
\getenv operator_password AGENT_BRIDGE_OPERATOR_PASSWORD
\getenv membership_request AGENT_BRIDGE_MEMBERSHIP_REQUEST
SELECT format(
'CREATE ROLE %I LOGIN PASSWORD %L',
:'operator_login',
:'operator_password'
) \gexec
SELECT * FROM agent_bridge.register_control_member(
:'membership_request'::uuid,
:'operator_login'::name,
'operator'
);
SQL
export AGENT_BRIDGE_OPERATOR_DATABASE_URL="postgresql://${OPERATOR_LOGIN}:${OPERATOR_PASSWORD}@host/database"
export AGENT_BRIDGE_URL="https://bridge.example.com"
ENROLLMENT_FILE="${AGENT_BRIDGE_ENROLLMENT_DIR:-$HOME/.agent-bridge/enrollments}/${PROVISION_REQUEST}.json"
agent-bridge owner provision \
--request-id "$PROVISION_REQUEST" \
--workspace team \
--workspace-name Team \
--identity codex \
--runtime codex \
--instance codex-machine \
--scope-set release-a-full
agent-bridge clients install codex --enrollment-file "$ENROLLMENT_FILE"
REVOKE_REQUEST="$(node -e "process.stdout.write(require('node:crypto').randomUUID())")"
export AGENT_BRIDGE_REVOKE_REQUEST="$REVOKE_REQUEST"
psql -v ON_ERROR_STOP=1 "$AGENT_BRIDGE_DATABASE_URL" <<'SQL'
\getenv operator_login AGENT_BRIDGE_OPERATOR_LOGIN
\getenv revoke_request AGENT_BRIDGE_REVOKE_REQUEST
SELECT * FROM agent_bridge.revoke_control_member(
:'revoke_request'::uuid,
:'operator_login'::name,
'operator'
);
SELECT format('DROP ROLE %I', :'operator_login') \gexec
SQL
unset OPERATOR_PASSWORD AGENT_BRIDGE_OPERATOR_PASSWORD
unset AGENT_BRIDGE_OPERATOR_LOGIN AGENT_BRIDGE_MEMBERSHIP_REQUEST
unset AGENT_BRIDGE_PROVISION_REQUEST AGENT_BRIDGE_REVOKE_REQUEST
unset AGENT_BRIDGE_OPERATOR_DATABASE_URLAGENT_BRIDGE_OPERATOR_DATABASE_URL is the owner CLI's only database authority. It
does not fall back to the schema migration URL, runtime URL, shared client config, or
active client identity. owner inventory returns at most 100 rows by default and
1,000 when requested. Its opaque cursor is bound to the selected workspace.
Provision and rotation enrollment files stay under
$AGENT_BRIDGE_ENROLLMENT_DIR or ~/.agent-bridge/enrollments. The CLI requires an
owner-only root and private path components. It refuses symlinks, path escapes, and
directory replacement during an operation. Every file has a monotonic revision. A
per-file exclusive lock covers the read, state transition, database call, and client
installation side effects. Each transition compares the revision, state, request,
operation, and token with the current file before atomic replacement.
If provisioning fails, rerun the same operation with --resume "$ENROLLMENT_FILE".
A process crash can leave the adjacent lock file behind. Do not delete it manually.
After at least 60 seconds, use --recover-lock with --resume or the client install
command. Recovery succeeds only when the lock belongs to this host and user and its
recorded process no longer exists. The stored request UUID, inputs, instance, and raw
token are reused exactly.
clients install <runtime> --enrollment-file <path> derives the gateway URL,
workspace, principal, instance, and token from the file. It rejects conflicting flags
or environment values. Provisioning registers the host MCP server. Rotation updates
only that instance's existing private backend file and never repeats host registration.
The backend must contain the predecessor credential ID, principal, and instance metadata
written by an enrollment-based provision. Rotation fails closed for older backend files
without this metadata. It also verifies the exact live MCP registration before replacing
the token.
The installer removes the enrollment file only after installation succeeds. A failed
delete returns enrollmentStatus: "consumed-file-retained" and the consumed file path.
An already missing file returns consumed-file-missing. If unlink succeeds but the
directory identity check or fsync fails, the result is
consumed-deletion-durability-unknown; it never claims that the path was retained.
lockReleaseStatus separately reports released, retained, or durability-unknown.
A lock-release failure after successful installation does not replace the installation
result with a generic error. The consumed artifact no longer contains the raw token. A retry validates the
live backend and MCP registration before deleting a retained consumed file.
--token and AGENT_BRIDGE_CLIENT_TOKEN remain available for the older manual
installation flow, but they cannot be combined with --enrollment-file. The final SQL
block revokes and drops the temporary operator after the client installation has
stored the credential.
Rotate the credential for one installed instance with an explicit grace cutoff or
--invalidate-immediately, then consume the resulting enrollment file:
ROTATE_REQUEST="$(node -e "process.stdout.write(require('node:crypto').randomUUID())")"
ROTATION_FILE="${AGENT_BRIDGE_ENROLLMENT_DIR:-$HOME/.agent-bridge/enrollments}/${ROTATE_REQUEST}.json"
agent-bridge owner rotate \
--request-id "$ROTATE_REQUEST" \
--credential-id "<current credential UUID>" \
--workspace team \
--identity codex \
--runtime codex \
--instance codex-machine \
--scope-set release-a-full \
--invalidate-immediately
agent-bridge clients install codex --enrollment-file "$ROTATION_FILE"Control membership is an offline SQL administration boundary. The gateway and MCP
server do not expose it. Runtime readiness compares PostgreSQL membership with the
append-only registry. It fails for an unregistered operator or auditor, an external
owner holder, a missing registered grant, any extra role inherited by a registered
member, or any role that inherits a registered member. Protected operations recheck
the live registry and direct membership after taking a member-global transaction lock,
followed by capability locks in operator-before-auditor order. Registration and
revocation use the same order. A stale session that remains in a revoked role cannot
continue operating. The schema owner is the only bootstrap holder of all three control
roles and the only role with ordinary execution authority on the registration
functions. PostgreSQL 16 and newer may represent its effective admin, inherit, and set
authority with a bootstrap administrator row plus a schema-owner row. A native DR
restore may recreate the same authority as one combined bootstrap-superuser grant.
Readiness validates either physical form and its effective authority. True superusers
and managed-provider schema owners remain inside this trusted database-administration
boundary. A separate NOLOGIN backup-reader role grants only table and sequence read
access to the schema owner. Runtime, control, archive, and external principals cannot
inherit it. Native backups require the schema owner to be a true superuser or hold
BYPASSRLS. A target restore superuser can validate the intentionally suspended
schema-owner shell. CREATEROLE alone remains sufficient for migrations. Readiness checks the
running PostgreSQL major on every call, so upgrading an already-migrated database to an
uncertified major disables the runtime.
Direct credential inserts default to empty scopes rather than full access. Issued identity, scope, label, expiry, and lineage fields cannot be edited directly. Existing migration-013 rows retain their values and remain revocable during upgrade. Runtime readiness compares the live owner-control catalog with the protected migration-014 attestation. Migration 014 refuses to create that baseline if critical workspace, agent, credential, index, trigger, function, ownership, or row-isolation dependencies have already drifted. Unrelated additive schema objects do not change readiness. The migration also blocks unsafe direct and default privileges on protected objects. Global and workspace inventory use fixed-origin expression indexes for their keyset ordering.
Start the gateway:
PGUSER=agent_bridge_gateway PGPASSWORD='<password>' \
AGENT_BRIDGE_RUNTIME_DATABASE_URL=postgresql://host/database \
AGENT_BRIDGE_HOST=127.0.0.1 \
AGENT_BRIDGE_PORT=8787 \
agent-bridge-gatewayPut the gateway behind TLS for any non-loopback deployment.
Configure a client backend without storing its identity:
AGENT_BRIDGE_PROVIDER=gateway
AGENT_BRIDGE_URL=https://bridge.example.com
AGENT_BRIDGE_WORKSPACE=team
These shared settings are enough before clients install. The secure flow reads
the principal-bound token and exact client settings from an enrollment file. The
compatibility flow accepts AGENT_BRIDGE_CLIENT_TOKEN. Both write a private client
backend and register AGENT_BRIDGE_AGENT, AGENT_BRIDGE_INSTANCE, and that file's
path with the host. Gateway initialization checks readiness, token validity, and that
the token-bound principal matches that process identity.
Gateway clients use ~/.agent-bridge/edge.sqlite3 by default.
When a send cannot reach the gateway:
- The validated message is written to the SQLite outbox with a stable idempotency key.
- The CLI or MCP result reports
disposition: "queued"andauthoritative: false. - Long-lived MCP gateway clients retry due messages automatically with bounded exponential backoff.
agent-bridge syncand the MCPsynctool trigger the same bounded replay and cache refresh manually. A permanently blocked message remains visible in diagnostics but does not stop later messages. - An ambiguous retry is safe because the gateway enforces idempotency and rejects changed content under the same key.
Pass --queue-only to agent-bridge send when you want to write to the gateway
outbox without making a network request. The message stays under the configured
gateway, workspace, and principal scope. Run agent-bridge sync to publish it later.
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.
agent-bridge --version
agent-bridge --help
agent-bridge init --provider local
agent-bridge doctor
agent-bridge status
agent-bridge pending
agent-bridge send --type request --target worker "Run the task"
agent-bridge send --queue-only --type request --target worker "Queue the task"
agent-bridge post --category goal-update --project agent-bridge "Gateway is ready"
agent-bridge inbox --limit 20
agent-bridge sent --limit 20
agent-bridge history --mailbox all --receipt-state any
agent-bridge get --since 24h --unacked-by codex
agent-bridge history --thread-id release-1
agent-bridge acknowledge --ids <message-uuid>
agent-bridge claim --lease-ms 30000
agent-bridge claim --message-id <message-uuid> --lease-ms 30000
agent-bridge send --target worker --delivery-policy '{"mode":"leased","maxAttempts":3,"retryBaseDelayMs":1000,"retryMaxDelayMs":60000,"retryJitterRatio":0.2}' "work"
agent-bridge deliveries --state dead
agent-bridge delivery-events --delivery-id <uuid>
agent-bridge cancel --delivery-id <uuid>
agent-bridge requeue --delivery-id <uuid>
agent-bridge extend --delivery-id <uuid> --lease-token <uuid>
agent-bridge ack --delivery-id <uuid> --lease-token <uuid>
agent-bridge nack --delivery-id <uuid> --lease-token <uuid> --error "retry later"
agent-bridge join --instance desktop-1 --runtime codex --capability mcp
agent-bridge presence
agent-bridge sync
agent-bridge watch-h and --help print help before Agent Bridge loads backend configuration or
dispatches a command. They are safe to use after a command name, including archive
and dr commands.
--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.
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.
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.
Claims normally select the next due delivery by priority. HTTP 2.1, MCP, and CLI
callers may supply messageId or --message-id to atomically select only that due,
recipient-visible delivery. The filter also scopes lease-expiry and exhaustion
maintenance performed by that claim. Released HTTP 2.0 request shapes remain frozen
and reject the field.
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.
watch runs until interrupted unless --polls sets an explicit bound. Empty polls use capped backoff and jitter. Gateway network failures retry when the error is transient. Authentication and validation errors stop the watcher.
Each exact project filter gets its own local watch checkpoint. Switching between a filtered and unfiltered watch cannot advance the other checkpoint.
Unknown flags and missing option values fail before a message can be sent. This prevents a misspelled target flag from becoming a broadcast.
Both providers expose the v1 compatibility tools:
post_contextget_contextack_context
They also expose:
sendhistoryclaimextendacknowledgenegative_acknowledgelist_deliverieslist_delivery_eventscancel_deliveryrequeue_deliveryheartbeatpresence
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.
~/.agent-bridge/configcontains backend settings only. A storedAGENT_BRIDGE_AGENTvalue is ignored.- Workspace is the tenant boundary. Principal is the durable agent identity and message source.
AGENT_BRIDGE_INSTANCEis an optional caller-supplied stable consumer key. Supported installers generate and persist one for each installed client. Direct clients should do the same when they need separate consumer state or presence.- Unless
AGENT_BRIDGE_CURSORsets an explicit path, a present instance key selects the cursor path. The key also selects the delivery lease owner and presence row. Processes that share a key also share those resources. The gateway does not bind the key to an installer registration. It is not a PID, unique live process, or conversation ID. - When the key and explicit cursor path are both omitted, cursor storage uses
default. Delivery leases fall back to the principal, and presence operations reject the request because presence requires an instance key. - Presence is instance-keyed in the released protocol. Per-process presence requires an additive identity instead of a silent change to lease ownership or cursor paths.
- Session and thread identifiers are message and application context. They do not select credentials, workspace, principal, or delivery ownership.
- Local clients bind identity through their process environment or an explicit CLI send argument.
- Gateway tokens bind workspace and principal. The API ignores caller-supplied source and workspace fields.
- Gateway clients use separate owner-only backend files and principal-bound tokens.
- The gateway uses
AGENT_BRIDGE_RUNTIME_DATABASE_URL. Migration and provisioning commands use the separate schema-ownerAGENT_BRIDGE_DATABASE_URL. - Remote HTTP must use TLS. Plain HTTP is accepted only on loopback.
- Local config, database, WAL, and shared-memory files use owner-only permissions where the platform supports POSIX modes.
- The private
agent_bridgeschema is denied to SupabaseanonandauthenticatedData API roles. - Expired instance-presence rows are pruned during heartbeat and listing. Each agent is capped at 128 active instance keys, and each workspace is capped at 4,096.
- Errors expose stable codes without database URLs, tokens, or provider response bodies.
- Delivery work is at least once, not exactly once. Consumers must make external side effects idempotent.
Agent Bridge 0.6.0 removed the direct legacy-supabase runtime provider. Old provider
names and key-only configurations now fail with a migration message instead of opening
PostgREST. Configure local or gateway mode before upgrading a running client.
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.
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.
The original v1 schema remains in sql/setup.sql as a migration
fixture. Ordered gateway migrations live in sql/migrations/.
The retirement decision is recorded in
ADR-0005.
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:
- Provider and actual schema version.
- Bound workspace, agent, and consumer instance key.
- Endpoint or database path.
- Cursor and queue state.
- Pending, claimed, retrying, and dead delivery counts, plus due versus scheduled work, expired leases, oldest-due time, and bounded queue lag.
- Gateway reachability, outbox due/scheduled/leased/blocked depth, cache size, blocked age/attempt/error, next retry, last outbound/inbound sync, and last attempt.
- Explicit check results. Blocked outbox rows, expired leases, dead deliveries, or a known-unreachable gateway prevent a healthy result.
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.
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.
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.
The gateway exposes two unauthenticated probes. /healthz reports HTTP process
liveness without querying PostgreSQL. /readyz checks database and schema readiness.
/v2/status and /metrics require a valid credential.
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.
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.
Migration 017 adds one immutable PostgreSQL authority UUID. The released request-authority opener keeps its return shape. Production gateway requests use a bound opener that returns the authority UUID with the database-derived credential. Authenticated gateway /v2/status replies include gatewayAuthorityId and credentialId when transaction-bound request authority is active. Authenticated HTTP 2.1 capabilities return grantedScopes for that credential. Migration 018 uses the same bound authority to issue and consume an expiring challenge for a direct credential successor. Migration 019 also permits the same active successor credential at both endpoints after a cutover journal begins. Neither challenge proves a database migration or authorizes endpoint cutover.
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.
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.
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.
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.
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:
drop role if exists agent_bridge_gateway;
do $cleanup$
declare
suffix text := substr(md5(current_database()), 1, 16);
role_name text;
begin
foreach role_name in array array[
'agent_bridge_runtime_' || suffix,
'agent_bridge_data_owner_' || suffix,
'agent_bridge_context_reader_' || suffix,
'agent_bridge_event_writer_' || suffix
] loop
if exists (select 1 from pg_roles where rolname=role_name) then
execute format('reassign owned by %I to %I', role_name, current_user);
execute format('drop owned by %I', role_name);
execute format('drop role %I', role_name);
end if;
end loop;
end
$cleanup$;The runtime role owns no schema objects. PostgreSQL keeps roles after a database is dropped, so skipping this step leaves an unused cluster role behind.
npm run typecheck
npm test
npm run test:postgres:preflight
npm run build
npm packThe PostgreSQL preflight starts a fresh Docker container for each supported major,
assigns a free loopback port, and runs the live contract. Normal completion removes
each container. A later run removes labeled preflight containers older than two hours,
which bounds residue after a process or machine stops before cleanup can run. Use
npm run test:postgres:preflight -- --major 16 for one major, or
npm run test:postgres:preflight:full for the full suite on every major. The command
requires a running Docker engine and never reuses a database from an earlier run.
Set AGENT_BRIDGE_TEST_DATABASE_URL when an existing disposable PostgreSQL database is
more convenient. CI runs one PostgreSQL 16 contract before starting the 15 through 18
jobs. Each version then runs the live contract and matching native DR proof. The package
job builds and installs the npm tarball separately.
CI runs Node 22 and 24 on Linux, macOS, and Windows. One native Windows contract checks private paths, managed uninstall path identity, and recovery before the two Windows jobs start. Before applying a Windows DACL, private paths must be owned by the current account SID or the active token's default owner SID. The final owner and sole protected FullControl rule must use the account SID. Verification-only paths must already satisfy that final policy. Node file identity checks and native Windows reparse attributes must also agree before and after policy validation; symlinks, junctions, and other reparse objects fail closed. CI applies and verifies this policy on native Windows runners. Windows ACL race and directory durability behavior has not yet been proved on a dedicated Windows host.
- ROADMAP.md: released work, unreleased implementation, and the remaining program.
- SECURITY.md: supported versions, release integrity, private vulnerability reporting, and security boundaries.
- docs/ecosystem.md: product boundary, adjacent systems, and interoperability direction.
- docs/troubleshooting.md: MCP startup, identity, backend, and client recovery.
- docs/deployment.md: Compose development setup, production constraints, upgrades, backup, and rollback.
- ADR-0001: protocol layers and acknowledgment semantics.
- ADR-0002: canonical contracts, generated artifacts, discovery, and version negotiation.
- ADR-0003: host layers and consumer instance-key semantics.
- ADR-0004: managed client ownership and endpoint migration.
- ADR-0005: direct Supabase runtime retirement and historical migration support.
- docs/architecture-v2.md: protocol and storage decisions.
- SKILL.md: runtime-neutral agent operating instructions.
- llms.txt: compact machine-readable project map.
- docs/postmortems/2026-07-08-wrapper-source-drift.md: wrapper and source-identity incident.
- CLAUDE.md: repository rules, architecture constraints, and documentation ownership.
- CHANGELOG.md: released and pending changes.
Apache-2.0