01Protocol Overview

Loqa federation enables independently operated instances to exchange messages, membership changes, reactions, and typing indicators in real time โ€” without a central coordinator. The protocol is designed around four core principles.

Decentralized

No single server is authoritative. Each instance manages its own users, guilds, and keys. Peers discover and authenticate each other directly โ€” there is no federation "hub" or registry.

E2EE-Preserving

The federation layer transmits opaque encrypted payloads. MLS group secrets and Double Ratchet sessions stay on client devices. The receiving server never sees plaintext.

Cryptographically Authenticated

Every request is signed with Ed25519. Receiving servers verify the signature against the sender's public key before processing. Replay attacks are blocked by a ยฑ5 minute timestamp window.

Reliable Delivery

Events are persisted in a transactional outbox and delivered with exponential backoff retry (up to 10 attempts). The inbox deduplicates events so processing is idempotent.

Federation Architecture
Instance A
loqa.chat
API Layer
Hooks
Outbox
Signed Events โ†’
โ† Signed Events
/_federation/v1/events
TLS 1.3 + Ed25519
Instance B
community.org
Inbox Dedup
Processor
Database

02Server Identity & Discovery

Every federation-enabled Loqa instance publishes a discovery document at a well-known URL. This document is the root of trust โ€” it tells other servers how to reach us, which public key to verify our signatures against, and what capabilities we support.

Discovery Document

GET https://loqa.chat/.well-known/loqa/federation
{
  "server_name": "loqa.chat",
  "federation_url": "https://loqa.chat/_federation",
  "public_key": "ed25519:aB3dEf7G:dGhlIHB1YmxpYyBrZXkgYmFzZTY0...",
  "version": "1.0",
  "capabilities": [
    "messages",
    "members",
    "reactions",
    "typing",
    "mls_e2ee",
    "guild_transfer"
  ]
}
          

Ed25519 Keypair

Each server generates an Ed25519 signing keypair on first boot. The private key is encrypted with AES-256-GCM using the server's master encryption key before storage. The public key and a key_id fingerprint (first 6 bytes, base64) are published in the discovery document.

Key Storage

Private keys are stored in the federation_keys table as hex(nonce):base64(ciphertext). The 12-byte AES-GCM nonce is unique per key. Keys can be rotated โ€” old keys are marked expired but retained for signature verification during the transition window.

Capability Negotiation

The capabilities array declares which federation features this instance supports. Peers check capabilities before sending events โ€” e.g., a server without guild_transfer will never receive transfer offers.

Server Name Validation

When fetching a remote discovery document, the server_name field is validated against the requested domain. A mismatch generates a warning โ€” preventing DNS hijacking from silently redirecting federation traffic.

03HTTP Signature Authentication

Every federation request carries three custom HTTP headers that cryptographically bind the request to the sending server. The receiving server verifies the signature before processing any event.

Signature Construction
1
Build signature payload
METHOD\nPATH\nTIMESTAMP\nSHA256(body)
โ†’
2
Sign with Ed25519
Server's private signing key
โ†’
3
Attach headers
Origin + Timestamp + Signature

Request Headers

Header Format Purpose
X-Loqa-Origin loqa.chat Identifies the sending server (used to look up its public key)
X-Loqa-Timestamp Unix epoch seconds Binds signature to a point in time โ€” prevents replay outside ยฑ5 min window
X-Loqa-Signature ed25519:<fingerprint>:<base64> Ed25519 signature over the constructed payload

Verification Flow

Check timestamp freshness
|now โˆ’ timestamp| โ‰ค 300 seconds
โ†“
Look up peer's public key
FROM federation_peers WHERE status = 'active'
โ†“
Rebuild payload + verify Ed25519 sig
METHOD + PATH + TIMESTAMP + SHA256(body)
โ†“
Accept or reject request
401 Unauthorized on any failure

Replay Protection

The ยฑ5 minute timestamp window ensures that intercepted requests cannot be replayed later. The body hash (SHA-256) is included in the signed payload, so modifying the request body invalidates the signature.

Key Rotation

The key_id in the signature header identifies which public key to verify against. When a server rotates its signing key, it publishes the new key in its discovery document. Old keys remain valid for signature verification until explicitly expired.

