Skip to content

Comments

tatanka: Add coordinated whitelist transitions#12

Open
martonp wants to merge 6 commits intobisoncraft:masterfrom
martonp:whitelistUpdate
Open

tatanka: Add coordinated whitelist transitions#12
martonp wants to merge 6 commits intobisoncraft:masterfrom
martonp:whitelistUpdate

Conversation

@martonp
Copy link
Contributor

@martonp martonp commented Feb 23, 2026

This PR adds coordinated whitelist transitions to the tatanka mesh network. Previously, whitelist changes required manually stopping nodes, editing files, and restarting. Now, nodes can propose a new whitelist and transition to it while running, using a two-phase consensus protocol.

When a node proposes a new whitelist, it broadcasts its proposal to the network via gossipsub. Other nodes in the mesh can independently propose the same whitelist. The whitelist manager on each node tracks the proposals of all peers and drives the transition through two phases. In the ready phase, a node marks itself as ready once at least 2/3 of the overlap peers (those present in both the current and proposed whitelists) are proposing the same whitelist, and no online overlap peer disagrees. In the commit phase, once a node is ready, it waits until every online overlap peer is also ready or has already switched, then atomically replaces its current whitelist with the proposed one. Offline overlap peers are not counted as disagreeing, but because the quorum threshold is computed from the full overlap set, they can block progress until they reconnect.

Nodes only actively maintain connections to peers in their current whitelist. Proposed peers are not connected to until the transition commits. During the handshake, nodes exchange both their current and proposed whitelists, and a connection is accepted as long as any combination of the two nodes' current and proposed whitelists match. This prevents nodes from rejecting each other when one has already committed the new whitelist while the other is still running the old one.

The whitelist is now stored as a JSON array of peer ID strings in the node's data directory. On disk, this file is the source of truth. The --whitelistpath flag has been replaced with --whitelist, which points to a plain-text file containing one peer ID per line (with support for # comments). On first run, the provided whitelist is installed into the data directory. On subsequent runs, the flag is only used for validation. If the provided list doesn't match the stored whitelist, the node refuses to start unless --forcewl is passed to explicitly overwrite it. If no --whitelist flag is given, the node loads its existing whitelist from the data directory.

Bootstrap addresses are now passed via --bootstrap flags (repeatable) in multiaddr format, e.g. --bootstrap /ip4/1.2.3.4/tcp/12345/p2p/12D3KooW.... A peerstore cache persists known peer addresses to disk, so after the first run bootstrap flags are no longer needed.

A new tatanka init subcommand generates (or loads) the node's private key and prints its peer ID to stdout. To set up a new network, you run tatanka init on each machine to get peer IDs, assemble a whitelist file containing all the peer IDs, then start each node with the whitelist and bootstrap addresses pointing to the other nodes.

The admin server has been refactored with new HTTP endpoints for proposing, clearing, force-setting, and adopting whitelists. The tatankactl TUI adds views for monitoring whitelist state, composing proposals with a checkbox-based peer editor, viewing diffs against mismatched peers, and adopting a peer's whitelist to resolve mismatches.

Add tatanka/types with Whitelist and WhitelistState types that provide
map-based peer ID sets with lazy SHA256 hashing for deterministic
equality comparisons, JSON serialization, and deep-copy support.
Persist known peer multiaddresses to a JSON file in the data directory.
On startup, the cache populates the libp2p peerstore so nodes can
reconnect to known peers without needing bootstrap addresses after
the initial run.
Move handleGetState to http_handlers.go and handleWebSocket + broadcast
to websocket.go, reducing the size of admin/server.go. No behavior
changes.
Replace the whitelist request/response handshake with a symmetric state
exchange. Each node now broadcasts its current and proposed whitelists
via gossipsub; a two-phase consensus mechanism (ready + commit) drives
the transition when a 2/3 overlap quorum agrees.

Key changes:
- Add whitelist_manager for proposal lifecycle and consensus tracking.
- Refactor mesh_connection_manager with callback-based config, tracker
  reconciliation, and per-peer whitelist state tracking.
- Refactor admin server with config-struct initialization, new HTTP
  endpoints (propose, force, adopt, clear), and granular WebSocket
  broadcast messages.
- Add admin notifier abstraction to avoid nil-checks for disabled UI.
- Add whitelist persistence (JSON) with init/save/load and flexible
  matching across current and proposed whitelists.
- Wire peerstore cache into startup and shutdown.
- Decompose TatankaNode.Run into discrete init phases.
Add TUI views for whitelist proposal lifecycle:
- Whitelist view showing current whitelist, active proposal, and
  network proposals aggregated from connected peers.
- Whitelist editor with checkbox-based peer selection for composing
  and submitting proposals.
- Diff view with adopt-peer's-whitelist flow for resolving mismatches.
- API client methods for propose, clear, force, and adopt operations.
- WebSocket message handlers for real-time peer, whitelist state,
  and whitelist membership updates.
Add 'init' subcommand that generates a private key and prints the peer
ID. Replace --whitelistpath with --whitelist (plain text, one peer ID
per line with # comments), --forcewl, and --bootstrap flags.

Update test harness to use 'tatanka init' instead of makeprivkey,
generate bootstrap flags automatically, and open TUI windows for each
node.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant