3.4 KiB
Mesh Routing
How It Works
Every Necropolis node (operator and implants) runs a circuit relay v2 service via
libp2p.EnableRelayService(). This means any node can relay encrypted traffic between
any two peers on the network. There is no distinction between "relay nodes" and
"client nodes". Every node is both.
Routing works in layers:
-
Direct stream. The implant opens a persistent
/bc/1.0.0stream to the operator via a direct libp2p connection. This is the primary path. -
PubSub fallback. If the direct stream is unavailable, beacons and results are published to GossipSub topics. Every node relays these topics (
Topic.Relay()), so even offline implants receive messages when they reconnect (GossipSub keeps the last 10 heartbeats of history). -
DHT dead-drop. When both direct streams and PubSub are unavailable, implants poll the DHT dead-drop at
/necropolis/cmd/<id>/<nonce>every 30s. The operator publishes signed command envelopes to the DHT, and implants fetch them in nonce sequence. -
Implant-to-implant relay. When an implant cannot reach the operator directly or through pubsub, it routes through another implant. This happens automatically:
- Every implant advertises as a relay in the DHT under
necropolis/relay/<op-peerid> - Every implant discovers other relay-capable implants via DHT and connects to them
- When the direct beacon stream to the operator fails, the implant tries a circuit
relay address through each connected relay peer (
/p2p/<relay>/p2p-circuit/p2p/<op>) - libp2p handles the circuit negotiation; the implant just connects and opens a stream
- Every implant advertises as a relay in the DHT under
Every node also auto-discovers relay candidates from its connected peers. Once an
implant connects to another implant (or the operator connects to an implant), libp2p's
EnableAutoRelay finds it as a relay candidate automatically.
Discovery Flow
Operator: advertises under necropolis/<peerid> (rendezvous)
advertises under necropolis/relay/<peerid> (relay service)
finds relay peers via DHT relay namespace
Implant: finds operator via DHT rendezvous
advertises as relay under necropolis/relay/<peerid>
discovers other implants in relay namespace
connects to discovered relay peers
when direct operator connection fails, routes through relay peers
Relay Health
Implants track every discovered relay peer with its connection state, last-seen time, and fail count. Relay candidates are sorted by failCount ascending, so reliable relays are tried first. Every 30 seconds the discovery loop prunes disconnected peers. If a relay peer drops, the next beacon attempt skips it and tries the next candidate.
CLI
The list command shows which implants are acting as relays:
necropolis> list
0: user@hostname [linux/amd64] last=5s peer=12D3Koo... [relay]
1: admin@server [linux/amd64] last=2s peer=12D3Koo...
2 connected (1 relays)
Implementation
pkg/transport/node.go:RelayRendezvousString(),AdvertiseRelay(),FindRelayPeers();EnableRelayServicein node config;EnableAutoRelaywith dynamic relay candidate discoveryimplant/core/agent.go:advertiseRelayLoop(),discoverRelaysLoop(),relayRendezvous(), relay routing ingetBeaconStream()server/core/operator.go:advertiseRelayLoop(),discoverRelayPeersLoop(),IsRelayflag onImplantRecord