Oracle3
Open-source trading engine and MCP server for prediction markets. Fee-aware no-arbitrage trading across related contracts, live on Kalshi, Polymarket and Solana or on paper.
Say what you spent. Check it. Done.
Translation pending
FinanceTypeScript · JavaScriptGitHub @PranavChhajedOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Voice-first expense capture for India. Say what you spent; a journal keeps the record honest and correctable.
Taken from the repository README
Loading the full README.
Read the full README ↗Say what you spent. Check it. Done.
Voice-first expense capture. You speak an expense the way you'd text it to yourself — "250 lunch" — and it becomes a structured transaction, with every field showing whether it was heard, worked out, filled from a default, or left blank because it's genuinely unknown. A confident capture saves itself, with an Undo that's there for exactly as long as it takes you to notice something's wrong; anything less certain stops on a review card first.
Start with CONTEXT.md — the whole product, architecture and
current state in one file. Product thinking lives in
PRD-VaaniTrack.md, evidence in
RESEARCH.md, what the project runs on in
TechStack.md, and the build log in
EXECUTION.md.
npm install
Copy .env.local.example to .env.local and add a Groq
API key (free at console.groq.com):
GROQ_API_KEY=gsk_...
One key covers both stages — whisper-large-v3 for transcription, and
openai/gpt-oss-120b for turning the transcript into a transaction. That is
the only variable capture needs.
The rest of .env.local.example configures the observability layer and the
diagnostic portal. Leave them unset and the app behaves identically — capture
just runs unobserved, and /portal denies everyone. To turn them on you need a
Supabase project with the SQL in supabase/ applied in order
(schema.sql, then 002, 003, 004) and a captures storage bucket:
DATABASE_URL= # Supavisor TRANSACTION pooler, not the direct host
NEXT_PUBLIC_SUPABASE_URL=
SUPABASE_SECRET_KEY= # sb_secret_... — server only, bypasses RLS
PORTAL_PASSWORD= # unset means /portal denies everyone
SAVE_CAPTURES=1 # local .captures/ files; off unless set, and
# structurally off in production regardless
npm run dev
Open http://localhost:3000. The microphone works here because browsers
treat localhost as a secure origin.
npm run dev:tunnel
It prints a QR code — point your phone camera at it — plus the URL, and
writes the same code to phone-link.png in case your terminal mangles the
block characters.
────────────────────────────────────────────────────
Point your phone camera at this:
[ QR code ]
...or open: https://something-random.trycloudflare.com
...or open phone-link.png in this folder and scan it.
────────────────────────────────────────────────────
On the phone: tap Say what you spent, allow the microphone when asked, speak, tap again to finish. A line under the button reports what was captured and heard, so a failure says where it broke.
The phone does not need to be on the same Wi-Fi. Mobile data is fine.
The URL is public while the tunnel runs. It's random and unlisted, and it dies when you stop the process — but anyone who has it can reach your app and spend your Groq quota. Don't post it anywhere.
The microphone is the whole product, and browsers only expose getUserMedia in
a secure context — HTTPS with a certificate the browser trusts, or
localhost.
Serving HTTPS from your laptop with a self-signed certificate is not enough. Chrome does not treat an origin with a failed certificate as secure even after you tap through the warning. You get the page; you never get the microphone. That is the wall you hit if the page loads on your phone but the mic refuses to open.
The tunnel avoids it entirely: your phone talks to Cloudflare over a certificate it already trusts, and Cloudflare forwards to your machine. No certificate to install, no interstitial, no firewall rules.
npm run dev:phone
Issues a certificate for your current LAN IP and prints https://192.168.1.12:3000.
Keeps everything on your own network — but you'll hit the Chrome problem above
unless you install the mkcert root CA on the phone
(C:\Users\<you>\AppData\Local\mkcert\rootCA.pem). Use the tunnel unless you
specifically need to stay off the internet.
Two things that bite on this path:
npm run dev | http on localhost. Fine on this machine; mic won't work from a phone. |
npm run dev:tunnel | Public URL with a trusted certificate. Use this for phone testing. |
npm run dev:phone | https with a certificate for your LAN IP. Stays off the internet, but see the Chrome caveat above. |
npm run dev:https | https via Next's own cert (localhost only). Fallback. |
npm run build | Production build |
node --experimental-strip-types scripts/journal.test.ts | The data-layer test suite. No framework, no build step. |
node --env-file=.env.local --experimental-strip-types scripts/observe.smoke.ts | Writes one full pipeline's worth of rows to Supabase, reads them back, deletes them. |
start-vaanitrack.bat | Windows one-click: clears stale processes, checks deps and the key, builds, tunnels. |
speak
│
▼
level meter is there actually sound in this? (dBFS)
│ a take under −55 dBFS is refused on the device
▼
whisper-large-v3 audio → text
│ stock hallucinations rejected by denylist
▼
gpt-oss-120b text → transaction + provenance
│ (never invents; leaves blanks blank)
▼
confidence gate four conditions; all must hold to save silently
│ ┌───────────────────┴───────────────────┐
│ ▼ ▼
│ all four hold any condition fails
│ auto-saved, with Undo review card — you check it,
│ then Save
▼ │
journal ◄──────────────────────────────────────────────────┘
localStorage, mirrored to Postgres if configured
Every stage above writes a row describing what it did, so a wrong expense can
be traced to the stage that produced it rather than guessed at. /portal is
where that is read back.
Why the review step exists: currency amounts are the single worst thing
speech recognition does — 36.3% accuracy at baseline, the lowest of any numeric
category measured. The amount is also the one field that must never be wrong.
Showing every transaction before it commits is what makes that survivable.
See RESEARCH.md §1.1.
Provenance. Every field records where its value came from — said, inferred, default, or unknown. Values the system worked out rather than heard are marked with a dotted underline on the review card. A missing category stays blank rather than being guessed.
| Route | What it is |
|---|---|
/ | The public landing page. No capture, no overlays. |
/app | Capture. Today's transactions only. The PWA's install target. |
/journal | The record — all populated days, month subtotals, edit and delete. |
/signin, /signup | Stubbed auth screens. The account is a UI concept only — nothing syncs yet, and these never touch the anonymous device id capture relies on. |
/portal | Diagnostic portal. Every capture, its pipeline stages, and a heuristic naming the likely point of failure. Password-gated; denies everyone when PORTAL_PASSWORD is unset. |
/diag | General diagnostics. |
/diag/mic | Microphone signal test. Runs the same utterance through three getUserMedia configurations and prints the dBFS that actually arrived. Built because Android routes capture through the platform voice path on some handsets, and whether that path works is a property of the handset. |
This is a two-day demo, deliberately scoped.
/signin and /signup are real
screens, but the session is a UI concept only. localStorage on the device
that recorded it is still the working copy. When Supabase is configured,
transactions also mirror to Postgres keyed by an anonymous per-browser
device id — that is durability, not sync: a second device is a different id
and sees nothing. Clearing site data still resets the id and orphans the
mirrored rows./diag/mic.