Skip to content

Infrastructure

Two pieces of hosted infrastructure support this project, both on a single free Cloudflare account:

NeedServiceFree tierCost at our scale
Host this private research siteCloudflare Pages + Cloudflare AccessUnlimited bandwidth/sites; Access gives 50 seats$0
Cache fetched market dataCloudflare R210 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):

  1. 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).
  2. Add the repo secrets (Settings → Secrets and variables → Actions):
    • CLOUDFLARE_API_TOKEN
    • CLOUDFLARE_ACCOUNT_ID
  3. Deploy once. Merge to main (any change under docs/ triggers the workflow) or run it manually from Actions → Deploy docsRun workflow. The first run creates the project and publishes to vibe-trader-two.pages.dev.
  4. 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.dev and 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:

  1. Create the bucket. Cloudflare dashboard → R2 → Create bucket, e.g. vibe-data.
  2. 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).
  3. Configure locally (in strategies/.env, gitignored):
    sh
    VIBE_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-...
    Then uv sync --extra r2 (installs s3fs, only needed for R2).
  4. Configure CI for the Refresh market data workflow. The workflow maps these GitHub secret names to the env vars the code reads (see its env: block):
    • Secrets: DATABENTO_API_KEY, CLOUDFLARE_R2_ENDPOINT, CLOUDFLARE_R2_ACCESS_ID, CLOUDFLARE_R2_ACCESS_KEY
    • Variable: VIBE_DATA_BUCKET

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 R2

For 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.

Last updated: