Production Deployment

View as MarkdownOpen in Claude

Production checklist

Security

  • HTTPS enabled with valid certificates
  • SWML_BASIC_AUTH_PASSWORD configured (username defaults to signalwire)
  • Firewall rules in place
  • No secrets in code or logs (SDK masks credentials in startup output automatically)

Reliability

  • Process manager (systemd/supervisor)
  • Health checks configured
  • Logging to persistent storage
  • Error monitoring/alerting

Performance

  • Multiple workers for concurrency
  • Reverse proxy (nginx) for SSL termination
  • Load balancing if needed

Environment variables

$## Authentication (password required; username defaults to 'signalwire')
$export SWML_BASIC_AUTH_PASSWORD="your-secure-password"
$# export SWML_BASIC_AUTH_USER="signalwire" # optional, defaults to 'signalwire'
$
$## SSL Configuration
$export SWML_SSL_ENABLED="true"
$export SWML_SSL_CERT_PATH="/etc/ssl/certs/agent.crt"
$export SWML_SSL_KEY_PATH="/etc/ssl/private/agent.key"
$
$## Domain configuration
$export SWML_DOMAIN="agent.example.com"
$
$## Proxy URL (if behind load balancer/reverse proxy)
$export SWML_PROXY_URL_BASE="https://agent.example.com"
$# APP_URL is accepted as a fallback for SWML_PROXY_URL_BASE

Running in production

Production deployment differs significantly by language. Each SDK provides its own HTTP server or integrates with language-specific production servers.

Use uvicorn with multiple workers:

$## Run with 4 workers
$uvicorn my_agent:app --host 0.0.0.0 --port 3000 --workers 4

Create an entry point module:

1#!/usr/bin/env python3
2# my_agent.py
3from signalwire import AgentBase
4
5class MyAgent(AgentBase):
6 def __init__(self):
7 super().__init__(name="my-agent")
8 self.add_language("English", "en-US", "rime.spore")
9 self.prompt_add_section("Role", "You are a helpful assistant.")
10
11if __name__ == "__main__":
12 agent = MyAgent()
13 agent.run(host="0.0.0.0", port=3000)

Systemd service

Create /etc/systemd/system/signalwire-agent.service. Adjust ExecStart for your language:

LanguageExecStart Example
Python/opt/agent/venv/bin/uvicorn app:app --host 127.0.0.1 --port 3000 --workers 4
TypeScript/usr/bin/node /opt/agent/app.js
1[Unit]
2Description=SignalWire AI Agent
3After=network.target
4
5[Service]
6Type=simple
7User=www-data
8Group=www-data
9WorkingDirectory=/opt/agent
10Environment="SWML_BASIC_AUTH_USER=your-username"
11Environment="SWML_BASIC_AUTH_PASSWORD=your-password"
12ExecStart=/opt/agent/venv/bin/uvicorn app:app --host 127.0.0.1 --port 3000 --workers 4
13Restart=always
14RestartSec=5
15
16[Install]
17WantedBy=multi-user.target

Enable and start:

$sudo systemctl enable signalwire-agent
$sudo systemctl start signalwire-agent
$sudo systemctl status signalwire-agent

Nginx reverse proxy

1## /etc/nginx/sites-available/agent
2server {
3 listen 443 ssl http2;
4 server_name agent.example.com;
5
6 ssl_certificate /etc/ssl/certs/agent.crt;
7 ssl_certificate_key /etc/ssl/private/agent.key;
8
9 location / {
10 proxy_pass http://127.0.0.1:3000;
11 proxy_http_version 1.1;
12 proxy_set_header Host $host;
13 proxy_set_header X-Real-IP $remote_addr;
14 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
15 proxy_set_header X-Forwarded-Proto $scheme;
16 proxy_set_header X-Forwarded-Host $host;
17 proxy_read_timeout 300s;
18 proxy_connect_timeout 75s;
19 }
20}
21
22server {
23 listen 80;
24 server_name agent.example.com;
25 return 301 https://$server_name$request_uri;
26}

Enable the site:

$sudo ln -s /etc/nginx/sites-available/agent /etc/nginx/sites-enabled/
$sudo nginx -t
$sudo systemctl reload nginx

Production architecture

Production architecture diagram showing nginx, uvicorn workers, and SignalWire Cloud.
Production Architecture

SSL configuration

Using environment variables

$export SWML_SSL_ENABLED="true"
$export SWML_SSL_CERT_PATH="/path/to/cert.pem"
$export SWML_SSL_KEY_PATH="/path/to/key.pem"

