@cortexkit/subc-client 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/subc-client",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "TypeScript client for the subc daemon. Wire-compatible (byte-for-byte) with the Rust subc-transport handshake and subc-protocol envelope.",
5
5
  "type": "module",
6
6
  "exports": {
package/src/client.ts CHANGED
@@ -521,7 +521,7 @@ export class SubcClient {
521
521
  timer: null,
522
522
  };
523
523
  pending.timer = setTimeout(() => {
524
- this.rejectPending(key, pending, new SubcError(`request on channel ${channel} timed out after ${ms}ms`));
524
+ this.rejectPending(key, pending, new SubcError(this.timeoutMessage(channel, corr, ms)));
525
525
  }, ms);
526
526
  this.pending.set(key, pending);
527
527
  this.sock.write(encodeFrame(frame), Date.now() + ms).catch((err) => {
@@ -586,7 +586,7 @@ export class SubcClient {
586
586
  classifyFailure,
587
587
  };
588
588
  pending.timer = setTimeout(() => {
589
- this.rejectPending(key, pending, new SubcError(`request on channel ${channel} timed out after ${ms}ms`));
589
+ this.rejectPending(key, pending, new SubcError(this.timeoutMessage(channel, corr, ms)));
590
590
  }, ms);
591
591
  this.pending.set(key, pending);
592
592
 
@@ -752,7 +752,14 @@ export class SubcClient {
752
752
  }
753
753
  for (const cached of this.routes.values()) {
754
754
  if (cached.closed) continue; // closed concurrently with reconnect — don't reopen.
755
- const channel = await this.routeOpen(cached.target, cached.identity);
755
+ // Thread the route's consumer identity through the reopen, exactly as the
756
+ // lazy per-call path (openCachedRoute) does. Dropping it here would make a
757
+ // route reopened after a reconnect send route.open with no consumer_identity,
758
+ // so the daemon would re-stamp it with a different (weaker) principal than the
759
+ // one it was originally bound under — a silent post-reconnect trust downgrade.
760
+ const channel = await this.routeOpen(cached.target, cached.identity, {
761
+ consumerIdentity: cached.consumerIdentity ?? null,
762
+ });
756
763
  // A closeRoute may have raced this reopen (flipping the tombstone during the
757
764
  // route.open await). If so, GOODBYE the channel instead of installing it, so the
758
765
  // closed route isn't silently re-established on the new connection.
@@ -765,6 +772,16 @@ export class SubcClient {
765
772
  }
766
773
  }
767
774
 
775
+ // A request timeout carries the local socket port and (channel, corr) so a
776
+ // packet capture can pinpoint the exact on-wire exchange — the decisive evidence
777
+ // for whether a "timed out" reply was actually delivered to this socket (a
778
+ // client-local demux problem) or never sent (a daemon/module problem).
779
+ private timeoutMessage(channel: number, corr: bigint, ms: number): string {
780
+ const port = this.sock.localPort();
781
+ const where = port === null ? "channel" : `local_port=${port} channel`;
782
+ return `request on ${where} ${channel} corr ${corr} timed out after ${ms}ms`;
783
+ }
784
+
768
785
  private routeClosedDuringOpen(): SubcCallError {
769
786
  return new SubcCallError("not_sent", "route was closed before route.open completed", "route_closed");
770
787
  }
package/src/socket.ts CHANGED
@@ -64,6 +64,15 @@ export class SubcSocket {
64
64
  sock.on("close", () => fail(new SocketClosedError("subc connection closed")));
65
65
  }
66
66
 
67
+ /**
68
+ * The OS-assigned local TCP port of this connection, or null if not yet
69
+ * connected/closed. Used to correlate a client-side timeout with a specific
70
+ * socket in a packet capture when diagnosing reply-delivery issues.
71
+ */
72
+ localPort(): number | null {
73
+ return this.sock.localPort ?? null;
74
+ }
75
+
67
76
  static connect(host: string, port: number, deadlineMs: number): Promise<SubcSocket> {
68
77
  return new Promise((resolve, reject) => {
69
78
  const sock = net.connect({ host, port });