Skip to content

WaterPistolAI/THON

Repository files navigation

THON - The Hackathon Organizer Node

Migrated to https://github.com/WaterPistolAI/thon.git

Run multiple VS Code sandbox instances concurrently with nginx SSL reverse proxy, groups-based user management, persistent workspaces, and optional local LLM inference via Lemonade Server.

Features

  • Unified CLI: thon install, thon init, thon setup, thon run, thon launch — one config file (thon.yaml) for everything
  • Multi-Instance: Multiple concurrent VS Code sandboxes from a single command
  • Groups-Based: Define users and groups in YAML or manage via dashboard
  • Web Dashboard: Streamlit dashboard with 7 pages for instance, group, user, workspace, Lemonade, and gateway management
  • REST API: FastAPI REST API with Swagger UI for programmatic access
  • SSL/TLS: Automatic nginx reverse proxy with 4 SSL providers (auto, certbot, mkcert, openssl)
  • Domain Support: Let's Encrypt certificates and domain-based URLs
  • Persistent Workspaces: PVC Docker volumes or host bind mounts for workspace persistence
  • Local LLM: Optional Lemonade Server integration for local inference (chat + embedding)
  • Semantic Indexing: Embedding model for Kilo Code's semantic code search
  • AI Gateway: Optional APISIX gateway with per-user or per-group rate limiting and per-model concurrency
  • Authentication: Local password for dashboard; OIDC/OAuth2 (GitHub, GitLab, LinkedIn) for REST API
  • Config Files: Store and manage groups YAML, kilo.jsonc, and VS Code settings in the database
  • Kilo Code Ready: Auto-generated config with skeleton merging, experimental flags, and indexing
  • LLM Observability: Optional Langfuse integration for tracing LLM calls
  • Azure NVMe: Ephemeral NVMe disk automation for high-speed container and model storage

Video Guide

https://youtu.be/YptAQQf_4dg

Quick Start

1. One-time Setup

bash ./scripts/setup.sh

Installs python3, nginx, docker.io, mkcert, and openssl.

2. Build the Docker Image

docker build -t waterpistol/thon:latest ./

3. Initialize Configuration

# Interactive setup wizard (recommended)
python -m thon init

# Or non-interactive (CI-friendly)
python -m thon init --non-interactive

This creates a thon.yaml config file with all settings.

4. Setup and Run

# Install prerequisites and configure all components
python -m thon setup

# Start the API server
python -m thon run

# Launch VS Code instances
python -m thon launch

Alternatively, use main.py directly:

python ./scripts/main.py --groups groups.yaml --external-ip 1.2.3.4

Each user gets their own VS Code sandbox at https://<ip>/<endpoint_path>/.

Architecture

┌─────────────────────────────────────────────────────────────┐
│                         Host Machine                         │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                    nginx (443)                       │    │
│  │         SSL termination + WebSocket proxy           │    │
│  └──────────────────────┬──────────────────────────────┘    │
│                         │                                    │
│  ┌──────────────────────┼──────────────────────────────┐    │
│  │                Docker Network                        │    │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐ │    │
│  │  │  Sandbox 1  │  │  Sandbox 2  │  │  Sandbox 3  │ │    │
│  │  │ code-server │  │ code-server │  │ code-server │ │    │
│  │  │   :8443     │  │   :8444     │  │   :8445     │ │    │
│  │  └─────────────┘  └─────────────┘  └─────────────┘ │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                              │
│  ┌─────────────────────────────────────────────────────┐    │
│  │           Lemonade Server (Optional)                 │    │
│  │    Chat model + Embedding model (semantic search)    │    │
│  │                   :13305                             │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                              │
│  ┌─────────────────────────────────────────────────────┐    │
│  │         APISIX AI Gateway (Optional)                 │    │
│  │    Rate limiting + per-user/group API keys           │    │
│  │    Chat route + Embedding route                      │    │
│  │                   :9080                              │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘

Components

