dsh-plugin-guide 0.4.5 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -1,8 +1,8 @@
1
1
  <div align="center">
2
2
 
3
3
  <picture>
4
- <source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-plugin-guide-flow-dark.svg">
5
- <img src="docs/assets/dsh-plugin-guide-flow-light.svg" width="100%" alt="dsh-plugin-guide assembly and routing: cordis.patch.yml inserts one row → index.js registers the skill through ctx.skills.register → the SKILL.md body → docs/levels.md for the three layers → dsh-standards' dsh-levels for the verdict">
4
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-plugin-guide-hero-en-dark.webp">
5
+ <img src="docs/assets/dsh-plugin-guide-hero-en-light.webp" width="100%" alt="dsh-plugin-guide: ENTRY / FLOW / BOOST question lists; the verdict belongs to dsh-standards">
6
6
  </picture>
7
7
 
8
8
  # dsh-plugin-guide
@@ -42,6 +42,11 @@ dsh plugin --profile web add dsh-plugin-guide
42
42
  | **`dsh-plugin-spec`** | The low-level rule text and clause source |
43
43
  | **`dsh-primer`** | The legacy build/scaffold skill and CLI (formerly `dsh-plugin-guide` 0.3.x) |
44
44
 
45
+ <picture>
46
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-plugin-guide-flow-v4-dark.svg">
47
+ <img src="docs/assets/dsh-plugin-guide-flow-v4-light.svg" width="100%" alt="dsh-plugin-guide assembly and routing: cordis.patch.yml → index.js → SKILL.md → docs/levels.md → dsh-standards">
48
+ </picture>
49
+
45
50
  ## Three-step setup
46
51
 
47
52
  1. **Install** (pick one channel):
package/README.es.md CHANGED
@@ -1,8 +1,8 @@
1
1
  <div align="center">
2
2
 
3
3
  <picture>
4
- <source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-plugin-guide-flow-dark.svg">
5
- <img src="docs/assets/dsh-plugin-guide-flow-light.svg" width="100%" alt="dsh-plugin-guide, montaje y enrutado: cordis.patch.yml inserta una fila → index.js registra la skill con ctx.skills.register → el cuerpo de SKILL.md → docs/levels.md para las tres capas → dsh-levels de dsh-standards para el veredicto">
4
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-plugin-guide-hero-en-dark.webp">
5
+ <img src="docs/assets/dsh-plugin-guide-hero-en-light.webp" width="100%" alt="dsh-plugin-guide: ENTRY / FLOW / BOOST question lists; the verdict belongs to dsh-standards">
6
6
  </picture>
7
7
 
8
8
  # dsh-plugin-guide
@@ -42,6 +42,11 @@ dsh plugin --profile web add dsh-plugin-guide
42
42
  | **`dsh-plugin-spec`** | El texto de reglas de bajo nivel y la fuente de las cláusulas |
43
43
  | **`dsh-primer`** | La skill y CLI heredadas de construcción/andamiaje (antiguo `dsh-plugin-guide` 0.3.x) |
44
44
 
45
+ <picture>
46
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-plugin-guide-flow-v4-dark.svg">
47
+ <img src="docs/assets/dsh-plugin-guide-flow-v4-light.svg" width="100%" alt="dsh-plugin-guide assembly and routing: cordis.patch.yml → index.js → SKILL.md → docs/levels.md → dsh-standards">
48
+ </picture>
49
+
45
50
  ## Instalación en tres pasos
46
51
 
47
52
  1. **Instalar** (elige un canal):
package/README.hi.md CHANGED
@@ -1,8 +1,8 @@
1
1
  <div align="center">
2
2
 
3
3
  <picture>
4
- <source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-plugin-guide-flow-dark.svg">
5
- <img src="docs/assets/dsh-plugin-guide-flow-light.svg" width="100%" alt="dsh-plugin-guide, असेंबली और रूटिंग: cordis.patch.yml एक पंक्ति जोड़ता है → index.js ctx.skills.register से स्किल दर्ज करता है → SKILL.md का बॉडी → तीन परतों के लिए docs/levels.md → निर्णय के लिए dsh-standards का dsh-levels">
4
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-plugin-guide-hero-en-dark.webp">
5
+ <img src="docs/assets/dsh-plugin-guide-hero-en-light.webp" width="100%" alt="dsh-plugin-guide: ENTRY / FLOW / BOOST question lists; the verdict belongs to dsh-standards">
6
6
  </picture>
7
7
 
8
8
  # dsh-plugin-guide
@@ -42,6 +42,11 @@ dsh plugin --profile web add dsh-plugin-guide
42
42
  | **`dsh-plugin-spec`** | निम्न-स्तरीय नियम-पाठ और धारा स्रोत |
43
43
  | **`dsh-primer`** | पुरानी बिल्ड/स्कैफ़ोल्ड स्किल और CLI (पूर्व `dsh-plugin-guide` 0.3.x) |
44
44
 
45
+ <picture>
46
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-plugin-guide-flow-v4-dark.svg">
47
+ <img src="docs/assets/dsh-plugin-guide-flow-v4-light.svg" width="100%" alt="dsh-plugin-guide assembly and routing: cordis.patch.yml → index.js → SKILL.md → docs/levels.md → dsh-standards">
48
+ </picture>
49
+
45
50
  ## तीन चरणों में शुरुआत
46
51
 
47
52
  1. **इंस्टॉल करें** (कोई एक चैनल चुनें):
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  <div align="center">
2
2
 
3
3
  <picture>
4
- <source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-plugin-guide-flow-dark.svg">
5
- <img src="docs/assets/dsh-plugin-guide-flow-light.svg" width="100%" alt="dsh-plugin-guide 装配与路由:cordis.patch.yml 插入一行 → index.js 经 ctx.skills.register 注册技能 → SKILL.md 正文 → docs/levels.md 三层 → dsh-standards 的 dsh-levels 出判定">
4
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-plugin-guide-hero-zh-dark.webp">
5
+ <img src="docs/assets/dsh-plugin-guide-hero-zh-light.webp" width="100%" alt="dsh-plugin-guide:ENTRY / FLOW / BOOST 三层问题清单,判定交给 dsh-standards">
6
6
  </picture>
7
7
 
8
8
  # dsh-plugin-guide
@@ -42,6 +42,11 @@ dsh plugin --profile web add dsh-plugin-guide
42
42
  | **`dsh-plugin-spec`** | 底层规则文本与条款来源 |
43
43
  | **`dsh-primer`** | 旧的构建/脚手架技能和 CLI(原 `dsh-plugin-guide` 0.3.x) |
44
44
 
45
+ <picture>
46
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-plugin-guide-flow-v4-dark.svg">
47
+ <img src="docs/assets/dsh-plugin-guide-flow-v4-light.svg" width="100%" alt="dsh-plugin-guide 装配与路由流程:cordis.patch.yml → index.js → SKILL.md → docs/levels.md → dsh-standards">
48
+ </picture>
49
+
45
50
  ## 三步上手
46
51
 
47
52
  1. **安装**(三选一):
package/README.pt.md CHANGED
@@ -1,8 +1,8 @@
1
1
  <div align="center">
2
2
 
3
3
  <picture>
4
- <source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-plugin-guide-flow-dark.svg">
5
- <img src="docs/assets/dsh-plugin-guide-flow-light.svg" width="100%" alt="dsh-plugin-guide, montagem e roteamento: cordis.patch.yml insere uma linha → index.js registra a skill via ctx.skills.register → o corpo de SKILL.md → docs/levels.md para as três camadas → dsh-levels do dsh-standards para o veredito">
4
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-plugin-guide-hero-en-dark.webp">
5
+ <img src="docs/assets/dsh-plugin-guide-hero-en-light.webp" width="100%" alt="dsh-plugin-guide: ENTRY / FLOW / BOOST question lists; the verdict belongs to dsh-standards">
6
6
  </picture>
7
7
 
8
8
  # dsh-plugin-guide
@@ -42,6 +42,11 @@ dsh plugin --profile web add dsh-plugin-guide
42
42
  | **`dsh-plugin-spec`** | O texto de regras de baixo nível e a fonte das cláusulas |
43
43
  | **`dsh-primer`** | A skill e CLI legadas de construção/andaime (antigo `dsh-plugin-guide` 0.3.x) |
44
44
 
45
+ <picture>
46
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/dsh-plugin-guide-flow-v4-dark.svg">
47
+ <img src="docs/assets/dsh-plugin-guide-flow-v4-light.svg" width="100%" alt="dsh-plugin-guide assembly and routing: cordis.patch.yml → index.js → SKILL.md → docs/levels.md → dsh-standards">
48
+ </picture>
49
+
45
50
  ## Instalação em três passos
46
51
 
47
52
  1. **Instalar** (escolha um canal):
package/SKILL.md CHANGED
@@ -56,3 +56,7 @@ It defines the three layers and the difference between a check and advice.
56
56
  This project is non-official and has no certification, accreditation, security
57
57
  audit, or market gate. It does not score or rank plugins. When in doubt, link to
58
58
  the source document and state what was not run.
