I have spent years working with enterprise WordPress, running websites that are critical to the business and simply cannot go down. When you are operating at that level, infrastructure is not something you can treat as an afterthought. You have to take it seriously from the very beginning if you want things to scale without breaking.
One of the hardest parts is dealing with dependencies, especially plugins and themes. For teams that manage WordPress through code, Composer is usually the go to tool. The problem is that Composer does not really understand WordPress on its own. To make it work, we depend on tools like wpackagist, which act as a bridge and turn WordPress.org plugins into something Composer can actually use.
When you install a package via wpackagist, it resolves to something like this:
{
"name": "wpackagist-plugin/wordpress-seo",
"version": "25.6",
"dist": {
"type": "zip",
"url": "https://downloads.wordpress.org/plugin/wordpress-seo.25.6.zip"
}
}
If you look closely, wpackagist is essentially a proxy. It points the Composer resolver to a raw ZIP file on WordPress.org.
This works for public plugins, but a mature WordPress site relies on commercial/private packages. This is where the headache begins. You have a few not so good options:
- Private Composer Repos: You have to manage authentication for every single vendor.
- Git URLs: It works, but requires maintaining access tokens.
- Manual Scripts: Custom bash scripts to download, unzip, and commit code.
None of these are free. They all come with maintenance overhead.
The Missing Features
A true package manager does more than just download files. It should:
- Resolve Dependencies: If I install Plugin A and it depends on Plugin B, it should automatically install Plugin B. Currently, because WordPress plugins lack standard dependency metadata, Composer cannot do this. You have to manually download both.
- Run Lifecycle Scripts: Automate build steps or cleanup tasks after installation.
- Verify Security: Ensure the file you downloaded is actually the file the author published.
The Security Gap: This is where things really start to fall apart for enterprises. There is no standard way today to verify where a WordPress plugin actually comes from. If the original source or a proxy server gets compromised, you could end up installing a modified plugin without ever realizing it.
Origin of the wpm Registry
To solve this, I created the wpm registry. Unlike existing solutions, this registry enforces a strict metadata schema (wpm.json) that allows for:
- True Dependency Resolution: Context-aware installation.
- Supply Chain Security: Attestation and cryptographically verifiable signatures at the time of publication.
We now had a single source of truth for public and private packages. The remaining problem was how to install them.
Origin of the wpm Package Manager
We built a specific registry with specific requirements, so we chose to write a package manager from scratch in Go. This leads to the most common question we get:
“Why reinvent the wheel? Why not just write a Composer plugin?”
We love Composer, but wpm solves infrastructure challenges that Composer wasn’t designed for:
1. Deterministic “Installer” vs. “Solver”
Composer is a dependency solver that calculates the best intersection of versions for a library. wpm is designed as a strict artifact installer. In the WordPress ecosystem, resolving version ranges can be risky (e.g., automated updates triggering database migrations). wpm is built to strictly enforce exact versions defined by the registry, ensuring 100% reproducibility.
2. The “Single Binary” Advantage
Composer requires a functional PHP CLI environment, specific extensions (zip/curl), and adequate memory limits. In the fragmented WordPress hosting world, we cannot guarantee these prerequisites.
By writing wpm in Go, we ship a single, static binary with zero dependencies. It runs on the OS, not the PHP runtime. It includes its own networking stack and Zstd decompressor, meaning it works even on restrictive hosting environments where PHP allows web traffic but blocks CLI tools.
3. Supply Chain Security (TUF & Sigstore)
We are prioritizing supply chain security from day one. wpm integrates TUF (The Update Framework) and provenance verification. The tooling for these standards is mature and native in the Go ecosystem (thanks to Cloud Native tools). Porting these complex security frameworks to PHP would be error-prone and slow; in Go, they are first-class citizens.
The Reality Check
Composer is the standard tool in the PHP world, but it assumes something that rarely exists in practice. It assumes every server has a clean and properly configured command line environment.
The reality with WordPress is much messier. Hosting is often locked down, runtimes vary from one server to another, and required extensions are frequently disabled. By building wpm in Go, we avoided these environmental issues altogether.
We did not build wpm because Composer is a bad tool. We built it because we needed a single, self contained binary that could run securely no matter how the server was set up.
Real World Example: Installing a Dependency
Let’s look at a common scenario: You want to install a plugin like dinopack-for-elementor. As the name suggests, this plugin requires the core Elementor plugin to function.
Here is how the experience differs between the two tools.
1. The Composer Way
First, Composer doesn’t know where to find WordPress plugins by default. You must create or edit your composer.json to add the wpackagist repository:
{
"repositories": [
{
"type": "composer",
"url": "https://wpackagist.org"
}
]
}
Next, you run the install command:
$ composer require wpackagist-plugin/dinopack-for-elementor
The Result: Composer downloads dinopack-for-elementor, but fails to download elementor.
Why? Because the wpackagist proxy doesn’t inject dependency metadata. You now have a broken site. You must manually figure out which version of Elementor is compatible, then run a second command to install it.
2. The wpm Way
With wpm, there is zero setup. You don’t need to create a config file or define repositories. The registry already knows the dependency graph.
You simply run:
$ wpm install dinopack-for-elementor
The Result: wpm detects the dependency, resolves the correct version, and installs everything in parallel with blazing speed.
$ wpm install dinopack-for-elementor
wpm install v0.1.7
+ dinopack-for-elementor 1.0.5
+ elementor 3.34.1
2 packages installed
No config files, no manual dependency hunting, no broken plugins. Just the artifact you requested, ready to run.
3. Clear visibility into your setup
Since wpm handles dependencies automatically, you might notice a package you didn’t explicitly request (like elementor above) and wonder, “Why is this here?”
With wpm, you don’t have to guess. You can simply ask the tool:
$ wpm why elementor
my-cool-project (dependencies)
└─ [email protected]
└─ [email protected]
It instantly visualizes the dependency tree, showing you exactly which plugin required elementor to be installed. No config files, no manual dependency hunting, just total transparency.
The Future of WordPress Dependencies
For a long time, the WordPress ecosystem has leaned on solutions that were simply good enough. We proxy zip files, ignore dependency graphs, and hope the server is set up the right way. That might work for small sites, but as WordPress runs more mission critical enterprise systems, good enough stops being acceptable.
wpm is not just another CLI tool. It is a rethink of how the WordPress supply chain should work. By treating plugins and themes as secure, immutable artifacts and separating the installer from the server’s PHP setup, we bring a level of reliability that WordPress has been missing.
Ready to dive in?
If you are ready to experience a faster, secure, and more reliable way to manage WordPress dependencies, here is where you can start:
- Install the CLI: https://github.com/trywpm/cli#installation – Get the single binary for macOS, Linux, or Windows.
- Registry Spec: https://registry.wpm.so/schema.json – View the metadata schema standard.
- GitHub: https://github.com/trywpm – Audit the code or contribute.