Component Role
thon CLI Unified entry point: thon install, thon init, thon setup, thon run, thon launch, thon config, thon nginx, thon gateway, thon cleanup
main.py Orchestrates sandbox creation, nginx configs, workspace setup
Streamlit Dashboard Web UI with 7 pages for instance, group, user, workspace, Lemonade, gateway, and settings management (:8501)
FastAPI REST API Programmatic API for instances, groups, users, Lemonade, gateway, auth, nginx, config files (:8100)
nginx SSL termination + WebSocket proxy (per-port or combined config)
code-server VS Code in the browser, runs HTTP inside each sandbox
Lemonade Server Optional local LLM inference (chat + embedding models)
APISIX Gateway Optional rate limiting with per-user or per-group API keys and per-model concurrency
SQLite Persistent storage for sandbox records, groups, users, events, and settings

Network Modes (auto-detected)

Mode Endpoint Format Detection
Host 127.0.0.1:8443 No / after port
Bridge 127.0.0.1:52322/proxy/8443 /proxy/ in endpoint

Auto-detected from the server-returned endpoint — not a CLI flag.

Workspace Persistence

Mode Storage Lifecycle
PVC Volume Docker named volume (thon-workspace-*) Persists across instance recreations
Bind Mount Host directory (--workspace-dir) Persists on host filesystem
Ephemeral Inside container Lost when container is removed

PVC volumes are created automatically when users are imported via the dashboard or thon.yaml. When a sandbox is recreated, the same PVC volume is reattached.

SSL/TLS

Provider Description Best For
auto (default) Tries mkcert → certbot → openssl in order Most setups
certbot Let's Encrypt certificates Production with domain
mkcert CA-trusted certs, filename includes IP hash Development
openssl Self-signed certs with IP/domain in SAN Fallback

When a domain is configured (nginx.domain in thon.yaml), nginx uses it as server_name and Let's Encrypt is preferred for CA-trusted certificates. CA cert served at https://<ip>/ca.crt for remote clients.

URL Display

URL Type Format Priority
Domain URL https://{domain}/{endpoint_path}/ Highest (when domain configured)
Public URL https://{ip}/{endpoint_path}/ Medium (via external IP)
Local URL http://127.0.0.1:{port}/ Lowest (local access)

InstanceInfo.url computed property: domain_url > public_url > local_url.

thon CLI Reference

python -m thon COMMAND [OPTIONS]
Command Description
thon install Install system prerequisites (run once)
thon init Interactive setup wizard (creates thon.yaml)
thon setup Configure services from thon.yaml
thon gateway Apply APISIX gateway config only
thon run Start the API server (FastAPI)
thon launch Launch VS Code instances (batch mode)
thon config show Display current config
thon config env Export config as .env file
thon config validate Validate thon.yaml
thon nginx sync Regenerate nginx config from active instances
thon nginx cleanup Remove all THON nginx configs
thon cleanup Tear down all resources
Global Option Default Description
--config PATH ~/.thon/thon.yaml Path to config file

install Options

Option Description Default
--non-interactive Install all optional components without prompting false
--with-apisix Install APISIX AI Gateway packages false
--with-lemonade Install Lemonade server packages false
--ssl-dir DIR SSL certificate directory /etc/nginx/ssl

run Options

Option Description Default
--log-level LEVEL Log level (DEBUG, INFO, WARNING, ERROR) From thon.yaml or INFO

launch Options

Option Description Default
--group GROUP Launch only this group (all groups)
--demo Create default workspace when no groups configured false

Examples

python -m thon install                          # Install prerequisites
python -m thon install --with-apisix             # Install with AI Gateway
python -m thon init                              # Interactive setup wizard
python -m thon init --non-interactive            # CI-friendly defaults
python -m thon setup                             # Install + configure
python -m thon run                               # Start API server
python -m thon run --log-level DEBUG             # With debug logging
python -m thon launch                            # Start instances
python -m thon launch --group alpha              # Start one group
python -m thon launch --demo                     # Demo mode
python -m thon config validate                   # Check config
python -m thon config env --output .env          # Export .env
python -m thon nginx sync                        # Sync nginx configs
python -m thon nginx cleanup                     # Remove nginx configs
python -m thon cleanup                           # Tear down

main.py CLI Reference

python ./scripts/main.py [OPTIONS]

Core Options

Option Description Default
--groups FILE Path to groups.yaml file (none, single instance)
--group GROUP Run only this group (all groups)
--from-db Read groups/users from database false
--port PORT Starting port for code-server 8443
--timeout MIN Sandbox timeout in minutes 0 (no timeout)

