Spells out the one-time secret generation that must NEVER be re-run.
Documents local k3d setup and operations runbooks.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two IngressRoutes (web → http-to-https redirect, websecure → app)
covering all configured hosts. Certificate covers all hosts as SANs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AOF persistence + LRU eviction + optional password. PVC for the
queue data so Horizon doesn't lose pending jobs on pod restart.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Renders only when mariadb.enabled=true. Generates a random root
password Secret with helm.sh/resource-policy=keep so uninstall
doesn't orphan the data volume.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When mariadb.enabled=true, references the in-cluster MariaDB this
chart deploys. When false, references an external CR via
mariadb.externalRef. Privileges scoped to the website's database
only — no global ALL PRIVILEGES.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Helm hook runs migrate (and optionally seed) before any pod rolls.
If the Job fails, helm upgrade aborts and the previous ReplicaSet
keeps serving traffic.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two-container pod sharing source via emptyDir populated by init
container. Nginx vhost in a separate ConfigMap. OAuth keys mounted
from the chart Secret as files under /var/www/html/secrets/, copied
into storage/ by the prod entrypoint.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>