---
title: "The Go SDK Is Here"
date: "2026-04-12T05:28"
description: "Tako's Go SDK wraps any http.Handler — Gin, Echo, Chi, or plain net/http — with health checks, secrets, and graceful shutdown in one line."
image: "7aa95afd375a"
canonical: "https://tako.sh/blog/the-go-sdk-is-here/"
---

Here's a complete Go app on Tako:

```go
package main

import (
  "net/http"
  "tako.sh"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello from Tako"))
  })
  tako.ListenAndServe(mux)
}
```

That's it. `tako init`, `tako deploy`, done. The [`tako.sh`](https://pkg.go.dev/tako.sh) Go module is now available — Go joins JavaScript/TypeScript as a first-class Tako runtime.

## One Interface: `http.Handler`

The JavaScript SDK is built around the [fetch handler pattern](/blog/the-fetch-handler-pattern) — one function, every runtime. For Go, the equivalent is `http.Handler`. It's the interface that every Go web framework already implements.

That means the integration is a single line:

```go
// Gin
r := gin.Default()
tako.ListenAndServe(r)

// Echo
e := echo.New()
tako.ListenAndServe(e)

// Chi
r := chi.NewRouter()
tako.ListenAndServe(r)
```

No adapters. No wrappers. No glue code. If your framework implements `http.Handler`, it works. Gin, Echo, Chi, gorilla/mux, plain `net/http` — pass it to `ListenAndServe` and Tako handles the rest.

The one exception is Fiber, which uses fasthttp instead of `net/http`. For that, there's `tako.Listener()` — it returns a `net.Listener` you hand directly to Fiber's server.

## Same Protocol, Same Guarantees

The Go SDK speaks the exact same [Tako protocol](/docs/how-tako-works) as the JavaScript SDK. Everything [we built the SDK for](/blog/why-tako-ships-an-sdk) works identically:

| Concern           | What the SDK does                                                        |
| ----------------- | ------------------------------------------------------------------------ |
| Readiness         | Signals `TAKO:READY:<port>` to stdout when the server is actually bound  |
| Health checks     | Intercepts `Host: tako.internal` with `/status` endpoint automatically   |
| Secrets           | Reads from fd 3 at init — before your code runs                          |
| Graceful shutdown | Catches SIGTERM/SIGINT, drains in-flight requests for 10 seconds         |
| Metadata          | `tako.InstanceID()`, `tako.Version()`, `tako.Uptime()` for observability |

The server doesn't care whether the process behind the socket is Go, Bun, or Node. It just waits for `TAKO:READY`, probes `/status`, and routes traffic. One protocol, focused runtimes.

## Type-Safe Secrets

Go's approach to [secrets](/blog/secrets-without-env-files) leans into the type system. Run `tako typegen` and it generates a `tako_secrets.go` file with a typed struct:

```go
// Generated by tako typegen — DO NOT EDIT.
var Secrets = struct {
  DatabaseUrl func() string
  ApiKey      func() string
}{
  DatabaseUrl: func() string { return tako.GetSecret("DATABASE_URL") },
  ApiKey:      func() string { return tako.GetSecret("API_KEY") },
}
```

Autocompletion in your editor. Compile-time errors if you typo a secret name. No more `os.Getenv("DATABSE_URL")` bugs that only surface in production.

Secrets come from one place: the fd 3 bootstrap envelope Tako hands to your process at startup. The same `tako secret set …` values work in `tako dev` and `tako deploy`, with no environment-variable ambiguity.

## Channels

The Go SDK ships with the same channel primitive as the JavaScript SDK — [channels](/docs/how-tako-works) for WebSocket/SSE communication:

```go
tako.Channels.Register("chat", tako.ChannelDefinition{
  ParamsSchema: []byte(`{
    "type": "object",
    "properties": { "roomId": { "type": "string" } },
    "required": ["roomId"]
  }`),
  Auth: &tako.ChannelAuthScheme{HeaderName: "authorization"},
  Verify: func(input tako.VerifyInput) tako.ChannelAuthDecision {
    userID := authenticate(input.Header)
    if userID != "" {
      return tako.AllowChannel(tako.ChannelGrant{Subject: userID})
    }
    return tako.RejectChannel()
  },
})
```

Typed params, per-channel auth callbacks, and transport selection are wired up through the same protocol the proxy already speaks. Clients connect to the exact channel name plus query params, for example `/channels/chat?roomId=lobby`.

## Getting Started

```bash
mkdir my-app && cd my-app
tako init
```

Tako detects Go from `go.mod`, installs the `tako.sh` module, and scaffolds your project. Write your handlers, `tako dev` for local development with [real HTTPS](/blog/local-dev-with-real-https), `tako deploy` when you're ready.

The full API is small on purpose — `ListenAndServe`, `Listener`, `GetSecret`, plus metadata and channel helpers. Check the [docs](/docs) for the complete reference, or browse the [examples on GitHub](https://github.com/lilienblum/tako/tree/master/examples/go) to see Gin, Echo, Chi, and net/http in action.

Go is a language that values small interfaces and explicit control flow. We think `tako.ListenAndServe(handler)` fits right in.
