Skip to content

Protocol Overview

Raiznet defines a layered protocol for decentralized crop monitoring. This page describes the communication stack from the ESP32 sensor to the server — as it is implemented today — and where the protocol is heading.

Layers

┌──────────────────────────────────────────┐
│        Application  (HTTP JSON API)      │
├──────────────────────────────────────────┤
│   Wire format  (JSON + signed raw string)│
├──────────────────────────────────────────┤
│        Transport  (HTTP POST)            │
├──────────────────────────────────────────┤
│          Identity  (Ed25519)             │
└──────────────────────────────────────────┘

Planned additions: a canonical Protobuf encoding (ADR-001), ESP-NOW device-to-device mesh, and server-to-server replication of a signed event log (ADR-004 — see the Roadmap).

Wire format

The current wire format is JSON over HTTP, with one crucial detail: the JSON is a transport envelope, and the signed message is a separate pipe-delimited ASCII string called raw.

<device_pubkey_hex>|<seq>|<timestamp_ms>|<key_version>[|ec=<v>][|ph=<v>][|waterLevel=<v>][|tempAmbient=<v>][|humidity=<v>]

The device signs the UTF-8 bytes of this string with its Ed25519 key and ships both the hex-encoded raw and the signature inside the JSON block. The server verifies the signature against the device's registered pubkey. See Telemetry for the full grammar.

Protobuf schemas for a canonical binary encoding already exist in packages/protocol/proto/ but are not used on the wire yet — see Proto Schemas.

Transport by hop

HopProtocolStatus
ESP32 → ServerHTTP POST (/v1/telemetry, JSON)Implemented
ESP32 → ESP32 (mesh)ESP-NOW on the Wi-Fi channelPlanned
Server → ServerSigned event log replicationIn design (ADR-004)

HTTP was chosen for the ESP32 → Server hop because sensors send infrequently (the reference firmware defaults to one reading per minute; battery devices will sleep far longer). A persistent connection (WebSocket, MQTT) would be held open for nothing and would drain battery. Stateless HTTP POST is the correct model for infrequent, fire-and-forget ingest.

Packet lifecycle

What happens to a reading today:

ESP32 reads sensors
  → builds the raw string and signs it (Ed25519, device key)
  → wraps raw + signature + plain/encrypted fields in a JSON block
  → POST /v1/telemetry (batched, 1..100 blocks)

Server receives the batch, per block:
  → looks up the device in the destination database
  → verifies the signature over the raw bytes
  → resolves each field's disposition (plain / encrypted / omit)
  → inserts into raiznet_public.db or raiznet_private.db
     (INSERT OR IGNORE — duplicates are idempotent)

The replication step (appending public blocks to a signed log and syncing it between nodes) is the next protocol phase and is not implemented yet.

Identifiers

Every entity is identified by its Ed25519 public key (32 bytes), serialized as lowercase hex in JSON. There are no auto-incremented integers in the protocol.

EntityID sourceStatus
UserPubkey derived from a BIP-39 seedImplemented
ServerPubkey generated at first boot (identity.mnemonic)Implemented
DevicePubkey generated at provisioning (hardware TRNG)Implemented
Filter / CatalogPubkey of its published logDesign

Sequence numbers

Each device maintains a monotonically increasing seq counter:

  • To protect flash from wear, the reference firmware reserves seq in blocks of 100: it persists only the start of the next block to NVS. After a reboot the device resumes from the next reserved block — small gaps in seq are normal and expected.
  • The device keeps recent readings in a RAM ring buffer and re-sends everything not yet confirmed with an HTTP 200. The server deduplicates by (device_pubkey, seq), so retransmission is always safe.
  • The server does not enforce monotonicity — rejecting older seqs would break recovery after a reconnection.

Readings that age out of the device buffer before syncing are lost from the network — unless the owner pulls them directly from the device via local HTTP, BLE, or serial.