rmi-backend/app/databus/arkham_ws.py
cryptorugmunch f932ac4e1e security(rmi-backend): remove hardcoded API keys, env-reference instead
Gitleaks flagged 4 production secrets in rmi-backend source:

  app/caching_shield/solana_tracker.py:    st_ZMzXzdUI54TXQPx5E6JkG
  app/databus/arkham_ws.py:                ws_Z5x09Rcr_1780418740765322917
  app/routers/webhooks_router.py:          helius-rmi-wh-2024
                                            rmi_helius_wh_secret_2024
  app/routers/stripe_integration.py:       pk_test_51Tn13MAXseReicQtM...

Each replaced with os.getenv() reads. Placeholder env lines added to
/srv/rmi-infra/.env.secrets. Keys themselves still need rotating at
the providers (Solana Tracker, Arkham, Helius, Stripe) and storing in
gopass — see REMAINING.md.

No behavior change: code that read from env before still does, and
the empty-string fallback means the call site is the same.

Note: this commit scrubs the keys from the working tree. They remain
in git history. A follow-up git filter-repo pass is required to purge
them from history (see REMAINING.md). After that, all clones and
external remotes must be force-updated.
2026-07-02 21:34:33 +02:00

111 lines
3.8 KiB
Python

"""
Arkham Intelligence WebSocket Client
=====================================
Real-time entity updates, transfer monitoring, label changes.
Auto-reconnects, caches through DataBus, triggers premium scanner.
WS Key: from ARKHAM_WS_KEY env var (set in /etc/secrets or docker env)
"""
import logging
import os
from datetime import datetime
import httpx
logger = logging.getLogger("arkham_ws")
ARKHAM_WS_URL = "wss://api.arkhamintelligence.com/ws"
# Track active subscriptions
_subscriptions: dict[str, dict] = {}
_connected = False
async def arkham_ws_subscribe(address: str = "", action: str = "subscribe", **kw) -> dict | None:
"""Subscribe to real-time updates for an address via Arkham WebSocket.
Args:
address: Ethereum/Solana address to track
action: 'subscribe', 'unsubscribe', or 'status'
Returns subscription status or cached data.
"""
ws_key = os.getenv("ARKHAM_WS_KEY", "") or kw.get("api_key", "")
if action == "status":
return {
"connected": _connected,
"active_subscriptions": len(_subscriptions),
"subscriptions": list(_subscriptions.keys())[:50],
"source": "arkham_ws",
}
if action == "unsubscribe":
_subscriptions.pop(address, None)
return {"status": "unsubscribed", "address": address, "source": "arkham_ws"}
if action == "subscribe" and address:
# Store subscription intent (actual WS connection is managed separately)
_subscriptions[address] = {
"subscribed_at": datetime.utcnow().isoformat(),
"last_update": None,
}
# Also fetch current entity data via REST as seed
try:
api_key = os.getenv("ARKHAM_API_KEY", "")
if api_key:
async with httpx.AsyncClient(timeout=10) as c:
r = await c.get(
f"https://api.arkhamintelligence.com/intelligence/address/{address}",
headers={"API-Key": api_key},
)
if r.status_code == 200:
data = r.json()
_subscriptions[address]["entity"] = data.get("arkhamEntity", {}).get("name", "")
_subscriptions[address]["label"] = data.get("arkhamLabel", {}).get("name", "")
_subscriptions[address]["last_update"] = datetime.utcnow().isoformat()
return {
"status": "subscribed",
"address": address,
"entity": data.get("arkhamEntity", {}),
"label": data.get("arkhamLabel", {}),
"chain": data.get("chain"),
"ws_key_active": bool(ws_key),
"source": "arkham_ws",
}
except Exception as e:
logger.warning(f"Arkham WS seed fetch failed for {address}: {e}")
return {
"status": "subscribed",
"address": address,
"ws_key_active": bool(ws_key),
"source": "arkham_ws",
}
return {"status": "no_action", "source": "arkham_ws"}
async def broadcast_ws_update(address: str, update: dict):
"""Called when Arkham WS pushes an update — route through DataBus."""
if address in _subscriptions:
_subscriptions[address]["last_update"] = datetime.utcnow().isoformat()
_subscriptions[address]["latest_data"] = update
# Push to DataBus WebSocket for frontend subscribers
try:
from app.databus.ws_stream import ws_manager
await ws_manager.broadcast(
"arkham_realtime",
{
"address": address,
"update": update,
"timestamp": datetime.utcnow().isoformat(),
},
)
except Exception:
pass