# Conflicts: # .agents/notes/implemented/architecture/2026-06-11-content-block-vocabulary.i18n.yaml # .agents/notes/implemented/architecture/2026-06-11-content-block-vocabulary.zh.md # .agents/notes/implemented/architecture/2026-06-18-shared-persistence-write-coordinator.i18n.yaml # .agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.i18n.yaml # .agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.zh.md # .agents/notes/implemented/bug-fix/2026-07-29-pnpm-setup-runner-isolation.i18n.yaml # .agents/notes/implemented/bug-fix/2026-07-29-pnpm-setup-runner-isolation.zh.md # .agents/notes/implemented/bug-fix/2026-08-18-request-image-payload-bound.i18n.yaml # .agents/notes/implemented/bug-fix/2026-08-18-request-image-payload-bound.zh.md # .agents/notes/implemented/feature/2026-07-06-sandbox.i18n.yaml # .agents/notes/implemented/feature/2026-07-06-sandbox.zh.md # .agents/notes/implemented/feature/2026-07-16-persistent-pty-sessions.i18n.yaml # .agents/notes/implemented/feature/2026-07-21-cross-session-references.i18n.yaml # .agents/notes/implemented/feature/2026-07-21-cross-session-references.zh.md # .agents/notes/implemented/feature/2026-07-22-web-multimodal-image-input-and-durable-attachments.i18n.yaml # .agents/notes/implemented/feature/2026-07-22-web-multimodal-image-input-and-durable-attachments.zh.md # .agents/notes/implemented/feature/2026-07-27-web-subagent-conversations.i18n.yaml # .agents/notes/implemented/feature/2026-07-31-permission-default-for-new-sessions.i18n.yaml # .agents/notes/implemented/feature/2026-08-03-web-search-source-scroll.i18n.yaml # .agents/notes/implemented/feature/2026-08-03-web-search-source-scroll.zh.md # .agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.i18n.yaml # .agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.zh.md # .agents/notes/implemented/feature/2026-08-05-agent-teams.i18n.yaml # .agents/notes/implemented/feature/2026-08-05-agent-teams.zh.md # .agents/notes/implemented/feature/2026-08-11-workspace-sidebar-order-and-folding.i18n.yaml # .agents/notes/implemented/feature/2026-08-15-product-subagent-noninteractive-permissions.i18n.yaml # .agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.i18n.yaml # .agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.zh.md # .agents/notes/implemented/process/2026-07-22-evidence-based-larger-hosted-runners.i18n.yaml # .agents/notes/implemented/process/2026-07-22-evidence-based-larger-hosted-runners.zh.md # .agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.i18n.yaml # .agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.zh.md # .agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.i18n.yaml # .agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.zh.md # .agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.i18n.yaml # .agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.zh.md # .agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.i18n.yaml # README.i18n.yaml # README.zh.md # docs/architecture.i18n.yaml # docs/architecture.zh.md # docs/development.i18n.yaml # docs/development.zh.md # docs/persistence-catalog.i18n.yaml # docs/persistence-catalog.zh.md # docs/subsystems/README.i18n.yaml # docs/subsystems/README.zh.md # docs/subsystems/agent-team.i18n.yaml # docs/subsystems/agent-team.zh.md # docs/subsystems/client-modules.i18n.yaml # docs/subsystems/client-modules.zh.md # docs/subsystems/commands.i18n.yaml # docs/subsystems/commands.zh.md # docs/subsystems/persistence.i18n.yaml # docs/subsystems/persistence.zh.md # docs/subsystems/session-reference.i18n.yaml # docs/tool-catalog.i18n.yaml # docs/tool-catalog.zh.md # docs/user/guide/providers.i18n.yaml # docs/user/guide/providers.zh.md # packages/README.i18n.yaml # packages/README.zh.md # packages/bundle/web-app/README.i18n.yaml # packages/bundle/web-app/README.zh.md # packages/client/README.i18n.yaml # packages/client/README.zh.md # packages/client/connection/README.i18n.yaml # packages/client/connection/README.zh.md # packages/client/ui-conversation/README.i18n.yaml # packages/client/ui-conversation/README.zh.md # packages/client/ui-primitives/README.i18n.yaml # packages/client/ui-primitives/README.zh.md # packages/client/ui-sidebar/README.i18n.yaml # packages/client/ui-sidebar/README.zh.md # packages/client/ui-workspace/README.i18n.yaml # packages/client/ui-workspace/README.zh.md # packages/context/README.i18n.yaml # packages/context/README.zh.md # packages/core/agent-loop/README.i18n.yaml # packages/credentials/README.i18n.yaml # packages/credentials/README.zh.md # packages/experimental/agent-team/README.i18n.yaml # packages/experimental/agent-team/README.zh.md # packages/experimental/tool-agent-team/README.i18n.yaml # packages/experimental/tool-agent-team/README.zh.md # packages/host/frontend-static/README.i18n.yaml # packages/host/frontend-static/README.zh.md # packages/host/webserver/README.i18n.yaml # packages/host/webserver/README.zh.md # packages/interaction/commands/README.i18n.yaml # packages/interaction/commands/README.zh.md # packages/plan/plan-mode/README.i18n.yaml # packages/plan/plan-mode/README.zh.md # packages/sandbox/sandbox-local/README.i18n.yaml # packages/sandbox/sandbox-local/README.zh.md # packages/session/README.i18n.yaml # packages/session/README.zh.md # packages/session/session-persistence-sqlite/README.i18n.yaml # packages/session/session-persistence-sqlite/README.zh.md # packages/session/session-projection-cache/README.i18n.yaml # packages/session/session-projection-cache/README.zh.md # packages/shell/tool-pwsh/README.i18n.yaml # packages/shell/tool-pwsh/README.zh.md # packages/subagent/subagent-codex/README.i18n.yaml # packages/subagent/subagent-codex/README.zh.md # packages/subagent/subagent/README.i18n.yaml # packages/subagent/subagent/README.zh.md # packages/web/tool-web/README.i18n.yaml # packages/web/tool-web/README.zh.md # scripts/snapshots/translation-prompt-v4/request-response.expected.json
7.9 KiB
dsh-credentials-local
English | 中文
文件型凭据提供方:四层来源,一套明确的优先级。
| 层 | 来源 id | 可写 | 优先 |
|---|---|---|---|
| 继承的进程环境 | env |
否 | 始终优先 |
$DSH_HOME/.credentials.yaml 文档 |
file |
是(set/unset) |
高于两个 .env 层 |
<invocation cwd>/.env |
project-env |
不在此处 | 高于用户 .env |
$DSH_HOME/.env |
user-env |
不在此处 | 其余情况 |
启动环境优先,因为按次覆盖(DEEPSEEK_API_KEY=… dsh、CI 机密、容器 -e)代表本次运行的操作者意图——而它无法从进程内部修改,就必须可见地只读:describe() 报告 source: 'env', writable: false,set/unset 直接拒绝,而不是写下一个读取方永远看不到的变更。
它之下的所有来源优先级都低于受管存储,因此 Models 页写入的密钥会立即生效,即使某个 .env 里还留着更旧的密钥。没有存储任何东西时这两层仍会参与解析,describe() 会把来源报告为 project-env 或 user-env 且 writable: true——存入一个密钥就会取代它们成为生效来源。
在产品 CLI(命令行界面)下,解析读取的是启动器冻结的环境快照而不是 process.env:只有快照才说得清某个值来自启动 shell 还是来自某个文件。并非由产品 CLI 启动的组合只有继承环境这一层,这让嵌入方保持它们原有的语义。
配置
| 字段 | 默认值 | 含义 |
|---|---|---|
path |
<harness home>/.credentials.yaml |
凭据文档位置。 |
dshHome |
$DSH_HOME 或 ~/.dsh |
path 缺省时使用的 harness home。 |
watch |
true |
热发布外部编辑。 |
debounceMs |
100 |
watcher 写入稳定窗口。 |
文档本身
一个带版本的 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
该文档只存放凭据,因此任何偏离都是拒绝,而不是跳过某个条目——被静默忽略的键读起来就是「我存进去的凭据没有生效」。非 mapping 的根、未知的顶层键、在其空间中不可寻址的键、类型不符的值、空字符串、未知的记录标签或字段、重复键以及格式错误的 YAML 全部失败:启动时明确报错,运行期热重载则告警并保留最后可用快照。
grant 的 payload 必须能经受 JSON 往返,读写两个方向都强制。YAML 能拼写出 JSON 没有的值——.inf、别名环——拥有者也可能递来 Date 或 bigint;无论哪种,存储都选择拒绝,而不是存下一个自己无法逐字读回的东西。
发布前的旧布局是没有 version 的扁平 mapping。启动时若能精确识别它——可寻址名称对非空字符串标量、且没有文档指令——就在写锁下原地升级:原有各行逐字下沉到 refs: 之下,值、注释与拼写逐字节保留。其余任何扁平形态都会被指名拒绝,并给出条目数与唯一需要做的编辑(version: 1,条目下沉到 refs:)——绝不当作空存储读过去,否则它会以第一次请求认证失败的形式出现,而不是在加载时。热重载从不迁移:运行中被恢复出来的扁平文档只会保住上一份完好快照,直到下次启动。
写入是对已解析文档打补丁而不是重建,因此注释与所有未触及条目的排版都会保留。直接位于某条目上方的注释属于该条目的注解,会随它一起删除。每次写入都先在 dsh-atomic-write 的跨进程写锁下重读文档、把此前未观察到的一切发布出去,再在仅属主可访问(0700)的目录下以 0600 权限原子提交——因此并发写入者、或落在 watcher 防抖窗口内的外部编辑会被并入,而不是被覆盖。磁盘上已经无法解析的文档会让写入失败,而不是覆盖提供方读不懂的内容。
任何字符串值都能往返,包括多行值,因此不会再有条目因为缺少可用引号样式而不可写。空的存储值等于不存在(seam 规则)——这也正是文档中的空字符串被直接拒绝的原因:unset 删除键,而不是把它置空。
权限
提供方以 0700 创建目录,以 0600 创建或原子替换文档。它对读取同样守住这条界线:在 POSIX 上,只要文档带有任何 group 或 other 权限位,就会在解析其内容之前失败——启动时与每次 reload 都检查——并在错误里给出 chmod 600 的修复命令。Windows 没有可检查的 mode,因此在那里跳过该检查而不是伪造它。
热重载
外部编辑在快照整体替换后按变更引用逐个发布 credentials/reference-updated——磁盘上删掉的条目绝不在内存滞留。在 Chokidar 打开目标之前,提供方会对层级最深的现有祖先路径执行 realpath 解析,再拼回缺失的后缀;文件访问和诊断仍使用配置路径,从而避免 Windows 混用 8.3 别名与 libuv 的长格式事件路径。提供方自己的写入按内容识别,只发布属于该次提交的一个事件。运行期文档不可读或无效时保留最后可用快照并告警;文件不存在即空存储;启动时不可读或无效则明确报错。
安全边界
文档在 0700 目录下以 0600 权限存放,这挡得住其他 OS 用户,挡不住模型。工具进程(bash、文件系统工具)以同一用户身份运行,而已交付的 workspace-write 文件策略限制的是修改而非读取,因此它们读这个文件与读该用户拥有的任何其他文件毫无二致;也没有任何沙箱模式会把它单独挑出来。harness 真正守住的更窄:它绝不把该文档的解析后路径交给模型,也绝不把它载入进程环境——这与用户的普通环境层 $DSH_HOME/.env 不同(见 app-boot 的 Harness home 各层)——因此要拿到这个值,需要刻意去读一条并未交给 agent(智能体)的路径。
这是审慎,不是边界。必须让提供方密钥远离自身 agent 的部署无法靠文件权限做到;OS 钥匙串提供方——一种模型运行所在进程根本无法读取的存储——才是延后的答案,它应当作为平级包与本提供方并列。
模型体验
经由消费它的 LLM(大语言模型)适配器间接生效:存储的值为适配器向提供方发出的请求授权,所有模型可见内容均由适配器负责。
KV Cache 影响
无直接失效;凭据绝不进入请求前缀。
已知限制与暂缓事项
- 同一引用的并发写入是后写胜出——写锁加读-改-写让并发写入者不会丢掉彼此的条目,但两个写入者编辑同一个引用时仍以较后的写入为准;没有修订检查。
- 同 UID 进程可以读取该文档——见安全边界:文件效果沙箱模式不会拒绝读取,OS 钥匙串提供方仍是延后项。
- 环境变化不可见:快照在启动时冻结,因此启动之后 export 的变量既不会进入解析,也不会进入
describe;要更换来自环境的凭据需要重启。 - 原子但不具备崩溃持久性——继承自
dsh-atomic-write;存储在启动时重新读取。