Server Connection

Option Description Default
--domain DOMAIN Sandbox server domain localhost:8080
--api-key KEY Sandbox API key (none)

Docker Options

Option Description Default
--image IMAGE Docker image waterpistol/thon:latest
--python-version VER Python version in sandbox 3.12

Security

Option Description Default
--secure Enable per-user passwords false

Network

Option Description Default
--external-ip IP External IP for SSL and URLs auto-detected
--ssl-dir DIR SSL cert storage directory /etc/nginx/ssl
--no-nginx Disable nginx, use direct HTTP false

Workspace

Option Description Default
--workspace-dir DIR Host dir for persistent bind mounts (none)

Lemonade Integration

Option Description Default
--lemonade KILO_JSON kilo.jsonc path for LLM config injection (none)
--vscode-settings JSON VS Code settings file to inject (none)

AI Gateway

Option Description Default
--gateway Enable APISIX AI Gateway with rate limiting false
--gateway-per-group One consumer per group (shared API key) false
--gateway-redis-host HOST Redis host for shared rate limiting (none)
--gateway-rate-limit N Token limit per consumer per time window 500
--gateway-time-window N Rate limit time window in seconds 60

Langfuse Observability

Option Description Default
--langfuse Enable Langfuse LLM tracing false
--langfuse-public-key KEY Langfuse public key (none)
--langfuse-secret-key KEY Langfuse secret key (none)
--langfuse-base-url URL Langfuse API base URL https://cloud.langfuse.com

Maintenance

Option Description Default
--cleanup Remove all nginx configs and exit false

Examples

# All groups with nginx SSL (default)
python ./scripts/main.py --groups groups.yaml --external-ip 1.2.3.4

# Single group
python ./scripts/main.py --groups groups.yaml --group alpha --external-ip 1.2.3.4

# From database (uses PVC workspace volumes)
python ./scripts/main.py --from-db --external-ip 1.2.3.4

# Per-user passwords
python ./scripts/main.py --groups groups.yaml --secure --external-ip 1.2.3.4

# Persistent workspaces
python ./scripts/main.py --groups groups.yaml --workspace-dir /thon-workspace --external-ip 1.2.3.4

# Direct HTTP (no nginx)
python ./scripts/main.py --groups groups.yaml --no-nginx

# Single instance (no groups)
python ./scripts/main.py

# With Lemonade LLM inference
python ./scripts/main.py --groups groups.yaml --external-ip 1.2.3.4 --lemonade kilo.jsonc

# With AI Gateway (per-user rate limiting)
python ./scripts/main.py --groups groups.yaml --external-ip 1.2.3.4 --gateway

# With AI Gateway (per-group shared API keys)
python ./scripts/main.py --groups groups.yaml --external-ip 1.2.3.4 --gateway --gateway-per-group

# With AI Gateway + Redis rate limiting
python ./scripts/main.py --groups groups.yaml --external-ip 1.2.3.4 --gateway --gateway-redis-host 127.0.0.1

# With Langfuse LLM observability
python ./scripts/main.py --groups groups.yaml --external-ip 1.2.3.4 --lemonade kilo.jsonc --langfuse

# With custom VS Code settings
python ./scripts/main.py --groups groups.yaml --external-ip 1.2.3.4 --vscode-settings vscode-settings.jsonc

# Cleanup nginx configs
python ./scripts/main.py --cleanup

Dashboard

THON includes a Streamlit-based web dashboard with 7 pages for managing VS Code sandbox instances, groups, users, workspaces, Lemonade Server, and AI Gateway. The FastAPI REST API provides Swagger UI for programmatic access.

Quick Start

# Install dependencies
pip install streamlit pandas fastapi uvicorn pydantic sqlmodel sqlalchemy

# Run the dashboard
streamlit run dashboard/streamlit_app.py --server.port 8501

# Dashboard at http://localhost:8501

Optionally run the FastAPI REST API for programmatic access:

python -m thon run
# API docs at http://localhost:8100/docs

Pages

