forked from StellarGateLabs/StellarGate
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path.env.example
More file actions
243 lines (222 loc) · 12.6 KB
/
Copy path.env.example
File metadata and controls
243 lines (222 loc) · 12.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# Server
PORT=3000
# Reject request bodies larger than this many bytes before they reach a
# handler. Default: 262144 (256 KiB), generous for the current API.
MAX_BODY_BYTES=262144
# How long (seconds) shutdown waits for background tasks to drain before
# forcing exit. Must exceed the orchestrator's own termination grace period
# (Kubernetes terminationGracePeriodSeconds, Docker stop_grace_period) or the
# orchestrator can SIGKILL mid-drain — see "Shutdown grace" in DEPLOYMENT.md.
# Default: 30.
SHUTDOWN_GRACE_SECS=30
# Stellar network: "testnet" or "public"
STELLAR_NETWORK=testnet
STELLAR_HORIZON_URL=https://horizon-testnet.stellar.org
# Gateway wallet — the account that receives payments. The gateway only ever
# watches this address for incoming payments; it never signs or submits
# transactions, so no secret key is needed or accepted.
STELLAR_GATEWAY_PUBLIC=GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# Assets to accept. Only native XLM may omit an issuer; every other asset is
# CODE:ISSUER. `ACCEPTED_ASSETS=XLM,USDC` (no issuer) is a boot failure — that
# typo used to let native XLM settle a USDC-priced intent.
# ACCEPTED_ASSETS=XLM,USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5
# Database — sqlx connection string (NOT a file path)
DATABASE_URL=sqlite:stellargate.db
# Payment detection mode:
# stream — subscribe to Horizon's SSE payment stream for ~1s settlement,
# with the poller running alongside as a reconciler (default)
# poll — interval polling only (no streaming connection)
STELLAR_LISTENER_MODE=stream
# How often (seconds) the Horizon poller reconciles pending payments.
# In stream mode this is the fallback that catches events missed on reconnect.
POLL_INTERVAL_SECS=10
# How long (seconds) the SSE stream connection may sit idle — no events, no
# keepalives — before it is considered dead and reconnected. A value too large
# means a silently-wedged stream goes undetected until the next poll cycle;
# too small causes unnecessary reconnects on a quiet network. Default: 30.
STREAM_IDLE_TIMEOUT_SECS=30
# How many payment records to request per Horizon page, both while catching
# up and during steady-state polling. Directly controls how long an
# uninterruptible poll cycle runs. Default: 200.
HORIZON_PAGE_LIMIT=200
# Timeout (seconds) for outbound Horizon HTTP requests — payment polling,
# trustline checks, and the readiness probe. Default: 30s. Operators on a
# low-latency private Horizon may want 5s while those on congested public
# nodes may want 60s. Must be >0.
HORIZON_TIMEOUT_SECS=30
# Multiplier on POLL_INTERVAL_SECS that may elapse without a successful poll
# or stream event before /ready reports the payment-detection cursor stale
# (503). A healthy poller cycles on the poll interval, so this only trips when
# the poller died or the stream wedged. Default: 3.
CURSOR_STALENESS_MULTIPLE=3
# Maximum Horizon pages (200 records each) a single poll cycle walks before it
# yields and waits for the next tick. This bounds how long one catch-up can
# monopolise the poller task: the cursor is checkpointed at every page boundary,
# so the next cycle resumes exactly where this one stopped. Set to 0 for
# unlimited (one cycle runs until fully caught up).
POLL_MAX_PAGES_PER_CYCLE=50
# How long (seconds) a payment intent stays pending before it is expired,
# counted from its created_at. Default: 3600 (1 hour).
PAYMENT_TTL_SECS=3600
# How many overdue intents the expiry sweeper transitions per sweep. Batching
# keeps each sweep's write short — SQLite has a single writer, so one unbounded
# sweep over a large backlog would stall payment writes. Default: 500.
EXPIRY_BATCH_SIZE=500
# Webhooks — secret signs the X-StellarGate-Signature header (HMAC-SHA256).
# Must be >= 32 characters.
#
# NOTE: `REPLACE_ME_webhook_signing_secret` is an INTENTIONAL placeholder that
# boot explicitly REJECTS (see src/config.rs validate_webhook_secret). This is
# correct security behaviour — the gateway refuses to start with a known
# placeholder so a misconfigured deploy fails loudly rather than silently
# signing webhooks with a guessable key.
#
# Replace with a randomly generated secret before running, e.g.:
# openssl rand -hex 32
WEBHOOK_SECRET=REPLACE_ME_webhook_signing_secret
WEBHOOK_RETRY_ATTEMPTS=3
# Base delay between inline retries. This is the FIRST step of an exponential
# schedule (base * 2^(attempt-1), capped by WEBHOOK_RETRY_MAX_DELAY_MS), with
# jitter applied so deliveries that fail together do not retry together.
WEBHOOK_RETRY_DELAY_MS=5000
# Ceiling on a single inline retry delay, so the doubling above stays bounded.
WEBHOOK_RETRY_MAX_DELAY_MS=60000
# URL schemes accepted for webhook_url on POST /payments (comma-separated).
# On STELLAR_NETWORK=public, HTTPS is enforced regardless of this setting.
# Including "http" here logs a startup warning, on every network — see
# SECURITY.md's "Webhook Payload Exposure" section.
ALLOWED_WEBHOOK_SCHEMES=https
# !! WARNING — DEVELOPMENT AND TEST USE ONLY !!
# When true, bypasses the SSRF guard that prevents webhook delivery to
# loopback, link-local, private, and reserved IP ranges (including the cloud
# metadata address 169.254.169.254). This MUST NEVER be set to true in
# production — doing so allows webhook delivery to internal infrastructure and
# metadata endpoints, which is a serious SSRF vulnerability. Set to true only
# in local dev or test environments where the receiver is on localhost.
WEBHOOK_ALLOW_PRIVATE_TARGETS=false
# How much detail an outbound webhook payload carries: "minimal" (event,
# payment_id, status, updated_at) or "full" (adds merchant_id, amount,
# paid_amount, asset, asset_issuer, tx_hash, delta). Default: minimal.
WEBHOOK_PAYLOAD_DETAIL=minimal
# Per-attempt timeout for outbound webhook POST requests (seconds).
# Each retry is bounded independently. Default: 10s.
WEBHOOK_TIMEOUT_SECS=10
# Redrive worker — re-attempts deliveries left pending/failed by a crash or
# an exhausted retry loop, independently of the inline retries above.
# How often (seconds) the worker scans for stuck deliveries.
WEBHOOK_REDRIVE_INTERVAL_SECS=30
# Maximum redrive HTTP attempts in flight at once.
WEBHOOK_REDRIVE_CONCURRENCY=4
# Total attempts (inline + redrive) before a delivery is left failed permanently.
WEBHOOK_REDRIVE_MAX_ATTEMPTS=8
# How long (seconds) a delivery must sit idle since its last attempt before
# the redrive worker will touch it, so it never races a still-in-flight
# inline delivery for the same row. Also the floor under the backoff below.
# Valid range: 1-86400 seconds.
WEBHOOK_REDRIVE_GRACE_SECS=60
# Exponential backoff (seconds) applied to redrive attempts once a delivery
# has failed at least once: initial * 2^(attempts-1), capped at max.
# Initial may be 0 to disable growth; max must be positive. Both are capped at
# 86400 seconds, and max must be greater than or equal to initial.
WEBHOOK_REDRIVE_BACKOFF_INITIAL_SECS=30
WEBHOOK_REDRIVE_BACKOFF_MAX_SECS=900
# Random extra delay (seconds, 0-N per row) added to redrive eligibility.
# Backoff alone does not desynchronise a batch that failed together: those rows
# share an attempts value and a last_attempt, so their next attempts coincide.
# Valid range: 0-86400 seconds. Set to 0 to disable.
WEBHOOK_REDRIVE_JITTER_SECS=30
# CORS — comma-separated list of allowed origins.
# Required when STELLAR_NETWORK=public; omit only for local dev (testnet).
# Example: CORS_ALLOWED_ORIGINS=https://app.example.com,https://www.example.com
CORS_ALLOWED_ORIGINS=
# SSRF guard — webhook target filtering.
# When a merchant sets a webhook_url, the gateway resolves its hostname and
# rejects URLs that resolve to loopback (127.x), link-local (169.254.x,
# including the cloud instance-metadata endpoint), private ranges, or other
# reserved addresses. This prevents an authenticated merchant from using
# webhook delivery as a server-side request forgery primitive to reach internal
# services or harvest cloud credentials.
#
# WEBHOOK_ALLOW_PRIVATE_TARGETS bypasses this guard. It exists ONLY for local
# development and integration tests that target a loopback mock server.
# Setting it to true on STELLAR_NETWORK=public aborts boot. Setting it to true
# on any other network logs a prominent startup warning.
# Do NOT set this in a deployment that handles real payments.
# WEBHOOK_ALLOW_PRIVATE_TARGETS=false
# Rate limiting — requests per second per IP for POST /payments and POST /merchants.
# Must be > 0; boot fails otherwise. There is no "disabled" value — 0 would
# otherwise be silently clamped up to the most aggressive limit (1 req/sec)
# rather than turning limiting off.
RATE_LIMIT_REQUESTS_PER_SEC=10
# Maximum number of distinct rate-limiter keys tracked at once (IP limiter and
# per-merchant limiter each use this cap). Once reached, the least-recently-
# used entry is evicted. Behind a proxy fronting many client IPs the default
# may evict constantly, losing limiter state; behind a single proxy IP it is
# mostly wasted slots. Default: 10000.
RATE_LIMITER_MAX_KEYS=10000
# How long (seconds) a per-key rate limiter is retained after its last access
# before being reclaimed. Default: 60.
RATE_LIMITER_IDLE_TTL_SECS=60
# Trusted proxies — comma-separated CIDR blocks whose X-Forwarded-For /
# X-Real-IP headers are honored for rate-limit bucketing and auth-log
# attribution. Those headers are client-supplied, so they are ignored unless
# the request's socket peer is one of these proxies. Unset (the default)
# means no proxy is trusted and the headers are always ignored — the safe
# choice for a directly-exposed gateway.
# Example behind a reverse proxy: TRUSTED_PROXY_CIDRS=10.0.0.0/8,192.168.0.0/16
TRUSTED_PROXY_CIDRS=
# Per-request timeout for the whole API (seconds). A request whose handler
# hasn't produced a response within this window is aborted with 408. Default: 30s.
REQUEST_TIMEOUT_SECS=30
# Pagination for list endpoints (GET /payments, /payments/webhooks, …).
# Default page size when the caller does not pass `limit`.
PAGINATION_DEFAULT_LIMIT=20
# Upper bound `limit` is clamped to, regardless of what the caller requests.
PAGINATION_MAX_LIMIT=100
# Maximum / minimum amount POST /payments accepts, in the asset's own units.
# A bare number applies to every asset; CODE:AMOUNT pins a bound to one asset
# specifically and always wins over the default; mix both with commas.
# Example: MAX_PAYMENT_AMOUNT=100000,USDC:50000
# Unset (the default) means no bound beyond i64 overflow in parse_stroops.
MAX_PAYMENT_AMOUNT=
MIN_PAYMENT_AMOUNT=
# Admin secret required (via the `X-Admin-Secret` header) to call POST /merchants.
# Leave unset (or empty) to disable merchant provisioning over HTTP entirely.
#
# When set, must be at least 32 characters. Known placeholder values (admin,
# changeme, secret, …) are rejected at boot.
# Generate a strong value: openssl rand -hex 32
ADMIN_PROVISIONING_SECRET=
# SQLite connection pool tuning
# WAL mode + busy_timeout prevent "database is locked" under concurrent writes.
DB_POOL_MAX_CONNECTIONS=10
DB_BUSY_TIMEOUT_MS=5000
# WAL auto-checkpoint threshold (pages). SQLite checkpoints when a write
# transaction ends and the WAL exceeds this size, unless a reader holds an old
# snapshot. Default: 1000 pages (~4 MiB at 4 KiB page size).
SQLITE_WAL_AUTOCHECKPOINT=1000
# Maximum -wal file size (bytes) before SQLite truncates it on the next
# successful checkpoint, providing a hard ceiling even when checkpoints are
# starved by long-lived readers. Default: 67108864 (64 MiB).
SQLITE_JOURNAL_SIZE_LIMIT=67108864
# Page cache size: negative = pages, positive = KiB. Default: -2000 (~8 MiB
# for a 4 KiB page size). Raising this reduces disk I/O on index-heavy queries
# at the cost of resident memory.
SQLITE_CACHE_SIZE=-2000
# Retention — bounds table growth. Both tables below grow with traffic and
# have no natural limit; on a single-volume deployment a full disk takes the
# gateway down. Set a window to 0 to keep those rows forever.
RETENTION_INTERVAL_SECS=3600
# Terminal (delivered/failed) webhook delivery rows. Pending rows are never
# pruned — the redrive worker still owns them.
WEBHOOK_DELIVERY_RETENTION_DAYS=30
# Idempotency keys only need to outlive the window a client might retry in.
IDEMPOTENCY_RETENTION_DAYS=7
# Rows removed (or compacted) per retention DELETE/UPDATE statement. Batching
# keeps each write lock short — SQLite has a single writer, so one unbounded
# statement over a large table would stall payment writes. Default: 500.
DB_PRUNE_BATCH_SIZE=500
# Upper bound on rows removed per table per retention cycle. Without this, the
# first run against a large backlog would delete indefinitely, monopolising
# the single writer; whatever is left is picked up next cycle. Default: 50000.
RETENTION_MAX_ROWS_PER_CYCLE=50000