feat: add gunicorn + gevent production server via start.sh

Add start.sh as the recommended production entry point with:
- gunicorn + gevent worker for concurrent SSE/WebSocket handling
- CLI flags for port, host, debug, HTTPS, and dependency checks
- Auto-fallback to Flask dev server if gunicorn not installed
- Conditional gevent monkey-patch in app.py via INTERCEPT_USE_GEVENT env var
- Docker CMD updated to use start.sh
- Updated all docs, setup.sh, and requirements.txt accordingly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Smittix
2026-02-28 17:18:40 +00:00
parent 155cb72597
commit 7f7e5c0fea
16 changed files with 249 additions and 56 deletions
+2
View File
@@ -15,11 +15,13 @@ All notable changes to iNTERCEPT will be documented in this file.
- **Multi-SDR WeFax** - Multiple SDR hardware support for WeFax decoder - **Multi-SDR WeFax** - Multiple SDR hardware support for WeFax decoder
- **Tool Path Overrides** - `INTERCEPT_*_PATH` environment variables for custom tool locations - **Tool Path Overrides** - `INTERCEPT_*_PATH` environment variables for custom tool locations
- **Homebrew Tool Detection** - Native path detection for Apple Silicon Homebrew installations - **Homebrew Tool Detection** - Native path detection for Apple Silicon Homebrew installations
- **Production Server** - `start.sh` with gunicorn + gevent for concurrent SSE/WebSocket handling — eliminates multi-client page load delays
### Changed ### Changed
- Morse decoder rebuilt with custom Goertzel decoder, replacing multimon-ng dependency - Morse decoder rebuilt with custom Goertzel decoder, replacing multimon-ng dependency
- GPS mode upgraded to textured 3D globe visualization - GPS mode upgraded to textured 3D globe visualization
- Destroy lifecycle added to all mode modules to prevent resource leaks - Destroy lifecycle added to all mode modules to prevent resource leaks
- Docker container now uses gunicorn + gevent by default via `start.sh`
### Fixed ### Fixed
- ADS-B device release leak and startup performance regression - ADS-B device release leak and startup performance regression
+9 -9
View File
@@ -28,12 +28,11 @@ docker compose --profile basic up -d --build
# Initial setup (installs dependencies and configures SDR tools) # Initial setup (installs dependencies and configures SDR tools)
./setup.sh ./setup.sh
# Run the application (requires sudo for SDR/network access) # Run with production server (gunicorn + gevent, handles concurrent SSE/WebSocket)
sudo -E venv/bin/python intercept.py sudo ./start.sh
# Or activate venv first # Or for quick local dev (Flask dev server)
source venv/bin/activate sudo -E venv/bin/python intercept.py
sudo -E python intercept.py
``` ```
### Testing ### Testing
@@ -69,8 +68,9 @@ mypy .
## Architecture ## Architecture
### Entry Points ### Entry Points
- `intercept.py` - Main entry point script - `start.sh` - Production startup script (gunicorn + gevent auto-detection, CLI flags, HTTPS, fallback to Flask dev server)
- `app.py` - Flask application initialization, global state management, process lifecycle, SSE streaming infrastructure - `intercept.py` - Direct Flask dev server entry point (quick local development)
- `app.py` - Flask application initialization, global state management, process lifecycle, SSE streaming infrastructure, conditional gevent monkey-patch
### Route Blueprints (routes/) ### Route Blueprints (routes/)
Each signal type has its own Flask blueprint: Each signal type has its own Flask blueprint:
@@ -121,7 +121,7 @@ Each signal type has its own Flask blueprint:
### Key Patterns ### Key Patterns
**Server-Sent Events (SSE)**: All real-time features stream via SSE endpoints (`/stream_pager`, `/stream_sensor`, etc.). Pattern uses `queue.Queue` with timeout and keepalive messages. **Server-Sent Events (SSE)**: All real-time features stream via SSE endpoints (`/stream_pager`, `/stream_sensor`, etc.). Pattern uses `queue.Queue` with timeout and keepalive messages. Under gunicorn + gevent, each SSE connection is a lightweight greenlet instead of an OS thread.
**Process Management**: External decoders run as subprocesses with output threads feeding queues. Use `safe_terminate()` for cleanup. Global locks prevent race conditions. **Process Management**: External decoders run as subprocesses with output threads feeding queues. Use `safe_terminate()` for cleanup. Global locks prevent race conditions.
@@ -152,7 +152,7 @@ Each signal type has its own Flask blueprint:
- **Mode Integration**: Each mode needs entries in `index.html` at ~12 points: CSS include, welcome card, partial include, visuals container, JS include, `validModes` set, `modeGroups` map, classList toggle, `modeNames`, visuals display toggle, titles, and init call in `switchMode()` - **Mode Integration**: Each mode needs entries in `index.html` at ~12 points: CSS include, welcome card, partial include, visuals container, JS include, `validModes` set, `modeGroups` map, classList toggle, `modeNames`, visuals display toggle, titles, and init call in `switchMode()`
### Docker ### Docker
- `Dockerfile` - Single-stage build with all SDR tools compiled from source (dump1090, AIS-catcher, slowrx, SatDump, etc.) - `Dockerfile` - Single-stage build with all SDR tools compiled from source (dump1090, AIS-catcher, slowrx, SatDump, etc.). CMD runs `start.sh` (gunicorn + gevent)
- `docker-compose.yml` - Two profiles: `basic` (standalone) and `history` (with Postgres for ADS-B) - `docker-compose.yml` - Two profiles: `basic` (standalone) and `history` (with Postgres for ADS-B)
- `build-multiarch.sh` - Multi-arch build script for amd64 + arm64 (RPi5) - `build-multiarch.sh` - Multi-arch build script for amd64 + arm64 (RPi5)
- Data persisted via `./data:/app/data` volume mount - Data persisted via `./data:/app/data` volume mount
+1 -1
View File
@@ -274,4 +274,4 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -sf http://localhost:5050/health || exit 1 CMD curl -sf http://localhost:5050/health || exit 1
# Run the application # Run the application
CMD ["python", "intercept.py"] CMD ["/bin/bash", "start.sh"]
+36 -34
View File
@@ -50,47 +50,49 @@ Support the developer of this open-source project
- **Meshtastic** - LoRa mesh network integration - **Meshtastic** - LoRa mesh network integration
- **Space Weather** - Real-time solar and geomagnetic data from NOAA SWPC, NASA SDO, and HamQSL (no SDR required) - **Space Weather** - Real-time solar and geomagnetic data from NOAA SWPC, NASA SDO, and HamQSL (no SDR required)
- **Spy Stations** - Number stations and diplomatic HF network database - **Spy Stations** - Number stations and diplomatic HF network database
- **Remote Agents** - Distributed SIGINT with remote sensor nodes - **Remote Agents** - Distributed SIGINT with remote sensor nodes
- **Offline Mode** - Bundled assets for air-gapped/field deployments - **Offline Mode** - Bundled assets for air-gapped/field deployments
--- ---
## CW / Morse Decoder Notes ## CW / Morse Decoder Notes
Live backend: Live backend:
- Uses `rtl_fm` piped into `multimon-ng` (`MORSE_CW`) for real-time decode. - Uses `rtl_fm` piped into `multimon-ng` (`MORSE_CW`) for real-time decode.
Recommended baseline settings: Recommended baseline settings:
- **Tone**: `700 Hz` - **Tone**: `700 Hz`
- **Bandwidth**: `200 Hz` (use `100 Hz` for crowded bands, `400 Hz` for drifting signals) - **Bandwidth**: `200 Hz` (use `100 Hz` for crowded bands, `400 Hz` for drifting signals)
- **Threshold Mode**: `Auto` - **Threshold Mode**: `Auto`
- **WPM Mode**: `Auto` - **WPM Mode**: `Auto`
Auto Tone Track behavior: Auto Tone Track behavior:
- Continuously measures nearby tone energy around the configured CW pitch. - Continuously measures nearby tone energy around the configured CW pitch.
- Steers the detector toward the strongest valid CW tone when signal-to-noise is sufficient. - Steers the detector toward the strongest valid CW tone when signal-to-noise is sufficient.
- Use **Hold Tone Lock** to freeze tracking once the desired signal is centered. - Use **Hold Tone Lock** to freeze tracking once the desired signal is centered.
Troubleshooting (no decode / noisy decode): Troubleshooting (no decode / noisy decode):
- Confirm demod path is **USB/CW-compatible** and frequency is tuned correctly. - Confirm demod path is **USB/CW-compatible** and frequency is tuned correctly.
- If multiple SDRs are connected and the selected one has no PCM output, Morse startup now auto-tries other detected SDR devices and reports the active device/serial in status logs. - If multiple SDRs are connected and the selected one has no PCM output, Morse startup now auto-tries other detected SDR devices and reports the active device/serial in status logs.
- Match **tone** and **bandwidth** to the actual sidetone/pitch. - Match **tone** and **bandwidth** to the actual sidetone/pitch.
- Try **Threshold Auto** first; if needed, switch to manual threshold and recalibrate. - Try **Threshold Auto** first; if needed, switch to manual threshold and recalibrate.
- Use **Reset/Calibrate** after major frequency or band condition changes. - Use **Reset/Calibrate** after major frequency or band condition changes.
- Raise **Minimum Signal Gate** to suppress random noise keying. - Raise **Minimum Signal Gate** to suppress random noise keying.
--- ---
## Installation / Debian / Ubuntu / MacOS ## Installation / Debian / Ubuntu / MacOS
**1. Clone and run:** **1. Clone and run:**
```bash ```bash
git clone https://github.com/smittix/intercept.git git clone https://github.com/smittix/intercept.git
cd intercept cd intercept
./setup.sh ./setup.sh
sudo -E venv/bin/python intercept.py sudo ./start.sh
``` ```
> **Production vs Dev server:** `start.sh` auto-detects gunicorn + gevent and runs a production server with cooperative greenlets — handles multiple SSE/WebSocket clients without blocking. Falls back to Flask dev server if gunicorn is not installed. For quick local development, you can still use `sudo -E venv/bin/python intercept.py` directly.
### Docker ### Docker
```bash ```bash
@@ -174,7 +176,7 @@ Set these as environment variables for either local installs or Docker:
```bash ```bash
INTERCEPT_ADSB_AUTO_START=true \ INTERCEPT_ADSB_AUTO_START=true \
INTERCEPT_SHARED_OBSERVER_LOCATION=false \ INTERCEPT_SHARED_OBSERVER_LOCATION=false \
sudo -E venv/bin/python intercept.py sudo ./start.sh
``` ```
**Docker example (.env)** **Docker example (.env)**
+9
View File
@@ -6,6 +6,15 @@ Flask application and shared state.
from __future__ import annotations from __future__ import annotations
import os as _os
if _os.environ.get('INTERCEPT_USE_GEVENT') == '1':
try:
from gevent import monkey as _monkey
_monkey.patch_all(subprocess=False)
except ImportError:
pass
del _os
import sys import sys
import site import site
+1
View File
@@ -20,6 +20,7 @@ CHANGELOG = [
"WeFax (Weather Fax) decoder with auto-scheduler and broadcast timeline", "WeFax (Weather Fax) decoder with auto-scheduler and broadcast timeline",
"System Health monitoring mode with telemetry dashboard", "System Health monitoring mode with telemetry dashboard",
"HTTPS support, HackRF TSCM RF scan, ADS-B voice alerts", "HTTPS support, HackRF TSCM RF scan, ADS-B voice alerts",
"Production server (start.sh) with gunicorn + gevent for concurrent multi-client support",
"Multi-SDR support for WeFax, tool path overrides, native Homebrew detection", "Multi-SDR support for WeFax, tool path overrides, native Homebrew detection",
"GPS mode upgraded to textured 3D globe", "GPS mode upgraded to textured 3D globe",
"Destroy lifecycle added to all mode modules to prevent resource leaks", "Destroy lifecycle added to all mode modules to prevent resource leaks",
+2
View File
@@ -1,6 +1,8 @@
# INTERCEPT - Signal Intelligence Platform # INTERCEPT - Signal Intelligence Platform
# Docker Compose configuration for easy deployment # Docker Compose configuration for easy deployment
# #
# Uses gunicorn + gevent production server via start.sh (handles concurrent SSE/WebSocket)
#
# Basic usage (build locally): # Basic usage (build locally):
# docker compose --profile basic up -d --build # docker compose --profile basic up -d --build
# #
+1
View File
@@ -466,6 +466,7 @@ The settings modal shows availability status for each bundled asset:
## General ## General
- **Web-based interface** - no desktop app needed - **Web-based interface** - no desktop app needed
- **Production server** - gunicorn + gevent via `start.sh` for concurrent SSE/WebSocket handling (falls back to Flask dev server)
- **Live message streaming** via Server-Sent Events (SSE) - **Live message streaming** via Server-Sent Events (SSE)
- **Audio alerts** with mute toggle - **Audio alerts** with mute toggle
- **Message export** to CSV/JSON - **Message export** to CSV/JSON
+5 -2
View File
@@ -139,10 +139,13 @@ pip install -r requirements.txt
After installation: After installation:
```bash ```bash
sudo -E venv/bin/python intercept.py sudo ./start.sh
# Custom port # Custom port
INTERCEPT_PORT=8080 sudo -E venv/bin/python intercept.py sudo ./start.sh -p 8080
# HTTPS
sudo ./start.sh --https
``` ```
Open **http://localhost:5050** in your browser. Open **http://localhost:5050** in your browser.
+2 -3
View File
@@ -18,10 +18,9 @@ By default, INTERCEPT binds to `0.0.0.0:5050`, making it accessible from any net
echo "block in on en0 proto tcp from any to any port 5050" | sudo pfctl -ef - echo "block in on en0 proto tcp from any to any port 5050" | sudo pfctl -ef -
``` ```
2. **Bind to Localhost**: For local-only access, set the host environment variable: 2. **Bind to Localhost**: For local-only access, set the host or use the CLI flag:
```bash ```bash
export INTERCEPT_HOST=127.0.0.1 sudo ./start.sh -H 127.0.0.1
python intercept.py
``` ```
3. **Trusted Networks Only**: Only run INTERCEPT on networks you trust. The application has no authentication mechanism. 3. **Trusted Networks Only**: Only run INTERCEPT on networks you trust. The application has no authentication mechanism.
+3 -3
View File
@@ -25,7 +25,7 @@ sudo apt install python3-flask python3-requests python3-serial python3-skyfield
# Then create venv with system packages # Then create venv with system packages
python3 -m venv --system-site-packages venv python3 -m venv --system-site-packages venv
source venv/bin/activate source venv/bin/activate
sudo venv/bin/python intercept.py sudo ./start.sh
``` ```
### "error: externally-managed-environment" (pip blocked) ### "error: externally-managed-environment" (pip blocked)
@@ -61,7 +61,7 @@ sudo apt install python3.11 python3.11-venv python3-pip
python3.11 -m venv venv python3.11 -m venv venv
source venv/bin/activate source venv/bin/activate
pip install -r requirements.txt pip install -r requirements.txt
sudo venv/bin/python intercept.py sudo ./start.sh
``` ```
### Alternative: Use the setup script ### Alternative: Use the setup script
@@ -336,7 +336,7 @@ rtl_fm -M am -f 118000000 -s 24000 -r 24000 -g 40 2>/dev/null | \
Run INTERCEPT with sudo: Run INTERCEPT with sudo:
```bash ```bash
sudo -E venv/bin/python intercept.py sudo ./start.sh
``` ```
### Interface not found after enabling monitor mode ### Interface not found after enabling monitor mode
+18 -2
View File
@@ -172,7 +172,7 @@ Set the following environment variables (Docker recommended):
```bash ```bash
INTERCEPT_ADSB_AUTO_START=true \ INTERCEPT_ADSB_AUTO_START=true \
INTERCEPT_SHARED_OBSERVER_LOCATION=false \ INTERCEPT_SHARED_OBSERVER_LOCATION=false \
sudo -E venv/bin/python intercept.py sudo ./start.sh
``` ```
**Docker example (.env)** **Docker example (.env)**
@@ -518,10 +518,26 @@ INTERCEPT can be configured via environment variables:
| `INTERCEPT_LOG_LEVEL` | `WARNING` | Log level (DEBUG, INFO, WARNING, ERROR) | | `INTERCEPT_LOG_LEVEL` | `WARNING` | Log level (DEBUG, INFO, WARNING, ERROR) |
| `INTERCEPT_DEFAULT_GAIN` | `40` | Default RTL-SDR gain | | `INTERCEPT_DEFAULT_GAIN` | `40` | Default RTL-SDR gain |
Example: `INTERCEPT_PORT=8080 sudo -E venv/bin/python intercept.py` Example: `INTERCEPT_PORT=8080 sudo ./start.sh`
## Command-line Options ## Command-line Options
### Production server (recommended)
```
./start.sh --help
-p, --port PORT Port to listen on (default: 5050)
-H, --host HOST Host to bind to (default: 0.0.0.0)
-d, --debug Run in debug mode (Flask dev server)
--https Enable HTTPS with self-signed certificate
--check-deps Check dependencies and exit
```
`start.sh` auto-detects gunicorn + gevent and runs a production WSGI server with cooperative greenlets — this handles multiple SSE streams and WebSocket connections concurrently without blocking. Falls back to the Flask dev server if gunicorn is not installed.
### Development server
``` ```
python3 intercept.py --help python3 intercept.py --help
+1 -1
View File
@@ -331,7 +331,7 @@
<pre><code>git clone https://github.com/smittix/intercept.git <pre><code>git clone https://github.com/smittix/intercept.git
cd intercept cd intercept
./setup.sh ./setup.sh
sudo -E venv/bin/python intercept.py</code></pre> sudo ./start.sh</code></pre>
</div> </div>
<p class="install-note">Requires Python 3.9+ and RTL-SDR drivers</p> <p class="install-note">Requires Python 3.9+ and RTL-SDR drivers</p>
</div> </div>
+4
View File
@@ -47,3 +47,7 @@ websocket-client>=1.6.0
# System health monitoring (optional - graceful fallback if unavailable) # System health monitoring (optional - graceful fallback if unavailable)
psutil>=5.9.0 psutil>=5.9.0
# Production WSGI server (optional - falls back to Flask dev server)
gunicorn>=21.2.0
gevent>=23.9.0
+5 -1
View File
@@ -337,7 +337,8 @@ install_python_deps() {
info "Installing optional packages..." info "Installing optional packages..."
for pkg in "numpy>=1.24.0" "scipy>=1.10.0" "Pillow>=9.0.0" "skyfield>=1.45" \ for pkg in "numpy>=1.24.0" "scipy>=1.10.0" "Pillow>=9.0.0" "skyfield>=1.45" \
"bleak>=0.21.0" "psycopg2-binary>=2.9.9" "meshtastic>=2.0.0" \ "bleak>=0.21.0" "psycopg2-binary>=2.9.9" "meshtastic>=2.0.0" \
"scapy>=2.4.5" "qrcode[pil]>=7.4" "cryptography>=41.0.0"; do "scapy>=2.4.5" "qrcode[pil]>=7.4" "cryptography>=41.0.0" \
"gunicorn>=21.2.0" "gevent>=23.9.0"; do
pkg_name="${pkg%%>=*}" pkg_name="${pkg%%>=*}"
if ! $PIP install "$pkg" 2>/dev/null; then if ! $PIP install "$pkg" 2>/dev/null; then
warn "${pkg_name} failed to install (optional - related features may be unavailable)" warn "${pkg_name} failed to install (optional - related features may be unavailable)"
@@ -1590,6 +1591,9 @@ final_summary_and_hard_fail() {
echo "============================================" echo "============================================"
echo echo
echo "To start INTERCEPT:" echo "To start INTERCEPT:"
echo " sudo ./start.sh"
echo
echo "Or for quick local dev:"
echo " sudo -E venv/bin/python intercept.py" echo " sudo -E venv/bin/python intercept.py"
echo echo
echo "Then open http://localhost:5050 in your browser" echo "Then open http://localhost:5050 in your browser"
Executable
+150
View File
@@ -0,0 +1,150 @@
#!/usr/bin/env bash
# INTERCEPT - Production Startup Script
#
# Starts INTERCEPT with gunicorn + gevent for production use.
# Falls back to Flask dev server if gunicorn is not installed.
#
# Usage:
# ./start.sh # Default: 0.0.0.0:5050
# ./start.sh -p 8080 # Custom port
# ./start.sh --https # HTTPS with self-signed cert
# ./start.sh --debug # Debug mode (Flask dev server)
# ./start.sh --check-deps # Check dependencies and exit
set -euo pipefail
# ── Defaults (can be overridden by env vars or CLI flags) ────────────────────
HOST="${INTERCEPT_HOST:-0.0.0.0}"
PORT="${INTERCEPT_PORT:-5050}"
DEBUG=0
HTTPS=0
CHECK_DEPS=0
# ── Parse CLI arguments ─────────────────────────────────────────────────────
while [[ $# -gt 0 ]]; do
case "$1" in
-p|--port)
PORT="$2"
shift 2
;;
-H|--host)
HOST="$2"
shift 2
;;
-d|--debug)
DEBUG=1
shift
;;
--https)
HTTPS=1
shift
;;
--check-deps)
CHECK_DEPS=1
shift
;;
-h|--help)
echo "Usage: start.sh [OPTIONS]"
echo ""
echo "Options:"
echo " -p, --port PORT Port to listen on (default: 5050)"
echo " -H, --host HOST Host to bind to (default: 0.0.0.0)"
echo " -d, --debug Run in debug mode (Flask dev server)"
echo " --https Enable HTTPS with self-signed certificate"
echo " --check-deps Check dependencies and exit"
echo " -h, --help Show this help message"
exit 0
;;
*)
echo "Unknown option: $1" >&2
exit 1
;;
esac
done
# ── Export for config.py ─────────────────────────────────────────────────────
export INTERCEPT_HOST="$HOST"
export INTERCEPT_PORT="$PORT"
# ── Dependency check (delegate to intercept.py) ─────────────────────────────
if [[ "$CHECK_DEPS" -eq 1 ]]; then
exec python intercept.py --check-deps
fi
# ── Debug mode always uses Flask dev server ──────────────────────────────────
if [[ "$DEBUG" -eq 1 ]]; then
echo "[INTERCEPT] Starting in debug mode (Flask dev server)..."
export INTERCEPT_DEBUG=1
exec python intercept.py --host "$HOST" --port "$PORT" --debug
fi
# ── HTTPS certificate generation ────────────────────────────────────────────
CERT_DIR="certs"
CERT_FILE="$CERT_DIR/intercept.crt"
KEY_FILE="$CERT_DIR/intercept.key"
if [[ "$HTTPS" -eq 1 ]]; then
if [[ ! -f "$CERT_FILE" || ! -f "$KEY_FILE" ]]; then
echo "[INTERCEPT] Generating self-signed SSL certificate..."
mkdir -p "$CERT_DIR"
openssl req -x509 -newkey rsa:2048 \
-keyout "$KEY_FILE" -out "$CERT_FILE" \
-days 365 -nodes \
-subj '/CN=intercept/O=INTERCEPT/C=US' 2>/dev/null
echo "[INTERCEPT] SSL certificate generated: $CERT_FILE"
else
echo "[INTERCEPT] Using existing SSL certificate: $CERT_FILE"
fi
fi
# ── Detect gunicorn + gevent ─────────────────────────────────────────────────
HAS_GUNICORN=0
HAS_GEVENT=0
if python -c "import gunicorn" 2>/dev/null; then
HAS_GUNICORN=1
fi
if python -c "import gevent" 2>/dev/null; then
HAS_GEVENT=1
fi
# ── Start the server ─────────────────────────────────────────────────────────
if [[ "$HAS_GUNICORN" -eq 1 && "$HAS_GEVENT" -eq 1 ]]; then
echo "[INTERCEPT] Starting production server (gunicorn + gevent)..."
echo "[INTERCEPT] Listening on ${HOST}:${PORT}"
export INTERCEPT_USE_GEVENT=1
GUNICORN_ARGS=(
-k gevent
-w 1
--timeout 300
--worker-connections 1000
--bind "${HOST}:${PORT}"
--access-logfile -
--error-logfile -
)
if [[ "$HTTPS" -eq 1 ]]; then
GUNICORN_ARGS+=(--certfile "$CERT_FILE" --keyfile "$KEY_FILE")
echo "[INTERCEPT] HTTPS enabled"
fi
exec gunicorn "${GUNICORN_ARGS[@]}" app:app
else
if [[ "$HAS_GUNICORN" -eq 0 ]]; then
echo "[INTERCEPT] gunicorn not found — falling back to Flask dev server"
fi
if [[ "$HAS_GEVENT" -eq 0 ]]; then
echo "[INTERCEPT] gevent not found — falling back to Flask dev server"
fi
echo "[INTERCEPT] Install with: pip install gunicorn gevent"
echo ""
FLASK_ARGS=(--host "$HOST" --port "$PORT")
if [[ "$HTTPS" -eq 1 ]]; then
FLASK_ARGS+=(--https)
fi
exec python intercept.py "${FLASK_ARGS[@]}"
fi