59
+
60
+ For plugin security — capabilities, credentials, egress, install scripts, and
61
+ supply chain — read `docs/security.md`. Its checks are static and advisory,
62
+ and runtime security needs a sandbox, not a checker.
@@ -0,0 +1,82 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1100" height="660" viewBox="0 0 1100 660" role="img" aria-label="dsh-plugin-guide assembly and routing">
2
+ <defs>
3
+ <linearGradient id="lineGrad" x1="0" y1="0" x2="1" y2="0">
4
+ <stop offset="0" stop-color="#8496f5" stop-opacity=".15"/>
5
+ <stop offset=".5" stop-color="#8496f5" stop-opacity=".9"/>
6
+ <stop offset="1" stop-color="#8496f5" stop-opacity=".15"/>
7
+ </linearGradient>
8
+ <filter id="soft" x="-40%" y="-40%" width="180%" height="200%">
9
+ <feDropShadow dx="0" dy="9" stdDeviation="12" flood-color="#1b1735" flood-opacity="0.55"/>
10
+ </filter>
11
+ <pattern id="dots" width="28" height="28" patternUnits="userSpaceOnUse">
12
+ <circle cx="1.3" cy="1.3" r="1.3" fill="#182031"/>
13
+ </pattern>
14
+ </defs>
15
+ <rect width="1100" height="660" fill="#0d1117"/>
16
+ <rect width="1100" height="660" fill="url(#dots)" opacity=".5"/>
17
+ <text x="50" y="62" fill="#f0f3f9" font-family="ui-monospace, SFMono-Regular, Menlo, monospace" font-size="32" font-weight="700">dsh-plugin-guide</text>
18
+ <text x="50" y="90" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="16">assembly and routing · the guide asks, dsh-standards decides</text>
19
+ <rect x="50" y="108" width="1000" height="1.6" rx="1" fill="#2a3142"/>
20
+ <g filter="url(#soft)">
21
+ <rect x="50" y="190" width="176" height="118" rx="17" fill="#161b26" stroke="#2a3142" stroke-width="1.3"/>
22
+ </g>
23
+ <circle cx="74" cy="214" r="12" fill="#242d42"/>
24
+ <text x="74" y="219" text-anchor="middle" fill="#8496f5" font-family="ui-monospace, monospace" font-size="11.5" font-weight="700">01</text>
25
+ <text x="138.0" y="256" text-anchor="middle" fill="#f0f3f9" font-family="ui-monospace, monospace" font-size="15.5" font-weight="700">cordis.patch.yml</text>
26
+ <text x="138.0" y="282" text-anchor="middle" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">one inserted row</text>
27
+ <path d="M231 249.0 C241.0 249.0, 241.0 249.0, 251 249.0" stroke="url(#lineGrad)" stroke-width="2.2" fill="none"/>
28
+ <path d="M241 244.0 l8 5 -8 5" fill="none" stroke="#8496f5" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
29
+ <g filter="url(#soft)">
30
+ <rect x="256" y="190" width="176" height="118" rx="17" fill="#161b26" stroke="#8496f5" stroke-width="2.6"/>
31
+ </g>
32
+ <circle cx="280" cy="214" r="12" fill="#242d42"/>
33
+ <text x="280" y="219" text-anchor="middle" fill="#8496f5" font-family="ui-monospace, monospace" font-size="11.5" font-weight="700">02</text>
34
+ <text x="344.0" y="256" text-anchor="middle" fill="#f0f3f9" font-family="ui-monospace, monospace" font-size="15.5" font-weight="700">index.js</text>
35
+ <text x="344.0" y="282" text-anchor="middle" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">ctx.skills.register</text>
36
+ <path d="M437 249.0 C447.0 249.0, 447.0 249.0, 457 249.0" stroke="url(#lineGrad)" stroke-width="2.2" fill="none"/>
37
+ <path d="M447 244.0 l8 5 -8 5" fill="none" stroke="#8496f5" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
38
+ <g filter="url(#soft)">
39
+ <rect x="462" y="190" width="176" height="118" rx="17" fill="#161b26" stroke="#2a3142" stroke-width="1.3"/>
40
+ </g>
41
+ <circle cx="486" cy="214" r="12" fill="#242d42"/>
42
+ <text x="486" y="219" text-anchor="middle" fill="#8496f5" font-family="ui-monospace, monospace" font-size="11.5" font-weight="700">03</text>
43
+ <text x="550.0" y="256" text-anchor="middle" fill="#f0f3f9" font-family="ui-monospace, monospace" font-size="15.5" font-weight="700">SKILL.md</text>
44
+ <text x="550.0" y="282" text-anchor="middle" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">the skill body</text>
45
+ <path d="M643 249.0 C653.0 249.0, 653.0 249.0, 663 249.0" stroke="url(#lineGrad)" stroke-width="2.2" fill="none"/>
46
+ <path d="M653 244.0 l8 5 -8 5" fill="none" stroke="#8496f5" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
47
+ <g filter="url(#soft)">
48
+ <rect x="668" y="190" width="176" height="118" rx="17" fill="#161b26" stroke="#2a3142" stroke-width="1.3"/>
49
+ </g>
50
+ <circle cx="692" cy="214" r="12" fill="#242d42"/>
51
+ <text x="692" y="219" text-anchor="middle" fill="#8496f5" font-family="ui-monospace, monospace" font-size="11.5" font-weight="700">04</text>
52
+ <text x="756.0" y="256" text-anchor="middle" fill="#f0f3f9" font-family="ui-monospace, monospace" font-size="15.5" font-weight="700">docs/levels.md</text>
53
+ <text x="756.0" y="282" text-anchor="middle" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">ENTRY / FLOW / BOOST</text>
54
+ <path d="M849 249.0 C859.0 249.0, 859.0 249.0, 869 249.0" stroke="url(#lineGrad)" stroke-width="2.2" fill="none"/>
55
+ <path d="M859 244.0 l8 5 -8 5" fill="none" stroke="#8496f5" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
56
+ <g filter="url(#soft)">
57
+ <rect x="874" y="190" width="176" height="118" rx="17" fill="#161b26" stroke="#3fb950" stroke-width="2.6"/>
58
+ </g>
59
+ <circle cx="898" cy="214" r="12" fill="#242d42"/>
60
+ <text x="898" y="219" text-anchor="middle" fill="#8496f5" font-family="ui-monospace, monospace" font-size="11.5" font-weight="700">05</text>
61
+ <text x="962.0" y="256" text-anchor="middle" fill="#f0f3f9" font-family="ui-monospace, monospace" font-size="15.5" font-weight="700">dsh-standards</text>
62
+ <text x="962.0" y="282" text-anchor="middle" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">dsh-levels verdict</text>
63
+ <g filter="url(#soft)">
64
+ <rect x="256" y="378" width="176" height="58" rx="15" fill="#242d42" stroke="#8496f5" stroke-width="1.2"/>
65
+ </g>
66
+ <text x="344" y="404" text-anchor="middle" fill="#8496f5" font-family="ui-monospace, monospace" font-size="13" font-weight="700">ENTRY</text>
67
+ <text x="344" y="423" text-anchor="middle" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">installs</text>
68
+ <g filter="url(#soft)">
69
+ <rect x="462" y="378" width="176" height="58" rx="15" fill="#242d42" stroke="#8496f5" stroke-width="1.2"/>
70
+ </g>
71
+ <text x="550" y="404" text-anchor="middle" fill="#8496f5" font-family="ui-monospace, monospace" font-size="13" font-weight="700">FLOW</text>
72
+ <text x="550" y="423" text-anchor="middle" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">works daily</text>
73
+ <g filter="url(#soft)">
74
+ <rect x="668" y="378" width="176" height="58" rx="15" fill="#242d42" stroke="#8496f5" stroke-width="1.2"/>
75
+ </g>
76
+ <text x="756" y="404" text-anchor="middle" fill="#8496f5" font-family="ui-monospace, monospace" font-size="13" font-weight="700">BOOST</text>
77
+ <text x="756" y="423" text-anchor="middle" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">faster</text>
78
+ <rect x="50" y="476" width="1000" height="58" rx="15" fill="#161b26" stroke="#2a3142"/>
79
+ <text x="74" y="502" fill="#8496f5" font-family="ui-monospace, monospace" font-size="13" font-weight="700">route / dsh-levels</text>
80
+ <text x="74" y="522" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="13">the guide explains what to check; the verdict belongs to dsh-standards</text>
81
+ <text x="50" y="596" fill="#a8b1c4" font-family="ui-monospace, monospace" font-size="13">Apache-2.0 · PerryLink DSH plugin family</text>
82
+ </svg>
@@ -0,0 +1,82 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1100" height="660" viewBox="0 0 1100 660" role="img" aria-label="dsh-plugin-guide assembly and routing">
2
+ <defs>
3
+ <linearGradient id="lineGrad" x1="0" y1="0" x2="1" y2="0">
4
+ <stop offset="0" stop-color="#4B67B4" stop-opacity=".15"/>
5
+ <stop offset=".5" stop-color="#4B67B4" stop-opacity=".9"/>
6
+ <stop offset="1" stop-color="#4B67B4" stop-opacity=".15"/>
7
+ </linearGradient>
8
+ <filter id="soft" x="-40%" y="-40%" width="180%" height="200%">
9
+ <feDropShadow dx="0" dy="9" stdDeviation="12" flood-color="#1b1735" flood-opacity="0.10"/>
10
+ </filter>
11
+ <pattern id="dots" width="28" height="28" patternUnits="userSpaceOnUse">
12
+ <circle cx="1.3" cy="1.3" r="1.3" fill="#f2f0f8"/>
13
+ </pattern>
14
+ </defs>
15
+ <rect width="1100" height="660" fill="#ffffff"/>
16
+ <rect width="1100" height="660" fill="url(#dots)" opacity=".5"/>
17
+ <text x="50" y="62" fill="#141419" font-family="ui-monospace, SFMono-Regular, Menlo, monospace" font-size="32" font-weight="700">dsh-plugin-guide</text>
18
+ <text x="50" y="90" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="16">assembly and routing · the guide asks, dsh-standards decides</text>
19
+ <rect x="50" y="108" width="1000" height="1.6" rx="1" fill="#e6e3f0"/>
20
+ <g filter="url(#soft)">
21
+ <rect x="50" y="190" width="176" height="118" rx="17" fill="#fcfcfe" stroke="#e6e3f0" stroke-width="1.3"/>
22
+ </g>
23
+ <circle cx="74" cy="214" r="12" fill="#ece7f9"/>
24
+ <text x="74" y="219" text-anchor="middle" fill="#4B67B4" font-family="ui-monospace, monospace" font-size="11.5" font-weight="700">01</text>
25
+ <text x="138.0" y="256" text-anchor="middle" fill="#141419" font-family="ui-monospace, monospace" font-size="15.5" font-weight="700">cordis.patch.yml</text>
26
+ <text x="138.0" y="282" text-anchor="middle" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">one inserted row</text>
27
+ <path d="M231 249.0 C241.0 249.0, 241.0 249.0, 251 249.0" stroke="url(#lineGrad)" stroke-width="2.2" fill="none"/>
28
+ <path d="M241 244.0 l8 5 -8 5" fill="none" stroke="#4B67B4" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
29
+ <g filter="url(#soft)">
30
+ <rect x="256" y="190" width="176" height="118" rx="17" fill="#fcfcfe" stroke="#4B67B4" stroke-width="2.6"/>
31
+ </g>
32
+ <circle cx="280" cy="214" r="12" fill="#ece7f9"/>
33
+ <text x="280" y="219" text-anchor="middle" fill="#4B67B4" font-family="ui-monospace, monospace" font-size="11.5" font-weight="700">02</text>
34
+ <text x="344.0" y="256" text-anchor="middle" fill="#141419" font-family="ui-monospace, monospace" font-size="15.5" font-weight="700">index.js</text>
35
+ <text x="344.0" y="282" text-anchor="middle" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">ctx.skills.register</text>
36
+ <path d="M437 249.0 C447.0 249.0, 447.0 249.0, 457 249.0" stroke="url(#lineGrad)" stroke-width="2.2" fill="none"/>
37
+ <path d="M447 244.0 l8 5 -8 5" fill="none" stroke="#4B67B4" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
38
+ <g filter="url(#soft)">
39
+ <rect x="462" y="190" width="176" height="118" rx="17" fill="#fcfcfe" stroke="#e6e3f0" stroke-width="1.3"/>
40
+ </g>
41
+ <circle cx="486" cy="214" r="12" fill="#ece7f9"/>
42
+ <text x="486" y="219" text-anchor="middle" fill="#4B67B4" font-family="ui-monospace, monospace" font-size="11.5" font-weight="700">03</text>
43
+ <text x="550.0" y="256" text-anchor="middle" fill="#141419" font-family="ui-monospace, monospace" font-size="15.5" font-weight="700">SKILL.md</text>
44
+ <text x="550.0" y="282" text-anchor="middle" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">the skill body</text>
45
+ <path d="M643 249.0 C653.0 249.0, 653.0 249.0, 663 249.0" stroke="url(#lineGrad)" stroke-width="2.2" fill="none"/>
46
+ <path d="M653 244.0 l8 5 -8 5" fill="none" stroke="#4B67B4" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
47
+ <g filter="url(#soft)">
48
+ <rect x="668" y="190" width="176" height="118" rx="17" fill="#fcfcfe" stroke="#e6e3f0" stroke-width="1.3"/>
49
+ </g>
50
+ <circle cx="692" cy="214" r="12" fill="#ece7f9"/>
51
+ <text x="692" y="219" text-anchor="middle" fill="#4B67B4" font-family="ui-monospace, monospace" font-size="11.5" font-weight="700">04</text>
52
+ <text x="756.0" y="256" text-anchor="middle" fill="#141419" font-family="ui-monospace, monospace" font-size="15.5" font-weight="700">docs/levels.md</text>
53
+ <text x="756.0" y="282" text-anchor="middle" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">ENTRY / FLOW / BOOST</text>
54
+ <path d="M849 249.0 C859.0 249.0, 859.0 249.0, 869 249.0" stroke="url(#lineGrad)" stroke-width="2.2" fill="none"/>
55
+ <path d="M859 244.0 l8 5 -8 5" fill="none" stroke="#4B67B4" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
56
+ <g filter="url(#soft)">
57
+ <rect x="874" y="190" width="176" height="118" rx="17" fill="#fcfcfe" stroke="#16a34a" stroke-width="2.6"/>
58
+ </g>
59
+ <circle cx="898" cy="214" r="12" fill="#ece7f9"/>
60
+ <text x="898" y="219" text-anchor="middle" fill="#4B67B4" font-family="ui-monospace, monospace" font-size="11.5" font-weight="700">05</text>
61
+ <text x="962.0" y="256" text-anchor="middle" fill="#141419" font-family="ui-monospace, monospace" font-size="15.5" font-weight="700">dsh-standards</text>
62
+ <text x="962.0" y="282" text-anchor="middle" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">dsh-levels verdict</text>
63
+ <g filter="url(#soft)">
64
+ <rect x="256" y="378" width="176" height="58" rx="15" fill="#ece7f9" stroke="#4B67B4" stroke-width="1.2"/>
65
+ </g>
66
+ <text x="344" y="404" text-anchor="middle" fill="#4B67B4" font-family="ui-monospace, monospace" font-size="13" font-weight="700">ENTRY</text>
67
+ <text x="344" y="423" text-anchor="middle" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">installs</text>
68
+ <g filter="url(#soft)">
69
+ <rect x="462" y="378" width="176" height="58" rx="15" fill="#ece7f9" stroke="#4B67B4" stroke-width="1.2"/>
70
+ </g>
71
+ <text x="550" y="404" text-anchor="middle" fill="#4B67B4" font-family="ui-monospace, monospace" font-size="13" font-weight="700">FLOW</text>
72
+ <text x="550" y="423" text-anchor="middle" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">works daily</text>
73
+ <g filter="url(#soft)">
74
+ <rect x="668" y="378" width="176" height="58" rx="15" fill="#ece7f9" stroke="#4B67B4" stroke-width="1.2"/>
75
+ </g>
76
+ <text x="756" y="404" text-anchor="middle" fill="#4B67B4" font-family="ui-monospace, monospace" font-size="13" font-weight="700">BOOST</text>
77
+ <text x="756" y="423" text-anchor="middle" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">faster</text>
78
+ <rect x="50" y="476" width="1000" height="58" rx="15" fill="#fcfcfe" stroke="#e6e3f0"/>
79
+ <text x="74" y="502" fill="#4B67B4" font-family="ui-monospace, monospace" font-size="13" font-weight="700">route / dsh-levels</text>
80
+ <text x="74" y="522" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="13">the guide explains what to check; the verdict belongs to dsh-standards</text>
81
+ <text x="50" y="596" fill="#4d5261" font-family="ui-monospace, monospace" font-size="13">Apache-2.0 · PerryLink DSH plugin family</text>
82
+ </svg>
@@ -0,0 +1,55 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1100" height="660" viewBox="0 0 1100 660" role="img" aria-label="dsh-plugin-guide three-level ladder ending in a routing arrow">
2
+ <defs>
3
+ <filter id="soft" x="-40%" y="-40%" width="180%" height="200%">
4
+ <feDropShadow dx="0" dy="9" stdDeviation="12" flood-color="#1b1735" flood-opacity="0.55"/>
5
+ </filter>
6
+ <pattern id="dots" width="28" height="28" patternUnits="userSpaceOnUse">
7
+ <circle cx="1.3" cy="1.3" r="1.3" fill="#182031"/>
8
+ </pattern>
9
+ <marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
10
+ <path d="M0 0 L10 5 L0 10 z" fill="#3fb950"/>
11
+ </marker>
12
+ </defs>
13
+ <rect width="1100" height="660" fill="#0d1117"/>
14
+ <rect width="1100" height="660" fill="url(#dots)" opacity=".5"/>
15
+ <text x="50" y="58" fill="#f0f3f9" font-family="ui-monospace, SFMono-Regular, Menlo, monospace" font-size="32" font-weight="700">dsh-plugin-guide</text>
16
+ <text x="50" y="86" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="16">three levels, deepening questions · the guide asks, dsh-standards decides</text>
17
+ <text x="50" y="122" fill="#8496f5" font-family="ui-sans-serif, system-ui, sans-serif" font-size="14" font-weight="700" letter-spacing="1.5">ADOPTION DEPTH</text>
18
+ <line x1="38" y1="150" x2="38" y2="482" stroke="#8496f5" stroke-width="2" stroke-dasharray="4 6" opacity=".6"/>
19
+
20
+ <rect x="50" y="150" width="392" height="92" rx="18" fill="#161b26" stroke="#2a3142" stroke-width="1.3" filter="url(#soft)"/>
21
+ <circle cx="82" cy="196" r="17" fill="#242d42"/>
22
+ <text x="82" y="202" text-anchor="middle" fill="#8496f5" font-family="ui-monospace, monospace" font-size="14" font-weight="700">1</text>
23
+ <text x="116" y="186" fill="#f0f3f9" font-family="ui-monospace, monospace" font-size="20" font-weight="700">ENTRY</text>
24
+ <text x="116" y="210" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="15">入门 · 6 个问题</text>
25
+ <text x="116" y="230" fill="#737b8d" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">能装上、能跑、不崩</text>
26
+ <line x1="38" y1="196" x2="50" y2="196" stroke="#8496f5" stroke-width="1.6" opacity=".35"/>
27
+ <rect x="84" y="268" width="392" height="92" rx="18" fill="#161b26" stroke="#2a3142" stroke-width="1.3" filter="url(#soft)"/>
28
+ <circle cx="116" cy="314" r="17" fill="#242d42"/>
29
+ <text x="116" y="320" text-anchor="middle" fill="#8496f5" font-family="ui-monospace, monospace" font-size="14" font-weight="700">2</text>
30
+ <text x="150" y="304" fill="#f0f3f9" font-family="ui-monospace, monospace" font-size="20" font-weight="700">FLOW</text>
31
+ <text x="150" y="328" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="15">顺畅 · 7 个问题</text>
32
+ <text x="150" y="348" fill="#737b8d" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">日常任务顺、错误可恢复</text>
33
+ <line x1="38" y1="314" x2="84" y2="314" stroke="#8496f5" stroke-width="1.6" opacity=".35"/>
34
+ <rect x="118" y="386" width="392" height="92" rx="18" fill="#161b26" stroke="#8496f5" stroke-width="2.4" filter="url(#soft)"/>
35
+ <circle cx="150" cy="432" r="17" fill="#242d42"/>
36
+ <text x="150" y="438" text-anchor="middle" fill="#8496f5" font-family="ui-monospace, monospace" font-size="14" font-weight="700">3</text>
37
+ <text x="184" y="422" fill="#f0f3f9" font-family="ui-monospace, monospace" font-size="20" font-weight="700">BOOST</text>
38
+ <text x="184" y="446" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="15">加速 · 5 个问题</text>
39
+ <text x="184" y="466" fill="#737b8d" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">提效证据可复现</text>
40
+ <line x1="38" y1="432" x2="118" y2="432" stroke="#8496f5" stroke-width="1.6" opacity=".35"/>
41
+ <path d="M510 432 L690 432" stroke="#3fb950" stroke-width="2.6" fill="none" marker-end="url(#arr)"/>
42
+ <text x="512" y="418" fill="#3fb950" font-family="ui-sans-serif, system-ui, sans-serif" font-size="13">verdict to</text>
43
+ <rect x="700" y="300" width="350" height="264" rx="20" fill="#161b26" stroke="#3fb950" stroke-width="2.4" filter="url(#soft)"/>
44
+ <text x="726" y="334" fill="#3fb950" font-family="ui-monospace, monospace" font-size="13" font-weight="700">dsh-standards</text>
45
+ <text x="726" y="366" fill="#f0f3f9" font-family="ui-sans-serif, system-ui, sans-serif" font-size="17">DSC-01...13 static checks</text>
46
+ <text x="726" y="394" fill="#f0f3f9" font-family="ui-sans-serif, system-ui, sans-serif" font-size="17">L1-L3 runtime measurement</text>
47
+ <text x="726" y="422" fill="#f0f3f9" font-family="ui-sans-serif, system-ui, sans-serif" font-size="17">dsh-levels verdict</text>
48
+ <line x1="726" y1="444" x2="1024" y2="444" stroke="#182031" stroke-width="2"/>
49
+ <text x="726" y="470" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="13">certification, scoring, ranking: none of them here</text>
50
+ <text x="726" y="494" fill="#737b8d" font-family="ui-monospace, monospace" font-size="12">guide asks · standards decides</text>
51
+ <text x="726" y="530" fill="#8496f5" font-family="ui-monospace, monospace" font-size="12">ENTRY / FLOW / BOOST</text>
52
+ <rect x="50" y="560" width="1000" height="52" rx="14" fill="#121824" stroke="#2a3142"/>
53
+ <text x="74" y="582" fill="#8496f5" font-family="ui-monospace, monospace" font-size="12.5" font-weight="700">route / dsh-levels</text>
54
+ <text x="74" y="602" fill="#a8b1c4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="13">every question has an owner: the guide says what to check, the verdict belongs to the checker</text>
55
+ </svg>
@@ -0,0 +1,55 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1100" height="660" viewBox="0 0 1100 660" role="img" aria-label="dsh-plugin-guide three-level ladder ending in a routing arrow">
2
+ <defs>
3
+ <filter id="soft" x="-40%" y="-40%" width="180%" height="200%">
4
+ <feDropShadow dx="0" dy="9" stdDeviation="12" flood-color="#1b1735" flood-opacity="0.10"/>
5
+ </filter>
6
+ <pattern id="dots" width="28" height="28" patternUnits="userSpaceOnUse">
7
+ <circle cx="1.3" cy="1.3" r="1.3" fill="#f2f0f8"/>
8
+ </pattern>
9
+ <marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
10
+ <path d="M0 0 L10 5 L0 10 z" fill="#16a34a"/>
11
+ </marker>
12
+ </defs>
13
+ <rect width="1100" height="660" fill="#ffffff"/>
14
+ <rect width="1100" height="660" fill="url(#dots)" opacity=".5"/>
15
+ <text x="50" y="58" fill="#141419" font-family="ui-monospace, SFMono-Regular, Menlo, monospace" font-size="32" font-weight="700">dsh-plugin-guide</text>
16
+ <text x="50" y="86" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="16">three levels, deepening questions · the guide asks, dsh-standards decides</text>
17
+ <text x="50" y="122" fill="#4B67B4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="14" font-weight="700" letter-spacing="1.5">ADOPTION DEPTH</text>
18
+ <line x1="38" y1="150" x2="38" y2="482" stroke="#4B67B4" stroke-width="2" stroke-dasharray="4 6" opacity=".6"/>
19
+
20
+ <rect x="50" y="150" width="392" height="92" rx="18" fill="#ffffff" stroke="#e6e3f0" stroke-width="1.3" filter="url(#soft)"/>
21
+ <circle cx="82" cy="196" r="17" fill="#ece7f9"/>
22
+ <text x="82" y="202" text-anchor="middle" fill="#4B67B4" font-family="ui-monospace, monospace" font-size="14" font-weight="700">1</text>
23
+ <text x="116" y="186" fill="#141419" font-family="ui-monospace, monospace" font-size="20" font-weight="700">ENTRY</text>
24
+ <text x="116" y="210" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="15">入门 · 6 个问题</text>
25
+ <text x="116" y="230" fill="#8f94a4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">能装上、能跑、不崩</text>
26
+ <line x1="38" y1="196" x2="50" y2="196" stroke="#4B67B4" stroke-width="1.6" opacity=".35"/>
27
+ <rect x="84" y="268" width="392" height="92" rx="18" fill="#ffffff" stroke="#e6e3f0" stroke-width="1.3" filter="url(#soft)"/>
28
+ <circle cx="116" cy="314" r="17" fill="#ece7f9"/>
29
+ <text x="116" y="320" text-anchor="middle" fill="#4B67B4" font-family="ui-monospace, monospace" font-size="14" font-weight="700">2</text>
30
+ <text x="150" y="304" fill="#141419" font-family="ui-monospace, monospace" font-size="20" font-weight="700">FLOW</text>
31
+ <text x="150" y="328" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="15">顺畅 · 7 个问题</text>
32
+ <text x="150" y="348" fill="#8f94a4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">日常任务顺、错误可恢复</text>
33
+ <line x1="38" y1="314" x2="84" y2="314" stroke="#4B67B4" stroke-width="1.6" opacity=".35"/>
34
+ <rect x="118" y="386" width="392" height="92" rx="18" fill="#ffffff" stroke="#4B67B4" stroke-width="2.4" filter="url(#soft)"/>
35
+ <circle cx="150" cy="432" r="17" fill="#ece7f9"/>
36
+ <text x="150" y="438" text-anchor="middle" fill="#4B67B4" font-family="ui-monospace, monospace" font-size="14" font-weight="700">3</text>
37
+ <text x="184" y="422" fill="#141419" font-family="ui-monospace, monospace" font-size="20" font-weight="700">BOOST</text>
38
+ <text x="184" y="446" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="15">加速 · 5 个问题</text>
39
+ <text x="184" y="466" fill="#8f94a4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12.5">提效证据可复现</text>
40
+ <line x1="38" y1="432" x2="118" y2="432" stroke="#4B67B4" stroke-width="1.6" opacity=".35"/>
41
+ <path d="M510 432 L690 432" stroke="#16a34a" stroke-width="2.6" fill="none" marker-end="url(#arr)"/>
42
+ <text x="512" y="418" fill="#16a34a" font-family="ui-sans-serif, system-ui, sans-serif" font-size="13">verdict to</text>
43
+ <rect x="700" y="300" width="350" height="264" rx="20" fill="#ffffff" stroke="#16a34a" stroke-width="2.4" filter="url(#soft)"/>
44
+ <text x="726" y="334" fill="#16a34a" font-family="ui-monospace, monospace" font-size="13" font-weight="700">dsh-standards</text>
45
+ <text x="726" y="366" fill="#141419" font-family="ui-sans-serif, system-ui, sans-serif" font-size="17">DSC-01...13 static checks</text>
46
+ <text x="726" y="394" fill="#141419" font-family="ui-sans-serif, system-ui, sans-serif" font-size="17">L1-L3 runtime measurement</text>
47
+ <text x="726" y="422" fill="#141419" font-family="ui-sans-serif, system-ui, sans-serif" font-size="17">dsh-levels verdict</text>
48
+ <line x1="726" y1="444" x2="1024" y2="444" stroke="#f2f0f8" stroke-width="2"/>
49
+ <text x="726" y="470" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="13">certification, scoring, ranking: none of them here</text>
50
+ <text x="726" y="494" fill="#8f94a4" font-family="ui-monospace, monospace" font-size="12">guide asks · standards decides</text>
51
+ <text x="726" y="530" fill="#4B67B4" font-family="ui-monospace, monospace" font-size="12">ENTRY / FLOW / BOOST</text>
52
+ <rect x="50" y="560" width="1000" height="52" rx="14" fill="#f7f5fc" stroke="#e6e3f0"/>
53
+ <text x="74" y="582" fill="#4B67B4" font-family="ui-monospace, monospace" font-size="12.5" font-weight="700">route / dsh-levels</text>
54
+ <text x="74" y="602" fill="#4d5261" font-family="ui-sans-serif, system-ui, sans-serif" font-size="13">every question has an owner: the guide says what to check, the verdict belongs to the checker</text>
55
+ </svg>
package/docs/boost.md CHANGED
@@ -90,6 +90,19 @@ pitfalls”), record it as a WARN or a note. Do not invent a number.
90
90
  loader/context-level fixtures.
