Single-org Salesforce connection and authentication. Set this OR orgs (exactly one). Omit when using the multi-org orgs list.
orgs
list[OrgConfig]
no
Multi-org list: ingest several Salesforce orgs from one process into one shared sink. Set this OR top-level salesforce (exactly one). Each entry carries its own salesforce + sources; the sink/state/service stay shared.
sources
SourcesConfig
no
Single-org event source selection and settings. Ignored (and rejected if customized) when orgs is used — put per-org sources under each org.
Optional: derived from environment when omitted; set to a custom My Domain URL to override. An explicit value always takes precedence over the environment-derived default.
environment
Literal['production', 'sandbox']
production
no
production | sandbox — derives login_url when login_url is unset; an explicit login_url takes precedence.
client_credentials flow secret (injectable from file/env like the key). Required when auth_mode is client_credentials; unused for jwt_bearer.
client_secret_file
Path
(secret)
no
File path to the client_credentials secret; required when auth_mode: client_credentials.
username
str
svc@example.com
no
Integration user (pre-authorised on the app's Policies tab). Required for jwt_bearer (the JWT sub claim); not needed when auth_mode: client_credentials.
private_key_file
Path
(secret)
no
File path to the jwt_bearer private key; not needed when auth_mode: client_credentials.
private_key
SecretStr
(secret)
no
jwt_bearer private key, inline (alternative to private_key_file).
api_version
str
"60.0"
no
Salesforce REST/SOAP API version to target.
org_id
str
null
no
Set this to keep the app on the api scope alone; left null it is auto-resolved via /services/oauth2/userinfo (which then needs the openid scope).
token_ttl
Duration
1h
no
Assumed access-token lifetime (Salesforce returns no expires_in for these flows; the real lifetime is the org's session timeout, which can be as short as 15m). Refresh is also reactive on 401, so this only tunes proactive re-mint cadence.
Bypass the fail-fast overlap guard that refuses to start when one event category is enabled on more than one source.
transform_salt
SecretStr
(secret)
no
Deployment-wide salt for hash transform rules (stable pseudonyms that still correlate within this deployment). Strongly recommended whenever a hash rule is configured — unsalted hashes of low-entropy values (IPs, usernames) are trivially reversible by table lookup.
transform_salt_file
Path
/etc/sf2loki/secrets/transform-salt
no
File path to the transform hash salt (alternative to transform_salt).
Flow-control batch size (1-100; Salesforce clamps at 100 and returns INVALID_ARGUMENT when over-asked).
replay_preset
Literal['LATEST', 'EARLIEST', 'CUSTOM']
CUSTOM
no
Replay position; falls back to LATEST when no stored replay_id.
topics
list[str]
[/event/LoginEventStream, /event/ApiAnomalyEvent]
no
Explicit topics, or "*" to DISCOVER and subscribe to every RTEM stream the org exposes (the *EventStream channels), re-filtered by include/exclude.
include
list[str]
["*"]
no
Operator inclusion globs applied to discovered/explicit topics.
exclude
list[str]
[]
no
Operator exclusion globs applied to discovered/explicit topics.
rediscovery_interval
Duration
1h
no
How often to re-run wildcard ("*") topic discovery while running, so channels enabled after startup are picked up without a restart. 0 disables (discovery then runs only at startup).
Opt-in lossy volume control: topic glob -> keep fraction (0-1], first matching glob wins. Sampling is deterministic by replay_id hash, so a replay keeps exactly the same rows (Loki dedup stays intact). Sampled-out events still advance checkpoints.
transforms
list[TransformRule]
no
Redaction/filter rules applied to each decoded event payload before shaping (see TransformRule).
bridge_max_bytes
int
134217728
no
Approximate byte budget for the Pub/Sub source's internal topic->events bridge queue, which sits UPSTREAM of the pipeline's sink.loki.batch.queue_max_bytes. Topic tasks block (structural backpressure, propagated to flow-control credits) once the bridged-but-undrained bytes exceed this, so a sink outage can't balloon per-org buffering past the count bound. Default 128 MiB; 0 disables byte accounting on the bridge.
The event object API name to poll (e.g. LoginEvent).
timestamp_field
str
EventDate
no
The field used as the per-row event time for polling/checkpointing.
poll_interval
Duration
5m
no
How often to poll this object.
lookback
Duration
1h
no
Initial window to fetch on first run (no checkpoint).
sample
float
1.0
no
Opt-in lossy volume control: keep fraction (0-1] of rows, deterministic by record Id hash (replay-stable). 1.0 keeps everything.
big_object
bool
false
no
Set true for Salesforce Big Objects (the stored RTEM event family: LoginEvent, ApiEvent, FileEventStore, *EventStore, ...). Big Objects reject ORDER BY ASC, so the source drains them newest-first (ORDER BY timestamp_field DESC) with a ratcheting upper bound and re-sorts each cycle's window ascending before emitting. Leave false for standard and custom objects (LoginHistory, MyAudit__c), which use the ASC path.
max_catchup_records
int
200000
no
Cap on records collected into memory per big_object DESC drain cycle. The drain buffers a cycle's window in memory to re-sort it ascending; a post-outage catch-up over a large gap would otherwise be unbounded and can OOM. When the cap is hit the drain emits that (internally sorted) segment and ratchets its upper bound down so catch-up proceeds in bounded chunks across cycles. 0 = unbounded (pre-cap behaviour). Ignored on the ASC path (streamed page-by-page, already bounded).
Daily (settled, ~1d lag) | Hourly (fresher, needs the Event Monitoring hourly opt-in in Setup) — pick ONE. Many orgs generate ONLY Daily files; check before Hourly.
event_types
list[EventLogFileTypeConfig]
no
ELF EventTypes to ingest (required when enabled — there is no sensible "all" default given ~70 types and the either/or-per-category model). Each item is a bare string (e.g. "Login") or a per-type object (name + optional structured_metadata_fields/labels). Use "*" to discover and ingest every EventType the org produces for this interval.
exclude
list[str]
[]
no
EventTypes to skip when the wildcard "*" is used (e.g. a category served by another source, or a high-volume type you don't want). Ignored otherwise.
poll_interval
Duration
1h
no
How often to list new files (Daily files land ~once/day).
lookback
Duration
1d
no
Initial window on first run (no checkpoint); reach back far enough to catch the last few settled Daily files.
timestamp_column
str
TIMESTAMP_DERIVED
no
Per-row event time column.
page_size
int
1000
no
SOQL LIMIT for the file-listing query.
settle_window
Duration
0s
no
Skip files whose CreatedDate is newer than now-settle_window, so we don't pull a half-written hourly CSV whose tail rows would then be skipped when the watermark passes it. Left unset it defaults to 5m for interval: Hourly (Hourly blobs can be listed while server-side incomplete) and 0 for Daily (files land long after the day closes). Set explicitly to override either.
download_max_age
Duration
1d
no
A file whose body keeps failing to download and is older than this is abandoned (checkpoint advances past it) so a permanently-missing file can't wedge the watermark forever. Files younger than this are retried.
transforms
list[TransformRule]
no
Redaction/filter rules applied to each CSV row before shaping (see TransformRule).
concurrency
int
4
no
Event types processed concurrently per poll cycle (per-type ordering and checkpoints are unaffected — types are independent). Peak memory is roughly concurrency x 8 MiB of download spool.
Initial window to fetch on first run (no checkpoint).
users
list[str]
[integration@example.com]
no
Salesforce usernames whose logs to ingest (matched via LogUser.Username). Empty = every ApexLog visible to the integration user.
max_body_bytes
int
5242880
no
Skip the body download for logs whose LogLength exceeds this (the metadata line is still shipped, flagged body_skipped); the per-line cap (sink.loki.batch.max_line_bytes) truncates whatever is shipped.
sample
float
1.0
no
Opt-in lossy volume control: keep fraction (0-1] of logs, deterministic by log Id hash (replay-stable). 1.0 keeps everything.
transforms
list[TransformRule]
no
Redaction/filter rules applied to each log's metadata before shaping.
Flush the batch after this much time, regardless of size.
max_line_bytes
int
262144
no
Per-line UTF-8 byte cap; lines longer than this are truncated (with a marker) before push so one oversized row can't 400 its whole batch. Mirrors Loki's server-side max_line_size default (256 KiB). 0 disables.
queue_maxsize
int
10000
no
Entry-count bound of each internal source->sink lane queue. Producers block (structural backpressure) when full. Applied PER LANE (streaming vs bulk), so the worst-case entry count is queue_maxsize x number-of-lanes (<= 2).
queue_max_bytes
int
268435456
no
Approximate byte budget for queued entries, applied PER LANE (streaming vs bulk). Producers on a lane block when its budget is exceeded, even if the entry-count bound is not reached. Worst-case buffered memory during a sink outage is therefore queue_max_bytes x number-of-lanes (<= 2x). Default 256 MiB; 0 disables accounting.
Token-bucket cap on pushed lines/second; 0 disables.
max_bytes_per_second
float
0
no
Token-bucket cap on pushed (pre-compression) bytes/second; 0 disables.
daily_byte_budget
int
0
no
Maximum pre-compression bytes pushed per UTC day; 0 disables. The used counter persists in the state store, so restarts don't reset it. WARN at 80%, ERROR + budget_action at 100%.
budget_action
Literal['pause', 'drop']
pause
no
What to do when the daily budget is exhausted: pause (hold pushes + checkpoints until the next UTC day; data delayed, never lost, readiness reports degraded) | drop (keep running, discard over-budget entries).
noop (single instance) | file_lease (active-passive via a shared lease file) | k8s_lease (active-passive via a Kubernetes Lease; needs the sf2loki[k8s] extra).
Grace period allowed for in-flight work to finish on shutdown.
unready_after_sink_failing
Duration
15m
no
/readyz reports 503 when Loki pushes have been failing continuously for this long (data is retried and safe, but the instance is degraded and an orchestrator should surface it). 0 disables the readiness degradation.
Full OTLP/HTTP metrics URL. For Grafana Cloud this is the stack OTLP gateway, e.g. https://otlp-gateway-.grafana.net/otlp/v1/metrics; for a local Alloy otelcol.receiver.otlp, e.g. http://alloy:4318/v1/metrics.
auth
Literal['basic', 'none']
basic
no
Auth for the OTLP endpoint. "basic" sends Authorization: Basic base64(user:token); "none" sends none (e.g. in-cluster Alloy). For basic, the credentials default to the Loki sink's tenant_id/auth_token when left blank — Grafana Cloud uses one stack credential for both Loki and OTLP.
basic_auth_user
str
""
no
Basic-auth username; defaults to the Loki sink's tenant_id when blank.
basic_auth_token
SecretStr
(secret)
no
Basic-auth token, inline; defaults to the Loki sink's auth_token when blank.
basic_auth_token_file
Path
(secret)
no
File path to the basic-auth token (alternative to basic_auth_token).
headers
dict[str, str]
{}
no
Explicit headers, merged on top of any computed Authorization header. Values support ${ENV} interpolation at config load.
export_interval
Duration
1m
no
How often to export accumulated metrics via OTLP.
resource_attributes
dict[str, str]
{}
no
Extra OTel resource attributes merged onto the defaults (service.name, etc.).