Documentation
¶
Overview ¶
Package config — user-level telemetry preference management.
Telemetry preference is stored in ~/.nself/config.toml under [telemetry]. This file does NOT ship a telemetry client at v1.0.9. The preference is persisted now so the v1.1.0 client can respect choices made today.
Env var NSELF_TELEMETRY_OPT_OUT=1 always takes precedence over the file.
Index ¶
- Constants
- Variables
- func BuildJWTSecret(cfg *Config) (string, error)
- func BuildServiceURL(subdomain, baseDomain string) string
- func DefaultFor(key string) string
- func DetectMonorepoRoot(dir string) string
- func EnvCascadeOrder(envName string, legacy bool) []string
- func FindDependsCycle(edges map[string][]string, roots []string) []string
- func FindNSelfRoot(startDir string) (string, error)
- func IsSecretKey(key string) bool
- func KnownEnvVars() []string
- func LegacyOrderActive() bool
- func QuoteEnvValue(v string) string
- func ResolveEnv(projectDir string) (value string, source string)
- func RouteToFQDN(route, baseDomain string) (string, error)
- func SanitizeDBName(input string) (string, error)
- func SanitizeDomain(input string) (string, error)
- func SanitizeName(input string) (string, error)
- func SetTelemetryEnabled(enabled bool) error
- func URLPassword(password string) string
- func URLUserInfo(user, password string) string
- func UnquoteEnvValue(v string) string
- func UseRandSource(r io.Reader) (restore func())
- func Validate(cfg *Config) error
- func ValidateBuildContext(key, projectDir, p, dockerfile string) error
- func ValidateHasuraDevMode(cfg *Config) error
- func ValidateMinioCredentials(cfg *Config) error
- func ValidateNginxInputs(cfg *Config) error
- func ValidateRedisPassword(cfg *Config) error
- func WithoutLocalOverride(order []string) []string
- type AdminConfig
- type ApiDocsConfig
- type AuthConfig
- type BackupConfig
- type CSDependency
- type CascadeFile
- type Config
- type CustomService
- type DRConfig
- type ElasticsearchConfig
- type EmailConfig
- type Explanation
- type FrontendApp
- type FunctionsConfig
- type HasuraConfig
- type InternalRoute
- type LicenseConfig
- type LoadOptions
- type Loader
- type MLflowConfig
- type MailpitConfig
- type MeiliSearchConfig
- type MinioConfig
- type MonitoringConfig
- type NginxConfig
- type PgBouncerConfig
- type PluginProConfig
- type PluginSystemConfig
- type PostgresConfig
- type RedisConfig
- type RemoteSchema
- type SearchConfig
- type SecretsConfig
- type Setter
- type TelemetryPreference
- type TenantConfig
- type TypesenseConfig
- type ValidatorFunc
- type ValidatorResult
Constants ¶
const EnvBackupHeartbeatRemote = "NSELF_BACKUP_HEARTBEAT_REMOTE"
EnvBackupHeartbeatRemote names the variable that selects the heartbeat remote.
const LegacyEnvOrderVar = "NSELF_LEGACY_ENV_ORDER"
LegacyEnvOrderVar is the escape-hatch environment variable that restores the pre-CLI-R18 cascade order for exactly one minor version after the reorder ships. Every use prints a warning naming the variable and the version it will stop being honored in — see warnLegacyEnvOrder in loader.go.
const URLPasswordEnvSuffix = "_URLENC"
URLPasswordEnvSuffix is appended to a password variable name to name its URL-encoded twin (POSTGRES_PASSWORD -> POSTGRES_PASSWORD_URLENC).
Variables ¶
var AllCascadeFilenames = []string{
".env",
".env.dev",
".env.staging",
".env.prod",
".env.secrets",
".env.local",
".env.ai",
}
AllCascadeFilenames is the fixed superset of every filename that appears in either cascade order, across every environment name. Used by the migration shim to read every candidate file once regardless of which order or env is being analyzed.
Functions ¶
func BuildJWTSecret ¶
BuildJWTSecret constructs the HASURA_GRAPHQL_JWT_SECRET JSON string. If the environment variable HASURA_GRAPHQL_JWT_SECRET is already set, it is returned directly. Otherwise the secret is constructed from cfg.Hasura.JWTKey and cfg.Hasura.JWTType, which are the single source of truth also used by buildAuthService for AUTH_JWT_SECRET/AUTH_JWT_TYPE (JWT-ALGO-01) — this guarantees Hasura verifies tokens with the exact algorithm+key auth signed them with. A missing JWTKey is auto-generated (matching ApplyDefaults' HS256-default behavior); an empty JWTType defaults to HS256 for the same reason: no RSA keypair generator exists in this codebase, so RS256 can only be produced correctly when the caller already went through ApplyDefaults (which rejects RS256 without a supplied key).
func BuildServiceURL ¶
BuildServiceURL constructs a full HTTPS URL for a service subdomain against the given baseDomain. If baseDomain already starts with "subdomain.", that prefix is stripped first to avoid double-prefixing (e.g. "auth.auth.example.com").
Examples:
BuildServiceURL("auth", "auth.example.com") → "https://auth.example.com"
BuildServiceURL("auth", "example.com") → "https://auth.example.com"
func DefaultFor ¶
DefaultFor returns the documented default value for an env var, as a human-readable string. Returns "" when the var has no static default (e.g. generated secrets, or dynamic per-env defaults).
func DetectMonorepoRoot ¶
DetectMonorepoRoot checks whether dir is a monorepo root by looking for an nself backend sub-directory containing a .env file. It probes, in order:
- <dir>/backend/.env
- <dir>/.backend/.env
Returns the full path to the backend directory if found, or "" if dir does not appear to be a monorepo root. The function never returns an error: a missing or unreadable path is treated as "not found".
func EnvCascadeOrder ¶ added in v1.3.0
EnvCascadeOrder returns the ordered list of filenames (lowest precedence first; later entries win) consulted to resolve the given environment name.
Canonical order (legacy=false), approved 2026-08-23 GATE B:
.env → .env.{dev|staging|prod} → .env.secrets → .env.local
.env is the shared, committed base. Exactly one of .env.dev/.env.staging/ .env.prod loads, matching envName ("local" loads .env.dev). A custom envName (qa, live, ...) loads .env.<envName> and never .env.dev (P7-DEPL-12). .env.secrets never ships in git. .env.local is the personal override and always wins. .env.ai no longer exists as a cascade layer — its content is folded into .env.secrets at init/upgrade (see internal/setup/envai.go and internal/migrate/env_order.go).
Legacy order (legacy=true), restored only via NSELF_LEGACY_ENV_ORDER for exactly one minor version:
.env.dev → .env.{staging|prod} → .env.secrets → .env.local → .env → .env.ai
.env.dev always loaded as a base layer regardless of envName (a quirk the canonical order removes), with .env.staging/.env.prod layered on top only for those two envs, and bare .env / .env.ai winning last.
func FindDependsCycle ¶ added in v1.4.13
FindDependsCycle returns the first dependency cycle reachable from roots in edges (name -> dependencies) as "a -> b -> a", or nil. Each node is visited once (depth-first, memoised), so a diamond is not a cycle and a cycle among services reached through a root is reported even when the root is not on it. Shared by compose generation (custom services only) and build post-validation (the whole compose set).
func FindNSelfRoot ¶
FindNSelfRoot walks up from startDir looking for a nself project root. It checks, at each directory level:
- startDir/.backend/.env → returns startDir/.backend (monorepo case)
- startDir/.env → returns startDir (already in backend dir)
Walking stops at $HOME, at /, or after 10 levels — whichever comes first. Returns an error if no project root is found.
func IsSecretKey ¶ added in v1.4.13
IsSecretKey reports whether key names a secret value.
func KnownEnvVars ¶
func KnownEnvVars() []string
KnownEnvVars returns the list of environment variable names that the CLI loader reads. Used by commands like `nself config list` to display known vars with their current values.
func LegacyOrderActive ¶ added in v1.3.0
func LegacyOrderActive() bool
LegacyOrderActive reports whether the NSELF_LEGACY_ENV_ORDER escape hatch is set to a truthy value in the current process environment.
func QuoteEnvValue ¶ added in v1.1.1
QuoteEnvValue wraps v in single quotes when v is a JSON object or array value. Non-JSON values are returned unchanged. Single-quoted values are also returned unchanged (idempotent).
Example:
QuoteEnvValue(`{"type":"HS256","key":"abc"}`) → `'{"type":"HS256","key":"abc"}'`
QuoteEnvValue("plain-value") → "plain-value"
QuoteEnvValue(`'already-quoted'`) → `'already-quoted'`
func ResolveEnv ¶ added in v1.4.4
ResolveEnv determines which environment name selects the .env.{ENV} cascade layer, and where that value came from (surfaced via slog and by `nself env explain`).
Order: the process environment wins outright — an operator or CI that already exported ENV=... is always honored. Otherwise the ENV= key inside the project's own .env file (the shared, committed base present in every clone, including a bare production checkout with no shell env set) is read directly. Otherwise "dev".
The .env fallback is read with a single targeted key lookup rather than running the full cascade, because the full cascade's file selection itself depends on the answer.
func RouteToFQDN ¶
RouteToFQDN constructs a valid FQDN from a route segment and base domain. It trims whitespace, removes leading/trailing dots and slashes from both inputs, lowercases both, and returns "route.domain". Returns error if either input is empty after normalization.
func SanitizeDBName ¶ added in v1.3.6
SanitizeDBName converts a Docker-compatible project name (as produced by SanitizeName: lowercase alphanumeric + hyphens) into a value that satisfies the stricter PostgreSQL unquoted-identifier syntax enforced by database.SanitizeIdentifier (start with a letter or underscore; only letters, digits, and underscores after that; max 63 bytes).
PROJECT_NAME and POSTGRES_DB have different validity domains — Docker container/network names allow hyphens and a leading digit, SQL identifiers allow neither — so the raw project name (e.g. from a hyphenated directory like "rls-pentest-project") cannot be reused verbatim as the database name. This normalizes it instead of rejecting it, so init always produces a working POSTGRES_DB regardless of the source directory name.
Returns ("", err) only if nothing usable remains after normalization (e.g. an input made entirely of characters outside [a-z0-9_-. ]).
func SanitizeDomain ¶
SanitizeDomain lowercases, trims whitespace, trims trailing dots, and validates that only hostname-safe characters remain: letters, digits, dots, and hyphens ([a-zA-Z0-9.-]). Any other character (semicolons, spaces, asterisks, dollar signs, newlines, etc.) causes an error to prevent nginx config injection and similar attacks. Returns ("", err) if the result is empty or contains invalid characters.
func SanitizeName ¶
SanitizeName lowercases, trims whitespace, replaces spaces/underscores with hyphens, removes all non-alphanumeric-hyphen characters, collapses consecutive hyphens, and trims leading/trailing hyphens. Returns ("", err) if the result is empty after normalization.
func SetTelemetryEnabled ¶ added in v1.0.11
SetTelemetryEnabled persists the telemetry preference to ~/.nself/config.toml. Creates the file and directory if they do not exist.
func URLPassword ¶ added in v1.4.13
URLPassword returns password percent-encoded for the password position of a connection URL's userinfo (RFC 3986 §3.2.1): the exact bytes URLUserInfo emits after the colon, so "redis://:" + URLPassword(p) + "@host" and URLUserInfo("", p) agree.
Purpose: docker compose can interpolate a variable into a URL but cannot encode it, so a password with "/", "@", ":", "?", "#" or "%" breaks every compose fragment that writes ":${POSTGRES_PASSWORD}@" (P7-PROD-31). nself build writes this value as POSTGRES_PASSWORD_URLENC / REDIS_PASSWORD_URLENC and fragments reference it. Encode once, at the source: the result must be substituted verbatim and never encoded again (a second pass turns "%40" into "%2540"). Inputs: any password, possibly empty. Outputs: the encoded password, or "" for an empty password (callers omit the variable so the compose fallback applies).
func URLUserInfo ¶ added in v1.4.10
URLUserInfo returns the "user:password" part of a connection URL with both halves percent-encoded for the userinfo component (RFC 3986 §3.2.1).
Purpose: every DSN the CLI builds (DATABASE_URL, REDIS_URL, pgbouncer and restore URLs) embeds a credential. A password containing "/", "@", ":", "?" or "#" must be escaped there, or the URL parses with the wrong host and clients fail with "Invalid URL" (nself-web prod, 2026-09-23: a base64 REDIS_PASSWORD with "/" left auth-server rate limiting, revocation and lockout without Redis). url.PathEscape is not a substitute: it leaves "@" and ":" unescaped. Inputs: user may be empty (Redis uses ":password"); password may be empty. Outputs: "user", "user:pw" or ":pw", escaped; "" when both are empty.
func UnquoteEnvValue ¶ added in v1.1.1
UnquoteEnvValue strips a single layer of wrapping single quotes from v when the entire value is single-quoted. It is the inverse of QuoteEnvValue.
Example:
UnquoteEnvValue(`'{"type":"HS256","key":"abc"}'`) → `{"type":"HS256","key":"abc"}`
UnquoteEnvValue("plain-value") → "plain-value"
func UseRandSource ¶ added in v1.4.13
UseRandSource makes every secret generated by this package (JWT key, admin and plugin secrets) read from r until the returned restore func runs; a nil r keeps crypto/rand. It is for the build orchestrator (build.Options.Rand), so a plan and an apply of one fresh project can generate identical secrets. The scope holds a package lock: concurrent scopes run one after another, and each restores crypto/rand on exit. Callers must call restore exactly once and must not nest scopes. Direct config.Load callers outside a scope always get crypto/rand unless a seeded scope is open in the same process; the seed is a test and plan-fidelity facility, never set it in production.
func Validate ¶
Validate checks cfg for security issues, port conflicts, and route collisions by running all registered validators. All failures are collected and returned together so the caller sees the full picture.
Password and JWT validations are only enforced when Env is "staging" or "prod". In dev mode, weak passwords are acceptable (the caller is expected to auto-generate strong ones before reaching validation).
func ValidateBuildContext ¶ added in v1.4.13
ValidateBuildContext enforces E528 on an ancestor CS_N_PATH (key names the variable in the message). projectDir is the project root ("" means the current directory); dockerfile is CS_N_DOCKERFILE ("" means Dockerfile). A path with no ".." segment is inside the project and passes. An ancestor context may reach at most the nearest ancestor of the project that holds .git (the project itself when none does), and the ignore file BuildKit will use must exclude the .env* and .secrets files at the context root, in every directory down to the project, and below it, so the monorepo's secrets are never sent to the image builder. A bare ".env*" is not enough: Docker matches it at the context root only, so proj/.env would still be sent; "**/.env*" and "**/.secrets" are the patterns that cover nested paths.
func ValidateHasuraDevMode ¶
ValidateHasuraDevMode returns an error if HASURA_GRAPHQL_DEV_MODE=true in prod. When the block fires it emits a structured slog.Error so that operators with centralised log aggregation (Loki/Datadog) can alert on accidental dev-mode exposure. The error is always returned; the log is a side effect.
func ValidateMinioCredentials ¶ added in v1.1.8
ValidateMinioCredentials enforces strong MinIO credentials in staging and production. In dev, minioadmin defaults are accepted without error.
Rules (staging/prod only):
- MINIO_ROOT_USER must not be empty or "minioadmin".
- MINIO_ROOT_PASSWORD must not be "minioadmin" and must be >=16 chars.
Returns nil for dev environments unconditionally.
func ValidateNginxInputs ¶
ValidateNginxInputs validates raw nginx template inputs that are interpolated without escaping. Prevents nginx directive injection via env vars.
func ValidateRedisPassword ¶
ValidateRedisPassword returns an error if Redis is enabled in staging/prod without a password.
func WithoutLocalOverride ¶ added in v1.4.13
WithoutLocalOverride returns order minus .env.local. The remote deploy cascade is EnvCascadeOrder with this applied, in the build and in the env snapshot pushed to the host alike.
Types ¶
type AdminConfig ¶
type AdminConfig struct {
Enabled bool `env:"NSELF_ADMIN_ENABLED"`
Version string `env:"NSELF_ADMIN_VERSION"` // latest
Port int `env:"NSELF_ADMIN_PORT"` // 3021
Route string `env:"NSELF_ADMIN_ROUTE"` // admin.{BD}
DevMode bool `env:"NSELF_ADMIN_DEV"` // false
DevPort int `env:"NSELF_ADMIN_DEV_PORT"` // 3000
SecretKey string `env:"ADMIN_SECRET_KEY"`
PasswordHash string `env:"ADMIN_PASSWORD_HASH"`
}
AdminConfig holds nSelf Admin GUI configuration.
type ApiDocsConfig ¶ added in v1.0.12
type ApiDocsConfig struct {
Enabled bool `env:"API_DOCS_ENABLED"` // default: true
Path string `env:"API_DOCS_PATH"` // serve path, default: /docs
Title string `env:"API_DOCS_TITLE"` // defaults to "<ProjectName> API"
Theme string `env:"API_DOCS_THEME"` // default | moon | purple | solarized
AuthEnvVar string `env:"API_DOCS_AUTH_ENV_VAR"` // env var with bearer token for try-out
HideEndpoints []string // paths to exclude from the spec
GraphQLEnabled bool `env:"API_DOCS_GRAPHQL_ENABLED"` // default: true
GraphQLEndpoint string `env:"API_DOCS_GRAPHQL_ENDPOINT"` // default: /v1/graphql
}
ApiDocsConfig holds the api_docs section from nself.yaml. Controls generation of the OpenAPI 3.1 spec and Scalar interactive docs page.
type AuthConfig ¶
type AuthConfig struct {
Version string `env:"AUTH_VERSION"` // 0.36.0
Port int `env:"AUTH_PORT"` // 4000
ClientURL string `env:"AUTH_CLIENT_URL"`
AccessTokenExpiry int `env:"AUTH_ACCESS_TOKEN_EXPIRES_IN"` // seconds
RefreshTokenExpiry int `env:"AUTH_REFRESH_TOKEN_EXPIRES_IN"` // seconds
Route string `env:"AUTH_ROUTE"`
SMTPHost string `env:"AUTH_SMTP_HOST"`
SMTPPort int `env:"AUTH_SMTP_PORT"`
SMTPUser string `env:"AUTH_SMTP_USER"`
SMTPPass string `env:"AUTH_SMTP_PASS"`
SMTPSecure bool `env:"AUTH_SMTP_SECURE"`
SMTPSender string `env:"AUTH_SMTP_SENDER"`
MemLimit string `env:"AUTH_MEM_LIMIT"` // 256m
CPULimit string `env:"AUTH_CPU_LIMIT"` // 0.25
ExtraRedirectURLs string `env:"AUTH_EXTRA_REDIRECT_URLS"` // comma-separated extra redirect URLs
WebAuthnEnabled bool `env:"AUTH_WEBAUTHN_ENABLED"`
MFATOTPEnabled bool `env:"AUTH_MFA_TOTP_ENABLED"` // false
LogLevel string `env:"AUTH_LOG_LEVEL"` // info
}
AuthConfig holds authentication service configuration.
type BackupConfig ¶
type BackupConfig struct {
Dir string `env:"BACKUP_DIR"` // ./backups — read by database/backup.go and restore.go
Enabled bool `env:"BACKUP_ENABLED"`
Schedule string `env:"BACKUP_SCHEDULE"` // legacy alias for BACKUP_SCHEDULE_FULL
RetentionDays int `env:"BACKUP_RETENTION_DAYS"` // legacy — use Daily/Weekly/Monthly instead
CloudProvider string `env:"BACKUP_CLOUD_PROVIDER"` // legacy — use Remote instead
// Cloud/remote storage
Remote string `env:"BACKUP_REMOTE"` // rclone remote path, e.g. s3://bucket/path
Encryption bool `env:"BACKUP_ENCRYPTION"` // enable age encryption
AgeRecipients string `env:"BACKUP_AGE_RECIPIENTS"` // age public key for encryption
ScheduleFull string `env:"BACKUP_SCHEDULE_FULL"` // cron expr for full backups (default: 0 3 * * *)
WALInterval int `env:"BACKUP_WAL_INTERVAL_SECONDS"` // WAL archive interval (default: 60)
RetentionDaily int `env:"BACKUP_RETENTION_DAILY"` // keep last N daily backups (default: 7)
RetentionWeekly int `env:"BACKUP_RETENTION_WEEKLY"` // keep last N weekly backups (default: 4)
RetentionMonthly int `env:"BACKUP_RETENTION_MONTHLY"` // keep last N monthly backups (default: 12)
RestoreTestSchedule string `env:"BACKUP_RESTORE_TEST_SCHEDULE"` // cron for restore tests (default: 0 5 * * 0)
AlertOnFailure bool `env:"BACKUP_ALERT_ON_FAILURE"` // send alert on backup failure
S3AccessKeyID string `env:"BACKUP_S3_ACCESS_KEY_ID"`
S3SecretAccessKey string `env:"BACKUP_S3_SECRET_ACCESS_KEY"`
S3Region string `env:"BACKUP_S3_REGION"`
S3Endpoint string `env:"BACKUP_S3_ENDPOINT"`
// CriticalTables overrides database.DefaultCriticalTables for the backup
// drill's smoke check (comma-separated table names, e.g.
// "users,licenses,audit_logs,plugins"). Empty (the default) keeps the
// np_-prefixed convention. Deployed schemas vary in whether they use
// nSelf's np_ multi-app-isolation prefix, so the drill's critical-table
// presence check must be project-configurable rather than hardcoded to
// one convention — see .claude/qa/bugs/drill-critical-tables-naming.md.
CriticalTables string `env:"BACKUP_CRITICAL_TABLES"`
}
BackupConfig holds backup and recovery configuration. Dir is read by internal/database/backup.go and restore.go for ad-hoc pg_dump/pg_restore. Scheduled, cloud, and retention features are managed via BACKUP_* env vars.
func (BackupConfig) HeartbeatRemote ¶ added in v1.4.13
func (BackupConfig) HeartbeatRemote() string
HeartbeatRemote returns the heartbeat remote from the environment, trimmed, or "" when it is not set.
type CSDependency ¶ added in v1.4.13
CSDependency is one CS_N_DEPENDS_ON entry. Condition is the compose value (service_started, service_healthy or service_completed_successfully).
type CascadeFile ¶ added in v1.3.0
type CascadeFile struct {
// Name is the filename relative to the project directory, e.g. ".env.secrets".
Name string
// Path is Name joined with the project directory.
Path string
// Exists reports whether the file is present on disk.
Exists bool
}
CascadeFile describes one file consulted by the env cascade, in load order.
func EnvCascade ¶ added in v1.3.0
func EnvCascade(projectDir, envName string, legacy bool) []CascadeFile
EnvCascade resolves EnvCascadeOrder to on-disk paths under projectDir, with existence checked. Used by `nself env explain` and the migration shim; both need to know not just the order but which files are actually present.
type Config ¶
type Config struct {
// Core
ProjectName string `env:"PROJECT_NAME"`
BaseDomain string `env:"BASE_DOMAIN"`
Env string `env:"ENV"` // dev, staging, prod
ProjectDescription string `env:"PROJECT_DESCRIPTION"`
AdminEmail string `env:"ADMIN_EMAIL"`
DBEnvSeeds bool `env:"DB_ENV_SEEDS"`
// AppName is an OPTIONAL app-prefix used to namespace generated core
// nginx subdomains (gap #5): when set, routes render as
// "api.{AppName}.{BASE_DOMAIN}" / "auth.{AppName}.{BASE_DOMAIN}" instead
// of the bare "api.{BASE_DOMAIN}" / "auth.{BASE_DOMAIN}" scheme. Deliberately
// distinct from PROJECT_NAME (which is always set, for container/network
// naming) so existing single-app deployments (ummat, unity) that only set
// PROJECT_NAME are unaffected — AppName defaults to "" (bare scheme).
AppName string `env:"APP_NAME"`
// PostgreSQL
Postgres PostgresConfig
// Hasura
Hasura HasuraConfig
// Auth
Auth AuthConfig
// Nginx
Nginx NginxConfig
// SSL
SSLMode string `env:"SSL_MODE"` // local, letsencrypt, custom, none
SSLProvider string `env:"SSL_PROVIDER"` // cloudflare, route53, digitalocean, custom
SSLWildcardDomain string `env:"SSL_WILDCARD_DOMAIN"` // *.example.com
ExtraSSLDomains string `env:"EXTRA_SSL_DOMAINS"` // comma-separated
CloudflareAPIKey string `env:"CLOUDFLARE_API_KEY"` // DNS-01 challenge
// WAF
WAFMode string `env:"WAF_MODE"` // off, detection, blocking
// Optional Services
Redis RedisConfig
Minio MinioConfig
Mailpit MailpitConfig
Functions FunctionsConfig
MLflow MLflowConfig
Admin AdminConfig
Monitoring MonitoringConfig
// Search (provider-agnostic)
Search SearchConfig
// Email Provider
Email EmailConfig
// PgBouncer connection pooler
PgBouncer PgBouncerConfig
// Backup & Recovery
Backup BackupConfig
// Disaster Recovery
DR DRConfig
// Multi-Tenancy & Billing
Tenant TenantConfig
// License
License LicenseConfig
// Secrets Management
Secrets SecretsConfig
// Plugin Pro Configuration
PluginConfig PluginProConfig
// Plugin System
PluginSystem PluginSystemConfig
// API Docs (Scalar)
ApiDocs ApiDocsConfig
// Custom Services
CustomServices []CustomService // CS_1..CS_10
// Frontend Apps
FrontendApps []FrontendApp // FRONTEND_APP_1..FRONTEND_APP_20
// Remote Schemas
RemoteSchemas []RemoteSchema
// Internal Routes (up to 20)
InternalRoutes []InternalRoute
// Docker
DockerNetwork string `env:"DOCKER_NETWORK"`
DockerLogMaxSize string `env:"DOCKER_LOG_MAX_SIZE"` // 10m
DockerLogMaxFile string `env:"DOCKER_LOG_MAX_FILE"` // 3
DockerStopGrace string `env:"DOCKER_STOP_GRACE_PERIOD"` // 30s
DockerBuildTimeout int `env:"NSELF_DOCKER_BUILD_TIMEOUT"` // 300
// Start/Stop behavior
StartMode string `env:"NSELF_START_MODE"` // smart, fresh, force
HealthCheckTimeout int `env:"NSELF_HEALTH_CHECK_TIMEOUT"` // seconds
HealthCheckInterval int `env:"NSELF_HEALTH_CHECK_INTERVAL"`
HealthCheckRequired int `env:"NSELF_HEALTH_CHECK_REQUIRED"` // percentage
CleanupOnStart string `env:"NSELF_CLEANUP_ON_START"` // auto/always/never
AllowExposedPorts bool `env:"NSELF_ALLOW_EXPOSED_PORTS"`
ParallelLimit int `env:"NSELF_PARALLEL_LIMIT"` // 5
LogLevel string `env:"NSELF_LOG_LEVEL"` // info
SkipHealthChecks bool `env:"NSELF_SKIP_HEALTH_CHECKS"`
StopTimeout int `env:"NSELF_STOP_TIMEOUT"` // 30
// Federation — GraphQL Federation via Apollo Router (G05).
// When true, nself build injects Apollo Router (CS_7) and composes a
// supergraph schema from installed plugin subgraphs. Default: false.
FederationEnabled bool `env:"NSELF_FEDERATION"`
// EmbeddedPG — when true, nself build omits the Docker postgres service
// and instead relies on the pglite/wasmtime embedded runtime started by
// `nself start --embedded-pg`. Hasura is wired via a Unix-domain socket
// bridge. Default: false.
EmbeddedPG bool `env:"NSELF_EMBEDDED_PG"`
// Passthrough: arbitrary env vars matching patterns (AUTH_PROVIDER_*, REMOTE_SCHEMA_*, etc.)
Passthrough map[string]string
}
Config is the top-level configuration struct for an nSelf project. All fields are populated from the .env cascade and environment variables.
func ApplyDefaults ¶
ApplyDefaults fills every empty/zero field in cfg with the canonical default value. It never overrides a non-empty string, non-zero int, or explicitly-set boolean. Empty string "" is considered unset for string fields; zero is considered unset for int fields.
Environment-specific overrides (Console, DevMode, CORS, BindIP, SSL) are applied after all static defaults.
func Load ¶
Load reads the .env cascade from projectDir, populates a Config struct from os.Getenv, applies smart defaults, and returns the complete configuration.
Cascade order (later overrides earlier), approved 2026-08-23 GATE B (CLI-R18):
.env → .env.{dev|staging|prod} → .env.secrets → .env.local
.env is the shared, committed base; exactly one of .env.dev/.env.staging/ .env.prod loads, matching ENV; .env.secrets never ships in git; .env.local is the personal override and always wins. .env.ai no longer exists as a cascade layer — its content is folded into .env.secrets at init/upgrade (internal/setup/envai.go, internal/migrate/env_order.go).
Setting NSELF_LEGACY_ENV_ORDER=1 restores the pre-CLI-R18 order for exactly one minor version (see EnvCascadeOrder in cascade.go for both orders in full), printing a warning on every use. Run `nself migrate` to move a project off the escape hatch permanently.
Each file is optional. Missing files are silently skipped.
func LoadWithOptions ¶ added in v1.4.13
func LoadWithOptions(projectDir string, opts LoadOptions) (*Config, error)
LoadWithOptions is Load with explicit options.
func (*Config) DatabaseURL ¶
DatabaseURL returns the computed PostgreSQL connection string using internal container networking (always port 5432, host "postgres"). The password is percent-encoded per RFC 3986 for safe URL inclusion (see URLUserInfo).
func (*Config) EmbeddedPGDatabaseURL ¶ added in v1.1.3
EmbeddedPGDatabaseURL returns a PostgreSQL DSN that connects via the Unix-domain socket bridge created by the pglite/wasmtime embedded runtime. The host field is the runtimeDir path; sslmode=disable is required because the embedded runtime does not perform TLS termination.
Use this DSN only when cfg.EmbeddedPG is true and runtimeDir is the directory passed to embedded.NewEmbeddedPGRuntime.
func (*Config) HostDatabaseURL ¶ added in v1.4.13
HostDatabaseURL returns the PostgreSQL connection string for a client that runs on the host, outside the compose network.
Purpose: DatabaseURL is the in-network DSN (host "postgres", port 5432) and does not resolve from the host. Compose publishes Postgres on loopback only, so host-side tools (importer, exporter, table hash) connect to 127.0.0.1 on POSTGRES_PORT. Inputs: cfg.Postgres User, Password, Port (0 means 5432) and DB (empty means "nself", the default applyDefaultsPostgres sets). Outputs: postgresql://<user>:<pass>@127.0.0.1:<port>/<db>, with the credentials percent-encoded through URLUserInfo exactly as DatabaseURL does. Constraints: the result carries the password; callers never log or print it.
func (*Config) IsProduction ¶
IsProduction reports whether the project environment is production. Both "prod" and "production" are treated as production; the loader normalises "production" → "prod" via normalizeEnv, so only "prod" is checked here.
type CustomService ¶
type CustomService struct {
Index int // 1-10
Name string // parsed from CS_N, then overwritten with SanitizeName(Name) for
// Docker service/container naming (hyphens only, no underscores).
// RawName preserves the pre-sanitization value.
//
// Purpose: a user's CS_N name (e.g. "ping_api") may contain underscores,
// which are valid in a directory name but not in a Docker compose
// service/container name. parseCustomServices sanitizes Name for that
// reason, but internal/compose.defaultCustomServiceBuildContext needs
// the original spelling to find the on-disk `services/<name>` directory
// the user actually created — using the sanitized Name there produced a
// build context ("./services/ping-api") that never matched an existing
// directory ("./services/ping_api"), so the image build failed for any
// name containing an underscore (see the F13/G-013-adjacent bug this
// field fixes). Empty when a CustomService is constructed by hand
// (tests, scaffolding) rather than through parseCustomServices.
RawName string
Template string // express-ts, fastapi, etc.
Port int
Route string // empty = internal only
Public bool
Memory string
CPU string
TablePrefix string // CS_N_TABLE_PREFIX
ExtraEnv string // CS_N_ENV (raw key=val pairs, comma-separated)
BuildPath string // CS_N_PATH: overrides default ./services/{name} build context
// HealthCheck is CS_N_HEALTHCHECK: a path (e.g. "/auth/health"), a full
// "CMD ..." / "CMD-SHELL ..." override, or "disabled"/"none"/"false" to
// omit the healthcheck entirely. Empty keeps the default GET /health.
HealthCheck string
// EnvPassthrough is CS_N_ENV_PASSTHROUGH: a comma-separated allowlist of
// project .env var names to forward into this container in addition to
// the fixed core set from coreEnvVars. CS_N_ENV still wins on conflict.
EnvPassthrough string
// Image is CS_N_IMAGE: a pre-built image reference (optionally digest-pinned,
// e.g. "minio/minio:RELEASE.2024-01-16T16-07-38Z@sha256:...") to run instead
// of building from a Dockerfile. When set, the compose generator emits
// `image:` and omits `build:` entirely — mutually exclusive with
// CS_N_PATH (G-013: closes the gap where a pinned third-party image had
// no CS_N representation and had to be hand-authored into
// docker-compose.override.yml).
Image string
// EnvFile is CS_N_ENV_FILE: a project-relative path to a dotenv-format
// file whose KEY=VALUE lines are injected into this container. Unlike
// CS_N_ENV (a single comma-joined line), a file has no comma/newline
// escaping problem, so it is the right vehicle for many vars or values
// that themselves contain commas (e.g. SMTP credentials). Precedence:
// applied after CS_N_ENV_PASSTHROUGH, before CS_N_ENV (CS_N_ENV always
// wins on conflict, per the existing coreEnvVars contract). Same
// relative-path rules as BuildPath (no absolute paths, no "..").
EnvFile string
// Volumes is CS_N_VOLUMES: a comma-separated list of extra Docker volume
// mounts in "host:container[:mode]" form (e.g.
// "./email-templates:/app/templates:ro"), appended to the service's
// generated volume list. Closes the gap where a required bind mount
// (e.g. a template directory) had no CS_N representation.
Volumes string
// EnvFiles is CS_N_ENV_FILE split on commas, in load order (later wins).
// EnvFile stays the first entry for callers that read it.
EnvFiles []string
// DependsOn is CS_N_DEPENDS_ON: extra compose depends_on entries. Names
// are syntax-checked here and resolved against the whole project in build
// post-validation (after the plugin step).
DependsOn []CSDependency
// Networks is CS_N_NETWORKS: extra project networks, each
// <PROJECT_NAME>_*, emitted as external networks (checked at compose time).
Networks []string
// Dockerfile is CS_N_DOCKERFILE: path relative to the build context.
Dockerfile string
// BuildTarget is CS_N_BUILD_TARGET: the multi-stage build target.
BuildTarget string
// Command is CS_N_COMMAND: exec-form command (whitespace-split, no shell).
Command []string
}
CustomService represents a user-defined custom service (CS_1..CS_10).
func CustomServicesFromEnv ¶ added in v1.4.13
func CustomServicesFromEnv() ([]CustomService, error)
CustomServicesFromEnv parses CS_1..CS_10 from the process environment. Build post-validation uses it to read each service's CS_N_DEPENDS_ON after the plugin step, without reloading the project config.
type DRConfig ¶ added in v1.0.6
type DRConfig struct {
SecondaryRegion string `env:"DR_SECONDARY_REGION"` // Hetzner region for standby
StandbyHost string `env:"DR_STANDBY_HOST"` // IP/hostname of warm standby
DrillSchedule string `env:"DR_DRILL_SCHEDULE"` // cron for DR drills (default: off)
}
DRConfig holds disaster recovery configuration.
type ElasticsearchConfig ¶
type ElasticsearchConfig struct {
Version string `env:"ELASTICSEARCH_VERSION"` // 8.11.3
Port int `env:"ELASTICSEARCH_PORT"` // 9200
Password string `env:"ELASTICSEARCH_PASSWORD"`
Memory string `env:"ELASTICSEARCH_MEMORY"` // 1Gi
}
ElasticsearchConfig holds Elasticsearch-specific configuration.
type EmailConfig ¶
type EmailConfig struct {
Provider string `env:"EMAIL_PROVIDER"` // mailpit/elasticemail/sendgrid/postmark/mailgun/ses/smtp
From string `env:"EMAIL_FROM"`
ElasticEmailAPIKey string `env:"ELASTIC_EMAIL_API_KEY"`
ElasticEmailAccount string `env:"ELASTIC_EMAIL_ACCOUNT_EMAIL"`
SendGridAPIKey string `env:"SENDGRID_API_KEY"`
PostmarkAPIKey string `env:"POSTMARK_API_KEY"`
MailgunAPIKey string `env:"MAILGUN_API_KEY"`
MailgunDomain string `env:"MAILGUN_DOMAIN"`
AWSAccessKeyID string `env:"AWS_ACCESS_KEY_ID"`
AWSSecretAccessKey string `env:"AWS_SECRET_ACCESS_KEY"`
AWSRegion string `env:"AWS_REGION"`
SMTPHost string `env:"SMTP_HOST"`
SMTPPort int `env:"SMTP_PORT"`
SMTPUser string `env:"SMTP_USER"`
SMTPPass string `env:"SMTP_PASS"`
SMTPSecure bool `env:"SMTP_SECURE"`
}
EmailConfig holds email provider configuration.
type Explanation ¶ added in v1.4.13
type Explanation struct {
// Key is the variable name asked about.
Key string
// Known reports whether the key is in KnownEnvVars().
Known bool
// Default is DefaultFor(Key); empty when the key has no static default.
Default string
// Env is the active environment name the cascade was built for.
Env string
// EnvSource is where Env came from (ResolveEnv: "process environment",
// ".env" or "default"); empty when the caller passed Env directly.
EnvSource string
// Legacy reports that NSELF_LEGACY_ENV_ORDER selected the legacy order.
Legacy bool
// Cascade is every file the loader consults, lowest precedence first.
Cascade []CascadeFile
// Setters are the existing cascade files that set the key, in cascade order.
Setters []Setter
// ProcessSet and ProcessValue describe the process environment.
ProcessSet bool
ProcessValue string
}
Explanation is the answer to "where does this key come from".
func ExplainActive ¶ added in v1.4.13
func ExplainActive(projectDir, key string) (Explanation, error)
ExplainActive is ExplainKey for the environment ResolveEnv picks in projectDir, with ResolveEnv's source kept in EnvSource.
func ExplainKey ¶ added in v1.4.13
func ExplainKey(projectDir, env, key string) (Explanation, error)
ExplainKey walks the env cascade of projectDir for the active environment env and reports every source of key. A file that exists but cannot be read is an error.
func (Explanation) Effective ¶ added in v1.4.13
func (e Explanation) Effective() (source, value string)
Effective names the source the loader ends up using and its value: a cascade file name, "process environment", "default" or "unset".
func (Explanation) SelectsCascade ¶ added in v1.4.13
func (e Explanation) SelectsCascade() bool
SelectsCascade reports whether the key is ENV, whose resolved value chooses which cascade files are read. For ENV the process value is consulted first (see ResolveEnv), so EnvSource can name the process environment even when a file wins the final value.
func (Explanation) Winner ¶ added in v1.4.13
func (e Explanation) Winner() (file, value string, ok bool)
Winner returns the winning cascade file and its value; ok is false when no file sets the key. The cascade is lowest precedence first, so the last setter wins.
type FrontendApp ¶
type FrontendApp struct {
Index int
DisplayName string
SystemName string
Port int
Route string
Framework string
TablePrefix string
Image string // FRONTEND_APP_N_IMAGE (optional docker image reference)
}
FrontendApp represents a frontend application (FRONTEND_APP_1..FRONTEND_APP_20).
type FunctionsConfig ¶
type FunctionsConfig struct {
Enabled bool `env:"FUNCTIONS_ENABLED"`
Version string `env:"FUNCTIONS_VERSION"` // latest
Port int `env:"FUNCTIONS_PORT"` // 3008
Route string `env:"FUNCTIONS_ROUTE"` // functions.{BD}
// Runtime selects the container image: node (default), deno, python.
Runtime string `env:"FUNCTIONS_RUNTIME"` // node|deno|python
// Resource limits for the functions container.
Memory string `env:"FUNCTIONS_MEMORY"` // 256M
CPU string `env:"FUNCTIONS_CPU"` // 0.5
Timeout string `env:"FUNCTIONS_TIMEOUT"` // 30s
}
FunctionsConfig holds serverless functions runtime configuration.
type HasuraConfig ¶
type HasuraConfig struct {
Version string `env:"HASURA_VERSION"`
AdminSecret string `env:"HASURA_GRAPHQL_ADMIN_SECRET"`
JWTKey string `env:"HASURA_JWT_KEY"`
JWTType string `env:"HASURA_JWT_TYPE"` // HS256
Console bool `env:"HASURA_GRAPHQL_ENABLE_CONSOLE"`
DevMode bool `env:"HASURA_GRAPHQL_DEV_MODE"`
CORSDomain string `env:"HASURA_GRAPHQL_CORS_DOMAIN"`
Route string `env:"HASURA_ROUTE"` // api.{BASE_DOMAIN}
Port int `env:"HASURA_PORT"` // 8080
MemLimit string `env:"HASURA_MEM_LIMIT"`
CPULimit string `env:"HASURA_CPU_LIMIT"`
LogLevel string `env:"HASURA_GRAPHQL_LOG_LEVEL"` // warn
}
HasuraConfig holds Hasura GraphQL engine configuration.
type InternalRoute ¶
type InternalRoute struct {
Index int
Name string // INTERNAL_ROUTE_N_NAME
Subdomain string // INTERNAL_ROUTE_N_SUBDOMAIN
Target string // INTERNAL_ROUTE_N_TARGET (e.g., hasura:8080)
RateZone string // INTERNAL_ROUTE_N_RATE_ZONE (default: general)
WebSocket bool // INTERNAL_ROUTE_N_WEBSOCKET
}
InternalRoute represents an internal Nginx route (INTERNAL_ROUTE_1..INTERNAL_ROUTE_20).
type LicenseConfig ¶ added in v1.0.6
type LicenseConfig struct {
PingURL string `env:"LICENSE_PING_URL"` // https://ping.nself.org
CachePath string `env:"LICENSE_CACHE_PATH"` // ~/.cache/nself/license.json
CheckInterval string `env:"LICENSE_CHECK_INTERVAL"` // 6h
OfflineMode bool `env:"LICENSE_OFFLINE_MODE"` // false
PublicKeyOverride string `env:"LICENSE_PUBLIC_KEY_OVERRIDE"` // hex-encoded Ed25519 pubkey for testing
SunsetAt time.Time `env:"LICENSE_SUNSET_AT"` // optional hard cutoff; zero = no sunset
}
LicenseConfig holds license validation and grace period configuration.
Offline grace-period lengths are NOT configurable here: they are fixed constants in internal/license/grace.go (GraceSoftThreshold, GraceHardThreshold), by design — see checker.go's exposure rationale (grace.go's GraceHardThreshold comment). A prior GraceDays field (env:"LICENSE_GRACE_DAYS") was declared here and referenced in a grace.go comment claiming the ladder was configurable, but nothing in the codebase ever read it. Both were removed together, P6-E12-W4-S4-T2, 2026-09.
type LoadOptions ¶ added in v1.4.13
type LoadOptions struct {
// RemoteDeploy drops .env.local from the cascade: the build is for a
// remote host, and a developer's personal override must not reach it (the
// env file pushed to the host omits it too).
RemoteDeploy bool
}
LoadOptions are the caller's choices for one Load. They are explicit arguments, never read from the environment or kept in process state.
type MLflowConfig ¶
type MLflowConfig struct {
Enabled bool `env:"MLFLOW_ENABLED"`
Route string `env:"MLFLOW_ROUTE"` // mlflow.{BD} — read by ssl/domains.go, urls.go, doctor.go
Version string `env:"MLFLOW_VERSION"` // plugin-managed: populated by nself plugin install mlflow
Port int `env:"MLFLOW_PORT"` // read by doctor.go for port-conflict checks; plugin-managed: populated by nself plugin install mlflow
DBName string `env:"MLFLOW_DB_NAME"` // plugin-managed: populated by nself plugin install mlflow
ArtifactsBucket string `env:"MLFLOW_ARTIFACTS_BUCKET"` // plugin-managed: populated by nself plugin install mlflow
AuthEnabled bool `env:"MLFLOW_AUTH_ENABLED"` // plugin-managed: populated by nself plugin install mlflow
AuthUsername string `env:"MLFLOW_AUTH_USERNAME"` // plugin-managed: populated by nself plugin install mlflow
AuthPassword string `env:"MLFLOW_AUTH_PASSWORD"` // plugin-managed: populated by nself plugin install mlflow
}
MLflowConfig holds MLflow experiment tracking configuration. Compose generation is plugin-managed: nself plugin install mlflow Enabled, Route, and Port are read by ssl/domains.go, urls.go, and doctor.go. All other fields are consumed exclusively by the nself-mlflow plugin at install time.
type MailpitConfig ¶
type MailpitConfig struct {
Enabled bool `env:"MAILPIT_ENABLED"`
Version string `env:"MAILPIT_VERSION"` // latest
SMTPPort int `env:"MAILPIT_SMTP_PORT"` // 1025
UIPort int `env:"MAILPIT_UI_PORT"` // 8025
MaxMessages int `env:"MAILPIT_MAX_MESSAGES"` // 500
Route string `env:"MAILPIT_ROUTE"` // mail.{BD}
UIUser string `env:"MAILPIT_UI_USER"` // admin (default)
UIPassword string `env:"MAILPIT_UI_PASSWORD"`
}
MailpitConfig holds Mailpit local email testing configuration.
type MeiliSearchConfig ¶
type MeiliSearchConfig struct {
Version string `env:"MEILISEARCH_VERSION"` // v1.6
MasterKey string `env:"MEILISEARCH_MASTER_KEY"`
Env string `env:"MEILISEARCH_ENV"` // development
}
MeiliSearchConfig holds MeiliSearch-specific configuration.
type MinioConfig ¶
type MinioConfig struct {
Enabled bool `env:"MINIO_ENABLED"`
Version string `env:"MINIO_VERSION"` // latest
Port int `env:"MINIO_PORT"` // 9000
ConsolePort int `env:"MINIO_CONSOLE_PORT"` // 9001
RootUser string `env:"MINIO_ROOT_USER"` // minioadmin
RootPassword string `env:"MINIO_ROOT_PASSWORD"` // minioadmin
DefaultBuckets string `env:"MINIO_DEFAULT_BUCKETS"` // uploads,public,private,temp
Region string `env:"MINIO_REGION"` // us-east-1
S3AccessKey string `env:"S3_ACCESS_KEY"`
S3SecretKey string `env:"S3_SECRET_KEY"`
S3Bucket string `env:"S3_BUCKET"` // nself
StorageVersion string `env:"STORAGE_VERSION"` // 0.6.1
StorageRoute string `env:"STORAGE_ROUTE"` // storage.{BD}
ConsoleRoute string `env:"STORAGE_CONSOLE_ROUTE"` // storage-console.{BD}
MemLimit string `env:"MINIO_MEMORY"` // 1G
CPULimit string `env:"MINIO_CPU"` // 0.5
}
MinioConfig holds MinIO S3-compatible object storage configuration.
type MonitoringConfig ¶
type MonitoringConfig struct {
Enabled bool `env:"MONITORING_ENABLED"`
GrafanaEnabled bool `env:"GRAFANA_ENABLED"`
GrafanaRoute string `env:"GRAFANA_ROUTE"` // read by ssl/domains.go, urls.go, doctor.go
GrafanaAdminPassword string `env:"GRAFANA_ADMIN_PASSWORD"` // read by config/validator.go
PrometheusEnabled bool `env:"PROMETHEUS_ENABLED"` // plugin-managed: populated by nself plugin install monitoring
PrometheusPort int `env:"PROMETHEUS_PORT"` // plugin-managed: populated by nself plugin install monitoring
GrafanaPort int `env:"GRAFANA_PORT"` // read by urls.go and doctor.go for port display; plugin-managed: populated by nself plugin install monitoring
GrafanaAdminUser string `env:"GRAFANA_ADMIN_USER"` // plugin-managed: populated by nself plugin install monitoring
LokiEnabled bool `env:"LOKI_ENABLED"` // plugin-managed: populated by nself plugin install monitoring
LokiPort int `env:"LOKI_PORT"` // plugin-managed: populated by nself plugin install monitoring
PromtailEnabled bool `env:"PROMTAIL_ENABLED"` // plugin-managed: populated by nself plugin install monitoring
TempoEnabled bool `env:"TEMPO_ENABLED"` // plugin-managed: populated by nself plugin install monitoring
TempoPort int `env:"TEMPO_PORT"` // plugin-managed: populated by nself plugin install monitoring
AlertmanagerEnabled bool `env:"ALERTMANAGER_ENABLED"` // plugin-managed: populated by nself plugin install monitoring
AlertmanagerPort int `env:"ALERTMANAGER_PORT"` // plugin-managed: populated by nself plugin install monitoring
CadvisorEnabled bool `env:"CADVISOR_ENABLED"` // plugin-managed: populated by nself plugin install monitoring
CadvisorPort int `env:"CADVISOR_PORT"` // plugin-managed: populated by nself plugin install monitoring
NodeExporterEnabled bool `env:"NODE_EXPORTER_ENABLED"` // plugin-managed: populated by nself plugin install monitoring
NodeExporterPort int `env:"NODE_EXPORTER_PORT"` // plugin-managed: populated by nself plugin install monitoring
PGExporterEnabled bool `env:"POSTGRES_EXPORTER_ENABLED"` // plugin-managed: populated by nself plugin install monitoring
PGExporterPort int `env:"POSTGRES_EXPORTER_PORT"` // plugin-managed: populated by nself plugin install monitoring
RedisExporterEnabled bool `env:"REDIS_EXPORTER_ENABLED"` // plugin-managed: populated by nself plugin install monitoring
RedisExporterPort int `env:"REDIS_EXPORTER_PORT"` // plugin-managed: populated by nself plugin install monitoring
// S34 additions
PrometheusRetention string `env:"PROMETHEUS_RETENTION"` // e.g. "30d"
LokiHotDays int `env:"LOKI_HOT_DAYS"` // default 30
LokiColdDays int `env:"LOKI_COLD_DAYS"` // default 365
AlertmanagerPagerdutyKey string `env:"ALERTMANAGER_PAGERDUTY_KEY"` // PagerDuty integration key
// Watchdog
WatchdogEnabled bool `env:"WATCHDOG_ENABLED"`
WatchdogCircuitBreakerAttempts int `env:"WATCHDOG_CIRCUIT_BREAKER_ATTEMPTS"` // default 3
WatchdogCircuitBreakerWindow string `env:"WATCHDOG_CIRCUIT_BREAKER_WINDOW"` // default 10m
WatchdogEscalationWebhook string `env:"WATCHDOG_ESCALATION_WEBHOOK"`
// Queue/Jobs
QueueWorkersPerQueue int `env:"QUEUE_WORKERS_PER_QUEUE"` // default 2
QueueDLQAlertThreshold int `env:"QUEUE_DLQ_ALERT_THRESHOLD"` // default 100
// Promotion
PromoteRequiresTwoApprovers bool `env:"PROMOTE_REQUIRES_TWO_APPROVERS"`
}
MonitoringConfig holds monitoring stack configuration. Compose generation is plugin-managed: nself plugin install monitoring Enabled, GrafanaEnabled, GrafanaRoute, GrafanaAdminPassword, and GrafanaPort are read by ssl/domains.go (SSL SANs), config/validator.go (password check), urls.go, and doctor.go. All other fields are consumed exclusively by the nself-monitoring plugin at install time.
type NginxConfig ¶
type NginxConfig struct {
Version string `env:"NGINX_VERSION"` // alpine
HTTPPort int `env:"NGINX_HTTP_PORT"` // 80
SSLPort int `env:"NGINX_HTTPS_PORT"` // 443
MaxBody string `env:"NGINX_CLIENT_MAX_BODY_SIZE"` // 100M
BindIP string `env:"NGINX_BIND_IP"` // computed: 127.0.0.1 (dev) or 0.0.0.0 (prod) — overridable
AuthRateLimit string `env:"AUTH_RATE_LIMIT"` // 30r/m
RateLimitAPI string `env:"RATE_LIMIT_API_RPS"` // 30
RateLimitAuth string `env:"RATE_LIMIT_AUTH_RPS"` // 5
RateLimitAI string `env:"RATE_LIMIT_AI_RPS"` // 10
// FrontedBy names the stack whose nginx fronts this project's domains
// (e.g. "nself-web"), when this project has no ingress nginx of its own.
// Empty (the default) means this stack runs its own nginx, unchanged
// from every prior release.
//
// When set, `nself build` omits the nginx service from
// docker-compose.yml entirely and excludes it from the service counts
// `nself status`/`nself start` expect. Without this, a stack meant to
// sit behind another stack's nginx still generates and tries to start
// its own nginx container, which fails to bind 80/443 (already held by
// the fronting stack's nginx via docker-proxy) and then sits forever as
// one unhealthy service nself status can never clear ("6/7" on ntask's
// staging deploy, 2026-09-03, fronted by nself-web).
//
// This flag only removes the container that can never run. It does NOT
// wire this project's containers onto the fronting stack's Docker
// network — the fronting nginx's `resolver 127.0.0.11` can only reach
// container names on a network it is attached to, so making
// auth.task.staging.nself.org resolve ntask_auth still requires the
// operator to attach the two projects' networks (or declare one as
// external in both compose files) by hand. That is a bigger, riskier
// change — automatically rewriting a Docker network topology from a
// per-project flag — and belongs in a follow-up once the desired shape
// of that wiring is decided.
FrontedBy string `env:"NGINX_FRONTED_BY"`
}
NginxConfig holds Nginx reverse proxy configuration.
type PgBouncerConfig ¶ added in v1.0.9
type PgBouncerConfig struct {
Enabled bool `env:"PGBOUNCER_ENABLED"`
Port int `env:"PGBOUNCER_PORT"` // 6432
PoolMode string `env:"PGBOUNCER_POOL_MODE"` // session, transaction, statement
MaxClientConn int `env:"PGBOUNCER_MAX_CLIENT_CONN"` // 100
DefaultPoolSize int `env:"PGBOUNCER_DEFAULT_POOL_SIZE"` // 25
MinPoolSize int `env:"PGBOUNCER_MIN_POOL_SIZE"` // 5
ReservePoolSize int `env:"PGBOUNCER_RESERVE_POOL_SIZE"` // 5
ServerIdleTimeout int `env:"PGBOUNCER_SERVER_IDLE_TIMEOUT"` // 600
LogConnections bool `env:"PGBOUNCER_LOG_CONNECTIONS"` // false
LogDisconnections bool `env:"PGBOUNCER_LOG_DISCONNECTIONS"` // false
AdminUsers string `env:"PGBOUNCER_ADMIN_USERS"` // postgres
StatsUsers string `env:"PGBOUNCER_STATS_USERS"` // postgres
}
PgBouncerConfig holds connection pooler configuration.
type PluginProConfig ¶
type PluginProConfig struct {
NotifySecret string `env:"NOTIFY_INTERNAL_SECRET"`
NotifyPort int `env:"NOTIFY_PORT"` // 3712
NotifyVAPIDPub string `env:"NOTIFY_VAPID_PUBLIC_KEY"`
NotifyVAPIDPriv string `env:"NOTIFY_VAPID_PRIVATE_KEY"`
NotifyRoute string `env:"NOTIFY_ROUTE"`
CronSecret string `env:"CRON_INTERNAL_SECRET"`
CronPort int `env:"CRON_PORT"` // 3713
CronRetention int `env:"CRON_RETENTION_DAYS"` // 90
AIMemLimit string `env:"PLUGIN_AI_MEMORY_LIMIT"` // 1g
AICPULimit string `env:"PLUGIN_AI_CPU_LIMIT"` // 1.0
MuxMemLimit string `env:"PLUGIN_MUX_MEMORY_LIMIT"` // 512m
MuxCPULimit string `env:"PLUGIN_MUX_CPU_LIMIT"` // 0.5
ClawMemLimit string `env:"PLUGIN_CLAW_MEMORY_LIMIT"` // 512m
ClawCPULimit string `env:"PLUGIN_CLAW_CPU_LIMIT"` // 0.5
DefaultMemLimit string `env:"PLUGIN_DEFAULT_MEMORY_LIMIT"` // 512m
DefaultCPULimit string `env:"PLUGIN_DEFAULT_CPU_LIMIT"` // 0.5
}
PluginProConfig holds per-plugin configuration for Pro plugins.
type PluginSystemConfig ¶
type PluginSystemConfig struct {
Dir string `env:"NSELF_PLUGIN_DIR"` // ~/.nself/plugins
Cache string `env:"NSELF_PLUGIN_CACHE"` // ~/.nself/cache/plugins
Registry string `env:"NSELF_PLUGIN_REGISTRY"` // https://plugins.nself.org
CacheTTL int `env:"NSELF_REGISTRY_CACHE_TTL"` // 300
LicenseKey string `env:"NSELF_PLUGIN_LICENSE_KEY"`
SkipVerify bool `env:"NSELF_LICENSE_SKIP_VERIFY"`
PingURL string `env:"NSELF_PING_API_URL"` // https://ping.nself.org
PricingURL string `env:"NSELF_PRICING_URL"` // https://nself.org/pricing
InternalSecret string `env:"PLUGIN_INTERNAL_SECRET"`
}
PluginSystemConfig holds plugin system management configuration.
type PostgresConfig ¶
type PostgresConfig struct {
Version string `env:"POSTGRES_VERSION"` // 16-alpine
// Image, when set, is the exact postgres image nself build emits and wins
// over every other resolution (see compose.ResolvePostgresImage). Set this
// to pin a running image (e.g. pgvector/pgvector:pg16) across regens
// without relying on POSTGRES_EXTENSIONS inference (cli#384).
Image string `env:"POSTGRES_IMAGE"`
Host string `env:"POSTGRES_HOST"` // postgres (container name)
Port int `env:"POSTGRES_PORT"` // 5432
DB string `env:"POSTGRES_DB"` // nself
User string `env:"POSTGRES_USER"` // postgres
Password string `env:"POSTGRES_PASSWORD"`
Extensions []string `env:"POSTGRES_EXTENSIONS"` // comma-separated list
ExposePort string `env:"POSTGRES_EXPOSE_PORT"` // auto, true, false
MemLimit string `env:"POSTGRES_MEM_LIMIT"` // 2g
CPULimit string `env:"POSTGRES_CPU_LIMIT"` // 2.0
// MaxConnections overrides Postgres max_connections. The stock default of
// 100 exhausts under a multi-service stack (PERF-POOL-01); nself raises it.
MaxConnections int `env:"POSTGRES_MAX_CONNECTIONS"` // default 200
}
PostgresConfig holds PostgreSQL database configuration.
type RedisConfig ¶
type RedisConfig struct {
Enabled bool `env:"REDIS_ENABLED"`
Version string `env:"REDIS_VERSION"` // 7-alpine
Port int `env:"REDIS_PORT"` // 6379
Password string `env:"REDIS_PASSWORD"` // empty = no auth
Memory string `env:"REDIS_MEMORY"` // 512M
CPU string `env:"REDIS_CPU"` // 0.5
PoolSize int `env:"REDIS_POOL_SIZE"` // 50 (prod default); use 20 for dev
}
RedisConfig holds Redis cache/queue configuration.
type RemoteSchema ¶
RemoteSchema represents a Hasura Remote Schema configuration.
type SearchConfig ¶
type SearchConfig struct {
Enabled bool `env:"SEARCH_ENABLED"`
Engine string `env:"SEARCH_ENGINE"` // meilisearch, typesense, etc.
Port int `env:"SEARCH_PORT"` // auto from provider
APIKey string `env:"SEARCH_API_KEY"` // auto-generated if unset
Route string `env:"SEARCH_ROUTE"` // search.{BD}
IndexPrefix string `env:"SEARCH_INDEX_PREFIX"`
AutoIndex bool `env:"SEARCH_AUTO_INDEX"` // true
Language string `env:"SEARCH_LANGUAGE"` // en
// Provider-specific (only populated for active provider)
MeiliSearch MeiliSearchConfig
Typesense TypesenseConfig
Elasticsearch ElasticsearchConfig
}
SearchConfig holds search engine configuration (provider-agnostic).
type SecretsConfig ¶ added in v1.0.6
type SecretsConfig struct {
AgeKeyPath string `env:"SECRETS_AGE_KEY_PATH"` // ~/.config/nself/age-key.txt
DeployAgeKey string `env:"DEPLOY_AGE_KEY"` // raw age private key for CI/CD
}
SecretsConfig holds secrets management configuration.
type Setter ¶ added in v1.4.13
type Setter struct {
// File is the cascade file name relative to the project directory.
File string
// Value is the value the file sets, in clear.
Value string
}
Setter is one existing cascade file that sets the key.
type TelemetryPreference ¶ added in v1.0.11
type TelemetryPreference struct {
// Enabled is the stored preference from config.toml.
// This value is overridden to false when NSELF_TELEMETRY_OPT_OUT=1 is set.
Enabled bool
// Source describes where the value came from: "env", "config", or "default".
Source string
}
TelemetryPreference describes the user's telemetry opt-in/out state.
func GetTelemetryPreference ¶ added in v1.0.11
func GetTelemetryPreference() TelemetryPreference
GetTelemetryPreference returns the effective telemetry preference. NSELF_TELEMETRY_OPT_OUT=1 beats the config file, which beats the default.
type TenantConfig ¶ added in v1.0.6
type TenantConfig struct {
DefaultPlan string `env:"TENANT_DEFAULT_PLAN"` // basic
DestroyBackupRetainDays int `env:"TENANT_DESTROY_BACKUP_RETAIN_DAYS"` // 90
StripeSecretKey string `env:"STRIPE_SECRET_KEY"`
StripeWebhookSecret string `env:"STRIPE_WEBHOOK_SECRET"`
StripeAPIVersion string `env:"STRIPE_API_VERSION"` // 2024-04-10
}
TenantConfig holds multi-tenancy and billing configuration.
type TypesenseConfig ¶
type TypesenseConfig struct {
Version string `env:"TYPESENSE_VERSION"` // 27.1
APIKey string `env:"TYPESENSE_API_KEY"`
EnableCORS bool `env:"TYPESENSE_ENABLE_CORS"`
LogLevel string `env:"TYPESENSE_LOG_LEVEL"`
NumMemoryShards int `env:"TYPESENSE_NUM_MEMORY_SHARDS"`
SnapshotIntervalS int `env:"TYPESENSE_SNAPSHOT_INTERVAL_SECONDS"`
}
TypesenseConfig holds Typesense-specific configuration.
type ValidatorFunc ¶
ValidatorFunc pairs a human-readable name with a validation function. The name is used in error messages and test assertions.
type ValidatorResult ¶
ValidatorResult pairs a validator name with its outcome.
func RunAllWithResults ¶
func RunAllWithResults(cfg *Config) []ValidatorResult
RunAllWithResults executes every registered validator and returns one ValidatorResult per validator, preserving the original name and error. Unlike RunAll, no error is returned — the caller inspects results directly.
Source Files
¶
- backup_heartbeat.go
- cascade.go
- custom_services.go
- custom_services_v2.go
- custom_services_validate.go
- defaults.go
- defaults_auth_nginx.go
- defaults_helpers.go
- defaults_ops.go
- defaults_plugins_backup.go
- defaults_postgres_hasura.go
- defaults_storage_services.go
- explain.go
- exports.go
- frontend_apps.go
- helpers.go
- helpers_unknown_vars.go
- host_database_url.go
- interfaces.go
- loader.go
- loader_helpers.go
- loader_known_vars.go
- loader_known_vars_core.go
- loader_known_vars_ops.go
- loader_known_vars_search.go
- loader_known_vars_storage.go
- loader_parse_env.go
- loader_parse_env_core.go
- loader_parse_env_ops.go
- loader_parse_env_storage.go
- monorepo.go
- registry.go
- remote_schemas.go
- runtime.go
- sanitize.go
- secretkeys.go
- telemetry.go
- types.go
- types_datastores.go
- types_ops_plugins.go
- types_services.go
- url_userinfo.go
- validator.go
- validator_placeholder_secrets.go
- validator_ports_services.go