Introduction
In the previous article, I wrote about the WP Plugin Update Server, a plugin that allows a WordPress site to act as a self-hosted update server for privately distributed plugins.
But the server is only one half of the system.
A private plugin still needs a way to participate in WordPress’ native update workflow. It needs to:
check whether a new version exists
show update notices in the admin
- populate the “View Details” modal for plugins download protected packages when needed install correctly even when GitHub ZIP archives use inconsistent folder names
That’s the role of the WPPF Update Helper.
It is the client-side package that lives inside the plugin being distributed and connects that plugin to a configured update server.
Why the Helper Exists
The update server exposes metadata, but WordPress does not automatically know how to use that metadata for a privately hosted plugin.
By default, WordPress expects plugin update information to come from the official WordPress.org infrastructure.
The Update Helper bridges that gap by hooking into WordPress’ normal update lifecycle and translating remote update responses into the structures WordPress already expects.
In other words, it makes a privately hosted plugin behave like a normal updatable plugin inside the WordPress admin.
How the Pieces Fit Together
The overall flow looks like this:
More specifically:
- A WordPress plugin includes the WPPF Update Helper.
- The plugin registers with the helper.
- During WordPress update checks, the helper requests update metadata from the server.
- If a newer version exists, the helper injects that response into the normal plugin update transient.
- WordPress displays the private plugin update in the admin as if it were part of the standard update system.
The helper also supports the plugin details modal and the package download/install process.
Registering a Plugin With the Helper
A host plugin registers itself with the helper by providing:
- the plugin slug
- the update server URL
From there, the helper can determine which plugins it should manage during update checks.
One implementation detail I like here is that the helper uses a WP filter-based registry rather than relying on a single mutable global store. That makes it easy for plugins to opt in while keeping the registration flow simple.
The implementation assumes the primary plugin file follows the standard naming pattern of:
slug/slug.php
This assumption is important because it affects both version lookup and post-install folder normalization.
Hooking Into WordPress Update Checks
The core of the update flow happens through the normal WordPress plugin update transient.
The helper hooks into:
pre_set_site_transient_update_plugins
When WordPress prepares the plugin update transient, the helper inspects the registered plugins, groups them by domain, and requests remote update metadata from the configured server.
The server endpoint used for that flow is:
/wp-json/wppf/api/plugin-updates/transients
If the response indicates that a remote version is newer than the installed version, the helper writes that response into:
$transient->response[...]
At that point, WordPress takes over and displays the update through the normal admin interface.
This is one of the most useful aspects of the design: the helper doesn’t create a parallel update UI. It plugs into the existing one.
Supporting the “View Details” Modal
WordPress also expects plugin metadata when a user clicks View details in the admin.
To support that, the helper hooks into:
plugins_api
When WordPress requests plugin information for a registered plugin, the helper calls the update server’s plugin information endpoint:
/wp-json/wppf/api/plugin-updates/plugins-api
It then returns that response in the shape WordPress expects for the plugin details modal.
This means privately distributed plugins can provide a more complete native experience, including things like descriptions, changelogs, icons, and other plugin metadata.
Handling Private Package Downloads
One of the more interesting parts of the helper is how it handles protected package downloads.
If an update response includes a token, the helper intercepts the package download step using:
upgrader_pre_download
At that point it:
- reads the update response from the update transient
- checks whether a token is present
- retrieves the configured SSL key from WPPF settings
- decrypts the token
- performs an authenticated request for the package
- writes the ZIP to a temporary file
- returns the local temp path to the WordPress upgrader
This allows the plugin update flow to work even when the package is protected behind authentication.
A nice implementation detail here is that the token is decrypted only at install time rather than being stored in a persistently usable form inside the plugin.
Fixing GitHub ZIP Folder Name Problems
If you have ever installed a package generated from a GitHub ZIP archive, you’ve probably run into the folder naming problem.
GitHub-generated ZIP files often extract into a directory name that does not match the canonical plugin folder WordPress expects, appending the primary branch name to the end of the plugin directory name.
The helper works around this by hooking into:
upgrader_post_install
After installation, it renames the extracted directory to the expected plugin slug folder inside wp-content/plugins.
That makes GitHub-based releases much easier to use in a normal WordPress plugin workflow.
Interesting Implementation Details
As a side note, some of the additional details surrounding the functionality can be found below:
- it batches update checks by domain, which reduces duplicate remote requests when multiple plugins use the same host
- it falls back to plugin header inspection if the transient does not already contain the local version
- it will first use project releases for versioning before falling back to commit versioning
Why This Part of the Ecosystem Matters
The update server gets most of the attention because it exposes the metadata and distribution endpoints.
But the helper is what makes the full experience possible inside an actual plugin.
Without it, the server is just an API.
With it, a private plugin can:
- appear in normal WordPress update checks
- show version updates in the admin
- support the details modal
- download protected packages
- install more cleanly from GitHub-based sources
That is what turns the system into a usable private plugin distribution workflow rather than just a backend service.
Explore the Project
If you want to inspect the implementation, you can explore the helper here:
Repository:
https://github.com/kyle-niemiec/wppf-update-helper
And the update server it connects to is here:
Update Server:
https://github.com/kyle-niemiec/wp-plugin-update-server
Part of the WPPF ecosystem:
Documentation:
https://wp-plugin-framework.codeflower.io


Top comments (1)
If you're exploring the WPPF ecosystem, the docs are here:
wp-plugin-framework.codeflower.io