⚠️ Pre-release. APIs may change before the first semver-tagged release. Early adopters welcome — pin to a specific commit or use@mainto track the latest, and check the open issues for known gaps. The internal protocol is stable; the Go API surface is still settling.
The Resonate Go SDK lets you build reliable, distributed Go applications on the durable execution programming model. Annotate ordinary Go functions, run them through Resonate, and the platform handles retries, recovery, and replay — when a process crashes mid-workflow, execution resumes from the last checkpoint, not from the beginning.
- Open an issue or pull request — contribution starts here while the SDK is prerelease
- Evaluate Resonate for your next project
- Example application library
- Distributed Async Await — the concepts that power Resonate
- Join the Discord
- Subscribe to the Journal
- Follow on X
- Follow on LinkedIn
- Subscribe on YouTube
- Install the Resonate Server & CLI
brew install resonatehq/tap/resonate- Install the Resonate Go SDK
go get github.com/resonatehq/resonate-sdk-go@latestNo semver tag is published yet — @latest resolves to a Go module pseudo-version pinned to the latest main commit. Pin to a specific commit if you need stability before v0.1.0 is cut.
- Write your first Resonate function
A durable greeting. The function below runs as a durable workflow: register it, invoke it with a unique ID, and Resonate checkpoints every step.
package main
import (
"context"
"fmt"
"log"
"time"
resonate "github.com/resonatehq/resonate-sdk-go"
)
type GreetArgs struct {
Name string `json:"name"`
}
func greet(_ *resonate.Context, args GreetArgs) (string, error) {
return fmt.Sprintf("hello, %s!", args.Name), nil
}
func main() {
r, err := resonate.New(resonate.Config{URL: "http://localhost:8001"})
if err != nil {
log.Fatalf("resonate.New: %v", err)
}
defer func() { _ = r.Stop() }()
greetFn, err := resonate.Register(r, "greet", greet)
if err != nil {
log.Fatalf("Register: %v", err)
}
ctx := context.Background()
id := fmt.Sprintf("greet-%d", time.Now().UnixNano())
h, err := greetFn.Run(ctx, id, GreetArgs{Name: "world"})
if err != nil {
log.Fatalf("Run: %v", err)
}
out, err := h.Result(ctx)
if err != nil {
log.Fatalf("Result: %v", err)
}
fmt.Println(out)
}The full version of this example lives in examples/hello.
- Start the server
resonate dev- Run the program
go run .Result
You'll see the greeting printed once the workflow settles:
hello, world!
What to try
- Inspect the durable promise the workflow created with
resonate promise get <id>(the ID is whatevergreet-...was printed in step 5). - Visualize the call graph with
resonate tree <id>. - Crash the worker mid-run (
Ctrl-Cbeforeh.Resultreturns) and restart it — the sameRuncall will resume rather than re-invokegreetfrom scratch.
You can also run the SDK entirely in-process with localnet -- no Resonate server installation required. This is ideal for local development, testing, and prototyping.
package main
import (
"context"
"fmt"
"log"
"time"
resonate "github.com/resonatehq/resonate-sdk-go"
"github.com/resonatehq/resonate-sdk-go/localnet"
)
type GreetArgs struct {
Name string `json:"name"`
}
func greet(_ *resonate.Context, args GreetArgs) (string, error) {
return fmt.Sprintf("hello, %s!", args.Name), nil
}
func main() {
pid := "worker-1"
r, err := resonate.New(resonate.Config{
Network: localnet.NewLocal("default", &pid),
Heartbeat: resonate.NoopHeartbeat{},
})
if err != nil {
log.Fatalf("resonate.New: %v", err)
}
defer func() { _ = r.Stop() }()
greetFn, err := resonate.Register(r, "greet", greet)
if err != nil {
log.Fatalf("Register: %v", err)
}
ctx := context.Background()
id := fmt.Sprintf("greet-%d", time.Now().UnixNano())
h, err := greetFn.Run(ctx, id, GreetArgs{Name: "world"})
if err != nil {
log.Fatalf("Run: %v", err)
}
out, err := h.Result(ctx)
if err != nil {
log.Fatalf("Result: %v", err)
}
fmt.Println(out)
}Key difference: localnet replaces the URL field with Network: localnet.NewLocal("default", &pid) and requires Heartbeat: resonate.NoopHeartbeat{}. Without NoopHeartbeat, the default AsyncHeartbeat spawns goroutines that attempt HTTP requests against a non-existent server endpoint. localnet has no HTTP layer -- NoopHeartbeat is the correct pairing.
Workflow functions execute from the top on every resume. Pure computation can
run directly in the workflow body, but observable side effects such as printing,
sending HTTP requests, or incrementing metrics will happen again on each replay
unless they are wrapped in ctx.Run. A settled ctx.Run call is recorded as a
durable child promise, so replay short-circuits the child and skips the body.
Beyond the workflow machinery, durable promises and cron schedules can be
managed directly. Values are codec-encoded (JSON, plus encryption when an
Encryptor is configured), and returned records come back decoded:
// Create a promise settled by some external party.
rec, err := r.Promises().Create(ctx, "order-1", 24*time.Hour, resonate.PromiseCreateOptions{
Param: Order{Item: "book"},
Tags: map[string]string{"kind": "order"},
})
// Settle it from anywhere holding the ID.
rec, err = r.Promises().Resolve(ctx, "order-1", Receipt{Total: 42})
// ...or r.Promises().Reject(ctx, "order-1", err) / r.Promises().Cancel(ctx, "order-1", nil)
// Read it back; Param/Value decode directly into Go values.
rec, err = r.Promises().Get(ctx, "order-1")
var receipt Receipt
err = rec.Value.Decode(&receipt)
// Cron schedules create a fresh promise on every firing.
s, err := r.Schedules().Create(ctx, "nightly", "0 0 * * *", "report-{{.timestamp}}", time.Hour,
resonate.ScheduleCreateOptions{PromiseParam: ReportArgs{Region: "us"}})
err = r.Schedules().Delete(ctx, "nightly")The package owns the workflow API (Context, Effects, Run, RPC, Sleep, Promise, Detached), the direct promise and schedule API (Resonate.Promises, Resonate.Schedules), the wire protocol (Sender, the Network interface, push-message decoding), and the shared domain types (PromiseRecord, TaskRecord, etc.). Concrete transports live in two leaf subpackages:
httpnet— HTTP + SSE transport for talking to a live Resonate server.localnet— in-process transport that runs the server state machine in a single actor goroutine. Useful for tests and for "no-server-required" local development.
See the package documentation on pkg.go.dev (published once the first semver tag is cut) or read doc.go directly.
Read the docs for the full programming model, deployment patterns, and the broader Resonate ecosystem. A Go-specific section will land on the docs site alongside the first tagged release; until then, the examples/ directory in this repo and the sibling TypeScript, Python, and Rust SDK READMEs are the closest reference.
Apache-2.0 — see LICENSE.