91
91
  - BOOST is not a market gate and must not become a certification.
92
92
 
93
+ <a id="security-boundary"></a>
94
+ ## Security boundary
95
+
96
+ BOOST measures Cordis practice, not security. A reversible effect or a seam is
97
+ good engineering, and it reduces blast radius, but it is not a sandbox: the
98
+ plugin still shares the host process, the user, and the network. The proposed
99
+ `DSC-15`–`DSC-22` checks are static and advisory outside `--scope security`
100
+ (which `dsh-standards` 1.4.x does not ship yet); they never change a BOOST
101
+ result and none of them proves a plugin safe. Runtime questions — actual egress,
102
+ file reads, prompt corruption — require a sandbox with a network policy and
103
+ capture, not an evidence card. Keep the security claim `not-run` instead of
104
+ borrowing BOOST evidence for it. See [security.md](security.md#scope).
105
+
93
106
  ## Run the audit
94
107
 
95
108
  ```sh
package/docs/entry.md CHANGED
@@ -160,6 +160,10 @@ pass.
160
160
  fs write, or subprocess spawn. Do that work in `apply()` and clean up with
161
161
  `ctx.effect()`.
162
162
  - README says how to install and what it needs.
163
+ - No `preinstall`/`install`/`postinstall`/`prepare` script in the published
164
+ package; if a build is needed, run it in CI, not on the user's machine.
165
+ - No `eval`/`new Function`/`vm`; credentials go through `ctx.credentials`, not
166
+ a backing file; third-party endpoints are disclosed.
163
167
  - No “official”, “certified”, “secure”, or ranking claim.
164
168
 
165
169
  ## Declaration fields
@@ -303,7 +307,42 @@ export function apply(ctx) {
303
307
  }
304
308
  ```
305
309
 
310
+ <a id="security-checks"></a>
311
+ ## Security checks (DSC-15 to DSC-22)
312
+
313
+ ENTRY is a compatibility gate; it does not answer "is this safe to install". The
314
+ 2026-11 audit proposes eight security checks as a separate scope:
315
+
316
+ | Check | What it looks for | Verdict in `--scope security` |
317
+ |---|---|---|
318
+ | `DSC-15` | `preinstall`/`install`/`postinstall`/`prepare` that download, decode, or execute | blocking |
319
+ | `DSC-16` | `eval`, `new Function`, `vm`, `module._compile`, `process.binding` | blocking |
320
+ | `DSC-17` | reads of `~/.ssh`, `~/.aws`, `.npmrc`, `.netrc`, or credential backing files | blocking |
321
+ | `DSC-18` | non-https, userinfo, bare-IP, webhook/paste/tunnel egress targets | WARN (clue) |
322
+ | `DSC-19` | minified blobs, large base64 blocks, wasm/native without source | advisory |
323
+ | `DSC-20` | tool/web text flowing into the system prompt without a boundary | advisory (runtime, v2) |
324
+ | `DSC-21` | typosquat names, fake scopes, unpinned or script-bearing dependencies | blocking in listing |
325
+ | `DSC-22` | `dist.integrity`, signatures, provenance attestations | advisory |
326
+
327
+ These checks are **not released** in `dsh-standards` 1.4.x, and `--scope
328
+ security` is a proposed flag; if your checker exits `3` for an unknown scope,
329
+ that is expected. Outside that scope they do not run, and a security `FAIL` is
330
+ capped at WARN, so it cannot change an ENTRY verdict. Four are mechanical and
331
+ four only return clues — none of them proves a plugin is safe.
332
+
333
+ ```sh
334
+ # proposed; not available in 1.4.x
335
+ npx dsh-standards ./my-plugin --scope security --format json
336
+ ```
337
+
338
+ The author, user, and market checklists — with copyable manifests, commands,
339
+ and the boundary between static checks and a runtime sandbox — are in
340
+ [security.md](security.md). ENTRY's own declaration fields are not enforcement,
341
+ and neither is a `DSC-11` PASS.
342
+
306
343
  ## What ENTRY does not do
307
344
 
308
345
  It is not a security audit, sandbox, full contract audit, market gate, score, or
309
- ranking. Those are separate concerns; do not borrow their authority.
346
+ ranking. Those are separate concerns; do not borrow their authority. The
347
+ proposed `DSC-15`–`DSC-22` are a static security scope, covered in
348
+ [security.md](security.md), not part of the ENTRY verdict.
package/docs/flow.md CHANGED
@@ -32,42 +32,114 @@ a rewrite is detectable.
32
32
  "schema": "dsh-flow-evidence/2",
33
33
  "evidenceId": "01JCM8Z6R4W1V2K3N4P5Q6R7S8",
34
34
  "generatedAt": "2026-11-06T12:00:00Z",
35
- "producer": { "kind": "local", "runner": "flow-collect 0.1.0", "runUrl": null, "actor": "author" },
35
+ "producer": {
36
+ "command": [
37
+ "node",
38
+ "40-checker/bin/dsh-flow-collect.mjs",
39
+ "--package",
40
+ "."
41
+ ],
42
+ "at": "2026-11-06T12:00:00Z",
43
+ "kind": "local",
44
+ "runner": "flow-collect 0.1.0",
45
+ "actor": "author"
46
+ },
36
47
  "subject": {
37
- "package": "@acme/dsh-hello",
48
+ "name": "@acme/dsh-hello",
38
49
  "version": "0.1.0",
39
50
  "source": "npm",
40
- "integrity": "sha512-…",
41
- "tarballSha256": "sha256:…",
51
+ "integrity": "sha512-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
52
+ "tarballSha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
42
53
  "commit": "ce7073d51254c26cd63b22a60835b8f948deb447",
43
- "treeSha256": "sha256:…"
54
+ "treeSha256": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
55
+ "treeStable": true
56
+ },
57
+ "host": {
58
+ "dsh": "0.2.2-alpha.2",
59
+ "dshCommit": null,
60
+ "cordis": "4.0.6-alpha.1",
61
+ "node": "v24.11.0",
62
+ "platform": "linux-x64"
44
63
  },
45
- "host": { "dsh": "0.2.2-alpha.2", "dshCommit": null, "cordis": "4.0.6-alpha.1", "node": "v24.11.0", "platform": "linux-x64" },
46
64
  "journeys": {
47
65
  "J1": {
48
66
  "status": "pass",
49
- "kind": "observed",
67
+ "limitations": [],
50
68
  "checks": [
51
69
  {
52
70
  "id": "J1.install",
53
- "status": "pass",
54
- "command": ["dsh", "plugin", "add", "."],
71
+ "kind": "observed",
72
+ "command": [
73
+ "dsh",
74
+ "plugin",
75
+ "add",
76
+ "."
77
+ ],
55
78
  "exitCode": 0,
56
79
  "startedAt": "2026-11-06T11:00:01Z",
57
80
  "endedAt": "2026-11-06T11:00:09Z",
58
- "stdout": { "path": "logs/J1.install.out", "sha256": "sha256:…" },
59
- "assertions": [{ "claim": "row visible in dump", "observed": true }]
81
+ "stdout": {
82
+ "path": "logs/J1.install.out",
83
+ "sha256": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
84
+ },
85
+ "assertions": [
86
+ {
87
+ "claim": "row visible in dump",
88
+ "observed": true
89
+ }
90
+ ],
91
+ "durationMs": 8000
60
92
  }
61
- ],
62
- "limitations": []
93
+ ]
94
+ },
95
+ "J2": {
96
+ "status": "not-run",
97
+ "reason": "not collected in this example",
98
+ "checks": []
99
+ },
100
+ "J3": {
101
+ "status": "not-run",
102
+ "reason": "not collected in this example",
103
+ "checks": []
104
+ },
105
+ "J4": {
106
+ "status": "not-run",
107
+ "reason": "not collected in this example",
108
+ "checks": []
109
+ },
110
+ "J5": {
111
+ "status": "not-run",
112
+ "reason": "not collected in this example",
113
+ "checks": []
63
114
  },
64
- "J8": { "status": "na", "reason": "no dsh.client" }
115
+ "J6": {
116
+ "status": "not-run",
117
+ "reason": "not collected in this example",
118
+ "checks": []
119
+ },
120
+ "J7": {
121
+ "status": "not-run",
122
+ "reason": "not collected in this example",
123
+ "checks": []
124
+ },
125
+ "J8": {
126
+ "status": "na",
127
+ "reason": "no dsh.client",
128
+ "checks": []
129
+ }
130
+ },
131
+ "claims": {
132
+ "claimLevel": "self-attested",
133
+ "notClaimed": [
134
+ "certification",
135
+ "security audit"
136
+ ],
137
+ "method": "structural example: one observed J1 run; J2-J8 not collected"
65
138
  },
