Architecture
1
CI builds and publishes to GitHub Releases
The
release.yml workflow triggers on v* tag push, builds the signed NSIS installer, and creates a GitHub Release with the installer .exe and its .sig signature file.2
Client checks for updates on launch
An
UpdateGate component wraps the app and blocks rendering until the update check completes. On web, it passes through immediately.3
Worker proxies GitHub API
The Tauri updater plugin calls the Cloudflare Worker at
/update/check. The Worker authenticates with the GitHub API using a server-side PAT, fetches the latest release metadata, resolves pre-signed download URLs, and returns Tauri-format JSON.4
Desktop downloads and installs
Tauri downloads the installer from the pre-signed URL (no auth needed), verifies the Ed25519 signature, installs, and relaunches.
5
Android opens the APK URL
Android checks the Odoo endpoint via JS, then opens the APK download URL in the system browser for manual install.
Desktop update endpoint
The desktop client does not talk to GitHub directly. A Cloudflare Worker (workers/update-proxy/) acts as a proxy that handles authentication for the private repo.
How the Worker proxy works
- Fetches the latest release from the GitHub API using a server-side PAT
- Maps installer filenames to Tauri platform keys (e.g.
*_x64-setup.exe→windows-x86_64) - Resolves pre-signed CDN download URLs for each asset (no auth needed to download)
- Reads
.sigfile contents inline - Returns Tauri-format JSON with version, notes, platforms, and signatures
No PAT or auth token is embedded in the app binary. The Worker stores the GitHub PAT as a Cloudflare secret.
Endpoint URL
The updater plugin reads the endpoint fromtauri.conf.json:
Fallback endpoint
If the Worker endpoint is unreachable, the desktop client falls back to a dynamic Odoo endpoint (ifPOS_API_BASE_URL is configured):
Android update endpoint
Android uses the Odoo endpoint directly (the Tauri updater plugin does not support mobile):Release workflow
Publishing a new version
- Bump
version.jsonand runnpm run version:sync - Commit and tag:
git tag v1.1.0 && git push origin main --tags - CI builds, signs, and creates a draft GitHub Release
- Review the draft on GitHub, edit release notes, then Publish
- The Worker picks up the new release within 5 minutes (cache TTL)
- Existing installations detect the update on next launch
CI configuration
Therelease.yml workflow:
- Validates (Ubuntu): lint, typecheck, unit tests
- Builds (Windows): Tauri NSIS installer with
tauri-apps/tauri-action - Signs with
TAURI_SIGNING_PRIVATE_KEYfrom GitHub secrets - Uploads installer + signature to GitHub Release
cache-warm.yml workflow runs cargo check on main branch pushes to warm the Rust build cache for faster release builds.
Required GitHub secrets
Required Cloudflare secrets
Signing
Tauri requires Ed25519 signatures to verify update artifacts. Generate a signing keypair:tauri.conf.json under plugins.updater.pubkey. The private key is stored as a GitHub secret for CI builds.
Updater artifacts
tauri.conf.json has "createUpdaterArtifacts": true, which tells the bundler to generate signed .exe installers with companion .sig signature files during CI builds.
Worker proxy
Source code
The Worker lives inworkers/update-proxy/ in the repo:
Endpoints
Caching
The Worker returnsCache-Control: public, max-age=300 (5 minutes). After publishing a new release, it takes up to 5 minutes for the Worker to serve the new version.
Deploying changes
Frontend components
UpdateGate
src/features/updates/UpdateGate.tsx wraps the entire app outside AuthProvider. It:
- Detects the platform via
getPlatformInfo()from runtime config flags (IS_DESKTOP,IS_ANDROID) - On web: passes through immediately (no update check)
- On desktop: tries the Tauri updater plugin first (Worker proxy endpoint), falls back to the Odoo endpoint only if the plugin check fails
- On Android: calls the Odoo endpoint directly, then prompts to download
- On error: shows “Continue Anyway” (the
min_client_versionsystem parameter acts as a backstop)
UpdateScreen
src/features/updates/UpdateScreen.tsx is a full-screen blocking component with i18n support (updateScreen namespace in en.json/es.json):
The “Later” button defers the update — the overlay will return on next app launch. During download/install, the overlay cannot be dismissed.
Platform detection
src/lib/updater.ts exports getPlatformInfo() which reads window.__POS_RUNTIME_CONFIG__:
Testing
Manual testing checklist
1
Worker endpoint
Verify the Worker returns valid JSON:
2
Desktop
Run
npm run desktop:dev. Verify the update check runs on launch. If a newer version is published, the overlay should appear.3
Local test server
Build with version
0.0.1, create a fake update JSON for 0.0.2, serve with npx serve, and temporarily point the endpoint to localhost. See AUTO_UPDATE_GUIDE.md for details.4
Android
Run
npm run tauri android dev. Verify the “Download Update” prompt appears and opens the URL in the browser.5
Web
Run
npm run dev and verify the app loads normally with no update screen.6
Offline
Disconnect from network. Verify the error screen appears with “Continue Anyway”, and tapping it proceeds to the app.
Unit tests
Error handling
The update system is designed to be non-blocking on failure:- Network errors during the update check show an error screen with “Continue Anyway”
- The existing
min_client_versionsystem parameter in Odoo acts as a backstop — clients below that version get aCLIENT_OUTDATEDerror after login - Signature verification failures on desktop prevent installation (security requirement)
- Android download failures are handled by the system browser, not the app