axon

package module
v0.5.3 Latest Latest
Warning

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

Go to latest
Published: Mar 23, 2026 License: MIT Imports: 35 Imported by: 7

README

axon

Toolkit · Part of the lamina workspace

Go toolkit for building LLM-powered web services. Provides HTTP server lifecycle, auth, database management, metrics, SSE streaming, and token stream filtering. Each package can be used independently.

Getting started

go get github.com/benaskins/axon@latest

A minimal service with health checks and graceful shutdown:

package main

import (
	"context"
	"log/slog"
	"net/http"
	"time"

	"github.com/benaskins/axon"
)

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("GET /hello", func(w http.ResponseWriter, r *http.Request) {
		axon.WriteJSON(w, http.StatusOK, map[string]string{"msg": "hello"})
	})

	axon.ListenAndServe("8080", mux,
		axon.WithDrainTimeout(10*time.Second),
		axon.WithShutdownHook(func(ctx context.Context) {
			slog.Info("cleanup complete")
		}),
	)
}

Packages

axon — Core service toolkit

Server lifecycleListenAndServe with graceful shutdown (SIGINT/SIGTERM), shutdown hooks, configurable drain and hook timeouts.

axon.ListenAndServe("8080", mux,
    axon.WithShutdownHook(cleanup),
    axon.WithDrainTimeout(30*time.Second),
)

AuthSessionValidator interface with AuthClient implementation. Validates sessions against a remote auth service with caching.

client := axon.NewAuthClientPlain(authURL,
    axon.WithEndpointPath("/auth/check"),
    axon.WithCacheTTL(time.Minute),
)

mux.Handle("/api/", axon.RequireAuth(client)(apiHandler))

// In handlers:
userID := axon.UserID(r.Context())
session := axon.Session(r.Context())
role := session.Claim("role")

Database — PostgreSQL with schema isolation, goose migrations from embedded FS, and per-test schemas.

db := axon.MustOpenDB(dsn, "myapp")
axon.MustRunMigrations(db, migrationsFS)

// In tests:
db := axon.OpenTestDB(t, dsn, migrationsFS)

SPA — Serve embedded static files with client-side routing fallback.

mux.Handle("/", axon.SPAHandler(staticFS, "build",
    axon.WithStaticPrefix("/_app/"),
))

Health checksListenAndServe auto-wires /health and /metrics. Add named checks with WithHealthCheck:

axon.ListenAndServe("8080", mux,
    axon.WithHealthCheck("database", func() error { return db.Ping() }),
)

Config and helpers:

axon.MustLoadConfig(&cfg)               // env vars -> struct
axon.DecodeJSON[MyRequest](w, r)        // decode + validate request body
sse/ — Server-Sent Events
sse.SetSSEHeaders(w)
sse.SendEvent(w, flusher, payload)

bus := sse.NewEventBus[Event]()
ch := bus.Subscribe("client-1")
bus.Publish(Event{Type: "update"})
stream/ — LLM stream filtering

Buffered token filter with lookahead for processing LLM output in real time.

filter := stream.NewStreamFilter(
    stream.NewToolCallMatcher(),
    stream.NewContentSafetyMatcher(blockedPatterns),
)

for token := range tokens {
    emitted, held := filter.Process(token)
    // emitted: safe to send to client
    // held: buffered pending matcher decisions
}

License

MIT

Documentation

Overview

Package axon provides a Go toolkit for building AI-powered web services.

It includes HTTP server lifecycle, configuration, database management, health checks, Prometheus metrics, request logging, auth middleware, SPA static file serving, and slug validation.

Index

Constants

View Source
const (
	// UserIDKey is the context key for the authenticated user's ID.
	UserIDKey contextKey = "user_id"
	// UsernameKey is the context key for the authenticated user's username.
	UsernameKey contextKey = "username"
	// SessionInfoKey is the context key for the full SessionInfo.
	SessionInfoKey contextKey = "session_info"
)
View Source
const (
	// MetaKey is the context key for X-Axon-* header metadata.
	MetaKey contextKey = "axon_meta"
)

Variables

View Source
var (
	ErrUnauthorized       = errors.New("unauthorized")
	ErrNotFound           = errors.New("not found")
	ErrServiceUnavailable = errors.New("service unavailable")
)
View Source
var ValidSlug = regexp.MustCompile(`^[a-z0-9]+(-[a-z0-9]+)*$`)

ValidSlug matches lowercase alphanumeric slugs with hyphens between words. Examples: "my-agent", "chat", "deploy-gate-v2"

Functions

func DecodeJSON

func DecodeJSON[T any](w http.ResponseWriter, r *http.Request) (T, bool)

DecodeJSON decodes the request body as JSON into T. The body is limited to 1MB to prevent abuse. If T implements Validatable, validation runs after decoding. On failure, it writes an error response and returns (zero, false). On success, it returns (value, true).

func HealthHandler deprecated

func HealthHandler(db *sql.DB) http.HandlerFunc

Deprecated: HealthHandler is superseded by WithHealthCheck which registers named health checks with ListenAndServe.

func IsStatusError

func IsStatusError(err error, code int) bool

IsStatusError checks if err is a StatusError with the given code.

func ListenAndServe

func ListenAndServe(port string, handler http.Handler, opts ...ServerOption)

ListenAndServe starts an HTTP server and blocks until SIGINT or SIGTERM. Performs graceful shutdown: runs shutdown hooks, then drains connections.

func LoadClientTLSConfig

func LoadClientTLSConfig() (*tls.Config, error)

LoadClientTLSConfig loads mTLS client certificates from environment variables CLIENT_CERT, CLIENT_KEY, and CA_CERT.

func LoadConfig added in v0.4.0

func LoadConfig(cfg any) error

LoadConfig parses environment variables into the provided struct. The struct should use `env` and `envDefault` tags from caarlos0/env.

func Meta

func Meta(ctx context.Context, key string) string

Meta retrieves an X-Axon-* header value from the request context. The key should be lowercase with prefix stripped: "run-id", "trace-id". Returns empty string if not present.

func MetaHeaders

func MetaHeaders(next http.Handler) http.Handler

MetaHeaders returns middleware that extracts X-Axon-* headers into the request context. Downstream handlers access values via Meta or RunID.

Header names are normalised to lowercase keys with the prefix stripped: X-Axon-Run-Id → "run-id", X-Axon-Trace-Id → "trace-id".

func MeterProvider added in v0.5.0

func MeterProvider() *sdkmetric.MeterProvider

MeterProvider returns the global OTel MeterProvider, allowing domain packages to create their own meters and instruments.

func MetricsHandler

func MetricsHandler() http.Handler

MetricsHandler returns the Prometheus metrics HTTP handler. Serves OTel metrics in Prometheus exposition format.

func MustLoadConfig

func MustLoadConfig(cfg any)

MustLoadConfig parses environment variables into the provided struct. Panics on failure.

func MustOpenDB

func MustOpenDB(dsn, schema string) *sql.DB

MustOpenDB opens a PostgreSQL connection, creates the schema if needed, sets the search_path, and pings to verify connectivity. Panics on failure.

func MustRunMigrations

func MustRunMigrations(db *sql.DB, migrationsFS embed.FS)

MustRunMigrations runs goose SQL migrations. Panics on failure.

func NewServiceMux deprecated

func NewServiceMux(db *sql.DB) *http.ServeMux

Deprecated: NewServiceMux is superseded by ListenAndServe which auto-wires /health and /metrics. Use WithHealthCheck for database health checks.

func OpenDB

func OpenDB(dsn, schema string) (*sql.DB, error)

OpenDB opens a PostgreSQL connection, creates the schema if needed, and sets the search_path via the DSN so it applies to all pooled connections.

func OpenTestDB

func OpenTestDB(t *testing.T, dsn string, migrations embed.FS) *sql.DB

OpenTestDB creates a unique PostgreSQL schema for test isolation. It opens a connection, runs migrations, and registers cleanup to drop the schema when the test finishes.

func RequestLogging

func RequestLogging(next http.Handler) http.Handler

RequestLogging returns middleware that logs each request with slog.

func RequestMetrics

func RequestMetrics(next http.Handler) http.Handler

RequestMetrics returns middleware that records HTTP request metrics using OpenTelemetry instruments exported as Prometheus metrics.

func RequireAuth

