The Hackathon Organizer Node

CLI Reference

Complete command-line reference for THON and Lemonade Server

CLI Reference

Synopsis

thon COMMAND [OPTIONS]

The thon CLI provides a unified entry point for interactive setup, configuration, and instance management. It reads from a single thon.yaml config file.

Commands

CommandDescription
thon initInteractive setup wizard (creates thon.yaml)
thon setupInstall prerequisites + configure from thon.yaml
thon runStart VS Code instances from thon.yaml
thon config showDisplay current config
thon config envExport config as .env file
thon config validateValidate thon.yaml
thon cleanupTear down all resources

Global Options

OptionDefaultDescription
--config PATH./thon.yamlPath to thon.yaml config file

init

python -m thon init [OPTIONS]

Interactive guided setup wizard that walks through every THON feature with sensible defaults, validates choices, and writes a thon.yaml config file.

OptionDefaultDescription
--non-interactivefalseGenerate config with defaults (no prompts)
--config PATH./thon.yamlOutput path for config file

setup

python -m thon setup [OPTIONS]

Installs system prerequisites and configures all components from thon.yaml:

  1. System prerequisites (setup.sh)
  2. SSL directory
  3. Lemonade Server (if lemonade.enabled)
  4. AI Gateway (if gateway.enabled)
  5. .env file generation
  6. Summary

run

python -m thon run [OPTIONS]

Starts VS Code instances from thon.yaml. Delegates to scripts/main.py.

OptionDefaultDescription
--group GROUP(all)Run only this group
--config PATH./thon.yamlPath to config file

config show

python -m thon config show [OPTIONS]

Displays the full resolved config as YAML.

config env

thon config env [OPTIONS]

Exports configuration as a .env file.

OptionDefaultDescription
--output PATH.envOutput .env file path

config validate

thon config validate [OPTIONS]

Validates thon.yaml for common errors (missing groups, auth without providers, etc.).

cleanup

python -m thon cleanup

Tear down all resources: nginx configs, Lemonade server, and AI Gateway.

Examples

# Interactive setup wizard
python -m thon init

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

# Install prerequisites and configure
python -m thon setup

# Start instances
python -m thon run

# Start only one group
python -m thon run --group alpha

# Validate config
python -m thon config validate

# Export .env file
python -m thon config env --output .env

# Clean up all resources
python -m thon cleanup

main.py - VS Code Instance Orchestrator

Synopsis

python ./scripts/main.py [OPTIONS]

Options

Core Options

OptionTypeDefaultDescription
--groups FILEstring(none)Path to groups.yaml file
--group GROUPstring(all)Run only this group (works with --groups or --from-db)
--from-dbflagfalseRead groups/users from the database instead of a YAML file
--port PORTint8443Starting port for code-server instances
--timeout MINint0Timeout in minutes (0 = no timeout)

Server Connection

OptionTypeDefaultDescription
--domain DOMAINstringlocalhost:8080Sandbox server domain
--api-key KEYstring(none)Sandbox API key

Docker Options

OptionTypeDefaultDescription
--image IMAGEstringwaterpistol/thon:latestDocker image for sandbox
--python-version VERstring3.11Python version in sandbox

Security

OptionTypeDefaultDescription
--secureflagfalseEnable per-user password authentication

Network

OptionTypeDefaultDescription
--external-ip IPstring(auto-detect)External IP for SSL cert and URLs
--ssl-dir DIRstring/etc/nginx/sslSSL certificate storage directory
--no-nginxflagfalseDisable nginx, use direct HTTP access

Workspace

OptionTypeDefaultDescription
--workspace-dir DIRstring(none)Host dir for persistent bind mounts

Lemonade Integration

OptionTypeDefaultDescription
--lemonade KILO_JSONstring(none)Path to kilo.json for LLM config injection
--vscode-settings JSONstring(none)VS Code settings file to inject

AI Gateway

