Aller au contenu

Configuration reference

Ce contenu n’est pas encore disponible dans votre langue.

SpeculaPro is configured entirely through environment variables. There is no configuration file. Every variable starts with SP_.

How you set them depends on how you run SpeculaPro:

You run SpeculaPro with Put settings in
make run (development) .env in the repository root. The Makefile loads it automatically
systemd Environment= lines, or an EnvironmentFile= with restricted permissions
Docker / Compose environment: or env_file:
Kubernetes A ConfigMap for ordinary settings and a Secret for passwords and keys
Type Format Examples
Duration A number followed by a unit: s, m, h. Units can be combined 30s, 15m, 12h, 1h30m
List Comma-separated, no spaces broker1:9092,broker2:9092
Boolean true or false true
Size A whole number of bytes 1048576 (1 MiB)

An invalid value stops SpeculaPro at start-up with a config: error naming the variable.

Variable Default Meaning
SP_ENV development The environment name. Anything other than development switches on production safeguards: encryption keys become mandatory, the interactive API docs are switched off by default, and browsers are told to use HTTPS only (HSTS). Use production for live systems and, for example, staging for test systems
SP_ROLES all Which roles speculapro run starts, as a list. The --role flag overrides it. Available: api, decision, ingest, persist, stream, worker; all starts all six. See Running roles
SP_METRICS_ADDR :9090 Address of the separate Prometheus metrics listener, which serves /metrics, plus /healthz and /readyz probes that only report that the process is up (for roles with no HTTP API). off (or an empty value) switches it off. Every process opens it, so two processes on one machine need different ports. If the port is taken, the process stops with metrics listener … instead of running unobserved. Keep it on an internal network
Variable Default Meaning
SP_DEPLOYMENT selfhost selfhost for an installation you run, where licensing applies; saas for workspaces SpeculaPro operates, where licensing is skipped entirely. Anything else stops SpeculaPro at start-up with config: SP_DEPLOYMENT must be selfhost or saas
SP_LICENSE_FILE (none) Path to a licence file, for example one mounted from a Kubernetes Secret. Licences installed under Settings → License or with speculapro license install count too; the valid one that runs longest applies. A file that can’t be read is skipped with a warning
SP_LICENSE_CHECKIN_URL (none) Optional. When set, the worker posts a signed usage report here once a day. Must be https:// (except with SP_ENV=development). Empty means SpeculaPro never contacts SpeculaPro. The licence never depends on it

Licensing never stops ingest, monitoring, real-time decisions, alerts, cases, webhooks or notifications. See Licensing.

Variable Default Meaning
SP_LOG_LEVEL info Minimum level written: debug, info, warn or error. An unrecognised value falls back to info
SP_LOG_FORMAT json json (one JSON object per line, for log collectors) or text (easier to read in a terminal). Logs go to standard output
Variable Default Meaning
SP_HTTP_ADDR :8080 Address and port to listen on. :8080 means every network interface, port 8080. Use 127.0.0.1:8080 to accept connections only from the same machine (for example behind a local reverse proxy)
SP_HTTP_READ_TIMEOUT 10s Maximum time to read a whole request
SP_HTTP_WRITE_TIMEOUT 30s Maximum time to write a response
SP_HTTP_SHUTDOWN_TIMEOUT 20s On stop, how long to let in-flight requests finish before closing
SP_HTTP_MAX_BODY_BYTES 1048576 Largest accepted request body (1 MiB) for every endpoint except the ingest API, which has its own limit (SP_INGEST_MAX_BODY_BYTES). Two screening endpoints differ: a list import always accepts up to 2 MiB, and a signed list bundle follows SP_INGEST_MAX_BODY_BYTES. Larger requests get 413 Request Entity Too Large
SP_HTTP_TRUST_PROXY false Use the client address from the X-Forwarded-For header. Only set true when SpeculaPro sits behind a reverse proxy or load balancer that you control and that adds to this header
SP_HTTP_PROXY_HOPS 1 With SP_HTTP_TRUST_PROXY=true: how many of your own proxies add to X-Forwarded-For on the way in. 1 for a load balancer; 2 for a CDN in front of a load balancer. The client address is the entry this many places from the right. If the header has fewer entries than that, or the entry isn’t an IP address, the address of the machine that connected is used
SP_HTTP_API_DOCS (unset) Serve the OpenAPI description and interactive docs at /api/openapi.json and /api/docs. When unset: on in development, off everywhere else
Variable Default Meaning
SP_SECURITY_MASTER_KEYS (unset) Encryption keys for personal data, as <version>:<base64 key> entries separated by commas. The first entry is used for new data; the others are kept so older data can still be read. Required unless SP_ENV=development
SP_SECURITY_BLIND_INDEX_KEY (unset) Key used to make searchable fingerprints of personal data (phone numbers, national ID numbers) without storing them in clear. Base64, 32 bytes. Required unless SP_ENV=development. Never changes
SP_SECURITY_SESSION_TTL 12h Longest a sign-in lasts, however active the user is
SP_SECURITY_SESSION_IDLE 30m A user who does nothing for this long is signed out
SP_SECURITY_COOKIE_SECURE true Send the sign-in cookie over HTTPS only, under the hardened name __Host-sp_session. Leave true in production. Browsers treat http://localhost as secure, so local development works with it on

Generate both keys with speculapro keys generate. Read Encryption keys before you store real data.

Variable Default Meaning
SP_DB_URL postgres://speculapro:speculapro@localhost:5432/speculapro?sslmode=disable Connection string for PostgreSQL 18+
SP_DB_SYSTEM_URL (unset: uses SP_DB_URL) Connection string for the system database role. The decision, persist, stream, connector, analytics and worker roles do their cross-workspace work through it, so in production it names speculapro_system, which bypasses row-level security; they always filter by workspace themselves, and refuse to start unless this user is a superuser or has BYPASSRLS. Every process, api and ingest included, also reads the installation’s own tables through it (the licence and its usage figures), which speculapro_app may not read: where the database roles are separate, set it for every process. See Database roles
SP_DB_MAX_CONNS 20 Maximum connections per connection pool. Every process with SP_DB_SYSTEM_URL set opens a second pool of the same size. Add them up across processes to check you stay below Postgres’s max_connections. SpeculaPro pools its own connections, so a pooler is often unnecessary; which connection may go through PgBouncer differs by role
SP_DB_APP_ROLE, SP_DB_SYSTEM_ROLE speculapro_app, speculapro_system The names of the two database roles. Read by speculapro migrate up, which, after the schema, grants these roles the database functions and table rights they need (the list is deploy/sql/grants.sql), so an upgrade never leaves a function the worker or the API may not call. Change them only if you named your roles differently. If the roles do not exist (a development database with one role), the step is skipped and the log says grants: skipped.
SP_DB_REPLICA_URL (unset) Optional. A read replica for backtests’ long history reads, used by worker processes with SP_BACKTEST_WORKERS above 0. Its user must bypass row-level security like speculapro_system: at start-up the worker checks, and refuses to start with SP_DB_REPLICA_URL: database role cannot bypass row-level security (use the speculapro_system role on the replica). When set, such a worker opens a third pool of up to SP_DB_MAX_CONNS connections. See A read replica for backtests

