{"_id":"@buckeyestudio/toh-credentials-local","name":"@buckeyestudio/toh-credentials-local","dist-tags":{"next":"0.1.1-rc.2","latest":"0.1.1-rc.2"},"versions":{"0.1.1-rc.2":{"name":"@buckeyestudio/toh-credentials-local","description":"File-backed credentials provider ($TOH_HOME/.env under the live process environment) for the TheOpen Harness","version":"0.1.1-rc.2","publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/dustinwloring1988/theopen-harness.git","directory":"packages/credentials/credentials-local"},"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"},"./src/*":"./src/*","./package.json":"./package.json"},"license":"MIT","author":{"name":"buckeyestudio"},"peerDependencies":{"@buckeyestudio/toh-credentials":"^0.1.1-rc.2","@buckeyestudio/toh-launch-environment":"^0.1.1-rc.2","@buckeyestudio/toh-invariants":"^0.1.1-rc.2","@buckeyestudio/toh-atomic-write":"^0.1.1-rc.2","@buckeyestudio/toh-home-paths":"^0.1.1-rc.2","@buckeyestudio/cordis":"^4.0.1"},"dependencies":{"chokidar":"^4.0.3","yaml":"^2.9.0","@buckeyestudio/schemastery":"^3.18.1"},"devDependencies":{"@buckeyestudio/toh-atomic-write":"^0.1.1-rc.2","@buckeyestudio/toh-launch-environment":"^0.1.1-rc.2","@buckeyestudio/toh-credentials":"^0.1.1-rc.2","@buckeyestudio/toh-invariants":"^0.1.1-rc.2","@buckeyestudio/toh-home-paths":"^0.1.1-rc.2","@buckeyestudio/cordis":"^4.0.1"},"_id":"@buckeyestudio/toh-credentials-local@0.1.1-rc.2","bugs":{"url":"https://github.com/dustinwloring1988/theopen-harness/issues"},"homepage":"https://github.com/dustinwloring1988/theopen-harness#readme","_integrity":"sha512-Dvv9s774+zO4aPQkvpPkUICk4pdx9av0gIcayQWO9B5HbNR7X0sWDNOuSr0BBsZj6SU9zXJyRZPNjzp4l5TyGQ==","_resolved":"/home/runner/work/theopen-harness/theopen-harness/dist/npm/buckeyestudio-toh-credentials-local-0.1.1-rc.2.tgz","_from":"file:/home/runner/work/theopen-harness/theopen-harness/dist/npm/buckeyestudio-toh-credentials-local-0.1.1-rc.2.tgz","_nodeVersion":"24.19.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-Dvv9s774+zO4aPQkvpPkUICk4pdx9av0gIcayQWO9B5HbNR7X0sWDNOuSr0BBsZj6SU9zXJyRZPNjzp4l5TyGQ==","shasum":"8cca8f16cfe57af7678dd271e4533cf8643b2eb1","tarball":"https://registry.npmjs.org/@buckeyestudio/toh-credentials-local/-/toh-credentials-local-0.1.1-rc.2.tgz","fileCount":9,"unpackedSize":64999,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEy8nUu2mIbGp9EX5x3bGezRVJXgQzREgsPggwWKkqg4AiBJKAic4bK17pVZ4+strgkI5o3i8osV1iy3zpFSVXrU0A=="}]},"_npmUser":{"name":"buckeyestudio","email":"dustinwloring1988@gmail.com"},"directories":{},"maintainers":[{"name":"buckeyestudio","email":"dustinwloring1988@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/toh-credentials-local_0.1.1-rc.2_1787489212182_0.7737094535559608"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-23T12:46:51.985Z","0.1.1-rc.2":"2026-08-23T12:46:52.312Z","modified":"2026-08-23T12:46:52.570Z"},"maintainers":[{"name":"buckeyestudio","email":"dustinwloring1988@gmail.com"}],"description":"File-backed credentials provider ($TOH_HOME/.env under the live process environment) for the TheOpen Harness","homepage":"https://github.com/dustinwloring1988/theopen-harness#readme","repository":{"type":"git","url":"git+https://github.com/dustinwloring1988/theopen-harness.git","directory":"packages/credentials/credentials-local"},"author":{"name":"buckeyestudio"},"bugs":{"url":"https://github.com/dustinwloring1988/theopen-harness/issues"},"license":"MIT","readme":"# toh-credentials-local\n\n[English](README.md) | 中文\n\n文件型[凭据](../credentials/README.zh.md)提供方：四层来源，一套明确的优先级。\n\n| 层 | 来源 id | 可写 | 优先 |\n|---|---|---|---|\n| 继承的进程环境 | `env` | 否 | 始终优先 |\n| `$TOH_HOME/.credentials.yaml` 文档 | `file` | 是（`set`/`unset`） | 高于两个 `.env` 层 |\n| `<invocation cwd>/.env` | `project-env` | 不在此处 | 高于用户 `.env` |\n| `$TOH_HOME/.env` | `user-env` | 不在此处 | 其余情况 |\n\n启动环境优先，因为按次覆盖（`DEEPSEEK_API_KEY=… toh`、CI 机密、容器 `-e`）代表本次运行的操作者意图——而它无法从进程内部修改，就必须*可见地*只读：`describe()` 报告 `source: 'env', writable: false`，`set`/`unset` 直接拒绝，而不是写下一个读取方永远看不到的变更。\n\n它之下的所有来源优先级都低于受管存储，因此 Models 页写入的密钥会立即生效，即使某个 `.env` 里还留着更旧的密钥。没有存储任何东西时这两层仍会参与解析，`describe()` 会把来源报告为 `project-env` 或 `user-env` 且 `writable: true`——存入一个密钥就会取代它们成为生效来源。\n\n在产品 CLI（命令行界面）下，解析读取的是启动器冻结的[环境快照](../../util/launch-environment/README.zh.md)而不是 `process.env`：只有快照才说得清某个值来自启动 shell 还是来自某个文件。并非由产品 CLI 启动的组合只有继承环境这一层，这让嵌入方保持它们原有的语义。\n\n## 配置\n\n| 字段 | 默认值 | 含义 |\n|---|---|---|\n| `path` | `<harness home>/.credentials.yaml` | 凭据文档位置。 |\n| `tohHome` | `$TOH_HOME` 或 `~/.toh` | `path` 缺省时使用的 harness home。 |\n| `watch` | `true` | 热发布外部编辑。 |\n| `debounceMs` | `100` | watcher 写入稳定窗口。 |\n\n## 文档本身\n\n一个带版本的 YAML 文档，每个键空间一个分节，除此之外别无他物：\n\n```yaml\nversion: 1\n\nrefs:\n  DEEPSEEK_API_KEY: sk-…\n  OPENAI_API_KEY: sk-…\n\nrecords:\n  llm-pi-ai/openai-codex:\n    kind: grant\n    payload:                    # written verbatim; this provider does not interpret it\n      type: oauth\n      access: eyJhbGciOi…\n      refresh: rft_9f8e7d…\n      expires: 1786000000000\n  llm-pi-ai/amazon-bedrock:\n    kind: api-key               # environment values, no key: this route uses an AWS profile\n    env:\n      AWS_PROFILE: prod\n  llm-pi-ai/amazon-bedrock-dev:\n    kind: api-key               # neither: the owner confirmed the ambient credential chain\n```\n\n该文档只存放凭据，因此任何偏离都是拒绝，而不是跳过某个条目——被静默忽略的键读起来就是「我存进去的凭据没有生效」。非 mapping 的根、未知的顶层键、在其空间中不可寻址的键、类型不符的值、空字符串、未知的记录标签或字段、重复键以及格式错误的 YAML 全部失败：启动时明确报错，运行期热重载则告警并保留最后可用快照。\n\n`grant` 的 payload 必须能经受 JSON 往返，读写两个方向都强制。YAML 能拼写出 JSON 没有的值——`.inf`、别名环——拥有者也可能递来 `Date` 或 `bigint`；无论哪种，存储都选择拒绝，而不是存下一个自己无法逐字读回的东西。\n\n发布前的旧布局是没有 `version` 的扁平 mapping。启动时若能精确识别它——可寻址名称对非空字符串标量、且没有文档指令——就在写锁下原地升级：原有各行逐字下沉到 `refs:` 之下，值、注释与拼写逐字节保留。其余任何扁平形态都会被指名拒绝，并给出条目数与唯一需要做的编辑（`version: 1`，条目下沉到 `refs:`）——绝不当作空存储读过去，否则它会以第一次请求认证失败的形式出现，而不是在加载时。热重载从不迁移：运行中被恢复出来的扁平文档只会保住上一份完好快照，直到下次启动。\n\n写入是对已解析文档打补丁而不是重建，因此注释与所有未触及条目的排版都会保留。直接位于某条目上方的注释属于该条目的注解，会随它一起删除。每次写入都先在 [`toh-atomic-write`](../../util/atomic-write/README.zh.md) 的跨进程写锁下重读文档、把此前未观察到的一切发布出去，再在仅属主可访问（`0700`）的目录下以 `0600` 权限原子提交——因此并发写入者、或落在 watcher 防抖窗口内的外部编辑会被并入，而不是被覆盖。磁盘上已经无法解析的文档会让写入失败，而不是覆盖提供方读不懂的内容。\n\n任何字符串值都能往返，包括多行值，因此不会再有条目因为缺少可用引号样式而不可写。空的存储值等于不存在（seam 规则）——这也正是文档中的空字符串被直接拒绝的原因：`unset` 删除键，而不是把它置空。\n\n## 权限\n\n提供方以 `0700` 创建目录，以 `0600` 创建或原子替换文档。它对*读取*同样守住这条界线：在 POSIX 上，只要文档带有任何 group 或 other 权限位，就会在解析其内容之前失败——启动时与每次 reload 都检查——并在错误里给出 `chmod 600` 的修复命令。Windows 没有可检查的 mode，因此在那里跳过该检查而不是伪造它。\n\n## 热重载\n\n外部编辑在快照**整体替换**后按变更引用逐个发布 `credentials/reference-updated`——磁盘上删掉的条目绝不在内存滞留。在 Chokidar 打开目标之前，提供方会对层级最深的现有祖先路径执行 realpath 解析，再拼回缺失的后缀；文件访问和诊断仍使用配置路径，从而避免 Windows 混用 8.3 别名与 libuv 的长格式事件路径。提供方自己的写入按内容识别，只发布属于该次提交的一个事件。运行期文档不可读或无效时保留最后可用快照并告警；文件不存在即空存储；启动时不可读或无效则明确报错。\n\n<a id=\"security-boundary\"></a>\n\n## 安全边界\n\n文档在 `0700` 目录下以 `0600` 权限存放，这挡得住其他 OS 用户，**挡不住**模型。工具进程（bash、文件系统工具）以同一用户身份运行，而已交付的 `workspace-write` 文件策略限制的是修改而非读取，因此它们读这个文件与读该用户拥有的任何其他文件毫无二致；也没有任何沙箱模式会把它单独挑出来。harness 真正守住的更窄：它绝不把该文档的解析后路径交给模型，也绝不把它载入进程环境——这与用户的普通环境层 `$TOH_HOME/.env` 不同（见 [app-boot 的 Harness home 各层](../../boot/app-boot/README.zh.md#profiles)）——因此要拿到这个值，需要刻意去读一条并未交给 agent（智能体）的路径。\n\n这是审慎，不是边界。必须让提供方密钥远离自身 agent 的部署无法靠文件权限做到；OS 钥匙串提供方——一种模型运行所在进程根本无法读取的存储——才是延后的答案，它应当作为平级包与本提供方并列。\n\n## 模型体验\n\n经由消费它的 LLM（大语言模型）适配器间接生效：存储的值为适配器向提供方发出的请求授权，所有模型可见内容均由适配器负责。\n\n#### KV Cache 影响\n\n无直接失效；凭据绝不进入请求前缀。\n\n## 已知限制与暂缓事项\n\n- **同一引用的并发写入是后写胜出**——写锁加读-改-写让并发写入者不会丢掉彼此的条目，但两个写入者编辑同一个引用时仍以较后的写入为准；没有修订检查。\n- **同 UID 进程可以读取该文档**——见[安全边界](#security-boundary)：文件效果沙箱模式不会拒绝读取，OS 钥匙串提供方仍是延后项。\n- **环境变化不可见**：快照在启动时冻结，因此启动之后 export 的变量既不会进入解析，也不会进入 `describe`；要更换来自环境的凭据需要重启。\n- **原子但不具备崩溃持久性**——继承自 `toh-atomic-write`；存储在启动时重新读取。\n","readmeFilename":"README.zh.md","_rev":"1-78580f0bd16b45909056ec91bd9056ee"}