lib-commons is Lerian's shared Go toolkit for service primitives, connectors, HTTP/server utilities, security, resilience, tenant-manager primitives, outbox, DLQ, certificate, JWT, and transaction helpers.
The current API surface is published on the v5 minor line. The v5 split-library line intentionally extracts observability/logging/runtime instrumentation to lib-observability, runtime configuration to lib-systemplane, and CloudEvents/Kafka streaming to lib-streaming.
Migrating from older packages?
Use the library boundary table below as the canonical direction for renamed, redesigned, removed, or extracted APIs in the split-library lib-commons line. Observability, logging, runtime, and assertion APIs are no longer exposed from lib-commons; import the owning library directly.
- Go
1.26.3or newer
go get github.com/LerianStudio/lib-commons/v6Lerian's shared platform code is split across four libraries:
| Library | Ownership |
|---|---|
github.com/LerianStudio/lib-commons |
Core helpers, connectors, HTTP/server utilities, security, resilience, tenant-manager primitives, outbox, DLQ, certificate, JWT, transaction helpers |
github.com/LerianStudio/lib-observability |
Logging, zap adapter, tracing, metrics, redaction, panic instrumentation, assertions, observability constants |
github.com/LerianStudio/lib-systemplane |
Runtime configuration, hot reload, systemplane admin routes, tenant-scoped runtime knobs, systemplane contract tests |
github.com/LerianStudio/lib-streaming |
CloudEvents/Kafka streaming, event emitters, streaming DLQs, outbox replay for streaming events |
app.go:Launcherfor concurrent app lifecycle management withNewLauncher(opts...)andRunAppoptionscontext.go: request-scoped logger/tracer/metrics/header-id tracking viaContextWith*helpers, safe timeout withWithTimeoutSafe, span attribute propagationerrors.go: standardized business error mapping withValidateBusinessErrorutils.go: UUID generation (GenerateUUIDv7returns error), struct-to-JSON, map merging, CPU/memory metrics, internal service detectionstringUtils.go: accent removal, case conversion, UUID placeholder replacement, lowercase hexadecimal SHA-256 hashing for strings (HashSHA256) and byte slices (HashSHA256Bytes), server address validationtime.go: date/time validation, range checking, parsing with end-of-day supportos.go: environment variable helpers (GetenvOrDefault,GetenvBoolOrDefault,GetenvIntOrDefault,GetenvDurationOrDefault), struct population from env tags viaSetConfigFromEnvVarscommons/constants: shared constants for datasource status, errors, headers, metadata, pagination, transactions, and obfuscation values
Observability has moved to github.com/LerianStudio/lib-observability. Use that library directly for logging, zap adapters, tracing, metrics, redaction, panic instrumentation, assertions, and observability constants.
The former commons/opentelemetry, commons/opentelemetry/metrics, commons/opentelemetry/constants, commons/opentelemetry/redaction, commons/log, commons/zap, commons/runtime, and commons/assert packages have been removed from lib-commons/v5. Consumers must import github.com/LerianStudio/lib-observability/{log,zap,assert,runtime,tracing,metrics,constants,redaction} directly.
commons/postgres:Config-based constructor (New),Resolver(ctx)for dbresolver access,Primary()for raw*sql.DB,NewMigratorfor schema migrations, backoff-based lazy-connect; dual-driver SQLSTATE error classification that unwraps both pgx (*pgconn.PgError) and lib/pq (*pq.Error) through wrapped chains viaerrors.As— accessorsSQLState(err) (string, bool)/Constraint(err) (string, bool)/DriverMessage(err) (string, bool)and predicatesIsUniqueViolation(23505) /IsForeignKeyViolation(23503) /IsCheckViolation(23514) /IsUndefinedTable(42P01); all nil-safe (nil or non-driver errors classify false / report absent)commons/mongo:Config-based client with functional options (NewClient), URI builder (BuildURI),Client(ctx)/ResolveClient(ctx)for access,EnsureIndexes(variadic), TLS support, credential clearingcommons/redis: topology-basedConfig(standalone/sentinel/cluster), GCP IAM auth with token refresh, distributed locking viaLockManagerinterface (NewRedisLockManager,LockHandle),SetPackageLoggerfor diagnostics, pool controls includingConnectionOptions.MaxActiveConns, TLS defaults to a TLS1.2 minimum floor withAllowLegacyMinVersionas an explicit temporary compatibility override, and TLS without a custom CA uses the host system trust storecommons/rabbitmq: connection/channel/health helpers for AMQP with*Context()variants,HealthCheck() (bool, error),Close()/CloseContext(), confirmable publisher with broker acks and auto-recovery, DLQ topology utilities, and health-check hardening (AllowInsecureHealthCheck,HealthCheckAllowedHosts,RequireHealthCheckAllowedHosts)commons/dlq: Redis-backed dead letter queue withNew(conn, keyPrefix, maxRetries, opts...)returning nil when conn is nil (all methods guard nil receiver viaErrNilHandler); key operations:Enqueue(RPush, stampsCreatedAt/MaxRetrieson first enqueue),Dequeue(LPop, at-most-once),QueueLength,ScanQueues(non-blocking SCAN for background consumers without tenant context),PruneExhaustedMessages(dequeue-discard-reenqueue cycle up to limit),ExtractTenantFromKey; tenant-scoped Redis keys ("<prefix><tenantID>:<source>"), backoff via exponential-with-jitter (base 30s, floor 5s, AWS Full Jitter); functional optionsWithLogger/WithTracer/WithMetrics/WithModule;DLQMetricsinterface (RecordRetried/RecordExhausted, nil-safe);NewConsumer(handler, retryFn, opts...) (*Consumer, error)for background poll loop —Run(ctx)blocks until stop,Stop()idempotent,ProcessOnce(ctx)exported for tests; consumer optionsWithConsumerLogger/WithConsumerTracer/WithConsumerMetrics/WithConsumerModule/WithPollInterval/WithBatchSize/WithSources; sentinel errorsErrNilHandler,ErrNilRetryFunc,ErrMessageExhausted- Streaming has moved to
github.com/LerianStudio/lib-streaming; runtime configuration has moved togithub.com/LerianStudio/lib-systemplane.
commons/net/http: Fiber HTTP helpers -- response (Respond/RespondStatus/RespondError/RespondErrorEnvelope/RenderError;RespondErrorEnvelopepreserves a caller-supplied status code and machine-readable error envelope), health (Ping/HealthWithDependencies), SSRF-protected reverse proxy (ServeReverseProxywithReverseProxyPolicy), pagination (offset/opaque cursor/timestamp cursor/sort cursor), validation (ParseBodyAndValidate/ValidateStruct/ValidateSortDirection/ValidateLimit), context/ownership (ParseAndVerifyTenantScopedID/ParseAndVerifyResourceScopedID), middleware (WithHTTPLogging/WithGrpcLogging/WithCORS/WithBasicAuth/NewTelemetryMiddleware),FiberErrorHandlercommons/net/http/ratelimit: Redis-backed distributed rate limiting middleware for Fiber —New(conn, opts...)returns a*RateLimiter(nil when disabled, nil-safe for pass-through),WithDefaultRateLimit(conn, opts...)as a one-liner that wiresNew+DefaultTierinto a ready-to-usefiber.Handler, fixed-window counter via atomic Lua script (INCR + PEXPIRE),RedisStorage.Increment(ctx,key,window)as the storage-only atomic primitive,WithRateLimit(tier)for static tiers,WithDynamicRateLimit(TierFunc)for per-request tier selection,MethodTierSelectorfor write-vs-read split, preset tiers (DefaultTier/AggressiveTier/RelaxedTier) configurable via env vars, identity extractors (IdentityFromIP/IdentityFromHeader/IdentityFromIPAndHeader— uses#separator to avoid conflict with IPv6 colons), fail-open/fail-closed policy,WithOnLimitedcallback,WithExceededHandlerfor caller-controlled 429 response bodies after standard rate-limit headers are set, and standardX-RateLimit-*/Retry-Afterheaders; also exportsRedisStorage(NewRedisStorage) for use with third-party Fiber middlewarecommons/net/http/idempotency: atomic at-most-once request middleware for Fiber — the shippedNew(conn, opts...) *Middlewarego-redis API remains fail-open by default and returns nil for a nil connection;NewWithStore(store, opts...) *Middlewareaccepts a backend-neutralStoreand always fails closed on a missing/errored backend;NewRedisStore(conn)exposes the built-in Redis adapter for store-contract composition;Storepreserves middleware-owned opaque bytes through only atomicAcquire, compare-safeComplete, and compare-safeRelease, with reusable adapter contract tests inidempotency/idempotencytest.Run; applies only to mutating methods (POST/PUT/PATCH/DELETE), passes GET/HEAD/OPTIONS unconditionally; readsIdempotency-Key(missing key passes through); key length defaults to 256 UTF-8 bytes; duplicate outcomes are matching exact response replay (status, content type, body, and multi-value headers) withIdempotency-Replayed: true, matching in-flight → 409IDEMPOTENCY_CONFLICTplusRetry-After: 1, and different method/path/body → 422IDEMPOTENCY_KEY_REUSE; an exact response that cannot be captured, encoded, persisted, or decoded fails closed with 503IDEMPOTENCY_UNAVAILABLEinstead of fabricating success; successful responses and handler failure/5xx releases are owner-compared so an expired acquisition cannot overwrite or delete its replacement; 4xx responses are cached by default and may instead release ownership viaWithClientErrorPolicy(ClientErrorPolicyRelease); request-specific retention is available throughWithTTLProvider; sensitive replay payloads can use authenticated encryption throughWithResponseCodec;WithMaxBodyCachebounds the raw response and encoded output is bounded to twice that value; tenant-scoped keys remain"<prefix><tenantID>:<idempotencyKey>"; rejection bodies remain customizable throughWithRejectedHandler,WithUnavailableHandler,WithConflictHandler, andWithKeyReuseHandlercommons/net/http/pacing: Redis-backed distributed pacing for OUTBOUND calls — anhttp.RoundTripper, not inbound middleware, so redirects and client retries are each paced.NewPacer(conn, prefix, opts...) (*Pacer, error)owns the evaluation script and the retry timing;NewRoundTripper(next, pacer, BucketsFunc) (*RoundTripper, error)wires it into anhttp.Client(nilnextfalls back tohttp.DefaultTransport). Buckets are built byTenantBucket(id, RateProvider)— canonicalized throughtenant-manager/core.CanonicalTenantID, so dashed and dashless UUID spellings collapse onto one budget, and slugs plusdefaultare accepted — andInstitutionBucket(id, RateProvider), validated for identifier grammar only and namespaced separately.Pacer.Acquire(ctx, buckets...)charges every supplied bucket in ONE Lua evaluation or charges none, so a tenant permit is never burned while an institution bucket blocks; a refusal writes no bucket at all.RateProvider func(ctx) (float64, error)is read on every wait, so a rate changed at runtime applies without a restart; 0 pauses the bucket until it turns positive or the context ends, and a rate aboveWithMaxRate(defaultDefaultMaxRate= 1000/s) or below one call per day is refused. Emission spacing is GCRA with a burst of one — a bucket stores its LAST GRANT and the next admission is derived from the interval in force at evaluation time, so a raised rate shortens an in-flight wait and a lowered rate cannot be expired by a shorter key lifetime. Time comes from the RedisTIMEcommand, never a local clock, and a per-prefix high-water mark refuses evaluation when the backend clock moves backwards. Keys arepacing:{<prefix>}:{tenant|inst}:<sha256-8-hex>— the brace is a Redis Cluster hash tag keeping one EVAL in one slot, and the identity is digested so no key, error, log field, or span attribute carries it. Everything fails closed:ErrPacerUnavailable,ErrInvalidPrefix,ErrInvalidIdentity,ErrNoBuckets,ErrDuplicateBucket,ErrInvalidRate,ErrInvalidPollInterval,ErrRateUnavailable,ErrBackendUnavailable,ErrClockWentBackwards,ErrWaitAborted(also wraps the context error). There is no fail-open mode and no burst option. Options:WithMaxRate,WithPollInterval(defaultDefaultPollInterval= 250ms, which also bounds how often rates are re-read),WithLoggercommons/webhook: outbound webhook delivery withNewDeliverer(lister, opts...) *Delivererreturning nil when lister is nil (bothDeliver/DeliverWithResultsguard nil receiver);Deliver(ctx, *Event) errorfans out to all active endpoints concurrently, returns errors only for pre-flight failures (nil receiver, nil event, listing failure) — per-endpoint failures are logged and metricked but do not propagate;DeliverWithResults(ctx, *Event) []DeliveryResultreturns per-endpoint outcomes for callers needing individual results; SSRF protection viaresolveAndValidateIP: single DNS lookup validates all resolved IPs against private/loopback/link-local/CGNAT/RFC-reserved ranges then pins URL to first resolved IP (eliminates DNS rebinding TOCTOU);WithAllowPrivateNetwork()only relaxes blocking for explicit private/loopback IP-literal URLs (for example127.0.0.1,10.0.0.5) when local/development tier allows it orALLOW_WEBHOOK_PRIVATE_NETWORKsupplies an explicit override reason; hostnames resolving to private IPs remain blocked; redirects blocked entirely to prevent 302-to-internal bypass; HMAC-SHA256 signing viaX-Webhook-Signature: sha256=<hex>over raw payload (timestamp not included — replay protection is the receiver's responsibility); encrypted secrets viaSecretDecryptorfunc (receives ciphertext withenc:prefix stripped, no decryptor + encrypted secret = fail-closed); retry with exponential backoff+jitter (base 1s), non-retryable on 4xx except 429; concurrency capped by semaphore (default 20);EndpointListerinterface (ListActiveEndpoints),DeliveryMetricsinterface (RecordDelivery); functional optionsWithLogger/WithTracer/WithMetrics/WithMaxConcurrency/WithMaxRetries/WithHTTPClient/WithSecretDecryptor/WithAllowPrivateNetwork; sentinel errorsErrNilDeliverer/ErrSSRFBlocked/ErrDeliveryFailed/ErrInvalidURLcommons/server:ServerManager-based graceful shutdown withWithHTTPServerfor Fiber,WithStdlibHTTPServerfor caller-owned*net/http.Server,WithStdlibHTTPListenerfor pre-bound stdlib listeners (stdlib HTTP variants are mutually exclusive with Fiber HTTP),WithGRPCServer/WithShutdownChannel/WithShutdownTimeout/WithShutdownHook,StartWithGracefulShutdown()/StartWithGracefulShutdownWithError(),ServersStarted()for test coordination
commons/certificate: thread-safe TLS certificate manager with hot reload —NewManager(certPath, keyPath string) (*Manager, error)loads PEM files at construction; both paths empty returns unconfigured manager (TLS optional), exactly one path →ErrIncompleteConfig; key file must have mode0600or stricter (checked before reading); PKCS#8 → PKCS#1 (RSA) → EC (SEC 1) key parsing order; full PEM chain parsed (allCERTIFICATEblocks, leaf first then intermediates);Rotate(cert *x509.Certificate, key crypto.Signer) erroratomically hot-reloads under write lock — validatesNotBefore/NotAftertemporal bounds and public-key match (ErrKeyMismatch) before swapping; read accessors (all nil-safe, read-locked):GetCertificate()/GetSigner()/PublicKey()/ExpiresAt()/DaysUntilExpiry(); TLS integration:TLSCertificate() tls.Certificatebuilds populated struct with full chain;GetCertificateFunc() func(*tls.ClientHelloInfo) (*tls.Certificate, error)for assignment totls.Config.GetCertificatefor transparent hot-reload; package-levelLoadFromFiles(certPath, keyPath string) (*x509.Certificate, crypto.Signer, error)for pre-flight validation without touching manager state; sentinel errorsErrNilManager/ErrCertRequired/ErrKeyRequired/ErrExpired/ErrNoPEMBlock/ErrKeyParseFailure/ErrNotSigner/ErrKeyMismatch/ErrIncompleteConfigcommons/circuitbreaker:Managerinterface with error-returning constructors (NewManager),TenantAwareManagertenant/service overloads for isolated per-tenant breakers (tenant-aware methods require non-empty valid tenant IDs; legacyManagermethods are the no-tenant/process-wide path),NewPassthroughManager/NewPassthroughTenantAwareManagerfor feature-flagged bypass while preserving validation contracts, config validation, preset configs (DefaultConfig/AggressiveConfig/ConservativeConfig/HTTPServiceConfig/DatabaseConfig), health checker (NewHealthCheckerWithValidation), metrics viaWithMetricsFactoryusingtenant_hashfor tenant-aware breakers instead of raw tenant IDs while preserving the legacy no-tenant metric label setcommons/backoff: exponential backoff with jitter (ExponentialWithJitter) and context-aware sleep (WaitContext)commons/errgroup: error-group concurrency with panic recovery (WithContext,Go,Wait), configurable logger viaSetLoggercommons/safe: panic-safe math (Divide/DivideRound/Percentageondecimal.Decimal,DivideFloat64), regex with caching (Compile/MatchString/FindString), slices (First/Last/Atwith*OrDefaultvariants)commons/security: sensitive field detection (IsSensitiveField), default field lists (DefaultSensitiveFields/DefaultSensitiveFieldsMap)
commons/transaction: intent-based transaction planning (BuildIntentPlan), balance eligibility validation (ValidateBalanceEligibility), posting flow (ApplyPosting), operation resolution (ResolveOperation), typed domain errors (NewDomainError)commons/outbox: transactional outbox contracts, dispatcher, sanitizer, and tenant-aware persistence adapters. PostgreSQL supports pool-per-tenant, schema-per-tenant, and column-per-tenant. A pool-per-tenant service with generic and module databases wraps its existing resolvers withpostgres.NewModulePoolResolver(genericResolver, defaultTenantID, loadConfig, postgres.ModulePool{Name: "consignado", Resolver: consignadoResolver}), then passes the result asMultiTenantConfig.PoolResolver.TenantDispatchScopeidentity is the exact(real TenantID, opaque PoolKey)pair: handlers always receive the real tenant, table-presence cache entries cannot leak between generic and module pools, and physical databases with the same canonical host/port/database/schema are scanned once. Empty scopes back off toColdDispatchInterval(one minute by default, configured withWithColdDispatchInterval) while active and recently active scopes retainDispatchInterval. Module topology is cached and refreshed by one caller per interval (one minute by default); useNewModulePoolResolverWithConfigto alignModulePoolResolverConfig.TopologyRefreshIntervalwith a service-specific cold interval. A failed refresh may use last-known-good topology under the existing fail-open contract, but every failure is retried after the interval and a failed first ownership lookup is never made permanent.ModulePoolResolver.InvalidateTopology()forces the next enumeration to refresh after tenant additions or topology changes.ModulePoolResolver.EvictTenant(tenantID)removes every scope immediately and forces refresh after removal or suspension; stale in-flight refreshes cannot restore evicted scopes. Newly committed/retryable/stuck rows remain governed by dispatcher cold-scope polling, which is unchanged. LegacyTenantPoolResolverimplementations and directManagerPoolResolverwiring remain one-scope-per-tenant and unchanged. MongoDB retains row-scoped tenants plus optional module database resolution throughmongo.WithModule/mongo.WithTenantDatabaseResolver. Tenant-aware repositories returnErrInvalidTenantIDfor IDs rejected bytenant-manager/core.IsValidTenantID. PostgreSQL additionally implements the optionaloutbox.TransactionalBatchWritercontract:CreateManyWithTx(ctx, tx, events)validates the full batch before issuing one set-wiseINSERT, returns rows in input order, and treats an empty batch as a no-op.commons/crypto: hashing (GenerateHash) and symmetric encryption (InitializeCipher/Encrypt/Decrypt) with credential-safefmtoutput (String()/GoString()redact secrets)commons/jwt: HS256/384/512 JWT signing (Sign), signature verification (Parse), combined signature + time-claim validation (ParseAndValidate), standalone time-claim validation (ValidateTimeClaims/ValidateTimeClaimsAt)commons/license: license validation with functional options (New(opts...),WithLogger,WithFailClosed), fail-closed default termination (Terminateexits with code 1 unless a custom handler is configured), handler management (SetHandler), error-returning validation (TerminateWithError/TerminateSafe)commons/pointers: pointer conversion helpers (String,Bool,Time,Int,Int64,Float64)commons/cron: cron expression parser (Parse) and scheduler (Schedule.Next)commons/secretsmanager: AWS Secrets Manager M2M and external credential retrieval viaGetM2MCredentials/GetExternalCredentials; version-addressed external credentials use the opaqueExternalCredentialReferencecapability, created byBuildExternalSecretVersionReferenceor parsed from storage withParseExternalCredentialReference(reference, trustedScope)beforeGetExternalCredentialsByReference; canonical UUID-versioned SecretIds (tenants/{env?}/{tenant}/{app}/external/{target}/credentials/versions/{uuid}), exact scope binding, strict input validation, typed retrieval errors, non-null string-only JSON objects, and theSecretsManagerClienttest seam
commons/tenant-manager/core: shared tenant types, context helpers (ContextWithTenantID,GetTenantIDFromContext), and tenant-manager error contractscommons/tenant-manager/cache: exported tenant-config cache contract (ConfigCache),ErrCacheMiss, and in-memory cache implementation used by the HTTP clientcommons/tenant-manager/client: Tenant Manager HTTP client with circuit breaker, cache options (WithCache,WithCacheTTL,WithSkipCache), cache invalidation, and response hardeningcommons/tenant-manager/consumer: dynamic multi-tenant queue consumer lifecycle management with tenant discovery, sync, retry, and per-tenant handlerscommons/tenant-manager/event: canonical tenant lifecycle dispatcher. Module-aware services register every PostgreSQL manager withWithPostgresManagersand every MongoDB manager withWithMongoManagers; removal events close all registered pools, while connection-setting events route only to the PostgreSQL manager matching the payload module. Existing singular options remain supported.commons/tenant-manager/middleware: Fiber middleware for tenant extraction, upstream auth assertion checks, and tenant-scoped DB resolutioncommons/tenant-manager/postgres: tenant-scoped PostgreSQL connection manager with LRU eviction, async settings revalidation, pool controls, andModule()for canonical lifecycle routing (""identifies the generic resource)commons/tenant-manager/mongo: tenant-scoped MongoDB connection manager with LRU eviction and idle-timeout controlscommons/tenant-manager/rabbitmq: tenant-scoped RabbitMQ connection manager with soft connection-pool limits and evictioncommons/tenant-manager/s3: tenant-prefixed S3/object storage.NewStoragepreserves the general upload/create/download/delete/list API.NewRetainedStorageadds immutable version custody:CreateRetainedperforms an atomicIf-None-Match: *write with explicit COMPLIANCE retention, canonicalizes retain-until to S3's whole-second UTC precision, and returns exact-version metadata;DownloadVersionandStatVersionrequire aVersionID;ValidateDefaultRetentionfails closed unless Object Lock is enabled with COMPLIANCE retention of at least five years (Years >= 5orDays >= 1827).NewRecoverableRetainedStorageadds deterministic create-or-recover for callers that can grants3:ListBucketVersions: after a duplicate or ambiguous PUT timeout, it uses a detached, bounded lookup and returns only one exact-key version that is both sole and latest, has a non-empty exactVersionID, remains under COMPLIANCE retention, and exactly matches the caller's expected content type, content length, and canonical retain-until time. Missing permission, multiple versions, a latest delete marker, truncated/empty listings, or metadata drift fail closed. Payload digest verification remains the caller's responsibility. The retained surfaces expose no delete or retention-bypass operation.commons/tenant-manager/valkey: tenant-prefixed Redis/Valkey key and pattern helpers with delimiter validation
commons/shell/: Makefile include helpers (makefile_colors.mk,makefile_utils.mk), shell scripts (colors.sh,ascii.sh), ASCII art (logo.txt)
import (
"github.com/LerianStudio/lib-commons/v5/commons"
)
func newRequestID() (string, error) {
id, err := commons.GenerateUUIDv7()
if err != nil {
return "", err
}
return id.String(), nil
}The following environment variables are recognized by lib-commons or by canonical sibling libraries that lib-commons integrates with. Observability variables are owned by lib-observability.
| Variable | Type | Default | Package | Description |
|---|---|---|---|---|
VERSION |
string |
"NO-VERSION" |
commons |
Application version, printed at startup by InitLocalEnvConfig |
ENV_NAME |
string |
"local" |
commons |
Environment name; when "local", a .env file is loaded automatically |
ENV |
string |
(none) | lib-observability/assert |
When set to "production", stack traces are omitted from assertion failures |
GO_ENV |
string |
(none) | lib-observability/assert |
Fallback production check (same behavior as ENV) |
LOG_LEVEL |
string |
"debug" (dev/local) / "info" (other) |
lib-observability/zap |
Log level override (debug, info, warn, error); Config.Level takes precedence if set |
LOG_ENCODING |
string |
"console" (dev/local) / "json" (other) |
lib-observability/zap |
Log output format: "json" for structured JSON, "console" for human-readable colored output |
LOG_OBFUSCATION_DISABLED |
bool |
false |
commons/net/http |
Set to "true" to disable sensitive-field obfuscation in HTTP access logs (not recommended in production) |
METRICS_COLLECTION_INTERVAL |
duration |
"5s" |
commons/net/http |
Background system-metrics collection interval (Go duration format, e.g. "10s", "1m") |
ACCESS_CONTROL_ALLOW_CREDENTIALS |
bool |
"false" |
commons/net/http |
CORS Access-Control-Allow-Credentials header value |
ACCESS_CONTROL_ALLOW_ORIGIN |
string |
"*" |
commons/net/http |
CORS Access-Control-Allow-Origin header value |
ACCESS_CONTROL_ALLOW_METHODS |
string |
"POST, GET, OPTIONS, PUT, DELETE, PATCH" |
commons/net/http |
CORS Access-Control-Allow-Methods header value |
ACCESS_CONTROL_ALLOW_HEADERS |
string |
"Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization" |
commons/net/http |
CORS Access-Control-Allow-Headers header value |
ACCESS_CONTROL_EXPOSE_HEADERS |
string |
"" |
commons/net/http |
CORS Access-Control-Expose-Headers header value |
RATE_LIMIT_ENABLED |
bool |
"false" |
commons/net/http/ratelimit |
Explicit opt-in: set to "true" to enable rate limiting. When unset or falsy, New returns nil and all requests pass through |
RATE_LIMIT_MAX |
int |
500 |
commons/net/http/ratelimit |
Maximum requests per window for DefaultTier |
RATE_LIMIT_WINDOW_SEC |
int |
60 |
commons/net/http/ratelimit |
Window duration in seconds for DefaultTier |
AGGRESSIVE_RATE_LIMIT_MAX |
int |
100 |
commons/net/http/ratelimit |
Maximum requests per window for AggressiveTier |
AGGRESSIVE_RATE_LIMIT_WINDOW_SEC |
int |
60 |
commons/net/http/ratelimit |
Window duration in seconds for AggressiveTier |
RELAXED_RATE_LIMIT_MAX |
int |
1000 |
commons/net/http/ratelimit |
Maximum requests per window for RelaxedTier |
RELAXED_RATE_LIMIT_WINDOW_SEC |
int |
60 |
commons/net/http/ratelimit |
Window duration in seconds for RelaxedTier |
RATE_LIMIT_REDIS_TIMEOUT_MS |
int |
500 |
commons/net/http/ratelimit |
Timeout in milliseconds for Redis operations; exceeded requests follow fail-open/fail-closed policy |
SECURITY_ENFORCEMENT |
bool |
false |
commons |
Enables hard enforcement for configured security-tier checks that otherwise warn during migration phases |
ALLOW_INSECURE_OTEL |
string |
"" |
lib-observability/tracing |
Justification override that allows insecure OTEL exporter endpoints in strict tier |
ALLOW_WEBHOOK_PRIVATE_NETWORK |
string |
"" |
commons/webhook |
Justification override that enables WithAllowPrivateNetwork outside permissive tier for explicit private IP-literal webhook targets |
OTEL_EXPORTER_OTLP_ENDPOINT |
string |
(none) | lib-observability/tracing |
General OTLP endpoint read by the OTel SDK; bare host:port values are normalized to http://host:port |
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT |
string |
(none) | lib-observability/tracing |
Traces-specific OTLP endpoint; bare host:port values are normalized to http://host:port |
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT |
string |
(none) | lib-observability/tracing |
Metrics-specific OTLP endpoint; bare host:port values are normalized to http://host:port |
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT |
string |
(none) | lib-observability/tracing |
Logs-specific OTLP endpoint; bare host:port values are normalized to http://host:port |
Additionally, commons.SetConfigFromEnvVars populates any struct using env:"VAR_NAME" field tags, supporting string, bool, integer types, time.Duration and []string. Consuming applications define their own variable names through these tags.
A field may carry an envDefault tag giving the value to use when its variable is unset, blank, or unparseable for the field's type:
type Config struct {
AuthEnabled bool `env:"PLUGIN_AUTH_ENABLED" envDefault:"true"`
Port int `env:"SERVER_PORT" envDefault:"8080"`
Timeout time.Duration `env:"REQUEST_TIMEOUT" envDefault:"30s"`
Origins []string `env:"CORS_ALLOWED_ORIGINS" envDefault:"https://app.example.com"`
}An explicit, non-blank, parseable value always wins, including false — the default only fills a gap, it does not override an operator.
A value that is present but unparseable for the field's type takes the default instead, and GetenvBoolOrDefault/GetenvIntOrDefault/GetenvDurationOrDefault warn to stderr when they do. That predates this tag and is deliberately unchanged: the alternative is refusing to boot on a typo in a variable the field has a working default for. It does mean PLUGIN_AUTH_ENABLED=flase yields true here rather than an error — so a guard that must reject an explicitly disabled value in production belongs in a validator that reads the raw variable, not in the default.
Without the tag a field takes its zero value, and for a bool that is false. A flag that must be ON unless an operator turns it off therefore MUST declare the default; relying on the variable being present ships the feature OFF to whoever forgets it. envDefault is the only accepted spelling — default is read by nothing, and a tag that is silently ignored is worse than no tag, because a reviewer sees it and passes.
An envDefault the field's type cannot hold — envDefault:"maybe" on a bool, or envDefault:"999" on an int8 — returns ErrInvalidDefaultValue at load time rather than falling back to zero. A default that does not apply is indistinguishable from no default at all, which is the failure mode this tag exists to remove.
A time.Duration field takes a value with a unit — 30s, 2m, 720h, 150ms — in both the environment variable and the envDefault tag. It is matched on its type, ahead of the integer types, because time.Duration is defined as an int64: a switch on reflect kind cannot tell the two apart, so until this was handled explicitly envDefault:"30s" failed the load outright and envDefault:"30" silently meant thirty nanoseconds.
A unit-less integer remains a nanosecond count, matching time.Duration's own numeric meaning and this loader's historical reading. Deployed configuration relies on it — a Helm value of "2000000000" means two seconds — so re-reading a bare integer as seconds would silently stretch a two-second timeout to roughly 63 years. Write the unit; the unit-less spelling is legacy that keeps working.
commons.GetenvDurationOrDefault(key, fallback) applies the same parsing to a single variable, for code that reads one value rather than populating a struct.
make build-- build all packagesmake ci-- run the local fix + verify pipeline (lint-fix,format,tidy,check-tests,sec,vet,test-unit,test-integration)make clean-- clean build artifacts and cachesmake tidy-- clean dependencies (go mod tidy)make format-- format code with gofmtmake help-- display all available commands
make test-- run unit tests (uses gotestsum if available)make test-unit-- run unit tests excluding integrationmake test-integration-- run integration tests with testcontainers (requires Docker)make test-all-- run all tests (unit + integration)
make coverage-unit-- unit tests with coverage report (respects.ignorecoverunit)make coverage-integration-- integration tests with coveragemake coverage-- run all coverage targets
make lint-- run lint checks (read-only)make lint-fix-- auto-fix lint issuesmake vet-- rungo veton all packagesmake sec-- run security checks using gosec (make sec SARIF=1for SARIF output)make check-tests-- verify test coverage for packages
LOW_RESOURCE=1-- reduces parallelism and disables race detector for constrained machinesRETRY_ON_FAIL=1-- retries failed tests onceRUN=<pattern>-- filter integration tests by name patternPKG=<path>-- filter to specific package(s)
make setup-git-hooks-- install and configure git hooksmake check-hooks-- verify git hooks installationmake check-envs-- check hooks + environment file security
make tools-- install test tools (gotestsum)make goreleaser-- create release snapshot
For coding standards, architecture patterns, testing requirements, and development guidelines, see docs/PROJECT_RULES.md.
This project is licensed under the terms in LICENSE.