跳转到主要内容

概览

桌面应用(Tauri)

  • 原生桌面应用,支持 macOS、Windows 和 Linux —— 打包完整的仪表盘,并附带一个本地 Node.js sidecar,在本地运行全部 60+ 个 API 处理器
  • OS 钥匙串集成 —— API 密钥存储在系统凭据管理器中(macOS Keychain、Windows Credential Manager),绝不以明文文件形式存储
  • Token 鉴权的 sidecar —— 一个唯一的会话 token 防止其他本地进程访问 localhost 上的 sidecar。每次启动时通过随机化哈希生成
  • 云端回退 —— 当本地 API 处理器失败或缺失时,请求会透明地回退到云端部署(worldmonitor.app),并剥离 Origin 头
  • 设置窗口 —— 专用配置 UI(Cmd+,),包含三个标签页:LLMs(Ollama 端点、模型选择、Groq、OpenRouter)、API Keys(12+ 个数据源凭据,逐键校验)以及 Debug & Logs(流量日志、详细模式、日志文件)。每个标签页运行独立的校验流水线 —— 在 LLMs 标签页保存不会阻塞 API Keys 的校验
  • 自动模型发现 —— 当你在 LLMs 标签页设置 Ollama 或 LM Studio 的端点 URL 时,设置面板会立即查询其可用模型(先尝试 Ollama 原生 /api/tags,再尝试 OpenAI 兼容的 /v1/models),并填充下拉菜单。Embedding 模型会被过滤掉。如果发现失败,会显示一个手动文本输入作为回退
  • 跨窗口密钥同步 —— 主仪表盘和设置窗口运行在独立的 webview 中,具有独立的 JS 上下文。在 Settings 中保存密钥时会写入 OS 钥匙串并广播一个 localStorage 变更事件。主窗口监听此事件并热重载所有密钥,无需重启应用
  • 合并式钥匙串保险库 —— 所有密钥作为一个 JSON blob 存储在单个钥匙串条目(secrets-vault)中,而非每个密钥一个条目。这将每次应用启动时的 macOS Keychain 授权提示从 20+ 次减少到正好 1 次。一次性迁移会读取现有任何单独条目,合并它们,并清理旧格式
  • 详细调试模式 —— 切换流量日志,状态在重启后保持。查看最近 200 个请求,包含时序、状态码和错误详情
  • DevTools 切换 —— Cmd+Alt+I 打开嵌入式 web 检查器进行调试
  • 自动更新检查器 —— 每 6 小时轮询云端 API 检查新版本。显示非侵入式更新徽章,附带直接下载链接,并支持按版本忽略。具备变体感知能力 —— Tech Monitor 桌面应用会链接到正确的 Tech Monitor 发布资产

多平台架构

全部六个变体运行在三个相互协作的平台上:
┌─────────────────────────────────────┐
│          Vercel (Edge)              │
│  60+ edge functions · static SPA    │
│  Proto gateway (24 typed services)  │
│  CORS allowlist · Redis cache       │
│  AI pipeline · market analytics     │
│  CDN caching (s-maxage) · PWA host  │
└──────────┬─────────────┬────────────┘
           │             │ fallback
           │             ▼
           │  ┌───────────────────────────────────┐
           │  │     Tauri Desktop (Rust + Node)   │
           │  │  OS keychain · Token-auth sidecar │
           │  │  60+ local API handlers · br/gzip    │
           │  │  Cloud fallback · Traffic logging │
           │  └───────────────────────────────────┘

           │ https:// (server-side)
           │ wss://   (client-side)