The connection string format is postgres://<user>:<password>@<host>:<port>/<database>?<options>. Useful options:

Option Use
sslmode=verify-full Encrypt the connection and check the server’s certificate and hostname. Use this in production
sslrootcert=/path/ca.pem The certificate authority to trust for verify-full
sslmode=disable No encryption. Local development only

Which database user to connect as is explained in PostgreSQL.

Variable Default Meaning
SP_KAFKA_BROKERS localhost:19092 List of broker addresses. A few are enough; the client discovers the rest of the cluster
SP_KAFKA_CLIENT_ID speculapro Name the client reports to the brokers, visible in broker logs and metrics
SP_KAFKA_TOPIC_PREFIX sp. Prefix added to every topic name, so txn.ingested becomes sp.txn.ingested. Change it to share one Kafka cluster between several SpeculaPro environments (for example sp-staging.)
SP_KAFKA_PARTITIONS 64 Partitions per topic, used only when speculapro topics creates a topic. 16 topics are created, so the broker must accept 16 times this number; use 3 to 6 on a laptop or a shared test broker. See How many partitions the broker accepts
SP_KAFKA_REPLICATION_FACTOR -1 Copies of each partition, used only when creating topics. -1 means the broker’s default (1 on a single node). Set 3 on a production cluster
SP_KAFKA_SASL_MECHANISM (unset) Broker authentication: empty (none), PLAIN, SCRAM-SHA-256 or SCRAM-SHA-512
SP_KAFKA_USERNAME (unset) SASL username
SP_KAFKA_PASSWORD (unset) SASL password
SP_KAFKA_TLS_ENABLED false Encrypt connections to the brokers
SP_KAFKA_TLS_CA_FILE (unset) PEM file of the certificate authority to trust. When unset, the operating system’s trusted authorities are used
SP_KAFKA_TLS_CERT_FILE (unset) Client certificate (PEM), for mutual TLS
SP_KAFKA_TLS_KEY_FILE (unset) Client private key (PEM), for mutual TLS
SP_KAFKA_TLS_SERVER_NAME (unset) Hostname expected on the broker’s certificate, when it differs from the address you connect to
SP_KAFKA_TLS_INSECURE_SKIP_VERIFY false Skip certificate checks. Never in production: it makes TLS useless against interception
Variable Default Meaning
SP_INGEST_MAX_BATCH 500 Most items accepted in one request. 500 is also the hard ceiling: this setting can only lower it (a higher or zero value is treated as 500). Senders get 413 above a lowered limit, and 422 above 500 whatever the setting
SP_INGEST_MAX_BODY_BYTES 8388608 Largest accepted request body on the ingest endpoints (8 MiB). Also the limit for a signed list bundle sent to POST /api/v1/screening/bundles; the bundle format itself allows 96 MiB, and the command line has no such limit
SP_INGEST_FX_MAX_AGE 168h How old the newest exchange rate may be (7 days). A foreign-currency transaction is rejected if there is no rate for its currency, or the newest one is older than this. Load rates daily; see Currencies and FX
Variable Default Meaning
SP_PERSIST_GROUP speculapro-persist Kafka consumer group name. Every persist process of one installation must use the same group, so they share the work instead of each writing everything
SP_PERSIST_BATCH_MAX 5000 Most records written to Postgres in one batch
SP_PERSIST_WORKERS 4 Parallel writers in this process. The total across all persist processes should not exceed the topic’s partition count (64 by default); extra workers sit idle

See Persister.

Variable Default Meaning
SP_STREAM_GROUP speculapro-stream Kafka consumer group name. Every stream process of one installation must use the same group, and it must differ from SP_PERSIST_GROUP: the two roles each read every transaction
SP_STREAM_BATCH_MAX 2000 Most transactions evaluated in one batch
SP_STREAM_WORKERS 4 Parallel evaluators in this process. The total across all stream processes should not exceed the topic’s partition count (64 by default); extra evaluators sit idle
SP_STREAM_REPLAY_TTL 30m How long the evaluator recognises a transaction it has already counted, so a re-delivered batch isn’t counted twice. Make it longer than your longest expected stream outage. Each transaction holds Redis memory for this long, so memory grows with throughput × this value. At least 1m; a smaller value stops SpeculaPro from starting. See Replays and retries

Rule changes, lists and suppressions are re-read every 15 seconds; that interval is fixed. See Stream evaluator.

The decision budget (how long a decision may take) and the failure policy (fail-open or fail-closed) are not set here: each workspace sets its own under Settings → Workspace. See Workspace settings. These settings tune the decision process itself.

Variable Default Meaning
SP_DECISION_STATE_TTL 10s How often each workspace’s rules, lists and decision policy are re-read. A rule activated, a list entry added or a policy saved reaches live decisions within this time
SP_DECISION_PROFILE_TTL 30s How stale a cached customer, account or counterparty profile may be. A risk designation or a customer update can take this long to reach live decisions
SP_DECISION_PROFILE_CACHE_SIZE 500000 Most profiles held in each process’s memory. Size it to your active customers and accounts; a profile not in the cache is read from Postgres, which is slower
SP_DECISION_IDEMPOTENCY_TTL 72h How long a retry with the same external_id gets the original answer back. At least 1h; a smaller value stops SpeculaPro from starting with config: SP_DECISION_IDEMPOTENCY_TTL must be at least 1h. Each decision holds Redis memory for this long
SP_DECISION_MAX_INFLIGHT 4096 Most decisions this process works on at once. Beyond it, a new request is answered at once by the workspace’s failure policy, marked degraded with the cause overload, instead of waiting in a queue. This stops a slow dependency from building a backlog that would make every later decision late too

How the role works and how to scale it: Running roles and Redis / Valkey.

Webhooks are registered through the api role and delivered by the worker role. Set SP_WEBHOOKS_ALLOW_PRIVATE and SP_WEBHOOKS_ALLOW_HTTP the same way on both.

Variable Default Meaning
SP_WEBHOOKS_ALLOW_PRIVATE false Allow endpoints on private or internal addresses, such as 10.0.0.5, 192.168.1.20, localhost or *.internal. Off, they are refused when registered and again at every connection. Turn it on only for a self-hosted installation whose receivers sit on your own network. Even when on, link-local and cloud metadata addresses (169.254.0.0/16, fe80::/10, 169.254.169.254 and similar) and 0.0.0.0/8 stay refused, and carrier-grade NAT (100.64.0.0/10) isn’t opened: “private” means RFC 1918, IPv6 unique-local and loopback only. See the address check
SP_WEBHOOKS_ALLOW_HTTP false Allow plain http:// endpoints. Development only: with any SP_ENV other than development, SpeculaPro refuses to start with config: SP_WEBHOOKS_ALLOW_HTTP is for development only; webhooks must use https
SP_WEBHOOKS_TIMEOUT 10s How long one delivery may take, from connecting to the receiver’s answer. Slower answers count as failures. Between 1s and 1m; anything else stops SpeculaPro at start-up with config: SP_WEBHOOKS_TIMEOUT must be between 1s and 1m
SP_WEBHOOKS_CONCURRENCY 16 Deliveries this worker process makes at the same time. Between 1 and 256; anything else stops SpeculaPro at start-up with config: SP_WEBHOOKS_CONCURRENCY must be between 1 and 256
SP_WEBHOOKS_DISABLE_AFTER 72h An endpoint that has failed for this long without a single success is switched off, and everyone with webhooks.manage is told. See When an endpoint is switched off
SP_WEBHOOKS_RETENTION 720h How long the delivery history is kept (30 days). Older finished deliveries are deleted by the worker’s maintenance run. Events still waiting for an endpoint that has been switched off for this whole period are given up and marked failed

