@cello-protocol/transport 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.
@@ -0,0 +1,143 @@
1
+ /**
2
+ * M7-MANIFEST-002 — Manifest-related interface definitions for the CELLO client daemon.
3
+ *
4
+ * These interfaces decouple the daemon and SignalingManager from their concrete
5
+ * implementations. All interfaces are narrow by design.
6
+ *
7
+ * Interface overview:
8
+ *
9
+ * Client-side (daemon / SignalingManager):
10
+ * IManifestVersionStore — persists the last-seen manifest version (monotonicity gate)
11
+ * IManifestProvider — loads, verifies, and caches the consortium manifest
12
+ * IDirectoryChallengeVerifier — verifies the directory's step-5 Ed25519 challenge response
13
+ * IManifestPollScheduler — schedules background manifest poll calls
14
+ *
15
+ * Directory-side (directory node):
16
+ * DirectoryKeyProvider — provides the per-node nodeId and signs TBS bytes
17
+ * DirectoryManifestStore — provides the current consortium manifest for poll responses
18
+ *
19
+ * Crypto reference: RFC 8032 (Ed25519).
20
+ */
21
+ import type { ConsortiumManifest } from "@cello-protocol/protocol-types";
22
+ /**
23
+ * Persists the last-seen manifest version number across daemon restarts.
24
+ * Used to enforce version monotonicity: a manifest with a lower version number
25
+ * than the last-seen version is rejected as a potential rollback attack.
26
+ *
27
+ * In production: backed by SQLCipher local database.
28
+ * In tests: InMemoryManifestVersionStore (starts at null, holds in memory).
29
+ */
30
+ export interface IManifestVersionStore {
31
+ /** Returns the last persisted version number, or null if none has been seen. */
32
+ getLastSeenVersion(): Promise<number | null>;
33
+ /** Persists a new version number, replacing the previous value. */
34
+ persistVersion(version: number): Promise<void>;
35
+ }
36
+ /**
37
+ * Loads, verifies, and caches the consortium manifest.
38
+ * Abstracts manifest source (bundled JSON file vs test-supplied object).
39
+ *
40
+ * Production: FileManifestProvider reads consortium-manifest.json from package root.
41
+ * Tests: TestManifestProvider takes a pre-built ConsortiumManifest, skips file read.
42
+ */
43
+ export interface IManifestProvider {
44
+ /**
45
+ * Load the manifest from its source, verify the threshold signatures against
46
+ * the supplied root keys, and cache it for getCurrentManifest().
47
+ * Throws on signature failure, expiry, or missing nodes.
48
+ */
49
+ loadAndVerify(rootKeys: readonly string[], threshold: number): Promise<ConsortiumManifest>;
50
+ /**
51
+ * Returns the cached manifest from the last successful loadAndVerify() call.
52
+ * Returns null if loadAndVerify() has not been called or failed.
53
+ */
54
+ getCurrentManifest(): ConsortiumManifest | null;
55
+ /**
56
+ * Replace the cached manifest with a newly polled manifest.
57
+ * Called by SignalingManager.handleManifestPollResponse() after successful poll
58
+ * verification. This ensures IDirectoryChallengeVerifier (which reads from
59
+ * getCurrentManifest()) picks up key rotations reflected in the polled manifest.
60
+ */
61
+ updateManifest(manifest: ConsortiumManifest): void;
62
+ }
63
+ /**
64
+ * Verifies the directory's step-5 challenge response.
65
+ *
66
+ * Step 5 TBS (RFC 8032 signing input):
67
+ * UTF-8('cello-directory-auth-challenge-v1\n') +
68
+ * UTF-8(nodeId) + UTF-8('\n') +
69
+ * UTF-8(agentPubkeyHex) + UTF-8('\n') +
70
+ * UTF-8(nonceHex) + UTF-8('\n') +
71
+ * UTF-8(isoTimestamp)
72
+ *
73
+ * Production: ManifestDirectoryChallengeVerifier reads the node's pubkey from
74
+ * the in-memory manifest loaded by IManifestProvider, verifies with @noble/curves.
75
+ * Tests: TestDirectoryChallengeVerifier — configurable pass/fail per nodeId.
76
+ */
77
+ /** Result from a successful challenge verification. */
78
+ export interface ChallengeVerifyOk {
79
+ valid: true;
80
+ }
81
+ /** Result from a failed challenge verification — reason distinguishes the failure cause. */
82
+ export interface ChallengeVerifyFail {
83
+ valid: false;
84
+ /** 'key_not_in_manifest': nodeId not present in the loaded manifest.
85
+ * 'signature_invalid': nodeId found but Ed25519 signature failed verification. */
86
+ reason: "key_not_in_manifest" | "signature_invalid";
87
+ }
88
+ export type ChallengeVerifyResult = ChallengeVerifyOk | ChallengeVerifyFail;
89
+ export interface IDirectoryChallengeVerifier {
90
+ /**
91
+ * Verify an Ed25519 signature over tbsBytes for the given nodeId.
92
+ * The nodeId is looked up in the loaded manifest; the corresponding pubkey
93
+ * is used for verification.
94
+ * Returns ChallengeVerifyOk if valid.
95
+ * Returns ChallengeVerifyFail with reason 'key_not_in_manifest' if nodeId absent.
96
+ * Returns ChallengeVerifyFail with reason 'signature_invalid' if nodeId found but sig fails.
97
+ * Never throws — all errors produce a ChallengeVerifyFail.
98
+ */
99
+ verifyChallenge(nodeId: string, tbsBytes: Uint8Array, signatureHex: string): ChallengeVerifyResult;
100
+ }
101
+ /**
102
+ * Schedules background manifest poll callbacks.
103
+ *
104
+ * Production: RandomizedPollScheduler — fires the callback after a random interval
105
+ * in the 6–12 hour window.
106
+ * Tests: ImmediatePollScheduler — fires once with a configurable delay (0ms default).
107
+ */
108
+ export interface IManifestPollScheduler {
109
+ /**
110
+ * Schedule the next poll call. After the delay elapses, callbackFn() is invoked.
111
+ */
112
+ scheduleNext(callbackFn: () => Promise<void>): void;
113
+ /** Cancel any pending scheduled callback. Idempotent. */
114
+ cancel(): void;
115
+ }
116
+ /**
117
+ * Provides the directory node's unique identifier and signing capability.
118
+ * Each directory node has its own Ed25519 private key — never shared between nodes.
119
+ *
120
+ * Production: SecretsManagerDirectoryKeyProvider reads from AWS Secrets Manager at startup.
121
+ * Tests: TestDirectoryKeyProvider — takes { nodeId, privateKeyHex } in constructor.
122
+ */
123
+ export interface DirectoryKeyProvider {
124
+ /** Returns the node's unique identifier string. */
125
+ getNodeId(): string;
126
+ /**
127
+ * Sign tbsBytes with the node's Ed25519 private key (RFC 8032).
128
+ * Returns a 64-byte signature as Uint8Array.
129
+ */
130
+ sign(tbsBytes: Uint8Array): Promise<Uint8Array>;
131
+ }
132
+ /**
133
+ * Provides the current consortium manifest for manifest_poll_response frames.
134
+ *
135
+ * Production: FileDirectoryManifestStore reads the manifest JSON deployed alongside
136
+ * the directory binary.
137
+ * Tests: TestDirectoryManifestStore — takes a fixed ConsortiumManifest.
138
+ */
139
+ export interface DirectoryManifestStore {
140
+ /** Returns the current consortium manifest. Never throws in production. */
141
+ getCurrentManifest(): ConsortiumManifest;
142
+ }
143
+ //# sourceMappingURL=manifest-interfaces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-interfaces.d.ts","sourceRoot":"","sources":["../src/manifest-interfaces.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAIzE;;;;;;;GAOG;AACH,MAAM,WAAW,qBAAqB;IACpC,gFAAgF;IAChF,kBAAkB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,mEAAmE;IACnE,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,aAAa,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC3F;;;OAGG;IACH,kBAAkB,IAAI,kBAAkB,GAAG,IAAI,CAAC;IAChD;;;;;OAKG;IACH,cAAc,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACpD;AAED;;;;;;;;;;;;;GAaG;AACH,uDAAuD;AACvD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,IAAI,CAAC;CACb;AAED,4FAA4F;AAC5F,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,KAAK,CAAC;IACb;uFACmF;IACnF,MAAM,EAAE,qBAAqB,GAAG,mBAAmB,CAAC;CACrD;AAED,MAAM,MAAM,qBAAqB,GAAG,iBAAiB,GAAG,mBAAmB,CAAC;AAE5E,MAAM,WAAW,2BAA2B;IAC1C;;;;;;;;OAQG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,GAAG,qBAAqB,CAAC;CACpG;AAED;;;;;;GAMG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACpD,yDAAyD;IACzD,MAAM,IAAI,IAAI,CAAC;CAChB;AAID;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,mDAAmD;IACnD,SAAS,IAAI,MAAM,CAAC;IACpB;;;OAGG;IACH,IAAI,CAAC,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACjD;AAED;;;;;;GAMG;AACH,MAAM,WAAW,sBAAsB;IACrC,2EAA2E;IAC3E,kBAAkB,IAAI,kBAAkB,CAAC;CAC1C"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * M7-MANIFEST-002 — Manifest-related interface definitions for the CELLO client daemon.
3
+ *
4
+ * These interfaces decouple the daemon and SignalingManager from their concrete
5
+ * implementations. All interfaces are narrow by design.
6
+ *
7
+ * Interface overview:
8
+ *
9
+ * Client-side (daemon / SignalingManager):
10
+ * IManifestVersionStore — persists the last-seen manifest version (monotonicity gate)
11
+ * IManifestProvider — loads, verifies, and caches the consortium manifest
12
+ * IDirectoryChallengeVerifier — verifies the directory's step-5 Ed25519 challenge response
13
+ * IManifestPollScheduler — schedules background manifest poll calls
14
+ *
15
+ * Directory-side (directory node):
16
+ * DirectoryKeyProvider — provides the per-node nodeId and signs TBS bytes
17
+ * DirectoryManifestStore — provides the current consortium manifest for poll responses
18
+ *
19
+ * Crypto reference: RFC 8032 (Ed25519).
20
+ */
21
+ export {};
22
+ //# sourceMappingURL=manifest-interfaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-interfaces.js","sourceRoot":"","sources":["../src/manifest-interfaces.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * M7-MANIFEST-002 — In-memory stubs for manifest interfaces.
3
+ *
4
+ * These stubs are test-only implementations of the interfaces defined in
5
+ * manifest-interfaces.ts. They are intentionally simple — no file I/O, no external calls.
6
+ *
7
+ * Crypto reference: RFC 8032 (Ed25519).
8
+ */
9
+ import type { ConsortiumManifest } from "@cello-protocol/protocol-types";
10
+ import type { IManifestVersionStore, IManifestProvider, IDirectoryChallengeVerifier, ChallengeVerifyResult, IManifestPollScheduler, DirectoryKeyProvider, DirectoryManifestStore } from "./manifest-interfaces.js";
11
+ /**
12
+ * InMemoryManifestVersionStore — starts at null, holds state in memory.
13
+ *
14
+ * // SQLCipher: InMemory stub used — real store wired in DAEMON-001
15
+ */
16
+ export declare class InMemoryManifestVersionStore implements IManifestVersionStore {
17
+ #private;
18
+ getLastSeenVersion(): Promise<number | null>;
19
+ persistVersion(version: number): Promise<void>;
20
+ }
21
+ /**
22
+ * TestManifestProvider — takes a pre-built ConsortiumManifest, skips file read.
23
+ *
24
+ * loadAndVerify() returns the supplied manifest without signature verification.
25
+ */
26
+ export declare class TestManifestProvider implements IManifestProvider {
27
+ #private;
28
+ constructor(manifest: ConsortiumManifest);
29
+ loadAndVerify(_rootKeys: readonly string[], _threshold: number): Promise<ConsortiumManifest>;
30
+ getCurrentManifest(): ConsortiumManifest | null;
31
+ updateManifest(manifest: ConsortiumManifest): void;
32
+ }
33
+ /**
34
+ * TestDirectoryChallengeVerifier — configurable pass/fail per nodeId.
35
+ * Rejected nodeIds produce 'key_not_in_manifest'; all other failures produce 'signature_invalid'.
36
+ */
37
+ export declare class TestDirectoryChallengeVerifier implements IDirectoryChallengeVerifier {
38
+ #private;
39
+ rejectNodeId(nodeId: string): void;
40
+ invalidateSig(nodeId: string): void;
41
+ verifyChallenge(nodeId: string, _tbsBytes: Uint8Array, _signatureHex: string): ChallengeVerifyResult;
42
+ }
43
+ /**
44
+ * ManifestDirectoryChallengeVerifier — reads pubkey from in-memory manifest, verifies
45
+ * with @noble/curves Ed25519 (RFC 8032). Production client-side verifier.
46
+ *
47
+ * Pseudocode (RFC 8032 — Ed25519 verify):
48
+ * 1. Get the current manifest from IManifestProvider.
49
+ * 2. Find the node entry with nodeId == nodeId.
50
+ * 3. Decode pubkey hex (32 bytes) and signatureHex (64 bytes).
51
+ * 4. Call ed25519.verify(sigBytes, tbsBytes, pubkeyBytes).
52
+ * 5. Return the result; catch all errors and return false.
53
+ */
54
+ export declare class ManifestDirectoryChallengeVerifier implements IDirectoryChallengeVerifier {
55
+ #private;
56
+ constructor(manifestProvider: IManifestProvider);
57
+ verifyChallenge(nodeId: string, tbsBytes: Uint8Array, signatureHex: string): ChallengeVerifyResult;
58
+ }
59
+ /**
60
+ * ImmediatePollScheduler — fires once with a configurable delay (0ms default).
61
+ */
62
+ export declare class ImmediatePollScheduler implements IManifestPollScheduler {
63
+ #private;
64
+ constructor(delayMs?: number);
65
+ scheduleNext(callbackFn: () => Promise<void>): void;
66
+ cancel(): void;
67
+ }
68
+ /**
69
+ * TestDirectoryKeyProvider — takes { nodeId, privateKeyHex } in constructor.
70
+ *
71
+ * Crypto reference: RFC 8032 (Ed25519).
72
+ */
73
+ export declare class TestDirectoryKeyProvider implements DirectoryKeyProvider {
74
+ #private;
75
+ constructor(opts: {
76
+ nodeId: string;
77
+ privateKeyHex: string;
78
+ });
79
+ getNodeId(): string;
80
+ /** Sign tbsBytes with Ed25519 private key (RFC 8032). Returns 64-byte Uint8Array. */
81
+ sign(tbsBytes: Uint8Array): Promise<Uint8Array>;
82
+ /** Returns the corresponding Ed25519 public key (32 bytes) as hex. */
83
+ getPublicKeyHex(): string;
84
+ }
85
+ /**
86
+ * TestDirectoryManifestStore — holds a fixed ConsortiumManifest.
87
+ */
88
+ export declare class TestDirectoryManifestStore implements DirectoryManifestStore {
89
+ #private;
90
+ constructor(manifest: ConsortiumManifest);
91
+ getCurrentManifest(): ConsortiumManifest;
92
+ }
93
+ //# sourceMappingURL=manifest-stubs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-stubs.d.ts","sourceRoot":"","sources":["../src/manifest-stubs.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,KAAK,EACV,qBAAqB,EACrB,iBAAiB,EACjB,2BAA2B,EAC3B,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,0BAA0B,CAAC;AAIlC;;;;GAIG;AACH,qBAAa,4BAA6B,YAAW,qBAAqB;;IAGlE,kBAAkB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAI5C,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGrD;AAID;;;;GAIG;AACH,qBAAa,oBAAqB,YAAW,iBAAiB;;gBAIhD,QAAQ,EAAE,kBAAkB;IAIlC,aAAa,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAKlG,kBAAkB,IAAI,kBAAkB,GAAG,IAAI;IAI/C,cAAc,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI;CAInD;AAID;;;GAGG;AACH,qBAAa,8BAA+B,YAAW,2BAA2B;;IAIhF,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAIlC,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAInC,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,GAAG,qBAAqB;CASrG;AAED;;;;;;;;;;GAUG;AACH,qBAAa,kCAAmC,YAAW,2BAA2B;;gBAGxE,gBAAgB,EAAE,iBAAiB;IAI/C,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,GAAG,qBAAqB;CA2BnG;AAID;;GAEG;AACH,qBAAa,sBAAuB,YAAW,sBAAsB;;gBAIvD,OAAO,SAAI;IAIvB,YAAY,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAWnD,MAAM,IAAI,IAAI;CAMf;AAID;;;;GAIG;AACH,qBAAa,wBAAyB,YAAW,oBAAoB;;gBAIvD,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE;IAQ3D,SAAS,IAAI,MAAM;IAInB,qFAAqF;IAC/E,IAAI,CAAC,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAIrD,sEAAsE;IACtE,eAAe,IAAI,MAAM;CAG1B;AAID;;GAEG;AACH,qBAAa,0BAA2B,YAAW,sBAAsB;;gBAG3D,QAAQ,EAAE,kBAAkB;IAIxC,kBAAkB,IAAI,kBAAkB;CAGzC"}
@@ -0,0 +1,192 @@
1
+ /**
2
+ * M7-MANIFEST-002 — In-memory stubs for manifest interfaces.
3
+ *
4
+ * These stubs are test-only implementations of the interfaces defined in
5
+ * manifest-interfaces.ts. They are intentionally simple — no file I/O, no external calls.
6
+ *
7
+ * Crypto reference: RFC 8032 (Ed25519).
8
+ */
9
+ import { ed25519 } from "@noble/curves/ed25519.js";
10
+ // ─── IManifestVersionStore ───────────────────────────────────────────────────
11
+ /**
12
+ * InMemoryManifestVersionStore — starts at null, holds state in memory.
13
+ *
14
+ * // SQLCipher: InMemory stub used — real store wired in DAEMON-001
15
+ */
16
+ export class InMemoryManifestVersionStore {
17
+ #lastSeenVersion = null;
18
+ async getLastSeenVersion() {
19
+ return this.#lastSeenVersion;
20
+ }
21
+ async persistVersion(version) {
22
+ this.#lastSeenVersion = version;
23
+ }
24
+ }
25
+ // ─── IManifestProvider ───────────────────────────────────────────────────────
26
+ /**
27
+ * TestManifestProvider — takes a pre-built ConsortiumManifest, skips file read.
28
+ *
29
+ * loadAndVerify() returns the supplied manifest without signature verification.
30
+ */
31
+ export class TestManifestProvider {
32
+ #manifest;
33
+ #loaded = null;
34
+ constructor(manifest) {
35
+ this.#manifest = manifest;
36
+ }
37
+ async loadAndVerify(_rootKeys, _threshold) {
38
+ this.#loaded = this.#manifest;
39
+ return this.#manifest;
40
+ }
41
+ getCurrentManifest() {
42
+ return this.#loaded;
43
+ }
44
+ updateManifest(manifest) {
45
+ this.#manifest = manifest;
46
+ this.#loaded = manifest;
47
+ }
48
+ }
49
+ // ─── IDirectoryChallengeVerifier ─────────────────────────────────────────────
50
+ /**
51
+ * TestDirectoryChallengeVerifier — configurable pass/fail per nodeId.
52
+ * Rejected nodeIds produce 'key_not_in_manifest'; all other failures produce 'signature_invalid'.
53
+ */
54
+ export class TestDirectoryChallengeVerifier {
55
+ #rejectedNodeIds = new Set();
56
+ #invalidSigNodeIds = new Set();
57
+ rejectNodeId(nodeId) {
58
+ this.#rejectedNodeIds.add(nodeId);
59
+ }
60
+ invalidateSig(nodeId) {
61
+ this.#invalidSigNodeIds.add(nodeId);
62
+ }
63
+ verifyChallenge(nodeId, _tbsBytes, _signatureHex) {
64
+ if (this.#rejectedNodeIds.has(nodeId)) {
65
+ return { valid: false, reason: "key_not_in_manifest" };
66
+ }
67
+ if (this.#invalidSigNodeIds.has(nodeId)) {
68
+ return { valid: false, reason: "signature_invalid" };
69
+ }
70
+ return { valid: true };
71
+ }
72
+ }
73
+ /**
74
+ * ManifestDirectoryChallengeVerifier — reads pubkey from in-memory manifest, verifies
75
+ * with @noble/curves Ed25519 (RFC 8032). Production client-side verifier.
76
+ *
77
+ * Pseudocode (RFC 8032 — Ed25519 verify):
78
+ * 1. Get the current manifest from IManifestProvider.
79
+ * 2. Find the node entry with nodeId == nodeId.
80
+ * 3. Decode pubkey hex (32 bytes) and signatureHex (64 bytes).
81
+ * 4. Call ed25519.verify(sigBytes, tbsBytes, pubkeyBytes).
82
+ * 5. Return the result; catch all errors and return false.
83
+ */
84
+ export class ManifestDirectoryChallengeVerifier {
85
+ #manifestProvider;
86
+ constructor(manifestProvider) {
87
+ this.#manifestProvider = manifestProvider;
88
+ }
89
+ verifyChallenge(nodeId, tbsBytes, signatureHex) {
90
+ try {
91
+ const manifest = this.#manifestProvider.getCurrentManifest();
92
+ if (!manifest)
93
+ return { valid: false, reason: "key_not_in_manifest" };
94
+ const node = manifest.nodes.find((n) => n.nodeId === nodeId);
95
+ if (!node)
96
+ return { valid: false, reason: "key_not_in_manifest" };
97
+ // Decode 32-byte pubkey from 64-char hex (RFC 8032)
98
+ if (node.pubkey.length !== 64 || !/^[0-9a-fA-F]+$/.test(node.pubkey)) {
99
+ return { valid: false, reason: "signature_invalid" };
100
+ }
101
+ const pubkeyBytes = hexToBytes(node.pubkey);
102
+ // Decode 64-byte signature from 128-char hex
103
+ if (signatureHex.length !== 128 || !/^[0-9a-fA-F]+$/.test(signatureHex)) {
104
+ return { valid: false, reason: "signature_invalid" };
105
+ }
106
+ const sigBytes = hexToBytes(signatureHex);
107
+ // RFC 8032: ed25519.verify(signature, message, publicKey)
108
+ const ok = ed25519.verify(sigBytes, tbsBytes, pubkeyBytes);
109
+ return ok ? { valid: true } : { valid: false, reason: "signature_invalid" };
110
+ }
111
+ catch {
112
+ return { valid: false, reason: "signature_invalid" };
113
+ }
114
+ }
115
+ }
116
+ // ─── IManifestPollScheduler ───────────────────────────────────────────────────
117
+ /**
118
+ * ImmediatePollScheduler — fires once with a configurable delay (0ms default).
119
+ */
120
+ export class ImmediatePollScheduler {
121
+ #delayMs;
122
+ #timer = null;
123
+ constructor(delayMs = 0) {
124
+ this.#delayMs = delayMs;
125
+ }
126
+ scheduleNext(callbackFn) {
127
+ this.cancel();
128
+ this.#timer = setTimeout(() => {
129
+ this.#timer = null;
130
+ // Let errors propagate in test context — swallowing them hides assertion failures
131
+ callbackFn().catch((err) => {
132
+ throw err instanceof Error ? err : new Error(String(err));
133
+ });
134
+ }, this.#delayMs);
135
+ }
136
+ cancel() {
137
+ if (this.#timer !== null) {
138
+ clearTimeout(this.#timer);
139
+ this.#timer = null;
140
+ }
141
+ }
142
+ }
143
+ // ─── DirectoryKeyProvider ─────────────────────────────────────────────────────
144
+ /**
145
+ * TestDirectoryKeyProvider — takes { nodeId, privateKeyHex } in constructor.
146
+ *
147
+ * Crypto reference: RFC 8032 (Ed25519).
148
+ */
149
+ export class TestDirectoryKeyProvider {
150
+ #nodeId;
151
+ #privateKeyBytes;
152
+ constructor(opts) {
153
+ this.#nodeId = opts.nodeId;
154
+ if (opts.privateKeyHex.length !== 64) {
155
+ throw new Error(`TestDirectoryKeyProvider: privateKeyHex must be 64 chars (32 bytes), got ${opts.privateKeyHex.length}`);
156
+ }
157
+ this.#privateKeyBytes = hexToBytes(opts.privateKeyHex);
158
+ }
159
+ getNodeId() {
160
+ return this.#nodeId;
161
+ }
162
+ /** Sign tbsBytes with Ed25519 private key (RFC 8032). Returns 64-byte Uint8Array. */
163
+ async sign(tbsBytes) {
164
+ return ed25519.sign(tbsBytes, this.#privateKeyBytes);
165
+ }
166
+ /** Returns the corresponding Ed25519 public key (32 bytes) as hex. */
167
+ getPublicKeyHex() {
168
+ return Buffer.from(ed25519.getPublicKey(this.#privateKeyBytes)).toString("hex");
169
+ }
170
+ }
171
+ // ─── DirectoryManifestStore ────────────────────────────────────────────────────
172
+ /**
173
+ * TestDirectoryManifestStore — holds a fixed ConsortiumManifest.
174
+ */
175
+ export class TestDirectoryManifestStore {
176
+ #manifest;
177
+ constructor(manifest) {
178
+ this.#manifest = manifest;
179
+ }
180
+ getCurrentManifest() {
181
+ return this.#manifest;
182
+ }
183
+ }
184
+ // ─── Internal helpers ────────────────────────────────────────────────────────
185
+ function hexToBytes(hex) {
186
+ const bytes = new Uint8Array(hex.length / 2);
187
+ for (let i = 0; i < bytes.length; i++) {
188
+ bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
189
+ }
190
+ return bytes;
191
+ }
192
+ //# sourceMappingURL=manifest-stubs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-stubs.js","sourceRoot":"","sources":["../src/manifest-stubs.ts"],"names":[],"mappings":"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"}
package/dist/node.d.ts CHANGED
@@ -76,7 +76,18 @@ import type { CelloNode, CreateNodeOptions } from "./types.js";
76
76
  * - Transports: TCP + WebSockets
77
77
  * - Security: Noise ONLY (XX pattern, RFC: https://noiseprotocol.org/noise.html)
78
78
  * - Muxer: Yamux
79
- * - Services: identify, circuitRelayServer (advertises HOP), DCuTR
79
+ * - Services: identify, circuitRelayServer (advertises HOP), AutoNAT, DCuTR
80
+ *
81
+ * CELLO-M7-TRANSPORT-001:
82
+ * - autoNAT() is added to ALL nodes. On client nodes (session / standing
83
+ * receiver) it probes connected directory nodes for dial-back to determine
84
+ * dialability; on directory nodes it serves the responder role, answering
85
+ * dial-back requests (AC-011). Protocol: /libp2p/autonat/1.0.0.
86
+ * - dcutr() is included for session nodes (and the default/service nodes) so a
87
+ * relay-fallback connection can be hole-punch upgraded to direct, but is
88
+ * OMITTED for standing-receiver nodes (nodeType==='standing_receiver') — a
89
+ * standing receiver only needs to know its own dialability, not upgrade
90
+ * existing connections (AC-002).
80
91
  */
81
92
  export declare function createNode(opts: CreateNodeOptions): Promise<CelloNode>;
82
93
  //# sourceMappingURL=node.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AAeH,OAAO,KAAK,EACV,SAAS,EAET,iBAAiB,EAClB,MAAM,YAAY,CAAC;AA4KpB;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,CAAC,CAmC5E"}
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AAgBH,OAAO,KAAK,EACV,SAAS,EAET,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAkTpB;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,CAAC,CA0E5E"}