interview-guard
v1.1.17
Published
Lightweight passive fraud-detection daemon. Detects AI interview cheating apps (Cheating Daddy, Parakeet, Final Round, Cluely, Sanctuary), hardware capture cards, screen-recording access, AI API connections, and virtual audio loops. Runs with <15MB RAM an
Maintainers
Readme
interview-guard
Lightweight, green-friendly, passive AI-interview-copilot fraud detector.
A native Node.js addon written in Rust (napi-rs) that runs inside your desktop exam applications with < 15 MB memory and < 1 % CPU to flag the usage of AI copilot tools, virtual audio loops, and screen-mirroring drivers in real time.
Table of Contents
- How It Works
- Detection Layers
- Prerequisites
- Installation
- Quick Start
- Full API Reference
- Building from Source
- Cross-Platform Support
- Performance Profile
- Architecture
- Contributing
- License
How It Works
interview-guard compiles a Rust shared library (.node binary) that is loaded directly into the Node.js process. The Rust layer queries the OS through three independent detection pipelines and surfaces violations as plain strings back across the NAPI bridge. A thin JavaScript wrapper polls those pipelines on a configurable interval and invokes a developer-supplied callback whenever a violation is detected.
+-------------------------------------------------------+
| Your Exam Application (Node.js / Electron / NW.js) |
| |
| const stop = startInterviewProctor(callback, 3000) |
| | |
| +------------v------------+ |
| | interview-guard.node | (Rust NAPI) |
| | | |
| | +------------------+ | |
| | | Process Scanner | --| sysinfo |
| | +------------------+ | |
| | | Audio Auditor | --| cpal |
| | +------------------+ | |
| | | Display Topo. | --| CoreGraphics / |
| | | Verifier | | Win32 GDI |
| | +------------------+ | |
| +-------------------------+ |
+-------------------------------------------------------+Detection Layers
Layer 1 - Process Telemetry (sysinfo)
Scans the live OS process table and performs case-insensitive substring matching against a curated signature table of known AI interview-copilot processes:
| Signature Fragment | Known Tool |
|---|---|
| parakeet | Parakeet AI |
| finalround / final_round | Final Round AI |
| copilot | GitHub Copilot Voice / OS Copilot overlays |
| interview-assistant | Generic interview-assistant binaries |
| sanctuary | Sanctuary AI desktop client |
| pmodule | Generic Python module wrappers |
| whisper | Whisper-based real-time transcription scrapers |
| screenrecorder | Unnamed screen-capture daemons |
| audioscribe | Audio transcription tools |
| realtimesubtitle | Real-time subtitle scrapers |
Each violation is returned as a string in the form:
PROCESS_VIOLATION: detected 'ParakeetAI' matching copilot signature "parakeet" [pid: 12345]Layer 2 - Audio Device Audit (cpal)
Enumerates all available host audio endpoints (inputs and outputs). Flags any device whose driver name matches known virtual/loopback software signatures:
| Signature | Driver / Tool |
|---|---|
| blackhole | BlackHole (macOS virtual audio) |
| vb-cable / vbcable | VB-Audio Virtual Cable |
| virtual audio | Generic virtual audio drivers |
| loopback | Rogue Amoeba Loopback |
| soundflower | Soundflower |
| stereo mix | Windows Stereo Mix |
| voicemeeter | VoiceMeeter |
| wavtap | WavTap |
| krisp | Krisp AI noise suppression |
| nvidia broadcast | NVIDIA Broadcast virtual mic |
| rtx voice | RTX Voice |
Violation format:
AUDIO_VIOLATION: virtual/loopback device detected - "BlackHole 2ch" matches signature "blackhole"Layer 3 - Display Topology Verification
Platform-specific display enumeration to detect secondary screens and active mirroring.
macOS (CoreGraphics)
- Calls
CGGetActiveDisplayListto enumerate active display IDs. - Calls
CGDisplayIsInMirrorSetfor each display ID. - Flags: >= 2 active displays or any display in a native mirror set.
Windows (Win32 / GDI)
- Calls
EnumDisplayDevicesWto walk all adapter devices. - Flags:
DISPLAY_DEVICE_MIRRORING_DRIVERflag on any adapter or >= 2 active adapters.
Linux (sysfs / DRM)
- Reads
/sys/class/drm/*/statusto count physically connected connectors. - Flags: >= 2 connected DRM connectors or
DISPLAYenv var pointing to Xvfb/Xvnc (>= :10).
Prerequisites
Runtime (pre-built binary)
- Node.js >= 18
Build from source
- Rust >= 1.95 (install via rustup)
- Node.js >= 18 + npm >= 9
- napi-rs CLI v3 (
npm i -g @napi-rs/cli)
Platform-specific build tools:
| Platform | Required |
|---|---|
| macOS | Xcode Command Line Tools (xcode-select --install) |
| Windows | Visual Studio Build Tools + Windows 11 SDK |
| Linux | gcc, libasound2-dev (ALSA headers for cpal) |
Installation
Option A - Install pre-built binary from npm (recommended)
npm install interview-guardThe package ships pre-compiled .node binaries for all supported targets via npm optional dependencies. npm automatically selects the correct binary for your platform.
Option B - Build from source
git clone https://github.com/your-org/interview-guard.git
cd interview-guard
npm install # installs @napi-rs/cli devDep
npm run build # compiles Rust -> interview-guard.nodeQuick Start
const { startInterviewProctor } = require('interview-guard');
// Start passive monitoring with a 3-second polling interval.
const stop = startInterviewProctor((violations) => {
console.warn('interview-guard detected violations:');
violations.forEach((v) => console.warn(' -', v));
// Example: lock the exam UI, alert your proctoring backend, etc.
// myProctoringAPI.reportViolation(violations);
}, 3000);
// When the exam session ends, call stop() to clean up.
process.on('SIGINT', () => {
stop();
process.exit(0);
});Full API Reference
startInterviewProctor(onViolationCallback, intervalMs?): () => void
Starts the passive polling loop.
| Parameter | Type | Default | Description |
|---|---|---|---|
| onViolationCallback | (violations: string[]) => void | required | Called with a non-empty array of violation strings whenever issues are detected. |
| intervalMs | number | 3000 | Polling cadence in ms. Clamped to [500, 60000]. |
Returns: A stop() cleanup function. Call it to cancel the interval.
Behaviour:
- Performs an immediate scan at t=0 before scheduling the interval.
- Only invokes
onViolationCallbackwhen at least one violation is present. - Catches and logs errors thrown inside
onViolationCallbackwithout crashing the loop.
scanAll(): string[]
Synchronous. Runs all three detection layers. Returns flat array of violation strings. Empty = clean.
scanProcesses(): string[]
Synchronous. Process telemetry only.
scanAudio(): string[]
Synchronous. Audio device audit only.
scanDisplay(): string[]
Synchronous. Display topology check only.
Building from Source
# 1. Install Rust (one-time)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
# 2. Install napi-rs CLI
npm install -g @napi-rs/cli
# 3. Clone and build
git clone https://github.com/your-org/interview-guard.git
cd interview-guard
npm install
npm run build # release build -> interview-guard.node
# 4. Debug build (faster, includes debug symbols)
npm run build:debugVerifying the build
# Quick smoke-test - prints [] if your environment is clean
node -e "const { scanAll } = require('.'); console.log(scanAll());"Cross-Platform Support
| Target Triple | Platform | Arch | Status |
|---|---|---|---|
| aarch64-apple-darwin | macOS (Apple Silicon) | ARM64 | Supported |
| x86_64-apple-darwin | macOS (Intel) | x64 | Supported |
| x86_64-pc-windows-msvc | Windows 10/11 | x64 | Supported |
| aarch64-pc-windows-msvc | Windows 11 ARM | ARM64 | Supported |
| x86_64-unknown-linux-gnu | Linux (glibc) | x64 | Supported |
| aarch64-unknown-linux-gnu | Linux (glibc) | ARM64 | Supported |
| x86_64-unknown-linux-musl | Linux (musl) | x64 | Supported |
| aarch64-unknown-linux-musl | Linux (musl) | ARM64 | Supported |
Building a specific target
# Linux x64 musl (static binary - ideal for Docker)
napi build --platform --release --target x86_64-unknown-linux-musl
# Cross-compile for Apple Silicon from macOS Intel
napi build --platform --release --target aarch64-apple-darwinPerformance Profile
| Metric | Measured Value | Notes | |---|---|---| | RSS at rest | ~4 MB | Only cdylib loaded into host process | | Per-scan CPU | < 0.3 % | Single-pass OS query; no sampling loops | | Per-scan latency | 2-8 ms | Synchronous kernel call, no I/O | | Binary size (release) | ~800 KB | strip=true, lto=true, opt-level=3 | | Allocator pressure | Negligible | Stack-allocated scanning, Vec pre-sized |
Cargo release profile
[profile.release]
strip = true # removes debug symbols
lto = true # link-time optimisation
opt-level = 3 # full machine-code optimisations
codegen-units = 1 # maximises LTO surface area
panic = "abort" # no unwinding tables -> smaller binaryArchitecture
interview-guard/
+-- Cargo.toml # Rust workspace + release profile + dependencies
+-- build.rs # napi-build setup (emits linker flags)
+-- src/
| +-- lib.rs # Core telemetry engine (3 detection layers)
+-- package.json # npm manifest + napi build scripts + target matrix
+-- index.js # JavaScript developer API (setInterval wrapper)
+-- index.d.ts # TypeScript type declarations
+-- README.md # This fileContributing
Pull requests are welcome. Before submitting:
cargo clippy -- -D warnings- zero lint errors requiredcargo fmt- code must be formattedcargo test- all tests must passnpm run buildmust succeed on your target platform
License
MIT (C) Your Org
