Local-first JARVIS OS v2 — Dashboard brain map, daily briefs, voice I/O, RAG chat, SimulatedHouse + Home Assistant adapter.
See DEMO.md for the 5-minute walkthrough and SECURITY.md for G0–G12 gates.
| Layer | Tech |
|---|---|
| Backend | FastAPI + Python 3.9+ |
| LLM | Ollama (llama3.2) + Groq fallback |
| Embeddings | sentence-transformers (all-MiniLM-L6-v2) |
| Vector DB | Qdrant (Docker) |
| STT | Web Speech API → Whisper |
| TTS | American-voice pyttsx3 / Coqui → Browser SpeechSynthesis |
| Frontend | Next.js 14 — Dashboard / Work / Lab |
| 3D | React Three Fiber + Three.js |
| House | SimulatedEnvironment + Home Assistant adapter |
| State | Zustand + SQLite action queue |
- Docker Desktop — for Qdrant (optional but recommended)
- Ollama — ollama.com —
ollama pull llama3.2 - Node.js 20+ and Python 3.9+
copy .env.example .env
REM set JARVIS_MASTER_KEY=... in .env
docker start jarvis-qdrant
cd backend
pip install -r requirements.txt
python -m uvicorn main:app --host 127.0.0.1 --port 8002 --reload
cd ..\frontend
echo NEXT_PUBLIC_API_URL=/backend> .env.local
npm install
npm run devOpen: http://localhost:3000 (API proxied at /backend → :8002)
- Keep backend + frontend running on this PC.
- Run:
scripts\expose-public.bat- Open the printed
https://….trycloudflare.comURL on the other laptop.
Same Wi-Fi only (no internet tunnel):
powershell -ExecutionPolicy Bypass -File scripts\expose-public.ps1 -LanOnlyThen start frontend bound to all interfaces: npm run dev -- -H 0.0.0.0 -p 3000 and open http://<your-lan-ip>:3000. Prefer the Cloudflare tunnel for camera/mic (browsers require HTTPS off-localhost).
start.batOpen: http://localhost:5050 (compose maps frontend :5050 → :3000, API :8001)
Demo builder: In chat say build me a website for … (any brief), or use Lab → Demos. Generates a cinematic Vite site, previews in-panel, Save/Rebuild, optional Cloudflare Publish. House automation is parked.
cd backend
pip install -r requirements.txt
# Windows needs ffmpeg for Whisper:
# Download from https://ffmpeg.org and add to PATH
uvicorn main:app --host 0.0.0.0 --port 8000 --reloadcd frontend
npm install
npm run devdocker run -p 6333:6333 qdrant/qdrant- Open http://localhost:5050 — the 3D brain loads with fallback data
- Click
+ GITHUBin the top bar → enter your GitHub username → click INGEST- All your repos get indexed into Qdrant in the background
- Brain graph nodes update with your actual repos
- Click
↺ HNto pull today's Hacker News signals - BRIEF tab → Click ▶ READ ALOUD to have JARVIS narrate your daily brief
- CHAT tab → Ask anything:
"What should I work on today?"or"Summarize my repos" - VOICE tab → Click ⏺ HOLD TO SPEAK → speak → JARVIS transcribes + responds + reads back
- Click any node in the 3D brain → details appear in the NODES panel
GET http://localhost:8000/health
→ { "status": "ok" }
POST http://localhost:8000/context
{
"daily_goals": ["Ship LexProbe MVP", "Review PRs"],
"active_project": "LexProbe",
"focus_time": "09:00-12:00"
}
GET http://localhost:8000/ingest/github/user/siddharthmishra
→ { "status": "ingesting", "repo_count": 12, "repos": [...] }
POST http://localhost:8000/ingest/github
{ "owner": "siddharthmishra", "repo": "lexprobe" }
GET http://localhost:8000/ingest/external
→ { "status": "ok", "stories": 15, "top": [...] }
GET http://localhost:8000/brief
→ {
"date": "Tuesday, April 1 2025",
"greeting": "Good morning...",
"priority_actions": ["🔥 Ship...", ...],
"insights": [...],
"learning_goals": [...],
"voice_summary": "..."
}
POST http://localhost:8000/chat
{ "message": "How is my LexProbe architecture?" }
→ { "response": "Your LexProbe stack...", "context_used": true }
POST http://localhost:8000/voice/input
Content-Type: multipart/form-data
file: <audio.webm>
→ { "text": "what should I work on today" }
POST http://localhost:8000/voice/output
{ "text": "JARVIS online. Here is your brief..." }
→ audio/wav binary
GET http://localhost:8000/voice/test
→ audio/wav binary
You speak
↓
Browser Web Speech API ──(primary)──→ Transcribed text
↓ (if unavailable)
MediaRecorder (webm)
↓
POST /voice/input → Whisper (backend)
↓
Transcribed text → POST /chat → JARVIS response
↓
POST /voice/output → pyttsx3 / Coqui ──(primary)──→ Audio playback
↓ (if backend TTS fails)
Browser SpeechSynthesis API
Edit .env:
# LLM
OLLAMA_MODEL=llama3.2 # or: mistral, codellama, phi3
GROQ_API_KEY=gsk_... # optional, from console.groq.com
# GitHub (optional, 5000 req/hr vs 60)
GITHUB_TOKEN=ghp_...
# Speech
WHISPER_MODEL=base # tiny | base | small | medium
TTS_ENGINE=pyttsx3 # pyttsx3 | coqui | espeak | auto
TTS_VOICE=american # prefers clear US-English voices
TTS_ESPEAK_VOICE=en-us # fallback backend voice if espeak is used
# Google Calendar
GOOGLE_CLIENT_ID=... # OAuth web app client id
GOOGLE_CLIENT_SECRET=... # OAuth web app client secret
GOOGLE_REDIRECT_URI=http://localhost:8001/calendar/google/callback
GOOGLE_FRONTEND_URL=http://localhost:5050
GOOGLE_CALENDAR_ID=primary- Create a Google OAuth client for a web application.
- Enable the Google Calendar API for that project.
- Add
http://localhost:8001/calendar/google/callbackas an authorized redirect URI. - Put the client id and secret into
.env, rebuild withdocker compose up -d --build, then open the newCALtab in the UI.
Once connected, the brief and chat views use upcoming events as schedule context.
jarvis-ai-brain/
├── backend/
│ ├── main.py # FastAPI app + CORS
│ ├── routers/
│ │ ├── context.py # GET/POST /context
│ │ ├── ingest.py # /ingest/github, /ingest/external
│ │ ├── brief.py # GET /brief
│ │ ├── chat.py # POST /chat
│ │ └── voice.py # POST /voice/input, /voice/output
│ ├── services/
│ │ ├── llm.py # Ollama + Groq chat_completion()
│ │ ├── rag.py # Qdrant + local keyword fallback
│ │ ├── github.py # GitHub API + fallback data
│ │ ├── hn.py # Hacker News Firebase API
│ │ ├── tts.py # Coqui / pyttsx3 / espeak / silent WAV
│ │ ├── stt.py # Whisper / faster-whisper
│ │ └── store.py # In-memory + JSON persistence
│ ├── data/ # Persisted JSON state + knowledge store
│ ├── requirements.txt
│ └── Dockerfile
├── frontend/
│ ├── app/
│ │ ├── page.js # Main layout: brain + side panel
│ │ ├── layout.js # Root HTML + fonts
│ │ ├── globals.css # Cyberpunk design system
│ │ └── store.js # Zustand global state + API calls
│ ├── components/
│ │ ├── BrainGraph.jsx # React Three Fiber 3D graph
│ │ ├── BriefPanel.jsx # Daily brief UI
│ │ ├── ChatPanel.jsx # JARVIS chat UI
│ │ ├── VoicePanel.jsx # Voice record/play UI
│ │ ├── NodePanel.jsx # Clicked node detail
│ │ └── HUD.jsx # Top status bar
│ ├── .env.local
│ ├── next.config.js
│ ├── package.json
│ └── Dockerfile
├── docker-compose.yml
├── .env.example
├── start.bat # Windows one-click start
└── README.md
| Issue | Fix |
|---|---|
| Docker daemon not found | Start Docker Desktop |
| Ollama timeout | Run ollama serve separately, or add GROQ_API_KEY |
| Microphone not working | Use Chrome/Edge; allow mic in browser settings |
| TTS silent output | Backend TTS fell back to silent WAV; browser SpeechSynthesis still works |
| 3D graph empty | Click + GITHUB and ingest your repos |
| Qdrant connection refused | Run docker-compose up qdrant -d first |
| Whisper model slow | Switch to WHISPER_MODEL=tiny in .env |
- Add YouTube scraping: implement
services/youtube.pyusing yt-dlp - Add a calendar agent:
POST /contextwith upcoming events from Google Calendar API - Switch LLM: change
OLLAMA_MODEL=codellamafor code-focused queries - Upgrade TTS: set
TTS_ENGINE=coquiand installTTSpackage for neural voice - Persistent chat history: SQLite in
services/chat_history.py(already shipped)
- Open http://localhost:5050 — JARVIS HUD online
- Chat:
Remember the word ORBIT→ thenWhat word did I ask you to remember?(multi-turn memory) - Watch the reply stream (Groq
openai/gpt-oss-120b) - Click + NEW in Threads, send a different message, switch back — sessions isolate
- Research:
Research Fourier neural operators and generate a report→ vault Reports - Site builder:
build me a website for …→ Lab → Demos → Monaco → REBUILD → PUBLISH - Lab → Vision (
qwen/qwen3.6-27b); Voice Space-PTT; Wake opt-in; House disabled
See DEMO.md for the full beat sheet.