04Event System

The event system is the core of Loqa federation. Local actions (sending a message, adding a reaction, joining a guild) trigger hooks that fan out signed event envelopes to all peer servers via a persistent outbox.

Event Types

Event Type Trigger Delivery Data Fields
MESSAGE_CREATE User sends a message Outbox id, channel, author, content, encrypted_content, nonce, attachments, embeds
MESSAGE_UPDATE User edits a message Outbox id, channel, updates
MESSAGE_DELETE User deletes a message Outbox id, channel
REACTION_ADD User reacts to a message Outbox message_id, channel_id, emoji, user_id
REACTION_REMOVE User removes a reaction Outbox message_id, channel_id, emoji, user_id
MEMBER_JOIN User joins a guild Outbox guild_id, user_id
MEMBER_LEAVE User leaves a guild Outbox guild_id, user_id
MEMBER_UPDATE Profile or role change Outbox guild_id, user_id, updates
TYPING_START User starts typing Direct (ephemeral) channel_id, user_id
GUILD_TRANSFER Guild migrated to new server Outbox guild_id, new_authority, origin
CHANNEL_CREATE Channel added to guild Outbox guild_id, channel (id, name, type, position, topic)
CHANNEL_UPDATE Channel metadata changed Outbox guild_id, channel_id, updates
CHANNEL_DELETE Channel removed Outbox guild_id, channel_id
ROLE_CREATE Role added to guild Outbox guild_id, role (id, name, color, permissions)
ROLE_UPDATE Role properties changed Outbox guild_id, role_id, updates
ROLE_DELETE Role removed Outbox guild_id, role_id
GUILD_UPDATE Guild metadata changed Outbox guild_id, updates (name, description, icon)

Event Envelope

Event envelope structure
{
  "event_id": "msg_create_abc123",
  "origin": "loqa.chat",
  "timestamp": 1739836800,
  "guild_id": "guild_456",
  "event_type": "MESSAGE_CREATE",
  "data": { ... },
  "signature": "ed25519:aB3dEf7G:base64_signature..."
}
          

Outbox & Inbox Architecture

API Route
Local DB write + Centrifugo push
โ†’
Hook
Fire-and-forget, never blocks API
โ†’
Outbox
Persistent queue in PostgreSQL
โ†’
Drain Task
Every 5s, batch of 50
HTTPS + Ed25519
Receive Events
Verify signature + peer status
โ†’
Inbox Dedup
ON CONFLICT (event_id) DO NOTHING
โ†’
Processor
Dispatch by event type
โ†’
Local DB
Federated message/member stored

Exponential Backoff

Failed deliveries are retried with exponential backoff: 2n seconds per attempt, capped at ~4 minutes (28 = 256s). After 10 failed attempts, the event is abandoned. Events for inactive peers are immediately dropped.

Idempotent Processing

Every event carries a unique event_id. The inbox table uses ON CONFLICT (event_id) DO NOTHING โ€” if the same event arrives twice (e.g., due to retry), the duplicate is silently discarded.

Fan-Out

Each guild can be federated with multiple peers. The hook layer queries federation_guild_peers and enqueues one outbox entry per peer. Direction control (inbound, outbound, both) limits which peers receive which events.

Ephemeral Events

TYPING_START is the only ephemeral event โ€” it bypasses the outbox entirely and is sent directly via HTTP. If delivery fails, it's silently dropped. Typing indicators are not persisted on either side.

05Peer Management

Federation peers are established through an invite โ†’ accept handshake. Each peer relationship is scoped to specific guilds with configurable direction control.

Peer Establishment Flow
1
Admin initiates federation
POST /api/admin/federation/peers
โ†“
2
Fetch target's discovery document
GET https://target/.well-known/loqa/federation
โ†“
3
Send signed invite
POST /_federation/v1/peers/invite
{ origin_server, guild_id, public_key, direction }
โ†“
4
Target persists as "pending"
Admin approval required on target
โ†“
5
Target accepts โ†’ exchanges public key
POST /_federation/v1/peers/accept
Returns server_name, public_key, federation_url
โ†“
โœ“
Peer Active โ€” events flow bidirectionally

