Your AI learning architect — describe a goal, get a structured path from fundamentals to mastery, each step backed by a real, verified resource.
🌐 Live at https://viapro.to
ViaProto solves the problem of information overload and deteriorating search quality. Instead of spending hours wading through SEO-gamed content farms, users get:
- Structured learning paths with 5-8 sections of curated resources
- 40+ AI models to choose from (Claude, GPT, Gemini, DeepSeek, etc.)
- Verified resources via AI web search (videos, articles, books, projects, courses, etc.)
- Free & paid options clearly labeled for each resource
- Team collaboration with seat-based pricing
Target audience: Professionals who need to upskill quickly in an era of mounting layoffs and increasing automation.
| Category | Technology | Version |
|---|---|---|
| Framework | Next.js (App Router) | 15.1.8 |
| Language | TypeScript | 5.9.2 |
| UI | React | 19.0.0 |
| Styling | Tailwind CSS + DaisyUI | 4.1.10 + 5.0.5 |
| Database | Supabase (PostgreSQL) | 2.45.0 |
| CMS | TinaCMS (blog) | 3.9.3 |
| Auth | Supabase Auth | 2.45.0 |
| AI | OpenRouter (40+ models) | via OpenAI SDK 6.9.1 |
| Payments | Stripe | 13.11.0 |
| Resend | 4.0.1 | |
| Images | Unsplash API | Custom integration |
| Background Jobs | Graphile Worker | 0.16.6 |
| Deployment | Coolify → Hetzner VPS | - |
- Node.js 18+
- Supabase CLI (install)
- Accounts: Supabase, Stripe, OpenRouter, Resend, Unsplash
# Clone and install
git clone https://github.com/chznbaum/via-proto.git
cd via-proto
npm install
# Environment setup
cp .env.example .env.local
# Edit .env.local with your keys# Start local Supabase
supabase start
# Apply migrations and seed data
supabase db reset
# Generate/apply topic seeds
node scripts/generate-seeds.jsUpdate .env.local with the credentials from supabase start output.
# Terminal 1: Next.js dev server
npm run dev
# → http://localhost:3001
# Terminal 2: Background job worker (required for path generation)
npm run worker:dev# Supabase
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=
DATABASE_URL= # Postgres connection string (required by the worker)
# Stripe
STRIPE_PUBLIC_KEY=
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
# AI
OPENROUTER_API_KEY=
# Services
RESEND_API_KEY=
UNSPLASH_ACCESS_KEY=
# Site
NEXT_PUBLIC_SITE_URL=http://localhost:3001
# Worker (Background Jobs)
WORKER_CONCURRENCY=3
WORKER_POLL_INTERVAL=1000
# TinaCMS (blog)
NEXT_PUBLIC_TINA_CLIENT_ID= # Client ID from TinaCloud
TINA_TOKEN= # Read-only content token
TINA_SEARCH_TOKEN= # Search indexer tokenvia-proto/
├── app/
│ ├── (auth)/ # Auth route group — login, register, OAuth callback
│ ├── (dashboard)/ # Protected route group — dashboard, skills, progress, account, upgrade
│ ├── (main)/ # Public route group (own root layout)
│ │ ├── blog/ # Blog (TinaCMS-managed MDX)
│ │ ├── explore/ # Public path discovery
│ │ ├── paths/[id]/ # Public path detail page
│ │ ├── invite/ # Team invite acceptance
│ │ └── layout.tsx # App layout (html/body, fonts, analytics)
│ ├── api/ # API route handlers
│ │ ├── paths/ # Path generation & management
│ │ ├── stripe/ # Checkout & portal
│ │ ├── topics/ # Topic search
│ │ └── webhook/ # Stripe webhooks
│ └── layout.tsx # Root layout (metadataBase)
├── components/ # React components
├── contexts/ # React context providers
├── hooks/ # Shared React hooks
├── content/
│ └── posts/ # Blog posts as MDX (managed by TinaCMS)
├── tina/
│ └── config.ts # TinaCMS schema & configuration
├── libs/
│ ├── models/ # AI model configuration (40+ models)
│ ├── jobs/ # Background job tasks (Graphile Worker)
│ ├── supabase/ # Database clients (server, client, service)
│ ├── openrouter.ts # AI generation logic
│ ├── stripe.ts # Payment utilities
│ └── validation/ # Zod schemas
├── supabase/
│ ├── migrations/ # Database schema (timestamped)
│ └── seed.sql # Topic seed data
├── data/ # JSON source for seeds
├── scripts/
│ └── generate-seeds.js # Seed file generator
├── worker.ts # Background worker entry point
└── config.ts # App configuration
Note on Route Groups: The app uses three route groups, each with its own layout:
(main)- Public-facing pages with custom fonts, analytics, and client-side providers(dashboard)- Authenticated/protected pages (dashboard, skills, progress, account, upgrade)(auth)- Login, register, and OAuth callback flows
| Tier | Monthly Paths | Cost | Models |
|---|---|---|---|
| Free | 1 | $0 | 7 free models |
| Pro | 5 | $12/mo or $100/yr | All 40+ models |
| Team | 10 + (3 per seat) | $10/seat/mo | All 40+ models |
Team scaling: 2 seats = 10 paths, 5 seats = 19 paths, 10 seats = 34 paths
- User selects topic, skill level, optional goals, optional AI model
- System checks rate limits and model access
- Job #1 (
generate_metadata): AI generates title, description, skill level - Job #2 (
fetch_unsplash_image): Fetches cover image from Unsplash - Job #3 (
generate_sections_resources): AI generates 5-8 sections with 3-7 resources each - Multi-stage status tracking:
pending→generating_metadata→fetching_image→curating_resources→completed - Each job retries up to 3 times on failure; users notified via email on permanent failure
- Result: 20-100+ hours of curated learning content
Worker Process: Run npm run worker to start the Graphile Worker background processor
- Personal accounts: 1 user, 1 seat
- Team accounts: 2+ users, shared quota
- Learning paths belong to accounts, not users
- Billing via Stripe
customer_idon account
Core tables with Row-Level Security:
profiles- User profilesaccounts- Billing entities (personal/team)account_users- User ↔ Account relationshiptopics- Pre-seeded learning topicslearning_paths- AI-generated pathssections- Path sections (ordered)resources- Learning resources (videos, articles, etc.)unsplash_images- Cached featured images
npm run dev # Development server (port 3001)
npm run worker # Background job worker (required for path generation)
npm run worker:dev # Worker with hot reload
npm run build # Production build
npm run lint # ESLint check
npm run postbuild # Generate sitemap (auto-runs)
# Database
supabase db reset # Reset with migrations + seed
supabase db push # Push migrations to remote
# Stripe testing
stripe listen --forward-to localhost:3001/api/webhook/stripeEdit libs/models/model-config.ts and add to MODEL_CATALOG:
{
id: "provider/model-name",
name: "Display Name",
provider: "ProviderName",
minimumTier: "free" | "pro",
costTier: "free" | "low" | "medium" | "high" | "premium",
supportsWebSearch: true,
supportsStructuredOutput: true,
description: "Brief description",
featured: true, // Optional
}Model appears automatically in UI and generation API.
Deployed via Coolify to Hetzner VPS with BunnyCDN for DNS/CDN.
Two Services Required:
Service 1: Web (Next.js)
- Create Next.js application in Coolify
- Set build command:
npm run build - Set start command:
npm run start - Add production environment variables
- Link Supabase remote database
- Configure domain:
viapro.to
Service 2: Worker (Background Jobs)
- Create Node.js application in Coolify
- Set build command:
npm install - Set start command:
npm run worker - Share same environment variables as web service
- Set restart policy: always
- Both services must connect to same DATABASE_URL
- Apply database migrations:
supabase db push - Seed topics database
- Configure Stripe webhook:
https://viapro.to/api/webhook/stripe - Test payment flows
- Verify RLS policies active
- Test path generation
- Enable SSL (Coolify auto-handles)
Full deployment guide: https://coolify.io/docs/applications/nextjs
POST /api/paths/initiate- Create path and queue background jobGET /api/paths/[id]/status- Check generation status (poll this during generation)GET /api/paths/[id]- Get path details
POST /api/stripe/create-checkout- Start paymentPOST /api/stripe/create-portal- Manage subscriptionPOST /api/webhook/stripe- Handle Stripe eventsGET /api/models- List available AI modelsGET /api/topics- Search topics (typeahead)
- Support: support@viapro.to
- Privacy: privacy@viapro.to
- Security: security@viapro.to
Proprietary - All rights reserved. Not open source.
Built with Next.js, Supabase, OpenRouter, Stripe, and Tailwind CSS.