gas

package module
v0.3.1 Latest Latest
Warning

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

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

README

Gas

Test Go Reference Go Version License

Gas is the core of a modular Gas ecosystem for building micro-SaaS applications. It provides shared infrastructure — dependency injection, routing, middleware, events, migrations, and service lifecycle management — so you can focus on business logic instead of rebuilding the same plumbing for every project.

Install

go get github.com/gasmod/gas

Key Concepts

Services are self-contained units of functionality (auth, billing, etc.) that implement a simple three-method interface:

type Service interface {
	Name() string // Unique identifier, e.g. "gas-auth"
	Init() error  // Register routes, middleware, subscriptions
	Close() error // Cleanup internal resources
}

Dependency injection. Services are registered with the DI container via constructors. The container resolves dependencies automatically, performs topological sorting, validates lifetime rules, and calls Init() on every Service after construction.

Three lifetimes:

  • Singleton — created once, shared everywhere. Init() is called during BuildAll().
  • Scoped — created once per Scope. Init() is called on first resolution within the scope.
  • Transient — created fresh on every resolution. Cannot implement Service (registration panics).

Infrastructure flows inward. Services never import each other. They receive shared infrastructure (router, event bus, providers) through constructor injection and communicate via events and provider interfaces.

Ownership tracking. Every route, middleware, and event subscription is tagged with its owning service, enabling surgical teardown of a single service at runtime.

Usage

App Lifecycle (HTTP)
package main

import "github.com/gasmod/gas"

func main() {
	app := gas.NewApp(
		gas.WithSingletonService[*auth.Service](auth.New),
		gas.WithSingletonService[*billing.Service](billing.New),
	)

	if err := app.Run(); err != nil {
		log.Fatal(err)
	}
}

The App creates a Router and EventBus internally and registers them in the DI container. Services receive them via constructor injection:

func New(router *gas.Router, bus *gas.EventBus) *Service {
	return &Service{router: router, bus: bus}
}

Run() initializes all services (via the DI container), runs pending migrations, executes any registered ready hooks, starts the HTTP server, and waits for a shutdown signal. On shutdown, services are closed in reverse init order.

Worker Lifecycle (non-HTTP)

For non-HTTP environments (AWS Lambda, background workers, CLI tools), use Worker instead of App. It provides the same DI container, service lifecycle, events, and migration support without routing or an HTTP server.

w := gas.NewWorker(
	gas.WithSingletonService[*database.Service](database.New()),
	gas.WithSingletonService[*myservice.Service](myservice.New),
)

// Start initializes services, runs migrations, and executes ready hooks.
if err := w.Start(); err != nil {
	log.Fatal(err)
}
defer w.Shutdown()

// Use the DI container directly — e.g. in a Lambda handler.
lambda.Start(func(ctx context.Context, event MyEvent) error {
	scope := w.ServiceContainer().NewScope()
	defer scope.Close()
	svc := gas.MustResolve[*myservice.Service](scope)
	return svc.Handle(ctx, event)
})

For long-running worker processes that should block until a shutdown signal:

w := gas.NewWorker(
	gas.WithSingletonService[*myservice.Service](myservice.New),
)
if err := w.Run(); err != nil { // Start + block on SIGINT/SIGTERM + Shutdown
	log.Fatal(err)
}

App embeds Worker — all DI registration options (WithSingletonService, WithService, WithReadyFunc, etc.) work with both NewApp and NewWorker. HTTP-specific options (WithErrorHandler, WithTrustedOrigin, etc.) only work with NewApp.

Registering Services

Register constructor-based services with a lifetime:

gas.WithService[*auth.Service](auth.New, gas.ServiceLifetimeSingleton)

Register pre-built instances (treated as singletons):

gas.WithServiceInstance[*MyService](myInstance)

Convenience shorthands that infer the lifetime from the function name:

gas.WithSingletonService[*auth.Service](auth.New)   // equivalent to WithService(ctor, ServiceLifetimeSingleton)
gas.WithScopedService[*RequestLog](NewRequestLog)    // equivalent to WithService(ctor, ServiceLifetimeScoped)
gas.WithTransientService[*Nonce](NewNonce)           // equivalent to WithService(ctor, ServiceLifetimeTransient)
Routing

Handle accepts both classic http.HandlerFunc handlers and DI-aware typed handlers:

func (s *Service) Init() error {
	// Classic http.HandlerFunc — still works, no wrapping.
	s.router.Handle(s.Name(), "GET", "/users", s.listUsers)

	// DI-aware handler — dependencies are auto-resolved from the request scope.
	s.router.Handle(s.Name(), "POST", "/users", s.createUser, gas.MiddlewareByName("require-auth"))
	return nil
}

Routes declare middleware using MiddlewareByName() (resolved from the router's registry) or MiddlewareFunc() (inline).

DI-Aware Handlers

Handlers can declare dependencies as typed function parameters. The router resolves each dependency from the per-request DI scope automatically — no manual RequestScope / MustResolve calls needed.

Handler contract: gas.Context first, dependencies in between, error return.

func (s *Service) createUser(ctx gas.Context, db gas.DatabaseProvider, mailer gas.EmailProvider) error {
	var req CreateUserRequest
	if err := ctx.BindJSON(&req); err != nil {
		return err
	}
	// db and mailer are resolved from the request-scoped DI container
	return ctx.JSON(http.StatusCreated, user)
}

At startup, InitServices() validates that every handler dependency is registered in the container. If a type is missing, initialization fails immediately — no runtime surprises.

Context

gas.Context is an interface that embeds context.Context and wraps http.ResponseWriter and *http.Request with convenience methods. Because it satisfies context.Context, you can pass it directly to database calls, gRPC clients, tracing libraries, and any other API that accepts a context.Context — no unwrapping needed.

Create one with NewContext:

ctx := gas.NewContext(parent, w, r, opts ...gas.ContextOption) // parent is a context.Context
Method Description
ResponseWriter() http.ResponseWriter Underlying response writer
Request() *http.Request Underlying request
JSON(status int, v any) error Write JSON response (application/json)
XML(status int, v any) error Write XML response (application/xml)
RSS(status int, v any) error Write RSS XML response (application/rss+xml)
HTML(status int, s string) error Write HTML response (text/html)
Text(status int, s string) error Write plain-text response (text/plain)
NoContent() error Write 204 No Content
Redirect(status int, url string) Send HTTP redirect
Param(key string) string URL path parameter (chi.URLParam)
Query(key string) string Query string parameter
Header(key string) string Request header value
SetHeader(key, value string) Set response header
BindJSON(dest any) error Decode JSON request body into dest and auto-validate
BindForm(dest any) error Decode form body into dest and auto-validate
Validator() *validator.Validate Access the validator instance
FormDecoder() *schema.Decoder Access the form decoder instance

BindForm uses the "form" struct tag for field mapping and has IgnoreUnknownKeys enabled. Both BindJSON and BindForm automatically validate the decoded struct using go-playground/validator.

Since gas.Context is an interface, you can mock it in tests without an HTTP server:

type mockContext struct {
	gas.Context // embed for default implementations
	// override only the methods your test needs
}
Error Handling

When a DI-aware handler returns a non-nil error, it is passed to the ErrorHandler. The default writes a 500 Internal Server Error with the default http.StatusText(http.StatusInternalServerError) body, and logs the error if a logger is registered in the service container.

Panic recovery: DI-aware handlers automatically recover from panics. When a handler panics, the stack trace is written to stderr, the error is logged via the request-scoped Logger (if available), and the panic is routed through the ErrorHandler as a gas: handler panic: <value> error. http.ErrAbortHandler is re-panicked to preserve net/http's connection-teardown behavior.

Override it with WithErrorHandler:

app := gas.NewApp(
	gas.WithErrorHandler(func(ctx gas.Context, err error) {
		ctx.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
	}),
)
Middleware

Register named middleware on the router:

router.Register("auth", "require-auth", func(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// validate token...
		next.ServeHTTP(w, r)
	})
})

Apply middleware globally:

router.UseMiddlewareFunc(func(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// logging, CORS, etc.
		next.ServeHTTP(w, r)
	})
})

Or by name (panics if not registered):

router.UseMiddlewareByName("require-auth")
Built-in Middleware

Gas ships with ready-to-use middleware for common concerns:

RequestLogger — logs every HTTP request/response with method, path, status, bytes, duration, and remote address. Responses with status >= 400 are logged at error level. Requires a scoped Logger in the DI container. If chi's RequestID middleware is mounted upstream, the request ID is automatically attached to the logger's base fields.

router.UseMiddlewareFunc(gas.RequestLogger[*mylogger.Logger]())

// Disable automatic request ID attachment:
router.UseMiddlewareFunc(gas.RequestLogger[*mylogger.Logger](
	gas.WithRequestLoggerAppendRequestID(false),
))

SecurityHeaders — sets common security response headers with secure defaults. Each header can be overridden or disabled (by passing an empty string). Headers with no default are only emitted when explicitly configured:

Header Default
X-Content-Type-Options nosniff
X-Frame-Options DENY
Referrer-Policy strict-origin-when-cross-origin
Permissions-Policy camera=(), microphone=(), geolocation=()
Content-Security-Policy (none — application-specific)
Strict-Transport-Security (none — enable once fully on HTTPS)
Cross-Origin-Opener-Policy (none)
Cross-Origin-Resource-Policy (none)
// Secure defaults — no options needed:
router.UseMiddlewareFunc(gas.SecurityHeaders())

// Override a specific header:
router.UseMiddlewareFunc(gas.SecurityHeaders(
	gas.WithSecurityHeadersFrameOptions("SAMEORIGIN"),
))

CacheControl — sets the Cache-Control response header based on path matching rules and configured directives. If no path filters are specified, the header applies to all requests. If no directives are specified, the middleware passes through without setting any header.

