target
One workload, on one interface of it, that this task's calls go to for as long as the handle is alive.
A handle is per interface, not per workload, because a workload need only export some of what a plugin imports. Binding both together is what makes the pairing checkable when it is made rather than when it is used: a handle exists only if that workload really serves that interface, so holding one is proof the call can be routed.
Handles nest per interface: the innermost live handle for the interface being called wins, and dropping it restores the one it shadowed. A handle for one interface does not redirect calls on another — those fall back to the workload whose capability call this task is serving, the same as if no handle were held, so a plugin calling back into its own caller needs no handle at all.
A handle also pins which component serves the interface, so a call it
scopes cannot be moved to another mid-dispatch. It does not pin the
workload: one held past the workload's stop — or past a redeploy under
the same id, which is a different workload as far as this interface is
concerned — answers not-running on the next call through it. Open a
handle for the work at hand rather than stashing one.
resource target;F get-workload-id
get-workload-id: func() -> workload-id;The workload this handle names.
F get-interface
get-interface: func() -> string;The interface this handle routes.
F open
open: func(id: workload-id, interface: string) -> option<target>;Direct this task's calls on interface to id, or none when that
workload is not currently callable on it — it is not running, or it
does not export that interface. %interface is spelled as it appears
in callable, which is where the pairs come from.
Check this rather than assuming: what callable returns is a
snapshot, and a workload can stop between reading it and acting on
it. A plugin's own wasi:cli/run dispatch loop races exactly that
way every time a workload is undeployed.