|
Download docs/subsystems/credentials.md from SaylorTwift/deepseek-harness: direct link, hf CLI and curl.
- Browser
- Download file 16.5 kB
-
https://huggingface.co/SaylorTwift/deepseek-harness/resolve/main/docs/subsystems/credentials.md
- Command line
-
hf download hf://SaylorTwift/deepseek-harness/docs/subsystems/credentials.md
-
curl -L -o credentials.md https://huggingface.co/SaylorTwift/deepseek-harness/resolve/main/docs/subsystems/credentials.md
16.5 kB
| # User Credentials | |
| English | [δΈζ](credentials.zh.md) | |
| The credential seam of [dsh-credentials](../../packages/credentials/credentials) keeps secrets out of configuration: settings sections and `cordis.yml` entries carry *references* (environment-variable names), providers such as [dsh-credentials-local](../../packages/credentials/credentials-local) own the values, and consumers resolve a reference once per operation β the LLM adapters resolve once per model request, so a rotated credential reaches the very next request without any restart. One seam-wide rule binds every provider: an empty stored value is absent everywhere. | |
| Source: [`packages/credentials/credentials/src/index.ts`](../../packages/credentials/credentials/src/index.ts) | |
| ## Identity | |
| A reference names one credential as a POSIX-style environment-variable name. The brand prevents callers from mixing credential references with other strings passed between packages or processes; construction validates the shell-identifier syntax. | |
| ```ts type-equiv | |
| /** Nominal reference to one credential: a POSIX-style environment-variable name. */ | |
| type CredentialRef = Branded<'CredentialRef'> | |
| ``` | |
| ## Resolution | |
| `resolve(ref)` returns the value with the provider-defined source layer that supplied it, or `undefined` while unconfigured. Consumers re-resolve at each operation and never cache across operations β that per-operation read is the hot-update mechanism. | |
| ```ts type-equiv | |
| /** One resolved credential value and the source layer that supplied it. */ | |
| interface ResolvedCredential { | |
| /** The non-empty secret value. */ | |
| value: string | |
| /** Provider-defined source layer id (the local provider uses `env`, `file`, `project-env`, and `user-env`). */ | |
| source: string | |
| } | |
| ``` | |
| ## Description | |
| `describe(ref)` answers configuration surfaces without ever exposing a value: whether the reference resolves, from which layer, and whether `set` would currently succeed. The local provider reports a reference supplied by the live process environment as `writable: false` β a write would appear to succeed while resolution kept returning the shadowing value, so the seam rejects it and the UI can render the reference read-only up front. | |
| ```ts type-equiv | |
| /** | |
| * Source and writability facts for one reference, safe for configuration UIs β | |
| * never the value. The view has no slot a value could ride in, which is what | |
| * lets the whole read half cross the Remote wire. | |
| */ | |
| interface CredentialInfo { | |
| /** Whether resolving the reference would currently return a value. */ | |
| configured: boolean | |
| /** Source layer currently supplying the value; absent while unconfigured. */ | |
| source?: string | |
| /** Whether the active provider can write this reference. */ | |
| writable: boolean | |
| } | |
| ``` | |
| ## Change commits | |
| `credentials/reference-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 surfaces refreshing a "configured" badge. | |
| <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) β do not edit between markers --> | |
| <a id="cordis-surface"></a> | |
| ## Cordis API | |
| Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnpm run verify-cordis-catalog` in doc-sync; regenerate with `pnpm run gen-cordis-catalog`) β the language sides differ only in locale-specific paired document paths. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.md#dispatch-modes), and the framework-inherited `ctx` API lives in [cordis-api/inherited.md](../cordis-api/inherited.md). | |
| <a id="ctxauthorization--authorizationservice"></a> | |
| ### `ctx.authorization` β `AuthorizationService` | |
| `ctx.authorization`: a registry of credential-obtaining flows, one attempt at a time per key. | |
| ```ts cordis-catalog | |
| /** | |
| * Offer a way to obtain one credential. One flow per key: two plugins | |
| * claiming the same key would each write a record in their own format, and | |
| * whichever ran last would leave the other reading a payload it cannot parse. | |
| * | |
| * @param flow - the key it writes, its label, its methods, and its runner. | |
| * @returns Disposer that withdraws this flow. | |
| * @throws {AuthorizationError} code `DUPLICATE_FLOW` when the key is already claimed. | |
| */ | |
| registerFlow(flow: AuthorizationFlow): () => void | |
| /** | |
| * Every registered flow, for a surface listing what can be authorized. | |
| * @returns one entry per flow, in registration order. | |
| */ | |
| list(): readonly AuthorizationEntry[] | |
| /** | |
| * One registered flow. | |
| * @param key - the credential record to ask about. | |
| * @returns the entry, or undefined when no flow claims that key. | |
| */ | |
| describe(key: CredentialKey): AuthorizationEntry | undefined | |
| /** | |
| * Withdraw the attempt running for a key, if any. Separate from the | |
| * request's own signal because a request/response transport answers a Cancel | |
| * button on a second call, with no handle on the first one's signal. | |
| * @param key - the credential record whose attempt should stop. | |
| */ | |
| cancel(key: CredentialKey): void | |
| /** | |
| * Run one attempt to authorize a key, and report how it ended. | |
| * | |
| * One attempt per key at a time. A second caller is refused rather than | |
| * joined: the two would be prompting different humans through the same flow, | |
| * and the second would answer questions the first was asked. | |
| * | |
| * @param request - the key, the method, the surface, and the cancel signal. | |
| * @returns `authorized` once the flow's record is committed during this | |
| * attempt and observed, or `cancelled` when the human declined or the | |
| * caller withdrew. | |
| * @throws {AuthorizationError} code `NO_FLOW` when nothing claims the key, | |
| * `UNKNOWN_METHOD` when the named method is not one the flow offers, | |
| * `ALREADY_IN_FLIGHT` when an attempt is already running for the key, or | |
| * `NOT_COMMITTED` when the flow resolved without committing a record | |
| * during the attempt. | |
| */ | |
| async begin(request: AuthorizationRequest): Promise<AuthorizationOutcome> | |
| ``` | |
| Source: [`packages/credentials/authorization/src/index.ts`](../../packages/credentials/authorization/src/index.ts) | |
| <a id="ctxcredentials--credentialprovider-abstract-seam"></a> | |
| ### `ctx.credentials` β `CredentialProvider` (abstract seam) | |
| Abstract credential service over two key spaces that answer two questions. | |
| A CredentialRef answers "what is behind this environment-variable name", layered over the process environment, the provider-managed store, and `.env` files. One seam-wide rule binds that half: an empty stored value is absent everywhere β `resolve` skips it, `describe` reports it unconfigured β so a blank never masquerades as a configured secret. | |
| A CredentialKey answers "what credential does this plugin hold for this id". Nothing can layer here β an authorization grant has no environment to be read from β so presence of the record is the whole fact, and modifyRecord is the only write path because a correct write depends on the current value (a token refresh is read-decide-replace under one lock). | |
| ```ts cordis-catalog | |
| /** | |
| * Resolve one reference to its current value. Resolution is per call: | |
| * consumers re-resolve at each operation and must not cache across | |
| * operations β that per-operation read is what makes a changed credential | |
| * reach the next operation without a restart. | |
| * @param ref - the reference to resolve. | |
| * @returns the value and its source, or `undefined` while unconfigured. | |
| */ | |
| abstract resolve(ref: CredentialRef): Promise<ResolvedCredential | undefined> | |
| /** | |
| * Describe one reference for configuration surfaces without exposing the | |
| * value. | |
| * @param ref - the reference to describe. | |
| * @returns configured state, supplying source, and writability. | |
| */ | |
| abstract describe(ref: CredentialRef): Promise<CredentialInfo> | |
| /** | |
| * Durably store one value in the provider-managed writable source. Rejects | |
| * while a read-only source shadows the reference β the write would appear | |
| * to succeed while resolution keeps returning the shadowing value β and | |
| * rejects an empty value (use {@link unset}). | |
| * @param ref - the reference to store. | |
| * @param value - the non-empty secret value. | |
| */ | |
| abstract set(ref: CredentialRef, value: string): Promise<void> | |
| /** | |
| * Remove one reference from the provider-managed writable source; removing | |
| * an absent reference is a no-op. Rejects while a read-only source shadows | |
| * the reference, like {@link set}. | |
| * @param ref - the reference to remove. | |
| */ | |
| abstract unset(ref: CredentialRef): Promise<void> | |
| /** | |
| * Read one stored record. The value is returned as its owner wrote it; a | |
| * {@link GrantRecord} payload is not interpreted on the way out. | |
| * @param key - the record to read. | |
| * @returns the record, or `undefined` while none is stored. | |
| */ | |
| abstract readRecord(key: CredentialKey): Promise<CredentialRecord | undefined> | |
| /** | |
| * Describe one record for configuration surfaces without exposing its value. | |
| * @param key - the record to describe. | |
| * @returns presence, discriminant, and writability. | |
| */ | |
| abstract describeRecord(key: CredentialKey): Promise<CredentialRecordInfo> | |
| /** | |
| * Enumerate every stored record's address and tag. Unlike the reference | |
| * half, which has no enumeration because configuration surfaces learn which | |
| * references exist from settings schemas, records have no such discovery | |
| * path: a surface that cannot list them cannot show what a user is | |
| * authorized for, nor find an orphan left by an uninstalled plugin. | |
| * @returns every stored record, values excluded. | |
| */ | |
| abstract listRecords(): Promise<readonly CredentialRecordEntry[]> | |
| /** | |
| * Serialized read-modify-write over one record β the only write path. | |
| * `mutate` sees the record as it stands at the moment the write is | |
| * exclusive, and returning `undefined` leaves the entry untouched. Exclusion | |
| * holds across processes where the backing store supports it, which is what | |
| * makes a token refresh safe: two processes rotating one refresh token | |
| * concurrently would otherwise lose whichever wrote first. | |
| * @param key - the record to modify. | |
| * @param mutate - receives the current record and returns its replacement, or `undefined` to leave it. | |
| * @returns the record after the write, or the current one when `mutate` declined. | |
| */ | |
| abstract modifyRecord( key: CredentialKey, mutate: (current: CredentialRecord | undefined) => Promise<CredentialRecord | undefined>, ): Promise<CredentialRecord | undefined> | |
| /** | |
| * Remove one record; removing an absent record is a no-op. | |
| * @param key - the record to remove. | |
| */ | |
| abstract deleteRecord(key: CredentialKey): Promise<void> | |
| ``` | |
| Source: [`packages/credentials/credentials/src/index.ts`](../../packages/credentials/credentials/src/index.ts) | |
| <a id="ctxcredentialscontroller--credentialscontroller"></a> | |
| ### `ctx.credentialsController` β `CredentialsController` | |
| Host service backing the generated `ctx.remote.credentials` namespace. It carries every wire obligation the credential seam itself does not: the batch fan-out bound, the field-by-field view projection, the reference-grammar guard, and the refusal mapping. Secret values cross in one direction only β no method here returns one. | |
| ```ts cordis-catalog | |
| /** | |
| * Describe several references for one configuration surface. Batched because | |
| * a settings page describes every reference its rows name at once, and one | |
| * round trip keeps those rows from settling separately. | |
| * @param refs - reference names, at most {@link MAX_DESCRIBE_REFS}; a name outside the grammar | |
| * rejects the whole call as `gateway/bad-request`. | |
| * @returns one view per requested name, keyed by that name. | |
| * @throws RemoteError when the request is invalid or no credential provider is mounted. | |
| */ | |
| @Remote async describe(refs: string[]): Promise<Record<string, CredentialInfo>> | |
| /** | |
| * Store one value from a configuration surface. The value crosses the wire in | |
| * this direction only: no read path returns it. | |
| * @param ref - reference name to store under. | |
| * @param value - the non-empty secret value. | |
| * @throws RemoteError when the request is invalid, no provider is mounted, or the provider refuses the write. | |
| */ | |
| @Remote async set(ref: string, value: string): Promise<void> | |
| /** | |
| * Remove one reference from a configuration surface. | |
| * @param ref - reference name to remove. | |
| * @throws RemoteError when the request is invalid, no provider is mounted, or the provider refuses the write. | |
| */ | |
| @Remote async unset(ref: string): Promise<void> | |
| ``` | |
| Source: [`packages/api/settings-controller/src/credentials.ts`](../../packages/api/settings-controller/src/credentials.ts) | |
| <a id="authorization-events"></a> | |
| ### `authorization/*` events | |
| <a id="authorizationsettled--emit"></a> | |
| #### `authorization/settled` β emit | |
| One authorization attempt has finished and released its key. Fires for every terminal outcome, failures included, so a surface watching a key it did not start (a second browser tab) learns the attempt is over. | |
| ```ts cordis-catalog | |
| /** | |
| * One authorization attempt has finished and released its key. Fires for | |
| * every terminal outcome, failures included, so a surface watching a key it | |
| * did not start (a second browser tab) learns the attempt is over. | |
| * @mode emit | |
| * @param key - the credential record the finished attempt was authorizing. | |
| * @param settlement - how it ended, including the `failed` case its caller sees as a thrown error. | |
| */ | |
| 'authorization/settled'(key: CredentialKey, settlement: AuthorizationSettlement): void | |
| ``` | |
| Source: [`packages/credentials/authorization/src/index.ts`](../../packages/credentials/authorization/src/index.ts) | |
| <a id="credentials-events"></a> | |
| ### `credentials/*` events | |
| <a id="credentialsrecord-updated--emit"></a> | |
| #### `credentials/record-updated` β emit | |
| Committed change to a stored credential record: a `modifyRecord` that wrote, a `deleteRecord` that removed, or an external edit observed in storage. Separate from `credentials/reference-updated` because the two key grammars are disjoint β a listener that received both on one event could not tell which space a subject belongs to. Listener failures are contained on the same terms as `credentials/reference-updated`. | |
| ```ts cordis-catalog | |
| /** | |
| * Committed change to a stored credential record: a `modifyRecord` that | |
| * wrote, a `deleteRecord` that removed, or an external edit observed in | |
| * storage. Separate from `credentials/reference-updated` because the two key | |
| * grammars are disjoint β a listener that received both on one event could | |
| * not tell which space a subject belongs to. Listener failures are | |
| * contained on the same terms as `credentials/reference-updated`. | |
| * @param key - the record whose stored value changed. | |
| * @mode emit | |
| */ | |
| 'credentials/record-updated'(key: CredentialKey): void | |
| ``` | |
| Source: [`packages/credentials/credentials/src/types.ts`](../../packages/credentials/credentials/src/types.ts) | |
| <a id="credentialsreference-updated--emit"></a> | |
| #### `credentials/reference-updated` β emit | |
| Committed change to a provider-managed credential source: a `set`, an `unset`, or an external edit observed in storage. Ambient process-environment changes are not observable and never emit. Listener failures are contained and logged β a sync throw and an async rejection alike β without changing the committed operation's outcome, except `INVARIANT`-coded failures, which rethrow after every listener ran; that rethrow reaches the emitter only from synchronous listeners, so invariant checks on this event must not be async functions. | |
| ```ts cordis-catalog | |
| /** | |
| * Committed change to a provider-managed credential source: a `set`, an | |
| * `unset`, or an external edit observed in storage. Ambient | |
| * process-environment changes are not observable and never emit. Listener | |
| * failures are contained and logged β a sync throw and an async rejection | |
| * alike β without changing the committed operation's outcome, except | |
| * `INVARIANT`-coded failures, which rethrow after every listener ran; | |
| * that rethrow reaches the emitter only from synchronous listeners, so | |
| * invariant checks on this event must not be async functions. | |
| * @param ref - the reference whose stored value changed. | |
| * @mode emit | |
| */ | |
| 'credentials/reference-updated'(ref: CredentialRef): void | |
| ``` | |
| Source: [`packages/credentials/credentials/src/types.ts`](../../packages/credentials/credentials/src/types.ts) | |
| <!-- END GENERATED cordis-surface --> | |