Skip to content

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

ComponentRole
thon CLIUnified entry point: thon install, thon init, thon setup, thon run, thon launch, thon config, thon nginx, thon gateway, thon cleanup
main.pyOrchestrates sandbox creation, nginx configs, workspace setup
Streamlit DashboardWeb UI with 7 pages for instance, group, user, workspace, Lemonade, gateway, and settings management (:8501)
FastAPI REST APIProgrammatic API for instances, groups, users, Lemonade, gateway, auth, nginx, config files (:8100)
nginxSSL termination + WebSocket proxy (per-port or combined config)
code-serverVS Code in the browser, runs HTTP inside each sandbox
Lemonade ServerOptional local LLM inference (chat + embedding models)
APISIX GatewayOptional rate limiting with per-user or per-group API keys and per-model concurrency
SQLitePersistent storage for sandbox records, groups, users, events, and settings

Network Modes (auto-detected)

ModeEndpoint FormatDetection
Host127.0.0.1:8443No / after port
Bridge127.0.0.1:52322/proxy/8443/proxy/ in endpoint

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

Workspace Persistence

ModeStorageLifecycle
PVC VolumeDocker named volume (thon-workspace-*)Persists across instance recreations
Bind MountHost directory (--workspace-dir)Persists on host filesystem
EphemeralInside containerLost 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

ProviderDescriptionBest For
auto (default)Tries mkcert → certbot → openssl in orderMost setups
certbotLet's Encrypt certificatesProduction with domain
mkcertCA-trusted certs, filename includes IP hashDevelopment
opensslSelf-signed certs with IP/domain in SANFallback

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 TypeFormatPriority
Domain URLhttps://{domain}/{endpoint_path}/Highest (when domain configured)
Public URLhttps://{ip}/{endpoint_path}/Medium (via external IP)
Local URLhttp://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]
CommandDescription
thon installInstall system prerequisites (run once)
thon initInteractive setup wizard (creates thon.yaml)
thon setupConfigure services from thon.yaml
thon gatewayApply APISIX gateway config only
thon runStart the API server (FastAPI)
thon launchLaunch VS Code instances (batch mode)
thon config showDisplay current config
thon config envExport config as .env file
thon config validateValidate thon.yaml
thon nginx syncRegenerate nginx config from active instances
thon nginx cleanupRemove all THON nginx configs
thon cleanupTear down all resources
Global OptionDefaultDescription
--config PATH~/.thon/thon.yamlPath to config file

install Options

OptionDescriptionDefault
--non-interactiveInstall all optional components without promptingfalse
--with-apisixInstall APISIX AI Gateway packagesfalse
--with-lemonadeInstall Lemonade server packagesfalse
--ssl-dir DIRSSL certificate directory/etc/nginx/ssl

run Options

OptionDescriptionDefault
--log-level LEVELLog level (DEBUG, INFO, WARNING, ERROR)From thon.yaml or INFO

launch Options

OptionDescriptionDefault
--group GROUPLaunch only this group(all groups)
--demoCreate default workspace when no groups configuredfalse

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

OptionDescriptionDefault
--groups FILEPath to groups.yaml file(none, single instance)
--group GROUPRun only this group(all groups)
--from-dbRead groups/users from databasefalse
--port PORTStarting port for code-server8443
--timeout MINSandbox timeout in minutes0 (no timeout)

Server Connection

OptionDescriptionDefault
--domain DOMAINSandbox server domainlocalhost:8080
--api-key KEYSandbox API key(none)

Docker Options

OptionDescriptionDefault
--image IMAGEDocker imagewaterpistol/thon:latest
--python-version VERPython version in sandbox3.12

Security

OptionDescriptionDefault
--secureEnable per-user passwordsfalse

Network

OptionDescriptionDefault
--external-ip IPExternal IP for SSL and URLsauto-detected
--ssl-dir DIRSSL cert storage directory/etc/nginx/ssl
--no-nginxDisable nginx, use direct HTTPfalse

