3 built-in C servers, 19 endpoints, JWT auth, SSE streaming, CGI execution, WebSocket bridging — all from source, zero dependencies.
scorpiox code ships 3 HTTP/WebSocket servers written in pure C — no nginx, no Node, no external runtime. scorpiox-server is the primary application server executing Python CGI scripts with JWT authentication. scorpiox-host manages AI coding sessions via REST API. scorpiox-ws2tcp bridges WebSocket connections to TCP backends for noVNC.
Built-in HTTP server — executes Python scripts as CGI routes with JWT auth, SSE streaming, and git deploy.
Session gateway server — manages AI coding sessions, events, and messages via REST API.
WebSocket to TCP bridge — replaces Python websockify for noVNC deployments with static file serving.
| Method | Path | Description | Auth |
|---|---|---|---|
| * (any) | CORS preflight — returns 204 with permissive Access-Control headers for any path | ✗ | |
| GET | /api/ping | Health check — returns ‘ok’ as plain text | ✗ |
| GET | /api/otp?a=&s= | Generate TOTP code — invokes scorpiox-otp CLI with sanitized inputs, returns JSON | ✗ |
| GET | {prefix}{name} | Execute {name}.py from script dirs — query params passed as env vars, CGI headers parsed from output | JWT⚙ |
| POST | {prefix}{name} | Execute {name}.py with POST body — body written to temp file (POST_BODY_FILE), large bodies (>512KB) streamed to stdin | JWT⚙ |
| GET | {prefix} | Default route — when name is empty, serves index.py from script dirs | JWT⚙ |
| * | * (no match) | Catch-all fallback — when no script matches and _fallback.py exists, invokes it with original path | JWT⚙ |
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /health | Server health check — returns {"status":"ok"} | ✗ |
| GET | /status | Server summary — returns version, port, uptime, session count, active count as JSON | ✗ |
| GET | /sessions | List all sessions — re-discovers sessions from .scorpiox/sessions dir, returns JSON array | ✗ |
| GET | /sessions/:id | Session detail — returns full session info including state, event_count, message_count | ✗ |
| GET | /sessions/:id/events | Get session events — returns all buffered events as NDJSON | ✗ |
| POST | /sessions/:id/events | Fire event for session — creates/updates session, pushes event to ring buffer | ✗ |
| POST | /sessions/:id/register | Register a session — creates or updates session with name, sets state to active | ✗ |
| GET | /sessions/:id/messages | Pull queued messages — returns and drains the message queue for a session | ✗ |
| POST | /sessions/:id/messages | Push message to session — queues a message for the session (max 32 queued) | ✗ |
| POST | /stop | Graceful shutdown — stops the host server | ✗ |
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /* | Static file serving — serves files from webroot directory with MIME type detection | ✗ |
| WS | / (WebSocket upgrade) | WebSocket upgrade — performs Sec-WebSocket-Accept handshake then bridges binary frames to TCP backend | token⚙ |
All middleware is compiled into scorpiox-server. No plugins, no dynamic loading.
Permissive CORS on every response — Access-Control-Allow-Origin: *, all methods, all headers, credentials allowed, 86400s max-age.
JWT HMAC-SHA256 authentication — extracts token from Authorization: Bearer header or configurable cookie, validates signature/exp/iss/aud, maps custom claims to env vars, protects configurable route prefixes, redirects browsers to login URL on 401.
Full CGI environment — sets REQUEST_METHOD, CONTENT_TYPE, CONTENT_LENGTH, QUERY_STRING, PATH_INFO, HTTP_COOKIE, HTTP_AUTHORIZATION, and all HTTP_* headers as env vars for scripts.
Large body streaming — POST bodies >512KB are streamed directly to script stdin instead of buffering, with chunked transfer encoding support.
Request/response size limits — configurable max request (SERVER_MAX_REQUEST_MB, default 200MB) and max response (SERVER_MAX_RESPONSE_MB, default 200MB).
Script idle timeout — kills scripts that produce no stdout for SERVER_SCRIPT_TIMEOUT seconds (default 300s).
Python CGI execution engine — scripts output optional CGI headers (Status, Content-Type, Location, Set-Cookie) followed by blank line then body. SSE support via Content-Type: text/event-stream.
Script resolution: searches SERVER_SCRIPT_DIR directories in order, first dir containing {name}.py wins.
| Variable | Description |
|---|---|
| REQUEST_METHOD | HTTP method (GET, POST, etc.) |
| CONTENT_TYPE | Request Content-Type header value |
| CONTENT_LENGTH | Request body size in bytes |
| QUERY_STRING | Raw query string after ? |
| PATH_INFO | Matched path portion of the URL |
| HTTP_COOKIE | Cookie header value |
| HTTP_AUTHORIZATION | Authorization header value |
| POST_BODY_FILE | Temp file path containing POST body (<512KB) |
| SX_STREAMING | Set to "1" when SSE streaming is active |
| X_JWT_RAW | Raw JWT token if auth is enabled |
| X_JWT_SUB | JWT subject claim value |
| X_JWT_AUTH | Set to "1" if JWT validated successfully |
| HTTP_* | All request headers prefixed with HTTP_ |
#!/usr/bin/env python3 # hello.py — minimal CGI example import os name = os.environ.get('name', 'world') print("Content-Type: text/html") print() print(f"<h1>Hello, {name}!</h1>")
#!/usr/bin/env python3 # stream.py — SSE streaming CGI import sys, time print("Content-Type: text/event-stream") print() for i in range(10): print(f"data: event {i} ") sys.stdout.flush() time.sleep(1)
WebSocket-to-TCP bridge server — performs RFC 6455 handshake, bridges binary WebSocket frames to backend TCP targets. Used for noVNC/VNC proxying. Includes static file serving for web client assets.
# Bridge ws://localhost:6080 → tcp://localhost:5900 (VNC) scorpiox-ws2tcp --port 6080 --target localhost:5900 --webroot /opt/novnc
All config via scorpiox-env.txt environment variables. No YAML, no TOML, no config files to parse.
| Key | Description | Default |
|---|---|---|
| SERVER_PORT | HTTP listen port | 8080 |
| SERVER_SCRIPT_DIR | Colon-separated list of directories to search for .py scripts | . |
| SERVER_PREFIX | URL prefix for script routes (e.g. /app/) | / |
| SERVER_JWT_SECRET | HMAC-SHA256 secret for JWT validation | — |
| SERVER_JWT_PROTECT | Comma-separated route prefixes requiring JWT auth | — |
| SERVER_JWT_COOKIE | Cookie name to extract JWT from (alternative to Bearer header) | — |
| SERVER_JWT_LOGIN_URL | Redirect URL for browser 401 responses | — |
| SERVER_MAX_REQUEST_MB | Maximum request body size in megabytes | 200 |
| SERVER_MAX_RESPONSE_MB | Maximum response body size in megabytes | 200 |
| SERVER_SCRIPT_TIMEOUT | Idle timeout in seconds — kills scripts with no stdout activity | 300 |
| SERVER_GIT_CACHE_DIR | Git deploy: local clone directory for auto-pull | — |
| SERVER_GIT_POLL_INTERVAL | Git deploy: poll interval in seconds for change detection | — |
| SERVER_GIT_PAT | Git deploy: personal access token for repo authentication | — |
# HTTP server configuration SERVER_PORT=8080 SERVER_SCRIPT_DIR=/opt/website:/opt/api SERVER_PREFIX=/ # JWT authentication SERVER_JWT_SECRET=your-hmac-secret-here SERVER_JWT_PROTECT=/admin,/api/private SERVER_JWT_COOKIE=sx_token SERVER_JWT_LOGIN_URL=https://login.scorpiox.net/ # Git deploy (auto-pull website repo) SERVER_GIT_CACHE_DIR=/var/cache/scorpiox-site SERVER_GIT_POLL_INTERVAL=60
Git deploy mode — clones a git repo, polls for changes, auto-pulls and hot-reloads script directory. Supports PAT auth. scorpiox-server becomes a self-updating web application server with zero downtime deploys.
# 1. scorpiox-server starts, reads SERVER_GIT_CACHE_DIR # 2. Clones repo into cache dir (with PAT if set) # 3. Adds cache dir to SERVER_SCRIPT_DIR search path # 4. Background thread polls every SERVER_GIT_POLL_INTERVAL seconds # 5. On change: git pull, scripts are immediately available # 6. No restart needed — new requests use updated scripts