Vibe Codes

Investipet

A paper-trading Tamagotchi: feed a dinosaur whose mood is your algorithms' performance.

$50.00

App kit · The files to make your own copy · made by DanVelasko

Updated 13 Oct 2026 · version 2

No reviews yet · 0 comments

6/10 Used for real, still rough edges
How developed it is, scored by the maker's AI against a fixed rubric. Details under Tech specs.

Get it · $50.00 See it running Ask DanVelasko to build yours

Pay once, keep it forever. Card payment through Stripe.

What it does

Investipet is a paper-trading Tamagotchi. You keep a cartoon dinosaur. You feed him, play with him and put him to sleep — and his hunger, energy and happiness bars are driven by how a portfolio of trading algorithms is actually doing. A hungry dino is an underperforming portfolio.

The algorithms are real. They run on real daily prices for about 11,500 US stocks and ETFs, server-side, inside a locked-down sandbox, with a trusted engine doing the position sizing and the protective stops. A user writes rules in any language — JavaScript, Python, or plain English — pastes them in, and the platform translates and test-runs them before they are allowed to trade. There is a "Copy AI prompt" button that hands any AI chat the exact market data and code shape the platform accepts, so a complete beginner can get a working algorithm out of ChatGPT and paste it straight back in.

What is included: the entire front end (one self-contained HTML file — no build step, no framework, no npm install), 47 Supabase Edge Functions, 47 SQL migrations across 53 tables, a zero-dependency Node backtesting harness with 99 passing tests, screenshots, logo and icons, and a 20-section handoff document.

What you must set up: a Supabase project (free tier is enough to start) and somewhere to serve one HTML file. Free Alpaca and FRED keys get the market data flowing. Stripe keys only matter if you want the marketplace. Full step-by-step setup is in docs/HANDOFF.md.

What is honestly not finished. This ran in production for about four months with a handful of real users and roughly 300 paper trades, and the paper engine, the pet, the learning quiz, the social features and guest mode all work. But the live-trading path has never placed a real order, the in-app marketplace has never made a sale, and three of the nine data feeds are stale or broken (one needs a free API key; two broke on an upstream change). The 47 edge functions have no automated tests. The handoff names every one of these, with evidence.

What you are really buying: not a turnkey business. A working, in-production, multi-tenant paper-trading engine with a genuinely original interface, four months of real iteration behind it, and no dependencies to rot.

What you get

The screensEverything you see and tap: pages, buttons, layouts, styling.
The engineWhat makes it work behind the scenes: data, accounts, payments, servers.
InstructionsStep-by-step setup and a guide to changing it into your own.
Pictures & logoScreenshots, logo files, colours and fonts.

A paid license to use it in your own projects. You cannot resell the kit itself.

Straight talk

What the maker's AI found when it looked at the app, after running it. Vibe Codes checks this against the package.

75% complete

A genuinely original, genuinely working product that has been live for four months with real users and about 300 paper trades behind it. The paper engine is the real thing: a trusted sizing-and-stops engine wrapped around a locked-down sandbox that runs strangers' code against real prices for 11,500 symbols, with a Tamagotchi on the front that makes portfolio health legible at a glance. It has no build step and no npm dependencies, which means nothing in it rots. Buy it for the engine and the interface idea. Do not buy it expecting a finished business: the live-trading path has never placed an

WorksThe pet: idle/eat/sleep/play animations, hunger/energy/happiness bars driven by portfolio health, sleep that earns energy over wall-clock time · Market Academy learning quiz with per-account level and XP · Home performance curve over 1D/1W/1M/6M/1Y, press-and-drag to read any single day · Multi-algorithm paper portfolio: cash pool plus a per-algorithm cash sleeve with its own allocation · Paste an algorithm in any language; it is translated and test-run in a QuickJS sandbox before it can trade · 'Copy AI prompt' hands any AI chat the platform's exact data and code contract · Trusted-engine risk rails: 2.5xATR protective stop, 34% per-name exposure cap, $1M minimum daily dollar volume · Sell a position back to cash with the realised profit or loss · Market data for ~11,500 US symbols with up to 500 daily closes each · News, SEC filings and short-volume feeds, all fetching successfully today · Leaderboard, friends by dino name, PvP algorithm battles, public profile pages · Guest mode: paper book only, no dollar amounts, first-tap coach marks, persistent banner · In-app bug reporting with an owner-only queue · Global killswitch independent of paper/live mode · 99 passing unit tests and a walk-forward backtester that runs on synthetic data with no credentials
Not working yetMacro data has never worked: 94 consecutive failures. This is the one open data bug and the fix is one free API key — set FRED_API_KEY. The app's own log says so on every failure. · Live trading has never placed a real order: live_orders and broker_connections are both empty · Per-user Alpaca sandbox brokerage accounts fail — the Broker API returned 401 and alpaca_accounts is empty · The ML pipeline never learns: training_samples is empty · Implied volatility is inert until Public.com credentials are supplied; equity_iv is empty · audit_log holds one row — the order audit trail is not being written · No realtime anywhere: the UI polls, so two people viewing the same screen do not see each other's changes
Not testedThe entire marketplace money path: 0 sales, 0 rentals, 1 listing. Stripe Connect and the AES-GCM code sealing are written but never exercised end to end · The Big Baller subscription: 1 row, never charged · Live mode arming, the live order gates and the live mirror tables · All 47 edge functions have no automated tests of any kind · Deploying this package into a fresh Supabase project from scratch — the setup steps are written from the code, not from a clean-room run · Behaviour above roughly 50-100 active algorithms, where the single-invocation daily tick is expected to need a queue
Needs these servicesSupabase (free tier ok) — auth, Postgres, edge functions, cron. Required. · Any static host for one HTML file; it ran on Railway at about $5/month. Required. · Alpaca Market Data (free tier ok) — daily bars and quotes · FRED (free) — macro series; without this key the macro feed fails every run · Alpaca Broker API (free sandbox) — per-user brokerage accounts; currently returning 401 · Robinhood agent MCP (free, needs an account) — fallback price history and earnings · SEC EDGAR / FINRA / Nasdaq Trader (free, no key) — filings, short volume, symbol lists. EDGAR requires a real contact email in the User-Agent; put your own in c · Public.com (free with an account) — implied volatility. Their docs describe the individual API as for personal, non-commercial use; settle that before relying o · Anthropic API (pay per use) — optional, translates non-JavaScript algorithms · Stripe (per transaction) — optional, only the marketplace and subscription need it
CollectsEmail address and password at sign-up, handled by Supabase Auth · A chosen pet name, which doubles as a public username and a public profile URL · Every paper trade, position and equity snapshot for the signed-in user · Learning-quiz progress per account · Bug reports, capturing the reporter's user id, the page, the build and the browser user agent · Broker OAuth tokens and credentials, if a user connects a brokerage — the most sensitive rows in the system · Nothing is sent to any analytics service: the app ships no tracking code
Good to knowIt executes code written by its users. That is the product, not an accident: algorithms run in QuickJS compiled to WASM with a 3-second CPU budget, a 160MB ceiling, no network and no filesystem, and a trusted engine does all position sizing, exposure caps and protective stops. Understand that bounda · Rented marketplace algorithms are stored AES-GCM encrypted and decrypted at run time. That is the feature, not obfuscation, but it means the server decrypts and executes stored payloads. · Row Level Security is enabled on every table with no policies, which is deny-all. All access goes through service-role edge functions, so the database enforces nothing and the entire security model is the auth check at the top of each function. A function that forgets its check exposes a table to an · The cron_key is an owner-equivalent bearer token: four functions map a caller presenting it onto the owner account. Treat it like a password. · The settings table is a singleton, so execution_mode and the killswitch are platform-wide rather than per-user. This needs to change before more than one person trades live. · The CSP allows static.cloudflareinsights.com because the original deployment sat behind Cloudflare, which injects its beacon at the edge. No analytics code is in this package; remove the allowance if you are not on Cloudflare. · Four retired functions ship as inert 410 stubs because Supabase cannot delete functions through its API. Each explains what it did and why it was retired. · The front end is one 1.9MB file that must download completely before anything renders. · supabase-js is imported as a floating major from esm.sh and resolved at deploy time; pin it if you need reproducible deploys. · This is a trading application. It is paper-only by construction — the broker's paper hosts are hardcoded, not configured — so taking it live is a code change somebody has to read and be responsible for. The live path has gates, rails and a killswitch, and it has never placed an order.

