Architecture Diagram
VMSC is an Electron application with three distinct runtime contexts that communicate through well-defined boundaries. The diagram below shows the high-level data flow between all major subsystems.
┌──────────────────────────────────┐ IPC (invoke/handle) ┌──────────────────────────┐
│ Main Process │◄─────────────────────────►│ Renderer (React) │
│ (Node.js) │ │ (UI) │
│ │ ipcRenderer.on() │ │
│ ┌────────────────────────────┐ │──────────────────────────►│ ┌────────────────────┐ │
│ │ EventBus │ │ push events │ │ Zustand Stores │ │
│ │ RuleEngine │ │ │ │ React Components │ │
│ │ ActionQueue │ │ │ │ IPC Hooks │ │
│ │ ActionExecutor │ │ │ └────────────────────┘ │
│ │ ConfigStore (encrypted) │ │ └──────────────────────────┘
│ │ ViewerDB (SQLite) │ │
│ │ ToxicityFilter │ │
│ │ GemmaManager (AI) │ │
│ │ LicenseManager (JWT) │ │
│ └────────────────────────────┘ │
│ │
│ ┌──────────┐ ┌──────────────┐ │ HTTP / WebSocket ┌──────────────────────────┐
│ │ TikTok │ │ Overlay │ │◄───────────────────►│ OBS Browser Source │
│ │ Source │ │ Server │ │ port 7890 │ Standalone Browser │
│ └──────────┘ │ (Express+WS) │ │ │ Mobile Overlay Viewer │
│ └──────────────┘ │ └──────────────────────────┘
│ │
│ ┌──────────┐ ┌──────────────┐ │ OSC (UDP) ┌──────────────────────────┐
│ │ PiShock │ │ VRChat OSC │ │◄───────────────────►│ VRChat Client │
│ │ (WS) │ │ Output │ │ port 9000/9001 │ Avatar Parameters │
│ └──────────┘ └──────────────┘ │ └──────────────────────────┘
└──────────────────────────────────┘
Main Process
The main process is the backbone of VMSC. It runs in a full Node.js environment with access to the filesystem, native modules, network sockets, and Electron APIs. All business logic executes here to ensure the renderer cannot bypass security boundaries like tier limits or license checks.
Core Services
- EventBus — Central pub/sub hub. Every stream event (chat, gift, follow, like, share) flows through the EventBus. It maintains a rolling history of recent events for the UI and debug tools.
- RuleEngine — Evaluates user-defined rules against incoming events. Each rule has a trigger (event type + filters) and a list of action references. The engine checks cooldowns, per-user limits, and filter logic (AND/OR) before dispatching.
- ActionQueue — Manages execution ordering. Actions can have delays, and gifts are coalesced into combined totals before execution. The queue ensures actions run sequentially within a pool and provides backpressure when the system is overloaded.
- ActionExecutor — Resolves action definitions to output drivers and executes them. Supports targets like VRChat OSC, PiShock, webhooks, scripts, keyboard input, audio playback, Discord webhooks, and more.
- ConfigStore — Wraps
electron-storewith AES-256-GCM encryption for sensitive fields (API keys, session tokens). Emitsconfig-changedevents whenever any value is updated, which the renderer subscribes to for live reactivity. - ViewerDB — SQLite-backed viewer database. Tracks every unique viewer with metadata: first seen, last seen, message count, gift total, custom tags, and notes. Supports full-text search.
- GemmaManager — Local AI inference engine using
node-llama-cpp. Manages GGUF model downloads, loading, context windows, and chat sessions. Supports both built-in model registry and custom HuggingFace models. Requires Premium tier. - LicenseManager — Handles Patreon OAuth, JWT token exchange with the Gatekeeper server, tier detection (Free/Premium), and token refresh. Tier limits are enforced in the main process on every IPC handler that creates or modifies gated resources.
Sources (Inbound)
- TikTokSource — Connects to TikTok LIVE via
tiktok-live-connector. Normalizes platform events into the unifiedStreamEventschema and publishes them to the EventBus. - TikFinitySource — Alternative TikTok connector using the TikFinity WebSocket bridge. Provides the same event normalization but routes through TikFinity's infrastructure.
- LiveChecker — Polls TikTok to detect when a stream goes live or offline. Emits status change events for the UI.
Outputs (Outbound)
- VRChatOSCOutput — Sends OSC messages to VRChat for avatar parameter control and chatbox messages. Also includes an OSC listener for receiving parameter updates back from VRChat.
- PiShockOutput — WebSocket connection to PiShock API for vibrate, beep, and shock commands. Includes emergency stop and shocker device discovery.
- WebhookOutput / DiscordWebhookOutput — HTTP POST to arbitrary URLs or Discord webhook endpoints with templated payloads.
- ScriptRunnerOutput — Spawns external scripts (.exe, .bat, .ps1, .py, .js) as child processes with event data passed as arguments.
- Local Actions — Built-in outputs for keyboard input simulation, audio playback, global variables, countdown timers, process kill, alert triggers, goal increments, file writes, stream overlay control, and widget control.
Renderer Process
The renderer is a React 18 application bundled with Vite. It provides the entire user interface: rule editor, action builder, settings panels, viewer database browser, AI chat, overlay editor, and dashboard widgets.
The renderer has no direct access to Node.js APIs, the filesystem, or native modules. All communication with the main process goes through the preload bridge (see below). State management uses Zustand stores that sync with the main process via IPC.
Key Subsystems
- Rule/Action Editor — Visual builder for event-driven automation rules. Each rule binds a trigger (event type + filters) to one or more actions with delays and cooldowns.
- Overlay Editor — Drag-and-drop canvas for designing stream overlays. Widgets (text, image, video, countdown, event feed, progress bar, goal tracker, custom HTML, browser source) can be positioned, resized, and configured visually.
- AI Chat — Frontend for the local Gemma AI assistant. Supports model management, chat history, and context-aware conversations about the user's rules and actions.
- Detachable Widgets — Dashboard components (Live Feed, OSC Monitor, Goals) can be popped out into separate always-on-top windows managed by the WidgetManager.
Preload Bridge
Electron's security model requires that the renderer cannot directly call ipcRenderer.invoke(). Instead, VMSC uses a preload script that creates a typed API object and exposes it via contextBridge.exposeInMainWorld('api', api).
// preload/index.ts — the invoke() wrapper
async function invoke(channel: string, ...args: any[]): Promise<any> {
const result = await ipcRenderer.invoke(channel, ...args)
if (result && typeof result === 'object' && result.__ipcError) {
throw new Error(result.error || 'IPC error')
}
return result
}
// Exposed as window.api in the renderer
const api = {
tiktok: {
connect: (config) => invoke('tiktok:connect', config),
disconnect: () => invoke('tiktok:disconnect'),
getStatus: () => invoke('tiktok:status')
},
// ... all other namespaces
}
contextBridge.exposeInMainWorld('api', api)
The invoke() wrapper automatically unwraps __ipcError sentinel values returned by the main process's safeHandle pattern, converting them back into proper Promise rejections so renderer code can use standard try/catch error handling. See the IPC Channels Reference for the complete channel list.
Overlay Renderer
Overlays run in a completely separate React application from the main UI. They are served by an Express HTTP server (default port 7890) embedded in the main process and rendered in OBS browser sources or standalone browsers.
Communication Flow
- The overlay page loads from
http://localhost:7890/overlay.html?id={overlayId} - It establishes a WebSocket connection back to the main process overlay server
- The main process pushes real-time updates: widget config changes, stream events (for event feeds), timer ticks, goal progress, and variable updates
- Each widget type (text, image, video, countdown, event feed, progress bar, goal tracker, custom HTML, browser source) has a dedicated renderer component optimized for overlay performance
For remote access, VMSC can provision a Cloudflare Tunnel to expose the overlay server over HTTPS, allowing overlays to be loaded from any device without port forwarding.
Config Store
VMSC persists all user configuration using electron-store, which writes an encrypted JSON file to the user's app data directory. The store holds:
- Source configuration (TikTok username, session ID, API keys)
- Output configuration (VRChat OSC host/port, PiShock credentials, webhook URLs)
- Rules and actions (the complete automation setup)
- Profiles (named snapshots of rules + actions + output configs)
- Goals, overlay definitions, plugin state, AI settings
- Connection profiles (Discord, TikTok platform profile cache)
- License/auth tokens (encrypted)
Every write to the config store emits a config-changed event that the renderer listens to, ensuring the UI always reflects the current state without polling.
License System
VMSC uses a two-tier licensing model (Free and Premium) enforced entirely in the main process. The flow works as follows:
- User clicks "Login with Patreon" which opens the Gatekeeper OAuth flow in their browser
- Gatekeeper (a remote FastAPI server) validates the Patreon pledge and issues a signed JWT
- The JWT is exchanged back to the app via a localhost callback and stored in the encrypted config
- LicenseManager decodes the JWT to determine the user's tier and enforces limits on every IPC handler that creates rules, actions, goals, or accesses AI features
- Tokens are refreshed automatically before expiry; if refresh fails, the app gracefully downgrades to Free tier
Tier limits are enforced at the IPC boundary using helper functions (wouldExceedLimit, tierLimitError) so that even a modified renderer cannot bypass restrictions. Premium-only IPC handlers use the premiumHandle wrapper which checks AI access before invoking the handler.