Installation and Setup
Complete installation guide for THON - The Hackathon Organizer Node
Installation and Setup
Prerequisites
System Requirements
- OS: Linux (Ubuntu 24.04+ recommended)
- Docker: 20.10+ with Docker daemon running
- Python: 3.10+
- Memory: 4GB+ RAM (8GB+ recommended for multiple instances)
- GPU: AMD ROCm or NVIDIA CUDA (optional, for Lemonade inference)
Network Requirements
- Ports 443 (nginx HTTPS) and 80 (nginx HTTP) available
- Port 8100 for FastAPI REST API
- Port 8501 for Streamlit dashboard
- Port 13305 available for Lemonade Server (optional)
- Port 9080 available for APISIX AI Gateway (optional)
- Unique port per VS Code instance (starting from 8443 by default)
Step-by-Step Installation
1. Clone Repository
git clone https://github.com/WaterPistolAI/thon.git
cd thon2. Run Setup Script
bash ./scripts/setup.shOr use the unified CLI:
python -m thon installThis installs:
- python3 and pip
- nginx web server
- docker.io container runtime
- mkcert for local CA certificates
- openssl as SSL fallback
With Optional Components
# With AI Gateway
python -m thon install --with-apisix
# With Lemonade Server packages
python -m thon install --with-lemonade
# Both
python -m thon install --with-apisix --with-lemonade
# Or legacy method
INSTALL_GATEWAY=true bash ./scripts/setup.shThe --with-apisix flag additionally installs:
- apisix API gateway
- etcd-server configuration store for APISIX
- redis-server for shared rate limiting
3. Build Docker Image
docker build -t waterpistol/thon:latest ./The image includes:
- code-server: VS Code in the browser
- Node.js 24: Runtime environment
- JRE: Java development
- .NET SDK 10: .NET development
- Chromium: Browser automation
- Open VSX: Extension marketplace
- Non-root user: Security best practice
4. Configure THON
Option A: Interactive Setup (Recommended)
# Interactive wizard — creates thon.yaml
python -m thon init
# Review and edit the generated config
cat thon.yamlThe wizard walks you through:
- External IP (auto-detected)
- Groups and users
- Sandbox settings (domain, image, port)
- VS Code security (password auth)
- Nginx and SSL
- Workspace persistence
- Lemonade Server (local LLM)
- Kilo Code integration
- AI Gateway (rate limiting)
- Dashboard settings
- OIDC authentication
Option B: Non-Interactive Setup (CI/CD)
python -m thon init --non-interactiveGenerates a thon.yaml with sensible defaults:
- One group
alphawith usersaliceandbob - All optional features disabled
- Can be edited manually afterward
Option C: Manual YAML
Create thon.yaml manually (see Configuration for full reference):
external_ip: "1.2.3.4"
groups:
alpha:
- alice
- bob
beta:
- charlie
- dave
lemonade:
enabled: false
gateway:
enabled: false5. Install Prerequisites and Configure
# Install all prerequisites and configure components from thon.yaml
python -m thon setupThis runs a 4-step process:
- SSL directory and certificates (with certbot fallback for domains)
- Lemonade Server (if
lemonade.enabled) - AI Gateway (if
gateway.enabled) .envfile generation
6. Start the API Server
# Start the FastAPI API server (Swagger UI at /docs)
python -m thon run
# With debug logging
python -m thon run --log-level DEBUGAPI docs available at http://localhost:8100/docs.
7. Launch VS Code Instances
# Start instances from thon.yaml
python -m thon launch
# Start only one group
python -m thon launch --group alpha
# Demo mode (default workspace when no groups configured)
python -m thon launch --demoOr use main.py directly for more control:
python ./scripts/main.py --groups groups.yaml --external-ip YOUR_EXTERNAL_IP8. Run the Dashboard (Optional)
The Streamlit dashboard provides a web UI with 7 pages for managing instances, groups, users, workspaces, Lemonade server, AI gateway, and settings:
pip install streamlit pandas
streamlit run dashboard/streamlit_app.py --server.port 8501Dashboard available at http://localhost:8501.
Securing the Dashboard
For internal deployments, set a simple password:
AUTH_LOCAL_PASSWORD=mysecret streamlit run dashboard/streamlit_app.py --server.port 8501For production, enable OIDC authentication on the REST API:
AUTH_ENABLED=true \
AUTH_SESSION_SECRET=$(openssl rand -hex 32) \
AUTH_GITHUB_CLIENT_ID=xxx \
AUTH_GITHUB_CLIENT_SECRET=xxx \
python -m thon runSee Dashboard → Authentication for full details.
The FastAPI REST API also provides Swagger UI at http://localhost:8100/docs
for programmatic access:
python -m thon runSee Dashboard for full documentation.
Alternative: Using main.py Directly
If you prefer the traditional CLI approach without thon.yaml:
Create Groups Configuration
Create groups.yaml in your project directory:
groups:
alpha:
users:
- alice
- bob
beta:
users:
- charlie
- dave
gamma:
users:
- eveRun from YAML
python ./scripts/main.py --groups groups.yaml --external-ip YOUR_EXTERNAL_IPRun from Database
After importing groups via the dashboard, you can start instances from the database:
# Start all groups from database
python ./scripts/main.py --from-db --external-ip YOUR_EXTERNAL_IP
# Start a specific group from database
python ./scripts/main.py --from-db --group beta --external-ip YOUR_EXTERNAL_IPWhen using --from-db, PVC workspace volumes from the database are
reattached automatically, preserving user data across instance recreations.
Configure SSL Certificates
Option A: mkcert (Recommended for Development)
# Install local CA
mkcert -install
# Generate certificate for your IP
mkcert -cert-file /etc/nginx/ssl/server.crt \
-key-file /etc/nginx/ssl/server.key \
165.245.138.159 localhostOption B: Let's Encrypt (Production with Domain)
Set in thon.yaml:
nginx:
domain: "your-domain.com"
ssl_provider: "certbot"
certbot_email: "[email protected]"Then run python -m thon setup to generate certificates automatically.
Or manually:
sudo certbot certonly --standalone -d your-domain.com
sudo cp /etc/letsencrypt/live/your-domain.com/fullchain.pem /etc/nginx/ssl/server.crt
sudo cp /etc/letsencrypt/live/your-domain.com/privkey.pem /etc/nginx/ssl/server.keyOption C: Self-Signed (Fallback)
Set ssl_provider: "openssl" in thon.yaml. THON will generate self-signed
certificates with the IP/domain in the SAN extension.
Optional: Lemonade Server Setup
For local LLM inference with Kilo Code, Continue, or Cline extensions.
Quick Setup
bash ./scripts/setup-lemonade.sh \
--groups groups.yaml \
--generate-keys \
--external-ip YOUR_EXTERNAL_IPThis will:
- Install Lemonade Server from PPA
- Configure for your GPU backend
- Generate API keys
- Download the default chat model (gemma-4-31b-it)
- Download the default embedding model (harrier-oss-v1-0.6b)
- Create
kilo.jsoncfor Kilo Code (with semantic indexing config)
Without Embedding Model
bash ./scripts/setup-lemonade.sh \
--groups groups.yaml \
--generate-keys \
--external-ip YOUR_EXTERNAL_IP \
--no-embeddingCustom llama.cpp Build (AMD MI300X)
For optimal performance on AMD MI300X GPUs:
# Build llama.cpp with ROCm support
bash ./build-amd-mi300x-llama-server.sh
# Then run setup with system binary preference
bash ./scripts/setup-lemonade.sh \
--groups groups.yaml \
--generate-keys \
--external-ip YOUR_EXTERNAL_IPSee Lemonade Server for detailed configuration options.
Optional: AI Gateway Setup
For rate limiting LLM API access with per-user or per-group API keys:
Quick Setup
# Install APISIX (or use INSTALL_GATEWAY=true during initial setup)
INSTALL_GATEWAY=true bash ./scripts/setup.sh
# Setup gateway with per-user rate limiting (includes embedding route)
python scripts/apisix_gateway.py setup --groups groups.yaml \
--lemonade-url http://127.0.0.1:13305
# Per-group (shared API key per group)
python scripts/apisix_gateway.py setup --groups groups.yaml \
--lemonade-url http://127.0.0.1:13305 --per-group
# Without embedding route
python scripts/apisix_gateway.py setup --groups groups.yaml \
--lemonade-url http://127.0.0.1:13305 --no-embeddingRunning with Gateway
# Per-user: each user gets their own API key and rate limit
python ./scripts/main.py --groups groups.yaml --external-ip 1.2.3.4 --gateway
# Per-group: all users in a group share an API key with combined rate limit
python ./scripts/main.py --groups groups.yaml --external-ip 1.2.3.4 \
--gateway --gateway-per-group
# With Redis-backed rate limiting (recommended for multi-gateway)
python ./scripts/main.py --groups groups.yaml --external-ip 1.2.3.4 \
--gateway --gateway-redis-host 127.0.0.1See AI Gateway for detailed configuration options.
Running with Lemonade
After setting up Lemonade Server:
python ./scripts/main.py \
--groups groups.yaml \
--external-ip YOUR_EXTERNAL_IP \
--lemonade kilo.jsoncThis injects kilo.jsonc into each sandbox at /home/vscode/.config/kilo/config.json,
enabling Kilo Code to connect to your local LLM with both chat and semantic indexing.
Optional: Langfuse Observability
For LLM tracing and observability via Langfuse:
# Via CLI flags
python ./scripts/main.py --groups groups.yaml --external-ip 1.2.3.4 \
--langfuse \
--langfuse-public-key "pk-lf-..." \
--langfuse-secret-key "sk-lf-..." \
--langfuse-base-url "https://cloud.langfuse.com"
# Or via environment variables
export LANGFUSE_PUBLIC_KEY="pk-lf-..."
export LANGFUSE_SECRET_KEY="sk-lf-..."
export LANGFUSE_BASEURL="https://cloud.langfuse.com"
python ./scripts/main.py --groups groups.yaml --external-ip 1.2.3.4 --langfuse
# Combined with Lemonade + Langfuse
bash ./scripts/setup-lemonade.sh --groups groups.yaml --generate-keys --external-ip 1.2.3.4 --langfuse
python ./scripts/main.py --groups groups.yaml --external-ip 1.2.3.4 --lemonade kilo.jsonc --langfuseWhen Langfuse is enabled:
LANGFUSE_PUBLIC_KEY,LANGFUSE_SECRET_KEY,LANGFUSE_BASEURLare injected as env vars into each sandbox"plugin": ["opencode-plugin-langfuse"]is added to the generatedkilo.jsoncexperimental.openTelemetryis set totruein the kilo config- All LLM traces are automatically sent to your Langfuse dashboard
Or in thon.yaml:
langfuse:
enabled: true
public_key: "pk-lf-..."
secret_key: "sk-lf-..."
base_url: "https://cloud.langfuse.com"Verification
Check Services
# Docker is running
docker ps
# Nginx is configured
ls /etc/nginx/sites-enabled/
# Lemonade server is running (if configured)
sudo systemctl status lemonade-server
# Config is valid
python -m thon config validateAccess VS Code
- Open browser to the URL shown in terminal output
- For mkcert certificates, install the CA:
# Download CA certificate curl -o ca.crt https://YOUR_IP/ca.crt # Install in browser (varies by browser) # Chrome: Settings > Privacy > Certificates > Authorities > Import # Firefox: Settings > Privacy > Certificates > View Certificates > Authorities > Import
Config File Management
The dashboard's Settings page and the /api/config-files REST API allow you
to store configuration files in the database:
| Config Key | Description |
|---|---|
config_groups_yaml | Groups and users definition |
config_kilo_json | Kilo Code provider config |
config_vscode_settings | VS Code settings for each sandbox |
When main.py runs without CLI flags (e.g., python ./scripts/main.py without --groups
or --lemonade), it reads these from the database. CLI flags always take
precedence over database-stored configs.
Uninstallation
Remove VS Code Instances
# Clean up nginx configs
python ./scripts/main.py --cleanup
# Or use thon CLI
python -m thon cleanup
# Stop all sandboxes (via Docker)
docker ps -q | xargs -r docker stopRemove Lemonade Server
sudo systemctl stop lemonade-server
sudo apt-get remove --purge lemonade-server
sudo rm -rf /var/lib/lemonade
sudo rm -rf /etc/systemd/system/lemonade-server.service.dRemove All
# Remove packages
sudo apt-get remove --purge nginx docker.io mkcert
# Remove certificates
sudo rm -rf /etc/nginx/ssl
# Remove Docker image
docker rmi waterpistol/thon:latest
# Remove database
rm -rf ~/.thon/thon.db