Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
C
capnproto
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
capnproto
Commits
f10950d6
Commit
f10950d6
authored
Sep 16, 2013
by
Kenton Varda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Initial RPC protocol definition.
parent
b6cbcb65
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
908 additions
and
0 deletions
+908
-0
rpc-confined.capnp
c++/src/capnp/rpc-confined.capnp
+105
-0
rpc.capnp
c++/src/capnp/rpc.capnp
+803
-0
No files found.
c++/src/capnp/rpc-confined.capnp
0 → 100644
View file @
f10950d6
# Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@0xa184c7885cdaf2a1;
# This file defines the "network-specific parameters" in rpc.proto to support a network consisting
# of two vats: a container, and the vat it contains. The container may actually be a full sandbox
# that confines the inner app. The contained app can only speak to the outside world through the
# container. Therefore, from the point of view of the containee, the container represents the
# whole world and all capabilities in the rest of the world appear "hosted" by the container.
# Meanwhile, the container proxies any capabilities exported by the containee, and thus the rest of
# the world only sees the container, and treats it as if it were itself the host of all of the
# containee's objects.
#
# Since there are only two vats in this network, there is never a need for three-way introductions.
# Joins _could_ be needed in cases where the container itself participates in a network that uses
# joins, as the container may export two objects to the containee which, unbeknownst to it, are
# actually proxies of the same remote object. However, from the containee's point of view, such
# a join is trivial to request, and the containee never needs to receive join requests.
#
# Therefore, a level 3 implementation of the confined network is barely more complicated than a
# level 1 implementation. However, such an implementation is able to make use of the container's
# implementation of whatever network it lives in. Thus, if you have an application that implements
# the confined network at level 3, your application can participate in _any_ network at level 3 so
# long as you pair it with the appropriate container.
#
# The "confined" network protocol may also be a reasonable basis for simple two-party client-server
# interactions, where the server plays the part of the container.
using Cxx = import "c++.capnp";
$Cxx.namespace("capnp::rpc::confined");
struct SturdyRef {
union {
external @0 :Object;
# The object lives outside the container. The container can handle `Restore` requests for this
# ref. The content of the ref is defined by the container implementation and opaque to the
# containee. The container ensures that the ref is encoded in such a way that the containee
# cannot possibly derive the original bits of the external-world reference, probably by storing
# the external ref on a table somewhere. See:
# http://www.erights.org/elib/capability/dist-confine.html
confined @1 :Object;
# The object lives inside the container -- it is implemented by the contained app. That app
# handles `Restore` requests for this ref. The content of the ref is defined by the app
# and opaque to the container. The container shall ensure that the raw bits of this ref are
# not revealed to the outside world, so as long as the app trusts the container, it need not
# worry about encrypting or signing the ref.
}
}
struct ProvisionId {
# Only used for joins, since three-way introductions never happen on a two-party network.
joinId @0 :UInt32;
# The ID from `JoinKeyPart`.
}
struct RecipientId {}
# Never used, because there are only two parties.
struct ThirdPartyCapId {}
# Never used, because there is no third party.
struct JoinKeyPart {
joinId @0 :UInt32;
# A number identifying this join, chosen by the contained app. Since the container will never
# issue a join _to_ the contained app, all ongoing joins across the whole (two-vat) network are
# uniquely identified by this ID.
partCount @1 :UInt16;
# The number of capabilities to be joined.
partNum @2 :UInt16;
# Which part this request targets -- a number in the range [0, partCount).
}
struct JoinHostId {
joinId @0 :UInt32;
# Matches `JoinKeyPart`.
succeeded @1 :Bool;
# All JoinHostIds in the set will have the same value for `succeeded`. The container actually
# implements the join by waiting for all the `JoinKeyParts` and then performing its own join on
# them, then going back and answering all the join requests afterwards.
}
c++/src/capnp/rpc.capnp
0 → 100644
View file @
f10950d6
# Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@0xb312981b2552a250;
# Recall that Cap'n Proto RPC allows messages to contain references to remote objects that
# implement interfaces. These references are called "capabilities", because they both designate
# the remote object to use and confer permission to use it.
#
# Recall also that Cap'n Proto RPC has the feature that when a method call itself returns a
# capability, the caller can begin calling methods on that capability _before the first call has
# returned_. The caller essentially sends a message saying "Hey server, as soon as you finish
# that previous call, do this with the result!". Cap'n Proto's RPC protocol makes this possible.
# As a result, it is more complicated than most.
#
# Cap'n Proto RPC is based heavily on CapTP:
# http://www.erights.org/elib/distrib/captp/index.html
#
# Cap'n Proto RPC takes place between "vats". A vat hosts some set of capabilities and talks to
# other vats through direct bilateral connections. Typically, there is a 1:1 correspondence
# between vats and processes (in the unix sense of the word), although this is not strictly always
# true (one process could run multiple vats, or a distributed vat might live across many processes).
#
# Cap'n Proto does not distinguish between "clients" and "servers" -- this is up to the application.
# Either end of any connection can potentially hold capabilities pointing to the other end, and
# can call methods on those capabilities. In the doc comments below, we user the words "sender"
# and "receiver". These refer to the sender and receiver of an instance of the struct or field
# being documented. Sometimes we refer to a "third-party" which is neither the sender nor the
# receiver.
#
# It is generally up to the vat network implementation to securely verify that connections are made
# to the intended vat as well as to encrypt transmitted data for privacy and integrity. See the
# `VatNetwork` example interface near the end of this file.
#
# Once a connection is formed, nothing interesting can happen until one side sends a Restore frame
# to obtain a persistent capability.
#
# Unless otherwise specified, messages must be delivered to the receiving application in the same
# order in which they were initiated by the sending application, just like in E:
# http://erights.org/elib/concurrency/partial-order.html
#
# Since the full protocol is complicated, we define multiple levels of support which an
# implementation may target. Comments in this file indicate which level requires the corresponding
# feature to be implemented -- if unspecified, the feature must be implemented at level 1.
#
# * **Level 1:** The implementation supports simple bilateral interaction, but interactions between
# three or more parties are supported only via proxying of objects. E.g. if Alice wants to send
# Bob a capability pointing to Carol, Alice must host a local proxy of Carol and send Bob a
# reference to that; Bob cannot form a direct connection to Carol. Level 1 implementations do
# not support "join" or "eq" across capabilities received from different vats, although they
# should be supported on capabilities received from the same vat.
#
# * **Level 2:** The implementation supports three-way interactions but does not implement "Join"
# operations. The implementation can be used effectively on networks that do not require joins,
# or to implement objects that never need to be joined.
#
# * **Level 3:** The entire protocol is implemented, including joins.
#
# Note that an implementation must also support specific networks (transports), as described in
# the "Network-specific Parameters" section below. An implementation might have different levels
# depending on the network used.
#
# New implementations of Cap'n Proto should start out targeting the simplistic "confined" network
# type as defined in `rpc-confined.capnp`. With this network type, "Level 2" is irrelevant and
# "Level 3" is much easier than usual to implement. When such an implementation is actually run
# inside a container, the contained app effectively gets to make full use of the container's
# network at level 3. And since Cap'n Proto IPC is extremely fast, it may never make sense to
# bother implementing any other network protocol -- just use the correct container type and get
# it for free.
using Cxx = import "c++.capnp";
$Cxx.namespace("capnp::rpc");
# ========================================================================================
# The Four Tables
#
# As in CapTP, for each open connection, a vat maintains four tables: questions, answers, imports,
# and exports. See the diagram at:
# http://www.erights.org/elib/distrib/captp/4tables.html
#
# The question table corresponds to the other end's answer table, and the imports table corresponds
# to the other end's exports table. Additionally, the export table is further subdivited into
# promises and capabilities. A promise will eventually be resolved and replaced with a capability,
# although the eventual replacement capability might live in some other vat and so may not be on
# the same table.
#
# IDs in the questions/answers tables are chosen by the questioner and generally represent method
# calls that are in progress.
#
# IDs in the imports/exports table are chosen by the exporter and generally represent objects on
# which methods may be called. Exports may be "settled", meaning the exported object is an actual
# object living in the exporter's vat, or they may be "promises", meaning the exported object is
# the unknown result of an ongoing operation and will eventually be resolved to some other object
# once that operation completes. Calls made to a promise will be forwarded to the eventual target
# once it is known.
#
# IDs can be reused over time. To make this safe, we carefully define the lifetime of IDs. Since
# messages using the ID could be traveling in both directions simultaneously, we must define the
# end of life of each ID _in each direction_. The ID is only safe to reuse once it has been
# released by both sides.
using QuestionId = UInt32;
# Identifies a question in the questions/answers table. The questioner (caller) chooses an ID
# when making a call. The ID remains valid in caller -> callee messages until a ReleaseAnswer
# message is sent, and remains valid in callee -> caller messages until a Return message is sent.
using ExportId = UInt32;
# Identifies an exported capability or promise in the exports/imports table. The exporter chooses
# an ID before sending a capability over the wire. If the capability is already in the table, the
# exporter should reuse the same ID. If the ID is a promise (as opposed to a settled capability),
# this must be indicated at the time the ID is transmitted; in this case, the importer shall
# expect a later Resolve frame which replaces the promise.
#
# ExportIds are subject to reference counting on the importing end. When an `ExportId` is received
# embedded in an question or answer, the export has an implicit reference until that question or
# answer is released (questions are released by `Return`, answers are released by `ReleaseAnswer`).
# Such an export can be retained beyond that point by including it in the `retainedCaps` list
# at the time the question/answer is released, thus incrementing its reference count. The
# reference count is later decremented by a `Release` message. Since the `Release` message can
# specify an arbitrary number by which to reduce the reference count, the importer should usually
# batch reference decrements and only send a `Release` when it believes the refererce count has
# hit zero.
#
# When an `ExportId` is received as part of a Frame but not embedded in a quention or answer, its
# reference count is automatically incremented unless otherwise specified.
#
# An `ExportId` remains valid in importer -> exporter messages until its reference count reaches
# zero and a `Release` message has been sent to release it.
# ========================================================================================
# Frames
struct Frame {
# An RPC connection is a bi-directional stream of Frames.
#
# When the RPC system wants to send a Frame, it instructs the transport layer to keep trying to
# send the frame until the transport can guarantee that one of the following is true:
# 1) The Frame was received, possibly multiple times.
# 2) The sessing is broken, and no further Frames can be sent.
# 3) The RPC system has asked to stop trying to send the Frame (e.g. because the call was canceled
# or timed out).
#
# In case 1, the Frame may have been received multiple times. In cases 2 and 3, it may have been
# received any number of times, including zero. The RPC system must handle all of these cases
# with some modicum of grace. Moreover, applications should design their interfaces such that
# methods are idempotent.
union {
# Level 1 features -----------------------------------------------
call @0 :Call; # Begin a method call.
return @1 :Return; # Complete a method call.
resolve @2 :Resolve; # Resolve a previously-sent promise.
release @3 :Release; # Release a capability so that the remote object can be deallocated.
releaseAnswer @4 :ReleaseAnswer; # Release a returned answer / cancel a call.
restore @5 :Restore; # Restore a persistent capability from a previous connection.
# Level 2 features -----------------------------------------------
provide @6 :Provide; # Provide a capability to a third party.
accept @7 :Accept; # Accept a capability provided by a third party.
# Level 3 features -----------------------------------------------
join @8 :Join; # Directly connect to the common root of two or more proxied caps.
}
}
struct Call {
# Frame type initiating a method call on a capability.
questionId @0 :QuestionId;
# A number, chosen by the caller, which identifies this call in future messages. This number
# must be different from all other calls originating from the same end of the connection (but
# may overlap with call IDs originating from the opposite end). A fine strategy is to use
# sequential call IDs, but the recipient should not assume this.
#
# TODO: Decide if it is safe to reuse a call ID. If not, extend to 64 bits.
target :union {
exportedCap @1 :ExportId;
# This call is to a capability or promise previously exported by the receiver.
promisedAnswer @2 :PromisedAnswer;
# This call is to a capability that is expected to be returned by another call that has not
# yet been completed.
}
interfaceId @3 :UInt64;
# The type ID of the interface being called. Each capability may implement multiple interfaces.
methodId @4 :UInt16;
# The ordinal number of the method to call within the requested interface.
request @5 :Object;
# The request struct. The fields of this struct correspond to the parameters of the method.
#
# The request may contain capabilities. These capabilities are automatically released when the
# call returns *unless* the Return frame explicitly indicates that they are being retained.
}
struct Return {
# Frame type sent from callee to caller indicating that the call has completed.
questionId @0 :QuestionId;
# Question ID which is being answered, as specified in the corresponding Call.
retainedCaps @1 :List(ExportId);
# List of capabilities from the request to which the callee continues to hold references. Any
# other capabilities from the request are implicitly released.
union {
answer @2 :Object;
# Result object. If the method returns a struct, this is it. Otherwise, this points to
# a struct which contains exactly one field, corresponding to the method's return type.
# (This implies that an method's return type can be upgraded from a non-struct to a struct
# without breaking wire compatibility.)
#
# If the response contains any capabilities, the caller is expected to send a Release frame for
# each one when done with them.
exception @3 :Exception;
# Indicates that the call failed and explains why.
canceled @4 :Void;
# Indicates that the call was canceled due to the caller sending a ReleaseAnswer message
# before the call had completed.
}
}
struct Resolve {
# Frame type sent to indicate that a previously-sent promise has now been resolved to some other
# object (possibly another promise) -- or broken, or canceled.
promiseId @0 :ExportId;
# The ID of the promise to be resolved.
#
# Unlike all other instances of `ExportId` sent from the exporter, the `Resolve` message does
# _not_ increase the reference count of `promiseId`.
#
# When a promise ID is first sent over the wire (e.g. in a `CapDescriptor`), the sender (exporter)
# guarantees that it will follow up at some point with exactly one `Resolve` message. If the
# same `promiseId` is sent again before `Resolve`, still only one `Resolve` is sent. If the
# same ID is reused again later _after_ a `Resolve`, it can only be because the export's
# reference count hit zero in the meantime and the ID was re-assigned to a new export, therefore
# this later promise does _not_ correspond to the earlier `Resolve`.
#
# If a promise ID's reference count reaches zero before a `Resolve` is sent, the `Resolve`
# message must still be sent, and the ID cannot be reused in the meantime. Moreover, the object
# to which the promise resolved itself needs to be released, even if the promise was already
# released before it resolved. (Although, the exporter may notice that the promise was released
# and send a `canceled` resolution, in which case nothing new is exported.)
#
# RPC implementations should keep in mind when they receive a `Resolve` that the promise ID may
# have appeared in a previous question or answer which the application has not consumed yet.
# The RPC implementation usually shouldn't dig through the question/answer itself looking for
# capabilities, so it won't be aware of any promise IDs in that message until the application
# actually goes through and extracts the capabilities it wishes to retain. Therefore, when
# a `Resolve` is received, the RPC implementation will have to keep track of this resolution
# at least until all previously-received questions and answers have been consumed and released
# by the application.
union {
cap @1 :CapDescriptor;
# The object to which the promise resolved.
exception @2 :Exception;
# Indicates that the promise was broken.
canceled @3 :Void;
# Indicates that this promise won't be resolved because its reference count reached zero before
# it had completed, so the operation was canceled.
}
}
struct Release {
# Frame type sent to indicate that the sender is done with the given capability and the receiver
# can free resources allocated to it.
id @0 :ExportId;
# What to release.
referenceCount @1 :UInt32;
# The number of times this ID has been received by the importer. The object is only truly
# released when the referenceCount from all Release messages for the ID adds up to the number of
# times the exporter has actually sent the object to the importer. This avoids a race condition
# where the exporter happens to send another copy of the same ID at the same time as the importer
# is sending a Release message for it.
}
struct ReleaseAnswer {
# Frame type sent from the caller to the callee to indicate:
# 1) The questionId will no longer be used in any messages sent by the callee (no further
# pipelined requests).
# 2) Any capabilities in the answer other than the ones listed below should be implicitly
# released.
# 3) If the answer has not returned yet, the caller no longer cares about the answer, so the
# callee may wish to immediately cancel the operation and send back a Return message with
# "canceled" set.
questionId @0 :QuestionId;
# ID of the question whose answer is to be released.
retainedCaps @1 :List(ExportId);
# List of capabilities from the answer to which the callee continues to hold references. Any
# other capabilities from the answer that need to be released are implicitly released along
# with the answer itself.
}
struct Restore {
# Frame type sent to restore a persistent capability obtained during a previous connection, or
# through other means.
questionId @0 :QuestionId;
# A new question ID identifying this restore message, which will eventually receive a Return
# message containing the restored capability.
ref @1 :SturdyRef;
# An object designating the capability to restore.
}
struct Provide {
# **Level 2 feature**
#
# Frame type sent to indicate that the sender wishes to make a particular capability implemented
# by the receiver available to a third party for direct access (without the need for the third
# party to proxy through the sender).
#
# (In CapTP, `Provide` and `Accept` are methods of the global `NonceLocator` object exported by
# every vat. In Cap'n Proto, we bake this into the core protocol.)
questionId @0 :QuestionId;
# Question ID to be held open until the recipient has received the capability. An answer will
# be returned once the third party has successfully received the capability. The sender must
# at some point send a ReleaseAnswer message as with any other call, and such a message can be
# used to cancel the whole operation.
target :union {
# What is to be provided to the third party.
exportedCap @1 :ExportId;
# An exported capability.
promisedAnswer @2 :PromisedAnswer;
# A capability expected to be returned in the answer to an outstanding question.
}
recipient @3 :RecipientId;
# Identity of the third party which is expected to pick up the capability.
}
struct Accept {
# **Level 2 feature**
#
# Frame type sent to pick up a capability hosted by the receiving vat and provided by a third
# party. The third party previously designated the capability using `Provide`.
questionId @0 :QuestionId;
# A new question ID identifying this accept message, which will eventually receive a Return
# message containing the provided capability.
provision @1 :ProvisionId;
# Identifies the provided object to be picked up.
}
struct Join {
# **Level 3 feature**
#
# Frame type sent to implement E.join(), which, given a number of capabilities which are expected
# to be equivalent, finds the underlying object upon which they all agree and forms a direct
# connection to it, skipping any proxies which may have been constructed by other vats while
# transmitting the capability. See:
# http://erights.org/elib/equality/index.html
#
# Note that this should only serve to bypass fully-transparent proxies -- proxies that were
# created merely for convenience, without any intention of hiding the underlying object.
#
# For example, say Bob holds two capabilities hosted by Alice and Carol, but he expects that both
# are simply proxies for a capability hosted elsewhere. He then issues a join request, which
# operates as follows:
# - Bob issues Join requests on both Alice and Carol. Each request contains a different piece
# of the JoinKey.
# - Alice is proxying a capability hosted by Dana, so forwards the request to Dana's cap.
# - Dana receives the first request and sees that the JoinKeyPart is one of two. She notes that
# she doesn't have the other part yet, so she records the request and responds with a
# JoinAnswer.
# - Alice relays the JoinAswer back to Bob.
# - Carol is also proxying a capability from Dana, and so forwards her Join request to Dana as
# well.
# - Dana receives Carol's request and notes that she now has both parts of a JoinKey. She
# combines them in order to form information needed to form a secure connection to Bob. She
# also responds with another JoinAnswer.
# - Bob receives the responses from Alice and Carol. He uses the returned JoinHostIds to
# determine how to connect to Dana and attempts to form the connection. Since Bob and Dana now
# agree on a secret key which neither Alice nor Carol ever saw, this connection can be made
# securely even if Alice or Carol is conspiring against the other. (If Alice and Carol are
# conspiring _together_, they can obviously reproduce the key, but this doesn't matter because
# the whole point of the join is to verify that Alice and Carol agree on what capability they
# are proxying.)
#
# If the two capabilities aren't actually proxies of the same object, then the join requests
# will come back with conflicting `hostId`s and the join will fail before attempting to form any
# connection.
questionId @0 :QuestionId;
# Question ID used to respond to this Join. (Note that this ID only identifies one part of the
# request for one hop; each part has a different ID and relayed copies of the request have
# (probably) different IDs still.)
capId @1 :ExportId;
# The capability to join.
keyPart @2 :JoinKeyPart;
# A part of the join key. These combine to form the complete join key which is used to establish
# a direct connection.
struct Answer {
# The answer to a `Join` frame (sent in a `Return` frame).
hostId @0 :JoinHostId;
# Information indicating where to connect to complete the join. Also used to verify that all
# the joins actually reached the same object.
vineId @1 :ExportId;
# A new capability in the sender's export table which must be released once the join is
# complete. This capability has no methods. This allows the joined capability's host to
# detect when a Join has failed and release the associated resources on its end.
}
}
# ========================================================================================
# Common structures used in frames
struct CapDescriptor {
# When an application-defined type contains an interface pointer, that pointer's encoding is the
# same as a struct pointer except that the bottom two bits are 1's instead of 0's. The pointer
# actually points to an instance of `CapDescriptor`. The runtime API should not reveal the
# CapDescriptor directly to the application, but should instead wrap it in some kind of callable
# object with methods corresponding to the interface that the capability implements.
#
# Keep in mind that `ExportIds` in a `CapDescriptor` are subject to reference counting. See the
# description of `ExportId`.
union {
senderHosted :group {
# A capability newly exported by the sender.
id @0 :ExportId;
# The ID of the new capability in the sender's export table (receiver's import table).
interfaces @1 :List(UInt64);
# Type IDs of interfaces supported by this descriptor. This must include at least the
# interface type as defined in the schema file, but could include others. The runtime API
# should allow the interface wrapper to be dynamically cast to these other types (probably
# not using the language's built-in cast syntax, but something equivalent).
}
senderPromise @2 :ExportId;
# A promise which the sender will resolve later. The sender will send exactly one Resolve
# message at a future point in time to replace this promise.
receiverHosted @3 :ExportId;
# A capability (or promise) previously exported by the receiver.
receiverAnswer @4 :PromisedAnswer;
# A capability expected to be returned in the answer for a currently-outstanding question posed
# by the sender.
thirdPartyHosted @5 :ThirdPartyCapDescriptor;
# A capability that lives in neither the sender's nor the receiver's vat. The sender needs
# to form a direct connection to a third party to pick up the capability.
}
sturdyRef @6 :SturdyRef;
# If non-null, this is a SturdyRef that can be used to store this capability persistently and
# restore access to in in the future (using a `Restore` frame). If null, this capability will be
# lost if the connection dies. Generally, application interfaces should define when a client can
# expect a capability to be persistent (and therefore have a SturdyRef attached). However,
# application protocols should never embed SturdyRefs directly, as various infrastructure like
# transports, gateways, and sandboxes may need to be aware of SturdyRefs being passed over the
# wire in order to transform them into different namespaces.
}
struct PromisedAnswer {
questionId @0 :QuestionId;
# ID of the question whose answer is expected to contain the capability.
path @1 :List(UInt16);
# Path to the capability in the response. This is a list of indexes into the pointer
# sections of structs forming a path from the root struct to a particular capability. Each
# pointer except for the last one must point to another struct -- it is an error if one ends
# up pointing to a list or something else.
#
# TODO(someday): Would it make sense to support lists in the path, and say that the method
# should be executed on every element of the list?
}
struct ThirdPartyCapDescriptor {
# Identifies a capability in a third-party vat which the sender wants the receiver to pick up.
id @0 :ThirdPartyCapId;
# Identifies the third-party host and the specific capability to accept from it.
vineId @1 :ExportId;
# Object in the sender's export table which must be Released once the capability has been
# successfully obtained from the third-party vat. This allows the sender to ensure that the
# final capability is not released before the receiver manages to accept it. In CapTP
# terminology this is called a "vine", because it is an indirect reference to the third-party
# object that snakes through the sender vat. The vine does not accept any method calls.
}
struct Exception {
reason @0 :Text;
# Human-readable failure description.
isPermanent @1 :Bool;
# In the best estimate of the error source, is this error likely to repeat if the same call is
# executed again? Callers might use this to decide when to retry a request.
isOverloaded @2 :Bool;
# In the best estimate of the error source, is it likely this error was caused by the system
# being overloaded? If so, the caller probably should not retry the request now, but may
# consider retrying it later.
nature @3 :Nature;
# The nature of the failure. This is intended mostly to allow classification of errors for
# reporting and monitoring purposes -- the caller is not expected to handle different natures
# differently.
enum Nature {
# These correspond to kj::Exception::Nature.
precondition @0;
localBug @1;
osError @2;
networkFailure @3;
other @4;
}
}
# ========================================================================================
# Network-specific Parameters
#
# Some parts of the Cap'n Proto RPC protocol are not specified here because different vat networks
# may wish to use different approaches to solving them. For example, on the public internet, you
# may want to authenticate vats using public-key cryptography, but on a local intranet with trusted
# infrastructure, you may be happy to authenticate based on network address only, or some other
# lightweight mechanism.
#
# To accommodate this, we specify several "parameter" types. Each type is defined here as an
# alias for `Object`, but a specific network will want to define a specific set of types to use.
# All vats in a vat network must agree on these parameters in order to be able to communicate.
# Inter-network communication can be accomplished through "gateways" that perform translation
# between the primitives used on each network; these gateways may need to be deeply stateful,
# depending on the translations they perform.
#
# For interaction over the global internet between parties with no other prior arrangement, a
# particular set of bindings for these types is defined elsewhere. (TODO(soon): Specify where
# these common definitions live.)
#
# Another common network type is the "confined" network, in which a contained vat interacts with
# the outside world entirely through a container/supervisor. All objects in the world that aren't
# hosted by the contained vat appear as if they were hosted by the container. This network type is
# interesting because from the containee's point of view, there are no three-party interactions at
# all, and joins are unusually simple to implement, so implementing at level 3 is barely more
# complicated than implementing at level 1. Moreover, if you pair an app implementing the confined
# network with a container that implements some other network, the app can then participate on
# the container's network just as if it implemented that network directly. The types used by the
# "confined" network are defined in `rpc-confined.capnp`.
#
# The things which we need to parameterize are:
# - How to authenticate vats in three-party introductions.
# - How to implement `Join`.
# - How to store capabilities long-term without holding a connection open.
#
# Three-party interactions
# ------------------------
#
# **Level 2 feature**
#
# In cases where more than two vats are interacting, we have situations where VatA holds a
# capability hosted by VatB and wants to send that capability to VatC. This can be accomplished
# by VatA proxying requests on the new capability, but doing so has two big problems:
# - It's inefficient, requiring an extra network hop.
# - If VatC receives another capability to the same object from VatD, it is difficult for VatC to
# detect that the two capabilities are really the same and to implement the E "join" operation,
# which is necessary for certain four-or-more-party interactions, such as the escrow pattern.
# See: http://www.erights.org/elib/equality/grant-matcher/index.html
#
# Instead, we want a way for VatC to form a direct, authenticated connection to VatB.
#
# Join
# ----
#
# **Level 3 feature**
#
# The `Join` frame type and corresponding operation arranges for a direct connection to be formed
# between the joiner and the host of the joined object, and this connection must be authenticated.
# Thus, the details are network-dependent.
#
# Persistent references
# ---------------------
#
# We want to allow some capabilities to be stored long-term, even if a connection is lost and later
# recreated. ExportId is a short-term identifier that is specific to a connection, so it doesn't
# help here. We need a way to specify long-term identifiers, as well as a strategy for
# reconnecting to a referenced capability later.
using SturdyRef = Object;
# Identifies a long-lived capability which can be obtained again in a future connection by sending
# a `Restore` frame. The base RPC protocol does not specify under what conditions a SturdyRef can
# be restored. For example:
# - Do you have to connect to a specific vat to restore the reference?
# - Is just any vat allowed to restore the SturdyRef, or is it tied to a specific vat requiring
# some form of authentication?
using ProvisionId = Object;
# **Level 2 feature**
#
# The information which must be sent in an `Accept` frame to identify the object being accepted.
#
# In a network where each vat has a public/private key pair, this could simply be the public key
# fingerprint of the provider vat along with the questionId used in the `Provide` frame sent from
# that provider.
using RecipientId = Object;
# **Level 2 feature**
#
# The information which must be sent in a `Provide` frame to identify the recipient of the
# capability.
#
# In a network where each vat has a public/private key pair, this could simply be the public key
# fingerprint of the recipient.
using ThirdPartyCapId = Object;
# **Level 2 feature**
#
# The information needed to connect to a third party and accept a capability from it.
#
# In a network where each vat has a public/private key pair, this could be a combination of the
# third party's public key fingerprint, hints on how to connect to the third party (e.g. an IP
# address), and the question ID used in the corresponding `Provide` frame sent to that third party
# (used to identify which capability to pick up).
using JoinKeyPart = Object;
# A piece of a secret key. One piece is sent along each path that is expected to lead to the same
# place. Once the pieces are combined, a direct connection may be formed between the sender and
# the receiver, bypassing any men-in-the-middle along the paths. See the "Join" frame type.
#
# The motivation for Joins is discussed under "Supporting Equality" in the "Unibus" protocol
# sketch: http://www.erights.org/elib/distrib/captp/unibus.html
#
# In a network where each vat has a public/private key pair and each vat forms no more than one
# connection to each other vat, Joins will rarely -- perhaps never -- be needed, as objects never
# need to be transparently proxied and references to the same object sent over the same connection
# have the same export ID. Thus, a successful join requires only checking that the two objects
# come from the same connection and have the same ID, and then completes immediately.
#
# However, in networks where two vats may form more than one connection between each other, or
# where proxying of objects occurs, joins are necessary.
#
# Typically, each JoinKeyPart would include a fixed-length data value such that all value parts
# XOR'd together forms a shared secret which can be used to form an encrypted connection between
# the joiner and the joined object's host. Each JoinKeyPart should also include an indication of
# how many parts to expect and a hash of the shared secret (used to match up parts).
using JoinHostId = Object;
# Information needed by the joiner in order to form a direct connection to a joined object. One
# of these is returned in response to each `Join` frame. This might simply be the address of the
# joined object's host vat, since the `JoinKey` has already been communicated so the two vats
# already have a shared secret to use to authenticate each other.
#
# The `JoinHostId` should also contain information that can be used to detect when the Join
# requests ended up reaching different objects, so that this situation can be detected easily.
# This could be a simple matter of including a sequence number -- if the joiner receives two
# `JoinHostId`s with sequence number 0, then they must have come from different objects and the
# whole join is a failure.
# ========================================================================================
# Network interface sketch
#
# The interfaces below are meant to be pseudo-code to illustrate how the details of a particular
# vat network might be abstracted away. They are written like Cap'n Proto interfaces, but in
# practice you'd probably define these interfaces manually in the target programming language. A
# Cap'n Proto RPC implementation should be able to use these interfaces without knowing the
# definitions of the various network-specific parameters defined above.
# interface Network {
# # Represents a vat network, with the ability to connect to particular vats and receive
# # connections from vats.
# #
# # Note that methods returning a `Connection` may return a pre-existing `Connection`, and the
# # caller is expected to find and share state with existing users of the connection.
#
# # Level 1 features -----------------------------------------------
#
# connectToHostOf(ref :SturdyRef) :Connection;
# # Connect to a host which can restore the given SturdyRef. The transport should return a
# # promise which does not resolve until authentication has completed, but allows messages to be
# # pipelined in before that; the transport either queues these messages until authenticated, or
# # sends them encrypted such that only the authentic vat would be able to decrypt them. The
# # latter approach avoids a round trip for authentication.
# #
# # Once connected, the caller should start by sending a `Restore` frame.
#
# acceptConnectionAsRefHost() :Connection;
# # Wait for the next incoming connection and return it. Only connections formed by
# # connectToHostOf() are returned by this method.
# #
# # Once connected, the first received frame will usually be a `Restore`.
#
# # Level 3 features -----------------------------------------------
#
# newJoiner(count :UInt32): NewJoinerResponse;
# # Prepare a new Join operation, which will eventually lead to forming a new direct connection
# # to the host of the joined capability. `count` is the number of capabilities to join.
#
# struct NewJoinerResponse {
# joinKeyParts :List(JoinKeyPart);
# # Key parts to send in Join frames to each capability.
#
# joiner :Joiner;
# # Used to establish the final connection.
# }
#
# interface Joiner {
# addHostId(hostId :JoinHostId) :Void;
# # Add a host ID at which the host might live. All `JoinHostId`s returned from all paths
# # must be added before trying to connect.
#
# connect() :ConnectionAndProvisionId;
# # Try to form a connection to the joined capability's host, verifying that it has received
# # all of the JoinKeyParts. Once the connection is formed, the caller should send an `Accept`
# # frame on it with the specified `ProvisionId` in order to receive the final capability.
# }
#
# acceptConnectionFromJoiner(parts: List(JoinKeyPart), paths :List(VatPath))
# :ConnectionAndProvisionId;
# # Called on a joined capability's host to receive the connection from the joiner, once all
# # key parts have arrived. The caller should expect to receive an `Accept` frame over the
# # connection with the given ProvisionId.
# }
#
# interface Connection {
# # Level 1 features -----------------------------------------------
#
# send(frame :Frame) :Void;
# # Send the frame. Returns successfully when the frame (and all preceding frames) has been
# # acknowledged by the recipient.
#
# receive() :Frame;
# # Receive the next frame, and acknowledges receipt to the sender. Frames are received in the
# # order in which they are sent.
#
# # Level 2 features -----------------------------------------------
#
# introduceTo(recipient :Connection) :IntroductionInfo;
# # Call before starting a three-way introduction, assuming a `Provide` frame is to be sent on
# # this connection and a `ThirdPartyCapId` is to be sent to `recipient`.
#
# struct IntroductionInfo {
# sendToRecipient :ThirdPartyCapId;
# sendToTarget :RecipientId;
# }
#
# connectToIntroduced(capId: ThirdPartyCapId) :ConnectionAndProvisionId;
# # Given a ThirdPartyCapId received over this connection, connect to the third party. The
# # caller should then send an `Accept` frame over the new connection.
#
# acceptIntroducedConnection(recipientId: RecipientId): Connection
# # Given a RecipientId received in a `Provide` frame on this `Connection`, wait for the
# # recipient to connect, and return the connection formed. Usually, the first frame received
# # on the new connection will be an `Accept` frame.
# }
#
# sturct ConnectionAndProvisionId {
# connection :Connection;
# # Connection on which to issue `Accept` frame.
#
# provision :ProvisionId;
# # `ProvisionId` to send in the `Accept` frame.
# }
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment