commit d77a09cff4106e8a82963a9a360173d76545e1a3 Author: supersanta Date: Wed Sep 30 00:43:24 2026 +0700 first commit diff --git a/Overview/.$MeetVault_UI_Wireframes_v2.drawio.bkp b/Overview/.$MeetVault_UI_Wireframes_v2.drawio.bkp new file mode 100644 index 0000000..d59fe52 --- /dev/null +++ b/Overview/.$MeetVault_UI_Wireframes_v2.drawio.bkp @@ -0,0 +1,577 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Overview/.$meeting_system_architecture.drawio.bkp b/Overview/.$meeting_system_architecture.drawio.bkp new file mode 100644 index 0000000..b5f58a0 --- /dev/null +++ b/Overview/.$meeting_system_architecture.drawio.bkp @@ -0,0 +1,384 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Overview/README.md b/Overview/README.md new file mode 100644 index 0000000..274c339 --- /dev/null +++ b/Overview/README.md @@ -0,0 +1,828 @@ +# MeetVault + +MeetVault is a self-hosted meeting recording and intelligence platform. + +The user machine handles meeting recording and local transcription. The backend handles authentication, meeting metadata, transcript ingestion, summarization jobs, task extraction, reminder scheduling, and persistent storage. + +Large media files are stored in SeaweedFS through its S3-compatible API, while PostgreSQL stores structured application data. + +--- + +## Architecture Overview + +### User Machine + +The browser or desktop application is responsible for: + +- Recording meeting audio/video +- Running local speech-to-text using Whisper, whisper.cpp, or another local STT engine +- Sending the generated transcript to the backend +- Uploading large media files directly to SeaweedFS using presigned URLs + +Local transcription keeps transcription compute and cost on the user's device rather than on the backend. + +### Backend Infrastructure + +The backend API is responsible for: + +- Authentication +- User management +- Meeting management +- Presigned upload URL generation +- Transcript ingestion +- Summarization job creation +- Action-item extraction +- Reminder scheduling +- Job status tracking + +PostgreSQL stores structured application data such as users, meetings, transcripts, summaries, tasks, schedules, and processing jobs. + +SeaweedFS stores the actual large binary files such as: + +- Video +- Audio +- Thumbnails +- Attachments +- Exported files + +--- + +## Data Flow + +```text +User machine + │ + ├── video ─────────────────────────────► SeaweedFS + │ + └── local transcript ────────────────► PostgreSQL + │ + ▼ + processing_jobs + │ + Summarizer + │ + ┌─────────────┴─────────────┐ + ▼ ▼ + meeting_tasks meeting_summaries + │ + ▼ + meeting_schedules + │ + ▼ + Reminder Worker +``` + +--- + +## Database Design + +```text +users + │ + ├──── auth_identities + │ + ├──── subscriptions + │ + ├──── meetings + │ │ + │ ├──── meeting_files ─────────► SeaweedFS + │ │ + │ ├──── transcripts + │ │ + │ ├──── meeting_summaries + │ │ + │ ├──── meeting_tasks + │ │ │ + │ │ └──── meeting_schedules + │ │ + │ └──── processing_jobs + │ + └──── meeting_schedules +``` + +--- + +# PostgreSQL Schema + +## Enable UUID Generation + +```sql +CREATE EXTENSION IF NOT EXISTS pgcrypto; +``` + +--- + +## Users + +```sql +CREATE TABLE users ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + + email VARCHAR(320) UNIQUE NOT NULL, + display_name VARCHAR(255), + + identification_status VARCHAR(30) + NOT NULL DEFAULT 'unidentified' + CHECK ( + identification_status IN ( + 'unidentified', + 'pending', + 'identified', + 'rejected' + ) + ), + + account_status VARCHAR(30) + NOT NULL DEFAULT 'active' + CHECK ( + account_status IN ( + 'active', + 'suspended', + 'disabled' + ) + ), + + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); +``` + +--- + +## Authentication Identities + +Supports local login and external providers such as Google or Microsoft. + +```sql +CREATE TABLE auth_identities ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + + user_id UUID NOT NULL + REFERENCES users(id) + ON DELETE CASCADE, + + provider VARCHAR(50) NOT NULL, + + provider_user_id VARCHAR(255), + password_hash TEXT, + + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + + UNIQUE(provider, provider_user_id) +); +``` + +Typical providers: + +```text +local +google +microsoft +``` + +--- + +## Subscriptions + +```sql +CREATE TABLE subscriptions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + + user_id UUID NOT NULL + REFERENCES users(id) + ON DELETE CASCADE, + + plan VARCHAR(50) + NOT NULL DEFAULT 'free', + + status VARCHAR(30) + NOT NULL DEFAULT 'active' + CHECK ( + status IN ( + 'active', + 'trialing', + 'past_due', + 'cancelled', + 'expired' + ) + ), + + provider VARCHAR(50), + + provider_customer_id VARCHAR(255), + provider_subscription_id VARCHAR(255), + + current_period_start TIMESTAMPTZ, + current_period_end TIMESTAMPTZ, + + cancel_at_period_end BOOLEAN + NOT NULL DEFAULT FALSE, + + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); +``` + +--- + +## Meetings + +```sql +CREATE TABLE meetings ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + + owner_user_id UUID NOT NULL + REFERENCES users(id) + ON DELETE CASCADE, + + title VARCHAR(500), + description TEXT, + + started_at TIMESTAMPTZ, + ended_at TIMESTAMPTZ, + + timezone VARCHAR(100), + + status VARCHAR(30) + NOT NULL DEFAULT 'created' + CHECK ( + status IN ( + 'created', + 'recording', + 'uploading', + 'transcribing', + 'processing', + 'ready', + 'failed', + 'archived', + 'deleted' + ) + ), + + metadata JSONB NOT NULL DEFAULT '{}', + + deleted_at TIMESTAMPTZ, + + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + + CHECK ( + ended_at IS NULL + OR started_at IS NULL + OR ended_at >= started_at + ) +); +``` + +Summaries are stored separately in `meeting_summaries`. + +--- + +## Meeting Files + +This table stores file metadata only. The actual media files are stored in SeaweedFS. + +```sql +CREATE TABLE meeting_files ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + + meeting_id UUID NOT NULL + REFERENCES meetings(id) + ON DELETE CASCADE, + + uploaded_by UUID + REFERENCES users(id) + ON DELETE SET NULL, + + file_type VARCHAR(50) + NOT NULL + CHECK ( + file_type IN ( + 'video', + 'audio', + 'thumbnail', + 'attachment', + 'export' + ) + ), + + original_filename VARCHAR(500), + mime_type VARCHAR(150), + + storage_provider VARCHAR(30) + NOT NULL DEFAULT 'seaweedfs', + + storage_bucket VARCHAR(255) NOT NULL, + storage_key TEXT NOT NULL, + + size_bytes BIGINT + CHECK ( + size_bytes IS NULL + OR size_bytes >= 0 + ), + + checksum VARCHAR(255), + + upload_status VARCHAR(30) + NOT NULL DEFAULT 'pending' + CHECK ( + upload_status IN ( + 'pending', + 'uploading', + 'uploaded', + 'failed', + 'deleted' + ) + ), + + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + + UNIQUE(storage_provider, storage_bucket, storage_key) +); +``` + +Example SeaweedFS object path: + +```text +meeting-storage/ +users/{user_id}/ +meetings/{meeting_id}/ +original/ +recording.webm +``` + +--- + +## Transcripts + +Transcription happens locally on the user's machine and is uploaded to the backend afterward. + +```sql +CREATE TABLE transcripts ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + + meeting_id UUID NOT NULL + REFERENCES meetings(id) + ON DELETE CASCADE, + + source VARCHAR(30) + NOT NULL DEFAULT 'local' + CHECK ( + source IN ( + 'local', + 'imported' + ) + ), + + engine VARCHAR(100), + language VARCHAR(20), + + version INTEGER + NOT NULL DEFAULT 1 + CHECK (version > 0), + + is_active BOOLEAN + NOT NULL DEFAULT TRUE, + + plain_text TEXT, + transcript_json JSONB, + + duration_seconds NUMERIC(12,2) + CHECK ( + duration_seconds IS NULL + OR duration_seconds >= 0 + ), + + status VARCHAR(30) + NOT NULL DEFAULT 'uploaded' + CHECK ( + status IN ( + 'uploaded', + 'processing', + 'ready', + 'failed' + ) + ), + + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + + UNIQUE(meeting_id, version) +); +``` + +Example transcript JSON: + +```json +{ + "segments": [ + { + "speaker": "Speaker 1", + "start": 4.2, + "end": 9.8, + "text": "Let's review the deployment." + } + ] +} +``` + +--- + +## Meeting Summaries + +```sql +CREATE TABLE meeting_summaries ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + + meeting_id UUID NOT NULL + REFERENCES meetings(id) + ON DELETE CASCADE, + + summary_type VARCHAR(50) + NOT NULL DEFAULT 'general', + + content TEXT NOT NULL, + + model VARCHAR(100), + + metadata JSONB NOT NULL DEFAULT '{}', + + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); +``` + +Possible summary types: + +```text +general +short +executive +technical +action-focused +``` + +--- + +## Meeting Tasks + +```sql +CREATE TABLE meeting_tasks ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + + meeting_id UUID NOT NULL + REFERENCES meetings(id) + ON DELETE CASCADE, + + assigned_user_id UUID + REFERENCES users(id) + ON DELETE SET NULL, + + title VARCHAR(500) NOT NULL, + description TEXT, + + due_at TIMESTAMPTZ, + + status VARCHAR(30) + NOT NULL DEFAULT 'open' + CHECK ( + status IN ( + 'open', + 'in_progress', + 'completed', + 'cancelled' + ) + ), + + source VARCHAR(30) + NOT NULL DEFAULT 'ai' + CHECK ( + source IN ( + 'ai', + 'manual', + 'imported' + ) + ), + + source_metadata JSONB, + + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); +``` + +--- + +## Meeting Schedules / Reminders + +```sql +CREATE TABLE meeting_schedules ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + + meeting_id UUID NOT NULL + REFERENCES meetings(id) + ON DELETE CASCADE, + + task_id UUID + REFERENCES meeting_tasks(id) + ON DELETE CASCADE, + + user_id UUID NOT NULL + REFERENCES users(id) + ON DELETE CASCADE, + + schedule_type VARCHAR(50) NOT NULL, + + scheduled_at TIMESTAMPTZ NOT NULL, + + status VARCHAR(30) + NOT NULL DEFAULT 'pending' + CHECK ( + status IN ( + 'pending', + 'processing', + 'executed', + 'failed', + 'cancelled' + ) + ), + + payload JSONB NOT NULL DEFAULT '{}', + + executed_at TIMESTAMPTZ, + + attempts INTEGER + NOT NULL DEFAULT 0, + + last_error TEXT, + + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); +``` + +Example reminder payload: + +```json +{ + "channel": "email", + "message": "Prepare the deployment report" +} +``` + +--- + +## Processing Jobs + +MeetVault can use PostgreSQL itself as the first job queue. + +```sql +CREATE TABLE processing_jobs ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + + meeting_id UUID + REFERENCES meetings(id) + ON DELETE CASCADE, + + job_type VARCHAR(50) + NOT NULL + CHECK ( + job_type IN ( + 'summarize_meeting', + 'extract_action_items', + 'generate_reminders' + ) + ), + + status VARCHAR(30) + NOT NULL DEFAULT 'queued' + CHECK ( + status IN ( + 'queued', + 'running', + 'completed', + 'failed', + 'cancelled' + ) + ), + + priority SMALLINT + NOT NULL DEFAULT 0, + + attempts INTEGER + NOT NULL DEFAULT 0, + + max_attempts INTEGER + NOT NULL DEFAULT 3 + CHECK (max_attempts > 0), + + input_data JSONB, + output_data JSONB, + + error_message TEXT, + + available_at TIMESTAMPTZ + NOT NULL DEFAULT NOW(), + + locked_at TIMESTAMPTZ, + locked_by VARCHAR(255), + + started_at TIMESTAMPTZ, + completed_at TIMESTAMPTZ, + + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); +``` + +Example job pickup query: + +```sql +SELECT * +FROM processing_jobs +WHERE status = 'queued' +AND available_at <= NOW() +ORDER BY priority DESC, created_at +FOR UPDATE SKIP LOCKED +LIMIT 1; +``` + +This allows multiple workers without two workers processing the same job. + +Redis or another dedicated queue can be added later if required. + +--- + +# Indexes + +```sql +CREATE INDEX idx_meetings_owner +ON meetings(owner_user_id); + +CREATE INDEX idx_meetings_owner_started +ON meetings(owner_user_id, started_at DESC); + +CREATE INDEX idx_meetings_status +ON meetings(status); + +CREATE INDEX idx_meeting_files_meeting +ON meeting_files(meeting_id); + +CREATE INDEX idx_transcripts_meeting +ON transcripts(meeting_id); + +CREATE INDEX idx_transcripts_active +ON transcripts(meeting_id, is_active); + +CREATE INDEX idx_summaries_meeting +ON meeting_summaries(meeting_id); + +CREATE INDEX idx_tasks_meeting +ON meeting_tasks(meeting_id); + +CREATE INDEX idx_tasks_assigned_user +ON meeting_tasks(assigned_user_id); + +CREATE INDEX idx_schedules_due +ON meeting_schedules(status, scheduled_at); + +CREATE INDEX idx_processing_jobs_queue +ON processing_jobs(status, available_at, priority DESC); +``` + +The most important worker indexes are: + +```text +meeting_schedules(status, scheduled_at) +processing_jobs(status, available_at, priority) +``` + +--- + +# Automatic `updated_at` + +PostgreSQL does not automatically modify `updated_at` when a row changes. + +Create a shared trigger function: + +```sql +CREATE OR REPLACE FUNCTION set_updated_at() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_at = NOW(); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; +``` + +Apply it to relevant tables: + +```sql +CREATE TRIGGER trg_users_updated_at +BEFORE UPDATE ON users +FOR EACH ROW +EXECUTE FUNCTION set_updated_at(); + +CREATE TRIGGER trg_subscriptions_updated_at +BEFORE UPDATE ON subscriptions +FOR EACH ROW +EXECUTE FUNCTION set_updated_at(); + +CREATE TRIGGER trg_meetings_updated_at +BEFORE UPDATE ON meetings +FOR EACH ROW +EXECUTE FUNCTION set_updated_at(); + +CREATE TRIGGER trg_transcripts_updated_at +BEFORE UPDATE ON transcripts +FOR EACH ROW +EXECUTE FUNCTION set_updated_at(); + +CREATE TRIGGER trg_tasks_updated_at +BEFORE UPDATE ON meeting_tasks +FOR EACH ROW +EXECUTE FUNCTION set_updated_at(); + +CREATE TRIGGER trg_schedules_updated_at +BEFORE UPDATE ON meeting_schedules +FOR EACH ROW +EXECUTE FUNCTION set_updated_at(); +``` + +--- + +# Final Architecture + +```text + MeetVault + + USER MACHINE + │ + ┌──────────┴──────────┐ + │ │ + Recording Local STT + │ Whisper + │ │ + ▼ ▼ + Video/audio Transcript + │ │ + │ ▼ + │ Backend API + │ │ + │ ▼ + │ PostgreSQL + │ │ + │ processing_jobs + │ │ + │ ▼ + │ Summary Worker + │ / \ + │ / \ + ▼ ▼ ▼ + SeaweedFS meeting_tasks summaries + │ + ▼ + meeting_schedules + │ + ▼ + Reminder Worker +``` + +--- + +# Recommended MVP Stack + +A practical first implementation could use: + +```text +Frontend +React / Next.js + +Backend +FastAPI or Node.js + +Database +PostgreSQL + +Media Storage +SeaweedFS + +Local Transcription +Whisper / whisper.cpp + +Backend Jobs +PostgreSQL processing_jobs + +Workers +Summarization Worker +Reminder Worker +``` + +For the first version, MeetVault does not require: + +- Kafka +- Elasticsearch +- Kubernetes +- A vector database +- A separate microservice architecture +- Redis, unless job volume grows enough to justify it + +PostgreSQL + SeaweedFS + the backend API + workers is sufficient for the MVP and leaves room to scale later. diff --git a/Overview/meeting_system_architecture.drawio b/Overview/meeting_system_architecture.drawio new file mode 100644 index 0000000..8abbb7b --- /dev/null +++ b/Overview/meeting_system_architecture.drawio @@ -0,0 +1,385 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..e69de29 diff --git a/Wireframes/Desktop/.$MeetVault_UI_Wireframes_v2.drawio.bkp b/Wireframes/Desktop/.$MeetVault_UI_Wireframes_v2.drawio.bkp new file mode 100644 index 0000000..d59fe52 --- /dev/null +++ b/Wireframes/Desktop/.$MeetVault_UI_Wireframes_v2.drawio.bkp @@ -0,0 +1,577 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wireframes/Desktop/MeetVault-Desktop-Demo.7z b/Wireframes/Desktop/MeetVault-Desktop-Demo.7z new file mode 100644 index 0000000..f4056ba Binary files /dev/null and b/Wireframes/Desktop/MeetVault-Desktop-Demo.7z differ diff --git a/Wireframes/Desktop/MeetVault_Desktop_README.md b/Wireframes/Desktop/MeetVault_Desktop_README.md new file mode 100644 index 0000000..9711789 --- /dev/null +++ b/Wireframes/Desktop/MeetVault_Desktop_README.md @@ -0,0 +1,1049 @@ +# MeetVault Desktop + +MeetVault Desktop is the local desktop client for MeetVault. + +Its main purpose is to: + +- Record meeting audio/video +- Capture microphone and system audio +- Transcribe speech locally using Whisper +- Detect and separate speakers in real time using diart +- Show a real-time transcription overlay on top of other applications +- Allow users to rename detected speakers during the meeting +- Upload recordings to SeaweedFS +- Send the final transcript to the MeetVault backend +- Receive generated summaries, action items, and reminders from the backend + +The desktop application should continue to work locally during a meeting even if the backend is temporarily unavailable. + +--- + +# Core Desktop Architecture + +```text + MeetVault Desktop + + ┌─────────────────┐ + │ Desktop UI │ + │ React + Tauri │ + └────────┬────────┘ + │ + ▼ + ┌─────────────────────┐ + │ Audio Engine │ + │ │ + │ Microphone capture │ + │ System audio capture│ + │ Audio mixing │ + └─────────┬───────────┘ + │ + ┌───────────────┴───────────────┐ + │ │ + ▼ ▼ + ┌───────────────┐ ┌────────────────┐ + │ Whisper │ │ diart │ + │ │ │ │ + │ transcription │ │ diarization │ + │ + timestamps │ │ speaker labels │ + └───────┬───────┘ └───────┬────────┘ + │ │ + └──────────────┬───────────────┘ + ▼ + ┌────────────────────┐ + │ Segment Merger │ + │ │ + │ text + timestamps │ + │ speaker timeline │ + └─────────┬──────────┘ + │ + ┌─────────────┴──────────────┐ + │ │ + ▼ ▼ + ┌─────────────────┐ ┌──────────────────┐ + │ Live Transcript │ │ Realtime Overlay │ + │ Window │ │ Always-on-top │ + └─────────────────┘ └──────────────────┘ +``` + +--- + +# Recommended Desktop Stack + +```text +Desktop shell +Tauri + +UI +React + TypeScript + +Native/local processing +Rust where required + +Transcription +Whisper / whisper.cpp + +Speaker diarization +diart + +Backend API +MeetVault backend + +Structured data +PostgreSQL + +Media storage +SeaweedFS +``` + +Tauri is recommended because the application needs: + +- Multiple windows +- Transparent overlay windows +- Always-on-top behavior +- Native audio access +- Local file access +- Background processes +- System tray support +- Global shortcuts +- Lower memory usage than a full Electron application + +--- + +# Real-Time Audio Pipeline + +The application processes meeting audio locally. + +```text +Microphone + │ + ├────────────────┐ + │ │ +System Audio │ + │ │ + └──────┬─────────┘ + ▼ + Audio Mixer + │ + ▼ + Audio Stream + │ + ┌─────┴─────┐ + │ │ + ▼ ▼ + Whisper diart + │ │ + │ │ + ▼ ▼ + text + speaker + timestamps segments + │ │ + └─────┬─────┘ + ▼ + Segment Merger + │ + ▼ + Live Transcript Event + │ + ┌─────┴────────┐ + │ │ + ▼ ▼ + Main Window Overlay Window +``` + +--- + +# Whisper + +Whisper is responsible for speech-to-text. + +For the first version, a lightweight local runtime such as `whisper.cpp` is recommended. + +The transcription engine should provide: + +```text +text +start timestamp +end timestamp +confidence if available +partial/final state +``` + +Example: + +```json +{ + "start": 18.2, + "end": 21.7, + "text": "We should finish the API before Friday.", + "final": true +} +``` + +## Suggested Model Strategy + +Default: + +```text +Whisper small +``` + +Possible fallback for lower-end hardware: + +```text +Whisper base +``` + +Possible higher-accuracy option: + +```text +Whisper medium +``` + +The selected model should be configurable in Settings. + +The goal is to balance: + +```text +accuracy +latency +CPU/GPU usage +memory usage +``` + +--- + +# diart + +diart is responsible for real-time speaker diarization. + +It does not need to know a person's real identity. + +It only needs to produce stable speaker labels such as: + +```text +Speaker 1 +Speaker 2 +Speaker 3 +``` + +Example diarization result: + +```json +{ + "speaker": "speaker_1", + "start": 18.0, + "end": 22.0 +} +``` + +The application combines the diart result with the Whisper result using timestamps. + +--- + +# Transcript + Speaker Merge + +Whisper may produce: + +```text +18.2 - 21.7 + +"We should finish the API before Friday." +``` + +diart may produce: + +```text +18.0 - 22.0 + +speaker_1 +``` + +The merger produces: + +```json +{ + "speaker_id": "speaker_1", + "speaker_name": "Speaker 1", + "start": 18.2, + "end": 21.7, + "text": "We should finish the API before Friday." +} +``` + +This becomes the core live transcript segment. + +--- + +# Speaker Rename + +Detected speakers initially appear as: + +```text +Speaker 1 +Speaker 2 +Speaker 3 +``` + +The user can rename them at any time. + +Example: + +```text +Speaker 1 → Alice +Speaker 2 → Bob +``` + +The underlying diarization ID does not change. + +```text +speaker_1 + ↓ +display name + ↓ +Alice +``` + +Recommended local mapping: + +```json +{ + "speaker_1": "Alice", + "speaker_2": "Bob" +} +``` + +Existing transcript lines and future overlay lines should immediately use the new display name. + +--- + +# Real-Time Overlay + +The overlay is a dedicated transparent Tauri window. + +It should be: + +- Always on top +- Transparent or semi-transparent +- Resizable +- Draggable +- Lockable +- Optionally click-through +- Hotkey controlled +- Able to show only the latest few transcript lines + +Example: + +```text +┌──────────────────────────────────────────────┐ +│ Alice │ +│ We should finish the API before Friday. │ +│ │ +│ Bob │ +│ I'll prepare the deployment plan tonight. │ +└──────────────────────────────────────────────┘ +``` + +The overlay should update continuously while the meeting is running. + +--- + +# Overlay Update Model + +The desktop app should distinguish between: + +```text +partial transcript +final transcript +``` + +Example: + +```text +Speaker 1: +We should finish the... +``` + +updates to: + +```text +Speaker 1: +We should finish the API before Friday. +``` + +A partial line may be visually distinguished from finalized text. + +Recommended behavior: + +```text +partial +→ replace current live line + +final +→ commit transcript segment +→ append to transcript history +``` + +--- + +# Main UI Screens + +## Dashboard + +Purpose: + +- Start a new meeting +- View recent meetings +- Check local transcription state +- Check SeaweedFS/backend connectivity +- Open Settings + +Main actions: + +```text +Start Meeting +Open Meeting +Preview Overlay +Settings +``` + +--- + +# Live Meeting Screen + +The Live Meeting screen is the main control surface while recording. + +It should show: + +- Recording state +- Meeting timer +- Live transcript +- Speaker labels +- Rename controls +- Microphone state +- System audio state +- Whisper model status +- Diarization status +- Overlay toggle +- Pause +- Finish meeting + +Example: + +```text +Weekly Product Sync REC 00:18:42 + +Live transcript + +Alice +We should finalize the API contract. + +Bob +I'll update the database migration. + +Alice +Great. I'll prepare the release notes. + + +Speakers + +Alice Rename +Bob Rename + + +Microphone ON +System Audio ON +Whisper Healthy +diart Healthy + +[Pause] [Show Overlay] [Finish Meeting] +``` + +--- + +# Speaker Management + +The speaker panel should display the diarization speakers detected in the current meeting. + +Example: + +```text +speaker_1 Alice +speaker_2 Bob +speaker_3 Speaker 3 +``` + +The user may rename a speaker at any time. + +The rename should affect: + +- Current live transcript +- Overlay +- Final transcript +- Meeting review + +--- + +# Meeting Details Screen + +After a meeting ends, the Meeting Details screen provides: + +- Video/audio playback +- Meeting information +- Transcript +- Speaker names +- Summary +- Action items +- Reminders +- Export options + +Example layout: + +```text +Weekly Product Sync +Sep 27, 2026 • 48 min + +┌──────────────────────────┐ +│ Video Player │ +└──────────────────────────┘ + +Transcript | Summary | Action Items | Files + +Alice 00:18:41 +We should finalize the API contract. + +Bob 00:18:49 +I'll update the database migration. +``` + +The transcript timestamp should be clickable so playback can jump to that position. + +--- + +# Settings + +Settings should include the following sections. + +## General + +```text +Launch at startup +Keep running in tray +Theme +Global hotkey +``` + +## Audio + +```text +Microphone device +System audio device +Input level +System audio level +Audio monitoring +``` + +## Transcription + +```text +Whisper model +Compute device +Language +Chunk size +Voice activity detection +``` + +Suggested model options: + +```text +base +small +medium +``` + +## Diarization + +```text +Enable speaker diarization +Maximum expected speakers +Speaker clustering sensitivity +``` + +## Overlay + +```text +Enable overlay +Always on top +Click-through mode +Opacity +Font size +Maximum visible lines +Show speaker labels +Screen position +``` + +## Storage & Sync + +```text +Local recording folder +Automatic upload +Upload after meeting +Delete local media after successful upload +Backend connection +SeaweedFS upload status +``` + +## Account + +```text +Signed-in user +Subscription +Sign out +``` + +--- + +# Local Meeting Session Model + +During a live meeting, the desktop application should maintain a local session object. + +Example: + +```json +{ + "meeting_id": "uuid", + "started_at": "2026-09-27T09:00:00Z", + "status": "recording", + + "speakers": { + "speaker_1": "Alice", + "speaker_2": "Bob" + }, + + "segments": [ + { + "id": "segment-1", + "speaker_id": "speaker_1", + "start": 18.2, + "end": 21.7, + "text": "We should finish the API before Friday." + } + ] +} +``` + +This state should be stored locally during recording so a temporary network failure does not lose the transcript. + +--- + +# Transcript Segment Model + +A transcript should internally be represented as segments rather than only one large text string. + +Recommended structure: + +```json +{ + "id": "segment-uuid", + "speaker_id": "speaker_1", + "speaker_name": "Alice", + "start": 18.2, + "end": 21.7, + "text": "We should finish the API before Friday.", + "final": true +} +``` + +Benefits: + +- Real-time rendering +- Speaker rename +- Timestamp navigation +- Transcript editing +- Summary generation +- Export +- Search + +--- + +# Recording Flow + +```text +User clicks Start Meeting + │ + ▼ +Create local meeting session + │ + ▼ +Start microphone capture + │ + ├──── Start system audio capture + │ + ▼ +Start recording + │ + ├──── Start Whisper + │ + └──── Start diart + │ + ▼ + Live transcript events + │ + ┌──────┴──────┐ + ▼ ▼ + Main UI Overlay +``` + +--- + +# Meeting Finish Flow + +```text +User clicks Finish Meeting + │ + ▼ +Stop audio capture + │ + ▼ +Finalize Whisper segments + │ + ▼ +Finalize speaker mapping + │ + ▼ +Save local transcript + │ + ├──── upload media ─────► SeaweedFS + │ + └──── send transcript ──► Backend API + │ + ▼ + PostgreSQL + │ + ▼ + processing_jobs + │ + ┌───────┴────────┐ + ▼ ▼ + Summarizer Action Items + │ + ▼ + Reminders +``` + +--- + +# Upload Strategy + +Large media should not pass through the backend API. + +Recommended: + +```text +Desktop + │ + │ request presigned URL + ▼ +Backend + │ + │ returns temporary upload URL + ▼ +Desktop ───────────────────────► SeaweedFS + media file +``` + +The transcript is small and can be sent directly through the backend API. + +```text +Desktop + │ + │ transcript JSON + ▼ +Backend API + │ + ▼ +PostgreSQL +``` + +--- + +# Offline / Network Failure Behavior + +The desktop application should not require continuous backend connectivity during a meeting. + +If the backend becomes unavailable: + +```text +recording continues +transcription continues +diarization continues +overlay continues +local saving continues +``` + +After the connection returns: + +```text +upload recording +send transcript +sync meeting metadata +``` + +This is an important design requirement for MeetVault Desktop. + +--- + +# Background Components + +The desktop application can be separated into these logical modules: + +```text +MeetVault Desktop + +├── UI +│ ├── Dashboard +│ ├── Live Meeting +│ ├── Meeting Details +│ └── Settings +│ +├── Overlay +│ └── Realtime subtitle window +│ +├── Audio Engine +│ ├── Microphone +│ ├── System audio +│ └── Mixer +│ +├── Speech Engine +│ ├── Whisper +│ └── diart +│ +├── Transcript Engine +│ ├── Segment merger +│ ├── Speaker mapping +│ └── Transcript state +│ +├── Recorder +│ └── Media recording +│ +├── Local Storage +│ └── Session recovery/cache +│ +└── Sync Client + ├── Backend API + └── SeaweedFS upload +``` + +--- + +# Realtime Event Flow + +Internally, the UI should consume events rather than directly controlling the models. + +Example events: + +```text +audio.started +audio.stopped + +transcript.partial +transcript.final + +speaker.detected +speaker.changed + +speaker.renamed + +recording.started +recording.stopped + +upload.started +upload.progress +upload.completed +upload.failed +``` + +Example event: + +```json +{ + "type": "transcript.final", + "payload": { + "speaker_id": "speaker_1", + "start": 18.2, + "end": 21.7, + "text": "We should finish the API before Friday." + } +} +``` + +Both the main transcript screen and overlay can subscribe to the same event stream. + +--- + +# Realtime Performance Goals + +Initial targets: + +```text +Overlay update latency +< 2 seconds + +Partial transcript refresh +~ 0.5–1.5 seconds + +Final segment delay +~ 1–3 seconds + +Overlay frame impact +Minimal + +Meeting duration +Several hours without restart +``` + +Actual latency depends on: + +- Whisper model +- CPU/GPU +- Audio chunk size +- Number of speakers +- diart processing speed + +--- + +# Speaker Diarization Limitations + +Speaker diarization is not perfect. + +Expected difficult cases include: + +- Two people speaking simultaneously +- Very similar voices +- Poor microphone quality +- Strong background noise +- Echo +- Speakers far from the microphone +- Very short speech segments + +The application should allow users to correct speaker names and, later, reassign individual transcript segments if necessary. + +No biometric speaker identification is required. + +MeetVault only needs: + +```text +who spoke when +``` + +not: + +```text +who is this person in real life +``` + +--- + +# Security + +Permanent SeaweedFS credentials must never be stored in the renderer/frontend. + +The desktop app should use: + +```text +Backend authentication ++ +temporary presigned upload URLs +``` + +Sensitive local files should be stored only in the configured MeetVault application data directory. + +Authentication tokens should use the operating system's secure credential storage when possible. + +--- + +# MVP Scope + +The first desktop release should focus on: + +- Start/stop meeting recording +- Microphone capture +- System audio capture +- Local Whisper transcription +- Real-time diart speaker separation +- Speaker 1 / Speaker 2 labels +- Rename speakers +- Real-time overlay +- Local transcript storage +- Meeting playback +- Transcript review +- Upload recording to SeaweedFS +- Send transcript to backend +- Receive generated summary +- View action items +- View reminders + +Not required for the first release: + +- Speaker biometric identification +- Automatic real-name recognition +- Cloud transcription +- Kafka +- Elasticsearch +- Kubernetes +- Vector database +- Complex microservice architecture + +--- + +# Final Desktop Architecture + +```text + MeetVault Desktop + + Audio Sources + / \ + Microphone System Audio + \ / + \ / + Audio Mixer + │ + ┌─────────┴─────────┐ + │ │ + ▼ ▼ + Whisper diart + │ │ + transcript speakers + │ │ + └─────────┬─────────┘ + ▼ + Segment Merger + │ + ┌─────────────┴──────────────┐ + │ │ + ▼ ▼ + Live Meeting UI Realtime Overlay + │ + ▼ + Local Session Store + │ + meeting completes + │ + ┌───────┴────────┐ + │ │ + ▼ ▼ + SeaweedFS Backend API + media transcript + │ + ▼ + PostgreSQL + │ + ▼ + processing_jobs + │ + ┌────────┴─────────┐ + ▼ ▼ + Summary Action Items + │ + ▼ + Reminders +``` diff --git a/Wireframes/Desktop/MeetVault_UI_Wireframes_v2.drawio b/Wireframes/Desktop/MeetVault_UI_Wireframes_v2.drawio new file mode 100644 index 0000000..856eb66 --- /dev/null +++ b/Wireframes/Desktop/MeetVault_UI_Wireframes_v2.drawio @@ -0,0 +1,568 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wireframes/Mobile/.$MeetVault_Android_UI_Wireframes.drawio.bkp b/Wireframes/Mobile/.$MeetVault_Android_UI_Wireframes.drawio.bkp new file mode 100644 index 0000000..1c7382c --- /dev/null +++ b/Wireframes/Mobile/.$MeetVault_Android_UI_Wireframes.drawio.bkp @@ -0,0 +1,17 @@ + \ No newline at end of file diff --git a/Wireframes/Mobile/MeetVault_Android_UI_Modern_v2.drawio b/Wireframes/Mobile/MeetVault_Android_UI_Modern_v2.drawio new file mode 100644 index 0000000..1a4fd6c --- /dev/null +++ b/Wireframes/Mobile/MeetVault_Android_UI_Modern_v2.drawio @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/Wireframes/Mobile/MeetVault_Android_UI_Wireframes.drawio b/Wireframes/Mobile/MeetVault_Android_UI_Wireframes.drawio new file mode 100644 index 0000000..2c3c193 --- /dev/null +++ b/Wireframes/Mobile/MeetVault_Android_UI_Wireframes.drawio @@ -0,0 +1,455 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +