# How Holehe Handles Concurrent Module Execution: Inside the Async Architecture

> Discover how Holehe achieves concurrent module execution using Trio nurseries and httpx to manage async requests, enabling simultaneous scanning of dozens of web services without blocking.

- Repository: [Palenath/holehe](https://github.com/megadose/holehe)
- Tags: internals
- Published: 2026-08-28

---

**Holehe executes site-specific checks concurrently using Trio nurseries to manage multiple async HTTP requests through a shared httpx.AsyncClient, allowing simultaneous scanning of dozens of web services without blocking.**

The megadose/holehe tool performs OSINT email checks across hundreds of platforms by treating each site-specific module as an independent asynchronous task. Understanding how Holehe handles concurrent module execution reveals why the tool can scan multiple services simultaneously without blocking or creating resource contention. The architecture relies on Python's Trio library to orchestrate parallel HTTP requests through a single shared client.

## Dynamic Module Discovery and Loading

Before concurrent execution begins, Holehe must discover and prepare the site-specific check functions.

### Recursive Module Importing

In [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py), the `import_submodules` function recursively imports every module under the `holehe.modules` package. This dynamic loading approach (lines 37-47) ensures that new platform checks added to the `holehe/modules/` directory are automatically available for concurrent execution without manual registration.

### Function Extraction and Normalization

Once imported, the `get_functions` utility (lines 50-63 in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py)) transforms the module objects into a list of callable async functions. Each function represents a single site-specific check (such as Twitter, GitHub, or Instagram), standardized to accept the target email, an HTTP client, and an output list as parameters.

## Trio Nursery Pattern for Concurrent HTTP Requests

The core of Holehe's concurrent architecture lies in its use of structured concurrency via Trio's nursery pattern.

### Shared Async Client Initialization

Rather than creating multiple HTTP connections, Holehe instantiates a single `httpx.AsyncClient` with a configurable timeout (lines 12-14 in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py)). This shared client allows all concurrent modules to reuse connection pooling and DNS caching, significantly reducing overhead when launching dozens of simultaneous requests.

### Spawning Concurrent Tasks

The concurrent execution happens inside a Trio nursery block (lines 18-21 in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py)):

```python
async with trio.open_nursery() as nursery:
    for website in websites:
        nursery.start_soon(launch_module, website, email, client, out)

```

This pattern simultaneously schedules all site-specific functions. When one module awaits an HTTP response, Trio switches to another ready task, ensuring the CPU never idles while waiting for network I/O.

### Task Wrapping and Exception Isolation

Each module runs inside the `launch_module` wrapper function (lines 66-78 in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py)). This wrapper awaits the site-specific coroutine, catches any exceptions to prevent one failed check from crashing the entire nursery, and normalizes results into a common dictionary format appended to the shared output list.

## Real-Time Progress Tracking

Monitoring dozens of concurrent tasks requires instrumentation that doesn't interfere with execution. Holehe implements `TrioProgress` in [`holehe/instruments.py`](https://github.com/megadose/holehe/blob/main/holehe/instruments.py) (lines 4-10) as a custom Trio instrument. This class hooks into Trio's task lifecycle to update a `tqdm` progress bar each time a `launch_module` task finishes, providing real-time feedback without polling or blocking the event loop.

## Implementation Examples

Understanding the concurrent architecture becomes clearer when examining how to invoke Holehe both from the command line and programmatically.

To run Holehe via CLI, which internally uses the nursery pattern:

```bash
$ holehe user@example.com --no-color --csv

```

To reproduce the concurrent execution programmatically:

```python
import httpx, trio
from holehe.core import import_submodules, get_functions, launch_module
from holehe.instruments import TrioProgress

async def run_checks(email):
    modules = import_submodules("holehe.modules")
    sites = get_functions(modules)
    client = httpx.AsyncClient(timeout=10)
    out = []
    instrument = TrioProgress(len(sites))
    trio.lowlevel.add_instrument(instrument)

    async with trio.open_nursery() as nursery:
        for fn in sites:
            nursery.start_soon(launch_module, fn, email, client, out)

    trio.lowlevel.remove_instrument(instrument)
    await client.aclose()
    return out

results = trio.run(run_checks, "user@example.com")

```

Individual modules follow an async pattern using the shared client:

```python
async def twitter(email, client, out):
    req = await client.get(
        "https://api.twitter.com/i/users/email_available.json",
        params={"email": email})
    # Response processing logic...

```

## Summary

- **Dynamic loading**: `import_submodules` and `get_functions` in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) discover and prepare site-specific check functions automatically.
- **Structured concurrency**: The Trio nursery pattern in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) lines 18-21 manages concurrent execution without blocking.
- **Resource efficiency**: A single `httpx.AsyncClient` instance shared across all modules enables connection reuse and reduces memory overhead.
- **Fault isolation**: The `launch_module` wrapper prevents individual module failures from crashing the entire concurrent scan.
- **Progress visibility**: The `TrioProgress` instrument in [`holehe/instruments.py`](https://github.com/megadose/holehe/blob/main/holehe/instruments.py) provides non-blocking progress updates via `tqdm`.

## Frequently Asked Questions

### What concurrency library does Holehe use for module execution?

Holehe uses the **Trio** library to handle concurrent module execution. Specifically, it utilizes `trio.open_nursery()` to spawn independent tasks for each site-specific check, leveraging Trio's structured concurrency model to ensure all tasks complete before the program exits.

### How does Holehe prevent one slow website from blocking other concurrent checks?

Holehe prevents blocking through **cooperative scheduling** via Trio's event loop. Each module uses `await` when making HTTP requests with the shared `httpx.AsyncClient`, yielding control back to the scheduler. This allows other ready tasks to execute while waiting for network I/O, ensuring slow responses don't stall the entire scan.

### Can I adjust the timeout settings for concurrent HTTP requests in Holehe?

Yes, you can configure the timeout by modifying the `httpx.AsyncClient` initialization parameters. In [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) lines 12-14, the client is created with a user-specified timeout value that applies to all concurrent requests, ensuring slow or unresponsive services don't hang indefinitely.

### How does Holehe aggregate results from modules running concurrently?

Results are collected in a **shared list** passed to each `launch_module` call. The `launch_module` function in [`holehe/core.py`](https://github.com/megadose/holehe/blob/main/holehe/core.py) (lines 66-78) appends normalized dictionaries to this list upon completion. Because Trio's nursery ensures all tasks finish before the `async with` block exits, the final list contains deterministic results from all concurrent checks.