chassis

package module
v11.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 2, 2026 License: MIT Imports: 13 Imported by: 0

README

chassis-go

A composable Go service toolkit for building production-grade microservices. Toolkit, not framework — chassis never owns main(), never hides wiring behind magic, and every package is independently importable.

go get github.com/ai8future/chassis-go/v11

Current version: 11.3.0 · Go: 1.26.x (module floor 1.26.0; tested with 1.26.4) · License: MIT


Requirements

chassis-go v11 declares go 1.26.0 in go.mod, so Go 1.26 is the compiler floor and Go 1.25 toolchains cannot build this module. Build, test, and deploy with the latest Go 1.26 patch release; this repository is currently verified with Go 1.26.4.


Why chassis-go?

Every Go microservice needs the same foundational concerns: env-based config, structured logging, graceful shutdown, health checks, HTTP middleware, gRPC interceptors, resilient HTTP clients, observability, feature flags, and request guards. Without a shared toolkit, teams re-implement these inconsistently across services.

chassis-go provides one cohesive, OTel-native solution where you wire together only what you need.


Packages

Tier 1: Foundation
Package Import Purpose
chassis github.com/ai8future/chassis-go/v11 Version gate (RequireMajor(11)) and deterministic port assignment (Port(name, offset) via djb2)
config .../v11/config Generic env-to-struct config loader via struct tags. Panics on missing required vars
logz .../v11/logz Structured JSON/text logging wrapping log/slog with automatic OTel trace_id/span_id injection
clikit .../v11/clikit Stdlib-first CLI toolkit: flat commands, env+flag binding, JSON output, exit codes, color, and opt-in registry integration
lifecycle .../v11/lifecycle Signal-aware graceful shutdown orchestration via errgroup
registry .../v11/registry File-based service registration at /tmp/chassis/. Status reporting, port declarations, custom commands, heartbeat
testkit .../v11/testkit Test helpers: NewLogger (writes to t.Log), SetEnv (with cleanup), GetFreePort
Tier 2: Transports and Clients
Package Import Purpose
httpkit .../v11/httpkit HTTP middleware: RequestID, Logging, Recovery, Tracing. JSON error responses
grpckit .../v11/grpckit gRPC interceptors: Logging, Recovery, Metrics, Tracing. Health service registration
health .../v11/health Parallel health check aggregation with HTTP handler and gRPC adapter
call .../v11/call Resilient outbound HTTP client: retry with exponential backoff, circuit breaker, OTel spans
Tier 3: Cross-Cutting
Package Import Purpose
guard .../v11/guard HTTP guards: rate limiter (LRU), CORS, IP filter, security headers, body limits, timeouts
flagz .../v11/flagz Feature flags with percentage rollouts (FNV-1a), pluggable sources, OTel span events
metrics .../v11/metrics OTel-native metrics recorder with cardinality protection (max 1000 label combos)
otel .../v11/otel OpenTelemetry bootstrap: OTLP gRPC traces + metrics, configurable samplers
errors .../v11/errors Unified error type with dual HTTP/gRPC codes and RFC 9457 Problem Details
secval .../v11/secval JSON security validation: blocks prototype pollution keys (__proto__, constructor, prototype) and deep nesting
work .../v11/work Structured concurrency: Map, All, Race, Stream — all OTel-traced
Tier 4: Utilities
Package Import Purpose
cache .../v11/cache Generic LRU+TTL in-memory cache with Prune()
seal .../v11/seal AES-256-GCM encrypt/decrypt, HMAC-SHA256 sign/verify, temporary tokens
tick .../v11/tick Periodic task components for lifecycle.Run (Every with Immediate/OnError options)
webhook .../v11/webhook HMAC-signed webhook send with retry, delivery tracking, VerifyPayloadID for signed delivery IDs, and legacy-compatible VerifyPayload wrapper
deploy .../v11/deploy Convention-based deploy directory discovery, environment detection, endpoints, dependencies, health
Tier 4: Integrations
Package Import Purpose
kafkakit .../v11/kafkakit Publish/subscribe to Redpanda event bus with Avro envelopes, tenant filtering, DLQ, AtLeastOnce delivery. Depends on schemakit. Uses github.com/twmb/franz-go
schemakit .../v11/schemakit Avro schema validation, registration, serialization. Confluent Schema Registry client
tracekit .../v11/tracekit Distributed trace ID propagation (tr_ + 32 lowercase hex, 128-bit entropy). HTTP middleware. Can be used alongside OTel/httpkit tracing
heartbeatkit .../v11/heartbeatkit Auto liveness heartbeats every 30s. Depends on kafkakit. Auto-activates with kafkakit
announcekit .../v11/announcekit Service/job lifecycle events. Depends on kafkakit. Auto-activates with kafkakit
registrykit .../v11/registrykit HTTP client to registry_svc for entity resolution. Depends on call
lakekit .../v11/lakekit Stdlib HTTP client to lake_svc for data lake access with tenant/trace headers and bounded response decoding
phasekit .../v11/phasekit Startup secret hydration from Phase via the phase CLI before config.MustLoad
inngestkit .../v11/inngestkit Thin setup glue for the Inngest Go SDK (config, HTTP mount, send). Functions/steps use inngestgo directly. Optional
Tier 4: External Service Clients

