ConfigMap renders all non-secret env vars including dynamic DB_HOST
and REDIS_HOST. Secret template only renders when secret.create=true
(dev convenience); production references an existing Secret.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Initial scaffold for the ezscale-website chart. Defaults assume
self-contained local dev (in-cluster MariaDB + Valkey). Production
overrides will live in values-us-prod.yaml.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three named targets (app, horizon, scheduler) sharing a runtime-base
with PHP 8.3-FPM, opcache, redis, and pinned php-fpm pool config.
Composer + Node build stages are separate so vendor/ and public/build/
are baked into the runtime image.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Outreach notes don't belong in the repo. GETTING_STARTED reconciled
against current composer/npm scripts: fix Gitea clone URL, drop Vuexy
references, remove Redis requirement, replace multi-terminal startup
with `composer run dev`, update PHP/Node versions to 8.3/24, fix
branch workflow to main. TASKS.md: mark multi-currency and KB as done,
fix CI/CD reference from GitHub to Gitea Actions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Update codebase counts to live values, fix Gitea repo URL (was GitHub),
move multi-currency/KB from 'not yet implemented' to 'implemented',
refresh footer date.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
19 bite-sized tasks covering README/docs cleanup, multi-stage prod
Dockerfile, Helm chart with all templates, values-local + values-us-prod,
Gitea Actions release workflow, and a local k3d e2e smoke test.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Locks in the production deployment shape: Helm chart matching sister
ezscale-api pattern, multi-stage Dockerfile with three targets
(app/horizon/scheduler), operator-managed MariaDB CRDs that plug into
the existing ezscale-namespace MariaDB instance, per-app Valkey,
Traefik IngressRoute + cert-manager TLS, Storj for file storage.
Critical invariant captured: APP_KEY and Passport keys are bootstrapped
once and never regenerated by the chart.
Two environments: local (k3d/minikube) and us-prod.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds two sections to the project-level CLAUDE.md:
- "Strategic Direction: WHMCS Replacement" — scopes the long-arc plan
(storefront, billing, provisioning, customer area, marketing site,
admin RBAC, notifications, marketing email automation, future
registrar / SSL / multi-tenant / partner API) so future sessions
don't accidentally re-litigate decisions.
- "Sister Projects (Reference)" — calls out infrastructure/ (Ezra +
capacity / costs source-of-truth) and ezscale_api/ (Battlelog/ACP
SaaS) as separate Gitea repos with their own CLAUDE.md, plus the
catalog-data flow (website pulls hardware inventory + IP
availability from infrastructure/).
Also clarifies cross-repo conventions: Gitea (not GitHub), no shared
DBs, design docs under docs/superpowers/specs/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds two boolean/numeric feature flags on each 14th-gen plan:
- cpu_premium: chassis supports the Platinum 8280 CPU upgrade
(R440/R540 false; R640/R740/R740xd true)
- max_ram_gb: maximum RAM the chassis can physically host
(16-DIMM chassis cap at 1 TB; 24-DIMM at 1.5 TB)
These drive the route-level config-option filters in
routes/marketing.php so customers never see Platinum CPU on R440 or
the 1.5 TB RAM tier on a 16-slot chassis.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the bare-metal `composer run dev` workflow with a fully
containerized 9-service stack orchestrated by docker compose. Single
command brings up the full app — three subdomains (marketing /
account / admin) reachable via Traefik with TLS, MariaDB + Valkey
+ Mailpit + Vite HMR + Horizon + scheduler all wired in.
Components:
- docker-compose.yml: traefik, app (php-fpm), web (nginx), mariadb,
valkey, mailpit, vite, horizon, scheduler.
- docker/: Dockerfiles, nginx config, entrypoint scripts.
- Makefile: convenience targets (up / down / logs / shell / migrate
/ seed / test / pint / etc).
- .env.docker.example: template for Docker-stack environment vars
(separate from website/.env so bare-metal devs aren't disrupted).
- website/vite.config.ts: server.host / origin / hmr / cors hooks
driven by VITE_HOST / VITE_ORIGIN / VITE_HMR_HOST so the same
config serves both bare-metal and Docker.
- website/bootstrap/app.php: redirectGuestsTo() now uses
request()->getScheme() so http: dev hosts don't get force-https
redirects.
- composer.json: drops laravel/sail (replaced by this stack).
- docs/superpowers/specs/2026-04-25-docker-compose-dev-environment-design.md:
full design spec.
Bare-metal `composer run dev` workflow stays usable for anyone who
prefers it — Docker stack reads .env.docker, doesn't fight
website/.env.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Working tree was accumulating debris across sessions:
- Headless-Chrome / visual-companion screenshots in repo root (dozens
of throwaway PNGs from design iteration)
- .claude/scheduled_tasks.lock from autonomous-loop runs
- .playwright-mcp/ cache from the Playwright MCP server
Adds explicit ignores so these stay local. Intentionally archived
screenshots belong under docs/; the /*.png pattern only catches
root-level debris.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three asks shipped together:
1. Default flipped from AlmaLinux 10 → AlmaLinux 9. Alma 10 stays in
the picker but isn't pre-selected; 9 is the more battle-tested
choice for production dedicated workloads.
2. Single-open accordion: openFamilies (Set<string>) → openFamily
(string). Opening any family closes whichever was previously
open. Click the open family's header to fully collapse. Watch
on `props.selected` keeps the active selection's family open on
first paint and on programmatic selection changes (URL hydration).
Removed the "Expand all / Collapse all" toggle in the title row —
redundant under single-open semantics.
3. "Minimal" image variants added for every distro that publishes one
upstream: AlmaLinux / Rocky / Ubuntu / Debian / Fedora / openSUSE /
FreeBSD. New labels add a clear "Minimal" suffix; new slugs use
`-min` suffix (e.g. alma9-min, ubuntu24-min). Proxmox / Windows /
"No OS" deliberately have no minimal variant — Proxmox is a
single-flavor hypervisor, Windows is BYOL, "No OS" is a no-op.
Total OS count: 22 → 38 (across 9 families). Reseeded the OS group;
20/20 dedicated tests still pass; npm run build clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
OS list updated to reflect each project's actual current and supported
versions as of 2026-04 (verified per project release calendars):
Added:
- AlmaLinux 10 (RHEL 10 rebuild, default) — was missing
- Rocky Linux 10 — was missing
- Ubuntu 26.04 LTS (Resolute Raccoon, released Apr 2026) — was missing
- Debian 13 Trixie (current stable since Aug 2025) — was missing;
bumped from oldstable Debian 12
- openSUSE Leap 16.0 (Oct 2025) — replaces 15.6 (EOLs Apr 30 2026)
- FreeBSD 15.0 (Dec 2025) and 14.4 (Mar 2026) — was a generic "14"
- Proxmox VE 9.1 (Nov 2025) — added alongside 8.4 (security maint
through Aug 2026)
Removed:
- Debian 11 (LTS ends Aug 2026, dropped to avoid offering near-EOL)
- openSUSE Leap 15.6 (EOLs in 4 days)
Default OS flipped from AlmaLinux 9 → AlmaLinux 10 (current major).
Proxmox logo color: the Wikimedia CoreUI Proxmox SVG was monochrome
(inheriting black). Added fill="#E57000" so the brand orange is
correct.
22 OS options total across 9 distro families. metaFor() in
OsGroupSelector already handles all families via slug prefix —
no component changes needed for the new versions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Logo source swap:
Replaced all 9 OS brand SVGs with the actual icon-only files mirrored
on Wikimedia Commons (each project's official press kit). The
simple-icons rendering of AlmaLinux specifically was a generic
circles pattern — Wikimedia carries the real multicolor flame mark.
Same correction for Rocky (real green wedge logo), Fedora (proper F
infinity), Debian (bare swirl), Ubuntu (Circle of Friends in orange
hex), FreeBSD (horned daemon mark), Proxmox (CoreUI icon-only),
Windows (4-square 2021 mark), and openSUSE (chameleon button).
Hand-drawn no-os.svg stays — it's a generic terminal indicator,
no brand to source.
OS list expanded 14 → 17 (latest non-EOL versions only):
- Added: AlmaLinux 8, Rocky Linux 8, Ubuntu 22.04 LTS, Debian 11,
Fedora 43, Fedora 44, openSUSE Leap 15.6, Windows Server 2025,
Windows Server 2019.
- Removed: Fedora 41 (EOL'd Nov 2025).
- Default flipped from "No OS" to AlmaLinux 9 in the previous commit;
unchanged here.
OsGroupSelector metaFor() gains an openSUSE family rank between
Fedora and FreeBSD. Reseeded the OS group; 20/20 dedicated tests
still pass; npm run build clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two changes to the OS picker that should be felt together:
1. Swapped my hand-authored SVGs for official brand marks pulled from
simple-icons (CC0). These are the actual path geometries used in
each distro's brand kit, just colored to read on dark navy.
AlmaLinux uses their accent orange (#FA9001) instead of the brand
navy (#0E1F3D) so it's visible on our dark background.
Affected: ubuntu, debian, almalinux, rocky, fedora, freebsd,
proxmox. windows.svg and no-os.svg unchanged (geometric / generic).
2. Each distro family is now a collapsible accordion. The family
containing the current selection auto-expands on mount; everything
else collapses to a one-line row showing logo + family name +
option count + chevron. Header gets a primary-color "selected"
chip + tinted border when its family contains the active choice.
"Expand all" / "Collapse all" toggle in the title row for power
users; collapseAll() keeps the active selection's family open.
Net effect: the picker is ~1/4 of its previous height when only one
family is in use, and the official logos replace my approximations
(AlmaLinux flame mark is now correct, FreeBSD daemon is correct,
Proxmox four-square crown is correct, etc.).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four customer-copy / UX cleanups bundled together:
1. Drive bay title strip — shortGroupLabel() collapses "LFF/SFF/NVMe
Drive Bays" to just "Drive Bays" everywhere it surfaces (rail
anchor, configurator section title, BuildSummary line item).
Each chassis only ever shows one drive bay group, so the
form-factor prefix was redundant noise.
2. HDD/SSD optgroups in Drive Selection — VSelect now interleaves
VListSubheader rows ("HDDs", "SSDs", "NVMe") between options.
Sentinel header values (`__hdr_<cat>`) are filtered in
onDriveChange so a stray header click can't propagate.
3. OS list expansion — went from 6 entries to 14: added AlmaLinux 8,
Rocky 8, Ubuntu 22.04 LTS, Debian 11, Fedora Server 41, FreeBSD 14,
Proxmox VE 8, Windows Server 2019 (BYOL). Default flipped from
"No OS" → "AlmaLinux 9" (matching what most dedicated buyers
actually want — flag and revert via seeder if you'd rather keep
bare-metal as the default).
4. OS picker grouped by distro — OsGroupSelector renders family
sections (AlmaLinux, Rocky Linux, Ubuntu, Debian, Fedora,
FreeBSD, Proxmox VE, Windows Server, Other) with a small
uppercase heading above each row of tiles. metaFor() helper
maps slug → family + logo path. New SVG logos for fedora,
freebsd, proxmox; refined geometry on almalinux + rocky + debian.
Reseeded the OS group (deleted old 6 values, recreated 14 with new
ordering). 20/20 dedicated tests still pass. `npm run build` clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Switches the dedicated server detail page from 2-column (configurator
+ sticky summary) to 3-column (anchor rail | configurator | sticky
summary) — option D from the visual companion exploration. Power-user
shape: every section visible at once, rail tracks completion state,
sticky-on-both-sides keeps navigation + price always in reach.
- New ConfigSectionRail.vue: vertical list of section anchors
(CPU, RAM, OS, Storage, Network, etc.) with three states
(untouched, touched ✓, active ●). Click to smooth-scroll.
- Configurator wraps each group in <section :id="cfg-<slug>"> with
scroll-margin-top: 92px to clear the navbar on scroll-into-view.
- IntersectionObserver in DedicatedConfigurator/index.vue updates
store.activeAnchorId as sections cross the upper viewport.
rootMargin '-92px 0px -65% 0px' picks the section nearest the top.
- Store: activeAnchorId reactive ref, isGroupTouched() helper
(compares selection against seeded default; drive bay groups
also require quantity > 0 to count), groupAnchorId() and
shortGroupLabel() helpers.
- Detail-grid CSS: 180px | 1fr | 380px on desktop. Rail hides at
≤1280px (tablet keeps the summary). Full stack at ≤1024px.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces stacked radio cards with VSelect dropdowns across CPU, RAM,
Bandwidth, IPv4, Private Networking, PCIe NVMe Add-in, and the Drive
Selection control inside each drive bay group. Major space savings —
the LFF Drive Selection alone goes from 15 stacked cards to one row
on screen, with the active price still visible at a glance via the
selection slot's right-aligned chip.
OS group becomes a tile-grid picker (`OsGroupSelector.vue`): 6 cards
with brand logos, distro name, and price chip. Logos shipped as
hand-authored SVGs at public/img/os/{ubuntu,debian,almalinux,rocky,
windows,no-os}.svg — no new npm dependency.
- Synchronous Pinia store init: moved store.init() out of onMounted
into setup so children's `selected` props are populated on first
render. Without this VSelect's selection slot fires with a stub
item before init completes and the whole tree throws on a
defensive `.toFixed` access.
- Defensive priceLabel guards in both OptionGroupSelector and
DriveBayGroupSelector for any future re-render where the slot's
raw item is incomplete.
- isOperatingSystemGroup() helper alongside isDriveBayGroup() in the
store; configurator switches OS → OsGroupSelector, drive bays →
DriveBayGroupSelector, everything else → OptionGroupSelector.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The drive bay UX leaked two more pieces of internal detail customers
shouldn't care about:
- "(LFF carrier)" suffix on every LFF SSD label (it's just a tray
adapter so a 2.5" drive fits a 3.5" bay — internal mechanical
detail, irrelevant to the customer's storage choice).
- "Mixed-size setups via post-order ticket" tail on the LFF/SFF/NVMe
group descriptions, plus the entire "Need a custom drive layout?"
card at the bottom of the detail page. We're not actually offering
per-bay custom drive layouts as a service, so pitching it as a
workflow was misleading.
- "No drives — configure via ticket" → "No drives" on the default
value across all three drive bay groups.
Reseeded with `Drive Selection` value labels deleted+recreated since
the seeder keys on `[option_id, label]`. Internal value slugs
unchanged so share URLs still resolve.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The PCIe NVMe Add-in description and value labels exposed our adapter
SKUs (EC-PCIE / EC-P4BF), the vendor's product line names (Rocket 4
Plus / Rocket 5 Gen5), and PCIe lane / bifurcation jargon — none of
which the customer needs to see, and our standing rule is no vendor
names on public pages.
- Description rewritten in customer terms: positions the group as
"fast scratch space or 4-drive bundle for more capacity" instead of
explaining the adapter card mechanism.
- Value labels collapsed to "1× N TB M.2 NVMe (Gen4)" / "(Gen5)" /
"(Gen4 bundle)" — keeps the generation distinction (which matters
to customers) and the count, drops the vendor product names and
adapter SKUs.
Internal value slugs (`1x1tb-r4p-pcie`, `4x1tb-r4p-p4bf`) left intact
so any in-flight share URLs and the seeder's update path don't break;
they're not customer-visible UI text.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the flat-radio combo pattern in LFF/SFF/NVMe drive bay groups
with a Drive Selection (radio, per-drive cost) + Drive Quantity
(stepper) composite. Adds SAS HDD/SSD variants on LFF and SFF, and
collapses NVMe to enterprise U.2 sizes only.
- Seeder: rewrites the 3 drive bay groups; LFF goes from 35 flat combo
values to 15 per-drive selections, SFF 8 → 8, NVMe 7 → 4. Adds
SAS HDD (12/16 TB) and SAS SSD (1.92/3.84/7.68 TB) on LFF, SAS SSD
trio on SFF, and 7.68 TB SATA SSD on both.
- Store: selections become Record<string, string | {drive,quantity}>;
driveBayCost computed as drive_selection × quantity.
- DriveBayGroupSelector.vue: new composite component with stepper.
- BuildSummary: renders drive bay rows as "N× <drive> = $Y".
- Route filter: clamps Drive Quantity max_qty to chassis bay_count
instead of filtering value slugs.
- URL contract: drive bay groups serialize as <prefix>_drive +
<prefix>_qty (lff/sff/nvme).
- Tests: rewrites bay-count filter test, adds 5 new tests covering
the two-option structure, SAS variants on LFF/SFF, NVMe enterprise
sizes, and per-drive pricing alignment with the spec table.
Implements docs/superpowers/specs/2026-04-26-dedicated-drive-bays-option-b-design.md.
20/20 dedicated tests pass; 30/30 marketing tests green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Captures the full design for splitting LFF/SFF/NVMe drive bay
groups from flat radios into Drive Selection + Drive Quantity
composite controls. Includes the SAS variants user requested,
per-drive pricing table, schema decisions (no migrations needed,
existing schema already supports multi-option groups), Pinia
store changes, new DriveBayGroupSelector component sketch, URL
param contract changes, and migration steps.
Implementation deferred to a focused next session — realistic
4-5 hour build (backend seeder + frontend component + store
rework + test rewrite). Phase A (PCIe NVMe Add-in) shipped
ahead of this in c74ca7f.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extended the LFF Drive Bays group from 7 → 35 entries to surface
real high-density configurations customers actually order.
High-capacity HDDs (12 new entries, prices set at ~12-month payback
× 1.5x markup on Server Part Deals Seagate Exos pricing scraped
2026-04-26):
12 TB Enterprise HDD: ×2 \$90, ×4 \$180, ×8 \$360, ×12 \$540
20 TB Enterprise HDD: ×2 \$110, ×4 \$220, ×8 \$440, ×12 \$660
24 TB Enterprise HDD: ×2 \$150, ×4 \$300, ×8 \$600, ×12 \$900
LFF SSDs in 3.5" carriers (16 new entries) — 2.5" SATA/SAS SSDs
mounted via SaveMyServer adapters into LFF trays. Same per-drive
pricing model as the SFF group:
480 GB SATA SSD: ×2 \$20, ×4 \$40, ×8 \$80, ×12 \$120
1.92 TB SATA SSD: ×2 \$36, ×4 \$72, ×8 \$144, ×12 \$216
3.84 TB SATA SSD: ×2 \$90, ×4 \$180, ×8 \$360, ×12 \$540
7.68 TB SAS SSD: ×2 \$200, ×4 \$400, ×8 \$800, ×12 \$1,200
Existing chassis-bay-count filter at the route level keys on the
leading number in each value slug (e.g. "8x12tb-hdd" → 8 bays),
so combos that don't fit a chassis stay hidden — no extra logic
needed for the new entries.
Group description updated to reflect HDDs + SSDs both supported.
14/14 dedicated tests pass; pint clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bandwidth: dropped the "10 Gbps unmetered (fair-use)" tier — at our
\$295/mo it was a margin trap (Evocative bills 95th percentile at
\$0.48/Mbps; saturated 10G = \~\$4,560/mo cost). Replaced with a
"10 Gbps + 500 TB" tier at \$345/mo. 500 TB covers 99% of legitimate
heavy use; abuse customers self-select into a metered package or
get billed for overage.
New bandwidth ladder:
1 Gbps unmetered (baseline) \$0
10 Gbps + 10 TB \$45
10 Gbps + 50 TB \$95
10 Gbps + 100 TB \$175
10 Gbps + 500 TB (NEW, replaces ∞) \$345
Private Networking: new group, separate from public Bandwidth.
Customer can pick a private intra-rack link speed independent of
their public uplink. Traffic stays on our internal switch fabric
and isn't metered. Flat monthly per the brainstorm decision (no
setup fees).
1 Gbit private (included, default) \$0
10 Gbit private \$25
40 Gbit private \$75
Attached to all 8 14th-gen plans.
Updated test count: now 10 Dedicated 14th Gen config groups (was 9).
14/14 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Recalibrated against Hetzner DX153 configurator data ($78/64GB-DDR5
module monthly) and OVH SYS-5 baseline (~\$0.42/GB monthly, but on
cheaper Silver-tier chassis without iDRAC9 Enterprise / BOSS — not
a 1:1 comp for our Gold-tier line).
New tier pricing at \$65/64GB-equivalent — sits ~17% under Hetzner's
premium tier and ~50% above OVH's budget-floor on a $/GB basis. At
this rate, 1 TB upgrade recoups our $6,240 hardware cost (16x
\$390/stick DDR4-2400 LRDIMM at 2026 Q2 shortage prices) inside
6 months of customer rental, with ~30% ongoing margin.
Per-tier changes:
- 64 GB: +\$35 → +\$65 (+\$30/mo, ~85% bump)
- 128 GB: +\$90 → +\$195 (+\$105/mo, ~115% bump)
- 256 GB: +\$195 → +\$260 (+\$65/mo, ~33% bump)
- 512 GB: +\$380 → +\$520 (+\$140/mo, ~37% bump)
- 1 TB: +\$580 → +\$1,040 (+\$460/mo, ~80% bump)
- 1.5 TB: +\$780 → +\$1,560 (+\$780/mo, ~100% bump)
The biggest jumps are at the high-density LRDIMM tiers where DDR4
EOL shortage hits hardest. Original pricing (set during the
brainstorm before the shortage data was researched) would have
left us underwater on hardware-cost recovery.
14/14 dedicated tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Original tiers ($349/$549/$799) drifted into Hetzner Dell US
setup-fee territory ($840-$1,020) — too aggressive for a small
Atlanta provider competing against ColoCrossing ($0 setup) and
OVH SYS ($60-$343). The competitor research at
infrastructure/docs/competitors-atlanta-2026.md and
dedicated-server-pricing-2026q2.md both flagged "no setup fees"
as a documented competitive advantage.
New tiers ($149/$249/$399) preserve a meaningful safety net on
monthly customers (~1 month of rental recoups the fee) while
sitting inside the OVH SYS price band. Annual / Semi-Annual
customers still pay $0 setup.
Per-plan changes:
- R440 / R640 SFF (Tier 2): $349 → $149
- R540 / R740 / R740xd SFF / R740xd LFF (Tier 3): $549 → $249
- R640 NVMe / R740xd NVMe (Tier 4): $799 → $399
Spec doc updated. Test expectations adjusted; 12/12 pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three changes bundled:
1. Drive bay configurator (3 new ConfigOptionSeeder groups):
- LFF Drive Bays (3.5") — 7 starter combos from "None" to
12× 8 TB SATA HDD; attached to R440 / R540 / R740xd LFF
- SFF Drive Bays (2.5") — 8 starter combos from "None" to
24× 1.92 TB SATA SSD; attached to R640 / R740 / R740xd SFF
- NVMe Drive Bays (U.2) — 7 starter combos from "None" to
16× 2 TB U.2 NVMe; attached to R640 NVMe / R740xd NVMe
Combos enforce chassis bay-count constraints via labels
("R740xd LFF only"); customers wanting heterogeneous setups
use the post-order ticket flow.
2. Sticky build-summary fix: previously the BuildSummary card
slid under the Vuetify navbar at scroll. Moved sticky from
the inner card to the .detail-grid__summary wrapper, removed
align-items: start so the right grid cell stretches to the
configurator column's height (giving sticky a tall enough
container), and offset top by 80px (64px navbar + 16px
breathing room). Mobile path drops sticky entirely.
3. Setup fee reword — "Hardware acquisition" was leaking our
cost structure and making the fee feel like procurement
passthrough. Now reads "Server provisioning & deployment"
in BuildSummary, and the FAQ describes what the fee covers
(build, racking, iDRAC config, deployment) without exposing
margins. Same shift across the non-refundable note: "once
your build starts" instead of "once hardware is purchased."
Detail page bay-strategy callout updated: drive selection IS now
self-serve, so the callout pivots to "need a custom drive layout?"
pointing customers with mixed-size / hot-spare / RAID-preference
needs to the contact form.
Tests: updated count assertion to 9 groups, added a new test
verifying drive bay groups attach to chassis by bay type.
22/22 of my session's Pest tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CPU upgrade ladder (standard, R440/R540/R640/R740) now 6 tiers:
- Gold 6230 (baseline, included)
- Gold 6244 high-clock (16C / 3.6 GHz, +$25/mo) — for per-core
licensed workloads (MS SQL / Oracle) and single-threaded compute
- Gold 6248 (40C / 2.5 GHz, +$35/mo)
- Gold 6230R sweet-spot (52C / 2.1 GHz, +$50/mo) — Cascade Lake
Refresh of the 6230, more cores at same clock, bridges baseline
and 6248R
- Gold 6248R (48C / 3.0 GHz, +$75/mo)
- Gold 6258R (56C / 2.7 GHz, +$145/mo)
R740xd CPU ladder unchanged (6230 / 6248R / 6258R / Platinum 8280).
IPv4 block options extended to /24:
- /29 ($12) · /28 ($36) · /27 ($80) · /26 ($145) · /25 ($275) ·
/24 ($499). All blocks above /29 require ARIN justification —
the group description explains the policy and each tier's label
carries a "justification required" tag.
Build summary sidebar replaces the bottom sticky footer on the
per-chassis page. New 2-column layout (configurator left, summary
right, sticky); collapses to single-column on tablet/mobile with
the summary stacked above the configurator so total stays visible.
The summary fixes the original "Total $468 billed monthly /
includes $349 setup" wording confusion by splitting into clearly
labeled sections:
- RECURRING: per-line itemized breakdown (baseline + each upgrade
with its actual cycle-priced cost), subtotal in /mo or /yr suffix
- ONE-TIME: setup fee with non-refundable note (or strikethrough +
"waived" badge when cycle is semi/annual)
- TOTAL: "First invoice $X" + "Then $Y/mo recurring" framing on
monthly/quarterly cycles; "Total due today" + renewal preview on
semi/annual
Removed: ConfiguratorFooter.vue (replaced by BuildSummary).
Pinned to top via position:sticky with viewport-height clamp +
internal scroll for tall configs. Order CTA + Copy share link
moved into the summary card.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Vendor sourcing is an internal procurement detail. Customer-facing
copy now reads "we source and assemble the chassis at our Atlanta
datacenter" — accurate and protects the supplier relationship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
11 tests covering:
- Landing page returns 16 plans (8 14th-gen + 8 legacy)
- All 8 14th-gen chassis have correct setup_fee per tier mapping
(R440 $349, R540 $549, R640 $349, R740 $549, R740xd $549,
R740xd LFF $549, R640 NVMe $799, R740xd NVMe $799)
- Legacy 12th/13th-gen plans are active with $0 setup fee
- Per-chassis detail page renders for every 14th-gen slug
- 404 for invalid slugs
- R740xd variants get the R740xd-specific CPU group; non-xd
chassis get the standard CPU group
- All 6 dedicated 14th-gen config groups exist after seeding
- RAM upgrade group standardizes on DDR4-2400 (per Q5 brainstorm)
- Checkout setupFee prop exposed correctly on dedicated plans
- Checkout setupFee is 0 on VPS plans
All 22 of my session's Pest tests pass (11 dedicated + 11 VPS
estimator). Pre-existing project test failures (DOMAIN_* mismatch
between hardcoded test URLs and docker dev env) are unrelated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New pages:
- /dedicated-servers (rewrite): chassis grid with generation filter
(All / Build to Order / In Stock — Rack inventory), hardware band,
ColoCrossing-vs-EZSCALE comparison table, 8-question FAQ, custom-
build CTA. Uses ChassisCard + GenerationFilter components.
- /dedicated-servers/{slug}: per-chassis page with locked baseline
spec sidebar, configurator section (4-cycle toggle, 6 option
groups via OptionGroupSelector, sticky ConfiguratorFooter with
setup-fee waiver display), bay-strategy reminder card.
New shared components:
- Components/Marketing/Dedicated/
- ChassisCard.vue — uniform card for both build-to-order and
in-stock variants; differentiates with badge + border tone
- GenerationFilter.vue — 3-option chip toggle with counts
- BuildStatusPanel.vue — 5-stage timeline (Ordered → Hardware
acquired → Assembly → Racked → Deployed) with editable prop
for admin use; reused on customer service detail page
- DedicatedConfigurator/
- index.vue — orchestrator, mounts store, debounces URL push
- CycleToggle.vue — 4 cycles (Monthly/Quarterly/Semi/Annual)
with setup-waived badges on 6+ month tiers
- OptionGroupSelector.vue — generic radio for any config group
- ConfiguratorFooter.vue — sticky total + share link + order CTA
Pinia store:
- stores/dedicatedConfigurator.ts: per-chassis state (selections
keyed by group name, cycle), getters for all sub-totals, setup-
fee waiver logic, hydrateFromUrl + shareUrl + checkoutUrl. URL
param shape: ?cycle=&cpu=&ram=&os=&bw=&ipv4= (only non-default
values serialized).
Comparison rows are sourced from the freshly-landed competitor
research at infrastructure/docs/json/competitors-2026q2.json
and ovh-2026q2.json — focuses on hardware transparency, iDRAC9
inclusion, BOSS boot, setup-fee policy, and engineer-first support.
Drive picker descoped to v1.1 (per design spec): the configurator
captures CPU/RAM/OS/Bandwidth/IPv4 self-serve; drive selection is
handled via post-order ticket in v1. Bay-strategy callout on the
detail page sets that expectation.
npm run build clean; DedicatedServers + DedicatedServerDetail
bundles are 14.7 / 16.3 kB (gzipped 5.8 each). Visually verified
in the docker dev stack.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Procurement decision: simpler single-DIMM-SKU sourcing across all
RAM tiers. The Gold 6230 IMC supports up to DDR4-2666, but
standardizing on 2400 keeps RDIMM ↔ LRDIMM transitions on the same
speed and means we don't need two separate procurement streams.
The ~10-18% memory bandwidth penalty vs 2666 is invisible for
typical hosting workloads (general compute, web/db, virtualization).
Memory-bandwidth-bound workloads (in-memory caches, ML inference)
can request a custom 2666 build via the contact form.
Updates:
- All 8 14th-gen plan rows: features.ram now reads
"32 GB DDR4-2400 ECC RDIMM" (was 2666).
- Dedicated 14th Gen — RAM Upgrade group: each value now carries
its actual DIMM type — RDIMM up through 128 GB, LRDIMM at
256 GB and above. All speeds DDR4-2400.
Note: this diverges from infrastructure/docs/dedicated-server-
configurations.md which still lists 2666 (matches what
SaveMyServer's configurator ships). Reconcile upstream when
procurement is finalized.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Migrations:
- add_setup_fee_to_plans_table: new decimal(10,2) default 0
- create_service_build_milestones_table: id, subscription_id,
stage, reached_at, note, updated_by, timestamps; unique
(subscription_id, stage); indexed (subscription_id, reached_at)
PlanSeeder:
- Removes the "archive 12th/13th-gen → hidden" block; flips all 8
legacy slugs (dell-r330-lff..dell-r730-lff) to status=active so
they surface as in-stock rack inventory alongside the new line.
- Replaces the 5-row 14th-gen placeholder with 8 proper SKUs:
r440-4lff ($119, $349 setup), r540-8lff ($159, $549),
r640-8sff ($179, $349), r740-16sff ($229, $549),
r740xd-24sff ($279, $549), r740xd-12lff ($249, $549),
r640-10nvme ($239, $799), r740xd-24nvme ($279, $799).
Setup fees per the brainstorm tier mapping.
- Each new SKU carries full features (chassis, form_factor,
bay_count, bay_type, locked baseline cpu/ram/boot/idrac/
network/bandwidth, lead_time_days '7-10', tier).
- All 8 new SKUs have per-cycle prices (M/Q/Semi/A) at 5/10/15%
discounts.
ServiceBuildMilestone model with STAGES const (ordered →
hardware_acquired → assembly → racked → deployed) and
subscription/updatedBy relations. Queries from this side until
we extend Cashier's Subscription model in a later phase.
Spec: docs/superpowers/specs/2026-04-26-dedicated-server-lineup-design.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Captures the brainstorm: 8 new Dell 14th-gen chassis SKUs alongside
existing rack inventory, per-chassis configurator at
/dedicated-servers/{slug}, 5-stage post-order build tracker,
tiered setup fees waived at Semi-Annual+ cycles.
User approved design + skipped spec-review gate. Implementation
phasing in 4 commits per the spec.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a "Built on" hardware band above the plan grid and a small
meta line on each plan card so shoppers can see what's actually
running their VPS.
Hardware band lists: Dell PowerEdge hypervisors · Intel Xeon · ECC
memory · Enterprise SSD storage · Atlanta, GA datacenter.
Per-card meta line: "Intel Xeon · ECC memory" in muted small type
under the regular spec list.
Copy is intentionally generic ("Intel Xeon", not Broadwell-EP /
Cascade Lake) to remain accurate across the current mixed pool
(R720xd Ivy Bridge being retired into the new R740xd ATL-04 next
week per infrastructure/docs/hypervisor-consolidation-r740xd-build.md).
Tighten the wording once consolidation lands.
Dropped the originally-proposed "10 Gbps inter-host" row — it's
internal cluster networking (used for live migration + shared
storage), and customers were likely to confuse it with their own
egress bandwidth.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The differentiator card and comparison row were positioning EZSCALE
as "no chatbots" — but we DO run Ezra, an AI assistant that
extends the support team. Reframed honestly: engineer-first support
where Ezra helps the team triage and respond faster, not the
other way around. That's still differentiated against tier-1
outsourced or AI-only support models the big clouds run.
Also drops in docs/integrations/savemyserver/dedicated-server-catalog.json
— 27 Dell 14th-gen products from SaveMyServer with EZSCALE baseline
config and pricing, generated by a separate scraping session at
/tmp/sms-configurator. Lives next to other vendor integration data
in docs/integrations/. Will feed the dedicated-server PlanSeeder
refresh in a follow-up task.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The old "Why Choose EZSCALE VPS?" grid was duplicating the
Included With All Plans card right below it (RAID 10, ZFS Snapshots,
VirtFusion Panel, Full Root Access appeared in both). Replaced with
6 actual differentiators that answer "why us, not Linode/Hetzner/
Vultr": real US humans, ZFS-backed reliability, predictable billing,
optional Pilot tier, 14-day money-back, owner-operated.
Other improvements bundled in this pass:
- Hero subtitle rewritten from feature dump to identity-first one-liner.
- Plans table → responsive PlanCard grid with a "Most popular" badge
on VPS-4 and a separate "Storage-focused" subgrid for stor-500/1tb.
Each card carries Order Now + Estimate-this-plan link to the
estimator (still wired to the prefillEstimator handler).
- Added a 5-row "EZSCALE vs typical big-cloud" comparison table —
honest differences only, no specific competitor names to avoid
liability.
- Added an 8-question FAQ section (provisioning time, OS list,
upgrades, refunds, DDoS roadmap, rDNS, location, migrations) using
a new reusable Faq accordion component.
- Bottom CTA copy fixed (it was still mentioning DDoS protection as
current; it's coming-soon upstream): now reads "Real humans on
support. 14-day money-back if it's not the right fit." Heading
flipped to "Ready to spin one up?" / "Talk to a Human."
New shared components: Components/Marketing/PlanCard.vue,
Components/Marketing/Faq.vue, Components/Marketing/ComparisonTable.vue.
All three are reusable for the dedicated/web/game pages later.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Windows BYOL is free and doesn't change the price, so the toggle
was UI clutter. The Included With All Plans card already
communicates the BYOL requirement (line updated to read
"Linux included; Windows supported (bring your own license)" for
clarity), and OS template selection at checkout still lets users
pick a Windows image when they're ready to deploy.
- AddOnsPanel.vue: removed the Windows License switch
- EstimatorSection.vue: removed onWindowsChange handler
- stores/estimator.ts: removed windowsLicense state, dropped from
share/checkout URL params and hydration
- ConfigOptionSeeder.php: dropped the legacy 'Windows License'
option from the VPS Add-ons group (deletes existing row)
- CheckoutController.php: removed ?windows=1 query-param handling
from buildPrefilledSelections
- VpsHosting.vue: tightened the BYOL line in the Included card
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaced --v-theme-surface-bright (auto-generated lighter tone that
clashed with the page's dark navy palette) with --v-theme-surface
across all 10 estimator components. Removed gradient backgrounds in
favor of the existing .glass-card / .section-alt-bg patterns from
resources/styles/_marketing.scss. Bumped contrast on the cycle toggle
discount badges and the EstimatorFooter card so the Copy share link
button reads clearly.
Components touched: EstimatorSection, BillingCycleToggle, WorkloadPicker,
MiniQuizDialog, RecommendedPlanCard, AddOnsPanel, IPv4Stepper,
ManagedSupportSelector, BackupTierSelector, EstimatorFooter.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements the design captured in
docs/superpowers/specs/2026-04-26-vps-hosting-estimator-design.md.
Estimator (after the hero on /vps-hosting):
- Workload picker (9 chips) recommends a plan; "Not sure" opens a
mini-quiz with a 12-app catalog and a traffic+priority follow-up.
- Recommended-plan card with "or pick another" alternates dropdown.
- Add-ons panel: IPv4 stepper (1-8, $8/extra), Windows BYOL toggle,
4-tier Managed Support radio (Self/Basic/Pro/Pilot @ $0/29/79/99),
5-tier Off-site Backup radio (None/Lite/Standard/Extended/Vault @
$0/5/12/25/59).
- Pilot tier gated to VPS-8+ via plan.features.tier; auto-fallback to
Pro on plan downgrade with snackbar warning.
- Billing cycle toggle (Monthly / Quarterly / Annual) reuses
per-cycle prices already on plan_prices and plan_config_values.
- Sticky footer with live total, "Order this configuration"
(deep-links to /checkout/{plan} with all params), and "Copy share
link" (history.replaceState debounced 300ms).
- Plans-table rows get an "Estimate →" link that pre-fills the
estimator with that plan and scrolls up.
Backend:
- PlanSeeder: each VPS plan gets features.tier (1-32) for gating.
- ConfigOptionSeeder: scope existing Server Management group to
dedicated only; add VPS Managed Support and Off-site Backup
groups with full per-cycle prices.
- routes/marketing.php /vps-hosting: pass addOns + workloadMap +
appExamples Inertia props.
- CheckoutController::show: build prefilledSelections from
?ipv4&windows&managed&backup query params; Vue page hydrates
configSelections from this prop.
Included With All Plans: rewritten to 13 accurate items with
per-line wording (10 Gbps fair-use uplink, ZFS snapshots free,
KVM virtualization, rDNS/PTR control, OOB console/VNC, 99.9%
SLA, etc.) plus a "Coming soon" badge for DDoS protection.
Tests: 10 Pest feature tests in tests/Feature/Marketing/
VpsHostingEstimatorTest.php cover the page props, both new
seeded groups, plan-tier metadata, Server Management dedicated
scope, configGroups attachment, and checkout query-param
pre-fill round-trip. All 10 pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Design captured from brainstorming: workload-driven estimator with
plan + add-on configurator (managed support + off-site backup tiers),
URL state, share link, checkout pre-fill, and rewritten Included list.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Major additions:
- Knowledge base with categories, articles, revisions, and voting
- Enhanced ticket system: departments, SLA policies, canned responses, tags, custom fields, satisfaction ratings, internal notes
- Multi-currency support with exchange rate sync
- Shopping cart and quote system with PDF generation
- Affiliate program with referrals, commissions, and payouts
- Account credits, credit notes, and debit notes
- Staff management with granular role-based permissions
- Fraud detection and order risk assessment
- ServerHunter SEO integration
- Service lifecycle events (suspend/unsuspend/terminate)
- Service management panels for VPS, Dedicated, Hosting, and Game servers
- Plan lifecycle fields and per-customer overrides
- 30+ migrations, 17 factories, 8 feature test suites
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
README: reflect completed phases (1-5, 8-9), accurate tech stack
(Vuetify 3 not Tailwind, Passport not Sanctum, standalone tickets
not SupportPal), current codebase counts (59 migrations, 29 models,
85 pages, ~497 tests), and separate implemented vs planned features.
CLAUDE.md: reduce from 281 to 184 lines — trim derivable directory
listings, remove stale counts, cut redundant examples, update all
metrics to current state.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Includes all work from phases 6-9+ and frontend polish rounds 1 & 2:
- Login history with device trust, new device notifications, session management
- Churn prevention: cancellation surveys, winback campaigns with email sequences
- Financial reports: revenue, P&L, tax, aging, refund, subscription reports with PDF/CSV/JSON export
- Configurable checkout: plan config groups/options, build-your-own VPS
- Frontend polish: fix broken legal links, add SEO meta tags, favicon, font display=swap,
Head titles on all 14 marketing pages, mobile responsive fixes, AuthLayout legal footer,
remove false 24/7 claims, hide empty stats, correct uptime SLA to 99.9%,
GameServers notify buttons linked to /contact, 301 redirects for /terms and /privacy
- WHMCS migration scripts
- Update legal page effective dates to March 16, 2026
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Eager-load plan prices in the pricing route and add a billing cycle
toggle (monthly/quarterly/semi-annual/annual) with discount badges and
per-month equivalent display for longer cycles. CTA links now pass
the selected cycle as a query param.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>