You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: ARCHITECTURE.md
+3Lines changed: 3 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -274,6 +274,7 @@ See [PROTOCOL.md](PROTOCOL.md) for the full message format specification.
274
274
3.**Message encryption**: Inner JSON payload is sealed with ChaCha20-Poly1305; nonce ‖ ciphertext is base64-encoded into `content` with `encrypted: true` (see **PROTOCOL.md**)
275
275
4.**Transport**: Standard WebSocket JSON; server does not decrypt
276
276
5.**Storage**: Server persists opaque ciphertext in the database
277
+
6.**Edits**: For `type: edit`, the reference client sends the replacement body ciphertext when E2E is enabled; the server updates `content` and persists `is_encrypted` from the wire `encrypted` field so reconnects and message metadata stay correct
277
278
278
279
## Database Schema
279
280
@@ -299,6 +300,8 @@ CREATE TABLE messages (
299
300
);
300
301
```
301
302
303
+
Edits update `content` and `edited`; `is_encrypted` follows the `encrypted` field on the edit message so ciphertext is not downgraded to plain text in the database after an edit.
Copy file name to clipboardExpand all lines: PROTOCOL.md
+3-1Lines changed: 3 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -122,7 +122,7 @@ These values of `type` extend the core chat protocol:
122
122
123
123
|`type`| Purpose |
124
124
|--------|---------|
125
-
|`edit`| Replace the text of an existing message. Requires `message_id` and new text in `content`. Only the original author may edit (server-enforced). |
125
+
|`edit`| Replace the text of an existing message. Requires `message_id` and new body in `content`. Set `encrypted` to `true` when `content` is E2E ciphertext (same base64 **nonce ‖ ciphertext** layout as `text`); the server persists that flag to `is_encrypted` so history and reconnects stay consistent. Only the original sender may edit; admins cannot edit someone else's message (unlike `delete`, where an admin may remove any message). Enforced by matching WebSocket `sender` to the stored row. |
126
126
|`delete`| Soft-delete a message. Requires `message_id`. Authors may delete their own messages; admins may delete any message. |
127
127
|`typing`| Typing indicator; `content` is not required. The server sets `sender` and broadcasts to clients (see [Channels](#channels) for delivery scope when `channel` is set). |
128
128
|`reaction`| Add or remove a reaction. Requires the `reaction` object (`emoji`, `target_id`, optional `is_removal`). |
@@ -158,6 +158,7 @@ Optional chat encryption uses a **shared symmetric key** for all participants (g
158
158
-**Algorithm**: ChaCha20-Poly1305 (RFC 8439). Each encrypted payload uses a random **12-byte nonce** (typical for this AEAD).
159
159
-**Text messages on the wire**: Same JSON message shape as plaintext chat; set `encrypted` to `true`. The `content` field is **standard base64** encoding of **nonce ‖ ciphertext** (nonce first, 12 bytes, then the Poly1305-sealed ciphertext). The plaintext decrypted by the AEAD is a JSON object representing the inner chat message (e.g. sender, content, type, timestamp) as produced by the reference client.
160
160
-**Files**: When `type` is `file` and E2E is enabled, the reference client encrypts file bytes with the same global key; see client implementation for the exact binary layout (nonce-prefixed ciphertext).
161
+
-**Edits** (`type`: `edit`): When E2E is enabled, the reference client encrypts the new plaintext the same way as a normal `text` message and sets `encrypted` accordingly. The server stores the new opaque `content` and updates `is_encrypted` from the incoming `encrypted` field (it does not force plaintext).
161
162
162
163
The server stores and relays opaque `content` (and encrypted file blobs) without performing decryption.
163
164
@@ -175,6 +176,7 @@ The server stores and relays opaque `content` (and encrypted file blobs) without
175
176
- Delivers to all connected clients **or** only to members of a channel when `channel` is non-empty and `sender` is not `System` (see [Channels](#channels)). Direct messages use a separate path (sender and recipient only).
176
177
- Reactions, read receipts, and last channel per user may be persisted server-side and replayed to reconnecting clients.
177
178
- Message history is capped at 1000 messages.
179
+
- On successful `type`: `edit`, the server updates `content`, sets `edited`, and sets stored encryption metadata from the incoming `encrypted` field (so edited ciphertext rows remain ciphertext with `is_encrypted` aligned to the wire).
Copy file name to clipboardExpand all lines: README.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -347,7 +347,7 @@ Run **`./marchat-client -doctor`** or **`./marchat-server -doctor`** for a text
347
347
### Messaging
348
348
| Command | Description |
349
349
|---------|-------------|
350
-
|`:edit <id> <text>`| Edit a message by its ID|
350
+
|`:edit <id> <text>`| Edit your own message by ID (admins cannot edit others' messages; with E2E on, the new text is encrypted like normal chat and the server keeps `is_encrypted` in sync)|
351
351
|`:delete <id>`| Delete a message by its ID |
352
352
|`:dm [user] [msg]`| Send a DM or toggle DM mode (no args exits DM mode) |
353
353
|`:search <query>`| Search message history on the server |
0 commit comments