Skip to content

Commit 4f3f1db

Browse files
committed
docs(protocol): document frame and replication contract
1 parent 3f723c3 commit 4f3f1db

4 files changed

Lines changed: 95 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ All notable changes to StreamHive are documented here. This project follows [Sem
77
### Added
88

99
- **`p2p`**: `PeerSnapshots` exposes connected peer metadata for operational tooling.
10+
- **Docs**: protocol reference for SHV1 frames, replication messages, limits, and repair behavior.
1011

1112
### Changed
1213

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ flowchart TB
164164
- [docs/WORKFLOWS.md](docs/WORKFLOWS.md) — local and CI expectations.
165165
- [docs/GOVERNANCE.md](docs/GOVERNANCE.md) — branch protection and release hygiene.
166166
- [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) — Docker and Kubernetes sketch.
167+
- [docs/PROTOCOL.md](docs/PROTOCOL.md) — frame format, replication messages, limits, and repair behavior.
167168
- [docs/RELEASE.md](docs/RELEASE.md) — release checklist.
168169

169170
## Contributing

docs/ARCHITECTURE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ flowchart TD
3232
| `internal/version` | Semver string for releases |
3333
| `.` | CLI: `run`, health HTTP server, replication demo flags |
3434

35+
For the wire-level frame and message contract, see [PROTOCOL.md](PROTOCOL.md).
36+
3537
### Core interfaces
3638

3739
The seams are interfaces, so storage and peers are swappable without touching the layers

docs/PROTOCOL.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Protocol Reference
2+
3+
StreamHive carries replication messages inside `SHV1` frames over a connected TCP peer.
4+
This document describes the current wire contract for operators and library users.
5+
6+
## Frame Format
7+
8+
Each frame has an 8-byte header followed by an opaque payload:
9+
10+
| Field | Size | Encoding | Meaning |
11+
|-------|------|----------|---------|
12+
| Magic | 4 bytes | ASCII | Must be `SHV1` |
13+
| Length | 4 bytes | big-endian uint32 | Payload length in bytes |
14+
| Payload | `Length` bytes | protocol-specific | Replication messages are JSON |
15+
16+
`p2p.ReadFrame` and `p2p.WriteFrame` default to `p2p.DefaultMaxFrameBytes`, currently
17+
`4 << 20` bytes. A frame with a bad magic value fails with `p2p.ErrBadMagic`. A frame
18+
whose declared payload length exceeds the configured maximum fails with
19+
`p2p.ErrFrameTooLarge`.
20+
21+
## Replication Payloads
22+
23+
Replication payloads are JSON values decoded into `replication.Message`:
24+
25+
```json
26+
{
27+
"type": "blob.put",
28+
"key": "base64-encoded-by-json",
29+
"data": "base64-encoded-by-json"
30+
}
31+
```
32+
33+
The Go `encoding/json` package encodes `[]byte` fields as base64 strings. This applies
34+
to `key`, `keys`, and `data` fields on the wire.
35+
36+
## Message Types
37+
38+
| Type | Fields | Meaning |
39+
|------|--------|---------|
40+
| `blob.put` | `key`, `data` | Store or replace one blob under `key`. |
41+
| `blob.has` | `keys` | Advertise keys available on the sender. |
42+
| `blob.missing` | `keys` | Ask the peer to send keys missing locally. |
43+
| `blob.get` | `key` | Ask the peer to send one key. |
44+
45+
The CLI replication handler uses `blob.has` and `blob.missing` for anti-entropy:
46+
47+
1. A peer advertises local keys on connect.
48+
2. When `-sync-interval` is set, a peer advertises local keys periodically.
49+
3. A receiver computes which advertised keys it lacks and sends `blob.missing`.
50+
4. The owner answers with `blob.put` for keys it can still read.
51+
52+
## Limits
53+
54+
Default replication limits are:
55+
56+
| Limit | Value | Error |
57+
|-------|-------|-------|
58+
| Max key size | 512 bytes | `replication.ErrKeyTooLarge` |
59+
| Max keys per inventory message | 4096 | `replication.ErrTooManyKeys` |
60+
| Max blob payload | `4 << 20` bytes | `replication.ErrDataTooLarge` |
61+
62+
Empty keys fail with `replication.ErrKeyEmpty`. Empty `keys` lists fail with
63+
`replication.ErrKeysEmpty`. Unknown message types fail with
64+
`replication.ErrUnknownMessageType`.
65+
66+
## Content Addressing
67+
68+
`-put-content-key` stores a blob under `SHA-256(data)`. The CLI logs SHA-256 keys as
69+
hex for readability. On receive, any 32-byte key is treated as a SHA-256 content key
70+
and verified against the payload before storage. A mismatch fails with
71+
`storage.ErrSHA256Mismatch` and the blob is not stored.
72+
73+
Opaque caller-chosen keys are still allowed. If an opaque key receives different data,
74+
the existing value is replaced. If an exact key/data pair is received again, the write is
75+
skipped and duplicate counters are incremented.
76+
77+
## Failure Behavior
78+
79+
Frame decode errors, message validation errors, storage errors, and peer write errors
80+
stop the current peer loop. The transport unregisters the peer and updates metrics.
81+
82+
There is no per-key acknowledgement protocol yet. Repair is driven by startup inventory,
83+
periodic inventory with `-sync-interval`, and reconnect behavior for static `-peers`
84+
when `-peer-reconnect` is enabled. If a blob send fails mid-sync, a later inventory pass
85+
or reconnect can request the missing key again.
86+
87+
## Observability
88+
89+
Use `/metrics` for JSON counters, `/metrics/prometheus` for Prometheus text format, and
90+
`/peers` for connected peer metadata. `/peers` includes remote address, local address,
91+
direction, connection timestamp, and connection age in milliseconds.

0 commit comments

Comments
 (0)