Help us shape the future of Go! Take the 2026 Go Developer Survey

config

package
v0.3.3 Latest Latest
Warning

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

Go to latest
Published: Oct 26, 2026 License: AGPL-3.0 Imports: 18 Imported by: 0

Documentation

Overview

Package config loads and validates the Instance's configuration from the process environment, with the .env files layered under it.

Index

Constants

View Source
const EnvE2E = "e2e"

EnvE2E is the end-to-end profile: a non-production env whose per-policy rate limits default to disabled (0), so a polling test client never trips ADR 0018/0025 budgets. An explicit RATE_LIMIT_* variable still wins. It tolerates a missing SPHERICON_JWT_SECRET like development does, but is not IsDev: the dev DKIM lookup stays off and the harness injects its own.

Variables

View Source
var DefaultRateLimits = RateLimits{Human: 60, APIBurst: 20, APIPerMinute: 600, FailedAuth: 30, Tracking: 600, LoginFailures: 5, LoginIP: 20, Collect: 6000, CollectIP: 300, ServicePerMinute: 600, ForgotAddress: 3, ForgotIP: 10}

DefaultRateLimits are the production budgets.

Functions

func EncryptionKey

func EncryptionKey(envName string) (string, error)

EncryptionKey is SPHERICON_ENCRYPTION_KEY as the process sees it (a `_FILE` sibling included), without loading the rest of the config: the key commands need no database.

func Port

func Port(envName string) (string, error)

Port is PORT as the process sees it, without loading the rest of the config: the container health check needs no database and no secrets.

func SecretValues

func SecretValues(cfg *Config) []string

SecretValues lists every secret value of the process: the ones in cfg (nil when the load failed) and the ones still in the environment, plus the password inside DATABASE_URL, raw and as the URL spells it. `sphericon doctor` redacts them from everything it prints.

func ValidateJWTSecret

func ValidateJWTSecret(secret string) error

ValidateJWTSecret applies the production rule for SPHERICON_JWT_SECRET: present, long enough and not a documented placeholder. Load applies it outside development; `sphericon doctor` applies it everywhere.

Types

type BodyLimits

type BodyLimits struct {
	Default      int64
	Collect      int64
	CollectEvent int64
}

BodyLimits are the request body caps in bytes. /collect is the public tracking ingestion (its key ships in customer pages, so anyone can post to it): Collect caps a batch (POST /collect/events), CollectEvent one event (an identify body, or each event inside a batch). Default applies to every other surface.

type Config

