Security & privacy

Abuse protection

Rate limits and detection at the edges that matter most.

Sensitive endpoints are throttled at both the CDN and application layer, every auth failure is fed to monitoring, and the CORS allow-list is explicit — no third-party page can act on your account without your consent. An attacker who tries to guess a password, stuff credentials or brute-force a pairing code hits a temporary block within seconds.

Rate limiting

Sensitive endpoints (sign-in, sign-up, confirmation, device pairing, OAuth) are throttled both at the application and CDN layer. An attacker trying to guess a password or pairing code hits a temporary block almost immediately.

Authentication-failure detection

Every failed attempt is tagged (type, reason) and forwarded to our monitoring system, so an attack spike is visible in real time.

CSRF and CORS

Web endpoints are protected against cross-site request forgery. The origins allowed to call our API are explicitly allow-listed — no third-party page can act on your account without your consent.

Real-IP-aware throttles

The guest-signup throttle reads both request.ip and CF-Connecting-IP, so an attacker can't defeat the limit by cycling open proxies behind our CDN.

Per-email signin lockout

Sign-in is throttled by both IP and email. Credential-stuffing a single account across many IPs still hits the per-email budget and gets blocked, even if the attacker has access to a large botnet.

Dual-layer DDoS mitigation

Cloudflare absorbs volumetric attacks upstream of our origin; application-level rate limiting takes care of low-and-slow abuse that survives the CDN. Neither layer alone is sufficient; together they cover both vectors.

Signed webhooks

Every inbound webhook we accept (PhoneVoice, Stripe, Postmark) carries an HMAC signature we verify against a shared secret. Forged webhook payloads are rejected before they reach any application logic.

No default-allow

Every new endpoint has to explicitly opt into being reachable without authentication. The default is: requires a valid session. No one can accidentally expose a sensitive action by forgetting an auth check.

Deep dive

Per-endpoint throttle budgets

Every sensitive endpoint has its own rate-limit budget, sized to what a legitimate user needs and no more:

  • 10 sign-ins per 15 min, per IP AND per email
  • 5 signups per hour per IP; 3 guest signups per hour per IP + per real IP
  • 20 confirmation-code attempts per 15 min per IP
  • 30 OAuth calls per 15 min per IP
  • 10 device-pair attempts per 15 min per IP
  • 300 total requests per 5 min per IP, across everything except assets and the ActionCable WebSocket

Over the budget we return HTTP 429 with a Retry-After header and log a throttle.rack_attack notification so attackers are visible on our dashboards. The cache store is Redis in production, so the budget is shared across every web worker.

CORS allow-list

The only origins allowed to call our API are twoody.com subdomains, the mobile/watch schemes (capacitor://localhost, ionic://localhost, twoody://), and dev localhost/LAN during development. Everything else gets a CORS preflight rejection before the request ever touches application code.

Credentials are disabled on CORS — a third-party origin can't get a session cookie from our API even if it tried. This is the modern replacement for the older "wide-open" CORS the app used to ship with.

Abuse signal feedback

Every authentication failure (wrong password, unknown email, expired confirmation code, replayed JWT, throttle hit) is tagged with a failure type and reason, and forwarded to our monitoring. A sudden spike in a specific failure class (say, unknown_email from one AS number) is visible on a dashboard within seconds and can be escalated to a CDN block without redeploying.

The same channel feeds the security@ mailbox: an unusual pattern that isn't yet a throttle hit can still be reviewed manually before it scales.

A few seconds of 429s is the expected cost of being connected to the open internet. We tune the budgets to make legitimate use invisible and abuse expensive — never the other way around.

Related topics