feat: EXT-007 P2P message history gossip
After hello verification, the connecting peer sends history_request to the first peer it meets (one per room, no fan-out). The responder queries SQLite and replies with a history_chunk. Received history is stored via INSERT OR IGNORE (mid dedup) and emitted as history_loaded IPC events. - proto: MsgHistoryRequest/Chunk types, HistoryEntry, EvtHistoryLoaded, ComputeMsgID (sha256 content-addressed ID), MsgID field on ChatMessage - store: ALTER TABLE ADD COLUMN msg_id + unique index migration (idempotent); RecentMessagesSince query (msg_id IS NOT NULL filter); msg_id persisted on save - mesh: RequestHistoryFrom, HandleHistoryRequest, HandleHistoryChunk methods; historyRequested/historyFirstPeer state to ensure single-peer requests - peer: dispatch history_request/history_chunk; RequestHistoryFrom after hello; stamp MsgID on incoming chat messages - ipc: stamp MsgID on outgoing group chat messages - EXTENSIONS.md: EXT-007 documented Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -224,3 +224,83 @@ Each in-progress `.tmp` file has a corresponding `.tmp.meta` JSON sidecar:
|
||||
|
||||
The sidecar is written when the transfer starts and removed on completion or
|
||||
corruption. Interrupted transfers keep the sidecar indefinitely.
|
||||
|
||||
---
|
||||
|
||||
## EXT-007 — P2P Message History Gossip
|
||||
|
||||
**Status:** implemented (daemon mode)
|
||||
**Affects:** peer-to-peer wire (two new message types); IPC (new event)
|
||||
|
||||
### Motivation
|
||||
|
||||
When a peer joins a network for the first time (or reconnects after an
|
||||
absence), they have no history. This extension lets them request recent
|
||||
messages from an existing peer over the already-established encrypted
|
||||
DataChannel, without involving the anchor.
|
||||
|
||||
### Wire messages
|
||||
|
||||
#### `history_request`
|
||||
|
||||
Sent by the newly-connected peer to the first peer whose hello is verified.
|
||||
One request per room.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "history_request",
|
||||
"room": "general",
|
||||
"since": 1700000000000,
|
||||
"limit": 200
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|---------|---------------|-------------|
|
||||
| `room` | string | Room to request history for. |
|
||||
| `since` | int64 (ms) | Only return messages with `ts > since`. 0 = return up to `limit` most recent. |
|
||||
| `limit` | int (max 500) | Maximum messages to return. Responder may return fewer. |
|
||||
|
||||
#### `history_chunk`
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "history_chunk",
|
||||
"room": "general",
|
||||
"history": [
|
||||
{ "mid": "...", "from": "<peer-id>", "from_alias": "alice", "text": "hello", "ts": 1700000001000 }
|
||||
],
|
||||
"history_done": true
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|----------------|--------|-------------|
|
||||
| `history` | array | Messages, oldest-first. |
|
||||
| `history_done` | bool | Always `true` (single-chunk response). |
|
||||
|
||||
### Deduplication
|
||||
|
||||
`mid` is the deduplication key. The store uses `INSERT OR IGNORE` on `mid`,
|
||||
so receiving a message twice (live or via gossip) is a no-op. Messages
|
||||
without a `mid` are assigned one at receive time and are not gossipped.
|
||||
|
||||
### Behaviour
|
||||
|
||||
- The **receiver** sends one `history_request` per known room immediately
|
||||
after hello verification with the **first** peer it connects to. Requesting
|
||||
only the first peer avoids fan-out amplification.
|
||||
- The **responder** queries its SQLite store and replies with a single
|
||||
`history_chunk`. `limit` is capped at 500 server-side. Rate-limited to one
|
||||
request per (peer, room) per 60 seconds.
|
||||
- Received history messages are saved to the local store (`INSERT OR IGNORE`)
|
||||
and emitted as `history_loaded` IPC events so the UI can display them.
|
||||
|
||||
### IPC event
|
||||
|
||||
```json
|
||||
{ "type": "history_loaded", "room": "general", "messages": [...] }
|
||||
```
|
||||
|
||||
Emitted once per room after a `history_chunk` is fully processed. The UI
|
||||
should render these messages with a visual separator from live messages.
|
||||
|
||||
Reference in New Issue
Block a user