Architecture

How a request flows through the platform, and where each module lives. Written for the maintainer changing one feature or one step of the flow — file names are the map.

The request lifecycle (reverse proxy, :443)

victim request (SNI = <phish_sub>.<base>)
  │
  ├─ botguard.go        JA4/UA check → non-browser gets the 141B decoy "It works!"
  │                     (trusted CIDRs skip scoring; telemetry endpoint /t/<token>)
  ├─ relay.go           path starts /__relay/*  → reverse-proxy to bgrelay :9445
  ├─ blacklist.go       IP blacklist
  ├─ http_proxy.go      host → phishlet resolution, session cookie → Session
  │   ├─ terminal_lures/
  │   │  pause flag     paused lure → block.go (benign redirect)
  │   ├─ UA filter      lure's ua_filter regex
  │   ├─ token-gate     missing/wrong ?t= → blockRedirect to redirect_url
  │   ├─ relay lure     Lure.Relay → relay.go relayPage (victim page at lure path)
  │   └─ params.go      extractParams (AES-GCM lurecrypto.go + legacy RC4)
  ├─ bodytools.go       js_inject, lure-param/URL patching (jsobf.go variants)
  ├─ hosts.go           phished↔original host mapping, session hosts
  └─ transport.go       upstream fetch: proxy routes (setProxy/applyTransport,
                        uTLS Chrome fingerprint), httpsWorker per-host certs
        │
        ▼ response
  ├─ http_proxy.go      cookie rewrite + capture → database (SQLite)
  │                     all-tokens complete → Finish + webhook.go + gophish.go
  └─ block.go           tracker image / intercepts / redirects

Module map (src/)

File Role Origin
main.go flags + startup order (config → db → phishlets → proxy → terminal → API) fork flags
core/http_proxy.go HttpProxy struct + the two goproxy closures (request/response) upstream + fork
core/{relay,block,params,bodytools,hosts,transport}.go topical extractions of http_proxy.go (behavior-neutral split) fork split
core/terminal.go + terminal_lures.go operator REPL (config/phishlets/sessions/lures) upstream + fork
core/botguard.go, ja4.go anti-bot: JA4 allowlist, UA/GREASE heuristics, decoy fork
core/lurecrypto.go AES-256-GCM lure params (server-side key) fork
core/apid.go hidden mTLS REST API (stealth base path, client certs) fork
core/hotreload.go phishlet hot-reload + cert refresh gating fork
core/config.go config + lure CRUD + proxy routes + sub-phishlet registry upstream + fork
core/phishlet.go, rewrite.go phishlet YAML loading + rewrite_urls upstream + fork
core/jsobf.go, webhook.go, gophish.go obfuscation / credential webhook fork/jsobf
core/certdb.go certmagic wrapper, crt/sites/* unmanaged certs upstream + fork
database/database.go SQLite storage (pure-Go) fork
puppet/main.go evilpuppet sidecar (chromedp telemetry) fork

The Google relay sidecar (tools/relay/)

victim page (relay.go serves it AT the lure path)
  └─ XHR /__relay/api/* ──► bgrelay.py :9445 (loopback)
        ├─ patchright Chromium, headful Xvfb, per-session
        ├─ HTTP→SOCKS bridge :8119 → residential exit (RELAY_SOCKS env, secret)
        ├─ mirror stream: 2x screenshots + DOM geometry (input/button/label)
        └─ capture on done → ~/bgrelay-store/<sid>.json
             operator: egconsole `open` / MCP open_session / relay_open_session

Operator tooling (tools/)

Path Role
lib/egapi.py the ONE mTLS API client + node list (my-servers.json)
lib/cookies.py cookie extraction / playwright conversion / Cookie-Editor export
lib/session_launcher.py open a real browser signed-in with a captured session
egconsole.py operator REPL (uses lib/)
egctl.py one-shot fleet CLI (uses lib/)
mcp/egmcp.py MCP server for AI agents (uses lib/)
relay/ bgrelay sidecar + victim page
patches/ HISTORICAL fork-derivation patches — frozen, src/ is the truth
lab/, make_phishlet.py, make_dns_zone.py, mint_internal_cert.sh authoring/lab kit

Deploy topology