HostLens
Find devices on a local network and turn what they expose into readable Python objects
pip install hostlens
from hostlens import identify
device = identify("192.168.1.42")
print(device.best_name)
print(device.manufacturer)
print(device.device_type)
print(device.confidence)
Samsung QN90C
Samsung Electronics
smart_tv
0.94
HostLens uses the local neighbor table, ARP, reverse DNS, OUI data, mDNS, SSDP, UPnP, NetBIOS, and a short list of useful TCP services
Unknown values stay None
Cloud enrichment stays off unless the caller enables it
Why HostLens exists
Most LAN discovery code stops at an IP and MAC address or exposes protocol-specific dictionaries
HostLens gives callers one DeviceProfile with the evidence that produced it
| What you need | What HostLens gives you |
|---|---|
| Quick inventory | Neighbor table and ARP discovery with fast enrichment |
| A useful name | Friendly name, hostname, model, or manufacturer fallback |
| Explainable identity | Raw evidence, selected fields, sources, and confidence |
| Async application support | Async client, bounded concurrency, and streaming events |
| A script or shell command | Sync helpers and the hostlens CLI |
| Local-only operation | No account, API key, or cloud request by default |
Scan a network
Async is the main implementation
import asyncio
from hostlens import HostLens
async def main() -> None:
intel = HostLens()
devices = await intel.scan_network(mode="normal")
for device in devices:
print(f"{device.ip:15} {device.best_name}")
asyncio.run(main())
HostLens detects the preferred local IPv4 subnet when one is not supplied
Pass a CIDR range when you want an exact network
devices = await intel.scan_network("192.168.1.0/24", mode="fast")
Select an interface on machines with several active adapters
intel = HostLens(interface="Wi-Fi")
Scanning modes
| Mode | Use it for | Collectors |
|---|---|---|
fast |
Quick inventory | Neighbor table, ARP, reverse DNS, OUI |
normal |
Everyday identification | Fast mode, mDNS, SSDP, UPnP, local rules |
deep |
More evidence from one device or a small network | Normal mode, NetBIOS, selected TCP services |
passive |
Watching devices already known to the OS | Neighbor table and OUI without HostLens probes |
Normal mode is the default
Deep mode checks ports 22, 80, 443, 445, 554, and 9100
It does not perform an unrestricted port scan
Progressive results
A network scan can report a target before its enrichment collectors finish
from hostlens.models import DeviceFound, DeviceUpdated, ScanCompleted
async for event in intel.scan_stream("192.168.1.0/24", mode="normal"):
if isinstance(event, DeviceFound):
print("found", event.device.ip)
elif isinstance(event, DeviceUpdated):
print("updated", event.device.best_name)
elif isinstance(event, ScanCompleted):
print("done", event.summary.duration)
Ask for counts and duration without using the event stream
result = await intel.scan_network(mode="fast", return_result=True)
print(result.summary.targets_checked)
print(result.summary.devices_found)
print(result.summary.duration)
Device profiles
Every scanning path returns the same Pydantic model
device.ip
device.mac
device.hostname
device.friendly_name
device.manufacturer
device.device_type
device.model
device.os
device.services
device.confidence
device.fields
device.evidence
device.best_name
Serialize it without another conversion layer
data = device.model_dump()
json_data = device.model_dump_json(indent=2)
Inspect the facts behind a result
for item in device.evidence:
print(item.source, item.field, item.value, item.confidence)
Or print the built-in explanation
print(device.explain())
Samsung QN90C
Confidence: 94%
Evidence:
- MAC prefix matches Samsung Electronics
- UPnP manufacturer reports Samsung Electronics
- UPnP modelName reports QN90C
- mDNS advertises _airplay._tcp.local
Confidence values are heuristics for choosing and ranking evidence
They are not calibrated probabilities
Read about profiles and evidence
Command line
The CLI calls the same Python API
hostlens identify 192.168.1.20
hostlens identify 192.168.1.20 --deep
hostlens scan
hostlens scan --fast
hostlens scan --deep
hostlens scan 192.168.1.0/24
hostlens discover
hostlens watch --passive
Privacy and Fingerbank
Local scanning works without an account or API key
Fingerbank is opt in
import os
intel = HostLens(
cloud=True,
fingerbank_api_key=os.environ["FINGERBANK_API_KEY"],
)
Preview the values that would leave the machine
payload = intel.preview_cloud_payload(device)
print(payload)
API keys are not added to evidence or logs
Read the cloud and privacy guide
Platform notes
HostLens supports Python 3.11 and newer on Windows, Linux, and macOS
Active ARP discovery may need extra system access
Windows
Install Npcap when Scapy cannot open the network adapter
Some systems also need an elevated terminal
Linux
The process needs raw socket access for active ARP discovery
The neighbor table can still work without it
macOS
Raw packet access may require elevation
The neighbor table and application-level collectors can still return results
Documentation
| Start here | Developer material |
|---|---|
| Installation | Development setup |
| First scan | Architecture |
| Python API | Collectors and parsers |
| Troubleshooting | Testing |
Full documentation lives at cyber0x3a.github.io/hostlens
Development
git clone https://github.com/Cyber0x3a/hostlens.git
cd hostlens
python -m venv .venv
python -m pip install -e ".[dev,docs]"
python -m ruff format --check .
python -m ruff check .
python -m pyright
python -m pytest
python -m build
python -m mkdocs build --strict
Normal unit tests do not use a live network
Live network tests must use the network marker
See CONTRIBUTING.md before changing protocol behavior or public models
License
HostLens is available under the MIT License
Metadata
Release files for hostlens 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| hostlens-0.1.1.tar.gz | 39.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hostlens-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 69.7 kB
Release files / hostlens-0.1.1.tar.gz
| Download URL | hostlens-0.1.1.tar.gz |
|---|---|
| Size | 39.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f269dd20a8dbef71265be055b631d8574cadd0e885c6bf8734d939aa1cbb1b77
|
|
BLAKE2b-256 checksum How to use checksums |
0d075dcecc5f37d2eac4fab753879ba28cda7699930e0bad6eb83f551e23df3e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.
Transparency logRelease files / hostlens-0.1.1-py3-none-any.whl
| Download URL | hostlens-0.1.1-py3-none-any.whl |
|---|---|
| Size | 30.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f0f74a1025f732d7044c34302eb5f6f85ee8eba88f5d6d56d8829ce7508ba0fc
|
|
BLAKE2b-256 checksum How to use checksums |
28e4ead65a1e704821b18c77f9366e5b5598eda85e28c108bbd72dc3267e9ca2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.
Transparency log