feat: M4 gossip ops and UX
Refs #1 - /v1/diff endpoint returns each node's fingerprint set + tombstoned auto-thread status - knox gossip diff <peer-url>: shows peer-only/local-only fingerprints (pull/keep preview) and tombstone divergence (resolved-on-peer vs would-resurrect) - knox gossip sync: one-shot anti-entropy sweep + reconcile - gossip server starts before initial seed so peers can reach a booting node; KNOX_PEER_ADDR alone now serves without KNOX_PEERS - integration tests for diff + tombstone reporting - e2e verified: two live daemons, diff previewed 1655 peer-only fps, sync converged second node to 1655 observations
This commit is contained in:
@@ -179,4 +179,44 @@ type Peer struct {
|
||||
LastHandshake string
|
||||
Cursor int64
|
||||
CreatedAt string
|
||||
}
|
||||
|
||||
// DistinctFingerprints returns the set of all observed fingerprints — the
|
||||
// ground-truth index of what this node knows. Used by gossip diff.
|
||||
func (k *KnoxDB) DistinctFingerprints() (map[string]bool, error) {
|
||||
rows, err := k.db.Query(`SELECT DISTINCT fingerprint FROM observations`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
set := make(map[string]bool)
|
||||
for rows.Next() {
|
||||
var f string
|
||||
if err := rows.Scan(&f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
set[f] = true
|
||||
}
|
||||
return set, rows.Err()
|
||||
}
|
||||
|
||||
// ThreadStatusByCluster returns cluster_key → status for auto-threaded threads,
|
||||
// excluding human-created threads (no cluster key). Diff uses it to show
|
||||
// tombstoned threads: a thread resolved on one node but active on another.
|
||||
func (k *KnoxDB) ThreadStatusByCluster() (map[string]string, error) {
|
||||
rows, err := k.db.Query(
|
||||
`SELECT cluster_key, status FROM threads WHERE cluster_key IS NOT NULL AND cluster_key<>''`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
out := make(map[string]string)
|
||||
for rows.Next() {
|
||||
var key, status string
|
||||
if err := rows.Scan(&key, &status); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[key] = status
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
Reference in New Issue
Block a user