@net-mesh/sdk 0.19.0
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/README.md +1684 -0
- package/dist/_internal.d.ts +25 -0
- package/dist/_internal.js +60 -0
- package/dist/capabilities.d.ts +271 -0
- package/dist/capabilities.js +186 -0
- package/dist/capability-enhancements.d.ts +574 -0
- package/dist/capability-enhancements.js +1324 -0
- package/dist/capability-schema.d.ts +112 -0
- package/dist/capability-schema.js +317 -0
- package/dist/channel.d.ts +56 -0
- package/dist/channel.js +95 -0
- package/dist/compute.d.ts +546 -0
- package/dist/compute.js +741 -0
- package/dist/cortex.d.ts +236 -0
- package/dist/cortex.js +584 -0
- package/dist/deck.d.ts +342 -0
- package/dist/deck.js +717 -0
- package/dist/groups.d.ts +208 -0
- package/dist/groups.js +431 -0
- package/dist/identity.d.ts +149 -0
- package/dist/identity.js +264 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.js +147 -0
- package/dist/mesh.d.ts +369 -0
- package/dist/mesh.js +433 -0
- package/dist/meshdb.d.ts +87 -0
- package/dist/meshdb.js +111 -0
- package/dist/meshos.d.ts +277 -0
- package/dist/meshos.js +359 -0
- package/dist/node.d.ts +120 -0
- package/dist/node.js +246 -0
- package/dist/redis-dedup.d.ts +48 -0
- package/dist/redis-dedup.js +52 -0
- package/dist/stream.d.ts +47 -0
- package/dist/stream.js +118 -0
- package/dist/subnets.d.ts +75 -0
- package/dist/subnets.js +54 -0
- package/dist/types.d.ts +102 -0
- package/dist/types.js +5 -0
- package/package.json +43 -0
package/dist/mesh.d.ts
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MeshNode — the multi-peer encrypted mesh handle.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the NAPI `NetMesh` with ergonomic TypeScript APIs: typed
|
|
5
|
+
* `StreamConfig`, classed `BackpressureError` / `NotConnectedError`
|
|
6
|
+
* for `instanceof`-based pattern matching, and the `send_with_retry`
|
|
7
|
+
* / `send_blocking` helpers from the Rust core.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { MeshNode, BackpressureError, Reliability } from '@net-mesh/sdk';
|
|
12
|
+
*
|
|
13
|
+
* const node = await MeshNode.create({
|
|
14
|
+
* bindAddr: '127.0.0.1:9000',
|
|
15
|
+
* psk: '0'.repeat(64),
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* await node.connect('127.0.0.1:9001', peerPubkey, 0x2222n);
|
|
19
|
+
* node.start();
|
|
20
|
+
*
|
|
21
|
+
* const stream = node.openStream(0x2222n, {
|
|
22
|
+
* streamId: 7n,
|
|
23
|
+
* reliability: 'reliable',
|
|
24
|
+
* windowBytes: 256,
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* try {
|
|
28
|
+
* await node.sendOnStream(stream, [Buffer.from('hello')]);
|
|
29
|
+
* } catch (e) {
|
|
30
|
+
* if (e instanceof BackpressureError) {
|
|
31
|
+
* // daemon chose: drop, buffer, or retry
|
|
32
|
+
* } else {
|
|
33
|
+
* throw e;
|
|
34
|
+
* }
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
import { type CapabilityFilter, type CapabilitySet, type ScopeFilter } from './capabilities';
|
|
39
|
+
import type { SubnetId, SubnetPolicy } from './subnets';
|
|
40
|
+
import type { Token } from './identity';
|
|
41
|
+
/** Reliability mode chosen at stream-open time. */
|
|
42
|
+
export type Reliability = 'fire_and_forget' | 'reliable';
|
|
43
|
+
/** Per-stream configuration for {@link MeshNode.openStream}. */
|
|
44
|
+
export interface StreamConfig {
|
|
45
|
+
/**
|
|
46
|
+
* Caller-chosen stream identifier. Opaque `bigint` at the transport
|
|
47
|
+
* layer; no value range has reserved meaning.
|
|
48
|
+
*/
|
|
49
|
+
streamId: bigint;
|
|
50
|
+
/** Reliability mode. Default: `'fire_and_forget'`. */
|
|
51
|
+
reliability?: Reliability;
|
|
52
|
+
/**
|
|
53
|
+
* Initial send-credit window in bytes. Leave unset to inherit the
|
|
54
|
+
* core's `DEFAULT_STREAM_WINDOW_BYTES` (64 KB) — v2 backpressure
|
|
55
|
+
* is ON out of the box. Pass `0` to restore the v1 unbounded-queue
|
|
56
|
+
* behavior on this stream.
|
|
57
|
+
*/
|
|
58
|
+
windowBytes?: number;
|
|
59
|
+
/**
|
|
60
|
+
* Fair-scheduler weight. `1` = equal share; higher = proportionally
|
|
61
|
+
* more packets per round. Default: `1`.
|
|
62
|
+
*/
|
|
63
|
+
fairnessWeight?: number;
|
|
64
|
+
}
|
|
65
|
+
/** Per-stream stats snapshot. */
|
|
66
|
+
export interface StreamStats {
|
|
67
|
+
txSeq: bigint;
|
|
68
|
+
rxSeq: bigint;
|
|
69
|
+
inboundPending: bigint;
|
|
70
|
+
lastActivityNs: bigint;
|
|
71
|
+
active: boolean;
|
|
72
|
+
/** Cumulative Backpressure rejections since stream opened. */
|
|
73
|
+
backpressureEvents: bigint;
|
|
74
|
+
/**
|
|
75
|
+
* Bytes of send credit still available. `0` means the next send
|
|
76
|
+
* will be rejected as Backpressure. Receiver-driven `StreamWindow`
|
|
77
|
+
* grants replenish this counter.
|
|
78
|
+
*/
|
|
79
|
+
txCreditRemaining: number;
|
|
80
|
+
/**
|
|
81
|
+
* Configured initial credit window in bytes. `0` disables
|
|
82
|
+
* backpressure entirely on this stream (escape hatch).
|
|
83
|
+
*/
|
|
84
|
+
txWindow: number;
|
|
85
|
+
/** Cumulative StreamWindow grants received from the peer. */
|
|
86
|
+
creditGrantsReceived: bigint;
|
|
87
|
+
/** Cumulative StreamWindow grants emitted to the peer. */
|
|
88
|
+
creditGrantsSent: bigint;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Thrown by {@link MeshNode.sendOnStream} / `sendWithRetry` /
|
|
92
|
+
* `sendBlocking` when the stream's per-stream in-flight window is
|
|
93
|
+
* full. **The event was NOT sent.** Caller decides whether to drop,
|
|
94
|
+
* retry, or buffer at the app layer — see the "Back-pressure" section
|
|
95
|
+
* in `docs/TRANSPORT.md` for the three canonical patterns.
|
|
96
|
+
*/
|
|
97
|
+
export declare class BackpressureError extends Error {
|
|
98
|
+
constructor(detail?: string);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Thrown when the stream's peer session is gone (peer never
|
|
102
|
+
* connected, disconnected, or the stream was closed). Distinct from
|
|
103
|
+
* {@link BackpressureError} because this is a "connection lost", not
|
|
104
|
+
* "too fast".
|
|
105
|
+
*/
|
|
106
|
+
export declare class NotConnectedError extends Error {
|
|
107
|
+
constructor(detail?: string);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Options for {@link MeshNode.subscribeChannel}. Struct form so
|
|
111
|
+
* future knobs (timeout override, priority) don't break callers.
|
|
112
|
+
*/
|
|
113
|
+
export interface SubscribeOptions {
|
|
114
|
+
/**
|
|
115
|
+
* Token to present to the publisher. The publisher verifies the
|
|
116
|
+
* ed25519 signature, checks the subject matches the subscribing
|
|
117
|
+
* peer's `EntityId`, and installs the token in its local cache
|
|
118
|
+
* before running `can_subscribe`. A matching token satisfies
|
|
119
|
+
* `requireToken` channels end-to-end.
|
|
120
|
+
*/
|
|
121
|
+
token?: Token;
|
|
122
|
+
}
|
|
123
|
+
/** Options for {@link MeshNode.create}. */
|
|
124
|
+
export interface MeshNodeConfig {
|
|
125
|
+
/** Local bind address (e.g. `"127.0.0.1:9000"`). */
|
|
126
|
+
bindAddr: string;
|
|
127
|
+
/** Hex-encoded 32-byte pre-shared key (64 hex chars). */
|
|
128
|
+
psk: string;
|
|
129
|
+
/** Heartbeat interval in milliseconds. Default: 5000. */
|
|
130
|
+
heartbeatIntervalMs?: number;
|
|
131
|
+
/** Session timeout in milliseconds. Default: 30000. */
|
|
132
|
+
sessionTimeoutMs?: number;
|
|
133
|
+
/** Inbound shard count. Default: 4. */
|
|
134
|
+
numShards?: number;
|
|
135
|
+
/**
|
|
136
|
+
* Capability-index GC sweep interval in milliseconds. Default:
|
|
137
|
+
* 60_000. Shorter values make TTL-driven eviction more responsive
|
|
138
|
+
* at the cost of extra CPU; primarily useful in tests.
|
|
139
|
+
*/
|
|
140
|
+
capabilityGcIntervalMs?: number;
|
|
141
|
+
/**
|
|
142
|
+
* Drop inbound `CapabilityAnnouncement` packets without a
|
|
143
|
+
* signature. Default: false. Signature *validity* is not yet
|
|
144
|
+
* enforced end-to-end — this is presence-only policy today.
|
|
145
|
+
*/
|
|
146
|
+
requireSignedCapabilities?: boolean;
|
|
147
|
+
/**
|
|
148
|
+
* Pin this node to a specific subnet. Omitted = no restriction
|
|
149
|
+
* (`SubnetId::GLOBAL`). Visibility checks on the publish +
|
|
150
|
+
* subscribe paths compare against this value.
|
|
151
|
+
*/
|
|
152
|
+
subnet?: SubnetId;
|
|
153
|
+
/**
|
|
154
|
+
* Policy that derives each peer's subnet from their capability
|
|
155
|
+
* announcements. Mesh-wide policy consistency is assumed —
|
|
156
|
+
* mismatched policies lead to asymmetric views of peer subnets.
|
|
157
|
+
*/
|
|
158
|
+
subnetPolicy?: SubnetPolicy;
|
|
159
|
+
/**
|
|
160
|
+
* 32-byte ed25519 seed. When set, the mesh's keypair is
|
|
161
|
+
* derived from this seed — so its `entityId` and `nodeId`
|
|
162
|
+
* are reproducible across restarts, and a caller-side
|
|
163
|
+
* `Identity.fromSeed(seed)` can issue tokens that validate
|
|
164
|
+
* against this mesh. Treat as secret material.
|
|
165
|
+
*/
|
|
166
|
+
identitySeed?: Buffer;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* An opaque stream handle. Pass back to `sendOnStream` /
|
|
170
|
+
* `sendWithRetry` / `sendBlocking` / `closeStream`. You normally
|
|
171
|
+
* don't need to read the fields — they're exposed for diagnostics.
|
|
172
|
+
*/
|
|
173
|
+
export interface MeshStream {
|
|
174
|
+
readonly peerNodeId: bigint;
|
|
175
|
+
readonly streamId: bigint;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* A node on the Net mesh with full stream multiplexing + backpressure
|
|
179
|
+
* support.
|
|
180
|
+
*/
|
|
181
|
+
export declare class MeshNode {
|
|
182
|
+
private native;
|
|
183
|
+
private constructor();
|
|
184
|
+
/** Create and configure a new mesh node. */
|
|
185
|
+
static create(config: MeshNodeConfig): Promise<MeshNode>;
|
|
186
|
+
/** 32-byte ed25519 entity id for this mesh. */
|
|
187
|
+
entityId(): Buffer;
|
|
188
|
+
/** Hex-encoded Noise static public key. */
|
|
189
|
+
publicKey(): string;
|
|
190
|
+
/** This node's id. */
|
|
191
|
+
nodeId(): bigint;
|
|
192
|
+
/** Connect to a peer as initiator. */
|
|
193
|
+
connect(peerAddr: string, peerPublicKey: string, peerNodeId: bigint): Promise<void>;
|
|
194
|
+
/** Accept an incoming connection as responder. Returns the peer's wire address. */
|
|
195
|
+
accept(peerNodeId: bigint): Promise<string>;
|
|
196
|
+
/** Start the receive loop / heartbeats / router. */
|
|
197
|
+
start(): Promise<void>;
|
|
198
|
+
/** Number of connected peers. */
|
|
199
|
+
peerCount(): number;
|
|
200
|
+
/**
|
|
201
|
+
* Open (or look up) a logical stream to a connected peer. Repeated
|
|
202
|
+
* calls for the same `(peer, streamId)` are idempotent; the first
|
|
203
|
+
* open wins and later differing configs are logged and ignored.
|
|
204
|
+
*/
|
|
205
|
+
openStream(peerNodeId: bigint, config: StreamConfig): MeshStream;
|
|
206
|
+
/** Close a stream. Idempotent. */
|
|
207
|
+
closeStream(peerNodeId: bigint, streamId: bigint): void;
|
|
208
|
+
/**
|
|
209
|
+
* Send a batch of events on an explicit stream. Throws
|
|
210
|
+
* {@link BackpressureError} when the stream's in-flight window is
|
|
211
|
+
* full (no events sent — caller decides what to do),
|
|
212
|
+
* {@link NotConnectedError} when the peer session is gone, or a
|
|
213
|
+
* plain `Error` for underlying transport failures.
|
|
214
|
+
*/
|
|
215
|
+
sendOnStream(stream: MeshStream, events: Buffer[]): Promise<void>;
|
|
216
|
+
/**
|
|
217
|
+
* Send events, retrying on {@link BackpressureError} with 5 ms → 200 ms
|
|
218
|
+
* exponential backoff up to `maxRetries` times. Transport errors and
|
|
219
|
+
* `NotConnectedError` are re-thrown immediately (they're not a
|
|
220
|
+
* pressure signal).
|
|
221
|
+
*/
|
|
222
|
+
sendWithRetry(stream: MeshStream, events: Buffer[], maxRetries?: number): Promise<void>;
|
|
223
|
+
/**
|
|
224
|
+
* Block the calling task until the send succeeds or a transport
|
|
225
|
+
* error occurs. Retries {@link BackpressureError} with 5 ms → 200 ms
|
|
226
|
+
* exponential backoff up to 4096 times (~13 min worst case) —
|
|
227
|
+
* effectively "block until the network lets up" under practical
|
|
228
|
+
* workloads, but with a hard upper bound so runaway pressure can't
|
|
229
|
+
* hang the caller forever. Use {@link sendWithRetry} for a tighter
|
|
230
|
+
* bound.
|
|
231
|
+
*/
|
|
232
|
+
sendBlocking(stream: MeshStream, events: Buffer[]): Promise<void>;
|
|
233
|
+
/** Snapshot per-stream stats. `null` if the peer or stream isn't open. */
|
|
234
|
+
streamStats(peerNodeId: bigint, streamId: bigint): StreamStats | null;
|
|
235
|
+
/**
|
|
236
|
+
* Register a channel on this node. Subscribers who ask to join are
|
|
237
|
+
* validated against `config` before being added to the roster.
|
|
238
|
+
*
|
|
239
|
+
* Mirrors the core `ChannelConfig` field-for-field. v1 omits
|
|
240
|
+
* `publishCaps` / `subscribeCaps` — those land with the security
|
|
241
|
+
* plan's identity surface.
|
|
242
|
+
*/
|
|
243
|
+
registerChannel(config: ChannelConfig): void;
|
|
244
|
+
/**
|
|
245
|
+
* Ask `publisherNodeId` to add this node to `channel`'s subscriber
|
|
246
|
+
* set. Blocks until the publisher's `Ack` arrives or the
|
|
247
|
+
* membership-ack timeout elapses.
|
|
248
|
+
*
|
|
249
|
+
* Pass `opts.token` to present a
|
|
250
|
+
* {@link Token PermissionToken} issued by the publisher — required
|
|
251
|
+
* when the channel was registered with `requireToken: true` or
|
|
252
|
+
* when your caps alone don't satisfy `subscribeCaps`. The
|
|
253
|
+
* publisher verifies the signature, checks `subject ===
|
|
254
|
+
* thisNode.entityId`, installs it in its local cache, then runs
|
|
255
|
+
* the ACL check.
|
|
256
|
+
*
|
|
257
|
+
* Throws a {@link ChannelAuthError} or {@link ChannelError} on
|
|
258
|
+
* rejection; network-level failures propagate as plain `Error`.
|
|
259
|
+
*/
|
|
260
|
+
subscribeChannel(publisherNodeId: bigint, channel: string, opts?: SubscribeOptions): Promise<void>;
|
|
261
|
+
/** Mirror of {@link subscribeChannel}. Idempotent on the publisher side. */
|
|
262
|
+
unsubscribeChannel(publisherNodeId: bigint, channel: string): Promise<void>;
|
|
263
|
+
/**
|
|
264
|
+
* Publish one payload to every subscriber of `channel`. Returns a
|
|
265
|
+
* {@link PublishReport} describing per-peer outcomes.
|
|
266
|
+
*/
|
|
267
|
+
publish(channel: string, payload: Buffer, config?: PublishConfig): Promise<PublishReport>;
|
|
268
|
+
/**
|
|
269
|
+
* Announce this node's capabilities to every directly-connected
|
|
270
|
+
* peer. Self-indexes too, so `findNodes` on this same node matches
|
|
271
|
+
* on the announcement. Multi-hop propagation is deferred — peers
|
|
272
|
+
* more than one hop away will not see the announcement.
|
|
273
|
+
*/
|
|
274
|
+
announceCapabilities(caps: CapabilitySet): Promise<void>;
|
|
275
|
+
/**
|
|
276
|
+
* Query the local capability index. Returns node ids (including
|
|
277
|
+
* our own `nodeId()` if self matches) whose latest announcement
|
|
278
|
+
* matches `filter`.
|
|
279
|
+
*/
|
|
280
|
+
findNodes(filter: CapabilityFilter): bigint[];
|
|
281
|
+
/**
|
|
282
|
+
* Scoped variant of {@link findNodes}. Filters candidates through
|
|
283
|
+
* a {@link ScopeFilter} derived from each peer's `scope:*`
|
|
284
|
+
* reserved tags (e.g. `scope:tenant:oem-123`,
|
|
285
|
+
* `scope:region:eu-west`, `scope:subnet-local`).
|
|
286
|
+
*
|
|
287
|
+
* Untagged peers stay visible under most filters by design;
|
|
288
|
+
* peers tagged `scope:subnet-local` only show up under
|
|
289
|
+
* `{ kind: 'sameSubnet' }`. See `docs/SCOPED_CAPABILITIES_PLAN.md`
|
|
290
|
+
* for the full table.
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```typescript
|
|
294
|
+
* // GPU pool for a specific tenant.
|
|
295
|
+
* const peers = node.findNodesScoped(
|
|
296
|
+
* { requireTags: ['model:llama3-70b'] },
|
|
297
|
+
* { kind: 'tenant', tenant: 'oem-123' },
|
|
298
|
+
* );
|
|
299
|
+
* ```
|
|
300
|
+
*/
|
|
301
|
+
findNodesScoped(filter: CapabilityFilter, scope: ScopeFilter): bigint[];
|
|
302
|
+
/** Shutdown the mesh node. */
|
|
303
|
+
shutdown(): Promise<void>;
|
|
304
|
+
}
|
|
305
|
+
export type Visibility = 'subnet-local' | 'parent-visible' | 'exported' | 'global';
|
|
306
|
+
export type OnFailure = 'best_effort' | 'fail_fast' | 'collect';
|
|
307
|
+
/** Channel configuration — mirror of the core `ChannelConfig`. */
|
|
308
|
+
export interface ChannelConfig {
|
|
309
|
+
/** Canonical channel name. Crosses the boundary as a string. */
|
|
310
|
+
name: string;
|
|
311
|
+
/** Default: `'global'`. */
|
|
312
|
+
visibility?: Visibility;
|
|
313
|
+
/** Default reliability for streams on this channel. */
|
|
314
|
+
reliable?: boolean;
|
|
315
|
+
/**
|
|
316
|
+
* When true, subscribers must present a valid
|
|
317
|
+
* `PermissionToken` whose subject matches their entity id.
|
|
318
|
+
*/
|
|
319
|
+
requireToken?: boolean;
|
|
320
|
+
/** Priority (0 = lowest). */
|
|
321
|
+
priority?: number;
|
|
322
|
+
/** Rate cap in packets per second. */
|
|
323
|
+
maxRatePps?: number;
|
|
324
|
+
/**
|
|
325
|
+
* Capability filter the publisher itself must satisfy before
|
|
326
|
+
* fan-out. `publish` rejects with a `channel:` error on
|
|
327
|
+
* mismatch.
|
|
328
|
+
*/
|
|
329
|
+
publishCaps?: CapabilityFilter;
|
|
330
|
+
/**
|
|
331
|
+
* Capability filter each subscriber must satisfy.
|
|
332
|
+
* `subscribeChannel` throws a `ChannelAuthError` on mismatch.
|
|
333
|
+
*/
|
|
334
|
+
subscribeCaps?: CapabilityFilter;
|
|
335
|
+
}
|
|
336
|
+
/** Publish-fanout config — mirror of the core `PublishConfig`. */
|
|
337
|
+
export interface PublishConfig {
|
|
338
|
+
/** Default: `'fire_and_forget'`. */
|
|
339
|
+
reliability?: Reliability;
|
|
340
|
+
/** Default: `'best_effort'`. */
|
|
341
|
+
onFailure?: OnFailure;
|
|
342
|
+
/** Max concurrent per-peer sends. Default 32. */
|
|
343
|
+
maxInflight?: number;
|
|
344
|
+
}
|
|
345
|
+
/** Per-peer report returned by {@link MeshNode.publish}. */
|
|
346
|
+
export interface PublishReport {
|
|
347
|
+
attempted: number;
|
|
348
|
+
delivered: number;
|
|
349
|
+
errors: Array<{
|
|
350
|
+
nodeId: bigint;
|
|
351
|
+
message: string;
|
|
352
|
+
}>;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Raised when a channel operation fails for a reason other than
|
|
356
|
+
* auth. The napi binding emits `"channel: ..."` prefixed errors that
|
|
357
|
+
* the SDK classifies into {@link ChannelAuthError} (unauthorized) or
|
|
358
|
+
* this class (everything else).
|
|
359
|
+
*/
|
|
360
|
+
export declare class ChannelError extends Error {
|
|
361
|
+
constructor(detail?: string);
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Raised when a Subscribe / Unsubscribe request is rejected because
|
|
365
|
+
* the subscriber isn't authorized on the publisher's channel config.
|
|
366
|
+
*/
|
|
367
|
+
export declare class ChannelAuthError extends ChannelError {
|
|
368
|
+
constructor(detail?: string);
|
|
369
|
+
}
|