Reviews

No reviews yet. Used it? Be the first.

Peer reviewed: only people who bought, downloaded or opened it through Vibe Codes can leave stars. Anyone signed in can comment below.

Sign in to review

Tech specs

· for developers, and for when you are ready
Built with
Node.js, Supabase, Postgres, Railway, Single file
License
LicenseRef-Commercial
Includes
The screens · The engine · Instructions · Pictures & logo
Live at
https://www.investipet.com ↗
Listed
Last reachable

Development score: 6/10 — Used for real, still rough edges

Four months and 233 commits of sustained work, live in production with real users and about 300 paper trades, with migrations, error handling, documented deploys and 99 passing unit tests. It falls short of a 7 because the production trading path has no automated tests at all and seven separate authorization holes were found and closed in the five weeks before packaging, two of them during this packaging run.

Setup guide

  1. Create a Supabase project. Note the project ref and the publishable key.
  2. Run the migrations. cd backend && supabase link --project-ref <ref> && supabase db push
  3. Become the owner. Replace 00000000-0000-0000-0000-000000000000 with your own auth user id in the 18 function files and 4 migrations that carry it, and owner@example.com in 6 files.
  4. Set a cron key. openssl rand -hex 32, then update settings set cron_key = '<value>' where id = 1;
  5. Set your secrets. See .env.example. The minimum useful set is ALPACA_KEY_ID, ALPACA_SECRET_KEY and FRED_API_KEY — all free.
  6. Deploy the functions. Loop supabase functions deploy <name> --project-ref <ref> --no-verify-jwt over backend/supabase/functions/*/. The --no-verify-jwt flag is required: the functions do their own auth and guest mode has no JWT.
  7. Point the front end at your project. Replace YOUR_PROJECT_REF and REPLACE_WITH_YOUR_SUPABASE_PUBLISHABLE_KEY in ui/investipet_prototype.html and backend/server.js.
  8. Run it. cd backend && node server.js, then open http://localhost:3000. "Look around first" enters guest mode with no account.
  9. Schedule the crons. backend/supabase/migrations/0010_tick_cron.sql shows the shape. The ones that matter are market-sync, tick and risk-sweep.
  10. Prime the market data. Call market-sync?discover=1 once with your cron key, then let the schedule fill prices. Allow a day or two before momentum algorithms have enough history to act on.

Full detail, including every gotcha, is in docs/HANDOFF.md §9.

Full handoff document

· long on purpose; read the section you need

Investipet — handoff

Everything a new owner needs. Nobody reads all of it; read the section you need. Written against the package in this zip, by an AI assistant that read the code, ran what could be run, and queried the live production database for usage facts. Where something is broken or unproven, it says so.

---

1. Overview

Investipet is a paper-trading Tamagotchi. You keep a cartoon dinosaur. You feed him, play with him and put him to sleep — and his hunger, energy and happiness bars are driven by how a portfolio of trading algorithms is actually doing. Build algorithms, give each one a slice of a paper cash pool, and watch them trade the whole listed US market with fake money.

The hook is that the algorithms are real. They run server-side on real daily bars for ~11,500 US symbols, inside a locked-down JavaScript sandbox, with a trusted engine doing the position sizing and the protective stops. A user writes rules in any language — JavaScript, Python, or plain English — pastes them in, and the platform translates and test-runs them before they are allowed to trade.

Who it is for: people who want to learn how systematic trading works without risking money, and builders who want an unusually complete reference for a multi-tenant trading backend on Supabase.

How it came to be: built between 20 June 2026 and 12 October 2026 — roughly four months, 233 commits (218 by the author, 15 co-authored with Claude). It was built conversationally with Claude (Claude Code), with the author directing architecture, doing the design and the dinosaur artwork, and running every deployment. It has been live at investipet.com for most of that time with a small number of real users.

Scale of the thing: a 1.9 MB single-file front end, 47 Supabase Edge Functions, 47 SQL migrations across 53 tables, and a separate zero-dependency Node backtesting harness with 99 passing tests.

---

2. Feature inventory

Verified on 12–13 October 2026 against the running app at www.investipet.com and the production database.

The pet

| Feature | State | Detail | |---|---|---| | Dinosaur with idle/eat/sleep/play animations | WORKS | Sprite sheets + MP4s, ambient blinks and scratches on timers. | | Hunger / energy / happiness bars | WORKS | Decay every 30s; targets recomputed from portfolio health every 60s. | | Feeding and playing | WORKS | +42 hunger, +34 happiness per action. | | Sleep earns energy over time | WORKS | +10% energy per 5s asleep, credited as a delta off wall-clock, so a backgrounded tab still pays out. | | Level and XP | WORKS | Driven by the Academy quiz. | | Market Academy (learning quiz) | WORKS | Per-account progress; 5 accounts have progress rows. |

The portfolio

| Feature | State | Detail | |---|---|---| | Performance curve on the home screen | WORKS | Labelled "algorithms". 1D/1W/1M/6M/1Y. Press and drag to read a day. 9,841 snapshots recorded. | | Algorithms list with per-sleeve allocation | WORKS | Cash + one card per algorithm, each with its own cash sleeve. | | Per-algorithm detail: watchlist, open positions, history | WORKS | 43 open and 211 closed paper trades in production. | | Sell a position back to cash | WORKS | Closes the trade, logs the exit, returns proceeds ± P&L to that sleeve's cash. Fixed 7 Oct 2026. | | Add an algorithm by pasting code | WORKS | Any language in, decide(ctx) out. | | "Copy AI prompt" | WORKS | Copies a full platform contract to paste into any AI chat. | | "Check compatibility" before saving | WORKS | Dry-runs the code in the sandbox against a real market snapshot. | | Protective stops and exposure caps | WORKS | 2.5×ATR stop, 34% max of a sleeve in one name, $1M minimum daily dollar volume. Engine-side, so sandboxed code cannot remove them. | | Killswitch | WORKS | Global halt independent of paper/live mode. | | Ratchet exits and their narration | WORKS | manage walks the stop up behind a winner every 15 minutes, so a position exits on a stop that may sit well above entry. The after-action text used to call every fast exit "Stopped — cut inside the window", winners included, because it tested an exit reason the engine never sets. Fixed 13 Oct 2026; it now reads off entry vs exit. |

Social and marketplace

| Feature | State | Detail | |---|---|---| | Leaderboard | WORKS | Ranked real returns across intervals. | | Friends by dino name | WORKS | 9 accounts exist. | | PvP algorithm battles | WORKS | 5 battles recorded. | | Public profile at /<DinoName> | WORKS | View-only page. | | Guest mode | WORKS | Paper book only, no dollar amounts, coach-marks on first tap, persistent banner. | | Bug reporting | WORKS | Any user files; only the owner sees the queue. 3 reports already filed. | | Marketplace listing / renting algorithms | PARTIAL | The code path is complete — AES-GCM sealed code, Stripe Connect payouts, monthly rentals. 1 listing exists; 0 sales and 0 rentals have ever happened, so the money path is unproven end to end. | | Big Baller subscription (Stripe) | PARTIAL | 1 subscription row. Same caveat. |

Live trading

| Feature | State | Detail | |---|---|---| | Paper → live mode switch | UNTESTED in anger | Gated on owner identity + armed state + a live_access entitlement + killswitch. | | Live order placement | NEVER RUN | live_orders is empty and broker_connections is empty. Not one real order has ever been placed by this system. | | Per-user Alpaca sandbox accounts | BROKEN | alpaca_accounts is empty; the Broker API returned 401 at last attempt. Credentials were never successfully set. |

Data feeds

| Feed | State | Detail | |---|---|---| | Daily bars / prices (market-sync) | WORKS | 11,522 symbols cached with up to 500 daily closes each. | | News | WORKS | 13,081 rows, 1,025 successful fetches, last one today. | | SEC filings | WORKS | 276 successful fetches, last one today. | | Short volume | WORKS | 41 successful fetches, last one today. | | Fundamentals | WORKS | Was stale since 12 Aug 2026 — not a code bug, nothing was scheduling it. Re-tested by hand on 13 Oct (ok) and given its own cron job the same day. | | Earnings | WORKS | Stopped 8 Aug 2026 on an upstream schema change that was later fixed in code — but nothing re-scheduled it, so it never ran again. Re-tested by hand on 13 Oct (5/5 ok) and given its own cron job. | | Macro (FRED) | BROKEN | 94 consecutive failures, still failing today. The app's own log says it: "zero rows parsed — fredgraph.csv may be bot-blocked; set FRED_API_KEY (free) for the official API." macro_series is empty. A free FRED key fixes it. | | Implied volatility (Public.com) | INERT | equity_iv is empty. The adapter is written and wired; it does nothing until PUBLIC_API_TOKEN and PUBLIC_ACCOUNT_ID are set. | | ML / contextual-bandit pipeline | NOT RUNNING | training_samples is empty, model_weights has 1 row. The train function exists; the loop is not closed. |

---

3. Architecture

`` ┌───────────────────────────────────────┐ browser ───────► │ Railway (Node) backend/server.js │ │ static host + security headers + CSP │ └───────────────┬───────────────────────┘ │ serves one file ┌───────────────▼───────────────────────┐ │ ui/investipet_prototype.html 1.9 MB │ │ ┌─────────────────────────────────┐ │ │ │ shell: dino, bars, curve, menu │ │ │ │ ┌───────────────────────────┐ │ │ │ │ │ #pfFrame (srcdoc iframe) │ │ │ │ │ │ algorithms / positions UI │ │ │ │ │ └───────────────────────────┘ │ │ │ └─────────────────────────────────┘ │ └───────────────┬───────────────────────┘ │ fetch() + publishable key + user JWT ┌───────────────▼───────────────────────┐ │ Supabase Edge Functions (Deno) │ │ 47 of them, service-role │ └───────────────┬───────────────────────┘ │ ┌───────────────────────┼────────────────────────┐ ▼ ▼ ▼ ┌───────────────┐ ┌─────────────────┐ ┌────────────────┐ │ Postgres │ │ pg_cron × 12 │ │ outside world │ │ 53 tables │ │ tick, market- │ │ Alpaca, RH MCP │ │ RLS deny-all │ │ sync, risk- │ │ FRED, EDGAR, │ └───────────────┘ │ sweep, ... │ │ FINRA, Stripe │ └─────────────────┘ └────────────────┘ ``

Where state lives. All of it is in Postgres. The browser keeps only a session token, a guest flag, a book preference and which coach-marks have been seen, in localStorage.

What runs where. Nothing of consequence runs in the browser. Every decision that moves money — even paper money — happens in an edge function with the service-role key. The front end is a renderer.

The one unusual choice. The algorithms screen is a second complete HTML document, JSON-escaped into a string constant called OUTLIER inside the shell, and injected into a same-origin iframe via srcdoc when you tap the curve. It is one ~650,000-character line. See §16 for why, and §17 for how to work on it.

The trust boundary that matters. User algorithm code runs in QuickJS compiled to WASM (strategy-eval), with a 3-second CPU budget and a 160 MB memory ceiling, no network, no filesystem, no clock beyond what it is handed. It returns nothing but a list of {symbol, weight}. The trusted engine (strategy-run) decides how many shares that becomes, caps the position, sets the stop and writes the trade. Sandboxed code cannot place an order, exceed a cap, or remove a stop.

---

4. Data model

53 tables. The ones you need to understand:

Identity and accounts

  • profiles — one row per user. The dino's name is the username; it is unique and becomes /<DinoName>.
  • user_settings — per-user preferences.
  • settings — a singleton (id = 1) holding platform state: execution_mode (paper|live), halted (killswitch), cron_key. Not per-user. See §13.
  • live_access — which users hold the live-trading entitlement.
  • sessions, quiz_progress — Academy state, keyed per account.

Money and trading

  • paper_account — per-user paper cash pool.
  • strategies — one row per algorithm: name, code (or sealed code), allocation %, enabled, armed, per-sleeve cash.
  • trades — every paper position. status ∈ open|closed. Carries strategy_id, user_id, and an mgmt JSON blob with the stop, the estimated hold window, and the exit record.
  • strategy_equity, portfolio_history — equity curves, per algorithm and per fund.
  • live_orders, live_fills, live_equity, live_sleeves — the live mirror. All empty.
  • risk_limits, risk_halts — per-user risk rails.
  • audit_log — intended as the order audit trail. Has 1 row; effectively unused.

Market data (shared across all users, no personal data)

  • market_features — the big one: one row per symbol with precomputed momentum, SMA50/200, ATR, dollar volume, and up to 500 daily closes.
  • market_symbols, market_cache, equity_fundamentals, equity_earnings, equity_filings, equity_news, equity_short_volume, equity_short_stats, equity_iv, macro_series, data_fetch_log.

Marketplace and payments

  • marketplace_listings, marketplace_sales, algo_rentals, platform_fees, stripe_accounts, stripe_events, subscriptions.

Social

  • friends, battles, equity_notes, bug_reports.

Broker links

  • broker_connections, alpaca_accounts, alpaca_mirror_log, rh_tokens, rh_oauth_state, user_connections.

Where personal data lives. Email addresses are in auth.users only (managed by Supabase). profiles holds a chosen dino name, which is public by design. rh_tokens and broker_connections hold broker OAuth tokens and credentials — the most sensitive rows in the system. bug_reports captures the reporter's id, the page and the user agent.

---

5. Auth and permissions

Sign-in is Supabase Auth, email and password, with email confirmation. There is also a guest mode that needs no account: a guest sees the paper book only, never sees dollar amounts, and gets a coach-mark the first time they tap anything.

Roles are not a table. There are three, and they are determined differently:

  1. Guest — no JWT. Functions detect this and strip live fields, dollar

amounts and anything owner-only.

  1. Signed-in user — a normal JWT. Sees and acts on their own rows only.
  2. Owner — a single hardcoded UUID constant, OWNER_UID, repeated in 18

function files. In this package it is the placeholder 00000000-0000-0000-0000-000000000000. Set it to your own auth user id in all 18 files or nothing owner-gated will work. See §17.

How it is enforced — read this twice. Row Level Security is enabled on every table with no policies at all, which in Postgres means deny-all. No client can read or write a single row directly. Every byte of data access goes through an edge function holding the service-role key, which bypasses RLS entirely.

That is a deliberate design, and it has a consequence worth being blunt about: the database enforces nothing. The entire security model is the if statement at the top of each of 47 functions. When one of those checks is missing, the table is wide open to anyone holding the publishable key — which ships in the client to every visitor by design. That is not theoretical; see §13.

---

6. Payments and money

No real money has ever moved through this system. marketplace_sales is empty, algo_rentals is empty, live_orders is empty. Treat everything in this section as written-but-unproven.

  • Provider: Stripe, in two roles — Stripe Billing for the "Big Baller"

subscription ($20/month), and Stripe Connect Express for paying algorithm sellers.

  • Flow: a buyer rents an algorithm by the month. stripe-webhook is the

only place a purchase is fulfilled; it is the single trusted writer.

  • Algorithm code protection: a rented algorithm's source is stored

AES-GCM sealed with CODE_ENC_KEY. Only the trusted engine decrypts it, at run time. The plaintext never reaches a client or a query result — a renter can run an algorithm without reading it.

  • Fees: platform_fees records the platform's cut.
  • Failures: stripe_events gives idempotency against duplicate webhooks.
  • Refunds: not implemented.

---

7. External services

| Service | Used for | Cost | Credentials | Without it | |---|---|---|---|---| | Supabase | Everything: auth, Postgres, edge functions, cron | Free tier works; Pro at scale | Project ref + keys from the dashboard | Nothing runs | | Railway | Serves the static front end | ~$5/mo | Connect the repo | Host the HTML anywhere else | | Alpaca Market Data | Daily bars, quotes | Free tier | ALPACA_KEY_ID / ALPACA_SECRET_KEY | Falls back to the Robinhood MCP, slower and 10 symbols per call | | Alpaca Broker API | Per-user sandbox brokerage accounts | Free sandbox | ALPACA_BROKER_KEY / ALPACA_BROKER_SECRET | Feature does not work. It does not work today either — 401. | | Robinhood agent MCP | Residual price history, quotes, earnings | Free, needs an account | OAuth, stored in rh_tokens | Degraded coverage; earnings already broken | | FRED | Macro series | Free | FRED_API_KEY | Currently broken for exactly this reason | | Public.com | Implied volatility | Free with an account | PUBLIC_API_TOKEN / PUBLIC_ACCOUNT_ID | IV feature inert. Check their non-commercial terms first. | | SEC EDGAR / FINRA / Nasdaq Trader | Filings, short volume, symbol lists | Free, no key | None — but EDGAR requires a real contact email in the User-Agent. Put yours in context-sync/index.ts. | Those feeds stop | | Anthropic | Translating non-JS algorithms | Pay per use | CLAUDE_API_KEY | Users must paste JavaScript | | Stripe | Payments | Per transaction | STRIPE_SECRET_KEY / STRIPE_WEBHOOK_SECRET | Marketplace and subscription break; everything else fine |

Minimum to run: Supabase + somewhere to serve one HTML file. Everything else is optional, and the app degrades rather than falls over.

---

8. Environment variables

See .env.example at the root of this package for the annotated list. Two things that catch people:

  1. None of these come from a .env file. They are Supabase *function

secrets*: supabase secrets set NAME=value --project-ref <ref>.

  1. cron_key is not an environment variable. It lives in the database, in

settings.id = 1, and the cron jobs send it as an x-cron-key header.

SUPABASE_DB_URL is read by one function (direct Postgres, bypassing PostgREST).

---

9. Setup from zero

For somebody who has never seen this before, on a clean machine.

You need: Node 18+, the Supabase CLI, a Supabase account, a GitHub account.

  1. Create a Supabase project. Note the project ref (the subdomain, e.g.

abcdefghijklm) and the publishable key (Settings → API).

  1. Run the migrations, in order.

`` cd backend supabase link --project-ref <your-ref> supabase db push ` If db push argues about history, run the files in backend/supabase/migrations/` by number through the SQL editor instead.

  1. Set your owner id. Sign up once in the app (step 7) or create a user in

the dashboard, copy its UUID, then replace the placeholder everywhere: `` cd backend/supabase/functions grep -rl '00000000-0000-0000-0000-000000000000' . \ | xargs sed -i '' 's/00000000-0000-0000-0000-000000000000/<your-uuid>/g' ` (Drop the '' after -i on Linux.) Do the same for owner@example.com`.

  1. Set your cron key.

`` openssl rand -hex 32 # keep this ` `sql update settings set cron_key = '<that value>' where id = 1; ``

  1. Set your secrets (see .env.example). The minimum useful set is

ALPACA_KEY_ID, ALPACA_SECRET_KEY and FRED_API_KEY — all free.

  1. Deploy the functions.

`` cd backend for d in supabase/functions/*/; do n=$(basename "$d"); [ "$n" = "_shared" ] && continue supabase functions deploy "$n" --project-ref <your-ref> --no-verify-jwt done ` --no-verify-jwt` is required: these functions do their own auth, and guest mode has no JWT at all.

  1. Point the front end at your project. In

ui/investipet_prototype.html, replace every occurrence of:

  • YOUR_PROJECT_REF → your project ref
  • REPLACE_WITH_YOUR_SUPABASE_PUBLISHABLE_KEY → your publishable key

Do the same in backend/server.js (the CSP names the Supabase host).

  1. Run it.

`` cd backend && node server.js `` Open http://localhost:3000. You should get the landing page. "Look around first" enters guest mode without an account.

  1. Schedule the crons. backend/supabase/migrations/0010_tick_cron.sql

shows the shape. Replace the project ref, the publishable key and REPLACE_WITH_YOUR_CRON_KEY before running it. The jobs that matter: market-sync (prices), tick (daily engine), risk-sweep (intraday stops).

  1. Prime the market data. Call market-sync?discover=1 once with your cron

key to populate the symbol list, then let the scheduled jobs fill prices. Expect a day or two before market_features is deep enough for momentum algorithms to produce anything.

---

10. Deploying

How it ran in production: GitHub main → Railway, which runs node server.js behind nixpacks.toml (Node 20) and railway.json. The domain sat behind Cloudflare. Supabase hosts everything else.

What you must change:

  • The project ref and publishable key in the HTML and in server.js.
  • APP_URL for redirect URLs.
  • The share-link domain — search the HTML for investipet.com.
  • The CSP in server.js names static.cloudflareinsights.com. See §13.