OptionTypeDefaultDescription
--gatewayflagfalseEnable APISIX AI Gateway with rate limiting
--gateway-per-groupflagfalseOne consumer per group (shared API key) instead of per user
--gateway-redis-host HOSTstring(none)Redis host for shared rate limiting
--gateway-rate-limit Nint500Token limit per consumer per time window
--gateway-time-window Nint60Rate limit time window in seconds

Maintenance

OptionTypeDefaultDescription
--cleanupflagfalseRemove all nginx configs and exit

Examples

Basic Usage

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

# All groups with nginx SSL
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

# Start instances from database instead of YAML
python ./scripts/main.py --from-db --external-ip 1.2.3.4

# Start a specific group from database
python ./scripts/main.py --from-db --group beta --external-ip 1.2.3.4

With Security

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

With Persistence

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

With Lemonade

# Local LLM inference
python ./scripts/main.py --groups groups.yaml --external-ip 1.2.3.4 --lemonade kilo.json

With AI Gateway

# Per-user rate limiting (each user gets own API key)
python ./scripts/main.py --groups groups.yaml --external-ip 1.2.3.4 --gateway

# Per-group rate limiting (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

Custom Settings

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

No Nginx

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

Cleanup

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

setup-lemonade.sh - Lemonade Server Setup

Synopsis

bash ./scripts/setup-lemonade.sh [OPTIONS]

Options

OptionDefaultDescription
--groups FILE(none)groups.yaml for user count
--group GROUP(all)Filter to single group
--num-users N1Override parallel user count
--port PORT13305Server port
--host HOST0.0.0.0Bind address
--backend BACKENDautollama.cpp backend: auto, vulkan, cpu
--ctx-size SIZE262144Per-user context size
--model MODELunsloth/gemma-4-31B-it-GGUF:Q8_K_XLHuggingFace checkpoint
--model-name NAMEgemma-4-31b-itShort model name
--mmproj FILEmmproj-BF16.ggufVision mmproj filename
--external-ip IP(auto)External IP for kilo.json
--generate-keysfalseGenerate API keys
--no-prefer-system(system)Use bundled llama.cpp
--llamacpp-bin PATH/usr/local/bin/llama-serverSystem binary path
--kilo-config PATH./kilo.jsonOutput path for kilo.json
--embeddingtrueEnable embedding model for semantic indexing
--no-embeddingfalseDisable embedding model
--embedding-model MODELSuperPauly/harrier-oss-v1-0.6b-gguf:harrier-oss-v1-0.6B-BF16Embedding model HuggingFace checkpoint
--embedding-model-name NAMEharrier-oss-v1-0.6bShort name for embedding model
-h, --helpShow help message

Environment Variables

VariableDescription
LEMONADE_PORTServer port
LEMONADE_HOSTBind address
LEMONADE_BACKENDllama.cpp backend
LEMONADE_CTX_SIZEPer-user context size
LEMONADE_MODELHuggingFace checkpoint
LEMONADE_MODEL_NAMEShort model name
LEMONADE_EXTERNAL_IPExternal IP
LEMONADE_GENERATE_KEYSGenerate API keys (true/false)
LEMONADE_NUM_USERSParallel user count
LEMONADE_KILO_CONFIGkilo.json output path
LEMONADE_PREFER_SYSTEMPrefer system binary (true/false)
LEMONADE_LLMACPP_BINSystem binary path
LEMONADE_MMPROJmmproj filename
LEMONADE_EMBEDDINGEnable embedding model (true/false)
LEMONADE_EMBEDDING_MODELEmbedding model HuggingFace checkpoint
LEMONADE_EMBEDDING_MODEL_NAMEShort name for embedding model

Examples

Basic Setup

bash setup-lemonade.sh --generate-keys --external-ip 1.2.3.4

With Groups

bash setup-lemonade.sh --groups groups.yaml --generate-keys --external-ip 1.2.3.4

Without Embedding Model

bash setup-lemonade.sh --generate-keys --external-ip 1.2.3.4 --no-embedding

Custom Embedding Model

bash setup-lemonade.sh \
    --embedding-model some-org/embedding-model-GGUF:Q8_0 \
    --embedding-model-name my-embedding \
    --generate-keys \
    --external-ip 1.2.3.4

Custom Model

bash setup-lemonade.sh \
    --model Qwen/Qwen2.5-Coder-7B-Instruct-GGUF:Q4_K_M \
    --model-name qwen-coder-7b \
    --generate-keys \
    --external-ip 1.2.3.4

Custom Binary

bash setup-lemonade.sh \
    --llamacpp-bin /opt/llama.cpp/llama-server \
    --generate-keys \
    --external-ip 1.2.3.4

lemonade_server.py - Python CLI Wrapper

Synopsis

python ./lemonade_server.py COMMAND [OPTIONS]

Commands

CommandDescription
installInstall lemonade-server via PPA
configureConfigure server settings
startStart the server
stopStop the server
restartRestart the server
statusCheck server status
pullPull a model to local cache
runFull setup + keep alive
count-usersCount users from groups.yaml
write-model-configsWrite user_models.json and recipe_options.json
generate-kilo-configGenerate kilo.json for Kilo Code
cleanupStop server and clean up

install

python lemonade_server.py install

Installs lemonade-server from PPA.

configure

python lemonade_server.py configure [OPTIONS]
OptionDefaultDescription
--port PORT13305Server port
--host HOST0.0.0.0Bind address
--llamacpp-backend BACKENDautoBackend: auto, vulkan, cpu
--ctx-size SIZE4096Default context size
--max-loaded-models N1Max models per type slot
--generate-keysfalseGenerate API keys
--prefer-systemtruePrefer system llama.cpp
--no-prefer-systemUse bundled llama.cpp
--llamacpp-bin PATH/usr/local/bin/llama-serverSystem binary path
--kilo-config PATH(none)Generate kilo.json
--model MODEL(default)Model for kilo.json
--external-ip IP(auto)External IP for kilo.json

pull

python lemonade_server.py pull --model MODEL
OptionDefaultDescription
--model MODEL(required)HuggingFace checkpoint

run

python lemonade_server.py run [OPTIONS]

Full setup: install + configure + start + pull model + keep alive.

OptionDefaultDescription
--model MODELunsloth/gemma-4-31B-it-GGUF:Q8_K_XLHuggingFace checkpoint
--model-name NAMEgemma-4-31b-itShort model name
--groups FILE(none)groups.yaml for user count
--group GROUP(all)Filter to single group
--num-users N1Override parallel user count
--port PORT13305Server port
--host HOST0.0.0.0Bind address
--llamacpp-backend BACKENDautoBackend: auto, vulkan, cpu
--ctx-size SIZE4096Default context size
--generate-keysfalseGenerate API keys
--external-ip IP(auto)External IP
--kilo-config PATH(auto)kilo.json output path
--prefer-systemtruePrefer system binary
--llamacpp-bin PATH/usr/local/bin/llama-serverSystem binary path
--mmproj FILEmmproj-BF16.ggufVision mmproj filename
--skip-installfalseSkip installation check
--embeddingtrueEnable embedding model for semantic indexing
--no-embeddingfalseDisable embedding model
--embedding-model MODELSuperPauly/harrier-oss-v1-0.6b-gguf:harrier-oss-v1-0.6B-BF16Embedding model checkpoint
--embedding-model-name NAMEharrier-oss-v1-0.6bShort name for embedding model

write-model-configs

