-
Notifications
You must be signed in to change notification settings - Fork 37
Expand file tree
/
Copy pathrender.mainnet.yaml
More file actions
163 lines (153 loc) · 7.76 KB
/
Copy pathrender.mainnet.yaml
File metadata and controls
163 lines (153 loc) · 7.76 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
# Render Blueprint — Quay on the Stellar PUBLIC network (mainnet).
#
# Separate from render.yaml on purpose. render.yaml deploys the testnet
# service; applying this file creates a DIFFERENT service (quay-api-mainnet)
# rather than repointing the existing one, so testnet stays available as a
# staging environment and a bad mainnet config never takes the testnet deploy
# down with it. Read docs/MAINNET.md before applying.
#
# REAL MONEY. Every `sync: false` value below must be set in the Render
# dashboard before the first deploy. The public-network guardrails in
# apps/api/src/env.ts throw at boot on anything missing or sandbox-pointed, so
# a misconfigured service fails loudly instead of taking payments it cannot
# settle. That is the intended behaviour — do not work around a boot failure by
# relaxing a guard.
#
# Secrets to set in the dashboard:
# DATABASE_URL libsql://<db-name>-<org>.turso.io (a real, backed-up DB)
# DATABASE_AUTH_TOKEN from `turso db tokens create <db>`
# SERVER_SIGNING_SECRET S... the platform's stable SEP-10 identity
# JWT_SECRET 64 hex chars — session signing
# WEBHOOK_SECRET_ENCRYPTION_KEY 64 hex chars — REQUIRED
# METRICS_TOKEN 64 hex chars — gates GET /metrics
#
# This blueprint ships the PAYMENTS-ONLY configuration (OFFRAMP=none), which is
# the path decided on 2026-08-21 and the fastest route to a live mainnet
# deploy: buyers pay the seller's wallet directly, the watcher confirms on the
# ledger, receipts and webhooks fire, and the seller moves their own funds.
# It needs no anchor agreement, holds no SEP-12 identity data, and keeps no
# seller secret key on the server — so DEFAULT_SELLER_SECRET, KYC_ENCRYPTION_KEY
# and the ANCHOR_* variables are deliberately absent below. Enabling cash-out
# later is a configuration change, not a migration: uncomment the anchor block.
#
# Set on the WEB deployment (Vercel), not here:
# NEXT_PUBLIC_STELLAR_NETWORK=public the browser signs with the passphrase
# this selects; anything else means every
# wallet signature is rejected, with no
# error naming the cause
# NEXT_PUBLIC_OFFRAMP_MODE=none or the dashboard offers a cash-out the
# API answers 501 to
#
# Before the first real payment, run the preflight — it checks the things a
# boot guard cannot see (does the seller account exist, is it funded, does it
# carry a Circle USDC trustline, is the deployed service really on pubnet):
#
# pnpm preflight:mainnet --api https://quay-api-mainnet.onrender.com
#
# Generate each 64-hex value with:
# node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
services:
- type: web
name: quay-api-mainnet
runtime: docker
# Free, deliberately, and this is a real trade rather than a default.
#
# A free instance spins down after 15 minutes idle, and the settlement
# watcher runs inside this process — so a sleeping instance is not watching
# the ledger. Two things make that survivable: the watcher resumes from its
# persisted cursor and backfills whatever it missed, so a payment is marked
# paid LATE rather than lost; and .github/workflows/uptime.yml pings every
# 5 minutes, under the idle timeout, which in practice keeps it awake.
#
# "In practice" is the weak word. GitHub's cron is not punctual and can be
# delayed under load. Before customers other than you depend on this, move
# to `starter` — $7/mo removes the whole category.
plan: free
# REAL MONEY DEPLOYS FROM HERE. `main` is protected (PR required, linear
# history), so nothing reaches the public network without passing CI and a
# merge — and by then it has already run on the testnet service, which
# deploys from `dev`.
branch: main
dockerfilePath: ./apps/api/Dockerfile
dockerContext: .
healthCheckPath: /ready
envVars:
- key: API_PORT
value: "8787"
- key: STELLAR_NETWORK
value: public
# Circle's USDC issuer on pubnet. VERIFY against Circle's own published
# address before taking a payment — minting an asset with the code
# "USDC" from a different issuer is trivial and it is worth nothing.
# https://developers.circle.com/stablecoins/stellar-usdc
- key: USDC_ISSUER_PUBLIC
value: GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN
- key: WATCH_POLL_MS
value: "6000"
# "none" and "anchor" are the two values that boot on public. env.ts
# refuses mock (fakes settlement after 8s) and testanchor (SDF sandbox,
# settles nothing) outright — both would show sellers a completed
# cash-out that never happened.
#
# "none" is shipped here: no cash-out leg, and therefore no anchor to
# trust, no seller PII to hold, and no seller key on the server. The
# offramp_* statuses stay in the state machine, unreachable, so turning
# cash-out on later is config rather than a migration.
- key: OFFRAMP
value: none
# Cash-out to fiat. Uncomment ALL FOUR together, set OFFRAMP=anchor
# above, set NEXT_PUBLIC_OFFRAMP_MODE=anchor on the web deployment, and
# re-read docs/MAINNET.md — this is the configuration that moves real
# money through a third party. No defaults anywhere: a default here would
# mean "the testnet sandbox".
# - key: ANCHOR_URL
# sync: false
# - key: ANCHOR_HOME_DOMAIN
# sync: false
# - key: KYC_ENCRYPTION_KEY # 64 hex — encrypts real seller PII
# sync: false
# - key: DEFAULT_SELLER_SECRET # S... signs SEP-10 auth for the anchor
# sync: false
# Set once you know the rail. Unset means "read /sep6/info and refuse if
# the anchor offers more than one withdrawal type".
# - key: OFFRAMP_TYPE
# value: bank_account
# Real production origin. No localhost.
- key: CORS_ORIGINS
sync: false
# HOME_DOMAIN is intentionally NOT declared: env.ts derives it from
# RENDER_EXTERNAL_HOSTNAME. Set it in the dashboard only if a custom
# domain or proxy sits in front of the API.
# Optional, and usually left unset. Quay is multi-tenant: a seller signs
# in with their own wallet over SEP-10 and every link they create is paid
# straight to that address, so this deployment needs no wallet of its own.
# Setting one only makes /health report whether that one wallet holds a
# USDC trustline; per-seller trustlines are checked on every link
# creation regardless.
# - key: DEFAULT_SELLER_WALLET
# sync: false
# A real, backed-up database. The fallback is file:./local.db, which is
# lost on every redeploy — taking the payment ledger with it.
- key: DATABASE_URL
sync: false
- key: DATABASE_AUTH_TOKEN
sync: false
- key: WEBHOOK_SECRET_ENCRYPTION_KEY
sync: false
- key: JWT_SECRET
sync: false
- key: SERVER_SIGNING_SECRET
sync: false
- key: METRICS_TOKEN
sync: false
# This blueprint declares a single instance, and the API now refuses to
# boot on pubnet without either REDIS_URL or this acknowledgement —
# because "how many instances are running" is otherwise a dashboard
# setting that silently changes two security properties. If you scale
# this service up, set REDIS_URL in the same change and remove this.
- key: SINGLE_INSTANCE
value: "true"
# Shared rate-limit counters. The in-process fallback means N instances
# allow N times the configured limit; set this before scaling past one.
# - key: REDIS_URL
# sync: false