VEEMAN is a conventional three-tier web application. This page explains what runs where and how a request flows — useful if you're evaluating the system or just curious.
| Layer | Technology | Hosted on |
|---|---|---|
| Frontend | Next.js (App Router), React, TypeScript, Tailwind, Recharts | Vercel |
| Backend | FastAPI (Python), NumPy / pandas / SciPy for the maths | Render (Docker) |
| Auth & database | Supabase (Postgres + Auth) | Supabase |
The frontend renders the UI and charts; the backend owns the analytical engines (equity aggregation, metrics, Monte Carlo, correlations, risk sizing); Supabase handles identity and persistent storage.
Browser ──▶ Next.js (Vercel) ──▶ FastAPI (Render) ──▶ Session manager (in-memory)
│ │
▼ ▼
Auth verification Math engines
(Supabase JWT) (NumPy/pandas/SciPy)
│
▼
Storage (files / Postgres)
user_id.No broker, no orders
There is no connection to any broker or exchange, no order routing, no live market data and no fund custody. The backend only ever processes the historical P/L you upload. See Security.
Your working set lives in an in-memory session keyed to your user_id:
Because the process does not live as long as the work, every session is continuously mirrored to a draft in storage, and rebuilt from it on demand. When a request arrives for a session id that is no longer in memory, the backend restores it from the draft under the same id — so the client's stored session id keeps working and the restart is invisible.
The draft is split in two records, because the cost is all in the P/L series:
A background loop compares the live session with what was last written and flushes only the difference, roughly every two seconds and once more on shutdown — so a graceful restart loses nothing and no keystroke ever waits on a database write. Drafts expire after 7 days; beyond the 5 most recent, dormant ones are pruned — drafts of sessions still live in memory are exempt.
This is why the app distinguishes the temporary session from things you explicitly save or freeze: a draft protects work in progress, it does not file it. See Core concepts.
Persistence goes through one storage abstraction with two backends:
user_data table keyed by (user_id, kind, name).The kind discriminates the data: portfolios, strategies, variants, history, drafts (the
session autosave above), and the
trade-level CSVs used by per-strategy sizing. Either backend is strictly per-user.
The backend accepts two auth regimes so the same code runs locally and in production:
Authorization: Bearer <jwt> header, verified by shared secret
(HS256) or by Supabase's public keys (JWKS). Your user_id is the Supabase UUID.user_id is a username.Routes are protected on both ends: the frontend guards /analysis/*, and every backend route requires
a verified identity. More in Security.
Frontend deploys to Vercel on push; the backend is a Docker image on Render; Supabase provides auth and the database. Configuration is entirely via environment variables — no secrets in the code.