@creatornader/agent-bridge 0.3.1 → 0.4.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 +150 -1
- package/README.md +239 -5
- package/ROADMAP.md +59 -7
- package/SECURITY.md +55 -5
- package/SKILL.md +103 -0
- package/dist/archive.d.ts +2 -2
- package/dist/archive.js +2 -2
- package/dist/{bridge-service-hX1ZQp4W.d.ts → bridge-service-YTjGr91A.d.ts} +1 -1
- package/dist/{bridge-store-DPVSCwfs.d.ts → bridge-store-Boaj-ZbO.d.ts} +2 -0
- package/dist/bridge.d.ts +17 -7
- package/dist/bridge.js +15 -7
- package/dist/{chunk-WAI2IYIQ.js → chunk-3YHVUPU2.js} +48 -5
- package/dist/chunk-3YHVUPU2.js.map +1 -0
- package/dist/{chunk-45BZGB77.js → chunk-6BTJE6D4.js} +168 -7
- package/dist/chunk-6BTJE6D4.js.map +1 -0
- package/dist/{chunk-5GMB3A77.js → chunk-DQOZJJZ2.js} +22 -10
- package/dist/chunk-DQOZJJZ2.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-GADH244D.js → chunk-IHZFI7TH.js} +12 -4
- package/dist/chunk-IHZFI7TH.js.map +1 -0
- package/dist/{chunk-CEXBMOI5.js → chunk-LAHNQZDN.js} +2 -2
- package/dist/{chunk-UAHMP6GC.js → chunk-MCKKVNCE.js} +9 -6
- package/dist/chunk-MCKKVNCE.js.map +1 -0
- package/dist/{chunk-MBPZXTJA.js → chunk-PDIHDBP3.js} +173 -20
- package/dist/chunk-PDIHDBP3.js.map +1 -0
- package/dist/{chunk-Q6PPMXJC.js → chunk-SV3SVBYS.js} +377 -21
- package/dist/chunk-SV3SVBYS.js.map +1 -0
- package/dist/{chunk-SPSMZOCJ.js → chunk-SYGONYMM.js} +4 -4
- package/dist/{chunk-A6W4QALF.js → chunk-TC6A7GMF.js} +2 -2
- package/dist/cli.js +5865 -436
- package/dist/cli.js.map +1 -1
- package/dist/client-runtime.d.ts +4 -2
- package/dist/client-runtime.js +6 -6
- 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-BQHpV-MZ.d.ts} +27 -3
- package/dist/gateway-main.js +69 -5
- package/dist/gateway-main.js.map +1 -1
- package/dist/gateway.d.ts +3 -3
- package/dist/gateway.js +2 -2
- package/dist/index.js +7 -7
- package/dist/{postgres-bridge-store-Bfd3hRId.d.ts → postgres-bridge-store-WASumj26.d.ts} +1 -1
- package/dist/server.js +7 -7
- package/dist/sqlite-edge-store-Bc15DvpX.d.ts +148 -0
- package/dist/sqlite.d.ts +19 -80
- package/dist/sqlite.js +11 -3
- package/docs/architecture-v2.md +256 -10
- package/docs/decisions/0004-client-lifecycle-and-endpoint-migration.md +204 -0
- package/docs/deployment.md +205 -0
- package/docs/troubleshooting.md +23 -0
- package/llms.txt +67 -0
- package/openapi/agent-bridge-v2.openapi.json +1023 -0
- package/package.json +6 -2
- package/schemas/agent-bridge-v2.capabilities.json +24 -0
- package/schemas/agent-bridge-v2.mcp.json +6 -0
- package/schemas/agent-bridge-v2.schema.json +163 -0
- package/sql/migrations/017_gateway_authority_binding.sql +99 -0
- package/sql/migrations/018_endpoint_migration_challenges.sql +1028 -0
- package/sql/migrations/019_endpoint_migration_same_successor.sql +324 -0
- package/dist/chunk-45BZGB77.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-UAHMP6GC.js.map +0 -1
- package/dist/chunk-WAI2IYIQ.js.map +0 -1
- /package/dist/{chunk-CEXBMOI5.js.map → chunk-LAHNQZDN.js.map} +0 -0
- /package/dist/{chunk-SPSMZOCJ.js.map → chunk-SYGONYMM.js.map} +0 -0
- /package/dist/{chunk-A6W4QALF.js.map → chunk-TC6A7GMF.js.map} +0 -0
package/dist/sqlite.d.ts
CHANGED
|
@@ -1,81 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
interface EdgeScope {
|
|
6
|
-
endpoint: string;
|
|
7
|
-
principal: BridgePrincipal;
|
|
8
|
-
}
|
|
9
|
-
interface EdgeOutboxRecord {
|
|
10
|
-
position: number;
|
|
11
|
-
draft: PendingMessage;
|
|
12
|
-
payloadHash: string;
|
|
13
|
-
attempts: number;
|
|
14
|
-
leaseToken: string;
|
|
15
|
-
createdAt: string;
|
|
16
|
-
}
|
|
17
|
-
interface EdgeEnqueueResult {
|
|
18
|
-
draft: PendingMessage;
|
|
19
|
-
created: boolean;
|
|
20
|
-
}
|
|
21
|
-
interface EdgeStats {
|
|
22
|
-
pending: number;
|
|
23
|
-
due: number;
|
|
24
|
-
scheduled: number;
|
|
25
|
-
leased: number;
|
|
26
|
-
blocked: number;
|
|
27
|
-
cached: number;
|
|
28
|
-
pullCursor?: string;
|
|
29
|
-
lastSyncAt?: string;
|
|
30
|
-
lastError?: string;
|
|
31
|
-
oldestDueAt?: string;
|
|
32
|
-
queueLagMs: number;
|
|
33
|
-
oldestBlockedAt?: string;
|
|
34
|
-
blockedAgeMs: number | null;
|
|
35
|
-
blockedMaxAttempts: number;
|
|
36
|
-
blockedLastError?: string;
|
|
37
|
-
nextRetryAt?: string;
|
|
38
|
-
lastOutboundSyncAt?: string;
|
|
39
|
-
lastInboundSyncAt?: string;
|
|
40
|
-
lastAttemptAt?: string;
|
|
41
|
-
}
|
|
42
|
-
declare class EdgeConflictError extends Error {
|
|
43
|
-
readonly code = "edge_idempotency_conflict";
|
|
44
|
-
}
|
|
45
|
-
declare function edgeMessageFingerprint(message: PendingMessage | BridgeMessage): string;
|
|
46
|
-
declare function stableIdempotency(message: PendingMessage): PendingMessage;
|
|
47
|
-
declare function edgeScopeKey(scope: EdgeScope): string;
|
|
48
|
-
declare class SQLiteEdgeStore {
|
|
49
|
-
private readonly path;
|
|
50
|
-
private readonly scope;
|
|
51
|
-
private readonly busyTimeoutMs;
|
|
52
|
-
private readonly db;
|
|
53
|
-
private readonly databasePath;
|
|
54
|
-
private readonly preexistingFiles;
|
|
55
|
-
private readonly key;
|
|
56
|
-
private initialized;
|
|
57
|
-
private initialization?;
|
|
58
|
-
constructor(path: string, scope: EdgeScope, busyTimeoutMs?: number);
|
|
59
|
-
private restrictFiles;
|
|
60
|
-
get scopeKey(): string;
|
|
61
|
-
initialize(): Promise<void>;
|
|
62
|
-
private initializeOnce;
|
|
63
|
-
private ready;
|
|
64
|
-
enqueue(input: PendingMessage, now?: Date): Promise<EdgeEnqueueResult>;
|
|
65
|
-
claimNext(now?: Date, leaseMs?: number): Promise<EdgeOutboxRecord | undefined>;
|
|
66
|
-
retry(record: EdgeOutboxRecord, error: string, availableAt: Date): Promise<void>;
|
|
67
|
-
block(record: EdgeOutboxRecord, error: string, now?: Date): Promise<void>;
|
|
68
|
-
commit(record: EdgeOutboxRecord, message: BridgeMessage, now?: Date): Promise<void>;
|
|
69
|
-
private cacheOne;
|
|
70
|
-
cachePage(messages: BridgeMessage[], cursor: string | undefined, now?: Date): Promise<void>;
|
|
71
|
-
cacheLatest(messages: BridgeMessage[], now?: Date): Promise<void>;
|
|
72
|
-
list(query?: MessageQuery): Promise<MessagePage>;
|
|
73
|
-
pullCursor(): Promise<string | undefined>;
|
|
74
|
-
noteError(error: string): Promise<void>;
|
|
75
|
-
noteAttempt(now?: Date): Promise<void>;
|
|
76
|
-
stats(now?: Date): Promise<EdgeStats>;
|
|
77
|
-
close(): Promise<void>;
|
|
78
|
-
}
|
|
1
|
+
import { c as BridgePrincipal, B as BridgeDelivery, g as DeliveryState, R as RetryPolicy, A as AgentPresence, b as BridgeMessage, a as BridgeDeliveryEvent } from './bridge-domain-CBX3IVqC.js';
|
|
2
|
+
import { M as MessagePage, B as BridgeDiagnostics, I as InsertMessageResult, a as BridgeStore, d as MessageQuery, C as ClaimOptions, c as DeliveryQuery, b as DeliveryPage, D as DeliveryEventPage } from './bridge-store-Boaj-ZbO.js';
|
|
3
|
+
import { e as EdgeMigrationGateState, S as SQLiteEdgeStore, a as EdgeDrainLease, P as PendingMessage } from './sqlite-edge-store-Bc15DvpX.js';
|
|
4
|
+
export { E as EdgeConflictError, b as EdgeEnqueueResult, c as EdgeMigrationDrainingError, d as EdgeMigrationGate, f as EdgeMigrationLeaseError, g as EdgeMigrationRetiredError, h as EdgeOutboxRecord, i as EdgeReadOnlyInspection, j as EdgeScope, k as EdgeStats, l as edgeMessageFingerprint, m as edgeScopeKey, n as inspectEdgeScopeReadOnly, s as stableIdempotency } from './sqlite-edge-store-Bc15DvpX.js';
|
|
79
5
|
|
|
80
6
|
interface SyncInsertResult extends InsertMessageResult {
|
|
81
7
|
disposition: "committed" | "queued";
|
|
@@ -100,6 +26,9 @@ interface SyncReport {
|
|
|
100
26
|
lastSyncedAt?: string;
|
|
101
27
|
lastError?: string;
|
|
102
28
|
failureRetryable?: boolean;
|
|
29
|
+
migrationState?: EdgeMigrationGateState;
|
|
30
|
+
migrationOperationId?: string;
|
|
31
|
+
migrationLeaseExpiresAt?: string;
|
|
103
32
|
}
|
|
104
33
|
interface SyncDiagnostics extends BridgeDiagnostics {
|
|
105
34
|
remoteReachable: boolean | null;
|
|
@@ -121,9 +50,12 @@ interface SyncDiagnostics extends BridgeDiagnostics {
|
|
|
121
50
|
lastOutboundSyncAt?: string;
|
|
122
51
|
lastInboundSyncAt?: string;
|
|
123
52
|
lastSyncAttemptAt?: string;
|
|
124
|
-
syncLoopState: "disabled" | "idle" | "running" | "backoff" | "stopped" | "failed";
|
|
53
|
+
syncLoopState: "disabled" | "idle" | "running" | "backoff" | "paused" | "stopped" | "failed";
|
|
125
54
|
syncLoopError?: string;
|
|
126
55
|
remoteError?: string;
|
|
56
|
+
migrationState: EdgeMigrationGateState;
|
|
57
|
+
migrationOperationId?: string;
|
|
58
|
+
migrationLeaseExpiresAt?: string;
|
|
127
59
|
}
|
|
128
60
|
interface SyncingBridgeStoreOptions {
|
|
129
61
|
now?: () => Date;
|
|
@@ -133,6 +65,9 @@ interface SyncingBridgeStoreOptions {
|
|
|
133
65
|
leaseMs?: number;
|
|
134
66
|
autoSync?: boolean;
|
|
135
67
|
idleDelayMs?: number;
|
|
68
|
+
edgeDrainLease?: EdgeDrainLease;
|
|
69
|
+
/** A maintenance caller may retain the edge while closing the sync wrapper. */
|
|
70
|
+
closeEdge?: boolean;
|
|
136
71
|
}
|
|
137
72
|
declare function isRetryableSyncError(error: unknown): boolean;
|
|
138
73
|
declare class SyncingBridgeStore implements BridgeStore {
|
|
@@ -146,6 +81,8 @@ declare class SyncingBridgeStore implements BridgeStore {
|
|
|
146
81
|
private readonly leaseMs;
|
|
147
82
|
private readonly autoSync;
|
|
148
83
|
private readonly idleDelayMs;
|
|
84
|
+
private edgeDrainLease?;
|
|
85
|
+
private readonly closeEdge;
|
|
149
86
|
private remoteReady;
|
|
150
87
|
private stopped;
|
|
151
88
|
private wakeTimer;
|
|
@@ -158,6 +95,8 @@ declare class SyncingBridgeStore implements BridgeStore {
|
|
|
158
95
|
private loopState;
|
|
159
96
|
constructor(edge: SQLiteEdgeStore, remote: BridgeStore, principal: BridgePrincipal, options?: SyncingBridgeStoreOptions);
|
|
160
97
|
initialize(): Promise<void>;
|
|
98
|
+
/** Replace a renewed maintenance lease without creating another edge wrapper. */
|
|
99
|
+
setDrainLease(lease: EdgeDrainLease | undefined): void;
|
|
161
100
|
private startTransportLoop;
|
|
162
101
|
private wait;
|
|
163
102
|
private wakeTransport;
|
|
@@ -235,4 +174,4 @@ declare class SQLiteBridgeStore implements BridgeStore {
|
|
|
235
174
|
close(): Promise<void>;
|
|
236
175
|
}
|
|
237
176
|
|
|
238
|
-
export { type CachedMessagePage,
|
|
177
|
+
export { type CachedMessagePage, EdgeDrainLease, EdgeMigrationGateState, PendingMessage, SQLiteBridgeStore, SQLiteEdgeStore, type SyncDiagnostics, type SyncInsertResult, type SyncReport, SyncingBridgeStore, type SyncingBridgeStoreOptions, isRetryableSyncError };
|
package/dist/sqlite.js
CHANGED
|
@@ -1,26 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
EdgeConflictError,
|
|
4
|
+
EdgeMigrationDrainingError,
|
|
5
|
+
EdgeMigrationLeaseError,
|
|
6
|
+
EdgeMigrationRetiredError,
|
|
4
7
|
SQLiteEdgeStore,
|
|
5
8
|
SyncingBridgeStore,
|
|
6
9
|
edgeMessageFingerprint,
|
|
7
10
|
edgeScopeKey,
|
|
11
|
+
inspectEdgeScopeReadOnly,
|
|
8
12
|
isRetryableSyncError,
|
|
9
13
|
stableIdempotency
|
|
10
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-SV3SVBYS.js";
|
|
11
15
|
import {
|
|
12
16
|
SQLiteBridgeStore
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-
|
|
17
|
+
} from "./chunk-DQOZJJZ2.js";
|
|
18
|
+
import "./chunk-GTK4KOED.js";
|
|
15
19
|
import "./chunk-VOQIDTCJ.js";
|
|
16
20
|
import "./chunk-BZLMF5O5.js";
|
|
17
21
|
export {
|
|
18
22
|
EdgeConflictError,
|
|
23
|
+
EdgeMigrationDrainingError,
|
|
24
|
+
EdgeMigrationLeaseError,
|
|
25
|
+
EdgeMigrationRetiredError,
|
|
19
26
|
SQLiteBridgeStore,
|
|
20
27
|
SQLiteEdgeStore,
|
|
21
28
|
SyncingBridgeStore,
|
|
22
29
|
edgeMessageFingerprint,
|
|
23
30
|
edgeScopeKey,
|
|
31
|
+
inspectEdgeScopeReadOnly,
|
|
24
32
|
isRetryableSyncError,
|
|
25
33
|
stableIdempotency
|
|
26
34
|
};
|
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,26 @@ 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
|
+
The repository Compose stack publishes the gateway and PostgreSQL on loopback for
|
|
44
|
+
development. Production deployments put PostgreSQL on a private network, terminate TLS
|
|
45
|
+
before every non-loopback gateway, and source credentials from the platform's secret
|
|
46
|
+
manager. A persistent volume is runtime storage, not a backup.
|
|
47
|
+
|
|
48
|
+
Upgrade order is database backup, migration, runtime bootstrap, readiness, then client
|
|
49
|
+
or gateway rollout. Because an older image may reject the newer migration plan, schema
|
|
50
|
+
rollback cannot rely on starting the previous container image. Operators apply a
|
|
51
|
+
forward fix or restore a verified native DR bundle into a fresh target before switching
|
|
52
|
+
authority. [The deployment guide](deployment.md) records the operational procedure.
|
|
53
|
+
|
|
34
54
|
### SQLite is local storage
|
|
35
55
|
|
|
36
56
|
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 +59,17 @@ The gateway outbox queues immutable message publication. Receipt and lease mutat
|
|
|
39
59
|
|
|
40
60
|
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
61
|
|
|
42
|
-
Local initialization must be idempotent.
|
|
62
|
+
Local initialization must be idempotent. Concurrent initialization and schema upgrade
|
|
63
|
+
use a 15-second minimum busy-retry window. Normal database operations retain the
|
|
64
|
+
configured timeout. The minimum supported SQLite build must include the WAL-reset
|
|
65
|
+
corruption fix. Node 22.23.1 on the current development machine embeds SQLite 3.51.3,
|
|
66
|
+
which includes that fix.
|
|
67
|
+
|
|
68
|
+
On Windows, an opening store verifies the private parent and main database before it
|
|
69
|
+
connects. It applies and verifies explicit ACLs on live WAL and shared-memory sidecars
|
|
70
|
+
after the connection establishes WAL and completes the serialized schema transaction.
|
|
71
|
+
This avoids first-open sidecar churn between concurrent processes. A sidecar that is
|
|
72
|
+
replaced during one ACL validation still fails closed.
|
|
43
73
|
|
|
44
74
|
### Portable archives cross storage engines without copying runtime state
|
|
45
75
|
|
|
@@ -123,10 +153,15 @@ PostgreSQL backup takes a repeatable-read exported snapshot while holding the na
|
|
|
123
153
|
advisory fence. `pg_dump` captures only the `agent_bridge` schema. A separate canonical
|
|
124
154
|
role inventory records the bounded role shells, membership options, and global or
|
|
125
155
|
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.
|
|
156
|
+
agent instances, rate-limit buckets, request authorities, endpoint-migration challenge
|
|
157
|
+
rows, and archive transaction authorizations. The manifest binds migration
|
|
158
|
+
inventory, table counts, claimed-delivery count, tool major, role inventory, and the
|
|
159
|
+
security, row-isolation, owner-control, and portable-archive readiness definitions.
|
|
160
|
+
|
|
161
|
+
The immutable gateway authority UUID is ordinary PostgreSQL data, so native backup and
|
|
162
|
+
restore preserve it. The UUID proves that a restored target represents the same logical
|
|
163
|
+
authority. It does not fence a live clone. Operators must still keep the source and
|
|
164
|
+
restored target from serving traffic at the same time.
|
|
130
165
|
|
|
131
166
|
PostgreSQL restore requires a dedicated fresh database, the same database name and
|
|
132
167
|
server major, an exact-major `pg_restore`, and superuser authority. It restores role
|
|
@@ -250,6 +285,189 @@ Protocol logic depends on a `BridgeStore` interface, not a provider SDK. The ini
|
|
|
250
285
|
|
|
251
286
|
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
287
|
|
|
288
|
+
Client lifecycle ownership is separate from registration existence. Local inspection
|
|
289
|
+
classifies a requested Codex, Claude Code, or Claude Desktop contract as absent,
|
|
290
|
+
unmanaged, managed, or drifted by comparing the exact identity, stable instance key,
|
|
291
|
+
backend path, scope, launch contract, and nonsecret registration locator. Desktop
|
|
292
|
+
locators name the normalized host config, Codex locators name the active profile
|
|
293
|
+
config, and Claude Code local or project locators include the invocation directory
|
|
294
|
+
because the native CLI exposes no stronger target. Adoption is plan-first and writes
|
|
295
|
+
only owner-private, credential-free metadata when `--apply` is explicit and the
|
|
296
|
+
unmanaged registration is exact. Backend files and their immediate parents must pass
|
|
297
|
+
the owner-only no-link policy. Codex JSON comparisons ignore diagnostic/status fields
|
|
298
|
+
and property order but require exactly the three Agent Bridge environment keys;
|
|
299
|
+
Claude Code connectivity status is not registration state. Claude Desktop inspection
|
|
300
|
+
shares the installer's validated absolute launch resolver. Applied adoption
|
|
301
|
+
re-inspects its postcondition. It does not read backend values or mutate the
|
|
302
|
+
registration.
|
|
303
|
+
Enrollment-based first-time provisioning retains strict registration and backend-file
|
|
304
|
+
collision refusal. See
|
|
305
|
+
[ADR 0004](decisions/0004-client-lifecycle-and-endpoint-migration.md).
|
|
306
|
+
|
|
307
|
+
Managed-client repair, update, and uninstall use a local crash-safe substrate. Strict
|
|
308
|
+
owner-private metadata is the mutation authority. Runtime plus stable instance locate
|
|
309
|
+
the metadata, and identity must match both the record and the immutable repair or
|
|
310
|
+
update request. Uninstall also binds the identity. Repair restores its launch contract. Update validates a new launch
|
|
311
|
+
before it creates the journal and stores only the identity, normalized command,
|
|
312
|
+
arguments, scope, and fixed Agent Bridge environment key names.
|
|
313
|
+
Caller backend, scope, and host-config flags are rejected. Owner-private revisioned
|
|
314
|
+
manifests live under
|
|
315
|
+
`~/.agent-bridge/operations/<uuid>` with private snapshots and an exclusive lock keyed
|
|
316
|
+
by runtime plus stable instance. Manifest publication fsyncs files, atomically renames,
|
|
317
|
+
verifies private paths, and fsyncs directories where supported. Snapshot publication
|
|
318
|
+
uses atomic no-replace links, so residue from an interrupted manifest update cannot be
|
|
319
|
+
overwritten. Sensitive file access pins its immediate directory. Operation-root and
|
|
320
|
+
target identities remain pinned throughout creation and destructive cleanup. The root
|
|
321
|
+
and locks identities remain pinned throughout stale recovery, and the artifact-directory
|
|
322
|
+
identity remains pinned across enumeration and every read. A typed credential-agnostic
|
|
323
|
+
request and immutable plan record non-sensitive locators, unique no-replace before and
|
|
324
|
+
after artifacts, and expected digests. Backend artifacts contain only a role, file
|
|
325
|
+
identity, and private or repairable policy state. They never contain backend bytes,
|
|
326
|
+
credentials, URLs, or workspace values. Begin is lock-covered and refuses another
|
|
327
|
+
unfinished operation; any blocked journal fences new mutation, including resume. Resume
|
|
328
|
+
is same-host only. The journal advances through prepared,
|
|
329
|
+
snapshotted, in-progress, applied, cleaning, and committed. Inspection separately
|
|
330
|
+
reports resumable, classification-required, blocked, or complete.
|
|
331
|
+
|
|
332
|
+
Repair and uninstall journals use operation format version 3. New update and reverse
|
|
333
|
+
rollback journals use v4. A v4 update request and terminal completion retain a bounded
|
|
334
|
+
credential-free inverse contract: prior managed metadata, prior exact registration
|
|
335
|
+
contract, and forward metadata and registration digests. Version 2 manifests retain
|
|
336
|
+
their released request interpretation and remain inspectable. A non-terminal version 2
|
|
337
|
+
record is blocked because it cannot prove the identity-bound request needed for these
|
|
338
|
+
public mutations.
|
|
339
|
+
|
|
340
|
+
Before a mutation, repair, update, and uninstall compare the full strict metadata record after
|
|
341
|
+
acquiring the lock. Each non-metadata step repeats that authority check. Registration
|
|
342
|
+
proofs include a bounded observation of the Agent Bridge entry and its full target
|
|
343
|
+
contract. Unknown environment keys, malformed values, unsafe arguments, an unexpected
|
|
344
|
+
scope, or an opaque registration shape stop before a journal is created.
|
|
345
|
+
|
|
346
|
+
Native repair and update remove the managed entry, prove absence, add the exact target,
|
|
347
|
+
and change metadata last when the launch changes. A native update command is one
|
|
348
|
+
executable contract. Bare commands cannot contain argument separators, while absolute
|
|
349
|
+
paths must resolve to executable files before an update is journaled. Codex runs get,
|
|
350
|
+
remove, and add with the recorded profile home over the supplied environment. Claude
|
|
351
|
+
Code uses its recorded scope, working directory, and supplied environment. Claude
|
|
352
|
+
Desktop reads its recorded config without following links or link ancestors, replaces
|
|
353
|
+
only `mcpServers.agent-bridge`, writes through an operation-scoped private temporary
|
|
354
|
+
file, and verifies the published entry. Node cannot make that update atomic with an
|
|
355
|
+
uncooperative same-user Desktop writer. The code pins and rechecks the file and parent
|
|
356
|
+
identities immediately before rename, but this is an advisory race boundary, not an OS
|
|
357
|
+
transaction guarantee. Windows accepts an already owner-private backend path and
|
|
358
|
+
refuses a non-private backend instead of claiming a safe permission tightening.
|
|
359
|
+
|
|
360
|
+
Uninstall removes a managed registration, proves its absence, deletes the private
|
|
361
|
+
backend file, then deletes the management metadata. It does not tighten an unsafe
|
|
362
|
+
backend before deletion. A backend that is not already private stops the plan before
|
|
363
|
+
it removes the registration. Uninstall never rolls a completed step back. Desktop
|
|
364
|
+
deletes only `mcpServers.agent-bridge` and preserves unrelated JSON. Before a backend
|
|
365
|
+
or metadata unlink, the code checks the private parent and file identities. POSIX
|
|
366
|
+
syncs that parent after deletion. Windows reports verified deletion with unavailable
|
|
367
|
+
directory durability. Node pathname deletion cannot make this atomic against an
|
|
368
|
+
uncooperative same-user writer, so that race remains advisory.
|
|
369
|
+
|
|
370
|
+
Rollback is an explicit update-only operation. `clients rollback` locates authority
|
|
371
|
+
only from a committed same-host v4 update record and requires the recorded identity as
|
|
372
|
+
an assertion. It rejects a changed forward registration or metadata state before it
|
|
373
|
+
creates a new reverse journal. Native rollback removes the forward registration,
|
|
374
|
+
proves absence, adds the prior registration, proves it, and writes prior metadata last.
|
|
375
|
+
Desktop replaces only `mcpServers.agent-bridge` and writes prior metadata last. The
|
|
376
|
+
reverse journal retains its source operation UUID and inverse contract so generic
|
|
377
|
+
resume needs no caller-provided backend, scope, command, runtime, or instance. A
|
|
378
|
+
failed forward update is never compensated automatically. Repair has no rollback, and
|
|
379
|
+
uninstall recovery requires re-enrollment.
|
|
380
|
+
|
|
381
|
+
Native Windows ACL checks start uncached for each acquired or resumed mutation lock.
|
|
382
|
+
While that lock remains held, later directory checks may reuse the result only for the
|
|
383
|
+
same directory path, device, and inode. File checks always use the native policy.
|
|
384
|
+
Passive inspection starts without a cache, and POSIX mode checks run on every access.
|
|
385
|
+
A same-user process can still change an ACL on an unchanged directory or replace it
|
|
386
|
+
with a directory that reuses the same filesystem identity. Local mode already treats
|
|
387
|
+
that OS user as trusted. Agent Bridge does not delete a cached directory during a
|
|
388
|
+
successful managed-client mutation.
|
|
389
|
+
|
|
390
|
+
Cleanup durably records per-artifact intent, verifies and unlinks the pinned artifact,
|
|
391
|
+
then records POSIX directory sync or explicit Windows unavailability. An absent file
|
|
392
|
+
is resumable only when intent predates the cleanup attempt. Inspection also recognizes
|
|
393
|
+
one verified after artifact left by an interrupted manifest publication. Other missing
|
|
394
|
+
or extra files block. Committed means verified writes and removed artifacts. Its
|
|
395
|
+
manifest drops requests, steps, locators, digests, and artifact metadata. A v4 update
|
|
396
|
+
completion retains only its inverse contract. A v5 stage and a v6 migration phase
|
|
397
|
+
retain bounded credential-free contracts. Other terminal completions retain the
|
|
398
|
+
operation kind, step count, completion time, and cleanup durability for audit. Uninstall
|
|
399
|
+
adds no rolled-back state. Physical erasure remains outside the filesystem contract.
|
|
400
|
+
|
|
401
|
+
`clients resume <operation-id>` accepts stored v3 repair, update, and uninstall
|
|
402
|
+
operations, supported v4 update and rollback operations, a committed v5 migration
|
|
403
|
+
stage, and v6 endpoint-migration operations, with optional same-host stale-lock
|
|
404
|
+
recovery. It derives authority from the immutable request. It rejects v2 records and
|
|
405
|
+
cannot take replacement caller authority. Generic resume finishes an uninstall
|
|
406
|
+
interrupted after metadata deletion and a reverse rollback interrupted after any
|
|
407
|
+
recorded boundary.
|
|
408
|
+
|
|
409
|
+
### Gateway client migration staging prepares a later cutover
|
|
410
|
+
|
|
411
|
+
`clients migrate stage <runtime>` creates a private successor backend and a
|
|
412
|
+
credential-free stage record. It leaves the managed registration, active backend, and
|
|
413
|
+
source edge gate state unchanged after initializing the gate schema. It requires a
|
|
414
|
+
rotation enrollment that keeps the predecessor valid through a future grace cutoff.
|
|
415
|
+
The stage record binds the managed
|
|
416
|
+
identity, source and target endpoint digests, both credential IDs, the source edge
|
|
417
|
+
database path and scope key, and the grace cutoff. It does not store either token or a
|
|
418
|
+
raw gateway URL.
|
|
419
|
+
|
|
420
|
+
The source backend must bind an absolute, normalized edge database file. Relative and
|
|
421
|
+
in-memory paths cannot identify the same durable outbox across harness working
|
|
422
|
+
directories, so staging rejects them.
|
|
423
|
+
|
|
424
|
+
An applied stage probes the active and staged bearer credentials, records the
|
|
425
|
+
enrollment credential IDs, initializes the recorded source edge database, and requires
|
|
426
|
+
its migration gate to be active. SQLite rejects a new outbox insert as soon as that
|
|
427
|
+
gate begins draining, including inserts from a client that had already opened the
|
|
428
|
+
database. These checks prepare a later drain and cutover. They do not authorize either
|
|
429
|
+
action.
|
|
430
|
+
|
|
431
|
+
The stage does not prove that source and target reach the same database authority. A
|
|
432
|
+
later endpoint cutover may use alternate URLs only after it dynamically attests one
|
|
433
|
+
logical authority. The authority identifier must survive clone and restore. Moving to
|
|
434
|
+
an independent database requires a separate owner-mediated fence and is outside this
|
|
435
|
+
staging command. A copied authority identifier does not prove that a live clone is
|
|
436
|
+
fenced.
|
|
437
|
+
|
|
438
|
+
### Endpoint cutover uses a separate v6 journal
|
|
439
|
+
|
|
440
|
+
`clients migrate cutover <stage-operation-id> --exclusive-edge --apply` accepts only a
|
|
441
|
+
committed v5 stage with its retained contract. It checks both live gateway status
|
|
442
|
+
responses under HTTP 2.1. Each response must bind the recorded workspace, principal,
|
|
443
|
+
and credential ID, and both must return the recorded immutable authority ID. The
|
|
444
|
+
successor must grant `status:read` and `messages:write`. The source issues a direct
|
|
445
|
+
predecessor-to-successor route challenge that the target consumes before the command
|
|
446
|
+
changes an edge gate or host registration.
|
|
447
|
+
|
|
448
|
+
The forward journal records full credential-free source and target metadata and
|
|
449
|
+
registration proofs, source and target edge paths, a normalized credential-free source
|
|
450
|
+
gateway URL, the v5 stage contract, and digest bindings for each proof. Once that
|
|
451
|
+
journal exists, every mutation uses a fresh same-successor route proof issued from the
|
|
452
|
+
retained source URL and consumed by the target. The source edge replays through the
|
|
453
|
+
target gateway with the successor credential. It creates a durable source drain lease.
|
|
454
|
+
The edge rejects new outbox inserts while draining. The lease worker must publish all
|
|
455
|
+
work and prove a zero outbox before a native remove or add, a Desktop replacement, or
|
|
456
|
+
the metadata-last switch. The command renews and reasserts the lease immediately before
|
|
457
|
+
each host write. It rechecks the live target authority before every mutation. Source
|
|
458
|
+
and target edge files must be distinct private regular files. The command rejects a
|
|
459
|
+
managed cohort that shares the source edge, including a hardlink alias.
|
|
460
|
+
`--exclusive-edge` covers unmanaged publishers that cannot be enumerated.
|
|
461
|
+
|
|
462
|
+
Finalization requires a fresh assertion after the grace cutoff. It verifies the
|
|
463
|
+
successor's live status binding and retires the retained source edge. Returning to a
|
|
464
|
+
former endpoint requires a new owner credential rotation and a separate forward
|
|
465
|
+
cutover. Cutover and finalization use exact durable before and after proofs for restart
|
|
466
|
+
classification. A replacement worker can resume a draining edge only after the prior
|
|
467
|
+
lease expires. Dry runs use read-only edge inspection. They do not initialize SQLite,
|
|
468
|
+
create sidecars, or contact gateways. A dry run refuses an edge with live WAL sidecars
|
|
469
|
+
because an immutable read could miss uncheckpointed outbox state.
|
|
470
|
+
|
|
253
471
|
### v1 compatibility is additive
|
|
254
472
|
|
|
255
473
|
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.
|
|
@@ -314,6 +532,33 @@ The stored definition is scoped to the control plane and its dependencies. It in
|
|
|
314
532
|
|
|
315
533
|
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
534
|
|
|
535
|
+
Migration 017 adds an immutable singleton authority UUID and a separate bound opener.
|
|
536
|
+
The released opener retains its original result shape. The bound opener returns the
|
|
537
|
+
same credential, workspace, principal, and scopes with the authority UUID in the same
|
|
538
|
+
request transaction. Runtime readiness checks the singleton row, mutation trigger,
|
|
539
|
+
function catalog, and direct table and column privileges. The UUID is returned only by
|
|
540
|
+
authenticated gateway status. It is not a capability field, log field, or readiness
|
|
541
|
+
field.
|
|
542
|
+
|
|
543
|
+
Migration 018 adds issue and consume operations for a gateway-only HTTP 2.1
|
|
544
|
+
endpoint-migration challenge. The issuer provides a lowercase 64-character hexadecimal
|
|
545
|
+
challenge, expected authority UUID, and direct active successor credential. The
|
|
546
|
+
successor consumes the same commitment with its active transaction-bound authority.
|
|
547
|
+
PostgreSQL hashes the challenge with a domain separator before persistence and retains
|
|
548
|
+
no raw challenge in records, events, or replies. Challenges expire within 60 seconds,
|
|
549
|
+
can be consumed once, and do not permit endpoint cutover. The migration 018
|
|
550
|
+
predecessor-to-successor relation alone does not prove that two distinct URLs share
|
|
551
|
+
live database state. The migration extends the credential-security catalog and records
|
|
552
|
+
endpoint and owner-control v5 attestations before readiness succeeds.
|
|
553
|
+
|
|
554
|
+
Migration 019 keeps the direct predecessor-to-successor challenge for initial
|
|
555
|
+
preflight and also permits one active successor credential to issue and consume a
|
|
556
|
+
challenge across the retained source and target URLs. It preserves the migration 018
|
|
557
|
+
challenge data, events, security boundaries, and native DR treatment. Endpoint v2 and
|
|
558
|
+
owner-control v6 attestations seal the updated catalog. Authenticated HTTP 2.1
|
|
559
|
+
capabilities report the credential's granted scopes so cutover can reject a successor
|
|
560
|
+
that lacks `status:read` or `messages:write` before it begins a journal.
|
|
561
|
+
|
|
317
562
|
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
563
|
|
|
319
564
|
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 +575,7 @@ Limits apply to content bytes, payload bytes, metadata depth, target count, batc
|
|
|
330
575
|
|
|
331
576
|
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
577
|
|
|
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.
|
|
578
|
+
`/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
579
|
|
|
335
580
|
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
581
|
|
|
@@ -346,10 +591,11 @@ source, a tag-to-version check, and npm provenance. The npm environment does not
|
|
|
346
591
|
currently require a human reviewer, so the release process must not claim a manual
|
|
347
592
|
approval gate.
|
|
348
593
|
|
|
349
|
-
GitHub immutable releases are enabled, and GitHub reports release `v0.3.
|
|
594
|
+
GitHub immutable releases are enabled, and GitHub reports release `v0.3.1` as
|
|
350
595
|
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.
|
|
596
|
+
actor. Recovery dispatches must run the workflow at the release tag ref so the
|
|
597
|
+
provenance workflow identity and checked-out source commit do not diverge. Release
|
|
598
|
+
packaging also waits for the exact commit to pass the full `test.yml` matrix on `main`.
|
|
353
599
|
|
|
354
600
|
## Acceptance checks
|
|
355
601
|
|