=== Fiction Drafts ===
Contributors: david-gaitan
Tags: backup, export, database, archive, download
Requires at least: 6.4
Tested up to: 7.1
Stable tag: 0.1.0
Requires PHP: 8.1
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Back up your site to downloadable zip volumes as resumable background jobs that never time out. Export only, so it never writes to your site.

== Description ==

Fiction Drafts copies a WordPress site — the database, the files, or both — into one or more `.zip` volumes you can download.

The work runs as a resumable background job. Each stage does a bounded amount of work, records where it got to, and hands control back. A backup of a large site therefore never depends on one long request surviving: if PHP hits its time limit, the next pass picks up from the recorded position instead of starting again.

= Export only. There is no restore =

This plugin has no import path, no unzip-into-place, no URL rewriting, and it runs no `UPDATE` or `DROP` against your content. That is not a missing feature on a roadmap — it is the boundary the plugin is built around, and it is enforced by an automated test that scans the source for the constructs a restore path would need and fails the build if one appears.

The practical consequence: installing this plugin cannot damage your site's data, because nothing in it is capable of writing to your site's content.

= What it does =

* **Five profiles** — Everything, Database only, Files only, Files without media, or Custom (tick the areas yourself).
* **A five-stage pipeline** — "Checking there is room", "Exporting the database", "Scanning files", "Building the archive", "Finishing up". The admin screen names the stage it is on rather than showing an unexplained bar.
* **Split volumes** with a size you choose, so a multi-gigabyte site does not become one file your host refuses to serve.
* **A SHA-256 checksum per volume**, plus a manifest recording what went into the archive — WordPress version, PHP version, active plugins, table list, byte counts.
* **Resumable downloads.** The download endpoint honours HTTP `Range`, so a connection that drops at 80% resumes rather than restarting. A truncated zip is a file that will not open; verifying the checksum tells you which case you are in.
* **A retention sweep** that keeps the newest N backups and deletes the rest, or keeps everything when set to `0`.

= How it differs from other backup plugins =

The directory has many backup plugins. This one makes four choices most of them do not:

1. **It only exports.** No restore path exists at any version, which removes the entire class of failure where a backup plugin damages the site it was protecting.
2. **`wp-config.php` is excluded by default.** Including the file that holds your database password and all eight authentication salts is a decision you make per backup, deliberately — not a setting you turn on once and forget.
3. **Download links are single-use, expiring, and stored hashed.** The plugin's own job is to put `wp_options` into a downloadable archive, so a download credential kept in plaintext would be copied — still valid — into the very backup it authorises. Only `sha256` of the token is ever stored.
4. **The archive is verifiable.** Per-volume checksums and a manifest mean you can tell a good copy from a truncated one without opening it.

= Privacy: no external services =

Fiction Drafts does not contact any external server. It sends no telemetry, registers no account, makes no API calls, loads no remote fonts, scripts or images, and requires no key. Every byte it produces stays on your own filesystem until you download it. There is no data for a privacy policy to describe because none leaves your site.

= Security =

Archives are written to `wp-content/fiction-drafts-{32 random hex characters}/`, a directory name generated once at activation and never guessable. Beside the archives the plugin writes an `index.php`, an empty `index.html`, and an `.htaccess` denying all access.

**Please read this if you run nginx:** `.htaccess` is an Apache file, and nginx does not read it. On nginx the random directory name and the fact that no index is served are what protect the archives, and that is defence by obscurity, not by configuration. The FAQ below has the `location` block to add if you want a real deny rule.

Downloads are served by PHP rather than by a public URL. Each one requires a fresh single-use token bound to the user, the job and the volume, valid for five minutes, and every path is resolved through a containment check that refuses symlinks and anything outside the storage directory.

= Source code and build tools =

The admin screen is a React application. The file shipped in `build/` is compiled and minified by `@wordpress/scripts`; the readable source is `assets/app/` in the public repository:

