Full rebrand across cosmetic branding, code identifiers, and infrastructure/data-plane naming, using the supplied Cairn OBS logo package. Cosmetic: favicon/logo swap (also closes a stale license-audit finding -- the old favicon was SvelteKit's unreplaced scaffold logo), new centered welcome landing page, larger/legible sidebar logo, page titles, CLAUDE.md/README/docs prose. Code identifiers: Go module path github.com/sentry/sentry -> github.com/cairnobs/cairnobs across all 13 modules and ~91 files (protoc regenerated); Rust crates sentry-agent/sentry-parser/sentry-search -> cairnobs-*; CLI sentryctl -> cairnobsctl; Terraform provider fully renamed (sentry_dashboard etc. -> cairnobs_dashboard, provider type, env vars); every session/auth cookie name; agent config paths and Windows service identity. Deliberately preserved: the gRPC wire protocol's protobuf packages (sentry.logs.v1, sentry.agent.v1) and their Go import directory (proto/sentry/...) -- renaming the wire-level package would break every currently-deployed agent binary (confirmed two real hosts, including mail.inbuxa.com, are actively streaming through this exact contract) until rebuilt and redeployed in lockstep with an ingest cutover. Only the Go module path wrapping the generated code changes. Infrastructure: every docker-compose container name (root and three component-level compose files); the Helm chart (directory, Chart.yaml, named-template helpers, all templates, values.yaml image repos); Kubernetes Operator (CRD group sentry.io -> cairnobs.io, both CRD YAML files, Go identifiers, RBAC markers); the coupled enterprise/tenantcrd package. Caught and fixed real path-coupling bugs along the way: the Helm chart's search/ingest volume mounts and the dev-only-credential detection constant vs. docker-compose.yml's literal values had to move together or a security warning would have silently stopped firing. Data plane: Postgres database sentry_metadata -> cairnobs_metadata and role sentry -> cairnobs; ClickHouse database sentry -> cairnobs; Kafka topic sentry.logs.raw -> cairnobs.logs.raw and its consumer groups. Source-level defaults, docker-compose.yml, and every migrate.sh/ provision script default updated together; already-applied migration files left untouched per this repo's immutable-migration convention. Verified at every layer: all 13 Go modules build/vet/test clean, both Rust workspaces (agent, search) build/clippy/test clean, npm run check/ build clean, docker compose config validates on all four compose files. Live-verified against a real docker stack multiple times through this work, including a final fresh-volume run confirming the actual renamed Postgres database/role, ClickHouse database, and Kafka topic all work end to end with a real login and query, zero console errors.
182 lines
7.2 KiB
Go
182 lines
7.2 KiB
Go
// Package config loads api's configuration from environment variables,
|
|
// same convention as /ingest: no config file format for Phase 0.
|
|
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
type Config struct {
|
|
HTTPListenAddr string
|
|
ClickHouse ClickHouseConfig
|
|
Postgres PostgresConfig
|
|
SearchGRPCAddr string
|
|
QueryTimeout time.Duration
|
|
CORSAllowedOrigin string
|
|
EnterpriseAuthURL string
|
|
AI AIConfig
|
|
LocalAuth LocalAuthConfig
|
|
}
|
|
|
|
// LocalAuthConfig gates single-tenant mode's local username/password
|
|
// login (see api/localauth) -- off unless Enabled, same "off unless
|
|
// configured" convention as EnterpriseAuthURL/AI.OllamaBaseURL. Only
|
|
// meaningful when EnterpriseAuthURL is empty -- a deployment with real
|
|
// SSO configured has no use for a second, local auth mechanism, and
|
|
// main.go's authorizer selection treats EnterpriseAuthURL as taking
|
|
// priority if both were somehow set.
|
|
type LocalAuthConfig struct {
|
|
Enabled bool
|
|
// SessionTTL is deliberately long (30 days default) compared to
|
|
// enterprise/'s session TTL -- there's no SSO round-trip here to
|
|
// silently refresh a session against, so a short TTL would just mean
|
|
// re-entering a password often on a self-hosted single-operator tool.
|
|
SessionTTL time.Duration
|
|
// CookieDomain empty means a host-only cookie (fine for local dev,
|
|
// where web/api are both localhost:<port>). Set to e.g.
|
|
// ".sentry.example.com" in production so the cookie is also sent to
|
|
// api.sentry.example.com/alerting.sentry.example.com.
|
|
CookieDomain string
|
|
// CookieSecure defaults true (never sent over plain HTTP) --
|
|
// deliberately opt-out via LOCAL_AUTH_COOKIE_SECURE=false, only
|
|
// useful to test the login flow locally over http://localhost.
|
|
CookieSecure bool
|
|
}
|
|
|
|
// AIConfig gates Phase 7's AI-assisted query features (Track A/B) --
|
|
// off unless OllamaBaseURL is set, same "off unless configured"
|
|
// convention as EnterpriseAuthURL and everything else optional in this
|
|
// codebase. OllamaFastModel is the per-operation override for
|
|
// Complete's tight latency budget (/docs/phase-7-ai-design.md's
|
|
// per-operation provider/model config) -- empty means Complete uses
|
|
// OllamaModel too, same as every other operation.
|
|
type AIConfig struct {
|
|
OllamaBaseURL string
|
|
OllamaModel string
|
|
OllamaFastModel string
|
|
}
|
|
|
|
type ClickHouseConfig struct {
|
|
Addr string
|
|
Database string
|
|
Username string
|
|
Password string
|
|
}
|
|
|
|
// PostgresConfig is the control-plane metadata store (dashboards, panels
|
|
// -- see /docs/phase-3-dashboard-design.md), distinct from ClickHouse
|
|
// which remains log-data-only.
|
|
type PostgresConfig struct {
|
|
Addr string
|
|
Database string
|
|
Username string
|
|
Password string
|
|
}
|
|
|
|
// devOnlyCredential is docker-compose.yml's zero-config default for
|
|
// every Postgres/ClickHouse password in this repo -- genuinely fine for
|
|
// local dev (that's the whole point of a zero-config default), but a
|
|
// real deployment that skips docker-compose.override.yml would
|
|
// otherwise go live with a password anyone can read straight off
|
|
// GitHub. See DevCredentialWarnings.
|
|
const devOnlyCredential = "cairnobs-dev-only"
|
|
|
|
// DevCredentialWarnings reports which configured credentials still
|
|
// equal docker-compose.yml's literal dev-only default -- cmd/api/main.go
|
|
// logs each one loudly at startup. Deliberately a warning, not a
|
|
// startup-refusing error: local dev's documented zero-config path is
|
|
// exactly "run docker-compose.yml with no override," which legitimately
|
|
// leaves every password at this literal value, so hard-failing here
|
|
// would break that path rather than only catching real deployments that
|
|
// forgot to override it.
|
|
func (c Config) DevCredentialWarnings() []string {
|
|
var warnings []string
|
|
if c.ClickHouse.Password == devOnlyCredential {
|
|
warnings = append(warnings, "CLICKHOUSE_PASSWORD is still the default dev-only value -- set a real password via docker-compose.override.yml (or your deployment's equivalent) before this is reachable outside local dev")
|
|
}
|
|
if c.Postgres.Password == devOnlyCredential {
|
|
warnings = append(warnings, "POSTGRES_PASSWORD is still the default dev-only value -- set a real password via docker-compose.override.yml (or your deployment's equivalent) before this is reachable outside local dev")
|
|
}
|
|
return warnings
|
|
}
|
|
|
|
func Load() (Config, error) {
|
|
cfg := Config{
|
|
HTTPListenAddr: getenv("HTTP_LISTEN_ADDR", ":8080"),
|
|
ClickHouse: ClickHouseConfig{
|
|
Addr: getenv("CLICKHOUSE_ADDR", "localhost:9000"),
|
|
Database: getenv("CLICKHOUSE_DATABASE", "sentry"),
|
|
Username: getenv("CLICKHOUSE_USERNAME", "default"),
|
|
Password: getenv("CLICKHOUSE_PASSWORD", ""),
|
|
},
|
|
Postgres: PostgresConfig{
|
|
Addr: getenv("POSTGRES_ADDR", "localhost:5432"),
|
|
Database: getenv("POSTGRES_DATABASE", "sentry_metadata"),
|
|
Username: getenv("POSTGRES_USERNAME", "sentry"),
|
|
Password: getenv("POSTGRES_PASSWORD", ""),
|
|
},
|
|
// Search service's gRPC address (see /search) -- default matches
|
|
// /search's own default GRPC_LISTEN_ADDR.
|
|
SearchGRPCAddr: getenv("SEARCH_GRPC_ADDR", "localhost:50052"),
|
|
// Phase 0 has no auth, so this is wide open by default to keep
|
|
// the local SvelteKit dev server (a different origin/port)
|
|
// working out of the box. Tighten before this is ever reachable
|
|
// from outside a trusted dev/homelab network.
|
|
CORSAllowedOrigin: getenv("CORS_ALLOWED_ORIGIN", "*"),
|
|
// Empty by default -- a single-tenant deployment without
|
|
// enterprise/ configured runs with authz.RequireRole* as a
|
|
// no-op, matching Phase 0-3 behavior. Set to enterprise-auth's
|
|
// base URL (e.g. "http://enterprise-auth:8081") to turn on
|
|
// real session/service-token enforcement.
|
|
EnterpriseAuthURL: getenv("ENTERPRISE_AUTH_URL", ""),
|
|
// Empty OllamaBaseURL means AI features are entirely disabled --
|
|
// /ai/* routes aren't even registered (see main.go), matching
|
|
// "no cloud dependency required for the default deployment" and,
|
|
// by the same reasoning, no *local* model dependency forced on a
|
|
// deployment that doesn't want one either. Model names default to
|
|
// the recommendation confirmed in /docs/phase-7-ai-design.md.
|
|
AI: AIConfig{
|
|
OllamaBaseURL: getenv("OLLAMA_BASE_URL", ""),
|
|
OllamaModel: getenv("OLLAMA_MODEL", "qwen2.5-coder:7b"),
|
|
OllamaFastModel: getenv("OLLAMA_FAST_MODEL", "qwen2.5-coder:1.5b"),
|
|
},
|
|
}
|
|
|
|
timeoutSec, err := strconv.Atoi(getenv("QUERY_TIMEOUT_SECONDS", "30"))
|
|
if err != nil {
|
|
return Config{}, fmt.Errorf("QUERY_TIMEOUT_SECONDS: %w", err)
|
|
}
|
|
cfg.QueryTimeout = time.Duration(timeoutSec) * time.Second
|
|
|
|
localAuthEnabled, err := strconv.ParseBool(getenv("LOCAL_AUTH_ENABLED", "false"))
|
|
if err != nil {
|
|
return Config{}, fmt.Errorf("LOCAL_AUTH_ENABLED: %w", err)
|
|
}
|
|
sessionTTLHours, err := strconv.Atoi(getenv("LOCAL_SESSION_TTL_HOURS", "720")) // 30 days
|
|
if err != nil {
|
|
return Config{}, fmt.Errorf("LOCAL_SESSION_TTL_HOURS: %w", err)
|
|
}
|
|
cookieSecure, err := strconv.ParseBool(getenv("LOCAL_AUTH_COOKIE_SECURE", "true"))
|
|
if err != nil {
|
|
return Config{}, fmt.Errorf("LOCAL_AUTH_COOKIE_SECURE: %w", err)
|
|
}
|
|
cfg.LocalAuth = LocalAuthConfig{
|
|
Enabled: localAuthEnabled,
|
|
SessionTTL: time.Duration(sessionTTLHours) * time.Hour,
|
|
CookieDomain: getenv("SESSION_COOKIE_DOMAIN", ""),
|
|
CookieSecure: cookieSecure,
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
func getenv(key, fallback string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return v
|
|
}
|
|
return fallback
|
|
}
|