Oak Nodes are implemented as WebAssembly modules, and so can only interact with things outside of the WebAssembly environment through specific entrypoints which form the Oak ABI:
- The Oak Runtime invokes each Oak Node via a single exported function.
- The Oak Node can make use of functionality provided by the Oak TCB by invoking host functions, available as WebAssembly imports.
These host functions provided by the Oak TCB revolve around the creation of other Nodes, and the use of channels for inter-node communication. This communication is further constrained by flows-to checks based on the labels associated with the Nodes and channels.
To communicate with the outside world beyond the Oak system, a Node may also create and communicate with pseudo-Nodes. The messages exchanged with pseudo-Nodes are encoded as serialized protocol buffer messages.
Note also that the Oak ABI interactions are quite low-level; for example, they involve manual management of linear memory. Oak Applications will typically use the higher-level Oak SDK which provides more convenient (and safer) wrappers around this functionality.
WebAssembly has two integer types (i32
and i64
) which are treated as
signed or unsigned values depending on the context.
Integer types that are passed across the Wasm boundary that forms the Oak ABI
are written as u32
or u64
in this document, to make clear that the
corresponding Wasm i32
or i64
values are always treated as unsigned values.
(There are currently no uses of signed values in the ABI.)
Integer types that refer to:
- offsets in linear memory
- sizes of regions in linear memory
are written as the usize
type, which is an alias for the u32
type in the
current WebAsssembly implementation(s). However, in any future
64-bit
version of WebAssembly this usize
type would instead be an alias for the u64
type.
Three specific sets of integer values are also used in the ABI:
- Many operations take a
u64
handle value; these values are Node-specific and non-zero (zero is reserved to indicate an invalid handle), and reference one half – read or write – of a particular channel. - Many ABI operations return a
u32
status value, indicating the result of the operation. The possible values for this are defined in theOakStatus
enum in oak_abi.proto. - The
wait_on_channels
host function fills in a channel status value, indicating the readiness status of a particular channel. The possible values for this are defined in theChannelReadStatus
enum in oak_abi.proto.
The host functions described below allow opaque blobs of data to be exchanged (along with handles to other channels). When communicating with the Runtime, these opaque blobs of data are defined to take the form of serialized protocol buffer messages:
- The label values included on Node and channel creation operations are in the
form of a serialized
Label
message. - The Node configuration information that is include on
node_create
operations is in the form of a serializedNodeConfiguration
message. - The sole initial message sent to the first Node of an Oak Application is in
the form of a serialized
ConfigMap
message.
Similarly, messages exchanged with the pseudo-Nodes provided by the Oak system are also defined to take the form of serialized protocol buffer messages. These include:
Each Oak WebAssembly module must expose at least one exported function as a
WebAssembly export,
with signature fn(u64) -> ()
. This function is invoked when the Oak Manager
executes the Oak Node; a handle for the read half of an initial channel is
passed in as a parameter. The name of this entrypoint function for a Node is
provided as part of the
Application configuration.
The entrypoint function for each Node should perform its own event loop, reading incoming messages that arrive on the read halves of its channels, sending outgoing messages over the write halves of channels. The entrypoint function is generally expected to run forever, but may return if the Node choses to terminate (whether expectedly or unexpectedly).
Each Oak Application starts with a single initial Oak Node. The Runtime sends a
single start-of-day configuration message on the initial channel that this Node
receives (and then the Runtime closes the write half of the channel). This
configuration message is a serialized ConfigMap
protocol buffer message,
intended to hold the Application's initial configurqtion information.
The initial Oak Node may create other Nodes (and the channels used to communicate with them), and will typically create a gRPC server pseudo-Node to allow communication from the outside world to happen.
Each Oak Module may also optionally rely on zero or more of the following host
functions as
WebAssembly imports
(all of them defined in the oak
module):
Blocks until data is available for reading from one of the specified channel
handles, unless any of the channels is invalid, orphaned, or violates the
information flow control. The channel handles are
encoded in a buffer that holds N contiguous 9-byte chunks, each of which is made
up of an 8-byte channel handle value (little-endian u64) followed by a single
channel status byte. Invalid handles will have an INVALID_CHANNEL
, ORPHANED
,
or PERMISSION_DENIED
status, but wait_on_channels
return value will only
fail for internal errors or if the runtime is terminating.
param[0]: usize
: Address of handle status bufferparam[1]: u32
: Count N of handles providedresult[0]: u32
: Status of operation
Reads a single message and associated channel handles from the specified
channel, setting the size of the data in the location provided by param[3]
,
and the count of returned handles in the location provided by param[6]
.
If the provided spaces for data (param[1]
and param[2]
) or handles
(param[4]
and param[5]
) are not large enough for the read operation, then no
data is written to the destination buffers, and the function returns either
BUFFER_TOO_SMALL
or HANDLE_SPACE_TOO_SMALL
; in either case, the required
sizes are written in the spaces provided by param[3]
and param[6]
.
If no messages are available on the channel, returns CHANNEL_EMPTY
.
If reading from the specified channel would violate
information flow control, returns
ERR_PERMISSION_DENIED
.
param[0]: u64
: Handle to channel receive halfparam[1]: usize
: Destination buffer addressparam[2]: usize
: Destination buffer size in bytesparam[3]: usize
: Address of a 4-byte location that will receive the number of bytes in the message (as a little-endian u32).param[4]: usize
: Destination handle array buffer (to receive little-endian u64 values)param[5]: u32
: Destination handle array countparam[6]: usize
: Address of a 4-byte location that will receive the number of handles retrieved with the message (as a little-endian u32)result[0]: u32
: Status of operation
Writes a single message to the specified channel, together with any associated handles.
If writing to the specified channel would violate
information flow control, returns
ERR_PERMISSION_DENIED
.
param[0]: u64
: Handle to channel send halfparam[1]: usize
: Source buffer address holding messageparam[2]: usize
: Source buffer size in bytesparam[3]: usize
: Source handle array (of little-endian u64 values)param[4]: u32
: Source handle array countresult[0]: u32
: Status of operation
Creates a new unidirectional Channel assigning the label specified by param[2]
and param[3]
to the newly created Channel, and returns the Channel handles for
its read and write halves as output parameters in param[0]
and param[1]
.
The label is a serialized Label
protobuf
message.
Because the label of the newly created Channel is effectively public, this function may only be invoked by Nodes whose label "flows to" the "public untrusted" label.
If creating the specified Channel would violate
information flow control, returns
ERR_PERMISSION_DENIED
.
param[0]: usize
: Address of an 8-byte location that will receive the handle for the write half of the channel (as a little-endian u64).param[1]: usize
: Address of an 8-byte location that will receive the handle for the read half of the channel (as a little-endian u64).param[2]: usize
: Source buffer holding serializedLabel
param[3]: usize
: Label size in bytesresult[0]: u32
: Status of operation
Closes the channel identified by param[0]
.
param[0]: u64
: Handle to channelresult[0]: u32
: Status of operation
Creates a new Node running the Node configuration identified by param[0]
and
param[1]
, assigning the label specified by param[2]
and param[3]
to the
newly created Node, passing in an initial handle to the read half of a channel
identified by param[4]
.
The Node configuration is a serialized
NodeConfiguration
protobuf message, and
the label is a serialized Label
protobuf
message.
Because the label of the newly created Node is effectively public, this function may only be invoked by Nodes whose label "flows to" the "public untrusted" label.
If creating the specified Node would violate
information flow control, returns
ERR_PERMISSION_DENIED
.
param[0]: usize
: Source buffer holding serializedNodeConfiguration
param[1]: usize
: Serialized NodeConfiguration size in bytesparam[2]: usize
: Source buffer holding serializedLabel
param[3]: usize
: Label size in bytesparam[4]: usize
: Handle to channelresult[0]: u32
: Status of operation
Fills a buffer with random bytes.
param[0]: usize
: Destination bufferparam[1]: usize
: Destination buffer size in bytesresult[0]: u32
: Status of operation