TruthTeller AI is a local app that runs a multi-model deliberation workflow through OpenRouter.
Instead of asking one model, you ask a council of models:
- Stage 1: each model answers independently.
- Stage 2: models evaluate and rank anonymized Stage 1 responses.
- Stage 3: a chairman model synthesizes a final answer.
The app supports multi-turn conversations, multi-file uploads, streaming updates, and advanced post-run analysis of council outputs. It runs as a web app (Rust backend + browser) or as a native macOS app (Tauri).
- 3-stage council orchestration (Stage 1/2/3)
- Multi-turn conversations
- Streaming responses (SSE in web mode, Tauri events in native mode)
- File uploads shared across all council models (
txt,md,markdown,pdf,docx,pptx) - Local file ingestion and text extraction
- Conversation sidebar with create/delete/search
- Full Settings UI (General, Models, Credentials, Advanced)
- Credential management UI for OpenRouter key (set/test/clear with masked status)
- Dark mode toggle (persisted)
- Export conversation to Markdown
- Advanced insights: consensus matrix, influence graph, traceability, side-by-side diff, uncertainty panel, cost/latency breakdown, interactive rerun controls
- Add LiteLLM support
- Support for Microsoft Windows installations
- Enable overrides for local data stores
crates/core/ Shared Rust domain: types, config, storage, council, openrouter, attachments
crates/server/ Web adapter: axum HTTP routes + SSE streaming
backend/ Native adapter: Tauri commands + event streaming
frontend/ Next.js React frontend with dual transport abstractionBoth adapters link the same t2ai-core crate. The frontend auto-detects the runtime and uses HTTP fetch (web) or Tauri IPC (native).
- Conversations:
{data_dir}/conversations/{id}.json - Uploaded files:
{data_dir}/uploads/{conversation_id}/... - Runtime model config:
{data_dir}/config.json - Stored secrets (current implementation for web + native):
{data_dir}/secrets.json
In web mode data_dir defaults to ./data. In native mode it uses the macOS Application Support directory.
- Rust (stable toolchain)
- Node.js + npm
- OpenRouter API key (via environment variable or Settings UI)
For native mode only:
- Xcode Command Line Tools (macOS)
# Rust workspace
cargo build
# Frontend
cd frontend
npm install
cd ..Option A (recommended): set in the app:
- Open Settings
- Go to
Credentials - Enter your key and click
Save Key
Option B: create .env in the project root:
OPENROUTER_API_KEY=sk-or-v1-...
Optional (useful for integration testing with a mock server):
OPENROUTER_BASE_URL=http://127.0.0.1:12345
If OPENROUTER_BASE_URL is unset, the app defaults to https://openrouter.ai.
Runtime settings are managed through the Settings UI and persisted to data/config.json. Defaults are defined in crates/core/src/types.rs.
GET /api/config now returns:
- all non-secret config fields
credentialsstatus object:openrouter_configuredsource(env,stored, ormissing)masked_hint(when available)
Config fields managed in Settings:
council_modelschairman_modelrequest_timeout_secondsmax_parallel_requestsretry_attemptsretry_backoff_msstage2_enabledstage3_model_overridethemedefault_export_formatinsights_expanded_default
Key precedence for runtime requests:
OPENROUTER_API_KEYenvironment variable- stored key from
secrets.json - missing key error
Runtime impact:
request_timeout_seconds,retry_attempts,retry_backoff_ms, andmax_parallel_requestsare applied to model requests.stage2_enabledcontrols whether Stage 2 runs.stage3_model_overrideoverrides the chairman model when set.
Terminal 1 (backend):
cargo run -p t2ai-serverTerminal 2 (frontend):
npm run dev --prefix frontendOpen http://localhost:3000. Backend runs on port 8001.
cargo tauri devThis starts the Next.js dev server and opens the Tauri window automatically.
cargo tauri buildProduces a .app bundle and .dmg installer in target/release/bundle/.
./scripts/release.sh --platform macosThis script:
- verifies/installs prerequisites (
scripts/prereqs.sh) - runs optional checks/tests
- builds Tauri release artifacts (
.app+.dmg)
Useful variants:
# faster build (skip checks/tests)
./scripts/release.sh --platform macos --skip-checks --skip-tests
# cross-target build
./scripts/release.sh --platform macos --target aarch64-apple-darwinSupported file types: .txt, .md, .markdown, .pdf, .docx, .pptx
Limits (defined in crates/core/src/attachments.rs):
| Limit | Value |
|---|---|
| Max files per message | 10 |
| Max file size | 15 MB |
| Max combined upload size | 40 MB |
| Max extracted chars per file | 30,000 |
| Max total context chars (Stage 1) | 80,000 |
Base URL: http://localhost:8001
| Method | Path | Description |
|---|---|---|
| GET | / |
Health check |
| GET | /api/conversations |
List conversations |
| POST | /api/conversations |
Create conversation |
| GET | /api/conversations/{id} |
Get conversation |
| DELETE | /api/conversations/{id} |
Delete conversation |
| POST | /api/conversations/{id}/message |
Send message (JSON) |
| POST | /api/conversations/{id}/message/upload |
Send message (multipart with files) |
| POST | /api/conversations/{id}/message/stream |
Send message with SSE streaming |
| POST | /api/conversations/{id}/message/stream/json |
Send JSON message with SSE streaming |
| POST | /api/conversations/{id}/retry |
Retry failed models |
| POST | /api/conversations/{id}/rerun |
Rerun Stage 2+3 or Stage 3 |
| GET | /api/config |
Get config |
| GET | /api/storage/info |
Get runtime storage paths (data, conversations, uploads, logs, config, secrets) |
| PUT | /api/config |
Update config |
| POST | /api/config/credentials/openrouter |
Set/update stored OpenRouter API key |
| DELETE | /api/config/credentials/openrouter |
Clear stored OpenRouter API key |
| POST | /api/config/credentials/openrouter/test |
Validate OpenRouter API key |
| GET | /api/models |
Fetch OpenRouter models |
Both web (SSE) and native (Tauri events) emit the same event types:
upload_processing_start, upload_processing_complete, stage1_start, stage1_complete, stage2_start, stage2_complete, stage3_start, stage3_complete, title_complete, complete, error
Use one command to compute frontend + backend coverage and print a ready-to-paste badge line:
./scripts/coverage.shCurrent line coverage snapshot used by the badge:
- Frontend:
86.8%(947/1091) - Backend (
t2ai-core):61.75%(1230/1992) - Combined:
70.6%(2177/3083)
This repo uses a single app version across Rust crates, Tauri, and frontend package metadata.
-
Source of truth:
VERSION -
Check consistency:
./scripts/version.sh check
-
Bump all package versions in one command:
./scripts/version.sh set 0.2.0
./scripts/ci.sh and ./scripts/release.sh both run version consistency checks automatically.
GitHub Actions workflows are included in .github/workflows/:
ci.yml(push tomainand pull requests)- Reuses
./scripts/ci.sh - Runs prerequisites, version check, format/lint/tests/build
- Reuses
release.yml(tag pushvX.Y.Z, plus manual dispatch)- Reuses
./scripts/release.sh - Verifies tag version matches
VERSION - Builds macOS Tauri artifacts and publishes a GitHub Release on tag runs
- Reuses
Release flow:
- Bump versions:
./scripts/version.sh set X.Y.Z - Commit and push
- Tag and push:
git tag vX.Y.Z && git push origin vX.Y.Z release.ymlbuilds artifacts and publishes the release
Structured logging is enabled via tracing.
- Set
RUST_LOG=debugfor verbose output. - Web server mode logs to process stdout/stderr.
- Native Tauri mode logs to both stdout/stderr and daily-rotated files in:
~/Library/Application Support/dev.t2ai.app/logs/t2ai.log.YYYY-MM-DD
TruthTeller AI was originally forked from karpathy/llm-council and has since been significantly modified and rebranded.
Portions of this codebase are derived from the upstream project and remain subject to the upstream project's license and attribution requirements.
This repository is distributed under the proprietary TruthTeller AI License (all rights reserved), which does not permit rebranding or redistribution without prior written permission.
Third-party and upstream components may have separate license terms that continue to apply to those components.