type Config struct {
	DatabaseURL    string
	Port           string
	CollectSiteKey string
	BootstrapToken string
	CORSOrigins    []string
	JWTSecret      string
	// JWTSecretSecondary is the second JWT secret of a rotation (SPHERICON_JWT_SECRET_SECONDARY):
	// accepted when verifying a session or a mailed link, never used to sign. Empty outside one.
	JWTSecretSecondary string
	// SessionTTL is the lifetime of a site session: both the token's exp and the
	// cookie's Max-Age (SPHERICON_SESSION_TTL, default 24h, no refresh; ADR 0020).
	SessionTTL time.Duration
	AppURL     string
	// MCPURL is the public origin of a dedicated MCP host (SPHERICON_MCP_URL); empty means the
	// MCP resource lives on AppURL. Read it through MCPOrigin.
	MCPURL string
	// TrackerURL is the public origin the tracker snippet loads t.js from and posts /collect to
	// (SPHERICON_TRACKER_URL); empty means AppURL. Read it through TrackerOrigin.
	TrackerURL    string
	SMTPHost      string
	SMTPPort      int
	SMTPUser      string
	SMTPPass      string
	SMTPFrom      string
	EncryptionKey string
	// LicenseKey is the offline EE license key (ADR 0014). Empty runs the plain core.
	LicenseKey string
	// AccountLinkURL is the address of the external account page the Owner's Account
	// link opens (SPHERICON_ACCOUNT_LINK_URL). The link is enabled only with AccountLinkSigningKey too.
	AccountLinkURL string
	// AccountLinkSigningKey is the base64 Ed25519 seed that signs the Account link
	// token (SPHERICON_ACCOUNT_LINK_SIGNING_KEY).
	AccountLinkSigningKey string
	// StaffJWTSecret signs the Staff member session and login challenge (SPHERICON_STAFF_JWT_SECRET,
	// ADR 0026). It is required, and must differ from SPHERICON_JWT_SECRET, only when the license
	// has the `staff` feature; ee.New validates it there.
	StaffJWTSecret string
	// StaffSessionTTL is the lifetime of a Staff member session (SPHERICON_STAFF_SESSION_TTL,
	// default 4h, no refresh, no remember-me): shorter than a customer's.
	StaffSessionTTL time.Duration
	// StaffHoldMaxExpiry is how far ahead a service may set a Staff hold's expiry
	// (SPHERICON_STAFF_HOLD_MAX_EXPIRY, default 720h): it is refused beyond it.
	StaffHoldMaxExpiry time.Duration
	// AbuseVolumeFloor is the fewest sends in the 24-hour window, per Sending domain, for
	// the abuse detector to judge a rate (SPHERICON_ABUSE_VOLUME_FLOOR, default 1000; ADR 0026).
	AbuseVolumeFloor int
	// The abuse detector's limits, in percent (ADR 0026): a warning at the first pair, a
	// freeze at the second. Defaults: warn 0.1% complaints / 5% bounces, freeze 0.3% / 10%.
	AbuseWarnComplaintPercent, AbuseWarnBouncePercent     float64
	AbuseFreezeComplaintPercent, AbuseFreezeBouncePercent float64
	// SupportEmail is the support contact shown with a suspension and in the owner notices
	// (SPHERICON_SUPPORT_EMAIL): where an owner appeals. Empty shows none.
	SupportEmail string
	AutoMigrate  bool
	// MigrateTimeout bounds the lock wait and the run of a start with AutoMigrate on
	// (SPHERICON_DB_MIGRATE_TIMEOUT); 0 is no limit.
	MigrateTimeout time.Duration
	BodyLimits     BodyLimits
	RateLimits     RateLimits
	// OutboxFloor is the minimum age of a domain-event outbox row before the
	// prune job may delete it (SPHERICON_OUTBOX_RETENTION_FLOOR_DAYS, default 7; ADR 0019).
	OutboxFloor time.Duration
	// EventsRetention is the age past which analytical Events are deleted
	// (SPHERICON_EVENTS_RETENTION_DAYS, default 400, 0 disables; ADR 0019).
	EventsRetention time.Duration
	// EventLiveWindow is how old a live Event may be before the external API refuses it
	// as history that belongs in a Backfill (SPHERICON_EVENT_LIVE_WINDOW_HOURS, default 72, at least 1).
	EventLiveWindow time.Duration
	// UnverifiedUserRetention is the age past which an Unverified User with no Membership
	// is deleted (SPHERICON_UNVERIFIED_USER_RETENTION_DAYS, default 7, 0 disables; ADR 0030).
	UnverifiedUserRetention time.Duration
	// TestInboxRetention is how long a Test inbox keeps a Captured message
	// (SPHERICON_TEST_INBOX_RETENTION_DAYS, default 7, at least 1; ADR 0058).
	TestInboxRetention time.Duration
	// TestInboxMaxMessages is how many Captured messages a Workspace keeps, the newest ones
	// (SPHERICON_TEST_INBOX_MAX_MESSAGES, default 200, at least 1; ADR 0058).
	TestInboxMaxMessages int
	DBPool               DBPool
	// IsDev is true for non-production envs (development/test). Used to relax
	// production-only behaviour locally — e.g. the sending-domain DKIM re-check
	// trusts seeded domains instead of hitting real DNS (ADR 0010).
	IsDev bool
	// SMTPAllowPrivateHosts lets a Workspace's SMTP Integration point at a loopback or
	// private address and any port (SPHERICON_SMTP_ALLOW_PRIVATE_HOSTS). Off in production by
	// default, so a tenant cannot use the server to reach internal hosts; on in
	// development and tests, where the relay is a local Mailpit.
	SMTPAllowPrivateHosts bool
	// OutboundAllow names the internal host:port pairs an outbound call to a third-party URL
	// (a Webhook endpoint) may reach although the address is private (SPHERICON_OUTBOUND_ALLOW,
	// ADR 0039). Empty by default and never set in SaaS; a malformed value fails the load.
	OutboundAllow webhook.Allowlist

	// Locale is the instance-wide UI/email language (a SaaS deployment runs per
	// country). One of the supported locales; anything else is coerced to "en".
	// Drives both the SPA (injected into index.html) and the backend's own
	// system emails and validation messages (internal/i18n).
	Locale string

	// Telemetry is SPHERICON_TELEMETRY when it is set to true or false: the environment
	// then decides the Telemetry report (ADR 0032). Nil (unset, or any other value) leaves
	// the decision to the Instance's Owner.
	Telemetry *bool
	// DoNotTrack is DO_NOT_TRACK=1: it switches the Telemetry report off and never on.
	DoNotTrack bool
	// TelemetryReceiverURL is where the daily Telemetry report goes. It is a constant of the
	// binary with no environment variable (ADR 0032): a test points it at its own receiver.
	TelemetryReceiverURL string
	// TelemetrySchemaURL is the page that documents the Telemetry report's schema; the SPA
	// links to it. A constant of the binary with no environment variable.
	TelemetrySchemaURL string
	// ReleaseCheck is SPHERICON_RELEASE_CHECK (default on): the daily Release check for a
	// newer release (ADR 0032). It sends nothing about the Instance and is independent of
	// the Telemetry report and DO_NOT_TRACK.
	ReleaseCheck bool
	// GitHubAPIURL is the API root the Release check asks; empty is api.github.com. It has
	// no environment variable: tests point it at a server of their own.
	GitHubAPIURL string

	// RequireEmailVerification keeps the email verification gate on (ADR 0030). Only an
	// explicit SPHERICON_REQUIRE_EMAIL_VERIFICATION=false turns it off, for a self-hosted install
	// without outbound mail; the default and any other value keep it on. It is never
	// inferred from the SMTP settings.
	RequireEmailVerification bool

	// SignupOpen lets anyone who reaches the Instance register. Only an explicit
	// SPHERICON_SIGNUP_OPEN=false closes it (ADR 0042): a new User then comes only through an
	// Invitation or `sphericon user create-owner`.
	SignupOpen bool

	// SignupSurvey asks a User who signed up the Sign-up survey once. Only an explicit
	// SPHERICON_SIGNUP_SURVEY=true turns it on (as ADR 0030 and 0042); it also needs the
	// `staff` license, and without it the survey stays off (signupsurvey.Enabled).
	SignupSurvey bool

	// Signin holds the credentials of the Sign-in providers the Instance enabled, by registry
	// name (ADR 0048): SPHERICON_SIGNIN_<PROVIDER>_CLIENT_ID and _CLIENT_SECRET, both or
	// neither (a half-configured provider stops the start). A provider not in the map is off.
	Signin map[string]SigninCredentials

	// Logging: level is one of debug|info|warn|error; format is text|json.
	// Dev defaults to a human-readable coloured text handler, prod to JSON.
	LogLevel  string
	LogFormat string

	// OtelServiceName is the service.name reported on OTel traces/metrics. The
	// export destinations come from the declarative config file OTEL_CONFIG_FILE
	// names (read by otelconf in internal/telemetry).
	OtelServiceName string

	// MetricsAddr (host:port) is where the opt-in Prometheus listener binds. Empty
	// (the default) means no listener; the public port never serves /metrics
	// (ADR 0025).
	MetricsAddr string

	// InternalAddr (host:port) is where the opt-in internal listener binds: the only
	// place the service endpoints of the Staff surface are served (ADR 0026). Empty
	// (the default) means no listener and no route for them anywhere; it must differ
	// from the public PORT and from SPHERICON_METRICS_ADDR. Nothing is served either without the
	// `staff` license.
	InternalAddr string

	// TrackingCNAMETarget is the edge host a Sending domain's Tracking domain CNAME
	// (track.<domain>) must point at (SPHERICON_TRACKING_CNAME_TARGET, ADR 0038). Empty (the
	// default) turns the feature off: no tracking record is shown and none is checked.
	TrackingCNAMETarget string

	// TrackingHostHeader names the request header the edge sets to the customer's
	// host (SPHERICON_TRACKING_HOST_HEADER, ADR 0038). It is read only from a peer inside
	// TrackingEdgeCIDRs; empty (the default) means the connection's Host is the only source.
	TrackingHostHeader string

	// TrackingEdgeCIDRs are the peer address ranges the edge connects from
	// (SPHERICON_TRACKING_EDGE_CIDRS): the only peers whose TrackingHostHeader is believed.
	// The default is loopback only; a proxy on another address must be listed, because a
	// private range as a default would let any peer of a shared network forge the host.
	TrackingEdgeCIDRs []netip.Prefix

	// PresetsDir is a directory of the Instance's custom Presets (ADR 0037): every
	// *.yaml and *.yml file at its root loads beside the built-in ones at start, and a
	// file that does not load, or reuses a built-in name, stops the start. Empty (the
	// default) means only the built-in Presets.
	PresetsDir string

	// System (platform) transactional email — sphericon's OWN sender, distinct from a
	// customer's per-workspace integration. Dev uses smtp → mailpit (the SMTP_*
	// values); prod uses ses (the SES_* values). Sent via the same messaging
	// Catalog, not a separate sender.
	SystemEmailProvider string // "smtp" | "ses"
	SystemEmailFrom     string
	SESRegion           string
	SESAccessKeyID      string
	SESSecretAccessKey  string
}

