Something went wrong. Try again.
An event companion and onboarding experience for the ATmosphere (alpha) atmo.quest
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139package config
import ( "fmt" "log/slog" "os" "strings" "sync"
"github.com/joho/godotenv")
type Environment string
const ( Dev Environment = "dev" Prod Environment = "prod")
type Config struct { Environment Environment Host string Port string LogLevel slog.Level
// SessionSecret is the HMAC key for gorilla cookie sessions. Required // in prod; defaulted in dev so getting started is friction-free. SessionSecret string
// PublicURL is the externally-visible origin of this server, used to // derive OAuth client_id, redirect URIs, and cookie security defaults. // Examples: "http://127.0.0.1:8080" (dev), "https://atmo.quest" (prod). PublicURL string
// DatabaseURL is the SQLite DSN for the local app DB (admin flags, event // cache, OAuth sessions, etc.). Example: // file:data/atmoquest.db?_pragma=journal_mode(WAL)&_pragma=foreign_keys(ON) DatabaseURL string
// OAuthPrivateKeyPath is the on-disk location of the ES256 (P-256) private // key used to sign client_assertion JWTs when running as a confidential // OAuth client. Auto-generated on first run if the file is missing. // Ignored when IsLocalhost() is true (loopback dev uses a public client). OAuthPrivateKeyPath string
// TapWSEndpoint is the WebSocket URL of the self-hosted Bluesky Tap // service, in collection-signal mode for quest.atmo.connection. When empty // (the default, incl. dev/tests) the Tap consumer is disabled and // reciprocity falls back to the login-drain queue. Example: // ws://127.0.0.1:9000/subscribe TapWSEndpoint string
// TapAuthToken is an optional bearer token sent when connecting to Tap. // Leave empty if Tap is unauthenticated (e.g. on a private network). TapAuthToken string}
var ( Global *Config once sync.Once)
func init() { once.Do(func() { Global = Load() })}
func getEnv(key, fallback string) string { if val, ok := os.LookupEnv(key); ok { return val } return fallback}
func loadBase() *Config { godotenv.Load()
host := getEnv("HOST", "0.0.0.0") port := getEnv("PORT", "8080") defaultPublicURL := fmt.Sprintf("http://127.0.0.1:%s", port)
return &Config{ Host: host, Port: port, LogLevel: func() slog.Level { switch os.Getenv("LOG_LEVEL") { case "DEBUG": return slog.LevelDebug case "INFO": return slog.LevelInfo case "WARN": return slog.LevelWarn case "ERROR": return slog.LevelError default: return slog.LevelInfo } }(), SessionSecret: getEnv("SESSION_SECRET", "session-secret"), PublicURL: getEnv("PUBLIC_URL", defaultPublicURL), DatabaseURL: getEnv("DATABASE_URL", "file:data/atmoquest.db?_pragma=journal_mode(WAL)&_pragma=foreign_keys(ON)&_pragma=busy_timeout(5000)"), OAuthPrivateKeyPath: getEnv("OAUTH_PRIVATE_KEY_PATH", "data/oauth_key.pem"), TapWSEndpoint: getEnv("TAP_WS_ENDPOINT", ""), TapAuthToken: getEnv("TAP_AUTH_TOKEN", ""), }}
// Validate enforces invariants that aren't safe to enforce in init(). Called// from main() so misconfiguration surfaces as a clean fatal error with a// message, not a mid-request panic.func (c *Config) Validate() error { if c.DatabaseURL == "" { return fmt.Errorf("DATABASE_URL is required") } if c.PublicURL == "" { return fmt.Errorf("PUBLIC_URL is required") } if c.Environment == Prod { if c.SessionSecret == "" || c.SessionSecret == "session-secret" { return fmt.Errorf("SESSION_SECRET must be set to a real secret in production") } } return nil}
// IsLocalhost reports whether PublicURL is a loopback origin. When true the// OAuth client runs in loopback/public mode (no client_assertion, no signing// key) because Authorization Servers reject http:// client_ids elsewhere.func (c *Config) IsLocalhost() bool { u := c.PublicURL return strings.HasPrefix(u, "http://localhost") || strings.HasPrefix(u, "http://127.0.0.1")}
// IsSecure reports whether PublicURL is https — used to decide cookie Secure flag.func (c *Config) IsSecure() bool { return strings.HasPrefix(c.PublicURL, "https://")}