Command-line client for Grim, the OSINT/recon platform.
Project description
grim-cli
A command-line client for Grim. It's a thin wrapper around Grim's HTTP API — every command maps to one API call, authenticated with a personal API key generated from the Grim account page.
Install
pipx install grim-cli # or: pip install grim-cli
grim --help
To install from source instead (e.g. to work on the CLI itself):
poetry install
poetry run grim --help
Setup
Generate an API key from the Grim account page (Account → API keys → Create), then:
grim configure
This prompts for your API key, verifies it against Grim, and saves it to
~/.config/grim-cli/config.toml (owner-only permissions). You only need to
do this once. The prompt is intentionally not a plain --api-key flag —
that would leak the key into shell history and be visible to other local
users via ps. Running grim configure again asks for confirmation before
overwriting an already-saved key. grim logout removes the saved key from
disk (the key itself stays valid server-side — revoke it from the account
page if it was compromised).
Also worth running once: grim --install-completion — this is a stock
Typer/Click feature (every grim --help shows it, not something specific
to this CLI), and it's a bigger win here than on a smaller tool given how
many nested subcommands there are (scan/template/schedule, each with
7-8 of their own). It detects your shell and adds a completion script to
its config (e.g. ~/.zshrc); restart your shell afterward for it to take
effect. grim --show-completion prints the same script to stdout instead
of installing it, if you'd rather wire it into your dotfiles by hand.
Usage
grim account # tier, credits, 2FA/Tor status
grim modules # list available scan modules
grim scan create example.com # scan with all modules
grim scan create example.com -m headers -m dns # only specific modules
grim scan create example.com --preset quick # canned module list (see below)
grim scan create example.com --force # re-run cached modules too
grim scan create example.com --wait # stream progress until done
grim scan list # your scan history
grim scan get # status + per-module results of the scan you just created
grim scan stream # tail a running scan's progress
grim scan cancel # cancel a running scan
grim scan delete # delete a scan and its results
grim scan retry headers # free re-run of one infra-failed module
grim scan diff # compare against the previous force re-scan
grim scan export # full Markdown report to stdout (or --output FILE)
grim template create quick -m headers -m dns -m tls # save a reusable module list
grim template list # your saved templates
grim template get <template-id> # a single template's modules
grim template update <template-id> --name renamed -m headers # rename/replace modules
grim template delete <template-id> # delete a template
grim schedule create example.com --interval daily # all modules, daily
grim schedule create example.com --interval weekly -m headers -m tls
grim schedule create example.com --interval monthly --template-id <template-id>
grim schedule create example.com --interval daily --diff-only # email a diff, not the full report
grim schedule list # your scheduled scans
grim schedule get <schedule-id> # a single schedule's settings
grim schedule update <schedule-id> --interval weekly
grim schedule update <schedule-id> --disable # pause without deleting
grim schedule update <schedule-id> --diff-only # switch report style
grim schedule delete <schedule-id> # delete a scheduled scan
grim schedule run-now <schedule-id> # trigger immediately instead of waiting
grim logout # remove the locally saved API key
grim --version # print the CLI version and exit
The scan ID is usually optional
Every scan subcommand that acts on a specific scan (get, stream,
cancel, delete, retry, diff, export) takes the scan ID as an
optional trailing argument — omit it and the CLI uses whichever scan you
most recently ran scan create against, saved locally in
~/.config/grim-cli/config.toml. This is what makes the common
create-then-inspect flow short:
grim scan create example.com --wait
grim scan export --output report.md # no id to paste — it's the scan you just created
Pass a scan ID explicitly at any time to target a different scan instead
(e.g. grim scan get abc-123); it always wins over the saved one, and
nothing here is fetched from the server or verified to still exist — it's
just "the last id scan create returned," refreshed every time you create
or resume a scan. scan list/scan get <explicit-id> never change it, so
checking on an old scan doesn't knock the scan you're actively working on
out of "last." If you've never run scan create (e.g. right after grim configure), omitting the ID fails with a clear error telling you to run
scan create first or pass one.
Running specific modules
-m/--module is repeatable and takes exactly the module names grim modules lists (e.g. headers, dns, tls, whois, crawler,
portscan, git_exposure — run grim modules for the full, current list
rather than relying on it being documented here, since new modules get
added over time):
grim modules # see what's available
grim scan create example.com -m headers -m tls -m dns # only these three
Omitting -m entirely runs every module. --preset quick (headers, dns,
tls) and --preset passive (whois, dns, wayback, typosquat) apply the same
canned module lists as the dashboard's Quick/Passive only buttons —
--preset passive in particular never touches the target directly (every
module in it is third-party-sourced or DNS-only). --module and --preset
are mutually exclusive; for a reusable named list instead of a one-off
preset, see grim template below. scan create --template-id doesn't
exist — templates are only a module-list source when creating a
schedule (--template-id); for a one-off scan, resolve the template's
modules yourself with grim template get and pass them via repeated -m.
Exporting the Markdown report
grim scan export downloads the full per-module report for a completed
scan — the same report generated by the dashboard's "Export as Markdown"
button and linked from scheduled-scan report emails, byte-for-byte
identical either way, since all three are the same server endpoint. It
fails with a 409 if the scan is still running or hasn't been scanned yet
(check grim scan get first). With no --output, the Markdown is written
straight to stdout so it composes with shell redirection:
grim scan create example.com --wait
grim scan export --output report.md # save to a file
grim scan export > report.md # equivalent, via redirection
grim scan export | grep -A2 "## Findings" # or pipe it straight into another tool
Unlike every other command here, scan export has no --json — it
downloads Markdown, not JSON, so there's no JSON form to switch to.
Scheduled scans always email their report to your account's own confirmed
address (add and confirm one from the Grim account page first — there's no
way to point a schedule at a different inbox) and always force a fresh
re-scan, charging a credit on every successful run just like scan create --force.
Every command accepts --json (except scan export, above) for raw JSON
output instead of a formatted table/message — handy for piping into jq.
On scan create --wait, scan stream, and scan retry --wait, --json
switches the live progress feed to one JSON object per line (instead of
colored text), so each event stays independently parseable, e.g. piped
through jq -c. Every invocation also prints a startup banner to stderr,
so none of this ever mixes into piped/redirected stdout — including the
"Using last scan: ..." note printed whenever a scan ID is defaulted, which
also goes to stderr for the same reason.
Commands that return a single JSON document (account, modules, every
scan/template/schedule subcommand except scan stream and scan export) also accept --output/-o FILE to save that JSON to disk in
addition to whatever's printed to the screen — independent of --json, so
you can keep the human-readable table on screen and still get a saved
artifact:
grim scan get --output result.json
template update and schedule update only change the fields you pass —
omitted fields are left as-is server-side. Calling either with no fields at
all is rejected locally before any request is sent.
If the connection drops during scan create --wait, scan stream, or
scan retry --wait, the CLI automatically reconnects with backoff (up to 5
attempts) instead of dying with a traceback; already-printed module results
aren't repeated. Any other network failure (unreachable host, timeout) is
reported as a clean one-line error instead of a raw Python traceback.
Common workflow — start a scan, wait for it, then fetch the results as JSON:
grim configure
grim scan create example.com --wait
grim scan get --json | jq
Scope
This wraps the API-key-accessible subset of the Grim API: scan create/get/list/cancel/delete/stream/retry/diff/export, scan templates, scheduled scans, the module list, and the caller's own account info (tier/credits). Admin operations (user management, tier/role changes, the audit log) require a browser session and aren't reachable with an API key — use the Grim web dashboard for those.
Development
poetry install --with dev
poetry run pytest
Releasing
Publishing to PyPI is automated (.github/workflows/publish-cli.yml) and
triggered by a cli-vX.Y.Z tag — the tag itself doesn't set the version,
it's checked against cli/pyproject.toml's version and the workflow
fails loudly if they don't match. To cut a release:
# bump the version field in cli/pyproject.toml, then:
git add cli/pyproject.toml
git commit -m "Release grim-cli vX.Y.Z"
git tag cli-vX.Y.Z
git push origin main cli-vX.Y.Z
The workflow runs cli/'s own test suite before building and publishing,
so a red test suite blocks the release rather than shipping a broken
package. Publishing uses PyPI's trusted publishing (OIDC), not a stored API
token — see the workflow file's header comment for the one-time PyPI-side
setup this needs before the first tag.
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 grim_cli-0.1.0.tar.gz.
File metadata
- Download URL: grim_cli-0.1.0.tar.gz
- Upload date:
- Size: 18.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c15e85843c4d656cb57e5d0a1164d8212218de639d39ab20662b3e92609fc76a
|
|
| MD5 |
43418833a32d5fac69d26788036cd6e6
|
|
| BLAKE2b-256 |
c229fac275c1208ec0aba9b0b5b45f99d2a9c62d8fdc62c042788202d52b25a7
|
Provenance
The following attestation bundles were made for grim_cli-0.1.0.tar.gz:
Publisher:
publish-cli.yml on smowggyayy/grim
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
grim_cli-0.1.0.tar.gz -
Subject digest:
c15e85843c4d656cb57e5d0a1164d8212218de639d39ab20662b3e92609fc76a - Sigstore transparency entry: 2162246320
- Sigstore integration time:
-
Permalink:
smowggyayy/grim@3ea741742cf0110169e2967b2250dfac85fabd23 -
Branch / Tag:
refs/tags/cli-v0.1.0 - Owner: https://github.com/smowggyayy
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-cli.yml@3ea741742cf0110169e2967b2250dfac85fabd23 -
Trigger Event:
push
-
Statement type:
File details
Details for the file grim_cli-0.1.0-py3-none-any.whl.
File metadata
- Download URL: grim_cli-0.1.0-py3-none-any.whl
- Upload date:
- Size: 17.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5bfc5d49bfa2d0dd8d23c8b518bb07f2d2390c7dab943f763d0d2928ba98bb4a
|
|
| MD5 |
c2335f71efe1a55b9d01330057322724
|
|
| BLAKE2b-256 |
203ba75fb194d1735318980ed1252240baa08b7d3856ce9ca255aaa1a6ea8581
|
Provenance
The following attestation bundles were made for grim_cli-0.1.0-py3-none-any.whl:
Publisher:
publish-cli.yml on smowggyayy/grim
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
grim_cli-0.1.0-py3-none-any.whl -
Subject digest:
5bfc5d49bfa2d0dd8d23c8b518bb07f2d2390c7dab943f763d0d2928ba98bb4a - Sigstore transparency entry: 2162246839
- Sigstore integration time:
-
Permalink:
smowggyayy/grim@3ea741742cf0110169e2967b2250dfac85fabd23 -
Branch / Tag:
refs/tags/cli-v0.1.0 - Owner: https://github.com/smowggyayy
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-cli.yml@3ea741742cf0110169e2967b2250dfac85fabd23 -
Trigger Event:
push
-
Statement type: