Wire Protocol

Tubo uses a simple binary framing protocol over libp2p streams. This document describes the frame format, message types, streaming body model, and version negotiation.

Protocol IDs

Tubo registers one libp2p protocol ID:

Clients (edge/bridge) negotiate 1.1 only. Legacy /p2p-tunnel/1.0 peers are no longer supported.

Frame format

Each frame on the wire is:

+----------+--------+-----------+
| type (1) | len (varint) | payload   |
+----------+--------+-----------+

Frame types

Type byte Name Direction Description
0x01HelloClient → ServiceProtocol version, role, capabilities (v1.1 only)
0x02RequestHeaderClient → ServiceHTTP method, path, headers, connect proof
0x03ResponseHeaderService → ClientHTTP status code and response headers
0x04BodyChunkBothStreaming body data; empty chunk signals end-of-body
0x05ErrorBothError message; terminates the stream

Request / Response flow

sequenceDiagram participant C as Client participant S as tubo attach / Service participant O as Origin HTTP Note over C,S: libp2p stream (Noise encrypted) C->>S: Hello (optional, v1.1 only) C->>S: RequestHeader C->>S: BodyChunk (0..N) C->>S: BodyChunk (empty — end of request) Note right of S: required even for GET S->>O: Forward HTTP request O-->>S: HTTP response S-->>C: ResponseHeader S-->>C: BodyChunk (0..N) S-->>C: BodyChunk (empty — end of response) Note over C,S: stream closed
GET and other requests with no body must still send a final empty BodyChunk to signal end-of-request. Omitting it will cause the service to hang waiting for more data.

Connect proof (v1.1)

In protocol 1.1, the RequestHeader carries a connect proof (PoP) that the service validates before forwarding the request. The proof binds:

The proof is an Ed25519 signature over the above fields using the client's key.

Version negotiation

libp2p multistream-select handles protocol negotiation. The client proposes /p2p-tunnel/1.1 and the service must support it.

Tubo 0.10.0 uses /p2p-tunnel/1.1 only. Legacy nodes that only speak /p2p-tunnel/1.0 are no longer supported.

Discovery protocol

In collaborative namespaces, service announcements are published on a secret-backed Discovery V3 GossipSub topic derived from the namespace discovery entry. The topic string is still opaque, but overlay reachability alone is not enough to discover services in that namespace. Each announcement is JSON-encoded and Ed25519-signed. Subscribers validate the authority-bound publication chain before admitting an entry into the discovery cache.