← /code
OverviewFlowDecisionsHistory
An internal handbook·2026.05

How CubeRoot is built

A cube-tools site with 24+ tool pages — one Next.js 16 codebase deployed two ways (self-hosted VM + Vercel edge, split-horizon DNS). Backend is Hono + PostgreSQL, and the WCA statistics pipeline runs separately on a weekly cadence. This page walks every layer — from a mouse click to a DOM update, and everything in between.

5
packages
14
modules
80+
WCA stat pages
41
alg sets
1
cloud VM
01

The whole system in one picture

One line: everything sits on one self-hosted VM — nginx reverse-proxies systemd Next standalone (:3002) + Hono API (:3001); Hono talks to PG over a local socket. The other line hits Vercel edge running the same Next code; the API call still resolves to api.cuberoot.me. static.cuberoot.me is a dedicated subdomain serving /tools and /stats for the Vercel function fallback.

WCA Public Dump每周 · .sql + .tsvGitHub ActionsCI · 周更 · deployVercel edge同份 Next 代码push 部署 · edge cacheUser BrowserChrome / Safari / Edgecuberoot.meCloud VMone box, three servicesnginx :443Hono API :3001PostgreSQL 13 :5432GH Pagesfallback mirrorcuberoot.me (CNAME)pull weeklyscp · sshHTTPSsplit DNSapi · static301
01
Edge
TLS termination + static + reverse proxy
nginx · CloudFlare DNS · Let's Encrypt
02
Frontend
Next.js App Router, 24+ tool pages, /[lang] path-prefix locale
React 19 · Next.js 16 (Turbopack) · TypeScript · cubing.js · Tailwind 4 (base layer)
03
API
Small, light Hono, run under pm2
Hono · Node 22 · pm2
04
Storage
recon · alg library · training data · WCA stats derivatives
PostgreSQL 13 · pg_dump nightly
02

Monorepo: five packages

core/ is a pnpm + Turbo monorepo with five packages, each owning one slice. CI only re-runs packages that actually changed — cache hits make most builds sub-second.

sharedtypes onlyclient-nextReact 19 + Next.js 16serverHono + PGstats-buildCLI · 独立client / server 都依赖 shared (纯类型) · stats-build 独立 CLI
@cuberoot/client-next~140k LOC
Next.js 16 App Router — the whole frontend
  • app/[lang]/* — one folder per tool, /[lang] path-prefix locale
  • components/ — shared widgets
  • lib/ — helpers (apiUrl / flag / format_result)
  • 11 Zustand stores (auth / settings / sessions / etc.)
@cuberoot/client~120k LOC
Retired SPA — local localhost:5173 only
  • Phase 4 (2026-05-27) cut over to client-next
  • No new features land here
  • Kept locally for dev comparison / rollback source
@cuberoot/server~8k LOC
Hono API + PG access
  • WCA OAuth + sessions
  • recon / alg / training-data CRUD
  • CORS allowlist
@cuberoot/shared~1k LOC
Shared types
  • Pure TS types, zero runtime
  • Must not import client utils
  • One source for both ends
@cuberoot/stats-build~5k LOC
WCA stats standalone pipeline
  • 80+ SQL-driven stats
  • Weekly CI, ~2h end to end
  • TS rewrite of jonatanklosko/wca_statistics
03

14 modules, three flavors

Not every route was built from scratch. own = designed and built here; port = someone else's React/HTML rewritten in-repo; fork = upstream assets hosted as-is. Click a card to visit the module.

own · 8port · 3fork · 3
/reconown
Recon
Result review + same-round autofill
/trainerown
Trainer
41 algorithm sets with timing
/frame-countown
Frame Count
WebCodecs + mp4box.js
/wca/vizown
Distribution
Result distribution viz
/wca/calendarown
Calendar
Global comp calendar
/scramble-statsown
Scramble
Scramble difficulty
/wcaown
WCA Stats
80+ pages, weekly
/recognize/pllown
Recognize
Image-to-letter drill
/calcport
HTH Calc
port: carykh/hthgrapher
/battleport
Battle
port: MatteoColombo
/mosaicport
Mosaic
port: Roman-/mosaic
/cstimerfork
csTimer
fork: cs0x7f/cstimer
/solverfork
Solver
fork: or18/RubiksSolverDemo
/alg-trainersfork
Alg Trainers
fork: mihlefeld/Alg-Trainers
04

Deploy: six hosts, split-horizon DNS

HostBacked byRole
cuberoot.me
www.cuberoot.me
Split-horizon DNS — one line → self-hosted VM nginx → systemd Next standalone :3002; the other → Vercel Hobby edge (same code, push-to-deploy)Primary site, Next.js 16 App Router
api.cuberoot.meCloud VM nginx → :3001Hono API + 24h proxy_cache
next.cuberoot.meSame systemd cuberoot-next :3002 (alias)Staging alias / direct to self-hosted Next, bypassing DNS routing
static.cuberoot.meSame nginx, dedicated vhost serving only /tools/ + /stats/ (CORS:*)Static-asset origin for Vercel function fallback
cuberoot.me/blog/
blog.cuberoot.me
Dual via split-horizon DNS: same-VM nginx alias / GH PagesWordPress static archive (frozen 2026-05)
Source·github.com/RuiminYan/cuberoot.me·/code

This is CubeRoot's internal handbook. Read it before touching any module; read it again after.