Skip to content

Tutorial 4: Configuration Deep Dive

⏱️ Time: 30 minutes | 🎯 Goal: Master Space and Agent configuration

In this tutorial, you’ll learn how to configure every aspect of your Viber workspace for production use.

  • LLM provider configuration
  • Storage adapter options
  • Environment variable management
  • Production deployment patterns
  • Completed previous tutorials
  • Understanding of Viber concepts
  • An LLM API key

Viber is designed to be flexible. Key configuration areas:

  1. LLM Providers — Which AI model to use
  2. Storage Adapters — Where to persist data
  3. Agent Settings — How agents behave
  4. Environment — Managing secrets and settings

Viber supports multiple LLM providers through the Vercel AI SDK:

.env
OPENAI_API_KEY=sk-your-key
.env
ANTHROPIC_API_KEY=your-key
.env
DEEPSEEK_API_KEY=your-key
.env
GOOGLE_GENERATIVE_AI_API_KEY=your-key

The provider is automatically detected based on which environment variable is set.

Viber supports two storage backends:

Uses SQLite for structured data and the filesystem for files. Perfect for:

  • Local development
  • Desktop applications
  • Offline-first apps
import { XAgent } from "viber";
import { createLocalAdapter } from "@viber/local";
// Create a local adapter (optional - this is the default)
const adapter = createLocalAdapter({
path: "./data", // Where to store data
});
const xAgent = await XAgent.start("My project", {
adapter,
});

Uses PostgreSQL for structured data and Supabase Storage for files. Ideal for:

  • Cloud-hosted applications
  • Multi-user collaboration
  • Apps requiring authentication
.env
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_KEY=your-service-key
import { XAgent } from "viber";
import { createSupabaseAdapter } from "@viber/supabase";
const adapter = createSupabaseAdapter({
supabaseUrl: process.env.SUPABASE_URL!,
supabaseKey: process.env.SUPABASE_SERVICE_KEY!,
});
const xAgent = await XAgent.start("My project", {
adapter,
});

Create a .env file:

Terminal window
# LLM Provider
OPENAI_API_KEY=sk-your-key
# Optional: Supabase for cloud storage
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_KEY=your-service-key
# Development settings
NODE_ENV=development
LOG_LEVEL=debug

For production, use secure environment management:

Terminal window
# Production .env (or use your platform's secrets management)
OPENAI_API_KEY=sk-production-key
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_KEY=your-service-key
NODE_ENV=production
LOG_LEVEL=info

Always load environment variables at the start of your application:

import "dotenv/config";
// or
import { config } from "dotenv";
config();

Viber uses sensible defaults from @viber/defaults:

import { XAgent } from "viber";
// Uses default configuration
const xAgent = await XAgent.start("My project");

You can customize agent behavior through YAML files in @viber/defaults:

Default agents include:

  • X — The orchestrating agent (project manager)
  • Researcher — Gathers information
  • Writer — Creates content
  • Developer — Writes code
  • Reviewer — Quality assurance
  • Web Designer — HTML/CSS creation

Agent prompts are stored in @viber/defaults/prompts/. You can override them:

import { XAgent } from "viber";
const xAgent = await XAgent.start("My project", {
// Custom system prompt for XAgent
systemPrompt: `You are a specialized assistant for...`,
});

Here’s a complete production setup:

src/config.ts
import "dotenv/config";
import { createSupabaseAdapter } from "@viber/supabase";
// Validate required environment variables
const requiredEnvVars = [
"OPENAI_API_KEY",
"SUPABASE_URL",
"SUPABASE_SERVICE_KEY",
];
for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
throw new Error(`Missing required environment variable: ${envVar}`);
}
}
// Create production adapter
export const adapter = createSupabaseAdapter({
supabaseUrl: process.env.SUPABASE_URL!,
supabaseKey: process.env.SUPABASE_SERVICE_KEY!,
});
// Export configuration
export const config = {
isProduction: process.env.NODE_ENV === "production",
logLevel: process.env.LOG_LEVEL || "info",
};
src/index.ts
import { XAgent } from "viber";
import { adapter, config } from "./config.js";
async function main() {
console.log(`Starting in ${config.isProduction ? "production" : "development"} mode`);
const xAgent = await XAgent.start("Production workspace", {
adapter,
});
// Your application logic...
}
main().catch(console.error);

For React applications, use @viber/react:

Terminal window
pnpm add @viber/react
// app/actions.ts (Next.js Server Actions)
"use server";
import { XAgent } from "viber";
export async function createSpace(goal: string) {
const xAgent = await XAgent.start(goal);
return xAgent.getSpace().spaceId;
}
export async function sendMessage(spaceId: string, message: string) {
const xAgent = await XAgent.resume(spaceId);
const stream = await xAgent.streamText({
messages: [{ role: "user", content: message }],
metadata: { mode: "agent", requestedAgent: "X" },
});
return stream;
}
app/components/Chat.tsx
"use client";
import { useSpace, useChat } from "@viber/react";
export function Chat({ spaceId }: { spaceId: string }) {
const { space } = useSpace(spaceId);
const { messages, sendMessage, isLoading } = useChat(spaceId);
return (
<div>
<h2>{space?.name}</h2>
{messages.map((msg, i) => (
<div key={i}>{msg.content}</div>
))}
<button onClick={() => sendMessage("Hello!")}>Send</button>
</div>
);
}
Terminal window
# Never commit secrets
echo ".env" >> .gitignore
# Use .env.example for documentation
cp .env .env.example
# Remove actual secrets from .env.example
try {
const xAgent = await XAgent.start("My project");
} catch (error) {
if (error.message.includes("API key")) {
console.error("Missing or invalid API key");
} else if (error.message.includes("network")) {
console.error("Network error - check your connection");
} else {
throw error;
}
}
// Configure logging level
process.env.LOG_LEVEL = "debug"; // debug, info, warn, error
// In production
process.env.LOG_LEVEL = "info";
// Always save state before exit
process.on("SIGINT", async () => {
console.log("\nSaving workspace...");
await space.persistState();
process.exit(0);
});
OptionTypeDescription
adapterDataAdapterStorage adapter (local or Supabase)
systemPromptstringOverride the default system prompt
OptionTypeDescription
adapterDataAdapterStorage adapter (must match original)
VariableRequiredDescription
OPENAI_API_KEYOne LLM key requiredOpenAI API key
ANTHROPIC_API_KEYOne LLM key requiredAnthropic API key
DEEPSEEK_API_KEYOne LLM key requiredDeepSeek API key
SUPABASE_URLFor cloud storageSupabase project URL
SUPABASE_SERVICE_KEYFor cloud storageSupabase service key
NODE_ENVNodevelopment or production
LOG_LEVELNodebug, info, warn, error

You now understand how to configure Viber for any environment:

âś… LLM Provider Configuration
âś… Storage Adapter Options
âś… Environment Variable Management
âś… Production Deployment Patterns
âś… React Integration

You’ve completed the core Viber tutorials! Here are some next steps:

Configuration not taking effect?

  • Ensure environment variables are loaded before using them
  • Check for typos in variable names
  • Verify the .env file is in the correct directory

Storage errors?

  • For Supabase, verify your URL and key are correct
  • For local storage, ensure the directory is writable
  • Check file permissions

Ready for advanced topics? Continue to Comprehensive Systems! 🏭