wp-packages: WP Packages: The Go Registry Rebuilding WordPress Composer from First Principles
A fast, SQLite-backed bridge from WordPress.org SVN to Composer that fixes package metadata, makes installs reproducible, and shows how much better a registry can be when it behaves like a live system.
- WP Packages replaces giant Composer index files with lazy, per-package metadata, so the registry behaves like a live service instead of a static dump.
- SQLite is not just storage here, because it also anchors build state, lookup speed, and deterministic registry behavior.
- The pipeline matters as much as the protocol, since discovery, update, build, and deploy are serialized like a real CI system.
- WP Packages is as much a governance story as a technical one, because it makes WordPress dependency infrastructure faster, more transparent, and community-owned.
For years, WordPress Composer users lived with a blunt tradeoff: use the ecosystem, but accept a registry that felt heavy and static. WP Packages changes the unit of work. It does not ship giant prebuilt indexes and hope for the best. It answers queries, one package at a time, and lets Composer v2 do less work.
The registry that refuses to ship giant indexes
That sounds like a small implementation detail. It is not. In package tooling, the difference between downloading a few targeted metadata records and fetching huge provider files is the difference between a quick resolve and a ritual.
Roots built WP Packages as a modern Composer repository for WordPress plugins and themes, but the real move is architectural. It treats package metadata as a live index that can be queried on demand, updated continuously, and served from a compact system of record.
| Dimension | WP Packages | WPackagist |
|---|---|---|
| Registry model | Live per-package metadata | Static generated indexes |
| Composer protocol | Composer v2 metadata-url | Older provider-includes model |
| Speed | Fast cold resolves | Slower bulk fetches |
| Metadata richness | Authors, descriptions, homepages, support links | More limited package metadata |
| Update cadence | Frequent syncs from WordPress.org | Slower refresh cycle |
| Reproducibility | Pinned untagged installs via SVN revisions | Less deterministic for trunk-style installs |
| Governance | Open, community-run, transparent | Controlled outside the community team |
The comparison is really about mental models. WPackagist behaves like a warehouse. WP Packages behaves like a service desk.
Why SQLite is the real engine here
SQLite is not acting like a humble embedded database here. It is the registry’s memory, state machine, and lookup layer. The service writes to it in batches, locks it during coordinated work, and uses it to serialize package metadata with deterministic behavior.
That matters because package registries fail in boring ways: partial writes, stale indexes, and race conditions between sync jobs. WP Packages avoids those traps by treating the database as the source of truth for both build state and serving state.
We just generally try to keep Composer referring to CLI/client side things and Packagist for the server/repository. Otherwise people just end up getting even more confused by these tools.
The pipeline that keeps WordPress.org in sync
The pipeline command is the quiet center of the project. It runs discovery, update, build, and deploy in order, and it uses a system-wide file lock so those steps do not collide when cron, a human operator, or a restart tries to trigger work at the same time.
func runPipeline() error {
lock, err := acquirePipelineLock()
if err != nil {
return err
}
defer lock.Close()
steps := []string{"discover", "update", "build", "deploy"}
for _, step := range steps {
start := time.Now()
if err := runStep(step); err != nil {
return err
}
recordBuildStep(step, time.Since(start))
}
return nil
}
That is the kind of code you write when you care about state as much as throughput. The point is not to do everything at once. The point is to make the registry predictable enough that everyone else can trust it.
Composer v2 changes everything
Composer v2’s metadata protocol is the real unlock. Instead of asking for a giant precomputed catalog, the client starts with a root query, then fetches package metadata only when it needs it. That reduces cold-start work and makes the registry feel faster even before caching kicks in.
WP Packages supports the newer metadata-url and metadata-changes-url flow, which means the server can reveal only the minimum required information at each step. The result is less wasted bandwidth, fewer round trips, and a cleaner fit between client and repository.
| Client behavior | Old registry model | WP Packages model |
|---|---|---|
| First request | Download a large index | Fetch root metadata |
| Package lookup | Rely on prebuilt provider files | Request only the needed package |
| Freshness | Wait for the next bulk regeneration | Use changes feed and background sync |
| Latency feel | Heavy on cold start | Light on cold start |
This is why the project can advertise big wins without magic. It is not hiding work. It is refusing to do unnecessary work.
Immutable installs for a mutable ecosystem
WordPress has always had a tricky edge case: the untagged install. If a package points at trunk, the source can drift underneath you. WP Packages fixes that by pinning those installs to specific SVN revisions, which turns a moving target into a reproducible artifact.
That is a deceptively important feature. It means a build today can match a build tomorrow, even when the upstream source tree is still in motion. For teams shipping production sites, that is the difference between convenience and trust.
The modern Go stack behind the scenes
The service stack is intentionally plain. Cobra powers the CLI, net/http handles serving, embed ships assets inside the binary, slog keeps logs structured, Sentry catches errors, and systemd socket activation lets the process behave like real infrastructure instead of a one-off script.
That discipline shows up in the repo layout too. The command layer stays separate from internal packages, deployment lives in Ansible, and the whole thing reads like a production service with a registry problem, not a registry toy with a deployment hobby.
WP Packages vs. WPackagist
| Category | WP Packages | WPackagist |
|---|---|---|
| Ownership | Community-run and open source | Acquired and operated under corporate ownership |
| Protocol | Composer v2 lazy metadata | Bulk index style repository |
| Performance | Faster cold resolves | Slower cold resolves |
| Metadata | Richer and more complete | Less complete |
| Update model | Frequent syncs from source | Slower refresh cadence |
| Reproducibility | Pinned revisions for untagged installs | Less deterministic trunk behavior |
| Naming | wp-plugin and wp-theme namespaces | wpackagist-plugin and wpackagist-theme namespaces |
The difference is not cosmetic. It is a better contract between the WordPress ecosystem and the dependency tooling that sits on top of it.
Why this matters beyond WordPress
WP Packages is a reminder that infrastructure can be redesigned in public without inventing a new category of software. The trick is not novelty for its own sake. The trick is choosing the right abstraction, then making it fast, transparent, and maintainable.
That is why the project matters to more than WordPress developers. It is a case study in how to modernize a legacy ecosystem by tightening the feedback loop between source, metadata, and delivery.





