1 Architecture
Claude edited this page 2026-02-19 23:29:30 +00:00

Node Architecture

The node package provides the high-level P2P mesh for the Lethean network. It handles identity management, encrypted WebSocket transport, peer discovery and selection, and a controller/worker protocol for remote node operations.

Identity

NodeManager manages node identity using X25519 key pairs (via the STMF library):

  • Key generation: GenerateIdentity(name, role) creates an X25519 keypair, derives a node ID from SHA-256 of the public key (first 16 bytes as hex = 32-char ID), and persists the private key with 0600 permissions
  • Shared secret: DeriveSharedSecret(peerPubKeyBase64) performs X25519 ECDH and hashes the result with SHA-256
  • Storage: Private key at ~/.local/share/lethean-desktop/node/private.key, identity config at ~/.config/lethean-desktop/node.json

NodeIdentity is the public identity struct containing ID, Name, PublicKey (X25519 base64), CreatedAt, and Role.

Node Roles

type NodeRole string
const (
    RoleController NodeRole = "controller"
    RoleWorker     NodeRole = "worker"
    RoleDual       NodeRole = "dual"
)

Transport

Transport manages encrypted WebSocket connections with SMSG encryption:

  • Configuration: TransportConfig with ListenAddr, WSPath, TLS paths, MaxConns, MaxMessageSize (default 1MB), PingInterval, PongTimeout
  • TLS hardening: Minimum TLS 1.2, X25519/P256 curves, AEAD cipher suites only
  • Connection lifecycle: Start(), Stop(), Connect(peer), Send(peerID, msg), Broadcast(msg)
  • Message encryption: SMSG with the shared secret derived from X25519 ECDH
  • Deduplication: MessageDeduplicator with configurable TTL (default 5 minutes) prevents amplification attacks
  • Rate limiting: PeerRateLimiter implements token-bucket per peer (default 100 burst, 50/sec refill)

Handshake

The handshake uses challenge-response authentication:

  1. Initiator sends MsgHandshake with its NodeIdentity, a 32-byte random challenge, and the protocol version
  2. Responder derives shared secret, verifies protocol version compatibility, checks the peer allowlist, signs the challenge with HMAC-SHA256, and sends MsgHandshakeAck
  3. Initiator verifies the challenge response and stores the shared secret
  4. All subsequent messages are SMSG-encrypted

Peer Management

PeerRegistry manages known peers with KD-tree-based selection (via the Poindexter library):

  • Peer struct: ID, Name, PublicKey, Address, Role, PingMS, Hops, GeoKM, Score (0--100)
  • Authentication modes: PeerAuthOpen (allow all) or PeerAuthAllowlist (pre-registered peers or allowlisted public keys only)
  • Optimal selection: SelectOptimalPeer() and SelectNearestPeers(n) use a 4D KD-tree with weighted dimensions (ping, hops, geo distance, inverted score)
  • Score tracking: RecordSuccess() (+1), RecordFailure() (-5), RecordTimeout() (-3), clamped to 0--100
  • Persistence: Debounced JSON writes (5-second coalesce interval) with atomic rename pattern

Message Protocol

Message is the envelope for all P2P communication:

type Message struct {
    ID      string          // UUID
    Type    MessageType     // e.g. "handshake", "ping", "get_stats"
    From    string          // Sender node ID
    To      string          // Recipient node ID
    Payload json.RawMessage // Type-specific payload
    ReplyTo string          // For request-response correlation
}

Message Types

Category Types
Connection handshake, handshake_ack, ping, pong, disconnect
Operations get_stats, stats, start_miner, stop_miner, miner_ack
Deployment deploy, deploy_ack
Logs get_logs, logs
Error error (with code: 1000--1005)

Disconnect codes: DisconnectNormal (1000), DisconnectGoingAway (1001), DisconnectProtocolErr (1002), DisconnectTimeout (1003), DisconnectShutdown (1004).

Controller

Controller manages remote peer operations with request-response correlation:

  • GetRemoteStats(peerID) -- fetch miner statistics
  • StartRemoteMiner(peerID, minerType, profileID, config) -- start a remote miner
  • StopRemoteMiner(peerID, minerName) -- stop a remote miner
  • GetRemoteLogs(peerID, minerName, lines) -- fetch console logs (max 10000 lines)
  • GetAllStats() -- parallel stats fetch from all connected peers
  • PingPeer(peerID) -- measure RTT and update peer metrics
  • Auto-connects to peers on first request

Worker

Worker handles incoming messages on worker nodes. It implements MinerManager and ProfileManager interfaces for pluggable integration:

type MinerManager interface {
    StartMiner(minerType string, config interface{}) (MinerInstance, error)
    StopMiner(name string) error
    ListMiners() []MinerInstance
    GetMiner(name string) (MinerInstance, error)
}

The worker dispatches messages by type: ping, get_stats, start_miner, stop_miner, get_logs, deploy.

Bundle System

Bundle handles encrypted deployment packages for P2P transfer:

  • Types: BundleProfile (JSON config), BundleMiner (binary + config), BundleFull (everything)
  • Encryption: TIM/STIM format via the Borg library
  • Integrity: SHA-256 checksum verification
  • Extraction: Tar archives with path traversal protection (Zip Slip prevention), symlink rejection, and 100MB per-file size limit

Buffer Pool

bufpool.go provides sync.Pool-backed byte buffers for JSON encoding in hot paths. Buffers larger than 64KB are not returned to the pool. The MarshalJSON function uses pooled buffers and disables HTML escaping.

Logging

The logging package provides structured logging with four levels (DEBUG, INFO, WARN, ERROR), key-value fields, component scoping, and both instance and global logger APIs.

See UEPS-Protocol for the low-level TLV wire protocol that operates beneath this layer.