release(v4.8.3): cross-channel msgId dedup + Shade.aliasSession
Two follow-ups to the V4.8.2 duplicate-fan-out fixes Prism filed. 1. `Inbox.acceptBridgeFrame(blob)` + shared 4096-entry msgId LRU. The relay durably stores blobs and pushes them to every active delivery channel; without a cross-channel ack the bridge frame ran first and the next inbox-poll re-dispatched the same blob ~30 s later, tripping on consumed prekeys. Bridge consumers now plumb pushed frames through `acceptBridgeFrame`, which shares the dedup gate + ack path with `pollOnce`. Whichever channel delivers first wins; the other acks-and-skips. Inbox records the msgId before the ack so a parallel poll can't observe an in-flight ack window. 2. `Shade.aliasSession(oldLabel, newLabel)`. First-contact forces the receiver to label the new session by the relay's sender fingerprint hint (`fp:<senderfp>`); the post-decrypt plaintext typically announces the peer's real address. Aliasing moves session, trusted identity, peer-verification, and identity- version under the canonical label. Holds the per-peer mutex on both labels (lexicographic order) so concurrent crypto ops can't observe a half-moved state. Refuses to overwrite an existing session at the new label. Wire change: `IncomingMessage.expiresAt?` now surfaces the relay's expiry so receivers can pass bridge frames straight to `acceptBridgeFrame` without inventing a TTL. Tests cover bridge-then-poll, poll-then-bridge, aliasSession happy path, refuse-to-overwrite, and same-label no-op. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@shade/transport-bridge",
|
||||
"version": "4.8.2",
|
||||
"version": "4.8.3",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -28,6 +28,14 @@ export interface IncomingMessage {
|
||||
receivedAt: number;
|
||||
/** Relay-assigned message id (sha256 of ciphertext). Useful for ack/dedup. */
|
||||
msgId?: string;
|
||||
/**
|
||||
* Absolute expiry (ms since epoch) reported by the relay. Surfaced for
|
||||
* symmetry with `Inbox.handleBlob` so that
|
||||
* `Inbox.acceptBridgeFrame(msg)` can be wired directly without the
|
||||
* caller having to invent a TTL. Optional — pre-V4.8.3 relays / non-
|
||||
* inbox bridges don't populate it.
|
||||
*/
|
||||
expiresAt?: number;
|
||||
}
|
||||
|
||||
/** Subscriber callback. Bridges MAY invoke it concurrently. */
|
||||
@@ -83,6 +91,7 @@ export function decodeWireMessage(wire: BridgeWireMessage): IncomingMessage {
|
||||
receivedAt: wire.receivedAt,
|
||||
};
|
||||
if (wire.msgId !== undefined) msg.msgId = wire.msgId;
|
||||
if (wire.expiresAt !== undefined) msg.expiresAt = wire.expiresAt;
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user