Page Features
Instances List, search/filter, create, pause/resume/kill, bulk actions, recreate with PVC volume
Groups CRUD groups/users, transfer users, start per-user/group instances with PVC workspaces
Users List, search, create/edit users with email, launch/stop per-user instances
Workspaces Overview of workspace volumes and mount status
Lemonade Server Status, health, performance, slots, system info, available models, dynamic rescale
AI Gateway Configure, setup, manage consumers, cleanup, mode/rate limit settings
Settings External IP, configuration file management (upload/edit/delete from DB)

Configuration Files in Database

The Settings page stores config files in the database. When main.py runs without CLI flags, it reads these from the database. Priority: CLI flag > database > none.

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

Lemonade Server (Local LLM Inference)

Provides an OpenAI-compatible API endpoint for VS Code extensions (Kilo Code, Continue, Cline) inside sandbox containers. Runs as a systemd service on the host. Supports both chat and embedding models for semantic code search.

Setup

# Full setup (install + configure + API keys + pull model + kilo.jsonc)
bash ./scripts/setup-lemonade.sh \
    --groups groups.yaml --generate-keys --external-ip 1.2.3.4

Or use the Python wrapper:

python ./scripts/lemonade_server.py run \
    --groups groups.yaml --generate-keys --external-ip 1.2.3.4

Without Embedding Model

bash ./scripts/setup-lemonade.sh --groups groups.yaml --generate-keys \
    --external-ip 1.2.3.4 --no-embedding

Service Management

sudo systemctl status lemonade-server
sudo systemctl stop lemonade-server
sudo systemctl restart lemonade-server
sudo journalctl -u lemonade-server -f

Dynamic Rescaling

Adjust context size and parallel slots without restarting the server:

# Via CLI
python ./scripts/lemonade_server.py rescale --num-users 8

# Via API
curl -X POST http://localhost:8100/api/lemonade/rescale?num_users=8

Configuration

File Location Purpose
config.json /var/lib/lemonade/.cache/lemonade/config.json Server settings (port, host, backend)
user_models.json Same directory User-registered custom models
server_models.json Same directory Server-suggested models
recipe_options.json Same directory Per-model runtime settings (ctx_size, backend, args)
API keys /etc/systemd/system/lemonade-server.service.d/override.conf LEMONADE_API_KEY, LEMONADE_ADMIN_API_KEY

Default Models

Model Checkpoint Short Name (API) Labels
Chat unsloth/gemma-4-31B-it-GGUF:Q8_K_XL user.gemma-4-31b-it custom, vision
Embedding SuperPauly/harrier-oss-v1-0.6b-gguf:harrier-oss-v1-0.6B-BF16 user.harrier-oss-v1-0.6b custom, embedding

The embedding model enables Kilo Code's semantic code search. Enabled by default; disable with --no-embedding. When enabled, max_loaded_models is automatically set to 2 (1 chat + 1 embedding).

Per-User Scaling

When --groups groups.yaml is passed, context size and parallel slots scale automatically:

Parameter Chat Model Embedding Model
ctx_size 262144 per user 32768 per user
-np num_users num_users

Lemonade-managed args (reserved, must NOT appear in llamacpp_args): --ctx-size, -c, -ngl, --gpu-layers, --n-gpu-layers, --jinja, --no-jinja, --model, -m, --port, --embedding, --embeddings, --mmproj*, --rerank*

setup-lemonade.sh Options

Option Description Default
--groups FILE groups.yaml for user count (none)
--group GROUP Filter to single group (all)
--num-users N Override parallel user count 1
--port PORT Server port 13305
--host HOST Bind address 0.0.0.0
--backend BACKEND llama.cpp backend: auto, vulkan, cpu auto
--ctx-size SIZE Per-user context size 262144
--model MODEL HuggingFace checkpoint unsloth/gemma-4-31B-it-GGUF:Q8_K_XL
--model-name NAME Short model name gemma-4-31b-it
--mmproj FILE Vision mmproj filename mmproj-BF16.gguf
--external-ip IP External IP for kilo.jsonc (auto-detect)
--generate-keys Generate API keys false
--no-prefer-system Use bundled llama.cpp (system preferred)
--llamacpp-bin PATH Path to system llama-server /usr/local/bin/llama-server
--kilo-config PATH Output path for kilo.jsonc ./kilo.jsonc
--no-embedding Disable embedding model false
--embedding-model MODEL Embedding model checkpoint SuperPauly/harrier-oss-v1-0.6b-gguf:harrier-oss-v1-0.6B-BF16
--embedding-model-name NAME Short name for embedding model harrier-oss-v1-0.6b

