Appearance
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
| Technology | Version | Purpose |
|---|---|---|
| TypeScript | ^5.8.x | Language |
| Bun | 1.3.x | Package manager + dev |
@vela/sdk | local link | Full client — instruction builders, PDAFactory, account deserializers |
uuidv7 | ^1.2.x | Time-ordered IDs for synthetic event correlation |
| Cloudflare Cron Workers | — | */15 * * * * trigger |
| Cloudflare D1 | — | Reuses vela-dashboard-dev (DB binding) — same database as the dashboard |
| wrangler | ^4.78.x | Deploy + 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:
- Resolve a synthetic merchant + subscriber pair (deterministic devnet keypairs).
- Create or refresh a synthetic plan.
- Subscribe the synthetic subscriber.
- Wait for the next pull window.
- Trigger
execute_pull. - Verify the resulting webhook event reaches D1 in time.
- Cancel the mandate.
- Confirm cancellation event lands.
- 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.jsonDeployment 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/migrationsso it sees the same schema the dashboard sees.
Consumers / Dependencies
| Consumer / Source | How |
|---|---|
| Protocol team | Receives pages on cron failure |
| Devnet staging stack | Target of every synthetic call |
vela-dashboard D1 | Reused as the destination for synthetic events |
vela-sdk | Drives 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.
Related
- E2E billing pipeline overview: Architecture / Billing Flows →
- Operational alerting and on-call: Incident Response →
- Devnet program IDs: Environment Registry →