Read the OWASP Amass asset database and pipe it into everything else
Project description
oamx
Read the OWASP Amass asset database and pipe it into everything else.
Zero dependencies. Read-only. Works across Amass v4 and v5.
The problem
This is the recon one-liner. Some version of it appears in every Amass tutorial, blog post and cheat sheet written in the last five years:
amass enum -passive -d example.com -o subs.txt
httpx -l subs.txt -silent | nuclei -severity high,critical
Since Amass v5 (August 2025), it writes an empty subs.txt.
v5 moved results into an asset database and stopped populating the text output.
The scan works. The data is there. But -o produces nothing, httpx probes
nothing, nuclei finds nothing, and the pipeline exits 0. It doesn't fail —
it succeeds at doing nothing, quietly, on a schedule, possibly for months.
Amass is a producer with no consumer story. The documented way out is
amass subs -names -d example.com, which gives you a flat list of hostnames and
nothing else — no IPs, no netblocks, no ports, no provenance, no scoping, no
"what changed since yesterday". Everyone else is pasting sqlite3 json_extract
incantations out of GitHub issues.
oamx is the consumer.
oamx names -d example.com --resolved-only | httpx -silent | nuclei -severity high,critical
Install
pipx install oamx # or: pip install oamx
Python 3.10+. No runtime dependencies — deliberately. A recon pipeline that breaks because of a transitive dependency resolution is worse than no tool.
There are two entry points and they are interchangeable:
oamx names -d example.com # the console script
python -m oamx names -d example.com # when PATH is not set up for you
The second is the one that keeps working inside a container, a
pip install --target layout, or anywhere the scripts directory is not on
PATH. Both are checked against the built wheel on every release.
Quickstart
# What is actually in my database?
oamx doctor
# Names, scoped to one target, that actually resolve
oamx names -d example.com --resolved-only
# Everything Amass found listening, ready for httpx or nuclei
oamx targets -d example.com --urls
# What genuinely appeared in the last day
oamx names -d example.com --new --since 24h
# The whole picture, with provenance, for storage or an agent to reason over
oamx json -d example.com > assets.jsonl
oamx doctor is the one to run first when a scan "found nothing":
database /home/you/.config/amass/amass.sqlite
layout v5 (entities/edges)
provenance yes
assets 22
FQDN 8
Service 3
IPAddress 3
Identifier 2
URL 1
TLSCertificate 1
SomeFutureAssetType 1
Netblock 1
ContactRecord 1
AutonomousSystem 1
That is real output from the test fixture rather than an illustration, and
everything below the database line is asserted against a live run — see
tests/test_readme.py. SomeFutureAssetType is in the fixture deliberately:
an asset type this release has never heard of still gets counted, and still
comes out of oamx json with a usable value.
Commands
| Command | Output |
|---|---|
names |
fully qualified domain names |
ips |
IP addresses (--ipv4 / --ipv6) |
cidrs |
netblocks in CIDR notation |
asns |
autonomous system numbers |
urls |
discovered URLs |
certs |
TLS certificates |
services |
responding network services |
orgs |
organisations |
emails |
email addresses |
targets |
host:port pairs, or --urls for scheme://host:port |
dns |
name<TAB>RRTYPE<TAB>target triples |
json |
every matching asset as JSONL, with provenance |
graph |
every matching relation as JSONL |
stats |
counts by asset type |
doctor |
what the database is and what is in it |
Filters
Every command takes the same set:
| Flag | Effect |
|---|---|
--db PATH |
database path (default: auto-discover the usual per-platform locations) |
-d, --domain |
scope to a root domain; repeatable and comma-separated |
--scope-depth N |
graph hops used to scope non-name assets (default 2, 0 disables) |
--since DUR |
last seen within 24h, 7d, 2w … |
--new |
with --since, match first seen — genuinely new, not re-confirmed |
--resolved-only |
only names with a DNS record |
--source NAME |
only assets asserted by this Amass plugin |
--exclude-source NAME |
drop assets whose only sources are these |
--min-confidence N |
drop assets below this source confidence (0–100) |
--json |
JSONL with provenance instead of bare values |
--count |
just the number |
--fail-empty |
exit 1 if nothing matched, for CI |
Why it works the way it does
Four decisions worth knowing about, because they change what you get back.
It introspects the schema instead of pinning to a version.
Amass has renamed its storage at least three times — v3 graph files, v4
assets/relations, v5 entities/edges, plus column churn inside v5 point
releases. oamx reads whatever tables and columns it finds and maps them onto
one logical model. Asset types that didn't exist when this was written still
come out with a usable value rather than raising. The adapter absorbs the churn
so your pipeline doesn't.
Scoping follows the graph, but names are matched by suffix.
-d example.com seeds on hostname suffix, then walks out --scope-depth hops
to pick up the IPs, netblocks and certificates attached to those names. But a
hostname reached by graph proximity is never pulled into scope. Without that
rule, one CNAME to a shared CDN drags the CDN's other customers into your
target list. Use --scope-depth 1 to stay on directly-resolved addresses, or
0 for names only.
Rows that reduce to the same thing are merged.
API.Example.COM. from a certificate SAN and api.example.com from a DNS
answer are two database rows and one host. oamx merges them: provenance is
unioned, the higher confidence wins, and the sighting window widens to cover
every contributing row. Merging happens before time and source filters, so a
host is never judged on only one of its sightings.
Rediscovery is not discovery.
--new --since 24h reports hosts first seen in that window. A host you've known
for a month, which a second data source just re-confirmed, is not new and won't
alert. That false-positive class is precisely what trains people to ignore their
monitoring.
Output schema
--json emits one object per line, versioned so downstream code can depend on
it even as Amass changes underneath:
{
"schema": "oamx/1",
"kind": "asset",
"id": 3,
"type": "FQDN",
"value": "api.example.com",
"first_seen": "2026-06-24 12:00:00.000000000+00:00",
"last_seen": "2026-07-24 10:00:00.000000000+00:00",
"sources": [{"name": "crtsh", "confidence": 95}, {"name": "DNS-IP", "confidence": 100}],
"attrs": {"name": "api.example.com"}
}
graph emits "kind": "edge" records with from, to, label, and decoded
DNS record types (rr_type: 1 becomes rr_name: "A", because nobody wants to
grep for 5).
As a library
from oamx.integrations import query, values
hosts = values("names", domains=["example.com"], resolved_only=True)
records = query("all", domains=["example.com"], since="7d")
query returns the same records oamx json emits, as dicts. values returns
just the values. Both raise OamxError with a message written to be read.
Reading is all it does: the database is opened mode=ro, so nothing here can
start a scan or send traffic. That is a useful property if you are handing it
to an agent — but oamx ships no agent-framework adapter, deliberately. Build
that on your side, where your own conventions for tool schemas and error
handling live.
The package ships py.typed, so annotations survive into whatever imports it.
Known limits
Stated plainly, because a recon tool that overstates itself is worse than useless.
- SQLite only. Amass also supports PostgreSQL and Neo4j. The reader is written behind an interface so those are clean additions, but shipping untested database drivers would defeat the point.
- Field names are verified for
FQDN,IPAddress,ServiceandIdentifieragainst the Open Asset Model documentation and, forIdentifier, the struct inowasp-amass/open-asset-modelitself. The remaining ~17 asset types use a best-effort key list with a generic fallback. If one of them extracts the wrong field for you, that's a one-line fix inmodel.pyand a very welcome issue —Identifierwas exactly that, reporting theunique_iddedupe key where the value lives inid. - The whole graph is loaded into memory to make scoping and merging correct. Fine for target-scoped databases; if yours has millions of entities, this will want a streaming path.
- Amass v3 graph files are not supported. Only the SQLite asset database.
Compatibility
| Amass | Layout | Status |
|---|---|---|
| v5.x | entities / edges |
tested |
| v4.x | assets / relations |
tested |
| v3.x | graph files | not supported |
Both layouts are covered by fixtures built from the documented schemas, not from guesses.
Development
python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest
122 tests in about a second, gated on branch coverage rather than line
coverage — this codebase is mostly branching, and line coverage calls a
half-tested if fully covered. ruff, mypy --strict and pylint run from
the same dev extra, so what CI runs and what you run cannot drift; see
CONTRIBUTING.md for the order.
The dev extra is not part of the zero-dependency promise, which is about what an installed oamx pulls in. CI proves that separately by installing the package alone and asserting nothing third-party came with it.
If you add behaviour, break it on purpose first and check something goes red. A test that has never been seen to fail has not been shown to test anything.
Upstream
The gap this fills is arguably an Amass issue, not a third-party one. If the
project wants an amass export that emits structured, scoped, provenance-carrying
output, the schema in oamx/model.py is a reasonable starting point and this
tool should stop being necessary. That would be the good outcome.
Licensed Apache-2.0, matching the Amass ecosystem.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file oamx-0.1.0.tar.gz.
File metadata
- Download URL: oamx-0.1.0.tar.gz
- Upload date:
- Size: 54.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3687055ad975495be76043cd73299676301e2211450d8cd8b2f921d574c3eb60
|
|
| MD5 |
df01fca556dc0d3ff36c29d5c949def4
|
|
| BLAKE2b-256 |
e28303642a203c1bec8557ec89e8e6eb3928e47555d9696bf9c3209d76b23231
|
Provenance
The following attestation bundles were made for oamx-0.1.0.tar.gz:
Publisher:
release.yml on coolhandle01/oamx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
oamx-0.1.0.tar.gz -
Subject digest:
3687055ad975495be76043cd73299676301e2211450d8cd8b2f921d574c3eb60 - Sigstore transparency entry: 2252564403
- Sigstore integration time:
-
Permalink:
coolhandle01/oamx@9d20e338a3833cfde09620d4e9b5a5344b9654cb -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/coolhandle01
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9d20e338a3833cfde09620d4e9b5a5344b9654cb -
Trigger Event:
push
-
Statement type:
File details
Details for the file oamx-0.1.0-py3-none-any.whl.
File metadata
- Download URL: oamx-0.1.0-py3-none-any.whl
- Upload date:
- Size: 30.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
41f629617eb5a549249b67b80e62d8da2ddba38f1d597052c3b633833e85cb23
|
|
| MD5 |
a5e2affc37260f8116e1593660c5b7f3
|
|
| BLAKE2b-256 |
937abf1f0e3ce68fd43b53700e6b969b9b4a088394a4480a9f4b11e624adb612
|
Provenance
The following attestation bundles were made for oamx-0.1.0-py3-none-any.whl:
Publisher:
release.yml on coolhandle01/oamx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
oamx-0.1.0-py3-none-any.whl -
Subject digest:
41f629617eb5a549249b67b80e62d8da2ddba38f1d597052c3b633833e85cb23 - Sigstore transparency entry: 2252564633
- Sigstore integration time:
-
Permalink:
coolhandle01/oamx@9d20e338a3833cfde09620d4e9b5a5344b9654cb -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/coolhandle01
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9d20e338a3833cfde09620d4e9b5a5344b9654cb -
Trigger Event:
push
-
Statement type: