FortiBlox LogoFortiBlox Docs
AXIO Block Engine

Relay Protocol

WebSocket wire protocol specification for communication between the AXIO relay and client validators.

Relay Protocol

This document defines the wire protocol between the AXIO relay and client validators.

Overview

AXIO Backend                          Client Validator
┌────────────┐                        ┌──────────────┐
│ axio-relay │ ──── WebSocket ────▶   │ forti-bundle │
│            │ ◀─── Confirmations ──  │   (crate)    │
└────────────┘                        └──────────────┘

Transport

PropertyValue
ProtocolWebSocket (ws:// or wss://)
Default Port4002
Path/v1/relay
AuthX-Auth-Token header on WebSocket upgrade
EncodingJSON

Connection Flow

  1. Client connects: wss://relay.axio.fortiblox.com/v1/relay
  2. Client sends Register message with validator identity
  3. Server confirms registration with Registered response
  4. Server pushes bundles when client has upcoming leader slots
  5. Client confirms inclusion or rejection

Message Types

Client → Server

Register

Sent immediately after WebSocket connection is established.

{
  "type": "register",
  "validator": "<base58 identity pubkey>",
  "version": "1.0.0",
  "capabilities": ["bundle-v1"],
  "mev_commission_bps": 500
}
FieldTypeDescription
validatorstringBase58 validator identity pubkey
versionstringClient software version
capabilitiesstring[]Supported protocols
mev_commission_bpsnumberMEV commission in basis points

BundleConfirmation

Sent after a bundle's target slot has passed, reporting the inclusion result.

{
  "type": "bundle_confirmation",
  "bundle_id": "<uuid>",
  "status": "included",
  "slot": 149900000,
  "signatures": ["<base58 sig>", "..."],
  "reason": null
}
FieldTypeDescription
bundle_idstringUUID of the delivered bundle
statusstring"included" | "rejected" | "expired"
slotnumberSlot where the bundle was (or should have been) included
signaturesstring[]On-chain transaction signatures (empty if rejected/expired)
reasonstring?Optional rejection reason

Heartbeat

Sent every 10 seconds to maintain the connection.

{
  "type": "heartbeat",
  "slot": 149900000,
  "timestamp": 1712750000000
}

Server → Client

Registered

Confirmation that the validator is registered and will receive bundles.

{
  "type": "registered",
  "status": "ok",
  "relay_version": "1.0.0"
}

BundleDelivery

A winning bundle from the auction, delivered just-in-time before the validator's leader slot.

{
  "type": "bundle_delivery",
  "bundle_id": "<uuid>",
  "target_slot": 149900001,
  "transactions": [
    {
      "data": "<base64 serialized tx>",
      "accounts": [
        { "pubkey": "<base58>", "isWritable": true }
      ]
    }
  ],
  "tip": 5000000,
  "ev_score": 7500000.0,
  "priority": 1,
  "expires_at_slot": 149900013
}
FieldTypeDescription
bundle_idstringUUID for tracking
target_slotnumberSlot to include this bundle
transactionsarrayOrdered list of transactions
tipnumberTip amount in lamports
ev_scorenumberExpected value score from auction
prioritynumberPriority rank (1 = highest)
expires_at_slotnumberDiscard after this slot

SlotAssignment

Notifies the validator of its upcoming leader slots (sent at the start of each epoch and when the schedule updates).

{
  "type": "slot_assignment",
  "slots": [149900100, 149900101, 149900102, 149900103],
  "epoch": 299800
}

Bundle Inclusion Contract

When a client validator receives a BundleDelivery:

  1. MUST include transactions in the specified order (atomic)
  2. MUST include the tip transaction (last tx in bundle)
  3. MUST respect expires_at_slot — discard if past
  4. SHOULD include higher-priority bundles first
  5. MUST send BundleConfirmation after the slot

For multi-tx bundles (2-5 transactions), the validator must execute them sequentially with state carryover — each transaction sees the effects of the previous one.

Tip Distribution

The tip transaction is always the last transaction in the bundle. It transfers lamports from the searcher to one of 8 rotating tip PDA vaults managed by the forti-tips program.

Program: 7dWho2FmC68LZBirmAf68GsrEJSFjpaVTF5pYPqVDCug

Fee Split:
  40% → Validator
  25% → Reserve injection (aXNT backing)
  15% → Staker yield
  20% → Treasury

A permissionless crank triggers tip distribution every 25-40 seconds (randomized to prevent frontrunning).

Error Handling

ScenarioBehavior
WebSocket disconnectsClient reconnects with exponential backoff (1s, 2s, 4s, 8s, max 30s)
Stale heartbeat (>10s)Server stops sending bundles until heartbeat resumes
Invalid auth tokenServer closes connection with code 4001
Bundle expired at deliveryClient discards silently, sends expired confirmation
All validators disconnectedRelay queues bundles for up to 30s, then drops

Multi-Validator Broadcast

AXIO supports multi-validator broadcast. When RELAY_BROADCAST_COUNT > 1, the relay delivers each winning bundle to N validators simultaneously. The first validator to include the bundle wins the tip. This provides redundancy and ensures bundles are included even if one validator misses a slot.

Versioning

PropertyValue
Protocol versionIn URL path: /v1/relay
Breaking changesNew path: /v2/relay
Client capabilitiesDeclared in Register message
Backward compatv1 supported for 6+ months after v2 launch