-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathrender.yaml
More file actions
299 lines (299 loc) · 11.6 KB
/
Copy pathrender.yaml
File metadata and controls
299 lines (299 loc) · 11.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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# Exported from Render on 2026-09-01T15:53:57Z, with comments added.
#
# This file describes the service as it runs. It is not a place to ask for a
# different one: Render rejects a blueprint whose region differs from the
# running service - "changing region not supported" - and a rejected blueprint
# applies nothing at all, so one hopeful line silently stops every other
# setting here from syncing. That happened once already. To move a service,
# create the new one first and export this file afterwards.
#
# Region: the service, the Neon database and the Upstash Redis instance now all
# live in eu-central-1, and so do almost all of the readers. Measured from
# Turkey, Frankfurt answers in 88 ms against 266 ms for Ohio and 287 ms for
# Oregon, and the API paid that distance again on every database and cache
# round trip - a ranked feed page makes about a dozen of them, which cost it
# roughly a second per request while the three were spread across two
# continents.
version: "1"
projects:
- name: TDN
environments:
- name: Production
services:
- type: web
name: tdn-api-frankfurt
runtime: docker
repo: https://github.com/the-developer-network/tdn-api
# Render's current name for what its billing still calls Starter. Not
# `free`: a free instance sleeps after fifteen minutes, and on a site
# this quiet that means most visitors wait out a cold start.
plan: 0.5c-512mb
envVars:
# The morning digest. Everything below has a default in env.schema.ts, so
# the service boots without any of them - but it boots with the digest
# OFF, which is the intended state until somebody decides otherwise.
#
# Enabling it is not reversible for the people who receive the first run:
# nobody has a delivery record yet, so the first morning mails every
# verified, active, subscribed account at once. Count that audience and
# check it against the Resend plan before setting ENABLED to true -
# docs/daily-digest.md has the query.
#
# API_URL and FRONTEND_URL below are load-bearing for this feature in a
# way they were not before: every link in the email is built from them,
# and the unsubscribe link - which mail clients also read out of the
# List-Unsubscribe header - comes from API_URL. If either still holds a
# localhost value, the emails ship broken links to everybody.
- key: DAILY_DIGEST_ENABLED
sync: false
- key: DAILY_DIGEST_CRON
sync: false
- key: DAILY_DIGEST_TIMEZONE
sync: false
- key: DAILY_DIGEST_WINDOW_HOURS
sync: false
- key: DAILY_DIGEST_MAX_WINDOW_DAYS
sync: false
- key: DAILY_DIGEST_USER_PAGE_SIZE
sync: false
- key: DAILY_DIGEST_BATCH_SIZE
sync: false
- key: DAILY_DIGEST_BATCH_PAUSE_MS
sync: false
- key: DAILY_DIGEST_MAX_NOTIFICATIONS
sync: false
- key: DAILY_DIGEST_MAX_POSTS
sync: false
- key: DAILY_DIGEST_CANDIDATE_POOL_SIZE
sync: false
- key: MEDIA_MODERATION_LEASE_SECONDS
sync: false
- key: MEDIA_MODERATION_MAX_ATTEMPTS
sync: false
- key: MEDIA_MODERATION_BATCH_SIZE
sync: false
- key: MEDIA_MODERATION_CRON
sync: false
- key: MODERATION_ENABLED
sync: false
# Set these on the service before merging the moderation work. With
# MODERATION_ENABLED true and either of them empty the provider refuses
# to construct, which fails the boot - deliberately, so a deploy that
# forgot them cannot come up healthy and answer 503 to every upload
# while nobody notices.
- key: SIGHTENGINE_API_USER
sync: false
- key: SIGHTENGINE_API_SECRET
sync: false
- key: FEED_EXPLORATION_RATE
sync: false
- key: USER_INTEREST_SIGNAL_LIMIT
sync: false
- key: USER_INTEREST_MIN_WEIGHT
sync: false
- key: USER_INTEREST_MAX
sync: false
- key: USER_INTEREST_HALF_LIFE_DAYS
sync: false
- key: USER_INTEREST_WINDOW_DAYS
sync: false
- key: USER_INTEREST_REBUILD_CRON
sync: false
- key: FEED_WEIGHT_AFFINITY
sync: false
- key: FEED_CANDIDATE_WINDOW_DAYS
sync: false
- key: FEED_CANDIDATE_POOL_SIZE
sync: false
- key: FEED_FOREIGN_LANGUAGE_QUOTA
sync: false
- key: FEED_MAX_POSTS_PER_AUTHOR
sync: false
- key: FEED_HALF_LIFE_HOURS
sync: false
- key: FEED_WEIGHT_ENGAGEMENT
sync: false
- key: FEED_WEIGHT_SOCIAL
sync: false
- key: FEED_WEIGHT_LANGUAGE
sync: false
- key: USER_PURGE_GRACE_PERIOD_DAYS
sync: false
- key: USER_PURGE_CRON
sync: false
- key: RESEND_API_KEY
sync: false
- key: REFRESH_TOKEN_PURGE_GRACE_PERIOD_DAYS
sync: false
- key: REFRESH_TOKEN_PURGE_CRON
sync: false
- key: REFRESH_TOKEN_EXPIRES_IN
sync: false
- key: REDIS_URL
sync: false
- key: R2_SECRET_ACCESS_KEY
sync: false
- key: R2_PUBLIC_URL
sync: false
- key: R2_ENDPOINT
sync: false
- key: R2_BUCKET_NAME
sync: false
- key: R2_ACCESS_KEY_ID
sync: false
- key: PORT
sync: false
- key: OTP_EXPIRY_SECONDS
sync: false
- key: NOTIFICATION_PURGE_GRACE_PERIOD_DAYS
sync: false
- key: NOTIFICATION_PURGE_CRON
sync: false
- key: GOOGLE_CLIENT_SECRET
sync: false
- key: GOOGLE_CLIENT_ID
sync: false
- key: GOOGLE_CALLBACK_URL
sync: false
- key: GITHUB_CLIENT_SECRET
sync: false
- key: GITHUB_CLIENT_ID
sync: false
- key: GITHUB_CALLBACK_URL
sync: false
- key: FRONTEND_URL
sync: false
- key: EMAIL_FROM
sync: false
- key: DEEPL_API_KEY
sync: false
- key: DATABASE_URL
sync: false
# Where an OAuth flow may return to. Both are exact-match lists; the
# native one also switches that flow's session onto the response body
# instead of a cookie, which is why it is a separate list rather than a
# rule about URL schemes. Empty means the web app's own page only, which
# is what the flow did before it could be asked.
- key: OAUTH_REDIRECT_ALLOWLIST
sync: false
- key: OAUTH_NATIVE_REDIRECT_ALLOWLIST
sync: false
# Push notifications. PUSH_ENABLED is the switch: with it false - the
# default - devices still register and nothing is delivered, so the
# feature can ship before there is an Expo project behind it.
# docs/push-notifications.md has the rest.
- key: PUSH_ENABLED
sync: false
- key: EXPO_ACCESS_TOKEN
sync: false
- key: DEVICE_RETENTION_DAYS
sync: false
- key: DEVICE_PURGE_CRON
sync: false
# Shared secret on the Play notification push URL. Empty keeps that
# endpoint closed, which is the right default for an unauthenticated
# route that writes billing state.
- key: PLAY_OIDC_AUDIENCE
sync: false
- key: PLAY_OIDC_SERVICE_ACCOUNT
sync: false
- key: PLAY_NOTIFICATIONS_TOKEN
sync: false
# The paid verification badge. Both have defaults; they are declared
# because the reconcile pass is what notices a ban, and turning it off or
# slowing it down is a decision somebody should have to make on purpose.
- key: SUBSCRIPTION_RECONCILE_CRON
sync: false
- key: SUBSCRIPTION_RECONCILE_BATCH_SIZE
sync: false
# Mobile clients. All four have defaults in env.schema.ts, so the service
# boots without them; they are declared because the two build numbers are
# what lets the API refuse a version that is too old to be talked to, and
# that is not a setting anybody should have to find in the schema.
- key: REFRESH_ROTATION_GRACE_SECONDS
sync: false
- key: MEDIA_MAX_FILE_SIZE_MB
sync: false
- key: MOBILE_MIN_SUPPORTED_BUILD
sync: false
- key: MOBILE_LATEST_BUILD
sync: false
- key: MOBILE_STORE_URL_ANDROID
sync: false
# Content reports. MODERATION_ALERT_EMAIL is the switch for both the
# escalation alert and the morning summary: with it empty - the default -
# reports are still filed and still queue up in the table, but nothing is
# mailed to anybody. Everything else here has a default in env.schema.ts.
# docs/reporting.md has the statements for reading and closing the queue,
# which is done by hand like a ban.
- key: MODERATION_ALERT_EMAIL
sync: false
- key: REPORT_ALERT_THRESHOLD
sync: false
- key: REPORT_DIGEST_ENABLED
sync: false
- key: REPORT_DIGEST_CRON
sync: false
- key: REPORT_DIGEST_TIMEZONE
sync: false
- key: REPORT_DIGEST_MAX_REPORTS
sync: false
- key: REPORT_RETENTION_DAYS
sync: false
- key: REPORT_PURGE_CRON
sync: false
- key: CORS_ORIGIN
sync: false
- key: COOKIE_SECRET
sync: false
- key: API_URL
sync: false
- key: ACCESS_TOKEN_SECRET_KEY
sync: false
# Encrypts message text at rest. 32 bytes, base64.
#
# Must exist on the service BEFORE the deploy that first needs it: the
# env schema declares it without a default, so an instance without it
# fails to boot rather than starting and writing rows nothing can read.
#
# Never rotate this casually. Every message and inbox preview written
# under the old key becomes unreadable - a rotation needs a re-encrypting
# backfill first, which does not exist yet.
- key: MESSAGE_ENCRYPTION_KEY
sync: false
# How long direct message history is kept, and when the purge runs.
# Both have defaults in env.schema.ts (365 days, 04:00 container time),
# so the service boots without them - they are declared here because
# shortening the window destroys user history that cannot be recovered,
# and that should be a visible setting rather than a constant only the
# schema knows.
- key: MESSAGE_RETENTION_DAYS
sync: false
- key: MESSAGE_RETENTION_CRON
sync: false
- key: ACCESS_TOKEN_EXPIRES_IN
sync: false
region: frankfurt
dockerContext: .
dockerfilePath: ./Dockerfile
# Migrations run here, and this is the only place they can run in the
# right order. Render builds from the commit (autoDeployTrigger below),
# so it ships new code the moment main moves; the migrate job in
# release.yml sits behind CI and semantic-release and therefore always
# lands after the deploy it was meant to precede. On 2026-09-05 that gap
# was three and a half minutes, during which every authenticated feed
# read answered 500 against a blocks table that did not exist yet.
#
# A pre-deploy command runs after the image is built and before the new
# instance takes traffic, and a non-zero exit cancels the deploy - so a
# migration that cannot be applied keeps the old code serving instead of
# promoting code its schema cannot support.
#
# `prisma migrate deploy` only applies what has not run yet and takes an
# advisory lock, so a redeploy with nothing pending is a no-op and two
# overlapping deploys queue rather than race.
preDeployCommand: pnpm db:deploy
domains:
- api.developernetwork.net
autoDeployTrigger: commit
renderSubdomainPolicy: disabled