- Node.js 20+, pnpm 9+
- A free Neon PostgreSQL database
- A free Clerk account (auth)
- A free Google AI Studio key (Gemini)
- A free Trigger.dev account (task execution)
- A Transloadit account (file uploads)
pnpm installcp .env.example apps/web/.env.local
# Fill in all values in apps/web/.env.localcd packages/core
pnpm db:generate # generates the Prisma client
pnpm db:push # pushes schema to Neon (no migration files)In your Transloadit dashboard:
- Create an image template (e.g.
/google/storerobot pointing at your GCS bucket) and copy its ID - Create a video template similarly and copy its ID
- Add to
apps/web/.env.local:
TRANSLOADIT_KEY=<your auth key>
TRANSLOADIT_SECRET=<your auth secret>- Set the template IDs in
apps/web/app/api/transloadit/route.ts:
const TEMPLATE_IDS = {
image: "<your image template id>",
video: "<your video template id>",
};pnpm --filter web devcd apps/web
npx trigger.dev@latest devThis connects your local Trigger.dev tasks to the cloud dashboard and lets them execute.
In your Clerk dashboard:
- Go to Webhooks → Add Endpoint
- URL:
https://<your-tunnel>/api/webhooks/clerk - Subscribe to
user.created,user.updated,user.deleted - Copy the signing secret to
CLERK_WEBHOOK_SIGNING_SECRET
Use ngrok or Cloudflare Tunnel to expose localhost.
- Push repo to GitHub
- Go to vercel.com → New Project → import the repo
- In Configure Project, set:
- Root Directory:
apps/web - Framework Preset: Next.js
- Node.js Version: 20.x
- Root Directory:
In Vercel → Settings → General → Build & Development Settings:
| Setting | Value |
|---|---|
| Install Command | cd ../.. && pnpm install |
| Build Command | pnpm build |
| Output Directory | .next (default) |
In Vercel → Settings → Environment Variables, add every key from .env.example:
DATABASE_URL
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
CLERK_SECRET_KEY
CLERK_WEBHOOK_SIGNING_SECRET
GEMINI_API_KEY
TRIGGER_SECRET_KEY
TRANSLOADIT_KEY
TRANSLOADIT_SECRET
Set all to Production, Preview, and Development environments.
- Update Clerk webhook URL →
https://<your-app>.vercel.app/api/webhooks/clerk - Deploy Trigger.dev tasks to production:
cd apps/web TRIGGER_SECRET_KEY=<prod-key> npx trigger.dev@latest deploy
- Run
pnpm db:pushfrompackages/coreagainst the productionDATABASE_URLto ensure schema is up to date - Visit
https://<your-app>.vercel.app— should redirect to sign-in
apps/web/
├── middleware.ts # Clerk auth guard (all routes except public ones)
├── app/api/ # Next.js API routes
│ ├── execute-workflow/ # Triggers workflow run via Trigger.dev
│ ├── runs/ # Fetch run history (polled by history sidebar)
│ ├── transloadit/ # Returns signed Transloadit assembly params
│ ├── workflows/ # CRUD for saved workflows
│ └── webhooks/clerk # Clerk user sync
├── trigger/ # Trigger.dev tasks (run on Trigger.dev cloud)
│ ├── orchestrator.ts # DAG execution engine
│ ├── llm.ts # Gemini API calls
│ └── media.ts # FFmpeg crop & frame extraction
├── store/canvas-store.ts # Zustand state (nodes, edges, undo/redo)
├── ui/workflow-builder/
│ ├── canvas/ # ReactFlow canvas + connection handler
│ ├── nodes/ # All 6 node implementations + renderers
│ └── sidebar/ # Left (node library) + Right (history)
└── config/
├── sidebar-nodes.ts # Node library configuration
└── sample-workflow.ts # Pre-built demo workflow
packages/core/
├── src/nodes/ # Node definitions (Zod-validated)
├── src/schema/ # Port & control schemas
├── src/helpers/ # hasCycle(), ValidateConnection()
├── src/db.ts # Prisma client singleton
└── prisma/schema.prisma
- Create
packages/core/src/nodes/my-node.tswith aNodeDefinitionSchema.parse(...)export - Register it in
packages/core/src/nodes/index.tsNodeRegistry - Create
apps/web/ui/workflow-builder/nodes/implementations/my-node.tsxusing<BaseNode> - Export from
apps/web/ui/workflow-builder/nodes/index.ts - Add to
NODE_TYPESinapps/web/ui/workflow-builder/type.ts - Add to
SIDEBAR_NODESinapps/web/config/sidebar-nodes.ts - Handle in
apps/web/trigger/orchestrator.tsswitch statement