Auto-Update System Guide
This guide covers the full setup and operation of the NuPOS auto-update mechanism for desktop and Android clients.Architecture Overview
reqwest) strips Authorization headers on cross-origin redirects to GitHub’s CDN. The Worker solves this by resolving pre-signed CDN URLs server-side — no secrets in the app binary.
Android (sideloaded): Frontend JS calls the Odoo backend endpoint, then opens the APK URL in the system browser for download and install.
Web: No update mechanism — deploy new static files to your server.
1. Initial Setup (One-Time)
1.1 Generate a Signing Keypair
The signing key ensures update integrity — Tauri will not apply unsigned updates. A keypair is already generated (public key is intauri.conf.json). If you need a new one:
~/.tauri/nupos.key— private key (NEVER commit this)~/.tauri/nupos.key.pub— public key (goes intotauri.conf.json)
1.2 Configure GitHub Repository Secrets
Set secrets via the CLI (not the web UI, which can corrupt base64 values):GITHUB_TOKEN is provided automatically by GitHub Actions — do not add it manually.
1.3 Configure the Cloudflare Worker
The Worker is already deployed athttps://pos-update-proxy.jespinal.workers.dev. If you need to redeploy or reconfigure:
GITHUB_PAT must be a fine-grained GitHub PAT with Contents: read permission on the repo.
1.4 Verify tauri.conf.json Configuration
The following should already be set innu_pos_react/src-tauri/tauri.conf.json:
plugins.updater.pubkey with the contents of the new .pub file. A mismatch causes “The signature was created with a different key than the one provided” errors.
2. Release Process
Step-by-Step
2a. Bump the Version
Editversion.json at the repo root:
desktop:dev and desktop:build, but run it manually if you want to verify or commit the stamped files.
2b. Commit and Tag
2c. GitHub Actions Runs Automatically
Therelease.yml workflow triggers on the v* tag push. It:
- Validates (Ubuntu): lint, typecheck, unit tests
- Builds (Windows): Compiles the Tauri app with NSIS installer, signs it, creates a draft GitHub Release with:
NuPOS-Restaurant_1.1.0_x64-setup.exe— NSIS installerNuPOS-Restaurant_1.1.0_x64-setup.exe.sig— Ed25519 signatureNuPOS-Restaurant_1.1.0_x64_en-US.msi— MSI installerNuPOS-Restaurant_1.1.0_x64_en-US.msi.sig— MSI signature
2d. Review and Publish the Release
- Go to GitHub → Releases — you’ll see a new Draft release
- Edit the release notes if needed (add changelog, known issues, etc.)
- Click Publish release
2e. Clients Detect the Update
The Worker caches responses for 5 minutes. After publishing, wait up to 5 minutes for the Worker cache to expire, then existing POS installations will detect the update on next launch.Manual Trigger (Testing)
You can also trigger the build manually via GitHub Actions → Release POS → Run workflow, without pushing a tag. This is useful for testing the CI pipeline.3. How the Update Flow Works
On App Launch
Download Flow (Private Repo)
The Worker solves the private repo download problem:- Worker calls GitHub API
/releases/assets/{id}with PAT andAccept: application/octet-stream, usingredirect: "manual" - GitHub returns 302 with a pre-signed CDN URL in the
Locationheader - Worker returns this pre-signed URL in the Tauri JSON response
- Tauri downloads directly from the pre-signed CDN URL — no auth headers needed
Fallback Behavior
If the Worker endpoint is unreachable (no internet, DNS failure, etc.):- If
POS_API_BASE_URLis set, falls back to the Odoo update endpoint - If that also fails, shows a brief error with “Continue Anyway”
- The POS never gets stuck — operators can always reach the order screen
- The
min_client_versionOdoo system parameter serves as a hard backstop at bootstrap time
4. CI Cost Budget
Per-Release (Windows Only)
First build may take ~15-20 real minutes (no Rust cache). Subsequent builds benefit from the
cache-warm.yml workflow that runs cargo check on main pushes to warm the Rust cache.
Budget: 2,000 free minutes / 18 per release ≈ ~110 releases/month — effectively unlimited.
The release workflow only triggers on tag pushes (v*), so normal development pushes consume zero release CI minutes.
Build performance optimizations
- Rust cache warming:
cache-warm.ymlruns on main pushes (when Tauri source changes), saving the Rust compilation cache so tag-triggered release builds can restore from it - LLD linker:
RUSTFLAGS: "-C link-arg=-fuse-ld=lld"for faster linking cache-on-failure: true: Saves partial build cache even on failures
5. Signing Key Management
Key Rotation
If the private key is compromised:- Generate a new keypair:
npx @tauri-apps/cli signer generate -w ~/.tauri/nupos-v2.key - Update
tauri.conf.jsonwith the new public key - Update the GitHub secrets via CLI:
gh secret set TAURI_SIGNING_PRIVATE_KEY < ~/.tauri/nupos-v2.key - Build and ship a release signed with the old key (so existing clients can verify it)
- That release now contains the new public key in its binary
- All subsequent releases are signed with the new private key
6. Android Update Strategy
Thetauri-plugin-updater does not support Android. Android updates use a separate path:
- The Odoo backend serves update check responses at
GET /pos-api/v1/updates/check/android/aarch64/{version} - On launch, the frontend calls this endpoint and compares versions
- If an update is available, the same blocking overlay appears
- “Download Update” opens the APK URL in the system browser
- Requires “Install from unknown sources” enabled on the tablet (one-time setup)
Adding Android APK to Releases
To also distribute Android APKs via GitHub Releases:- Build the APK:
cd nu_pos_react && npm run tauri android build - Attach the APK to the same GitHub Release as the Windows artifacts
- Update the Odoo release record to point to the GitHub Release APK URL
7. Testing the Updater Locally
Without GitHub Releases
You can test the update flow with a local HTTP server:- Build the app with version
0.0.1(set inversion.json) - Create a
latest.jsonfor version0.0.2:
- Serve the directory:
npx serve -l 8080 ./test-update-server - Temporarily change the endpoint in
tauri.conf.jsontohttp://localhost:8080/latest.json - Run the
0.0.1build — it should detect and offer the0.0.2update
Verifying the Worker
After publishing a release, verify the Worker endpoint:url fields should be pre-signed CDN URLs (not api.github.com URLs).
Health check
8. Troubleshooting
Client doesn’t check for updates
- Web: Expected — web has no update mechanism.
- Desktop: Verify the app is a Tauri build (check window title shows version). The plugin reads endpoints from
tauri.conf.jsonwhich is baked into the binary at build time. - Android: Verify
POS_API_BASE_URLis set and the Odoo endpoint is reachable from the device.
Worker returns 204 (no releases)
- Check the GitHub Release is published (not draft)
- Check the
GITHUB_PATsecret is set:cd workers/update-proxy && wrangler secret list - Check the PAT has
Contents: readpermission on the repo
Worker returns 502
- The GitHub API returned an error. Check the PAT hasn’t expired.
- Verify with:
curl -s -H "Authorization: token <PAT>" https://api.github.com/repos/josespinal/rost_pos_restaurant/releases/latest
Update found but download fails
- Pre-signed URLs expire in ~10 minutes. If the user waited too long, retry the update check.
- Check the release has both
.exeand.sigfiles uploaded
Signature verification fails
- The public key in
tauri.conf.jsonmust match the private key in theTAURI_SIGNING_PRIVATE_KEYGitHub secret - If you regenerated keys, both must be updated together
- Set secrets via CLI (
gh secret set), not the web UI (which can corrupt base64) - Don’t manually re-upload a modified binary without re-signing
CI build fails
- Missing secrets: Verify
TAURI_SIGNING_PRIVATE_KEYandTAURI_SIGNING_PRIVATE_KEY_PASSWORDare set in GitHub repo settings - Rust compilation errors: Check Cargo.toml dependencies match the Tauri version
- Version mismatch: Ensure
version.json,tauri.conf.json,Cargo.toml, andpackage.jsonall have the same version (runnpm run version:sync)
“Later” defers but update appears every launch
This is by design. The operator can defer with “Later”, but the update check runs on every app launch. Once an update is available, the overlay will appear each time until the update is installed. During download/install, the overlay cannot be dismissed.Android download doesn’t trigger install
- Verify
REQUEST_INSTALL_PACKAGESpermission is inAndroidManifest.xml - The user may need to enable “Install from unknown sources” in Android settings
- Some Android versions require: Settings → Apps → Browser → Install unknown apps
9. Rollback
Tauri does not include a built-in rollback mechanism. If a bad update is deployed:- Fastest: Publish a new release with the fix (bump patch version)
- Manual: Reinstall the previous version from the GitHub Releases page
- Prevention: Use
releaseDraft: truein CI (current setting) to review builds before publishing
10. Worker Proxy Reference
Source
workers/update-proxy/ in the repo root.
Configuration
wrangler.toml:
Platform key mapping
The Worker maps release asset filenames to Tauri platform keys:
Assets without a matching
.sig file are excluded from the response (Tauri requires signatures).
Deploying Worker changes
11. File Reference
12. Release Checklist
- Bump
version.jsonto the new version - Run
npm run version:sync(or let the CI do it from the tag) - Commit:
git commit -m "release: vX.Y.Z" - Tag:
git tag vX.Y.Z - Push:
git push origin main --tags - Wait for CI to complete (check GitHub Actions)
- Review the draft release on GitHub → edit notes → Publish
- Verify Worker:
curl -s https://pos-update-proxy.jespinal.workers.dev/update/check | python3 -m json.tool - (Optional) Update
nu_pos.min_client_versionin Odoo to enforce the minimum version - (Android) Create/update Odoo release record with APK URL if shipping Android update