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.
Decisions
| Topic | Decision | Why |
|---|---|---|
| Billing shape | Nothing 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 owner | rochen-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 billed | Compute 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 pool | Unlimited 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. |
| Software | cPanel, 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 gate | Stored card required and account in good standing; the server deploys on order. | Nothing is invoiced up front, but AWS cost starts at launch. |
| Catalogue | One 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. |
| Automation | Resumable queued jobs in rochen-api (the MigrateServerJob pattern); per-service AWS member account. | The legacy awscron path was never activated and has defects. |
| Code layout | Servers/AWS shared SDK wrappers · Services/AWS stays MCS-only · new Services/RochenOne/Dedicated. | The two products never share business logic, only AWS calls. |
| Hostname | Auto-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
parent_id = pool; never children of the R1 containerCatalogue
Product 464
| Field | Today | Becomes |
|---|---|---|
tblpricing (type product) | −1 in every currency | stays −1: there is no advance amount; tblhosting.amount is 0.00 as on every Rochen One service |
servergroup | 17 "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 |
autosetup | order | '': WHMCS must never call the legacy aws_CreateAccount |
tblproductconfiglinks | groups 8 and 10 | links removed, no group linked (region groups are found by name; the legacy module reads every linked group and would collide) |
| Custom fields | hostname, aws_username, aws_id, instance_id, zabbix_id, can_reboot, can_scale | add 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.
| Option | Type | Range | Priced? | Meaning |
|---|---|---|---|---|
Templates | dropdown | one sub-option per instance size, r1d.<type> | yes: monthly = hourly × 744 | the compute rate for the region |
IOPS | quantity | 3000–16000 | per 100 above 3000 | gp3 baseline included |
Throughput | quantity | 125–1000 MiB/s | per 25 above 125 | gp3 baseline included |
DataTransfer | quantity | 500 GB allowance | msetupfee allowance, monthly per GB above | outbound transfer |
cPanelLicence · LiteSpeedLicence · ImunifyLicence | dropdown | tier names | no | records the tier the system assigned, visible in the WHMCS service page (Chris's suggestion) |
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.
| Software | Provider | Tier rule |
|---|---|---|
| cPanel & WHM | Manage2 (add to IP, change package) | 1 account → Solo · 2–5 → Admin · 6–30 → Pro · 31–100 → Premier 100 · then Premier 150, 200 … 500 |
| Imunify360 | CLN key, usage limit | 1 → 1 user · 2–30 → 30 users · 31–250 → 250 users · 251+ → unlimited |
| LiteSpeed | LiteSpeed (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 · KernelCare | CLN keys | one per server |
| JetBackup | JetLicense | one per server |
| MailChannels | Console sub-account | one per server |
| Softaculous | Softaculous 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.
| Attribute | Value |
|---|---|
| Product | new row, gid 70 "Rochen One", servertype = one, type = hostingaccount, every tblpricing row −1; tblhosting.amount always 0.00 |
| Option groups | none: no configurable options at all |
| Custom fields | parent_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 |
| Server | tblhosting.server = the pool's own tblservers row, so the Rochen One site code and the worker address the right box |
| Billing | never: no rochen_one_logs rows, never a child of the R1 container, nextduedate advanced with the pool's so WHMCS skips it |
| Counts for | the 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.
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
- Add a server rowtblservers: name "Paris", hostname
eu-west-3; add it to the Dedicated server group. - 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. - Add Templates sub-optionsOne per instance size offered there, named
r1d.<type>, monthly price = hourly × 744. - Fill dedicated_templatesIn the WHMCS admin page: region AMI, vCPU, RAM, EBS-optimized, burstable, description.
- Run the check
php artisan dedicated:check-cataloguemust pass. The region appears in the store on the next product fetch.
Operational decisions (Ben's review, Chris's answers)
| # | Point | Decision / proposal |
|---|---|---|
| 1 | Snapshots | No 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. |
| 2 | 1Password | Deployment 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. |
| 3 | Failed deployment | Licences 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. |
| 4 | Deletion | The 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. |
| 5 | Elastic IP after termination | Held 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. |
| 6 | Hostname | Auto-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. |
| 7 | Largest instance / non-payment risk | proposal 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. |
| 8 | New-account monitoring | proposal 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
| Point | Working assumption |
|---|---|
| Storage tiering | Bands 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 region | m7i family starter set in the mock-up with example prices; the real list is data entry |
| LiteSpeed | Tier 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 alerts | Proposals in the table above; thresholds to agree with Chris and Ben |
| Tier hysteresis | Up at once, down after 24 h below the threshold |
| Defaults | 3-day Elastic IP hold after termination · 24 h abort window on immediate cancellation |
| Legacy security fix | Independent of this product: stop sending keys/aws.json as instance user-data in the legacy module and rotate those keys |