Treating a prober as a customer
Look at an unauthenticated write endpoint — a form that sends mail — and you will usually find one problem being discussed and another being missed. The first is abuse: someone finds the URL and uses your endpoint to send things. The second is measurement: every probe of that endpoint looks, in your own numbers, like a person who wanted to reach you.
The second problem survives fixing the first.
Where the credential lives decides who can use the endpoint
The site is a static Jekyll build, so the POST goes to /contact/submit, which Caddy proxies to a small loopback-only listener that relays over the mesh to the fleet’s mail host. The endpoint requires a shared token, and a deploy step is where it becomes real:
1
2
3
4
# It is deliberately NOT baked into the site (the repo is public and
# the token must be rotatable without a rebuild): it is written here, at
# deploy time ... If the file is absent the endpoint stays locked and the
# form shows the visitor a mailto fallback rather than failing silently.
That last sentence is the design worth copying. An absent secret is a fail-closed condition: no token is published, the endpoint rejects everything with 403, and the page still offers a working mailto link rather than a form that appears to work and loses the message.
Rejections should be cheap, and ordered
pkgs/contact-form/contact_form.py states its ordering as a design constraint, and the ordering is the argument: method, content type, content length and rate limit are checked before any parsing, and parsing is capped before any SMTP connection is attempted.
- wrong content type gives
415 - a missing or oversized
Content-Length(cap: 16 KiB) gives413, refused before the body is read - a missing or wrong token gives
403 - more than 5 accepted submissions per source address per hour gives
429
The token is a header the page’s JavaScript sets, not a form field, so it is not part of a plain form POST, and it is compared with hmac.compare_digest. One line in the file is the decision most endpoints get backwards:
1
2
3
# Only an authenticated caller consumes rate-limit budget: the limit is
# there to bound a visitor (or a token-scraper), not to let unauthenticated
# probes lock out real submissions.
A rate limit any anonymous request can spend is a denial-of-service primitive aimed at your own visitors; here the cheap rejections never touch the budget.
A form in a comment still passes a naive check
The site also ships a gate, scripts/check-contact-form-visible.sh, and its header names the mistake it exists to catch: a grep for <form passes even when the markup sits inside an HTML comment. So it verifies the way a browser renders — it strips <!-- ... --> pairs, then asserts that id="ymrtech-form" and the controls name, email, message and website are all in the visible remainder, and that a stray opener has not swallowed the page.
That is the same lesson as the status claims elsewhere on this site: the audit page is built from signed artifacts for the same reason — check the artifact in the state it is actually in.
The half that survives the fix
Now the measurement half. A 403 from a token-less prober and a 200 from a real visitor are both one line against /contact/submit. If the number you watch is derived from paths, your funnel counts attackers as demand — and unlike the spam, that does not stop when the endpoint gets better.
So:
- Rate-limit by behaviour and by source, not by User-Agent. See don’t ban the Googlebot: that header is chosen by the client.
- Separate “a request arrived” from “a person tried to reach us” before counting anything. The endpoint’s own rejections are the interesting signal, and here they go to its journal —
bad token from <ip>,rate limited <ip>— while the host’s log collector picks up Caddy access logs rather than service stdout. Rejections are legible on the host, not in the log store, until the collector’s glob is extended. - Treat the form’s success metric as untrusted until you can say what was filtered. A conversion count you cannot decompose counts traffic, not people.
The asymmetry: you can reduce spam and clean up your metrics, but a public unauthenticated endpoint will keep receiving some non-human traffic. The useful goal is not zero — it is that none of it contaminates a decision. The same trap is in a test request is a real request: an observation made from the wrong position is evidence about the position.
What you can check
- The ordering, in the file.
pkgs/contact-form/contact_form.pyin thenix-configrepository: the415/413/403/429sequence and the comment that anonymous probes do not spend rate-limit budget. - The fail-closed deploy. The
Publish contact-form tokenstep inymrtech-deploy.ymlwarns and publishes an empty token when the host-readable file is unavailable — and the endpoint then stays locked. - The visibility gate. Run
scripts/check-contact-form-visible.shagainst a built_site/contact/index.html; it names the specific control it cannot find.
For how a missing token and a missing log line are recorded as evidence rather than assumed, see the logging stack and what no logs cannot promise.