API surface explorations #25
@@ -14,4 +14,5 @@ examples/http-dialer/http-dialer
|
||||
examples/pairchat/pairchat
|
||||
examples/simple-pipe/simple-pipe
|
||||
examples/timeserve/timeserve
|
||||
examples/timeserve-close/timeserve
|
||||
iroh-go/target/
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# Contributing
|
||||
|
||||
Please read the [README](./README.md) before diving in. Please [raise an issue
|
||||
before starting on large
|
||||
changes](https://git.coopcloud.tech/decentral1se/iroh-go/issues/new).
|
||||
|
||||
## Conventions
|
||||
|
||||
* API wrappers (such as `endpoint.go`) should return upstream errors even
|
||||
though it would be more convenient to use `iroh.As` internally. This is to
|
||||
maintain consistency in error handling across the entire API boundary
|
||||
(bindings or custom). The API consumer uses `iroh.As`, not the producer.
|
||||
@@ -115,6 +115,10 @@ in [`libs`](./libs).
|
||||
Apache 2.0 & MIT, following the upstream licensing choices in
|
||||
[`iroh-ffi`](https://github.com/n0-computer/iroh-ffi).
|
||||
|
||||
## Contributing
|
||||
|
||||
[`CONTRIBUTING.md`](./CONTRIBUTING.md).
|
||||
|
||||
## LLM Policy
|
||||
|
||||
We adopt the [Strict No LLM / No AI
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package iroh_ffi
|
||||
|
||||
type Alpn []byte
|
||||
|
||||
func Alpns(alpns ...Alpn) [][]byte {
|
||||
var a [][]byte
|
||||
|
||||
for _, alpn := range alpns {
|
||||
a = append(a, []byte(alpn))
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package iroh_ffi
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
func (e *Endpoint) Accept(ctx context.Context, a Accepter) error {
|
||||
g, gCtx := errgroup.WithContext(ctx)
|
||||
|
||||
g.Go(func() error {
|
||||
for {
|
||||
incomingConn := *e.AcceptNext()
|
||||
if incomingConn == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
acceptedConn, err := incomingConn.Accept()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conn, err := acceptedConn.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Log.Debug("incoming connection established")
|
||||
|
||||
if err := a.Accept(gCtx, conn); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
})
|
||||
|
||||
return g.Wait()
|
||||
}
|
||||
|
||||
func (e *Endpoint) Open(ctx context.Context, addr *EndpointAddr, alpn Alpn, o Opener) error {
|
||||
conn, err := e.Connect(addr, alpn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Log.Debug("remote connection established")
|
||||
|
||||
if err := o.Open(ctx, conn); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package endpoint
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
iroh "git.coopcloud.tech/decentral1se/iroh-go"
|
||||
)
|
||||
|
||||
type Option func(*iroh.EndpointBuilder) error
|
||||
|
||||
func New(options ...Option) (*iroh.Endpoint, error) {
|
||||
|
decentral1se marked this conversation as resolved
Outdated
|
||||
var err error
|
||||
|
||||
endpointBuilder := iroh.NewEndpointBuilder()
|
||||
|
||||
for _, opt := range options {
|
||||
if optErr := opt(endpointBuilder); optErr != nil {
|
||||
|
decentral1se marked this conversation as resolved
Outdated
cblgh
commented
beware beware, this beware beware, this `endpoint.Online` bit can, depending on relay connectivity (I THINK) hang indefinitely
cblgh
commented
some more thinking, maybe the online wait could be an option? some more thinking, maybe the online wait could be an option?
```golang
func WithUntilOnline(){}
```
decentral1se
commented
I think I'll have to leave this to the option API consumer as the options that get passed in are for constructing the endpoint, not running anything on top of it. I guess it gives more control since we don't really know what is coming back out of that function call 😆 I think I'll have to leave this to the option API consumer as the options that get passed in are for constructing the endpoint, not running anything on top of it. I guess it gives more control since we don't really know what is coming back out of that function call 😆
|
||||
err = errors.Join(err, optErr)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(d1): implement DNS customisation when it lands
|
||||
// https://docs.iroh.computer/connecting/dns-address-lookup#use-your-own-server
|
||||
// https://github.com/n0-computer/iroh-ffi/issues/289
|
||||
|
||||
endpoint, err := endpointBuilder.Bind()
|
||||
if bindErr := iroh.As(err); err != nil {
|
||||
err = errors.Join(err, bindErr)
|
||||
}
|
||||
|
||||
return endpoint, err
|
||||
}
|
||||
|
||||
func WithAlpns(alpn ...iroh.Alpn) Option {
|
||||
return func(endpointBuilder *iroh.EndpointBuilder) error {
|
||||
endpointBuilder.Alpns(iroh.Alpns(alpn...))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithApplyN0() Option {
|
||||
return func(endpointBuilder *iroh.EndpointBuilder) error {
|
||||
endpointBuilder.ApplyN0()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithApplyN0DisableRelay() Option {
|
||||
return func(endpointBuilder *iroh.EndpointBuilder) error {
|
||||
endpointBuilder.ApplyN0DisableRelay()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithApplyMinimal() Option {
|
||||
return func(endpointBuilder *iroh.EndpointBuilder) error {
|
||||
endpointBuilder.ApplyMinimal()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithRelayMode(rMode *iroh.RelayMode) Option {
|
||||
return func(endpointBuilder *iroh.EndpointBuilder) error {
|
||||
endpointBuilder.RelayMode(rMode)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithDebug() Option {
|
||||
return func(endpointBuilder *iroh.EndpointBuilder) error {
|
||||
lvl := new(slog.LevelVar)
|
||||
lvl.Set(slog.LevelDebug)
|
||||
iroh.Log = slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: lvl}))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -4,4 +4,6 @@ go 1.26.1
|
||||
|
||||
require git.coopcloud.tech/decentral1se/iroh-go v0.0.0-20260717110820-68ad06fe2a25
|
||||
|
||||
require golang.org/x/sync v0.24.0 // indirect
|
||||
|
||||
replace git.coopcloud.tech/decentral1se/iroh-go => ../../
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
|
||||
golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
@@ -33,6 +33,7 @@ require (
|
||||
github.com/wailsapp/mimetype v1.4.1 // indirect
|
||||
golang.org/x/crypto v0.33.0 // indirect
|
||||
golang.org/x/net v0.35.0 // indirect
|
||||
golang.org/x/sync v0.24.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
)
|
||||
|
||||
@@ -64,6 +64,8 @@ golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5
|
||||
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
|
||||
golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
||||
@@ -4,4 +4,6 @@ go 1.26.1
|
||||
|
||||
require git.coopcloud.tech/decentral1se/iroh-go v0.0.0-20260717110820-68ad06fe2a25
|
||||
|
||||
require golang.org/x/sync v0.24.0 // indirect
|
||||
|
||||
replace git.coopcloud.tech/decentral1se/iroh-go => ../../
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
|
||||
golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
|
||||
@@ -4,6 +4,9 @@ go 1.26.1
|
||||
|
||||
require git.coopcloud.tech/decentral1se/iroh-go v0.0.0-20260717110820-68ad06fe2a25
|
||||
|
||||
require gomod.cblgh.org/cerca v0.3.3 // indirect
|
||||
require (
|
||||
golang.org/x/sync v0.24.0 // indirect
|
||||
gomod.cblgh.org/cerca v0.3.3
|
||||
)
|
||||
|
||||
replace git.coopcloud.tech/decentral1se/iroh-go => ../../
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
|
||||
golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
gomod.cblgh.org/cerca v0.3.3 h1:NaDO+J/cHn5nIsmLhjBUfWb/4RkNg6Dn3frrbX7TvHc=
|
||||
gomod.cblgh.org/cerca v0.3.3/go.mod h1:ZLFyHtI0Dd9GohYRn2Y+HpY3vpHQHa21efOTeSXmWvo=
|
||||
|
||||
@@ -4,6 +4,9 @@ go 1.26.1
|
||||
|
||||
require git.coopcloud.tech/decentral1se/iroh-go v0.0.0-20260717110820-68ad06fe2a25
|
||||
|
||||
require gomod.cblgh.org/cerca v0.3.3 // indirect
|
||||
require (
|
||||
golang.org/x/sync v0.24.0 // indirect
|
||||
gomod.cblgh.org/cerca v0.3.3
|
||||
)
|
||||
|
||||
replace git.coopcloud.tech/decentral1se/iroh-go => ../../
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
|
||||
golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
gomod.cblgh.org/cerca v0.3.3 h1:NaDO+J/cHn5nIsmLhjBUfWb/4RkNg6Dn3frrbX7TvHc=
|
||||
gomod.cblgh.org/cerca v0.3.3/go.mod h1:ZLFyHtI0Dd9GohYRn2Y+HpY3vpHQHa21efOTeSXmWvo=
|
||||
|
||||
@@ -4,4 +4,6 @@ go 1.26.1
|
||||
|
||||
require git.coopcloud.tech/decentral1se/iroh-go v0.0.0-20260717110820-68ad06fe2a25
|
||||
|
||||
require golang.org/x/sync v0.24.0 // indirect
|
||||
|
||||
replace git.coopcloud.tech/decentral1se/iroh-go => ../../
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
|
||||
golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
|
||||
@@ -4,6 +4,9 @@ go 1.26.1
|
||||
|
||||
require git.coopcloud.tech/decentral1se/iroh-go v0.0.0-20260717110820-68ad06fe2a25
|
||||
|
||||
require gomod.cblgh.org/cerca v0.3.3 // indirect
|
||||
require (
|
||||
golang.org/x/sync v0.24.0 // indirect
|
||||
gomod.cblgh.org/cerca v0.3.3
|
||||
)
|
||||
|
||||
replace git.coopcloud.tech/decentral1se/iroh-go => ../../
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
|
||||
golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
gomod.cblgh.org/cerca v0.3.3 h1:NaDO+J/cHn5nIsmLhjBUfWb/4RkNg6Dn3frrbX7TvHc=
|
||||
gomod.cblgh.org/cerca v0.3.3/go.mod h1:ZLFyHtI0Dd9GohYRn2Y+HpY3vpHQHa21efOTeSXmWvo=
|
||||
|
||||
@@ -4,4 +4,6 @@ go 1.26.1
|
||||
|
||||
require git.coopcloud.tech/decentral1se/iroh-go v0.0.0-20260717110820-68ad06fe2a25
|
||||
|
||||
require golang.org/x/sync v0.24.0 // indirect
|
||||
|
||||
replace git.coopcloud.tech/decentral1se/iroh-go => ../../
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
|
||||
golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
@@ -4,4 +4,6 @@ go 1.26.1
|
||||
|
||||
require git.coopcloud.tech/decentral1se/iroh-go v0.0.0-20260717110820-68ad06fe2a25
|
||||
|
||||
require golang.org/x/sync v0.24.0 // indirect
|
||||
|
||||
replace git.coopcloud.tech/decentral1se/iroh-go => ../../
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
|
||||
golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# timeserve-close
|
||||
|
||||
> **WIP**
|
||||
@@ -0,0 +1,9 @@
|
||||
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
|
||||
|
||||
require golang.org/x/sync v0.24.0 // indirect
|
||||
|
||||
replace git.coopcloud.tech/decentral1se/iroh-go => ../../
|
||||
@@ -0,0 +1,2 @@
|
||||
golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
|
||||
golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
@@ -0,0 +1,2 @@
|
||||
default:
|
||||
@go build -v ./...
|
||||
@@ -0,0 +1,108 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
iroh "git.coopcloud.tech/decentral1se/iroh-go"
|
||||
"git.coopcloud.tech/decentral1se/iroh-go/endpoint"
|
||||
)
|
||||
|
||||
var (
|
||||
alpn = iroh.Alpn("iroh-go-timeserve/0")
|
||||
frameSize uint32 = 256
|
||||
endpointPeer *string
|
||||
numMessages = 5
|
||||
)
|
||||
|
||||
type Timeserve struct{}
|
||||
|
||||
func (t Timeserve) Open(ctx context.Context, conn *iroh.Connection) error {
|
||||
stream, err := conn.OpenBi()
|
||||
if err := iroh.As(err); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = stream.Ready()
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
recv := stream.Recv()
|
||||
for range numMessages {
|
||||
frame, err := recv.Read(frameSize)
|
||||
if err := iroh.As(err); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("recv: %s: %s\n", recv.Id(), string(frame))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t Timeserve) Accept(ctx context.Context, conn *iroh.Connection) error {
|
||||
stream, err := conn.AcceptBi()
|
||||
if err := iroh.As(err); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
send := stream.Send()
|
||||
for range numMessages {
|
||||
now := time.Now().Format(time.DateTime)
|
||||
fmt.Printf("send: %s: %s\n", send.Id(), now)
|
||||
|
||||
err := send.WriteAll([]byte(now))
|
||||
if err := iroh.As(err); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
<-time.After(1 * time.Second)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
endpointPeer = flag.String("endpoint", "", "endpoint id of peer")
|
||||
flag.Parse()
|
||||
|
||||
endpoint, err := endpoint.New(
|
||||
endpoint.WithAlpns(alpn),
|
||||
endpoint.WithApplyN0(),
|
||||
endpoint.WithDebug(),
|
||||
)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
endpoint.Online()
|
||||
|
||||
timeserve := Timeserve{}
|
||||
ctx := context.Background()
|
||||
|
||||
if *endpointPeer == "" {
|
||||
fmt.Println("endpoint online! use id to connect peer:")
|
||||
fmt.Printf("timeserve -endpoint %s\n", endpoint.Id())
|
||||
|
||||
err := endpoint.Accept(ctx, timeserve)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
remote, err := iroh.EndpointIdFromString(*endpointPeer)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
addr := iroh.NewEndpointAddr(remote, nil, nil)
|
||||
|
||||
err = endpoint.Open(ctx, addr, alpn, timeserve)
|
||||
if err := iroh.As(err); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@@ -4,4 +4,6 @@ go 1.26.1
|
||||
|
||||
require git.coopcloud.tech/decentral1se/iroh-go v0.0.0-20260717110820-68ad06fe2a25
|
||||
|
||||
require golang.org/x/sync v0.24.0 // indirect
|
||||
|
||||
replace git.coopcloud.tech/decentral1se/iroh-go => ../../
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
|
||||
golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
@@ -1,3 +1,5 @@
|
||||
module git.coopcloud.tech/decentral1se/iroh-go
|
||||
|
||||
go 1.26.1
|
||||
|
||||
require golang.org/x/sync v0.24.0
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
|
||||
golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
@@ -0,0 +1,16 @@
|
||||
package iroh_ffi
|
||||
|
||||
import "context"
|
||||
|
||||
type Accepter interface {
|
||||
Accept(context.Context, *Connection) error
|
||||
}
|
||||
|
||||
type Opener interface {
|
||||
Open(context.Context, *Connection) error
|
||||
}
|
||||
|
||||
type AcceptOpener interface {
|
||||
Accepter
|
||||
Opener
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package iroh_ffi
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"os"
|
||||
)
|
||||
|
||||
var Log = slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{}))
|
||||
Reference in New Issue
Block a user
since this is following the "options" pattern, i think it's better here if you do
endpointBuilder.ApplyMinimal. a later option can then set a more maximal preset. the reason is some situations i do not want to use N0's defaults. this is not just a preference for my own projects but important in other situations, like testing strictly local connectivity :)