| Metadata | Value |
|---|---|
| RFC ID | 001 |
| Title | EntglDb Wire Protocol & Security Specification |
| Status | Active |
| Protocol Version | v1.0 |
| Date | 2026-03-19 |
This document defines the standard for the peer-to-peer communication protocol used by EntglDb. The purpose is to formalize the discovery, connection, security, and data synchronization mechanics to ensure interoperability between different implementations (C#, Node.js, Kotlin, Dart, etc.).
The protocol ensures:
- Automatic Discovery of nodes on the local network.
- Secure Channel (authenticated and encrypted) over TCP.
- Efficient Synchronization via Oplog and Vector Clocks (Hybrid Logical Clock).
- Transport: TCP (Data), UDP (Discovery).
- Framing: Custom Length-Prefixed framing.
- Serialization: Protocol Buffers (Proto3).
- Security: ECDH (P-256) for key exchange, AES-256-GCM for encryption.
- Compression: Brotli (optional/negotiated).
Nodes announce their presence via UDP broadcast.
- Port:
25000 - Interval: 5000 ms
- Payload: UTF-8 JSON
{
"node_id": "<UUID>",
"tcp_port": 25000
}Once a peer is discovered, the node initiates a TCP connection to IP Address : tcp_port.
Every message exchanged on the TCP socket follows this structure (Little Endian for numbers):
[Length (4 bytes)] [Type (1 byte)] [Compression (1 byte)] [Payload (N bytes)]
- Length: 32-bit Signed Integer (Little Endian) indicating the length of the Payload field (N). It does not include the header bytes (Length, Type, Compression).
- Type: Byte identifying the message type (Enum
MessageType). - Compression:
0x00: No compression.0x01: Brotli (The Payload must be decompressed before parsing).
- Payload: The serialized data (Protobuf).
Mapping from the MessageType enum in sync.proto:
| ID | Name | Description |
|---|---|---|
| 1 | HandshakeReq |
Connection and auth request |
| 2 | HandshakeRes |
Connection response |
| 3 | GetClockReq |
Request current HLC |
| 4 | ClockRes |
HLC response |
| 5 | PullChangesReq |
Request Oplog diff |
| 6 | ChangeSetRes |
Send batch of changes |
| 7 | PushChangesReq |
Proactive changes push |
| 8 | AckRes |
Receipt confirmation |
| 9 | SecureEnv |
Encrypted envelope (See Sec. 5) |
Security is mandatory for production communications. The protocol uses an "Encrypt-then-MAC" approach (via AES-GCM) and ephemeral key exchange.
As soon as the TCP socket connects, before sending any Protobuf message, the exchange of public keys (Raw Bytes) takes place.
- Initiator sends:
[Length (4 bytes)] [Public Key (SubjectPublicKeyInfo format)] - Responder sends:
[Length (4 bytes)] [Public Key (SubjectPublicKeyInfo format)]
Algorithm: NIST P-256 Curve.
Both nodes compute the Shared Secret via ECDH. Session keys are derived as follows (Simplified HKDF-like):
-
Key1 = SHA256(Secret + [0x00]) -
Key2 = SHA256(Secret + [0x01]) -
Initiator: Encrypts with
Key1, Decrypts withKey2. -
Responder: Encrypts with
Key2, Decrypts withKey1.
After key exchange, all subsequent messages must be of type SecureEnv (ID 9).
The Payload of the SecureEnv frame is a SecureEnvelope Protobuf message:
message SecureEnvelope {
bytes ciphertext = 1; // Encrypted data
bytes nonce = 2; // IV (12 bytes for AES-GCM)
bytes auth_tag = 3; // Tag (16 bytes)
}The decrypted ciphertext contains the original message structure "encapsulated":
[Type (1 byte)] [Compression (1 byte)] [Inner Payload (N bytes)]
This allows the receiver to:
- Decrypt
SecureEnvelope. - Read the inner
Type(e.g.,HandshakeReq). - Read the inner
Compressionflag. - Decompress (if necessary) and deserialize the
Inner Payload.
- Connect (TCP).
- Secure Handshake (Key Exchange).
- App Handshake (Encrypted):
- Client sends
HandshakeReq(NodeID, AuthToken, Brotli Support). - Server responds
HandshakeRes(Accepted: true/false, Selected Compression).
- Client sends
- Sync:
- Client requests remote clock:
GetClockReq. - Client calculates delta and requests data:
PullChangesReq(sinceLastKnownHlc). - Server sends
ChangeSetRes.
- Client requests remote clock:
See sync.proto file for the exact message definitions.
message HandshakeRequest {
string node_id = 1;
string auth_token = 2;
repeated string supported_compression = 3;
}
// ... (other messages)- Framing: Previous documentation indicated
[Len][Comp][Payload]framing. The current implementation (v0.7.0) uses[Len][Type][Comp][Payload]. This RFC formalizes the version with explicitType. - Handshake: ECDH exchange happens before any Protobuf message.