CodexBar 声明式自定义 Provider 设计:运行时身份接缝、映射契约与安全边界 CodexBar 声明式自定义 Provider 设计运行时身份接缝、映射契约与安全边界【免费下载链接】CodexBarShow usage stats for OpenAI Codex and Claude Code, without having to login.项目地址: https://gitcode.com/GitHub_Trending/co/CodexBar本文基于 CodexBar 仓库中的设计文档 docs/custom-provider-design.md 展开。该文档是一份已接受的架构与安全边界设计用于评估 issue #1735 提出的运行时自定义 Provider 身份方案。文章将完整继承原文档的配置契约、映射语言、威胁模型与实施计划并结合当前仓库中已落地的ProviderInstanceID运行时身份接缝、ProviderEndpointOverrideValidator、ProviderHTTPClient等源码进行纵深印证。读者阅读完本文后将理解为什么自定义 Provider 不能简单复用 LLM Proxy / LiteLLM 的改个 base URL思路、声明式 MVP 的 JSON 配置长什么样、映射语言如何受控地抽取响应字段以及 CodexBar 如何在引入可配置端点 不可信响应两条新信任边界时守住密钥与网络安全的底线。一、决策摘要为什么自定义 Provider 不是一个小扩展CodexBar 目前内置了数量众多的第一方 ProviderOpenAI、Claude、Gemini、Cursor、Kimi、DeepSeek、Ollama 等它们拥有编译期确定的UsageProvider身份、描述符、实现、请求形态与响应解码器。文档明确指出A declarative provider can reduce one-off integrations, but it is not a small extension of LLM Proxy or LiteLLM.LLM Proxy 与 LiteLLM 虽然接受配置化的 base URL但它们的请求路径、鉴权头、解码逻辑与快照映射仍然是Provider 专属的 Swift 代码对应仓库中的 LLMProxyUsageFetcher.swift、LiteLLMUsageFetcher.swift 等实现。自定义 Provider 之所以不同是因为它引入了两条全新的信任边界配置文件决定 CodexBar 把密钥发送到哪里端点由用户配置而非编译期常量不可信的响应数据控制用户可见的用量、费用与身份字段响应解码不再由第一方 Swift 解码器把关。因此已接受的方向是先分离运行时 Provider 实例身份与封闭的UsageProvider枚举再推进一个纯配置 仅 GET HTTP JSON的 MVP。同时明确否决加一个.custom枚举 case的做法——多个自定义 Provider 若共享同一个枚举值会在缓存、状态栏项、历史记录、小组件与设置中相互冲突。二、当前约束为什么需要共享接缝而不是平行 UI 路径原文档列出的一组现状约束恰恰解释了动态身份必须走共享接缝的原因我们逐条与仓库源码对照ProviderConfig.id目前直接解码为UsageProvider。在仓库中这一约束已被部分解除ProviderConfig的id已解码为ProviderInstanceID见 ProviderConfigCoding.swift并在 CodexBarConfig.swift 的顶层解码时通过isKnownProviderInstance过滤掉未知实例 ID。ProviderDescriptorRegistry为UsageProvider.allCases的每个值引导恰好一个描述符ProviderImplementationRegistry用穷举的UsageProviderswitch 构造实现。用量、错误、状态、历史、图标、设置与菜单状态在整款应用中都以UsageProvider为键。设置侧边栏已把 Provider 面板选择持久化为provider:UsageProvider.rawValue且仍假设每个编译期 Provider 一个面板。ProviderEndpointOverrideValidator已提供加固过的 HTTPS 主机解析与显式回环 HTTP 模式ProviderHTTPClient将重定向限制在同源 HTTPS。文档给出的结论是动态身份需要共享接缝即运行时实例身份独立化而不是为自定义 Provider 另开一条平行 UI 路径。三、提议的 MVP 契约3.1 配置版本 2 带标签的 Provider 定义MVP 保留现有 provider 数组引入配置版本 2并以kind标签区分第一方定义与自定义定义。现有条目仍是第一方定义自定义条目拥有用户自选的稳定实例 ID 和固定的实现种类custom-http-json。原文档给出的完整示例配置如下{ version: 2, providers: [ { id: acme-gateway, kind: custom-http-json, label: Acme Gateway, enabled: true, request: { method: GET, url: https://gateway.example.com/v1/quota, authentication: { type: bearer } }, mapping: { primary: { usedPercent: { path: quota.used_pct }, resetsAt: { path: quota.reset_at, dateFormat: iso8601 }, windowMinutes: { path: quota.window_minutes } }, cost: { used: { path: spend.usd }, currency: USD, period: Approx. spend }, identity: { organization: { path: plan.name }, loginMethod: { literal: api } } } } ] }字段规则逐条如下均为原文档定义字段规则id小写 ASCII 字母、数字与连字符1–64 字符在全部第一方与自定义 Provider 中唯一label必填trim 后 1–80 字符MVP 使用内置通用图标无自定义 SVG/文件图标request.method仅允许GETMVP 不支持 POST/PUT/PATCH/DELETE、请求体、刷新变更或多个端点request.authentication仅none、bearer、x-api-key三种密钥永不内联。已鉴权实例只读取其派生变量CODEXBAR_CUSTOM_INSTANCE_ID_API_KEYID 大写、连字符替换为下划线。定义不能指定任意环境变量或任意 headerbearer 固定用Authorizationx-api-key 固定用X-API-Keymapping.primary可选。存在时要求usedPercent与remainingPercent二选一。可选字段在其路径缺失或为 null 时省略mapping.cost存在时要求usedcurrency为三位大写字母字面量period为受限字面量。缺失的 limit 映射为零与现有稀疏成本快照一致mapping.identity可选受限字符串。快照身份由配置的 Provider 实例 ID决定而非响应数据整体有效性既无速率窗口数据也无成本数据的定义为无效3.2 映射语言受控的 typed dot-path 子集映射语言刻意不使用 JSONPath、jq、JavaScript、谓词或字符串插值语法为path segment *(. segment / [ index ]) segment ALPHA *(ALPHA / DIGIT / _ / -) index 1*DIGIT类型规则每个目标字段决定其接受的类型数字强制转换仅接受 JSON 数字与有限的数字字符串。百分比在拒绝 NaN 与无穷大之后钳制到 0–100。日期必须显式指定格式iso8601、unix-seconds或unix-milliseconds。展示字符串会被 trim 并做长度限制。缺失的可选路径不会让整个快照失败但值存在而类型错误会让快照失败。同时禁止通配符、递归下降、过滤器、算术、模板求值与用户提供的代码多窗口数组与聚合属于后续设计工作。硬性限制必须在遍历前校验每定义最多 16 个映射叶子每路径最长 256 UTF-8 字节且最多 32 个组件每段最长 64 个 ASCII 字符数组索引范围 0–4095响应 JSON 嵌套深度上限 64映射出的展示字符串最长 256 UTF-8 字节。此外在物化 JSON 之前必须直接对受限的响应字节流做迭代式、字符串感知的预检结构扫描防止恶意深层嵌套耗尽调用栈。3.3 网络与密钥边界这是整个设计的安全核心原文档的规定可以归纳为以下几条硬规则公开源必须 HTTPS。设置中配置的源仅在回环地址、RFC 1918 IPv4、IPv4 link-local、IPv6 unique-local/link-local 或.local目标上允许 HTTP且必须走独立的键入式审批门。本地例外可以带鉴权——因为自托管的 LLM Proxy 与 LiteLLM 部署与它们现有 Swift Provider 支持的 bearer 行为一致。拒绝 URL 中的 user info 与 fragment。扩展ProviderEndpointOverrideValidator而不是另写第二个 URL 解析器。这一点与仓库源码高度吻合现有 ProviderEndpointOverrideValidator.swift 已实现normalizedHTTPSURL拒绝 user/password、拒绝百分号编码主机与编码分隔符、isLoopbackHostlocalhost、::1、127.x、isPrivateNetworkHost10/8、172.16–31、192.168、169.254、.local、IPv6 ULA/link-local并区分了allowAnyHTTPSHost与providerOwnedOnly两种策略。自定义 Provider 使用专用的ProviderHTTPClient配置拒绝一切重定向。对比共享客户端 ProviderHTTPClient.swift 中ProviderHTTPRedirectGuardDelegate的安全策略——它只允许同 scheme 同 host 同归一化端口的 HTTPS 重定向而自定义 Provider 的策略更严格3xx 一律视为错误这样密钥就始终绑定在被审批的原始源上不会被重定向带到其他主机。本地审批记录任何自定义 Provider 抓取之前必须存在一条把实例 ID、完整归一化请求 URL、origin 与鉴权类型绑定在一起的本地审批记录存储在 Provider 配置之外。首次使用需要显式的应用内或交互式 CLI 确认并展示精确归一化 URL 与鉴权字段无头环境默认失败关闭fail closed。禁止导入或批量审批。回环、IP 字面量、.local与显式私有目标要求手动键入归一化 URL而非点击按钮。任一绑定字段变更都会使审批失效。此门同样适用于无鉴权的回环 HTTP。密钥绝不插值进 URL、路径、查询、请求体、label、映射、诊断或日志派生环境变量只在审批通过、Provider 启用且抓取开始时解析不枚举环境。专用 ephemeral 会话使用URLSessionConfiguration.ephemeralhttpCookieStorage nil、httpShouldSetCookies false、urlCredentialStorage nil、urlCache nil且采用忽略本地缓存的重新加载策略不与第一方 Provider 共享会话。专用 challenge handler 只允许正常的服务端信任评估取消客户端证书或 HTTP 鉴权 challenge。响应有界发送Accept-Encoding: identity拒绝非 identity 的Content-Encoding并在 URL loading 解码之后、JSON 物化之前强制流式 1 MiB 上限超限即取消任务总超时 15 秒并校验 JSON content-type。只接受 2xx 响应。错误文本可以包含状态码与受限的通用摘要绝不包含请求头或原始响应体。自定义 Provider 的响应数据不得进入Provider 状态轮询、cookie 导入、OAuth、Keychain、token accounts、浏览器自动化与 CLI 子进程路径。自定义定义只能是本地配置无远程目录、无下载定义、无配置 URL。3.4 运行时身份接缝ProviderInstanceID引入一个表示单个已配置实例的字符串值ProviderInstanceID同时保留UsageProvider作为第一方 Provider 的编译期实现种类ProviderDefinition firstParty(instanceID, UsageProvider, ProviderConfig) customHTTPJSON(instanceID, CustomHTTPJSONConfig)需要迁移的运行时字典、持久化键、ProviderIdentitySnapshot.providerID以及代表已启用 Provider 实例的身份访问器一律迁移到ProviderInstanceID。第一方实例 ID 保留其当前 rawValue从而保留既有配置与历史记录Provider 专属 fetcher 继续接收UsageProvider而自定义 fetcher 只接收其校验后的自定义定义Provider 专属身份载荷仍按编译期实现种类键控共享的 organization 与 login-method 字段则归属于 Provider 实例。这一接缝必须独立落地并先有特征化测试然后才能开发自定义网络路径否则自定义 Provider 会被塞进穷举的第一方 switch或与其他自定义实例共享状态。从仓库现状看该接缝已经落地ProviderInstanceID.swift 定义了ProviderInstanceID其isValid恰如文档所要求——1–64 个 UTF-8 字节仅小写 ASCII 字母、数字与连字符a-z/0-9/-且为RawRepresentable、Hashable、Codable、SendableUsageProvider.instanceID使第一方 Provider 的实例 ID 等于其现有 rawValue。ProviderIdentitySnapshot.swift 的providerID字段已是ProviderInstanceID?并提供了scoped(to instanceID:)方法。ProviderConfig.id在 ProviderConfigCoding.swift 中按ProviderInstanceID解码。CodexBarConfig.swift 在读取配置时校验ProviderInstanceID(rawValue:)并用isKnownProviderInstance过滤未知实例。四、威胁模型原文档给出的威胁模型表是这份设计的核心安全论证完整继承如下威胁必需缓解措施共享或恶意配置外泄密钥专用按实例变量密钥解析前单独的全 URL/鉴权审批配置变更使审批失效禁用重定向端点把鉴权重定向到其他主机把一切重定向视为失败共享配置静默探测或改变 GET 目标独立的全 URL 审批之前无网络访问任何 URL 变更使审批失效无批量审批对显式本地/私有目标提高确认门槛恶意 JSON 造成 CPU 或内存压力1 MiB 上限有界深度与路径长度无递归表达式请求超时响应注入误导性或巨型菜单文本类型化目标数值边界字符串 trim 与长度限制配置的身份优先密钥或响应通过诊断泄漏脱敏的请求描述错误/日志中不含头与原始响应体两个自定义 Provider 互相覆盖运行时与持久化全程使用稳定的ProviderInstanceID键配置静默改变第一方行为带标签的定义版本化解码器重复/保留 ID 拒绝迁移测试范围外说明不负责防御用户显式批准过的请求 URL包括后来解析到本地/私有地址的公共主机名——审批即授予该 origin 对已批准 URL 的网络权威确认文本必须明确声明这一点。CodexBar 仍必须限制住服务的响应且绝不泄露无关凭据。五、明确的非目标原文档用一组Explicit non-goals划定了 MVP 的边界任何实现都不得越过用于创建/编辑自定义 Provider 的设置 UI。完整 JSONPath、jq、脚本、插件、变换、算术或模板。POST/PUT/PATCH/DELETE、请求体、刷新型变更或多个端点。任意请求头、cookie、OAuth、浏览器会话、Keychain 发现、文件密钥引用或内联密钥。自定义 SVG/文件图标、下载资源或远程 Provider 清单。状态页发现、事件通知、聊天/模型 API、成本日志扫描、小组件或 token accounts。速率窗口数组、跨响应连接、分页、聚合或 Provider 专属特例。把未知第一方 Provider ID 重新解释为自定义 Provider 的兼容性垫片。六、实施切片五个可独立评审的 PR原文档把实现拆成五个互不混合的切片并强调身份迁移与任意网络能力绝不能合并到一次变更里身份接缝新增ProviderInstanceID在不改变行为的前提下迁移配置/运行时/持久化键补充解码、历史、启用、菜单、CLI 与小组件的特征化测试。纯求值器仅用 fixture 数据新增配置类型、校验器、dot-path 解析器、类型化强制转换与UsageSnapshot映射。有界传输新增 URL/鉴权策略与可注入 HTTP 传输证明重定向、超时、大小、content-type、状态与脱敏行为。配置与 CLI 集成版本 2 迁移、codexbar config validate、本地审批记录与交互式审批命令、诊断、自定义 Provider 的 CLI 输出测试中不得出现真实凭据。应用集成通过既有共享 Provider UI 完成通用元数据/图标、刷新生命周期、菜单渲染、持久化与禁用/错误状态。七、启用功能前必须证明的事项原文档规定了一组Required proof任何 PR 合并前都要满足配置 v1 往返与 v1→v2 迁移保留每一个既有 Provider 条目。重复、保留、畸形与冲突的实例 ID 均校验失败。多个自定义实例的快照、错误、历史、菜单选择与持久化相互隔离。URL 表覆盖 HTTPS、user info、fragment、回环 HTTP、私有/公共 HTTP、IPv4/IPv6、端口与重定向。鉴权测试证明密钥只到达目标 header且永不进入 URL、错误、fixtures、快照或日志。审批测试证明首次使用与每次绑定字段变更都在网络或环境访问之前失败关闭一个实例不能复用另一个实例的审批或派生密钥变量UI/CLI 证据覆盖精确 URL 展示、无批量审批以及回环/IP 字面量/.local/显式私有目标的键入式确认。映射测试覆盖缺失/null 路径、数组、错误类型、日期格式、有限数字强制、每个数值/路径/深度/数量边界、迭代深度拒绝与字符串限制。传输测试覆盖超时、取消、解码后响应上限、压缩响应拒绝、content type、非 2xx 与 3xx且不依赖真实网络。传输隔离测试证明环境 cookie 与 URL 凭据既不被发送也不被持久化缓存响应不被复用。源盲 CLI 证明fixture 端点 隔离配置产生预期的用量、成本、身份与脱敏失败输出。每个实现 PR 的make test、make check、结构化自动审阅与精确头 CI 全部通过。八、已接受的负责人决策与实施门禁原文档记录了五项负责人决策是后续实现必须遵守的边界声明式 Provider 支持值得承担运行时身份迁移与长期版本化 schema 支持的成本。MVP 仅允许在同样的独立审批门下使用私有网络 HTTP含归一化 origin 的键入式确认公共 origin 一律 HTTPS。捆绑的第一方代码可跳过交互式审批但仅限 LLM Proxy 与 LiteLLM——它们的现有 Swift 实现已授予相同目标权威。派生按实例环境变量 本地 URL/鉴权审批是 MVP 唯一的密钥来源Keychain 存储延后初始设计不得暗示或保留第二条密钥路径。MVP 支持一个主速率窗口成本与身份可选多窗口与聚合语义不在范围内。第一个集成面仅限 CLI应用设置与菜单集成要等共享运行时在无 Provider 专属旁路的情况下接受动态身份。最后是实施门禁原文的要点在独立的身份迁移、纯求值器、有界传输、审批流程及其必需证明以可独立评审的变更落地之前保持自定义 Provider 网络能力关闭。如果某个实现无法守住这条边界就停下来——而不是交付单个.custom槽位、一条平行 UI 路径或一个兼容性回退。九、当前实施状态接缝已落地声明式 MVP 被本地插件路径取代原文档在开头明确记录了当前状态Implementation status: theProviderInstanceIDruntime identity seam has landed. By owner decision, the local JavaScript/TypeScript plugin path now supersedes this documents declarative dot-path MVP. The network, secret, approval, redirect, response-bound, and runtime-identity requirements in the threat-model table remain binding for plugins.翻译过来即ProviderInstanceID运行时身份接缝已经落地本文第二章、第三章.4 引用的源码即为证据经负责人决定本地 JavaScript/TypeScript 插件路径取代了本文档的声明式 dot-path MVP而威胁模型表中的网络、密钥、审批、重定向、响应边界与运行时身份要求对插件依然具有约束力。因此在阅读本设计文档时需要注意它是一份已接受的设计边界accepted design boundary定义了一个有界 MVP本身并未授权运行时联网或实现该功能声明式的kind: custom-http-json配置形态第三章示例是历史提案当前仓库中实现自定义能力的主路径是本地 JS/TS 插件相关代码位于 Sources/CodexBarCore/Plugins如UserProviderPlugins.swift、ProviderPluginRuntime.swift等但文档的安全约束——尤其是密钥只到已审批的 origin、拒绝重定向、1 MiB 响应上限、脱敏错误、按实例身份隔离——依然是评估任何动态 Provider 身份的准绳。若要在当前仓库中进一步验证这套边界可以顺次阅读定义实例身份与校验规则的 ProviderInstanceID.swift、按实例 ID 解码的 ProviderConfigCoding.swift、配置顶层过滤未知实例的 CodexBarConfig.swift、加固 URL/主机解析的 ProviderEndpointOverrideValidator.swift以及控制重定向与 HTTP 传输的 ProviderHTTPClient.swift。【免费下载链接】CodexBarShow usage stats for OpenAI Codex and Claude Code, without having to login.项目地址: https://gitcode.com/GitHub_Trending/co/CodexBar创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考