Skip to content

Deploy

EstimateIQ deploys as a single Cloudflare Worker to esb.guru on the derekethandavis Cloudflare account (d810128e…).

What a deploy ships

wrangler deploy (run from cloudflare/) publishes, in one artifact:

  • the Hono API, the Astro SSR UI (built into the Worker), and the static Assets bundle;
  • the Durable Objects (EstimateIQ, SlaTimerDO), created by the v1 migration on first deploy;
  • the RevisionWorkflow and the MAIL_INGEST queue consumer;
  • the hourly SLA cron (0 * * * *) and the esb.guru custom domain.
Terminal window
cd cloudflare
bunx wrangler deploy

Build

The root bun run build runs the full pipeline that CI mirrors: seed → snapshot → build:web (Astro) → build:docs. The docs site (this site) builds separately into docs/dist and is copied to web/dist/docs, so it ships alongside the app at https://esb.guru/docs.

GitHub Actions CI/CD

Two workflows run in the asdtransport/revsrc repo:

  • ci (.github/workflows/ci.yml) — on every push / PR: install, typecheck, bun run build, and a wrangler deploy --dry-run bundle check to confirm the Worker compiles for the Workers runtime.
  • deploy (.github/workflows/deploy.yml) — on push to main (or manual dispatch): build, apply D1 migrations (wrangler d1 migrations apply estimateiq --remote), then wrangler deploy.

Cloudflare credentials are repo-level: CLOUDFLARE_API_TOKEN (secret) and CLOUDFLARE_ACCOUNT_ID (variable). Worker secrets (ANTHROPIC_API_KEY, CF_AIG_TOKEN, REVSRC_SECRET_KEY, …) persist on the Worker across deploys — set once with wrangler secret put, never re-pushed by CI.

Access & environment

The ENVIRONMENT var gates Cloudflare Access: preview leaves the Access gate off so esb.guru is reachable immediately; flip to production (with the Access app + ACCESS_AUD wired) to require a valid Access JWT on every non-public path. /healthz is always public.

First-time deploy — full runbook

A fresh deployment comes up with an empty Durable Object and an empty search index — nothing is auto-seeded. These post-deploy steps are run by hand (or scripted); the app is up after step 3, but not populated until step 5, and search returns nothing until you reindex (step 6).

  1. Provision the data plane (once per account/env) — Terraform stands up D1, KV, R2, Queues, Vectorize. Paste the output ids into cloudflare/wrangler.jsonc (or import the existing ones).

    Terminal window
    cd infra/terraform && export TF_VAR_cloudflare_api_token=cfat_… && terraform apply
    terraform output bindings # ids → wrangler.jsonc

    See Infrastructure as code.

  2. Set the Worker secrets (once; they persist across deploys — CI never re-pushes them):

    Terminal window
    cd cloudflare
    printf '%s' "$ANTHROPIC_API_KEY" | wrangler secret put ANTHROPIC_API_KEY
    printf '%s' "$CF_AIG_TOKEN" | wrangler secret put CF_AIG_TOKEN
    openssl rand -base64 32 | wrangler secret put REVSRC_SECRET_KEY
    printf '%s' "$WEBHOOK_TOKEN" | wrangler secret put WEBHOOK_TOKEN
  3. Build & deploy the Worker (ships API + Astro SSR + Assets + DOs + Workflow + queue consumer + cron

    • the esb.guru custom domain):
    Terminal window
    bun run build
    cd cloudflare && wrangler deploy
  4. Apply D1 migrations (the event/audit mirror):

    Terminal window
    wrangler d1 migrations apply estimateiq --remote
  5. Load the data through the API — the DO is empty until you do this:

    Terminal window
    export REVSRC_ADMIN_TOKEN=<REVSRC_SECRET_KEY>
    bun run load # ansible → POST /api/admin/clear + /api/admin/load (infra/data/dataset.json)
  6. Build the search indexrequired, or semantic search returns 0 results. This calls Workers AI to embed every estimate into Vectorize:

    Terminal window
    curl -X POST https://esb.guru/api/admin/reindex -H "authorization: Bearer $REVSRC_ADMIN_TOKEN"
    # → {"ok":true,"indexed":N}. Vectorize is eventually consistent — search is live within ~30s.
  7. Verify in Cloudflare — open the Worker in the dashboard and confirm the bindings (DO, D1, R2, KV, Vectorize, AI, Queue, Workflow, cron) are attached, then tail the live edge logs and check the trail:

    Terminal window
    wrangler tail estimateiq # live requests hitting the Worker + DO (outcome: ok)
    curl -s https://esb.guru/healthz # { ok: true }
    curl -s https://esb.guru/api/metrics

    The Audit log at esb.guru/audit should show the admin.load + admin.clear entries you just created.