Complete rebranding of all components: - Core miner: xmrig -> miner (binary, version.h, CMakeLists.txt) - Proxy: xmrig-proxy -> miner-proxy - CUDA plugin: xmrig-cuda -> miner-cuda - Heatmap: xmrig-nonces-heatmap -> miner-nonces-heatmap - Go CLI wrapper: miner-cli -> miner-ctrl Vendored XMRig ecosystem into miner/ directory: - miner/core - XMRig CPU/GPU miner - miner/proxy - Stratum proxy - miner/cuda - NVIDIA CUDA plugin - miner/heatmap - Nonce visualization tool - miner/config - Configuration UI - miner/deps - Pre-built dependencies Updated dev fee to use project wallet with opt-out (kMinimumDonateLevel=0) Updated branding to Lethean (domain, copyright, version 0.1.0) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
566 lines
20 KiB
Markdown
566 lines
20 KiB
Markdown
Multi-Node P2P Mining Management Plan
|
|
|
|
Overview
|
|
|
|
Add secure peer-to-peer communication between Mining CLI instances, enabling control of remote mining rigs without commercial mining OS
|
|
dependencies.
|
|
|
|
Libraries
|
|
|
|
- Borg (github.com/Snider/Borg) - Encryption & packaging toolkit
|
|
- pkg/smsg - SMSG encrypted messaging (ChaCha20-Poly1305)
|
|
- pkg/stmf - X25519 keypairs for node identity
|
|
- pkg/tim - Terminal Isolation Matrix for deployment bundles
|
|
- Poindexter (github.com/Snider/Poindexter) - KD-tree peer selection
|
|
- Multi-dimensional ranking by ping/hops/geo/score
|
|
- Optimal peer routing
|
|
|
|
---
|
|
Architecture Overview
|
|
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ CONTROLLER NODE │
|
|
│ ┌─────────────┐ ┌──────────────┐ ┌────────────────────────┐ │
|
|
│ │ NodeManager │ │ PeerRegistry │ │ Poindexter KD-Tree │ │
|
|
│ │ (identity) │ │ (known peers)│ │ (peer selection) │ │
|
|
│ └──────┬──────┘ └──────┬───────┘ └────────────────────────┘ │
|
|
│ │ │ │
|
|
│ ┌──────┴────────────────┴───────────────────────────────────┐ │
|
|
│ │ MessageRouter │ │
|
|
│ │ SMSG encrypt/decrypt | Command dispatch | Response │ │
|
|
│ └──────────────────────────┬────────────────────────────────┘ │
|
|
│ │ TCP/TLS │
|
|
└─────────────────────────────┼───────────────────────────────────┘
|
|
│
|
|
┌─────────────────────┼─────────────────────┐
|
|
│ │ │
|
|
▼ ▼ ▼
|
|
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
|
|
│ WORKER NODE │ │ WORKER NODE │ │ WORKER NODE │
|
|
│ rig-alpha │ │ rig-beta │ │ rig-gamma │
|
|
│ ────────────│ │ ────────────│ │ ────────────│
|
|
│ XMRig │ │ TT-Miner │ │ XMRig │
|
|
│ 12.5 kH/s │ │ 45.2 MH/s │ │ 11.8 kH/s │
|
|
└───────────────┘ └───────────────┘ └───────────────┘
|
|
|
|
---
|
|
Phase 1: Node Identity System
|
|
|
|
1.1 Data Structures
|
|
|
|
File: pkg/node/identity.go
|
|
type NodeIdentity struct {
|
|
ID string `json:"id"` // Derived from public key (first 16 bytes hex)
|
|
Name string `json:"name"` // Human-friendly name
|
|
PublicKey string `json:"publicKey"` // X25519 base64
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
Role NodeRole `json:"role"` // controller | worker
|
|
}
|
|
|
|
type NodeRole string
|
|
const (
|
|
RoleController NodeRole = "controller" // Manages remote workers
|
|
RoleWorker NodeRole = "worker" // Receives commands, runs miners
|
|
RoleDual NodeRole = "dual" // Both controller AND worker (default)
|
|
)
|
|
|
|
// Dual mode: Node can control remote peers AND run local miners
|
|
// - Can receive commands from other controllers
|
|
// - Can send commands to worker peers
|
|
// - Runs its own miners locally
|
|
|
|
File: pkg/node/manager.go
|
|
type NodeManager struct {
|
|
identity *NodeIdentity
|
|
privateKey []byte // Never serialized to JSON
|
|
keyPath string // ~/.local/share/lethean-desktop/node/private.key
|
|
configPath string // ~/.config/lethean-desktop/node.json
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
// Key methods:
|
|
func NewNodeManager() (*NodeManager, error) // Load or generate identity
|
|
func (n *NodeManager) GenerateIdentity(name string, role NodeRole) error
|
|
func (n *NodeManager) GetIdentity() *NodeIdentity
|
|
func (n *NodeManager) Sign(data []byte) ([]byte, error)
|
|
func (n *NodeManager) DeriveSharedSecret(peerPubKey []byte) ([]byte, error)
|
|
|
|
1.2 Storage Layout
|
|
|
|
~/.config/lethean-desktop/
|
|
├── node.json # Public identity (ID, name, pubkey, role)
|
|
└── peers.json # Registered peers
|
|
|
|
~/.local/share/lethean-desktop/node/
|
|
└── private.key # X25519 private key (0600 permissions)
|
|
|
|
---
|
|
Phase 2: Peer Registry
|
|
|
|
2.1 Data Structures
|
|
|
|
File: pkg/node/peer.go
|
|
type Peer struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
PublicKey string `json:"publicKey"`
|
|
Address string `json:"address"` // host:port
|
|
Role NodeRole `json:"role"`
|
|
AddedAt time.Time `json:"addedAt"`
|
|
LastSeen time.Time `json:"lastSeen"`
|
|
|
|
// Poindexter metrics (updated dynamically)
|
|
PingMS float64 `json:"pingMs"`
|
|
Hops int `json:"hops"`
|
|
GeoKM float64 `json:"geoKm"`
|
|
Score float64 `json:"score"` // Reliability score 0-100
|
|
}
|
|
|
|
type PeerRegistry struct {
|
|
peers map[string]*Peer
|
|
kdTree *poindexter.KDTree[*Peer] // For optimal selection
|
|
path string
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
2.2 Key Methods
|
|
|
|
func (r *PeerRegistry) AddPeer(peer *Peer) error
|
|
func (r *PeerRegistry) RemovePeer(id string) error
|
|
func (r *PeerRegistry) GetPeer(id string) *Peer
|
|
func (r *PeerRegistry) ListPeers() []*Peer
|
|
func (r *PeerRegistry) UpdateMetrics(id string, ping, geo float64, hops int)
|
|
func (r *PeerRegistry) SelectOptimalPeer() *Peer // Poindexter query
|
|
func (r *PeerRegistry) SelectNearestPeers(n int) []*Peer // k-NN query
|
|
|
|
---
|
|
Phase 3: Message Protocol
|
|
|
|
3.1 Message Types
|
|
|
|
File: pkg/node/message.go
|
|
type MessageType string
|
|
const (
|
|
MsgHandshake MessageType = "handshake" // Initial key exchange
|
|
MsgPing MessageType = "ping" // Health check
|
|
MsgPong MessageType = "pong"
|
|
MsgGetStats MessageType = "get_stats" // Request miner stats
|
|
MsgStats MessageType = "stats" // Stats response
|
|
MsgStartMiner MessageType = "start_miner" // Start mining command
|
|
MsgStopMiner MessageType = "stop_miner" // Stop mining command
|
|
MsgDeploy MessageType = "deploy" // Deploy config/bundle
|
|
MsgDeployAck MessageType = "deploy_ack"
|
|
MsgGetLogs MessageType = "get_logs" // Request console logs
|
|
MsgLogs MessageType = "logs" // Logs response
|
|
MsgError MessageType = "error"
|
|
)
|
|
|
|
type Message struct {
|
|
ID string `json:"id"` // UUID
|
|
Type MessageType `json:"type"`
|
|
From string `json:"from"` // Sender node ID
|
|
To string `json:"to"` // Recipient node ID
|
|
Timestamp time.Time `json:"ts"`
|
|
Payload json.RawMessage `json:"payload"`
|
|
Signature []byte `json:"sig"` // Ed25519 signature
|
|
}
|
|
|
|
3.2 Payload Types
|
|
|
|
// Handshake
|
|
type HandshakePayload struct {
|
|
Identity NodeIdentity `json:"identity"`
|
|
Challenge []byte `json:"challenge"` // Random bytes for auth
|
|
}
|
|
|
|
// Start Miner
|
|
type StartMinerPayload struct {
|
|
ProfileID string `json:"profileId"`
|
|
Config *Config `json:"config,omitempty"` // Override profile config
|
|
}
|
|
|
|
// Stats Response
|
|
type StatsPayload struct {
|
|
Miners []MinerStats `json:"miners"`
|
|
}
|
|
|
|
// Deploy (STIM bundle)
|
|
type DeployPayload struct {
|
|
BundleType string `json:"type"` // "profile" | "miner" | "full"
|
|
Data []byte `json:"data"` // STIM-encrypted bundle
|
|
Checksum string `json:"checksum"`
|
|
}
|
|
|
|
---
|
|
Phase 4: Transport Layer (WebSocket + SMSG)
|
|
|
|
4.1 Connection Manager
|
|
|
|
File: pkg/node/transport.go
|
|
type TransportConfig struct {
|
|
ListenAddr string // ":9091" default
|
|
WSPath string // "/ws" - WebSocket endpoint path
|
|
TLSCertPath string // Optional TLS for wss://
|
|
TLSKeyPath string
|
|
MaxConns int
|
|
PingInterval time.Duration // WebSocket keepalive
|
|
PongTimeout time.Duration
|
|
}
|
|
|
|
type Transport struct {
|
|
config TransportConfig
|
|
server *http.Server
|
|
upgrader websocket.Upgrader // gorilla/websocket
|
|
conns map[string]*PeerConnection
|
|
node *NodeManager
|
|
registry *PeerRegistry
|
|
handler MessageHandler
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
type PeerConnection struct {
|
|
Peer *Peer
|
|
Conn *websocket.Conn
|
|
SharedSecret []byte // Derived via X25519 ECDH, used for SMSG
|
|
LastActivity time.Time
|
|
writeMu sync.Mutex // Serialize WebSocket writes
|
|
}
|
|
|
|
4.2 WebSocket Protocol
|
|
|
|
Client connects: ws://host:9091/ws
|
|
wss://host:9091/ws (with TLS)
|
|
|
|
Each WebSocket message is:
|
|
┌────────────────────────────────────────────────────┐
|
|
│ Binary frame containing SMSG-encrypted payload │
|
|
│ (JSON Message struct inside after decryption) │
|
|
└────────────────────────────────────────────────────┘
|
|
|
|
Benefits of WebSocket over raw TCP:
|
|
- Better firewall/NAT traversal
|
|
- Built-in framing (no need for length prefixes)
|
|
- HTTP upgrade allows future reverse-proxy support
|
|
- Easy browser integration for web dashboard
|
|
|
|
4.3 Key Methods
|
|
|
|
func (t *Transport) Start() error // Start WS server
|
|
func (t *Transport) Stop() error // Graceful shutdown
|
|
func (t *Transport) Connect(peer *Peer) (*PeerConnection, error) // Dial peer
|
|
func (t *Transport) Send(peerID string, msg *Message) error // SMSG encrypt + send
|
|
func (t *Transport) Broadcast(msg *Message) error // Send to all peers
|
|
func (t *Transport) OnMessage(handler MessageHandler) // Register handler
|
|
|
|
// WebSocket handlers
|
|
func (t *Transport) handleWSUpgrade(w http.ResponseWriter, r *http.Request)
|
|
func (t *Transport) handleConnection(conn *websocket.Conn)
|
|
func (t *Transport) readLoop(pc *PeerConnection)
|
|
func (t *Transport) keepalive(pc *PeerConnection) // Ping/pong
|
|
|
|
---
|
|
Phase 5: Command Handlers
|
|
|
|
5.1 Controller Commands
|
|
|
|
File: pkg/node/controller.go
|
|
type Controller struct {
|
|
node *NodeManager
|
|
peers *PeerRegistry
|
|
transport *Transport
|
|
manager *Manager // Local miner manager
|
|
}
|
|
|
|
// Remote operations
|
|
func (c *Controller) StartRemoteMiner(peerID, profileID string) error
|
|
func (c *Controller) StopRemoteMiner(peerID, minerName string) error
|
|
func (c *Controller) GetRemoteStats(peerID string) (*StatsPayload, error)
|
|
func (c *Controller) GetRemoteLogs(peerID, minerName string, lines int) ([]string, error)
|
|
func (c *Controller) DeployProfile(peerID string, profile *MiningProfile) error
|
|
func (c *Controller) DeployMinerBundle(peerID string, minerType string) error
|
|
|
|
// Aggregation
|
|
func (c *Controller) GetAllStats() map[string]*StatsPayload
|
|
func (c *Controller) GetTotalHashrate() float64
|
|
|
|
5.2 Worker Handlers
|
|
|
|
File: pkg/node/worker.go
|
|
type Worker struct {
|
|
node *NodeManager
|
|
transport *Transport
|
|
manager *Manager
|
|
}
|
|
|
|
func (w *Worker) HandleMessage(msg *Message) (*Message, error)
|
|
func (w *Worker) handleGetStats(msg *Message) (*Message, error)
|
|
func (w *Worker) handleStartMiner(msg *Message) (*Message, error)
|
|
func (w *Worker) handleStopMiner(msg *Message) (*Message, error)
|
|
func (w *Worker) handleDeploy(msg *Message) (*Message, error)
|
|
func (w *Worker) handleGetLogs(msg *Message) (*Message, error)
|
|
|
|
---
|
|
Phase 6: CLI Commands
|
|
|
|
6.1 Node Management
|
|
|
|
File: cmd/mining/cmd/node.go
|
|
// miner-ctrl node init --name "rig-alpha" --role worker
|
|
// miner-ctrl node init --name "control-center" --role controller
|
|
var nodeInitCmd = &cobra.Command{
|
|
Use: "init",
|
|
Short: "Initialize node identity",
|
|
}
|
|
|
|
// miner-ctrl node info
|
|
var nodeInfoCmd = &cobra.Command{
|
|
Use: "info",
|
|
Short: "Show node identity and status",
|
|
}
|
|
|
|
// miner-ctrl node serve --listen :9091
|
|
var nodeServeCmd = &cobra.Command{
|
|
Use: "serve",
|
|
Short: "Start P2P server for remote connections",
|
|
}
|
|
|
|
6.2 Peer Management
|
|
|
|
File: cmd/mining/cmd/peer.go
|
|
// miner-ctrl peer add --address 192.168.1.100:9091 --name "rig-alpha"
|
|
var peerAddCmd = &cobra.Command{
|
|
Use: "add",
|
|
Short: "Add a peer node (initiates handshake)",
|
|
}
|
|
|
|
// miner-ctrl peer list
|
|
var peerListCmd = &cobra.Command{
|
|
Use: "list",
|
|
Short: "List registered peers with status",
|
|
}
|
|
|
|
// miner-ctrl peer remove <peer-id>
|
|
var peerRemoveCmd = &cobra.Command{
|
|
Use: "remove",
|
|
Short: "Remove a peer from registry",
|
|
}
|
|
|
|
// miner-ctrl peer ping <peer-id>
|
|
var peerPingCmd = &cobra.Command{
|
|
Use: "ping",
|
|
Short: "Ping a peer and update metrics",
|
|
}
|
|
|
|
6.3 Remote Operations
|
|
|
|
File: cmd/mining/cmd/remote.go
|
|
// miner-ctrl remote status [peer-id]
|
|
// Shows stats from all peers or specific peer
|
|
var remoteStatusCmd = &cobra.Command{
|
|
Use: "status",
|
|
Short: "Get mining status from remote peers",
|
|
}
|
|
|
|
// miner-ctrl remote start <peer-id> --profile <profile-id>
|
|
var remoteStartCmd = &cobra.Command{
|
|
Use: "start",
|
|
Short: "Start miner on remote peer",
|
|
}
|
|
|
|
// miner-ctrl remote stop <peer-id> [miner-name]
|
|
var remoteStopCmd = &cobra.Command{
|
|
Use: "stop",
|
|
Short: "Stop miner on remote peer",
|
|
}
|
|
|
|
// miner-ctrl remote deploy <peer-id> --profile <profile-id>
|
|
// miner-ctrl remote deploy <peer-id> --miner xmrig
|
|
var remoteDeployCmd = &cobra.Command{
|
|
Use: "deploy",
|
|
Short: "Deploy config or miner bundle to remote peer",
|
|
}
|
|
|
|
// miner-ctrl remote logs <peer-id> <miner-name> --lines 100
|
|
var remoteLogsCmd = &cobra.Command{
|
|
Use: "logs",
|
|
Short: "Get console logs from remote miner",
|
|
}
|
|
|
|
---
|
|
Phase 7: REST API Extensions
|
|
|
|
7.1 New Endpoints
|
|
|
|
File: pkg/mining/service.go (additions)
|
|
// Node endpoints
|
|
nodeGroup := router.Group(s.namespace + "/node")
|
|
nodeGroup.GET("/info", s.handleNodeInfo)
|
|
nodeGroup.POST("/init", s.handleNodeInit)
|
|
|
|
// Peer endpoints
|
|
peerGroup := router.Group(s.namespace + "/peers")
|
|
peerGroup.GET("", s.handleListPeers)
|
|
peerGroup.POST("", s.handleAddPeer)
|
|
peerGroup.DELETE("/:id", s.handleRemovePeer)
|
|
peerGroup.POST("/:id/ping", s.handlePingPeer)
|
|
|
|
// Remote operations
|
|
remoteGroup := router.Group(s.namespace + "/remote")
|
|
remoteGroup.GET("/stats", s.handleRemoteStats) // All peers
|
|
remoteGroup.GET("/:peerId/stats", s.handlePeerStats) // Single peer
|
|
remoteGroup.POST("/:peerId/start", s.handleRemoteStart)
|
|
remoteGroup.POST("/:peerId/stop", s.handleRemoteStop)
|
|
remoteGroup.POST("/:peerId/deploy", s.handleRemoteDeploy)
|
|
remoteGroup.GET("/:peerId/logs/:miner", s.handleRemoteLogs)
|
|
|
|
---
|
|
Phase 8: Deployment Bundles (TIM/STIM)
|
|
|
|
8.1 Bundle Creation
|
|
|
|
File: pkg/node/bundle.go
|
|
type BundleType string
|
|
const (
|
|
BundleProfile BundleType = "profile" // Just config
|
|
BundleMiner BundleType = "miner" // Miner binary + config
|
|
BundleFull BundleType = "full" // Everything
|
|
)
|
|
|
|
func CreateProfileBundle(profile *MiningProfile) (*tim.TerminalIsolationMatrix, error)
|
|
func CreateMinerBundle(minerType string, profile *MiningProfile) (*tim.TerminalIsolationMatrix, error)
|
|
|
|
// Encrypt for transport
|
|
func EncryptBundle(t *tim.TerminalIsolationMatrix, password string) ([]byte, error) {
|
|
return t.ToSigil(password) // Returns STIM-encrypted bytes
|
|
}
|
|
|
|
// Decrypt on receipt
|
|
func DecryptBundle(data []byte, password string) (*tim.TerminalIsolationMatrix, error) {
|
|
return tim.FromSigil(data, password)
|
|
}
|
|
|
|
---
|
|
Phase 9: UI Integration
|
|
|
|
9.1 New UI Pages
|
|
|
|
File: ui/src/app/pages/nodes/nodes.component.ts
|
|
- Show local node identity
|
|
- List connected peers with status
|
|
- Actions: Add peer, remove peer, ping
|
|
- View aggregated stats from all nodes
|
|
|
|
File: ui/src/app/pages/fleet/fleet.component.ts (or extend Workers)
|
|
- Fleet-wide view of all miners across all nodes
|
|
- Group by node or show flat list
|
|
- Remote start/stop actions
|
|
- Deploy profiles to remote nodes
|
|
|
|
9.2 Sidebar Addition
|
|
|
|
Add "Nodes" or "Fleet" navigation item to sidebar between Workers and Graphs.
|
|
|
|
---
|
|
Implementation Order
|
|
|
|
Sprint 1: Node Identity & Peer Registry
|
|
|
|
1. Create pkg/node/identity.go - NodeIdentity, NodeManager
|
|
2. Create pkg/node/peer.go - Peer, PeerRegistry
|
|
3. Add STMF dependency (github.com/Snider/Borg)
|
|
4. Implement key generation and storage
|
|
5. Add node init and node info CLI commands
|
|
|
|
Sprint 2: Transport Layer
|
|
|
|
1. Create pkg/node/message.go - Message types and payloads
|
|
2. Create pkg/node/transport.go - TCP transport with SMSG encryption
|
|
3. Implement handshake protocol
|
|
4. Add node serve CLI command
|
|
5. Add peer add and peer list CLI commands
|
|
|
|
Sprint 3: Remote Operations
|
|
|
|
1. Create pkg/node/controller.go - Controller operations
|
|
2. Create pkg/node/worker.go - Worker message handlers
|
|
3. Integrate with existing Manager for local operations
|
|
4. Add remote status/start/stop/logs CLI commands
|
|
|
|
Sprint 4: Poindexter Integration & Deployment
|
|
|
|
1. Add Poindexter dependency
|
|
2. Integrate KD-tree peer selection
|
|
3. Create pkg/node/bundle.go - TIM/STIM bundles
|
|
4. Add remote deploy CLI command
|
|
5. Add peer metrics (ping, geo, score)
|
|
|
|
Sprint 5: REST API & UI
|
|
|
|
1. Add node/peer REST endpoints to service.go
|
|
2. Add remote operation REST endpoints
|
|
3. Create Nodes UI page
|
|
4. Update Workers page for fleet view
|
|
5. Add node status to stats panel
|
|
|
|
---
|
|
Critical Files
|
|
|
|
New Files
|
|
|
|
pkg/node/
|
|
├── identity.go # NodeIdentity, NodeManager
|
|
├── peer.go # Peer, PeerRegistry
|
|
├── message.go # Message types and protocol
|
|
├── transport.go # TCP transport with SMSG
|
|
├── controller.go # Controller operations
|
|
├── worker.go # Worker message handlers
|
|
└── bundle.go # TIM/STIM deployment bundles
|
|
|
|
cmd/mining/cmd/
|
|
├── node.go # node init/info/serve commands
|
|
├── peer.go # peer add/list/remove/ping commands
|
|
└── remote.go # remote status/start/stop/deploy/logs commands
|
|
|
|
ui/src/app/pages/nodes/
|
|
└── nodes.component.ts # Node management UI
|
|
|
|
Modified Files
|
|
|
|
go.mod # Add Borg, Poindexter deps
|
|
pkg/mining/service.go # Add node/peer/remote REST endpoints
|
|
pkg/mining/manager.go # Integrate with node transport
|
|
cmd/mining/cmd/root.go # Register node/peer/remote commands
|
|
ui/src/app/components/sidebar/sidebar.component.ts # Add Nodes nav
|
|
|
|
---
|
|
Security Considerations
|
|
|
|
1. Private key storage: 0600 permissions, never in JSON
|
|
2. Shared secrets: Derived per-peer via X25519 ECDH, used for SMSG
|
|
3. Message signing: All messages signed with sender's private key
|
|
4. TLS option: Support TLS for transport (optional, SMSG provides encryption)
|
|
5. Peer verification: Handshake verifies identity before accepting commands
|
|
6. Command authorization: Workers only accept commands from registered controllers
|
|
|
|
---
|
|
Design Decisions Summary
|
|
|
|
| Decision | Choice | Rationale |
|
|
|----------------|--------------------------|------------------------------------------------------------------------|
|
|
| Discovery | Manual only | Simpler, more secure - explicit peer registration |
|
|
| Transport | WebSocket + SMSG | Better firewall traversal, built-in framing, browser-friendly |
|
|
| Node Mode | Dual (default) | Maximum flexibility - each node controls remotes AND runs local miners |
|
|
| Encryption | SMSG (ChaCha20-Poly1305) | Uses Borg library, password-derived keys via ECDH |
|
|
| Identity | X25519 keypairs (STMF) | Standard, fast, 32-byte keys |
|
|
| Peer Selection | Poindexter KD-tree | Multi-factor optimization (ping, hops, geo, score) |
|
|
| Deployment | TIM/STIM bundles | Encrypted container bundles for miner+config deployment |
|
|
|
|
---
|
|
Dependencies to Add
|
|
|
|
// go.mod additions
|
|
require (
|
|
github.com/Snider/Borg v0.x.x // SMSG, STMF, TIM encryption
|
|
github.com/Snider/Poindexter v0.x.x // KD-tree peer selection
|
|
github.com/gorilla/websocket v1.5.x // WebSocket transport
|
|
)
|