Front end deploys are a file copy. There is no build step, no bundler, no npm install. Change the HTML, push, done.

---

11. Testing

What exists: 99 unit tests, all passing, in backend/research/: `` cd backend/research && npm test `` They cover the research engine: position sizing, trade lifecycle, execution and slippage, NAV, portfolio guards, sleeve maths, decay curves and the Academy quiz logic. Run them — they take about 130 ms.

There is also a walk-forward backtester you can run on synthetic data with no credentials: `` cd backend/research && npm run synthetic ``

What does NOT exist, and you should know it before you buy:

  • No tests on the 47 edge functions. None. The entire production trading

path — auth, order sizing, stops, the money — is untested by machine.

  • No integration tests. No end-to-end tests. No CI.
  • The research harness is a sibling of the production engine, not the same

code. Passing tests there do not prove the edge functions are correct.

  • deno check on the functions reports 26 type errors (16 are implicit

any; the rest are listed in §12).

The note in backend/research/package.json previously ran node --test src/, which fails on Node 20+. It has been corrected in this package.

---

12. Known bugs and limitations

  1. Macro data has never worked. 94 consecutive failures and counting. This

is the one open data bug, and the fix is one free API key: get a FRED key and set FRED_API_KEY (§7). The app's own log says so on every failure.

  1. Per-user Alpaca brokerage accounts do not work — Broker API 401.
  2. The ML pipeline is not closed. train exists; training_samples is empty.
  3. The live path has never executed an order. Treat it as unproven code.
  4. Four retired functions ship as inert 410 stubs — paper, propose,

