A peer-to-peer network for the sovereign age.
Rings is a browser-native, structured peer-to-peer network for applications that need their own network layer instead of a server-owned data path. Browser tabs and native daemons can join the same overlay, discover peers by DID, and exchange messages over direct WebRTC datachannels routed by a Chord DHT.
The current overlay threat model is documented in SECURITY.md. DID authentication proves key control; it is not, by itself, Sybil or eclipse resistance for permissionless public membership.
At the application layer, Rings gives developers a namespace-scoped protocol runtime: write a pure state machine, attach an interpreter shell, and run it over a decentralized overlay. Built-in protocols already cover peer service relay and fold-scheme zkSNARK proving; the roadmap extends this into a fully server-less network layer and privacy layer.
The canonical protocol paper is maintained in this repository:
If you cite Rings in academic or technical writing, use:
@misc{rings-network,
author = {Ryan J. Kung},
title = {Rings: A peer-to-peer network for sovereign age},
year = {2023},
month = feb,
url = {https://github.com/RingsNetwork/rings/blob/master/papers/rings.pdf},
note = {Repository-owned whitepaper and LaTeX source: https://github.com/RingsNetwork/rings/tree/master/papers}
}
Rings runs in browsers through WebAssembly and web_sys, and on native hosts through the same Rust node stack. WebRTC datachannels carry peer-to-peer traffic, including browser-to-browser connections without an application server in the data path.
Peers are addressed by decentralized identifiers backed by selectable signature schemes, including secp256k1, secp256r1, ed25519, BLS, and bip137. This lets Rings bridge browser, daemon, and wallet-oriented identity workflows without binding the network to one key system. See the threat model for the boundary between DID authentication and Sybil resistance.
The overlay uses a Chord DHT for successor/finger-table routing, DID lookup, message relay, stabilization, and network_id isolation. Independent overlays stay separate while retaining deterministic routing behavior. Chord routing assumes an acceptable membership model; see the overlay threat model.
Application protocols are namespace-scoped. A protocol's step function stays pure, and all side effects are performed by its Interpret shell through a scoped capability. That keeps protocol logic extensible without adding a global effect bus to the core.
You can install rings-node either from Cargo or from source.
Install the rings CLI from crates.io:
cargo install rings-node
Install the CLI from a local checkout:
git clone git@github.com:RingsNetwork/rings.git
cd ./rings
cargo install --path crates/node
Build the browser provider with Cargo and wasm-bindgen:
cargo build -p rings-node --release --target wasm32-unknown-unknown --no-default-features --features browser
wasm-bindgen --out-dir pkg --target web ./target/wasm32-unknown-unknown/release/rings_node.wasm
Or build with wasm-pack:
wasm-pack build --scope ringsnetwork -t web crates/node --no-default-features --features browser,console_error_panic_hook
rings --help
The browser and extension frontend lives in frontend. It is the user-facing Rings web surface for the landing guide, browser node console, onion proxy WorkBench, wallet login, SDP/HTTP connectivity, topology, proof workbench, and custom messages.
Runnable examples live in examples/:
| Example | What it shows |
|---|---|
native |
A minimal native node registering a custom namespaced protocol |
relay |
TCP & UDP tunnels to a peer's service over the overlay (tcp.rs / udp.rs) |
snark |
Fold-scheme zkSNARK proving / verification |
proof-demo |
A browser zk-proof app (Yew / Trunk) |
dweb |
A decentralized-web app (Yew / Trunk) |
ffi |
Driving a node over the C FFI |
A protocol is a pure state machine; all IO lives in its interpreter shell, which can only act within its own namespace. Inbound overlay messages are routed to a protocol by namespace.
// Register a pure Protocol + its Interpret shell, then route inbound envelopes to it.
provider.register_protocol(Echo, EchoShell)?;
provider.set_backend()?;
// Built-in relay: tunnel a local socket to a peer's service over the overlay — no server.
let relay = RelayHandle::install(&provider.extensions())?;
relay.register_tcp_service("web".into(), "example.com:80".parse()?).await?; // server side
relay.open_tcp_tunnel(local_addr, peer_did, "web".into()).await?; // client side
In the browser a protocol can be a JS handler instead: provider.on(namespace, initialState, handler). See examples/relay and crates/node/src/extension.
| Resource | Link | Notes |
|---|---|---|
| Rings Whitepaper | PDF, LaTeX source, citation | Canonical protocol paper |
| Security model | SECURITY.md | Overlay assumptions, deployment models, and Sybil boundary |
| Browser frontend | frontend |
Landing guide, web app, and extension workflow |
| Examples | examples/ |
Native, dweb, proof, relay, snark, and FFI examples |
core: DHT, swarm, DID routing, messages, and cryptographic identity primitives.
node: Native daemon, browser/WASM provider, extension runtime, relay protocol, and FFI provider.
rpc: Rings RPC shared types and the JSON-RPC client/handlers (over HTTP).
derive: Rings macros, including wasm_export macro.
transport: Native WebRTC transport and web_sys-based browser transport.
snark: Fold-scheme zkSNARK proving and verification protocol.
Rings is layered so that every layer is decentralized — there is no server in the data path. This is a data-path topology statement, not a permissionless Sybil-resistance claim; see SECURITY.md. Each layer maps directly to a crate/module:
┌──────────────────────────────────────────────────────────────────────┐
│ Applications dWeb · zk-proof demo · relay/tunnel · your own app │
├──────────────────────────────────────────────────────────────────────┤
│ Protocols built-ins: relay (tcp/udp tunnels), SNARK, echo — │ node::extension::protocols
│ (namespaced) plus any user Protocol, addressed by namespace │ crates/snark
├──────────────────────────────────────────────────────────────────────┤
│ Extension pure `Protocol::step` → `Effect` → `Interpret` shell │ node::extension::ext
│ runtime over a namespace-scoped `Scope` (send / self-inject) │
├──────────────────────────────────────────────────────────────────────┤
│ Overlay Chord DHT: successor / finger tables, stabilization, │ crates/core
│ (routing) DID addressing, message relay, network_id isolation │
├──────────────────────────────────────────────────────────────────────┤
│ Transport direct WebRTC datachannels (native + browser/web_sys), │ crates/transport
│ STUN / ICE / SDP NAT traversal │
├──────────────────────────────────────────────────────────────────────┤
│ Identity DID + secp256k1 / secp256r1 / ed25519 / BLS / bip137 │ crates/core::ecc
└──────────────────────────────────────────────────────────────────────┘
external_ip, webrtc_udp_port_min, and webrtc_udp_port_max; for example, 49160..=49200 maps to an AWS security-group rule for UDP 49160-49200. Browser nodes still use the browser ICE stack, whose local UDP ports are not controlled by Rings.network_id.step), and all IO happens in its Interpret shell, which only ever receives a namespace-scoped capability (Scope). The core owns no global effect/command bus — adding a protocol never touches it, and a protocol cannot reach another namespace.provider.register_protocol(..) (Rust) or provider.on(namespace, ..) (JS).Where this is heading — a fully server-less, sovereign network layer and privacy layer — is described in ROADMAP.md.
We welcome contributions to rings-node!
If you have a bug report or feature request, please open an issue on GitHub.
If you'd like to contribute code, please follow these steps:
Fork the repository on GitHub.
Create a new branch for your changes.
Make your changes and commit them with descriptive commit messages.
Push your changes to your fork.
Create a pull request from your branch to the main repository.
We'll review your pull request as soon as we can, and we appreciate your contributions!