ae946150959fd8111a31adb92684652cc453519d
slhaf-hub
Kotlin-based dynamic script host with HTTP APIs, root/sub token auth, script metadata, timeout control, and companion CLI/TUI tools.
Language:
- English: README.md
- 中文: README.zh-CN.md
Features
- Dynamic script loading from
scripts/*.hub.ktswithout restarting host - Root/Sub token authorization model
- Metadata in script comments (
@desc,@timeout,@param) - Compiled script cache (reuses compiled artifacts when script file is unchanged)
- Script CRUD + run + metadata APIs
- Subtoken management APIs
- Run concurrency limit (
--max-run-concurrency) - Script timeout (default 10s, script-level override)
Requirements
- JDK 17+
- Gradle (or Gradle wrapper)
Quick Start
Clone project:
git clone https://github.com/slhafzjw/slhaf-hub.git
cd slhaf-hub
Server
1) Run from terminal (Gradle)
./gradlew runWeb --args='--host=0.0.0.0 --port=8080 --scripts-dir=./scripts'
2) Run with Docker
docker build -t slhaf-hub:latest .
docker run --rm -p 8080:8080 \
-v "$(pwd)/scripts:/app/scripts" \
-e SLHAF_HUB_TOKEN=your-token \
-e MAX_RUN_CONCURRENCY=8 \
slhaf-hub:latest
3) Run with Docker Compose
# optional: export SLHAF_HUB_TOKEN=your-token
# optional: export SLHAF_HUB_PORT=8080
# optional: export MAX_RUN_CONCURRENCY=8
docker compose up -d --build
Health check:
curl http://127.0.0.1:8080/health
Clients
CLI
kotlin tools/slhaf-hub-cli.kts --base-url=http://127.0.0.1:8080 --token-file=./scripts/.host-api-token list
kotlin tools/slhaf-hub-cli.kts --token-file=./scripts/.host-api-token type
kotlin tools/slhaf-hub-cli.kts --token-file=./scripts/.host-api-token run hello --arg=name=Alice --arg=upper=true
TUI
kotlin tools/slhaf-hub-tui.kts --base-url=http://127.0.0.1:8080 --token-file=./scripts/.host-api-token
CLI/TUI env vars:
SLHAF_HUB_BASE_URLSLHAF_HUB_TOKENSLHAF_HUB_TOKEN_FILE
Auth Model
Auth headers:
Authorization: Bearer <token>(recommended)X-Host-Token: <token>
Token source priority:
SLHAF_HUB_TOKENenv varscripts/.host-api-token- Auto-generated token saved to
scripts/.host-api-token
Token types:
root: full accesssub: access to/health,/type, filtered/scripts, and allowed-script/meta/{script}+/run/{script}
Script Metadata (*.hub.kts)
Example:
// @desc: Demo greeting API
// @timeout: 10s
// @param: name | required=false | default=world | desc=Name to greet
val args: Array<String> = emptyArray()
val kv = args.mapNotNull {
val i = it.indexOf('=')
if (i <= 0) null else it.substring(0, i) to it.substring(i + 1)
}.toMap()
println("hello " + (kv["name"] ?: "world"))
Fields:
@desc: <text>@timeout: <value>@param: <name> | required=true|false | default=<value> | desc=<text>
Timeout formats:
500ms,10s,1m, or plain integer seconds (10)
Default timeout:
10s
Metadata Validation
POST /scripts/{script} and PUT /scripts/{script} validate metadata before saving.
On validation failure, server returns 400 Bad Request with:
- line-based reason details
- valid metadata examples
API Summary
Public:
GET /health
Authenticated:
GET /typeGET /scriptsGET /meta/{script}GET /run/{script}?k=vPOST /run/{script}?k=v
Root only:
GET /scripts/{script}POST /scripts/{script}PUT /scripts/{script}DELETE /scripts/{script}GET /subtokensGET /subtokens/{name}POST /subtokens/{name}PUT /subtokens/{name}DELETE /subtokens/{name}
Common statuses:
200,201,400,401,403,404,408(timeout)
Runtime Controls
Run concurrency:
- arg:
--max-run-concurrency=<N> - env (compose):
MAX_RUN_CONCURRENCY - default: number of available processors
The limit only applies to /run/* endpoints.
Testing
Run:
./gradlew test
Current automated coverage focuses on WebHost APIs:
- auth behavior
- script CRUD/meta/run
- metadata validation responses
- subtoken permission filtering
- run timeout behavior
Project Layout
src/main/kotlin/work/slhaf/hub: host implementationsrc/test/kotlin/work/slhaf/hub: automated testsscripts: runtime scripts and token/subtoken storagetools: standalone CLI/TUI scriptsDockerfile,docker-compose.yml: container deployment
Description
Languages
Kotlin
99.6%
Dockerfile
0.4%