Production-oriented REST API built with FastAPI, focused on two things:
π Keeping data consistent under concurrent operations. π Learning to defend itself from attacks in real time.
A behavioral analysis system that learns from every request.
Request β Logs (eyes) β PostgreSQL (memory) β Random Forest (reasoning) β Block or Allow
- Random Forest trained on synthetic traffic (NORMAL / SUSPICIOUS / BLOCKED)
class_weight="balanced"to handle class imbalanceml_predictor.pyloads the model and assistsagent_defender.pyin real time- Auto-override if model confidence β₯ 85%
- Feedback loop: every request is stored in DB β next training cycle improves the model
The model doesn't just block known attacks β it learns patterns and blocks what the rules didn't anticipate.
| Class | F1-Score | Notes |
|---|---|---|
| NORMAL | 0.93 | Clean browsing patterns |
| BLOQUEADO | 0.96 | High-confidence attack detection |
| SOSPECHOSO | 0.45 | Intentionally hard β this is a transitional state that escalates to BLOQUEADO quickly, so few samples exist by design |
Overall accuracy: 92%
Live API endpoint: https://inventory-api-kbvx.onrender.com
Most beginner APIs work fine⦠until real-world conditions hit:
- Multiple users modifying the same resource
- Race conditions corrupting data
- Business rules enforced inconsistently
This project focuses on solving those problems.
This project intentionally goes beyond MVP scope β concurrency control, Redis-based token management, and real infrastructure testing β to understand how production systems handle these problems before encountering them at scale.
How do you guarantee stock consistency when multiple requests hit the same product at the same time?
- Read stock
- Modify value
- Save
User A reads stock = 1 User B reads stock = 1 Both write β stock = -1 β
-
Row-level locking using
SELECT FOR UPDATEinside database transactions, ensuring concurrent requests cannot read stale values before mutation -
Atomic stock operations
-
Domain-level validation before persistence
-
Rate limiting on all endpoints via middleware and nginx reverse proxy, preventing brute force and DoS attacks
π Result: Stock remains consistent under concurrent requests
api/
βββ routes/ β HTTP handling, maps errors to status codes
βββ services/ β Business logic & domain rules
βββ models/ β Persistence (SQLAlchemy ORM)
βββ domain/ β Domain exceptions (InsufficientStockError, ProductNotFoundError)
βββ schemas/ β Input/output validation (Pydantic)
βββ core/ β JWT, password hashing & rate limiting
βββ middleware/ β Rate limiting + structured audit logging
βββ db/ β Session management
βββ tests/ β Unit, integration & concurrency tests
Business logic stays independent from the web framework.
This structure allows business rules to be tested independently from the HTTP layer and database implementation.
SELECT FOR UPDATE locks rows before mutation, preventing race conditions during simultaneous stock updates.
Business rules enforced in the service layer:
- No negative stock
- Typed exceptions (
InsufficientStockError,ProductNotFoundError) - Mapped to HTTP responses in routes
Network architecture β single entry point:
- Only port 80 (Nginx) exposed to the exterior
- Redis, PostgreSQL, PgBouncer, and API have no exposed ports
- All internal communication via Docker network only
Defense in depth:
- TLS/HTTPS β managed by Render (Let's Encrypt, automatic renewal) β encrypted transport in production
- Nginx reverse proxy with rate limiting (30 req/s per IP, burst 50) β first line of defense
- ModSecurity WAF + OWASP CRS β 846 rules active, blocks SQL injection, XSS and known attacks (HTTP 403)
- ModSecurity CRS Paranoia Level 2 β blocks malicious user-agents (sqlmap, nikto, scrapy) and requests missing standard browser headers at network level
- Argon2 password hashing (resistant to GPU/ASIC attacks)
- JWT-based authentication with refresh token rotation and server-side revocation (Redis)
- CORS restricted to specific origins (configured via environment variable)
- Rate limiting via middleware (Redis) β applied to all endpoints
- PgBouncer β connection pooling, no direct database access from exterior
- Agent detection middleware β behavioral analysis before requests reach business logic:
- Honeypot endpoints β flags and blocks automation instantly
- IP blocking via Redis (1 hour TTL)
- Timing analysis β detects non-human request intervals via standard deviation (< 50ms variability = agent)
- Behavioral analysis agent (
agent_defender) β evaluates each action in real time via scoring:- Mass product scraping detection
- Stock manipulation without prior product lookup
- Automated off-hours activity detection
- Repetitive sequence detection β same endpoints looping = bot
- Repeated stock manipulation detection
- Decisions:
NORMAL/SUSPICIOUS/BLOCKED - Dual blacklist: IP-based (1h TTL) + user_id-based (1h or 24hs based on score) β proxy-resistant
- Long-term memory (24hs): cross-session tracking β detects attackers who recon first, attack later
- Recon/attack correlation: if β₯2 recon signals in long_history (24hs) + active attack now β score Γ3.0 multiplier
- Adaptive thresholds: block thresholds adjust dynamically based on time of day (night = stricter), system-wide attack pressure (global block counter in Redis), and recidivism β thresholds can drop up to 66% for high-risk contexts
- ML layer: Random Forest model trained on real traffic assists decisions in real time β auto-override at β₯85% confidence
- A01 Broken Access Control β owner_id enforced on all product endpoints (list, get, movements) β users only see their own data. Generic 404 on all not-found responses to avoid confirming resource existence
- A02 Cryptographic Failures β Argon2 password hashing + TLS in transit (Render)
- A03 Injection β SQLAlchemy ORM, no raw queries + ModSecurity WAF blocking injection patterns at network level
- A05 Security Misconfiguration β CORS restricted to specific origins, no exposed ports except Nginx
- A06 Vulnerable Components β dependency scanning via pip-audit in CI
- A07 Authentication Failures β JWT, rate limiting on auth endpoints, refresh token rotation. Timing attack mitigation: dummy Argon2 hash computed on non-existent users to normalize response time and prevent user enumeration
- A08 Software and Data Integrity β Pydantic input validation, transactional rollbacks
- A09 Logging Failures β structured audit logging on all endpoints + agent detection events (honeypot_triggered, agent_detected_timing, blocked_ip_request)
Known gaps (intentional trade-offs):
- Encryption at rest β PostgreSQL data on disk is not encrypted β requires filesystem-level or pgcrypto solution, deferred for this stage
- Unit tests for business logic
- Integration tests for full HTTP flow (Redis mocked for isolation)
- CI via GitHub Actions on every push
- Concurrency tests against real PostgreSQL via Testcontainers to validate
SELECT FOR UPDATE - Concurrency tests against real Redis via Testcontainers to validate rate limiting behavior
Structured logging across two layers:
- Middleware β every request: method, path, status code, duration, user_id, IP
- Service layer β business events: stock_lock_waiting, stock_lock_acquired, stock_updated (before/after/delta), stock_insufficient
Under 1000 concurrent requests (rate limiter disabled for testing), logs confirmed:
- SELECT FOR UPDATE serialized all operations correctly
- No request read a stale stock value
- System saturated at PostgreSQL max_connections=100 β infrastructure limit, not a code bug. Protected in production by rate limiting (10 req/min on stock endpoint)
βοΈ Fast, no DB server needed in CI
β No support for SELECT FOR UPDATE
π Concurrency tests run against real PostgreSQL to validate locking behavior. Unit and integration tests use SQLite for speed.
This introduces a gap between test and production behavior, which is an intentional trade-off for faster CI execution.
βοΈ Integration tests mock Redis for speed and isolation β Mock doesn't validate real rate limiting behavior
π Concurrency tests run against real Redis via Testcontainers to validate blocking behavior.
Under 1000 concurrent requests, PostgreSQL max_connections=100 was saturated β 500 errors. In production this cannot occur β rate limiting caps stock endpoint at 10 req/min. Production-scale solution implemented: PgBouncer with transaction pooling (max 1000 client connections β 20 real PostgreSQL connections).
FastAPI Β· PostgreSQL Β· SQLAlchemy Β· Pydantic Β· Argon2 Β· JWT Β· Alembic Β· Docker Β· Redis Β· Pytest Β· Testcontainers Β· GitHub Actions Β· Nginx Β· ModSecurity Β· PgBouncer Β· scikit-learn Β· Random Forest Β· SMOTE
This project is not about building an API that works.