476afaf329f7e97b2f5a2e7aac4f167d1320f8b2
Some checks failed
CI / Rust Format (push) Has been cancelled
CI / Clippy (push) Has been cancelled
CI / Test Server (push) Has been cancelled
CI / Frontend Check (push) Has been cancelled
CI / Tauri Client Check (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Build Tauri (Linux) (push) Has been cancelled
- Update release workflow with matrix strategy for Linux/macOS/Windows - Upgrade @tauri-apps/api from v1 to v2 in frontend - Change all tauri/tauri imports to tauri/core (Tauri v2) - Sync tauri.conf.json across project root and src-tauri
EifelDC
Discord-like Matrix chat platform built with Rust, Svelte, and Tauri.
Architecture
| Component | Crate | Description |
|---|---|---|
| Server | eifeldc-server |
Axum web server, Matrix SDK, LiveKit voice, SQLite sessions |
| Bot SDK | eifeldc-bot-sdk |
SDK for building Matrix bots |
| Client | eifeldc-client |
Tauri desktop app |
| Frontend | — | Svelte + TypeScript UI |
Prerequisites
- Rust 1.82+ (stable)
- Node.js 20+
- A running Matrix Synapse homeserver
- LiveKit server (for voice)
Quick Start
Build Server & Bot SDK
cargo build --release -p eifeldc-server -p eifeldc-bot-sdk
Build Frontend
cd client/src-ui
npm ci
npm run build
Build Tauri Desktop App
Requires system dependencies:
# Ubuntu/Debian
sudo apt-get install -y libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev libssl-dev
cargo build --release -p eifeldc-client
Run the Server
cargo run -p eifeldc-server
The server starts on http://0.0.0.0:3000 and serves the frontend from client/src-ui/dist.
Environment Variables
| Variable | Default | Description |
|---|---|---|
EIFELDC_DB |
eifeldc.db |
SQLite database path for session storage |
EIFELDC_STATIC_DIR |
client/src-ui/dist |
Path to frontend static files |
LIVEKIT_API_KEY |
devkey |
LiveKit API key |
LIVEKIT_API_SECRET |
devsecret |
LiveKit API secret |
LIVEKIT_URL |
ws://localhost:7880 |
LiveKit server URL |
RUST_LOG |
— | Logging filter (e.g. eifeldc_server=info) |
EIFELDC_LOG_FORMAT |
pretty |
Log format: pretty or json |
EIFELDC_CORS_ORIGINS |
* |
Comma-separated allowed origins (e.g. https://app.example.org,https://dev.example.org) |
EIFELDC_SESSION_TTL |
— | Session TTL in seconds (no expiry if unset) |
EIFELDC_RATE_LIMIT |
60 |
Max requests per minute per client |
EIFELDC_MAX_UPLOAD_MB |
50 |
Max upload size in MB |
Docker Deployment
# Configure environment
cp infra/.env.example infra/.env
# Edit infra/.env with your domain and secrets
# Setup infrastructure (generates secrets, configs, certs)
bash infra/scripts/setup.sh your.domain.com
# Or manually:
cd infra
docker compose up -d
Services: EifelDC server, Synapse, PostgreSQL, LiveKit, coturn, nginx.
Useful Commands
make docker-build # Build Docker image
make docker-up # Start containers
make docker-down # Stop containers
make docker-logs # Follow EifelDC logs
make health # Run healthcheck
make backup # Backup data
API Reference
All endpoints are under /api. Public endpoints require no auth. Protected endpoints require a Bearer token in the Authorization header.
Authentication
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/login |
No | Login with Matrix credentials |
| POST | /api/register |
No | Register a new Matrix user |
| GET | /api/current-user |
No | Get current user ID from token |
| POST | /api/logout |
Yes | Logout and invalidate session |
Login Request:
{ "homeserver": "https://matrix.example.org", "username": "alice", "password": "secret" }
Login Response:
{ "success": true, "user_id": "@alice:example.org", "token": "uuid-token", "error": null }
Rooms
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/rooms |
List joined rooms |
| POST | /api/rooms/create |
Create a new room |
| POST | /api/rooms/join |
Join a room by ID or alias |
| POST | /api/rooms/{room_id}/leave |
Leave a room |
| GET | /api/rooms/{room_id}/members |
List room members |
| POST | /api/rooms/{room_id}/name |
Set room name |
| POST | /api/rooms/{room_id}/topic |
Set room topic |
| POST | /api/rooms/{room_id}/avatar |
Set room avatar (multipart) |
| GET | /api/rooms/unread |
Get unread notification counts |
| POST | /api/rooms/{room_id}/read |
Mark room as read |
Messages
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/rooms/{room_id}/messages |
Get room messages |
| POST | /api/rooms/{room_id}/send |
Send a message |
| POST | /api/rooms/{room_id}/edit |
Edit a message |
| POST | /api/rooms/{room_id}/delete/{event_id} |
Delete a message |
| POST | /api/rooms/{room_id}/react |
React to a message |
| POST | /api/rooms/{room_id}/typing |
Set typing notification |
| POST | /api/rooms/{room_id}/upload |
Upload a file (multipart) |
Threads
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/rooms/{room_id}/threads |
List threads in a room |
| GET | /api/rooms/{room_id}/threads/{thread_id} |
Get thread messages |
| POST | /api/rooms/{room_id}/threads/{thread_id}/reply |
Reply in a thread |
| POST | /api/rooms/{room_id}/reply |
Reply to a message |
Voice (LiveKit)
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/voice/join |
Join a voice channel |
| POST | /api/voice/leave |
Leave voice channel |
| POST | /api/voice/toggle-mute |
Toggle mute |
| POST | /api/voice/toggle-deafen |
Toggle deafen |
| GET | /api/voice/participants |
Get voice participants |
Presence
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/presence/set |
Set presence status |
| GET | /api/presence/{user_id} |
Get user presence |
Profile
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/profile/me |
Get own profile |
| GET | /api/profile/{user_id} |
Get user profile |
| POST | /api/profile/displayname |
Set display name |
| POST | /api/profile/avatar |
Upload avatar (multipart) |
Emoji & Roles
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/rooms/{room_id}/emoji |
Get custom emoji |
| POST | /api/rooms/{room_id}/emoji/upload |
Upload custom emoji |
| GET | /api/rooms/{room_id}/roles |
Get room roles |
| POST | /api/rooms/{room_id}/roles/assign |
Assign role to user |
| POST | /api/rooms/{room_id}/roles/remove |
Remove role from user |
| GET | /api/rooms/{room_id}/permissions/{user_id} |
Get user permissions |
Media & WebSocket
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/media/{mxc_path} |
Proxy Matrix media |
| GET | /api/metrics |
Prometheus metrics endpoint |
| GET | /api/ws?token={token} |
WebSocket for real-time events |
WebSocket Events
Monitoring
Prometheus Metrics are exposed at /api/metrics:
| Metric | Type | Description |
|---|---|---|
eifeldc_http_requests_total |
Counter | Total HTTP requests |
eifeldc_active_sessions |
Gauge | Active user sessions |
eifeldc_active_websockets |
Gauge | Active WebSocket connections |
eifeldc_messages_sent_total |
Counter | Total messages sent |
eifeldc_rooms_joined_total |
Counter | Total room join operations |
eifeldc_uploads_total |
Counter | Total file uploads |
eifeldc_voice_participants |
Gauge | Current voice participants |
Structured Logging — set EIFELDC_LOG_FORMAT=json for JSON output:
{"timestamp":"2026-04-29T10:00:00Z","level":"INFO","target":"eifeldc_server::routes::auth","fields":{"message":"LiveKit URL: ws://localhost:7880"}}
Use RUST_LOG for filtering: RUST_LOG=eifeldc_server=debug,tower_http=info
WebSocket Events
{ "type": "message", "room_id": "...", "event_id": "...", "sender": "...", "body": "...", "timestamp": 0 }
{ "type": "message_edited", "room_id": "...", "event_id": "...", "new_body": "..." }
{ "type": "message_deleted", "room_id": "...", "redacts": "..." }
{ "type": "reaction", "room_id": "...", "event_id": "...", "key": "👍", "sender": "..." }
{ "type": "room_joined", "room_id": "...", "name": "..." }
{ "type": "room_left", "room_id": "..." }
{ "type": "presence", "user_id": "...", "status": "online|idle|offline", "status_msg": "..." }
{ "type": "typing", "room_id": "...", "user_id": "...", "typing": true }
{ "type": "voice_state_update", "room_id": "...", "user_id": "...", "muted": false, "deafened": false }
{ "type": "voice_user_joined", "room_id": "...", "user_id": "..." }
{ "type": "voice_user_left", "room_id": "...", "user_id": "..." }
{ "type": "thread_reply", "room_id": "...", "root_event_id": "...", "event_id": "...", "sender": "...", "body": "...", "timestamp": 0 }
Bot SDK
use std::sync::Arc;
use eifeldc_bot_sdk::{BotClient, BotEvent, CommandContext};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut bot = BotClient::new("https://matrix.example.org")
.with_auth("botuser", "botpassword");
bot.on_event(|event| {
match event {
BotEvent::Message { room_id, sender, body, .. } => {
println!("{} in {}: {}", sender, room_id, body);
}
_ => {}
}
});
bot.on_command("hello", Arc::new(|ctx: CommandContext| {
println!("Hello command from {} in {}!", ctx.sender, ctx.room_id);
}));
bot.start().await?;
Ok(())
}
Development
# Check compilation
cargo check -p eifeldc-server -p eifeldc-bot-sdk
# Run linter
cargo clippy -p eifeldc-server -p eifeldc-bot-sdk -- -D warnings
# Check formatting
cargo fmt --all -- --check
# Run tests
cargo test -p eifeldc-server -p eifeldc-bot-sdk
# Build frontend
cd client/src-ui && npm ci && npm run build
# Run dev server
cargo run -p eifeldc-server
License
All rights reserved.
Description
Languages
Rust
60.7%
Svelte
22.8%
TypeScript
9.9%
Shell
5.1%
Dockerfile
0.7%
Other
0.8%