← All posts

Building a Community Marketplace for Terminal Themes (on Cloudflare Pages)

The architecture behind moltamp.com: Astro + Cloudflare Pages + D1 + R2, and what I learned shipping a real marketplace.

moltamp.com is the community hub for MOLTamp skins, widgets, and visualizers. It's built on Astro + Cloudflare Pages + D1 (SQLite at the edge) and went from zero to production in about two weeks. Here's the architecture and what I learned.

Why Cloudflare Pages

I evaluated three platforms: Vercel, Netlify, and Cloudflare Pages. Cloudflare won for two reasons:

  1. D1 is SQLite at the edge — no external database to manage, no connection pooling headaches, no cold starts. For a community marketplace that needs fast reads and occasional writes, it's perfect.
  2. R2 is S3-compatible storage — skin zip files, user avatars, and screenshots live in R2 with zero egress fees. That matters when users are downloading 5MB skin zips.

The trade-off: Cloudflare's developer experience is rougher than Vercel's. Wrangler has quirks. D1 migrations need manual tracking. But the infrastructure cost at scale is dramatically lower.

The Stack

Astro was the right choice for a content-heavy site with islands of interactivity. Most pages are static HTML. The skin detail page is a React island () because it needs interactive ratings, comments, and screenshot carousels.

The Upload Flow

This is the most interesting part architecturally. When someone uploads a skin:

  1. The client sends with the zip file
  2. The server extracts the zip in memory using fflate, validates the manifest, validates the CSS (including the six-family effect scanner and dead-toggle detector), validates assets, and returns the validation result with the full text
  3. The client renders a live preview — the theme.css is injected into a sandboxed iframe containing a canonical mock shell, so the author sees exactly what downloaders will see
  4. If the author clicks "Publish," the client re-sends the same zip without , and the server writes to D1

The dryRun pattern is key: it lets us show a rich preview and surface all validator warnings before committing anything to the database. Cancel is free — just throw away the client state.

Lessons Learned

D1 migrations need discipline. D1's migration tracker () works, but only if you maintain the tracking table meticulously. We had a period where migrations drifted out of sync and had to be manually re-tracked. Now we apply each migration via and track separately until Cloudflare improves the tooling.

Astro's scoped styles are a trap for JS-rendered content. If you inject HTML via JavaScript (, card renderers, etc.), Astro's scoped tags won't match because the injected elements lack attributes. Use for anything rendered by JS. We got bitten by this multiple times.

Cache-bust your public JS. Files in are served with CDN caching. After changing a JS file, bump the version query parameter in the script tag ( → ). Forgetting this means users see stale code until the cache expires.

The live preview is the best marketing. The upload preview interstitial — where authors see their skin rendering live before publishing — turned out to be the feature that makes people say "oh, this is real." It's proof that the CSS actually works, not just a promise.

What's Next

We're working on gating community content behind the 0 license — so the free tier gets bundled skins and a curated selection, while licensed users get the full community library. The validator and preview system were the foundation; the marketplace economics are the next layer.

If you're building a community marketplace on Cloudflare, start with the upload flow. Get that right and everything else follows.