// Cache static assets for 1 year:
router.UseMiddlewareFunc(gas.CacheControl(
	gas.WithCacheControlPathPrefix("/static/"),
	gas.WithCacheControlPublic(),
	gas.WithCacheControlMaxAge(365 * 24 * time.Hour),
	gas.WithCacheControlImmutable(),
))

// Disable caching for API responses:
router.UseMiddlewareFunc(gas.CacheControl(
	gas.WithCacheControlPathPrefix("/api/"),
	gas.WithCacheControlNoStore(),
))
Grouping Routes

Use Group() for inline middleware scoping:

router.Group(func(sub *gas.Router) {
	sub.UseMiddlewareByName("require-auth")
	sub.Handle("admin", "GET", "/admin/dashboard", s.dashboard)
	sub.Handle("admin", "GET", "/admin/settings", s.settings)
})

Use Route() for pattern-scoped groups:

router.Route("/api", func(sub *gas.Router) {
	sub.Handle("api", "GET", "/users", s.listUsers)
	sub.Handle("api", "GET", "/items", s.listItems)
})
Events

Events use typed Event[T] definitions for compile-time safety:

// Define a typed event
var UserCreated = gas.Event[UserCreatedPayload]{Name: "user:created"}

type UserCreatedPayload struct {
	Email string
}

// Subscribe with ownership tracking
gas.SubscribeWithOwner(bus, s.Name(), UserCreated, func(data UserCreatedPayload) {
	// provision billing account for data.Email
})

// Emit (returns *sync.WaitGroup for concurrent handlers)
gas.Emit(bus, UserCreated, UserCreatedPayload{Email: "user@example.com"}).Wait()
Kill-Switch

Disable a service at runtime without restarting the server:

app.CloseService("auth") // routes return 503, middleware + subscriptions removed, Close() called
app.RestartService("auth") // re-initializes the service

Other services can react to closures:

gas.SubscribeWithOwner(bus, s.Name(), gas.SystemServiceClosed, func(data gas.SystemServiceClosedPayload) {
	// enter degraded mode if data.ServiceName was a dependency
})
Ready Hooks

Register functions that run after all services are initialized and migrations have completed, but before the HTTP server starts accepting traffic (App) or before Start returns (Worker). Use this for data seeding or any other startup work that requires a live DI container:

app := gas.NewApp(
	gas.WithSingletonService[*DB](NewDB),
	gas.WithReadyFunc(func(sc *gas.ServiceContainer) error {
		db := gas.MustResolve[*DB](sc)
		return seed.Run(db)
	}),
)

Multiple hooks are called in registration order. Any error aborts startup before the server starts.

CSRF Protection

Gas enables cross-origin request protection by default using Go's net/http.CrossOriginProtection. It rejects non-safe cross-origin browser requests (POST, PUT, PATCH, DELETE, etc.) that originate from untrusted origins. Safe methods (GET, HEAD, OPTIONS) are always allowed. Requests without Sec-Fetch-Site or Origin headers (e.g. non-browser clients, curl) are also allowed.

No configuration is required for same-origin apps. For apps that receive cross-origin requests from known front-ends, add trusted origins:

app := gas.NewApp(
	gas.WithTrustedOrigin("https://app.example.com"),
	gas.WithTrustedOrigin("https://admin.example.com"),
)

To bypass protection for specific paths (e.g. webhook receivers that validate their own signatures):

app := gas.NewApp(
	gas.WithCSRFInsecureBypassPattern("/webhooks/stripe"),
)

To customize the response returned for rejected requests (default: 403 Forbidden):

app := gas.NewApp(
	gas.WithCSRFDenyHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "cross-origin request denied", http.StatusForbidden)
	})),
)
Request Scopes

The App automatically installs middleware that creates a DI Scope per HTTP request. Scoped services get a fresh instance for each request — Init() is called on first resolution and Close() is called when the request completes.

DI-aware handlers resolve scoped services automatically — just declare the dependency as a parameter. For classic http.HandlerFunc handlers, use the request-scope convenience helpers:

func (s *Service) handleOrder(w http.ResponseWriter, r *http.Request) {
	txLog := gas.MustResolveFromRequestScope[*TransactionLog](r)
	txLog.Record("order created")
	// txLog.Close() is called automatically when the request ends
}

Or the two-value form to handle missing registrations without panicking:

txLog, err := gas.ResolveFromRequestScope[*TransactionLog](r)

Both helpers are thin wrappers around gas.RequestScope(r) + gas.Resolve/gas.MustResolve. For full scope access (e.g. resolving multiple services), use gas.RequestScope(r) directly:

scope := gas.RequestScope(r)
txLog := gas.MustResolve[*TransactionLog](scope)

Register scoped services with ServiceLifetimeScoped:

app := gas.NewApp(
	gas.WithService[*TransactionLog](NewTransactionLog, gas.ServiceLifetimeScoped),
)

For non-HTTP use cases (background jobs, tests), create scopes manually:

scope := container.NewScope()
defer scope.Close() // calls Close() on all scoped Service instances

svc, err := gas.Resolve[*MyScopedService](scope)

To inject a scope into a context.Context (useful in tests or background jobs that call code expecting a request scope):

scope := container.NewScope()
defer scope.Close()

ctx := gas.WithRequestScope(context.Background(), scope)
// code that calls gas.RequestScope(r) on a request built from ctx will find this scope
Provider Interfaces

Services depend on interfaces, not implementations. Gas defines common providers that any service can accept:

Interface Methods
DatabaseProvider DB, Driver, Ping, Query, Exec, BeginTx, WithTx
CacheProvider Get, Set, Delete, Exists
JobQueueProvider Enqueue, Dequeue, Ack, Nack
EmailProvider Send, SendFromTemplate
StorageProvider Upload, Download, Delete, PresignURL (all accept ...StorageOption)
ConfigProvider SetDefault, SetDefaults, Set, Bind, Get, Find, Values
TemplateProvider Get, List, Register, RegisterFS
UIProvider Render, RenderWithStatus, RenderFragment, RegisterFuncs
HealthProvider CheckHealth(ctx) map[string]error — Worker satisfies it
ReadyProvider CheckReady(ctx) map[string]error — Worker satisfies it
HealthReporter CheckHealth(ctx) error — opt-in per service
ReadyReporter CheckReady(ctx) error — opt-in per service
Logger Trace, Debug, Info, Warn, Error, With, SetBaseFields, Flush
MigrationManager Register, RegisterSlice, RegisterFS, RunPending, Down
Authenticator Authenticate
Authorizer Authorize
PrincipalRevoker Revoke, RevokeAll, RevokeAllByScheme
Authentication & Authorization

Gas defines three separate interfaces for auth concerns — each can be implemented independently:

  • Authenticator — extracts a Principal from an *http.Request (JWT, session, API key, etc.)
  • Authorizer — checks whether a Principal can perform an action on a resource
  • PrincipalRevoker — invalidates credentials (single, all for a subject, or all by scheme)

A Principal represents an authenticated identity:

type Principal interface {
	Subject() string        // stable user identifier
	Scheme() string         // auth method: "jwt", "session", "apikey", etc.
	CredentialID() string   // specific credential: session ID, JWT jti, API key ID
	Metadata() PrincipalMetadata
}

Store and retrieve a Principal in context:

ctx = gas.WithPrincipal(ctx, principal)
p := gas.PrincipalFromContext(ctx)    // returns nil if absent
p := gas.MustPrincipalFromContext(ctx) // panics if absent

Use MetadataValue for type-safe metadata access:

if role, ok := gas.MetadataValue[string](p.Metadata(), "role"); ok {
	// ...
}

BasePrincipalMetadata is a ready-to-use map[string]any implementation of PrincipalMetadata.

Logger context helpers
// Store a logger in a context (e.g. in middleware)
ctx = gas.WithLogger(ctx, logger)

// Retrieve it downstream (returns nil if absent)
l := gas.LoggerFromContext(ctx)

With() branches into a new Logger instance. For request-scoped middleware that shares one Logger instance across the whole request, use SetBaseFields() instead — it mutates the receiver in place so every subsequent log call carries the attached fields automatically:

func authMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        logger := gas.MustResolveFromRequestScope[gas.Logger](r)
        logger.SetBaseFields().Str("user_id", userID).Str("trace_id", traceID).Apply()
        // All subsequent log calls within this request — including in the handler — carry user_id and trace_id.
        next.ServeHTTP(w, r)
    })
}
Writing a Service
package myservice

import (
	"net/http"

	"github.com/gasmod/gas"
)

type Service struct {
	router *gas.Router
	bus    *gas.EventBus
}

// New is the constructor — dependencies are injected by the DI container.
func New(router *gas.Router, bus *gas.EventBus) *Service {
	return &Service{router: router, bus: bus}
}

func (s *Service) Name() string { return "myservice" }

func (s *Service) Init() error {
	// DI-aware handler — db is resolved per-request from the scoped container.
	s.router.Handle(s.Name(), "GET", "/hello", s.handleHello)

	// Classic http.HandlerFunc still works.
	s.router.Handle(s.Name(), "GET", "/health", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
	})

	gas.SubscribeWithOwner(s.bus, s.Name(), gas.SystemServiceClosed,
		func(payload gas.SystemServiceClosedPayload) {
			// react to another service being closed, e.g. enter degraded mode
		})

	return nil
}

func (s *Service) handleHello(ctx gas.Context, db gas.DatabaseProvider) error {
	return ctx.JSON(http.StatusOK, map[string]string{"status": "ok"})
}

func (s *Service) Close() error { return nil }

Register it in the App:

app := gas.NewApp(
	gas.WithSingletonService[*myservice.Service](myservice.New),
)

System Events

