Appearance
Infrastructure
Two pieces of hosted infrastructure support this project, both on a single free Cloudflare account:
| Need | Service | Free tier | Cost at our scale |
|---|---|---|---|
| Host this private research site | Cloudflare Pages + Cloudflare Access | Unlimited bandwidth/sites; Access gives 50 seats | $0 |
| Cache fetched market data | Cloudflare R2 | 10 GB storage, $0 egress — always | $0 |
Why Cloudflare
- GitHub Pages doesn't fit — this repo is private, and private Pages needs a paid GitHub plan with only coarse access control.
- Netlify / Vercel host private-repo static sites free, but put password protection behind a paid plan. Cloudflare Access is free for 50 seats.
- R2's zero egress fee (on free and paid tiers) is the deciding factor for data: CI and agents re-read the same bars constantly, and S3/B2 charge for every read. R2 never does. It's also S3-compatible, so it drops into the existing pandas/parquet code with no new concepts.
Sources: R2 pricing · Pages limits · Cloudflare Access.
Hosting the site (Cloudflare Pages)
The Deploy docs workflow builds docs/ and publishes docs/.vitepress/dist to a Pages project on every push to main that touches the site. The build still fails on dead internal links, so a broken link blocks the deploy.
One-time setup (no manual project creation — the workflow creates the vibe-trader-two Pages project on its first run):
- Create an API token. My Profile → API Tokens → Create Token → template "Edit Cloudflare Workers" (or a custom token with Account → Cloudflare Pages → Edit; the Edit permission is what lets the workflow create the project). Copy the token and your Account ID (right sidebar of any dashboard page).
- Add the repo secrets (Settings → Secrets and variables → Actions):
CLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_ID
- Deploy once. Merge to
main(any change underdocs/triggers the workflow) or run it manually from Actions → Deploy docs → Run workflow. The first run creates the project and publishes tovibe-trader-two.pages.dev. - Lock it down with Access. After that first deploy, Zero Trust → Access → Applications → Add a self-hosted application for the Pages domain (
vibe-trader-two.pages.devand any custom domain). Add an Allow policy scoped to your email(s) or an email domain. Viewers then authenticate with a one-time PIN (or SSO) before the site loads.
The site deploys at the Pages project root, so leave base unset in docs/.vitepress/config.ts — the /vibe-trader-two/ base mentioned there is only for a GitHub Pages deploy, which we are not using.
Caching market data (Cloudflare R2)
vibe_trader.data.storage resolves each parquet key to a local path or an s3:// URI on R2, decided purely by environment. Nothing is hard-coded to the cloud: with VIBE_DATA_BUCKET unset, ingest and backtests read/write strategies/data/raw/ exactly as before, so a fresh checkout and the offline smoke test need no account.
One-time setup:
- Create the bucket. Cloudflare dashboard → R2 → Create bucket, e.g.
vibe-data. - Create an R2 API token. R2 → Manage R2 API Tokens → Create → Object Read & Write. Note the Access Key ID, Secret Access Key, and the S3 endpoint (
https://<account id>.r2.cloudflarestorage.com— the account id is your Cloudflare account id, the same one used for Pages). - Configure locally (in
strategies/.env, gitignored):shThenVIBE_DATA_BUCKET=vibe-data R2_ACCESS_KEY_ID=<access key id> R2_SECRET_ACCESS_KEY=<secret access key> R2_ENDPOINT=https://<account id>.r2.cloudflarestorage.com # (or set R2_ACCOUNT_ID=<account id> instead of R2_ENDPOINT) DATABENTO_API_KEY=db-...uv sync --extra r2(installss3fs, only needed for R2). - Configure CI for the
Refresh market dataworkflow. The workflow maps these GitHub secret names to the env vars the code reads (see itsenv:block):- Secrets:
DATABENTO_API_KEY,CLOUDFLARE_R2_ENDPOINT,CLOUDFLARE_R2_ACCESS_ID,CLOUDFLARE_R2_ACCESS_KEY - Variable:
VIBE_DATA_BUCKET
- Secrets:
The refresh workflow runs weekly (Wednesday 00:30 UTC, after Tuesday's CME settlement) and on demand via Run workflow. It overwrites each <symbol>_1d.parquet in place, so backtests always read the latest bars.
Reading the cache from an agent session
Any script with the four R2 env vars set reads the shared cache — no re-fetch:
python
from vibe_trader.data.databento_ingest import load_bars_df
df = load_bars_df("MES") # pulls s3://vibe-data/mes_1d.parquet off R2For ad-hoc analysis, DuckDB queries the parquet directly over R2's S3 API:
python
import duckdb
con = duckdb.connect()
con.execute("""
CREATE SECRET r2 (
TYPE r2,
KEY_ID '…', SECRET '…',
ACCOUNT_ID '…'
);
""")
con.sql("SELECT * FROM read_parquet('r2://vibe-data/mes_1d.parquet') LIMIT 5").show()Cost summary
Everything here sits inside free tiers at this project's scale (a few small parquet files, one low-traffic private site). R2 storage beyond 10 GB is $0.015/GB/month with no egress charge; the first paid dollar is a long way off.