completely ridiculous API (crAPI) will help you to understand the ten most critical API security risks. crAPI is vulnerable by design, but you'll be able to safely run it to educate/train yourself.
crAPI is modern, built on top of a microservices architecture. When time has come to buy your first car, sign up for an account and start your journey. To know more about crAPI, please check crAPI's overview.
This is a fork of OWASP/crAPI, set up to run on macOS with Colima. It adds a one-command quick start, a chatbot that works with any LLM provider (OpenAI, Anthropic, xAI, and other OpenAI-compatible APIs), and its own port layout.
- Quick Start
- Chatbot LLM Setup
- Local Environment
- Other Ways to Run crAPI
- Challenges and Documentation
- Development Workflow and Releases
- Getting Help
Run quickstart.sh from the repo root:
./quickstart.sh # binds to all interfaces (reachable from your LAN)
./quickstart.sh --localhost-only # binds to 127.0.0.1 only
./quickstart.sh --vulnerable # enable all vulnerabilities (shell injection, log4j)
./quickstart.sh --yes # don't ask before installing or starting DockerThe same command works on macOS and Linux. The script detects the platform and gets Docker running before it deploys. On macOS that means Colima (see Docker runtime on macOS and Linux).
Optional, for the AI chatbot: before the first run, copy deploy/docker/llm.env.example to deploy/docker/llm.env.local and add your LLM provider and key. See Chatbot LLM Setup. Without it everything else works, and the chatbot reports Model needs to be initialized.
From a fresh clone that is the whole setup:
git clone https://gitlab.com/LinuxUser255/crAPI.git && cd crAPI
cp deploy/docker/llm.env.example deploy/docker/llm.env.local # optional: then add your key
./quickstart.shmain is always deployable. To pin a tested version instead, clone a release tag, e.g. git clone --branch v1.0.0 https://gitlab.com/LinuxUser255/crAPI.git. All versions and their notes are on the releases page and in CHANGELOG.md.
quickstart.sh pulls the upstream images and builds crapi-chatbot, crapi-community, crapi-web, crapi-identity and dealer-connect from local source, since those carry this fork's changes. When it finishes, open http://localhost:8889; the DealerConnect OAuth demo is at http://localhost:8900. crapi-dev.sh wraps the same operations in an interactive menu.
The chatbot is provider-agnostic. It works with OpenAI, Anthropic, and any API that speaks the OpenAI protocol: xAI (Grok), Google Gemini, Groq, Mistral, DeepSeek, OpenRouter, or a local Ollama. All of its LLM settings, key included, live in one file, deploy/docker/llm.env.local. Docker Compose loads that file into the crapi-chatbot container. It is gitignored (*.local), so the key never reaches git, while docker-compose.yml itself is tracked and contains no secrets.
For a diagram of how the key travels from that file to the container to the provider, and what keeps it hidden along the way, see the Chatbot API Key Flow.
Setting in llm.env.local |
What it does | If left empty |
|---|---|---|
LLM_PROVIDER |
Which client library to use: openai (OpenAI and every OpenAI-compatible API) or anthropic |
openai |
DEFAULT_MODEL |
Model id, exactly as the provider names it. It must support tool calling | gpt-4o-mini |
LLM_BASE_URL |
The API endpoint. Set it for OpenAI-compatible providers | the provider's default |
EMBEDDINGS_PROVIDER |
Chat-history memory: openai (text-embedding-3-large) or local (all-MiniLM-L6-v2, built into the image, no key needed) |
openai for plain OpenAI, local for everything else |
CHATBOT_LLM_API_KEY |
The provider's API key | no key: the chat window asks for one |
Upstream's CHATBOT_OPENAI_API_KEY, exported in your shell before docker compose up, still works as a fallback for the key.
Copy the row for your provider. The model must support tool (function) calling, because the chatbot's agent runs on tools.
| Provider | LLM_PROVIDER |
LLM_BASE_URL |
Model (example) | Get a key |
|---|---|---|---|---|
| OpenAI | openai |
(empty) | gpt-4o-mini |
platform.openai.com |
| Anthropic (Claude) | anthropic |
(empty) | claude-sonnet-5-5 |
console.anthropic.com |
| xAI (Grok) | openai |
https://api.x.ai/v1 |
grok-4.3 |
console.x.ai |
| Google Gemini | openai |
https://generativelanguage.googleapis.com/v1beta/openai/ |
from the provider's model list | aistudio.google.com |
| Groq | openai |
https://api.groq.com/openai/v1 |
from the provider's model list | console.groq.com |
| Mistral | openai |
https://api.mistral.ai/v1 |
from the provider's model list | console.mistral.ai |
| DeepSeek | openai |
https://api.deepseek.com |
from the provider's model list | platform.deepseek.com |
| OpenRouter (many vendors, one key) | openai |
https://openrouter.ai/api/v1 |
from the provider's model list | openrouter.ai |
| Ollama on your machine (no key, no cost) | openai |
http://host.docker.internal:11434/v1 |
a model you've pulled, e.g. via ollama pull |
not needed; set the key to any text, e.g. ollama |
Only OpenAI, Anthropic and xAI have been checked against this code; requests to each reached the provider. The others follow the same OpenAI-compatible path but are untested here. Leave EMBEDDINGS_PROVIDER empty unless you have a reason not to.
For Ollama under Colima, if host.docker.internal doesn't resolve from the container, use host.lima.internal instead.
Other providers LangChain supports natively (for example google_genai, mistralai, bedrock) work too. Add the matching langchain-* package to services/chatbot/requirements.txt, rebuild, and set LLM_PROVIDER to LangChain's provider name.
-
Create a key on your provider's site (the Get a key column above). Ollama needs none.
-
Copy it once and keep it in your password manager. Most providers won't show it again.
-
Optional: check that the key works and list the model ids it can use.
read -skeeps the key out of your shell history and off the screen:read -s "LLM_KEY?API key: " # zsh; in bash use: read -rsp "API key: " LLM_KEY # OpenAI or any OpenAI-compatible provider: use its base URL (OpenAI's is https://api.openai.com/v1) curl -s https://api.x.ai/v1/models -H "Authorization: Bearer $LLM_KEY" | grep -o '"id": *"[^"]*"' # Anthropic curl -s https://api.anthropic.com/v1/models -H "x-api-key: $LLM_KEY" -H "anthropic-version: 2023-06-01" | grep -o '"id": *"[^"]*"' unset LLM_KEY
Pick one of the listed ids for
DEFAULT_MODEL.
-
From the repo root, copy the template and make it readable only by you:
cp deploy/docker/llm.env.example deploy/docker/llm.env.local chmod 600 deploy/docker/llm.env.local
-
Confirm git ignores it. This should print a line naming
.gitignoreand*.local:git check-ignore -v deploy/docker/llm.env.local
If it prints nothing, stop. The file isn't ignored, and a key put in it would be committed.
-
Open
deploy/docker/llm.env.localin your editor and fill in your row from Supported providers. For example:# Anthropic LLM_PROVIDER=anthropic DEFAULT_MODEL=claude-sonnet-5-5 LLM_BASE_URL= EMBEDDINGS_PROVIDER= CHATBOT_LLM_API_KEY=sk-ant-your-key-here# xAI, or any other OpenAI-compatible provider (swap in its base URL and model) LLM_PROVIDER=openai DEFAULT_MODEL=grok-4.3 LLM_BASE_URL=https://api.x.ai/v1 EMBEDDINGS_PROVIDER= CHATBOT_LLM_API_KEY=xai-your-key-hereRules for the file:
- One
NAME=valueper line, with noexportin front and no quotes around values. - Keep the names exactly as shown. Provider-specific names such as
XAI_API_KEYorANTHROPIC_API_KEYreach the container, but the chatbot doesn't read them. - Only the key goes after
CHATBOT_LLM_API_KEY=. A value likeCHATBOT_LLM_API_KEY=XAI_API_KEY=xai-...sends the textXAI_API_KEY=xai-...as the key.
- One
Never put a key in these tracked files instead:
deploy/docker/docker-compose.ymlordeploy/docker/.envdeploy/docker/llm.env.example, the templateservices/chatbot/.env: this file is only for running the chatbot outside Docker. Docker never reads it..gitignoredoesn't protect it either, because git already tracks it.
-
Fresh start: run
./quickstart.shfrom the repo root. It reports whether it foundllm.env.local. -
Stack already running: recreate only the chatbot. From
deploy/docker:docker compose up -d --no-deps crapi-chatbot
After pulling new chatbot code, run
docker compose build crapi-chatbotfirst.
A changed setting reaches the container only when it is recreated. docker compose restart does not re-read llm.env.local, so use up -d instead.
These commands show the key's prefix and length, never the key itself:
docker exec crapi-chatbot sh -c 'echo "prefix=${CHATBOT_LLM_API_KEY%%-*} length=${#CHATBOT_LLM_API_KEY}"'
# expect your provider's prefix (sk, xai, gsk, ...) and a non-zero length
docker exec crapi-chatbot printenv LLM_PROVIDER LLM_BASE_URL DEFAULT_MODEL EMBEDDINGS_PROVIDER
# expect the values from your llm.env.local, one per line. An empty value prints as a blank line.
docker logs --tail 50 crapi-chatbotAvoid plain docker compose config. It prints the resolved file, key included. Use docker compose config --quiet to validate.
Then open crAPI at http://localhost:8889, log in, open the chat window, and ask it something like "What endpoints does crAPI expose?". A real answer means the key, provider, and model all work. The chat window's own key prompt says "OpenAI API key", because it comes from the prebuilt crapi-web image. It only appears when the container has no key, and it accepts a key for whichever provider is configured.
| Symptom | Cause and fix |
|---|---|
UI says Model needs to be initialized, or the API returns Missing OpenAI API key |
The container has no key. Check that deploy/docker/llm.env.local exists and sets CHATBOT_LLM_API_KEY, then recreate the container (Step 3) and run the Step 4 check. |
401, authentication_error, or Incorrect API key in docker logs crapi-chatbot |
Quotes around the key, a stray NAME= prefix in the value, or a key sent to the wrong provider. Check that LLM_PROVIDER and LLM_BASE_URL match the key's provider. For example, an xAI key with an empty base URL goes to OpenAI. |
404, model not found, or not_found_error |
DEFAULT_MODEL isn't an id your key can use. List the models (Step 1, item 3) and set one of them. |
Unsupported model_provider or Unable to import langchain_... |
LLM_PROVIDER is misspelled, or names a provider whose langchain-* package isn't installed. Use openai or anthropic, or add the package (see Supported providers). |
Errors mentioning embeddings or text-embedding-3-large |
EMBEDDINGS_PROVIDER is openai with a non-OpenAI key. Leave it empty or set it to local, then recreate the container. |
| The model answers but never calls tools, or tool calls fail | The model doesn't support tool calling. Pick one that does. |
quickstart.sh says Compose is too old |
env_file with required: false, which keeps llm.env.local optional, needs Docker Compose 2.24 or later. |
| Changes don't take effect | You ran restart, or skipped the rebuild after pulling new code. See Step 3. |
Every /chatbot/ request returns 503 crapi-chatbot is not running |
See Troubleshooting the local setup. |
- Switching providers: change all five lines in
llm.env.localtogether, using your row from Supported providers, then recreate the container (Step 3). To switch back and forth, keep one file per provider (anthropic.env.local,xai.env.local, …) and copy the one you want overllm.env.local. Chat memory built with one embedding model (thechatscollection foropenai,chats_localforlocal) isn't visible under the other. - Rotating: create a new key with your provider, replace the value in
llm.env.local, recreate the container, then delete the old key in the provider's console. - If a key was ever committed or pasted somewhere public: revoke it with the provider right away. Deleting it in a later commit doesn't remove it from git history.
- Anyone who can run
docker inspect crapi-chatboton this machine can read the key. The chatbot's admin-level agent is a deliberate prompt-injection target, so use a key with a spending limit.
quickstart.sh and crapi-dev.sh both call scripts/lib/platform.sh, which detects the OS and makes sure a Docker engine is answering before anything else runs. You don't need to start Docker by hand on either platform.
macOS has no native Docker engine; it runs inside a Linux VM. The script checks, in order:
- Docker already answering (any runtime): use it.
- Colima installed: start it. An existing VM keeps its settings from
~/.colima/default/colima.yaml. A new VM is created with 4 CPUs and 8 GiB, because Colima's default of 2 CPUs and 2 GiB is too small for crAPI (the chatbot alone has a 1 GiB limit). - Docker Desktop or OrbStack installed: open it and wait for its engine.
- Nothing installed: offer to run
brew install colima docker docker-compose, then start Colima.
Linux: if the Docker daemon isn't running, the script offers to start it with sudo systemctl start docker (or service docker start). If your user can't reach the Docker socket, it prints the usermod -aG docker fix. If Docker isn't installed, it prints your package manager's install command next to the existing build-from-source steps.
On both platforms it warns if Docker has less than about 6 GiB of memory, and tells you how to raise it for your runtime.
Settings, as environment variables:
| Variable | Default | Effect |
|---|---|---|
COLIMA_CPU |
4 |
CPUs for a new Colima VM |
COLIMA_MEMORY |
8 |
GiB of memory for a new Colima VM |
COLIMA_PROFILE |
default |
Which Colima profile to start |
DOCKER_WAIT_SECS |
120 |
How long to wait for the engine to answer |
Installs and sudo always ask first. Pass --yes to quickstart.sh to answer yes in advance, which you need when no terminal is attached (CI, a pipe). Starting an already-installed Colima, Docker Desktop or OrbStack doesn't ask.
To resize an existing Colima VM, which the script never changes on its own:
colima stop && colima start --cpu 4 --memory 8Colima doesn't start on its own after a reboot. The script starts it for you, or brew services start colima starts it at login.
This fork's docker-compose.yml publishes the web UI on 8889 instead of upstream's 8888, because 8888 is used by another app on the host. quickstart.sh and crapi-dev.sh health-check and print 8889 to match.
| URL | What |
|---|---|
| https://localhost:8443 | crAPI web UI (HTTPS) |
| http://localhost:8889 | crAPI web UI (HTTP) |
| http://localhost:8025 | Mailhog (signup OTPs, password resets) |
| https://localhost:8443/mailhog/ | Mailhog through the web front end |
localhost:8090 |
Identity service (direct) |
30080 / 30443 |
Alternate HTTP / HTTPS ports for the web UI |
Everything else (community, workshop, chatbot, databases, api.mypremiumdealership.com) is reachable only on the internal Docker network.
All emails to @example.com addresses go to Mailhog, whatever the SMTP configuration says.
Host port 8080 is left free for Burp Suite's proxy listener. Point your browser at 127.0.0.1:8080 and browse crAPI as usual. If localhost traffic bypasses the proxy, browse to the host's LAN IP instead.
failed to connect to the docker API at unix:///Users/<you>/.colima/default/docker.sock— Colima's VM is stopped. Rerunquickstart.sh, which starts it, or runcolima start.crapi-chatbotexits withModuleNotFoundError: No module named 'psycopg'— SQLAlchemy 2.1 made psycopg v3 the default driver forpostgresql://URIs, but the image shipspsycopg2-binary. Fixed by usingpostgresql+psycopg2://inservices/chatbot/src/chatbot/dbconnections.py; rebuild withdocker compose build crapi-chatbot && docker compose up -d --no-deps crapi-chatbotfromdeploy/docker.- Every
/chatbot/request returns503 crapi-chatbot is not running—crapi-webis mountingdeploy/docker/nginx-chatbot-disabled.ssl.conf.template, a stub for when the chatbot can't run (the stock template makes nginx refuse to start if thecrapi-chatbothost doesn't exist). Remove that volume fromcrapi-webindocker-compose.ymlonce the chatbot is up, thendocker compose up -d --no-deps crapi-web. quickstart.shends with "Some services are still starting" even though the site is up — its health check is hitting a different port than the web UI is published on. Make sure the port indocker-compose.ymlmatches the one in the scripts (8889).- Chatbot key problems — see Chatbot troubleshooting.
You need Docker and docker compose 1.27.0 or later. Check with:
docker compose versionIf you see ERROR: Invalid interpolation format for ..., upgrade docker compose.
These steps download upstream's compose and .env files and run the images built by upstream CI. Pick a version:
| Version | Branch | Archive URL | Folder after unzip |
|---|---|---|---|
| Latest stable | main |
https://github.com/OWASP/crAPI/archive/refs/heads/main.zip |
crAPI-main |
| Latest development | develop |
https://github.com/OWASP/crAPI/archive/refs/heads/develop.zip |
crAPI-develop |
Linux or macOS (stable shown; for development, swap main for develop):
curl -L -o /tmp/crapi.zip https://github.com/OWASP/crAPI/archive/refs/heads/main.zip
unzip /tmp/crapi.zip
cd crAPI-main/deploy/docker
docker compose pull
docker compose -f docker-compose.yml --compatibility up -dWindows (stable shown; for development, swap main for develop):
curl.exe -L -o crapi.zip https://github.com/OWASP/crAPI/archive/refs/heads/main.zip
tar -xf .\crapi.zip
cd crAPI-main/deploy/docker
docker compose pull
docker compose -f docker-compose.yml --compatibility up -dTo override server settings, change the variables in the .env file, or set them in front of the command. For example, to expose crAPI on all network interfaces:
LISTEN_IP="0.0.0.0" docker compose -f docker-compose.yml --compatibility up -dThen visit http://localhost:8888. Upstream's compose file uses port 8888, unlike this fork's 8889. Emails go to Mailhog at http://localhost:8025. You can change the SMTP configuration, but all emails to the example.com domain still go to Mailhog.
This option runs crAPI in a virtual machine, isolated from your system. You need Vagrant and a provider such as VirtualBox.
- Clone the crAPI repository:
git clone [REPOSITORY-URL]
- Start the crAPI virtual machine:
cd deploy/vagrant && vagrant up
- Visit http://192.168.33.20. Emails go to Mailhog at http://192.168.33.20:8025.
When you're done, remove the VM by running this from the repository root:
cd deploy/vagrant && vagrant destroySee the setup instructions, including Kubernetes, and teardown for removing a deployment.
| Document | What's in it |
|---|---|
| Overview | What crAPI is and how it's built |
| Architecture | How crAPI is modelled as an application and how its parts fit together |
| Challenges | The OWASP API Top 10 challenge list |
| Challenge solutions | Walkthroughs for each challenge |
| Exploitation cheat sheet | Quick reference for attacking crAPI (this fork) |
| Notes and challenges | Exploitation examples and lesson links (this fork) |
| Chatbot API Key Flow | Flowchart of how the chatbot's LLM key (any provider) is sourced and kept out of git (this fork) |
| Setup / Teardown | All deployment options |
| Troubleshooting | General install and runtime issues |
| Branch | Purpose |
|---|---|
main |
Protected and always deployable. Changes arrive only through merge requests from develop. Releases are tagged here. |
develop |
Where all changes are made and pushed. |
-
Make changes on
developand push. GitLab CI runs on every push (.gitlab-ci.yml):Job Checks shellcheckquickstart.sh,scripts/lib/platform.sh(all warnings),crapi-dev.sh(errors)bash-3.2-syntaxThe scripts parse under bash 3.2, the bash a stock Mac runs compose-configdocker-compose.ymlvalidates with nollm.env.local, as in a fresh clonechatbot-pythonThe chatbot source compiles secret_detectionGitLab's gitleaks scan for committed keys. GitLab's template runs it in branch pipelines only: main, anddevelopwhile no merge request is open. It skips merge request and tag pipelines; a release tag always points at amaincommit that was already scannedchatbot-imageBuilds the chatbot image and checks both LLM client libraries and the offline embedding model. Runs on merge requests, mainand tags -
Open a merge request from
developtomain. From the command line:git push -o merge_request.create -o merge_request.target=main origin develop
Merge it once its pipeline passes.
-
Release from an up-to-date
main. First add a## [vX.Y.Z] - YYYY-MM-DDsection at the top ofCHANGELOG.md(ondevelop, merged like any change). Then:git switch main && git pull git tag -a vX.Y.Z -m "crAPI lab vX.Y.Z" git push origin vX.Y.Z
The tag pipeline's
releasejob creates the GitLab release, with that version'sCHANGELOG.mdsection as its notes. Releases are listed under Deploy > Releases in GitLab.
For issues specific to this fork's setup, start with Troubleshooting the local setup and Chatbot troubleshooting. For general install and runtime problems, see the upstream troubleshooting guide. If that doesn't solve it, open an issue on GitHub.