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:
201
.env.example
201
.env.example
@@ -11,127 +11,136 @@
|
|||||||
# - `#` starts a comment, `export KEY=value` is accepted.
|
# - `#` starts a comment, `export KEY=value` is accepted.
|
||||||
# - Malformed assignments (e.g. unquoted spaces) abort `make dev-config`.
|
# - Malformed assignments (e.g. unquoted spaces) abort `make dev-config`.
|
||||||
#
|
#
|
||||||
# See pkg/bootstrap/builder.go for the full env-var surface; the values
|
# All probod-bootstrap inputs are prefixed with PROBOD_. See pkg/bootstrap/builder.go; the values
|
||||||
# set below in the Makefile's `cfg/dev.yaml` recipe are the dev defaults
|
# set below in the Makefile's `cfg/dev.yaml` recipe are the dev defaults
|
||||||
# that this file can override.
|
# that this file can override.
|
||||||
|
|
||||||
# ── Base URL & ports ──────────────────────────────────────────────────
|
# ── Base URL & ports ──────────────────────────────────────────────────
|
||||||
# PROBOD_BASE_URL=http://localhost:8080
|
# PROBOD_BASE_URL=http://localhost:8080
|
||||||
# API_ADDR=:8080
|
# PROBOD_API_ADDR=:8080
|
||||||
# API_CORS_ALLOWED_ORIGINS=http://localhost:8080,http://localhost:5173,http://localhost:5174
|
# PROBOD_API_CORS_ALLOWED_ORIGINS=http://localhost:8080,http://localhost:5173,http://localhost:5174
|
||||||
# TRUST_CENTER_HTTP_ADDR=:80
|
# PROBOD_TRUST_CENTER_HTTP_ADDR=:80
|
||||||
# TRUST_CENTER_HTTPS_ADDR=:443
|
# PROBOD_TRUST_CENTER_HTTPS_ADDR=:443
|
||||||
|
|
||||||
# ── Observability ─────────────────────────────────────────────────────
|
# ── Observability ─────────────────────────────────────────────────────
|
||||||
# METRICS_ADDR=localhost:8081
|
# PROBOD_METRICS_ADDR=localhost:8081
|
||||||
# TRACING_ADDR=localhost:4317
|
# PROBOD_TRACING_ADDR=localhost:4317
|
||||||
|
|
||||||
# ── Secrets (rotate before prod) ──────────────────────────────────────
|
# ── Secrets (rotate before prod) ──────────────────────────────────────
|
||||||
# PROBOD_ENCRYPTION_KEY=thisisnotasecretAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
# PROBOD_ENCRYPTION_KEY=thisisnotasecretAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
||||||
# AUTH_COOKIE_SECRET=this-is-a-secure-secret-for-cookie-signing-at-least-32-bytes
|
# PROBOD_AUTH_COOKIE_SECRET=this-is-a-secure-secret-for-cookie-signing-at-least-32-bytes
|
||||||
# AUTH_PASSWORD_PEPPER=this-is-a-secure-pepper-for-password-hashing-at-least-32-bytes
|
# PROBOD_AUTH_PASSWORD_PEPPER=this-is-a-secure-pepper-for-password-hashing-at-least-32-bytes
|
||||||
|
#
|
||||||
|
# Optional: load secrets from AWS Secrets Manager by setting env vars to
|
||||||
|
# aws://<secret-id> (e.g. PROBOD_ENCRYPTION_KEY=aws://probo/sandbox/probod/encryption_key).
|
||||||
|
# The path after aws:// is the Secrets Manager secret name or ARN; the
|
||||||
|
# plaintext SecretString is used directly. Plain env values are also supported.
|
||||||
|
# Requires AWS credentials for Secrets Manager resolution (IAM role, AWS_ACCESS_KEY_ID,
|
||||||
|
# or AWS_PROFILE) and AWS_REGION. These use the standard AWS SDK default chain, not
|
||||||
|
# PROBOD_AWS_*.
|
||||||
|
# PROBOD_ENCRYPTION_KEY=aws://probo/sandbox/probod/encryption_key
|
||||||
|
|
||||||
# ── Cookie ────────────────────────────────────────────────────────────
|
# ── Cookie ────────────────────────────────────────────────────────────
|
||||||
# AUTH_COOKIE_DOMAIN=localhost
|
# PROBOD_AUTH_COOKIE_DOMAIN=localhost
|
||||||
# AUTH_COOKIE_SECURE=false
|
# PROBOD_AUTH_COOKIE_SECURE=false
|
||||||
# AUTH_COOKIE_DURATION=24
|
# PROBOD_AUTH_COOKIE_DURATION=24
|
||||||
|
|
||||||
# ── Postgres ──────────────────────────────────────────────────────────
|
# ── Postgres ──────────────────────────────────────────────────────────
|
||||||
# PG_ADDR=localhost:5432
|
# PROBOD_PG_ADDR=localhost:5432
|
||||||
# PG_USERNAME=postgres
|
# PROBOD_PG_USERNAME=postgres
|
||||||
# PG_PASSWORD=postgres
|
# PROBOD_PG_PASSWORD=postgres
|
||||||
# PG_DATABASE=probod
|
# PROBOD_PG_DATABASE=probod
|
||||||
# PG_POOL_SIZE=100
|
# PROBOD_PG_POOL_SIZE=100
|
||||||
# PG_MIN_POOL_SIZE=10
|
# PROBOD_PG_MIN_POOL_SIZE=10
|
||||||
# PG_MAX_CONN_IDLE_TIME_SECONDS=1800
|
# PROBOD_PG_MAX_CONN_IDLE_TIME_SECONDS=1800
|
||||||
# PG_MAX_CONN_LIFETIME_SECONDS=3600
|
# PROBOD_PG_MAX_CONN_LIFETIME_SECONDS=3600
|
||||||
# PG_DEBUG=false
|
# PROBOD_PG_DEBUG=false
|
||||||
|
|
||||||
# ── Object storage (SeaweedFS via compose) ────────────────────────────
|
# ── Object storage (SeaweedFS via compose) ────────────────────────────
|
||||||
# AWS_REGION=us-east-1
|
# PROBOD_AWS_REGION=us-east-1
|
||||||
# AWS_BUCKET=probod
|
# PROBOD_AWS_BUCKET=probod
|
||||||
# AWS_ACCESS_KEY_ID=probod
|
# PROBOD_AWS_ACCESS_KEY_ID=probod
|
||||||
# AWS_SECRET_ACCESS_KEY=thisisnotasecret
|
# PROBOD_AWS_SECRET_ACCESS_KEY=thisisnotasecret
|
||||||
# AWS_ENDPOINT=http://127.0.0.1:8333
|
# PROBOD_AWS_ENDPOINT=http://127.0.0.1:8333
|
||||||
|
|
||||||
# ── Mailer (Mailpit via compose) ──────────────────────────────────────
|
# ── Mailer (Mailpit via compose) ──────────────────────────────────────
|
||||||
# SMTP_ADDR=localhost:1025
|
# PROBOD_SMTP_ADDR=localhost:1025
|
||||||
# SMTP_USER=
|
# PROBOD_SMTP_USER=
|
||||||
# SMTP_PASSWORD=
|
# PROBOD_SMTP_PASSWORD=
|
||||||
# SMTP_TLS_REQUIRED=false
|
# PROBOD_SMTP_TLS_REQUIRED=false
|
||||||
# MAILER_SENDER_EMAIL=no-reply@notification.getprobo.com
|
# PROBOD_MAILER_SENDER_EMAIL=no-reply@notification.getprobo.com
|
||||||
# MAILER_SENDER_NAME=Probo
|
# PROBOD_MAILER_SENDER_NAME=Probo
|
||||||
|
|
||||||
# ── LLM providers ─────────────────────────────────────────────────────
|
# ── LLM providers ─────────────────────────────────────────────────────
|
||||||
# OPENAI_API_KEY=sk-...
|
# PROBOD_OPENAI_API_KEY=sk-...
|
||||||
# ANTHROPIC_API_KEY=sk-ant-...
|
# PROBOD_ANTHROPIC_API_KEY=sk-ant-...
|
||||||
# LLM_DEFAULT_PROVIDER=openai
|
# PROBOD_LLM_DEFAULT_PROVIDER=openai
|
||||||
# LLM_DEFAULT_MODEL_NAME=gpt-4o
|
# PROBOD_LLM_DEFAULT_MODEL_NAME=gpt-4o
|
||||||
|
|
||||||
# ── LLM-backed workers (inherit from LLM defaults when unset) ─────────
|
# ── LLM-backed workers (inherit from LLM defaults when unset) ─────────
|
||||||
# PROBO_AGENT_PROVIDER=anthropic
|
# PROBOD_PROBO_AGENT_PROVIDER=anthropic
|
||||||
# PROBO_AGENT_MODEL_NAME=claude-sonnet-4-6
|
# PROBOD_PROBO_AGENT_MODEL_NAME=claude-sonnet-4-6
|
||||||
# EVIDENCE_DESCRIBER_PROVIDER=openai
|
# PROBOD_EVIDENCE_DESCRIBER_PROVIDER=openai
|
||||||
# EVIDENCE_DESCRIBER_MODEL_NAME=gpt-4o-mini
|
# PROBOD_EVIDENCE_DESCRIBER_MODEL_NAME=gpt-4o-mini
|
||||||
# AGENT_THIRD_PARTY_VETTER_PROVIDER=openai # inherits AGENT_DEFAULT_PROVIDER when unset
|
# PROBOD_AGENT_THIRD_PARTY_VETTER_PROVIDER=openai # inherits AGENT_DEFAULT_PROVIDER when unset
|
||||||
# AGENT_THIRD_PARTY_VETTER_MODEL_NAME=gpt-4o # inherits AGENT_DEFAULT_MODEL_NAME when unset
|
# PROBOD_AGENT_THIRD_PARTY_VETTER_MODEL_NAME=gpt-4o # inherits AGENT_DEFAULT_MODEL_NAME when unset
|
||||||
# THIRD_PARTY_VETTING_INTERVAL=10
|
# PROBOD_THIRD_PARTY_VETTING_INTERVAL=10
|
||||||
# THIRD_PARTY_VETTING_STALE_AFTER=1500
|
# PROBOD_THIRD_PARTY_VETTING_STALE_AFTER=1500
|
||||||
# THIRD_PARTY_VETTING_MAX_CONCURRENCY=1
|
# PROBOD_THIRD_PARTY_VETTING_MAX_CONCURRENCY=1
|
||||||
|
|
||||||
# ── Document signature/approval notifications (seconds) ───────────────
|
# ── Document signature/approval notifications (seconds) ───────────────
|
||||||
# DOCUMENT_NOTIFICATION_INTERVAL=300
|
# PROBOD_DOCUMENT_NOTIFICATION_INTERVAL=300
|
||||||
# DOCUMENT_NOTIFICATION_DEBOUNCE_DELAY=900
|
# PROBOD_DOCUMENT_NOTIFICATION_DEBOUNCE_DELAY=900
|
||||||
# DOCUMENT_NOTIFICATION_REMINDER_INTERVAL=86400
|
# PROBOD_DOCUMENT_NOTIFICATION_REMINDER_INTERVAL=86400
|
||||||
|
|
||||||
# ── OIDC sign-in providers ────────────────────────────────────────────
|
# ── OIDC sign-in providers ────────────────────────────────────────────
|
||||||
# AUTH_GOOGLE_CLIENT_ID=
|
# PROBOD_AUTH_GOOGLE_CLIENT_ID=
|
||||||
# AUTH_GOOGLE_CLIENT_SECRET=
|
# PROBOD_AUTH_GOOGLE_CLIENT_SECRET=
|
||||||
# AUTH_MICROSOFT_CLIENT_ID=
|
# PROBOD_AUTH_MICROSOFT_CLIENT_ID=
|
||||||
# AUTH_MICROSOFT_CLIENT_SECRET=
|
# PROBOD_AUTH_MICROSOFT_CLIENT_SECRET=
|
||||||
|
|
||||||
# ── Connectors (set _CLIENT_ID to enable; _CLIENT_SECRET is required) ─
|
# ── Connectors (set _CLIENT_ID to enable; _CLIENT_SECRET is required) ─
|
||||||
# CONNECTOR_SLACK_CLIENT_ID=
|
# PROBOD_CONNECTOR_SLACK_CLIENT_ID=
|
||||||
# CONNECTOR_SLACK_CLIENT_SECRET=
|
# PROBOD_CONNECTOR_SLACK_CLIENT_SECRET=
|
||||||
# CONNECTOR_SLACK_SIGNING_SECRET=
|
# PROBOD_CONNECTOR_SLACK_SIGNING_SECRET=
|
||||||
# CONNECTOR_HUBSPOT_CLIENT_ID=
|
# PROBOD_CONNECTOR_HUBSPOT_CLIENT_ID=
|
||||||
# CONNECTOR_HUBSPOT_CLIENT_SECRET=
|
# PROBOD_CONNECTOR_HUBSPOT_CLIENT_SECRET=
|
||||||
# CONNECTOR_GITHUB_CLIENT_ID=
|
# PROBOD_CONNECTOR_GITHUB_CLIENT_ID=
|
||||||
# CONNECTOR_GITHUB_CLIENT_SECRET=
|
# PROBOD_CONNECTOR_GITHUB_CLIENT_SECRET=
|
||||||
# CONNECTOR_NOTION_CLIENT_ID=
|
# PROBOD_CONNECTOR_NOTION_CLIENT_ID=
|
||||||
# CONNECTOR_NOTION_CLIENT_SECRET=
|
# PROBOD_CONNECTOR_NOTION_CLIENT_SECRET=
|
||||||
# CONNECTOR_SENTRY_CLIENT_ID=
|
# PROBOD_CONNECTOR_SENTRY_CLIENT_ID=
|
||||||
# CONNECTOR_SENTRY_CLIENT_SECRET=
|
# PROBOD_CONNECTOR_SENTRY_CLIENT_SECRET=
|
||||||
# CONNECTOR_INTERCOM_CLIENT_ID=
|
# PROBOD_CONNECTOR_INTERCOM_CLIENT_ID=
|
||||||
# CONNECTOR_INTERCOM_CLIENT_SECRET=
|
# PROBOD_CONNECTOR_INTERCOM_CLIENT_SECRET=
|
||||||
# CONNECTOR_DOCUSIGN_CLIENT_ID=
|
# PROBOD_CONNECTOR_DOCUSIGN_CLIENT_ID=
|
||||||
# CONNECTOR_DOCUSIGN_CLIENT_SECRET=
|
# PROBOD_CONNECTOR_DOCUSIGN_CLIENT_SECRET=
|
||||||
# CONNECTOR_BREX_CLIENT_ID=
|
# PROBOD_CONNECTOR_BREX_CLIENT_ID=
|
||||||
# CONNECTOR_BREX_CLIENT_SECRET=
|
# PROBOD_CONNECTOR_BREX_CLIENT_SECRET=
|
||||||
# CONNECTOR_GITLAB_CLIENT_ID=
|
# PROBOD_CONNECTOR_GITLAB_CLIENT_ID=
|
||||||
# CONNECTOR_GITLAB_CLIENT_SECRET=
|
# PROBOD_CONNECTOR_GITLAB_CLIENT_SECRET=
|
||||||
# CONNECTOR_BITBUCKET_CLIENT_ID=
|
# PROBOD_CONNECTOR_BITBUCKET_CLIENT_ID=
|
||||||
# CONNECTOR_BITBUCKET_CLIENT_SECRET=
|
# PROBOD_CONNECTOR_BITBUCKET_CLIENT_SECRET=
|
||||||
# CONNECTOR_HEROKU_CLIENT_ID=
|
# PROBOD_CONNECTOR_HEROKU_CLIENT_ID=
|
||||||
# CONNECTOR_HEROKU_CLIENT_SECRET=
|
# PROBOD_CONNECTOR_HEROKU_CLIENT_SECRET=
|
||||||
# CONNECTOR_PAGERDUTY_CLIENT_ID=
|
# PROBOD_CONNECTOR_PAGERDUTY_CLIENT_ID=
|
||||||
# CONNECTOR_PAGERDUTY_CLIENT_SECRET=
|
# PROBOD_CONNECTOR_PAGERDUTY_CLIENT_SECRET=
|
||||||
# CONNECTOR_ASANA_CLIENT_ID=
|
# PROBOD_CONNECTOR_ASANA_CLIENT_ID=
|
||||||
# CONNECTOR_ASANA_CLIENT_SECRET=
|
# PROBOD_CONNECTOR_ASANA_CLIENT_SECRET=
|
||||||
# CONNECTOR_NETLIFY_CLIENT_ID=
|
# PROBOD_CONNECTOR_NETLIFY_CLIENT_ID=
|
||||||
# CONNECTOR_NETLIFY_CLIENT_SECRET=
|
# PROBOD_CONNECTOR_NETLIFY_CLIENT_SECRET=
|
||||||
# CONNECTOR_CLICKUP_CLIENT_ID=
|
# PROBOD_CONNECTOR_CLICKUP_CLIENT_ID=
|
||||||
# CONNECTOR_CLICKUP_CLIENT_SECRET=
|
# PROBOD_CONNECTOR_CLICKUP_CLIENT_SECRET=
|
||||||
# CONNECTOR_VERCEL_CLIENT_ID=
|
# PROBOD_CONNECTOR_VERCEL_CLIENT_ID=
|
||||||
# CONNECTOR_VERCEL_CLIENT_SECRET=
|
# PROBOD_CONNECTOR_VERCEL_CLIENT_SECRET=
|
||||||
# CONNECTOR_VERCEL_INTEGRATION_SLUG=
|
# PROBOD_CONNECTOR_VERCEL_INTEGRATION_SLUG=
|
||||||
# CONNECTOR_MONDAY_CLIENT_ID=
|
# PROBOD_CONNECTOR_MONDAY_CLIENT_ID=
|
||||||
# CONNECTOR_MONDAY_CLIENT_SECRET=
|
# PROBOD_CONNECTOR_MONDAY_CLIENT_SECRET=
|
||||||
# CONNECTOR_DATADOG_CLIENT_ID=
|
# PROBOD_CONNECTOR_DATADOG_CLIENT_ID=
|
||||||
# CONNECTOR_DATADOG_CLIENT_SECRET=
|
# PROBOD_CONNECTOR_DATADOG_CLIENT_SECRET=
|
||||||
# Zendesk OAuth requires a Zendesk-approved global OAuth client (Marketplace).
|
# Zendesk OAuth requires a Zendesk-approved global OAuth client (Marketplace).
|
||||||
# CONNECTOR_ZENDESK_CLIENT_ID=
|
# PROBOD_CONNECTOR_ZENDESK_CLIENT_ID=
|
||||||
# CONNECTOR_ZENDESK_CLIENT_SECRET=
|
# PROBOD_CONNECTOR_ZENDESK_CLIENT_SECRET=
|
||||||
# PostHog Cloud (US + EU) OAuth needs NO config: it uses the CIMD public-client
|
# PostHog Cloud (US + EU) OAuth needs NO config: it uses the CIMD public-client
|
||||||
# flow (no app registration, no client_secret), auto-enabled when this
|
# flow (no app registration, no client_secret), auto-enabled when this
|
||||||
# deployment is publicly reachable at PROBOD_BASE_URL. Self-hosted PostHog uses
|
# deployment is publicly reachable at PROBOD_BASE_URL. Self-hosted PostHog uses
|
||||||
@@ -140,9 +149,9 @@
|
|||||||
# ── OAuth2 authorization server ───────────────────────────────────────
|
# ── OAuth2 authorization server ───────────────────────────────────────
|
||||||
# Comma-separated HTTPS client metadata document URLs allowed for CIMD
|
# Comma-separated HTTPS client metadata document URLs allowed for CIMD
|
||||||
# OAuth clients (e.g. MCP connectors). Leave unset to disable CIMD.
|
# OAuth clients (e.g. MCP connectors). Leave unset to disable CIMD.
|
||||||
# OAUTH2_SERVER_CIMD_ALLOWED_CLIENT_IDS=https://chatgpt.com/oauth/client.json,https://claude.ai/oauth/client.json
|
# PROBOD_OAUTH2_SERVER_CIMD_ALLOWED_CLIENT_IDS=https://chatgpt.com/oauth/client.json,https://claude.ai/oauth/client.json
|
||||||
|
|
||||||
# ── Custom domains (Pebble ACME via compose) ──────────────────────────
|
# ── Custom domains (Pebble ACME via compose) ──────────────────────────
|
||||||
# CUSTOM_DOMAINS_CNAME_TARGET=custom.getprobo.com
|
# PROBOD_CUSTOM_DOMAINS_CNAME_TARGET=custom.getprobo.com
|
||||||
# ACME_DIRECTORY=https://localhost:14000/dir
|
# PROBOD_ACME_DIRECTORY=https://localhost:14000/dir
|
||||||
# ACME_EMAIL=admin@probo.com
|
# PROBOD_ACME_EMAIL=admin@probo.com
|
||||||
|
|||||||
2
.github/workflows/make.yaml
vendored
2
.github/workflows/make.yaml
vendored
@@ -464,7 +464,7 @@ jobs:
|
|||||||
GOTESTSUM_FORMAT: "testname"
|
GOTESTSUM_FORMAT: "testname"
|
||||||
GOTESTSUM_JUNITFILE: "junit-e2e.xml"
|
GOTESTSUM_JUNITFILE: "junit-e2e.xml"
|
||||||
run: |
|
run: |
|
||||||
ACME_ROOT_CA="$(cat compose/pebble/certs/rootCA.pem)" \
|
PROBOD_ACME_ROOT_CA="$(cat compose/pebble/certs/rootCA.pem)" \
|
||||||
CGO_ENABLED=1 go tool gotestsum -- -race -cover -coverprofile=coverage.out -count=1 ./e2e/console/...
|
CGO_ENABLED=1 go tool gotestsum -- -race -cover -coverprofile=coverage.out -count=1 ./e2e/console/...
|
||||||
- name: "Upload test results"
|
- name: "Upload test results"
|
||||||
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7
|
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7
|
||||||
|
|||||||
25
GNUmakefile
25
GNUmakefile
@@ -201,19 +201,18 @@ $(CFG_DEV_OAUTH2_KEY):
|
|||||||
cfg/dev.yaml: bin/probod-bootstrap $(CFG_DEV_OAUTH2_KEY) compose/pebble/certs/rootCA.pem $(wildcard $(DEV_ENV))
|
cfg/dev.yaml: bin/probod-bootstrap $(CFG_DEV_OAUTH2_KEY) compose/pebble/certs/rootCA.pem $(wildcard $(DEV_ENV))
|
||||||
@$(MKDIR) $(@D)
|
@$(MKDIR) $(@D)
|
||||||
set -a; \
|
set -a; \
|
||||||
PROBOD_ENCRYPTION_KEY="thisisnotasecretAAAAAAAAAAAAAAAAAAAAAAAAAAA="; \
|
PROBOD_ENCRYPTION_KEY="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; \
|
||||||
AUTH_COOKIE_SECRET="this-is-a-secure-secret-for-cookie-signing-at-least-32-bytes"; \
|
PROBOD_AUTH_COOKIE_SECRET="this-is-a-secure-secret-for-cookie-signing-at-least-32-bytes"; \
|
||||||
AUTH_PASSWORD_PEPPER="this-is-a-secure-pepper-for-password-hashing-at-least-32-bytes"; \
|
PROBOD_AUTH_PASSWORD_PEPPER="this-is-a-secure-pepper-for-password-hashing-at-least-32-bytes"; \
|
||||||
AUTH_COOKIE_SECURE=false; \
|
PROBOD_AUTH_COOKIE_SECURE=false; \
|
||||||
OAUTH2_SERVER_SIGNING_KEY="$$($(CAT) $(CFG_DEV_OAUTH2_KEY))"; \
|
PROBOD_OAUTH2_SERVER_SIGNING_KEY="$$($(CAT) $(CFG_DEV_OAUTH2_KEY))"; \
|
||||||
API_CORS_ALLOWED_ORIGINS="http://localhost:8080,http://localhost:5173,http://localhost:5174"; \
|
PROBOD_API_CORS_ALLOWED_ORIGINS="http://localhost:8080,http://localhost:5173,http://localhost:5174"; \
|
||||||
AWS_ACCESS_KEY_ID=probod; \
|
PROBOD_AWS_ACCESS_KEY_ID=probod; \
|
||||||
AWS_SECRET_ACCESS_KEY=thisisnotasecret; \
|
PROBOD_AWS_SECRET_ACCESS_KEY=thisisnotasecret; \
|
||||||
AWS_ENDPOINT=http://127.0.0.1:8333; \
|
PROBOD_AWS_ENDPOINT=http://127.0.0.1:8333; \
|
||||||
OPENAI_API_KEY=thisisnotasecret; \
|
PROBOD_OPENAI_API_KEY=thisisnotasecret; \
|
||||||
AGENT_THIRD_PARTY_VETTER_PROVIDER=openai; \
|
PROBOD_AGENT_THIRD_PARTY_VETTER_PROVIDER=openai; \
|
||||||
ACME_DIRECTORY=https://localhost:14000/dir; \
|
PROBOD_ACME_DIRECTORY=https://localhost:14000/dir; \
|
||||||
ACME_ROOT_CA="$$($(CAT) compose/pebble/certs/rootCA.pem)"; \
|
|
||||||
if [ -f $(DEV_ENV) ]; then . $(DEV_ENV); fi; \
|
if [ -f $(DEV_ENV) ]; then . $(DEV_ENV); fi; \
|
||||||
set +a; \
|
set +a; \
|
||||||
./bin/probod-bootstrap -output $@
|
./bin/probod-bootstrap -output $@
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
builder := bootstrap.NewBuilder(nil)
|
builder := bootstrap.NewBuilder(bootstrap.NewResolver(nil))
|
||||||
|
|
||||||
cfg, err := builder.Build()
|
cfg, err := builder.Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -4,42 +4,42 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
# Required secrets (use secure values in production)
|
# Required secrets (use secure values in production)
|
||||||
PROBOD_ENCRYPTION_KEY: ${PROBOD_ENCRYPTION_KEY}
|
PROBOD_ENCRYPTION_KEY: ${PROBOD_ENCRYPTION_KEY}
|
||||||
AUTH_COOKIE_SECRET: ${AUTH_COOKIE_SECRET}
|
PROBOD_AUTH_COOKIE_SECRET: ${PROBOD_AUTH_COOKIE_SECRET}
|
||||||
AUTH_PASSWORD_PEPPER: ${AUTH_PASSWORD_PEPPER}
|
PROBOD_AUTH_PASSWORD_PEPPER: ${PROBOD_AUTH_PASSWORD_PEPPER}
|
||||||
TRUST_AUTH_TOKEN_SECRET: ${TRUST_AUTH_TOKEN_SECRET}
|
PROBOD_TRUST_AUTH_TOKEN_SECRET: ${PROBOD_TRUST_AUTH_TOKEN_SECRET}
|
||||||
|
|
||||||
# Application settings
|
# Application settings
|
||||||
PROBOD_BASE_URL: ${PROBOD_BASE_URL}
|
PROBOD_BASE_URL: ${PROBOD_BASE_URL}
|
||||||
API_ADDR: ${API_ADDR}
|
PROBOD_API_ADDR: ${PROBOD_API_ADDR}
|
||||||
API_CORS_ALLOWED_ORIGINS: ${API_CORS_ALLOWED_ORIGINS}
|
PROBOD_API_CORS_ALLOWED_ORIGINS: ${PROBOD_API_CORS_ALLOWED_ORIGINS}
|
||||||
|
|
||||||
# PostgreSQL database
|
# PostgreSQL database
|
||||||
PG_ADDR: "postgres:5432"
|
PROBOD_PG_ADDR: "postgres:5432"
|
||||||
PG_USERNAME: "postgres"
|
PROBOD_PG_USERNAME: "postgres"
|
||||||
PG_PASSWORD: "postgres"
|
PROBOD_PG_PASSWORD: "postgres"
|
||||||
PG_DATABASE: "probod"
|
PROBOD_PG_DATABASE: "probod"
|
||||||
PG_POOL_SIZE: "100"
|
PROBOD_PG_POOL_SIZE: "100"
|
||||||
|
|
||||||
# S3-compatible storage (SeaweedFS)
|
# S3-compatible storage (SeaweedFS)
|
||||||
AWS_REGION: "us-east-1"
|
PROBOD_AWS_REGION: "us-east-1"
|
||||||
AWS_BUCKET: "probod"
|
PROBOD_AWS_BUCKET: "probod"
|
||||||
AWS_ACCESS_KEY_ID: "probod"
|
PROBOD_AWS_ACCESS_KEY_ID: "probod"
|
||||||
AWS_SECRET_ACCESS_KEY: "thisisnotasecret"
|
PROBOD_AWS_SECRET_ACCESS_KEY: "thisisnotasecret"
|
||||||
AWS_ENDPOINT: "http://seaweedfs:8333"
|
PROBOD_AWS_ENDPOINT: "http://seaweedfs:8333"
|
||||||
AWS_USE_PATH_STYLE: "true"
|
PROBOD_AWS_USE_PATH_STYLE: "true"
|
||||||
|
|
||||||
# Observability - Metrics & Tracing
|
# Observability - Metrics & Tracing
|
||||||
METRICS_ADDR: "probo:8081"
|
PROBOD_METRICS_ADDR: "probo:8081"
|
||||||
TRACING_ADDR: ""
|
PROBOD_TRACING_ADDR: ""
|
||||||
|
|
||||||
# Email notifications
|
# Email notifications
|
||||||
SMTP_ADDR: "your.smtp.server:587"
|
PROBOD_SMTP_ADDR: "your.smtp.server:587"
|
||||||
SMTP_TLS_REQUIRED: "false"
|
PROBOD_SMTP_TLS_REQUIRED: "false"
|
||||||
MAILER_SENDER_NAME: "Probo"
|
PROBOD_MAILER_SENDER_NAME: "Probo"
|
||||||
MAILER_SENDER_EMAIL: "no-reply@notification.getprobo.com"
|
PROBOD_MAILER_SENDER_EMAIL: "no-reply@notification.getprobo.com"
|
||||||
|
|
||||||
# Chrome for PDF generation
|
# Chrome for PDF generation
|
||||||
CHROME_DP_ADDR: "chrome:9222"
|
PROBOD_CHROME_DP_ADDR: "chrome:9222"
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
- "8081:8081"
|
- "8081:8081"
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ Go struct (pkg/probod/)
|
|||||||
│
|
│
|
||||||
├─► bootstrap builder.go (env var → struct)
|
├─► bootstrap builder.go (env var → struct)
|
||||||
│ │
|
│ │
|
||||||
|
│ ├─► Resolver (aws:// secret-id refs + plaintext env literals)
|
||||||
│ ├─► GNUmakefile dev-config (env vars → probod-bootstrap → cfg/dev.yaml)
|
│ ├─► GNUmakefile dev-config (env vars → probod-bootstrap → cfg/dev.yaml)
|
||||||
│ ├─► e2e/internal/testutil/ (env map → bootstrap.Build, tests)
|
│ ├─► e2e/internal/testutil/ (env map → bootstrap.Build, tests)
|
||||||
│ ├─► contrib/lima/provision.sh (env vars → probod-bootstrap)
|
│ ├─► contrib/lima/provision.sh (env vars → probod-bootstrap)
|
||||||
@@ -42,11 +43,11 @@ Go struct (pkg/probod/)
|
|||||||
## Rules
|
## Rules
|
||||||
|
|
||||||
1. **Never add a Go config field without updating every file in the checklist.**
|
1. **Never add a Go config field without updating every file in the checklist.**
|
||||||
2. **Env var naming** — follow the existing convention in `builder.go`: `SECTION_FIELD_NAME` (e.g. `AUTH_COOKIE_DOMAIN`, `CUSTOM_DOMAINS_RENEWAL_INTERVAL`).
|
2. **Env var naming** — `probod-bootstrap` reads every input from the process environment with a `PROBOD_` prefix (e.g. `PROBOD_AUTH_COOKIE_DOMAIN`, `PROBOD_CUSTOM_DOMAINS_RENEWAL_INTERVAL`). Use the full name in `builder.go`, Helm templates, and docs.
|
||||||
3. **Secrets** go through `secret.yaml` and are referenced via `secretKeyRef` in `deployment.yaml`. Non-secret values are set inline.
|
3. **Secrets** go through `secret.yaml` and are referenced via `secretKeyRef` in `deployment.yaml`. Non-secret values are set inline.
|
||||||
4. **`make dev-config`** writes `cfg/dev.yaml` via `probod-bootstrap` with safe, non-production defaults (plaintext passwords, `localhost`, `secure: false`). The generated file and the per-dev OAuth2 signing key (`cfg/.dev-oauth2-signing-key.pem`) are both gitignored. The recipe sources `.env` at the repo root if present so devs can override any env var without editing the `GNUmakefile`; keep `.env.example` in sync when you add or rename env vars.
|
4. **`make dev-config`** writes `cfg/dev.yaml` via `probod-bootstrap` with safe, non-production defaults (plaintext passwords, `localhost`, `secure: false`). The generated file and the per-dev OAuth2 signing key (`cfg/.dev-oauth2-signing-key.pem`) are both gitignored. The recipe sources `.env` at the repo root if present so devs can override any env var without editing the `GNUmakefile`; keep `.env.example` in sync when you add or rename env vars.
|
||||||
5. **`e2e/internal/testutil/testutil.go`** builds the e2e config through `bootstrap.NewBuilder` with a test-only env-var map (different ports, `probod_test` DB, shorter intervals). Any new field whose test value differs from the bootstrap default must be added to that map.
|
5. **`e2e/internal/testutil/testutil.go`** builds the e2e config through `bootstrap.NewBuilder` with a test-only env-var map (different ports, `probod_test` DB, shorter intervals). Any new field whose test value differs from the bootstrap default must be added to that map.
|
||||||
6. **`provision.sh`** only sets env vars that differ from `builder.go` defaults (e.g. `PROBOD_BASE_URL`, `AUTH_COOKIE_DOMAIN`, `AUTH_COOKIE_SECURE`). If the new field's default is acceptable in the sandbox, no env var is needed.
|
6. **`provision.sh`** only sets env vars that differ from `builder.go` defaults (e.g. `PROBOD_BASE_URL`, `PROBOD_AUTH_COOKIE_DOMAIN`, `PROBOD_AUTH_COOKIE_SECURE`). If the new field's default is acceptable in the sandbox, no env var is needed.
|
||||||
7. **Helm `values.yaml`** exposes the field under the appropriate `probo.*` key with a sensible default. `values-production.yaml.example` includes it only when the production value differs or the user must set it.
|
7. **Helm `values.yaml`** exposes the field under the appropriate `probo.*` key with a sensible default. `values-production.yaml.example` includes it only when the production value differs or the user must set it.
|
||||||
8. **Optional features** (custom domains, SAML, connectors, tracing) are gated by `{{- if }}` blocks in the Helm templates; follow the same pattern for new optional fields.
|
8. **Optional features** (custom domains, SAML, connectors, tracing) are gated by `{{- if }}` blocks in the Helm templates; follow the same pattern for new optional fields.
|
||||||
9. **Bootstrap tests** (`pkg/bootstrap/builder_test.go`) must cover the new env var mapping.
|
9. **Bootstrap tests** (`pkg/bootstrap/builder_test.go`) must cover the new env var mapping.
|
||||||
|
|||||||
@@ -2,10 +2,28 @@
|
|||||||
|
|
||||||
E2E tests live in `e2e/console/` (package `console_test`) and run against a live `bin/probod` instance. The test infrastructure handles server lifecycle, authentication, and test data creation.
|
E2E tests live in `e2e/console/` (package `console_test`) and run against a live `bin/probod` instance. The test infrastructure handles server lifecycle, authentication, and test data creation.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
E2e uses the local [Pebble](https://github.com/letsencrypt/pebble) ACME server over HTTPS. Pebble’s TLS certificate is minted with [mkcert](https://github.com/FiloSottile/mkcert); register mkcert’s root CA in your system trust store once per machine:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkcert -install
|
||||||
|
```
|
||||||
|
|
||||||
|
Without this step, probod cannot verify Pebble’s HTTPS endpoint when it registers an ACME account at startup.
|
||||||
|
|
||||||
|
You also need the Docker stack running and `bin/probod` built. `make stack-up` generates Pebble TLS material under `compose/pebble/certs/` (via mkcert):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make stack-up
|
||||||
|
make build
|
||||||
|
```
|
||||||
|
|
||||||
|
E2e config is built at test startup in `e2e/internal/testutil/testutil.go` (`generateConfig` → `probod-bootstrap`). It points ACME at Pebble but does not set `PROBOD_ACME_ROOT_CA`; local runs rely on the system trust store populated by `mkcert -install`. CI passes `PROBOD_ACME_ROOT_CA` in the workflow instead.
|
||||||
|
|
||||||
## Running tests
|
## Running tests
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make build # Build the binary (backend only)
|
|
||||||
make test-e2e # Run all e2e tests
|
make test-e2e # Run all e2e tests
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -322,6 +322,41 @@ spec:
|
|||||||
key: probo/db-password
|
key: probo/db-password
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Native AWS Secrets Manager (probod-bootstrap)
|
||||||
|
|
||||||
|
Alternatively, `probod-bootstrap` can fetch secrets directly from AWS Secrets
|
||||||
|
Manager without External Secrets Operator. Point env vars at individual secrets
|
||||||
|
with the `aws://<secret-id>` prefix (e.g.
|
||||||
|
`PROBOD_ENCRYPTION_KEY=aws://probo/sandbox/probod/encryption_key`). The path
|
||||||
|
after `aws://` is the secret name or ARN; the plaintext `SecretString` is used
|
||||||
|
directly. Each env var can reference a different secret. Plain env values are
|
||||||
|
also supported for non-sensitive config.
|
||||||
|
|
||||||
|
Grant the caller `secretsmanager:GetSecretValue` on each secret (EKS IRSA
|
||||||
|
example):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
env:
|
||||||
|
- name: AWS_REGION
|
||||||
|
value: "us-east-1"
|
||||||
|
- name: PROBOD_BASE_URL
|
||||||
|
value: "https://app.example.com"
|
||||||
|
- name: PROBOD_ENCRYPTION_KEY
|
||||||
|
value: "aws://probo/sandbox/probod/encryption_key"
|
||||||
|
- name: PROBOD_AUTH_COOKIE_SECRET
|
||||||
|
value: "aws://probo/sandbox/probod/cookie_secret"
|
||||||
|
- name: PROBOD_AUTH_PASSWORD_PEPPER
|
||||||
|
value: "aws://probo/sandbox/probod/password_pepper"
|
||||||
|
- name: PROBOD_OAUTH2_SERVER_SIGNING_KEY
|
||||||
|
value: "aws://probo/sandbox/probod/oauth2_signing_key"
|
||||||
|
```
|
||||||
|
|
||||||
|
Each secret in AWS Secrets Manager stores a single plaintext value (for
|
||||||
|
example a base64 key, password, or PEM).
|
||||||
|
|
||||||
|
When `PROBOD_ENCRYPTION_KEY` or another bootstrap env var is set (including
|
||||||
|
`aws://` references), the container entrypoint runs `probod-bootstrap`.
|
||||||
|
|
||||||
## Full Values
|
## Full Values
|
||||||
|
|
||||||
| Key | Type | Default | Description |
|
| Key | Type | Default | Description |
|
||||||
|
|||||||
@@ -43,19 +43,19 @@ spec:
|
|||||||
- name: CONFIG_FILE
|
- name: CONFIG_FILE
|
||||||
value: "/data/probod/config.yml"
|
value: "/data/probod/config.yml"
|
||||||
# Observability - Metrics
|
# Observability - Metrics
|
||||||
- name: METRICS_ADDR
|
- name: PROBOD_METRICS_ADDR
|
||||||
value: "0.0.0.0:{{ .Values.probo.metrics.port }}"
|
value: "0.0.0.0:{{ .Values.probo.metrics.port }}"
|
||||||
{{- if .Values.probo.tracing.enabled }}
|
{{- if .Values.probo.tracing.enabled }}
|
||||||
# Observability - Tracing
|
# Observability - Tracing
|
||||||
- name: TRACING_ADDR
|
- name: PROBOD_TRACING_ADDR
|
||||||
value: {{ .Values.probo.tracing.addr | quote }}
|
value: {{ .Values.probo.tracing.addr | quote }}
|
||||||
- name: TRACING_MAX_BATCH_SIZE
|
- name: PROBOD_TRACING_MAX_BATCH_SIZE
|
||||||
value: {{ .Values.probo.tracing.maxBatchSize | quote }}
|
value: {{ .Values.probo.tracing.maxBatchSize | quote }}
|
||||||
- name: TRACING_BATCH_TIMEOUT
|
- name: PROBOD_TRACING_BATCH_TIMEOUT
|
||||||
value: {{ .Values.probo.tracing.batchTimeout | quote }}
|
value: {{ .Values.probo.tracing.batchTimeout | quote }}
|
||||||
- name: TRACING_EXPORT_TIMEOUT
|
- name: PROBOD_TRACING_EXPORT_TIMEOUT
|
||||||
value: {{ .Values.probo.tracing.exportTimeout | quote }}
|
value: {{ .Values.probo.tracing.exportTimeout | quote }}
|
||||||
- name: TRACING_MAX_QUEUE_SIZE
|
- name: PROBOD_TRACING_MAX_QUEUE_SIZE
|
||||||
value: {{ .Values.probo.tracing.maxQueueSize | quote }}
|
value: {{ .Values.probo.tracing.maxQueueSize | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
# Application Configuration
|
# Application Configuration
|
||||||
@@ -66,96 +66,96 @@ spec:
|
|||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "probo.fullname" . }}
|
name: {{ include "probo.fullname" . }}
|
||||||
key: encryption-key
|
key: encryption-key
|
||||||
- name: CHROME_DP_ADDR
|
- name: PROBOD_CHROME_DP_ADDR
|
||||||
value: {{ include "probo.chrome.addr" . | quote }}
|
value: {{ include "probo.chrome.addr" . | quote }}
|
||||||
- name: BRANDING
|
- name: PROBOD_BRANDING
|
||||||
value: {{ .Values.probo.branding | quote }}
|
value: {{ .Values.probo.branding | quote }}
|
||||||
# API Configuration
|
# API Configuration
|
||||||
- name: API_ADDR
|
- name: PROBOD_API_ADDR
|
||||||
value: ":{{ .Values.service.port }}"
|
value: ":{{ .Values.service.port }}"
|
||||||
- name: API_CORS_ALLOWED_ORIGINS
|
- name: PROBOD_API_CORS_ALLOWED_ORIGINS
|
||||||
value: {{ join "," .Values.probo.cors.allowedOrigins | quote }}
|
value: {{ join "," .Values.probo.cors.allowedOrigins | quote }}
|
||||||
# PostgreSQL Database
|
# PostgreSQL Database
|
||||||
- name: PG_ADDR
|
- name: PROBOD_PG_ADDR
|
||||||
value: {{ printf "%s:%v" (include "probo.postgresql.host" .) (include "probo.postgresql.port" .) | quote }}
|
value: {{ printf "%s:%v" (include "probo.postgresql.host" .) (include "probo.postgresql.port" .) | quote }}
|
||||||
- name: PG_USERNAME
|
- name: PROBOD_PG_USERNAME
|
||||||
value: {{ include "probo.postgresql.username" . | quote }}
|
value: {{ include "probo.postgresql.username" . | quote }}
|
||||||
- name: PG_PASSWORD
|
- name: PROBOD_PG_PASSWORD
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "probo.fullname" . }}
|
name: {{ include "probo.fullname" . }}
|
||||||
key: db-password
|
key: db-password
|
||||||
- name: PG_DATABASE
|
- name: PROBOD_PG_DATABASE
|
||||||
value: {{ include "probo.postgresql.database" . | quote }}
|
value: {{ include "probo.postgresql.database" . | quote }}
|
||||||
- name: PG_POOL_SIZE
|
- name: PROBOD_PG_POOL_SIZE
|
||||||
value: {{ .Values.postgresql.poolSize | default "100" | quote }}
|
value: {{ .Values.postgresql.poolSize | default "100" | quote }}
|
||||||
- name: PG_MIN_POOL_SIZE
|
- name: PROBOD_PG_MIN_POOL_SIZE
|
||||||
value: {{ .Values.postgresql.minPoolSize | default "10" | quote }}
|
value: {{ .Values.postgresql.minPoolSize | default "10" | quote }}
|
||||||
- name: PG_MAX_CONN_IDLE_TIME_SECONDS
|
- name: PROBOD_PG_MAX_CONN_IDLE_TIME_SECONDS
|
||||||
value: {{ .Values.postgresql.maxConnIdleTimeSeconds | default "1800" | quote }}
|
value: {{ .Values.postgresql.maxConnIdleTimeSeconds | default "1800" | quote }}
|
||||||
- name: PG_MAX_CONN_LIFETIME_SECONDS
|
- name: PROBOD_PG_MAX_CONN_LIFETIME_SECONDS
|
||||||
value: {{ .Values.postgresql.maxConnLifetimeSeconds | default "3600" | quote }}
|
value: {{ .Values.postgresql.maxConnLifetimeSeconds | default "3600" | quote }}
|
||||||
- name: PG_MAX_CONN_LIFETIME_JITTER_SECONDS
|
- name: PROBOD_PG_MAX_CONN_LIFETIME_JITTER_SECONDS
|
||||||
value: {{ .Values.postgresql.maxConnLifetimeJitterSeconds | default "300" | quote }}
|
value: {{ .Values.postgresql.maxConnLifetimeJitterSeconds | default "300" | quote }}
|
||||||
- name: PG_HEALTH_CHECK_PERIOD_SECONDS
|
- name: PROBOD_PG_HEALTH_CHECK_PERIOD_SECONDS
|
||||||
value: {{ .Values.postgresql.healthCheckPeriodSeconds | default "60" | quote }}
|
value: {{ .Values.postgresql.healthCheckPeriodSeconds | default "60" | quote }}
|
||||||
{{- if .Values.postgresql.caBundle }}
|
{{- if .Values.postgresql.caBundle }}
|
||||||
- name: PG_CA_BUNDLE
|
- name: PROBOD_PG_CA_BUNDLE
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "probo.fullname" . }}
|
name: {{ include "probo.fullname" . }}
|
||||||
key: pg-ca-bundle
|
key: pg-ca-bundle
|
||||||
{{- else if .Values.postgresql.caBundlePath }}
|
{{- else if .Values.postgresql.caBundlePath }}
|
||||||
- name: PG_CA_BUNDLE_PATH
|
- name: PROBOD_PG_CA_BUNDLE_PATH
|
||||||
value: {{ .Values.postgresql.caBundlePath | quote }}
|
value: {{ .Values.postgresql.caBundlePath | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
# Authentication
|
# Authentication
|
||||||
- name: AUTH_DISABLE_SIGNUP
|
- name: PROBOD_AUTH_DISABLE_SIGNUP
|
||||||
value: {{ .Values.probo.auth.disableSignup | quote }}
|
value: {{ .Values.probo.auth.disableSignup | quote }}
|
||||||
- name: AUTH_INVITATION_TOKEN_VALIDITY
|
- name: PROBOD_AUTH_INVITATION_TOKEN_VALIDITY
|
||||||
value: {{ .Values.probo.auth.invitationTokenValidity | quote }}
|
value: {{ .Values.probo.auth.invitationTokenValidity | quote }}
|
||||||
- name: AUTH_COOKIE_NAME
|
- name: PROBOD_AUTH_COOKIE_NAME
|
||||||
value: {{ .Values.probo.auth.cookieName | quote }}
|
value: {{ .Values.probo.auth.cookieName | quote }}
|
||||||
- name: AUTH_COOKIE_DOMAIN
|
- name: PROBOD_AUTH_COOKIE_DOMAIN
|
||||||
value: {{ .Values.probo.auth.cookieDomain | quote }}
|
value: {{ .Values.probo.auth.cookieDomain | quote }}
|
||||||
- name: AUTH_COOKIE_SECRET
|
- name: PROBOD_AUTH_COOKIE_SECRET
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "probo.fullname" . }}
|
name: {{ include "probo.fullname" . }}
|
||||||
key: cookie-secret
|
key: cookie-secret
|
||||||
- name: AUTH_COOKIE_DURATION
|
- name: PROBOD_AUTH_COOKIE_DURATION
|
||||||
value: {{ .Values.probo.auth.cookieDuration | quote }}
|
value: {{ .Values.probo.auth.cookieDuration | quote }}
|
||||||
- name: AUTH_PASSWORD_PEPPER
|
- name: PROBOD_AUTH_PASSWORD_PEPPER
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "probo.fullname" . }}
|
name: {{ include "probo.fullname" . }}
|
||||||
key: password-pepper
|
key: password-pepper
|
||||||
- name: OAUTH2_SERVER_SIGNING_KEY
|
- name: PROBOD_OAUTH2_SERVER_SIGNING_KEY
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "probo.fullname" . }}
|
name: {{ include "probo.fullname" . }}
|
||||||
key: oauth2-signing-key
|
key: oauth2-signing-key
|
||||||
{{- if .Values.probo.oauth2.cimdAllowedClientIds }}
|
{{- if .Values.probo.oauth2.cimdAllowedClientIds }}
|
||||||
- name: OAUTH2_SERVER_CIMD_ALLOWED_CLIENT_IDS
|
- name: PROBOD_OAUTH2_SERVER_CIMD_ALLOWED_CLIENT_IDS
|
||||||
value: {{ join "," .Values.probo.oauth2.cimdAllowedClientIds | quote }}
|
value: {{ join "," .Values.probo.oauth2.cimdAllowedClientIds | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
- name: AUTH_PASSWORD_ITERATIONS
|
- name: PROBOD_AUTH_PASSWORD_ITERATIONS
|
||||||
value: {{ .Values.probo.auth.passwordIterations | quote }}
|
value: {{ .Values.probo.auth.passwordIterations | quote }}
|
||||||
{{- if .Values.probo.saml.enabled }}
|
{{- if .Values.probo.saml.enabled }}
|
||||||
# SAML Authentication
|
# SAML Authentication
|
||||||
- name: SAML_SESSION_DURATION
|
- name: PROBOD_SAML_SESSION_DURATION
|
||||||
value: {{ .Values.probo.saml.sessionDuration | quote }}
|
value: {{ .Values.probo.saml.sessionDuration | quote }}
|
||||||
- name: SAML_CLEANUP_INTERVAL_SECONDS
|
- name: PROBOD_SAML_CLEANUP_INTERVAL_SECONDS
|
||||||
value: {{ .Values.probo.saml.cleanupIntervalSeconds | quote }}
|
value: {{ .Values.probo.saml.cleanupIntervalSeconds | quote }}
|
||||||
{{- if .Values.probo.saml.certificate }}
|
{{- if .Values.probo.saml.certificate }}
|
||||||
- name: SAML_CERTIFICATE
|
- name: PROBOD_SAML_CERTIFICATE
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "probo.fullname" . }}
|
name: {{ include "probo.fullname" . }}
|
||||||
key: saml-certificate
|
key: saml-certificate
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.saml.privateKey }}
|
{{- if .Values.probo.saml.privateKey }}
|
||||||
- name: SAML_PRIVATE_KEY
|
- name: PROBOD_SAML_PRIVATE_KEY
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "probo.fullname" . }}
|
name: {{ include "probo.fullname" . }}
|
||||||
@@ -163,93 +163,93 @@ spec:
|
|||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
# Trust Center Authentication
|
# Trust Center Authentication
|
||||||
- name: TRUST_AUTH_COOKIE_NAME
|
- name: PROBOD_TRUST_AUTH_COOKIE_NAME
|
||||||
value: {{ .Values.probo.trustAuth.cookieName | quote }}
|
value: {{ .Values.probo.trustAuth.cookieName | quote }}
|
||||||
- name: TRUST_AUTH_COOKIE_DOMAIN
|
- name: PROBOD_TRUST_AUTH_COOKIE_DOMAIN
|
||||||
value: {{ .Values.probo.trustAuth.cookieDomain | quote }}
|
value: {{ .Values.probo.trustAuth.cookieDomain | quote }}
|
||||||
- name: TRUST_AUTH_COOKIE_DURATION
|
- name: PROBOD_TRUST_AUTH_COOKIE_DURATION
|
||||||
value: {{ .Values.probo.trustAuth.cookieDuration | quote }}
|
value: {{ .Values.probo.trustAuth.cookieDuration | quote }}
|
||||||
- name: TRUST_AUTH_TOKEN_DURATION
|
- name: PROBOD_TRUST_AUTH_TOKEN_DURATION
|
||||||
value: {{ .Values.probo.trustAuth.tokenDuration | quote }}
|
value: {{ .Values.probo.trustAuth.tokenDuration | quote }}
|
||||||
- name: TRUST_AUTH_REPORT_URL_DURATION
|
- name: PROBOD_TRUST_AUTH_REPORT_URL_DURATION
|
||||||
value: {{ .Values.probo.trustAuth.reportUrlDuration | quote }}
|
value: {{ .Values.probo.trustAuth.reportUrlDuration | quote }}
|
||||||
- name: TRUST_AUTH_TOKEN_SECRET
|
- name: PROBOD_TRUST_AUTH_TOKEN_SECRET
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "probo.fullname" . }}
|
name: {{ include "probo.fullname" . }}
|
||||||
key: trust-token-secret
|
key: trust-token-secret
|
||||||
- name: TRUST_AUTH_SCOPE
|
- name: PROBOD_TRUST_AUTH_SCOPE
|
||||||
value: {{ .Values.probo.trustAuth.scope | quote }}
|
value: {{ .Values.probo.trustAuth.scope | quote }}
|
||||||
- name: TRUST_AUTH_TOKEN_TYPE
|
- name: PROBOD_TRUST_AUTH_TOKEN_TYPE
|
||||||
value: {{ .Values.probo.trustAuth.tokenType | quote }}
|
value: {{ .Values.probo.trustAuth.tokenType | quote }}
|
||||||
# AWS / S3 Storage
|
# AWS / S3 Storage
|
||||||
- name: AWS_REGION
|
- name: PROBOD_AWS_REGION
|
||||||
value: {{ .Values.s3.region | quote }}
|
value: {{ .Values.s3.region | quote }}
|
||||||
- name: AWS_BUCKET
|
- name: PROBOD_AWS_BUCKET
|
||||||
value: {{ .Values.s3.bucket | quote }}
|
value: {{ .Values.s3.bucket | quote }}
|
||||||
- name: AWS_ACCESS_KEY_ID
|
- name: PROBOD_AWS_ACCESS_KEY_ID
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "probo.fullname" . }}
|
name: {{ include "probo.fullname" . }}
|
||||||
key: s3-access-key
|
key: s3-access-key
|
||||||
- name: AWS_SECRET_ACCESS_KEY
|
- name: PROBOD_AWS_SECRET_ACCESS_KEY
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "probo.fullname" . }}
|
name: {{ include "probo.fullname" . }}
|
||||||
key: s3-secret-key
|
key: s3-secret-key
|
||||||
{{- $s3Endpoint := include "probo.s3.endpoint" . }}
|
{{- $s3Endpoint := include "probo.s3.endpoint" . }}
|
||||||
{{- if $s3Endpoint }}
|
{{- if $s3Endpoint }}
|
||||||
- name: AWS_ENDPOINT
|
- name: PROBOD_AWS_ENDPOINT
|
||||||
value: {{ $s3Endpoint | quote }}
|
value: {{ $s3Endpoint | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.s3.usePathStyle }}
|
{{- if .Values.s3.usePathStyle }}
|
||||||
- name: AWS_USE_PATH_STYLE
|
- name: PROBOD_AWS_USE_PATH_STYLE
|
||||||
value: "true"
|
value: "true"
|
||||||
{{- end }}
|
{{- end }}
|
||||||
# Email (SMTP)
|
# Email (SMTP)
|
||||||
- name: MAILER_SENDER_NAME
|
- name: PROBOD_MAILER_SENDER_NAME
|
||||||
value: {{ .Values.probo.mailer.senderName | quote }}
|
value: {{ .Values.probo.mailer.senderName | quote }}
|
||||||
- name: MAILER_SENDER_EMAIL
|
- name: PROBOD_MAILER_SENDER_EMAIL
|
||||||
value: {{ .Values.probo.mailer.senderEmail | quote }}
|
value: {{ .Values.probo.mailer.senderEmail | quote }}
|
||||||
- name: SMTP_ADDR
|
- name: PROBOD_SMTP_ADDR
|
||||||
value: {{ .Values.probo.mailer.smtp.addr | quote }}
|
value: {{ .Values.probo.mailer.smtp.addr | quote }}
|
||||||
{{- if .Values.probo.mailer.smtp.user }}
|
{{- if .Values.probo.mailer.smtp.user }}
|
||||||
- name: SMTP_USER
|
- name: PROBOD_SMTP_USER
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "probo.fullname" . }}
|
name: {{ include "probo.fullname" . }}
|
||||||
key: smtp-user
|
key: smtp-user
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.mailer.smtp.password }}
|
{{- if .Values.probo.mailer.smtp.password }}
|
||||||
- name: SMTP_PASSWORD
|
- name: PROBOD_SMTP_PASSWORD
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "probo.fullname" . }}
|
name: {{ include "probo.fullname" . }}
|
||||||
key: smtp-password
|
key: smtp-password
|
||||||
{{- end }}
|
{{- end }}
|
||||||
- name: SMTP_TLS_REQUIRED
|
- name: PROBOD_SMTP_TLS_REQUIRED
|
||||||
value: {{ .Values.probo.mailer.smtp.tlsRequired | quote }}
|
value: {{ .Values.probo.mailer.smtp.tlsRequired | quote }}
|
||||||
{{- if .Values.probo.mailer.smtp.helloName }}
|
{{- if .Values.probo.mailer.smtp.helloName }}
|
||||||
- name: SMTP_HELLO_NAME
|
- name: PROBOD_SMTP_HELLO_NAME
|
||||||
value: {{ .Values.probo.mailer.smtp.helloName | quote }}
|
value: {{ .Values.probo.mailer.smtp.helloName | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
# OpenAI Integration
|
# OpenAI Integration
|
||||||
{{- if .Values.probo.openai.apiKey }}
|
{{- if .Values.probo.openai.apiKey }}
|
||||||
- name: OPENAI_API_KEY
|
- name: PROBOD_OPENAI_API_KEY
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "probo.fullname" . }}
|
name: {{ include "probo.fullname" . }}
|
||||||
key: openai-api-key
|
key: openai-api-key
|
||||||
- name: OPENAI_TEMPERATURE
|
- name: PROBOD_OPENAI_TEMPERATURE
|
||||||
value: {{ .Values.probo.openai.temperature | quote }}
|
value: {{ .Values.probo.openai.temperature | quote }}
|
||||||
- name: OPENAI_MODEL_NAME
|
- name: PROBOD_OPENAI_MODEL_NAME
|
||||||
value: {{ .Values.probo.openai.modelName | quote }}
|
value: {{ .Values.probo.openai.modelName | quote }}
|
||||||
- name: OPENAI_MAX_TOKENS
|
- name: PROBOD_OPENAI_MAX_TOKENS
|
||||||
value: {{ .Values.probo.openai.maxTokens | quote }}
|
value: {{ .Values.probo.openai.maxTokens | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
# Agent Tools
|
# Agent Tools
|
||||||
{{- if .Values.probo.agentTools.firecrawlApiKey }}
|
{{- if .Values.probo.agentTools.firecrawlApiKey }}
|
||||||
- name: FIRECRAWL_API_KEY
|
- name: PROBOD_FIRECRAWL_API_KEY
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "probo.fullname" . }}
|
name: {{ include "probo.fullname" . }}
|
||||||
@@ -257,193 +257,193 @@ spec:
|
|||||||
{{- end }}
|
{{- end }}
|
||||||
# Third-Party Vetter Agent
|
# Third-Party Vetter Agent
|
||||||
{{- if .Values.probo.thirdPartyVetter.provider }}
|
{{- if .Values.probo.thirdPartyVetter.provider }}
|
||||||
- name: AGENT_THIRD_PARTY_VETTER_PROVIDER
|
- name: PROBOD_AGENT_THIRD_PARTY_VETTER_PROVIDER
|
||||||
value: {{ .Values.probo.thirdPartyVetter.provider | quote }}
|
value: {{ .Values.probo.thirdPartyVetter.provider | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.thirdPartyVetter.modelName }}
|
{{- if .Values.probo.thirdPartyVetter.modelName }}
|
||||||
- name: AGENT_THIRD_PARTY_VETTER_MODEL_NAME
|
- name: PROBOD_AGENT_THIRD_PARTY_VETTER_MODEL_NAME
|
||||||
value: {{ .Values.probo.thirdPartyVetter.modelName | quote }}
|
value: {{ .Values.probo.thirdPartyVetter.modelName | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.thirdPartyVetter.temperature }}
|
{{- if .Values.probo.thirdPartyVetter.temperature }}
|
||||||
- name: AGENT_THIRD_PARTY_VETTER_TEMPERATURE
|
- name: PROBOD_AGENT_THIRD_PARTY_VETTER_TEMPERATURE
|
||||||
value: {{ .Values.probo.thirdPartyVetter.temperature | quote }}
|
value: {{ .Values.probo.thirdPartyVetter.temperature | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.thirdPartyVetter.maxTokens }}
|
{{- if .Values.probo.thirdPartyVetter.maxTokens }}
|
||||||
- name: AGENT_THIRD_PARTY_VETTER_MAX_TOKENS
|
- name: PROBOD_AGENT_THIRD_PARTY_VETTER_MAX_TOKENS
|
||||||
value: {{ .Values.probo.thirdPartyVetter.maxTokens | quote }}
|
value: {{ .Values.probo.thirdPartyVetter.maxTokens | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
# Third-Party Vetting Worker
|
# Third-Party Vetting Worker
|
||||||
{{- if .Values.probo.thirdPartyVettingWorker.interval }}
|
{{- if .Values.probo.thirdPartyVettingWorker.interval }}
|
||||||
- name: THIRD_PARTY_VETTING_INTERVAL
|
- name: PROBOD_THIRD_PARTY_VETTING_INTERVAL
|
||||||
value: {{ .Values.probo.thirdPartyVettingWorker.interval | quote }}
|
value: {{ .Values.probo.thirdPartyVettingWorker.interval | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.thirdPartyVettingWorker.maxConcurrency }}
|
{{- if .Values.probo.thirdPartyVettingWorker.maxConcurrency }}
|
||||||
- name: THIRD_PARTY_VETTING_MAX_CONCURRENCY
|
- name: PROBOD_THIRD_PARTY_VETTING_MAX_CONCURRENCY
|
||||||
value: {{ .Values.probo.thirdPartyVettingWorker.maxConcurrency | quote }}
|
value: {{ .Values.probo.thirdPartyVettingWorker.maxConcurrency | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.thirdPartyVettingWorker.staleAfter }}
|
{{- if .Values.probo.thirdPartyVettingWorker.staleAfter }}
|
||||||
- name: THIRD_PARTY_VETTING_STALE_AFTER
|
- name: PROBOD_THIRD_PARTY_VETTING_STALE_AFTER
|
||||||
value: {{ .Values.probo.thirdPartyVettingWorker.staleAfter | quote }}
|
value: {{ .Values.probo.thirdPartyVettingWorker.staleAfter | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
# Third-party Disambiguation Agent
|
# Third-party Disambiguation Agent
|
||||||
{{- if .Values.probo.thirdPartyDisambiguation.provider }}
|
{{- if .Values.probo.thirdPartyDisambiguation.provider }}
|
||||||
- name: AGENT_THIRD_PARTY_DISAMBIGUATION_PROVIDER
|
- name: PROBOD_AGENT_THIRD_PARTY_DISAMBIGUATION_PROVIDER
|
||||||
value: {{ .Values.probo.thirdPartyDisambiguation.provider | quote }}
|
value: {{ .Values.probo.thirdPartyDisambiguation.provider | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.thirdPartyDisambiguation.modelName }}
|
{{- if .Values.probo.thirdPartyDisambiguation.modelName }}
|
||||||
- name: AGENT_THIRD_PARTY_DISAMBIGUATION_MODEL_NAME
|
- name: PROBOD_AGENT_THIRD_PARTY_DISAMBIGUATION_MODEL_NAME
|
||||||
value: {{ .Values.probo.thirdPartyDisambiguation.modelName | quote }}
|
value: {{ .Values.probo.thirdPartyDisambiguation.modelName | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.thirdPartyDisambiguation.temperature }}
|
{{- if .Values.probo.thirdPartyDisambiguation.temperature }}
|
||||||
- name: AGENT_THIRD_PARTY_DISAMBIGUATION_TEMPERATURE
|
- name: PROBOD_AGENT_THIRD_PARTY_DISAMBIGUATION_TEMPERATURE
|
||||||
value: {{ .Values.probo.thirdPartyDisambiguation.temperature | quote }}
|
value: {{ .Values.probo.thirdPartyDisambiguation.temperature | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.thirdPartyDisambiguation.maxTokens }}
|
{{- if .Values.probo.thirdPartyDisambiguation.maxTokens }}
|
||||||
- name: AGENT_THIRD_PARTY_DISAMBIGUATION_MAX_TOKENS
|
- name: PROBOD_AGENT_THIRD_PARTY_DISAMBIGUATION_MAX_TOKENS
|
||||||
value: {{ .Values.probo.thirdPartyDisambiguation.maxTokens | quote }}
|
value: {{ .Values.probo.thirdPartyDisambiguation.maxTokens | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
# Tracker Mapping Agent
|
# Tracker Mapping Agent
|
||||||
{{- if .Values.probo.trackerMapping.provider }}
|
{{- if .Values.probo.trackerMapping.provider }}
|
||||||
- name: AGENT_TRACKER_MAPPING_PROVIDER
|
- name: PROBOD_AGENT_TRACKER_MAPPING_PROVIDER
|
||||||
value: {{ .Values.probo.trackerMapping.provider | quote }}
|
value: {{ .Values.probo.trackerMapping.provider | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.trackerMapping.modelName }}
|
{{- if .Values.probo.trackerMapping.modelName }}
|
||||||
- name: AGENT_TRACKER_MAPPING_MODEL_NAME
|
- name: PROBOD_AGENT_TRACKER_MAPPING_MODEL_NAME
|
||||||
value: {{ .Values.probo.trackerMapping.modelName | quote }}
|
value: {{ .Values.probo.trackerMapping.modelName | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.trackerMapping.temperature }}
|
{{- if .Values.probo.trackerMapping.temperature }}
|
||||||
- name: AGENT_TRACKER_MAPPING_TEMPERATURE
|
- name: PROBOD_AGENT_TRACKER_MAPPING_TEMPERATURE
|
||||||
value: {{ .Values.probo.trackerMapping.temperature | quote }}
|
value: {{ .Values.probo.trackerMapping.temperature | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.trackerMapping.maxTokens }}
|
{{- if .Values.probo.trackerMapping.maxTokens }}
|
||||||
- name: AGENT_TRACKER_MAPPING_MAX_TOKENS
|
- name: PROBOD_AGENT_TRACKER_MAPPING_MAX_TOKENS
|
||||||
value: {{ .Values.probo.trackerMapping.maxTokens | quote }}
|
value: {{ .Values.probo.trackerMapping.maxTokens | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
# Tracker Enrichment Agent
|
# Tracker Enrichment Agent
|
||||||
{{- if .Values.probo.trackerEnrichment.provider }}
|
{{- if .Values.probo.trackerEnrichment.provider }}
|
||||||
- name: AGENT_TRACKER_ENRICHMENT_PROVIDER
|
- name: PROBOD_AGENT_TRACKER_ENRICHMENT_PROVIDER
|
||||||
value: {{ .Values.probo.trackerEnrichment.provider | quote }}
|
value: {{ .Values.probo.trackerEnrichment.provider | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.trackerEnrichment.modelName }}
|
{{- if .Values.probo.trackerEnrichment.modelName }}
|
||||||
- name: AGENT_TRACKER_ENRICHMENT_MODEL_NAME
|
- name: PROBOD_AGENT_TRACKER_ENRICHMENT_MODEL_NAME
|
||||||
value: {{ .Values.probo.trackerEnrichment.modelName | quote }}
|
value: {{ .Values.probo.trackerEnrichment.modelName | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.trackerEnrichment.temperature }}
|
{{- if .Values.probo.trackerEnrichment.temperature }}
|
||||||
- name: AGENT_TRACKER_ENRICHMENT_TEMPERATURE
|
- name: PROBOD_AGENT_TRACKER_ENRICHMENT_TEMPERATURE
|
||||||
value: {{ .Values.probo.trackerEnrichment.temperature | quote }}
|
value: {{ .Values.probo.trackerEnrichment.temperature | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.trackerEnrichment.maxTokens }}
|
{{- if .Values.probo.trackerEnrichment.maxTokens }}
|
||||||
- name: AGENT_TRACKER_ENRICHMENT_MAX_TOKENS
|
- name: PROBOD_AGENT_TRACKER_ENRICHMENT_MAX_TOKENS
|
||||||
value: {{ .Values.probo.trackerEnrichment.maxTokens | quote }}
|
value: {{ .Values.probo.trackerEnrichment.maxTokens | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
# Tracker Mapping Worker
|
# Tracker Mapping Worker
|
||||||
{{- if .Values.probo.trackerMappingWorker.interval }}
|
{{- if .Values.probo.trackerMappingWorker.interval }}
|
||||||
- name: TRACKER_MAPPING_INTERVAL
|
- name: PROBOD_TRACKER_MAPPING_INTERVAL
|
||||||
value: {{ .Values.probo.trackerMappingWorker.interval | quote }}
|
value: {{ .Values.probo.trackerMappingWorker.interval | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.trackerMappingWorker.maxConcurrency }}
|
{{- if .Values.probo.trackerMappingWorker.maxConcurrency }}
|
||||||
- name: TRACKER_MAPPING_MAX_CONCURRENCY
|
- name: PROBOD_TRACKER_MAPPING_MAX_CONCURRENCY
|
||||||
value: {{ .Values.probo.trackerMappingWorker.maxConcurrency | quote }}
|
value: {{ .Values.probo.trackerMappingWorker.maxConcurrency | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.trackerMappingWorker.staleAfter }}
|
{{- if .Values.probo.trackerMappingWorker.staleAfter }}
|
||||||
- name: TRACKER_MAPPING_STALE_AFTER
|
- name: PROBOD_TRACKER_MAPPING_STALE_AFTER
|
||||||
value: {{ .Values.probo.trackerMappingWorker.staleAfter | quote }}
|
value: {{ .Values.probo.trackerMappingWorker.staleAfter | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.trackerMappingWorker.agentTimeout }}
|
{{- if .Values.probo.trackerMappingWorker.agentTimeout }}
|
||||||
- name: TRACKER_MAPPING_AGENT_TIMEOUT
|
- name: PROBOD_TRACKER_MAPPING_AGENT_TIMEOUT
|
||||||
value: {{ .Values.probo.trackerMappingWorker.agentTimeout | quote }}
|
value: {{ .Values.probo.trackerMappingWorker.agentTimeout | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.trackerMappingWorker.agentMaxTurns }}
|
{{- if .Values.probo.trackerMappingWorker.agentMaxTurns }}
|
||||||
- name: TRACKER_MAPPING_AGENT_MAX_TURNS
|
- name: PROBOD_TRACKER_MAPPING_AGENT_MAX_TURNS
|
||||||
value: {{ .Values.probo.trackerMappingWorker.agentMaxTurns | quote }}
|
value: {{ .Values.probo.trackerMappingWorker.agentMaxTurns | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.trackerMappingWorker.disambiguationAgentTimeout }}
|
{{- if .Values.probo.trackerMappingWorker.disambiguationAgentTimeout }}
|
||||||
- name: TRACKER_MAPPING_DISAMBIGUATION_AGENT_TIMEOUT
|
- name: PROBOD_TRACKER_MAPPING_DISAMBIGUATION_AGENT_TIMEOUT
|
||||||
value: {{ .Values.probo.trackerMappingWorker.disambiguationAgentTimeout | quote }}
|
value: {{ .Values.probo.trackerMappingWorker.disambiguationAgentTimeout | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
# Common Pattern Enrichment Worker
|
# Common Pattern Enrichment Worker
|
||||||
{{- if .Values.probo.commonPatternEnrichmentWorker.interval }}
|
{{- if .Values.probo.commonPatternEnrichmentWorker.interval }}
|
||||||
- name: COMMON_PATTERN_ENRICHMENT_INTERVAL
|
- name: PROBOD_COMMON_PATTERN_ENRICHMENT_INTERVAL
|
||||||
value: {{ .Values.probo.commonPatternEnrichmentWorker.interval | quote }}
|
value: {{ .Values.probo.commonPatternEnrichmentWorker.interval | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.commonPatternEnrichmentWorker.maxConcurrency }}
|
{{- if .Values.probo.commonPatternEnrichmentWorker.maxConcurrency }}
|
||||||
- name: COMMON_PATTERN_ENRICHMENT_MAX_CONCURRENCY
|
- name: PROBOD_COMMON_PATTERN_ENRICHMENT_MAX_CONCURRENCY
|
||||||
value: {{ .Values.probo.commonPatternEnrichmentWorker.maxConcurrency | quote }}
|
value: {{ .Values.probo.commonPatternEnrichmentWorker.maxConcurrency | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.commonPatternEnrichmentWorker.staleAfter }}
|
{{- if .Values.probo.commonPatternEnrichmentWorker.staleAfter }}
|
||||||
- name: COMMON_PATTERN_ENRICHMENT_STALE_AFTER
|
- name: PROBOD_COMMON_PATTERN_ENRICHMENT_STALE_AFTER
|
||||||
value: {{ .Values.probo.commonPatternEnrichmentWorker.staleAfter | quote }}
|
value: {{ .Values.probo.commonPatternEnrichmentWorker.staleAfter | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.commonPatternEnrichmentWorker.agentTimeout }}
|
{{- if .Values.probo.commonPatternEnrichmentWorker.agentTimeout }}
|
||||||
- name: COMMON_PATTERN_ENRICHMENT_AGENT_TIMEOUT
|
- name: PROBOD_COMMON_PATTERN_ENRICHMENT_AGENT_TIMEOUT
|
||||||
value: {{ .Values.probo.commonPatternEnrichmentWorker.agentTimeout | quote }}
|
value: {{ .Values.probo.commonPatternEnrichmentWorker.agentTimeout | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.commonPatternEnrichmentWorker.agentMaxTurns }}
|
{{- if .Values.probo.commonPatternEnrichmentWorker.agentMaxTurns }}
|
||||||
- name: COMMON_PATTERN_ENRICHMENT_AGENT_MAX_TURNS
|
- name: PROBOD_COMMON_PATTERN_ENRICHMENT_AGENT_MAX_TURNS
|
||||||
value: {{ .Values.probo.commonPatternEnrichmentWorker.agentMaxTurns | quote }}
|
value: {{ .Values.probo.commonPatternEnrichmentWorker.agentMaxTurns | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
# Common Third Party Enrichment Agent
|
# Common Third Party Enrichment Agent
|
||||||
{{- if .Values.probo.commonThirdPartyEnrichment.provider }}
|
{{- if .Values.probo.commonThirdPartyEnrichment.provider }}
|
||||||
- name: AGENT_COMMON_THIRD_PARTY_ENRICHMENT_PROVIDER
|
- name: PROBOD_AGENT_COMMON_THIRD_PARTY_ENRICHMENT_PROVIDER
|
||||||
value: {{ .Values.probo.commonThirdPartyEnrichment.provider | quote }}
|
value: {{ .Values.probo.commonThirdPartyEnrichment.provider | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.commonThirdPartyEnrichment.modelName }}
|
{{- if .Values.probo.commonThirdPartyEnrichment.modelName }}
|
||||||
- name: AGENT_COMMON_THIRD_PARTY_ENRICHMENT_MODEL_NAME
|
- name: PROBOD_AGENT_COMMON_THIRD_PARTY_ENRICHMENT_MODEL_NAME
|
||||||
value: {{ .Values.probo.commonThirdPartyEnrichment.modelName | quote }}
|
value: {{ .Values.probo.commonThirdPartyEnrichment.modelName | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.commonThirdPartyEnrichment.temperature }}
|
{{- if .Values.probo.commonThirdPartyEnrichment.temperature }}
|
||||||
- name: AGENT_COMMON_THIRD_PARTY_ENRICHMENT_TEMPERATURE
|
- name: PROBOD_AGENT_COMMON_THIRD_PARTY_ENRICHMENT_TEMPERATURE
|
||||||
value: {{ .Values.probo.commonThirdPartyEnrichment.temperature | quote }}
|
value: {{ .Values.probo.commonThirdPartyEnrichment.temperature | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.commonThirdPartyEnrichment.maxTokens }}
|
{{- if .Values.probo.commonThirdPartyEnrichment.maxTokens }}
|
||||||
- name: AGENT_COMMON_THIRD_PARTY_ENRICHMENT_MAX_TOKENS
|
- name: PROBOD_AGENT_COMMON_THIRD_PARTY_ENRICHMENT_MAX_TOKENS
|
||||||
value: {{ .Values.probo.commonThirdPartyEnrichment.maxTokens | quote }}
|
value: {{ .Values.probo.commonThirdPartyEnrichment.maxTokens | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
# Common Third Party Enrichment Worker
|
# Common Third Party Enrichment Worker
|
||||||
{{- if .Values.probo.commonThirdPartyEnrichmentWorker.interval }}
|
{{- if .Values.probo.commonThirdPartyEnrichmentWorker.interval }}
|
||||||
- name: COMMON_THIRD_PARTY_ENRICHMENT_INTERVAL
|
- name: PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_INTERVAL
|
||||||
value: {{ .Values.probo.commonThirdPartyEnrichmentWorker.interval | quote }}
|
value: {{ .Values.probo.commonThirdPartyEnrichmentWorker.interval | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.commonThirdPartyEnrichmentWorker.maxConcurrency }}
|
{{- if .Values.probo.commonThirdPartyEnrichmentWorker.maxConcurrency }}
|
||||||
- name: COMMON_THIRD_PARTY_ENRICHMENT_MAX_CONCURRENCY
|
- name: PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_MAX_CONCURRENCY
|
||||||
value: {{ .Values.probo.commonThirdPartyEnrichmentWorker.maxConcurrency | quote }}
|
value: {{ .Values.probo.commonThirdPartyEnrichmentWorker.maxConcurrency | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.commonThirdPartyEnrichmentWorker.staleAfter }}
|
{{- if .Values.probo.commonThirdPartyEnrichmentWorker.staleAfter }}
|
||||||
- name: COMMON_THIRD_PARTY_ENRICHMENT_STALE_AFTER
|
- name: PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_STALE_AFTER
|
||||||
value: {{ .Values.probo.commonThirdPartyEnrichmentWorker.staleAfter | quote }}
|
value: {{ .Values.probo.commonThirdPartyEnrichmentWorker.staleAfter | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.commonThirdPartyEnrichmentWorker.agentTimeout }}
|
{{- if .Values.probo.commonThirdPartyEnrichmentWorker.agentTimeout }}
|
||||||
- name: COMMON_THIRD_PARTY_ENRICHMENT_AGENT_TIMEOUT
|
- name: PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_AGENT_TIMEOUT
|
||||||
value: {{ .Values.probo.commonThirdPartyEnrichmentWorker.agentTimeout | quote }}
|
value: {{ .Values.probo.commonThirdPartyEnrichmentWorker.agentTimeout | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.commonThirdPartyEnrichmentWorker.agentMaxTurns }}
|
{{- if .Values.probo.commonThirdPartyEnrichmentWorker.agentMaxTurns }}
|
||||||
- name: COMMON_THIRD_PARTY_ENRICHMENT_AGENT_MAX_TURNS
|
- name: PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_AGENT_MAX_TURNS
|
||||||
value: {{ .Values.probo.commonThirdPartyEnrichmentWorker.agentMaxTurns | quote }}
|
value: {{ .Values.probo.commonThirdPartyEnrichmentWorker.agentMaxTurns | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if ne .Values.probo.commonThirdPartyEnrichmentWorker.confidenceThreshold nil }}
|
{{- if ne .Values.probo.commonThirdPartyEnrichmentWorker.confidenceThreshold nil }}
|
||||||
- name: COMMON_THIRD_PARTY_ENRICHMENT_CONFIDENCE_THRESHOLD
|
- name: PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_CONFIDENCE_THRESHOLD
|
||||||
value: {{ .Values.probo.commonThirdPartyEnrichmentWorker.confidenceThreshold | quote }}
|
value: {{ .Values.probo.commonThirdPartyEnrichmentWorker.confidenceThreshold | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.probo.commonThirdPartyEnrichmentWorker.maxAttempts }}
|
{{- if .Values.probo.commonThirdPartyEnrichmentWorker.maxAttempts }}
|
||||||
- name: COMMON_THIRD_PARTY_ENRICHMENT_MAX_ATTEMPTS
|
- name: PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_MAX_ATTEMPTS
|
||||||
value: {{ .Values.probo.commonThirdPartyEnrichmentWorker.maxAttempts | quote }}
|
value: {{ .Values.probo.commonThirdPartyEnrichmentWorker.maxAttempts | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
# Custom Domains
|
# Custom Domains
|
||||||
{{- if .Values.probo.customDomains.enabled }}
|
{{- if .Values.probo.customDomains.enabled }}
|
||||||
- name: CUSTOM_DOMAINS_RENEWAL_INTERVAL
|
- name: PROBOD_CUSTOM_DOMAINS_RENEWAL_INTERVAL
|
||||||
value: {{ .Values.probo.customDomains.renewalInterval | quote }}
|
value: {{ .Values.probo.customDomains.renewalInterval | quote }}
|
||||||
- name: CUSTOM_DOMAINS_PROVISION_INTERVAL
|
- name: PROBOD_CUSTOM_DOMAINS_PROVISION_INTERVAL
|
||||||
value: {{ .Values.probo.customDomains.provisionInterval | quote }}
|
value: {{ .Values.probo.customDomains.provisionInterval | quote }}
|
||||||
- name: CUSTOM_DOMAINS_CNAME_TARGET
|
- name: PROBOD_CUSTOM_DOMAINS_CNAME_TARGET
|
||||||
value: {{ .Values.probo.customDomains.cnameTarget | quote }}
|
value: {{ .Values.probo.customDomains.cnameTarget | quote }}
|
||||||
- name: ACME_DIRECTORY
|
- name: PROBOD_ACME_DIRECTORY
|
||||||
value: {{ .Values.probo.customDomains.acme.directory | quote }}
|
value: {{ .Values.probo.customDomains.acme.directory | quote }}
|
||||||
- name: ACME_EMAIL
|
- name: PROBOD_ACME_EMAIL
|
||||||
value: {{ .Values.probo.customDomains.acme.email | quote }}
|
value: {{ .Values.probo.customDomains.acme.email | quote }}
|
||||||
- name: ACME_KEY_TYPE
|
- name: PROBOD_ACME_KEY_TYPE
|
||||||
value: {{ .Values.probo.customDomains.acme.keyType | quote }}
|
value: {{ .Values.probo.customDomains.acme.keyType | quote }}
|
||||||
{{- if .Values.probo.customDomains.acme.accountKey }}
|
{{- if .Values.probo.customDomains.acme.accountKey }}
|
||||||
- name: ACME_ACCOUNT_KEY
|
- name: PROBOD_ACME_ACCOUNT_KEY
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "probo.fullname" . }}
|
name: {{ include "probo.fullname" . }}
|
||||||
@@ -453,30 +453,30 @@ spec:
|
|||||||
# Connectors (Slack OAuth2)
|
# Connectors (Slack OAuth2)
|
||||||
{{- range .Values.probo.connectors }}
|
{{- range .Values.probo.connectors }}
|
||||||
{{- if eq .name "slack" }}
|
{{- if eq .name "slack" }}
|
||||||
- name: CONNECTOR_SLACK_CLIENT_ID
|
- name: PROBOD_CONNECTOR_SLACK_CLIENT_ID
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "probo.fullname" $ }}
|
name: {{ include "probo.fullname" $ }}
|
||||||
key: connector-slack-client-id
|
key: connector-slack-client-id
|
||||||
- name: CONNECTOR_SLACK_CLIENT_SECRET
|
- name: PROBOD_CONNECTOR_SLACK_CLIENT_SECRET
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "probo.fullname" $ }}
|
name: {{ include "probo.fullname" $ }}
|
||||||
key: connector-slack-client-secret
|
key: connector-slack-client-secret
|
||||||
{{- if .config.redirectUri }}
|
{{- if .config.redirectUri }}
|
||||||
- name: CONNECTOR_SLACK_REDIRECT_URI
|
- name: PROBOD_CONNECTOR_SLACK_REDIRECT_URI
|
||||||
value: {{ .config.redirectUri | quote }}
|
value: {{ .config.redirectUri | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .config.authUrl }}
|
{{- if .config.authUrl }}
|
||||||
- name: CONNECTOR_SLACK_AUTH_URL
|
- name: PROBOD_CONNECTOR_SLACK_AUTH_URL
|
||||||
value: {{ .config.authUrl | quote }}
|
value: {{ .config.authUrl | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .config.tokenUrl }}
|
{{- if .config.tokenUrl }}
|
||||||
- name: CONNECTOR_SLACK_TOKEN_URL
|
- name: PROBOD_CONNECTOR_SLACK_TOKEN_URL
|
||||||
value: {{ .config.tokenUrl | quote }}
|
value: {{ .config.tokenUrl | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .config.signingSecret }}
|
{{- if .config.signingSecret }}
|
||||||
- name: CONNECTOR_SLACK_SIGNING_SECRET
|
- name: PROBOD_CONNECTOR_SLACK_SIGNING_SECRET
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "probo.fullname" $ }}
|
name: {{ include "probo.fullname" $ }}
|
||||||
|
|||||||
@@ -114,21 +114,21 @@ if [ -f /workspace/.sandbox.env ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
PROBOD_BASE_URL="http://${VM_IP}:8080" \
|
PROBOD_BASE_URL="http://${VM_IP}:8080" \
|
||||||
AUTH_COOKIE_DOMAIN="${VM_IP}" \
|
PROBOD_AUTH_COOKIE_DOMAIN="${VM_IP}" \
|
||||||
AUTH_COOKIE_SECURE=false \
|
PROBOD_AUTH_COOKIE_SECURE=false \
|
||||||
AUTH_COOKIE_SECRET="this-is-a-secure-secret-for-cookie-signing-at-least-32-bytes" \
|
PROBOD_AUTH_COOKIE_SECRET="this-is-a-secure-secret-for-cookie-signing-at-least-32-bytes" \
|
||||||
AUTH_PASSWORD_PEPPER="this-is-a-secure-pepper-for-password-hashing-at-least-32-bytes" \
|
PROBOD_AUTH_PASSWORD_PEPPER="this-is-a-secure-pepper-for-password-hashing-at-least-32-bytes" \
|
||||||
PROBOD_ENCRYPTION_KEY="thisisnotasecretAAAAAAAAAAAAAAAAAAAAAAAAAAA=" \
|
PROBOD_ENCRYPTION_KEY="thisisnotasecretAAAAAAAAAAAAAAAAAAAAAAAAAAA=" \
|
||||||
OAUTH2_SERVER_SIGNING_KEY="$(cat "${OAUTH2_SIGNING_KEY_PATH}")" \
|
PROBOD_OAUTH2_SERVER_SIGNING_KEY="$(cat "${OAUTH2_SIGNING_KEY_PATH}")" \
|
||||||
API_CORS_ALLOWED_ORIGINS="http://${VM_IP}:8080,http://${VM_IP}:5173,http://${VM_IP}:5174" \
|
PROBOD_API_CORS_ALLOWED_ORIGINS="http://${VM_IP}:8080,http://${VM_IP}:5173,http://${VM_IP}:5174" \
|
||||||
AWS_ENDPOINT="http://127.0.0.1:8333" \
|
PROBOD_AWS_ENDPOINT="http://127.0.0.1:8333" \
|
||||||
AWS_ACCESS_KEY_ID="probod" \
|
PROBOD_AWS_ACCESS_KEY_ID="probod" \
|
||||||
AWS_SECRET_ACCESS_KEY="thisisnotasecret" \
|
PROBOD_AWS_SECRET_ACCESS_KEY="thisisnotasecret" \
|
||||||
AWS_USE_PATH_STYLE=true \
|
PROBOD_AWS_USE_PATH_STYLE=true \
|
||||||
ACME_DIRECTORY="https://127.0.0.1:14000/dir" \
|
PROBOD_ACME_DIRECTORY="https://127.0.0.1:14000/dir" \
|
||||||
ACME_EMAIL="admin@probo.com" \
|
PROBOD_ACME_EMAIL="admin@probo.com" \
|
||||||
ACME_KEY_TYPE="EC256" \
|
PROBOD_ACME_KEY_TYPE="EC256" \
|
||||||
ACME_ROOT_CA="$(cat /workspace/compose/pebble/certs/rootCA.pem)" \
|
PROBOD_ACME_ROOT_CA="$(cat /workspace/compose/pebble/certs/rootCA.pem)" \
|
||||||
/workspace/bin/probod-bootstrap -output /etc/probod/config.yml
|
/workspace/bin/probod-bootstrap -output /etc/probod/config.yml
|
||||||
|
|
||||||
# probod runs as ${LIMA_USER} but bootstrap writes config.yml as root with 0600
|
# probod runs as ${LIMA_USER} but bootstrap writes config.yml as root with 0600
|
||||||
|
|||||||
@@ -256,68 +256,68 @@ func generateConfig() (string, error) {
|
|||||||
|
|
||||||
env := map[string]string{
|
env := map[string]string{
|
||||||
// Required.
|
// Required.
|
||||||
"PROBOD_ENCRYPTION_KEY": "thisisnotasecretAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
|
"PROBOD_ENCRYPTION_KEY": "thisisnotasecretAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
|
||||||
"AUTH_COOKIE_SECRET": "this-is-a-secure-secret-for-cookie-signing-at-least-32-bytes",
|
"PROBOD_AUTH_COOKIE_SECRET": "this-is-a-secure-secret-for-cookie-signing-at-least-32-bytes",
|
||||||
"AUTH_PASSWORD_PEPPER": "this-is-a-secure-pepper-for-password-hashing-at-least-32-bytes",
|
"PROBOD_AUTH_PASSWORD_PEPPER": "this-is-a-secure-pepper-for-password-hashing-at-least-32-bytes",
|
||||||
"OAUTH2_SERVER_SIGNING_KEY": oauth2SigningKey,
|
"PROBOD_OAUTH2_SERVER_SIGNING_KEY": oauth2SigningKey,
|
||||||
|
|
||||||
// Unit.
|
// Unit.
|
||||||
"METRICS_ADDR": "localhost:19081",
|
"PROBOD_METRICS_ADDR": "localhost:19081",
|
||||||
"TRACING_ADDR": "localhost:14317",
|
"PROBOD_TRACING_ADDR": "localhost:14317",
|
||||||
|
|
||||||
// Probod base.
|
// Probod base.
|
||||||
"PROBOD_BASE_URL": "http://localhost:18080",
|
"PROBOD_BASE_URL": "http://localhost:18080",
|
||||||
|
|
||||||
// API.
|
// API.
|
||||||
"API_ADDR": "localhost:18080",
|
"PROBOD_API_ADDR": "localhost:18080",
|
||||||
"API_CORS_ALLOWED_ORIGINS": "http://localhost:18080",
|
"PROBOD_API_CORS_ALLOWED_ORIGINS": "http://localhost:18080",
|
||||||
|
|
||||||
// PG.
|
// PG.
|
||||||
"PG_DATABASE": "probod_test",
|
"PROBOD_PG_DATABASE": "probod_test",
|
||||||
"PG_POOL_SIZE": "10",
|
"PROBOD_PG_POOL_SIZE": "10",
|
||||||
"PG_MIN_POOL_SIZE": "1",
|
"PROBOD_PG_MIN_POOL_SIZE": "1",
|
||||||
|
|
||||||
// Auth.
|
// Auth.
|
||||||
"AUTH_COOKIE_SECURE": "false",
|
"PROBOD_AUTH_COOKIE_SECURE": "false",
|
||||||
"AUTH_PASSWORD_ITERATIONS": "600000",
|
"PROBOD_AUTH_PASSWORD_ITERATIONS": "600000",
|
||||||
|
|
||||||
// OAuth2 server durations kept small for faster e2e flows.
|
// OAuth2 server durations kept small for faster e2e flows.
|
||||||
"OAUTH2_SERVER_ACCESS_TOKEN_DURATION": "10",
|
"PROBOD_OAUTH2_SERVER_ACCESS_TOKEN_DURATION": "10",
|
||||||
"OAUTH2_SERVER_REFRESH_TOKEN_DURATION": "10",
|
"PROBOD_OAUTH2_SERVER_REFRESH_TOKEN_DURATION": "10",
|
||||||
"OAUTH2_SERVER_AUTHORIZATION_CODE_DURATION": "5",
|
"PROBOD_OAUTH2_SERVER_AUTHORIZATION_CODE_DURATION": "5",
|
||||||
"OAUTH2_SERVER_DEVICE_CODE_DURATION": "15",
|
"PROBOD_OAUTH2_SERVER_DEVICE_CODE_DURATION": "15",
|
||||||
|
|
||||||
// Trust center.
|
// Trust center.
|
||||||
"TRUST_CENTER_HTTP_ADDR": ":10080",
|
"PROBOD_TRUST_CENTER_HTTP_ADDR": ":10080",
|
||||||
"TRUST_CENTER_HTTPS_ADDR": ":10443",
|
"PROBOD_TRUST_CENTER_HTTPS_ADDR": ":10443",
|
||||||
|
|
||||||
// AWS / S3 (SeaweedFS).
|
// AWS / S3 (SeaweedFS).
|
||||||
"AWS_BUCKET": "probod-test",
|
"PROBOD_AWS_BUCKET": "probod-test",
|
||||||
"AWS_ACCESS_KEY_ID": "probod",
|
"PROBOD_AWS_ACCESS_KEY_ID": "probod",
|
||||||
"AWS_SECRET_ACCESS_KEY": "thisisnotasecret",
|
"PROBOD_AWS_SECRET_ACCESS_KEY": "thisisnotasecret",
|
||||||
"AWS_ENDPOINT": "http://127.0.0.1:8333",
|
"PROBOD_AWS_ENDPOINT": "http://127.0.0.1:8333",
|
||||||
|
|
||||||
// Mailer.
|
// Mailer.
|
||||||
"MAILER_SENDER_NAME": "Probo Test",
|
"PROBOD_MAILER_SENDER_NAME": "Probo Test",
|
||||||
"MAILER_SENDER_EMAIL": "no-reply@test.getprobo.com",
|
"PROBOD_MAILER_SENDER_EMAIL": "no-reply@test.getprobo.com",
|
||||||
"MAILER_INTERVAL": "1",
|
"PROBOD_MAILER_INTERVAL": "1",
|
||||||
|
|
||||||
// LLM.
|
// LLM.
|
||||||
"OPENAI_API_KEY": "thisisnotasecret",
|
"PROBOD_OPENAI_API_KEY": "thisisnotasecret",
|
||||||
|
|
||||||
// Custom domains.
|
// Custom domains.
|
||||||
"CUSTOM_DOMAINS_CNAME_TARGET": "custom.test.getprobo.com",
|
"PROBOD_CUSTOM_DOMAINS_CNAME_TARGET": "custom.test.getprobo.com",
|
||||||
"ACME_DIRECTORY": "https://localhost:14000/dir",
|
"PROBOD_ACME_DIRECTORY": "https://localhost:14000/dir",
|
||||||
"ACME_EMAIL": "admin@test.getprobo.com",
|
"PROBOD_ACME_EMAIL": "admin@test.getprobo.com",
|
||||||
}
|
}
|
||||||
|
|
||||||
builder := bootstrap.NewBuilder(func(key string) string {
|
builder := bootstrap.NewBuilder(bootstrap.NewResolver(func(key string) string {
|
||||||
if v, ok := env[key]; ok {
|
if v, ok := env[key]; ok {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
return os.Getenv(key)
|
return os.Getenv(key)
|
||||||
})
|
}))
|
||||||
|
|
||||||
cfg, err := builder.Build()
|
cfg, err := builder.Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ set -e
|
|||||||
CONFIG_FILE="${CONFIG_FILE:-/etc/probod/config.yml}"
|
CONFIG_FILE="${CONFIG_FILE:-/etc/probod/config.yml}"
|
||||||
|
|
||||||
# If bootstrap env vars are set, always (re)generate the config from them.
|
# If bootstrap env vars are set, always (re)generate the config from them.
|
||||||
|
# This includes literal values and aws:// Secrets Manager references.
|
||||||
# This ensures that updated env vars take effect even when a stale config
|
# This ensures that updated env vars take effect even when a stale config
|
||||||
# file exists on a persistent volume. When no env vars are present, fall
|
# file exists on a persistent volume. When no env vars are present, fall
|
||||||
# back to an existing config file (e.g., mounted from a ConfigMap).
|
# back to an existing config file (e.g., mounted from a ConfigMap).
|
||||||
|
|||||||
7
go.mod
7
go.mod
@@ -6,11 +6,12 @@ require (
|
|||||||
codeberg.org/miekg/dns v0.6.81
|
codeberg.org/miekg/dns v0.6.81
|
||||||
github.com/99designs/gqlgen v0.17.90
|
github.com/99designs/gqlgen v0.17.90
|
||||||
github.com/anthropics/anthropic-sdk-go v1.48.0
|
github.com/anthropics/anthropic-sdk-go v1.48.0
|
||||||
github.com/aws/aws-sdk-go-v2 v1.41.12
|
github.com/aws/aws-sdk-go-v2 v1.42.0
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.32.23
|
github.com/aws/aws-sdk-go-v2/config v1.32.23
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.22
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.22
|
||||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.53.4
|
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.53.4
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.103.2
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.103.2
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.42.3
|
||||||
github.com/brianvoe/gofakeit/v7 v7.15.0
|
github.com/brianvoe/gofakeit/v7 v7.15.0
|
||||||
github.com/charmbracelet/huh v1.0.0
|
github.com/charmbracelet/huh v1.0.0
|
||||||
github.com/charmbracelet/lipgloss v1.1.0
|
github.com/charmbracelet/lipgloss v1.1.0
|
||||||
@@ -159,8 +160,8 @@ require (
|
|||||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||||
github.com/agnivade/levenshtein v1.2.1 // indirect
|
github.com/agnivade/levenshtein v1.2.1 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.13 // indirect
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.13 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.28 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.28 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.29 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.29 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.21 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.21 // indirect
|
||||||
|
|||||||
14
go.sum
14
go.sum
@@ -61,8 +61,8 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3d
|
|||||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.41.12 h1:DIKX2c31ekm9RA2D9FBj1EWXx++9AdAqRw+e78Tq2Ck=
|
github.com/aws/aws-sdk-go-v2 v1.42.0 h1:XvXMJTkFQtpBKIWZnmr9ZEOc2InWM2yldjXEJ/bymhA=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.41.12/go.mod h1:27+ACypSLljLAEKsCYOmrjKh83vuTRkuAe9Uv/3A4bg=
|
github.com/aws/aws-sdk-go-v2 v1.42.0/go.mod h1:27+ACypSLljLAEKsCYOmrjKh83vuTRkuAe9Uv/3A4bg=
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.13 h1:p1BBrg/Hhp6uK7zpejeI8QFXHJeC/mynzi04Sl03k9g=
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.13 h1:p1BBrg/Hhp6uK7zpejeI8QFXHJeC/mynzi04Sl03k9g=
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.13/go.mod h1:8cIfkE9MDhkRZGpQ22aV6/lkYeYSozpz16Smrs5x4Ls=
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.13/go.mod h1:8cIfkE9MDhkRZGpQ22aV6/lkYeYSozpz16Smrs5x4Ls=
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.32.23 h1:PYDobtcsJXK6bQe9I8RQk6s19Bz3xa3xRU08Hy1Em3Y=
|
github.com/aws/aws-sdk-go-v2/config v1.32.23 h1:PYDobtcsJXK6bQe9I8RQk6s19Bz3xa3xRU08Hy1Em3Y=
|
||||||
@@ -71,10 +71,10 @@ github.com/aws/aws-sdk-go-v2/credentials v1.19.22 h1:SHfH6wyPsEgG7fVsi5rQxWEt7tu
|
|||||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.22/go.mod h1:54nO8lKD4aQPOntM/VTWjnR+DYzTwx0YkSMZMhAgewQ=
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.22/go.mod h1:54nO8lKD4aQPOntM/VTWjnR+DYzTwx0YkSMZMhAgewQ=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.28 h1:b+kcDejJrXc30zU/w8Tc9klISwaO5wh+6T0sMBdDoHM=
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.28 h1:b+kcDejJrXc30zU/w8Tc9klISwaO5wh+6T0sMBdDoHM=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.28/go.mod h1:LnI62O9GnSv6GcuLXxOYqlq0C8EmxMcgnF6m7LdYuOY=
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.28/go.mod h1:LnI62O9GnSv6GcuLXxOYqlq0C8EmxMcgnF6m7LdYuOY=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.28 h1:Xf2j7NdVcUKomlZ4iihOP4AZ3Fzlr8h4yKpXeP+OFPg=
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 h1:f3vKqSo13fhTYb+JEcXwXefZQE26I1FB5eTSniU67ko=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.28/go.mod h1:O8cDo1dW63jU7ki//kRe1z+tLGcpnD1jrouitsQddDw=
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29/go.mod h1:MzoLFUArKGpGD+ukmPiTPG1X5x4o6M2kq4v2dr1FiEc=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.28 h1:KqIfN9kpkKkcBqBbNpNGTIrXO6ExTUvFKvXkC+YAzVo=
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 h1:RdwIf/CuUsvJX3RgJagbOyotl/cxoLY4xviKuE7p2GY=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.28/go.mod h1:uxtQiKvLtNS4iXVsH2McVD/ls8FKN/uUhe1hGxPjrw0=
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29/go.mod h1:71wt8W2EgswdZy9Mf9KNnzxZ3TiZlv4caKghPktDOkA=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.29 h1:VkE9FuzTQVjBBrnj4+oCdxCLFIz7aqLYKUCjtvxVcOs=
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.29 h1:VkE9FuzTQVjBBrnj4+oCdxCLFIz7aqLYKUCjtvxVcOs=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.29/go.mod h1:H32Z2Qth9b+9LqjyBsCnozMQ8H2N7YBUDVXwbs0iggg=
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.29/go.mod h1:H32Z2Qth9b+9LqjyBsCnozMQ8H2N7YBUDVXwbs0iggg=
|
||||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.53.4 h1:gNpZEJpHw/ANDvgAk2Z9yaiLmuFWfDnjZmzXZBMfhaw=
|
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.53.4 h1:gNpZEJpHw/ANDvgAk2Z9yaiLmuFWfDnjZmzXZBMfhaw=
|
||||||
@@ -91,6 +91,8 @@ github.com/aws/aws-sdk-go-v2/service/kms v1.52.0 h1:QNtg+Mtj1zmepk568+UKBD5DFfqh
|
|||||||
github.com/aws/aws-sdk-go-v2/service/kms v1.52.0/go.mod h1:Y0+uxvxz6ib4KktRdK0V4X45Vcs/JyYoz8H71pO8xeI=
|
github.com/aws/aws-sdk-go-v2/service/kms v1.52.0/go.mod h1:Y0+uxvxz6ib4KktRdK0V4X45Vcs/JyYoz8H71pO8xeI=
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.103.2 h1:b4ikkRk22T4xYkEgaWc3Voe+3xbt5YbbFhNehOWyUiY=
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.103.2 h1:b4ikkRk22T4xYkEgaWc3Voe+3xbt5YbbFhNehOWyUiY=
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.103.2/go.mod h1:Gp7eHZ0NZ8ZK5RXpoIUp/C8OeAmJqpCgdwEK1D/QOek=
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.103.2/go.mod h1:Gp7eHZ0NZ8ZK5RXpoIUp/C8OeAmJqpCgdwEK1D/QOek=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.42.3 h1:L9gPLf3sFH1/ao3oB2QZcaX1xGYi8hj+WJlsf3/dN+M=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.42.3/go.mod h1:9DKRlwDCw2OUDlyCIFcQCroL5M0mQTUU9qW8JEDcXmI=
|
||||||
github.com/aws/aws-sdk-go-v2/service/signin v1.1.4 h1:YcpVyIPLCbiypN6KSphijN5fC7DDjX114SqA7prnnxg=
|
github.com/aws/aws-sdk-go-v2/service/signin v1.1.4 h1:YcpVyIPLCbiypN6KSphijN5fC7DDjX114SqA7prnnxg=
|
||||||
github.com/aws/aws-sdk-go-v2/service/signin v1.1.4/go.mod h1:5ZICS++oFTRPfa1GsBqFDWX/8WamZ/QQOcCzIuU/zLw=
|
github.com/aws/aws-sdk-go-v2/service/signin v1.1.4/go.mod h1:5ZICS++oFTRPfa1GsBqFDWX/8WamZ/QQOcCzIuU/zLw=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.31.2 h1:ySNWu7TPmj5fKFIa1GYvX+Ddxd5ccruqC20aMNuyWDM=
|
github.com/aws/aws-sdk-go-v2/service/sso v1.31.2 h1:ySNWu7TPmj5fKFIa1GYvX+Ddxd5ccruqC20aMNuyWDM=
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ package bootstrap
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"go.probo.inc/probo/pkg/probodconfig"
|
"go.probo.inc/probo/pkg/probodconfig"
|
||||||
@@ -26,18 +25,18 @@ import (
|
|||||||
type EnvGetter func(key string) string
|
type EnvGetter func(key string) string
|
||||||
|
|
||||||
type Builder struct {
|
type Builder struct {
|
||||||
getEnv EnvGetter
|
resolver *Resolver
|
||||||
samlCertificate string
|
samlCertificate string
|
||||||
samlPrivateKey string
|
samlPrivateKey string
|
||||||
oauth2SigningKey string
|
oauth2SigningKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBuilder(getEnv EnvGetter) *Builder {
|
func NewBuilder(resolver *Resolver) *Builder {
|
||||||
if getEnv == nil {
|
if resolver == nil {
|
||||||
getEnv = os.Getenv
|
resolver = NewResolver(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Builder{getEnv: getEnv}
|
return &Builder{resolver: resolver}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) Build() (*probodconfig.FullConfig, error) {
|
func (b *Builder) Build() (*probodconfig.FullConfig, error) {
|
||||||
@@ -57,272 +56,272 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
|
|||||||
cfg := &probodconfig.FullConfig{
|
cfg := &probodconfig.FullConfig{
|
||||||
Unit: probodconfig.UnitConfig{
|
Unit: probodconfig.UnitConfig{
|
||||||
Metrics: probodconfig.MetricsConfig{
|
Metrics: probodconfig.MetricsConfig{
|
||||||
Addr: b.getEnvOrDefault("METRICS_ADDR", "localhost:8081"),
|
Addr: b.resolver.getEnvOrDefault("PROBOD_METRICS_ADDR", "localhost:8081"),
|
||||||
},
|
},
|
||||||
Tracing: probodconfig.TracingConfig{
|
Tracing: probodconfig.TracingConfig{
|
||||||
Addr: b.getEnvOrDefault("TRACING_ADDR", "localhost:4318"),
|
Addr: b.resolver.getEnvOrDefault("PROBOD_TRACING_ADDR", "localhost:4318"),
|
||||||
MaxBatchSize: b.getEnvIntOrDefault("TRACING_MAX_BATCH_SIZE", 512),
|
MaxBatchSize: b.resolver.getEnvIntOrDefault("PROBOD_TRACING_MAX_BATCH_SIZE", 512),
|
||||||
BatchTimeout: b.getEnvIntOrDefault("TRACING_BATCH_TIMEOUT", 5),
|
BatchTimeout: b.resolver.getEnvIntOrDefault("PROBOD_TRACING_BATCH_TIMEOUT", 5),
|
||||||
ExportTimeout: b.getEnvIntOrDefault("TRACING_EXPORT_TIMEOUT", 30),
|
ExportTimeout: b.resolver.getEnvIntOrDefault("PROBOD_TRACING_EXPORT_TIMEOUT", 30),
|
||||||
MaxQueueSize: b.getEnvIntOrDefault("TRACING_MAX_QUEUE_SIZE", 2048),
|
MaxQueueSize: b.resolver.getEnvIntOrDefault("PROBOD_TRACING_MAX_QUEUE_SIZE", 2048),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Probod: probodconfig.Config{
|
Probod: probodconfig.Config{
|
||||||
BaseURL: b.getEnvOrDefault("PROBOD_BASE_URL", "http://localhost:8080"),
|
BaseURL: b.resolver.getEnvOrDefault("PROBOD_BASE_URL", "http://localhost:8080"),
|
||||||
EncryptionKey: b.getEnv("PROBOD_ENCRYPTION_KEY"),
|
EncryptionKey: b.resolver.getEnv("PROBOD_ENCRYPTION_KEY"),
|
||||||
ChromeDPAddr: b.getEnvOrDefault("CHROME_DP_ADDR", "localhost:9222"),
|
ChromeDPAddr: b.resolver.getEnvOrDefault("PROBOD_CHROME_DP_ADDR", "localhost:9222"),
|
||||||
Api: probodconfig.APIConfig{
|
Api: probodconfig.APIConfig{
|
||||||
Addr: b.getEnvOrDefault("API_ADDR", ":8080"),
|
Addr: b.resolver.getEnvOrDefault("PROBOD_API_ADDR", ":8080"),
|
||||||
ProxyProtocol: probodconfig.ProxyProtocolConfig{
|
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{
|
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),
|
ExtraHeaderFields: make(map[string]string),
|
||||||
},
|
},
|
||||||
Pg: probodconfig.PgConfig{
|
Pg: probodconfig.PgConfig{
|
||||||
Addr: b.getEnvOrDefault("PG_ADDR", "localhost:5432"),
|
Addr: b.resolver.getEnvOrDefault("PROBOD_PG_ADDR", "localhost:5432"),
|
||||||
Username: b.getEnvOrDefault("PG_USERNAME", "probod"),
|
Username: b.resolver.getEnvOrDefault("PROBOD_PG_USERNAME", "probod"),
|
||||||
Password: b.getEnvOrDefault("PG_PASSWORD", "probod"),
|
Password: b.resolver.getEnvOrDefault("PROBOD_PG_PASSWORD", "probod"),
|
||||||
Database: b.getEnvOrDefault("PG_DATABASE", "probod"),
|
Database: b.resolver.getEnvOrDefault("PROBOD_PG_DATABASE", "probod"),
|
||||||
PoolSize: int32(b.getEnvIntOrDefault("PG_POOL_SIZE", 100)),
|
PoolSize: int32(b.resolver.getEnvIntOrDefault("PROBOD_PG_POOL_SIZE", 100)),
|
||||||
MinPoolSize: int32(b.getEnvIntOrDefault("PG_MIN_POOL_SIZE", 10)),
|
MinPoolSize: int32(b.resolver.getEnvIntOrDefault("PROBOD_PG_MIN_POOL_SIZE", 10)),
|
||||||
MaxConnIdleTimeSeconds: b.getEnvIntOrDefault("PG_MAX_CONN_IDLE_TIME_SECONDS", 1800),
|
MaxConnIdleTimeSeconds: b.resolver.getEnvIntOrDefault("PROBOD_PG_MAX_CONN_IDLE_TIME_SECONDS", 1800),
|
||||||
MaxConnLifetimeSeconds: b.getEnvIntOrDefault("PG_MAX_CONN_LIFETIME_SECONDS", 3600),
|
MaxConnLifetimeSeconds: b.resolver.getEnvIntOrDefault("PROBOD_PG_MAX_CONN_LIFETIME_SECONDS", 3600),
|
||||||
MaxConnLifetimeJitterSeconds: b.getEnvIntOrDefault("PG_MAX_CONN_LIFETIME_JITTER_SECONDS", 300),
|
MaxConnLifetimeJitterSeconds: b.resolver.getEnvIntOrDefault("PROBOD_PG_MAX_CONN_LIFETIME_JITTER_SECONDS", 300),
|
||||||
HealthCheckPeriodSeconds: b.getEnvIntOrDefault("PG_HEALTH_CHECK_PERIOD_SECONDS", 60),
|
HealthCheckPeriodSeconds: b.resolver.getEnvIntOrDefault("PROBOD_PG_HEALTH_CHECK_PERIOD_SECONDS", 60),
|
||||||
CACertBundle: pgCACertBundle,
|
CACertBundle: pgCACertBundle,
|
||||||
Debug: b.getEnvBoolOrDefault("PG_DEBUG", false),
|
Debug: b.resolver.getEnvBoolOrDefault("PROBOD_PG_DEBUG", false),
|
||||||
},
|
},
|
||||||
Auth: probodconfig.AuthConfig{
|
Auth: probodconfig.AuthConfig{
|
||||||
DisableSignup: b.getEnvBoolOrDefault("AUTH_DISABLE_SIGNUP", false),
|
DisableSignup: b.resolver.getEnvBoolOrDefault("PROBOD_AUTH_DISABLE_SIGNUP", false),
|
||||||
InvitationConfirmationTokenValidity: b.getEnvIntOrDefault("AUTH_INVITATION_TOKEN_VALIDITY", 3600),
|
InvitationConfirmationTokenValidity: b.resolver.getEnvIntOrDefault("PROBOD_AUTH_INVITATION_TOKEN_VALIDITY", 3600),
|
||||||
PasswordResetTokenValidity: b.getEnvIntOrDefault("AUTH_PASSWORD_RESET_TOKEN_VALIDITY", 3600),
|
PasswordResetTokenValidity: b.resolver.getEnvIntOrDefault("PROBOD_AUTH_PASSWORD_RESET_TOKEN_VALIDITY", 3600),
|
||||||
MagicLinkTokenValidity: b.getEnvIntOrDefault("AUTH_MAGIC_LINK_TOKEN_VALIDITY", 900),
|
MagicLinkTokenValidity: b.resolver.getEnvIntOrDefault("PROBOD_AUTH_MAGIC_LINK_TOKEN_VALIDITY", 900),
|
||||||
Cookie: probodconfig.CookieConfig{
|
Cookie: probodconfig.CookieConfig{
|
||||||
Name: b.getEnvOrDefault("AUTH_COOKIE_NAME", "SSID"),
|
Name: b.resolver.getEnvOrDefault("PROBOD_AUTH_COOKIE_NAME", "SSID"),
|
||||||
Domain: b.getEnvOrDefault("AUTH_COOKIE_DOMAIN", "localhost"),
|
Domain: b.resolver.getEnvOrDefault("PROBOD_AUTH_COOKIE_DOMAIN", "localhost"),
|
||||||
Secret: b.getEnv("AUTH_COOKIE_SECRET"),
|
Secret: b.resolver.getEnv("PROBOD_AUTH_COOKIE_SECRET"),
|
||||||
Duration: b.getEnvIntOrDefault("AUTH_COOKIE_DURATION", 24),
|
Duration: b.resolver.getEnvIntOrDefault("PROBOD_AUTH_COOKIE_DURATION", 24),
|
||||||
Secure: b.getEnvBoolOrDefault("AUTH_COOKIE_SECURE", true),
|
Secure: b.resolver.getEnvBoolOrDefault("PROBOD_AUTH_COOKIE_SECURE", true),
|
||||||
},
|
},
|
||||||
Password: probodconfig.PasswordConfig{
|
Password: probodconfig.PasswordConfig{
|
||||||
Pepper: b.getEnv("AUTH_PASSWORD_PEPPER"),
|
Pepper: b.resolver.getEnv("PROBOD_AUTH_PASSWORD_PEPPER"),
|
||||||
Iterations: b.getEnvIntOrDefault("AUTH_PASSWORD_ITERATIONS", 1000000),
|
Iterations: b.resolver.getEnvIntOrDefault("PROBOD_AUTH_PASSWORD_ITERATIONS", 1000000),
|
||||||
},
|
},
|
||||||
SAML: probodconfig.SAMLConfig{
|
SAML: probodconfig.SAMLConfig{
|
||||||
SessionDuration: b.getEnvIntOrDefault("SAML_SESSION_DURATION", 604800),
|
SessionDuration: b.resolver.getEnvIntOrDefault("PROBOD_SAML_SESSION_DURATION", 604800),
|
||||||
CleanupIntervalSeconds: b.getEnvIntOrDefault("SAML_CLEANUP_INTERVAL_SECONDS", 0),
|
CleanupIntervalSeconds: b.resolver.getEnvIntOrDefault("PROBOD_SAML_CLEANUP_INTERVAL_SECONDS", 0),
|
||||||
Certificate: samlCert,
|
Certificate: samlCert,
|
||||||
PrivateKey: samlKey,
|
PrivateKey: samlKey,
|
||||||
DomainVerificationIntervalSeconds: b.getEnvIntOrDefault("SAML_DOMAIN_VERIFICATION_INTERVAL_SECONDS", 60),
|
DomainVerificationIntervalSeconds: b.resolver.getEnvIntOrDefault("PROBOD_SAML_DOMAIN_VERIFICATION_INTERVAL_SECONDS", 60),
|
||||||
DomainVerificationResolverAddr: b.getEnvOrDefault("SAML_DOMAIN_VERIFICATION_RESOLVER_ADDR", "8.8.8.8:53"),
|
DomainVerificationResolverAddr: b.resolver.getEnvOrDefault("PROBOD_SAML_DOMAIN_VERIFICATION_RESOLVER_ADDR", "8.8.8.8:53"),
|
||||||
},
|
},
|
||||||
Google: probodconfig.OIDCProviderConfig{
|
Google: probodconfig.OIDCProviderConfig{
|
||||||
ClientID: b.getEnv("AUTH_GOOGLE_CLIENT_ID"),
|
ClientID: b.resolver.getEnv("PROBOD_AUTH_GOOGLE_CLIENT_ID"),
|
||||||
ClientSecret: b.getEnv("AUTH_GOOGLE_CLIENT_SECRET"),
|
ClientSecret: b.resolver.getEnv("PROBOD_AUTH_GOOGLE_CLIENT_SECRET"),
|
||||||
Enabled: b.getEnv("AUTH_GOOGLE_CLIENT_ID") != "" && b.getEnv("AUTH_GOOGLE_CLIENT_SECRET") != "",
|
Enabled: b.resolver.getEnv("PROBOD_AUTH_GOOGLE_CLIENT_ID") != "" && b.resolver.getEnv("PROBOD_AUTH_GOOGLE_CLIENT_SECRET") != "",
|
||||||
},
|
},
|
||||||
Microsoft: probodconfig.OIDCProviderConfig{
|
Microsoft: probodconfig.OIDCProviderConfig{
|
||||||
ClientID: b.getEnv("AUTH_MICROSOFT_CLIENT_ID"),
|
ClientID: b.resolver.getEnv("PROBOD_AUTH_MICROSOFT_CLIENT_ID"),
|
||||||
ClientSecret: b.getEnv("AUTH_MICROSOFT_CLIENT_SECRET"),
|
ClientSecret: b.resolver.getEnv("PROBOD_AUTH_MICROSOFT_CLIENT_SECRET"),
|
||||||
Enabled: b.getEnv("AUTH_MICROSOFT_CLIENT_ID") != "" && b.getEnv("AUTH_MICROSOFT_CLIENT_SECRET") != "",
|
Enabled: b.resolver.getEnv("PROBOD_AUTH_MICROSOFT_CLIENT_ID") != "" && b.resolver.getEnv("PROBOD_AUTH_MICROSOFT_CLIENT_SECRET") != "",
|
||||||
},
|
},
|
||||||
OAuth2Server: probodconfig.OAuth2ServerConfig{
|
OAuth2Server: probodconfig.OAuth2ServerConfig{
|
||||||
SigningKeys: []probodconfig.OAuth2SigningKeyConfig{{
|
SigningKeys: []probodconfig.OAuth2SigningKeyConfig{{
|
||||||
PrivateKey: oauth2SigningKey,
|
PrivateKey: oauth2SigningKey,
|
||||||
KID: b.getEnvOrDefault("OAUTH2_SERVER_SIGNING_KEY_KID", "default"),
|
KID: b.resolver.getEnvOrDefault("PROBOD_OAUTH2_SERVER_SIGNING_KEY_KID", "default"),
|
||||||
Active: true,
|
Active: true,
|
||||||
}},
|
}},
|
||||||
AccessTokenDuration: b.getEnvIntOrDefault("OAUTH2_SERVER_ACCESS_TOKEN_DURATION", 3600),
|
AccessTokenDuration: b.resolver.getEnvIntOrDefault("PROBOD_OAUTH2_SERVER_ACCESS_TOKEN_DURATION", 3600),
|
||||||
RefreshTokenDuration: b.getEnvIntOrDefault("OAUTH2_SERVER_REFRESH_TOKEN_DURATION", 2592000),
|
RefreshTokenDuration: b.resolver.getEnvIntOrDefault("PROBOD_OAUTH2_SERVER_REFRESH_TOKEN_DURATION", 2592000),
|
||||||
AuthorizationCodeDuration: b.getEnvIntOrDefault("OAUTH2_SERVER_AUTHORIZATION_CODE_DURATION", 600),
|
AuthorizationCodeDuration: b.resolver.getEnvIntOrDefault("PROBOD_OAUTH2_SERVER_AUTHORIZATION_CODE_DURATION", 600),
|
||||||
DeviceCodeDuration: b.getEnvIntOrDefault("OAUTH2_SERVER_DEVICE_CODE_DURATION", 600),
|
DeviceCodeDuration: b.resolver.getEnvIntOrDefault("PROBOD_OAUTH2_SERVER_DEVICE_CODE_DURATION", 600),
|
||||||
CIMDAllowedClientIDs: b.parseOriginsList(
|
CIMDAllowedClientIDs: b.parseOriginsList(
|
||||||
b.getEnv("OAUTH2_SERVER_CIMD_ALLOWED_CLIENT_IDS"),
|
b.resolver.getEnv("PROBOD_OAUTH2_SERVER_CIMD_ALLOWED_CLIENT_IDS"),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
TrustCenter: probodconfig.TrustCenterConfig{
|
TrustCenter: probodconfig.TrustCenterConfig{
|
||||||
HTTPAddr: b.getEnvOrDefault("TRUST_CENTER_HTTP_ADDR", ":80"),
|
HTTPAddr: b.resolver.getEnvOrDefault("PROBOD_TRUST_CENTER_HTTP_ADDR", ":80"),
|
||||||
HTTPSAddr: b.getEnvOrDefault("TRUST_CENTER_HTTPS_ADDR", ":443"),
|
HTTPSAddr: b.resolver.getEnvOrDefault("PROBOD_TRUST_CENTER_HTTPS_ADDR", ":443"),
|
||||||
ProxyProtocol: probodconfig.ProxyProtocolConfig{
|
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{
|
AWS: probodconfig.AWSConfig{
|
||||||
Region: b.getEnvOrDefault("AWS_REGION", "us-east-1"),
|
Region: b.resolver.getEnvOrDefault("PROBOD_AWS_REGION", "us-east-1"),
|
||||||
Bucket: b.getEnvOrDefault("AWS_BUCKET", "probod"),
|
Bucket: b.resolver.getEnvOrDefault("PROBOD_AWS_BUCKET", "probod"),
|
||||||
AccessKeyID: b.getEnv("AWS_ACCESS_KEY_ID"),
|
AccessKeyID: b.resolver.getEnv("PROBOD_AWS_ACCESS_KEY_ID"),
|
||||||
SecretAccessKey: b.getEnv("AWS_SECRET_ACCESS_KEY"),
|
SecretAccessKey: b.resolver.getEnv("PROBOD_AWS_SECRET_ACCESS_KEY"),
|
||||||
Endpoint: b.getEnv("AWS_ENDPOINT"),
|
Endpoint: b.resolver.getEnv("PROBOD_AWS_ENDPOINT"),
|
||||||
UsePathStyle: b.getEnvBoolOrDefault("AWS_USE_PATH_STYLE", false),
|
UsePathStyle: b.resolver.getEnvBoolOrDefault("PROBOD_AWS_USE_PATH_STYLE", false),
|
||||||
},
|
},
|
||||||
Notifications: probodconfig.NotificationsConfig{
|
Notifications: probodconfig.NotificationsConfig{
|
||||||
Mailer: probodconfig.MailerConfig{
|
Mailer: probodconfig.MailerConfig{
|
||||||
SenderName: b.getEnvOrDefault("MAILER_SENDER_NAME", "Probo"),
|
SenderName: b.resolver.getEnvOrDefault("PROBOD_MAILER_SENDER_NAME", "Probo"),
|
||||||
SenderEmail: b.getEnvOrDefault("MAILER_SENDER_EMAIL", "no-reply@notification.getprobo.com"),
|
SenderEmail: b.resolver.getEnvOrDefault("PROBOD_MAILER_SENDER_EMAIL", "no-reply@notification.getprobo.com"),
|
||||||
MailerInterval: b.getEnvIntOrDefault("MAILER_INTERVAL", 60),
|
MailerInterval: b.resolver.getEnvIntOrDefault("PROBOD_MAILER_INTERVAL", 60),
|
||||||
SMTP: probodconfig.SMTPConfig{
|
SMTP: probodconfig.SMTPConfig{
|
||||||
Addr: b.getEnvOrDefault("SMTP_ADDR", "localhost:1025"),
|
Addr: b.resolver.getEnvOrDefault("PROBOD_SMTP_ADDR", "localhost:1025"),
|
||||||
User: b.getEnv("SMTP_USER"),
|
User: b.resolver.getEnv("PROBOD_SMTP_USER"),
|
||||||
Password: b.getEnv("SMTP_PASSWORD"),
|
Password: b.resolver.getEnv("PROBOD_SMTP_PASSWORD"),
|
||||||
TLSRequired: b.getEnvBoolOrDefault("SMTP_TLS_REQUIRED", false),
|
TLSRequired: b.resolver.getEnvBoolOrDefault("PROBOD_SMTP_TLS_REQUIRED", false),
|
||||||
HelloName: b.getEnv("SMTP_HELLO_NAME"),
|
HelloName: b.resolver.getEnv("PROBOD_SMTP_HELLO_NAME"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Slack: probodconfig.SlackConfig{
|
Slack: probodconfig.SlackConfig{
|
||||||
SenderInterval: b.getEnvIntOrDefault("SLACK_SENDER_INTERVAL", 60),
|
SenderInterval: b.resolver.getEnvIntOrDefault("PROBOD_SLACK_SENDER_INTERVAL", 60),
|
||||||
SigningSecret: b.getEnv("CONNECTOR_SLACK_SIGNING_SECRET"),
|
SigningSecret: b.resolver.getEnv("PROBOD_CONNECTOR_SLACK_SIGNING_SECRET"),
|
||||||
},
|
},
|
||||||
Webhook: probodconfig.WebhookConfig{
|
Webhook: probodconfig.WebhookConfig{
|
||||||
SenderInterval: b.getEnvIntOrDefault("WEBHOOK_SENDER_INTERVAL", 5),
|
SenderInterval: b.resolver.getEnvIntOrDefault("PROBOD_WEBHOOK_SENDER_INTERVAL", 5),
|
||||||
CacheTTL: b.getEnvIntOrDefault("WEBHOOK_CACHE_TTL", 86400),
|
CacheTTL: b.resolver.getEnvIntOrDefault("PROBOD_WEBHOOK_CACHE_TTL", 86400),
|
||||||
},
|
},
|
||||||
Document: probodconfig.DocumentNotificationConfig{
|
Document: probodconfig.DocumentNotificationConfig{
|
||||||
Interval: b.getEnvIntOrDefault("DOCUMENT_NOTIFICATION_INTERVAL", 300),
|
Interval: b.resolver.getEnvIntOrDefault("PROBOD_DOCUMENT_NOTIFICATION_INTERVAL", 300),
|
||||||
DebounceDelay: b.getEnvIntOrDefault("DOCUMENT_NOTIFICATION_DEBOUNCE_DELAY", 900),
|
DebounceDelay: b.resolver.getEnvIntOrDefault("PROBOD_DOCUMENT_NOTIFICATION_DEBOUNCE_DELAY", 900),
|
||||||
ReminderInterval: b.getEnvIntOrDefault("DOCUMENT_NOTIFICATION_REMINDER_INTERVAL", 86400),
|
ReminderInterval: b.resolver.getEnvIntOrDefault("PROBOD_DOCUMENT_NOTIFICATION_REMINDER_INTERVAL", 86400),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Agents: probodconfig.AgentsConfig{
|
Agents: probodconfig.AgentsConfig{
|
||||||
Providers: map[string]probodconfig.LLMProviderConfig{
|
Providers: map[string]probodconfig.LLMProviderConfig{
|
||||||
"openai": {
|
"openai": {
|
||||||
Type: "openai",
|
Type: "openai",
|
||||||
APIKey: b.getEnv("OPENAI_API_KEY"),
|
APIKey: b.resolver.getEnv("PROBOD_OPENAI_API_KEY"),
|
||||||
},
|
},
|
||||||
"anthropic": {
|
"anthropic": {
|
||||||
Type: "anthropic",
|
Type: "anthropic",
|
||||||
APIKey: b.getEnv("ANTHROPIC_API_KEY"),
|
APIKey: b.resolver.getEnv("PROBOD_ANTHROPIC_API_KEY"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Default: probodconfig.LLMAgentConfig{
|
Default: probodconfig.LLMAgentConfig{
|
||||||
Provider: b.getEnvOrDefault("AGENT_DEFAULT_PROVIDER", "openai"),
|
Provider: b.resolver.getEnvOrDefault("PROBOD_AGENT_DEFAULT_PROVIDER", "openai"),
|
||||||
ModelName: b.getEnvOrDefault("AGENT_DEFAULT_MODEL_NAME", "gpt-4o"),
|
ModelName: b.resolver.getEnvOrDefault("PROBOD_AGENT_DEFAULT_MODEL_NAME", "gpt-4o"),
|
||||||
Temperature: new(b.getEnvFloatOrDefault("AGENT_DEFAULT_TEMPERATURE", 0.1)),
|
Temperature: new(b.resolver.getEnvFloatOrDefault("PROBOD_AGENT_DEFAULT_TEMPERATURE", 0.1)),
|
||||||
MaxTokens: new(b.getEnvIntOrDefault("AGENT_DEFAULT_MAX_TOKENS", 4096)),
|
MaxTokens: new(b.resolver.getEnvIntOrDefault("PROBOD_AGENT_DEFAULT_MAX_TOKENS", 4096)),
|
||||||
},
|
},
|
||||||
Probo: probodconfig.LLMAgentConfig{
|
Probo: probodconfig.LLMAgentConfig{
|
||||||
Provider: b.getEnvOrDefault("AGENT_PROBO_PROVIDER", ""),
|
Provider: b.resolver.getEnvOrDefault("PROBOD_AGENT_PROBO_PROVIDER", ""),
|
||||||
ModelName: b.getEnvOrDefault("AGENT_PROBO_MODEL_NAME", ""),
|
ModelName: b.resolver.getEnvOrDefault("PROBOD_AGENT_PROBO_MODEL_NAME", ""),
|
||||||
Temperature: b.getEnvFloatPtr("AGENT_PROBO_TEMPERATURE"),
|
Temperature: b.resolver.getEnvFloatPtr("PROBOD_AGENT_PROBO_TEMPERATURE"),
|
||||||
MaxTokens: b.getEnvIntPtr("AGENT_PROBO_MAX_TOKENS"),
|
MaxTokens: b.resolver.getEnvIntPtr("PROBOD_AGENT_PROBO_MAX_TOKENS"),
|
||||||
},
|
},
|
||||||
EvidenceDescriber: probodconfig.LLMAgentConfig{
|
EvidenceDescriber: probodconfig.LLMAgentConfig{
|
||||||
Provider: b.getEnvOrDefault("AGENT_EVIDENCE_DESCRIBER_PROVIDER", ""),
|
Provider: b.resolver.getEnvOrDefault("PROBOD_AGENT_EVIDENCE_DESCRIBER_PROVIDER", ""),
|
||||||
ModelName: b.getEnvOrDefault("AGENT_EVIDENCE_DESCRIBER_MODEL_NAME", ""),
|
ModelName: b.resolver.getEnvOrDefault("PROBOD_AGENT_EVIDENCE_DESCRIBER_MODEL_NAME", ""),
|
||||||
Temperature: b.getEnvFloatPtr("AGENT_EVIDENCE_DESCRIBER_TEMPERATURE"),
|
Temperature: b.resolver.getEnvFloatPtr("PROBOD_AGENT_EVIDENCE_DESCRIBER_TEMPERATURE"),
|
||||||
MaxTokens: b.getEnvIntPtr("AGENT_EVIDENCE_DESCRIBER_MAX_TOKENS"),
|
MaxTokens: b.resolver.getEnvIntPtr("PROBOD_AGENT_EVIDENCE_DESCRIBER_MAX_TOKENS"),
|
||||||
},
|
},
|
||||||
ThirdPartyVetter: probodconfig.LLMAgentConfig{
|
ThirdPartyVetter: probodconfig.LLMAgentConfig{
|
||||||
Provider: b.getEnvOrDefault("AGENT_THIRD_PARTY_VETTER_PROVIDER", ""),
|
Provider: b.resolver.getEnvOrDefault("PROBOD_AGENT_THIRD_PARTY_VETTER_PROVIDER", ""),
|
||||||
ModelName: b.getEnvOrDefault("AGENT_THIRD_PARTY_VETTER_MODEL_NAME", ""),
|
ModelName: b.resolver.getEnvOrDefault("PROBOD_AGENT_THIRD_PARTY_VETTER_MODEL_NAME", ""),
|
||||||
Temperature: b.getEnvFloatPtr("AGENT_THIRD_PARTY_VETTER_TEMPERATURE"),
|
Temperature: b.resolver.getEnvFloatPtr("PROBOD_AGENT_THIRD_PARTY_VETTER_TEMPERATURE"),
|
||||||
MaxTokens: b.getEnvIntPtr("AGENT_THIRD_PARTY_VETTER_MAX_TOKENS"),
|
MaxTokens: b.resolver.getEnvIntPtr("PROBOD_AGENT_THIRD_PARTY_VETTER_MAX_TOKENS"),
|
||||||
},
|
},
|
||||||
ThirdPartyDisambiguation: probodconfig.LLMAgentConfig{
|
ThirdPartyDisambiguation: probodconfig.LLMAgentConfig{
|
||||||
Provider: b.getEnvOrDefault("AGENT_THIRD_PARTY_DISAMBIGUATION_PROVIDER", ""),
|
Provider: b.resolver.getEnvOrDefault("PROBOD_AGENT_THIRD_PARTY_DISAMBIGUATION_PROVIDER", ""),
|
||||||
ModelName: b.getEnvOrDefault("AGENT_THIRD_PARTY_DISAMBIGUATION_MODEL_NAME", ""),
|
ModelName: b.resolver.getEnvOrDefault("PROBOD_AGENT_THIRD_PARTY_DISAMBIGUATION_MODEL_NAME", ""),
|
||||||
// The disambiguation agent emits a single id plus a
|
// The disambiguation agent emits a single id plus a
|
||||||
// short rationale, but the budget must leave headroom
|
// short rationale, but the budget must leave headroom
|
||||||
// for reasoning models whose reasoning tokens count
|
// for reasoning models whose reasoning tokens count
|
||||||
// against max_tokens; too small a budget truncates the
|
// against max_tokens; too small a budget truncates the
|
||||||
// JSON.
|
// JSON.
|
||||||
Temperature: b.getEnvFloatPtr("AGENT_THIRD_PARTY_DISAMBIGUATION_TEMPERATURE"),
|
Temperature: b.resolver.getEnvFloatPtr("PROBOD_AGENT_THIRD_PARTY_DISAMBIGUATION_TEMPERATURE"),
|
||||||
MaxTokens: new(b.getEnvIntOrDefault("AGENT_THIRD_PARTY_DISAMBIGUATION_MAX_TOKENS", 4096)),
|
MaxTokens: new(b.resolver.getEnvIntOrDefault("PROBOD_AGENT_THIRD_PARTY_DISAMBIGUATION_MAX_TOKENS", 4096)),
|
||||||
},
|
},
|
||||||
TrackerMapping: probodconfig.LLMAgentConfig{
|
TrackerMapping: probodconfig.LLMAgentConfig{
|
||||||
Provider: b.getEnvOrDefault("AGENT_TRACKER_MAPPING_PROVIDER", ""),
|
Provider: b.resolver.getEnvOrDefault("PROBOD_AGENT_TRACKER_MAPPING_PROVIDER", ""),
|
||||||
ModelName: b.getEnvOrDefault("AGENT_TRACKER_MAPPING_MODEL_NAME", ""),
|
ModelName: b.resolver.getEnvOrDefault("PROBOD_AGENT_TRACKER_MAPPING_MODEL_NAME", ""),
|
||||||
// The tracker agents emit tiny structured JSON, but
|
// The tracker agents emit tiny structured JSON, but
|
||||||
// the budget must leave headroom for reasoning
|
// the budget must leave headroom for reasoning
|
||||||
// models whose reasoning tokens count against
|
// models whose reasoning tokens count against
|
||||||
// max_tokens; too small a budget truncates the JSON.
|
// max_tokens; too small a budget truncates the JSON.
|
||||||
Temperature: b.getEnvFloatPtr("AGENT_TRACKER_MAPPING_TEMPERATURE"),
|
Temperature: b.resolver.getEnvFloatPtr("PROBOD_AGENT_TRACKER_MAPPING_TEMPERATURE"),
|
||||||
MaxTokens: new(b.getEnvIntOrDefault("AGENT_TRACKER_MAPPING_MAX_TOKENS", 4096)),
|
MaxTokens: new(b.resolver.getEnvIntOrDefault("PROBOD_AGENT_TRACKER_MAPPING_MAX_TOKENS", 4096)),
|
||||||
},
|
},
|
||||||
TrackerEnrichment: probodconfig.LLMAgentConfig{
|
TrackerEnrichment: probodconfig.LLMAgentConfig{
|
||||||
Provider: b.getEnvOrDefault("AGENT_TRACKER_ENRICHMENT_PROVIDER", ""),
|
Provider: b.resolver.getEnvOrDefault("PROBOD_AGENT_TRACKER_ENRICHMENT_PROVIDER", ""),
|
||||||
ModelName: b.getEnvOrDefault("AGENT_TRACKER_ENRICHMENT_MODEL_NAME", ""),
|
ModelName: b.resolver.getEnvOrDefault("PROBOD_AGENT_TRACKER_ENRICHMENT_MODEL_NAME", ""),
|
||||||
// See the tracker-mapping note: keep ample headroom so
|
// See the tracker-mapping note: keep ample headroom so
|
||||||
// reasoning models do not truncate the structured JSON.
|
// reasoning models do not truncate the structured JSON.
|
||||||
Temperature: b.getEnvFloatPtr("AGENT_TRACKER_ENRICHMENT_TEMPERATURE"),
|
Temperature: b.resolver.getEnvFloatPtr("PROBOD_AGENT_TRACKER_ENRICHMENT_TEMPERATURE"),
|
||||||
MaxTokens: new(b.getEnvIntOrDefault("AGENT_TRACKER_ENRICHMENT_MAX_TOKENS", 4096)),
|
MaxTokens: new(b.resolver.getEnvIntOrDefault("PROBOD_AGENT_TRACKER_ENRICHMENT_MAX_TOKENS", 4096)),
|
||||||
},
|
},
|
||||||
CommonThirdPartyEnrichment: probodconfig.LLMAgentConfig{
|
CommonThirdPartyEnrichment: probodconfig.LLMAgentConfig{
|
||||||
Provider: b.getEnvOrDefault("AGENT_COMMON_THIRD_PARTY_ENRICHMENT_PROVIDER", ""),
|
Provider: b.resolver.getEnvOrDefault("PROBOD_AGENT_COMMON_THIRD_PARTY_ENRICHMENT_PROVIDER", ""),
|
||||||
ModelName: b.getEnvOrDefault("AGENT_COMMON_THIRD_PARTY_ENRICHMENT_MODEL_NAME", ""),
|
ModelName: b.resolver.getEnvOrDefault("PROBOD_AGENT_COMMON_THIRD_PARTY_ENRICHMENT_MODEL_NAME", ""),
|
||||||
// Agent B browses pages and emits a moderate structured
|
// Agent B browses pages and emits a moderate structured
|
||||||
// output; the budget must leave headroom for reasoning
|
// output; the budget must leave headroom for reasoning
|
||||||
// models whose reasoning tokens count against max_tokens.
|
// models whose reasoning tokens count against max_tokens.
|
||||||
Temperature: b.getEnvFloatPtr("AGENT_COMMON_THIRD_PARTY_ENRICHMENT_TEMPERATURE"),
|
Temperature: b.resolver.getEnvFloatPtr("PROBOD_AGENT_COMMON_THIRD_PARTY_ENRICHMENT_TEMPERATURE"),
|
||||||
MaxTokens: new(b.getEnvIntOrDefault("AGENT_COMMON_THIRD_PARTY_ENRICHMENT_MAX_TOKENS", 8192)),
|
MaxTokens: new(b.resolver.getEnvIntOrDefault("PROBOD_AGENT_COMMON_THIRD_PARTY_ENRICHMENT_MAX_TOKENS", 8192)),
|
||||||
},
|
},
|
||||||
Tools: probodconfig.AgentToolsConfig{
|
Tools: probodconfig.AgentToolsConfig{
|
||||||
FirecrawlAPIKey: b.getEnv("FIRECRAWL_API_KEY"),
|
FirecrawlAPIKey: b.resolver.getEnv("PROBOD_FIRECRAWL_API_KEY"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
CustomDomains: probodconfig.CustomDomainsConfig{
|
CustomDomains: probodconfig.CustomDomainsConfig{
|
||||||
RenewalInterval: b.getEnvIntOrDefault("CUSTOM_DOMAINS_RENEWAL_INTERVAL", 3600),
|
RenewalInterval: b.resolver.getEnvIntOrDefault("PROBOD_CUSTOM_DOMAINS_RENEWAL_INTERVAL", 3600),
|
||||||
ProvisionInterval: b.getEnvIntOrDefault("CUSTOM_DOMAINS_PROVISION_INTERVAL", 30),
|
ProvisionInterval: b.resolver.getEnvIntOrDefault("PROBOD_CUSTOM_DOMAINS_PROVISION_INTERVAL", 30),
|
||||||
CnameTarget: b.getEnvOrDefault("CUSTOM_DOMAINS_CNAME_TARGET", "custom.getprobo.com"),
|
CnameTarget: b.resolver.getEnvOrDefault("PROBOD_CUSTOM_DOMAINS_CNAME_TARGET", "custom.getprobo.com"),
|
||||||
ResolverAddr: b.getEnvOrDefault("CUSTOM_DOMAINS_RESOLVER_ADDR", "8.8.8.8:53"),
|
ResolverAddr: b.resolver.getEnvOrDefault("PROBOD_CUSTOM_DOMAINS_RESOLVER_ADDR", "8.8.8.8:53"),
|
||||||
CAAIssuerDomain: b.getEnvOrDefault("CUSTOM_DOMAINS_CAA_ISSUER_DOMAIN", "letsencrypt.org"),
|
CAAIssuerDomain: b.resolver.getEnvOrDefault("PROBOD_CUSTOM_DOMAINS_CAA_ISSUER_DOMAIN", "letsencrypt.org"),
|
||||||
ACME: probodconfig.ACMEConfig{
|
ACME: probodconfig.ACMEConfig{
|
||||||
Directory: b.getEnvOrDefault("ACME_DIRECTORY", "https://acme-v02.api.letsencrypt.org/directory"),
|
Directory: b.resolver.getEnvOrDefault("PROBOD_ACME_DIRECTORY", "https://acme-v02.api.letsencrypt.org/directory"),
|
||||||
Email: b.getEnvOrDefault("ACME_EMAIL", "admin@probo.com"),
|
Email: b.resolver.getEnvOrDefault("PROBOD_ACME_EMAIL", "admin@probo.com"),
|
||||||
KeyType: b.getEnvOrDefault("ACME_KEY_TYPE", "EC256"),
|
KeyType: b.resolver.getEnvOrDefault("PROBOD_ACME_KEY_TYPE", "EC256"),
|
||||||
RootCA: b.getEnv("ACME_ROOT_CA"),
|
RootCA: b.resolver.getEnv("PROBOD_ACME_ROOT_CA"),
|
||||||
AccountKey: b.getEnv("ACME_ACCOUNT_KEY"),
|
AccountKey: b.resolver.getEnv("PROBOD_ACME_ACCOUNT_KEY"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
SCIMBridge: probodconfig.SCIMBridgeConfig{
|
SCIMBridge: probodconfig.SCIMBridgeConfig{
|
||||||
SyncInterval: b.getEnvIntOrDefault("SCIM_BRIDGE_SYNC_INTERVAL", 900),
|
SyncInterval: b.resolver.getEnvIntOrDefault("PROBOD_SCIM_BRIDGE_SYNC_INTERVAL", 900),
|
||||||
PollInterval: b.getEnvIntOrDefault("SCIM_BRIDGE_POLL_INTERVAL", 30),
|
PollInterval: b.resolver.getEnvIntOrDefault("PROBOD_SCIM_BRIDGE_POLL_INTERVAL", 30),
|
||||||
},
|
},
|
||||||
ESign: probodconfig.ESignConfig{
|
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{
|
EvidenceDescriber: probodconfig.EvidenceDescriberConfig{
|
||||||
Interval: b.getEnvIntOrDefault("EVIDENCE_DESCRIBER_INTERVAL", 10),
|
Interval: b.resolver.getEnvIntOrDefault("PROBOD_EVIDENCE_DESCRIBER_INTERVAL", 10),
|
||||||
StaleAfter: b.getEnvIntOrDefault("EVIDENCE_DESCRIBER_STALE_AFTER", 300),
|
StaleAfter: b.resolver.getEnvIntOrDefault("PROBOD_EVIDENCE_DESCRIBER_STALE_AFTER", 300),
|
||||||
MaxConcurrency: b.getEnvIntOrDefault("EVIDENCE_DESCRIBER_MAX_CONCURRENCY", 10),
|
MaxConcurrency: b.resolver.getEnvIntOrDefault("PROBOD_EVIDENCE_DESCRIBER_MAX_CONCURRENCY", 10),
|
||||||
},
|
},
|
||||||
ThirdPartyVetting: probodconfig.ThirdPartyVettingWorkerConfig{
|
ThirdPartyVetting: probodconfig.ThirdPartyVettingWorkerConfig{
|
||||||
Interval: b.getEnvIntOrDefault("THIRD_PARTY_VETTING_INTERVAL", 10),
|
Interval: b.resolver.getEnvIntOrDefault("PROBOD_THIRD_PARTY_VETTING_INTERVAL", 10),
|
||||||
StaleAfter: b.getEnvIntOrDefault("THIRD_PARTY_VETTING_STALE_AFTER", 1500),
|
StaleAfter: b.resolver.getEnvIntOrDefault("PROBOD_THIRD_PARTY_VETTING_STALE_AFTER", 1500),
|
||||||
MaxConcurrency: b.getEnvIntOrDefault("THIRD_PARTY_VETTING_MAX_CONCURRENCY", 1),
|
MaxConcurrency: b.resolver.getEnvIntOrDefault("PROBOD_THIRD_PARTY_VETTING_MAX_CONCURRENCY", 1),
|
||||||
},
|
},
|
||||||
TrackerMappingWorker: probodconfig.TrackerMappingWorkerConfig{
|
TrackerMappingWorker: probodconfig.TrackerMappingWorkerConfig{
|
||||||
Interval: b.getEnvIntOrDefault("TRACKER_MAPPING_INTERVAL", 10),
|
Interval: b.resolver.getEnvIntOrDefault("PROBOD_TRACKER_MAPPING_INTERVAL", 10),
|
||||||
MaxConcurrency: b.getEnvIntOrDefault("TRACKER_MAPPING_MAX_CONCURRENCY", 3),
|
MaxConcurrency: b.resolver.getEnvIntOrDefault("PROBOD_TRACKER_MAPPING_MAX_CONCURRENCY", 3),
|
||||||
StaleAfter: b.getEnvIntOrDefault("TRACKER_MAPPING_STALE_AFTER", 600),
|
StaleAfter: b.resolver.getEnvIntOrDefault("PROBOD_TRACKER_MAPPING_STALE_AFTER", 600),
|
||||||
AgentTimeout: b.getEnvIntOrDefault("TRACKER_MAPPING_AGENT_TIMEOUT", 45),
|
AgentTimeout: b.resolver.getEnvIntOrDefault("PROBOD_TRACKER_MAPPING_AGENT_TIMEOUT", 45),
|
||||||
AgentMaxTurns: b.getEnvIntOrDefault("TRACKER_MAPPING_AGENT_MAX_TURNS", 10),
|
AgentMaxTurns: b.resolver.getEnvIntOrDefault("PROBOD_TRACKER_MAPPING_AGENT_MAX_TURNS", 10),
|
||||||
DisambiguationAgentTimeout: b.getEnvIntOrDefault("TRACKER_MAPPING_DISAMBIGUATION_AGENT_TIMEOUT", 45),
|
DisambiguationAgentTimeout: b.resolver.getEnvIntOrDefault("PROBOD_TRACKER_MAPPING_DISAMBIGUATION_AGENT_TIMEOUT", 45),
|
||||||
},
|
},
|
||||||
CommonPatternEnrichmentWorker: probodconfig.CommonPatternEnrichmentWorkerConfig{
|
CommonPatternEnrichmentWorker: probodconfig.CommonPatternEnrichmentWorkerConfig{
|
||||||
Interval: b.getEnvIntOrDefault("COMMON_PATTERN_ENRICHMENT_INTERVAL", 10),
|
Interval: b.resolver.getEnvIntOrDefault("PROBOD_COMMON_PATTERN_ENRICHMENT_INTERVAL", 10),
|
||||||
MaxConcurrency: b.getEnvIntOrDefault("COMMON_PATTERN_ENRICHMENT_MAX_CONCURRENCY", 2),
|
MaxConcurrency: b.resolver.getEnvIntOrDefault("PROBOD_COMMON_PATTERN_ENRICHMENT_MAX_CONCURRENCY", 2),
|
||||||
StaleAfter: b.getEnvIntOrDefault("COMMON_PATTERN_ENRICHMENT_STALE_AFTER", 600),
|
StaleAfter: b.resolver.getEnvIntOrDefault("PROBOD_COMMON_PATTERN_ENRICHMENT_STALE_AFTER", 600),
|
||||||
AgentTimeout: b.getEnvIntOrDefault("COMMON_PATTERN_ENRICHMENT_AGENT_TIMEOUT", 45),
|
AgentTimeout: b.resolver.getEnvIntOrDefault("PROBOD_COMMON_PATTERN_ENRICHMENT_AGENT_TIMEOUT", 45),
|
||||||
AgentMaxTurns: b.getEnvIntOrDefault("COMMON_PATTERN_ENRICHMENT_AGENT_MAX_TURNS", 10),
|
AgentMaxTurns: b.resolver.getEnvIntOrDefault("PROBOD_COMMON_PATTERN_ENRICHMENT_AGENT_MAX_TURNS", 10),
|
||||||
},
|
},
|
||||||
CommonThirdPartyEnrichmentWorker: probodconfig.CommonThirdPartyEnrichmentWorkerConfig{
|
CommonThirdPartyEnrichmentWorker: probodconfig.CommonThirdPartyEnrichmentWorkerConfig{
|
||||||
Interval: b.getEnvIntOrDefault("COMMON_THIRD_PARTY_ENRICHMENT_INTERVAL", 10),
|
Interval: b.resolver.getEnvIntOrDefault("PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_INTERVAL", 10),
|
||||||
MaxConcurrency: b.getEnvIntOrDefault("COMMON_THIRD_PARTY_ENRICHMENT_MAX_CONCURRENCY", 1),
|
MaxConcurrency: b.resolver.getEnvIntOrDefault("PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_MAX_CONCURRENCY", 1),
|
||||||
StaleAfter: b.getEnvIntOrDefault("COMMON_THIRD_PARTY_ENRICHMENT_STALE_AFTER", 900),
|
StaleAfter: b.resolver.getEnvIntOrDefault("PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_STALE_AFTER", 900),
|
||||||
AgentTimeout: b.getEnvIntOrDefault("COMMON_THIRD_PARTY_ENRICHMENT_AGENT_TIMEOUT", 90),
|
AgentTimeout: b.resolver.getEnvIntOrDefault("PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_AGENT_TIMEOUT", 90),
|
||||||
AgentMaxTurns: b.getEnvIntOrDefault("COMMON_THIRD_PARTY_ENRICHMENT_AGENT_MAX_TURNS", 12),
|
AgentMaxTurns: b.resolver.getEnvIntOrDefault("PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_AGENT_MAX_TURNS", 12),
|
||||||
ConfidenceThreshold: b.getEnvFloatOrDefault("COMMON_THIRD_PARTY_ENRICHMENT_CONFIDENCE_THRESHOLD", 0.7),
|
ConfidenceThreshold: b.resolver.getEnvFloatOrDefault("PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_CONFIDENCE_THRESHOLD", 0.7),
|
||||||
MaxAttempts: b.getEnvIntOrDefault("COMMON_THIRD_PARTY_ENRICHMENT_MAX_ATTEMPTS", 3),
|
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 = append(
|
||||||
cfg.Probod.Connectors,
|
cfg.Probod.Connectors,
|
||||||
probodconfig.ConnectorConfig{
|
probodconfig.ConnectorConfig{
|
||||||
@@ -330,16 +329,16 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
|
|||||||
Protocol: "oauth2",
|
Protocol: "oauth2",
|
||||||
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
||||||
ClientID: slackClientID,
|
ClientID: slackClientID,
|
||||||
ClientSecret: b.getEnv("CONNECTOR_SLACK_CLIENT_SECRET"),
|
ClientSecret: b.resolver.getEnv("PROBOD_CONNECTOR_SLACK_CLIENT_SECRET"),
|
||||||
},
|
},
|
||||||
RawSettings: map[string]any{
|
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 = append(
|
||||||
cfg.Probod.Connectors,
|
cfg.Probod.Connectors,
|
||||||
probodconfig.ConnectorConfig{
|
probodconfig.ConnectorConfig{
|
||||||
@@ -347,13 +346,13 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
|
|||||||
Protocol: "oauth2",
|
Protocol: "oauth2",
|
||||||
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
||||||
ClientID: hubspotClientID,
|
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 = append(
|
||||||
cfg.Probod.Connectors,
|
cfg.Probod.Connectors,
|
||||||
probodconfig.ConnectorConfig{
|
probodconfig.ConnectorConfig{
|
||||||
@@ -361,13 +360,13 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
|
|||||||
Protocol: "oauth2",
|
Protocol: "oauth2",
|
||||||
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
||||||
ClientID: docusignClientID,
|
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 = append(
|
||||||
cfg.Probod.Connectors,
|
cfg.Probod.Connectors,
|
||||||
probodconfig.ConnectorConfig{
|
probodconfig.ConnectorConfig{
|
||||||
@@ -375,13 +374,13 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
|
|||||||
Protocol: "oauth2",
|
Protocol: "oauth2",
|
||||||
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
||||||
ClientID: notionClientID,
|
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 = append(
|
||||||
cfg.Probod.Connectors,
|
cfg.Probod.Connectors,
|
||||||
probodconfig.ConnectorConfig{
|
probodconfig.ConnectorConfig{
|
||||||
@@ -389,13 +388,13 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
|
|||||||
Protocol: "oauth2",
|
Protocol: "oauth2",
|
||||||
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
||||||
ClientID: githubClientID,
|
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 = append(
|
||||||
cfg.Probod.Connectors,
|
cfg.Probod.Connectors,
|
||||||
probodconfig.ConnectorConfig{
|
probodconfig.ConnectorConfig{
|
||||||
@@ -403,13 +402,13 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
|
|||||||
Protocol: "oauth2",
|
Protocol: "oauth2",
|
||||||
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
||||||
ClientID: sentryClientID,
|
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 = append(
|
||||||
cfg.Probod.Connectors,
|
cfg.Probod.Connectors,
|
||||||
probodconfig.ConnectorConfig{
|
probodconfig.ConnectorConfig{
|
||||||
@@ -417,13 +416,13 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
|
|||||||
Protocol: "oauth2",
|
Protocol: "oauth2",
|
||||||
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
||||||
ClientID: intercomClientID,
|
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 = append(
|
||||||
cfg.Probod.Connectors,
|
cfg.Probod.Connectors,
|
||||||
probodconfig.ConnectorConfig{
|
probodconfig.ConnectorConfig{
|
||||||
@@ -431,13 +430,13 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
|
|||||||
Protocol: "oauth2",
|
Protocol: "oauth2",
|
||||||
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
||||||
ClientID: brexClientID,
|
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 = append(
|
||||||
cfg.Probod.Connectors,
|
cfg.Probod.Connectors,
|
||||||
probodconfig.ConnectorConfig{
|
probodconfig.ConnectorConfig{
|
||||||
@@ -445,13 +444,13 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
|
|||||||
Protocol: "oauth2",
|
Protocol: "oauth2",
|
||||||
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
||||||
ClientID: googleWorkspaceClientID,
|
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 = append(
|
||||||
cfg.Probod.Connectors,
|
cfg.Probod.Connectors,
|
||||||
probodconfig.ConnectorConfig{
|
probodconfig.ConnectorConfig{
|
||||||
@@ -459,7 +458,7 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
|
|||||||
Protocol: "oauth2",
|
Protocol: "oauth2",
|
||||||
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
||||||
ClientID: microsoft365ClientID,
|
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",
|
"MONDAY",
|
||||||
"DATADOG",
|
"DATADOG",
|
||||||
"ZENDESK",
|
"ZENDESK",
|
||||||
|
"LINEAR",
|
||||||
} {
|
} {
|
||||||
clientID := b.getEnv("CONNECTOR_" + provider + "_CLIENT_ID")
|
clientID := b.resolver.getEnv("PROBOD_CONNECTOR_" + provider + "_CLIENT_ID")
|
||||||
if clientID == "" {
|
if clientID == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -489,7 +489,7 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
|
|||||||
Protocol: "oauth2",
|
Protocol: "oauth2",
|
||||||
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
||||||
ClientID: clientID,
|
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
|
// Vercel needs the operator-supplied integration slug to resolve the
|
||||||
// templated AuthURL ("https://vercel.com/integrations/{integration_slug}/new").
|
// 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 = append(
|
||||||
cfg.Probod.Connectors,
|
cfg.Probod.Connectors,
|
||||||
probodconfig.ConnectorConfig{
|
probodconfig.ConnectorConfig{
|
||||||
@@ -505,13 +505,17 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
|
|||||||
Protocol: "oauth2",
|
Protocol: "oauth2",
|
||||||
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
RawConfig: probodconfig.ConnectorConfigOAuth2{
|
||||||
ClientID: vercelClientID,
|
ClientID: vercelClientID,
|
||||||
ClientSecret: b.getEnv("CONNECTOR_VERCEL_CLIENT_SECRET"),
|
ClientSecret: b.resolver.getEnv("PROBOD_CONNECTOR_VERCEL_CLIENT_SECRET"),
|
||||||
IntegrationSlug: b.getEnv("CONNECTOR_VERCEL_INTEGRATION_SLUG"),
|
IntegrationSlug: b.resolver.getEnv("PROBOD_CONNECTOR_VERCEL_INTEGRATION_SLUG"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.resolver.Err() != nil {
|
||||||
|
return nil, b.resolver.Err()
|
||||||
|
}
|
||||||
|
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -520,28 +524,28 @@ func (b *Builder) validateRequired() error {
|
|||||||
|
|
||||||
required := []string{
|
required := []string{
|
||||||
"PROBOD_ENCRYPTION_KEY",
|
"PROBOD_ENCRYPTION_KEY",
|
||||||
"AUTH_COOKIE_SECRET",
|
"PROBOD_AUTH_COOKIE_SECRET",
|
||||||
"AUTH_PASSWORD_PEPPER",
|
"PROBOD_AUTH_PASSWORD_PEPPER",
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, key := range required {
|
for _, key := range required {
|
||||||
if b.getEnv(key) == "" {
|
if b.resolver.getEnv(key) == "" {
|
||||||
missing = append(missing, key)
|
missing = append(missing, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.oauth2SigningKey == "" && b.getEnv("OAUTH2_SERVER_SIGNING_KEY") == "" {
|
if b.oauth2SigningKey == "" && b.resolver.getEnv("PROBOD_OAUTH2_SERVER_SIGNING_KEY") == "" {
|
||||||
missing = append(missing, "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{
|
slackRequired := []string{
|
||||||
"CONNECTOR_SLACK_CLIENT_SECRET",
|
"PROBOD_CONNECTOR_SLACK_CLIENT_SECRET",
|
||||||
"CONNECTOR_SLACK_SIGNING_SECRET",
|
"PROBOD_CONNECTOR_SLACK_SIGNING_SECRET",
|
||||||
}
|
}
|
||||||
for _, key := range slackRequired {
|
for _, key := range slackRequired {
|
||||||
if b.getEnv(key) == "" {
|
if b.resolver.getEnv(key) == "" {
|
||||||
missing = append(missing, key+" (required when CONNECTOR_SLACK_CLIENT_ID is set)")
|
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_MONDAY", []string{"CLIENT_SECRET"}},
|
||||||
{"CONNECTOR_DATADOG", []string{"CLIENT_SECRET"}},
|
{"CONNECTOR_DATADOG", []string{"CLIENT_SECRET"}},
|
||||||
{"CONNECTOR_ZENDESK", []string{"CLIENT_SECRET"}},
|
{"CONNECTOR_ZENDESK", []string{"CLIENT_SECRET"}},
|
||||||
|
{"CONNECTOR_LINEAR", []string{"CLIENT_SECRET"}},
|
||||||
{"CONNECTOR_VERCEL", []string{"CLIENT_SECRET", "INTEGRATION_SLUG"}},
|
{"CONNECTOR_VERCEL", []string{"CLIENT_SECRET", "INTEGRATION_SLUG"}},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range oauthProviders {
|
for _, p := range oauthProviders {
|
||||||
clientIDKey := p.envPrefix + "_CLIENT_ID"
|
clientIDKey := "PROBOD_" + p.envPrefix + "_CLIENT_ID"
|
||||||
if b.getEnv(clientIDKey) != "" {
|
if b.resolver.getEnv(clientIDKey) != "" {
|
||||||
for _, suffix := range p.required {
|
for _, suffix := range p.required {
|
||||||
key := p.envPrefix + "_" + suffix
|
key := "PROBOD_" + p.envPrefix + "_" + suffix
|
||||||
if b.getEnv(key) == "" {
|
if b.resolver.getEnv(key) == "" {
|
||||||
missing = append(missing, key+" (required when "+clientIDKey+" is set)")
|
missing = append(missing, key+" (required when "+clientIDKey+" is set)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -585,6 +590,10 @@ func (b *Builder) validateRequired() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(missing) > 0 {
|
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 - "))
|
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
|
key = b.samlPrivateKey
|
||||||
|
|
||||||
if cert == "" {
|
if cert == "" {
|
||||||
cert = b.getEnv("SAML_CERTIFICATE")
|
cert = b.resolver.getEnv("PROBOD_SAML_CERTIFICATE")
|
||||||
}
|
}
|
||||||
|
|
||||||
if key == "" {
|
if key == "" {
|
||||||
key = b.getEnv("SAML_PRIVATE_KEY")
|
key = b.resolver.getEnv("PROBOD_SAML_PRIVATE_KEY")
|
||||||
}
|
}
|
||||||
|
|
||||||
if cert == "" || key == "" {
|
if cert == "" || key == "" {
|
||||||
@@ -618,77 +627,18 @@ func (b *Builder) getOAuth2SigningKey() string {
|
|||||||
return b.oauth2SigningKey
|
return b.oauth2SigningKey
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.getEnv("OAUTH2_SERVER_SIGNING_KEY")
|
return b.resolver.getEnv("PROBOD_OAUTH2_SERVER_SIGNING_KEY")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) getPgCACertBundle() string {
|
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)
|
data, err := os.ReadFile(path)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return string(data)
|
return string(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.getEnv("PG_CA_BUNDLE")
|
return b.resolver.getEnv("PROBOD_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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) parseOriginsList(s string) []string {
|
func (b *Builder) parseOriginsList(s string) []string {
|
||||||
|
|||||||
@@ -33,10 +33,10 @@ func mockEnv(env map[string]string) EnvGetter {
|
|||||||
|
|
||||||
func requiredEnv() map[string]string {
|
func requiredEnv() map[string]string {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"PROBOD_ENCRYPTION_KEY": "test-encryption-key-32-bytes-long",
|
"PROBOD_ENCRYPTION_KEY": "test-encryption-key-32-bytes-long",
|
||||||
"AUTH_COOKIE_SECRET": "test-cookie-secret-32-bytes-long!",
|
"PROBOD_AUTH_COOKIE_SECRET": "test-cookie-secret-32-bytes-long!",
|
||||||
"AUTH_PASSWORD_PEPPER": "test-password-pepper-32-bytes-lo",
|
"PROBOD_AUTH_PASSWORD_PEPPER": "test-password-pepper-32-bytes-lo",
|
||||||
"OAUTH2_SERVER_SIGNING_KEY": "test-oauth2-signing-key",
|
"PROBOD_OAUTH2_SERVER_SIGNING_KEY": "test-oauth2-signing-key",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,68 +49,68 @@ func TestBuilder_Build_MissingRequiredEnvVars(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "all missing",
|
name: "all missing",
|
||||||
env: map[string]string{},
|
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",
|
name: "missing oauth2 signing key",
|
||||||
env: map[string]string{
|
env: map[string]string{
|
||||||
"PROBOD_ENCRYPTION_KEY": "key",
|
"PROBOD_ENCRYPTION_KEY": "key",
|
||||||
"AUTH_COOKIE_SECRET": "secret",
|
"PROBOD_AUTH_COOKIE_SECRET": "secret",
|
||||||
"AUTH_PASSWORD_PEPPER": "pepper",
|
"PROBOD_AUTH_PASSWORD_PEPPER": "pepper",
|
||||||
},
|
},
|
||||||
wantMissing: []string{"OAUTH2_SERVER_SIGNING_KEY"},
|
wantMissing: []string{"PROBOD_OAUTH2_SERVER_SIGNING_KEY"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "missing encryption key",
|
name: "missing encryption key",
|
||||||
env: map[string]string{
|
env: map[string]string{
|
||||||
"AUTH_COOKIE_SECRET": "secret",
|
"PROBOD_AUTH_COOKIE_SECRET": "secret",
|
||||||
"AUTH_PASSWORD_PEPPER": "pepper",
|
"PROBOD_AUTH_PASSWORD_PEPPER": "pepper",
|
||||||
},
|
},
|
||||||
wantMissing: []string{"PROBOD_ENCRYPTION_KEY"},
|
wantMissing: []string{"PROBOD_ENCRYPTION_KEY"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "missing cookie secret",
|
name: "missing cookie secret",
|
||||||
env: map[string]string{
|
env: map[string]string{
|
||||||
"PROBOD_ENCRYPTION_KEY": "key",
|
"PROBOD_ENCRYPTION_KEY": "key",
|
||||||
"AUTH_PASSWORD_PEPPER": "pepper",
|
"PROBOD_AUTH_PASSWORD_PEPPER": "pepper",
|
||||||
},
|
},
|
||||||
wantMissing: []string{"AUTH_COOKIE_SECRET"},
|
wantMissing: []string{"PROBOD_AUTH_COOKIE_SECRET"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "slack connector missing required fields",
|
name: "slack connector missing required fields",
|
||||||
env: map[string]string{
|
env: map[string]string{
|
||||||
"PROBOD_ENCRYPTION_KEY": "key",
|
"PROBOD_ENCRYPTION_KEY": "key",
|
||||||
"AUTH_COOKIE_SECRET": "secret",
|
"PROBOD_AUTH_COOKIE_SECRET": "secret",
|
||||||
"AUTH_PASSWORD_PEPPER": "pepper",
|
"PROBOD_AUTH_PASSWORD_PEPPER": "pepper",
|
||||||
"CONNECTOR_SLACK_CLIENT_ID": "client-id",
|
"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",
|
name: "google workspace connector missing required fields",
|
||||||
env: map[string]string{
|
env: map[string]string{
|
||||||
"PROBOD_ENCRYPTION_KEY": "key",
|
"PROBOD_ENCRYPTION_KEY": "key",
|
||||||
"AUTH_COOKIE_SECRET": "secret",
|
"PROBOD_AUTH_COOKIE_SECRET": "secret",
|
||||||
"AUTH_PASSWORD_PEPPER": "pepper",
|
"PROBOD_AUTH_PASSWORD_PEPPER": "pepper",
|
||||||
"CONNECTOR_GOOGLE_WORKSPACE_CLIENT_ID": "client-id",
|
"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",
|
name: "microsoft 365 connector missing required fields",
|
||||||
env: map[string]string{
|
env: map[string]string{
|
||||||
"PROBOD_ENCRYPTION_KEY": "key",
|
"PROBOD_ENCRYPTION_KEY": "key",
|
||||||
"AUTH_COOKIE_SECRET": "secret",
|
"PROBOD_AUTH_COOKIE_SECRET": "secret",
|
||||||
"AUTH_PASSWORD_PEPPER": "pepper",
|
"PROBOD_AUTH_PASSWORD_PEPPER": "pepper",
|
||||||
"CONNECTOR_MICROSOFT_365_CLIENT_ID": "client-id",
|
"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 {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
b := NewBuilder(mockEnv(tt.env))
|
b := NewBuilder(NewResolver(mockEnv(tt.env)))
|
||||||
_, err := b.Build()
|
_, err := b.Build()
|
||||||
|
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
@@ -123,7 +123,7 @@ func TestBuilder_Build_MissingRequiredEnvVars(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestBuilder_Build_Defaults(t *testing.T) {
|
func TestBuilder_Build_Defaults(t *testing.T) {
|
||||||
b := NewBuilder(mockEnv(requiredEnv()))
|
b := NewBuilder(NewResolver(mockEnv(requiredEnv())))
|
||||||
b.samlCertificate = "test-cert"
|
b.samlCertificate = "test-cert"
|
||||||
b.samlPrivateKey = "test-key"
|
b.samlPrivateKey = "test-key"
|
||||||
|
|
||||||
@@ -285,129 +285,129 @@ func TestBuilder_Build_Defaults(t *testing.T) {
|
|||||||
func TestBuilder_Build_CustomValues(t *testing.T) {
|
func TestBuilder_Build_CustomValues(t *testing.T) {
|
||||||
env := requiredEnv()
|
env := requiredEnv()
|
||||||
// Unit
|
// Unit
|
||||||
env["METRICS_ADDR"] = "0.0.0.0:9090"
|
env["PROBOD_METRICS_ADDR"] = "0.0.0.0:9090"
|
||||||
env["TRACING_ADDR"] = "jaeger:4317"
|
env["PROBOD_TRACING_ADDR"] = "jaeger:4317"
|
||||||
env["TRACING_MAX_BATCH_SIZE"] = "1024"
|
env["PROBOD_TRACING_MAX_BATCH_SIZE"] = "1024"
|
||||||
// Probod
|
// Probod
|
||||||
env["PROBOD_BASE_URL"] = "https://app.example.com"
|
env["PROBOD_BASE_URL"] = "https://app.example.com"
|
||||||
env["CHROME_DP_ADDR"] = "chrome:9222"
|
env["PROBOD_CHROME_DP_ADDR"] = "chrome:9222"
|
||||||
// API
|
// API
|
||||||
env["API_ADDR"] = "0.0.0.0:8080"
|
env["PROBOD_API_ADDR"] = "0.0.0.0:8080"
|
||||||
env["API_CORS_ALLOWED_ORIGINS"] = "https://app.example.com,https://admin.example.com"
|
env["PROBOD_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_PROXY_PROTOCOL_TRUSTED_PROXIES"] = "10.0.0.1,10.0.0.2"
|
||||||
// PG
|
// PG
|
||||||
env["PG_ADDR"] = "postgres.example.com:5432"
|
env["PROBOD_PG_ADDR"] = "postgres.example.com:5432"
|
||||||
env["PG_USERNAME"] = "probo"
|
env["PROBOD_PG_USERNAME"] = "probo"
|
||||||
env["PG_PASSWORD"] = "secret123"
|
env["PROBOD_PG_PASSWORD"] = "secret123"
|
||||||
env["PG_DATABASE"] = "probo_prod"
|
env["PROBOD_PG_DATABASE"] = "probo_prod"
|
||||||
env["PG_POOL_SIZE"] = "200"
|
env["PROBOD_PG_POOL_SIZE"] = "200"
|
||||||
env["PG_MIN_POOL_SIZE"] = "25"
|
env["PROBOD_PG_MIN_POOL_SIZE"] = "25"
|
||||||
env["PG_MAX_CONN_IDLE_TIME_SECONDS"] = "900"
|
env["PROBOD_PG_MAX_CONN_IDLE_TIME_SECONDS"] = "900"
|
||||||
env["PG_MAX_CONN_LIFETIME_SECONDS"] = "7200"
|
env["PROBOD_PG_MAX_CONN_LIFETIME_SECONDS"] = "7200"
|
||||||
env["PG_MAX_CONN_LIFETIME_JITTER_SECONDS"] = "600"
|
env["PROBOD_PG_MAX_CONN_LIFETIME_JITTER_SECONDS"] = "600"
|
||||||
env["PG_HEALTH_CHECK_PERIOD_SECONDS"] = "30"
|
env["PROBOD_PG_HEALTH_CHECK_PERIOD_SECONDS"] = "30"
|
||||||
env["PG_DEBUG"] = "true"
|
env["PROBOD_PG_DEBUG"] = "true"
|
||||||
// Auth
|
// Auth
|
||||||
env["AUTH_DISABLE_SIGNUP"] = "true"
|
env["PROBOD_AUTH_DISABLE_SIGNUP"] = "true"
|
||||||
env["AUTH_INVITATION_TOKEN_VALIDITY"] = "7200"
|
env["PROBOD_AUTH_INVITATION_TOKEN_VALIDITY"] = "7200"
|
||||||
env["AUTH_PASSWORD_RESET_TOKEN_VALIDITY"] = "1800"
|
env["PROBOD_AUTH_PASSWORD_RESET_TOKEN_VALIDITY"] = "1800"
|
||||||
env["AUTH_MAGIC_LINK_TOKEN_VALIDITY"] = "600"
|
env["PROBOD_AUTH_MAGIC_LINK_TOKEN_VALIDITY"] = "600"
|
||||||
env["AUTH_COOKIE_DOMAIN"] = ".example.com"
|
env["PROBOD_AUTH_COOKIE_DOMAIN"] = ".example.com"
|
||||||
env["AUTH_COOKIE_DURATION"] = "48"
|
env["PROBOD_AUTH_COOKIE_DURATION"] = "48"
|
||||||
// SAML
|
// SAML
|
||||||
env["SAML_DOMAIN_VERIFICATION_INTERVAL_SECONDS"] = "120"
|
env["PROBOD_SAML_DOMAIN_VERIFICATION_INTERVAL_SECONDS"] = "120"
|
||||||
env["SAML_DOMAIN_VERIFICATION_RESOLVER_ADDR"] = "1.1.1.1:53"
|
env["PROBOD_SAML_DOMAIN_VERIFICATION_RESOLVER_ADDR"] = "1.1.1.1:53"
|
||||||
// Trust center
|
// Trust center
|
||||||
env["TRUST_CENTER_HTTP_ADDR"] = ":8080"
|
env["PROBOD_TRUST_CENTER_HTTP_ADDR"] = ":8080"
|
||||||
env["TRUST_CENTER_HTTPS_ADDR"] = ":8443"
|
env["PROBOD_TRUST_CENTER_HTTPS_ADDR"] = ":8443"
|
||||||
env["TRUST_CENTER_PROXY_PROTOCOL_TRUSTED_PROXIES"] = "10.0.1.1,10.0.1.2"
|
env["PROBOD_TRUST_CENTER_PROXY_PROTOCOL_TRUSTED_PROXIES"] = "10.0.1.1,10.0.1.2"
|
||||||
// AWS
|
// AWS
|
||||||
env["AWS_REGION"] = "eu-west-1"
|
env["PROBOD_AWS_REGION"] = "eu-west-1"
|
||||||
env["AWS_BUCKET"] = "probo-files"
|
env["PROBOD_AWS_BUCKET"] = "probo-files"
|
||||||
env["AWS_ACCESS_KEY_ID"] = "AKIAIOSFODNN7EXAMPLE"
|
env["PROBOD_AWS_ACCESS_KEY_ID"] = "AKIAIOSFODNN7EXAMPLE"
|
||||||
env["AWS_SECRET_ACCESS_KEY"] = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
|
env["PROBOD_AWS_SECRET_ACCESS_KEY"] = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
|
||||||
env["AWS_ENDPOINT"] = "https://s3.example.com"
|
env["PROBOD_AWS_ENDPOINT"] = "https://s3.example.com"
|
||||||
env["AWS_USE_PATH_STYLE"] = "true"
|
env["PROBOD_AWS_USE_PATH_STYLE"] = "true"
|
||||||
// Notifications
|
// Notifications
|
||||||
env["WEBHOOK_SENDER_INTERVAL"] = "10"
|
env["PROBOD_WEBHOOK_SENDER_INTERVAL"] = "10"
|
||||||
env["WEBHOOK_CACHE_TTL"] = "3600"
|
env["PROBOD_WEBHOOK_CACHE_TTL"] = "3600"
|
||||||
env["CONNECTOR_SLACK_SIGNING_SECRET"] = "slack-signing-secret"
|
env["PROBOD_CONNECTOR_SLACK_SIGNING_SECRET"] = "slack-signing-secret"
|
||||||
env["DOCUMENT_NOTIFICATION_INTERVAL"] = "120"
|
env["PROBOD_DOCUMENT_NOTIFICATION_INTERVAL"] = "120"
|
||||||
env["DOCUMENT_NOTIFICATION_DEBOUNCE_DELAY"] = "60"
|
env["PROBOD_DOCUMENT_NOTIFICATION_DEBOUNCE_DELAY"] = "60"
|
||||||
env["DOCUMENT_NOTIFICATION_REMINDER_INTERVAL"] = "43200"
|
env["PROBOD_DOCUMENT_NOTIFICATION_REMINDER_INTERVAL"] = "43200"
|
||||||
// Firecrawl
|
// Firecrawl
|
||||||
env["FIRECRAWL_API_KEY"] = "fc-test-key"
|
env["PROBOD_FIRECRAWL_API_KEY"] = "fc-test-key"
|
||||||
// Agents — providers
|
// Agents — providers
|
||||||
env["OPENAI_API_KEY"] = "sk-test-key"
|
env["PROBOD_OPENAI_API_KEY"] = "sk-test-key"
|
||||||
env["ANTHROPIC_API_KEY"] = "sk-ant-test-key"
|
env["PROBOD_ANTHROPIC_API_KEY"] = "sk-ant-test-key"
|
||||||
// Agents — default
|
// Agents — default
|
||||||
env["AGENT_DEFAULT_PROVIDER"] = "openai"
|
env["PROBOD_AGENT_DEFAULT_PROVIDER"] = "openai"
|
||||||
env["AGENT_DEFAULT_MODEL_NAME"] = "gpt-4-turbo"
|
env["PROBOD_AGENT_DEFAULT_MODEL_NAME"] = "gpt-4-turbo"
|
||||||
env["AGENT_DEFAULT_TEMPERATURE"] = "0.5"
|
env["PROBOD_AGENT_DEFAULT_TEMPERATURE"] = "0.5"
|
||||||
env["AGENT_DEFAULT_MAX_TOKENS"] = "8192"
|
env["PROBOD_AGENT_DEFAULT_MAX_TOKENS"] = "8192"
|
||||||
// Agents — evidence-describer override
|
// Agents — evidence-describer override
|
||||||
env["AGENT_EVIDENCE_DESCRIBER_PROVIDER"] = "anthropic"
|
env["PROBOD_AGENT_EVIDENCE_DESCRIBER_PROVIDER"] = "anthropic"
|
||||||
env["AGENT_EVIDENCE_DESCRIBER_MODEL_NAME"] = "claude-sonnet-4-20250514"
|
env["PROBOD_AGENT_EVIDENCE_DESCRIBER_MODEL_NAME"] = "claude-sonnet-4-20250514"
|
||||||
env["AGENT_EVIDENCE_DESCRIBER_TEMPERATURE"] = "0.2"
|
env["PROBOD_AGENT_EVIDENCE_DESCRIBER_TEMPERATURE"] = "0.2"
|
||||||
env["AGENT_EVIDENCE_DESCRIBER_MAX_TOKENS"] = "4096"
|
env["PROBOD_AGENT_EVIDENCE_DESCRIBER_MAX_TOKENS"] = "4096"
|
||||||
// Agents — third-party-vetter override
|
// Agents — third-party-vetter override
|
||||||
env["AGENT_THIRD_PARTY_VETTER_PROVIDER"] = "openai"
|
env["PROBOD_AGENT_THIRD_PARTY_VETTER_PROVIDER"] = "openai"
|
||||||
env["AGENT_THIRD_PARTY_VETTER_MODEL_NAME"] = "gpt-4o"
|
env["PROBOD_AGENT_THIRD_PARTY_VETTER_MODEL_NAME"] = "gpt-4o"
|
||||||
env["AGENT_THIRD_PARTY_VETTER_TEMPERATURE"] = "0.3"
|
env["PROBOD_AGENT_THIRD_PARTY_VETTER_TEMPERATURE"] = "0.3"
|
||||||
env["AGENT_THIRD_PARTY_VETTER_MAX_TOKENS"] = "8192"
|
env["PROBOD_AGENT_THIRD_PARTY_VETTER_MAX_TOKENS"] = "8192"
|
||||||
// Agents — third-party-disambiguation override
|
// Agents — third-party-disambiguation override
|
||||||
env["AGENT_THIRD_PARTY_DISAMBIGUATION_PROVIDER"] = "anthropic"
|
env["PROBOD_AGENT_THIRD_PARTY_DISAMBIGUATION_PROVIDER"] = "anthropic"
|
||||||
env["AGENT_THIRD_PARTY_DISAMBIGUATION_MODEL_NAME"] = "claude-sonnet-4-20250514"
|
env["PROBOD_AGENT_THIRD_PARTY_DISAMBIGUATION_MODEL_NAME"] = "claude-sonnet-4-20250514"
|
||||||
env["AGENT_THIRD_PARTY_DISAMBIGUATION_TEMPERATURE"] = "0.4"
|
env["PROBOD_AGENT_THIRD_PARTY_DISAMBIGUATION_TEMPERATURE"] = "0.4"
|
||||||
env["AGENT_THIRD_PARTY_DISAMBIGUATION_MAX_TOKENS"] = "2048"
|
env["PROBOD_AGENT_THIRD_PARTY_DISAMBIGUATION_MAX_TOKENS"] = "2048"
|
||||||
// Agents — tracker-mapping override
|
// Agents — tracker-mapping override
|
||||||
env["AGENT_TRACKER_MAPPING_PROVIDER"] = "openai"
|
env["PROBOD_AGENT_TRACKER_MAPPING_PROVIDER"] = "openai"
|
||||||
env["AGENT_TRACKER_MAPPING_MODEL_NAME"] = "gpt-4o-mini"
|
env["PROBOD_AGENT_TRACKER_MAPPING_MODEL_NAME"] = "gpt-4o-mini"
|
||||||
env["AGENT_TRACKER_MAPPING_TEMPERATURE"] = "0.1"
|
env["PROBOD_AGENT_TRACKER_MAPPING_TEMPERATURE"] = "0.1"
|
||||||
env["AGENT_TRACKER_MAPPING_MAX_TOKENS"] = "1024"
|
env["PROBOD_AGENT_TRACKER_MAPPING_MAX_TOKENS"] = "1024"
|
||||||
// Agents — tracker-enrichment override
|
// Agents — tracker-enrichment override
|
||||||
env["AGENT_TRACKER_ENRICHMENT_PROVIDER"] = "openai"
|
env["PROBOD_AGENT_TRACKER_ENRICHMENT_PROVIDER"] = "openai"
|
||||||
env["AGENT_TRACKER_ENRICHMENT_MODEL_NAME"] = "gpt-4o"
|
env["PROBOD_AGENT_TRACKER_ENRICHMENT_MODEL_NAME"] = "gpt-4o"
|
||||||
env["AGENT_TRACKER_ENRICHMENT_TEMPERATURE"] = "0.2"
|
env["PROBOD_AGENT_TRACKER_ENRICHMENT_TEMPERATURE"] = "0.2"
|
||||||
env["AGENT_TRACKER_ENRICHMENT_MAX_TOKENS"] = "2048"
|
env["PROBOD_AGENT_TRACKER_ENRICHMENT_MAX_TOKENS"] = "2048"
|
||||||
// Tracker worker tuning override
|
// Tracker worker tuning override
|
||||||
env["TRACKER_MAPPING_INTERVAL"] = "20"
|
env["PROBOD_TRACKER_MAPPING_INTERVAL"] = "20"
|
||||||
env["TRACKER_MAPPING_MAX_CONCURRENCY"] = "5"
|
env["PROBOD_TRACKER_MAPPING_MAX_CONCURRENCY"] = "5"
|
||||||
env["TRACKER_MAPPING_STALE_AFTER"] = "1200"
|
env["PROBOD_TRACKER_MAPPING_STALE_AFTER"] = "1200"
|
||||||
env["TRACKER_MAPPING_AGENT_TIMEOUT"] = "30"
|
env["PROBOD_TRACKER_MAPPING_AGENT_TIMEOUT"] = "30"
|
||||||
env["TRACKER_MAPPING_AGENT_MAX_TURNS"] = "6"
|
env["PROBOD_TRACKER_MAPPING_AGENT_MAX_TURNS"] = "6"
|
||||||
env["TRACKER_MAPPING_DISAMBIGUATION_AGENT_TIMEOUT"] = "35"
|
env["PROBOD_TRACKER_MAPPING_DISAMBIGUATION_AGENT_TIMEOUT"] = "35"
|
||||||
env["COMMON_PATTERN_ENRICHMENT_INTERVAL"] = "15"
|
env["PROBOD_COMMON_PATTERN_ENRICHMENT_INTERVAL"] = "15"
|
||||||
env["COMMON_PATTERN_ENRICHMENT_MAX_CONCURRENCY"] = "4"
|
env["PROBOD_COMMON_PATTERN_ENRICHMENT_MAX_CONCURRENCY"] = "4"
|
||||||
env["COMMON_PATTERN_ENRICHMENT_STALE_AFTER"] = "900"
|
env["PROBOD_COMMON_PATTERN_ENRICHMENT_STALE_AFTER"] = "900"
|
||||||
env["COMMON_PATTERN_ENRICHMENT_AGENT_TIMEOUT"] = "50"
|
env["PROBOD_COMMON_PATTERN_ENRICHMENT_AGENT_TIMEOUT"] = "50"
|
||||||
env["COMMON_PATTERN_ENRICHMENT_AGENT_MAX_TURNS"] = "5"
|
env["PROBOD_COMMON_PATTERN_ENRICHMENT_AGENT_MAX_TURNS"] = "5"
|
||||||
// Common third party enrichment agent + worker tuning override
|
// Common third party enrichment agent + worker tuning override
|
||||||
env["AGENT_COMMON_THIRD_PARTY_ENRICHMENT_PROVIDER"] = "openai"
|
env["PROBOD_AGENT_COMMON_THIRD_PARTY_ENRICHMENT_PROVIDER"] = "openai"
|
||||||
env["AGENT_COMMON_THIRD_PARTY_ENRICHMENT_MODEL_NAME"] = "gpt-4o"
|
env["PROBOD_AGENT_COMMON_THIRD_PARTY_ENRICHMENT_MODEL_NAME"] = "gpt-4o"
|
||||||
env["AGENT_COMMON_THIRD_PARTY_ENRICHMENT_MAX_TOKENS"] = "16384"
|
env["PROBOD_AGENT_COMMON_THIRD_PARTY_ENRICHMENT_MAX_TOKENS"] = "16384"
|
||||||
env["COMMON_THIRD_PARTY_ENRICHMENT_INTERVAL"] = "25"
|
env["PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_INTERVAL"] = "25"
|
||||||
env["COMMON_THIRD_PARTY_ENRICHMENT_MAX_CONCURRENCY"] = "2"
|
env["PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_MAX_CONCURRENCY"] = "2"
|
||||||
env["COMMON_THIRD_PARTY_ENRICHMENT_STALE_AFTER"] = "1200"
|
env["PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_STALE_AFTER"] = "1200"
|
||||||
env["COMMON_THIRD_PARTY_ENRICHMENT_AGENT_TIMEOUT"] = "120"
|
env["PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_AGENT_TIMEOUT"] = "120"
|
||||||
env["COMMON_THIRD_PARTY_ENRICHMENT_AGENT_MAX_TURNS"] = "8"
|
env["PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_AGENT_MAX_TURNS"] = "8"
|
||||||
env["COMMON_THIRD_PARTY_ENRICHMENT_CONFIDENCE_THRESHOLD"] = "0.85"
|
env["PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_CONFIDENCE_THRESHOLD"] = "0.85"
|
||||||
env["COMMON_THIRD_PARTY_ENRICHMENT_MAX_ATTEMPTS"] = "5"
|
env["PROBOD_COMMON_THIRD_PARTY_ENRICHMENT_MAX_ATTEMPTS"] = "5"
|
||||||
env["THIRD_PARTY_VETTING_INTERVAL"] = "15"
|
env["PROBOD_THIRD_PARTY_VETTING_INTERVAL"] = "15"
|
||||||
env["THIRD_PARTY_VETTING_STALE_AFTER"] = "1800"
|
env["PROBOD_THIRD_PARTY_VETTING_STALE_AFTER"] = "1800"
|
||||||
env["THIRD_PARTY_VETTING_MAX_CONCURRENCY"] = "2"
|
env["PROBOD_THIRD_PARTY_VETTING_MAX_CONCURRENCY"] = "2"
|
||||||
// Custom domains
|
// Custom domains
|
||||||
env["CUSTOM_DOMAINS_RESOLVER_ADDR"] = "1.1.1.1:53"
|
env["PROBOD_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_ACME_ACCOUNT_KEY"] = "-----BEGIN EC PRIVATE KEY-----\ntest\n-----END EC PRIVATE KEY-----"
|
||||||
// SCIM bridge
|
// SCIM bridge
|
||||||
env["SCIM_BRIDGE_SYNC_INTERVAL"] = "1800"
|
env["PROBOD_SCIM_BRIDGE_SYNC_INTERVAL"] = "1800"
|
||||||
env["SCIM_BRIDGE_POLL_INTERVAL"] = "60"
|
env["PROBOD_SCIM_BRIDGE_POLL_INTERVAL"] = "60"
|
||||||
// ESign
|
// ESign
|
||||||
env["ESIGN_TSA_URL"] = "http://custom.tsa.example.com"
|
env["PROBOD_ESIGN_TSA_URL"] = "http://custom.tsa.example.com"
|
||||||
// Branding
|
// Branding
|
||||||
env["BRANDING"] = "false"
|
env["PROBOD_BRANDING"] = "false"
|
||||||
|
|
||||||
b := NewBuilder(mockEnv(env))
|
b := NewBuilder(NewResolver(mockEnv(env)))
|
||||||
b.samlCertificate = "test-cert"
|
b.samlCertificate = "test-cert"
|
||||||
b.samlPrivateKey = "test-key"
|
b.samlPrivateKey = "test-key"
|
||||||
|
|
||||||
@@ -545,10 +545,10 @@ func TestBuilder_Build_CustomValues(t *testing.T) {
|
|||||||
|
|
||||||
func TestBuilder_Build_GoogleWorkspaceConnector(t *testing.T) {
|
func TestBuilder_Build_GoogleWorkspaceConnector(t *testing.T) {
|
||||||
env := requiredEnv()
|
env := requiredEnv()
|
||||||
env["CONNECTOR_GOOGLE_WORKSPACE_CLIENT_ID"] = "gw-client-id"
|
env["PROBOD_CONNECTOR_GOOGLE_WORKSPACE_CLIENT_ID"] = "gw-client-id"
|
||||||
env["CONNECTOR_GOOGLE_WORKSPACE_CLIENT_SECRET"] = "gw-client-secret"
|
env["PROBOD_CONNECTOR_GOOGLE_WORKSPACE_CLIENT_SECRET"] = "gw-client-secret"
|
||||||
|
|
||||||
b := NewBuilder(mockEnv(env))
|
b := NewBuilder(NewResolver(mockEnv(env)))
|
||||||
b.samlCertificate = "test-cert"
|
b.samlCertificate = "test-cert"
|
||||||
b.samlPrivateKey = "test-key"
|
b.samlPrivateKey = "test-key"
|
||||||
|
|
||||||
@@ -566,10 +566,10 @@ func TestBuilder_Build_GoogleWorkspaceConnector(t *testing.T) {
|
|||||||
|
|
||||||
func TestBuilder_Build_Microsoft365Connector(t *testing.T) {
|
func TestBuilder_Build_Microsoft365Connector(t *testing.T) {
|
||||||
env := requiredEnv()
|
env := requiredEnv()
|
||||||
env["CONNECTOR_MICROSOFT_365_CLIENT_ID"] = "ms365-client-id"
|
env["PROBOD_CONNECTOR_MICROSOFT_365_CLIENT_ID"] = "ms365-client-id"
|
||||||
env["CONNECTOR_MICROSOFT_365_CLIENT_SECRET"] = "ms365-client-secret"
|
env["PROBOD_CONNECTOR_MICROSOFT_365_CLIENT_SECRET"] = "ms365-client-secret"
|
||||||
|
|
||||||
b := NewBuilder(mockEnv(env))
|
b := NewBuilder(NewResolver(mockEnv(env)))
|
||||||
b.samlCertificate = "test-cert"
|
b.samlCertificate = "test-cert"
|
||||||
b.samlPrivateKey = "test-key"
|
b.samlPrivateKey = "test-key"
|
||||||
|
|
||||||
@@ -592,16 +592,16 @@ func TestBuilder_Build_AccessReviewConnectors(t *testing.T) {
|
|||||||
providers := []string{
|
providers := []string{
|
||||||
"GITLAB", "BITBUCKET", "HEROKU", "PAGERDUTY",
|
"GITLAB", "BITBUCKET", "HEROKU", "PAGERDUTY",
|
||||||
"ASANA", "NETLIFY", "CLICKUP", "MONDAY", "DATADOG",
|
"ASANA", "NETLIFY", "CLICKUP", "MONDAY", "DATADOG",
|
||||||
"ZENDESK",
|
"ZENDESK", "LINEAR",
|
||||||
}
|
}
|
||||||
|
|
||||||
env := requiredEnv()
|
env := requiredEnv()
|
||||||
for _, provider := range providers {
|
for _, provider := range providers {
|
||||||
env["CONNECTOR_"+provider+"_CLIENT_ID"] = strings.ToLower(provider) + "-id"
|
env["PROBOD_CONNECTOR_"+provider+"_CLIENT_ID"] = strings.ToLower(provider) + "-id"
|
||||||
env["CONNECTOR_"+provider+"_CLIENT_SECRET"] = strings.ToLower(provider) + "-secret"
|
env["PROBOD_CONNECTOR_"+provider+"_CLIENT_SECRET"] = strings.ToLower(provider) + "-secret"
|
||||||
}
|
}
|
||||||
|
|
||||||
b := NewBuilder(mockEnv(env))
|
b := NewBuilder(NewResolver(mockEnv(env)))
|
||||||
b.samlCertificate = "test-cert"
|
b.samlCertificate = "test-cert"
|
||||||
b.samlPrivateKey = "test-key"
|
b.samlPrivateKey = "test-key"
|
||||||
|
|
||||||
@@ -628,11 +628,11 @@ func TestBuilder_Build_AccessReviewConnectors(t *testing.T) {
|
|||||||
|
|
||||||
func TestBuilder_Build_VercelConnector(t *testing.T) {
|
func TestBuilder_Build_VercelConnector(t *testing.T) {
|
||||||
env := requiredEnv()
|
env := requiredEnv()
|
||||||
env["CONNECTOR_VERCEL_CLIENT_ID"] = "vercel-id"
|
env["PROBOD_CONNECTOR_VERCEL_CLIENT_ID"] = "vercel-id"
|
||||||
env["CONNECTOR_VERCEL_CLIENT_SECRET"] = "vercel-secret"
|
env["PROBOD_CONNECTOR_VERCEL_CLIENT_SECRET"] = "vercel-secret"
|
||||||
env["CONNECTOR_VERCEL_INTEGRATION_SLUG"] = "probo-app"
|
env["PROBOD_CONNECTOR_VERCEL_INTEGRATION_SLUG"] = "probo-app"
|
||||||
|
|
||||||
b := NewBuilder(mockEnv(env))
|
b := NewBuilder(NewResolver(mockEnv(env)))
|
||||||
b.samlCertificate = "test-cert"
|
b.samlCertificate = "test-cert"
|
||||||
b.samlPrivateKey = "test-key"
|
b.samlPrivateKey = "test-key"
|
||||||
|
|
||||||
@@ -651,11 +651,11 @@ func TestBuilder_Build_VercelConnector(t *testing.T) {
|
|||||||
|
|
||||||
func TestBuilder_Build_SlackConnector(t *testing.T) {
|
func TestBuilder_Build_SlackConnector(t *testing.T) {
|
||||||
env := requiredEnv()
|
env := requiredEnv()
|
||||||
env["CONNECTOR_SLACK_CLIENT_ID"] = "slack-client-id"
|
env["PROBOD_CONNECTOR_SLACK_CLIENT_ID"] = "slack-client-id"
|
||||||
env["CONNECTOR_SLACK_CLIENT_SECRET"] = "slack-client-secret"
|
env["PROBOD_CONNECTOR_SLACK_CLIENT_SECRET"] = "slack-client-secret"
|
||||||
env["CONNECTOR_SLACK_SIGNING_SECRET"] = "slack-signing-secret"
|
env["PROBOD_CONNECTOR_SLACK_SIGNING_SECRET"] = "slack-signing-secret"
|
||||||
|
|
||||||
b := NewBuilder(mockEnv(env))
|
b := NewBuilder(NewResolver(mockEnv(env)))
|
||||||
b.samlCertificate = "test-cert"
|
b.samlCertificate = "test-cert"
|
||||||
b.samlPrivateKey = "test-key"
|
b.samlPrivateKey = "test-key"
|
||||||
|
|
||||||
@@ -675,7 +675,7 @@ func TestBuilder_Build_SlackConnector(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestBuilder_Build_SAMLAutoGeneration(t *testing.T) {
|
func TestBuilder_Build_SAMLAutoGeneration(t *testing.T) {
|
||||||
b := NewBuilder(mockEnv(requiredEnv()))
|
b := NewBuilder(NewResolver(mockEnv(requiredEnv())))
|
||||||
|
|
||||||
cfg, err := b.Build()
|
cfg, err := b.Build()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -688,10 +688,10 @@ func TestBuilder_Build_SAMLAutoGeneration(t *testing.T) {
|
|||||||
|
|
||||||
func TestBuilder_Build_SAMLFromEnv(t *testing.T) {
|
func TestBuilder_Build_SAMLFromEnv(t *testing.T) {
|
||||||
env := requiredEnv()
|
env := requiredEnv()
|
||||||
env["SAML_CERTIFICATE"] = "env-cert"
|
env["PROBOD_SAML_CERTIFICATE"] = "env-cert"
|
||||||
env["SAML_PRIVATE_KEY"] = "env-key"
|
env["PROBOD_SAML_PRIVATE_KEY"] = "env-key"
|
||||||
|
|
||||||
b := NewBuilder(mockEnv(env))
|
b := NewBuilder(NewResolver(mockEnv(env)))
|
||||||
|
|
||||||
cfg, err := b.Build()
|
cfg, err := b.Build()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -701,7 +701,7 @@ func TestBuilder_Build_SAMLFromEnv(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestBuilder_Build_SAMLPreset(t *testing.T) {
|
func TestBuilder_Build_SAMLPreset(t *testing.T) {
|
||||||
b := NewBuilder(mockEnv(requiredEnv()))
|
b := NewBuilder(NewResolver(mockEnv(requiredEnv())))
|
||||||
b.samlCertificate = "preset-cert"
|
b.samlCertificate = "preset-cert"
|
||||||
b.samlPrivateKey = "preset-key"
|
b.samlPrivateKey = "preset-key"
|
||||||
|
|
||||||
@@ -713,7 +713,7 @@ func TestBuilder_Build_SAMLPreset(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestBuilder_Build_OAuth2Defaults(t *testing.T) {
|
func TestBuilder_Build_OAuth2Defaults(t *testing.T) {
|
||||||
b := NewBuilder(mockEnv(requiredEnv()))
|
b := NewBuilder(NewResolver(mockEnv(requiredEnv())))
|
||||||
|
|
||||||
cfg, err := b.Build()
|
cfg, err := b.Build()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -733,15 +733,15 @@ func TestBuilder_Build_OAuth2Defaults(t *testing.T) {
|
|||||||
|
|
||||||
func TestBuilder_Build_OAuth2FromEnv(t *testing.T) {
|
func TestBuilder_Build_OAuth2FromEnv(t *testing.T) {
|
||||||
env := requiredEnv()
|
env := requiredEnv()
|
||||||
env["OAUTH2_SERVER_SIGNING_KEY"] = "env-signing-key"
|
env["PROBOD_OAUTH2_SERVER_SIGNING_KEY"] = "env-signing-key"
|
||||||
env["OAUTH2_SERVER_SIGNING_KEY_KID"] = "env-kid"
|
env["PROBOD_OAUTH2_SERVER_SIGNING_KEY_KID"] = "env-kid"
|
||||||
env["OAUTH2_SERVER_ACCESS_TOKEN_DURATION"] = "10"
|
env["PROBOD_OAUTH2_SERVER_ACCESS_TOKEN_DURATION"] = "10"
|
||||||
env["OAUTH2_SERVER_REFRESH_TOKEN_DURATION"] = "20"
|
env["PROBOD_OAUTH2_SERVER_REFRESH_TOKEN_DURATION"] = "20"
|
||||||
env["OAUTH2_SERVER_AUTHORIZATION_CODE_DURATION"] = "30"
|
env["PROBOD_OAUTH2_SERVER_AUTHORIZATION_CODE_DURATION"] = "30"
|
||||||
env["OAUTH2_SERVER_DEVICE_CODE_DURATION"] = "40"
|
env["PROBOD_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_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()
|
cfg, err := b.Build()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -770,7 +770,7 @@ func TestBuilder_Build_OAuth2Preset(t *testing.T) {
|
|||||||
env := requiredEnv()
|
env := requiredEnv()
|
||||||
delete(env, "OAUTH2_SERVER_SIGNING_KEY")
|
delete(env, "OAUTH2_SERVER_SIGNING_KEY")
|
||||||
|
|
||||||
b := NewBuilder(mockEnv(env))
|
b := NewBuilder(NewResolver(mockEnv(env)))
|
||||||
b.oauth2SigningKey = "preset-signing-key"
|
b.oauth2SigningKey = "preset-signing-key"
|
||||||
|
|
||||||
cfg, err := b.Build()
|
cfg, err := b.Build()
|
||||||
@@ -782,9 +782,9 @@ func TestBuilder_Build_OAuth2Preset(t *testing.T) {
|
|||||||
|
|
||||||
func TestBuilder_Build_PgCABundleFromEnv(t *testing.T) {
|
func TestBuilder_Build_PgCABundleFromEnv(t *testing.T) {
|
||||||
env := requiredEnv()
|
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.samlCertificate = "test-cert"
|
||||||
b.samlPrivateKey = "test-key"
|
b.samlPrivateKey = "test-key"
|
||||||
|
|
||||||
@@ -801,9 +801,9 @@ func TestBuilder_Build_PgCABundleFromFile(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
env := requiredEnv()
|
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.samlCertificate = "test-cert"
|
||||||
b.samlPrivateKey = "test-key"
|
b.samlPrivateKey = "test-key"
|
||||||
|
|
||||||
|
|||||||
225
pkg/bootstrap/resolver.go
Normal file
225
pkg/bootstrap/resolver.go
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
package bootstrap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go-v2/aws"
|
||||||
|
"github.com/aws/aws-sdk-go-v2/config"
|
||||||
|
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
|
||||||
|
)
|
||||||
|
|
||||||
|
const awsSecretRefPrefix = "aws://"
|
||||||
|
|
||||||
|
type (
|
||||||
|
SecretsManagerClient interface {
|
||||||
|
GetSecretValue(
|
||||||
|
ctx context.Context,
|
||||||
|
params *secretsmanager.GetSecretValueInput,
|
||||||
|
optFns ...func(*secretsmanager.Options),
|
||||||
|
) (*secretsmanager.GetSecretValueOutput, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
Resolver struct {
|
||||||
|
lookup EnvGetter
|
||||||
|
secretsManagerClient SecretsManagerClient
|
||||||
|
smCache map[string]string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewResolver(lookup EnvGetter) *Resolver {
|
||||||
|
if lookup == nil {
|
||||||
|
lookup = os.Getenv
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Resolver{lookup: lookup}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) Err() error {
|
||||||
|
return r.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) getEnv(key string) string {
|
||||||
|
if r.err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := r.resolve(key)
|
||||||
|
if err != nil {
|
||||||
|
r.err = err
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) getEnvOrDefault(key, defaultValue string) string {
|
||||||
|
if value := r.getEnv(key); value != "" {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) getEnvIntOrDefault(key string, defaultValue int) int {
|
||||||
|
if value := r.getEnv(key); value != "" {
|
||||||
|
if intValue, err := strconv.ParseInt(value, 10, 32); err == nil {
|
||||||
|
return int(intValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) getEnvFloatOrDefault(key string, defaultValue float64) float64 {
|
||||||
|
if value := r.getEnv(key); value != "" {
|
||||||
|
if floatValue, err := strconv.ParseFloat(value, 64); err == nil {
|
||||||
|
return floatValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) getEnvFloatPtr(key string) *float64 {
|
||||||
|
if value := r.getEnv(key); value != "" {
|
||||||
|
if floatValue, err := strconv.ParseFloat(value, 64); err == nil {
|
||||||
|
return &floatValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) getEnvIntPtr(key string) *int {
|
||||||
|
if value := r.getEnv(key); value != "" {
|
||||||
|
if intValue, err := strconv.ParseInt(value, 10, 32); err == nil {
|
||||||
|
v := int(intValue)
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) getEnvBoolOrDefault(key string, defaultValue bool) bool {
|
||||||
|
if value := r.getEnv(key); value != "" {
|
||||||
|
if boolValue, err := strconv.ParseBool(value); err == nil {
|
||||||
|
return boolValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) resolve(key string) (string, error) {
|
||||||
|
raw := r.lookup(key)
|
||||||
|
|
||||||
|
secretID, ok := parseAWSSecretRef(raw)
|
||||||
|
if !ok {
|
||||||
|
return raw, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := r.loadPlaintextSecret(secretID)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("cannot resolve %s: %w", key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) loadPlaintextSecret(secretID string) (string, error) {
|
||||||
|
if r.smCache != nil {
|
||||||
|
if value, ok := r.smCache[secretID]; ok {
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := fetchPlaintextSecret(
|
||||||
|
context.Background(),
|
||||||
|
secretsManagerOptions{
|
||||||
|
SecretID: secretID,
|
||||||
|
Client: r.secretsManagerClient,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.smCache == nil {
|
||||||
|
r.smCache = make(map[string]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.smCache[secretID] = value
|
||||||
|
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseAWSSecretRef(value string) (string, bool) {
|
||||||
|
if !strings.HasPrefix(value, awsSecretRefPrefix) {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
secretID := strings.TrimPrefix(value, awsSecretRefPrefix)
|
||||||
|
if secretID == "" {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
return secretID, true
|
||||||
|
}
|
||||||
|
|
||||||
|
type secretsManagerOptions struct {
|
||||||
|
SecretID string
|
||||||
|
Client SecretsManagerClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchPlaintextSecret(ctx context.Context, opts secretsManagerOptions) (string, error) {
|
||||||
|
client, err := secretsManagerClient(ctx, opts)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := client.GetSecretValue(ctx, &secretsmanager.GetSecretValueInput{
|
||||||
|
SecretId: aws.String(opts.SecretID),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("cannot load secret from AWS Secrets Manager: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if out.SecretString == nil || *out.SecretString == "" {
|
||||||
|
return "", fmt.Errorf("secret %q has an empty SecretString", opts.SecretID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return *out.SecretString, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func secretsManagerClient(ctx context.Context, opts secretsManagerOptions) (SecretsManagerClient, error) {
|
||||||
|
if opts.Client != nil {
|
||||||
|
return opts.Client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := config.LoadDefaultConfig(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot load AWS config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return secretsmanager.NewFromConfig(cfg), nil
|
||||||
|
}
|
||||||
179
pkg/bootstrap/resolver_test.go
Normal file
179
pkg/bootstrap/resolver_test.go
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
package bootstrap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go-v2/aws"
|
||||||
|
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mockSecretsManagerClient struct {
|
||||||
|
secrets map[string]string
|
||||||
|
called []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockSecretsManagerClient) GetSecretValue(
|
||||||
|
_ context.Context,
|
||||||
|
params *secretsmanager.GetSecretValueInput,
|
||||||
|
_ ...func(*secretsmanager.Options),
|
||||||
|
) (*secretsmanager.GetSecretValueOutput, error) {
|
||||||
|
secretID := aws.ToString(params.SecretId)
|
||||||
|
m.called = append(m.called, secretID)
|
||||||
|
|
||||||
|
value, ok := m.secrets[secretID]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("secret %q not found", secretID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &secretsmanager.GetSecretValueOutput{
|
||||||
|
SecretString: aws.String(value),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testResolver(env map[string]string, client SecretsManagerClient) *Resolver {
|
||||||
|
r := NewResolver(mockEnv(env))
|
||||||
|
r.secretsManagerClient = client
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolver_ResolveLiteralEnv(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
r := NewResolver(mockEnv(map[string]string{"PROBOD_FOO": "bar"}))
|
||||||
|
assert.Equal(t, "bar", r.getEnv("PROBOD_FOO"))
|
||||||
|
require.NoError(t, r.Err())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuilder_Build_ResolvesAWSSecretRefs(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
client := &mockSecretsManagerClient{
|
||||||
|
secrets: map[string]string{
|
||||||
|
"probo/sandbox/probod/encryption_key": "test-encryption-key-32-bytes-long",
|
||||||
|
"probo/sandbox/probod/cookie_secret": "test-cookie-secret-32-bytes-long!",
|
||||||
|
"probo/sandbox/probod/password_pepper": "test-password-pepper-32-bytes-lo",
|
||||||
|
"probo/sandbox/probod/oauth2_signing_key": "test-oauth2-signing-key",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
env := map[string]string{
|
||||||
|
"PROBOD_ENCRYPTION_KEY": "aws://probo/sandbox/probod/encryption_key",
|
||||||
|
"PROBOD_AUTH_COOKIE_SECRET": "aws://probo/sandbox/probod/cookie_secret",
|
||||||
|
"PROBOD_AUTH_PASSWORD_PEPPER": "aws://probo/sandbox/probod/password_pepper",
|
||||||
|
"PROBOD_OAUTH2_SERVER_SIGNING_KEY": "aws://probo/sandbox/probod/oauth2_signing_key",
|
||||||
|
"PROBOD_BASE_URL": "https://app.example.com",
|
||||||
|
}
|
||||||
|
b := NewBuilder(testResolver(env, client))
|
||||||
|
|
||||||
|
cfg, err := b.Build()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, client.called, 4)
|
||||||
|
|
||||||
|
assert.Equal(t, "test-encryption-key-32-bytes-long", cfg.Probod.EncryptionKey)
|
||||||
|
assert.Equal(t, "https://app.example.com", cfg.Probod.BaseURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolver_ResolveAWSSecretRefsCachesBySecretID(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
client := &mockSecretsManagerClient{
|
||||||
|
secrets: map[string]string{
|
||||||
|
"probo/sandbox/probod/encryption_key": "secret-value",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
r := testResolver(map[string]string{
|
||||||
|
"PROBOD_ENCRYPTION_KEY": "aws://probo/sandbox/probod/encryption_key",
|
||||||
|
"PROBOD_AUTH_COOKIE_SECRET": "aws://probo/sandbox/probod/encryption_key",
|
||||||
|
"PROBOD_AUTH_PASSWORD_PEPPER": "plaintext-pepper",
|
||||||
|
"PROBOD_OAUTH2_SERVER_SIGNING_KEY": "plaintext-oauth2",
|
||||||
|
}, client)
|
||||||
|
|
||||||
|
assert.Equal(t, "secret-value", r.getEnv("PROBOD_ENCRYPTION_KEY"))
|
||||||
|
assert.Equal(t, "secret-value", r.getEnv("PROBOD_AUTH_COOKIE_SECRET"))
|
||||||
|
require.NoError(t, r.Err())
|
||||||
|
require.Len(t, client.called, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuilder_Build_MixedPlaintextAndAWSSecrets(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
client := &mockSecretsManagerClient{
|
||||||
|
secrets: map[string]string{
|
||||||
|
"probo/sandbox/probod/encryption_key": "test-encryption-key-32-bytes-long",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
b := NewBuilder(testResolver(map[string]string{
|
||||||
|
"PROBOD_ENCRYPTION_KEY": "aws://probo/sandbox/probod/encryption_key",
|
||||||
|
"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",
|
||||||
|
}, client))
|
||||||
|
|
||||||
|
cfg, err := b.Build()
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "test-encryption-key-32-bytes-long", cfg.Probod.EncryptionKey)
|
||||||
|
assert.Equal(t, "test-cookie-secret-32-bytes-long!", cfg.Probod.Auth.Cookie.Secret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolver_ResolveAWSSecretRefMissingSecret(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
client := &mockSecretsManagerClient{secrets: map[string]string{}}
|
||||||
|
r := testResolver(map[string]string{
|
||||||
|
"PROBOD_ENCRYPTION_KEY": "aws://probo/sandbox/probod/encryption_key",
|
||||||
|
}, client)
|
||||||
|
|
||||||
|
assert.Empty(t, r.getEnv("PROBOD_ENCRYPTION_KEY"))
|
||||||
|
require.Error(t, r.Err())
|
||||||
|
assert.Contains(t, r.Err().Error(), "cannot resolve PROBOD_ENCRYPTION_KEY")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolver_ResolveAWSSecretRefEmptySecretString(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
client := &mockSecretsManagerClient{
|
||||||
|
secrets: map[string]string{
|
||||||
|
"probo/sandbox/probod/encryption_key": "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
r := testResolver(map[string]string{
|
||||||
|
"PROBOD_ENCRYPTION_KEY": "aws://probo/sandbox/probod/encryption_key",
|
||||||
|
}, client)
|
||||||
|
|
||||||
|
assert.Empty(t, r.getEnv("PROBOD_ENCRYPTION_KEY"))
|
||||||
|
require.Error(t, r.Err())
|
||||||
|
assert.Contains(t, r.Err().Error(), "empty SecretString")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseAWSSecretRef(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
secretID, ok := parseAWSSecretRef("aws://probo/sandbox/probod/encryption_key")
|
||||||
|
require.True(t, ok)
|
||||||
|
assert.Equal(t, "probo/sandbox/probod/encryption_key", secretID)
|
||||||
|
|
||||||
|
_, ok = parseAWSSecretRef("literal-value")
|
||||||
|
assert.False(t, ok)
|
||||||
|
|
||||||
|
_, ok = parseAWSSecretRef("aws://")
|
||||||
|
assert.False(t, ok)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user