Skip to main content

Quick Start

Prerequisites​

1. Create a Docker Compose file​

Save as docker-compose.yml:

services:
database:
image: pgvector/pgvector:0.8.2-pg18-trixie
environment:
POSTGRES_DB: soat
POSTGRES_USER: soat_user
POSTGRES_PASSWORD: soat_password
volumes:
- postgres_data:/var/lib/postgresql
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U soat_user -d soat']
interval: 10s
timeout: 5s
retries: 5

ollama:
image: ollama/ollama:latest
volumes:
- ollama_cache:/root/.ollama
entrypoint:
- /bin/sh
- -c
- 'ollama serve > /dev/null 2>&1 & sleep 5 && ollama pull qwen3-embedding:0.6b > /dev/null 2>&1 && ollama pull qwen2.5:0.5b > /dev/null 2>&1 && wait'
healthcheck:
test:
[
'CMD-SHELL',
'ollama list | grep qwen3-embedding && ollama list | grep qwen2.5',
]
interval: 10s
timeout: 30s
retries: 60
start_period: 60s

server:
image: ttoss/soat
depends_on:
database:
condition: service_healthy
ollama:
condition: service_healthy
ports:
- '5047:5047'
environment:
SOAT_ADMIN_USERNAME: admin
SOAT_ADMIN_PASSWORD: Admin1234!
DATABASE_HOST: database
DATABASE_PORT: '5432'
DATABASE_NAME: soat
DATABASE_USER: soat_user
DATABASE_PASSWORD: soat_password
SECRETS_ENCRYPTION_KEY: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
FILES_STORAGE_DIR: /data/files
OLLAMA_BASE_URL: http://ollama:11434
EMBEDDING_PROVIDER: ollama
EMBEDDING_MODEL: qwen3-embedding:0.6b
EMBEDDING_DIMENSIONS: '1024'
volumes:
- files_data:/data/files

volumes:
postgres_data:
ollama_cache:
files_data:
warning

Replace SOAT_ADMIN_PASSWORD and SECRETS_ENCRYPTION_KEY with strong values before exposing SOAT outside localhost. See Configuration.

2. Start the stack​

docker compose up -d
ServiceDescription
databasePostgreSQL 18 with pgvector for relational and vector storage
ollamaLocal LLM runtime (downloads qwen3-embedding and qwen2.5 models)
serverSOAT REST API + MCP server, exposed on port 5047

The first run pulls images and Ollama models (a few minutes). Wait until all three show healthy or running:

docker compose ps

3. Log in and obtain a token​

TOKEN=$(curl -s -X POST http://localhost:5047/api/v1/users/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"Admin1234!"}' | jq -r '.token')

echo "Token: ${TOKEN:0:40}..."

Subsequent requests send this JWT in the Authorization header.

4. Create your first project​

PROJECT=$(curl -s -X POST http://localhost:5047/api/v1/projects \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"name":"my-first-project"}' | jq)

echo "$PROJECT" | jq .

PROJECT_ID=$(echo "$PROJECT" | jq -r '.id')

5. Send your first chat message​

Register Ollama as an AI provider for the project:

AI_PROVIDER_ID=$(curl -s -X POST http://localhost:5047/api/v1/ai-providers \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d "{
\"project_id\": \"$PROJECT_ID\",
\"name\": \"Local Ollama\",
\"provider\": \"ollama\",
\"base_url\": \"http://ollama:11434\",
\"default_model\": \"qwen2.5:0.5b\"
}" | jq -r '.id')

echo "AI Provider: $AI_PROVIDER_ID"

Send a stateless completion (no chat resource required):

SOAT_BASE_URL=http://localhost:5047 SOAT_TOKEN="$TOKEN" \
soat create-chat-completion \
--ai-provider-id "$AI_PROVIDER_ID" \
--messages '[
{ "role": "system", "content": "You are a helpful assistant." },
{ "role": "user", "content": "What is the color of the sky?" }
]' | jq '.choices[0].message.content'

6. What's next?​

CLI path parameters are resource-specific kebab-case flags (--project-id, --agent-id, --session-id), never a generic --id.

GoalWhere to go
Understand the permission modelIAM module
Browse the current CLI command surfaceCLI Commands Reference
Connect an LLM provider (OpenAI, Anthropic, …)AI Providers module
Save a reusable chat configurationChats module
Define and run an agentAgents module
Interact via MCPMCP docs
Use the TypeScript SDKSDK docs
Full REST referenceAPI Reference
Tune environment variablesConfiguration