@creatornader/agent-bridge 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +222 -1
- package/README.md +312 -12
- package/ROADMAP.md +88 -7
- package/SECURITY.md +78 -5
- package/SKILL.md +108 -1
- package/dist/archive.d.ts +2 -2
- package/dist/archive.js +2 -2
- package/dist/{bridge-service-hX1ZQp4W.d.ts → bridge-service-CNBKdYXh.d.ts} +2 -1
- package/dist/{bridge-store-DPVSCwfs.d.ts → bridge-store-ByJ6CaJk.d.ts} +3 -0
- package/dist/bridge.d.ts +25 -8
- package/dist/bridge.js +19 -9
- package/dist/{chunk-MBPZXTJA.js → chunk-7VJI2MXI.js} +285 -42
- package/dist/chunk-7VJI2MXI.js.map +1 -0
- package/dist/{chunk-4VB44KJU.js → chunk-AWE3DFZV.js} +3 -2
- package/dist/chunk-AWE3DFZV.js.map +1 -0
- package/dist/{chunk-GADH244D.js → chunk-E4OWHGUE.js} +16 -5
- package/dist/chunk-E4OWHGUE.js.map +1 -0
- package/dist/{chunk-NCO7RLBM.js → chunk-GTK4KOED.js} +108 -8
- package/dist/chunk-GTK4KOED.js.map +1 -0
- package/dist/{chunk-SPSMZOCJ.js → chunk-HUHG7XTB.js} +6 -5
- package/dist/chunk-HUHG7XTB.js.map +1 -0
- package/dist/{chunk-5GMB3A77.js → chunk-IVG4XWWS.js} +26 -14
- package/dist/chunk-IVG4XWWS.js.map +1 -0
- package/dist/{chunk-WAI2IYIQ.js → chunk-P635FAN4.js} +50 -7
- package/dist/chunk-P635FAN4.js.map +1 -0
- package/dist/{chunk-45BZGB77.js → chunk-QNXHDOJ5.js} +218 -24
- package/dist/chunk-QNXHDOJ5.js.map +1 -0
- package/dist/{chunk-UAHMP6GC.js → chunk-QPC6JHA3.js} +10 -7
- package/dist/chunk-QPC6JHA3.js.map +1 -0
- package/dist/{chunk-Q6PPMXJC.js → chunk-SN7PIVDF.js} +377 -21
- package/dist/chunk-SN7PIVDF.js.map +1 -0
- package/dist/{chunk-A6W4QALF.js → chunk-TC6A7GMF.js} +2 -2
- package/dist/{chunk-2DFNOLMO.js → chunk-XDNT6ZNZ.js} +9 -6
- package/dist/chunk-XDNT6ZNZ.js.map +1 -0
- package/dist/{chunk-CEXBMOI5.js → chunk-Z6JP6MYD.js} +2 -2
- package/dist/cli.js +5867 -438
- package/dist/cli.js.map +1 -1
- package/dist/client-runtime.d.ts +4 -2
- package/dist/client-runtime.js +7 -7
- package/dist/contracts/generate.js +1 -1
- package/dist/dr.d.ts +9 -2
- package/dist/dr.js +6 -4
- package/dist/{gateway-CggQ8vTC.d.ts → gateway-CW-61lvs.d.ts} +27 -3
- package/dist/gateway-main.js +71 -7
- package/dist/gateway-main.js.map +1 -1
- package/dist/gateway.d.ts +3 -3
- package/dist/gateway.js +3 -3
- package/dist/index.js +8 -8
- package/dist/{postgres-bridge-store-Bfd3hRId.d.ts → postgres-bridge-store-C9KG9Z69.d.ts} +1 -1
- package/dist/postgres-production-preflight.d.ts +32 -0
- package/dist/postgres-production-preflight.js +249 -0
- package/dist/postgres-production-preflight.js.map +1 -0
- package/dist/server.js +8 -8
- package/dist/sqlite-edge-store-CdMIOrho.d.ts +148 -0
- package/dist/sqlite.d.ts +19 -80
- package/dist/sqlite.js +11 -3
- package/docs/architecture-v2.md +292 -15
- package/docs/decisions/0004-client-lifecycle-and-endpoint-migration.md +204 -0
- package/docs/deployment.md +349 -0
- package/docs/troubleshooting.md +23 -0
- package/llms.txt +69 -1
- package/openapi/agent-bridge-v2.openapi.json +1151 -124
- package/package.json +10 -2
- package/schemas/agent-bridge-v2.capabilities.json +26 -1
- package/schemas/agent-bridge-v2.mcp.json +11 -1
- package/schemas/agent-bridge-v2.schema.json +167 -0
- package/sql/migrations/007_runtime_role.sql +8 -4
- package/sql/migrations/013_row_isolation.sql +71 -8
- package/sql/migrations/014_owner_control_plane.sql +124 -31
- package/sql/migrations/015_portable_archives.sql +143 -41
- package/sql/migrations/016_native_dr_fence.sql +102 -35
- package/sql/migrations/017_gateway_authority_binding.sql +99 -0
- package/sql/migrations/018_endpoint_migration_challenges.sql +1054 -0
- package/sql/migrations/019_endpoint_migration_same_successor.sql +348 -0
- package/sql/migrations/020_managed_authority_compat.sql +698 -0
- package/sql/migrations/021_native_backup_reader.sql +421 -0
- package/sql/setup.sql +1 -3
- package/dist/chunk-2DFNOLMO.js.map +0 -1
- package/dist/chunk-45BZGB77.js.map +0 -1
- package/dist/chunk-4VB44KJU.js.map +0 -1
- package/dist/chunk-5GMB3A77.js.map +0 -1
- package/dist/chunk-GADH244D.js.map +0 -1
- package/dist/chunk-MBPZXTJA.js.map +0 -1
- package/dist/chunk-NCO7RLBM.js.map +0 -1
- package/dist/chunk-Q6PPMXJC.js.map +0 -1
- package/dist/chunk-SPSMZOCJ.js.map +0 -1
- package/dist/chunk-UAHMP6GC.js.map +0 -1
- package/dist/chunk-WAI2IYIQ.js.map +0 -1
- /package/dist/{chunk-A6W4QALF.js.map → chunk-TC6A7GMF.js.map} +0 -0
- /package/dist/{chunk-CEXBMOI5.js.map → chunk-Z6JP6MYD.js.map} +0 -0
package/docs/architecture-v2.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Agent Bridge v2 architecture
|
|
2
2
|
|
|
3
|
-
Status: living architecture
|
|
3
|
+
Status: living architecture for the 0.4.x package line.
|
|
4
4
|
|
|
5
5
|
## Product boundary
|
|
6
6
|
|
|
@@ -31,6 +31,42 @@ A standalone Agent Bridge API sits between remote clients and PostgreSQL. Client
|
|
|
31
31
|
|
|
32
32
|
Supabase can still host the database. Existing direct PostgREST deployments remain available through an explicitly named legacy adapter while they migrate.
|
|
33
33
|
|
|
34
|
+
### Deployment separates schema and request authority
|
|
35
|
+
|
|
36
|
+
The schema-owner connection runs migrations as an explicit one-shot operation. A
|
|
37
|
+
separate bootstrap step creates or updates a restricted runtime login and grants only
|
|
38
|
+
the database-derived runtime role. The gateway starts only after both steps succeed and
|
|
39
|
+
receives no schema-owner authority. Its readiness check fails on an incomplete
|
|
40
|
+
migration plan, unsupported PostgreSQL major, row-isolation drift, protected catalog
|
|
41
|
+
drift, or an invalid owner-control membership graph.
|
|
42
|
+
|
|
43
|
+
Runtime bootstrap runs in one transaction. Before it changes an existing login, it
|
|
44
|
+
rejects every database-derived Agent Bridge authority name, unexpected role edge, and
|
|
45
|
+
object owner. PostgreSQL 16 and newer may add one managed-provider administrator edge
|
|
46
|
+
when the schema owner creates the login. The bootstrap validates that edge exactly. It
|
|
47
|
+
does not adopt a `NOLOGIN` shell restored by native DR.
|
|
48
|
+
|
|
49
|
+
The schema owner can be a true superuser or a managed-provider administrator with
|
|
50
|
+
`CREATEROLE`. That authority is sufficient for migrations. Native backup requires the
|
|
51
|
+
schema owner to be a true superuser or hold `BYPASSRLS`. A restore target validates the
|
|
52
|
+
intentionally suspended restored schema-owner shell through its superuser session. PostgreSQL 16 and newer may
|
|
53
|
+
split its authority over an internal role between a bootstrap administrator row and a
|
|
54
|
+
schema-owner inherit-and-set row. Native DR may restore the same effective authority as
|
|
55
|
+
one combined bootstrap-superuser row. Security checks aggregate the effective options
|
|
56
|
+
but also validate each allowed physical grant shape. Registered operator and archive
|
|
57
|
+
memberships remain single, schema-owner-granted edges without the admin option.
|
|
58
|
+
|
|
59
|
+
The repository Compose stack publishes the gateway and PostgreSQL on loopback for
|
|
60
|
+
development. Production deployments put PostgreSQL on a private network, terminate TLS
|
|
61
|
+
before every non-loopback gateway, and source credentials from the platform's secret
|
|
62
|
+
manager. A persistent volume is runtime storage, not a backup.
|
|
63
|
+
|
|
64
|
+
Upgrade order is database backup, migration, runtime bootstrap, readiness, then client
|
|
65
|
+
or gateway rollout. Because an older image may reject the newer migration plan, schema
|
|
66
|
+
rollback cannot rely on starting the previous container image. Operators apply a
|
|
67
|
+
forward fix or restore a verified native DR bundle into a fresh target before switching
|
|
68
|
+
authority. [The deployment guide](deployment.md) records the operational procedure.
|
|
69
|
+
|
|
34
70
|
### SQLite is local storage
|
|
35
71
|
|
|
36
72
|
SQLite WAL provides local-only operation, a durable outbox, an inbox cache, cursor state, and a cheap pending-work check. Outbox writes use `synchronous=FULL`. It is not the canonical multi-machine database.
|
|
@@ -39,7 +75,17 @@ The gateway outbox queues immutable message publication. Receipt and lease mutat
|
|
|
39
75
|
|
|
40
76
|
Long-lived gateway MCP clients own a cancellable transport loop that replays publications and refreshes the inbox cache with bounded exponential backoff. It is transport maintenance, not agent monitoring. Manual MCP and CLI sync use the same replay path. A publication timeout after the gateway may have committed remains queued under its stable idempotency key. A retry resolves to the original immutable message instead of publishing a duplicate.
|
|
41
77
|
|
|
42
|
-
Local initialization must be idempotent.
|
|
78
|
+
Local initialization must be idempotent. Concurrent initialization and schema upgrade
|
|
79
|
+
use a 15-second minimum busy-retry window. Normal database operations retain the
|
|
80
|
+
configured timeout. The minimum supported SQLite build must include the WAL-reset
|
|
81
|
+
corruption fix. Node 22.23.1 on the current development machine embeds SQLite 3.51.3,
|
|
82
|
+
which includes that fix.
|
|
83
|
+
|
|
84
|
+
On Windows, an opening store verifies the private parent and main database before it
|
|
85
|
+
connects. It applies and verifies explicit ACLs on live WAL and shared-memory sidecars
|
|
86
|
+
after the connection establishes WAL and completes the serialized schema transaction.
|
|
87
|
+
This avoids first-open sidecar churn between concurrent processes. A sidecar that is
|
|
88
|
+
replaced during one ACL validation still fails closed.
|
|
43
89
|
|
|
44
90
|
### Portable archives cross storage engines without copying runtime state
|
|
45
91
|
|
|
@@ -123,19 +169,39 @@ PostgreSQL backup takes a repeatable-read exported snapshot while holding the na
|
|
|
123
169
|
advisory fence. `pg_dump` captures only the `agent_bridge` schema. A separate canonical
|
|
124
170
|
role inventory records the bounded role shells, membership options, and global or
|
|
125
171
|
schema-scoped default ACLs that a schema-only dump omits. The dump excludes data for
|
|
126
|
-
agent instances, rate-limit buckets, request authorities,
|
|
127
|
-
authorizations. The manifest binds migration
|
|
128
|
-
count, tool major, role inventory, and the
|
|
129
|
-
portable-archive readiness definitions.
|
|
172
|
+
agent instances, rate-limit buckets, request authorities, endpoint-migration challenge
|
|
173
|
+
rows, and archive transaction authorizations. The manifest binds migration
|
|
174
|
+
inventory, table counts, claimed-delivery count, tool major, role inventory, and the
|
|
175
|
+
security, row-isolation, owner-control, and portable-archive readiness definitions.
|
|
176
|
+
|
|
177
|
+
The immutable gateway authority UUID is ordinary PostgreSQL data, so native backup and
|
|
178
|
+
restore preserve it. The UUID proves that a restored target represents the same logical
|
|
179
|
+
authority. It does not fence a live clone. Operators must still keep the source and
|
|
180
|
+
restored target from serving traffic at the same time.
|
|
130
181
|
|
|
131
182
|
PostgreSQL restore requires a dedicated fresh database, the same database name and
|
|
132
183
|
server major, an exact-major `pg_restore`, and superuser authority. It restores role
|
|
133
184
|
shells as `NOLOGIN`, then schema objects, memberships, and default privileges. Claimed
|
|
134
185
|
deliveries become retrying deliveries with cleared leases. The restore checks migrations,
|
|
135
186
|
counts, role inventory, and every readiness attestation before success. External
|
|
136
|
-
principals remain `NOLOGIN`.
|
|
137
|
-
|
|
138
|
-
|
|
187
|
+
principals remain `NOLOGIN`. Runtime memberships are omitted. Active control and archive
|
|
188
|
+
memberships are omitted and receive explicit revocation events after their prior history.
|
|
189
|
+
A matching schema-owner role may be reused only when it is the target session user and
|
|
190
|
+
a true superuser. Every other role collision fails. A failure after target mutation
|
|
191
|
+
begins disables new target connections. If that offlining step fails, the command
|
|
192
|
+
reports the target as unsafe and lists residual role shells.
|
|
193
|
+
|
|
194
|
+
Managed schema owners need read access to protected ledgers and sequences before
|
|
195
|
+
`pg_dump` can produce a complete native backup. A database-derived `NOLOGIN`
|
|
196
|
+
backup-reader role receives only that access and is granted only to the schema owner.
|
|
197
|
+
Native backup requires that schema owner to be a true superuser or hold `BYPASSRLS`.
|
|
198
|
+
During restore, the target superuser may validate the intentionally suspended restored
|
|
199
|
+
schema-owner shell. A restricted runtime login cannot substitute for that authority.
|
|
200
|
+
`CREATEROLE` alone can apply migrations but cannot produce a complete native backup.
|
|
201
|
+
Native-backup readiness attests the role, authority, membership, and object ACLs. The
|
|
202
|
+
gateway and external operator roles cannot inherit the reader. This backup gate is
|
|
203
|
+
separate from gateway runtime readiness, so drift in an owner-only backup object cannot
|
|
204
|
+
take a healthy gateway out of service.
|
|
139
205
|
|
|
140
206
|
PostgreSQL source and target URLs come only from
|
|
141
207
|
`AGENT_BRIDGE_DR_SOURCE_DATABASE_URL` and
|
|
@@ -173,7 +239,7 @@ A receipt records that a principal read a message.
|
|
|
173
239
|
|
|
174
240
|
A publisher stores an immutable `deliveryPolicy` with each message. Untargeted messages default to mailbox mode. Targeted messages default to leased mode with `maxAttempts: 5`, `retryBaseDelayMs: 1000`, `retryMaxDelayMs: 60000`, and `retryJitterRatio: 0.2`. Leased policy may also set `notBefore`. Mailbox messages create no deliveries, and legacy mode rejects leased policy.
|
|
175
241
|
|
|
176
|
-
A leased delivery records executable work for a recipient. Its state is one of `pending`, `claimed`, `acked`, `retrying`, `dead`, or `cancelled`. `attempt` is lifetime-monotonic, while `cycleAttempt` resets on publisher requeue and `requeueCount` increments. Stored policy owns retry and exhaustion. Claims order due work by urgent, high, then info priority, followed by availability, message creation time, and the stable delivery ID tie-break.
|
|
242
|
+
A leased delivery records executable work for a recipient. Its state is one of `pending`, `claimed`, `acked`, `retrying`, `dead`, or `cancelled`. `attempt` is lifetime-monotonic, while `cycleAttempt` resets on publisher requeue and `requeueCount` increments. Stored policy owns retry and exhaustion. Claims order due work by urgent, high, then info priority, followed by availability, message creation time, and the stable delivery ID tie-break. HTTP 2.1, MCP, and CLI may optionally identify one message. That form atomically selects only the eligible delivery for the authenticated recipient and scopes claim-time expiry and exhaustion maintenance to the same message. Omitting the filter preserves claim-next behavior. The frozen HTTP 2.0 request rejects the additive field.
|
|
177
243
|
|
|
178
244
|
Consumer-side `maxAttempts` on claim and `retryPolicy` on nack are compatibility inputs for one release. The service validates them and ignores them. This keeps older clients working without letting a consumer change immutable publisher intent.
|
|
179
245
|
|
|
@@ -250,6 +316,189 @@ Protocol logic depends on a `BridgeStore` interface, not a provider SDK. The ini
|
|
|
250
316
|
|
|
251
317
|
Host and wakeup adapters are independent from storage. A client manifest declares identity injection, supported wake modes, config locations, health checks, and install actions for Codex, Claude Code, Claude Desktop, OpenClaw, or a generic MCP client. The v1 manifest key named `runtime` identifies an installation target for compatibility. It does not identify a unique running process. The `codex` adapter configures the profile shared by the Codex CLI and the Codex surface in the ChatGPT desktop app. Scripts, CI jobs, daemons, and application services may instead call the CLI or HTTPS API or embed the Node library without pretending to be a harness adapter.
|
|
252
318
|
|
|
319
|
+
Client lifecycle ownership is separate from registration existence. Local inspection
|
|
320
|
+
classifies a requested Codex, Claude Code, or Claude Desktop contract as absent,
|
|
321
|
+
unmanaged, managed, or drifted by comparing the exact identity, stable instance key,
|
|
322
|
+
backend path, scope, launch contract, and nonsecret registration locator. Desktop
|
|
323
|
+
locators name the normalized host config, Codex locators name the active profile
|
|
324
|
+
config, and Claude Code local or project locators include the invocation directory
|
|
325
|
+
because the native CLI exposes no stronger target. Adoption is plan-first and writes
|
|
326
|
+
only owner-private, credential-free metadata when `--apply` is explicit and the
|
|
327
|
+
unmanaged registration is exact. Backend files and their immediate parents must pass
|
|
328
|
+
the owner-only no-link policy. Codex JSON comparisons ignore diagnostic/status fields
|
|
329
|
+
and property order but require exactly the three Agent Bridge environment keys;
|
|
330
|
+
Claude Code connectivity status is not registration state. Claude Desktop inspection
|
|
331
|
+
shares the installer's validated absolute launch resolver. Applied adoption
|
|
332
|
+
re-inspects its postcondition. It does not read backend values or mutate the
|
|
333
|
+
registration.
|
|
334
|
+
Enrollment-based first-time provisioning retains strict registration and backend-file
|
|
335
|
+
collision refusal. See
|
|
336
|
+
[ADR 0004](decisions/0004-client-lifecycle-and-endpoint-migration.md).
|
|
337
|
+
|
|
338
|
+
Managed-client repair, update, and uninstall use a local crash-safe substrate. Strict
|
|
339
|
+
owner-private metadata is the mutation authority. Runtime plus stable instance locate
|
|
340
|
+
the metadata, and identity must match both the record and the immutable repair or
|
|
341
|
+
update request. Uninstall also binds the identity. Repair restores its launch contract. Update validates a new launch
|
|
342
|
+
before it creates the journal and stores only the identity, normalized command,
|
|
343
|
+
arguments, scope, and fixed Agent Bridge environment key names.
|
|
344
|
+
Caller backend, scope, and host-config flags are rejected. Owner-private revisioned
|
|
345
|
+
manifests live under
|
|
346
|
+
`~/.agent-bridge/operations/<uuid>` with private snapshots and an exclusive lock keyed
|
|
347
|
+
by runtime plus stable instance. Manifest publication fsyncs files, atomically renames,
|
|
348
|
+
verifies private paths, and fsyncs directories where supported. Snapshot publication
|
|
349
|
+
uses atomic no-replace links, so residue from an interrupted manifest update cannot be
|
|
350
|
+
overwritten. Sensitive file access pins its immediate directory. Operation-root and
|
|
351
|
+
target identities remain pinned throughout creation and destructive cleanup. The root
|
|
352
|
+
and locks identities remain pinned throughout stale recovery, and the artifact-directory
|
|
353
|
+
identity remains pinned across enumeration and every read. A typed credential-agnostic
|
|
354
|
+
request and immutable plan record non-sensitive locators, unique no-replace before and
|
|
355
|
+
after artifacts, and expected digests. Backend artifacts contain only a role, file
|
|
356
|
+
identity, and private or repairable policy state. They never contain backend bytes,
|
|
357
|
+
credentials, URLs, or workspace values. Begin is lock-covered and refuses another
|
|
358
|
+
unfinished operation; any blocked journal fences new mutation, including resume. Resume
|
|
359
|
+
is same-host only. The journal advances through prepared,
|
|
360
|
+
snapshotted, in-progress, applied, cleaning, and committed. Inspection separately
|
|
361
|
+
reports resumable, classification-required, blocked, or complete.
|
|
362
|
+
|
|
363
|
+
Repair and uninstall journals use operation format version 3. New update and reverse
|
|
364
|
+
rollback journals use v4. A v4 update request and terminal completion retain a bounded
|
|
365
|
+
credential-free inverse contract: prior managed metadata, prior exact registration
|
|
366
|
+
contract, and forward metadata and registration digests. Version 2 manifests retain
|
|
367
|
+
their released request interpretation and remain inspectable. A non-terminal version 2
|
|
368
|
+
record is blocked because it cannot prove the identity-bound request needed for these
|
|
369
|
+
public mutations.
|
|
370
|
+
|
|
371
|
+
Before a mutation, repair, update, and uninstall compare the full strict metadata record after
|
|
372
|
+
acquiring the lock. Each non-metadata step repeats that authority check. Registration
|
|
373
|
+
proofs include a bounded observation of the Agent Bridge entry and its full target
|
|
374
|
+
contract. Unknown environment keys, malformed values, unsafe arguments, an unexpected
|
|
375
|
+
scope, or an opaque registration shape stop before a journal is created.
|
|
376
|
+
|
|
377
|
+
Native repair and update remove the managed entry, prove absence, add the exact target,
|
|
378
|
+
and change metadata last when the launch changes. A native update command is one
|
|
379
|
+
executable contract. Bare commands cannot contain argument separators, while absolute
|
|
380
|
+
paths must resolve to executable files before an update is journaled. Codex runs get,
|
|
381
|
+
remove, and add with the recorded profile home over the supplied environment. Claude
|
|
382
|
+
Code uses its recorded scope, working directory, and supplied environment. Claude
|
|
383
|
+
Desktop reads its recorded config without following links or link ancestors, replaces
|
|
384
|
+
only `mcpServers.agent-bridge`, writes through an operation-scoped private temporary
|
|
385
|
+
file, and verifies the published entry. Node cannot make that update atomic with an
|
|
386
|
+
uncooperative same-user Desktop writer. The code pins and rechecks the file and parent
|
|
387
|
+
identities immediately before rename, but this is an advisory race boundary, not an OS
|
|
388
|
+
transaction guarantee. Windows accepts an already owner-private backend path and
|
|
389
|
+
refuses a non-private backend instead of claiming a safe permission tightening.
|
|
390
|
+
|
|
391
|
+
Uninstall removes a managed registration, proves its absence, deletes the private
|
|
392
|
+
backend file, then deletes the management metadata. It does not tighten an unsafe
|
|
393
|
+
backend before deletion. A backend that is not already private stops the plan before
|
|
394
|
+
it removes the registration. Uninstall never rolls a completed step back. Desktop
|
|
395
|
+
deletes only `mcpServers.agent-bridge` and preserves unrelated JSON. Before a backend
|
|
396
|
+
or metadata unlink, the code checks the private parent and file identities. POSIX
|
|
397
|
+
syncs that parent after deletion. Windows reports verified deletion with unavailable
|
|
398
|
+
directory durability. Node pathname deletion cannot make this atomic against an
|
|
399
|
+
uncooperative same-user writer, so that race remains advisory.
|
|
400
|
+
|
|
401
|
+
Rollback is an explicit update-only operation. `clients rollback` locates authority
|
|
402
|
+
only from a committed same-host v4 update record and requires the recorded identity as
|
|
403
|
+
an assertion. It rejects a changed forward registration or metadata state before it
|
|
404
|
+
creates a new reverse journal. Native rollback removes the forward registration,
|
|
405
|
+
proves absence, adds the prior registration, proves it, and writes prior metadata last.
|
|
406
|
+
Desktop replaces only `mcpServers.agent-bridge` and writes prior metadata last. The
|
|
407
|
+
reverse journal retains its source operation UUID and inverse contract so generic
|
|
408
|
+
resume needs no caller-provided backend, scope, command, runtime, or instance. A
|
|
409
|
+
failed forward update is never compensated automatically. Repair has no rollback, and
|
|
410
|
+
uninstall recovery requires re-enrollment.
|
|
411
|
+
|
|
412
|
+
Native Windows ACL checks start uncached for each acquired or resumed mutation lock.
|
|
413
|
+
While that lock remains held, later directory checks may reuse the result only for the
|
|
414
|
+
same directory path, device, and inode. File checks always use the native policy.
|
|
415
|
+
Passive inspection starts without a cache, and POSIX mode checks run on every access.
|
|
416
|
+
A same-user process can still change an ACL on an unchanged directory or replace it
|
|
417
|
+
with a directory that reuses the same filesystem identity. Local mode already treats
|
|
418
|
+
that OS user as trusted. Agent Bridge does not delete a cached directory during a
|
|
419
|
+
successful managed-client mutation.
|
|
420
|
+
|
|
421
|
+
Cleanup durably records per-artifact intent, verifies and unlinks the pinned artifact,
|
|
422
|
+
then records POSIX directory sync or explicit Windows unavailability. An absent file
|
|
423
|
+
is resumable only when intent predates the cleanup attempt. Inspection also recognizes
|
|
424
|
+
one verified after artifact left by an interrupted manifest publication. Other missing
|
|
425
|
+
or extra files block. Committed means verified writes and removed artifacts. Its
|
|
426
|
+
manifest drops requests, steps, locators, digests, and artifact metadata. A v4 update
|
|
427
|
+
completion retains only its inverse contract. A v5 stage and a v6 migration phase
|
|
428
|
+
retain bounded credential-free contracts. Other terminal completions retain the
|
|
429
|
+
operation kind, step count, completion time, and cleanup durability for audit. Uninstall
|
|
430
|
+
adds no rolled-back state. Physical erasure remains outside the filesystem contract.
|
|
431
|
+
|
|
432
|
+
`clients resume <operation-id>` accepts stored v3 repair, update, and uninstall
|
|
433
|
+
operations, supported v4 update and rollback operations, a committed v5 migration
|
|
434
|
+
stage, and v6 endpoint-migration operations, with optional same-host stale-lock
|
|
435
|
+
recovery. It derives authority from the immutable request. It rejects v2 records and
|
|
436
|
+
cannot take replacement caller authority. Generic resume finishes an uninstall
|
|
437
|
+
interrupted after metadata deletion and a reverse rollback interrupted after any
|
|
438
|
+
recorded boundary.
|
|
439
|
+
|
|
440
|
+
### Gateway client migration staging prepares a later cutover
|
|
441
|
+
|
|
442
|
+
`clients migrate stage <runtime>` creates a private successor backend and a
|
|
443
|
+
credential-free stage record. It leaves the managed registration, active backend, and
|
|
444
|
+
source edge gate state unchanged after initializing the gate schema. It requires a
|
|
445
|
+
rotation enrollment that keeps the predecessor valid through a future grace cutoff.
|
|
446
|
+
The stage record binds the managed
|
|
447
|
+
identity, source and target endpoint digests, both credential IDs, the source edge
|
|
448
|
+
database path and scope key, and the grace cutoff. It does not store either token or a
|
|
449
|
+
raw gateway URL.
|
|
450
|
+
|
|
451
|
+
The source backend must bind an absolute, normalized edge database file. Relative and
|
|
452
|
+
in-memory paths cannot identify the same durable outbox across harness working
|
|
453
|
+
directories, so staging rejects them.
|
|
454
|
+
|
|
455
|
+
An applied stage probes the active and staged bearer credentials, records the
|
|
456
|
+
enrollment credential IDs, initializes the recorded source edge database, and requires
|
|
457
|
+
its migration gate to be active. SQLite rejects a new outbox insert as soon as that
|
|
458
|
+
gate begins draining, including inserts from a client that had already opened the
|
|
459
|
+
database. These checks prepare a later drain and cutover. They do not authorize either
|
|
460
|
+
action.
|
|
461
|
+
|
|
462
|
+
The stage does not prove that source and target reach the same database authority. A
|
|
463
|
+
later endpoint cutover may use alternate URLs only after it dynamically attests one
|
|
464
|
+
logical authority. The authority identifier must survive clone and restore. Moving to
|
|
465
|
+
an independent database requires a separate owner-mediated fence and is outside this
|
|
466
|
+
staging command. A copied authority identifier does not prove that a live clone is
|
|
467
|
+
fenced.
|
|
468
|
+
|
|
469
|
+
### Endpoint cutover uses a separate v6 journal
|
|
470
|
+
|
|
471
|
+
`clients migrate cutover <stage-operation-id> --exclusive-edge --apply` accepts only a
|
|
472
|
+
committed v5 stage with its retained contract. It checks both live gateway status
|
|
473
|
+
responses under HTTP 2.1. Each response must bind the recorded workspace, principal,
|
|
474
|
+
and credential ID, and both must return the recorded immutable authority ID. The
|
|
475
|
+
successor must grant `status:read` and `messages:write`. The source issues a direct
|
|
476
|
+
predecessor-to-successor route challenge that the target consumes before the command
|
|
477
|
+
changes an edge gate or host registration.
|
|
478
|
+
|
|
479
|
+
The forward journal records full credential-free source and target metadata and
|
|
480
|
+
registration proofs, source and target edge paths, a normalized credential-free source
|
|
481
|
+
gateway URL, the v5 stage contract, and digest bindings for each proof. Once that
|
|
482
|
+
journal exists, every mutation uses a fresh same-successor route proof issued from the
|
|
483
|
+
retained source URL and consumed by the target. The source edge replays through the
|
|
484
|
+
target gateway with the successor credential. It creates a durable source drain lease.
|
|
485
|
+
The edge rejects new outbox inserts while draining. The lease worker must publish all
|
|
486
|
+
work and prove a zero outbox before a native remove or add, a Desktop replacement, or
|
|
487
|
+
the metadata-last switch. The command renews and reasserts the lease immediately before
|
|
488
|
+
each host write. It rechecks the live target authority before every mutation. Source
|
|
489
|
+
and target edge files must be distinct private regular files. The command rejects a
|
|
490
|
+
managed cohort that shares the source edge, including a hardlink alias.
|
|
491
|
+
`--exclusive-edge` covers unmanaged publishers that cannot be enumerated.
|
|
492
|
+
|
|
493
|
+
Finalization requires a fresh assertion after the grace cutoff. It verifies the
|
|
494
|
+
successor's live status binding and retires the retained source edge. Returning to a
|
|
495
|
+
former endpoint requires a new owner credential rotation and a separate forward
|
|
496
|
+
cutover. Cutover and finalization use exact durable before and after proofs for restart
|
|
497
|
+
classification. A replacement worker can resume a draining edge only after the prior
|
|
498
|
+
lease expires. Dry runs use read-only edge inspection. They do not initialize SQLite,
|
|
499
|
+
create sidecars, or contact gateways. A dry run refuses an edge with live WAL sidecars
|
|
500
|
+
because an immutable read could miss uncheckpointed outbox state.
|
|
501
|
+
|
|
253
502
|
### v1 compatibility is additive
|
|
254
503
|
|
|
255
504
|
The MCP tools `post_context`, `get_context`, and `ack_context` remain available. Existing CLI verbs and flags remain accepted. Text responses remain present while v2 adds structured result data.
|
|
@@ -294,7 +543,7 @@ must already satisfy that final policy. Node identity and file-type checks run b
|
|
|
294
543
|
and after the native policy check. Native reparse attributes
|
|
295
544
|
reject symlinks, junctions, and other reparse objects before and after DACL work.
|
|
296
545
|
|
|
297
|
-
The schema owner is the trusted offline role administrator. It holds all three control roles with
|
|
546
|
+
The schema owner is the trusted offline role administrator. It holds all three control roles with effective admin, inherit, and set authority and registers eligible login roles through `register_control_member`; `revoke_control_member` removes them. Those functions are not available through HTTP or MCP. Each call is idempotent by request UUID and appends the database session actor to the membership ledger. Runtime readiness compares the active registry with the full `pg_has_role` closure and normalized direct `pg_auth_members` edges. It also checks the raw grant origins and option shapes. It rejects unregistered operator or auditor holders, every external owner holder, missing registered grants, unsafe login attributes, extra roles inherited by a registered member, and roles that inherit a registered member. Only the schema owner is exempt from the broad closure check because a PostgreSQL superuser reports membership across roles. A direct `GRANT` is never a valid deployment shortcut.
|
|
298
547
|
|
|
299
548
|
Every protected operation first locks a member-global key for `session_user`, followed by capability keys in operator-before-auditor order. It then checks the active registry, direct membership edge, safe login attributes, and complete upstream and downstream role closure. Inventory accepts either registered capability. Registration and revocation use the same global-first lock hierarchy. Opposite operator and auditor changes for one member therefore cannot deadlock. A revocation waits for an operation that already passed authorization, while every operation begun after revocation observes the new state. A session that remains in a revoked role through `SET ROLE` cannot use that stale authorization.
|
|
300
549
|
|
|
@@ -314,6 +563,33 @@ The stored definition is scoped to the control plane and its dependencies. It in
|
|
|
314
563
|
|
|
315
564
|
Migration 012 removes direct runtime reads of credential digest material and establishes transaction-bound request authority. Node computes the credential hash, so PostgreSQL never receives the raw bearer credential. Request authority, security accounting, and domain work use one checked-out PostgreSQL client and one explicit transaction. A security-definer opener matches the credential ID and hash against current revocation, expiry, successor-grace, agent, and workspace state in one locked statement, then records the database-derived credential, workspace, principal, and scopes for that backend and transaction. Instance remains validated same-principal attribution and never establishes authority. Claim, cancel, and requeue reuse the outer transaction without nested `BEGIN`.
|
|
316
565
|
|
|
566
|
+
Migration 017 adds an immutable singleton authority UUID and a separate bound opener.
|
|
567
|
+
The released opener retains its original result shape. The bound opener returns the
|
|
568
|
+
same credential, workspace, principal, and scopes with the authority UUID in the same
|
|
569
|
+
request transaction. Runtime readiness checks the singleton row, mutation trigger,
|
|
570
|
+
function catalog, and direct table and column privileges. The UUID is returned only by
|
|
571
|
+
authenticated gateway status. It is not a capability field, log field, or readiness
|
|
572
|
+
field.
|
|
573
|
+
|
|
574
|
+
Migration 018 adds issue and consume operations for a gateway-only HTTP 2.1
|
|
575
|
+
endpoint-migration challenge. The issuer provides a lowercase 64-character hexadecimal
|
|
576
|
+
challenge, expected authority UUID, and direct active successor credential. The
|
|
577
|
+
successor consumes the same commitment with its active transaction-bound authority.
|
|
578
|
+
PostgreSQL hashes the challenge with a domain separator before persistence and retains
|
|
579
|
+
no raw challenge in records, events, or replies. Challenges expire within 60 seconds,
|
|
580
|
+
can be consumed once, and do not permit endpoint cutover. The migration 018
|
|
581
|
+
predecessor-to-successor relation alone does not prove that two distinct URLs share
|
|
582
|
+
live database state. The migration extends the credential-security catalog and records
|
|
583
|
+
endpoint and owner-control v5 attestations before readiness succeeds.
|
|
584
|
+
|
|
585
|
+
Migration 019 keeps the direct predecessor-to-successor challenge for initial
|
|
586
|
+
preflight and also permits one active successor credential to issue and consume a
|
|
587
|
+
challenge across the retained source and target URLs. It preserves the migration 018
|
|
588
|
+
challenge data, events, security boundaries, and native DR treatment. Endpoint v2 and
|
|
589
|
+
owner-control v6 attestations seal the updated catalog. Authenticated HTTP 2.1
|
|
590
|
+
capabilities report the credential's granted scopes so cutover can reject a successor
|
|
591
|
+
that lacks `status:read` or `messages:write` before it begins a journal.
|
|
592
|
+
|
|
317
593
|
The domain savepoint separates expected rejected domain work from security effects: expected scope and rate accounting can commit while failed domain mutations are rolled back. An abort before commit rolls the whole request back. Once commit dispatch begins, the gateway never retries and reports `mutation_outcome_unknown` if the outcome is ambiguous. A failed rollback discards the pooled connection.
|
|
318
594
|
|
|
319
595
|
Migration 013 enables and forces RLS on the five domain tables: messages, receipts, deliveries, delivery events, and agent instances. A database-specific no-login role owns those tables. Two more no-login roles isolate request-context reads and delivery-event writes. The gateway login inherits only the runtime role, which has `NOBYPASSRLS` and cannot inherit any of the owner roles. Zero-argument stable context functions read the workspace and principal from the authority row bound to the current backend, transaction ID, and session user. Policies call those functions through scalar subqueries so PostgreSQL evaluates them as InitPlans rather than once per row.
|
|
@@ -330,7 +606,7 @@ Limits apply to content bytes, payload bytes, metadata depth, target count, batc
|
|
|
330
606
|
|
|
331
607
|
The CLI provides `init`, `doctor`, `status`, `demo`, `send`, `inbox`, `history`, `claim`, `ack`, `nack`, `watch`, `sync`, portable archive, and migration commands. Existing `post` and `get` aliases remain available.
|
|
332
608
|
|
|
333
|
-
`/readyz` reports storage and schema readiness. Authenticated HTTP status reports the bound principal, provider schema, delivery counts, and the oldest due delivery. CLI `doctor` and `status` use a separate client-status contract that also reports local edge state and remote gateway reachability.
|
|
609
|
+
`/readyz` reports storage and schema readiness. Authenticated HTTP status reports the bound principal, provider schema, delivery counts, and the oldest due delivery. When production request authority is active, it also returns additive `gatewayAuthorityId` and `credentialId` fields. They are optional in HTTP 2.1. CLI `doctor` and `status` use a separate client-status contract that also reports local edge state and remote gateway reachability.
|
|
334
610
|
|
|
335
611
|
Gateway responses carry a request ID, including stable error envelopes. Scope failures return required scopes under `error.details`. Rate failures return a rounded retry delay in both the HTTP header and error details. Authenticated Prometheus output counts requests, errors, timeouts, and authentication failures. Responses, metrics, and append-only security events exclude bearer tokens, hashes, database URLs, payload bodies, arbitrary metadata, and credential material.
|
|
336
612
|
|
|
@@ -346,10 +622,11 @@ source, a tag-to-version check, and npm provenance. The npm environment does not
|
|
|
346
622
|
currently require a human reviewer, so the release process must not claim a manual
|
|
347
623
|
approval gate.
|
|
348
624
|
|
|
349
|
-
GitHub immutable releases are enabled, and GitHub reports release `v0.3.
|
|
625
|
+
GitHub immutable releases are enabled, and GitHub reports release `v0.3.1` as
|
|
350
626
|
immutable. A tag ruleset also blocks update and deletion of `v*` tags with no bypass
|
|
351
|
-
actor.
|
|
352
|
-
provenance workflow identity and checked-out source commit do not diverge.
|
|
627
|
+
actor. Recovery dispatches must run the workflow at the release tag ref so the
|
|
628
|
+
provenance workflow identity and checked-out source commit do not diverge. Release
|
|
629
|
+
packaging also waits for the exact commit to pass the full `test.yml` matrix on `main`.
|
|
353
630
|
|
|
354
631
|
## Acceptance checks
|
|
355
632
|
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# ADR 0004: Client lifecycle ownership and endpoint migration
|
|
2
|
+
|
|
3
|
+
Status: accepted
|
|
4
|
+
|
|
5
|
+
Date: 2026-07-18
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
Agent Bridge can install Codex, Claude Code, and Claude Desktop registrations, but an
|
|
10
|
+
existing registration may have been created by an older package or by an operator.
|
|
11
|
+
Repair, update, uninstall, and endpoint migration must not silently take ownership of
|
|
12
|
+
such state. In particular, host configuration and per-client backend files may contain
|
|
13
|
+
credential-bearing references and queued gateway edge state.
|
|
14
|
+
|
|
15
|
+
Lifecycle unit 1 needs a safe ownership boundary before later mutating operations are
|
|
16
|
+
added. It must distinguish missing state, exact but unowned state, owned state, and
|
|
17
|
+
drift without probing a gateway or exposing backend configuration values.
|
|
18
|
+
|
|
19
|
+
## Decision
|
|
20
|
+
|
|
21
|
+
The CLI provides two local lifecycle operations for the three automated adapters:
|
|
22
|
+
|
|
23
|
+
- `clients inspect` is read-only. The operator supplies the expected runtime,
|
|
24
|
+
identity, stable instance key, backend file path, launch command, and Claude Code
|
|
25
|
+
scope when applicable. Inspection reads only the host registration, backend path
|
|
26
|
+
existence, and managed metadata. It does not read backend values, contact the
|
|
27
|
+
gateway, interpret connectivity health, rewrite a host config, or create metadata.
|
|
28
|
+
- `clients adopt` uses the same exact contract. Without `--apply` it returns a plan.
|
|
29
|
+
With `--apply` it may create managed metadata only when inspection reports an exact
|
|
30
|
+
unmanaged registration. Adoption never rewrites the registration or backend file.
|
|
31
|
+
|
|
32
|
+
Inspection returns one of four states:
|
|
33
|
+
|
|
34
|
+
- `absent`: the requested registration, backend file, and managed metadata are all absent.
|
|
35
|
+
- `unmanaged`: the host registration, identity, instance, backend path, scope, and
|
|
36
|
+
launch contract are exact, the backend path and its immediate parent pass the
|
|
37
|
+
no-link owner-only private-path policy, and metadata is absent.
|
|
38
|
+
- `managed`: the exact registration and backend file are accompanied by exact managed
|
|
39
|
+
metadata.
|
|
40
|
+
- `drifted`: any other combination, including malformed or inexact registration,
|
|
41
|
+
missing, insecure, or linked backend residue without a registration, stale metadata,
|
|
42
|
+
or metadata that structurally differs from the requested contract.
|
|
43
|
+
|
|
44
|
+
Managed metadata uses schema `agent-bridge.client-management`, version 1, under the
|
|
45
|
+
owner-only `~/.agent-bridge/clients/` directory. It records only runtime, identity,
|
|
46
|
+
instance, absolute backend path, launch command and arguments, scope, and a nonsecret
|
|
47
|
+
registration locator. Claude Desktop records the normalized configuration path.
|
|
48
|
+
Codex records the active profile configuration path. Claude Code records scope and,
|
|
49
|
+
for local or project scope, the normalized invocation directory. Its native CLI does
|
|
50
|
+
not expose a stronger configuration locator. User scope has no directory context. A
|
|
51
|
+
later inspection from a different locator reports drift instead of guessing which
|
|
52
|
+
registration to mutate. Metadata never copies a token, backend setting, credential
|
|
53
|
+
identifier, URL, workspace, or backend file contents. Publication uses a private
|
|
54
|
+
same-directory temporary file, file fsync, atomic rename, private-path verification,
|
|
55
|
+
and directory fsync where supported.
|
|
56
|
+
|
|
57
|
+
Codex inspection compares the semantic registration fields in its real JSON MCP
|
|
58
|
+
description and ignores status, timeout, and transport diagnostic fields. JSON object
|
|
59
|
+
property order is not significant, while the environment must contain exactly the
|
|
60
|
+
three Agent Bridge keys. Claude Code inspection uses its native MCP description and
|
|
61
|
+
exact scope/removal contract but excludes connectivity status from registration
|
|
62
|
+
exactness. Claude Desktop inspection reads only the `agent-bridge` entry in its JSON
|
|
63
|
+
configuration and uses the installer's canonical resolver: an explicit executable is
|
|
64
|
+
validated, while the default is absolute Node plus the absolute server entry.
|
|
65
|
+
|
|
66
|
+
Identity, instance, backend, and command inputs are normalized before comparison and
|
|
67
|
+
metadata publication. Desktop configuration and Codex profile paths are normalized
|
|
68
|
+
before they become locators. Claude Code local and project adoption must run from the
|
|
69
|
+
directory that later lifecycle operations should use. After publication, adoption
|
|
70
|
+
re-inspects the live registration and fails if it changed. Inspection and plan-only
|
|
71
|
+
adoption preserve backend bytes, inode, and modification time. Applied adoption
|
|
72
|
+
writes only the management metadata described above; registration state and client
|
|
73
|
+
health remain separate authorities.
|
|
74
|
+
|
|
75
|
+
First-time `clients install` and enrollment behavior remains unchanged. A provision
|
|
76
|
+
enrollment still refuses an existing backend file or MCP registration, so adoption
|
|
77
|
+
cannot weaken first-install collision protection.
|
|
78
|
+
|
|
79
|
+
`clients repair`, `clients update`, and `clients uninstall` operate only on a strict
|
|
80
|
+
managed metadata record.
|
|
81
|
+
The runtime and stable instance locate that record. `--identity` must match the record
|
|
82
|
+
and the immutable request, but never locates it. These commands reject
|
|
83
|
+
backend path, scope, and config-path flags.
|
|
84
|
+
They plan without writing by default. A no-op exact registration creates no operation.
|
|
85
|
+
|
|
86
|
+
Repair restores the recorded launch. Update resolves a replacement launch before it
|
|
87
|
+
creates a journal. The update request records the identity, command, arguments, scope,
|
|
88
|
+
and fixed three Agent Bridge environment key names. It does not record their values.
|
|
89
|
+
Native launch arguments are empty. A native command is one executable contract. Bare
|
|
90
|
+
commands reject argument separators and URL-like surfaces. Absolute paths must resolve
|
|
91
|
+
to executable files before an update is journaled. Claude Desktop uses the installer's
|
|
92
|
+
absolute launch resolver. An action-specific resume uses that recorded request and
|
|
93
|
+
rejects another action, runtime, instance, identity, or replacement command.
|
|
94
|
+
|
|
95
|
+
Before a mutation starts, metadata load verifies the private management root, clients
|
|
96
|
+
directory, and metadata file. It rejects links, replacement, oversized files, unknown
|
|
97
|
+
keys, incorrect schema versions, malformed locators, and non-absolute backend paths.
|
|
98
|
+
The file is opened without following links and checked against its path identity before
|
|
99
|
+
the loader accepts it. The full record is loaded again after the client lock is acquired
|
|
100
|
+
and before every non-metadata mutation. Any difference blocks the operation.
|
|
101
|
+
|
|
102
|
+
Repair can tighten backend privacy only when that change removes access. Its journal
|
|
103
|
+
proofs contain the role, file identity, and private or repairable policy state. They
|
|
104
|
+
never contain backend bytes, credentials, URLs, workspace, or configuration values.
|
|
105
|
+
Links, disappearance, owner changes, replacement, and a permission change that would
|
|
106
|
+
grant new owner access fail closed. Windows accepts an already private backend path and
|
|
107
|
+
refuses a non-private one because the repair path cannot prove a monotonic ACL change.
|
|
108
|
+
|
|
109
|
+
Registration proofing records a bounded Agent Bridge observation and its full target
|
|
110
|
+
contract. Unknown environment keys, malformed values, unexpected scope, opaque fields,
|
|
111
|
+
or arguments outside the stored current or target launch stop before journal creation.
|
|
112
|
+
For Codex, get, remove, and add run with the recorded `CODEX_HOME` over the supplied
|
|
113
|
+
environment. Claude Code uses the recorded scope, invocation directory, and supplied
|
|
114
|
+
environment. Native changes remove, verify absence, add, verify the target, and write
|
|
115
|
+
changed metadata last. Claude Desktop reads the recorded JSON config without following
|
|
116
|
+
links or link ancestors, changes only `mcpServers.agent-bridge`, and publishes through
|
|
117
|
+
a private operation-scoped temporary file. It pins and rechecks the original file and
|
|
118
|
+
parent identities before rename, then verifies the final entry. Claude Desktop has no
|
|
119
|
+
cooperative transaction protocol. Node cannot guarantee a transaction against an
|
|
120
|
+
independent same-user writer.
|
|
121
|
+
|
|
122
|
+
The operation substrate remains internal with read-only inspection. Revisioned
|
|
123
|
+
manifests contain typed credential-agnostic requests, non-sensitive locators, and
|
|
124
|
+
unique no-replace before/after artifacts. Begin holds the runtime-plus-instance lock
|
|
125
|
+
while refusing another unfinished operation; a blocked journal fences new mutations and
|
|
126
|
+
resume. Resume is same-host only. Journal states
|
|
127
|
+
are prepared, snapshotted, in-progress, applied, cleaning, and committed. Inspection
|
|
128
|
+
separately reports resumable, classification-required, blocked, or complete.
|
|
129
|
+
|
|
130
|
+
Repair and uninstall journals use operation format version 3. New update journals use
|
|
131
|
+
v4 and carry a bounded credential-free inverse contract. Gateway migration staging
|
|
132
|
+
uses v5 and binds a managed identity, source and target endpoint digests, enrollment
|
|
133
|
+
credential IDs, a source edge database path and scope key, and a predecessor grace
|
|
134
|
+
cutoff. It contains no raw gateway URL or token. A version 2 journal keeps its released
|
|
135
|
+
request shape and remains inspectable. A non-terminal version 2 journal cannot resume
|
|
136
|
+
because it lacks an identity-bound request.
|
|
137
|
+
|
|
138
|
+
Endpoint cutover uses v6. Its journal retains the normalized source gateway URL without
|
|
139
|
+
a token so post-journal recovery can reconstruct the source edge route from the target
|
|
140
|
+
successor backend. Initial preflight uses the predecessor and direct successor. Every
|
|
141
|
+
later proof and source-edge replay uses the successor credential at both URLs. There is
|
|
142
|
+
no endpoint reverse operation. Returning to a prior endpoint requires a fresh owner
|
|
143
|
+
rotation and a new forward cutover.
|
|
144
|
+
|
|
145
|
+
Creation pins the operation root before it publishes state. Cleanup pins that root,
|
|
146
|
+
the operation directory, and the snapshots directory before it records intent or
|
|
147
|
+
unlinks anything. Cleanup records durable intent per artifact, verifies and unlinks the pinned file, and
|
|
148
|
+
records POSIX directory sync or explicit Windows unavailability. An absent artifact is
|
|
149
|
+
safe only when intent predates the cleanup attempt. Inspection recognizes a verified
|
|
150
|
+
after artifact for the current intent-recorded step, but rejects other missing or extra
|
|
151
|
+
files. Committed means verified writes and removed artifacts. It drops requests, steps,
|
|
152
|
+
locators, digests, and artifact metadata while retaining a bounded credential-free
|
|
153
|
+
completion record for audit. Public repair, update, and uninstall use this substrate.
|
|
154
|
+
|
|
155
|
+
Uninstall is forward-only. It removes the managed registration and proves absence,
|
|
156
|
+
deletes the already private backend file, then deletes metadata. It refuses a backend
|
|
157
|
+
that still needs privacy repair, so it cannot loosen that policy as a side effect of
|
|
158
|
+
deletion. A later failure does not restore a registration, backend, or metadata file.
|
|
159
|
+
The uninstall plan never stores backend bytes. File deletion confirms the file and
|
|
160
|
+
private parent identities before unlink. POSIX syncs the parent directory after a
|
|
161
|
+
verified absence. Windows records unavailable directory durability. Node's pathname
|
|
162
|
+
unlink cannot provide a transaction against a same-user writer, so this is an advisory
|
|
163
|
+
race boundary. Desktop removes only `mcpServers.agent-bridge`.
|
|
164
|
+
|
|
165
|
+
`clients rollback <source-operation-id> --identity <name>` is plan-first and accepts
|
|
166
|
+
only a committed same-host v4 update source. `--apply` creates a new v4 `rollback`
|
|
167
|
+
operation. It verifies the asserted identity, prior nonsecret metadata and exact
|
|
168
|
+
registration contract, and the current forward metadata and registration digests.
|
|
169
|
+
Native rollback removes the forward registration, proves absence, adds the prior
|
|
170
|
+
registration, proves it, then writes prior metadata. Desktop replaces only
|
|
171
|
+
`mcpServers.agent-bridge` and writes prior metadata last. The source journal remains
|
|
172
|
+
unchanged. Reverse resume derives authority from its recorded source UUID and inverse
|
|
173
|
+
contract. A changed current state, v2 or v3 source, repair, uninstall, unsafe inverse
|
|
174
|
+
fields, or a different host fails closed. A failed forward update never rolls back
|
|
175
|
+
automatically. Repair remains monotonic. Uninstall recovery is re-enrollment.
|
|
176
|
+
|
|
177
|
+
`clients migrate stage <runtime> --identity <name> --instance <key>
|
|
178
|
+
--enrollment-file <path>` is plan-first. With `--apply`, it creates a private staged
|
|
179
|
+
successor backend and a credential-free v5 record without changing the managed
|
|
180
|
+
registration or active backend. It initializes the source edge gate and leaves its
|
|
181
|
+
state active. It probes active and staged bearer credentials and requires a
|
|
182
|
+
non-immediate predecessor grace cutoff. It neither drains the outbox nor authorizes a
|
|
183
|
+
cutover. It requires an absolute, normalized source edge database path and rejects
|
|
184
|
+
in-memory edge state. The stage does not prove that source and target URLs reach the
|
|
185
|
+
same database authority.
|
|
186
|
+
|
|
187
|
+
`clients resume <operation-id> [--recover-lock]` resumes v3 repair, update, and
|
|
188
|
+
uninstall requests, supported v4 update and rollback requests, and v5 migration-stage
|
|
189
|
+
requests. It derives action, runtime, instance, identity, launch, and reverse source
|
|
190
|
+
from the record and accepts no replacement client authority. It cannot resume a version
|
|
191
|
+
2 operation. It also completes an uninstall interrupted after metadata deletion and a
|
|
192
|
+
rollback interrupted after any ordered step. A later endpoint cutover is limited to
|
|
193
|
+
alternate URLs and must dynamically attest one logical database authority. Physical
|
|
194
|
+
erasure is outside the contract.
|
|
195
|
+
|
|
196
|
+
## Consequences
|
|
197
|
+
|
|
198
|
+
Managed repair, update, and uninstall have a narrow authority boundary and can fail
|
|
199
|
+
closed on drift, metadata corruption, ambiguous crash state, and unsafe backend policy
|
|
200
|
+
changes. Existing exact registrations still require intentional, reviewable adoption.
|
|
201
|
+
Migration staging preserves active state while it prepares later work. Endpoint
|
|
202
|
+
cutover uses a separate v6 journal to drain the gateway edge outbox, attest shared live
|
|
203
|
+
route state, change the host registration, and retain enough credential-free authority
|
|
204
|
+
for crash recovery and post-grace source retirement.
|