jessn-dev/osint-atlas

GitHub: jessn-dev/osint-atlas

一个基于力导向图谱的开源情报工具导航平台,收录约 5,000 个资源,以 YAML 为唯一数据源并配备自动化链接健康检查、Wayback 回退和上游同步机制。

Stars: 0 | Forks: 0

# OSINT Atlas OSINT Atlas 是一个图谱优先、具备链接健康感知的地图,收录了约 5,000 个开源情报工具和资源。通过力导向图谱进行浏览 (分组 → 类别 → 工具),并支持全局搜索、状态/分组过滤器以及 URL 关联枢轴。该项目以每个类别的 YAML 作为 唯一数据源,由 Astro 编译为部署在 GitHub Pages 上的静态网站。失效或被劫持的链接每周会被标记,并回退到 Wayback Machine;新工具会自动从上游备忘单同步。衍生自 Jieyab89 的 OSINT Cheat Sheet。 以 [Jieyab89/OSINT-Cheat-sheet](https://github.com/Jieyab89/OSINT-Cheat-sheet) 作为数据参考从零开始重建。 - **唯一数据源:** `data/categories/` 中每个类别对应一个 YAML 文件(经 Zod 验证)。 - **网站:** [Astro](https://astro.build) 静态构建 + D3 力导向图谱,部署至 GitHub Pages。 - **浏览:** 渐进式展开图谱(分组 → 类别 → 工具),全局模糊搜索、 状态/分组过滤器、URL 关联枢轴以及下钻侧边栏。 - **质量:** schema 验证、每周进行失效/被劫持链接检查并提供 Wayback 回退、 每周上游同步、单元测试、lint 以及部署后的冒烟测试 — 全部在 CI 中完成。 ## 为什么进行重建 我经常依赖原始的备忘单,但每次我想修复死链或添加 工具时,都要在一个 6,000 行的文件中挣扎。因此,我围绕数据而不是文档对其进行了重建 — 相同的、我信任的资源,以一种我能够真正**维护、验证并保持安全可用**的形式。 | 原版中的问题 | 造成的后果 | 在此修复 | | --- | --- | --- | | 单个 6,300 行的 `README.md` 是唯一数据源 | Diff 痛苦、合并冲突频发、无法附加元数据 | 每个类别对应一个小型 YAML 文件,由 schema 强制约束 | | `osint_data.json` 是从 README 中*反向抓取出来的* | 数据模型永远不会比 markdown 链接更丰富 | 直接编写的 YAML,包含标签、状态、描述和笔记 | | 抓取器存在双 `requests.get` 漏洞,硬编码 `sleep(6)`,且无错误处理 | 速度慢、脆弱、会静默丢失数据 | 具备超时 + 重试 + 回写功能的并发验证器;仅添加的上游同步 | | 尽管开发笔记中强烈呼吁,但没有链接健康检查 | 失效和被劫持的域名(被篡改为赌博重定向)依然被列出 | `check-links.ts` 每周标记 `dead`/`risky`;死链会获得 Wayback 回退 | | 手写的漂移动画;点击仅触发 `window.open` | 没有真正的布局,没有集群,没有元数据,在约 5k 个节点时变成无法阅读的一团糟 | 真正的 `d3-force`,渐进式展开,搜索/过滤,关联,详情面板 | | 扁平的类别,没有分组或标签 | 难以浏览约 5k 条目 | 15 个图谱分组 + 推断出的标签(`open-source`、`onion`、`official` 等) | | 没有测试,没有 lint,没有 CI | 隐式发布功能衰退 | 单元测试,eslint/prettier,验证 + 部署 + 冒烟工作流 | ## 改变了什么以及为什么 按区域分组。每一行都是一个有目的性的变更及其动机。 ### 数据与内容 | 变更 / 实现 | 原因 | | --- | --- | | **每个类别一个 YAML** (`data/categories/*.yml`) + Zod schema (`data/schema.ts`) | 小巧且易于审查的 Diff;在扁平的 markdown 中无法实现的元数据(标签/状态/描述/存档) | | 通过 `scripts/regroup.ts` 实现 **15 组分类法**(`transport`、`finance`、`media`、`ai`、`search` 等) | 原本的扁平列表将约 108 个类别倾倒进一个桶里;分组驱动了图谱集群和图例 | | **去重** (`scripts/dedupe.ts`) | 删除了 106 个类别内的重复 URL(真正的 bug);跨类别共享的 URL 被有意保留 — 它们为关联功能提供了动力 | | **启发式标签** (`scripts/enrich.ts`) | 从 URL/名称信号中获取低成本的、确定性的标签(`open-source`、`onion`、`official` 等) — 无需 API | | **LLM 描述/标签** (`scripts/llm-enrich.ts` Anthropic, `scripts/llm-enrich-gemini.ts` Gemini) | 真正的单行描述;两者皆是幂等且可恢复的。Gemini 路径为免费层级,因此贡献者无需付费密钥 | | **死链 Wayback 回退** (`scripts/archive-dead.ts`) | 17% 的链接已失效 — 与其删除,不如链接至 `web.archive.org`,使资源保持有用 | ### 浏览体验 | 变更 / 实现 | 原因 | | --- | --- | | **渐进式展开**(分组 → 类别 → 工具,分批,轨道布局) | 同时渲染所有 182 个类别 + 5k 个工具会变成一团无法阅读、无法点击的混乱 | | **URL 关联开关** | 跨类别共享的 URL 成为枢轴链接 — 这正是原始版本暗示过的“情报”视图 | | **全局模糊搜索** (Fuse.js) + **状态/分组过滤器** | 在数千个工具中查找,无需手动展开图谱 | | **下钻侧边栏**,带有过滤框、状态点、存档链接 | 原版只能打开一个 URL;该面板使任何节点都可完全检查和导航 | | **深层链接** (`?q`/`?group`/`?cat`/`?status`/`?corr`) + **移动端布局** + **ARIA** | 可共享的视图,在手机上可用,对屏幕阅读器友好 | ### 工具、CI 与部署 | 变更 / 实现 | 原因 | | --- | --- | | **单元测试** (`node:test`) + **eslint/prettier** | 锁定纯逻辑(`slugify`/`inferGroup`/`parseMarkdown`)和代码风格;作为每个 PR 的门禁 | | **更智能的链接检查**(`FRESH_DAYS` 跳过,针对单个主机的礼貌策略) | 不要每周重新探测 5k 个链接或猛击单个主机;行为更快且更规范 | | **SEO 再生成** (`scripts/seo.ts`: robots/sitemap/llms.txt) + OG meta + favicon | 可发现性,从数据中重新生成,因此永远不会产生偏移 | | **构建守卫** (`scripts/check-build.ts`) + **部署后冒烟测试** (`scripts/smoke.ts`) | 过去“绿色”的部署有时会发布一个空白页;这些操作在缺少 asset / feed 为空 / 实时 URL 错误时会响亮地报警 | | **`.nojekyll`** + 正确的 `configure-pages` 顺序 | GitHub Pages 的 Jekyll 剥离了 Astro 的哈希 asset 目录;base path 必须在构建前设置好 | ## 数据模型 ``` category: Social Media Search slug: social-media-search # unique, kebab-case group: socmint # one of the 15 graph clusters description: ... items: - name: Tool X url: https://example.com tags: [free, account-required] status: active # active | dead | risky | unchecked last_checked: 2026-06-22 archive_url: https://web.archive.org/web/2/https://example.com # set for dead links note: optional ``` Schema 和类型:`data/schema.ts`(唯一数据源,被每个脚本导入)。 ## 命令 ``` npm install npm run dev # local site npm run build # data:build + seo + astro build + build guard npm test # unit tests npm run lint # eslint + prettier check (npm run format to fix) # data pipeline npm run data:validate # schema-check all categories (CI gate) npm run data:build # compile YAML -> public/osint.json npm run data:dedupe # drop within-category duplicate URLs npm run data:regroup # re-apply the group taxonomy npm run data:enrich # heuristic tags + templated descriptions (idempotent) npm run data:archive # add Wayback fallback to dead links (idempotent) # ingestion & health npm run data:sync # add-only pull of new tools from upstream (DRY=1 to preview) npm run links:check # probe URLs, write status back (LIMIT/FRESH_DAYS/CONCURRENCY) # LLM enrichment (选择其一;两者均为 idempotent + resumable) GEMINI_API_KEY=... npm run data:llm-enrich-gemini # free tier, slow, daily cap ANTHROPIC_API_KEY=... npm run data:llm-enrich # paid Batch API, one-shot # ops npm run seo # regenerate robots.txt / sitemap.xml / llms.txt SMOKE_URL=... npm run smoke # check a deployed URL’s page + assets + feed ``` ## 架构 ``` add-only sync ─┐ upstream cheat-sheet ───────────────────┤ ▼ data/categories/*.yml ── validate ──► build-data ──► public/osint.json ▲ ▲ ▲ │ check-links│ archive│ enrich/regroup│ (status / archive_url / tags writeback) └──────┴──────┴──── (all write back into the YAML) ───┘ ▼ src/ (Astro page + D3 graph island) │ build guard ──► GitHub Pages ──► smoke test ``` YAML 是唯一数据源。每个脚本都会读取/写入 YAML;`public/osint.json` 是一个生成的、 被 git 忽略的 feed,供前端获取。 ## 维护计划 该仓库将**内容**与**质量控制**分离开来,因此大部分维护工作都可以自动化。 ### 1. 添加 / 编辑资源(人工) - 编辑 `data/categories/*.yml`,或添加一个带有唯一 `slug` + `group` 的新文件。 - 最低限度需要 `name` + `url`;将 `status` 留为 `unchecked` — 链接检查器会将其填充完整。 - 适用于多个类别的工具应出现在每个类别中 — 共享的 URL 是有意为之的,并 将成为图谱中的关联链接。 - 发起一个 PR。`validate.yml` 将运行 lint + typecheck + 测试 + schema 验证 + 构建。 ### 2. 上游同步(自动,每周 — `sync.yml`,UTC 时间周一 05:00) - 运行 `data:sync` 并使用在上游发现的新工具打开 `bot/upstream-sync`。 - **仅添加**:从不编辑/删除现有条目,因此手动添加的标签/描述/状态得以保留。 新工具以 `status: unchecked` 状态落地。审查其相关性和安全性,然后合并。 ### 3. 链接健康(自动,每周 — `links.yml`,UTC 时间周一 06:00) - 运行 `links:check` 然后 `data:archive`,打开 `bot/link-health` 并带有更新后的 `status` / `last_checked`,以及针对新死链的 Wayback 回退。 - 对 PR 进行分类:清理/替换 `dead`,立即移除 `risky`(已知的不良域名或赌博/篡改 关键词)。将新被劫持的域名录入到 `scripts/check-links.ts` 的 `KNOWN_BAD` 中。 ### 4. 丰富数据(手动,根据需要) - 在批量导入之后,运行 `data:enrich`(启发式)和/或进行一次 LLM 处理以获取真实描述。 - Gemini 路径属于免费层级且可恢复 — 可跨天运行;它会自动跳过已有描述的条目。 ### 5. 发布(自动 — `deploy.yml`) - 合并到 `main` → 构建(带有守卫) → 部署至 Pages → 对实时站点进行**冒烟测试**。 - `public/osint.json` 和 SEO 文件是生成的,从不进行手动编辑(被 git 忽略)。 ### 6. 定期维护 - 在大批量导入后重新运行 `data:dedupe` / `data:regroup` / `data:enrich`(皆是幂等操作)。 - 随着数据的增长,重新审视 `scripts/derive.ts` / `scripts/enrich.ts` 中的分组分类法和标签规则。 **健康信号:** 网站页眉显示实时的 `categories · tools · dead · risky` 计数。上升的 `dead`/`risky` 意味着是时候进行一次清理了。 ## 部署 GitHub Pages 通过 `.github/workflows/deploy.yml` 实现。**Settings → Pages → Source 必须设置为“GitHub Actions.”** `PAGES_BASE` 是从 Pages 配置中设置的(`configure-pages` 在构建*之前*运行,因此 base path 已被固化到 asset URL 中)。两个不太明显的要求,都是惨痛教训后学到的, 现在都由冒烟测试把关: - **`public/.nojekyll`** — 没有它,Pages 会运行 Jekyll 并剥离 Astro 哈希过的 `assets/` 目录, 导致尽管部署“成功”,提供的却是一个空白页。 - **没有 `static_site_generator` 输入项** 在 `configure-pages@v5` 上 — 它在 v5 上会抛出隐晦的 `TypeError`;`.nojekyll` 已经处理好了 Jekyll。 ## 致谢 资源列表衍生自 Jieyab89 的 OSINT Cheat Sheet(由社区维护)。代码为净室重新实现;请参阅[为什么进行重建](#why-this-was-rebuilt)。
标签:Astro, D3.js, ESC4, OSINT, 自动化攻击, 资源导航, 静态站点