Repository navigation
Settings & Credentials
The harness keeps user configuration (settings.yaml) and secrets
(.credentials.yaml) strictly separate — settings hold references, the credential
store holds literals, and nothing crosses that line.
| File | Holds | Permissions |
|---|---|---|
$DSH_HOME/settings.yaml |
Provider profiles, shell budgets, tool config, persona — everything user-tunable | normal |
$DSH_HOME/.credentials.yaml |
The literal API keys | owner-only (0600) |
$DSH_HOME/profiles/<name>/ |
Profile manifests + patch layers | — |
DSH_HOME overrides the default ~/.dsh for everything above.
Every plugin contributes its own namespace — the Settings page renders them from
their declared schemas, and the same namespaces are the keys in settings.yaml:
| Namespace | Owns |
|---|---|
llm-deepseek |
The direct DeepSeek route: models, capacities, baseURL, output cap |
llm-pi-ai |
All other providers: one profile per route, apiKeyEnv, models, compat switches |
| shell namespaces | Command budgets: timeouts, output caps, spill limits, kill grace |
| tool namespaces | Per-tool defaults |
Writes travel as path operations (set/unset on a field path) with an
expectedRevision, so two surfaces editing simultaneously can't clobber each other — a
stale writer gets a settings-conflict answer, not a silent overwrite.
Credentials are addressed by reference (credentialRef), not by value:
- Settings say
apiKeyEnv: DEEPSEEK_API_KEY— a name, not a secret. - The credential store resolves the reference to the literal at request time.
- A key typed into the Models page lands in
.credentials.yaml; the settings document only ever sees the reference. - The subprocess layer scrubs credential-shaped values from any environment handed to a child process.
- Removing a key makes it unresolvable by design — no dangling half-credentials.
Environment facts (ambient credentials, launch-provided values) resolve in a defined
order so a stored reference beats a stale ambient value, and a provider's own ambient
discovery (AWS credential chains, gcloud, OAuth state) still works when it should —
the auth context answers env() from the credential seam first, then the process
environment.
Every settings mutation bumps the namespace revision. The check happens at the front
of the write queue — not at call time — so a writer holding a snapshot that a
predecessor already superseded is rejected with settings-conflict instead of racing.
Queued writes chain per-namespace, and one rejected write never poisons the queue for
the next caller.
- Changing model/provider config takes effect on the next request — no restart.
- Every value is validated against its declared schema on write; invalid values are refused with the path that failed.
- Read-only namespaces (pinned by deployment) render as such in the UI and refuse mutations.