Per-Guild Scoping

Peer relationships are linked to specific guilds via federation_guild_peers. A single peer connection can federate multiple guilds, each with independent direction settings.

Direction Control

Each guild-peer link has a direction: inbound (receive only), outbound (send only), or both. This allows read-only mirrors, one-way broadcasting, or full bidirectional federation.

Health Checks

GET /_federation/v1/peers/status returns the server's health, protocol version, and supported capabilities โ€” enabling peers to verify connectivity and feature compatibility.

Public Key Exchange

On accept, both servers exchange their Ed25519 public keys. These keys are stored in federation_peers and used for all subsequent signature verification. Key rotation triggers a discovery document refresh.

06Guild Transfer Protocol

Loqa supports seamless guild migration between instances โ€” moving an entire community (channels, roles, members, emoji, bans) from one server to another without data loss.

Guild Transfer Sequence
1 Admin Initiates
Server admin triggers transfer
POST /api/servers/:id/federation/transfer
2 Offer Sent
Origin sends transfer offer to target
POST /_federation/v1/guilds/:id/transfer/offer
Includes guild preview (name, description, member count)
3 Snapshot Streamed
Origin builds full state snapshot and streams it
POST /_federation/v1/guilds/:id/transfer/snapshot
Contains guild, channels, roles, members, emoji, bans
4 Target Imports
Target imports all data and becomes the new authority
Guild is created with federation_enabled = true
5 Tombstone & Notify
Origin sets tombstone redirect and broadcasts
GUILD_TRANSFER event to all peers
Clients are redirected to the new authority

Snapshot Contents

Object Fields Conflict Strategy
Guild name, owner_id, description, icon ON CONFLICT (id) DO UPDATE
Channels id, name, type, position, topic ON CONFLICT (id) DO NOTHING
Roles id, name, rank, color, permissions ON CONFLICT (id) DO NOTHING
Members user_id, username, display_name, avatar, nickname, roles Via federated_users table
Emoji guild_id, name, animated, url ON CONFLICT DO NOTHING
Bans server_id, user_id, reason ON CONFLICT DO NOTHING

Tombstone Redirect

After a successful transfer, the origin server stores a federation_transfers record with a redirect_until timestamp. Any API requests for the transferred guild return a redirect to the new authority, giving clients time to update.

Peer Notification

A GUILD_TRANSFER event is broadcast to all federation peers via the outbox. Peers update their federation_guild_peers records to point to the new authority โ€” future events for this guild route to the new server.

07E2EE Compatibility

The federation protocol is designed from the ground up to preserve end-to-end encryption. Neither the sending nor receiving server can read encrypted message content โ€” they simply relay opaque ciphertext.

Encrypted Message Federation
Client A
Instance A
MLS encrypt or
Double Ratchet encrypt
Federated Payload
encrypted_content
nonce
sender_key_id
content: null
Servers see only opaque bytes โ€” no plaintext
Client B
Instance B
MLS decrypt or
Double Ratchet decrypt

MLS Groups Span Instances

MLS group state lives entirely on client devices via OpenMLS WASM. When a message is sent to a federated guild, the encrypted_content and nonce fields are carried verbatim through the federation layer โ€” the receiving instance stores them as-is for its local clients to decrypt.

Federated User IDs

Users from remote instances are identified as user_id@origin_server (e.g., [email protected]). This namespacing prevents ID collisions and makes it clear which instance is authoritative for each user's identity.

Media Proxy

When media_proxy is enabled (default: true), remote media URLs are proxied through the local server. This prevents IP leakage โ€” clients never make direct requests to remote instances, preserving user privacy.

Origin Tracking

Every federated message is tagged with federation_origin in the database. Updates and deletes are scoped to matching origin servers โ€” a remote server can only modify or delete its own messages, never local ones.

08Connecting Your Instance

This section walks through the practical steps for self-hosting operators who want to federate their Loqa instance with the main network or another operator.

Prerequisites

1. Running Loqa Instance