Let’s Encrypt with Certbot

$## Install certbot
$sudo apt install certbot python3-certbot-nginx
$
$## Get certificate
$sudo certbot --nginx -d agent.example.com
$
$## Auto-renewal is configured automatically

Health checks

For AgentServer deployments:

$## Health check endpoint
$curl https://agent.example.com/health

Response:

1{
2 "status": "ok",
3 "agents": 1,
4 "routes": ["/"]
5}

For load balancers, use this endpoint to verify agent availability.

Logging configuration

1import logging
2
3## Configure logging for production
4logging.basicConfig(
5 level=logging.INFO,
6 format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
7 handlers=[
8 logging.FileHandler('/var/log/agent/agent.log'),
9 logging.StreamHandler()
10 ]
11)

Or use environment variable:

$export SIGNALWIRE_LOG_MODE=default

Monitoring

Prometheus metrics

Add custom metrics to your agent:

1from prometheus_client import Counter, Histogram, start_http_server
2
3## Start metrics server on port 9090
4start_http_server(9090)
5
6## Define metrics
7call_counter = Counter('agent_calls_total', 'Total calls handled')
8call_duration = Histogram('agent_call_duration_seconds', 'Call duration')

External monitoring

  • Uptime monitoring: Monitor the health endpoint
  • Log aggregation: Ship logs to ELK, Datadog, or similar
  • APM: Use Application Performance Monitoring tools

Scaling considerations

Vertical scaling

LanguageScaling Approach
PythonIncrease uvicorn workers (--workers N)

| TypeScript | Increase PM2 instances (pm2 scale agent 8) |

  • Use larger server instances
  • Optimize agent code and external calls

Horizontal scaling

  • Multiple server instances behind load balancer
  • Stateless agent design
  • Shared session storage (Redis) if needed

Serverless

  • Auto-scaling with Lambda/Cloud Functions
  • Pay per invocation
  • No server management

Built-in security features

The SDK includes several security hardening features enabled by default.

Security headers

All HTTP responses automatically include security headers:

HeaderValuePurpose
X-Content-Type-OptionsnosniffPrevent MIME-type sniffing
X-Frame-OptionsDENYPrevent clickjacking
Referrer-Policystrict-origin-when-cross-originLimit referrer information
Strict-Transport-Securitymax-age=31536000; includeSubDomainsForce HTTPS (when SSL enabled)

No configuration is needed — these headers are added automatically by middleware on AgentBase, AgentServer, and SWMLService.

SSRF protection

DataMap HTTP requests, skill remote URLs, and MCP gateway URLs are validated against private IP ranges to prevent Server-Side Request Forgery attacks. By default, requests to internal networks (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8, 169.254.0.0/16, and IPv6 equivalents) are blocked.

To allow private URLs (e.g., when your backend services are on a private network):

$export SWML_ALLOW_PRIVATE_URLS=true

Timing-safe authentication

Basic auth credential comparison uses hmac.compare_digest() to prevent timing side-channel attacks. SWAIG token validation also uses constant-time comparison.

Credential masking

Startup log output shows (credentials configured) instead of the actual password:

Agent 'my-agent' is available at:
URL: http://0.0.0.0:3000
Basic Auth: signalwire:(credentials configured) (source: environment)

Default authentication username

SWML_BASIC_AUTH_USER defaults to signalwire when not explicitly set. You only need to configure SWML_BASIC_AUTH_PASSWORD:

$export SWML_BASIC_AUTH_PASSWORD="your-secure-password"
$# Username defaults to 'signalwire' — no need to set SWML_BASIC_AUTH_USER

Proxy header validation

X-Forwarded headers (X-Forwarded-Host, X-Forwarded-Proto) are only trusted when explicitly configured. Set SWML_TRUST_PROXY_HEADERS=true if your agent runs behind a reverse proxy and you want the SDK to auto-detect the public URL from forwarded headers:

$export SWML_TRUST_PROXY_HEADERS=true

When SWML_PROXY_URL_BASE is set via environment variable, proxy headers are automatically trusted for URL construction.

Security best practices

DO:

  • Use HTTPS everywhere
  • Set strong basic auth credentials
  • Use environment variables for secrets
  • Enable firewall and limit access
  • Regularly update dependencies
  • Monitor for suspicious activity

DON’T:

  • Expose debug endpoints in production
  • Log sensitive data
  • Use default credentials
  • Disable SSL verification
  • Run as root user