Claude Code-style declarative permission rules for DeepSeek Harness: ordered allow/deny/ask rules with tool-name, argument (glob/regex), and workspace-path matching on the tools/pre-execute waterfall, session-log audit, and HMR reload.
在 DeepSeek Harness 终端运行:
dsh plugin --profile web add perrylink/dsh-permission-rulesnpm i -g dsh1024 once, then dsh1024 plugin --profile web add dsh-permission-rules (counts toward the deepseek1024.com install ranking).Claude Code-style declarative permission rules for DeepSeek Harness.
Rules decide what is known. A reviewer model decides what is not.
| Surface | Status |
|---|---|
| Harness | DeepSeek Harness dsh-v0.1.5-rc.1 (adapted 2026-09-09, full gate chain + profile install smoke): its Session.append still cannot stamp the ignorable marker — verified on the published 0.1.5-rc.1 package, where the third argument is silently dropped and the envelope field survives for stored-log reads only — so the whole 0.1.5-alpha line is pre-checked as unmarked and session-log audit stays disabled by default. The 0.1.3-alpha line keeps the same surface-only append signature. Both lines' log migrations refuse unclassified plugin events even when marked, so strip v1 audit rows before a 0.1.3-alpha host opens the log and v2 audit rows before a 0.1.5-alpha host migrates it (native v3 logs only need repair). |
| Node | `^22.19.0 |
| Platforms | All (host + web settings client) |
| Model | Any (deny/ask reasons surface through tool results) |
dsh-permission-rules puts an ordered allow / deny / ask rule list in front of every tool call on the tools/pre-execute waterfall — deterministic, instant, auditable, and written by you in plain YAML:
deny blocks the call; the rule's reason becomes the model-visible error.ask rides the official approval seam (mount dsh-auto-review for a second-model answerer, or a human answers; with neither, the harness fails closed).allow (and no-match) strictly delegates via next() — downstream listeners are never short-circuited.Every hit and every passthrough is audit-logged as a permissionRules/decision session event (log-only — nothing extra is injected into the model context).
mcp__*), agent-identity selectors (main / subagent / preset:*), argument key/value globs or regexes (with !pattern negation and an absent key dimension), workspace-relative path globs at any nesting depth, when host conditions (env vars, platform), and shell command decomposition (argv: command word, argument tokens, pipeline signature) for token-precise command matching.builtin.enabled.searchUp merges every .dsh/rules.yaml from the session cwd to the filesystem root, nearest first.enforce: false audits what the policy would do while passing every call through.maxRules rules fail the load.# <project>/.dsh/rules.yaml
rules:
- match: { tools: [bash, pwsh], params: { command: "git push*" }, paths: ["**/secrets/**"] }
action: deny
reason: "No pushes from protected paths"
- match: { tools: [edit, write] }
action: ask
reason: "File writes need confirmation"
tools (globs, incl. mcp__*), agents (main / subagent / preset:<name>; unknown identity never matches — fail closed), params (key/value globs or regexes, !pattern negation, absent key dimension), paths (workspace-relative globs extracted at any nesting depth), when (env var globs/regexes + a closed platform list), and network (domains / ips / ports / schemes — globs, wildcards, CIDRs, port ranges).allow / deny / ask, evaluated in file order, first match wins.enabled: false (visible but inert), description, tags; unknown fields fail the load.# yaml-language-server: $schema=...); the full vocabulary and a 5-rule security baseline live in docs/rules-format.en.md.A Codex-style process-level network policy: shell subprocess traffic flows through a built-in local HTTP/CONNECT proxy, and every connection is decided by ordered network rules or by three modes mapped onto the official sandbox presets:
deny-all — the read-only sandbox preset: block all outbound.
whitelist — the workspace-write preset: allow listed targets, unlisted: ask (or deny) for the rest.
allow-all — the danger-full-access preset: allow everything.
auto (default) — follows the sandbox preset; on hosts without the sandbox-policy service it resolves to autoFallback (allow-all).
Matching — match.network with domains / ips / ports / schemes (globs, wildcards, CIDRs, port ranges; numeric YAML ports are accepted). URL-candidate extraction on the tools/pre-execute hot path fires on web-tool arguments and URLs embedded in bash/pwsh command text; loopback targets can short-circuit rules per loopback policy.
Audit — denied connections append permissionRules/network to the owning session (same adaptive ignorable gate), with block counters and recent interceptions in /rules network and the settings page.
# 1. install the bundle into your profile
dsh plugin --profile web add "github:PerryLink/dsh-permission-rules#main"
# or from npm (published releases)
dsh plugin --profile web add dsh-permission-rules
# 2. restart and verify the row
dsh --profile web --dump-config | grep -A4 'id: permission-rules'
main): dsh plugin --profile web add "github:PerryLink/dsh-permission-rules#main" — the prepare script builds with production dependencies only.dsh plugin --profile web add dsh-permission-rules.pnpm pack in this repo, then dsh plugin --profile web add ./dsh-permission-rules-<version>.tgz.dsh plugin --profile web remove dsh-permission-rules.All tunables are Schemastery Config fields (changeable from cordis.yml). An id-targeted override replaces the whole row — restate every key you need.
| Key | Default | Meaning |
|---|---|---|
rulesFile |
.dsh/rules.yaml |
Rule file location; relative = resolved against the calling session's cwd, absolute = global and validated at mount |
fallbackPath |
(none) | Rule file used when per-cwd discovery finds nothing; validated at mount |
badFilePolicy |
fail |
Bad rule file: fail errors the pending tool call loudly; ignore-with-warning warns and continues empty |
maxRules |
256 |
Hard cap on rule count across the effective source chain |
maxCachedWorkspaces |
512 |
Hard cap on cached per-workspace rule loads (LRU eviction) |
patternMode |
glob |
params/paths/when.env pattern flavor: glob or regex (tool names are always globs) |
watch |
true |
Chokidar watch + reload on change |
watchStabilityThresholdMs |
200 |
Reload debounce window (ms) |
language |
en |
/rules output language: en, zh, es, pt, hi |
caseInsensitivePaths |
(win32) | paths patterns and workspace-root comparison ignore ASCII case; true on Windows |
audit |
all |
Audit granularity: all logs every hit AND passthrough; hits skips passthrough events |
searchUp |
false |
Walk parent directories from the session cwd and merge every found rule file, nearest first |
maxGlobStars |
2 |
Hard cap on unbounded */** quantifiers per glob pattern |
enforce |
true |
false = dry-run mode: deny/ask hits are audit-logged with a dryRun marker and every call passes through |
allowUnmarkedAudit |
false |
Pre-marker hosts drop the ignorable marker; the plugin disables session-log audit with a warning. Set true to opt back in |
network.enabled |
true |
Master switch for the proxy, env injection, and web-tool mode defaults |
network.mode |
auto |
Policy mode: auto follows the sandbox preset, or deny-all / whitelist / allow-all |
network.autoFallback |
allow-all |
Mode used when auto has no sandbox-policy service |
network.unlisted |
ask |
Whitelist-mode handling of targets no rule matched: ask or deny |
network.proxyBind |
127.0.0.1 |
Local proxy bind address (loopback only) |
network.proxyPort |
0 |
Local proxy port; 0 picks a free ephemeral port |
network.proxyMaxRecent |
100 |
Cap on recent-block records kept for the settings page |
network.loopback |
allow |
Loopback targets: allow (Codex parity) or policy |
network.injectEnv |
true |
Whether proxy environment variables are injected for subprocesses |
network.noProxy |
clear |
Subprocess NO_PROXY handling: clear enforces the policy or preserve |
builtin.enabled |
true |
Built-in high-risk baseline: false disables the shipped deny/ask ruleset entirely |
builtin.path |
(shipped) | Replacement baseline file (absolute, or relative to process.cwd()); validated at mount |
| Surface | Kind | Notes |
|---|---|---|
tools/pre-execute |
listener | First-match allow/deny/ask rules + network URL-candidate extraction |
/rules |
command | list · reload · decisions [n] · test <tool> <json> |
permissionRules/decision |
event | Log-only audit for every hit and passthrough |
permissionRules/network |
event | Proxy-layer audit for blocked connections |
| HTTP/CONNECT proxy | service | Built-in local proxy governing shell subprocess traffic |
| settings page | client | Network-mode editor, rule editor, block counters, recent interceptions |
/rules list the active rules, their source files, and any last-reload error
/rules list explicit alias for the bare listing
/rules reload re-read the rule-file chain for this workspace
/rules decisions [n] show the last n permission decisions of this session (default 10)
/rules test <tool> <json> dry-evaluate the rules against a hypothetical call
/rules test also accepts leading flags: --cwd <dir>, --env KEY=VALUE (repeatable), --agent <selector> (repeatable), and --platform <name>. In multi-file chains (e.g. searchUp), every listed rule line is attributed to its own source file.
files:read, files:watch, files:write, session:append, and network:outbound in its workshop manifest. ask decisions ride the official approval seam — nothing is re-implemented or bypassed.permissionRules/decision is never injected into the model context and is appended with the envelope's ignorable: true marker so any harness build loads the log.paths candidates come only from a documented set of argument keys (at any nesting depth, depth-capped), and only workspace-relative paths match.ask decision is the end of its work.maxGlobStars unbounded star expansions; regex-mode patterns reject nested unbounded quantifiers and quantified overlapping literal alternations.permissionRules/decision is appended with ignorable: true; hosts whose Session.append predates the marker (the 0.1.0-rc.1–rc.7 and 0.1.1-rc.1–rc.7 lines) silently drop it, the 0.1.2-rc line ships the alpha.5 surface (no append option writes the marker), the 0.1.2-alpha line refuses plugin events on read even when marked, and the 0.1.3-alpha and 0.1.5-alpha lines keep the same surface-only append signature (verified on the published 0.1.3-alpha.1/0.1.5-alpha.1 packages) — the runtime pre-checks all of these before the first append and disables session-log audit with a one-time warning. Cross-generation migration refuses marked audit rows too: the 0.1.3-alpha v1→v2 gate refuses unknown v1 events, and the 0.1.5-alpha v2→v3 gate refuses every unclassified event (its inventory is frozen to the released v2 vocabulary), so strip v1 rows before a 0.1.3 host opens the log and v2 rows before upgrading to a 0.1.5 host. Native v3 logs accept marked plugin rows, so they only need repair. Set allowUnmarkedAudit: true to opt back in; repair already-written logs with scripts/repair-session-logs.mjs (its strip mode removes audit rows where the marker cannot help).caseInsensitivePaths is on.dsh-permission-rules produces ask; dsh-auto-review answers on the approval/request waterfall with a read-only second-model verdict (or delegates to humans). Mount both for the full closed loop.permissionRules/decision → approval/asked → autoReview/verdict → approval/decided, with the reviewer replaced by a scripted mock.never approval policy and every fail-closed guarantee of the official harness stay untouched.Session logs written before the ignorable marker existed can be refused by newer harness builds (SessionFormatUnsupportedError). The shipped scripts/repair-session-logs.mjs rewrites only the targeted audit rows to carry ignorable: true, frame-preserving, with backups:
node scripts/repair-session-logs.mjs scan [--home DIR] # report foreign rows, change nothing
node scripts/repair-session-logs.mjs repair [--home DIR] [--dry-run]
node scripts/repair-session-logs.mjs strip [--home DIR] [--dry-run]
--home defaults to $DSH_HOME/sessions (or ~/.dsh/sessions). Every generation-addressed log is discovered by its canonical basename — session.jsonl, session.v2.jsonl, session.v3.jsonl, each optionally .zstd-compressed — so pick the mode by generation:
session.v3.jsonl, written natively by the 0.1.5-alpha line) — the read path accepts marked plugin rows, so repair is enough.session.v2.jsonl, written by the 0.1.3-alpha line) — repair opens it on the host that wrote it, but the 0.1.5-alpha v2→v3 migration refuses every unclassified event even when marked: run strip on v2 logs before upgrading to a 0.1.5-alpha host.session.jsonl) — the 0.1.3-alpha v1→v2 migration refuses unknown v1 events even when marked: run strip before a 0.1.3-or-later host first opens the log.pnpm install # node ^22.19 || >=24
pnpm run typecheck # tsc, src + tests
pnpm run lint # eslint, src + tests + scripts
pnpm test # vitest: 280 tests, 23 files
pnpm run test:coverage # coverage gate (90/80/90/90)
pnpm run build # tsc declarations + tsdown bundles (lib/)
pnpm run pack:check # build + pack (the published artifact)
node scripts/check-readme-sync.mjs # five-language README sync gate (also in CI)
See VERIFICATION.md for the headless end-to-end verification record.
dsh, dsh-plugin, deepseek-harness, permission, policy, allow-deny-ask, approval, safety, network, network-policy, proxy
ignorable marker (#2) and the upstream harness discussion; the v0.4.1 runtime host-capability detection and the documentation correction drew directly from that analysis.docs/rule-test-vectors/, and supplied the AST-decomposition boundary cases on the design discussion.0.1.0-rc.7 harness compatibility gap (PR #9).0.1.1-rc line still drops the ignorable marker even though the version gate covered only 0.1.0 (#11); the widened gate drew directly from that analysis.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-mask | PII masking middleware: anonymize at the model boundary, restore at the display layer |
| 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-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 |
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.
Apache License 2.0 © 2026 dsh-permission-rules contributors
登录后即可为该插件评分和评价。
还没有人评价这个插件,来抢个沙发吧!