Hunter Bown 071d23a4b7 fix(auth): dual-write API key to keyring + config so stale keyring stops shadowing onboarding (#593)
Reproduction (from the user who filed #593, also the reporter of #586):

1. At any prior point, the user runs `deepseek auth set --provider deepseek`,
   which writes to the OS keyring under the `deepseek` slot.
2. The key is later rotated, the prior install is replaced, or the user
   moves to a different account.
3. The user opens the TUI, gets the in-TUI onboarding screen, and pastes
   their fresh API key.
4. `submit_api_key` → `save_api_key` writes only to `~/.deepseek/config.toml`.
5. At request time, `Secrets::resolve` follows the documented
   `keyring → env → config-file` precedence, and the **stale** keyring
   entry shadows the fresh config.toml value.
6. API call goes out with the dead key, gets a 401, the TUI shows
   "no response" with no obvious diagnostic.

The fix
=======

`save_api_key` now writes to **both** layers when a keyring backend is
reachable:

* The config file remains the durable, inspectable record of the
  active key (works in npm installs, IDE terminals, headless CI —
  everywhere). v0.8.8 made this the canonical location for a reason.
* The OS keyring entry is rewritten on every onboarding submit so a
  stale credential from a prior install is overwritten in place.

`SavedCredential` gains a new `KeyringAndConfigFile { backend, path }`
variant; the existing `ConfigFile(PathBuf)` variant remains the
fallback when no keyring backend is reachable (or under `cfg(test)`,
so the unit suite never pollutes the host keyring). The onboarding
toast naturally reports the actual outcome via
`SavedCredential::describe`, which now reads
`OS keyring (system keyring) and ~/.deepseek/config.toml` for the
common case.

`save_api_key_for` (the multi-provider entry point) is updated to
extract the path from either variant, so non-DeepSeek providers
(OpenRouter / Novita / Fireworks / NIM / SGLang) continue writing
provider-table entries to config.toml only, with no behavior change.

`deepseek doctor` warning
=========================

`run_doctor` now compares the keyring's `deepseek` slot against the
config file's `api_key` slot. When both are present and differ, the
report surfaces the discrepancy with copy-paste remediation —
`deepseek auth set --provider deepseek` rewrites both layers in one
shot, and the in-TUI onboarding now does the same. The check skips
keyring probes for other providers because they don't write to the
keyring today; probing absent slots only triggers macOS Always-Allow
prompts for nothing.

Why dual-write rather than keyring-only
=======================================

A previous attempt (`4e360274`, never merged to main) swapped the
write path to keyring-only. That hides the key from anyone who
expected to see it under `~/.deepseek/config.toml` and breaks the
"deepseek-tui works in every folder, in npm installs, in IDE
terminals" promise of v0.8.8. Dual-write keeps the inspectable copy
and adds the layered override that defeats stale-shadow without
changing the visible mental model.

Tests
=====

* `saved_credential_describe_lists_both_targets_for_keyring_and_config`
  pins the toast text shape so the user sees both targets after
  onboarding.
* The existing `save_api_key_writes_config_file_under_cfg_test` and
  `test_save_api_key_doesnt_match_similar_keys` continue to pass —
  under `cfg(test)` the keyring path is gated out, so the
  config-only outcome remains the test-time contract.

Verification
============

* `cargo fmt --all -- --check` clean.
* `cargo clippy -p deepseek-tui --bin deepseek-tui --all-features
  --locked -- -D warnings` clean.
* `cargo test -p deepseek-tui --bin deepseek-tui --locked` →
  2029 passed, 2 ignored.

Closes #593.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 13:55:07 -05:00
2026-04-25 07:59:01 -05:00
2026-04-24 16:29:01 -05:00
2026-01-20 08:57:35 -06:00

DeepSeek TUI

A terminal-native coding agent built around DeepSeek V4's 1M-token context and prefix cache. Single binary, no Node/Python runtime required — ships an MCP client, sandbox, and durable task queue out of the box.

简体中文 README

npm i -g deepseek-tui

CI npm crates.io

Buy me a coffee

DeepSeek TUI screenshot


What is it?

DeepSeek TUI is a coding agent that runs entirely in your terminal. It gives DeepSeek's frontier models direct access to your workspace — reading and editing files, running shell commands, searching the web, managing git, and orchestrating sub-agents — all through a fast, keyboard-driven TUI.

Built for DeepSeek V4 (deepseek-v4-pro / deepseek-v4-flash) with 1M-token context windows and native thinking-mode (chain-of-thought) streaming.

Key Features

  • Native RLM (rlm_query) — fans out 116 cheap deepseek-v4-flash children in parallel for batched analysis and parallel reasoning, all against the existing API client
  • Thinking-mode streaming — watch the model's chain-of-thought unfold in real time as it works through your tasks
  • Full tool suite — file ops, shell execution, git, web search/browse, apply-patch, sub-agents, MCP servers
  • 1M-token context — automatic intelligent compaction when context fills up; prefix-cache aware for cost efficiency
  • Three modes — Plan (read-only explore), Agent (interactive with approval), YOLO (auto-approved)
  • Reasoning-effort tiers — cycle through off → high → max with Shift+Tab
  • Session save/resume — checkpoint and resume long-running sessions
  • Workspace rollback — side-git pre/post-turn snapshots with /restore and revert_turn, without touching your repo's .git
  • Durable task queue — background tasks survive restarts; think scheduled automation, long-running reviews
  • HTTP/SSE runtime APIdeepseek serve --http for headless agent workflows
  • MCP protocol — connect to Model Context Protocol servers for extended tooling; see docs/MCP.md
  • LSP diagnostics — inline error/warning surfacing after every edit via rust-analyzer, pyright, typescript-language-server, gopls, clangd
  • User memory — optional persistent note file injected into the system prompt for cross-session preferences
  • Localized UIen, ja, zh-Hans, pt-BR with auto-detection
  • Live cost tracking — per-turn and session-level token usage and cost estimates; cache hit/miss breakdown
  • Skills system — composable, installable instruction packs from GitHub with no backend service required

How it's wired

deepseek (dispatcher CLI) → deepseek-tui (companion binary) → ratatui interface ↔ async engine ↔ OpenAI-compatible streaming client. Tool calls route through a typed registry (shell, file ops, git, web, sub-agents, MCP, RLM) and results stream back into the transcript. The engine manages session state, turn tracking, the durable task queue, and an LSP subsystem that feeds post-edit diagnostics into the model's context before the next reasoning step.

See docs/ARCHITECTURE.md for the full walkthrough.


Quickstart

npm install -g deepseek-tui
deepseek

Prebuilt binaries are published for Linux x64, Linux ARM64 (v0.8.8+), macOS x64, macOS ARM64, and Windows x64. For other targets (musl, riscv64, FreeBSD, etc.), see Install from source or docs/INSTALL.md.

On first launch you'll be prompted for your DeepSeek API key. The key is saved to ~/.deepseek/config.toml so it works from any directory without OS credential prompts.

You can also set it ahead of time:

deepseek auth set --provider deepseek   # saves to ~/.deepseek/config.toml

export DEEPSEEK_API_KEY="YOUR_KEY"      # env var alternative; use ~/.zshenv for non-interactive shells
deepseek

deepseek doctor                          # verify setup

To rotate or remove a saved key: deepseek auth clear --provider deepseek.

Linux ARM64 (Raspberry Pi, Asahi, Graviton, HarmonyOS PC)

npm i -g deepseek-tui works on glibc-based ARM64 Linux from v0.8.8 onward. You can also download prebuilt binaries from the Releases page and place them side by side on your PATH.

China / mirror-friendly install

If GitHub or npm downloads are slow from mainland China, use a Cargo registry mirror:

# ~/.cargo/config.toml
[source.crates-io]
replace-with = "tuna"

[source.tuna]
registry = "sparse+https://mirrors.tuna.tsinghua.edu.cn/crates.io-index/"

Then install both binaries (the dispatcher delegates to the TUI at runtime):

cargo install deepseek-tui-cli --locked   # provides `deepseek`
cargo install deepseek-tui     --locked   # provides `deepseek-tui`
deepseek --version

Prebuilt binaries can also be downloaded from GitHub Releases. Use DEEPSEEK_TUI_RELEASE_BASE_URL for mirrored release assets.

Install from source

Works on any Tier-1 Rust target — including musl, riscv64, FreeBSD, and older ARM64 distros.

# Linux build deps (Debian/Ubuntu/RHEL):
#   sudo apt-get install -y build-essential pkg-config libdbus-1-dev
#   sudo dnf install -y gcc make pkgconf-pkg-config dbus-devel

git clone https://github.com/Hmbown/DeepSeek-TUI.git
cd DeepSeek-TUI

cargo install --path crates/cli --locked   # requires Rust 1.85+; provides `deepseek`
cargo install --path crates/tui --locked   # provides `deepseek-tui`

Both binaries are required. Cross-compilation and platform-specific notes: docs/INSTALL.md.

Other API providers

# NVIDIA NIM
deepseek auth set --provider nvidia-nim --api-key "YOUR_NVIDIA_API_KEY"
deepseek --provider nvidia-nim

# Fireworks
deepseek auth set --provider fireworks --api-key "YOUR_FIREWORKS_API_KEY"
deepseek --provider fireworks --model deepseek-v4-pro

# Self-hosted SGLang
SGLANG_BASE_URL="http://localhost:30000/v1" deepseek --provider sglang --model deepseek-v4-flash

What's new in v0.8.10

A patch release: hotfixes, UX polish, and runtime API additions for the whalescale desktop integration. No breaking changes. Full changelog.

  • Stacked toast overlay — status toasts queue and render together instead of overwriting each other
  • File @-mention frecency — file mention suggestions learn from recent selections (~/.deepseek/file-frecency.jsonl)
  • Runtime API expansion — CORS origins config, full thread editing (PATCH /v1/threads/{id}), archived_only query filter, aggregate usage endpoint (GET /v1/usage?group_by=day|model|provider|thread)
  • Language picker on first run — new onboarding step selects locale before entering the API key
  • OPENCODE shell.env hook — lifecycle hooks can inject shell environment into spawned commands
  • Cache-aware compaction — compaction calls reuse cached prompt prefixes, cutting /compact costs significantly
  • glibc 2.28 baseline — prebuilts now target glibc 2.28 (via cargo zigbuild), covering older distros; npm postinstall fails fast with a clear source-build message when incompatible
  • Better markdown rendering — transcript now handles tables, bold/italic, and horizontal rules; no more infinite loops on unclosed markers
  • MCP SIGTERM on shutdown — stdio servers receive SIGTERM with a 2-second grace period instead of SIGKILL
  • Shell-child PDEATHSIG on Linux — children auto-SIGTERM when the parent exits, closing a leak window
  • Windows Terminal paste fix — Ctrl/Cmd+V during onboarding now works correctly
  • Terminal startup repaint — no more stale background rows above the first frame
  • Slash-prefix Enter activation — typing /mo and pressing Enter activates the first match
  • Shell cwd boundary validation — path escape returns PathEscape on out-of-workspace cwd, consistent with file tools

6 first-time contributors: @staryxchen (#556), @shentoumengxin (#524), @Vishnu1837 (#565), @20bytes (#569), @loongmiaow-pixel (#578), @WyxBUPT-22 (#579). Thanks also to @lloydzhou, @jeoor, @toi500, @xsstomy, and @melody0709 for bug reports.


Usage

deepseek                                       # interactive TUI
deepseek "explain this function"              # one-shot prompt
deepseek --model deepseek-v4-flash "summarize" # model override
deepseek --yolo                                # auto-approve tools
deepseek auth set --provider deepseek         # save API key
deepseek doctor                                # check setup & connectivity
deepseek doctor --json                         # machine-readable diagnostics
deepseek setup --status                        # read-only setup status
deepseek setup --tools --plugins               # scaffold tool/plugin dirs
deepseek models                                # list live API models
deepseek sessions                              # list saved sessions
deepseek resume --last                         # resume latest session
deepseek serve --http                          # HTTP/SSE API server
deepseek pr <N>                                # fetch PR and pre-seed review prompt
deepseek mcp list                              # list configured MCP servers
deepseek mcp validate                          # validate MCP config/connectivity
deepseek mcp-server                            # run dispatcher MCP stdio server

Keyboard shortcuts

Key Action
Tab Complete / or @ entries; while running, queue draft as follow-up; otherwise cycle mode
Shift+Tab Cycle reasoning-effort: off → high → max
F1 Searchable help overlay
Esc Back / dismiss
Ctrl+K Command palette
Ctrl+R Resume an earlier session
Alt+R Search prompt history and recover cleared drafts
Ctrl+S Stash current draft (/stash list, /stash pop to recover)
@path Attach file/directory context in composer
(at composer start) Select attachment row for removal
Alt+↑ Edit last queued message

Full shortcut catalog: docs/KEYBINDINGS.md.


Modes

Mode Behavior
Plan 🔍 Read-only investigation — model explores and proposes a plan (update_plan + checklist_write) before making changes
Agent 🤖 Default interactive mode — multi-step tool use with approval gates; model outlines work via checklist_write
YOLO Auto-approve all tools in a trusted workspace; still maintains plan and checklist for visibility

Configuration

User config: ~/.deepseek/config.toml. Project overlay: <workspace>/.deepseek/config.toml (denied: api_key, base_url, provider, mcp_config_path). config.example.toml has every option.

Key environment variables:

Variable Purpose
DEEPSEEK_API_KEY API key
DEEPSEEK_BASE_URL API base URL
DEEPSEEK_MODEL Default model
DEEPSEEK_PROVIDER deepseek (default), nvidia-nim, fireworks, sglang
DEEPSEEK_PROFILE Config profile name
DEEPSEEK_MEMORY Set to on to enable user memory
NVIDIA_API_KEY / FIREWORKS_API_KEY / SGLANG_API_KEY Provider auth
SGLANG_BASE_URL Self-hosted SGLang endpoint
NO_ANIMATIONS=1 Force accessibility mode at startup
SSL_CERT_FILE Custom CA bundle for corporate proxies

UI locale is separate from model language — set locale in settings.toml, use /config locale zh-Hans, or rely on LC_ALL/LANG. See docs/CONFIGURATION.md and docs/MCP.md.


Models & Pricing

Model Context Input (cache hit) Input (cache miss) Output
deepseek-v4-pro 1M $0.003625 / 1M* $0.435 / 1M* $0.87 / 1M*
deepseek-v4-flash 1M $0.0028 / 1M $0.14 / 1M $0.28 / 1M

Legacy aliases deepseek-chat / deepseek-reasoner map to deepseek-v4-flash. NVIDIA NIM variants use your NVIDIA account terms.

*DeepSeek Pro rates are a limited-time 75% discount valid until 2026-05-05 15:59 UTC; the TUI cost estimator falls back to base Pro rates after that timestamp.


Publishing your own skill

DeepSeek TUI discovers skills from workspace directories (.agents/skillsskills.opencode/skills.claude/skills) and the global ~/.deepseek/skills. Each skill is a directory with a SKILL.md file:

~/.deepseek/skills/my-skill/
└── SKILL.md

Frontmatter required:

---
name: my-skill
description: Use this when DeepSeek should follow my custom workflow.
---

# My Skill
Instructions for the agent go here.

Commands: /skills (list), /skill <name> (activate), /skill new (scaffold), /skill install github:<owner>/<repo> (community), /skill update / uninstall / trust. Community installs from GitHub require no backend service. Installed skills appear in the model-visible session context; the agent can auto-select relevant skills via the load_skill tool when your task matches their descriptions.


Documentation

Doc Topic
ARCHITECTURE.md Codebase internals
CONFIGURATION.md Full config reference
MODES.md Plan / Agent / YOLO modes
MCP.md Model Context Protocol integration
RUNTIME_API.md HTTP/SSE API server
INSTALL.md Platform-specific install guide
MEMORY.md User memory feature guide
SUBAGENTS.md Sub-agent role taxonomy and lifecycle
KEYBINDINGS.md Full shortcut catalog
RELEASE_RUNBOOK.md Release process
OPERATIONS_RUNBOOK.md Ops & recovery

Full changelog: CHANGELOG.md.


Thanks

Earlier releases shipped with help from these contributors:

  • Hafeez Pizofreude — SSRF protection in fetch_url and Star History chart
  • Unic (YuniqueUnic) — Schema-driven config UI (TUI + web)
  • Jason — SSRF security hardening

Contributing

See CONTRIBUTING.md. Pull requests welcome — check the open issues for good first contributions.

Not affiliated with DeepSeek Inc.

License

MIT

Star History

Star History Chart

S
Description
No description provided
Readme 24 MiB
Languages
Rust 94%
TypeScript 2.6%
JavaScript 1.6%
Shell 0.8%
Python 0.6%
Other 0.1%