Workspace

OptionDescriptionDefault
--workspace-dir DIRHost dir for persistent bind mounts(none)

Lemonade Integration

OptionDescriptionDefault
--lemonade KILO_JSONkilo.jsonc path for LLM config injection(none)
--vscode-settings JSONVS Code settings file to inject(none)

AI Gateway

OptionDescriptionDefault
--gatewayEnable APISIX AI Gateway with rate limitingfalse
--gateway-per-groupOne consumer per group (shared API key)false
--gateway-redis-host HOSTRedis host for shared rate limiting(none)
--gateway-rate-limit NToken limit per consumer per time window500
--gateway-time-window NRate limit time window in seconds60

Langfuse Observability

OptionDescriptionDefault
--langfuseEnable Langfuse LLM tracingfalse
--langfuse-public-key KEYLangfuse public key(none)
--langfuse-secret-key KEYLangfuse secret key(none)
--langfuse-base-url URLLangfuse API base URLhttps://cloud.langfuse.com

Maintenance

OptionDescriptionDefault
--cleanupRemove all nginx configs and exitfalse

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

PageFeatures
InstancesList, search/filter, create, pause/resume/kill, bulk actions, recreate with PVC volume
GroupsCRUD groups/users, transfer users, start per-user/group instances with PVC workspaces
UsersList, search, create/edit users with email, launch/stop per-user instances
WorkspacesOverview of workspace volumes and mount status
Lemonade ServerStatus, health, performance, slots, system info, available models, dynamic rescale
AI GatewayConfigure, setup, manage consumers, cleanup, mode/rate limit settings
SettingsExternal 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 KeyDescription
config_groups_yamlGroups and users definition
config_kilo_jsonKilo Code provider config
config_vscode_settingsVS 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

FileLocationPurpose
config.json/var/lib/lemonade/.cache/lemonade/config.jsonServer settings (port, host, backend)
user_models.jsonSame directoryUser-registered custom models
server_models.jsonSame directoryServer-suggested models
recipe_options.jsonSame directoryPer-model runtime settings (ctx_size, backend, args)
API keys/etc/systemd/system/lemonade-server.service.d/override.confLEMONADE_API_KEY, LEMONADE_ADMIN_API_KEY

Default Models

ModelCheckpointShort Name (API)Labels
Chatunsloth/gemma-4-31B-it-GGUF:Q8_K_XLuser.gemma-4-31b-itcustom, vision
EmbeddingSuperPauly/harrier-oss-v1-0.6b-gguf:harrier-oss-v1-0.6B-BF16user.harrier-oss-v1-0.6bcustom, 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:

ParameterChat ModelEmbedding Model
ctx_size262144 per user32768 per user
-npnum_usersnum_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

OptionDescriptionDefault
--groups FILEgroups.yaml for user count(none)
--group GROUPFilter to single group(all)
--num-users NOverride parallel user count1
--port PORTServer port13305
--host HOSTBind address0.0.0.0
--backend BACKENDllama.cpp backend: auto, vulkan, cpuauto
--ctx-size SIZEPer-user context size262144
--model MODELHuggingFace checkpointunsloth/gemma-4-31B-it-GGUF:Q8_K_XL
--model-name NAMEShort model namegemma-4-31b-it
--mmproj FILEVision mmproj filenamemmproj-BF16.gguf
--external-ip IPExternal IP for kilo.jsonc(auto-detect)
--generate-keysGenerate API keysfalse
--no-prefer-systemUse bundled llama.cpp(system preferred)
--llamacpp-bin PATHPath to system llama-server/usr/local/bin/llama-server
--kilo-config PATHOutput path for kilo.jsonc./kilo.jsonc
--no-embeddingDisable embedding modelfalse
--embedding-model MODELEmbedding model checkpointSuperPauly/harrier-oss-v1-0.6b-gguf:harrier-oss-v1-0.6B-BF16
--embedding-model-name NAMEShort name for embedding modelharrier-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:

