{"slug":"the-go-sdk-is-here","url":"https://tako.sh/blog/the-go-sdk-is-here/","canonical":"https://tako.sh/blog/the-go-sdk-is-here/","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.","author":null,"image":"7aa95afd375a","imageAlt":null,"headings":[{"depth":2,"slug":"one-interface-httphandler","text":"One Interface: http.Handler"},{"depth":2,"slug":"same-protocol-same-guarantees","text":"Same Protocol, Same Guarantees"},{"depth":2,"slug":"type-safe-secrets","text":"Type-Safe Secrets"},{"depth":2,"slug":"channels","text":"Channels"},{"depth":2,"slug":"getting-started","text":"Getting Started"}],"markdown":"Here's a complete Go app on Tako:\n\n```go\npackage main\n\nimport (\n  \"net/http\"\n  \"tako.sh\"\n)\n\nfunc main() {\n  mux := http.NewServeMux()\n  mux.HandleFunc(\"/\", func(w http.ResponseWriter, r *http.Request) {\n    w.Write([]byte(\"Hello from Tako\"))\n  })\n  tako.ListenAndServe(mux)\n}\n```\n\nThat'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.\n\n## One Interface: `http.Handler`\n\nThe 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.\n\nThat means the integration is a single line:\n\n```go\n// Gin\nr := gin.Default()\ntako.ListenAndServe(r)\n\n// Echo\ne := echo.New()\ntako.ListenAndServe(e)\n\n// Chi\nr := chi.NewRouter()\ntako.ListenAndServe(r)\n```\n\nNo 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.\n\nThe 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.\n\n## Same Protocol, Same Guarantees\n\nThe 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:\n\n| Concern           | What the SDK does                                                        |\n| ----------------- | ------------------------------------------------------------------------ |\n| Readiness         | Signals `TAKO:READY:<port>` to stdout when the server is actually bound  |\n| Health checks     | Intercepts `Host: tako.internal` with `/status` endpoint automatically   |\n| Secrets           | Reads from fd 3 at init — before your code runs                          |\n| Graceful shutdown | Catches SIGTERM/SIGINT, drains in-flight requests for 10 seconds         |\n| Metadata          | `tako.InstanceID()`, `tako.Version()`, `tako.Uptime()` for observability |\n\nThe 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.\n\n## Type-Safe Secrets\n\nGo'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:\n\n```go\n// Generated by tako typegen — DO NOT EDIT.\nvar Secrets = struct {\n  DatabaseUrl func() string\n  ApiKey      func() string\n}{\n  DatabaseUrl: func() string { return tako.GetSecret(\"DATABASE_URL\") },\n  ApiKey:      func() string { return tako.GetSecret(\"API_KEY\") },\n}\n```\n\nAutocompletion in your editor. Compile-time errors if you typo a secret name. No more `os.Getenv(\"DATABSE_URL\")` bugs that only surface in production.\n\nSecrets 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.\n\n## Channels\n\nThe Go SDK ships with the same channel primitive as the JavaScript SDK — [channels](/docs/how-tako-works) for WebSocket/SSE communication:\n\n```go\ntako.Channels.Register(\"chat\", tako.ChannelDefinition{\n  ParamsSchema: []byte(`{\n    \"type\": \"object\",\n    \"properties\": { \"roomId\": { \"type\": \"string\" } },\n    \"required\": [\"roomId\"]\n  }`),\n  Auth: &tako.ChannelAuthScheme{HeaderName: \"authorization\"},\n  Verify: func(input tako.VerifyInput) tako.ChannelAuthDecision {\n    userID := authenticate(input.Header)\n    if userID != \"\" {\n      return tako.AllowChannel(tako.ChannelGrant{Subject: userID})\n    }\n    return tako.RejectChannel()\n  },\n})\n```\n\nTyped 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`.\n\n## Getting Started\n\n```bash\nmkdir my-app && cd my-app\ntako init\n```\n\nTako 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.\n\nThe 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.\n\nGo is a language that values small interfaces and explicit control flow. We think `tako.ListenAndServe(handler)` fits right in."}