Single sign-on is set up per workspace in Settings → Single sign-on. These settings, read by the api role, apply to the whole installation.

Variable Default Meaning
SP_SSO_PUBLIC_URL SP_NOTIFY_PUBLIC_URL The address browsers and identity providers use to reach SpeculaPro, for example https://speculapro.yourbank.com.gh. The OIDC redirect URI, the SAML entity ID, ACS URL and single logout URL, the SCIM base URL (<address>/scim/v2) and the post-logout address are built from it, so it must be exactly what people type. If unset, SP_NOTIFY_PUBLIC_URL is used, which itself defaults to http://localhost:8080. Outside SP_ENV=development, single sign-on can’t be turned on while it is local (localhost, a .localhost name or a loopback address)
SP_SSO_VERIFY_DOMAINS true A claimed email domain counts only once a DNS TXT record proves the workspace controls it. Keep true for SaaS or any installation shared by several institutions. A self-hosted installation for one institution, whose administrators are trusted, can set false: domains are then verified as soon as they are added
SP_SSO_ALLOW_PRIVATE false Let issuer, JWKS, token, userinfo and SAML metadata addresses resolve to private or internal addresses. Turn it on only for a self-hosted installation whose identity provider (Keycloak, ADFS) sits on your own network. Off, such addresses are refused at every connection. Even when on, link-local and cloud metadata addresses (169.254.0.0/16, fe80::/10, 169.254.169.254 and similar) and 0.0.0.0/8 stay refused, and carrier-grade NAT (100.64.0.0/10) isn’t opened: “private” means RFC 1918, IPv6 unique-local and loopback only. See the address check
SP_SSO_ALLOW_HTTP false Allow plain http:// issuer and metadata URLs. Development only: with any SP_ENV other than development, SpeculaPro refuses to start with config: SP_SSO_ALLOW_HTTP is for development only; identity providers must use https

SSO keeps its short-lived sign-in state in Redis, so SSO sign-in needs Redis. While Redis is down, break-glass administrators can still sign in with their password.

Variable Default Meaning
SP_WORKER_INTERVAL 6h How often maintenance runs. It also runs once at start-up
SP_WORKER_INGEST_KEY_RETENTION 2160h How long the record of each external_id you sent is kept (90 days). After that, resending an old external_id is treated as new, not as a duplicate

See Worker.

Backtests are run by worker processes. See Worker: backtests.

Variable Default Meaning
SP_BACKTEST_WORKERS 2 Backtests one worker process runs at once. 0 turns them off in that process, for example on a worker kept for notifications
SP_BACKTEST_GLOBAL_LIMIT 4 Backtests running at once across the whole installation. Set the same value on every worker
SP_BACKTEST_TENANT_LIMIT 1 Backtests running at once per workspace. Set the same value on every worker
SP_BACKTEST_TIMEOUT 30m A run taking longer is stopped and marked failed
SP_BACKTEST_MAX_MEMORY_MB 1024 Feature memory one run may hold. A run that needs more fails with a message saying so
SP_BACKTEST_MAX_EVENTS 30000000 Transactions one run may replay, warm-up included. A larger period is refused before it starts. For a scheduled rule’s backtest, the transaction rows its simulated runs read in all, counting a transaction once for each run whose window holds it
SP_BACKTEST_PAGE_SIZE 5000 Rows per history read

SpeculaPro refuses to start if SP_BACKTEST_WORKERS is negative, either limit is below 1, SP_BACKTEST_PAGE_SIZE is below 100 or SP_BACKTEST_MAX_MEMORY_MB is below 64, with:

config: SP_BACKTEST_* limits are out of range (workers >= 0, limits >= 1, page size >= 100, memory >= 64 MB)

Risk models are trained, and their drift measured, by worker processes. Scoring in the decision and stream roles needs no setting: a workspace without a shadow or live model isn’t scored at all. See Risk models for operators.

Variable Default Meaning
SP_MODELS_WORKERS 1 Trainings one worker process runs at once. 0 turns training and drift measurement off in that process
SP_MODELS_MIN_ROWS 5000 The fewest transactions a training window may hold; a training on less is refused. Set the same value on the api role, which checks it before queuing
SP_MODELS_TIMEOUT 1h A training taking longer is stopped and marked failed
SP_MODELS_MAX_MEMORY_MB 1024 Feature memory one training may hold. A training that needs more fails with a message saying so
SP_MODELS_MAX_EVENTS 30000000 Transactions one training may replay, the history before the window included
SP_MODELS_DRIFT_DAYS 7 The last so many days are compared with the training data. Set the same value on the api role, which shows it
SP_MODELS_DRIFT_INTERVAL 1h How often drift is measured

At most two trainings run at once across the installation, and one per workspace; neither is a setting. Keep the worker’s memory limit above SP_BACKTEST_WORKERS × SP_BACKTEST_MAX_MEMORY_MB + SP_MODELS_WORKERS × SP_MODELS_MAX_MEMORY_MB.

SpeculaPro refuses to start if SP_MODELS_WORKERS is negative, SP_MODELS_MIN_ROWS is below 100, SP_MODELS_MAX_MEMORY_MB is below 64, SP_MODELS_MAX_EVENTS is below 1,000, SP_MODELS_DRIFT_DAYS is outside 1 to 90, or SP_MODELS_DRIFT_INTERVAL or SP_MODELS_TIMEOUT is under a minute, with:

config: SP_MODELS_* settings are out of range (workers >= 0, min rows >= 100, memory >= 64 MB, max events >= 1000, drift days 1 to 90, drift interval and timeout >= 1m)

The AI assistant advises analysts on open alerts and never decides. It is off for every workspace until that workspace switches it on under Settings → AI assistant. These settings decide what an installation offers. The api role makes the calls people ask for; the worker role makes the automatic ones.

