These docs have been generated using AI. Expect inaccuracies until we remove this banner.

Adding Plugins to Your Repository

Once Troy Server is installed, adding plugins is straightforward. You have four options:

  1. Upload a ZIP file — Select a file, done
  2. Connect a GitHub repository — Automatic releases from tags
  3. Connect a WordPress.org plugin — Automatic releases from tags
  4. Enter a ZIP URL — Troy Server fetches and processes it

Plugins vs Packages:

Plugins are for updates. Packages are for distribution.

If you distribute plugins directly, users need Troy Client installed first—either manually or through someone else's Package.

Packages are mini-installer plugins that bundle Troy Client. When users install your Package, they get Troy Client + your plugins in one step. Packages always fetch the latest version at install time, so you don't need to rebuild them for every release.

Use Packages to distribute. Use Plugins to serve updates.

Adding Your First Plugin

1

Create a New Plugin Post

In your WordPress admin:

  1. Go to Plugins → Add New Plugin
  2. A new plugin post opens in the Block Editor
2

Set the Plugin Slug

In the sidebar, set the plugin slug (e.g., example-plugin). This becomes both the identifier for your plugin and the folder name when installed.

3

Upload a Version

In the sidebar, find the Versions panel:

  • Select a file — Choose your plugin ZIP
  • Enter a URL — Troy Server will fetch and process it

Troy Server automatically extracts from your ZIP:

  • Plugin name (updates the post title)
  • Version number
  • Description, author, and requirements
  • Changelog (from readme.txt or readme.md)

Troy Header Required:

Your plugin must include a Troy: header pointing to your repository URL before uploading. Without this header, the upload will fail. See Plugin Headers for the format.

4

Set Version to Tag

New uploads start as Unreleased. In the Versions panel, click the version and change its type to Tag (stable) or Beta.

Unreleased versions cannot be downloaded.

5

Publish

Click Publish. Your plugin is now available at:

https://your-server.com/plugin/get/zip/example-plugin

When you publish, the plugin status automatically changes from Pending to Public.

It's a 20-second setup (with some practice):

That's the complete workflow! Optionally, add a banner (1544Ă—500), logo (256Ă—256), select an author, set a short description, and configure homepage/donation/support links.

Tip: Set the author's Display Name in their WordPress user profile for proper attribution.


Plugin Headers

For users to receive updates from your Troy Server, your plugin needs the Troy: header pointing to your repository URL.

Example Plugin Header

<?php
/**
 * My Plugin Name
 *
 * @package   Company\MyPluginName
 * @author    J Doe
 * @copyright 2025 J Doe, Company (https://example.com/)
 * @license   MIT
 *
 * @troy-repo
 * Troy: repo.example.com
 *
 * @wordpress-plugin
 * Plugin Name: My Plugin Name
 * Plugin URI: https://example.com/my-plugin
 * Description: This is a short description of my plugin.
 * Version: 1.2.3
 * Author: J Doe
 * Author URI: https://example.com/
 * License: MIT
 * Text Domain: my-plugin-slug
 * Requires at least: 6.8
 * Tested up to: 6.9
 * Requires PHP: 7.4
 */

Header Reference

HeaderRequiredDescription
TroyYesRepository URL (max 191 chars). Without this, the version will be blocked.
VersionYesMust follow SemVer format (e.g., 1.2.3).
Plugin NameYesStandard WordPress requirement.
Tested up toNoHighest WordPress version tested. Falls back to latest WordPress version if missing.
Requires at leastNoMinimum WordPress version.
Requires PHPNoMinimum PHP version.
Troy DependencyNoPlugin dependencies. See Dependencies.

Repository URL Format

The Troy: header accepts flexible URL formats. All of these are valid:

Troy: example.com
Troy: sub.example.com/
Troy: sub.sub.example.com/repo/path
Troy: localhost/repo
Troy: 85.10.155.46/repo/
Troy: [2a01:7c8:bb0b:34d:0:aced:1337:46]:443/path/to/repo

Troy Server normalizes these to fully-qualified HTTPS URLs automatically. So, although Troy: https://example.com/ is valid, it's not necessary to include the scheme or trailing slash; Troy: example.com is exactly the same.

Version Detection:

Versions containing -dev, -alpha, -beta, -rc, or similar suffixes are automatically detected as Beta releases. For example, 1.2.3-beta.1 or 2.0.0-rc1 will be sorted as beta versions when using GitHub or WordPress.org integrations.


Version Types

Every uploaded version has a type that determines its availability:

