dsh-mask
已验证 · 实测可装 PerryLink
功能简介
PII masking middleware for DeepSeek Harness: anonymize names, phones, emails, ID cards, bank cards, keys, and addresses to placeholders before they reach the model, restore them at the display layer, keep the restore table only in memory and a controlled storage domain, never log plaintext, and expose /mask and the mask_test tool
可用 — 实测通过,早期项目
PII masking middleware for DeepSeek Harness: anonymize names, phones, emails, ID cards, bank cards, keys, and addresses to placeholders before they reach the model, restore them at the display layer, keep the restore table only in memory and a controlled storage domain, never log plaintext, and expose /mask and the mask_test tool 实测能干净安装、正常启动。早期项目,但功能可用。
「已验证」表示我们的自动化 CI 在干净 profile 里实际执行了 dsh plugin add 并启动成功——仅此而已。功能描述与版本兼容性均为作者声明。这不是安全审计,也不代表对第三方代码的背书。
README
dsh-mask
- 1024 store channel:
npm i -g dsh1024once, thendsh1024 plugin --profile web add dsh-mask(counts toward the deepseek1024.com install ranking).
PII masking middleware for DeepSeek Harness — anonymize personal data before it reaches the model, restore it at the display layer.
Phones, emails, ID cards, bank cards, keys, and more become placeholders at the model boundary; the plaintext never enters your session log.
Compatibility
| Surface | Status |
|---|---|
| Harness | DeepSeek Harness dsh-v0.1.3-alpha.1 (adapted 2026-09-02): the session envelope keeps its ignorable field for stored-log read compatibility only - Session.append still cannot stamp it, so audit-gate behavior is unchanged. Verified 2026-09-06 against the dsh-v0.1.3-alpha.1 master checkout (full gate chain + profile install smoke). |
| Node | ^22.19.0 || >=24.0.0 |
| Platforms | Anywhere DSH runs (pure host, zero-dependency regex; no browser half) |
| Model | Text models fully supported; no extra model capability required |
What you get
dsh-mask anonymizes personal data at the model boundary — before a message reaches the model — and keeps a restore table so placeholders can be mapped back to the originals at the display layer:
- Request-time masking —
agent/pre-stepmessages are rewritten so phones, emails, ID cards, bank cards, keys, and IPs (each opt-in) become<PHONE_1>-style placeholders. The masked text is what gets logged and sent to the model. - Restore table — the
placeholder → originalmap lives only in memory and a controlled storage domain (dsh_mask); the plaintext never enters the session log. - Audit, not plaintext — the
mask/appliedsession event records only "replaced N values + type distribution", never the original text or the mapping. /maskcommand —status(counts + distribution),on/off(runtime toggle),restore <text>(unmap placeholders),help.mask_testtool — run a snippet through the detector and see the placeholder result; it never reveals the original values.
user message ──agent/pre-step──▶ placeholders ──model──▶ placeholders ──restore──▶ display
▲ │
└──────── restore table (memory + dsh_mask) ────────┘
Quick start
# 1. install the bundle into your profile
dsh plugin --profile web add "github:PerryLink/dsh-mask#main"
# or from npm (published releases)
dsh plugin --profile web add dsh-mask
# 2. verify the row mounts
dsh --profile web --dump-config | grep -A2 'id: mask'
Then tailor the entity list in your profile patch:
- insert:
- id: mask
name: dsh-mask
config:
entities: [phone, email, id-card, bank-card, key]
> /mask status
> /mask restore <PHONE_1>
Install & uninstall
- git channel (latest
main):dsh plugin --profile web add "github:PerryLink/dsh-mask#main"(equivalent to installing fromgit+https://github.com/PerryLink/dsh-mask.git). No build step —index.mjsandlib/are the shipped artifacts. - npm channel (published releases):
dsh plugin --profile web add dsh-mask. - tarball channel:
pnpm packin this repo, thendsh plugin --profile web add ./dsh-mask-<version>.tgz. - uninstall:
dsh plugin --profile web remove dsh-mask(or remove the row from the profile patch).
dsh-mask no longer bundles the storage stack. Profiles that already compose it (the web profile does, via @deepseek-ai/dsh-web-app) provide storageDomain, so persistence works out of the box. On a bare profile without storage the plugin still mounts and masks, but the restore table is memory-only (lost on restart) — compose the storage stack in your profile patch, or set persistRestoreTable: false.
Configuration
All tunables are Schemastery Config fields (changeable from cordis.yml). An id-targeted override replaces the whole row — restate every key you need. cordis.patch.yml documents each key inline.
| Key | Default | Meaning |
|---|---|---|
enabled |
true |
Master switch; false unregisters the listener, the /mask command, and the mask_test tool |
mode |
regex |
Detection mode; only regex is implemented (regex+ner for name/address recognition is reserved and fails loud) |
entities |
[phone, email, id-card, bank-card, key] |
Which PII types to mask; ip is also regex-capable (opt-in), person/address require NER |
scope |
[messages] |
Masking surface(s); messages masks agent/pre-step messages, tools masks tool-result text on tools/post-execute. Accepts a string or an array, e.g. [messages, tools] |
registerCommand |
true |
Register the /mask command |
registerTools |
true |
Register the mask_test tool when the tools service is present |
persistRestoreTable |
true |
Persist the restore table to the controlled dsh_mask storage domain (false = memory only) |
maxRestoreEntriesPerSession |
500 |
Per-session restore entry cap (oldest evicted first) |
maxSessions |
1000 |
In-memory session cap (least-recently-used evicted, mapping reloaded on demand) |
maskClientEnabled |
false |
Feature flag for the browser half "reveal" bubble (defensive; off by default until the live slot catalog verifies the target slot) |
Example override in your profile patch:
- insert:
- id: mask
name: dsh-mask
config:
entities: [phone, email, id-card, bank-card, key, ip]
persistRestoreTable: false
registerCommand: true
Tools & surfaces
| Surface | Reveals plaintext | Notes |
|---|---|---|
agent/pre-step masking |
never | Rewrites messages to placeholders before they are logged or sent to the model |
tools/post-execute masking |
never | Rewrites tool-result text blocks to placeholders before they are logged or fed back to the model (scope: tools) |
/mask status |
never | Enabled state, total replaced, type distribution |
/mask on / /mask off |
never | Runtime toggle (resets to config.enabled on restart) |
/mask restore <text> |
yes (explicit) | Unmaps placeholders back to the values stored for this session |
mask_test |
never | Masks a snippet and reports the placeholder result + counts |
Permissions & data
- Permissions:
dsh-maskperforms no network requests and stores no credentials; it only reads the session at theagent/pre-stepboundary and writes its owndsh_maskstorage domain. ThedshWorkshopmanifest declaresnetwork:noneandcredentials:none. - Data: the
placeholder → originalrestore table lives in memory and, whenpersistRestoreTable: true, in the controlleddsh_maskstorage domain — this is the only place plaintext PII is stored, and it is never written to the session log. - Session log:
mask/appliedis declared intypes.d.tsand appended only when the host records the type (see Known limitations). Its payload is counts + type distribution only.
Security boundaries
- Plaintext never enters the session log. The masked (placeholder) form is what gets logged and sent to the model, so model-visible content is reconstructable from the log in placeholder form; the originals stay in the restore table.
- Sanitize before display/log.
lib/sanitize.mjsredacts PII, secrets, and URL credentials before any text reaches the model or the log;mask_testand/mask statusnever echo originals. - Controlled restore.
/mask restoreis the single explicit reveal surface, and it only reads the mapping for the active session. - Fail closed. Unimplemented
mode(regex+ner), unknownscopevalues, NER-only entities, and out-of-bounds numbers all fail loudly at load. - Registrations are effects. The listener, command, tool, and storage-domain close are all Cordis effects — stop/hot-reload removes them.
Known limitations
- Regex only. Name (
person) and address (address) recognition needs an external NER recognizer, which the pure-host zero-dependency form does not bundle;mode: regex+nerand those entities fail loudly at load. The PII types covered out of the box are phone, email, ID card, bank card, key, and (opt-in) IP. - Display-layer restore needs a client half. Masking is fully host-side, but transparently un-masking the assistant bubbles in the client UI is a browser-half feature this pure-host form does not ship; the restore table and
restore()are the complete host-side seam a client plugin would consume, and/mask restorecovers interactive needs today. - Session events on
0.1.2-rc.1. The harness does not yet recordmask/*event types, and itsSession.appenddoes not stamp theignorableenvelope, so on alpha.3 the session-log audit appends are skipped (sessions keep loading); the plugin enables them automatically once a host records the types or supports theignorableenvelope.
Development
pnpm install # node ^22.19 || >=24
pnpm run typecheck && pnpm run typecheck:ci # tsc --checkJs against the published 0.1.2-rc.1 peers
pnpm test # node --test
pnpm run verify:self-contained # dependency specs resolve from the registry
pnpm run verify:artifacts # shipped files present + index.mjs importable
pnpm run check:readmes # five-language README consistency
pnpm pack # the published tarball
There is no build step: pure ESM, index.mjs and lib/ are the shipped artifacts.
Benchmark
The PII benchmark (per-type P/R/F1 over 108 synthetic samples) is published in benchmark/RESULTS.md; regenerate it with node benchmark/run.mjs (no build step, zero new dependencies).
Topics
dsh, dsh-plugin, deepseek-harness, deepseek, cordis, pii, mask, privacy, anonymization, security
Contributors
- @PerryLink — creator and maintainer: the regex PII detector ported from Pii-Stripper-Middleware, the
agent/pre-stepmasking seam, the restore table, the/maskcommand andmask_testtool, and the five-language docs.
PerryLink DSH Plugin Family
This project is one of the 37 DeepSeek Harness plugins maintained by PerryLink. If this one helps you, the others likely will too:
| Plugin | One-liner |
|---|---|
| dsh-auto-review | Second-model auto-review on the approval chain, fail-closed by default |
| dsh-background-agents | Durable background child agents with a Web UI sidebar, messaging and interrupt |
| dsh-budget | Cost governance for DeepSeek Harness: budgets, carbon, and latency in one panel. |
| dsh-checkpoint-rewind | Claude Code /rewind-equivalent: snapshots, session forks, one-shot restore |
| dsh-claude-move | Migrate Claude Code sessions, memory, skills and CLAUDE.md into DSH |
| dsh-click | Cross-platform native desktop control for DeepSeek Harness — Windows first. |
| dsh-composer-history | Terminal-style input history for the web composer: arrows, Ctrl+R search |
| dsh-data-quality | Dataset quality checks and citation cross-checks (the optional numeric bridge consumed here) |
| dsh-defend | Prompt-injection, jailbreak, and secret-leak defense for DeepSeek Harness. |
| dsh-doublecheck | Engineering-discipline guard: requirements grill, test gates, adversary review |
| dsh-draw | Unified static-image generation routing for DeepSeek Harness. |
| dsh-fast | Read-only performance diagnostics for DeepSeek Harness. |
| dsh-fund-research | Deterministic research reports for Chinese public mutual funds |
| dsh-github | GitHub PR/issues integration for DSH, every write gated by approval |
| dsh-industry-research | Industry research orchestration that seals its deliverables through this plugin's ctx.researchReport.assemble |
| dsh-library | Local document knowledge base for DeepSeek Harness. |
| dsh-local-ai | Local-model (Ollama) integration for DeepSeek Harness. |
| dsh-lsp-actions | LSP diagnostics, formatting, completion, code actions and rename over language servers |
| dsh-mcp-panel | Read-only MCP runtime panel: /mcp command + Settings tab with status, tools and errors |
| dsh-memento | Approval-gated cross-session memory: ctx.memory seam + SQLite + memory tool |
| dsh-observe | OpenTelemetry and Langfuse observability exporter for DeepSeek Harness. |
| dsh-output-styles | Claude Code outputStyles-equivalent runtime style switching |
| dsh-permission-rules | Claude Code-style declarative allow/deny/ask permission rules with audit |
| dsh-personal-directive | Personal directive injector with top-bar toggle (framework edition) |
| dsh-plugin-guide | Plugin-development knowledge base as an on-demand agent skill |
| dsh-reach | Multi-channel approval/question bridge: WeChat/Telegram/Feishu, session console |
| dsh-research-report | Verifiable research-report engine: content-addressed evidence ledger and sealed versions |
| dsh-score | Multi-dimensional quality scoring for DeepSeek Harness plugins. |
| dsh-session-pin | Pin sessions in the Web sidebar with durable ordering |
| dsh-session-sync | Cross-device session sync for DeepSeek Harness — a dedicated git mirror of your session store. |
| dsh-skill-pack-security | Security-audit skill pack: secret scan, dependency and supply-chain review |
| dsh-talk | Voice-first session loop for DeepSeek Harness: talk to it, hear it answer. |
| dsh-test-drive | Isolated install-and-smoke test drives for DeepSeek Harness plugins. |
| dsh-ticktick | TickTick/Dida365 task bridge: session-header panel + 11 tools |
| dsh-translate | Vendor parameter translation and deterministic JSON repair for DeepSeek Harness. |
| dsh-wechat | WeChat ↔ DSH bridge (Tencent iLink bot): text/image/file/voice, approvals in chat |
Install from the DSH Desktop Market
All PerryLink plugins are browsable in the built-in DSH Desktop Market: Market → Sources → add source → paste https://perrylink-dsh-catalog.perrylink.workers.dev/catalog-source.json → select it. Installation still goes through the Market's npm-identity verification and your confirmation.
License
LICENSE (Apache License 2.0) © 2026 dsh-mask contributors
安装
装一次目录插件,之后本站所有插件都能让 DeepSeek Harness 自动找、自动装:
dsh plugin add dshbase-catalog 然后对 agent 说「帮我装 dsh-mask」,它会在目录里找到并自动安装。文档:dshbase-catalog · 已验证场景包。
该插件是 GitHub 源码(未发 npm)——直接从仓库装:
Web profile:
dsh plugin --profile web add github:PerryLink/dsh-mask Headless(CLI)profile:
dsh plugin --profile headless add github:PerryLink/dsh-mask 实测报告
验证通过:从 GitHub 源码完成 L1 安装 + L2 加载 + L3 运行(dsh 0.1.0-rc.6)。