Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[3.1.3] - 2026-07-14
Fixed
- [CRITICAL] Native Android/iOS update checks now speak the real wire
contract. Both native layers previously called the dead
{serverUrl}/checkpath with NO auth headers, so on-devicesync()/getLatest()/ background checks silently reported "up to date" forever against the hosted backend. They now issueGET {serverUrl}/v1/updates/check?channel=…withX-API-Key,X-Device-ID(stable per-install UUID, newly minted/persisted),X-Current-Version(active bundle version — Android previously hardcoded "1.0.0"),X-Platform, andX-App-Version(binary version), and parse the v3 response fields (version/downloadUrl/checksum/signature/size/mandatory/releaseNotes). - Flat
initialize(PluginInitConfig)now configures the NATIVE layers too. Nativeconfigure/initializeonly accepted the wrapped{ config: {...} }shape and rejected the flat form outright, so serverUrl/apiKey never reached Android/iOS. Both shapes (plus nestedUpdateConfig) are now accepted and normalized. configure({ liveUpdate })can finally carry the API key — newLiveUpdateConfig.apiKeyfield, mapped through to the config manager and the native layers.- Web
getLatest()implemented (was a hardcoded{available:false}stub) — now delegates to the shared/v1/updates/checkimplementation. @capacitor/filesystem+@capacitor/preferencesdeclared as peerDependencies — the web path statically imports them, but they were only devDependencies, so consumers got runtime module-resolution failures with no install-time warning.- Legacy-key error is actionable:
INVALID_API_KEY_FORMATnow tells you to mint annu_app_…key in the dashboard.
Added
- Dashboard API-key management (backend + app detail page): create (max 3 active keys per app), rotate (replaces the secret in place; old value dies immediately), delete, and copy-anytime reveal (keys now stored encrypted at rest alongside the lookup hash — keys minted before this release can't be revealed, only rotated). Lifecycle actions are rate-limited per app from a DB-backed audit trail: create 1/hour, rotate 1/hour per key, and 10/30-days + 20/180-days caps per action — friendly 429s with retry timing. The ConfigPage now emits copy-paste snippets with the app's REAL bundle id, API key, and the canonical backend URL.
Changed
-
Canonical
serverUrlrule documented everywhere:https://nativeupdatebe.aoneahsan.com/api— the plugin appends/v1/updates/check. Fixed the wrong…/api/updatesexamples in the website's AI docs dump and the staleapi.nativeupdate.aoneahsan.comhost in MIGRATION.md. -
node-express example implements the real contract (X-API-Key auth, contract field names, SHA-256 checksums at upload, semver comparison) — it previously served the legacy fictional shape.
-
Canonical documentation moved to nativeupdate-docs.aoneahsan.com (Docusaurus on GitHub Pages, CI-deployed on every push). README and all package links now point there;
nativeupdate.aoneahsan.com/docs/*URLs 301-redirect to the matching page, so previously published links keep working. -
npm tarball slimmed.
files[]now ships a curated doc set (docs/CHANGELOG.md,docs/MIGRATION.md,docs/server-requirements.md,docs/KNOWN_LIMITATIONS.md) instead of the wholedocs/tree — 134 files instead of 321, and internal planning material no longer ships to npm. -
Docs corpus consolidated + corrected. The duplicated in-repo consumer guides (quick start, feature guides, api/, getting-started/, guides/, examples set) were deleted in favor of the docs site; the retained docs were corrected to the real API (nested
configureconfig,SyncResult.status,canRequestReview().canRequest, real event names, real/v1/updates/checkwire contract, correct backend host). -
react-capacitor example
capacitor.config.tsdropped inert config keys and the staleanalytics: { provider: 'firebase' }block (Firebase was removed from the SDK in v3.0.0).
[3.1.2] - 2026-07-14
Fixed
- npm README fully refreshed and de-contradicted. Removed the false
"yarn workspace / workspace:*" and "3 examples incl. Firebase backend"
claims (two example apps; the example uses yarn's
portal:protocol), replaced the legacy "What You Need to Build" scaffold section with an accurate "What Ships in This Package", correctedstartImmediateUpdate→performImmediateUpdate, fixed the live-update example to the realSyncResult.status+getLatest()flow, corrected the requirement to Capacitor 8.x and the backend host tonativeupdatebe.aoneahsan.com. - AI-INTEGRATION-GUIDE.md now matches the real API. Nested
UpdateConfigforconfigure(),SyncResult.statusenum,download({url, version, checksum}),canRequestReview().canRequest, the real event names, the/v1/updates/checkwire contract (was a fictionalGET /api/check), no nonexistentbundle uploadcommand, and no legacyplay:coreGradle instruction (the plugin bundles Play App Update + Play Review already). - Website docs viewer no longer renders the SPA shell as markdown.
Hosting rewrites answer
200 text/htmlfor missing files; the viewer now rejects HTML responses and falls through to the real file, so/docs/AI-INTEGRATION-GUIDErenders correctly and "View Raw Markdown" always points at an existing.mdURL.
Changed
- Single source of truth for the version: root
package.json. The CLI already reads it; the website now bakes it into__APP_VERSION__, JSON-LDsoftwareVersion,humans.txt, andllms-full.txtat build time ({{PKG_VERSION}} placeholders), andcli/package.jsonno longer carries its own version field. No file hardcodes the version anymore. - Website
package-docscopies are synced from the repo root on every build (yarn sync:package-docs, refresh-only — internal docs are never newly published), ending the stale-copy drift (the public copy was stuck at the v2.0.0-era content).
[3.1.1] - 2026-07-13
Fixed
- Update checks now share ONE wire-contract implementation. New
src/core/update-check.tsimplements the/v1/updates/checkcontract;plugin.ts, the version manager, and the background scheduler's live fallback all route through it. The latter two previously called paths the backend never served (/check,/updates/latest), so their remote checks could never succeed against the hosted backend.
Changed
- Native app-store version paths documented as extension points. The
/app-versionendpoints used by the native app-store update module are now explicitly documented (JSDoc + docs) as custom-backend extension points rather than hosted-backend routes. No public API changes.
[3.1.0] - 2026-07-02
Added
exportsmap in package.json. Modern conditional exports (types/import/require) with./dist/*and./cli/*wildcard passthrough so existing deep imports keep resolving. Legacymain/module/types/unpkgfields retained.website/.env.production. Production builds now always bake the production backend URL; the dev.envcan safely point at localhost.- Composite index
builds(app_id, channel, status, created_at). Covers the update-check lookup's sort; removes a filesort from the hottest query.
Changed
- Update-check hot path (backend). The two per-request DB writes
(device activity + analytics event) now run after the response is
sent (
dispatch(...)->afterResponse()), and the latest-build lookup is cached for 45s with model-event invalidation. Rollout bucketing stays per-device and uncached. analytics:cleanupnow also prunesanalytics_eventsin 5000-row chunks (previously only device-activity rows; the events table grew unboundedly).- Web validateUpdate() reuses downloaded bundle bytes. Validating a
bundle right after
download()no longer re-downloads it to hash. - Landing page animation engine loads lazily (LazyMotion): ~44 KB of framer-motion moves off the first-paint critical path.
- Firebase Hosting config consolidated to the repo root with long-lived immutable caching for hashed assets and revalidation for HTML.
- CORS
supports_credentialsdisabled (bearer-token auth needs no credentialed CORS).
Removed
- 9 dead env keys from
website/.env(Firebase-era), 10 commented-out debug lines insrc/web.ts, 2 framework scaffold test files.
Fixed
- Doc drift: backend is Laravel 13 (not 11); the test suite is PHPUnit
(not Pest); canonical backend host is
nativeupdatebe.aoneahsan.com.
[3.0.1] - 2026-05-11
Added
- Documentation site. A new public Docusaurus documentation site
ships alongside the marketing site. 58 pages total: SDK reference
(23), CLI reference (9), Backend setup (5), Platform guides (3),
Tutorials (2), How-to guides (6), Concepts (6), Getting Started (2),
About + intro pages. Full Diátaxis coverage. Lives in the sibling
native-update-docs/repository; deployed to Firebase Hosting atdocs.nativeupdate.aoneahsan.com. Discovery files (robots.txtwith 30+ AI-bot allowlist,llms.txt,llms-full.txt,humans.txt,.well-known/security.txt,sitemap-index.json,sitemap.xml) and 3 JSON-LD blocks per page (WebSite, SoftwareApplication, Organization) for AI-search citability. docs/security/PRE-LAUNCH-PENTEST-CHECKLIST.md. 10-section DIY security checklist covering dep hygiene, authentication, authorization, CORS/CSRF, bundle integrity, file safety, data handling, operational safety, mobile platform integration, 9 specific attack paths, and a final sanity sweep.docs/PERFORMANCE.md+tools/bench/. Vitest microbench scaffold for the SHA-256 checksum and RSA-SHA256 signature hot paths, with calibration targets and a regression-investigation workflow.- CI: example-bundle workflow.
.github/workflows/example-bundle.ymlbuilds, signs, and attaches an OTA bundle fromexample-apps/react-capacitorto each GitHub Release. Also runs weekly as a drift-detection canary. - Example-app capability cards.
example-apps/react-capacitor/src/App.tsxnow demonstrates App Update (getAppUpdateInfo+openAppStore), App Review (canRequestReview+requestReview), and Background Update (toggle withallowMeteredConnection+nextCheckTime) alongside the existing Live Update flow. One section per feature area.
Fixed
website/src/pages/ExamplesPage.tsx. Removed the stale third example card advertising theexample-apps/firebase-backenddirectory, which was deleted in v3.0.0. The GitHub source link 404'd and the description ("Firestore manifests") contradicted the website's own v3 architecture rule.
Documentation
docs/project-audit/MARKETING-WEBSITE-FINDINGS-2026-05-11.mdproduced by the production-readiness audit Batch 9 sweep. Documents 3 deferred follow-ups (stale MDX docs mirror, HomePage missing Screenshots/Demo section, DocsPage size violation) and 7 surfaces confirmed healthy.docs/MARKETING_WEBSITE_TRACKER.mdannotated as SUPERSEDED with a redirect block to the new findings doc.example-apps/react-capacitor/README.mddocuments the React 18 vs 19 version-choice reasoning (the plugin is React-version-agnostic; the example pins React 18 to maximise readable audience).
Closed
- Production-readiness audit (10 batches) initiated 2026-05-08 is
complete as of 2026-05-11. Authoritative tracker:
docs/tracking/production-readiness-audit-tracker.json. The flow enters a 7-day cooldown; next allowed re-run 2026-05-18.
[3.0.0] - 2026-05-04
Removed (BREAKING)
- Firestore backend.
backendType: 'firestore'and thefirestore: { … }config field are gone. The plugin now speaks HTTP to a single backend (the hosted Native Update SaaS / Laravel reference implementation inbackend/). Apps using the Firestore branch must switch toserverUrl+apiKey— see MIGRATION.md. src/firestore/module.FirestoreClient,ManifestReader,firestore.rules,firestore.indexes.json, the schema types, and theFirestoreConfigpublic export are removed.example-apps/firebase-backend/. The standalone Firebase Cloud Functions example is removed;example-apps/node-express/is the reference HTTP implementation of the/v1/updates/checkcontract.
Changed
- Rollout / device-info / update-check types moved to
src/types/rollout.tsand are re-exported from the package root.FirestoreTimestampis replaced byRolloutTimestamp(number | string | Date) with atoEpochMs()helper. - Website auth-service drops the Firestore user-profile mirror. The
Laravel
ValidateFirebaseTokenmiddleware already upserts the user record from verified token claims on every authenticated request, so the client mirror was redundant.
[2.0.0] - 2026-04-18
Native (Android + iOS)
- Boot-time bundle re-verification. On every cold start the plugin
re-hashes the active OTA bundle and compares against the checksum
stored at install time. If the host app configured a public key and the
bundle carries a signature, the signature is re-verified too. Mismatch
triggers an immediate rollback to the previous known-good bundle and
emits
updateStateChangedwithstatus: 'ROLLBACK'. Closes an on-disk-tampering gap on rooted/jailbroken devices. - Automatic rollback on crash loop. Activating a bundle marks it as
pending-verify. Each cold start before
notifyAppReady()fires increments a counter; after 2 failed boots the plugin rolls back to the previous bundle automatically. Catches a bundle that crashes the app before it can tell the plugin it booted cleanly. - Bundle metadata now persists the signature alongside checksum so the boot re-verify can run without needing the manifest or a re-download.
- iOS zip-slip + zip-bomb protection. Bundle extraction now inspects every archive entry before writing to disk: rejects symlinks, absolute paths, and any path that resolves outside the destination directory, and caps total uncompressed size at 500 MB. A malicious bundle can no longer overwrite files outside the sandbox or exhaust device storage. (Android does not currently extract bundles in-plugin; when that is added, the same validation must be applied.)
- Secure storage is now always on. Android persists secrets only
through
EncryptedSharedPreferences(Keystore-backed); iOS persists only through the Keychain. ThesecureStorage: falseopt-out is removed — passing it is ignored. On web,initialize()config is sanitised before write-through tolocalStorage:apiKey, top-level andliveUpdate.publicKey, andsecurity.publicKeyare stripped, so a compromised script or stale cache cannot surface the credential. Host apps must pass the apiKey/publicKey on everyinitialize(). - Android background retries are bounded + backoff-aware. All three
enqueue sites (periodic, one-shot, notification-action) now pass
setBackoffCriteria(EXPONENTIAL, 30s). The worker itself caps at 5 retry attempts per run, so a persistently-failing server can no longer burn battery in a tight retry loop — total wall-clock budget for a failing run is ~15 minutes before falling through to the next scheduled tick. - iOS background-task diagnostics.
BGTaskScheduler.submiterrors are now classified (.notPermitted,.tooManyPendingTaskRequests,.unavailable) and logged with actionable messages..notPermittedalmost always means the host app'sInfo.plistis missing the scheduler identifier — previously a silent failure. We also cancel any prior pending request before resubmitting to dodge the "already-scheduled" edge case observed on a few iOS versions. Host apps must declareBGTaskSchedulerPermittedIdentifierswithcom.aoneahsan.nativeupdate.backgroundin their own Info.plist — the framework's plist does not propagate to the app bundle.
Security (BREAKING)
- Signature verification fails closed. If the host app configures
publicKey,requireSignature, orenableSignatureValidation, a missing signature now throws instead of silently passing. Previously, a manifest with the signature field stripped out would bypass all integrity checks. - Checksum verification fails closed.
verifyChecksum(data, '')now throws. Every OTA bundle manifest must ship a SHA-256 checksum; a missing one is treated as tampering. - Monotonic version enforcement persists across reinstalls. The plugin now records the highest version ever applied per channel in Preferences and refuses to apply any older bundle — even if the active-bundle state was wiped. Closes a downgrade-attack path.
setUpdateUrl()is deprecated and ignored at runtime. The update server URL is fixed atinitialize()time. Runtime mutation was an XSS attack path (compromised WebView could repoint updates to a hostile host). Re-callinitialize()to switch servers.- Removed
allowDowngradefromUpdateOptions. Downgrades are a debug/recovery operation and do not belong in a production option flag. - Removed
enableEncryption,encryptionKey,encryptionSaltfrom the public API. Bundle encryption was only wired on the web path and never implemented on Android or iOS — the option shipped a false guarantee. Sign bundles (RSA-PSS/ECDSA) and serve them over HTTPS; the plugin's existing verification is the correct integrity primitive.
Backend (native-update/backend)
- API key checksum comparison uses
hash_equals(timing-safe) — closes a byte-by-byte response-time leak on brute-force against short suffixes. - Bundle download asserts
build->app_id === authenticated app->idas defense-in-depth beyond the signed URL. - Device identifiers are HMAC-SHA256 hashed (keyed by
APP_KEY) via an Eloquent mutator before landing inanalytics_eventsanddevice_activities. Casual DB access can no longer correlate a row back to a specific device. - Build upload rejects versions ≤ current channel head unless an explicit rollback flow is used. Prevents accidental and malicious downgrades on live channels.
GET /api/healthunauth'd endpoint added for Hostinger uptime pings./api/v1/*now carries a route-levelthrottle:600,15as defense-in-depth on top of per-key rate limiting in middleware..env.exampletightened for production defaults:APP_ENV=production,APP_DEBUG=false,LOG_LEVEL=warning,LOG_CHANNEL=daily,DB_CONNECTION=mysql.- Signed bundle URLs include checksum + app_id in their signed payload
and
BundleController::downloadre-asserts both against theBuildrow before streaming. A replayed URL whose Build was flipped to archived now returns 403 instead of serving the old bundle. - Signed URL TTL moved from 5 minutes → 30 minutes and made
configurable via
NATIVE_UPDATE_DOWNLOAD_TTL_MIN. Five minutes frequently expired mid-download on a slow mobile link. - Bundle downloads stream through Laravel, never redirect to the underlying storage URL — comment enforces the invariant. Keeps auth, rate-limit, and audit in one chokepoint.
- Signing-key decryption is now per-request memoised in
SigningKey::getPrivateKey().rotate()andrevoke()clear the cache so stale keys can't leak across a long-running worker.
Optimization
- Resume-download correctness.
resumeDownload()now records whether the server actually returned HTTP 206 (Partial Content) and decides whether to concatenate the partial prefix or discard it accordingly. Previously a server that fell back to HTTP 200 (full response) silently produced a corrupted bundle by double-writing the prefix — a latent corruption bug on CDNs that don't honour Range. - Jittered exponential backoff on download retry.
downloadWithRetrynow adds ±25% random jitter to each retry interval. Avoids the thundering-herd scenario where many clients retry in lockstep after a common failure (CDN blip, server restart) and hammer a recovering server simultaneously. - Auto-cleanup is now the default. After a successful bundle apply
the plugin keeps the last 3 bundles (active + 2 recent) and deletes
the rest. Host apps that want the old every-bundle-forever behavior
can pass
cleanupOldBundles: falseexplicitly.
Payments (BREAKING)
- Stripe + Cashier removed; PayPal (
srmklive/paypal) takes over. NewSubscriptionControllerexposessubscribe → activate → cancel / resume / changePlanwith PayPal's approval-URL flow. Webhook endpoint moved from/api/webhooks/stripe→/api/webhooks/paypal, with a newPayPalWebhookControllerthat verifies transmissions via PayPal's ownverifyWebHookAPI. - New migration
2026_04_18_000001_add_paypal_provider_to_subscriptionsintroduces provider-agnostic columns (provider,provider_subscription_id,provider_status,provider_plan_id) onsubscriptions,subscription_items, andusers. Legacystripe_*columns are kept nullable for one release to ease rollback. .env.examplereplacesSTRIPE_*withPAYPAL_MODE,PAYPAL_LIVE_CLIENT_ID / SECRET / APP_ID,PAYPAL_PLAN_PRO,PAYPAL_PLAN_ENTERPRISE,PAYPAL_WEBHOOK_ID.Usermodel drops the CashierBillabletrait and grows anactiveSubscription()helper against the new schema.
Migration
- Apps relying on
allowDowngrade: trueinUpdateOptionswill fail to apply older bundles. Remove the flag and treat rollback as a separate flow. - Apps passing
enableEncryption,encryptionKey, orencryptionSalttoinitialize()orconfigure()will see them silently dropped. Rely on signature + checksum verification for integrity. - Apps calling
setUpdateUrl()at runtime will receive a deprecation warning and the call is a no-op. PassserverUrltoinitialize().
[1.4.9] - 2026-03-11
Added
- Firestore-backed manifest reads through
backendType: 'firestore' - Secure manifest lookup support via
apiKeys/{apiKey}/manifests/{channel} - Concrete no-cost backend implementation guide for
website + Firestore + Google Drive
Changed
- Website dashboard publish flow aligned to Google Drive asset uploads plus Firestore manifest publishing
- Website dashboard configuration/docs now recommend Firestore instead of the old
/api/updatespath - Package config types now expose Firestore configuration for production app integration
Fixed
- Manifest publishing now writes the full manifest shape to both secure and compatibility Firestore paths
- Google Drive asset uploads now preserve correct filenames for ZIP bundles and generated manifest files
[1.4.5] - 2026-02-25
Changed
- Updated all dependencies to latest versions
- Full verification suite passing (81 tests, 0 lint warnings)
- Android Gradle build verified (assembleDebug successful)
- Website and Firebase Functions builds verified
- Release-ready state confirmed
Maintenance
- Package updates: Capacitor 8.x, TypeScript 5.9.3, Vitest 4.0.18, ESLint 9.39.2
- Node.js engine requirement: >=24.13.0
[1.0.7] - 2025-01-15
Fixed
- Critical Android Build Issues:
- Fixed Google Play app-update library dependencies by using explicit version 2.1.0 instead of variable
- Resolved kotlinVersion resolution issues by hardcoding version in gradle plugin
- Made all Google Play Services dependencies use explicit versions to prevent resolution errors
- Added variables.gradle file for better variable management
- Ensured no manual intervention is required by users
Changed
- Updated package version to 1.0.7
- Hardcoded critical dependency versions in Android build.gradle to prevent resolution issues
[1.0.6] - 2025-01-15
Fixed
- Android Build Issues:
- Fixed Google Play app-update library version error (changed from non-existent 18.2.0 to correct 2.1.0)
- Fixed
kotlinVersionMissingPropertyException by properly defining it in buildscript ext block - Added missing Kotlin stdlib dependency
- Reorganized build.gradle structure to ensure proper variable scope
Documentation Fixes
- Corrected all API method references to match TypeScript definitions
- Fixed method signatures and parameters across all documentation
- Updated event names to match the 4 defined events (downloadProgress, updateStateChanged, backgroundUpdateProgress, backgroundUpdateNotification)
- Removed references to non-existent methods
- Fixed broken documentation links
Android Configuration
- kotlinVersion: 1.9.22 (stable version)
- playAppUpdateVersion: 2.1.0 (correct Google Play version)
- playReviewVersion: 2.0.1
- All dependencies now use proper version variables
[1.4.0] - 2025-01-02
Added
- Complete TypeScript implementation with strict typing
- Core plugin architecture with modular design
- Comprehensive error handling system
- Security validation framework
- Bundle management system
- Download manager with retry logic
- Version management capabilities
- Configuration manager
- Logging system with multiple log levels
- Cache management
- Background update support
- Plugin initialization system
Changed
- Migrated from class-based to functional architecture
- Improved type definitions and interfaces
- Enhanced security with input validation
- Better error messages and error codes
Fixed
- Removed circular dependencies
- Fixed TypeScript compilation issues
- Proper exclusion of test files in tsconfig
- Removed console.log statements in favor of Logger
[0.0.2] - 2024-12-01
Added
- Comprehensive documentation for all features
- Bundle signing and security utilities
- CI/CD pipeline with GitHub Actions
- Example update server implementation
- TypeScript strict mode
- ESLint v9 flat config support
Fixed
- Security vulnerabilities in update process
- TypeScript compilation errors
- Path traversal prevention
Security
- Enforced HTTPS for all update downloads
- Added RSA signature verification
- Implemented checksum validation
- Added certificate pinning support
[0.0.1] - 2024-01-08
Added
- Initial release of Capacitor Native Update plugin
- Live/OTA update functionality
- Bundle download and management
- Multiple update strategies (immediate, background, manual)
- Delta update support
- Automatic rollback on failed updates
- Update channels (production, staging, development)
- End-to-end encryption and signature verification
- Bundle integrity checks with SHA-256/512 checksums
- Native app update functionality
- App store version checking
- Immediate and flexible update flows (Android)
- Direct app store navigation
- Version comparison and update priority
- App review functionality
- In-app review prompts using native APIs
- Smart triggering with configurable conditions
- Rate limiting to respect platform quotas
- Security features
- HTTPS enforcement by default
- Certificate pinning support
- Public key signature verification
- Input validation and sanitization
- Secure storage using platform keystores
- Platform support
- iOS implementation using Swift
- Android implementation using Kotlin
- Web fallback implementation
- Comprehensive TypeScript definitions
- Example application demonstrating all features
- Extensive documentation
- API reference
- Security guidelines
- Migration guide
- Feature overview
Security
- Enforced HTTPS for all update operations by default
- Required checksum validation for all bundle downloads
- Implemented path traversal prevention
- Added secure storage for sensitive configuration
Known Issues
- ESLint configuration needs update for v9 compatibility
- Certificate pinning requires manual certificate configuration
- Delta updates require server-side implementation
Future Releases
[0.1.0] - Planned
- Add delta update server reference implementation
- Improve offline update handling
- Add update scheduling APIs
- Enhanced progress reporting with ETA
- Batch update support
[0.2.0] - Planned
- Machine learning for optimal update timing
- Peer-to-peer update distribution
- Advanced A/B testing framework
- Custom update UI components
- Differential compression algorithms