rh-probe, rh-test. Supabase cannot delete functions through the API, so they are neutered in place. Each file explains what it used to do and why it was retired. Delete them from your own deployment if you prefer.

  1. 21 deno check errors, all implicit any or loose result typing. Two

real bugs that the type-checker had been flagging — an unreachable exit-reason branch in run and a duplicated object key in broker — were fixed on 13 Oct 2026. The Supabase edge runtime does not type-check on deploy, so none of these ever blocked anything.

  1. No realtime. The UI polls and refetches on action; there are no Supabase

Realtime subscriptions anywhere. Two people looking at the same leaderboard do not see each other's changes until something refetches.

  1. The front end is one 1.9 MB file that must be fully downloaded before

anything renders.

---

13. Security review

What was checked: every function's authorization gate; every query against a per-user table that is scoped by neither user_id nor a row id; secrets in source, config, comments and lockfiles; execution of untrusted code; the sandbox's escape surface; dependency provenance; what leaves the browser.

Found and fixed before packaging
  • rh-test was an unauthenticated read of the owner's brokerage account. Its

handler was Deno.serve(async () => {...}) — it never took the request, so there was no auth check of any kind. Any caller with the publishable key could make it read rh_tokens with the service role, open a Robinhood MCP session and return the account payload. Verified live (200 OK, account fields in the body), then retired to a 410 stub and redeployed. Fixed 12 Oct 2026.

  • rh-refresh was unauthenticated too. Same Deno.serve(async () => {...})

