-
Notifications
You must be signed in to change notification settings - Fork 7
Notes On The Design Of An Ocap Kernel
@Chip Morningstar
"The naming of names is a difficult matter, it isn't just one of your holiday games." -- not T. S. Eliot
We probably should pick a proper label for this thing we are creating. We've been referring to it as the "ocap kernel", but I think it's more than that. While our team is labeled "the ocap kernel team" (and I suspect the higher level management discussions that lead to our being organized and tasked with doing this probably used this language as well), there's a distinction to be made between the platform as a whole and the kernel per se. In particular, I expect we are headed for some kind of kernel+vats architecture. This suggests that there's almost certainly going to be a component that is the trusted supervisory code that lives inside the vat but above any user code. This component is clearly part of our platform but definitionally is not part of the kernel.
(Though I will note in passing that "ocap kernel" conveniently abbreviates as "OK", which might be a source of amusement as we contemplate the Naming Of Things.)
A further complication is that each user who is hosting some set of vats will be running a separate instance of our platform, and such instances probably also need a term to label them. An object inside a vat may exchange messages not only with objects in other vats on its same platform instance but also with objects in vats hosted by other users elsewhere on the internet. Consequently, we'll probably want a word meaning "all the vats hosted by some user". Also, I'm being a little loose here with the term "user". Our default mental model is of vats being hosted inside some person's web browser, and it's natural to think of that person as "the user", but of course we also want to consider vats being hosted by independent servers in some data center somewhere whose purpose is to provide services unrelated to any particular human -- it's the internet, after all. You could think of these servers (or their operators) as "users" in some sense, though at that point the language gets a little strained. And of course that's before we even start talking about message traffic exchanged with entities entirely outside our paradigm (via, say, HTTP or whatever), even if those entities remain part of the ecosystem we're concerned with.
So I'm just going to start inventing terminology and making up names for things. Don't get overly fixated on the language that I've chosen (despite the fact that I'm obsessing over it quite a bit here myself) but rather consider all these words provisional, subject to revision, elimination, or addition as we refine our design and get clearer in our own minds what we are doing. In this document I'm going to try to follow the convention writing any new bit of jargon in BOLD ALL CAPS the first time it appears (hopefully in the context of some kind of definition or explanation) and thereafter follow the Germanic convention of Capitalizing These Words when they're used.
Also, as I've been going I've been encountering unanswered design questions that I don't already have a strong opinion on. These are marked with OPEN DESIGN QUESTION in the text.
Everything here takes as a given that we're building on a common base of
Hardened JavaScript, the ocap security model, and various related concepts such
as compartments, eventual messaging, and so on, as provided by the SES shim or
the XS engine, plus various supporting packages in the Agoric endo repository
for things like bundling or marshaling. For purposes of this discussion, I'm
going to presume that everybody generally knows what these things are and is on
board with this approach rather than stopping to explain or justify these bits,
except when there are nuances that merit deeper discussion (ultimately, of
course, there will be more outward reaching documentation that explains All The
Things, but that's for later).
At the bottom we have OBJECTS. Because we are building in Hardened JavaScript, these are all JavaScript object, but for our purposes we further divide them into three varieties: DATA OBJECTS, BEHAVIORAL OBJECTS, and ORDINARY OBJECTS. This distinction is relevant in the context of marshaling and serialization, and in the places those are used, namely messaging and persistence.
-
Data Objects are passed by value; they are selfless (that is, they have no identity) and are described entirely by the bits from which they are composed. All own properties of Data Objects must be either (a) JavaScript primitive values (e.g., number, string, boolean), (b) References to Behavioral Objects, or (c) recursively nested Data Objects. Since Data Objects are passed by value, they cannot have methods (i.e., own properties that are functions) because these can't be serialized. The prototype of a Data Object must be
nullor the JavaScript built-in objectObject. -
Behavioral Objects are passed by reference. They have identity and may have methods, but can have no visible state (i.e., no JavaScript own properties other than methods). An empty object (i.e., one with no own properties at all) is considered a Behavioral Object since References to empty object instances are extremely useful as passable tokens (i.e., things that have identity and essentially nothing else).
-
Ordinary Objects are regular JavaScript objects that remain inside the vat. They may have any mixture of JavaScript properties whatsoever but are not passable.
Each Object resides within some VAT, an isolated process that can send and receive messages. When a message is sent from an object in a Vat to the world outside that Vat, the message may contain OBJECT REFERENCES that designate Behavioral Objects (these are typically, but by no means necessarily, contained by the Vat sending the message). When a message is received by a Vat from the outside world, the message may contain Object References that designate Behavioral Objects contained by other Vats. Such References can be used by code within the Vat as targets for future messages it might send.
Broadly speaking, a Vat consists of two parts: a SUPERVISOR and USER CODE.
The Supervisor is trusted, privileged code that is part of our platform. It is responsible for:
-
receiving, deserializing, and queueing messages inbound from other Vats, then, when appropriate according to our code execution model, dequeueing, demarshaling, and dispatching these to User Code.
-
accepting, marshaling, and queueing messages sent from User Code to other vats, then, when appropriate, dequeueing, serializing, and transmitting them to the outside world (specifically, transmitting them via the Kernel; more on this shortly).
-
overseeing persistent storage, both for the Supervisor's own use (e.g., holding the inbound and outbound message queues) and for the storage of persistent objects belonging to User Code.
-
providing other system level services to User Code as we see fit to provide, potentially including process termination, access to other communications channels (e.g., HTTP), clocks and timers, and so on. The list possible services that might be included here is open ended and potentially large, though of course we'll want to constrain and thoroughly specify it in the final design.
User Code is a bundle of arbitrary, user-provided JavaScript that is executed within a compartment that the Supervisor maintains for that purpose. In general, User Code is untrusted, except insofar as we might have some kind of vetting and certification procedure in place for a limited number of special, privileged applications to be granted special endowments that provide capabilities to perform selected sensitive operations that are normally closely held (e.g., access to the clock or to the browser DOM).
The Vats running on a given host are managed by a KERNEL. The Kernel and Vats together are called a CLUSTER (note: of all the vocabulary I've been defining here, this is the word I'd most like to have a better alternative for). The Kernel is responsible for:
-
creating, loading, starting, pausing, resuming, and stopping the Vats in its Cluster (note that a Vat might be created as the result of extrinsic user action or by the intrinsic action of another Vat via a service that the Kernel makes available to it for this purpose)
-
managing the granting of powers to specific Vats under the control of the user whose Cluster it is
-
providing a KERNEL MANAGEMENT API, enabling a user interface by which its associated user can control or configure the Kernel's operation and that of its Vats
-
conveying messages between the Vats that it is managing
-
receiving messages inbound from other Clusters and forwarding them to the appropriate Vats
-
accepting messages outbound from Vats in its Cluster that are targeted at objects hosted in other Clusters, and transmitting them over the network to those Clusters
-
translating inbound and outbound object references (contained in messages) between Vat-relative and global forms
-
Possibly: holding and managing the received message queues for any unsettled promises for which one of its Vats is the decider (OPEN DESIGN QUESTION: should responsibility for queueing messages to an unsettled promise be vested in the Kernel or in the deciding Vat? As a piece of Vat-associated state, there's obvious sense in having the Vat do it, but to the extent that promises can (and possibly often will) resolve to objects external to the deciding Vat, it might make sense to decouple this component of promise state from the Vat itself, in order to enable more efficient message forwarding, to limit certain kinds of potential Vat misbehavior, and ultimately to support automatic shortening of promise chains.)
A Cluster will be hosted either by a browser extension (where the primary UX will be presented in the browser itself via the web interface) or by a standalone Node executable (where the primary UX will most likely be command line arguments and configuration files). Whether it is presented as an interactive web page or as a CLI, we'll refer to the the controlling entity as the CONSOLE. The Console governs the operation of the Cluster via the Kernel. A Console itself consists of two parts:
-
A CONSOLE DAEMON, an independent process that maintains persistent console state independent of the execution state of the Kernel or any Vats. It interacts with the Kernel via the Kernel Management API. Note that the Console Daemon needs to be independent of the Kernel because one of its jobs will be launching the Cluster in the first place, prior to which there is) no Kernel. The Console Daemon in turn presents an API to the:
-
A CONSOLE USER INTERFACE, a possibly ephemeral process (or a series of such processes over time) that actually implements the user facing controls for the cluster, whether via a web interface, a CLI, or something else.
One of the things that the Console Daemon maintains is the user's collection of PETNAMES, which are human readable, human meaningful, user assigned labels for those Objects within the Cluster's Vats that have been made available for the user's direct manipulation (using the Console User Interface) as the result of operations invoked from the Console.
We anticipate there will be a single Console Daemon implementation that serves for all purposes, but at least two and possibly more Console User Interface implementations that vary depending on the particulars of the type of control UX that is being realized.
Typically, a given machine will only host a single Cluster, with the notable exception of multiple Clusters on a machine in service of testing and debugging (i.e., not the normal use case but one that is still important to support as part of the DX).
The ultimate affordance of an Object Reference is its use as the target
destination for a message send, though the use of Object References as identity
bearing tokens (i.e., values that can be compared with === and !==) is also
supported.
At bottom, any given message is sent from one Object to another. However, to describe how things actually work we also need to consider the role of Vats and Clusters. In particular, a message sent to an Object in another Vat within the same Cluster as the sender needs to be handled rather differently from a message sent to an Object in a Vat in a different Cluster.
Within a Cluster, a message sent by an Object in Vat A to a different Object in Vat B is transferred from the sender to the receiver via the Kernel. The Kernel needs to be able to determine, based solely on the Object Reference itself, which Vat to deliver the message to, and in what form to deliver it such that the receiving Vat's Supervisor will understand which of its Objects is to be the actual recipient.
The primary way (aside from bootstrapping, about which more in a moment) that a Vat could know the identity of an Object outside itself is if the reference to that Object had previously been imported into the Vat as an argument of an earlier message, which in turn means that the Object Reference had to have been exported by the originating Vat as part of (or prior to) sending that earlier message. Export preceding import means that the Kernel is always able to associate any Object Reference with the Vat that exported it, and thus knows where to deliver any subsequent message addressed to that same Reference.
In order for this process of Reference begetting Reference not to lead to infinite regress, some kind of bootstrap mechanism is required, else there'd be no way for any Vat to ever know about anything outside itself in the first place and thus no way for it to ever send messages anywhere. To bootstrap the reference graph, whenever a Vat is created, its initialization creates within it an initial ROOT OBJECT, a Reference to which is exported to the creator as the result of the Vat creation operation. If the Vat was created by the action of User Code in another Vat, this Root Object Reference is delivered to the creator Object as the return value from its invocation of the Vat creation operation. If the Vat was created by an action of the user using the Console, the Root Object Reference is placed into the user's power box for potential future use in operations commanded via the Console (see the discussion of power boxes and petnames below).
The logic for messages between Objects in separate Clusters does not map directly to that for messages between Objects in Vats within the same Cluster. The first important difference is that an observer outside a given Cluster has no way to know which of that Cluster's Vats a given Object Reference feeds into. On the other hand, there's no particular reason for this outside observer to know or care about which specific Vat it is either. Instead, we consider the internal organization of a Cluster to be the Cluster's private business. Outside an Object's containing Cluster we only associate its Object Reference with the Cluster itself. Another way of putting this is: since you don't have visibility into the inner workings of a Cluster, you must treat all the Objects within the Cluster as if the Cluster was a single giant Vat, and that any finer-grained distinction within that Cluster that you might become aware of is an illusion presented to you by the Cluster's Kernel. The logic of References begetting References via message passing in the intra-Cluster case is recapitulated in the inter-Cluster case, but at the granularity of Clusters rather than the granularity of Vats.
A second, and probably more important, difference is that between Clusters there is no entity analogous to a Kernel possessing visibility onto all the Object Reference relationships that are in play between Clusters. Instead, Clusters connect to each other in an arbitrary peer-to-peer network rather than via the star topology that the Kernel organizes its Vats into. This has important implications with respect to the process by which connectivity between Objects is bootstrapped across Cluster boundaries.
Whereas Vats are created by other Vats via the Kernel or by the user via the Console, new Clusters are created by the autonomous actions of users, external to any pre-existing Cluster (e.g., a user boots a new Cluster instance on their machine and connects it to the internet). In particular, the Cluster per se has no notion of a Root Object, since in its initial state a Cluster need not have any Vats at all. Once the Cluster does have Vats (either through later user action via the Console or as a consequence of configuration information provided as part of Cluster creation), these Vats (or rather, the Objects within them) have no a priori connection to anything, so initial connectivity between Clusters cannot be established purely by reference passing. Instead, we require some means for a Cluster's user, or software running on the user's behalf, to publish the equivalent of an Object Reference in a form that can be communicated out of band to the users of other Clusters, who in turn need a means to accept these and provide them as regular Object References to Objects of their own. To this end, the Kernel has the capability to translate between Object References, which have meaning in the context of messages between Objects as represented in the implementation of the inter-object message passing scheme, and OCAP URLS, which are text strings that can be communicated via pretty much any means you like (email, SMS, PostIt notes, QR codes on the sides of buses, etc.). The two directions of translation (from Ocap URL to Object Reference and from Object Reference to Ocap URL) are treated as capabilities that are made available to the user via the Console. From the Console these capabilities may be used directly or granted to selected Objects to which the user has access.
Taken all together, the arrangements discussed above imply five different possible forms an Object Reference may take depending on context:
-
A VREF (short for "vat reference") designates an Object within the scope of the Objects known to a particular Vat. It is used across the Kernel/Vat boundary in the marshaling of messages delivered into or sent by that Vat. A Vref is generated and assigned by the Kernel when importing an Object Reference into a Vat for the first time and by the Vat when exporting an Object Reference from it for the first time.
-
A KREF (short for "kernel reference") designates an Object within the scope of the Kernel itself. It is used in the translation of References between one Vat and another. A Kref is generated and assigned by the Kernel whenever an Object reference is imported into or exported from a Vat for the first time. Krefs are strictly internal to the Kernel implementation. (Note: the differentiation between Vrefs and Krefs is important because it enables the Kernel to maintain 2N Reference mapping tables for N Vats rather than having to potentially maintain N2 mapping tables.)
-
An RREF (short for "remote reference") designates an object within the scope of an established point-to-point communications Channel between two Clusters (more on Channels below). An Rref does not survive the Channel it is associated with. An Rref is generated when the Kernel for one Cluster exports an Object Reference into the Channel connecting it to the Kernel for another Cluster.
-
An OCAP URL is an externalizable string that designates an object globally in a form suitable for transmission out of band. It incorporates addressing information that allows Kernel identify the Cluster containing the Object it refers to and create a Channel to it if one does not already exist or to obtain the existing Channel if it does. It designates an Object independent of whether the Cluster containing the Object is currently available or not and independent of the Channel used to establish connectivity. It cannot be used to address messages directly, but can be used to establish connectivity and ultimately obtain an Rref which does permit messaging.
-
A PETNAME is a human readable, human meaningful, user assigned label that designates an Object within the scope of a given Cluster's Console. Internally, the Console maintains a table mapping these labels to Krefs or Rrefs. Petnames may manifest as textual names, such as a user might type or paste into a text input box on a web form, or draggable labels (with distinguishing text, icons, or both) that can be manipulated as part of an interactive Console GUI, or they might take some other form depending on the Console UX design, as long as they have the property of being directly meaningful to the user.
A CHANNEL is a communications connection between two Clusters that provides a context for message traffic between them. In particular, it establishes a scope for the interpretation of shared Rrefs. A Channel is a sturdy version of a TCP-like session connection. It may, in fact, be transported over an ordinary TCP (actually, more likely TLS) connection, but it can survive loss and reestablishment of the underlying transport session, as well as termination and restart of the Kernel or Vat processes at either end, while preserving all of the Object References stretched over the connection and preserving unbroken message streams back and forth between the two endpoints.
Connecting one Cluster to another requires solving two important sub-problems: knowing or learning where to make a network connection to (i.e., IP address and port number) in order to interact with the Cluster, and, once having connected, verifying that what one has connected to is, in fact, the Cluster one actually intended to connect to (or, indeed, if it is a Cluster at all).
In addition, the actual mechanism of network connectivity needs to account for our intention to host Clusters inside users' web browsers. This is significant because browsers commonly run on machines that aren't in a position to directly accept incoming network connections due to network address translation, dynamic IP address assignment, firewalls, proxies, and all the other myriad ways that the IT organizations of the world have gummed up the proper functioning of IP networks over the years. Unless and until proven otherwise, I'm going to assume we can use one of the several pre-existing solutions to this problem that clever people have devised rather than trying to invent our own workaround. However, we do need to acknowledge that whatever solution we adopt will most likely end up as a key part of our network stack, since it will almost certainly need to be baked into our design at a fairly fundamental level. Consequently, for the time being I'm just going to treat this as a solved problem, making the naive but useful presumption that one computer can make a connection to another at will, while handwaving away all the complications.
An Ocap URL contains several distinct pieces of information:
-
Network addressing information that indicates how to contact the Cluster that contains the object it designates.
-
Cluster identification information that enables the party establishing a Channel to verify that it is validly in contact with the proper Cluster.
-
An opaque encoding of the object's identity in a way that can be decoded by the target Cluster but which by itself does not uniquely reveal the Object's identity. In particular, multiple different Ocap URLs may all encode the same Object Reference, and it should generally be considered a best practice to vend a new, unique Ocap URL each time an Object Reference is externalized from its containing Cluster.
Verifying that a connection has been made to the proper endpoint as part of setting up a secure communications pathway is a job traditionally performed by TLS. However, in order to authenticate that a communications counterparty is the one expected, in normal operation TLS leverages DNS: when connecting to a server via TLS, the server provides a cryptographic certificate attesting to the association between the DNS name of the server and the public key used in the TLS handshake to generate the session key. This certificate is signed by a certificate authority (CA) whose validity is verified either by another certificate signed by a higher level CA (repeating this process recursively) or by being found in a collection of root CAs that the browser is pre-configured to automatically trust. The determination of the ultimate authenticity of the root CAs is highly centralized, leading to a number of points of vulnerability -- the authorities maintaining the root CA list could be compromised, individual CAs could be compromised without their parent CA's knowledge, revocation of a compromised CA's certificate might not have reached the browser (for many different possible reasons), or the root CA list held by the browser could itself be compromised (either within a given user's environment or at some point in the browser's implementation, build, and distribution path). Many of these failures have, in fact, occurred repeatedly in the history of the web. DNS itself is also a point of vulnerability: in the event that the operator of a server loses control over the DNS registry for its domain name, some other party may be positioned to substitute its own server address and corresponding TLS certificate chain. These sorts of compromises have also happened in practice. Because the root of the CA hierarchy is highly centralized, there remains a risk that some of these compromises could happen at scale. A further problem is that even without security failures, establishing the connection requires that the entity connected to possess the requisite certificates in the first place, which is itself administratively complex and highly error prone (and whether or not DNS is involved merely varies the modes of complexity and failure).
An alternative approach, which I want to advocate here, is for our Ocap URLs to employ something like Tyler Close's YURL scheme. While there are lots of details and possible variations, the basic idea behind them all is simple: embed the fingerprint of the contactee's public key into the URL itself, so that when a connection is made its authenticity can be validated directly. Tyler's documentation (just linked) shows how this can be accomplished with existing, mature TLS implementations, requiring only small and relatively simple changes to the way TLS is used. In the 20+ years since YURLs were first proposed they have seen only experimental adoption, in large part because of the huge entrenched installed base built around TLS, HTTPS, and the CA hierarchy, but also because YURLs and their brethren involve URLs that are large and unwieldy, giving them very poor ergonomics in many web use cases. However, because we are already handling Object References using Petnames combined with automation, our typical use cases do not generally require exposing representations of Object References to humans. YURLs do lend themselves very well to being handled via automation in such a fashion, which will allow us to have much better decentralization, avoid a large swath of potential security vulnerabilities, and bypass a lot of complicated, difficult to understand, and error prone administrative procedures (the latter being particularly important if we want this to be used by ordinary users in their browsers rather than just by professional IT people).
(OPEN DESIGN QUESTION: should we have a distinct Ocap URL scheme such as
ocap for these URLs or are we better off sticking to some convention for
using, say, https? Related: should we look into the cap URL scheme that
Mark Miller registered years ago for E?)
(OPEN DESIGN QUESTION: should the host address in an Ocap URL be regarded as the actual site to contact to reach the Object in question or should it be regarded as a hint as to where one might look, with the URL providing entry into a directory service if it doesn't reach the Object directly? In some of the work with E we referred to this as a "redirectory", drawing analogy to a web server that might serve up a page or might serve up an HTTP redirect.)
With a couple of exceptions (which will be described when we get to them), we conceive of the interfaces between the various communicating components of the overall system in terms of asynchronous messaging between loosely coupled actors. Note that use of the term "message" here can be a source of confusion, since among the most common and important kinds of things being transported by messages between components are messages between Objects, but these are "messages" at two different levels of abstraction. When helpful for clarity I will sometimes refer to SYSTEM MESSAGES or OBJECT MESSAGES to disambiguate what is being talked about. All the traffic on these interfaces described in this section consists of System Messages; some of these System Messages will carry Object Messages in their payloads.
Typically, connections between processes on the same machine will be carried by interprocess pipes. Channels, which nominally connect across machine boundaries, will be carried via a network protocol layered over TCP/IP. In any case, these connections are always asynchronous and bidirectional. We will of course hide all that under an abstraction layer so that the components themselves are shielded from having to know the particulars of how they talk to each other.
Between any two kinds of components we define an interface that specifies what the allowed messages in each direction are and what operations they invoke. Every component implements some set of operations, some of which may be made available on more than one of its interfaces. In this sense, operations and interfaces are somewhat orthogonal, despite many operations being interface-specific. Consequently, we will present operations and interfaces in separate sections as we describe the various components below.
The Kernel interacts with three different (kinds of) entities via three different interfaces:
- Console (via the Console Daemon) to Kernel C->K
- Vats (via a Vat Supervisor) to Kernel V->K
- Remote Cluster (via a Channel) to Kernel the R->K
Inbound to the Kernel, messages are consumed eagerly (though subject to rate limiting to protect against attacks on availability) from the connection upon which they arrive, then queued within the Kernel itself. The Kernel maintains a separate queue for each connection to simplify prioritization of traffic handling. In general, the Console has priority over Vats which in turn have priority over Channels, while contending traffic from multiple Vats or multiple Channels are serviced round-robin. More complicated scheduling strategies are possible and might be worth exploring, but I think that at this stage in our design process we know too little about our requirements and our likely operating ecosystem to have actionable opinions about what, if any, alternatives might be desirable.
Outbound from the Kernel, messages are transmitted immediately, though subject to whatever buffering the host operating system might employ. In the fullness of time we probably need to consider how flow control might back up into actually blocking further processing on the sending end of an interface, but that seems like an advanced consideration that should not hold up engineering progress right now.
-
StartKernel* -- Launch a new Kernel process for a Cluster
- Establish a connection to Kernel's Console Daemon.
- Interrogate Kernel's persistent storage to determine the state of its
Cluster. If the Kernel is being launched as a part of the launch of a new
Cluster (indicated by absence of such state), initialize persistent state
with:
- New Cluster ID
- Empty extant Vats set
- Empty extant Channels set
- If there was already a running Kernel process with this Cluster's Cluster
ID:
- Send error feedback to the Console
- Exit
- For each extant Vat:
- If the corresponding Vat process is already running: connect to the running Vat process
- If not: launch a new Vat process
- For each extant Channel:
- Attempt to reopen the Channel
- If this succeeds: resynchronize the message streams to and from the corresponding remote Clusters according to the Ken protocol.
- If this fails:
- Destroy the Channel
- Send appropriate error feedback to the Console
* Note: this operation is not exposed on any Kernel interface, since it cannot be invoked by sending a message to the Kernel: by definition, at the time the operation is initiated there is no Kernel to send a message to. Instead, it is the operation implied by launching the Kernel application at the operating system level in the first place. Parameters governing the operation are passed as command line arguments rather than as message arguments.
-
StopCluster -- Shutdown all of a Cluster's processes in way that can be restarted
-
TerminateCluster -- Shutdown all of a Clusters's process and reclaim its resources
- LaunchVat -- Create a new Vat and start it running in a process
- StopVat -- Shutdown a Vat process retaining its persistent state
- UpdateVat -- Replace a Vat's code without loss of persistent state
- RestartVat -- Restart a previously shutdown Vat from its persistent state
- Pause Vat* -- Shutdown a Vat process in a way that retains its in-memory state
- Resume Vat* -- Restart a previously shutdown Vat, reloading its in-memory state
- Terminate Vat -- Shutdown a Vat process and discard its persistent state
- OcapURLToObjectReference -- Given an Object Reference, provide an equivalent Ocap URL
- ObjectReferenceToOcapURL -- Given an Ocap URL, provide the corresponding Object Reference
- DeliverObjectMessage -- Deliver an Object Message to the appropriate Vat
- NotifyPromiseSettlement -- Deliver a promise resolution/rejection to the appropriate Vat
- ReceiveCrankCompletion -- Accept the results from a Vat of processing an Object Message or promise settlement, consisting of: result status, Object Message sends, and promise settlements
- DropImports -- Note that a Vat will no longer Reference some Object
- RetireImports -- Note that a Vat will no longer recognize some Object
- DropExports -- Note that a Vat no longer contains some Object
- RetireExports -- Note that a Vat may no longer Reference some Object
- ListVats -- Provide a list of all extant Vats, with accompanying descriptive metadata
- ListChannels -- Provide a list of all extant Channels, with accompanying descriptive metadata
- DebugVat -- Command a Vat to make itself available to the/a JavaScript debugger
- DumpVatInfo -- Provide a bundle of info about a Vat, including its c-lists and persistent state
- DumpKernelInfo -- Provide a bundle of info about the Kernel, including its persistent state
- DumpChannelInfo -- Provide a bundle of info about a Channel, including its c-lists
Vats interact with two different (kinds of) entities via two different interfaces:
- Kernel to Vat Supervisor K->V
- Vat Supervisor to User Code V->U
The K->V interface is how the outside world talks to a Vat, while the V->U interface is strictly internal to the Vat.
Unlike almost all the other interfaces in this design, the V->U interface is an asynchronous function call interface rather than a serialized data stream. The Vat Supervisor dequeues an event from its run queue, dispatches it to the User Code, which then processes it to completion and returns to the Supervisor. Aside from the Supervisor reading from the Kernel connection and capturing System Messages in its run queue, at any given time either the Vat Supervisor has agency or the User Code does, but never both. All Supervisor activity aside from input capture happens in between invocations of the User Code.
object reference representation details Channel and inter-Cluster connection logic handling promises and eventual send