-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnetlify.toml
More file actions
131 lines (124 loc) · 7.49 KB
/
Copy pathnetlify.toml
File metadata and controls
131 lines (124 loc) · 7.49 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
# Netlify configuration for the Aztec announcement tool (Next.js 16 App Router).
#
# Docs consulted while writing this file (2026-08-21):
# - Build settings / file-based config:
# https://docs.netlify.com/build/configure-builds/file-based-configuration/
# - Next.js runtime on Netlify (OpenNext adapter, zero-config):
# https://docs.netlify.com/frameworks/next-js/overview/
# - Scheduled functions:
# https://docs.netlify.com/build/functions/scheduled-functions/
# - Rate limiting:
# https://docs.netlify.com/manage/security/secure-access-to-sites/rate-limiting/
[build]
command = "npm run web:build"
# `publish` MUST be set to `.next`. Leaving it unset does NOT mean "let the
# adapter decide" — Netlify's default is the repository root, and the adapter
# then fails with "Your publish directory cannot be the same as the base
# directory of your site". Two real deploys proved this: the first with the
# value set in the UI (publishOrigin: ui), the second with the UI field
# cleared (publishOrigin: default). Both resolved to /opt/build/repo and both
# failed identically. The adapter reads `.next` and rewrites it into the
# deployable output itself.
publish = ".next"
# The Next.js adapter MUST be declared explicitly. An earlier version of this
# file asserted it applies automatically and told readers not to add it — that
# was WRONG, and a real deploy proved it: the build compiled all 18 routes, then
# uploaded 16 static files and only the two scheduled functions. No server
# function was produced for the app at all, so every page returned Netlify's own
# "Page not found".
#
# Netlify's zero-config detection is suppressed once a project declares a custom
# build command and a custom functions directory, both of which this file does.
# Declaring the plugin restores it. If you ever see the deploy log jump straight
# from "build.command completed" to "Functions bundling" with no adapter section
# in between, this block has gone missing again.
[[plugins]]
package = "@netlify/plugin-nextjs"
[build.environment]
# Non-secret. Read by the app to distinguish "running on Netlify" (functions,
# no long-lived worker process) from the VM deployment (persistent worker).
DEPLOY_TARGET = "netlify"
# -----------------------------------------------------------------------------
# Scheduled function
# -----------------------------------------------------------------------------
# netlify/functions/tick-scheduled.ts already exports its own schedule:
#
# export const config: ScheduledConfig = { schedule: '* * * * *' };
#
# Netlify's docs describe inline config (TS/JS) and netlify.toml
# `[functions."name"] schedule = "..."` as the TWO alternative ways to declare
# a schedule, not complementary ones — the function file already uses the
# inline form. Do NOT also add a `[functions."tick-scheduled"]` schedule block
# here: that would duplicate the declaration rather than reinforce it, and it
# is unclear from the docs which one would win if they disagreed.
# -----------------------------------------------------------------------------
# Rate limiting — PARTIAL COVERAGE, read this before assuming both public
# write paths are protected.
# -----------------------------------------------------------------------------
# RATE LIMITING IS NOT CONFIGURED HERE, DELIBERATELY.
#
# An earlier version of this file carried a rule with `from = "/"`, intending to
# throttle the two public write paths (subscribeEmail and subscribeWebhook, both
# Next.js Server Actions that POST to "/"). It was removed on 2026-08-23 after a
# real deployment showed it breaks the site:
#
# `from = "/"` matches as a PREFIX in Netlify, so it applied to EVERY path,
# including /admin/login. A browser loading one page makes many requests
# (HTML, scripts, styles, RSC payloads), so 20/minute was tripped instantly —
# a second publisher trying to sign in got HTTP 429 and could not reach the
# login page at all. On a tool whose whole point is that a SECOND person must
# approve a critical announcement, a rule that blocks the second person from
# signing in is worse than no rule.
#
# Netlify's rate limiting matches on request path only. Server Actions have no
# path of their own — the browser POSTs back to the page's own URL and Next
# dispatches internally via a `Next-Action` header that redirect rules cannot
# see. So there is NO path expression that throttles the write paths without
# also throttling ordinary page loads.
#
# The abuse this was meant to bound: subscribeEmail sends a confirmation email
# to any address given, so an unthrottled endpoint is an email bomber pointed at
# a third party. That risk is real and currently UNMITIGATED.
# Options if it matters: throttle inside the action against a shared store
# (Netlify Blobs or the database, since in-memory state does not survive
# serverless cold starts), or put a CAPTCHA on the subscribe form. Both are real
# work; neither is a config line. Do not "fix" this by re-adding a path rule.
# -----------------------------------------------------------------------------
# Secrets — DO NOT put real values in this file. Set these in the Netlify UI
# (Site configuration > Environment variables) or via `netlify env:set`.
# -----------------------------------------------------------------------------
# TICK_SECRET - shared secret authenticating tick-scheduled -> tick-background
# AUTH0_DOMAIN - provisioned by the Netlify Auth0 extension
# AUTH0_CLIENT_ID - provisioned by the Netlify Auth0 extension
# AUTH0_AUDIENCE - provisioned by the Netlify Auth0 extension
# AUTH0_ISSUER - provisioned by the Netlify Auth0 extension
# AUTH0_CLIENT_SECRET - Auth0 application client secret; needed to exchange the
# authorization code at /admin/callback. The app refuses
# to start on this shape without it.
# SESSION_SECRET - signs the browser session cookie (32+ chars). The app
# refuses to start on this shape without it.
# DATABASE_URL - Postgres connection string for the deployed database
#
# NOT A SECRET, BUT STILL REQUIRED IN THE UI:
# DEPLOY_TARGET - the `[build.environment]` block above sets this for the
# BUILD only. It must ALSO be set to "netlify" in the
# Netlify UI (Site configuration > Environment variables),
# scoped so it reaches the function and server runtime.
# Two things read it at RUNTIME, not build time:
# - instrumentation.ts / the production guard, which
# refuses to boot on an unset or unrecognized value;
# - src/core/identity.ts, which trusts the forgeable
# `Tailscale-User-Login` header ONLY when it reads
# exactly "vm".
# Unset at runtime, the app refuses to serve rather than
# mis-trusting a header — but it does refuse, so set it.
#
# ENABLED_CHANNELS - comma-separated channels this deployment fans out to.
# Must be scoped to the FUNCTION AND SERVER RUNTIME, not
# only [build.environment]: the tick function, the preview
# and the admin UI all read it at runtime. The Netlify
# deployment omits 'signal' — there is no signal-cli
# sidecar to reach from a serverless function.
#
# Admin routes (app/admin/*) are already protected by Auth0 JWT verification
# in middleware.ts and do not need a rate-limit rule here.