Docs · Drivers

Build a driver

hop-core has no bearer system of its own, it only moves bytes. A drivergives it bearers: it implements the HopContract protocol for one transport or platform and registers it with the node. Everything else, sealing, addressing, routing, fragmentation, retransmit, dedup, delivery, stays in the core.

What the core does vs. what your driver does

hop-core owns

  • Sealing & addressing (keys, not IPs)
  • Routing & multipath epidemic forward
  • Fragmentation & reassembly
  • Retransmit, dedup, delivery acks, custody

Your driver owns

  • Discovery & connection lifecycle
  • Framing & moving raw bytes
  • Background operation & power for the platform
  • Transport authentication & link cost

The Bearer protocol

Under HopContract, a bearer is defined by lifecycle control (start and stop), sending bytes over identified links (send), and optional teardown hooks (closeand authenticated).

// HopContract: universal Bearer protocol in Swift
public protocol Bearer: AnyObject {
    var sink: LinkSink? { get set }
    var transportName: String { get }   // e.g. "BT", "LAN", "Relay"

    // Lifecycle: start radio / advertising; stop and release resources
    func start()
    func stop()

    // Send frame bytes over an active link
    func send(_ bytes: Data, on link: LinkId)

    // Optional link lifecycle hooks
    func close(_ link: LinkId)
    func authenticated(_ link: LinkId)
}

Reporting link events with LinkSink

Your transport drives the core through LinkSink. Surface links as they connect, feed inbound byte buffers directly in, and signal disconnection or authentication updates.

// Report link events and inbound bytes back to the node via LinkSink:
sink?.linkUp(linkId, role: .initiator, peerId: peerKey)  // link formed with peer
sink?.linkBytes(linkId, bytes: data)                    // inbound frame arrived
sink?.linkDown(linkId)                                  // link disconnected
sink?.linkAuthenticated(linkId, peerId: peerKey)        // authenticated session ready
sink?.linkCost(linkId, cost: 10)                        // route metric / cost

Multiplexing bearers with BearerManager

Add one bearer or several. BearerManager translates local link identifiers from each bearer into a single process-wide LinkId space and handles priority across BLE, Wi-Fi/LAN, and relays.

// BearerManager multiplexes multiple bearers into a single id space
let manager = BearerManager()
manager.register(CoreBluetoothBearer())
manager.register(LanBearer())
manager.sink = node
manager.start()

Android Kotlin implementation

On Android, the driver implements the exact same Bearer contract in Kotlin via JNA over the C ABI. The method signatures and lifecycle expectations mirror the Swift protocol:

// HopContract: universal Bearer interface in Kotlin (sh.hop.Bearer)
interface Bearer {
    var sink: LinkSink?
    val transportName: String

    fun start()
    fun stop()
    fun send(bytes: ByteArray, link: LinkId)
    fun close(link: LinkId) {}
    fun authenticated(link: LinkId) {}
}

The universal C ABI floor

The shared foundation beneath all platform SDKs is the C ABI (sdk/hop.h). Native and embedded drivers (Linux/server, ESP32) bind this interface directly without intermediate wrapper runtimes.

Checklist

  • Implement start(), stop(), and send().
  • On connection established, call sink.linkUp().
  • On every inbound frame, call sink.linkBytes(), never reassemble yourself.
  • On disconnect, call sink.linkDown().
  • Register bearer instances with BearerManager for unified routing.

Illustrative, the shape of the interface, not the exact signatures; the real contract ships with the SDK in HopContract. Bringing an unusual transport or platform?Tell us.

Related

Any transportBLE bearerESP32 driverInterface reference