Open-source AI search intelligence platform.
Self-hosted or managed on Cloudflare. Track how your brand and competitors appear across AI-generated answers, citations, recommendations, ads, and shopping results.
Know when AI recommends you, and when it doesn't.
Belki schedules prompts across ChatGPT, Gemini, Google AI Mode, and Google AI Overviews, captures the returned answers and source evidence, and turns those captures into project-level visibility analytics. It supports multiple isolated workspaces, multiple brands per workspace, and source-specific reporting while retaining the evidence behind every metric.
The default Cloudflare topology is designed for at least one million runs per month on larger self-hosted installations.
- Supported AI platforms
- Capture providers
- Features
- Metrics
- Architecture
- Requirements
- Quick start: self-host on Cloudflare
- Local development
- Production deployment
- Managed cloud
- Operations
- Security and retention
- Verification
- Repository map
- Contributing and security
Belki supports six AI platforms through five capture providers, including Perplexity and Microsoft Copilot through Olostep. Each enabled platform runs every active prompt independently. The overview can combine enabled platforms, while prompts, responses, citations, related queries, entities, ads, and shopping data remain separated by their originating source.
| Platform | Data captured |
|---|---|
| ChatGPT Search | Answers, citations, retrieved sources, fan-out queries, entities, ads, shopping blocks, and retained page evidence |
| Gemini | Answers, citations, retrieved sources, fan-out queries, and entities |
| Google AI Mode | Answers, cited and retrieved sources, fan-out queries, and entities |
| Google AI Overviews | Whether an AI Overview appeared, its answer text, cited sources, and entities |
| Perplexity | Answers, citations, search queries, and entities |
| Microsoft Copilot | Answers, citations, and entities |
ChatGPT is enabled by default. Additional sources must be available in the deployment and allowed by the workspace plan. Claude is not part of the current release.
Belki keeps AI-answer collection behind an internal provider interface. The rest of the product works with one normalized capture contract, so analytics, evidence, alerts, and exports do not need provider-specific code.
The current provider order is DataForSEO, Bright Data, Cloro, Oxylabs, then Olostep. An installation operator connects the first account during onboarding, can add or rotate accounts later under Operator → Providers, and chooses:
- one installation-wide default provider;
- an optional provider override for each AI platform; and
- whether each AI platform is available for new collection.
If a platform has no override, Belki starts with the installation default. If a selected override or default cannot serve that platform at dispatch time, routing advances through active, configured, compatible accounts by provider priority. Disabled or unconfigured accounts are never selected.
| Provider | ChatGPT | Gemini | Google AI Mode | Google AI Overviews | Perplexity | Copilot |
|---|---|---|---|---|---|---|
| DataForSEO | Yes | Yes | Yes | Yes | — | — |
| Bright Data | Yes | — | — | — | — | — |
| Cloro | Yes | Yes | Yes | Yes | — | — |
| Oxylabs | Yes | — | Yes | Yes | — | — |
| Olostep | Yes | Yes | Yes | Yes | Yes | Yes |
Apply control migration 0039_olostep.sql and snapshot migration 0009_olostep.sql
to all shards before deploying this version (the normal migration command does this).
Connect Olostep with an API key under Operator → Providers, then choose it
as the default or as a platform override. Enable Perplexity and Copilot there and
select them in project settings. Self-hosted workspaces can use all six platforms;
managed installations must add the new platforms to their billing plans as needed.
Existing managed plan choices and limits are preserved.
The Olostep GEO API runs asynchronously: Belki submits one item per run, polls every 30 seconds, and retrieves the matching parsed result after completion. Typical collection takes 5–8 minutes; Belki stops polling after 30 minutes. Queue recovery resumes stalled polls without submitting another batch. Both standard and live delivery use this asynchronous flow.
Country targeting uses the selected location's ISO country code. Country coverage is parser-specific and enforced by Olostep; unknown countries fail explicitly. This API does not expose city targeting, language overrides, forced web search, or model selection. Answers, citations, retrieved sources, available search-query telemetry, and documented ChatGPT ads/products are normalized. Other commercial modules remain in retained JSON evidence. Reported USD batch cost is recorded when available; credits are not treated as dollars. No API key is needed to run the mocked integration tests.
Provider accounts belong to the installation, not to customer workspaces.
Credentials are verified before storage, encrypted with APP_CRYPTO_KEY, and
versioned when rotated. The API exposes configuration and health metadata but
never returns stored secrets.
Every collection attempt records its provider account, provider, adapter and credential versions, external task ID, state transitions, timestamps, and errors. Operator → Providers summarizes the last 30 days by account and AI platform: submitted, completed, failed or uncertain, in progress, success rate, average latency, and last activity.
Provider plans, credits, balances, spend, and invoices remain in the provider's own dashboard. Belki links to those dashboards but does not reproduce provider billing or attempt to normalize monetary costs across vendors. Belki's Billing page remains responsible only for Belki subscriptions and workspace limits.
flowchart LR
Onboarding[Installation onboarding] --> First[Connect first provider]
Operator[Operator: Providers] --> Accounts[Provider accounts]
Operator --> DefaultChoice[Installation default]
Operator --> Routes[AI platform choices]
First --> Accounts
Accounts --> Vault[Encrypted, versioned credentials]
DefaultChoice --> Resolver[Provider resolver]
Routes --> Resolver
Vault --> Resolver
Prompt[Prompt + AI platform] --> Resolver
Resolver --> Compatible{Active, configured, compatible?}
Compatible -->|Yes| Adapter[Selected provider adapter]
Compatible -->|No| Stop[Do not dispatch]
Adapter --> Attempt[Tracked provider attempt]
Attempt --> Normalize[Normalized capture contract]
Normalize --> Product[Evidence, analytics, alerts, exports]
Operator -. credits and invoices .-> Vendor[Provider dashboard]
Belki has two deployment modes:
- Self-hosted: the repository default. The installation owner supplies the Cloudflare resources and connects at least one supported capture provider during onboarding. Manual prompt runs are available, and subscription billing is disabled.
- Managed cloud: a hosted subscription service using platform credentials, modular Stripe or Dodo Payments billing, plan limits, signup codes, and protected operator tools.
Unless labeled otherwise, the features below are available in both modes.
Install belki-analytics.js to measure sessions originating from recognized
AI utm_source values, subsequent page views, approximate visible time, country,
device and conversions by page or JavaScript event. Collection is opt-in, with
per-project domains, visible built-in mappings, custom aliases and sources, goals
and 12 months of history.
AI Analytics → Installation generates the complete tag and provides a temporary
test session excluded from real reports. Conversion delivery includes a bounded
per-tab retry queue and server deduplication. Conversions and Sources have
their own configuration sections. Each section has a direct route: /ai-traffic
for Overview, /ai-traffic/installation, /ai-traffic/conversions, and
/ai-traffic/sources.
Open-source installs work with the application origin by default. Run
pnpm configure:analytics to choose an independent analytics subdomain or an
optional CDN script URL. The deployment commands support both modes, sharing the
same SDK and collection logic. Operator → Analytics service shows the resolved
configuration and can check availability. No Belki-hosted service is required.
See AI Analytics installation, hosting and metric definitions.
- Users can belong to multiple isolated workspaces and switch between them without mixing projects, memberships, billing, or analytics.
- Installation onboarding connects the first capture provider before project setup. Guided workspace onboarding then creates the first project and its initial prompts.
- Every project defines an owned brand and optional primary domain for mention, citation, seller, and competitor comparisons.
- Projects select one or more available AI platforms, subject to the active plan.
- Projects can be paused without releasing their plan slot. Scheduled captures, recurring enrichment, and queued analytical work stop until the project is resumed; history and configuration remain available.
- Workspace owners and administrators can create and edit projects. Archived projects retain their history and can be restored when the plan has room.
- Project and source selectors preserve the relevant report context and safely return from detail pages when the selected scope changes.
The 30-day overview provides an All platforms rollup and an individual tab for every enabled source. It includes:
- owned-brand visibility, Share of Voice, visibility gap, and completed capture counts;
- AI Overview appearance counts when Google AI Overviews is selected;
- daily owned-brand and competitor visibility, Share of Voice, or average first-mention position trends;
- pause intervals rendered as missing-data gaps rather than zero-value results;
- prompt-set change annotations so changes in the monitored sample are visible;
- leading citation-source trends, with the project domain identified separately;
- leading competitors, top products, and recently detected products when the underlying data exists.
The competitor chart displays the owned brand and the four most visible competitors. Share of Voice includes every competitor tracked for that capture. Adding, editing, or removing a tracked brand takes effect for new captures and does not invalidate or hide completed historical position metrics.
- Create prompts individually or in bulk, assign tags, search and filter them, and enable, pause, edit, or permanently delete them.
- Schedule prompts daily or weekly at a local time with a selected language, location, and IANA time zone. Scheduling remains DST-aware.
- Run an enabled prompt immediately in self-hosted mode; managed plans rely on scheduled runs.
- Review the latest status, capture count, mentions, mention rate, citations, and change signals for each prompt.
- Open per-prompt response history and navigate to the exact captured response.
- Browse a searchable library of proven brand-reputation prompt templates, inspect a template, customize it, and add it with the same locale, schedule, source, and tag controls as a new prompt.
- Deleting a prompt starts an asynchronous purge across control D1, its snapshot shard, and R2. Aggregate usage history remains for operational integrity.
Belki retains a global response explorer as well as history for each prompt. Response detail can show:
- the captured answer rendered as readable Markdown, including tables;
- cited sources separately from sources retrieved or considered by the model;
- shopping results and fan-out queries returned with the answer;
- tracked-brand and competitor mentions, extracted entities, and entities supplied by the provider;
- changes from the previous capture and links to recent captures;
- authorized downloads of normalized raw evidence and, when enabled, the original response-page HTML.
Provider callbacks are authenticated, normalized asynchronously, and recoverable when a postback is missed. Failed or incomplete runs retain enough status data for diagnostics without being counted as completed answers.
- Aggregate recurring fan-out queries generated while an AI platform expands an answer.
- Search and compare 7-, 30-, 90-, or 365-day periods.
- See frequency, tracking status, parent prompts, and every matching response.
- Inspect cited URLs separately from retrieved-but-not-cited URLs in the context of the related query.
- Turn a useful untracked query into a new monitored prompt in one step.
- Lists and evidence tables use cursor pagination instead of loading unbounded result sets.
- Measure citation rate, source share, leading domains, and daily source trends.
- Separate explicit citations from retrieved search results throughout the UI.
- Open a source to see its trend, prompts citing it, cited pages, occurrence counts, and links to the exact response evidence.
- Source and URL tables use bounded cursor pagination for large projects.
- Tracked entities contain the owned brand and competitors included in visibility and Share of Voice reporting.
- Discovered entities contain brands, companies, people, places, and other names extracted asynchronously from captured answers.
- Candidate relationship labels and evidence strength distinguish likely competitors from unrelated mentions and provider entities.
- Expand an entity to review answer evidence and corroboration before tracking, ignoring, or restoring it.
- Search and filter tracked and discovered entities independently by type, relationship, or review status.
- Both entity tabs use debounced server-side search and 25-item cursor pages, so thousands of discoveries are never fetched into the browser at once.
- Versioned v2 extraction validates exact spans from the answer, tolerates malformed model output, classifies competitive context separately, and combines answer, provider, domain, and tracked-brand evidence.
- Existing retained responses can be backfilled into a new extractor version. Automatic competitor tracking is opt-in and requires repeated strong evidence.
Ads appears after the selected project has captured a sponsored result. Reports are available for sources that actually returned ad data and include:
- ad appearance rate and daily rendered-versus-served-but-hidden activity;
- advertisers, destination domains and URLs, observed positions, and prompts;
- creative copy, images, and favicons mirrored into private R2 storage;
- 7-, 30-, 90-, and 365-day advertiser detail and links to source snapshots.
Advertisements remain visible in their originating response even when the Ads navigation item is hidden for a project with no current ad data.
Shopping appears for sources that returned product blocks. The catalog and product detail reports include:
- product and merchant search, period filters, and bounded cursor pagination;
- visibility, appearances, win rate, average and median position, and position distribution;
- co-featured products, price history, rating and review-count trends;
- prompts, merchant observations, and links to the captured response history;
- owned-seller identification based on workspace domains.
Optional DataForSEO Google Product Info enrichment adds catalog specifications, seller offers, delivery and availability, market price ranges and history, and review data for compatible Google Shopping product identifiers. Automatic enrichment runs at most weekly for each product and locale; administrators can also request an eligible refresh. It does not bulk-backfill historical products.
- Configure change-detection rules and review read or unread alert events in the app.
- Add workspace-wide public HTTPS webhook destinations for external delivery.
- Webhook secrets are displayed once. Payloads include an event ID and timestamp
and are signed with HMAC-SHA256 in
x-ai-tracker-signature. - Generate compressed analytics and captured-response indexes asynchronously as CSV or NDJSON, monitor their status, and download completed exports.
- Role-based access uses
owner,admin,editor, andviewerpermissions. - Owners and administrators can invite members, select their role, inspect pending invitations, and revoke an invitation immediately.
- Invitation links are always displayed once for copying and can optionally be sent with Cloudflare Email Sending. Links are validated before the acceptance form is shown and expire after 7 days.
- Local password authentication supports one-use password-reset links that expire after 1 hour.
- Sessions last up to 14 days and are backed by secure cookies and CSRF tokens.
Settings are divided by ownership boundary:
- Project: project identity, enabled AI platforms, original HTML retention, Shopping enrichment, entity extraction, automatic competitor tracking, and workload-specific cost warnings.
- Workspace: workspace identity, active and archived projects, and signed webhook destinations.
- Diagnostics: administrator-only checks for the control database, all 32 snapshot shards, capture-provider readiness, payment-provider mode, and required runtime bindings.
Capture-provider plans, credits, invoices, and monetary spend remain in each provider's dashboard. The operator provider page reports comparable activity instead: submitted, completed, failed or uncertain, and in-progress attempts, plus success rate, latency, and last activity by provider and AI platform.
Self-hosted installations show their configured Belki limits without enforcing a subscription plan. Provider billing is not duplicated inside Belki.
Managed cloud adds:
- an installation-defined subscription catalogue with arbitrary plan names, ordering, limits, allowed sources, currency, and prices;
- current subscription, monthly limits, and usage reporting;
- provider-hosted Checkout and customer management through Stripe or Dodo;
- immediate, prorated upgrades confirmed by the selected payment provider;
- downgrades scheduled for renewal with an exact transition preview;
- replacement or cancellation of a pending downgrade;
- non-destructive allowance enforcement that archives excess projects, pauses excess prompts, and disables excess sources without deleting history;
- read-only state for cancelled or unpaid workspaces.
These capabilities are available only in a managed deployment and must be protected with Cloudflare Access.
- Create email-bound, one-use signup URLs; list recent codes; and revoke unused codes.
- Inspect managed workspaces, subscription state, allowances, and operator interventions.
- Suspend or restore a workspace for support and abuse handling, and reconcile state that normally follows verified payment-provider webhooks.
- Analyze aggregate citation trends across retained managed projects with source, domain, period, and project filters.
- Analyze aggregate fan-out telemetry, model activity, query signals, year terms, response coverage, and fan-outs per response without unbounded D1 reads.
| Metric | Definition |
|---|---|
| Visibility | Completed responses mentioning the tracked brand ÷ all completed responses |
| Share of Voice | Tracked-brand appearances ÷ appearances across the owned brand and every competitor tracked for that capture |
| Average position | Mean first-mention rank among brands tracked for that capture; lower is better |
| Visibility gap | Owned-brand visibility minus the visibility of the leading tracked competitor, in percentage points |
| Citation rate | Completed responses containing at least one citation ÷ all completed responses |
| Ad appearance rate | Completed responses containing at least one sponsored result ÷ all completed responses |
| Product win rate | Product appearances at position 1 ÷ all responses in which the product appeared |
Share of Voice and visibility gap become available after at least one competitor is tracked. Google AI Overview metrics distinguish a completed search with no AI Overview from a capture in which the surface appeared.
Belki runs a React single-page application and Hono API in one Cloudflare Worker. A control D1 database stores tenancy, orchestration, configuration, and aggregate state. Thirty-two snapshot D1 shards distribute detailed captures. R2 stores raw evidence, optional HTML, generated exports, and mirrored advertising assets.
One SQLite-backed Durable Object per workspace serializes usage reservations and project-limit claims so concurrent requests and queues cannot overspend an allowance.
flowchart LR
Browser[Belki React SPA] --> API[Hono Worker API]
Operator[Installation operator] --> API
Cron[Cloudflare Cron Triggers] --> StandardQ[Scheduled task queue]
API --> LiveQ[Manual task queue]
API --> Control[(Control D1)]
Control --> ProviderConfig[Accounts / defaults / AI platform choices]
Crypto[[APP_CRYPTO_KEY]] -. credential decryption .-> Resolver[Provider resolver]
ProviderConfig --> Resolver
StandardQ --> Resolver
LiveQ --> Resolver
Resolver --> Registry[Provider registry]
Registry --> DFSAdapter[DataForSEO adapter]
Registry --> BrightAdapter[Bright Data adapter]
Registry --> CloroAdapter[Cloro adapter]
Registry --> OxyAdapter[Oxylabs adapter]
Registry --> OlostepAdapter[Olostep adapter]
DFSAdapter --> Providers[External AI capture providers]
BrightAdapter --> Providers
CloroAdapter --> Providers
OxyAdapter --> Providers
OlostepAdapter --> Providers
Resolver --> Attempts[Provider attempt provenance]
Providers --> Result[Callback / polled / synchronous result]
Result --> Attempts
Result --> R2[(Private R2 evidence)]
Result --> NormalizeQ[Normalization queue]
NormalizeQ --> Shards[(32 snapshot D1 shards)]
NormalizeQ --> Control[(Control D1)]
NormalizeQ --> EntityQ[Entity extraction queue]
NormalizeQ --> AssetQ[Ad asset queue]
EntityQ --> WorkersAI[Workers AI]
AssetQ --> R2
Shards --> RollupQ[Analytics rollup queue]
RollupQ --> Control
Control --> ProductQ[Product enrichment queue]
ProductQ --> Merchant[DataForSEO Google Product Info]
Merchant --> ProductPostback[Authenticated product result]
ProductPostback --> R2
ProductPostback --> ProductQ
Control --> Automation[Alerts / exports / webhooks / purges]
API --> Control
API --> Shards
API --> R2
The control database owns workspaces, users, projects, prompts, schedules, job
state, compact snapshot indexes, rollups, ads, entities, enrichment state,
alerts, Belki billing, provider configuration and attempt provenance, and
exports. Each prompt is assigned to one snapshot shard for its lifetime,
avoiding cross-database joins on detail reads. Provider credentials are stored
as encrypted, versioned records in the control database; APP_CRYPTO_KEY
remains a Worker secret and is never stored beside them.
flowchart TD
Prompt[Scheduled or manually requested prompt] --> Source[Enabled AI platform]
Source --> Resolve[Resolve platform override or installation default]
Resolve --> Compatible{Compatible provider account available?}
Compatible -->|No| Blocked[Keep failure state; do not dispatch]
Compatible -->|Yes| Attempt[Create versioned provider attempt]
Attempt --> Adapter[Dispatch through selected adapter]
Adapter --> Mode{Provider execution mode}
Mode -->|Asynchronous| Async[Submit task with signed callback]
Mode -->|Synchronous| Direct[Wait for provider response]
Async --> Callback[Authenticated provider callback]
Async -. missed callback recovery .-> Recover[Adapter-specific reconciliation]
Callback --> Capture[Provider result]
Recover --> Capture
Direct --> Capture
Capture --> AttemptState[Update attempt state and provenance]
Capture --> Evidence[Store raw evidence in R2]
Evidence --> Normalize[Normalize provider payload into Belki contract]
Normalize --> Snapshot[Persist answer / sources / products / ads]
Snapshot --> Rollup[Update analytics and internal usage]
Snapshot --> Entities{Entity extraction enabled?}
Snapshot --> Ads{Ad assets present?}
Snapshot --> Shopping{Eligible product enrichment?}
Entities -->|Yes| NER[Extract / validate / corroborate entities]
Ads -->|Yes| Assets[Mirror creative assets to R2]
Shopping -->|Yes| ProductInfo[Capture Shopping market snapshot]
Rollup --> Reports[Overview and source-separated reports]
NER --> Reports
Assets --> Reports
ProductInfo --> Reports
- Node.js 24 or newer
- pnpm 11
- A Cloudflare account with Workers Paid
- Credentials for at least one supported capture provider
- DataForSEO Merchant Google Product Info access for optional Shopping enrichment
- A Cloudflare Email Sending domain only when invitation emails are required
- A Stripe or Dodo Payments account only when managed subscriptions are enabled (configured in the operator UI)
Workers Paid is required by the default topology because it declares one control database and 32 snapshot databases. Workers Free permits fewer D1 databases than this architecture requires.
Email is disabled in the checked-in self-hosted configuration. Invitations
still return a copyable link, and the sign-in page hides email password recovery.
To enable delivery, onboard a sending domain in Cloudflare Email, set
EMAIL_FROM_ADDRESS in wrangler.jsonc, and optionally restrict the EMAIL
binding with allowed_sender_addresses. The managed deployment supplies its
sender through its managed deployment profile; no Belki sender is inherited by
a fork.
Install Belki in your own Cloudflare account using the steps below. The Deploy button requires this source repository to be publicly accessible.
The software is Apache-2.0 licensed. You pay Cloudflare and your chosen capture provider directly for their services; a Belki subscription is not required. The default installation uses 33 D1 databases and requires Workers Paid. Provider usage and optional enrichment can incur additional charges. Start with one manual prompt before enabling scheduled collection.
-
Create a Cloudflare Workers Paid account and choose an initial capture provider: DataForSEO, Bright Data, Cloro, Oxylabs, or Olostep.
-
Generate two different secrets locally. Keep the outputs out of Git, screenshots, issue reports, and shell history shared with other people.
openssl rand -base64 32 openssl rand -base64 32
-
Click Deploy to Cloudflare. Cloudflare creates a fork, provisions the declared Worker, D1, R2, Queue, Durable Object, Workers AI, and asset bindings, then runs the repository's deploy command.
-
Enter only these two required application secrets when Cloudflare asks for bindings:
Secret Value APP_CRYPTO_KEYFirst generated value; encrypts provider credentials and authenticates provider callbacks BOOTSTRAP_TOKENSecond generated value; save it for step 6 -
Wait for the deployment and migrations to finish, then open the generated
workers.devURL. LeavingPUBLIC_APP_ORIGINempty is intentional for this first deployment: setup records the origin you opened. -
The first-run screen appears automatically. Enter the exact
BOOTSTRAP_TOKEN, owner details, and workspace details to create the first account. Then select the initial capture provider and enter its credentials. Finally choose managed subscriptions—defining the installation's plans and configuring either Stripe or Dodo—or self-hosted/in-house mode without payments. Belki verifies and encrypts all configured credentials before use. The token cannot create a second owner after setup is complete. -
Create a project and a test prompt, enable only the sources you intend to use, and run it once before scheduling recurring collection.
-
Clone your new fork, authenticate Wrangler, and apply the checked-in R2 lifecycle policy with
pnpm r2:lifecyclebefore enabling original HTML capture.
Self-hosted mode does not use a payment provider. Invitation email is also optional: with the default empty sender, owners can copy invitation links directly from the application.
First confirm the workers.dev deployment works. Then edit your fork's
wrangler.jsonc so the final HTTPS origin and custom-domain route are deployed
together:
The hostname must belong to a Cloudflare zone in the same account and the
origin must not contain a path or trailing slash. Deploy the change, open the
custom domain, and update any workspace URL that was saved during the initial
workers.dev setup under Settings → Workspace. The configured origin is
authoritative for future provider callbacks and generated links. For an
existing installation that has already changed domains, use the repair steps
in the deployment runbook.
pnpm install
cp .dev.vars.example .dev.vars
pnpm db:migrate:local
pnpm devConfigure these secrets in .dev.vars:
| Secret | Purpose |
|---|---|
APP_CRYPTO_KEY |
Encrypts operator-owned capture and payment-provider credentials and authenticates provider jobs and callbacks; changing it makes stored credentials unreadable and invalidates outstanding jobs |
BOOTSTRAP_TOKEN |
One-time token entered during first-run owner setup |
Use independent, high-entropy values for the two application secrets. Add the first provider through the setup screen, then manage provider accounts, the default, and the provider for each AI platform under Operator → Providers. Managed installations also configure the payment account, environment, encrypted API/webhook secrets, installation-defined plan catalogue, and provider-specific webhook URL during installation or under Operator → Payments. The initial GetBelki catalogue is only an editable template: an installer can rename, reorder, add, or remove plans and set each plan's limits, allowed tracking sources, price, and currency before saving the first payment account. Payment secrets are never returned to the browser or stored as Worker bindings. For locally submitted asynchronous tasks, the provider must be able to reach the Worker. Use a deployed staging Worker or secure public tunnel and set the workspace public application URL accordingly. Self-hosted live runs do not need an inbound postback.
For resource provisioning, secret placement, payment-provider configuration, Cloudflare
Access, existing-install migration, verification, and rollback, read
docs/deployment-and-migration.md.
Adapter ownership and the Stripe/Dodo integration contract are documented in
docs/payment-provider-adapters.md.
The Cloudflare Deploy button provisions the declared D1, R2, Queue, Workers AI, and secret bindings and can connect the fork to Workers Builds:
For a manual deployment, first provision the resources declared in
wrangler.jsonc in your own account and configure their bindings. Authenticate
Wrangler, configure the two required production secrets, and run:
pnpm build
pnpm deploy
pnpm r2:lifecycleBefore first-run setup, set PUBLIC_APP_ORIGIN in wrangler.jsonc to the final
HTTPS origin, such as https://tracker.example.com. It is the canonical base
for provider postbacks, invitations, password resets, and billing return URLs.
Leave it empty only for local development or when you want each workspace owner
to manage the URL under Settings → Workspace. A configured value overrides
the stored workspace value and prevents a legacy workers.dev hostname from
reappearing after a custom-domain migration.
pnpm deploy applies control migrations and the same snapshot migrations to all
32 shards before publishing with Wrangler. pnpm r2:lifecycle replaces the
ai-tracker-evidence bucket lifecycle configuration with the checked-in
prefix-specific retention rules.
For an interactive release that validates the checkout, verifies required Worker secret names, previews and applies every D1 migration, inspects the migrated provider/payment/plan state, deploys the Worker, and runs a public smoke check, use:
pnpm deploy:guidedThe assistant asks whether this is a GetBelki or self-hosted deployment. For a repeatable non-interactive invocation, pass the target explicitly:
pnpm deploy:guided -- --target self-hosted --url https://tracker.example.com --yes
pnpm deploy:guided -- --target getbelki --yesA pre-launch installation whose only billing state is disposable sandbox data can explicitly reset payments while keeping projects, captures, users, provider configuration, and logical plan definitions:
pnpm deploy:guided -- --target getbelki --reset-test-paymentsThe reset runs after the additive migrations and before Worker publication. It
prints the affected accounts/workspaces and refuses to continue if any live or
ambiguous payment marker exists. It then records a CONTROL_DB Time Travel
bookmark and requires the exact phrase DISCARD-TEST-PAYMENTS; --yes does not
bypass that phrase. For a non-interactive run, the destructive confirmation
must also be explicit:
pnpm deploy:guided -- --target getbelki --reset-test-payments \
--confirm-reset-test-payments DISCARD-TEST-PAYMENTS --yesThis resets Belki's database only. Cancel the discarded subscription and remove its legacy webhook in the provider's sandbox dashboard; the deployment helper does not mutate external Stripe or Dodo objects.
Provider credentials and commercial choices are deliberately completed in the authenticated Belki UI after deployment. This prevents secrets from appearing in shell history and ensures each account is remotely verified before Belki encrypts it in D1. The script prints direct links and the remaining checklist.
Self-hosted mode is the repository default: DEPLOYMENT_MODE=self_hosted and
PAYMENT_MODE=disabled. During installation onboarding, the operator connects
the first capture provider and then explicitly chooses Self-hosted / in-house
or Managed subscriptions. The self-hosted path requires no payment account
and disables Checkout. It can later be changed to managed under
Operator → Payments by adding or activating a verified payment account.
Belki encrypts provider credentials with APP_CRYPTO_KEY before storing them in
the control database; credentials can be replaced but are never returned to the
browser.
Belki's managed profile lives in deployment/getbelki.json, while
wrangler.jsonc remains the self-hosting default.
# Publish application code without changing the database schema
pnpm deploy:getbelki
# Apply pending D1 migrations before publishing
pnpm deploy:getbelki:with-migrationsProduction changes should pass CI on a feature branch, be reviewed through a
pull request, and merge through the protected main branch. The deployment
system can then publish the managed profile. Code-only deployments deliberately
remain separate from migration-bearing deployments.
flowchart LR
Branch[Feature branch] --> PR[Pull request]
PR --> CI[pnpm check / types check / deploy dry run]
CI --> Merge[Protected main branch]
Merge --> Publish{Schema migration?}
Publish -->|No| Code[deploy:getbelki]
Publish -->|Yes| Migrate[deploy:getbelki:with-migrations]
Managed mode uses operator-owned capture-provider accounts and sells prompt tracking, not provider credentials or API credits. The installation operator can choose a default provider and override it for each AI platform.
The table below is GetBelki's current commercial catalogue, not a repository-wide requirement. A fresh Belki installation starts with this editable template and can define 1–8 plans of its own while connecting its first payment account. For each plan the operator sets a stable internal ID, customer-facing name and description, display order, featured state, monthly usage allowance, project and prompt limits, allowed tracking sources, source limit, ISO 4217 currency, and monthly price.
| Plan | Price | Active prompts | Projects | Tracking sources per project | Seats |
|---|---|---|---|---|---|
| Starter | €10/month | 25 | 1 | ChatGPT | Unlimited |
| Studio | €29/month | 75 | 5 | Up to 2 enabled sources | Unlimited |
| Agency | €149/month | 250 | 30 | Up to 4 enabled sources | Unlimited |
The six supported platforms are available according to each plan's allowed sources and the installation's enabled provider routes. Managed operators must enable Perplexity and Microsoft Copilot and add them to the appropriate plans. Each additional source runs the project's active prompts independently.
Set DEPLOYMENT_MODE=managed and enable the PAYMENT_MODE runtime kill switch.
After the operator creates the installation owner and capture-provider account,
Belki asks for an installation mode. Managed setup requires exactly one verified
payment account and lets the operator choose either Stripe or Dodo Payments.
Rebill and Polar appear as disabled Coming soon entries so the roadmap is
visible without implying that an unimplemented adapter can accept payments.
Every provider account maps the installation's logical plans to that provider's
external objects, so a later provider can use different IDs and amounts without
changing customer entitlements:
- Stripe uses a test/live secret key, webhook signing secret, one monthly Price ID and amount per active installation plan, and an action Customer Portal configuration ID.
- Dodo Payments uses a test/live API key, Standard Webhooks signing secret, and one monthly Product ID and amount per active installation plan. Its customer portal is created through Dodo's API.
Plan definitions become the shared installation catalogue when the first payment account is saved. Additional accounts reuse those definitions and only map them to their own Price/Product IDs and amounts. This prevents Stripe and Dodo from silently describing different entitlements.
Copy the unique webhook URL shown for that account into the selected provider. Additional Stripe or Dodo accounts can be added later under Operator → Payments. Changing the installation default affects only new Checkout sessions; each existing workspace remains pinned to the account that created its subscription.
Signup is email-bound and one-use. A platform operator creates a URL through the
Platform operations page or POST /api/v1/operator/signup-codes. A workspace
remains pending until a verified payment-provider webhook activates it. Protect
both /operator* and /api/v1/operator/* with the same Cloudflare Access
policy. Operator navigation intentionally performs a full document request so
Access can issue or renew its authorization cookie before protected API data is
requested. The Worker also verifies that the Access-authenticated email belongs
to platform_operators. Disable workers.dev and attach a custom production
route before launch.
Belki maintains internal workload budgets for cost and abuse protection. Those weights are implementation details, not a customer-facing credit balance. Reservations occur before paid work: accepted provider work is committed, while pre-submit failures and explicit provider rejections release the reservation.
| Schedule | Responsibility |
|---|---|
| Every 5 minutes | Enqueue due prompts, recover stale jobs, enqueue eligible product enrichments, and reconcile product work |
| Every minute | Reconcile billing changes, recover missed source and product postbacks, reconcile purges, entity extraction and ad assets, and advance analytics backfills |
| Daily at 03:00 UTC | Expire sessions, tokens, ingest events, jobs, indexes, hot snapshots, and completed exports |
| Sunday at 04:00 UTC | Refresh the DataForSEO locale catalog and reconcile metric outboxes |
The scheduler uses separate standard and live queues, signed provider job tags, idempotent callbacks, retryable normalization, and adapter-specific recovery. Cloudflare observability is enabled and scheduled handlers emit structured metrics. Brand-only migration work uses a separate bulk path of up to 250 snapshots per minute; ordinary tracked-brand changes are forward-effective and do not start a historical project-wide rebuild.
- Workspace membership and every project read are tenant-scoped.
- Local password authentication uses secure cookies, CSRF protection, role checks, hashed one-use tokens, and bounded session lifetime.
- Invitation links expire after 7 days; password-reset links expire after 1 hour; sessions expire after 14 days.
- Stored provider credentials use AES-256-GCM with key material derived from
APP_CRYPTO_KEY; rotation creates a new credential version without exposing prior values. - Provider callbacks require a valid signed job tag and are matched to the provider account and external task recorded for the attempt.
- Stripe webhook signatures are verified before subscription state changes.
- Managed operator endpoints combine Cloudflare Access identity with an internal operator allowlist.
- Webhook delivery accepts only public HTTPS destinations and signs each payload.
- R2 is private; evidence and export downloads are authorized by the Worker.
- Raw prompts should not contain personal or sensitive information.
| Data | Default retention |
|---|---|
| Normalized raw provider results | 730 days |
| Original response-page HTML | 90 days when project capture is enabled |
| Hot snapshot detail | 12 months |
| Completed exports | 7 days |
Original HTML capture is opt-in per project. Permanent prompt deletion removes its detailed D1 and R2 evidence asynchronously while preserving aggregate usage history needed for operational integrity.
Run the same aggregate application checks used by CI:
pnpm check
pnpm types:check
pnpm deploy:dryMaintainers changing Belki's managed deployment must additionally run
pnpm deploy:getbelki:dry. Self-hosters do not need the managed deployment
profile or payment-provider credentials.
pnpm check runs application and Worker type checking, strict linting, unit
tests, Workers integration tests, and the production build. CI also checks both
self-hosted and managed deployment packages without publishing them. GitHub Actions uses
Node.js 24 and pnpm 11.0.8 and runs on every pull request and push to main.
Entity-extraction releases also have an offline gold-set gate. Keep private or
sanitized JSONL fixtures out of Git. pnpm evaluate:entities creates the local
stratified sample, v1/v2 predictions, and review page. After review, run:
pnpm benchmark:entities -- --gold <gold.jsonl> --prediction v2=<predictions.jsonl>Fixture shape, metrics, and acceptance thresholds are documented in
evaluation/entity-extraction.
src/app: React dashboard, onboarding, authentication, and product experiencesrc/worker: Hono API, provider adapters, routing, ingestion, analytics, automation, and scheduled worksrc/shared: request schemas, provider and source definitions, plan access, activity summaries, and queue contractsmigrations/control: tenancy, provider accounts and attempts, orchestration, analytics, entities, ads, enrichment, billing, and project settingsmigrations/snapshot: detailed response, citation, product, and ad evidencedeployment: environment-specific deployment profilesinfrastructure: R2 lifecycle configurationscripts/migrate.mjs: control and 32-shard migration runnerdocs/deployment-and-migration.md: production provisioning and migration runbookdocs/public-release-checklist.md: maintainer checklist for approving public releaseevaluation/entity-extraction: offline entity quality workflowai-tracker-prd.md: original product and architecture requirements
Read CONTRIBUTING.md before opening a pull request. Report suspected vulnerabilities privately using the process in SECURITY.md; do not put exploit details or secrets in an issue.
Apache-2.0