IFM Protocol Specification
Version
v0.1 Draft — This specification is a work in progress.
Overview
IFM is a packet-based broadcast protocol over a decentralized mesh network. Unlike streaming protocols, everything in IFM is represented as discrete packets with metadata. Applications needing continuous media (voice, video) send packets at regular intervals.
Key Properties
- Binary format — Compact, fast serialization
- Signed packets — Every packet authenticated via Ed25519
- TTL-limited — Packets expire after 16 hops
- Deduplicated — BLAKE3 packet IDs prevent replay
- Transport-agnostic — Works over QUIC, WebRTC, TCP
Companion document: Station Discovery & Relay Protocol — how stations are announced/found and relays are scored/selected without a central server.
Wire Format
All packets use a binary encoding (bincode-compatible). The on-wire format:
Field Definitions
| Field | Type | Size | Description |
|---|---|---|---|
version | u8 | 1 byte | Protocol version (current: 1) |
frequency | [u8; 32] | 32 bytes | BLAKE3 hash of canonical frequency identifier |
packet_id | [u8; 32] | 32 bytes | BLAKE3 hash of entire packet (minus signature) |
sender | [u8; 32] | 32 bytes | Sender's Ed25519 public key |
timestamp | u64 | 8 bytes | Unix milliseconds since epoch |
ttl | u8 | 1 byte | Time-to-live in hops (default: 16) |
sequence | u64 | 8 bytes | Monotonically increasing per-sender sequence |
payload_type | u8 | 1 byte | See Payload Types table |
payload_length | u32 | 4 bytes | Length of payload in bytes |
payload | Vec<u8> | Variable | Encrypted/compressed payload data |
signature | [u8; 64] | 64 bytes | Ed25519 signature over all above fields |
Payload Types
| Value | Constant | Description |
|---|---|---|
0x00 | TEXT | UTF-8 text message |
0x01 | VOICE | Opus-encoded audio frame (20ms) |
0x02 | VIDEO | Video frame (codec indicated in payload) |
0x03 | IMAGE | Image data (format in payload header) |
0x04 | FILE | File chunk (see File Transfer) |
0x05 | JSON | Structured JSON data |
0x06 | PING | Keepalive / latency probe |
0x07 | PRESENCE | Presence update (join/leave/status) |
0x08 | CONTROL | Protocol control messages |
0x09 | PLUGIN | Plugin-specific payload |
Packet Types (Control Messages)
Control messages use payload_type = CONTROL (0x08) with a JSON payload:
{
"type": "HELLO | WELCOME | JOIN | LEAVE | ANNOUNCE | PING | PONG",
"data": { ... }
}HELLO
Sent when tuning to a frequency to announce presence.
{
"type": "HELLO",
"data": {
"node_id": "12D3KooW...",
"display_name": "optional name",
"capabilities": ["voice", "chat", "file"],
"frequencies": ["91.700", "chat.general"]
}
}WELCOME
Response to HELLO from peers already on the frequency.
{
"type": "WELCOME",
"data": {
"node_id": "12D3KooW...",
"peer_count": 42,
"frequency_metadata": {
"name": "General Chat",
"description": "Main discussion channel",
"language": "en",
"encryption": "none"
}
}
}JOIN
Explicit join request (for protected/hidden frequencies).
{
"type": "JOIN",
"data": {
"frequency": "private.team",
"key_proof": "optional cryptographic proof of key possession"
}
}LEAVE
Graceful departure from a frequency.
{
"type": "LEAVE",
"data": {
"frequency": "91.700",
"reason": "user_left | timeout | error"
}
}ANNOUNCE
Frequency metadata broadcast (periodic or on change).
{
"type": "ANNOUNCE",
"data": {
"frequency": "91.700",
"metadata": {
"name": "Music Lounge",
"description": "Lo-fi beats",
"listener_count": 128,
"capabilities": ["voice", "music"]
}
}
}PING / PONG
Latency measurement and keepalive.
{ "type": "PING", "data": { "timestamp": 1699999999999, "nonce": "abc123" } }
{ "type": "PONG", "data": { "original_timestamp": 1699999999999, "nonce": "abc123" } }Packet ID Generation
packet_id = BLAKE3(
version ||
frequency ||
sender ||
timestamp ||
ttl ||
sequence ||
payload_type ||
payload_length ||
payload
)Properties:
- Deterministic — same packet always produces same ID
- Collision-resistant — 256-bit output
- Enables duplicate detection without storing full packets
- Used for deduplication cache keys
Packet Lifetime (TTL)
- Initial TTL: 16 hops
- Decrement: Each relay decrements by 1
- Expiry: Packet discarded when TTL reaches 0
- Purpose: Prevents infinite circulation, bounds network diameter
Broadcast Mechanism (GossipSub)
IFM uses libp2p GossipSub for mesh broadcast:
1. Node creates packet, signs it
2. Publishes to GossipSub topic = frequency hash
3. GossipSub delivers to mesh peers (fanout=6)
4. Each peer:
a. Verifies signature (Ed25519)
b. Checks TTL > 0
c. Checks packet_id not in dedup cache
d. Adds to local cache
e. Forwards to its mesh peers (TTL - 1)
5. Recipients decode and deliver to applicationGossipSub Parameters
| Parameter | Value | Description |
|---|---|---|
fanout | 6 | Number of peers to forward to |
mesh_n | 6 | Target mesh degree |
mesh_n_low | 4 | Minimum mesh degree |
mesh_n_high | 12 | Maximum mesh degree |
gossip_factor | 0.25 | Percentage of mesh to gossip to |
Frequency Resolution
Human Frequency → Topic ID
Frequency Types & Canonical Prefixes
| Type | Prefix | Example | Encryption |
|---|---|---|---|
| Public | ifm://public/ | ifm://public/91.700 | None |
| Protected | ifm://protected/ | ifm://protected/team.alpha | ChaCha20-Poly1305 |
| Hidden | ifm://hidden/ | ifm://hidden/game.room.42 | ChaCha20-Poly1305 |
Serialization
Binary Encoding (bincode)
- Little-endian
- Fixed-size integers
- Length-prefixed variable data
- No schema in band — schema defined by protocol version
Example: Minimal TEXT Packet (hex)
01 # version = 1
abcdef... (32 bytes) # frequency hash
123456... (32 bytes) # packet_id
fedcba... (32 bytes) # sender public key
00 00 00 00 00 65 4a 8b # timestamp (little-endian u64)
10 # ttl = 16
00 00 00 00 00 00 00 01 # sequence = 1
00 # payload_type = TEXT (0)
05 00 00 00 # payload_length = 5
48 65 6c 6c 6f # payload = "Hello"
9a 8b 7c... (64 bytes) # Ed25519 signatureVersioning
versionfield in packet header- Current version:
1 - Breaking changes increment version
- Nodes MUST reject packets with unknown version
- Forward compatibility: ignore unknown payload types
Error Handling
| Condition | Action |
|---|---|
| Invalid signature | Drop packet, do not relay |
| TTL = 0 | Drop packet |
| Duplicate packet_id | Drop packet (already in cache) |
| Unknown version | Drop packet |
| Payload too large | Drop packet (max 1MB default) |
| Decode failure | Drop packet |
Maximum Sizes
| Limit | Value | Rationale |
|---|---|---|
| Max packet size | 1 MB | Prevents DoS, fits in MTU with fragmentation |
| Max payload size | 1 MB - overhead | ~1 MB for file chunks |
| Dedup cache | 100,000 entries | ~3.2 MB for packet IDs |
| Sequence window | 2^32 per sender | Wraparound handled by timestamp |