add docs, protocol, plugins, README, CHANGELOG
This commit is contained in:
parent
1d2209bfac
commit
f262da222b
44 changed files with 9269 additions and 0 deletions
266
docs/02-protocol/packets.md
Normal file
266
docs/02-protocol/packets.md
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
# Packets
|
||||
|
||||
> **Phase 1 note:** TCP control payloads are serialized as **JSON**, not Protobuf.
|
||||
> Protobuf is the Phase 2 target. See [01-architecture.md](../01-architecture.md).
|
||||
|
||||
## Base packet
|
||||
|
||||
All LNEx packets share a common header, regardless of transport.
|
||||
|
||||
```
|
||||
┌──────────────┬──────────────┬──────────────┬────────────────┬─────────┐
|
||||
│ packet_id │ flags │ sequence │ payload_length │ payload │
|
||||
│ (2 bytes) │ (2 bytes) │ (4 bytes) │ (4 bytes) │ (var) │
|
||||
└──────────────┴──────────────┴──────────────┴────────────────┴─────────┘
|
||||
```
|
||||
|
||||
### Fields
|
||||
|
||||
`packet_id` — identifies the packet type. See packet type registry below.
|
||||
|
||||
`flags` — bitmask:
|
||||
|
||||
```
|
||||
bit 0 — COMPRESSED payload is zstd compressed
|
||||
bit 1 — ENCRYPTED payload is encrypted (ChaCha20-Poly1305)
|
||||
bit 2 — FRAGMENTED this is a fragment of a larger payload
|
||||
bit 3 — LAST_FRAG this is the last fragment
|
||||
bit 4 — ACK_REQ sender requests acknowledgement (TCP path only)
|
||||
bit 5-15 — reserved
|
||||
```
|
||||
|
||||
`sequence` — monotonically increasing per-session counter.
|
||||
On the UDP voice path, gaps in sequence indicate packet loss.
|
||||
|
||||
`payload_length` — byte length of the payload that follows.
|
||||
|
||||
`payload` - packet-type-specific data. **Phase 1:** JSON. **Phase 2:** Protobuf (planned).
|
||||
|
||||
---
|
||||
|
||||
## Voice packet
|
||||
|
||||
Voice packets are sent over UDP. They extend the base header with
|
||||
audio-specific fields before the Opus payload.
|
||||
|
||||
```
|
||||
┌──────────────┬──────────────┬──────────────┬────────────────┬────────────────┬────────────┐
|
||||
│ packet_id │ flags │ voice_seq │ timestamp │ channel_id │ opus_data │
|
||||
│ = 0x0010 │ (2 bytes) │ (4 bytes) │ (4 bytes) │ (8 bytes) │ (var) │
|
||||
└──────────────┴──────────────┴──────────────┴────────────────┴────────────────┴────────────┘
|
||||
```
|
||||
|
||||
### Fields
|
||||
|
||||
`voice_seq` — separate sequence counter for the voice stream.
|
||||
Resets per channel join. Used by jitter buffer for reordering.
|
||||
|
||||
`timestamp` — RTP-style timestamp in samples (48000 Hz clock).
|
||||
Used for jitter buffer and playout scheduling.
|
||||
|
||||
`channel_id` — identifies which voice channel this frame belongs to.
|
||||
Allows a single UDP socket to carry multiple channels.
|
||||
|
||||
`opus_data` — raw Opus-encoded frame. Length derived from `payload_length`
|
||||
minus the fixed voice header (16 bytes).
|
||||
|
||||
---
|
||||
|
||||
## Packet type registry
|
||||
|
||||
```
|
||||
0x0001 HELLO server → client on connect
|
||||
0x0002 AUTH client → server, identity + signature
|
||||
0x0003 SESSION server → client, session established
|
||||
0x0004 PING either direction
|
||||
0x0005 PONG reply to PING
|
||||
|
||||
0x0010 VOICE_PACKET UDP, client ↔ voice-node
|
||||
0x0011 VOICE_STATE speaking / silent / muted
|
||||
|
||||
0x0020 CHAT_MESSAGE text message in channel
|
||||
0x0021 CHAT_HISTORY batch of historical messages
|
||||
|
||||
0x0030 JOIN_CHANNEL client → gateway
|
||||
0x0031 LEAVE_CHANNEL client → gateway
|
||||
0x0032 CHANNEL_STATE gateway → client, full channel snapshot
|
||||
|
||||
0x0040 USER_JOIN broadcast to channel members
|
||||
0x0041 USER_LEAVE broadcast to channel members
|
||||
|
||||
0x0050 PERMISSION_CHECK gateway → client
|
||||
0x0051 PERMISSION_DENY gateway → client
|
||||
|
||||
0x0060 DM_START client → gateway, initiate 1:1 DM conversation
|
||||
0x0061 DM_MESSAGE client ↔ gateway, individual DM message
|
||||
0x0062 DM_HISTORY client → gateway, fetch last 50 messages
|
||||
|
||||
0x0100 GUILD_CREATE client → gateway
|
||||
0x0101 GUILD_DELETE client → gateway
|
||||
0x0102 GUILD_LIST client → gateway
|
||||
0x0103 GUILD_MEMBER_JOIN client → gateway
|
||||
0x0104 GUILD_MEMBER_LEAVE client → gateway
|
||||
0x0105 GUILD_MEMBER_KICK client → gateway
|
||||
0x0106 ROLE_CREATE client → gateway
|
||||
0x0107 ROLE_DELETE client → gateway
|
||||
0x0108 INVITE_CREATE client → gateway
|
||||
0x0109 INVITE_ACCEPT client → gateway
|
||||
0x010A INVITE_DELETE client → gateway
|
||||
|
||||
0x0140 PRESENCE_UPDATE client → gateway
|
||||
0x0141 PRESENCE_SYNC gateway → client
|
||||
0x0142 PRESENCE_EVENT gateway → client
|
||||
|
||||
0x0150 FRIEND_REQUEST client → gateway
|
||||
0x0151 FRIEND_ACCEPT client → gateway
|
||||
0x0152 FRIEND_DECLINE client → gateway
|
||||
0x0153 FRIEND_REMOVE client → gateway
|
||||
0x0154 FRIEND_LIST client → gateway
|
||||
0x0155 BLOCK_USER client → gateway
|
||||
0x0156 UNBLOCK_USER client → gateway
|
||||
0x0157 BLOCK_LIST client → gateway
|
||||
|
||||
0x00F0 ERROR any direction, see error codes
|
||||
0x00FF DISCONNECT graceful close
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Direct Messages (Phase 1.1)
|
||||
|
||||
DM support enables 1:1 private messaging with persistent history. DMs are identified by a canonical ID format:
|
||||
`dm_{lexicographically_smaller_uid}_{larger_uid}`. This ensures both participants reference the same conversation.
|
||||
|
||||
### DmStart (0x0060)
|
||||
|
||||
Client initiates or opens an existing DM conversation with another user.
|
||||
|
||||
```json
|
||||
{
|
||||
"target_user_id": "user_pubkey_b64"
|
||||
}
|
||||
```
|
||||
|
||||
Response (server sends back as 0x0060):
|
||||
|
||||
```json
|
||||
{
|
||||
"dm_id": "dm_userid1_userid2",
|
||||
"other_user_id": "user_pubkey_b64",
|
||||
"other_nickname": "Alice",
|
||||
"messages": [
|
||||
{
|
||||
"dm_id": "dm_userid1_userid2",
|
||||
"sender_id": "user_pubkey_b64",
|
||||
"content": "Hello!",
|
||||
"timestamp": 1234567890000
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### DmMessage (0x0061)
|
||||
|
||||
Send a new DM or receive one from another user.
|
||||
|
||||
```json
|
||||
{
|
||||
"dm_id": "dm_userid1_userid2",
|
||||
"sender_id": "user_pubkey_b64",
|
||||
"content": "Hello Alice!",
|
||||
"timestamp": 1234567890000
|
||||
}
|
||||
```
|
||||
|
||||
Server-authoritative: sender_id and timestamp are set by the server based on the authenticated session.
|
||||
|
||||
### DmHistory (0x0062)
|
||||
|
||||
Fetch historical messages from a DM.
|
||||
|
||||
Request:
|
||||
```json
|
||||
{
|
||||
"dm_id": "dm_userid1_userid2"
|
||||
}
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"dm_id": "dm_userid1_userid2",
|
||||
"messages": [
|
||||
{ "dm_id": "...", "sender_id": "...", "content": "...", "timestamp": 1234567890000 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Protobuf schema
|
||||
|
||||
Payload of each packet is a serialized Protobuf message.
|
||||
Schemas live in `protocol/` at the repository root.
|
||||
|
||||
Example — `ChatMessage`:
|
||||
|
||||
```protobuf
|
||||
syntax = "proto3";
|
||||
|
||||
message ChatMessage {
|
||||
string message_id = 1; // UUID v4
|
||||
string channel_id = 2;
|
||||
string sender_id = 3; // sender pubkey (short form)
|
||||
string content = 4;
|
||||
int64 timestamp = 5; // Unix ms
|
||||
}
|
||||
```
|
||||
|
||||
Example — `VoiceState`:
|
||||
|
||||
```protobuf
|
||||
message VoiceState {
|
||||
string user_id = 1;
|
||||
string channel_id = 2;
|
||||
|
||||
enum State {
|
||||
SILENT = 0;
|
||||
SPEAKING = 1;
|
||||
MUTED = 2;
|
||||
DEAFENED = 3;
|
||||
}
|
||||
|
||||
State state = 3;
|
||||
}
|
||||
```
|
||||
|
||||
Full schema reference: `protocol/*.proto`
|
||||
|
||||
---
|
||||
|
||||
## Fragmentation
|
||||
|
||||
Payloads larger than 1400 bytes on the UDP path are fragmented.
|
||||
Each fragment carries the same `sequence`, with `FRAGMENTED` and optionally
|
||||
`LAST_FRAG` flags set. The receiver reassembles before passing to the
|
||||
application layer.
|
||||
|
||||
On TCP, fragmentation is not used — TCP handles it at the transport layer.
|
||||
|
||||
---
|
||||
|
||||
## Error codes
|
||||
|
||||
```
|
||||
0x01 ERR_VERSION_MISMATCH unsupported LNEx version
|
||||
0x02 ERR_AUTH_FAILED invalid signature or unknown identity
|
||||
0x03 ERR_SESSION_EXPIRED session token no longer valid
|
||||
0x04 ERR_PERMISSION_DENIED insufficient permissions for action
|
||||
0x05 ERR_CHANNEL_NOT_FOUND channel does not exist on this node
|
||||
0x06 ERR_RATE_LIMITED too many packets in window
|
||||
0x07 ERR_INVALID_PACKET malformed header or payload
|
||||
0x08 ERR_NODE_UNAVAILABLE federation target node unreachable
|
||||
0x09 ERR_GUILD_NOT_FOUND guild does not exist
|
||||
0x0A ERR_BLOCKED you are blocked by this user
|
||||
0xFF ERR_INTERNAL server-side error
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue