← Home

2026 · work

MetaForest LauncherElectron · Windows / macOS

Built a Windows and macOS desktop launcher from scratch and moved distribution to a CDN

Role
Design, implementation, deployment
Period
2026.06 – 2026.08
Status
In production
Stack
Electron, TypeScript, S3 · CloudFront, GitHub Actions, NSIS

Context

MetaForest is a metaverse psychological counseling platform accessed through a Unity client. The old launcher was WPF (Windows only), and counselor/client features were missing from it, so users bounced between the web and the app.

What I built

  • A launcher for Windows and macOS. Login (ID, Kakao, Naver, Google, Apple), role-based feature tabs (chat, session history, room booking, client management, AI session records), home news and notices.
  • Desktop notifications, tray residency, five languages. The launcher session is injected into the game client so there is no second login.
  • Resumable downloads, Windows CI builds (GitHub Actions), an auto-update feed.

Distribution migration (2026.08)

  1. Set up an S3 bucket with a CloudFront distribution on a cdn subdomain.
  2. Switched launcher downloads to the CDN, splitting files six ways for parallel download and flat install.
  3. Built a user guide program and switched the website’s download buttons to fixed CDN names.
  4. Added removal of the old WPF launcher (MSI) to the launcher itself, so users migrate without touching anything.

Troubleshooting: updates that “sometimes” failed

Symptom: a new version existed, but the launcher could not find it or downloaded stale files. Server and CDN caches were the first suspects, but the actual candidate was a missing HTTP cache invalidation on Electron’s net.fetch. After v1.0.4 shipped cache-busting on the feed request, the failure stopped reproducing. Four related PRs were merged, deployed and verified along the way.

Lessons

  • Code signing: EV certificates give no SmartScreen advantage (per Microsoft), and a Korean company can use Azure Artifact Signing, which electron-builder already supports.
  • Object-storage upload trap: aws-cli v2’s default checksum caused AccessDenied only on PutObject; when_required fixed it.