| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
WhatsApp-first verification & safety platform. Users message a WhatsApp number; Vishwas checks what they send — URLs, files (APK/PE/ELF/documents), images, audio, video, government documents — and replies in plain language with a verdict, a confidence band, and practical next steps. Built for constrained CPU-only hardware (designed around an i5-8250U laptop), zero-cloud, stdlib-first, zero-retention by contract.
| You send | It checks | Verdict vocabulary |
|---|---|---|
| A link | normalisation → SSRF guard → VirusTotal reputation → host-string + DOM phishing heuristics | trust / caution / do_not_use / unable_to_verify |
| An executable or archive (APK, PE, ELF/.so, .jar, zip…) | magic-byte validation → file entropy → ClamAV → YARA-x → Quark-Engine → PE static analysis → APK statics (MoBFS-lite) → (gated) dynamic sandbox (Cape/firejail+strace) | same |
| A document (PAN, DL, certificate, PDF/image scan) | extraction/OCR → document-type ID → field-level validation → GPG signature / PAdES verification / QR payload + native check / RAG template cache (retrieval cache, not source of truth) | same |
| Photo/video/audio | ffprobe probe → frame/audio heuristics → (gated) named deepfake detectors: EFFORT (ICML 2025 Oral spatial), aasist3 (Spectra-AASIST3), XLSR-Mamba, NYUAD (image, 2nd signal); cross-modal AV consistency (HAVIC); transform-battery robustness (no raw-score averaging) | same |
Deterministic routing end-to-end: input type → MIME/magic bytes → capability set. No LLM-gated decisions anywhere; the LLM (when provisioned) only narrates over already-computed evidence, behind a prompt-injection guard.
All figures below were re-measured on 2026-10-21 by re-scoring the raw media with the real checkpoints (not by replaying stored feature vectors). Raw records are in docs/research/.
Per-detector accuracy, on the corpora named:
| Channel | Detector | Corpus | n | AUC | EER |
|---|---|---|---|---|---|
| Audio | aasist3 (Spectra-AASIST3) | official ASVspoof2019-LA eval | 300 | 1.0000 | 0.00% |
| Audio | xlsr-mamba | official ASVspoof2019-LA eval | 300 | 1.0000 | 0.00% |
| Image | SPAI (spectral) | raw image corpus (30 AI / 30 real) | 60 | 0.7433 | 26.67% |
| Video | EFFORT (FF++-trained, face crop) | leak-free corpus (30 fake / 30 real) | 60 | 0.7789 | 50.00% |
Posterior medians: aasist3 bonafide 0.0000 / spoof 0.9831 (perfectly separated, 0/150 misclassified at 0.5) · SPAI real 0.0015 / AI 0.6790 · EFFORT real 0.4619 / fake 0.6881.
EFFORT was being fed the wrong pixels. It is a CLIP ViT-L/14, trained on RGB, but the serving path handed it BGR (cv2.imread) with no conversion — and then normalised it with ImageNet statistics instead of CLIP's. Every sibling arch (spai.py, nyuad.py, effnet_image.py) does its own BGR→RGB conversion; EFFORT was the only one relying on the shared preprocess, which is why the omission was invisible. Fixing the input is worth +0.070 AUC (0.6511 → 0.7189) and it also fixed the operator's silent real video, which used to read MEDIUM and now reads LOW.
EFFORT is a face-forensics model and was being fed whole frames. The upstream paper applies it to face regions, and the cross-modal capability already loads a YuNet face detector for the HAVIC first-face crop. Cropping to the detected face (with a whole-frame fallback, because a clip may have no detectable face) is worth a further +0.060 AUC (0.7189 → 0.7789) and lifts the operator's AI videos from 0.32–0.37 to 0.54–0.65 on effort.
The fusion offset was re-derived twice to match (‑0.55 → ‑0.30 → ‑0.40), because the old offset was fitted to the broken score distribution. See docs/research/ and the measurement notes.
The cross-modal gates were originally weighted on the 87-clip calibration corpus, which has no audio stream on 83 of its 87 rows — so AV-sync (weight 3.5) and HAVIC (weight 0.8) were a known_gap on 95% of the set the weights were tuned on, and their weights were never actually tested.
The first cross-modal corpus built to fill that gap (rows_crossmodal_v1) muxed bonafide audio onto real clips and spoof audio onto fake clips — so the audio label was a deterministic function of the video label. That inflated the cross-modal gates by a large margin. A leak-free rebuild (tools/build_crossmodal_corpus_v2.py, which distributes the same audio pool across both classes and asserts the marginals match) measured the truth:
| Signal | leaked corpus AUC | leak-free AUC | verdict |
|---|---|---|---|
| effort (video-only) | 0.6328 | 0.6511 | the only video signal with real separation |
| frameheur (video-only) | 0.5233 | 0.5350 | noise |
| av_risk | 0.6422 | 0.4744 | below chance once the leak is gone |
| havic | 0.9461 | 0.6272 | the 0.9461 was label leakage |
Fixes applied (2026-10-21):
Measured outcome on the leak-free corpus (60 rows, production code paths): 0 genuine clips read DO_NOT_USE (was 2), 0 fakes read TRUST. ⚠️ 2026-10-22: "the operator's 3 AI videos now read do_not_use end-to-end" IS NO LONGER TRUE. It was true, but it was carried by the cross-modal reading av=0.5 — which was the silence artefact described above. With the honest reading the 3 AI videos read MEDIUM/caution, which is the truthful verdict: measured on the operator's real traffic, no rule over the current signals reaches do_not_use for them without also flagging genuine media. The 4 genuine clips still read LOW/trust.
The honest limit: this corpus cannot yield both zero false positives and full detection. real_006 and fake_006 differ by 0.003 in calibrated EFFORT, so no threshold on the video-side signals separates them. The gate therefore keeps 3 of 30 fakes flagged and misses the rest to CAUTION. Treat the video channel as single-signal plus a cross-modal corroborator, not as a detector with demonstrated separation.
Where each detector still falls short — stated because a security tool that only publishes its wins is not telling you the truth:
Test suite: 605 passed / 1 skipped on a developer machine (the service venv: Python 3.11, torch 2.14.1+cu130, all learned detectors installed, and the legacy pylibs tree present). In CI — a clean checkout with only the declared dependencies — the same suite measures 506 passed / 31 skipped (bare, no torch) and 582 passed / 21 skipped (with torch). The bare run skips the six torch-gated architecture modules wholesale, so a bare run is not evidence that the detectors work; CI asserts both counts separately with environment-specific floors (495 bare / 575 full).
Run the suite with scripts/run_tests_split.sh on a memory-constrained host. The full suite in ONE process is OOM-killed by the cgroup on a 25 GB machine (each torch checkpoint costs ~4 GB RSS; the kill appears as exit -9 with an empty pytest output and dmesg reports "Memory cgroup out of memory"). The script splits the run into two processes that between them cover every test file (54 + 7 = 61, no gap) and prints the usual pytest output for each.
Two CI gates run on every push, and a third checks the shipping invariants (the aasist3 helper path exists, shell scripts are LF, the launcher keeps pylibs off the PYTHONPATH, no secret file is tracked). master is branch-protected: a direct push is rejected, and a PR must pass all three checks to merge.
A learned fusion stack trained on out-of-fold predictions scored OOF AUC 0.512 against a 0.502 heuristic baseline, so it is not wired into the serving path. A gradient-boosted alternative was rejected at OOF AUC 0.499 at n=84 — chance.
The audio detector was vendored, loaded cleanly (1022/1022 checkpoint keys) and rejected on a 240-clip validation slice at AUC 0.54 against a bar of AUC ≥ 0.85 fixed before the run. That result was later superseded: the same checkpoint was re-measured on the official ASVspoof2019-LA evaluation set at AUC 0.9967 / EER 0.70% (vendor) and re-measured here at AUC 1.0000 / EER 0.00% on 300 official-eval samples. The detector is now wired in as the audio channel, with clean audio reading LOW and a spoof reading HIGH. The 0.54 figure was a bad validation slice, not a bad model — but it was a real measurement at the time, and the rejection was honest given the evidence available then.
Image verdicts deliberately cap at CAUTION: a single learned-model read never escalates a photo to DO_NOT_USE, because that detector false-highs 6/30 real images on our own check set.
cd /home/hermes/vishwas
export PYTHONPATH=$PWD/src
# Use the launcher, NOT a bare `python3` — it selects the interpreter that
# can actually load the learned detectors (see "Runtime" below). Calling
# `python3 -m vishwas.app` directly works for the cheap tiers, but the
# torch-gated detectors silently degrade to "unavailable" and the audio and
# image channels fail OPEN.
#
# CLI — simulate a WhatsApp user locally
scripts/run_vishwas.sh cli --text "https://bank-secure-login.example.net"
scripts/run_vishwas.sh cli --file ./path/to/file.apk
# Webhook server (pair with OpenWA below)
OPENWA_BASE_URL=http://localhost:2785 OPENWA_SESSION_ID=main \
OPENWA_API_KEY=<key> OPENWA_WEBHOOK_SECRET=<secret> \
scripts/run_vishwas.sh webhook --port 2790
# Full test suite (offline, no network needed): 471 tests, hermetic
python3 -m pytest tests/ -qThe learned detectors need a specific interpreter, and using the wrong one degrades the platform silently:
scripts/run_vishwas.sh handles both: it execs the dedicated venv (/home/hermes/.venv-ambient) and keeps PYTHONPATH to the repo's own src/. If the venv is missing the launcher refuses to start, printing the rebuild command, rather than starting a verifier whose detectors are all dead.
Rebuild it with:
uv venv --python 3.11 /home/hermes/.venv-ambient
VIRTUAL_ENV=/home/hermes/.venv-ambient uv pip install --python $VIRTUAL_ENV/bin/python \
-e ".[learned,binary]"Runtime dependencies are pinned in pyproject.toml.
See docs/ARCHITECTURE.md for the full diagram.
| Env var | Default | Purpose |
|---|---|---|
| OPENWA_BASE_URL | http://localhost:2785 | OpenWA REST base |
| OPENWA_SESSION_ID | main | Which session handles traffic |
| OPENWA_API_KEY | — | Bearer auth for OpenWA REST calls |
| OPENWA_WEBHOOK_SECRET | — | Expected HMAC for inbound webhooks; empty = unsigned accepted (documented operator choice) |
| VISHWAS_WEBHOOK_HOST/PORT | 127.0.0.1 / 8899 | Inbound listener bind (the systemd unit uses 2790) |
| VISHWAS_VT_API_KEY | — | Enables VirusTotal (files + URLs) |
| VISHWAS_LLM_BASE_URL / _API_KEY / _MODEL (or OPENAI_API_KEY) | — | Optional LLM narration layer |
| VISHWAS_FFMPEG_BIN/FFPROBE_BIN/THREADS | ffmpeg / ffprobe / 2 | Media processing; thread cap is thermal-safe (see docs/PERFORMANCE.md) |
| VISHWAS_CLAMSCAN_BIN / CLAMD_DB | clamscan / /var/lib/clamav | Local AV |
| VISHWAS_YARA_RULES | — | YARA rule pack path |
| VISHWAS_TESSERACT_BIN / DOCLING / GPG_BIN | tesseract / docling / gpg | OCR / document parsing / signature verification |
| VISHWAS_QUARK_PY / MOBSF_CLI / CAPE_CMD | — | Quark-Engine wrapper, MoBFS CLI, dynamic sandbox command |
| VISHWAS_<MODEL>_WEIGHTS (EFFORT, AASIST, XLSRMAMBA, SSL_AUDIO, HAVIC, IMAGE_FACE, NYUAD) | — | Path to a downloaded model weight set; enables that detector (canonical sources: docs/research/MODEL_WEIGHTS_MANIFEST.json) |
| VISHWAS_FUSION_DIR | — | Trained LR-stacking checkpoints (from fusion_train.py) |
| VISHWAS_RAG_CACHE / _VERSION | — | Document-template retrieval cache (NOT a source of truth) |
| VISHWAS_QUARANTINE / STALE_TTL_S / WORKDIR / AUDIT_LOG / LOG_LEVEL / BUDGET_S | see app.py | storage paths, sweep TTL, audit trail, hard time budget |
Real-world wiring with OpenWA (docker compose, webhook registration, secret management, health checks) is in docs/DEPLOYMENT.md. Non-technical behaviour guide for users and operators is in docs/USER_GUIDE.md; adversary-facing design rationale is in docs/THREAT_MODEL.md; the honest capability inventory and how to enable every gated feature is in docs/GAPS_AND_ENABLEMENT.md.
More writing on this stack: https://dev.to/dawnofgenx
| Back | FazBrowse Home | New Git URL |