Most client packages below are built on call.Client (retry, circuit breaker, OTel spans). Exceptions are lakekit and ollamakit, which intentionally use stdlib http.Client for their current APIs/streaming behavior.

Package Import Purpose
inferkit .../v11/inferkit Provider-agnostic client for OpenAI-compatible LLM APIs: chat completions, SSE streaming, embeddings. Works with OpenAI, DeepInfra, Groq, Ollama (compat mode)
ollamakit .../v11/ollamakit Stdlib HTTP client for Ollama's native /api/ endpoints: chat, generate, embeddings, model management; streaming/pull operations use a no-timeout client while caller context controls cancellation
meilikit .../v11/meilikit Client for Meilisearch: index and document management, search
qdrantkit .../v11/qdrantkit Client for the Qdrant vector database: collections, points, filtered search
posthogkit .../v11/posthogkit Non-blocking, batched PostHog analytics client; buffered capture flushed periodically or by size. No-op when disabled

Tier isolation: Foundation packages avoid transport/runtime stacks such as gRPC and the OTel SDK unless you import packages that need them. clikit adds no CLI framework and reuses existing chassis/logz plumbing; its trace-aware logging path may include the already-present OTel trace API, but not the OTel SDK.


Quick Start

CLI / batch tool
func main() {
    chassis.SetAppVersion(mytool.AppVersion) // app-owned VERSION embed
    chassis.RequireMajor(11)                 // owns --version and freshness

    app := clikit.New(clikit.Config{Name: "mytool"}).Command(clikit.Command{
        Name: "greet",
        Run: func(ctx context.Context, c *clikit.Context) error {
            return c.Out.Emit(map[string]string{"message": "hello"})
        },
    })
    os.Exit(app.Run(os.Args))
}

clikit does not register --version and does not replace RequireMajor(11). Existing v11 services that do not import clikit keep their current startup, version, and freshness behavior.

HTTP service
package main

import (
    "context"
    "fmt"
    "net"
    "net/http"
    "time"

    chassis "github.com/ai8future/chassis-go/v11"
    "github.com/ai8future/chassis-go/v11/config"
    "github.com/ai8future/chassis-go/v11/guard"
    "github.com/ai8future/chassis-go/v11/health"
    "github.com/ai8future/chassis-go/v11/httpkit"
    "github.com/ai8future/chassis-go/v11/lifecycle"
    "github.com/ai8future/chassis-go/v11/logz"
)

type AppConfig struct {
    Port     int    `env:"PORT" default:"8080"`
    LogLevel string `env:"LOG_LEVEL" default:"info"`
}

func main() {
    // Version gate — must be first
    chassis.SetAppVersion(myapp.AppVersion) // enables --version flag and auto-rebuild
    chassis.RequireMajor(11)

    cfg := config.MustLoad[AppConfig]()
    logger := logz.New(cfg.LogLevel)
    logger.Info("starting service", "version", chassis.Version)

    // Routes
    mux := http.NewServeMux()
    mux.HandleFunc("GET /hello", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "hello world")
    })
    mux.Handle("GET /health", health.Handler(map[string]health.Check{
        "self": func(_ context.Context) error { return nil },
    }))

    // Middleware stack
    handler := httpkit.Recovery(logger)(
        httpkit.Tracing()(
            httpkit.RequestID(
                guard.Timeout(10*time.Second)(
                    httpkit.Logging(logger)(mux),
                ),
            ),
        ),
    )

    // Run with graceful shutdown
    lifecycle.Run(context.Background(),
        func(ctx context.Context) error {
            addr := fmt.Sprintf(":%d", cfg.Port)
            srv := &http.Server{Addr: addr, Handler: handler}
            ln, _ := net.Listen("tcp", addr)
            logger.Info("listening", "addr", ln.Addr().String())

            errCh := make(chan error, 1)
            go func() { errCh <- srv.Serve(ln) }()
            select {
            case <-ctx.Done():
                return srv.Shutdown(context.Background())
            case err := <-errCh:
                return err
            }
        },
    )
}

Package Details

config — Environment-Based Configuration

Load environment variables into typed structs using struct tags. Fail-fast: missing required config panics at startup. As with other chassis modules, call chassis.RequireMajor(11) once before config.MustLoad.

type AppConfig struct {
    Port        int           `env:"PORT" default:"8080"`
    DatabaseURL string        `env:"DATABASE_URL"`                // required (default)
    Debug       bool          `env:"DEBUG" required:"false"`      // optional
    Timeout     time.Duration `env:"TIMEOUT" default:"30s"`
    AllowedIPs  []string      `env:"ALLOWED_IPS" default:"127.0.0.1"`
}

chassis.RequireMajor(11)
cfg := config.MustLoad[AppConfig]()

Supported types: string, int, int64, float64, bool, time.Duration, []string (comma-separated)

phasekit - Phase Secret Hydration

Hydrate environment variables from Phase before config.MustLoad runs:

phasekit.MustHydrate(ctx, phasekit.Config{
    ServiceToken: os.Getenv("PHASE_SERVICE_TOKEN"),
    Host:         os.Getenv("PHASE_HOST"),
    App:          "myservice",
    Env:          "Production",
    RequiredKeys: []string{"DATABASE_URL"},
})

cfg := config.MustLoad[AppConfig]()

Existing environment variables win by default, missing phase binaries fall back to the existing process environment, dynamic secret leases are disabled in v1, and [REDACTED] values fail startup. See INTEGRATING_PHASE.md for Docker and CI guidance.

logz — Structured JSON Logging

Wraps log/slog with automatic OpenTelemetry trace correlation. When OTel is active, every log line includes trace_id and span_id at the top level of JSON output — even inside slog.Group scopes.

logger := logz.New("info")  // "debug", "info", "warn", "error"
logger.Info("request handled", "status", 200, "duration_ms", 42)

Output:

{"time":"...","level":"INFO","msg":"request handled","trace_id":"abc123","span_id":"def456","status":200,"duration_ms":42}
lifecycle — Graceful Shutdown

Signal-aware orchestrator using errgroup. Catches SIGTERM/SIGINT, cancels the shared context, and waits for all components to drain. Automatically initializes the registry on startup — every service is registered at /tmp/chassis/ with heartbeat and command polling.

lifecycle.Run(ctx,
    httpServerComponent,
    grpcServerComponent,
    workerComponent,
)

Each component receives a context that cancels on signal or when any peer returns an error.

registry — File-Based Service Registration

Every service automatically registers itself at /tmp/chassis/<service-name>/ when lifecycle.Run() is called. The registry writes a JSON PID file, maintains a structured log, and provides a command interface for external tooling.

What gets created:

/tmp/chassis/<service-name>/
  <pid>.json        # Registration: name, PID, hostname, version, available commands
  <pid>.log.jsonl   # Structured event log: startup, heartbeat, status, errors, shutdown
  <pid>.cmd.json    # Command file (written by external tools, consumed by the service)

Automatic behavior (managed by lifecycle.Run()):

  • Heartbeat event logged every 30 seconds
  • Command file polled every 3 seconds
  • Built-in stop command triggers graceful shutdown
  • Built-in restart command sets the restart flag and triggers shutdown
  • Stale PID files from dead processes are cleaned up on startup

Module-level API — no object to pass around:

import "github.com/ai8future/chassis-go/v11/registry"

// Report status (written to the service log)
registry.Status("processing batch 42")

// Report errors
registry.Errorf("failed to connect to %s: %v", host, err)

// Register custom commands (must be called before lifecycle.Run)
registry.Handle("flush-cache", "Clear all cached data", func() error {
    // clear application cache here
    return nil
})

The service name is resolved from CHASSIS_SERVICE_NAME env var, falling back to the working directory name. The service version comes from chassis.SetAppVersion when set, otherwise from a VERSION file in the working directory.

call — Resilient HTTP Client

Outbound HTTP with retry (exponential backoff + jitter), circuit breaker (Closed/Open/HalfOpen states), and OTel client spans.

client := call.New(
    call.WithTimeout(5*time.Second),
    call.WithRetry(3, 500*time.Millisecond),
    call.WithCircuitBreaker("payments-api", 5, 30*time.Second),
)

resp, err := client.Do(req)

Batch concurrent requests with client.Batch(ctx, requests) — powered by work.Map under the hood.

errors — Unified Error Type

Dual HTTP + gRPC error codes with RFC 9457 Problem Details. Fluent API for decorating errors.