Building llama.cpp from Source (AMD MI300X)

bash ./build-amd-mi300x-llama-server.sh

Builds llama.cpp with ROCm/HIP for gfx942 and installs to /usr/local. The Lemonade config uses prefer_system: true with rocm_bin: /usr/local/bin/llama-server by default.

Kilo Code Integration

Three deployment modes for kilo.jsonc:

Mode CLI Flag Kilo Points To
lemonade-direct --lemonade kilo.jsonc Lemonade server directly
gateway-per-user --gateway APISIX gateway (per-user API key)
gateway-per-group --gateway --gateway-per-group APISIX gateway (shared group API key)
  1. setup-lemonade.sh --generate-keys creates API keys and writes kilo.jsonc
  2. kilo.jsonc is built by deep-merging config/kilo.jsonc.skeleton with dynamic fields
  3. Template variables substituted per-user: $THON_USERNAME, $THON_USER_EMAIL, $WORKSPACE
  4. Base URL resolution: --external-ip > Docker bridge gateway > localhost
  5. main.py --lemonade kilo.jsonc injects config into each sandbox at /home/vscode/.config/kilo/config.json
  6. Kilo Code reads the config and connects to the Lemonade server

Full Workflow

# Terminal 1: Set up Lemonade server with groups-based scaling
bash setup-lemonade.sh --groups groups.yaml --generate-keys --external-ip 1.2.3.4

# Terminal 2: Start VS Code sandboxes with Lemonade inference
python ./scripts/main.py --groups groups.yaml --external-ip 1.2.3.4 --lemonade kilo.jsonc

AI Gateway (APISIX Rate Limiting)

An optional APISIX API Gateway provides token-based rate limiting and per-consumer API keys for LLM endpoints. Creates two routes: /v1/chat/completions (ai-proxy-multi) and /v1/embeddings (upstream proxy for semantic indexing).

Consumer Modes

Mode Description Best For
per-user (default) Each user gets own API key and rate limit Individual accountability
per-group Each group shares one API key with combined limit (rate_limit × num_users) Team-based, shared capacity

Rate Limit Scopes

Scope Description
per-user Uniform limits across all models
per-model Different concurrency/token limits per model via model_concurrency list

Setup

# Install APISIX (or use INSTALL_GATEWAY=true during initial setup)
INSTALL_GATEWAY=true bash ./scripts/setup.sh

# Per-user mode
python scripts/apisix_gateway.py setup --groups groups.yaml \
    --lemonade-url http://127.0.0.1:13305

# Per-group mode
python scripts/apisix_gateway.py setup --groups groups.yaml \
    --lemonade-url http://127.0.0.1:13305 --per-group

# With Redis-backed rate limiting
python scripts/apisix_gateway.py setup --groups groups.yaml \
    --lemonade-url http://127.0.0.1:13305 --redis-host 127.0.0.1

Running with main.py

# 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: shared API key per group
python ./scripts/main.py --groups groups.yaml --external-ip 1.2.3.4 --gateway --gateway-per-group

# With Redis-backed rate limiting
python ./scripts/main.py --groups groups.yaml --external-ip 1.2.3.4 --gateway --gateway-redis-host 127.0.0.1

Rate Limiting Modes

Mode Redis Host Policy Scope
Local (not set) local Per-gateway-instance counters
Redis 127.0.0.1 redis Shared across all gateway instances

When enabled, main.py generates a gateway-aware kilo.jsonc that points to the gateway instead of directly to Lemonade. In per-group mode, all users in the same group receive the same kilo.jsonc with the shared group API key.

Security

Sandbox Instances

Flag code-server auth Password
(default) --auth none None
--secure --auth password Auto-generated per-user (24-char token)

Dashboard Authentication

Two independent mechanisms:

