=== MigratePilot ===
Contributors: sibusisokhoza
Tags: migration, export, import, backup
Requires at least: 6.4
Tested up to: 7.1
Requires PHP: 8.1
Stable tag: 1.0.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Move a single WordPress site's database, uploads, themes, and plugins in a private streaming package.

== Description ==

MigratePilot removes plugin-imposed site byte, file-count, and row-count ceilings. New exports use checkpointed .wmp streaming packages, resumable chunked uploads, bounded worker requests, a review step, and a short final commit. Practical capacity depends on free disk space, filesystem and PHP integer support, database packet limits, and memory for individual database rows and serialized settings. 64-bit PHP is required. No claim of infinite hosting capacity or crash-proof recovery is made.

Imports replace destination users and credentials with those from the source site. Destination home and site URLs remain in use. MigratePilot remains installed and active. Packages contain executable PHP, personal information, password hashes, and secrets stored in database settings: only import trusted packages and keep downloaded packages private. Checksums detect corruption; they do not authenticate a package's creator. Packages are not encrypted.

== Privacy and external connections ==

The plugin does not include analytics, advertising, usage telemetry, or automatic connections to the developer. It does not contact migratepilot.sibusiso.dev to perform migrations. Documentation and author links open only when clicked. The product website has its own Google Analytics integration; that tag is not bundled in this WordPress plugin.

Direct site-to-site migration contacts only the source WordPress URL explicitly entered by an authorized administrator. The destination sends a temporary bearer connection code and block offset to that source over HTTPS, and receives the sensitive export package. These are your selected sites, not a developer-operated transfer service. Use this feature only for sites you are authorized to migrate.

Packages contain personal data, password hashes, executable PHP, and stored secrets and are not encrypted. Private temporary packages normally expire after 24 hours of inactivity; activity records are retained for up to 30 days. Cleanup depends on scheduled tasks and site activity. Deactivating or deleting the plugin does not erase recovery evidence or guarantee immediate removal of private temporary data. Have an administrator review and remove retained artifacts safely when retiring the plugin.

== Credits and licensing ==