func Load

func Load(envName string) (*Config, error)

Load reads the configuration from the process environment layered over the .env files (see envFiles): a variable the process sets wins over every file.

func (*Config) JWTSecrets

func (c *Config) JWTSecrets() []string

JWTSecrets is every JWT secret the instance accepts: JWTSecret first, then the secondary one.

func (*Config) JWTVerifySecrets

func (c *Config) JWTVerifySecrets() []string

JWTVerifySecrets are the secrets a session or a mailed link is also accepted under besides JWTSecret: the secondary key of a rotation, none outside one.

func (*Config) MCPOrigin

func (c *Config) MCPOrigin() string

MCPOrigin is the public origin the MCP resource is served from: SPHERICON_MCP_URL when the deployment gives MCP its own host, otherwise AppURL.

func (*Config) SecureCookies

func (c *Config) SecureCookies() bool

SecureCookies reports whether session cookies carry the Secure attribute: whenever the instance is served over HTTPS (SPHERICON_APP_URL reflects the public scheme, even though a proxy terminates TLS and the Go server listens on plain HTTP). Local http dev stays false, where a Secure cookie would never be sent back.

func (*Config) SystemMailConfigured

func (c *Config) SystemMailConfigured() bool

SystemMailConfigured reports whether the Instance has outbound mail, that is, a System sender: the SES provider, or SMTP with a host. Without it there is none (ADR 0041).

func (*Config) TrackerOrigin

func (c *Config) TrackerOrigin() string

TrackerOrigin is the public origin of the tracker: SPHERICON_TRACKER_URL when the deployment serves /t.js and /collect on a host of its own, otherwise AppURL.

func (*Config) UseListener

func (c *Config) UseListener(addr net.Addr)

UseListener points the public URL and PORT at a listener the caller opened, so links built from AppURL reach that listener.

type DBPool

type DBPool struct {
	MaxOpenConns    int
	MaxIdleConns    int
	ConnMaxLifetime time.Duration
	// RiverMaxConns bounds river's own queries (fetch, completion, leader election). River polls
	// and holds no LISTEN connection; job workers use the database/sql pool, so it need not
	// match river's MaxWorkers sum.
	RiverMaxConns int32
}

DBPool bounds the Postgres connections one replica may open: the database/sql pool (ent, pubsub) and the pgx pool river runs on. Defaults assume at most two replicas against max_connections=100: (15+25) x 2 x 1.15 = 92 <= 97.

type RateLimits

type RateLimits struct {
	// Human caps the public human-facing endpoints (signup, invitation accept,
	// consent confirm) per client IP and endpoint.
	Human int
	// APIBurst caps /api and /mcp per Workspace per second (one shared budget).
	APIBurst int
	// APIPerMinute caps /api and /mcp per Workspace per minute, stacked on APIBurst.
	APIPerMinute int
	// FailedAuth caps failed credential checks (bearer token, collect key) per
	// client IP per minute; successful ones are not counted.
	FailedAuth int
	// Tracking caps how many opens and clicks one client IP may have recorded per
	// minute. It never refuses a recipient: over it the event is not recorded.
	Tracking int
	// LoginFailures is how many failed logins one account may have within 15 minutes
	// before login answers 429 with an exponentially growing delay (no lockout).
	LoginFailures int
	// LoginIP caps login requests per client IP per minute.
	LoginIP int
	// Collect caps /collect per Workspace per minute (a budget of its own, apart
	// from /api and /mcp).
	Collect int
	// CollectIP caps /collect per client IP per minute.
	CollectIP int
	// ServicePerMinute caps the internal listener's service endpoints per Service
	// credential per minute.
	ServicePerMinute int
	// ForgotAddress is how many mails one address may be sent per hour by each of
	// forgot-password, registration and an email-change request (ADR 0031). Over it
	// the request is still answered 202 and nothing is sent.
	ForgotAddress int
	// ForgotIP caps forgot-password requests per client IP per hour (429 over it).
	ForgotIP int
}

RateLimits are the per-policy request budgets per minute (ADR 0025). Every limit has a default and 0 disables it. They are core, never gated by the EE licence.

type SigninCredentials

type SigninCredentials struct {
	ClientID     string
	ClientSecret string
}

SigninCredentials is the OAuth client of one Sign-in provider (ADR 0048): the Instance operator registers an app with the provider and sets its id and secret.

Jump to

Keyboard shortcuts

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