Introduction
Daku is an asynchronous host interface abstraction API for WebAssembly plugins, drivers, applications, and more! Daku is both an API and a file format. Ardaku is an engine you can use for running Daku modules on different Wasm runtimes.
Daku Specification v1.0.0-pre.0 (draft v15)
The current version of Daku targets the full WebAssembly 2.0 spec without any non-standard or experimental features.
Goals
- Simple
- Efficient
- Modular
- Minimal (in API surface, and memory footprint)
- Asynchronous
- Backwards Compatible
- Reduced context switching
- Security-first
- First-class multimedia portals (WASI compatible)
Glossary
Guest
A program that interfaces with the Daku API.
Host
The program that embeds the guest.
Command
A command is a data structure that is sent from the guest to the host. All commands execute once and complete asynchronously. Any command may be canceled.
Channel
Channels receieve commands from the guest. For the guest to receive data, the guest must send a memory address of a buffer to the host; The buffer then gets overwritten upon completion of the command.
Portal
Portals are channels that get opened before execution of the guest WebAssembly module begins. A custom command portal is always opened at channel 0. Other portals can be opened using the portal extension, which provides system-interface APIs similar to what WASI does.
Primitives
Daku defines a few shared types that can be used in the command definitions. Occasionally, a command should use a custom type that packs data better, but it should match similarly to another type defined here.
Primitives
valArbitrary untyped 32 bitsint32-bit integernum32-bit floating point (May require all 1 bits for NaN representation)
long64-bit integerhalf16-bit integerbyte8-bit integernybl4-bit integer
ptr[T]32-bit addressopt[T]32-bit address or Null (0)
List
Type: List[T]
List of elements of type T
Fields
size: intNumber of elements pointed to ataddraddr: ptr[T]Packed list ofsizeelements
Buffer
Type: Buffer
List of bytes
Fields
data: List[byte]Buffer data
Command
Type: Command
Command sent from the guest to the host
Fields
channel: int(in) Which channel is being used, (out) number of channels openedcapacity: intCapacity ofbufferbuffer: BufferData buffer
Runtime
The Daku runtime can be used alone or with other parts of the Daku specification. When using Daku, your target is a Daku plugin app. The Daku runtime specification defines how to asynchronously communicate between the plugin and the host app (which could either be another WASM sandboxed Daku plugin or a native Daku engine like Ardaku).
Guest Exports (Host Imports)
- Export 32-bit memory as
m - Export function
main() -> ()asa - Export ready list global
List[ptr[Command]]pointer asr
Guest Imports (Host Exports)
- Import function
ar(cmd_size, cmd_addr)Asynchronous Request (Retconned from "Ardaku")
Plugin Communication Flow
- Host calls guest's
a()function - Guest creates a list of commands
- Guest calls host's
ar()function with list of commands - Host executes commands, when at least one completes, returns from
ar()having set the data at pointerrin memorymto a list (using default length as capacity) of completedptr[Command]list - Guest loops through the
ptr[Command]ready-list and processes them, may either free or reüse the command structures - Guest generates new list of commands, calling
ar()again (repeat) - Guest's
a()function completes and plugin has stopped
Function: ar()
(import "daku" "ar" (func $event
(param $cmd_size i32) ;; List[ptr[Command]].size
(param $cmd_addr i32) ;; List[ptr[Command]].addr
))
Parameters
$cmd_sizeThe size of the data pointed to by$cmd_addr$cmd_addrA pointer in the wasm memory to aCommandlist of size$cmd_size
Channels
Portal Channels
Portal channels represent a bus (and permission to talk to it) on the computer.
Portal channels are opened before the program starts (based on the daku custom
section). Going in order of the portals, will allocate channels in ascending
order starting from 1. Each portal allocates one channel. They can not and
will not be closed until the WASM module exits.
Channel 0
Channel 0 is a special channel that allows you to send arbitrary bytes
(Buffer) to the host from the guest. This is what a custom
plugin API would use. Sending commands on a channel opened to portal 0 may also
open "Device Channels". The buffer sent is re-written with received bytes, and
the Command.channel field set to number of device channels opened.
Device Channels
Device channels represent something (physically or virtually) connected to a bus. Device channels are opened by sending commands to channels (this command is not allowed to fail if constructed properly, even if the data is stale - so you may open a channel to a disconnected device). They are allocated after the portal channels. When the device channel is closed it's number goes into a garbage list. The last ID is popped off the garbage list when opening a new device channel (if it exists).
Daku Custom Section
The Daku custom section is a WebAssembly custom section that must follow the order of conventional sections
nameproducerstarget_featuresdaku
The base daku section without extensions is just a WebAssembly vector of
portal IDs.
For details on the experimental nucleide extension see https://docs.rs/nucleide/latest/nucleide/#daku-daku
Portal Types
There are common type definitions shared between the portals' APIs
Text
Type: Text (subtype of List)
A buffer of UTF-8 text.
Fields
text: BufferUTF-8 formatted buffer
Vector
Type: Vector
A 4-dimensional vector.
Fields
x: numX position (-left +right)y: numY position (-up +down)z: numZ position (-back +front)w: numW position (0 = vector, 1 = position)
Positions
Type: Positions (subtype of List)
A list of audio channel physical positions relative to the user at the origin.
The position must either be a unit vector (length of 1) or all zeros (center).
Fields
list: List[Vector]- List of speakers / microphones in configuration.x: num- X position (-left +right)y: num- Y position (-up +down)z: num- Z position (-back +front)w: num- LFE? (0 = No, NaN = Yes)
Audio
Type: Audio
A buffer of floating-point audio.
Fields
size: valframe: byte- Number of channels per frame.chunk: byte- Number of frames per chunk.count: half- Number of chunks.
addr: ptr[float]- Interleaved samples; List ofsize.frame * size.chunk * size.count32-bit floats.rate: int- Sample rate of the audio (hertz)config: opt[Positions]- Custom channel position configuration, default is FLAC/SMPTE/ITU-R recommendations.
FLAC/SMPTE/ITU-R recommendations
- 1 Channel: Mono (Mono)
(0, 0, 0, 0)
- 2 Channels: Stereo (Left, Right)
(-1, 0, 0, 0)(1, 0, 0, 0)
- 3 Channels: Surround 3.0 (Left, Right, Center)
(-1, 0, 0, 0)(1, 0, 0, 0)(0, 0, 0, 0)
- 4 Channels: Surround 4.0 (FrontL, FrontR, SurroundL, SurroundR)
(-0.5, 0, 0.8660254037844387, 0)(0.5, 0, 0.8660254037844387, 0)(-0.9396926207859084, 0, -0.3420201433256687, 0)(0.9396926207859084, 0, -0.3420201433256687, 0)
- 5 Channels: Surround 5.0 (FrontL, FrontR, Front, SurroundL, SurroundR)
(-0.5, 0, 0.8660254037844387, 0)(0.5, 0, 0.8660254037844387, 0)(0, 0, 1, 0)(-0.9396926207859084, 0, -0.3420201433256687, 0)(0.9396926207859084, 0, -0.3420201433256687, 0)
- 6 Channels: Surround 5.1 (FrontL, FrontR, Front, Lfe, SurroundL, SurroundR)
(-0.5, 0, 0.8660254037844387, 0)(0.5, 0, 0.8660254037844387, 0)(0, 0, 1, 0)(0, 0, 0, 1)(-0.9396926207859084, 0, -0.3420201433256687, 0)(0.9396926207859084, 0, -0.3420201433256687, 0)
- 7 Channels: Surround 6.1 (FrontL, FrontR, Front, Lfe, Back, Left, Right)
(-0.5, 0, 0.8660254037844387, 0)(0.5, 0, 0.8660254037844387, 0)(0, 0, 1, 0)(0, 0, 0, 1)(0, 0, -1, 0)(-1, 0, 0, 0)(1, 0, 0, 0)
- 8 Channels: Surround 7.1 (FrontL, FrontR, Front, Lfe, BackL, BackR, Left, Right)
(-0.5, 0, 0.8660254037844387, 0)(0.5, 0, 0.8660254037844387, 0)(0, 0, 1, 0)(0, 0, 0, 1)(-0.5, 0, -0.8660254037844387, 0)(0.5, 0, -0.8660254037844387, 0)(-1, 0, 0, 0)(1, 0, 0, 0)
Dimensions
Type: Dimensions
A 2-D integer width/height pair.
Fields
size: valDimensions pairwidth: halfWidth in pixelsheight: halfHeight in pixels
Raster
Type: Raster
A buffer of pixels (image, picture, video, etc.).
Fields
size: DimensionsSize of the imageaddr: ptrList ofsize.width*size.heightpixels by row according to format.format: intBits per component88 bits (integer)1616 bits (integer)3232 bits (float)
colorspace: int0Mask (1 component - grayscale/alphascale)1RGBA (4 components)2BGRA (4 components)
Timestamp
Type: Timestamp
The number of TAI microseconds since Jan 1 00:00:00.000_000, year 0 in ISO 8601:2004
This gives about a range of ±292_470 years since year 0.
This differs from Unix time in 3 ways:
- Epoch is
0000-01-01T00:00:00.000_000 TAIinstead of1970-01-01T00:00:00.000_000 UTC - Precision is microseconds instead of seconds
- TAI not UTC, meaning that a leap second gets representation, rather than being represented as repeat of previous second as it would be in unix time.
Fields
micros: long
Date
Type: Date
A naïve date (unspecified timezone)
Fields
year: halfRange: 0 ~ 65_535month: byteRange: 1 ~ 12day: byteRange: (of week: 1 ~ 7) << 5 | (of month: 1 ~ 31)
Time
Type: Time
A naïve time (unspecified timezone)
Fields
hour: byteRange: 0 ~ 23minute: byteRange: 1 ~ 59millis: halfRange: 0 ~ 60_999 (can represent leap seconds)
DateTime
Type: DateTime
A naïve date and time (unspecified timezone)
Fields
date: DateThe datetime: TimeThe time
Lang
Type: Lang
Spoken language
Fields
identifier: [byte; 2]
Language Identifiers
en: Englisheo: Esperanto
Region
Type: Region
Region code
Fields
identifier: [byte; 2]
Region Identifiers
US: United StatesGB: Great Britain / UK
LangRegion
Type: LangRegion
A spoken language plus a region code for dialect
Fields
lang: LangThe languageregion: RegionThe region to specify dialect
LeapSecond
Type: LeapSecond
A leap second
Fields
year: halfWhich yearmonth: byteAlways the last day of the month (0-11)delta: byteDirection (either -1 or +1)
TimeDesignation
Type: TimeDesignation
Name of a timezone
Fields
designation: int
Designations
- UTC
TimeAdjustment
Type: TimeAdjustment
A time adjustment like daylight savings time
Fields
old: DateTimeOld timenew: DateTimeNew time
TimeZone
Type: TimeZone
A timezone specification
Fields
designation: TimeDesignationName of the timezoneextension: opt[_]: Reserved for timezone specification extensionsoffset: DateTimeDate and time of this timezone for UTC jan 1 00:00:00:000 year 0adjustments: List[TimeAdjustment]List of adjustments made in this timezoneleap_seconds: List[LeapSecond]: List of leap seconds
Portals
Portals allow Daku applications to interface with the hardware and environment / OS. The environment is not required to grant access to all portals requested by the application, and may either stop the application or mock out a fake implementation to protect the user's privacy.
Implementations of portals should strive to be "as mathematical" as possible, meaning that there's no fancy engineering abstractions - just sending and receiving data and defining the required functionality. This is to reduce the risk for possibly needing to deprecating portals in the future. That said, it's ok to pack smaller pieces of data into one value if 32-bits can be guaranteed to most likely never be needed.
Portals should also make use of shared high-level types.
0x00 - Log
Log a message with an associated target and log level, usually to help with debugging.
Readiness
Becomes ready once logging has completed (stopping the process after ready wouldn't result in a partially-formed log message).
Command: Log
Fields
[_; _]level: intLog level
- Fatal
- Error
- Warn
- Info
- Debug
- Trace
- Stdout
- Stderr
log: opt[_]Log target and messagetarget: TextTarget namemessage: TextMessage to print
Traps
- If
messageis not valid UTF-8, or contains a NUL byte - If
targetis not valid UTF-8, or contains a NUL byte - If address at
message.addr + message.size - 1has no page - If address at
target.addr + target.size - 1has no page
0x01 - Prompt
Receive a line of textual user input from a debugging console.
The text appended to the buffer won't contain the newline character.
Readiness
Becomes ready once a line of text has been entered. With either
- Buffer is not big enough, with
capacitymodified to what is required - Buffer is big enough,
commandtext overwritten
Command: Prompt
Read textual user input from some source.
Fields
capacity: ptr[int]- (In/Out) Pointer to capacity ofcommand.command: ptr[Text]- (In/Out) Pointer to user-sent line of input.
Traps
- If input
capacityis less thancommand.size - If address at (input)
command.addr + capacityhas no page - If address at
capacity + 3has no page
0x02 - Fetch 🧪
Do an HTTPS request to specified URL.
SSE is expected to be implemented as an abstraction over this API (rather than be provided as its own portal).
Connection
Host device channel is opened upon readiness (allocated only if successful).
The channel allocation should happen immediately upon return of ar(), and must
follow in the order of the ready list.
Readiness
Becomes ready once either
- The resource host can't be reached (network error),
capacityset to 0. - The resource host hung up,
capacityset to 0. - The resource host has sent back a chunk of the resource (a new device
channel has been opened)
- Buffer is not big enough,
bufferoverwritten up tocapacitybytes, andcapacitymodified how many more bytes are required - Buffer is big enough,
bufferoverwritten,capacityunchanged.
- Buffer is not big enough,
Command: Fetch
Fields
url: Text- URL to do an HTTPS request to (does not includehttps://protocol)headers: Text- Extra headers to send (separated by\n)body: opt[List[byte]]- Optional payload/content body to sendmethod: int- 0: GET, 1: HEAD, 2: POST, 3: PUT, 4: DELETE.capacity: ptr[int]- (In/Out) Pointer to capacity ofbuffer.buffer: ptr[List[byte]]- (In/Out) Pointer to buffer for receiving parts of the HTTP response.
Traps
- If
urldoes not start with one of- Domain name lowercase (ranged a~z or 0~9,
-and.allowed after first character, but not consecutively, and not last character before termination character - one of:/?) - IPv4 Address - 4 integers ranged 0:255 each separated by
.) - Ipv6 Address -
[, then 8 lowercase hexaxecimal numbers from 1 to 4 digits separated by:, and]. A grouping of consecutive zero numbers can be replaced with::, rather than requiring all 8 numbers.
- Domain name lowercase (ranged a~z or 0~9,
- If
urlafter domain/IP doesn't either start with one of:/?or end - If
urlsections:/?are out of order - If
urlport after:is not in range 0~65535 - If
urlpath after/is nota~z,A~Z,-,_,%,.,~,+, or/ - If
urlquery after?is nota~z,A~Z,0~9,-,_.,~,+,=,;, or& - If
headersbegins with\nor ends with\n - If
headersline does not matchTitle-Kebab-Case: expected-type(no\rallowed) - If
headersline contains an invalid (LikeNot-A-Header), redudant (LikeContent-Length), or insecure (Also likeContent-Length)header - If input
capacityis less thanbuffer.size - If address at (input)
buffer.addr + body.capacityhas no page - If address at
body.addr + body.sizehas no page - If address at
url.addr + url.sizehas no page - If address at
headers.addr + headers.sizehas no page - If address at
body + 3has no page - If address at
capacity + 3has no page - If address at
buffer + 3has no page
Device: Host 🧪
Channel representing an HTTPS connection to a server.
Type: FetchError
An error may be indicated once either Fetch or Host command becomes ready.
To indicate an error, capacity will be set to 0. buffer.size will be set to
an error code:
Variants (int)
Network- Server unreachable (network error).Hangup- Server hung up.
Readiness
Becomes ready once either
- The resource host can't be reached (network error),
capacityset to 0. - The resource host hung up,
capacityset to 0. - The resource host has sent back a chunk of the resource (a new device
channel has been opened)
- Buffer is not big enough,
bufferoverwritten up tocapacitybytes, andcapacitymodified how many more bytes are required - Buffer is big enough,
bufferoverwritten,capacityunchanged.
- Buffer is not big enough,
Command: Host
Variants (int)
Poll- Poll for more data from server.Hangup- Hang up connection to server.
Traps
- If variant is unknown (not
0or1)
0x03 - Serve 🧪
Serve resources over HTTPS.
SSE is expected to be implemented as an abstraction over this API (rather than be provided as its own portal).
Connection
Client device channel is opened upon readiness. The channel allocation should
happen immediately upon return of ar(), and must follow in the order of the
ready list.
Readiness
Becomes ready once either
- A client connects.
- The number of connection errors (with
errorsbeing non-null) since the last successful connetion, in one of the 4 categories has exceeded 255
Command: Serve
Fields
config: valport: halfPort number to connect toconnections: halfHow many client connections to attempt maximum- If sign bit is set to negative, allow other computers to connection as a client
errors: opt[val]Output number of errors that occured since last successful connection (or since start, if becoming ready on first success)load: byteNumber rejected based on load being too high (clients connecting is larger than ready list size)maximum: byteNumber rejected based on too many connections exceeding maximum set withconnectionshttps: byteNumber rejected for invalid HTTPStimeout: byteNumber rejected based on timeout on sending headers
Traps
- If
connections = -32768 - If
port = 0(local only) andconnectionssign bit is negative - If address at
errors + 3has no page
Device: Client 🧪
Channel representing an HTTPS connection to a client.
Type: Poll
Variants (int)
Continue- Keep polling.Hangup- Hang up connection.
- Respond with informational response status code
100-199(FIXME: full list) - Respond with successful response status code
200-299(FIXME: full list) - Respond with redirection message status code
300-399(FIXME: full list) - Respond with client error response status code
400-499(FIXME: full list) - Respond with server error response status code
500-599(FIXME: full list)
Readiness
Becomes ready once either
- The client has headers and possible content body ready to be retrieved (no error is set to 0).
- The client has headers, but they are too large for
request(request.capacityset to required capacity) - Errors have overflown (at least one error set to
255) - The client hung up (
pollset toHangup) - The client is ready for more data (
pollset toContinue)
Command: Client
Fields
content: List[byte]Content body to send.poll: opt[Poll](In/Out) Pointer to poll state (In:Server, Out:Client)request: opt[_]Request buffercapacity: ptr[int](In/Out) Pointer to capacity ofheaders_content.headers_content: ptr[Text](In/Out) Pointer to headers and content.
Traps
- If
pollinvalid/unknown variant - If
pollis not0or1after response headers already sent - If
pollis non-null and not a reponse status code before headers sent - If
request.size > request.capacity - If address at
content.addr + content.sizehas no page - If address at
error + 3has no page - If address at
request + 7has no page - If address at
request.capacity + 3has no page - If address at
request.headers_content.addr + request.capacityhas no page