Software Distribution

Publish versioned software releases, configure secure vault storage, and distribute updates to entitled customers

Overview & security scope

WooNooW Software Distribution manages software releases, changelogs, private artifact storage outside the public webroot, SHA-256 integrity verification, and short-lived one-time download authorization.

The architecture is product-generic: authorization and storage logic make no assumptions about product names, editions, or client frameworks. It serves WordPress plugins, themes, desktop binaries, CLI tools, and arbitrary clients capable of communicating via the website-identity and package protocol.

Local artifact protection allows ZIP files by default; trusted deployments may extend allowed source extensions via the woonoow/software/allowed_artifact_extensions filter. Direct ZIP upload is the default release publication mechanism: operators choose an ordinary ZIP file from their workstation and publish immediately into secure storage, without routing packages through the public WordPress Media Library or configuring pre-existing WooCommerce downloadable file rows.

Zero-setup automatic secure storage & dual storage architecture

WooNooW Software Distribution is designed to work immediately from the WordPress admin dashboard without requiring SSH access, terminal commands (mkdir/chmod), manual directory path entry, or cryptographic key generation. When the module is activated, WooNooW automatically selects and initializes the most secure viable local storage mechanism:

graph TD
    A[Admin Publishes Release ZIP] --> B{Outside Webroot Vault Viable?}
    B -->|Yes: Verified & Writable Path Outside Document Root| C[Private Filesystem Vault]
    B -->|No: Root-Owned Parent / Unverified Webroot / Shared Hosting| D[Encrypted-at-Rest Storage Fallback]
    C --> E[Plaintext ZIP in Private Vault Directory]
    D --> F[Authenticated Encryption: Sodium Secretstream XChaCha20-Poly1305]
    F --> G[Ciphertext .wnwenc in Writable WP Storage]
    E --> H[Authorized One-Time Token Download]
    G --> H
    H --> I[PHP Streams Decrypted Original Bytes to Client]
  1. Private Filesystem Vault (Outside Webroot — Primary when viable):

    • Used when a writable directory outside the server document root is verified and accessible.
    • Canonical packages are placed outside the webroot (e.g. /srv/woonoow-vault/products/{productId}/{filename}).
    • Direct web server requests cannot reach the directory because it sits outside every checked web document root.
  2. Encrypted-at-Rest Storage (Within Writable WP Storage — Automatic Fallback):

    • Automatically activates when the outside-webroot vault is not viable (e.g. managed hosting environments like notif.in where the parent directory above webroot is owned by root:root, environments with open_basedir restrictions, or hosts where DOCUMENT_ROOT is unverified).
    • Stores artifacts inside WordPress's standard writable directory (wp_upload_dir()['basedir'] . '/woonoow-vault'), which is guaranteed writable in functional WordPress installations without SSH or permission adjustments.
    • Authenticated Encryption at Rest: Packages are encrypted using Sodium Secretstream XChaCha20-Poly1305 (WNWENC1 format) with 64 KB chunking.
    • Never Plaintext in Public Storage: Uploaded packages are stream-encrypted immediately during ingestion into private staging. Plaintext files are never staged, cached, or written into public or web-accessible folders.
    • Server-Managed Key Isolation: Encryption keys (256-bit) are stored in the database (wp_options, woonoow_software_vault_key) with autoload = 'no' (or overridden via WOONOOW_SOFTWARE_ENCRYPTION_KEY in wp-config.php). Keys are never stored in files or within the public uploads directory, and are explicitly queried from the database only when software publishing, download streaming, or storage diagnostics require them.
    • Streaming Decrypted Original Bytes: When an entitled client redeems an authorized one-time token, PHP stream-decrypts the package chunk-by-chunk in bounded memory, delivering the exact original uncorrupted bytes with verified original SHA-256 and byte length.
    • Coexistence without Forced Migration: Existing outside-webroot vault artifacts are preserved intact; both drivers coexist in the same database without forced or automatic cross-migration.

The public webroot bypass problem & protection limitations

Files uploaded to standard WordPress locations (wp-content/uploads/) are served directly by web servers (Nginx, Apache, LiteSpeed) or cached by CDNs without invoking PHP or WooCommerce access controls. Adding a .htaccess rule, a database permission row, or a custom query parameter does not guarantee security across diverse hosting environments.

For local delivery, WooNooW stores the canonical release binary in a private filesystem vault located outside every document root that PHP can verify. When an entitled client requests an update, PHP streams the package from the vault through a single-use, short-lived token URL rather than exposing a direct file path or Media URL.

Storage modes & operational split

Software Distribution enforces a strict, honest operational split between store merchants, client software updaters, and hosting infrastructure:

Operational DimensionMerchant (Store Operator)Client Software (Updater / End-User)Hosting Provider (Infrastructure & Server)
Primary InteractionWordPress Admin SPA UI (Products → Software Versions). Picks ZIP from computer, enters version and changelog, clicks publish.Communicates exclusively via REST API (POST /software/package and GET /software/download?token=...).Runs PHP 7.4+, web server (Nginx/Apache), MySQL database, and local filesystem storage.
Server Credentials RequiredNone. No SSH, no terminal access, no chmod, chown, or mkdir permissions needed.None. Client only needs their active license key and website identity pair (installation_id + site_url).Server administrator / hosting control panel sets PHP limits and basic filesystem ownership.
Storage Driver AwarenessAutomatic. Admin sees storage mode indicator (private_vault or encrypted_at_rest). May override via settings.Completely unaware. Client updater always receives decrypted original ZIP bytes with matching SHA-256.File path boundaries: outside webroot (if parent writable) or standard writable uploads directory (wp_upload_dir()).
Cryptographic Key ManagementAutomatic. 256-bit vault key generated atomically via add_option on first encrypted release; stored in database.Completely isolated. Clients never handle, receive, or request encryption keys.Optional key override via WOONOOW_SOFTWARE_ENCRYPTION_KEY in wp-config.php or environment variable.
Failure ResponsibilityEnsures database and files are backed up together. Fixes catalog conflicts or duplicate versions.Retries transient network failures; re-requests package tokens when expired.Ensures PHP process has write permission to uploads folder, enables ext-sodium, audits reverse proxy/CDN caches.

Private vault prerequisites (when using outside-webroot storage)