66
- "totals": { "pass": 7, "na": 1, "fail": 0 },
67
- "conclusion": "flow-pass",
68
- "claims": { "claimLevel": "self-attested", "notClaimed": ["certification", "security audit"] },
69
- "chain": { "seq": 12, "prev": "sha256:…", "entrySha256": "sha256:…" },
70
- "signature": null
139
+ "conclusion": "flow-warn",
140
+ "chain": {
141
+ "evidenceHash": "sha256:ee0364bfd26362dca8265c04b7609f6d489c100826c4778e46a1f9f56f3aa138"
142
+ }
71
143
  }
72
144
  ```
73
145
 
@@ -93,40 +165,22 @@ Honesty rules for the file itself:
93
165
  Put the limitation in `limitations[]` instead of dropping the row.
94
166
 
95
167
  <a id="what-the-checker-reads"></a>
96
- ### What reads this file today
97
-
98
- `dsh-levels --flow-evidence` does **not** read the nested v2 shape yet. Its
99
- reader wants a flat map and only reads `status`:
100
-
101
- ```json
102
- {
103
- "J1": { "status": "pass", "evidence": "add → dump → remove; logs/J1.*.out" },
104
- "J2": { "status": "pass", "evidence": "same row id disable/enable; two dumps" }
105
- }
106
- ```
168
+ ### What reads this file
107
169
 
108
- - Accepted values are `pass`, `fail`, `not-run`, `manual`. Anything else,
109
- including `na` and `warn`, is silently downgraded to `not-run`.
110
- - FLOW becomes `runtime` only at **8/8 pass**. One `na` gives `7/8` and
111
- `not-run` — which is the honest answer for a package with no client, but it
112
- is not a pass.
113
- - The flat map has no binding, no hash, and no host. A hand-written 8-pass file
114
- passes. That is why the v2 file above exists: keep the flat map as the
115
- projection, and keep the v2 file plus logs as the thing you can defend.
170
+ `dsh-levels --flow-evidence` reads this nested shape directly since
171
+ `dsh-standards` 1.3.0. The flat `{ "J1": { "status": "pass" } }` map and the
172
+ older `dsh-flow-journey/1` / `dsh-flow-runtime/1` files are `deprecated` and
173
+ never count as a pass.
116
174
 
117
- Project the v2 file to the flat map before running the audit:
118
-
119
- ```sh
120
- node -e '
121
- const fs = require("node:fs");
122
- const evidence = require("./flow-evidence.json");
123
- const flat = {};
124
- for (const [id, journey] of Object.entries(evidence.journeys)) {
125
- flat[id] = { status: journey.status, evidence: journey.reason ?? journey.checks?.[0]?.id ?? null };
126
- }
127
- fs.writeFileSync("flow-flat.json", JSON.stringify(flat, null, 2));
128
- '
129
- ```
175
+ - Every journey claimed as `pass` needs at least one `checks[]` entry with
176
+ `exitCode: 0`, an observed (non-`static`) kind, and a log hash.
177
+ - `na` is a skip with a reason, not a pass. A package with no client ends at
178
+ 7/8 and `not-run` — that is the honest answer.
179
+ - `runtime` is reserved for `sandbox-attested` evidence (`producer.kind =
180
+ "sandbox"` or `claims.claimLevel = "sandbox-attested"`). Self-attested or CI
181
+ evidence keeps its pass count but lands on `not-run`.
182
+ - The file binds subject, host, and a recomputable `chain.evidenceHash`; a
183
+ hand-written flat 8-pass file no longer passes.
130
184
 
131
185
  <a id="minimum-evidence-per-journey"></a>
132
186
  ## Minimum evidence per journey
@@ -147,6 +201,23 @@ the original `command[]`, `exitCode`, the last 20 lines of stderr, and the
147
201
  absolute log path. Then attach the clause anchor the journey maps to (for
148
202
  example `J2.toggle → 32-DSH-FLOW #R2`).
