Licensed Plugin Updates
Private releases, licence eligibility, verified downloads and operator recovery.
Stardetha One now distributes private plugin releases to active licensed installations. The operator imports a reviewed ZIP on the service host; ILS One mode discovers newer versions through WordPress's normal update checks. WordPress downloads a fresh authorized package and verifies its exact size and SHA-256 before passing the temporary file to the installer. No browser upload console is included; importing is an operator CLI workflow.
First upgrade and prerequisites
- Back up the service database, APP_KEY, signing keys, WordPress database/files and WordPress salts. Preserve encrypted licence options and existing configuration.
- Deploy Stardetha One 0.8.0 with the existing locked dependencies. Run
php artisan migrate --force, clear/rebuild the normal application caches, and keep the scheduler running. Migration 2026_10_08_065223_create_plugin_release_tables adds plugin_releases and plugin_downloads. It does not change issued licences or usage balances. - Enable PHP's zip extension on the operator CLI runtime. The importer reports a clear error if ZipArchive is unavailable. No new Composer or npm packages are required.
- Set APP_URL to the exact public HTTPS base URL configured as IST_ONE_SERVER_URL, including any base path. HTTPS must terminate correctly through your proxy. Package responses must bypass shared caching and redirects.
- Keep storage/app/releases private and writable by the application. It is a dedicated local disk with no public serving route. Never symlink it into public/storage or expose it directly through the web server. Back up these files together with release records; multi-node deployments need the same private filesystem mounted on every serving node.
- Manually install ILS 0.24.0 once. Earlier One-mode versions cannot fetch this first update because they have no One updater. Keep ILS active and connected to One; legacy mode is available by explicit override and its original updater is preserved. The optional SDK example plugin is not required by ILS and does not update itself.
- Install matching ILS documentation viewer 1.5.0/site 0.24.0 and platform documentation viewer 0.8.0. Rehearse the complete update on staging before customer rollout.
The existing monthly usage migration must already be present. Upgrading from pre-0.7.0 runs all pending migrations; read usage-metering.html for its separate counting and rollout contract.
Publish a reviewed ZIP
Transfer the final installable plugin ZIP and optional UTF-8 plain-text release notes to a private operator directory on the service host. Do not import the outer source bundle or documentation ZIP.
php artisan releases:import instant-language-switcher 0.24.0 /secure/releases/instant-language-switcher-v0.24.0.zip --notes=/secure/releases/notes.txt
The command copies and validates a private snapshot, then publishes its metadata in one database transaction. It prints a public release UUID and SHA-256, not a download credential. A release at the same version cannot be replaced or reactivated: publish a new version for changed bytes. The first import can register 0.24.0; existing 0.24.0 clients see no update until a later, higher version is imported.
Only configured plugin products are accepted; this release configures instant-language-switcher/instant-language-switcher.php. Versions must be stable major.minor.patch, with no prerelease channel. Version ordering is semantic rather than string ordering. All active licences for the product, including evaluation/manual licences, are eligible; there is no separate paid-updates entitlement. Withdrawn releases are excluded and downgrades are never offered. The newest release is selected; incompatible PHP/WordPress requirements block installation rather than selecting an older compatible release.
Validation requires a ZIP no larger than 25 MiB, at most 2,000 entries, at most 10 MiB per entry and 100 MiB total expanded bytes. Entries must stay under the configured plugin folder. Traversal, duplicate/case-colliding paths, conflicting file/directory paths, links, special entries, encryption and corrupt entry bytes are rejected. The main plugin Version must match; Requires at least and Requires PHP must be numeric version headers. Release notes are limited to 20,000 UTF-8 bytes, stripped of HTML at import and escaped in the WordPress details view. Validation is structural: the operator remains responsible for reviewing the executable PHP/JavaScript code being distributed.
Eligibility, downloads and WordPress behavior
| Stage | Contract |
|---|---|
| Update check | Authenticated installation credential, active licence and matching billing environment; only that licence's product releases are returned. WordPress also requires a current, verified cached signed licence before making the request. |
| WordPress update transient | Contains a non-secret release marker URL and version/compatibility metadata. No raw licence key, installation credential or short-lived download token is stored in it. |
| Installation start | The ILS upgrader hook exchanges the release UUID for a fresh random download token. It checks plugin identity, a newer version and local PHP/WordPress compatibility. |
| Download lifetime | Five minutes maximum, capped by licence expiry. Only a SHA-256 hash of the random token is stored on the service. A link can retry within its lifetime; it is not single-use. Anyone holding the link can redeem it while it remains eligible, so treat it as a temporary bearer secret. |
| Redemption | Rechecks activation, credential rotation, licence status/expiry/environment, product and release withdrawal before serving bytes. A request already authorized and streaming cannot be recalled. |
| Integrity | The service verifies private file size/hash before serving; ILS verifies downloaded size/hash again. Expected metadata is authenticated by the configured HTTPS service. Release metadata is not a separate RSA-signed/offline manifest; the existing RSA token protocol remains v2. |
| HTTP policy | Safe WordPress HTTPS requests, certificate verification enabled, no redirects, bounded response size, streamed temporary file and no installation Authorization header on the package GET. |
| Failure | Refuse the update and remove its temporary file. A service outage, revoked/expired licence, altered package or incompatible runtime cannot fall back to an unverified install. Existing plugin files are not handed to WordPress for replacement until verification succeeds. |
Dashboard Update now, bulk updates and WordPress's own automatic-update mechanism use the same download hook while ILS is active. This release does not automatically enable WordPress auto-updates; administrators keep control of that setting. An inactive plugin does not register its updater hooks, so use the reviewed manual ZIP workflow to update an inactive ILS installation. Multisite remains unsupported in One mode.
If the signed licence cache expired, use License → Check now to refresh it before checking for updates. A stale update notification may remain until the next WordPress check; fresh download authorization still enforces current eligibility. Manual ZIP replacement is an operator-controlled path outside this updater and must use independently reviewed packages.
API and SDK
| Method and path | Request / response |
|---|---|
| POST /api/v1/licenses/updates/check | Installation Bearer credential; { "version": "0.24.0" }; returns data.release metadata or null. |
| POST /api/v1/licenses/updates/ticket | Installation Bearer credential; { "release_id": "UUID" }; returns release metadata, download_url and expires_at. |
| GET /api/v1/plugin-downloads/{token} | Temporary bearer URL; returns application/zip with no-store, private, nosniff and no-referrer headers. No installation credential is required in the header. |
Metadata contains id, product, plugin basename, version, sha256, size, requires, requires_php and plain notes. Wrong-product/missing/withdrawn releases and unknown/expired download tokens return 404. Invalid installation credentials return 401; inactive/expired or wrong-environment licences 403; invalid input 422; unavailable/corrupt stored packages 503. No update endpoints accept arbitrary remote package URLs or upload executable content from customers. Routes are rate-limited using the existing 60/minute credential+IP limiter, plus the 120/minute API limit; unauthenticated package GETs share their IP limit.
SDK additions are Client::checkUpdate(version), Client::updatePackage(releaseId) for a non-secret marker, and Client::prepareUpdate(releaseId). Both existing V050 namespaces and vault encryption context remain unchanged. The ILS OneUpdater owns actual package streaming and WordPress hooks. Authorizations are online-only; they neither consume monthly translation characters nor create new activation slots.
Exclude /api/v1/plugin-downloads/* tokens from reverse-proxy/access logs and tracing, and never paste live links into support tickets. API responses use no-store. Keep the application signing configuration and installed code trusted; client-controlled PHP code can bypass local controls, and these checks do not prevent copying a package after an authorized download.
Withdraw and recover
php artisan releases:withdraw RELEASE_UUID
php artisan releases:prune-downloads
Withdrawal hides the release and blocks its outstanding links on their next redemption. It retains records and bytes for diagnosis, does not alter installed sites, and does not downgrade customers. The daily scheduler prunes expired download rows; it never deletes release artifacts. Publish a corrected higher version to recover. Local import failures remove their temporary private snapshot; a process crash can leave an unreferenced ZIP that operators should reconcile against records before deleting.
Do not drop the release tables to roll back a production deployment: doing so destroys registry identities. Prefer a forward fix and disable/withdraw a problematic release. Existing manual installation remains available from a separately reviewed artifact. Back up before WordPress updates; WordPress owns extraction, installation and rollback behavior after verified download.
Validation and remaining acceptance
Automated coverage exercises real Laravel endpoints and ZIP files, database authorization, both SDK namespaces, and WordPress hook adapters with narrow doubles. It covers unsafe archives, immutable versions, semantic ordering, expiry/retries, revoked/expired/deactivated/rotated installations, wrong environment/product, withdrawal, corrupt packages, untrusted URLs, compatibility and outage refusal. These tests do not substitute for native WordPress/MySQL/object-cache/cron/admin execution or an actual bulk/background upgrade. Run that staging matrix, the PostgreSQL contention tests and PayFast/PayGate sandbox acceptance before production sign-off. No live release was published or customer installation changed by preparing this bundle.
Staging acceptance (0.9.0)
Read staging-acceptance.html for the read-only readiness checker, disposable native WordPress runner, CI matrix and checks that remain pending. No production sign-off is implied.
Customer downloads — platform 0.11.0
Customers can now obtain the first ZIP from a paid order or their Licences page. See Customer Plugin Downloads. Existing staging overrides are preserved.