Documentation
¶
Index ¶
Constants ¶
const ( DefaultJWTIssuer = "llmsafespaces" DefaultJWTAudience = "llmsafespaces" )
DefaultJWTIssuer / DefaultJWTAudience are the iss/aud claims minted on every JWT and validated on every parse, when the operator hasn't set them explicitly. Defaults keep out-of-the-box deploys working; operators running multiple LLMSafeSpaces instances that should not accept each other's tokens set their own values via Helm or env.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AWSKMSConfig ¶ added in v0.3.0
type AWSKMSConfig struct {
Region string `mapstructure:"region"`
CredentialsFile string `mapstructure:"credentialsFile"`
KeyArns map[string]string `mapstructure:"keyArns"`
}
AWSKMSConfig holds AWS KMS-specific provider configuration. CredentialsFile is the path to an AWS shared-credentials file mounted from a K8s Secret (D2: file-mount, not IRSA — narrower trust surface). Region is the AWS region of the configured keys. KeyArns maps purpose strings to KMS key ARNs; exactly three purposes are expected (D4): "providerCredentials", "orgCredentials", "masterKek".
type Config ¶
type Config struct {
Server struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
ShutdownTimeout time.Duration `mapstructure:"shutdownTimeout"`
// InferenceRelayURL is the CF Worker URL for free-tier inference relay (Epic 26).
// When set, ListModels remaps free-tier opencode models to providerID=opencode-relay.
InferenceRelayURL string `mapstructure:"inferenceRelayURL"`
} `mapstructure:"server"`
// Use the shared Kubernetes config
Kubernetes k8sconfig.KubernetesConfig `mapstructure:"kubernetes"`
Database struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
User string `mapstructure:"user"`
Password string `mapstructure:"password"`
Database string `mapstructure:"database"`
SSLMode string `mapstructure:"sslMode"`
MaxOpenConns int `mapstructure:"maxOpenConns"`
MaxIdleConns int `mapstructure:"maxIdleConns"`
ConnMaxLifetime time.Duration `mapstructure:"connMaxLifetime"`
} `mapstructure:"database"`
Redis struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
Password string `mapstructure:"password"`
DB int `mapstructure:"db"`
PoolSize int `mapstructure:"poolSize"`
} `mapstructure:"redis"`
Auth struct {
JWTSecret string `mapstructure:"jwtSecret"`
// JWTPreviousSecrets is the list of previous JWT signing keys
// retained for VALIDATION ONLY. Tokens signed with any entry
// here are still accepted; new tokens are always signed with
// JWTSecret. Operators rotate by:
// 1. Move current JWTSecret to head of JWTPreviousSecrets.
// 2. Set JWTSecret to a fresh random string.
// 3. Restart API; old sessions stay valid until they
// expire (TokenDuration), at which point the entry can
// be removed.
// Closes F1.7.5 (Epic 17). Set via env
// LLMSAFESPACES_AUTH_JWTPREVIOUSSECRETS as a comma-separated
// list, OR via the YAML key `jwtPreviousSecrets: [...]`.
JWTPreviousSecrets []string `mapstructure:"jwtPreviousSecrets"`
// JWTIssuer is the iss claim minted on every token and validated
// on every parse. Default "llmsafespaces". Set when deploying
// multiple LLMSafeSpaces instances that should not accept each
// other's tokens. Set via env LLMSAFESPACES_AUTH_JWTISSUER.
JWTIssuer string `mapstructure:"jwtIssuer"`
// JWTAudience is the aud claim minted on every token and validated
// on every parse. Default "llmsafespaces". Same deployment-shape
// rationale as JWTIssuer. Set via env
// LLMSAFESPACES_AUTH_JWTAUDIENCE.
JWTAudience string `mapstructure:"jwtAudience"`
TokenDuration time.Duration `mapstructure:"tokenDuration"`
APIKeyPrefix string `mapstructure:"apiKeyPrefix"`
CookieName string `mapstructure:"cookieName"`
RememberMeDuration time.Duration `mapstructure:"rememberMeDuration"`
RegistrationEnabled bool `mapstructure:"registrationEnabled"`
LockoutEnabled bool `mapstructure:"lockoutEnabled"`
LockoutAttempts int `mapstructure:"lockoutAttempts"`
LockoutDuration time.Duration `mapstructure:"lockoutDuration"`
APIKeyDEKTTL time.Duration `mapstructure:"apiKeyDEKTTL"`
} `mapstructure:"auth"`
Security struct {
AllowedOrigins []string `mapstructure:"allowedOrigins"`
AllowCredentials bool `mapstructure:"allowCredentials"`
RootKeyProvider string `mapstructure:"rootKeyProvider"`
SealedKeyPath string `mapstructure:"sealedKeyPath"`
PassphrasePath string `mapstructure:"passphrasePath"`
SkipMasterKeyWarning bool `mapstructure:"skipMasterKeyWarning"`
// KMS holds cloud KMS provider configuration (Epic 57 US-57.1).
// When RootKeyProvider is "aws-kms", KMS.AWS.KeyArns must contain
// ARNs for each purpose (providerCredentials, orgCredentials,
// masterKek). See design/stories/epic-57-rce-resistance-hardening/
// README.md D4 for the three-key model.
KMS KMSConfig `mapstructure:"kms"`
} `mapstructure:"security"`
Logging struct {
Level string `mapstructure:"level"`
Development bool `mapstructure:"development"`
Encoding string `mapstructure:"encoding"`
} `mapstructure:"logging"`
RateLimiting struct {
Enabled bool `mapstructure:"enabled"`
DefaultLimit int `mapstructure:"defaultLimit"`
DefaultWindow time.Duration `mapstructure:"defaultWindow"`
BurstSize int `mapstructure:"burstSize"`
Strategy string `mapstructure:"strategy"`
} `mapstructure:"rateLimiting"`
Proxy struct {
RequestBufferSizePerWorkspace int `mapstructure:"requestBufferSizePerWorkspace"`
RequestBufferTimeoutSeconds int `mapstructure:"requestBufferTimeoutSeconds"`
} `mapstructure:"proxy"`
// Billing holds Stripe configuration for org subscriptions (Epic 43).
// When SecretKey is empty, a NoopCheckoutProvider is used and the webhook
// endpoint rejects all deliveries — development/test mode.
Billing struct {
SecretKey string `mapstructure:"secretKey"`
WebhookSecret string `mapstructure:"webhookSecret"`
CheckoutSuccessURL string `mapstructure:"checkoutSuccessUrl"`
CheckoutCancelURL string `mapstructure:"checkoutCancelUrl"`
PortalReturnURL string `mapstructure:"portalReturnUrl"`
PlanPrices map[string]string `mapstructure:"planPrices"`
Meters map[string]string `mapstructure:"meters"`
} `mapstructure:"billing"`
// Email holds outbound email configuration (US-43.2 invitations). When
// Provider is empty or "noop", NoopProvider logs to stderr — no AWS
// dependency. "ses" requires AWS credentials via IRSA or env.
Email struct {
Provider string `mapstructure:"provider"`
SESRegion string `mapstructure:"sesRegion"`
FromAddress string `mapstructure:"fromAddress"`
BaseURL string `mapstructure:"baseUrl"`
} `mapstructure:"email"`
// OIDC holds SSO login wiring (US-43.10, D17). RedirectBaseURL is the
// origin the IdP redirects back to after authentication; the full callback
// is {RedirectBaseURL}/api/v1/auth/sso/:orgSlug/callback. When empty the
// start endpoint derives it from the incoming request. FrontendRedirectURL
// is where the browser lands after a successful or failed SSO callback.
OIDC struct {
RedirectBaseURL string `mapstructure:"redirectBaseUrl"`
FrontendRedirectURL string `mapstructure:"frontendRedirectUrl"`
// StateCookieName is the signed PKCE/state cookie name.
StateCookieName string `mapstructure:"stateCookieName"`
} `mapstructure:"oidc"`
// OrgSubdomainRouting holds Epic 54 (US-54.1) email-led login discovery
// config. When BaseDomain is non-empty, POST /auth/lookup redirects found
// users to https://<orgSlug>.<baseDomain>. When empty (subdomain routing
// disabled — the default), the lookup falls back to the direct SSO start
// URL (/api/v1/auth/sso/<slug>/start), which works regardless of chart
// config. CookieDomain is the value set on the lsp_session cookie's
// Domain attribute so the session survives root→subdomain redirects; it
// is consumed by the auth cookie setter (US-54.3 wires it through Helm).
OrgSubdomainRouting struct {
BaseDomain string `mapstructure:"baseDomain"`
CookieDomain string `mapstructure:"cookieDomain"`
} `mapstructure:"orgSubdomainRouting"`
// Turnstile is Cloudflare's CAPTCHA. When Enabled, the /register
// middleware validates the client-supplied cf-turnstile-response
// token against SecretKey using VerifyURL. When Enabled but SecretKey
// is empty, /register 500s at startup — fail-closed, don't run in
// a state where the operator thinks Turnstile is on but it isn't.
//
// When Enabled is false, the middleware is a no-op and /register
// accepts requests without a token. All chart-side wiring flows
// through Enabled+SecretKey+SiteKey; there's no partial-config state.
//
// Wired via ops-prod cluster-config → chart values → env:
// LLMSAFESPACES_TURNSTILE_ENABLED ("true" | unset)
// LLMSAFESPACES_TURNSTILE_SECRETKEY (secretKeyRef; never in ConfigMap)
// LLMSAFESPACES_TURNSTILE_VERIFYURL (defaults to Cloudflare production)
Turnstile struct {
Enabled bool `mapstructure:"enabled"`
SecretKey string `mapstructure:"secretKey"`
VerifyURL string `mapstructure:"verifyURL"`
} `mapstructure:"turnstile"`
// Workspace holds Helm-managed workspace defaults. Currently only
// DefaultStorageClass is exposed here: when non-empty the API pins the
// `workspace.defaultStorageClass` instance setting via SetHelmOverrides
// so admins cannot override it via the settings UI (Tier 1). When
// empty, the setting stays admin-mutable (Tier 2) with its DB-backed
// value (which itself defaults to "" — meaning "use cluster default SC").
//
// This pathway exists so operators running LLMSafeSpaces on clusters
// with dedicated low-durability StorageClasses (e.g. Longhorn 2-replica
// pools) can declare that choice in the Helm chart rather than having
// to remember to set it in the admin UI after every install/re-install.
//
// Wired via: values.yaml `workspace.defaultStorageClass` → API
// ConfigMap `workspace.defaultStorageClass` → this field → app.go
// SetHelmOverrides → workspace service Create path.
Workspace struct {
DefaultStorageClass string `mapstructure:"defaultStorageClass"`
} `mapstructure:"workspace"`
// Terminal holds the WebSocket terminal proxy's security config.
//
// AllowedOrigins governs the gorilla/websocket Upgrader's CheckOrigin:
// - Empty (default): same-origin only. Browser requests whose Origin
// does not match the API's own Host are rejected at upgrade.
// Non-browser clients (no Origin) are accepted; they authenticate
// via the single-use ticket, not cookies.
// - Contains "*": all origins accepted (the historical behavior).
// Operators who really want this must opt in explicitly.
// - Otherwise: same-origin requests plus anything in the list.
//
// Wired via: values.yaml `terminal.allowedOrigins` → API ConfigMap
// `terminal.allowedOrigins` → this field → app.go → NewTerminalHandler.
// Default empty so an out-of-the-box install is fail-closed against
// cross-site WebSocket hijacking (G35).
Terminal struct {
AllowedOrigins []string `mapstructure:"allowedOrigins"`
} `mapstructure:"terminal"`
}
Config represents the application configuration
type KMSConfig ¶ added in v0.3.0
type KMSConfig struct {
AWS AWSKMSConfig `mapstructure:"aws"`
}
KMSConfig holds cloud KMS provider configuration for the master KEK (Epic 57 US-57.1). When Security.RootKeyProvider is "aws-kms", KMS.AWS must be fully configured. When "gcp-kms" (future US-57.3), KMS.GCP. Otherwise this struct is ignored.