Application is live at: https://bullcheck.pontus-dorsay.workers.dev/ ✨
BullCheck is a statistics-focused question-answering system designed to combat misinformation by providing accurate, verifiable answers based on official sources, such as Statistics Sweden (SCB). While it currently connects to a single source, the application’s architecture is designed to make adding additional sources simple and straightforward.
Built as a showcase for a Cloudflare Internship application, this project demonstrates how to leverage the full Cloudflare Developer Platform to build high-performance, resilient, and truthful AI applications.
Test Account:
-
Username:
cloudflare@gmail.com -
Password:
CloudflareRocks123 -
Population & Demographics
-
Births & Deaths
-
Marriages & Divorces
-
Life Expectancy
-
Migration & Asylum
-
Households
-
Labor Market
-
Wages & Income
-
Macroeconomy (GDP)
-
Prices & Inflation (CPI/PPI)
-
Trade (Imports/Exports)
-
Housing & Construction
-
Energy (Electricity prices/consumption/production)
-
Environment (Greenhouse gases)
-
Transport (Vehicles/registrations)
Please try questions within the domains above. Example questions are "How many deaths were there in Sweden 2017?" or "What was the average salary in Sweden 2023?".
- Edge-Native Performance: Deployed globally on Cloudflare's edge network for <50ms latency.
- Grounded Truth: Unlike standard LLMs, BullCheck never invents numbers. It retrieves real data from trusted statistical sources and uses the LLM only for presentation.
- Strictly Statistical: A deterministic agent filters out off-topic queries.
- Transparent Sources: Every answer is traceable to a specific source.
- Secure: Built with privacy-first principles and secure authentication via Better Auth.
Selected for its 0ms cold start and global distribution. Serving the SvelteKit app and API from the edge ensures the fastest possible Time-to-First-Byte (TTFB).
Chosen for its ability to compile to highly efficient edge-ready code.
- Runes: Utilizes Svelte 5's fine-grained reactivity for a responsive UI.
- Edge Adapter: Seamless deployment via
@sveltejs/adapter-cloudflare.
Why? AI agents need memory. We use Durable Objects to serialize requests and manage the state of chat sessions. This ensures strong consistency and prevents race conditions when the agent is "thinking," a capability stateless functions lack.
- Cloudflare D1 (SQLite): Stores our curated index of SCB tables (
scb_tables), allowing complex SQL queries at the edge. - Cloudflare KV: Implements critical caching:
- Metadata Cache (7-day TTL): Table definitions.
- Response Cache (24-hour TTL): SCB API responses.
- Cloudflare AI Gateway: Provides observability, caching, and rate limiting for LLM inference calls.
- Runtime: Cloudflare Workers
- Framework: SvelteKit
- Language: TypeScript
- Database: Cloudflare D1
- ORM: Drizzle ORM
- Styling: TailwindCSS
- Auth: Better Auth
- Testing: Vitest & Playwright
-
Clone the repository:
git clone https://github.com/Lifehack25/BullCheck.git cd BullCheck -
Install dependencies:
pnpm install
-
Configure environment:
cp .env.example .env
-
Database Setup:
pnpm run db:generate pnpm run db:migrate
-
Start Dev Server:
pnpm run dev
Access the app at
http://localhost:5173. -
Preview Worker:
pnpm run preview
The project is configured via wrangler.jsonc and uses the following bindings:
DB: D1 database for the table index.AI: Cloudflare Workers AI binding.BULLCHECK_AGENT: Durable Object namespace for chat sessions.SOURCE_METADATA_CACHE/SOURCE_RESPONSE_CACHE: KV Namespaces.
- Orchestrator: Classifies the prompt (DATA / REPHRASE / OFFTOPIC).
- SCB Specialist:
- Searches D1
scb_tablesindex (seeseed.sql). - Deterministically selects the best table.
- Builds a valid SCB API v2 query.
- Searches D1
- Data Retrieval: Checks KV cache -> Fetches from SCB -> Caches result.
- Presentation: LLM formats the raw data into a natural language response.
The system is designed for growth. The Orchestrator pattern decouples query understanding from data retrieval, making it trivial to plug in additional "Specialist" agents (e.g., for Eurostat, World Bank, or OECD data) simply by registering a new intent classifier and a corresponding Durable Object or service.
src/
├── lib/ # Shared utilities and components
├── routes/ # SvelteKit file-based routing
├── hooks.server.ts # Server-side hooks (Auth, etc.)
└── worker.ts # Worker entry point
drizzle/ # Database migrations
tests/ # End-to-end tests
We use Vitest for unit testing and Playwright for end-to-end testing.
# Run unit tests
pnpm test
# Run End-to-End tests
pnpm test:e2eDeploy to Cloudflare Workers with a single command:
pnpm run deployBuilt with ❤️ for the Cloudflare Team