python lemonade_server.py write-model-configs [OPTIONS]
OptionDefaultDescription
--model MODEL(default)HuggingFace checkpoint
--model-name NAMEgemma-4-31b-itShort model name
--num-users N1Parallel user count
--llamacpp-backend BACKENDautoBackend
--mmproj FILEmmproj-BF16.ggufVision mmproj filename
--embeddingtrueAlso write embedding model configs
--no-embeddingfalseSkip embedding model configs
--embedding-model MODELSuperPauly/harrier-oss-v1-0.6b-gguf:harrier-oss-v1-0.6B-BF16Embedding model checkpoint
--embedding-model-name NAMEharrier-oss-v1-0.6bShort name for embedding model

generate-kilo-config

python lemonade_server.py generate-kilo-config [OPTIONS]
OptionDefaultDescription
--model MODEL(default)HuggingFace checkpoint
--model-name NAMEgemma-4-31b-itShort model name
--external-ip IP(auto)External IP
--output PATHkilo.jsonOutput path
--api-key KEY(none)API key
--admin-api-key KEY(none)Admin API key
--embedding-model-name NAMEharrier-oss-v1-0.6bEmbedding model name for indexing config
--no-embeddingfalseOmit indexing section from kilo.json

Examples

# Full setup (with embedding model)
python lemonade_server.py run --groups groups.yaml --generate-keys --external-ip 1.2.3.4

# Full setup without embedding model
python lemonade_server.py run --groups groups.yaml --generate-keys --external-ip 1.2.3.4 --no-embedding

# Just configure
python lemonade_server.py configure --generate-keys --external-ip 1.2.3.4

# Write model configs only (includes embedding by default)
python lemonade_server.py write-model-configs --num-users 6

# Generate kilo.json without embedding/indexing section
python lemonade_server.py generate-kilo-config --admin-api-key YOUR_KEY --external-ip 1.2.3.4 --no-embedding

Environment Variables

Sandbox Server

VariableDefaultDescription
SANDBOX_DOMAINlocalhost:8080Sandbox server address
SANDBOX_API_KEY(none)Sandbox API key
SANDBOX_IMAGEwaterpistol/thon:latestDocker image
PYTHON_VERSION3.11Python in sandbox

Lemonade Server

VariableDescription
LEMONADE_API_KEYAPI key for regular endpoints
LEMONADE_ADMIN_API_KEYAPI key for admin endpoints
LEMONADE_EMBEDDINGEnable embedding model (true/false)
LEMONADE_EMBEDDING_MODELEmbedding model HuggingFace checkpoint
LEMONADE_EMBEDDING_MODEL_NAMEShort name for embedding model

AI Gateway

VariableDescription
GATEWAY_ENABLEDEnable AI Gateway (true/false)
GATEWAY_ADMIN_URLAPISIX Admin API URL
GATEWAY_ADMIN_KEYAPISIX Admin API key
GATEWAY_PROXY_PORTAPISIX proxy port
GATEWAY_REDIS_HOSTRedis host for rate limiting
GATEWAY_REDIS_PORTRedis port
GATEWAY_REDIS_PASSWORDRedis password
GATEWAY_RATE_LIMIT_TOKENSToken limit per consumer per window
GATEWAY_RATE_LIMIT_WINDOWTime window in seconds
GATEWAY_MODEConsumer mode: per-user or per-group

Database

VariableDescription
THON_DB_PATHSQLite database path (default: ~/.thon/thon.db)
THON_WORKSPACE_DIRWorkspace directory for groups

Authentication

VariableDefaultDescription
AUTH_ENABLEDfalseEnable OIDC authentication on the REST API
AUTH_SESSION_SECRET(none)HMAC secret for signing session tokens
AUTH_LOCAL_PASSWORD(none)Single password for Streamlit dashboard access
AUTH_GITHUB_CLIENT_ID(none)GitHub OAuth App client ID
AUTH_GITHUB_CLIENT_SECRET(none)GitHub OAuth App client secret
AUTH_GITLAB_CLIENT_ID(none)GitLab OAuth App client ID
AUTH_GITLAB_CLIENT_SECRET(none)GitLab OAuth App client secret
AUTH_LINKEDIN_CLIENT_ID(none)LinkedIn OIDC client ID
AUTH_LINKEDIN_CLIENT_SECRET(none)LinkedIn OIDC client secret