Method Scope Mechanism
Local Password Streamlit dashboard AUTH_LOCAL_PASSWORD=mysecret — single shared password
OIDC/OAuth2 FastAPI REST API GitHub, GitLab, or LinkedIn via PKCE flow
# Local password for dashboard
AUTH_LOCAL_PASSWORD=mysecret streamlit run dashboard/streamlit_app.py --server.port 8501

# OIDC for 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 run

REST API Endpoints

The FastAPI REST API on port 8100 provides Swagger UI at /docs.

Instances

Method Path Description
GET /api/instances List instances (filter by state, paginate)
POST /api/instances Create new instance
GET /api/instances/{id} Get instance details
POST /api/instances/{id}/pause Pause instance
POST /api/instances/{id}/resume Resume instance
DELETE /api/instances/{id} Terminate instance
POST /api/instances/{id}/renew Extend TTL
POST /api/instances/bulk/pause Bulk pause
POST /api/instances/bulk/resume Bulk resume
POST /api/instances/bulk/kill Bulk terminate

Groups

Method Path Description
GET /api/groups List all groups with users
POST /api/groups Create a new group
GET /api/groups/export Export groups as YAML dict
PUT /api/groups/{group_id} Rename a group
DELETE /api/groups/{group_id} Delete a group and its users
POST /api/groups/{group_id}/users Add a user to a group
DELETE /api/groups/{group_id}/users/{user_id} Delete a user
POST /api/groups/{group_id}/users/{user_id}/transfer Transfer user to another group

Users

Method Path Description
GET /api/users List all users
POST /api/users Create a user
GET /api/users/{user_id} Get user details
PUT /api/users/{user_id} Update user
DELETE /api/users/{user_id} Delete user
POST /api/users/{user_id}/launch Launch sandbox instance for user
POST /api/users/{user_id}/stop Stop user's sandbox instance

Config Files

Method Path Description
GET /api/config-files List all config file slots
GET /api/config-files/{key} Get config file content
PUT /api/config-files/{key} Update config file content
POST /api/config-files/{key}/upload Upload a config file
DELETE /api/config-files/{key} Delete a config file

Lemonade

Method Path Description
GET /api/lemonade/status Server status
GET /api/lemonade/models Available models
GET /api/lemonade/api-info API endpoint info
GET /api/lemonade/health Proxy: server health
GET /api/lemonade/stats Proxy: performance stats
GET /api/lemonade/system-info Proxy: hardware details
GET /api/lemonade/live Proxy: liveness probe
GET /api/lemonade/slots Proxy: slot states
POST /api/lemonade/slots/{id}/save Proxy: save slot cache
POST /api/lemonade/slots/{id}/restore Proxy: restore slot cache
POST /api/lemonade/slots/{id}/erase Proxy: erase slot cache
POST /api/lemonade/pull Proxy: pull a model
GET /api/lemonade/pull/variants Proxy: GGUF variants for a checkpoint
POST /api/lemonade/delete Proxy: delete a model
POST /api/lemonade/load Proxy: load a model
POST /api/lemonade/unload Proxy: unload a model
POST /api/lemonade/install Proxy: install a backend
POST /api/lemonade/uninstall Proxy: remove a backend
POST /api/lemonade/rescale Dynamic rescale (adjust ctx_size/np for user count)

Gateway

Method Path Description
GET /api/gateway/status Gateway status
GET /api/gateway/consumers List consumers
POST /api/gateway/consumers Create consumer
DELETE /api/gateway/consumers/{username} Delete consumer
POST /api/gateway/setup Full setup
POST /api/gateway/route Create/update AI proxy route
DELETE /api/gateway/route Delete AI proxy route
POST /api/gateway/cleanup Remove all consumers and routes

Nginx

Method Path Description
GET /api/nginx/status Nginx status
POST /api/nginx/sync Regenerate nginx config from active instances
POST /api/nginx/cleanup Remove all THON nginx configs

Auth

Method Path Description
GET /api/auth/providers List enabled OIDC/OAuth providers
GET /api/auth/login/{provider} Start OAuth flow
GET /api/auth/callback/{provider} OAuth callback
POST /api/auth/logout End session
GET /api/auth/me Current user info

Troubleshooting

Service Worker SSL Error

SecurityError: Failed to register a ServiceWorker — An SSL certificate error occurred

