Documentation
¶
Overview ¶
Package debuglet is the Go client library for writing debuglets (WASM measurement programs) that run on the Debuglet executor.
A debuglet is a standard WASI command module: write a normal `func main()`, build it with `GOOS=wasip1 GOARCH=wasm`, and the executor streams whatever you print to stdout/stderr back to the user. The executor passes the user's command-line arguments verbatim as WASI argv, so `flag.CommandLine.Parse(os.Args)` reads them directly (note: there is no program name at os.Args[0]).
This package wraps the raw host imports exported by the executor engine (internal/executor/debuglet/wasm/host_functions.go) so that debuglet authors do not have to deal with //go:wasmimport declarations or unsafe pointer math. Instead of juggling 32-bit memory offsets, you work with a small Conn type:
conn, err := debuglet.ConnectTCP("example.com:80")
if err != nil { ... }
defer conn.Close()
if err := conn.Write([]byte("GET / HTTP/1.0\r\n\r\n")); err != nil { ... }
body, err := io.ReadAll(conn) // Read returns io.EOF when the peer closes
if err != nil { ... }
fmt.Println(string(body))
Conn.Read follows the io.Reader contract: short reads are normal, and a TCP or TLS stream (including connections from AcceptTCP) reports the peer's clean close as (0, io.EOF). UDP and ICMP reads return (0, nil) for an empty datagram, never io.EOF. Reads of an empty buffer return (0, nil) without a host call. Conn.Write keeps its Write([]byte) error signature; it is not an io.Writer.
One host call transfers at most MaxIOBytes bytes. Read therefore fills a larger buffer only up to that bound, which is an ordinary short read. Write splits a longer stream payload into consecutive calls, and rejects a longer datagram rather than sending a truncated one.
In the legacy Conn API, only a clean TCP/TLS end of stream surfaces as a Go error. Other host-side failures reach the caller as neither a value nor an error: a refused or policy-denied destination, a socket error other than EOF, an invalid handle and an invalid buffer all abort the guest as WASM traps, so the job ends with whatever the guest printed before the call.
Dial and DialTimeout use the optional debuglet_io_v1 extension and return Socket instead. Its Read and Write preserve byte counts alongside typed transport errors, so a diagnostic can handle a timeout or reset and continue. Invalid memory and never-issued handles still trap.
On non-wasip1 platforms the package compiles against panic stubs (debuglet_stub.go) so the rest of the module builds and tests on the host; every SDK call panics off-target. The real implementation is in debuglet_wasip1.go.
This stub lets the package (and therefore the whole module) build and test on the host. The host imports it shadows are only available inside a wasip1 guest, so calling these off-target panics. Build a debuglet with `GOOS=wasip1 GOARCH=wasm` to get the real implementation.
Index ¶
- Constants
- Variables
- func ListenAddr() (string, error)
- func ListenUDPAddr() (string, error)
- func ReadFromUDP(buf []byte) (n int, from string, err error)
- func Ready(ctx context.Context, metadata []byte) (experimentwire.Experiment, error)
- func WaitStart(ctx context.Context, experiment experimentwire.Experiment) error
- type Conn
- type IOError
- type Socket
Constants ¶
const MaxExperimentMetadata = experimentwire.MaxExperimentMetadata
MaxExperimentMetadata is the opaque metadata allowance for one participant.
const MaxIOBytes = 8192
MaxIOBytes is the number of bytes one host call transfers. It belongs to the guest ABI rather than to this package: the host reads or writes at most this many bytes of the buffer it is given, whatever the buffer's length is.
Variables ¶
var ErrConnect = errors.New("debuglet: connect failed")
ErrConnect reports a connection the SDK or the host rejected as a value: an empty address, or a negative handle. A destination that is refused, outside the job's policy, or otherwise unreachable aborts the guest inside the host call instead of returning this error.
var ErrExperimentLate = errors.New("debuglet: experiment start already passed")
ErrExperimentLate means the agreed start passed before the guest could wait.
var ErrTooLarge = errors.New("debuglet: payload exceeds the host transfer bound")
ErrTooLarge reports a datagram longer than MaxIOBytes. Splitting it would change one datagram into several, so Write rejects it instead.
Functions ¶
func ListenAddr ¶
ListenAddr returns the public "host:port" address of the debuglet's TCP listener, i.e. where clients should connect. It returns an error when no listener has been started (the executor has no public host configured).
func ListenUDPAddr ¶
ListenUDPAddr returns the public "host:port" address of the debuglet's UDP listener, i.e. where clients should send datagrams. It returns an error when no UDP listener has been started (the executor has no public host configured).
func ReadFromUDP ¶
ReadFromUDP blocks until a single datagram arrives on the debuglet's UDP listener and returns the number of bytes read into buf, the sender's "host:port" address, and any error. It mirrors Go's PacketConn.ReadFrom: the call blocks until a datagram is available or the listener is closed.
func Ready ¶ added in v0.3.0
func Ready(ctx context.Context, metadata []byte) (experimentwire.Experiment, error)
Ready publishes immutable metadata for this run and waits for every run in its submitted batch. Listeners and application setup should be ready first. The host supplies run identity and authorization; metadata grants no network access. Waiting is limited to 30 seconds, the caller's deadline and the run's lifetime. Cancellation is checked between bounded host calls (at most one second each).
func WaitStart ¶ added in v0.3.0
func WaitStart(ctx context.Context, experiment experimentwire.Experiment) error
WaitStart waits for the agreed wall-clock time. It rejects a start already in the past; callers should record their observed start themselves. This helper cannot guarantee synchronized clocks or simultaneous execution across hosts.
Types ¶
type Conn ¶
type Conn struct {
// contains filtered or unexported fields
}
Conn is a connection handle returned by the Connect* and AcceptTCP helpers. It wraps the integer socket handle owned by the host and routes Write/Read/Close to the correct host functions for its transport. TLS and accepted TCP connections are plain TCP streams to the SDK.
func AcceptTCP ¶
AcceptTCP blocks until the host's TCP listener accepts one inbound connection and returns it. Used by server-style debuglets (e.g. an echo or throughput sink). Requires the executor's TCP listener to be enabled.
func ConnectICMP4 ¶
ConnectICMP4 opens a raw ICMPv4 socket to addr (an IPv4 address, no port). Use it to send/receive ICMP echo packets, e.g. for a ping probe.
func ConnectTCP ¶
ConnectTCP dials a plaintext TCP connection to addr ("host:port").
func ConnectTLS ¶
ConnectTLS dials a TLS-over-TCP connection to addr ("host:port"). The host performs the TLS handshake; reads and writes carry plaintext application data.
func ConnectUDP ¶
ConnectUDP dials a UDP connection to addr ("host:port"). The returned Conn sends and receives datagrams on the connected socket; no UDP listener is required to use it.
func (*Conn) Handle ¶
Handle returns the raw host socket handle. Most debuglets do not need this; it is exposed for diagnostics and advanced use.
func (*Conn) RemoteAddr ¶
type IOError ¶ added in v0.3.0
type IOError uint32
IOError is a stable transport outcome from the debuglet_io_v1 extension. Use errors.Is(err, ErrTimeout), for example, to handle a wrapped outcome.
const ( ErrTimeout IOError = guestio.Timeout ErrReset IOError = guestio.Reset ErrClosed IOError = guestio.Closed ErrRefused IOError = guestio.Refused ErrDenied IOError = guestio.Denied ErrIO IOError = guestio.Failure ErrCanceled IOError = guestio.Canceled ErrUnsupported IOError = guestio.Unsupported )
type Socket ¶ added in v0.3.0
type Socket struct {
// contains filtered or unexported fields
}
Socket is an io.ReadWriteCloser with recoverable transport errors. Unlike Conn, its Write method returns a byte count. It requires debuglet_io_v1; existing Connect* functions and compiled guests keep their legacy behavior.
func AcceptTCPTimeout ¶ added in v0.3.0
AcceptTCPTimeout accepts an admitted connection on the run's TCP listener. The timeout bounds the whole accept, including refused peers. Zero uses the run's remaining lifetime; a negative timeout expires immediately. ErrTimeout leaves the listener usable for another call. The returned Socket has its own read/write deadlines and remains open until Close or the run ends.
This requires the accept_tcp export in debuglet_io_v1 and a run with TCP listening enabled. Older hosts without that export fail to link the guest. Use short timeouts in event loops: guest goroutines cannot make progress while the host is blocked accepting a connection.
func Dial ¶ added in v0.3.0
Dial connects using "tcp", "tls", "udp" or "ip4:icmp". Policy and resource admission are identical to Connect*. The run's lifetime bounds the call.
func DialTimeout ¶ added in v0.3.0
DialTimeout also bounds resolution, connection and TLS handshake. Zero uses the run's remaining lifetime; a negative timeout expires immediately.