binding

package
v0.0.0-...-eda937b Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 22, 2026 License: Apache-2.0, MIT Imports: 14 Imported by: 0

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

Examples

Constants

View Source
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

func NewMalformedArgumentsError(cause error) error

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

type Binding[Args, OK CBORValue] struct {
	command.Command
}

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

func Bind[Args, OK CBORValue](cmd command.Command) Binding[Args, OK]

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.

func (Binding[Args, OK]) Unpack

func (c Binding[Args, OK]) Unpack(rcpt ucan.Receipt) (OK, error)

Unpack the command's typed result (OK) from a receipt. If the receipt reports a failure, Unpack decodes the standard error model and returns it as an error.

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

func (r *Request[Args]) Task() *Task[Args]

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

func (r *Response[OK]) Metadata() ucan.Container

Metadata returns the metadata container attached to the response, if any.

func (*Response[OK]) Receipt

func (r *Response[OK]) Receipt() ucan.Receipt

Receipt returns the receipt recording the task's outcome, set by SetSuccess, SetFailure, or SetReceipt.

func (*Response[OK]) SetFailure

func (r *Response[OK]) SetFailure(x error) error

SetFailure issues and sets a receipt reporting that the task failed with x.

func (*Response[OK]) SetMetadata

func (r *Response[OK]) SetMetadata(meta ucan.Container) error

SetMetadata attaches a metadata container to the response.

func (*Response[OK]) SetReceipt

func (r *Response[OK]) SetReceipt(receipt ucan.Receipt) error

SetReceipt sets a pre-built receipt as the task's outcome.

func (*Response[OK]) SetSuccess

func (r *Response[OK]) SetSuccess(o OK) error

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

type SignerSetter interface {
	SetSigner(ucan.Signer) error
}

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.

func (*Task[Args]) Arguments

func (t *Task[Args]) Arguments() Args

Arguments returns the arguments bound to the type for this task.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL