Security, abuse prevention, rate-limiter placement, and multi-tenant design
The defensive stack — where each control belongs, and the keys that make them work.
Concept → your projects
Defense in depth, ordered by where it sits (the layering you should be able to draw):
- Edge: WAF/DDoS, TLS, IP reputation, global rate limits (coarse, keyed by IP/ASN).
- Gateway/auth boundary: authentication (JWT/API key/OAuth), per-principal rate limits, request size limits, schema validation.
- Application: authorization (per-resource, deny-by-default), idempotency, input validation, output encoding, audit logging.
- Data: encryption at rest, tenant scoping in every query, least-privilege DB users, secrets management, backup isolation.
- Detection/response: anomaly alerts, suspicious-activity signals, incident response, forensics (your tamper-evident audit log).
Rate limiter placement — the decision matrix (the topic your list names explicitly): key × placement.
- Keys: IP (coarse, shared by NAT — false positives), user (needs auth first), API key/tenant (the right unit for B2B — zoth's choice), route+user (per-endpoint budgets), and combinations (
tenant:route). - Placement: edge (protects everything, no app context), gateway/middleware (auth-aware, cheap to change), service-level per-dependency (protects downstream — the AI ladder, GitHub API), and per-user budgets for expensive operations (recon: 60/15 min).
- Algorithms: fixed window (simplest, boundary bursts), sliding window (Redis ZSET:
ZREMRANGEBYSCOREolder-than-window,ZCARD, add — exact rolling limit at the cost of memory per key; or sliding-log-counter: two weighted fixed windows — cheaper, near-exact), token bucket (bursts allowed, O(1) state, the general-purpose favorite), leaky bucket (smoothed output). - Distributed correctness: in-process limiters fragment across replicas (both your projects' current state) → Redis/Lua for atomic refill or gateway-level enforcement.
Multi-tenant design (beyond Day 40's data isolation — the systemic view): noisy-neighbor controls (per-tenant rate limits and quotas, connection-pool partitioning, per-tenant concurrency caps), per-tenant encryption keys (KMS envelope, key rotation, crypto-shredding), per-tenant observability (metrics dimensioned by tenant so a noisy customer is visible before it's a page), fair scheduling (priority/weighted queues rather than FIFO across tenants — the classic "one tenant's backlog starves everyone" failure), and blast-radius planning (a tenant's data import can't consume global resources — the recon quota is the same instinct at user scale).
Abuse prevention specifics worth naming: enumeration defense (uniform 404s, timing-equalized responses — Day 72), credential stuffing (strict auth limits + lockouts + breach-list checks), scraping (rate limits + behavior heuristics), resource exhaustion (body-size caps, timeouts, pagination limits — every unbounded input is an attack surface), and cost abuse (the AI ladder is money per call — per-user budgets are a security control, not just fairness).
Your systems' mapping (and the honest gaps)
- Zoth: per-tenant token bucket implemented (unmounted — Day 24), API keys with write-scope, deny-by-default policy engine, tenant-scoped queries, AES-256-GCM credentials, audit chain. Gaps: limiter unmounted, no per-tenant connection/quota partitioning, single encryption key (no per-tenant KMS).
- Traverse: layered express-rate-limit (global 100/min, auth 5/15 min, recon 60/15 min), bcrypt-12, JWT with revocation-on-password-change, Razorpay HMAC, idempotency keys, redacted logs, BYOK key isolation. Gaps: in-memory limiters, no breach-list checks, timing-unsafe comparisons in two places (Day 90).
Bulletproof your resume
Say these answers aloud before moving on
These are the follow-ups an interviewer will actually ask about your projects.
Where should a rate limiter live?
Depends on what you're protecting: edge for volumetric/IP abuse (no app context needed), gateway/middleware for per-principal fairness (needs identity), and per-dependency budgets for downstream protection — my AI ladder and GitHub calls are the scarce resources, so their limits belong closest to them. Keys matter as much as placement: tenant for B2B (my zoth limiter is per-tenant), user+route for per-endpoint fairness, IP only as a coarse outer layer. And the distributed correctness rule: in-process counters fragment at N replicas, so anything security-meaningful goes to Redis or the gateway.
Design a sliding-window rate limiter.
Redis ZSET per key: on each request, ZREMRANGEBYSCORE key 0 (now-window) to drop expired entries, ZCARD to count, reject if ≥ limit, else ZADD now and set TTL. It's exact for a rolling window at the cost of one member per request (memory O(limit) per key). The cheaper approximation is two fixed windows weighted by overlap (sliding log counter) — near-exact, constant memory. Token buckets are the alternative when you want burst tolerance rather than strict windows. My current limiters are fixed-window in-memory; the upgrade path is this ZSET version in Redis, or a gateway-level limiter if the fleet grows.
How do you stop one tenant from degrading others?
Four controls, none of which I fully have yet: per-tenant rate limits and quotas (the limiter exists, unmounted), per-tenant concurrency caps or weighted fair queuing so one tenant's backlog can't starve the queue (BullMQ priorities and RabbitMQ prefetch are the primitives), per-tenant observability so the noisy neighbor is visible (dimension metrics by tenant_id — my spans carry it already), and resource-level quotas for expensive operations (traverse's recon quota is exactly this pattern at user scale). The design principle: fairness is a scheduling property, so enforce it where work is scheduled, not where it's requested.