Repository navigation
Releases: GooDAnDReaDY/dsh-key-rotation
Release list
v0.8.72 [DSH v0.2.1-rc.2]
Release v0.8.72 [DSH v0.2.1-rc.2]
Security release resolving issue #516.
Why this release
Addresses a security vulnerability where LAN/Tailscale clients proxied to loopback by dsh-lanmode without an Origin header could bypass source authorization on mutating endpoints, and makes /auto-import fail closed on invalid body payloads.
What changed
- Fail-closed write-origin guard (#516): Mutating HTTP methods (
POST,PUT,PATCH,DELETE) without anOriginheader are rejected with403 Forbiddenunless positively identified as the Desktop shell (@deepseek-ai/dsh-desktop/inUser-AgentANDdsh-auth-inCookie) or a matching same-originReferer. - Fail-closed
/auto-import(#516): Invalid JSON returns400 Bad Requestinstead of defaulting to importing all discovered keys. Auto-import requires an explicitrefsarray orall: true. The Settings card client explicitly sends{ all: true }. - Desktop Shell identification: Extracted
isDesktopShellRequest(req)inlib/pool.jsmatching official Desktop rendererforwardWebRequestbehavior. - Test suite: Full regression coverage added (
test/issue-516-write-origin-guard.test.mjs, 958/958 passing tests).
Verification
npm test: 958/958 pass, 0 failnpm run check: 205 JS files syntax cleanpreflight.sh: FAIL=0- Package size: 232 kB packed (limit 262 kB)
Commit correspondence
- Gitea commit:
5dcfbc0 - GitHub mirror commit:
2e8cfe6cb867ff5040caee38f9767bcc2b6ae15d
v0.8.68
v0.8.67 [DSH v0.2.1-alpha.2]
Features
- Full Pool Configuration Export & Import (#504):
- Masked export: exports topology and pools with masked credentials (
sk-...cdef) and hidden webhook secrets, safe for bug reports and sharing. - Full backup export with AES-256-GCM encryption: optional passphrase encrypts configuration with PBKDF2 (100,000 iterations) using standard
node:crypto. - Config import route
POST /dsh-key-rotation/config-import: supports file upload, raw JSON, or encrypted payload with passphrase decryption. - Diff preview mode:
dryRun: truereturns summary of created/updated/removed providers and keys count before applying. - Interactive UI in Settings card: modal drawers for Export (masked vs full, optional passphrase) and Import (file upload, merge vs replace modes, diff preview, apply).
- Masked export: exports topology and pools with masked credentials (
- Quick Key Actions in Chat Header Popover (#499):
- Extended
KeyRotationHeaderChippopover: displays active provider and key tail (...abcd) for each configured pool. - Interactive inline action buttons:
- ⏭️ Skip: advances active key cursor to the next healthy candidate in the pool without leaving chat.
- 🔄 Reset: immediately resets failure counts, cooldowns, and circuit breaker for the active provider.
- ⏸️ Pause: toggles pause state on the currently active key for immediate fault isolation.
- Endpoint
POST /dsh-key-rotation/quick-action: secure loopback-validated operational route executing skip, reset, and pause commands with audit telemetry events.
- Extended
Commits & Verification
- Gitea commit:
1f85ba151805607a79bef510eff7b2b6d8c698e8 - GitHub mirror:
e8a92cf40ead95030cf7d7e16764f471c37a8040 - Tests: 934 passing, Preflight:
FAIL=0 WARN=3.
v0.8.66 [DSH v0.2.1-alpha.2]
Features
- Dynamic Reset Scheduler Based on Rate-Limit Headers (#498):
- Exact cooldown alignment: parses upstream
retry-after,x-ratelimit-reset-*, andanthropic-ratelimit-*-resetheaders upon rate-limit failures, setting precise cooldownfailedUntil = now + dynamicCooldownMs(+250ms safety jitter, bounded bymaxCooldownMs) withresetReason = 'header'. - Immediate unref recovery timer: schedules an exact unref
setTimeoutwake-up for the penalised key right when the upstream window rolls over. - Exposes
resetReasonandheaderResetMsin/dsh-key-rotation/statusand WebUI countdown badge.
- Exact cooldown alignment: parses upstream
- Upstream Balance Inspector & Credit Tracking (#495):
- Multi-provider balance checker (
lib/balance-inspector.js): queries live account balances and remaining credits for DeepSeek, SiliconFlow, OpenRouter, and OpenAI. - Balance ops route
GET/POST /dsh-key-rotation/balance(lib/ops-balance.js): queries cached balances or triggers immediate live balance inspection across provider keys. - Pre-emptive empty balance protection: keys with confirmed zero balance enter a soft pause (
EMPTY_BALANCE_PAUSED), auto-resuming when refreshed with positive balance. - Persistent balance state: cached in
pool.state.balancesand persisted vialib/persistence.js. - WebUI integration: interactive
Check Balancesaction button in provider footer and real-time balance badge pills on key cards.
- Multi-provider balance checker (
Commits & Verification
- Gitea commit:
933589eee5d59d794b770fbeea72d5b26833bbde - GitHub mirror:
94259fcf4ce58391790ec2edf78409296e42d07c - Tests: 927 passing, Preflight:
FAIL=0 WARN=3.
v0.8.64 [DSH v0.2.1-alpha.2]
What's Changed
- Permanent Auth Pause & Granular Error Policies (#501):
- Differentiated error actions: upstream authentication failures (HTTP 401, 403,
invalid_api_key,unauthorized) immediately place the failing key into a permanent auth pause state (pausedRefs.add,pauseReasons.set('AUTH_FAILURE_PAUSED'),failedUntil: Infinity,KEY_AUTH_PAUSED). - Dead or unauthorized keys are stopped from waking up repeatedly every 60s cooldown, preventing repetitive broken dispatches.
- Granular soft error handling: transient infrastructure drops (HTTP 502, 503, 504,
TIMEOUT, socket drops) receive a short flat cooldown (<=10s) without doubling exponential penalty counters or incrementing toward recoverable quarantine. - Successful Test Matrix / manual re-test probe automatically unpauses the key.
- Differentiated error actions: upstream authentication failures (HTTP 401, 403,
- Automatic Model Capabilities Discovery & Routing (#500):
- In
lib/sandbox.js(probeModels), parse and return array of supported model IDs from upstream/models(body.data.map(m => m.id)). - Discovered model capabilities are cached per key (
pool.state.modelCapabilities: Map<ref, Set<modelId>>). - In
lib/resolver.jsandlib/rotate.js, filter candidate keys for a requested model so prompts are never dispatched to a key that doesn't support that model (e.g., DeepSeek-R1 vs DeepSeek-Chat V3). Keys not yet probed are optimistically allowed. - Atomic persistence across restarts:
pausedRefs,pauseReasons, andmodelCapabilitiesserialize and restore atomically vialib/persistence.js. - Settings Card UI: Display
Auth Paused (401/403)status badge and discovered model count badges (🤖 Nm) on configured keys.
- In
Full Changelog: v0.8.63...v0.8.64
v0.8.63 [DSH v0.2.1-alpha.2]
What's Changed
- feat(ui): add configurable switch to disable chat header status chip (#493 / GitHub #25)
- Adds
showChatHeaderChipboolean setting (defaulttrue) inSettingsSchemaand runtime configuration. - Adds user-facing toggle in Settings card ("Chat Header Status Chip" / "聊天头部状态按钮") under rotation settings.
KeyRotationHeaderChip(conversation.session.header.utilities) dynamically checks settings snapshot and health status, cleanly returningnullwhen disabled to free up conversational header toolbar space.- Additive field
showChatHeaderChipexposed in/dsh-key-rotation/statusand/dsh-key-rotation/health.
- Adds
Full Changelog: v0.8.62...v0.8.63
v0.8.62 [DSH v0.2.1-alpha.2]
Summary
Release v0.8.62 adds session-aware sticky routing affinity to protect prompt cache hit rates across parallel chat sessions and subagents (#489), a 1-click Test Matrix dashboard for batch probing all configured keys (#490), and zero-config pool auto-discovery from ~/.dsh/.credentials.yaml and environment variables (#491).
What Changed
- Session-Aware Sticky Routing Affinity for Prompt Caching (#489):
- Enhanced
stickyrouting strategy with LRU session affinity (pool.state.sessionSticky, capped at 500 active sessions). - Parallel DSH chat sessions or multi-agent subtasks retain dedicated sticky keys based on
sessionId(options.sessionId/options.session/options.metadata.sessionId), preventing cross-session cache evictions and maximizing prompt prefix cache hit rates for Anthropic, DeepSeek, and OpenAI. - Graceful failover to the next healthy candidate if a session's pinned key is in cooldown or quota-exhausted; automatic eviction from session map on error.
- Enhanced
- 1-Click Test Matrix Dashboard (#490):
- Added
/dsh-key-rotation/test-matrixendpoint inlib/ops-test.jssupporting batch validation of all configured keys across all pools with bounded concurrency (5 workers) and latency tracking. - Added "⚡ Test All Keys" action button in the WebUI Settings Card with an interactive results modal displaying operational key status, latency in milliseconds, and error details.
- Added
- Zero-Config Pool Auto-Discovery & 1-Click Import (#491):
- Added
/dsh-key-rotation/discoverand/dsh-key-rotation/auto-importendpoints inlib/ops-keys.js. - Automatically scans
~/.dsh/.credentials.yamland environment variables for unconfigured provider keys matching standard patterns (DEEPSEEK_API_KEY,OPENCODE_GO_API_KEY_*,OPENAI_API_KEY,GROQ_API_KEY, etc.), excluding keys already assigned to rotation pools. - Added smart auto-discovery banner to the Settings Card with 1-click import into provider pools.
- Added
- Verification:
- Added dedicated test suite
test/features-0862.test.mjs. - All 897 tests passing (
pass 897, fail 0), preflightFAIL=0 WARN=3.
- Added dedicated test suite
Artifacts & Correspondence
- npm:
@goodandready/dsh-key-rotation@0.8.62 - Gitea SHA:
a6aa2aaf7c284a2ff4c0142a8c57ecee158df75a - GitHub mirror commit:
3a16cd9583e7d5c08ac6133112ffb224b25a4cf0
v0.8.61 [DSH v0.2.1-alpha.2]
Summary
Release v0.8.61 fixes false calendar quota lockouts triggered by transient upstream rate limiting/RPM/TPM throttling (#24), introduces the new sticky key routing strategy to preserve prompt cache hit rates on LLM providers (#23), and enhances self-healing for quarantined keys.
What Changed
- Prevent Transient Rate Limit Deadlocks from Calendar Quota Locks (#24):
- Upstream errors returning transient 429, RPM/TPM exhaustion, or concurrency limits (such as SenseNova
{"message":"rpm exhausted","type":"quota_exceeded_error"}or{"message":"inference exceeds tpm/rpm limit","code":"insufficient_quota"}) are now classified asisRateThrottlingand excluded fromisQuota. - Transient rate limits receive standard short backoff (
cooldownMswith jitter) rather than being locked out until next UTC midnight. - Cleared
pool.state.quotaWindowsinrecordSuccessupon first successful request. - Extended background
autoUnbreakBrokenKeysand proactive health probing to probe keys with calendar quota locks, restoring healthy keys immediately upon receiving 200 OK from upstream. - Added
INSUFFICIENT_QUOTAand present-tense regex matches to switchable error classification.
- Upstream errors returning transient 429, RPM/TPM exhaustion, or concurrency limits (such as SenseNova
- Sticky Key Routing Strategy to Preserve Prompt Caching (#23):
- Added
'sticky'routing strategy option in settings and provider configurations. - In
stickymode, the resolver retains the same active key across consecutive requests as long as it remains healthy, maximizing prefix prompt cache hit rate on providers that cache per API key (Anthropic, DeepSeek, OpenAI). - Automatically fails over to the next healthy key in the pool if the sticky key experiences an error or cooldown, then sticks to the new key.
- Added UI dropdown selection in the Key Rotation Settings Card with English and Chinese localizations.
- Added
- Verification:
- Added dedicated test suite
test/issues-23-24.test.mjsverifying sticky key persistence, failover, quota lock avoidance, and self-healing. - All 891 tests passing (
pass 891, fail 0), preflightFAIL=0.
- Added dedicated test suite
Artifacts & Correspondence
- npm:
@goodandready/dsh-key-rotation@0.8.61 - Gitea SHA:
f3e06e3090b63de1d5555cff9bca623013de9c5e - GitHub mirror commit:
e6adae9d5401eeed02432031f5256cecf68cc374
v0.8.60 [DSH v0.2.1-alpha.2]
Summary
Release v0.8.60 expands the @deepseek-ai/dsh-llm peerDependency range to admit DSH Core 0.2.1-alpha.2, allowing the harness to load and run key rotation without skipping the plugin.
What Changed
- PeerDependencies Expanded for DSH Core 0.2.1-alpha.2:
- Range updated to
^0.1.7-rc.2 || >=0.2.0-rc.1 <0.2.1-0 || >=0.2.1-alpha.1 <0.2.2-0. - Fixes plugin skip during DSH core startup where
evaluatePluginCompatibilitymarked the plugin disabled due to the<0.2.1-0prerelease boundary. - Verified backward compatibility with
0.2.0-rc.2,0.2.0, and0.1.7-rc.2. - Upper bound
<0.2.2-0guards against untested 0.2.2 line.
- Range updated to
- Acceptance Verification:
- Added dedicated test suite
test/dsh-llm-021-alpha2.test.mjsverifying compatibility rules, service launch on 0.2.1-alpha.2 runtime, 429 key failover rotation, and pool status reporting. - All 885 unit and integration tests passing (
FAIL=0).
- Added dedicated test suite
Artifacts & Correspondence
- npm:
@goodandready/dsh-key-rotation@0.8.60 - Gitea SHA:
a7582d56dd08eb1cdd0fbb74e6f8e661c646039b - GitHub mirror commit:
65b5f41fb5cabf6d090df2a8124e344b56430384
v0.8.59
Refactored & Cleaned
- Legacy Settings Registration Removed (#482):
- Deleted dead legacy
settings.registerbranch inlib/index.js, standardizing configuration liveness oncreateConfigReader.
- Deleted dead legacy
- Dead Subscriptions & Settings Fallbacks Removed (#482):
- Removed dead
ctx.on('config')subscription inlib/config-compat.js. - Removed unreachable legacy
settings.getfallback increateProviderProfilesReaderin favor of standardsettings.describe.
- Removed dead
- Test Harness Synchronization (#482):
- Updated test fixtures for modern descriptor caching and multi-listener lifecycle events.
Gitea commit: d88675dde4b6b3e7804f1f8eb2f1ccbba2cb2793
Mirror commit: 8bee2556e5e127a3fc849b962359fefef1aa5315