
Locally-hosted, air-gapped VAPT platform that runs 8 parallel scanning modules, deterministically scores findings with CVSS v3.1, and generates PDF reports with optional AI-generated remediation via local LLM.
Built by maverickaayush.
A locally-hosted, air-gapped VAPT tool: point it at an authorized target domain and it runs 8 scanning modules in parallel, deterministically scores every finding (CVSS v3.1), optionally adds AI-generated plain-English descriptions via a local LLM, and produces a PDF report plus a live web dashboard. No external API calls - everything runs on your own network.
Two ways to run ONUS:
- Self-hosted (this repo):
docker compose up→ enter a domain → tick the authorization box → scan. No account, no sign-in, no email verification - it's single-operator by design, and everything below covers this path.- Hosted: a managed instance is live at tryonus.tech. It layers on production-only features (accounts / OAuth sign-in, a scan queue, per-user history) that default off in this repo, so self-hosting stays simple. See Self-hosted vs hosted.
| Self-hosted (this repo) | Hosted (tryonus.tech) | |
|---|---|---|
| Setup | docker compose up | none - just open the site |
| Sign-in | none - single operator | account / Google / GitHub OAuth |
| Scan flow | domain → authorization box → scan | same, after signing in |
| Extras | - | scan queue, per-user history, hosted email |
| Runs on | your own network, air-gapped | managed cloud |
The self-hosted default path is intentionally the simple one: no sign-up, no
email verification, no OAuth. The hosted-only features (authentication, scan
queue) live behind config flags that default off (REQUIRE_AUTH=false,
HOSTED_QUEUE_ENABLED=false; see Configuration) and are not
set in docker-compose.yml - so running ONUS locally never puts you behind a
login. Prefer zero setup? Use the hosted site at
tryonus.tech.
| New Scan | Live Scan Status |
|---|---|
![]() | ![]() |
| Report Dashboard | Scans Discovery |
|---|---|
![]() | ![]() |
domain → [recon | webscan | ssl_tls | headers | owasp | tech_fingerprint | nuclei | enumeration] (parallel, Celery)
→ any module failed/timed out? → pause for operator retry/continue/cancel
→ aggregator (dedup + OWASP-map + sort)
→ confidence verification (passive re-observation)
→ deterministic CVSS scoring
→ Ollama (Qwen 2.5 7B) AI analysis
→ WeasyPrint PDF + PostgreSQL
→ dashboard / PDF download
Six layers: Next.js frontend → FastAPI → Celery/Redis → 8 parallel scanning
modules → Ollama (Qwen 2.5 7B) → WeasyPrint PDF + dashboard. Full details in
ARCHITECTURE.md (schemas, contracts, guardrails) and
docs/QUICK_REF.md (quick lookup for common changes).
mem_limit: 4g),
and Postgres, Redis, the API, the worker and the frontend run alongside it.See Prerequisites above: ~8GB disk and ~6GB RAM, and expect a 10-15 minute first build.
cp .env.example .env
cp backend/subfinder-config/provider-config.yaml.example backend/subfinder-config/provider-config.yaml
docker compose up -d
docker compose ps # wait for zap to report healthy (~2 min)
Open http://localhost:3000, enter a domain, tick the authorization box, and start the scan.
No sign-in, sign-up, or email verification. Self-hosted ONUS is single-operator by default - there's no account step between opening the dashboard and scanning.
This works with no Ollama install. CVSS/severity/priority scoring is
always deterministic (analysis/cvss_scorer.py) - without Ollama running,
findings just get a rule-based description template instead of AI-generated
prose. See "Optional: enable AI-generated descriptions" below to turn that on.
Zero API keys are required to run this tool. Every scanning tool it
wraps (nmap, ZAP, Nikto, testssl.sh, Nuclei, Amass, Naabu, httpx, WhatWeb,
WAFW00F, FFUF) and Ollama itself work with no key at all. The subfinder
config copy step above is the one optional exception - leaving it as the
empty template is fine, subfinder just runs with free/public sources only.
To deepen subdomain enumeration, you can add up to two free-tier keys to
that file before starting: a GitHub personal access token and a
ProjectDiscovery Chaos API key - see
the comments inside provider-config.yaml.example.
Ollama runs natively on the host (not in Docker) so it can use the
host's GPU directly; containers reach it via host.docker.internal.