Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AIConfig ¶
type AIConfig struct {
Provider string `yaml:"provider"` // env: COREMETRY_AI_PROVIDER (anthropic|github|openai)
APIKey string `yaml:"api_key"` // env: COREMETRY_AI_API_KEY
Model string `yaml:"model"` // env: COREMETRY_AI_MODEL
BaseURL string `yaml:"base_url"` // env: COREMETRY_AI_BASE_URL (openai provider only)
}
AIConfig wires the optional AI Copilot. Three providers supported:
- "anthropic" (default): APIKey is an `sk-ant-…` key from the Anthropic console.
- "github": APIKey is a GitHub OAuth token (`ghu_…`) with Copilot access; Coremetry exchanges it for a session token and calls api.githubcopilot.com (OpenAI-compatible).
- "openai": Any OpenAI-compatible /v1/chat/completions endpoint. Drives self-hosted local LLMs (Ollama, LM Studio, vLLM, llama.cpp server) and the real OpenAI API. Set BaseURL to your local endpoint (e.g. http://ollama:11434/v1) and APIKey is optional for endpoints that don't gate on it.
Env config is the boot-time default; the admin Settings tab can override at runtime, persisted to system_settings.
type AuthConfig ¶
type AuthConfig struct {
JWTSecret string `yaml:"jwt_secret"` // HS256 key — set via COREMETRY_JWT_SECRET in prod
TokenTTL time.Duration `yaml:"token_ttl"` // session lifetime (default 24h)
InitialAdmin string `yaml:"initial_admin"` // email — seeded if users table is empty
InitialPassword string `yaml:"initial_password"` // bcrypted on first boot
// AdminReset (COREMETRY_ADMIN_RESET) makes the env creds authoritative for
// the bootstrap admin: when true, InitialAdmin's password is reconciled
// from InitialPassword on EVERY boot, even if the users table already has
// rows. Set once to recover a locked-out admin (then remove), or leave on
// for GitOps installs where the secret is the source of truth. Default off
// preserves the seed-once behaviour (UI password rotation survives restart).
AdminReset bool `yaml:"admin_reset"`
OIDC OIDCConfig `yaml:"oidc"`
TrustedHeader TrustedHeaderConfig `yaml:"trusted_header"`
// Demo only — when true, /api/auth/config exposes the initial admin
// credentials so the login page can pre-fill them. NEVER enable in
// production: anyone hitting /api/auth/config gets the admin password.
DemoMode bool `yaml:"demo_mode"`
}
AuthConfig drives JWT + initial-admin bootstrap. The HMAC secret is generated on first run if neither config nor env supply one — but that rotates every restart and invalidates sessions, so production deployments should set it explicitly.
type BackgroundConfig ¶ added in v0.4.95
type BackgroundConfig struct {
// Interval between anomaly-detector sweeps. Default 2m.
AnomalyInterval time.Duration `yaml:"anomaly_interval"`
// Recorder cadence + backfill window. Default 1m / 5m.
AnomalyRecordInterval time.Duration `yaml:"anomaly_record_interval"`
AnomalyRecordBackfill time.Duration `yaml:"anomaly_record_backfill"`
// SMTP settings refresh TTL. Default 30s — short so the
// operator's Settings change is picked up on the next
// alert send without a restart.
SMTPCacheTTL time.Duration `yaml:"smtp_cache_ttl"`
// Status probe ceiling — hard timeout on /api/status so a
// stuck CH/Redis client driver doesn't park the goroutine
// forever. Default 5s, well above the per-probe 2s timeout.
StatusProbeTimeout time.Duration `yaml:"status_probe_timeout"`
// LogAnomalyEnabled gates the worker-role log-pattern anomaly
// recorder + Drain template puller — the jobs that periodically
// QUERY the logstore (ES at billion-doc scale: curated-pattern
// _msearch, significant_text, sample pulls). Default true. Set
// COREMETRY_LOG_ANOMALY_ENABLED=false to silence that ES traffic
// when the operator doesn't want log-based anomalies. Metric
// anomaly detection (CH-backed) is unaffected.
LogAnomalyEnabled bool `yaml:"log_anomaly_enabled"`
}
BackgroundConfig controls the cadence of every internal worker loop — anomaly detector, recorder, status probes, etc. Default values match what was hard-coded in main.go pre-v0.4.95. Tuning these matters at scale: a 2-min detector tick on a busy stack adds up to non-trivial CH load, and operators on slow CH clusters want to back off; operators on demo deployments often want to crank them down so anomalies surface quickly.
Each field is a duration; zero means "use the default". The defaults() function applies them so a config file that names the section but omits a knob still gets the sensible value.
type CHConfig ¶
type CHConfig struct {
Addr string `yaml:"addr"` // comma-separated for cluster
Database string `yaml:"database"`
Username string `yaml:"username"`
Password string `yaml:"password"`
Secure bool `yaml:"secure"` // native-TLS (9440)
InsecureSkipVerify bool `yaml:"insecure_skip_verify"` // self-signed CA escape hatch
MaxOpenConns int `yaml:"max_open_conns"`
DialTimeout string `yaml:"dial_timeout"`
// ClusterName turns on Distributed-CH mode. When non-empty:
// - All DDL gets `ON CLUSTER <ClusterName>` so schema is
// applied across every node in the named ZK-coordinated
// cluster (cluster definition lives in CH's remote_servers).
// - High-volume tables (spans, logs, metric_points, profiles)
// are created as `<name>_local` ReplicatedMergeTree on each
// shard plus a `<name>` Distributed wrapper that fans out
// inserts and queries.
// - Materialized views feed the *_local tables; their
// Distributed wrappers re-export.
// When empty (default), single-node MergeTree behaviour is
// preserved exactly — existing deployments keep working.
ClusterName string `yaml:"cluster_name"`
// ReplicaPath is the ZooKeeper / Keeper prefix used for
// ReplicatedMergeTree path argument. {shard}/{replica} macros
// are appended automatically. Default: "/clickhouse/tables".
ReplicaPath string `yaml:"replica_path"`
// ShardKey: the SQL expression used as the Distributed shard
// key. Defaults to "rand()" — even distribution, no locality
// guarantee. Set to "cityHash64(trace_id)" if you want all
// spans of a trace to land on the same shard (faster joins,
// at the cost of slightly less even rows-per-shard).
ShardKey string `yaml:"shard_key"`
// AllowUnsetCluster (COREMETRY_CH_ALLOW_UNSET_CLUSTER) is the escape hatch
// for the genuinely-broken external-Distributed-unset state: `spans` is an
// external Distributed table but ClusterName is empty, so Coremetry can't
// own spans_local — MV insert-triggers never fire (empty dashboards) and
// op_group ALTERs can't reach the shards. By default boot HARD-ERRORS there
// with the cluster to set; set this true to run in degraded mode anyway
// (raw-spans reads only). v0.8.213.
AllowUnsetCluster bool `yaml:"allow_unset_cluster"`
}
CHConfig describes the ClickHouse connection. `Addr` accepts either a single endpoint or a comma-separated list of seeds (e.g. "ch1.example:9440,ch2.example:9440,ch3.example:9440,ch4.example:9440") — driver load-balances + fails over across them, so an external N-node cluster can be configured without an upstream LB.
`Secure` toggles native-TLS (port 9440); leave false for plain 9000. `InsecureSkipVerify` is the escape hatch for self-signed internal certs — set false in production once a CA bundle is in place.
type Config ¶
type Config struct {
Listen ListenConfig `yaml:"listen"`
ClickHouse CHConfig `yaml:"clickhouse"`
Retention RetentionConfig `yaml:"retention"`
Ingestion IngestionConfig `yaml:"ingestion"`
Auth AuthConfig `yaml:"auth"`
Redis RedisConfig `yaml:"redis"`
Logs LogsConfig `yaml:"logs"`
AI AIConfig `yaml:"ai"`
Background BackgroundConfig `yaml:"background"`
Exemplars ExemplarsConfig `yaml:"exemplars"`
// PublicURL is the operator-facing base URL of this
// Coremetry deployment (e.g. https://coremetry.bank.local).
// Notification bodies (Slack / Teams / Zoom / email /
// generic webhook) include deep-links to the relevant
// problem / anomaly / incident detail when set. Empty
// disables the linking — back-compat for deployments
// where the URL isn't reachable from the recipient's
// network.
PublicURL string `yaml:"public_url"`
}
type ESConfig ¶
type ESConfig struct {
Addresses []string `yaml:"addresses"`
Username string `yaml:"username"`
Password string `yaml:"password"`
APIKey string `yaml:"api_key"`
InsecureSkipVerify bool `yaml:"insecure_skip_verify"`
Index string `yaml:"index"`
// IndexTemplate (v0.8.231) narrows service-scoped queries to the
// concrete per-service index instead of fanning out over Index.
// Placeholders: {service} = the queried service name verbatim,
// {namespace} = the service's namespace resolved from span resource
// attributes (k8s.namespace.name / service.namespace); unresolved →
// "*". Example: "app-{service}.{namespace}". Empty = disabled.
IndexTemplate string `yaml:"index_template"` // env: COREMETRY_ES_INDEX_TEMPLATE
// MLEnabled turns on the v0.5.120 read-only ML anomaly job
// poller — Coremetry calls /_ml/anomaly_detectors and ingests
// significant records into anomaly_events so existing Elastic
// ML jobs surface on the /anomalies page alongside the native
// detectors. Read-only against Elastic.
MLEnabled bool `yaml:"ml_enabled"`
// MLMinScore overrides the per-record score threshold (default
// 75 — Elastic's "critical" band; lower brings more noise).
MLMinScore float64 `yaml:"ml_min_score"`
// Fields pins Coremetry's ES query to the operator's document
// mapping (field paths). Empty members fall back to the ECS-ish
// defaults in logstore (@timestamp / trace.id / span.id /
// service.name / message / log.level). Set these when the log
// pipeline uses different paths so Coremetry queries the right
// fields WITHOUT re-indexing — e.g. trace_id instead of trace.id.
Fields ESFieldsConfig `yaml:"fields"`
}
ESConfig mirrors logstore.ESConfig. Kept here so the config package stays the single source of truth for env-var bindings; main.go copies these fields into the logstore.ESConfig at construction time.
type ESFieldsConfig ¶ added in v0.8.228
type ESFieldsConfig struct {
Timestamp string `yaml:"timestamp"` // env: COREMETRY_ES_FIELD_TIMESTAMP (default @timestamp)
TraceID string `yaml:"trace_id"` // env: COREMETRY_ES_FIELD_TRACE_ID (default trace.id)
SpanID string `yaml:"span_id"` // env: COREMETRY_ES_FIELD_SPAN_ID (default span.id)
Service string `yaml:"service"` // env: COREMETRY_ES_FIELD_SERVICE (default service.name)
Message string `yaml:"message"` // env: COREMETRY_ES_FIELD_MESSAGE (default message)
SeverityText string `yaml:"severity_text"` // env: COREMETRY_ES_FIELD_SEVERITY_TEXT (default log.level)
SeverityNumber string `yaml:"severity_number"` // env: COREMETRY_ES_FIELD_SEVERITY_NUMBER (default "" — skipped)
// Environment (v0.8.400 — env-separation Phase 4): the document
// field the ?env= filter targets. Default "" = SELF-DISCOVER via a
// cached field_caps over the candidate shapes (logstore
// es_env_field.go); set only when the pipeline uses a custom path.
Environment string `yaml:"environment"` // env: COREMETRY_ES_FIELD_ENV (default "" — self-discover)
}
ESFieldsConfig is the env/yaml binding for the ES document field map. Mirrors logstore.ESFieldMap; main.go copies it across at construction. Each member maps to a COREMETRY_ES_FIELD_<NAME> env var (see Load). Leaving one empty keeps the logstore default.
type ExemplarsConfig ¶ added in v0.8.328
type ExemplarsConfig struct {
RequireTraceContext bool `yaml:"require_trace_context"`
// MaxPerSeriesPerMinute (v0.8.433, exemplar audit Faz C) — ingest-side
// cap: at most N exemplars per series_fingerprint per wall-clock
// minute; excess is dropped (intentional, counted separately from
// data loss). 0 = unlimited (the default and the pre-Faz-C behavior:
// exemplar volume is producer-bounded — SDKs keep ~1 per series per
// export — so most installs never need this). Set it when a
// misbehaving SDK or a custom exporter floods the exemplars table.
MaxPerSeriesPerMinute int `yaml:"max_per_series_per_minute"` // env: COREMETRY_EXEMPLARS_MAX_PER_SERIES_MIN
}
ExemplarsConfig gates OTLP metric-exemplar ingest (v0.8.328, cross-signal pivot). RequireTraceContext (yaml exemplars.require_trace_context, default TRUE) drops exemplars that carry no trace_id — a stored exemplar exists to be clicked through to its trace, so trace-less ones are dead weight unless the operator explicitly wants them.
Zero-value handling: the default lives in the `defaults` var (true) and Load unmarshals the YAML ON TOP of it — an absent key keeps true while an explicit `require_trace_context: false` is honoured. A post-load zero-fill (the retention-days pattern) would be WRONG for a bool: it can't tell "unset" from "operator said false". Same mechanism as Background.LogAnomalyEnabled.
type IngestionConfig ¶
type IngestionConfig struct {
BatchSize int `yaml:"batch_size"`
BufferSize int `yaml:"buffer_size"`
FlushInterval time.Duration `yaml:"flush_interval"`
Workers int `yaml:"workers"`
// ByteBudgetMB caps the APPROXIMATE megabytes EACH signal consumer
// may hold in memory — channel backlog + accumulating and in-flight
// batches (v0.8.355, HA audit 🟡#1). BufferSize alone bounds item
// COUNT; with fat items (15-25KB Java stack-trace log bodies) a
// 500k-item buffer behind a stalled ClickHouse is multi-GB and the
// kubelet OOMKill destroys ALL buffered signals — a counted drop
// loses less. ×5 math: the budget is PER consumer and there are 5
// (spans / logs / metrics / exemplars / span_links), so worst-case
// buffered memory ≈ 5 × ByteBudgetMB — the 512 default bounds it at
// ~2.5GB, sized for typical 4-8GB pods. Explicit 0 disables the
// byte cap (count-only, pre-v0.8.355 behavior).
ByteBudgetMB int `yaml:"byte_budget_mb"`
}
type ListenConfig ¶
type LogsConfig ¶
type LogsConfig struct {
Backend string `yaml:"backend"` // "clickhouse" (default) | "elasticsearch"
Elasticsearch ESConfig `yaml:"elasticsearch"`
}
LogsConfig picks which read backend serves /api/logs. Ingest still always writes to ClickHouse — this only changes the read path so an operator can point Coremetry at an external ES that their existing shipping pipeline already populates, without re-indexing.
backend: clickhouse → query the local CH `logs` table (default) backend: elasticsearch → query Elasticsearch via Elastic.Addresses
type OIDCConfig ¶
type OIDCConfig struct {
Enabled bool `yaml:"enabled"`
IssuerURL string `yaml:"issuer_url"` // e.g. https://accounts.google.com
ClientID string `yaml:"client_id"`
ClientSecret string `yaml:"client_secret"`
RedirectURL string `yaml:"redirect_url"` // public URL of /api/auth/oidc/callback
Scopes []string `yaml:"scopes"` // default: ["openid", "email", "profile"]
DisplayName string `yaml:"display_name"` // shown on login button (default: "SSO")
DefaultRole string `yaml:"default_role"` // role for first-time OIDC users (default: viewer)
AllowedDomains []string `yaml:"allowed_domains"` // optional email-domain whitelist (e.g. ["acme.com"])
}
OIDCConfig is fully optional — when Enabled is false, only local username/password auth is offered. Local auth is never disabled, even when OIDC is on, so admins always have a fallback path.
type RedisConfig ¶
type RedisConfig struct {
URL string `yaml:"url"` // e.g. "redis://localhost:6379/0"
}
RedisConfig is fully optional. When URL is empty Coremetry runs in single-instance mode: no cache (always misses) and the in-process "always-leader" lock — meaning background workers always run.
type RetentionConfig ¶
type TrustedHeaderConfig ¶ added in v0.4.90
type TrustedHeaderConfig struct {
Enabled bool `yaml:"enabled"`
// Header names — defaults match oauth2-proxy (which is the
// dominant implementation in the OAuth-proxy-fronts-app
// pattern). Operators using a different proxy override.
EmailHeader string `yaml:"email_header"` // default "X-Auth-Request-Email"
UserHeader string `yaml:"user_header"` // default "X-Auth-Request-User"
GroupsHeader string `yaml:"groups_header"` // default "X-Auth-Request-Groups"
// AutoProvision: first-sight user lands in the users table
// with DefaultRole. Without it, an unmapped email returns
// 403 — admins pre-create accounts.
AutoProvision bool `yaml:"auto_provision"`
DefaultRole string `yaml:"default_role"` // default "viewer"
// TrustedProxies — CIDR blocks the headers are accepted
// from. Required when Enabled is true; an empty list with
// Enabled=true is a config error (the validate step refuses
// to boot to keep operators from accidentally opening a
// header-spoofing hole).
TrustedProxies []string `yaml:"trusted_proxies"`
}
TrustedHeaderConfig wires the oauth2-proxy / IAP / Cloudflare Access pattern: an upstream proxy validates SSO and sets identity headers on the request, and Coremetry trusts them without re-doing the OIDC dance itself. Common for banks running oauth2-proxy + Dex / Keycloak in front of every internal service so each app doesn't need its own OIDC client registration.
SECURITY: this mode is only safe behind a proxy that strips + re-injects the headers. Anyone able to send a raw request to Coremetry on a network where the proxy isn't enforced could spoof `X-Auth-Request-Email: admin@bank.com` and become admin. TrustedProxies enforces source-IP gating; we refuse to honour the headers from any other source.