Surqo is an end-to-end IoT and AI platform built entirely from free-tier cloud services, an async Python backend, a React server-components frontend, and a $15 hardware node. Every technology choice was made to minimise cost at the target scale of ~500 users while keeping the codebase fully typed, observable, and testable. The sections below describe each layer in detail, from the ESP32 firmware running in a Colombian field to the Vercel Edge deployment serving the dashboard.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/ricardomb-tech/surqo/llms.txt
Use this file to discover all available pages before exploring further.
Backend Stack
The backend is a fully async Python 3.11 application built on FastAPI and deployed as a single-process Uvicorn server on Fly.io. All database I/O uses SQLAlchemy 2.0 async sessions with theasyncpg driver, so the event loop is never blocked by a database query.
| Technology | Version | Role |
|---|---|---|
| Python | 3.11 | Runtime |
| FastAPI | 0.115+ | HTTP + WebSocket framework |
| uv (Astral) | latest | Package manager (10–100× faster than pip) |
| SQLAlchemy | 2.0 async | ORM |
| asyncpg | latest | Async PostgreSQL driver |
| Pydantic v2 | latest | Validation + Settings |
| paho-mqtt | latest | MQTT client for HiveMQ |
| httpx | 0.27+ | Async HTTP (Open-Meteo, LLM APIs) |
| redis | latest | Cache + alert cooldowns |
| resend | latest | Email SDK |
| anthropic | latest | Claude SDK |
| slowapi | latest | Rate limiting per-IP |
| logfire | 0.50+ | Structured observability |
| pytest + pytest-asyncio | latest | 86+ tests with SQLite in-memory |
The test suite uses SQLite in-memory databases via pytest fixtures in
conftest.py, so no external Supabase connection is needed to run uv run pytest tests/ -v. All 86+ tests pass on a clean checkout with only the dev dependencies installed.Frontend Stack
The frontend is a Next.js 16 application using the App Router with React Server Components for data-heavy pages and client components for real-time WebSocket feeds. Authentication state is managed server-side using@supabase/ssr with HTTP-only cookies, so protected routes are enforced before any JavaScript is sent to the browser.
| Technology | Version | Role |
|---|---|---|
| Next.js | 16.x | App Router, SSR, Edge |
| React | 19.0+ | UI |
| TypeScript | 5.x | Type safety |
| Tailwind CSS | 3.4+ | Styles |
| @supabase/ssr | 0.10+ | Auth SSR (HTTP-only cookies) |
| Framer Motion | 11.0+ | Animations |
| Recharts | 2.x | SVG charts |
| Lucide React | 0.400+ | Icons |
Hardware
Each sensor node costs approximately $15 USD assembled and delivers roughly two weeks of autonomy on a pair of 18650 Li-Ion cells. The deep sleep current draw is ~10µA — the ESP32 is effectively off between readings.| Component | Spec | Cost |
|---|---|---|
| ESP32 WROOM-32 | Dual-core 240MHz, WiFi | $4 USD |
| DHT22 | Temp ±0.5°C, Humidity ±2% | $2 USD |
| DS18B20 waterproof | Soil temp −55/125°C, OneWire | $2 USD |
| Capacitive soil sensor v2.0 | 0–100% moisture, no corrosion | $1.5 USD |
| ML8511 | UV index 0–11+ | $2 USD |
| 2× 18650 Li-Ion | 2,000mAh, ~2 week autonomy | $4 USD |
| Total node cost | — | ~$15 USD |
External Services
Supabase
PostgreSQL database and Auth. Provides Row Level Security policies, JWT issuance with ES256 keys, and a managed connection pooler. The JWKS endpoint exposes the P-256 public key used by the backend to verify tokens without a network call on each request.
HiveMQ Cloud
Managed MQTT broker. The free tier supports 100 simultaneous connections, which is sufficient for hundreds of sensor nodes sending one message every 15 minutes. TLS on port 8883 encrypts all firmware-to-cloud traffic.
Upstash Redis
Serverless Redis with pay-per-request pricing. Used for two purposes: caching Open-Meteo API responses for 1 hour, and enforcing the 30-minute alert email cooldown per farm. The free tier provides 10,000 requests per day.
Resend
Transactional email API with high deliverability. The backend sends HTML alert emails with sensor readings and recommended actions. The free tier provides 3,000 emails per month.
LLM Provider Comparison
llm_service.py selects a provider at startup based on the LLM_PROVIDER environment variable. All three implementations satisfy the same LLMProvider protocol, so switching providers requires only a configuration change — no code change.
| Provider | Model | Cost | Use Case |
|---|---|---|---|
| Groq | Llama 3.3 70B (llama-3.3-70b-versatile) | $0 (14.4K req/day) | Primary — all production analyses |
| Anthropic | Claude Haiku 4.x (claude-haiku-4-5-20251001) | ~$0.0003/analysis | Fallback — premium accuracy |
| Ollama | Local model (configurable) | $0 | Local development — no network or quota needed |
- Groq
- Anthropic
- Ollama
The
GroqProvider class in llm_service.py calls the Groq OpenAI-compatible chat completions endpoint with temperature: 0.2 for analysis and temperature: 0.4 for conversational chat. When json_mode=True is set, the request includes "response_format": {"type": "json_object"} to prevent malformed JSON from reasoning-style responses.For image-based crop diagnosis (chat with photo), the provider automatically switches to meta-llama/llama-4-scout-17b-16e-instruct and injects the base64-encoded image as a multimodal message content block.