wire-mesh-core 0.0.0 → 1.0.2

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.
Files changed (80) hide show
  1. package/README.md +32 -0
  2. package/dist/adapters/frame-codec.cjs +37 -0
  3. package/dist/adapters/frame-codec.d.cts +12 -0
  4. package/dist/adapters/frame-codec.d.mts +12 -0
  5. package/dist/adapters/frame-codec.mjs +33 -0
  6. package/dist/adapters/memory-storage.cjs +14 -0
  7. package/dist/adapters/memory-storage.d.cts +5 -0
  8. package/dist/adapters/memory-storage.d.mts +5 -0
  9. package/dist/adapters/memory-storage.mjs +13 -0
  10. package/dist/adapters/node-identity.cjs +55 -0
  11. package/dist/adapters/node-identity.d.cts +11 -0
  12. package/dist/adapters/node-identity.d.mts +11 -0
  13. package/dist/adapters/node-identity.mjs +52 -0
  14. package/dist/adapters/system-clock.cjs +8 -0
  15. package/dist/adapters/system-clock.d.cts +5 -0
  16. package/dist/adapters/system-clock.d.mts +5 -0
  17. package/dist/adapters/system-clock.mjs +7 -0
  18. package/dist/adapters/tcp-transport.cjs +154 -0
  19. package/dist/adapters/tcp-transport.d.cts +5 -0
  20. package/dist/adapters/tcp-transport.d.mts +5 -0
  21. package/dist/adapters/tcp-transport.mjs +153 -0
  22. package/dist/adapters/tls-transport.cjs +178 -0
  23. package/dist/adapters/tls-transport.d.cts +9 -0
  24. package/dist/adapters/tls-transport.d.mts +9 -0
  25. package/dist/adapters/tls-transport.mjs +177 -0
  26. package/dist/clock-DiSx-WKM.d.cts +7 -0
  27. package/dist/clock-DiSx-WKM.d.mts +7 -0
  28. package/dist/domain/device-id.cjs +27 -0
  29. package/dist/domain/device-id.d.cts +9 -0
  30. package/dist/domain/device-id.d.mts +9 -0
  31. package/dist/domain/device-id.mjs +24 -0
  32. package/dist/domain/handshake.cjs +23 -0
  33. package/dist/domain/handshake.d.cts +16 -0
  34. package/dist/domain/handshake.d.mts +16 -0
  35. package/dist/domain/handshake.mjs +21 -0
  36. package/dist/domain/mesh-session.cjs +439 -0
  37. package/dist/domain/mesh-session.d.cts +101 -0
  38. package/dist/domain/mesh-session.d.mts +101 -0
  39. package/dist/domain/mesh-session.mjs +436 -0
  40. package/dist/domain/relay-hub.cjs +99 -0
  41. package/dist/domain/relay-hub.d.cts +10 -0
  42. package/dist/domain/relay-hub.d.mts +10 -0
  43. package/dist/domain/relay-hub.mjs +98 -0
  44. package/dist/domain/revocation-view.cjs +22 -0
  45. package/dist/domain/revocation-view.d.cts +12 -0
  46. package/dist/domain/revocation-view.d.mts +12 -0
  47. package/dist/domain/revocation-view.mjs +21 -0
  48. package/dist/domain/tokens.cjs +300 -0
  49. package/dist/domain/tokens.d.cts +86 -0
  50. package/dist/domain/tokens.d.mts +86 -0
  51. package/dist/domain/tokens.mjs +296 -0
  52. package/dist/generated/protocol.cjs +466 -0
  53. package/dist/generated/protocol.d.cts +2 -0
  54. package/dist/generated/protocol.d.mts +2 -0
  55. package/dist/generated/protocol.mjs +382 -0
  56. package/dist/generated/runtime.cjs +8 -0
  57. package/dist/generated/runtime.d.cts +2 -0
  58. package/dist/generated/runtime.d.mts +2 -0
  59. package/dist/generated/runtime.mjs +2 -0
  60. package/dist/identity-BRLEUfVY.d.cts +17 -0
  61. package/dist/identity-qNkmGytv.d.mts +17 -0
  62. package/dist/ports/clock.cjs +0 -0
  63. package/dist/ports/clock.d.cts +2 -0
  64. package/dist/ports/clock.d.mts +2 -0
  65. package/dist/ports/clock.mjs +1 -0
  66. package/dist/ports/identity.cjs +0 -0
  67. package/dist/ports/identity.d.cts +2 -0
  68. package/dist/ports/identity.d.mts +2 -0
  69. package/dist/ports/identity.mjs +1 -0
  70. package/dist/ports/storage.cjs +0 -0
  71. package/dist/ports/storage.d.cts +9 -0
  72. package/dist/ports/storage.d.mts +9 -0
  73. package/dist/ports/storage.mjs +1 -0
  74. package/dist/ports/transport.cjs +0 -0
  75. package/dist/ports/transport.d.cts +29 -0
  76. package/dist/ports/transport.d.mts +29 -0
  77. package/dist/ports/transport.mjs +1 -0
  78. package/dist/protocol-B26-5VX7.d.cts +1112 -0
  79. package/dist/protocol-B26-5VX7.d.mts +1112 -0
  80. package/package.json +130 -2
