--- name: protocol-marchat description: >- Maintains marchat WebSocket JSON protocol and shared wire types including E2E encoding. Use when changing PROTOCOL.md, shared/types.go, shared/crypto.go, handshake, message types, or client-server wire compatibility. paths: - "PROTOCOL.md" - "shared/**" - "client/websocket*.go" --- # Protocol (marchat) Normative spec: `PROTOCOL.md`. Types and crypto: `shared/types.go`, `shared/crypto.go`. ## Wire basics - WebSocket `/ws`; JSON messages. - Handshake is first message after connect (`username`, optional `admin` / `admin_key`). - Message types: text, system, typing, reactions, edits, deletes, pins, DMs (with `recipient`), channels, read receipts, files. - Chat `content` byte cap (default 32 KiB, `MARCHAT_MAX_MESSAGE_BYTES` / `MARCHAT_MAX_MESSAGE_MB`) is separate from the file cap. Non-file inbound `content`, including E2E ciphertext, over the cap gets a System reply and is not persisted. File payloads use the file limit. WebSocket `SetReadLimit` stays the file DoS ceiling. ## E2E (chat) - **Global shared symmetric key** (32 bytes), ChaCha20-Poly1305 on the wire. - Server stores opaque ciphertext; does not decrypt chat content. - When `encrypted` is true, `content` is base64-encoded `nonce || ciphertext`. - Key distribution: out-of-band (`MARCHAT_GLOBAL_E2E_KEY`, or shared `keystore.dat` + passphrase). - **Not** per-user X25519 key exchange for chat. Client keystore: `client/crypto/keystore.go` (PBKDF2 + AES-GCM; v3 portable salt; legacy migrate on load). ## Breaking changes - Avoid breaking handshake or message JSON without discussion. - Document in `CHANGELOG.md` and `PROTOCOL.md`. - Consider older clients on mixed-version servers. ## Change workflow 1. Update `shared/` types and constants first. 2. Server hub/handlers relay or persist without decrypting E2E payloads. 3. Client encode/decode in `client/websocket.go` and related paths. 4. Add round-trip tests (`client/websocket_e2e_test.go`, `shared/crypto_test.go`). 5. Sync `PROTOCOL.md` and `ARCHITECTURE.md`. Plugin wire types extend `plugin/sdk`; keep compatible with `shared` message shapes where bridged.