- ☁️ Cloud / Self hosted Support: Use our Cloud to manage your app updates or yours.
- 📦 Bundle Management: Download, assign to channel, rollback.
- 📺 Channel Support: Use channels to manage different environments.
- 🎯 Set Channel to specific device to do QA or debug one user.
- 🔄 Auto Update: Automatically download and set the latest bundle for the app.
- 🛟 Rollback: Reset the app to last working bundle if an incompatible bundle has been set.
- 🔁 Delta Updates: Make instant updates by only downloading changed files.
- 🔒 Security: Encrypt and sign each updates with best in class security standards.
- ⚔️ Battle-Tested: Used in more than 3000 projects.
- 📊 View your deployment statistics
- 🔋 Supports Android and iOS
- ⚡️ Capacitor 8/7/6/5 support
- 🌐 Open Source: Licensed under GNU AFFERO GENERAL PUBLIC LICENSE
- 🌐 Open Source Backend: Self install our backend in your infra
Capgo is deployed to production on Cloudflare workers and Supabase.
Cloudflare workers take 99% of the traffic. Supabase is used for internal calls, for internal tasks such as CRON jobs that call functions.
When self-hosted, installing only Supabase is sufficient.
We support both deployments for practical reasons:
- Supabase is the legacy backend and the only required piece for self-hosting.
- Cloudflare Workers runs the same backend code (via the Hono adapter) but is much cheaper at Capgo scale. With ~50M devices, Supabase Edge Functions are cost-prohibitive because they follow AWS pricing. Cloudflare is ~10x cheaper for our traffic profile.
In production, we route most traffic through Cloudflare Workers for cost and scale, while Supabase remains the reference backend and the default for self-hosted deployments. Private endpoints and trigger/CRON workloads still run on Supabase in production.
If you're self-hosting, the key pieces live in a few top-level directories:
supabase/- Primary backend for self-hostingsupabase/functions/- Edge functions (Deno) that power the API_backend/- Core implementation used by both Supabase and Cloudflarepublic/- Public API routes used by customers and appsprivate/- Internal API routes for the console and ops toolingplugins/- Plugin endpoints (updates, stats, channel_self, etc.)triggers/- Database triggers and CRON functions
supabase/migrations/- Database schema and RLS policiessupabase/seed.sql- Local seed data for tests/dev
supabase/schemas/prod.sql- Production schema dump (reference only)cloudflare_workers/- Optional Cloudflare Workers deployment (prod traffic)cloudflare_workers/snippet/- Geo routing for replicas- Worker entry points and deploy config live here
src/- Frontend Vue 3 web console (Vite + Tailwind + DaisyUI)src/pages/- File-based routessrc/components/- Reusable UI componentssrc/services/- API clients and integrationssrc/stores/- Pinia stores
sql/- Raw SQL helpers and maintenance scriptsscripts/- Dev/build scripts used by CI and local toolingtests/- Backend Vitest tests (run in parallel)playwright/- Frontend E2E testsdocs/- Extra documentation and guidesandroid/,ios/- Capacitor native projects (mobile builds)
Quick self-hosting path:
supabase/is enough to run the backend locally.src/is the web console you point to your own backend.cloudflare_workers/is only needed if you want to run the Workers layer instead of (or in front of) Supabase.
The backend is split by responsibility to keep routes clear and access scoped:
supabase/functions/_backend/public/- Public API exposed to customers. This is the documented API on the website for customers that want to interact with Capgo programmatically (apps, channels, bundles, devices, etc.).supabase/functions/_backend/private/- Private API used internally. The console (web UI) uses this heavily for admin/ops workflows. It is not publicly accessible. Some UI flows still use the public API where appropriate.supabase/functions/_backend/plugins/- Plugin API used by the@capgo/capacitor-updaterplugin running inside apps:updates- device update checks and bundle download flowstats- upload usage stats from deviceschannel_self- allow a device to opt into a channel (QA/debug)
supabase/functions/_backend/triggers/- Triggers & CRON for automated backend jobs (queue consumers, scheduled tasks, DB-triggered flows).
When self-hosting, you generally expose public + plugins. private should
stay internal and locked down. triggers runs automatically.
supabase/schemas/prod.sql is a schema dump of the production database. It is
generated via bun run schemas (or bun run schemas:local) and is meant for
reference/diffing, not as a source of truth. All actual schema changes live in
supabase/migrations/.
https://github.com/Cap-go/capacitor-updater/wiki/Capgo-Sandbox-App
All the following official plugins are already installed and pre-configured:
- Action Sheet - Provides access to native Action Sheets.
- App - Handles high level App state and events.
- App Launcher - Allows to check if an app can be opened and open it.
- Browser - Provides the ability to open an in-app browser and subscribe to browser events.
- Camera - Provides the ability to take a photo with the camera or choose an existing one from the photo album.
- Clipboard - Enables copy and pasting to/from the system clipboard.
- Device - Exposes internal information about the device, such as the model and operating system version, along with user information such as unique ids.
- Dialog - Provides methods for triggering native dialog windows for alerts, confirmations, and input prompts.
- Filesystem - Provides a NodeJS-like API for working with files on the device.
- Geolocation - Provides simple methods for getting and tracking the current position of the device using GPS, along with altitude, heading, and speed information if available.
- Haptics - Provides physical feedback to the user through touch or vibration.
- Keyboard - Provides keyboard display and visibility control, along with event tracking when the keyboard shows and hides.
- Local Notifications - Provides a way to schedule device notifications locally (i.e. without a server sending push notifications).
- Motion - Tracks accelerometer and device orientation (compass heading, etc.).
- Network - Provides network and connectivity information.
- Push Notifications - Provides access to native push notifications.
- Screen Reader - Provides access to TalkBack/VoiceOver/etc. and Provides simple text-to-speech capabilities for visual accessibility.
- Share - Provides methods for sharing content in any sharing-enabled apps the user may have installed.
- Splash Screen - Provides methods for showing or hiding a Splash image.
- Status Bar - Provides methods for configuring the style of the Status Bar, along with showing or hiding it.
- Storage - Provides a simple key/value persistent store for lightweight data.
- Text Zoom - Provides the ability to change Web View text size for visual accessibility.
- Toast - Provides a notification pop up for displaying important information to a user. Just like real toast!
Tests are split by backend (API/plugin), CLI, database SQL, and frontend:
tests/- Backend Vitest tests (API + plugin + CLI)playwright/e2e/- Frontend Playwright testssupabase/tests/- SQL tests for functions, RLS, and DB logic
Backend test groups (Vitest):
- API tests: public/private endpoints and general backend behavior
- Plugin tests:
tests/updates*.test.ts,tests/stats*.test.ts,tests/channel_self*.test.ts - CLI tests:
tests/cli*.test.ts(CLI auth, upload, metadata, etc.)
Run tests:
# Supabase Edge Functions (default)
bun test:all
bun test:backend
bun test:cli
bun test:local
bun test:front
# Database SQL tests (Supabase CLI)
supabase test db
# Cloudflare Workers
bun test:cloudflare:all
bun test:cloudflare:backend
bun test:cloudflare:updates
# Local Cloudflare Workers (required for cloudflare tests)
./scripts/start-cloudflare-workers.shNotes:
- Tests run in parallel across files. If a test mutates shared data, add
dedicated seed data in
supabase/seed.sql. LOCAL_CLI_PATH=true bun test:all:localuses a local CLI build.- SQL tests in
supabase/tests/are run by the Supabase CLI test runner. - Run
supabase startfirst so the local DB is available.
- Use Composition API with
<script setup>SFC syntax - ESLint with @antfu/eslint-config, single quotes, no semi.
- TypeScript
- bun - fast javascript runtime, package manager, bundler, test runner an all-in-one toolkit
- critters - Critical CSS
- Cloudflare - zero-config deployment
- VS Code Extensions
- Vite - Fire up Vite server automatically
- Volar -
Vue 3
<script setup>IDE support - Iconify IntelliSense - Icon inline display and autocomplete
- i18n Ally - All in one i18n support
- Windi CSS Intellisense - IDE support for Windi CSS
- ESLint
Use the CLI to deploy to preprod
bun run dev-build
# then deploy
bun run deploy:cloudflare:console:preprodor Prod
bun run build
# then deploy
bun run deploy:cloudflare:console:prodYou will need to start each local server in separate terminals.
Before continuing, ensure you have the following installed:
You can install the supabase CLI globally with bun install supabase -g and
you'll be able to invoke supabase from anywhere.
Alternatively, you can install the supabase CLI locally with
bun install supabase --save-dev but, to invoke it, you have to use:
./node_modules/supabase/bin/supabase.
In the following guideline, we will assume that you have installed the
supabase CLI globally.
Start the Supabase DB:
supabase startIf the command is completed successfully, your console should output:
Started supabase local development setup.
API URL: http://localhost:54321
GraphQL URL: http://localhost:54321/graphql/v1
DB URL: postgresql://postgres:postgres@localhost:54322/postgres
Studio URL: http://localhost:54323
Inbucket URL: http://localhost:54324
JWT secret: super-secret-jwt-token-with-at-least-32-characters-long
anon key: xxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXx.xxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXx
service_role key: xxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXx.xxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxXxxxxxXxxxxxX[!WARNING]
supabase db resetbun install
bun serve:localVisit http://localhost:5173
There are two login credentials you can use:
| Account | Username | Password |
|---|---|---|
| Demo User | [email protected] | testtest |
| Admin User | [email protected] | adminadmin |
The demo user account has some demo data in it. If the data is not fresh, just
reset the db with supabase db reset. The seed has been made in a way that
ensures the data is always fresh.
The admin user account has administration rights so the user can impersonate other users. You can find the interface for that in the "Account" section.
[!WARNING]
This will seed the DB with demo data.
supabase db resetTo deploy the supabase instance in self-hosted, use the Supabase official guide.
Before deploying, duplicate supabase/functions/.env.example to
supabase/functions/.env, replace the placeholder values with your
self-hosted credentials, and keep the file local (it is gitignored). Use that
file for commands such as
supabase secrets set --env-file supabase/functions/.env.
To deploy the Supabase instance on cloud, you need a paid account, which costs $25/month.
Link the project to the cloud with the following command:
supabase linkhttps://supabase.com/docs/reference/cli/supabase-link
Then you need to push the migrations to the cloud with the following command:
supabase db push --linkedhttps://supabase.com/docs/reference/cli/supabase-migration-up
And seed the DB with demo data:
supabase seed bucketshttps://supabase.com/docs/reference/cli/supabase-seed-buckets
Seed the secret for functions:
supabase secrets set --env-file supabase/functions/.envPush the functions to the cloud:
supabase functions deployBy default, the configuration uses Capgo production values from configs.json. For self-hosted deployments, you must override all configuration values using environment variables.
All configuration keys from configs.json can be overridden by setting their uppercase equivalent as environment variables:
| Environment Variable | Description | Default (Prod) | Required for Self-Hosted |
|---|---|---|---|
BASE_DOMAIN |
Console domain | console.capgo.app |
✅ Yes |
SUPA_ANON |
Supabase anonymous key | Capgo production key | ✅ Yes |
SUPA_URL |
Supabase URL | https://xvwzpoazmxkqosrdewyv.supabase.co |
✅ Yes |
API_DOMAIN |
API domain | api.capgo.app |
✅ Yes |
CAPTCHA_KEY |
Turnstile captcha key | Capgo production key |
# .env file for self-hosted deployment
BASE_DOMAIN=console.yourdomain.com
SUPA_ANON=your-supabase-anon-key
SUPA_URL=https://your-supabase-url.supabase.co
API_DOMAIN=api.yourdomain.com
CAPTCHA_KEY=your-turnstile-keyThe configuration system (scripts/utils.mjs) checks for environment variables first:
- If an uppercase environment variable exists (e.g.,
SUPA_URL), it uses that value - Otherwise, it falls back to the appropriate value from
configs.jsonbased on the branch (prod,preprod,development, orlocal)
Important: Without setting these environment variables, your self-hosted instance will attempt to connect to Capgo's production infrastructure, which will fail.
To build the web app in mobile, in order to push to mobile stores, run:
bun install
bun mobileAnd you will see the generated files in the dist directory, ready to be served
on stores.