┌──────────────────────────────────────────┐
│        Railway (Relay Server)            │
│  AIS WebSocket · OpenSky OAuth2          │
│  Telegram MTProto (26 OSINT channels)    │
│  OREF rocket alerts (residential proxy)  │
│  Polymarket proxy (queue backpressure)   │
│  ICAO NOTAM · RSS proxy · gzip all resp │
└──────────────────────────────────────────┘
为什么需要两个平台? 一些上游 API(OpenSky Network、CNN RSS、UN News、CISA、IAEA)会主动屏蔽来自 Vercel IP 段的请求,还有一些需要持久连接或 edge function 不支持的协议。Railway 中继服务器作为备用源站,负责处理:
  • AIS 船舶追踪 —— 维护到 AISStream.io 的持久 WebSocket 连接,并将其多路复用到所有已连接的浏览器客户端,避免触发每用户连接限制
  • OpenSky 航空器数据 —— 通过 OAuth2 client credentials 流程进行认证(Vercel IP 在没有 auth token 时会被 OpenSky 返回 403)
  • Telegram 情报 —— 一个 GramJS MTProto 客户端以 60 秒为周期轮询 26 个 OSINT 频道,具备逐频道超时和 FLOOD_WAIT 处理
  • OREF 火箭警报 —— 通过 curl 经住宅代理轮询以色列后方司令部警报系统(Akamai WAF 会屏蔽数据中心的 TLS 指纹)
  • Polymarket 代理 —— 从 Gamma API 拉取,带有并发上游限流(最多 3 个同时请求,队列背压阈值为 20)、在途请求去重,以及 10 分钟缓存,以防止来自 11 个并行标签查询的踩踏
  • ICAO NOTAM 代理 —— 通过中继路由 MENA 机场的 NOTAM 关闭查询,绕过 ICAO API 上对 Vercel IP 的限制
  • GDELT 正面事件 —— 一个 15 分钟的 cron 任务拉取三个主题化的 GDELT GEO API 查询(突破/可再生能源、保护/人道、志愿/慈善),按事件名去重,校验坐标,按类别分类,并以 45 分钟 TTL 写入 Redis。这取代了直接调用 Vercel Edge Function 的方式 —— 后者因 GDELT 约 31 秒的顺序响应时间超过 25 秒的 edge 超时,在 99.9% 的调用中失败。已注册 bootstrap 水合,使 Happy 变体在首次渲染时即有数据
  • RSS feeds —— 代理来自屏蔽 Vercel IP 的域名的 feeds,并带有独立的域名白名单以保证安全。支持条件 GET(ETag/If-Modified-Since)以减少未变更 feeds 的带宽消耗
Vercel edge function 通过 WS_RELAY_URL(服务端,HTTPS)连接到 Railway,而浏览器客户端通过 VITE_WS_RELAY_URL(客户端,WSS)连接。这种分离使中继 URL 可按部署配置,而不会把服务端配置泄露给浏览器。 当客户端接受且负载超过 1KB 时,所有 Railway 中继响应都会进行 gzip 压缩(zlib gzipSync),可减少 JSON 与 XML 响应约 80% 的出口流量。桌面本地 sidecar 现在优先使用 Brotli(br),对于大于 1KB 的负载回退到 gzip,自动设置 Content-EncodingVary: Accept-Encoding

桌面应用架构

Tauri 桌面应用将仪表盘封装在一个原生窗口中(macOS、Windows、Linux),并附带一个本地 Node.js sidecar,运行全部 API 处理器而无需依赖云端:
┌─────────────────────────────────────────────────┐
│              Tauri (Rust)                       │
│  Window management · Consolidated keychain vault│
│  Token generation · Log management · Menu bar   │
│  Polymarket native TLS bridge                   │
└─────────────────────┬───────────────────────────┘
                      │ spawn + env vars

┌─────────────────────────────────────────────────┐
│      Node.js Sidecar (dynamic port)             │
│  60+ API handlers · Local RSS proxy             │
│  Brotli/Gzip compression · Cloud fallback       │
│  Traffic logging · Verbose debug mode           │
└─────────────────────┬───────────────────────────┘
                      │ fetch (on local failure)

┌─────────────────────────────────────────────────┐
│         Cloud (worldmonitor.app)                │
│  Transparent fallback when local handlers fail  │
└─────────────────────────────────────────────────┘

密钥管理

