You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
An automated, end-to-end Google Meet recording → transcription → summarization → AI chat platform. Shadow Bot dispatches ephemeral Docker containers that silently join your meetings, records them, and pipes the audio through a multi-stage AI pipeline (ElevenLabs Scribe → Gemini Flash → Qdrant Vector Search) so every meeting becomes instantly searchable and conversational.
graph TB
subgraph "Client Layer"
A["Next.js Web App<br/>(React + Tailwind)"]
B["Chrome Extension<br/>(Manifest V3)"]
end
subgraph "API Gateway"
C["HTTP Service<br/>(Express v5)"]
end
subgraph "Workers"
D["Docker Manager<br/>(Dockerode)"]
E["Transcribe Service"]
end
subgraph "External AI APIs"
J["ElevenLabs<br/>Scribe v2"]
K["Google Gemini<br/>Flash"]
end
subgraph "Infrastructure"
F[("PostgreSQL<br/>(Prisma ORM)")]
G[("Redis<br/>(Queue Broker)")]
H[("Qdrant<br/>(Vector DB)")]
I["Docker Engine<br/>(gmeet-recorder)"]
end
A -->|REST API| C
B -->|REST API| C
C -->|Read/Write| F
C -->|Enqueue Join| G
C -->|Vector Search| H
C -->|LLM Chat| K
G -->|BLPOP join_meet_queue| D
D -->|Start Container| I
D -->|Update Status| F
D -->|Enqueue Transcription| G
G -->|BLPOP transcription-queue| E
E -->|Stream Audio| J
E -->|Summarize + Tag + Embed| K
E -->|Store Vectors| H
E -->|Save Results| F
Loading
🚀 Core Features
1. 🎙️ Isolated Meeting Recording
Ephemeral Docker Containers: Each recording spins up a dedicated browser container (rahmatdeep/gmeet-recorder) that joins the meeting as "Shadow Bot" and records audio/video.
Per-User Limits: Configurable concurrent container cap (MAX_CONCURRENT_CONTAINERS, default 2) with per-user single-container enforcement.
Auto-Cleanup: Containers are AutoRemove: true. Invalid meeting links trigger automatic file deletion and status updates.
Status Tracking: Real-time status progression: PENDING → ASKING_TO_JOIN → JOINED → COMPLETED/FAILED/TIMEOUT. Updated via Docker container log stream monitoring.
Retry Logic: Up to 3 attempts to start a recorder with 2-second backoff between failures.
2. 🗣️ Streaming AI Transcription
ElevenLabs Scribe v2: High-fidelity speech-to-text using scribe_v2 model.
Memory-Efficient Streaming: Uses Node.js createReadStream instead of loading the full .webm file into memory.
Timestamped Output: Every utterance is tagged with precise [HH:MM:SS] timestamps in a separate transcriptWithTimeStamps field.
Dual Format: Both raw transcript and timestamped transcript are stored for different use cases.
3. 📝 Multi-Stage Summarization
Executive Summary (Structured JSON): Extracts title, goal, keyPoints[], and actionItems[] using Gemini Flash with Zod-validated structured output.
Detailed Summary (Free-Text): Comprehensive, chronological, lossless summary that preserves every statement, including side conversations and tone — formatted as readable paragraphs.
sequenceDiagram
participant User
participant WebApp as Web App / Extension
participant API as HTTP Service
participant Redis
participant DockerMgr as Docker Manager
participant Container as Recorder Container
participant TransSvc as Transcribe Service
participant ElevenLabs
participant Gemini as Gemini Flash
participant Qdrant
participant DB as PostgreSQL
User->>WebApp: Submit meeting link
WebApp->>API: POST /api/v1/meeting/join
API->>DB: Create Recording (PENDING)
API->>Redis: RPUSH join_meet_queue
API-->>WebApp: { status: "queued", recordingId }
Redis->>DockerMgr: BLPOP join_meet_queue
DockerMgr->>Container: Start gmeet-recorder
DockerMgr->>DB: Update status → ASKING_TO_JOIN
Container-->>DockerMgr: Log: "Bot admitted"
DockerMgr->>DB: Update status → JOINED
Note over Container: Recording in progress...
Container-->>DockerMgr: Container exits (code 0)
DockerMgr->>DB: Update status → COMPLETED
DockerMgr->>Redis: RPUSH transcription-queue
Redis->>TransSvc: BLPOP transcription-queue
TransSvc->>DB: Transcription → IN_PROGRESS
TransSvc->>ElevenLabs: Stream .webm file
ElevenLabs-->>TransSvc: Transcription result
TransSvc->>DB: Save transcript + timestamps
TransSvc->>Gemini: Executive summary
Gemini-->>TransSvc: { title, goal, keyPoints, actionItems }
TransSvc->>DB: Save summary
par Parallel AI Processing
TransSvc->>Gemini: Detailed summary
Gemini-->>TransSvc: Prose summary
TransSvc->>DB: Save detailed summary
and
TransSvc->>Gemini: Generate tags
Gemini-->>TransSvc: ["tag1", "tag2", ...]
TransSvc->>DB: Save tags
and
TransSvc->>Gemini: Generate embeddings
Gemini-->>TransSvc: Vector embeddings
TransSvc->>Qdrant: Store chunks + metadata
TransSvc->>DB: embeddingStatus → COMPLETED
end
Loading
🤖 AI Pipeline Deep Dive
flowchart LR
subgraph "Input"
A[".webm Recording"]
end
subgraph "Stage 1: Transcription"
B["ElevenLabs Scribe v2<br/>createReadStream()"]
end
subgraph "Stage 2: Summarization"
C["Executive Summary<br/>(Gemini + Zod Schema)"]
D["Detailed Summary<br/>(Gemini Free-Text)"]
end
subgraph "Stage 3: Enrichment"
E["Tag Generation<br/>(Gemini + Zod)"]
F["Vector Embeddings<br/>(gemini-embedding-001)"]
end
subgraph "Storage"
G[("PostgreSQL<br/>Transcript Table")]
H[("Qdrant<br/>transcript_chunks")]
end
A --> B
B --> C
B --> D
C --> E
B --> F
C --> G
D --> G
E --> G
F --> H
Loading
Each stage updates a dedicated status field in the database:
Stage
Status Field
Values
Transcription
transcriptionStatus
PENDING → IN_PROGRESS → COMPLETED / FAILED
Executive Summary
summaryStatus
PENDING → IN_PROGRESS → COMPLETED / FAILED
Detailed Summary
detailedSummaryStatus
PENDING → IN_PROGRESS → COMPLETED / FAILED
Tags
tagsStatus
PENDING → IN_PROGRESS → COMPLETED / FAILED
Embeddings
embeddingStatus
PENDING → IN_PROGRESS → COMPLETED / FAILED
📡 API Reference
All endpoints (except auth) require: Authorization: Bearer <JWT>
Auth (/api/v1/auth)
Method
Endpoint
Description
POST
/signup
Register with email, password, name
POST
/login
Login with email + password → JWT
POST
/google-auth
Google OAuth sign-in / account linking
Meetings (/api/v1/meeting)
Method
Endpoint
Description
GET
/
List all user recordings with statuses
GET
/:id
Get recording details + transcript + recent chats
GET
/:id/status
Poll recording + transcription status
GET
/:id/transcript
Get full transcript, timestamps, and summary
POST
/join
Queue a new meeting recording
Chat (/api/v1/chat)
Method
Endpoint
Description
POST
/start
Create a new chat session for a recording
POST
/message
Send a message, get AI response with full history
GET
/
List all chat sessions (optionally filter by recording)
# Start everything (http, docker-manager, transcribe-service, web)
pnpm dev
Or start individual services:
pnpm --filter http run dev # API on :3005
pnpm --filter docker-manager run dev # Container orchestrator
pnpm --filter transcribe-service run dev # AI pipeline worker
pnpm --filter web run dev # Web dashboard on :3000
7. Verify
# Health check
curl http://localhost:3005/health
# API status
curl http://localhost:3005/api/v1/status
🧩 Chrome Extension
Installation
Open Chrome → chrome://extensions/
Enable Developer Mode (top-right toggle)
Click "Load unpacked"
Select apps/chrome-extension/
Pin the extension (🧩 → 📌)
Usage
Login on http://localhost:3000 first (the extension shares your session)
Navigate to any Google Meet tab — the extension auto-detects the link
Click "Use" to fill the meeting URL, then "Join Meeting" to start recording
Active recordings show as live status cards in the popup
🔑 Environment Variables
Variable
Service
Required
Description
DATABASE_URL
packages/db
✅
PostgreSQL connection string
JWT_SECRET
http
✅
Secret for signing JWTs
HTTP_PORT
http
❌
API port (default: 3000)
GEMINI_API_KEY
http, transcribe-service
✅
Google AI API key
ELEVENLABS_API_KEY
transcribe-service
✅
ElevenLabs API key
RECORDINGS_DIR
transcribe-service
❌
Custom recordings path
MAX_CONCURRENT_CONTAINERS
docker-manager
❌
Container limit (default: 2)
NEXT_PUBLIC_API_URL
web
✅
API URL for the frontend
🗂️ Database Schema
erDiagram
User ||--o{ Recording : has
User ||--o{ QuerySession : has
Recording ||--o| Transcript : has
Recording ||--o{ ChatSession : has
ChatSession ||--o{ ChatMessage : contains
QuerySession ||--o{ QueryMessage : contains
User {
string id PK
string email UK
string password
string name
string provider
string providerAccountId
}
Recording {
string id PK
string userId FK
string link
string fileName
string title
RecordingStatus status
json errorMetadata
}
Transcript {
string id PK
string recordingId FK
string transcript
string transcriptWithTimeStamps
json summary
string detailedSummary
string[] tags
TranscriptStatus transcriptionStatus
TranscriptStatus summaryStatus
TranscriptStatus detailedSummaryStatus
TranscriptStatus tagsStatus
TranscriptStatus embeddingStatus
string failureReason
}
ChatSession {
string id PK
string recordingId FK
string title
}
ChatMessage {
string id PK
string chatSessionId FK
ChatRole role
string content
}
QuerySession {
string id PK
string userId FK
string title
}
QueryMessage {
string id PK
string querySessionId FK
ChatRole role
string content
}
Loading
Built with ❤️ using Turborepo, Node.js, and Langchain.