Architecture Overview
Pterodactyl Revamp is an admin-side operations layer that lives inside the Pterodactyl Panel (1.12.x–1.14.x). It is not a standalone app and it has no node-side component: everything runs inside the panel's Laravel process, talks to Wings through the same Daemon*Repository classes the core panel uses, and stores its state in a set of revamp_* MySQL tables alongside the core schema.
The codebase is organized as a classic Laravel add-on: a service provider (RevampServiceProvider) registers routes, migrations, views, view composers, and the scheduler; controllers stay thin and delegate to app/Services/Revamp/*; queued work runs on a dedicated revamp queue. The admin UI is plain Blade enhanced by jQuery "islands" served from public/ext/revamp/, with an optional React bundle layered on top.
Component Overview
Three route surfaces exist, all registered by RevampServiceProvider::registerRoutes() except the client route, which ships as a Blueprint client router:
| Surface | Prefix | Middleware | File |
|---|---|---|---|
| Admin web UI | /admin/revamp | web, auth.session, 2FA, AdminAuthenticate | routes/admin-revamp.php |
| Application API | /api/application/revamp | api, application-api, throttle:api.application, RequireRevampRootAdmin | routes/api-revamp.php |
| Client API | /api/client/servers/{server}/tags | ServerSubject, AuthenticateServerAccess | routes/blueprint/client/revamp.php (Blueprint install only) |
Application API keys are not enough
Every /api/application/revamp route additionally passes through RequireRevampRootAdmin, so an API key only grants Revamp access if its owner is a root admin. This surface exists for external integrations (WHMCS and similar billing systems) and mirrors the admin controllers.
The admin UI itself is reachable at /admin/revamp and, on Blueprint installs, through the Extensions hub (admin.extensions.pterodactylrevamp.index), which renders the same revamp::admin.revamp._overview partial. No core sidebar link is injected by default.
Frontend layers
- Blade views — the Revamp dashboard, tags, templates, health, audit log, activity, settings, and multi-create pages under
resources/views/admin/, loaded through therevamp::namespace byloadViewsFrom(). - jQuery islands — static assets in
public/ext/revamp/(server-create helpers, allocation port picker, multi-server create, server-list bulk/filters/tags, template admin). They are pulled in bypartials/revamp-admin-assets.blade.php, which is injected intolayouts/admin.blade.phpand only loads the scripts relevant to the current route. - Optional React bundle —
resources/scripts/blueprint/revamp/index.tsx(AllocationPicker, ServerMetricsChart, BulkJobProgress, MultiServerCreate, GlobalSearch) is bundled bydata/build-revamp.shwith esbuild intopublic/ext/revamp/app.js. The assets partial only includesapp.jswhen the file exists, so the addon works fully without it. - View composers —
RevampServiceProvidercomposes tag data into the stockadmin.servers.new,admin.servers.view.details, andadmin.servers.indexviews, so the patched core forms render tag pickers with live data.
Queue & Scheduler
All heavy work is queued. The scheduler only runs thin command entrypoints; each command dispatches one or more jobs onto the dedicated revamp queue, and a queue worker picks them up. Schedules are registered at service-provider boot time — no Kernel.php edit is needed, only the stock Pterodactyl cron entry running schedule:run every minute.
| Command | Cadence | Job(s) | Writes |
|---|---|---|---|
revamp:sample-metrics | Every 5 minutes | SampleServerMetricsJob (per 50-server chunk) | revamp_metric_samples |
revamp:rollup-metrics | Hourly | RollupHourlyMetricsJob | revamp_metric_rollups_hourly (also purges raw samples) |
revamp:compute-health | Every 10 minutes | ComputeHealthJob | revamp_server_health_snapshots, revamp_node_health_snapshots, revamp_node_scores (also purges old snapshots) |
revamp:evaluate-rules | Daily at 03:00 | EvaluateUpgradeRulesJob | revamp_recommendations |
Behavior worth knowing:
- Rate-limited sampling —
MetricSamplingServiceskips a server if a sample already exists within the last 4 minutes, so overlapping schedule runs never double-sample. One unresponsive server is logged and skipped, never aborts the batch. - Health retention —
ComputeHealthJobpurges health snapshots older than thehealth_retention_dayssetting (default 30) on every run; raw metric samples are purged by the rollup job pastmetrics_retention_days(default 90). Hourly rollups are kept indefinitely. - Sync fallback — bulk operations are dispatched to the
revampqueue by default; settingREVAMP_BULK_SYNC=trueruns them synchronously instead, which is useful on panels without a worker during testing. - Wings touchpoints — metric sampling reads utilization through
DaemonServerRepository::getDetails(); bulk power actions go throughDaemonPowerRepository. Bulk suspend/unsuspend/delete/reinstall reuse the coreSuspensionService,ServerDeletionService, andReinstallServerService, so core behavior (including Wings-side cleanup) is preserved.
Queue worker is mandatory
Without php artisan queue:work --queue=revamp,default, no metrics, health snapshots, recommendations, or bulk operations will ever process. See Installation for the supervisor setup.
Bulk move is opt-in
BulkMoveService only re-points panel database records to another node — no Wings data transfer happens. The entire path throws a DisplayException unless the bulk_move_enabled setting is explicitly turned on (default off).
Database Schema
Ten migrations create the revamp_* schema. Foreign keys point into the core servers, nodes, users, and allocations tables; most cascade on delete so Revamp rows disappear with their core subjects. A later migration makes revamp_admin_audit_logs.actor_id nullable via a raw ALTER, because audit entries can originate from queued/CLI contexts with no authenticated user.
Notes on the schema:
revamp_settingsis a key/value table seeded with 14 defaults at migration time (naming pattern, allocation page size, retention windows, upgrade thresholds, health thresholds, tag visibility). All reads go throughRevampSettingsRepository's 5-minute cache, with typed fallbacks for keys missing from the table (includinghealth_retention_days= 30 andbulk_move_enabled= false).revamp_tagsis seeded with seven default tags (Premium, Suspended, Unstable, Abusive, Trial, Enterprise, Migrated);revamp_server_tagis the server pivot with a composite primary key.revamp_server_groups+revamp_server_group_memberare created by the migrations but are currently unused — no controller, service, or model references them. They are scaffolding for a future grouping feature.revamp_template_revisions.configstores the full deploy configuration as JSON in the same shape as the coreServerController@storepayload, which is what makes one-click deploy-from-template possible.revamp_eventsis Revamp's own event stream, supplemental to the coreactivity_logs.BulkMoveServicewrites move events here,ServerHealthServicereads it to count restarts over the health window, andActivityControllermerges it with core activity logs into a unified feed.revamp_user_allocation_recentspowers the allocation picker's per-admin "recently used ports" list; the composite key is(user_id, allocation_id)andused_atbumps on every reuse.
Blade Patching Mechanism
Revamp ships UI for stock core pages (server create, server details, server list), which means it must modify core Blade templates. It does this with a marker-guarded Python patcher that is fully reversible.
Every injected block is wrapped in HTML comments:
<!-- pterodactylrevamp-server-list-filters-start -->
<div class="revamp-server-filters-host clearfix" id="revamp-server-filters-blade"></div>
<!-- pterodactylrevamp-server-list-filters-end -->Patched core templates and what goes in:
| Template | Injections |
|---|---|
layouts/admin.blade.php | assets (@include('partials.revamp-admin-assets')) and nav-search (global search mount point in the navbar) |
admin/servers/new.blade.php | Auto-naming checkbox + preview, inline tag picker, allocation port buttons (previous/next/random/favorites), additional-allocation picker, Multi Create button + modal, favorite-ports and field-help modals, script/style includes |
admin/servers/view/details.blade.php | Tag picker pre-seeded with the server's current tags |
admin/servers/index.blade.php | Filter bar host, bulk-select checkbox column, per-row tag labels, pagination simplification |
Idempotency by design:
- If a block's start marker already exists, the patcher prints
already presentand skips it — reinstalling is a no-op. - Before injecting,
strip_revamp_blocks()removes orphaned markers (a start without an end, or vice versa) left by an interrupted run, while leaving well-formed blocks untouched. inject_replace()keeps the original line as a Blade comment ({{-- pterodactylrevamp-original: ... --}}) so the unpatcher can restore it byte-for-byte.- Missing anchor strings produce a warning, never a hard failure — the patcher degrades gracefully across minor panel version differences.
Removal (data/remove.sh): deletes the provider line from config/app.php by matching the // pterodactylrevamp comment, removes any sidebar block, then clears config/view caches. The Blueprint copy additionally restores the stock admin overview from vanilla/admin-index.blade.php and runs unpatch-blades.py, which strips every marker-wrapped block and restores anchor-preserved originals byte-for-byte — on a standalone removal, run unpatch-blades.py yourself with PTERODACTYL_DIRECTORY set. The revamp_* tables are left in place either way (drop them manually if you want a clean slate).
Stale bytecode guard
install.sh deletes the patcher's __pycache__ before running it — without that, Python can execute an old cached patch-blades even when the .py file was updated, producing confusing "already present" output on a fresh install.
Repository Layout
The repo ships the same panel payload twice: once inside the Blueprint extension wrapper, once standalone. blueprint/pterodactylrevamp/data/ and standalone/data/ are kept identical (install scripts, patchers, vanilla template).
pterodactyl-revamp/
├── README.md # install-path overview, requirements
├── blueprint/ # Blueprint extension (recommended path)
│ ├── pterodactylrevamp.blueprint # packaged extension archive
│ └── pterodactylrevamp/ # extension source (identifier: pterodactylrevamp)
│ ├── conf.yml # Blueprint manifest — version 1.2.0, target beta-2026-06
│ ├── controller.php # Extensions hub controller (renders the overview)
│ ├── view.blade.php # Extensions hub view (includes revamp::admin.revamp._overview)
│ ├── components/ # Blueprint dashboard components (Components.yml, TSX)
│ ├── routes/
│ │ ├── web.php # empty shell — real routes come from the provider
│ │ └── blueprint/client/revamp.php # client API: GET /servers/{server}/tags
│ └── data/ # install/remove scripts + PanelFiles payload
│ ├── install.sh # merge, provider registration, patching, migrate
│ ├── remove.sh # full uninstall of core-file edits
│ ├── patch-blades.py # marker-guarded Blade injector
│ ├── unpatch-blades.py # strips injected blocks, restores originals
│ ├── build-revamp.sh # optional esbuild bundle → /ext/revamp/app.js
│ ├── vanilla/admin-index.blade.php # stock admin overview for restore
│ └── PanelFiles/ # full panel tree (see below)
└── standalone/ # manual merge path (no Blueprint)
├── README.standalone.txt
├── data/ # same scripts as blueprint/.../data
└── PanelFiles/ # same payload as blueprint/.../data/PanelFilesPanelFiles/ mirrors the panel root and merges directly over it:
PanelFiles/
├── app/
│ ├── Console/Commands/Revamp/ # 4 scheduled command entrypoints
│ ├── Http/
│ │ ├── Controllers/Admin/Revamp/ # 14 admin controllers
│ │ ├── Controllers/Api/Client/Servers/RevampServerTagsController.php
│ │ ├── Middleware/RequireRevampRootAdmin.php
│ │ └── Requests/Admin/Revamp/ # StoreTagRequest, UpdateSettingsRequest
│ ├── Jobs/Revamp/ # 11 queued jobs (7 bulk + 4 scheduled)
│ ├── Models/Revamp/ # 13 Eloquent models over revamp_* tables
│ ├── Providers/RevampServiceProvider.php # routes, views, composers, migrations, schedules
│ └── Services/Revamp/ # Settings, BulkOps, Metrics, Health, Tags,
│ # Templates, Allocations, Search, Audit,
│ # Recommendations, Admin stats, RevampNav
├── config/revamp.php # admin_home_route (overwritten by install.sh on Blueprint)
├── database/migrations/ # 10 migrations → the revamp_* schema
├── public/ext/revamp/ # jQuery islands + CSS (allocation, server create,
│ # multi-create, server list, tags, templates)
├── resources/
│ ├── scripts/blueprint/revamp/ # optional React islands source (TSX + API clients)
│ └── views/ # revamp:: Blade views + partials/revamp-admin-assets
└── routes/
├── admin-revamp.php # /admin/revamp web routes
└── api-revamp.php # /api/application/revamp routesKeeping the copies in sync
blueprint/pterodactylrevamp/data/PanelFiles/ and standalone/PanelFiles/ are byte-identical payloads. When contributing, change one and sync the other — drifting copies are the most common source of "works on Blueprint, broken standalone" bugs.
