Skip to main content

Cgi Mode

CGI Mode

Summary: Deploy agents as CGI scripts on traditional web servers like Apache or nginx. The SDK automatically detects CGI environments and handles requests appropriately.

CGI Overview

CGI (Common Gateway Interface) allows web servers to execute scripts and return their output as HTTP responses.

Benefits:

  • Works with shared hosting
  • Simple deployment - just upload files
  • No separate process management
  • Compatible with Apache, nginx

Drawbacks:

  • New process per request (slower)
  • No persistent connections
  • Limited scalability

CGI Detection

The SDK detects CGI mode via the GATEWAY_INTERFACE environment variable:

## Automatic detection
if os.getenv('GATEWAY_INTERFACE'):
# CGI mode detected
mode = 'cgi'

Basic CGI Script

#!/usr/bin/env python3
## agent.py - Basic CGI agent script
from signalwire_agents import AgentBase


class MyAgent(AgentBase):
def __init__(self):
super().__init__(name="my-agent")
self.add_language("English", "en-US", "rime.spore")
self.prompt_add_section("Role", "You are a helpful assistant.")


if __name__ == "__main__":
agent = MyAgent()
agent.run() # Automatically detects CGI mode

Make it executable:

chmod +x agent.py

CGI Request Flow

┌─────────────────────────────────────────────────────────────────────────────┐
│ CGI Request Flow │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ SignalWire │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Web Server │ Apache/nginx │
│ │ (httpd) │ │
│ └──────────────────┘ │
│ │ │
│ │ Sets environment variables: │
│ │ • GATEWAY_INTERFACE=CGI/1.1 │
│ │ • PATH_INFO=/swaig │
│ │ • CONTENT_LENGTH=... │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Python CGI │ agent.py │
│ │ Script │ │
│ └──────────────────┘ │
│ │ │
│ │ PATH_INFO="" → Return SWML document │
│ │ PATH_INFO=/swaig → Execute SWAIG function │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ stdout │ JSON response to web server │
│ └──────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

Apache Configuration

Enable CGI

## Enable CGI module
LoadModule cgi_module modules/mod_cgi.so

## Configure CGI directory
<Directory "/var/www/cgi-bin">
Options +ExecCGI
AddHandler cgi-script .py
Require all granted
</Directory>

Virtual Host Configuration

<VirtualHost *:443>
ServerName agent.example.com

SSLEngine on
SSLCertificateFile /etc/ssl/certs/agent.crt
SSLCertificateKeyFile /etc/ssl/private/agent.key

ScriptAlias / /var/www/cgi-bin/agent.py

<Directory "/var/www/cgi-bin">
Options +ExecCGI
SetHandler cgi-script
Require all granted
</Directory>

# Set environment variables
SetEnv SWML_BASIC_AUTH_USER "myuser"
SetEnv SWML_BASIC_AUTH_PASSWORD "mypassword"
</VirtualHost>

nginx Configuration

nginx doesn't natively support CGI, but you can use FastCGI with fcgiwrap:

server {
listen 443 ssl;
server_name agent.example.com;

ssl_certificate /etc/ssl/certs/agent.crt;
ssl_certificate_key /etc/ssl/private/agent.key;

location / {
fastcgi_pass unix:/var/run/fcgiwrap.socket;
fastcgi_param SCRIPT_FILENAME /var/www/cgi-bin/agent.py;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param PATH_INFO $uri;
fastcgi_param SWML_BASIC_AUTH_USER "myuser";
fastcgi_param SWML_BASIC_AUTH_PASSWORD "mypassword";
include fastcgi_params;
}
}

CGI Host Configuration

In CGI mode, the SDK needs to know the external hostname for generating URLs:

## Using swaig-test to simulate CGI mode
swaig-test my_agent.py --simulate-serverless cgi --cgi-host agent.example.com

Or set environment variable:

SetEnv SWML_PROXY_URL_BASE "https://agent.example.com"

Testing CGI Locally

Use swaig-test to simulate CGI environment:

## Test SWML generation in CGI mode
swaig-test my_agent.py --simulate-serverless cgi --dump-swml

## With custom host
swaig-test my_agent.py --simulate-serverless cgi --cgi-host mysite.com --dump-swml

## Test a function
swaig-test my_agent.py --simulate-serverless cgi --exec function_name --param value

Authentication in CGI Mode

The SDK checks basic auth in CGI mode:

## Authentication is automatic when these are set
## SWML_BASIC_AUTH_USER
## SWML_BASIC_AUTH_PASSWORD

## The SDK reads Authorization header and validates

If authentication fails, returns 401 with WWW-Authenticate header.

Directory Structure

/var/www/cgi-bin/
├── agent.py # Main CGI script
├── requirements.txt # Dependencies
└── venv/ # Virtual environment (optional)

Shared Hosting Deployment

For shared hosting where you can't install system packages:

#!/usr/bin/env python3
## agent_shared.py - CGI agent for shared hosting
import sys
import os

## Add local packages directory
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'packages'))

from signalwire_agents import AgentBase


class MyAgent(AgentBase):
def __init__(self):
super().__init__(name="my-agent")
self.add_language("English", "en-US", "rime.spore")


if __name__ == "__main__":
agent = MyAgent()
agent.run()

Install packages locally:

pip install --target=./packages signalwire-agents

CGI Best Practices

Performance

  • Keep imports minimal - each request starts fresh
  • Consider FastCGI for better performance
  • Cache what you can (but remember process dies)

Security

  • Set proper file permissions (750 or 755)
  • Don't expose .py files directly if possible
  • Use HTTPS always
  • Set auth credentials as environment variables

Debugging

  • Check web server error logs
  • Verify shebang line (#!/usr/bin/env python3)
  • Test script from command line first
  • Ensure proper line endings (LF, not CRLF)

Common CGI Issues

IssueSolution
500 Internal Server ErrorCheck error logs, verify permissions
Permission deniedchmod +x agent.py
Module not foundCheck sys.path, install dependencies
Wrong Python versionUpdate shebang to correct Python
Malformed headersEnsure proper Content-Type output
TimeoutOptimize code, increase server timeout

Migration from CGI

When you outgrow CGI:

CGI → FastCGI

Keep same code, use fcgiwrap or gunicorn. Better performance, persistent processes.

CGI → Server Mode

Same code works - just run differently (python agent.py instead of CGI). Add systemd service, nginx reverse proxy.

CGI → Serverless

Same code works with minor changes. Add Lambda handler wrapper. Deploy to AWS/GCP/Azure.