
Nerkhbaan
In productionنرخبان · 2026 — Present
A price platform for gold, currencies, and crypto with history, alerts, and web, admin, and desktop apps. Every quote names its source and time and carries an explicit expiry, so an outdated price is never shown as current.
- Role
- Founder & sole engineer
- Category
- Backend · DevOps · Product engineering
- Status
- Live at nerkhbaan.ir (release 2.8.1)
- Evidence
- nerkhbaan.ir (opens in a new tab)Source code (opens in a new tab)
Key facts
- Live release
- 2.8.1
- API paths (OpenAPI)
- 117
- CI jobs passed
- 15/15
- p95 in a 30-min load test
- 323 ms
From the project’s status record and CI, October 2026. The load test ran on an isolated copy of the stack.
My part
- Built the whole product: FastAPI API, TimescaleDB and Redis data layer, React web and admin apps, an Electron desktop app, and a Go feed sidecar.
- Run it in production on Docker Compose: two API replicas behind Nginx, migrations gated by readiness, and scheduled backups with a tested restore.
- Every release passes 15 CI jobs, including integration tests on real TimescaleDB and Redis, Playwright browser tests, and Trivy scans of seven images.
Stack
- FastAPI
- TimescaleDB
- Redis
- Go
- React
- Electron
- Docker Compose
- Nginx
- GitHub Actions
- Prometheus
- Trivy
Read the case study
Architecture
Feed job
A scheduled GitHub Actions job fetches public market APIs every 5 minutes, checks ranges and age, and publishes one JSON feed.
Go sidecar
Pulls the feed every 60 s, re-validates it, and pushes quotes to the API with an HMAC-SHA256 signature. It holds no database or admin credentials.
FastAPI × 2
Verifies signatures and replays, refreshes each instrument under a Redis lease, flags anomalies, and assigns stale and expiry times.
TimescaleDB + Redis
TimescaleDB keeps price history. Redis holds leases, the quote cache, live-update pub/sub, and a persistence outbox.
Nginx
TLS, rate limits, REST and WebSocket routing to the API replicas.
Clients
React web app (PWA), React admin panel, and Electron desktop app. Live prices over WebSocket, with polling as fallback.
Engineering case study
Problem and constraints
- Show prices where every number states its instrument, unit, source, and time, and never present an outdated price as current.
- Units and purities are never converted or estimated: a missing direct gold price stays unavailable.
- Runs on a single server in Iran, where some foreign data providers are not reachable.
Data freshness
- Each quote stores when it was observed plus explicit stale and expiry times; its status moves from live to stale to expired against the clock.
- Stale, suspicious, expired, or unpersisted quotes are never shown as current.
- Disagreeing sources are flagged by volatility-adjusted anomaly checks, and confirmation requires an independent source.
Fault handling
- Only one replica refreshes a given instrument at a time, using Redis leases with owner-checked release.
- Provider calls have request budgets, retries, and a circuit breaker; a stale primary source falls back to a fresh secondary.
- If a database write fails, the quote is queued in a Redis stream outbox and marked unpersisted until it is flushed.
- If Redis is down, refresh pauses, stored values keep serving, and WebSocket clients fall back to polling.
- Readiness returns 200 only when the database is reachable and migrations are current; Redis and refresh health are reported alongside.
CI/CD and deployment
- 15 CI jobs: locked Node builds, Python unit tests, the Go sidecar, Compose and monitoring config checks, Playwright browser tests, dependency and secret scans, and Trivy scans of seven images that fail on any HIGH or CRITICAL finding.
- Integration tests run on real TimescaleDB and Redis, apply migrations twice to prove they are idempotent, verify migration checksums, and run a restore drill.
- Production is Docker Compose on one host with two API replicas. Release 2.8.1 was rolled out by scaling to three replicas and removing the old ones one at a time.
Observability and security
- Eight Prometheus alert rules cover API down, unavailable or expired prices, stalled refresh, provider failure rate, dead letters, persistence backlog, and Redis memory; CI validates them with promtool.
- The admin panel uses separate session cookies, CSRF protection, permission checks, re-authentication, and required TOTP MFA.
- Containers drop all capabilities with no-new-privileges, the API port is internal only, and Python dependencies are hash-pinned and checked with pip-audit.
Verified outcomes
- Release 2.8.1 went live on nerkhbaan.ir in October 2026 after all 15 CI jobs passed and the final API image showed no HIGH or CRITICAL findings.
- Load test on an isolated copy of the stack: 27,000 of 27,000 requests succeeded over 30 minutes at about 15 requests per second, p95 323 ms, with 200 WebSocket clients connected and no errors.
- Fault tests on the same copy: all 200 WebSocket clients recovered from an API shutdown in 3.7 s and resynchronized after a Redis outage in 4.5 s.
- A full database restore matched every table and row count.
Known limitations
- Single host, with no disaster-recovery site.
- Zero downtime is not claimed: the 2.8.1 rollout recorded two short readiness timeouts.
- Load figures cover anonymous price reads and live streams, not authenticated admin traffic.
- The scheduled feed job depends on GitHub’s cron, which has delayed runs by hours.
