Product design v2 · 1 of 6

Rochen One Dedicated

A dedicated compute pool: a single-tenant cPanel server on AWS EC2 in the region the client chooses, with all software included (cPanel, LiteSpeed, CloudLinux, Imunify360, KernelCare, JetBackup, MailChannels, Softaculous), as many sites as they like at no extra charge, managed from the app exactly like Rochen One Shared, and everything billed hourly in arrears by rochen-api.

DecisionsThe model in one pictureCatalogueIncluded software & tiersDedicated Site productNew tablesCode layoutAdding a regionOperational decisionsOpen points

Decisions

TopicDecisionWhy
Billing shapeNothing in advance, everything hourly in arrears. No management fee, no software lines, no per-site charge.Chris: one price, all software included, billed like Rochen One usage.
Billing ownerrochen-api generates every Dedicated invoice from the change log. WHMCS never invoices product 464 or the sites on a pool.Dedicated is tracked only in new tables; nothing of it lives in the legacy aws or R1 log tables.
What is billedCompute by template (region-priced) · storage allocated to the pool (100 GB minimum, R1 storage tier rates) · IOPS and throughput above the gp3 baseline · data transfer over the allowance.Storage and extras are metered exactly like the rest of Rochen One.
Sites on a poolUnlimited and free, each a service of a new always-free product, Rochen One Dedicated Site, managed from the app exactly like Rochen One Shared sites. No WHM or root access for the client. Resource limits and disk quota per site are set by the client (tier or custom) and tracked on the service.Chris: manage just the same as R1 shared; no billing for resource limits. A product row keeps the tracking in WHMCS and the price at zero for good.
SoftwarecPanel, LiteSpeed, CloudLinux, Imunify360, KernelCare, JetBackup, MailChannels and Softaculous, all included. The licence tier is assigned by the system from instance type and the number of sites on the pool, and moves up or down as sites come and go.Chris + Jeremy's rules; no upsells.
Deploy gateStored card required and account in good standing; the server deploys on order.Nothing is invoiced up front, but AWS cost starts at launch.
CatalogueOne product (464), a new Dedicated server group, one option group per region matched by name.Adding a region is data entry in WHMCS, no code.
AutomationResumable queued jobs in rochen-api (the MigrateServerJob pattern); per-service AWS member account.The legacy awscron path was never activated and has defects.
Code layoutServers/AWS shared SDK wrappers · Services/AWS stays MCS-only · new Services/RochenOne/Dedicated.The two products never share business logic, only AWS calls.
HostnameAuto-generated adjective-noun.directrouter.com; DNS in Cloudflare and reverse DNS created by the deployment.Rochen controls the zone; random names rule out phishing-style labels (Ben / Chris).

The model in one picture

