Selective challenge
Turnstile appears only after the rolling 10/10min threshold. A cooldown token bypasses it for 60 minutes after success.
Build-ready · Phase 1 security
A concise, implementation-ready plan for rate limiting, Turnstile challenges, and daily guest-interest digests that protects providers without adding friction.
Non-negotiable constraints to align product, security, and comms.
Turnstile appears only after the rolling 10/10min threshold. A cooldown token bypasses it for 60 minutes after success.
Send daily emails only within the defined window, and only if guest views ≥ 3.
No per-location throttles in this phase. All limits are keyed to the visitor identity.
A server-derived key that rotates daily, preventing long-term tracking while keeping limits consistent per visitor.
visitor_key = sha256(ip + "|" + user_agent + "|" + day_salt)
Thresholds, cooldown behavior, and API contract for contact reveals.
visitor_key + exp.scope: "reveal_bypass" to keep intent explicit.POST /api/locations/:locationId/reveal
cooldown_token?, turnstile_token?{ contactDetails, cooldown_token? }{ error: "challenge_required" }{ error: "rate_limited", retry_after_seconds }visitor_key.Track guest interest only after successful reveals and send a single daily digest per location when interest is meaningful.
Increment guest_views only when a guest successfully reveals contact details (after any Turnstile challenge).
(location_id, local_date).digest_enabled.digest_enabled: default truedigest_threshold: default 3 (future-configurable)Suggested worker routes and Cloudflare-friendly storage choices.
Inline challenge flow that keeps the reveal action fast and clear.
challenge_required, render Turnstile widget inline.turnstile_token.This phase intentionally avoids per-location limits while still deterring scraping and repeat abuse.
Distributed attacks across many IPs against one provider. If this becomes a problem, add per-location throttles as a future toggle.