Skip to content

Deployment

Seepient Agent Server is a stateless Node.js process that can be deployed as a Docker container, on Cloud Run, or directly on any Node.js host.

Docker

Build and run

bash
docker run -d -p 7337:7337 \
  -e ANTHROPIC_API_KEY=sk-ant-... \
  -v ~/.seepient:/root/.seepient \
  seepient-server

With multiple providers

bash
docker run -d -p 7337:7337 \
  -e OPENAI_API_KEY=sk-... \
  -e ANTHROPIC_API_KEY=sk-ant-... \
  -e GLM_API_KEY=... \
  -e LLM_PROVIDER=anthropic \
  -v ~/.seepient:/root/.seepient \
  seepient-server

With custom session directory

bash
docker run -d -p 7337:7337 \
  -e ANTHROPIC_API_KEY=sk-ant-... \
  -e SEEPIENT_SESSION_DIR=/data/sessions \
  -v session-data:/data/sessions \
  seepient-server

Docker Compose

yaml
services:
  seepient:
    image: seepient-server
    build: .
    ports:
      - "7337:7337"
    environment:
      - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - LLM_PROVIDER=anthropic
      - SEEPIENT_SESSION_TTL=86400
    volumes:
      - ./data/.seepient:/root/.seepient
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:7337/v1/health"]
      interval: 30s
      timeout: 5s
      retries: 3

Google Cloud Run

bash
gcloud run deploy seepient \
  --image seepient-server \
  --port 7337 \
  --min-instances 1 \
  --max-instances 10 \
  --timeout 3600 \
  --set-env-vars "ANTHROPIC_API_KEY=sk-ant-..."

Cloud Run WebSocket considerations

Cloud Run has WebSocket limitations

  • Heartbeat: Send a ping message every 30 seconds to keep the connection alive. Cloud Run may close idle connections.
  • Request timeout: Cloud Run has a maximum request duration of 60 minutes. Long-running conversations should use the reconnection protocol.
  • Session externalization: File-based sessions do not persist across Cloud Run instances. Use the SEEPIENT_SESSION_DIR environment variable to point to a mounted volume, or externalize session storage with Redis.

Cloud Run with secrets

bash
gcloud run deploy seepient \
  --image seepient-server \
  --port 7337 \
  --min-instances 1 \
  --max-instances 10 \
  --timeout 3600 \
  --set-secrets "ANTHROPIC_API_KEY=anthropic-key:latest"

Bare metal / Node.js

Direct Node.js

bash
# Install
npm install -g seepient

# Run with environment
ANTHROPIC_API_KEY=sk-ant-... seepient server

Programmatic

typescript
import { runSeepientServer } from "seepient/server";

const server = await runSeepientServer({
  port: 7337,
  host: "0.0.0.0",
  cors: true,
  sessionTTL: 86400,
});

Embedding the server in another process