Event Payload Type Fired When
gas.SystemServiceClosed SystemServiceClosedPayload A service is killed via CloseService
gas.SystemServiceInitialized SystemServiceInitializedPayload A service finishes Init (including restart)
gas.SystemAllServicesInitialized SystemAllServicesInitializedPayload All services have been successfully initialized
gas.SystemShuttingDown SystemShuttingDownPayload Worker or App begins shutdown (always fires)
gas.SystemServerShuttingDown SystemServerShuttingDownPayload HTTP server begins graceful shutdown (App only)
gas.AppConfigUpdated AppConfigUpdatedPayload App config is updated after binding (App only)

Configuration

gas.DefaultConfig() returns a *Config with sensible defaults. Pass a custom config via WithServiceInstance:

cfg := gas.DefaultConfig()
cfg.Server.Port = 9090

app := gas.NewApp(
	gas.WithServiceInstance[*gas.Config](cfg),
)
Config fields

Config embeds env.WithGasEnv (from gas-env) for environment detection, and holds a Server ServerSettings sub-struct.

Field Default Description
Server.Host 0.0.0.0 Hostname or IP address to bind
Server.Port 8080 TCP port to listen on
Server.ReadTimeout 5s Maximum duration for reading the entire request
Server.WriteTimeout 10s Maximum duration before timing out response writes
Server.IdleTimeout 2m Maximum idle time between keep-alive requests
Server.ShutdownTimeout 30s How long to wait for in-flight requests during shutdown

Config.Validate() checks that Server.Host is a valid IP or resolvable hostname.

Worker Methods

Method Returns Description
w.Start() error InitServices → migrations → ready hooks (non-blocking)
w.Shutdown() error Emit shutdown event, close services in reverse order
w.Run() error Start + block on signal + Shutdown
w.EventBus() *EventBus
w.ServiceContainer() *ServiceContainer
w.MigrationManager() MigrationManager (or nil)
w.ConfigProvider() ConfigProvider (or nil)
w.ActiveServices() []string
w.CloseService(name) error Kill-switch for a single service
w.RestartService(name) error Re-initialize a previously closed service
w.CheckHealth(ctx) map[string]error Concurrently polls all active HealthReporter services; satisfies HealthProvider
w.CheckReady(ctx) map[string]error Concurrently polls all active ReadyReporter services; satisfies ReadyProvider

App Methods

App embeds Worker, so all Worker methods are available. Additionally:

Method Returns
app.Router() *Router
app.Config() *Config

Documentation

Overview

Package gas provides the core framework: DI container, App/Worker lifecycle, Router with ownership tracking, EventBus, ConfigProvider, and the provider interfaces (Logger, Authenticator, StorageProvider, ...) that other gasmod modules implement.

See the module README for usage examples and design rationale.

SPDX-License-Identifier: MIT

Index

Constants

This section is empty.

Variables

View Source
var AppConfigUpdated = Event[AppConfigUpdatedPayload]{Name: "gas:config-updated"}

AppConfigUpdated is emitted when the app config is updated.

View Source
var SystemAllServicesInitialized = Event[SystemAllServicesInitializedPayload]{Name: "gas:all-services-initialized"}

SystemAllServicesInitialized is emitted when all services have been successfully initialized.

View Source
var SystemServerShuttingDown = Event[SystemServerShuttingDownPayload]{Name: "gas:server-shutting-down"}

SystemServerShuttingDown is emitted when the HTTP server is shutting down. For code that should run on any shutdown (not just HTTP), subscribe to SystemShuttingDown instead.

View Source
var SystemServiceClosed = Event[SystemServiceClosedPayload]{Name: "gas:service-closed"}

SystemServiceClosed is emitted when a service is closed at runtime.

View Source
var SystemServiceInitialized = Event[SystemServiceInitializedPayload]{Name: "gas:service-initialized"}

SystemServiceInitialized is emitted when a service is (re-)initialized at runtime.

View Source
var SystemShuttingDown = Event[SystemShuttingDownPayload]{Name: "gas:shutting-down"}

SystemShuttingDown is emitted when a Worker or App begins its shutdown sequence. It fires for both HTTP (App) and non-HTTP (Worker) workloads.

Functions

func ApplyEnqueueOptions

func ApplyEnqueueOptions(opts []EnqueueOption) (delay time.Duration, groupID, dedupeID string, attrs map[string]string)

ApplyEnqueueOptions resolves variadic EnqueueOption values into a concrete enqueueOptions struct. Implementations call this inside their Enqueue method.

func ApplyStorageOptions

func ApplyStorageOptions(opts []StorageOption) (bucket, contentType string, metadata map[string]string)

ApplyStorageOptions resolves variadic StorageOption values into their individual fields. Implementations call this inside their methods.

func CacheControl

func CacheControl(opt ...CacheControlOption) func(next http.Handler) http.Handler

CacheControl is middleware that sets the Cache-Control response header based on path matching rules and configured directives. If no path filters are specified, the header is applied to all requests. If no directives are specified, the middleware passes through without setting any header.

func Emit

func Emit[T any](bus *EventBus, event Event[T], data T) *sync.WaitGroup

Emit dispatches a typed event to all subscribers concurrently.

func MetadataValue

func MetadataValue[T any](m PrincipalMetadata, key string) (T, bool)

MetadataValue is a type-safe helper that retrieves a typed value from PrincipalMetadata. It returns the value and true if the key exists and the type assertion succeeds, or the zero value and false otherwise.

func MustResolve

func MustResolve[T any](r Resolver) T

MustResolve is like Resolve but panics if the service cannot be resolved.

func MustResolveFromRequestScope

func MustResolveFromRequestScope[T any](r *http.Request) T

MustResolveFromRequestScope retrieves a service of type T from the request's Scope and panics if it cannot be resolved.

func RegisterCtor

func RegisterCtor[T any](c *ServiceContainer, ctor any, lifetime ServiceLifetime)

RegisterCtor registers a constructor for type T with an optional lifetime. Constructor signature: func(DepA, DepB, ...) T or func(DepA, DepB, ...) (T, error)

Panics if lifetime is Transient and T implements Service — transient services cannot have managed lifecycles. Use Singleton or Scoped instead.

func RegisterInstance

func RegisterInstance[T any](c *ServiceContainer, val T)

RegisterInstance registers a pre-built value. Treated as a singleton.

func RequestLogger

func RequestLogger[T Logger](opt ...RequestLoggerOption) func(next http.Handler) http.Handler

RequestLogger is middleware that logs HTTP requests and responses using a scoped Logger resolved from the request's DI scope. It logs method, path, status, bytes written, duration, and remote address. Responses with status >= 400 are logged at error level; all others at info level.

The type parameter T must be the concrete Logger implementation registered in the DI container. If the logger cannot be resolved, the middleware passes through silently.

When appendRequestID is enabled (the default), the middleware expects chi's RequestID middleware to be mounted upstream so that a request ID is available via middleware.GetReqID.

func Resolve

func Resolve[T any](r Resolver) (T, error)

Resolve retrieves or builds a service of type T from a Resolver (either *ServiceContainer or *Scope).

func ResolveFromRequestScope

func ResolveFromRequestScope[T any](r *http.Request) (T, error)

ResolveFromRequestScope retrieves or builds a service of type T from the per-request scope in the provided *http.Request.

func SecurityHeaders

func SecurityHeaders(opt ...SecurityHeadersOption) func(next http.Handler) http.Handler

SecurityHeaders is middleware that sets common security-related HTTP response headers. It applies secure defaults out of the box (nosniff, DENY framing, strict referrer policy, and restrictive permissions policy). Use the functional options to override individual header values, or pass an empty string to disable a specific header.

func Subscribe

func Subscribe[T any](bus *EventBus, event Event[T], handler func(T))

Subscribe registers a typed handler for an event without service ownership.

func SubscribeWithOwner

func SubscribeWithOwner[T any](bus *EventBus, service string, event Event[T], handler func(T))

SubscribeWithOwner registers a typed handler for an event with service ownership tracking.

func WithLogger

func WithLogger(ctx context.Context, logger Logger) context.Context

WithLogger returns a copy of ctx with the given Logger attached.

func WithPrincipal

func WithPrincipal(ctx context.Context, principal Principal) context.Context

WithPrincipal stores a Principal in the given context.

func WithRequestScope

func WithRequestScope(ctx context.Context, scope *Scope) context.Context

WithRequestScope adds a Scope instance to the context using a custom key for managing scoped service lifetimes. Useful for testing and managing scoped service lifetimes within request contexts.

Types

type App

type App struct {
	*Worker
	// contains filtered or unexported fields
}

App manages service lifecycle, the HTTP server, and graceful shutdown. It embeds a Worker for DI, events, migrations, and service management, and adds routing, CSRF protection, and an HTTP listener on top.

func NewApp

func NewApp(opts ...Option) *App

NewApp creates an App with the given options. Router and EventBus are created internally and registered in the container.

func (*App) Config

func (a *App) Config() *Config

Config retrieves the application's configuration.

func (*App) Handler

func (a *App) Handler() http.Handler

Handler returns the fully wrapped http.Handler used by the App's server (router behind CSRF protection). Useful for embedding the App in tests or in an externally managed listener.

func (*App) Router

func (a *App) Router() *Router

Router returns the App's router.

func (*App) Run

func (a *App) Run() error

Run initializes all services, runs pending migrations, starts the HTTP server, and blocks until a shutdown signal is received.

func (*App) Serve

func (a *App) Serve() error

Serve starts the HTTP server and blocks until it stops. A clean shutdown (http.ErrServerClosed) returns nil; any other listener error is returned.

func (*App) Server

func (a *App) Server() *http.Server

Server returns the App's *http.Server, constructing it on first call from the current Config. The instance is cached and safe to call concurrently.

func (*App) Start

func (a *App) Start() error

Start initializes the underlying Worker (services, migrations, hooks) and binds configuration. It does not start the HTTP server; call Serve for that, or use Run to do both and block until shutdown.

func (*App) Stop

func (a *App) Stop() error

