.Riyaan Docs
Concepts

Architecture

This page explains Riyaan's data model and runtime flow so you can understand how all the pieces connect.

Workspace Layout

next.config.ts
.env.local

The App — Central Entity

Everything in Riyaan hangs off a single entity: the App. An app represents one configured voice agent. In the database, it's a single row in the apps table, identified by a unique slug.

FieldWhat it controls
slugUnique identifier (e.g. demo-facility-management)
secretShared key for server-to-server auth
systemPromptInstructions sent to the LLM as the system message
personaNameAgent's display name (e.g. "Mina")
personaGreetingFirst message the agent says when a session starts
personaToneTone guidance (e.g. "calm, operational, concise")
voiceTTS voice selection (e.g. "Kore", "Puck")
modelIdLLM model identifier
isActiveKill switch to disable the app

What Hangs Off an App

Every app has five related data sets, all linked by appSlug:

Related tableCardinalityPurpose
ToolsN rowsFunctions the agent can call (e.g. create_maintenance_ticket)
Guardrail RulesN rowsRegex-based safety rules checked on every message
Knowledge DocumentsN rowsRAG documents with vector embeddings for semantic search
Scenario State1 rowMutable JSON blob that tool handlers read/write for demos
Outcome DefinitionsN rowsMeasurable business events for analytics and ROI

There is no separate "scenario" entity. The app row is the scenario. The word "scenario" in the codebase just means "a pre-configured app with sample data."

Tools

Each tool has a name, description, and parametersSchema (JSON Schema). At runtime, tool calls from the LLM are routed to handlers that either call an external API or return mock data for demos. See Tools.

Guardrail Rules

Each rule has a type (e.g. topic, pii), a regex pattern, and an action (block, warn, log). See Guardrails.

Knowledge Documents

Documents are chunked and embedded at seed time. At runtime, the agent searches them by semantic similarity when it needs domain-specific information. See Knowledge Base.

Scenario State

This is how demo scenarios maintain state across tool calls — an appointment gets rescheduled, an order gets a return initiated, a maintenance ticket gets created. Tool handlers receive the current state and can return a stateUpdate to mutate it.

Outcome Definitions

Each definition has a name (e.g. appointment_booked), a displayName, and an optional estimatedValue. When an outcome fires at runtime, an event is logged for the analytics and ROI dashboards.

Authentication

There are two auth modes. Both resolve to the same app row.

App Secret (server-to-server)

Your trusted backend sends the appSlug and appSecret to Convex. Used by server routes, the agent process, and CLI scripts.

POST /api/auth/session
{ "appSlug": "my-app", "appSecret": "sk_..." }

Response: { "sessionToken": "...", "expiresAt": 1234567890 }

Session Token (browser-safe)

The session token is passed to the browser. All subsequent API calls use it in the Authorization header.

GET /api/tools
Authorization: Bearer <sessionToken>

The token resolves to a session row, which contains the appSlug, which resolves to the app row and all its related data.

APP_SECRET

The secret in the apps table is the same value as APP_SECRET in your web app's environment. It's how Convex knows "this request is authorized to use this app."

Runtime Flow

Here's what happens from the moment a user opens a demo page to when the agent responds:

  1. Dashboard picks scenario — The frontend knows the appSlug for the selected scenario.

  2. Session created — The dashboard server route calls POST /api/auth/session with appSlug + appSecret and gets back a sessionToken.

  3. LiveKit room created — The dashboard calls POST /api/livekit/rooms. The room name encodes the appSlug.

  4. Agent joins — The agent process reads the appSlug from the room name, fetches the app config from Convex (system prompt, persona, voice, tools), and connects to the LLM.

  5. User speaks — Audio flows through LiveKit to the agent, which sends it to the LLM. The LLM responds with text/audio and optional tool calls.

  6. Tool execution — When the LLM calls a tool, the agent sends it to POST /api/tools/execute. Convex routes to the appropriate handler, which reads scenario state, returns a result, and optionally updates the state.

  7. Guardrails check — Every message is checked against the app's guardrail rules. If a rule fires, the message is blocked or flagged before reaching the user.

Seeding

Seeding is a one-time setup step that writes all the configuration for an app into Convex. A single seed command creates:

  • The apps row (identity, prompt, voice, auth)
  • tools rows (capabilities)
  • guardrailRules rows (safety)
  • outcomeDefinitions rows (metrics)
  • knowledgeDocuments rows with embeddings (RAG)
  • scenarioState row (initial mock data for demos)
# Seed one scenario
npx convex run seedScenarios:seedScenarioBySlug \
  '{"secret":"<APP_SECRET>","slug":"demo-facility-management"}' --prod

# Seed all scenarios
npx convex run seedScenarios:seedAllAction '{"secret":"<APP_SECRET>"}' --prod

# Seed a blank app (no tools/guardrails/knowledge)
npx convex run seed:seedApp \
  '{"slug":"my-app","name":"My App","secret":"...","systemPrompt":"You are a helpful assistant."}'

The secret parameter becomes the apps.secret field. Use the same value as APP_SECRET in your web app's environment.

Deployment

Deployments are triggered by git tags via GitHub Actions:

Tag patternWhat it deploysWhere
dashboard-v*Next.js web appRailway
agent-v*Voice agent processRailway
backend-v*Convex functionsConvex cloud

Important: Deploying the backend pushes code. Seeding writes config data. You need both when adding a new scenario.

Glossary

TermMeaning
AppA configured voice agent (one row in apps table)
ScenarioA pre-configured app with seed data — used interchangeably with "demo"
SlugUnique string identifier for an app
SecretShared key between your server and Convex for auth
Session TokenShort-lived browser-safe token derived from app secret
ToolA function the agent can call during conversation
Scenario StateMutable JSON blob that tool handlers read/write
Guardrail RuleRegex-based safety rule checked on every message
Knowledge DocRAG document with vector embedding
OutcomeA measurable business event
PersonaThe agent's name, greeting, tone, and voice settings
Edit on GitHub

Last updated on

On this page