err := errors.NotFoundError("user not found").
    WithDetail("user_id", "abc123").
    WithType("https://api.example.com/errors/user-not-found").
    WithCause(dbErr)

// Factory constructors:
errors.ValidationError(msg)    // 400 / INVALID_ARGUMENT
errors.UnauthorizedError(msg)  // 401 / UNAUTHENTICATED
errors.ForbiddenError(msg)     // 403 / PERMISSION_DENIED
errors.NotFoundError(msg)      // 404 / NOT_FOUND
errors.PayloadTooLargeError(msg) // 413 / INVALID_ARGUMENT
errors.RateLimitError(msg)     // 429 / RESOURCE_EXHAUSTED
errors.TimeoutError(msg)       // 504 / DEADLINE_EXCEEDED
errors.DependencyError(msg)    // 503 / UNAVAILABLE
errors.InternalError(msg)      // 500 / INTERNAL

Write RFC 9457 responses directly:

errors.WriteProblem(w, r, err, requestID)
httpkit — HTTP Middleware

Standard func(http.Handler) http.Handler middleware — compatible with any router.

// Recommended stack order (outermost first):
handler := httpkit.Recovery(logger)(        // catch panics → 500
    httpkit.Tracing()(                      // OTel server spans + duration metric
        httpkit.RequestID(                  // UUID v4 request ID
            httpkit.Logging(logger)(mux),   // structured request logging
        ),
    ),
)

// Access request ID from context
id := httpkit.RequestIDFrom(r.Context())

Response helpers:

httpkit.JSONError(w, r, http.StatusBadRequest, "invalid input")
httpkit.JSONProblem(w, r, serviceErr)
grpckit — gRPC Interceptors

Unary and stream interceptors for logging, panic recovery, metrics, and tracing. Wire them with grpc.ChainUnaryInterceptor.

srv := grpc.NewServer(
    grpc.ChainUnaryInterceptor(
        grpckit.UnaryRecovery(logger),
        grpckit.UnaryTracing(),
        grpckit.UnaryLogging(logger),
        grpckit.UnaryMetrics(),
    ),
    grpc.ChainStreamInterceptor(
        grpckit.StreamRecovery(logger),
        grpckit.StreamTracing(),
        grpckit.StreamLogging(logger),
        grpckit.StreamMetrics(),
    ),
)

// Register gRPC health service
grpckit.RegisterHealth(srv, health.CheckFunc(checks))
health — Health Checks

Composable health checks that run in parallel. Supports both HTTP and gRPC transports.

checks := map[string]health.Check{
    "database": func(ctx context.Context) error { return db.PingContext(ctx) },
    "cache":    func(ctx context.Context) error { return redis.Ping(ctx).Err() },
}

// HTTP handler: 200 {"status":"healthy",...} or 503 {"status":"unhealthy",...}
mux.Handle("GET /health", health.Handler(checks))

// gRPC adapter
grpckit.RegisterHealth(srv, health.CheckFunc(checks))
guard — Request Guards

HTTP middleware for rate limiting, CORS, IP filtering, security headers, body limits, and timeouts.

// Rate limiter with LRU eviction (O(1))
guard.RateLimit(guard.RateLimitConfig{
    Rate:    100,
    Window:  time.Minute,
    MaxKeys: 10000,
    KeyFunc: guard.XForwardedFor("10.0.0.0/8"),  // spoof-resistant
})

// CORS
guard.CORS(guard.CORSConfig{
    AllowOrigins: []string{"https://app.example.com"},
    AllowMethods: []string{"GET", "POST"},
    MaxAge:       time.Hour,
})

// Security headers (CSP, HSTS 2yr, X-Frame-Options: DENY, etc.)
guard.SecurityHeaders(guard.DefaultSecurityHeaders)

// IP allow/deny by CIDR (deny takes precedence)
guard.IPFilter(guard.IPFilterConfig{
    Allow: []string{"10.0.0.0/8"},
    Deny:  []string{"10.0.0.1/32"},
})

// Body size limit
guard.MaxBody(2 * 1024 * 1024)  // 2 MB

// Request timeout with buffered response writer
guard.Timeout(10 * time.Second)

Key functions for rate limiter identification:

guard.RemoteAddr()                          // r.RemoteAddr
guard.XForwardedFor("10.0.0.0/8")          // rightmost untrusted IP
guard.HeaderKey("X-API-Key")               // arbitrary header
flagz — Feature Flags

Feature flags with boolean checks, percentage rollouts, and multi-source configuration.

// Sources: env, map, JSON file, or composite
flags := flagz.New(flagz.Multi(
    flagz.FromEnv("FLAG"),       // FLAG_NEW_CHECKOUT=true
    flagz.FromJSON("flags.json"),
))

