{"id":50307497,"url":"https://github.com/leoveanv/argusterm","last_synced_at":"2026-05-28T17:32:16.620Z","repository":{"id":349288660,"uuid":"1201550988","full_name":"leoveanv/argusterm","owner":"leoveanv","description":"Minimalist TUI that uses LLMs to triage CVE feeds for AI-security relevance.","archived":false,"fork":false,"pushed_at":"2026-04-26T15:11:31.000Z","size":22478,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-27T15:38:12.827Z","etag":null,"topics":["ai-security","cve-scanning","cybersecurity","ratatui"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/leoveanv.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-04T20:39:53.000Z","updated_at":"2026-04-26T15:11:35.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/leoveanv/argusterm","commit_stats":null,"previous_names":["futurisold/argusterm","xm4ch1n3/argusterm","xm4ch1ne/argusterm","leoveanv/argusterm"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/leoveanv/argusterm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leoveanv%2Fargusterm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leoveanv%2Fargusterm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leoveanv%2Fargusterm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leoveanv%2Fargusterm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leoveanv","download_url":"https://codeload.github.com/leoveanv/argusterm/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leoveanv%2Fargusterm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33619967,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-28T02:00:06.440Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-security","cve-scanning","cybersecurity","ratatui"],"created_at":"2026-05-28T17:32:13.144Z","updated_at":"2026-05-28T17:32:16.613Z","avatar_url":"https://github.com/leoveanv.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# argusterm\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"artifacts/argus.jpg\" height=\"249\" alt=\"Argus — the all-seeing sentinel\" /\u003e\n  \u003cimg src=\"artifacts/cover.gif\" height=\"249\" alt=\"argusterm TUI\" /\u003e\n\u003c/p\u003e\n\n\u003e **Disclaimer:** this is a personal project so some choices reflect that — e.g. using Anthropic / Parallel among others. Feel free to fork the project and adapt it to your needs.\n\nPolls 16 RSS/Atom feeds, scrapes full article content, triages entries via a two-model LLM pipeline (Haiku for structured extraction, Sonnet for triage) with authoritative NVD enrichment for single-CVE entries, generates ASCII teaching diagrams that decompose each exploit into chokepoint dependencies (with `why` the catalyst works and `how` it operates in this case), and renders everything in a ratatui TUI with persistent SQLite caching.\n\n## Architecture\n\n```\nRSS/Atom Feeds (16)\n  → reqwest async polling · feed-rs parsing · HTML strip · dedup · date filter\n    → Parallel.ai primary scrape of entry URL         (skipped for MSRC SPA pages)\n      → Haiku llm_extract: extract_ids (structured JSON)\n        → if exactly ONE CVE id, the hop chain fires:\n            → Parallel.ai scrape nvd.nist.gov/vuln/detail/\u003cid\u003e\n            → Haiku llm_extract: pick_ref               (one most-technical reference,\n                                                         rationale-first structured JSON)\n            → Parallel.ai scrape the chosen reference\n        → Sonnet llm_summarize: triage (primary + nvd + hop concatenated)\n          → content_type · relevance score · severity · summary · chokepoint_analysis\n            · structured Diagram { nodes, edges } with why/how on each chokepoint\n            → Rust builds DOT from the typed Diagram (word-wraps labels deterministically)\n              → graph-easy: DOT → ASCII\n                → SQLite cache → ratatui TUI\n```\n\nThe pipeline is fail-open at every step: if any scrape, extraction, or hop fails, the summarize call still runs with whatever context is available (at minimum the RSS title + description). Multi-CVE entries (news roundups, patch bundles, research chains) skip the NVD hop chain entirely — enriching one CVE from a bundle would bias the summary — and summarize runs on the primary scrape alone.\n\n## How Scoring Works\n\nEach entry is scored 0.0–1.0 on how much it relates to **AI changing security**:\n\n| Score Range | Meaning | Examples |\n|---|---|---|\n| **0.7–1.0** | Core AI-security | LLMs finding zero-days, AI-powered vuln research, attacks on ML systems, AI security tooling |\n| **0.3–0.7** | Tangential | Traditional vulns in AI-adjacent software, AI policy/regulation |\n| **0.0–0.3** | No AI connection | Standard CVEs, generic security news |\n\nThe LLM also classifies each entry as `cve`, `advisory`, `news`, `research`, `promotional`, or `irrelevant`. Entries classified as `promotional` or `irrelevant` are automatically hidden.\n\n## Prerequisites\n\n- **Rust** (edition 2024)\n- **[Graph::Easy](https://metacpan.org/pod/Graph::Easy)** (Perl) — converts Graphviz DOT to ASCII diagrams\n- **Anthropic API key** — used by both `llm_extract` (Haiku) and `llm_summarize` (Sonnet)\n- **Parallel.ai API key** — strongly recommended; drives primary page scraping, NVD enrichment, and the reference hop. Without it the pipeline degrades to RSS-summary-only triage and diagrams will be thin.\n\n### Install Graph::Easy\n\n```bash\n# via cpanm\ncpanm Graph::Easy\n\n# or via cpan\ncpan Graph::Easy\n```\n\n## Build \u0026 Run\n\n```bash\ncargo install --path .\nargus            # launch TUI\nargus --nuke-db  # wipe SQLite cache and start fresh\nargus --help     # show usage\n```\n\n## Configuration\n\n```bash\ncp config/argusterm.eg.toml config/argusterm.toml\n```\n\nEdit `config/argusterm.toml`:\n\n```toml\n[feeds]\npoll_interval_secs = 300       # feed poll interval (seconds)\n\n[llm]\nmodel_extract = \"claude-haiku-4-5-20251001\"  # Haiku: fast structured extraction (CVE ids, ref URL picking)\nmodel_summarize = \"claude-sonnet-4-6\"         # Sonnet: main triage — summary, chokepoint analysis, teaching diagram, scoring\napi_key = \"your-anthropic-api-key\"\nmax_concurrent = 20                           # concurrent triage tasks\n\n[scraper]\napi_key = \"your-parallel-ai-key\"  # Parallel.ai key — recommended; the multi-hop enrichment\n                                  # path and all NVD/reference scraping require it. Without\n                                  # it, triage falls back to the RSS summary only.\n\n[diagram]\ngraph_easy_bin = \"/usr/local/bin/graph-easy\"  # path to graph-easy binary\nperl5lib = \"/usr/local/lib/perl5\"             # Perl5 lib path for Graph::Easy\n\n[filters]\ndays_lookback = 7              # only show entries from the last N days\n\n[tui]\nrefresh_rate_ms = 250          # TUI redraw interval (milliseconds)\n```\n\nThe `days_lookback` setting controls both which cached entries are loaded on startup and which new feed entries are ingested. The DB retains all entries permanently — widening the window instantly surfaces older cached data.\n\n## Keybindings\n\n| Context | Keys |\n|---|---|\n| **Feed List** | `j`/`k` nav · `d`/`u` half-page · `gg`/`G` top/bottom |\n| **Detail** | `j`/`k` vscroll · `h`/`l` hscroll · `c` enter CVE bar |\n| **CVE Bar** | `h`/`l` nav · `o` open on NVD · `Esc` exit |\n| **Filter Bar** | type to filter · `Backspace` delete · `Esc`/`Enter` exit |\n| **Global** | `m` cycle mark · `o` open URL · `r` re-triage · `x` delete (permanent) · `s` cycle sort · `p` pause/resume polling · `/` filter · `Tab` cycle panes · `q`/`Esc` quit |\n\n## Marks\n\nEach entry can be marked to track your reading state. Press `m` to cycle the mark on the selected entry. The mark is persisted in SQLite and rendered non-intrusively in the feed list.\n\n| Mark | Glyph | Meaning |\n|---|---|---|\n| **None** | ` ` | Unmarked (default) |\n| **Read** | `✓` | Fully read — the entire line is dimmed to de-emphasize it |\n| **Bookmarked** | `★` | Save for later |\n| **Skimmed** | `~` | Glanced over, not fully read |\n\nThe glyph appears in parentheses before the entry title, e.g. `(★) title here...`. `Read` entries are rendered in `DarkGray` so they fade into the background while remaining visible.\n\n## Data Persistence\n\nAll state lives in `.argusterm/cache.db` (SQLite) across three tables:\n\n- **`entries`** — every ingested feed item with its LLM triage output (summary, chokepoint analysis, ASCII diagram, score, CVE ids, scraped content). On restart, rows within `days_lookback` load instantly; only entries missing LLM results are re-triaged.\n- **`cve_hop_cache`** — CVE-id-keyed cache of the reference URL the picker chose for each CVE and the scraped content of that page. When a single-CVE entry is triaged and the same CVE id has been seen before, the pipeline reuses the cached hop content and skips the NVD scrape, the `pick_ref` Haiku call, and the reference scrape entirely. Cross-feed CVE duplicates (e.g. the same kernel CVE showing up in MSRC *and* CISA) become cheap. NVD itself is intentionally not cached — the record matures over time — but the picked reference URL is stable once chosen.\n- **`deleted_entries`** — tombstone table. Pressing `x` inserts the entry's id here in addition to removing it from `entries`, so the next feed poll's dedup check skips the id instead of re-ingesting it. Deletion is **permanent** across feed polls and app restarts. Recovery requires a manual `DELETE FROM deleted_entries WHERE id = '...'` if you hit `x` by mistake.\n\nPressing `r` on an entry clears all cached LLM output (summary, chokepoint analysis, diagram, score, CVE ids) **and** the scraped content used as input, so re-triage truly restarts the pipeline from scratch. The hop cache in `cve_hop_cache` is *not* cleared by `r` — if the entry has a single CVE id that's already in the cache, re-triage still reuses the cached hop. Nuke the DB with `argus --nuke-db` if you want a clean slate.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleoveanv%2Fargusterm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleoveanv%2Fargusterm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleoveanv%2Fargusterm/lists"}