Skip to main content
The auto-update system keeps desktop (Tauri) and Android (sideloaded APK) clients up to date. Desktop uses a Cloudflare Worker that proxies GitHub Releases from the private repo. Android uses an Odoo backend endpoint.

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

The Worker:
  1. Fetches the latest release from the GitHub API using a server-side PAT
  2. Maps installer filenames to Tauri platform keys (e.g. *_x64-setup.exewindows-x86_64)
  3. Resolves pre-signed CDN download URLs for each asset (no auth needed to download)
  4. Reads .sig file contents inline
  5. 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 from tauri.conf.json:

Fallback endpoint

If the Worker endpoint is unreachable, the desktop client falls back to a dynamic Odoo endpoint (if POS_API_BASE_URL is configured):
The fallback only runs when the primary check throws a network error — not when it succeeds with no update available.

Android update endpoint

Android uses the Odoo endpoint directly (the Tauri updater plugin does not support mobile):
This endpoint is public (no JWT auth) because the client may not have valid tokens yet at launch.

Release workflow

Publishing a new version

  1. Bump version.json and run npm run version:sync
  2. Commit and tag: git tag v1.1.0 && git push origin main --tags
  3. CI builds, signs, and creates a draft GitHub Release
  4. Review the draft on GitHub, edit release notes, then Publish
  5. The Worker picks up the new release within 5 minutes (cache TTL)
  6. Existing installations detect the update on next launch
See AUTO_UPDATE_GUIDE.md for the full step-by-step release process and checklist.

CI configuration

The release.yml workflow:
  • Validates (Ubuntu): lint, typecheck, unit tests
  • Builds (Windows): Tauri NSIS installer with tauri-apps/tauri-action
  • Signs with TAURI_SIGNING_PRIVATE_KEY from GitHub secrets
  • Uploads installer + signature to GitHub Release
A separate 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:
The public key is embedded in tauri.conf.json under plugins.updater.pubkey. The private key is stored as a GitHub secret for CI builds.
Never commit the private key (~/.tauri/nupos.key) to the repository. Only the public key in tauri.conf.json is safe to commit.
The public key in tauri.conf.json must match the private key in the TAURI_SIGNING_PRIVATE_KEY GitHub secret. If you regenerate keys, update both — a mismatch causes “The signature was created with a different key than the one provided” errors on the client.
Set signing secrets via CLI (gh secret set TAURI_SIGNING_PRIVATE_KEY < ~/.tauri/nupos.key), not the GitHub web UI. The web UI can corrupt base64 values during copy-paste.

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 in workers/update-proxy/ in the repo:

Endpoints

Caching

The Worker returns Cache-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_version system 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

Covers semver parsing, version comparison, and platform detection.

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_version system parameter in Odoo acts as a backstop — clients below that version get a CLIENT_OUTDATED error after login
  • Signature verification failures on desktop prevent installation (security requirement)
  • Android download failures are handled by the system browser, not the app

File map