go-kit
A small, framework-agnostic Go module for infrastructure code shared by Porygon Labs services. It provides focused packages for configuration, outbound HTTP, structured logging, HTTP responses, opaque integer IDs, and OpenTelemetry tracing.
[!NOTE]
This project is not affiliated with github.com/go-kit/kit.
[!IMPORTANT]
The current release is pre-v1. Public interfaces may change while the module is hardened for production use. See the roadmap.
Purpose
go-kit exists to remove recurring service setup without becoming a web framework or a grab bag of generic helpers. A package belongs here only when the same stable behavior is needed by multiple services and can remain independent of their framework and domain.
Read Purpose and design for the admission criteria, design principles, trade-offs, and non-goals.
Packages
| Package |
Use it for |
Dependencies |
config |
Load an optional .env file and parse environment variables into a caller-defined struct |
caarlos0/env, godotenv |
hash |
Encode positive int64 IDs as canonical, URL-safe Sqids |
sqids-go |
httpclient |
Create validated Resty clients with optional safe retry policy |
go-resty/resty/v2 |
logger |
Configure and access a process-wide slog.Logger |
Standard library |
response |
Write the shared { "meta", "data" } JSON response envelope |
Standard library |
telemetry |
Initialize OTLP/gRPC tracing and create caller-named spans |
OpenTelemetry SDK |
Import only the packages your application uses.
Requirements
The module currently declares Go 1.26.5 in go.mod. The supported Go-version policy is part of the high-priority roadmap.
Install
go get github.com/porygon-labs/go-kit@latest
Usage
Configuration
type Config struct {
Port uint16 `env:"PORT" envDefault:"8080"`
DSN string `env:"DB_DSN,required,notEmpty"`
}
cfg, err := config.Load[Config]()
if err != nil {
return err
}
Load reads .env from the current working directory when present. Existing process environment variables take precedence.
Outbound HTTP
client, err := httpclient.New(httpclient.Config{
BaseURL: "https://orders.example.com/v1",
Timeout: 3 * time.Second,
})
if err != nil {
return err
}
var order Order
resp, err := client.R().
SetContext(ctx).
SetResult(&order).
Get("/orders/42")
if err != nil {
return err
}
if resp.IsError() {
return fmt.Errorf("orders API returned %s", resp.Status())
}
Each call should use SetContext. HTTP error statuses are Resty responses, not Go errors. Retries are disabled by default; enable bounded retries with httpclient.WithRetry, which defaults to GET, HEAD, and OPTIONS only. Resty's internal logging is discarded so applications can redact and log returned errors at their own seam. See the HTTP client guide for retries, authentication, TLS, OTel, and testing.
Logging
logger.Init(logger.New("info", "json"))
logger.Info("server started", "port", 8080)
logger.Error("request failed", "error", err)
Call Init during startup. Before initialization, the package falls back to slog.Default().
HTTP responses
func handler(w http.ResponseWriter, _ *http.Request) {
if err := response.OK(w, map[string]string{"hello": "world"}); err != nil {
logger.Error("write response", "error", err)
}
}
The response body is:
{"meta":{"is_success":true,"message":"OK"},"data":{"hello":"world"}}
The package accepts http.ResponseWriter, so it works directly with net/http and routers built on it. Other frameworks can pass their underlying response writer; go-kit does not depend on framework-specific types.
Opaque IDs
codec, err := hash.NewHash("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
if err != nil {
return err
}
encoded, err := codec.Encode(42)
if err != nil {
return err
}
ids, err := codec.Decode(encoded)
if err != nil {
return err
}
[!WARNING]
Sqids are obfuscation, not encryption or access control. Always authorize access to the decoded resource.
Tracing
shutdown, err := telemetry.Init(ctx, "orders-api", "otel-collector:4317")
if err != nil {
return err
}
defer shutdown(context.Background())
ctx, end := telemetry.StartSpan(ctx)
defer end()
StartSpan derives the span name from its caller. The current Init implementation uses insecure gRPC transport; TLS and exporter options are a high-priority change.
Documentation
License
MIT — see LICENSE.