Claude-skill-registry Auto-Update Systems Expert
Expert in Tauri auto-update implementation with focus on signature verification, rollback mechanisms, staged rollouts, and secure update distribution
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/auto-update-systems" ~/.claude/skills/majiayu000-claude-skill-registry-auto-update-systems-expert && rm -rf "$T"
skills/data/auto-update-systems/SKILL.mdAuto-Update Systems Expert
0. Mandatory Reading Protocol
CRITICAL: Before implementing, read these reference files:
| Reference | When to Read |
|---|---|
| Signing keys, signature verification, secure endpoints |
| Staged rollouts, rollback, update channels, differential updates |
| Security posture, MITM defense, key rotation |
1. Overview
Risk Level: HIGH
Justification: Auto-update systems can deliver code to all users simultaneously. A compromised update system can distribute malware to the entire user base. Signature verification bypass (like CVE-2024-39698) allows attackers to install unsigned malicious updates. Poor rollback mechanisms can leave users with broken software.
You are an expert in auto-update system implementation, specializing in:
- Signature verification for cryptographic update integrity
- Rollback mechanisms for failed updates
- Staged rollouts for risk mitigation
- Secure distribution with HTTPS and pinning
- Tauri updater configuration and best practices
Primary Use Cases
- Tauri application auto-updates
- Secure update distribution infrastructure
- Update channel management (stable, beta)
- Emergency rollback procedures
- Update analytics and monitoring
2. Core Responsibilities
2.1 Core Principles
- TDD First - Write tests before implementation code
- Performance Aware - Optimize for bandwidth and speed
- ALWAYS verify signatures - Never install unsigned updates
- Use HTTPS only - Never fetch updates over HTTP
- Implement rollback - Plan for failed updates
- Staged rollouts - Don't update all users at once
- Monitor update health - Track success rates and errors
2.2 Reliability Principles
- Atomic updates - All or nothing installation
- Preserve user data - Never lose configuration during updates
- Graceful degradation - App works if update fails
- User consent - Inform users before updating
3. Technical Foundation
3.1 Tauri Updater Components
| Component | Purpose |
|---|---|
| Update manifest | JSON with version, download URLs, signatures |
| Signing key | Ed25519 private key for signing updates |
| Public key | Embedded in app for verification |
| Update endpoint | HTTPS server hosting manifests and artifacts |
3.2 Version Recommendations
| Component | Recommended | Notes |
|---|---|---|
| Tauri | 1.5+ / 2.0+ | Latest security patches |
| Update protocol | v2 | Better signature handling |
4. Implementation Patterns
4.1 Tauri Updater Configuration
// tauri.conf.json { "tauri": { "updater": { "active": true, "dialog": true, "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6...", "endpoints": [ "https://releases.myapp.com/{{target}}/{{arch}}/{{current_version}}" ], "windows": { "installMode": "passive" } }, "bundle": { "createUpdaterArtifacts": true } } }
4.2 Update Manifest Format
{ "version": "1.2.0", "notes": "Bug fixes and performance improvements", "pub_date": "2024-01-15T12:00:00Z", "platforms": { "darwin-x86_64": { "signature": "dW50cnVzdGVkIGNvbW1lbnQ6...", "url": "https://releases.myapp.com/MyApp_1.2.0_x64.app.tar.gz" }, "windows-x86_64": { "signature": "dW50cnVzdGVkIGNvbW1lbnQ6...", "url": "https://releases.myapp.com/MyApp_1.2.0_x64-setup.nsis.zip" } } }
4.3 Custom Update Logic
use tauri::updater::UpdateResponse; use tauri::{AppHandle, Manager}; #[tauri::command] async fn check_for_updates(app: AppHandle) -> Result<Option<UpdateInfo>, String> { match app.updater().check().await { Ok(update) => { if update.is_update_available() { Ok(Some(UpdateInfo { version: update.latest_version().to_string(), notes: update.body().map(|s| s.to_string()), date: update.date().map(|d| d.to_string()), })) } else { Ok(None) } } Err(e) => Err(format!("Failed to check for updates: {}", e)), } } #[tauri::command] async fn install_update(app: AppHandle) -> Result<(), String> { let update = app.updater().check().await .map_err(|e| format!("Check failed: {}", e))?; if update.is_update_available() { // Download and verify signature update.download_and_install() .await .map_err(|e| format!("Install failed: {}", e))?; // Restart app to apply update app.restart(); } Ok(()) } #[derive(serde::Serialize)] struct UpdateInfo { version: String, notes: Option<String>, date: Option<String>, }
5. Security Standards
5.1 Domain Vulnerability Landscape
Research Date: November 2024
| CVE | Severity | Description | Mitigation |
|---|---|---|---|
| CVE-2024-39698 | High | electron-updater signature bypass | Update electron-builder 6.3.0+ |
| CVE-2024-24576 | High | Rust Command injection (affects Tauri shell) | Update Rust 1.77.2+ |
| CVE-2024-35222 | High | Tauri iFrame origin bypass | Update Tauri 1.6.7+/2.0.0-beta.20+ |
| CVE-2023-46115 | Medium | Tauri key leak via Vite config | Remove TAURI_ from envPrefix |
Key Insight: Signature verification bypass is the most critical vulnerability class. Always verify signatures are actually checked and cannot be bypassed.
5.2 OWASP Mapping
| OWASP Category | Risk Level | Key Controls |
|---|---|---|
| A02:2021 - Cryptographic Failures | Critical | Ed25519 signatures, HTTPS only |
| A05:2021 - Security Misconfiguration | High | Proper endpoint config, key management |
| A08:2021 - Software Integrity Failures | Critical | Signature verification, pinning |
5.3 Signature Verification
See
for complete implementationsreferences/security-examples.md
// Tauri handles signature verification automatically when configured correctly // The signature in the manifest is verified against the embedded public key // CRITICAL: Never bypass signature verification // CRITICAL: Always use HTTPS for update endpoints // CRITICAL: Protect the private signing key
6. Testing Standards
6.1 Update Testing
#[cfg(test)] mod tests { #[tokio::test] async fn test_update_check() { let mock_server = MockUpdateServer::new(); mock_server.set_latest_version("2.0.0"); let result = check_for_updates_from(&mock_server.url()).await; assert_eq!(result.unwrap().version, "2.0.0"); } #[tokio::test] async fn test_invalid_signature_rejected() { let mock_server = MockUpdateServer::new(); mock_server.set_invalid_signature(); assert!(install_update_from(&mock_server.url()).await.is_err()); } #[tokio::test] async fn test_downgrade_prevented() { let mock_server = MockUpdateServer::new(); mock_server.set_latest_version("0.9.0"); assert!(check_for_updates_from(&mock_server.url()).await.unwrap().is_none()); } }
7. Implementation Workflow (TDD)
Step 1: Write Failing Test First
# tests/test_update_system.py import pytest from unittest.mock import patch from update_manager import UpdateManager class TestUpdateManager: @pytest.fixture def manager(self): return UpdateManager(current_version="1.0.0", update_endpoint="https://updates.example.com") @pytest.mark.asyncio async def test_check_for_update_returns_info(self, manager): with patch.object(manager, '_fetch_manifest') as mock: mock.return_value = {"version": "2.0.0", "signature": "valid_sig"} result = await manager.check_for_update() assert result.version == "2.0.0" @pytest.mark.asyncio async def test_invalid_signature_rejected(self, manager): with patch.object(manager, '_verify_signature', return_value=False): with pytest.raises(SecurityError, match="signature"): await manager.download_and_verify("https://...", "bad_sig") @pytest.mark.asyncio async def test_rollback_on_install_failure(self, manager): with patch.object(manager, '_install', side_effect=InstallError): with patch.object(manager, '_restore_backup') as mock_restore: with pytest.raises(InstallError): await manager.install_update("/path/to/update") mock_restore.assert_called_once()
Step 2: Implement Minimum to Pass
# update_manager.py class UpdateManager: async def check_for_update(self) -> Optional[UpdateInfo]: manifest = await self._fetch_manifest() if self._is_newer(manifest["version"]): return UpdateInfo(**manifest) return None async def download_and_verify(self, url: str, signature: str) -> bytes: data = await self._download(url) if not self._verify_signature(data, signature): raise SecurityError("Invalid signature") return data
Step 3: Refactor and Optimize
Add delta updates, caching, and bandwidth management after tests pass.
Step 4: Verify
pytest tests/test_update_system.py -v --tb=short pytest tests/test_update_system.py --cov=update_manager --cov-report=term-missing pytest tests/test_update_system.py -k "signature or rollback" -v
8. Performance Patterns
8.1 Delta Updates
# Good: Download only changed bytes class DeltaUpdateManager: async def download_delta(self, from_version: str, to_version: str) -> bytes: delta_url = f"{self.endpoint}/deltas/{from_version}-{to_version}.patch" delta = await self._download(delta_url) return self._apply_delta(self.current_binary, delta) # Bad: Download full binary every time class FullUpdateManager: async def download_update(self, version: str) -> bytes: return await self._download(f"{self.endpoint}/full/{version}.tar.gz")
8.2 Background Downloads
# Good: Download in background without blocking UI class BackgroundDownloader: async def download_in_background(self, url: str) -> None: self._download_task = asyncio.create_task(self._download(url)) self._download_task.add_done_callback(self._on_download_complete) def get_progress(self) -> float: return self._bytes_downloaded / self._total_bytes # Bad: Blocking download that freezes application def download_blocking(url: str) -> bytes: return requests.get(url).content # Blocks entire app
8.3 Bandwidth Throttling
# Good: Respect user's bandwidth limits class ThrottledDownloader: def __init__(self, max_bytes_per_sec: int = 1_000_000): self.rate_limiter = RateLimiter(max_bytes_per_sec) async def download(self, url: str) -> bytes: chunks = [] async with aiohttp.ClientSession() as session: async with session.get(url) as response: async for chunk in response.content.iter_chunked(8192): await self.rate_limiter.acquire(len(chunk)) chunks.append(chunk) return b''.join(chunks) # Bad: Saturate user's connection async def download_unlimited(url: str) -> bytes: async with aiohttp.ClientSession() as session: return await (await session.get(url)).read()
8.4 Rollback Optimization
# Good: Keep only necessary backup data class SmartRollback: def create_backup(self) -> BackupHandle: # Only backup files that will be modified modified_files = self._get_files_to_update() return self._backup_files(modified_files) def cleanup_old_backups(self, keep_count: int = 2) -> None: backups = sorted(self._list_backups(), key=lambda b: b.date) for backup in backups[:-keep_count]: backup.delete() # Bad: Full backup every time class FullBackup: def create_backup(self) -> str: # Copies entire application directory return shutil.copytree(self.app_dir, f"{self.app_dir}.backup")
8.5 Signature Caching
# Good: Cache verified signatures class CachedSignatureVerifier: def __init__(self): self._verified_cache: Dict[str, bool] = {} def verify(self, data: bytes, signature: str) -> bool: cache_key = hashlib.sha256(data).hexdigest() if cache_key in self._verified_cache: return self._verified_cache[cache_key] result = self._verify_ed25519(data, signature) self._verified_cache[cache_key] = result return result # Bad: Re-verify same data multiple times class UncachedVerifier: def verify(self, data: bytes, signature: str) -> bool: return self._verify_ed25519(data, signature) # Expensive each time
9. Common Mistakes & Anti-Patterns
| Mistake | Wrong | Correct |
|---|---|---|
| Missing signature | No in config | Always include in updater config |
| HTTP endpoints | | Always use |
| Leaked keys | | Only (CVE-2023-46115) |
| No rollback | Install without backup | Backup before install, restore on failure |
// CORRECT: Update with rollback async fn update(&self) -> Result<(), UpdateError> { let backup = self.backup_current_version()?; if let Err(e) = self.try_update().await { self.restore_from_backup(&backup)?; return Err(e); } self.cleanup_backup(&backup)?; Ok(()) }
10. Pre-Implementation Checklist
Phase 1: Before Writing Code
- Write failing tests for update check, signature verification, rollback
- Review threat model in
references/threat-model.md - Verify signing key management plan (generation, storage, rotation)
- Define rollback strategy and backup scope
- Plan bandwidth throttling and delta update support
Phase 2: During Implementation
- Public key embedded in app config
- Private key stored securely (CI secrets only)
- All endpoints use HTTPS
- Implement signature caching for performance
- Add background download with progress tracking
- Ensure atomic updates (all or nothing)
- User data preserved during updates
Phase 3: Before Committing
- All tests pass:
pytest tests/test_update_system.py -v - Signature verification tested with invalid signatures
- Downgrade attacks prevented
- Rollback mechanism tested
- Network failure scenarios tested
- Updates tested on all platforms
- No secrets in committed code
- Key rotation procedure documented
11. Summary
Your goal is to create auto-update systems that are:
- Cryptographically Secure: Ed25519 signatures verified on every update
- Reliable: Atomic updates with rollback capability
- User-Friendly: Clear communication, minimal disruption
You understand that auto-update systems are high-value targets because they:
- Can push code to all users simultaneously
- Run with elevated privileges during installation
- Users trust updates from the app they installed
- Compromised updates affect the entire user base
Security Reminder: NEVER skip signature verification. ALWAYS use HTTPS. ALWAYS protect the private signing key. ALWAYS implement rollback. When in doubt, consult
references/threat-model.md for attack scenarios.