This document describes how a Cobalt client connects to an already existing Cobalt island somewhere on the network ======= 1. Introduction ======= To connect to an island, a client needs information about how to find that island. This information is contained in an instance of TPostcard. TPostcards are typically created out of XML joinme strings or data returned from an LDAP server. ======= 2. Establishing basic connectivity ======= Connecting consists of the following steps: 0. Have a TPostcard on hand 1. Resolve the TPostcard into a TContact 2. Connect to a Cobalt dispatcher running at some address and port (given by the TContact instance) 3. Connect to a specific router running on the dispatcher 0. Have a TPostcard on hand A client begins connecting by calling the method CroquetHarness >> openConnectionTo: with its TPostcard instance. 1. Resolve the TPostcard into a TContect A TPostcard is a high-level description of where to find a router, but a TContact actually specifies where on the network it may be. The harness resolves the TPostcard into a TContact by calling TPathNameResolver >> rosolveContactByPostcard:ifAbsent: (from within CroquetHarness >> openConnectionTo:ifUnavailable:). I won't go into how that works, as it's outside the scope of this document. 2. Connect to a Cobalt dispatcher Once it has a TContact to work with, the harness creates a CobaltController to manage the high-level connection with the island [TContact >> setupContactHarness, CobaltHarness >> connectTo:port:sessionID]. The controller, in turn allocates a CobaltControllerConnection to handle the low-level communication with the router. The first step is simple: the connection opens a TCP socket to the IP address and port specified in the TContact [TMessageRelay >> connect]. 3. Connect to a Cobalt message router A dispatcher does nothing but connect clients to routers, and also may create routers, depending on configuration. To connect to a router, the client sends a 128-bit session identifier on the newly-opened TCP connection with the dispatcher [TMessageRelay >> connectTo:port:sessionID:]. If the router is unknown to the dispatcher, it may create one with that ID, depending on configuration [TSessionDispatcher >> dispatchConnection:sessionID:]. The router then allocates a CobaltMessageRouterClient to handle the low-level communication with the client [TMessageRouter >> acceptConnectionFrom:]. ======= 3. Teatime Protocol ======= The client is now connected to the router thru a TCP socket. An instance of CobaltControllerConnection handles the protocol details on the client end, and an instance of CobaltMessageRouterClient handles the details on the router side. From this point forward, the protocol is symmetric; CobaltControllerConnection and CobaltMessageRouterClient share a common subclass: TMessageRelay. The TMessageRelay protocol is an ordered datagram protocol. The client and router exchange datagrams one after the other, with nothing in between, over the TCP socket the client and dispatcher established. The structure of the datagram is this [TMessageRelay >> runReaderProcess, TMessageRelay >> sendDatagram:]: bits 0 8 16 24 31 bytes 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 0 | Size (32-bit big-endian unsigned integer) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 4 | CRC Checksum (32-bit big-endian unsigned integer) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 8 | | + + 16 | | + Facet ID (128-bit UUID) + 24 | | + + 32 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 48 | | . . . Payload ( bytes) . . . | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ The Facet ID and Payload may be encrypted by an ARC4 cipher. The Size and Checksum never are Size: the size of the payload, in bytes. Checksum: CRC checksum of the (decrypted) Facet ID and Payload fields. Facet ID: an identifier for a payload interpreter Payload: Uninterpreted raw data of bytes. A facet is a method that accepts two arguments: [TMessageRelay >> invokeFacet:with:] 1. The payload from the datagram 2. The relay it was delivered to message format. If identifies a facet the receiver understands, the facet will interpret the payload Each facet has a name and an identifier. The name is a simple string, and the identifier is a 128-bit UUID. A Cobalt router understands the following facets: Reciever (Router) Reciever Name (Router) ID Sender (Client) Sender Name (Client) Creator ------------------------------------------------------------------------------------------------------------------------------------------------ CobaltMessageRouter >> requestRouterInfo:from: CobaltController class >> requestAuthServicesFacet a554dfd6b22eb0708bbf938da499be82 CobaltController >> requestRouterInfo CobaltController class >> requestAuthServicesFacet CobaltMessageRouter >> initializeFacets CobaltMessageRouter >> login:from: CobaltAuthService class >> loginFacet bbd9dab1b2cb31cf9837d3b463cfe931 CobaltAuthService >> login:password:controller: CobaltAuthService class >> loginFacet CobaltMessageRouter >> initializeWithConfig: CobaltMessageRouter >> identity:from: CobaltMessageRouterClient ivar identifyFacet CoPublicKeyAuthService >> challenge:from:controller: temporary identifyFacet CoPublicKeyAuthService >> login:from:router: CobaltMessageRouter >> listFacets:from: TMessageRouterClient ivar listFacet CobaltAuthService >> challenge:from:controller: temporary listFacet CobaltAuthService >> login:from:router: TMessageRouter >> join:from: facet joinFacet TRemoteController >> join facet join TMessageRouter >> initializeFacets TMessageRouter >> leave:from: facet leaveFacet TRemoteController >> leave facet leave TMessageRouter >> initializeFacets TMessageRouter >> sync:from: facet syncFacet TRemoteController >> sync: facet sync TMessageRouter >> initializeFacets CobaltMessageProxy >> sync:from: controller facet sync -- PROXY FORWARD -- TMessageRouterClient >> syncReply:from: TRemoteController >> serve:from: temporary facet TMessageRouter >> sync:from: TMessageRouter >> heartbeat:from: facet heartbeatFacet TRemoteController >> heartbeat: facet heartbeat TMessageRouter >> initializeFacets TMessageRouter >> beServer:from: facet beServerFacet TRemoteController >> beServer facet beServer TMessageRouter >> initializeFacets TMessageRouter >> send:from: facet sendFacet TRemoteController >> sendMessage: facet send TMessageRouter >> initializeFacets TMessageRouter >> timeStamp:from: facet timeStampFacet TRemoteController >> routerStamp: facet timeStamp TMessageRouter >> initializeFacets ------------------------------------------------------------------------------------------------------------------------------------------------ A Cobalt client understands the following facets: Reciever (Client) Reciever Name (Client) ID Sender (Router) Sender Name (Router) Creator ------------------------------------------------------------------------------------------------------------------------------------------------ CobaltController >> routerInfo:from: facet routerInfo CobaltMessageRouter >> requestRouterInfo:from: temporary routerInfoFacet CobaltController >> requestRouterInfo CobaltController >> challenge:from: facet challenge CobaltAuthService >> login:from:router: temporary challengeFacet CobaltAuthService >> login:password:controller: CobaltController >> recvFacets:from: facet recvFacets CobaltAuthService >> listFacetsTo:from:router: temporary responseFacet CobaltAuthService >> challenge:from:controller: TRemoteController >> joinComplete:from: facet joinComplete TMessageRouter >> join:from: temporary respFacet TRemoteController >> join TRemoteController >> sync:from: facet syncFrom TMessageRouterClient >> syncReply:from: TMessageRouterClient ivar syncFacet TRemoteController >> sync: TRemoteController >> serve:from: facet serve TMessageRouter >> sync:from: TMessageRouterClient ivar serveFacet TRemoteController >> beServer TRemoteController >> recv:from: facet recv TMessageRouter >> dispatchMessage: TMessageRouterClient ivar recvFacet TRemoteController >> join TRemoteController >> tick:from: facet tick TMessageRouterClient >> sendTick TMessageRouterClient ivar tickFacet TRemoteController >> heartbeat: TRemoteController >> stampComplete:from: facet stampComplete TMessageRouter >> timeStamp:from: temporary replyFacet TRemoteController >> routerStamp: ------------------------------------------------------------------------------------------------------------------------------------------------ ======= 4. Connecting to an island ======= Continuing from section 2, the client still needs to do the following before it is really in an island, or session. There is exactly one island per router 4. Authenticate to the router All of the client-side steps to connect to an island are in CobaltHarness >> connectToIslandAt:port:sessionId: - Router installs core facets for the connection, but doesn't tell the client about them [...] Note: I think the router uses the same facet ID's for the core facets of every connection (join, send, sync, etc...). This is a security hole, as it could allow a logged-in client to give other clients access to facets the router hasn't authorized them to use. Check how CobaltMessageRouterClient is created in the router. - Client sends router a requestRouterInfo datagram [CobaltController >> requestRouterInfo] - Router sends client a routerInfo datagram [CobaltMessageRouter >> requestRouterInfo:from:] - This causes the promise p in CobaltHarness >> connectTo:port:sessionID: to resolve - Client sends router a login datagram [CobaltController >> loginWithCredential:] - If authentication fails, the router closes the TCP socket [TMessageRouterClient >> destroy] - Router installs list facet [CobaltAuthService >> login:from:router:] - Router sends client a challenge datagram [CobaltMessageRouterClient >> loginData:] - Router begins sending and only accepting encrypted traffic [CobaltAuthService >> login:from:router:] - Client decodes the challenge datagram and installs the encryption keys [CobaltAuthService >> challenge:from:controller:] - Client sends router a list datagram (or identify if using public key authentication). The client has to successfully decode the encryption keys and the facet ID from the challenge datagram in order to do this. [CobaltAuthService >> challenge:from:controller:] - Router authenticates the user if an identity datagram was sent, and closes the socket if auth failed [CoPublicKeyAuthService >> identity:from:router:] - Router collects a list of facets the user is allowed to send to [CobaltAuthService >> facetsForUser:authServices:]. Current demo implementations require this to send all facets regardless of user, namely: join, send, sync, heartbeat, beServer, leave, and timeStamp [CobaltAuthService >> allFacets, CobaltController >> recvFacets:from:] - Router packs the facets into a recvFacets datagram and sends it to the client [CobaltAuthService >> challenge:from:controller:] - Client installs the facets and resolves the login promise CobaltHarness >> connectToIslandAt:port:sessionID: is waiting on [CobaltController >> recvFacets:from:] 5. Connect to the island We're still in CobaltHarness >> connectToIslandAt:port:sessionID: - Client sends router a join datagram [CobaltController >> join] - Router sends client a joinComplete datagram [TMessageRouter >> join:from:] - Client begins receiving a stream of update messages from the router and buffering them [TRemoteController >> recv:from:] - Client sends router a sync datagram [CobaltController >> sync] - Router forwards the sync datagram to a server in the form of a serve datagram [TMessageRouter >> sync:from:] - Server sends the router a snapshot of the island in a SyncReply datagram [TRemoteController >> serve:from:] - Router forwards the sync reply datagram to the client in the form of a syncFrom datagram [TMessageRouterClient >> syncReply:from:] - Client leads the snapshot and discards all messages that are older than the snapshot, and waits for a newer one to arrive [TRemoteController >> install:] - The server that provided the snapshot sends a no-op message thru the router (Object >> yourself), which will be later than the snapshot [TRemoteController >> serve:from:] - Client recieves a message later than the snapshot (which is probably, but not necessarily, the one the server just sent. There may be other traffic on the island). The client executes the message and is done installing the snapshot. [TRemoteController >> install:] - Client sends a heartbeat datagram to the router requesting tick messages be sent every 20 milliseconds [TRemoteController >> heartbeat:] 6: tell the harness about the island - addController: and addIsland:postcard: at the end of CroquetHarness >> openConnectionTo:ifUnavailable 7: teleport or make a portal to the island - TPostcard >> makePortalForAvatar:portalType: - The avatar sends (or at least recieves) 3 messages during CroquetHarness >> gotoSnapshot: ======= 5. Details about each facet ======= requestRouterInfo Payload bits 0 8 16 24 31 bytes 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 0 | | + + 4 | | + Reply Facet ID (128-bit UUID) + 8 | | + + 12 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ requestRouterInfo reply Revocation: on send Payload bits 0 8 16 24 31 bytes 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 0 | | . . . DataStream encoded reply ( bytes) . . . end | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ The DataStream encodes an Array with the following data [CobaltMessageRouter >> requestRouterInfo:from, CobaltController >> unpackRouterInfo:]: objOut 1. dispatcher dispatcherInfo 1. dispatcher uuid 2. dispatcher internalContactInfo 3. dispatcher externalContactInfo 2. CobaltMessageRouter >> authServicesInfo 1. authServices first listForController 1. authService class name 1. authService serviceName 2. authServices second listForController 3. etc... login Payload bits 0 8 16 24 31 bytes 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 0 | Version ID (2 bytes: 0x0100) | n = Service Name Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 4 | | . . . Authentication Service Name (UTF-8 string of n bytes) . . . | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 4+n | | + + 8+n | | + Response Facet ID (128 bits) + 12+n | | + + 16+n | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 20+n | | . . . Authentication Payload (-n-20 bytes) . . . end | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ length: 2-byte big-endian unsigned integer < 1024 Service name must be LocalSecret Auth Payload [CobaltLocalSecretTestAuthService >> login:pass:controller:, bits 0 8 16 24 31 bytes 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 0 | | . . . Username (UTF-8 encoded string of bytes) . . . end | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Challenge (Router -> Client) Revocation: on receive Payload is ARC4 encrypted with the user's password or public key bits 0 8 16 24 31 bytes 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 0 | | + + 4 | | + Send Session Key (128 bits) + 8 | | + + 12 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 16 | | + + 20 | | + Receive Session Key (128 bits) + 24 | | + + 28 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 32 | | + + 36 | | + Next Facet ID (128-bit UUID) + 40 | | + + 44 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Next facet is either list (for most authentication services), or identify (for public key authentication) List (Client -> Router) bits 0 8 16 24 31 bytes 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 0 | | + + 4 | | + Receive Facets Facet ID (128-bit UUID) + 8 | | + + 12 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Identify (Client -> Router) bits 0 8 16 24 31 bytes 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 0 | | + + 4 | | + Receive Facets Facet ID (128-bit UUID) + 8 | | + + 12 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 16 | n = Username Length | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 20 | | . . . Username (UTF-8 string of n bytes) . . . | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 18+n | m = Password Length | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 22+n | | . . . Password (UTF-8 string of m bytes) . . . end | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ m, n < 1024 [String >> asLVRouterData] recvFacets (Router -> Client) bits 0 8 16 24 31 bytes 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 0 | | + + 4 | | + Join Facet ID (128 bits) + 8 | | + + 12 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 16 | | + + 20 | | + Send Facet ID (128 bits) + 24 | | + + 28 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 32 | | + + 36 | | + Sync Facet ID (128 bits) + 40 | | + + 44 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 48 | | + + 52 | | + Heartbeat Facet ID (128 bits) + 56 | | + + 60 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 64 | | + + 68 | | + BeServer Facet ID (128 bits) + 72 | | + + 76 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 80 | | + + 84 | | + Leave Facet ID (128 bits) + 88 | | + + 92 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 96 | | + + 100 | | + TimeStamp Facet ID (128 bits) + 104 | | + + 108 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Join (Client -> Router) bits 0 8 16 24 31 bytes 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 0 | | + + 4 | | + Recv Facet ID (128 bits) + 8 | | + + 12 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 16 | | + + 20 | | + Response Facet ID (128 bits) + 24 | | + + 28 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ JoinComplete (router -> client) no payload Sync (Client -> Router) bits 0 8 16 24 31 bytes 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 0 | | + + 4 | | + Response Facet ID (on client) (128 bits) + 8 | | + + 12 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 16 | | + + 20 | Session ID (for sync requests) (128 bits) | + - or - + 24 | Resource ID (for resource requests) (128 bits) | + + 28 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Serve (Router -> Server) bits 0 8 16 24 31 bytes 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 0 | | + + 4 | | + Response Facet ID (on router) (128 bits) + 8 | | + + 12 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 16 | | + + 20 | Session ID (for sync requests) (128 bits) | + - or - + 24 | Resource ID (for resource requests) (128 bits) | + + 28 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ SyncReply (Server -> Router) bits 0 8 16 24 31 bytes 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 0 | | . TSnapshotWriter-encoded island snapshot (for sync requests) . . - or - . . ??? (for resource requests) . | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ SyncFrom (Router -> Client) bits 0 8 16 24 31 bytes 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 0 | | . TSnapshotWriter-encoded island snapshot (for sync requests) . . - or - . . ??? (for resource requests) . | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Heartbeat (Client -> Router) bits 0 8 16 24 31 bytes 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 0 | | + + 4 | | + Response Facet ID (128 bits) + 8 | | + + 12 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 16 | tickPeriod (milliseconds) (32-bit big-endian unsigned integer)| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Tick (Router -> Client) bits 0 8 16 24 31 bytes 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 0 | | + timeStamp (IEEE 754 double-precision floating point number) + 4 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Leave (Client -> Router) no payload also causes the router to close the tcp connection timeStamp (Client -> Router) bits 0 8 16 24 31 bytes 0 1 2 3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 0 | | + + 4 | | + Response Facet ID (128 bits) + 8 | | + + 12 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 16 | | + timeStamp (IEEE 754 double-precision floating point number) + 20 | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ stampComplete (Router -> Client) no payload ========= 6. Authority in Teatime ========== Note: none of this is yet implemented (as of 9 November 2010) These are use cases we want to support via policy on the router: Note: an auth service (using its name in the code, CobaltAuthService) is short for "Authentication Service"; it is responsible for verifying a users identity. It is more or less independent from an authority service (not yet implemented) that is responsible for knowing what resources, facets, or other actions a user has access to. such a service is just code that the router and controller share to know how to handle a particular type of authentication or authorization. The service may or may not contact an external provider over the network as part of its duties. We may need to give them more distinguishable names, like id service or login service (for what is currently named auth service) and authority service (for what this section is about) --- 1. Anonymous join --- 1. controller requests router to set its imported receive facet id 2. router verifies that anonymous is allowed to join 3. router responds OK, and sets the receive facet 4. router starts sending --- 2. Identified join --- Same protocol steps as anonymous --- 3. Encrypt and join --- 1. controller requests router to set its imported receive facet id 2. router finds that anonymous is allowed to join, but requires the connection to be encrypted 2. router responds Authorization Required, with a null authentication scheme that starts encryption 3. Client runs the login scheme and issues STARTTLS 4. ** Then a miracle occurs ** 5. over the encrypted channel, the annonymous controller re-issues the set receive facet request id 6. router responds OK, and sets the receive facet 7. router starts sending --- 4. Identify and join --- 1. controller requests router to set its imported receive facet id 2. router finds that anonymous is not allowed to join 2. router responds Authorization Required with information about which auth services are supported 3. Client chooses a login facility, gathers the required information from the user, and runs the auth service 4. Client re-issues the request to set receive facet, possibly over an encrypted channel or with some sort of cookie the router provided from the auth service ====== 5.2 Types of requests ======= There are two types of requests to deal with facets: request for export: Ask the router to give you the ability to send a certain type of message. Example: send, request for import: Ask the router to send you certain types of messages. Example: join, beServer, heartbeat --- 1. Anonymous join --- --- 1. Anonymous join --- --- 1. Anonymous join --- --- 1. Anonymous join --- --- 1. Anonymous join --- --- 1. Anonymous join --- ===== 5.3 Moderation ======= Note: This section is heavily influenced by the policies of Jabber Multi-user chat, as described in XEP-0045: http://xmpp.org/extensions/xep-0045.html A member of a cobalt session on a router has two authorization types: role, and affiliation Affiliation is persistent across sessions for the same island, and is stored on a server outside the router and queried by the router thru a cgi script who's address is set in the cobalt-router.conf file. The affiliation types are: -1. outcast 0. none 1. member 2. admin 3. owner These are mapped from the userid the user provided by the cgi script. Currently, the userid's are unauthentecated and the list must be modified out-of-band by anyone with access to the appropriate server. Roles are associated with the session, and do not persist across sessions. They are: 0. none (not really a role; user is not connected to the island) 1. visitor 2. participant 3. moderator users with owner and admin affiliation receive moderator role by default; everybody else receives visitor or participant role by default, depending on router configuration ==== 5.7 Things that need changing on the client to implement all this ==== Change sign on to not use anonymous credentials by default; for now, just always bring up the login dialog box ==== 5.8 Things that need changing on the router to implement all this ==== add a role and affiliation ivar to TMessageRouterClient Implement a way to list users connected to a router; probably need a per-user per-session session ID in TMessageRouterClient; there is already a session id sent by TRemoteController in all message sends, I could possibly reuse; currently it's only use is for identifying messages sent by oneself for the purpose of statistics generation. Implement a facet for listing users connected to the router. To enable full annonymity; the only required thing in the list should be each user's session id, but the username should be provided too if allowed implement a facet for changing role of a session id. May be used as a request by visitors for increasing priveledges temporarily, and could also include a message send to be released on moderator approval. Implement a client facet for the router to send moderators stuff to approve. To prevent the router from having to queue up requests, it can send the request in it's entirety and trust the moderators to not tamper with it when submitting it after approval high-level presence notification is explicitly not part of the router protocol; the only presence notifications the router sends are link-local facet change notifications. Any higher level presence notification will be the responsibility of the island and embedded in the replicated message stream by some other process. This enables moderation at a course-grained level of basic ability to do things. Moderation at the per-message level, which proxies are expected to do, will be done later.