If you explicitly configure or run the dedicated Outside-Webroot Private Vault (storage_driver: local), ensure your hosting environment satisfies these prerequisites. (If your environment does not permit placing files outside webroot, WooNooW automatically runs the encrypted-at-rest driver within writable storage, bypassing these requirements completely):

  1. Configured Vault Path: By default, WooNooW creates and uses woonoow-vault as a sibling directory placed beside (never beneath) the verified document root (e.g. /srv/woonoow-vault). You may override this path using the WOONOOW_SOFTWARE_STORAGE_PATH environment variable or PHP constant in wp-config.php.
  2. Verified Document Root: WooNooW evaluates $_SERVER['DOCUMENT_ROOT']. If your environment runs behind a reverse proxy, in Docker containers, or uses symlinked release paths where DOCUMENT_ROOT is unverified or inaccurate, define WOONOOW_SOFTWARE_DOCUMENT_ROOT in wp-config.php:
    php
    define('WOONOOW_SOFTWARE_DOCUMENT_ROOT', '/srv/www/public');
    
  3. Strict Filesystem Boundary: The vault path must be an absolute path strictly outside every verified document root and public WordPress directory (ABSPATH). Paths inside public roots are rejected immediately.
  4. No Symbolic Links: The vault path, its parent directories, and internal product folders must not be symbolic links.
  5. Safe File and Directory Permissions:
    • Vault root and product directories: mode 0750 (or stricter), owned and writable by the web server process (e.g. www-data).
    • Staged packages and internal index guards: mode 0640.
    • WooNooW writes a defense-in-depth index file (<?php // Silence is golden\n) with mode 0640 inside the vault and staging directories upon creation. Filesystem placement outside the webroot remains the primary security boundary.
  6. Diagnostics Verification: Verify storage health at any time by inspecting the REST endpoint GET /wp-json/woonoow/v1/software/storage/status or viewing storage indicators in the Admin SPA.

Encrypted-at-rest storage architecture & container format

When running in standard shared hosting, managed WordPress hosts (such as notif.in), or environments where the parent directory above webroot is owned by root and unwritable by PHP, WooNooW uses Encrypted-at-Rest Storage.

Container binary specification (WNWENC1)

Encrypted packages on disk (.wnwenc) use a compact, versioned, tamper-proof authenticated container:

text
+-----------------------------------------------------------------------------------+
| Container Header (40 bytes)                                                       |
|   Magic: "WNWENC" (6B)                                                            |
|   Version: 0x01 (1B)                                                              |
|   Cipher Suite: 0x01 (1B = Sodium Secretstream XChaCha20-Poly1305)                |
|   Chunk Size: 0x00010000 (4B BE uint32 = 65,536 bytes / 64 KB)                    |
|   Reserved / Flags: 0x00000000 (4B)                                               |
|   Secretstream Header: 24 bytes (Cryptographic salt/nonce context)                |
+-----------------------------------------------------------------------------------+
| Chunk 1 Ciphertext (up to 65,553 bytes) [Plaintext + 1B internal tag + 16B MAC]   |
| Chunk 2 Ciphertext (up to 65,553 bytes) [Plaintext + 1B internal tag + 16B MAC]   |
| ...                                                                               |
| Final Chunk Ciphertext [Plaintext + 1B TAG_FINAL + 16B MAC]                       |
+-----------------------------------------------------------------------------------+

Cryptographic properties & security guarantees

  • AEAD Authentication: Every 64 KB chunk carries a 16-byte Poly1305 MAC computed with additional authenticated data (WNWENC1). Any modified byte, altered bit, or corrupted sector is detected immediately upon reading.
  • Truncation & Drop Protection: The final chunk is tagged with SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL. If an incomplete or truncated file is read, the stream fails authentication and aborts; truncated packages are never delivered to clients.
  • Reordering & Replay Immunity: Libsodium Secretstream maintains an internal sequential counter state across chunks. Chunks cannot be reordered, swapped, or omitted.
  • Bounded Memory Consumption: Decryption buffers only 64 KB in memory at any point. Streaming multi-gigabyte files requires under 1 MB of PHP memory.
  • Decrypted Original Bytes Guarantee: The output stream matches the original unencrypted archive byte-for-byte. The package SHA-256 hash reported in the X-Package-Sha256 HTTP header and the Content-Length header strictly reflect the unencrypted release binary.

Key management rules (fail-closed)

  • Database Key Storage: The active key is stored in wp_options under option woonoow_software_vault_key as a 32-byte binary key (hex-encoded) with autoload = 'no'. Because autoload is disabled, WordPress does not query or load the key into memory during normal page views, cart/checkout operations, or unrelated REST API requests. It is explicitly queried from the database only when software release publishing, download streaming, or storage diagnostics require it.
  • Lazy Automatic Initialization: The encryption key is not generated during plugin installation or module activation. It is generated lazily and automatically upon the merchant's first encrypted release publication (using atomic add_option to eliminate concurrent creation races). Store merchants require no SSH access, key generation tools, or server-level setup—ordinary product management and ZIP uploads work out of the box with zero extra merchant work.
  • No Public Key Files: Encryption keys are never written to disk files, never placed in wp-content/uploads/, and never exposed in client API responses.
  • Fail-Closed on Missing or Corrupt Keys: If encrypted releases exist in the database or encrypted ciphertext files (.wnwenc) exist on disk in the vault, but the encryption key is missing or corrupted, WooNooW strictly fails closed with HTTP 500 encryption_key_missing or encryption_key_corrupted.
  • No Silent Regeneration: WooNooW never silently generates a new key when encrypted releases already exist (either as database release rows or as ciphertext .wnwenc files on disk). Silently regenerating a key would permanently orphan and corrupt existing encrypted releases. A new key is generated atomically (using add_option to eliminate concurrent creation races) only when zero encrypted releases exist in the store and vault.
  • Server Override: Operators may define WOONOOW_SOFTWARE_ENCRYPTION_KEY (32-byte raw or 64-character hex) in wp-config.php to manage keys outside the database.

Honest backup, restore, and migration model

Because WooNooW Software Distribution couples database records with filesystem artifacts, administrators must observe an honest, disciplined backup strategy:

graph LR
    subgraph Synchronized Backup Pair
        DB[(MySQL Database)]
        FS[Filesystem Storage]
    end
    DB -->|Holds Keys, Versions, Entitlements| R1[Recovery Point]
    FS -->|Holds Encrypted or Vault Binaries| R1

1. Coupled backup requirement (Database + Files together)

  • The Database holds: Software version rows, release metadata, SHA-256 checksums, entitlement snapshots, and the server-managed encryption key (woonoow_software_vault_key).
  • The Filesystem holds: The encrypted packages (wp-content/uploads/woonoow-vault/products/{id}/{name}.wnwenc) or vault packages (woonoow-vault/products/{id}/{name}.zip).
  • Restoration consequence:
    • Restoring files without database: Download tokens cannot be issued or claimed. The releases do not exist in WooCommerce.
    • Restoring database without files: Download token issuance succeeds, but redemption fails with 404 artifact_not_found.
    • Restoring older database with newer files: Newer files will be unrecognized by the older database. A subsequent upload of the same version safely reuses the verified on-disk artifact.
    • Restoring older files with newer database: If an encryption key was changed or files are missing, redemption fails closed with encryption_key_missing or checksum mismatch errors.

2. Migrating between servers or hosting providers

When migrating your store from one host to another:

  1. Export the complete MySQL database.
  2. Copy the entire WordPress uploads directory, specifically including wp-content/uploads/woonoow-vault (for encrypted storage) or the external woonoow-vault directory (for private vault storage).
  3. Ensure the destination server has the PHP sodium extension enabled (ext-sodium, bundled in PHP 7.2+).
  4. If moving from outside-webroot storage to shared hosting (or vice-versa), existing release rows retain their assigned storage_driver (local or local_encrypted). WooNooW does not automatically alter or force-migrate existing records. Both storage types operate simultaneously without conflict.

Comprehensive threat model & security boundaries

WooNooW Software Distribution implements multiple overlapping security boundaries. This matrix defines what the security architecture protects against and where hosting audits remain necessary:

Threat / Attack VectorProtection MechanismSecurity Boundary / Residual Risk
Direct Web Server Bypass (attacker visits /wp-content/uploads/woonoow-vault/... directly via browser or CDN)Cryptographic at rest. The file on disk is authenticated ciphertext (WNWENC1). Even if the web server serves the file directly, the attacker obtains unreadable encrypted bytes.Files cannot be decrypted without the 256-bit database key. Defense-in-depth .htaccess and web.config deny access; Nginx requires standard vhost protection for uploads.
Shared Hosting / Multi-tenant Directory TraversalNormalized relative path validation (strpos($cleanKey, 'products/' . $product_id . '/') === 0), is_path_within() jail checks, null-byte rejection, and symlink rejection.Other tenants on the same server without PHP user isolation could read filesystem files, but cannot decrypt without the database key from wp_options.
Ciphertext Tampering / Bit-flippingLibsodium Secretstream Poly1305 MAC on every 64 KB chunk.Any modified bit fails MAC verification immediately. Streaming terminates and download aborts.
Archive Truncation / Drop AttacksAuthenticated TAG_FINAL tag enforced at stream completion. Decrypted byte count and final SHA-256 verified against immutable release record.Truncated files fail authentication and terminate before completion.
Token Theft / Replay AttacksDownload tokens are single-use bearer tokens bound to a persistent installation_id, domain_normalized, and release version. Stored as SHA-256 hashes. Claimed atomically within a database transaction (UPDATE ... WHERE used_at IS NULL).Tokens expire in 5 minutes (default). Once claimed or expired, repeated requests return 403 token_consumed. Range and HEAD requests are rejected to prevent token burn.
Public Source Leakage via Legacy MediaRelease publication checks whether the artifact file resides in the public webroot. If unencrypted copies exist in Media, publication halts with 409 source_still_public unless destructive authorized deletion (delete_public_source) is approved and references are verified clear.Direct ZIP upload bypasses Media Library entirely, eliminating public source exposure at the root.
Edge CDN / Reverse Proxy CachingOne-time token URLs return Cache-Control: no-cache, must-revalidate and Pragma: no-cache. Single-use tokens expire immediately.Hosting administrators must ensure reverse proxies (Cloudflare, Fastly, Varnish, Nginx proxy_cache) do not cache /wp-json/woonoow/v1/software/download* requests.
Web Server AliasesStorage diagnostics report status: "host_verification_required" because PHP cannot inspect web server virtual host alias directives.Operators must complete the host verification audit (probing direct URLs externally).

Architecture & product modeling

Install and enable WooNooW once on the selling WordPress store. That one store-level installation can manage many software products and releases. Within it, distribution maps directly to the WooCommerce catalog:

  • One parent product per authorization boundary and release stream: Each independently licensed software product or edition should have its own parent WooCommerce product and unique software slug.
  • Variations determine commercial terms and entitlements, not release streams: Variations belonging to one parent (such as annual/lifetime purchase options or different activation limits) can determine pricing, usage duration, activation limits, update windows, and support rights. They do not create independent software slugs or release streams.
  • Artifact origin flexibility within one parent: A release source may be selected from a simple parent product or any variation owned by a variable parent. The resulting release always belongs to the parent stream. If all variations receive the same binary, attach it to one chosen variation and reference that artifact when publishing the parent release; do not publish one release per commercial variation.
  • Separate parent products remain separate: If two parent products intentionally use the same bytes, each still needs its own authorized release record because product entitlement and vault storage are parent-scoped. A license for one parent never authorizes the other. Avoid pointing two parents at one public source that must be deleted: the shared-reference audit will correctly block deletion until each dependency is migrated.

Illustrative catalog example

Consider a vendor selling two editions of an application with different feature sets:

text
Product 1 (Parent): "Product Standard Edition"
  ├── Slug: standard-edition
  ├── Software Releases: v1.0.0, v1.1.0, v1.2.0 (Single stream)
  └── Variations:
        ├── "Personal Annual" (1 site, 1 year of updates)
        └── "Team Lifetime"   (5 sites, usage has no expiry; update policy configured separately)

Product 2 (Parent): "Product Enterprise Edition"
  ├── Slug: enterprise-edition
  ├── Software Releases: v2.0.0, v2.1.0 (Separate stream)
  └── Variations:
        ├── "Enterprise Annual" (Unlimited sites, 1 year of updates + support)
        └── "Enterprise 3-Year" (Unlimited sites, 3 years of updates + support)

Note: The labels "Standard Edition", "Enterprise Edition", "Personal Annual", and "Team Lifetime" are arbitrary merchant catalog data, not hardcoded plugin behaviors. You may model your catalog using simple or variable products to suit your business needs.

Module setup & dependencies

Both Software Licensing and Software Distribution are disabled by default.

To enable them:

  1. Navigate to WooNooW → Settings → Modules.
  2. Enable Software Licensing (licensing) when distributing protected software.
  3. Enable Software Distribution (software).

Dependency and fail-closed behavior

The relationship between Licensing and Software Distribution is strictly enforced:

  • Public software: Products configured with _woonoow_licensing_enabled: "no" can distribute packages without requiring active licenses or the Licensing module.
  • Protected software: Products configured with licensing enabled (or missing/invalid licensing meta, which defaults to protected) require the Licensing module.
  • Fail-closed protection: If Software Distribution is enabled while Licensing is disabled, requests to check updates, fetch package tokens, or download protected software fail closed with HTTP 503 licensing_unavailable. WooNooW never downgrades a protected product to public access when Licensing is inactive.

Product configuration workflow

To prepare a product for software distribution:

  1. Assign a Unique Stable Slug to the Parent: Enter a unique slug in the parent product's Software Slug field (_woonoow_software_slug). Product resolution is fail-closed: the slug must match exactly one published parent product. If no product matches, WooNooW returns product_not_found; if duplicate published products share a slug, it returns software_slug_ambiguous.
  2. Enable Software Updates: Check Enable Software Updates (_woonoow_software_enabled).
  3. Confirm Licensing Policy: Ensure Enable Licensing (_woonoow_licensing_enabled) is checked unless the product is deliberately free and public.
  4. Configure WordPress Integration (Optional): If distributing a WordPress plugin or theme, check WordPress Plugin/Theme (_woonoow_software_wp_enabled) and provide:
    • Requires WP (_woonoow_software_requires_wp, e.g. 6.4)
    • Tested WP (_woonoow_software_tested_wp, e.g. 6.7)
    • Requires PHP (_woonoow_software_requires_php, e.g. 7.4)

Save the product to persist these settings.

Filename templates & output conventions

WooNooW standardizes package naming when creating vault copies.

  • Default template: {slug}_v_{version}.zip
  • Available template variables:
    • {slug} — The product software slug.
    • {version} — The release version string.
    • {product_id} — The WooCommerce parent product ID.
    • {name} — The sanitized product title.
    • {ext} — The package file extension (typically zip).

Merchants can configure a site-wide template under WooNooW → Settings → Modules → Software Distribution, or specify an override per product via _woonoow_software_filename_template (e.g. {slug}-app-v_{version}.zip).

Output organization boundary

Filename templates exist solely for merchant file organization and client header presentation. Clients, automated updaters, and third-party scripts must never infer a storage bucket, private vault path, Media Library URL, or target version string from the filename structure.

Release publication standard operating procedures

WooNooW provides two distinct local publication workflows: the modern Direct ZIP Upload (default) and the Legacy WooCommerce Download Migration workflow.

Primary SOP: Direct ZIP Upload (Local Vault - Default)

Use this default procedure to publish a software release directly from a local .zip file on your computer.

Step 1: Open Software Versions

  1. Navigate to WooNooW → Products → Software Versions.
  2. Select your software product from the list.
  3. Click New Version.

Step 2: Configure release details & choose file

  1. Ingestion Mode: Defaults to Direct ZIP Upload (Local Vault - Default).
  2. Package ZIP File: Click the file picker (Choose File) and select the release .zip archive from your workstation.
  3. Version Number: Enter a consistently ordered version string, such as 1.2.0. Semantic Versioning is recommended, while core ordering uses PHP version_compare(). Once published, the version string, vault key, and checksum are immutable.
  4. Live Template Preview: Inspect the real-time preview card showing:
    • Software Slug: The product's configured slug.
    • Product ID: The WooCommerce parent product ID.
    • Target Vault Filename Preview: The rendered filename (e.g. my-plugin_v_1.2.0.zip) according to the active filename template.
  5. Custom Filename Override (Optional): If you need a specific target filename, enter it in the custom override field. WooNooW validates the name against the product's filename template pattern and sanitizes directory traversal sequences (.., /, \, \0). Leave empty to use the standard template.
  6. Changelog: Enter an optional narrative overview and add structured change points (ADD, FIX, CHANGE, REMOVE, SECURITY, DEPRECATE).
  7. Set as Current Release: Ensure Set as current release is checked (enabled by default) if this version should immediately become the active release for update checkers.

Step 3: Publish release

Click Add Version. The Admin SPA builds a multipart payload and streams the archive directly to POST /wp-json/woonoow/v1/software/products/{product_id}/versions.

Under the hood: Direct upload guarantees

  • No WooCommerce Artifact ID Required: Direct upload requires no pre-existing WooCommerce downloadable file entry or manual artifact_download_id lookup. The server generates a unique UUIDv4 (artifact_download_id) automatically upon ingestion.
  • Automatic SHA-256 Checksum & File Sizing: The server streams the staged package into {vaultDir}/products/{productId}/{filename}, calculating the SHA-256 hash and byte size in-flight. No client-side checksum entry is required.
  • Master Client File Unaffected, PHP Temp Consumed: The master archive on your workstation is completely unaffected. On the server, standard PHP HTTP upload places the file in temporary storage ($_FILES['file']['tmp_name']). WooNooW verifies is_uploaded_file(), stages it into private vault staging ({vaultDir}/staging/{uuid}.zip) via move_uploaded_file() (which safely consumes PHP's temporary file), applies safe permissions (0640), verifies generic ZIP integrity, copies exclusively into the product vault, and unlinks the staging file immediately.
  • Generic ZIP Structure & Integrity Validation: WooNooW validates generic ZIP structure:
    • Inspects binary magic headers (PK\x03\x04, PK\x05\x06, PK\x07\x08).
    • Opens and validates archive consistency with ZipArchive::open().
    • No WordPress Header Inspection: The archive validator treats ZIP packages as generic opaque software bundles (supporting WordPress plugins, themes, desktop applications, CLI binaries, or arbitrary ZIP archives). It does not inspect or enforce WordPress-specific plugin header comments or require specific PHP files.
  • Staging Cleanup & Safe Vault Retention on Failure:
    • Staging Temporary Files Always Cleaned Up: Temporary staging files ({vaultDir}/staging/{uuid}.zip) are unlinked immediately following ingestion into the product vault or upon validation failure.
    • No Eager Deletion of Ingested Vault Artifacts: If version publication fails (whether on pre-draft conflicts like duplicate versions or mid-transaction commit rollbacks), successfully ingested vault artifacts are preserved safely in the private product vault rather than eagerly deleted. This eliminates concurrency races where one failing upload could delete the artifact out from under another simultaneous upload.
  • Safe Retry & Verified Reuse: On publication retries or subsequent versions using identical packages, WooNooW verifies the SHA-256 and byte size against the existing vault file and safely reuses the verified artifact.
  • Zero Absolute Path Exposure: Failure error responses return safe recovery metadata (storage_key, recovery_action) without leaking server-internal absolute filesystem paths.
  • Signatures Optional & Nullable (No Input Support): The database schema includes columns for signature (text, nullable), signing_key_id (varchar, nullable), and signed_at (datetime, nullable), and API response payloads include these fields (defaulting to null). However, neither the direct multipart upload endpoint nor the Admin UI currently provides input fields or supports passing cryptographic signatures or signing keys during version publication. Releases are published unsigned with null signature metadata.

SOP: Private Artifact Library (Upload Advance & Select Package)

WooNooW features a dedicated Product-Scoped Private Artifact Library. This workflow decouples package uploading from release publication:

  1. Upload Ahead of Time: Store operators can upload software bundles before deciding on the final release version number or writing changelogs.
  2. Reuse Packages Without Re-uploading: The same build archive can be selected for multiple versions (e.g. testing in a draft, publishing an initial version, and referencing the package in subsequent maintenance releases).
  3. Product-Scoped Privacy: Packages in the artifact library are stored strictly in the product's private vault (or authenticated encrypted vault) outside the public webroot. They are never imported into the WordPress Media Library or accessible without authorization.
  4. Independent Original Filenames: The artifact library preserves the original source filename (e.g. AppBuild-v3.0.0-rc2.zip), while the published release automatically renders client binaries according to the product's distribution filename template (e.g. app-slug_v_3.0.0.zip).
  5. Fail-Closed Cryptographic Key Safety: Unreferenced library packages stored under local_encrypted are automatically detected by SoftwareStorage::has_encrypted_artifacts(). WooNooW strictly prevents accidental or silent key regeneration whenever encrypted library packages exist, ensuring existing bytes remain decryptable.

Step 1: Manage Artifacts via Artifact Library

  1. Navigate to WooNooW → Products → Software Versions.
  2. Select your software product from the list.
  3. Click Artifact Library in the header.
  4. Review existing packages with their original filenames, byte sizes, upload dates, checksum previews, and release usage badges.
  5. To upload a new package, select a .zip archive under Upload New Package to Library. WooNooW streams the file directly to private vault staging, verifies generic ZIP integrity, computes the SHA-256 checksum, and adds it to the product's durable library.
  6. Click Release as Version on any package to immediately launch the New Version modal with that package selected.

Step 2: Publish a Release Using an Uploaded Package

  1. In Software Versions, click New Version.
  2. Under workflow options, select Select uploaded package.
  3. Use the search input to filter packages by filename.
  4. Select the desired package from the list. The UI displays the package details, checksum preview, and whether the package is unused or already linked to existing release versions.
  5. Enter the Version Number (e.g. 2.0.0).
  6. Review the Target Vault Filename Preview or specify an optional Custom Filename Override.
  7. Enter the changelog and click Add Version.
  8. WooNooW binds the release to the server-authoritative artifact ID, verifies physical file integrity against the database SHA-256 and size, and publishes the release.

Legacy SOP: Existing WooCommerce Download (Legacy Local Vault)

Use this workflow only when migrating pre-existing downloadable files attached to WooCommerce products or stored in wp-content/uploads/.

Step 1: Prepare the WooCommerce downloadable file

  1. For a simple product, edit the parent, mark it Downloadable, and attach the ZIP under Downloadable files. For a variable product, attach it to one owned variation.
  2. Save the product. WooCommerce assigns a stable download_id (the artifact ID).

Step 2: Configure legacy migration in Software Versions

  1. In Software Versions, click New Version.
  2. Set Ingestion Mode to Existing WooCommerce Download (Legacy Local Vault).
  3. Enter the Version Number and paste the exact WooCommerce download_id into the WooCommerce Download Artifact ID field (artifact_download_id).
  4. Review the Authorize safe deletion of public source file from Media Library (Destructive) checkbox (delete_public_source):
    • Unchecked (delete_public_source: false): Publication from a public Media/uploads source stops with HTTP 409 source_still_public before a deliverable release is created. The original file remains untouched.
    • Checked (delete_public_source: true): WooNooW stages the private vault copy, creates a draft, detaches matching native WooCommerce download entries, audits shared references, and permanently unlinks the public source file.

The legacy migration pipeline under the hood

text
1. Source Resolution ──> 2. Exclusive Vault Copy ──> 3. Insert Draft Release
                                                            │
6. Transactional Publish <── 5. Revalidate Artifact <── 4. Detach WC Downloads
                                                           & Delete Public Source
  1. Exclusive verified copy: Streams bytes from the uploaded file into woonoow-vault/products/{product_id}/{filename}, calculating the SHA-256 checksum in-flight and applying strict permissions (0750 for directories, 0640 for files).
  2. Durable draft creation: Inserts a complete non-current draft row (draft_version_id) into the database.
  3. WooCommerce download detachment: Snapshots _downloadable_files across the parent product and all child variations, then removes all entries matching this source.
  4. Reference auditing and safe deletion: Audits known references (other release rows, post meta, post content, and attachment parent links). If another reference exists, deletion is blocked with 409 source_has_shared_references, the public file remains intact, and detached entries are restored. Only when verified clear, WooNooW unlinks the public file.
  5. Final publication: Revalidates the vault artifact and transactionally publishes the draft.

Draft recovery workflow

If publication halts after draft creation (for example, due to a reference, deletion, validation, or database failure):

  1. The API returns the error along with draft_version_id and recovery_action: "review_release_draft".
  2. The release appears in the Admin UI with a Draft status badge and warning details.
  3. The staged vault copy is preserved; WooNooW does not delete it automatically.
  4. Operators can inspect the warning, resolve the shared reference or click Delete Public Source File when applicable, then use Publish Draft to revalidate and publish without re-uploading the package.

Package streaming (not a Media URL redirect)

WooNooW never simply redirects clients to a WordPress Media URL.

When an entitled client requests an update:

  1. The client calls POST /wp-json/woonoow/v1/software/package with its license key and installation UUID + domain.
  2. WooNooW verifies entitlements and mints a short-lived bearer token URL (default TTL: 5 minutes).
  3. The client calls GET /wp-json/woonoow/v1/software/download?token={token}.
  4. WooNooW atomically consumes the token in a short database transaction.
  5. PHP streams the package directly from the private vault with headers:
    • Content-Type: application/zip
    • Content-Length: {size}
    • X-Package-Sha256: {sha256}
    • Accept-Ranges: none
    • Cache-Control: no-cache, must-revalidate

Requests with Range headers return 416 range_not_supported, and HEAD requests return 405 head_not_supported, preserving the single-use token from accidental consumption.

External releases SOP

For packages hosted on external infrastructure (such as vendor CDNs or public mirrors):

  1. Create a WooCommerce downloadable file entry containing the external https:// URL.
  2. In Software Versions, click New Version.
  3. Select External Link (Manual download only).
  4. Paste the exact WooCommerce Download Artifact ID into the current text input. The selected entry must belong to the parent or one of its variations and contain the HTTPS URL.
  5. Enter the exact Package SHA-256 (64 lowercase hexadecimal characters). WooNooW strictly requires this checksum at publication time.
  6. Click Release Version.

External delivery constraints

  • Manual download only: External releases set manual_download_only: true. They never issue automatic-updater bearer tokens. The manual receiving workflow should hash the provider's completed bytes and compare them with WooNooW's file_sha256 metadata before installation.
  • Gated manual access: For a protected product, the client calls GET /wp-json/woonoow/v1/software/products/{product_id}/versions/{version_id}/external-link with the license and installation identity. The route returns JSON containing the provider URL; it does not proxy the bytes or automatically redirect the browser. Explicitly public products retain the purchaser-login/admin gate.
  • Boundary limitation: Once the external HTTPS URL is disclosed to the client, it is outside WooNooW's revocable byte-delivery boundary. WooNooW cannot revoke access to third-party URLs or prevent link sharing once exposed.
  • No built-in cloud storage drivers in core: WooNooW core does not contain built-in integrations for Amazon S3, Cloudflare R2, Google Drive, or Microsoft OneDrive. A dynamically generated private/presigned provider URL requires a custom addon contract through the core extension filters; merely storing one expiring URL as a core external release is not a refreshable integration. Public external URLs must never be assumed private.

Release lifecycle: withdrawal and republishing

Withdrawing a release

If a security flaw or critical regression is discovered in a published release:

  1. In Software Versions, find the release row and click the Withdraw button.
  2. Provide a withdrawal reason (e.g. "Critical security advisory CVE-XXXX").
  3. Confirm withdrawal.

Withdrawal semantics:

  • Status changes to withdrawn and withdrawn_at is recorded.
  • The release is immediately removed from current status and excluded from all update checks and package resolution.
  • WooNooW transactionally invalidates all outstanding unclaimed bearer tokens issued for that release.

Republishing a release

To reinstate a withdrawn release or publish a recovered draft:

  1. Click the Republish button on the release row.
  2. Choose whether to mark it as the current release (set_current: true).

Republishing semantics:

  • Preserved publication cutoff: For a withdrawn release, republishing preserves the original published_at timestamp. It does not reset the release date or artificially extend customer update entitlement windows.
  • Invalidated tokens stay dead: Previously invalidated download tokens are never revived. Clients must perform a fresh update check to receive a new package token.
  • Draft publication: For a new draft release, the first successful publication sets published_at to the current UTC timestamp.

Entitlement policies and issuance snapshots

When a protected software release is published, client access is governed by the shared entitlement engine (LicenseEntitlements::authorize_release()).

Review the Entitlements Concept Overview for complete architectural specifications.

Key entitlement dimensions evaluated during release selection:

  1. Usage validity: The license must be active, unrevoked, and within its usage term.
  2. Update cutoff (updates_expires_at): A release published after the cutoff is blocked even when general usage remains active. Once the window expires, pre-cutoff releases are available only when historical access permits them.
  3. Historical access: If the license has historical_downloads: true, all three purposes may receive an otherwise matching release published on or before the update cutoff, even after that window has elapsed.
  4. Purpose equality: Purpose parameters (install, reinstall, update) are checked equally against entitlement cutoffs; a client cannot bypass an expired update right by requesting an install.

Immutable issuance snapshot

Policies are configured at the parent product or variation level via _woonoow_entitlement_policy and can be inspected or updated via REST API:

http
GET  /wp-json/woonoow/v1/licensing/products/{product_id}/entitlement-policy
PUT  /wp-json/woonoow/v1/licensing/products/{product_id}/entitlement-policy

When an order completes, WooNooW snapshots the policy immutably into the license record. Subsequent changes to product catalog policies do not alter rights granted to existing licenses.


SOP: Software Artifact Deletion & Admin-Controlled Retention (P0)

WooNooW provides store operators with complete, transparent control over server storage. Operators can permanently delete unneeded package files—whether unused, associated with historical releases, or even currently active—from the WordPress dashboard without requiring SSH access or raw filesystem knowledge.

Storing every historical software archive indefinitely is neither sustainable nor a policy that software developers should impose on merchants. Growth in package archives consumes local disk space and shared cloud object storage quotas across projects.

  • Revision of the Historical Veto (O-04): Earlier design guidelines forbade cleanup from touching historical releases if customers held reinstall rights. This has been revised: WooNooW never deletes files automatically or silently, but store administrators may permanently delete historical packages after reviewing an honest impact preview.
  • Customer Entitlements as Impact Information: A customer's historical download entitlement is treated as business impact information for the merchant, not an immutable technical lock that forces infinite byte retention. The merchant decides their service policy.
  • Never Eager Delete on Upload/Publish Failure: When version publication or file ingestion fails (due to validation errors, pre-draft conflicts, or mid-transaction rollbacks), successfully staged vault artifacts are safely preserved in the private vault rather than eagerly deleted. This eliminates concurrency races between simultaneous uploads and ensures safe draft recovery. Automatic cleanup applies only to temporary staging files ({vaultDir}/staging/{uuid}.zip).

Withdrawal vs. Physical deletion

Store operators must understand the critical operational difference between withdrawing a release and deleting an artifact:

AttributeWithdrawalPhysical Deletion
Physical file bytesPreserved intact in private vaultPermanently unlinked from disk/storage
Release metadataRetained (marked withdrawn)Retained as an immutable tombstone
DistributionHalted; tokens invalidatedHalted; tokens invalidated; un-redownloadable
Reversible?Yes, can be republishedNo. Deleted bytes cannot be undone without backups
Customer impactTemporarily unavailablePermanently unavailable for install/reinstall

Hapus File is NOT Version Replacement: Deleting an artifact does not erase the release version string from the database. The unique (product_id, version) constraint remains permanently allocated, and release changelogs, publication dates, and SHA-256 checksums are preserved in the audit trail. Publishing updated code requires a new, distinct version number.

Step-by-Step: Deleting artifacts from the Admin UI

Entry point 1: From the Artifact Library

  1. Navigate to WooNooW → Products → Software Versions.
  2. Click Artifact Library in the header.
  3. Locate the package you wish to remove. Review its original filename, physical stored size, and release usage badges.
  4. Click the Delete button (trash icon) on the artifact card or table row.
  5. To delete multiple artifacts, select the checkboxes beside the target packages and click Delete Selected.

Entry point 2: From the Software Versions table

  1. In Software Versions, locate the release row whose package file you want to delete.
  2. Click the action menu and select Delete package file.
  3. WooNooW resolves the bound physical artifact and all other releases that reference the same underlying file.

Tiered impact acknowledgement

Before any physical deletion occurs, WooNooW generates an impact preview modal. Depending on how the file is used, the modal requires explicit confirmation checkboxes:

  1. Unused Packages: Low-friction confirmation displaying the original filename, plaintext ZIP size, and physical stored bytes. Confirms permanent deletion from disk.
  2. Referenced Historical Releases: Displays the list of affected release versions. Requires checking:

    I acknowledge that affected releases will no longer be available for customer download or reinstall, including for members previously entitled to these versions.

  3. Current Release: If deleting the package bound to the active current release, requires checking:

    I acknowledge that the Current Version flag will be removed and no older release will be automatically promoted to current.

  4. Last Remaining Artifact: If no other packages remain for the product, requires checking:

    I acknowledge that no downloadable packages will remain for this product. New installs, reinstalls, and updates will be unavailable until a new package is uploaded.

  5. Shared Physical Packages and Cross-Product Scope: If a single archive is bound to multiple releases or shared across products, the preview modal lists all referencing versions and affected products. Deletion requires the actor to have edit_post permission on all affected products and acknowledge the cross-product impact (cross_product_impact). Cross-product deletion is allowed with all affected products consent/permissions, not an absolute prohibition. Deletion withdraws all associated releases across all affected products while performing exactly one physical file unlink.

Click Permanently Delete File(s) to confirm.

Current release & cutoff semantics

  • No Auto-Promotion: Deleting the current release clears _current_version and the release is_current flag. WooNooW never automatically promotes an older release to current. Operators may explicitly assign another release as current or leave the product without a current version.
  • Strict Cutoff Enforcement (No Free Upgrades): If an entitled customer's license update window expired on a historical version whose package was deleted, the update resolver returns no_eligible_release. WooNooW strictly prohibits falling back to newer releases outside the customer's purchase entitlement.

Physical stored bytes vs. Plaintext ZIP size

The storage summary (GET /software/storage/usage and the UI header) clearly distinguishes between:

  • Stored Bytes: The actual physical disk blocks occupied by the managed file. For unencrypted local files, this matches the file size. For local_encrypted storage, this reflects the .wnwenc ciphertext archive on disk.
  • Plaintext ZIP Size: The uncompressed/plaintext byte size delivered to customers during download streaming.
  • Measurement Confidence: Stored statistics report measurement as known (all objects have verified exact stored byte counts), estimated (some objects fallback to legacy catalog file sizes), or mixed (some objects have unknown sizes where neither stored bytes nor catalog file sizes exist).
  • Encryption Key Safety: When deleting an encrypted artifact (local_encrypted), WooNooW unlinks the .wnwenc file only. The Sodium Secretstream encryption key stored in the database is never deleted, rotated, or regenerated, ensuring other encrypted packages remain decryptable.
  • Corrupt & Missing-Key Deletion: Packages that fail integrity checks or have lost encryption keys can still be deleted if their product vault placement is verified. Decryption is not required to delete unneeded bytes.

Active downloads & POSIX delayed block release

  • File Descriptor Pinning: During download streaming, WooNooW pins open file descriptors.
  • POSIX File System Unlink: When a file is unlinked on Linux/macOS filesystems while an authorized download is in progress, the directory entry is removed immediately, but the physical disk blocks remain allocated until the streaming process closes the file handle. Active downloads complete without interruption.
  • Billing & Backup Latency: Disk blocks are freed by the operating system once handles close. Local server backups, cloud snapshots, and provider storage tier billing operate on independent cycles and do not reflect immediate reductions.

Two-phase journal, retry, and no fake "Undo"

  • Durable Journaling & Idempotency Surviving Preview Expiry: Deletions are coordinated via database journal tables (woonoow_software_deletion_jobs and woonoow_software_deletion_items), decoupling destructive unlinking from short-lived web requests. Replaying an execution request with the same idempotency key and matching scope/preview token returns the committed operation without re-initiating unlinks, even if the preview transient has expired or been consumed.
  • Asynchronous Processing, Retry & Exponential Backoff: Background workers process deletion queues. When an item encounters a retryable storage failure, workers set its status to pending with outcome = 'retryable_failure' and apply an exponential backoff delay (min(300, 2^attempts * 30) seconds). The Retry action re-enqueues both failed items and pending items in retryable_failure outcome, resetting them to pending with 0 attempts for immediate execution.
  • Verified Post-Stat Absence: When using addon storage drivers, both deleted and already_missing outcomes require verified post-stat identity absence via stat_managed_artifact. Missing files and delete markers report 0 removed bytes (delete markers do not count as released bytes). Absence evidence code strictly rejects raw HTTP 4xx/5xx status codes and failure keywords, requiring normalized named strings (not_found or confirmed_absent). WooNooW core contains no cloud storage driver implementation.
  • API Redaction & Storage Key Contract: Absolute filesystem paths are never exposed in API responses, but relative storage_key is retained in baseline artifact and version publication success response contracts.
  • No Undo Button: WooNooW provides no fake "Undo" button or trash bin. Physical bytes are permanently unlinked. Restoring deleted packages requires the operator to restore the archive from external offsite backups and upload it as a new version.

Roadmap boundary: P0 vs. Future P1/P2/Cloud

  • P0 (implemented; automated verification recorded TESTING_CHECKLIST; live host acceptance pending): Manual deletion of unused, historical, current, last, and shared artifacts; two-phase durable journaling; local/encrypted storage deletion; admin impact preview dialog; storage usage summary.
  • P1 (Future Scope): Automatic upload deduplication for identical packages and controlled orphan file reconciliation.
  • P2 (Future Scope): Scheduled automatic retention policies (e.g. retain last N versions or packages older than X months).
  • Cloud Addons (Future Scope): Cloudflare R2 and Amazon S3 direct storage integration via capability hooks.

Storage diagnostics and host verification checklist

Administrators can inspect storage health via REST API (manage_woocommerce capability required):

http
GET /wp-json/woonoow/v1/software/storage/status

A representative response:

json
{
  "driver": "local",
  "effective_driver": "local_encrypted",
  "storage_mode": "encrypted_at_rest",
  "status": "host_verification_required",
  "filesystem_status": "secure",
  "warning": "Releases are stored encrypted-at-rest within writable WordPress storage using authenticated encryption (Sodium Secretstream XChaCha20-Poly1305). Keys are server-managed in the database and never stored in publicly served directories. Downloads stream decrypted original bytes.",
  "encrypted": {
    "configured_path": "/srv/www/public/wp-content/uploads/woonoow-vault",
    "exists": true,
    "writable": true,
    "permissions_safe": true,
    "sodium_supported": true,
    "key_available": true,
    "key_source": "database",
    "cipher_suite": "sodium_secretstream_xchacha20poly1305",
    "status": "secure"
  },
  "local": {
    "configured_path": "/srv/private/woonoow-vault",
    "exists": false,
    "writable": false,
    "permissions_safe": false,
    "is_outside_webroot": true,
    "webroot": "/srv/www/public",
    "wordpress_root": "/srv/www/public",
    "checked_public_roots": [
      "/srv/www/public"
    ],
    "document_root_verified": true,
    "document_root_source": "server_document_root",
    "security_scope": "local_filesystem_only",
    "filesystem_status": "insecure",
    "web_server_aliases_verified": false,
    "cdn_copies_verified": false,
    "status": "insecure",
    "warning": "Protected vault directory does not exist yet."
  },
  "filename_template": {
    "default": "{slug}_v_{version}.zip",
    "available_variables": ["{slug}", "{version}", "{product_id}", "{name}", "{ext}"],
    "example": "software_v_1.0.0.zip"
  }
}

Filesystem status vs. host verification

Notice the distinction between the two status fields:

  • filesystem_status: "secure": Confirms that PHP's filesystem checks passed. The directory exists outside known document roots, permissions are 0750, files are 0640, and no symlinks exist in the path.
  • status: "host_verification_required": Confirms that hosting-level verification remains necessary. PHP cannot inspect web server configuration files (Nginx vhosts, Apache virtual hosts), web server alias directives, proxy/CDN caching rules, or public file URLs that were cached before migration.

Operator host verification checklist

Complete these verification steps before distributing software in production:

  1. Configure the Real Document Root: If your server does not expose an accurate DOCUMENT_ROOT (common in reverse-proxy, Docker, or symlinked deploy setups), define WOONOOW_SOFTWARE_DOCUMENT_ROOT in wp-config.php:
    php
    define('WOONOOW_SOFTWARE_DOCUMENT_ROOT', '/srv/www/public');
    
  2. Verify Vault Directory Placement: By default, WooNooW places woonoow-vault as a sibling directory beside your verified document root. Override with WOONOOW_SOFTWARE_STORAGE_PATH if needed.
  3. Register Additional Public Roots: If your host serves public files from alternate directories (e.g. a static asset domain or upload subdomain), register them using the woonoow/software/additional_public_document_roots filter.
  4. Audit Permissions: Verify that the vault directory is owned by the web server user (e.g. www-data), with directory mode 0750 and file mode 0640. Group-writable or world-readable permissions fail security checks.
  5. Perform Unauthenticated HTTP Probes: Audit the web-server/vhost configuration for aliases that map to the absolute vault directory. From an external client, probe every plausible mapped path and the exact old Media URL. A direct vault URL should not exist; every probe must return 403 or 404. If bytes are returned, reconfigure the host before release.
  6. Purge CDN and Old Upload Caches: After publishing a release with public source deletion, verify that the former Media Library URL returns 404 Not Found across all CDN edge nodes and reverse proxies.
  7. Coupled Backup Strategy: Always back up the MySQL database and the private vault directory together. The database holds the release records, SHA-256 bindings, and entitlement snapshots; the vault holds the actual binary files. Restoring one without the other leads to integrity errors.
  8. Measure Server Concurrency and Capacity:
    • WooNooW core streams local downloads using PHP's readfile(). Web-server direct offloading (X-Accel-Redirect or X-Sendfile) is not implemented in core.
    • Streaming a 25 MB package ties up a PHP-FPM worker for the duration of the customer's download. A burst of 25–50 customer sites can therefore occupy a comparable number of PHP workers while transfers remain active.
    • Do not assume your server can handle 25–50 concurrent large downloads without verification. Conduct measured load tests against your specific host plan, PHP-FPM process limits, and network throughput before launching major releases.

Pre-flight release checklist

Before making a software version available to customers, verify each item on this checklist:

  • Modules Enabled: Software Distribution is active; Software Licensing is also active for protected products.
  • Product Setup: The parent has a unique Software Slug and software updates enabled; licensing policy is explicitly confirmed.
  • Catalog Entitlements: Explicit update/support/history policy is configured on the parent or variations, or legacy behavior is intentionally accepted.
  • Secure Storage Verified: Outside-webroot private vault is verified (filesystem_status: secure), or encrypted-at-rest storage is active (storage_mode: encrypted_at_rest) with verified Sodium Secretstream and non-autoload database key.
  • Direct Access Probed: Every plausible host alias and former upload URL were tested externally; no plaintext artifact bytes are returned.
  • Package Ingested: The release package is uploaded directly into secure storage (or migrated from a legacy WooCommerce download with explicit source deletion authorized and references audited).
  • Checksum Verified: Server-computed SHA-256 hash matches the exact binary compiled by your build pipeline.
  • Changelog Formatted: Narrative overview and structured points (ADD, FIX, etc.) are documented.
  • Coupled Backup Verified: Database (including woonoow_software_vault_key option) and software storage directory (wp-content/uploads/woonoow-vault or outside-webroot vault) are backed up together.
  • Load Capacity Tested: Server PHP-FPM concurrency limits are tuned for anticipated download spikes.

Last updated Sep 8, 2026