@cello-protocol/daemon 0.0.3 → 0.0.5
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/dist/agent-loader.d.ts +41 -0
- package/dist/agent-loader.d.ts.map +1 -0
- package/dist/agent-loader.js +94 -0
- package/dist/agent-loader.js.map +1 -0
- package/dist/bin/cello-daemon.d.ts +13 -0
- package/dist/bin/cello-daemon.d.ts.map +1 -0
- package/dist/bin/cello-daemon.js +170 -0
- package/dist/bin/cello-daemon.js.map +1 -0
- package/dist/cello-node-transport-dialer.d.ts +59 -0
- package/dist/cello-node-transport-dialer.d.ts.map +1 -0
- package/dist/cello-node-transport-dialer.js +108 -0
- package/dist/cello-node-transport-dialer.js.map +1 -0
- package/dist/challenge-verifier.d.ts +12 -0
- package/dist/challenge-verifier.d.ts.map +1 -0
- package/dist/challenge-verifier.js +11 -0
- package/dist/challenge-verifier.js.map +1 -0
- package/dist/connect-or-start.d.ts +25 -0
- package/dist/connect-or-start.d.ts.map +1 -0
- package/dist/connect-or-start.js +117 -0
- package/dist/connect-or-start.js.map +1 -0
- package/dist/content-park-client.d.ts +49 -0
- package/dist/content-park-client.d.ts.map +1 -0
- package/dist/content-park-client.js +196 -0
- package/dist/content-park-client.js.map +1 -0
- package/dist/daemon.d.ts +65 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +3202 -0
- package/dist/daemon.js.map +1 -0
- package/dist/directory-bootstrap.d.ts +55 -0
- package/dist/directory-bootstrap.d.ts.map +1 -0
- package/dist/directory-bootstrap.js +102 -0
- package/dist/directory-bootstrap.js.map +1 -0
- package/dist/file-manifest-provider.d.ts +18 -0
- package/dist/file-manifest-provider.d.ts.map +1 -0
- package/dist/file-manifest-provider.js +72 -0
- package/dist/file-manifest-provider.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/ipc-client.d.ts +31 -0
- package/dist/ipc-client.d.ts.map +1 -0
- package/dist/ipc-client.js +112 -0
- package/dist/ipc-client.js.map +1 -0
- package/dist/ipc-server.d.ts +49 -0
- package/dist/ipc-server.d.ts.map +1 -0
- package/dist/ipc-server.js +268 -0
- package/dist/ipc-server.js.map +1 -0
- package/dist/lock-file.d.ts +27 -0
- package/dist/lock-file.d.ts.map +1 -0
- package/dist/lock-file.js +84 -0
- package/dist/lock-file.js.map +1 -0
- package/dist/manifest-loader.d.ts +33 -0
- package/dist/manifest-loader.d.ts.map +1 -0
- package/dist/manifest-loader.js +70 -0
- package/dist/manifest-loader.js.map +1 -0
- package/dist/manifest-poll-scheduler.d.ts +31 -0
- package/dist/manifest-poll-scheduler.d.ts.map +1 -0
- package/dist/manifest-poll-scheduler.js +59 -0
- package/dist/manifest-poll-scheduler.js.map +1 -0
- package/dist/manifest-version-store-file.d.ts +18 -0
- package/dist/manifest-version-store-file.d.ts.map +1 -0
- package/dist/manifest-version-store-file.js +40 -0
- package/dist/manifest-version-store-file.js.map +1 -0
- package/dist/manifest-version-store.d.ts +14 -0
- package/dist/manifest-version-store.d.ts.map +1 -0
- package/dist/manifest-version-store.js +13 -0
- package/dist/manifest-version-store.js.map +1 -0
- package/dist/network-directory-node.d.ts +94 -0
- package/dist/network-directory-node.d.ts.map +1 -0
- package/dist/network-directory-node.js +626 -0
- package/dist/network-directory-node.js.map +1 -0
- package/dist/nonce-dedup.d.ts +68 -0
- package/dist/nonce-dedup.d.ts.map +1 -0
- package/dist/nonce-dedup.js +204 -0
- package/dist/nonce-dedup.js.map +1 -0
- package/dist/notification-dispatcher.d.ts +65 -0
- package/dist/notification-dispatcher.d.ts.map +1 -0
- package/dist/notification-dispatcher.js +138 -0
- package/dist/notification-dispatcher.js.map +1 -0
- package/dist/registration-context.d.ts +69 -0
- package/dist/registration-context.d.ts.map +1 -0
- package/dist/registration-context.js +118 -0
- package/dist/registration-context.js.map +1 -0
- package/dist/registration-manager.d.ts +72 -0
- package/dist/registration-manager.d.ts.map +1 -0
- package/dist/registration-manager.js +267 -0
- package/dist/registration-manager.js.map +1 -0
- package/dist/registration-persistence.d.ts +131 -0
- package/dist/registration-persistence.d.ts.map +1 -0
- package/dist/registration-persistence.js +233 -0
- package/dist/registration-persistence.js.map +1 -0
- package/dist/retry-queue.d.ts +144 -0
- package/dist/retry-queue.d.ts.map +1 -0
- package/dist/retry-queue.js +444 -0
- package/dist/retry-queue.js.map +1 -0
- package/dist/seal-frontier-verify.d.ts +58 -0
- package/dist/seal-frontier-verify.d.ts.map +1 -0
- package/dist/seal-frontier-verify.js +87 -0
- package/dist/seal-frontier-verify.js.map +1 -0
- package/dist/seal-legibility-tbs.d.ts +25 -0
- package/dist/seal-legibility-tbs.d.ts.map +1 -0
- package/dist/seal-legibility-tbs.js +78 -0
- package/dist/seal-legibility-tbs.js.map +1 -0
- package/dist/seal-upgrade.d.ts +90 -0
- package/dist/seal-upgrade.d.ts.map +1 -0
- package/dist/seal-upgrade.js +178 -0
- package/dist/seal-upgrade.js.map +1 -0
- package/dist/session-assignment-parser.d.ts +22 -0
- package/dist/session-assignment-parser.d.ts.map +1 -0
- package/dist/session-assignment-parser.js +139 -0
- package/dist/session-assignment-parser.js.map +1 -0
- package/dist/session-ceremony.d.ts +156 -0
- package/dist/session-ceremony.d.ts.map +1 -0
- package/dist/session-ceremony.js +447 -0
- package/dist/session-ceremony.js.map +1 -0
- package/dist/session-connection-gater.d.ts +91 -0
- package/dist/session-connection-gater.d.ts.map +1 -0
- package/dist/session-connection-gater.js +146 -0
- package/dist/session-connection-gater.js.map +1 -0
- package/dist/session-node-manager.d.ts +585 -0
- package/dist/session-node-manager.d.ts.map +1 -0
- package/dist/session-node-manager.js +2609 -0
- package/dist/session-node-manager.js.map +1 -0
- package/dist/session-relay-client.d.ts +101 -0
- package/dist/session-relay-client.d.ts.map +1 -0
- package/dist/session-relay-client.js +520 -0
- package/dist/session-relay-client.js.map +1 -0
- package/dist/session-tree.d.ts +80 -0
- package/dist/session-tree.d.ts.map +1 -0
- package/dist/session-tree.js +123 -0
- package/dist/session-tree.js.map +1 -0
- package/dist/signaling-connect.d.ts +83 -0
- package/dist/signaling-connect.d.ts.map +1 -0
- package/dist/signaling-connect.js +266 -0
- package/dist/signaling-connect.js.map +1 -0
- package/dist/transcript-cipher.d.ts +31 -0
- package/dist/transcript-cipher.d.ts.map +1 -0
- package/dist/transcript-cipher.js +74 -0
- package/dist/transcript-cipher.js.map +1 -0
- package/dist/transport-composition.d.ts +31 -0
- package/dist/transport-composition.d.ts.map +1 -0
- package/dist/transport-composition.js +55 -0
- package/dist/transport-composition.js.map +1 -0
- package/dist/transport-selector.d.ts +189 -0
- package/dist/transport-selector.d.ts.map +1 -0
- package/dist/transport-selector.js +195 -0
- package/dist/transport-selector.js.map +1 -0
- package/dist/types.d.ts +265 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +33 -0
- package/dist/types.js.map +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CELLO Daemon — transport-composition.ts (CELLO-M7-TRANSPORT-001)
|
|
3
|
+
*
|
|
4
|
+
* Composition-root selection of the transport adapters by CELLO_ENV. Per CLAUDE.md
|
|
5
|
+
* the daemon composition root instantiates all adapters and fails fast at startup
|
|
6
|
+
* (not at first session) when a production environment is missing required config.
|
|
7
|
+
*
|
|
8
|
+
* 'local' | 'test' → in-process stubs (no network).
|
|
9
|
+
* 'dev' | 'staging' | 'production' → real adapters; require their backing
|
|
10
|
+
* dependencies (a TransportDialer for the
|
|
11
|
+
* selector). Missing config → throw at
|
|
12
|
+
* startup naming what is missing (AC-010).
|
|
13
|
+
*/
|
|
14
|
+
import { LocalTransportSelectorStub, TransportSelector, } from "./transport-selector.js";
|
|
15
|
+
/** Resolve CELLO_ENV. Unknown/undefined defaults to 'local' (in-process stubs). */
|
|
16
|
+
export function resolveCelloEnv(raw) {
|
|
17
|
+
switch (raw) {
|
|
18
|
+
case "local":
|
|
19
|
+
case "test":
|
|
20
|
+
case "dev":
|
|
21
|
+
case "staging":
|
|
22
|
+
case "production":
|
|
23
|
+
return raw;
|
|
24
|
+
default:
|
|
25
|
+
return "local";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** True for environments that require real (network-backed) transport adapters. */
|
|
29
|
+
export function isProductionVariant(env) {
|
|
30
|
+
return env === "dev" || env === "staging" || env === "production";
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Build the transport selector for the given environment. Stub for local/test;
|
|
34
|
+
* real TransportSelector for production variants (requires a TransportDialer).
|
|
35
|
+
*/
|
|
36
|
+
export function createTransportSelector(opts) {
|
|
37
|
+
const { env, logger, transportDialer, directDialTimeoutMs } = opts;
|
|
38
|
+
if (!isProductionVariant(env)) {
|
|
39
|
+
return new LocalTransportSelectorStub();
|
|
40
|
+
}
|
|
41
|
+
if (!transportDialer) {
|
|
42
|
+
throw new Error(`CELLO_ENV='${env}' requires a transport dialer (directory/relay-backed) for the ` +
|
|
43
|
+
`transport selector, but none was provided to the daemon composition root. The ` +
|
|
44
|
+
`transport selector cannot dial a counterparty without it — fix the daemon ` +
|
|
45
|
+
`configuration (config.transportDialer) before startup.`);
|
|
46
|
+
}
|
|
47
|
+
return new TransportSelector({ dialer: transportDialer, logger, directDialTimeoutMs });
|
|
48
|
+
}
|
|
49
|
+
// NOTE (CELLO-M7-TRANSPORT-001): there is no createAutoNatService here. The
|
|
50
|
+
// daemon's runtime IAutoNatService is the NodeAutoNatService wrapping the standing
|
|
51
|
+
// receiver node — that node is created inside startDaemon (after this composition
|
|
52
|
+
// runs), so the AutoNAT service is resolved from SessionNodeManager.
|
|
53
|
+
// getStandingReceiverAutoNat() rather than constructed in the composition root.
|
|
54
|
+
// config.autoNatService remains an explicit test override.
|
|
55
|
+
//# sourceMappingURL=transport-composition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport-composition.js","sourceRoot":"","sources":["../src/transport-composition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,0BAA0B,EAC1B,iBAAiB,GAGlB,MAAM,yBAAyB,CAAC;AAKjC,mFAAmF;AACnF,MAAM,UAAU,eAAe,CAAC,GAAuB;IACrD,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,OAAO,CAAC;QACb,KAAK,MAAM,CAAC;QACZ,KAAK,KAAK,CAAC;QACX,KAAK,SAAS,CAAC;QACf,KAAK,YAAY;YACf,OAAO,GAAG,CAAC;QACb;YACE,OAAO,OAAO,CAAC;IACnB,CAAC;AACH,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,mBAAmB,CAAC,GAAa;IAC/C,OAAO,GAAG,KAAK,KAAK,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,YAAY,CAAC;AACpE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAKvC;IACC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,eAAe,EAAE,mBAAmB,EAAE,GAAG,IAAI,CAAC;IACnE,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9B,OAAO,IAAI,0BAA0B,EAAE,CAAC;IAC1C,CAAC;IACD,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CACb,cAAc,GAAG,iEAAiE;YAChF,gFAAgF;YAChF,4EAA4E;YAC5E,wDAAwD,CAC3D,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,iBAAiB,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC,CAAC;AACzF,CAAC;AAED,4EAA4E;AAC5E,mFAAmF;AACnF,kFAAkF;AAClF,qEAAqE;AACrE,gFAAgF;AAChF,2DAA2D"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CELLO Daemon — transport-selector.ts (CELLO-M7-TRANSPORT-001)
|
|
3
|
+
*
|
|
4
|
+
* Direct-P2P-by-default transport selection with relay fallback and a best-effort
|
|
5
|
+
* dcutr hole-punch upgrade.
|
|
6
|
+
*
|
|
7
|
+
* ─── Specification (Phase S) ──────────────────────────────────────────────────
|
|
8
|
+
* Given a FROST-signed SessionAssignment (produced by WIRE-001), choose how to
|
|
9
|
+
* dial the counterparty's session node:
|
|
10
|
+
* 1. Read transport_mode from the assignment. This is the ONLY authority for
|
|
11
|
+
* the dial strategy (SI-001 / AC-015) — NEVER infer mode from address format.
|
|
12
|
+
* 2. transport_mode === 'direct' → dial counterparty_session_addrs directly.
|
|
13
|
+
* 3. direct dial fails OR transport_mode === 'relay' → dial the relay circuit
|
|
14
|
+
* address (from the daemon's relay registry, independent of transport_mode —
|
|
15
|
+
* AC-006).
|
|
16
|
+
* 4. After a relay connection is established → attempt a dcutr hole-punch upgrade
|
|
17
|
+
* (non-blocking, best-effort). Failure is non-fatal (SI-003).
|
|
18
|
+
* 5. If BOTH direct and relay fail → terminal 'relay_fallback_also_failed' (AC-008).
|
|
19
|
+
*
|
|
20
|
+
* Error code registry (fixed strings — also in CONTEXT.md):
|
|
21
|
+
* 'autonat_unavailable' — internal; no probers reachable.
|
|
22
|
+
* 'direct_dial_failed_falling_back_to_relay' — WARN; intermediate, non-fatal.
|
|
23
|
+
* 'relay_fallback_also_failed' — terminal session failure.
|
|
24
|
+
* 'dcutr_upgrade_failed' — DEBUG; non-fatal.
|
|
25
|
+
*
|
|
26
|
+
* ─── Pseudocode (Phase P) ─────────────────────────────────────────────────────
|
|
27
|
+
* dial(assignment, { correlationId }):
|
|
28
|
+
* mode = assignment.transport_mode // authoritative
|
|
29
|
+
* sessionId = hex(assignment.session_id)
|
|
30
|
+
* peerId = assignment.counterparty_session_peer_id
|
|
31
|
+
* directAddrs = assignment.counterparty_session_addrs ?? []
|
|
32
|
+
* if mode === 'direct':
|
|
33
|
+
* try { dialer.dialDirect(peerId, directAddrs); log mode.selected('direct'); return {ok, 'direct'} }
|
|
34
|
+
* catch e { log direct_dial.failed(WARN, e.message) } // fall through
|
|
35
|
+
* try { dialer.dialRelay(dialer.relayCircuitAddr(assignment), peerId) }
|
|
36
|
+
* catch { return {ok:false, 'relay_fallback_also_failed', guidance} } // NO mode.selected
|
|
37
|
+
* log mode.selected('relay')
|
|
38
|
+
* dcutrSettled = (async () => {
|
|
39
|
+
* try { dialer.attemptDcutr(peerId); log dcutr.upgraded(INFO); return true }
|
|
40
|
+
* catch e { log dcutr.failed(DEBUG, e.message); return false } })()
|
|
41
|
+
* return {ok:true, 'relay', dcutrSettled} // dial does NOT await dcutr
|
|
42
|
+
*/
|
|
43
|
+
import type { SessionAssignment } from "@cello-protocol/protocol-types";
|
|
44
|
+
import type { Logger } from "./types.js";
|
|
45
|
+
import type { Dialability } from "@cello-protocol/transport";
|
|
46
|
+
export declare const TRANSPORT_ERROR: {
|
|
47
|
+
/** Internal: AutoNAT could not run because no directory-node probers are reachable. */
|
|
48
|
+
readonly AUTONAT_UNAVAILABLE: "autonat_unavailable";
|
|
49
|
+
/** WARN-level intermediate, non-fatal: direct dial failed, relay fallback initiated. */
|
|
50
|
+
readonly DIRECT_DIAL_FAILED_FALLING_BACK: "direct_dial_failed_falling_back_to_relay";
|
|
51
|
+
/** Terminal: both direct dial and relay fallback failed. */
|
|
52
|
+
readonly RELAY_FALLBACK_ALSO_FAILED: "relay_fallback_also_failed";
|
|
53
|
+
/** DEBUG-level non-fatal: dcutr hole-punch upgrade attempt failed. */
|
|
54
|
+
readonly DCUTR_UPGRADE_FAILED: "dcutr_upgrade_failed";
|
|
55
|
+
};
|
|
56
|
+
/** Default direct-dial timeout (ms). Not a user-facing option (implementation_notes). */
|
|
57
|
+
export declare const DEFAULT_DIRECT_DIAL_TIMEOUT_MS = 5000;
|
|
58
|
+
/**
|
|
59
|
+
* The outcome of a transport selection. Discriminated by `ok` so the failure
|
|
60
|
+
* branch is exactly the `{ ok:false, reason, guidance }` shape the MCP layer
|
|
61
|
+
* surfaces to the caller (AC-008).
|
|
62
|
+
*
|
|
63
|
+
* For the relay branch, `dcutrSettled` resolves when the background dcutr attempt
|
|
64
|
+
* settles — `true` if the connection was hole-punch upgraded to direct, `false`
|
|
65
|
+
* otherwise. dial() does NOT await it: the session is usable immediately upon the
|
|
66
|
+
* relay connection (AC-007). Production callers may ignore `dcutrSettled`.
|
|
67
|
+
*/
|
|
68
|
+
export type TransportResult = {
|
|
69
|
+
ok: true;
|
|
70
|
+
mode: "direct";
|
|
71
|
+
} | {
|
|
72
|
+
ok: true;
|
|
73
|
+
mode: "relay";
|
|
74
|
+
dcutrSettled: Promise<boolean>;
|
|
75
|
+
} | {
|
|
76
|
+
ok: false;
|
|
77
|
+
reason: typeof TRANSPORT_ERROR.RELAY_FALLBACK_ALSO_FAILED;
|
|
78
|
+
guidance: string;
|
|
79
|
+
};
|
|
80
|
+
export interface TransportDialOptions {
|
|
81
|
+
/** Correlation ID threaded through every event for this session flow. */
|
|
82
|
+
correlationId: string;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Adapter interface for choosing and performing the transport dial.
|
|
86
|
+
* Real implementation: TransportSelector (drives a TransportDialer + Logger).
|
|
87
|
+
* Stub: LocalTransportSelectorStub (returns configurable canned results — used by
|
|
88
|
+
* the composition root when CELLO_ENV is 'local'|'test').
|
|
89
|
+
*/
|
|
90
|
+
export interface ITransportSelector {
|
|
91
|
+
dial(assignment: SessionAssignment, opts: TransportDialOptions): Promise<TransportResult>;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The advertised address this daemon offers to the directory for a new session,
|
|
95
|
+
* derived from the standing receiver's AutoNAT dialability (AC-004 / AC-019):
|
|
96
|
+
* a direct multiaddr when dialable, the relay circuit address otherwise.
|
|
97
|
+
*/
|
|
98
|
+
export interface SessionNegotiationContext {
|
|
99
|
+
agentName: string;
|
|
100
|
+
correlationId: string;
|
|
101
|
+
advertisedAddress: AdvertisedAddress;
|
|
102
|
+
params: Record<string, unknown>;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Outcome of directory session negotiation. On success it carries the
|
|
106
|
+
* FROST-signed SessionAssignment (transport_mode + counterparty_session_addrs)
|
|
107
|
+
* that the TransportSelector then consumes.
|
|
108
|
+
*/
|
|
109
|
+
export type SessionNegotiationResult = {
|
|
110
|
+
ok: true;
|
|
111
|
+
assignment: SessionAssignment;
|
|
112
|
+
} | {
|
|
113
|
+
ok: false;
|
|
114
|
+
reason: string;
|
|
115
|
+
guidance: string;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Adapter that performs directory session negotiation and returns the
|
|
119
|
+
* FROST-signed SessionAssignment. The real implementation is owned by
|
|
120
|
+
* WIRE-001/SIGNAL-001 (directory signaling); it is injected into the daemon
|
|
121
|
+
* composition root. When absent, cello_initiate_session reports
|
|
122
|
+
* directory_signaling_not_configured (it does NOT crash — the transport adapters
|
|
123
|
+
* are still wired). This is the seam that lets cello_initiate_session drive the
|
|
124
|
+
* transport selector once an assignment exists (AC-010(c)).
|
|
125
|
+
*/
|
|
126
|
+
export interface SessionNegotiator {
|
|
127
|
+
negotiate(ctx: SessionNegotiationContext): Promise<SessionNegotiationResult>;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* The low-level dial operations the selector composes. The real implementation
|
|
131
|
+
* wraps a CelloNode (direct dial + relay circuit dial) plus the daemon's relay
|
|
132
|
+
* registry. Each operation throws on failure; the selector catches and routes.
|
|
133
|
+
*/
|
|
134
|
+
export interface TransportDialer {
|
|
135
|
+
/** Dial the counterparty directly. Throws on timeout/refused/unreachable. */
|
|
136
|
+
dialDirect(peerId: string | undefined, addrs: string[], timeoutMs: number): Promise<void>;
|
|
137
|
+
/** Dial the counterparty via the relay circuit address. Throws on failure. */
|
|
138
|
+
dialRelay(relayCircuitAddr: string, peerId: string | undefined): Promise<void>;
|
|
139
|
+
/**
|
|
140
|
+
* The relay circuit address for this assignment. Sourced from the daemon's relay
|
|
141
|
+
* registry (populated during directory connection) — NOT parsed from the
|
|
142
|
+
* assignment's address fields, and available regardless of transport_mode (AC-006).
|
|
143
|
+
*/
|
|
144
|
+
relayCircuitAddr(assignment: SessionAssignment): string;
|
|
145
|
+
/** Attempt a dcutr hole-punch upgrade. Throws on failure (non-fatal — SI-003). */
|
|
146
|
+
attemptDcutr(peerId: string | undefined): Promise<void>;
|
|
147
|
+
}
|
|
148
|
+
export declare class TransportSelector implements ITransportSelector {
|
|
149
|
+
#private;
|
|
150
|
+
constructor(opts: {
|
|
151
|
+
dialer: TransportDialer;
|
|
152
|
+
logger: Logger;
|
|
153
|
+
directDialTimeoutMs?: number;
|
|
154
|
+
});
|
|
155
|
+
dial(assignment: SessionAssignment, opts: TransportDialOptions): Promise<TransportResult>;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* In-process transport selector stub. Returns a configurable canned result with
|
|
159
|
+
* no network access. Selected by the composition root when CELLO_ENV is
|
|
160
|
+
* 'local' | 'test', so the daemon's session establishment path is wired and does
|
|
161
|
+
* not crash with "adapter not wired" (AC-010). Defaults to a successful relay
|
|
162
|
+
* selection (the conservative, always-available transport).
|
|
163
|
+
*/
|
|
164
|
+
export declare class LocalTransportSelectorStub implements ITransportSelector {
|
|
165
|
+
#private;
|
|
166
|
+
constructor(result?: TransportResult);
|
|
167
|
+
dial(_assignment: SessionAssignment, _opts: TransportDialOptions): Promise<TransportResult>;
|
|
168
|
+
/** Test/composition driver: set the canned result. */
|
|
169
|
+
setResult(result: TransportResult): void;
|
|
170
|
+
}
|
|
171
|
+
export type AdvertisedAddress = {
|
|
172
|
+
kind: "direct";
|
|
173
|
+
addr: string;
|
|
174
|
+
} | {
|
|
175
|
+
kind: "relay";
|
|
176
|
+
addr: string;
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Decide what address a node advertises in its SessionAssignment negotiation,
|
|
180
|
+
* given its AutoNAT dialability and its relay circuit address.
|
|
181
|
+
*
|
|
182
|
+
* dialable with a publicAddr → advertise the direct address (transport_mode will
|
|
183
|
+
* be 'direct'). Otherwise (behind NAT, or AutoNAT unavailable because the
|
|
184
|
+
* directory is reconnecting) → advertise the relay circuit address (conservative
|
|
185
|
+
* fallback; transport_mode will be 'relay'). This decision is made once at
|
|
186
|
+
* session establishment and is immutable for that session (AC-019).
|
|
187
|
+
*/
|
|
188
|
+
export declare function selectAdvertisedAddress(dialability: Dialability, relayCircuitAddr: string): AdvertisedAddress;
|
|
189
|
+
//# sourceMappingURL=transport-selector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport-selector.d.ts","sourceRoot":"","sources":["../src/transport-selector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CELLO Daemon — transport-selector.ts (CELLO-M7-TRANSPORT-001)
|
|
3
|
+
*
|
|
4
|
+
* Direct-P2P-by-default transport selection with relay fallback and a best-effort
|
|
5
|
+
* dcutr hole-punch upgrade.
|
|
6
|
+
*
|
|
7
|
+
* ─── Specification (Phase S) ──────────────────────────────────────────────────
|
|
8
|
+
* Given a FROST-signed SessionAssignment (produced by WIRE-001), choose how to
|
|
9
|
+
* dial the counterparty's session node:
|
|
10
|
+
* 1. Read transport_mode from the assignment. This is the ONLY authority for
|
|
11
|
+
* the dial strategy (SI-001 / AC-015) — NEVER infer mode from address format.
|
|
12
|
+
* 2. transport_mode === 'direct' → dial counterparty_session_addrs directly.
|
|
13
|
+
* 3. direct dial fails OR transport_mode === 'relay' → dial the relay circuit
|
|
14
|
+
* address (from the daemon's relay registry, independent of transport_mode —
|
|
15
|
+
* AC-006).
|
|
16
|
+
* 4. After a relay connection is established → attempt a dcutr hole-punch upgrade
|
|
17
|
+
* (non-blocking, best-effort). Failure is non-fatal (SI-003).
|
|
18
|
+
* 5. If BOTH direct and relay fail → terminal 'relay_fallback_also_failed' (AC-008).
|
|
19
|
+
*
|
|
20
|
+
* Error code registry (fixed strings — also in CONTEXT.md):
|
|
21
|
+
* 'autonat_unavailable' — internal; no probers reachable.
|
|
22
|
+
* 'direct_dial_failed_falling_back_to_relay' — WARN; intermediate, non-fatal.
|
|
23
|
+
* 'relay_fallback_also_failed' — terminal session failure.
|
|
24
|
+
* 'dcutr_upgrade_failed' — DEBUG; non-fatal.
|
|
25
|
+
*
|
|
26
|
+
* ─── Pseudocode (Phase P) ─────────────────────────────────────────────────────
|
|
27
|
+
* dial(assignment, { correlationId }):
|
|
28
|
+
* mode = assignment.transport_mode // authoritative
|
|
29
|
+
* sessionId = hex(assignment.session_id)
|
|
30
|
+
* peerId = assignment.counterparty_session_peer_id
|
|
31
|
+
* directAddrs = assignment.counterparty_session_addrs ?? []
|
|
32
|
+
* if mode === 'direct':
|
|
33
|
+
* try { dialer.dialDirect(peerId, directAddrs); log mode.selected('direct'); return {ok, 'direct'} }
|
|
34
|
+
* catch e { log direct_dial.failed(WARN, e.message) } // fall through
|
|
35
|
+
* try { dialer.dialRelay(dialer.relayCircuitAddr(assignment), peerId) }
|
|
36
|
+
* catch { return {ok:false, 'relay_fallback_also_failed', guidance} } // NO mode.selected
|
|
37
|
+
* log mode.selected('relay')
|
|
38
|
+
* dcutrSettled = (async () => {
|
|
39
|
+
* try { dialer.attemptDcutr(peerId); log dcutr.upgraded(INFO); return true }
|
|
40
|
+
* catch e { log dcutr.failed(DEBUG, e.message); return false } })()
|
|
41
|
+
* return {ok:true, 'relay', dcutrSettled} // dial does NOT await dcutr
|
|
42
|
+
*/
|
|
43
|
+
// ─── Error codes (fixed) ──────────────────────────────────────────────────────
|
|
44
|
+
export const TRANSPORT_ERROR = {
|
|
45
|
+
/** Internal: AutoNAT could not run because no directory-node probers are reachable. */
|
|
46
|
+
AUTONAT_UNAVAILABLE: "autonat_unavailable",
|
|
47
|
+
/** WARN-level intermediate, non-fatal: direct dial failed, relay fallback initiated. */
|
|
48
|
+
DIRECT_DIAL_FAILED_FALLING_BACK: "direct_dial_failed_falling_back_to_relay",
|
|
49
|
+
/** Terminal: both direct dial and relay fallback failed. */
|
|
50
|
+
RELAY_FALLBACK_ALSO_FAILED: "relay_fallback_also_failed",
|
|
51
|
+
/** DEBUG-level non-fatal: dcutr hole-punch upgrade attempt failed. */
|
|
52
|
+
DCUTR_UPGRADE_FAILED: "dcutr_upgrade_failed",
|
|
53
|
+
};
|
|
54
|
+
const RELAY_FALLBACK_GUIDANCE = "Both direct P2P connection and relay fallback failed. The counterparty may be " +
|
|
55
|
+
"offline or unreachable from any path. Try again later, or check network connectivity.";
|
|
56
|
+
/** Default direct-dial timeout (ms). Not a user-facing option (implementation_notes). */
|
|
57
|
+
export const DEFAULT_DIRECT_DIAL_TIMEOUT_MS = 5000;
|
|
58
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
59
|
+
function sessionIdHex(assignment) {
|
|
60
|
+
return Buffer.from(assignment.session_id).toString("hex");
|
|
61
|
+
}
|
|
62
|
+
function errMessage(err) {
|
|
63
|
+
return err instanceof Error ? err.message : String(err);
|
|
64
|
+
}
|
|
65
|
+
// ─── TransportSelector (real) ─────────────────────────────────────────────────
|
|
66
|
+
export class TransportSelector {
|
|
67
|
+
#dialer;
|
|
68
|
+
#logger;
|
|
69
|
+
#directDialTimeoutMs;
|
|
70
|
+
constructor(opts) {
|
|
71
|
+
this.#dialer = opts.dialer;
|
|
72
|
+
this.#logger = opts.logger;
|
|
73
|
+
this.#directDialTimeoutMs = opts.directDialTimeoutMs ?? DEFAULT_DIRECT_DIAL_TIMEOUT_MS;
|
|
74
|
+
}
|
|
75
|
+
async dial(assignment, opts) {
|
|
76
|
+
const sessionId = sessionIdHex(assignment);
|
|
77
|
+
const { correlationId } = opts;
|
|
78
|
+
const peerId = assignment.counterparty_session_peer_id;
|
|
79
|
+
const directAddrs = assignment.counterparty_session_addrs ?? [];
|
|
80
|
+
// transport_mode is the ONLY authority (SI-001 / AC-015). Never inferred.
|
|
81
|
+
const mode = assignment.transport_mode;
|
|
82
|
+
// Step 1: direct dial when transport_mode === 'direct'.
|
|
83
|
+
if (mode === "direct") {
|
|
84
|
+
try {
|
|
85
|
+
await this.#dialer.dialDirect(peerId, directAddrs, this.#directDialTimeoutMs);
|
|
86
|
+
this.#logger.info("session.transport.mode.selected", {
|
|
87
|
+
sessionId,
|
|
88
|
+
mode: "direct",
|
|
89
|
+
correlationId,
|
|
90
|
+
});
|
|
91
|
+
return { ok: true, mode: "direct" };
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
// Recoverable: log WARN and fall through to relay fallback (AC-006).
|
|
95
|
+
// failureReason is the actual error message — never ${error}.
|
|
96
|
+
this.#logger.warn("session.transport.direct_dial.failed", {
|
|
97
|
+
sessionId,
|
|
98
|
+
counterpartyPeerId: peerId,
|
|
99
|
+
failureReason: errMessage(err),
|
|
100
|
+
reason: TRANSPORT_ERROR.DIRECT_DIAL_FAILED_FALLING_BACK,
|
|
101
|
+
correlationId,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Step 2: relay fallback (transport_mode === 'relay', or direct dial failed).
|
|
106
|
+
try {
|
|
107
|
+
const relayAddr = this.#dialer.relayCircuitAddr(assignment);
|
|
108
|
+
await this.#dialer.dialRelay(relayAddr, peerId);
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
// Terminal: both direct and relay failed. session.transport.mode.selected is
|
|
112
|
+
// NOT logged (no mode was selected — AC-008). We DO log the underlying relay
|
|
113
|
+
// failure reason at WARN so the on-call engineer can diagnose why the
|
|
114
|
+
// fallback failed (never swallow the exception silently).
|
|
115
|
+
this.#logger.warn("session.transport.relay_fallback.failed", {
|
|
116
|
+
sessionId,
|
|
117
|
+
counterpartyPeerId: peerId,
|
|
118
|
+
failureReason: errMessage(err),
|
|
119
|
+
reason: TRANSPORT_ERROR.RELAY_FALLBACK_ALSO_FAILED,
|
|
120
|
+
correlationId,
|
|
121
|
+
});
|
|
122
|
+
return {
|
|
123
|
+
ok: false,
|
|
124
|
+
reason: TRANSPORT_ERROR.RELAY_FALLBACK_ALSO_FAILED,
|
|
125
|
+
guidance: RELAY_FALLBACK_GUIDANCE,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
this.#logger.info("session.transport.mode.selected", {
|
|
129
|
+
sessionId,
|
|
130
|
+
mode: "relay",
|
|
131
|
+
correlationId,
|
|
132
|
+
});
|
|
133
|
+
// Step 3: best-effort dcutr upgrade — non-blocking. The session is usable
|
|
134
|
+
// immediately upon relay connection (AC-007); dcutr runs in the background.
|
|
135
|
+
const dcutrSettled = this.#attemptDcutr(peerId, sessionId, correlationId);
|
|
136
|
+
return { ok: true, mode: "relay", dcutrSettled };
|
|
137
|
+
}
|
|
138
|
+
async #attemptDcutr(peerId, sessionId, correlationId) {
|
|
139
|
+
try {
|
|
140
|
+
await this.#dialer.attemptDcutr(peerId);
|
|
141
|
+
this.#logger.info("session.transport.dcutr.upgraded", { sessionId, correlationId });
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
// Non-fatal (SI-003). DEBUG because dcutr failure is expected in many
|
|
146
|
+
// network topologies. failureReason is error.message — never ${error}.
|
|
147
|
+
this.#logger.debug("session.transport.dcutr.failed", {
|
|
148
|
+
sessionId,
|
|
149
|
+
failureReason: errMessage(err),
|
|
150
|
+
reason: TRANSPORT_ERROR.DCUTR_UPGRADE_FAILED,
|
|
151
|
+
correlationId,
|
|
152
|
+
});
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// ─── LocalTransportSelectorStub ───────────────────────────────────────────────
|
|
158
|
+
/**
|
|
159
|
+
* In-process transport selector stub. Returns a configurable canned result with
|
|
160
|
+
* no network access. Selected by the composition root when CELLO_ENV is
|
|
161
|
+
* 'local' | 'test', so the daemon's session establishment path is wired and does
|
|
162
|
+
* not crash with "adapter not wired" (AC-010). Defaults to a successful relay
|
|
163
|
+
* selection (the conservative, always-available transport).
|
|
164
|
+
*/
|
|
165
|
+
export class LocalTransportSelectorStub {
|
|
166
|
+
#result;
|
|
167
|
+
constructor(result) {
|
|
168
|
+
this.#result =
|
|
169
|
+
result ?? { ok: true, mode: "relay", dcutrSettled: Promise.resolve(false) };
|
|
170
|
+
}
|
|
171
|
+
async dial(_assignment, _opts) {
|
|
172
|
+
return this.#result;
|
|
173
|
+
}
|
|
174
|
+
/** Test/composition driver: set the canned result. */
|
|
175
|
+
setResult(result) {
|
|
176
|
+
this.#result = result;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Decide what address a node advertises in its SessionAssignment negotiation,
|
|
181
|
+
* given its AutoNAT dialability and its relay circuit address.
|
|
182
|
+
*
|
|
183
|
+
* dialable with a publicAddr → advertise the direct address (transport_mode will
|
|
184
|
+
* be 'direct'). Otherwise (behind NAT, or AutoNAT unavailable because the
|
|
185
|
+
* directory is reconnecting) → advertise the relay circuit address (conservative
|
|
186
|
+
* fallback; transport_mode will be 'relay'). This decision is made once at
|
|
187
|
+
* session establishment and is immutable for that session (AC-019).
|
|
188
|
+
*/
|
|
189
|
+
export function selectAdvertisedAddress(dialability, relayCircuitAddr) {
|
|
190
|
+
if (dialability.dialable && dialability.publicAddr) {
|
|
191
|
+
return { kind: "direct", addr: dialability.publicAddr };
|
|
192
|
+
}
|
|
193
|
+
return { kind: "relay", addr: relayCircuitAddr };
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=transport-selector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport-selector.js","sourceRoot":"","sources":["../src/transport-selector.ts"],"names":[],"mappings":"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"}
|