Variable Default What it does
SP_AI_API_KEY (empty) The installation’s key with the model’s provider (SP_AI_PROVIDER). With it, workspaces can choose Provided with your subscription: their calls are made and counted on this key. Empty: a workspace can only use a key of its own. Treat it as a secret
SP_AI_PROVIDER anthropic Whose API the installation’s key is for: anthropic (Claude), openai, google (Gemini), mistral, or custom for any other API that speaks the OpenAI chat format, such as a model you host yourself. Any other value stops start-up. It governs the installation’s key only: a workspace with its own key chooses its own provider in its settings
SP_AI_BASE_URL (empty) With anthropic: send Claude calls through your own gateway instead of the public API. With openai, google or mistral: use this address instead of the provider’s own. With custom: the address of the API, normally ending in /v1; required when a key is set. Must be an https address outside SP_ENV=development, except with custom, where plain http is accepted for a model on your own network. It isn’t checked against private addresses: it is yours. See the note below on workspaces’ own keys
SP_AI_MODEL claude-sonnet-5-5 The model a workspace gets on the installation’s key when it hasn’t chosen one. With anthropic: claude-sonnet-5-5, claude-haiku-5-5 or claude-opus-5-5; any other value stops start-up. With another provider and a key set: that provider’s id for a model, which must not be empty or start with claude-. Workspaces on the installation’s key can’t choose another model then
SP_AI_MONTHLY_CALLS 0 The default for how many calls a workspace may make on the installation’s key in a calendar month (UTC). 0 is no limit. On a hosted installation a workspace’s plan can set its own number, which takes over from this one, or leave the installation’s key out; without the provider console this setting is the only allowance. A call is counted before it is made, one at a time for each workspace, so the limit can’t be overrun, and a call that fails at the provider still counts (it may be billed). Past the limit, a request is refused with 429 and automatic reviews stop for that workspace until the next month. A workspace’s own key is never limited

Setting none of these sends nothing anywhere: no call is made for a workspace that hasn’t switched the assistant on. With the assistant on, the pods that make the calls need outbound https access to the API or to your gateway, and to the address of every provider a workspace may choose with its own key.

Start-up refuses a configuration that can’t work:

config: SP_AI_PROVIDER must be anthropic, openai, google, mistral or custom
config: SP_AI_MODEL must name a model of SP_AI_PROVIDER=openai
config: SP_AI_PROVIDER=custom needs SP_AI_BASE_URL, the address of the OpenAI-compatible API (ending in /v1)
config: SP_AI_BASE_URL must be an https address

Workspaces’ own keys. A workspace that brings its own key chooses a provider under Settings → AI assistant, whatever SP_AI_PROVIDER says:

The workspace’s provider Where its calls go
Anthropic (Claude) With SP_AI_PROVIDER=anthropic and SP_AI_BASE_URL set: that gateway. Whoever runs it handles the workspace’s key, and the workspace’s settings tab says calls to Claude go through a gateway the operator runs. Otherwise straight to Anthropic’s API
OpenAI, Google (Gemini), Mistral Straight to that provider’s address. Never through SP_AI_BASE_URL
Another OpenAI-compatible API The address the workspace gave. It must be a public https address unless you set SP_CONNECTOR_ALLOW_PRIVATE=true (private and internal addresses) or SP_CONNECTOR_ALLOW_HTTP=true (plain http), the same two switches that govern connectors. Link-local and cloud metadata addresses are refused either way, at every connection, and redirects are not followed

The OpenAI-compatible path has been verified against local stand-in servers, not against any real provider, and support for structured JSON output differs between models. Try the model you configure before you offer it.

Asking for more calls. A workspace’s administrators can ask for more calls only where the installation runs the provider console and has a key. Without the console, SP_AI_MONTHLY_CALLS is the only allowance, and the settings tab tells them to ask whoever runs the installation.

The optional analytics store. Leave SP_CLICKHOUSE_URL empty and none of these settings does anything: everything reads PostgreSQL. Set the connection settings on every process: api and worker read the store, and the analytics role fills it.

Connection

Variable Default Meaning
SP_CLICKHOUSE_URL empty ClickHouse’s native-protocol address: clickhouse://host:9000, or port 9440 with TLS. Setting it switches the analytics store on. No user or password in it
SP_CLICKHOUSE_USER default The ClickHouse user
SP_CLICKHOUSE_PASSWORD empty Its password. A secret
SP_CLICKHOUSE_DATABASE speculapro The database. speculapro migrate up creates it. Letters, digits and underscores only
SP_CLICKHOUSE_TLS_ENABLED false Connect with TLS
SP_CLICKHOUSE_TLS_CA_FILE empty The CA that signed ClickHouse’s certificate. Empty: the system’s trusted roots
SP_CLICKHOUSE_TLS_CERT_FILE, SP_CLICKHOUSE_TLS_KEY_FILE empty A client certificate and key, for mutual TLS
SP_CLICKHOUSE_TLS_SERVER_NAME empty The name to check the certificate against, when it differs from the host in the URL
SP_CLICKHOUSE_TLS_INSECURE_SKIP_VERIFY false Skip certificate checks. For testing only
SP_CLICKHOUSE_MAX_CONNS 10 Connections each process may open
SP_CLICKHOUSE_DIAL_TIMEOUT 5s How long connecting may take
SP_CLICKHOUSE_QUERY_TIMEOUT 30s How long an Analytics page or scheduled-rule read may run. Backtests read for as long as SP_BACKTEST_TIMEOUT allows
SP_CLICKHOUSE_READER_ROLE empty A ClickHouse role that speculapro migrate up creates, grants SELECT, INSERT and ALTER DELETE on the database, and puts row policies on, as a second layer of workspace isolation. Empty: no row policies

Who reads it (roles api and worker)

Variable Default Meaning
SP_CLICKHOUSE_BACKTEST_THRESHOLD 720h A backtest whose history, warm-up included, spans more than this (30 days) reads its transactions from ClickHouse, when the workspace’s copy is complete and the writer has caught up. Set the same value on every worker
SP_CLICKHOUSE_SCENARIO_THRESHOLD 720h A scheduled rule with a ClickHouse version (I2, I3) reads it when its window is at least this long, the workspace’s copy is complete from the window’s start and the writer has caught up

The analytics role

Variable Default Meaning
SP_CLICKHOUSE_GROUP speculapro-analytics The role’s Kafka consumer group. Renaming it makes the role re-read everything Kafka retains (safely, but slowly)
SP_CLICKHOUSE_WORKERS 2 Kafka consumers per analytics process. The total across all analytics processes shouldn’t exceed the partition count of txn.ingested
SP_CLICKHOUSE_BATCH_MAX 50000 The most rows in one insert. ClickHouse prefers few large inserts
SP_CLICKHOUSE_FLUSH_INTERVAL 2s How long a batch that isn’t full waits before it is written. It is the store’s normal delay behind live
SP_CLICKHOUSE_SYNC_INTERVAL 30s How often rule hits, alerts and cases are copied from PostgreSQL
SP_CLICKHOUSE_SYNC_OVERLAP 5m How far before its last copy each pass re-reads, so rows committed late aren’t missed

With SP_CLICKHOUSE_URL set, SpeculaPro refuses to start on a bad value:

config: SP_CLICKHOUSE_URL must be a native-protocol address such as clickhouse://clickhouse:9000
config: put ClickHouse credentials in SP_CLICKHOUSE_USER and SP_CLICKHOUSE_PASSWORD, not in the URL
config: SP_CLICKHOUSE_DATABASE must be a plain identifier (letters, digits, underscore)
config: SP_CLICKHOUSE_READER_ROLE must be a plain identifier
config: SP_CLICKHOUSE_BACKTEST_THRESHOLD is 0s; it must be between 1h0m0s and 87600h0m0s

The last form names the variable, the value it read and the range it accepts. Every number is checked:

