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 v12)
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.
Daku Custom Section
The Daku custom section is a WebAssembly custom section that must follow the order of conventional sections
name
producers
target_features
daku
The base 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
Channels
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 channel 0 may also open "Device
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. There may be more than one channel per portal. They can
not and will not be closed until the WASM module exits.
Device Channels
Device channels represent something (physically or virtually) connected to a bus. Device channels are opened by sending commands to portals (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).
Commands
Commands are data sent from the guest to the host. Their structure is specific to the channel which they are sent. All commands have a lifetime, and the data sent should only be freed after the command lifetime has ended.
Command Lifetime Flow
- Guest allocates a command on the heap
- Guest sends the command address to the host with
ar()
- Host returns from
ar()
with the address of the command in the "ready list" - Host frees or reüses (for streams) the command as its lifetime has ended
Type: Command
Commands are the way the Daku application sends messages to the environment.
Fields
chan: int
Channel to send data onaddr: opt[T]
Pointer to command data for commandT
Host Exports
The exported WebAssembly API contains a single function:
Function: ar()
Retconned from "Ardaku"; Asynchronous Request function.
Returns once at least one command (with a non-zero ready
value) completes. An
early return can be forced with a null noöp command.
(import "daku" "ar" (func $event
(param $cmd_size i32) ;; List[Command].size
(param $cmd_addr i32) ;; List[Command].addr
(result i32) ;; List[Uint32].size
))
Parameters
$cmd_size
The size of the data pointed to by$cmd_addr
$cmd_addr
A pointer in the wasm memory to a list of$cmd_size
commands
Returns
- The number of ready commands in the ready list (new length of ready list).
Guest Exports
- Export 32-bit memory as
mem
- Export "main" function as
run
- Export ready list global pointer as
rl4
for a size 16 ready listrl0
for a size 1 ready listrl1
for a size 2 ready listrl2
for a size 4 ready listrl3
for a size 8 ready listrl4
for a size 16 ready listrl5
for a size 32 ready listrl6
for a size 64 ready listrl7
for a size 128 ready listrl8
for a size 256 ready listrl9
for a size 512 ready list
Type: ReadyList[N]
The list of ready/complete commands
Fields
ready_list: [opt[T]; N]
Ready list of commands
Types
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
val
An arbitrary untyped 32 bitsint
A 32-bit integernum
A 32-bit floating point (May require all 1 bits for NaN representation)
long
A 64-bit integerhalf
A 16-bit integerbyte
An 8-bit integernybl
A 4-bit integer
ptr[T]
A 32-bit addressopt[T]
A 32-bit address or Null (0)
List
Type: List[T]
A list of elements of type T
Fields
size: int
Number of elements pointed to ataddr
addr: ptr[T]
Packed list ofsize
elements
Buffer
Type: Buffer
A list of bytes
Fields
data: List[byte]
Buffer data
Text
Type: Text
(subtype of List
)
A buffer of UTF-8 text.
Fields
text: Buffer
UTF-8 formatted buffer
Vector
Type: Vector
A 4-dimensional vector.
Fields
x: num
X position (-left +right)y: num
Y position (-up +down)z: num
Z position (-back +front)w: num
W 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: val
frame: 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.count
32-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: val
Dimensions pairwidth: half
Width in pixelsheight: half
Height in pixels
Raster
Type: Raster
A buffer of pixels (image, picture, video, etc.).
Fields
size: Dimensions
Size of the imageaddr: ptr
List ofsize.width
*size.height
pixels by row according to format.format: int
Bits per component8
8 bits (integer)16
16 bits (integer)32
32 bits (float)
colorspace: int
0
Mask (1 component - grayscale/alphascale)1
RGBA (4 components)2
BGRA (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 TAI
instead 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: half
Range: 0 ~ 65_535month: byte
Range: 1 ~ 12day: byte
Range: (of week: 1 ~ 7) << 5 | (of month: 1 ~ 31)
Time
Type: Time
A naïve time (unspecified timezone)
Fields
hour: byte
Range: 0 ~ 23minute: byte
Range: 1 ~ 59millis: half
Range: 0 ~ 60_999 (can represent leap seconds)
DateTime
Type: DateTime
A naïve date and time (unspecified timezone)
Fields
date: Date
The datetime: Time
The time
Lang
Type: Lang
Spoken language
Fields
identifier: [byte; 2]
Language Identifiers
Region
Type: Region
Region code
Fields
identifier: [byte; 2]
Region Identifiers
LangRegion
Type: LangRegion
A spoken language plus a region code for dialect
Fields
lang: Lang
The languageregion: Region
The region to specify dialect
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.
Portal Channels
- Error Log Level
- Warn Log Level
- Info Log Level
- Debug Log Level
- Trace Log Level
Readiness
Becomes ready once logging has completed (stopping the process after ready wouldn't result in a partially-formed log message).
Command: Log
If no target is necessary, prefer empty target for traditional stdout/stderr
compatibility. Treat I
/D
/T
as stdout, and W
/E
/F
as stderr,
preferring I
and W
.
Fields
target: Text
Target namemessage: Text
Message to print
Traps
- If
message
is not valid UTF-8, or contains a NUL byte - If
target
is not valid UTF-8, or contains a NUL byte - If address at
message.addr + message.size - 1
has no page - If address at
target.addr + target.size - 1
has 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.
Portal Channels
- Developer prompt
Readiness
Becomes ready once a line of text has been entered. With either
- Buffer is not big enough, with
capacity
modified to what is required - Buffer is big enough,
command
text 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
capacity
is less thancommand.size
- If address at (input)
command.addr + capacity
has no page - If address at
capacity + 3
has 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).
Portal Channels
- Initiate HTTPS request
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),
capacity
set to 0. - The resource host hung up,
capacity
set 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,
buffer
overwritten up tocapacity
bytes, andcapacity
modified how many more bytes are required - Buffer is big enough,
buffer
overwritten,capacity
unchanged.
- 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
url
does 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
url
after domain/IP doesn't either start with one of:/?
or end - If
url
sections:/?
are out of order - If
url
port after:
is not in range 0~65535 - If
url
path after/
is nota~z
,A~Z
,-
,_
,%
,.
,~
,+
, or/
- If
url
query after?
is nota~z
,A~Z
,0~9
,-
,_
.
,~
,+
,=
,;
, or&
- If
headers
begins with\n
or ends with\n
- If
headers
line does not matchTitle-Kebab-Case: expected-type
(no\r
allowed) - If
headers
line contains an invalid (LikeNot-A-Header
), redudant (LikeContent-Length
), or insecure (Also likeContent-Length
)header
- If input
capacity
is less thanbuffer.size
- If address at (input)
buffer.addr + body.capacity
has no page - If address at
body.addr + body.size
has no page - If address at
url.addr + url.size
has no page - If address at
headers.addr + headers.size
has no page - If address at
body + 3
has no page - If address at
capacity + 3
has no page - If address at
buffer + 3
has 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),
capacity
set to 0. - The resource host hung up,
capacity
set 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,
buffer
overwritten up tocapacity
bytes, andcapacity
modified how many more bytes are required - Buffer is big enough,
buffer
overwritten,capacity
unchanged.
- 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
0
or1
)
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).
Portal Channels
- Accept HTTPS clients
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
errors
being non-null) since the last successful connetion, in one of the 4 categories has exceeded 255
Command: Serve
Fields
config: val
port: half
Port number to connect toconnections: half
How 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: byte
Number rejected based on load being too high (clients connecting is larger than ready list size)maximum: byte
Number rejected based on too many connections exceeding maximum set withconnections
https: byte
Number rejected for invalid HTTPStimeout: byte
Number rejected based on timeout on sending headers
Traps
- If
connections = -32768
- If
port = 0
(local only) andconnections
sign bit is negative - If address at
errors + 3
has 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.capacity
set to required capacity) - Errors have overflown (at least one error set to
255
) - The client hung up (
poll
set toHangup
) - The client is ready for more data (
poll
set 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
poll
invalid/unknown variant - If
poll
is not0
or1
after response headers already sent - If
poll
is non-null and not a reponse status code before headers sent - If
request.size > request.capacity
- If address at
content.addr + content.size
has no page - If address at
error + 3
has no page - If address at
request + 7
has no page - If address at
request.capacity + 3
has no page - If address at
request.headers_content.addr + request.capacity
has no page