Machine-produced by `pnpm run rescope-vendor --apply` plus the regeneration it prints: `pnpm install` for the lockfile, `pnpm run gen-third-party-notices`, `verify-translation-pairing --write` for the touched bilingual pairs, `gen-doc-graphs`, and one typert snapshot whose ids embed character offsets. `pnpm run rescope-vendor --check` verifies the result. Renames nine vendored packages (cordis, cosmokit, schemastery and the six @cordisjs plugins) and every reference that resolves them: manifest names and dependency keys, module specifiers including declare-module merges, cordis.yml plugin names, tsconfig paths, every Markdown fence, and `docs/` prose. Directory names, upstream versions, and dependency ranges are unchanged, so vendor/README.md still reads as an upstream snapshot; its manifest table gains an upstream-name column so THIRD_PARTY_NOTICES keeps MIT attribution pointed at each fork's origin. The tutorial tier follows the rename end to end: its yaml fences named plugins the Loader can no longer resolve, its `ts ignore-check` fences disagreed with the compiled fences beside them, and its prose quoted both. The contracts that told readers to keep upstream names — the root convention and the vendoring cookbook's tree comment and manifest invariant — now say to rescope instead. Two rules read `@deepseek-ai/` as "another workspace plugin": the client bundle purity gate now names the vendored libraries a browser bundle inlines, and the files where a bare `cordis` is an agent-preset id keep that product data.
49 lines
3.5 KiB
Markdown
49 lines
3.5 KiB
Markdown
# dsh-credentials
|
|
|
|
English | [中文](README.zh.md)
|
|
|
|
Credential Service Definition (`ctx.credentials`). One doctrine, three consequences:
|
|
|
|
**Configuration carries references to secrets, never the secrets.** A settings section or `cordis.yml` entry says `apiKeyEnv: DEEPSEEK_API_KEY`; the value behind that reference lives with a credential provider. So the settings document stays safe to sync and to render in a configuration UI, `describe()` can answer "is this configured, where from, can I write it" without ever holding a value, and rotating a secret touches no configuration file.
|
|
|
|
**Consumers resolve per operation.** `resolve(ref)` is called at the start of each operation (the LLM adapters resolve once per model request) and never cached across operations — that read is what makes a changed credential reach the very next request without restarting any plugin.
|
|
|
|
**An empty stored value is absent.** Everywhere: `resolve` skips it, `describe` reports it unconfigured. A blank can never masquerade as a configured secret.
|
|
|
|
## Surface
|
|
|
|
```ts
|
|
import type { Context } from '@deepseek-ai/cordis'
|
|
import { credentialRef } from '@deepseek-ai/dsh-credentials'
|
|
|
|
declare const ctx: Context
|
|
|
|
const ref = credentialRef('DEEPSEEK_API_KEY') // POSIX shell identifier, branded
|
|
const hit = await ctx.credentials.resolve(ref) // { value, source } | undefined
|
|
const info = await ctx.credentials.describe(ref) // { configured, source?, writable } — never the value
|
|
await ctx.credentials.set(ref, 'sk-…') // rejects while a read-only source shadows the ref
|
|
await ctx.credentials.unset(ref) // no-op when absent; same shadowing rule
|
|
```
|
|
|
|
`credentials/updated (ref)` fires after a committed change to a provider-managed source — a `set`, an `unset`, or an external edit observed in storage. Ambient process-environment changes are not observable and never emit. Consumers do not need the event (they re-resolve per operation); it exists for configuration UIs refreshing a "configured" badge.
|
|
|
|
The shadowing rule on `set`/`unset` is deliberate fail-loud: when a read-only source (the live process environment, in the local provider) currently supplies the reference, a write would appear to succeed while resolution keeps returning the shadowing value — the seam rejects instead, and `describe().writable` lets a UI render the reference read-only up front.
|
|
|
|
## Providers
|
|
|
|
[`dsh-credentials-local`](../credentials-local/README.md) layers the inherited process environment over its managed `$DSH_HOME/.credentials.yaml` document, with the launcher's project and user `.env` layers as fallbacks. The seam shape leaves room for keyring-, helper-command-, and KMS-backed providers; a remote settings provider never needs to carry secrets.
|
|
|
|
## Model Experience
|
|
|
|
Indirectly, through the consuming LLM adapters: a resolved value authorizes their provider requests, and the adapter owns every model-visible surface.
|
|
|
|
#### KV Cache effect
|
|
|
|
No direct invalidation; credentials never enter a request prefix.
|
|
|
|
## Known Limitations and Deferred Work
|
|
|
|
- **No enumeration** — the seam answers questions about references it is given; configuration surfaces learn the references from settings schemas, so a `list()` has no current consumer.
|
|
- **References are environment-variable-shaped** — one flat POSIX-identifier namespace until a provider needs richer addressing.
|
|
- **Process-environment changes are invisible** — no event can fire for them; a UI only re-reads `describe()` on its own navigation.
|