cannam@132: # Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors cannam@132: # Licensed under the MIT License: cannam@132: # cannam@132: # Permission is hereby granted, free of charge, to any person obtaining a copy cannam@132: # of this software and associated documentation files (the "Software"), to deal cannam@132: # in the Software without restriction, including without limitation the rights cannam@132: # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell cannam@132: # copies of the Software, and to permit persons to whom the Software is cannam@132: # furnished to do so, subject to the following conditions: cannam@132: # cannam@132: # The above copyright notice and this permission notice shall be included in cannam@132: # all copies or substantial portions of the Software. cannam@132: # cannam@132: # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR cannam@132: # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, cannam@132: # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE cannam@132: # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER cannam@132: # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, cannam@132: # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN cannam@132: # THE SOFTWARE. cannam@132: cannam@132: @0xa184c7885cdaf2a1; cannam@132: # This file defines the "network-specific parameters" in rpc.capnp to support a network consisting cannam@132: # of two vats. Each of these vats may in fact be in communication with other vats, but any cannam@132: # capabilities they forward must be proxied. Thus, to each end of the connection, all capabilities cannam@132: # received from the other end appear to live in a single vat. cannam@132: # cannam@132: # Two notable use cases for this model include: cannam@132: # - Regular client-server communications, where a remote client machine (perhaps living on an end cannam@132: # user's personal device) connects to a server. The server may be part of a cluster, and may cannam@132: # call on other servers in the cluster to help service the user's request. It may even obtain cannam@132: # capabilities from these other servers which it passes on to the user. To simplify network cannam@132: # common traversal problems (e.g. if the user is behind a firewall), it is probably desirable to cannam@132: # multiplex all communications between the server cluster and the client over the original cannam@132: # connection rather than form new ones. This connection should use the two-party protocol, as cannam@132: # the client has no interest in knowing about additional servers. cannam@132: # - Applications running in a sandbox. A supervisor process may execute a confined application cannam@132: # such that all of the confined app's communications with the outside world must pass through cannam@132: # the supervisor. In this case, the connection between the confined app and the supervisor might cannam@132: # as well use the two-party protocol, because the confined app is intentionally prevented from cannam@132: # talking to any other vat anyway. Any external resources will be proxied through the supervisor, cannam@132: # and so to the contained app will appear as if they were hosted by the supervisor itself. cannam@132: # cannam@132: # Since there are only two vats in this network, there is never a need for three-way introductions, cannam@132: # so level 3 is free. Moreover, because it is never necessary to form new connections, the cannam@132: # two-party protocol can be used easily anywhere where a two-way byte stream exists, without regard cannam@132: # to where that byte stream goes or how it was initiated. This makes the two-party runtime library cannam@132: # highly reusable. cannam@132: # cannam@132: # Joins (level 4) _could_ be needed in cases where one or both vats are participating in other cannam@132: # networks that use joins. For instance, if Alice and Bob are speaking through the two-party cannam@132: # protocol, and Bob is also participating on another network, Bob may send Alice two or more cannam@132: # proxied capabilities which, unbeknownst to Bob at the time, are in fact pointing at the same cannam@132: # remote object. Alice may then request to join these capabilities, at which point Bob will have cannam@132: # to forward the join to the other network. Note, however, that if Alice is _not_ participating on cannam@132: # any other network, then Alice will never need to _receive_ a Join, because Alice would always cannam@132: # know when two locally-hosted capabilities are the same and would never export a redundant alias cannam@132: # to Bob. So, Alice can respond to all incoming joins with an error, and only needs to implement cannam@132: # outgoing joins if she herself desires to use this feature. Also, outgoing joins are relatively cannam@132: # easy to implement in this scenario. cannam@132: # cannam@132: # What all this means is that a level 4 implementation of the confined network is barely more cannam@132: # complicated than a level 2 implementation. However, such an implementation allows the "client" cannam@132: # or "confined" app to access the server's/supervisor's network with equal functionality to any cannam@132: # native participant. In other words, an application which implements only the two-party protocol cannam@132: # can be paired with a proxy app in order to participate in any network. cannam@132: # cannam@132: # So, when implementing Cap'n Proto in a new language, it makes sense to implement only the cannam@132: # two-party protocol initially, and then pair applications with an appropriate proxy written in cannam@132: # C++, rather than implement other parameterizations of the RPC protocol directly. cannam@132: cannam@132: using Cxx = import "/capnp/c++.capnp"; cannam@132: $Cxx.namespace("capnp::rpc::twoparty"); cannam@132: cannam@132: # Note: SturdyRef is not specified here. It is up to the application to define semantics of cannam@132: # SturdyRefs if desired. cannam@132: cannam@132: enum Side { cannam@132: server @0; cannam@132: # The object lives on the "server" or "supervisor" end of the connection. Only the cannam@132: # server/supervisor knows how to interpret the ref; to the client, it is opaque. cannam@132: # cannam@132: # Note that containers intending to implement strong confinement should rewrite SturdyRefs cannam@132: # received from the external network before passing them on to the confined app. The confined cannam@132: # app thus does not ever receive the raw bits of the SturdyRef (which it could perhaps cannam@132: # maliciously leak), but instead receives only a thing that it can pass back to the container cannam@132: # later to restore the ref. See: cannam@132: # http://www.erights.org/elib/capability/dist-confine.html cannam@132: cannam@132: client @1; cannam@132: # The object lives on the "client" or "confined app" end of the connection. Only the client cannam@132: # knows how to interpret the ref; to the server/supervisor, it is opaque. Most clients do not cannam@132: # actually know how to persist capabilities at all, so use of this is unusual. cannam@132: } cannam@132: cannam@132: struct VatId { cannam@132: side @0 :Side; cannam@132: } cannam@132: cannam@132: struct ProvisionId { cannam@132: # Only used for joins, since three-way introductions never happen on a two-party network. cannam@132: cannam@132: joinId @0 :UInt32; cannam@132: # The ID from `JoinKeyPart`. cannam@132: } cannam@132: cannam@132: struct RecipientId {} cannam@132: # Never used, because there are only two parties. cannam@132: cannam@132: struct ThirdPartyCapId {} cannam@132: # Never used, because there is no third party. cannam@132: cannam@132: struct JoinKeyPart { cannam@132: # Joins in the two-party case are simplified by a few observations. cannam@132: # cannam@132: # First, on a two-party network, a Join only ever makes sense if the receiving end is also cannam@132: # connected to other networks. A vat which is not connected to any other network can safely cannam@132: # reject all joins. cannam@132: # cannam@132: # Second, since a two-party connection bisects the network -- there can be no other connections cannam@132: # between the networks at either end of the connection -- if one part of a join crosses the cannam@132: # connection, then _all_ parts must cross it. Therefore, a vat which is receiving a Join request cannam@132: # off some other network which needs to be forwarded across the two-party connection can cannam@132: # collect all the parts on its end and only forward them across the two-party connection when all cannam@132: # have been received. cannam@132: # cannam@132: # For example, imagine that Alice and Bob are vats connected over a two-party connection, and cannam@132: # each is also connected to other networks. At some point, Alice receives one part of a Join cannam@132: # request off her network. The request is addressed to a capability that Alice received from cannam@132: # Bob and is proxying to her other network. Alice goes ahead and responds to the Join part as cannam@132: # if she hosted the capability locally (this is important so that if not all the Join parts end cannam@132: # up at Alice, the original sender can detect the failed Join without hanging). As other parts cannam@132: # trickle in, Alice verifies that each part is addressed to a capability from Bob and continues cannam@132: # to respond to each one. Once the complete set of join parts is received, Alice checks if they cannam@132: # were all for the exact same capability. If so, she doesn't need to send anything to Bob at cannam@132: # all. Otherwise, she collects the set of capabilities (from Bob) to which the join parts were cannam@132: # addressed and essentially initiates a _new_ Join request on those capabilities to Bob. Alice cannam@132: # does not forward the Join parts she received herself, but essentially forwards the Join as a cannam@132: # whole. cannam@132: # cannam@132: # On Bob's end, since he knows that Alice will always send all parts of a Join together, he cannam@132: # simply waits until he's received them all, then performs a join on the respective capabilities cannam@132: # as if it had been requested locally. cannam@132: cannam@132: joinId @0 :UInt32; cannam@132: # A number identifying this join, chosen by the sender. May be reused once `Finish` messages are cannam@132: # sent corresponding to all of the `Join` messages. cannam@132: cannam@132: partCount @1 :UInt16; cannam@132: # The number of capabilities to be joined. cannam@132: cannam@132: partNum @2 :UInt16; cannam@132: # Which part this request targets -- a number in the range [0, partCount). cannam@132: } cannam@132: cannam@132: struct JoinResult { cannam@132: joinId @0 :UInt32; cannam@132: # Matches `JoinKeyPart`. cannam@132: cannam@132: succeeded @1 :Bool; cannam@132: # All JoinResults in the set will have the same value for `succeeded`. The receiver actually cannam@132: # implements the join by waiting for all the `JoinKeyParts` and then performing its own join on cannam@132: # them, then going back and answering all the join requests afterwards. cannam@132: cannam@132: cap @2 :AnyPointer; cannam@132: # One of the JoinResults will have a non-null `cap` which is the joined capability. cannam@132: # cannam@132: # TODO(cleanup): Change `AnyPointer` to `Capability` when that is supported. cannam@132: }