decentrl/docs
SDK

Getting Started

Install the Decentrl SDK and build your first encrypted application.

Prerequisites

  • Node.js 20 or later
  • TypeScript with "moduleResolution": "bundler" or "node16"
  • A package manager (npm, pnpm, or yarn)

Installation

npm install @decentrl/sdk zod

For React applications, also install the React bindings:

npm install @decentrl/sdk-react

TypeScript Configuration

Decentrl uses native ES modules. Your tsconfig.json must use ESM-compatible settings:

tsconfig.json
{
  "compilerOptions": {
    "module": "esnext",
    "moduleResolution": "bundler",
    "target": "ES2022"
  }
}

Quick Start

Define your app, create a client, publish an event:

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

interface Message {
  text: string
  threadId: string
  senderDid: string
  eventId: string
}

export const app = defineDecentrlApp({
  events: {
    message: {
      schema: z.object({
        text: z.string(),
        threadId: z.string().uuid(),
      }),
      tags: ['thread.${threadId}'],
    },
  },
  state: {
    messages: {
      initial: [] as Message[],
      reduce: {
        // `data` and `meta` are typed from the schema; who sent it comes from `meta`
        message: (messages, data, meta) => [
          ...messages,
          { ...data, senderDid: meta.senderDid, eventId: meta.eventId },
        ],
      },
    },
  },
})

const client = app.createClient({
  mediatorDid: 'did:web:mediator.decentrl.io',
  persist: { key: 'my-app' },
})

// Create identity on first launch
if (!client.identity.getIdentity()) {
  await client.identity.create({
    alias: 'alice',
    mediatorDid: 'did:web:mediator.decentrl.io',
  })
}

// Publish an encrypted event
await client.publish('message', {
  text: 'Hello, private world.',
  threadId: crypto.randomUUID(),
})

client.getState().messages   // Message[]

createClient() sets up the transport layer. identity.create() generates keys, creates a DID, and registers with the mediator. The client is then ready to publish events, manage contracts, and query encrypted state. With persist, the identity, contract keys and a snapshot of the state survive reloads.

Reducers need no type annotations: each slice's type comes from its initial ([] as Message[]), and data and meta are inferred from the event. Every event is validated against its schema and applied once, so reducers don't need to dedupe. See defineDecentrlApp.

With React

Register your app once, and every hook is typed from it — no generics, no casts:

decentrl.ts
import { app } from './app'

declare module '@decentrl/sdk-react' {
  interface Register {
    app: typeof app
  }
}
Messages.tsx
import { useDecentrlState, usePublish } from '@decentrl/sdk-react'

export function Messages({ threadId }: { threadId: string }) {
  const messages = useDecentrlState((state) => state.messages)   // Message[]
  const { publish, isPending } = usePublish()

  return (
    <>
      {messages.map((m) => <p key={m.eventId}>{m.text}</p>)}
      <button disabled={isPending} onClick={() => publish('message', { text: 'Hi', threadId })}>
        Send
      </button>
    </>
  )
}

Wrap the tree in <DecentrlProvider client={client}>. See React Hooks.

Public Beta. Decentrl is in active development. The protocol specification is at v0.1. APIs may change between minor versions. Pin your dependency to avoid surprises.