Files
chat-server/ARCHITECTURE.md
T
2026-02-20 16:37:30 +03:00

173 lines
4.6 KiB
Markdown

# 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:
1. **Stage (Client):** User picks files. Client shows local previews. User reorders/adds spoilers. *No network request yet.*
2. **Commit (Client):** User hits "Send". Client adds a "Dummy Message" to the UI with a progress bar.
3. **Upload (Network):** Client POSTs files to `/api/uploads`.
4. **Publish (Network):** Client gets `FileId`s, builds the `componentsJson`, and POSTs to `/api/messages`.
5. **Resolve (Network):** Server broadcasts the new message via Gateway. Client swaps "Dummy" for "Real".
### 3.2 New/Modified Database Models
```dart
@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:**
```json
{
"type": "text | gallery | collapsible | embed",
"modifiers":, // "spoiler" applies blur
"fallback": "String for push notifs",
"data": { ... }
}
```
**Gallery Component (Images/Video):**
```json
{
"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) or `Authorization: Bearer <jwt>` (Header).
* **OpCode: Ready** (Server -> Client on connect):
```json
{ "op": "ready", "connectionId": "gw_1", "userId": "alice@home" }
```
### Client -> Server Ops
**Subscribe:**
```json
{ "op": "subscribe", "channelIds": [12, 13] }
```
**Unsubscribe:**
```json
{ "op": "unsubscribe", "channelIds": [12] }
```
**Heartbeat:**
```json
{ "op": "ping" }
```
### Server -> Client Ops (Events)
**Message Created:**
```json
{
"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:**
```json
{
"messages": [],
"hasMore": true,
"nextBefore": 51, // Cursor for older
"nextAfter": 100 // Cursor for newer
}
```
### POST /api/uploads (New)
* **Format:** `multipart/form-data`
* **Response:**
```json
{
"id": 505,
"url": "/uploads/505.png",
"width": 1920,
"height": 1080,
"mimeType": "image/png"
}
```