Fix: Use mkcert CA-trusted certs or Let's Encrypt. Remote clients must download and import the CA root from https://<ip>/ca.crt.

Bad Gateway (502)

Caused by --base-path on code-server or including upstream path in proxy_pass. Do NOT use --base-path and ensure proxy_pass ends with / only.

Model Not Found (404)

The user. prefix is required for user-registered models. Kilo Code should send user.gemma-4-31b-it as the model name, not gemma-4-31b-it.

Reserved llama.cpp Arguments

Lemonade manages these arguments internally and rejects them in llamacpp_args: -ngl, --jinja, --ctx-size, -c, -m, --port, --mmproj*, --rerank*

Embedding Model Not Loading

  1. Check max_loaded_models is at least 2 in config.json
  2. Verify GPU memory can support both models
  3. Try disabling: --no-embedding flag

Environment Variables

Sandbox Server

Variable Description Default
SANDBOX_DOMAIN Sandbox server address localhost:8080
SANDBOX_API_KEY Sandbox API key (none)
SANDBOX_IMAGE Docker image waterpistol/thon:latest
THON_DB_PATH SQLite database path ~/.thon/thon.db
THON_WORKSPACE_DIR Workspace directory for groups ~/.thon/workspace
THON_DOMAIN Domain name for nginx and Let's Encrypt (none)
THON_SSL_PROVIDER SSL provider: auto, certbot, mkcert, openssl auto
THON_CERTBOT_EMAIL Email for Let's Encrypt registration (none)
THON_KILO_CONFIG Path to kilo.jsonc (none)
THON_VSCODE_SETTINGS Path to VS Code settings file (none)
THON_LOG_LEVEL Logging level INFO
PYTHON_VERSION Python version in sandbox 3.12

Lemonade Server

Variable Description Default
LEMONADE_HOST Lemonade server bind address 0.0.0.0
LEMONADE_PORT Lemonade server port 13305
LEMONADE_API_KEY Lemonade API key (regular) (none)
LEMONADE_ADMIN_API_KEY Lemonade admin key (elevated) (none)

AI Gateway

Variable Description Default
GATEWAY_ENABLED Enable AI Gateway false
GATEWAY_ADMIN_URL APISIX Admin API URL http://127.0.0.1:9180
GATEWAY_ADMIN_KEY APISIX Admin API key (auto-detected)
GATEWAY_PROXY_PORT APISIX proxy port 9080
GATEWAY_REDIS_HOST Redis host for rate limiting (none)
GATEWAY_REDIS_PORT Redis port 6379
GATEWAY_REDIS_PASSWORD Redis password (none)
GATEWAY_RATE_LIMIT_TOKENS Token limit per consumer per window 500
GATEWAY_RATE_LIMIT_WINDOW Rate limit time window in seconds 60
GATEWAY_MODE Consumer mode: per-user or per-group per-user
GATEWAY_RATE_LIMIT_SCOPE Rate limit scope: per-user or per-model per-user
GATEWAY_CONCURRENCY_LIMIT Concurrency limit per consumer 1

Dashboard & Database

Variable Description Default
DASHBOARD_HOST FastAPI bind address 0.0.0.0
DASHBOARD_PORT FastAPI port 8100
DASHBOARD_SECRET_KEY FastAPI secret key (none)
DASHBOARD_DEBUG Enable debug/reload mode false

Authentication

Variable Description Default
AUTH_LOCAL_PASSWORD Single password for Streamlit dashboard (none)
AUTH_ENABLED Enable OIDC authentication on REST API false
AUTH_SESSION_SECRET HMAC secret for session tokens (none)
AUTH_GITHUB_CLIENT_ID GitHub OAuth App client ID (none)
AUTH_GITHUB_CLIENT_SECRET GitHub OAuth App client secret (none)
AUTH_GITLAB_CLIENT_ID GitLab OAuth App client ID (none)
AUTH_GITLAB_CLIENT_SECRET GitLab OAuth App client secret (none)
AUTH_LINKEDIN_CLIENT_ID LinkedIn OIDC client ID (none)
AUTH_LINKEDIN_CLIENT_SECRET LinkedIn OIDC client secret (none)

Langfuse

