Skip to content

dweb · application-level networking

Application-level logical networks — game rooms, not system VPNs.

Multi-device applications form logical networks — like game rooms, not a system-level VPN — with controlled, invite-based membership. Peers connect directly over QUIC when possible and fall back to a self-hostable relay.

Ed25519 EndpointId Signed-fact Roster QUIC direct + relay fallback MIT OR Apache-2.0
GitHub ↗ Quick start
opendweb — server

$npx opendweb server

* opendweb server v0.2.1

> Local: http://localhost:8787

> Network: http://192.168.1.100:8787

gateway 8787 · rendezvous 8787 · relay 3340

What’s inside

Identity

Ed25519 EndpointId

Stable identity decoupled from network addresses; z-base-32 display form.

identity

Roster

Signed facts, converged by union-merge

Genesis/Grant/Join/Revoke facts are content-addressed (BLAKE3) and converge by union-merge.

roster

Membership

Controlled, invite-based joins

Issuer-online single redemption: challenge-response PoP plus invite_id CAS consumption.

invites

Session

iroh 1.1: QUIC direct + relay fallback

Direct connections with NAT traversal; falls back to a self-hostable relay. Gating on both sides — gate before data.

session

Sync

Opaque envelopes, bidirectional

Send/receive opaque envelopes both ways; an Automerge adapter is planned as a separate change.

sync

Self-hosting

One-liner server

npx opendweb server or docker ghcr.io/gaubee/dweb — gateway 8787 + relay 3340.

server

Plugins

Marketplace, vendor-neutral core

Any non-builtin first token dispatches adaptively; missing plugins fetch on first use. Cloudflare Tunnel ships as a plugin.

plugins

SDK

Node SDK (napi-rs)

@jixo/opendweb-client-sdk embeds fabrics in your own app (darwin-arm64 / win32-x64).

sdk

Quick start

Start the server, invite a peer, chat

The gateway (8787) serves /healthz, /services.json and rendezvous; the relay (3340) is a separate listener. Invites must be redeemed while the inviter is online — the inviter's process stays running during redemption.

quick-start.sh
# 1. Start the self-hosted server (gateway + relay) — top-level CLI
npx opendweb server
#   or: docker run -p 8787:8787 -p 3340:3340 ghcr.io/gaubee/dweb
#   The banner lists every Network address. Any of them is the single
#   config entry for clients — the gateway discovers the relay URL
#   automatically via /services.json.

# 2. On each client machine: one-time config (persisted to ~/.opendweb/config.json)
npx @jixo/opendweb-example config set relay http://192.168.2.13:8787

# 3. Terminal A: initialize and keep a chat session running
npx @jixo/opendweb-example init --data ~/.dweb-a
npx @jixo/opendweb-example invite --data ~/.dweb-a --ttl 30m   # copy the token
npx @jixo/opendweb-example chat --data ~/.dweb-a

# 4. Terminal B (another directory/device): redeem the invite and chat
npx @jixo/opendweb-example join --data ~/.dweb-b <token>
npx @jixo/opendweb-example chat --data ~/.dweb-b
expected server banner
* opendweb server v0.2.1
> Local:   http://localhost:8787
> Network: http://192.168.1.100:8787

  NAME         PORT   STATE
  gateway      8787   entry point
  rendezvous   8787   merged into gateway
  relay        3340   enabled

Self-host without npx: docker ghcr.io/gaubee/dweb. Behind a reverse proxy or tunnel, set DWEB_PUBLIC_GATEWAY_URL / DWEB_PUBLIC_RELAY_URL — see the README for the vendor-neutral recipe.

Packages

One CLI, one server binary, one SDK — all published

All packages are published at v0.2.1. The server CLI is the marketplace host; plugins and the Node SDK extend the same fabric.

npm packageRole
opendwebServer CLI — `npx opendweb server` starts the self-hosted gateway + relay; plugin marketplace host
@jixo/opendweb-server-binaryServer binary wrapper used by the CLI; also exposes a programmatic `startServer()`
@jixo/opendweb-exampleReference two-process client CLI (`init` / `invite` / `join` / `chat`)
@jixo/opendweb-client-sdkNode SDK for embedding fabrics in your own app (napi-rs; darwin-arm64 / win32-x64)
@jixo/opendweb-config`definePlugin` helper for local plugin files (runtime-agnostic: deno / bun / node)
@jixo/opendweb-ext-cfCloudflare Tunnel plugin: ingress push via API, DNS routing, end-to-end verification, optional cloudflared co-spawn

Ecosystem

Vendor integrations are plugins; your app embeds the SDK

The CLI core stays vendor-neutral by construction — any non-builtin first token dispatches to a marketplace plugin. The Cloudflare Tunnel plugin covers no-public-IP deployments; the Node SDK embeds fabrics directly.

Plugin — Cloudflare Tunnel

opendweb — cf plugin
opendweb plugin add cf            # install (detected pm), lock name@version
opendweb cf setup --hostname dweb.example.com
                                  # push ingress via CF API, route DNS,
                                  # write opendweb.config.toml, verify end-to-end
opendweb cf plan --hostname dweb.example.com   # zero-side-effect preview

Node SDK — fabric in your app

sdk.cjs
const { Fabric } = require("@jixo/opendweb-client-sdk");

const relay = { mode: "custom", urls: ["http://192.168.2.13:3340"] };

// Machine A: create the fabric (this node becomes root) and sign an invite
const a = await Fabric.createRoot({ dataDir: "/path/a", relay });
const token = await a.invite(60 * 60_000, null); // dweb1.-prefixed token

// Machine B: redeem the token (issuer must be online) and exchange messages
const b = await Fabric.joinWithToken({ dataDir: "/path/b", relay }, token);
await b.connect(a.endpointId);
await a.send(b.endpointId, Buffer.from("ping"));
await a.revoke(b.endpointId); // root-only

Event stream: peer-connected / peer-disconnected, roster-updated, message, path-changed (direct / relay), relay-online / relay-offline. Full contract on GitHub.