-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path.env.production.example
More file actions
251 lines (225 loc) · 12.5 KB
/
Copy path.env.production.example
File metadata and controls
251 lines (225 loc) · 12.5 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
244
245
246
247
248
249
250
251
# =============================================================================
# Prova backend — PRODUCTION template
# =============================================================================
#
# Copy to `.env` and fill in. That file is gitignored; this one holds placeholders
# only and is safe to commit.
#
# cp .env.production.example .env
#
# The name matters: docker-compose reads `.env` and nothing else. Copying to
# `.env.production` leaves every value below unset, and the stack starts on
# development defaults without complaining.
#
# Every value marked REQUIRED has no safe default. Where a default exists it is
# a development default, and shipping it is worse than leaving the value empty —
# the app would start and behave insecurely rather than refuse.
#
# QUOTE any value containing a space, "#", or quotes. Unquoted, `#` starts a
# comment and a space ends the value: both have silently truncated a secret in
# this project before.
#
# -----------------------------------------------------------------------------
# DEPLOYING TO TESTNET FIRST? Only four values actually need your attention.
# -----------------------------------------------------------------------------
# RELAYER_KEY a raw S... secret, not a CLI identity name — or every
# send fails at submission
# PROVER_BIN the path inside the container — or folding dies and
# nothing ever becomes spendable
# COMPLIANCE_TOKEN or /ops is open to anyone who finds the URL
# SMTP_* or sign-in cannot send a code
#
# Everything else can keep its current development value while you are on
# testnet with assets that have no worth. In particular POOL_SETUP_SEED=42 is
# fine there: forging proofs would mint test tokens nobody can spend. It becomes
# the single hardest blocker the day real money is involved, and not before.
#
# Deploy, learn the path, then harden. The list below marks what changes when
# you move to mainnet and real users.
# =============================================================================
# RUNTIME
# =============================================================================
APP_ENV=production
PORT=8080
# all | api | indexer
# all — one process does everything. Correct for a single server.
# api — HTTP only. Safe to run several behind a load balancer.
# indexer — chain indexer + folder. Run EXACTLY ONE, ever.
#
# The folder batches queued notes into the Merkle tree; until it runs, deposits
# and change stay "confirming" and cannot be spent. Two of them competing over
# the same queue is a mess to debug.
RUN_MODE=all
# Set true ONLY when a proxy you control (Caddy, nginx, ALB) sets X-Forwarded-For.
# Left false behind a proxy, every request appears to come from the proxy's own
# address: the per-IP rate limiter then sees one client and throttles everybody
# together. Set true where nothing sanitises the header and any caller can spoof
# their way past the limiter.
TRUST_PROXY_HEADERS=true
# =============================================================================
# DATA STORES [REQUIRED]
# =============================================================================
# Same machine, via the docker-compose you already run, is the right first
# answer: fewer accounts, no network hop, and the compose file is already
# working. `postgres` and `redis` below are the compose service names.
#
# Move Postgres to managed (Neon / Supabase) when there are real users — the KYC
# audit log is the one table whose loss cannot be recovered by re-running
# anything, and that is what you are buying backups for. It is a change of this
# one line plus a dump and restore. Put it in the SAME REGION as this service:
# the indexer writes continuously.
DATABASE_URL=postgres://prova:CHANGE_ME@postgres:5432/prova?sslmode=disable
# true ONLY for a transaction-mode pooler (Supabase's pooled endpoint, pgBouncer),
# which cannot use prepared statements. Direct/session connections: leave false.
# Run migrations over the DIRECT connection, not the pooled one.
DB_SIMPLE_PROTOCOL=false
# Backs the rate limiter, so limits hold across replicas rather than per-instance.
# The compose service. Redis holds nothing that needs to survive a restart, so
# there is no reason to pay anyone to host it.
REDIS_URL=redis://redis:6379/0
# =============================================================================
# STELLAR NETWORK
# =============================================================================
# Staying on testnet is a legitimate production choice while the corridor has no
# licensed anchor. Moving to mainnet means redeploying both contracts and putting
# the new ids below — the testnet ids are meaningless there.
STELLAR_NETWORK=testnet
SOROBAN_RPC_URL=https://soroban-testnet.stellar.org
HORIZON_URL=https://horizon-testnet.stellar.org
# Absolute path inside the container. The image installs it here.
STELLAR_BIN=/usr/local/bin/stellar
# From contracts/DEPLOYMENTS.md, or the output of scripts/deploy_*.sh.
CONTRACT_ID=C...
POOL_CONTRACT_ID=C...
# =============================================================================
# RELAYER [REQUIRED]
# =============================================================================
# The account that submits every private transfer, so users' own Stellar accounts
# are never recorded alongside their spends. It pays fees, so it must stay funded.
#
# MUST be a raw secret key (S...), NOT a named CLI identity like "prova-test".
# A name is resolved from ~/.config/stellar on the machine that created it; that
# directory does not exist in a container, and every send fails at submission.
#
# Generate: stellar keys generate prova-relayer --network testnet
# stellar keys show prova-relayer # prints the S... secret
# Fund: https://friendbot.stellar.org/?addr=<the G... address>
RELAYER_KEY=S...
# =============================================================================
# PROVER / FOLDER [REQUIRED]
# =============================================================================
# Absolute path to prova-prover inside the container. prover-init builds it once
# and publishes it into the shared `prover-bin` volume, which api and indexer both
# mount at /opt/prover — so this is where it actually is, not /usr/local/bin.
#
# Getting it wrong fails silently and completely: the folder wakes every few
# seconds, cannot produce a proof, and no note is ever added to the tree. Balances
# sit at "confirming" forever and every send fails, while the API stays healthy.
PROVER_BIN=/opt/prover/prova-prover
# ⚠️ A MAINNET BLOCKER — and only a mainnet blocker.
#
# The proving and verifying keys are derived deterministically from this seed.
# Development uses 42, a public constant, so anyone who knows it can derive the
# toxic waste and forge proofs. On testnet that mints tokens with no worth, which
# is why 42 is perfectly fine here.
#
# Before real money: the seed must come from a public Powers of Tau ceremony and
# the contract's embedded verification key updated to its output. There is no
# value you can invent that makes this safe — see Docs/implementation-guide.md.
POOL_SETUP_SEED=42
# Where the fold proving key is cached. /tmp is fine — it is regenerated at boot
# in a few seconds. A mounted volume avoids that pause.
POOL_FOLD_KEY_CACHE=/tmp/prova-fold-pk.bin
# How often queued notes are folded into the tree. Lower means money becomes
# spendable sooner and more CPU is spent proving.
POOL_FOLD_INTERVAL_SECONDS=8
# How many ledgers back a FRESH indexer scan starts from (no stored position yet). Once it has a
# position it just resumes, so this only matters on a new database.
#
# Set this when you point a NEW backend at an EXISTING pool. The default reaches back ~27 hours;
# deposits older than that are never replayed, so the tree stays empty, /pool/path returns nothing,
# and wallets show a cached balance they cannot spend — a failure with no error message anywhere.
#
# The ceiling is not this setting: Soroban RPC only serves events for a rolling ~7-day window
# (~120,000 testnet ledgers). Notes older than that cannot be recovered from the chain at all, at
# any value. 120000 asks for everything the RPC still has.
INDEXER_LOOKBACK_LEDGERS=20000
# =============================================================================
# ANCHOR / KYC [REQUIRED]
# =============================================================================
# Signs KYC credentials. The circuit checks a signature from this key, so it is
# the root of trust for "this person is verified".
#
# On testnet, reusing your development key is fine — it signs credentials for
# accounts holding worthless assets. Generate a FRESH one before real users and
# keep it in a secret manager:
# openssl rand -hex 32
#
# After changing it, publish the new public key on-chain with `set_anchor`, or
# every credential signed by it is rejected. Record only the public x/y in
# contracts/DEPLOYMENTS.md.
ANCHOR_SEED=<64 hex characters>
# The anchor whose TOML is read for the settlement asset. testanchor.stellar.org
# is SDF's sandbox — replace with the licensed anchor's domain when there is one.
ANCHOR_HOME_DOMAIN=testanchor.stellar.org
ANCHOR_ASSET=XLM
# Issuer for a non-native asset. Leave EMPTY for XLM: the native asset has no
# issuer, and setting one here would describe a different asset entirely.
ANCHOR_ASSET_ISSUER=
# simulated | anchor. Production uses the real rails.
DEPOSIT_MODE=anchor
# Every submission goes to a human reviewer. Leave true until a licensed
# verification vendor is integrated: nothing else in the pipeline inspects a
# document, so auto-approval would mean telling people they are verified on the
# strength of no check at all.
KYC_MANUAL_REVIEW=true
KYC_MOCK_DELAY_SECONDS=4
# Bearer token for POST /kyc/verifications/{id}/decide and everything under /ops
# — the review queue and the support inbox the web console reads.
#
# EMPTY MEANS THE CHECK IS SKIPPED. Deployed empty, anyone who finds the URL can
# approve their own KYC. The web app's COMPLIANCE_TOKEN must match this exactly.
# openssl rand -hex 32
COMPLIANCE_TOKEN=<64 hex characters> # [REQUIRED]
# HMAC-SHA256 secret a verification vendor signs its webhook with. Empty skips
# signature checking, which means trusting an unsigned "this person is approved".
KYC_WEBHOOK_SECRET=
# =============================================================================
# SIGN-IN [REQUIRED]
# =============================================================================
# Email sign-in has NO fixed-code fallback: with SMTP unset, /auth/otp/* refuse in
# every environment rather than accepting a code printed in the source.
#
# AUTH_MODE only governs the KYC phone step now, which has no SMS provider yet:
# development — that step accepts DEV_OTP. Never deploy this.
# production — that step refuses until a real SMS provider exists.
AUTH_MODE=production
# The code accepted by the KYC phone step while AUTH_MODE=development. Inert in
# production, and kept only so the value is never implicitly the built-in default.
DEV_OTP=
# Gmail works for testnet (~500/day) but sign-in codes from a plain Gmail address
# land in spam. For real users use a transactional provider — Resend, Postmark or
# SES — sending from your own domain with SPF and DKIM. Same six variables.
#
# Gmail needs an APP PASSWORD, not the account password:
# 1. myaccount.google.com/security → turn on 2-Step Verification
# 2. myaccount.google.com/apppasswords → create one
# 3. remove the spaces Google shows, and quote it below
#
# PORT 587, not 465: the mailer opens a plain connection and upgrades with
# STARTTLS, refusing to send if the server will not. 465 expects TLS from the
# first byte and will not connect.
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USERNAME=you@your-domain.com
SMTP_PASSWORD="<16 characters, no spaces>"
SMTP_FROM=you@your-domain.com
SMTP_FROM_NAME=Prova
# =============================================================================
# MAINTENANCE
# =============================================================================
# The app renders a maintenance screen from these, so a planned outage is stated
# rather than appearing as a broken app.
MAINTENANCE_MODE=false
MAINTENANCE_MESSAGE="We are carrying out scheduled maintenance. Your funds and account are safe."
MAINTENANCE_UNTIL=