**https://github.com/dgaitan/Fiction-Drafts**

To rebuild it from source:

`git clone https://github.com/dgaitan/Fiction-Drafts.git`
`cd Fiction-Drafts`
`composer install --no-dev`
`bun install && bun run build`

The repository also builds the exact distributable zip with `bun run package`, which verifies its own output before reporting success.

= Bundled libraries =

Action Scheduler (GPLv3) is bundled as this plugin's own Composer dependency, so background processing never depends on WooCommerce or any other plugin being installed. `psr/container` (MIT), `react-router` (MIT) and `@tanstack/react-query` (MIT) are also included. All are GPL-compatible. React itself is not bundled — the plugin uses the copy WordPress ships.

== Installation ==

1. Upload the plugin through **Plugins → Add New → Upload Plugin**, or extract it to `wp-content/plugins/fiction-drafts/`.
2. Activate it. Activation creates the storage directory and its random suffix.
3. Go to **Fiction Drafts** in the admin menu, pick a profile, and start a backup.

Requires PHP 8.1 or later and WordPress 6.4 or later.

== Frequently Asked Questions ==

= Can this restore a backup? =

No, and it never will. There is no import path in the plugin, and an automated test fails the build if one is added. Use the archives with any tool you like — they are ordinary zip files containing an ordinary SQL dump.

= Does it include wp-config.php? =

Not unless you tick the box for that specific backup. That file holds your database password and all eight authentication salts, so including it is a per-backup decision rather than a saved preference.

= I run nginx. Are my archives exposed? =

The `.htaccess` the plugin writes does nothing on nginx. Add this to your server block for a real deny rule, replacing the suffix with your own directory name:

`location ~ ^/wp-content/fiction-drafts-[a-f0-9]{32}/ { deny all; return 404; }`

Without it, the protection is the unguessable directory name and the absence of a directory index. The download endpoint itself does not rely on this — it serves bytes through PHP behind a capability check and a single-use token.

= My download stopped halfway. Do I have to start over? =

No. The endpoint supports HTTP `Range`, so any download manager that resumes will resume. Compare the finished file against the SHA-256 shown for that volume to confirm it is complete.

= Why is the backup split into several files? =

Because you asked for a volume size, or because the default one applied. Very large single files are refused or truncated by a fair number of hosts and browsers. Each volume is a complete, independently checksummed zip.

= Does it work on multisite? =

Not yet. On a network install the screen and every endpoint require `manage_network_options`, so it will not half-work for a site administrator. Full multisite support is planned for a later version.

= Does it send anything anywhere? =

No. It makes no external requests of any kind. See the Description for the full statement.

= Will it slow my site down? =

The work runs through Action Scheduler on background requests, in bounded slices. Nothing runs on your site's front end — the plugin registers no front-end hooks at all.

= What happens when I uninstall it? =

`uninstall.php` removes the plugin's own tables, options and storage directory. It deliberately leaves the `actionscheduler_*` tables alone, because those are shared with every other plugin that bundles the same library.

== Screenshots ==

1. The backup list — date, profile, size, volume count, and whether the archive contains credentials.
2. Starting a backup. Five profiles, and wp-config.php off unless you tick it for this one backup.
3. A running job names the stage it is on and counts real work, instead of showing an unexplained bar.
4. Settings — default profile, extra exclusions, maximum volume size, and how many backups to keep.

== Changelog ==

= 0.1.0 =
* First release.
* Five backup profiles: Everything, Database only, Files only, Files without media, Custom.
* Resumable five-stage pipeline built on a bundled Action Scheduler.
* Split volumes with a SHA-256 checksum and a manifest for each.
* Single-use, expiring, hashed download grants; downloads support HTTP `Range` resume.
* Retention sweep with a keep-newest-N setting.
* `wp-config.php` excluded by default, opt-in per backup.

== Upgrade Notice ==

= 0.1.0 =
First release.