// Boolean check
if flags.Enabled("new-checkout") { ... }

// Percentage rollout (consistent per user via FNV-1a hash)
if flags.EnabledFor(ctx, "new-checkout", flagz.Context{
    UserID:  user.ID,
    Percent: 25,  // 25% of users
}) { ... }

// String variant
theme := flags.Variant("theme", "light")
metrics — OTel Metrics with Cardinality Protection

Pre-configured metrics recorder with automatic cardinality limits. Drops new label combinations after 1000 per metric to prevent backend explosions.

rec := metrics.New("ordersvc", logger)

// Pre-built request metrics
rec.RecordRequest(ctx, method, status, durationMs, contentLength)

// Custom domain counters and histograms
orders := rec.Counter("orders_placed")
orders.Add(ctx, 1, "region", "us-east", "tier", "premium")

latency := rec.Histogram("payment_duration_seconds", metrics.DurationBuckets)
latency.Observe(ctx, 0.042, "provider", "stripe")
otel — OpenTelemetry Bootstrap

One-call OTel SDK initialization: OTLP gRPC exporters for traces and metrics, W3C propagation, configurable samplers.

shutdown := otel.Init(otel.Config{
    ServiceName:    "ordersvc",
    ServiceVersion: chassis.Version,
    Endpoint:       "otel-collector:4317",   // default: localhost:4317
    Sampler:        otel.RatioSample(0.1),   // 10% sampling; default: AlwaysSample
    Insecure:       true,                    // plaintext for dev; default: TLS
})
defer shutdown(context.Background())
secval — JSON Security Validation

Validates JSON payloads against dangerous keys and excessive nesting. Zero cross-module dependencies.

if err := secval.ValidateJSON(body); err != nil {
    // errors.Is(err, secval.ErrDangerousKey)
    // errors.Is(err, secval.ErrNestingDepth)
    // errors.Is(err, secval.ErrInvalidJSON)
}

Blocks prototype pollution keys: __proto__, constructor, prototype. Common business-domain words are intentionally excluded to avoid false positives. Max nesting depth: 20.

work — Structured Concurrency

Parallel execution primitives with bounded worker pools and automatic OTel tracing.

// Map: transform items concurrently (preserves order)
results, err := work.Map(ctx, items, processItem, work.Workers(8))

// All: run tasks concurrently, fail on first error
err := work.All(ctx, []func(context.Context) error{task1, task2, task3})

// Race: first success wins, cancels the rest
result, err := work.Race(ctx, fetchFromPrimary, fetchFromReplica)

// Stream: process channel items concurrently
out := work.Stream(ctx, inChan, transform, work.Workers(4))
for r := range out {
    fmt.Println(r.Value, r.Err)
}
testkit — Test Utilities
func TestMyHandler(t *testing.T) {
    logger := testkit.NewLogger(t)        // writes to t.Log, hidden on pass
    testkit.SetEnv(t, map[string]string{  // auto-cleanup via t.Cleanup
        "PORT": "0",
        "DATABASE_URL": "postgres://...",
    })
    port, _ := testkit.GetFreePort()      // OS-assigned free TCP port
}
Integration & Client Kits

The integration kits are optional. Import only the ones a service needs — each keeps its heavier dependencies out of the core import graph.

Event bus (kafkakit + schemakit) — publish/subscribe to Redpanda over the Kafka protocol with a standard event envelope (event ID, ms timestamp, source, subject, trace ID, tenant ID, entity refs), tenant-based delivery filtering, dead-letter routing on handler error, wildcard subscriptions, and at-most-once (default) or AtLeastOnce delivery. schemakit loads .avsc Avro schemas and serializes/registers them in Confluent wire format against a Schema Registry.

Liveness & lifecycle (heartbeatkit + announcekit)heartbeatkit publishes liveness payloads to ai8.infra.heartbeat on a fixed interval; announcekit publishes service- and job-lifecycle events to ai8.infra.{service}.lifecycle.{state} and ai8.infra.{service}.job.{state}. Both depend on kafkakit and auto-activate when it is configured.

Platform clients (registrykit + lakekit) — typed HTTP clients for registry_svc (entity resolution, relationship/graph traversal, entity management) and lake_svc (SQL queries, entity history, dataset listing/stats). registrykit is call-backed; lakekit uses stdlib HTTP with bounded response reads. Both set X-Tenant-ID and X-Trace-ID on every request.