Developed by [Sibusiso Dev](https://www.sibusiso.dev/). Powered by [AfriBiz Tech](https://afribiz.tech/).

Plugin code and original Flightmark artwork: copyright 2026 Sibusiso Dev, GPL-2.0-or-later. The full license is bundled in LICENSE.txt.

Bundled Manrope font: copyright 2018 The Manrope Project Authors (https://github.com/googlefonts/manrope), licensed under the SIL Open Font License 1.1. Its license is included in assets/fonts/OFL.txt. The original variable font source is included in assets/fonts/Manrope-Variable.ttf. Fonts are served locally; no font-service request is made.

For product documentation and support, visit https://migratepilot.sibusiso.dev/migration-guide.html or contact wpmigratepilot@sibusiso.dev. Report suspected security issues privately rather than posting sensitive packages or connection codes in public support threads.

== Installation ==

1. Place the migratepilot folder in wp-content/plugins, or upload the installable ZIP through Plugins > Add New.
2. Activate MigratePilot in Plugins on both the source and destination.
3. Open MigratePilot in the main WordPress sidebar, or choose Start migration on the Plugins page or activation welcome notice. Tools > MigratePilot also remains available.
4. Read the formatted installation and migration guide: https://migratepilot.sibusiso.dev/migration-guide.html

== Migration workflow ==

1. Install the same WordPress version on the source and destination. Enable PHP ZIP and use PHP 8.1 or later. Site tables must use InnoDB.
2. Take independent backups of both sites. Pause writes (orders, comments, editing, scheduled jobs, and external integrations) during the entire migration window. Streaming export spans multiple requests and does not hold a cross-request transaction snapshot. You must keep the source database and files quiescent throughout export, including orders, editing, cron, integrations, and deployments. MigratePilot requires acknowledgement but does not freeze all external writers automatically.
3. Open Migrate site on the source and choose Create export package. Confirm that source writes are paused. Keep the browser open while checkpoints advance, then download the .wmp package. Pause processing or resume it from Activity when needed. Keep it private.
4. On the destination, upload the package using Upload / resume & review. Large packages are sent in blocks, up to 4 MB per request and reduced when a proxy returns HTTP 413. If the connection stops, select the exact same local file again to resume accepted bytes. File identity is a resume hint, not proof of authenticity. Validation runs without replacing the destination. For .wmp packages, review the grouped compatibility scan and choose safe repair or preservation for every affected class/table/column before confirming. Follow the recovery checklist and download a destination backup first. After completion, download the HTML migration report and test the site in your browser. Legacy ZIP packages retain their earlier import workflow. Review the source, WordPress version, table count, and file counts. Confirm your backup and trust in the package, then type the exact destination hostname and choose Confirm & replace destination.
5. After a successful import your existing session is ended. Use Sign in to migrated site and log in using a source-site administrator account.
6. Review the migrated site, save Settings > Permalinks, clear external/CDN caches, and check domain-specific integrations such as email, payments, and licenses.

== Direct site-to-site transfer ==

Both sites need MigratePilot v1. On the source, pause writers, create a .wmp export, and open the completed job in Activity. Choose Create connection code and copy its source installation URL and temporary code to the destination's Migrate site > Bring it here by URL form. Creating a new code revokes the previous one. Revoke the code manually after the transfer when desired.

Open both administrator workspaces over HTTPS before creating or entering a code. The source must have HTTPS with a valid certificate and be reachable from the destination at its public installation URL, including any WordPress subdirectory. The destination pulls one 1 MiB block per request, saves verified byte offsets, and then runs ordinary package validation. Destination replacement still requires explicit review and hostname confirmation. Keep the destination browser open; pause or resume direct transfer in the job details. After a disconnect, resume from Activity. If the one-hour code expires, generate a new code for the SAME completed source export and reconnect to resume the accepted bytes. Regenerating an export creates a different package and starts a separate transfer.

Codes are bearer secrets granting read access to one sensitive export. They are sent in HTTPS POST bodies, not URLs. Only a SHA-256 secret hash is stored at the source; the destination stores its connection privately until reception completes, cancellation, or retention cleanup. Code expiry is at most one hour and never exceeds package expiry. Codes do not permit source writes, database operations, or destination replacement. Downloads and codes stop working after source package cancellation or expiry. Revocation prevents subsequent reads; bytes already received cannot be recalled, and an in-flight block can still finish.

Direct pulls reject HTTP, redirects, embedded credentials, custom ports, and private/loopback/reserved IPv4 source addresses. A hostname must resolve to public IPv4; IPv6-only sources are not supported. WordPress safe HTTP validation remains enabled. Network firewalls, authentication gateways, domain aliases, and TLS failures can prevent connections; use the canonical WordPress installation URL or file export/upload. Local development hosts use the file workflow. The source code must remain private and should be revoked if exposed. WordPress HTTP debugging plugins and infrastructure administrators may be able to inspect outbound request bodies.

== Included ==

* All tables beginning with the source site's database prefix, including plugin tables.
* Files in the standard wp-content/uploads, wp-content/themes, and wp-content/plugins directories.
* Nested serialized array and stdClass settings, including serialized string lengths.
* URL and absolute installation-path replacement in database values, plus role and usermeta prefix changes.
* JSON-escaped URLs, binary database values, and SQL NULL values.

== Limits and exclusions ==

* Single site only; multisite is blocked.
* No fixed plugin package-size, file-count, or row-count cap. Free storage is checked before uploads and before each written block. Allow space for the uncompressed streaming archive, incoming content, database staging, and existing destination data. The archive format is uncompressed and may be larger than a legacy ZIP.
* New .wmp work is driven by the open browser through short worker requests targeting approximately four seconds of processing. There is no unattended transfer daemon. Closing the browser stops advancement after the current work unit; reopen Activity and continue the queued/paused job. Host query, memory, and filesystem limits still apply, particularly to a single very large database row or serialized value. Source tables without primary keys use offset pagination and may export slowly.
* Legacy ZIP packages remain importable via the compatibility worker, which is not resumable during extraction or restore. Export a new .wmp package for large-site transfers. Use MigratePilot v1 on both sites for new .wmp migrations.
* Both sites must use the same WordPress version. Content locations are resolved through WordPress APIs. Relocated uploads and a single theme/plugin root are supported when they are separate local directories with writable parents. Overlapping roots, multiple theme roots, symbolic links and filesystem aliases require manual migration. Install MigratePilot in its own folder in the WordPress plugins directory.
* Core WordPress files, wp-config.php, server configuration, root files, must-use plugins, and cache/database drop-ins are excluded. Prepare those separately on the destination.
* Foreign keys, views, triggers, generated columns, symbolic links, and some nonstandard table schemas are unsupported. Custom serialized classes pause new .wmp imports for value-level review: supported string/object records can be repaired as serialized data without loading classes, or the selected value can be kept byte-for-byte unchanged. Opaque Serializable payloads, references, enums, malformed data, and ambiguous formats cannot be automatically repaired. Verify the affected plugin after migration.
* URL replacement affects database values. Hard-coded URLs inside PHP, CSS, or other files need separate review.
* The current destination migration plugin is retained. Streaming packages that attempt to replace MigratePilot are rejected; normal exports exclude it. Legacy ZIP imports preserve the installed version.
* Successful imports discard temporary rollback data; there is no one-click restore. The Activity screen retains up to 30 days of operation records and shows the latest 100. Private export packages and uploads expire after 24 hours of inactivity; successful upload blocks and worker steps renew retention; downloads are blocked immediately at expiry. Post-import and retention cleanup run in bounded work through a one-minute WordPress scheduled task and when creating jobs. Cleanup can take multiple passes on a large site. Low-traffic or disabled-cron sites can retain files longer until cleanup next runs. Failed or interrupted imports needing recovery retain their evidence for manual review. System temporary storage can also be purged by the host before retention ends.

== Serialized value compatibility ==

New .wmp imports pause in staging when an unsupported serialized class is encountered. Operation details show the original class, source table, row position within the package (not its database primary key), and column. Plugin attribution is an explicitly labeled inference from the class prefix and plugin headers indexed during validation; some plugins or themes cannot be identified reliably. Setting contents are not included in diagnostics or logs.

An administrator can acknowledge a per-value decision: attempt supported serialized string repair, or skip URL changes for that one value and retain its original bytes. Neither action disables the entire plugin or deletes the row. Preserved values may contain source URLs/paths that require manual correction. Class names and property names are preserved during repair; custom PHP classes are never loaded or executed. Opaque payloads, references, enums and malformed records are not automatically repaired. By default decisions apply to one immutable package cell. An explicit matching-values option applies the choice to remaining values with the exact same serialized class, source table and column in this import only. Every matching repair still passes the safe parser; unsupported values pause for separate review. Approvals are recorded privately with the operator and resume from the saved checkpoint. Legacy ZIP imports require fixing the source data and creating a new .wmp export for this recovery workflow.

== Failure handling ==

Packages are extracted into random private system temporary directories outside the public document root. Imports reject unsafe paths, symbolic links, unexpected records, oversized individual framing units, version mismatches, truncated streams, changed block checksums, and invalid final integrity chains before changing live content. Files and database rows can span many framing units, so the per-block safeguard does not cap their total size. Downloads support authenticated HTTP byte ranges for download-manager resume while the artifact and nonce remain valid.

Each operation saves its status, operator, timestamps, and a bounded progress log outside the migrated database. Only administrators with plugin installation and unfiltered HTML permissions can use migration endpoints. Requests and downloads require WordPress nonces. Activity records are operational records, not immutable compliance audit logs. No telemetry is sent. The optional Google Drive preview sends archive data only after explicit administrator consent.

The importer creates separate staging database tables and prepares incoming content on the destination volume. A private recovery journal records the staging table prefix, backup table prefix, planned directory swaps, and whether the database commit or rollback completed. It briefly enables WordPress maintenance mode, swaps content directories, then performs one database RENAME TABLE statement. Caught failures before that database commit attempt to restore original content. Streaming imports preserve their prepared directories and tables when rollback succeeds so the final commit can be retried. Transactional receipts ensure replaying a committed row block cannot duplicate data. A journal confirming a completed database commit is used to recover the operation outcome; ambiguous final swaps require manual recovery review. This does not provide crash-proof transactions across the database and filesystem.

Ordinary upload and processing interruptions leave resumable checkpoints. A hard interruption inside the brief final replacement, an out-of-disk condition, or a failed rollback can still leave a partial migration or maintenance marker. Restore your independent backup in that case; do not assume an interrupted request succeeded. Remove ABSPATH/.maintenance only after checking that recovery is complete. A hard process kill can also leave random temporary packages or .wp-migratepilot staging directories; inspect and securely remove those after recovery.

== Development ==

The development test harness is distributed with the review kit, not the runtime plugin. tests/run.ps1 creates a disposable WordPress installation context and MySQL database with WP_DEBUG enabled. It exercises permissions, backup export/verification, mocked Google Drive requests, relocated directories, complete streaming import, and forced pre-commit rollback. It never targets the user's live database. tests/plugin-check.ps1 runs WordPress Plugin Check separately. Runtime paths in these local convenience scripts must be adjusted on other machines.

Google HTTP and email are intercepted in the automated suite. These checks do not replace hosted two-site migration tests or testing with a real Google account. Large-site benchmarks and a complete PHP/WordPress version matrix are not claimed by this review build.

Finish or cancel operations before updating the plugin. Old checkpoints without directory validation must be restarted. Existing archive packages remain readable; keep an independent backup before any import.

== Frequently Asked Questions ==

= Where do I open the plugin after activation? =

Select MigratePilot in the main WordPress sidebar. The Plugins page also has a Start migration link, and the activating administrator receives a dismissible welcome notice. Tools > MigratePilot remains available.

= Is there a paid size-limit upgrade? =

No. The plugin has no package-size, file-count, or database-row cap. Hosting storage, filesystem limits, database packet limits and memory for individual values still determine practical capacity.

= Can I migrate from a URL? =

Yes, from a completed .wmp export on a public HTTPS source site using its temporary connection code. The package is validated for review before destination replacement. See Direct site-to-site transfer above.

= Does it support every WordPress or PHP version? =

No. It requires WordPress 6.4 or later, matching WordPress versions on both sites, 64-bit PHP 8.1 or later, the ZIP extension and a supported database/layout. See Limits and exclusions above.

= Is the product website's Google Analytics tag installed on my WordPress site? =

No. Website analytics is separate from this plugin. The plugin includes no analytics or usage telemetry.

== Scheduled local backups ==

Open MigratePilot > Backups. Select a private directory outside the public document root, a frequency (manual, every 2/4/8/12 hours, daily, weekly, fortnightly, or monthly), the first run in your site timezone, and retention of 1–100 successful copies. An optional maximum age removes older copies; 0 disables age-based expiry. The newest successful backup is always preserved, and protected copies are excluded from automatic removal and copy counts. Failed partial backups expire after seven days. Saving a policy authorizes its cleanup on subsequent worker runs.

Back up now and scheduled runs use WordPress cron and saved checkpoints without keeping the browser open. Low traffic or disabled cron can delay backups and retention. Configure a server task to invoke WordPress cron every minute for reliable processing. The schedule pauses when its author loses the required administrator permissions, and waits for migration work. Deactivation stops the worker without deleting retained archives. A migrated source configuration does not enable destination backups automatically; configure the destination separately.

Backups include supported site tables, configured uploads, themes and plugins. Core, wp-config.php, custom server files and the running migration plugin are excluded. Archives are uncompressed and unencrypted, stored on this server only, and require space for retained copies plus an in-progress copy. Download copies to an independent location. A live backup does not hold a transaction snapshot across requests; changing files or database rows can produce inconsistent application data. For a consistent migration export, pause all writers and use the migration workflow.

Completed archives pass block-chain and package validation before download and retention cleanup. Restore by downloading the .wmp file and using Migrate site > Import, including review and explicit destination confirmation. Verification is not a runtime restore test. The optional Google Drive preview provides off-server copies; archive encryption, incremental backups and one-click rollback are not included.

== Changelog ==

= 1.0.0 =
* Add optional Google Drive preview, backup alerts, disk-space planning and administrator-recorded restore rehearsals.



* Add scheduled local backups, manual background backups, calendar scheduling, count and age retention, protected copies, verified downloads and checkpoint retries.
* Keep the newest successful backup when retention runs; clean failed partial backups after seven days.
* Isolate backup configuration per installation and prevent backup/migration workers from overlapping.



* Scan serialized database values during streaming package validation and group review choices before destination replacement.
* Add a recovery preparation checklist with a route to export the destination before importing.
* Check destination URLs, active plugin entry files, theme stylesheets and maintenance state after streaming replacement.
* Download a formatted HTML migration report with group decisions and automatic check results. Runtime behavior still requires manual verification.



* Initial public v1 release of MigratePilot.
* Improved reading sizes, input boundaries, text contrast, and touch targets.
* Added accessible activity table captions, consistent button names, tab-panel focus, and dialog focus return.
* Improved narrow-screen navigation and contained table overflow.
* Explicit green activity bars for unmeasured processing, measured upload percentages, and full completion bars.
* Active export workers display Running between checkpoints instead of Queued.
* Accessible progress descriptions and reduced-motion support.
* Direct server-to-server retrieval of completed .wmp exports using an HTTPS source URL and temporary code.
* Revocable one-hour source grants, bounded verified blocks, saved offsets, and Activity pause/resume.
* Public-source URL validation and unchanged import validation/review before replacement.
* Expanded direct-transfer protocol, interruption, corruption, and authorization regression coverage.
* Checkpointed .wmp exports, validation, staged imports, and resumable chunked uploads.
* No fixed plugin site-size, file-count, or database-row cap.
* Review-before-replacement workflow, explicit hostname confirmation, and migration preparation guidance.
* Protected range downloads, private package storage, retained activity, and downloadable logs.
* Recovery journals, bounded cleanup, and legacy ZIP import compatibility.
* Flightmark identity, outlined wordmarks, locally hosted Manrope, and a responsive administrator workspace.

* Added a main sidebar entry, Plugins-page Start migration link, and dismissible activation welcome notice.
* Added a formatted HTML installation and migration guide on the website.
* Upload progress shows fractional sizes and exact received/total byte counts instead of whole-gigabyte rounding.
* Added author/company attribution, GPL/OFL disclosures, translation template, WordPress-native static UI translations, and submission reviewer notes.
* Parameterized additional database operations and removed the obsolete single-request upload endpoint; legacy ZIP import remains available through the resumable upload flow.
* Added private serialized-class diagnostics, inferred source plugin names, and acknowledged per-value repair/preserve recovery for .wmp imports.

* Submission review: respect the host PHP error-display configuration and use explicit HTTPS URL sanitization.


== Optional external services ==

Google Drive is an opt-in off-server destination. Nothing is sent to Google until an authorized administrator connects their own Google OAuth web application and consents to uploads. New completed archives are then copied automatically; older copies require an explicit action. Archives include site files, database records, personal data and stored secrets. They are not encrypted by MigratePilot. OAuth credentials and resumable session URLs are encrypted in installation-bound private storage, outside the public site, and are never included in backup archives.

The integration contacts accounts.google.com for authorization, oauth2.googleapis.com for token exchange and refresh, and www.googleapis.com for folder creation, resumable archive upload, and size/identity checks. It requests only the drive.file scope for application-created files. No sharing permissions are created. Google terms: https://policies.google.com/terms . Google privacy policy: https://policies.google.com/privacy . Google API services user data policy: https://developers.google.com/terms/api-services-user-data-policy .

Local retention does not delete Google Drive files. Manage cloud retention and quota in your Google account. Pending or failed transfers are excluded from automatic local retention until completed or explicitly skipped. Disconnect removes local credentials and stops transfers; revoke access separately at https://myaccount.google.com/connections . Existing Drive files remain. Changing WordPress salts invalidates stored credentials and requires reconnection.

Email alerts are also opt-in. WordPress sends the configured recipient a site hostname and issue category, never an archive or setting contents. Alerts rely on WordPress cron/admin visits and the site's mail transport. Acceptance by wp_mail does not prove inbox delivery. A complete server outage cannot be detected by this local monitor.

== Backup planning and restore rehearsals ==

The storage estimate uses the most recent completed archive plus 20 percent headroom, desired copy count, protected copies, and a configurable disk reserve. It is a planning estimate, not a guarantee or a filesystem quota measurement. Unknown free space is shown for review.

Restore rehearsal guides administrators to import into a separate, isolated test site, disable live outbound integrations, and check sign-in, content and critical features. Results are administrator-reported, not an automated restore certification. No production replacement is started by the checklist.

== Current validation scope ==

The Google Drive integration is a preview: automated tests intercept Google HTTP and email. A real-account upload/download/restore rehearsal is still required before relying on it for recovery. Use your own Google Cloud project with Drive API enabled, a web application client, and the exact HTTPS redirect URI shown in Backups. Testing-mode OAuth grants may expire; configure your Google consent application for ongoing use. Keep independent backups during evaluation.

= Updating a pre-directory test installation =

The main plugin file is now migratepilot.php. Finish or cancel pending operations, deactivate the previous test build, replace it with this ZIP and activate MigratePilot again. WordPress may mark the old entry inactive because its main filename changed. Existing backup files are not removed by deactivation. Do not keep both main PHP files in the plugin directory.
