Ryoku is an AI customer-support operating system for small and growing businesses. It lets a business create a branded support bot, ingest knowledge-base content, answer customers through an embeddable chat widget, hand off conversations to agents, and review analytics from a dashboard.
The project is a Next.js App Router application with Auth.js, Neon Postgres, Drizzle ORM, Upstash Redis rate limiting, Pusher realtime events, Groq chat models, Google embeddings, and Resend email.
Live demo: https://ryoku-iota.vercel.app
- Fast business onboarding: create a business profile, pick an industry, configure support policies, and generate starter FAQs. AI-powered FAQ generation reads your website and drafts 10 Q&As instantly, with options to generate more on demand.
- RAG-backed support: upload or ingest documents and website content, embed them, and use them as support context.
- Embeddable chat widget: add a single script tag to any website and point it at a business slug.
- Human-in-the-Loop Handoff:
- One-Click Escalation: Customers can request a human agent instantly.
- Natural Transitions: Automated "bot thinking" simulations with typing indicators and transition messages for a professional feel.
- Agent Workspace (Command Center):
- Real-time Queue: Live dashboard with "Urgent" (escalated) and "Live" (active) badges powered by Pusher.
- Agent Presence: Automatic tracking of agent status (Online/Away/Offline).
- Canned Responses: Save and trigger pre-written responses for common queries.
- Session Control: Take over conversations, resolve them, or permanently delete history with custom professional confirmation modals.
- Intelligent Offline Support:
- Status Detection: The widget automatically detects if agents are offline.
- Lead Generation: Displays a "No agents available" query box if no one is online.
- Email Escalation: Forwards offline customer queries directly to the business escalation email via Resend.
- Analytics and Intelligence: Track chat volume, sentiment analysis, knowledge gaps, and export conversation data.
- AI Token Optimization: Built-in hard limits for production efficiency:
- Message Cap: 1,000 characters per message.
- Context Cap: 4,000 characters of RAG knowledge per turn.
- History Cap: 12 most recent turns (6 back-and-forth) to minimize context window costs.
- Security & Safety:
- Prompt-Injection Protection: Built-in guardrails against LLM manipulation.
- Input Sanitization: Multi-layer sanitization for both AI and Human inputs.
- API access: generate API keys and call a versioned chat endpoint for external integrations.
| Area | Tools |
|---|---|
| App framework | Next.js 16, React 19, TypeScript |
| Styling and UI | Tailwind CSS 4, Framer Motion, lucide-react |
| Auth | Auth.js / NextAuth v5 beta, Google OAuth |
| Database | Neon Postgres, Drizzle ORM, pgvector |
| AI | Vercel AI SDK, Groq chat models, Google embedding models |
| Realtime | Pusher Channels |
| Cache and rate limiting | Upstash Redis |
| Resend | |
| Deployment | Vercel |
.env Environment variables (not in version control)
.env.example Template for required environment variables
drizzle.config.ts Drizzle ORM configuration for migrations
eslint.config.mjs ESLint rules for code quality
next.config.ts Next.js build and runtime configuration
next-env.d.ts Auto-generated TypeScript definitions for Next.js
tsconfig.json TypeScript compiler configuration
package.json Project dependencies and npm scripts
postcss.config.mjs PostCSS plugins for Tailwind CSS
vercel.json Vercel deployment configuration
instrumentation-client.ts PostHog analytics instrumentation
posthog-setup-report.md PostHog configuration documentation
Main application structure using Next.js App Router:
src/app/
├── layout.tsx Root layout wrapper
├── page.tsx Home page
├── globals.css Global styles
├── proxy.ts Auth middleware for dashboard protection
│
├── api/ Backend API routes
│ ├── agent/ Agent management endpoints
│ │ ├── route.ts Main agent endpoints
│ │ ├── conversations/ Agent conversation endpoints
│ │ ├── messages/ Message endpoints
│ │ └── status/ Agent status endpoints
│ │
│ ├── chat/ Chat endpoints
│ │ ├── [[...slug]]/ Dynamic chat route with slug
│ │ ├── escalate/ Escalation workflow
│ │ ├── offline-query/ Offline query handling
│ │ └── typing/ Typing indicator endpoint
│ │
│ ├── auth/ Authentication
│ │ └── [...nextauth]/ NextAuth dynamic routes
│ │
│ ├── analytics/ Analytics endpoints
│ │ ├── route.ts Analytics data collection
│ │ ├── export/ Export analytics data
│ │ └── summary/ Analytics summaries
│ │
│ ├── conversations/ Conversation management
│ │ ├── route.ts List/create conversations
│ │ └── [id]/ Individual conversation routes
│ │
│ ├── ingest/ Document ingestion
│ │ └── route.ts Handle file/web content ingestion
│ │
│ ├── scrape/ Web scraping
│ │ └── route.ts Scrape website content
│ │
│ ├── widget/ Widget configuration
│ │ └── [slug]/ Per-business widget config
│ │
│ ├── evals/ Evaluation endpoints
│ │ └── route.ts Run quality evaluations
│ │
│ ├── generate-faqs/ FAQ generation
│ │ └── route.ts AI-powered FAQ generation
│ │
│ ├── keys/ API key management
│ │ └── route.ts CRUD for API keys
│ │
│ ├── profile/ User profile endpoints
│ │ └── route.ts User profile data
│ │
│ ├── canned-responses/ Pre-written responses
│ │ └── route.ts Manage canned responses
│ │
│ ├── agent-kits/ AI agent toolkits
│ │ └── route.ts Available agent tools/capabilities
│ │
│ ├── push/ Web push notifications
│ │ └── subscribe/ Push subscription management
│ │
│ ├── check-slug/ Business slug validation
│ │ └── route.ts Check if slug is available
│ │
│ ├── contact/ Contact form submission
│ │ └── route.ts Handle contact messages
│ │
│ ├── digest/ Email digest generation
│ │ └── route.ts Generate periodic digests
│ │
│ ├── sdk/ SDK endpoint
│ │ └── route.ts SDK distribution endpoint
│ │
│ ├── v1/ Versioned API
│ │ └── chat/ Chat API v1 endpoints
│ │
│ ├── debug/ Debug endpoints
│ │ └── auth-config/ Auth configuration debug
│ │
│ ├── pusher/ Pusher integration
│ │ └── auth/ Pusher channel authentication
│ │
│ ├── flows/ Business workflow execution
│ │ └── execute/ Execute workflow steps
│ │
│ └── admin/ Admin-only operations
│
├── auth/ Authentication pages
│ └── login/ Google OAuth login page
│
├── chat/ Chat UI
│ └── [slug]/ Per-business chat interface
│
├── dashboard/ Main dashboard
│ ├── page.tsx Dashboard home
│ ├── agent/ Agent management interface
│ ├── analytics/ Analytics dashboard
│ ├── billing/ Billing and subscription
│ └── conversations/ Conversation history view
│
└── docs/ Documentation pages
├── layout.tsx Docs layout
└── sdk/ SDK documentation
src/components/
├── ContactDialog.tsx Contact form modal
├── Navbar.tsx Navigation header
├── Providers.tsx Context providers (Auth, UI, etc.)
├── PushNotificationToggle.tsx Web push notification toggle
│
└── Docs/
└── CodeBlock.tsx Syntax-highlighted code display
src/lib/
├── agent-kits.ts Available AI agent tools and capabilities
├── ai-provider.ts Groq/LLM chat model initialization
├── auth.ts Auth.js configuration and helpers
├── billing.ts Stripe/billing logic
├── chat-handler.ts Chat message processing and responses
├── faq-packs.ts FAQ management
├── generate-draft-faqs.ts AI FAQ generation pipeline
├── intelligence.ts RAG and embeddings logic
├── posthog-server.ts Server-side PostHog tracking
├── prompt-guard.ts Prompt injection protection
├── pusher-client.ts Client-side Pusher configuration
├── pusher-server.ts Server-side Pusher integration
├── redis.ts Upstash Redis client
├── toast.ts Toast notification utilities
├── tools.ts LLM tool definitions for agents
├── web-push.ts Web Push API utilities
├── web-tools.ts Web scraping and content extraction
│
├── db/
│ ├── index.ts Database connection and queries
│ └── schema.ts Drizzle ORM schema definitions
│ - Users, businesses, documents, conversations
│ - Messages, agents, API keys, canned responses
│ - Analytics, push subscriptions
│
└── sdk/ TypeScript SDK package
├── index.ts Main SDK entry point
├── types.ts SDK TypeScript definitions
├── public.ts Public SDK API surface
├── build.js SDK build script
├── package.json SDK package metadata
├── tsconfig.json SDK TypeScript config
└── README.md SDK documentation
public/
├── widget.js Embeddable chat widget script (~compiled bundle)
├── sdk/
│ ├── ryoku-chat.umd.js SDK as UMD module for npm/CDN
│ └── types.d.ts TypeScript definitions for SDK
└── sw.js Service worker for web push notifications
drizzle/
├── 0000_bumpy_living_mummy.sql Initial schema migration
└── meta/
├── _journal.json Migration history log
└── 0000_snapshot.json Schema snapshot for verification
scripts/
├── migrate-add-email-verification.ts Add email verification column
├── migrate-full-schema.ts Full schema migration
└── reseed-test-biz.ts Reset test data
packages/
└── ryoku-chat/ Chat component package (if modular)
scratch/
├── check_sdk.js Verify SDK build
├── check_stream_proto.js Test protobuf streaming
└── check_stream_result.js Test result streaming
- API Routes: Each
/api/*endpoint is a separate route handler for modularity - Database Layer: All queries go through
src/lib/db/for consistency - Authentication: Auth.js manages Google OAuth flow with session persistence
- Realtime: Pusher handles agent presence, queue updates, and typing indicators
- AI Pipeline: LLM calls, embeddings, and RAG happen in
chat-handler.tsandintelligence.ts - Widget: Self-contained vanilla JS script that loads into customer websites
- SDK: Separate TypeScript package for programmatic API access
- Node.js 20 or newer
- npm
- A PostgreSQL database with the
pgvectorextension available - Google OAuth client credentials
- Upstash Redis REST URL and token
- Groq API key
- Google AI API keys for embeddings
- Pusher Channels app credentials
- Resend API key for contact and digest emails
Clone the repository and install dependencies:
git clone https://github.com/Trishix/ryoku.git
cd ryoku
npm installCreate a local environment file:
cp .env.example .envFill in the required variables:
# Neon Postgres
DATABASE_URL=postgresql://user:password@host.neon.tech/ryoku?sslmode=require
# Auth.js
AUTH_URL=http://localhost:3000
AUTH_SECRET=generate-with-openssl-rand-base64-32
AUTH_TRUST_HOST=true
AUTH_GOOGLE_ID=your-google-client-id
AUTH_GOOGLE_SECRET=your-google-client-secret
# Upstash Redis
UPSTASH_REDIS_REST_URL=https://your-redis.upstash.io
UPSTASH_REDIS_REST_TOKEN=your-redis-token
# Groq chat models
GROQ_API_KEY=your-groq-api-key
# Google AI embeddings
FREE_API_KEY_1=your-google-ai-key-1
FREE_API_KEY_2=your-google-ai-key-2
FREE_API_KEY_3=your-google-ai-key-3
# App
NEXT_PUBLIC_APP_URL=http://localhost:3000
# Pusher realtime
PUSHER_APP_ID=your-pusher-app-id
NEXT_PUBLIC_PUSHER_KEY=your-pusher-key
PUSHER_SECRET=your-pusher-secret
NEXT_PUBLIC_PUSHER_CLUSTER=us2
# Resend email
RESEND_API_KEY=re_your-resend-api-key
RESEND_EMAIL=ryoku@resend.dev
SUPPORT_EMAIL=support@example.com
# Vercel cron protection
CRON_SECRET=generate-a-random-secretPush the database schema:
npx drizzle-kit pushRun the development server:
npm run devOpen http://localhost:3000, sign in with Google, and complete the dashboard onboarding flow.
In Google Cloud Console, create an OAuth 2.0 client and add these redirect URIs:
http://localhost:3000/api/auth/callback/google
https://your-production-domain.com/api/auth/callback/google
For production, set:
AUTH_URL=https://your-production-domain.com
# Some environments also use NEXTAUTH_URL; this app will fall back to it if AUTH_URL is missing.
NEXT_PUBLIC_APP_URL=https://your-production-domain.com
AUTH_TRUST_HOST=trueIf Google reports redirect_uri_mismatch, the redirect URI in Google Cloud does not match the AUTH_URL used by the deployed app.
npm run dev # Start local development
npm run build # Build production bundle with webpack
npm run start # Start the production server
npm run lint # Run ESLint
npx drizzle-kit push # Apply schema changes to the databaseAfter creating a business in the dashboard, copy its slug and embed public/widget.js from your deployed app:
<script
src="https://your-production-domain.com/widget.js"
data-slug="your-business-slug"
data-position="bottom-right"
data-theme="dark"
data-color="#6366f1">
</script>The widget loads /chat/{slug}?embed=1 in an iframe and uses /api/widget/{slug} for public widget configuration.
Generate an API key from the dashboard, then call the versioned chat endpoint:
curl -X POST "https://your-production-domain.com/api/v1/chat/your-business-slug" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer rk_live_your_key" \
-d '{
"messages": [
{ "role": "user", "content": "What is your return policy?" }
]
}'The browser chat UI uses /api/chat/{slug}. External integrations should prefer /api/v1/chat/{slug}.
The repository includes vercel.json with:
-
Production deployment: https://ryoku-iota.vercel.app
-
npm install --include=dev -
npm run build -
a weekly
/api/digestcron schedule
Before deploying, configure all required environment variables in Vercel. At minimum, production needs:
DATABASE_URLAUTH_URLAUTH_SECRETAUTH_TRUST_HOSTAUTH_GOOGLE_IDAUTH_GOOGLE_SECRETUPSTASH_REDIS_REST_URLUPSTASH_REDIS_REST_TOKENGROQ_API_KEYFREE_API_KEY_1NEXT_PUBLIC_APP_URLPUSHER_APP_IDNEXT_PUBLIC_PUSHER_KEYPUSHER_SECRETNEXT_PUBLIC_PUSHER_CLUSTERRESEND_API_KEYSUPPORT_EMAILCRON_SECRET
Run these checks before pushing a deployment:
npm run lint
npm run buildThe schema lives in src/lib/db/schema.ts. Core tables include:
- Auth tables:
users,accounts,sessions,verification_tokens - Business setup:
businesses,documents,api_keys,widget_configs - Chat:
conversations,messages - Operations:
analytics_events,knowledge_gaps,agents,canned_responses,push_subscriptions
Document embeddings use a vector(3072) column, so the target Postgres database must support pgvector.
For help with setup or bugs:
- Check this README first, especially the environment and OAuth sections.
- Review the relevant route or module under
src/app/api/andsrc/lib/. - Open an issue in the GitHub repository with reproduction steps, expected behavior, actual behavior, and relevant logs.
Avoid posting real API keys, OAuth secrets, database URLs, or customer data in issues.
Ryoku is maintained by the repository owner at Trishix/ryoku.
Contribution flow:
- Create a branch from
main. - Keep changes focused and small enough to review.
- Run
npm run lintandnpm run build. - Open a pull request with a clear summary, screenshots for UI changes, and notes about any schema or environment changes.
There is not yet a separate CONTRIBUTING.md or LICENSE file in this repository. Add those before accepting broad external contributions or publishing this as an open-source package.