ScribeNode is a full-stack, high-throughput AI audio transcription and speech intelligence web application. Powered by Google's specialized Gemini Flash and Audio AI model suite (gemini-3.8-flash, gemini-3.7-flash, gemini-3.6-flash, and gemini-3.5-flash), ScribeNode transforms raw podcast recordings, meeting audio, interviews, and voice notes into polished clean-verbatim transcripts, structured chapters, executive summaries, and actionable key takeaways.
Note on Development: This project was built using AI-assisted pair programming ("vibecoded") and then manually audited, refined, and tested for code quality, type safety, and container security.
- 🎧 Broad Format & Configurable Uploads: Transcribe MP3, WAV, M4A, OGG, and FLAC audio files with configurable file size limits (
MAX_UPLOAD_SIZE_MB, defaults to 100MB). - 📻 Podcast RSS Feed & Remote URL Ingestion: Directly transcribe episodes from public podcast RSS feeds or direct audio URLs with live feed inspection and episode selection.
- 📖 Custom Vocabulary & Technical Glossary: Inject domain-specific terminology, brand names, acronyms, and proper nouns into transcription prompts to maximize recognition accuracy.
- ✏️ Inline Quick Edit & Global Speaker Renaming: In-place transcript editing via markdown editor and one-click global speaker alias replacement across entire transcripts.
- 🎙️ High-Fidelity Audio Transcription: Multimodal transcription with
gemini-3.8-flashfeaturing conversational reasoning, speaker diarization, and clean-verbatim parsing. - 🔄 Intelligent Multi-Tier Failover Cascade: Seamless automatic failover across
gemini-3.8-flash➡️gemini-3.7-flash➡️gemini-3.6-flash➡️gemini-3.5-flash➡️gemini-2.5-flash➡️gemini-3.5-flash-lite➡️gemini-3.1-flash-lite➡️gemini-flash-lite-latest➡️gemini-flash-latest. - 🩺 Per-Model Diagnostics & Error Translation: Live Model Orchestration inspector translating raw 503/429/400/403 errors into human-friendly explanations with one-click recovery.
- 📱 Adaptive Mobile Workspace: Full responsive mobile navigation drawer, segmented workspace tabs, and touch-friendly controls with zero desktop layout regression.
- ⚡ Clean Verbatim Transcription: Specialized prompting removes speech disfluencies, filler words (uh, um, like), stutters, and false starts while preserving technical domain terms.
- 👥 Speaker Diarization & Name Detection: Contextually identifies speaker names and formats dialogue seamlessly with bold speaker labels and timestamps.
- 📌 Automated Chaptering & Intelligence: Generates timestamped chapters, high-level summaries, key bulleted takeaways, and actionable next steps powered by
gemini-3.8-flash. - 🔍 Interactive Live Viewer & Audio Sync: Live transcript filtering, full-text search, jump-to-timestamp playback, and text-selection inspection.
- 📥 Export & Sharing Options: Download transcripts and intelligence assets in Markdown (
.md) or Plain Text (.txt), with instant copy-to-clipboard support. - 🔒 Private Homelab & Basic Auth Ready: Native support for HTTP Basic Authentication and Docker containerization for secure private self-hosting.
| Layer | Technology |
|---|---|
| Frontend | React 19, Vite 8, Tailwind CSS v4, Lucide React Icons, Motion v12 |
| Backend | Node.js (v24 LTS & v26), Express 5, Multer File Upload |
| AI Audio Engine | Google Gen AI SDK (@google/genai), Gemini 3.8 Flash (gemini-3.8-flash), Gemini 3.7 Flash |
| AI Reasoning & Analysis | Google Gemini 3.8 Flash (gemini-3.8-flash), Gemini 3.7 Flash, Gemini 3.6 Flash / Flash Lite |
| Bundler & Build | ESBuild (Node CJS bundling), Vite 8 |
| Container & CI/CD | Docker (node:26-alpine), Docker Compose, GitHub Actions (Node 24 / 26), GHCR |
- Upload & Ingestion: Audio files are uploaded to the Express backend via streaming multipart forms with configurable payload limits (
MAX_UPLOAD_SIZE_MB). - Multimodal Audio Comprehension: The engine routes directly to
gemini-3.8-flash, combining acoustic processing with contextual reasoning to recognize domain vocabulary, infer human speaker names, and generate structured clean-verbatim transcripts in a single pass. - Resilient Multi-Tier Fallback Cascade: If the primary model encounters temporary capacity constraints (503), quota limits (429), or parameter incompatibilities, the engine automatically fails over through:
gemini-3.8-flash➡️gemini-3.7-flash➡️gemini-3.6-flash➡️gemini-3.5-flash➡️gemini-2.5-flash➡️gemini-3.5-flash-lite➡️gemini-3.1-flash-lite➡️gemini-flash-lite-latest➡️gemini-flash-latest - Interactive Model Selection & Instant Reset: Users can specifically choose any model from the dropdown to prioritize it first, automatically re-ordering the fallback cascade with instant one-click reset to the standard built-in order.
- Console & Container Startup Visibility: ScribeNode displays the active primary model, port binding, and the complete fallback chain in the pretty console and Docker startup banner upon boot.
- Developer System Instructions: Full system instructions (
BASE_TRANSCRIPTION_STANDARDS/getSystemInstruction()) guide model outputs to strict, publication-ready Markdown. - Downstream Intelligence Generation: Executive summaries, timestamped chapters, bulleted key takeaways, and social media posts are processed using
gemini-3.8-flash(with fallback across Flash reasoning models). - Live Orchestration & Friendly Diagnostics: The UI tracks individual model health in real time, translating raw API errors into clear diagnostic messages ("Model demand too high, try again later", "Rate limit reached", "Configuration parameters adapted") with instant one-click recovery.
- State Persistence: Processing jobs, transcripts, chapters, and audio files are persisted to
/app/uploads/jobs.jsonwithin the mounted volume (scribenode_uploads), preserving all transcript data across container restarts and rebuilds.
To run ScribeNode, configuration values can be provided via a .env file or directly passed as environment variables in Docker Compose / container settings.
ScribeNode connects to Google's specialized Gemini Audio and Flash AI model suite (gemini-3.8-flash, gemini-3.7-flash, gemini-3.6-flash, gemini-3.5-flash, etc.) using the official @google/genai SDK. For ScribeNode to function properly, your Google Cloud project MUST have the Generative Language API (generativelanguage.googleapis.com) enabled.
- Navigate to the Google AI Studio API Key Portal.
- Click Create API key.
- Select Create API key in new project (or pick an existing Google Cloud project).
- Copy your newly created API key (
AIzaSy...).
Note: Keys created directly through Google AI Studio have the Generative Language API automatically enabled by default!
If you manage your own Google Cloud organization or wish to create/restrict credentials inside Google Cloud Console:
- Open the Google Cloud Console.
- Select or create your Google Cloud Project.
- Open the API Library or go directly to the Generative Language API Library Page.
- Click ENABLE to activate the Generative Language API (
generativelanguage.googleapis.com). - Navigate to APIs & Services > Credentials in the sidebar.
- Click Create Credentials $\rightarrow$ API key.
- (Recommended Security Best Practice) Click Edit API key, and under API restrictions, choose Restrict key and select Generative Language API.
- Paste the key into your
.envfile asGEMINI_API_KEY="AIzaSy...".
PERMISSION_DENIEDorAPI has not been used in project ... or it is disabled: Your Google Cloud project does not have the Generative Language API activated. Visit console.cloud.google.com/apis/library/generativelanguage.googleapis.com and click Enable.API_KEY_INVALID: Verify that the API key was copied completely with no leading/trailing spaces or typos.- Quota & Billing: Google AI Studio offers a free quota tier. For high-volume production or enterprise pipelines, attach a Cloud Billing account in Google Cloud Console to ensure continuous throughput.
Create a .env file in the same directory as docker-compose.yml or your application root:
# 🔑 REQUIRED: Google Gemini API Key
# Required API in Google Cloud: "Generative Language API" (generativelanguage.googleapis.com)
# Obtain via https://aistudio.google.com/app/apikey or https://console.cloud.google.com/apis/library/generativelanguage.googleapis.com
GEMINI_API_KEY="AIzaSyYourActualGeminiApiKeyHere"
# 🌐 OPTIONAL: Base URL of your app instance
# Default: http://localhost:3000
APP_URL="http://localhost:3000"
# 🔌 OPTIONAL: Container/Server Port
# Default: 3000 (Can be set to 4200, 8080, etc.)
PORT=3000
# 🏷️ OPTIONAL: Custom Page Title for Browser Tab
# Default: ScribeNode – Transcription Engine
APP_TITLE="ScribeNode – Transcription Engine"
# 🔒 OPTIONAL: Private HTTP Basic Authentication
# Basic Auth is FULLY DISABLED by default.
# To explicitly enable password protection, set BASIC_AUTH_ENABLED="true" and configure user/pass:
BASIC_AUTH_ENABLED="false"
BASIC_AUTH_USER="admin"
BASIC_AUTH_PASS="your_secure_password_here"
# 📦 OPTIONAL: Disable Preseeded Example Items
# Defaults to false. Set to "true" to prevent example audio items from being added on startup.
DISABLE_DEFAULT_ITEMS="false"
# 📤 OPTIONAL: Maximum Upload Size in Megabytes (MB)
# Defaults to 100MB. Increase this value (e.g. 250, 500, or 1000) for long multi-hour podcast recordings or uncompressed audio.
MAX_UPLOAD_SIZE_MB=100
# 🛡️ OPTIONAL: Remote Podcast & RSS Domain Allowlist (SSRF Defense)
# Comma-separated list of allowed domains or subdomains for fetching remote audio or RSS feeds.
# Major podcast platforms and CDNs (Buzzsprout, Libsyn, Spotify, Apple, Transistor, Megaphone, AWS S3, Google Cloud, etc.) are permitted by default.
# Set to "*" to permit all public internet domains while maintaining full IP/CIDR and cloud metadata SSRF defenses.
ALLOWED_REMOTE_DOMAINS=""ScribeNode is optimized for home lab deployment via Docker Compose using either local compilation or pre-built container images from GitHub Container Registry (GHCR).
Docker Compose supports two primary ways to set environment variables for your ScribeNode container:
-
Recommended Method — Central
.envFile:- Place a
.envfile alongsidedocker-compose.yml. - The
docker-compose.ymlfile uses variable placeholders (e.g.,GEMINI_API_KEY=${GEMINI_API_KEY}). - Why this is best: Keeps sensitive secrets (like API keys and passwords) out of
docker-compose.yml, making your compose file safe to commit to Git or share.
- Place a
-
Alternative Method — Direct Inline Values in
docker-compose.yml:- Hardcode literal values directly into
docker-compose.yml(e.g.,- GEMINI_API_KEY=AIzaSyYourKeyHere). - Note: If you hardcode values directly in
docker-compose.yml, you do not need a.envfile, but be careful not to expose API keys publicly.
- Hardcode literal values directly into
If an environment variable is defined in multiple places, Docker Compose resolves values in the following precedence order (highest priority wins):
-
Explicit values hardcoded in
docker-compose.yml: E.g.,- GEMINI_API_KEY=my_hardcoded_keyoverrides everything. -
Host shell environment variables: E.g., running
export GEMINI_API_KEY="key"in terminal beforedocker compose up. -
Values in the
.envfile: Key-value pairs defined in the.envfile sitting next todocker-compose.yml. -
Default fallbacks inside
${VAR:-default}syntax: E.g.,${PORT:-3000}uses3000ifPORTis omitted from both shell and.env.
Save the following docker-compose.yml file to your deployment directory:
services:
scribenode:
# -------------------------------------------------------------------------
# Option 1: Pull official pre-built public image (Recommended, zero build step)
image: ghcr.io/willpresley/scribenode-podcast-transcription:latest
# Option 2: Or build locally from source code
# build:
# context: .
# dockerfile: Dockerfile
# Option 3: Or pull from your own private/forked GHCR image
# image: ghcr.io/YOUR_FORK_USERNAME/scribenode-podcast-transcription:latest
# -------------------------------------------------------------------------
container_name: scribenode-app
restart: unless-stopped
ports:
- "${PORT:-3000}:${PORT:-3000}"
# Variable references pass values automatically from your .env file
environment:
- NODE_ENV=production
- PORT=${PORT:-3000}
- GEMINI_API_KEY=${GEMINI_API_KEY}
- APP_URL=${APP_URL:-http://localhost:3000}
- APP_TITLE=${APP_TITLE:-ScribeNode – Transcription Engine}
- BASIC_AUTH_ENABLED=${BASIC_AUTH_ENABLED:-false}
- BASIC_AUTH_USER=${BASIC_AUTH_USER:-}
- BASIC_AUTH_PASS=${BASIC_AUTH_PASS:-}
- DISABLE_DEFAULT_ITEMS=${DISABLE_DEFAULT_ITEMS:-false}
- MAX_UPLOAD_SIZE_MB=${MAX_UPLOAD_SIZE_MB:-100}
volumes:
- scribenode_uploads:/app/uploads
volumes:
scribenode_uploads:Create a .env file in the same directory:
GEMINI_API_KEY=AIzaSyYourActualGeminiApiKeyHere
PORT=3000
APP_TITLE=ScribeNode – Homelab Engine
BASIC_AUTH_ENABLED=false
DISABLE_DEFAULT_ITEMS=truedocker compose up -dYour ScribeNode container will automatically read the .env file, bind to the configured port, and persist audio jobs to the scribenode_uploads volume!
ScribeNode utilizes GitHub Container Registry (GHCR) for continuous automated container builds via GitHub Actions (.github/workflows/deploy.yml).
Once the package is set to Public, anyone can pull and run the pre-built Docker image directly on any server or homelab node without needing a GitHub account or personal access token:
# Direct Docker run
docker run -d \
-p 3000:3000 \
--name scribenode-app \
--env-file .env \
-v scribenode_uploads:/app/uploads \
ghcr.io/willpresley/scribenode-podcast-transcription:latestWhen you first push code to main/master, GitHub Actions will build and publish your image to GHCR as Private by default. To allow anyone to pull the image publicly:
- Navigate to your GitHub Profile or Organization page.
- Click the Packages tab.
- Click on the
scribenode-podcast-transcriptionpackage. - In the right sidebar, click Package settings.
- Scroll down to the Danger Zone section and click Change package visibility.
- Select Public, type
scribenode-podcast-transcriptionto confirm, and click I understand the consequences, make this package public. - (Optional) Under Repository source, link the package to your
scribenode-podcast-transcriptionrepository to enable automatic public synchronization.
If you maintain a private fork of ScribeNode and want to keep your GHCR package private:
Ensure GitHub Actions has permission to publish images:
- In your repository, go to Settings $\rightarrow$ Actions $\rightarrow$ General.
- Under Workflow permissions, select Read and write permissions.
- Click Save.
To pull a private package on your home server or VM:
- Generate a C
0 comments
log in to comment.