---
title: Getting started
description: Add go-iroh to a module, connect two endpoints over loopback, and confirm the toolchain.
icon: rocket
---

This page gets a Go program talking to itself over iroh. It is a
five-minute environment check; the real tutorial is
[Build an echo protocol](first-app).

## Requirements

go-iroh's `go.mod` declares `go 1.26`, so you need a Go toolchain that can
build it. Check yours:

```sh
go version
```

There is no cgo, no C toolchain, and no native library to install. A plain
`go build` is enough.

## 1. Create a project

```sh
mkdir go-iroh-demo
cd go-iroh-demo
go mod init go-iroh-demo
```

## 2. Paste the program

Create a file named `main.go` and paste in the following. It binds two
endpoints on loopback and connects one to the other:

```go
package main

import (
	"context"
	"fmt"
	"log"
	"net/netip"

	"github.com/tmc/go-iroh/iroh"
	"github.com/tmc/go-iroh/netaddr"
)

const alpn = "example/hello/1"

func main() {
	ctx := context.Background()
	loopback := netip.AddrPortFrom(netip.IPv6Loopback(), 0)

	server, err := iroh.Bind(ctx, iroh.WithALPNs(alpn), iroh.WithBindAddr(loopback))
	if err != nil {
		log.Fatal(err)
	}
	defer server.Shutdown(ctx)

	accepted := make(chan *iroh.Conn, 1)
	go func() {
		conn, err := server.Accept(ctx)
		if err != nil {
			log.Print(err)
			return
		}
		accepted <- conn
	}()

	client, err := iroh.Bind(ctx, iroh.WithBindAddr(loopback))
	if err != nil {
		log.Fatal(err)
	}
	defer client.Shutdown(ctx)

	addr := netaddr.NewEndpointAddr(server.ID()).WithIP(server.LocalAddr())
	conn, err := client.Connect(ctx, addr, alpn)
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	in := <-accepted
	defer in.Close()

	fmt.Printf("connected to %s over %s\n", conn.RemoteID().Z32()[:8], conn.ALPN())
}
```

## 3. Fetch the dependency

```sh
go mod tidy
```

This resolves the two imports and adds `github.com/tmc/go-iroh` to your
`go.mod`.

## 4. Run it

```sh
go run .
```

You should see one line naming the server's endpoint ID and the negotiated
ALPN, for example:

```text
connected to 37w49eed over example/hello/1
```

The first eight characters differ on every run — `iroh.Bind` generates a fresh
secret key when you do not supply one with `iroh.WithSecretKey`.

## What just happened

- `iroh.Bind` created an [endpoint](concepts#endpoint): a secret key, a UDP
  socket, and the QUIC transport used to dial and accept.
- The server advertised an [ALPN](concepts#alpn), the string that names your
  application protocol. Connections arriving with a different ALPN are
  rejected.
- The client dialed a `netaddr.EndpointAddr` — an endpoint ID plus at least one
  path to reach it. Here the path was a loopback IP address; over the internet
  it is usually a relay URL, a discovered direct address, or both.

## Next steps

- [Build an echo protocol](first-app) — the tutorial: a real handler, a
  `Router`, and two processes connected by a ticket.
- [Concepts and terminology](concepts) — endpoint, ALPN, relay, path,
  discovery.
- [Troubleshooting](troubleshooting) — when `Connect` hangs or returns an
  error.
