https://github.com/geeknik/h1-numbers
Local dashboard that runs analytics on your HackerOne data.
https://github.com/geeknik/h1-numbers
analytics bug-bounty dashboard hackerone local-first
Last synced: 10 days ago
JSON representation
Local dashboard that runs analytics on your HackerOne data.
- Host: GitHub
- URL: https://github.com/geeknik/h1-numbers
- Owner: geeknik
- License: apache-2.0
- Created: 2026-05-07T15:07:35.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-05-07T19:00:03.000Z (3 months ago)
- Last Synced: 2026-06-03T04:33:55.758Z (about 2 months ago)
- Topics: analytics, bug-bounty, dashboard, hackerone, local-first
- Homepage: https://deepforkcyber.com/
- Size: 15.6 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# h1-numbers
Local dashboard that runs analytics on your HackerOne data.
Pulls from the [HackerOne hacker API](https://api.hackerone.com/getting-started-hacker-api/),
stores everything in SQLite, and serves a small FastAPI dashboard at
`http://127.0.0.1:8765`.
## What it tracks
- **You** — your reports, earnings, payouts, severity/state breakdowns,
monthly trends, top-paying programs, top weaknesses, recent reports.
- **Public hacktivity** — disclosed-report sample with severity mix,
top paying programs, top CWEs, top reporters, monthly trends.
- **Programs** — overview of accessible programs, bounty/open-scope counts,
submission-state distribution.
## Setup
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
Configure `.env` (already created — confirm your username is correct):
```
HACKERONE_API_USERNAME=your_h1_username
HACKERONE_API_KEY=your_api_token
```
Get an API token at .
## Usage
Fetch all your data into SQLite (first run takes a minute or two):
```bash
python -m h1n.cli fetch
```
Or fetch a single scope:
```bash
python -m h1n.cli fetch --scope my_reports,earnings
```
Available scopes: `my_reports`, `earnings`, `payouts`, `programs`, `hacktivity`.
Run the dashboard:
```bash
python -m h1n.cli serve
```
The server prints a refresh token at startup — paste it into the dashboard's
refresh-token input to trigger refetches from the UI. (Localhost-only by
default; the token prevents drive-by CSRF from anything running locally.)
Open .

## Architecture
```
h1n/
├── client.py httpx Basic-auth client, paginated GET, 429 backoff
├── config.py env loader (HACKERONE_API_*, H1N_*)
├── db.py SQLite schema + upserts, raw_json preserved
├── fetcher.py JSON:API → flat rows, scope-by-scope ETL
├── analytics.py read-only SQL queries
├── server.py FastAPI routes + background refresh
└── cli.py `python -m h1n.cli {fetch,serve,status}`
frontend/
├── index.html tabs: My reports / Hacktivity / Programs
├── app.js Chart.js charts, table rendering, refresh trigger
└── styles.css dark theme
data/h1.db SQLite (auto-created, gitignored)
```
## Hardening notes
- `.env` is gitignored. Token is loaded once via `python-dotenv`,
passed only to `httpx.BasicAuth`, never logged.
- `client.py` strips error details so credentials never end up in logs.
- Server binds `127.0.0.1` by default. `/api/refresh` requires a
per-process token (printed at startup, hashed-compared) to prevent
any local browser from triggering an unauthenticated fetch.
- All SQL uses parameterized queries; user-controlled `limit` values
are clamped before formatting.
- `H1N_HACKTIVITY_MAX_PAGES` and `H1N_PROGRAMS_MAX_PAGES` bound
ingestion size — hacktivity is huge; you want a sample, not the firehose.
- Pagination is bounded at the client; pages are clamped to ≤100 items
(HackerOne's max), and a small sleep between pages stays well below
the 600 reads/min limit.
## Re-fetching
Each `fetch` upserts by id, so subsequent runs are incremental updates.
A scheduled cron / launchd job hitting `python -m h1n.cli fetch` daily
will keep the dashboard fresh.
### Hacktivity specifics
The hacktivity API has two hard limits:
- Page size caps at ~50 (we ask for 100 anyway).
- Pagination depth caps at <100 pages — a single linear walk reaches
~5000 newest disclosures, which is roughly mid-2017 to today.
The fetcher mitigates both:
- **Watermark.** On every non-`--full` run, `fetch_hacktivity` reads
`MAX(disclosed_at)` from SQLite and adds `disclosed_at:>=YYYY-MM-DD`
(with one day of overlap) to the Lucene query, so daily refreshes
only pull what's new.
- **`--full` flag.** Bypasses the watermark and re-paginates from the
newest disclosure to the API depth cap. Use after a long pause or
to verify completeness:
```bash
python -m h1n.cli fetch --scope hacktivity --full --hacktivity-max-pages 99
```
Takes ~80s for ~4950 items.
The dashboard's refresh button has the same `full` toggle.
## License
[Apache License 2.0](LICENSE)