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

@@ -33,10 +33,10 @@ func mockEnv(env map[string]string) EnvGetter {
func requiredEnv() map[string]string {
return map[string]string{
"PROBOD_ENCRYPTION_KEY": "test-encryption-key-32-bytes-long",
"AUTH_COOKIE_SECRET": "test-cookie-secret-32-bytes-long!",
"AUTH_PASSWORD_PEPPER": "test-password-pepper-32-bytes-lo",
"OAUTH2_SERVER_SIGNING_KEY": "test-oauth2-signing-key",
"PROBOD_ENCRYPTION_KEY": "test-encryption-key-32-bytes-long",
"PROBOD_AUTH_COOKIE_SECRET": "test-cookie-secret-32-bytes-long!",
"PROBOD_AUTH_PASSWORD_PEPPER": "test-password-pepper-32-bytes-lo",
"PROBOD_OAUTH2_SERVER_SIGNING_KEY": "test-oauth2-signing-key",
}
}
@@ -49,68 +49,68 @@ func TestBuilder_Build_MissingRequiredEnvVars(t *testing.T) {
{
name: "all missing",
env: map[string]string{},
wantMissing: []string{"PROBOD_ENCRYPTION_KEY", "AUTH_COOKIE_SECRET", "AUTH_PASSWORD_PEPPER", "OAUTH2_SERVER_SIGNING_KEY"},
wantMissing: []string{"PROBOD_ENCRYPTION_KEY", "PROBOD_AUTH_COOKIE_SECRET", "PROBOD_AUTH_PASSWORD_PEPPER", "PROBOD_OAUTH2_SERVER_SIGNING_KEY"},
},
{
name: "missing oauth2 signing key",
env: map[string]string{
"PROBOD_ENCRYPTION_KEY": "key",
"AUTH_COOKIE_SECRET": "secret",
"AUTH_PASSWORD_PEPPER": "pepper",
"PROBOD_ENCRYPTION_KEY": "key",
"PROBOD_AUTH_COOKIE_SECRET": "secret",
"PROBOD_AUTH_PASSWORD_PEPPER": "pepper",
},
wantMissing: []string{"OAUTH2_SERVER_SIGNING_KEY"},
wantMissing: []string{"PROBOD_OAUTH2_SERVER_SIGNING_KEY"},
},
{
name: "missing encryption key",
env: map[string]string{
"AUTH_COOKIE_SECRET": "secret",
"AUTH_PASSWORD_PEPPER": "pepper",
"PROBOD_AUTH_COOKIE_SECRET": "secret",
"PROBOD_AUTH_PASSWORD_PEPPER": "pepper",
},
wantMissing: []string{"PROBOD_ENCRYPTION_KEY"},
},
{
name: "missing cookie secret",
env: map[string]string{
"PROBOD_ENCRYPTION_KEY": "key",
"AUTH_PASSWORD_PEPPER": "pepper",
"PROBOD_ENCRYPTION_KEY": "key",
"PROBOD_AUTH_PASSWORD_PEPPER": "pepper",
},
wantMissing: []string{"AUTH_COOKIE_SECRET"},
wantMissing: []string{"PROBOD_AUTH_COOKIE_SECRET"},
},
{
name: "slack connector missing required fields",
env: map[string]string{
"PROBOD_ENCRYPTION_KEY": "key",
"AUTH_COOKIE_SECRET": "secret",
"AUTH_PASSWORD_PEPPER": "pepper",
"CONNECTOR_SLACK_CLIENT_ID": "client-id",
"PROBOD_ENCRYPTION_KEY": "key",
"PROBOD_AUTH_COOKIE_SECRET": "secret",
"PROBOD_AUTH_PASSWORD_PEPPER": "pepper",
"PROBOD_CONNECTOR_SLACK_CLIENT_ID": "client-id",
},
wantMissing: []string{"CONNECTOR_SLACK_CLIENT_SECRET", "CONNECTOR_SLACK_SIGNING_SECRET"},
wantMissing: []string{"PROBOD_CONNECTOR_SLACK_CLIENT_SECRET", "PROBOD_CONNECTOR_SLACK_SIGNING_SECRET"},
},
{
name: "google workspace connector missing required fields",
env: map[string]string{
"PROBOD_ENCRYPTION_KEY": "key",
"AUTH_COOKIE_SECRET": "secret",
"AUTH_PASSWORD_PEPPER": "pepper",
"CONNECTOR_GOOGLE_WORKSPACE_CLIENT_ID": "client-id",
"PROBOD_ENCRYPTION_KEY": "key",
"PROBOD_AUTH_COOKIE_SECRET": "secret",
"PROBOD_AUTH_PASSWORD_PEPPER": "pepper",
"PROBOD_CONNECTOR_GOOGLE_WORKSPACE_CLIENT_ID": "client-id",
},
wantMissing: []string{"CONNECTOR_GOOGLE_WORKSPACE_CLIENT_SECRET"},
wantMissing: []string{"PROBOD_CONNECTOR_GOOGLE_WORKSPACE_CLIENT_SECRET"},
},
{
name: "microsoft 365 connector missing required fields",
env: map[string]string{
"PROBOD_ENCRYPTION_KEY": "key",
"AUTH_COOKIE_SECRET": "secret",
"AUTH_PASSWORD_PEPPER": "pepper",
"CONNECTOR_MICROSOFT_365_CLIENT_ID": "client-id",
"PROBOD_ENCRYPTION_KEY": "key",
"PROBOD_AUTH_COOKIE_SECRET": "secret",
"PROBOD_AUTH_PASSWORD_PEPPER": "pepper",
"PROBOD_CONNECTOR_MICROSOFT_365_CLIENT_ID": "client-id",
},
wantMissing: []string{"CONNECTOR_MICROSOFT_365_CLIENT_SECRET"},
wantMissing: []string{"PROBOD_CONNECTOR_MICROSOFT_365_CLIENT_SECRET"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b := NewBuilder(mockEnv(tt.env))
b := NewBuilder(NewResolver(mockEnv(tt.env)))
_, err := b.Build()
require.Error(t, err)
@@ -123,7 +123,7 @@ func TestBuilder_Build_MissingRequiredEnvVars(t *testing.T) {
}
func TestBuilder_Build_Defaults(t *testing.T) {
b := NewBuilder(mockEnv(requiredEnv()))
b := NewBuilder(NewResolver(mockEnv(requiredEnv())))
b.samlCertificate = "test-cert"
b.samlPrivateKey = "test-key"
@@ -285,129 +285,129 @@ func TestBuilder_Build_Defaults(t *testing.T) {
func TestBuilder_Build_CustomValues(t *testing.T) {
env := requiredEnv()
// Unit
env["METRICS_ADDR"] = "0.0.0.0:9090"
env["TRACING_ADDR"] = "jaeger:4317"
env["TRACING_MAX_BATCH_SIZE"] = "1024"
env["PROBOD_METRICS_ADDR"] = "0.0.0.0:9090"
env["PROBOD_TRACING_ADDR"] = "jaeger:4317"
env["PROBOD_TRACING_MAX_BATCH_SIZE"] = "1024"
// Probod
env["PROBOD_BASE_URL"] = "https://app.example.com"
env["CHROME_DP_ADDR"] = "chrome:9222"
env["PROBOD_CHROME_DP_ADDR"] = "chrome:9222"
// API
env["API_ADDR"] = "0.0.0.0:8080"
env["API_CORS_ALLOWED_ORIGINS"] = "https://app.example.com,https://admin.example.com"
env["API_PROXY_PROTOCOL_TRUSTED_PROXIES"] = "10.0.0.1,10.0.0.2"
env["PROBOD_API_ADDR"] = "0.0.0.0:8080"
env["PROBOD_API_CORS_ALLOWED_ORIGINS"] = "https://app.example.com,https://admin.example.com"
env["PROBOD_API_PROXY_PROTOCOL_TRUSTED_PROXIES"] = "10.0.0.1,10.0.0.2"
// PG
env["PG_ADDR"] = "postgres.example.com:5432"
env["PG_USERNAME"] = "probo"
env["PG_PASSWORD"] = "secret123"
env["PG_DATABASE"] = "probo_prod"
env["PG_POOL_SIZE"] = "200"
env["PG_MIN_POOL_SIZE"] = "25"
env["PG_MAX_CONN_IDLE_TIME_SECONDS"] = "900"
env["PG_MAX_CONN_LIFETIME_SECONDS"] = "7200"
env["PG_MAX_CONN_LIFETIME_JITTER_SECONDS"] = "600"
env["PG_HEALTH_CHECK_PERIOD_SECONDS"] = "30"
env["PG_DEBUG"] = "true"
env["PROBOD_PG_ADDR"] = "postgres.example.com:5432"
env["PROBOD_PG_USERNAME"] = "probo"
env["PROBOD_PG_PASSWORD"] = "secret123"
env["PROBOD_PG_DATABASE"] = "probo_prod"
env["PROBOD_PG_POOL_SIZE"] = "200"
env["PROBOD_PG_MIN_POOL_SIZE"] = "25"
env["PROBOD_PG_MAX_CONN_IDLE_TIME_SECONDS"] = "900"
env["PROBOD_PG_MAX_CONN_LIFETIME_SECONDS"] = "7200"
env["PROBOD_PG_MAX_CONN_LIFETIME_JITTER_SECONDS"] = "600"
env["PROBOD_PG_HEALTH_CHECK_PERIOD_SECONDS"] = "30"
env["PROBOD_PG_DEBUG"] = "true"
// Auth
env["AUTH_DISABLE_SIGNUP"] = "true"
env["AUTH_INVITATION_TOKEN_VALIDITY"] = "7200"
env["AUTH_PASSWORD_RESET_TOKEN_VALIDITY"] = "1800"
env["AUTH_MAGIC_LINK_TOKEN_VALIDITY"] = "600"
env["AUTH_COOKIE_DOMAIN"] = ".example.com"
env["AUTH_COOKIE_DURATION"] = "48"
env["PROBOD_AUTH_DISABLE_SIGNUP"] = "true"
env["PROBOD_AUTH_INVITATION_TOKEN_VALIDITY"] = "7200"
env["PROBOD_AUTH_PASSWORD_RESET_TOKEN_VALIDITY"] = "1800"
env["PROBOD_AUTH_MAGIC_LINK_TOKEN_VALIDITY"] = "600"
env["PROBOD_AUTH_COOKIE_DOMAIN"] = ".example.com"
env["PROBOD_AUTH_COOKIE_DURATION"] = "48"
// SAML
env["SAML_DOMAIN_VERIFICATION_INTERVAL_SECONDS"] = "120"
env["SAML_DOMAIN_VERIFICATION_RESOLVER_ADDR"] = "1.1.1.1:53"
env["PROBOD_SAML_DOMAIN_VERIFICATION_INTERVAL_SECONDS"] = "120"
env["PROBOD_SAML_DOMAIN_VERIFICATION_RESOLVER_ADDR"] = "1.1.1.1:53"
// Trust center
env["TRUST_CENTER_HTTP_ADDR"] = ":8080"
env["TRUST_CENTER_HTTPS_ADDR"] = ":8443"
env["TRUST_CENTER_PROXY_PROTOCOL_TRUSTED_PROXIES"] = "10.0.1.1,10.0.1.2"
env["PROBOD_TRUST_CENTER_HTTP_ADDR"] = ":8080"
env["PROBOD_TRUST_CENTER_HTTPS_ADDR"] = ":8443"
env["PROBOD_TRUST_CENTER_PROXY_PROTOCOL_TRUSTED_PROXIES"] = "10.0.1.1,10.0.1.2"
// AWS
env["AWS_REGION"] = "eu-west-1"
env["AWS_BUCKET"] = "probo-files"
env["AWS_ACCESS_KEY_ID"] = "AKIAIOSFODNN7EXAMPLE"
env["AWS_SECRET_ACCESS_KEY"] = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
env["AWS_ENDPOINT"] = "https://s3.example.com"
env["AWS_USE_PATH_STYLE"] = "true"
env["PROBOD_AWS_REGION"] = "eu-west-1"
env["PROBOD_AWS_BUCKET"] = "probo-files"
env["PROBOD_AWS_ACCESS_KEY_ID"] = "AKIAIOSFODNN7EXAMPLE"
env["PROBOD_AWS_SECRET_ACCESS_KEY"] = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
env["PROBOD_AWS_ENDPOINT"] = "https://s3.example.com"
env["PROBOD_AWS_USE_PATH_STYLE"] = "true"
// Notifications
env["WEBHOOK_SENDER_INTERVAL"] = "10"
env["WEBHOOK_CACHE_TTL"] = "3600"
env["CONNECTOR_SLACK_SIGNING_SECRET"] = "slack-signing-secret"
env["DOCUMENT_NOTIFICATION_INTERVAL"] = "120"
env["DOCUMENT_NOTIFICATION_DEBOUNCE_DELAY"] = "60"
env["DOCUMENT_NOTIFICATION_REMINDER_INTERVAL"] = "43200"
env["PROBOD_WEBHOOK_SENDER_INTERVAL"] = "10"
env["PROBOD_WEBHOOK_CACHE_TTL"] = "3600"
env["PROBOD_CONNECTOR_SLACK_SIGNING_SECRET"] = "slack-signing-secret"
env["PROBOD_DOCUMENT_NOTIFICATION_INTERVAL"] = "120"
env["PROBOD_DOCUMENT_NOTIFICATION_DEBOUNCE_DELAY"] = "60"
env["PROBOD_DOCUMENT_NOTIFICATION_REMINDER_INTERVAL"] = "43200"
// Firecrawl
env["FIRECRAWL_API_KEY"] = "fc-test-key"
env["PROBOD_FIRECRAWL_API_KEY"] = "fc-test-key"
// Agents — providers
env["OPENAI_API_KEY"] = "sk-test-key"
env["ANTHROPIC_API_KEY"] = "sk-ant-test-key"
env["PROBOD_OPENAI_API_KEY"] = "sk-test-key"
env["PROBOD_ANTHROPIC_API_KEY"] = "sk-ant-test-key"
// Agents — default
env["AGENT_DEFAULT_PROVIDER"] = "openai"
env["AGENT_DEFAULT_MODEL_NAME"] = "gpt-4-turbo"
env["AGENT_DEFAULT_TEMPERATURE"] = "0.5"
env["AGENT_DEFAULT_MAX_TOKENS"] = "8192"
env["PROBOD_AGENT_DEFAULT_PROVIDER"] = "openai"
env["PROBOD_AGENT_DEFAULT_MODEL_NAME"] = "gpt-4-turbo"
env["PROBOD_AGENT_DEFAULT_TEMPERATURE"] = "0.5"
env["PROBOD_AGENT_DEFAULT_MAX_TOKENS"] = "8192"
// Agents — evidence-describer override
env["AGENT_EVIDENCE_DESCRIBER_PROVIDER"] = "anthropic"
env["AGENT_EVIDENCE_DESCRIBER_MODEL_NAME"] = "claude-sonnet-4-20250514"
env["AGENT_EVIDENCE_DESCRIBER_TEMPERATURE"] = "0.2"
env["AGENT_EVIDENCE_DESCRIBER_MAX_TOKENS"] = "4096"
env["PROBOD_AGENT_EVIDENCE_DESCRIBER_PROVIDER"] = "anthropic"
env["PROBOD_AGENT_EVIDENCE_DESCRIBER_MODEL_NAME"] = "claude-sonnet-4-20250514"
env["PROBOD_AGENT_EVIDENCE_DESCRIBER_TEMPERATURE"] = "0.2"
env["PROBOD_AGENT_EVIDENCE_DESCRIBER_MAX_TOKENS"] = "4096"
// Agents — third-party-vetter override
env["AGENT_THIRD_PARTY_VETTER_PROVIDER"] = "openai"
env["AGENT_THIRD_PARTY_VETTER_MODEL_NAME"] = "gpt-4o"
env["AGENT_THIRD_PARTY_VETTER_TEMPERATURE"] = "0.3"
env["AGENT_THIRD_PARTY_VETTER_MAX_TOKENS"] = "8192"
env["PROBOD_AGENT_THIRD_PARTY_VETTER_PROVIDER"] = "openai"
env["PROBOD_AGENT_THIRD_PARTY_VETTER_MODEL_NAME"] = "gpt-4o"
env["PROBOD_AGENT_THIRD_PARTY_VETTER_TEMPERATURE"] = "0.3"
env["PROBOD_AGENT_THIRD_PARTY_VETTER_MAX_TOKENS"] = "8192"
// Agents — third-party-disambiguation override
env["AGENT_THIRD_PARTY_DISAMBIGUATION_PROVIDER"] = "anthropic"
env["AGENT_THIRD_PARTY_DISAMBIGUATION_MODEL_NAME"] = "claude-sonnet-4-20250514"
env["AGENT_THIRD_PARTY_DISAMBIGUATION_TEMPERATURE"] = "0.4"
env["AGENT_THIRD_PARTY_DISAMBIGUATION_MAX_TOKENS"] = "2048"
env["PROBOD_AGENT_THIRD_PARTY_DISAMBIGUATION_PROVIDER"] = "anthropic"
env["PROBOD_AGENT_THIRD_PARTY_DISAMBIGUATION_MODEL_NAME"] = "claude-sonnet-4-20250514"
env["PROBOD_AGENT_THIRD_PARTY_DISAMBIGUATION_TEMPERATURE"] = "0.4"
env["PROBOD_AGENT_THIRD_PARTY_DISAMBIGUATION_MAX_TOKENS"] = "2048"
// Agents — tracker-mapping override
env["AGENT_TRACKER_MAPPING_PROVIDER"] = "openai"
env["AGENT_TRACKER_MAPPING_MODEL_NAME"] = "gpt-4o-mini"
env["AGENT_TRACKER_MAPPING_TEMPERATURE"] = "0.1"
env["AGENT_TRACKER_MAPPING_MAX_TOKENS"] = "1024"
env["PROBOD_AGENT_TRACKER_MAPPING_PROVIDER"] = "openai"
env["PROBOD_AGENT_TRACKER_MAPPING_MODEL_NAME"] = "gpt-4o-mini"
env["PROBOD_AGENT_TRACKER_MAPPING_TEMPERATURE"] = "0.1"
env["PROBOD_AGENT_TRACKER_MAPPING_MAX_TOKENS"] = "1024"
// Agents — tracker-enrichment override
env["AGENT_TRACKER_ENRICHMENT_PROVIDER"] = "openai"
env["AGENT_TRACKER_ENRICHMENT_MODEL_NAME"] = "gpt-4o"
env["AGENT_TRACKER_ENRICHMENT_TEMPERATURE"] = "0.2"
env["AGENT_TRACKER_ENRICHMENT_MAX_TOKENS"] = "2048"
env["PROBOD_AGENT_TRACKER_ENRICHMENT_PROVIDER"] = "openai"
env["PROBOD_AGENT_TRACKER_ENRICHMENT_MODEL_NAME"] = "gpt-4o"
env["PROBOD_AGENT_TRACKER_ENRICHMENT_TEMPERATURE"] = "0.2"
env["PROBOD_AGENT_TRACKER_ENRICHMENT_MAX_TOKENS"] = "2048"
// Tracker worker tuning override
env["TRACKER_MAPPING_INTERVAL"] = "20"
env["TRACKER_MAPPING_MAX_CONCURRENCY"] = "5"
env["TRACKER_MAPPING_STALE_AFTER"] = "1200"
env["TRACKER_MAPPING_AGENT_TIMEOUT"] = "30"
env["TRACKER_MAPPING_AGENT_MAX_TURNS"] = "6"
env["TRACKER_MAPPING_DISAMBIGUATION_AGENT_TIMEOUT"] = "35"
env["COMMON_PATTERN_ENRICHMENT_INTERVAL"] = "15"
env["COMMON_PATTERN_ENRICHMENT_MAX_CONCURRENCY"] = "4"
env["COMMON_PATTERN_ENRICHMENT_STALE_AFTER"] = "900"
env["COMMON_PATTERN_ENRICHMENT_AGENT_TIMEOUT"] = "50"
env["COMMON_PATTERN_ENRICHMENT_AGENT_MAX_TURNS"] = "5"
env["PROBOD_TRACKER_MAPPING_INTERVAL"] = "20"
env["PROBOD_TRACKER_MAPPING_MAX_CONCURRENCY"] = "5"
env["PROBOD_TRACKER_MAPPING_STALE_AFTER"] = "1200"
env["PROBOD_TRACKER_MAPPING_AGENT_TIMEOUT"] = "30"
env["PROBOD_TRACKER_MAPPING_AGENT_MAX_TURNS"] = "6"
env["PROBOD_TRACKER_MAPPING_DISAMBIGUATION_AGENT_TIMEOUT"] = "35"
env["PROBOD_COMMON_PATTERN_ENRICHMENT_INTERVAL"] = "15"
env["PROBOD_COMMON_PATTERN_ENRICHMENT_MAX_CONCURRENCY"] = "4"
env["PROBOD_COMMON_PATTERN_ENRICHMENT_STALE_AFTER"] = "900"
env["PROBOD_COMMON_PATTERN_ENRICHMENT_AGENT_TIMEOUT"] = "50"
env["PROBOD_COMMON_PATTERN_ENRICHMENT_AGENT_MAX_TURNS"] = "5"
// Common third party enrichment agent + worker tuning override
env["AGENT_COMMON_THIRD_PARTY_ENRICHMENT_PROVIDER"] = "openai"
env["AGENT_COMMON_THIRD_PARTY_ENRICHMENT_MODEL_NAME"] = "gpt-4o"
env["AGENT_COMMON_THIRD_PARTY_ENRICHMENT_MAX_TOKENS"] = "16384"
env["COMMON_THIRD_PARTY_ENRICHMENT_INTERVAL"] = "25"
env["COMMON_THIRD_PARTY_ENRICHMENT_MAX_CONCURRENCY"] = "2"
env["COMMON_THIRD_PARTY_ENRICHMENT_STALE_AFTER"] = "1200"
env["COMMON_THIRD_PARTY_ENRICHMENT_AGENT_TIMEOUT"] = "120"
env["COMMON_THIRD_PARTY_ENRICHMENT_AGENT_MAX_TURNS"] = "8"
env["COMMON_THIRD_PARTY_ENRICHMENT_CONFIDENCE_THRESHOLD"] = "0.85"
env["COMMON_THIRD_PARTY_ENRICHMENT_MAX_ATTEMPTS"] = "5"
env["THIRD_PARTY_VETTING_INTERVAL"] = "15"
env["THIRD_PARTY_VETTING_STALE_AFTER"] = "1800"
env["THIRD_PARTY_VETTING_MAX_CONCURRENCY"] = "2"
env["PROBOD_AGENT_COMMON_THIRD_PARTY_ENRICHMENT_PROVIDER"] = "openai"
env["PROBOD_AGENT_COMMON_THIRD_PARTY_ENRICHMENT_MODEL_NAME"] = "gpt-4o"
env["PROBOD_AGENT_COMMON_THIRD_PARTY_ENRICHMENT_MAX_TOKENS"] = "16384"
env["PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_INTERVAL"] = "25"
env["PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_MAX_CONCURRENCY"] = "2"
env["PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_STALE_AFTER"] = "1200"
env["PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_AGENT_TIMEOUT"] = "120"
env["PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_AGENT_MAX_TURNS"] = "8"
env["PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_CONFIDENCE_THRESHOLD"] = "0.85"
env["PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_MAX_ATTEMPTS"] = "5"
env["PROBOD_THIRD_PARTY_VETTING_INTERVAL"] = "15"
env["PROBOD_THIRD_PARTY_VETTING_STALE_AFTER"] = "1800"
env["PROBOD_THIRD_PARTY_VETTING_MAX_CONCURRENCY"] = "2"
// Custom domains
env["CUSTOM_DOMAINS_RESOLVER_ADDR"] = "1.1.1.1:53"
env["ACME_ACCOUNT_KEY"] = "-----BEGIN EC PRIVATE KEY-----\ntest\n-----END EC PRIVATE KEY-----"
env["PROBOD_CUSTOM_DOMAINS_RESOLVER_ADDR"] = "1.1.1.1:53"
env["PROBOD_ACME_ACCOUNT_KEY"] = "-----BEGIN EC PRIVATE KEY-----\ntest\n-----END EC PRIVATE KEY-----"
// SCIM bridge
env["SCIM_BRIDGE_SYNC_INTERVAL"] = "1800"
env["SCIM_BRIDGE_POLL_INTERVAL"] = "60"
env["PROBOD_SCIM_BRIDGE_SYNC_INTERVAL"] = "1800"
env["PROBOD_SCIM_BRIDGE_POLL_INTERVAL"] = "60"
// ESign
env["ESIGN_TSA_URL"] = "http://custom.tsa.example.com"
env["PROBOD_ESIGN_TSA_URL"] = "http://custom.tsa.example.com"
// Branding
env["BRANDING"] = "false"
env["PROBOD_BRANDING"] = "false"
b := NewBuilder(mockEnv(env))
b := NewBuilder(NewResolver(mockEnv(env)))
b.samlCertificate = "test-cert"
b.samlPrivateKey = "test-key"
@@ -545,10 +545,10 @@ func TestBuilder_Build_CustomValues(t *testing.T) {
func TestBuilder_Build_GoogleWorkspaceConnector(t *testing.T) {
env := requiredEnv()
env["CONNECTOR_GOOGLE_WORKSPACE_CLIENT_ID"] = "gw-client-id"
env["CONNECTOR_GOOGLE_WORKSPACE_CLIENT_SECRET"] = "gw-client-secret"
env["PROBOD_CONNECTOR_GOOGLE_WORKSPACE_CLIENT_ID"] = "gw-client-id"
env["PROBOD_CONNECTOR_GOOGLE_WORKSPACE_CLIENT_SECRET"] = "gw-client-secret"
b := NewBuilder(mockEnv(env))
b := NewBuilder(NewResolver(mockEnv(env)))
b.samlCertificate = "test-cert"
b.samlPrivateKey = "test-key"
@@ -566,10 +566,10 @@ func TestBuilder_Build_GoogleWorkspaceConnector(t *testing.T) {
func TestBuilder_Build_Microsoft365Connector(t *testing.T) {
env := requiredEnv()
env["CONNECTOR_MICROSOFT_365_CLIENT_ID"] = "ms365-client-id"
env["CONNECTOR_MICROSOFT_365_CLIENT_SECRET"] = "ms365-client-secret"
env["PROBOD_CONNECTOR_MICROSOFT_365_CLIENT_ID"] = "ms365-client-id"
env["PROBOD_CONNECTOR_MICROSOFT_365_CLIENT_SECRET"] = "ms365-client-secret"
b := NewBuilder(mockEnv(env))
b := NewBuilder(NewResolver(mockEnv(env)))
b.samlCertificate = "test-cert"
b.samlPrivateKey = "test-key"
@@ -592,16 +592,16 @@ func TestBuilder_Build_AccessReviewConnectors(t *testing.T) {
providers := []string{
"GITLAB", "BITBUCKET", "HEROKU", "PAGERDUTY",
"ASANA", "NETLIFY", "CLICKUP", "MONDAY", "DATADOG",
"ZENDESK",
"ZENDESK", "LINEAR",
}
env := requiredEnv()
for _, provider := range providers {
env["CONNECTOR_"+provider+"_CLIENT_ID"] = strings.ToLower(provider) + "-id"
env["CONNECTOR_"+provider+"_CLIENT_SECRET"] = strings.ToLower(provider) + "-secret"
env["PROBOD_CONNECTOR_"+provider+"_CLIENT_ID"] = strings.ToLower(provider) + "-id"
env["PROBOD_CONNECTOR_"+provider+"_CLIENT_SECRET"] = strings.ToLower(provider) + "-secret"
}
b := NewBuilder(mockEnv(env))
b := NewBuilder(NewResolver(mockEnv(env)))
b.samlCertificate = "test-cert"
b.samlPrivateKey = "test-key"
@@ -628,11 +628,11 @@ func TestBuilder_Build_AccessReviewConnectors(t *testing.T) {
func TestBuilder_Build_VercelConnector(t *testing.T) {
env := requiredEnv()
env["CONNECTOR_VERCEL_CLIENT_ID"] = "vercel-id"
env["CONNECTOR_VERCEL_CLIENT_SECRET"] = "vercel-secret"
env["CONNECTOR_VERCEL_INTEGRATION_SLUG"] = "probo-app"
env["PROBOD_CONNECTOR_VERCEL_CLIENT_ID"] = "vercel-id"
env["PROBOD_CONNECTOR_VERCEL_CLIENT_SECRET"] = "vercel-secret"
env["PROBOD_CONNECTOR_VERCEL_INTEGRATION_SLUG"] = "probo-app"
b := NewBuilder(mockEnv(env))
b := NewBuilder(NewResolver(mockEnv(env)))
b.samlCertificate = "test-cert"
b.samlPrivateKey = "test-key"
@@ -651,11 +651,11 @@ func TestBuilder_Build_VercelConnector(t *testing.T) {
func TestBuilder_Build_SlackConnector(t *testing.T) {
env := requiredEnv()
env["CONNECTOR_SLACK_CLIENT_ID"] = "slack-client-id"
env["CONNECTOR_SLACK_CLIENT_SECRET"] = "slack-client-secret"
env["CONNECTOR_SLACK_SIGNING_SECRET"] = "slack-signing-secret"
env["PROBOD_CONNECTOR_SLACK_CLIENT_ID"] = "slack-client-id"
env["PROBOD_CONNECTOR_SLACK_CLIENT_SECRET"] = "slack-client-secret"
env["PROBOD_CONNECTOR_SLACK_SIGNING_SECRET"] = "slack-signing-secret"
b := NewBuilder(mockEnv(env))
b := NewBuilder(NewResolver(mockEnv(env)))
b.samlCertificate = "test-cert"
b.samlPrivateKey = "test-key"
@@ -675,7 +675,7 @@ func TestBuilder_Build_SlackConnector(t *testing.T) {
}
func TestBuilder_Build_SAMLAutoGeneration(t *testing.T) {
b := NewBuilder(mockEnv(requiredEnv()))
b := NewBuilder(NewResolver(mockEnv(requiredEnv())))
cfg, err := b.Build()
require.NoError(t, err)
@@ -688,10 +688,10 @@ func TestBuilder_Build_SAMLAutoGeneration(t *testing.T) {
func TestBuilder_Build_SAMLFromEnv(t *testing.T) {
env := requiredEnv()
env["SAML_CERTIFICATE"] = "env-cert"
env["SAML_PRIVATE_KEY"] = "env-key"
env["PROBOD_SAML_CERTIFICATE"] = "env-cert"
env["PROBOD_SAML_PRIVATE_KEY"] = "env-key"
b := NewBuilder(mockEnv(env))
b := NewBuilder(NewResolver(mockEnv(env)))
cfg, err := b.Build()
require.NoError(t, err)
@@ -701,7 +701,7 @@ func TestBuilder_Build_SAMLFromEnv(t *testing.T) {
}
func TestBuilder_Build_SAMLPreset(t *testing.T) {
b := NewBuilder(mockEnv(requiredEnv()))
b := NewBuilder(NewResolver(mockEnv(requiredEnv())))
b.samlCertificate = "preset-cert"
b.samlPrivateKey = "preset-key"
@@ -713,7 +713,7 @@ func TestBuilder_Build_SAMLPreset(t *testing.T) {
}
func TestBuilder_Build_OAuth2Defaults(t *testing.T) {
b := NewBuilder(mockEnv(requiredEnv()))
b := NewBuilder(NewResolver(mockEnv(requiredEnv())))
cfg, err := b.Build()
require.NoError(t, err)
@@ -733,15 +733,15 @@ func TestBuilder_Build_OAuth2Defaults(t *testing.T) {
func TestBuilder_Build_OAuth2FromEnv(t *testing.T) {
env := requiredEnv()
env["OAUTH2_SERVER_SIGNING_KEY"] = "env-signing-key"
env["OAUTH2_SERVER_SIGNING_KEY_KID"] = "env-kid"
env["OAUTH2_SERVER_ACCESS_TOKEN_DURATION"] = "10"
env["OAUTH2_SERVER_REFRESH_TOKEN_DURATION"] = "20"
env["OAUTH2_SERVER_AUTHORIZATION_CODE_DURATION"] = "30"
env["OAUTH2_SERVER_DEVICE_CODE_DURATION"] = "40"
env["OAUTH2_SERVER_CIMD_ALLOWED_CLIENT_IDS"] = "https://chatgpt.com/oauth/client.json,https://claude.ai/oauth/client.json"
env["PROBOD_OAUTH2_SERVER_SIGNING_KEY"] = "env-signing-key"
env["PROBOD_OAUTH2_SERVER_SIGNING_KEY_KID"] = "env-kid"
env["PROBOD_OAUTH2_SERVER_ACCESS_TOKEN_DURATION"] = "10"
env["PROBOD_OAUTH2_SERVER_REFRESH_TOKEN_DURATION"] = "20"
env["PROBOD_OAUTH2_SERVER_AUTHORIZATION_CODE_DURATION"] = "30"
env["PROBOD_OAUTH2_SERVER_DEVICE_CODE_DURATION"] = "40"
env["PROBOD_OAUTH2_SERVER_CIMD_ALLOWED_CLIENT_IDS"] = "https://chatgpt.com/oauth/client.json,https://claude.ai/oauth/client.json"
b := NewBuilder(mockEnv(env))
b := NewBuilder(NewResolver(mockEnv(env)))
cfg, err := b.Build()
require.NoError(t, err)
@@ -770,7 +770,7 @@ func TestBuilder_Build_OAuth2Preset(t *testing.T) {
env := requiredEnv()
delete(env, "OAUTH2_SERVER_SIGNING_KEY")
b := NewBuilder(mockEnv(env))
b := NewBuilder(NewResolver(mockEnv(env)))
b.oauth2SigningKey = "preset-signing-key"
cfg, err := b.Build()
@@ -782,9 +782,9 @@ func TestBuilder_Build_OAuth2Preset(t *testing.T) {
func TestBuilder_Build_PgCABundleFromEnv(t *testing.T) {
env := requiredEnv()
env["PG_CA_BUNDLE"] = "test-ca-bundle-content"
env["PROBOD_PG_CA_BUNDLE"] = "test-ca-bundle-content"
b := NewBuilder(mockEnv(env))
b := NewBuilder(NewResolver(mockEnv(env)))
b.samlCertificate = "test-cert"
b.samlPrivateKey = "test-key"
@@ -801,9 +801,9 @@ func TestBuilder_Build_PgCABundleFromFile(t *testing.T) {
require.NoError(t, err)
env := requiredEnv()
env["PG_CA_BUNDLE_PATH"] = caFile
env["PROBOD_PG_CA_BUNDLE_PATH"] = caFile
b := NewBuilder(mockEnv(env))
b := NewBuilder(NewResolver(mockEnv(env)))
b.samlCertificate = "test-cert"
b.samlPrivateKey = "test-key"