dsh-veil
Verifieddsh-veil Β· v0.1.0 Β· Apache-2.0
DeepSeek Harness plugin: the model sees consistent pseudonyms, your machine keeps the real values (veil privacy proxy)
Install
dsh plugin add dsh-veil Confirm the layer applied with dsh --profile default --dump-config β see the install guide.
Source
Tags
Creators
Readme
dsh-veil
A DeepSeek Harness (dsh) plugin that sends
every DeepSeek model request through veil, a local
privacy proxy. The model sees consistent pseudonyms for your customers' names, emails, phones and
other seeded values. Your machine keeps the real values: dsh's tools run with them, and you read
them in dsh's answers.
dsh βββΊ veil (127.0.0.1, started by this plugin) βββΊ DeepSeek API
real values β pseudonyms on the way out
pseudonyms β real values on the way back (answers, tool calls)
Install
Install veil 0.3.1 or later, then seed it with the data the model must not see:
pip install veil-proxy # or: brew install lemonadesaltbagel/veil/veil veil seed postgresql://localhost/app ./customers.db # any sources veil supportsAdd the plugin to a dsh profile (dsh 0.2.1-rc.2 or later;
dsh pluginneedspnpm):dsh plugin --profile headless add dsh-veil dsh plugin --profile web add dsh-veilBefore the npm package is published, install a release tarball or a checkout:
dsh plugin --profile headless add https://github.com/lemonadesaltbagel/dsh-veil/releases/download/v0.1.0/dsh-veil-0.1.0.tgzUse dsh as usual.
dsh --profile headless "β¦"and the web app start veil with the session and stop it on exit./veilshows the proxy, the upstream and the counts of seeded values and pseudonyms.
What it changes in your profile
The bundle's patch (cordis.patch.yml) does four things:
- Starts veil. The
veilrow runsveil proxyon a free loopback port. It provides theveilservice only after the proxy answers its health check. - Routes DeepSeek through it. The
llm-deepseekandllm-deepseek-accountrows inject that service and takebaseURLfrom it. - Fails closed. If veil can't start, those rows never activate. dsh then reports
NO_ADAPTERand sends nothing, instead of calling DeepSeek directly. - Turns off
llm-pi-ai. That route sends other providers (OpenAI, Gemini, β¦) straight to their APIs, past veil.
A fetch guard backs this up. It refuses any model request (/messages, /chat/completions,
/responses, :generateContent) that isn't addressed to veil. It catches a later layer that points
an adapter elsewhere, such as a profile cordis.patch.yml, a --patch overlay or a Models setting,
and prints dsh-veil: refused a model request to β¦.
Tool calls
veil restores real values into a tool call only for tools it knows by name. dsh's tools take Claude
Code's argument names, so the plugin maps them onto veil's roles (veil proxy --tool-alias):
| dsh tool | veil role | what the tool receives |
|---|---|---|
bash |
Bash | real values, shell-escaped; commands that send data off the machine keep pseudonyms |
read, grep, glob, working_directory |
Read / Grep / Glob | real values |
edit, write |
Edit / Write | real values only where the file already had them, escaped for the file's language |
str_replace_editor |
(built in) | as Write / Edit |
every other tool (run_code, workflow, pwsh, subagent, web_*, terminals) |
none | pseudonyms only |
Configuration
Override the veil row in your profile's cordis.patch.yml:
- id: veil
config:
command: /opt/homebrew/bin/veil # default: veil on PATH
upstream: https://api.deepseek.com/anthropic # default: $DEEPSEEK_BASE_URL, then this
args: [--ner] # extra `veil proxy` arguments
# url: http://127.0.0.1:8787 # use a running `veil proxy` instead of starting one
# toolAliases: {bash: Bash} # replaces the table above
# logFile: /tmp/veil.log # default: <tmp>/dsh-veil/veil-<pid>.log
# startTimeoutMs: 60000
# guardFetch: true
veil's own settings (vault, seeded sources, display mode) live in VEIL_HOME, shared with the
veil CLI. See the veil README.
Limits
- Your machine is trusted. dsh's session logs hold real values, because veil hands dsh real values on the way back. Only the DeepSeek API sees pseudonyms.
- Images aren't scanned. veil passes images and
/v1/filesuploads through unchanged. - No other providers. The DeepSeek account route signs in with an account token, which the account service may refuse to send to a local address; the API-key route is the tested one. The pi-ai providers are off.
- Tested surface. The end-to-end test covers
dsh --profile headless. The web app uses the same rows, but/veilhasn't been exercised in its UI yet. - Leftover proxy. veil stops with dsh. If dsh is killed with SIGKILL, its veil proxy keeps running until you stop it.
Development
npm install && npm run build
npm test # unit tests
npm run e2e # real dsh + real veil + a scripted model: 0 leaks, tools get real values,
# fails closed without veil, refuses a baseURL past veil
The e2e test needs dsh, pnpm and veil on PATH, or DSH_BIN and VEIL_BIN. CI runs it on
every push against the npm dsh and the PyPI veil-proxy.
License
Apache License 2.0, like veil.