Creating OTA Bundles
📦 Creating OTA Bundles
Section titled “📦 Creating OTA Bundles”This guide walks through building, signing, and publishing frontend bundles for hotswap delivery.
1. Build your frontend
Section titled “1. Build your frontend”pnpm build# or: npm run build, yarn build, etc.This produces your dist/ or build/ directory — the same output that Tauri embeds via frontendDist.
2. Create the archive
Section titled “2. Create the archive”tar.gz (default, no extra features needed)
Section titled “tar.gz (default, no extra features needed)”tar -czf frontend.tar.gz -C dist .zip (requires features = ["zip"])
Section titled “zip (requires features = ["zip"])”cd dist && zip -r ../frontend.zip . && cd ..⚠️ The archive root should contain
index.htmldirectly — not a subdirectory. Use-C dist .(not-C . dist).Note:
tar -C dist .produces entries with a./prefix (e.g../index.html). This is fine — the plugin strips leading./components during extraction. What is rejected: absolute paths and..path traversal.
3. Sign the bundle
Section titled “3. Sign the bundle”Use the same signing key you generated for the plugin (see Security > Signing Guide).
# Using the Tauri CLIpnpm tauri signer sign frontend.tar.gz -k ~/.tauri/hotswap.key
# Or with minisign directlyminisign -Sm frontend.tar.gz -s ~/.tauri/hotswap.keyThis creates frontend.tar.gz.sig. Read its contents — this is the signature field in your manifest.
# Read the signaturecat frontend.tar.gz.sig4. Upload to your CDN
Section titled “4. Upload to your CDN”Upload both files:
# AWS S3aws s3 cp frontend.tar.gz s3://my-bucket/updates/v0.1.0-ota.3/aws s3 cp frontend.tar.gz.sig s3://my-bucket/updates/v0.1.0-ota.3/
# Or any HTTPS-accessible host5. Publish the manifest
Section titled “5. Publish the manifest”Make your update endpoint return the manifest pointing to the uploaded bundle:
{ "version": "0.1.0-ota.3", "sequence": 43, "min_binary_version": "0.1.0", "url": "https://cdn.example.com/updates/v0.1.0-ota.3/frontend.tar.gz", "signature": "untrusted comment: signature from minisign secret key\nRWQ...<base64>...", "notes": "Fixed dark mode colors", "bundle_size": 2097152}See the full Server Contract for details.
CI/CD Example (GitHub Actions)
Section titled “CI/CD Example (GitHub Actions)”name: OTA Release
on: workflow_dispatch: inputs: version: description: 'OTA version (e.g. 0.1.0-ota.3)' required: true
jobs: release: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- uses: actions/setup-node@v4 with: node-version: 20
- run: npm ci - run: npm run build
- name: Create bundle run: tar -czf frontend.tar.gz -C dist .
- name: Sign bundle run: | echo "${{ secrets.HOTSWAP_PRIVATE_KEY }}" > /tmp/hotswap.key npx @tauri-apps/cli signer sign frontend.tar.gz -k /tmp/hotswap.key rm /tmp/hotswap.key
- name: Upload to S3 run: | aws s3 cp frontend.tar.gz s3://my-bucket/updates/v${{ inputs.version }}/ aws s3 cp frontend.tar.gz.sig s3://my-bucket/updates/v${{ inputs.version }}/
- name: Publish manifest run: | SIGNATURE=$(cat frontend.tar.gz.sig) SIZE=$(stat -f%z frontend.tar.gz 2>/dev/null || stat -c%s frontend.tar.gz) curl -X POST https://api.example.com/updates \ -H "Authorization: Bearer ${{ secrets.API_TOKEN }}" \ -H "Content-Type: application/json" \ -d "{ \"version\": \"${{ inputs.version }}\", \"sequence\": ${{ github.run_number }}, \"min_binary_version\": \"0.1.0\", \"url\": \"https://cdn.example.com/updates/v${{ inputs.version }}/frontend.tar.gz\", \"signature\": $(echo "$SIGNATURE" | jq -Rs .), \"bundle_size\": $SIZE }"💡 Using
${{ github.run_number }}as the sequence is a simple way to get a monotonically increasing counter.
Versioning Convention
Section titled “Versioning Convention”| Event | Version | Sequence |
|---|---|---|
Binary release 0.1.0 | — | — |
| First OTA hotfix | 0.1.0-ota.1 | 1 |
| Second OTA hotfix | 0.1.0-ota.2 | 2 |
Binary release 0.2.0 | — | — |
First OTA for 0.2.0 | 0.2.0-ota.1 | 3 |
Sequences are global and always increase. The version field is for display only.