runSeepientServer() is designed to be embedded inside a host application (an existing Express/Fastify app's process, a worker, a control plane).

Return value. It returns the Node.js http.Server (typed as SeepientHttpServer, i.e. http.Server & { dispose(): void }). You may attach your own listeners to it, call server.address(), close it, etc.

listen: false. Pass listen: false to receive the configured http.Server without it listening — for example to mount the server on your own host/port, or behind a router you control:

typescript
import { runSeepientServer } from "seepient/server";

const server = await runSeepientServer({ listen: false });
await new Promise<void>((resolve) => server.listen(8080, "127.0.0.1", resolve));

Signal handlers. When the server listens, runSeepientServer registers SIGINT/SIGTERM handlers that close the server and force-exit after a 5-second drain. When listen: false is used, no signal handlers are registered — the embedder owns process lifecycle entirely. Repeated construction never accumulates listeners.

Dispose. The returned server carries a dispose() handle for full teardown: it un-registers the signal handlers runSeepientServer registered for that instance AND closes the server (which also detaches its WebSocket layer). Note that server.close() alone is already enough — the close event removes the signal handlers too:

typescript
server.dispose();   // un-registers handlers + closes the server
// or simply:
server.close();     // close event detaches the signal handlers as well

Multiple servers per process. Each runSeepientServer() call creates a fully independent HTTP + WebSocket stack (per-instance WebSocket server, connection registry, approval store). Closing one server never closes another server's connections. One caveat: default on-disk state (sessions under ./.seepient/sessions, the local audit store, durable approvals under ~/.seepient) is process-wide by default — when embedding multiple servers, inject per-server persist, auditStore, policyStore, and capabilityLedger contracts to keep their state separated (see Stateless workers).

Process manager (PM2)

bash
npm install -g pm2 seepient

# Start with PM2
ANTHROPIC_API_KEY=sk-ant-... pm2 start "seepient server" --name seepient

# Save for auto-restart
pm2 save
pm2 startup

Provider environment variables

VariableDescriptionRequired
OPENAI_API_KEYOpenAI API keyFor OpenAI provider
OPENAI_MODELDefault OpenAI model (default: gpt-5.4)No
ANTHROPIC_API_KEYAnthropic API keyFor Anthropic provider
ANTHROPIC_MODELDefault Anthropic model (default: claude-sonnet-4-6-20260320)No
GLM_API_KEYGLM API keyFor GLM provider
GLM_MODELDefault GLM model (default: glm-5.1)No
OPENAI_COMPAT_API_KEYAPI key for OpenAI-compatible providerFor compatible provider
OPENAI_COMPAT_BASE_URLBase URL for OpenAI-compatible providerFor compatible provider
LLM_MODELDefault model for OpenAI-compatible provider (default: gpt-5.4)No
LLM_PROVIDERDefault provider (auto-detected if not set)No
SEEPIENT_SKILLS_PATHColon-separated paths to skill directoriesNo
SEEPIENT_MAX_BODY_BYTESRequest body size cap in bytes across all REST routes — chat, settings, gateway, and provider management (default: 10485760). Set to 0 for an unlimited body sizeNo
SEEPIENT_CORS_ORIGINSComma-separated CORS origin allowlist, or * to reflect any origin (default: no CORS headers at all)No
SEEPIENT_WS_MAX_CONNECTIONS_PER_KEYPer-key WebSocket connection cap (default: 50); dead peers are terminated by a 30s heartbeat sweep and release their slotNo
SEEPIENT_RATE_LIMIT_RPMPer-key requests-per-minute cap for REST and WebSocket traffic (default: 300). Set to 0 to disableNo

Provider auto-detection

If LLM_PROVIDER is not set, the server uses the first configured provider. If OPENAI_API_KEY is set, OpenAI becomes the default. Otherwise, the first provider with a configured API key is used.

Error codes

REST error codes

CodeHTTP StatusRetryableDescription
UNAUTHORIZED401NoInvalid or missing API key
FORBIDDEN403NoAPI key lacks required scope
BAD_REQUEST400NoInvalid request body or missing fields
NOT_FOUND404NoEndpoint or session not found
PROVIDER_ERROR502YesLLM provider returned an error
GENERATION_ERROR500YesText generation failed
INTERNAL_ERROR500NoUnexpected server error

WebSocket error codes

CodeRetryableDescription
UNAUTHORIZEDNoAuthentication failed on upgrade
INVALID_MESSAGENoMalformed JSON
UNKNOWN_MESSAGE_TYPENoUnrecognized message type
PROVIDER_ERRORYesLLM provider error
STREAM_ERRORNoInternal streaming failure
SESSION_NOT_FOUNDNoSession expired or missing
ABORTEDNoRequest cancelled by client

Retry strategy

For retryable errors (PROVIDER_ERROR, GENERATION_ERROR):

Attempt 1 ──► wait 1s ──► Attempt 2 ──► wait 2s ──► Attempt 3 ──► fail
  • Maximum 3 retries
  • Exponential backoff: 1s, 2s, 4s
  • Do not retry non-retryable errors

Graceful shutdown

When runSeepientServer() starts a listening server it handles SIGINT and SIGTERM with a 5-second drain timeout (listen: false servers register no signal handlers — the embedder owns shutdown):

  1. Stops accepting new connections
  2. Closes all active WebSocket connections with code 1001
  3. Stops the session cleanup timer
  4. Waits up to 5 seconds for in-flight requests to complete
  5. Force exits if drain exceeds the timeout

Health monitoring

Use the /v1/health endpoint for load balancer health checks:

bash
curl -f http://localhost:7337/v1/health || exit 1

Response:

json
{
  "status": "ok",
  "version": "0.1.1",
  "uptime": 3600
}

Production checklist

  • [ ] Set at least one provider API key via environment variable
  • [ ] Generate API keys with minimal required scopes
  • [ ] Verify ~/.seepient/server-keys.json permissions are 0600
  • [ ] Mount a persistent volume for ./.seepient/sessions/ if using sessions
  • [ ] Configure health check against /v1/health
  • [ ] Set SEEPIENT_SESSION_TTL appropriate for your use case
  • [ ] Enable WebSocket heartbeat (ping/pong every 30s) for Cloud Run deployments
  • [ ] Configure reverse proxy (nginx, Cloud Load Balancer) with WebSocket upgrade support
  • [ ] Set up log aggregation for [server] and [ws] log prefixes

Released under the Business Source License 1.1.