Skip to main content

VoltAgent Server Architecture

VoltAgent 1.x introduces a framework-agnostic server architecture that separates core functionality from server implementation. This design allows you to use different server frameworks or create custom implementations.

Architecture Overview

The server system consists of two main packages:

@voltagent/server-core

The core package provides framework-agnostic components:

  • Route Definitions - Standardized route specifications (/packages/server-core/src/routes/definitions.ts)
  • Request Handlers - Business logic for all endpoints
  • Base Provider - Abstract class for server implementations
  • WebSocket Support - Real-time communication infrastructure
  • Auth Interface - Pluggable authentication system

@voltagent/server-hono

The official server implementation using Hono:

  • Fast, lightweight web framework
  • Built-in OpenAPI and Swagger UI support
  • TypeScript-first with excellent DX
  • Works with Node.js, Bun, Deno, and Edge runtimes

Core Concepts

Server Provider Interface

Every server implementation must satisfy the IServerProvider interface:

interface IServerProvider {
start(): Promise<{ port: number }>;
stop(): Promise<void>;
isRunning(): boolean;
}

Server Provider Factory

Server providers are created using factory functions:

type ServerProviderFactory = (deps: ServerProviderDeps) => IServerProvider;

The ServerProviderDeps provides access to:

  • agentRegistry - Manage and access agents
  • workflowRegistry - Manage and access workflows
  • logger - Logging infrastructure
  • voltOpsClient - Telemetry and prompt management
  • observability - OpenTelemetry integration

Base Server Provider

The BaseServerProvider class handles common server tasks:

abstract class BaseServerProvider implements IServerProvider {
// Manages port allocation
// Sets up WebSocket server
// Handles graceful shutdown
// Prints startup messages

protected abstract startServer(port: number): Promise<Server>;
protected abstract stopServer(): Promise<void>;
}

Using the Hono Server

Basic Setup

import { VoltAgent } from "@voltagent/core";
import { honoServer } from "@voltagent/server-hono";

new VoltAgent({
agents: { myAgent },
server: honoServer({
port: 3141,
enableSwaggerUI: true,
}),
});

Configuration Options

interface HonoServerConfig {
// Port to listen on (default: 3141)
port?: number;

// Enable Swagger UI (default: true in dev, false in prod)
enableSwaggerUI?: boolean;

// Configure the Hono app directly
configureApp?: (app: OpenAPIHono) => void | Promise<void>;

// Authentication provider
auth?: AuthProvider;
}

Advanced Configuration

Access the Hono app directly for custom middleware and routes:

new VoltAgent({
agents: { myAgent },
server: honoServer({
configureApp: (app) => {
// Add custom middleware
app.use("/admin/*", authMiddleware);

// Add custom routes
app.get("/health", (c) => c.json({ status: "ok" }));

// Create route groups
const api = app.basePath("/api/v2");
api.get("/users", getUsersHandler);

// Add rate limiting
app.use("*", rateLimiter());
},
}),
});

Port Management

VoltAgent includes intelligent port management:

  1. Default Port: 3141
  2. Fallback Ports: If default is taken, tries 4310, 1337, etc.
  3. Port Allocation: Central manager prevents conflicts
  4. Graceful Release: Ports are released on shutdown

WebSocket Support

WebSocket is automatically enabled for real-time features:

  • Base path: /ws
  • Endpoints:
    • /ws – connection test/echo
    • /ws/logs – real-time logs (filterable via query params)
    • /ws/observability – spans and logs for observability (optional entityId/entityType filters)
  • Protocol: Standard WebSocket with JSON messages

Note: The Hono server config does not currently expose WebSocket configuration options. WebSockets are on by default and use the /ws path. If you need to change the path or behavior, implement a custom provider using @voltagent/server-core.

Route Registration

Routes are defined in a framework-agnostic format:

const AGENT_ROUTES = {
listAgents: {
method: "get" as const,
path: "/agents",
summary: "List all registered agents",
tags: ["Agent Management"],
responses: {
200: { description: "Success" },
500: { description: "Server error" },
},
},
// ... more routes
};

