Add Secrets Manager resolution to probod-bootstrap

Introduce a Resolver that owns env lookup and typed parsing for
probod-bootstrap. Env values prefixed with aws://<secret-id> are
fetched from AWS Secrets Manager (plaintext SecretString); each
secret ID is cached per run. Builder now takes a Resolver only.

Prefix every probod-bootstrap input with PROBOD_ so bootstrap config
does not collide with unrelated process environment (for example
AWS_* used by other tooling). Secrets Manager authentication uses
the standard AWS SDK default chain (AWS_REGION, IAM role, profile);
PROBOD_AWS_* vars configure S3 in the generated config only.

Update Helm deployment env names, GNUmakefile dev-config, Lima
provision, e2e testutil, compose.prod.yaml, and docs.

Deployments must rename bootstrap env vars to PROBOD_* (e.g.
AUTH_COOKIE_SECRET → PROBOD_AUTH_COOKIE_SECRET).

BREAKING CHANGE: all env vars are now prefixed by `PROBOD_`.

Signed-off-by: Ludovic Vielle <ludovic@probo.com>
This commit is contained in:
Ludovic Vielle
2026-06-24 11:06:49 +02:00
parent 612e9cbc22
commit 2b8f6f5b3b
18 changed files with 1152 additions and 732 deletions

View File

