Static marketing site for Levi Meeting Audio Recorder, served at https://levirecorder.app. Plain HTML/CSS/JS (no framework, no build step) plus a small Cloudflare Worker that backs the contact form.
- Static pages:
index.html,contact.html,privacy.html. - Styles:
styles.css(home),contact.css,privacy.css. - Scripts:
script.js(home interactions + conversion tracking),contact.js(contact form),i18n.js(localization). - Localization:
locales/*.json(en, pt-BR, es, it, fr) loaded byi18n.js. - Contact backend:
worker/— Cloudflare Workerlevi-contactthat receives the form POST and sends mail via the Resend API.
It is a static site, so any static server works:
python3 -m http.server 8000 # then open http://localhost:8000localhost:8000 / 127.0.0.1:8000 are already in the Worker's CORS allow-list,
so the contact form works locally against the deployed Worker.
-
Site: hosted on GitHub Pages. Pushing to
maintriggers the automatic Pages build/publish — there is no.github/workflows; Pages serves the branch directly. The custom domain is pinned by theCNAMEfile (levirecorder.app). -
Contact Worker: deployed separately with Wrangler from
worker/:cd worker && npx wrangler deploy
The Worker needs a
RESEND_API_KEYsecret (npx wrangler secret put RESEND_API_KEY). The form posts tohttps://levi-contact.infinitybuilder.workers.dev.
In August 2026 a bot drove this form as an open spam relay: it submitted in a loop with a rotating sender address, and the Worker's auto-confirmation mailed "We received your message" to each of those third-party addresses. Two rules came out of that and must not be relaxed:
- Never send email to an address that came from the form. The form is unauthenticated, so that address is unverified and may belong to anyone. The submitter gets the on-page success state, not an email.
- A submission that cannot be proven human does not send. Turnstile failing, erroring, or being unconfigured all reject the submission.
The Worker drops spam before it reaches Resend, since every email it sends comes from our own domain — a Resend suppression entry cannot stop the inbox notification, which is addressed to us.
| Control | Where | Behaviour |
|---|---|---|
| Kill switch | CONTACT_FORM_ENABLED var |
"0" → 503, nothing sent, checked first |
| Bot check | Turnstile + TURNSTILE_SECRET_KEY |
Fail/error/unconfigured → 403/503 |
| Honeypot | botcheck field |
Silently accepted, nothing sent |
| Burst limit | CONTACT_RATE_LIMITER binding |
3 submissions/min per IP, then 429 |
| Daily quota | CONTACT_QUOTA KV namespace |
5 submissions/day per IP, then 429 |
| Sender blocklist | BLOCKED_SENDERS var |
Silently accepted, nothing sent |
| Link flood | MAX_LINKS_IN_MESSAGE |
More than 3 links is silently dropped |
A blocklist only catches addresses that repeat, so it is the weakest control here — the August bot never reused one. Turnstile is what actually stops that.
The widget is levirecorder-contact in the Cloudflare dashboard
(Turnstile → Edit Widget), mode Managed, with three hostnames:
levirecorder.app, www.levirecorder.app, localhost. Its site key is public
and lives on the .cf-turnstile div in contact.html.
localhost is there so the form can be exercised against
python3 -m http.server 8000; the manual widget flow does not add it for you,
only the "Set up with Spin" flow does.
The secret key is a Worker secret and is never committed:
cd worker && npx wrangler secret put TURNSTILE_SECRET_KEYRotate it from the same Edit Widget page ("Rotate Secret Key", at most once every 2 hours; the old key keeps working during the swap).
Tokens are single-use, so contact.js calls turnstile.reset() after every
failed submit; without that, a retry would reuse a spent token and always fail.
Both limiters key on the IPv4 address, or on the /64 prefix for IPv6, since a sender rotates freely inside their own /64.
The daily quota is a KV counter because the ratelimit binding only accepts a
10s or 60s window. KV reads can be up to a minute stale, so a burst may slip a
few past the cap — the 3/min burst guard is what bounds that overshoot, and it
also keeps a flood from exhausting the KV write quota. If KV fails, the quota
check fails open so an outage cannot take the contact form down.
To block an address or a whole domain, edit BLOCKED_SENDERS in
worker/wrangler.toml and redeploy:
BLOCKED_SENDERS = "bot@spam.com, casino.ru"Entries match the full address or the bare domain, are case-insensitive, and
ignore plus-addressing (bot+tag@spam.com is caught by bot@spam.com).
Blocked submissions return success: true on purpose — a bot that sees a
rejection starts probing for the rule that stopped it. Drops are visible in
npx wrangler tail.
Four trackers are loaded on every page (see the <head> of each .html):
| Tool | ID | Purpose |
|---|---|---|
| Google Analytics | G-3L5J12BFJ0 |
GA4 traffic/behavior |
| Google Ads | AW-18232657346 |
Conversion tracking for paid traffic |
| Microsoft Clarity | wulz7uk3oy |
Session heatmaps/recordings |
| Reddit Pixel | a2_j6ez7imlu8si |
Reddit Ads conversion tracking |
The key paid-campaign signal is a download click from a real macOS visitor.
It is implemented in script.js (leviIsRealMac() + a delegated click listener
on every a[href*="apps.apple.com"]):
- Fires only when the visitor is on real macOS — explicitly excludes
iPhone/iPad/Android/Windows, including iPadOS Safari which masquerades as Mac
(filtered via
navigator.maxTouchPoints). - On a qualifying click it sends two events:
gtag('event', 'conversion', { send_to: 'AW-18232657346/UoHVCI3Krb4cEMKLgfZD' }); gtag('event', 'download_click_macos', { link_location: '…' });
- The
conversionevent feeds the Google Ads conversion action "macOS Download Click" (category Inscrição / Sign-up, primary, count One, 90-day click window, data-driven attribution, value 1). - Ad-click attribution is handled by Google automatically via the
gclid(auto-tagging is on); we do not gate on it. Google only counts clicks that carried agclidwithin the conversion window. - The
download_click_macosGA4 event is currently for analysis only. It can be marked as a GA4 Key Event and imported into Ads later if we move off the native conversion (do not run both as Primary for the same action — that double-counts).
We cannot measure the actual App Store install from the website — the Mac App Store has no install postback to Google Ads. The download click is the proxy conversion. To estimate real installs, see App Store campaign tracking below.
Mirrors the Google Ads setup, in the same click listener in script.js, so
the same real-macOS gate (leviIsRealMac()) applies — no double-firing on
iPad/iPhone/Android/Windows.
- The Reddit Pixel base code lives in the
<head>of every page and firesrdt('track','PageVisit')on load (analogous to the GA4 pageview). - On a qualifying macOS download click it sends a custom conversion event:
rdt('track', 'Custom', { customEventName: 'Download', conversionId: '<uuid>' });
conversionIdis a fresh UUID per click. It exists for pixel ⇄ Conversions API deduplication: if a server-side CAPI event is ever added for the same click, send the same id as itsconversion_id.- Ad-click attribution is automatic — the pixel stores the Reddit click id
(
rdt_cid) from the landing URL/cookie and attaches it. - In Reddit Events Manager, map the custom event
Downloadas the campaign's conversion event (theTRAFFICcampaign objective can then optimize toward it). From 2026-07-13, ad groups/CBO campaigns require aconversion_pixel_id— this pixela2_j6ez7imlu8siis it.
Conversions API (CAPI) token is intentionally NOT in this repo. The token from Reddit's setup is a server-side secret; embedding it in a static site would leak it publicly. Only the client-side pixel (public
pixel_id) is installed here. If we later want server-side CAPI events (POST /api/v3/pixels/a2_j6ez7imlu8si/conversion_events), do it from the Cloudflareworker/with the token stored as a secret env var, reusing theconversionIdabove for dedup.
Download links default to ?ct=website&mt=12. The ct (campaign text) shows up
in App Store Connect → Analytics → Acquisition, and mt=12 routes to the Mac
App Store.
To attribute installs to a paid channel, the campaign flows through the site, not straight to the App Store (the visitor must land on the site for the macOS download conversion to fire):
ad → levirecorder.app/?ct=googleads → apps.apple.com/...?ct=googleads&mt=12
script.js reads an incoming ct (and optional pt) from the landing URL and
rewrites every App Store link to carry that token, overriding the default
ct=website. Without this the token would stay on the landing page and never
reach the App Store, so paid installs would be miscredited to "website".
These are not in code and must be done in the respective consoles:
- Google Ads — Final URL + suffix: set the ad Final URL to the site
(
https://levirecorder.app/), not the App Store directly, so the visitor lands on the site and the conversion can fire. Put the campaign token in the Final URL suffix:ct=googleads(plus anyutm_*you want). The site then propagatesctto the App Store link (see App Store campaign tracking above).mt=12is an App Store param and is not needed on the site URL. - Google Ads — campaign goal: ensure the Inscrição goal (which holds
macOS Download Click) is active on the campaigns. It is set as an account-default goal, so it should apply automatically. - Google Ads status: the conversion shows "Configuração incorreta"
until the first real conversions (clicks carrying a
gclid) arrive — this clears on its own (up to ~24h). Verify with Google Tag Assistant on a Mac: load the site, click Download, confirm theconversionhit withsend_to: AW-18232657346/UoHVCI3Krb4cEMKLgfZD. - Microsoft Clarity: the
clarity.mstag was observed returning503on load (likely transient). Re-check that sessions are recording.
index.html / contact.html / privacy.html pages
styles.css / contact.css / privacy.css styles
script.js home + conversion tracking
contact.js contact form
i18n.js + locales/*.json localization (en, pt-BR, es, it, fr)
assets/ images, logos, icons
worker/ Cloudflare Worker (contact → Resend)
CNAME custom domain (levirecorder.app)