17 Commits

27 changed files with 618 additions and 69 deletions
+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/
+84 -10
View File
@@ -1,18 +1,92 @@
# iroh-go
# `iroh-go`
> **WARNING** Extremely Experimental ™️
> **WARNING** Highly Experimental ™️
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
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).
+107
View File
@@ -0,0 +1,107 @@
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 errors.New(fmt.Sprintf("%s: %s", err.Message(), err.DebugMessage()))
}
return err
}
// Is suports 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
}
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 initiating or completing an outgoing connection",
}
var ConnectError error = sentinel{
kind: uint(IrohErrorKindConnect),
message: "iroh: failure while initiating or completing an outgoing connectio",
}
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: stream read/write/control operation failed",
}
var StreamError error = sentinel{
kind: uint(IrohErrorKindStream),
message: "iroh: datagram send/receive 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",
}
+12 -9
View File
@@ -1,9 +1,10 @@
# 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.
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
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
@@ -48,11 +49,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.
+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=
+2 -2
View File
@@ -4,9 +4,9 @@ import (
"bufio"
"flag"
"fmt"
"strings"
"os"
"os/signal"
"strings"
"syscall"
iroh "git.coopcloud.tech/decentral1se/iroh-go"
@@ -70,7 +70,7 @@ func listen(e *iroh.Endpoint) {
}
}
func handleIncoming (incoming *iroh.Incoming) {
func handleIncoming(incoming *iroh.Incoming) {
accepting, err := incoming.Accept()
check(err)
+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
+5
View File
@@ -0,0 +1,5 @@
# errors
How to deal with error handling in a convenient way. See
[`#263`](https://github.com/n0-computer/iroh-ffi/issues/263) for the juicy
details.
+37
View File
@@ -0,0 +1,37 @@
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)
// BEGIN API PREVIEW ZONE
// 1. convert error to IrohError with As
// bring IrohError.Message/DebugMessage back
// maintain `err != nil` compatibility
if err := iroh.As(err); err != nil {
panic(err)
}
// 1. no As machinery required
// sentinel check with errors.Is support
// works like usual `err`
if err != nil && errors.Is(err, iroh.BindError) {
panic(err)
}
// END API PREVIEW ZONE
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
+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 => ../../
+3 -4
View File
@@ -4,9 +4,9 @@ import (
"bufio"
"flag"
"fmt"
"strings"
"os"
"os/signal"
"strings"
"syscall"
iroh "git.coopcloud.tech/decentral1se/iroh-go"
@@ -61,7 +61,7 @@ func listen(e *iroh.Endpoint) {
}
}
func handleIncoming (incoming *iroh.Incoming) {
func handleIncoming(incoming *iroh.Incoming) {
accepting, err := incoming.Accept()
check(err)
@@ -77,7 +77,7 @@ 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 != "" {
@@ -118,7 +118,6 @@ func handleReading(recv *iroh.RecvStream, done chan struct{}) {
close(done)
}
func main() {
parseFlags()
+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 => ../../
+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