Documentation
¶
Overview ¶
Package binding provides Binding, a typed UCAN command: a command path paired with the Go types of the arguments it accepts (Args) and the result it returns (OK). A Binding makes those types the single definition for a command, so they are checked consistently everywhere the command is used:
- Invoke encodes Args into an invocation (on the client).
- A handler decodes Args from the invocation and encodes OK into the receipt (on the server), through Request[Args] and Response[OK].
- Unpack decodes OK out of the receipt (on the client).
These checks happen at compile time. The wire is still validated at run time: a peer may send bytes that do not conform to Args or OK, which surfaces as a decode error.
Declare each command once with Bind and a command from the command package, then derive every use from that one value:
var Info = binding.Bind[*InfoArgs, *InfoOK](command.MustParse("/space/info"))
inv, err := Info.Invoke(issuer, subject, &InfoArgs{...}) // client
out, err := Info.Unpack(receipt) // client
On the server, register a handler for the command in one of three ways, from most to least typed:
- server.NewRoute bundles the command and a typed handler into a Route, so they cannot diverge — the usual choice.
- Binding.Handler (or the free NewHandler) adapts a typed handler into the untyped execution.HandlerFunc a server registers.
- registering a raw execution.HandlerFunc against the embedded command.Command directly, bypassing Args/OK typing, when a handler needs the lower-level request and response (e.g. custom transport metadata).
See the package example for the full client-and-server round trip.
Example ¶
Example walks the full lifecycle of a typed command: a client invokes it with typed arguments, a server handles it and returns a typed result, and the client unpacks that result — all without hand-written encoding or decoding.
package main
import (
"context"
"fmt"
"github.com/fil-forge/ucantone/binding"
"github.com/fil-forge/ucantone/execution"
"github.com/fil-forge/ucantone/principal/ed25519"
tdm "github.com/fil-forge/ucantone/testutil/datamodel"
"github.com/fil-forge/ucantone/ucan/command"
)
// echo is the single declaration of the /example/echo command: its path bound
// to the Go type of the arguments it accepts (*tdm.TestObject) and the result
// it returns (*tdm.TestObject2). Invoke, Handler, and Unpack below all derive
// their types from echo, so a mismatched argument or result type is a compile
// error rather than a run-time surprise.
var echo = binding.Bind[*tdm.TestObject, *tdm.TestObject2](command.MustParse("/example/echo"))
// Example walks the full lifecycle of a typed command: a client invokes it with
// typed arguments, a server handles it and returns a typed result, and the
// client unpacks that result — all without hand-written encoding or decoding.
func main() {
client, _ := ed25519.Generate()
service, _ := ed25519.Generate()
// Client: invoke the command with typed arguments. Invoke encodes them into
// a signed invocation addressed to the service.
inv, _ := echo.Invoke(client, service.DID(), &tdm.TestObject{Bytes: []byte("hi")})
// Server: a typed handler receives the decoded arguments and sets a typed
// result. Handler adapts it to the untyped handler the server registers.
handle := echo.Handler(func(req *binding.Request[*tdm.TestObject], res *binding.Response[*tdm.TestObject2]) error {
args := req.Task().Arguments()
return res.SetSuccess(&tdm.TestObject2{Str: string(args.Bytes)})
})
xreq := execution.NewRequest(context.Background(), inv)
xres, _ := execution.NewResponse(inv.Task().Link(), execution.WithSigner(service))
_ = handle(xreq, xres)
// Client: unpack the typed result (OK) out of the receipt.
out, _ := echo.Unpack(xres.Receipt())
fmt.Println(out.Str)
}
Output: hi
Index ¶
- Constants
- func NewHandler[Args cbg.CBORUnmarshaler, OK cbg.CBORMarshaler](handler HandlerFunc[Args, OK]) execution.HandlerFunc
- func NewMalformedArgumentsError(cause error) error
- func NewRoute[Args, OK CBORValue](cmd Binding[Args, OK], fn HandlerFunc[Args, OK]) server.Route
- type Binding
- func (c Binding[Args, OK]) Delegate(issuer ucan.Signer, audience did.DID, subject did.DID, ...) (ucan.Delegation, error)
- func (c Binding[Args, OK]) Handler(fn HandlerFunc[Args, OK]) execution.HandlerFunc
- func (c Binding[Args, OK]) Invoke(issuer ucan.Signer, subject did.DID, arguments Args, ...) (ucan.Invocation, error)
- func (c Binding[Args, OK]) Route(fn HandlerFunc[Args, OK]) server.Route
- func (c Binding[Args, OK]) Unpack(rcpt ucan.Receipt) (OK, error)
- type CBORValue
- type HandlerFunc
- type Request
- type RequestOption
- type Response
- func (r *Response[OK]) Metadata() ucan.Container
- func (r *Response[OK]) Receipt() ucan.Receipt
- func (r *Response[OK]) SetFailure(x error) error
- func (r *Response[OK]) SetMetadata(meta ucan.Container) error
- func (r *Response[OK]) SetReceipt(receipt ucan.Receipt) error
- func (r *Response[OK]) SetSuccess(o OK) error
- type ResponseOption
- func WithFailure[OK cbg.CBORMarshaler](signer ucan.Signer, task cid.Cid, x error) ResponseOption[OK]
- func WithMetadata[OK cbg.CBORMarshaler](meta ucan.Container) ResponseOption[OK]
- func WithReceipt[OK cbg.CBORMarshaler](receipt ucan.Receipt) ResponseOption[OK]
- func WithSigner[OK cbg.CBORMarshaler](signer ucan.Signer) ResponseOption[OK]
- func WithSuccess[OK cbg.CBORMarshaler](o OK) ResponseOption[OK]
- type SignerSetter
- type Task
Examples ¶
Constants ¶
const MalformedArgumentsErrorName = "MalformedArguments"
MalformedArgumentsErrorName is the error name reported in a receipt when an invocation's arguments cannot be decoded into the command's Args type.
Variables ¶
This section is empty.
Functions ¶
func NewHandler ¶
func NewHandler[Args cbg.CBORUnmarshaler, OK cbg.CBORMarshaler](handler HandlerFunc[Args, OK]) execution.HandlerFunc
NewHandler adapts a typed HandlerFunc into the untyped execution.HandlerFunc a server registers. It decodes the invocation's arguments into Args before calling handler, reporting a MalformedArgumentsErrorName failure if they do not conform.
func NewMalformedArgumentsError ¶
NewMalformedArgumentsError builds the error a handler returns when argument decoding fails; see NewHandler, which reports it automatically.
func NewRoute ¶
func NewRoute[Args, OK CBORValue](cmd Binding[Args, OK], fn HandlerFunc[Args, OK]) server.Route
NewRoute builds a [Route] from a command binding and its handler. The command and handler are taken from the same binding.Binding, so they cannot diverge, and the handler's argument and result types are checked against the command's (Args and OK) at compile time.
Types ¶
type Binding ¶
Binding ties a command to its typed argument (Args) and result (OK) types. Both must round-trip through CBOR (CBORValue): Args is encoded by Invoke and decoded when the command is handled; OK is encoded by the handler and decoded by Unpack.
func Bind ¶
Bind pairs an already-valid command with the Go types of its arguments (Args) and result (OK). The command carries its own validity (it cannot be constructed except through command.Parse, command.MustParse, or command.New), so Bind cannot fail. Construct or parse the command with the command package, then attach its types here:
b := binding.Bind[*Args, *OK](command.MustParse("/space/info"))
func (Binding[Args, OK]) Delegate ¶
func (c Binding[Args, OK]) Delegate(issuer ucan.Signer, audience did.DID, subject did.DID, options ...delegation.Option) (ucan.Delegation, error)
Delegate issues a delegation granting authority over this command. It does not involve the argument or result types.
func (Binding[Args, OK]) Handler ¶
func (c Binding[Args, OK]) Handler(fn HandlerFunc[Args, OK]) execution.HandlerFunc
Handler adapts a typed handler into an execution.HandlerFunc for registration on a server. The handler's argument and result types are checked against the command's Args and OK at compile time.
func (Binding[Args, OK]) Invoke ¶
func (c Binding[Args, OK]) Invoke(issuer ucan.Signer, subject did.DID, arguments Args, options ...invocation.Option) (ucan.Invocation, error)
Invoke constructs a signed invocation of the command carrying the given typed arguments.
func (Binding[Args, OK]) Route ¶
func (c Binding[Args, OK]) Route(fn HandlerFunc[Args, OK]) server.Route
Route adapts a typed handler into an execution.HandlerFunc and wraps it in a server.Route along with the command for registration with a server. The handler's argument and result types are checked against the command's Args and OK at compile time.
type CBORValue ¶
type CBORValue interface {
cbg.CBORMarshaler
cbg.CBORUnmarshaler
}
CBORValue is any value that round-trips through CBOR: marshalled into a UCAN (e.g. an invocation's arguments or a receipt's result) and unmarshalled back out again. It is the single contract a typed command's argument and result types must satisfy.
type HandlerFunc ¶
type HandlerFunc[Args cbg.CBORUnmarshaler, OK cbg.CBORMarshaler] = func(*Request[Args], *Response[OK]) error
HandlerFunc handles an invocation of a command: it reads typed arguments from the Request and reports the outcome on the Response. Register it with a server via Binding.Handler, NewHandler, or server.NewRoute.
type Request ¶
type Request[Args cbg.CBORUnmarshaler] struct { execution.Request // contains filtered or unexported fields }
Request is an execution.Request whose invocation arguments have been decoded into the typed Args. A handler reads them through Task; see HandlerFunc.
func NewRequest ¶
func NewRequest[Args cbg.CBORUnmarshaler](ctx context.Context, inv ucan.Invocation, options ...RequestOption) (*Request[Args], error)
NewRequest decodes inv's arguments into Args and wraps it as a typed Request, attaching any proofs, delegations, or receipts supplied via options. It fails if the arguments do not conform to Args.
func (*Request[Args]) Task ¶
Task returns an object containing just the fields that comprise the task for the invocation.
https://github.com/ucan-wg/invocation/blob/main/README.md#task
type RequestOption ¶
type RequestOption = func(cfg *requestConfig)
RequestOption configures the proofs and supporting UCANs carried alongside the invocation in a Request built by NewRequest.
func WithDelegations ¶
func WithDelegations(delegations ...ucan.Delegation) RequestOption
WithDelegations adds delegations to the execution request. They should be linked from the invocation to be executed.
func WithInvocations ¶
func WithInvocations(invocations ...ucan.Invocation) RequestOption
WithInvocations adds additional invocations to the execution request.
func WithReceipts ¶
func WithReceipts(receipts ...ucan.Receipt) RequestOption
WithReceipts adds receipts to the execution request.
type Response ¶
type Response[OK cbg.CBORMarshaler] struct { // contains filtered or unexported fields }
Response is the result of executing a task. A handler reports the outcome by calling SetSuccess with a typed OK value or SetFailure with an error; binding encodes it into the receipt.
func NewResponse ¶
func NewResponse[OK cbg.CBORMarshaler](task cid.Cid, options ...ResponseOption[OK]) (*Response[OK], error)
NewResponse creates a response for the given task, applying any options (such as WithSuccess, WithFailure, or WithSigner) that set its outcome.
func (*Response[OK]) Metadata ¶
Metadata returns the metadata container attached to the response, if any.
func (*Response[OK]) Receipt ¶
Receipt returns the receipt recording the task's outcome, set by SetSuccess, SetFailure, or SetReceipt.
func (*Response[OK]) SetFailure ¶
SetFailure issues and sets a receipt reporting that the task failed with x.
func (*Response[OK]) SetMetadata ¶
SetMetadata attaches a metadata container to the response.
func (*Response[OK]) SetReceipt ¶
SetReceipt sets a pre-built receipt as the task's outcome.
func (*Response[OK]) SetSuccess ¶
SetSuccess issues and sets a receipt reporting that the task succeeded with the typed result o.
type ResponseOption ¶
type ResponseOption[OK cbg.CBORMarshaler] func(r *Response[OK]) error
ResponseOption configures a Response as it is built by NewResponse, typically setting its outcome (success or failure), signer, or metadata.
func WithFailure ¶
func WithFailure[OK cbg.CBORMarshaler](signer ucan.Signer, task cid.Cid, x error) ResponseOption[OK]
WithFailure issues and sets a receipt for a failed execution of a task.
func WithMetadata ¶
func WithMetadata[OK cbg.CBORMarshaler](meta ucan.Container) ResponseOption[OK]
WithMetadata attaches a metadata container to the response, carried alongside the receipt (e.g. transport-specific headers).
func WithReceipt ¶
func WithReceipt[OK cbg.CBORMarshaler](receipt ucan.Receipt) ResponseOption[OK]
WithReceipt sets a pre-built receipt as the response's outcome instead of issuing one from a success or failure value.
func WithSigner ¶
func WithSigner[OK cbg.CBORMarshaler](signer ucan.Signer) ResponseOption[OK]
WithSigner sets the signer used to issue the response's receipt. It fails if the underlying response cannot accept one (does not implement SignerSetter).
func WithSuccess ¶
func WithSuccess[OK cbg.CBORMarshaler](o OK) ResponseOption[OK]
WithSuccess issues and sets a receipt for a successful execution of a task.
type SignerSetter ¶
SignerSetter is implemented by responses that can issue their own receipt and therefore need a signer. WithSigner uses it to set that signer.
type Task ¶
type Task[Args cbg.CBORUnmarshaler] struct { *invocation.Task // contains filtered or unexported fields }
func NewTask ¶
func NewTask[Args cbg.CBORUnmarshaler]( subject did.DID, command ucan.Command, argsBytes []byte, nonce []byte, ) (*Task[Args], error)
NewTask constructs a typed task. argsBytes must be the raw CBOR encoding of the args (typically obtained from invocation.Invocation.ArgumentsBytes); the bytes are decoded directly into the typed argument struct Args via cborgen.