Skip to main content

UDP Transport

Syncs over LAN UDP without any broker or infrastructure. Suitable for devices on the same subnet (WiFi LAN, ad-hoc hotspot).

Peer discovery

On start, each node binds to the configured port and begins broadcasting a compact presence frame to 255.255.255.255 every 10 seconds. Any node that receives a presence frame records the sender's address and considers it a live peer (TTL 90s).

Frame format

FrameByte 0Bytes 1–16Bytes 17–18
Presence0x01NodeID (16 bytes)Port (big-endian uint16)
Data0x02NodeID (16 bytes)Payload

Go

node.AddTransportUDP(7946) // default port; bind and broadcast

Android

node.addTransportUDP() // default port 7946
node.addTransportUDP(port = 9000)

Seeds (explicit peers)

LAN broadcast does not loop back on loopback interfaces. For localhost testing or environments where broadcast is filtered, specify seed addresses explicitly:

node.AddTransportUDP(17946, "127.0.0.1:17947")

The heartbeat loop unicasts a presence frame to each seed address in addition to the broadcast, ensuring discovery even when broadcast is unavailable.

Blob transfer (TCP sidecar)

When a blob store is configured (node.SetBlobStore / node.setBlobPolicy), the UDP transport opens a TCP listener on port + 1 (default 7947) alongside the UDP socket. This sidecar is used exclusively for blob byte transfer — CRDT changesets continue over UDP.

Blob transfer is triggered automatically after each changeset merge: if the received changesets include new ara_blobs metadata rows that the local node does not yet have, the SDK dials the owner's TCP sidecar and streams the bytes, resuming from any partial download.

The TCP wire protocol uses a 53-byte binary header:

BytesField
0–3Magic 0x41524142 ("ARAB")
4Op: 0x01 = send, 0x02 = fetch-request, 0x03 = fetch-response
5–36Blob id (32 bytes, raw SHA-256)
37–44Offset (uint64 big-endian) — resume position
45–52Size (uint64 big-endian) — byte count following the header

Permissions (Android)

<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />

Some Android devices suppress multicast/broadcast by default. Acquire a MulticastLock before starting UDP:

val wifiManager = context.getSystemService(WifiManager::class.java)
val lock = wifiManager.createMulticastLock("ara-udp")
lock.acquire()
// ... start node ...
// lock.release() when done