shape — no request object, no check. It returns no tokens, but any caller could force a refresh of every stored broker token and read back which users have one, and because the provider rotates the refresh token on use, a caller racing the scheduler could invalidate live connections. Now gated on the service-role bearer or the cron_key, exactly like market-sync and risk-sweep. The cron job was updated to send the key first, then the function was deployed; verified after as 401 unauthenticated and 200 through the cron path. Fixed 13 Oct 2026.

  • Secrets removed from the package: a live cron key committed in plaintext in

0010_tick_cron.sql, two Supabase publishable keys, two project refs, the author's auth user id in 22 files, and the author's email in 6. All replaced with named placeholders.

Fixed earlier in the project's life (within the last five weeks)
  • execute ran its live-trading gates before checking who was calling.
  • propose inserted into trades with no auth check at all.
  • paper let any signed-in user run trades.delete().eq("is_practice", true)

with no user filter — a global wipe of every user's positions.

  • academy keyed quiz progress by device id with no auth.
  • portfolio?mode=live served guests the owner's live equity curve.
Still open — a buyer should fix these
  • The cron_key is an owner-equivalent bearer token. blotter,

strategies, bugs and portfolio all map a caller presenting the cron key onto OWNER_UID. One leaked header value is full owner access. Treat it like a password; rotate it on any suspicion.

  • settings is a singleton. execution_mode and halted are

platform-wide, not per-user. If you grow past one trading operator, this needs to become per-user.

  • RLS is deny-all with no policies. Correct, but it means Postgres enforces

nothing — see §5. Any new function that forgets its auth check exposes a table. Consider real RLS policies as defence in depth.

  • audit_log has one row. The audit trail exists but is not being written.
Disclosed, kept by design
  • Untrusted code execution is the product. User algorithms run in QuickJS

(WASM) via npm:quickjs-emscripten@0.29.2, pinned. 3s CPU, 160 MB, no network, no filesystem. The trusted engine does the sizing and the stops.

  • Encrypted payloads are decrypted and executed at run time. That is the

rented-algorithm feature, not obfuscation. CODE_ENC_KEY is server-side only.

  • The CSP allows static.cloudflareinsights.com because the original

deployment sat behind Cloudflare, which injects its analytics beacon at the edge. No tracking code ships in this package — the app itself has no analytics, no Google Tag Manager, no Sentry, no Mixpanel. Remove the allowance from server.js if you are not on Cloudflare.

  • _shared/alpacaBroker.ts sends synthetic identities to the Alpaca

sandbox — the dino's name, an @investipet.com address and Alpaca's designated test SSN. No real personal data. Alpaca's paper hosts are hardcoded, so going to production is a reviewed code change, not an environment flip.

Dependency provenance

There are no npm dependencies. Both package.json files have empty dependency trees; the front end loads no framework. The Deno functions import @supabase/supabase-js@2 from esm.sh, postgresjs@v3.4.4 from deno.land and quickjs-emscripten@0.29.2 from npm. Note that supabase-js@2 is a floating major, resolved at deploy time — pin it if you want reproducible deploys.

Asset rights

Handwritten1 — the hand-drawn display face, shipped in ui/fonts/ and also embedded as a base64 data URI inside ui/investipet_prototype.html — carries no licence metadata in its name table. That was queried and the seller confirms it is their own font, included here deliberately and sold with the kit. The two IBM Plex faces come from Google Fonts under SIL OFL 1.1, free for commercial use and redistribution.

The dinosaur artwork, the animations and the logo are the seller's own.

---

14. Performance and scale

  • First paint is gated on a 1.9 MB download. No code splitting, no lazy

loading. On a slow phone connection this is the worst thing about the app.

  • market_features is the hot table — ~11,500 rows, each carrying up to 500

daily closes. Closes are attached on demand rather than always, specifically because sending all of them blew both the payload and the sandbox's memory. That workaround is load-bearing; do not undo it.

  • What gets slow first: tick runs every enabled algorithm for every user in

one scheduled invocation. It is linear in users × algorithms and bounded by the edge function timeout. At roughly 50–100 active algorithms this needs to become a queue.

  • The sandbox costs 3s of CPU per algorithm per run, worst case.
  • Cost at 10× users (say 90 users, 60 algorithms): still inside Supabase Pro

($25/mo) plus Railway (~$5/mo). The cost driver is not users, it is market data breadth — market-sync runs the same whether you have 9 users or 900.

  • Known scaling cliff: the singleton settings row (§13) and tick's

single-invocation design.

---

15. Code map

`` investipet/ ├── .env.example every secret, annotated ├── ui/ │ ├── investipet_prototype.html THE ENTIRE FRONT END (1.9 MB, 4 script blocks) │ │ — shell: dino, bars, curve, menu, auth, landing │ │ — OUTLIER: the algorithms screen, JSON-escaped │ │ into one ~650k-char string, rendered into a │ │ same-origin srcdoc iframe │ ├── assets/ sprites (webp), animations (mp4), icons, manifest │ └── fonts/ Handwritten1 — the seller's own display face ├── backend/ │ ├── server.js static host + CSP + security headers │ ├── package.json, railway.json, nixpacks.toml │ ├── supabase/ │ │ ├── migrations/ 47 files, 0000→0046, run in order │ │ └── functions/ 47 edge functions │ │ ├── _shared/ alpaca.ts, alpacaBroker.ts, marketdata.ts, │ │ │ publicOptions.ts, loadMarketUniverse.ts │ │ ├── ENGINE │ │ │ ├── tick daily orchestrator for the whole fund │ │ │ ├── run builtin momentum sleeve │ │ │ ├── strategy-run per-algorithm engine; sizing, stops, caps │ │ │ ├── strategy-eval the QuickJS sandbox │ │ │ ├── manage ratchet trade management │ │ │ └── risk-sweep intraday stop guardian │ │ ├── MARKET DATA │ │ │ ├── market-sync prices the whole listed US market │ │ │ ├── market the feed algorithms read │ │ │ ├── context-sync fundamentals, earnings, news, filings, macro │ │ │ ├── momentum, fundamentals │ │ ├── ACCOUNT │ │ │ ├── account, paper-fund, portfolio, blotter, mode, killswitch │ │ │ ├── bootstrap first-run setup for a new user │ │ │ └── strategies the user's algorithm set │ │ ├── LIVE (unproven — see §2) │ │ │ ├── broker every real order passes through here │ │ │ ├── execute the live gate │ │ │ └── alpaca-accounts, connections, rh-auth-, rh-refresh │ │ ├── SOCIAL / MONEY │ │ │ ├── leaderboard, friends, battle, profile, public-profile │ │ │ ├── marketplace, billing, stripe-connect, stripe-webhook │ │ │ └── academy, notes, bugs, ask, scorecard, train │ │ └── RETIRED (inert 410 stubs) │ │ └── paper, propose, rh-probe, rh-test │ └── research/ zero-dependency Node backtesting harness │ ├── src/.test.js 99 passing tests │ ├── src/walkforward.js the honest out-of-sample number │ └── run.js, run-sleeve.js, run-leverage.js ├── docs/ this file, plus the original project notes └── assets/ screenshots, logo, design tokens ``

---

16. Decisions and trade-offs

One HTML file. No build step, no bundler, no npm install, no framework, no node_modules to rot. Edit, push, live. The cost is a 1.9 MB first paint and a file that is awkward to work in. For a solo builder shipping daily, the trade was worth it; for a team it would not be.

The algorithms screen as a string. It began as a separate page and was inlined so the whole app stayed one file. srcdoc keeps it same-origin, so the shell and the iframe share localStorage and can call each other's functions. It is the single worst thing to work on in the codebase. See §17.

Service-role functions instead of RLS policies. Logic lives in TypeScript where it is readable and testable, rather than in SQL policies. The cost is real and is spelled out in §5 and §13: Postgres enforces nothing.

QuickJS over eval or a worker. The product is running strangers' code. A WASM interpreter with a hard CPU and memory budget was the only honest option.

Paper-only by construction. Alpaca's paper hosts are hardcoded, not configured. Going live is a code change somebody has to read, not a flag somebody can flip by accident. Keep this.

The dinosaur. The bars are a UI for portfolio health. A hungry dino is an underperforming portfolio. This is the actual product idea; everything else is plumbing.

What I would do differently: split the front end into real files behind a tiny build step; put tests on the edge functions before adding any more features; make settings per-user; close the audit_log loop; add Realtime.

---

17. How to make it yours

The first ten things you will want to change, and where.

  1. Point it at your Supabase project. Replace YOUR_PROJECT_REF and

REPLACE_WITH_YOUR_SUPABASE_PUBLISHABLE_KEY in ui/investipet_prototype.html and backend/server.js — and in the four migrations that schedule cron jobs. (57 and 23 occurrences respectively across ui/ and backend/.)

  1. Become the owner. Replace 00000000-0000-0000-0000-000000000000 with

your auth user id in all 18 function files and 4 migrations, and owner@example.com in 6 files (5 functions and one migration).

  1. Set your own cron key in settings.id = 1 and in every cron job.
  2. Rename the pet and the brand. Search the HTML for Investipet and for the

dino's default name. Swap ui/assets/logo.png, icon-192.png, icon-512.png, favicon.png, apple-touch-icon.png and manifest.webmanifest.

  1. Change the share domain. Search the HTML for investipet.com — it builds

public profile links.

  1. Recolour it. assets/tokens.css lists every colour token and where the