API 密钥存储在操作系统的凭据管理器中(macOS Keychain、Windows Credential Manager)—— 绝不以明文配置文件形式存储。所有密钥都合并到钥匙串中的单个 JSON 保险库条目里,因此无论配置了多少密钥,应用启动都只需要一次 OS 授权提示。 sidecar 启动时,会读取保险库、解析,并作为环境变量注入。空值或仅含空白字符的值会被跳过。密钥也可以在运行时更新而无需重启 sidecar:在 Settings 窗口保存密钥会触发一次 POST /api/local-env-update 调用,热补丁 process.env,使处理器立即取到新值。 校验流水线 —— 当你在 Settings 中输入凭据时,应用会针对实际的提供商 API 进行校验(Groq → /openai/v1/models,Ollama → /api/tags,FRED → GDP 测试查询,NASA FIRMS → 火灾数据拉取等)。网络错误(超时、DNS 失败、主机不可达)被视为软通过 —— 密钥会被保存并附带”无法验证”提示,而不会阻塞。只有来自提供商的明确 401/403 响应才会将密钥标记为无效。这防止了临时网络问题把用户锁在自己的凭据之外。 智能重新校验 —— 保存设置时,校验流水线会跳过自上次成功校验以来未修改的密钥。这避免了当用户只改动一个密钥但已有 15 个已配置且校验通过的密钥时,对提供商 API 产生不必要的往返请求。只有新输入或修改过的密钥才会触发校验请求。 桌面端特有要求 —— 一些功能在桌面端需要的凭据比 Web 端更少。例如,Web 端的 AIS 船舶追踪同时需要中继 URL 和 API 密钥,但桌面 sidecar 在内部处理中继连接,因此只需要 API 密钥。设置面板会根据检测到的平台调整其必填字段显示。

桌面运行时配置 Schema

World Monitor 桌面端使用一个运行时配置 schema,具备按功能切换的开关与密钥支撑的凭据。

密钥

桌面保险库 schema(Rust SUPPORTED_SECRET_KEYS)支持以下 25 个密钥:
  • GROQ_API_KEY
  • OPENROUTER_API_KEY
  • FRED_API_KEY
  • EIA_API_KEY
  • FINNHUB_API_KEY
  • CLOUDFLARE_API_TOKEN
  • ACLED_ACCESS_TOKEN
  • URLHAUS_AUTH_KEY
  • OTX_API_KEY
  • ABUSEIPDB_API_KEY
  • NASA_FIRMS_API_KEY
  • WINGBITS_API_KEY
  • WS_RELAY_URL
  • VITE_WS_RELAY_URL
  • VITE_OPENSKY_RELAY_URL
  • OPENSKY_CLIENT_ID
  • OPENSKY_CLIENT_SECRET
  • AISSTREAM_API_KEY
  • OLLAMA_API_URL
  • OLLAMA_MODEL
  • WORLDMONITOR_API_KEY —— 控制云端回退访问(最少 16 字符)
  • WTO_API_KEY
  • AVIATIONSTACK_API
  • ICAO_API_KEY
  • UCDP_ACCESS_TOKEN

功能 schema

每个功能包含:
  • id:稳定的功能标识符。
  • requiredSecrets:必须存在且有效的密钥列表。
  • enabled:来自运行时设置面板的用户切换状态。
  • available:计算值(enabled && requiredSecrets valid)。
  • fallback:面向用户的降级行为描述。

桌面密钥存储

桌面构建通过 Tauri 命令绑定将密钥持久化到 OS 凭据存储中,底层为 Rust keyring 条目(world-monitor 服务命名空间)。 前端不会以明文文件形式存储密钥。

降级行为

若所需密钥缺失/被禁用:
  • 摘要:Groq/OpenRouter 被禁用,回退到浏览器模型。
  • FRED / EIA / Finnhub:经济、石油分析和股票数据返回空状态。
  • Cloudflare / ACLED:中断/冲突返回空状态。
  • 网络威胁 feeds(URLhaus、OTX、AbuseIPDB):网络威胁图层返回空状态。
  • NASA FIRMS:卫星火灾检测返回空状态。
  • Wingbits:航班增强被禁用,仅保留基于启发式的航班分类。
  • AIS / OpenSky 中继:实时追踪功能被干净地禁用。
  • World Monitor API 密钥:云端回退被阻塞;桌面端仅以本地模式运行。

Sidecar

Sidecar 鉴权

每次应用启动时,通过随机化哈希状态(Rust 标准库的 RandomState)生成一个唯一的 32 字符十六进制 token。该 token 会:
  1. 作为 LOCAL_API_TOKEN 注入 sidecar
  2. 前端通过 get_local_api_token Tauri 命令获取(在首次 API 请求时懒加载)
  3. 作为 Authorization: Bearer <token> 附加到每个本地请求
/api/service-status 健康检查端点免除 token 校验,以支持监控工具。

动态端口分配