ModeCLI FlagKilo Points To
lemonade-direct--lemonade kilo.jsoncLemonade server directly
gateway-per-user--gatewayAPISIX gateway (per-user API key)
gateway-per-group--gateway --gateway-per-groupAPISIX 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

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

Rate Limit Scopes

ScopeDescription
per-userUniform limits across all models
per-modelDifferent 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

ModeRedis HostPolicyScope
Local(not set)localPer-gateway-instance counters
Redis127.0.0.1redisShared 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

Flagcode-server authPassword
(default)--auth noneNone
--secure--auth passwordAuto-generated per-user (24-char token)

Dashboard Authentication

Two independent mechanisms:

MethodScopeMechanism
Local PasswordStreamlit dashboardAUTH_LOCAL_PASSWORD=mysecret — single shared password
OIDC/OAuth2FastAPI REST APIGitHub, 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

MethodPathDescription
GET/api/instancesList instances (filter by state, paginate)
POST/api/instancesCreate new instance
GET/api/instances/{id}Get instance details
POST/api/instances/{id}/pausePause instance
POST/api/instances/{id}/resumeResume instance
DELETE/api/instances/{id}Terminate instance
POST/api/instances/{id}/renewExtend TTL
POST/api/instances/bulk/pauseBulk pause
POST/api/instances/bulk/resumeBulk resume
POST/api/instances/bulk/killBulk terminate

Groups

MethodPathDescription
GET/api/groupsList all groups with users
POST/api/groupsCreate a new group
GET/api/groups/exportExport 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}/usersAdd a user to a group
DELETE/api/groups/{group_id}/users/{user_id}Delete a user
POST/api/groups/{group_id}/users/{user_id}/transferTransfer user to another group

Users

MethodPathDescription
GET/api/usersList all users
POST/api/usersCreate 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}/launchLaunch sandbox instance for user
POST/api/users/{user_id}/stopStop user's sandbox instance

Config Files

MethodPathDescription
GET/api/config-filesList 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}/uploadUpload a config file
DELETE/api/config-files/{key}Delete a config file

Lemonade

MethodPathDescription
GET/api/lemonade/statusServer status
GET/api/lemonade/modelsAvailable models
GET/api/lemonade/api-infoAPI endpoint info
GET/api/lemonade/healthProxy: server health
GET/api/lemonade/statsProxy: performance stats
GET/api/lemonade/system-infoProxy: hardware details
GET/api/lemonade/liveProxy: liveness probe
GET/api/lemonade/slotsProxy: slot states
POST/api/lemonade/slots/{id}/saveProxy: save slot cache
POST/api/lemonade/slots/{id}/restoreProxy: restore slot cache
POST/api/lemonade/slots/{id}/eraseProxy: erase slot cache
POST/api/lemonade/pullProxy: pull a model
GET/api/lemonade/pull/variantsProxy: GGUF variants for a checkpoint
POST/api/lemonade/deleteProxy: delete a model
POST/api/lemonade/loadProxy: load a model
POST/api/lemonade/unloadProxy: unload a model
POST/api/lemonade/installProxy: install a backend
POST/api/lemonade/uninstallProxy: remove a backend
POST/api/lemonade/rescaleDynamic rescale (adjust ctx_size/np for user count)

Gateway

MethodPathDescription
GET/api/gateway/statusGateway status
GET/api/gateway/consumersList consumers
POST/api/gateway/consumersCreate consumer
DELETE/api/gateway/consumers/{username}Delete consumer
POST/api/gateway/setupFull setup
POST/api/gateway/routeCreate/update AI proxy route
DELETE/api/gateway/routeDelete AI proxy route
POST/api/gateway/cleanupRemove all consumers and routes

Nginx

