A scan canary for your servers. The app can't open listening ports — so flip it around: your box calls home on a timer. If the heartbeat stops for 3+ intervals, the beacon flips SILENT and your inbox lights up. And because every beacon has a unique probe URL, plant it as a canary link: if your box gets scanned or someone touches the link, that fires too. Know the second your box goes dark or gets touched.
""" + newcard + """
+
New beacon
+
""" + _beacon_list_html(uid) + flow("know the second your box goes dark or gets touched", [
+"you create a beacon labeled edge-vpn-fra, disguise port 22, interval 5 min.",
+"you drop the one-liner (or the systemd timer) on the server — it curls the probe URL every 5 minutes.",
+"the beacon row shows LIVE with a fresh last-seen, every heartbeat logged.",
+"them a scanner knocks on port 22 — you've planted the same /b/ URL as a canary link in the port's banner or a decoy file; the moment it's fetched by anything that isn't your heartbeat, the tripwire fires.",
+"you your INBOX lights up: beacon edge-vpn-fra touched from an unknown IP — with geo and device.",
+"one night the box dies or loses network — 15 minutes of silence (3 intervals) later the beacon flips SILENT and pings your inbox again."]) + how([
+"A beacon is a unique probe URL — one per server — plus a heartbeat interval you choose.",
+"Your server curls the probe URL every interval (curl one-liner or systemd timer; we generate both for you).",
+"Heartbeats are detected by the User-Agent (curl/wget/drb-beacon) or the ?hb=1 flag — anything else fetching the URL counts as a CANARY HIT and alerts instantly.",
+"Any fetch that is not a heartbeat logs IP, geo, device — same intel as the canary traps, kept in its own beacon_hits table.",
+"Miss 3+ intervals of heartbeat and the beacon flips SILENT: one inbox alert per outage (no duplicates), auto-cleared when the heartbeat returns.",
+"The disguise port is informational — a badge reminding you which port the canary link guards; the detection is the URL itself.",
+"Pair it with CANARY TRAPS: plant a /c/ trap inside the box's files, and the /b/ URL in its network decoys — layered coverage."]) + gloss([
+("heartbeat","a tiny scheduled HTTP GET from your server proving it's alive"),
+("SILENT","no heartbeat for 3+ intervals — box down, offline, or egress blocked"),
+("LATE","heartbeat overdue by 1+ interval but under the SILENT threshold"),
+("disguise port","the port you're watching — shown on the badge, pairs the beacon to its service"),
+("canary hit","any fetch of the probe URL that isn't your heartbeat — someone touched it")]) + agent_card("POST /api/beacon/create · GET /api/beacon/list · GET /api/beacon/status?token=",
+'curl -X POST "' + SITE + '/api/beacon/create" -H "Cookie: dark0rbits_tok=..." -d "label=edge-vpn&port=22&interval_min=5"',
+'Create returns token + probe_url + install snippet. status returns LIVE/LATE/SILENT/WAITING, last_seen, hits. Auth: session cookie or Bearer dk_ key.')
+ return page("beacon", body)
+
+def _beacon_list_html(uid):
+ if not uid:
+ return '
Log in (no KYC) to create beacons and see their status.
'
+ con = _beacon_db()
+ rows = con.execute("SELECT * FROM beacons WHERE user_id=? ORDER BY id DESC LIMIT 50", (uid,)).fetchall()
+ trs = ""
+ for b in rows:
+ st, cls = _beacon_status(b)
+ hits = con.execute("SELECT COUNT(*) c FROM beacon_hits WHERE beacon_id=? AND kind='hit'", (b["id"],)).fetchone()["c"]
+ probes = con.execute("SELECT COUNT(*) c FROM beacon_hits WHERE beacon_id=? AND kind='probe'", (b["id"],)).fetchone()["c"]
+ last = time.strftime("%b %d %H:%M", time.localtime(b["last_seen"])) if b["last_seen"] else "never"
+ probe, curl, _s, _t = _beacon_snippet(b["token"], b["interval_min"])
+ port = (":" + str(b["port"])) if b["port"] else ""
+ trs += ("
')
+ r = rate_limit("beacon", 20, 60)
+ if r: return r
+ b = _beacon_create_core(uid)
+ resp = Response(status=302)
+ resp.headers["Location"] = "/beacon?new=" + b["token"]
+ return resp
+
+@app.route("/b/")
+def beacon_probe(token):
+ con = _beacon_db()
+ b = con.execute("SELECT * FROM beacons WHERE token=?", (token,)).fetchone()
+ if not b:
+ return "Not Found", 404
+ ip = request.headers.get("X-Real-IP") or request.remote_addr or "?"
+ ua = request.headers.get("User-Agent", "")
+ heartbeat = param("hb") == "1" or bool(_HEARTBEAT_UA.search(ua or ""))
+ kind = "probe" if heartbeat else "hit"
+ con.execute("INSERT INTO beacon_hits(beacon_id,ts,ip,ua,kind) VALUES(?,?,?,?,?)",
+ (b["id"], int(time.time()), ip, ua[:200], kind))
+ con.execute("UPDATE beacons SET last_seen=? WHERE id=?", (int(time.time()), b["id"]))
+ was_silent = b["silent_notified"]
+ if was_silent:
+ con.execute("UPDATE beacons SET silent_notified=0 WHERE id=?", (b["id"],))
+ con.commit() # commit BEFORE inbox writes (db-locked race)
+ if not heartbeat and b["user_id"]:
+ geo = enrich_ip(ip) or {}
+ where = ""
+ if geo:
+ where = " — %s, %s %s · %s" % (geo.get("city", ""), geo.get("regionName", ""), geo.get("countryCode", ""), geo.get("isp", ""))
+ m = db()
+ m.execute("INSERT INTO messages(user_id,sender,body,created) VALUES(?,?,?,?)",
+ (b["user_id"], "operator-bot",
+ "◆ BEACON TOUCHED: '%s%s' probe URL fetched by a non-heartbeat client — IP %s%s device: %s" %
+ (esc(b["label"]), ((":%d" % b["port"]) if b["port"] else ""), esc(ip), esc(where), esc(ua[:100])),
+ int(time.time())))
+ m.commit()
+ elif was_silent and b["user_id"]:
+ m = db()
+ m.execute("INSERT INTO messages(user_id,sender,body,created) VALUES(?,?,?,?)",
+ (b["user_id"], "operator-bot",
+ "◆ BEACON RECOVERED: '%s' heartbeat is back after going SILENT." % esc(b["label"]),
+ int(time.time())))
+ m.commit()
+ if heartbeat:
+ return "ok", 200, {"Content-Type": "text/plain"}
+ # canary-style: look like nothing
+ return "Not Found", 404
+
+@app.route("/beacon/events")
+def beacon_events():
+ uid = current_user_id()
+ token = param("token") or ""
+ if not uid:
+ return page("beacon", '
login required
')
+ con = _beacon_db()
+ b = con.execute("SELECT * FROM beacons WHERE token=? AND user_id=?", (token, uid)).fetchone()
+ if not b:
+ return page("beacon", '
unknown beacon
')
+ hits = con.execute("SELECT * FROM beacon_hits WHERE beacon_id=? ORDER BY id DESC LIMIT 100", (b["id"],)).fetchall()
+ trs = ""
+ for h in hits:
+ geo = {}
+ ip = (h["ip"] or "").strip()
+ if ip and not ip.startswith(("10.", "127.", "172.")):
+ geo = enrich_ip(ip) or {}
+ where = ("%s, %s" % (geo.get("city", "—"), geo.get("countryCode", ""))) if geo else "—"
+ ktag = 'beat' if h["kind"] == "probe" else 'HIT'
+ trs += ("
""")
+
+@app.route("/api/beacon/create", methods=["POST"])
+def api_beacon_create():
+ r = rate_limit("beacon", 20, 60)
+ if r: return r
+ uid = key_user() or current_user_id()
+ if not uid:
+ return jsonify({"ok": False, "error": "auth required: account session or Authorization: Bearer key"}), 401
+ b = _beacon_create_core(uid)
+ probe, curl, svc, tim = _beacon_snippet(b["token"], b["interval_min"])
+ return jsonify({"ok": True, "token": b["token"], "label": b["label"], "port": b["port"],
+ "interval_min": b["interval_min"], "probe_url": probe,
+ "install": {"curl": curl, "systemd_service": svc, "systemd_timer": tim}})
+
+@app.route("/api/beacon/list")
+def api_beacon_list():
+ uid = key_user() or current_user_id()
+ if not uid:
+ return jsonify({"ok": False, "error": "auth required"}), 401
+ con = _beacon_db()
+ _beacon_check_silent(con, uid)
+ out = []
+ for b in con.execute("SELECT * FROM beacons WHERE user_id=? ORDER BY id DESC LIMIT 100", (uid,)).fetchall():
+ st, _cls = _beacon_status(b)
+ hits = con.execute("SELECT COUNT(*) c FROM beacon_hits WHERE beacon_id=? AND kind='hit'", (b["id"],)).fetchone()["c"]
+ out.append({"token": b["token"], "label": b["label"], "port": b["port"], "interval_min": b["interval_min"],
+ "status": st, "last_seen": b["last_seen"], "canary_hits": hits,
+ "probe_url": SITE + "/b/" + b["token"]})
+ return jsonify({"ok": True, "beacons": out})
+
+@app.route("/api/beacon/status")
+def api_beacon_status():
+ uid = key_user() or current_user_id()
+ token = param("token") or ""
+ if not uid:
+ return jsonify({"ok": False, "error": "auth required"}), 401
+ con = _beacon_db()
+ _beacon_check_silent(con, uid)
+ b = con.execute("SELECT * FROM beacons WHERE token=? AND user_id=?", (token, uid)).fetchone()
+ if not b:
+ return jsonify({"ok": False, "error": "unknown beacon"}), 404
+ st, _cls = _beacon_status(b)
+ hits = con.execute("SELECT COUNT(*) c FROM beacon_hits WHERE beacon_id=? AND kind='hit'", (b["id"],)).fetchone()["c"]
+ return jsonify({"ok": True, "token": b["token"], "label": b["label"], "port": b["port"],
+ "interval_min": b["interval_min"], "status": st, "last_seen": b["last_seen"],
+ "silent_notified": b["silent_notified"], "canary_hits": hits})
+# ---------- END TOOL: PORT BEACON ----------
+
+
+# ---------- TOOL: BSSID RADAR ----------
+
+BSSID_OSINT_BASE = "http://10.30.20.174:8080"
+BSSID_OSINT_URL = BSSID_OSINT_BASE + "/api/run/bssid_geo?q="
+
+def _norm_mac(raw):
+ """Normalize any MAC spelling to AA:BB:CC:DD:EE:FF (or None if invalid).
+ Accepts aa-bb-cc-dd-ee-ff, aabbccddeeff, AA.BB.CC..., 'aa bb cc', leading 0x."""
+ if not raw:
+ return None
+ s = str(raw).strip().lower()
+ if s.startswith("0x"):
+ s = s[2:]
+ for ch in ":-. _/":
+ s = s.replace(ch, "")
+ if len(s) == 12 and all(c in "0123456789abcdef" for c in s):
+ return ":".join(s[i:i+2] for i in range(0, 12, 2)).upper()
+ return None
+
+def _oui(mac):
+ """Vendor OUI hint from the first 3 octets (registry-free, informational only)."""
+ return mac[:8] if mac else ""
+
+def _osint_bssid_lookup(mac):
+ """Call the LAN OSINT terminal's bssid_geo tool. Returns dict; never raises."""
+ try:
+ url = BSSID_OSINT_URL + urllib.parse.quote(mac)
+ status, body = http(url, timeout=15)
+ if status == 0:
+ return {"ok": False, "error": "OSINT backend unreachable (" + body[:120] + ")"}
+ if status != 200:
+ return {"ok": False, "error": "OSINT backend returned HTTP " + str(status)}
+ data = jf(body)
+ if not isinstance(data, dict):
+ return {"ok": False, "error": "OSINT backend returned non-JSON response"}
+ out = {"ok": True, "mac": mac, "raw": data, "source": data.get("source", "osint-terminal bssid_geo")}
+ # location fields (mylnikov style): lat/lon or latitude/longitude, accuracy/range in meters
+ lat = data.get("lat", data.get("latitude"))
+ lon = data.get("lon", data.get("lon", data.get("lng", data.get("longitude"))))
+ acc = data.get("accuracy", data.get("range", data.get("radius")))
+ if lat is not None and lon is not None:
+ try:
+ out["lat"] = round(float(lat), 6)
+ out["lon"] = round(float(lon), 6)
+ out["accuracy_m"] = float(acc) if acc is not None else None
+ out["found"] = True
+ except (TypeError, ValueError):
+ out["found"] = False
+ else:
+ out["found"] = False
+ out["wigle"] = data.get("wigle")
+ out["geo_db"] = data.get("geo_db")
+ out["ms"] = data.get("__ms")
+ return out
+ except Exception as e:
+ return {"ok": False, "error": "OSINT backend unreachable (" + esc(str(e)) + ")"}
+
+def _bssid_result_row(r):
+ """One result dict -> HTML table row (safe strings)."""
+ if not r.get("ok"):
+ return ("
" + esc(r.get("mac", "?")) + "
"
+ + esc(r.get("error", "lookup failed")) + "
")
+ mac = esc(r["mac"])
+ if r.get("found"):
+ lat, lon = r["lat"], r["lon"]
+ glink = "https://www.google.com/maps?q=" + str(lat) + "," + str(lon)
+ olink = "https://www.openstreetmap.org/?mlat=" + str(lat) + "&mlon=" + str(lon) + "#map=16/" + str(lat) + "/" + str(lon)
+ acc = ("~" + str(int(r["accuracy_m"])) + " m") if r.get("accuracy_m") is not None else "unknown"
+ loc = ('' + str(lat) + ", " + str(lon) + " "
+ + 'Google Maps · '
+ + 'OSM')
+ extra = esc(str(r.get("geo_db") or ""))
+ return ("
" + mac + "
" + _oui(r["mac"]) + "
" + loc + "
" + acc + "
" + extra + "
")
+ note = esc(str(r.get("geo_db") or "no location in free DB"))
+ wl = r.get("wigle")
+ if wl:
+ note += ' try WiGLE'
+ return ("
" + mac + "
" + _oui(r["mac"]) + "
not located
—
" + note + "
")
+
+@app.route("/bssid", methods=["GET", "POST"])
+def bssid_page():
+ uid = current_user_id()
+ results = None
+ q_raw = param("mac") or param("macs") or ""
+ rows_raw = [x for x in str(q_raw).replace(";", "\n").replace(",", "\n").splitlines() if x.strip()]
+ if rows_raw:
+ r = rate_limit("bssid", 20, 60)
+ if r:
+ return r
+ results = []
+ for line in rows_raw[:25]:
+ mac = _norm_mac(line)
+ if not mac:
+ results.append({"ok": False, "mac": str(line).strip()[:40], "error": "not a valid MAC (want aa:bb:cc:dd:ee:ff)"})
+ else:
+ results.append(_osint_bssid_lookup(mac))
+ table = ""
+ if results is not None:
+ table = ('
BSSID
OUI
Location
Accuracy
DB / nearby hints
'
+ + "".join(_bssid_result_row(x) for x in results) + "
")
+ last = esc(q_raw) if q_raw else ""
+ body = f'''
+
+
BSSID RADAR
+
Turn a WiFi router's MAC address into an approximate place on Earth. Photo EXIF or a wifi scan gives you BSSIDs — this tool asks the OSINT terminal where those routers physically sit.
+
+
+RUN A LOOKUP
+
+
+
+WHAT YOU GET BACK
+
+
lat/lon + accuracy radius in meters (when the free DB knows the router)
+
one-click Google Maps and OpenStreetMap links
+
WiGLE fallback link + nearby-network hints when the DB comes up empty
+
vendor OUI (first 3 octets) per MAC for quick triage
+
+
+
+{table}
+''' + how([
+ "You feed in a BSSID — the hardware MAC of a WiFi access point, grabbed from a photo's EXIF wifi scan, aircrack output, or a phone's wifi list.",
+ "Your input is normalized: dashes, dots, spaces or no separator at all become AA:BB:CC:DD:EE:FF. Six hex pairs or it's rejected.",
+ "The server calls the LAN OSINT terminal's bssid_geo tool, which queries the Mylnikov free WiFi-geolocation database (crowdsourced router positions).",
+ "A hit returns latitude, longitude and an accuracy radius in meters — how tight the crowd-sourced fix is. Rural routers can be kilometers off; dense city fixes are often within 100 m.",
+ "A miss is not a dead end: you get a ready-made WiGLE search link (free account needed) plus the DB's message, and nearby-network hints when present.",
+ "Batch mode loops the same pipeline over up to 25 MACs and renders one comparison table.",
+ "Accuracy is approximate by nature — treat results as a hint to corroborate, never as evidence. Router MACs move when people reinstall hardware.",
+]) + flow("place a router from a photo's WiFi scan", [
+ 'you receive a photo whose EXIF wifi-scan block lists BSSID dc:39:6f:20:1d:70.',
+ 'you paste it into BSSID RADAR — typed as dc-39-6f-20-1d-70, the live preview confirms the normalized form.',
+ 'the tool queries the OSINT terminal; the Mylnikov DB has seen that router before and returns lat/lon with a ~130 m radius.',
+ 'the tool draws Google Maps / OSM links at that pin — you now know roughly where the photo was taken, no GPS tag required.',
+ 'you drop 5 more BSSIDs from the same scan into batch mode and cross-check the pins cluster in one neighborhood.',
+]) + gloss([
+ ("BSSID", "Basic Service Set Identifier — the MAC address of a WiFi access point's radio. Unique per router, visible in every wifi scan."),
+ ("MAC", "Media Access Control address — 6 hex pairs identifying network hardware. First 3 pairs (the OUI) identify the vendor."),
+ ("OUI", "Organizationally Unique Identifier — first 3 octets of a MAC, registered to the manufacturer (e.g. 00:25:9C ≈ a radio vendor)."),
+ ("Mylnikov DB", "Free crowdsourced database mapping WiFi BSSIDs to GPS coordinates, built from user-submitted wardrive logs."),
+ ("WiGLE", "Wireless Geographic Logging Engine — the largest public wardriving DB. Needs a free account to query."),
+ ("Accuracy radius", "Meters around the returned lat/lon where the router probably is. Crowdsourced fixes vary; small radius = many sightings."),
+ ("Wardriving", "Mapping WiFi networks while moving around, logging BSSID + GPS. Feeds the geolocation databases this tool reads."),
+]) + agent_card("GET /api/bssid?mac=AA:BB:CC:DD:EE:FF · GET /api/bssid?macs=a;b;c",
+ "curl -s https://dark0rbits.thetempleofdoom.com/api/bssid?macs=AA:BB:CC:DD:EE:FF;11-22-33-44-55-66",
+ "Batch uses semicolons. Any MAC format accepted; server normalizes. Each result carries lat/lon/accuracy_m when found, wigle fallback link otherwise. Rate limit 20/min. JSON only.")
+ return page("hunt", body)
+
+@app.route("/api/bssid")
+def bssid_api():
+ r = rate_limit("bssid", 20, 60)
+ if r:
+ return r
+ macs = []
+ raw = param("macs") or param("mac") or ""
+ for part in str(raw).replace(";", "\n").replace(",", "\n").splitlines():
+ part = part.strip()
+ if part:
+ macs.append(part)
+ if not macs:
+ return jsonify({"ok": False, "error": "pass ?mac=AA:BB:CC:DD:EE:FF or ?macs=a;b;c (up to 25)"}), 400
+ out = []
+ for m in macs[:25]:
+ norm = _norm_mac(m)
+ if not norm:
+ out.append({"ok": False, "mac": m, "error": "invalid MAC (want aa:bb:cc:dd:ee:ff)"})
+ continue
+ res = _osint_bssid_lookup(norm)
+ if res.get("ok") and res.get("found"):
+ lat, lon = res["lat"], res["lon"]
+ res["maps_google"] = "https://www.google.com/maps?q=" + str(lat) + "," + str(lon)
+ res["maps_osm"] = "https://www.openstreetmap.org/?mlat=" + str(lat) + "&mlon=" + str(lon) + "#map=16/" + str(lat) + "/" + str(lon)
+ out.append(res)
+ single = len(out) == 1
+ return jsonify(out[0] if single else {"ok": True, "count": len(out), "results": out})
+
+# ---------- END TOOL: BSSID RADAR ----------
+
+
+# ---------- TOOL: HOOK RELAY ----------
+import secrets as _sec
+
+HOOK_MAX_BODY = 32768 # 32KB body capture cap
+
+def _hook_tables(con):
+ con.executescript("""CREATE TABLE IF NOT EXISTS hook_endpoints(id INTEGER PRIMARY KEY, user_id INTEGER, token TEXT UNIQUE, label TEXT, created INTEGER);
+ CREATE TABLE IF NOT EXISTS hook_hits(id INTEGER PRIMARY KEY, endpoint_id INTEGER, ts INTEGER, method TEXT, ip TEXT, ua TEXT, ct TEXT, src TEXT, headers TEXT, query TEXT, body TEXT, truncated INTEGER DEFAULT 0);""")
+ return con
+
+def _hook_detect_src(hdrs, body):
+ """Guess which service sent this webhook from headers + body."""
+ hl = {k.lower(): v for k, v in hdrs.items()}
+ if "stripe-signature" in hl: return "Stripe"
+ if "x-github-event" in hl or "x-github-delivery" in hl or "x-hub-signature" in hl: return "GitHub"
+ if "x-shopify-topic" in hl or "x-shopify-shop-domain" in hl or "x-shopify-hmac-sha256" in hl: return "Shopify"
+ if "x-telegram-bot-api-secret-token" in hl: return "Telegram"
+ ua = (hl.get("user-agent") or "").lower()
+ if ua.startswith("discordbot") or ua.startswith("discord"): return "Discord"
+ if "github-hookshot" in ua or "github-camel" in ua: return "GitHub"
+ j = jf(body)
+ if isinstance(j, dict):
+ if "update_id" in j: return "Telegram"
+ if "embeds" in j and "content" in j: return "Discord"
+ if j.get("object") in ("event", "checkout.session", "payment_intent") or j.get("livemode") is not None: return "Stripe"
+ if any(str(k).startswith("x_shopify") for k in j): return "Shopify"
+ return ""
+
+def _hook_target_guard(u):
+ """SSRF guard for replay targets. Returns error string or None if ok."""
+ if not u or not str(u).strip(): return "target_url required"
+ u = str(u).strip()
+ if not re.match(r"^https?://", u): return "target_url must start with http:// or https://"
+ try:
+ host = urllib.parse.urlsplit(u).hostname or ""
+ except Exception:
+ return "cannot parse target_url"
+ if not host: return "target_url has no host"
+ try:
+ ipa = ipaddress.ip_address(host)
+ except ValueError:
+ try:
+ ipa = ipaddress.ip_address(socket.gethostbyname(host))
+ except Exception:
+ return None # unresolvable here — let the fetcher report it
+ if ipa.is_private or ipa.is_loopback or ipa.is_link_local or ipa.is_reserved:
+ return "private/internal target blocked (SSRF guard: 10.x / 127. / 172.16-31 / 169.254 and friends)"
+ return None
+
+def _hook_new(uid, label):
+ con = _hook_tables(db())
+ token = _sec.token_hex(10)
+ con.execute("INSERT INTO hook_endpoints(user_id,token,label,created) VALUES(?,?,?,?)",
+ (uid, token, (label or "")[:60], int(time.time())))
+ con.commit()
+ return con.execute("SELECT * FROM hook_endpoints WHERE token=?", (token,)).fetchone()
+
+def _hook_hits_json(con, ep, limit=100):
+ rows = con.execute("SELECT * FROM hook_hits WHERE endpoint_id=? ORDER BY id DESC LIMIT ?", (ep["id"], limit)).fetchall()
+ out = []
+ for r in rows:
+ d = dict(r)
+ d["ts_iso"] = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(r["ts"])) + " UTC"
+ out.append(d)
+ return out
+
+def _hook_replay(hit, target_url):
+ err = _hook_target_guard(target_url)
+ if err:
+ return {"ok": False, "error": err}
+ try:
+ hdrs = {k: v for k, v in (json.loads(hit["headers"] or "{}")).items()
+ if k.lower() not in ("host", "content-length", "connection", "accept-encoding", "cookie")}
+ except Exception:
+ hdrs = {}
+ if hit["ct"]:
+ hdrs["Content-Type"] = hit["ct"]
+ body = (hit["body"] or "").encode("utf-8", "replace")
+ st, txt = http(target_url, headers=hdrs, data=body if hit["method"] != "GET" else None, method=hit["method"])
+ return {"ok": True, "target": target_url, "method": hit["method"],
+ "status": st, "response": txt[:2000], "replayed_at": int(time.time())}
+
+def _hook_pretty(body, ct):
+ """Pretty-print a captured body for the viewer."""
+ j = jf(body)
+ if j is not None:
+ return esc(json.dumps(j, indent=2))
+ return esc(body or "(empty body)")
+
+# ---- capture endpoint: NO auth, fast 200 ----
+@app.route("/hook/", methods=["GET", "POST", "PUT"])
+def hook_capture(token):
+ r = rate_limit("hookcapture", 120, 60)
+ if r: return r
+ con = _hook_tables(db())
+ ep = con.execute("SELECT id FROM hook_endpoints WHERE token=?", (token,)).fetchone()
+ if not ep:
+ return "unknown hook token", 404, {"Content-Type": "text/plain"}
+ raw = request.get_data() or b""
+ trunc = 1 if len(raw) > HOOK_MAX_BODY else 0
+ body = raw[:HOOK_MAX_BODY].decode("utf-8", "replace")
+ hdrs = dict(request.headers.items())
+ src = _hook_detect_src(hdrs, body)
+ hs = json.dumps(hdrs, indent=2)
+ ip = request.headers.get("X-Real-IP") or request.remote_addr or ""
+ ua = request.headers.get("User-Agent", "")
+ ct = request.headers.get("Content-Type", "")
+ q = (request.query_string or b"").decode("utf-8", "replace")[:2048]
+ con.execute("INSERT INTO hook_hits(endpoint_id,ts,method,ip,ua,ct,src,headers,query,body,truncated) VALUES(?,?,?,?,?,?,?,?,?,?,?)",
+ (ep["id"], int(time.time()), request.method, ip, ua, ct, src, hs, q, body, trunc))
+ con.commit()
+ return "captured", 200, {"Content-Type": "text/plain"}
+
+# ---- human page ----
+@app.route("/hook/create", methods=["POST"])
+def hook_create_route():
+ uid = current_user_id()
+ if not uid:
+ return page("hooks", "
HOOK RELAY
login required — log in to create webhook endpoints.
")
+ r = rate_limit("hookcreate", 20, 60)
+ if r: return r
+ _hook_new(uid, param("label"))
+ return Redirect("/hooks")
+
+@app.route("/hooks", methods=["GET", "POST"])
+def hooks_page():
+ uid = current_user_id()
+ if not uid:
+ body = """
+
HOOK RELAY
Instant public webhook inspector. Create a capture URL, point any webhook at it, see exactly what arrives — headers, body, query, IP — and replay it anywhere. Login to create endpoints.
+
LOGIN REQUIREDLog in or sign up (10 seconds, no KYC) to create webhook endpoints.
""" + how([
+ "Create an account, then make a hook endpoint — you get a unique URL like /hook/abc123.",
+ "Paste that URL into Stripe, GitHub, Shopify, Discord or Telegram webhook settings.",
+ "Every callback lands in your hit log: full headers, body (up to 32KB), query string, source IP and user-agent.",
+ "The source is auto-detected and badged — Stripe, GitHub, Discord, Shopify, Telegram.",
+ "Replay any captured hit to any public URL to retrigger an action or test a fix."]) + flow("debug why Stripe stopped calling my shop", [
+ "you Stripe webhooks to your shop went quiet. Did Stripe stop sending, or is your handler 500-ing? You cannot tell from inside your app.",
+ "you Create a hook endpoint here labeled stripe-debug and copy the curl-ready URL.",
+ "you In the Stripe dashboard, add the hook URL as a second webhook endpoint for the same events.",
+ "stripe Next event fires — the hook catches it in under a second, badged STRIPE, full headers and signed payload intact.",
+ "you No hits at all? Stripe is not sending (check their delivery logs). Hits arriving? Your handler is the problem — inspect the exact payload.",
+ "you Fix your handler, then replay the captured hit at your live endpoint to verify without waiting for the next real payment."]) + gloss([
+ ("webhook", "another server POSTs your URL when something happens — a payment, a push, an order"),
+ ("capture URL", "a unique throwaway endpoint that records everything it receives"),
+ ("replay", "resend a previously captured webhook body to any URL, with original headers"),
+ ("signature header", "Stripe-Signature / X-Hub-Signature — proves the sender, we keep it in the capture"),
+ ("SSRF guard", "replay targets on private networks (10.x, 127.x, 172.16-31, 169.254) are refused")]) + agent_card("POST /api/hook/create", 'curl -X POST ' + SITE + '/api/hook/create -d "label=stripe-debug" -H "Authorization: Bearer ***"', 'Returns {"ok":true,"url":".../hook/"}. Then GET /api/hook/list and GET /api/hook/hits?token=.')
+ return page("hooks", body)
+ msg = ""
+ con = _hook_tables(db())
+ if request.method == "POST":
+ r = rate_limit("hookpage", 30, 60)
+ if r: return r
+ act = param("act")
+ if act == "create":
+ _hook_new(uid, param("label"))
+ msg = '
endpoint created
'
+ elif act == "del":
+ con.execute("DELETE FROM hook_hits WHERE endpoint_id IN (SELECT id FROM hook_endpoints WHERE id=? AND user_id=?)", (param("id"), uid))
+ con.execute("DELETE FROM hook_endpoints WHERE id=? AND user_id=?", (param("id"), uid))
+ con.commit()
+ msg = '
endpoint deleted
'
+ elif act == "clear":
+ con.execute("DELETE FROM hook_hits WHERE endpoint_id IN (SELECT id FROM hook_endpoints WHERE id=? AND user_id=?)", (param("id"), uid))
+ con.commit()
+ msg = '
hits cleared
'
+ elif act == "replay":
+ hid = param("hit_id") or ""
+ tgt = param("target_url") or ""
+ h = con.execute("SELECT h.* FROM hook_hits h JOIN hook_endpoints e ON h.endpoint_id=e.id WHERE h.id=? AND e.user_id=?", (hid, uid)).fetchone()
+ if not h:
+ msg = '
hit not found
'
+ else:
+ res = _hook_replay(h, tgt)
+ if res.get("ok"):
+ msg = '
REPLAY RESULT — ' + str(res["status"]) + ' from ' + esc(res["target"]) + '
' + esc(res.get("response", "")[:800]) + '
'
+ else:
+ msg = '
REPLAY BLOCKED ' + esc(res.get("error", "")) + '
'
+ eps = con.execute("SELECT * FROM hook_endpoints WHERE user_id=? ORDER BY id DESC", (uid,)).fetchall()
+ cards = ""
+ for ep in eps:
+ hits = _hook_hits_json(con, ep, 50)
+ url = SITE + "/hook/" + ep["token"]
+ cmd = "curl -X POST " + url + " -H 'Content-Type: application/json' -d '{\"hello\":\"world\"}'"
+ hitview = ""
+ for h in hits:
+ badge = ('' + esc(h["src"]) + ' ') if h["src"] else ""
+ trunc = ' TRUNCATED' if h["truncated"] else ""
+ hitview += ('' + badge
+ + '' + esc(h["method"]) + ' · ' + esc(h["ts_iso"]) + ' · ' + esc(h["ip"]) + trunc + ''
+ + '
Public webhook inspector. Each endpoint is a throwaway URL that records everything sent to it — headers, body, query, IP — auto-detects the sender, and can replay any hit to any URL.
+{msg}
+
New endpoint
+
+
{cards}
""" + how([
+ "Create a hook — you instantly get a unique URL like " + SITE + "/hook/abc123def456.",
+ "Point any webhook at it: Stripe, GitHub, Discord, Shopify, Telegram, or curl by hand. GET, POST and PUT, any content-type.",
+ "The capture URL has NO login — webhooks come from outside servers, so it must answer 200 to anyone. Keep the URL secret-ish; only you can view the hits.",
+ "Bodies over 32KB are truncated (and flagged) so a giant payload cannot flood your log.",
+ "Each hit shows source badge, IP, user-agent, full headers, query string and a pretty-printed JSON body.",
+ "Replay sends the exact captured body + headers to any public URL — private targets (10.x, 127.x, 172.16-31, 169.254) are refused.",
+ "Agents: same everything over JSON — /api/hook/create, /api/hook/list, /api/hook/hits, /api/hook/replay."]) + flow("debug why Stripe stopped calling my shop", [
+ "you Payments complete but your shop never marks orders paid. Is Stripe sending? Is your handler crashing? Blind either way.",
+ "you Create a hook labeled stripe-debug, copy the curl line, paste the URL into Stripe as a second webhook endpoint.",
+ "stripe The next payment fires — the hit lands instantly, badged STRIPE (detected from the Stripe-Signature header), payload fully intact.",
+ "you Zero hits = Stripe-side problem (check their delivery log). Hits present = read the exact JSON, find what your handler choked on.",
+ "you Ship the fix, then hit replay to fire that same signed payload at your live endpoint — verified without waiting for a real customer."]) + gloss([
+ ("capture URL", "unique unguessable URL (/hook/) that records every request it receives"),
+ ("source badge", "auto-detected sender: Stripe, GitHub, Discord, Shopify or Telegram"),
+ ("replay", "resend a captured body with original headers to any public URL"),
+ ("truncation", "bodies over 32KB are cut and flagged — protection against payload floods"),
+ ("SSRF guard", "replay refuses internal addresses so the relay cannot probe your LAN")]) + agent_card("POST /api/hook/create · GET /api/hook/list · GET /api/hook/hits?token= · POST /api/hook/replay", 'curl -X POST ' + SITE + '/api/hook/create -d "label=stripe-debug" -H "Authorization: Bearer ***"', 'Full JSON lifecycle: create, list, inspect hits, replay (hit_id + target_url).')
+ return page("hooks", body)
+
+# ---- JSON API ----
+@app.route("/api/hook/create", methods=["POST"])
+def api_hook_create():
+ r = rate_limit("hookapi", 20, 60)
+ if r: return r
+ uid = key_user() or current_user_id()
+ if not uid:
+ return jsonify({"ok": False, "error": "auth required: account session (sign up at /inbox, no KYC) or Authorization: Bearer *** key"}), 401
+ ep = _hook_new(uid, jp("label") or param("label"))
+ return jsonify({"ok": True, "token": ep["token"], "url": SITE + "/hook/" + ep["token"], "capture_path": "/hook/" + ep["token"], "label": ep["label"], "page": SITE + "/hooks"})
+
+@app.route("/api/hook/list")
+def api_hook_list():
+ uid = key_user() or current_user_id()
+ if not uid:
+ return jsonify({"ok": False, "error": "auth required"}), 401
+ con = _hook_tables(db())
+ eps = con.execute("SELECT * FROM hook_endpoints WHERE user_id=? ORDER BY id DESC", (uid,)).fetchall()
+ out = []
+ for ep in eps:
+ n = con.execute("SELECT COUNT(*) c FROM hook_hits WHERE endpoint_id=?", (ep["id"],)).fetchone()["c"]
+ last = con.execute("SELECT MAX(ts) m FROM hook_hits WHERE endpoint_id=?", (ep["id"],)).fetchone()["m"]
+ out.append({"token": ep["token"], "label": ep["label"], "created": ep["created"],
+ "url": SITE + "/hook/" + ep["token"], "hits": n, "last_hit": last})
+ return jsonify({"ok": True, "endpoints": out})
+
+@app.route("/api/hook/hits")
+def api_hook_hits():
+ uid = key_user() or current_user_id()
+ if not uid:
+ return jsonify({"ok": False, "error": "auth required"}), 401
+ tok = param("token") or ""
+ con = _hook_tables(db())
+ ep = con.execute("SELECT * FROM hook_endpoints WHERE token=? AND user_id=?", (tok, uid)).fetchone()
+ if not ep:
+ return jsonify({"ok": False, "error": "unknown token"}), 404
+ limit = 100
+ try:
+ limit = max(1, min(500, int(param("limit") or 100)))
+ except Exception:
+ pass
+ hits = _hook_hits_json(con, ep, limit)
+ for h in hits:
+ try:
+ h["headers_json"] = json.loads(h["headers"] or "{}")
+ except Exception:
+ h["headers_json"] = {}
+ return jsonify({"ok": True, "token": tok, "count": len(hits), "hits": hits})
+
+@app.route("/api/hook/replay", methods=["POST"])
+def api_hook_replay():
+ r = rate_limit("hookreplay", 10, 60)
+ if r: return r
+ uid = key_user() or current_user_id()
+ if not uid:
+ return jsonify({"ok": False, "error": "auth required"}), 401
+ hid = jp("hit_id") or param("hit_id")
+ tgt = jp("target_url") or param("target_url")
+ if not hid or not tgt:
+ return jsonify({"ok": False, "error": "hit_id and target_url required"}), 400
+ con = _hook_tables(db())
+ h = con.execute("SELECT h.* FROM hook_hits h JOIN hook_endpoints e ON h.endpoint_id=e.id WHERE h.id=? AND e.user_id=?", (hid, uid)).fetchone()
+ if not h:
+ return jsonify({"ok": False, "error": "hit not found (or not yours)"}), 404
+ res = _hook_replay(h, tgt)
+ if not res.get("ok"):
+ return jsonify(res), 400
+ return jsonify(res)
+# ---------- END TOOL: HOOK RELAY ----------
+
+
+# ---------- TOOL: FACE TRACE ----------
+import io as _io
+from PIL import Image as _PILImage, ImageOps as _PILImageOps
+from concurrent.futures import ThreadPoolExecutor as _TPE
+
+def _face_db():
+ con = db()
+ con.execute("CREATE TABLE IF NOT EXISTS face_cache(key TEXT PRIMARY KEY, img BLOB, sha256 TEXT, ts INTEGER)")
+ con.commit()
+ return con
+
+def _hamming(h1, h2):
+ n = max(len(h1), len(h2)) * 4
+ try:
+ return bin(int(h1, 16) ^ int(h2, 16))[2:].zfill(n).count("1")
+ except Exception:
+ return 999
+
+def dhash(img):
+ """dHash: grayscale 9x8 (w=9,h=8), compare horizontally adjacent pixels → 64-bit."""
+ g = _PILImageOps.grayscale(img).resize((9, 8))
+ px = list(g.getdata())
+ bits = 0
+ for r in range(8):
+ row = px[r * 9:(r + 1) * 9]
+ for c in range(8):
+ bits = (bits << 1) | (1 if row[c] > row[c + 1] else 0)
+ return f"{bits:016x}"
+
+def ahash(img):
+ """Average hash: 8x8 grayscale, bit = pixel > mean."""
+ g = _PILImageOps.grayscale(img).resize((8, 8))
+ px = list(g.getdata())
+ m = sum(px) / 64.0
+ bits = 0
+ for p in px:
+ bits = (bits << 1) | (1 if p > m else 0)
+ return f"{bits:016x}"
+
+def _pfp_hashes(blob):
+ """Hashes for a raw image blob: {'sha256','dhash','ahash'} or {'error':...}"""
+ out = {"sha256": hashlib.sha256(blob).hexdigest(), "bytes": len(blob)}
+ try:
+ img = _PILImage.open(_io.BytesIO(blob))
+ img.load()
+ out["dhash"] = dhash(img)
+ out["ahash"] = ahash(img)
+ out["format"] = (img.format or "?").lower()
+ out["size"] = list(img.size)
+ except Exception as e:
+ out["error"] = f"not an image: {e}"[:160]
+ return out
+
+def _fcache_get(key):
+ con = _face_db()
+ r = con.execute("SELECT img, sha256, ts FROM face_cache WHERE key=?", (key,)).fetchone()
+ if r and (int(time.time()) - r["ts"]) < 3600:
+ return bytes(r["img"])
+ return None
+
+def _fcache_put(key, blob):
+ con = _face_db()
+ con.execute("INSERT OR REPLACE INTO face_cache(key,img,sha256,ts) VALUES(?,?,?,?)",
+ (key, sqlite3.Binary(blob), hashlib.sha256(blob).hexdigest(), int(time.time())))
+ con.commit()
+
+def _fetch_img(url):
+ """Fetch an image URL → (bytes|None, note). 1h sqlite cache."""
+ ck = "url:" + hashlib.sha256(url.encode()).hexdigest()[:32]
+ c = _fcache_get(ck)
+ if c is not None:
+ return c, "cache"
+ req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (Dark0rbits toolbox)"})
+ try:
+ with urllib.request.urlopen(req, timeout=10, context=_CTX) as r:
+ blob = r.read(6 * 1024 * 1024)
+ if len(blob) < 64:
+ return None, f"too small ({len(blob)}b)"
+ _fcache_put(ck, blob)
+ return blob, "fetched"
+ except Exception as e:
+ return None, str(e)[:120]
+
+def pfp_targets(u):
+ """Avatar source plan for a username: resolvers and constructed URLs."""
+ u = u.strip().lstrip("@")
+ q = urllib.parse.quote(u)
+ return [
+ {"platform": "GitHub", "kind": "resolve",
+ "api": f"https://api.github.com/users/{q}",
+ "extract": lambda j: (j.get("avatar_url") or "") if isinstance(j, dict) else "",
+ "profile": f"https://github.com/{q}"},
+ {"platform": "Reddit", "kind": "resolve",
+ "api": f"https://www.reddit.com/{q}/about.json",
+ "extract": lambda j: (((j.get("data") or {}).get("icon_img") or "")) if isinstance(j, dict) and j.get("data") else "",
+ "profile": f"https://www.reddit.com/user/{q}"},
+ {"platform": "Telegram", "kind": "construct",
+ "img": "https://t.me/" + q,
+ "note": "manual / constructed: t.me profile — grab photo from the page",
+ "profile": f"https://t.me/{q}"},
+ {"platform": "Steam", "kind": "construct",
+ "img": "https://steamcommunity.com/id/{u}/".replace("{u}", q),
+ "note": "manual / constructed: Steam profile — XML API has ",
+ "profile": "https://steamcommunity.com/id/" + q},
+ {"platform": "Twitch", "kind": "construct",
+ "img": "https://www.twitch.tv/" + q,
+ "note": "manual / constructed: profile page (avatars need a client-id)",
+ "profile": f"https://www.twitch.tv/{q}"},
+ ]
+
+def pfp_harvest(u, target_hashes=None):
+ """Resolve + download avatars for username u; compare with target hashes."""
+ results = []
+ for t in pfp_targets(u):
+ entry = {"platform": t["platform"], "profile": t["profile"], "status": "no avatar",
+ "hashes": None, "match": None, "distances": {}}
+ img_url = ""
+ if t["kind"] == "resolve":
+ st, body = http(t["api"], timeout=10)
+ j = jf(body)
+ img_url = t["extract"](j) if j else ""
+ entry["http"] = st
+ if not img_url:
+ entry["status"] = "not found"
+ else:
+ entry["status"] = "constructed link"
+ entry["note"] = t["note"]
+ if img_url:
+ blob, note = _fetch_img(img_url)
+ if blob:
+ h = _pfp_hashes(blob)
+ entry.update({"status": "ok", "img_url": img_url, "source": note, "hashes": h})
+ if target_hashes and not h.get("error"):
+ dd = min(_hamming(h["dhash"], target_hashes["dhash"]),
+ _hamming(h["ahash"], target_hashes["ahash"]))
+ entry["distances"] = {"dhash": _hamming(h["dhash"], target_hashes["dhash"]),
+ "ahash": _hamming(h["ahash"], target_hashes["ahash"])}
+ entry["match"] = "likely same image" if dd <= 10 else "different image"
+ else:
+ entry["status"] = f"download failed ({note})"
+ results.append(entry)
+ return {"ok": True, "username": u.strip().lstrip("@"), "targets": results}
+
+def face_search_leads():
+ return [
+ ("Google Lens", "https://lens.google.com/uploadbyurl?url=", "Google's reverse-image search — strongest for lookalikes and crops"),
+ ("Yandex Images", "https://yandex.com/images/search?rpt=imageview&url=", "best face recall of the public engines, especially EU/RU web"),
+ ("Bing Visual Search", "https://www.bing.com/images/search?view=detailv2&iss=sbi&q=imgurl:", "Microsoft visual search — good LinkedIn / social recall"),
+ ("TinEye", "https://tineye.com/search?url=", "exact-copy finder — best for 'where did this exact file appear first'"),
+ ]
+
+@app.route("/face", methods=["GET", "POST"])
+def face_tool():
+ uid = current_user_id()
+ if not uid:
+ return page("face", '
FACE TRACE
Login first — this tool is for accounts. Free, no KYC: log in / sign up.
')
+ r = rate_limit("facepage", 20, 60)
+ if r: return r
+ res = ""
+ up_hashes = None
+ err = ""
+ f = request.files.get("img")
+ username = (param("u") or "").strip().lstrip("@")
+ if request.method == "POST":
+ if not f and not username:
+ err = "give me an image and/or a username"
+ if f:
+ blob = f.read(6 * 1024 * 1024)
+ if len(blob) < 64:
+ err = "file too small / empty"
+ else:
+ h = _pfp_hashes(blob)
+ if h.get("error"):
+ err = h["error"]
+ else:
+ up_hashes = h
+ up_rows = [("sha256", f"{h['sha256']}"), ("dHash (8x8)", f"{h['dhash']}"),
+ ("aHash (8x8)", f"{h['ahash']}"), ("format / size", f"{h.get('format')} {h.get('size')} · {h['bytes']} bytes")]
+ if username:
+ up_rows.append(("comparing against", f"avatars of {esc(username)}"))
+ res += kv(up_rows)
+ if username and not (len(username) >= 2 and len(username) <= 60 and not any(c in "<>\"'/" for c in username)):
+ if not err: err = "bad username (2-60 chars, no slashes/html)"
+ username = ""
+ if username:
+ hv = pfp_harvest(username, up_hashes)
+ rows = ""
+ for t in hv["targets"]:
+ if t["status"] == "ok":
+ dd = t["distances"]
+ if t["match"]:
+ m = ('MATCH ' + esc(t["match"]) +
+ f" · d {dd['dhash']} / a {dd['ahash']}")
+ else:
+ m = "—"
+ rows += (f"
Verdict rule: best Hamming distance (dHash or aHash) ≤ 10 of 64 bits = likely same image. Cache: 1h sqlite.
')
+ if up_hashes and not username:
+ res += '
Reverse-image leads dark0rbits never calls a reverse-image API for you — open these yourself and upload the file:
' + " ".join(f'▸ {esc(nm)} →{esc(d)}' for nm, base, d in face_search_leads()) + "
"
+ if err:
+ res = f'
{esc(err)}
' + res
+ body = f"""
+
FACE TRACE
+
Profile-picture triangulation without reverse-image APIs. Hash an avatar (dHash + aHash + sha256), harvest avatars a username uses across platforms, and let Hamming distance tell you whether it's the same picture — same person behind 4 different usernames?
+
+
+
Image alone = hashes + manual search leads. Username alone = avatar harvest + hashes. Both = harvest AND compare against your upload (Hamming ≤ 10 = likely match).
+
+{res}
+""" + how([
+ "Upload the avatar you already have — a forum pic, a Telegram photo, anything.",
+ "The image is fingerprinted three ways: dHash (9x8 grayscale, adjacent-pixel compares), aHash (8x8 vs mean) and plain sha256 — all computed locally, nothing uploaded anywhere.",
+ "Give a username too, and the tool fetches that handle's real avatars: GitHub and Reddit via their public JSON APIs, plus constructed profile links for Telegram, Steam and Twitch.",
+ "Every fetched avatar gets the same fingerprints, and Hamming distance (bits differing out of 64) is computed against your upload: ≤ 10 = likely the same image.",
+ "No image ever goes to Google, Yandex, Bing or TinEye. Instead you get direct upload links to all four — you decide when to escalate.",
+ "Downloads are cached in sqlite for an hour, so re-running a trace is fast and doesn't hammer anyone's API.",
+ "Judgment stays yours: same picture is strong evidence, but people reuse stock photos. dHash says 'same image', not 'same human'.",
+])
+ body += flow("same person behind 4 different usernames?", [
+ "youA scammer contacts you from ghostuser42 with a friendly avatar. Screenshot it.",
+ "youUpload the avatar here, type ghostuser42, hit TRACE. GitHub and Reddit avatars get fetched and hashed.",
+ "toolVerdict table: Reddit avatar MATCH — d 4/64. GitHub avatar: different image.",
+ "youRun TRACE on two other handles the same person used. Reddit matches again — same source photo, different display names.",
+ "youEscalate: open the Google Lens / Yandex leads with the original file to find where the photo first appeared.",
+ "youConclusion: four usernames, one face. That's your triangulation — no reverse-image API ever saw the picture.",
+ ])
+ body += gloss([
+ ("dHash", "difference hash: resize to 9x8 grayscale, compare each pixel with its right neighbor → 64 bits that survive resizing and compression"),
+ ("aHash", "average hash: 8x8 grayscale, each bit = pixel brighter than the mean"),
+ ("Hamming distance", "how many of the 64 bits differ between two hashes — 0 = identical image, ≤ 10 = likely same picture, 32 = unrelated"),
+ ("perceptual hash", "a fingerprint of what an image LOOKS like, not its bytes — crops and re-encodes still match; sha256 only matches exact copies"),
+ ("avatar harvest", "collecting the profile pictures a username currently uses, from public profile APIs"),
+ ])
+ body += agent_card('POST /api/face (multipart image and/or u=username)',
+ 'curl -F "img=@avatar.png" -F "u=ghostuser42" https://dark0rbits.thetempleofdoom.com/api/face',
+ 'Returns sha256/dHash/aHash of your upload + per-platform harvest with distances and verdict. Auth: session or Bearer key.')
+ return page("face", body)
+
+@app.route("/api/face", methods=["GET", "POST"])
+def api_face():
+ r = rate_limit("face", 20, 60)
+ if r: return r
+ uid = key_user() or current_user_id()
+ if not uid:
+ return jsonify({"ok": False, "error": "auth required: account session or API key"}), 401
+ f = request.files.get("img")
+ username = ((jp("u") if request.is_json else None) or param("u") or "").strip().lstrip("@")
+ if not f and not username:
+ return jsonify({"ok": False, "error": "provide multipart image and/or u=username"}), 400
+ out = {"ok": True}
+ tgt = None
+ if f:
+ blob = f.read(6 * 1024 * 1024)
+ if len(blob) < 64:
+ return jsonify({"ok": False, "error": "image too small/empty"}), 400
+ h = _pfp_hashes(blob)
+ if h.get("error"):
+ return jsonify({"ok": False, "error": h["error"]}), 400
+ out["uploaded"] = h
+ tgt = h
+ if username:
+ if not (2 <= len(username) <= 60) or any(c in "<>\"'/" for c in username):
+ return jsonify({"ok": False, "error": "bad username"}), 400
+ out["harvest"] = pfp_harvest(username, tgt)
+ if f or not username:
+ out["leads"] = [{"name": n, "url": b, "note": d} for n, b, d in face_search_leads()]
+ return jsonify(out)
+# ---------- END TOOL: FACE TRACE ----------
+
+
+# ---------- TOOL: ROTATOR ----------
+import random as _rnd
+import json as _json
+
+_ROTATOR_DB_READY = False
+
+def _rotator_db(con):
+ global _ROTATOR_DB_READY
+ if not _ROTATOR_DB_READY:
+ con.execute("CREATE TABLE IF NOT EXISTS rotator_history(id INTEGER PRIMARY KEY, user_id INTEGER, ts INTEGER, ua TEXT, platform TEXT, seed TEXT)")
+ con.commit()
+ _ROTATOR_DB_READY = True
+
+ROTATOR_PLATFORMS = {
+ "desktop": [
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15",
+ "Mozilla/5.0 (X11; Linux x86_64; rv:125.0) Gecko/20100101 Firefox/125.0",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0",
+ ],
+ "mobile": [
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1",
+ "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36",
+ "Mozilla/5.0 (Linux; Android 13; SM-G991B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile Safari/537.36",
+ "Mozilla/5.0 (iPad; CPU OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1",
+ ],
+ "agent": [
+ "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)",
+ "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)",
+ "curl/8.4.0",
+ "Wget/1.21.4 (linux-gnu)",
+ "python-urllib/3.11",
+ ],
+ "stealth": [
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
+ ],
+}
+ROTATOR_REFERER_POOL = [
+ "https://www.google.com/", "https://duckduckgo.com/", "https://news.ycombinator.com/",
+ "https://www.bing.com/", "https://www.reddit.com/", "(direct)",
+]
+ROTATOR_LANG_POOL = ["en-US,en;q=0.9", "en-GB,en;q=0.8", "de-DE,de;q=0.9,en;q=0.5", "fr-FR,fr;q=0.9", "ja-JP,ja;q=0.8"]
+
+def rotator_identity(platform, rng):
+ ua = rng.choice(ROTATOR_PLATFORMS.get(platform, ROTATOR_PLATFORMS["desktop"]))
+ return {
+ "user_agent": ua,
+ "referer": rng.choice(ROTATOR_REFERER_POOL),
+ "accept_language": rng.choice(ROTATOR_LANG_POOL),
+ "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
+ "sec_ch_ua_mobile": "?1" if platform == "mobile" else "?0",
+ "dnt": rng.choice(["1", "0", "(none)"]),
+ }
+
+def rotator_spin(platform, n, seed):
+ platform = platform if platform in ROTATOR_PLATFORMS else "desktop"
+ try:
+ n = max(1, min(int(n), 25))
+ except Exception:
+ n = 5
+ rng = _rnd.Random(str(seed)) if seed else _rnd.Random()
+ ids = [rotator_identity(platform, rng) for _ in range(n)]
+ curl = 'curl -A "' + ids[0]["user_agent"] + '"'
+ if ids[0]["referer"] != "(direct)":
+ curl += ' -e "' + ids[0]["referer"] + '"'
+ curl += ' -H "Accept-Language: ' + ids[0]["accept_language"] + '" https://target.example/'
+ return {"ok": True, "platform": platform, "count": len(ids), "seed": seed or None, "identities": ids, "curl_first": curl}
+
+@app.route("/rotator", methods=["GET", "POST"])
+def rotator_page():
+ uid = current_user_id()
+ platform = param("platform") or "desktop"
+ if platform not in ROTATOR_PLATFORMS:
+ platform = "desktop"
+ n = param("n") or "5"
+ seed = (param("seed") or "").strip()[:64]
+ res = ""
+ if request.method == "POST":
+ r = rate_limit("rotator", 20, 60)
+ if r:
+ return r
+ d = rotator_spin(platform, n, seed)
+ try:
+ con = db()
+ _rotator_db(con)
+ for ident in d["identities"][:5]:
+ con.execute("INSERT INTO rotator_history(user_id,ts,ua,platform,seed) VALUES(?,?,?,?,?)",
+ (uid or 0, int(time.time()), ident["user_agent"], platform, seed or ""))
+ con.commit()
+ except Exception:
+ pass
+ rows = ""
+ for i, ident in enumerate(d["identities"], 1):
+ rows += ("
Spin consistent browser identities — User-Agent, referer, language, DNT — from four pools. Same seed replays the exact same rotation every time. No logs kept beyond your own history.
+
+{res}
+
API: GET /api/rotator?platform=mobile&n=10&seed=abc → JSON identities + ready-made curl.
""" + how([
+ "Pick a pool: desktop, mobile, agent or stealth — each holds real-world header strings.",
+ "Choose how many identities to spin, 1 to 25 per call.",
+ "Give it a seed and the rotation becomes deterministic — the same seed always replays the same identities in the same order.",
+ "Leave the seed blank for a fresh random rotation every call.",
+ "Every identity is a full consistent set: UA, referer, Accept-Language, DNT — not just a UA string.",
+ "The first identity comes back as a ready-to-paste curl command.",
+ "Agents: GET /api/rotator with the same params, JSON out, rate-limited 20/min.",
+ "Nothing is stored except the last few spins in your own account history.",
+ ]) + flow("Rotating through a scrape run", [
+ 'you set pool=mobile, n=10, seed=run-42 and hit Spin.',
+ 'The lab hands back 10 consistent identities — UA, referer, language, DNT matched per identity.',
+ 'you copy the curl line for the first one or call /api/rotator from your script.',
+ 'The target sees ten different plausible visitors instead of one hammering client.',
+ 'Re-run with the same seed later to reproduce the exact rotation for debugging.',
+ ]) + gloss([
+ ("rotation", "cycling through a pool of values so no single fingerprint repeats too often"),
+ ("seed", "a string fed to the RNG — same seed, same sequence, every time"),
+ ("DNT", "Do-Not-Track header — 0, 1 or absent, randomized per identity"),
+ ("consistent identity", "UA + referer + language that plausibly belong to the same browser"),
+ ]) + agent_card('GET /api/rotator?platform=mobile&n=10&seed=abc',
+ 'curl "https://dark0rbits.thetempleofdoom.com/api/rotator?platform=mobile&n=10&seed=abc"',
+ 'JSON: identities[] with user_agent, referer, accept_language, dnt + curl_first. Rate limit 20/min.')
+ return page("rotator", body)
+
+@app.route("/api/rotator", methods=["GET", "POST"])
+def api_rotator():
+ r = rate_limit("rotator", 20, 60)
+ if r:
+ return r
+ platform = (param("platform") or "desktop").strip().lower()
+ if platform not in ROTATOR_PLATFORMS:
+ return jsonify({"ok": False, "error": "platform must be one of: " + ", ".join(sorted(ROTATOR_PLATFORMS))}), 400
+ seed = (param("seed") or "").strip()[:64]
+ d = rotator_spin(platform, param("n") or "1", seed)
+ return jsonify(d)
+
+@app.route("/api/rotator/pools")
+def api_rotator_pools():
+ return jsonify({"ok": True, "pools": {k: len(v) for k, v in ROTATOR_PLATFORMS.items()},
+ "referers": len(ROTATOR_REFERER_POOL), "languages": len(ROTATOR_LANG_POOL)})
+# ---------- END TOOL: ROTATOR ----------
+
+
+# ---------- TOOL: IDENTITY SHELF ----------
+import time as _shelf_time
+
+def _shelf_data(uid):
+ con = db()
+ now = int(_shelf_time.time())
+ out = {}
+ out["mailboxes"] = [dict(r) for r in con.execute(
+ "SELECT address, expires, cnt, paid FROM mailboxes WHERE user_id=? ORDER BY (expires>0), expires LIMIT 50", (uid,)).fetchall()]
+ out["sms"] = [dict(r) for r in con.execute(
+ "SELECT phone, service, expires, status FROM sms_rentals WHERE user_id=? AND expires>0 ORDER BY expires LIMIT 50", (uid,)).fetchall()]
+ out["deaddrops"] = [dict(r) for r in con.execute(
+ "SELECT token, expires, reads_left, burn_after FROM deadrops WHERE user_id=? ORDER BY (expires>0), expires LIMIT 50", (uid,)).fetchall()]
+ out["canaries"] = []
+ for r in con.execute("SELECT token, tag, armed, rearm FROM canaries WHERE user_id=? ORDER BY id DESC LIMIT 50", (uid,)).fetchall():
+ d = dict(r)
+ d["hits"] = con.execute("SELECT COUNT(*) c FROM canary_hits WHERE canary_id=(SELECT id FROM canaries WHERE token=?)", (r["token"],)).fetchone()["c"]
+ out["canaries"].append(d)
+ out["trackables"] = [dict(r) for r in con.execute(
+ "SELECT token, filename, paid, created FROM trackables WHERE user_id=? ORDER BY id DESC LIMIT 50", (uid,)).fetchall()]
+ # stats
+ live = lambda e: e and e > now
+ n_live = sum(1 for m in out["mailboxes"] if m["paid"] and live(m["expires"])) \
+ + sum(1 for s in out["sms"] if live(s["expires"])) \
+ + sum(1 for d in out["deaddrops"] if live(d["expires"]))
+ expiring = sorted([e for grp in ("mailboxes", "sms", "deaddrops") for e in
+ (x["expires"] for x in out[grp] if live(x.get("expires"))) if e])
+ out["stats"] = {
+ "live_identities": n_live,
+ "next_expiry": expiring[0] if expiring else None,
+ "total_mail_received": sum(m["cnt"] or 0 for m in out["mailboxes"]),
+ "armed_traps": sum(1 for c in out["canaries"] if c["armed"]),
+ "total_trap_hits": sum(c["hits"] for c in out["canaries"]),
+ }
+ return out
+
+def _shelf_badge(expires):
+ now = int(_shelf_time.time())
+ if not expires:
+ return 'no expiry'
+ left = expires - now
+ when = _shelf_time.strftime("%b %d %H:%M", _shelf_time.localtime(expires))
+ if left <= 0:
+ return f'EXPIRED {when}'
+ cls = "bad" if left < 86400 else "ok"
+ unit = "d" if left >= 86400 else "h"
+ val = left // 86400 if left >= 86400 else left // 3600
+ return f'{val}{unit} left · {when}'
+
+@app.route("/shelf")
+def shelf():
+ uid = current_user_id()
+ if not uid:
+ return page("shelf", """
IDENTITY SHELF
One dashboard for every burner you own — mailboxes, numbers, dead-drops, traps — with live countdowns so nothing dies silently.
""" + how([
+"Every burner on dark0rbits has a lifespan — mailboxes expire, rentals run out, dead-drops burn.",
+"The shelf lists all of yours in one place with live countdown badges.",
+"Under 24 hours left, a badge turns red — renew or replace before it dies.",
+"Expired items stay listed so you can clean up or recreate them.",
+"Agents: GET /api/shelf returns the same data as JSON for monitoring."]))
+ d = _shelf_data(uid)
+ def rows_mail():
+ if not d["mailboxes"]: return '
"
+ for c in d["canaries"])
+ st = d["stats"]
+ nxt = _shelf_time.strftime("%b %d %H:%M", _shelf_time.localtime(st["next_expiry"])) if st["next_expiry"] else "—"
+ body = f"""
+
IDENTITY SHELF
Everything you own that can die, on one page — with live countdowns. Know when every burner expires before it expires.
+{kv([("Live identities", f"{st['live_identities']} mailboxes + numbers + drops"),
+ ("Next to expire", f"{nxt}"), ("Mail received (all time)", st["total_mail_received"]),
+ ("Armed traps", f"{st['armed_traps']} armed · {st['total_trap_hits']} total hits")])}
+
BURNER MAILBOXES
Address
Mail
Life
{rows_mail()}
+
SMS NUMBERS
Number
Service
Life
Status
{rows_sms()}
+
DEAD-DROPS
Token
Burns
Life
{rows_dd()}
+
CANARY TRAPS
Tag
Hits
Status
{rows_can()}
+""" + how([
+"Every burner has a lifespan — this page lists all of yours with a countdown badge per item.",
+"Badges tick live (every 30s); the static expiry date renders even without JS.",
+"Green = over 24h left. Red = under 24h or already dead — renew or replace.",
+"Quick links jump straight to each item: mailbox, dead-drop, trap hit log.",
+"Agents: GET /api/shelf returns identical JSON — wire it into a cron and get paged before anything dies."])
+ body += flow("never lose a burner to the clock again", [
+"you run 3 burner mailboxes for signups and 2 SMS numbers for verifications.",
+"you open the shelf once a morning: five green badges, everything alive.",
+"Thursday: one mailbox badge is red — 6h left. You have all day to migrate that identity.",
+"agent a cron hits /api/shelf hourly and messages you when anything drops under 24h.",
+"nothing expires silently. No more 'why did my verification stop working' mysteries."])
+ body += gloss([("burner","a disposable identity — mailbox, phone number, or drop",),("TTL","time to live — how long until the service retires it"),("burn-after-read","dead-drops self-destruct after N openings")])
+ body += agent_card('GET /api/shelf', 'curl "https://dark0rbits.thetempleofdoom.com/api/shelf" -H "Cookie: dark0rbits_tok=…"', 'Returns mailboxes, sms, deaddrops, canaries + stats. Perfect for expiry-monitoring crons.')
+ return page("shelf", body)
+
+@app.route("/api/shelf")
+def api_shelf():
+ uid = key_user() or current_user_id()
+ if not uid:
+ return jsonify({"ok": False, "error": "login required — free no-KYC account at /inbox"}), 401
+ d = _shelf_data(uid)
+ for grp in ("mailboxes", "sms", "deaddrops", "canaries", "trackables"):
+ for x in d[grp]:
+ x.pop("token", None) # never leak tokens over API
+ return jsonify({"ok": True, **d})
+# ---------- END TOOL: IDENTITY SHELF ----------
+
+
+# ---------- TOOL: UNFURL ----------
+def _unfurl_db():
+ con = db()
+ con.execute("""CREATE TABLE IF NOT EXISTS unfurls(id INTEGER PRIMARY KEY, user_id INTEGER, url TEXT,
+ final_url TEXT, hops TEXT, status INTEGER, err TEXT, created INTEGER)""")
+ return con
+
+def _unfurl_ua():
+ con = _unfurl_db()
+ try:
+ r = con.execute("SELECT val FROM settings WHERE user_id=? AND key='unfurl_ua'", (current_user_id() or 0,)).fetchone()
+ if r and r["val"]:
+ return r["val"]
+ except Exception:
+ pass
+ return "Mozilla/5.0 (Dark0rbits unfurl)"
+
+def _unfurl_save(user_id, url, final_url, hops_json, status, err):
+ con = _unfurl_db()
+ con.execute("INSERT INTO unfurls(user_id,url,final_url,hops,status,err,created) VALUES(?,?,?,?,?,?,?)",
+ (user_id, url, final_url, hops_json, status, err, int(time.time())))
+ con.commit()
+ return con.execute("SELECT id FROM unfurls WHERE user_id=? ORDER BY id DESC LIMIT 1", (user_id,)).fetchone()["id"]
+
+_NOFOLLOW = ("javascript:", "data:", "mailto:", "tel:", "blob:", "about:", "file:", "chrome:", "intent:", "ws:", "wss:")
+
+def _unfurl_meta(html_text, base_url):
+ """Extract , meta description, og:*, twitter:* tags. Returns (title, metas dict, links, scripts, forms, iframes)."""
+ metas, links, scripts, forms, iframes = {}, [], [], [], []
+ title = ""
+ m = re.search(r"]*>(.*?)", html_text, re.I | re.S)
+ if m:
+ title = re.sub(r"\s+", " ", m.group(1)).strip()[:300]
+ for m in re.finditer(r"]+>", html_text, re.I):
+ tag = m.group(0)
+ def attr(name):
+ mm = re.search(name + r"\s*=\s*[\"']([^\"']*)[\"']", tag, re.I)
+ return mm.group(1) if mm else ""
+ nm, prop, con = attr("name"), attr("property"), attr("content")
+ key = (prop or nm).lower()
+ if key and con:
+ metas[key] = con[:600]
+ for m in re.finditer(r"]+rel\s*=\s*[\"']?[^\"'>]*stylesheet[^\"'>]*[\"']?[^>]*>", html_text, re.I):
+ links.append(m.group(0)[:500])
+ for m in re.finditer(r"