feat: M3 peer gossip protocol

Refs #1

- peers table (peer_id, addr, cursor, last_handshake)
- watch serves HTTP API: GET /v1/ping (knowledge vector),
  GET /v1/log?node=&after= (cursor-paged pull), POST /v1/obs/batch
- anti-entropy sweep (Run): ping, pull what we lack, push own obs;
  echo suppressed by node_id ownership; reconcile-on-pull
- config via KNOX_PEERS / KNOX_PEER_ADDR; knox gossip status
- db: GossipObservation wire type, PushObservations, ObservationsAfter,
  KnowledgeVector, peer upsert/list
- integration tests: bidirectional convergence + idempotency
This commit is contained in:
2026-08-29 04:53:26 -07:00
parent aa0dec68c1
commit 8c054094a1
10 changed files with 755 additions and 20 deletions
+8 -4
View File
@@ -225,10 +225,14 @@ UNIQUE index) making auto-creation idempotent; `knox reconcile` rebuilds
create/0 link on re-run); a log-only DB reconstructs `entries` and thread
`cluster_key`s bit-identical to the original.
**M3 — Peer protocol.** `peers` settings table, `/v1/ping`, `/v1/log` pull,
`/v1/obs/batch` push, handshake + periodic anti-entropy, echo suppression.
Verify: two nodes converge to identical logs after partition (integration test).
`time.Now()`-free fact paths confirmed by grep.
**M3 — Peer protocol.** DONE. `peers` table; HTTP API (`GET /v1/ping` with
knowledge vector, `GET /v1/log?node=&after=` pull with cursor paging,
`POST /v1/obs/batch` push); `watch` daemon serves its log and runs a periodic
anti-entropy sweep (`KNOX_PEERS`, `KNOX_PEER_ADDR`), reconcile-on-pull; echo
suppression by `node_id` ownership; `knox gossip status`. Verify: two nodes
converge to identical logs after a bidirectional sweep (integration test
`internal/watch/gossip_test.go`); second sweep is idempotent; `time.Now()`-free
fact paths confirmed by grep.
**M4 — Ops & UX.** `knox gossip` subcommand (status/diff), reconcile-on-pull,
tombstoned thread handling in diff output, logging, config (env `KNOX_PEER_ADDR`,