5.4 KiB
5.4 KiB
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)
userIdis an immutable opaque identifier (host-independent).handleis derived metadata:@username@home.ChatUser.rolescontrols authorization (admin,member, etc.).GET /api/mereturnsrolesso 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_setupinconfig.yaml. - When
first_setup: true, the first registered local user getsadmin. - Later registrations remain
member.
3. Storage Model
3.1 Entities
ChatUser: identity/profile/auth data.ChatChannel: channel metadata.ChatMessage: channel message, search text, andcomponentsJson.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)sha256mimeType,sizeBytes,width,heightusageType:message | avatar | server_icon | server_bannerstatus:pending | ready | failed
4. Upload and Message Flow
4.1 Recommended Client Flow (Implemented Server Contract)
- Client stages attachment in composer/component editor.
- Client calls
POST /api/uploads/initto create pending upload. - Client uploads bytes to
PUT /api/uploads/:uploadId. - Client sends
POST /api/messageswith components referencinguploadId/attachmentId. - 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)usernamehandle(computed, mutable as home host changes)
- Cross-server join request now expects:
userIdusernamehomeServertimestampsignatureoveruserId: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.createdmessage.updatedmessage.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.
- A message can be created with media items in
7. Component Media Item Contract (Client)
Each media item in a component should carry stable IDs and upload state:
{
"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.childrenwith depth/size limits. - Media item rules:
source=local_upload=>attachmentIdrequired.source=external_url=> validhttp/httpsurlrequired.
- 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)WebPOriginal
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.