Server implementations use these definitions to:

  • Generate OpenAPI documentation
  • Register routes with proper handlers
  • Ensure consistency across implementations

Middleware and Plugins

The Hono server supports standard middleware:

configureApp: (app) => {
// CORS
app.use("*", cors());

// Logging
app.use("*", logger());

// Compression
app.use("*", compress());
};

Graceful Shutdown

VoltAgent provides comprehensive shutdown handling to ensure clean resource cleanup and compatibility with other frameworks.

How Shutdown Works

When VoltAgent receives a shutdown signal (SIGINT/SIGTERM):

  1. Server stops first - Prevents new requests from being accepted
  2. Workflows suspend - All active workflows are gracefully suspended
  3. Telemetry shuts down - Observability resources are properly closed
  4. Process exits (conditionally) - Only if VoltAgent is the sole signal handler

Automatic Shutdown Handling

VoltAgent automatically registers SIGINT and SIGTERM handlers that clean up resources:

const voltAgent = new VoltAgent({
agents: { myAgent },
server: honoServer({ port: 3141 }),
});

// When you press Ctrl+C or the process receives SIGTERM:
// 1. Server stops accepting new connections
// 2. Active workflows are suspended
// 3. Telemetry is flushed and closed
// 4. Process exits (if no other handlers exist)

Programmatic Shutdown

Use the shutdown() method for manual resource cleanup:

const voltAgent = new VoltAgent({
agents: { myAgent },
server: honoServer({ port: 3141 }),
});

// Later in your application
await voltAgent.shutdown();
// All resources are now cleaned up

Framework Integration

VoltAgent respects other frameworks' shutdown handlers. When multiple handlers exist (e.g., from Adonis, NestJS, Express), VoltAgent:

  1. Performs its cleanup (server, workflows, telemetry)
  2. Does not call process.exit()
  3. Allows other handlers to complete their cleanup
  4. Lets the framework control the final exit

Example with Express:

import express from "express";
import { VoltAgent } from "@voltagent/core";
import { honoServer } from "@voltagent/server-hono";

const app = express();
const voltAgent = new VoltAgent({
agents: { myAgent },
server: honoServer({ port: 3141 }),
});

// Express graceful shutdown
process.on("SIGTERM", async () => {
console.log("Express: Closing server...");

// VoltAgent already cleaned up its resources
// Now Express can do its cleanup
server.close(() => {
console.log("Express: Server closed");
process.exit(0);
});
});

Server Provider Shutdown

The BaseServerProvider handles common shutdown tasks:

  • Closes WebSocket connections
  • Stops the HTTP server
  • Releases allocated ports
  • Ensures all pending operations complete

Custom server providers should implement proper cleanup:

class CustomServerProvider extends BaseServerProvider {
protected async stopServer(): Promise<void> {
// Close all connections
await this.closeConnections();

// Stop the server
if (this.server) {
await new Promise((resolve) => {
this.server.close(resolve);
});
}

// Clean up any custom resources
await this.cleanupCustomResources();
}
}

Shutdown Order Matters

Resources are cleaned up in a specific order to prevent issues:

  1. Server first - Stop accepting new work
  2. Workflows second - Suspend active operations
  3. Telemetry last - Ensure all events are recorded

This order ensures that:

  • No new requests arrive during cleanup
  • Active operations can complete or suspend gracefully
  • All telemetry data is captured before shutdown

Best Practices

  1. Use the Official Implementation: Start with @voltagent/server-hono unless you have specific requirements
  2. Leverage configureApp: Add custom routes and middleware through the configuration
  3. Follow Route Definitions: Use the standardized route definitions for consistency
  4. Handle Errors Properly: Implement proper error handling in custom providers
  5. Support Graceful Shutdown: Always clean up resources properly
  6. Test Shutdown Behavior: Verify your application exits cleanly in different scenarios
  7. Use shutdown() in Tests: Ensure proper cleanup in test suites

Next Steps

Table of Contents