163 lines
5.4 KiB
Markdown
163 lines
5.4 KiB
Markdown
# Reliquary Server Architecture
|
|
|
|
## 1. Overview
|
|
Reliquary is a self-hosted chat server with:
|
|
- JWT auth
|
|
- ObjectBox persistence
|
|
- WebSocket gateway fanout
|
|
- Component-based messages
|
|
- Decoupled upload pipeline (upload first, message commit anytime)
|
|
|
|
## 2. Identity and Roles (Current)
|
|
- `userId` is an immutable opaque identifier (host-independent).
|
|
- `handle` is derived metadata: `@username@home`.
|
|
- `ChatUser.roles` controls authorization (`admin`, `member`, etc.).
|
|
- `GET /api/me` returns `roles` so the client can gate admin UI.
|
|
|
|
### Runtime Config Split
|
|
- `server.bind_host` + `server.bind_port`: where Relic listens locally.
|
|
- `server.public_base_url`: canonical external URL used for identity/handles/cross-server metadata.
|
|
- This avoids conflating local bind settings with public identity/routing.
|
|
|
|
### First Admin Bootstrap
|
|
- Config key: `server.first_setup` in `config.yaml`.
|
|
- When `first_setup: true`, the first registered local user gets `admin`.
|
|
- Later registrations remain `member`.
|
|
|
|
## 3. Storage Model
|
|
|
|
### 3.1 Entities
|
|
- `ChatUser`: identity/profile/auth data.
|
|
- `ChatChannel`: channel metadata.
|
|
- `ChatMessage`: channel message, search text, and `componentsJson`.
|
|
- `ChatFile`: canonical media object for:
|
|
- message attachments
|
|
- user avatar media
|
|
- server icon/banner media
|
|
- `ServerSettings`: singleton row for mutable server presentation settings.
|
|
|
|
### 3.2 ChatFile Fields (Important)
|
|
- `attachmentId` (unique external ID; API-facing)
|
|
- `originalFileName` (preserved filename metadata)
|
|
- `storedFileName` (physical disk key)
|
|
- `sha256`
|
|
- `mimeType`, `sizeBytes`, `width`, `height`
|
|
- `usageType`: `message | avatar | server_icon | server_banner`
|
|
- `status`: `pending | ready | failed`
|
|
|
|
## 4. Upload and Message Flow
|
|
|
|
### 4.1 Recommended Client Flow (Implemented Server Contract)
|
|
1. Client stages attachment in composer/component editor.
|
|
2. Client calls `POST /api/uploads/init` to create pending upload.
|
|
3. Client uploads bytes to `PUT /api/uploads/:uploadId`.
|
|
4. Client sends `POST /api/messages` with components referencing `uploadId`/`attachmentId`.
|
|
5. If upload completes after message create, server emits `message.updated`.
|
|
|
|
### 4.2 Why This Flow
|
|
- No chronological jumping for text.
|
|
- Immediate local optimistic UI.
|
|
- Progressive media readiness via gateway updates.
|
|
- Server keeps authoritative metadata and storage consistency.
|
|
|
|
## 5. API Surface
|
|
|
|
### 5.0 Identity Contract
|
|
- Registration returns:
|
|
- `userId` (opaque stable ID)
|
|
- `username`
|
|
- `handle` (computed, mutable as home host changes)
|
|
- Cross-server join request now expects:
|
|
- `userId`
|
|
- `username`
|
|
- `homeServer`
|
|
- `timestamp`
|
|
- `signature` over `userId:timestamp`
|
|
|
|
### 5.1 Uploads and Attachments
|
|
- `POST /api/uploads/init` (auth)
|
|
- `PUT /api/uploads/:uploadId` (auth)
|
|
- `POST /api/uploads` (auth, legacy single-step compatibility)
|
|
- `GET /attachments/:attachmentId` (public immutable media URL)
|
|
- `GET /api/channels/:channelId/media` (auth, gallery index feed)
|
|
|
|
### 5.2 Profile and Server Media
|
|
- `PATCH /api/me` (auth): update profile text fields.
|
|
- `PUT /api/me/avatar` (auth): bind uploaded media as avatar.
|
|
- `GET /avatars/:userId` (public alias for current avatar).
|
|
- `GET /icons/:serverId` (public alias for current server icon).
|
|
- `GET /banners/:serverId` (public alias for current server banner).
|
|
|
|
### 5.3 Server Settings
|
|
- `GET /api/server/settings` (auth)
|
|
- `PATCH /api/server/settings` (admin)
|
|
- `PUT /api/server/icon` (admin)
|
|
- `PUT /api/server/banner` (admin)
|
|
|
|
## 6. Gateway Behavior
|
|
- Existing events:
|
|
- `message.created`
|
|
- `message.updated`
|
|
- `message.deleted`
|
|
- For upload-driven updates:
|
|
- A message can be created with media items in `uploading`.
|
|
- When upload becomes ready, server rewrites affected components and emits `message.updated`.
|
|
|
|
## 7. Component Media Item Contract (Client)
|
|
Each media item in a component should carry stable IDs and upload state:
|
|
|
|
```json
|
|
{
|
|
"itemId": "local_or_stable_id",
|
|
"source": "local_upload|external_url",
|
|
"uploadId": "pending_or_ready_attachment_id",
|
|
"attachmentId": "same_as_uploadId_when_known",
|
|
"status": "uploading|ready|failed",
|
|
"url": "/attachments/<attachmentId>",
|
|
"originalFileName": "image.png",
|
|
"mimeType": "image/webp",
|
|
"sizeBytes": 12345,
|
|
"width": 1280,
|
|
"height": 720,
|
|
"thumbHash": "optional"
|
|
}
|
|
```
|
|
|
|
### 7.1 Validation Rules (Server)
|
|
- Every component node must include:
|
|
- `id` (string)
|
|
- `type` (string)
|
|
- `typeVersion` (positive int)
|
|
- `data` (object)
|
|
- Nested components are supported under `data.children` with depth/size limits.
|
|
- Media item rules:
|
|
- `source=local_upload` => `attachmentId` required.
|
|
- `source=external_url` => valid `http/https` `url` required.
|
|
- Invalid component payloads are rejected server-side.
|
|
|
|
## 8. Client Implementation Notes
|
|
|
|
### 8.1 Staging and Transform Choice
|
|
On file pick/drop, choose transform mode before upload starts:
|
|
- `Compressed WebP` (recommended default)
|
|
- `WebP`
|
|
- `Original`
|
|
|
|
If user changes mode for a staged file:
|
|
- cancel in-flight upload
|
|
- reprocess
|
|
- re-upload
|
|
|
|
### 8.2 Metadata Trust
|
|
Client may send metadata hints, but server metadata is canonical.
|
|
Client should refresh with upload response values.
|
|
|
|
### 8.3 Caching
|
|
- `/attachments/:attachmentId`: immutable and long-cache.
|
|
- `/avatars`, `/icons`, `/banners`: mutable aliases; revalidate frequently.
|
|
|
|
## 9. AVIF and JPEG XL Policy
|
|
- MVP default: WebP (best compatibility/perf tradeoff).
|
|
- AVIF: optional client mode if target platform decode support is verified.
|
|
- JPEG XL: defer for now due to uneven ecosystem support.
|