@aria-cli/wireguard 1.0.38 → 1.0.40
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/bootstrap-authority.d.ts +2 -0
- package/dist/bootstrap-authority.js +47 -0
- package/dist/bootstrap-tls.d.ts +14 -0
- package/dist/bootstrap-tls.js +69 -0
- package/dist/db-owner-fencing.d.ts +10 -0
- package/dist/db-owner-fencing.js +44 -0
- package/dist/derp-relay.d.ts +75 -0
- package/dist/derp-relay.js +311 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.js +100 -0
- package/dist/nat.d.ts +84 -0
- package/dist/nat.js +397 -0
- package/dist/network-state-store.d.ts +46 -0
- package/dist/network-state-store.js +248 -0
- package/dist/network.d.ts +590 -0
- package/dist/network.js +3391 -0
- package/dist/peer-discovery.d.ts +133 -0
- package/dist/peer-discovery.js +486 -0
- package/dist/resilient-tunnel.d.ts +70 -0
- package/dist/resilient-tunnel.js +389 -0
- package/dist/route-ownership.d.ts +23 -0
- package/dist/route-ownership.js +79 -0
- package/dist/tunnel.d.ts +141 -0
- package/dist/tunnel.js +474 -0
- package/index.js +52 -52
- package/npm/darwin-arm64/package.json +1 -1
- package/npm/darwin-x64/package.json +1 -1
- package/npm/linux-arm64-gnu/package.json +1 -1
- package/npm/linux-x64-gnu/package.json +1 -1
- package/npm/win32-x64-msvc/package.json +1 -1
- package/package.json +13 -20
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Network manager — auto-setup, peer management, and invite flow for ARIA.
|
|
3
|
+
*
|
|
4
|
+
* Creates a secure WireGuard network on first run with zero user configuration.
|
|
5
|
+
* Manages peer lifecycle (invite, revoke, list) through the manage_network tool.
|
|
6
|
+
* Persists peer registry in Memoria DB (network_peers table).
|
|
7
|
+
*
|
|
8
|
+
* Architecture:
|
|
9
|
+
* NetworkManager owns a SecureTunnel per peer. On startup it loads peers from
|
|
10
|
+
* the DB, creates tunnels, and starts them. New peers join via invite tokens
|
|
11
|
+
* that encode the leader's public key + endpoint + PSK.
|
|
12
|
+
*/
|
|
13
|
+
import type Database from "better-sqlite3";
|
|
14
|
+
import { type NetworkPeerRepairFailureRef, type NetworkPeerRepairResultRef, type NetworkPeerRevocationFailureRef, type NetworkPeerRevocationResultRef, type NetworkPeerRegistrationFailureRef, type NetworkPeerRegistrationResultRef, type NodeId, type ControlEndpointAdvertisement, type DeliveryAck, type LegacyPeerRegistryStatus, type TlsCaFingerprint, type TransportInviteToken, type PrincipalFingerprint, type PeerTransportId, type TransportEndpointAdvertisement } from "@aria-cli/tools";
|
|
15
|
+
import { ResilientTunnel } from "./resilient-tunnel.js";
|
|
16
|
+
/** Network configuration persisted to disk */
|
|
17
|
+
export interface NetworkConfig {
|
|
18
|
+
/** Durable node identity for this local peer */
|
|
19
|
+
nodeId: NodeId;
|
|
20
|
+
/** X25519 public key (base64) */
|
|
21
|
+
publicKey: PeerTransportId;
|
|
22
|
+
/** X25519 private key (base64) — stored only in config, never transmitted */
|
|
23
|
+
privateKey: string;
|
|
24
|
+
/** Ed25519 public key for message signing (base64 SPKI DER) */
|
|
25
|
+
signingPublicKey: string;
|
|
26
|
+
/** Ed25519 private key for message signing (base64 PKCS#8 DER) — stored only in config, never transmitted */
|
|
27
|
+
signingPrivateKey: string;
|
|
28
|
+
/** UDP listen port for WireGuard */
|
|
29
|
+
listenPort: number;
|
|
30
|
+
/** External endpoint discovered via STUN */
|
|
31
|
+
externalEndpoint?: {
|
|
32
|
+
address: string;
|
|
33
|
+
port: number;
|
|
34
|
+
};
|
|
35
|
+
/** Monotonic revision for the published external endpoint advertisement. */
|
|
36
|
+
endpointRevision?: number;
|
|
37
|
+
/** When the network was created */
|
|
38
|
+
createdAt: number;
|
|
39
|
+
/** Coordination server URL for peer discovery and endpoint propagation */
|
|
40
|
+
coordinationUrl?: string;
|
|
41
|
+
/** DERP relay server URL for symmetric NAT fallback */
|
|
42
|
+
relayUrl?: string;
|
|
43
|
+
/** Public display snapshot used by bootstrap/publication surfaces. */
|
|
44
|
+
localDisplayNameSnapshot?: string;
|
|
45
|
+
}
|
|
46
|
+
/** Peer information from the database */
|
|
47
|
+
export interface PeerInfo {
|
|
48
|
+
publicKey: PeerTransportId;
|
|
49
|
+
nodeId: NodeId | null;
|
|
50
|
+
name: string;
|
|
51
|
+
endpointHost: string | null;
|
|
52
|
+
endpointPort: number | null;
|
|
53
|
+
endpointRevision: number;
|
|
54
|
+
controlEndpointHost: string | null;
|
|
55
|
+
controlEndpointPort: number | null;
|
|
56
|
+
controlTlsCaFingerprint: TlsCaFingerprint | null;
|
|
57
|
+
controlEndpoint: {
|
|
58
|
+
host: string | null;
|
|
59
|
+
port: number | null;
|
|
60
|
+
tlsCaFingerprint?: TlsCaFingerprint | null;
|
|
61
|
+
tlsServerIdentity?: PrincipalFingerprint | null;
|
|
62
|
+
protocolVersion: number;
|
|
63
|
+
endpointRevision?: number;
|
|
64
|
+
} | null;
|
|
65
|
+
status: LegacyPeerRegistryStatus;
|
|
66
|
+
membershipStatus?: LegacyPeerRegistryStatus;
|
|
67
|
+
routeOwnership?: "current" | "superseded";
|
|
68
|
+
routeOwnerNodeId?: NodeId | null;
|
|
69
|
+
sessionState?: "none" | "handshaking" | "connected" | "reconnecting" | "dead";
|
|
70
|
+
deliveryReadiness?: "cannot_address" | "can_queue_only" | "can_send_now";
|
|
71
|
+
lastHandshake: number | null;
|
|
72
|
+
createdAt: number;
|
|
73
|
+
updatedAt: number | null;
|
|
74
|
+
/** Ed25519 signing public key (base64 SPKI DER) */
|
|
75
|
+
signingPublicKey?: string | null;
|
|
76
|
+
}
|
|
77
|
+
/** Decoded invite token */
|
|
78
|
+
export type InviteToken = TransportInviteToken;
|
|
79
|
+
/**
|
|
80
|
+
* Peer registry — wraps the network_peers table.
|
|
81
|
+
*/
|
|
82
|
+
export declare class PeerRegistry {
|
|
83
|
+
private db;
|
|
84
|
+
constructor(db: Database.Database);
|
|
85
|
+
private reconcileSchema;
|
|
86
|
+
/** Add or update a peer */
|
|
87
|
+
upsert(peer: {
|
|
88
|
+
publicKey: string;
|
|
89
|
+
nodeId?: NodeId;
|
|
90
|
+
name: string;
|
|
91
|
+
endpointHost?: string;
|
|
92
|
+
endpointPort?: number;
|
|
93
|
+
endpointRevision?: number;
|
|
94
|
+
controlEndpointHost?: string;
|
|
95
|
+
controlEndpointPort?: number;
|
|
96
|
+
controlTlsCaFingerprint?: TlsCaFingerprint;
|
|
97
|
+
presharedKey?: string;
|
|
98
|
+
allowedIps?: string;
|
|
99
|
+
inviteToken?: string;
|
|
100
|
+
status?: string;
|
|
101
|
+
signingPublicKey?: string;
|
|
102
|
+
}): void;
|
|
103
|
+
/** Get all active peers */
|
|
104
|
+
listActive(): PeerInfo[];
|
|
105
|
+
/** Get all peers (any status) */
|
|
106
|
+
listAll(): PeerInfo[];
|
|
107
|
+
/** List peers by display name. Display names are not unique principals. */
|
|
108
|
+
listByName(name: string): PeerInfo[];
|
|
109
|
+
/** Get a peer by name */
|
|
110
|
+
getByName(name: string): PeerInfo | undefined;
|
|
111
|
+
/** Get a peer by durable node identity */
|
|
112
|
+
getByNodeId(nodeId: NodeId): PeerInfo | undefined;
|
|
113
|
+
/** Get a peer by durable transport identity */
|
|
114
|
+
getByPublicKey(publicKey: string): PeerInfo | undefined;
|
|
115
|
+
/** Update peer signing key by durable peer identity */
|
|
116
|
+
setSigningKeyByPublicKey(publicKey: string, signingPublicKey: string): void;
|
|
117
|
+
/** Revoke a peer by durable node identity. */
|
|
118
|
+
revokeByNodeId(nodeId: NodeId): void;
|
|
119
|
+
/** Revoke a peer's access while preserving durable audit state for restart and introspection. */
|
|
120
|
+
revoke(publicKey: string): void;
|
|
121
|
+
/** Delete a superseded transport binding by its public key. */
|
|
122
|
+
delete(publicKey: string): void;
|
|
123
|
+
/** Check if a token nonce was already consumed */
|
|
124
|
+
isTokenConsumed(nonce: string): boolean;
|
|
125
|
+
/**
|
|
126
|
+
* P0-3: Atomically claim a token nonce. Returns true if this call claimed it
|
|
127
|
+
* (first use), false if already claimed. Uses a SQLite transaction to eliminate
|
|
128
|
+
* the race window between check and mark in acceptInvite.
|
|
129
|
+
*/
|
|
130
|
+
claimToken(nonce: string): boolean;
|
|
131
|
+
/** Release a previously claimed token (on error rollback). */
|
|
132
|
+
releaseToken(nonce: string): void;
|
|
133
|
+
/** Remove stale token claims older than maxAge (default 24h). Returns count of removed claims. */
|
|
134
|
+
cleanupStaleClaims(maxAgeMs?: number): number;
|
|
135
|
+
/**
|
|
136
|
+
* Delete disposable invite stubs that can never become active again.
|
|
137
|
+
* Revoked peers stay durable so restart reconciliation, audits, and operator
|
|
138
|
+
* surfaces can still explain why a peer disappeared from active routing.
|
|
139
|
+
* Returns count of removed rows.
|
|
140
|
+
*/
|
|
141
|
+
pruneTerminalPeers(): number;
|
|
142
|
+
/** Update last handshake timestamp */
|
|
143
|
+
updateHandshake(publicKey: string): void;
|
|
144
|
+
/** Update last handshake timestamp by durable node identity. */
|
|
145
|
+
updateHandshakeByNodeId(nodeId: NodeId): void;
|
|
146
|
+
/** Get peers by status */
|
|
147
|
+
listByStatus(status: LegacyPeerRegistryStatus): PeerInfo[];
|
|
148
|
+
/** Get a pending peer by invite token nonce */
|
|
149
|
+
getPendingByInviteToken(inviteTokenNonce: string): PeerInfo | undefined;
|
|
150
|
+
/** Count active peers */
|
|
151
|
+
activeCount(): number;
|
|
152
|
+
/** Get a peer by public key with PSK for tunnel reconstruction */
|
|
153
|
+
getWithPsk(publicKey: string): {
|
|
154
|
+
publicKey: string;
|
|
155
|
+
nodeId: NodeId | null;
|
|
156
|
+
name: string;
|
|
157
|
+
presharedKey: string | null;
|
|
158
|
+
inviteToken: string | null;
|
|
159
|
+
endpointHost: string | null;
|
|
160
|
+
endpointPort: number | null;
|
|
161
|
+
endpointRevision: number;
|
|
162
|
+
status: LegacyPeerRegistryStatus;
|
|
163
|
+
signingPublicKey: string | null;
|
|
164
|
+
} | undefined;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Generate an Ed25519 keypair for message signing.
|
|
168
|
+
* Returns base64-encoded SPKI DER (public) and PKCS#8 DER (private).
|
|
169
|
+
*/
|
|
170
|
+
export declare function generateSigningKeypair(): {
|
|
171
|
+
publicKey: string;
|
|
172
|
+
privateKey: string;
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* Generate a new X25519 keypair using node:crypto.
|
|
176
|
+
*/
|
|
177
|
+
export declare function generateKeyPair(): {
|
|
178
|
+
publicKey: string;
|
|
179
|
+
privateKey: string;
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* Create an invite token for a new peer.
|
|
183
|
+
* Encodes: leader's public key + endpoint + PSK + display snapshots.
|
|
184
|
+
*
|
|
185
|
+
* Token format: aria-net-v4-<base64url(JSON{payload,signature})>
|
|
186
|
+
* The payload is always signed with Ed25519.
|
|
187
|
+
*/
|
|
188
|
+
export declare function createInviteToken(params: {
|
|
189
|
+
leaderPublicKey: string;
|
|
190
|
+
nodeId: NodeId;
|
|
191
|
+
audienceNodeId?: NodeId;
|
|
192
|
+
leaderDisplayNameSnapshot?: string;
|
|
193
|
+
host: string;
|
|
194
|
+
port: number;
|
|
195
|
+
controlEndpoint: ControlEndpointAdvertisement;
|
|
196
|
+
displayNameSnapshot?: string;
|
|
197
|
+
durationMs?: number;
|
|
198
|
+
signingPublicKey?: string;
|
|
199
|
+
caCert?: string;
|
|
200
|
+
coordinationUrl?: string;
|
|
201
|
+
networkId?: string;
|
|
202
|
+
/** Ed25519 private key (base64 PKCS#8 DER) — required for signed v4 token */
|
|
203
|
+
signingPrivateKey?: string;
|
|
204
|
+
}): {
|
|
205
|
+
token: string;
|
|
206
|
+
psk: string;
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Decode an invite token.
|
|
210
|
+
*
|
|
211
|
+
* Accepts v4 (aria-net-v4-, Ed25519 signed) format only.
|
|
212
|
+
*/
|
|
213
|
+
export declare function decodeInviteToken(token: string): InviteToken;
|
|
214
|
+
/**
|
|
215
|
+
* Ensure a secure network exists — auto-creates on first run.
|
|
216
|
+
*
|
|
217
|
+
* This is the zero-configuration entry point. Called during ARIA startup.
|
|
218
|
+
* If no network config exists, generates keypair, picks a random port,
|
|
219
|
+
* discovers external endpoint via STUN, and saves the config.
|
|
220
|
+
*/
|
|
221
|
+
export declare function ensureSecureNetwork(ariaDir: string, arionName?: string, options?: {
|
|
222
|
+
nodeId?: NodeId;
|
|
223
|
+
}): Promise<NetworkConfig>;
|
|
224
|
+
/**
|
|
225
|
+
* NetworkManager — manages the lifecycle of all peer tunnels.
|
|
226
|
+
*
|
|
227
|
+
* Loads peers from DB on start, creates/destroys tunnels on demand,
|
|
228
|
+
* handles invite acceptance, and provides status queries.
|
|
229
|
+
*/
|
|
230
|
+
export declare class NetworkManager {
|
|
231
|
+
private static readonly MAX_PEERS;
|
|
232
|
+
/** Interval for attempting direct connection upgrade when using relay */
|
|
233
|
+
private static readonly RELAY_UPGRADE_INTERVAL_MS;
|
|
234
|
+
private config;
|
|
235
|
+
private tunnels;
|
|
236
|
+
/** In-flight tunnel startups keyed by peer public key. */
|
|
237
|
+
private pendingTunnelStarts;
|
|
238
|
+
/** Pending starts that must self-cancel before publishing an active tunnel. */
|
|
239
|
+
private canceledTunnelStarts;
|
|
240
|
+
/** Relay transports for peers behind symmetric NAT (publicKey -> relay) */
|
|
241
|
+
private relayTransports;
|
|
242
|
+
/** Periodic timers for attempting direct connection upgrade from relay */
|
|
243
|
+
private relayUpgradeTimers;
|
|
244
|
+
/** Known peer Ed25519 signing public keys keyed by display-name snapshot. */
|
|
245
|
+
private _peerSigningKeys;
|
|
246
|
+
/** Public display snapshot surfaced through runtime/publication boundaries. */
|
|
247
|
+
private _localDisplayNameSnapshot;
|
|
248
|
+
/** Our durable local node identity projected from the runtime authority. */
|
|
249
|
+
private _localNodeId;
|
|
250
|
+
/** Runtime-published local control endpoint advertisement for invite/join flows. */
|
|
251
|
+
private _localControlEndpoint;
|
|
252
|
+
/** Cached bootstrap CA authority used to mint pinned invite tokens. */
|
|
253
|
+
private _localBootstrapCaCert;
|
|
254
|
+
/** Consecutive failed upgrade attempts per relay (for exponential backoff) */
|
|
255
|
+
private relayUpgradeAttempts;
|
|
256
|
+
private appendDiagnostic;
|
|
257
|
+
/** Callback for relay data received through DERP relay.
|
|
258
|
+
* Set by daemon to wire relay-received messages into the EventQueue. */
|
|
259
|
+
onRelayDataReceived?: (data: Buffer, from: string) => void;
|
|
260
|
+
private _transportListeners;
|
|
261
|
+
private _messageListeners;
|
|
262
|
+
private _deliveryAckListeners;
|
|
263
|
+
private _peerActivationListeners;
|
|
264
|
+
/** Subscribe to transport events (tunnel/relay up/down). Returns unsubscribe fn. */
|
|
265
|
+
addTransportListener(listener: {
|
|
266
|
+
onTransportUp?: (displayNameSnapshot: string, transport: {
|
|
267
|
+
sendPlaintext(data: Buffer): void;
|
|
268
|
+
}, nodeId: NodeId) => void;
|
|
269
|
+
onRouteBootstrapAvailable?: (displayNameSnapshot: string, transport: {
|
|
270
|
+
sendPlaintext(data: Buffer): void;
|
|
271
|
+
routeBootstrapOnly?: boolean;
|
|
272
|
+
}, nodeId: NodeId) => void;
|
|
273
|
+
onTransportDown?: (displayNameSnapshot: string, nodeId: NodeId) => void;
|
|
274
|
+
}): () => void;
|
|
275
|
+
/** Subscribe to incoming messages. Returns unsubscribe fn. */
|
|
276
|
+
addMessageListener(listener: (message: unknown) => boolean | void): () => void;
|
|
277
|
+
/** Subscribe to delivery acknowledgements. Returns unsubscribe fn. */
|
|
278
|
+
addDeliveryAckListener(listener: (ack: DeliveryAck) => void): () => void;
|
|
279
|
+
/** Subscribe to verified peer activation events. Returns unsubscribe fn. */
|
|
280
|
+
addPeerActivationListener(listener: (input: {
|
|
281
|
+
nodeId: NodeId;
|
|
282
|
+
displayNameSnapshot: string;
|
|
283
|
+
signingPublicKey: string;
|
|
284
|
+
}) => void): () => void;
|
|
285
|
+
/** Get a snapshot of currently active transports (for late-bind mailbox scenarios). */
|
|
286
|
+
getActiveTransports(): Array<{
|
|
287
|
+
type: "tunnel" | "relay";
|
|
288
|
+
nodeId: NodeId;
|
|
289
|
+
displayNameSnapshot: string;
|
|
290
|
+
transport: {
|
|
291
|
+
sendPlaintext(data: Buffer): void;
|
|
292
|
+
routeBootstrapOnly?: boolean;
|
|
293
|
+
};
|
|
294
|
+
}>;
|
|
295
|
+
/** Shared UDP socket for all WG tunnels (single-port model like real WireGuard).
|
|
296
|
+
* Created in initialize(), bound to config.listenPort. All tunnels send/receive
|
|
297
|
+
* through this one socket. Incoming packets are offered to registered tunnel
|
|
298
|
+
* handlers until one claims ownership. */
|
|
299
|
+
private sharedSocket;
|
|
300
|
+
/** Packet handlers registered by tunnels using the shared socket.
|
|
301
|
+
* Each handler returns a disposition object indicating whether it handled the packet. */
|
|
302
|
+
private sharedSocketHandlers;
|
|
303
|
+
/** ExternalSocket interface exposed to SecureTunnels */
|
|
304
|
+
private get externalSocketInterface();
|
|
305
|
+
private readonly ariaDir;
|
|
306
|
+
private readonly arionName;
|
|
307
|
+
private readonly peerRegistry?;
|
|
308
|
+
constructor(ariaDir: string, arionNameOrPeerRegistry?: string | PeerRegistry, peerRegistry?: PeerRegistry);
|
|
309
|
+
private isLocalIdentityCandidate;
|
|
310
|
+
/**
|
|
311
|
+
* Remote peers and the local machine identity must never share the same registry.
|
|
312
|
+
* This guard makes the invalid state unrepresentable for all peer-registration paths.
|
|
313
|
+
*/
|
|
314
|
+
assertRemotePeerIdentity(candidate: {
|
|
315
|
+
publicKey?: string | null;
|
|
316
|
+
signingPublicKey?: string | null;
|
|
317
|
+
name?: string | null;
|
|
318
|
+
}, context?: string): void;
|
|
319
|
+
private quarantineCorruptedLocalPeerRows;
|
|
320
|
+
private resolveUniqueLivePeerPublicKeyByNodeId;
|
|
321
|
+
private getDirectRouteOwnershipSnapshot;
|
|
322
|
+
private getDirectRouteOwnershipForPublicKey;
|
|
323
|
+
private resolveCurrentDirectRoutePeerPublicKeyByNodeId;
|
|
324
|
+
private ensureCurrentDirectRouteOwner;
|
|
325
|
+
private reconcileDirectRouteOwnership;
|
|
326
|
+
private resolvePeerByNodeId;
|
|
327
|
+
private listSupersededPrincipalRows;
|
|
328
|
+
private cleanupSupersededPrincipalRows;
|
|
329
|
+
private getCachedOrDurablePeerSigningKey;
|
|
330
|
+
ensureNativeRuntimeAvailable(): Promise<void>;
|
|
331
|
+
private refreshExternalEndpointFromSharedSocket;
|
|
332
|
+
/**
|
|
333
|
+
* Resolve a unique peer name — disambiguates if the proposed name collides
|
|
334
|
+
* with our own identity or an existing peer with a different signing key.
|
|
335
|
+
* Returns the original name if no collision, or "name-<8hex>" if collision.
|
|
336
|
+
*/
|
|
337
|
+
resolveUniquePeerName(proposedName: string, signingPublicKey: string): string;
|
|
338
|
+
private _disambiguateName;
|
|
339
|
+
private matchesPeerNodeIdClaim;
|
|
340
|
+
private matchesDeliveryAckNodeId;
|
|
341
|
+
/** Stage signing-key material through the runtime-owned peer identity boundary. */
|
|
342
|
+
stagePendingPeerSigningKey(input: {
|
|
343
|
+
nodeId: NodeId;
|
|
344
|
+
peerPublicKey: PeerTransportId;
|
|
345
|
+
displayNameSnapshot: string;
|
|
346
|
+
signingPublicKey: string;
|
|
347
|
+
}): void;
|
|
348
|
+
/** Stage a direct-pair peer through the runtime-owned pending_verification transition. */
|
|
349
|
+
applyDirectPairActivation(input: {
|
|
350
|
+
nodeId: NodeId;
|
|
351
|
+
displayNameSnapshot: string;
|
|
352
|
+
peerPublicKey: PeerTransportId;
|
|
353
|
+
signingPublicKey: string;
|
|
354
|
+
transportEndpoint: TransportEndpointAdvertisement;
|
|
355
|
+
controlEndpoint: ControlEndpointAdvertisement;
|
|
356
|
+
presharedKey: string;
|
|
357
|
+
}): Promise<void>;
|
|
358
|
+
/** Get all known peer signing keys keyed by durable peer principal id. */
|
|
359
|
+
getAllPeerSigningKeysByPrincipal(): Map<NodeId, string>;
|
|
360
|
+
/**
|
|
361
|
+
* Activate a peer that is in pending_verification status.
|
|
362
|
+
*
|
|
363
|
+
* Called after the first successfully verified signed message from the peer.
|
|
364
|
+
* Updates the peer's status to "active" in PeerRegistry and invokes the
|
|
365
|
+
* onPeerActivated callback so the runtime can durably persist the peer's
|
|
366
|
+
* verified signing identity.
|
|
367
|
+
*
|
|
368
|
+
* Idempotent: no-op if the peer is already active.
|
|
369
|
+
*/
|
|
370
|
+
activatePendingPeer(nodeId: NodeId): void;
|
|
371
|
+
/** Initialize the network — ensure config exists, load peers */
|
|
372
|
+
initialize(): Promise<NetworkConfig>;
|
|
373
|
+
/** Get the current network config */
|
|
374
|
+
getConfig(): NetworkConfig | null;
|
|
375
|
+
get localNodeId(): NodeId | undefined;
|
|
376
|
+
get localControlEndpoint(): ControlEndpointAdvertisement | undefined;
|
|
377
|
+
/** Get our own local display snapshot through the runtime boundary. */
|
|
378
|
+
getLocalDisplayNameSnapshot(): string | undefined;
|
|
379
|
+
setLocalDisplayNameSnapshot(name: string): void;
|
|
380
|
+
setLocalNodeId(nodeId: NodeId): void;
|
|
381
|
+
setLocalControlEndpoint(controlEndpoint: ControlEndpointAdvertisement | undefined): void;
|
|
382
|
+
/**
|
|
383
|
+
* Update the external endpoint (called when STUN refresh detects a change).
|
|
384
|
+
* Persists to disk so the new endpoint survives restarts.
|
|
385
|
+
*/
|
|
386
|
+
updateExternalEndpoint(address: string, port: number): void;
|
|
387
|
+
/**
|
|
388
|
+
* Set the coordination server URL (persisted to disk).
|
|
389
|
+
*/
|
|
390
|
+
setCoordinationUrl(url: string): void;
|
|
391
|
+
/**
|
|
392
|
+
* Set the DERP relay server URL (persisted to disk).
|
|
393
|
+
*/
|
|
394
|
+
setRelayUrl(url: string): void;
|
|
395
|
+
/** Persist current config to disk */
|
|
396
|
+
private persistConfig;
|
|
397
|
+
/**
|
|
398
|
+
* Compute deterministic complementary tunnel IPs from key ordering.
|
|
399
|
+
*
|
|
400
|
+
* Both peers must agree on who is .1 and who is .2. We use lexicographic
|
|
401
|
+
* ordering of public keys: the smaller key gets 10.0.0.1, the larger gets
|
|
402
|
+
* 10.0.0.2. This ensures Alice.selfIp === Bob.peerIp and vice versa,
|
|
403
|
+
* making the tunnel functional in both directions.
|
|
404
|
+
*/
|
|
405
|
+
private computePeerIps;
|
|
406
|
+
/** Start a SecureTunnel for a registered peer by durable node identity. */
|
|
407
|
+
startTunnel(nodeId: NodeId): Promise<void>;
|
|
408
|
+
/** Transport-internal tunnel startup by transport key. */
|
|
409
|
+
private startTunnelByTransportKey;
|
|
410
|
+
/** Stop and remove a tunnel for a peer by durable node identity. */
|
|
411
|
+
stopTunnel(nodeId: NodeId): void;
|
|
412
|
+
/** Transport-internal tunnel shutdown by transport key. */
|
|
413
|
+
private stopTunnelByTransportKey;
|
|
414
|
+
/** Compatibility surface for the typed network manager ref: revoke by durable peer principal. */
|
|
415
|
+
revokePeer(nodeId: NodeId): boolean;
|
|
416
|
+
/** Get a tunnel by durable peer identity (for testing/inspection). */
|
|
417
|
+
getTunnel(nodeId: NodeId): ResilientTunnel | undefined;
|
|
418
|
+
/** Get the number of active tunnels */
|
|
419
|
+
get activeTunnelCount(): number;
|
|
420
|
+
/** Generate an invite token for a new peer */
|
|
421
|
+
protected resolveLocalInviteEndpoint(): {
|
|
422
|
+
address: string;
|
|
423
|
+
port: number;
|
|
424
|
+
} | null;
|
|
425
|
+
invite(displayNameSnapshot?: string, options?: number | {
|
|
426
|
+
durationMs?: number;
|
|
427
|
+
controlEndpoint: ControlEndpointAdvertisement;
|
|
428
|
+
nodeId?: NodeId;
|
|
429
|
+
caCert?: string;
|
|
430
|
+
transportHostOverride?: string;
|
|
431
|
+
}): {
|
|
432
|
+
token: string;
|
|
433
|
+
psk: string;
|
|
434
|
+
};
|
|
435
|
+
/** Accept an invite from another ARIA instance */
|
|
436
|
+
acceptInvite(token: string, options?: string | {
|
|
437
|
+
nodeId?: NodeId;
|
|
438
|
+
displayNameSnapshot?: string;
|
|
439
|
+
controlEndpoint?: ControlEndpointAdvertisement;
|
|
440
|
+
}): Promise<PeerInfo>;
|
|
441
|
+
/**
|
|
442
|
+
* Complete the join process — upgrade a pending peer to active with real credentials.
|
|
443
|
+
*
|
|
444
|
+
* Called by the leader when a peer POSTs its credentials back after accepting an invite.
|
|
445
|
+
* Removes the pending placeholder entry and registers the real peer with its actual
|
|
446
|
+
* WireGuard public key, signing key, and endpoint.
|
|
447
|
+
*/
|
|
448
|
+
completeJoin(params: {
|
|
449
|
+
nodeId: NodeId;
|
|
450
|
+
principalFingerprint: PrincipalFingerprint;
|
|
451
|
+
peerPublicKey: PeerTransportId;
|
|
452
|
+
peerSigningKey: string;
|
|
453
|
+
peerTransportEndpoint: TransportEndpointAdvertisement;
|
|
454
|
+
peerControlEndpoint: ControlEndpointAdvertisement;
|
|
455
|
+
displayNameSnapshot?: string;
|
|
456
|
+
inviteTokenNonce: string;
|
|
457
|
+
}): Promise<{
|
|
458
|
+
effectiveName: string;
|
|
459
|
+
}>;
|
|
460
|
+
/** Record a heartbeat from a peer principal (updates last_handshake timestamp) */
|
|
461
|
+
heartbeat(nodeId: NodeId): boolean;
|
|
462
|
+
/**
|
|
463
|
+
* Apply a register/reconcile mutation by durable peer identity.
|
|
464
|
+
*
|
|
465
|
+
* This centralizes the peer-state checks that used to be reimplemented
|
|
466
|
+
* route-by-route using mutable display names.
|
|
467
|
+
*/
|
|
468
|
+
applyPeerRegistration(input: {
|
|
469
|
+
nodeId: NodeId;
|
|
470
|
+
endpointHost?: string;
|
|
471
|
+
endpointPort?: number;
|
|
472
|
+
endpointRevision?: number;
|
|
473
|
+
}): NetworkPeerRegistrationResultRef | NetworkPeerRegistrationFailureRef;
|
|
474
|
+
/** Revoke a peer's access */
|
|
475
|
+
revoke(nodeId: NodeId): boolean;
|
|
476
|
+
applyPeerRevocation(input: {
|
|
477
|
+
nodeId: NodeId;
|
|
478
|
+
fingerprint?: PrincipalFingerprint;
|
|
479
|
+
}): NetworkPeerRevocationResultRef | NetworkPeerRevocationFailureRef;
|
|
480
|
+
/** List all peers */
|
|
481
|
+
listPeers(): PeerInfo[];
|
|
482
|
+
listPendingInvites(): Array<{
|
|
483
|
+
inviteId: string;
|
|
484
|
+
inviteLabel?: string;
|
|
485
|
+
createdAt: number;
|
|
486
|
+
expiresAt: number | null;
|
|
487
|
+
}>;
|
|
488
|
+
cancelInvite(inviteId: string): boolean;
|
|
489
|
+
/**
|
|
490
|
+
* Update a peer's endpoint in the registry by durable peer identity.
|
|
491
|
+
*
|
|
492
|
+
* Called when a peer's STUN-discovered IP changes and is reported via
|
|
493
|
+
* the coordination server's POST /register endpoint.
|
|
494
|
+
*/
|
|
495
|
+
updatePeerEndpoint(nodeId: NodeId, host: string, port: number, endpointRevision: number): void;
|
|
496
|
+
/**
|
|
497
|
+
* Apply a transport-only repair mutation by durable peer identity.
|
|
498
|
+
*
|
|
499
|
+
* This is the authoritative mutation boundary for operator-driven endpoint
|
|
500
|
+
* repair. It shares the same identity-state gate as register/discovery but
|
|
501
|
+
* does not manufacture a heartbeat side effect.
|
|
502
|
+
*/
|
|
503
|
+
applyPeerRepair(input: {
|
|
504
|
+
nodeId: NodeId;
|
|
505
|
+
endpointHost: string;
|
|
506
|
+
endpointPort: number;
|
|
507
|
+
endpointRevision: number;
|
|
508
|
+
}): NetworkPeerRepairResultRef | NetworkPeerRepairFailureRef;
|
|
509
|
+
/**
|
|
510
|
+
* Apply a transport endpoint projection after a NodeId-owned caller has already
|
|
511
|
+
* resolved the authoritative remote actor. Transport keys stay internal here.
|
|
512
|
+
*/
|
|
513
|
+
private applyEndpointProjectionByTransportKey;
|
|
514
|
+
/**
|
|
515
|
+
* Get the relay transport for a peer (if using DERP relay fallback).
|
|
516
|
+
* Returns the same { send } interface as tunnel transports for Mailbox compatibility.
|
|
517
|
+
*/
|
|
518
|
+
getRelayTransport(peerPublicKey: string): {
|
|
519
|
+
send: (data: Buffer) => void;
|
|
520
|
+
} | undefined;
|
|
521
|
+
/**
|
|
522
|
+
* Set up a DERP relay fallback for a peer.
|
|
523
|
+
*
|
|
524
|
+
* Called when a direct tunnel dies (MAX_RECONNECT_ATTEMPTS exhausted).
|
|
525
|
+
* Creates a DerpRelay connection and registers it as a transport.
|
|
526
|
+
* Periodically attempts to re-establish the direct tunnel via lightweight probe.
|
|
527
|
+
*/
|
|
528
|
+
setupRelayFallback(peerPublicKey: string, relay: {
|
|
529
|
+
send: (data: Buffer) => void;
|
|
530
|
+
disconnect: () => void;
|
|
531
|
+
}): Promise<void>;
|
|
532
|
+
/**
|
|
533
|
+
* Schedule a lightweight probe to check if direct tunnel is possible.
|
|
534
|
+
* Uses exponential backoff: 5min → 10min → 20min → cap at 30min.
|
|
535
|
+
* On success, tears down relay and restores direct tunnel.
|
|
536
|
+
*/
|
|
537
|
+
private scheduleUpgradeProbe;
|
|
538
|
+
/**
|
|
539
|
+
* Lightweight probe: create a temporary SecureTunnel, wait 5s for handshake.
|
|
540
|
+
* Returns true if peer is directly reachable, false otherwise.
|
|
541
|
+
* Does NOT create a persistent tunnel — just checks connectivity.
|
|
542
|
+
*/
|
|
543
|
+
private probeDirectConnection;
|
|
544
|
+
/** Tear down relay for a peer and notify listeners */
|
|
545
|
+
private tearDownRelay;
|
|
546
|
+
/** Reset relay upgrade backoff (call when STUN detects endpoint change) */
|
|
547
|
+
resetRelayUpgradeBackoff(): void;
|
|
548
|
+
/** Shut down all tunnels, relays, and clean up resources */
|
|
549
|
+
shutdown(): Promise<void>;
|
|
550
|
+
/** Get network status */
|
|
551
|
+
status(): {
|
|
552
|
+
configured: boolean;
|
|
553
|
+
nodeId: NodeId | null;
|
|
554
|
+
publicKey: PeerTransportId | null;
|
|
555
|
+
listenPort: number | null;
|
|
556
|
+
externalEndpoint: {
|
|
557
|
+
address: string;
|
|
558
|
+
port: number;
|
|
559
|
+
} | null;
|
|
560
|
+
activePeers: number;
|
|
561
|
+
totalPeers: number;
|
|
562
|
+
activeTunnels: number;
|
|
563
|
+
connectedPeers: number;
|
|
564
|
+
handshakingPeers: number;
|
|
565
|
+
queueOnlyPeers: number;
|
|
566
|
+
supersededPeers: number;
|
|
567
|
+
signingPublicKey: string | null;
|
|
568
|
+
};
|
|
569
|
+
/** Handle an incoming control request from a peer through the WG tunnel. */
|
|
570
|
+
private handleControlRequest;
|
|
571
|
+
/** Pending peer list responses from control requests we sent, keyed by peer transport identity. */
|
|
572
|
+
private pendingPeerListCallbacks;
|
|
573
|
+
/** Handle a control response from a peer (response to our request). */
|
|
574
|
+
private handleControlResponse;
|
|
575
|
+
private resolvePeerPrincipalId;
|
|
576
|
+
private getDurablePeerSigningKey;
|
|
577
|
+
private getPeerSigningFingerprintByPublicKey;
|
|
578
|
+
/**
|
|
579
|
+
* Request the peer list from a remote peer through the WG tunnel.
|
|
580
|
+
* Returns a promise that resolves with the peer list or rejects on timeout.
|
|
581
|
+
*/
|
|
582
|
+
requestPeerListViaTunnel(peerPublicKey: string, timeoutMs?: number): Promise<PeerInfo[]>;
|
|
583
|
+
/**
|
|
584
|
+
* Send a heartbeat through the WG tunnel to a peer.
|
|
585
|
+
* This is usually unnecessary — WG handshake timestamps track liveness —
|
|
586
|
+
* but can be used for application-layer keepalive.
|
|
587
|
+
*/
|
|
588
|
+
sendHeartbeatViaTunnel(peerPublicKey: string): void;
|
|
589
|
+
}
|
|
590
|
+
//# sourceMappingURL=network.d.ts.map
|