# 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:

1. **Direct stream**. The implant opens a persistent `/bc/1.0.0` stream to the operator
   via a direct libp2p connection. This is the primary path.

2. **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).

3. **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.

4. **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 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()`; `EnableRelayService` in node config; `EnableAutoRelay` with
  dynamic relay candidate discovery
- `implant/core/agent.go`: `advertiseRelayLoop()`, `discoverRelaysLoop()`,
  `relayRendezvous()`, relay routing in `getBeaconStream()`
- `server/core/operator.go`: `advertiseRelayLoop()`, `discoverRelayPeersLoop()`,
  `IsRelay` flag on `ImplantRecord`