apisix_gateway.py - AI Gateway Manager

Synopsis

python scripts/apisix_gateway.py COMMAND [OPTIONS]

Commands

CommandDescription
setupFull gateway setup: create routes + consumers from groups.yaml
create-consumerCreate a single consumer with API key
delete-consumerDelete a consumer by username
generate-kiloGenerate kilo.json for a consumer
statusCheck gateway status
cleanupRemove all consumers and routes

setup

python scripts/apisix_gateway.py setup [OPTIONS]

Creates two APISIX routes:

  • /v1/chat/completions — chat completions via ai-proxy-multi
  • /v1/embeddings — embedding requests via upstream proxy (when --no-embedding is not set)
OptionDefaultDescription
--groups FILE(none)Path to groups.yaml
--group GROUP(all)Filter to single group
--lemonade-url URLhttp://127.0.0.1:13305Lemonade server URL
--lemonade-api-key KEY(none)Lemonade API key
--lemonade-model MODELuser.gemma-4-31b-itLemonade model name
--per-groupfalseOne consumer per group with shared API key
--admin-key KEY(default)APISIX Admin API key
--admin-port PORT9180APISIX Admin API port
--proxy-port PORT9080APISIX proxy port
--redis-host HOST(none)Redis host for rate limiting
--redis-port PORT6379Redis port
--redis-password PW(none)Redis password
--rate-limit N500Token limit per consumer per time window
--time-window N60Rate limit time window in seconds
--generate-kilofalseGenerate kilo.json for each consumer
--external-ip IP(auto)External IP for kilo.json base URL
--embedding-model MODELuser.harrier-oss-v1-0.6bEmbedding model name for Lemonade
--no-embeddingfalseDisable embedding route creation

create-consumer

python scripts/apisix_gateway.py create-consumer --username alice [OPTIONS]
OptionDefaultDescription
--username(required)Consumer username
--api-key(auto)API key (auto-generated if omitted)
--rate-limit500Token limit per time window
--time-window60Time window in seconds

generate-kilo

python scripts/apisix_gateway.py generate-kilo --username alice --api-key KEY [OPTIONS]
OptionDefaultDescription
--username(required)Consumer username
--api-key(required)Consumer API key
--proxy-port9080APISIX proxy port
--external-ip127.0.0.1External IP for gateway URL
--modeluser.gemma-4-31b-itModel name for kilo.json
--embedding-modeluser.harrier-oss-v1-0.6bEmbedding model name for indexing config
--no-embeddingfalseOmit indexing section from kilo.json

Examples

# Full setup from groups.yaml (includes embedding route)
python scripts/apisix_gateway.py setup --groups groups.yaml \
    --lemonade-url http://127.0.0.1:13305

# Setup without embedding route
python scripts/apisix_gateway.py setup --groups groups.yaml \
    --lemonade-url http://127.0.0.1:13305 --no-embedding

# Per-group setup with Redis rate limiting
python scripts/apisix_gateway.py setup --groups groups.yaml \
    --lemonade-url http://127.0.0.1:13305 --per-group --redis-host 127.0.0.1

# Create single consumer
python scripts/apisix_gateway.py create-consumer --username alice --rate-limit 500

# Generate kilo.json (with embedding/indexing config)
python scripts/apisix_gateway.py generate-kilo --username alice --api-key KEY \
    --external-ip 1.2.3.4

# Generate kilo.json without embedding
python scripts/apisix_gateway.py generate-kilo --username alice --api-key KEY \
    --external-ip 1.2.3.4 --no-embedding

# Cleanup all gateway resources
python scripts/apisix_gateway.py cleanup

On this page