Prohori (প্রহরী)
A laptop-sized mini-SOC: from raw logs to detections, enriched incidents and analyst-ready reports, with honest machine-learning evaluation and a streaming data pipeline.
Prohori (প্রহরী) is Bengali for "guard". It is an open-source, defensive security-operations toolkit in Python that runs on a single laptop with free tools only, offline by default.
Status: alpha (v0.1.0). The batch core is complete: one command goes from a log file to scored incidents and shareable reports, and a labelled attack simulator measures the rules. Machine learning, the streaming pipeline and the LLM analyst follow in later releases. See the CHANGELOG and the roadmap.
60-second demo
pipx install prohori # or: pip install prohori
prohori demo
The demo simulates an account takeover, an SSH brute force and a web scan, triages each one
offline and writes an HTML report per incident to prohori-demo/. It needs no configuration and
sends nothing over the network. Sample reports: Markdown,
HTML (download and open it) and
redacted.
Contents
- What it does
- Install
- Quick start
- Example: an account takeover
- Commands
- Built-in detection rules
- How well the rules work
- Privacy and safety
- Documentation
- Limitations
- Roadmap
- Development
What it does
flowchart LR
A[Log files<br>auth.log, nginx, app JSON] --> B[parse<br>common event schema]
B --> C[detect<br>15 YAML rules]
C --> D[incidents<br>group, score, ATT&CK]
E[enrich<br>GeoLite2, AbuseIPDB, VirusTotal] --> D
D --> F[report<br>Markdown, HTML, JSON, CSV]
| Stage | Today |
|---|---|
| Parse | Linux auth.log (sshd, sudo, PAM), nginx/Apache access logs, application security events (JSON Lines) and any JSON Lines layout through a YAML field mapping, into one typed event schema. Format detection, streaming with bounded memory, hostile input handled (oversized lines, bad encodings, control characters). |
| Detect | 15 readable YAML rules (Sigma-inspired) with field modifiers, thresholds, distinct counts, ratios, sliding and tumbling windows and ordered sequences. Every rule ships with test fixtures and a runbook; regular expressions run on RE2, so no pattern can hang a run. |
| Enrich | IP geolocation, network owner and reputation from local GeoLite2 files, AbuseIPDB and VirusTotal, all optional. SQLite cache, daily budgets, rate limiting and bounded retries. Private and documentation addresses never leave the machine. |
| Incidents | Related alerts (same IP, account, request or session within a time gap) become one incident with a points-based score you can read line by line, severity, confidence, MITRE ATT&CK v20.0 mapping, a timeline, recommended actions and stated limitations. Detects sign-ins from an IP or app new to the account. |
| Simulate | Eight labelled attack scenarios (SSH brute force, password spraying, credential stuffing, token reuse, account takeover, web scanning, injection, admin misuse) hidden in realistic background activity, byte-identical for a seed, documentation IP addresses only. Used to measure every rule's precision and recall. |
| Report | Markdown, a self-contained HTML page, JSON or a CSV timeline, with an appendix of input and rule-file SHA-256 hashes. --redact replaces account names, ids and emails (and optionally IPs) with keyed pseudonyms, and refuses to write a report that would still leak one. |
Prohori only recommends actions. It never blocks addresses, disables accounts or touches remote systems.
Install
Prohori needs Python 3.11 or newer. CI tests it on Linux and Windows; macOS should work but is not tested.
- From PyPI:
pipx install prohoriputs theprohoricommand in its own environment;pip install prohoriworks in any virtual environment. Local GeoLite2 support is an extra:pip install "prohori[geoip]". - From source: install uv, then
git clone https://github.com/UtshaBasak/Prohori.git && cd Prohori && uv sync, and run commands asuv run prohori .... The sample files used below are in the repository.
Quick start
Everything below runs offline on the synthetic samples in the repository (run from a clone).
uv run prohori triage samples/logs/account-takeover.jsonl -o triage/ # the whole pipeline
uv run prohori parse samples/logs/auth.log # normalise a log
uv run prohori detect samples/logs/auth.log # run the 15 rules
uv run prohori enrich 203.0.113.7 --offline # look up an IP (demo data)
uv run prohori incidents samples/logs/account-takeover.jsonl # group alerts into incidents
uv run prohori report samples/incidents/INC-ee7c8818fbb34989.json -f html -o report.html
uv run prohori simulate account-takeover --seed 1 --out out/ # your own labelled attack
prohori triage writes triage/summary.md, triage/incidents.jsonl and an HTML report per
incident. Open a report in a browser: one file, no scripts, no external resources. For your own
logs, prohori triage /var/log/auth.log --year 2026 -o triage/ works the same way (add
--redact-ips before sharing the output).
Example: an account takeover
samples/logs/account-takeover.jsonl is a synthetic attack on a web shop account: twelve wrong
passwords from one IP in 33 seconds, a successful sign-in, two-step verification switched off and
the bKash payout number changed. Prohori raises four alerts and joins them into one critical
incident:
$ uv run prohori incidents samples/logs/account-takeover.jsonl # first lines
INC-ee7c8818fbb34989 critical score 100
+-------------------------------------------------------------------------------------------------+
| title | Successful sign-in after many failures from one IP and 3 related alerts |
| time (UTC) | 2026-10-24 21:10:00 - 2026-10-24 21:13:10 |
| confidence | high |
| rules | PRH-ACCT-001, PRH-AUTH-001, PRH-AUTH-003 |
| entities | source.ip: 203.0.113.7 |
| | user.email_hash: 0cc175b9c0f1b6a831c399e269772661 |
| | user.id: u-2001 |
Every point of the score is explained (from prohori report):
| Points | Reason |
|---|---|
| +50 | Most severe alert: PRH-AUTH-003 (critical) |
| +20 | 2 more rules fired: PRH-ACCT-001, PRH-AUTH-001 |
| +20 | A sign-in succeeded after failed attempts |
| +15 | Sensitive account or admin change: account.2fa.disabled, account.bkash.changed |
| +20 | Reputation of 203.0.113.7: abuse confidence 97; 9 vendors flag it as malicious (from bundled demo data) |
| +10 | Sign-in to u-2001 from 203.0.113.7, an IP not seen for this account before (1 earlier IP) |
| +5 | Sign-in to u-2001 with client web, not seen for this account before |
| -40 | Capped at 100 |
| 100 | Total |
The report adds the timeline, the IP's enrichment, the ATT&CK techniques (T1110 Brute Force, T1078 Valid Accounts, T1098 Account Manipulation, T1556.006 Multi-Factor Authentication), recommended actions from the runbooks and the limitations, such as "single log source" and "enrichment is bundled demo data".
Commands
| Command | What it does | Docs |
|---|---|---|
prohori demo |
Simulate attacks, triage them and show the results; no configuration, offline | CLI.md |
prohori triage FILE |
Log in, incidents and a report per incident out (--format, --redact, --online) |
CLI.md |
prohori parse FILE |
Parse and normalise a log; table or JSON Lines | PARSERS.md |
prohori detect FILE |
Run the 15 built-in rules (and --rules DIR); exit code 3 when alerts are found |
RULES.md |
prohori rules list / validate / test |
Show, check and test detection rules | RULES.md |
prohori enrich IP... |
Geolocation and reputation; --offline never uses the network |
ENRICHMENT.md |
prohori incidents FILE |
Parse, detect and group alerts into scored incidents; offline enrichment unless --online |
INCIDENTS.md |
prohori report INCIDENT |
Render an incident as md, html, json or csv; --redact, --redact-ips |
REPORTS.md |
prohori simulate SCENARIO --out DIR |
Write a labelled attack scenario (--list, --seed, --variant heldout, --set) |
SIMULATOR.md |
prohori version |
The version, ATT&CK release and built-in rule set |
Commands that take a file also read standard input with -, and every command has --help.
Exit codes are the same everywhere: 0 nothing found, 3 alerts or incidents found
(--exit-zero turns this off for CI pipelines), 1 nothing could be processed and 2 invalid
usage. The full reference is docs/CLI.md.
Built-in detection rules
| Rule | Detects | Severity | ATT&CK |
|---|---|---|---|
| PRH-AUTH-001 | Brute-force sign-in attempts from one IP | high | T1110, T1110.001 |
| PRH-AUTH-002 | Password spraying or credential stuffing from one IP | high | T1110.003, T1110.004 |
| PRH-AUTH-003 | Successful sign-in after many failures from one IP | critical | T1110, T1078 |
| PRH-AUTH-004 | Account lockouts across many accounts | high | T1110 |
| PRH-SESS-001 | Refresh-token reuse | high | T1528, T1550.001 |
| PRH-ACCT-001 | Sensitive account change shortly after sign-in | high | T1098, T1556.006 |
| PRH-AUTHZ-001 | Authorisation denials across many resources | medium | T1595.003 |
| PRH-ABUSE-001 | Rate-limit bursts or bot-check failures from one IP | medium | T1499.002 |
| PRH-ADMIN-001 | Admin role granted | high | T1098 |
| PRH-ADMIN-002 | Admin action at an unusual hour (22:00-07:00 local time) | medium | T1078 |
| PRH-WEB-001 | Web scanning from one IP | medium | T1595.003 |
| PRH-WEB-002 | Injection patterns in request paths | medium | T1190 |
| PRH-WEB-003 | Scanner or empty user agent at a high request rate | medium | T1595.002 |
| PRH-WEB-004 | Probes for sensitive files and admin tools | medium | T1595.003 |
| PRH-SSH-001 | SSH invalid-user storm or repeated authentication-limit disconnects | high | T1110, T1110.001 |
ATT&CK tags are verified against the official Enterprise ATT&CK v20.0 data. Each rule has a
runbook in src/prohori/runbooks/. Writing your own rules is covered in
docs/RULES.md.
How well the rules work
prohori simulate writes each attack scenario in two variants: default, the attack the rules
were designed for, and held-out, the same attack done the way a careful attacker would
(slower, from more addresses, with a browser user agent, obfuscated, or at another hour).
scripts/evaluate_rules.py runs all rules over five seeds of each and compares the alerts with
the line labels.
| Default | Held-out | |
|---|---|---|
| Attacks caught by at least one expected rule | 40/40 | 35/40 |
Expected rules that missed in the held-out variants (runs missed out of runs):
account-takeover: PRH-ACCT-001 3/5, PRH-AUTH-001 5/5admin-misuse: PRH-ADMIN-002 5/5credential-stuffing: PRH-AUTH-001 5/5, PRH-AUTH-002 5/5, PRH-AUTH-003 5/5password-spraying: PRH-AUTH-001 5/5ssh-bruteforce: PRH-AUTH-001 5/5web-scanning: PRH-WEB-003 5/5
PRH-ACCT-001 has low precision on purpose: it flags every sensitive change soon after a sign-in, including the owners' own. These are synthetic results written by the rules' author; the full per-rule table and caveats are in docs/SIMULATOR.md.
Privacy and safety
- Offline by default. Nothing leaves the machine unless you configure API keys and, for
incidents, pass
--online. Tests block real network access. - Private, reserved and documentation IP addresses are never sent to outside services, even when keys are configured. VirusTotal keeps the addresses it is asked about, so querying is a deliberate choice.
- Logs are untrusted input. Parsers cap line lengths and strip control characters; reports escape every value for their format (HTML, Markdown, CSV formulas) and are tested with hostile input.
- Shareable reports.
--redact/--redact-ipsuse keyed pseudonyms and fail closed. - No secrets in the repository. API keys live in the environment or a git-ignored
.env; gitleaks runs on every commit and in CI. - Samples are synthetic and use documentation address ranges only.
Third-party data terms (MITRE ATT&CK, GeoLite2, AbuseIPDB, VirusTotal) are summarised in docs/LICENSES.md.
Documentation
| Document | Contents |
|---|---|
| CLI reference | Every command, the shared options, formats and exit codes |
| Architecture | Components, data flow and run modes |
| Event schema | Event, Alert and Incident fields |
| Parsers | Supported formats, options and field mappings |
| Rules | Rule format, evaluation semantics, testing and the built-in rules |
| Enrichment | Providers, privacy, cache, limits and configuration |
| Incidents | Grouping, the scoring table, confidence and limitations |
| Reports | Formats, sections, redaction and output safety |
| Simulator | Attack scenarios, labels, and rule precision and recall |
| Decisions | Architecture decision records |
| Releasing | How releases are built, tested and published |
| Third-party terms | Data and service licences |
| Wiki | Installation, quick start and guides |
Limitations
Prohori is honest about what it does not do:
- It reads logs after the fact. There is no live monitoring yet (the streaming pipeline is planned for v0.3.0), and it never blocks, disables or changes anything.
- Rules see only what the logs say. Request bodies, headers and encrypted traffic are not inspected; the web rules use the request line only.
- Thresholds can be evaded. The held-out evaluation shows that slow or widely distributed attacks stay under the brute-force and spraying thresholds; see How well the rules work.
- Some rules are noisy by design. PRH-ACCT-001 flags every sensitive change shortly after a sign-in, including the owner's own; incidents list such benign causes as limitations.
- The evaluation is synthetic and written by the rules' author. Test the rules on your own logs before relying on them.
- Enrichment free tiers are small and non-commercial, and reputation data is evidence, not proof.
- Supported formats are
auth.log, nginx/Apache access logs and JSON Lines (built-in app layout or a field mapping). Windows event logs and cloud audit logs are not supported yet.
Roadmap
| Release | Contents | State |
|---|---|---|
| v0.1.0 | Batch core: parsers, rules, enrichment, incidents, reports, log simulator with rule evaluation, triage and demo commands |
Released |
| v0.2.0 | Machine learning: leakage-controlled intrusion detection on public flow datasets, log-derived anomaly detection, model cards | Planned |
| v0.3.0 | Pipeline: streaming core, Redpanda and PostgreSQL in Docker, dbt models, dashboard, orchestration | Planned |
| v1.0.0 | Optional LLM analyst (opt-in, redacted input, validated output), final polish | Planned |
Development
uv sync # create .venv with the package and dev tools
uv run pre-commit install # optional: run checks on every commit
uv run ruff check . # lint
uv run ruff format --check . # formatting
uv run mypy # strict type check
uv run pytest --cov # tests with coverage (kept at 85 % or more)
uv run prohori rules test # run every rule against its fixtures
CI runs on Linux and Windows with Python 3.11 to 3.14, plus CodeQL, secret scanning, parser fuzzing and the OpenSSF Scorecard. All GitHub Actions are pinned to commit SHAs. See CONTRIBUTING.md for conventions.
Security
To report a vulnerability, see SECURITY.md.
License
MIT. Third-party data has its own terms; see docs/LICENSES.md.
Metadata
Release files for prohori 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| prohori-0.1.0.tar.gz | 248.2 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| prohori-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 451.9 kB
Release files / prohori-0.1.0.tar.gz
| Download URL | prohori-0.1.0.tar.gz |
|---|---|
| Size | 248.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0948a7e2c98fbec1024c4d04ad7c63ad44ebc73a67b09544327ededbe77b4a7a
|
|
BLAKE2b-256 checksum How to use checksums |
5cad87b324f2155a79e67da9c1b538823e193cddede24fbca2ff16047793788a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.
Transparency logRelease files / prohori-0.1.0-py3-none-any.whl
| Download URL | prohori-0.1.0-py3-none-any.whl |
|---|---|
| Size | 203.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e1d0b92df7cbfe08922224ba82774e75dc6c2e5d49d1e148d7cf4f5f9d5d0ff0
|
|
BLAKE2b-256 checksum How to use checksums |
a1a20698516168cdfab66681a7d4a833b4e54ebd190725345413b1cdec2b77fe
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.
Transparency logSecurity for prohori 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for prohori-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.