Inference & search clientsinferkit (OpenAI-compatible chat/stream/ embeddings), meilikit (Meilisearch), and qdrantkit (Qdrant vector DB) are thin call-backed clients for common backends. ollamakit uses stdlib HTTP for Ollama native APIs and long-running streams/pulls. posthogkit is a non-blocking batched analytics client that no-ops when disabled.

Durable workflows (inngestkit) — thin setup glue (config, HTTP mount, event send) for the Inngest Go SDK. Function and step definitions use inngestgo directly; see INNGEST.md. Optional — services without durable-workflow needs should skip it.


Version Gate

chassis-go enforces a mandatory version compatibility contract. Every service must declare which major version it expects and provide its app version:

func main() {
    chassis.SetAppVersion(myapp.AppVersion) // from appversion.go at repo root
    chassis.RequireMajor(11)                // must be called before any chassis module
    // ...
}

SetAppVersion enables two automatic features:

  • --version flag: myservice --version prints myservice 1.2.3 (chassis-go 11.x.y) and exits
  • Auto-rebuild: if the binary's compiled version is older than the VERSION file on disk, it recompiles and re-execs automatically. Opt out with CHASSIS_NO_REBUILD=1.

See INTEGRATING.md for the full appversion.go setup pattern.

If the installed library's major version doesn't match, the process exits immediately with a clear migration message. Every chassis module calls AssertVersionChecked() at its entry points — importing a chassis module without calling RequireMajor first causes an immediate crash.


Examples

The examples/ directory contains runnable services demonstrating progressive complexity:

Example What It Demonstrates
examples/01-cli Minimal CLI: config + logz
examples/02-service gRPC service: config + grpckit + health + lifecycle
examples/03-client Resilient HTTP client: call with retry + circuit breaker
examples/04-full-service Full wiring: all packages combined (HTTP + admin server)
cmd/demo-shutdown Graceful shutdown demonstration with two worker goroutines

Run any example:

go run ./examples/04-full-service

Test it:

curl http://localhost:9090/health
curl -X POST http://localhost:8080/v1/demo -d '{"input":"hello"}'
curl -X POST http://localhost:8080/v1/demo -d '{"__proto__":"evil"}'  # → 400

Design Principles

  1. Toolkit, not framework — Chassis never owns main(). You call it, not the other way around.
  2. Tier isolation — Importing config does not pull in gRPC or the OTel SDK. Dependencies scale with what you use; clikit adds no CLI framework and reuses existing chassis/logz primitives.
  3. Visible wiring — No magic startup, no global init. All assembly happens in your code.
  4. Fail fast — Missing config, invalid guard parameters, or wrong major version crash immediately at startup with clear messages.
  5. OTel native — Tracing, metrics, and log correlation are built in from the ground up, not bolted on.
  6. Standard interfaces — HTTP middleware uses func(http.Handler) http.Handler. gRPC uses standard interceptors. No custom types to learn.

Auto-Instrumented Observability

When OTel is initialized, the following telemetry is collected automatically:

Traces:

  • httpkit.Tracing() — HTTP server spans with W3C context propagation
  • grpckit.UnaryTracing() / StreamTracing() — gRPC server spans from metadata
  • call.Client.Do() — HTTP client spans with header injection
  • work.Map/All/Race/Stream — parent + per-item child spans

Metrics:

  • http.server.request.duration — HTTP server latency histogram
  • http.client.request.duration — HTTP client latency histogram
  • rpc.server.duration — gRPC server latency histogram

Log correlation:

  • Every logz log line includes trace_id and span_id from the active span context

Dependencies

The core packages keep a thin direct-dependency surface (OTel, golang.org/x/sync, golang.org/x/crypto, google.golang.org/grpc). Heavier dependencies are isolated to the integration kits that need them, so they are only pulled in when you import those packages:

go.opentelemetry.io/otel          v1.40.0   (core)
go.opentelemetry.io/otel/sdk      v1.40.0   (otel)
golang.org/x/sync                 v0.21.0   (core)
golang.org/x/crypto               v0.53.0   (seal)
google.golang.org/grpc            v1.79.3   (grpckit, otel)
github.com/twmb/franz-go          v1.20.7   (kafkakit)
github.com/hamba/avro/v2          v2.31.0   (schemakit)
github.com/inngest/inngestgo      v0.15.1   (inngestkit)

See go.mod for the full, pinned dependency list.


Database Access

chassis-go ships no database driver. For Postgres, pair it with chassis-go-addons/pgkit (a pgxpool wrapper) plus pressly/goose for ledger-based migrations.