You need a running Loqa backend (Rust) with PostgreSQL. The instance must be reachable via HTTPS on a domain you control โ€” federation endpoints are served under /_federation/v1/*.

2. TLS Certificate

A valid TLS certificate for your domain. Self-signed certs are rejected โ€” use Let's Encrypt or similar. Federation traffic requires TLS 1.2+ (1.3 recommended).

Step-by-Step Setup

Federation Setup Checklist
1
Enable federation in your config
Set federation.enabled = true and federation.server_name to your domain
โ†“
2
Restart the backend โ€” keys auto-generate
Ed25519 keypair created on first boot, encrypted and stored in DB
โ†“
3
Verify your discovery document
GET https://yourdomain/.well-known/loqa/federation should return JSON with your public key
โ†“
4
Send a peer invite to the target instance
POST /api/admin/federation/peers with the target domain โ€” your server will fetch their discovery doc and send a signed invite
โ†“
5
Target admin accepts the invite
Peer status changes from 'pending' โ†’ 'active' and public keys are exchanged
โ†“
6
Link guilds to the peer
POST /api/admin/federation/guilds to associate specific guilds with the peer, setting direction (inbound/outbound/both)
โ†“
โœ“
Federation active โ€” events flow between linked guilds

Configuration Reference

Setting Default Description
federation.enabled false Master switch โ€” enables the /_federation router and discovery endpoint
federation.server_name โ€” Your domain (e.g., community.org). Must match your TLS certificate.
federation.media_proxy true Proxy remote media through your server (prevents IP leakage)
federation.outbox_drain_interval_secs 5 How often the outbox drain task runs (seconds)
federation.max_retry_attempts 10 Max delivery retries before an event is abandoned

Reverse Proxy

If you run behind Caddy, Nginx, or similar, ensure /_federation/* and /.well-known/loqa/* are forwarded to the Loqa backend. The discovery document must be publicly accessible without authentication.

Firewall Rules

Open port 443 for inbound HTTPS. Federation uses standard HTTPS โ€” no custom ports or protocols. Outbound requests go to port 443 of peer domains.

Health Verification

After peering, hit GET /_federation/v1/peers/status on both sides to confirm connectivity. You should see the peer listed as active with matching protocol versions.

Troubleshooting

If events aren't flowing, check: (1) peer status is active, (2) guild is linked with correct direction, (3) federation_enabled = true on the guild's server, (4) outbox drain task is running (check logs for Federation event queued).

09Reproducible Audit Scope

Every protocol claim in this whitepaper can be independently verified by reading 14 self-contained Rust source files. The federation crate has zero business logic dependencies โ€” it relies only on standard cryptographic libraries (ed25519-dalek, aes-gcm, sha2) and PostgreSQL.

File Purpose Lines
lib.rs Crate root โ€” FederationState initialization, module exports 62
auth.rs HTTP signature construction & verification (Ed25519 + SHA-256) 84
signing.rs Ed25519 key generation, AES-256-GCM encrypted storage, signing & verifying 127
config.rs Federation configuration (server_name, media_proxy, retry limits) 44
discovery.rs Remote .well-known document fetching & validation 56
transport.rs Outbound signed HTTP POST transport 41
outbox.rs Persistent event queue with exponential backoff retry 133
hooks.rs Fire-and-forget hooks โ€” fan-out events to peers after local writes 313
processor.rs Inbound event processor โ€” dedup, dispatch, and per-type handlers 488
routes.rs Axum router โ€” /_federation/v1/* endpoints 157
handlers/peers.rs Peer invite, accept, and health check handlers 132
handlers/guild.rs Guild state snapshot export with peer authentication 240
handlers/transfer.rs Guild transfer โ€” offer, snapshot import, tombstone, peer notification 280
handlers/mod.rs Handler module re-exports 4
Total auditable surface 2,161
๐Ÿ”ฌ

For security researchers: These 14 files contain 100% of the federation protocol logic. No federation decisions are made outside this boundary. Every signature, every event dispatch, every peer authentication check is in these files.

๐Ÿ” Read the Encryption Whitepaper โ†’

Questions About Federation?

We welcome review from security researchers, self-hosting operators, and anyone interested in decentralized communication. We're happy to provide additional technical detail or discuss deployment.