Stop emits the server-shutting-down event, gracefully shuts down the HTTP server within ShutdownTimeout, and then shuts down the underlying Worker.

type AppConfigUpdatedPayload

type AppConfigUpdatedPayload struct {
	Config Config
}

AppConfigUpdatedPayload carries the updated config.

type AppOption

type AppOption func(*App)

AppOption configures HTTP-specific aspects of an App (error handler, CSRF, trusted origins). Only NewApp accepts AppOption values; passing one to NewWorker panics.

func WithCSRFDenyHandler

func WithCSRFDenyHandler(h http.Handler) AppOption

WithCSRFDenyHandler sets the handler invoked when a cross-origin request is rejected by CSRF protection. The default handler returns 403 Forbidden.

func WithCSRFInsecureBypassPattern

func WithCSRFInsecureBypassPattern(pattern string) AppOption

WithCSRFInsecureBypassPattern adds a URL path pattern that bypasses CSRF cross-origin protection. Use only for endpoints that require unauthenticated cross-origin access and implement their own request validation (e.g. webhook receivers).

func WithErrorHandler

func WithErrorHandler(h ErrorHandler) AppOption

WithErrorHandler configures the function that converts DI-aware handler errors into HTTP responses.

func WithTrustedOrigin

func WithTrustedOrigin(origin string) AppOption

WithTrustedOrigin adds an origin that is permitted to make cross-origin non-safe requests (POST, PUT, PATCH, DELETE, etc.). The origin must be an absolute URL with a scheme and host, e.g. "https://app.example.com". Panics if the origin is not a valid absolute URL.

type Authenticator

type Authenticator interface {
	Authenticate(ctx context.Context, r *http.Request) (Principal, error)
}

Authenticator extracts a Principal from an HTTP request. Implementations may use JWTs, server-side sessions, API keys, or any other credential scheme.

type Authorizer

type Authorizer interface {
	Authorize(ctx context.Context, principal Principal, action, resource string) error
}

Authorizer decides whether a Principal is allowed to perform an action on a resource. It is a separate concern from authentication — an Authenticator identifies who the caller is, while an Authorizer enforces what they can do.

type BasePrincipalMetadata

type BasePrincipalMetadata map[string]any

BasePrincipalMetadata is a map-based implementation of PrincipalMetadata.

func (BasePrincipalMetadata) Value

func (m BasePrincipalMetadata) Value(key string) any

Value returns the metadata value for the given key, or nil if not present.

type CacheControlOption

type CacheControlOption func(*CacheControlOptions)

CacheControlOption is a functional option for configuring CacheControl.

func WithCacheControlImmutable

func WithCacheControlImmutable() CacheControlOption

WithCacheControlImmutable appends the "immutable" directive.

func WithCacheControlMaxAge

func WithCacheControlMaxAge(val time.Duration) CacheControlOption

WithCacheControlMaxAge appends a "max-age" directive with the given duration.

func WithCacheControlMustRevalidate

func WithCacheControlMustRevalidate() CacheControlOption

WithCacheControlMustRevalidate appends the "must-revalidate" directive.

func WithCacheControlMustUnderstand

func WithCacheControlMustUnderstand() CacheControlOption

WithCacheControlMustUnderstand appends the "must-understand" directive.

func WithCacheControlNoCache

func WithCacheControlNoCache() CacheControlOption

WithCacheControlNoCache appends the "no-cache" directive.

func WithCacheControlNoStore

func WithCacheControlNoStore() CacheControlOption

WithCacheControlNoStore appends the "no-store" directive.

func WithCacheControlNoTransform

func WithCacheControlNoTransform() CacheControlOption

WithCacheControlNoTransform appends the "no-transform" directive.

func WithCacheControlPath

func WithCacheControlPath(val string) CacheControlOption

WithCacheControlPath adds an exact path that should receive the Cache-Control header.

func WithCacheControlPathPrefix

func WithCacheControlPathPrefix(val string) CacheControlOption

WithCacheControlPathPrefix adds a path prefix to match against. Any request whose path starts with this prefix will receive the Cache-Control header.

func WithCacheControlPathPrefixes

func WithCacheControlPathPrefixes(val []string) CacheControlOption

WithCacheControlPathPrefixes adds multiple path prefixes to match against.

func WithCacheControlPathSuffix

func WithCacheControlPathSuffix(val string) CacheControlOption

WithCacheControlPathSuffix adds a path suffix to match against (e.g., ".css", ".js"). Any request whose path ends with this suffix will receive the Cache-Control header.

func WithCacheControlPathSuffixes

func WithCacheControlPathSuffixes(val []string) CacheControlOption

WithCacheControlPathSuffixes adds multiple path suffixes to match against.

func WithCacheControlPaths

func WithCacheControlPaths(val []string) CacheControlOption

WithCacheControlPaths adds multiple exact paths that should receive the Cache-Control header.

func WithCacheControlPrivate

func WithCacheControlPrivate() CacheControlOption

WithCacheControlPrivate appends the "private" directive.

func WithCacheControlProxyRevalidate

func WithCacheControlProxyRevalidate() CacheControlOption

WithCacheControlProxyRevalidate appends the "proxy-revalidate" directive.

func WithCacheControlPublic

func WithCacheControlPublic() CacheControlOption

WithCacheControlPublic appends the "public" directive.

func WithCacheControlSMaxAge

func WithCacheControlSMaxAge(val time.Duration) CacheControlOption

WithCacheControlSMaxAge appends an "s-maxage" directive (shared/CDN cache max age).

func WithCacheControlStaleIfError

func WithCacheControlStaleIfError(val time.Duration) CacheControlOption

WithCacheControlStaleIfError appends a "stale-if-error" directive.

func WithCacheControlStaleWhileRevalidate

func WithCacheControlStaleWhileRevalidate(val time.Duration) CacheControlOption

WithCacheControlStaleWhileRevalidate appends a "stale-while-revalidate" directive.

type CacheControlOptions

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

CacheControlOptions configures the behavior of the CacheControl middleware.

type CacheProvider

type CacheProvider interface {
	Get(ctx context.Context, key string) ([]byte, error)
	Set(ctx context.Context, key string, value []byte, ttl time.Duration) error
	Delete(ctx context.Context, key string) error
	Exists(ctx context.Context, key string) (bool, error)
}

CacheProvider abstracts key-value caching. Implemented by in-memory, Redis, Valkey, or any other cache service.

type Config

type Config struct {
	// Embedded GasEnv
	env.WithGasEnv

	Server ServerSettings
}

Config holds server-level configuration passed from the host server to the App. Sensible defaults are applied via DefaultConfig().

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig returns a Config with sensible defaults.

func (*Config) Validate

func (c *Config) Validate() error

Validate checks that the Config fields are valid.

type ConfigProvider

type ConfigProvider interface {
	SetDefault(key string, value any)
	SetDefaults(values any) error
	Set(key string, value any)
	Bind(dest any, options ...config.BindOption) error
	Get(key string) any
	Find(key string) (value any, exist bool)
	Values() map[string]any
}

ConfigProvider defines the config struct API, used by other modules that needs access to the config provider.

type Context

type Context interface {
	context.Context
	// ResponseWriter returns the underlying http.ResponseWriter.
	ResponseWriter() http.ResponseWriter
	// Request returns the underlying *http.Request.
	Request() *http.Request
	// JSON serializes v as JSON and writes it with the given status code.
	JSON(status int, v any) error
	// XML serializes v as XML and writes it with the given status code.
	// Uses content type "application/xml; charset=utf-8".
	XML(status int, v any) error
	// RSS serializes v as XML with content type "application/rss+xml; charset=utf-8".
	RSS(status int, v any) error
	// HTML writes an HTML response with the given status code and content string.
	HTML(status int, s string) error
	// Text writes a plain-text response with the given status code.
	Text(status int, s string) error
	// NoContent writes a 204 No Content response.
	NoContent() error
	// Redirect sends an HTTP redirect to the given URL with the given status code.
	Redirect(status int, url string)
	// Param returns the URL parameter value by name (chi.URLParam).
	Param(key string) string
	// Query returns the query string parameter value by name.
	Query(key string) string
	// Header returns the request header value by name.
	Header(key string) string
	// SetHeader sets a response header.
	SetHeader(key, value string)
	// BindJSON decodes the request body as JSON into dest and performs automatic validation
	// using the configured validator.
	BindJSON(dest any) error
	// BindForm binds form data from the HTTP request to the provided destination object
	// and performs automatic validation using the configured validator.
	BindForm(dest any) error
	// Validator returns a pointer to the validator.Validate instance used for request validation.
	Validator() *validator.Validate
	// FormDecoder returns a preconfigured *schema.Decoder instance for decoding form data into structs.
	FormDecoder() *schema.Decoder
}

Context is the first parameter of every DI-aware handler. It wraps the HTTP response writer and request into a single value. The per-request scope is accessible via RequestScope(c.Request()) — the adapter resolves dependencies automatically, so handlers rarely need to access the scope directly.

func NewContext

func NewContext(parent context.Context, w http.ResponseWriter, r *http.Request, opts ...ContextOption) Context

NewContext creates a Context from the standard HTTP pair.

type ContextOption

type ContextOption func(*reqContext)

ContextOption is a functional option used to modify or extend the behavior of a reqContext at creation time.

func WithFormDecoder

func WithFormDecoder(d *schema.Decoder) ContextOption

WithFormDecoder sets a custom form decoder for the reqContext using the provided *schema.Decoder instance.

func WithValidate

func WithValidate(v *validator.Validate) ContextOption

WithValidate returns a ContextOption that sets the provided *validator.Validate instance to the reqContext.

type DatabaseProvider

type DatabaseProvider interface {
	DB() *sql.DB
	Driver() string
	Ping(ctx context.Context) error
	Query(ctx context.Context, query string, args ...any) (Rows, error)
	Exec(ctx context.Context, query string, args ...any) (Result, error)
	BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error)
	WithTx(ctx context.Context, opts *sql.TxOptions, fn func(*sql.Tx) error) (err error)
}