149
203
 
204
+ <a id="security-boundary"></a>
205
+ ## Security boundary
206
+
207
+ J5 checks that permissions are declared consistently; it is static preflight,
208
+ not a security test. The proposed `DSC-15`–`DSC-22` live behind a separate
209
+ `--scope security` that `dsh-standards` 1.4.x does not ship yet, and they never
210
+ turn a FLOW journey into a security pass. Outside that scope a security finding
211
+ is advisory: it is capped at WARN and cannot change the FLOW verdict.
212
+
213
+ Static checks also cannot observe what a plugin does at runtime — which URLs it
214
+ reaches, which files it reads, or whether a prompt was corrupted. Those answers
215
+ need a sandbox with a network policy plus capture, and a sandbox that only
216
+ limits subprocess file writes is not that boundary. So do not write “FLOW
217
+ passes, therefore it is safe”; write what was observed, and keep the security
218
+ claim `not-run` until it was actually run. Details:
219
+ [security.md](security.md#scope).
220
+
150
221
  ## Status, not a score
151
222
 
152
223
  | Status | Meaning |
@@ -186,7 +257,7 @@ the audit is
186
257
  The three-layer audit reads FLOW evidence through `--flow-evidence`:
187
258
 
188
259
  ```sh
189
- npx -p dsh-standards dsh-levels ./my-plugin --flow-evidence flow-flat.json --format json
260
+ npx -p dsh-standards dsh-levels ./my-plugin --flow-evidence flow-evidence.json --format json
190
261
  ```
191
262
 
192
263
  ## Author checklist
package/docs/levels.md CHANGED
@@ -20,6 +20,7 @@
20
20
  | an ENTRY question | [entry.md](entry.md) — package shape, runtime surface, declarations |
21
21
  | a FLOW journey to record | [flow.md](flow.md) — the `dsh-flow-evidence/2` file and J1–J8 evidence |
22
22
  | a BOOST practice to evidence | [boost.md](boost.md) — the thirteen-field evidence card |
23
+ | a security question — what can a plugin do, is this safe to install, what must a market disclose | [security.md](security.md) — author/user/market checklists and the `DSC-15`–`DSC-22` boundary |
23
24
 
24
25
  The two reference docs exist because a report ID alone does not tell you what to
25
26
  change: [rules.md](rules.md) maps IDs to fixes, and [recipes.md](recipes.md)
@@ -73,6 +74,24 @@ Start here: [BOOST guide](boost.md) and the canonical
73
74
  The card fields are in
74
75
  [boost.md § evidence, not a badge](boost.md#thirteen-fields).
75
76
 
77
+ ## Security is not a fourth layer
78
+
79
+ Security checks are a **scope**, not a level, and they are proposed, not
80
+ released: `DSC-15`–`DSC-22` sit behind a `--scope security` that
81
+ `dsh-standards` 1.4.x does not have yet. They never change an ENTRY, FLOW, or
82
+ BOOST verdict, and outside that scope a security `FAIL` is capped at WARN.
83
+
84
+ - Static checks only match known source shapes; they cannot see runtime egress,
85
+ file reads, or prompt corruption. Runtime security needs a sandbox with a
86
+ network policy and capture.
87
+ - No security `PASS` means “safe”. It means “no known pattern matched in this
88
+ static pass”.
89
+ - The three layers stay about compatibility, user journeys, and Cordis practice.
90
+ Do not turn them into a security certification, and do not add a “security
91
+ level” to the vocabulary.
92
+
93
+ Read [security.md](security.md) for the author, user, and market checklists.
94
+
76
95
  ## Run the checks
77
96
 
78
97
  ```sh
@@ -94,5 +113,6 @@ The legacy build skill/CLI is [`dsh-primer`](https://github.com/PerryLink/dsh-pr
94
113
  - `static` ≠ `runtime`
95
114
  - `not-run` ≠ `pass`
96
115
  - `boost-evidenced` ≠ plugin grade
116
+ - security check ≠ security audit
97
117
  - `A–D` grades (where they exist) are orthogonal to ENTRY/FLOW/BOOST
98
118
  - No official, certified, verified, audited, secure, recommended, or ranked claim
package/docs/rules.md CHANGED
@@ -35,6 +35,7 @@ at an older page, this table is the current mapping.
35
35
  | `--scope entry --entry-claim static` | 01, 02, 11, 12, 13 (+ 03 for listing) | 04–09 are attempted and printed (usually `ERROR`/`NA`), 14 is `NA`; a `DSC-11`/`DSC-12`/`DSC-13` FAIL is downgraded to `WARN` |
36
36
  | `--scope entry --entry-claim runtime` | + 14 | 14 carries the runtime verdict, not 04–09 |
37
37
  | `--scope mvp` | 01, 02, 04–09 | the MVP subset, labelled `MVP-13` |
38
+ | `--scope security` (proposed, not in 1.4.x) | + 15, 16, 17, 21; 18/19/20/22 advisory; 21 listing only | security scope only; DS-C 01–14 are unchanged and the report gains an additive `security` block |
38
39
 
39
40
  <a id="table"></a>
40
41
  ## The check table
@@ -55,10 +56,25 @@ at an older page, this table is the current mapping.
55
56
  | **DSC-12** | C-12 credentials + seams | Blocking (ds-c); `WARN` in ENTRY. | Read credentials through `ctx.credentials`, declare `credentials:list` for `listRecords()` and `credentials:provider` for a provider, and never read the backing file. | [entry.md § credentials and seams](entry.md#credentials-and-seams) |
56
57
  | **DSC-13** | C-13 labels + mirror | Blocking (ds-c); `WARN` in ENTRY, advisory as a WARN. | Use only the five labels, add the label each capability requires, point `dsh.upstream` at an https URL that is not this package **and** add `mirror`, and drop endorsement words. | [entry.md § labels and mirror](entry.md#labels-and-mirror) |
57
58
  | **DSC-14** | C-14 entry run | Blocking only for a runtime claim (`--scope entry`). | Supply the real host: `--host` for a bundle (G0–G3) or `--runtime` for a plugin (L1); a client or preset cannot claim runtime. | [entry.md § runtime surface](entry.md#runtime-surface) |
59
+ | **DSC-15** | C-15 lifecycle scripts (proposed) | **Blocking only under `--scope security`**; not run in the default scopes. | Ship no `preinstall`/`install`/`postinstall`/`prepare`; if one is unavoidable, no download-to-shell, `base64 -d`, or `node -e`. | [security.md § author 4](security.md#author-files) |
60
+ | **DSC-16** | C-16 dynamic execution (proposed) | **Blocking only under `--scope security`**. | Remove `eval`, `new Function`, `vm.runIn*`, `module._compile`, and `process.binding`; dynamic `import()` takes a static path only. | [security.md § author 4](security.md#author-files) |
61
+ | **DSC-17** | C-17 sensitive files (proposed) | **Blocking only under `--scope security`**. | Read credentials through `ctx.credentials`; never read `~/.ssh`, `~/.aws`, `.npmrc`, `.netrc`, or a credential backing file. | [security.md § author 2](security.md#author-credentials) |
62
+ | **DSC-18** | C-18 egress endpoints (proposed) | **WARN** under `--scope security` (clue, not blocking). | Use a constant `https` allowlist, `redirect: 'manual'`, no credentials across origins, and disclose third-party endpoints. | [security.md § author 3](security.md#author-egress) |
63
+ | **DSC-19** | C-19 obfuscation/binary (proposed) | **Advisory** under `--scope security`. | No minified blobs or binary-only code; ship wasm/native with source, build method, and `sha256`. | [security.md § author 4](security.md#author-files) |
64
+ | **DSC-20** | C-20 prompt injection (proposed) | **Advisory** (FLOW runtime, v2), never blocking. | Treat tool/web/file/session text as untrusted, validate `output.schema`, and wrap external text before it reaches a prompt. | [security.md § author 5](security.md#author-prompt) |
65
+ | **DSC-21** | C-21 supply chain (proposed) | **Blocking under `--scope security` in the listing profile**; non-blocking outside a listing. | Commit the lockfile, pin dependencies, reject typosquat/lookalike scopes, and treat an unpinned or script-bearing dependency as `not-run`. | [security.md § author 6](security.md#author-publish) |
66
+ | **DSC-22** | C-22 provenance/integrity (proposed) | **Advisory**, never blocking. | Publish with provenance/attestations; `dist.integrity` only binds the tarball to the registry, not the code to a source. | [security.md § author 6](security.md#author-publish) |
58
67
 
59
68
  `DSC-14` has no `C-14` in the clause text; it is the ENTRY run-set gate
60
69
  (`ENT-14` in 31-DSH-ENTRY). Treat it as "did the thing actually start".
61
70
 
71
+ `DSC-15`–`DSC-22` are **proposed** by the 2026-11 audit and are not in
72
+ `dsh-standards` 1.4.x. Only `--scope security` makes `15`/`16`/`17` (and `21`
73
+ in a listing) blocking; in every other scope they are absent or capped at WARN,
74
+ and none of them changes the existing DS-C verdict. They are static evidence:
75
+ four can fail mechanically, four only return clues, and no PASS proves a plugin
76
+ safe. The author/user/market checklists are in [security.md](security.md).
77
+
62
78
  <a id="warn"></a>
63
79
  ## Two things that look like passes but are not
64
80
 
@@ -89,6 +105,11 @@ them:
89
105
  - whether a `red-team` label was actually used for red-team work (`C-13.5`
90
106
  only asks for `dsh.usageScope`)
91
107
  - browser rendering of a client bundle (`G4` is a static shape check)
108
+ - runtime network egress, file reads, or prompt corruption — `DSC-18`/`DSC-20`
109
+ are static clues; observing the behavior needs a sandbox with a network policy
110
+ and capture (see [security.md](security.md#scope))
111
+ - whether a dependency's own install script ran, or whether a package is
112
+ malicious. `DSC-15`/`DSC-21` cover known shapes, not intent.
92
113
 
93
114
  The checker's default `--runtime-version` is `0.2.2-alpha.2`. Pass
94
115
  `--runtime-version <the host you target>` when that differs, or `DSC-02` will
@@ -0,0 +1,385 @@
1
+ # Security: what a plugin can do, and what the checks cannot prove
2
+
3
+ > **Read this first.** A DSH plugin runs in the host's process, as the same
4
+ > user, with the host's full permissions. The host does not sandbox a loaded
5
+ > plugin: the sandbox only restricts file writes made by tool subprocesses, and
6
+ > network/process visibility is outside its vocabulary. `dsh.capabilities` is a
7
+ > declaration the host does not read or enforce. So a plugin can open and write
8
+ > files, spawn processes, reach the network, and read the credential store
9
+ > whether or not it declares that it does.
10
+
11
+ `DSC-15`–`DSC-22` are **proposed** security checks from the 2026-11 audit. They
12
+ are not in `dsh-standards` 1.4.x, and `--scope security` is a proposed scope, not
13
+ a released flag. When they ship, four are mechanical (`15`, `16`, `17`, `21`)
14
+ and four only produce clues (`18`, `19`, `20`, `22`); all eight reduce risk, and
15
+ **none of them proves a plugin is safe**. Until then, this page is a checklist,
16
+ not a gate.
17
+
18
+ The same boundary applies to the existing scopes: outside `--scope security`,
19
+ a security `FAIL` can land in `buckets.blocking` while `status` and the exit
20
+ code stay `0`, because only the mandatory set decides the verdict. Read the
21
+ scope, not the bucket.
22
+
23
+ ## The trust boundary in one table
24
+
25
+ | Fact | What follows |
26
+ |---|---|
27
+ | plugin and host share a process | `import node:fs`, `fetch`, `child_process`, and `process.env` are always reachable |
28
+ | `dsh.capabilities` is a declaration | declaring `[]` does not remove a capability; over-declaring is a WARN, under-declaring is a static FAIL |
29
+ | the sandbox covers tool subprocess file writes only | it is not a boundary around plugin code, and not a network policy |
30
+ | the checker executes the plugin | `--runtime`/C-4/C-14 import it; the check path has no sandbox and inherits `HOME` |
31
+ | static checks see known source shapes | they cannot see runtime egress, dynamic URLs, or whether a prompt was really corrupted |
32
+ | runtime safety needs containment | a network-policy sandbox plus capture/audit is the only way to observe what code actually does |
33
+
34
+ <a id="author"></a>
35
+ ## Author guide
36
+
37
+ <a id="author-least-privilege"></a>
38
+ ### 1. Least privilege and the capability declaration (DSC-16, DSC-18)
39
+
40
+ Declare only the closed-set IDs the plugin really touches. Every static trigger
41
+ needs its entry (`C-11.2`), and an entry with no trigger is an over-declaration
42
+ WARN (`C-11.5`). There are 15 IDs: `fs:read`, `fs:write`, `shell`, `subprocess`,
43
+ `network:egress`, `network:ingress`, `credentials:read`, `credentials:list`,
44
+ `credentials:provider`, `llm:adapter`, `sessions:read`, `system-prompt:write`,
45
+ `tools:register`, `remote:expose`, `plugin:install`.
46
+
47
+ ```json
48
+ {
49
+ "dsh": {
50
+ "capabilities": ["tools:register"]
51
+ },
52
+ "security": {
53
+ "status": "draft",
54
+ "note": "non-host disclosure only; not read, not enforced",
55
+ "permissions": {
56
+ "files": [],
57
+ "network": [],
58
+ "credentials": [],
59
+ "shell": false,
60
+ "subprocess": false
61
+ }
62
+ }
63
+ }
64
+ ```
65
+
66
+ `dsh.permissions` and `security.permissions` are **not host fields**. The
67
+ standard is not fixed, so this guide prints a `security.permissions` block
68
+ marked `draft`: it is disclosure for a human or a market, never enforcement.
69
+ If you do not want to make the disclosure claim, delete the block — an absent
70
+ field means "not declared", not "no permissions". `dsh.commitments` and
71
+ `dsh.disclosure` are the same kind of optional disclosure: `false` means "no
72
+ promise", absent means "not declared". Do not fill every commitment `true`; an
73
+ unsupported claim is worse than a missing one.
74
+
75
+ <a id="author-credentials"></a>
76
+ ### 2. Credentials and sensitive files (DSC-17)
77
+
78
+ Resolve a reference per operation, through the seam, and never print, log,
79
+ persist, or put a value in `argv`. `describe()` is the only shape a UI should
80
+ see. Do not read `$DSH_HOME/.credentials.yaml`, `~/.ssh`, `~/.aws`, `~/.netrc`,
81
+ `.npmrc`, or any other backing file, and do not enumerate unrelated records.
82
+
83
+ ```ts
84
+ import { credentialRef } from '@deepseek-ai/dsh-credentials'
85
+
86
+ const ref = credentialRef('MY_API_KEY')
87
+ const resolved = await ctx.credentials.resolve(ref) // re-resolve per operation
88
+ if (!resolved) throw new Error('MY_API_KEY is not configured') // never print resolved.value
89
+
90
+ // configuration UI: presence and writability only
91
+ const info = await ctx.credentials.describe(ref)
92
+ ```
93
+
94
+ Environment scrubbing applies to `ctx.subprocess`: the child gets a scrubbed
95
+ parent environment, and an explicit `env` entry is a deliberate opt-in. Do not
96
+ forward a token "just in case".
97
+
98
+ <a id="author-egress"></a>
99
+ ### 3. Egress endpoints (DSC-18)
100
+
101
+ Use a constant allowlist of exact hostnames and `https` only. Never fetch a URL
102
+ that came from a model, a tool result, or a user message; redirects are checked
103
+ hop by hop, and a credential is never sent across an origin. Disclose third-party
104
+ endpoints, what is retained, and whether session text is forwarded.
105
+
106
+ ```ts
107
+ const ALLOW = new Set(['api.example.com'])
108
+
109
+ export function checkedUrl(raw: string): URL {
110
+ const url = new URL(raw)
111
+ if (url.protocol !== 'https:' || !ALLOW.has(url.hostname)) {
112
+ throw new Error('blocked endpoint')
113
+ }
114
+ return url
115
+ }
116
+
117
+ const res = await fetch(checkedUrl('https://api.example.com/v1/x'), {
118
+ redirect: 'manual',
119
+ })
120
+ ```
121
+
122
+ The host has no egress policy and `web_fetch` can reach private addresses. This
123
+ allowlist is the plugin's own discipline, not a sandbox. If you must call a
124
+ third party, say so in `dsh.disclosure.endpoints` (when that field exists) and
125
+ in the README.
126
+
127
+ <a id="author-files"></a>
128
+ ### 4. Files, subprocesses, and dynamic execution (DSC-15, DSC-16, DSC-17, DSC-19)
129
+
130
+ - Use `ctx.fs`: `resolve()` the target, then `contains(workspace, target)`.
131
+ Reject `..`, absolute paths, and symlinks (check with `lstat` first), bound the
132
+ size, and keep temp files at `0700`/`0600` (ACL on Windows).
133
+ - Do not start a subprocess by default. When you must, use
134
+ `ctx.subprocess.spawn` with an `argv` array, an absolute executable, `cwd`
135
+ inside the workspace, the scrubbed environment, a timeout/abort, and bounded
136
+ output. Never build a shell command string.
137
+ - Never use `eval`, `new Function`, `vm.runIn*`, `module._compile`, or
138
+ `process.binding`. Dynamic `import()` takes a static path only. Do not publish
139
+ minified blobs or binary-only code; if you ship wasm/native, ship the source,
140
+ the build method, and a `sha256`.
141
+ - Lifecycle scripts are the install-time RCE surface (`DSC-15`): ship none. If a
142
+ build truly needs one, never pipe a download into a shell, and never hide it
143
+ behind `base64 -d` or `node -e`.
144
+
145
+ ```ts
146
+ const workspace = await ctx.fs.resolve(process.cwd())
147
+ const target = await ctx.fs.resolve(userPath)
148
+ if (!ctx.fs.contains(workspace, target)) throw new Error('path escapes the workspace')
149
+
150
+ const child = ctx.subprocess.spawn({
151
+ argv: ['/usr/bin/git', 'status'],
152
+ cwd: process.cwd(),
153
+ stdio: { stdin: 'ignore', stdout: { maxBytes: 64_000 }, stderr: { maxBytes: 64_000 } },
154
+ graceMs: 5_000,
155
+ signal,
156
+ })
157
+ const outcome = await child.done
158
+ ```
159
+
160
+ The sandbox still only covers subprocess file writes; a plugin's own `fetch` and
161
+ `node:fs` bypass every seam. Do not read a declaration as isolation.
162
+
163
+ <a id="author-prompt"></a>
164
+ ### 5. Tool output and prompt injection (DSC-20)
165
+
166
+ Tool results, web pages, files, and session text are **untrusted data**. Validate
167
+ them against the tool's `output.schema` (a mismatch is `INVALID_TOOL_OUTPUT`),
168
+ and wrap external text with a fixed untrusted prefix, a per-call random boundary,
169
+ and JSON escaping before it reaches a prompt. Never put external text in the
170
+ system prompt or turn it into instructions. Instruction hierarchy belongs in
171
+ code: system > user > external data. Model output stays data; validate it, do
172
+ not shell it, and do not let it choose an arbitrary URL.
173
+
174
+ ```ts
175
+ import { randomUUID } from 'node:crypto'
176
+
177
+ const id = randomUUID()
178
+ const wrap = (s: string) =>
179
+ `<untrusted-${id}>\n${JSON.stringify(s).replaceAll('<', '\\u003c')}\n</untrusted-${id}>`
180
+ ```
181
+
182
+ Rendering is part of the same boundary: use `textContent`/React escaping, never
183
+ `innerHTML`, and escape log and error text too. Wrapping lowers the success rate
184
+ of injection; it is not isolation.
185
+
186
+ <a id="author-publish"></a>
187
+ ### 6. Dependencies, build, and publish (DSC-15, DSC-21, DSC-22)
188
+
189
+ Commit the lockfile and install with `npm ci --ignore-scripts` (or
190
+ `--frozen-lockfile`). Keep dependencies minimal and check for typosquats —
191
+ lookalike scopes and edit-distance names around `dsh`/`@deepseek-ai/dsh`. Use a
192
+ `files` allowlist, publish the build output, pin Node and the package manager,
193
+ and make a tag rebuild reproducibly.
194
+
195
+ ```sh
196
+ npm ci --ignore-scripts && npm test && npm pack --dry-run
197
+ npm audit --omit=dev && npm audit signatures
198
+ # CI only: trusted publishing, provenance, no long-lived token
199
+ npm publish --access public --provenance
200
+ ```
201
+
202
+ Publish with npm trusted publishing (OIDC) and `--provenance`; keep 2FA on,
203
+ protect tags, require review, and never reuse a version (deprecate a bad one).
204
+ Check that `package.json` has no `preinstall`/`install`/`postinstall`/`prepare`:
205
+
206
+ ```sh
207
+ node -e "const s=require('./package.json').scripts||{}; if(s.preinstall||s.install||s.postinstall||s.prepare) process.exit(1)"
208
+ ```
209
+
210
+ `dist.integrity` only proves the tarball matches the registry, and an
211
+ attestation needs signature verification plus a bound build source. Neither is
212
+ a code-safety proof.
213
+
214
+ <a id="author-response"></a>
215
+ ### 7. Vulnerability response (process, not a DSC)
216
+
217
+ Ship a `SECURITY.md` with a private channel (GitHub Security Advisories), the
218
+ supported versions, a 7-day first response, and 90-day coordinated disclosure.
219
+ Do not take vulnerability reports in public issues. After a fix, publish a
220
+ patch release, an advisory, and credit; revoke and rotate leaked tokens; for a
221
+ malicious version, deprecate it, ask the market to delist, and keep the
222
+ evidence.
223
+
224
+ ```md
225
+ # Security Policy
226
+
227
+ ## Supported versions
228
+ | Version | Supported |
229
+ |---|---|
230
+ | 0.1.x | yes |
231
+
232
+ ## Reporting
233
+ Use <repo>/security/advisories/new; do not open a public issue.
234
+ Include the version, DSH version, impact, and a minimal repro. First response within 7 days.
235
+ ```
236
+
237
+ <a id="user"></a>
238
+ ## User guide
239
+
240
+ The checks tell you what a package declares; they do not tell you what it does.
241
+ Assume a loaded plugin can do anything the user can. The proposed checks that
242
+ touch this path are `DSC-15` (lifecycle scripts), `DSC-21` (source pinning,
243
+ typosquat), and `DSC-22` (integrity/provenance) before install; `DSC-17`/`DSC-18`
244
+ only ever produce static clues about files and egress.
245
+
246
+ **Before installing.** Read the manifest: `dsh.capabilities`, `dsh.labels`,
247
+ `scripts`, `files`, `engines.dsh`, and the source pin. Then look at the source
248
+ and the publisher: an exact npm version with `dist.integrity`, or a git
249
+ 40-hex commit; how recent the release is; whether `dist.signatures` /
250
+ `dist.attestations` exist; and whether an install script is present. An empty
251
+ capabilities list means **not declared**, not "no capabilities". A label such as
252
+ `prompt-modification`, `third-party-model-provider`, `remote-access`,
253
+ `red-team`, or `mirror` is the author telling you which risk to check, not a
254
+ clearance.
255
+
256
+ ```sh
257
+ npm view pkg@1.2.3 version dsh scripts dist.integrity dist.signatures dist.attestations repository.url time.modified --json
258
+ ```
259
+
260
+ **During install.** Start from a throwaway profile (`dsh plugin --profile lab
261
+ ...` initializes it), add `--ignore-scripts`, and understand that it stops
262
+ install scripts only, not the code the host loads. The GUI build approval runs
263
+ a script with your permissions: read the script before approving. The CLI
264
+ inherits your whole environment, so launch it from a clean shell with no
265
+ tokens. A new session defaults to `workspace-write` + `ask`; do not switch to
266
+ `danger-full-access` with `never`. `DSH_PERMISSION_MODE=read-only` constrains
267
+ tool subprocesses, not plugins.
268
+
269
+ ```sh
270
+ dsh plugin --profile lab add pkg@1.2.3 --ignore-scripts
271
+ dsh --profile lab --dump-config
272
+ dsh plugin --profile lab remove pkg
273
+ ```
274
+
275
+ **After install.** Return to `workspace-write`, watch the install log and the
276
+ session's tool calls, and remove the package when you no longer need it. Check
277
+ the profile's `package.json`/lockfile and the credential records. If something
278
+ looks wrong, disable it, keep the logs and the package hash, and report through
279
+ its `SECURITY.md` (for a malicious npm package, also npm support). Do not
280
+ publish the details first.
281
+
282
+ <a id="market"></a>
283
+ ## Market / repository guide
284
+
285
+ **Record facts before listing.** `name@version`; source (npm integrity or git
286
+ 40-hex plus subdir); `dsh.bundle.patch` (no `!!js`), `files`, `engines.dsh`, and
287
+ whether `scripts` contains an install hook; publisher vs `repository`;
288
+ `SECURITY.md`, 2FA, protected tags; publish time; provenance/attestations;
289
+ capability/label validity; license (advisory). Do not install or execute the
290
+ package to fill the row, and do not grade, score, or rank it. These facts map
291
+ to `DSC-15`–`DSC-22`: a row records them, it does not certify them.
292
+
293
+ ```sh
294
+ npm view pkg@1.2.3 --json
295
+ mkdir -p inspect && npm pack pkg@1.2.3 --ignore-scripts --pack-destination ./inspect && tar -tzf inspect/*.tgz
296
+ git clone --filter=blob:none --no-checkout <url> r && git -C r archive <40hex> | tar -t
297
+ ```
298
+
299
+ **Show risk, not a badge.** Put capabilities in the same view as labels and mark
300
+ them "author declaration, not enforced"; mark unknowns "not declared / not
301
+ checked / not run". The index's only risk field is labels; capabilities must be
302
+ read from the manifest. Missing data is never a pass. State that static checks
303
+ cover known patterns and are not a security audit. The checker can import the
304
+ plugin, so do not run it against an untrusted package on a host you care about.
305
+
306
+ **Disputes and delisting.** Hard-reject only four cases: not a plugin /
307
+ unparseable, source not pinnable, impersonation or infringement, and a malicious
308
+ security hold (manual review with an appeal). List everything else with the
309
+ finding attached. The author can withdraw at any time; a 90-day expiry removes
310
+ only the label; a 14-day appeal window; keep history for at least two years and
311
+ write the reason to `market-actions.jsonl`.
312
+
313
+ **Wording ban.** Never write "secure", "certified", "official", "audited", or
314
+ "100% risk-free", and never imply certification with a shield or a checkmark.
315
+ Use the fixed disclaimer: "unofficial · not certified · not security-audited ·
316
+ not endorsed · no warranty".
317
+
318
+ <a id="template"></a>
319
+ ## Template: minimal secure defaults
320
+
321
+ The smallest honest starting point is: no lifecycle scripts, empty
322
+ capabilities, a permissions disclosure marked draft, and a `SECURITY.md` in the
323
+ tarball. The `dsh-scaffold` template ships these defaults.
324
+
325
+ ```json
326
+ {
327
+ "name": "@acme/dsh-hello",
328
+ "version": "0.1.0",
329
+ "license": "Apache-2.0",
330
+ "files": ["lib", "cordis.patch.yml", "SECURITY.md", "LICENSE"],
331
+ "scripts": { "test": "vitest run" },
332
+ "dsh": {
333
+ "capabilities": [],
334
+ "labels": []
335
+ },
336
+ "security": {
337
+ "status": "draft",
338
+ "note": "non-host disclosure only; not read, not enforced",
339
+ "permissions": { "files": [], "network": [], "credentials": [], "shell": false, "subprocess": false }
340
+ }
341
+ }
342
+ ```
343
+
344
+ `SECURITY.md` must name the private channel, the supported versions, and the
345
+ response window — see the author section above. Verify the shape before publish:
346
+
347
+ ```sh
348
+ node -e "const s=require('./package.json').scripts||{}; if(s.preinstall||s.install||s.postinstall||s.prepare) process.exit(1)"
349
+ npm pack --dry-run # SECURITY.md must be in the file list
350
+ ```
351
+
352
+ This shape is the starting point for `DSC-15` (no lifecycle scripts), `DSC-21`
353
+ (no typosquat dependency), and `DSC-22` (provenance publishing); it also gives
354
+ `DSC-17` and `DSC-18` a truthful empty disclosure to read. An empty
355
+ `capabilities` array is correct only while the plugin touches no host surface.
356
+ Once it registers a tool or reads `ctx.fs`, add the ID; a copied template with a
357
+ stale empty array hides the very `C-11.2` failure the check is for.
358
+
359
+ <a id="scope"></a>
360
+ ## Where the security checks sit
361
+
362
+ | Scope | What runs | Verdict |
363
+ |---|---|---|
364
+ | `--scope ds-c` (default) / `entry` / `flow` / `boost` | DSC-01–14 mandatory; DSC-15–22 non-mandatory if implemented | a security `FAIL` is recorded in `blocking` but cannot change `status`/exit |
365
+ | `--scope security` (proposed, not released) | + DSC-15–22 | `15`/`16`/`17` blocking; `21` blocking in the listing profile; `18` WARN; `19`/`20`/`22` advisory |
366
+ | runtime observation | sandbox with a network policy + capture | the only evidence that code actually egressed, read a file, or rewrote a prompt |
367
+
368
+ When the scope ships, the JSON report gets an additive top-level `security`
369
+ block; the DS-C verdicts stay unchanged. A PASS in that block means "no known
370
+ shape matched in this static pass", not "safe".
371
+
372
+ <a id="limits"></a>
373
+ ## What no static check can tell you
374
+
375
+ - whether a URL was really fetched, or where a redirect went;
376
+ - whether a file outside the package was read or written;
377
+ - whether a dependency's own install script ran;
378
+ - whether a model was actually steered by injected text;
379
+ - whether the author is trustworthy — signatures are self-asserted evidence
380
+ until the key chain and build provenance are verified by a human.
381
+
382
+ Runtime security needs a sandbox with an egress policy and an audit trail.
383
+ Windows has no `unshare`, so the containment story is weaker there, and a
384
+ sandbox that only limits writes is not a network boundary. Until you have that
385
+ evidence, write `not-run` or `not-measured` — never "secure".
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-guide",
3
- "version": "0.4.5",
3
+ "version": "0.5.0",
4
4
  "description": "A practical guide to DSH plugin quality: ENTRY checks, FLOW checks, and BOOST guidance. Community guide; not official, not a certification, not a ranking.",
5
5
  "type": "module",
6
6
  "main": "index.js",