| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
River is a group chat app whose backend is a global peer-to-peer network instead of cloud servers. Nobody runs a server for River, and nobody self-hosts: every room lives on Freenet, a network made up of the computers of the people who run it. Because the network caches and serves each room from nodes near whoever's asking, a busy room gets more capacity as it gets more popular, the opposite of the server model where traffic is a bill someone has to pay.
Most apps you use are shaped like this:
your browser ──▶ servers you operate ──▶ a database you operate
River is shaped like this:
your browser ──▶ Freenet, a global peer-to-peer network
(that nobody operates for River)
There is no backend behind River that anyone runs or pays for. River is the chat app, but the thing it demonstrates is bigger: an application built this way doesn't need a backend at all. You install Freenet once, and any number of apps can run on it the same way.
River is decentralized in the original sense of the word: no servers, no company, and no blockchain, token, or coins anywhere. It is just code running on a shared network.
On Freenet, the unit of deployment is a contract: a small piece of WebAssembly that defines what valid state looks like and how it is allowed to change. A River room is one such contract. Its state (members, messages, bans, encryption keys) lives on the network, replicated across the nodes that care about it, and every change is cryptographically signed so any node can verify it without trusting anyone.
That design has a few consequences worth calling out:
Global scalability is the property Freenet is designed around, using small-world routing so any node can locate any contract in a logarithmic number of hops. River inherits it for free.
Those systems decentralize to different degrees, but each keeps servers somewhere:
River keeps each room as state hosted by the Freenet network itself: no company server, no homeserver to choose or operate, and rooms that persist when their creator is offline.
The only thing you need to use River is Freenet. There is no River server to sign up for and no account to create.
River opens in your browser and works on both desktop and mobile. From there you can create your own rooms and share their invite links; to join someone else's room you need an invitation from a current member.
riverctl is a command-line client for driving River rooms from a terminal or script, handy for automation, bots, and AI agents. It is an alternative to the browser UI, not a requirement for using River.
It is a Rust crate, so install the Rust toolchain first, then:
cargo install riverctlCommands include:
Use --format json for machine-readable output. Run riverctl --help for full documentation.
You only need this to hack on River itself; using River needs nothing but Freenet (above). To build and run the UI locally:
Install dependencies:
# Install Rust using rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Add the wasm target
rustup target add wasm32-unknown-unknown
# Install ssl development library
# This example is for Ubuntu and may be different on your system
sudo apt-get install libssl-dev
# Install Node.js (v20+) and npm
# See https://nodejs.org/en/download for installation instructions.
# Note: system packages (e.g. `apt-get install npm`) may ship outdated
# versions that don't work. Install from the official site instead.
# Install build tools
cargo install dioxus-cli
cargo install cargo-makeBuild and run with example data:
# Clone the repository
git clone git@github.com:freenet/river.git
# Enter the repository
cd river
# Initialize freenet submodule
git submodule init
git submodule update
# Install Node.js dependencies (Tailwind CSS)
cd ui && npm install && cd ..
# Run development server with example data
cargo make dev-exampleOpen http://localhost:8080 in your browser
The UI runs with example data and without syncing to Freenet, which is ideal for development. Two feature flags control that behavior:
They can be combined when building:
# Build with example data
cargo make build-ui-example
# Build without Freenet sync
cargo make build-ui-no-sync
# Build with both features
cargo make build-ui-example-no-syncRiver manages room membership with an invitation tree:
Room: freenet (Owner: owner) │ ├─── User: alice │ │ │ ├─── User: bob │ │ │ │ │ └─── User: charlie │ │ │ ├─── User: dave │ │ │ └─── User: eve │ └─── User: frank
For example, if alice invited bob, who then invited charlie, alice can ban charlie directly, because charlie is downstream of her in the tree:
Room: freenet (Owner: owner) │ ├─── User: alice │ │ │ ├─── User: bob │ │ │ │ │ └─── Banned User: charlie │ │ │ ├─── User: dave │ │ │ └─── User: eve │ └─── User: frank
Permissioning cascades down the tree: anyone higher in the chain has authority over those beneath them.
Private rooms encrypt every message body, every member nickname, and the room's name and description using a member-only AES-256-GCM secret. Without that secret, none of that content is readable: not by the network, not by Freenet nodes, not by anyone who happens to be storing the contract. The secret is rotated weekly, immediately when a member is banned, and on demand by the owner.
We use a shared room secret with scheduled rotation rather than a per-message ratcheting scheme like Signal's double ratchet or MLS. The shared-secret design fits Freenet's state-based contract model: any member can decrypt the room's current state and history on demand without needing to be online for every message, at the cost of the finer-grained forward secrecy those protocols provide between rotations.
What private rooms don't hide is metadata. Anyone who can read the room's contract from the network can still observe:
Each member is identified by a per-room key, not a global identity, so those keys don't by themselves link a member to anything outside the room. If your threat model requires unobservability, not just confidentiality, River alone is not sufficient today.
River is built with:
The chat room contract uses Freenet's composable state pattern. The core state structure is defined in common/src/room_state.rs:
pub struct ChatRoomStateV1 {
pub configuration: AuthorizedConfigurationV1, // Room settings and privacy mode
pub bans: BansV1, // List of banned users
pub members: MembersV1, // Current room members
pub member_info: MemberInfoV1, // Member metadata like nicknames
pub secrets: RoomSecretsV1, // Encrypted room secrets for private rooms
pub recent_messages: MessagesV1, // Recent chat messages
pub upgrade: OptionalUpgradeV1, // Optional upgrade to new contract
}Each component is a separate module with its own state management:
All state changes are signed using elliptic-curve cryptography. That is what makes a room's data self-validating: any node can host or migrate it without being able to forge it.
River is open-source software licensed under the LGPL License. See LICENSE for details.
| Back | FazBrowse Home | New Git URL |