Consider magic-wormhole connections as having two layers: the generic setup of a channel (or many subchannels in Dilation) and an "application layer" on top (such as file-transfer).
Most "application layer" protocols will want a way to gracefully end their session.
(I use "session" here to include Dilation because those may span many TCP connections).
The file-transfer protocol has an implicit end-of-session: any offer that is exchanged is the only one, so no more mailbox communication will happen after that.
However, the next-generation file transfer (see magic-wormhole/magic-wormhole-protocols#23 ) for example needs some way to indicate that the entire session is over.
I expect all Dilation protocols will have a similar need. In Dilation specifically, we could choose to declare that closing the control-channel is this "session is over" signal. However: that doesn't work for non-Dilated protocols; and the control-channel may never exist (because of network conditions). So, we need a mailbox-level message here.
Another consideration (thanks @warner) is synchronization around close signals. In subchannels in Dilation, there is a "half-closed" state where side A has sent all the data they intend to, and sent a CLOSE message. So side A knows side B will see all their data before the CLOSE. Similarly, since side A will wait for side B's CLOSE message, they now they'll see all of side B's data. Maybe it is possible to extend this synchronization to the mailbox itself.
Currently, the w.close() API sends a CLOSE message to the relay, which responds with a CLOSED (but doesn't, for example, send a CLOSED to the other side). So an option could be to re-use this signal -- amend the protocol so that either side could receive a CLOSED message that isn't in response to a CLOSE. This kind of breaks the symmetry of close/closed though.
I think an explicit mailbox-level message + phase should be added to the protocol. This can be used by both "classic" Transit-using (or even mailbox-only) protocols and by Dilation. It also allows proper recover in "journaled" mode, I believe.
The rough idea is this:
- one side (call it
A) sends its final mailbox message
- it then sends a
phase=closing mailbox message with body {"closing": true}
- after sending such a message, that side MUST NOT post any more mailbox messages (that is, no more number-phase application messages)
- upon seeing the
phase=closing message, side B posts any remaining mailbox messages as then a final phase=closing message with body {"closed": true}.
- side
B MUST close its mailbox immediately after this (sends CLOSE to mailbox-server, gets CLOSED back) -- it knows side A will produce no new messages
- side
A receives any additional mailbox messages from side B (maybe none)
- side
A gets a phase=closing with {"closed": true} from side B
- side
A now knows it has all mailbox messages, and MUST immediately close its mailbox (because it knows the other side has done this also)
- the session is now concluded
Consider magic-wormhole connections as having two layers: the generic setup of a channel (or many subchannels in Dilation) and an "application layer" on top (such as file-transfer).
Most "application layer" protocols will want a way to gracefully end their session.
(I use "session" here to include Dilation because those may span many TCP connections).
The file-transfer protocol has an implicit end-of-session: any offer that is exchanged is the only one, so no more mailbox communication will happen after that.
However, the next-generation file transfer (see magic-wormhole/magic-wormhole-protocols#23 ) for example needs some way to indicate that the entire session is over.
I expect all Dilation protocols will have a similar need. In Dilation specifically, we could choose to declare that closing the control-channel is this "session is over" signal. However: that doesn't work for non-Dilated protocols; and the control-channel may never exist (because of network conditions). So, we need a mailbox-level message here.
Another consideration (thanks @warner) is synchronization around
closesignals. In subchannels in Dilation, there is a "half-closed" state where side A has sent all the data they intend to, and sent a CLOSE message. So side A knows side B will see all their data before the CLOSE. Similarly, since side A will wait for side B's CLOSE message, they now they'll see all of side B's data. Maybe it is possible to extend this synchronization to the mailbox itself.Currently, the
w.close()API sends a CLOSE message to the relay, which responds with a CLOSED (but doesn't, for example, send a CLOSED to the other side). So an option could be to re-use this signal -- amend the protocol so that either side could receive aCLOSEDmessage that isn't in response to aCLOSE. This kind of breaks the symmetry of close/closed though.I think an explicit mailbox-level message + phase should be added to the protocol. This can be used by both "classic" Transit-using (or even mailbox-only) protocols and by Dilation. It also allows proper recover in "journaled" mode, I believe.
The rough idea is this:
A) sends its final mailbox messagephase=closingmailbox message with body{"closing": true}phase=closingmessage, sideBposts any remaining mailbox messages as then a finalphase=closingmessage with body{"closed": true}.BMUST close its mailbox immediately after this (sends CLOSE to mailbox-server, gets CLOSED back) -- it knows sideAwill produce no new messagesAreceives any additional mailbox messages from sideB(maybe none)Agets aphase=closingwith{"closed": true}from sideBAnow knows it has all mailbox messages, and MUST immediately close its mailbox (because it knows the other side has done this also)