description: "面向用户与维护者的文件型凭据提供方:选择、配置或排查本地凭据存储及其环境分层。"
English | 中文
dsh-credentials-local 把 API 密钥和其他机密保存在 harness home 下的私有文件中。你可以通过配置界面保存凭据,也可以直接编辑文件;变更会自动重载,保存的值也会跨重启保留。凭据查找采用固定优先级:启动环境优先,其次是存储文件、项目 .env 和 harness home 的 .env;新保存的值会立即覆盖 .env 中的旧值。只有你的 OS 用户能读取该文件,但 agent(智能体)的工具进程以同一用户身份运行,因此该存储无法向 agent 隔离机密。
本包为组合提供本地凭据存储:API 密钥与其他机密只需保存一次,之后每个按名引用它们的请求都会用到。常用路径是显式的:加载存储、通过配置界面或 ctx.credentials 保存密钥,然后在需要时由产品解析。
把它作为默认本地存储:产品的基础组合会加载它,你通过配置界面保存的密钥会立即生效。当部署必须让提供方密钥远离自身 agent 时选择其他存储——文件权限做不到这一点,因为 agent 的工具进程以你的 OS 用户身份运行(见「谁能读取该文件」)。
- name: '@deepseek-ai/dsh-credentials-local'
config:
path: /absolute/path/to/.credentials.yaml
| 字段 | 默认值 | 含义 |
|---|---|---|
path |
<harness home>/.credentials.yaml |
凭据文件所在位置 |
dshHome |
$DSH_HOME 或 ~/.dsh |
path 缺省时使用的 harness home |
watch |
true |
文件在磁盘上变化时自动重载 |
debounceMs |
100 |
变化后等待这么久再重载,单位为毫秒 |
生成的配置目录完整列出了所有受支持字段及其 JSDoc,是这些信息的真源。
用 set 保存密钥、用 unset 移除、用 describe 检查密钥是否已配置——与凭据 API 提供的操作相同:
import type { Context } from '@deepseek-ai/cordis'
import { credentialRef } from '@deepseek-ai/dsh-credentials'
declare const ctx: Context
const ref = credentialRef('DEEPSEEK_API_KEY')
await ctx.credentials.set(ref, 'sk-…') // save
await ctx.credentials.describe(ref) // { configured, source?, writable } — never the value
await ctx.credentials.unset(ref) // remove
你保存的密钥会被下一个按名引用它的请求使用;describe 报告它是否已设置、来自哪里、能否写入——绝不返回值本身。记录也持久化在同一文件中:插件按 <owner>/<id> 寻址一条记录,并用 seam 的记录操作(readRecord、describeRecord、listRecords、modifyRecord、deleteRecord)管理它。
密钥按一个固定顺序解析——先有值的位置胜出:
| 位置 | 可写? | 优先于 |
|---|---|---|
你启动时的环境(DEEPSEEK_API_KEY=… dsh) |
否 | 一切 |
| 存储文件 | 是(set/unset) |
两个 .env 文件 |
项目的 .env(<invocation cwd>/.env) |
不在此处 | 主目录 .env |
主目录的 .env($DSH_HOME/.env) |
不在此处 | 无 |
启动环境优先,因为按次覆盖——DEEPSEEK_API_KEY=… dsh、CI 机密、容器 -e——代表本次运行的明确意图;它无法从产品内部修改,因此被报告为只读,写入会被拒绝。其他一切来源都输给存储文件,这正是你保存的密钥会立即生效的原因,即使某个 .env 里还留着更旧的密钥;没有存储任何东西时,那两个 .env 层会参与解析。环境层是启动时拍摄的启动器环境快照,因此启动之后才导出的变量不会被看到。
带版本的 YAML 文档,每个键空间一个分节,除此之外别无他物:
version: 1
refs:
DEEPSEEK_API_KEY: sk-…
OPENAI_API_KEY: sk-…
records:
llm-pi-ai/openai-codex:
kind: grant
payload: # written verbatim; this provider does not interpret it
type: oauth
access: eyJhbGciOi…
refresh: rft_9f8e7d…
expires: 1786000000000
llm-pi-ai/amazon-bedrock:
kind: api-key # environment values, no key: this route uses an AWS profile
env:
AWS_PROFILE: prod
llm-pi-ai/amazon-bedrock-dev:
kind: api-key # neither: the owner confirmed the ambient credential chain
你可以直接编辑该文件——存储会自动重载并接收变更,包括你删除的密钥或记录。refs 按环境变量名存放密钥值;records 按 <owner>/<id> 存放各插件的凭据,每条都带 api-key 或 grant 标签,其中 grant 的 payload 由存储逐字保留,因为只有它的拥有者能解释。产品写入时会保留注释与未触及条目的排版;直接位于某条目上方的注释属于该条目的注解,会随它一起删除。文件只存放凭据,因此任何其他内容都会被明确拒绝,而不是被静默忽略:非 mapping 的根、未知的顶层键、在其键空间内不可寻址的键、类型错误或空的值、未知的记录标签或字段、重复键以及格式错误的 YAML 都会在启动时失败;运行期热重载时则保留最后可用内容并告警。
密钥的值可以是任意文本,包括多行值——不需要任何引号技巧。空值等于「没有密钥」,这正是文件中的空字符串被拒绝的原因:移除密钥是删除它,而不是把它置空。grant 的 payload 必须经受 JSON 往返,进出两个方向都会强制这一点,因此存储会拒绝无法逐字读回的值。如果磁盘上的文件已无法解析,保存会失败,而不是覆盖产品读不懂的内容。
只有你的 OS 用户能读取该文件:产品以仅属主可访问的权限创建它,在 POSIX 上还会拒绝加载任何其他用户可读的文件——错误会提示你运行 chmod 600。Windows 没有可检查的 mode,因此在那里跳过该检查而不是伪造它。agent 不是另一个用户:它的工具进程以你的身份运行,因此它们读这个文件与读你拥有的任何其他文件毫无二致。产品绝不把文件路径交给 agent,也绝不把文件载入环境,因此要拿到某个值,需要刻意去读一条并未交给 agent 的路径。这是审慎,不是边界:必须让提供方密钥远离自身 agent 的部署无法靠文件权限做到。
DEEPSEEK_API_KEY=… dsh 在本轮运行中优先,保存或移除它都会被拒绝。请先在启动 shell 中清除该变量。当提供方级约定不够用时阅读以下页面。它们从 seam 约定逐步进入环境快照、原子写入原语与启动期环境层。
resolve、describe、set、unset、记录操作与 seam 的更新事件。CredentialRef、按操作解析、对 UI 安全的 CredentialInfo、提供方层。process.env。.env 载入快照与 process.env。经由 ctx.credentials 的消费方间接生效:消费方拥有存储值所启用的全部模型可见行为。
无直接失效;存储值绝不进入请求前缀。
这些限制说明本提供方何时不合适,或何时需要特别的运维注意。它们是当前包约束,不是任务积压。
describe;要更换来自环境的凭据需要重启。dsh-atomic-write;存储在启动时重新读取。