Platform endpoints
go.putnami.dev/platform mounts the standard operational HTTP surface every workload needs — liveness, readiness, version, and (optionally) pprof — and auto-discovers per-plugin probes from the application's module tree.
It replaces the per-workload boilerplate (and the legacy http.NewHealthPlugin()'s single /_/health endpoint) with one plugin you opt in to.
Plugin setup
import (
"go.putnami.dev/app"
"go.putnami.dev/http"
"go.putnami.dev/platform"
)
server := http.NewServerPlugin(http.ServerConfig{Port: 8080})
platformPlugin := platform.NewPlugin(platform.Config{
Version: platform.VersionInfo{Name: "my-service", Version: "1.0.0"},
})
platformPlugin.RegisterOn(server)
a := app.New("my-service").
Use(server).
Use(platformPlugin)
a.ListenAndServe()Endpoints
| Path | Purpose |
|---|---|
/livez |
Lightweight liveness. Returns 200 {"status":"ok"} whenever the handler can run. No probes, no flags. Safe to use as a Kubernetes liveness probe. |
/healthz |
Liveness aggregate. 200 when running and every app.HealthChecker probe passes. 503 {"status":"unavailable"} before Start and after Stop. 503 {"status":"degraded","checks":{…}} when any probe fails. |
/readyz |
Readiness aggregate. Same shape as /healthz, driven by app.ReadinessChecker probes. The endpoint to use as a Kubernetes readiness probe. |
/version |
Build metadata as JSON. Caller-supplied VersionInfo wins; empty fields fall back to runtime/debug.ReadBuildInfo. |
/debug/pprof/* |
net/http/pprof index, named profiles (heap, goroutine, allocs, …) and on-demand collectors (profile, trace, cmdline, symbol). Disabled by default. |
Default mount is root. Set Config.Prefix to namespace (Prefix: "/_" → /_/healthz, …).
Capability interfaces
The platform plugin discovers two interfaces by walking the module tree from the root and registers each implementation under its Name().
// in go.putnami.dev/app
type HealthChecker interface {
Plugin
CheckHealth(ctx context.Context) error
}
type ReadinessChecker interface {
Plugin
CheckReadiness(ctx context.Context) error
}| Interface | Semantics | Failure consequence (k8s) |
|---|---|---|
HealthChecker |
Dependency is broken in a way only a restart fixes (liveness) | Pod restart |
ReadinessChecker |
Dependency is temporarily down — drain traffic but don't restart | Traffic drained, no restart |
A single plugin can implement both: CheckHealth contributes to /healthz, CheckReadiness contributes to /readyz.
Implementations must be safe to call concurrently and respect context cancellation. The plugin enforces a per-request Config.ProbeTimeout (default 5s) so a hung probe can't stall the endpoint.
Built-in contributor: database.Plugin implements app.HealthChecker — pool.Ping shows up under the database key on /healthz automatically when both plugins are mounted.
Explicit probes
For probes not owned by a plugin (an external URL, an ad-hoc check), register directly:
platformPlugin.AddHealthChecker("upstream", func(ctx context.Context) error {
return checkUpstreamURL(ctx, "https://example.com/health")
})
platformPlugin.AddReadinessChecker("warm", func(ctx context.Context) error {
if !cacheWarmed.Load() {
return errors.New("cache warming")
}
return nil
})Explicit registrations win over auto-discovered probes of the same name — useful for overriding a plugin-provided probe in a specific environment.
Configuration
type Config struct {
Prefix string // path prefix (default: "" → root)
EnablePprof bool // expose /debug/pprof/* (default: false)
Version VersionInfo // /version payload
ProbeTimeout time.Duration // per-probe timeout (default: 5s)
}
type VersionInfo struct {
Name string `json:"name,omitempty"`
Version string `json:"version,omitempty"`
SHA string `json:"sha,omitempty"`
Branch string `json:"branch,omitempty"`
BuildTime string `json:"buildTime,omitempty"`
}Build metadata
For production builds, embed version info at link time:
var version = "dev"
var commit = ""
platform.NewPlugin(platform.Config{
Version: platform.VersionInfo{
Name: "my-service",
Version: version,
SHA: commit,
},
})Build with go build -ldflags "-X main.version=1.2.3 -X main.commit=$(git rev-parse HEAD)". Leaving Version empty falls back to runtime/debug.ReadBuildInfo (module path, vcs.revision, vcs.time when built with -buildvcs).
pprof
/debug/pprof/* exposes Go's runtime profiler. Off by default — profiles reveal heap layout and goroutine details, and the CPU/trace collectors are expensive.
platform.NewPlugin(platform.Config{EnablePprof: true})Do not expose pprof on a public port. Mount the platform plugin on a separate admin server, or restrict access via a sidecar / network policy.
Migrating from http.NewHealthPlugin()
The legacy http.HealthPlugin (single /_/health endpoint) still works and now also auto-discovers app.HealthChecker implementations. To get the full operational surface:
// before
a.Use(server).Use(http.NewHealthPlugin())
// after
platformPlugin := platform.NewPlugin(platform.Config{Prefix: "/_"}) // keep /_ namespace
platformPlugin.RegisterOn(server)
a.Use(server).Use(platformPlugin)/_/health becomes /_/healthz, and you also get /_/livez, /_/readyz, /_/version. Drop the Prefix for plain k8s-style paths (/healthz, /livez, …). Don't mount both — they overlap on probe registration.