These docs have been generated using AI. Expect inaccuracies until we remove this banner.
- Docs
- Troy Server
- Plugin Management
Adding Plugins to Your Repository
Once Troy Server is installed, adding plugins is straightforward. You have four options:
- Upload a ZIP file — Select a file, done
- Connect a GitHub repository — Automatic releases from tags
- Connect a WordPress.org plugin — Automatic releases from tags
- 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
Create a New Plugin Post
In your WordPress admin:
- Go to Plugins → Add New Plugin
- A new plugin post opens in the Block Editor
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.
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.
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.
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
| Header | Required | Description |
|---|---|---|
Troy | Yes | Repository URL (max 191 chars). Without this, the version will be blocked. |
Version | Yes | Must follow SemVer format (e.g., 1.2.3). |
Plugin Name | Yes | Standard WordPress requirement. |
Tested up to | No | Highest WordPress version tested. Falls back to latest WordPress version if missing. |
Requires at least | No | Minimum WordPress version. |
Requires PHP | No | Minimum PHP version. |
Troy Dependency | No | Plugin 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:
| Type | Description | Downloadable |
|---|---|---|
| Tag | Stable release. Served to all users. | âś… Yes |
| Beta | Pre-release. Only served to users on the beta channel. | âś… Yes (beta channel) |
| Unreleased | Not approved for distribution. Default for new uploads. | ❌ No |
Changing Version Types
- In the plugin editor sidebar, find the Versions panel
- Click the version you want to modify
- Select Tag, Beta, or Unreleased
- 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
| Status | Behavior |
|---|---|
| Public | Available to everyone with Troy Client |
| Unlisted | Only serves updates to users who already have the plugin installed |
| Pending | Not yet available. Automatically converts to Public when published. |
| Disabled | Blocked 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'sTroy: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
- Troy Server validates dependency format on upload and displays them in the Versions panel
- Troy Client checks for missing dependencies and attempts installation
- 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.
Connecting GitHub (Recommended)
Instead of manually uploading ZIPs, connect your GitHub repository:
- Go to your plugin's edit screen
- Find the GitHub Integration panel
- Enter your repository (e.g.,
your-username/your-plugin) - Optionally add a Personal Access Token for private repos
- Set Auto-process to control which releases are imported
- Save
Troy Server checks for new tags every 30 minutes and processes up to 2 tags each minute.
Auto-Process Options
| Setting | Behavior |
|---|---|
| All new tags | Import both stable and beta releases |
| Stable releases only | Only import versions without pre-release suffixes |
| Beta releases only | Only import pre-release versions |
| None | Disable 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
| Header | Description |
|---|---|
Locale | Language/region code (e.g., en_US). Planned feature. |
Short Description | Brief summary of the plugin. |
Homepage URL | Main website for the plugin. |
Support URI | Where 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
- Create a Package for easy distribution
- Connect GitHub for automatic releases
- Read the API docs if you need to integrate programmatically