package/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # wire-mesh-core
2
+
3
+ The TypeScript implementation of wire-mesh's protocol, built ports/adapters: domain logic (`src/domain/`) depends only on port contracts (`src/ports/`) and the [cddl.js](https://github.com/ExaDev/cddl.js)-generated Zod schemas (`src/generated/protocol.ts`, regenerated from `../../../spec/protocol.cddl` via `generate.ts` -- never edited by hand), never on a specific adapter's own imports.
4
+
5
+ ## Ports
6
+
7
+ - **Transport** (`src/ports/transport.ts`) -- an async contract for sending/receiving `Frame` values over a connection. Adapter: `src/adapters/tcp-transport.ts` (length-prefixed CBOR frames over plain `node:net`, matching Cascade's own transport shape).
8
+ - **Storage** (`src/ports/storage.ts`) -- an async key/value contract. Adapter: `src/adapters/memory-storage.ts` (in-process, for tests and single-process nodes).
9
+ - **Identity** (`src/ports/identity.ts`) -- device-id derivation and COSE signing/verification. Adapter: `src/adapters/node-identity.ts` (Web Crypto, ECDSA P-256 and Ed25519 -- the two algorithms the spec's own conformance vectors use).
10
+ - **Clock** (`src/ports/clock.ts`) -- injected time, never `Date.now()` pulled directly into domain logic. Adapter: `src/adapters/system-clock.ts`.
11
+
12
+ ## Domain logic implemented
13
+
14
+ - **Handshake negotiation** (`src/domain/handshake.ts`) -- protocol-version and capability-domain negotiation between two peers, the mechanism agent-comms issue #31 is fixed by.
15
+ - **Capability-token verification** (`src/domain/tokens.ts`) -- the full chain tokens.cddl documents: COSE_Sign1 signature verification, the self-certifying issuer-key check (`sha256(issuer-key.public-key) == issuer`), expiry/not-before, issuer-matched revocation (only a token's own issuer's signed revocation-entry counts, checked across every ancestor in the delegation chain, not just the leaf), and recursive delegation-chain narrowing across all three axes of authority: a delegated token's issuer must be its parent's bearer (the chain is unbroken), its expiry must not exceed its parent's, and its scope must narrow its parent's (identical kind, equal-or-descendant path when the parent carries one) with an identical capability verb (the verb grammar has no sub-verb relation, so a different verb is different authority, not narrower). Also exports `verifyRevocationEntry` for ingesting gossiped revocation-announce frames: each entry is itself a signed, self-certifying COSE_Sign1 over revocation-claims, verified before it may enter the revocation view.
16
+
17
+ ## Deliberately deferred
18
+
19
+ Every other frame family (management/exec, streaming, data-domain, discovery, coordinator election) is covered by schema validation only -- `conformance-check` proves the generated schemas decode and re-encode every golden vector byte-exactly, including these families, but no domain-level business logic (dispatch, session bookkeeping, PTY/proc lifecycle, oplog replication, coordinator term tracking) exists for them yet. This first pass deliberately scopes domain logic to transport + handshake + tokens (the families with real business rules worth pinning down before the others), because the conformance suite already covers every family's wire shape either way -- nothing here is unverified, only unimplemented. (The federation link protocol no longer exists in the spec at all: cross-scope sharing is ordinary capability-token delegation, and `src/domain/tokens.ts`'s verification chain is exactly the mechanism that governs it.)
20
+
21
+ ## Regenerating the schema
22
+
23
+ ```sh
24
+ pnpm generate # rewrites src/generated/protocol.ts from ../../../spec/protocol.cddl
25
+ pnpm test # confirms it still round-trips every conformance vector
26
+ ```
27
+
28
+ CI regenerates and diffs against the committed file, the same way `conformance/`'s own vector files are verified never to drift from hand-editing.
29
+
30
+ ## Publishing
31
+
32
+ Published to npm as [`wire-mesh-core`](https://www.npmjs.com/package/wire-mesh-core). A push to `main` runs `semantic-release` (`ts/packages/core/release.config.ts`, tagged `core-v*` to keep this package's releases distinct from any other publishable package in the workspace), which versions from conventional-commit messages, builds `dist/` fresh, and publishes it -- `dist/` itself is never committed to the repo.
@@ -0,0 +1,37 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_generated_protocol = require("../generated/protocol.cjs");
3
+ let cbor2 = require("cbor2");
4
+ //#region src/adapters/frame-codec.ts
5
+ function messageFromFrame(frame) {
6
+ return new Uint8Array((0, cbor2.encode)(frame, cbor2.cdeEncodeOptions));
7
+ }
8
+ /** A frame that fails schema validation, caught separately from a decode failure so it can be dropped without disconnecting. */
9
+ var SchemaInvalidFrameError = class extends Error {
10
+ constructor(message) {
11
+ super(message);
12
+ this.name = "SchemaInvalidFrameError";
13
+ }
14
+ };
15
+ /** Decodes one message, distinguishing a decode failure (connection-level) from a schema failure (drop this frame, keep the connection). */
16
+ function decodeMessage(data) {
17
+ const decoded = (0, cbor2.decode)(new Uint8Array(data), cbor2.cdeDecodeOptions);
18
+ const result = require_generated_protocol.frameSchema.safeParse(decoded);
19
+ if (!result.success) throw new SchemaInvalidFrameError(result.error.message);
20
+ return result.data;
21
+ }
22
+ /** Best-effort decode of a nested Frame from raw bytes, e.g. a relay-data frame's opaque payload -- unlike decodeMessage, neither a CBOR decode failure nor a schema mismatch is a connection-level failure here: the bytes may simply not be a nested Frame at all (ordinary opaque relay-data with some other meaning), so this returns null instead of throwing either way. */
23
+ function tryDecodeFrame(bytes) {
24
+ let decoded;
25
+ try {
26
+ decoded = (0, cbor2.decode)(bytes, cbor2.cdeDecodeOptions);
27
+ } catch {
28
+ return null;
29
+ }
30
+ const result = require_generated_protocol.frameSchema.safeParse(decoded);
31
+ return result.success ? result.data : null;
32
+ }
33
+ //#endregion
34
+ exports.SchemaInvalidFrameError = SchemaInvalidFrameError;
35
+ exports.decodeMessage = decodeMessage;
36
+ exports.messageFromFrame = messageFromFrame;
37
+ exports.tryDecodeFrame = tryDecodeFrame;
@@ -0,0 +1,12 @@
1
+ import { w as Frame } from "../protocol-B26-5VX7.cjs";
2
+ //#region src/adapters/frame-codec.d.ts
3
+ export declare function messageFromFrame(frame: Frame): Uint8Array<ArrayBuffer>;
4
+ /** A frame that fails schema validation, caught separately from a decode failure so it can be dropped without disconnecting. */
5
+ export declare class SchemaInvalidFrameError extends Error {
6
+ constructor(message: string);
7
+ }
8
+ /** Decodes one message, distinguishing a decode failure (connection-level) from a schema failure (drop this frame, keep the connection). */
9
+ export declare function decodeMessage(data: Readonly<ArrayBuffer>): Frame;
10
+ /** Best-effort decode of a nested Frame from raw bytes, e.g. a relay-data frame's opaque payload -- unlike decodeMessage, neither a CBOR decode failure nor a schema mismatch is a connection-level failure here: the bytes may simply not be a nested Frame at all (ordinary opaque relay-data with some other meaning), so this returns null instead of throwing either way. */
11
+ export declare function tryDecodeFrame(bytes: Readonly<Uint8Array>): Frame | null;
12
+ //#endregion
@@ -0,0 +1,12 @@
1
+ import { w as Frame } from "../protocol-B26-5VX7.mjs";
2
+ //#region src/adapters/frame-codec.d.ts
3
+ export declare function messageFromFrame(frame: Frame): Uint8Array<ArrayBuffer>;
4
+ /** A frame that fails schema validation, caught separately from a decode failure so it can be dropped without disconnecting. */
5
+ export declare class SchemaInvalidFrameError extends Error {
6
+ constructor(message: string);
7
+ }
8
+ /** Decodes one message, distinguishing a decode failure (connection-level) from a schema failure (drop this frame, keep the connection). */
9
+ export declare function decodeMessage(data: Readonly<ArrayBuffer>): Frame;
10
+ /** Best-effort decode of a nested Frame from raw bytes, e.g. a relay-data frame's opaque payload -- unlike decodeMessage, neither a CBOR decode failure nor a schema mismatch is a connection-level failure here: the bytes may simply not be a nested Frame at all (ordinary opaque relay-data with some other meaning), so this returns null instead of throwing either way. */
11
+ export declare function tryDecodeFrame(bytes: Readonly<Uint8Array>): Frame | null;
12
+ //#endregion
@@ -0,0 +1,33 @@
1
+ import { frameSchema } from "../generated/protocol.mjs";
2
+ import { cdeDecodeOptions, cdeEncodeOptions, decode, encode } from "cbor2";
3
+ //#region src/adapters/frame-codec.ts
4
+ function messageFromFrame(frame) {
5
+ return new Uint8Array(encode(frame, cdeEncodeOptions));
6
+ }
7
+ /** A frame that fails schema validation, caught separately from a decode failure so it can be dropped without disconnecting. */
8
+ var SchemaInvalidFrameError = class extends Error {
9
+ constructor(message) {
10
+ super(message);
11
+ this.name = "SchemaInvalidFrameError";
12
+ }
13
+ };
14
+ /** Decodes one message, distinguishing a decode failure (connection-level) from a schema failure (drop this frame, keep the connection). */
15
+ function decodeMessage(data) {
16
+ const decoded = decode(new Uint8Array(data), cdeDecodeOptions);
17
+ const result = frameSchema.safeParse(decoded);
18
+ if (!result.success) throw new SchemaInvalidFrameError(result.error.message);
19
+ return result.data;
20
+ }
21
+ /** Best-effort decode of a nested Frame from raw bytes, e.g. a relay-data frame's opaque payload -- unlike decodeMessage, neither a CBOR decode failure nor a schema mismatch is a connection-level failure here: the bytes may simply not be a nested Frame at all (ordinary opaque relay-data with some other meaning), so this returns null instead of throwing either way. */
22
+ function tryDecodeFrame(bytes) {
23
+ let decoded;
24
+ try {
25
+ decoded = decode(bytes, cdeDecodeOptions);
26
+ } catch {
27
+ return null;
28
+ }
29
+ const result = frameSchema.safeParse(decoded);
30
+ return result.success ? result.data : null;
31
+ }
32
+ //#endregion
33
+ export { SchemaInvalidFrameError, decodeMessage, messageFromFrame, tryDecodeFrame };
@@ -0,0 +1,14 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/adapters/memory-storage.ts
3
+ /** An in-process KeyValueStorage backed by a Map -- for tests and single-process nodes; a real deployment substitutes a persistent adapter behind the same contract without touching anything that depends on the port. Not declared `async`: every operation is genuinely synchronous under the hood, so the contract's Promise return is satisfied directly via Promise.resolve() rather than an async function with no await in its body. */
4
+ function createMemoryStorage() {
5
+ const store = /* @__PURE__ */ new Map();
6
+ return {
7
+ get: async (key) => Promise.resolve(store.get(key)),
8
+ set: async (key, value) => Promise.resolve(store.set(key, value)).then(() => void 0),
9
+ delete: async (key) => Promise.resolve(store.delete(key)).then(() => void 0),
10
+ keys: async (prefix) => Promise.resolve([...store.keys()].filter((key) => key.startsWith(prefix)))
11
+ };
12
+ }
13
+ //#endregion
14
+ exports.createMemoryStorage = createMemoryStorage;
@@ -0,0 +1,5 @@
1
+ import { KeyValueStorage } from "../ports/storage.cjs";
2
+ //#region src/adapters/memory-storage.d.ts
3
+ /** An in-process KeyValueStorage backed by a Map -- for tests and single-process nodes; a real deployment substitutes a persistent adapter behind the same contract without touching anything that depends on the port. Not declared `async`: every operation is genuinely synchronous under the hood, so the contract's Promise return is satisfied directly via Promise.resolve() rather than an async function with no await in its body. */
4
+ export declare function createMemoryStorage(): KeyValueStorage;
5
+ //#endregion
@@ -0,0 +1,5 @@
1
+ import { KeyValueStorage } from "../ports/storage.mjs";
2
+ //#region src/adapters/memory-storage.d.ts
3
+ /** An in-process KeyValueStorage backed by a Map -- for tests and single-process nodes; a real deployment substitutes a persistent adapter behind the same contract without touching anything that depends on the port. Not declared `async`: every operation is genuinely synchronous under the hood, so the contract's Promise return is satisfied directly via Promise.resolve() rather than an async function with no await in its body. */
4
+ export declare function createMemoryStorage(): KeyValueStorage;
5
+ //#endregion
@@ -0,0 +1,13 @@
1
+ //#region src/adapters/memory-storage.ts
2
+ /** An in-process KeyValueStorage backed by a Map -- for tests and single-process nodes; a real deployment substitutes a persistent adapter behind the same contract without touching anything that depends on the port. Not declared `async`: every operation is genuinely synchronous under the hood, so the contract's Promise return is satisfied directly via Promise.resolve() rather than an async function with no await in its body. */
3
+ function createMemoryStorage() {
4
+ const store = /* @__PURE__ */ new Map();
5
+ return {
6
+ get: async (key) => Promise.resolve(store.get(key)),
7
+ set: async (key, value) => Promise.resolve(store.set(key, value)).then(() => void 0),
8
+ delete: async (key) => Promise.resolve(store.delete(key)).then(() => void 0),
9
+ keys: async (prefix) => Promise.resolve([...store.keys()].filter((key) => key.startsWith(prefix)))
10
+ };
11
+ }
12
+ //#endregion
13
+ export { createMemoryStorage };
@@ -0,0 +1,55 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let node_crypto = require("node:crypto");
3
+ //#region src/adapters/node-identity.ts
4
+ const ES256 = -7;
5
+ const EDDSA = -8;
6
+ function algParams(alg) {
7
+ if (alg === ES256) return {
8
+ name: "ECDSA",
9
+ hash: "SHA-256"
10
+ };
11
+ if (alg === EDDSA) return { name: "Ed25519" };
12
+ throw new Error(`unsupported identity-key alg ${String(alg)}`);
13
+ }
14
+ /** Copies into a fresh, non-shared, whole-buffer Uint8Array -- Web Crypto's BufferSource parameters reject a view over a SharedArrayBuffer or a sub-range view, neither of which a caller-supplied Uint8Array is guaranteed not to be. */
15
+ function toBufferSource(bytes) {
16
+ return Uint8Array.from(bytes);
17
+ }
18
+ async function importPublicKey(key) {
19
+ if (key.alg === ES256) return node_crypto.webcrypto.subtle.importKey("raw", toBufferSource(key["public-key"]), {
20
+ name: "ECDSA",
21
+ namedCurve: "P-256"
22
+ }, false, ["verify"]);
23
+ if (key.alg === EDDSA) return node_crypto.webcrypto.subtle.importKey("raw", toBufferSource(key["public-key"]), { name: "Ed25519" }, false, ["verify"]);
24
+ throw new Error(`unsupported identity-key alg ${String(key.alg)}`);
25
+ }
26
+ async function deriveDeviceId(publicKey) {
27
+ return new Uint8Array(await node_crypto.webcrypto.subtle.digest("SHA-256", toBufferSource(publicKey)));
28
+ }
29
+ async function verifyWithPublicKey(key, message, signature) {
30
+ const cryptoKey = await importPublicKey(key);
31
+ return node_crypto.webcrypto.subtle.verify(algParams(key.alg), cryptoKey, toBufferSource(signature), toBufferSource(message));
32
+ }
33
+ /**
34
+ * Builds an IdentityPort from a Web Crypto private key already generated for this node -- ECDSA P-256 (alg -7) or Ed25519 (alg -8), the two algorithms identity-key.alg actually carries in the spec's own conformance vectors. Device-id derivation and signature verification are pure functions of the given key bytes (exported separately above), so they work for an arbitrary issuer-key too, not just this node's own.
35
+ */
36
+ async function createNodeIdentity(privateKey, publicKeyBytes, alg) {
37
+ const identityKey = {
38
+ alg,
39
+ "public-key": toBufferSource(publicKeyBytes)
40
+ };
41
+ return {
42
+ deviceId: await deriveDeviceId(publicKeyBytes),
43
+ identityKey,
44
+ async sign(message) {
45
+ const signature = await node_crypto.webcrypto.subtle.sign(algParams(alg), privateKey, toBufferSource(message));
46
+ return new Uint8Array(signature);
47
+ },
48
+ verify: verifyWithPublicKey,
49
+ deriveDeviceId
50
+ };
51
+ }
52
+ //#endregion
53
+ exports.createNodeIdentity = createNodeIdentity;
54
+ exports.deriveDeviceId = deriveDeviceId;
55
+ exports.verifyWithPublicKey = verifyWithPublicKey;
@@ -0,0 +1,11 @@
1
+ import { j as IdentityKey, v as DeviceId } from "../protocol-B26-5VX7.cjs";
2
+ import { t as IdentityPort } from "../identity-BRLEUfVY.cjs";
3
+ import { webcrypto } from "node:crypto";
4
+ //#region src/adapters/node-identity.d.ts
5
+ export declare function deriveDeviceId(publicKey: Uint8Array): Promise<DeviceId>;
6
+ export declare function verifyWithPublicKey(key: IdentityKey, message: Uint8Array, signature: Uint8Array): Promise<boolean>;
7
+ /**
8
+ * Builds an IdentityPort from a Web Crypto private key already generated for this node -- ECDSA P-256 (alg -7) or Ed25519 (alg -8), the two algorithms identity-key.alg actually carries in the spec's own conformance vectors. Device-id derivation and signature verification are pure functions of the given key bytes (exported separately above), so they work for an arbitrary issuer-key too, not just this node's own.
9
+ */
10
+ export declare function createNodeIdentity(privateKey: webcrypto.CryptoKey, publicKeyBytes: Uint8Array, alg: number): Promise<IdentityPort>;
11
+ //#endregion
@@ -0,0 +1,11 @@
1
+ import { j as IdentityKey, v as DeviceId } from "../protocol-B26-5VX7.mjs";
2
+ import { t as IdentityPort } from "../identity-qNkmGytv.mjs";
3
+ import { webcrypto } from "node:crypto";
4
+ //#region src/adapters/node-identity.d.ts
5
+ export declare function deriveDeviceId(publicKey: Uint8Array): Promise<DeviceId>;
6
+ export declare function verifyWithPublicKey(key: IdentityKey, message: Uint8Array, signature: Uint8Array): Promise<boolean>;
7
+ /**
8
+ * Builds an IdentityPort from a Web Crypto private key already generated for this node -- ECDSA P-256 (alg -7) or Ed25519 (alg -8), the two algorithms identity-key.alg actually carries in the spec's own conformance vectors. Device-id derivation and signature verification are pure functions of the given key bytes (exported separately above), so they work for an arbitrary issuer-key too, not just this node's own.
9
+ */
10
+ export declare function createNodeIdentity(privateKey: webcrypto.CryptoKey, publicKeyBytes: Uint8Array, alg: number): Promise<IdentityPort>;
11
+ //#endregion
@@ -0,0 +1,52 @@
1
+ import { webcrypto } from "node:crypto";
2
+ //#region src/adapters/node-identity.ts
3
+ const ES256 = -7;
4
+ const EDDSA = -8;
5
+ function algParams(alg) {
6
+ if (alg === ES256) return {
7
+ name: "ECDSA",
8
+ hash: "SHA-256"
9
+ };
10
+ if (alg === EDDSA) return { name: "Ed25519" };
11
+ throw new Error(`unsupported identity-key alg ${String(alg)}`);
12
+ }
13
+ /** Copies into a fresh, non-shared, whole-buffer Uint8Array -- Web Crypto's BufferSource parameters reject a view over a SharedArrayBuffer or a sub-range view, neither of which a caller-supplied Uint8Array is guaranteed not to be. */
14
+ function toBufferSource(bytes) {
15
+ return Uint8Array.from(bytes);
16
+ }
17
+ async function importPublicKey(key) {
18
+ if (key.alg === ES256) return webcrypto.subtle.importKey("raw", toBufferSource(key["public-key"]), {
19
+ name: "ECDSA",
20
+ namedCurve: "P-256"
21
+ }, false, ["verify"]);
22
+ if (key.alg === EDDSA) return webcrypto.subtle.importKey("raw", toBufferSource(key["public-key"]), { name: "Ed25519" }, false, ["verify"]);
23
+ throw new Error(`unsupported identity-key alg ${String(key.alg)}`);
24
+ }
25
+ async function deriveDeviceId(publicKey) {
26
+ return new Uint8Array(await webcrypto.subtle.digest("SHA-256", toBufferSource(publicKey)));
27
+ }
28
+ async function verifyWithPublicKey(key, message, signature) {
29
+ const cryptoKey = await importPublicKey(key);
30
+ return webcrypto.subtle.verify(algParams(key.alg), cryptoKey, toBufferSource(signature), toBufferSource(message));
31
+ }
32
+ /**
33
+ * Builds an IdentityPort from a Web Crypto private key already generated for this node -- ECDSA P-256 (alg -7) or Ed25519 (alg -8), the two algorithms identity-key.alg actually carries in the spec's own conformance vectors. Device-id derivation and signature verification are pure functions of the given key bytes (exported separately above), so they work for an arbitrary issuer-key too, not just this node's own.
34
+ */
35
+ async function createNodeIdentity(privateKey, publicKeyBytes, alg) {
36
+ const identityKey = {
37
+ alg,
38
+ "public-key": toBufferSource(publicKeyBytes)
39
+ };
40
+ return {
41
+ deviceId: await deriveDeviceId(publicKeyBytes),
42
+ identityKey,
43
+ async sign(message) {
44
+ const signature = await webcrypto.subtle.sign(algParams(alg), privateKey, toBufferSource(message));
45
+ return new Uint8Array(signature);
46
+ },
47
+ verify: verifyWithPublicKey,
48
+ deriveDeviceId
49
+ };
50
+ }
51
+ //#endregion
52
+ export { createNodeIdentity, deriveDeviceId, verifyWithPublicKey };
@@ -0,0 +1,8 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/adapters/system-clock.ts
3
+ /** Wraps the system wall clock -- the only place Date.now() appears in this package. */
4
+ function createSystemClock() {
5
+ return { now: () => Date.now() };
6
+ }
7
+ //#endregion
8
+ exports.createSystemClock = createSystemClock;
@@ -0,0 +1,5 @@
1
+ import { t as Clock } from "../clock-DiSx-WKM.cjs";
2
+ //#region src/adapters/system-clock.d.ts
3
+ /** Wraps the system wall clock -- the only place Date.now() appears in this package. */
4
+ export declare function createSystemClock(): Clock;
5
+ //#endregion
@@ -0,0 +1,5 @@
1
+ import { t as Clock } from "../clock-DiSx-WKM.mjs";
2
+ //#region src/adapters/system-clock.d.ts
3
+ /** Wraps the system wall clock -- the only place Date.now() appears in this package. */
4
+ export declare function createSystemClock(): Clock;
5
+ //#endregion
@@ -0,0 +1,7 @@
1
+ //#region src/adapters/system-clock.ts
2
+ /** Wraps the system wall clock -- the only place Date.now() appears in this package. */
3
+ function createSystemClock() {
4
+ return { now: () => Date.now() };
5
+ }
6
+ //#endregion
7
+ export { createSystemClock };
@@ -0,0 +1,154 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_generated_protocol = require("../generated/protocol.cjs");
3
+ let cbor2 = require("cbor2");
4
+ let node_net = require("node:net");
5
+ //#region src/adapters/tcp-transport.ts
6
+ const LENGTH_PREFIX_BYTES = 4;
7
+ function parseAddress(address) {
8
+ const lastColon = address.lastIndexOf(":");
9
+ if (lastColon === -1) throw new Error(`expected "host:port", got "${address}"`);
10
+ return {
11
+ host: address.slice(0, lastColon),
12
+ port: Number(address.slice(lastColon + 1))
13
+ };
14
+ }
15
+ function writeFrame(socket, frame) {
16
+ const body = (0, cbor2.encode)(frame, cbor2.cdeEncodeOptions);
17
+ const header = Buffer.alloc(LENGTH_PREFIX_BYTES);
18
+ header.writeUInt32BE(body.length, 0);
19
+ socket.write(header);
20
+ socket.write(body);
21
+ }
22
+ /** Reassembles length-prefixed CBOR frames from a byte stream, validating each against frameSchema before handing it to a consumer. A body that doesn't even decode as CBOR rejects the receive() iteration and destroys the connection -- hostile wire input is a connection-level failure, surfaced through the Transport port rather than crashing the process or being silently swallowed. */
23
+ function frameReader(socket) {
24
+ let buffer = Buffer.alloc(0);
25
+ const pending = [];
26
+ const waiters = [];
27
+ let ended = false;
28
+ let failure = null;
29
+ function tryDrain() {
30
+ while (buffer.length >= LENGTH_PREFIX_BYTES) {
31
+ const bodyLength = buffer.readUInt32BE(0);
32
+ if (buffer.length < LENGTH_PREFIX_BYTES + bodyLength) break;
33
+ const body = buffer.subarray(LENGTH_PREFIX_BYTES, LENGTH_PREFIX_BYTES + bodyLength);
34
+ buffer = buffer.subarray(LENGTH_PREFIX_BYTES + bodyLength);
35
+ let decoded;
36
+ try {
37
+ decoded = (0, cbor2.decode)(body, cbor2.cdeDecodeOptions);
38
+ } catch (error) {
39
+ const connectionError = error instanceof Error ? error : /* @__PURE__ */ new Error(`frame body failed to decode: ${String(error)}`);
40
+ failAll(connectionError);
41
+ socket.destroy(connectionError);
42
+ return;
43
+ }
44
+ const result = require_generated_protocol.frameSchema.safeParse(decoded);
45
+ if (result.success) {
46
+ const waiter = waiters.shift();
47
+ if (waiter) waiter.resolve({
48
+ value: result.data,
49
+ done: false
50
+ });
51
+ else pending.push(result.data);
52
+ }
53
+ }
54
+ }
55
+ function endAll() {
56
+ ended = true;
57
+ for (const waiter of waiters.splice(0)) waiter.resolve({
58
+ value: void 0,
59
+ done: true
60
+ });
61
+ }
62
+ /** Ends the iteration with the connection-level error: pending and future next() calls reject, so a consumer iterating receive() sees the failure where it consumed the stream. */
63
+ function failAll(error) {
64
+ failure = error;
65
+ ended = true;
66
+ for (const waiter of waiters.splice(0)) waiter.reject(error);
67
+ }
68
+ socket.on("data", (chunk) => {
69
+ buffer = Buffer.concat([buffer, chunk]);
70
+ tryDrain();
71
+ });
72
+ socket.on("end", endAll);
73
+ socket.on("close", endAll);
74
+ socket.on("error", () => void 0);
75
+ return { [Symbol.asyncIterator]() {
76
+ return { async next() {
77
+ const next = pending.shift();
78
+ if (next !== void 0) return Promise.resolve({
79
+ value: next,
80
+ done: false
81
+ });
82
+ if (failure !== null) return Promise.reject(failure);
83
+ if (ended) return Promise.resolve({
84
+ value: void 0,
85
+ done: true
86
+ });
87
+ return new Promise((resolve, reject) => {
88
+ waiters.push({
89
+ resolve,
90
+ reject
91
+ });
92
+ });
93
+ } };
94
+ } };
95
+ }
96
+ function wrapSocket(socket) {
97
+ const frames = frameReader(socket);
98
+ return {
99
+ send: async (frame) => {
100
+ writeFrame(socket, frame);
101
+ return Promise.resolve();
102
+ },
103
+ receive: () => frames,
104
+ close: async () => new Promise((resolve) => {
105
+ socket.end(() => {
106
+ resolve();
107
+ });
108
+ })
109
+ };
110
+ }
111
+ /** A Node net.Socket-based Transport: length-prefixed, CBOR-encoded frames over plain TCP -- matching Cascade's own transport shape, since interop with Cascade nodes is wire-mesh's stated goal. Framing (not TLS) is this adapter's own concern; a TLS-terminated variant is a separate adapter behind the same Transport contract. */
112
+ function createTcpTransport() {
113
+ return {
114
+ async connect(address) {
115
+ const { host, port } = parseAddress(address);
116
+ return new Promise((resolve, reject) => {
117
+ const socket = (0, node_net.connect)({
118
+ host,
119
+ port
120
+ });
121
+ socket.once("connect", () => {
122
+ resolve(wrapSocket(socket));
123
+ });
124
+ socket.once("error", reject);
125
+ });
126
+ },
127
+ async listen(address, onConnection) {
128
+ const { host, port } = parseAddress(address);
129
+ return new Promise((resolve, reject) => {
130
+ const server = (0, node_net.createServer)((socket) => {
131
+ onConnection(wrapSocket(socket));
132
+ });
133
+ server.once("error", reject);
134
+ server.listen(port, host, () => {
135
+ const bound = server.address();
136
+ if (bound === null || typeof bound === "string") {
137
+ reject(/* @__PURE__ */ new Error("listener did not report a bound address"));
138
+ return;
139
+ }
140
+ resolve({
141
+ close: async () => new Promise((resolveClose) => {
142
+ server.close(() => {
143
+ resolveClose();
144
+ });
145
+ }),
146
+ address: `${bound.address}:${String(bound.port)}`
147
+ });
148
+ });
149
+ });
150
+ }
151
+ };
152
+ }
153
+ //#endregion
154
+ exports.createTcpTransport = createTcpTransport;
@@ -0,0 +1,5 @@
1
+ import { Transport } from "../ports/transport.cjs";
2
+ //#region src/adapters/tcp-transport.d.ts
3
+ /** A Node net.Socket-based Transport: length-prefixed, CBOR-encoded frames over plain TCP -- matching Cascade's own transport shape, since interop with Cascade nodes is wire-mesh's stated goal. Framing (not TLS) is this adapter's own concern; a TLS-terminated variant is a separate adapter behind the same Transport contract. */
4
+ export declare function createTcpTransport(): Transport;
5
+ //#endregion
@@ -0,0 +1,5 @@
1
+ import { Transport } from "../ports/transport.mjs";
2
+ //#region src/adapters/tcp-transport.d.ts
3
+ /** A Node net.Socket-based Transport: length-prefixed, CBOR-encoded frames over plain TCP -- matching Cascade's own transport shape, since interop with Cascade nodes is wire-mesh's stated goal. Framing (not TLS) is this adapter's own concern; a TLS-terminated variant is a separate adapter behind the same Transport contract. */
4
+ export declare function createTcpTransport(): Transport;
5
+ //#endregion