DatabaseProvider abstracts database access. Implemented by gas-database or any other database service. DB() exposes the underlying *sql.DB so that sqlc-generated code and transactions can use it directly.

type Email

type Email struct {
	From     string
	ReplyTo  string
	Subject  string
	TextBody string
	HTMLBody string

	Headers map[string]string

	To  []string
	Cc  []string
	Bcc []string
}

Email represents an email message.

type EmailProvider

type EmailProvider interface {
	Send(ctx context.Context, msg *Email) error
	SendFromTemplate(ctx context.Context, msg *TemplatedEmail) error
}

EmailProvider abstracts email sending.

type EnqueueOption

type EnqueueOption func(*enqueueOptions)

EnqueueOption configures an Enqueue call.

func WithDedupeID

func WithDedupeID(id string) EnqueueOption

WithDedupeID sets a deduplication identifier.

func WithDelay

func WithDelay(d time.Duration) EnqueueOption

WithDelay sets an initial delay before the job becomes visible to consumers.

func WithGroupID

func WithGroupID(id string) EnqueueOption

WithGroupID sets the message group for FIFO queue ordering.

func WithJobAttributes

func WithJobAttributes(attrs map[string]string) EnqueueOption

WithJobAttributes attaches provider-specific key-value metadata to the job.

type ErrorHandler

type ErrorHandler func(ctx Context, err error)

ErrorHandler converts a handler error into an HTTP response.

type Event

type Event[TData any] struct {
	Name string
}

Event is a typed event definition

type EventBus

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

EventBus is a publish/subscribe message bus with service ownership tracking.

func NewEventBus

func NewEventBus() *EventBus

NewEventBus creates an empty EventBus.

func (*EventBus) Emit

func (bus *EventBus) Emit(event string, data any) *sync.WaitGroup

Emit dispatches an event to all subscribers concurrently and returns a WaitGroup.

func (*EventBus) RemoveByModule

func (bus *EventBus) RemoveByModule(service string)

RemoveByModule removes all subscriptions registered by the given service.

func (*EventBus) Subscribe

func (bus *EventBus) Subscribe(event string, handler func(any))

Subscribe registers a handler for an event without service ownership. Use SubscribeWithOwner when subscribing from a service so that RemoveByModule can clean up subscriptions.

func (*EventBus) SubscribeWithOwner

func (bus *EventBus) SubscribeWithOwner(service, event string, handler func(any))

SubscribeWithOwner registers a handler for an event with service ownership tracking. The base server uses this ownership info during kill-switch to remove all subscriptions belonging to a closed service.

type HealthProvider

type HealthProvider interface {
	CheckHealth(ctx context.Context) map[string]error
}

HealthProvider aggregates health signals across all active services that implement HealthReporter. The returned map keys are service names; a nil value indicates a healthy service.

type HealthReporter

type HealthReporter interface {
	CheckHealth(ctx context.Context) error
}