func RequireAuth(sv SessionValidator, opts ...AuthOption) func(http.Handler) http.Handler

RequireAuth returns middleware that validates session tokens via a SessionValidator. On success, sets UserIDKey, UsernameKey, and SessionInfoKey in the request context. On failure, responds with 401 or 503.

func RunID

func RunID(ctx context.Context) string

RunID is a shortcut for Meta(ctx, "run-id").

func RunMigrations

func RunMigrations(db *sql.DB, migrationsFS embed.FS) error

RunMigrations runs goose SQL migrations from an embedded filesystem. The migrations FS should embed a "migrations" directory containing SQL files (e.g., //go:embed migrations/*.sql).

func SPAHandler

func SPAHandler(files embed.FS, subdir string, opts ...SPAOption) http.Handler

SPAHandler serves embedded static files with SPA fallback. Files are served from the given subdirectory of the embed.FS. Unknown routes get index.html (for client-side routing). Use WithStaticPrefix to make paths under a given prefix 404 on miss instead of falling back to index.html. index.html is served with Cache-Control: no-cache.

func StandardMiddleware deprecated

func StandardMiddleware(handler http.Handler) http.Handler

Deprecated: StandardMiddleware is applied automatically by ListenAndServe. Only use directly if serving HTTP without ListenAndServe.

func UserID

func UserID(ctx context.Context) string

UserID extracts the authenticated user ID from the request context. Returns empty string if not present.

func Username

func Username(ctx context.Context) string

Username extracts the authenticated username from the request context. Returns empty string if not present.

func ValidateSlug

func ValidateSlug(s string) error

ValidateSlug returns an error if s is not a valid slug.

func WrapHandler added in v0.5.0

func WrapHandler(handler http.Handler, checks ...HealthCheck) http.Handler

WrapHandler wraps a user handler with automatic observability:

  • GET /metrics — Prometheus metrics endpoint
  • GET /health — health check endpoint (extensible via WithHealthCheck)
  • All other routes — delegated through StandardMiddleware (logging + metrics)

Routes /metrics and /health are reserved and cannot be overridden by the inner handler.

func WriteError

func WriteError(w http.ResponseWriter, status int, message string)

WriteError writes a JSON error response: {"error": "message"}.

func WriteJSON

func WriteJSON(w http.ResponseWriter, status int, v any)

WriteJSON writes v as JSON with the given HTTP status code.

Types

type AuthClient

type AuthClient struct {
	// contains filtered or unexported fields
}

AuthClient validates session tokens against a remote auth service.

func NewAuthClient

func NewAuthClient(authServiceURL string, opts ...AuthClientOption) (*AuthClient, error)

NewAuthClient creates an AuthClient with mTLS from environment variables. Returns an error if client TLS configuration fails.

func NewAuthClientPlain

func NewAuthClientPlain(authServiceURL string, opts ...AuthClientOption) *AuthClient

NewAuthClientPlain creates an AuthClient without mTLS (plain HTTP). Use for services that talk to auth over localhost.

func (*AuthClient) Close

func (ac *AuthClient) Close()

Close stops the cache cleanup goroutine. Safe to call multiple times.

func (*AuthClient) ValidateSession

func (ac *AuthClient) ValidateSession(sessionToken string) (*SessionInfo, error)

type AuthClientOption

type AuthClientOption func(*authClientConfig)

AuthClientOption configures AuthClient behavior.

func WithCacheTTL

func WithCacheTTL(d time.Duration) AuthClientOption

WithCacheTTL sets the duration cached sessions remain valid. Defaults to 30 seconds.

func WithDecodeFunc

func WithDecodeFunc(fn func(*http.Response) (map[string]any, error)) AuthClientOption

WithDecodeFunc sets how the validation response is decoded into claims. Defaults to JSON decoding the response body.

func WithEndpointPath

func WithEndpointPath(path string) AuthClientOption

WithEndpointPath sets the validation endpoint path. Defaults to "/api/validate".

func WithTokenSender

func WithTokenSender(fn func(*http.Request, string)) AuthClientOption

WithTokenSender sets how the session token is attached to the validation request. Defaults to sending as a cookie named "session".

type AuthOption

type AuthOption func(*authConfig)

AuthOption configures RequireAuth behavior.

func WithCookieName

func WithCookieName(name string) AuthOption

WithCookieName sets the session cookie name. Defaults to "session".

func WithTokenExtractor

func WithTokenExtractor(fn func(*http.Request) (string, error)) AuthOption

WithTokenExtractor sets a custom function to extract the session token from the request. Overrides the default cookie-based extraction.

type HealthCheck added in v0.5.0

type HealthCheck struct {
	Name  string
	Check func() error
}

HealthCheck is a named health check function.

type InternalClient

type InternalClient struct {
	BaseURL    string
	HTTPClient *http.Client
}

InternalClient is a lightweight HTTP client for internal service-to-service calls. It handles JSON marshalling/unmarshalling and status code checks.

func NewInternalClient

func NewInternalClient(baseURL string) *InternalClient

NewInternalClient creates a client with sensible defaults (10s timeout).

func (*InternalClient) Get

func (c *InternalClient) Get(ctx context.Context, path string, result any) error

Get performs a GET request and decodes the JSON response into result. Returns an error if the status code is not 200.

func (*InternalClient) Post

func (c *InternalClient) Post(ctx context.Context, path string, body, result any) error

Post performs a POST request with a JSON body and decodes the response into result. Returns a *StatusError if the status code is not 2xx.

type SPAOption

type SPAOption func(*spaConfig)

SPAOption configures SPAHandler behavior.

func WithStaticPrefix

func WithStaticPrefix(prefix string) SPAOption

WithStaticPrefix sets a URL prefix for static assets that should 404 on miss instead of falling back to index.html. For example, WithStaticPrefix("/_app/") prevents serving HTML for missing JS/CSS. When not set, all unknown paths fall back to index.html.

type ServerOption

type ServerOption func(*serverConfig)

ServerOption configures ListenAndServe behavior.

func WithDrainTimeout

func WithDrainTimeout(d time.Duration) ServerOption

WithDrainTimeout sets the maximum time to wait for in-flight requests to complete during shutdown. Defaults to 30 seconds.

func WithHealthCheck added in v0.5.0

func WithHealthCheck(name string, check func() error) ServerOption

WithHealthCheck registers a named health check with ListenAndServe. When GET /health is called, all checks run and their results are included in the response. If any check fails, the endpoint returns 503.

func WithHookTimeout

func WithHookTimeout(d time.Duration) ServerOption

WithHookTimeout sets the maximum time to wait for shutdown hooks to complete. Defaults to 10 seconds.

func WithShutdownHook

func WithShutdownHook(fn func(context.Context)) ServerOption

WithShutdownHook registers a function to call before draining connections. Multiple hooks run in registration order. Use for background task cleanup.

func WithTLSCert

func WithTLSCert(certFile, keyFile string) ServerOption

WithTLSCert sets the TLS certificate and key files for ListenAndServeTLS. Must be used together with WithTLSConfig.

func WithTLSConfig

func WithTLSConfig(cfg *tls.Config) ServerOption

WithTLSConfig sets the TLS configuration for the server. Requires WithTLSCert to provide certificate and key files.

type SessionInfo

type SessionInfo struct {
	Claims map[string]any
}

SessionInfo holds claims from a validated session.

func Session

func Session(ctx context.Context) *SessionInfo

Session extracts the full SessionInfo from the request context. Returns nil if not present.

func (*SessionInfo) Claim

func (s *SessionInfo) Claim(key string) any

Claim returns a single claim value by key, or nil if absent.

func (*SessionInfo) UserID

func (s *SessionInfo) UserID() string

UserID returns the "user_id" claim as a string, or empty if absent.

func (*SessionInfo) Username

func (s *SessionInfo) Username() string

Username returns the "username" claim as a string, or empty if absent.

type SessionValidator

type SessionValidator interface {
	ValidateSession(token string) (*SessionInfo, error)
}

SessionValidator validates a session token and returns session info.

type StatusError

type StatusError struct {
	Code int
}

StatusError is returned when the server responds with an unexpected HTTP status code.

func (*StatusError) Error

func (e *StatusError) Error() string

type Validatable added in v0.5.2

type Validatable interface {
	Validate() error
}

Validatable is implemented by request types that can validate themselves. DecodeJSON calls Validate automatically after successful decoding.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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