Protocol Specification v0.1

Your data. Your keys.
Your rules.

Programmable infrastructure for decentralized communication. Identity, encryption, consent-based routing, and encrypted storage — with zero-knowledge mediators.

Alicedid:decentrl:alice@m1.io
Message
 
idle
Alice's Mediator
Your copy (stored)
 
listening
dual delivery
Bob's Mediator
Payload (relay)
 
listening
Bobdid:decentrl:bob@m2.io
Message
 
idle

Dual delivery — one copy stored on yours, one relayed to theirs. Neither mediator can read it.

MIT License

The Decentrl Stack

Four building blocks that give you ownership of your identity, data, and communications. Your mediator hosts encrypted data it can never read.

Decentralized Identity

Every user gets a DID with cryptographic key pairs for signing and encryption. No central authority needed.

Your DID Document
DIDdid:decentrl:alias@social.decentrl.network
Mediatormediator.decentrl.networkConnected
Signing Key
Ed25519 · z6Mkf5rG...xR8kPq
Encryption Key
X25519 · z6LSbg4...mN7wQp
What is a DID?

A Decentralized Identifier is a globally unique ID that you control. Unlike usernames on centralized platforms, your DID is cryptographically yours.

Key Concepts

Ed25519 for digital signatures (proving identity). X25519 for key agreement (establishing shared secrets). Both generated client-side. Private keys never leave your device.

Encrypted Messaging

Messages are encrypted end-to-end using X25519 key agreement and AES-256-GCM. The mediator relays ciphertext it can never read.

Message Flow
Communication Contract
Both parties sign a bilateral contract establishing the channel
Key Agreement
X25519 ECDH derives a shared secret from both parties' keys
3
Encrypt & Send
Message encrypted with AES-256-GCM, sent via mediator
4
Mediator Relay
Mediator receives opaque ciphertext and forwards it
5
Decrypt
Recipient derives the same shared secret and decrypts locally
Sender
Mediator
Recipient
Communication Contracts

Before messaging, both parties sign a bilateral contract. This establishes mutual consent and exchanges encryption keys. Either party can revoke at any time.

Zero-Knowledge Relay

The mediator only sees encrypted blobs. It cannot read message content or tamper with messages. Ed25519 signatures ensure authenticity.

Encrypted Event Store

All data is stored as encrypted events. Reducers derive current state from event history. You own your data.

Event Stream → Reducer → State
profile.name.set
{ name: "Luka Furlan" }
profile.bio.set
{ bio: "Building the decentralized web" }
profile.status.set
{ status: "Working on SDK v0.5" }
profile.name.set
{ name: "Luka F." }
Reducer Output (Current State) ↓
{ "name": "Luka F.", "bio": "Building the decentralized web", "status": "Working on SDK v0.5" }
Event Sourcing

Every change is an immutable event. Current state is derived by reducing all events. Full audit trail and ability to reconstruct state at any point.

Encrypted at Rest

Every event is encrypted before being stored. The mediator stores opaque blobs. Only you can decrypt and read your own events.

Decentralized Profile

Your profile is built from events you publish. Display name, bio, posts — all self-sovereign data.

Live Profile (from Event Store)
LF
Luka F.
did:decentrl:luka@social.decentrl.network
Building the decentralized web. Creator of Decentrl SDK.
Latest post
Why Communication Contracts Matter
4 min read
⚡ Built from encrypted events via reducer
Self-Sovereign Data

Every piece of profile data is an event you published. Change your name? Event. Update bio? Event. You can take your entire profile to a different mediator.

Portability

Your profile is just events encrypted with your keys. Export and re-publish them elsewhere. No vendor lock-in, no data held hostage.

Schema in, encrypted app out

Define events and reducers. Get identity, encryption, contracts, and sync for free.

app.ts
12345678910111213141516171819202122232425
import { defineDecentrlApp } from '@decentrl/sdk'
import { z } from 'zod'

const app = defineDecentrlApp({
  events: {
    'chat.message': {
      schema: z.object({
        id: z.string().uuid(),
        chatId: z.string(),
        text: z.string(),
      }),
      tags: ['chat.${chatId}'],
    },
  },
  state: {
    messages: {
      initial: [],
      reduce: {
        'chat.message': (state, event) =>
          state.some(msg => msg.id === event.id)
            ? state : [...state, event],
      },
    },
  },
})
schema validates at publish time, infers TypeScript types
reduce runs on decrypted events, derives state automatically
chat.tsx
12345678910111213141516171819202122232425
import { useDecentrl, useDecentrlState }
  from '@decentrl/sdk-react'

function Chat({ chatId }) {
  const { publish } = useDecentrl()
  const messages = useDecentrlState(
    state => state.messages.filter(
      msg => msg.chatId === chatId
    )
  )

  return (
    <div>
      {messages.map(msg => (
        <Message key={msg.id} data={msg} />
      )))}
      <Composer onSend={text =>
        publish('chat.message', {
          id: crypto.randomUUID(),
          chatId, text,
        })
      } />
    </div>
  )
}
useDecentrlState re-renders on state changes via useSyncExternalStore
publish() handles encrypt, sign, and route under the hood

One install.
Zero compromises.

$npm install @decentrl/sdk