PinkGlasses — external attack surface, continuously watched.

A self-hosted external attack-surface scanner: a Go control plane, a React SPA, and a fleet of workers you own — including VPS boxes you enrol from the UI.

This wiki covers how the scanner behaves in practice. For getting it running, see the README; for the design rationale, Architecture.

Pages

  • Accounts and access — the three roles and where the boundaries fall, why sessions are server-side, how API tokens are scoped, and how to put an identity provider in front.
  • Port scanning — which scanner runs when, the exact nmap and naabu command lines, every setting you can change, and what the defaults cost you in noise and accuracy.
  • Alerts — per-company Slack or JSON webhooks, fed one digest per scan with the changes each channel asked for; every delivery attempt is recorded.
  • Workers and containers — what a worker is as opposed to a Docker container, and how an active scan travels through the VPN gateway, step by step, with pictures.
  • MCP server — let an AI client do what the web UI does, through the API and under an API token: which tools exist, what is deliberately not exposed, and how to run it over stdio or HTTP.
  • Passive discovery sources — every subfinder source that takes an API key, in the order of .env.example, with the link to get each key.
  • Where scans run from — passive stages on the standing workers, active stages from a chosen exit: an ephemeral fleet behind a VPN gateway, or a pool of remote workers. Why the privilege is not in the worker, why routing is per task, and what happens when the tunnel drops.
  • API — every route the server serves, grouped by the role it needs, with request and response shapes; kept in step with the code by a test.
  • Wordlists — the three kinds of list, how a run picks them, how they reach a worker, and which one decides how loud a scan is.

The pipeline in one picture

subdomains ─┬─────────────────┐
            └─ dns bruteforce ┴→ dns resolve ─→ [new addresses, deduped]
                                                  → port scan (batched, 64/task)
                                                         → service probe
                                                              ├─ tech detect
                                                              ├─ screenshot
                                                              ├─ directory brute
                                                              └─ vulnerability check
Stage Primary tool Fallback
Subdomains subfinder stdlib resolver
DNS bruteforce shuffledns + massdns skipped
Resolution & enrichment dnsx, Team Cymru stdlib resolver
Ports & services nmap -sV alone at top-100; naabu → nmap when wider Go connect scan
Tech & versions httpx -tech-detect, cookie names header/body fingerprint
Screenshots httpx -screenshot needs the browser capability
Directory brute katana, urlfinder → gobuster/ffuf built-in common-path probe
Vulnerabilities nuclei, default templates, severity low and up; findings nuclei:<template-id> skipped

Every tool invocation is logged with its result count and duration; a tool that exits cleanly while producing nothing and complaining on stderr is reported as a failure; and a batch of results the gateway refuses is logged by both ends with the reason. That class of silent breakage — the stage runs, the results evaporate — has cost real debugging time here, and each of those three checks was added after it had already happened.

Scheduled scans

The Start-a-scan dialog runs a scan now, once at a chosen time, or on a repeat from hourly to yearly, with the same profile, exit and settings either way. A schedule starts its runs through the same code the button uses — so it is refused for the same reasons, and the refusal is shown on the schedule rather than lost in a log. A run still going when the next is due is skipped, never stacked; a one-off disables itself once it has started. Runs can be paused (nothing more is leased, in-flight tasks finish, the fleet stays up), resumed, stopped and rerun with the same choices; a finished run can be deleted with everything it recorded, its screenshots included. See the README’s Scheduled scans and Watching a scan, and Architecture §3.3.

Who can do what

Every endpoint needs a signed-in session or an API token; three ordered roles decide the rest. Starting a scan needs operator, because it sends packets at somebody else’s infrastructure; managing accounts, workers and VPN configurations needs admin, because each hands out a credential, and so does deleting a company, because that takes its whole inventory and history with it. See Accounts and access.

Target authorization

Targets come in groups: what one Add targets on the Dashboard produced, a name and its list of domains, IPs and CIDRs. A scan picks groups; a schedule remembers groups and expands them when each run starts, so editing a group later changes what its scheduled runs cover. The same entry may sit in several groups and is scanned once.

A target is passive-only unless it carries an explicit active authorization, recorded with who gave it and when — the tick on the group’s form applies it to every entry. Only addresses belonging to an authorized target are port scanned; everything else is enumerated and resolved but never probed. An entry in several groups counts as authorized if any of them authorizes it, and as excluded if any excludes it. RFC1918 and loopback targets are rejected outright.