Variable Description Default
LANGFUSE_ENABLED Enable Langfuse observability false
LANGFUSE_PUBLIC_KEY Langfuse public key (none)
LANGFUSE_SECRET_KEY Langfuse secret key (none)
LANGFUSE_BASEURL Langfuse API base URL https://cloud.langfuse.com

File Map

Unified CLI

File Purpose
thon/__main__.py Entry point: delegates to thon.cli.main()
thon/cli.py Unified CLI: install, init, setup, gateway, run, launch, config, nginx, cleanup
thon/config.py ThonConfig + 14 Pydantic settings models (thon.yaml schema)
thon/install.py System package installer (config-free phase)
thon/interactive.py Init wizard with 12 interactive steps

Legacy CLI

File Purpose
main.py Entry point; CLI; groups; sandbox orchestration; kilo.jsonc injection
scripts/setup.sh One-time host prerequisite installation
scripts/nginx_config.py Per-port nginx config generation
scripts/ssl_cert.py SSL certificate generation (4 providers: auto, certbot, mkcert, openssl)
scripts/lemonade_server.py Lemonade server manager (Python CLI with rescale subcommand)
scripts/setup-lemonade.sh All-in-one Lemonade setup (shell, recommended)
scripts/apisix_gateway.py APISIX AI Gateway manager with per-model concurrency
scripts/build-amd-mi300x-llama-server.sh Build llama.cpp for AMD MI300X (gfx942)

Dashboard Application

File Purpose
app/main.py FastAPI application entry point; lifespan; route mounting
app/config.py AppConfig — loaded from environment variables
app/models.py Pydantic domain models (InstanceInfo, InstanceState, UserInfo, etc.)
app/db.py SQLite persistence (5 tables: sandbox_records, app_settings, event_records, group_records, user_records)
app/nginx_service.py Combined nginx config generator for API server
app/kilo_config.py Kilo config generation with skeleton merging
app/services/sandbox_service.py SandboxService — fleet CRUD operations
app/services/lemonade_service.py LemonadeService — server status, rescaling
app/services/groups_service.py GroupsService — group/user CRUD with Docker volume management
app/services/apisix_service.py ApisixService — APISIX Admin API wrapper
app/api/routes/instances.py REST API: instance endpoints
app/api/routes/lemonade.py REST API: Lemonade endpoints (including rescale)
app/api/routes/groups.py REST API: groups CRUD, events, export
app/api/routes/users.py REST API: users CRUD, launch/stop
app/api/routes/gateway.py REST API: gateway management
app/api/routes/config_files.py REST API: config file CRUD
app/api/routes/nginx.py REST API: nginx sync/cleanup/status
app/api/routes/auth.py REST API: OIDC/OAuth2 endpoints
app/auth/providers.py OIDC/OAuth2 provider implementations
app/auth/sessions.py SessionStore — HMAC-signed session management

Dashboard Frontend

File Purpose
dashboard/streamlit_app.py Streamlit dashboard: 7 pages with sidebar navigation
dashboard/streamlit_styles.py Dark theme CSS injection

Development Tools

File Purpose
development-tools/azure/bootstrap-ephemeral.sh NVMe disk bootstrap for Azure ephemeral storage
development-tools/azure/ephemeral-setup.service systemd unit for boot-time NVMe setup
development-tools/azure/README.md Azure ephemeral NVMe orchestration docs

Documentation Site

File Purpose
fumadocs/ Next.js + Fumadocs documentation site

Config

File Purpose
config/groups.yaml.example Groups and users configuration template
config/kilo.jsonc.example Kilo Code config template
config/kilo.jsonc.skeleton Base kilo.jsonc with experimental flags, permissions, MCP, indexing
config/vscode-settings.jsonc.example VS Code settings template
config/extensions.txt VS Code extensions list for Docker image
Dockerfile Sandbox image: ubuntu:24.04 + Node.js 24 + JRE + .NET 10 + Chromium + Open VSX

About

The Hackathon Organizer Node (THON) -- Run multiple VS Code sandbox instances concurrently with nginx SSL reverse proxy, groups-based user management, persistent workspaces, and optional local LLM inference via Lemonade Server.

Topics

Resources

License

Stars

Watchers

Forks

Contributors