MethodPathDescription
GET/api/nginx/statusNginx status
POST/api/nginx/syncRegenerate nginx config from active instances
POST/api/nginx/cleanupRemove all THON nginx configs

Auth

MethodPathDescription
GET/api/auth/providersList enabled OIDC/OAuth providers
GET/api/auth/login/{provider}Start OAuth flow
GET/api/auth/callback/{provider}OAuth callback
POST/api/auth/logoutEnd session
GET/api/auth/meCurrent 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

VariableDescriptionDefault
SANDBOX_DOMAINSandbox server addresslocalhost:8080
SANDBOX_API_KEYSandbox API key(none)
SANDBOX_IMAGEDocker imagewaterpistol/thon:latest
THON_DB_PATHSQLite database path~/.thon/thon.db
THON_WORKSPACE_DIRWorkspace directory for groups~/.thon/workspace
THON_DOMAINDomain name for nginx and Let's Encrypt(none)
THON_SSL_PROVIDERSSL provider: auto, certbot, mkcert, opensslauto
THON_CERTBOT_EMAILEmail for Let's Encrypt registration(none)
THON_KILO_CONFIGPath to kilo.jsonc(none)
THON_VSCODE_SETTINGSPath to VS Code settings file(none)
THON_LOG_LEVELLogging levelINFO
PYTHON_VERSIONPython version in sandbox3.12

Lemonade Server

VariableDescriptionDefault
LEMONADE_HOSTLemonade server bind address0.0.0.0
LEMONADE_PORTLemonade server port13305
LEMONADE_API_KEYLemonade API key (regular)(none)
LEMONADE_ADMIN_API_KEYLemonade admin key (elevated)(none)

AI Gateway

VariableDescriptionDefault
GATEWAY_ENABLEDEnable AI Gatewayfalse
GATEWAY_ADMIN_URLAPISIX Admin API URLhttp://127.0.0.1:9180
GATEWAY_ADMIN_KEYAPISIX Admin API key(auto-detected)
GATEWAY_PROXY_PORTAPISIX proxy port9080
GATEWAY_REDIS_HOSTRedis host for rate limiting(none)
GATEWAY_REDIS_PORTRedis port6379
GATEWAY_REDIS_PASSWORDRedis password(none)
GATEWAY_RATE_LIMIT_TOKENSToken limit per consumer per window500
GATEWAY_RATE_LIMIT_WINDOWRate limit time window in seconds60
GATEWAY_MODEConsumer mode: per-user or per-groupper-user
GATEWAY_RATE_LIMIT_SCOPERate limit scope: per-user or per-modelper-user
GATEWAY_CONCURRENCY_LIMITConcurrency limit per consumer1

Dashboard & Database

VariableDescriptionDefault
DASHBOARD_HOSTFastAPI bind address0.0.0.0
DASHBOARD_PORTFastAPI port8100
DASHBOARD_SECRET_KEYFastAPI secret key(none)
DASHBOARD_DEBUGEnable debug/reload modefalse

Authentication

VariableDescriptionDefault
AUTH_LOCAL_PASSWORDSingle password for Streamlit dashboard(none)
AUTH_ENABLEDEnable OIDC authentication on REST APIfalse
AUTH_SESSION_SECRETHMAC secret for session tokens(none)
AUTH_GITHUB_CLIENT_IDGitHub OAuth App client ID(none)
AUTH_GITHUB_CLIENT_SECRETGitHub OAuth App client secret(none)
AUTH_GITLAB_CLIENT_IDGitLab OAuth App client ID(none)
AUTH_GITLAB_CLIENT_SECRETGitLab OAuth App client secret(none)
AUTH_LINKEDIN_CLIENT_IDLinkedIn OIDC client ID(none)
AUTH_LINKEDIN_CLIENT_SECRETLinkedIn OIDC client secret(none)

Langfuse

