Project Now Korea
Warning
This post is more than a year old. Information may be outdated.
1.High‑level architecture
- Vercel hosts the static Next.js UI, Edge Functions, and scheduled (cron) jobs.
- Upstash Redis is the hot data‑plane:
- single‑bit/byte writes (
SETBIT/SETRANGE) keep the canvas in memory with O(1) latency(upstash.com) - native Pub/Sub plus Server‑Sent Events (SSE) push updates globally(upstash.com)
- atomic Lua scripts and the Upstash Rate‑Limit helper enforce "1 pixel /Nsec" per user.
- single‑bit/byte writes (
- Neon Postgres is the durable system‑of‑record. The edge‑compatible serverless driver lets Edge Functions upsert rows over HTTPS/WebSockets with 10ms cold‑start overhead(neon.com). Keeping history here enables time‑lapse, analytics, and roll‑back with Neon's cheap "branching" snapshots(neon.com).
2.Data layout in Upstash Redis
Atomic write + broadcast script
SETRANGE modifies ≤1byte so requests stay <1MB(upstash.com) and are atomic at the Redis level.
3.Edge Function: POST/api/place
- Auth - header cookie/JWT or fallback to IP.
- Rate‑limit - Upstash helper (
@upstash/ratelimit) → 429 if user has written in the last _N_seconds. - Tile calculation - map
x,ytotileKeyand byte offset. - Lua script EVALSHA - atomic write + publish.
- Async durability -
await sqlvia@neondatabase/serverlessto insert intopixel_events(id, ts, x, y, color, user_id); errors are logged but don't block the user path.
Edge Functions are short‑lived; they fit Vercel's normal limits (max 30s run, 25MB bundle(vercel.com)).
4.Real‑time fan‑out (/api/updates)
Vercel doesn't keep raw WebSockets alive end‑to‑end(vercel.com), but long‑running SSE streams are supported because they're just HTTP responses.
The route:
The browser's EventSource patches the <canvas> in ~50ms worldwide (Upstash's multi‑region POPs).
5.Cold‑start & initial page load
- Path1 - Static snapshot -- hourly Vercel Cron downloads all tiles, flattens to a PNG, uploads to Vercel Blob Storage or the Next.js public folder (served by Vercel CDN). First paint shows a compressed image.
- Path2 - Progressive tiles -- client lazily
GET /api/tile?x=3&y=7(Edge Function readsGETRANGE4096B and responds with Uint8Array).
6.Background jobs (Vercel Cron)
Cron functions are entirely serverless; zero cost when idle.
7.Scaling & limits
Cost example (1keps, 2M MAU):
8.Security & abuse protection
- HTTPS only; Vercel auto‑TLS.
- Origin shielding - strict CORS and
Referrer‑Policy. - Rate‑limit at API layer + Cloudflare Turnstile to deter scripts.
- Auditable history - every pixel stored immutably in Postgres, write‑blocked by RLS so only Edge Functions can insert.
9.Extensibility
- Multiple boards - prefix keys
board:<id>:<tileX>:<tileY>; Postgres foreign‑keyboard_id. - Undo / disputes - keep last colour in Lua script return; moderators can "rewind" by replaying events.
- Time‑lapse video - Neon Branch → generate frames in a Vercel Job, push to Cloudflare Stream.
- Gamification - Postgres materialized view for streaks, leaderboards.
10.Why this stays "serverless"
- Scale‑to‑zero -- no container or DB CPU when idle (Neon & Upstash pause).
- Global latency -- Vercel Edge + Upstash POPs replicate closer to users; no region pinning.
- Ops burden -- backups, fail‑over, TLS, and upgrades are delegated to the three SaaS providers.