An interactive menu container for customers who don't have git or Python set up locally (or are
otherwise blocked) but need to run one of the ownjoo
query_* API tools. docker run it, pick a tool from the menu, and it clones the repo, installs
its dependencies, and drops you into a shell already cd'd into it — ready to run.
Base: Chainguard Wolfi, matching the same supply-chain-hardened
approach as packet-bench.
docker pull ownjooorg/query-benchPublished automatically by .github/workflows/docker-publish.yml
on every push to main (tag latest) and on v* tags (semver tags). Requires the
DOCKERHUB_USERNAME/DOCKERHUB_TOKEN repo secrets to be configured before the workflow can push.
Images are cosign-signed and ship an attached SBOM.
docker build -t query-bench .docker run --rm -it query-benchYou'll first see a red-bordered disclaimer (no warranty, no liability, no
security guarantee — see below) that you must explicitly accept to continue. After that, a
green-bordered menu queries the GitHub API live for every public query_* repo under ownjoo
and lists them — no rebuild needed when a new tool is added org-side. A 0 row is always
available to skip straight to a shell with nothing cloned.
Pick a tool and it clones the repo into your home directory inside the container, runs
pip install -r requirements.txt, lists the tool's .py files, and execs into an interactive
shell in that directory:
- Exactly one
.pyfile (the common case): unambiguous entry point, so it just runspython <file>.py -hfor you and shows the real usage output immediately. - Multiple
.pyfiles: prints aTry: python <file>.py --helphint instead — the entry point is auto-detected (main.pyif present, otherwise whichever file has anif __name__ == "__main__":guard), but not run automatically since which file matters more when there's more than one.
The GitHub API is unauthenticated by default (60 requests/hr per IP). If you're hitting that
limit, pass a token: docker run --rm -it -e GITHUB_TOKEN=ghp_... query-bench.
Nothing this container does can reach the host filesystem. Docker containers get their own
isolated root filesystem via Linux namespaces; unless you explicitly add -v/--mount, there is
no path inside the container leading to any host file — not restricted, genuinely absent. The
documented docker run command above has no mounts, no --privileged, and no elevated
capabilities (--cap-add) — none are ever needed for anything this container does (clone a repo,
pip install, run a Python script). It also runs as a non-root user (toolrunner) as defense in
depth. None of -v/--mount, --privileged, a Docker-socket mount, or --net=host/--pid=host
(the ways this guarantee could be broken) appear anywhere in this repo. Verify yourself:
docker run --rm query-bench sh -c 'ls / ; whoami' # no host paths visible, non-root
docker inspect <container_id> --format '{{json .Mounts}}' # empty mount listCleanup is --rm, not just stopping. docker stop only halts the process — the container's
writable layer (the cloned repo, installed packages, anything typed at a tool's prompts, including
API credentials) still sits on disk until the container is actually removed. The documented run
command uses --rm, so Docker removes that layer automatically the moment the shell exits —
nothing from a session survives past that point, no separate cleanup step required.
This is distinct from the image (query-bench:latest itself — Python, git, menu.py,
DISCLAIMER.md): that's static, public, identical for every user, and persists in your local
Docker image cache after the container exits, same as any image you docker pull — that's normal
and contains nothing session- or customer-specific. Remove it explicitly with docker rmi query-bench if you don't want it cached locally.
Want the container instance to persist instead? Drop --rm:
docker run -it query-benchThe stopped container (and its writable layer — the clone, installed packages, anything typed)
then sticks around on disk so you can docker start -ai <container> to resume it later, or
docker cp <container>:/path ./local to pull files out. You're opting back into the state
described above though — including that anything sensitive typed at a prompt (e.g. API
credentials) remains on disk until you docker rm it yourself.
Want results to survive on purpose? Mount a local directory at /output (created and owned by
the non-root user at build time, so this works cleanly whether or not anything's mounted there):
docker run --rm -it -v "$(pwd)/output:/output" query-benchPoint whichever tool you pick at /output for its results (e.g. python main.py --output /output/results.json, or cd /output before running it, depending on that tool's own CLI). The
menu tells you after setup whether /output is actually mounted. This is the one deliberate,
opt-in exception to "nothing persists" — everything else about the isolation above still holds.
DISCLAIMER.md is shown and must be explicitly accepted (y) before the menu
loads — declining exits immediately without cloning or installing anything. It draws a clear line:
this container and the query_* tools it clones are ownjoo.org's own code, but those tools pull in
numerous third-party libraries that aren't maintained by ownjoo.org and aren't under our control —
no warranty, no liability, and no guarantee of security for any of it, plus a recommendation that
your security team independently evaluate any tool before use. This is a plain-language disclaimer,
not legal advice — have it reviewed by counsel before relying on it.
git,python-3.14+ a venv withoj-toolkit(this org's own console-formatting library —Box/Table/Outputfor the menu UI itself; not restricted to the target tools' own dependencies, which get installed fresh into the same venv per-tool). Building this menu found and fixed three real bugs inoj-toolkitalong the way (aTablecolumn-count bug, aBoxtitled-border width mismatch, and added theborder_colorfeature this menu's red/green borders use) — released as 0.2.3, 0.2.4, and 0.3.0.- Non-root by default (
toolrunneruser); the venv ischown'd to that user at build time so runtimepip install(of whatever tool you pick) actually has write permission PYTHONUNBUFFERED=1— without it, this menu's own status messages could print out of order relative togit/pip's output, since Python block-buffers stdout when it isn't a TTY
The menu is dynamic — it lists every public, non-archived repo under ownjoo whose name
starts with query_, fetched live via the GitHub API (see fetch_query_tools() in
menu.py). Nothing to maintain here when a new query tool is added or removed org-side.
Private query_* repos (e.g. query_ionix_async) never appear — the unauthenticated API call
simply can't see them, so a customer without their own GitHub credentials can't be shown a tool
they couldn't clone anyway.
The only requirement for a tool to work through this menu is that it installs cleanly via
pip install -r requirements.txt. Three tools previously didn't, due to a stale dependency
reference; see git history / ownjoo/utils for context.
Since the list is now fetched live, newly-added query_* repos aren't pre-vetted — if one fails to
install or run, that's a bug in that tool's own repo, not this container.