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
docker run -d -p 7337:7337 \
-e ANTHROPIC_API_KEY=sk-ant-... \
-v ~/.seepient:/root/.seepient \
seepient-serverWith multiple providers
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-serverWith custom session directory
docker run -d -p 7337:7337 \
-e ANTHROPIC_API_KEY=sk-ant-... \
-e SEEPIENT_SESSION_DIR=/data/sessions \
-v session-data:/data/sessions \
seepient-serverDocker Compose
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: 3Google Cloud Run
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
pingmessage 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_DIRenvironment variable to point to a mounted volume, or externalize session storage with Redis.
Cloud Run with secrets
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
# Install
npm install -g seepient
# Run with environment
ANTHROPIC_API_KEY=sk-ant-... seepient serverProgrammatic
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:
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:
server.dispose(); // un-registers handlers + closes the server
// or simply:
server.close(); // close event detaches the signal handlers as wellMultiple 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)
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 startupProvider environment variables
| Variable | Description | Required |
|---|---|---|
OPENAI_API_KEY | OpenAI API key | For OpenAI provider |
OPENAI_MODEL | Default OpenAI model (default: gpt-5.4) | No |
ANTHROPIC_API_KEY | Anthropic API key | For Anthropic provider |
ANTHROPIC_MODEL | Default Anthropic model (default: claude-sonnet-4-6-20260320) | No |
GLM_API_KEY | GLM API key | For GLM provider |
GLM_MODEL | Default GLM model (default: glm-5.1) | No |
OPENAI_COMPAT_API_KEY | API key for OpenAI-compatible provider | For compatible provider |
OPENAI_COMPAT_BASE_URL | Base URL for OpenAI-compatible provider | For compatible provider |
LLM_MODEL | Default model for OpenAI-compatible provider (default: gpt-5.4) | No |
LLM_PROVIDER | Default provider (auto-detected if not set) | No |
SEEPIENT_SKILLS_PATH | Colon-separated paths to skill directories | No |
SEEPIENT_MAX_BODY_BYTES | Request body size cap in bytes across all REST routes — chat, settings, gateway, and provider management (default: 10485760). Set to 0 for an unlimited body size | No |
SEEPIENT_CORS_ORIGINS | Comma-separated CORS origin allowlist, or * to reflect any origin (default: no CORS headers at all) | No |
SEEPIENT_WS_MAX_CONNECTIONS_PER_KEY | Per-key WebSocket connection cap (default: 50); dead peers are terminated by a 30s heartbeat sweep and release their slot | No |
SEEPIENT_RATE_LIMIT_RPM | Per-key requests-per-minute cap for REST and WebSocket traffic (default: 300). Set to 0 to disable | No |
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
| Code | HTTP Status | Retryable | Description |
|---|---|---|---|
UNAUTHORIZED | 401 | No | Invalid or missing API key |
FORBIDDEN | 403 | No | API key lacks required scope |
BAD_REQUEST | 400 | No | Invalid request body or missing fields |
NOT_FOUND | 404 | No | Endpoint or session not found |
PROVIDER_ERROR | 502 | Yes | LLM provider returned an error |
GENERATION_ERROR | 500 | Yes | Text generation failed |
INTERNAL_ERROR | 500 | No | Unexpected server error |
WebSocket error codes
| Code | Retryable | Description |
|---|---|---|
UNAUTHORIZED | No | Authentication failed on upgrade |
INVALID_MESSAGE | No | Malformed JSON |
UNKNOWN_MESSAGE_TYPE | No | Unrecognized message type |
PROVIDER_ERROR | Yes | LLM provider error |
STREAM_ERROR | No | Internal streaming failure |
SESSION_NOT_FOUND | No | Session expired or missing |
ABORTED | No | Request 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):
- Stops accepting new connections
- Closes all active WebSocket connections with code
1001 - Stops the session cleanup timer
- Waits up to 5 seconds for in-flight requests to complete
- Force exits if drain exceeds the timeout
Health monitoring
Use the /v1/health endpoint for load balancer health checks:
curl -f http://localhost:7337/v1/health || exit 1Response:
{
"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.jsonpermissions are0600 - [ ] Mount a persistent volume for
./.seepient/sessions/if using sessions - [ ] Configure health check against
/v1/health - [ ] Set
SEEPIENT_SESSION_TTLappropriate 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