Variable Accepted range
SP_CLICKHOUSE_MAX_CONNS 1 to 1000
SP_CLICKHOUSE_BATCH_MAX 100 to 1,000,000
SP_CLICKHOUSE_WORKERS 1 to 64
SP_CLICKHOUSE_DIAL_TIMEOUT 100ms to 2m
SP_CLICKHOUSE_QUERY_TIMEOUT 1s to 1h
SP_CLICKHOUSE_FLUSH_INTERVAL 10ms to 5m
SP_CLICKHOUSE_SYNC_INTERVAL 1s to 1h
SP_CLICKHOUSE_SYNC_OVERLAP 1s to 24h
SP_CLICKHOUSE_BACKTEST_THRESHOLD, SP_CLICKHOUSE_SCENARIO_THRESHOLD 1h to 87600h (ten years)

The thresholds can’t be zero or negative: that would send every backtest and scheduled rule to ClickHouse, however short, where the newest transactions arrive a moment later than in PostgreSQL. SP_CLICKHOUSE_GROUP must be a Kafka group name and SP_CLICKHOUSE_USER can’t be empty.

Sanctions and PEP screening: the worker fetches the lists and screens; api processes answer from what is stored. Set these the same on every process. See Screening lists for operators.

Variable Default Meaning
SP_SCREENING_ENABLED true false: the worker does no screening work (no list fetches, no screening, no re-screens), and decision processes screen no counterparty inline. The settings page and API still answer from stored data
SP_SCREENING_AIR_GAPPED false true: never fetch a list from its publisher. Lists come from signed list bundles and imports only
SP_SCREENING_ALLOW_PRIVATE false true: list fetches and screening providers may reach private network addresses (a yente server or list mirror on your own network), and a provider address or a list address in SP_SCREENING_SOURCE_URLS may be plain http. Link-local and cloud metadata addresses stay refused. Self-hosted only: refused when SP_DEPLOYMENT=saas (SpeculaPro doesn’t start), like SP_CONNECTOR_ALLOW_PRIVATE. See the caution
SP_SCREENING_FETCH_TIMEOUT 5m The longest one list download may take. At least 1s
SP_SCREENING_INTERVAL 30s Time between the worker’s screening passes (list fetches that are due, list changes, new and changed customers and counterparties, provider retries, queued re-screens). At least 1s
SP_SCREENING_BATCH_SIZE 1000 Customers or counterparties screened per database transaction. From 1 to 10000
SP_SCREENING_SOURCE_URLS (empty) Your own download address for a built-in list, as space-separated key=url pairs, for example ke-frc=https://www.frc.go.ke/wp-content/uploads/2026/09/Domestic-List_Kenya.xlsx. Keys: un-sc, us-ofac-sdn, us-ofac-cons, eu-fsf, gb-uksl, za-fic, ca-sema, au-dfat, ke-frc. https only (http only with SP_SCREENING_ALLOW_PRIVATE=true); the address goes through the same destination checks as any fetch. No user name, password or #fragment in an address. It wins over an address stored with speculapro screening source set-url <key> <url>, which wins over the built-in address; start-up overwrites neither. See When a publisher moves a list
SP_SCREENING_MAX_REMOVAL_PERCENT 20 A fetched list or signed bundle that would remove more than this percentage of the current version’s entries is refused, and the current version stays, until someone accepts it: the operator with speculapro screening source fetch <key> --accept-removal or screening import-bundle <file> --accept-removal, or a person with screening.accept_removal with accept_removal=true on the bundle import API. Helm: screening.maxRemovalPercent. From 1 to 100; 100 switches the check off. See When a list loses many entries
SP_SCREENING_MAX_REMOVAL_MIN_ENTRIES 50 Lists whose current version has fewer entries than this are not checked. At least 1. Helm: screening.maxRemovalMinEntries

SpeculaPro checks these when it starts, and refuses to start when one is wrong:

Refused Message
SP_SCREENING_ALLOW_PRIVATE=true with SP_DEPLOYMENT=saas config: SP_SCREENING_ALLOW_PRIVATE can't be set when SP_DEPLOYMENT=saas
SP_SCREENING_INTERVAL or SP_SCREENING_FETCH_TIMEOUT below 1s config: SP_SCREENING_INTERVAL and SP_SCREENING_FETCH_TIMEOUT must be at least 1s
SP_SCREENING_BATCH_SIZE below 1 or above 10000 config: SP_SCREENING_BATCH_SIZE must be between 1 and 10000, got …
SP_SCREENING_MAX_REMOVAL_PERCENT below 1 or above 100, or SP_SCREENING_MAX_REMOVAL_MIN_ENTRIES below 1 config: SP_SCREENING_MAX_REMOVAL_PERCENT must be between 1 and 100 and SP_SCREENING_MAX_REMOVAL_MIN_ENTRIES at least 1
A pair in SP_SCREENING_SOURCE_URLS that is not key=url config: SP_SCREENING_SOURCE_URLS: "…" is not key=url (pairs are separated by spaces)
A key twice in SP_SCREENING_SOURCE_URLS config: SP_SCREENING_SOURCE_URLS names … twice
A key in SP_SCREENING_SOURCE_URLS that is not a fetched list config: SP_SCREENING_*: invalid: … is not a fetched list (the fetched lists: …)
An address in SP_SCREENING_SOURCE_URLS that is not https, is malformed or carries credentials, or points at a private or metadata host config: SP_SCREENING_*: invalid: the URL for … must be https, … is not valid, or … : destination not allowed: …

With the Helm chart these are the screening values; see Kubernetes with Helm.

The speculapro screening commands (signed bundles, installation lists from a file, list addresses, installation-wide providers) are described in Screening lists for operators. They connect with SP_DB_SYSTEM_URL when it is set; speculapro screening provider set also needs SP_SECURITY_MASTER_KEYS, to encrypt the provider’s credentials.

Only for a hosted service that runs in more than one region. Leave all of these unset on a single installation, which is every self-hosted one: nothing changes. See Running more than one region for what they do, how to generate the keys and what stops start-up. .env.example lists them all, commented out.

Setting Default Meaning
SP_CELL_REGION (empty) This installation’s region key: 2 to 11 characters, a lowercase letter first, then lowercase letters, digits or dashes. Never change it once workspaces exist
SP_CELL_NAME the region key The region’s name as people see it
SP_CELL_PUBLIC_URL (empty) Where this region is reached. Required with SP_DIRECTORY_URL
SP_DIRECTORY_URL (empty) The directory’s address (https). Needs SP_CELL_REGION
SP_DIRECTORY_KEY (empty) Secret. This cell’s signing key, <version>:<base64 of 32 bytes>, from speculapro directory keygen <region>. Required with SP_DIRECTORY_URL
SP_DIRECTORY_PUBLIC_URL (empty) The global sign-in address, linked from this region’s sign-in page
SP_DIRECTORY_ALLOW_PRIVATE false The directory is on your private network
SP_DIRECTORY_TIMEOUT 10s Per call to the directory; 1s to 1m
SP_DIRECTORY_ALLOW_HTTP false Development only

Read only by the directory itself (speculapro directory run):

Setting Default Meaning
SP_DIRECTORY_DB_URL (empty) Secret. The directory’s own database, never a cell’s
SP_DIRECTORY_CELL_KEYS (empty) Secret. The cells’ keys, gh:1:<base64>,eu:1:<base64>
SP_DIRECTORY_ADDR :8085 Listen address
SP_DIRECTORY_RESOLVE_PER_MINUTE 20 Workspace look-ups one client address may make a minute

