4.6 KiB
4.6 KiB
Reliquary Architecture & Protocol Reference
1. Project Overview
Reliquary is a self-hosted, high-performance chat platform operating on the Reliquary Protocol.
- Philosophy: Desktop-first, native performance, decentralized identity.
- Routing: No server-to-server federation. The Client handles all routing.
- Identity: Cryptographic keypairs (secp256k1). User ID:
user@home.server. - Push Notifications: Explicitly out of scope for MVP. Desktop relies on background WebSockets; Android relies on Foreground Services.
2. Current System State
The following features are implemented or have a fixed specification.
✅ Core Features
- Auth: JWT-based. Register/Login returns encrypted keypair.
- Join Flow: Cryptographic signature verification.
- REST API: Basic CRUD for Channels and Text-only Messages.
- Gateway: WebSocket server for event fanout (Specification defined in Section 4).
💾 Database Models (ObjectBox)
- User:
userId,publicKey,passwordHash(home only). - Channel:
id,name,createdAt. - Message:
id,channelId,authorUserId,content,createdAt.
3. Active Implementation Target: Component & File System
⚠️ THIS SECTION DESCRIBES NEW FEATURES TO BE CODED ⚠️
We are migrating from plain text to Reliquary Components. This allows rich media, embeds, and collapsible UIs.
3.1 UX Flow: Staged Uploads (Discord-Style)
To ensure instant UI feedback and save server bandwidth:
- Stage (Client): User picks files. Client shows local previews. User reorders/adds spoilers. No network request yet.
- Commit (Client): User hits "Send". Client adds a "Dummy Message" to the UI with a progress bar.
- Upload (Network): Client POSTs files to
/api/uploads. - Publish (Network): Client gets
FileIds, builds thecomponentsJson, and POSTs to/api/messages. - Resolve (Network): Server broadcasts the new message via Gateway. Client swaps "Dummy" for "Real".
3.2 New/Modified Database Models
@Entity()
class Message {
// ... existing fields ...
// Search Index (Server concatenates text from all components)
String content;
// Source of Truth (JSON List<Component>)
String componentsJson;
}
@Entity()
class File {
int id;
int uploaderId;
String path; // /uploads/uuid.jpg
String mimeType;
int width; // CRITICAL for layout
int height;
int sizeBytes;
bool isAttached; // False until linked to a Message
DateTime createdAt;
}
3.3 Component JSON Schema
All items in componentsJson must follow this structure.
Base Wrapper:
{
"type": "text | gallery | collapsible | embed",
"modifiers":, // "spoiler" applies blur
"fallback": "String for push notifs",
"data": { ... }
}
Gallery Component (Images/Video):
{
"type": "gallery",
"modifiers": [],
"fallback": "Image",
"data": {
"items":
}
}
4. Gateway Protocol Specification (WebSockets)
The server implements a WebSocket Gateway at /gateway.
Connection & Auth
- URL:
ws://host:port/gateway - Auth:
?token=<jwt>(Query param) orAuthorization: Bearer <jwt>(Header). - OpCode: Ready (Server -> Client on connect):
{ "op": "ready", "connectionId": "gw_1", "userId": "alice@home" }
Client -> Server Ops
Subscribe:
{ "op": "subscribe", "channelIds": [12, 13] }
Unsubscribe:
{ "op": "unsubscribe", "channelIds": [12] }
Heartbeat:
{ "op": "ping" }
Server -> Client Ops (Events)
Message Created:
{
"op": "event",
"event": "message.created",
"channelId": 12,
"message": {
"id": 101,
"content": "Legacy text or fallback",
"componentsJson": "[...]",
"authorId": "alice@home",
"createdAt": "..."
}
}
(Also supports message.updated and message.deleted)
5. API Reference: Pagination & Uploads
GET /api/messages
Used for initial load and history backfilling.
-
Params:
channelId(required)limit(default 50)before(Message ID) -> Load older messages.after(Message ID) -> Load newer messages (catch-up).
-
Response:
{ "messages": [], "hasMore": true, "nextBefore": 51, // Cursor for older "nextAfter": 100 // Cursor for newer }
POST /api/uploads (New)
- Format:
multipart/form-data - Response:
{ "id": 505, "url": "/uploads/505.png", "width": 1920, "height": 1080, "mimeType": "image/png" }