@cortexkit/subc-client 0.19.0 → 0.21.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/README.md +48 -1
- package/dist/client.d.ts +38 -11
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +140 -20
- package/dist/client.js.map +1 -1
- package/dist/connection-file.d.ts +6 -1
- package/dist/connection-file.d.ts.map +1 -1
- package/dist/connection-file.js +32 -10
- package/dist/connection-file.js.map +1 -1
- package/dist/envelope.d.ts +11 -1
- package/dist/envelope.d.ts.map +1 -1
- package/dist/envelope.js +23 -7
- package/dist/envelope.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/provider.d.ts +3 -0
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +17 -8
- package/dist/provider.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +164 -25
- package/src/connection-file.ts +37 -10
- package/src/envelope.ts +32 -13
- package/src/index.ts +2 -0
- package/src/provider.ts +19 -7
package/README.md
CHANGED
|
@@ -78,6 +78,45 @@ fails loud with an `AuthError` rather than connecting insecurely.
|
|
|
78
78
|
- **Connection-file security.** On unix the file must be owner-only (`0600`); a
|
|
79
79
|
group/world-readable file is rejected, because the key has effectively leaked.
|
|
80
80
|
|
|
81
|
+
### Scoped routes
|
|
82
|
+
|
|
83
|
+
Use a daemon advertising `scopes/v1` and pass a selector to `routeOpen()` or to
|
|
84
|
+
managed `call()` / `callBinary()` options:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const scope = {
|
|
88
|
+
owner: { kind: "reserved", module_id: "session-owner" } as const,
|
|
89
|
+
ref: "session-1",
|
|
90
|
+
scopeEpoch: 1,
|
|
91
|
+
};
|
|
92
|
+
const route = await client.routeOpen(target, identity, { scope });
|
|
93
|
+
await client.call("provider", "echo", {}, { scope });
|
|
94
|
+
// Close only this scoped managed route, not the unscoped or another scope's route.
|
|
95
|
+
await client.closeManagedRoute(target, identity, { scope });
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The daemon checks who the caller is from its own connection record, not from the request: the caller must be the module that registered the scope (its owner), or a module the owner listed as a carrier on that scope record.
|
|
99
|
+
Passing `scope` does not grant that authority: use the supervised caller's
|
|
100
|
+
`consumerIdentity` (or its default environment identity) as usual. `owner` uses
|
|
101
|
+
the existing `Principal` type; the reserved form above is the normal case, and
|
|
102
|
+
the daemon refuses other owner kinds.
|
|
103
|
+
|
|
104
|
+
The exact wire selector is
|
|
105
|
+
`scope: { owner: { kind: "reserved", module_id: "session-owner" }, ref: "session-1", scope_epoch: 1 }`.
|
|
106
|
+
With no selector, there is no `scope` key. A reserved owner's module id and the
|
|
107
|
+
ref must be non-empty strings, and `scopeEpoch` must be present and a safe
|
|
108
|
+
non-negative integer (zero is valid); invalid selectors fail locally before I/O.
|
|
109
|
+
|
|
110
|
+
Managed routes are cached separately by target, bind and consumer identities,
|
|
111
|
+
reverse capabilities, and the whole scope selector (owner, ref, epoch). Scoped
|
|
112
|
+
and unscoped calls never share a route, nor do different scopes or daemon
|
|
113
|
+
incarnations. Reconnect opens a fresh handle under the same selector.
|
|
114
|
+
`scope_not_synced` and `scope_changed` refusals retain the managed path's retries
|
|
115
|
+
within the caller's deadline. A scope-revocation close (such as `scope_ended` or
|
|
116
|
+
`scope_carrier_removed`) is terminal: neither a later managed call nor a
|
|
117
|
+
reconnect automatically reopens that cache entry. Use a new selector for a new
|
|
118
|
+
session; reload and restart close reasons still permit reopens.
|
|
119
|
+
|
|
81
120
|
### Launch nonce
|
|
82
121
|
|
|
83
122
|
A module the daemon spawns proves who it is with a launch nonce. On macOS and
|
|
@@ -115,7 +154,9 @@ the environment copy. The environment copy is read only when
|
|
|
115
154
|
## Testing
|
|
116
155
|
|
|
117
156
|
```sh
|
|
118
|
-
bun
|
|
157
|
+
bun run typecheck
|
|
158
|
+
bun test # unit/mock suite; live tests are gated by RUN_SUBC_LIVE=1
|
|
159
|
+
RUN_SUBC_LIVE=1 bun test tests/live-scoped-open.test.ts
|
|
119
160
|
```
|
|
120
161
|
|
|
121
162
|
The live-handshake tests boot the real daemon binary
|
|
@@ -124,6 +165,12 @@ byte-identity authority for this client. They skip automatically when the binary
|
|
|
124
165
|
is not built; run `cargo build -p subc-core` first (the CI lane does this; the
|
|
125
166
|
package builds the `ck-subc` executable).
|
|
126
167
|
|
|
168
|
+
The scope admission test uses the same supervised `fake-aft-stub` fixture as the
|
|
169
|
+
Rust SDK (built by `cargo build -p subc-core --bins`) to sync an owner's scope and
|
|
170
|
+
observe the real provider bind. Live helpers hard-link the daemon as
|
|
171
|
+
`ckdev-subc` inside the scratch runtime directory before spawning it, and isolate
|
|
172
|
+
all three XDG homes so tests cannot reach the operator's daemon data or config.
|
|
173
|
+
|
|
127
174
|
## Layout
|
|
128
175
|
|
|
129
176
|
| File | Responsibility |
|
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ConnectionInfo } from "./connection-file.js";
|
|
2
2
|
import { AdmissionClass, Priority } from "./envelope.js";
|
|
3
|
+
import type { Principal } from "./provider.js";
|
|
3
4
|
import { ReverseRequestRegistry, RouteHandle } from "./route-handle.js";
|
|
4
5
|
export declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
5
6
|
export declare const TIMEOUT_ARBITRATION_GRACE_MS = 50;
|
|
@@ -29,7 +30,18 @@ export interface ConsumerIdentity {
|
|
|
29
30
|
module_id: string;
|
|
30
31
|
launch_nonce: string;
|
|
31
32
|
}
|
|
33
|
+
/** A scope selector; scopeEpoch is required even for the scope's owner. */
|
|
34
|
+
export interface RouteScope {
|
|
35
|
+
/** Owner principal, passed verbatim. The daemon decides which principal kinds may own scopes. */
|
|
36
|
+
owner: Principal;
|
|
37
|
+
/** Opaque, non-empty ref within the owner. */
|
|
38
|
+
ref: string;
|
|
39
|
+
/** Safe non-negative integer, including zero; serialized as scope_epoch. */
|
|
40
|
+
scopeEpoch: number;
|
|
41
|
+
}
|
|
32
42
|
export interface RouteOpenOptions {
|
|
43
|
+
/** Open under this scope. The caller must be its owner or a listed carrier; use a daemon advertising scopes/v1. Serialized as { owner, ref, scope_epoch }. */
|
|
44
|
+
scope?: RouteScope;
|
|
33
45
|
/** Optional override for the consumer identity; by default the SUBC_MODULE_ID environment variable and the process's launch nonce (see `launchNonce`) are used when both are non-empty; a refused launch-nonce descriptor counts as no nonce. Set null to send route.open without consumer_identity. */
|
|
34
46
|
consumerIdentity?: ConsumerIdentity | null;
|
|
35
47
|
/**
|
|
@@ -113,6 +125,8 @@ export interface RequestOptions {
|
|
|
113
125
|
signal?: AbortSignal;
|
|
114
126
|
}
|
|
115
127
|
export interface ManagedCallOptions extends RequestOptions {
|
|
128
|
+
/** Scope for the cached route and its reconnect reopens. The caller must be the scope's owner or a listed carrier; use a daemon advertising scopes/v1. Each owner/ref/epoch has a separate cache entry. */
|
|
129
|
+
scope?: RouteScope;
|
|
116
130
|
/** Overrides the per-client identity used for route.open before this call. */
|
|
117
131
|
identity?: BindIdentity;
|
|
118
132
|
/** Defaults to management_surface, matching the store/host management APIs. */
|
|
@@ -142,6 +156,8 @@ export interface CloseRouteOptions {
|
|
|
142
156
|
drain?: boolean;
|
|
143
157
|
}
|
|
144
158
|
export interface ManagedCloseRouteOptions extends CloseRouteOptions {
|
|
159
|
+
/** Scope selector used by the cached managed route; omitted selects only an unscoped route. */
|
|
160
|
+
scope?: RouteScope;
|
|
145
161
|
/** Consumer identity used by the cached managed route. */
|
|
146
162
|
consumerIdentity?: ConsumerIdentity | null;
|
|
147
163
|
/** The registry whose derived capability set identifies the cached route. */
|
|
@@ -187,6 +203,8 @@ export declare class SubcCallError extends Error {
|
|
|
187
203
|
* classify on it and must not have to reach into `cause`.
|
|
188
204
|
*/
|
|
189
205
|
get detail(): unknown;
|
|
206
|
+
/** Why the route ended. A reason never makes an in-flight call safe to resend. */
|
|
207
|
+
get closeReason(): RouteEndReason | undefined;
|
|
190
208
|
}
|
|
191
209
|
/**
|
|
192
210
|
* A live subscription to a provider's event stream, riding a single held-open
|
|
@@ -210,6 +228,7 @@ export declare class SubcError extends Error {
|
|
|
210
228
|
* those reasons at the transport boundary.
|
|
211
229
|
*/
|
|
212
230
|
readonly detail?: unknown | undefined;
|
|
231
|
+
readonly closeReason?: RouteEndReason | undefined;
|
|
213
232
|
constructor(message: string, code?: string | undefined,
|
|
214
233
|
/**
|
|
215
234
|
* Machine-parsable payload from the wire `ErrorBody.detail` field, carried
|
|
@@ -217,7 +236,7 @@ export declare class SubcError extends Error {
|
|
|
217
236
|
* synapse's certification-refusal reasons); dropping it here would strand
|
|
218
237
|
* those reasons at the transport boundary.
|
|
219
238
|
*/
|
|
220
|
-
detail?: unknown | undefined);
|
|
239
|
+
detail?: unknown | undefined, closeReason?: RouteEndReason | undefined);
|
|
221
240
|
}
|
|
222
241
|
export interface ConnectOptions {
|
|
223
242
|
connectionFile: string;
|
|
@@ -266,15 +285,11 @@ export interface ConnectOptions {
|
|
|
266
285
|
*/
|
|
267
286
|
timeoutArbitrationGraceMs?: number;
|
|
268
287
|
/**
|
|
269
|
-
* Observer for daemon
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
* what they don't know, per the MUST-ignore clause), a push that fails to
|
|
275
|
-
* parse as JSON is dropped without surfacing, and an observer throw is
|
|
276
|
-
* swallowed like every other caller callback so it cannot fail the read loop
|
|
277
|
-
* or unrelated requests.
|
|
288
|
+
* Observer for parsed daemon control pushes, including unrecognized ops which
|
|
289
|
+
* callers should ignore. Malformed JSON is dropped and observer exceptions are
|
|
290
|
+
* swallowed so they cannot fail unrelated requests. The client records route
|
|
291
|
+
* reasons before notifying the observer, but closes routes only on the separate
|
|
292
|
+
* GOODBYE frame, never on a control push.
|
|
278
293
|
*/
|
|
279
294
|
onControlPush?: (push: ControlPush) => void;
|
|
280
295
|
}
|
|
@@ -286,9 +301,11 @@ export interface ControlPush {
|
|
|
286
301
|
body: Record<string, unknown>;
|
|
287
302
|
}
|
|
288
303
|
/** A known route-close reason, excluding forward-compatible unknown values. */
|
|
289
|
-
export type KnownRouteCloseReason = "reload" | "restart" | "disable" | "crash" | "capability_denied";
|
|
304
|
+
export type KnownRouteCloseReason = "reload" | "restart" | "disable" | "crash" | "capability_denied" | "scope_ended" | "scope_carrier_removed" | "scope_delegation_changed" | "scope_parent_ended";
|
|
290
305
|
/** A route-close reason decoded from a daemon control push. */
|
|
291
306
|
export type RouteCloseReason = KnownRouteCloseReason | "unknown";
|
|
307
|
+
/** Why a route ended. Even a planned closure can leave a call's outcome unknown. */
|
|
308
|
+
export type RouteEndReason = RouteCloseReason | "closed_by_caller" | "connection_lost";
|
|
292
309
|
/** Whether the close reason alone permits reopening a route automatically. */
|
|
293
310
|
export type RouteCloseDisposition = "may_reopen" | "must_not_reopen";
|
|
294
311
|
/**
|
|
@@ -311,6 +328,9 @@ export declare class SubcClient {
|
|
|
311
328
|
private readonly lateResponses;
|
|
312
329
|
private readonly routes;
|
|
313
330
|
private readonly liveRoutes;
|
|
331
|
+
private readonly routeModules;
|
|
332
|
+
private readonly routeEndReasons;
|
|
333
|
+
private readonly legacyChannelReasons;
|
|
314
334
|
private connectionToken;
|
|
315
335
|
private ingressEpochDropCount;
|
|
316
336
|
private closedErr;
|
|
@@ -339,6 +359,7 @@ export declare class SubcClient {
|
|
|
339
359
|
* Open a route and return its connection-bound immutable handle. At most
|
|
340
360
|
* MAX_ROUTE_OPENS_IN_FLIGHT opens are outstanding at once; further calls wait
|
|
341
361
|
* their turn.
|
|
362
|
+
* A scoped open requires the caller to be the scope's owner or a listed carrier.
|
|
342
363
|
*/
|
|
343
364
|
routeOpen(target: RouteTarget, identity: BindIdentity, opts?: RouteOpenOptions): Promise<RouteHandle>;
|
|
344
365
|
/** One route.open exchange. The caller holds a route-open gate slot. */
|
|
@@ -348,6 +369,8 @@ export declare class SubcClient {
|
|
|
348
369
|
/**
|
|
349
370
|
* Managed route + request convenience. Opens and caches a route for the module,
|
|
350
371
|
* reconnecting and re-opening cached routes after connection drops.
|
|
372
|
+
* Scoped routes are isolated by owner/ref/epoch and require owner or carrier authority.
|
|
373
|
+
* Terminal scope close reasons prevent automatic reopens under the same selector.
|
|
351
374
|
*/
|
|
352
375
|
call<Response = unknown>(moduleId: string, method: string, params?: unknown, opts?: ManagedCallOptions): Promise<Response>;
|
|
353
376
|
/**
|
|
@@ -460,6 +483,7 @@ export declare class SubcClient {
|
|
|
460
483
|
private reopenCachedRoutes;
|
|
461
484
|
private timeoutMessage;
|
|
462
485
|
private routeClosedDuringOpen;
|
|
486
|
+
private endedCachedRoute;
|
|
463
487
|
private readLoop;
|
|
464
488
|
private dispatch;
|
|
465
489
|
/**
|
|
@@ -478,6 +502,9 @@ export declare class SubcClient {
|
|
|
478
502
|
private sendReverseReply;
|
|
479
503
|
private errorFromFrame;
|
|
480
504
|
private evictRouteHandle;
|
|
505
|
+
private recordRouteEnd;
|
|
506
|
+
private preventRouteReopen;
|
|
507
|
+
private routeEndReason;
|
|
481
508
|
private failHandle;
|
|
482
509
|
private fail;
|
|
483
510
|
private notSentCallError;
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
CHANGED
|
@@ -107,10 +107,16 @@ export class SubcCallError extends Error {
|
|
|
107
107
|
get detail() {
|
|
108
108
|
return this.cause instanceof SubcError ? this.cause.detail : undefined;
|
|
109
109
|
}
|
|
110
|
+
/** Why the route ended. A reason never makes an in-flight call safe to resend. */
|
|
111
|
+
get closeReason() {
|
|
112
|
+
return this.cause instanceof SubcError || this.cause instanceof SubcCallError
|
|
113
|
+
? this.cause.closeReason : undefined;
|
|
114
|
+
}
|
|
110
115
|
}
|
|
111
116
|
export class SubcError extends Error {
|
|
112
117
|
code;
|
|
113
118
|
detail;
|
|
119
|
+
closeReason;
|
|
114
120
|
constructor(message, code,
|
|
115
121
|
/**
|
|
116
122
|
* Machine-parsable payload from the wire `ErrorBody.detail` field, carried
|
|
@@ -118,10 +124,11 @@ export class SubcError extends Error {
|
|
|
118
124
|
* synapse's certification-refusal reasons); dropping it here would strand
|
|
119
125
|
* those reasons at the transport boundary.
|
|
120
126
|
*/
|
|
121
|
-
detail) {
|
|
127
|
+
detail, closeReason) {
|
|
122
128
|
super(message);
|
|
123
129
|
this.code = code;
|
|
124
130
|
this.detail = detail;
|
|
131
|
+
this.closeReason = closeReason;
|
|
125
132
|
}
|
|
126
133
|
}
|
|
127
134
|
// The wire flags belong to the frame, not the public error API. Keep them with
|
|
@@ -146,6 +153,10 @@ export function parseRouteCloseReason(reason) {
|
|
|
146
153
|
case "disable":
|
|
147
154
|
case "crash":
|
|
148
155
|
case "capability_denied":
|
|
156
|
+
case "scope_ended":
|
|
157
|
+
case "scope_carrier_removed":
|
|
158
|
+
case "scope_delegation_changed":
|
|
159
|
+
case "scope_parent_ended":
|
|
149
160
|
return reason;
|
|
150
161
|
default:
|
|
151
162
|
return "unknown";
|
|
@@ -160,6 +171,10 @@ export function classifyRouteCloseReason(reason) {
|
|
|
160
171
|
case "disable":
|
|
161
172
|
case "crash":
|
|
162
173
|
case "capability_denied":
|
|
174
|
+
case "scope_ended":
|
|
175
|
+
case "scope_carrier_removed":
|
|
176
|
+
case "scope_delegation_changed":
|
|
177
|
+
case "scope_parent_ended":
|
|
163
178
|
case "unknown":
|
|
164
179
|
return "must_not_reopen";
|
|
165
180
|
}
|
|
@@ -258,6 +273,9 @@ export class SubcClient {
|
|
|
258
273
|
lateResponses = new Map();
|
|
259
274
|
routes = new Map();
|
|
260
275
|
liveRoutes = new Map();
|
|
276
|
+
routeModules = new Map();
|
|
277
|
+
routeEndReasons = new Map();
|
|
278
|
+
legacyChannelReasons = new Map();
|
|
261
279
|
connectionToken = newConnectionToken();
|
|
262
280
|
ingressEpochDropCount = 0;
|
|
263
281
|
closedErr = null;
|
|
@@ -328,12 +346,14 @@ export class SubcClient {
|
|
|
328
346
|
* Open a route and return its connection-bound immutable handle. At most
|
|
329
347
|
* MAX_ROUTE_OPENS_IN_FLIGHT opens are outstanding at once; further calls wait
|
|
330
348
|
* their turn.
|
|
349
|
+
* A scoped open requires the caller to be the scope's owner or a listed carrier.
|
|
331
350
|
*/
|
|
332
351
|
async routeOpen(target, identity, opts = {}) {
|
|
352
|
+
const scope = validatedRouteScope(opts.scope);
|
|
333
353
|
// Without a timeout the gate always grants a slot eventually.
|
|
334
354
|
const release = (await this.routeOpenGate.acquire());
|
|
335
355
|
try {
|
|
336
|
-
return await this.sendRouteOpen(target, identity, opts);
|
|
356
|
+
return await this.sendRouteOpen(target, identity, { ...opts, scope });
|
|
337
357
|
}
|
|
338
358
|
finally {
|
|
339
359
|
release();
|
|
@@ -341,6 +361,7 @@ export class SubcClient {
|
|
|
341
361
|
}
|
|
342
362
|
/** One route.open exchange. The caller holds a route-open gate slot. */
|
|
343
363
|
async sendRouteOpen(target, identity, opts = {}) {
|
|
364
|
+
const scope = validatedRouteScope(opts.scope);
|
|
344
365
|
const consumerIdentity = routeOpenConsumerIdentity(opts);
|
|
345
366
|
const reverseRequests = opts.reverseRequests ?? new ReverseRequestRegistry();
|
|
346
367
|
reverseRequests.seal();
|
|
@@ -351,6 +372,7 @@ export class SubcClient {
|
|
|
351
372
|
identity,
|
|
352
373
|
...(consumerIdentity ? { consumer_identity: consumerIdentity } : {}),
|
|
353
374
|
...(consumerCapabilities.length > 0 ? { consumer_capabilities: consumerCapabilities } : {}),
|
|
375
|
+
...(scope ? { scope: { owner: scope.owner, ref: scope.ref, scope_epoch: scope.scopeEpoch } } : {}),
|
|
354
376
|
});
|
|
355
377
|
let installed = null;
|
|
356
378
|
const install = (frame) => {
|
|
@@ -361,6 +383,7 @@ export class SubcClient {
|
|
|
361
383
|
throw new SubcError(`route.open returned no route handle: ${JSON.stringify(parsed)}`);
|
|
362
384
|
}
|
|
363
385
|
installed = this.installRoute(parsed.route_channel, parsed.route_epoch, reverseRequests);
|
|
386
|
+
this.routeModules.set(parsed.route_channel, target.module_id);
|
|
364
387
|
return true;
|
|
365
388
|
};
|
|
366
389
|
const closeLateRoute = (frame) => {
|
|
@@ -371,7 +394,7 @@ export class SubcClient {
|
|
|
371
394
|
if (typeof parsed.route_channel !== "number" || typeof parsed.route_epoch !== "number")
|
|
372
395
|
return;
|
|
373
396
|
const lateHandle = this.installRoute(parsed.route_channel, parsed.route_epoch);
|
|
374
|
-
this.failHandle(lateHandle, new SubcError("late route.open was closed", "route_closed"));
|
|
397
|
+
this.failHandle(lateHandle, new SubcError("late route.open was closed", "route_closed", undefined, "closed_by_caller"));
|
|
375
398
|
this.liveRoutes.delete(lateHandle.channel);
|
|
376
399
|
this.sendRouteGoodbye(lateHandle, true);
|
|
377
400
|
}
|
|
@@ -397,6 +420,8 @@ export class SubcClient {
|
|
|
397
420
|
/**
|
|
398
421
|
* Managed route + request convenience. Opens and caches a route for the module,
|
|
399
422
|
* reconnecting and re-opening cached routes after connection drops.
|
|
423
|
+
* Scoped routes are isolated by owner/ref/epoch and require owner or carrier authority.
|
|
424
|
+
* Terminal scope close reasons prevent automatic reopens under the same selector.
|
|
400
425
|
*/
|
|
401
426
|
async call(moduleId, method, params, opts = {}) {
|
|
402
427
|
if (opts.binary) {
|
|
@@ -611,14 +636,14 @@ export class SubcClient {
|
|
|
611
636
|
}
|
|
612
637
|
if (opts.drain)
|
|
613
638
|
await this.drainUnaryOnHandle(handle);
|
|
614
|
-
this.failHandle(handle, new SubcError("route closed by closeRoute", "route_closed"));
|
|
639
|
+
this.failHandle(handle, new SubcError("route closed by closeRoute", "route_closed", undefined, "closed_by_caller"));
|
|
615
640
|
if (this.liveRoutes.get(handle.channel) === handle)
|
|
616
641
|
this.liveRoutes.delete(handle.channel);
|
|
617
642
|
this.sendRouteGoodbye(handle);
|
|
618
643
|
}
|
|
619
644
|
/** Close a cached managed route by its route-open identity tuple. */
|
|
620
645
|
async closeManagedRoute(target, identity, opts = {}) {
|
|
621
|
-
const key = routeCacheKey(target, identity, routeOpenConsumerIdentity(opts), (opts.reverseRequests ?? new ReverseRequestRegistry()).capabilities());
|
|
646
|
+
const key = routeCacheKey(target, identity, routeOpenConsumerIdentity(opts), (opts.reverseRequests ?? new ReverseRequestRegistry()).capabilities(), validatedRouteScope(opts.scope));
|
|
622
647
|
const cached = this.routes.get(key);
|
|
623
648
|
if (!cached)
|
|
624
649
|
return;
|
|
@@ -715,13 +740,15 @@ export class SubcClient {
|
|
|
715
740
|
};
|
|
716
741
|
pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, channel, corr, ms), ms);
|
|
717
742
|
this.pending.set(key, pending);
|
|
718
|
-
if (signal)
|
|
719
|
-
this.attachCancelSignal(signal, key, handle, corr, priority);
|
|
720
743
|
writeBorrowed(this.sock, encodeFrame(frame), Date.now() + ms).catch((error) => {
|
|
721
744
|
const current = this.pending.get(key);
|
|
722
745
|
if (current)
|
|
723
746
|
this.rejectPending(key, current, error instanceof Error ? error : new SubcError(String(error)));
|
|
724
747
|
});
|
|
748
|
+
// Queue REQUEST first: an already-aborted signal emits CANCEL immediately,
|
|
749
|
+
// and the daemon can only cancel a correlation it has already received.
|
|
750
|
+
if (signal)
|
|
751
|
+
this.attachCancelSignal(signal, key, handle, corr, priority);
|
|
725
752
|
});
|
|
726
753
|
}
|
|
727
754
|
/**
|
|
@@ -813,8 +840,6 @@ export class SubcClient {
|
|
|
813
840
|
};
|
|
814
841
|
pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, handle.channel, corr, ms), ms);
|
|
815
842
|
this.pending.set(key, pending);
|
|
816
|
-
if (signal)
|
|
817
|
-
this.attachCancelSignal(signal, key, handle, corr, priority);
|
|
818
843
|
const write = writeTrackedBorrowed(this.sock, encodeFrame(frame), Date.now() + ms);
|
|
819
844
|
handedToSocket = write.queued;
|
|
820
845
|
write.completed.catch((error) => {
|
|
@@ -822,9 +847,13 @@ export class SubcClient {
|
|
|
822
847
|
if (current)
|
|
823
848
|
this.rejectPending(key, current, error instanceof Error ? error : new SubcError(String(error)));
|
|
824
849
|
});
|
|
850
|
+
// Preserve REQUEST/CANCEL order even when the signal was already aborted.
|
|
851
|
+
if (signal && write.queued)
|
|
852
|
+
this.attachCancelSignal(signal, key, handle, corr, priority);
|
|
825
853
|
});
|
|
826
854
|
}
|
|
827
855
|
async cachedRouteHandle(moduleId, opts) {
|
|
856
|
+
const scope = validatedRouteScope(opts.scope);
|
|
828
857
|
const identity = opts.identity ?? this.opts.identity;
|
|
829
858
|
if (!identity) {
|
|
830
859
|
throw new SubcCallError("terminal", "managed call requires a BindIdentity in SubcClient.connect({ identity }) or call(..., { identity })", "missing_identity");
|
|
@@ -833,7 +862,7 @@ export class SubcClient {
|
|
|
833
862
|
const consumerIdentity = routeOpenConsumerIdentity(opts);
|
|
834
863
|
const reverseRequests = opts.reverseRequests ?? new ReverseRequestRegistry();
|
|
835
864
|
reverseRequests.seal();
|
|
836
|
-
const key = routeCacheKey(target, identity, consumerIdentity, reverseRequests.capabilities());
|
|
865
|
+
const key = routeCacheKey(target, identity, consumerIdentity, reverseRequests.capabilities(), scope);
|
|
837
866
|
let cached = this.routes.get(key);
|
|
838
867
|
if (!cached) {
|
|
839
868
|
cached = {
|
|
@@ -841,6 +870,7 @@ export class SubcClient {
|
|
|
841
870
|
moduleId,
|
|
842
871
|
target,
|
|
843
872
|
identity,
|
|
873
|
+
scope,
|
|
844
874
|
consumerIdentity,
|
|
845
875
|
reverseRequests,
|
|
846
876
|
handle: null,
|
|
@@ -848,6 +878,8 @@ export class SubcClient {
|
|
|
848
878
|
};
|
|
849
879
|
this.routes.set(key, cached);
|
|
850
880
|
}
|
|
881
|
+
if (cached.endReason !== undefined)
|
|
882
|
+
throw this.endedCachedRoute(cached.endReason);
|
|
851
883
|
if (cached.handle && this.isLiveHandle(cached.handle))
|
|
852
884
|
return cached.handle;
|
|
853
885
|
// The retries end at the retry deadline or at the call's own timeoutMs,
|
|
@@ -888,6 +920,8 @@ export class SubcClient {
|
|
|
888
920
|
for (;;) {
|
|
889
921
|
if (cached.closed)
|
|
890
922
|
throw this.routeClosedDuringOpen();
|
|
923
|
+
if (cached.endReason !== undefined)
|
|
924
|
+
throw this.endedCachedRoute(cached.endReason);
|
|
891
925
|
try {
|
|
892
926
|
await this.ensureConnectedForManaged();
|
|
893
927
|
}
|
|
@@ -904,6 +938,7 @@ export class SubcClient {
|
|
|
904
938
|
try {
|
|
905
939
|
outcome = {
|
|
906
940
|
handle: await this.sendRouteOpen(cached.target, cached.identity, {
|
|
941
|
+
scope: cached.scope,
|
|
907
942
|
consumerIdentity: cached.consumerIdentity ?? null,
|
|
908
943
|
reverseRequests: cached.reverseRequests,
|
|
909
944
|
}),
|
|
@@ -1149,10 +1184,11 @@ export class SubcClient {
|
|
|
1149
1184
|
}
|
|
1150
1185
|
for (const key of routeKeys) {
|
|
1151
1186
|
const cached = this.routes.get(key);
|
|
1152
|
-
if (!cached || cached.closed)
|
|
1187
|
+
if (!cached || cached.closed || cached.endReason !== undefined)
|
|
1153
1188
|
continue;
|
|
1154
1189
|
try {
|
|
1155
1190
|
const handle = await this.routeOpen(cached.target, cached.identity, {
|
|
1191
|
+
scope: cached.scope,
|
|
1156
1192
|
consumerIdentity: cached.consumerIdentity ?? null,
|
|
1157
1193
|
reverseRequests: cached.reverseRequests,
|
|
1158
1194
|
});
|
|
@@ -1182,7 +1218,12 @@ export class SubcClient {
|
|
|
1182
1218
|
return `request on ${where} ${channel} corr ${corr} timed out after ${ms}ms`;
|
|
1183
1219
|
}
|
|
1184
1220
|
routeClosedDuringOpen() {
|
|
1185
|
-
|
|
1221
|
+
const cause = new SubcError("route was closed before route.open completed", "route_closed", undefined, "closed_by_caller");
|
|
1222
|
+
return new SubcCallError("not_sent", cause.message, cause.code, cause);
|
|
1223
|
+
}
|
|
1224
|
+
endedCachedRoute(reason) {
|
|
1225
|
+
const cause = new SubcError(`cached route closed by subc (${reason}); must not reopen`, "route_closed", undefined, reason);
|
|
1226
|
+
return this.terminalCallError(cause.message, cause);
|
|
1186
1227
|
}
|
|
1187
1228
|
async readLoop(sock, generation) {
|
|
1188
1229
|
try {
|
|
@@ -1222,12 +1263,12 @@ export class SubcClient {
|
|
|
1222
1263
|
if (frame.header.channel === 0 && frame.header.ty === FrameType.Push) {
|
|
1223
1264
|
// Daemon-originated control push (route.closing / route.closed / future
|
|
1224
1265
|
// ops). Never matches a pending (corr is daemon-chosen), never an error
|
|
1225
|
-
// path: unparseable bodies
|
|
1226
|
-
//
|
|
1266
|
+
// path: unparseable bodies drop silently. Reasons are recorded even without
|
|
1267
|
+
// an observer, and an observer throw is
|
|
1227
1268
|
// swallowed for the same reason as onProgress below -- a caller callback
|
|
1228
1269
|
// must not fail the read loop.
|
|
1229
1270
|
const observer = this.opts.onControlPush;
|
|
1230
|
-
|
|
1271
|
+
{
|
|
1231
1272
|
let parsed = null;
|
|
1232
1273
|
try {
|
|
1233
1274
|
const body = this.parseJson(frame);
|
|
@@ -1238,8 +1279,9 @@ export class SubcClient {
|
|
|
1238
1279
|
// Unparseable control push: ignored by contract.
|
|
1239
1280
|
}
|
|
1240
1281
|
if (parsed) {
|
|
1282
|
+
this.recordRouteEnd(parsed);
|
|
1241
1283
|
try {
|
|
1242
|
-
observer(parsed);
|
|
1284
|
+
observer?.(parsed);
|
|
1243
1285
|
}
|
|
1244
1286
|
catch {
|
|
1245
1287
|
// Observer's own throw, on its own stack; the stream must survive.
|
|
@@ -1320,7 +1362,8 @@ export class SubcClient {
|
|
|
1320
1362
|
// so it stays kind=outcome_unknown -- the same class as a mid-flight socket
|
|
1321
1363
|
// drop, and never the not_sent/unknown_channel class that call() retries.
|
|
1322
1364
|
// The code says WHICH route ended, not that it is safe to send again.
|
|
1323
|
-
this.failHandle(handle, new SubcError("route closed by subc (GOODBYE)", "route_closed"));
|
|
1365
|
+
this.failHandle(handle, new SubcError("route closed by subc (GOODBYE)", "route_closed", undefined, this.routeEndReason(handle.channel)));
|
|
1366
|
+
this.preventRouteReopen(handle.channel, this.routeEndReason(handle.channel));
|
|
1324
1367
|
if (this.liveRoutes.get(handle.channel) === handle)
|
|
1325
1368
|
this.liveRoutes.delete(handle.channel);
|
|
1326
1369
|
this.evictRouteHandle(handle);
|
|
@@ -1431,6 +1474,48 @@ export class SubcClient {
|
|
|
1431
1474
|
cached.handle = null;
|
|
1432
1475
|
}
|
|
1433
1476
|
}
|
|
1477
|
+
recordRouteEnd(push) {
|
|
1478
|
+
const reason = routeCloseReason(push);
|
|
1479
|
+
if (reason === undefined)
|
|
1480
|
+
return;
|
|
1481
|
+
if ("channels" in push.body) {
|
|
1482
|
+
if (Array.isArray(push.body.channels)) {
|
|
1483
|
+
for (const channel of push.body.channels) {
|
|
1484
|
+
if (typeof channel !== "number" || !Number.isInteger(channel) || channel < 0 || channel > 65535)
|
|
1485
|
+
continue;
|
|
1486
|
+
const final = push.op === "route.closed";
|
|
1487
|
+
if (final || !this.routeEndReasons.get(channel)?.final) {
|
|
1488
|
+
this.routeEndReasons.set(channel, { reason, final });
|
|
1489
|
+
if (final)
|
|
1490
|
+
this.preventRouteReopen(channel, reason);
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
else if (typeof push.body.module_id === "string") {
|
|
1496
|
+
for (const [channel, module] of this.routeModules) {
|
|
1497
|
+
if (module === push.body.module_id) {
|
|
1498
|
+
this.legacyChannelReasons.set(channel, reason);
|
|
1499
|
+
if (push.op === "route.closed")
|
|
1500
|
+
this.preventRouteReopen(channel, reason);
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
preventRouteReopen(channel, reason) {
|
|
1506
|
+
if (classifyRouteCloseReason(reason) !== "must_not_reopen")
|
|
1507
|
+
return;
|
|
1508
|
+
for (const cached of this.routes.values()) {
|
|
1509
|
+
if (cached.handle?.channel === channel)
|
|
1510
|
+
cached.endReason = reason;
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
routeEndReason(channel) {
|
|
1514
|
+
const specific = this.routeEndReasons.get(channel);
|
|
1515
|
+
if (specific)
|
|
1516
|
+
return specific.reason;
|
|
1517
|
+
return this.legacyChannelReasons.get(channel) ?? "unknown";
|
|
1518
|
+
}
|
|
1434
1519
|
failHandle(handle, error) {
|
|
1435
1520
|
for (const [key, pending] of this.pending) {
|
|
1436
1521
|
if (pending.handle && sameRouteHandle(pending.handle, handle))
|
|
@@ -1438,10 +1523,15 @@ export class SubcClient {
|
|
|
1438
1523
|
}
|
|
1439
1524
|
}
|
|
1440
1525
|
fail(err) {
|
|
1526
|
+
const routeError = err instanceof SubcError && err.closeReason ? err
|
|
1527
|
+
: new SubcError(err.message, errorCode(err), undefined, this.closeStarted ? "closed_by_caller" : "connection_lost");
|
|
1441
1528
|
if (!this.closedErr)
|
|
1442
1529
|
this.closedErr = err;
|
|
1530
|
+
this.routeEndReasons.clear();
|
|
1531
|
+
this.legacyChannelReasons.clear();
|
|
1532
|
+
this.routeModules.clear();
|
|
1443
1533
|
for (const [key, pending] of this.pending) {
|
|
1444
|
-
this.rejectPending(key, pending, err);
|
|
1534
|
+
this.rejectPending(key, pending, pending.handle ? routeError : err);
|
|
1445
1535
|
}
|
|
1446
1536
|
}
|
|
1447
1537
|
notSentCallError(message, cause) {
|
|
@@ -1468,6 +1558,9 @@ export class SubcClient {
|
|
|
1468
1558
|
}
|
|
1469
1559
|
installRoute(channel, epoch, reverseRequests = new ReverseRequestRegistry()) {
|
|
1470
1560
|
const handle = createRouteHandle(channel, epoch, this.connectionToken, reverseRequests);
|
|
1561
|
+
this.routeEndReasons.delete(channel);
|
|
1562
|
+
this.legacyChannelReasons.delete(channel);
|
|
1563
|
+
this.routeModules.delete(channel);
|
|
1471
1564
|
this.liveRoutes.set(channel, handle);
|
|
1472
1565
|
return handle;
|
|
1473
1566
|
}
|
|
@@ -1565,6 +1658,9 @@ export function isRetryableRouteOpenCode(code) {
|
|
|
1565
1658
|
// A capability deny is policy, never a transient target-availability failure.
|
|
1566
1659
|
if (code === "capability_forbidden")
|
|
1567
1660
|
return false;
|
|
1661
|
+
// Decoding a flow stamp alone does not promise flow behaviour.
|
|
1662
|
+
if (code === "target_flow_unsupported")
|
|
1663
|
+
return false;
|
|
1568
1664
|
return (code === "module_reloading" ||
|
|
1569
1665
|
code === "module_warming" ||
|
|
1570
1666
|
code === "target_unavailable" ||
|
|
@@ -1681,12 +1777,36 @@ export function isValidCapabilityIdentifier(identifier) {
|
|
|
1681
1777
|
const numericVersion = Number(version);
|
|
1682
1778
|
return Number.isSafeInteger(numericVersion) && numericVersion >= 1 && numericVersion <= 0xffff_ffff;
|
|
1683
1779
|
}
|
|
1684
|
-
function routeCacheKey(target, identity, consumerIdentity, consumerCapabilities) {
|
|
1780
|
+
function routeCacheKey(target, identity, consumerIdentity, consumerCapabilities, scope) {
|
|
1685
1781
|
const consumerPart = consumerIdentity
|
|
1686
1782
|
? `${consumerIdentity.module_id}\0${consumerIdentity.launch_nonce}`
|
|
1687
1783
|
: "";
|
|
1688
1784
|
const capabilitiesPart = [...consumerCapabilities].sort().join("\0");
|
|
1689
|
-
|
|
1785
|
+
// A JSON tuple keeps opaque refs (including embedded separators) distinct.
|
|
1786
|
+
const scopePart = scope
|
|
1787
|
+
? JSON.stringify([scope.owner.kind, scope.owner.kind === "reserved" ? scope.owner.module_id : null, scope.ref, scope.scopeEpoch])
|
|
1788
|
+
: "";
|
|
1789
|
+
return `${target.kind}\0${target.module_id}\0${identity.project_root}\0${identity.harness}\0${identity.session}\0${consumerPart}\0${capabilitiesPart}\0${scopePart}`;
|
|
1790
|
+
}
|
|
1791
|
+
/** Validate before any route-open I/O, and snapshot caller-owned mutable values for reopens. */
|
|
1792
|
+
function validatedRouteScope(scope) {
|
|
1793
|
+
if (scope === undefined)
|
|
1794
|
+
return undefined;
|
|
1795
|
+
if (!scope || typeof scope.owner !== "object" || !scope.owner ||
|
|
1796
|
+
!["reserved", "direct", "unverified"].includes(scope.owner.kind) ||
|
|
1797
|
+
(scope.owner.kind === "reserved" && (typeof scope.owner.module_id !== "string" || scope.owner.module_id.length === 0))) {
|
|
1798
|
+
throw new SubcError("scope.owner must be a Principal with a non-empty reserved module_id", "invalid_scope");
|
|
1799
|
+
}
|
|
1800
|
+
if (typeof scope.ref !== "string" || scope.ref.length === 0) {
|
|
1801
|
+
throw new SubcError("scope.ref must be a non-empty string", "invalid_scope");
|
|
1802
|
+
}
|
|
1803
|
+
if (scope.scopeEpoch === undefined) {
|
|
1804
|
+
throw new SubcError("scope.scopeEpoch is required", "scope_epoch_required");
|
|
1805
|
+
}
|
|
1806
|
+
if (!Number.isSafeInteger(scope.scopeEpoch) || scope.scopeEpoch < 0) {
|
|
1807
|
+
throw new SubcError("scope.scopeEpoch must be a safe non-negative integer", "invalid_scope");
|
|
1808
|
+
}
|
|
1809
|
+
return { owner: { ...scope.owner }, ref: scope.ref, scopeEpoch: scope.scopeEpoch };
|
|
1690
1810
|
}
|
|
1691
1811
|
function routeOpenConsumerIdentity(opts = {}) {
|
|
1692
1812
|
if (opts.consumerIdentity !== undefined)
|