Dedicated pool · product 464
one EC2 server in one region
template (size) · storage allocation · IOPS/throughput
billed hourly by rochen-api, nothing in advance
has → sites, software, a change log
Sites on the pool · product "Rochen One Dedicated Site"
unlimited, always free; tier or custom limits and a disk quota tracked per site
created and managed from the app only (Rochen One site code on the pool's server); no WHM access
rows with parent_id = pool; never children of the R1 container
Included software
cPanel & WHM · LiteSpeed · CloudLinux · Imunify360 · KernelCare · JetBackup · MailChannels · Softaculous
tier chosen from instance type + number of sites on the pool; re-tiered automatically
Change log = billing timeline
every applied change stores the whole configuration
create, resize, disk grow, IOPS/throughput, terminate open a new usage span; licence re-tiers and site events are logged but not billed
What the client sees
One product. GET /v1/product/464?region=506 returns the regions, that region's template prices, the storage tiers, the extras and the included-software list. POST /v1/order with one services[] item creates the pool and deploys it. The app shows the pool with its sites, software tiers, costs and history; "Create Site" offers "Rochen One (shared)" or any of the client's pools.

Catalogue

Product 464

FieldTodayBecomes
tblpricing (type product)−1 in every currencystays −1: there is no advance amount; tblhosting.amount is 0.00 as on every Rochen One service
servergroup17 "AWS US"new group Dedicated holding the region servers 498 London (eu-west-2), 500 Ohio (us-east-2), 506 Frankfurt (eu-central-1); MCS groups untouched
autosetuporder'': WHMCS must never call the legacy aws_CreateAccount
tblproductconfiglinksgroups 8 and 10links removed, no group linked (region groups are found by name; the legacy module reads every linked group and would collide)
Custom fieldshostname, aws_username, aws_id, instance_id, zabbix_id, can_reboot, can_scaleadd disk_size (allocated GB, as sites have); hostname is no longer written, the name lives on the pool's tblservers row
tblhosting.username / password—null, as on the Shared container. The instance's root password and WHM token are stored once, WHMCS-encrypted on the pool's own tblservers row, exactly like every cPanel server. Never exposed to the client

Region option groups

Naming rule: Rochen One Dedicated (<region>) where <region> equals the hostname of a server in the Dedicated group. Prices in tblpricing per currency.

OptionTypeRangePriced?Meaning
Templatesdropdownone sub-option per instance size, r1d.<type>yes: monthly = hourly × 744the compute rate for the region
IOPSquantity3000–16000per 100 above 3000gp3 baseline included
Throughputquantity125–1000 MiB/sper 25 above 125gp3 baseline included
DataTransferquantity500 GB allowancemsetupfee allowance, monthly per GB aboveoutbound transfer
cPanelLicence · LiteSpeedLicence · ImunifyLicencedropdowntier namesnorecords the tier the system assigned, visible in the WHMCS service page (Chris's suggestion)
Storage is not in the region groupsThe pool's allocation is priced with the Rochen One storage tiers (the Shared DiskSize [1-5] … [201-*] sub-options in the client's currency), the same in every region. rochen-api walks those bands over the client's total dedicated storage. Folding shared-site storage into the same band is not done in v1 (it would need WHMCS billing code); see Open points.

dedicated_templates holds one row per Templates sub-option (AMI per region, vCPU, RAM, EBS-optimized, burstable, description), edited from a new WHMCS admin page cloned from configawstemplates.php. php artisan dedicated:check-catalogue verifies regions, groups, templates and prices.

Included software & tiers

Licence products are not sold any more; they only supply the Manage2 package ids and CLN product codes. The tier rules live in config/dedicated.php. The count that drives them is the number of live Dedicated Site services on the pool (parent_id = pool, Active, Pending or Suspended); a daily reconcile compares it with the cPanel account list on the server.

SoftwareProviderTier rule
cPanel & WHMManage2 (add to IP, change package)1 account → Solo · 2–5 → Admin · 6–30 → Pro · 31–100 → Premier 100 · then Premier 150, 200 … 500
Imunify360CLN key, usage limit1 → 1 user · 2–30 → 30 users · 31–250 → 250 users · 251+ → unlimited
LiteSpeedLiteSpeed (API to confirm; change of type by ticket in v1)m7i.large: ≤5 accounts → Site Owner, >5 → Web Host Lite · m7i.xlarge: ≤5 → Site Owner Plus, >5 → Web Host Essential · 2xlarge → Professional · 4xlarge → Enterprise · 8xlarge and up → Elite (instance decides alone from 2xlarge)
CloudLinux · KernelCareCLN keysone per server
JetBackupJetLicenseone per server
MailChannelsConsole sub-accountone per server
SoftaculousSoftaculous NOC API (licence to the IP; API to confirm, ticket in v1 until then)one per server

When tiers move

A site created or deleted, a template change, and a daily dedicated:retier-licences reconcile. Up-tier at once; down-tier only after 24 hours below the threshold.

What a re-tier does

Provider call (change package, update key limit), the three tracking options updated, a change_licence row in the change log. Never a line on an invoice.

Where ids live

dedicated_instances.licences json: provider, tier, external id or key id per software. Secrets only in SSM.

Sites on a pool: the "Rochen One Dedicated Site" product

Every site on a pool is a service of a new product, Rochen One Dedicated Site (the pool's counterpart of "Rochen One Shared SubAccount"). It is always free and exists to track what the client has deployed and how the pool's resources are split between sites.

AttributeValue
Productnew row, gid 70 "Rochen One", servertype = one, type = hostingaccount, every tblpricing row −1; tblhosting.amount always 0.00
Option groupsnone: no configurable options at all
Custom fieldsparent_id (the pool) · disk_size (GB quota) · speed · pmem · io · iops · ep · nproc — the effective LVE limits. The app's Tier 1–8 picker is a convenience that fills these fields; Custom lets the client type them
Servertblhosting.server = the pool's own tblservers row, so the Rochen One site code and the worker address the right box
Billingnever: no rochen_one_logs rows, never a child of the R1 container, nextduedate advanced with the pool's so WHMCS skips it
Counts forthe licence tiers (live rows with parent_id = pool), the pool's site list and its resource-distribution view

Created from the app only

"Create Site" offers Rochen One (shared) or any of the client's pools, the way it offers Premium plans today. RochenOne::store() routes a Dedicated Site to Dedicated::storeSite(), which reuses the Shared site path (cPanel account, DNS, quota, LVE limits, Softaculous, mail) on the pool's server. The client never gets WHM or root.

Resources distributed as the client wishes

Each site gets limits (CPU speed, physical memory, IO, IOPS, entry processes, max processes) and a disk quota, chosen from a tier preset or typed in, stored in its custom fields. The pool page shows the sum of quotas and limits against the pool's allocation and instance size, warns when oversubscribed, and blocks only a single quota larger than the pool's volume.

Data transfer

Unchanged: tracked per pool from Cost Explorer, not per site.

Same pages as a shared site

Details, resources (now free, with the custom form), disk, PHP version, monitoring, migration and delete all work for a Dedicated Site; only the price labels disappear.

New tables

Three tables in rochen-api migrations (2026_10_01_00000N_*). Nothing from aws, aws_template, aws_data_transfer or rochen_one_logs is used.

tblhosting (WHMCS)
pool: product 464, amount 0
pool: username and password null; server = the pool's own tblservers row
sites: product "Rochen One Dedicated Site", parent_id = pool, server = the pool's server row, limits in custom fields, no options
status, nextduedate (advanced by rochen-api)
hosting_id → both new tables
dedicated_instances · 1 row per pool
AWS-side configuration only: instance_type, template_id, image_id, disk_size (allocation), volumes (ids, size, iops, throughput), eip allocation/association ids, key pair name, security groups, aws account id, zabbix host
server_id = the pool's own tblservers row, created right after the Elastic IP is allocated; region_server_id = the region row. Hostname, IPs, root password and WHM token live only on tblservers
licences json · site_count (live rows, cached for listings)
lifecycle: status, plan json (resumable steps), timers
billing: billed_through, data_transfer json per month
template_id → dedicated_templates
dedicated_server_changes · append-only
billable: create, resize_instance, resize_disk, resize_iops_throughput, terminate
logged only: change_licence, site_added, site_removed, suspend, unsuspend, reboot, stop, start, rotate_key, cancel, cancel_termination, close_account, reconcile
actor · from · to (the complete resulting config) · billable
status requested → applying → applied | failed | rolled_back
applied_at is immutable: it is the billing timeline
dedicated_templates
option_id → Templates sub-option
region · instance_type · image_id
cpu · ram · ebs_optimized · burstable
description · active
edited in the WHMCS admin page
Rules
Every applied change stores the whole configuration, never a delta. Rows are never edited after they are applied; corrections are new reconcile rows. A usage span runs from one billable applied_at to the next.

Code layout

app/Libraries/Servers/AWS/*

Shared AWS SDK wrappers used by both products: ClientFactory, Organizations, SSOAdmin, Account, IAM, EC2, SSM, CostExplorer. No product logic.

app/Libraries/Services/AWS/*

Legacy Fully Managed Services only. Untouched.

app/Libraries/Services/RochenOne/Dedicated/*

Dedicated (show, store, showHosting, updateHosting, suspend, delete, costs, sites), Provisioning/Runner + steps, Licences/* providers behind a LicenceProvider contract + TierRules, Invoicing/RenewalInvoice, tickets.

Jobs & commands

DeployDedicatedInstanceJob, TerminateDedicatedInstanceJob, ResizeDedicatedInstanceJob, ResizeDedicatedDiskJob, RetierDedicatedLicencesJob, RotateDedicatedKeyJob; dedicated:* commands in cmd.html.

Adding a region

  1. Add a server rowtblservers: name "Paris", hostname eu-west-3; add it to the Dedicated server group.
  2. Create the option groupName it Rochen One Dedicated (eu-west-3). Add Templates, IOPS, Throughput, DataTransfer with regional prices in every sold currency, and the three licence-tracking dropdowns.
  3. Add Templates sub-optionsOne per instance size offered there, named r1d.<type>, monthly price = hourly × 744.
  4. Fill dedicated_templatesIn the WHMCS admin page: region AMI, vCPU, RAM, EBS-optimized, burstable, description.
  5. Run the checkphp artisan dedicated:check-catalogue must pass. The region appears in the store on the next product fetch.

Operational decisions (Ben's review, Chris's answers)

#PointDecision / proposal
1SnapshotsNo EBS snapshots. The 30-day figure was this design's invention; MCS has none. Backups are JetBackup to a per-server Wasabi bucket, which is what survives deletion. The "daily EBS snapshots" line leaves the included list.
21PasswordDeployment writes each server's credentials (root password, WHM token, licence keys, Wasabi key) as one item into a dedicated inbox vault through a 1Password service account that has access to that vault only. Staff move items to the real vault. The deployer never reads any other vault; the residual is that it can read its own inbox until items are moved. No manual step during deployment. SSM stays the source the instance itself boots from.
3Failed deploymentLicences bound to the IP are expired and the instance, IP and volumes released automatically when a deployment finally fails; a retry starts fresh (new IP, new licences), reusing the member account.
4DeletionThe 1Password item and the Wasabi bucket are kept. The system has no Wasabi delete permission; termination opens a Maintenance ticket "Wasabi bucket to review" and mails devops.
5Elastic IP after terminationHeld unassociated for 3 days (dedicated.ip_hold_days), then released by a daily command. A redeploy within the hold reuses the IP, so DNS never changes.
6HostnameAuto-generated adjective-noun.directrouter.com (for example signal-fox), unique, created in Cloudflare (API token scoped to the directrouter.com zone, DNS edit only). The client does not choose it; staff can rename on request. No phishing-style names possible.
7Largest instance / non-payment riskproposal per-account ceiling: accounts with fewer than 3 paid months (or any overdue history) may run one pool up to m7i.xlarge; established accounts up to m7i.4xlarge; anything larger, or more pools, needs a staff approval recorded on the client (dedicated_limits custom field or config override). The store hides sizes above the ceiling and explains why.
8New-account monitoringproposal a "new pool created" mail to devops with client, fraud score, template and region; daily data-transfer log raises a ticket when a pool younger than 30 days passes 1 TB in 7 days, or any pool passes 5 TB in a month; a weekly digest of pools per account age. Manual watch of new accounts remains the rule initially, as Chris said.

Open points

PointWorking assumption
Storage tieringBands walked over the client's total dedicated storage (all pools) at the R1 storage rates. Rolling shared-site storage into the same band would need WHMCS billing code, so it is not done in v1. Confirm with Chris.
Dedicated Site product name"Rochen One Dedicated Site" (alternative: "Rochen One Pool Site")
Instance sizes per regionm7i family starter set in the mock-up with example prices; the real list is data entry
LiteSpeedTier rules from Jeremy's sheet; xlarge with ≤5 accounts read as Site Owner Plus. Licence API to confirm; type changes by ticket until then
Instance-size ceiling and new-account alertsProposals in the table above; thresholds to agree with Chris and Ben
Tier hysteresisUp at once, down after 24 h below the threshold
Defaults3-day Elastic IP hold after termination · 24 h abort window on immediate cancellation
Legacy security fixIndependent of this product: stop sending keys/aws.json as instance user-data in the legacy module and rotate those keys
Rochen One Dedicated · product design v2 · September 2026