Composer Repository

Troy Server provides a native Composer 2 repository. Every plugin and package you publish becomes available via composer require — alongside WordPress.org plugins from WP Packages or WPackagist.


Prerequisites

RequirementDetails
Troy ServerInstalled and active, with at least one published plugin. Packages are recommended — they include Troy Client for update request filtering and active install reporting.
ComposerVersion 2+. Troy Server uses the metadata-url protocol.
HTTPSYour Troy Server must be accessible over HTTPS. Composer rejects plain HTTP repositories by default.

Add Your Repository

Add your Troy Server as a Composer repository in your project's composer.json under the repositories key:

{
  "repositories": [
    {
      "type": "composer",
      "url": "https://repo.example.org/composer/get"
    }
  ]
}

This repository entry sits alongside any other Composer repositories in your project.

JSON Breakdown
KeyDescription
"repositories"An array of package sources Composer checks when resolving dependencies. Each entry points to a repository.
  ⤷ "type"Set to "composer". Tells Composer this is a Composer repository that speaks the packages.json protocol.
  ⤷ "url"Your Troy Server's Composer endpoint. It serves packages.json at the root and individual package metadata at subpaths like /composer/get/{vendor}-plugin/{slug}.json.

See How Composer Resolution Works for the full response format.

No Trailing Slash:

The repository URL has no trailing slash. Composer appends /packages.json automatically.


Install Troy Server via Composer

Choose method:

Run these commands from a terminal in your project root. The first registers the repository in your composer.json, the second installs the package immediately.

composer config repositories.deploytroy composer https://repo.deploytroy.org/composer/get
composer require deploytroy-package/troy-server-installer

Composer fetches the metapackage and automatically pulls in Troy Client as a dependency.

Packages Bundle Troy Client:

Requiring a {vendor}-package installs a metapackage. Metapackages have no downloadable archive themselves — Composer resolves each bundled plugin individually. Troy Client is always included for update request filtering and active install reporting.

Read more about Packages in the Packages documentation.


Example With Multiple Repositories

A working example installing The SEO Framework via its Troy Server alongside Gentime via WP Packages:

{
  "repositories": [
    {
      "name": "wp-packages",
      "type": "composer",
      "url": "https://repo.wp-packages.org",
      "only": ["wp-plugin/*", "wp-theme/*"]
    },
    {
      "type": "composer",
      "url": "https://repo.theseoframework.com/composer/get"
    }
  ],
  "require": {
    "theseoframework-package/tsf-em-installer": "*",
    "wp-plugin/gentime": "^2.0"
  }
}
JSON Breakdown
KeyDescription
"repositories"An array of package sources. This example uses two: WP Packages and a Troy Server.
  ⤷ "name"A label for the WP Packages repository. Composer uses it for display only.
  ⤷ "type"Set to "composer". Tells Composer this is a Composer repository (as opposed to VCS, path, etc.).
  ⤷ "url" (WP Packages)The WP Packages endpoint. Serves WordPress.org plugins and themes.
  ⤷ "only"Limits WP Packages to wp-plugin/* and wp-theme/*. Composer won't ask it about theseoframework-package/* names.
  ⤷ "url" (Troy Server)The Troy Server endpoint for The SEO Framework. Its available-package-patterns list theseoframework-package/* and theseoframework-plugin/*, so Composer knows where those packages live.
"require"A map of packages to install. Each key is a package name, each value is a version constraint.
  ⤷ "theseoframework-package/tsf-em-installer"The SEO Framework's installer metapackage. Composer matches theseoframework-package/* against the Troy Server's listed patterns and fetches the metadata from there. The "*" constraint accepts any version.
  ⤷ "wp-plugin/gentime"Gentime from WP Packages. Composer matches wp-plugin/* against the WP Packages only filter. The "^2.0" constraint requires version 2.x.

See How Composer Resolution Works for the full packages.json response format that drives this matching.


Plugins vs Packages

Troy Server provides these Composer package types, where {vendor} is your repository's vendor slug:

TypePatternComposer TypeIncludes Troy Client
Plugin{vendor}-plugin/{slug}wordpress-pluginNo.
Package{vendor}-package/{slug}metapackageYes.
Which type to use:

Use {vendor}-package when setting up a site for the first time. The metapackage bundles Troy Client alongside your plugins.


Bedrock Setup

Bedrock already manages WordPress plugins via Composer. Adding a Troy repository takes one entry.

1

Add the Repository

Open your Bedrock project's composer.json and add your Troy Server to the repositories array:

{
  "repositories": [
    {
      "name": "wp-packages",
      "type": "composer",
      "url": "https://repo.wp-packages.org",
      "only": ["wp-plugin/*", "wp-theme/*"]
    },
    {
      "type": "composer",
      "url": "https://repo.example.org/composer/get"
    }
  ]
}
JSON Breakdown
KeyDescription
"repositories"Bedrock's array of package sources. Add your Troy Server entry here.
  ⤷ "name"Bedrock ships with this optional label. Composer uses it for display only.
  ⤷ "url"Your Troy Server's Composer endpoint. No "only" needed — the packages.json available-package-patterns already limits it to your vendor prefix.
  ⤷ "only"Limits this repository to wp-plugin/* and wp-theme/*. Composer won't ask WP Packages about your Troy packages.
2

Require Troy Server

Run this from your Bedrock project root. It adds the package to your composer.json and installs it immediately.

composer require deploytroy-package/troy-server-installer
3

Install

Run composer install (or composer update if you already have a lock file). Composer resolves your Troy packages alongside WordPress core and WP Packages plugins.

Troy plugins land in web/app/plugins/ — Bedrock's installer-paths already maps the wordpress-plugin type to that directory.

𒄈

Vendor Slug

Every Troy Server has a vendor slug — a short prefix that identifies your repository in Composer package names. It appears in:

  • Plugin names: {vendor}-plugin/my-plugin.
  • Package names: {vendor}-package/my-package.

Set the vendor slug in Settings → Troy Server → Composer. Troy pre-fills it from your site name at install time.

The Vendor Slug Is Immutable:

Once consumers reference your vendor slug in their composer.json, do not ever change it. Changing the slug breaks Composer resolution for every existing consumer — and the error message they get will not mention the vendor change, making the breakage difficult to diagnose.


How Composer Resolution Works

When Composer encounters your Troy repository, it fetches packages.json at the repository URL. That response contains no package data — only instructions for fetching individual packages:

{
  "packages": {},
  "metadata-url": "/composer/get/%package%.json",
  "available-package-patterns": [
    "deploytroy-plugin/*",
    "deploytroy-package/*"
  ]
}
Response Breakdown
KeyDescription
"packages"Empty object. The root response contains no package data — Composer fetches each package individually.
"metadata-url"Tells Composer where to fetch metadata for a specific package. Composer replaces %package% with the full package name (e.g., deploytroy-plugin/troy-server) and fetches only what it needs. No full catalog download.
"available-package-patterns"Tells Composer which namespaces this repository provides. Composer skips repositories that don't match the package it's looking for — this is why Troy and WP Packages don't interfere with each other by default.

You can inspect live responses from Deploy Troy's repository:


Troubleshooting

Composer Reports "Package Not Found"

  • Verify that the vendor slug in your composer require command matches the vendor slug configured in Troy Server.
  • Verify that the plugin or package is published and has at least one released version.
  • Verify that the repository URL in your composer.json is correct — no trailing slash, and the path ends with /composer/get.

HTTPS Errors

Troy Server requires HTTPS. If your server uses a self-signed certificate, Composer will reject it. Use a valid certificate (Let's Encrypt is free) or pass --no-verify-ssl during development only.

Stale Results After Publishing

Composer caches repository metadata. Run composer clear-cache and then composer update to force a fresh fetch.

Namespace Collisions

Troy's available-package-patterns tells Composer which namespaces your repository provides. Composer skips repositories that don't match the package it's resolving, so collisions are unlikely in practice.

If a misconfigured third-party repository still interferes, you can add an only filter to each repository entry in your composer.json to explicitly scope which packages it may resolve. See the Composer documentation on repository priorities for details.