Skip to content

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:

SurfacePrefixMiddlewareFile
Admin web UI/admin/revampweb, auth.session, 2FA, AdminAuthenticateroutes/admin-revamp.php
Application API/api/application/revampapi, application-api, throttle:api.application, RequireRevampRootAdminroutes/api-revamp.php
Client API/api/client/servers/{server}/tagsServerSubject, AuthenticateServerAccessroutes/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 the revamp:: namespace by loadViewsFrom().
  • 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 by partials/revamp-admin-assets.blade.php, which is injected into layouts/admin.blade.php and 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 by data/build-revamp.sh with esbuild into public/ext/revamp/app.js. The assets partial only includes app.js when the file exists, so the addon works fully without it.
  • View composers — RevampServiceProvider composes tag data into the stock admin.servers.new, admin.servers.view.details, and admin.servers.index views, 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.

CommandCadenceJob(s)Writes
revamp:sample-metricsEvery 5 minutesSampleServerMetricsJob (per 50-server chunk)revamp_metric_samples
revamp:rollup-metricsHourlyRollupHourlyMetricsJobrevamp_metric_rollups_hourly (also purges raw samples)
revamp:compute-healthEvery 10 minutesComputeHealthJobrevamp_server_health_snapshots, revamp_node_health_snapshots, revamp_node_scores (also purges old snapshots)
revamp:evaluate-rulesDaily at 03:00EvaluateUpgradeRulesJobrevamp_recommendations

Behavior worth knowing:

  • Rate-limited sampling — MetricSamplingService skips 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 — ComputeHealthJob purges health snapshots older than the health_retention_days setting (default 30) on every run; raw metric samples are purged by the rollup job past metrics_retention_days (default 90). Hourly rollups are kept indefinitely.
  • Sync fallback — bulk operations are dispatched to the revamp queue by default; setting REVAMP_BULK_SYNC=true runs 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 through DaemonPowerRepository. Bulk suspend/unsuspend/delete/reinstall reuse the core SuspensionService, ServerDeletionService, and ReinstallServerService, 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_settings is 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 through RevampSettingsRepository's 5-minute cache, with typed fallbacks for keys missing from the table (including health_retention_days = 30 and bulk_move_enabled = false).
  • revamp_tags is seeded with seven default tags (Premium, Suspended, Unstable, Abusive, Trial, Enterprise, Migrated); revamp_server_tag is the server pivot with a composite primary key.
  • revamp_server_groups + revamp_server_group_member are 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.config stores the full deploy configuration as JSON in the same shape as the core ServerController@store payload, which is what makes one-click deploy-from-template possible.
  • revamp_events is Revamp's own event stream, supplemental to the core activity_logs. BulkMoveService writes move events here, ServerHealthService reads it to count restarts over the health window, and ActivityController merges it with core activity logs into a unified feed.
  • revamp_user_allocation_recents powers the allocation picker's per-admin "recently used ports" list; the composite key is (user_id, allocation_id) and used_at bumps 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:

html
<!-- 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:

TemplateInjections
layouts/admin.blade.phpassets (@include('partials.revamp-admin-assets')) and nav-search (global search mount point in the navbar)
admin/servers/new.blade.phpAuto-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.phpTag picker pre-seeded with the server's current tags
admin/servers/index.blade.phpFilter 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 present and 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).

text
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/PanelFiles

PanelFiles/ mirrors the panel root and merges directly over it:

text
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 routes

Keeping 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.