@@ -17,7 +17,6 @@ package bootstrap
import (
"fmt"
"os"
"strconv"
"strings"
"go.probo.inc/probo/pkg/probodconfig"
@@ -26,18 +25,18 @@ import (
type EnvGetter func(key string) string
type Builder struct {
getEnv EnvGetter
resolver *Resolver
samlCertificate string
samlPrivateKey string
oauth2SigningKey string
}
func NewBuilder(getEnv EnvGetter) *Builder {
if getEnv == nil {
getEnv = os.Getenv
func NewBuilder(resolver *Resolver) *Builder {
if resolver == nil {
resolver = NewResolver(nil)
}
return &Builder{getEnv: getEnv}
return &Builder{resolver: resolver}
}
func (b *Builder) Build() (*probodconfig.FullConfig, error) {
@@ -57,272 +56,272 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
cfg := &probodconfig.FullConfig{
Unit: probodconfig.UnitConfig{
Metrics: probodconfig.MetricsConfig{
Addr: b.getEnvOrDefault("METRICS_ADDR", "localhost:8081"),
Addr: b.resolver.getEnvOrDefault("PROBOD_METRICS_ADDR", "localhost:8081"),
},
Tracing: probodconfig.TracingConfig{
Addr: b.getEnvOrDefault("TRACING_ADDR", "localhost:4318"),
MaxBatchSize: b.getEnvIntOrDefault("TRACING_MAX_BATCH_SIZE", 512),
BatchTimeout: b.getEnvIntOrDefault("TRACING_BATCH_TIMEOUT", 5),
ExportTimeout: b.getEnvIntOrDefault("TRACING_EXPORT_TIMEOUT", 30),
MaxQueueSize: b.getEnvIntOrDefault("TRACING_MAX_QUEUE_SIZE", 2048),
Addr: b.resolver.getEnvOrDefault("PROBOD_TRACING_ADDR", "localhost:4318"),
MaxBatchSize: b.resolver.getEnvIntOrDefault("PROBOD_TRACING_MAX_BATCH_SIZE", 512),
BatchTimeout: b.resolver.getEnvIntOrDefault("PROBOD_TRACING_BATCH_TIMEOUT", 5),
ExportTimeout: b.resolver.getEnvIntOrDefault("PROBOD_TRACING_EXPORT_TIMEOUT", 30),
MaxQueueSize: b.resolver.getEnvIntOrDefault("PROBOD_TRACING_MAX_QUEUE_SIZE", 2048),
},
},
Probod: probodconfig.Config{
BaseURL: b.getEnvOrDefault("PROBOD_BASE_URL", "http://localhost:8080"),
EncryptionKey: b.getEnv("PROBOD_ENCRYPTION_KEY"),
ChromeDPAddr: b.getEnvOrDefault("CHROME_DP_ADDR", "localhost:9222"),
BaseURL: b.resolver.getEnvOrDefault("PROBOD_BASE_URL", "http://localhost:8080"),
EncryptionKey: b.resolver.getEnv("PROBOD_ENCRYPTION_KEY"),
ChromeDPAddr: b.resolver.getEnvOrDefault("PROBOD_CHROME_DP_ADDR", "localhost:9222"),
Api: probodconfig.APIConfig{
Addr: b.getEnvOrDefault("API_ADDR", ":8080"),
Addr: b.resolver.getEnvOrDefault("PROBOD_API_ADDR", ":8080"),
ProxyProtocol: probodconfig.ProxyProtocolConfig{
TrustedProxies: b.parseOriginsList(b.getEnv("API_PROXY_PROTOCOL_TRUSTED_PROXIES")),
TrustedProxies: b.parseOriginsList(b.resolver.getEnv("PROBOD_API_PROXY_PROTOCOL_TRUSTED_PROXIES")),
},
Cors: probodconfig.CorsConfig{
AllowedOrigins: b.parseOriginsList(b.getEnvOrDefault("API_CORS_ALLOWED_ORIGINS", "http://localhost:8080")),
AllowedOrigins: b.parseOriginsList(b.resolver.getEnvOrDefault("PROBOD_API_CORS_ALLOWED_ORIGINS", "http://localhost:8080")),
},
ExtraHeaderFields: make(map[string]string),
},
Pg: probodconfig.PgConfig{
Addr: b.getEnvOrDefault("PG_ADDR", "localhost:5432"),
Username: b.getEnvOrDefault("PG_USERNAME", "probod"),
Password: b.getEnvOrDefault("PG_PASSWORD", "probod"),
Database: b.getEnvOrDefault("PG_DATABASE", "probod"),
PoolSize: int32(b.getEnvIntOrDefault("PG_POOL_SIZE", 100)),
MinPoolSize: int32(b.getEnvIntOrDefault("PG_MIN_POOL_SIZE", 10)),
MaxConnIdleTimeSeconds: b.getEnvIntOrDefault("PG_MAX_CONN_IDLE_TIME_SECONDS", 1800),
MaxConnLifetimeSeconds: b.getEnvIntOrDefault("PG_MAX_CONN_LIFETIME_SECONDS", 3600),
MaxConnLifetimeJitterSeconds: b.getEnvIntOrDefault("PG_MAX_CONN_LIFETIME_JITTER_SECONDS", 300),
HealthCheckPeriodSeconds: b.getEnvIntOrDefault("PG_HEALTH_CHECK_PERIOD_SECONDS", 60),
Addr: b.resolver.getEnvOrDefault("PROBOD_PG_ADDR", "localhost:5432"),
Username: b.resolver.getEnvOrDefault("PROBOD_PG_USERNAME", "probod"),
Password: b.resolver.getEnvOrDefault("PROBOD_PG_PASSWORD", "probod"),
Database: b.resolver.getEnvOrDefault("PROBOD_PG_DATABASE", "probod"),
PoolSize: int32(b.resolver.getEnvIntOrDefault("PROBOD_PG_POOL_SIZE", 100)),
MinPoolSize: int32(b.resolver.getEnvIntOrDefault("PROBOD_PG_MIN_POOL_SIZE", 10)),
MaxConnIdleTimeSeconds: b.resolver.getEnvIntOrDefault("PROBOD_PG_MAX_CONN_IDLE_TIME_SECONDS", 1800),
MaxConnLifetimeSeconds: b.resolver.getEnvIntOrDefault("PROBOD_PG_MAX_CONN_LIFETIME_SECONDS", 3600),
MaxConnLifetimeJitterSeconds: b.resolver.getEnvIntOrDefault("PROBOD_PG_MAX_CONN_LIFETIME_JITTER_SECONDS", 300),
HealthCheckPeriodSeconds: b.resolver.getEnvIntOrDefault("PROBOD_PG_HEALTH_CHECK_PERIOD_SECONDS", 60),
CACertBundle: pgCACertBundle,
Debug: b.getEnvBoolOrDefault("PG_DEBUG", false),
Debug: b.resolver.getEnvBoolOrDefault("PROBOD_PG_DEBUG", false),
},
Auth: probodconfig.AuthConfig{
DisableSignup: b.getEnvBoolOrDefault("AUTH_DISABLE_SIGNUP", false),
InvitationConfirmationTokenValidity: b.getEnvIntOrDefault("AUTH_INVITATION_TOKEN_VALIDITY", 3600),
PasswordResetTokenValidity: b.getEnvIntOrDefault("AUTH_PASSWORD_RESET_TOKEN_VALIDITY", 3600),
MagicLinkTokenValidity: b.getEnvIntOrDefault("AUTH_MAGIC_LINK_TOKEN_VALIDITY", 900),
DisableSignup: b.resolver.getEnvBoolOrDefault("PROBOD_AUTH_DISABLE_SIGNUP", false),
InvitationConfirmationTokenValidity: b.resolver.getEnvIntOrDefault("PROBOD_AUTH_INVITATION_TOKEN_VALIDITY", 3600),
PasswordResetTokenValidity: b.resolver.getEnvIntOrDefault("PROBOD_AUTH_PASSWORD_RESET_TOKEN_VALIDITY", 3600),
MagicLinkTokenValidity: b.resolver.getEnvIntOrDefault("PROBOD_AUTH_MAGIC_LINK_TOKEN_VALIDITY", 900),
Cookie: probodconfig.CookieConfig{
Name: b.getEnvOrDefault("AUTH_COOKIE_NAME", "SSID"),
Domain: b.getEnvOrDefault("AUTH_COOKIE_DOMAIN", "localhost"),
Secret: b.getEnv("AUTH_COOKIE_SECRET"),
Duration: b.getEnvIntOrDefault("AUTH_COOKIE_DURATION", 24),
Secure: b.getEnvBoolOrDefault("AUTH_COOKIE_SECURE", true),
Name: b.resolver.getEnvOrDefault("PROBOD_AUTH_COOKIE_NAME", "SSID"),
Domain: b.resolver.getEnvOrDefault("PROBOD_AUTH_COOKIE_DOMAIN", "localhost"),
Secret: b.resolver.getEnv("PROBOD_AUTH_COOKIE_SECRET"),
Duration: b.resolver.getEnvIntOrDefault("PROBOD_AUTH_COOKIE_DURATION", 24),
Secure: b.resolver.getEnvBoolOrDefault("PROBOD_AUTH_COOKIE_SECURE", true),
},
Password: probodconfig.PasswordConfig{
Pepper: b.getEnv("AUTH_PASSWORD_PEPPER"),
Iterations: b.getEnvIntOrDefault("AUTH_PASSWORD_ITERATIONS", 1000000),
Pepper: b.resolver.getEnv("PROBOD_AUTH_PASSWORD_PEPPER"),
Iterations: b.resolver.getEnvIntOrDefault("PROBOD_AUTH_PASSWORD_ITERATIONS", 1000000),
},
SAML: probodconfig.SAMLConfig{
SessionDuration: b.getEnvIntOrDefault("SAML_SESSION_DURATION", 604800),
CleanupIntervalSeconds: b.getEnvIntOrDefault("SAML_CLEANUP_INTERVAL_SECONDS", 0),
SessionDuration: b.resolver.getEnvIntOrDefault("PROBOD_SAML_SESSION_DURATION", 604800),
CleanupIntervalSeconds: b.resolver.getEnvIntOrDefault("PROBOD_SAML_CLEANUP_INTERVAL_SECONDS", 0),
Certificate: samlCert,
PrivateKey: samlKey,
DomainVerificationIntervalSeconds: b.getEnvIntOrDefault("SAML_DOMAIN_VERIFICATION_INTERVAL_SECONDS", 60),
DomainVerificationResolverAddr: b.getEnvOrDefault("SAML_DOMAIN_VERIFICATION_RESOLVER_ADDR", "8.8.8.8:53"),
DomainVerificationIntervalSeconds: b.resolver.getEnvIntOrDefault("PROBOD_SAML_DOMAIN_VERIFICATION_INTERVAL_SECONDS", 60),
DomainVerificationResolverAddr: b.resolver.getEnvOrDefault("PROBOD_SAML_DOMAIN_VERIFICATION_RESOLVER_ADDR", "8.8.8.8:53"),
},
Google: probodconfig.OIDCProviderConfig{
ClientID: b.getEnv("AUTH_GOOGLE_CLIENT_ID"),
ClientSecret: b.getEnv("AUTH_GOOGLE_CLIENT_SECRET"),
Enabled: b.getEnv("AUTH_GOOGLE_CLIENT_ID") != "" && b.getEnv("AUTH_GOOGLE_CLIENT_SECRET") != "",
ClientID: b.resolver.getEnv("PROBOD_AUTH_GOOGLE_CLIENT_ID"),
ClientSecret: b.resolver.getEnv("PROBOD_AUTH_GOOGLE_CLIENT_SECRET"),
Enabled: b.resolver.getEnv("PROBOD_AUTH_GOOGLE_CLIENT_ID") != "" && b.resolver.getEnv("PROBOD_AUTH_GOOGLE_CLIENT_SECRET") != "",
},
Microsoft: probodconfig.OIDCProviderConfig{
ClientID: b.getEnv("AUTH_MICROSOFT_CLIENT_ID"),
ClientSecret: b.getEnv("AUTH_MICROSOFT_CLIENT_SECRET"),
Enabled: b.getEnv("AUTH_MICROSOFT_CLIENT_ID") != "" && b.getEnv("AUTH_MICROSOFT_CLIENT_SECRET") != "",
ClientID: b.resolver.getEnv("PROBOD_AUTH_MICROSOFT_CLIENT_ID"),
ClientSecret: b.resolver.getEnv("PROBOD_AUTH_MICROSOFT_CLIENT_SECRET"),
Enabled: b.resolver.getEnv("PROBOD_AUTH_MICROSOFT_CLIENT_ID") != "" && b.resolver.getEnv("PROBOD_AUTH_MICROSOFT_CLIENT_SECRET") != "",
},
OAuth2Server: probodconfig.OAuth2ServerConfig{
SigningKeys: []probodconfig.OAuth2SigningKeyConfig{{
PrivateKey: oauth2SigningKey,
KID: b.getEnvOrDefault("OAUTH2_SERVER_SIGNING_KEY_KID", "default"),
KID: b.resolver.getEnvOrDefault("PROBOD_OAUTH2_SERVER_SIGNING_KEY_KID", "default"),
Active: true,
}},
AccessTokenDuration: b.getEnvIntOrDefault("OAUTH2_SERVER_ACCESS_TOKEN_DURATION", 3600),
RefreshTokenDuration: b.getEnvIntOrDefault("OAUTH2_SERVER_REFRESH_TOKEN_DURATION", 2592000),
AuthorizationCodeDuration: b.getEnvIntOrDefault("OAUTH2_SERVER_AUTHORIZATION_CODE_DURATION", 600),
DeviceCodeDuration: b.getEnvIntOrDefault("OAUTH2_SERVER_DEVICE_CODE_DURATION", 600),
AccessTokenDuration: b.resolver.getEnvIntOrDefault("PROBOD_OAUTH2_SERVER_ACCESS_TOKEN_DURATION", 3600),
RefreshTokenDuration: b.resolver.getEnvIntOrDefault("PROBOD_OAUTH2_SERVER_REFRESH_TOKEN_DURATION", 2592000),
AuthorizationCodeDuration: b.resolver.getEnvIntOrDefault("PROBOD_OAUTH2_SERVER_AUTHORIZATION_CODE_DURATION", 600),
DeviceCodeDuration: b.resolver.getEnvIntOrDefault("PROBOD_OAUTH2_SERVER_DEVICE_CODE_DURATION", 600),
CIMDAllowedClientIDs: b.parseOriginsList(
b.getEnv("OAUTH2_SERVER_CIMD_ALLOWED_CLIENT_IDS"),
b.resolver.getEnv("PROBOD_OAUTH2_SERVER_CIMD_ALLOWED_CLIENT_IDS"),
),
},
},
TrustCenter: probodconfig.TrustCenterConfig{
HTTPAddr: b.getEnvOrDefault("TRUST_CENTER_HTTP_ADDR", ":80"),
HTTPSAddr: b.getEnvOrDefault("TRUST_CENTER_HTTPS_ADDR", ":443"),
HTTPAddr: b.resolver.getEnvOrDefault("PROBOD_TRUST_CENTER_HTTP_ADDR", ":80"),
HTTPSAddr: b.resolver.getEnvOrDefault("PROBOD_TRUST_CENTER_HTTPS_ADDR", ":443"),
ProxyProtocol: probodconfig.ProxyProtocolConfig{
TrustedProxies: b.parseOriginsList(b.getEnv("TRUST_CENTER_PROXY_PROTOCOL_TRUSTED_PROXIES")),
TrustedProxies: b.parseOriginsList(b.resolver.getEnv("PROBOD_TRUST_CENTER_PROXY_PROTOCOL_TRUSTED_PROXIES")),
},
},
AWS: probodconfig.AWSConfig{
Region: b.getEnvOrDefault("AWS_REGION", "us-east-1"),
Bucket: b.getEnvOrDefault("AWS_BUCKET", "probod"),
AccessKeyID: b.getEnv("AWS_ACCESS_KEY_ID"),
SecretAccessKey: b.getEnv("AWS_SECRET_ACCESS_KEY"),
Endpoint: b.getEnv("AWS_ENDPOINT"),
UsePathStyle: b.getEnvBoolOrDefault("AWS_USE_PATH_STYLE", false),
Region: b.resolver.getEnvOrDefault("PROBOD_AWS_REGION", "us-east-1"),
Bucket: b.resolver.getEnvOrDefault("PROBOD_AWS_BUCKET", "probod"),
AccessKeyID: b.resolver.getEnv("PROBOD_AWS_ACCESS_KEY_ID"),
SecretAccessKey: b.resolver.getEnv("PROBOD_AWS_SECRET_ACCESS_KEY"),
Endpoint: b.resolver.getEnv("PROBOD_AWS_ENDPOINT"),
UsePathStyle: b.resolver.getEnvBoolOrDefault("PROBOD_AWS_USE_PATH_STYLE", false),
},
Notifications: probodconfig.NotificationsConfig{
Mailer: probodconfig.MailerConfig{
SenderName: b.getEnvOrDefault("MAILER_SENDER_NAME", "Probo"),
SenderEmail: b.getEnvOrDefault("MAILER_SENDER_EMAIL", "no-reply@notification.getprobo.com"),
MailerInterval: b.getEnvIntOrDefault("MAILER_INTERVAL", 60),
SenderName: b.resolver.getEnvOrDefault("PROBOD_MAILER_SENDER_NAME", "Probo"),
SenderEmail: b.resolver.getEnvOrDefault("PROBOD_MAILER_SENDER_EMAIL", "no-reply@notification.getprobo.com"),
MailerInterval: b.resolver.getEnvIntOrDefault("PROBOD_MAILER_INTERVAL", 60),
SMTP: probodconfig.SMTPConfig{
Addr: b.getEnvOrDefault("SMTP_ADDR", "localhost:1025"),
User: b.getEnv("SMTP_USER"),
Password: b.getEnv("SMTP_PASSWORD"),
TLSRequired: b.getEnvBoolOrDefault("SMTP_TLS_REQUIRED", false),
HelloName: b.getEnv("SMTP_HELLO_NAME"),
Addr: b.resolver.getEnvOrDefault("PROBOD_SMTP_ADDR", "localhost:1025"),
User: b.resolver.getEnv("PROBOD_SMTP_USER"),
Password: b.resolver.getEnv("PROBOD_SMTP_PASSWORD"),
TLSRequired: b.resolver.getEnvBoolOrDefault("PROBOD_SMTP_TLS_REQUIRED", false),
HelloName: b.resolver.getEnv("PROBOD_SMTP_HELLO_NAME"),
},
},
Slack: probodconfig.SlackConfig{
SenderInterval: b.getEnvIntOrDefault("SLACK_SENDER_INTERVAL", 60),
SigningSecret: b.getEnv("CONNECTOR_SLACK_SIGNING_SECRET"),
SenderInterval: b.resolver.getEnvIntOrDefault("PROBOD_SLACK_SENDER_INTERVAL", 60),
SigningSecret: b.resolver.getEnv("PROBOD_CONNECTOR_SLACK_SIGNING_SECRET"),
},
Webhook: probodconfig.WebhookConfig{
SenderInterval: b.getEnvIntOrDefault("WEBHOOK_SENDER_INTERVAL", 5),
CacheTTL: b.getEnvIntOrDefault("WEBHOOK_CACHE_TTL", 86400),
SenderInterval: b.resolver.getEnvIntOrDefault("PROBOD_WEBHOOK_SENDER_INTERVAL", 5),
CacheTTL: b.resolver.getEnvIntOrDefault("PROBOD_WEBHOOK_CACHE_TTL", 86400),
},
Document: probodconfig.DocumentNotificationConfig{
Interval: b.getEnvIntOrDefault("DOCUMENT_NOTIFICATION_INTERVAL", 300),
DebounceDelay: b.getEnvIntOrDefault("DOCUMENT_NOTIFICATION_DEBOUNCE_DELAY", 900),
ReminderInterval: b.getEnvIntOrDefault("DOCUMENT_NOTIFICATION_REMINDER_INTERVAL", 86400),
Interval: b.resolver.getEnvIntOrDefault("PROBOD_DOCUMENT_NOTIFICATION_INTERVAL", 300),
DebounceDelay: b.resolver.getEnvIntOrDefault("PROBOD_DOCUMENT_NOTIFICATION_DEBOUNCE_DELAY", 900),
ReminderInterval: b.resolver.getEnvIntOrDefault("PROBOD_DOCUMENT_NOTIFICATION_REMINDER_INTERVAL", 86400),
},
},
Agents: probodconfig.AgentsConfig{
Providers: map[string]probodconfig.LLMProviderConfig{
"openai": {
Type: "openai",
APIKey: b.getEnv("OPENAI_API_KEY"),
APIKey: b.resolver.getEnv("PROBOD_OPENAI_API_KEY"),
},
"anthropic": {
Type: "anthropic",
APIKey: b.getEnv("ANTHROPIC_API_KEY"),
APIKey: b.resolver.getEnv("PROBOD_ANTHROPIC_API_KEY"),
},
},
Default: probodconfig.LLMAgentConfig{
Provider: b.getEnvOrDefault("AGENT_DEFAULT_PROVIDER", "openai"),
ModelName: b.getEnvOrDefault("AGENT_DEFAULT_MODEL_NAME", "gpt-4o"),
Temperature: new(b.getEnvFloatOrDefault("AGENT_DEFAULT_TEMPERATURE", 0.1)),
MaxTokens: new(b.getEnvIntOrDefault("AGENT_DEFAULT_MAX_TOKENS", 4096)),
Provider: b.resolver.getEnvOrDefault("PROBOD_AGENT_DEFAULT_PROVIDER", "openai"),
ModelName: b.resolver.getEnvOrDefault("PROBOD_AGENT_DEFAULT_MODEL_NAME", "gpt-4o"),
Temperature: new(b.resolver.getEnvFloatOrDefault("PROBOD_AGENT_DEFAULT_TEMPERATURE", 0.1)),
MaxTokens: new(b.resolver.getEnvIntOrDefault("PROBOD_AGENT_DEFAULT_MAX_TOKENS", 4096)),
},
Probo: probodconfig.LLMAgentConfig{
Provider: b.getEnvOrDefault("AGENT_PROBO_PROVIDER", ""),
ModelName: b.getEnvOrDefault("AGENT_PROBO_MODEL_NAME", ""),
Temperature: b.getEnvFloatPtr("AGENT_PROBO_TEMPERATURE"),
MaxTokens: b.getEnvIntPtr("AGENT_PROBO_MAX_TOKENS"),
Provider: b.resolver.getEnvOrDefault("PROBOD_AGENT_PROBO_PROVIDER", ""),
ModelName: b.resolver.getEnvOrDefault("PROBOD_AGENT_PROBO_MODEL_NAME", ""),
Temperature: b.resolver.getEnvFloatPtr("PROBOD_AGENT_PROBO_TEMPERATURE"),
MaxTokens: b.resolver.getEnvIntPtr("PROBOD_AGENT_PROBO_MAX_TOKENS"),
},
EvidenceDescriber: probodconfig.LLMAgentConfig{
Provider: b.getEnvOrDefault("AGENT_EVIDENCE_DESCRIBER_PROVIDER", ""),
ModelName: b.getEnvOrDefault("AGENT_EVIDENCE_DESCRIBER_MODEL_NAME", ""),
Temperature: b.getEnvFloatPtr("AGENT_EVIDENCE_DESCRIBER_TEMPERATURE"),
MaxTokens: b.getEnvIntPtr("AGENT_EVIDENCE_DESCRIBER_MAX_TOKENS"),
Provider: b.resolver.getEnvOrDefault("PROBOD_AGENT_EVIDENCE_DESCRIBER_PROVIDER", ""),
ModelName: b.resolver.getEnvOrDefault("PROBOD_AGENT_EVIDENCE_DESCRIBER_MODEL_NAME", ""),
Temperature: b.resolver.getEnvFloatPtr("PROBOD_AGENT_EVIDENCE_DESCRIBER_TEMPERATURE"),
MaxTokens: b.resolver.getEnvIntPtr("PROBOD_AGENT_EVIDENCE_DESCRIBER_MAX_TOKENS"),
},
ThirdPartyVetter: probodconfig.LLMAgentConfig{
Provider: b.getEnvOrDefault("AGENT_THIRD_PARTY_VETTER_PROVIDER", ""),
ModelName: b.getEnvOrDefault("AGENT_THIRD_PARTY_VETTER_MODEL_NAME", ""),
Temperature: b.getEnvFloatPtr("AGENT_THIRD_PARTY_VETTER_TEMPERATURE"),
MaxTokens: b.getEnvIntPtr("AGENT_THIRD_PARTY_VETTER_MAX_TOKENS"),
Provider: b.resolver.getEnvOrDefault("PROBOD_AGENT_THIRD_PARTY_VETTER_PROVIDER", ""),
ModelName: b.resolver.getEnvOrDefault("PROBOD_AGENT_THIRD_PARTY_VETTER_MODEL_NAME", ""),
Temperature: b.resolver.getEnvFloatPtr("PROBOD_AGENT_THIRD_PARTY_VETTER_TEMPERATURE"),
MaxTokens: b.resolver.getEnvIntPtr("PROBOD_AGENT_THIRD_PARTY_VETTER_MAX_TOKENS"),
},
ThirdPartyDisambiguation: probodconfig.LLMAgentConfig{
Provider: b.getEnvOrDefault("AGENT_THIRD_PARTY_DISAMBIGUATION_PROVIDER", ""),
ModelName: b.getEnvOrDefault("AGENT_THIRD_PARTY_DISAMBIGUATION_MODEL_NAME", ""),
Provider: b.resolver.getEnvOrDefault("PROBOD_AGENT_THIRD_PARTY_DISAMBIGUATION_PROVIDER", ""),
ModelName: b.resolver.getEnvOrDefault("PROBOD_AGENT_THIRD_PARTY_DISAMBIGUATION_MODEL_NAME", ""),
// The disambiguation agent emits a single id plus a
// short rationale, but the budget must leave headroom
// for reasoning models whose reasoning tokens count
// against max_tokens; too small a budget truncates the
// JSON.
Temperature: b.getEnvFloatPtr("AGENT_THIRD_PARTY_DISAMBIGUATION_TEMPERATURE"),
MaxTokens: new(b.getEnvIntOrDefault("AGENT_THIRD_PARTY_DISAMBIGUATION_MAX_TOKENS", 4096)),
Temperature: b.resolver.getEnvFloatPtr("PROBOD_AGENT_THIRD_PARTY_DISAMBIGUATION_TEMPERATURE"),
MaxTokens: new(b.resolver.getEnvIntOrDefault("PROBOD_AGENT_THIRD_PARTY_DISAMBIGUATION_MAX_TOKENS", 4096)),
},
TrackerMapping: probodconfig.LLMAgentConfig{
Provider: b.getEnvOrDefault("AGENT_TRACKER_MAPPING_PROVIDER", ""),
ModelName: b.getEnvOrDefault("AGENT_TRACKER_MAPPING_MODEL_NAME", ""),
Provider: b.resolver.getEnvOrDefault("PROBOD_AGENT_TRACKER_MAPPING_PROVIDER", ""),
ModelName: b.resolver.getEnvOrDefault("PROBOD_AGENT_TRACKER_MAPPING_MODEL_NAME", ""),
// The tracker agents emit tiny structured JSON, but
// the budget must leave headroom for reasoning
// models whose reasoning tokens count against
// max_tokens; too small a budget truncates the JSON.
Temperature: b.getEnvFloatPtr("AGENT_TRACKER_MAPPING_TEMPERATURE"),
MaxTokens: new(b.getEnvIntOrDefault("AGENT_TRACKER_MAPPING_MAX_TOKENS", 4096)),
Temperature: b.resolver.getEnvFloatPtr("PROBOD_AGENT_TRACKER_MAPPING_TEMPERATURE"),
MaxTokens: new(b.resolver.getEnvIntOrDefault("PROBOD_AGENT_TRACKER_MAPPING_MAX_TOKENS", 4096)),
},
TrackerEnrichment: probodconfig.LLMAgentConfig{
Provider: b.getEnvOrDefault("AGENT_TRACKER_ENRICHMENT_PROVIDER", ""),
ModelName: b.getEnvOrDefault("AGENT_TRACKER_ENRICHMENT_MODEL_NAME", ""),
Provider: b.resolver.getEnvOrDefault("PROBOD_AGENT_TRACKER_ENRICHMENT_PROVIDER", ""),
ModelName: b.resolver.getEnvOrDefault("PROBOD_AGENT_TRACKER_ENRICHMENT_MODEL_NAME", ""),
// See the tracker-mapping note: keep ample headroom so
// reasoning models do not truncate the structured JSON.
Temperature: b.getEnvFloatPtr("AGENT_TRACKER_ENRICHMENT_TEMPERATURE"),
MaxTokens: new(b.getEnvIntOrDefault("AGENT_TRACKER_ENRICHMENT_MAX_TOKENS", 4096)),
Temperature: b.resolver.getEnvFloatPtr("PROBOD_AGENT_TRACKER_ENRICHMENT_TEMPERATURE"),
MaxTokens: new(b.resolver.getEnvIntOrDefault("PROBOD_AGENT_TRACKER_ENRICHMENT_MAX_TOKENS", 4096)),
},
CommonThirdPartyEnrichment: probodconfig.LLMAgentConfig{
Provider: b.getEnvOrDefault("AGENT_COMMON_THIRD_PARTY_ENRICHMENT_PROVIDER", ""),
ModelName: b.getEnvOrDefault("AGENT_COMMON_THIRD_PARTY_ENRICHMENT_MODEL_NAME", ""),
Provider: b.resolver.getEnvOrDefault("PROBOD_AGENT_COMMON_THIRD_PARTY_ENRICHMENT_PROVIDER", ""),
ModelName: b.resolver.getEnvOrDefault("PROBOD_AGENT_COMMON_THIRD_PARTY_ENRICHMENT_MODEL_NAME", ""),
// Agent B browses pages and emits a moderate structured
// output; the budget must leave headroom for reasoning
// models whose reasoning tokens count against max_tokens.
Temperature: b.getEnvFloatPtr("AGENT_COMMON_THIRD_PARTY_ENRICHMENT_TEMPERATURE"),
MaxTokens: new(b.getEnvIntOrDefault("AGENT_COMMON_THIRD_PARTY_ENRICHMENT_MAX_TOKENS", 8192)),
Temperature: b.resolver.getEnvFloatPtr("PROBOD_AGENT_COMMON_THIRD_PARTY_ENRICHMENT_TEMPERATURE"),
MaxTokens: new(b.resolver.getEnvIntOrDefault("PROBOD_AGENT_COMMON_THIRD_PARTY_ENRICHMENT_MAX_TOKENS", 8192)),
},
Tools: probodconfig.AgentToolsConfig{
FirecrawlAPIKey: b.getEnv("FIRECRAWL_API_KEY"),
FirecrawlAPIKey: b.resolver.getEnv("PROBOD_FIRECRAWL_API_KEY"),
},
},
CustomDomains: probodconfig.CustomDomainsConfig{
RenewalInterval: b.getEnvIntOrDefault("CUSTOM_DOMAINS_RENEWAL_INTERVAL", 3600),
ProvisionInterval: b.getEnvIntOrDefault("CUSTOM_DOMAINS_PROVISION_INTERVAL", 30),
CnameTarget: b.getEnvOrDefault("CUSTOM_DOMAINS_CNAME_TARGET", "custom.getprobo.com"),
ResolverAddr: b.getEnvOrDefault("CUSTOM_DOMAINS_RESOLVER_ADDR", "8.8.8.8:53"),
CAAIssuerDomain: b.getEnvOrDefault("CUSTOM_DOMAINS_CAA_ISSUER_DOMAIN", "letsencrypt.org"),
RenewalInterval: b.resolver.getEnvIntOrDefault("PROBOD_CUSTOM_DOMAINS_RENEWAL_INTERVAL", 3600),
ProvisionInterval: b.resolver.getEnvIntOrDefault("PROBOD_CUSTOM_DOMAINS_PROVISION_INTERVAL", 30),
CnameTarget: b.resolver.getEnvOrDefault("PROBOD_CUSTOM_DOMAINS_CNAME_TARGET", "custom.getprobo.com"),
ResolverAddr: b.resolver.getEnvOrDefault("PROBOD_CUSTOM_DOMAINS_RESOLVER_ADDR", "8.8.8.8:53"),
CAAIssuerDomain: b.resolver.getEnvOrDefault("PROBOD_CUSTOM_DOMAINS_CAA_ISSUER_DOMAIN", "letsencrypt.org"),
ACME: probodconfig.ACMEConfig{
Directory: b.getEnvOrDefault("ACME_DIRECTORY", "https://acme-v02.api.letsencrypt.org/directory"),
Email: b.getEnvOrDefault("ACME_EMAIL", "admin@probo.com"),
KeyType: b.getEnvOrDefault("ACME_KEY_TYPE", "EC256"),
RootCA: b.getEnv("ACME_ROOT_CA"),
AccountKey: b.getEnv("ACME_ACCOUNT_KEY"),
Directory: b.resolver.getEnvOrDefault("PROBOD_ACME_DIRECTORY", "https://acme-v02.api.letsencrypt.org/directory"),
Email: b.resolver.getEnvOrDefault("PROBOD_ACME_EMAIL", "admin@probo.com"),
KeyType: b.resolver.getEnvOrDefault("PROBOD_ACME_KEY_TYPE", "EC256"),
RootCA: b.resolver.getEnv("PROBOD_ACME_ROOT_CA"),
AccountKey: b.resolver.getEnv("PROBOD_ACME_ACCOUNT_KEY"),
},
},
SCIMBridge: probodconfig.SCIMBridgeConfig{
SyncInterval: b.getEnvIntOrDefault("SCIM_BRIDGE_SYNC_INTERVAL", 900),
PollInterval: b.getEnvIntOrDefault("SCIM_BRIDGE_POLL_INTERVAL", 30),
SyncInterval: b.resolver.getEnvIntOrDefault("PROBOD_SCIM_BRIDGE_SYNC_INTERVAL", 900),
PollInterval: b.resolver.getEnvIntOrDefault("PROBOD_SCIM_BRIDGE_POLL_INTERVAL", 30),
},
ESign: probodconfig.ESignConfig{
TSAURL: b.getEnvOrDefault("ESIGN_TSA_URL", "http://timestamp.digicert.com"),
TSAURL: b.resolver.getEnvOrDefault("PROBOD_ESIGN_TSA_URL", "http://timestamp.digicert.com"),
},
EvidenceDescriber: probodconfig.EvidenceDescriberConfig{
Interval: b.getEnvIntOrDefault("EVIDENCE_DESCRIBER_INTERVAL", 10),
StaleAfter: b.getEnvIntOrDefault("EVIDENCE_DESCRIBER_STALE_AFTER", 300),
MaxConcurrency: b.getEnvIntOrDefault("EVIDENCE_DESCRIBER_MAX_CONCURRENCY", 10),
Interval: b.resolver.getEnvIntOrDefault("PROBOD_EVIDENCE_DESCRIBER_INTERVAL", 10),
StaleAfter: b.resolver.getEnvIntOrDefault("PROBOD_EVIDENCE_DESCRIBER_STALE_AFTER", 300),
MaxConcurrency: b.resolver.getEnvIntOrDefault("PROBOD_EVIDENCE_DESCRIBER_MAX_CONCURRENCY", 10),
},
ThirdPartyVetting: probodconfig.ThirdPartyVettingWorkerConfig{
Interval: b.getEnvIntOrDefault("THIRD_PARTY_VETTING_INTERVAL", 10),
StaleAfter: b.getEnvIntOrDefault("THIRD_PARTY_VETTING_STALE_AFTER", 1500),
MaxConcurrency: b.getEnvIntOrDefault("THIRD_PARTY_VETTING_MAX_CONCURRENCY", 1),
Interval: b.resolver.getEnvIntOrDefault("PROBOD_THIRD_PARTY_VETTING_INTERVAL", 10),
StaleAfter: b.resolver.getEnvIntOrDefault("PROBOD_THIRD_PARTY_VETTING_STALE_AFTER", 1500),
MaxConcurrency: b.resolver.getEnvIntOrDefault("PROBOD_THIRD_PARTY_VETTING_MAX_CONCURRENCY", 1),
},
TrackerMappingWorker: probodconfig.TrackerMappingWorkerConfig{
Interval: b.getEnvIntOrDefault("TRACKER_MAPPING_INTERVAL", 10),
MaxConcurrency: b.getEnvIntOrDefault("TRACKER_MAPPING_MAX_CONCURRENCY", 3),
StaleAfter: b.getEnvIntOrDefault("TRACKER_MAPPING_STALE_AFTER", 600),
AgentTimeout: b.getEnvIntOrDefault("TRACKER_MAPPING_AGENT_TIMEOUT", 45),
AgentMaxTurns: b.getEnvIntOrDefault("TRACKER_MAPPING_AGENT_MAX_TURNS", 10),
DisambiguationAgentTimeout: b.getEnvIntOrDefault("TRACKER_MAPPING_DISAMBIGUATION_AGENT_TIMEOUT", 45),
Interval: b.resolver.getEnvIntOrDefault("PROBOD_TRACKER_MAPPING_INTERVAL", 10),
MaxConcurrency: b.resolver.getEnvIntOrDefault("PROBOD_TRACKER_MAPPING_MAX_CONCURRENCY", 3),
StaleAfter: b.resolver.getEnvIntOrDefault("PROBOD_TRACKER_MAPPING_STALE_AFTER", 600),
AgentTimeout: b.resolver.getEnvIntOrDefault("PROBOD_TRACKER_MAPPING_AGENT_TIMEOUT", 45),
AgentMaxTurns: b.resolver.getEnvIntOrDefault("PROBOD_TRACKER_MAPPING_AGENT_MAX_TURNS", 10),
DisambiguationAgentTimeout: b.resolver.getEnvIntOrDefault("PROBOD_TRACKER_MAPPING_DISAMBIGUATION_AGENT_TIMEOUT", 45),
},
CommonPatternEnrichmentWorker: probodconfig.CommonPatternEnrichmentWorkerConfig{
Interval: b.getEnvIntOrDefault("COMMON_PATTERN_ENRICHMENT_INTERVAL", 10),
MaxConcurrency: b.getEnvIntOrDefault("COMMON_PATTERN_ENRICHMENT_MAX_CONCURRENCY", 2),
StaleAfter: b.getEnvIntOrDefault("COMMON_PATTERN_ENRICHMENT_STALE_AFTER", 600),
AgentTimeout: b.getEnvIntOrDefault("COMMON_PATTERN_ENRICHMENT_AGENT_TIMEOUT", 45),
AgentMaxTurns: b.getEnvIntOrDefault("COMMON_PATTERN_ENRICHMENT_AGENT_MAX_TURNS", 10),
Interval: b.resolver.getEnvIntOrDefault("PROBOD_COMMON_PATTERN_ENRICHMENT_INTERVAL", 10),
MaxConcurrency: b.resolver.getEnvIntOrDefault("PROBOD_COMMON_PATTERN_ENRICHMENT_MAX_CONCURRENCY", 2),
StaleAfter: b.resolver.getEnvIntOrDefault("PROBOD_COMMON_PATTERN_ENRICHMENT_STALE_AFTER", 600),
AgentTimeout: b.resolver.getEnvIntOrDefault("PROBOD_COMMON_PATTERN_ENRICHMENT_AGENT_TIMEOUT", 45),
AgentMaxTurns: b.resolver.getEnvIntOrDefault("PROBOD_COMMON_PATTERN_ENRICHMENT_AGENT_MAX_TURNS", 10),
},
CommonThirdPartyEnrichmentWorker: probodconfig.CommonThirdPartyEnrichmentWorkerConfig{
Interval: b.getEnvIntOrDefault("COMMON_THIRD_PARTY_ENRICHMENT_INTERVAL", 10),
MaxConcurrency: b.getEnvIntOrDefault("COMMON_THIRD_PARTY_ENRICHMENT_MAX_CONCURRENCY", 1),
StaleAfter: b.getEnvIntOrDefault("COMMON_THIRD_PARTY_ENRICHMENT_STALE_AFTER", 900),
AgentTimeout: b.getEnvIntOrDefault("COMMON_THIRD_PARTY_ENRICHMENT_AGENT_TIMEOUT", 90),
AgentMaxTurns: b.getEnvIntOrDefault("COMMON_THIRD_PARTY_ENRICHMENT_AGENT_MAX_TURNS", 12),
ConfidenceThreshold: b.getEnvFloatOrDefault("COMMON_THIRD_PARTY_ENRICHMENT_CONFIDENCE_THRESHOLD", 0.7),
MaxAttempts: b.getEnvIntOrDefault("COMMON_THIRD_PARTY_ENRICHMENT_MAX_ATTEMPTS", 3),
Interval: b.resolver.getEnvIntOrDefault("PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_INTERVAL", 10),
MaxConcurrency: b.resolver.getEnvIntOrDefault("PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_MAX_CONCURRENCY", 1),
StaleAfter: b.resolver.getEnvIntOrDefault("PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_STALE_AFTER", 900),
AgentTimeout: b.resolver.getEnvIntOrDefault("PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_AGENT_TIMEOUT", 90),
AgentMaxTurns: b.resolver.getEnvIntOrDefault("PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_AGENT_MAX_TURNS", 12),
ConfidenceThreshold: b.resolver.getEnvFloatOrDefault("PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_CONFIDENCE_THRESHOLD", 0.7),
MaxAttempts: b.resolver.getEnvIntOrDefault("PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_MAX_ATTEMPTS", 3),
},
Branding: b.getEnvBoolOrDefault("BRANDING", true),
Branding: b.resolver.getEnvBoolOrDefault("PROBOD_BRANDING", true),
},
}
if slackClientID := b.getEnv("CONNECTOR_SLACK_CLIENT_ID"); slackClientID != "" {
if slackClientID := b.resolver.getEnv("PROBOD_CONNECTOR_SLACK_CLIENT_ID"); slackClientID != "" {
cfg.Probod.Connectors = append(
cfg.Probod.Connectors,
probodconfig.ConnectorConfig{
@@ -330,16 +329,16 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
Protocol: "oauth2",
RawConfig: probodconfig.ConnectorConfigOAuth2{
ClientID: slackClientID,
ClientSecret: b.getEnv("CONNECTOR_SLACK_CLIENT_SECRET"),
ClientSecret: b.resolver.getEnv("PROBOD_CONNECTOR_SLACK_CLIENT_SECRET"),
},
RawSettings: map[string]any{
"signing-secret": b.getEnv("CONNECTOR_SLACK_SIGNING_SECRET"),
"signing-secret": b.resolver.getEnv("PROBOD_CONNECTOR_SLACK_SIGNING_SECRET"),
},
},
)
}
if hubspotClientID := b.getEnv("CONNECTOR_HUBSPOT_CLIENT_ID"); hubspotClientID != "" {
if hubspotClientID := b.resolver.getEnv("PROBOD_CONNECTOR_HUBSPOT_CLIENT_ID"); hubspotClientID != "" {
cfg.Probod.Connectors = append(
cfg.Probod.Connectors,
probodconfig.ConnectorConfig{
@@ -347,13 +346,13 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
Protocol: "oauth2",
RawConfig: probodconfig.ConnectorConfigOAuth2{
ClientID: hubspotClientID,
ClientSecret: b.getEnv("CONNECTOR_HUBSPOT_CLIENT_SECRET"),
ClientSecret: b.resolver.getEnv("PROBOD_CONNECTOR_HUBSPOT_CLIENT_SECRET"),
},
},
)
}
if docusignClientID := b.getEnv("CONNECTOR_DOCUSIGN_CLIENT_ID"); docusignClientID != "" {
if docusignClientID := b.resolver.getEnv("PROBOD_CONNECTOR_DOCUSIGN_CLIENT_ID"); docusignClientID != "" {
cfg.Probod.Connectors = append(
cfg.Probod.Connectors,
probodconfig.ConnectorConfig{
@@ -361,13 +360,13 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
Protocol: "oauth2",
RawConfig: probodconfig.ConnectorConfigOAuth2{
ClientID: docusignClientID,
ClientSecret: b.getEnv("CONNECTOR_DOCUSIGN_CLIENT_SECRET"),
ClientSecret: b.resolver.getEnv("PROBOD_CONNECTOR_DOCUSIGN_CLIENT_SECRET"),
},
},
)
}
if notionClientID := b.getEnv("CONNECTOR_NOTION_CLIENT_ID"); notionClientID != "" {
if notionClientID := b.resolver.getEnv("PROBOD_CONNECTOR_NOTION_CLIENT_ID"); notionClientID != "" {
cfg.Probod.Connectors = append(
cfg.Probod.Connectors,
probodconfig.ConnectorConfig{
@@ -375,13 +374,13 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
Protocol: "oauth2",
RawConfig: probodconfig.ConnectorConfigOAuth2{
ClientID: notionClientID,
ClientSecret: b.getEnv("CONNECTOR_NOTION_CLIENT_SECRET"),
ClientSecret: b.resolver.getEnv("PROBOD_CONNECTOR_NOTION_CLIENT_SECRET"),
},
},
)
}
if githubClientID := b.getEnv("CONNECTOR_GITHUB_CLIENT_ID"); githubClientID != "" {
if githubClientID := b.resolver.getEnv("PROBOD_CONNECTOR_GITHUB_CLIENT_ID"); githubClientID != "" {
cfg.Probod.Connectors = append(
cfg.Probod.Connectors,
probodconfig.ConnectorConfig{
@@ -389,13 +388,13 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
Protocol: "oauth2",
RawConfig: probodconfig.ConnectorConfigOAuth2{
ClientID: githubClientID,
ClientSecret: b.getEnv("CONNECTOR_GITHUB_CLIENT_SECRET"),
ClientSecret: b.resolver.getEnv("PROBOD_CONNECTOR_GITHUB_CLIENT_SECRET"),
},
},
)
}
if sentryClientID := b.getEnv("CONNECTOR_SENTRY_CLIENT_ID"); sentryClientID != "" {
if sentryClientID := b.resolver.getEnv("PROBOD_CONNECTOR_SENTRY_CLIENT_ID"); sentryClientID != "" {
cfg.Probod.Connectors = append(
cfg.Probod.Connectors,
probodconfig.ConnectorConfig{
@@ -403,13 +402,13 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
Protocol: "oauth2",
RawConfig: probodconfig.ConnectorConfigOAuth2{
ClientID: sentryClientID,
ClientSecret: b.getEnv("CONNECTOR_SENTRY_CLIENT_SECRET"),
ClientSecret: b.resolver.getEnv("PROBOD_CONNECTOR_SENTRY_CLIENT_SECRET"),
},
},
)
}
if intercomClientID := b.getEnv("CONNECTOR_INTERCOM_CLIENT_ID"); intercomClientID != "" {
if intercomClientID := b.resolver.getEnv("PROBOD_CONNECTOR_INTERCOM_CLIENT_ID"); intercomClientID != "" {
cfg.Probod.Connectors = append(
cfg.Probod.Connectors,
probodconfig.ConnectorConfig{
@@ -417,13 +416,13 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
Protocol: "oauth2",
RawConfig: probodconfig.ConnectorConfigOAuth2{
ClientID: intercomClientID,
ClientSecret: b.getEnv("CONNECTOR_INTERCOM_CLIENT_SECRET"),
ClientSecret: b.resolver.getEnv("PROBOD_CONNECTOR_INTERCOM_CLIENT_SECRET"),
},
},
)
}
if brexClientID := b.getEnv("CONNECTOR_BREX_CLIENT_ID"); brexClientID != "" {
if brexClientID := b.resolver.getEnv("PROBOD_CONNECTOR_BREX_CLIENT_ID"); brexClientID != "" {
cfg.Probod.Connectors = append(
cfg.Probod.Connectors,
probodconfig.ConnectorConfig{
@@ -431,13 +430,13 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
Protocol: "oauth2",
RawConfig: probodconfig.ConnectorConfigOAuth2{
ClientID: brexClientID,
ClientSecret: b.getEnv("CONNECTOR_BREX_CLIENT_SECRET"),
ClientSecret: b.resolver.getEnv("PROBOD_CONNECTOR_BREX_CLIENT_SECRET"),
},
},
)
}
if googleWorkspaceClientID := b.getEnv("CONNECTOR_GOOGLE_WORKSPACE_CLIENT_ID"); googleWorkspaceClientID != "" {
if googleWorkspaceClientID := b.resolver.getEnv("PROBOD_CONNECTOR_GOOGLE_WORKSPACE_CLIENT_ID"); googleWorkspaceClientID != "" {
cfg.Probod.Connectors = append(
cfg.Probod.Connectors,
probodconfig.ConnectorConfig{
@@ -445,13 +444,13 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
Protocol: "oauth2",
RawConfig: probodconfig.ConnectorConfigOAuth2{
ClientID: googleWorkspaceClientID,
ClientSecret: b.getEnv("CONNECTOR_GOOGLE_WORKSPACE_CLIENT_SECRET"),
ClientSecret: b.resolver.getEnv("PROBOD_CONNECTOR_GOOGLE_WORKSPACE_CLIENT_SECRET"),
},
},
)
}
if microsoft365ClientID := b.getEnv("CONNECTOR_MICROSOFT_365_CLIENT_ID"); microsoft365ClientID != "" {
if microsoft365ClientID := b.resolver.getEnv("PROBOD_CONNECTOR_MICROSOFT_365_CLIENT_ID"); microsoft365ClientID != "" {
cfg.Probod.Connectors = append(
cfg.Probod.Connectors,
probodconfig.ConnectorConfig{
@@ -459,7 +458,7 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
Protocol: "oauth2",
RawConfig: probodconfig.ConnectorConfigOAuth2{
ClientID: microsoft365ClientID,
ClientSecret: b.getEnv("CONNECTOR_MICROSOFT_365_CLIENT_SECRET"),
ClientSecret: b.resolver.getEnv("PROBOD_CONNECTOR_MICROSOFT_365_CLIENT_SECRET"),
},
},
)
@@ -476,8 +475,9 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
"MONDAY",
"DATADOG",
"ZENDESK",
"LINEAR",
} {
clientID := b.getEnv("CONNECTOR_" + provider + "_CLIENT_ID")
clientID := b.resolver.getEnv("PROBOD_CONNECTOR_" + provider + "_CLIENT_ID")
if clientID == "" {
continue
}
@@ -489,7 +489,7 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
Protocol: "oauth2",
RawConfig: probodconfig.ConnectorConfigOAuth2{
ClientID: clientID,
ClientSecret: b.getEnv("CONNECTOR_" + provider + "_CLIENT_SECRET"),
ClientSecret: b.resolver.getEnv("PROBOD_CONNECTOR_" + provider + "_CLIENT_SECRET"),
},
},
)
@@ -497,7 +497,7 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
// Vercel needs the operator-supplied integration slug to resolve the
// templated AuthURL ("https://vercel.com/integrations/{integration_slug}/new").
if vercelClientID := b.getEnv("CONNECTOR_VERCEL_CLIENT_ID"); vercelClientID != "" {
if vercelClientID := b.resolver.getEnv("PROBOD_CONNECTOR_VERCEL_CLIENT_ID"); vercelClientID != "" {
cfg.Probod.Connectors = append(
cfg.Probod.Connectors,
probodconfig.ConnectorConfig{
@@ -505,13 +505,17 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
Protocol: "oauth2",
RawConfig: probodconfig.ConnectorConfigOAuth2{
ClientID: vercelClientID,
ClientSecret: b.getEnv("CONNECTOR_VERCEL_CLIENT_SECRET"),
IntegrationSlug: b.getEnv("CONNECTOR_VERCEL_INTEGRATION_SLUG"),
ClientSecret: b.resolver.getEnv("PROBOD_CONNECTOR_VERCEL_CLIENT_SECRET"),
IntegrationSlug: b.resolver.getEnv("PROBOD_CONNECTOR_VERCEL_INTEGRATION_SLUG"),
},
},
)
}
if b.resolver.Err() != nil {
return nil, b.resolver.Err()
}
return cfg, nil
}
@@ -520,28 +524,28 @@ func (b *Builder) validateRequired() error {
required := []string{
"PROBOD_ENCRYPTION_KEY",
"AUTH_COOKIE_SECRET",
"AUTH_PASSWORD_PEPPER",
"PROBOD_AUTH_COOKIE_SECRET",
"PROBOD_AUTH_PASSWORD_PEPPER",
}
for _, key := range required {
if b.getEnv(key) == "" {
if b.resolver.getEnv(key) == "" {
missing = append(missing, key)
}
}
if b.oauth2SigningKey == "" && b.getEnv("OAUTH2_SERVER_SIGNING_KEY") == "" {
missing = append(missing, "OAUTH2_SERVER_SIGNING_KEY")
if b.oauth2SigningKey == "" && b.resolver.getEnv("PROBOD_OAUTH2_SERVER_SIGNING_KEY") == "" {
missing = append(missing, "PROBOD_OAUTH2_SERVER_SIGNING_KEY")
}
if slackClientID := b.getEnv("CONNECTOR_SLACK_CLIENT_ID"); slackClientID != "" {
if slackClientID := b.resolver.getEnv("PROBOD_CONNECTOR_SLACK_CLIENT_ID"); slackClientID != "" {
slackRequired := []string{
"CONNECTOR_SLACK_CLIENT_SECRET",
"CONNECTOR_SLACK_SIGNING_SECRET",
"PROBOD_CONNECTOR_SLACK_CLIENT_SECRET",
"PROBOD_CONNECTOR_SLACK_SIGNING_SECRET",
}
for _, key := range slackRequired {
if b.getEnv(key) == "" {
missing = append(missing, key+" (required when CONNECTOR_SLACK_CLIENT_ID is set)")
if b.resolver.getEnv(key) == "" {
missing = append(missing, key+" (required when PROBOD_CONNECTOR_SLACK_CLIENT_ID is set)")
}
}
}
@@ -569,15 +573,16 @@ func (b *Builder) validateRequired() error {
{"CONNECTOR_MONDAY", []string{"CLIENT_SECRET"}},
{"CONNECTOR_DATADOG", []string{"CLIENT_SECRET"}},
{"CONNECTOR_ZENDESK", []string{"CLIENT_SECRET"}},
{"CONNECTOR_LINEAR", []string{"CLIENT_SECRET"}},
{"CONNECTOR_VERCEL", []string{"CLIENT_SECRET", "INTEGRATION_SLUG"}},
}
for _, p := range oauthProviders {
clientIDKey := p.envPrefix + "_CLIENT_ID"
if b.getEnv(clientIDKey) != "" {
clientIDKey := "PROBOD_" + p.envPrefix + "_CLIENT_ID"
if b.resolver.getEnv(clientIDKey) != "" {
for _, suffix := range p.required {
key := p.envPrefix + "_" + suffix
if b.getEnv(key) == "" {
key := "PROBOD_" + p.envPrefix + "_" + suffix
if b.resolver.getEnv(key) == "" {
missing = append(missing, key+" (required when "+clientIDKey+" is set)")
}
}
@@ -585,6 +590,10 @@ func (b *Builder) validateRequired() error {
}
if len(missing) > 0 {
if err := b.resolver.Err(); err != nil {
return err
}
return fmt.Errorf("missing required environment variables:\n - %s", strings.Join(missing, "\n - "))
}
@@ -596,11 +605,11 @@ func (b *Builder) getSAMLCredentials() (cert, key string, err error) {
key = b.samlPrivateKey
if cert == "" {
cert = b.getEnv("SAML_CERTIFICATE")
cert = b.resolver.getEnv("PROBOD_SAML_CERTIFICATE")
}
if key == "" {
key = b.getEnv("SAML_PRIVATE_KEY")
key = b.resolver.getEnv("PROBOD_SAML_PRIVATE_KEY")
}
if cert == "" || key == "" {
@@ -618,77 +627,18 @@ func (b *Builder) getOAuth2SigningKey() string {
return b.oauth2SigningKey
}
return b.getEnv("OAUTH2_SERVER_SIGNING_KEY")
return b.resolver.getEnv("PROBOD_OAUTH2_SERVER_SIGNING_KEY")
}
func (b *Builder) getPgCACertBundle() string {
if path := b.getEnv("PG_CA_BUNDLE_PATH"); path != "" {
if path := b.resolver.getEnv("PROBOD_PG_CA_BUNDLE_PATH"); path != "" {
data, err := os.ReadFile(path)
if err == nil {
return string(data)
}
}
return b.getEnv("PG_CA_BUNDLE")
}
func (b *Builder) getEnvOrDefault(key, defaultValue string) string {
if value := b.getEnv(key); value != "" {
return value
}
return defaultValue
}
func (b *Builder) getEnvIntOrDefault(key string, defaultValue int) int {
if value := b.getEnv(key); value != "" {
if intValue, err := strconv.ParseInt(value, 10, 32); err == nil {
return int(intValue)
}
}
return defaultValue
}
func (b *Builder) getEnvFloatOrDefault(key string, defaultValue float64) float64 {
if value := b.getEnv(key); value != "" {
if floatValue, err := strconv.ParseFloat(value, 64); err == nil {
return floatValue
}
}
return defaultValue
}
func (b *Builder) getEnvFloatPtr(key string) *float64 {
if value := b.getEnv(key); value != "" {
if floatValue, err := strconv.ParseFloat(value, 64); err == nil {
return &floatValue
}
}
return nil
}
func (b *Builder) getEnvIntPtr(key string) *int {
if value := b.getEnv(key); value != "" {
if intValue, err := strconv.ParseInt(value, 10, 32); err == nil {
v := int(intValue)
return &v
}
}
return nil
}
func (b *Builder) getEnvBoolOrDefault(key string, defaultValue bool) bool {
if value := b.getEnv(key); value != "" {
if boolValue, err := strconv.ParseBool(value); err == nil {
return boolValue
}
}
return defaultValue
return b.resolver.getEnv("PROBOD_PG_CA_BUNDLE")
}
func (b *Builder) parseOriginsList(s string) []string {