The purpose of this page is to describe the protocol used by the SubEthaEdit? collaborative editor. This information can then be used to interface other editors with SubEthaEdit?, for example Emacs.
For projects working on interfacing Emacs and SubEthaEdit? see:
Before getting on to the meat of this page: Please do not add any information of questionable source. This means that information gleaned from examination of the binary you got from http://codingmonkeys.de is right out, including simply running strings on it. What’s in is information gleaned by any of the methods described in http://samba.org/ftp/tridge/misc/french_cafe.txt -- specifically, anything that only involves running the program, and examining the information it sends across the wire. While it is my belief that reverse engineering for the purpose of interoperability is a protected right, it’s not 100% clear, esp as you go from jurisdiction to jurisdiction. I figure that if Andrew Tridgel feels OK saying in open court that he did these things against Microsoft, we’re probably safe from the presumably less litigious codingmonkeys.
The SubEthaEdit? protocol has a fair number of parts. You’ve probably heard that it uses Bonjour (AKA ZeroConf, Rendezvous). While this is true, its use of Bonjour isn’t interesting; it’s only useful when you have multiple users on the same subnet. In any case, it simply advertises <username>@<computername>._see, with no additional data that I can see. I have no particular interest in supporting this part of the protocol: sessions will be initiated by giving the hostname or IP and optionally port. (The default port is 6942, which codingmonkeys says can be remembered because 6*9=42 – it’s a Hitchhiker's Guide to the Galaxy reference.)
The actual conversations between SubEthaEdit? client and server are done on top of the BlocksExtensibleExchangeProtocol? (RFC 3080 and RFC 3081), which is a generic, channel multiplexed, transport protocol that uses XML and MIME. (Very simple versions of both in case of the SubEthaEdit? protocol – the only MIME header used is Content-Type.)
The BEEP protocol manages multiple virtual channels for independent data streams over a single connection. Messages in the special channel 0 are fully specified in RFC 3080, all other channels are freely usable by application protocols. Each channel has an associated profile, identified by an URI, that defines the protocol for messages exchanged on that channel.
The authors of SubEthaEdit? decided to ignore the recommendation of the RFC authors to use MIME. Instead, the payloads exchanged on the application channels consist of three-letter major command codes, mostly followed by three-letter minor command codes, often followed by bencoded data.
Note by a SEE author: we did this for the obvious reason that: a) having our own profiles means that doesn’t worry anybody else and b) since we use a lot of very small messages, the redundant MIME headers would just add up and use unnecessary bandwidth).
Messages generally look similar to the following example:
MSG 1 0 . 0 97 GRTd3:url27:see://codingmonkeys.de:67423:uid36:770da15c-0cb0-10da-a8ba-4a773bbddd094:vers3:200e END
Here, the bencoded data starts immediately after ‘GRT’. Bencoding is the data format used by bittorent; it is specified (very briefly) at http://www.bittorrent.com/protocol.html. The dialect that SubEthaEdit? uses has a small extension: Strings may have a ‘.’ (ASCII 0x2e) after the length instead of a ‘:’. These dot-strings are used for user-identifiers, and other such things that are not human-readable strings, but rather a bunch of random bytes. I suspect the correct interpretation is that ‘.’ strings contain binary data without semantics, whereas ‘:’ strings are always UTF-8. (Note that the length for strings of both types is in bytes, not logical characters.)
The following section describes the structure of a typical session (with encryption disabled). The roles “Initiating” and “Listening” peer refer to the network connection. Although the description here distinguishes these roles, the protocol seems to be symmetrical.
http://www.codingmonkeys.de/BEEP/SubEthaEditHandshake)SubEthaEditHandshake GRT messageSubEthaEditHandshake ACK messagehttp://www.codingmonkeys.de/BEEP/TCMMMStatus)TCMMMStatus USRCHG, FCAYES, STAVIS and USRRCH messageshttp://www.codingmonkeys.de/BEEP/TCMMMStatus)TCMMMStatus USRCHG, FCAYES, STAVIS, USRRCH messagesDOCANN messages for its documentshttp://www.codingmonkeys.de/BEEP/SubEthaEditSession)SubEthaEditSession JONJON messageSubEthaEditSession JONACK messageSubEthaEditSession SESINF messageSubEthaEditSession USRREQ replyAs soon as an initiating SEE instance has an open socket, it sends
<greeting> <profile uri="http://www.codingmonkeys.de/BEEP/SubEthaEditHandshake"/> <profile uri="http://www.codingmonkeys.de/BEEP/TCMMMStatus"/> <profile uri="http://www.codingmonkeys.de/BEEP/SubEthaEditSession"/> </greeting>
on BEEP channel 0, advertising support for three BEEP profiles:
The listening instance replies with an almost identical message, the difference being one additional profile:
Upon receiving this message from the listening end, the initiating client can request a SubEthaEditHandshake channel from the server:
MSG 0 1 . 301 136 Content-Type: application/beep+xml <start number="1"><profile uri="http://www.codingmonkeys.de/BEEP/SubEthaEditHandshake"/></start> END
or request TLS encryption:
MSG 0 1 . 263 135 Content-Type: application/beep+xml <start number='1'><profile uri='http://iana.org/beep/TLS'><![CDATA[<ready />]]></profile></start>END
GRT MessageGRT message that is identically structured but can have different dictionary values. Takes a dictionary with the following values: url ⇒ String. URL of the remote end of the connection -- sometimes has a see:// protocol on it, sometimes does not. Always seems to include the port. Example: see://codingmonkeys.de:6942. In the response from the listening peer, the semantics are reversed: it is the IP-address and port of the initiating peer. Example: 212.204.60.78:65164uid ⇒ UUID of… something. Undetermined if this is the client install, the user, or what. Normal hex, dash-separated format, without curly braces. (As specified in RFC 4122) Example: c26bff50-3f16-b1f6-e60c-8858649bdc56vers ⇒ String. The version of the protocol in use? Only known value is “200”.uag ⇒ String. Only present in the listening peer’s response (?). Probably “User Agent”. Seems to describe the software version. Example: SubEthaEdit/3.5.1 (3263) ACK MessageACK message of the initiating peer by sending a BEEP message that has no payload. This message seems to conclude the handshake.…more later.
A TCMMMStatus channel is created by both sides of the connection. The listening peer requests a TCMMMStatus channel after receiving the ‘ACK’ message on the handshake channel.
STAVIS MessageSTAINV MessageFCAYES MessageUSRCHG Messagecnt ⇒ An integer, presumably some sort of count (of what?)name ⇒ User name as stringuID ⇒ A 16 byte id string unique for the user (presumably)USRRCH Messageuid ⇒ 36 byte id string; Seems to equal to the id used in the GRT message of the SubEthaEditHandshake profile.url ⇒ URL with see:// schema, IP address and port of the initiating peer.USRREQ Messagename ⇒ StringuID ⇒ 16 byte id of the requested user (?)cnt ⇒ IntegerUSRFUL Messagecnt ⇒ An integer, counting something.. ?hue ⇒ As an integer, color of something, probably highlight color of this user’s textuID ⇒ Users unique ID, same as in USRCHGname ⇒ Users name, same as in USRCHGmail ⇒ Users email address, probably taken from Address Book dataPNG ⇒ The users icon, as a PNG file (contains the actual picture data as a byte string), probably taken from NetInfo? dataAIM ⇒ Users AIM screenname, probably taken from Address Book dataDOCANN MessageStarted by the client (either side?) to join a particular document.
JONJON MessageUserNotification ⇒ a dictionary containing:cnt ⇒ An integer, counting ?name ⇒ Users nameuID ⇒ Users unique IDSessionID ⇒ UUID denoting the session, see GRTJONACK MessageSESINF MessageContentLength ⇒ Integer - doc length?ReadWrite ⇒ a list of dictionaries containing:User ⇒ a dictionary containing:cnt ⇒ integer, counter (?)name ⇒ users nameuID ⇒ Users unique idSessionProperties ⇒ an empty dictionary ?User ⇒ ..Contributors ⇒ a list of dictionaries of:cntnameuIDDocumentSessionInformation ⇒ a dictionary containing:FileType ⇒ String. Only known value “SEETextType?”UseTabs ⇒ Integer (0/1)DocumentMode ⇒ String. Known values: “SEEMode.Base” | “SEEMode.Conference”LineEnding ⇒ Integer. Known values: 1TabWidth ⇒ Integer 4WrapLines ⇒ Integer. Known values: 1WrapMode ⇒ Integer. Known values: 0 | 1USRREQ MessageUSRREQ in
TCMMMStatus profile above.SESCON MessageTextStorage ⇒ dictionary containing:String ⇒ String containing document contentEncoding ⇒ Integer 30Attributes ⇒ dictionary containing:ChangedByUserID ⇒ list of dictionary containing:val ⇒ UUIDloc ⇒ Integer 0len ⇒ Integer 1508WrittenByUserID ⇒ list of dictionary containing:val ⇒ UUIDloc ⇒ Integerlen ⇒ IntegerDOCMSG Messageop ⇒ dictionary containing:ot ⇒ String. Known values: “sel” | “txt” | “nop”len ⇒ Integer (not present with ot ⇒ “nop”)uid ⇒ 16 byte user unique idloc ⇒ Integer (not present with ot ⇒ “nop”) - cursor locationstr ⇒ String that was added (only present when ot ⇒ “txt”)