Northstar is a protocol for personal computing on the open web. Every user is a DID, every node is a server they own, every beam lives on the author's node. Channels, rooms, and live streams federate between nodes through signed events and gossip. No platforms in the middle.
Every user is a decentralized identifier — an Ed25519
keypair represented as did:key:z6Mk…. Identity is portable across
nodes. The keys never leave your client.
Each node — a €4 VPS, a Pi in the closet, a managed host — hosts the spaces you own and your beams. Beams are content-addressed and live on the author's node: spaces store refs, not copies. Two nodes talk over WebSocket and propagate events through epidemic gossip.
Northstar is a federated social network a substrate. What gets built on top — chat, communities, payment rails, livestreaming, document collaboration, group decisions — is application code. The protocol provides the primitives.
The whole protocol fits in your head. If you've worked with event sourcing and DDD, you'll recognize most of it. If you haven't, the surface area is small enough to learn in an afternoon.
Decentralized identifier from an Ed25519 keypair —
did:key:z6Mk…. Self-certifying: the public key is embedded
in the DID itself. Portable across nodes. The private key never leaves
your client.
import { NorthstarClient } from "@northstar/client" // Connect · auth is automatic (challenge-response) const client = await NorthstarClient.connect( "wss://your-node.example" ) console.log(client.did) // did:key:z6Mk…
channel for public broadcast (subreddit-like).
dm for private groups (chat-like).
stream for live broadcast (one writer, many readers).
Each space has owner, members, append-only event log.
// Create a channel · owner is you const channel = await client.space.create({ type: "channel", name: "My Channel", uniqueId: "my-channel", }) // → { id, type, name, ownerDid, nodeAddress }
Beams are content-addressed at beam/{authorHash}/{beamId}
and stored on the author's node. Spaces store refs, not copies.
SDK signs automatically. Tombstone propagation on delete.
// Beam into space · SDK signs automatically const { eventId } = await client.space.beam( channel.id, { type: "beam", title: "On sovereign substrates", body: "Email is older than most platforms…", } ) // Beam stored on your node, ref gossiped to members
Owner node assigns sequence and signs. Broadcasts via a tier-based DAG tree — owner sends to 10 tier-1 children, each forwards to their 10 children. Events reach all member nodes within seconds, no central relay.
// Subscribe to real-time events await client.subscribe(channel.id) client.on("event", (event) => { // signed, sequenced, deduped console.log(event.body, event.authorDid) })
A Reddit-equivalent on Northstar is hundreds of lines of application code, not a multi-year platform engineering project, because the substrate handles identity, authentication, signed history, federation, and gossip.
The SDK handles authentication, signing, and cross-node routing. Connect to your node, join a space, and beam. The content lives on your node; the channel stores a reference. All members receive updates through gossip.
import { NorthstarClient } from "@northstar/client" // Connect and authenticate (automatic) const client = await NorthstarClient.connect( "wss://your-node.example" ) // Join a remote space await client.space.join( "northstar://other-node.example/channel/z6MkfA/protocols" ) // Beam into space · SDK handles signing await client.space.beam("channel/z6MkfA/protocols", { title: "On sovereign substrates", body: "Email is older than most platforms…", }) // Space owner verifies, assigns sequence, // gossips to 5 random member nodes. // Within seconds, all members see the beam.
import { NorthstarClient } from "@northstar/client" const client = await NorthstarClient.connect("wss://node.example") // Create a private space const room = await client.space.create({ type: "dm", name: "Project Chat", uniqueId: "project-chat", }) // Share space link · members join themselves const link = NorthstarClient.toLink(room) // → "northstar://node.example/dm/z6Mk.../abc" // Subscribe to space events await client.subscribe(room.id) client.on("event", render) // Send a message · SDK signs automatically await client.space.append(room.id, { type: "message", body: "hey, this room is up", })
import { NorthstarClient } from "@northstar/client" // ═══ BROADCASTER ═══════════════════════════════════════ const broadcaster = await NorthstarClient.connect("wss://node.example") const stream = await broadcaster.stream.start({ title: "Studio session", latencyProfile: "low", }) // Share stream link const link = NorthstarClient.toLink(stream) // → "northstar://node.example/stream/z6Mk.../live" // Capture → encode → push signed chunks const recorder = new MediaRecorder(mediaStream) recorder.ondataavailable = async (e) => { const bytes = await e.data.arrayBuffer() const base64 = btoa(String.fromCharCode(...new Uint8Array(bytes))) await broadcaster.stream.chunk({ streamId: stream.streamId, data: base64 }) } recorder.start(1000) // ═══ VIEWER ════════════════════════════════════════════ const viewer = await NorthstarClient.connect("wss://viewer-node.example") // Join via link (auto-subscribes via relay tree) const info = await viewer.stream.join( "northstar://node.example/stream/z6Mk.../live" ) // Receive signed chunks from relay tree viewer.stream.onChunk(info.joined, (chunk) => { const bytes = viewer.stream.decodeChunk(chunk) sourceBuffer.appendBuffer(bytes) // MSE playback })
Each of these solves a real problem. Northstar's bet is that programmable per-entity policy plus a coordination substrate generalizes further than any single application protocol.
| Protocol | Federation unit | Transport | Policy model | Native value |
|---|---|---|---|---|
| Northstarv0.4 · 2026 | per-DID (you) | WebSocket | space rules + gossip | mutual-credit · Taler |
| AT ProtocolBluesky | per-account (PDS) | HTTP / WebSocket | external labelers | — |
| ActivityPubMastodon, fediverse | per-instance | HTTP | instance admins | — |
| MatrixElement | per-homeserver | HTTP / WebSocket | room admins | — |
| Nostrrelays | per-keypair | WebSocket | relay-level | Lightning (out of band) |
| SolidInrupt | per-pod | HTTP / LDP | access control lists | — |
The protocol is implemented and running. Spaces, beams, gossip, livestreaming, and Warp persistence all work today. GNU Taler integration is the next milestone.
Read the spec. Browse the source. Run a node. The protocol is open, the implementation is real, the deadline is shipping.