TradeScope AI — Project Scope & Status
Prepared: 2026-07-07. Updated 2026-07-08 after the P0 security-hardening pass and the P1 compliance-core build. Audience: incoming engineering team.
This document describes what the platform is, what is already built and verified working on each side, what is simulated versus real, and which phase the project is in right now.
0. The whole app on one chart
dashboard / web trader / wallet /
KYC / AI trading / live traders / news"] end subgraph Admin["Admin Zone - tradescope-admin.pages.dev"] AZ["Same bundle, /admin routes
CRM / workspace / money / KYC review /
calling / dialer / import / RBAC / settings"] end subgraph Backend["FastAPI on Render - web-app-test-two.onrender.com"] API["32 routers under /dashboard
~50 store modules, lazy settlement engine"] end subgraph Data["Supabase"] PG[("Postgres
39 migrations, RLS on")] ST[("Storage
KYC docs / recordings")] end LK["LiveKit
web calls / PSTN / dialer"] WB["whatsapp-bridge"] FEEDS["Quote refs: Coinbase, Frankfurter
News RSS: Yahoo, Google, Investing.com"] CRON["Cloudflare Worker cron
keep-alive /health"] CZ -->|"public client-scoped calls"| API AZ -->|"X-Operator-Token + RBAC"| API API --> PG API --> ST API <--> LK WB --> API API --> FEEDS CRON --> API
Module status chart
| # | Module | Side | Status | What it runs on |
|---|---|---|---|---|
| 1 | Operator auth + RBAC (7 roles, 18-perm matrix, audit) | Admin | LIVE, real | Server tokens, scrypt, fail-closed |
| 2 | CRM clients/leads + client card + phones directory | Admin | LIVE, real | Supabase, per-field audit |
| 3 | Money ledger (deposit/withdraw/adjust + approve/reject) | Both | LIVE, real bookkeeping | Single decide() path; no PSP connected |
| 4 | Balance Control (admin manual adjustments) | Admin | LIVE, real (2026-07-07) | Ledger adjustment rows, overdraw-guarded, audited |
| 5 | User Management overview | Admin | LIVE, real (2026-07-07) | /users-overview join: CRM + balances + flags + AI + positions |
| 6 | Platform Settings | Admin | LIVE, server-owned (2026-07-07) | min-deposit + withdrawals enforced; spread/commission multipliers applied; leverage/signups stored, labeled not-yet-enforced |
| 7 | Web Trader terminal (35 instruments, pending orders) | Client | LIVE | Simulated fills on the sim book |
| 8 | Charts | Client | LIVE, server history (2026-07-07) | /web-trader/candles, deterministic, gapless to live mid |
| 9 | Market news feed | Client | LIVE, real headlines (2026-07-07) | /news RSS proxy, 10-min cache |
| 10 | AI auto-trading + per-client loss-limit/profit-target | Both | LIVE, simulated PnL | Paper sessions; per-session overrides (2026-07-07) |
| 11 | AI Control monitoring | Admin | LIVE, real counts (2026-07-07) | /ai-trading/summary, agrees with reports |
| 12 | Live Traders board | Client | LIVE, real + anonymized (2026-07-07) | Salted-hash pseudonyms; honest empty state under 3 active sessions |
| 13 | KYC upload + review | Both | LIVE, real | Private Storage bucket, signed URLs |
| 14 | Mass Data Import (staging→commit→rollback) | Admin | LIVE, drilled 12/12 | Awaits security pass before real book |
| 15 | Web calls (LiveKit) + call log + recording seam | Both | LIVE, real | Recording env-gated |
| 16 | PSTN dial-out + predictive dialer + number health | Admin | Built, dry-run | Blocked on outbound SIP trunk env only |
| 17 | WhatsApp inbound inbox | Admin | LIVE, real | Bridge service; outbound = roadmap |
| 18 | SMS two-way | Admin | Seam built | Blocked on provider choice (3 env vars) |
| 19 | Agent Workspace (3-zone daily board) | Admin | LIVE (2026-07-07) | Priority tiers, agenda, inboxes |
| 20 | Client auth (real accounts, email + scrypt) | Client | LIVE, real; enforcement gate built DARK (2026-07-08) | client_users + opaque tokens; register/login/reset rate-limited, identity uniqueness enforced; ownership gate ships behind ENFORCE_CLIENT_AUTH (off until Ben's go) |
| 21 | Client identity & fraud intelligence | Both | LIVE, real (2026-07-07) | Device + IP/VPN/TOR intel (ip-api.com), stated-vs-verified consistency checks, ok/attention badge |
| 22 | Client credentials + welcome/reset email | Admin | LIVE (credentials); email seam-only (2026-07-07) | clients:credentials slug; email_provider.py returns 503 until Render env set |
| 23 | Market prices | Backend | Demo-grade sim | Nudged to real FX refs; vendor swap = one seam |
| 24 | Trade execution | Backend | Simulated by design | Real execution gated on broker bridge + license |
| 25 | Security hardening pass (P0) | Backend | LIVE 2026-07-08 | RLS (037/038), auth rate-limiting, CORS narrowed from *, dark client-ownership gate, Cloudflare real-IP fix; drilled live |
| 26 | Sanctions/PEP screening (P1) | Both | LIVE as scaffolding (2026-07-08) | Provider seam (503 until env), identity-keyed store, admin review UI + audit; advisory, real provider = go-live decision |
| 27 | KYC risk-tiering (P1) | Both | LIVE, DARK gate (2026-07-08) | Tiers 0-3 + per-tier limits; ENFORCE_KYC_TIER off (no-op proven live) at deposit/order/AI-session; limits pending legal sign-off |
1. What the product is
TradeScope AI is a forex/CFD broker platform with two user-facing zones plus a shared backend:
- Client Zone — a trading web app for the broker's clients: dashboard, multi-asset web trading terminal, wallet (deposits/withdrawals), KYC document submission, AI auto-trading opt-in, live chat and web calls with the operator team.
- Agent/Admin Zone — a full broker back-office CRM for the operations team: client management, lead pipeline, transaction approval, KYC review, calling (WebRTC + PSTN dial-out + predictive dialer), mass data import, reports, role-based access control, and a daily "agent workspace" board.
- Backend — one FastAPI service that powers both zones.
The business is a real forex-broker operation (owner: Ben). The platform currently runs on simulated market data and simulated trading fills by deliberate decision; the CRM, ledger records, calling infrastructure, auth, and audit systems are real.
2. Architecture, repos, environments
Repositories
| Repo | Location | Remote | Deploy |
|---|---|---|---|
| Frontend (both zones) | ~/apex-ai-app |
none — local only | Cloudflare Pages via wrangler |
| Backend | ~/web-app-test-two/backend |
github.com/benf-ux/web-app-test-two-backend |
Render (auto-deploy on push to main) |
| Legacy operator dashboard + call widget | ~/web-app-test-two/dashboard |
(same parent dir) | Cloudflare Pages project web-app-test-two |
Onboarding note: the frontend repo has no git remote. First team task should be pushing it to a private GitHub repo. All history is intact locally.
Frontend stack
Expo SDK 56 universal app (React Native + react-native-web + expo-router, TypeScript). One codebase renders the client zone, the admin zone, and (future) native iOS/Android. Web is the current focus; app-store submission is deliberately on hold until the web product is final. Charting uses TradingView lightweight-charts v5 (canvas) on web with a native SVG fallback. State is a small custom store (src/lib/data/store.ts) — the single seam between screens and the repository/fetch layer; screens never call the network directly.
Backend stack
FastAPI (Python 3.12) on Render (free tier, kept warm by a Cloudflare Worker cron pinging /health every 10 minutes). Persistence pattern used everywhere: Supabase Postgres (via PostgREST) is the store of record, with an in-memory write-through cache per store module that also serves as the whole store if Supabase is unconfigured. No ORM, no SQLite (Render disk is ephemeral). 35 SQL migrations (migrations/001 … 035), all additive and idempotent, applied via the Supabase Management API. Binary files (KYC docs, call recordings) live in Supabase Storage (private buckets, signed URLs); an optional Cloudflare R2 driver exists behind the same seam for larger files.
Live URLs
| Surface | URL |
|---|---|
| Client zone (production project) | https://tradescope-app.pages.dev |
| Client zone (preview alias, same bundle) | https://tradescope-web-preview.apex-ai-app.pages.dev |
| Client zone (custom domain, CNAME to the apex-ai-app Pages project) | clientzoneapp.faircroftcorebit.net |
| Admin zone | https://tradescope-admin.pages.dev (root 302 → /admin/login) |
| Backend API | https://web-app-test-two.onrender.com (all routes under /dashboard) |
| Legacy operator panel + embeddable call widget | https://web-app-test-two.pages.dev |
All three Pages targets are built from the same expo export --platform web bundle; the admin target adds a / → /admin/login redirect.
3. Client Zone — built and working
Authentication (client side) — REAL, RATE-LIMITED, ENFORCEMENT GATE BUILT DARK
Client authentication is now real as of 2026-07-07 (Client Card v2 build). Sign-up registers a genuine server account (client_users table, scrypt password hashing, SHA-256-hashed opaque session tokens with a 30-day TTL — the same pattern as the operator store). /client-auth/register|login|reset-request|profile are live. Sign-up also captures the device (expo-device/constants), the sign-up URL/referrer, and the client IP for the identity intelligence below.
Enforcement is built but dark. As of the P0 security pass (2026-07-08) an ownership gate (require_client_owner, behind the ENFORCE_CLIENT_AUTH env flag, default off) is wired onto every identity-scoped client endpoint (web-trader, ai-trading, KYC, transactions, visit ping, chat history) — a no-op today, so the legacy public endpoints behave exactly as before and pre-auth users are never locked out. Two real hardening changes did land live in the same pass: the public /client-auth/register|login|reset-request routes are now rate-limited per real client IP (resolved from Cloudflare's CF-Connecting-IP, since the app sits behind Cloudflare → Render), and client_users.identity is uniqueness-enforced at the app layer and by a DB unique index (migration 038), so a second account can no longer claim an existing client's identity to defeat the gate. Flipping ENFORCE_CLIENT_AUTH on is the remaining cutover and still needs Ben's explicit go plus the client-side 401/403/429 UI states.
Screens that exist and work
Dashboard (accounts, quick actions, balance), Accounts / New Account / Account Details, Wallet with Deposit and Withdraw flows, Transactions history, Transfer, Analysis, Live Traders (real anonymized board since 2026-07-07: active AI sessions rendered under salted-hash pseudonyms, honest empty state below 3 active sessions), TradeScope AI (header stats — balance, buying power, daily P/L, open trades — now read the real account payload and AI session status; the market-analysis visual stays cosmetic and is labeled SIMULATED; the news feed inside it shows real RSS headlines), AI Settings with per-client daily-loss-limit and profit-target steppers + a blocking AI-trading opt-in disclosure gate, Verification (KYC upload), Personal Info, Profile, Settings, Support, Notifications, Referrals, Terms gate, Sign-in/Sign-up (desktop split-screen design), password-reset stub, and the in-call screen (call.tsx) with chat.
Web Trader terminal (flagship, rebuilt to professional grade 2026-07-06)
Desktop layout ≥1024px: watchlist rail (35 instruments: 10 FX, 10 commodities, 10 stocks, 5 indices) with class filters and search, chart area, order ticket column, positions panel, account bar. Mobile: stacked layout with a watchlist overlay.
Chart engine (TradingView lightweight-charts v5):
- ~1,500 bars of deterministic history per (symbol, timeframe), generated backward from the live price so the last close always equals the live anchor. History is frozen per symbol/timeframe for the session; polls only update the last bar. Zoom and pan survive every data refresh by construction.
- Mouse-wheel zoom, Ctrl/Cmd+wheel accelerated zoom anchored at the cursor, drag-to-pan, axis drag scaling, double-click view reset, pinch on touch.
- Live OHLC legend following the crosshair, dotted bid/ask spread lines, countdown-to-bar-close chip, on-chart price lines for every open position (entry/SL/TP) and pending order.
- Timeframes 1m/5m/15m/1H/1D; candles/line/area; SMA-20/EMA-50 overlays.
- Keyboard shortcuts (+/- zoom, arrows pan, F fullscreen, 1-5 timeframes, with input-focus guards), element fullscreen, and a one-click "ARMED" trading mode that skips confirmation dialogs (persisted per browser).
- QA seam:
window.__apexChartexposes read-only chart state for automated tests.
Order management: market orders, limit and stop pending orders (MT-style placement validation; SL/TP validated against the entry price; pending orders reserve no margin; fills happen at the stored entry when the book crosses it, with a margin check at fill time and auto-cancel if insufficient), position modify (SL/TP), close, cancel, Open/Pending/History tabs. The account engine computes balance, equity, floating PnL, margin, free margin, margin level; commissions per instrument class and overnight swap are simulated.
Verified live via automated Playwright drills (21/22 passing; the one failure is a headless-browser fullscreen artifact, not an app bug).
Client communication
Web call button (LiveKit WebRTC) connecting the client to an operator room, with in-call text chat over the LiveKit data channel; every message is also persisted to the CRM. A public "visit ping" updates the client's last-seen time on the admin card.
4. Agent/Admin Zone — built and working
Operator authentication + RBAC v2 (live, production-drilled 14/14)
Real server-side operator accounts: scrypt password hashes, SHA-256-hashed session tokens with 7-day expiry, fail-closed auth on every admin endpoint. 7 roles (owner, admin, account_manager, retention_manager, conversion_manager, it_manager, agent) with an owner-editable 18-permission matrix at /admin/roles (30s cache; changes take effect live). Owner bypasses the matrix; the last active owner cannot be demoted. Permission denials return 403 "Missing permission: <slug>". The old shared-secret backdoor has been fully removed. Every operator action writes to a per-field audit trail (crm_audit_log) with the real operator username.
Agent Workspace (admin Overview — live 2026-07-07)
Three-zone daily board: systematically prioritized client lists (tier order: new FTD ≤7 days → deposit pending → meeting scheduled → plain → no-answer last, annotated server-side on every client list), a 7-day meetings/callbacks agenda (reminders with snooze/dismiss, red flag for missed-call meetings, flashing imminent meetings, browser notification + chime), and a WhatsApp + SMS inbox with per-operator unread counts. SMS is provider-ready (webhook + send seam wired, returns 503 until a provider's three env vars are set on Render).
Clients (CRM)
Full client/lead pipeline: create/edit/delete with duplicate detection (identity → phone → email), lead vs client status, deposit status roll-ups, per-client flags (do-not-call, high-risk, complaint), consent timestamps, notes. In-place client expansion on the list (click a row → key-details and more-details accordions, no page jump) plus a full client card with collapsible sections: facts row (created date, FTD date, last visit, client-local time from IANA timezone), qualification profile (age, occupation, trading experience, savings, retirement fund — labeled Superannuation/Building Society by country), live trading account summary (balance, equity, P/L, trades, win rate, weekly/monthly result), KYC documents review, transactions, calls, tasks, phones directory (multiple numbers per client with primary mirroring), communication channel buttons (Web call / Phone call / WhatsApp), and the AI-trading session card. Every field edit is audited.
Client Card v2 — identity intelligence, credentials, onboarding (live 2026-07-07)
The client card gained a broker-grade identity and fraud layer:
- Stated vs verified identity. Each profile field carries a client-stated value and an operator-verified value. Approving a KYC document (rides the existing
kyc:decidepermission) pops a skippable "verified-fill" modal so the operator can promote stated → verified in place. - Consistency checks (
identity_checks.py, pure functions): stated-vs-verified per field, email-vs-name (informational), timezone-vs-IP, phone dial-code-vs-country, and proxy/hosting/TOR warnings, rolled into a single ok / attention / unknown badge on the Identity card. - Device + IP/VPN intelligence. Sign-up captures the device (expo-device/constants) and the client IP; the backend resolves it through free keyless ip-api.com (45 req/min, HTTP, fail-open to "unknown") plus a TOR exit-node list, cached in-process 24h and in an
ip_intel_cachetable. A "Relay/Hosting" badge flags datacenter or VPN egress. Live proxy detection was proven during the drill (the drill machine's own egress returned proxy = true). - Credentials block. With the new
clients:credentialspermission (16th slug; admin + it_manager by default, owner bypasses), an operator can view a client's account state and set a temporary password. "Include credentials" emails an auto-set temp password (to be replaced by a reset link once a real email provider is wired). - Welcome / reset email is a seam only (
email_provider.py, mirroring the SMS provider): setEMAIL_PROVIDER_URL/KEY/FROM+CLIENT_APP_URLon Render to activate; until then it returns a clean 503 and logs tonotification_logtypeemail.
Backend was independently verified live by this session (route POST /client-auth/login responds, migrations 034 + 035 applied in Supabase); the build's own record reports test suites 47/47 + 43/43 green and a 31/31 production drill.
Compliance — sanctions/PEP screening + KYC risk-tiering (P1, live 2026-07-08 as dark scaffolding)
The forex-broker compliance layer, built as preparation without wiring a real compliance vendor (that connection is a deliberate go-live decision). Everything ships so production behavior is unchanged until it is switched on.
- Sanctions/PEP screening — the client card gained a Screening panel (status: not-run / clear / review / blocked / cleared, last run, match summary) with operator actions to run a screen and to clear or confirm a hit, gated by a new
compliance:screenpermission and audited. Screening runs through a provider seam (sanctions_provider.py, mirroring the SMS/email seams): withSANCTIONS_PROVIDER_URL/KEYunset it returns a clean 503 "provider not configured". State lives in an identity-keyed store (client_screening+ aclient_screening_runshistory). It is advisory — it records and surfaces risk, it does not hard-block money on its own. - KYC risk-tiering — an identity-keyed tier 0–3 per client (
client_risk_tier), a suggested tier computed from KYC/verification/screening signals, and an operator override control on the card gated by a newkyc:set_tierpermission. Per-tier limits (TIER_LIMITS, placeholder values pending compliance/legal sign-off) feed a dark gate (kyc_tier_gate.py, behindENFORCE_KYC_TIER, default off) wired at the three money/trading chokepoints — deposit request, manual order, AI-session start. With the flag off it is a proven no-op (a deposit above a tier-0 cap still succeeds live); with it on, an over-limit deposit or a not-permitted trade is refused. Enforcement reads the materialized tier (default 0 = fail-closed), never a join to the UUID-keyed client row.
New backend: routers client_screening + client_risk_tier under /dashboard, stores client_screening_store/client_risk_tier_store, sanctions_provider.py, kyc_tier_gate.py, migration 039 (three identity-keyed tables, RLS on). Local suite test_compliance 39/39. Flipping ENFORCE_KYC_TIER on needs Ben's go and requires tiers to be set first (un-tiered clients fail-closed to tier 0).
Money
Transaction ledger (crm_transactions): client deposit/withdraw requests → pending rows → admin approve/reject with a single decide() write path, decided_by recorded, client balance credited on completion, deposit fields recomputed. Verified end-to-end on production (client submits → admin approves → client sees completed with correct totals). Balance Control screen for manual adjustments. No real payment provider is connected — the ledger is real bookkeeping, money movement is not.
KYC review
Client uploads (jpeg/png/pdf, ≤10MB) go to a private Supabase Storage bucket; admin reviews via signed URLs (each view audited), approves/rejects per document; aggregate client KYC status recomputed automatically. Document types include ID, utility bill, and source-of-funds.
Mass Data Import (Ben: "the core foundation for this CRM") — live, drilled 12/12
Staging-first pipeline for importing an existing broker book: upload CSV/XLSX (25MB / 50k rows) → column auto-mapping with synonym + Hebrew-header + value-pattern detection and reusable mapping templates → validation with dedup preview → chunked idempotent commit engine (≤200 rows/tick, cursor-resumable, crash-safe) → full rollback (children first, merge targets never deleted, deposit fields recomputed). Lineage table guarantees idempotency. Call-recording files upload via presigned URLs to the storage seam (Supabase Storage free driver live, 50MB/object cap; R2 driver ready as an env-var upgrade for bigger files).
Calling stack
- Web calls: admin call center (
/admin/call) joins LiveKit rooms with clients; call log with duration, outcome, and recording linkage; call recording via LiveKit Egress → S3-compatible storage (env-gated). - PSTN dial-out:
dial-clientand permission-gated custom dial (with DNC guard via a phone → client reverse lookup) place a real phone call into the same LiveKit room the operator sits in. Blocked on one thing: an outbound SIP trunk (DIALER_OUTBOUND_TRUNK_IDenv). Until set, endpoints return a cleandialed:falsedry-run. Inbound trunkST_ewgoxxYiNUnpexists and is live for number routing. - Predictive dialer: campaigns, agent presence, pacing governor with a hard 2% internal abandoned-call ceiling (US TCPA legal line is 3%; ≥3% latches and requires manual reset), abandoned-call handling, claim-race-safe entry assignment (verified with concurrent requests against production). Compliance constants are load-bearing — do not loosen without owner sign-off.
- Phone Numbers / Agents / Number Health: number inventory with bulk paste import, agent directory, per-number LiveKit dispatch-rule assignment (verified creating/tearing down real dispatch rules), daily number-health job (answer rates, best call windows, quiet-hours violations, env-gated spam-score checking).
- WhatsApp: a separate bridge service feeds inbound WhatsApp messages into the CRM inbox (inbound auth fixed 2026-07-07). Outbound business messaging via the WhatsApp Cloud API is on the roadmap, not built.
Other admin screens
All of the following were rewired from seed data to the real backend on 2026-07-07 and verified with production drills:
- User Management — renders
GET /users-overview(one-round-trip join of CRM identity, real balance, open-position count, AI-active flag, view-only flag). The old fake suspend/activate and per-user AI switches were replaced by the real trading view-only toggle and a read-only AI badge. - Balance Control — posts real ledger adjustments (
POST /transactions/adjust, permissiontransactions:approve): amount validated, overdraw rejected, row borncompletedwithdecided_by, audit written, client balance actually moves (the client app shows it immediately). - Platform Settings — server-owned row (
GET/PATCH /platform-settings, permissiontrading:settings). Min-deposit and withdrawals-enabled are enforced at request creation with clear error messages; spread-markup and commission multipliers are applied inside the trading engine; leverage cap and signups are stored and honestly labeled "not yet enforced". - AI Control — header counts come from
GET /ai-trading/summary(active sessions, trades, win rate) and match the reports rollup; global enable / emergency stop / risk / capacity were already server-enforced. - Transactions — filter segments All / Deposits / Withdrawals / Adjustments (server-side
kindparam), adjustment rows get a distinct badge; approve/reject/CSV unchanged.
Also: Reports (funnel + KPIs with collapsible sections), Operators management, Roles & Permissions matrix, Data Import wizard, Dialer console.
5. Backend inventory
- 32 routers under
/dashboard(client_auth is client-facing, mounted at the root): crm, transactions, kyc, client_profile, tasks, calendar, inbox, whatsapp, notifications, call_log, calling, recording, webrtc_dashboard, phone_numbers, number_health, dialer, ai_trading, web_trader, import_router, import_commit_router, reports, operator_auth, users_overview, platform_settings, news, live_traders, client_auth, client_admin, client_identity, client_screening, client_risk_tier, plus the SMS webhook. - ~50 store modules, all following the same Supabase-authoritative + memory write-through pattern (
store_base.pyprovides shared id/time helpers).platform_settings_storeadds the peek()/warm() discipline (sync cache-only reads in hot paths, async 30s-TTL refresh riding existing entry points) shared withquote_providerandnews_provider.client_user_storecopies the operator-store auth pattern (scrypt + SHA-256-hashed opaque tokens);ip_intel/identity_checks/device_snapshot_storepower the Client Card v2 intelligence;client_screening_store/client_risk_tier_store(identity-keyed, mirroringclient_trading_flags_store) back the P1 compliance layer, withsanctions_provider.pyas the vendor seam andkyc_tier_gate.py/client_owner.pyas the two dark env-gated enforcement seams.rate_limit.py/client_ip.pyadd the P0 per-IP throttle keyed on Cloudflare's real-client IP. - 39 migrations applied through
039(032 = ledger adjustment type, 033 = platform_settings, 034 = client auth accounts + tokens, 035 = stated/verified identity + device snapshots + IP-intel cache, 036 = chat channel, 037 = RLS defense-in-depth across the sensitive set, 038 =client_users.identityunique index, 039 = compliance core: screening + screening-runs + risk-tier tables). Numbering discipline matters: concurrent work streams claim numbers, so always check the real directory before creating one. - 18 permission slugs in the RBAC matrix; the newest are
compliance:screenandkyc:set_tier(P1 compliance), afterclients:credentials(Client Card v2). Tests assert againstlen(PERMISSIONS), not a literal count. - Auth model: operator endpoints require
X-Operator-Tokenand fail closed, permissions checked per-slug viarequire_permission. Client endpoints remain public and scoped to aclient_idstring today, but the P0 pass added a dark ownership gate (require_client_owner,ENFORCE_CLIENT_AUTH) over every identity-scoped one, rate-limited the public auth routes, narrowed CORS from*to an explicit allowlist (CORS_ALLOWED_ORIGINS), and re-asserted RLS on the whole sensitive table set — so flipping enforcement on is now a config + client-UI step, not a backend one. The P1 KYC-tier gate (ENFORCE_KYC_TIER) is a second dark seam over the money/trading paths. - Key engine detail: there is no background worker (free Render tier). Everything that looks scheduled is lazy: SL/TP triggers, pending-order fills, and AI-trading ticks settle on the next read. The one true cron is the external keep-alive Worker.
- Test suites live next to the code and run standalone in memory mode: web-trader v2 + pending orders, pacing governor, RBAC, workspace suites, import pipeline, the realness suites (adjustments, users-overview, settings, candles, news, AI overrides, live traders), Client Card v2 (
test_client_auth,test_identity_intel), the P0 security suites (test_rate_limit,test_client_owner), and the P1test_compliance. They are the regression gate before any deploy.
6. What is real vs simulated (critical for the team)
| Area | Status |
|---|---|
| Market prices | Demo-grade: deterministic two-scale random walk, nudged toward real reference rates for FX (Coinbase → Frankfurter, free keyless APIs, stale-while-error caching). Not an execution feed. A paid vendor (Polygon/Finnhub/broker feed) is deliberately deferred until a paying client exists; the swap point is a single seam (quote_provider.py / _raw_mid). |
| Chart history | Real server history of the sim book (GET /web-trader/candles): deterministic sampling of the same price function the trading engine fills against, gapless, last bar closes at the live mid. The client chart loads it with a synthetic offline fallback. |
| Market news | Real headlines (GET /news): backend RSS proxy over Yahoo Finance, Google News, Investing.com; 10-minute cache, stale-while-error. |
| Trade execution | Simulated end-to-end (fills at the sim book, no broker). Real execution is gated on an MT4/MT5/cTrader bridge (not built) and the owner's trading license (in progress). No live-execution code path exists anywhere by design. |
| AI auto-trading | Simulated (paper sessions, Gaussian PnL per risk profile, server-enforced stops and global emergency stop). Session records, counts, and the admin summary are real. |
| Live Traders board | Real and anonymized: active AI sessions only, pseudonyms from a salted hash, no identity fields in the payload, honest empty state below 3 active sessions. Nothing is fabricated. |
| Admin screens (users / balances / settings / AI control / transactions) | Real backend data end-to-end since 2026-07-07. No seed arrays remain in the admin bundle. |
| Client money | Ledger records are real and audited; no payment provider is connected. |
| Client auth | Real accounts; rate-limited; enforcement gate built DARK (2026-07-08): /client-auth/* live with scrypt + opaque tokens; public auth routes now rate-limited (per real client IP via Cloudflare CF-Connecting-IP); identity uniqueness enforced. The ENFORCE_CLIENT_AUTH ownership gate is wired but off, so legacy public endpoints are unchanged and no one is locked out; the flip needs Ben's go + client 401/403/429 UI. |
| Client identity intelligence | Real: device + IP/VPN/TOR resolution and stated-vs-verified consistency checks (live proxy detection proven). |
| Welcome / reset email | Seam only: 503 until an email provider's env vars are set on Render. |
| Sanctions/PEP screening | Scaffolding live, provider not wired (2026-07-08): admin screening panel + run/clear/confirm review + audit; sanctions_provider.py returns 503 until a vendor env is set. Advisory; connecting a real provider is a go-live decision. |
| KYC risk-tiering | Live, dark gate (2026-07-08): identity-keyed tiers 0-3 + per-tier limits; ENFORCE_KYC_TIER off (no-op proven live) at deposit/order/AI-session start; TIER_LIMITS are placeholders pending compliance/legal sign-off. |
| Security posture (P0 pass) | Hardened live (2026-07-08): RLS re-asserted on the sensitive table set, auth rate-limiting, CORS narrowed from * to an allowlist, Cloudflare real-IP resolution, dark ownership gate. Secret rotation is the one manual step still owed. |
| Operator auth / RBAC / audit | Real and enforced in production. |
| Calling (WebRTC) | Real (LiveKit). |
| Calling (PSTN out) | Built and verified in dry-run; blocked on outbound SIP trunk credentials only. |
| SMS | Seam built; blocked on choosing a provider (3 env vars). |
| WhatsApp inbound | Real via the bridge. Outbound Cloud API: roadmap. |
| KYC docs, recordings, imports | Real (Supabase Storage / optional R2). |
| Push-to-call (mobile push) | Built, blocked on FCM/EAS prerequisites. |
7. Current phase
The demo/sim platform is feature-complete and live, every screen reads the real backend, and the security + compliance foundations for real-client onboarding are now in place. Recent builds: the professional web-trader terminal (2026-07-06), the agent workspace (2026-07-07), the "kill the demo data" realness build (2026-07-07), Client Card v2 (2026-07-07), then the P0 security-hardening pass (2026-07-08: backend e74dae4+ea96c64+099f90c+8eec51e, migrations 037/038) and the P1 compliance core (2026-07-08: backend f3d76fc + migration 039, frontend bdcd3ba). Both repos are clean, committed, deployed, and verified.
P0 security hardening is done (the phase agreed with the owner on 2026-07-05, before any real client data is mass-imported): RLS re-asserted across the sensitive table set (037/038), API rate limiting on the public auth routes, CORS narrowed from * to an explicit allowlist, a Cloudflare real-client-IP fix, a dark client-ownership gate over every identity-scoped endpoint, and client_users.identity uniqueness enforced. A live gate-drill caught and fixed a rate-limiter that was inert behind Cloudflare. An independent security review caught and fixed an identity-collision that would have defeated the ownership gate. Two things remain before the enforcement flip: secret rotation (a manual owner step) and the client-side 401/403/429 UI, plus Ben's explicit go to turn ENFORCE_CLIENT_AUTH on.
P1 compliance core is done as dark scaffolding (owner directive: build everything possible now, wire real compliance at go-live): sanctions/PEP screening (provider seam, admin review UI, audit) and KYC risk-tiering (identity-keyed tiers + a dark ENFORCE_KYC_TIER gate over the money/trading paths). Nothing enforces yet; production behavior is unchanged. Before enforcement: legal/compliance sign-off on TIER_LIMITS, a real screening vendor, and tiers assigned to clients.
The next external gate is go-live infrastructure and licensing — everything runs on free trials today; the first real-client mass import and real money movement require paid upgrades (Supabase Pro, Render always-on) and licenses (PSP merchant account, trading license/broker bridge, WhatsApp/SMS/email providers). This is tracked as a standing checklist, surfaced to the owner when a specific upgrade becomes the blocker.
Open blockers that need external action (not code)
- Outbound SIP trunk credentials →
DIALER_OUTBOUND_TRUNK_IDon Render (unblocks all PSTN dialing). - SMS provider choice → 3 env vars (unblocks two-way SMS in the workspace inbox).
- WhatsApp Cloud API onboarding (unblocks outbound WhatsApp + calling API).
- TURN server for WebRTC (currently no TURN — calls fail on symmetric-NAT networks).
- Payment service provider (real deposits/withdrawals).
- Broker bridge + trading license (real execution).
- Render Starter upgrade (~$7/mo) to retire the keep-alive Worker and the free-tier hour cap.
- Business registration completion → Free Caller Registry for number reputation.
Known deferred/backlog items (by decision, not accident)
Web-trader: drawing tools, draggable SL/TP price lines, order expiry (GTC only today), partial close, cancelled-order history view, depth-of-market ladder. CRM: IB/affiliate commissions, malware scan on uploads, OCR autofill of verified identity (operator fills manually at approval for now); sanctions/PEP screening and KYC risk-tiering now exist as dark scaffolding (P1, 2026-07-08) awaiting a real screening vendor + legal sign-off on the tier limits before enforcement. Workspace: month-grid calendar, per-thread read state. Client zone: real server-side client accounts exist with a dark enforcement gate and rate-limited auth routes; still open are the enforcement cutover (flip ENFORCE_CLIENT_AUTH + the client 401/403/429 UI) and a real email provider for welcome/reset. Platform: production domain cutover for the admin zone, legacy clientzoneapp alias strategy, secret rotation, iOS/Android store submission (on hold until web is final).
8. Access and secrets (locations only — no values in this document)
- Cloudflare Pages deploy token and Supabase Management API token: a secrets store on the owner's machine.
- Render env holds:
OPERATOR_SECRET(whatsapp-bridge server-to-server only),SUPABASE_URL/SUPABASE_SERVICE_KEY, LiveKit keys, the gated integrations' vars (RECORDING_S3_*,SMS_*,EMAIL_PROVIDER_*,DIALER_OUTBOUND_TRUNK_ID,R2_*,SANCTIONS_PROVIDER_*), and the security/compliance controls (CORS_ALLOWED_ORIGINS,RATE_LIMIT_*, and the two dark enforcement flagsENFORCE_CLIENT_AUTHandENFORCE_KYC_TIER— both unset/off in production today). - Admin login: username withheld (owner-held); password held by the owner.
- Client zone needs no credentials (demo auth).
9. Working practices the team should keep
- Additive, idempotent migrations only; verify the highest migration number in the repo and what actually ran in Supabase before writing the next one.
- Every store follows the Supabase-or-memory write-through pattern; keep it — tests depend on memory mode.
- Screens talk to the store, the store talks to the repository; do not fetch from screens.
- Run the standalone backend test suites and
tsc/eslint/expo exportbefore deploying; deploy frontend from a clean worktree; verify live with a Playwright drill after deploying. - The dialer pacing constants and the DNC guard are compliance-load-bearing. Treat them as frozen without owner sign-off.