VariableDescriptionDefault
LANGFUSE_ENABLEDEnable Langfuse observabilityfalse
LANGFUSE_PUBLIC_KEYLangfuse public key(none)
LANGFUSE_SECRET_KEYLangfuse secret key(none)
LANGFUSE_BASEURLLangfuse API base URLhttps://cloud.langfuse.com

File Map

Unified CLI

FilePurpose
thon/__main__.pyEntry point: delegates to thon.cli.main()
thon/cli.pyUnified CLI: install, init, setup, gateway, run, launch, config, nginx, cleanup
thon/config.pyThonConfig + 14 Pydantic settings models (thon.yaml schema)
thon/install.pySystem package installer (config-free phase)
thon/interactive.pyInit wizard with 12 interactive steps

Legacy CLI

FilePurpose
main.pyEntry point; CLI; groups; sandbox orchestration; kilo.jsonc injection
scripts/setup.shOne-time host prerequisite installation
scripts/nginx_config.pyPer-port nginx config generation
scripts/ssl_cert.pySSL certificate generation (4 providers: auto, certbot, mkcert, openssl)
scripts/lemonade_server.pyLemonade server manager (Python CLI with rescale subcommand)
scripts/setup-lemonade.shAll-in-one Lemonade setup (shell, recommended)
scripts/apisix_gateway.pyAPISIX AI Gateway manager with per-model concurrency
scripts/build-amd-mi300x-llama-server.shBuild llama.cpp for AMD MI300X (gfx942)

Dashboard Application

FilePurpose
app/main.pyFastAPI application entry point; lifespan; route mounting
app/config.pyAppConfig — loaded from environment variables
app/models.pyPydantic domain models (InstanceInfo, InstanceState, UserInfo, etc.)
app/db.pySQLite persistence (5 tables: sandbox_records, app_settings, event_records, group_records, user_records)
app/nginx_service.pyCombined nginx config generator for API server
app/kilo_config.pyKilo config generation with skeleton merging
app/services/sandbox_service.pySandboxService — fleet CRUD operations
app/services/lemonade_service.pyLemonadeService — server status, rescaling
app/services/groups_service.pyGroupsService — group/user CRUD with Docker volume management
app/services/apisix_service.pyApisixService — APISIX Admin API wrapper
app/api/routes/instances.pyREST API: instance endpoints
app/api/routes/lemonade.pyREST API: Lemonade endpoints (including rescale)
app/api/routes/groups.pyREST API: groups CRUD, events, export
app/api/routes/users.pyREST API: users CRUD, launch/stop
app/api/routes/gateway.pyREST API: gateway management
app/api/routes/config_files.pyREST API: config file CRUD
app/api/routes/nginx.pyREST API: nginx sync/cleanup/status
app/api/routes/auth.pyREST API: OIDC/OAuth2 endpoints
app/auth/providers.pyOIDC/OAuth2 provider implementations
app/auth/sessions.pySessionStore — HMAC-signed session management

Dashboard Frontend

FilePurpose
dashboard/streamlit_app.pyStreamlit dashboard: 7 pages with sidebar navigation
dashboard/streamlit_styles.pyDark theme CSS injection

Development Tools

FilePurpose
development-tools/azure/bootstrap-ephemeral.shNVMe disk bootstrap for Azure ephemeral storage
development-tools/azure/ephemeral-setup.servicesystemd unit for boot-time NVMe setup
development-tools/azure/README.mdAzure ephemeral NVMe orchestration docs

Documentation Site

FilePurpose
fumadocs/Next.js + Fumadocs documentation site

Config

FilePurpose
config/groups.yaml.exampleGroups and users configuration template
config/kilo.jsonc.exampleKilo Code config template
config/kilo.jsonc.skeletonBase kilo.jsonc with experimental flags, permissions, MCP, indexing
config/vscode-settings.jsonc.exampleVS Code settings template
config/extensions.txtVS Code extensions list for Docker image
DockerfileSandbox 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

Stars

4 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages