Your own private, OpenAI-compatible model server for GGUF models, with the model sandboxed in a locked-down worker.
Developer toolsC++ · CMakeGitHub @Yog-SothoOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Sandboxed, OpenAI-compatible LLM server for GGUF models: drop in a model → get a ChatGPT-style API → the model runs in a padded cell. llama.cpp engine · continuous batching + prompt cache (faster than llama-server under load) · namespace/Landlock/seccomp sandbox · hashed API keys · TLS/mTLS · Prometheus metrics · signed static binaries & Docker
Taken from the repository README
Loading the full README.
Read the full README ↗
You found a cool model on Hugging Face. You want your apps, scripts and agents to talk to it the way they talk to ChatGPT. And you'd rather not hand a multi-gigabyte binary blob from a stranger the keys to your server.
LiSa-LLM (Lightweight Sandboxed LLM server) does exactly that. One small binary, one
model file, and you get the OpenAI API (/v1/chat/completions, streaming and all) on your own
hardware. Every OpenAI SDK and most AI tools work with it unchanged: point them at your server
and go.
The twist: LiSa never trusts the model file. GGUF files are parsed by complicated code, and a booby-trapped one could exploit it. So the part of LiSa that talks to the network never even opens the model. A separate worker process does, after locking itself into a padded cell: no files, no network, no other processes, no privileges, and a system-call filter that kills it the moment it tries anything funny.
And it's quick: continuous batching and a prompt cache make it faster than llama.cpp's own
llama-server when several clients talk to it at once, sandbox included.
What people build with it:
usage with cached
tokens, OpenAI-style errors. Chat templates come straight from the GGUF file (54 built-in formats).parallel_slots requests generate together, and new prompts
join the running batch instead of waiting their turn.Retry-After), timeouts, TLS 1.3, mutual TLS, HSTS./healthz, /readyz, Prometheus /metrics (latency, queue wait, time to first
token, tokens/s, worker restarts), JSON logs with request ids, graceful drain on SIGTERM.The broker talks to the world and never reads the model. The worker reads the model and can't talk to the world. A malicious GGUF gets a padded cell, not your server.
New requests join the running batch the moment a slot frees up, and a prompt prefix that's already cached is copied instead of computed again.
Same machine, same model, same llama.cpp commit. LiSa runs with the sandbox on and still comes out ahead, by up to 31 %.
Linux binary (x86_64 or aarch64, any distro with glibc 2.35+):
VERSION=1.0.1 ARCH=x86_64 # or aarch64
curl -LO https://github.com/Yog-Sotho/LiSa-LLM/releases/download/v$VERSION/lisa-$VERSION-linux-$ARCH.tar.gz
tar xzf lisa-$VERSION-linux-$ARCH.tar.gz
sudo install -m 755 lisa-$VERSION-linux-$ARCH/bin/lisa /usr/local/bin/
mkdir -p models
curl -L -o models/model.gguf \
https://huggingface.co/Qwen/Qwen2.5-0.5B-Instruct-GGUF/resolve/main/qwen2.5-0.5b-instruct-q4_k_m.gguf
export LISA_API_KEYS="$(openssl rand -hex 24)"; echo "Your API key: $LISA_API_KEYS"
lisa --model models/model.gguf
Look for "Sandboxed worker ready" in the log: the model is loaded, inside the cell. 🎉
KEY="$(openssl rand -hex 24)"; echo "Your API key: $KEY"
docker run --rm -p 8080:8080 -v ./models:/models:ro \
-e LISA_API_KEYS="$KEY" -e LISA_API_KEY= \
ghcr.io/yog-sotho/lisa-llm:1.0.1 # :1.0.1-cuda for NVIDIA GPUs
docker-compose.yml has ready-made CPU, sandboxed and GPU services. Building
from source, the sandbox in containers and the support matrix: Installation.
Release archives and images are signed (Sigstore, keyless) and carry SLSA provenance and SBOMs.
Checking takes one command each, e.g.
gh attestation verify lisa-1.0.1-linux-x86_64.tar.gz --repo Yog-Sotho/LiSa-LLM.
All the commands: Verifying a release.
Anything that speaks OpenAI speaks LiSa:
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8080/v1", api_key="<your key>")
stream = client.chat.completions.create(
model="model", # any name: one model is loaded
messages=[{"role": "user", "content": "Write a haiku about sandboxes."}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")
curl http://localhost:8080/v1/chat/completions \
-H "Authorization: Bearer $LISA_API_KEYS" -H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Hello!"}], "max_tokens": 50}'
| Area | Supported |
|---|---|
| Routes | /v1/chat/completions, /v1/completions, /v1/models, plus /healthz, /readyz, /metrics |
| Sampling | temperature, top_p, top_k, min_p, presence/frequency/repeat penalties, seed, stop |
| Output | streaming (SSE) with optional usage, JSON mode (response_format: json_object, grammar-constrained) |
| Not (yet) | tools, json_schema, logprobs, n > 1, images: refused with a clear 400, never silently ignored |
Everything else, with examples: Using the API.
Qwen2.5-0.5B-Instruct Q4_K_M on 4 vCPUs, 64 tokens per request, every prompt distinct. LiSa with
the sandbox on and 8 slots versus llama-server -np 8 built from the same llama.cpp commit:
| Concurrent clients | LiSa-LLM | llama-server |
|---|---|---|
| 1 | 37.4 tok/s | 35.6 tok/s |
| 2 | 48.8 tok/s | 42.8 tok/s |
| 4 | 61.9 tok/s | 47.2 tok/s |
| 8 | 66.7 tok/s | 59.2 tok/s |
| 8, shared prompt prefix | 90.4 tok/s | 82.8 tok/s |
Measure your own box with python3 bench/bench.py --url http://127.0.0.1:8080 --key "$KEY".
Methodology, time-to-first-token numbers, how batching works and how to tune it:
Performance.
The network-facing broker checks keys, rate-limits, queues and validates every request, and never parses the model. The worker parses it inside user, mount, network, PID, IPC, UTS and cgroup namespaces, on an empty read-only root, with no capabilities, Landlock and a seccomp allow-list. The broker treats the worker as hostile too: a malformed reply gets it killed and restarted. If the sandbox can't be set up, LiSa refuses to start rather than run unprotected.
Put it on a network the right way: Security and the sandbox has the checklist, and the threat model has every rule and its limits. Found a hole? Please report it privately: SECURITY.md.
One YAML file (config.yaml, every option commented), LISA_<KEY> environment
variables for each setting, and a few flags (--model, --host, --port, --check-config,
--health-check, --hash-key). The ones you'll touch first:
| Setting | Default | What it does |
|---|---|---|
model_path | The GGUF file to serve | |
listen_addr / listen_port | 127.0.0.1 / 8080 | Where to listen |
api_keys | Keys or sha256: digests (lisa --hash-key) | |
parallel_slots | 4 | Requests generated together |
max_context | 2048 | Context window per slot |
rate_limit_per_minute | 0 (off) | Per-key rate limit |
enable_tls | false | HTTPS (plus client_ca_file for mutual TLS) |
sandbox_enabled | true | Run the model in the padded cell |
All of them: Configuration. Deploying with Docker, Kubernetes, systemd, TLS or Prometheus: Deployment.
Already available (v1.0.1):
On the wishlist:
json_schema structured output and tool callingIdeas and pull requests welcome!
Read the docs as a website at yog-sotho.github.io/LiSa-LLM, or right here:
| Guide | What you'll learn |
|---|---|
| Installation | Binaries, containers, building from source, verifying releases |
| Quick start | Your first answer in 5 minutes, and hooking up your tools |
| Using the API | Requests, streaming, JSON mode, prompt caching, errors |
| Configuration | Every setting, flag and environment variable |
| Deployment | Docker, Compose, TLS, health checks, Prometheus, systemd |
| Security and the sandbox | How the sandbox works and how to expose LiSa safely |
| Performance | Benchmarks, batching, prompt cache, memory and tuning |
| Troubleshooting | Common errors and their fixes |
| FAQ | Quick answers |
Behind the scenes: the threat model, and the codebase audit that started the v1.0.0 rebuild, with the progress log of every phase.
Fork it, hack it, open a PR, and keep the humour and the seccomp filters tight. CONTRIBUTING.md explains the build presets, the tests (sanitizers, sandbox, fuzzing) and what CI checks.
Found a bug? Open an issue with your OS, kernel
(uname -r), how you run LiSa and the log lines around the error. Security issues go through
SECURITY.md instead.
MIT, because sharing is caring, even when sandboxing. Third-party licences are listed in THIRD-PARTY-NOTICES.md and ship with every release.
Made with ❤️ by Yog-Sotho
“May your logits be low‑entropy and your cgroups never OOM.”
If LiSa keeps your models in line, a ⭐ on GitHub means the world.