This is an internal-use fork of zhom/donutbrowser maintained for self-hosted team deployments. It strips every cloud-subscription touchpoint from the upstream build โ no commercial trial modal, no cloud control plane, no Pro feature gates โ and replaces them with a single self-hosted donut-sync server that you run yourself.
If you want the upstream commercial product (paid cloud sync, location proxies, BotBrowser fingerprint tokens), use zhom/donutbrowser directly. This fork is for teams that prefer to own their own infrastructure end-to-end.
AGPL-3.0 notice: all derivatives (including this fork) must stay open source under the same license. The CloakBrowser Chromium runtime is a separate proprietary asset and is not included in this repo or in any binary release โ you fetch it yourself (see Cloak runtime below).
- What's different from upstream
- Features
- Quick start (running it)
- Self-hosted
donut-syncserver - Cloak Chromium runtime
- Build the desktop app from source
- Technical architecture
- Development
- License
| Area | Upstream zhom/donutbrowser |
This fork |
|---|---|---|
| Cloud control plane | Paid cloud API for subscription, location proxies, Wayfern token | Stubbed โ cloud_auth.rs returns no-ops, all gating predicates pass. No external HTTP calls. |
| Commercial trial | Trial-expiration modal counts down on every launch | Removed โ commercial_license.rs, commercial-trial-modal.tsx, 12 i18n keys ร 7 locales gone |
| Pro feature gating | "PRO" badges on cross-OS profiles, advanced fingerprint fields, encrypted sync, extensions, sync tabs | Removed โ crossOsUnlocked / syncUnlocked / limitedMode / canUseEncryption props ripped from all 12 components; ProBadge component deleted |
| Cloud login | Device-code OAuth flow against donutbrowser.com |
Removed โ device-code-verify-dialog.tsx deleted, sync dialog is self-hosted-only |
| Sync server | Single shared bearer token, plain file blobs | JWT + multi-user team mode by default โ Postgres-backed users / teams / permissions / locks / audit log; S3/MinIO for profile bundles |
| CloakBrowser runtime | Bundled in private internal builds only | Not in repo, not in releases โ you drop the CloakHQ release tarball into vendor-private/cloakbrowser/<platform>/ before building |
| Sync token decryption | Hard-fails launch if DONUT_BROWSER_VAULT_PASSWORD changes between builds |
Self-healing โ corrupt / undecryptable token is logged + discarded, app degrades to "logged out" instead of bricking |
| Team-lock acquisition on launch | Hard-fails launch if server is unreachable | Best-effort โ logs warning, launches locally anyway |
| Pro-only preflight on launch | Runs for all sync-enabled engines (Cloak/Wayfern blocked when server disagrees) | BotBrowser only โ Cloak/Wayfern launch locally without server roundtrip |
In code, the cleanest way to see the diff is git log --oneline c0d1b8f^..HEAD โ every commit since the fork started has a self-contained explanation in the message body.
The fork keeps every feature of upstream Donut Browser, just without the commercial gating:
- Unlimited browser profiles โ each fully isolated with its own fingerprint, cookies, extensions, storage
- Four engines โ Wayfern (Chromium, default), Cloak (private Chromium with deeper fingerprint patches), Camoufox (Firefox), BotBrowser (legacy compat)
- Per-profile proxies โ HTTP, HTTPS, SOCKS4, SOCKS5, dynamic URLs, optional WireGuard VPN per profile
- Self-hosted team sync โ Postgres + S3/MinIO backend, JWT auth, permissions (owner/editor/viewer), profile locks with heartbeat, audit log
- Local API & MCP โ REST API and Model Context Protocol server for Claude / automation / custom scripts
- Profile groups, templates, bulk launch/stop, scheduled tasks, webhooks, cookie snapshots, proxy failover, profile compare โ every optimization from the recent functional pass is in
You need two moving parts:
- A self-hosted
donut-syncserver (Postgres + MinIO + Nest API behind a reverse proxy) - The Donut desktop app built from this repo (you build it once per platform)
cd donut-sync
cp .env.example .env # then edit ADMIN_EMAIL / ADMIN_PASSWORD / JWT_SECRET
docker compose up -dThe default docker-compose.yml runs Postgres + MinIO + the NestJS API bound to 127.0.0.1:12342 and MinIO on 127.0.0.1:8987. For production you put a reverse proxy (Caddy / Nginx / ๅฎๅก) in front to terminate TLS. Full instructions live in docs/self-hosting-donut-sync.md and docs/team-deployment-guide.md.
pnpm install
# optional but recommended โ bake your own vault password so a rebuild
# doesn't invalidate existing on-disk encrypted tokens
export DONUT_BROWSER_VAULT_PASSWORD="$(openssl rand -hex 32)"
pnpm tauri buildThe bundled .app lands at src-tauri/target/release/bundle/macos/Donut.app. On macOS without an Apple Developer ID:
# ad-hoc sign so Gatekeeper allows local launch
codesign --force --deep --sign - src-tauri/target/release/bundle/macos/Donut.app
cp -R src-tauri/target/release/bundle/macos/Donut.app /Applications/Linux / Windows packaging mirrors upstream โ see docs/team-deployment-guide.md.
- Right-side dropdown menu (
ยทยทยท) โ ่ดฆๆท (Sync Service) - Server URL: your sync server URL (e.g.
https://sync.example.com) - Email + Password: the admin account you set in step 1 โ additional users are created from ๅข้็ฎก็ (Team Admin) once logged in
- Save โ the team menu (
ๅข้็ฎก็/ๅ ฑไบซ Profiles/ๅไบซ profile) appears once you're authenticated
Three components:
โโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Donut desktop app โโโโโถโ donut- โโโโโถโ Postgres โ
โ (macOS / Win / Lx) โ โ sync API โ โ users, teams, profiles, โ
โโโโโโโโโโโโโโโโโโโโโโ โ (NestJS) โ โ permissions, locks, โ
โ โ โ audit log โ
โ JWT โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ auth โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโคโโโโถโ S3 / MinIO โ
โ โ profile bundles, โ
โ โ Cookie snapshots, โ
โ โ BotBrowser .enc assets, โ
โ โ extensions, tombstones โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โผ
(presigned upload/download URLs
so desktop pushes/pulls big
blobs directly to S3)
Required env vars (full list in docs/self-hosting-donut-sync.md):
MULTI_USER_ENABLED=true # team JWT mode (recommended)
DATABASE_URL=postgresql://...
JWT_SECRET=<random 32+ bytes>
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=<initial admin password>
S3_ENDPOINT=http://minio:9000 # server-to-S3
S3_PUBLIC_ENDPOINT=https://s3... # client-to-S3 via presign
S3_ACCESS_KEY_ID=...
S3_SECRET_ACCESS_KEY=...
S3_BUCKET=donut-syncThe legacy single-token mode (MULTI_USER_ENABLED=false + SYNC_TOKEN) still works but team features and the new UI assume JWT mode.
Cloak is the optional anti-detect Chromium engine. It is not included in this repo or in any binary you'd get from this fork โ it's a separately-maintained Chromium build.
Quick path (macOS Apple Silicon):
mkdir -p vendor-private/cloakbrowser/macos-aarch64
curl -fL -o /tmp/cloak.tgz \
https://github.com/CloakHQ/CloakBrowser/releases/download/chromium-v142.0.7444.175/cloakbrowser-darwin-arm64.tar.gz
tar -xzf /tmp/cloak.tgz -C vendor-private/cloakbrowser/macos-aarch64
pnpm tauri build # the binary gets copied into the .app bundleFor other platforms (Intel macOS, Linux x64, Windows x64) check the CloakHQ releases page for the matching cloakbrowser-<platform>.tar.gz. The platform folder names this fork looks for are:
vendor-private/cloakbrowser/macos-aarch64/ โ Apple Silicon
vendor-private/cloakbrowser/macos-x64/ โ Intel Mac
vendor-private/cloakbrowser/linux-x64/ โ Linux 64-bit
vendor-private/cloakbrowser/windows-x64/ โ Windows 64-bit
vendor-private/ is gitignored โ don't commit the binary. If you skip this step Cloak profiles will fail to launch with "CloakBrowser runtime was not found"; Wayfern, Camoufox, BotBrowser profiles are unaffected.
Note: CloakHQ's public macOS release lags Linux. As of writing, macOS arm64 is on Chromium 142, Linux on 146.
pnpm install # JS + Rust deps + Tauri sidecars
pnpm tauri dev # hot-reload dev mode
pnpm tauri build # release .app / .dmg / .deb / .msiQuality gate before committing (the pre-commit hook runs this automatically):
pnpm format && pnpm lint && pnpm testRuns typos, biome, cargo fmt, cargo clippy --all-targets -- -D warnings, the 459-test Rust unit suite, sync e2e (15), integration (15 + 14), and Jest for donut-sync. Adding a Tauri command requires at least one frontend invoke() call โ test_no_unused_tauri_commands enforces that.
See CONTRIBUTING.md for the upstream contribution flow if you intend to send patches upstream too.
Brief โ see docs/architecture.md for full diagrams + control flow.
- Frontend: Next.js 16 (App Router, React 19, Turbopack), Tailwind, biome lint, i18next (en / es / fr / ja / pt / ru / zh). UI in
src/components/, page entry insrc/app/page.tsx. - Backend: Tauri 2 (Rust), ~100 Tauri commands registered in
src-tauri/src/lib.rs. Each browser engine (browser_runner.rs,cloakbrowser.rs,wayfern_manager.rs,camoufox_manager.rs,botbrowser.rs) handles its own launch flags, fingerprint injection, and profile data path. - Sync engine:
src-tauri/src/sync/engine.rsruns delta sync (S3 manifests + per-file SHA256), profile-bundle round-trip, encrypted sync (Argon2 + AES-GCM with optional E2E password), scheduled background sync. - Team mode:
src-tauri/src/self_hosted_auth.rscaches JWT + user JSON,self_hosted_team.rswraps REST calls,team_lock.rsmanages distributed profile locks with heartbeat. AllCLOUD_AUTH.*predicates in the rest of the code are stubs that return permissive defaults so the team-mode call sites never have to special-case "no cloud". - Vault encryption: long-lived secrets (sync token, settings) are encrypted at rest with Argon2id + AES-256-GCM using
env!("DONUT_BROWSER_VAULT_PASSWORD")baked at compile time.build.rsnow setsrerun-if-env-changedso cargo actually rebuilds when the env changes, andget_sync_tokenself-heals if the password drifts (logs, discards, returnsNone).
- AGENTS.md (also
CLAUDE.mdโ same file) โ repository structure, lint rules, theming rules, i18n rules, security guardrails. Read this if you're sending patches. - CONTRIBUTING.md โ upstream contribution flow.
- docs/team-deployment-guide.md / .zh.md โ deployment runbook for VPS + ๅฎๅก / Caddy / Nginx setups.
- docs/team-current-feature-overview.md / .zh.md โ what the team mode actually does today.
- docs/team-self-hosted-botbrowser.md / .zh.md โ BotBrowser-specific runbook (legacy engine).
- docs/team-product-test-plan.md / .zh.md โ acceptance test plan.
AGPL-3.0 โ see LICENSE. Any derivative must stay open source under the same license. The Cloak Chromium runtime you place under vendor-private/cloakbrowser/ is a separate artifact governed by its own license from CloakHQ โ this repo only knows how to load it.
Upstream Donut Browser is ยฉ the original zhom/donutbrowser authors. This fork doesn't claim ownership of upstream work โ only of the changes documented in the commit history since c0d1b8f.

