21 Commits
Author SHA1 Message Date
decentral1se 3b6f179add docs: build badge 2026-07-31 21:39:27 +02:00
decentral1se 296a4bc690 test: first run at testing 2026-07-31 21:39:27 +02:00
decentral1se 1b28cd7e20 refactor: rework examples, wording and fmt.Errorf 2026-07-31 21:17:40 +02:00
cblgh cf4b3933e4 improve reading experience of docstrings + fix typos and a few erroneous messages 2026-07-31 18:23:09 +02:00
decentral1se 412c2f48ea feat: better error handling 2026-07-23 17:57:42 +02:00
decentral1se b60f37f85e fix: build examples from local HEAD 2026-07-17 13:50:57 +02:00
decentral1se 68ad06fe2a feat: v1.1.0 bindings
See https://github.com/n0-computer/iroh-ffi/releases/tag/v1.1.0
2026-07-17 13:08:20 +02:00
decentral1se 14a1ebf8d8 docs(README): more known issues 2026-07-03 10:59:14 +02:00
decentral1se c4522df071 docs(iroh-relay): how to use own relay 2026-07-03 10:30:33 +02:00
decentral1se 0858f533fa docs: point to iroh-tracker 2026-07-02 08:46:50 +02:00
decentral1se b2c2f9363c feat: apache2 & mit licenses 2026-06-28 00:11:53 +02:00
decentral1se c668dd01d8 docs: add godocs 2026-06-26 15:42:01 +02:00
decentral1se d8668c7949 chore: ignore timeserve and sort 2026-06-26 06:53:38 +02:00
decentral1se f3c78b79ff docs(README): bindings 2026-06-26 06:43:08 +02:00
decentral1se 0da4eb7dd2 chore: update libs 2026-06-26 06:41:22 +02:00
decentral1se e7f90ff4f6 feat: add aarch64-unknown-linux-musl 2026-06-26 06:40:52 +02:00
decentral1se c151d398bc docs(README): share known state 2026-06-26 06:31:43 +02:00
decentral1se 7162741cb0 fix: use forked iroh-ffi for now 2026-06-26 06:03:22 +02:00
decentral1se 6be98f24b7 chore: remove unused file 2026-06-26 06:03:03 +02:00
decentral1se fcd131a0e4 chore: formatting and minor typos 2026-06-26 06:02:41 +02:00
cblgh a2494e624c examples(connect-desktop): update title 2026-06-25 17:26:14 +02:00
36 changed files with 801 additions and 205 deletions
+20
View File
@@ -0,0 +1,20 @@
---
kind: pipeline
name: git.coopcloud.tech/decentral1se/iroh-go
steps:
- name: go build
image: golang:1.26
commands:
- go build -v ./...
- name: examples
image: golang:1.26
commands:
- for ex in $(find examples -mindepth 1 -maxdepth 1 -type d -not -path "*-desktop"); do make -C $ex; done
depends_on:
- go build
trigger:
action:
exclude:
- synchronized
+5 -3
View File
@@ -1,7 +1,9 @@
.*.sw[a-z]
examples/connect/connect
examples/pairchat/pairchat
iroh-go/target/
examples/connect-desktop/build
examples/connect-desktop/frontend/dist
examples/connect-desktop/frontend/wailsjs
examples/connect/connect
examples/errors/errors
examples/pairchat/pairchat
examples/timeserve/timeserve
iroh-go/target/
+86 -10
View File
@@ -1,18 +1,94 @@
# iroh-go
# `iroh-go`
> **WARNING** Extremely Experimental ™️
[![Build Status](https://build.coopcloud.tech/api/badges/decentral1se/iroh-go/status.svg?ref=refs/heads/main)](https://build.coopcloud.tech/decentral1se/iroh-go)
Go bindings for [`iroh-ffi`](https://github.com/n0-computer/iroh-ffi). They're
ready for testing but due to [this
issue](https://github.com/n0-computer/iroh-ffi/issues/248#issuecomment-4777541073),
there are surely unknown horrors awaiting you deep inside the `staticlib`. It's
a work in progress but you can yeet some bytes across the interwebz Right Now
> **WARNING** Highly Experimental ™️
Go FFI bindings for [`iroh`](https://iroh.computer) via
[`iroh-ffi`](https://github.com/n0-computer/iroh-ffi).
## Motivation
The goal is to provide, as far as possible, seamless "just `go build` it"
support for Go iroh hackers without requiring them to deal with shared
libraries, the Rust toolchain or a deepdive in how FFI works. The current
approach is to embed `libiroh` as a `staticlib` built with Rust for each
platform. We use [`musl`](https://musl.libc.org) to avoid `glibc`
incompatibilities on different linux systems. It's then possible to build a
static binary with `go build` or [with
Zig](https://dev.to/kristoff/zig-makes-go-cross-compilation-just-work-29ho).
`iroh-go` does require `CGO_ENABLED=1`.
## Platform support
* `x86_64-unknown-linux-musl`
* `aarch64-unknown-linux-musl`
## Documentation
See [`docs.rs`](https://docs.rs/iroh/1.0.0/iroh/) for the original Rust API
documentation. The exposed Go API is 1:1. Here's the Go bindings documentation
on [`godocs.io`](https://godocs.io/git.coopcloud.tech/decentral1se/iroh-go).
## Examples
See [`examples`](./examples).
## Rebuild `iroh-ffi`
## Run your own `iroh-relay`
`./scripts/generate.sh`
See [`iroh-relay`](https://git.coopcloud.tech/decentral1se/iroh-relay) if you
want more small footprint static binary goodness. You can also make use of the
upstream provided docker image: `n0computer/iroh-relay`.
### Local hacking
First, run `iroh-relay` in development mode with noisy logging.
```
RUST_LOG=info,iroh_relay=debug ./iroh-relay --dev
```
You can then use `http://localhost:3340` as the default location of your own
relay in the construction of your endpoint as follows. Once you create an
endpoint, you should see your relay respond locally with corresponding logs.
```go
preset := iroh.PresetN0DisableRelay()
relay, _ := iroh.RelayModeCustomFromUrls([]string{"http://localhost:3340"})
opts := iroh.EndpointOptions{
Preset: &preset,
RelayMode: &relay,
// ...moar config...
}
```
## Generate bindings
See [`iroh-go`](./iroh-go) for the Rust toolchain configuration. Run
`./scripts/generate.sh` to build a new set of libraries which end up vendored
in [`libs`](./libs).
## Who is using it?
* [`iroh-tracker`](https://git.coopcloud.tech/cblgh/iroh-tracker)
## Known issues
* We need to patch the upstream `iroh-ffi` `endpoint.rs` to fix an
incompatibility with
[`uniffi-bindgen-go`](https://github.com/NordSecurity/uniffi-bindgen-go). See
[this
commit](https://github.com/decentral1se/iroh-ffi/commit/3e102569e1c348480416a9b05cf020760f607dfe)
for more.
* We need to patch the upstream `iroh-ffi` `Cargo.toml` to remove the
`crate-type` configuration when runnings builds here. See [this
commit](https://github.com/decentral1se/iroh-ffi/commit/207e5d7cd7dee6eab350d3aad85029431a63ca3f)
for more.
* Error handling is extremely inconvenient due to
[`iroh-ffi#263`](https://github.com/n0-computer/iroh-ffi/issues/263).
Proposals for improvement are very welcome.
## Licenses
Apache 2.0 & MIT, following the upstream licensing choices in
[`iroh-ffi`](https://github.com/n0-computer/iroh-ffi).
+111
View File
@@ -0,0 +1,111 @@
package iroh_ffi
import (
"errors"
"fmt"
)
// `As` casts an error to IrohError and back, extracting relevant error message
// information. It is a single argument variant of the typical errors.As API
// because we always expect an IrohError back from the iroh FFI bindings. The
// bindings do not return an IrohError for reasons and will not for the
// forseeable future due to API stability guarantees.
func As(err error) error {
if err, ok := errors.AsType[IrohError](err); ok {
return fmt.Errorf("%s: %s", err.Message(), err.DebugMessage())
}
return err
}
// `Is` supports matching against the stable iroh error taxonomy using the
// error sentinel construction below. The caller must pass in a valid error
// sentinel specified by this library, e.g. iroh.BindError.
func (e *IrohError) Is(target error) bool {
if err, ok := errors.AsType[sentinel](target); ok {
return uint(e.Kind()) == err.kind
}
return false
}
type sentinel struct {
kind uint
message string
}
func (s sentinel) Error() string {
return s.message
}
func (s sentinel) String() string {
return s.message
}
var InvalidInputError error = sentinel{
kind: uint(IrohErrorKindInvalidInput),
message: "iroh: invalid input supplied by the caller",
}
var BindError error = sentinel{
kind: uint(IrohErrorKindBind),
message: "iroh: failure while binding an endpoint",
}
var ConnectError error = sentinel{
kind: uint(IrohErrorKindConnect),
message: "iroh: failure while initiating or completing an outgoing connection",
}
var ConnectionError error = sentinel{
kind: uint(IrohErrorKindConnection),
message: "iroh: an established connection failed or closed unexpectedly",
}
var AlpnError error = sentinel{
kind: uint(IrohErrorKindAlpn),
message: "iroh: ALPN negotiation or lookup failed",
}
var KeyParsingError error = sentinel{
kind: uint(IrohErrorKindKeyParsing),
message: "iroh: endpoint id / public key parsing failed",
}
var TicketParsingError error = sentinel{
kind: uint(IrohErrorKindKeyParsing),
message: "iroh: ticket parsing failed",
}
var RelayError error = sentinel{
kind: uint(IrohErrorKindRelay),
message: "iroh: relay configuration or relay operation failed",
}
var StreamError error = sentinel{
kind: uint(IrohErrorKindStream),
message: "iroh: stream read/write/control operation failed",
}
var DatagramError error = sentinel{
kind: uint(IrohErrorKindDatagram),
message: "iroh: datagram send/receive operation failed",
}
var FCallbackError error = sentinel{
kind: uint(IrohErrorKindCallback),
message: "iroh: foreign callback failed",
}
var ClosedError error = sentinel{
kind: uint(IrohErrorKindClosed),
message: "iroh: operation was attempted on a closed stream/connection/resource",
}
var TimeoutError error = sentinel{
kind: uint(IrohErrorKindTimeout),
message: "iroh: operation timed out",
}
var InternalError error = sentinel{
kind: uint(IrohErrorKindInternal),
message: "iroh: unclassified internal error",
}
+26 -24
View File
@@ -1,34 +1,34 @@
# connect-desktop-example
# connect-desktop
A version of [`../connect`](../connect) as a desktop application. On running the desktop
application, an Iroh endpoint id will be displayed. This id can be pluggedinto a terminal
running the [`../connect`](../connect) example as a sender. What the sender types in the
terminal will appear in the connect-desktop application window.
> **WARNING** Due to Wails requiring some additional system dependencies, we
> are not testing this example regularly on CI/CD. Therefore, this example
> might not build when you try it. Contributions are very welcome!
A version of [`../connect`](../connect) as a desktop application. When running
the desktop application, an Iroh endpoint id will be displayed. This id can be
plugged into a terminal running the [`../connect`](../connect) example as a
sender. What the sender types in the terminal will appear in the
connect-desktop application window.
## Dependencies
* Wails v2.10.2 (old version, haven't tried on a newer yet - hopefully works there too!)
* Vite v3.0.7
* Go v1.18
* npm v10.2+
* node v20.11+
This example has been tested on the following versions.
**Getting setup**
* Wails v2.10.2
* Node v20.11+
* Install node and golang
Please consult the [Wails
docs](https://wails.io/docs/gettingstarted/installation) for everything you
need to get up and running.
Install Wails:
### Wails
```
go install github.com/wailsapp/wails/v2/cmd/wails@v2.10.2 // for tested version
// OR: go install github.com/wailsapp/wails/v2/cmd/wails@latest // for latest
// then install frontend dependency
cd frontend
npm i
go install github.com/wailsapp/wails/v2/cmd/wails@v2.10.2
cd frontend && npm i
```
## Running in development mode
## Development mode
```
wails dev
@@ -48,11 +48,13 @@ wails build .
## Terminal 2 (sender)
Run [`../connect`](../connect) in a terminal and connect to the endpoint id displayed by the
listener.
Run [`../connect`](../connect) in a terminal and connect to the endpoint id
displayed by the listener.
```
./connect -endpoint <endpoint-id>
```
You'll see a connection notice if the two endpoints manage to connect to each other. You can
type messages in the sender terminal and they will be shown on the screen of the desktop app.
You'll see a connection notice if the two endpoints manage to connect to each
other. You can type messages in the sender terminal and they will be shown on
the screen of the desktop app.
+20 -21
View File
@@ -2,11 +2,11 @@ package main
import (
"context"
"errors"
"fmt"
"strings"
iroh "git.coopcloud.tech/decentral1se/iroh-go"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
var (
@@ -15,16 +15,9 @@ var (
endpointPeer *string
)
func check(err error) {
if err != nil {
irohErr := err.(*iroh.IrohError)
panic(irohErr.Message())
}
}
// App struct
type App struct {
ctx context.Context
ctx context.Context
endpoint *iroh.Endpoint
}
@@ -45,7 +38,9 @@ func (a *App) startup(ctx context.Context) {
}
endpoint, err := iroh.EndpointBind(opts)
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
endpoint.Online()
a.endpoint = endpoint
@@ -63,28 +58,32 @@ func listen(ctx context.Context, e *iroh.Endpoint) {
}
}
func handleIncoming (ctx context.Context, incoming *iroh.Incoming) {
func handleIncoming(ctx context.Context, incoming *iroh.Incoming) {
accepting, err := incoming.Accept()
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
conn, err := accepting.Connect()
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
stream, err := conn.AcceptBi()
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
recv := stream.Recv()
for {
frame, err := recv.Read(frameSize)
// handle timeout
if err != nil {
irohErr := err.(*iroh.IrohError)
errMsg := irohErr.Message()
if strings.HasPrefix(errMsg, "ConnectionLost") {
if errors.Is(err, iroh.TimeoutError) {
fmt.Println("lost connection")
break
} else {
check(err)
}
if err := iroh.As(err); err != nil {
panic(err)
}
}
runtime.EventsEmit(ctx, "data", map[string]interface{}{
+2
View File
@@ -36,3 +36,5 @@ require (
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
)
replace git.coopcloud.tech/decentral1se/iroh-go => ../../
-2
View File
@@ -1,5 +1,3 @@
git.coopcloud.tech/decentral1se/iroh-go v0.0.0-20260623204924-1a469c4ae933 h1:NcMTCeSpf7j5zuXMsvqvUpQMI1QEyjQoiEPlOcxqQ5w=
git.coopcloud.tech/decentral1se/iroh-go v0.0.0-20260623204924-1a469c4ae933/go.mod h1:UdtdmIaX3URspGOweIFN1Xlv0KuRyHGKo7PDjtUxPqE=
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+3 -5
View File
@@ -16,7 +16,7 @@ func main() {
app := NewApp()
// Create application with options
err := wails.Run(&options.App{
if err := wails.Run(&options.App{
Title: "go-iroh-desktop-example",
Width: 1024,
Height: 768,
@@ -28,9 +28,7 @@ func main() {
Bind: []interface{}{
app,
},
})
if err != nil {
println("Error:", err.Error())
}); err != nil {
panic(err)
}
}
+32 -24
View File
@@ -2,9 +2,9 @@ package main
import (
"bufio"
"errors"
"flag"
"fmt"
"strings"
"os"
"os/signal"
"syscall"
@@ -18,13 +18,6 @@ var (
endpointPeer *string
)
func check(err error) {
if err != nil {
irohErr := err.(*iroh.IrohError)
panic(irohErr.Message())
}
}
func awaitInterrupt() {
done := make(chan os.Signal, 1)
signal.Notify(done, syscall.SIGINT, syscall.SIGTERM)
@@ -39,22 +32,30 @@ func parseFlags() {
func send(e *iroh.Endpoint, id string) {
remote, err := iroh.EndpointIdFromString(id)
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
addr := iroh.NewEndpointAddr(remote, nil, nil)
conn, err := e.Connect(addr, alpn)
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
stream, err := conn.OpenBi()
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
fmt.Println("connected")
fmt.Println("connection established")
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
if line := scanner.Text(); line != "" {
err = stream.Send().WriteAll([]byte(line))
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
}
}
@@ -70,28 +71,32 @@ func listen(e *iroh.Endpoint) {
}
}
func handleIncoming (incoming *iroh.Incoming) {
func handleIncoming(incoming *iroh.Incoming) {
accepting, err := incoming.Accept()
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
conn, err := accepting.Connect()
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
stream, err := conn.AcceptBi()
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
recv := stream.Recv()
for {
frame, err := recv.Read(frameSize)
// handle timeout
if err != nil {
irohErr := err.(*iroh.IrohError)
errMsg := irohErr.Message()
if strings.HasPrefix(errMsg, "ConnectionLost") {
if errors.Is(err, iroh.TimeoutError) {
fmt.Println("lost connection")
break
} else {
check(err)
}
if err := iroh.As(err); err != nil {
panic(err)
}
}
fmt.Println(string(frame))
@@ -108,7 +113,10 @@ func main() {
}
endpoint, err := iroh.EndpointBind(opts)
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
endpoint.Online()
if *endpointPeer == "" {
+7
View File
@@ -0,0 +1,7 @@
module git.coopcloud.tech/decentral1se/iroh-go/examples/connect
go 1.26.1
require git.coopcloud.tech/decentral1se/iroh-go v0.0.0-20260717110820-68ad06fe2a25
replace git.coopcloud.tech/decentral1se/iroh-go => ../../
View File
+2
View File
@@ -0,0 +1,2 @@
default:
@go build -v ./...
+9
View File
@@ -0,0 +1,9 @@
# errors
This example shows a brief example of how to deal with error handling in a
convenient way. Unfortunately, the error handling coming down the pipes in the
FFI bindings is not ergonomic by default ends up creating extremely verbose
error handling. We've created an error handling wrapper API which we've tried
to make as intuitive as possible. See
[`#263`](https://github.com/n0-computer/iroh-ffi/issues/263) for the juicy
details.
+33
View File
@@ -0,0 +1,33 @@
package main
import (
"errors"
iroh "git.coopcloud.tech/decentral1se/iroh-go"
)
func main() {
preset := iroh.PresetN0DisableRelay()
opts := iroh.EndpointOptions{
Preset: &preset,
Alpns: &[][]byte{[]byte("iroh-go-errors/0")},
}
endpoint, err := iroh.EndpointBind(opts)
// NOTE(d1): The main route: convert errors to IrohError with `iroh.As`. This
// brings the `IrohError.Message`/`DebugMessage` back in a regular error
// value. This ensures we maintain `err != nil` compatibility
if err := iroh.As(err); err != nil {
panic(err)
}
// NOTE(d1): If you need more fine grained error catching: no `iroh.As`
// machinery is required. Sentinel checks with the usual `errors.Is` API are
// supported. It works as expected with `err` values
if err != nil && errors.Is(err, iroh.BindError) {
panic(err)
}
endpoint.Online()
}
+7
View File
@@ -0,0 +1,7 @@
module git.coopcloud.tech/decentral1se/iroh-go/examples/errors
go 1.26.1
require git.coopcloud.tech/decentral1se/iroh-go v0.0.0-20260717110820-68ad06fe2a25
replace git.coopcloud.tech/decentral1se/iroh-go => ../../
View File
+2
View File
@@ -0,0 +1,2 @@
default:
@go build -v ./...
+7
View File
@@ -0,0 +1,7 @@
module git.coopcloud.tech/decentral1se/iroh-go/examples/pairchat
go 1.26.1
require git.coopcloud.tech/decentral1se/iroh-go v0.0.0-20260717110820-68ad06fe2a25
replace git.coopcloud.tech/decentral1se/iroh-go => ../../
+2
View File
@@ -0,0 +1,2 @@
default:
@go build -v ./...
+34 -30
View File
@@ -2,9 +2,9 @@ package main
import (
"bufio"
"errors"
"flag"
"fmt"
"strings"
"os"
"os/signal"
"syscall"
@@ -18,13 +18,6 @@ var (
endpointPeer *string
)
func check(err error) {
if err != nil {
irohErr := err.(*iroh.IrohError)
panic(irohErr.Message())
}
}
func awaitInterrupt() {
done := make(chan os.Signal, 1)
signal.Notify(done, syscall.SIGINT, syscall.SIGTERM)
@@ -39,15 +32,21 @@ func parseFlags() {
func send(e *iroh.Endpoint, id string) {
remote, err := iroh.EndpointIdFromString(id)
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
addr := iroh.NewEndpointAddr(remote, nil, nil)
conn, err := e.Connect(addr, alpn)
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
stream, err := conn.OpenBi()
check(err)
fmt.Println("connected")
if err := iroh.As(err); err != nil {
panic(err)
}
fmt.Println("connection established")
recv := stream.Recv()
done := make(chan struct{})
go handleInput(stream, done)
@@ -61,15 +60,21 @@ func listen(e *iroh.Endpoint) {
}
}
func handleIncoming (incoming *iroh.Incoming) {
func handleIncoming(incoming *iroh.Incoming) {
accepting, err := incoming.Accept()
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
conn, err := accepting.Connect()
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
stream, err := conn.AcceptBi()
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
recv := stream.Recv()
done := make(chan struct{})
@@ -77,12 +82,14 @@ func handleIncoming (incoming *iroh.Incoming) {
go handleReading(recv, done)
}
func handleInput (stream *iroh.BiStream, done chan struct{}) {
func handleInput(stream *iroh.BiStream, done chan struct{}) {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
if line := scanner.Text(); line != "" {
err := stream.Send().WriteAll([]byte(line))
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
}
select {
@@ -102,23 +109,18 @@ func handleInput (stream *iroh.BiStream, done chan struct{}) {
func handleReading(recv *iroh.RecvStream, done chan struct{}) {
for {
frame, err := recv.Read(frameSize)
// handle timeout
if err != nil {
irohErr := err.(*iroh.IrohError)
errMsg := irohErr.Message()
if strings.HasPrefix(errMsg, "ConnectionLost") {
fmt.Println("lost connection")
break
} else {
check(err)
}
if errors.Is(err, iroh.TimeoutError) {
fmt.Println("lost connection")
break
}
if err := iroh.As(err); err != nil {
panic(err)
}
fmt.Println(string(frame))
}
close(done)
}
func main() {
parseFlags()
@@ -129,7 +131,9 @@ func main() {
}
endpoint, err := iroh.EndpointBind(opts)
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
endpoint.Online()
if *endpointPeer == "" {
+7 -6
View File
@@ -1,9 +1,9 @@
# timeserve
This example demonstrates that an endpoint can support multiple live connections at once. The
endpoint aka listener writes the time, once every 2 seconds, to every connected peer.
Try connecting to a running listener with 3 (or more!) receiver peers at once.
This example demonstrates that an endpoint can support multiple live
connections at once. The endpoint aka listener writes the time, once every 2
seconds, to every connected peer. Try connecting to a running listener with 3
(or more!) receiver peers at once.
## Build a binary
@@ -39,5 +39,6 @@ go build -v -a -ldflags '-extldflags "-static"' ./timeserve.go
./timeserve -endpoint <endpoint-id>
```
You'll see a connection notice if a receiver peer manages to connect to the listener.
Receivers (Terminal 2 and Terminal 3) will display the time sent by the listener (Terminal 1).
You'll see a connection notice if a receiver peer manages to connect to the
listener. Receivers (Terminal 2 and Terminal 3) will display the time sent by
the listener (Terminal 1).
+7
View File
@@ -0,0 +1,7 @@
module git.coopcloud.tech/decentral1se/iroh-go/examples/timeserve
go 1.26.1
require git.coopcloud.tech/decentral1se/iroh-go v0.0.0-20260717110820-68ad06fe2a25
replace git.coopcloud.tech/decentral1se/iroh-go => ../../
+2
View File
@@ -0,0 +1,2 @@
default:
@go build -v ./...
+44 -41
View File
@@ -1,11 +1,11 @@
package main
import (
"errors"
"flag"
"fmt"
"os"
"os/signal"
"strings"
"syscall"
"time"
@@ -18,13 +18,6 @@ var (
endpointPeer *string
)
func check(err error) {
if err != nil {
irohErr := err.(*iroh.IrohError)
panic(irohErr.Message())
}
}
func awaitInterrupt() {
done := make(chan os.Signal, 1)
signal.Notify(done, syscall.SIGINT, syscall.SIGTERM)
@@ -39,51 +32,47 @@ func parseFlags() {
func receiver(e *iroh.Endpoint, id string) {
remote, err := iroh.EndpointIdFromString(id)
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
addr := iroh.NewEndpointAddr(remote, nil, nil)
conn, err := e.Connect(addr, alpn)
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
// from docs:
// "The peer that calls open_bi must write to its SendStream before the peer Connection is able to accept the stream
// using accept_bi()"
// from docs: "The peer that calls open_bi must write to its SendStream
// before the peer Connection is able to accept the stream using accept_bi()"
// https://docs.rs/iroh/latest/iroh/endpoint/struct.Connection.html#method.accept_bi
stream, err := conn.OpenBi()
check(err)
// NOTE: for the other peer to realize the bidi stream is open, you have to send some data! the data sent cannot be
// the empty string ""; empty string == no data
if err := iroh.As(err); err != nil {
panic(err)
}
// NOTE: for the other peer to realize the bidi stream is open, you have to
// send some data! the data sent cannot be the empty string ""; empty string
// == no data
err = stream.Send().WriteAll([]byte{1})
if err := iroh.As(err); err != nil {
panic(err)
}
check(err)
handleReading(stream.Recv())
}
func handleReading(recv *iroh.RecvStream) {
for {
frame, err := recv.Read(frameSize)
if checkForTimeout(err) {
if errors.Is(err, iroh.TimeoutError) {
fmt.Println("lost connection")
break
}
fmt.Println(string(frame))
check(err)
}
}
func checkForTimeout(err error) bool {
// handle timeout
if err != nil {
irohErr := err.(*iroh.IrohError)
errMsg := irohErr.Message()
if strings.HasPrefix(errMsg, "ConnectionLost") {
fmt.Println("lost connection")
return true
} else {
// panics and exits bc unhandled err
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
fmt.Println(string(frame))
}
return false
}
func listen(e *iroh.Endpoint) {
@@ -96,20 +85,32 @@ func listen(e *iroh.Endpoint) {
func handleIncoming(incoming *iroh.Incoming) {
accepting, err := incoming.Accept()
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
conn, err := accepting.Connect()
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
stream, err := conn.AcceptBi()
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
for {
err := stream.Send().WriteAll([]byte(time.Now().Format(time.DateTime)))
if checkForTimeout(err) {
if errors.Is(err, iroh.TimeoutError) {
fmt.Println("lost connection")
break
}
// prints every two seconds (not for any technical reason, 1/sec just feels hectic!)
if err := iroh.As(err); err != nil {
panic(err)
}
// prints every two seconds (not for any technical reason, 1/sec just feels
// hectic!)
<-time.After(2 * time.Second)
}
}
@@ -124,7 +125,9 @@ func main() {
}
endpoint, err := iroh.EndpointBind(opts)
check(err)
if err := iroh.As(err); err != nil {
panic(err)
}
endpoint.Online()
if *endpointPeer == "" {
+20 -19
View File
@@ -432,9 +432,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "chacha20"
version = "0.10.0"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
dependencies = [
"cfg-if",
"cpufeatures 0.3.0",
@@ -1787,7 +1787,8 @@ dependencies = [
[[package]]
name = "iroh-ffi"
version = "1.0.0"
version = "1.1.0"
source = "git+https://github.com/decentral1se/iroh-ffi?rev=455ba0df43d07b1a28890a70f325b71b05ddeb88#455ba0df43d07b1a28890a70f325b71b05ddeb88"
dependencies = [
"anyhow",
"async-trait",
@@ -2074,9 +2075,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.102"
version = "0.3.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31"
checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
dependencies = [
"cfg-if",
"futures-util",
@@ -4221,9 +4222,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "uuid"
version = "1.23.3"
version = "1.23.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7"
checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53"
dependencies = [
"getrandom 0.4.3",
"js-sys",
@@ -4316,9 +4317,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen"
version = "0.2.125"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a"
checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
dependencies = [
"cfg-if",
"once_cell",
@@ -4329,9 +4330,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.75"
version = "0.4.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280"
checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -4339,9 +4340,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.125"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d"
checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -4349,9 +4350,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.125"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd"
checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
dependencies = [
"bumpalo",
"proc-macro2",
@@ -4362,9 +4363,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.125"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f"
checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
dependencies = [
"unicode-ident",
]
@@ -4384,9 +4385,9 @@ dependencies = [
[[package]]
name = "web-sys"
version = "0.3.102"
version = "0.3.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d"
checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141"
dependencies = [
"js-sys",
"wasm-bindgen",
+1 -10
View File
@@ -8,17 +8,8 @@ publish = false
name = "iroh"
crate-type = ["staticlib"]
[[bin]]
name = "uniffi-bindgen"
path = "src/uniffi-bindgen.rs"
[dependencies]
# NOTE(d1): locally patched with https://github.com/n0-computer/iroh-ffi/pull/254
# iroh-ffi = { git = "https://github.com/n0-computer/iroh-ffi" }
iroh-ffi = { path = "../../iroh-ffi" }
# [build-dependencies]
# uniffi = { version = "0.31.1", features = ["build"] }
iroh-ffi = { git = "https://github.com/decentral1se/iroh-ffi", rev = "455ba0df43d07b1a28890a70f325b71b05ddeb88" }
[profile.release]
codegen-units = 1
-3
View File
@@ -1,3 +0,0 @@
fn main() {
uniffi::uniffi_bindgen_main()
}
+212 -1
View File
@@ -975,6 +975,15 @@ func uniffiCheckChecksums() {
panic("iroh_ffi: uniffi_iroh_ffi_checksum_method_endpointbuilder_apply_n0_disable_relay: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_iroh_ffi_checksum_method_endpointbuilder_bind()
})
if checksum != 5850 {
// If this happens try cleaning and rebuilding your project
panic("iroh_ffi: uniffi_iroh_ffi_checksum_method_endpointbuilder_bind: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_iroh_ffi_checksum_method_endpointbuilder_bind_addr()
@@ -1173,11 +1182,38 @@ func uniffiCheckChecksums() {
panic("iroh_ffi: uniffi_iroh_ffi_checksum_method_sendstream_write_all: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_iroh_ffi_checksum_method_iroherror_debug_message()
})
if checksum != 33751 {
// If this happens try cleaning and rebuilding your project
panic("iroh_ffi: uniffi_iroh_ffi_checksum_method_iroherror_debug_message: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_iroh_ffi_checksum_method_iroherror_is_kind()
})
if checksum != 10479 {
// If this happens try cleaning and rebuilding your project
panic("iroh_ffi: uniffi_iroh_ffi_checksum_method_iroherror_is_kind: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_iroh_ffi_checksum_method_iroherror_kind()
})
if checksum != 11512 {
// If this happens try cleaning and rebuilding your project
panic("iroh_ffi: uniffi_iroh_ffi_checksum_method_iroherror_kind: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_iroh_ffi_checksum_method_iroherror_message()
})
if checksum != 64767 {
if checksum != 60838 {
// If this happens try cleaning and rebuilding your project
panic("iroh_ffi: uniffi_iroh_ffi_checksum_method_iroherror_message: UniFFI API checksum mismatch")
}
@@ -1461,6 +1497,15 @@ func uniffiCheckChecksums() {
panic("iroh_ffi: uniffi_iroh_ffi_checksum_constructor_endpoint_bind: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_iroh_ffi_checksum_constructor_endpointbuilder_new()
})
if checksum != 16347 {
// If this happens try cleaning and rebuilding your project
panic("iroh_ffi: uniffi_iroh_ffi_checksum_constructor_endpointbuilder_new: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_iroh_ffi_checksum_constructor_endpointid_from_bytes()
@@ -3906,6 +3951,13 @@ type EndpointBuilderInterface interface {
ApplyN0()
// Replay the n0 preset with relays disabled.
ApplyN0DisableRelay()
// Consume the builder and bind a new [`Endpoint`].
//
// The returned `Endpoint` has no protocol handlers — use
// [`Endpoint::bind`] with [`EndpointOptions::protocols`] to attach them.
// The builder is single-use; a second `bind` returns
// `EndpointBuilder already consumed`.
Bind() (*Endpoint, error)
// Set the address the endpoint binds to (`host:port`).
BindAddr(addr string) error
// Set the relay mode.
@@ -3925,6 +3977,15 @@ type EndpointBuilder struct {
ffiObject FfiObject
}
// Create a fresh empty endpoint builder. Apply a preset (`apply_n0`,
// `apply_minimal`, `apply_n0_disable_relay`) before [`bind`](Self::bind);
// the preset installs the crypto provider, without one `bind` will error.
func NewEndpointBuilder() *EndpointBuilder {
return FfiConverterEndpointBuilderINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint64_t {
return C.uniffi_iroh_ffi_fn_constructor_endpointbuilder_new(_uniffiStatus)
}))
}
// Set the advertised ALPNs.
func (_self *EndpointBuilder) Alpns(alpns [][]byte) {
_pointer := _self.ffiObject.incrementPointer("*EndpointBuilder")
@@ -3969,6 +4030,45 @@ func (_self *EndpointBuilder) ApplyN0DisableRelay() {
})
}
// Consume the builder and bind a new [`Endpoint`].
//
// The returned `Endpoint` has no protocol handlers — use
// [`Endpoint::bind`] with [`EndpointOptions::protocols`] to attach them.
// The builder is single-use; a second `bind` returns
// `EndpointBuilder already consumed`.
func (_self *EndpointBuilder) Bind() (*Endpoint, error) {
_pointer := _self.ffiObject.incrementPointer("*EndpointBuilder")
defer _self.ffiObject.decrementPointer()
res, err := uniffiRustCallAsync[*IrohError](
FfiConverterIrohErrorINSTANCE,
// completeFn
func(handle C.uint64_t, status *C.RustCallStatus) C.uint64_t {
res := C.ffi_iroh_ffi_rust_future_complete_u64(handle, status)
return res
},
// liftFn
func(ffi C.uint64_t) *Endpoint {
return FfiConverterEndpointINSTANCE.Lift(ffi)
},
C.uniffi_iroh_ffi_fn_method_endpointbuilder_bind(
_pointer),
// pollFn
func(handle C.uint64_t, continuation C.UniffiRustFutureContinuationCallback, data C.uint64_t) {
C.ffi_iroh_ffi_rust_future_poll_u64(handle, continuation, data)
},
// freeFn
func(handle C.uint64_t) {
C.ffi_iroh_ffi_rust_future_free_u64(handle)
},
)
if err == nil {
return res, nil
}
return res, err
}
// Set the address the endpoint binds to (`host:port`).
func (_self *EndpointBuilder) BindAddr(addr string) error {
_pointer := _self.ffiObject.incrementPointer("*EndpointBuilder")
@@ -4886,6 +4986,14 @@ func (_ FfiDestroyerIncoming) Destroy(value *Incoming) {
// An Error.
type IrohErrorInterface interface {
// Detailed debug representation of the original Rust error.
DebugMessage() string
// Convenience helper for bindings that do not expose enum comparison
// ergonomically.
IsKind(kind IrohErrorKind) bool
// Stable high-level error category.
Kind() IrohErrorKind
// Human-readable error message.
Message() string
}
@@ -4894,6 +5002,42 @@ type IrohError struct {
ffiObject FfiObject
}
// Detailed debug representation of the original Rust error.
func (_self *IrohError) DebugMessage() string {
_pointer := _self.ffiObject.incrementPointer("*IrohError")
defer _self.ffiObject.decrementPointer()
return FfiConverterStringINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
return GoRustBuffer{
inner: C.uniffi_iroh_ffi_fn_method_iroherror_debug_message(
_pointer, _uniffiStatus),
}
}))
}
// Convenience helper for bindings that do not expose enum comparison
// ergonomically.
func (_self *IrohError) IsKind(kind IrohErrorKind) bool {
_pointer := _self.ffiObject.incrementPointer("*IrohError")
defer _self.ffiObject.decrementPointer()
return FfiConverterBoolINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) C.int8_t {
return C.uniffi_iroh_ffi_fn_method_iroherror_is_kind(
_pointer, FfiConverterIrohErrorKindINSTANCE.Lower(kind), _uniffiStatus)
}))
}
// Stable high-level error category.
func (_self *IrohError) Kind() IrohErrorKind {
_pointer := _self.ffiObject.incrementPointer("*IrohError")
defer _self.ffiObject.decrementPointer()
return FfiConverterIrohErrorKindINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
return GoRustBuffer{
inner: C.uniffi_iroh_ffi_fn_method_iroherror_kind(
_pointer, _uniffiStatus),
}
}))
}
// Human-readable error message.
func (_self *IrohError) Message() string {
_pointer := _self.ffiObject.incrementPointer("*IrohError")
defer _self.ffiObject.decrementPointer()
@@ -8601,6 +8745,73 @@ func (_ FfiDestroyerIncomingLocalAddr) Destroy(value IncomingLocalAddr) {
value.Destroy()
}
// Stable high-level error categories exposed across the FFI boundary.
//
// These are intentionally coarser than the upstream Rust error types. They
// give foreign bindings a stable taxonomy for `errors.Is`-style handling
// without leaking the internal `iroh` / `n0-error` error hierarchy.
type IrohErrorKind uint
const (
// Invalid input supplied by the caller.
IrohErrorKindInvalidInput IrohErrorKind = 1
// Failure while binding an endpoint.
IrohErrorKindBind IrohErrorKind = 2
// Failure while initiating or completing an outgoing connection.
IrohErrorKindConnect IrohErrorKind = 3
// An established connection failed or closed unexpectedly.
IrohErrorKindConnection IrohErrorKind = 4
// ALPN negotiation or lookup failed.
IrohErrorKindAlpn IrohErrorKind = 5
// Endpoint id / public key parsing failed.
IrohErrorKindKeyParsing IrohErrorKind = 6
// Ticket parsing failed.
IrohErrorKindTicketParsing IrohErrorKind = 7
// Relay configuration or relay operation failed.
IrohErrorKindRelay IrohErrorKind = 8
// Stream read/write/control operation failed.
IrohErrorKindStream IrohErrorKind = 9
// Datagram send/receive operation failed.
IrohErrorKindDatagram IrohErrorKind = 10
// Foreign callback failed.
IrohErrorKindCallback IrohErrorKind = 11
// Operation was attempted on a closed stream/connection/resource.
IrohErrorKindClosed IrohErrorKind = 12
// Operation timed out.
IrohErrorKindTimeout IrohErrorKind = 13
// Unclassified internal error.
IrohErrorKindInternal IrohErrorKind = 14
)
type FfiConverterIrohErrorKind struct{}
var FfiConverterIrohErrorKindINSTANCE = FfiConverterIrohErrorKind{}
func (c FfiConverterIrohErrorKind) Lift(rb RustBufferI) IrohErrorKind {
return LiftFromRustBuffer[IrohErrorKind](c, rb)
}
func (c FfiConverterIrohErrorKind) Lower(value IrohErrorKind) C.RustBuffer {
return LowerIntoRustBuffer[IrohErrorKind](c, value)
}
func (c FfiConverterIrohErrorKind) LowerExternal(value IrohErrorKind) ExternalCRustBuffer {
return RustBufferFromC(LowerIntoRustBuffer[IrohErrorKind](c, value))
}
func (FfiConverterIrohErrorKind) Read(reader io.Reader) IrohErrorKind {
id := readInt32(reader)
return IrohErrorKind(id)
}
func (FfiConverterIrohErrorKind) Write(writer io.Writer, value IrohErrorKind) {
writeInt32(writer, int32(value))
}
type FfiDestroyerIrohErrorKind struct{}
func (_ FfiDestroyerIrohErrorKind) Destroy(value IrohErrorKind) {
}
// The logging level. See the rust (log crate)[https://docs.rs/log] for more information.
type LogLevel uint
+56
View File
@@ -932,6 +932,12 @@ uint64_t uniffi_iroh_ffi_fn_clone_endpointbuilder(uint64_t handle, RustCallStatu
#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_FFI_FN_FREE_ENDPOINTBUILDER
#define UNIFFI_FFIDEF_UNIFFI_IROH_FFI_FN_FREE_ENDPOINTBUILDER
void uniffi_iroh_ffi_fn_free_endpointbuilder(uint64_t handle, RustCallStatus *out_status
);
#endif
#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_FFI_FN_CONSTRUCTOR_ENDPOINTBUILDER_NEW
#define UNIFFI_FFIDEF_UNIFFI_IROH_FFI_FN_CONSTRUCTOR_ENDPOINTBUILDER_NEW
uint64_t uniffi_iroh_ffi_fn_constructor_endpointbuilder_new(RustCallStatus *out_status
);
#endif
#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_FFI_FN_METHOD_ENDPOINTBUILDER_ALPNS
@@ -954,6 +960,11 @@ void uniffi_iroh_ffi_fn_method_endpointbuilder_apply_n0(uint64_t ptr, RustCallSt
void uniffi_iroh_ffi_fn_method_endpointbuilder_apply_n0_disable_relay(uint64_t ptr, RustCallStatus *out_status
);
#endif
#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_FFI_FN_METHOD_ENDPOINTBUILDER_BIND
#define UNIFFI_FFIDEF_UNIFFI_IROH_FFI_FN_METHOD_ENDPOINTBUILDER_BIND
uint64_t uniffi_iroh_ffi_fn_method_endpointbuilder_bind(uint64_t ptr
);
#endif
#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_FFI_FN_METHOD_ENDPOINTBUILDER_BIND_ADDR
#define UNIFFI_FFIDEF_UNIFFI_IROH_FFI_FN_METHOD_ENDPOINTBUILDER_BIND_ADDR
void uniffi_iroh_ffi_fn_method_endpointbuilder_bind_addr(uint64_t ptr, RustBuffer addr, RustCallStatus *out_status
@@ -1139,6 +1150,21 @@ uint64_t uniffi_iroh_ffi_fn_clone_iroherror(uint64_t handle, RustCallStatus *out
void uniffi_iroh_ffi_fn_free_iroherror(uint64_t handle, RustCallStatus *out_status
);
#endif
#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_FFI_FN_METHOD_IROHERROR_DEBUG_MESSAGE
#define UNIFFI_FFIDEF_UNIFFI_IROH_FFI_FN_METHOD_IROHERROR_DEBUG_MESSAGE
RustBuffer uniffi_iroh_ffi_fn_method_iroherror_debug_message(uint64_t ptr, RustCallStatus *out_status
);
#endif
#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_FFI_FN_METHOD_IROHERROR_IS_KIND
#define UNIFFI_FFIDEF_UNIFFI_IROH_FFI_FN_METHOD_IROHERROR_IS_KIND
int8_t uniffi_iroh_ffi_fn_method_iroherror_is_kind(uint64_t ptr, RustBuffer kind, RustCallStatus *out_status
);
#endif
#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_FFI_FN_METHOD_IROHERROR_KIND
#define UNIFFI_FFIDEF_UNIFFI_IROH_FFI_FN_METHOD_IROHERROR_KIND
RustBuffer uniffi_iroh_ffi_fn_method_iroherror_kind(uint64_t ptr, RustCallStatus *out_status
);
#endif
#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_FFI_FN_METHOD_IROHERROR_MESSAGE
#define UNIFFI_FFIDEF_UNIFFI_IROH_FFI_FN_METHOD_IROHERROR_MESSAGE
RustBuffer uniffi_iroh_ffi_fn_method_iroherror_message(uint64_t ptr, RustCallStatus *out_status
@@ -2301,6 +2327,12 @@ uint16_t uniffi_iroh_ffi_checksum_method_endpointbuilder_apply_n0(void
#define UNIFFI_FFIDEF_UNIFFI_IROH_FFI_CHECKSUM_METHOD_ENDPOINTBUILDER_APPLY_N0_DISABLE_RELAY
uint16_t uniffi_iroh_ffi_checksum_method_endpointbuilder_apply_n0_disable_relay(void
);
#endif
#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_FFI_CHECKSUM_METHOD_ENDPOINTBUILDER_BIND
#define UNIFFI_FFIDEF_UNIFFI_IROH_FFI_CHECKSUM_METHOD_ENDPOINTBUILDER_BIND
uint16_t uniffi_iroh_ffi_checksum_method_endpointbuilder_bind(void
);
#endif
#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_FFI_CHECKSUM_METHOD_ENDPOINTBUILDER_BIND_ADDR
@@ -2433,6 +2465,24 @@ uint16_t uniffi_iroh_ffi_checksum_method_sendstream_write(void
#define UNIFFI_FFIDEF_UNIFFI_IROH_FFI_CHECKSUM_METHOD_SENDSTREAM_WRITE_ALL
uint16_t uniffi_iroh_ffi_checksum_method_sendstream_write_all(void
);
#endif
#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_FFI_CHECKSUM_METHOD_IROHERROR_DEBUG_MESSAGE
#define UNIFFI_FFIDEF_UNIFFI_IROH_FFI_CHECKSUM_METHOD_IROHERROR_DEBUG_MESSAGE
uint16_t uniffi_iroh_ffi_checksum_method_iroherror_debug_message(void
);
#endif
#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_FFI_CHECKSUM_METHOD_IROHERROR_IS_KIND
#define UNIFFI_FFIDEF_UNIFFI_IROH_FFI_CHECKSUM_METHOD_IROHERROR_IS_KIND
uint16_t uniffi_iroh_ffi_checksum_method_iroherror_is_kind(void
);
#endif
#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_FFI_CHECKSUM_METHOD_IROHERROR_KIND
#define UNIFFI_FFIDEF_UNIFFI_IROH_FFI_CHECKSUM_METHOD_IROHERROR_KIND
uint16_t uniffi_iroh_ffi_checksum_method_iroherror_kind(void
);
#endif
#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_FFI_CHECKSUM_METHOD_IROHERROR_MESSAGE
@@ -2625,6 +2675,12 @@ uint16_t uniffi_iroh_ffi_checksum_method_watchhandle_stop(void
#define UNIFFI_FFIDEF_UNIFFI_IROH_FFI_CHECKSUM_CONSTRUCTOR_ENDPOINT_BIND
uint16_t uniffi_iroh_ffi_checksum_constructor_endpoint_bind(void
);
#endif
#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_FFI_CHECKSUM_CONSTRUCTOR_ENDPOINTBUILDER_NEW
#define UNIFFI_FFIDEF_UNIFFI_IROH_FFI_CHECKSUM_CONSTRUCTOR_ENDPOINTBUILDER_NEW
uint16_t uniffi_iroh_ffi_checksum_constructor_endpointbuilder_new(void
);
#endif
#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_FFI_CHECKSUM_CONSTRUCTOR_ENDPOINTID_FROM_BYTES
+1
View File
@@ -1,4 +1,5 @@
package iroh_ffi
// #cgo linux,amd64 LDFLAGS: -L${SRCDIR}/libs/x86_64-unknown-linux-musl -liroh -lm
// #cgo linux,arm64 LDFLAGS: -L${SRCDIR}/libs/aarch64-unknown-linux-musl -liroh -lm
import "C"
Binary file not shown.
Binary file not shown.
+12
View File
@@ -0,0 +1,12 @@
Copyright (c) 2026 iroh-go contributors
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 iroh-go contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+10 -6
View File
@@ -9,8 +9,7 @@ RUST_FOLDER="$THIS_SCRIPT_DIR/../iroh-go"
FFI_FOLDER="$THIS_SCRIPT_DIR/../iroh-ffi"
GO_FOLDER="$THIS_SCRIPT_DIR/../"
# TARGETS="x86_64-unknown-linux-musl aarch64-unknown-linux-musl aarch64-apple-darwin x86_64-apple-darwin"
TARGETS="x86_64-unknown-linux-musl"
TARGETS="x86_64-unknown-linux-musl aarch64-unknown-linux-musl"
if ! command -v cross &> /dev/null; then
echo "▸ cross tool not found, installing"
@@ -21,7 +20,9 @@ if ! command -v rustup &> /dev/null; then
echo "▸ rustup not found, please install it"
exit 1
else
rustup target add $TARGETS
for TARGET in $TARGETS; do
rustup target add $TARGET
done
fi
export RUSTFLAGS="-C target-feature=+crt-static"
@@ -32,11 +33,11 @@ for TARGET in $TARGETS; do
cp "${RUST_FOLDER}/target/${TARGET}/release/${LIB_NAME}" "${GO_FOLDER}/libs/${TARGET}/${LIB_NAME}"
done
TARGET="x86_64-unknown-linux-musl"
echo "▸ Generate Go bindings"
BINDGEN_TARGET="x86_64-unknown-linux-musl"
echo "▸ generate Go bindings"
cd "$RUST_FOLDER"
uniffi-bindgen-go \
"${GO_FOLDER}/libs/${TARGET}/${LIB_NAME}" \
"${GO_FOLDER}/libs/${BINDGEN_TARGET}/${LIB_NAME}" \
--library \
--out-dir "${RUST_FOLDER}/target/go"
@@ -45,3 +46,6 @@ cp -r "${RUST_FOLDER}/target/go/iroh_ffi/iroh_ffi.go" "${GO_FOLDER}"
test -f "${RUST_FOLDER}/target/go/iroh_ffi/iroh_ffi.h"
cp -r "${RUST_FOLDER}/target/go/iroh_ffi/iroh_ffi.h" "${GO_FOLDER}"
go -C "${GO_FOLDER}" fmt ./...
go -C "${GO_FOLDER}" build -v iroh_ffi.go