mimir-mediator is the experimental alpha implementation of a multi-user chat room server for Mimir Messenger.
It provides decentralized group chat functionality over the Yggdrasil network, using yggquic as its QUIC-based transport layer.
- Multi-room, multi-user chat support.
- Authenticated connections using Ed25519 public keys.
- Per-room permissions (owner, admin, moderator, user, read-only, banned).
- Persistent storage via
modernc.org/sqlite. - Live message broadcasting to connected subscribers.
- Cryptographic verification of signed requests.
- Automatic creation and deletion of per-chat tables.
- Safe concurrent handling of many users and chats.
- Go ≥ 1.22
- Revertron's fork of
github.com/Revertron/yggquic modernc.org/sqlitegithub.com/yggdrasil-network/yggdrasil-go/src/core
git clone https://github.com/Revertron/mimir-mediator.git
cd mediator
go mod init mediator
go get modernc.org/sqlite
go get github.com/Revertron/yggquic
go get github.com/yggdrasil-network/yggdrasil-go/src/core
go build -o mediator mediator.go tlv.goYou’ll end up with a binary named mediator.
The mediator requires one or more Yggdrasil peer endpoints for bootstrapping:
./mediator -peer tcp://203.0.113.5:12345 -peer tcp://203.0.113.6:12345At first run, it will:
- Generate a persistent Ed25519 keypair (
mediator.key). - Create a SQLite database (
mediator.db). - Launch a Yggdrasil node and begin accepting QUIC client connections.
You’ll see log output with your public key prefix (for debugging).
| Table | Purpose |
|---|---|
nonces(pubkey, nonce, ts) |
Nonces used during authentication. |
chats(id, owner_pubkey, created_at) |
Registry of all chat rooms. |
Each chat room id has its own three tables:
| Table | Purpose |
|---|---|
settings-{id} |
Room metadata (name, description, avatar, permissions). |
users-{id} |
Members list and their roles (owner/admin/mod/user/readonly). |
messages-{id} |
Message metadata (id, timestamp, guid, author) - includes user/system messages. |
- Client sends GET_NONCE(pubkey) → receives random 32-byte nonce.
- Client signs this nonce using Ed25519.Sign(priv, nonce).
- Client sends AUTH(pubkey, nonce, signature) → if valid, session becomes authenticated.
Signatures must satisfy:
- Valid Ed25519 signature
- First two signature bytes are zero (
sig[0]==0 && sig[1]==0)
Nonces are stored in the DB and can be purged daily.
Each request/response is a framed binary packet.
[version:1=0x01][cmd:1][reqId:2][len:4][payload:len]
[status:1][reqId:2][len:4][payload:len]
| Value | Meaning |
|---|---|
0x00 |
OK |
0x01 |
Error |
| Cmd | Code | Direction | Description |
|---|---|---|---|
GET_NONCE |
0x01 | C→S | Request a new nonce for authentication. |
AUTH |
0x02 | C→S | Authenticate with pubkey + signature. |
CREATE_CHAT |
0x10 | C→S | Create a new chat (signed over nonce+key). |
DELETE_CHAT |
0x11 | C→S | Delete owned chat. |
ADD_USER |
0x20 | C→S | Add a user to an existing chat. |
DELETE_USER |
0x21 | C→S | Ban/remove user from chat. |
LEAVE_CHAT |
0x22 | C→S | User leaves chat (deleted from users table). |
GET_USER_CHATS |
0x23 | C→S | List all chats user belongs to (not banned). |
PING |
0x03 | C→S | Keep-alive ping. |
SEND_MESSAGE |
0x30 | C→S | Send encrypted message blob to chat. |
DELETE_MESSAGE |
0x31 | C→S | Delete message (admin/mod only). |
GOT_MESSAGE |
0x32 | S→C | Server-pushed message (user/system). |
GET_LAST_MESSAGE_ID |
0x33 | C→S | Query the latest message ID. |
SUBSCRIBE |
0x35 | C→S | Subscribe connection to live updates of a chat. |
GET_MESSAGES_SINCE |
0x36 | C→S | Batch fetch messages since ID. |
SEND_INVITE |
0x40 | C→S | Send invite to user. |
GOT_INVITE |
0x41 | S→C | Server-pushed invite delivery. |
INVITE_RESPONSE |
0x42 | C→S | Client responds to invite (accept/reject). |
UPDATE_MEMBER_INFO |
0x50 | C→S | Update member info (nickname, avatar). |
REQUEST_MEMBER_INFO |
0x51 | S→C | Server requests member info from client. |
GET_MEMBERS_INFO |
0x52 | C→S | Get all members info (snapshot/incremental). |
GET_MEMBERS |
0x53 | C→S | Get list of member public keys. |
GOT_MEMBER_INFO |
0x54 | S→C | Server-pushed member info updated. |
| Bit | Name | Meaning |
|---|---|---|
0x80 |
permOwner |
Chat creator, full control. |
0x40 |
permAdmin |
Administrative privileges. |
0x20 |
permMod |
Can moderate users/messages. |
0x10 |
permUser |
Regular participant. |
0x08 |
permReadOnly |
May only read, not post. |
0x01 |
permBanned |
User is banned from chat. |
When a user sends a message:
-
It’s inserted into the
messages-{id}table. -
The server broadcasts the message to all connected subscribers of that chat (except sender).
-
Subscribers receive it immediately with:
[statusOK][reqId=0][len][payload=[msg_id][msg_len][msg_blob]] -
This happens asynchronously.
- On connect: client sends
protoClientbyte (0x00). - On disconnect: the server automatically unsubscribes the user from all subscribed chats.
- On Ctrl+C: server closes cleanly (SQLite + Yggdrasil node shutdown).
- No message encryption on server-side (payloads are stored raw as encrypted blobs from clients).
- No TTL cleanup for old nonces (to be added).
- No user nickname or text rank logic implemented yet.
- Owner cannot leave their own chat (must delete instead).
| File | Purpose |
|---|---|
mediator.go |
Full server implementation. |
mediator.db |
SQLite database (auto-created). |
mediator.key |
Server Ed25519 keypair. |
-
Authenticate:
GET_NONCE(pubkey)AUTH(pubkey, nonce, signature)
-
Create chat:
CREATE_CHAT(owner_pubkey, nonce, key, signature, name, desc, avatar)
-
Invite users:
ADD_USER(chat_id, pubkey)
-
Subscribe to messages:
SUBSCRIBE(chat_id)
-
Send and receive messages:
SEND_MESSAGE(chat_id, blob)- Receive live updates via
GOT_MESSAGE.
-
Leave chat:
LEAVE_CHAT(chat_id)
-
Query chats:
GET_USER_CHATS()
requestId(u16) is echoed in every response, allowing pipelined async requests.- Max payload size: 32 MB.
- SQLite WAL mode is used for better concurrency.
- Designed to be embedded into Yggdrasil-based distributed messengers.
MIT License © 2025 Revertron Use freely with attribution. This software is experimental and provided as is.