Skip to content
This repository was archived by the owner on Jul 18, 2025. It is now read-only.

Commit 6349048

Browse files
committed
Protocol 0.2.0
Update to the protocol as a result of libchan meeting with Matteo Collina. Bump the version to 0.2.0 and name the set the previous version as 0.1.0. Protocol changes: - Define stream provider to support multiple multiplexing protocols - Support CBOR in addition to Msgpack for channel message encoding - Add extended type codes definition - Require byte-streams to send *"libchan-parent-ref"* - Allow byte-streams as duplex or half-duplex - Add channel synchronization through ack definition - Add channel errors - Update description of relationship to Go channels Other changes: - Add Derek and Matteo to authors - Reformatted to 80 character lines - Much cleanup and rewording Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
1 parent d5db941 commit 6349048

1 file changed

Lines changed: 214 additions & 106 deletions

File tree

PROTOCOL.md

Lines changed: 214 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -2,179 +2,287 @@
22

33
Extreme portability is a key design goal of libchan.
44

5-
This document specifies the libchan protocol to allow multiple implementations to co-exist with
6-
full interoperability.
5+
This document specifies the libchan protocol to allow multiple implementations
6+
to co-exist with full interoperability.
77

88
## Version
99

10-
No version yet.
10+
0.2.0
1111

12-
## Author
12+
## Authors
1313

1414
Solomon Hykes <solomon@docker.com>
15+
Derek McGowan <derek@docker.com>
16+
Matteo Collina <matteo.collina@gmail.com>
1517

1618
## Status
1719

18-
This specification is still work in progress. Things will change, probably in reverse-incompatible ways.
19-
We hope to reach full API stability soon.
20+
This specification is nearing a stable release. The protocol still may change in
21+
reverse-incompatible ways.
2022

2123
## Terminology
2224

2325
### Channel
2426

25-
A `channel` is an object which allows 2 concurrent programs to communicate with each other. The semantics
26-
of a libchan channel are very similar (but not identical) to those of Go's native channels.
27+
A `channel` is an object which allows 2 concurrent programs to communicate with
28+
each other. The semantics of a libchan channel are very similar (but not
29+
identical) to those of Go's native channels. A channel may be used
30+
synchronously, but do not support Go's channel select semantics.
2731

28-
A channel has 2 ends: a `Sender` end and a `Receiver` end. The Sender can send messages and close the channel.
29-
The Receiver can receive messages. Messages arrive in the same order they were sent.
32+
A channel has 2 ends: a `Sender` end and a `Receiver` end. The Sender can send
33+
messages and close the channel. The Receiver can receive messages. Messages
34+
arrive in the same order they were sent.
3035

31-
A channel is uni-directional: messages can only flow in one direction. So channels are more similar to pipes
32-
than to sockets.
36+
A channel is uni-directional: messages can only flow in one direction. So
37+
channels are more similar to pipes than to sockets.
3338

3439
### Message
3540

36-
A message is a discrete packet of data which can be sent on a channel. Messages are structured into multiple
37-
fields. The protocol defines which data types can be carried by a message, and how transports should encode and
38-
decode them.
41+
A message is a discrete packet of data which can be sent on a channel. The
42+
protocol defines which data types can be carried sent as a message, and how
43+
transports should encode and decode them.
3944

4045
### Byte stream
4146

42-
A byte stream is an object which implements raw IO with `Read`, `Write` and `Close` methods.
43-
Typical byte streams are text files, network sockets, memory buffers, pipes, and so on.
47+
A byte stream is an object which implements raw IO with `Read`, `Write` and
48+
`Close` methods. Typical byte streams are text files, network sockets, memory
49+
buffers, pipes, and so on. A byte stream may either be read only, write only, or
50+
full duplex.
4451

45-
One distinct characteristic of libchan is that it can encode byte streams as first class fields
46-
in a message, alongside more basic types like integers or strings.
52+
One distinct characteristic of libchan is that it can encode byte streams as
53+
first class fields in a message, alongside more basic types like integers or
54+
strings.
4755

