Skip to content

d3chat Overview

Source of truth:

  • d3chat/backend/app/main.py
  • d3chat/backend/app/models/*.py
  • d3chat/backend/app/routers/*.py
  • d3chat/backend/app/websocket/*.py
  • d3chat/backend/app/federation/*.py
  • d3chat/frontend/src/store/*.ts
  • d3chat/frontend/src/crypto/*.ts

Purpose and Design Intent

d3chat is designed for operator-controlled, self-hosted messaging where:

  1. message confidentiality is enforced by client cryptography
  2. server-side systems still provide realtime reliability and moderation controls
  3. 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, sessions
  • channels, channel_members, messages
  • device_keys, sender_keys
  • server_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

  1. Client calls POST /auth/register
  2. Backend validates registration policy (registration_mode, domain allowlist)
  3. User + first device + refresh session are created
  4. Client stores tokens/device id, loads profile, boots key bundles (/keys/upload)
  5. Client opens websocket via one-time ticket

Local message write path

  1. Client encrypts payload (or plaintext fallback)
  2. Client posts message to /channels/{id}/messages
  3. Backend verifies membership and writes message row
  4. Backend publishes message.new to Redis topic
  5. WebSocket manager forwards to connected subscribers

Federated message write path

After local write:

  1. Backend determines remote member domains
  2. Constructs signed message.relay event
  3. Sends to each remote /federation/inbox
  4. Remote validates signature/replay/dedup
  5. Remote writes local message and emits local realtime event

Read path with pagination

  1. Client fetches /channels/{id}/messages?before=&limit=
  2. Backend returns newest-first query window, then normalized list + has_more
  3. Client decrypts sequentially where sender-key ratchets require order
  4. 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:

  1. runs migrations
  2. initializes federation server identity
  3. ensures default superadmin if none exists
  4. 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:

  1. strict local persistence of tokens + crypto state
  2. idempotent realtime merge with HTTP history
  3. single-threaded or ordered decrypt pipeline per sender-key stream
  4. refresh+reconnect state machine that survives background/foreground transitions
  5. role-aware UX branching for admin-only surfaces

Companion docs: