Architecture
Message Types
- hello - Initial greeting with version info and public key
- welcome - Response with known peer list and public key for discovery
- ping - Health check heartbeat
- pong - Heartbeat response
- script - User message (triggers script execution)
- text - Text message (logged by receiver)
- binary - Binary message (raw data)
- relay - Forward message through intermediate peer
- punch - NAT traversal request
End-to-End Encryption
MsgTier implements X25519 Elliptic Curve Diffie-Hellman (ECDH) key exchange with symmetric encryption. All data messages are encrypted, including those sent to self (loopback).
- Key Generation - Each node generates an X25519 key pair on startup
- Key Exchange - Public keys are exchanged during the hello/welcome handshake
- Shared Secret - Peers compute shared secrets via ECDH (including a self-shared secret for loopback)
- Encryption - All payloads are symmetrically encrypted using the shared secret
Encryption Flow:
Sender Receiver
│ │
│ 1. Exchange public keys │
│ (hello/welcome handshake) │
├──────────────────────────────►│
│◄──────────────────────────────┤
│ │
│ 2. Both compute shared │
│ secret via X25519 ECDH │
│ │
│ 3. Encrypt with shared secret│
│ (Force Binary MsgPack) │
├──────────────────────────────►│
│ │
│ 4. Decrypt with shared secret│
│ │Messages are automatically encrypted. If a peer's shared secret is not available, transmission falls back to unencrypted with a warning (but for script/binary messages, we strive for encryption).
Peer Discovery
The network uses a gossip-based peer discovery mechanism:
- Node reads configuration with initial peer list
- Node sends
hellomessages to configured peers - Peers respond with
welcomecontaining their known peers - Node automatically discovers and connects to new peers
- Each new peer is greeted with a
hellomessage - Network topology expands organically
Example - Multi-hop Discovery:
node1.json → peers: [node2]
node2.json → peers: [node1, node3]
node3.json → peers: [node2]
Result:
- node1 connects to node2 (configured)
- node2 sends welcome with node3's address
- node1 automatically discovers and connects to node3
- All nodes can communicate with each otherForeign Network Relay
When a direct P2P connection or intra-network route cannot be established, MsgTier supports Foreign Network Relay. This allows messages to be forwarded across different networks via public nodes.
- Packet Wrapping: The original message is wrapped in a
ForeignNetworkPacket, preserving the destination network and peer ID. - Public Node Fallback: If the
PeerManagerfinds no route in its routing table, it searches for a connected "public node" (non-private IP). - Forwarding: The wrapped packet is sent to the public node, which acts as a gateway/relay.
- Unwrapping: The receiving node unwraps the packet and attempts to deliver it to the final destination (either directly or by relaying further).
- Rate Limiting: To prevent abuse, foreign relays are subject to bandwidth limits configured via
foreign_relay_bps_limit.
Health Monitoring
The system implements continuous connection health monitoring:
- Heartbeat Interval: 10 seconds - Periodic ping sent to all peers
- Timeout Threshold: 60 seconds - No pong response marks connection inactive
- Automatic Reconnection: Failed connections retry with exponential backoff
- Connection State Tracking: Each connection is marked as Active/Disconnected
- Real-time Status: Query via HTTP
/api/statusendpoint to monitor health
Connections that don't receive pong responses within 60 seconds are automatically marked as disconnected, and the system attempts to reconnect periodically.
Message Flow
Messages flow through the system in the following sequence:
┌─────────────────┐
│ HTTP Client │ User sends POST /api/send
└────────┬────────┘
│ {target: "peer_id", body: "message"}
▼
┌──────────────────────────┐
│ HTTP Handler │ 1. Check if Loopback (target == self) -> Direct Queue
│ (add_pending_message) │ 2. Else -> Encrypt & Queue for Remote
└────────┬─────────────────┘
│
▼
┌──────────────────────────┐
│ Pending Message │ Background task processes queue every 100ms
│ Processor (100ms loop) │ Route via direct conn, intra-network relay,
│ │ or public node (foreign relay)
└────────┬─────────────────┘
│
▼
┌──────────────────────────┐
│ Transport Layer │ Send message via UDP/TCP/WS to remote address
│ (via active connection) │ OR Handle Loopback internally
└────────┬─────────────────┘
│
▼
┌──────────────────────────┐
│ Target Node │ Receive and process message
│ (Listener) │
└────────┬─────────────────┘
│ Decrypt & Parse
▼
┌─────────────────────────────────────┐
│ Is kind == "script"? │
│ Does body match a script name? │
└──────────┬────────────────────┬─────┘
│ │
Yes │ │ No
▼ ▼
┌─────────────┐ ┌──────────────┐
│ Execute │ │ Message │
│ Script │ │ Logged Only │
└─────┬───────┘ └──────────────┘
│
▼
┌─────────────┐
│ Send │
│ Response │
└─────────────┘Key Features:
- Asynchronous Processing: HTTP handler waits for response (with timeout) but processing is async.
- Unified Encryption: All payloads are encrypted, even to self.
- Best Connection Selection: Automatically picks active connection with best quality (prioritizing LAN addresses).
- Script Matching: Message body matched against configured script names (case-sensitive).
- Platform-Specific Execution: Scripts run via platform-specific shells (Windows: cmd.exe, Unix: sh).
How It Works
1. Initialization Phase
On startup, the node:
- Loads configuration from JSON file (node ID, listeners, peers, scripts)
- Generates X25519 key pair for end-to-end encryption
- Starts HTTP API server on configured address
- Creates listeners on all configured addresses (UDP, TCP, WebSocket)
- Spawns three background tasks per listener: heartbeat, reconnection, pending message processing
2. Connection Establishment
For each listener, the system:
- Reads initial peer list from configuration
- Sends
hellomessages to all configured peer addresses with public key - Builds a peer information structure as responses arrive
- Computes shared secrets with peers for encryption
- Creates and tracks connections (local_addr ↔ remote_addr pairs)
- Handles version exchange for compatibility tracking
3. Peer Discovery via Gossip
When a hello message is received:
- A
welcomemessage is sent back containing all known peers and the node's public key - The sender extracts unknown peers from the welcome message
- The sender computes a shared secret with the peer for future encrypted communication
- New peers are automatically discovered and greeted with
hello - This creates an organic network topology without manual configuration
4. Health Management Loop
The heartbeat background task:
- Every 10 seconds: Sends
pingto all known peer connections - Tracks
pongresponses and updateslast_seentimestamps - After 60 seconds without a pong: Marks connection as inactive
- Continues monitoring for reconnection opportunity
5. Message Sending
When HTTP POST /send is received:
- Encryption: Payload is encrypted using the shared secret for the target (or self)
- Queuing: Encrypted message is added to
pending_messages - Processing: Background task (100ms interval) retrieves the queue
- Routing: Finds best active connection, internal route, or public relay fallback (prioritizing LAN addresses)
- Transmission: Sends message via appropriate transport (UDP/TCP/WS)
- Cleanup: Clears processed messages from queue
6. Message Reception and Script Execution
When a message arrives (via any transport):
- Message is decoded from MessagePack format
- Connection state is updated (marked Active)
- If message is encrypted: decrypt using shared secret derived from X25519 ECDH
- If message type is
script: extract script name from body - Lookup script name in configuration scripts map
- If found: spawn async task to execute script with platform-specific shell
- Execute asynchronously to avoid blocking the transport handler
Message Types Reference
| Type | Direction | Purpose |
|---|---|---|
hello | Bidirectional | Initial greeting with version info |
welcome | Response | Contains peer list for discovery |
ping | To peer | Health check request |
pong | Response | Health check response |
script | To peer | User message (triggers script) |
text | To peer | Text message (logged only) |
binary | To peer | Binary message (raw data) |
relay | To relay peer | Forward message through intermediate |
punch_request | For NAT | Initiate NAT traversal |
punch | For NAT | NAT traversal message |
punch_ack | For NAT | NAT traversal acknowledgment |
Connection State Machine
┌──────────┐
│ Init │
└────┬─────┘
│ hello received
▼
┌──────────┐
┌────│ Active │◄────┐
│ └──────────┘ │
│ │ │
│ 60s │ pong │ reconnect
│ no │ received │ succeeds
│ pong │ │
│ ▼ │
│ ┌──────────┐ │
└───►│Disconnect├─────┘
└──────────┘