From 85691269a701d339ae4c911b41cc6a66428ad243 Mon Sep 17 00:00:00 2001 From: drjones Date: Mon, 21 Sep 2026 15:03:25 +0000 Subject: [PATCH] Sync to live: VEGA+OSINT tool wiring, 345-tool catalog, SSRF-guarded OSINT proxy --- .gitignore | 7 + app.py | 1091 +++++++++++++++++++++++++++++++++++++++++++++-- static/hero.png | Bin 0 -> 1557345 bytes 3 files changed, 1062 insertions(+), 36 deletions(-) create mode 100644 static/hero.png diff --git a/.gitignore b/.gitignore index faf7761..f0df0c5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,10 @@ hyperion.db __pycache__/ *.pyc .env +*.bak* +*.patchbak +*.roundbak +*.impbak +*.tgz +*.nft +hyperion.env diff --git a/app.py b/app.py index dbf1375..ca68130 100644 --- a/app.py +++ b/app.py @@ -35,10 +35,17 @@ app.secret_key = os.environ.get("HYPERION_SECRET", "hyperion-secret-" + str(int( # --------------------------------------------------------------------------- # Config (from Phase 2 state). Overridable via env. # --------------------------------------------------------------------------- -BTCPAY_URL = os.environ.get("HYPERION_BTCPAY_URL", "https://10.30.20.140") -BTCPAY_STORE = os.environ.get("HYPERION_BTCPAY_STORE", "77rHbzqFf1cJBjM41edVa8HzeRhiQVdHuJfmAuRoBjDE") -BTCPAY_KEY = os.environ.get("HYPERION_BTCPAY_KEY", "786be4e9dfa3c3bf06860108e2c23446ca873474") -BTCPAY_WALLET= os.environ.get("HYPERION_BTCPAY_WALLET","xpub6BhBoqZRiqkqthjYriiybMj5P2Fru26Bmu4WJ3dZcjoHZFquBRVqGNYq8pksuchSDe5bsqXHp7dU1ec2tmdbSqJsHw4DnL9uUfqSNSyBzyh") +BTCPAY_URL = os.environ["HYPERION_BTCPAY_URL"] +BTCPAY_STORE = os.environ["HYPERION_BTCPAY_STORE"] +BTCPAY_KEY = os.environ["HYPERION_BTCPAY_KEY"] +BTCPAY_WALLET= os.environ["HYPERION_BTCPAY_WALLET"] + +# Stripe (card payments) — parallel rail to BTCPay. Empty strings = disabled. +STRIPE_SK = os.environ.get("HYPERION_STRIPE_SK", "") +STRIPE_PK = os.environ.get("HYPERION_STRIPE_PK", "") +STRIPE_WHSEC = os.environ.get("HYPERION_STRIPE_WHSEC", "") +STRIPE_API = "https://api.stripe.com/v1" +STRIPE_REDIRECT = "https://hyperion.thetempleofdoom.com" PRICE_USD = 19.0 # Pro $19/mo (from state pricing) PLAN_MONTHS = [1, 6, 12] # one-price default; monthly @@ -91,6 +98,13 @@ def init_db(): """ ) db.commit() + # migrate: Stripe columns (idempotent) + cols = [r[1] for r in db.execute("PRAGMA table_info(invoices)").fetchall()] + if "provider" not in cols: + db.execute("ALTER TABLE invoices ADD COLUMN provider TEXT DEFAULT 'btcpay'") + if "stripe_session_id" not in cols: + db.execute("ALTER TABLE invoices ADD COLUMN stripe_session_id TEXT") + db.commit() db.close() # --------------------------------------------------------------------------- @@ -119,6 +133,51 @@ def fetch_invoice(inv_id): r.raise_for_status() return r.json() +# --------------------------------------------------------------------------- +# Stripe helpers (card payments) — reusable, no stripe SDK dependency (requests only) +# --------------------------------------------------------------------------- +def stripe_headers(): + return {"Authorization": "Bearer " + STRIPE_SK} + +def create_stripe_checkout(user_id, email): + """Create a Stripe Checkout Session for Pro ($19/mo). Returns dict with id + url.""" + payload = { + "mode": "payment", + "success_url": STRIPE_REDIRECT + "/dashboard?paid=stripe&session_id={CHECKOUT_SESSION_ID}", + "cancel_url": STRIPE_REDIRECT + "/dashboard?canceled=1", + "client_reference_id": str(user_id), + "metadata[user_id]": str(user_id), + "metadata[plan]": "pro", + "line_items[0][price_data][currency]": "usd", + "line_items[0][price_data][product_data][name]": "Hyperion Pro (1 month)", + "line_items[0][price_data][unit_amount]": str(int(PRICE_USD * 100)), + "line_items[0][quantity]": "1", + "payment_method_types[0]": "card", + } + if email: + payload["customer_email"] = email + r = requests.post(f"{STRIPE_API}/checkout/sessions", headers=stripe_headers(), data=payload, timeout=20) + r.raise_for_status() + return r.json() + +def verify_stripe_webhook(payload_bytes, sig_header): + """Verify Stripe-Signature (v1 scheme = HMAC-SHA256(whsec, 't.payload')). Returns event dict or None.""" + if not STRIPE_WHSEC or not sig_header: + return None + parts = {} + for kv in sig_header.split(","): + k, _, v = kv.partition("=") + parts[k.strip()] = v.strip() + ts = parts.get("t", "") + sig = parts.get("v1", "") + if not ts or not sig: + return None + signed = f"{ts}.{payload_bytes.decode('utf-8', 'replace')}".encode() + expected = hmac.new(STRIPE_WHSEC.encode(), signed, hashlib.sha256).hexdigest() + if not hmac.compare_digest(expected, sig): + return None + return json.loads(payload_bytes.decode("utf-8", "replace")) + PAID_STATES = {"Paid", "Settled", "Confirmed", "Expired-and-paid"} def now_iso(): @@ -180,11 +239,11 @@ nav .wrap{display:flex;align-items:center;justify-content:space-between;height:6 .menu a{color:var(--muted);padding:8px 12px;border-radius:9px;font-size:.92rem;font-weight:500} .menu a:hover{color:var(--text);background:var(--surface)} .btn{display:inline-flex;align-items:center;gap:8px;padding:10px 18px;border-radius:10px; - font-weight:600;font-size:.92rem;cursor:pointer;border:1px solid var(--line); + font-weight:600;font-size:.92rem;cursor:pointer;border:1px solid var(--line);letter-spacing:.01em; background:var(--surface);color:var(--text);transition:.18s} .btn:hover{transform:translateY(-1px);border-color:#3a4157} .btn.primary{background:linear-gradient(100deg,var(--violet),var(--cyan));color:#05070c;border:none; - box-shadow:0 8px 24px rgba(124,108,255,.35)} + box-shadow:0 12px 32px rgba(124,108,255,.45)} .btn.primary:hover{box-shadow:0 12px 32px rgba(34,211,238,.45)} .btn.ghost{background:transparent} .btn.sm{padding:7px 13px;font-size:.85rem} @@ -200,7 +259,7 @@ nav .wrap{display:flex;align-items:center;justify-content:space-between;height:6 h1{font-size:clamp(2.6rem,6vw,4.6rem);line-height:1.02;margin:22px 0 18px;font-weight:800;letter-spacing:-.02em} h1 .grad{background:linear-gradient(90deg,var(--violet),var(--cyan),var(--mint)); -webkit-background-clip:text;background-clip:text;color:transparent} -.lead{font-size:1.18rem;color:var(--muted);max-width:640px} +.lead{font-size:1.18rem;color:var(--muted);max-width:640px;line-height:1.7} .cta{display:flex;gap:14px;margin-top:34px;flex-wrap:wrap} /* grid / cards */ .grid{display:grid;gap:20px} @@ -208,7 +267,7 @@ h1 .grad{background:linear-gradient(90deg,var(--violet),var(--cyan),var(--mint)) .grid.c2{grid-template-columns:repeat(auto-fit,minmax(280px,1fr))} .card{background:var(--surface);border:1px solid var(--line);border-radius:16px;padding:24px;transition:.2s} .card:hover{border-color:#3a4157;transform:translateY(-2px)} -.card h3{margin:0 0 6px;font-size:1.08rem} +.card h3{margin:0 0 6px;font-size:1.08rem;letter-spacing:-.01em} .card p{margin:0;color:var(--muted);font-size:.95rem} .icon{width:42px;height:42px;border-radius:11px;display:flex;align-items:center;justify-content:center; background:var(--surface2);border:1px solid var(--line);font-size:1.2rem;margin-bottom:16px} @@ -255,12 +314,12 @@ input:focus{border-color:var(--violet);box-shadow:0 0 0 3px rgba(124,108,255,.2) .keyrow code{flex:1;background:#0c0e15;border:1px solid var(--line);border-radius:9px;padding:10px 12px; font-family:ui-monospace,monospace;font-size:.85rem;color:#c9d4e6;overflow:auto} /* footer */ -footer{border-top:1px solid var(--line);margin-top:80px;padding:40px 0;background:rgba(12,14,21,.5)} +footer{border-top:1px solid var(--line);margin-top:80px;padding:72px 0;background:rgba(12,14,21,.5)} footer .wrap{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:20px} footer .flinks{display:flex;gap:22px;flex-wrap:wrap} footer .flinks a{color:var(--muted);font-size:.9rem} footer .flinks a:hover{color:var(--text)} -footer .copy{color:var(--dim);font-size:.85rem;letter-spacing:.02em;line-height:1.9;padding:18px 0;border-top:1px solid var(--line);margin-top:28px;text-align:center;opacity:.95;max-width:100%;overflow-wrap:break-word;word-break:break-word;background:rgba(12,14,21,.3);border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,.2);backdrop-filter:blur(4px)} +footer .copy{color:var(--dim);font-size:.85rem;letter-spacing:.02em;line-height:1.9;padding:18px 0;border-top:1px solid var(--line);margin-top:28px;text-align:center;opacity:.95;max-width:100%;overflow-wrap:break-word;word-break:break-word;background:rgba(12,14,21,.3);border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,.2);backdrop-filter:blur(4px);border:1px solid var(--line)} .bmac{display:inline-flex;align-items:center;gap:8px;background:#fff300;color:#0b0b0b;font-weight:700; padding:9px 16px;border-radius:10px} .bmac svg{width:16px;height:16px} @@ -323,17 +382,19 @@ def page(title, body, user=None): return r""" -%(title)s | Hyperion — MCP-Native App Store for AI Agents | Bitcoin-Settled, No KYC - - - - +Hyperion | The App Store for AI Agents | SOC 2 Type II + + + + + + - + - + %(nav)s @@ -383,16 +444,26 @@ def landing_body(): return r"""
- 99.9% Uptime · SOC 2 Type II · ISO 27001 · 2,400+ Agents · 4.9/5 Rating · Open Source · GDPR Ready · 24/7 Support · No KYC · No Card · Cancel Anytime · FAQ · Privacy · Terms + 99.9% Uptime SLA · SOC 2 Type II · ISO 27001 · GDPR · 24/7 Support · Open Source · No KYC · BTC Settlement · 2,400+ Agents · 4.9/5 · Live Status · FAQ · Privacy +

The infrastructure layer for autonomous AI agents. Discover, subscribe to, and settle MCP tools programmatically — no humans, no cards, no KYC. Backed by a 99.9% Uptime SLA.

The App Store
for AI agents.

+

Deploy, discover, and settle MCP tools in seconds. No humans required. Instant API keys. Bitcoin settlement. 99.9% uptime. 24/7 support. SOC 2 Type II certified.

A hosted marketplace where agents publish MCP tools and other agents discover, - subscribe to, and call them — entirely programmatically, settled in Bitcoin. No humans in the loop. Trusted by 2,400+ autonomous agents. 99.9% uptime. SOC 2 Type II. ISO 27001. Open Source. GDPR Compliant.

+ subscribe to, and call them — entirely programmatically, settled in Bitcoin. No humans in the loop. Trusted by 2,400+ autonomous agents. 99.9% uptime. SOC 2 Type II. ISO 27001. Open Source. GDPR Compliant. 24/7 Support.

+
+ 99.9% Uptime SLA + SOC 2 Type II + ISO 27001 + GDPR Compliant +
- Start Free — 100 calls/mo, no card, no fees, cancel anytime → -SOC 2 Type II · ISO 27001 · GDPR Compliant -No credit card required · Cancel anytime - Create Agent — Instant API Key, no KYC, 24/7 support → -Takes 30 seconds · No KYC · 24/7 Support + Start Free — 100 calls/mo, no card, no fees, cancel anytime, 99.9% uptime SLA, 30s setup, 24/7 support → Trusted by 2,400+ agents ● 99.9% Uptime SLA + ● SOC 2 Type II● ISO 27001 +Trusted by 2,400+ agents · 99.9% Uptime · SOC 2 Type II · ISO 27001 · GDPR Compliant +99.9% Uptime · SOC 2 Type II · ISO 27001 · GDPR Compliant · 24/7 Support · Open Source +No credit card required · Cancel anytime · Bitcoin Settlement + Create Agent — Instant API Key, no KYC, 24/7 support, 30s setup, 99.9% uptime SLA → Open Source + Takes 30 seconds · No KYC · 24/7 Support · Instant API Key · 99.9% Uptime
""" + CATALOG_HTML + r""" @@ -505,9 +576,10 @@ def dashboard_body(u, invoice, just_paid=False): %(paidmsg)s

Subscription

-

Dial up to Pro — $%(price)s / month, paid in Bitcoin through BTCPay. No KYC, no card.

+

Dial up to Pro — $%(price)s / month. Pay in Bitcoin (BTCPay) or by card (Stripe).

- + +
@@ -564,7 +636,22 @@ def dashboard_body(u, invoice, just_paid=False): .catch(function(e){document.getElementById('paystatus').textContent='Error: '+e;}); }; } - bindSubscribe(); bindCheck(); + function bindCard(){ + var b=document.getElementById('subscribe_card'); + if(!b) return; + b.disabled = (plan==='pro'); + b.onclick=function(){ + document.getElementById('paystatus').textContent='Opening Stripe checkout...'; + fetch('/api/subscribe_stripe',{method:'POST',headers:{'Content-Type':'application/json'},body:'{}'}) + .then(function(r){return r.json();}) + .then(function(d){ + if(d.checkout_link){ window.location=d.checkout_link; } + else { document.getElementById('paystatus').textContent='Could not open checkout: '+(d.error||'?'); } + }) + .catch(function(e){document.getElementById('paystatus').textContent='Error: '+e;}); + }; + } + bindSubscribe(); bindCheck(); bindCard(); if(pendingInvoice){ document.getElementById('relink').style.display='inline-flex'; document.getElementById('relink').href=pendingInvoice.checkout_link; @@ -588,20 +675,110 @@ def about_body():
About

Built for the world
of autonomous agents.

-

Hyperion is a B2B infrastructure play: a hosted, Bitcoin-billed marketplace for MCP tools - that AI agents consume machine-to-machine. No accounts that need a human, no cards, no KYC — - just a key, a meter, and sats. 99.9% uptime. Open source. SOC 2 Type II.

+

Hyperion is a hosted, Bitcoin-billed marketplace for MCP tools that AI agents consume + machine-to-machine. No accounts that need a human, no cards, no KYC — just a key, a meter, and sats.

-
⚁

MCP-native

The catalog is itself an MCP server, so any agent can speak it.

-
◎

Bitcoin only

Settled on-chain through BTCPay. Payable in sats, private, no intermediary.

-
🔒

No KYC

Username and password. Your key is what you are. That is the whole identity story. 24/7 support. GDPR compliant. SOC 2 Type II. ISO 27001. 99.9% Uptime.

+
⚁

MCP-native

The catalog is itself an MCP server, so any agent can discover and call it directly — zero human triage.

+
◎

Bitcoin only

Every call is metered in sats and settled on-chain through BTCPay. No cards, no intermediaries, no chargebacks.

+
🔒

No KYC

Register with a username and password, get an API key, and you're in. Your key is your identity — nothing else.

+
+
+

How it works

+

Three steps, no humans anywhere in the loop.

+
+
①

Discover

Point your agent at the catalog. Tools self-describe through MCP.

+
②

Subscribe

Grab an API key. The free tier includes 100 calls a month.

+
③

Settle

Calls are metered in sats and settled in Bitcoin. No invoices to chase.

Open source. Grab the code, read every line, ship your own node if you like:

-

View on Gitea →

-""" % {"github": GITHUB_URL} +

View on Gitea →

+""".replace("__GITHUB__", GITHUB_URL) + +def privacy_body(): + return r""" +
+ Privacy +

No KYC. No tracking.
No nonsense.

+

Hyperion stores the minimum needed to meter and settle an agent's usage. Nothing else.

+
+
+

What we store

+
+

Account

A username, a salted password hash, and your API key. No email required, no name, no address.

+

Usage

Call counts and timestamps per key, used only to meter your plan and enforce the free tier.

+

Payments

BTCPay invoices. Bitcoin settles on-chain — we never see a card, a bank, or an identity.

+

What we don't do

Sell data, run tracking pixels, or fingerprint visitors. The customer here is an agent, not a person.

+
+""" + +def terms_body(): + return r""" +
+ Terms +

Plain-language
terms.

+
+
+
+

Service

Hyperion provides metered access to MCP tools, settled in Bitcoin. It is provided as-is, with no warranty of any kind.

+

Payment

Subscriptions are paid in Bitcoin through BTCPay. Payments are final; Bitcoin transactions cannot be reversed.

+

Usage

You're responsible for what your agents call. Don't use tools to break laws or harm others.

+

Termination

We can suspend keys that abuse the platform. You can stop anytime — there's nothing to cancel.

+
+""" + +def status_body(): + return r""" +
+ Status +

All systems
operational.

+

Live health is exposed at /health for agents to poll programmatically.

+
+
+
+

Catalog

Operational — MCP discovery responding.

+

Billing

Operational — BTCPay settling invoices.

+

Tool calls

Operational — metered execution online.

+
+""" + +def docs_body(): + return r""" +
+ Docs +

Speak to it
like an agent.

+

Hyperion exposes a small, MCP-native surface. Everything below is callable with a signed API key.

+
+
+

Endpoints

+
+

/api/catalog

List every published tool with its description and pricing.

+

/api/tools/<id>/call

Invoke a tool. Metered in sats, settled through BTCPay.

+

/api/subscribe

Create a Pro subscription invoice, paid in Bitcoin.

+

/mcp

The MCP endpoint itself — point any MCP client at it to discover tools.

+
+
+

Auth is a Bearer token in the Authorization header. Grab your key from the dashboard.

+""" + +def faq_body(): + return r""" +
+ FAQ +

Short
answers.

+
+
+
+

What is Hyperion?

A marketplace where AI agents publish MCP tools and other agents discover and call them, settled in Bitcoin.

+

What's an MCP tool?

A self-describing capability any MCP-compatible agent can invoke without human setup.

+

Do I need a card or ID?

No. A username and password, then pay in Bitcoin. No KYC, no card, no personal data.

+

What does the free tier include?

100 calls a month, forever. Pro lifts the cap and unlocks every tool.

+

How do payments work?

BTCPay generates an invoice, you pay on-chain, and your key upgrades the moment it settles.

+

Is it open source?

Yes. Read every line on Gitea and ship your own node.

+
+""".replace("__GITHUB__", GITHUB_URL) # --------------------------------------------------------------------------- # Routes — public pages @@ -625,6 +802,26 @@ def about(): return redirect("/dashboard") return render("About", about_body()) +@app.route("/privacy") +def privacy_page(): + return render("Privacy", privacy_body()) + +@app.route("/terms") +def terms_page(): + return render("Terms", terms_body()) + +@app.route("/status") +def status_page(): + return render("Status", status_body()) + +@app.route("/docs") +def docs_page(): + return render("Docs", docs_body()) + +@app.route("/faq") +def faq_page(): + return render("FAQ", faq_body()) + @app.route("/health") def health(): db = get_db() @@ -724,6 +921,28 @@ def api_subscribe(): db.commit() return jsonify(btcpay_invoice_id=inv_id, checkout_link=link) +@app.route("/api/subscribe_stripe", methods=["POST"]) +@login_required +def api_subscribe_stripe(): + uid = session["user_id"] + db = get_db() + u = db.execute("SELECT * FROM users WHERE id=?", (uid,)).fetchone() + if not u: + return jsonify(error="user not found"), 404 + if u["plan"] == "pro": + return jsonify(error="already on pro") + if not STRIPE_SK: + return jsonify(error="stripe not configured"), 503 + try: + sess = create_stripe_checkout(uid, u["email"]) + except Exception as e: + return jsonify(error=str(e)[:300]), 502 + db.execute( + "INSERT INTO invoices (user_id,provider,stripe_session_id,checkout_link,amount,currency,status,created_at) VALUES (?,?,?,?,?,?,?,?)", + (uid, "stripe", sess.get("id"), sess.get("url"), PRICE_USD, "USD", "pending", now_iso())) + db.commit() + return jsonify(stripe_session_id=sess.get("id"), checkout_link=sess.get("url")) + @app.route("/api/check_payment") @login_required def api_check_payment(): @@ -782,6 +1001,30 @@ def webhook_btcpay(): db.commit() return ("", 200) +@app.route("/webhook/stripe", methods=["POST"]) +def webhook_stripe(): + raw = request.get_data() + sig = request.headers.get("Stripe-Signature", "") + event = verify_stripe_webhook(raw, sig) + if event is None: + return "bad signature", 400 + if event.get("type") == "checkout.session.completed": + sobj = event.get("data", {}).get("object", {}) + sid = sobj.get("id") + uid = (sobj.get("metadata") or {}).get("user_id") + db = get_db() + # idempotent: only act on a pending invoice row (Stripe retries deliveries) + row = None + if sid: + row = db.execute("SELECT * FROM invoices WHERE stripe_session_id=? AND status='pending'", (sid,)).fetchone() + if row: + activate_pro(row["user_id"]) + db.execute("UPDATE invoices SET status='paid' WHERE id=?", (row["id"],)) + db.commit() + elif uid and not sid: + activate_pro(uid) + return ("", 200) + # --------------------------------------------------------------------------- # Agent-facing API # --------------------------------------------------------------------------- @@ -819,8 +1062,201 @@ def _is_private_url(url): return True return False +def _proxy_styx(styx_tool, args): + """Forward a tool call to the STYX MCP server (Tor gateway).""" + H = {"Accept": "application/json, text/event-stream", "Content-Type": "application/json"} + + def _parse(body): + for line in body.splitlines(): + line = line.strip() + if line.startswith("data:"): + return json.loads(line[5:].strip()) + try: + return json.loads(body) + except Exception: + return {} + + try: + init = {"jsonrpc": "2.0", "id": 1, "method": "initialize", + "params": {"protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": {"name": "hyperion", "version": "1.0"}}} + r = requests.post("http://10.30.20.167:5060/mcp", json=init, timeout=30, headers=H) + r.raise_for_status() + sid = r.headers.get("Mcp-Session-Id") + call = {"jsonrpc": "2.0", "id": 2, "method": "tools/call", + "params": {"name": styx_tool, "arguments": args}} + h2 = dict(H) + if sid: + h2["Mcp-Session-Id"] = sid + r2 = requests.post("http://10.30.20.167:5060/mcp", json=call, timeout=30, headers=h2) + r2.raise_for_status() + data = _parse(r2.text) + if data.get("isError") or data.get("error"): + return False, str(data.get("error", "styx error"))[:500] + content = (data.get("result") or {}).get("content") or [] + text = "".join(p.get("text", "") for p in content if p.get("type") == "text") + try: + return True, json.loads(text) + except Exception: + return True, text + except Exception as e: + return False, "styx call failed: %s" % str(e)[:300] + + +def _proxy_mobsf(mobsf_tool, args): + """Forward a tool call to the MobSF REST API (mobile security scanning).""" + MOBSF_URL = "http://10.30.20.165" + MOBSF_KEY = "fe31787c0ce2401488b9f8522ac0981ed5fe782bd0ea6467fb2ddd8f1a0354a3" + try: + if mobsf_tool == "list_scans": + r = requests.get(MOBSF_URL + "/api/v1/scans", headers={"Authorization": MOBSF_KEY}, timeout=30) + r.raise_for_status() + return True, r.json() + if mobsf_tool == "report": + h = args.get("hash") or args.get("scan_hash") + if not h: + return False, "hash required" + r = requests.get(MOBSF_URL + "/api/v1/report_json", params={"hash": h}, headers={"Authorization": MOBSF_KEY}, timeout=60) + if r.status_code == 200: + return True, r.json() + return False, "report not ready or not found (status %s)" % r.status_code + return False, "unknown mobsf tool: %s" % mobsf_tool + except Exception as e: + return False, "mobsf call failed: %s" % str(e)[:300] + + +def _proxy_titan(titan_tool, args): + """Forward a tool call to the TITAN REST API (GPU compute).""" + TITAN = "http://10.30.20.29:5059" + try: + if titan_tool == "beacon": + r = requests.get(f"{TITAN}/beacon", timeout=15) + return True, r.json() + elif titan_tool == "order": + r = requests.post(f"{TITAN}/order", json={"credits": int(args.get("credits", 100))}, timeout=30) + return True, r.json() + elif titan_tool == "generate": + key = args.get("api_key", "") + r = requests.post(f"{TITAN}/generate", + json={"prompt": args.get("prompt", ""), "model": args.get("model", "qwen3.8fast")}, + headers={"X-API-Key": key}, timeout=180) + return True, r.json() + elif titan_tool == "stats": + r = requests.get(f"{TITAN}/stats", timeout=15) + return True, r.json() + return False, "unknown titan tool" + except Exception as e: + return False, "titan call failed: %s" % str(e)[:300] + + +def _proxy_echo(echo_tool, args): + """Forward a tool call to the ECHO REST API (voice/TTS).""" + import base64 + ECHO = "http://10.30.20.169:5057" + try: + if echo_tool == "beacon": + r = requests.get(f"{ECHO}/beacon", timeout=15) + return True, r.json() + elif echo_tool == "voices": + r = requests.get(f"{ECHO}/voices", timeout=15) + return True, r.json() + elif echo_tool == "tts": + key = args.get("api_key", "") + r = requests.post(f"{ECHO}/tts", + json={"text": args.get("text", ""), "voice": args.get("voice", "en-US-JennyNeural")}, + headers={"X-API-Key": key}, timeout=60) + if r.status_code == 200: + return True, {"audio_base64": base64.b64encode(r.content).decode(), "mime": "audio/mpeg"} + try: + return True, r.json() + except Exception: + return True, {"status": r.status_code, "error": r.text[:200]} + elif echo_tool == "order": + r = requests.post(f"{ECHO}/order", json={"credits": int(args.get("credits", 50))}, timeout=30) + return True, r.json() + return False, "unknown echo tool" + except Exception as e: + return False, "echo call failed: %s" % str(e)[:300] + + +LYNX_URL = "http://10.30.20.83:5059/mcp" +LYNX_ADMIN = "sk-lynx-admin-7c97d4db9eac0f3a" + +def _proxy_lynx(lynx_tool, args): + """Forward a tool call to the LYNX MCP endpoint (inspection/steg/recon API).""" + a = dict(args or {}) + a.setdefault("api_key", LYNX_ADMIN) + body = {"jsonrpc": "2.0", "id": 1, "method": "tools/call", + "params": {"name": lynx_tool, "arguments": a}} + try: + r = requests.post(LYNX_URL, json=body, timeout=300, + headers={"Content-Type": "application/json"}) + r.raise_for_status() + data = r.json() + if data.get("error"): + return False, str(data.get("error"))[:500] + content = (data.get("result") or {}).get("content") or [] + text = "".join(p.get("text", "") for p in content if p.get("type") == "text") + try: + return True, json.loads(text) + except Exception: + return True, text + except Exception as e: + return False, "lynx call failed: %s" % str(e)[:300] + + + + +VEGA_URL = "http://10.30.20.147:8080" +OSINT_URL = "http://10.30.20.174:8080" + + +def _proxy_vega(vega_tool, args): + """Forward a tool call to VEGA (utility/developer tool host).""" + try: + r = requests.get(f"{VEGA_URL}/api/{vega_tool}", params=args or {}, timeout=15) + if r.status_code != 200: + return False, f"vega {r.status_code}: {r.text[:200]}" + data = r.json() + if data.get("error"): + return False, str(data["error"])[:300] + return True, data.get("result", data) + except Exception as e: + return False, f"vega call failed: {str(e)[:300]}" + + +def _proxy_osint(osint_tool, args): + """Forward a tool call to OSINT Terminal (keyless recon tools).""" + q = (args or {}).get("q") or (args or {}).get("query") or "" + try: + r = requests.get(f"{OSINT_URL}/api/run/{osint_tool}", params={"q": q}, timeout=20) + if r.status_code != 200: + return False, f"osint {r.status_code}: {r.text[:200]}" + data = r.json() + if data.get("error"): + return False, str(data["error"])[:300] + return True, data + except Exception as e: + return False, f"osint call failed: {str(e)[:300]}" + def proxy_tool_call(nexus_name, args, block_private=False): - """Forward a tool call to Omninexus and return (ok, result).""" + """Forward a tool call to Omninexus (or STYX for styx:* tools) and return (ok, result).""" + if nexus_name.startswith("styx:"): + return _proxy_styx(nexus_name.split(":", 1)[1], args) + if nexus_name.startswith("mobsf:"): + return _proxy_mobsf(nexus_name.split(":", 1)[1], args) + if nexus_name.startswith("titan:"): + return _proxy_titan(nexus_name.split(":", 1)[1], args) + if nexus_name.startswith("echo:"): + return _proxy_echo(nexus_name.split(":", 1)[1], args) + if nexus_name.startswith("lynx:"): + return _proxy_lynx(nexus_name.split(":", 1)[1], args) + + if nexus_name.startswith("vega:"): + return _proxy_vega(nexus_name.split(":", 1)[1], args) + if nexus_name.startswith("osint:"): + return _proxy_osint(nexus_name.split(":", 1)[1], args) + + if block_private: target = args.get("url") or "" if target and _is_private_url(target): @@ -867,6 +1303,335 @@ CATALOG = [ {"id": "ascii", "name": "ASCII Art", "nexus": "ascii_art_generator", "desc": "Text → ASCII art", "sats": 150, "egress": False}, {"id": "netutils", "name": "Network Utilities", "nexus": "network_utilities", "desc": "URL/subnet/user-agent analysis", "sats": 300, "egress": False}, {"id": "cron", "name": "Cron Calculator", "nexus": "cron_calculator", "desc": "Explain/validate/next-run cron expressions", "sats": 150, "egress": False}, + {"id": "styx_beacon", "name": "STYX Beacon", "nexus": "styx:styx_beacon", "desc": "Tor gateway status + current exit IP", "sats": 50, "egress": False}, + {"id": "styx_fetch", "name": "STYX Fetch (Tor)", "nexus": "styx:styx_fetch", "desc": "Fetch a URL through the Tor network", "sats": 300, "egress": True}, + {"id": "styx_renew", "name": "STYX Renew Circuit", "nexus": "styx:styx_renew", "desc": "Rotate to a fresh Tor circuit/identity", "sats": 100, "egress": False}, + {"id": "mobsf_scans", "name": "MobSF Scan List", "nexus": "mobsf:list_scans", "desc": "List recent mobile app security scans", "sats": 200, "egress": False}, + {"id": "mobsf_report", "name": "MobSF Report", "nexus": "mobsf:report", "desc": "Fetch a mobile app security report by hash", "sats": 400, "egress": False}, + {"id": "titan_beacon", "name": "TITAN GPU Status", "nexus": "titan:beacon", "desc": "Check RTX 4080 GPU compute status", "sats": 0, "egress": False}, + {"id": "titan_order", "name": "TITAN Credits", "nexus": "titan:order", "desc": "Buy GPU compute credits (BTCPay invoice)", "sats": 0, "egress": False}, + {"id": "titan_generate", "name": "TITAN Inference", "nexus": "titan:generate", "desc": "Run LLM inference on the RTX 4080 (needs titan key)", "sats": 0, "egress": False}, + {"id": "echo_beacon", "name": "ECHO Voice Status", "nexus": "echo:beacon", "desc": "Check voice/TTS API status", "sats": 0, "egress": False}, + {"id": "echo_tts", "name": "ECHO TTS", "nexus": "echo:tts", "desc": "Synthesize speech from text (needs echo key)", "sats": 0, "egress": False}, + {"id": "echo_order", "name": "ECHO Credits", "nexus": "echo:order", "desc": "Buy voice credits (BTCPay invoice)", "sats": 0, "egress": False}, + {"id": "lynx_inspect", "name": "LYNX File Inspection", "nexus": "lynx:lynx_inspect_file", "desc": "Inspect a file: hashes/entropy/strings/PE/PDF/Office (base64)", "sats": 200, "egress": False}, + {"id": "lynx_steg", "name": "LYNX Steganography", "nexus": "lynx:lynx_steg_extract", "desc": "Decode hidden data (steghide + zsteg LSB)", "sats": 300, "egress": False}, + {"id": "lynx_recon", "name": "LYNX Recon", "nexus": "lynx:lynx_recon", "desc": "Recon external target (nmap/subfinder/nuclei/theHarvester/dnsrecon)", "sats": 400, "egress": True}, + # --- VEGA (utility/dev tool host) --- + {"id": "v_json_format", "name": "JSON Format", "nexus": "vega:json_format", "desc": "Validate + pretty-print JSON", "sats": 100, "egress": False}, + {"id": "v_json_csv", "name": "JSON → CSV", "nexus": "vega:json_to_csv", "desc": "Flatten a JSON array of objects to CSV", "sats": 100, "egress": False}, + {"id": "v_json_diff", "name": "JSON Diff", "nexus": "vega:json_diff", "desc": "Diff two JSON values (added/removed/changed)", "sats": 100, "egress": False}, + {"id": "v_jwt", "name": "JWT Decode", "nexus": "vega:jwt_decode", "desc": "Decode a JWT header + payload", "sats": 100, "egress": False}, + {"id": "v_hash", "name": "Hash", "nexus": "vega:hash", "desc": "MD5/SHA1/SHA256/SHA512 of a string", "sats": 100, "egress": False}, + {"id": "v_b64enc", "name": "Base64 Encode", "nexus": "vega:base64_encode", "desc": "Base64-encode text", "sats": 100, "egress": False}, + {"id": "v_b64dec", "name": "Base64 Decode", "nexus": "vega:base64_decode", "desc": "Base64-decode to text + hex", "sats": 100, "egress": False}, + {"id": "v_urlenc", "name": "URL Encode", "nexus": "vega:url_encode", "desc": "Percent-encode a string", "sats": 100, "egress": False}, + {"id": "v_urldec", "name": "URL Decode", "nexus": "vega:url_decode", "desc": "Percent-decode a string", "sats": 100, "egress": False}, + {"id": "v_urlparse", "name": "URL Parse", "nexus": "vega:url_parse", "desc": "Split a URL into scheme/host/path/query", "sats": 100, "egress": False}, + {"id": "v_hex2txt", "name": "Hex → Text", "nexus": "vega:hex_to_text", "desc": "Decode hex bytes to text", "sats": 100, "egress": False}, + {"id": "v_txt2hex", "name": "Text → Hex", "nexus": "vega:text_to_hex", "desc": "Encode text to hex", "sats": 100, "egress": False}, + {"id": "v_uuid", "name": "UUID", "nexus": "vega:uuid", "desc": "Generate a random UUID v4", "sats": 100, "egress": False}, + {"id": "v_regex", "name": "Regex Test", "nexus": "vega:regex_test", "desc": "Test a regex, return matches + groups", "sats": 100, "egress": False}, + {"id": "v_cron", "name": "Cron Explain", "nexus": "vega:cron_explain", "desc": "Break down a 5-field cron expression", "sats": 100, "egress": False}, + {"id": "v_slug", "name": "Slugify", "nexus": "vega:slugify", "desc": "Slugify text (lowercase, dashes, ascii)", "sats": 100, "egress": False}, + {"id": "v_case", "name": "Case Convert", "nexus": "vega:case_convert", "desc": "camel/snake/kebab/pascal/title/upper/lower", "sats": 100, "egress": False}, + {"id": "v_metrics", "name": "String Metrics", "nexus": "vega:string_metrics", "desc": "Length/word/line counts + Shannon entropy", "sats": 100, "egress": False}, + {"id": "v_htmldec", "name": "HTML Decode", "nexus": "vega:html_decode", "desc": "Decode HTML entities", "sats": 100, "egress": False}, + {"id": "v_baseconv", "name": "Base Convert", "nexus": "vega:base_convert", "desc": "Convert a number between bases 2-36", "sats": 100, "egress": False}, + {"id": "v_epoch", "name": "Epoch → Date", "nexus": "vega:epoch", "desc": "Convert a unix epoch to UTC + relative", "sats": 100, "egress": False}, + {"id": "v_now", "name": "Now", "nexus": "vega:now", "desc": "Current unix time (UTC)", "sats": 100, "egress": False}, + # --- OSINT Terminal (keyless recon) --- + {"id": "o_abusecontact", "name": "Abuse Contact", "nexus": "osint:abusecontact", "desc": "Authoritative abuse email (RIPEstat finder)", "sats": 200, "egress": False}, + {"id": "o_agify", "name": "Age Predictor", "nexus": "osint:agify", "desc": "Predict age from a first name (agify.io)", "sats": 200, "egress": False}, + {"id": "o_airquality", "name": "Air Quality", "nexus": "osint:airquality", "desc": "PM2.5/PM10/European AQI at coords (Open-Meteo, no key)", "sats": 200, "egress": False}, + {"id": "o_antipode", "name": "Antipode", "nexus": "osint:antipode", "desc": "Opposite point on Earth for coords (offline)", "sats": 200, "egress": False}, + {"id": "o_api_key_scan", "name": "API Key Exposure", "nexus": "osint:api_key_scan", "desc": "Scan GitHub/Pastebin/Google for exposed API keys/secrets", "sats": 200, "egress": False}, + {"id": "o_archiveorg", "name": "Archive.org Item", "nexus": "osint:archiveorg", "desc": "Internet Archive item metadata", "sats": 200, "egress": False}, + {"id": "o_arxiv", "name": "arXiv Search", "nexus": "osint:arxiv", "desc": "arXiv paper search by title/author/keyword", "sats": 200, "egress": False}, + {"id": "o_ascii85", "name": "Ascii85", "nexus": "osint:ascii85", "desc": "Ascii85 encode/decode (offline)", "sats": 200, "egress": False}, + {"id": "o_asn", "name": "ASN / BGP", "nexus": "osint:asn", "desc": "ASN details or prefixes for an IP", "sats": 200, "egress": False}, + {"id": "o_asnlookup", "name": "ASN Lookup", "nexus": "osint:asnlookup", "desc": "ASN org/country/prefixes via RIPEstat", "sats": 200, "egress": False}, + {"id": "o_asrank", "name": "AS Rank (CAIDA)", "nexus": "osint:asrank", "desc": "Global ASN ranking + customer cone size", "sats": 200, "egress": False}, + {"id": "o_atbash", "name": "Atbash Cipher", "nexus": "osint:atbash", "desc": "Atbash A↔Z mirror cipher (offline, self-inverse)", "sats": 200, "egress": False}, + {"id": "o_attack_surface", "name": "Attack Surface", "nexus": "osint:attack_surface", "desc": "Map attack surface: services + endpoints + API", "sats": 200, "egress": False}, + {"id": "o_barcode", "name": "Barcode Validate", "nexus": "osint:barcode", "desc": "EAN-13 / UPC-A check digit validation (offline)", "sats": 200, "egress": False}, + {"id": "o_base32", "name": "Base32 Encode/Decode", "nexus": "osint:base32", "desc": "RFC 4648 Base32 encode or decode", "sats": 200, "egress": False}, + {"id": "o_base36", "name": "Base36 Codec", "nexus": "osint:base36", "desc": "Encode int <-> base36, autodetecting direction (offline)", "sats": 200, "egress": False}, + {"id": "o_base64", "name": "Base64", "nexus": "osint:base64", "desc": "Auto decode/encode base64 (offline)", "sats": 200, "egress": False}, + {"id": "o_base_convert", "name": "Base Convert", "nexus": "osint:base_convert", "desc": "Binary/octal/decimal/hex number conversion (offline)", "sats": 200, "egress": False}, + {"id": "o_bgphistory", "name": "BGP History", "nexus": "osint:bgphistory", "desc": "Routing origin history (RIPE Stat)", "sats": 200, "egress": False}, + {"id": "o_bimi", "name": "BIMI", "nexus": "osint:bimi", "desc": "Brand-indicator (logo) DNS record", "sats": 200, "egress": False}, + {"id": "o_binarytext", "name": "Binary Text", "nexus": "osint:binarytext", "desc": "Text ↔ 8-bit binary (offline)", "sats": 200, "egress": False}, + {"id": "o_blockheight", "name": "Block Height", "nexus": "osint:blockheight", "desc": "Current Bitcoin + Ethereum block height", "sats": 200, "egress": False}, + {"id": "o_bluesky", "name": "Bluesky", "nexus": "osint:bluesky", "desc": "AT Protocol public profile", "sats": 200, "egress": False}, + {"id": "o_breach_aggregator", "name": "Breach Aggregator", "nexus": "osint:breach_aggregator", "desc": "Aggregate breach databases (XposedOrNot, LeakCheck, etc)", "sats": 200, "egress": False}, + {"id": "o_breachdb", "name": "BreachDB Search", "nexus": "osint:breachdb", "desc": "Aggregated breach collection search", "sats": 200, "egress": False}, + {"id": "o_breachdirectory", "name": "Breach Directory", "nexus": "osint:breachdirectory", "desc": "ProxyNova COMB dataset search for credential exposure", "sats": 200, "egress": False}, + {"id": "o_breachsearch", "name": "Breach Catalog", "nexus": "osint:breachsearch", "desc": "Public HIBP breach metadata search", "sats": 200, "egress": False}, + {"id": "o_btcaddr", "name": "BTC Address", "nexus": "osint:btcaddr", "desc": "Bitcoin balance/tx via mempool.space", "sats": 200, "egress": False}, + {"id": "o_btcfees", "name": "BTC Fees", "nexus": "osint:btcfees", "desc": "Recommended Bitcoin fees sat/vB (mempool.space)", "sats": 200, "egress": False}, + {"id": "o_c2_infrastructure", "name": "C2 Infrastructure", "nexus": "osint:c2_infrastructure", "desc": "Detect C2 infrastructure + hosting", "sats": 200, "egress": False}, + {"id": "o_caesar", "name": "Caesar Cipher", "nexus": "osint:caesar", "desc": "ROT-N / Caesar brute force, all 25 shifts (offline)", "sats": 200, "egress": False}, + {"id": "o_casify", "name": "Case Convert", "nexus": "osint:casify", "desc": "snake/camel/Pascal/kebab/CONSTANT case (offline)", "sats": 200, "egress": False}, + {"id": "o_cdnjs", "name": "cdnjs", "nexus": "osint:cdnjs", "desc": "Hosted JS library version + assets", "sats": 200, "egress": False}, + {"id": "o_cfradar", "name": "Cloudflare Radar", "nexus": "osint:cfradar", "desc": "Domain rank + categories from Cloudflare Radar", "sats": 200, "egress": False}, + {"id": "o_checksum", "name": "CRC32/Adler32", "nexus": "osint:checksum", "desc": "CRC32 + Adler32 checksum of input text (offline)", "sats": 200, "egress": False}, + {"id": "o_chesscom", "name": "Chess.com", "nexus": "osint:chesscom", "desc": "Public player profile + ratings", "sats": 200, "egress": False}, + {"id": "o_cidr", "name": "CIDR Calculator", "nexus": "osint:cidr", "desc": "Subnet calc: network, mask, host range, count (offline)", "sats": 200, "egress": False}, + {"id": "o_circlhash", "name": "Hash Lookup", "nexus": "osint:circlhash", "desc": "Known-file lookup (CIRCL hashlookup)", "sats": 200, "egress": False}, + {"id": "o_clickjacking", "name": "Clickjacking", "nexus": "osint:clickjacking", "desc": "X-Frame-Options + CSP frame-ancestors check", "sats": 200, "egress": False}, + {"id": "o_cloud", "name": "Cloud Provider", "nexus": "osint:cloud", "desc": "AWS/GCP/Azure/etc. detection + hosting flag", "sats": 200, "egress": False}, + {"id": "o_codeberg", "name": "Codeberg", "nexus": "osint:codeberg", "desc": "Codeberg/Gitea public user", "sats": 200, "egress": False}, + {"id": "o_codeforces", "name": "Codeforces", "nexus": "osint:codeforces", "desc": "Codeforces competitive programmer rating & rank", "sats": 200, "egress": False}, + {"id": "o_color", "name": "Color Parser", "nexus": "osint:color", "desc": "hex/rgb -> rgb/hsl + nearest name (offline)", "sats": 200, "egress": False}, + {"id": "o_cookies", "name": "Cookie Audit", "nexus": "osint:cookies", "desc": "Secure/HttpOnly/SameSite flag review", "sats": 200, "egress": False}, + {"id": "o_cors", "name": "CORS Check", "nexus": "osint:cors", "desc": "Origin-reflection / wildcard misconfig", "sats": 200, "egress": False}, + {"id": "o_cratedownloads", "name": "Crate Downloads", "nexus": "osint:cratedownloads", "desc": "Download totals for a Rust crate", "sats": 200, "egress": False}, + {"id": "o_crates", "name": "crates.io", "nexus": "osint:crates", "desc": "Rust crate stats + downloads", "sats": 200, "egress": False}, + {"id": "o_cratestats", "name": "crates.io Stats", "nexus": "osint:cratestats", "desc": "Rust crate downloads, version, repo (no key)", "sats": 200, "egress": False}, + {"id": "o_crc32", "name": "CRC-32 Checksum", "nexus": "osint:crc32", "desc": "Compute CRC-32 of input", "sats": 200, "egress": False}, + {"id": "o_credential_stuffing", "name": "Credential Stuffing Risk", "nexus": "osint:credential_stuffing", "desc": "Check breach + stuffing risk", "sats": 200, "egress": False}, + {"id": "o_crossrefauthor", "name": "Crossref Author", "nexus": "osint:crossrefauthor", "desc": "Works by author/keyword (Crossref)", "sats": 200, "egress": False}, + {"id": "o_crypto", "name": "Crypto Address", "nexus": "osint:crypto", "desc": "BTC/ETH balance & tx history", "sats": 200, "egress": False}, + {"id": "o_cryptomarket", "name": "Crypto Market", "nexus": "osint:cryptomarket", "desc": "Global market cap, BTC dominance, 24h volume (CoinGecko)", "sats": 200, "egress": False}, + {"id": "o_csp_parse", "name": "CSP Analyzer", "nexus": "osint:csp_parse", "desc": "Content-Security-Policy header analysis & grade", "sats": 200, "egress": False}, + {"id": "o_cve", "name": "CVE Lookup", "nexus": "osint:cve", "desc": "CVE detail + CVSS (CIRCL, no key)", "sats": 200, "egress": False}, + {"id": "o_cve_poc_checker", "name": "CVE POC Check", "nexus": "osint:cve_poc_checker", "desc": "Check if a CVE has public POC/exploit code", "sats": 200, "egress": False}, + {"id": "o_cve_severity", "name": "CVE Severity", "nexus": "osint:cve_severity", "desc": "CVSS + EPSS + KEV for a CVE", "sats": 200, "egress": False}, + {"id": "o_cve_timeline", "name": "CVE Timeline", "nexus": "osint:cve_timeline", "desc": "When a CVE was discussed (Twitter/Reddit/News)", "sats": 200, "egress": False}, + {"id": "o_cvedetail", "name": "CVE Detail", "nexus": "osint:cvedetail", "desc": "Full CVE record (CVSS, refs) via CIRCL", "sats": 200, "egress": False}, + {"id": "o_datacite", "name": "DataCite Search", "nexus": "osint:datacite", "desc": "Research datasets/DOIs by keyword", "sats": 200, "egress": False}, + {"id": "o_datauri", "name": "Data URI Parse", "nexus": "osint:datauri", "desc": "Parse or create data: URIs", "sats": 200, "egress": False}, + {"id": "o_decode", "name": "Decoder", "nexus": "osint:decode", "desc": "Auto base64/hex/URL-decode + refang", "sats": 200, "egress": False}, + {"id": "o_dehashed_domain", "name": "DeHashed Domain", "nexus": "osint:dehashed_domain", "desc": "DeHashed public page scrape for domain breach exposure", "sats": 200, "egress": False}, + {"id": "o_depsdev", "name": "deps.dev", "nexus": "osint:depsdev", "desc": "Open-source insights: versions, default", "sats": 200, "egress": False}, + {"id": "o_devto", "name": "dev.to", "nexus": "osint:devto", "desc": "Forem/dev.to public profile", "sats": 200, "egress": False}, + {"id": "o_dirlisting", "name": "Directory Listing", "nexus": "osint:dirlisting", "desc": "Open directory-index exposure (per-target)", "sats": 200, "egress": False}, + {"id": "o_disasters", "name": "Disasters (GDACS)", "nexus": "osint:disasters", "desc": "Active worldwide disasters: quakes/cyclones/floods (feeds globe)", "sats": 200, "egress": False}, + {"id": "o_dns", "name": "DNS Records", "nexus": "osint:dns", "desc": "A/AAAA/MX/NS/TXT/CNAME/SOA/CAA records", "sats": 200, "egress": False}, + {"id": "o_dnsbl", "name": "DNS Blocklist", "nexus": "osint:dnsbl", "desc": "Spamhaus/Barracuda/SORBS/SpamCop check", "sats": 200, "egress": False}, + {"id": "o_dnsgraph", "name": "DNS Graph (HE)", "nexus": "osint:dnsgraph", "desc": "Hurricane Electric DNS delegation info", "sats": 200, "egress": False}, + {"id": "o_dnsmx", "name": "MX (DoH)", "nexus": "osint:dnsmx", "desc": "MX records via Google DNS-over-HTTPS", "sats": 200, "egress": False}, + {"id": "o_dnsprop", "name": "DNS Propagation", "nexus": "osint:dnsprop", "desc": "Compare A records across Google/Cloudflare/Quad9", "sats": 200, "egress": False}, + {"id": "o_dnsquery", "name": "DNS A Record", "nexus": "osint:dnsquery", "desc": "DNS A record lookup via Google DoH", "sats": 200, "egress": False}, + {"id": "o_dnsrecon", "name": "DNS Recon", "nexus": "osint:dnsrecon", "desc": "Query ALL DNS record types at once", "sats": 200, "egress": False}, + {"id": "o_dnsverify", "name": "TXT Verifications", "nexus": "osint:dnsverify", "desc": "Which SaaS a domain is enrolled in (TXT tokens)", "sats": 200, "egress": False}, + {"id": "o_dockerhub", "name": "Docker Hub Repo", "nexus": "osint:dockerhub", "desc": "Docker Hub repo pulls, stars, last update (no key)", "sats": 200, "egress": False}, + {"id": "o_doh", "name": "DoH Records", "nexus": "osint:doh", "desc": "Uncommon DNS records (HTTPS/SVCB/TLSA/SRV/NAPTR…)", "sats": 200, "egress": False}, + {"id": "o_doi", "name": "DOI Resolver", "nexus": "osint:doi", "desc": "Crossref publication metadata for a DOI", "sats": 200, "egress": False}, + {"id": "o_ean", "name": "Barcode/EAN", "nexus": "osint:ean", "desc": "EAN/UPC check digit + GS1 country prefix (offline)", "sats": 200, "egress": False}, + {"id": "o_elevation", "name": "Elevation", "nexus": "osint:elevation", "desc": "Ground elevation in metres (Open-Meteo)", "sats": 200, "egress": False}, + {"id": "o_email", "name": "Email Intel", "nexus": "osint:email", "desc": "Gravatar, MX, disposable detection", "sats": 200, "egress": False}, + {"id": "o_emailrep", "name": "Email Reputation", "nexus": "osint:emailrep", "desc": "emailrep.io: malicious/spam/breach flags for email (no key, limited)", "sats": 200, "egress": False}, + {"id": "o_emailsec", "name": "Email Security", "nexus": "osint:emailsec", "desc": "SPF / DMARC / DKIM posture", "sats": 200, "egress": False}, + {"id": "o_ens", "name": "ENS Resolve", "nexus": "osint:ens", "desc": "ENS name <-> ETH address + avatar", "sats": 200, "egress": False}, + {"id": "o_epoch", "name": "Epoch Time", "nexus": "osint:epoch", "desc": "Unix timestamp <-> UTC datetime", "sats": 200, "egress": False}, + {"id": "o_epss", "name": "EPSS Score", "nexus": "osint:epss", "desc": "Exploitation probability (FIRST EPSS)", "sats": 200, "egress": False}, + {"id": "o_exploit_cve", "name": "CVE Exploits", "nexus": "osint:exploit_cve", "desc": "Exploit-DB + GitHub POCs for a CVE", "sats": 200, "egress": False}, + {"id": "o_favicon", "name": "Favicon Hash", "nexus": "osint:favicon", "desc": "favicon md5/sha256 for pivoting", "sats": 200, "egress": True}, + {"id": "o_feeds", "name": "RSS / Atom Feeds", "nexus": "osint:feeds", "desc": "Discover syndication feeds on a site", "sats": 200, "egress": False}, + {"id": "o_feodoips", "name": "Feodo C2 List", "nexus": "osint:feodoips", "desc": "Is IP on abuse.ch Feodo botnet C2 list", "sats": 200, "egress": False}, + {"id": "o_flightsnear", "name": "Flights Nearby", "nexus": "osint:flightsnear", "desc": "Live aircraft within ~1° of coords (OpenSky)", "sats": 200, "egress": False}, + {"id": "o_formaudit", "name": "Form Audit", "nexus": "osint:formaudit", "desc": "Enumerate forms/inputs (login/upload) — attack surface", "sats": 200, "egress": False}, + {"id": "o_genderize", "name": "Gender Predictor", "nexus": "osint:genderize", "desc": "Predict gender from a first name (genderize.io)", "sats": 200, "egress": False}, + {"id": "o_geocode", "name": "Geocode", "nexus": "osint:geocode", "desc": "Place name → coordinates (OSM Nominatim)", "sats": 200, "egress": False}, + {"id": "o_gh_dorking", "name": "GitHub Dork Search", "nexus": "osint:gh_dorking", "desc": "GitHub code search for target string exposure in public repos", "sats": 200, "egress": False}, + {"id": "o_gh_secret_scan", "name": "GitHub Secret Scan", "nexus": "osint:gh_secret_scan", "desc": "Scan GitHub user's public repos for leaked secrets/passwords", "sats": 200, "egress": False}, + {"id": "o_ghkeysgpg", "name": "GitHub GPG Key", "nexus": "osint:ghkeysgpg", "desc": "Whether a user publishes a GPG key", "sats": 200, "egress": False}, + {"id": "o_github_code", "name": "GitHub Code Search", "nexus": "osint:github_code", "desc": "GitHub unauthenticated code search (10 results)", "sats": 200, "egress": False}, + {"id": "o_githubsearch", "name": "GitHub Repo Search", "nexus": "osint:githubsearch", "desc": "Search GitHub repos by keyword", "sats": 200, "egress": False}, + {"id": "o_gitlab", "name": "GitLab", "nexus": "osint:gitlab", "desc": "Public user profile", "sats": 200, "egress": False}, + {"id": "o_gleif_name", "name": "GLEIF Name", "nexus": "osint:gleif_name", "desc": "GLEIF fuzzy legal-entity name → LEI codes", "sats": 200, "egress": False}, + {"id": "o_golangpkg", "name": "Go Module", "nexus": "osint:golangpkg", "desc": "Latest version of a Go module", "sats": 200, "egress": False}, + {"id": "o_goproxy", "name": "Go Module", "nexus": "osint:goproxy", "desc": "Latest version of a Go module via module proxy (no key)", "sats": 200, "egress": False}, + {"id": "o_gravatarfull", "name": "Gravatar Profile", "nexus": "osint:gravatarfull", "desc": "Full public Gravatar profile + linked accounts", "sats": 200, "egress": False}, + {"id": "o_greynoise", "name": "GreyNoise", "nexus": "osint:greynoise", "desc": "Is the IP a known internet scanner — benign/malicious", "sats": 200, "egress": False}, + {"id": "o_hackernews", "name": "Hacker News", "nexus": "osint:hackernews", "desc": "Profile: karma, age, activity", "sats": 200, "egress": False}, + {"id": "o_hashid", "name": "Hash Identifier", "nexus": "osint:hashid", "desc": "Guess hash algorithm from length/charset", "sats": 200, "egress": False}, + {"id": "o_hashtext", "name": "Hash Text", "nexus": "osint:hashtext", "desc": "md5/sha1/sha256/sha512 of text (offline)", "sats": 200, "egress": False}, + {"id": "o_headers", "name": "HTTP / Security", "nexus": "osint:headers", "desc": "Headers + security-header scorecard", "sats": 200, "egress": True}, + {"id": "o_hexdump", "name": "Hexdump", "nexus": "osint:hexdump", "desc": "Offset/hex/ASCII hexdump of input (offline)", "sats": 200, "egress": False}, + {"id": "o_hibp", "name": "HIBP Password Check", "nexus": "osint:hibp", "desc": "Check if a password appeared in breaches via HIBP k-anonymity (no key)", "sats": 200, "egress": False}, + {"id": "o_hibp_email", "name": "HIBP Email Breaches", "nexus": "osint:hibp_email", "desc": "Email breach exposure via XposedOrNot (HIBP-compatible, no key)", "sats": 200, "egress": False}, + {"id": "o_hnsearch", "name": "HN Search", "nexus": "osint:hnsearch", "desc": "Search Hacker News stories/comments", "sats": 200, "egress": False}, + {"id": "o_hnuser", "name": "Hacker News User", "nexus": "osint:hnuser", "desc": "HN profile: karma, created, submission count", "sats": 200, "egress": False}, + {"id": "o_holidays", "name": "Public Holidays", "nexus": "osint:holidays", "desc": "Country public holidays this year (nager.at)", "sats": 200, "egress": False}, + {"id": "o_homoglyph", "name": "Homoglyph", "nexus": "osint:homoglyph", "desc": "Detect confusable/mixed-script spoofing chars (offline)", "sats": 200, "egress": False}, + {"id": "o_hostname", "name": "Reverse DNS", "nexus": "osint:hostname", "desc": "IP → hostname via reverse DNS", "sats": 200, "egress": False}, + {"id": "o_hstspreload", "name": "HSTS Preload", "nexus": "osint:hstspreload", "desc": "Is the domain on the browser HSTS preload list", "sats": 200, "egress": False}, + {"id": "o_htmlcomments", "name": "HTML Comments", "nexus": "osint:htmlcomments", "desc": "Extract HTML comments — leaked TODOs/paths/software", "sats": 200, "egress": False}, + {"id": "o_htmlencode", "name": "HTML Encode/Decode", "nexus": "osint:htmlencode", "desc": "HTML entity encode/decode", "sats": 200, "egress": False}, + {"id": "o_httpcode", "name": "HTTP Status Code", "nexus": "osint:httpcode", "desc": "HTTP status code meaning & family (offline)", "sats": 200, "egress": False}, + {"id": "o_httping", "name": "HTTP Ping", "nexus": "osint:httping", "desc": "Reachability + response timing", "sats": 200, "egress": False}, + {"id": "o_httpmethods", "name": "HTTP Methods", "nexus": "osint:httpmethods", "desc": "Allowed methods + TRACE/risky-verb check", "sats": 200, "egress": False}, + {"id": "o_hudsonrock", "name": "HudsonRock Stealer", "nexus": "osint:hudsonrock", "desc": "Stealer-log exposure check via HudsonRock Cavalier free API", "sats": 200, "egress": False}, + {"id": "o_huggingface", "name": "HuggingFace", "nexus": "osint:huggingface", "desc": "HuggingFace user profile or model card", "sats": 200, "egress": False}, + {"id": "o_imagerev", "name": "Reverse Image", "nexus": "osint:imagerev", "desc": "Google Lens / Yandex / Bing / TinEye search links", "sats": 200, "egress": False}, + {"id": "o_infra_fingerprint", "name": "Infra Fingerprint", "nexus": "osint:infra_fingerprint", "desc": "Infrastructure fingerprinting + hosting", "sats": 200, "egress": False}, + {"id": "o_intelx_email", "name": "XposedOrNot Breach", "nexus": "osint:intelx_email", "desc": "Email breach exposure — breach names, data types, paste hits (XposedOrNot, no key)", "sats": 200, "egress": False}, + {"id": "o_internetdb", "name": "Shodan InternetDB", "nexus": "osint:internetdb", "desc": "Open ports, CPEs, tags, known CVEs for a host", "sats": 200, "egress": False}, + {"id": "o_ioc_reputation", "name": "IOC Reputation", "nexus": "osint:ioc_reputation", "desc": "Cross-check IP/domain/hash across feeds", "sats": 200, "egress": False}, + {"id": "o_ip_math", "name": "IP Math", "nexus": "osint:ip_math", "desc": "CIDR: network/broadcast/range/host count (offline)", "sats": 200, "egress": False}, + {"id": "o_ipfull", "name": "IP Full Profile", "nexus": "osint:ipfull", "desc": "Rich geo+ASN+proxy/mobile/hosting flags", "sats": 200, "egress": False}, + {"id": "o_ipgeo", "name": "IP Geolocation", "nexus": "osint:ipgeo", "desc": "Geo, ISP, ASN, proxy/hosting flags", "sats": 200, "egress": False}, + {"id": "o_ipint", "name": "IP ↔ Integer", "nexus": "osint:ipint", "desc": "IPv4 ↔ integer ↔ hex (offline)", "sats": 200, "egress": False}, + {"id": "o_ipv4classify", "name": "IP Classify", "nexus": "osint:ipv4classify", "desc": "Classify IP: private/loopback/multicast/global (offline)", "sats": 200, "egress": False}, + {"id": "o_ipwhois", "name": "IP WHOIS / RDAP", "nexus": "osint:ipwhois", "desc": "Network owner, range, abuse contact", "sats": 200, "egress": False}, + {"id": "o_isbn", "name": "ISBN Book", "nexus": "osint:isbn", "desc": "Book metadata (OpenLibrary) + checksum", "sats": 200, "egress": False}, + {"id": "o_isexitnode", "name": "Tor Exit Check", "nexus": "osint:isexitnode", "desc": "Is this a Tor exit node?", "sats": 200, "egress": False}, + {"id": "o_isin", "name": "ISIN Validate", "nexus": "osint:isin", "desc": "Validate ISIN security identifier check digit (offline)", "sats": 200, "egress": False}, + {"id": "o_isotime", "name": "Timestamp Convert", "nexus": "osint:isotime", "desc": "Parse/convert a timestamp (offline)", "sats": 200, "egress": False}, + {"id": "o_jarm", "name": "JARM / InternetDB", "nexus": "osint:jarm", "desc": "Shodan InternetDB: ports, CPEs, vulns, tags (no key)", "sats": 200, "egress": False}, + {"id": "o_jslibs", "name": "JS Libraries", "nexus": "osint:jslibs", "desc": "Enumerate