Documentation
¶
Overview ¶
Package zkit provides default-safe assembly helpers for building net/http services with an operator-friendly admin surface.
The main entry points are:
- NewDefaultService: assemble a runnable, shutdownable Service (servers + optional tasks/tuning/log level), with admin either mounted under a prefix or served on a dedicated port.
- NewDefaultAdmin: assemble the admin subtree as an http.Handler (fixed v1 paths).
You can start using zkit by only calling NewDefaultService and then writing your business handlers as usual. When you need more control, zkit exposes lower-level building blocks in subpackages (listed below).
Quick start: primary server + mounted admin ¶
This example mounts admin under "/-/" on the same server:
mux := http.NewServeMux()
mux.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte("hello"))
})
svc := zkit.NewDefaultService(zkit.ServiceSpec{
Primary: &zkit.HTTPServerSpec{
Addr: ":8080",
Handler: mux,
},
Admin: &zkit.AdminSpec{
// For demos only. In production, protect admin with token/IP-based guards.
ReadGuard: zkit.AllowAll(),
},
AdminMountPrefix: "/-/",
})
_ = svc.Run(context.Background())
With the default admin kit, the always-on read endpoints include:
- /report
- /healthz
- /readyz
- /buildinfo
- /runtime
Security model (read vs write) ¶
zkit's admin endpoints are designed to be default-safe:
Reads are explicit and guarded:
AdminSpec.ReadGuard is required and protects all read endpoints.
A nil ReadGuard is treated as an assembly error and will panic (fail-fast).
Writes are off by default:
AdminSpec.WriteGuard == nil disables all write endpoints.
If WriteGuard is non-nil, enable write groups explicitly: EnableLogLevelSet for /log/level/set; TuningWritesEnabled / TaskWritesEnabled for tuning and task writes. Allowlist (empty = deny-all) applies for tuning/task writes.
/provided (custom diagnostic snapshot) is disabled by default because it is typically more sensitive; enable it explicitly by setting AdminSpec.ProvidedItems to a non-nil map.
Tip: it is common to use separate guards for reads and writes (e.g. a read token and a stronger write token).
Admin serving mode: mount vs standalone (mutually exclusive) ¶
NewDefaultService supports two admin modes:
Mount: route requests to admin first when URL path matches the reserved prefix.
Prefix defaults to "/-/".
If a request hits the base path without the trailing slash (e.g. "/-"), it is redirected to the normalized prefix (e.g. "/-/") with HTTP 307.
Mount requires ServiceSpec.Primary (otherwise it panics).
Standalone: run admin on its own managed server (usually a dedicated port).
This is required for "worker-only" processes with no primary server.
Service lifecycle and defaults ¶
Service provides Start/Wait/Shutdown/Run:
- Start: starts managed components (tasks if enabled) and starts serving on managed servers (not idempotent)
- Wait: waits until the service fully stops (idempotent)
- Shutdown: triggers graceful shutdown (idempotent), using ShutdownTimeout (default: 30s)
- Run: Start → wait for an exit condition (ctx.Done or OS signal) → Shutdown → Wait
Signal handling in Run:
- Enabled by default (SignalsDisable=false).
- Default signals:
- Unix: SIGINT + SIGTERM
- Non-Unix: os.Interrupt
HTTP server defaults (only when you use Addr+Handler and let zkit build *http.Server):
- ReadHeaderTimeout: 5s
- IdleTimeout: 60s
If you provide HTTPServerSpec.Server, zkit does not override your timeouts/BaseContext/ErrorLog/etc.
Optional components: tasks / tuning / log level ¶
NewDefaultService can host these optional components and (optionally) expose them to admin:
Tasks: TasksManager!=nil = enabled and started; TasksExposeToAdmin = wire into admin when admin enabled.
Tuning: Tuning != nil = enabled; TuningExposeToAdmin = wire into admin when admin enabled.
Log: LogLevelVar!=nil = enabled; LogExposeToAdmin = wire /log/level (read) and optionally /log/level/set (write).
Note: if admin config (especially write groups) requires a component (e.g. task writes require a task manager), NewDefaultService may create the component to satisfy assembly, but write capabilities still require explicit enabling via AdminSpec.WriteGuard and the enable flags (EnableLogLevelSet, TuningWritesEnabled, TaskWritesEnabled) plus allowlists.
Spec reference (parameters at a glance) ¶
ServiceSpec (NewDefaultService): SignalsDisable, Signals, ShutdownTimeout, Primary, Extra, Admin (*AdminSpec), AdminMountPrefix, AdminStandaloneServer, TasksManager, TasksExposeToAdmin, Tuning, TuningExposeToAdmin, LogLevelVar, LogExposeToAdmin, OnStart, OnShutdown, OnServeError.
AdminSpec (Admin field / NewDefaultAdmin): ReadGuard (required), TrustedProxies, TrustedHeaders, ReadyChecks, LogLevelVar, Tuning, TaskManager, TuningReadAllowPrefixes/Keys/Func, TaskReadAllowPrefixes/Names/Func, ProvidedItems, ProvidedMaxBytes, WriteGuard, EnableLogLevelSet, TuningWritesEnabled, TuningWriteAllowPrefixes/Keys/Func, TaskWritesEnabled, TaskWriteAllowPrefixes/Names/Func.
HTTPServerSpec (Primary, Extra, AdminStandaloneServer): Name, Critical, Server (or Addr+Handler).
Building blocks (when you need finer-grained control) ¶
- github.com/evan-idocoding/zkit/admin: admin subtree assembler (Option-based, explicit EnableXxx + explicit Guard)
- github.com/evan-idocoding/zkit/ops: operational handlers (health/runtime/buildinfo/tasks/tuning/loglevel/provided snapshots)
- github.com/evan-idocoding/zkit/httpx: net/http middleware chain helpers
- github.com/evan-idocoding/zkit/httpx/client: HTTP client builder (independent transport + RoundTripper middlewares)
- github.com/evan-idocoding/zkit/rt/task: background task primitives
- github.com/evan-idocoding/zkit/rt/tuning: runtime-tunable parameters
- github.com/evan-idocoding/zkit/rt/safego: panic/error observable goroutine runner
Package zkit re-exports Guard types and constructors from the admin subpackage so callers can configure admin without importing admin.
Index ¶
- Constants
- Variables
- func NewDefaultAdmin(spec AdminSpec) http.Handler
- type AdminSpec
- type Guard
- func AllowAll() Guard
- func Check(fn func(r *http.Request) bool) Guard
- func DenyAll() Guard
- func HotTokens(set TokenSetLike, opts ...TokenOption) Guard
- func HotTokensAndIPAllowList(set TokenSetLike, cidrsOrIPs []string, opts ...TokenOption) Guard
- func HotTokensOrIPAllowList(set TokenSetLike, cidrsOrIPs []string, opts ...TokenOption) Guard
- func IPAllowList(cidrsOrIPs ...string) Guard
- func Tokens(tokens []string, opts ...TokenOption) Guard
- func TokensAndIPAllowList(tokens []string, cidrsOrIPs []string, opts ...TokenOption) Guard
- func TokensOrIPAllowList(tokens []string, cidrsOrIPs []string, opts ...TokenOption) Guard
- type HTTPServerSpec
- type ReadyCheck
- type Service
- type ServiceSpec
- type TokenOption
- type TokenSetLike
Constants ¶
const DefaultTokenHeader = admin.DefaultTokenHeader
DefaultTokenHeader is the default header name for token-based guards.
Variables ¶
var ( // ErrAlreadyStarted indicates Start/Run was called more than once. ErrAlreadyStarted = errors.New("zkit: service already started") // ErrNotStarted indicates Wait was called before Start. ErrNotStarted = errors.New("zkit: service not started") )
Functions ¶
func NewDefaultAdmin ¶
NewDefaultAdmin assembles a default-safe admin subtree handler from a flat spec.
Paths are fixed (v1 default kit). For custom paths or composition, use admin.New(...) + admin.EnableXxx(...).
Types ¶
type AdminSpec ¶
type AdminSpec struct {
// Required. Protects all read endpoints.
ReadGuard Guard
// TrustedProxies: CIDRs or single IPs of trusted proxies. Empty = do not trust proxy headers, use RemoteAddr.
TrustedProxies []string
// TrustedHeaders: header names used to extract client IP (e.g. "X-Forwarded-For", "X-Real-IP"). Empty = default order.
TrustedHeaders []string
// Ready checks for /readyz. Empty = no checks (endpoint still responds OK).
ReadyChecks []ReadyCheck
// Optional read sources.
LogLevelVar *slog.LevelVar
Tuning *tuning.Tuning
TaskManager *task.Manager
// Tuning read access: zero = no filter. AllowFunc mutually exclusive with AllowPrefixes/AllowKeys.
TuningReadAllowPrefixes []string
TuningReadAllowKeys []string
TuningReadAllowFunc func(key string) bool
// Task read access: zero = no filter. AllowFunc mutually exclusive with AllowPrefixes/AllowNames.
TaskReadAllowPrefixes []string
TaskReadAllowNames []string
TaskReadAllowFunc func(name string) bool
// Provided (sensitive). Non-nil = enable /provided with this map; nil = disabled.
ProvidedItems map[string]any
ProvidedMaxBytes int // <= 0 uses ops default
// Writes: nil = no write endpoints. Non-nil = guard for all write endpoints; individual groups gated by their Enable flag and allowlists.
WriteGuard Guard
// Enable /log/level/set. Requires WriteGuard != nil and LogLevelVar != nil.
EnableLogLevelSet bool
// Tuning writes: TuningWritesEnabled true = enable group (requires Tuning != nil). Allowlist applies; empty = deny-all. AllowFunc mutually exclusive with slices.
TuningWritesEnabled bool
TuningWriteAllowPrefixes []string
TuningWriteAllowKeys []string
TuningWriteAllowFunc func(key string) bool
// Task writes: TaskWritesEnabled true = enable group (requires TaskManager != nil). Allowlist applies; empty = deny-all. AllowFunc mutually exclusive with slices.
TaskWritesEnabled bool
TaskWriteAllowPrefixes []string
TaskWriteAllowNames []string
TaskWriteAllowFunc func(name string) bool
}
AdminSpec configures NewDefaultAdmin. All fields are optional except ReadGuard.
Assembly errors are fail-fast and will panic.
Required ¶
- ReadGuard: must be non-nil; protects all read endpoints.
Optional reads (zero = disabled or no filter) ¶
- TrustedProxies: CIDRs or IPs of trusted proxies; empty = do not trust proxy headers, use RemoteAddr.
- TrustedHeaders: header names used to extract client IP (e.g. X-Forwarded-For, X-Real-IP). Empty = default order.
- ReadyChecks: /readyz checks; empty slice = endpoint still enabled, no checks.
- LogLevelVar: enables /log/level (read) when non-nil.
- Tuning + TuningReadAllow*: tuning read endpoints; Tuning must be non-nil. Read allowlist: zero = no filter.
- TaskManager + TaskReadAllow*: /tasks/snapshot; TaskManager must be non-nil. Read allowlist: zero = no filter.
- ProvidedItems: when non-nil, enables /provided with this map; nil = disabled. ProvidedMaxBytes optional (<=0 = default).
Note on overlap with ServiceSpec:
- AdminSpec.{LogLevelVar,Tuning,TaskManager} are admin handler data sources. When NewDefaultService is used and both ServiceSpec.<X> and ServiceSpec.Admin.<X> are provided, Admin.<X> takes precedence for the admin subtree.
- Service lifecycle ownership is controlled by ServiceSpec (e.g. Service starts/shuts down TasksManager only when ServiceSpec.TasksManager is non-nil or when Service creates/manages one for admin write needs).
Writes (WriteGuard nil = all write endpoints disabled) ¶
- WriteGuard: when non-nil, write endpoints may be enabled; this guard protects them. Required for any write.
- EnableLogLevelSet: requires WriteGuard != nil and LogLevelVar != nil (coexistence).
- Tuning write group (/tuning/set, reset-default, reset-last): set TuningWritesEnabled true to enable; requires Tuning != nil. Allowlist (empty = deny-all) applies.
- Task write group (/tasks/trigger, trigger-and-wait): set TaskWritesEnabled true to enable; requires TaskManager != nil. Allowlist (empty = deny-all) applies.
Access allowlist rules (Tuning* and Task* Allow* fields) ¶
- For reads: zero (nil slices + nil func) = no filtering. Non-zero = allowlist applied.
- For writes: set TuningWritesEnabled/TaskWritesEnabled to enable the group; allowlist then applies (empty = deny-all). AllowFunc is mutually exclusive with the slice fields (admin will panic if both set).
type Guard ¶ added in v0.1.2
Guard enforces request admission for admin endpoints. Use AllowAll, DenyAll, Tokens, IPAllowList, etc. to create one.
func AllowAll ¶ added in v0.1.2
func AllowAll() Guard
AllowAll returns a guard that allows all requests.
func Check ¶ added in v0.1.2
Check returns a guard backed by a custom fast predicate (must not block, no I/O).
func DenyAll ¶ added in v0.1.2
func DenyAll() Guard
DenyAll returns a guard that denies all requests with HTTP 403.
func HotTokens ¶ added in v0.1.2
func HotTokens(set TokenSetLike, opts ...TokenOption) Guard
HotTokens returns a guard that validates requests using a hot-update token set.
func HotTokensAndIPAllowList ¶ added in v0.1.2
func HotTokensAndIPAllowList(set TokenSetLike, cidrsOrIPs []string, opts ...TokenOption) Guard
HotTokensAndIPAllowList is like TokensAndIPAllowList but with a hot-update token set.
func HotTokensOrIPAllowList ¶ added in v0.1.2
func HotTokensOrIPAllowList(set TokenSetLike, cidrsOrIPs []string, opts ...TokenOption) Guard
HotTokensOrIPAllowList is like TokensOrIPAllowList but with a hot-update token set.
func IPAllowList ¶ added in v0.1.2
IPAllowList returns a guard backed by a static IP allowlist (CIDRs or single IPs).
func Tokens ¶ added in v0.1.2
func Tokens(tokens []string, opts ...TokenOption) Guard
Tokens returns a guard that validates requests using a static token list.
func TokensAndIPAllowList ¶ added in v0.1.2
func TokensAndIPAllowList(tokens []string, cidrsOrIPs []string, opts ...TokenOption) Guard
TokensAndIPAllowList returns a guard that allows when token is allowed AND IP is allowlisted.
func TokensOrIPAllowList ¶ added in v0.1.2
func TokensOrIPAllowList(tokens []string, cidrsOrIPs []string, opts ...TokenOption) Guard
TokensOrIPAllowList returns a guard that allows when token is allowed OR IP is allowlisted.
type HTTPServerSpec ¶
type HTTPServerSpec struct {
// Name is used for error reporting (OnServeError) and diagnostics.
// Empty name will be replaced by a default name (primary / extra#N / admin).
Name string
// Critical controls fail-fast behavior.
//
// When Critical is true (default), an unexpected Serve error triggers a global shutdown.
// When Critical is false, unexpected errors do NOT trigger shutdown but are reported via OnServeError.
//
// If nil, it defaults to true.
Critical *bool
// Provide either:
// - Server: a fully configured *http.Server (zkit will not override timeouts, BaseContext, ErrorLog, etc)
// - or Addr + Handler: zkit will build a server with conservative defaults (ReadHeaderTimeout, IdleTimeout).
//
// Assembly rules:
// - If Server != nil, Addr/Handler fields are ignored.
// - If Server == nil, Addr must be non-empty and Handler must be non-nil (when used for Primary or Extra;
// when used as AdminStandaloneServer, Handler may be nil and zkit injects the admin handler).
Server *http.Server
Addr string
Handler http.Handler
}
HTTPServerSpec describes a managed http.Server.
type ReadyCheck ¶ added in v0.1.2
ReadyCheck is a single readiness check for /readyz. Name is required (used in the report); Timeout is optional (zero = no extra timeout).
type Service ¶
type Service struct {
// Assembly outputs (optional depending on spec).
PrimaryServer *http.Server
ExtraServers []*http.Server
AdminServer *http.Server
AdminHandler http.Handler
TaskManager *task.Manager
Tuning *tuning.Tuning
LogLevelVar *slog.LevelVar
// contains filtered or unexported fields
}
Service is a runnable, shutdownable assembled service.
func NewDefaultService ¶
func NewDefaultService(spec ServiceSpec) *Service
NewDefaultService assembles a default-safe runnable Service.
Assembly errors are fail-fast and will panic. Runtime errors are returned from Start/Wait/Run/Shutdown.
func (*Service) Run ¶
Run is equivalent to Start → wait for exit condition → Shutdown → return.
It is NOT idempotent. If called after Start, it returns ErrAlreadyStarted.
func (*Service) Shutdown ¶
Shutdown triggers shutdown. It is idempotent.
If Start was never called, Shutdown returns nil. If Shutdown is already in progress, calling it again waits again using the new ctx.
type ServiceSpec ¶
type ServiceSpec struct {
// Signals: SignalsDisable true = Run() does not listen for OS signals. Signals nil/empty = default set (SIGINT+SIGTERM on Unix).
SignalsDisable bool
Signals []os.Signal
// ShutdownTimeout: <= 0 means default (30s).
ShutdownTimeout time.Duration
// Primary: optional. Required if admin is mounted (Admin != nil and AdminStandaloneServer == nil).
Primary *HTTPServerSpec
// Extra: additional servers; admin is not mounted onto them.
Extra []*HTTPServerSpec
// Admin: nil = admin disabled. Non-nil = admin enabled; use AdminMountPrefix or AdminStandaloneServer (mutually exclusive).
Admin *AdminSpec
// AdminMountPrefix: used when Admin != nil and AdminStandaloneServer == nil. Empty => "/-/".
AdminMountPrefix string
// AdminStandaloneServer: when set, admin is served on this server only (worker-only). Mutually exclusive with mount.
AdminStandaloneServer *HTTPServerSpec
// Tasks: Manager != nil = enabled and started. ExposeToAdmin = wire into admin when admin enabled.
TasksManager *task.Manager
TasksExposeToAdmin bool
// Tuning: non-nil = enabled. ExposeToAdmin = wire into admin when admin enabled.
Tuning *tuning.Tuning
TuningExposeToAdmin bool
// Log: LogLevelVar != nil = enabled. ExposeToAdmin = wire into admin when admin enabled.
LogLevelVar *slog.LevelVar
LogExposeToAdmin bool
// Lifecycle hooks and serve error observer.
OnStart []func(context.Context) error
OnShutdown []func(context.Context) error
OnServeError func(name string, err error, critical bool)
}
ServiceSpec configures NewDefaultService. All fields are optional.
Admin: nil = admin disabled. When non-nil, admin is either mounted on Primary (AdminMountPrefix, default "/-/") or served on its own server (AdminStandaloneServer). Mount and Standalone are mutually exclusive.
Tasks/Tuning/Log: non-nil Manager/Tuning/LevelVar = enabled and started/wired; nil + ExposeToAdmin or admin write need causes Service to create a default instance.
Override & ownership rules (when both ServiceSpec.<X> and ServiceSpec.Admin.<X> are set):
- Admin wiring uses Admin.<X> (Admin overrides Service-level source for the admin subtree).
- Service lifecycle ownership is controlled by ServiceSpec fields:
- TasksManager: when non-nil, Service starts/shuts down the task manager; when nil, Service does not manage it (even if Admin.TaskManager is set to expose tasks endpoints).
- Tuning/LogLevelVar: not started/stopped by Service; they are data sources only.
type TokenOption ¶ added in v0.1.2
type TokenOption = admin.TokenOption
TokenOption configures token-based guards (e.g. header name).
func WithTokenHeader ¶ added in v0.1.2
func WithTokenHeader(name string) TokenOption
WithTokenHeader overrides the token header name for token-based guards.
type TokenSetLike ¶ added in v0.1.2
type TokenSetLike = admin.TokenSetLike
TokenSetLike is a token set for hot-update guards (e.g. HotTokens).
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package admin assembles a secure, explicit, side-loadable admin subtree (http.Handler).
|
Package admin assembles a secure, explicit, side-loadable admin subtree (http.Handler). |
|
Package httpx provides small, stable and standard-library flavored net/http helpers.
|
Package httpx provides small, stable and standard-library flavored net/http helpers. |
|
client
Package client provides a small, stable and standard-library flavored HTTP client builder.
|
Package client provides a small, stable and standard-library flavored HTTP client builder. |
|
Package ops provides small, standard-library flavored net/http handlers for operational endpoints.
|
Package ops provides small, standard-library flavored net/http handlers for operational endpoints. |
|
rt
|
|
|
safego
Package safego provides helpers for running functions with panic/error reporting.
|
Package safego provides helpers for running functions with panic/error reporting. |
|
task
Package task provides small, standard-library flavored background task primitives.
|
Package task provides small, standard-library flavored background task primitives. |
|
tuning
Package tuning provides runtime-tunable parameters for online hot changes.
|
Package tuning provides runtime-tunable parameters for online hot changes. |
|
tuning/tuningslog
Package tuningslog provides small helpers to bind tuning variables to log/slog.
|
Package tuningslog provides small helpers to bind tuning variables to log/slog. |