TypeDescriptionDownloadable
TagStable release. Served to all users.âś… Yes
BetaPre-release. Only served to users on the beta channel.âś… Yes (beta channel)
UnreleasedNot approved for distribution. Default for new uploads.❌ No

Changing Version Types

  1. In the plugin editor sidebar, find the Versions panel
  2. Click the version you want to modify
  3. Select Tag, Beta, or Unreleased
  4. Save or Update the post

Blocked Versions:

If a version fails validation during upload (missing Troy: header, invalid version format, etc.), it will be blocked. Blocked versions are stored with their file hash to prevent re-processing.

To fix a blocked version, correct the issue in your plugin and upload a new release.


Plugin Status Options

StatusBehavior
PublicAvailable to everyone with Troy Client
UnlistedOnly serves updates to users who already have the plugin installed
PendingNot yet available. Automatically converts to Public when published.
DisabledBlocked from all update requests

Deleting vs Disabling:

If you delete a plugin, its files move to a graveyard directory for potential recovery. If you're unsure, set it to Disabled instead—this blocks access while keeping everything intact. See File Recovery for details.


Dependencies

Plugins can declare dependencies on other Troy-hosted plugins using the Troy Dependency or Troy Dependencies header.

Dependency Format

Troy Dependency: plugin-slug
Troy Dependency: plugin-slug <repo.example.com>
Troy Dependencies: first-plugin, second-plugin <other-repo.com>
  • Simple format: plugin-slug — Fetches from the same repository as the dependent plugin's Troy: header.
  • With repository: plugin-slug <repo-uri> — Fetches from the specified repository. Angle brackets are required.
  • Multiple: Comma-separated list, max 5 dependencies.

How Dependencies Work

  1. Troy Server validates dependency format on upload and displays them in the Versions panel
  2. Troy Client checks for missing dependencies and attempts installation
  3. If a dependency isn't available, the user sees a warning and Client retries every minute

Use Dependencies Sparingly:

Dependencies force installation without user consent. For optional plugins, use a Package instead—Packages are opt-in and give users control over what gets installed.


Instead of manually uploading ZIPs, connect your GitHub repository:

  1. Go to your plugin's edit screen
  2. Find the GitHub Integration panel
  3. Enter your repository (e.g., your-username/your-plugin)
  4. Optionally add a Personal Access Token for private repos
  5. Set Auto-process to control which releases are imported
  6. Save

Troy Server checks for new tags every 30 minutes and processes up to 2 tags each minute.

Auto-Process Options

SettingBehavior
All new tagsImport both stable and beta releases
Stable releases onlyOnly import versions without pre-release suffixes
Beta releases onlyOnly import pre-release versions
NoneDisable automatic imports (manual only)

Version Type Mismatch:

If your tag name suggests a beta (e.g., v1.0.0-beta) but the plugin header contains a stable version (e.g., Version: 1.0.0), the version will be kept as Unreleased and you'll see a warning.

See GitHub Integration for detailed setup.


Readme File Format

Troy Server parses readme.txt (or readme.md) files from your plugin ZIP to extract additional metadata.

Troy-Exclusive Headers

HeaderDescription
LocaleLanguage/region code (e.g., en_US). Planned feature.
Short DescriptionBrief summary of the plugin.
Homepage URLMain website for the plugin.
Support URIWhere users can get support.

Example Readme (Troy-only)

=== My Plugin Name ===
Homepage URL: https://example.com/
Locale: en_US
Short Description: This is an example plugin that demonstrates the proper format.
Donate link: https://github.com/sponsors/example
Support URI: https://example.com/support

== Description ==

A word about my plugin...

Example Readme (WordPress.org Compatible)

If you also publish on WordPress.org, include their required headers. You can leave out Short Description:

=== My Plugin Name ===
Contributors: WordPressOrgUserHandle
Donate link: https://github.com/sponsors/example
Support URI: https://example.com/support
Homepage URL: https://example.com/
Tags: example, demo, sample
Requires at least: 6.8
Tested up to: 6.9
Requires PHP: 7.4.0
Stable tag: 1.2.3
License: GPLv3
License URI: http://www.gnu.org/licenses/gpl-3.0.html
Locale: en_US

This is an example plugin.

== Description ==

A word about my plugin...

Tested up to:

The Tested up to header in your plugin file is exclusive to Troy. You don't need to duplicate it in readme.txt since Troy reads it from the plugin headers.

For more details on readme format, see WordPress.org's readme documentation.


Statistics

Troy Server tracks anonymous statistics:

  • Download counts per version
  • Active installations (estimated via rotating weekly IDs)
  • PHP and WordPress version distribution
  • Locale distribution

All data is anonymized. No domain names or personal information is collected.


Next Steps