For typed queries, sqlc is the recommended default: real SQL in .sql files, generated plain-Go code, no runtime ORM, and the generated Queries struct accepts the same *pgxpool.Pool from pgkit.Open. Caveat: sqlc generates one function per query, so it shines on stable query shapes (CRUD-heavy services). For workloads dominated by dynamic queries — admin screens with toggleable filters, reporting, search builders — Bun or hand-rolled pgx composes more cleanly.


License

MIT — see LICENSE.

Documentation

Overview

Package chassis provides the chassis-go toolkit version and version compatibility check.

Index

Constants

View Source
const (
	PortHTTP    = 0 // Primary HTTP/REST API
	PortGRPC    = 1 // gRPC transport
	PortMetrics = 2 // Admin, Prometheus metrics, health
)

Standard port role offsets for chassis transport roles.

Variables

View Source
var Version = strings.TrimSpace(rawVersion)

Version returns the current release of chassis-go, read from the VERSION file. This is the single source of truth for the version number.

Functions

func AssertVersionChecked

func AssertVersionChecked()

AssertVersionChecked crashes if RequireMajor has not been called yet. Other chassis modules call this at their entry points.

func Port

func Port(name string, offset ...int) int

Port returns a deterministic port number derived from a service name using the djb2 hash algorithm. The result is in the range 5000–48000, well below the OS ephemeral port range (49152+).

The optional offset parameter (default 0) allows multiple ports per service:

chassis.Port("my_svc")                    // base port (HTTP)
chassis.Port("my_svc", chassis.PortGRPC)  // base + 1 (gRPC)
chassis.Port("my_svc", chassis.PortMetrics) // base + 2 (metrics)

func RequireMajor

func RequireMajor(required int)

RequireMajor crashes the process if the chassis major version does not match the required version. Services must call this at the top of main() before using any other chassis module.

If os.Args contains "--version", RequireMajor prints the version and exits 0. This ensures every chassis binary automatically supports --version.

func ResetVersionCheck

func ResetVersionCheck()

ResetVersionCheck is for testing only — resets the version assertion state.

func SetAppVersion

func SetAppVersion(v string)

SetAppVersion sets the consumer application's own version string. When set, --version output includes both the app version and the chassis version. Call this before RequireMajor if you want it included.

Types

This section is empty.

Directories

