How the OpenSEO Onboarding Chat System Works: Architecture and Implementation
The OpenSEO onboarding chat system is a durable-object-based AI agent that provides new users with 7 free SEO strategy questions before requiring a subscription, enforcing limits through both client-side gating and server-side credit metering.
OpenSEO's onboarding chat functions as a short-lived, credit-metered assistant that demonstrates the platform's SEO capabilities to prospective users. The system combines Durable Objects for stateful persistence, SQLite for conversation storage, and tool-augmented LLM calls to deliver contextual strategy advice. This article breaks down the complete architecture based on the every-app/open-seo source code.
Client-Side Gating: The Free Question Limit
The onboarding experience begins with a hardcoded constant that synchronizes client and server behavior.
// src/shared/onboardingChat.ts
export const FREE_ONBOARDING_QUESTION_LIMIT = 7;
This FREE_ONBOARDING_QUESTION_LIMIT serves as the single source of truth across both environments. The client UI consumes this constant to:
- Display "X questions left" indicators
- Disable the message composer when the limit is reached
- Trigger subscription prompts at the appropriate moment
Keeping this value in a shared module ensures the client preview never diverges from server enforcement.
Server-Side Architecture: OnboardingChatAgent
The core of the OpenSEO onboarding chat is the OnboardingChatAgent class, a Durable Object that extends AIChatAgent and lives in src/server/features/onboarding/OnboardingChatAgent.ts.
Message Persistence and State
The agent maintains conversation history in SQLite, with automatic retry logic for transient database errors. The persistMessages method guarantees durability even under load spikes.
Dual Enforcement: Count and Credits
When processing a onChatMessage request, the agent performs two sequential checks:
-
Question count – Filters stored messages by
role === 'user'and compares againstFREE_ONBOARDING_QUESTION_LIMIT. If exceeded and the organization lacks managed access, returns a static subscription response (lines 31-38). -
Credit balance – In hosted mode, calls
checkUsageCreditsDepletedto verify onboarding credits remain. If depleted, returns the same subscription prompt (lines 42-48).
// Core enforcement logic from OnboardingChatAgent.ts
async onChatMessage(onFinish, options) {
const project = await ProjectRepository.getProjectById(this.name);
const questionCount = this.messages.filter(m => m.role === 'user').length;
// Free-question cap enforcement
if (questionCount > FREE_ONBOARDING_QUESTION_LIMIT &&
!(await customerHasManagedAccess(project.organizationId))) {
return staticAssistantResponse(
'You\'ve used all your free strategy questions. Subscribe to continue.'
);
}
// Credit enforcement in hosted environments
if (await checkUsageCreditsDepleted(project.organizationId, 'onboarding')) {
return staticAssistantResponse('Subscribe to continue.');
}
// ... LLM orchestration
}
LLM Orchestration and the "Sam" Persona
The OpenSEO onboarding chat presents itself as "Sam", an SEO strategy assistant. The buildSystemPrompt method (lines 26-60) constructs detailed instructions governing:
- Tone and style – Professional but approachable SEO expert voice
- Content boundaries – Allowed topics and response patterns
- Tool availability – Which capabilities the model may invoke
Available Tools for the Onboarding Assistant
The buildOnboardingTools factory exposes SEO-specific capabilities:
| Tool | Purpose |
|---|---|
read_website |
Fetches and analyzes page content |
get_seo_metrics |
Retrieves performance indicators |
research_keywords |
Identifies keyword opportunities |
These tools allow the onboarding chat to demonstrate real OpenSEO functionality rather than generic advice.
Billing Integration and Cost Tracking
The OpenSEO onboarding chat system meters actual LLM spend against organization credits:
- Token budgeting – Each request caps output at 4000 tokens
- Per-step cost tracking –
streamTextruns with cost instrumentation - Credit consumption –
trackUsageCreditSpendrecords expenditure against the organization's onboarding credit pool (lines 70-84)
This ensures free previews remain economically bounded while providing genuine utility.
Key Files and Responsibilities
| File Path | Role in Onboarding Chat |
|---|---|
src/shared/onboardingChat.ts |
Shared constants (FREE_ONBOARDING_QUESTION_LIMIT) |
src/server/features/onboarding/OnboardingChatAgent.ts |
Durable Object agent, enforcement, LLM orchestration |
src/server/features/onboarding/onboardingChatTools.ts |
Tool factories (read_website, get_seo_metrics, etc.) |
src/server/lib/openrouter.ts |
Model selection and cost reporting |
src/server/billing/subscription.ts |
Credit checking and tracking utilities |
src/server/lib/chatAgent.ts |
Base agent utilities and cost calculations |
Summary
- The OpenSEO onboarding chat provides 7 free strategy questions via a synchronized client-server limit (
FREE_ONBOARDING_QUESTION_LIMIT) - The
OnboardingChatAgentDurable Object enforces both message-count and credit-based restrictions before serving LLM responses - The "Sam" persona operates through a constructed system prompt with access to real SEO tools (
read_website,get_seo_metrics,research_keywords) - Cost tracking via
trackUsageCreditSpendbinds free usage to actual LLM expenditure, preventing abuse - SQLite persistence with retry logic ensures conversation state survives restarts and errors
Frequently Asked Questions
What happens when a user exceeds the free question limit in OpenSEO?
The OnboardingChatAgent counts user messages in its SQLite store. Once the count exceeds FREE_ONBOARDING_QUESTION_LIMIT (7) and the organization lacks managed access, the agent returns a static response prompting subscription. The client UI simultaneously disables the composer based on the same constant.
How does OpenSEO prevent unlimited free usage of the onboarding chat?
The system implements dual enforcement: message counting for immediate gating, plus credit balance checking via checkUsageCreditsDepleted for hosted deployments. Even if a client bypasses the UI limit, the server rejects requests when trackUsageCreditSpend indicates exhausted credits.
What tools can the onboarding chat assistant use?
According to buildSystemPrompt and buildOnboardingTools, the assistant "Sam" may invoke read_website, get_seo_metrics, and research_keywords. These tools demonstrate actual OpenSEO capabilities rather than delivering generic SEO advice.
Why does OpenSEO use a Durable Object for onboarding chat?
The OnboardingChatAgent extends AIChatAgent as a Durable Object to maintain stateful conversation persistence across HTTP requests. SQLite storage inside the Durable Object guarantees messages survive connection drops, with persistMessages implementing retry logic for transient failures.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →