d3chat Overview
Source of truth:
d3chat/backend/app/main.pyd3chat/backend/app/models/*.pyd3chat/backend/app/routers/*.pyd3chat/backend/app/websocket/*.pyd3chat/backend/app/federation/*.pyd3chat/frontend/src/store/*.tsd3chat/frontend/src/crypto/*.ts
Purpose and Design Intent
d3chat is designed for operator-controlled, self-hosted messaging where:
- message confidentiality is enforced by client cryptography
- server-side systems still provide realtime reliability and moderation controls
- federation allows independent servers to interoperate using signed transport events
This architecture intentionally separates concerns:
- confidentiality: client-side crypto and key custody
- durability and consistency: backend APIs + PostgreSQL
- low-latency distribution: Redis pub/sub + WebSocket manager
- cross-domain trust: signed federation requests + replay/dedup guards
Trust Boundaries
Boundary 1: Client device
The client is trusted for:
- generating device identity and prekeys
- deriving DM/group message keys
- encrypting/decrypting message payloads
Compromise of a device compromises data accessible from that device’s local state.
Boundary 2: Local server
The server is trusted for:
- authentication and authorization
- persistence and query integrity
- event fanout and federation relay
- moderation and policy enforcement
The server is not trusted with plaintext confidentiality for encrypted payloads.
Boundary 3: Remote federated server
Remote servers are treated as semi-trusted peers authenticated by signature verification and domain discovery.
Boundary 4: Network transport
Security depends on TLS/reverse-proxy hygiene in deployment. Federation supports HTTP fallback in development contexts, but production assumes secure transport.
Layered System Architecture
Application layer (FastAPI)
Route groups:
- auth
- users
- devices
- channels
- messages
- keys
- admin
- avatars
- federation inbox and lookup
- websocket endpoint
Cross-cutting middleware and dependencies:
- bearer auth extraction + JWT decode
- role guards (
admin,superadmin) - request rate limiting
Persistence layer (PostgreSQL)
Primary entities:
users,devices,sessionschannels,channel_members,messagesdevice_keys,sender_keysserver_settings,audit_logs,servers
Indexing strategy includes channel-time index on messages for efficient paginated read windows.
Coordination layer (Redis)
Used for:
- pub/sub channel fanout (
channel:*,user:*) - websocket ticket storage/consumption
- presence hints
- low-prekey alerts
- federation replay/dedup/rate counters
- public configuration cache
Client layer (React + Zustand + Crypto modules)
Responsibilities:
- token/session restoration
- API retries via refresh workflow
- websocket reconnection backoff
- state normalization for channels/messages/members
- protocol-specific encryption/decryption orchestration
Data Flow: End-to-End Paths
User registration and bootstrap
- Client calls
POST /auth/register - Backend validates registration policy (
registration_mode, domain allowlist) - User + first device + refresh session are created
- Client stores tokens/device id, loads profile, boots key bundles (
/keys/upload) - Client opens websocket via one-time ticket
Local message write path
- Client encrypts payload (or plaintext fallback)
- Client posts message to
/channels/{id}/messages - Backend verifies membership and writes message row
- Backend publishes
message.newto Redis topic - WebSocket manager forwards to connected subscribers
Federated message write path
After local write:
- Backend determines remote member domains
- Constructs signed
message.relayevent - Sends to each remote
/federation/inbox - Remote validates signature/replay/dedup
- Remote writes local message and emits local realtime event
Read path with pagination
- Client fetches
/channels/{id}/messages?before=&limit= - Backend returns newest-first query window, then normalized list +
has_more - Client decrypts sequentially where sender-key ratchets require order
- Client merges with websocket stream and deduplicates by message id
Consistency Model
Durable source of truth
PostgreSQL state is authoritative for channels, memberships, and message history.
Realtime plane semantics
WebSocket delivery is near-realtime but best-effort within connection lifecycle. Clients must reconcile on reconnect by re-fetching authoritative API slices.
Federation semantics
Federation is eventually consistent across participating servers. Event deduplication avoids duplicate side effects by event_id keying.
Security Model Summary
Authentication
- access token: JWT with short expiry
- refresh token: opaque secret, hashed at rest
- websocket auth: short-lived one-time ticket
Authorization
- route-level dependency guards enforce role permissions
- channel/member ownership checks gate channel/message operations
- moderation routes enforce anti-self-harm invariants (self-ban/self-delete blocked)
Abuse controls
- global request rate limiting by IP
- federation origin-level rate limiting
- replay-window checks on federation signatures
Operational Model
Startup lifecycle
On process startup:
- runs migrations
- initializes federation server identity
- ensures default superadmin if none exists
- prepares avatar upload directories
Runtime mutability
Operational settings are database-backed and updateable at runtime via admin APIs, with cache invalidation for public config.
Observability hooks
- structured log points in message, websocket, and federation paths
- audit logs for admin actions
- explicit status/error responses for client telemetry mapping
Mobile-First Implications
For a native client built from docs only, required architectural behaviors are:
- strict local persistence of tokens + crypto state
- idempotent realtime merge with HTTP history
- single-threaded or ordered decrypt pipeline per sender-key stream
- refresh+reconnect state machine that survives background/foreground transitions
- role-aware UX branching for admin-only surfaces
Companion docs: