NEW v0.8.0 — provenance on every number, in-app provider keys, Kite & CSV import, and a Docker image on GHCR. Release notes →
// Documentation

Up and running,
fast.

Install, configure providers, connect agents and understand how data flows. Everything you need to self-host the terminal.

Quick start

One command. The installer detects your OS, writes a single .env, generates secrets, seeds an admin account and starts the terminal — in Docker if you have it, otherwise with local Python and Node.

bash
$ git clone https://github.com/Hitheshkaranth/OpenTerminalUI.git
$ cd OpenTerminalUI
$ ./install.sh          # Windows: ./install.ps1
✓ OpenTerminalUI is ready  → http://localhost:8000
    email:    admin@openterminal.local
    password: <generated>

The credentials are also saved in .env. Change the password after first login. Force a mode with OTUI_MODE=docker or OTUI_MODE=local.

Prerequisites

Either Docker, or Python and Node for the local path. Nothing else to configure.

SoftwareVersionNeeded for
Docker20.10+ with Compose v2Recommended — full stack in containers
Python3.11+Local backend
Node.js20+Local frontend
Hardware2 cores · 4 GB RAM · 2 GB diskMinimum; 4 cores / 8 GB recommended

Docker image

Every release publishes a multi-arch image (linux/amd64 and linux/arm64) to GitHub Container Registry, tagged X.Y.Z, X.Y and latest. No clone needed.

bash
$ docker run -p 8000:8000 -v otui-data:/app/data \
    ghcr.io/hitheshkaranth/openterminalui:latest
✓ http://localhost:8000

Pass keys with -e FMP_API_KEY=… or --env-file .env. The volume keeps the SQLite database across upgrades; without Redis the quote bus falls back to in-memory automatically.

Docker Compose

bash
$ cp .env.example .env            # add API keys if you have them
$ docker compose up --build       # backend + Redis, SQLite
$ docker compose --profile postgres up --build   # with PostgreSQL 16
$ docker compose down -v          # stop and wipe the database

The UI and API are served from the backend container at http://127.0.0.1:8000. Compose maps host.docker.internal so the container can reach LM Studio on your host.

Local development

backend
$ python3.11 -m venv .venv
$ source .venv/bin/activate
$ pip install -r backend/requirements.txt
$ PYTHONPATH=. uvicorn backend.main:app --reload
frontend
$ cd frontend
$ npm ci
$ npm run dev
→ http://127.0.0.1:5173

Run every check before a PR with make gate.

API keys & providers

All keys are optional — the terminal runs on free sources out of the box. Add keys from the terminal or in the browser.

In the browser (admins)

Settings → Data Providers shows what each provider unlocks, its live status and last error. Set keys writes the same .env the installer uses, applies values live where possible and lists what needs a restart; Test runs the real probe. Placeholder values such as your-api-key are not treated as configured.

From the terminal

bash
$ make keys        # or ./scripts/setup-keys.sh — guided, shows what each key unlocks

Provider waterfall

ProviderMarketProvides
Zerodha KiteIndiaLive ticks, OHLCV at all intervals, holdings/positions import
NSEPythonIndiaF&O chains, OI, PCR, corporate actions
FMPUSOHLCV, fundamentals, earnings, news
FinnhubUSWebSocket ticks, OHLCV, news
AlpacaUSQuotes
Yahoo FinanceGlobalFallback OHLCV and quotes — no key
FRED · FXMacroDataMacroPolicy rates, CPI, GDP, release calendar — FXMacroData needs no key

Environment

VariablePurpose
FMP_API_KEYUS equities, fundamentals, earnings
FINNHUB_API_KEYUS real-time WebSocket ticks
KITE_API_KEY · _SECRET · _ACCESS_TOKENZerodha Kite — India live + historical (access token is daily)
OPENROUTER_API_KEYAI research agent — free :free models work
LM_STUDIO_BASE_URL · _MODELLocal model for news sentiment and the agent
DATABASE_URL · REDIS_URLSQLite by default; PostgreSQL and Redis optional
JWT_SECRET_KEY · CACHE_SIGNING_KEYGenerated by the installer
BOOTSTRAP_ADMIN_EMAIL · _PASSWORDFirst-run admin; skipped once any user exists

The full list is in the README.

Provenance

Every security snapshot carries {source, quality, as_of, latency_ms, note}. The UI renders it as a chip next to the value:

served by a live feedLIVE · KITE
exchange-delayedDELAYED · YAHOO
from cache, with ageCACHED · FMP 4m
generated — never a price basisSYNTHETIC
no provider could fill itNO DATA

Provider health comes from GET /api/providers/status, which runs real probes — an expired Kite token reads down.

MCP & agents

The agent's tool registry is exposed over the Model Context Protocol, so Claude Code, Claude Desktop or any MCP client can use the terminal.

1 · Create an API key

In Settings → API Keys, create an otui_ key with read (38 read-only tools) or read_write permission (adds 7 propose_* tools). The key maps to your user, so tools see your portfolio, watchlists and alerts.

2 · Connect

http
GET  /api/mcp/manifest          # tools, resources, prompts
GET  /api/mcp/tools
POST /api/mcp/tools/{{name}}
     X-API-Key: otui_…

For a local stdio session run python -m backend.mcp from the repo root (anonymous — market-data tools only).

Writes are proposals: a propose_* call creates a pending row that a human confirms in the app. Nothing places real orders — trading is paper only.

Every result is returned as {ok, data, provenance}, and five prompts ship with the server: morning_brief, position_review, screen_to_thesis, risk_check, idea_to_backtest.

Caching & failover

request→L1 SQLite→L2 Redis→primary provider→fallback→503
LayerBehaviour
L1 SQLitePer symbol and interval, default TTL 900 s
L2 RedisOptional via REDIS_URL; also quote pub/sub fan-out
Live candlesWebSocket ticks update bars in memory without touching the cache
PrefetchOPENTERMINALUI_PREFETCH_ENABLED=1 warms the cache

Keyboard

Ctrl GGO bar
Ctrl KCommand palette
Ctrl JAI agent
F1–F9Workspaces
1–7Chart timeframes
EscClose panel