sidecar 默认使用端口 46123,但会优雅地处理 EADDRINUSE —— 如果端口被占用(另一个 World Monitor 实例,或任何其他进程),sidecar 会绑定到端口 0 并让 OS 分配一个可用的临时端口。实际绑定的端口会写入一个端口文件(日志目录中的 sidecar.port),Rust 宿主在启动时轮询该文件(100 毫秒间隔,5 秒超时)。前端在运行时通过 get_local_api_port IPC 命令发现端口,runtime.ts 中的 getApiBaseUrl() 是规范的访问器 —— 禁止在前端代码中硬编码端口 46123。CSP 的 connect-src 指令使用 http://127.0.0.1:* 以适配任意端口。

本地 RSS 代理

sidecar 内置了一个 RSS 代理处理器,直接从源域名拉取新闻 feeds,完全绕过云端 RSS 代理。这意味着桌面应用可以加载全部 500+ 个 RSS feeds 而无需任何云端依赖 —— Vercel edge 代理使用的相同域名白名单会在本地强制执行。结合本地 API 处理器,这使桌面应用能够作为一个完全自包含的情报聚合平台运行。

Sidecar 弹性

sidecar 采用多种弹性模式,以在上游 API 降级时维持数据可用性:
  • 出错时返回旧数据 —— 当上游 API 返回 5xx 错误或超时时,sidecar 从其内存缓存中提供最后一次成功响应,而非传播失败。面板会显示旧数据并附带可视的”重试中”指示器,而不是变空白
  • 负缓存 —— 上游失败后,sidecar 会记录一个 5 分钟的负缓存条目,以防止立即重新命中同一故障端点。冷却期间的后续请求会立即收到旧响应
  • 错峰请求 —— 具有严格速率限制的 API(Yahoo Finance)使用顺序请求批处理,请求间延迟 150 毫秒,而非 Promise.all。这会把 10 个并发请求(会触发 HTTP 429)转换为一个保持在速率限制之下的错峰序列
  • 在途请求去重 —— 对同一资源的并发请求(例如多个面板轮询同一端点)被合并为单次上游拉取。第一个请求创建一个 Promise 存储在在途映射中;所有并发请求都等待该单个 Promise
  • 面板重试指示器 —— 当面板的数据拉取失败并重试时,Panel 基类会显示一个非侵入式的”Retrying…”指示器,让用户理解仪表盘正在自我修复而非已损坏

云端回退

当本地 API 处理器缺失、抛出错误或返回 5xx 状态时,sidecar 会透明地将请求代理到云端部署。失败的端点会被标记为 cloudPreferred —— 后续请求会跳过本地处理器并直接访问云端,直到 sidecar 重启。代理前会剥离 Origin 和 Referer 头,以维持服务端到服务端的对等。

可观测性

  • 流量日志 —— 最近 200 个请求的环形缓冲区,包含方法、路径、状态和耗时(毫秒),可通过 GET /api/local-traffic-log 访问
  • 详细模式 —— 通过 POST /api/local-debug-toggle 切换,在 verbose-mode.json 中跨 sidecar 重启持久化
  • 双日志文件 —— desktop.log 捕获 Rust 端事件(启动、密钥注入计数、菜单操作),而 local-api.log 捕获 Node.js 的 stdout/stderr
  • 强制 IPv4 拉取 —— sidecar 补丁 globalThis.fetch 以对所有出站请求强制使用 IPv4。政府 API(NASA FIRMS、EIA、FRED)发布 AAAA DNS 记录,但其 IPv6 端点经常超时。该补丁使用 node:https 并设置 family: 4 以绕过 Happy Eyeballs,避免级联的 ETIMEDOUT 失败
  • DevTools —— Cmd+Alt+I 切换嵌入式 web 检查器

自动更新

桌面应用通过轮询 worldmonitor.app/api/version 检查新版本 —— 启动时一次(延迟 5 秒),之后每 6 小时一次。当检测到较新版本时(semver 比较),会出现一个非侵入式的更新徽章,附带指向 GitHub Release 页面的直接链接。 更新提示可按版本忽略 —— 忽略 v2.5.0 不会抑制 v2.6.0 的通知。更新器具备变体感知能力:Tech Monitor 桌面构建会链接到 Tech Monitor 的发布资产,而非 full 变体。 /api/version 端点会读取最新的 GitHub Release 标签并将结果缓存 1 小时,因此版本检查不会在每次请求时都命中 GitHub API。