A decentralized trust infrastructure for real-life communities. People meet in person, verify each other's identity via QR code, and build reputation through attestations over time.
No central server sees your data. Everything is end-to-end encrypted and stored locally.
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ VERIFY │ ──► │ COLLABORATE │ ──► │ ATTEST │
│ │ │ │ │ │
│ Confirm identity│ │ Share encrypted │ │ Build reputation│
│ by meeting in │ │ content (tasks, │ │ through real │
│ person (QR scan)│ │ calendar, maps) │ │ actions │
└─────────────────┘ └─────────────────┘ └─────────────────┘
Verification ≠ Trust. Verification only confirms: "This is really that person." Actual trust is built through attestations over time.
- Demo App: web-of-trust.de/demo
- CRDT Benchmark: web-of-trust.de/benchmark — measure Yjs vs Automerge on your device
- Relay:
wss://relay.utopia-lab.org - Profiles:
https://profiles.utopia-lab.org
The system is built on swappable adapters — same interfaces, different implementations. This allows experimenting with different CRDT frameworks, messaging protocols, and storage backends without touching application code.
┌───────────────────┐
│ Your App / Demo │
└─────────┬─────────┘
│
┌──────────────────────────┴──────────────────────────┐
│ wot-core │
│ ┌─────────┐ ┌──────────┐ ┌────────┐ ┌───────────┐ │
│ │ Storage │ │ Reactive │ │ Crypto │ │ Discovery │ │
│ └─────────┘ └──────────┘ └────────┘ └───────────┘ │
│ ┌───────────┐ ┌─────────────┐ ┌───────────────┐ │
│ │ Messaging │ │ Replication │ │ Authorization │ │
│ └───────────┘ └─────────────┘ └───────────────┘ │
└──────────────────────────┬──────────────────────────┘
│
┌────────────────┴────────────────┐
│ adapter-yjs / adapter-automerge │
└────────────────┬────────────────┘
│
┌────────────────┼────────────────┐
│ │ │
┌─────┴─────┐ ┌──────┴─────┐ ┌───────┴───────┐
│ wot-relay │ │ wot-vault │ │ wot-profiles │
└───────────┘ └────────────┘ └───────────────┘
| Adapter | Purpose | Implementation |
|---|---|---|
| StorageAdapter | Local persistence, CRUD | Yjs (default) or Automerge |
| ReactiveStorageAdapter | Live queries, subscriptions | Observables on CRDT changes |
| CryptoAdapter | Signing, encryption | WebCrypto (Ed25519, X25519, AES-256-GCM) |
| DiscoveryAdapter | Public profile lookup | HTTP + offline cache |
| MessagingAdapter | 1:1 message delivery | WebSocket Relay (ACK + Outbox) |
| ReplicationAdapter | Encrypted CRDT Spaces | Yjs or Automerge + E2EE + GroupKeys |
| AuthorizationAdapter | Capabilities / permissions | UCAN-inspired, offline-verifiable |
Three CRDT-agnostic services — they only see encrypted bytes, never plaintext:
| Service | Transport | Purpose |
|---|---|---|
| wot-relay | WebSocket | Real-time sync + delivery ACK |
| wot-vault | HTTP | Encrypted backup for new device restore |
| wot-profiles | HTTP | Public profile discovery (JWS-signed) |
Data is also persisted locally in IndexedDB (CompactStore) for offline access.
| Package | CRDT | Runtime | Notes |
|---|---|---|---|
| adapter-yjs | Yjs | Pure JavaScript (69KB) | Default. Fast on all devices. |
| adapter-automerge | Automerge | Rust → WASM (1.7MB) | Alternative. Heavier on mobile. |
Switch at startup with VITE_CRDT=automerge. Both pass the same 11 end-to-end tests. Try the in-browser benchmark to compare on your device.
- BIP39 Mnemonic — 12-word recovery phrase (German wordlist)
- Ed25519 — Signing (via @noble/ed25519)
- X25519 — Key agreement (ECDH)
- did:key — W3C Decentralized Identifier
- HKDF Master Key — Non-extractable CryptoKey, hardware isolation when available
- Encrypted seed storage — PBKDF2 (600k iterations) + AES-GCM in IndexedDB
All data is encrypted before it leaves the device. The relay server only sees ciphertext.
- Symmetric: AES-256-GCM (CRDT updates, group content)
- Asymmetric: X25519 ECIES (key exchange, 1:1 messages)
- Envelope Auth: Ed25519-signed message envelopes
- Group Keys: Per-space key with generation-based rotation
- Group Spaces — CRDT-based collaboration (ReplicationAdapter)
- Selective Sharing — Item-level encryption keys
- 1:1 Delivery — Attestations, verifications via Relay
- Node.js 22+
- pnpm 9+
# Install dependencies
pnpm install
# Start demo app (default: Yjs)
pnpm dev:demo
# Start demo app with Automerge
VITE_CRDT=automerge pnpm dev:demo
# Start landing page
pnpm dev:landing
# Run tests
pnpm test # all packages
pnpm test:e2e # Playwright E2E tests
# Build
pnpm build:coreweb-of-trust/
├── packages/
│ ├── wot-core/ # @web_of_trust/core — Core library
│ ├── adapter-yjs/ # @web_of_trust/adapter-yjs — Yjs CRDT adapter (default)
│ ├── adapter-automerge/ # @web_of_trust/adapter-automerge — Automerge CRDT adapter
│ ├── wot-relay/ # WebSocket Relay Server (Node.js, SQLite)
│ ├── wot-vault/ # Encrypted Document Store (HTTP, SQLite)
│ └── wot-profiles/ # Public Profile Service (HTTP, SQLite, JWS)
├── apps/
│ ├── demo/ # Demo App (React 19, i18n, Dark Mode)
│ ├── benchmark/ # CRDT Benchmark (Yjs vs Automerge)
│ └── landing/ # Landing Page
└── docs/ # Architecture docs & specifications
| Package | Description | Links |
|---|---|---|
@web_of_trust/core |
Core library — identity, crypto, adapters, services | npm |
@web_of_trust/adapter-yjs |
Yjs CRDT adapter (default) — pure JS, 76x faster on mobile | |
@web_of_trust/adapter-automerge |
Automerge CRDT adapter — Rust→WASM | |
wot-relay |
WebSocket Relay Server — message forwarding, delivery ACK, SQLite | |
wot-vault |
Encrypted Document Store — append-only, capability auth, SQLite | |
wot-profiles |
Public Profile Service — JWS verification, REST API, SQLite |
// Core — identity, crypto, messaging
import {
WotIdentity,
WebCryptoAdapter,
HttpDiscoveryAdapter,
WebSocketMessagingAdapter,
OutboxMessagingAdapter,
ProfileService,
EncryptedSyncService,
GroupKeyService,
} from '@web_of_trust/core'
// CRDT adapter — choose one
import { YjsReplicationAdapter } from '@web_of_trust/adapter-yjs'
// or: import { AutomergeReplicationAdapter } from '@web_of_trust/adapter-automerge'
// Create identity from 12 magic words
const identity = new WotIdentity()
await identity.create('my-passphrase', true)
console.log(identity.getDid()) // did:key:z6Mk...| Package | Tests | Framework |
|---|---|---|
| wot-core | 392 | Vitest 4.1.0 |
| wot-relay | 24 | Vitest 4.1.0 |
| wot-vault | 27 | Vitest 4.1.0 |
| wot-profiles | 25 | Vitest 4.1.0 |
| Demo (Unit) | 59 | Vitest 4.1.0 |
| Demo (E2E) | 7 | Playwright |
| Total | 534 |
All 11 E2E tests pass with both CRDT adapters (Yjs and Automerge).
- Onboarding — Create identity with 12 Magic Words + passphrase
- Recovery — Restore identity from seed on any device
- QR Verification — In-person identity verification via camera
- Contacts — Manage verified contacts
- Attestations — Attest skills/properties, receive, publish
- Spaces — Encrypted group collaboration (CRDT)
- Profile Sync — JWS-signed profiles published to wot-profiles
- Public Profile — Viewable without login
- Multi-Device — Sync via Relay + Vault
- Offline-First — Local data, offline banner, outbox queue
- i18n — German + English
- Dark Mode — Fully supported
- Debug Panel — Persistence metrics, relay status, CRDT info
Note: Most specification documents are in German. The implementation status is documented in English in CURRENT_IMPLEMENTATION.md.
| Document | Description |
|---|---|
| Current Implementation | What's built, what works, architecture decisions |
| NLNet Application | Funding application (NGI Zero Commons Fund) |
| Adapter Architecture v2 | 7-adapter specification |
| Framework Evaluation | 16 frameworks evaluated |
| DID Methods Comparison | 6 DID methods evaluated (did:key confirmed) |
| Vault Sync Architecture | Three sync patterns |
| Social Recovery | Shamir Secret Sharing concept |
| Threat Model | STRIDE analysis |
| Encryption Protocol | E2E encryption design |
- Real Life Stack — Modular app toolkit for local communities, built on Web of Trust
We're looking for:
- Communities who want to try it
- Feedback on UX and concept
- Developers who want to build with us