BOLT-01: Base Protocol
Lightning Network base protocol specification. Message format, feature bits, and connection establishment.
| Type | Basis of Lightning Technology |
| Number | bolt-01 |
| Status | Final |
| Authors | Lightning Network Developers |
| Original | https://github.com/lightning/bolts/blob/master/01-messaging.md |
BOLT-01: Base Protocol
BOLT-01 defines the fundamental message format and feature negotiation for Lightning Network communication. All other BOLTs build on these primitives.
Specification Summary
| Aspect | Value |
|---|---|
| Status | Final |
| Layer | Transport |
| Purpose | Message framing and features |
| Dependencies | BOLT-08 (encryption) |
Message Format
All Lightning messages follow this structure:
┌──────────────────────────────────────────┐
│ type (2 bytes, big-endian) │
├──────────────────────────────────────────┤
│ payload (variable length) │
└──────────────────────────────────────────┘
Type ranges:
| Range | Purpose |
|---|---|
| 0-32767 | P2P messages (requires handshake) |
| 32768-65535 | Custom/experimental |
Message Size Limits
- Maximum message size: 65535 bytes (after encryption)
- Includes 2-byte type field
- Payload: up to 65533 bytes
Core Messages
init (type 16)
Sent immediately after connection establishment:
┌─────────────────────────────────────────┐
│ gflen (2 bytes): global features length │
│ globalfeatures (gflen bytes) │
│ flen (2 bytes): features length │
│ features (flen bytes) │
│ [tlvs]: optional TLV stream │
└─────────────────────────────────────────┘
error (type 17)
Report errors to peer:
┌─────────────────────────────────────────┐
│ channel_id (32 bytes) │
│ len (2 bytes): data length │
│ data (len bytes): error message │
└─────────────────────────────────────────┘
Special channel_id: All zeros means connection-level error.
warning (type 1)
Non-fatal warning (added later):
┌─────────────────────────────────────────┐
│ channel_id (32 bytes) │
│ len (2 bytes): data length │
│ data (len bytes): warning message │
└─────────────────────────────────────────┘
ping (type 18)
Keep-alive and latency measurement:
┌─────────────────────────────────────────┐
│ num_pong_bytes (2 bytes) │
│ byteslen (2 bytes) │
│ ignored (byteslen bytes) │
└─────────────────────────────────────────┘
pong (type 19)
Response to ping:
┌─────────────────────────────────────────┐
│ byteslen (2 bytes) │
│ ignored (byteslen bytes) │
└─────────────────────────────────────────┘
Feature Bits
Features are negotiated via bit vectors in init messages.
Bit Interpretation
| Bit Position | Meaning |
|---|---|
| Even (0, 2, 4…) | Required—disconnect if not understood |
| Odd (1, 3, 5…) | Optional—ignore if not understood |
Feature Pairs
Each feature uses two bits:
- Bit N (even): “I require this”
- Bit N+1 (odd): “I support this”
Core Features
| Bits | Name | Description |
|---|---|---|
| 0/1 | data_loss_protect | Channel reestablish includes state |
| 4/5 | upfront_shutdown_script | Pre-agreed shutdown address |
| 6/7 | gossip_queries | Gossip query support |
| 8/9 | var_onion_optin | Variable-length onion |
| 10/11 | gossip_queries_ex | Extended gossip queries |
| 12/13 | static_remotekey | Static output keys |
| 14/15 | payment_secret | Payment secret in invoices |
| 16/17 | basic_mpp | Multi-path payments |
| 18/19 | option_support_large_channel | Wumbo channels |
| 20/21 | option_anchor_outputs | Anchor outputs |
| 22/23 | option_anchors_zero_fee_htlc_tx | Zero-fee HTLC anchors |
Feature Negotiation
Connection Establishment
Alice Bob
│ │
│──────── TCP connect ────────────→│
│ │
│←───── Noise handshake ──────────→│
│ (BOLT-08) │
│ │
│──────── init ───────────────────→│
│←─────── init ────────────────────│
│ │
│ Features negotiated │
│ │
Negotiation Rules
- If peer sets unknown even bit: disconnect
- If peer sets unknown odd bit: ignore
- Connection features = intersection of both init messages
- Channel features negotiated separately (channel_type)
TLV Format
Type-Length-Value encoding for extensibility:
┌─────────────────────────────────────────┐
│ type (BigSize) │
│ length (BigSize) │
│ value (length bytes) │
└─────────────────────────────────────────┘
BigSize Encoding
Variable-length integer:
| Range | Encoding |
|---|---|
| 0-0xfc | 1 byte (value) |
| 0xfd-0xffff | 0xfd + 2 bytes |
| 0x10000-0xffffffff | 0xfe + 4 bytes |
| larger | 0xff + 8 bytes |
Agent Implementation
Parsing Messages
import struct
def parse_message(data: bytes) -> tuple[int, bytes]:
"""Parse Lightning message into type and payload."""
if len(data) < 2:
raise ValueError("Message too short")
msg_type = struct.unpack('>H', data[:2])[0]
payload = data[2:]
return msg_type, payload
def create_message(msg_type: int, payload: bytes) -> bytes:
"""Create Lightning message from type and payload."""
return struct.pack('>H', msg_type) + payload
Feature Handling
def has_feature(features: bytes, bit: int) -> bool:
"""Check if feature bit is set."""
byte_index = bit // 8
bit_index = bit % 8
if byte_index >= len(features):
return False
# Features are stored little-endian
return bool(features[byte_index] & (1 << bit_index))
def requires_feature(features: bytes, bit: int) -> bool:
"""Check if feature is required (even bit set)."""
return has_feature(features, bit & ~1) # Even bit
def supports_feature(features: bytes, bit: int) -> bool:
"""Check if feature is supported (any bit in pair)."""
return has_feature(features, bit & ~1) or has_feature(features, bit | 1)
Init Message
def create_init(features: bytes, networks: list[bytes] = None) -> bytes:
"""Create init message."""
payload = struct.pack('>H', 0) # global features (legacy, empty)
payload += struct.pack('>H', len(features))
payload += features
if networks:
# Add networks TLV (type 1)
tlv = create_tlv(1, b''.join(networks))
payload += tlv
return create_message(16, payload)
Error Handling
Error Severity
| Error Type | Action |
|---|---|
| Connection error | Close TCP connection |
| Channel error | Fail/close specific channel |
| Warning | Log, continue operation |
All-Zero Channel ID
Channel ID of 32 zero bytes indicates error applies to entire connection:
ALL_ZERO_CHANNEL = b'\x00' * 32
def is_connection_error(channel_id: bytes) -> bool:
return channel_id == ALL_ZERO_CHANNEL
Related BOLTs
Machine-Readable Summary
{
"bolt": "01",
"title": "Base Protocol",
"status": "final",
"messages": [
{"type": 16, "name": "init"},
{"type": 17, "name": "error"},
{"type": 1, "name": "warning"},
{"type": 18, "name": "ping"},
{"type": 19, "name": "pong"}
],
"key_concepts": [
"message-format",
"feature-bits",
"tlv-encoding"
]
}