Skip to study
EW Studio

EWSTUDIOS / the platform

Five different AI products. One $7 server.

The shared work is deciding what the browser is allowed to ask for.

Heroku Basic only. Cloudflare Workers, Supabase and Gemini use free tiers. Five apps share the API.

Emmanuel Wilson's portfolio character
Role
Product, design direction and engineering lead, built with Claude Code
Timeline
Shared platform · October 2026
Stack
Hono · TypeScript · Heroku · Supabase · Gemini
$7 / month

Heroku Basic only. Cloudflare Workers, Supabase and Gemini use free tiers. Five apps share the API.

The five featured products

Shared infrastructure.
Independent experiences.

Three engineering decisions

The constraint shapes
the implementation.

Keep the trust boundary on the server

Constraint. A shared model key must not become a general chatbot

Decision. clients send typed, capped data to server-owned templates, with validation, identity and quota guards

Result. the quota increment and return happen in one atomic SQL statement, avoiding parallel calls slipping through. The region gate uses the last X-Forwarded-For entry because earlier entries can be forged.

Source and evidence limits

ew-apps-api/README.md, “Decisions worth explaining” → “Templates live on the server”, “The quota is one atomic SQL statement”, “The region block is on the server”. Referenced implementation: src/app.ts, src/apps/, supabase/migrations/. No claim that prompt injection is universally solved.

Read the pinned README ↗

Stream within the host's deadline

Constraint. Heroku drops a silent request after 30s

Decision. begin streaming, send a blank-line keep-alive every 10 seconds while waiting, and abort the model at 25s

Result. clients receive partial output and an explicit upper bound, rather than waiting for a silent router failure. Keep-alives stop at the first model chunk so a translation's line breaks remain intact.

Source and evidence limits

ew-apps-api/README.md, “Streaming, with a 25s stop”, “Keep-alives stop at the first chunk”, “JSON Lines output”. These are configured behaviors, not latency percentiles.

Read the pinned README ↗

Share infrastructure, keep app data local

Constraint. Five featured products need shared AI identity and budgets

Decision. static Workers frontends, shared Supabase identity/quota and one $7 Heroku dyno; a 6-digit email code is the fallback

Result. product data remains local while free-server AI has shared identity.

Source and evidence limits

ew-apps-api/README.md → “The idea”, “The IP limit is in memory”; taskpilot/README.md and pixe/README.md → “One sign-in for every EWSTUDIOS app”. The API README now documents workers.dev as a public suffix: ewstudios.workers.dev is the shared registrable passkey domain. Four apps offer passkeys; Natour uses email code only. Shared identity does not mean shared task, board or library storage.

Read the pinned README ↗

Architecture

Follow one request.

Browsers01Hono guards02Atomic quota03Server template04Gemini stream05Source CDN mediaSupabase / identity + atomic quotaQueues and caches: process memoryBrowsersHono guardsAtomic quotaServer templateGemini stream

Supabase supplies identity beside the guards and the atomic quota. Source-CDN media goes straight to the browser. Queues and caches remain in one process.

Browsers send capped data through region, IP, JWT and input guards, then an atomic Supabase quota and a server-owned template. Gemini streams back to the browser. Supabase supplies identity and quota; source-CDN media travels directly to the browser. Queues and caches are single-process memory.

Passkey domain: ewstudios.workers.dev. Because workers.dev is a public suffix, its child is registrable and can be the shared RP ID. Supabase origin limits mean four apps offer passkeys; Natour uses the emailed 6-digit code only. API README, “One passkey for every app, because workers.dev is a public suffix” · aafe065 ↗

Quality proof / evidence, with scope

Show what was checked.

App evidence

README documents route tests with fake dependencies; npm test, npm run lint and npm run typecheck. No current run or test total is claimed.

Repository report, read 26 October 2026. These app scores were not rerun for this portfolio.

Commands and evidence in the README ↗

This portfolio page

Static HTML, deferred decorative Stage, explicit media intent and complete reduced-motion content.

performance
99/100
accessibility
100/100
best practices
100/100
seo
100/100

Lighthouse mobile, light theme, cold cache, simulated throttling. Measured 2026-10-26. Gzip static export on localhost.

Eight page cases across Chromium/WebKit, phone/desktop and both themes; two axe audits with zero violations. WebKit playback and real Safari hardware remain unverified.

Read the dated portfolio check results ↗

The honest next step

What remains.

The IP limiter is in memory. Move it to Postgres before scaling beyond one dyno.

Next / TaskPilotYour day, co-piloted. ← Back to studio index