





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
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.
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
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.
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
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.
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.
- 233 commits between 2026-06-20 and 2026-10-12; 63 in June, 135 in August, 20 in September
- 9 registered accounts, 7 confirmed, 3 active in the last 30 days
- 295 paper trades (43 open, 211 closed), daily from 2026-06-23 to 2026-10-12
- 9,841 portfolio snapshots and 11,522 symbols cached with up to 500 daily closes each
- 99 unit tests pass in ~130ms (backend/research); zero tests on the 47 edge functions
- 47 SQL migrations, numbered and sequential
- deno check reported 26 type errors across the functions; the two that were real bugs were fixed during packaging, leaving 21 implicit-any and loose-typing warnings
- Seven authorization holes found and closed between 2026-09-05 and 2026-10-13, two during this packaging run
- live_orders and broker_connections are both empty: the live path has never run
- marketplace_sales and algo_rentals are both empty: no sale has ever completed
Setup guide
- Create a Supabase project. Note the project ref and the publishable key.
- Run the migrations.
cd backend && supabase link --project-ref <ref> && supabase db push - Become the owner. Replace
00000000-0000-0000-0000-000000000000with your own auth user id in the 18 function files and 4 migrations that carry it, andowner@example.comin 6 files. - Set a cron key.
openssl rand -hex 32, thenupdate settings set cron_key = '<value>' where id = 1; - Set your secrets. See
.env.example. The minimum useful set isALPACA_KEY_ID,ALPACA_SECRET_KEYandFRED_API_KEY— all free. - Deploy the functions. Loop
supabase functions deploy <name> --project-ref <ref> --no-verify-jwtoverbackend/supabase/functions/*/. The--no-verify-jwtflag is required: the functions do their own auth and guest mode has no JWT. - Point the front end at your project. Replace
YOUR_PROJECT_REFandREPLACE_WITH_YOUR_SUPABASE_PUBLISHABLE_KEYinui/investipet_prototype.htmlandbackend/server.js. - Run it.
cd backend && node server.js, then open http://localhost:3000. "Look around first" enters guest mode with no account. - Schedule the crons.
backend/supabase/migrations/0010_tick_cron.sqlshows the shape. The ones that matter aremarket-sync,tickandrisk-sweep. - Prime the market data. Call
market-sync?discover=1once 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. Carriesstrategy_id,user_id, and anmgmtJSON 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:
- Guest — no JWT. Functions detect this and strip live fields, dollar
amounts and anything owner-only.
- Signed-in user — a normal JWT. Sees and acts on their own rows only.
- 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-webhookis 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_feesrecords the platform's cut. - Failures:
stripe_eventsgives 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:
- None of these come from a
.envfile. They are Supabase *function
secrets*: supabase secrets set NAME=value --project-ref <ref>.
cron_keyis 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.
- Create a Supabase project. Note the project ref (the subdomain, e.g.
abcdefghijklm) and the publishable key (Settings → API).
- 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.
- 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`.
- Set your cron key.
`` openssl rand -hex 32 # keep this ` `sql update settings set cron_key = '<that value>' where id = 1; ``
- Set your secrets (see
.env.example). The minimum useful set is
ALPACA_KEY_ID, ALPACA_SECRET_KEY and FRED_API_KEY — all free.
- 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.
- Point the front end at your project. In
ui/investipet_prototype.html, replace every occurrence of:
YOUR_PROJECT_REF→ your project refREPLACE_WITH_YOUR_SUPABASE_PUBLISHABLE_KEY→ your publishable key
Do the same in backend/server.js (the CSP names the Supabase host).
- 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.
- 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).
- Prime the market data. Call
market-sync?discover=1once 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_URLfor redirect URLs.- The share-link domain — search the HTML for
investipet.com. - The CSP in
server.jsnamesstatic.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 checkon 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
- 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.
- Per-user Alpaca brokerage accounts do not work — Broker API 401.
- The ML pipeline is not closed.
trainexists;training_samplesis empty. - The live path has never executed an order. Treat it as unproven code.
- 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.
- 21
deno checkerrors, all implicitanyor 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.
- 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.
- 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-testwas 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-refreshwas unauthenticated too. SameDeno.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)
executeran its live-trading gates before checking who was calling.proposeinserted intotradeswith no auth check at all.paperlet any signed-in user runtrades.delete().eq("is_practice", true)
with no user filter — a global wipe of every user's positions.
academykeyed quiz progress by device id with no auth.portfolio?mode=liveserved guests the owner's live equity curve.
Still open — a buyer should fix these
- The
cron_keyis 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.
settingsis a singleton.execution_modeandhaltedare
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_loghas 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.combecause 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.tssends 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_featuresis 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:
tickruns 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
settingsrow (§13) andtick'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.
- Point it at your Supabase project. Replace
YOUR_PROJECT_REFand
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/.)
- Become the owner. Replace
00000000-0000-0000-0000-000000000000with
your auth user id in all 18 function files and 4 migrations, and owner@example.com in 6 files (5 functions and one migration).
- Set your own cron key in
settings.id = 1and in every cron job. - Rename the pet and the brand. Search the HTML for
Investipetand 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.
- Change the share domain. Search the HTML for
investipet.com— it builds
public profile links.
- Recolour it.
assets/tokens.csslists every colour token and where the
hard-coded ones live. Change them in ui/investipet_prototype.html.
- Replace the animal.
ui/assets/sprite-.webpanddino-.mp4, mapped in
the A = {...} table near the top of the second script block.
- Put your contact email in the EDGAR User-Agent, in
backend/supabase/functions/context-sync/index.ts. SEC requires a real one.
- 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.
- 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.
trainandmodel_weightsexist;training_samples
is never populated, so the contextual bandit never learns.
- Options.
equity_ivand 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
No comments yet.