{"_id":"@buddhilive/dsh-credentials","name":"@buddhilive/dsh-credentials","dist-tags":{"alpha":"0.1.2-alpha.3","latest":"0.1.2-alpha.3"},"versions":{"0.1.2-alpha.3":{"name":"@buddhilive/dsh-credentials","description":"Abstract credential seam (ctx.credentials): settings carry references to secrets, providers own the values","version":"0.1.2-alpha.3","publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/Buddhilive/buddhi-ai-harness.git","directory":"packages/credentials/credentials"},"type":"module","main":"lib/index.js","types":"lib/types/index.d.ts","exports":{".":{"types":"./lib/types/index.d.ts","default":"./lib/index.js"},"./invariant":{"types":"./lib/types/invariant.d.ts","default":"./lib/invariant.js"},"./types":{"types":"./lib/types/types.d.ts","default":"./lib/types/types.js"},"./src/*":"./src/*","./package.json":"./package.json"},"license":"MIT","peerDependencies":{"@deepseek-ai/cordis":"^4.0.2","@buddhilive/dsh-invariants":"^0.1.2-alpha.3"},"devDependencies":{"@deepseek-ai/cordis":"^4.0.2","@buddhilive/dsh-invariants":"^0.1.2-alpha.3"},"dependencies":{"@buddhilive/dsh-brand":"^0.1.2-alpha.3"},"_id":"@buddhilive/dsh-credentials@0.1.2-alpha.3","bugs":{"url":"https://github.com/Buddhilive/buddhi-ai-harness/issues"},"homepage":"https://github.com/Buddhilive/buddhi-ai-harness#readme","_integrity":"sha512-c6HikOmQ0Xh1rgAlSJBrqiPTiDdCwMs5p9gJXsTOJNmNXlYe83TaramjsOY2Xc/gbb/jmxU9jNyp/dfGESKyPw==","_resolved":"C:\\DevDojo\\Buddhi\\buddhi-ai-harness\\dist\\npm\\buddhilive-dsh-credentials-0.1.2-alpha.3.tgz","_from":"file:C:/DevDojo/Buddhi/buddhi-ai-harness/dist/npm/buddhilive-dsh-credentials-0.1.2-alpha.3.tgz","_nodeVersion":"24.13.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-c6HikOmQ0Xh1rgAlSJBrqiPTiDdCwMs5p9gJXsTOJNmNXlYe83TaramjsOY2Xc/gbb/jmxU9jNyp/dfGESKyPw==","shasum":"8387678ee7871f2f9e1c4d8f9f556a3cad9618a0","tarball":"https://registry.npmjs.org/@buddhilive/dsh-credentials/-/dsh-credentials-0.1.2-alpha.3.tgz","fileCount":13,"unpackedSize":63545,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCAOC6+K5rGl6uzBDHjAWEYMp+9E13sQJ8tfjU0Fxe0EgIhAIHVCU4v2NSajrosWLgg2Exi03UM3OhMMPqIY/yZ4E2M"}]},"_npmUser":{"name":"buddhilive","email":"visitbudkavin@gmail.com"},"directories":{},"maintainers":[{"name":"buddhilive","email":"visitbudkavin@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dsh-credentials_0.1.2-alpha.3_1788164179795_0.08237802451877418"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-31T08:16:19.607Z","0.1.2-alpha.3":"2026-08-31T08:16:19.929Z","modified":"2026-08-31T08:16:20.224Z"},"maintainers":[{"name":"buddhilive","email":"visitbudkavin@gmail.com"}],"description":"Abstract credential seam (ctx.credentials): settings carry references to secrets, providers own the values","homepage":"https://github.com/Buddhilive/buddhi-ai-harness#readme","repository":{"type":"git","url":"git+https://github.com/Buddhilive/buddhi-ai-harness.git","directory":"packages/credentials/credentials"},"bugs":{"url":"https://github.com/Buddhilive/buddhi-ai-harness/issues"},"license":"MIT","readme":"---\ndescription: \"面向用户与维护者的凭据 seam：在不把机密值写进配置的前提下解析、描述或存储凭据——引用值与持久化记录。\"\nkind: \"package-reference\"\n---\n\n# @buddhilive/dsh-credentials\n\n[English](README.md) | 中文\n\n## 概述\n\n`dsh-credentials` 让机密值留在配置之外：API 密钥只存一次，在 settings 或 `cordis.yml` 中按名引用（`DEEPSEEK_API_KEY`），产品在提供方请求需要时提供该值。在这些引用之外，它还保存持久化的凭据记录——按插件组织的条目，例如授权 grant 或提供方环境值——让插件跨重启持有它为自身 id 管理的凭据。轮换后的密钥会作用于紧随其后的下一次请求——无需重启，无需改配置。配置界面能告诉你某个密钥或记录是否已设置、来自哪里、能否修改，而绝不显示值本身。存储空值等于「没有密钥」，因此空白永远不会伪装成已配置的机密；记录的存在本身就是全部事实，一条不含任何值的条目是有意陈述，而不是空白。\n\n## 目录\n\n- [使用本包](#use-this-package)\n- [理解实现](#understand-the-implementation)\n- [进一步探索](#further-exploration)\n- [模型体验](#model-experience)\n- [已知限制与延期工作](#known-limitations-and-deferred-work)\n- [开发备注](#dev-note)\n\n-----\n\n<a id=\"use-this-package\"></a>\n## 使用本包\n\n本包是产品中负责存储与查询机密值的部分：密钥只存一次、处处按名引用，并可在任意时刻读取、检查或移除。它还保存持久化的凭据记录，让插件可以为自身 id 存储、更新或移除它持有的凭据。产品的默认组合已包含凭据存储；自定义组合只需加载本地存储包并给出文件路径。\n\n### 何时使用\n\n只要配置需要与机密值绝缘，就使用凭据存储：需要同步、共享或渲染进配置界面的设置文件，或希望在不改配置的情况下轮换密钥的团队。当插件必须保存没有单一环境变量的凭据——登录流程产生的授权 grant，或提供方环境值——并希望配置界面能列出用户已授权什么时，请使用记录。配置界面能显示某个密钥或记录是否已设置、来自哪里、能否修改——但绝不显示值本身。如果只需要一个固定的环境变量，直接读该变量即可，无需存储。\n\n### 加入你的组合\n\n加载本地存储包并给出文档路径：\n\n```yaml\n- name: '@buddhilive/dsh-credentials-local'\n  config:\n    path: /absolute/path/to/.credentials.yaml\n```\n\n本地存储 README 拥有完整配置面；生成的[配置目录](../../../docs/config-catalog.zh.md#buddhilivedsh-credentials-local)是穷尽式字段清单。\n\n### 存储、检查与移除密钥\n\n```ts\nimport type { Context } from '@deepseek-ai/cordis'\nimport { credentialRef } from '@buddhilive/dsh-credentials'\n\ndeclare const ctx: Context\n\nconst ref = credentialRef('DEEPSEEK_API_KEY')          // POSIX shell identifier, branded\nconst hit = await ctx.credentials.resolve(ref)         // { value, source } | undefined\nconst info = await ctx.credentials.describe(ref)       // { configured, source?, writable } — never the value\nawait ctx.credentials.set(ref, 'sk-…')                 // rejects while a read-only source shadows the ref\nawait ctx.credentials.unset(ref)                       // no-op when absent; same shadowing rule\n```\n\n用 `set` 存储密钥、用 `unset` 移除、用 `describe` 检查状态、在操作需要时用 `resolve` 读取当前值。`describe` 报告密钥是否已设置、来自哪里、能否写入——它绝不返回值。\n\n### 存储、更新与移除记录\n\n插件按 `<scope>/<id>` 寻址每条记录——自身注册名加一个自选 id，例如提供方路由键——并读取、修改或移除它所持有的内容：\n\n```ts\nimport type { Context } from '@deepseek-ai/cordis'\nimport { credentialKey } from '@buddhilive/dsh-credentials'\n\ndeclare const ctx: Context\n\nconst key = credentialKey('llm-pi-ai', 'openai-codex')   // <owner>/<id>, branded\nconst hit = await ctx.credentials.readRecord(key)        // CredentialRecord | undefined\nawait ctx.credentials.describeRecord(key)                // { configured, kind?, writable } — never the value\nawait ctx.credentials.listRecords()                      // [{ key, kind }] — never values\nawait ctx.credentials.modifyRecord(key, async () => ({ kind: 'grant', payload: { token: '…' } }))\nawait ctx.credentials.deleteRecord(key)                  // no-op when absent\n```\n\n`modifyRecord` 是唯一写路径：它让你的变更函数看到写入取得独占那一刻的记录，返回 `undefined` 则保持原状。记录没有空值规则——一条既无 key 也无环境值的记录，陈述的是其拥有者确认了 ambient 认证——配置界面还可以枚举每条记录，显示你已授权什么，并找出已卸载插件留下的记录。\n\n### 在配置中使用密钥\n\nsettings 分节或 `cordis.yml` 条目按名引用密钥，而不是包含密钥本身——例如 LLM（大语言模型）适配器接受 `apiKeyEnv`：\n\n```yaml\napiKeyEnv: DEEPSEEK_API_KEY\n```\n\n需要该密钥的请求使用它当前存储的值，因此轮换密钥会作用于紧随其后的下一次请求——无需重启，无需改配置。\n\n### 可能出错的地方\n\n- **启动环境提供的密钥无法被覆盖**——`DEEPSEEK_API_KEY=… dsh`（或 CI 机密、容器 `-e`）在本轮运行中优先，并被报告为只读；请先在启动 shell 中清除该变量，再存储其他值。\n- **空值无法存储**——存储空字符串会被拒绝；请改为移除密钥。\n- **密钥值绝不会出现在配置界面或诊断信息中**——界面只显示密钥是否已设置、来自哪里、能否修改；值本身留在存储中。\n\n-----\n\n<a id=\"understand-the-implementation\"></a>\n## 理解实现\n\n<details>\n<summary>实现细节——点击展开</summary>\n\n本节解释本包背后的设计决策，并指出实现它们的代码位置；可观察行为已在[使用本包](#use-this-package)中完整说明。\n\n### 设计理念\n\n一条准则，四个推论：\n\n- **配置只携带引用，绝不携带机密。** settings 分节或 `cordis.yml` 条目点名一个凭据；引用背后的值存放在提供方处。设置文档可以放心同步、放心渲染，`describe()` 无需持有值就能回答，轮换机密不触碰任何配置文件。\n- **消费方按操作解析。** 解析是一次按调用读取，无跨操作缓存；这次读取正是热更新机制。\n- **空的存储值等于不存在。** `resolve` 跳过它，`describe` 报告未配置——空白永远不会伪装成已配置的机密。\n- **记录是持久化的，存在即事实。** 记录按 `<scope>/<id>` 存储并跨重启保留；空值规则不适用，因此一条既无 key 也无环境值的 `api-key` 记录是有意陈述，而不是空白。\n- **监听器失败被包含。** `notifyUpdated` 扇出 `credentials/reference-updated`，保证每个监听器都会运行；同步抛出与异步拒绝都会被记录，不改变已提交操作的结果，`INVARIANT` 编码的失败除外——它们在所有监听器运行完毕后重新抛出。\n\n### credentials/reference-updated 事件\n\n`credentials/reference-updated (ref)` 在提供方管理的来源发生已提交变更后触发——`set`、`unset` 或在存储中观察到的外部编辑。进程环境变量的变化不可观测，永不触发。消费方不需要该事件（它们按操作重新解析）；它服务于配置界面刷新「已配置」徽标。\n\n`credentials/record-updated (key)` 在存储记录发生已提交变更后触发——一次确实写入的 `modifyRecord`、一次确实移除的 `deleteRecord`，或在存储中观察到的外部编辑。它保持独立事件，因为两个键文法互斥：一个监听器若在同一事件上同时收到两个空间，将无法分辨主体属于哪一边。\n\n### 记录写入与读取路径\n\n`modifyRecord` 是唯一写路径，因为正确的写入依赖当前值：刷新 token 是「读—决定—替换」，变更函数看到的是写入取得独占那一刻的记录——返回 `undefined` 则保持原状。独占在支持它的底层存储上跨进程成立，这正是防止两个进程同时轮换一个 refresh token、丢掉先写那一个的机制。读取与引用一侧对称，但绝不分层：没有任何东西能遮蔽记录，`grant` 的 payload 会原样返回给其拥有者，因为只有拥有它的插件能解释它。\n\n### 源码地图\n\n| 文件 | 职责 |\n|---|---|\n| [`src/index.ts`](src/index.ts) | Service Definition：`credentialRef`/`credentialKey` 品牌、`ResolvedCredential`/`CredentialRecordInfo`、覆盖两个键空间的抽象提供方、包含式扇出 |\n| [`src/types.ts`](src/types.ts) | 客户端安全类型面：`CredentialRef` 与 `CredentialKey` 品牌、存储记录联合类型、`CredentialInfo` 引用视图、`credentials/reference-updated` 与 `credentials/record-updated` 事件声明 |\n| [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件：`credentials/reference-updated` 只在凭据服务存活时触发 |\n\n### 客户端安全类型\n\n`./types` 子路径出口把事件声明与其点名的 `CredentialRef`、`CredentialKey` 品牌、存储记录联合类型，以及配置界面读取的 `CredentialInfo` 引用视图放在一起，包根继续 re-export 它们。于是 Host 编译面之外的消费方读到的正是 Host 发射的那一份签名，而不必再写一遍。\n\n### 生命周期\n\n服务是提供方注册的 Cordis `Service`：释放挂载 fiber 会移除 `ctx.credentials`。不变式伴生插件检查 `credentials/reference-updated` 绝不在服务存活之前触发——释放后仍有发射意味着提供方把工作泄漏到了 teardown 完全停稳之后。\n\n</details>\n\n-----\n\n<a id=\"further-exploration\"></a>\n## 进一步探索\n\n当包级约定不够用时阅读以下页面。它们从共享子系统词汇逐步进入随附存储与能力架构。\n\n- [凭据子系统参考](../../../docs/subsystems/credentials.zh.md)——`CredentialRef`/`CredentialKey`、按操作解析、对 UI 安全的信息、提供方层与生成的 cordis 接口面。\n- [本地凭据存储](../credentials-local/README.zh.md)——默认本机存储：密钥与记录存放在哪里、环境层如何排序。\n- [能力 seam](../../../docs/capability-seams.zh.md)——本包遵循的 Service Definition / Service Provider / Consumer 拆分。\n\n-----\n\n<a id=\"model-experience\"></a>\n## 模型体验\n\n经由消费方适配器间接生效：适配器解析每个凭据引用，并拥有值所授权的全部模型可见用途。\n\n#### KV Cache 影响\n\n无直接失效；解析出的值绝不进入请求前缀。\n\n## 已知限制与延期工作\n\n<a id=\"known-limitations-and-deferred-work\"></a>\n\n\n这些限制说明本包何时不合适或需要特别注意。它们是当前包约束，不是任务积压。\n\n- **引用不提供枚举**——seam 只回答被问到的引用；配置界面从 settings schema 得知引用集合，对这一半做 `list()` 没有当前消费方。记录出于无 schema 可发现的原因则可枚举。\n- **引用限定为环境变量形状**——单一扁平的 POSIX 标识符命名空间，因为引用同时就是它借以解析的环境变量名。记录使用更丰富的 `<owner>/<id>` 寻址。\n- **进程环境变化不可见**——无法为启动 shell 中改变的变量发出通知；界面只能在自身导航时重新读取 `describe()`。\n- **记录的拥有者就是它的 scope，而没有任何环节核验该 scope 是否已挂载**——seam 存下被交予的内容，并报告它存了什么；识别孤儿是调用方在 `listRecords()` 与拥有该 scope 的注册表之间做的连接，seam 自身没有可供核对的注册表。\n\n<a id=\"dev-note\"></a>\n### 开发备注\n\n<details>\n<summary>维护者的工作上下文——点击展开</summary>\n\n本开发备注是维护者的工作上下文：开放问题与尚未决定的探索方向。它明确不具权威性——已交付的行为与限制以上文和包代码为准。\n\n该 seam 的接口为 keyring、辅助命令与 KMS 后端提供方预留了扩展空间；远端设置提供方永远不必携带机密。目前没有任何一种随附，也没有当前消费方需要它们。\n\n</details>\n","readmeFilename":"README.zh.md","_rev":"1-dad8d1f0e09f641f3d3a1bc09ab3c2a8"}