VEEMANDocs
DocumentationSupportOpen Dashboard

Getting started

  • Introduction
  • Quick start
  • Core concepts

Importing data

  • Supported formats
  • How import works
  • Splitting a file
  • Troubleshooting

Building your portfolio

  • Workspace & equity
  • Weights & capital
  • Weekday filter
  • Library
  • Variants & comparison
  • History (undo/redo)
  • Shared spaces

Analytics

  • Metrics reference
  • Charts (deep-dive)
  • Monthly P&L
  • Correlations
  • Strategy comparison
  • Monte Carlo
  • Equity Control
  • Optimization
  • Veeman AI

Reference

  • Keyboard & command palette
  • Exporting data
  • Settings

Technical

  • Architecture
  • API reference
  • Security
  • Your data & privacy
  • Risk disclosure
← PreviousSettingsNext →API reference

Architecture

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.

Components

LayerTechnologyHosted on
FrontendNext.js (App Router), React, TypeScript, Tailwind, RechartsVercel
BackendFastAPI (Python), NumPy / pandas / SciPy for the mathsRender (Docker)
Auth & databaseSupabase (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.

Request flow

Browser ──▶ Next.js (Vercel) ──▶ FastAPI (Render) ──▶ Session manager (in-memory)
                                       │                       │
                                       ▼                       ▼
                               Auth verification          Math engines
                              (Supabase JWT)            (NumPy/pandas/SciPy)
                                       │
                                       ▼
                                Storage (files / Postgres)
  1. The browser calls the API with a bearer token.
  2. The backend verifies the token and resolves your user_id.
  3. The request is served from your in-memory session (or persisted storage).
  4. Engines compute the result and return JSON; the frontend draws it.

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.

Sessions

Your working set lives in an in-memory session keyed to your user_id:

  • Time-to-live: 24 hours, swept periodically.
  • Isolated — a session can only be read by the user who owns it.
  • Held in RAM — a backend restart (a deploy, or an idle instance waking back up) empties it.

Session drafts (autosave)

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 light record — the recipe (capital, mode, weights, visibility, sizing, costs, groups, weekday filters) plus provenance and the unsaved-changes marker. A few KB, rewritten on every change.
  • a heavy record — the strategies with their series embedded and the trade-level companion CSVs. Rewritten only when the data itself changes: an upload, a confirmed import, an update, a delete or a rename.

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.

Storage

Persistence goes through one storage abstraction with two backends:

  • File storage — local development; JSON and CSV under a per-user folder.
  • Postgres storage — production; a single 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.

Authentication (dual-mode)

The backend accepts two auth regimes so the same code runs locally and in production:

  • Supabase (production) — an Authorization: Bearer <jwt> header, verified by shared secret (HS256) or by Supabase's public keys (JWKS). Your user_id is the Supabase UUID.
  • Legacy cookie (local dev) — a backend-signed JWT cookie; the 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.

Deployment

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.