My Financial Agent Uses Eight Cloudflare Products. It Still Couldn't Ship Without One Old-Fashioned VM
Building FinZeit - an AI financial advisor with exactly one user: me.
I got caught with the agentic AI wave and couldn't resist myself into building one. I just kept one constraint; I will try to keep running costs to minimal - and lo and behold FinZeit is an Agentic financial advisor I built for exactly one user: myself and it runs for free (thanks to generous free tier by Cloudflare).
It connects to my ICICI Direct brokerage account, computes my Indian capital-gains tax exposure in real time, and lets me ask things like "Should I rebalance my existing holdings?" — answered by an LLM (Gemma-4) calling live tools against my actual portfolio, not dispensing generic advice steered by my investment strategy document.
With AI coding tools at your disposal, you don't have to worry a lot about syntax, boilerplate code, or memorizing obscure API functions these days.
Instead of getting bogged down in the minutiae of how to write the code line-by-line, you can shift your focus to the bigger picture:
- System Architecture: Designing how different parts of your application interact and scale.
- Problem Solving: Defining the logic, user experience, and unique value of your project.
- Prompt Engineering: Learning how to clearly articulate what you want the AI to build.
A quick reality check: While you don't have to sweat the small stuff as much, you still need to worry about debugging and code verification. AI is brilliant at generating code, but it can still confidently hallucinate bugs or security flaws, meaning your role as the "chief code reviewer" is more important than ever.
The Problem: Dashboards Show Holdings, Not Answers
Broker dashboards are built to show you what you own, not to answer complex, multi-year questions. Indian LTCG/STCG rules require strict FIFO lot matching across years of trades. To figure out your true tax exposure, you have to parse which lots have crossed the 12-month threshold and calculate exactly how much of your annual ₹1.25L exemption remains. Like most people trying to keep track of this, I was trapped doing all of it manually in a sprawling spreadsheet.
FinZeit Features:
- AI chat - Agentic chat on Gemma 4 (26B) via Cloudflare Workers AI, with a hand-rolled tool-calling loop (up to 4 rounds)
- 9 callable tools: holdings, trades, tax estimate, stock price, stock news, fundamentals (P/E, market cap, 52-week range), deep financial data (analyst targets, company profile, earnings calendar, sentiment, ownership), price history, current date
- Streams tool-call progress live (a "Thinking…" panel) as each tool resolves, before the final answer
- Optional per-message attachments: portfolio snapshot, ad-hoc web search, market news
- Full conversation history, persisted and resumable
- Portfolio - Live holdings pulled from ICICI Direct via the Breeze API (NSE + BSE), with quantity, average cost, current price, and P&L per stock
- Tax - LTCG/STCG estimate computed via FIFO lot matching across 3 years of trade history
- Daily digest - Cron-generated once a day (10:00 AM IST), Per-holding take + a concrete "Suggested Maneuvers" line (references actual quantity/avg price)
- Strategy - Free-text investment plan you write yourself, referenced by both chat and the digest to ground suggestions instead of generic advice
- Platform - Installable PWA, Responsive layout (desktop sidebar / mobile bottom nav)






How It Works?
The AI agent uses Workers AI running Gemma 4 26B to power a hand-rolled tool-calling loop, bypassing constraints in Cloudflare’s standard helpers. To ensure reliable data fetching, the agent forces tool execution in the first round and opens up autonomous decision-making for subsequent turns.
But it hit a hard wall when it met the real world:
- Google and Yahoo aggressively block serverless shared egress IPs.
- My broker mandates a strictly whitelisted static IP for API execution.
The solution? A single, permanent Oracle Cloud VM acting as a secure proxy gate to handle the cryptographic signatures, session caching, and data fetching.
When a user asks financial questions, the agent dynamically triggers custom tools to query your secure Oracle VM proxy.
This proxy aggregates real-time portfolio data, Google News RSS, and Yahoo Finance metrics, injecting optimized text snapshots straight back into Gemma's context window for accurate, live financial reasoning.
The Stack: Serverless Edge + A Fixed Perimeter
The architecture is heavily optimized for a fast, low-latency edge deployment, utilizing eight distinct Cloudflare components wrapped around a classic cloud instance:
- Frontend: Native browser modules hosted as static files on Cloudflare Pages.
- API & Automation: A lightweight Cloudflare Worker handling API routing and daily cron job.
- Persistence: Cloudflare D1 for relational chat history (
conversationsandmessagestables withON DELETE CASCADE) and portfolio snapshots, paired with Cloudflare KV for fast session lookups and password hashes. - AI Layer: Workers AI (Gemma 4 26B) acting as the core agent, routed through Cloudflare AI Gateway for telemetry and observability.
- Security & Networking: Cloudflare Turnstile on the login gate, with a secure Cloudflare Tunnel bridging the edge to the backend.
[ Browser / PWA Frontend ]
│
▼ (HTTPS)
[ Cloudflare Pages ]
│
▼ (XHR / SSE)
[ API Worker + Cron ] ──(waitUntil)──► [ D1 + KV (History/Sessions) ]
│
├───────────────────────────► [ Workers AI (Gemma 4) via AI Gateway ]
│
▼ (Secure Cloudflare Tunnel)
[ Proxy (Oracle Cloud VM) ]
│
▼ (Static IP Whitelist)
[ External APIs: Breeze / Yahoo Finance / Google News ]
That Oracle Cloud VM is my favorite constraint of the whole project. Google News RSS and Yahoo Finance both block Cloudflare Workers' shared egress IPs outright, and my broker requires API calls to originate from a single, registered static IP. Every external intelligence fetch route through a ~200-line Node.js/PM2 proxy that handles caching, request masking, and Yahoo's cookie-and-crumb auth dance—automatically refreshing tokens the moment Yahoo rejects them.

Four Decisions I had to make along the way:
1. I hand-rolled the agentic loop
Cloudflare's runWithTools helper silently drops tool_choice, meaning the model can never be strictly forced to call a tool when context is required. To fix this, my hand-rolled loop forces a tool execution in Round 1, permits the model to decide freely in Rounds 2–4, and streams a custom progress event to the UI the moment each tool resolves. In the PWA UI, you watch the agent live-fetch holdings, query news sentiment, and compute tax data before it streams the final narrative response.
2. The tax engine is pure functions (and the math wasn't the hard part)
The math behind FIFO matching is straightforward; coding around a broken broker API is where the real engineering happened. The broker's API entirely ignores its own exchange filters, double counting every holding until you manually dedupe the payload. It returns total trade value in a field deceptively named like a unit price. Worse, it rejects timestamps missing a trailing Z—its .NET backend parses untagged strings as IST and fails its own internal time-window safety checks.
3. No build step anywhere
The Worker consists of raw ES modules bundled natively by Wrangler during deployment; the frontend relies entirely on native browser modules served as static files. There is no build pipeline to break, no node_modules bloating the repository, and zero configuration drift over time.
4. The "attached context" pattern kept paying for itself
When I built the chat's attach chips (allowing me to pin a portfolio snapshot, a web search, or a news query above the composer), I didn't set out to build a reusable design primitive. But recently, I wired a "Discuss in chat" button onto the Daily Digest tab through that exact same pipeline. Clicking it turns the digest’s AI-written summary into attached context for a fresh conversation. Because the model reasons against text it already has in its prompt window, there is no new external fetch and no extra network round-trip. Reusing an interface you didn’t know was general is a quiet win, but it’s the kind that proves the architectural shape was right.
Honest Trade-offs
- Daily manual alignment: I manually paste a fresh broker session token every single morning because the brokerage sessions expire nightly. However, a cached D1 snapshot ensures the 10:00 AM automated digest cron functions perfectly even if I haven't logged in yet.
- The black box of LLM latency: Decision-round latency can swing unpredictably from 5 seconds to 60 seconds for identical prompts. I added Cloudflare AI Gateway purely to make this variance observable, as nothing on my application layer explains the variance.
Designing for Minimal Exposure
Every screenshot of the live application has a dedicated Privacy Mode toggled on. This was born out of necessity: I wanted a single switch that would immediately mask every rupee amount, blur out sensitive AI responses, and clear the screen so I could safely use the app or share my screen in public spaces without exposing my total net worth.
Building for a single user has been incredibly liberating. Every table in D1 still carries a standard customer_id column so that multi-tenancy remains structurally possible in the future, but I never had to compromise the architecture or add overhead to pretend I was building a venture-backed SaaS. I built an asset that serves me perfectly every day.
What's the most flagrant "the docs say X, the API does Y" behavior you've ever had to build a proxy pattern around? Let's swap war stories in the comments below.