DSH plugin credentials: CredentialRef and per-call resolve
The DSH plugin credential seam has one unbreakable rule: configuration carries references, never values. Settings sections and cordis.yml entries hold CredentialRef (an environment-variable name) while the actual secret belongs to a provider such as dsh-credentials-local; consumers re-resolve on every operation, so a rotated credential reaches the next request without a restart. The design also enforces a hard rule: an empty stored value is absent everywhere — resolve skips it and describe reports it unconfigured, so a blank can never masquerade as a configured secret. With those two ideas, reads, writes and descriptions all fall into place.
DSH plugin credential baseline: references in config, values in providers
Bottom line: a reference may live in configuration; the value belongs to the provider — that is what makes credentials safe to back up and share.
- Configuration holds references: settings store environment-variable names, and
cordis.ymlentries do the same. - The provider owns values: the local provider reads from the process environment, its managed store and
.envfiles. - Empty means absent: a blank stored value is never treated as configured.
- Audit before committing: make sure no key-shaped string appears in configuration.
- Verify: share the configuration with another machine — references remain, but the value must be configured separately.
DSH plugin's two key spaces: references and records
Bottom line: a reference answers "what is behind this environment-variable name"; a record answers "what does this plugin hold for this id."
| Key space | Question | Layered | Main operations |
|---|---|---|---|
| CredentialRef | What is behind the variable | Yes (env / store / .env) | resolve, describe, set, unset |
| CredentialKey | What the plugin holds for an id | No | readRecord, describeRecord, listRecords, modifyRecord, deleteRecord |
- References cover secrets: model API keys, third-party tokens.
- Records cover grants: plugin-owned authorization such as OAuth.
- References have no enumeration: configuration surfaces learn them from settings schemas; records must be enumerable so a UI can show grants and find orphans.
- Do not mix them: resolving a record returns nothing.
DSH plugin per-operation resolve: why not to cache
Bottom line: re-resolving each operation is how hot rotation works; caching shuts rotation out.
- Call shape:
const resolved = await ctx.credentials.resolve(ref)
if (!resolved) { /* unconfigured: report clearly, never request with an empty string */ }
- LLM adapters are the model: resolve once per model request, so rotation applies to the next one.
- Never cache across operations: the official contract forbids it; per-operation reads are the mechanism.
- Unconfigured is undefined: branch on it instead of falling back to an empty string.
- Verify: change the environment value without restarting and send the next request.
DSH plugin describe and CredentialInfo: why some references are read-only
Bottom line: describe returns facts and never values; writable: false means a read-only source shadows the reference.
interface CredentialInfo {
configured: boolean
source?: string
writable: boolean
}
- UI badges: configured state, supplying layer, and whether writing is possible.
- The local read-only case: references supplied by the process environment report
writable: false; a write would appear to succeed while resolution keeps returning the shadowing value, so the seam rejects it. - Render read-only early instead of letting users click and fail.
- Change notification:
credentials/reference-updatedfires afterset,unsetor an observed external edit; process environment changes are unobservable and never emit. - Consumers do not need the event: per-operation resolution is enough; the event serves the UI.
DSH plugin writes: set/unset and modifyRecord
Bottom line: references use set/unset; records use only modifyRecord, the one safe way to refresh a token.
- Reference writes:
await ctx.credentials.set(ref, value) // empty values are rejected; use unset to remove
await ctx.credentials.unset(ref) // removing an absent reference is a no-op
- Shadowed writes are rejected: both
setandunsetrefuse while a read-only source shadows the reference. - Record writes:
await ctx.credentials.modifyRecord(key, async (current) => next) // undefined leaves it untouched
- Why not read-then-write: two processes refreshing the same token would lose the first write;
modifyRecordholds one lock for the whole read-decide-replace. - Authorization has its own entry:
ctx.authorizationregisters credential-obtaining flows, one attempt per key, and a duplicate registration fails withDUPLICATE_FLOW.
DSH plugin credential troubleshooting
Bottom line: split failures into cannot read, cannot write and cannot see, then check shadowing or flow conflicts.
- Cannot read —
resolvereturns undefined; check the variable name and provider layer. - Cannot write — check
describe().writable(read-only shadowing). - Cannot see it in UI — references are not enumerable; ensure the settings schema declares it.
- Rotation not applied — the plugin cached
resolveresults. - Token refresh lost writes — switch to
modifyRecord. - Repair status, stated honestly: the two key spaces, empty-value rule, per-operation resolve and write refusal semantics follow the official User credentials page and package source notes; confirm signatures against the Cordis API block and your version.
Sources: User credentials, dsh-credentials source, Plugin config forms.
FAQ
A DSH plugin config should carry a credential reference (an environment-variable name), never the secret itself. Settings sections and cordis.yml entries hold CredentialRef values while the provider owns the value, so configuration stays safe to share and back up.
DSH plugin credential resolution is per operation, and caching breaks hot updates: a rotated value would leave you calling with the old secret. Consumers re-resolve at each operation so rotation reaches the next call without a restart.
For DSH plugin credentials, writable false means a read-only source shadows the reference, for example the process environment. Against DSH plugin behavior, a write would appear to succeed while resolution keeps returning the shadowing value, so the seam rejects it and the UI renders it read-only.
DSH plugin credentials have two key spaces: CredentialRef is an environment-variable name with layering, while CredentialKey is a stored record a plugin holds for an id. References cover secrets, records cover authorization grants.
DSH plugin modifyRecord is a serialized read-decide-replace under one lock, holding across processes where the store supports it. A DSH plugin doing read-then-write would lose the update when two processes refresh the same token concurrently.
Related Terms
- CredentialRef
- CredentialRef is a nominal reference to one credential, shaped as a POSIX-style environment-variable name and validated against shell identifier syntax at construction. It lets configuration carry references instead of values.— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/credentials
- CredentialInfo
- CredentialInfo is the credential fact set for configuration UIs: whether it is configured, which layer supplies it, and whether it is writable, with no slot that could carry a value. That is what makes it safe to send across the Remote wire.— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/credentials
- CredentialKey
- CredentialKey identifies a stored credential record a plugin holds for an id, such as an authorization grant. Unlike a reference it has no resolvable environment, so the record's presence is the whole fact.— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/credentials
- modifyRecord
- modifyRecord is the only write path for credential records: a serialized read-modify-write over one record, holding a lock across processes where supported. It makes token refreshes safe from concurrent overwrites.— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/credentials
Sources
- DeepSeek Harness docs — User credentials (CredentialRef, resolve, describe, writes, two key spaces)· deepseek-harness.github.io
- dsh-credentials source (resolve/describe/set/unset and record operations)· deepseek-ai (GitHub)
- DeepSeek Harness docs — Plugin config forms (secret fields and redaction)· deepseek-harness.github.io