"""Stealth-egress httpx.AsyncClient factory. Per the project's stealth posture, outbound calls to *third-party* services (threat-intel providers, public APIs) MUST NOT advertise "DECNET" in their User-Agent or other request fingerprints — operators running honeypots want their reconnaissance dependencies to look like generic infra, not like a tagged tool. Canonical helper for any future module that needs to call a public API without leaking the DECNET label. Internal calls (worker → operator's own SIEM via webhook, swarm agent → master) deliberately keep DECNET-tagged user-agents because the recipient wants the audit trail — do NOT route those through this client. Usage:: from decnet.net.http import stealth_client async with stealth_client() as client: resp = await client.get("https://api.greynoise.io/v3/community/1.2.3.4") The chosen UA mimics ``curl`` because it's the single most common "non-browser, non-named-tool" UA on the public internet — anti-bot filters routinely permit it, and an attacker who got a peek at our egress wouldn't learn anything more specific than "something used curl". """ from __future__ import annotations from typing import Optional import httpx # Pinned to a recent-but-not-bleeding-edge curl release. Bump on the # normal cadence; anything in-distribution is fine. Keep this string as # the single source of truth so future stealth helpers (browser-shaped, # Go-shaped) live as siblings, not divergent constants. DEFAULT_STEALTH_USER_AGENT: str = "curl/7.88.1" def stealth_client( *, timeout: float = 10.0, user_agent: Optional[str] = None, follow_redirects: bool = False, ) -> httpx.AsyncClient: """Return an httpx.AsyncClient with a generic stealth User-Agent. Returns a fresh client per call — callers own the lifecycle and SHOULD use ``async with`` to ensure connection-pool teardown. ``follow_redirects`` defaults to ``False`` because most threat-intel APIs return canonical URLs and a redirect typically signals an auth or path mistake we'd rather surface than chase. """ return httpx.AsyncClient( headers={"User-Agent": user_agent or DEFAULT_STEALTH_USER_AGENT}, timeout=timeout, follow_redirects=follow_redirects, )