See Setting up the provider console.

Setting Default Meaning
SP_PROVIDER_CONSOLE_ENABLED false Serves the provider console from api processes. Only with SP_DEPLOYMENT=saas; otherwise SpeculaPro doesn’t start. Needs SP_PROVIDER_ADDR, SP_DB_SYSTEM_URL and SP_SECURITY_COOKIE_SECURE=true
SP_PROVIDER_ADDR none The console’s own listener, for example :8090. Required when the console is on; must differ from SP_HTTP_ADDR and SP_METRICS_ADDR
SP_PROVIDER_ALLOWED_CIDRS empty Comma-separated networks or single addresses staff may come from. Everything else is answered 403. Empty: whoever the network lets through
SP_PROVIDER_SESSION_IDLE 30m Idle timeout of a staff session, 1m to 1h
SP_PROVIDER_SESSION_TTL 8h Longest a staff session lasts, from the idle timeout up to 12h
SP_PROVIDER_PURGE_ENABLED false Lets the worker delete a workspace whose scheduled deletion is due. Only with SP_DEPLOYMENT=saas. Set it for the worker and api processes alike. See Scheduled deletion
SP_PROVIDER_DEV_NO_TOTP false Development only: staff sign in without a second factor. Refused outside SP_ENV=development

The connector role reads institution Kafka topics, SFTP / S3 file drops and change data capture sources (Debezium topics and Postgres replication) set up under Settings → Data sources. See Connector. Set SP_CONNECTOR_ALLOW_PRIVATE, SP_CONNECTOR_ALLOW_HTTP, SP_CONNECTOR_KAFKA_TOPIC_NAMESPACE and SP_CONNECTOR_KAFKA_SHARED_TOPICS the same on the api role, which checks data sources against them when they are saved. Test connection runs on a connector (in the same process in all, otherwise queued for one), so only connectors need outbound access.

