Run OSINT tools in Docker without writing a Dockerfile
ot runs every registry tool in a read-only container with no capabilities, no host files and no network unless the tool declares it.
To run an OSINT tool in Docker without writing a Dockerfile, install ot and run the tool by name, for example
ot run osintverse-community/holehe e:ada@example.com. The first run pulls the tool’s image by its digest and checks
its signature. Every run then starts a fresh container that is read-only, has every Linux capability dropped, runs as
an unprivileged user, sees none of your files, and has no network unless the tool’s manifest says it needs one. Python
tools get the same container: ot builds their pinned package into a small image of its own. You need Docker Desktop
or Podman running, and nothing else.
What you would otherwise write
Running a Python tool in Docker by hand starts with a Dockerfile like this one, which is what the registry’s holehe package is built from:
FROM python:3.12-slim
RUN useradd --uid 10001 --create-home app
RUN pip install --no-cache-dir holehe==1.61
USER app
ENTRYPOINT ["holehe"]
Then a docker build, a tag to remember, and a docker run line with the flags that keep the container in its
place, which are long enough that most people leave them out. That is one tool. The next one has its own Dockerfile,
and both need rebuilding when they release.
On the registry that work is done once, in the package’s own CI, and the image is signed there. Each tool’s
manifest records the image by digest, the command, the inputs it takes and whether it needs the
network. ot reads the manifest, so the one command above is the whole job.
What ot run starts
For every Docker tool, ot run starts the container with these flags (they come from ContainerArgs in the ot
source, and the home page shows the same):
docker run --rm --name ot-<random> \
--read-only --tmpfs /tmp:rw,size=256m \
--cap-drop ALL --security-opt no-new-privileges \
--pids-limit 256 --memory 1g --cpus 2 \
--user 65534:65534 -e HOME=/tmp \
--network none \
-e <KEY_NAME> \
--entrypoint <command> <image>@sha256:<digest> <args>
What each part does:
--rmand a random name. The container is removed when the run ends. If you press Ctrl-C,otstops the container too, so nothing is left running.--read-onlywith a 256 MB/tmp. The tool can write scratch files in/tmpand nowhere else.HOMEpoints there, so tools that keep a cache still start.--cap-drop ALLandno-new-privileges. The process has no Linux capabilities and cannot gain any, even through a setuid program in the image.--pids-limit 256 --memory 1g --cpus 2. A tool that misbehaves cannot take over your machine.--user 65534:65534. The unprivilegednobodyuser, whatever the image’s ownUSERline says.- No
-v. None of your folders is mounted, so the tool cannot read your home directory, your other cases or your SSH keys. When a tool writes files you want to keep,--out-dir DIRmounts that one folder at/out. --entrypointand the arguments. The manifest’s command is a list, never a shell string, and each value you give fills its own place in that list. Nothing you type passes through a shell.
Network only when the tool declares it
--network none is left out only when the manifest says network: {egress: true}. A manifest without a network
block runs offline. The smoke test the registry runs on every new release follows the same rule, so a tool behaves
on your machine the way it did in its scan.
Most OSINT tools have to reach a website or an API, and their manifests say so: holehe, sherlock and checkdmarc all declare egress. Others don’t need it. email-header-parser reads the raw headers you paste in and runs with no network at all, so the headers of a phishing message never leave your machine:
ot run osintverse-community/email-header-parser --headers "$(cat headers.txt)" -f table
Every tool page says “Goes online” or “Runs offline”, next to whether the tool contacts the target, logs in to accounts or returns personal data.
Pinned, and signed by the package’s own build
A Docker tool’s manifest names its image by digest, as in
ghcr.io/osintverse-community/holehe@sha256:<64 hex digits>, never by a tag that can be moved to other code. ot refuses
an image that is not pinned (exit code 4). When it installs a tool, it pulls that digest and runs cosign verify
against the signing identity the registry recorded at publish: for a package released from a repository, that
repository’s own release workflow. If they don’t match, the install stops with
signature verification failed for <image>; refusing to install. Every run afterwards starts the image by the same
digest, so what runs is what was checked.
If cosign isn’t installed, ot warns that the signature was not verified locally and carries on; ot doctor tells
you it is missing. When an AI agent asks through ot mcp, the rule is stricter: an unsigned image, or a missing
cosign, is refused.
Before a release is published it is scanned: Trivy for vulnerabilities and leaked secrets, an SBOM, GuardDog for
Python packages, and the smoke test. Each tool’s Security tab shows the results for every version. If GitHub’s
container registry later refuses an image, ot can pull the same digest from the registry’s mirror at
mirror.otinstaller.com and check the same signature. A network error never triggers that fallback, only a refusal.
Python tools get the same container
Many OSINT tools are Python packages on PyPI, such as sherlock and
maigret. Their manifests pin one exact wheel by its SHA-256. ot downloads it,
checks the hash, builds it into an image of its own on python:3.12-slim (tagged
ot-local/<namespace>-<name>:<version>), and runs that image with exactly the flags above. Your own Python is never
touched, and two tools with conflicting requirements never share an environment. The post on
installing Sherlock walks through one.
Without a container engine, or with --native, ot installs a Python tool with pipx on your machine instead and
runs it outside the sandbox. On Linux and macOS without Docker, otlite is the edition built for that.
Docker Desktop, Podman, OrbStack or Colima
ot uses docker when it finds one and podman otherwise, with the same flags. OrbStack and Colima provide the
docker command, so they work too. ot doctor checks that the engine is running, and looks for cosign, Python and
your OS keychain:
ot doctor
When Docker is installed but not running, ot says so, asks you to start it, and exits with code 5.
Keys stay out of the command line
When a tool declares an API key, ot reads it from your OS keychain and passes -e NAME without a value, so Docker
copies it from ot’s own environment. The key never appears in the process list, in the run’s saved .meta.json or
in a log, and the container receives only HOME and the keys that tool declares. ot keys set NAME stores one.
In short
This is the long form of the “Trust it” row on the home page: every tool pinned to one digest or wheel hash,
scanned when it is published, and run in a container that can’t read your files. The
security model lists exactly what ot refuses and why, and
Getting started installs ot in a minute.
More posts · Changelog · This post as Markdown