Path Synopsis
Package announcekit provides standardized lifecycle events for services and jobs.
Package announcekit provides standardized lifecycle events for services and jobs.
Package cache provides an in-memory TTL/LRU cache with optional OTel metrics.
Package cache provides an in-memory TTL/LRU cache with optional OTel metrics.
Package call provides a resilient HTTP client with retry, circuit breaker, and timeout support using a composable builder pattern.
Package call provides a resilient HTTP client with retry, circuit breaker, and timeout support using a composable builder pattern.
Package clikit provides a small, stdlib-first toolkit for chassis command line programs and batch tools.
Package clikit provides a small, stdlib-first toolkit for chassis command line programs and batch tools.
cmd
demo-shutdown command
Command demo-shutdown verifies that lifecycle.Run handles SIGTERM correctly, cancels Contexts, and drains gracefully.
Command demo-shutdown verifies that lifecycle.Run handles SIGTERM correctly, cancels Contexts, and drains gracefully.
Package config provides a generic, reflection-based configuration loader that populates structs from environment variables using struct tags.
Package config provides a generic, reflection-based configuration loader that populates structs from environment variables using struct tags.
Package deploy provides convention-based deploy directory discovery.
Package deploy provides convention-based deploy directory discovery.
Package errors provides a unified error type with dual HTTP and gRPC status codes.
Package errors provides a unified error type with dual HTTP and gRPC status codes.
examples
01-cli command
Example 01-cli demonstrates a simple CLI tool using config + logz.
Example 01-cli demonstrates a simple CLI tool using config + logz.
02-service command
Example 02-service demonstrates a reference gRPC service using config + logz + lifecycle + grpckit + health.
Example 02-service demonstrates a reference gRPC service using config + logz + lifecycle + grpckit + health.
03-client command
Example 03-client demonstrates the call package with retries and circuit breaking.
Example 03-client demonstrates the call package with retries and circuit breaking.
04-full-service command
Example 04-full-service demonstrates chassis modules wired together: config, logz, lifecycle, errors, secval, metrics, health, httpkit, grpckit, otel.
Example 04-full-service demonstrates chassis modules wired together: config, logz, lifecycle, errors, secval, metrics, health, httpkit, grpckit, otel.
05-clikit command
Package flagz provides feature flags with percentage rollouts and pluggable sources.
Package flagz provides feature flags with percentage rollouts and pluggable sources.
Package grpckit provides gRPC server utilities including standard interceptors for logging, panic recovery, and health-check registration.
Package grpckit provides gRPC server utilities including standard interceptors for logging, panic recovery, and health-check registration.
Package health provides composable health checks with parallel execution and a standard HTTP handler that returns structured JSON results.
Package health provides composable health checks with parallel execution and a standard HTTP handler that returns structured JSON results.
Package heartbeatkit provides zero-config automatic liveness events.
Package heartbeatkit provides zero-config automatic liveness events.
Package httpkit provides standard HTTP middleware and response utilities.
Package httpkit provides standard HTTP middleware and response utilities.
Package inferkit provides a provider-agnostic HTTP client for OpenAI-compatible LLM inference APIs.
Package inferkit provides a provider-agnostic HTTP client for OpenAI-compatible LLM inference APIs.
Package inngestkit provides thin setup glue for wiring inngest's Go SDK into a chassis service.
Package inngestkit provides thin setup glue for wiring inngest's Go SDK into a chassis service.
internal
otelutil
Package otelutil provides shared OTel API helpers for chassis-go packages.
Package otelutil provides shared OTel API helpers for chassis-go packages.
Package kafkakit provides publish/subscribe to Redpanda via the Kafka protocol, with envelope wrapping, tenant filtering, dead letter queue routing, and stats.
Package kafkakit provides publish/subscribe to Redpanda via the Kafka protocol, with envelope wrapping, tenant filtering, dead letter queue routing, and stats.
Package lakekit provides an HTTP client for lake_svc, the data lake query service.
Package lakekit provides an HTTP client for lake_svc, the data lake query service.
Package lifecycle provides a minimal orchestration primitive for running concurrent components that share a single context for cancellation and graceful shutdown via OS signals.
Package lifecycle provides a minimal orchestration primitive for running concurrent components that share a single context for cancellation and graceful shutdown via OS signals.
Package logz provides structured JSON logging with trace ID propagation.
Package logz provides structured JSON logging with trace ID propagation.
Package meilikit provides an HTTP client for Meilisearch built on raw HTTP via chassis call.
Package meilikit provides an HTTP client for Meilisearch built on raw HTTP via chassis call.
Package metrics provides OpenTelemetry metrics with cardinality protection.
Package metrics provides OpenTelemetry metrics with cardinality protection.
Package ollamakit provides an HTTP client for Ollama's native /api/ endpoints.
Package ollamakit provides an HTTP client for Ollama's native /api/ endpoints.
Package otel bootstraps OpenTelemetry trace and metric pipelines for chassis-go services.
Package otel bootstraps OpenTelemetry trace and metric pipelines for chassis-go services.
Package phasekit hydrates environment variables from Phase before config.MustLoad runs.
Package phasekit hydrates environment variables from Phase before config.MustLoad runs.
phasetest
Package phasetest provides fake phase CLI binaries for testing phasekit integrations without network access.
Package phasetest provides fake phase CLI binaries for testing phasekit integrations without network access.
Package posthogkit provides a non-blocking, batched PostHog analytics client.
Package posthogkit provides a non-blocking, batched PostHog analytics client.
Package qdrantkit provides an HTTP client for Qdrant vector database.
Package qdrantkit provides an HTTP client for Qdrant vector database.
Package registry provides file-based service registration at /tmp/chassis/.
Package registry provides file-based service registration at /tmp/chassis/.
Package registrykit provides an HTTP client for registry_svc, the entity registry service.
Package registrykit provides an HTTP client for registry_svc, the entity registry service.
Package schemakit provides Avro schema loading, validation, serialization, deserialization, and registration with a Schema Registry (e.g.
Package schemakit provides Avro schema loading, validation, serialization, deserialization, and registration with a Schema Registry (e.g.
Package seal provides cryptographic primitives: AES-256-GCM encryption, HMAC-SHA256 signing, and temporary signed tokens.
Package seal provides cryptographic primitives: AES-256-GCM encryption, HMAC-SHA256 signing, and temporary signed tokens.
Package secval provides JSON security validation: dangerous key detection and nesting depth limits.
Package secval provides JSON security validation: dangerous key detection and nesting depth limits.
Package testkit provides lightweight test helpers for chassis-go services.
Package testkit provides lightweight test helpers for chassis-go services.
Package tick provides periodic task components for use with lifecycle.Run.
Package tick provides periodic task components for use with lifecycle.Run.
Package tracekit propagates trace IDs across events and HTTP calls using Go's context mechanism.
Package tracekit propagates trace IDs across events and HTTP calls using Go's context mechanism.
Package work provides structured concurrency primitives with bounded parallelism and OpenTelemetry tracing.
Package work provides structured concurrency primitives with bounded parallelism and OpenTelemetry tracing.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL