npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nightnetwork/moonbeam

v1.0.1

Published

Browser network stack. Wisp relay, virtual LAN, TCP/UDP NAT.

Readme

@nightnetwork/moonbeam

Browser network stack. Wisp relay, virtual LAN, TCP/UDP NAT.

Complete browser-side networking library providing a Wisp v2.1 client, in-page relay with local TCP listener registration, virtual LAN gateway with DHCP, TCP/UDP NAT, configurable egress policy, and QEMU-wasm integration.

Version 1.0.0 · Apache-2.0


Features

MoonbeamRelay

In-page Wisp relay endpoint. Accepts Wisp frames from local consumers (Nova, other WASM clients) over MessagePort transports and forwards them through a shared upstream WispClient.

  • MessagePort transport — each attached client gets its own MessagePort; no WebSocket per client
  • Stream-level pass-through — stream IDs are rewritten to relay-unique IDs before forwarding upstream, rewritten back on the return path
  • Local TCP listener registrationregisterListener(host, port, handler) intercepts CONNECT requests matching a host:port and routes them to an in-process handler instead of upstream
  • Virtual IP pool — each attached client receives a unique 100.64.0.x address from a 254-address CGNAT pool
  • Flow control — CONTINUE credit protocol for TCP streams; bounded receive windows for local streams with automatic replenishment
  • Client snapshotsclientsSnapshot() and listenersSnapshot() return frozen observability views of all attached clients, their streams, and registered listeners

WispClient

From-scratch Wisp v2.1 protocol client.

  • Wisp v2.1 and v1 support — full v2 INFO handshake with extension negotiation; v1 fallback enabled by default (allowV1: true)
  • WebSocket transport — single multiplexed WebSocket connection; works in browsers, Web Workers, and Node 22+
  • Stream multiplexing — create TCP and UDP streams over one connection with automatic stream ID management
  • CONTINUE credit protocol — per-stream TCP backpressure with credit tracking and send queuing
  • Dual API — both Streams API (readable/writable) and event API (on('data'), on('close'), etc.) per stream
  • Extension negotiation — UDP, Stream Open Confirmation, MOTD, password/pubkey auth detection
  • Error codesE_WISP_HANDSHAKE, E_WISP_V1_UNSUPPORTED, E_WISP_AUTH_REQUIRED, E_WISP_STREAM_ID_EXHAUSTED, E_WISP_UDP_UNSUPPORTED

Gateway

Top-level orchestrator composing a virtual LAN from all subsystems.

  • Full virtual LAN — lwIP NetworkStack with Tun, Loopback, and Tap interfaces wired in LIFO order per spec §2.4
  • Automatic DHCP — single-client DHCP lease for the VM (configurable or disabled with vmIp: null)
  • Soft-router — parallel IP forwarder that intercepts off-subnet guest packets and routes them to the NATs, bypassing lwIP's endpoint-only limitation
  • Host-side service bindingsconnectTcp(), listenTcp(), openUdp() for host code
  • Hot-swap supportsetWispClient() atomically swaps the upstream Wisp connection; NATs read the current client via callback
  • Lifecycle eventsconnect, disconnect, error, wisp-changed

TcpNat

Stateful TCP NAT translating guest IPv4/TCP packets into multiplexed Wisp TCP streams.

  • NAT transparency — guest sees responses as if from the remote server (src=remote, dst=guest)
  • Sequence-number tracking — u32 modular arithmetic per RFC 793; out-of-order packets dropped (guest retransmits)
  • MSS chunking — upstream Wisp chunks are segmented to 1460B MSS before delivery to guest (avoids virtio-net oversized frame crashes)
  • Swap-in-progress queue — SYNs during Wisp hot-swap are buffered (bounded, default 256) and drained when the new client is live
  • Stream Open Confirmation — defers SYN-ACK until upstream confirms (when negotiated), giving the guest ECONNREFUSED semantics
  • resetAll() — RST every active flow for hot-swap orchestration

UdpNat

Stateful UDP NAT with per-5-tuple flow tracking.

  • LRU eviction — capped at maxFlows (default 1024) with least-recently-used eviction
  • Idle sweeper — closes flows idle longer than idleTimeoutMs (default 60s)
  • ICMP port-unreachable — synthesized for policy-denied or swap-in-progress datagrams
  • StatsgetStats() returns active flows, total created, forwarded, and dropped counts

EgressPolicy

Configurable allow/deny policy for outbound connections.

  • Allow tokens'public' (excludes all IANA reserved ranges), '*' (everything except loopback unless opted in), or explicit CIDR strings
  • Deny list — CIDR-based hard deny (wins over allow)
  • Port deny — block specific ports globally
  • Loopback guardallowLoopback: false (default) prevents routing localhost through remote Wisp
  • onBlocked callback — receives structured BlockedInfo with IP, port, proto, and reason (rfc1918, cgnat, loopback, multicast, deny-cidr, deny-port, etc.)
  • Allocation-free success pathpermits() allocates nothing on allow; only the blocked path creates objects

FakeWebSocket

QEMU-wasm WebSocket interception.

  • WebSocket-shaped API — extends EventTarget; supports addEventListener, on* setters, send(), close(), readyState, binaryType
  • FakeWebSocketHost interfaceonConnect, onSend, onClose callbacks route bytes into the in-page lwIP stack
  • installFakeWebSocket(Module, host) — patches Emscripten's Module.websocket.WebSocketConstructor and optionally globalThis.WebSocket with sentinel-URL detection; returns an uninstall function
  • No globals in workers — auto-detects WorkerGlobalScope and skips the global patch unless forceGlobalPatch: true

Packet helpers

Pure-function codec for Ethernet, IPv4, TCP, UDP, ICMP, and ARP.

  • parseIPv4 / buildIPv4Packet
  • parseTcp / buildTcpSegment
  • parseUdp / buildUdpSegment
  • parseEthernet / buildEthernetFrame
  • buildIcmpPortUnreachable
  • ipToNum / numToIp / parseCidr / isInSubnet / macEquals

Installation

npm install @nightnetwork/moonbeam

Requires a modern JS runtime (browser or Node ≥ 20) with EventTarget, Uint8Array, ReadableStream/WritableStream, and WebSocket. Cross-origin isolation (COOP same-origin + COEP require-corp) is needed if callers use SharedArrayBuffer.


Quick start

MoonbeamRelay with Nova

import { MoonbeamRelay } from '@nightnetwork/moonbeam';

// Create a relay backed by a Wisp server
const relay = await MoonbeamRelay.create({
  wispUrl: 'wss://your-wisp-server/',
});

// Attach a client — returns a MessagePort to hand to Nova
const port = relay.attach({ label: 'nova-worker' });

// Register a local TCP listener (e.g., for localhost:8080)
const unregister = relay.registerListener('localhost', 8080, (socket) => {
  socket.onData((data) => {
    // Handle incoming data from the client
    socket.send(new TextEncoder().encode('HTTP/1.1 200 OK\r\n\r\nHello'));
    socket.close();
  });
});

// Observability
console.log(relay.clientCount());        // 1
console.log(relay.clientsSnapshot());    // frozen snapshot of all clients + streams
console.log(relay.listenersSnapshot());  // frozen snapshot of registered listeners

// Cleanup
unregister();          // remove the listener
relay.detach(port);    // detach the client
await relay.close();   // tear down relay + upstream WebSocket

Gateway with QEMU-wasm

import {
  Gateway,
  FakeWebSocket,
  installFakeWebSocket,
  type FakeWebSocketHost,
} from '@nightnetwork/moonbeam';

// 1. Create and initialize the gateway
const gateway = new Gateway({
  wispUrl: 'wss://your-wisp-server/',
  egress: { allow: ['public'] },
  gatewayIp: '192.168.127.1/24',
});
await gateway.init();

// 2. Wire QEMU-wasm's networking through the gateway
const tap = gateway.getTap();
const softRouter = gateway.getSoftRouter();

const host: FakeWebSocketHost = {
  onConnect(send) {
    // Pump frames from tap + soft-router to QEMU
    const reader = tap.readable.getReader();
    (async () => {
      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        send(value);
      }
    })();
    softRouter.setSend(send);
  },
  onSend(data) {
    // Guest frame → lwIP + soft-router
    const writer = tap.writable.getWriter();
    writer.write(data).then(() => writer.releaseLock());
    softRouter.ingress(data);
  },
  onClose() {
    gateway.destroy();
  },
};

// 3. Install the FakeWebSocket before QEMU boots
const uninstall = installFakeWebSocket(Module, host);

// QEMU's `-netdev socket,connect=ws://wisp-gateway.local/` will be
// intercepted and routed through the in-page gateway.

API reference

MoonbeamRelay

class MoonbeamRelay {
  static create(opts: MoonbeamRelayOptions): Promise<MoonbeamRelay>
  attach(metadata?: MoonbeamAttachmentMetadata): MessagePort
  detach(port: MessagePort): void
  registerListener(host: string, port: number, handler: MoonbeamLocalConnectionHandler): () => void
  close(): Promise<void>
  clientCount(): number
  streamCount(): number
  clientsSnapshot(): readonly MoonbeamClientSnapshot[]
  listenerCount(): number
  listenersSnapshot(): readonly MoonbeamListenerSnapshot[]
  isClosed(): boolean
}

| Method | Description | |---|---| | create(opts) | Async factory. Connects to the upstream Wisp server and resolves when the handshake completes. | | attach(metadata?) | Attach a new client. Returns a MessagePort the client uses to speak Wisp frames. Assigns a virtual IP from the 100.64.0.0/24 pool. | | detach(port) | Detach a client by its MessagePort. Closes all streams and reclaims the virtual IP. | | registerListener(host, port, handler) | Register a local TCP listener. CONNECT requests matching host:port are routed to handler instead of upstream. Returns an unregister function. | | close() | Gracefully shut down: detach all clients, close listeners, close the upstream WispClient. | | clientCount() | Number of currently attached clients. | | streamCount() | Total open upstream + local streams across all clients. | | clientsSnapshot() | Frozen snapshot of all clients and their streams (id, virtualIp, label, connectedAt, streams). | | listenerCount() | Number of registered listeners. | | listenersSnapshot() | Frozen snapshot of all listeners (host, port, activeStreams). | | isClosed() | Whether the relay has been closed. |

Gateway

class Gateway {
  constructor(config: GatewayConfig)
  init(): Promise<void>
  destroy(): Promise<void>
  getStack(): NetworkStack
  getTap(): TapInterface
  getTun(): TunInterface
  getSoftRouter(): SoftRouter
  getVmIp(): string
  get wisp(): WispClient
  get swapInProgress(): boolean
  connectTcp(host: string, port: number): Promise<TcpSocket>
  listenTcp(port: number): Promise<TcpListener>
  openUdp(port?: number): Promise<UdpSocket>
  setWispClient(newClient: WispClient): void
  on(event: string, listener: Function): () => void
}

| Option | Default | Description | |---|---|---| | wispUrl | (required) | Wisp v2.1 endpoint URL | | egress | { allow: ['public'] } | Egress policy configuration | | gatewayIp | '192.168.127.1/24' | LAN-side gateway IP in CIDR | | gatewayMac | '02:00:00:00:00:01' | Gateway MAC address | | vmIp | gateway + 1 | VM IP for DHCP; null disables DHCP | | dnsServer | '1.1.1.1' | DNS advertised via DHCP | | dhcpLeaseTime | 86400 | DHCP lease duration in seconds | | tunIp | '240.0.0.1/0' | Tun interface IP (Class E, never-routable) | | allowV1 | true | Accept Wisp v1 servers | | udpAssumedInV1 | true | Assume UDP support for v1 servers |


Architecture overview

┌─────────────────────────────────────────────────────────┐
│  Browser page                                           │
│                                                         │
│  ┌──────────┐     MessagePort      ┌────────────────┐   │
│  │  Nova /  │◄────────────────────►│ MoonbeamRelay  │   │
│  │  WASM    │  Wisp frames         │  (in-page)     │   │
│  └──────────┘                      └───────┬────────┘   │
│                                            │            │
│  ┌──────────┐   FakeWebSocket    ┌─────────▼────────┐   │
│  │  QEMU-   │◄──────────────────►│    Gateway       │   │
│  │  wasm    │  Ethernet frames   │  ┌─────────────┐ │   │
│  └──────────┘                    │  │  lwIP Stack  │ │   │
│                                  │  │  Tap/Tun/Lo  │ │   │
│                                  │  └──────┬──────┘ │   │
│                                  │         │        │   │
│                                  │  ┌──────▼──────┐ │   │
│                                  │  │ SoftRouter   │ │   │
│                                  │  └──┬───────┬──┘ │   │
│                                  │     │       │    │   │
│                                  │  ┌──▼──┐ ┌──▼──┐│   │
│                                  │  │TcpNat│ │UdpNat││   │
│                                  │  └──┬──┘ └──┬──┘│   │
│                                  │     │       │    │   │
│                                  │  ┌──▼───────▼──┐│   │
│                                  │  │ EgressPolicy ││   │
│                                  │  └──────┬──────┘│   │
│                                  └─────────┼───────┘   │
│                                            │            │
│                                     ┌──────▼──────┐     │
│                                     │ WispClient  │     │
│                                     │ (WebSocket) │     │
│                                     └──────┬──────┘     │
└────────────────────────────────────────────┼─────────────┘
                                             │
                                    ┌────────▼────────┐
                                    │  Wisp Server    │
                                    │  (remote)       │
                                    └─────────────────┘

The Gateway path handles full virtual-machine networking: guest Ethernet frames flow through the lwIP stack and soft-router. Off-subnet IP packets are extracted, passed through the TCP/UDP NATs, checked against the egress policy, and forwarded as Wisp streams.

The MoonbeamRelay path is lighter-weight: WASM clients speak Wisp directly over MessagePort. The relay rewrites stream IDs and multiplexes all clients onto a single upstream WispClient. Local TCP listeners allow in-process request interception without hitting the network.


Testing

npm test              # 330+ unit tests via Vitest
npm run test:watch    # watch mode
npm run typecheck     # tsc --noEmit (see known issue below)
npm run build         # emit dist/ (tsc + ESM extension fixer)

The test suite covers all components: WispClient handshake (v1 and v2), stream multiplexing, credit protocol, MoonbeamRelay attach/detach/listener lifecycle, TCP NAT connection/data/FIN/RST/swap-queue flows, UDP NAT flow tracking/eviction/ICMP, DHCP DISCOVER/REQUEST/NAK/RELEASE, egress policy allow/deny/CIDR/port/loopback, FakeWebSocket state machine, soft-router MAC learning and IP forwarding, and packet codec round-trips.

Known issue: npm run typecheck currently reports errors due to a rootDir/include contradiction inherited from the original tsconfig. Runtime, build, and tests are unaffected — Vitest uses esbuild and does not hit this path.


Browser requirements

  • Modern browser or Node ≥ 20
  • EventTarget, MessageChannel, MessagePort
  • ReadableStream, WritableStream (WHATWG Streams)
  • WebSocket (for WispClient; not needed if using _injectWebSocket)
  • Uint8Array, DataView, ArrayBuffer
  • Cross-origin isolation headers if using SharedArrayBuffer

Migration from 0.x

0.x → 1.0.0

  • Stable API — all public interfaces are now considered stable. The MoonbeamRelay, WispClient, Gateway, and all NAT/policy/packet APIs have settled.
  • MoonbeamRelay — introduced in 0.2, now fully stable with local TCP listener support, virtual IP assignment, and observability snapshots.
  • No breaking changes from 0.3 — the 1.0 release is a stability milestone. All existing imports and usage patterns continue to work.
  • Package access — published as public on npm (was restricted in 0.x).

Contributing

  1. Fork & clone
  2. npm install
  3. npm test — all tests must pass
  4. npm run typecheck — note the known tsconfig issue; focus on new code being type-clean
  5. Open a PR against main

License

Apache-2.0