Documentation
¶
Index ¶
Constants ¶
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 ¶
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 ¶
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 SecretValues ¶
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 ¶
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 ¶
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
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
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
// 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 ¶
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 ¶
JWTSecrets is every JWT secret the instance accepts: JWTSecret first, then the secondary one.
func (*Config) JWTVerifySecrets ¶
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 ¶
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 ¶
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 ¶
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) UseListener ¶
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 ¶
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.