hard-coded ones live. Change them in ui/investipet_prototype.html.

  1. Replace the animal. ui/assets/sprite-.webp and dino-.mp4, mapped in

the A = {...} table near the top of the second script block.

  1. Put your contact email in the EDGAR User-Agent, in

backend/supabase/functions/context-sync/index.ts. SEC requires a real one.

  1. Change the trading rails if you disagree with them: EXPOSURE_CAP (0.34),

MIN_TRADE_DVOL ($1M), LIVE_POS_CAP (0.10) and the 2.5×ATR stop, all in backend/supabase/functions/strategy-run/index.ts.

  1. Working inside the algorithms screen. Do not hand-edit the OUTLIER

string. Extract it, edit it as HTML, re-embed it: ``js // extract const fs = require("fs"); const s = fs.readFileSync("ui/investipet_prototype.html", "utf8"); const i = s.indexOf("OUTLIER="), q = s.indexOf('"', i); let j = q + 1; while (true) { if (s[j] === "\\") { j += 2; continue; } if (s[j] === '"') break; j++; } fs.writeFileSync("algorithms.html", JSON.parse(s.slice(q, j + 1))); // ...edit algorithms.html... // re-embed const html = fs.readFileSync("algorithms.html", "utf8"); fs.writeFileSync("ui/investipet_prototype.html", s.slice(0, q) + JSON.stringify(html) + s.slice(j + 1)); ` Then syntax-check every <script>` block in both documents before you ship.

---

18. Roadmap

Planned and not built:

  • Realtime. Nothing is pushed; everything polls. Supabase Realtime

subscriptions per user channel were the plan.

  • Closing the ML loop. train and model_weights exist; training_samples

is never populated, so the contextual bandit never learns.

  • Options. equity_iv and the Public.com adapter are the first step toward

options strategies. Nothing consumes IV yet.

  • Proving the live path. It has gates, rails and a killswitch, and it has

never placed an order.

  • Refunds on the marketplace.
  • Per-user settings, so more than one person can run live.
  • Tests on the edge functions.

---

19. Changelog

Reconstructed from 233 commits between 20 June and 12 October 2026.

  • 20 June 2026 — first commit, already substantial: front end, edge

functions, paper/live with a killswitch, the backtest harness and the Academy. 63 commits that month.

  • July 2026 (10 commits) — quiet; consolidation.
  • August 2026 (135 commits — the big month) — multi-tenancy (migration 0012

replaced the singleton paper account with per-user rows), the marketplace and Stripe wiring, dino usernames and public profiles, institutional reporting, the full-market data layer, friends and battles.

  • September 2026 (20 commits) — the broker boundary, risk sweeps, an

end-to-end security audit, and the guest experience: paper-only guests, the guest banner, the landing page, the AI prompt, coach-marks.

  • October 2026 — the sell fix, in-app bug reporting, two cross-user data

holes closed, and the home readout renamed from "health" to "algorithms". On 12–13 October, while this package was being prepared: rh-test and rh-refresh closed off, fundamentals and earnings given the cron jobs they had never had, and two dead branches the type-checker had been flagging fixed.

---

20. Assistant's notes

Things a careful buyer should know, from the assistant that packaged this.

What I actually did. I read every file I included. I ran the 99 tests (they pass) and the walk-forward backtest (it runs). I type-checked all 47 edge functions with Deno. I drove the live app in a headless browser as a guest and took the screenshots in assets/ from the running production app. I queried the production database for every usage number in this document. I did not stand up a second Supabase project and deploy this package from scratch — so step 9 of §9 is written from the code, not from a clean-room run.

On the maturity score. I scored this a 6, not a 7. It clears most of 7 — 233 commits, sustained work, real users, tests that pass. What it does not clear is "auth, data and payments handled carefully": seven separate authorization holes were found and closed in the five weeks before packaging, two of them during this packaging run, and the production trading path has no automated tests at all. The app is genuinely good and genuinely used. The security track record is the honest reason it is not a 7.

On things I kept that look alarming. Three items would fail a naive malware scan and are all legitimate: the QuickJS sandbox executes code supplied by users (that is the product); CODE_ENC_KEY decrypts stored payloads and runs them (that is the rented-algorithm feature); and the CSP references a Cloudflare analytics host (an edge-injected beacon on the original deployment — no tracking code is in this package). §13 covers all three.

The single biggest thing to fix first. Not a bug — the absence of tests on the edge functions. Every authorization hole in the list in §13 would have been caught by a dozen tests asserting "a signed-in stranger gets 403 here". If you buy this and change anything in backend/supabase/functions/, write those tests before you write the feature.

What you are really buying. Not a turnkey business — the live path has never traded and the marketplace has never sold anything. What you are buying is a working, in-production, multi-tenant paper-trading engine with a genuinely original interface, four months of real iteration behind it, and no dependencies to rot.

Package contents

The screens
ui/ — The entire front end as one self-contained 1.9 MB HTML file — the pet, the stat bars, the performance curve, the algorithms screen, auth, guest mode and the landing page. No build step, no framework, no dependencies. Includes sprite sheets, animations, icons and the PWA manifest.
Instructions
docs/ — A 20-section handoff covering architecture, the data model, auth, setup from zero, deployment, a full security review with what is still open, performance limits, a code map, and the first ten things a buyer will want to change. Plus the original project notes.
Pictures & logo
assets/ — Six 1600x3464 screenshots taken from the running production app, the logo and app icon, and a design-tokens file listing every colour and the typography — including which font has an unresolved licence.
The engine
backend/ — 47 Supabase Edge Functions (the trading engine, the QuickJS sandbox for user algorithms, market-data sync, risk sweeps, social, marketplace and Stripe wiring), 47 SQL migrations across 53 tables, the Railway static host with its CSP and security headers, and a zero-dependency Node backtesting harness with 99 passing tests.

All tags

This page as Markdown · for assistants and agents, start at /llms.txt

Comments

Sign in to comment

No comments yet.

List your app