github.com/storacha/go-ucanto@v0.7.2/README.md (about)

     1  # go-ucanto
     2  
     3  [![Go Report Card](https://goreportcard.com/badge/github.com/storacha/go-ucanto)](https://goreportcard.com/report/github.com/storacha/go-ucanto)
     4  
     5  Ucanto UCAN RPC in Golang.
     6  
     7  ## Install
     8  
     9  ```console
    10  go get github.com/storacha/go-ucanto
    11  ```
    12  
    13  ## Usage
    14  
    15  ### Client
    16  
    17  ```go
    18  package main
    19  
    20  import (
    21    "..."
    22  )
    23  
    24  // service URL & DID
    25  serviceURL, _ := url.Parse("https://up.web3.storage")
    26  servicePrincipal, _ := did.Parse("did:web:web3.storage")
    27  
    28  // HTTP transport and CAR encoding
    29  channel := http.NewHTTPChannel(serviceURL)
    30  conn, _ := client.NewConnection(servicePrincipal, channel)
    31  
    32  // private key to sign UCANs with
    33  priv, _ := ioutil.ReadFile("path/to/private.key")
    34  signer, _ := ed25519.Parse(priv)
    35  
    36  audience := servicePrincipal
    37  
    38  type StoreAddCaveats struct {
    39  	Link ipld.Link
    40  	Size int
    41  }
    42  
    43  func (c StoreAddCaveats) ToIPLD() (datamodel.Node, error) {
    44  	return ipld.WrapWithRecovery(&c, StoreAddType())
    45  }
    46  
    47  func StoreAddType() ipldschema.Type {
    48  	ts, _ := ipldprime.LoadSchemaBytes([]byte(`
    49  		type StoreAdd struct {
    50  			link Link
    51  			size Int
    52  		}
    53  	`))
    54  	return ts.TypeByName("StoreAdd")
    55  }
    56  
    57  capability := ucan.NewCapability(
    58  	"store/add",
    59  	did.Parse("did:key:z6MkwDuRThQcyWjqNsK54yKAmzfsiH6BTkASyiucThMtHt1T").String(),
    60  	StoreAddCaveats{
    61  		// TODO
    62  	},
    63  )
    64  
    65  // create invocation(s) to perform a task with granted capabilities
    66  inv, _ := invocation.Invoke(signer, audience, capability, delegation.WithProofs(...))
    67  invocations := []invocation.Invocation{inv}
    68  
    69  // send the invocation(s) to the service
    70  resp, _ := client.Execute(context.Background(), invocations, conn)
    71  
    72  // define datamodels for ok and error outcome
    73  type OkModel struct {
    74    Status string
    75  }
    76  type ErrModel struct {
    77  	Message string
    78  }
    79  
    80  // create new receipt reader, passing the IPLD schema for the result and the
    81  // ok and error types
    82  reader, _ := receipt.NewReceiptReader[OkModel, ErrModel]([]byte(`
    83  	type Result union {
    84  		| Ok "ok"
    85  		| Err "error"
    86  	} representation keyed
    87  
    88  	type Ok struct {
    89  		status String
    90  	}
    91  
    92  	type Err struct {
    93  		message String
    94  	}
    95  `))
    96  
    97  // get the receipt link for the invocation from the response
    98  rcptlnk, _ := resp.Get(invocations[0].Link())
    99  // read the receipt for the invocation from the response
   100  rcpt, _ := reader.Read(rcptlnk, res.Blocks())
   101  
   102  fmt.Println(rcpt.Out().Ok())
   103  ```
   104  
   105  ### Server
   106  
   107  ```go
   108  package main
   109  
   110  import (
   111  	"..."
   112  )
   113  
   114  type TestEcho struct {
   115  	Echo string
   116  }
   117  
   118  func (c TestEcho) ToIPLD() (ipld.Node, error) {
   119  	return ipld.WrapWithRecovery(&c, EchoType())
   120  }
   121  
   122  func EchoType() ipldschema.Type {
   123  	ts, _ := ipldprime.LoadSchemaBytes([]byte(`
   124  		type TestEcho struct {
   125  			echo String
   126  		}
   127  	`))
   128  	return ts.TypeByName("TestEcho")
   129  }
   130  
   131  func createServer(signer principal.Signer) (server.ServerView, error) {
   132  	// Capability definition(s)
   133  	testecho := validator.NewCapability(
   134  		"test/echo",
   135  		schema.DIDString(),
   136  		schema.Struct[TestEcho](EchoType(), nil),
   137  		validator.DefaultDerives,
   138  	)
   139  
   140  	return server.NewServer(
   141  		signer,
   142  		// Handler definitions
   143  		server.WithServiceMethod(
   144  			testecho.Can(),
   145  			server.Provide(
   146  				testecho,
   147  				func(ctx context.Context, cap ucan.Capability[TestEcho], inv invocation.Invocation, ictx server.InvocationContext) (TestEcho, receipt.Effects, error) {
   148  					return TestEcho{Echo: cap.Nb().Echo}, nil, nil
   149  				},
   150  			),
   151  		),
   152  	)
   153  }
   154  
   155  func main() {
   156  	signer, _ := ed25519.Generate()
   157  	server, _ := createServer(signer)
   158  
   159  	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
   160  		res, _ := server.Request(r.Context(), uhttp.NewHTTPRequest(r.Body, r.Header))
   161  
   162  		for key, vals := range res.Headers() {
   163  			for _, v := range vals {
   164  				w.Header().Add(key, v)
   165  			}
   166  		}
   167  
   168  		if res.Status() != 0 {
   169  			w.WriteHeader(res.Status())
   170  		}
   171  
   172  		io.Copy(w, res.Body())
   173  	})
   174  
   175  	listener, _ := net.Listen("tcp", ":0")
   176  
   177  	port := listener.Addr().(*net.TCPAddr).Port
   178  	fmt.Printf("{\"id\":\"%s\",\"url\":\"http://127.0.0.1:%d\"}\n", signer.DID().String(), port)
   179  
   180  	http.Serve(listener, nil)
   181  }
   182  ```
   183  
   184  ## API
   185  
   186  [pkg.go.dev Reference](https://pkg.go.dev/github.com/storacha/go-ucanto)
   187  
   188  ## Related
   189  
   190  * [Ucanto in Javascript](https://github.com/storacha/ucanto)
   191  
   192  ## Contributing
   193  
   194  Feel free to join in. All welcome. Please [open an issue](https://github.com/storacha/go-ucanto/issues)!
   195  
   196  ## License
   197  
   198  Dual-licensed under [MIT + Apache 2.0](LICENSE.md)