feat: add polo score and display total nodes in dashboard#7
Merged
Conversation
TeoSlayer
pushed a commit
that referenced
this pull request
Apr 30, 2026
* feat: display polo score in table * fix: run pre-commit tools * fix: adjust colours * feat: display total nodes in dashboard --------- Co-authored-by: Alex Godoroja <alex@vulturelabs.io>
TeoSlayer
pushed a commit
that referenced
this pull request
May 3, 2026
Symptom: a peer pair establishes a tunnel, exchanges some traffic,
then both sides go idle. Consumer-grade NATs purge UDP mappings
after 30-90 s of no outbound traffic; CGNAT and enterprise NATs
after a few minutes. When either side eventually wants to send,
their packet arrives at the peer's NAT but there's no inbound
mapping → silently dropped. Recovery requires the peer's app to
send first AND for that send to trigger a key-exchange (only true
if our pc became un-decryptable, which usually it hasn't).
This is the CAUSE; iter 5 (NAT-remap address-learning) fixes the
CONSEQUENCE. Together they ensure long-idle peer pairs survive
NAT mapping churn.
Why per-Connection keepalives don't cover this: idleSweepLoop only
sends keepalive ACKs for connections in StateEstablished. Tunnel
peer state (tm.peers + tm.crypto) outlives individual connections.
A long-lived peer relationship with bursty connection cycles can
have NO active Connection for hours, during which no keepalive
fires.
Lands in this RED commit (compile-clean half of the fix):
- lastOutboundSend map[uint32]time.Time field on TunnelManager,
init in NewTunnelManager, cleared in RemovePeer
- recordOutboundSend(nodeID) helper called from writeFrame's two
success paths (relay-wrapped + direct). Updates timestamp
under tm.mu.
- TunnelKeepaliveInterval = 25s tunable var (below consumer-NAT
lower bound of ~30s)
- keepaliveSweep(now) STUB that returns 0 — body lands in GREEN
Tests:
- TestKeepaliveAbsentForIdlePeer: pins stub returns 0 and peer's
socket sees zero unsolicited frames in 150ms — current bug.
GREEN flips: sweep returns 1 + peer socket receives a frame.
- TestRecordOutboundSendStampsTimestamp: pins the new tracking —
writeFrame stamps tm.lastOutboundSend on success.
TeoSlayer
pushed a commit
that referenced
this pull request
May 3, 2026
Bug fixed: tunnel-layer NAT keepalive. A goroutine spawned from
TunnelManager.Listen() now wakes every 5 s, identifies peers in
tm.peers whose lastOutboundSend is older than TunnelKeepaliveInterval
(25 s default — below consumer-NAT 30-90 s lower bound), and emits
one tiny encrypted ping per stale peer. Peers' NAT mappings stay
warm; a long-idle peer pair survives indefinitely without their
mappings expiring and silently breaking the next outbound send.
Why per-Connection keepalives don't cover this: idleSweepLoop only
sends ACK probes for ESTABLISHED Connection objects. Tunnel peer
state outlives Connections — a long-lived relationship with bursty
connection cycles can have no active Connection for hours.
Implementation (pkg/daemon/tunnel.go):
- keepaliveSweep(now): scans tm.peers under RLock, builds a slice
of stale peers (lastOutboundSend missing or older than
TunnelKeepaliveInterval, pc != nil && pc.ready), then encrypts
+ writeFrame's an empty ProtoControl/PortPing packet to each.
Returns count for tests.
- keepaliveLoop: ticker at keepaliveTickerInterval (5s default),
drives keepaliveSweep until tm.done closes.
- Listen(): spawns keepaliveLoop alongside readLoop and
rekeyRetransmitLoop.
- handleEncrypted: drops isTunnelKeepalive(pkt) packets BEFORE
recvCh send but AFTER all liveness side-effects (recordInbound
Decrypt, clearRelayOnDirect, address-learning). Application
layer never sees keepalives; the side-effects keep flowing.
Authentication invariant: keepalives go through encryptFrame, which
seals AAD = local nodeID. Peer's AEAD verifies that AAD on receipt.
An attacker can't forge keepalives without our private key, so they
can't keep stale entries warm or drive relay decisions.
Tests:
- TestKeepaliveAbsentForIdlePeer: 1-minute-idle peer → sweep
returns 1, peer socket receives 1 PILS-magic-prefixed encrypted
frame, lastOutboundSend refreshed.
- TestKeepaliveSweepSkipsRecentlyActivePeers: 1s-idle peer → sweep
returns 0 (no redundant traffic).
- TestHandleEncryptedDropsKeepaliveBeforeRecvCh: inbound keepalive
sets lastDirectRecv but does NOT appear on recvCh.
- TestRecordOutboundSendStampsTimestamp: writeFrame stamps
lastOutboundSend on success.
Race-clean across full pkg/daemon (62s).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add POLO Score and Display Total Nodes in Dashboard
Overview
This PR:
Changes
Backend (Registry Server)
PoloScorefield toDashboardNodestruct (pkg/registry/server.go)Frontend (Dashboard UI)
New POLO Score Column
Enhanced Filtering
Advanced Sorting Options
Testing Infrastructure
Created
tests/pilot_dashboard/directoryseed_dashboard.go- Core seeding logic with POLO scoresrun_dashboard_test.go- Go test for manual dashboard testingrun-dashboard.sh- Bash script for quick local deploymentseed_dashboard_main.go- Standalone seed utilityREADME.md- Usage documentationTest Data includes:
Usage
View Dashboard with Test Data
Then visit: http://127.0.0.1:8080
Run via Go Test
go test -v -run TestRunDashboardWithSeed -timeout=0 ./tests/pilot_dashboardScreenshots
The dashboard now shows:
Benefits
Breaking Changes
None. This is a backward-compatible enhancement.
Testing
Related Issues
Addresses the need for POLO score visibility in the dashboard UI.