Variable Default Meaning
SP_CONNECTOR_ALLOW_PRIVATE false Allow SFTP servers and object stores on private or internal addresses. Turn it on for a self-hosted installation whose servers sit on your network; never on one shared by several institutions: with SP_DEPLOYMENT=saas, SpeculaPro refuses to start with it (config: SP_CONNECTOR_ALLOW_PRIVATE can't be set when SP_DEPLOYMENT=saas). Off, such hosts are refused when a data source is saved and again at every connection. Even when on, link-local and cloud metadata addresses (169.254.0.0/16, fe80::/10, 169.254.169.254 and similar) and 0.0.0.0/8 stay refused, and carrier-grade NAT (100.64.0.0/10) isn’t opened: “private” means RFC 1918, IPv6 unique-local and loopback only. See the address check
SP_CONNECTOR_ALLOW_HTTP false Allow plain-http S3 endpoints, such as an on-premises MinIO without TLS. Credentials then travel in the clear: development or a private network only
SP_CONNECTOR_INTERVAL 30s How often due file drops are looked for (at least 1s). Run now takes effect within this
SP_CONNECTOR_CONCURRENCY 2 File drops this process runs at once, 1 to 64
SP_CONNECTOR_FILE_WORKERS 4 Batches of one file loaded in parallel, 1 to 64
SP_CONNECTOR_CONNECT_TIMEOUT 15s Connecting, including the SSH handshake (at least 1s)
SP_CONNECTOR_READ_TIMEOUT 60s A transfer making no progress for this long fails (at least 1s)
SP_CONNECTOR_MAX_FILE_BYTES 2147483648 The largest file, in bytes (2 GiB; at least 1 MiB). Also caps a .gz file’s decompressed content. The Helm chart sets 900 MiB
SP_CONNECTOR_MAX_FILES_PER_RUN 100 Files one run of a data source loads; the rest wait for the next run
SP_CONNECTOR_SPOOL_DIR Empty: the OS temporary directory Where files are staged while they load. Needs room for the largest file times SP_CONNECTOR_CONCURRENCY
SP_CONNECTOR_MAX_STORED_RESULTS_BYTES 33554432 Results files up to this size (compressed; 32 MiB) are kept for download in the app. Larger ones are only written back to the source
SP_CONNECTOR_HISTORY_RETENTION 2160h How long file history and stored results are kept (90 days). After that, a file left in place is no longer recognised and is loaded again
SP_CONNECTOR_KAFKA_BROKERS Empty The cluster institutions publish to, comma-separated. Empty: SpeculaPro’s own cluster and its SP_KAFKA_* settings
SP_CONNECTOR_KAFKA_SASL_MECHANISM, _USERNAME, _PASSWORD Empty Credentials for that cluster: PLAIN, SCRAM-SHA-256 or SCRAM-SHA-512
SP_CONNECTOR_KAFKA_TLS_ENABLED false TLS to that cluster, with _TLS_CA_FILE, _TLS_CERT_FILE, _TLS_KEY_FILE and _TLS_SERVER_NAME as for Kafka
SP_CONNECTOR_KAFKA_GROUP speculapro-connector The connector’s consumer group
SP_CONNECTOR_KAFKA_TOPIC_NAMESPACE Empty On a cluster shared by several institutions: each workspace may only read topics named <namespace><workspace slug>.<name>. Your broker’s access rules must let each institution write only to its own namespace. Empty: any topic. See Who may read which topic
SP_CONNECTOR_KAFKA_SHARED_TOPICS Empty Comma-separated topics that several workspaces may read, each record naming its workspace in the sp-workspace header. Only for producers you trust to name any workspace
SP_CONNECTOR_KAFKA_BATCH_MAX 2000 Records fetched at a time
SP_CONNECTOR_KAFKA_RELOAD_INTERVAL 30s How often topic mappings are re-read
SP_CONNECTOR_KAFKA_PAUSE_LAG 500000 Pause reading while the persister or stream evaluator is this many records behind on SpeculaPro’s transaction topic; resume below half. 0 never pauses
SP_CONNECTOR_CDC_ALLOW_PLAINTEXT false Allow Postgres replication sources that don’t verify the server: ssl_mode require (TLS, any certificate accepted) and disable (no TLS). Without it only verify-full and verify-ca are accepted. The replication user’s password and every row could then reach an impostor or travel in the clear: a private development network only, and refused when SP_DEPLOYMENT=saas. Set it the same on the api role, which checks sources when they are saved. See Postgres replication for DBAs: TLS
SP_CONNECTOR_CDC_BATCH_MAX 2000 Change data capture: changes applied at once. 1 to 50,000
SP_CONNECTOR_CDC_FLUSH_INTERVAL 500ms The longest a change waits before it is applied. 10ms to 1m
SP_CONNECTOR_CDC_STATUS_INTERVAL 10s How often the connector confirms its position to a Postgres source. 1s to 1m
SP_CONNECTOR_CDC_SNAPSHOT_BATCH 5000 Rows fetched at once during a Postgres source’s first copy. 1 to 100,000
SP_CONNECTOR_CDC_LAG_INTERVAL 30s How often a Postgres replication slot’s lag is measured. 5s to 1h
SP_CONNECTOR_CDC_SLOT_LAG_ALERT_BYTES 1073741824 A replication slot further behind than this (1 GiB) raises the alert: the source shows Falling behind, data-source managers are notified, and speculapro_cdc_slot_behind is 1. At least 1 MiB
SP_CONNECTOR_CDC_APPLIED_RETENTION_DAYS 30 How long the ledger of applied changes is kept, which recognises a change read twice. A change replayed after that is still caught by the usual duplicate protection. 1 to 3650. SpeculaPro refuses to start on a value outside any of these ranges

SpeculaPro refuses to start if SP_CONNECTOR_FILE_WORKERS or SP_CONNECTOR_CONCURRENCY is outside 1 to 64, SP_CONNECTOR_MAX_FILE_BYTES is below 1 MiB, or the interval or a timeout is below one second, for example with config: SP_CONNECTOR_FILE_WORKERS must be between 1 and 64, got 0.

Email, text-message and WhatsApp notifications are sent by the worker role (WhatsApp verification codes and test messages by the api role). People choose their own channels in the app (Notifications); these settings decide how messages leave your installation.

Variable Default Meaning
SP_NOTIFY_PUBLIC_URL http://localhost:8080 The address people use to open SpeculaPro, for example https://speculapro.yourbank.com.gh. Every message links here. Outside SP_ENV=development, when the email provider is smtp or spalce, the SMS provider is spalce or the WhatsApp provider is gowa, it must be set to a real address: SpeculaPro refuses to start if it is empty or points at localhost or 127.0.0.1
SP_NOTIFY_EMAIL_PROVIDER log How email is sent: smtp (your mail server), spalce (the Spalce Notification Service), log (written to the log only, for development) or none (not sent)
SP_NOTIFY_SMS_PROVIDER none How text messages are sent: spalce, log (development only) or none. Outside SP_ENV=development, log stops SpeculaPro at start-up
SP_NOTIFY_WHATSAPP_PROVIDER none How WhatsApp messages are sent: gowa (your own GOWA server), log (development only) or none (people can’t choose WhatsApp). Outside SP_ENV=development, log stops SpeculaPro at start-up
SP_NOTIFY_LOG_SHOW_CODES false Development only. The log provider normally masks phone numbers to their last three digits and writes one-time codes as [code]; true prints codes in full, so you can confirm a WhatsApp number on your own machine. With any SP_ENV other than development, SpeculaPro refuses to start with config: SP_NOTIFY_LOG_SHOW_CODES is for development only. The development stack sets it
SP_NOTIFY_DEFAULT_COUNTRY_CODE 233 The country calling code added to a local number someone types without one, so 0244 123 456 becomes +233244123456. Digits only, no +
SP_NOTIFY_FROM_EMAIL [email protected] Sender address. Use an address on your own domain that your mail server is allowed to send from (SPF/DKIM), or messages land in spam
SP_NOTIFY_FROM_NAME SpeculaPro Sender name shown in mail clients
SP_NOTIFY_EMAIL_COMPANY_NAME (empty) Legal name of the company behind the service, shown in the footer of notification emails. Left out while empty
SP_NOTIFY_EMAIL_COMPANY_ADDRESS (empty) Its registered or postal address, on one line, shown after the company name. Left out while empty
SP_NOTIFY_EMAIL_WEBSITE speculapro.com Website named in the email footer
SP_NOTIFY_EMAIL_SUPPORT_EMAIL [email protected] Support address in the email footer (“Need help?”)
SP_NOTIFY_EMAIL_SUPPORT_PHONE (empty) Support phone in international format, shown after the support address. Left out while empty
SP_NOTIFY_INTERVAL 5s How often the worker looks for messages to send
SP_NOTIFY_MAX_ATTEMPTS 6 How many times a message is tried before it is marked failed. Retries wait 30 seconds, then 1, 2, 4 and 8 minutes, doubling up to an hour, so the default gives up after about 15 minutes. If your mail server can be down longer than that, raise it: 10 keeps trying for about 3 hours, 12 for about 5. A logged-out WhatsApp session doesn’t use up these tries: see SP_NOTIFY_WHATSAPP_MAX_WAIT

Notification emails use one designed layout in English and French: see What an email looks like. With smtp each email is sent with a plain-text part and an HTML part; with spalce the HTML part only. The button and the settings link appear only when SP_NOTIFY_PUBLIC_URL is an http or https address. Developers can write every email in both languages to a folder to look at in a browser: cd backend && SP_EMAIL_SAMPLES=<folder> go test ./internal/notify -run TestEmailSamples.

GOWA (when SP_NOTIFY_WHATSAPP_PROVIDER=gowa; see WhatsApp through GOWA):

Variable Default Meaning
SP_NOTIFY_GOWA_BASE_URL (none) Your GOWA server’s address, including GOWA’s APP_BASE_PATH if you set one, for example http://gowa:3000. Required with gowa. Must be https unless SP_NOTIFY_GOWA_ALLOW_PRIVATE=true. No user name or password in it, and no ?query or #fragment. A change is recorded in the audit log when a worker starts with it
SP_NOTIFY_GOWA_USERNAME (empty) The user name from GOWA’s APP_BASIC_AUTH. Required outside development: SpeculaPro refuses to start with a GOWA server that has no user name and password unless SP_ENV=development
SP_NOTIFY_GOWA_PASSWORD (empty) The password from GOWA’s APP_BASIC_AUTH. Keep it in your secret manager. Set both, or (in development only) neither
SP_NOTIFY_GOWA_DEVICE_ID (empty) Sent as X-Device-Id. Needed only when your GOWA server holds more than one WhatsApp device
SP_NOTIFY_GOWA_ALLOW_PRIVATE false true lets SpeculaPro reach GOWA at a private address (RFC 1918 or IPv6 unique-local) or loopback, and over plain http. Link-local and cloud metadata addresses stay refused. Normal when GOWA runs next to SpeculaPro; see the trade-off
SP_NOTIFY_GOWA_TIMEOUT 15s How long to wait for GOWA to answer one request
SP_NOTIFY_GOWA_STATUS_INTERVAL 1m How often the worker checks that the WhatsApp session is still connected and logged in. At least 5s
SP_NOTIFY_WHATSAPP_MAX_WAIT 24h While the WhatsApp session is logged out or its device is missing, messages wait in the outbox, tried again every 30 seconds rising to every 5 minutes, without using up SP_NOTIFY_MAX_ATTEMPTS. A message still waiting this long after it was raised is marked failed. At least 1m

SMTP (when SP_NOTIFY_EMAIL_PROVIDER=smtp):

Variable Default Meaning
SP_NOTIFY_SMTP_HOST (none) Your mail server. Required with smtp, or SpeculaPro won’t start
SP_NOTIFY_SMTP_PORT 587 Usually 587 with starttls, 465 with tls
SP_NOTIFY_SMTP_USERNAME (empty) Leave empty for a relay that doesn’t need a login
SP_NOTIFY_SMTP_PASSWORD (empty) Keep it in your secret manager
SP_NOTIFY_SMTP_SECURITY starttls starttls (upgrade to TLS after connecting; port 587), tls (TLS from the start; port 465) or none (no encryption: only for a relay on the same machine or private network)

Spalce Notification Service (when either provider is spalce; one account covers email and SMS):

Variable Default Meaning
SP_NOTIFY_SPALCE_BASE_URL https://notify.spalce.net Service address
SP_NOTIFY_SPALCE_API_KEY (none) Required with spalce
SP_NOTIFY_SPALCE_MERCHANT_CODE (none) Required with spalce
SP_NOTIFY_SPALCE_COUNTRY_CODE GH Your country, as two letters
SP_NOTIFY_SPALCE_SMS_SENDER_ID SpeculaPro The name text messages come from. It must be a sender ID registered with the SMS gateway, or messages are refused

At start-up SpeculaPro checks these settings and refuses to start with a clear message if a provider name is wrong (config: SP_NOTIFY_EMAIL_PROVIDER must be log, smtp, spalce or none; config: SP_NOTIFY_WHATSAPP_PROVIDER must be log, gowa or none), uses the log provider for SMS or WhatsApp outside development (config: SP_NOTIFY_WHATSAPP_PROVIDER and SP_NOTIFY_SMS_PROVIDER can't be log outside development; choose a provider or none) or a required value is missing (config: SP_NOTIFY_SMTP_HOST is required when SP_NOTIFY_EMAIL_PROVIDER=smtp). The GOWA checks and their messages are listed in WhatsApp through GOWA. How sending works, retries and what you see in the logs: Worker: notifications.

Variable Default Meaning
SP_REDIS_ADDRS localhost:6379 List of addresses. What you list decides the mode (see below)
SP_REDIS_SENTINEL_MASTER (unset) The name of the primary that the Sentinels watch (often mymaster). Setting it switches on Sentinel mode
SP_REDIS_USERNAME (unset) Username (Redis/Valkey ACLs)
SP_REDIS_PASSWORD (unset) Password
SP_REDIS_DB 0 Database number (standalone and Sentinel only; Cluster always uses 0)
SP_REDIS_SENTINEL_USERNAME (unset) Username for the Sentinels themselves, if they require authentication
SP_REDIS_SENTINEL_PASSWORD (unset) Password for the Sentinels themselves
SP_REDIS_POOL_SIZE 0 Connections per pool. 0 means 10 for each CPU the process can use. A process running the decision role opens a second pool of this size for decisions alone, so that other work in the same process can’t take the connections a live decision is waiting for. Raise it on busy decision nodes. In Cluster mode the size applies to each node
SP_REDIS_MIN_IDLE_CONNS 8 Connections kept open and ready in each pool, so a burst doesn’t wait for new connections
SP_REDIS_TLS_ENABLED, SP_REDIS_TLS_CA_FILE, SP_REDIS_TLS_CERT_FILE, SP_REDIS_TLS_KEY_FILE, SP_REDIS_TLS_SERVER_NAME, SP_REDIS_TLS_INSECURE_SKIP_VERIFY as for Kafka TLS settings, with the same meaning as the Kafka ones above

How the mode is chosen:

You set Mode
One address, no SP_REDIS_SENTINEL_MASTER Standalone: one server
SP_REDIS_SENTINEL_MASTER and the Sentinel addresses (usually port 26379) Sentinel: automatic failover between a primary and replicas
Several addresses, no SP_REDIS_SENTINEL_MASTER Cluster: data spread over several primaries
Terminal window
# Sentinel
SP_REDIS_ADDRS=s1:26379,s2:26379,s3:26379
SP_REDIS_SENTINEL_MASTER=mymaster
# Cluster
SP_REDIS_ADDRS=n1:6379,n2:6379,n3:6379

See Redis / Valkey for which to choose.

Tracing is off unless you set an OTLP endpoint. SpeculaPro then sends traces over OTLP/gRPC to your OpenTelemetry Collector (or any backend that accepts OTLP, such as Grafana Tempo or Jaeger). These are the standard OpenTelemetry variables, without the SP_ prefix, so they work the way your observability team already knows. What a trace contains, and what it doesn’t: Monitoring: tracing.

Variable Default Meaning
OTEL_EXPORTER_OTLP_ENDPOINT (unset: tracing off) Your collector’s OTLP/gRPC address, for example https://otel-collector.internal:4317. Use http:// only for a collector on the same machine or a trusted network
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT (unset) Same, for traces only. Setting either one switches tracing on
OTEL_EXPORTER_OTLP_HEADERS (unset) Headers sent to the collector, as key=value pairs separated by commas, for example authorization=Bearer <token>. Treat it as a secret
OTEL_SERVICE_NAME speculapro The service name traces are filed under. Set it per role group (for example speculapro-decision) if you want to tell them apart
OTEL_TRACES_SAMPLER parentbased_always_on Which requests are traced. The default traces every one
OTEL_TRACES_SAMPLER_ARG (unset) The sampler’s setting, such as the fraction to keep
OTEL_RESOURCE_ATTRIBUTES (unset) Extra labels for every trace, for example deployment.environment=production

Other standard OTEL_EXPORTER_OTLP_* settings, such as OTEL_EXPORTER_OTLP_CERTIFICATE for a private certificate authority, also apply.

If the tracing settings are wrong (for example an unreadable endpoint), SpeculaPro stops at start-up with telemetry: …. When tracing starts, the log says tracing enabled.

Terminal window
SP_ENV=production
SP_ROLES=all
SP_LOG_FORMAT=json
SP_HTTP_ADDR=127.0.0.1:8080
SP_HTTP_TRUST_PROXY=true # behind your own reverse proxy
SP_SECURITY_MASTER_KEYS=1:<from keys generate>
SP_SECURITY_BLIND_INDEX_KEY=<from keys generate>
SP_DB_URL=postgres://speculapro_app:<password>@pg.internal:5432/speculapro?sslmode=verify-full&sslrootcert=/etc/speculapro/pg-ca.pem
SP_DB_SYSTEM_URL=postgres://speculapro_system:<password>@pg.internal:5432/speculapro?sslmode=verify-full&sslrootcert=/etc/speculapro/pg-ca.pem
SP_DB_MAX_CONNS=20
SP_KAFKA_BROKERS=kafka1.internal:9092,kafka2.internal:9092,kafka3.internal:9092
SP_KAFKA_REPLICATION_FACTOR=3
SP_KAFKA_SASL_MECHANISM=SCRAM-SHA-512
SP_KAFKA_USERNAME=speculapro
SP_KAFKA_PASSWORD=<password>
SP_KAFKA_TLS_ENABLED=true
SP_KAFKA_TLS_CA_FILE=/etc/speculapro/kafka-ca.pem
SP_REDIS_ADDRS=s1.internal:26379,s2.internal:26379,s3.internal:26379
SP_REDIS_SENTINEL_MASTER=mymaster
SP_REDIS_PASSWORD=<password>
SP_REDIS_TLS_ENABLED=true
SP_NOTIFY_PUBLIC_URL=https://speculapro.yourbank.com.gh
SP_NOTIFY_EMAIL_PROVIDER=smtp
SP_NOTIFY_FROM_EMAIL=[email protected]
SP_NOTIFY_SMTP_HOST=smtp.yourbank.com.gh
SP_NOTIFY_SMTP_USERNAME=speculapro
SP_NOTIFY_SMTP_PASSWORD=<password>
SP_METRICS_ADDR=10.0.1.15:9090 # an internal interface only