HealthReporter is implemented by services that can report their own health. Answers the question "am I alive?" — a failure here signals that the process is in a broken state that a restart would resolve (lost connection that won't recover, corrupt internal state, deadlock). Maps to a Kubernetes livenessProbe. A nil return means healthy; a non-nil error describes the failure.

type Job

type Job struct {
	ID            string
	ReceiptHandle string            // opaque token used by Ack/Nack
	Attributes    map[string]string // provider-specific metadata
	Body          []byte
}

Job represents a message received from a queue.

type JobQueueProvider

type JobQueueProvider interface {
	Enqueue(ctx context.Context, queue string, payload []byte, opts ...EnqueueOption) error
	Dequeue(ctx context.Context, queue string, maxMessages int, wait time.Duration) ([]Job, error)
	Ack(ctx context.Context, queue string, job Job) error
	Nack(ctx context.Context, queue string, job Job) error
}

JobQueueProvider abstracts async job/message queue processing. Implemented by gas-queue-sqs or any other queue service. The interface is pull-based: consumers call Dequeue in their own worker loop and acknowledge results with Ack/Nack.

type LogEvent

type LogEvent interface {
	Str(key, val string) LogEvent
	Int(key string, val int) LogEvent
	Int64(key string, val int64) LogEvent
	Float64(key string, val float64) LogEvent
	Bool(key string, val bool) LogEvent
	Err(key string, val error) LogEvent
	Duration(key string, val time.Duration) LogEvent
	Any(key string, val any) LogEvent
	// Send finalizes and emits the log event.
	Send()
}

LogEvent is a single structured log entry. Field methods return the same LogEvent for chaining. Call Send to finalize and emit the event.

type Logger

type Logger interface {
	// Trace starts a log event at the TRACE level.
	Trace(msg string) LogEvent
	// Debug starts a log event at the DEBUG level.
	Debug(msg string) LogEvent
	// Info starts a log event at the INFO level.
	Info(msg string) LogEvent
	// Warn starts a log event at the WARN level.
	Warn(msg string) LogEvent
	// Error starts a log event at the ERROR level.
	Error(msg string) LogEvent

	// With returns a LoggerContext for building a sub-logger with persistent fields.
	With() LoggerContext

	// SetBaseFields returns a MutableLoggerContext for attaching persistent fields
	// directly to this logger instance. Unlike With, which branches into a new logger,
	// SetBaseFields mutates the receiver so that all subsequent log events carry the
	// accumulated fields. Intended for use by request-scoped middleware.
	SetBaseFields() MutableLoggerContext

	// Flush writes any buffered log entries to the underlying output.
	Flush()
}

Logger is the provider interface for structured logging in the Gas ecosystem. Implementations produce LogEvent values at a given severity level. Each method returns a LogEvent that can be enriched with typed fields before calling Send.

Use With to derive a sub-logger that carries persistent fields across all subsequent log events.

Call Flush to ensure all buffered log entries are written (e.g. before shutdown).

func LoggerFromContext

func LoggerFromContext(ctx context.Context) Logger

LoggerFromContext returns the Logger stored in ctx, or nil if none is present.

type LoggerContext

type LoggerContext interface {
	Str(key, val string) LoggerContext
	Int(key string, val int) LoggerContext
	Int64(key string, val int64) LoggerContext
	Float64(key string, val float64) LoggerContext
	Bool(key string, val bool) LoggerContext
	Err(key string, val error) LoggerContext
	Duration(key string, val time.Duration) LoggerContext
	Any(key string, val any) LoggerContext
	// Logger returns a new Logger that includes all fields added to this context.
	Logger() Logger
}

LoggerContext is a builder for deriving a sub-logger with persistent fields. Each field method returns the same LoggerContext for chaining. Call Logger to obtain the resulting Logger that carries all accumulated fields.

type Middleware

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

Middleware represents either a named middleware (resolved from the router's registry at apply time) or an inline middleware function.

func MiddlewareByName

func MiddlewareByName(name string) Middleware

MiddlewareByName creates a Middleware that will be resolved by name from the router's internal registry when applied.

func MiddlewareFunc

func MiddlewareFunc(fn func(http.Handler) http.Handler) Middleware

MiddlewareFunc creates a Middleware that wraps an inline handler function directly. The middleware will be anonymous and omitted from the route map. Use MiddlewareFuncWithName to give it a display name.

func MiddlewareFuncWithName

func MiddlewareFuncWithName(name string, fn func(http.Handler) http.Handler) Middleware

MiddlewareFuncWithName creates a named inline Middleware. The name appears in the route map alongside routes that this middleware applies to.

type Migration

type Migration struct {
	Version     string
	Service     string
	Description string
	Up          string
	Down        string
}

Migration represents a single database migration owned by a service.

type MigrationManager

type MigrationManager interface {
	Service

	// Register adds a migration and tracks which service owns it.
	Register(service string, m Migration)

	// RegisterSlice adds multiple migrations at once for the given service.
	RegisterSlice(service string, migrations []Migration)

	// RegisterFS reads .up.sql/.down.sql files from an fs.FS and registers
	// them as migrations for the given service. Files must follow the naming
	// convention: {version}_{description}.up.sql / {version}_{description}.down.sql
	// (e.g. 20250216_001_create_users.up.sql).
	RegisterFS(service string, fsys fs.FS) error

	// RunPending applies all unapplied migrations in global version order.
	// If any migration is marked dirty, execution is blocked until the
	// dirty state is manually resolved.
	RunPending() error

	// Down reverses the last n applied migrations in reverse version order.
	Down(n int) error
}

MigrationManager is the interface for registering and executing database migrations. Services call Register during Init() to declare their migrations. The implementation lives in a separate service (gas-migrate) and is wired in by the App.

type MutableLoggerContext

type MutableLoggerContext interface {
	Str(key, val string) MutableLoggerContext
	Int(key string, val int) MutableLoggerContext
	Int64(key string, val int64) MutableLoggerContext
	Float64(key string, val float64) MutableLoggerContext
	Bool(key string, val bool) MutableLoggerContext
	Err(key string, val error) MutableLoggerContext
	Duration(key string, val time.Duration) MutableLoggerContext
	Any(key string, val any) MutableLoggerContext
	// Apply mutates the originating Logger in place with all accumulated fields.
	Apply()
}

MutableLoggerContext is a builder for attaching persistent fields directly to an existing Logger instance. Unlike LoggerContext (returned by With), which produces a new branched Logger, MutableLoggerContext.Apply() mutates the receiver in place. Intended for use in request-scoped middleware where the same Logger instance is shared across the whole request.

type NopLogEvent

type NopLogEvent struct{}

NopLogEvent is a LogEvent that discards all fields and performs no action on Send. All methods return the receiver for chaining.

func (*NopLogEvent) Any

func (e *NopLogEvent) Any(string, any) LogEvent

Any adds a key-value pair to the event, where the value can be of any type, returning the event for chaining.

func (*NopLogEvent) Bool

func (e *NopLogEvent) Bool(string, bool) LogEvent

Bool adds a boolean field to the log event and returns the event for method chaining.

func (*NopLogEvent) Duration

func (e *NopLogEvent) Duration(string, time.Duration) LogEvent

Duration adds a field with a duration value to the log entry, identified by the given key.

func (*NopLogEvent) Err

func (e *NopLogEvent) Err(string, error) LogEvent

Err adds an error field to the log event with the specified key and value. Returns the log event for chaining.

func (*NopLogEvent) Float64

func (e *NopLogEvent) Float64(string, float64) LogEvent

Float64 sets a float64 value for the specified key in the log event. It returns the LogEvent instance for chaining.

func (*NopLogEvent) Int

func (e *NopLogEvent) Int(string, int) LogEvent

Int adds an integer field to the log event with the provided key and value, returning the LogEvent for chaining.

func (*NopLogEvent) Int64

func (e *NopLogEvent) Int64(string, int64) LogEvent

Int64 adds an int64 field to the log event with the specified key and value. It returns the LogEvent for chaining.

func (*NopLogEvent) Send

func (e *NopLogEvent) Send()

Send finalizes the log event. In NopLogEvent, it performs no action and discards the event.

func (*NopLogEvent) Str

func (e *NopLogEvent) Str(string, string) LogEvent

Str adds a string field with the given key and value to the log event and returns the event for chaining.

type NopLogger

type NopLogger struct{}

NopLogger is a Logger implementation that silently discards all log output. It uses a singleton pattern — all instances share the same underlying value, resulting in zero allocations per log call.

func (*NopLogger) Debug

func (l *NopLogger) Debug(string) LogEvent

Debug logs a debug-level message and returns a no-op LogEvent for chaining.

func (*NopLogger) Error

func (l *NopLogger) Error(string) LogEvent

Error logs an error-level message with no operational effect, returning a no-op LogEvent.

func (*NopLogger) Flush

func (l *NopLogger) Flush()

Flush performs no operation as NopLogger discards all log output without buffering.

func (*NopLogger) Info

func (l *NopLogger) Info(string) LogEvent

Info logs an informational message and returns a no-op LogEvent instance.

func (*NopLogger) SetBaseFields

func (l *NopLogger) SetBaseFields() MutableLoggerContext

SetBaseFields returns a no-op MutableLoggerContext associated with the NopLogger instance.

func (*NopLogger) Trace

func (l *NopLogger) Trace(string) LogEvent

Trace creates a no-op log event for a trace-level message and silently discards it.

func (*NopLogger) Warn

func (l *NopLogger) Warn(string) LogEvent

Warn logs a warning-level message and returns a no-op LogEvent instance.

func (*NopLogger) With

func (l *NopLogger) With() LoggerContext

With creates a new LoggerContext for deriving a sub-logger with persistent fields across log events.

type NopLoggerContext

type NopLoggerContext struct{}

NopLoggerContext is a LoggerContext that discards all fields. All methods return the receiver for chaining and Logger returns a NopLogger.

func (*NopLoggerContext) Any

Any adds a field with the given key and value to the context, supporting any type, and returns the LoggerContext.

func (*NopLoggerContext) Bool

Bool adds a boolean field to the logging context under the specified key for structured logging.

func (*NopLoggerContext) Duration

Duration adds a time.Duration field with the specified key and value to the logging context and returns it.

func (*NopLoggerContext) Err

Err adds an error field to the logger context with the specified key and error value, returning the same context.

func (*NopLoggerContext) Float64

Float64 adds a float64 field with the specified key and value to the context and returns the LoggerContext for chaining.

func (*NopLoggerContext) Int

Int adds an integer field to the logger context with the specified key. Returns the same LoggerContext for chaining.

func (*NopLoggerContext) Int64

Int64 adds a key-value pair with an int64 value to the log context and returns the updated LoggerContext.

func (*NopLoggerContext) Logger

func (c *NopLoggerContext) Logger() Logger

Logger returns a no-op Logger instance that discards all log events.

func (*NopLoggerContext) Str

Str adds a string key-value pair to the logger context and returns the same context for chaining.

type NopLoggerCtor

type NopLoggerCtor func() *NopLogger

NopLoggerCtor defines a constructor function that returns a nop-logger implementing the Logger interface.

func NewNopLogger

func NewNopLogger() NopLoggerCtor

NewNopLogger returns a NopLoggerCtor that constructs a nop-logger implementing the Logger interface.

type NopMutableLoggerContext

type NopMutableLoggerContext struct{}

NopMutableLoggerContext is a MutableLoggerContext that discards all fields. All methods return the receiver for chaining and Apply is a no-op.

func (*NopMutableLoggerContext) Any

Any adds a custom key-value pair with a generic value to the context and returns the current MutableLoggerContext.

func (*NopMutableLoggerContext) Apply

func (c *NopMutableLoggerContext) Apply()

Apply performs a no-op operation and returns without modifying any state or producing side effects.

func (*NopMutableLoggerContext) Bool

Bool adds a boolean field with the given key and value to the logger context and returns the context for chaining.

func (*NopMutableLoggerContext) Duration

Duration adds a duration field to the logging context with the specified key and value.

func (*NopMutableLoggerContext) Err

Err adds an error field to the logger context and returns the same context for chaining.

func (*NopMutableLoggerContext) Float64

Float64 adds a float64 key-value pair to the context without storing it and returns the receiver for chaining.

func (*NopMutableLoggerContext) Int

Int adds an integer field to the context and returns the updated MutableLoggerContext.

func (*NopMutableLoggerContext) Int64

Int64 adds a key-value pair where the value is an int64 and returns the current MutableLoggerContext for chaining.

func (*NopMutableLoggerContext) Str

Str adds a string field with the given key and value to the logger context and returns the context for chaining.

type ObjectInfo

type ObjectInfo struct {
	LastModified time.Time
	Metadata     map[string]string
	ContentType  string
	Size         int64
}

ObjectInfo holds metadata about an object without its body, returned by Head.

type Option

type Option interface {
	// contains filtered or unexported methods
}

Option is a marker interface satisfied by both WorkerOption and AppOption. NewWorker and NewApp accept ...Option so callers can pass either type.

type Principal

type Principal interface {
	Subject() string
	Scheme() string
	CredentialID() string
	Metadata() PrincipalMetadata
}

Principal represents an authenticated identity. Subject is the stable user identifier, Scheme identifies the authentication method (e.g. "jwt", "session", "apikey"), and CredentialID is the specific credential instance (session ID, JWT jti, API key ID).

func MustPrincipalFromContext

func MustPrincipalFromContext(ctx context.Context) Principal

MustPrincipalFromContext retrieves the Principal from the context and panics if no principal is present.

func PrincipalFromContext

func PrincipalFromContext(ctx context.Context) Principal

PrincipalFromContext retrieves the Principal from the context, or nil if no principal is present.

type PrincipalMetadata

type PrincipalMetadata interface {
	Value(key string) any
}

PrincipalMetadata provides read-only access to arbitrary key-value metadata attached to a Principal at construction time.

type PrincipalRevoker

type PrincipalRevoker interface {
	Revoke(ctx context.Context, principal Principal) error
	RevokeAll(ctx context.Context, subject string) error
	RevokeAllByScheme(ctx context.Context, subject, scheme string) error
}

PrincipalRevoker invalidates credentials associated with a Principal. Use Revoke to invalidate a single credential, RevokeAll to invalidate every credential for a subject, or RevokeAllByScheme to invalidate all credentials of a specific scheme (e.g. all sessions, all API keys) for a subject.

type ReadyProvider

type ReadyProvider interface {
	CheckReady(ctx context.Context) map[string]error
}

ReadyProvider aggregates readiness signals across all active services that implement ReadyReporter. The returned map keys are service names; a nil value indicates a ready service.

type ReadyReporter

type ReadyReporter interface {
	CheckReady(ctx context.Context) error
}

ReadyReporter is implemented by services that can report whether they are ready to accept traffic. Answers the question "should traffic be routed to me?" — distinct from health, since a service can be alive but not yet ready (warming caches, pending migrations, dependency briefly unavailable, draining during shutdown). Maps to a Kubernetes readinessProbe. A nil return means ready; a non-nil error describes why the service is not ready.

type RegisteredRoute

type RegisteredRoute struct {
	Method     string
	Path       string
	Middleware []string
}

RegisteredRoute is an exported snapshot of a registered route.

type RequestLoggerOption

type RequestLoggerOption func(*RequestLoggerOptions)

RequestLoggerOption is a functional option for configuring RequestLogger.

func WithRequestLoggerAppendRequestID

func WithRequestLoggerAppendRequestID(val bool) RequestLoggerOption

WithRequestLoggerAppendRequestID controls whether the request ID (from chi's RequestID middleware) is added to the logger's base fields. Defaults to true.

func WithRequestLoggerBuilder

func WithRequestLoggerBuilder(fn func(Logger, *http.Request) Logger) RequestLoggerOption

WithRequestLoggerBuilder sets a function that customizes the logger used for each request. The builder receives the resolved Logger and the current request, and returns a (possibly wrapped or enriched) Logger. It is called after the request ID field is appended (if enabled). This can be used to add extra fields, swap the logger, or adjust log levels per request.

type RequestLoggerOptions

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

RequestLoggerOptions configures the behavior of the RequestLogger middleware.

type Resolver

type Resolver interface {
	// contains filtered or unexported methods
}

Resolver is implemented by ServiceContainer and Scope to provide dependency resolution. The unexported method restricts implementation to this package.

type Result

type Result interface {
	RowsAffected() (int64, error)
	LastInsertId() (int64, error)
}

Result represents the outcome of an Exec operation.

type Router

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

Router is a smart router that wraps Chi and tracks route and middleware ownership by service. MiddlewareByName middleware is resolved from an internal registry at registration time. The base server uses RemoveByModule during kill-switch to replace a closed service's routes with 503 handlers and remove its middleware.

Top-level routers created via NewRouter() start unsealed: Use, Handle, Group, and Route calls are deferred until Seal() is called. This lets services register middleware and routes in any order during Init(). Seal() flushes all pending middleware first (via chi.Use), then replays pending route operations — guaranteeing middleware-before-routes ordering that Chi requires.

Sub-routers (created inside Group/Route callbacks) are always sealed so their calls pass through to Chi immediately.

Concurrency model: once sealed, the live chi tree is immutable. ServeHTTP reads it lock-free via an atomic pointer (served). Runtime mutators (RemoveByModule, and Handle/Route/Group/Use via RestartService) never touch the tree in flight — they rebuild a fresh chi tree under r.mu by replaying the recorded registrations and atomically swap it in. In-flight requests keep serving the previous tree, which is never written after publication.

func NewRouter

func NewRouter() *Router

NewRouter creates a Router backed by Chi with an empty middleware registry. The router starts unsealed — Use/Handle/Group/Route calls are deferred until Seal() is called.

func (*Router) Group

func (r *Router) Group(fn func(sub *Router))

Group creates an inline route group. The callback receives a sub-Router that shares the parent's middleware registry and route tracking. When the router is unsealed, the group is deferred until Seal().

func (*Router) Handle

func (r *Router) Handle(service, method, path string, handler any, middleware ...Middleware)

Handle registers a route and tracks ownership. The handler can be:

  • http.HandlerFunc or func(http.ResponseWriter, *http.Request) — passed through directly
  • A DI-aware function: func(gas.Context, Dep1, Dep2, ...) error — dependencies are auto-resolved from the per-request scope at call time

Middleware is resolved from the internal registry (for MiddlewareByName) or used directly (for MiddlewareFunc) and applied in order (outermost first). Panics if a named middleware is not registered or if a DI-aware handler has an invalid signature. When the router is unsealed, the registration is deferred until Seal().

func (*Router) Mux

func (r *Router) Mux() chi.Router

Mux returns the underlying Chi router so the base server can add global middleware, mount sub-routers, or pass it to http.Server.

This returns the most recently built tree. Mutating it directly bypasses the router's synchronization and is only safe before serving begins.

func (*Router) NamedMiddleware

func (r *Router) NamedMiddleware() map[string]string

NamedMiddleware returns a snapshot of the named middleware registry as a map of middleware name to owning service.

func (*Router) NotFound

func (r *Router) NotFound(service string, handler any)

NotFound registers a custom not-found handler for the router, associated with the specified service. The handler can be http.HandlerFunc or a DI-aware function (same rules as Handle). Panics if a not found handler is already registered by another service.

func (*Router) Register

func (r *Router) Register(service, name string, mw func(http.Handler) http.Handler)

Register adds a named middleware to the internal registry and tracks which service owns it.

func (*Router) RemoveByModule

func (r *Router) RemoveByModule(service string)

RemoveByModule removes all routes and middleware registered by the given service. Routes are replaced with 503 Service Unavailable handlers.

The teardown is applied by rebuilding a fresh routing tree and swapping it in atomically, so requests in flight on the old tree are never disrupted.

func (*Router) Route

func (r *Router) Route(pattern string, fn func(sub *Router))

Route creates a pattern-scoped route group. The callback receives a sub-Router that shares the parent's middleware registry and route tracking. When the router is unsealed, the route is deferred until Seal().

Calling Route with the same pattern more than once on the same parent is allowed: subsequent calls attach to the sub-mux created by the first call instead of panicking. Each call's body runs inside its own chi.Group, so middleware added via Use() inside a given block only applies to handlers registered in that block.

func (*Router) Routes

func (r *Router) Routes() map[string][]RegisteredRoute

Routes returns a snapshot of all explicitly registered routes grouped by service. Implicit HEAD routes (auto-registered alongside GET) are excluded.

func (*Router) Seal

func (r *Router) Seal()

Seal builds the routing tree from the deferred middleware and route registrations and publishes it. Middleware is applied first (via chi.Use), then route operations are replayed in order. After Seal, structural changes rebuild the tree and swap it in atomically. Seal is idempotent — calling it on an already-sealed router is a no-op.

func (*Router) ServeHTTP

func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP implements http.Handler, delegating to the published Chi tree. The tree is loaded atomically with no lock, so serving never contends with runtime route mutations.

func (*Router) SetErrorHandler

func (r *Router) SetErrorHandler(h ErrorHandler)

SetErrorHandler configures the function that converts DI-aware handler errors into HTTP responses. Must be called before Handle() registrations.

func (*Router) Use

func (r *Router) Use(middleware ...Middleware)

Use applies middleware to the router. Each Middleware is resolved (by name from the registry or used directly) and applied via chi's Use. Panics if a named middleware is not registered. When the router is unsealed, middleware is queued and applied during Seal().

func (*Router) UseMiddlewareByName

func (r *Router) UseMiddlewareByName(middleware string)

UseMiddlewareByName applies a middleware to the router by resolving it from the registry using its name. Panics if the middleware is not registered.

func (*Router) UseMiddlewareFunc

func (r *Router) UseMiddlewareFunc(middleware func(http.Handler) http.Handler)

UseMiddlewareFunc applies a middleware function directly to the router by wrapping it as a MiddlewareFunc.

type Rows

type Rows interface {
	Next() bool
	Scan(dest ...any) error
	Close() error
	Err() error
}

Rows represents the result set of a query. Mirrors the standard database/sql Rows interface so implementations can wrap it directly.

type Scope

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

Scope is a resolution context for scoped service lifetimes.

func RequestScope

func RequestScope(r *http.Request) *Scope

RequestScope returns the per-request Scope from the request context. Panics if called outside the scope middleware (i.e. before InitServices installs it, or on a non-App-managed handler).

func (*Scope) Close

func (s *Scope) Close() error

Close calls Close() on all scoped Service instances resolved in this scope.

type SecurityHeadersOption

type SecurityHeadersOption func(*SecurityHeadersOptions)

SecurityHeadersOption is a functional option for configuring SecurityHeaders.

func WithSecurityHeadersContentSecurityPolicy

func WithSecurityHeadersContentSecurityPolicy(val string) SecurityHeadersOption

WithSecurityHeadersContentSecurityPolicy sets the Content-Security-Policy header value. Pass an empty string to disable this header. No default is applied, since CSP values are highly application-specific.

func WithSecurityHeadersContentTypeOptions

func WithSecurityHeadersContentTypeOptions(val string) SecurityHeadersOption

WithSecurityHeadersContentTypeOptions sets the X-Content-Type-Options header value. Pass an empty string to disable this header. Default: "nosniff".

func WithSecurityHeadersCrossOriginOpenerPolicy

func WithSecurityHeadersCrossOriginOpenerPolicy(val string) SecurityHeadersOption

WithSecurityHeadersCrossOriginOpenerPolicy sets the Cross-Origin-Opener-Policy header value. Pass an empty string to disable this header. No default is applied.

func WithSecurityHeadersCrossOriginResourcePolicy

func WithSecurityHeadersCrossOriginResourcePolicy(val string) SecurityHeadersOption

WithSecurityHeadersCrossOriginResourcePolicy sets the Cross-Origin-Resource-Policy header value. Pass an empty string to disable this header. No default is applied.

func WithSecurityHeadersFrameOptions

func WithSecurityHeadersFrameOptions(val string) SecurityHeadersOption

WithSecurityHeadersFrameOptions sets the X-Frame-Options header value. Pass an empty string to disable this header. Default: "DENY".

func WithSecurityHeadersPermissionsPolicy

func WithSecurityHeadersPermissionsPolicy(val string) SecurityHeadersOption

WithSecurityHeadersPermissionsPolicy sets the Permissions-Policy header value. Pass an empty string to disable this header. Default: "camera=(), microphone=(), geolocation=()".

func WithSecurityHeadersReferrerPolicy

func WithSecurityHeadersReferrerPolicy(val string) SecurityHeadersOption

WithSecurityHeadersReferrerPolicy sets the Referrer-Policy header value. Pass an empty string to disable this header. Default: "strict-origin-when-cross-origin".

func WithSecurityHeadersStrictTransportSecurity

func WithSecurityHeadersStrictTransportSecurity(val string) SecurityHeadersOption

WithSecurityHeadersStrictTransportSecurity sets the Strict-Transport-Security (HSTS) header value. Pass an empty string to disable this header. No default is applied, since HSTS should only be enabled once the site is fully served over HTTPS.

type SecurityHeadersOptions

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

SecurityHeadersOptions configures which security headers are set by the SecurityHeaders middleware.

type ServerSettings

type ServerSettings struct {
	// Host specifies the hostname or IP address where the server will be hosted.
	Host string

	// Port is the port number on which the server listens for incoming requests.
	Port int

	// ReadTimeout is the maximum duration for reading the entire request.
	ReadTimeout time.Duration

	// WriteTimeout is the maximum duration before timing out writes of the response.
	WriteTimeout time.Duration

	// IdleTimeout is the maximum time to wait for the next request when keep-alives are enabled.
	IdleTimeout time.Duration

	// ShutdownTimeout is how long to wait for in-flight requests to complete
	// during graceful shutdown.
	ShutdownTimeout time.Duration
}

ServerSettings defines the configuration for a server, including host, port, timeouts, and graceful shutdown settings.

type Service

type Service interface {
	// Name returns the unique identifier for this service (e.g., "gas-auth").
	Name() string

	// Init initializes the service. Called automatically by the DI container
	// after construction. Services register routes, middleware, migrations,
	// and event subscriptions here.
	Init() error

	// Close gracefully shuts down the service. Called at App shutdown
	// (singletons) or when a Scope is closed (scoped services).
	Close() error
}

Service is the core interface for lifecycle-managed services. Any type registered with the DI container that implements this interface will have Init() called after construction and Close() called at shutdown (singletons) or scope end (scoped). Transient services cannot implement this interface — registration will be rejected.

type ServiceContainer

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

ServiceContainer is a dependency injection container that manages service registration, construction, and lifetime-scoped resolution.

func NewServiceContainer

func NewServiceContainer() *ServiceContainer

NewServiceContainer creates a new ServiceContainer.

func (*ServiceContainer) BuildAll

func (c *ServiceContainer) BuildAll() error

BuildAll eagerly resolves all singleton services in dependency order. Transient and scoped services are validated but not constructed.

func (*ServiceContainer) CanResolve

func (c *ServiceContainer) CanResolve(t reflect.Type) bool

CanResolve reports whether the container has an instance or registration that can satisfy the given type (including interface matching).

func (*ServiceContainer) EachInstance

func (c *ServiceContainer) EachInstance(fn func(reflect.Value))

EachInstance iterates all built singleton instances (including pre-registered ones).

func (*ServiceContainer) NewScope

func (c *ServiceContainer) NewScope() *Scope

NewScope creates a scoped resolution context. Scoped services resolved within this scope share instances; singletons delegate to the container.

type ServiceLifetime

type ServiceLifetime uint8

ServiceLifetime controls how a service instance is created and cached.

const (
	// ServiceLifetimeSingleton services are created once and shared across all consumers.
	ServiceLifetimeSingleton ServiceLifetime = iota
	// ServiceLifetimeScoped services are created once per Scope.
	ServiceLifetimeScoped
	// ServiceLifetimeTransient services are created fresh on every resolution.
	ServiceLifetimeTransient
)

func (ServiceLifetime) String

func (l ServiceLifetime) String() string

type StorageObject

type StorageObject struct {
	Body        io.ReadCloser
	Metadata    map[string]string
	ContentType string
	Size        int64
}

StorageObject holds the response from a Download call, including the body and any metadata the backend provides (content type, size, etc.).

type StorageOption

type StorageOption func(*storageOptions)

StorageOption configures a storage operation.

func InBucket

func InBucket(name string) StorageOption

InBucket overrides the default bucket for a single operation.

func WithContentType

func WithContentType(ct string) StorageOption

WithContentType sets the content type for an Upload.

func WithMetadata

func WithMetadata(m map[string]string) StorageOption

WithMetadata attaches provider-specific key-value metadata to an Upload.

type StorageProvider

type StorageProvider interface {
	Upload(ctx context.Context, key string, data io.Reader, opts ...StorageOption) error
	Download(ctx context.Context, key string, opts ...StorageOption) (*StorageObject, error)
	Delete(ctx context.Context, key string, opts ...StorageOption) error
	Head(ctx context.Context, key string, opts ...StorageOption) (*ObjectInfo, error)
	PresignDownloadURL(ctx context.Context, key string, expires time.Duration, opts ...StorageOption) (string, error)
	PresignUploadURL(ctx context.Context, key string, expires time.Duration, opts ...StorageOption) (string, error)
}

StorageProvider abstracts file storage (S3, DO Spaces, local filesystem, etc.).

type SystemAllServicesInitializedPayload

type SystemAllServicesInitializedPayload struct{}

SystemAllServicesInitializedPayload is an empty payload for the all-services-initialized event.

type SystemServerShuttingDownPayload

type SystemServerShuttingDownPayload struct{}

SystemServerShuttingDownPayload is an empty payload for the server-shutting-down event.

type SystemServiceClosedPayload

type SystemServiceClosedPayload struct {
	ServiceName string
}

SystemServiceClosedPayload carries the name of the closed service.

type SystemServiceInitializedPayload

type SystemServiceInitializedPayload struct {
	ServiceName string
}

SystemServiceInitializedPayload carries the name of the initialized service.

type SystemShuttingDownPayload

type SystemShuttingDownPayload struct{}

SystemShuttingDownPayload is an empty payload for the shutting-down event.

type TemplateProvider

type TemplateProvider interface {
	// Get returns the raw template content by name.
	Get(ctx context.Context, name string) ([]byte, error)

	// List returns all available template names.
	List(ctx context.Context) ([]string, error)

	// Register adds or replaces a template by name and raw content.
	Register(ctx context.Context, name string, content []byte) error

	// RegisterFS walks an fs.FS and registers every template file found.
	RegisterFS(ctx context.Context, fsys fs.FS) error
}

TemplateProvider abstracts template storage and retrieval. Implementations may be backed by a filesystem, database, memory, or any combination. Consumers such as UIProvider (HTML rendering) and EmailProvider (email templating) resolve raw template content through this interface.

type TemplatedEmail

type TemplatedEmail struct {
	SubjectTemplate string
	TextTemplate    string
	HTMLTemplate    string
	Data            any
	Email
}

TemplatedEmail represents an email message where templates and data define subject, text, and HTML bodies.

type UIProvider

type UIProvider interface {
	Render(w http.ResponseWriter, name string, data any) error
	RenderFragment(w http.ResponseWriter, name string, data any) error // renders without layout wrapper, useful for HTMX
	RenderWithStatus(w http.ResponseWriter, status int, name string, data any) error
	RegisterFuncs(funcs template.FuncMap)
}

UIProvider abstracts template rendering. Implemented by gas-ui or any other UI service. Template storage and retrieval is handled by TemplateProvider; UIProvider focuses on compilation and rendering.

type Worker

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

Worker manages service lifecycle, dependency injection, and the event bus without an HTTP server. Use it for non-HTTP environments such as AWS Lambda, background job processors, or CLI tools. For HTTP servers, use App which embeds Worker and adds routing, CSRF protection, and an HTTP listener.

func NewWorker

func NewWorker(opts ...Option) *Worker

NewWorker creates a Worker with the given options. Only WorkerOption values are applied; passing an AppOption panics.

func (*Worker) ActiveServices

func (w *Worker) ActiveServices() []string

ActiveServices returns the names of all currently active services.

func (*Worker) ActiveServicesMap

func (w *Worker) ActiveServicesMap() map[string]Service

ActiveServicesMap returns a map of all currently active services. This is a test helper.

func (*Worker) CheckHealth

func (w *Worker) CheckHealth(ctx context.Context) map[string]error

CheckHealth runs CheckHealth concurrently on every active service that implements HealthReporter and returns a map of service name to result (nil if healthy). Services that do not implement HealthReporter are omitted.

func (*Worker) CheckReady

func (w *Worker) CheckReady(ctx context.Context) map[string]error

CheckReady runs CheckReady concurrently on every active service that implements ReadyReporter and returns a map of service name to result (nil if ready). Services that do not implement ReadyReporter are omitted.

func (*Worker) CloseService

func (w *Worker) CloseService(name string) error

CloseService performs the kill-switch sequence for a single service at runtime. Infrastructure is cleaned up first so that even if Close() panics or fails, routes and subscriptions are already removed.

func (*Worker) ConfigProvider

func (w *Worker) ConfigProvider() ConfigProvider

ConfigProvider resolves the ConfigProvider from the DI container. Returns nil if no ConfigProvider is registered.

func (*Worker) EventBus

func (w *Worker) EventBus() *EventBus

EventBus returns the Worker's event bus.

func (*Worker) InitServices

func (w *Worker) InitServices() (err error)

InitServices builds all singletons via the DI container (which calls Init() on each Service automatically), then collects Service instances for runtime management.

func (*Worker) MigrationManager

func (w *Worker) MigrationManager() MigrationManager

MigrationManager resolves the MigrationManager from the DI container. Returns nil if no MigrationManager is registered.

func (*Worker) RestartService

func (w *Worker) RestartService(name string) error

RestartService re-initializes a previously closed service. The service must have been registered with the Worker at construction time and built during InitServices (i.e., it must be a singleton in serviceOrder).

func (*Worker) Run

func (w *Worker) Run() error

Run initializes services, runs migrations and ready hooks, then blocks until a SIGINT or SIGTERM signal is received before shutting down. For non-blocking lifecycle control, use Start and Shutdown directly.

func (*Worker) ServiceContainer

func (w *Worker) ServiceContainer() *ServiceContainer

ServiceContainer returns the Worker's dependency injection container.

func (*Worker) Shutdown

func (w *Worker) Shutdown() error

Shutdown emits SystemShuttingDown and closes all services in reverse initialization order. Safe to call multiple times (subsequent calls are no-ops once services are closed).

func (*Worker) Start

func (w *Worker) Start() error

Start initializes all services, runs pending migrations, and executes ready hooks. It does NOT block — use it when you need explicit lifecycle control (e.g. AWS Lambda). Call Shutdown when done.

type WorkerOption

type WorkerOption func(*Worker)

WorkerOption configures a Worker. It is the base option type for DI registration, ready hooks, and other non-HTTP concerns. Both NewWorker and NewApp accept WorkerOption values.

func WithReadyFunc

func WithReadyFunc(fn func(*ServiceContainer) error) WorkerOption

WithReadyFunc registers a function that runs after all services are initialized and migrations are applied but before Run blocks or Start returns. Multiple funcs are called in registration order; any error aborts startup.

func WithScopedService

func WithScopedService[T any](ctor any) WorkerOption

WithScopedService registers a service constructor with a scoped lifetime.

func WithService

func WithService[T any](ctor any, lifetime ServiceLifetime) WorkerOption

WithService registers a constructor-based service with the given lifetime.

func WithServiceInstance

func WithServiceInstance[T any](val T) WorkerOption

WithServiceInstance registers a pre-built service instance (singleton).

func WithSingletonService

func WithSingletonService[T any](ctor any) WorkerOption

WithSingletonService registers a singleton service constructor.

func WithTransientService

func WithTransientService[T any](ctor any) WorkerOption

WithTransientService registers a transient service constructor.

Jump to

Keyboard shortcuts

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