4856
### Nesting
4957

50-
Libchan supports nesting. This means that a libchan message can include a channel, which itself
51-
can be used to send and receive messages, and so on all the way down.
58+
Libchan supports nesting. This means that a libchan message can include a
59+
channel, which itself can be used to send and receive messages, and so on all
60+
the way down.
5261

5362
Nesting is a fundamental property of libchan.
5463

5564
## Underlying transport
5665

57-
The libchan protocol requires a reliable, 2-way byte stream as a transport.
58-
The most popular options are TCP connections, unix stream sockets and TLS sessions.
66+
The libchan protocol requires a reliable, 2-way byte stream with support for
67+
multiplexing as a transport. The underlying byte stream protocol is abstracted
68+
to the libchan protocol through a simple multiplexed stream interface which may
69+
use SPDY/3.1, HTTP/2, or SSH over over TCP connections, unix stream sockets and
70+
TLS sessions.
5971

60-
It is also possible to use websocket as an underlying transport, which allows exposing
61-
a libchan endpoint at an HTTP1 url.
72+
It is also possible to use websocket as an underlying byte streamtransport,
73+
which allows exposing a libchan endpoint at an HTTP/1 url.
6274

6375
## Authentication and encryption
6476

65-
Libchan can optionally use TLS to authenticate and encrypt communications. After the initial
66-
handshake and protocol negotiation, the TLS session is simply used as the transport for
67-
the libchan wire protocol.
68-
69-
## Wire protocol
70-
71-
Libchan uses SPDY (protocol draft 3) as its wire protocol, with no modification.
77+
Libchan can optionally use TLS to authenticate and encrypt communications. After
78+
the initial handshake and protocol negotiation, the TLS session is simply used
79+
as the transport for the libchan multiplexed stream provider.
80+
81+
## Stream Provider
82+
83+
Libchan uses a stream provider to establish new channels and byte streams over
84+
an underlying byte stream. The stream provider must be able to send
85+
headers when creating new streams and retrieve headers for remotely created
86+
streams.
87+
88+
### Headers
89+
The stream provider must support sending key-value headers on stream creation.
90+
91+
- *"libchan-ref"* - String representation of a unique 64 bit integer identifier
92+
for the established stream.
93+
- *"libchan-parent-ref"* String representation of a unique 64 bit integer
94+
identifier for parent of the established stream. (see *"Sending nested
95+
channels"* and *"Sending byte streams"*)
96+
97+
### Streams
98+
The stream provider provides the functionality for creating new streams as
99+
well as accepting streams created remotely. A stream is create with
100+
a set of headers and an accepted stream has a method for returning the
101+
headers. Closing a stream must put the stream in a half-closed state and
102+
not allow anymore data to be written. If the remote side has already
103+
closed, the stream is fully closed. Reseting a stream forces the stream
104+
into a fully closed state and should only be used in error cases.
105+
Resetting does not give the remote a chance to finish sending data and
106+
cleanly close.
107+
108+
## Stream identifiers
109+
Libchan creates a unique identifier for every stream created by the stream
110+
provider. The identifiers are integer values and should never be reused.
111+
The identifier is only unique to a given endpoint, meaning both sides of a
112+
connection may have the same identifier for two different streams. The
113+
identifiers received from the remote endpoint should only be used to reference
114+
streams from that endpoint, and never streams created locally. A remote
115+
endpoint's stream identifier should never be sent in a libchan message.
116+
To send a stream created remotely, a new stream should be created
117+
locally, copied from the remote stream, and the identifier to the local copy
118+
should be used.
72119

73120
## Control protocol
74121

75-
Once 2 libchan endpoints have established a SPDY session, they communicate with the following
76-
control protocol.
122+
Once 2 libchan endpoints have established a multiplexed stream session, they
123+
communicate with the following control protocol.
77124

78125
### Top-level channels
79126

80-
Each SPDY session may carry multiple concurrent channels, in both directions, using standard
81-
SPDY framing and stream multiplexing. Each libchan channel is implemented by an underlying
82-
SPDY stream.
127+
Each libchan session may carry multiple concurrent channels, in both directions,
128+
using stream multiplexing. Each libchan channel is implemented by an underlying
129+
stream.
83130

84-
To use a SPDY session, either endpoint may initiate new channels, wait for its peer to
85-
initiate new channels, or a combination of both. Channels initiated in this way are called
86-
*top-level channels*.
131+
To use a libchan session, either endpoint may initiate new channels, wait for
132+
its peer to initiate new channels, or a combination of both. Channels initiated
133+
in this way are called *top-level channels*.
87134

88-
* To initiate a new top-level channel, either endpoint may initiate a new SPDY stream, then
89-
start sending messages to it (see *"sending messages"*).
135+
* To initiate a new top-level channel, either endpoint may initiate a new
136+
stream, then start sending messages to it (see *"sending messages"*).
90137

91-
* The endpoint initiating a top-level channel MAY NOT allow the application to receive messages
92-
from it and MUST ignore inbound messages received on that stream.
138+
* The endpoint initiating a top-level channel MAY NOT allow the application to
139+
receive messages from it and MUST interpret inbound messages received on that
140+
stream as an ack or error message.
93141

94-
* When an endpoint receives a new inbound SPDY stream, and the initial headers DO NOT include
95-
the key `libchan-ref`, it MUST queue a new `Receiver` channel to pass to the application.
142+
* The endpoint initiating the channel must create a unique identifier for the
143+
channel and include the value in the *"libchan-ref"* header when creating
144+
the new stream.
96145

97-
* The endpoint receiving a top-level channel MAY NOT allow the application to send messages to
98-
it.
146+
* When an endpoint receives a new stream without the header
147+
*"libchan-parent-ref"*, it MUST interpret the stream as an inbound top-level
148+
channel and queue a new `Receiver` channel to pass to the application.
99149

150+
* The endpoint receiving a top-level channel MAY NOT allow the application to
151+
send messages to it.
100152

101-
### Sending messages on a channel
102153

103-
Once a SPDY stream is initiated, it can be used as a channel, with the initiating endpoint holding
104-
the `Sender` end of the channel, and the recipient endpoint holding the `Receiver` end.
154+
### Sending messages on a channel
105155

106-
* To send a message, the sender MUST encode it using the [msgpack](https://msgpack.org) encoding format, and
107-
send a single data frame on the corresponding SPDY stream, with the encoded message as the exact content of
108-
the frame.
156+
Once a stream is initiated, it can be used as a channel, with the initiating
157+
endpoint holding the `Sender` end of the channel, and the recipient endpoint
158+
holding the `Receiver` end.
109159

110-
* When receiving a data frame on any active SPDY stream, the receiver MUST decode it using msgpack. If
111-
the decoding fails, the receiver MUST close the underlying stream, and future calls to `Receive` on that
112-
channel MUST return an error.
160+
* To send a message, the sender MUST encode it using the message encoding format
161+
(see *"message encoding"*), and send the encoded message on the corresponding
162+
stream.
113163

114-
* A valid msgpack decode operation with leftover trailing or leading data is considered an *invalid* msgpack
115-
decode operation, and MUST yield the corresponding error.
164+
* When receiving a data on any active stream, the receiver MUST decode it using
165+
the same message encoding format. If the decoding fails, the receiver MUST close
166+
the underlying stream, and future calls to `Receive` on that channel MUST return
167+
an error.
116168

117-
### Closing a channel
169+
* Every send message should have a corresponding receive of an ack message from
170+
the peer. The ack message is a map with at least one field named `code`. The
171+
`code` field should have an integer value, with an a value of zero considered
172+
a successful ack and non-zero as an error. An error should be accompanied with
173+
an additional `message` field of type string, describing the error. If an error
174+
is received, the sender should close and pass an error to the application.
118175

119-
The endpoint which initiated a channel MAY close it by closing the underlying SPDY stream.
176+
### Sending nested channels
120177

121-
*FIXME: provide more details*
178+
* When sending a nested channel, in addition to the *"libchan-ref"* header, the
179+
*"libchan-parent-ref"* header must be sent identifying the channel used to
180+
create the nested channel.
122181

123-
### Sending byte streams
182+
### Closing a channel
124183

125-
Libchan messages support a special type called *byte streams*. Unlike regular types like integers or strings,
126-
byte streams are not fully encoded in the message. Instead, the message encodes a *reference* which allows
127-
the receiving endpoint to reconstitute the byte stream after receiving the message, and pass it to the
128-
application.
184+
The endpoint which holds the send side of a channel MAY close it which will
185+
half-close the stream. The receive side should respond by closing the stream,
186+
putting the stream in a fully closed state. Any send or receive call from the
187+
application after close should return an error.
129188

130-
*FIXME: specify use of msgpack extended types to encode byte streams*
189+
When an error is received on a channel, the underlying stream should be
190+
closed by both ends.
131191

132-
Libchan supports 2 methods for sending byte streams: a default method which is supported on all transports,
133-
and an optional method which requires unix stream sockets. All implementations MUST support both methods.
192+
### Sending byte streams
134193

135-
#### Default method: SPDY streams
194+
Libchan messages support a special type called *byte streams*. Unlike regular
195+
types like integers or strings, byte streams are not fully encoded in the
196+
message. Instead, the message encodes a *reference* which allows the receiving
197+
endpoint to reconstitute the byte stream after receiving the message, and pass
198+
it to the application.
136199

137-
The primary method for sending a byte stream is to send it over a SPDY stream, with the following protocol:
200+
Byte streams use the raw stream returned by the stream provider.
138201

139-
* When encoding a message including 1 or more byte stream values, the sender MUST assign to each value
140-
an identifier unique to the session, and store these identifiers for future use.
202+
* When encoding a message including 1 or more byte stream values, the sender
203+
MUST assign to each value an identifier unique to the session, and store these
204+
identifiers for future use.
141205

142-
* After sending the encoded message, the sender MUST initiate 1 new SPDY stream for each byte stream value
143-
in the message.
206+
* After sending the encoded message, the sender MUST create 1 new stream for
207+
each byte stream value in the message.
144208

145-
* Each of those SPDY stream MUST include an initial header with as a key the string "*libchan-ref*", and
146-
as a value the identifier of the corresponding byte stream.
209+
* Each of new stream MUST include a header with the key *"libchan-ref"* and
210+
a value of the identifier of the corresponding byte stream. It must also
211+
include a header with the key *"libchan-parent-ref"* and a value of the
212+
stream identifier for the message channel which created the byte stream.
147213

148214
Conversely, the receiver must follow this protocol:
149215

150-
* When decoding a message including 1 or more byte stream values, the receiver MUST store the unique identifier
151-
of each value in a session-wide table of pending byte streams. It MAY then immediately pass the decoded message to the application.
152-
153-
* The sender SHOULD cap the size of its pending byte streams table to a reasonable value. It MAY make that value
154-
configurable by the application. If it receives a message with 1 or more byte stream references, and the table
155-
is full, the sender MAY suspend processing of the message until there is room in the table.
156-
157-
* When receiving new SPDY streams which include the header key "*libchan-ref*", the receiver MUST lookup that
158-
header value in the table of pending byte streams. If the value is registered in the table, that SPDY stream
159-
MUST be passed to the application.
160-
161-
On either end, once the SPDY stream for a byte-stream value is established, it MUST be exposed to the application
162-
as follows:
163-
164-
* After sending each of those SPDY streams, each write operation by the application to a byte-stream field MUST
165-
trigger the sending of a single data frame on the corresponding SPDY stream.
166-
167-
* Each read operation by the application from a byte-stream field MUST yield the content of the next
168-
data frame received on the corresponding SPDY stream. If the reading end of the SPDY stream is closed,
169-
the read operation MUST yield EOF.
170-
171-
* A close operation by the application on the a byte-stream field MUST trigger the closing of the writing end
172-
of the corresponding SPDY stream.
173-
174-
#### Optional method: file descriptor passing
216+
* When decoding a message including 1 or more byte stream values, the receiver
217+
MUST store the unique identifier of each value in a session-wide table of
218+
pending byte streams. It MAY then immediately pass the decoded message to the
219+
application.
175220

176-
*FIXME*
221+
* The sender SHOULD cap the size of its pending byte streams table to a
222+
reasonable value. It MAY make that value configurable by the application. If it
223+
receives a message with 1 or more byte stream references, and the table
224+
is full, the sender MAY suspend processing of the message until there is room in
225+
the table.
177226

178-
### Sending nested channels
227+
* When receiving new streams which include the header key "*libchan-ref*", the
228+
receiver MUST lookup that header value in the table of pending byte streams. If
229+
the value is registered in the table, that stream MUST be passed to the
230+
application.
179231

180-
*FIXME*
232+
On either end, once the stream for a byte-stream value is established, it MUST
233+
be exposed to the application as follows:
234+
235+
* After sending each of those streams, each write operation by the application
236+
to a byte-stream field MUST trigger the sending of a single data frame on the
237+
corresponding stream.
238+
239+
* Each read operation by the application from a byte-stream field MUST yield
240+
the content of the next data frame received on the corresponding stream. If the
241+
reading end of the stream is closed, the read operation MUST yield EOF.
242+
243+
* A close operation by the application on the a byte-stream field MUST trigger
244+
the closing of the writing end of the corresponding SPDY stream.
245+
246+
## Message encoding
247+
A message may be any type which supported by the libchan encoder. A libchan
248+
message encoder must support encoding raw byte stream types as well as channels.
249+
In addition to the libchan data types, time must also be encoded as a custom
250+
type to increase portability of the protocol.
251+
252+
Currently supported message encoders are msgpack and soon CBOR.
253+
254+
### Custom Types
255+
Each custom type defines a type code and the byte layout to represent that type.
256+
Directions of descriptions are from the point of view of the endpoint encoding.
257+
All multi-byte integers are encoded big endian. The length of bytes of the
258+
encoded value will be provided by the encoding format, allowing integer values
259+
to be variable length.
260+
261+
| Type | Code | Byte Layout|
262+
|---|---|---|
263+
| Duplex Byte Stream | 1 | 4 or 8 byte integer identifier |
264+
| Inbound Byte Stream | 2 | 4 or 8 byte integer identifier |
265+
| Outbound Byte Stream | 3 | 4 or 8 byte integer identifier |
266+
| Inbound channel | 4 | 4 or 8 byte integer identifier |
267+
| Outbound channel | 5 | 4 or 8 byte integer identifier |
268+
| time | 6 | 8 byte integer seconds + 4 byte integer nanoseconds |
269+
270+
## Version History
271+
272+
0.2.0
273+
- Define stream provider to support multiple multiplexing protocols
274+
- Support CBOR in addition to Msgpack for channel message encoding
275+
- Add extended type codes definition
276+
- Require byte-streams to send *"libchan-parent-ref"*
277+
- Allow byte-streams as duplex or half-duplex
278+
- Add channel synchronization through ack definition
279+
- Add channel errors
280+
- Update description of relationship to Go channels
281+
282+
0.1.0
283+
- Initial specification
284+
- Message channels
285+
- Nested message channels
286+
- Duplex byte streams
287+
- Msgpack channel message encoding
288+
- SPDY stream multiplexing

0 commit comments

Comments
 (0)