Collaborative video review, built on Cloudflare. Organize work into team spaces, upload up to 5GB, stack new versions, share a link, and collect timestamped feedback from your team or clients — no account required for external reviewers.
Think Frame.io, but small, focused, and running entirely on the edge.
- Resumable 5GB uploads — drag-drop with TUS-resumable direct uploads to Cloudflare Stream
- Spaces for teams and clients — create separate workspaces, invite members, switch contexts, and keep videos scoped to the right group
- Version stacking — upload new cuts into an ordered stack while keeping review history version-specific
- Timestamped comments — feedback pinned to the exact frame
- Threaded replies + resolve — keep review discussions organized
- Approval workflows — configure required approvals per space and track sign-off on each cut
- Invite notifications — pending space invites show in the account menu and can be accepted from
/notifications - Public share links — reviewers comment without an account; revoke anytime
- Global HLS playback — adaptive streaming via Cloudflare Stream
- Auth + sessions — email/password (PBKDF2), HttpOnly secure cookies, "remember me"
| Layer | Tech |
|---|---|
| Framework | Astro 6 (SSR) with React 19 islands |
| Styling | Tailwind CSS v4 (dark, purple-accented design system) |
| Database ORM | Drizzle ORM |
| Validation | Zod |
| Deployment | Cloudflare Workers via @astrojs/cloudflare |
| Tooling | Wrangler, pnpm, TypeScript |
QuickCut runs end-to-end on Cloudflare's network. No origin servers, no separate CDN.
The entire Astro app — auth, API routes, page rendering — is deployed as a single Worker via @astrojs/cloudflare. Sub-50ms cold starts in 300+ cities mean reviewers never wait on the network.
- Configured in
wrangler.jsoncwithnodejs_compatfor Node-style APIs used by Drizzle. - Astro middleware (
src/middleware.ts) enforces auth for protected routes and API endpoints.
Serverless SQLite backs every persistent record. The DB binding gives the Worker direct, low-latency SQL access with zero infrastructure to manage.
Tables (see src/db/schema.ts):
users— accounts (PBKDF2-hashed passwords)sessions— server-side sessions tied toquickcut_sessioncookiespaces— team/client workspaces, ownership, and required approval settingsspace_members— user membership and role per spacespace_invites— pending, accepted, declined, and revoked space invitesvideos— Stream UID, status, metadata, version group fields, and space ownershipshare_links— token, status, view countcomments— timestamped, threaded (parentId), resolvableapprovals— per-user approval records for videos
Migrations are managed via drizzle-kit and applied with wrangler d1 migrations apply.
A VideoRoom Durable Object — one instance per video version, routed deterministically with getByName(videoId) — fans out new comments to every connected reviewer in real time.
- The Astro app upgrades
/api/videos/[id]/liveinto a WebSocket forwarded to the per-video DO - Hibernatable WebSockets (
ctx.acceptWebSocket) keep idle rooms at zero duration cost - Comment POST routes call the DO's
broadcastCommentRPC after persisting to D1, so D1 stays the source of truth and the DO is purely a coordination/fan-out layer - Anonymous reviewers must enter a display name on page load before any video, comments, or socket activity loads
wrangler.jsoncdeclares the binding (VIDEO_ROOM) and thenew_sqlite_classesmigration; types are regenerated onpnpm types/pnpm dev/pnpm build
All video lifecycle is offloaded to Cloudflare Stream:
- Direct creator uploads via TUS (
/accounts/{id}/stream?direct_user=true) — files never pass through our Worker - Automatic transcoding, thumbnail generation, duration detection
- Adaptive HLS/DASH delivery from the Cloudflare edge
- Webhook handler at
/api/webhooks/streamflipsvideos.statusfromprocessing→ready/failed - Result: zero egress fees, resumable multi-GB uploads, global playback out of the box
The static Astro build (./dist) is served via the ASSETS binding — cached at every edge POP for instant page loads. No separate CDN, no S3 bucket, no signing setup.
Workers Observability is enabled in wrangler.jsonc for production logs and metrics.
- One platform — compute, database, video, static assets, and observability live behind a single set of bindings
- Zero ops — nothing to provision, scale, or patch
- Edge by default — every request is served from the closest POP without extra config
- Cost-aligned with usage — Workers, D1, and Stream all scale to zero
src/
├── components/ Astro + React components (VideoCard, VideoPlayer, CommentThread, ...)
├── db/ Drizzle schema and client factory
├── durable-objects/ Durable Object classes (VideoRoom for real-time comment fan-out)
├── layouts/ Layout.astro
├── lib/ Auth, Stream, spaces, invites, broadcast (DO RPC), realtime (client WS) helpers
├── middleware.ts Session loader + route protection
├── worker.ts Custom Worker entry — delegates to Astro and exports DOs
├── pages/
│ ├── index.astro Marketing landing page
│ ├── login.astro Sign-in form
│ ├── register.astro Account creation
│ ├── dashboard.astro Video grid (auth)
│ ├── notifications.astro Pending space invites
│ ├── upload.astro Upload (auth)
│ ├── spaces/[id]/settings.astro Space settings, members, and invites
│ ├── videos/[id].astro Authenticated review view
│ ├── invites/[token].astro Direct invite accept/decline view
│ ├── s/[token].astro Public share view (no auth)
│ └── api/ Auth, spaces, invites, videos, versions, comments, share-links, webhooks, /live (WS upgrade)
└── styles/ Tailwind + design tokens
migrations/ D1 migrations
wrangler.jsonc Worker + bindings config (DB, Durable Objects, vars)
- Node.js 22.12+
- pnpm
- A Cloudflare account with Workers, D1, and Stream enabled
- A Stream API token
pnpm installCreate a .dev.vars file in the project root for local secrets (already gitignored):
STREAM_API_TOKEN=your_stream_api_token
STREAM_WEBHOOK_SECRET=your_webhook_secret
Update wrangler.jsonc with your STREAM_ACCOUNT_ID.
Generate and apply migrations locally:
pnpm db:generate
pnpm db:migrate:localFor the remote D1 database:
pnpm db:migrate:remotepnpm devApp runs at http://localhost:4321.
pnpm deployBuilds the Astro app and deploys to Cloudflare Workers via Wrangler.
| Command | Action |
|---|---|
pnpm dev |
Start the dev server |
pnpm build |
Build for production |
pnpm preview |
Preview the production build locally |
pnpm deploy |
Build + deploy to Cloudflare |
pnpm db:generate |
Generate Drizzle migrations from schema |
pnpm db:migrate:local |
Apply migrations to local D1 |
pnpm db:migrate:remote |
Apply migrations to remote D1 |
pnpm types |
Regenerate Cloudflare runtime types from wrangler.jsonc |
MIT