Skip to content

vela-synthetic

A 15-minute Cloudflare Cron Worker that exercises the v1.5 billing funnel end-to-end on devnet staging and pages on failure. The first line of defense for catching protocol-or-app regressions in production-shaped infrastructure before a real merchant or subscriber does.

Purpose and Role

  • Run a full subscribe → pull → cancel sweep every 15 minutes against deployed devnet staging.
  • Validate not just the protocol path but the supporting data plane — D1 ingest, webhook delivery, dashboard read paths.
  • Page (alert) immediately on failure with enough context to triage: which step failed, with what error, against which mandate.
  • Reuse vela-dashboard's D1 binding so synthetic events land in the same database as real events — easier to spot anomalies side-by-side.

Tech Stack

TechnologyVersionPurpose
TypeScript^5.8.xLanguage
Bun1.3.xPackage manager + dev
@vela/sdklocal linkFull client — instruction builders, PDAFactory, account deserializers
uuidv7^1.2.xTime-ordered IDs for synthetic event correlation
Cloudflare Cron Workers*/15 * * * * trigger
Cloudflare D1Reuses vela-dashboard-dev (DB binding) — same database as the dashboard
wrangler^4.78.xDeploy + dev

Worker Surface

The Worker exposes no HTTP surface — it runs purely on the cron trigger.

[triggers]
crons = ["*/15 * * * *"]    # Every 15 minutes (STAGING-10 contract)

Each cron firing runs the full funnel:

  1. Resolve a synthetic merchant + subscriber pair (deterministic devnet keypairs).
  2. Create or refresh a synthetic plan.
  3. Subscribe the synthetic subscriber.
  4. Wait for the next pull window.
  5. Trigger execute_pull.
  6. Verify the resulting webhook event reaches D1 in time.
  7. Cancel the mandate.
  8. Confirm cancellation event lands.
  9. On any failure, emit a structured alert with mandate, step, and typed error.

Directory Structure

vela-synthetic/
├── src/
│   └── index.ts              # Cron handler — runs the funnel
├── tests/                    # bun:test unit tests for funnel helpers
├── wrangler.toml             # Cron trigger + D1 binding (reuses dashboard DB)
├── package.json
└── tsconfig.json

Deployment Target

  • Cloudflare Cron Worker — deployed via wrangler deploy.
  • Database — uses the dashboard's D1 binding (vela-dashboard-dev) to surface synthetic activity in the same dataset as real events. Operator overrides the database ID for prod / staging environments.
  • Migrations — points at ../vela-dashboard/worker/drizzle/migrations so it sees the same schema the dashboard sees.

Consumers / Dependencies

Consumer / SourceHow
Protocol teamReceives pages on cron failure
Devnet staging stackTarget of every synthetic call
vela-dashboard D1Reused as the destination for synthetic events
vela-sdkDrives every protocol interaction

Notable Design Decisions

Cadence Is the SLO

A 15-minute cadence is a quiet "we expect billing to work end-to-end at least four times an hour." Faster cadence increases noise and devnet RPC load; slower cadence widens the blind window. 15 minutes is the contracted floor (STAGING-10).

Reuse Dashboard D1

A separate database for synthetic data would dodge cross-contamination but lose the "is the dashboard ingest path actually working?" signal. Synthetic events land in the dashboard's D1 so the worker is a real consumer of the same plumbing real merchants use.

Deterministic Keypairs

Synthetic merchants and subscribers are deterministic devnet keypairs so the same actors persist across cron firings. State (active mandates, used pull windows) is reset by the funnel itself rather than rotated at each tick.

Pages on Typed Errors

The funnel uses @velapay/sdk's typed error classes — the page (alert) carries the typed error name (PullTooEarlyError, MandateExpiredError, etc.) which lets oncall route to the right runbook without parsing free-form messages.

Internal knowledge base for the Vela Labs workspace.