ssh-mcp-pro 1.1.5 → 1.2.3

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.
Files changed (135) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +47 -7
  3. package/SECURITY.md +4 -0
  4. package/SECURITY_DECISIONS.md +32 -1
  5. package/dist/agent-bin.js +0 -0
  6. package/dist/connector-profile.d.ts +19 -2
  7. package/dist/connector-profile.d.ts.map +1 -1
  8. package/dist/connector-profile.js +36 -14
  9. package/dist/connector-profile.js.map +1 -1
  10. package/dist/http-security.d.ts.map +1 -1
  11. package/dist/http-security.js +19 -4
  12. package/dist/http-security.js.map +1 -1
  13. package/dist/http-server-lifecycle.d.ts +17 -0
  14. package/dist/http-server-lifecycle.d.ts.map +1 -0
  15. package/dist/http-server-lifecycle.js +122 -0
  16. package/dist/http-server-lifecycle.js.map +1 -0
  17. package/dist/http-server-router.d.ts +28 -0
  18. package/dist/http-server-router.d.ts.map +1 -0
  19. package/dist/http-server-router.js +108 -0
  20. package/dist/http-server-router.js.map +1 -0
  21. package/dist/http-server-runtime.d.ts +21 -0
  22. package/dist/http-server-runtime.d.ts.map +1 -0
  23. package/dist/http-server-runtime.js +415 -0
  24. package/dist/http-server-runtime.js.map +1 -0
  25. package/dist/http-session-registry.d.ts.map +1 -1
  26. package/dist/http-session-registry.js.map +1 -1
  27. package/dist/index.js +12 -5
  28. package/dist/index.js.map +1 -1
  29. package/dist/mcp.d.ts +1 -1
  30. package/dist/mcp.js +1 -1
  31. package/dist/prompts.d.ts +2 -11
  32. package/dist/prompts.d.ts.map +1 -1
  33. package/dist/prompts.js +8 -1
  34. package/dist/prompts.js.map +1 -1
  35. package/dist/remote/agent-cli.d.ts.map +1 -1
  36. package/dist/remote/agent-cli.js +20 -1
  37. package/dist/remote/agent-cli.js.map +1 -1
  38. package/dist/remote/agent-connection-lifecycle.d.ts +47 -0
  39. package/dist/remote/agent-connection-lifecycle.d.ts.map +1 -0
  40. package/dist/remote/agent-connection-lifecycle.js +304 -0
  41. package/dist/remote/agent-connection-lifecycle.js.map +1 -0
  42. package/dist/remote/agent-executor.d.ts.map +1 -1
  43. package/dist/remote/agent-executor.js +89 -67
  44. package/dist/remote/agent-executor.js.map +1 -1
  45. package/dist/remote/agent-handler.d.ts +22 -8
  46. package/dist/remote/agent-handler.d.ts.map +1 -1
  47. package/dist/remote/agent-handler.js +151 -98
  48. package/dist/remote/agent-handler.js.map +1 -1
  49. package/dist/remote/config.d.ts.map +1 -1
  50. package/dist/remote/config.js +14 -0
  51. package/dist/remote/config.js.map +1 -1
  52. package/dist/remote/control-plane.d.ts.map +1 -1
  53. package/dist/remote/control-plane.js +16 -0
  54. package/dist/remote/control-plane.js.map +1 -1
  55. package/dist/remote/oauth-handler.d.ts +6 -0
  56. package/dist/remote/oauth-handler.d.ts.map +1 -1
  57. package/dist/remote/oauth-handler.js +61 -1
  58. package/dist/remote/oauth-handler.js.map +1 -1
  59. package/dist/remote/policy-update-guard.d.ts +14 -0
  60. package/dist/remote/policy-update-guard.d.ts.map +1 -0
  61. package/dist/remote/policy-update-guard.js +27 -0
  62. package/dist/remote/policy-update-guard.js.map +1 -0
  63. package/dist/remote/policy.d.ts.map +1 -1
  64. package/dist/remote/policy.js +13 -2
  65. package/dist/remote/policy.js.map +1 -1
  66. package/dist/remote/safe-file.d.ts +6 -0
  67. package/dist/remote/safe-file.d.ts.map +1 -0
  68. package/dist/remote/safe-file.js +192 -0
  69. package/dist/remote/safe-file.js.map +1 -0
  70. package/dist/remote/store.d.ts.map +1 -1
  71. package/dist/remote/store.js.map +1 -1
  72. package/dist/remote/types.d.ts +5 -0
  73. package/dist/remote/types.d.ts.map +1 -1
  74. package/dist/remote/websocket.d.ts +38 -13
  75. package/dist/remote/websocket.d.ts.map +1 -1
  76. package/dist/remote/websocket.js +90 -126
  77. package/dist/remote/websocket.js.map +1 -1
  78. package/dist/render-http.js +2 -1
  79. package/dist/render-http.js.map +1 -1
  80. package/dist/resources.d.ts +2 -7
  81. package/dist/resources.d.ts.map +1 -1
  82. package/dist/resources.js.map +1 -1
  83. package/dist/retry.d.ts.map +1 -1
  84. package/dist/retry.js +4 -2
  85. package/dist/retry.js.map +1 -1
  86. package/dist/server-http.d.ts +1 -1
  87. package/dist/server-http.d.ts.map +1 -1
  88. package/dist/server-http.js +1 -421
  89. package/dist/server-http.js.map +1 -1
  90. package/dist/session.d.ts +4 -0
  91. package/dist/session.d.ts.map +1 -1
  92. package/dist/session.js +38 -15
  93. package/dist/session.js.map +1 -1
  94. package/dist/tools/connector.provider.d.ts.map +1 -1
  95. package/dist/tools/connector.provider.js +2 -1
  96. package/dist/tools/connector.provider.js.map +1 -1
  97. package/dist/types.d.ts.map +1 -1
  98. package/dist/types.js.map +1 -1
  99. package/docs/agent-runtime-config.md +34 -0
  100. package/docs/development/api-stability.md +44 -0
  101. package/docs/development/coding-standards.md +41 -0
  102. package/docs/development/commit-conventions.md +35 -0
  103. package/docs/development/dependency-management.md +60 -0
  104. package/docs/development/deprecation-policy.md +29 -0
  105. package/docs/development/local-setup.md +46 -0
  106. package/docs/development/release-process.md +31 -0
  107. package/docs/development/testing-policy.md +47 -0
  108. package/docs/explanation/architecture.md +44 -0
  109. package/docs/how-to/README.md +14 -0
  110. package/docs/how-to/configure-ssh-policy.md +50 -0
  111. package/docs/openssf-evidence.md +75 -0
  112. package/docs/openssf-gap-analysis.md +63 -0
  113. package/docs/openssf-proposal-links.md +37 -0
  114. package/docs/professionalization-plan.md +81 -0
  115. package/docs/reference/README.md +21 -0
  116. package/docs/reference/cli.md +41 -0
  117. package/docs/reference/compatibility.md +46 -0
  118. package/docs/reference/configuration.md +72 -0
  119. package/docs/remote-mcp-hardening.md +27 -0
  120. package/docs/repo-maturity-report.md +565 -0
  121. package/docs/security/assurance-case.md +58 -0
  122. package/docs/security/dependency-audit-exceptions.json +4 -0
  123. package/docs/security/dependency-audit-exceptions.md +15 -0
  124. package/docs/security/input-validation.md +54 -0
  125. package/docs/security/release-integrity.md +28 -0
  126. package/docs/security/scanning-policy.md +50 -0
  127. package/docs/security/secrets-management.md +49 -0
  128. package/docs/security/threat-model.md +65 -0
  129. package/docs/testing.md +24 -0
  130. package/docs/troubleshooting.md +56 -0
  131. package/docs/tutorials/getting-started.md +84 -0
  132. package/mcp.json +1 -1
  133. package/package.json +86 -70
  134. package/registry/ssh-mcp-pro/mcp.json +1 -1
  135. package/server.json +3 -3
@@ -0,0 +1,565 @@
1
+ # Repository Maturity Report — ssh-mcp-pro
2
+
3
+ **Date:** 2026-07-03
4
+ **Scope:** `oaslananka/ssh-mcp-pro` (GitHub + local checkout)
5
+ **Method:** Static inspection of repo contents, GitHub API (branch protection, security
6
+ alerts, collaborators, releases, open PRs, workflow runs), and workflow source review.
7
+ Classifications use only observed evidence; no criterion is marked `Passed` without a
8
+ file, workflow run, or API response backing it.
9
+
10
+ Legend for evidence tables (current-state assessment): `Passed` · `Partial` · `Missing` ·
11
+ `Not applicable` · `Needs human confirmation`.
12
+
13
+ Legend for prioritization tables (refactor/action items): `Required now` ·
14
+ `Recommended` · `Optional` · `Future` · `Not applicable` · `Needs human confirmation`.
15
+ Each table states which legend it uses.
16
+
17
+ > **Update (2026-07-03, same day, post-merge):** The two PRs this report originally
18
+ > described as pending (`repo-maturity/professional-oss-hardening`,
19
+ > `security-fixes/codeql-and-supply-chain`) have both merged to `main`, along with two
20
+ > Dependabot Actions-version-bump PRs. What changed on the ground, in brief — full detail
21
+ > is in the sections below, not just here:
22
+ >
23
+ > - CodeQL: all 8 alerts closed (1 fixed in code, 7 dismissed with verified rationale —
24
+ > see "Security/supply-chain maturity"). 0 open CodeQL alerts as of this update.
25
+ > - Private vulnerability reporting: confirmed **enabled** via
26
+ > `GET /repos/.../private-vulnerability-reporting` (was "needs human confirmation").
27
+ > - Dependabot vulnerability alerts and automated security fixes: both **enabled** (were
28
+ > disabled).
29
+ > - **New finding, not present at initial audit time:** the Trivy container scan added in
30
+ > the security-fixes PR surfaced **50+ open alerts** against the built Docker image —
31
+ > see "Package publishing maturity" and "Security/supply-chain maturity" below. This is
32
+ > a real gap the original audit had no visibility into (Trivy didn't exist in the
33
+ > pipeline yet), not a regression introduced by merging.
34
+ > - Branch protection, the release-please PR (#1), and npm trusted-publisher registration
35
+ > remain untouched, exactly as originally recommended — still manual/maintainer
36
+ > decisions.
37
+ >
38
+ > The narrative below is left largely as originally written (including the "before this
39
+ > PR" framing) so the audit trail stays intact; treat table rows tagged **[updated]**
40
+ > as current, and the executive summary's "seven CodeQL alerts... open" and "three PRs to
41
+ > date" as the state *at the time of the original audit*, not now.
42
+
43
+ ## Executive summary
44
+
45
+ ssh-mcp-pro is a five-day-old (created 2026-06-28), single-maintainer TypeScript project
46
+ with an unusually deep engineering foundation for its age: pinned-SHA GitHub Actions,
47
+ CodeQL, OpenSSF Scorecard, REUSE-compliant licensing, Conventional Commits tooling,
48
+ release-please automation with SBOM generation, SHA-256 checksums, and build-provenance
49
+ attestation, a Governance issue-taxonomy, coverage thresholds (85–90%), and mutation
50
+ testing on policy-critical files. Most of the *tooling* substance normally associated
51
+ with "Professional/Mature OSS" is already present.
52
+
53
+ What is missing is not tooling but the parts of maturity that require either people or
54
+ time: there is one collaborator (`oaslananka`, admin), `main` has no GitHub branch
55
+ protection or ruleset actually applied (a ruleset JSON exists in-repo but is not active
56
+ per the Branch Protection API), no release has ever been published, no external
57
+ contributor or reviewer has ever opened or reviewed a PR (the three PRs to date are all
58
+ bot-authored — Dependabot ×2, release-please ×1), and seven CodeQL alerts (one High) are
59
+ open. Community-health documents that depend on there being a community —
60
+ `CODE_OF_CONDUCT.md`, `GOVERNANCE.md`, `MAINTAINERS.md`, `ROADMAP.md` — were absent
61
+ before this change.
62
+
63
+ This PR closes the documentation/governance/evidence gaps that are safe to close without
64
+ a maintainer decision, and adds one low-risk workflow (`gitleaks.yml`). It does not
65
+ enable branch protection, does not change Dependabot/security settings, does not touch
66
+ CodeQL findings, and does not claim OpenSSF Silver/Gold — those require actions only a
67
+ repository admin or a second maintainer can take, and are listed explicitly under
68
+ "Next actions" and in the PR's manual-actions checklist.
69
+
70
+ ## Current maturity level
71
+
72
+ **CNCF-style framing:** between **Experimental** and **Incubating-like**. The
73
+ engineering rigor (CI breadth, security automation, mutation testing, release
74
+ provenance) reads like a mature/Incubating project. The social characteristics (single
75
+ maintainer, zero external contributors, zero shipped releases, no branch protection)
76
+ read like an early Experimental/Sandbox project. Repo age (5 days) and commit count (3
77
+ feature commits on `main` at time of audit) mean CHAOSS metrics like release cadence,
78
+ time-to-first-response, and contributor growth have no history to measure yet — they are
79
+ reported as `Not applicable (insufficient history)` rather than graded.
80
+
81
+ **Recommended overall label: "Professional solo-maintainer OSS project, pre-first-release."**
82
+
83
+ ## Target maturity level
84
+
85
+ **Professional OSS / Mature OSS**, as scoped by this task. Concretely, that means: full
86
+ GitHub Community Standards checklist satisfied, OpenSSF Best Practices **Passing**
87
+ tier achievable once a couple of process gaps close, Scorecard score improved by
88
+ enabling branch protection and dependency-review enforcement, Diátaxis-organized docs,
89
+ and governance docs that honestly describe a solo-maintainer project rather than
90
+ pretending otherwise.
91
+
92
+ **Gold/foundation-grade is explicitly out of scope for a claim in this report.** Gold
93
+ requires multiple active maintainers, independent contributor/reviewer participation,
94
+ routine human code review, branch protection, high test coverage sustained over time,
95
+ and a repeatable release history — none of which can exist five days after repo
96
+ creation with one collaborator. See "Gold / foundation-grade gap analysis" in
97
+ [docs/openssf-gap-analysis.md](openssf-gap-analysis.md) for what would need to be true
98
+ before that label is reconsidered.
99
+
100
+ ## Repository inventory
101
+
102
+ | Attribute | Value | Evidence |
103
+ | --- | --- | --- |
104
+ | Visibility | Public | `GET /repos/oaslananka/ssh-mcp-pro` |
105
+ | Default branch | `main` | Same |
106
+ | License | MIT | `LICENSE`, repo metadata `license.spdx_id: MIT` |
107
+ | Maintenance signal | Active — created 2026-06-28, commits and one open release PR as of this audit | Commit history, `list_workflow_runs` |
108
+ | Archived/deprecated signal | None found | `archived: false`, `disabled: false` |
109
+ | Primary purpose | MCP server exposing policy-guarded SSH automation tools to LLM clients | `README.md` intro, `package.json` `description` |
110
+ | Stated maturity in README | Not explicitly labeled experimental/beta/stable | README documents strict security defaults and a full config surface, which reads as production-intent, but no version-stability statement (e.g. "pre-1.0, breaking changes expected") is made anywhere — **Needs human confirmation**: is `1.x` intended as stable, or still stabilizing? |
111
+
112
+ ## Language and package ecosystem inventory
113
+
114
+ | Attribute | Value |
115
+ | --- | --- |
116
+ | Primary language | TypeScript (100% of `src/`) |
117
+ | Runtime | Node.js, `engines.node: ">=22.22.2"` (also supports `24.15.0`, `26.3.0` per CI matrix) |
118
+ | Package manager | pnpm `11.28.5`, pinned exactly via `packageManager` field and `corepack`; `pnpm-lock.yaml` present and respected everywhere (no lockfile changes made by this audit) |
119
+ | Build system | `tsc` (`tsconfig.json`), no bundler — ships plain compiled JS + `.d.ts` |
120
+ | Test framework | Vitest (unit/integration/e2e/perf projects), Stryker (mutation testing), `fast-check` (property-based testing) |
121
+ | Lint/format | ESLint (`eslint.config.mjs`), Prettier, `.editorconfig` |
122
+ | Docs generator | TypeDoc, deployed to GitHub Pages |
123
+ | CLI/API/SDK/MCP surface | MCP server (stdio + Streamable HTTP transports) with two published binaries (`ssh-mcp-pro`, `ssh-mcp-pro-agent`); no separate SDK package |
124
+ | Container | Docker, multi-stage, multi-arch (`linux/amd64`, `linux/arm64`), published to GHCR |
125
+ | Monorepo/multi-package | No — confirmed single package. `pnpm-workspace.yaml` declares `packages: [.]` only; its other fields (`engineStrict`, `strictDepBuilds`, `allowBuilds`, dependency `overrides`) are pnpm supply-chain hardening settings, not a multi-package structure |
126
+ | Other language ecosystems (Python, Go, Rust) | **Not applicable** — no `pyproject.toml`, `go.mod`, or `Cargo.toml` found in the repository |
127
+
128
+ ## Publishing and release inventory
129
+
130
+ | Platform | Present? | Package/artifact name | Version source | Publish trigger |
131
+ | --- | --- | --- | --- | --- |
132
+ | npm | Yes | `ssh-mcp-pro` | `package.json` `version`, synced via `scripts/sync-version.mjs` | `release.yml` → `release-assets` job, conditional on repo variable `AUTO_RELEASE_PUBLISH` |
133
+ | Docker / GHCR | Yes | `ghcr.io/oaslananka/ssh-mcp-pro` | Derived from the pushed release tag (`v*.*.*` or `ssh-mcp-pro-v*`) | `docker.yml` → `publish-ghcr`, on tag push / published release / manual dispatch |
134
+ | GitHub Releases | Configured, unused | — | release-please | 0 releases published to date |
135
+ | PyPI | **Not applicable** | — | — | No Python packaging files present |
136
+ | VS Code Marketplace | **Not applicable** | — | — | Not an editor extension |
137
+ | Homebrew | **Not applicable** (no formula found) | — | — | Could be added later as a distribution channel; not currently planned anywhere in the repo |
138
+ | MCP Registry | Yes | `io.github.oaslananka/ssh-mcp-pro` | `server.json`/`mcp.json`, validated by `mcp-registry.yml` | Manual/PR-validated, tracked in `REGISTRY_SUBMISSION.md` |
139
+ | Documentation site | Yes | TypeDoc → GitHub Pages | Generated from `src/` entry points | `docs.yml`, on push to `main` |
140
+
141
+ See "Package publishing maturity" below for the per-platform detail (metadata quality,
142
+ trusted publishing, checksums, provenance) this table only inventories.
143
+
144
+ ## GitHub Community Standards status
145
+
146
+ | Item | Status | Evidence |
147
+ | --- | --- | --- |
148
+ | README | Passed | [README.md](../README.md) — install, quickstart, config reference, security defaults |
149
+ | LICENSE | Passed | MIT, [LICENSE](../LICENSE) |
150
+ | CONTRIBUTING | Passed | [CONTRIBUTING.md](../CONTRIBUTING.md) — setup, quality gate, commits, release process |
151
+ | CODE_OF_CONDUCT | Passed (added by this PR) | [CODE_OF_CONDUCT.md](../CODE_OF_CONDUCT.md) |
152
+ | SECURITY | Passed | [SECURITY.md](../SECURITY.md) — private reporting, SLA, scope |
153
+ | SUPPORT | Passed | [SUPPORT.md](../SUPPORT.md) |
154
+ | Issue templates | Passed | `.github/ISSUE_TEMPLATE/{bug_report,feature_request,release_task,config}.yml` |
155
+ | Pull request template | Passed | [.github/PULL_REQUEST_TEMPLATE.md](../.github/PULL_REQUEST_TEMPLATE.md) |
156
+ | CODEOWNERS | Passed | [.github/CODEOWNERS](../.github/CODEOWNERS) |
157
+
158
+ GitHub's own "Community Standards" checklist will likely still show a gap for Code of
159
+ Conduct until this PR merges — GitHub caches the checklist and reads root-level
160
+ `CODE_OF_CONDUCT.md`, which is added here for the first time.
161
+
162
+ ## OpenSSF Best Practices status
163
+
164
+ Self-assessed against the [OpenSSF Best Practices Passing criteria](https://www.bestpractices.dev/en/criteria/0).
165
+ Full criterion-by-criterion notes are in [docs/openssf-evidence.md](openssf-evidence.md).
166
+ `.bestpractices.json` (added by this PR) is an **internal tracking file**, not an
167
+ official badge artifact — the OpenSSF badge is only issued via a self-certification
168
+ form at bestpractices.dev; this file exists to make filling that form out mechanical
169
+ and to keep the self-assessment under version control.
170
+
171
+ | Category | Status |
172
+ | --- | --- |
173
+ | Basics (repo, license, docs, changelog) | Passed |
174
+ | Change control (version control, unique versions, release notes) | Passed |
175
+ | Reporting (bug/vuln reporting process, response) | Passed |
176
+ | Quality (build, tests, coverage, warnings-as-errors, static analysis) | Passed |
177
+ | Security (secure delivery, vuln history, crypto, secrets handling) | Partial — see CodeQL alerts below |
178
+ | Analysis (static + dynamic analysis in CI) | Passed (CodeQL + ESLint + mutation testing) |
179
+
180
+ **Passing tier is realistically achievable.** The main blocker to actually submitting is
181
+ organizational, not technical: the badge form asks about code review process, and this
182
+ repo has never had a human-reviewed PR to point to (see Scorecard "Code-Review" below).
183
+
184
+ ## Scorecard readiness
185
+
186
+ `scorecard.yml` runs weekly and on push, but `publish_results: false` is set
187
+ deliberately (documented in-workflow: the OpenSSF Scorecard API rejects workflows using
188
+ global `env:`/`defaults:` blocks, several of which exist across the workflow set, and
189
+ fixing all of them was called out of scope by whoever set this comment). This means the
190
+ Scorecard badge in the README currently resolves to **no published score**, not a
191
+ passing one — `get_scorecard` against the public API returned no data. This is worth
192
+ flagging directly: **the badge is potentially misleading until either scores are
193
+ published or the badge is removed.**
194
+
195
+ | Check | Status | Evidence |
196
+ | --- | --- | --- |
197
+ | Branch-Protection | Missing | `GET .../branches/main/protection` → 404 "Branch not protected". A ruleset JSON exists at `.github/rulesets/main-protection.json` describing 10 required status checks + 1 approval + linear history, but it is **not applied** on GitHub — Needs human confirmation whether it was ever imported via Settings → Rules, or is a design document awaiting import |
198
+ | Code-Review | Missing **[updated]** | 1 collaborator. 6 PRs total as of this update: 2 Dependabot (merged), 1 release-please (open), and 3 authored and merged by the sole maintainer/owner (`author_association: OWNER`) via this audit's own branches — including this report itself. None were reviewed by an independent second party; self-merging your own PR is not what this check measures. Still `Missing` in substance, just with more data points |
199
+ | Maintained | Needs human confirmation | Active commit history over the observed window, but 5-day repo age is too short for Scorecard's 90-day activity window to mean anything |
200
+ | Security-Policy | Passed | `SECURITY.md` present, linked from `.github/ISSUE_TEMPLATE/config.yml` |
201
+ | License | Passed | MIT + REUSE-compliant |
202
+ | CI-Tests | Passed | `ci.yml` runs unit/integration/e2e/perf on every PR |
203
+ | Dependency-Update-Tool | Passed | `renovate.json` (npm ecosystem) + `dependabot.yml` (github-actions ecosystem) |
204
+ | Pinned-Dependencies | Passed | All third-party Actions pinned to commit SHA (verified in `release.yml`, `scorecard.yml`, `ci.yml`) |
205
+ | Token-Permissions | Passed | Every workflow sets `permissions:` at workflow level, narrowed further per job (e.g. `contents: write` only in `release`/`release-assets`) |
206
+ | Dangerous-Workflow | Passed (best-effort) | No `pull_request_target` usage found; no script-injection-shaped `run:` blocks observed in the reviewed workflows |
207
+ | SAST | Passed | CodeQL (`codeql.yml`), weekly + on PR |
208
+ | Fuzzing | Not applicable | No fuzz-testable binary parsing surface identified; property-based testing (`fast-check` dependency) is used instead, which is the more relevant technique for this codebase |
209
+
210
+ **Realistic near-term Scorecard improvement path:** enabling branch protection on `main`
211
+ (or importing the existing ruleset) and getting one human-reviewed PR would move two of
212
+ the lowest-scoring checks from `Missing` to `Passed`. Both require a maintainer/admin
213
+ action, not a code change — see "Next actions."
214
+
215
+ ## Documentation maturity (Diátaxis)
216
+
217
+ Before this PR, documentation was substantial but flat (12+ root/`docs/` files with no
218
+ explicit tutorial/how-to/reference/explanation separation). This PR adds the four
219
+ Diátaxis folders and organizes existing content into them via index pages, rather than
220
+ duplicating it:
221
+
222
+ | Diátaxis category | Status | Notes |
223
+ | --- | --- | --- |
224
+ | Tutorial | Passed (added) | `docs/tutorials/getting-started.md` — new, learning-oriented walkthrough |
225
+ | How-to guides | Passed (added) | `docs/how-to/README.md` indexes existing goal-oriented docs (`adding-a-device.md`, `docker.md`, `remote-mcp-hardening.md`) and adds one new guide |
226
+ | Reference | Passed (added) | `docs/reference/README.md` indexes the canonical env-var table in README, `mcp.json`/`server.json`, and adds a CLI reference |
227
+ | Explanation | Passed (added) | `docs/explanation/architecture.md` — new, links to `ARCHITECTURE.md` and `SECURITY_DECISIONS.md` rather than duplicating them |
228
+
229
+ ## Release maturity
230
+
231
+ | Item | Status | Evidence |
232
+ | --- | --- | --- |
233
+ | Semantic Versioning | Passed | `package.json` version `1.1.5`, `release-please-config.json` uses `node` release type |
234
+ | CHANGELOG.md, Keep a Changelog format | Passed | Header explicitly declares Keep a Changelog v1.1.0 + SemVer |
235
+ | GitHub Releases | Missing | `list_releases` → `[]`. No **GitHub Release** has ever been published — this is still accurate and unrelated to npm (see correction below) |
236
+ | Release notes | Needs human confirmation | release-please generates them on first GitHub Release; unverified via that mechanism specifically until one ships |
237
+ | Release workflow | Passed | `release.yml` — release-please PR flow, gated on `github.repository` |
238
+ | Checksums | Passed | `sha256sum` generated for the packed tarball and SBOM in `release-assets` job |
239
+ | Artifact provenance / attestation | Passed | `actions/attest-build-provenance` run twice (package + SBOM) |
240
+ | SBOM | Passed | `pnpm run sbom` (CycloneDX) generated and attested per release |
241
+ | npm publish | Passed **[corrected 2026-07-03]** | Originally marked "needs human confirmation" on the assumption nothing had been published. Verified directly against `registry.npmjs.org/ssh-mcp-pro`: versions `1.0.0`–`1.1.5` are live, with 613 npm downloads in the preceding month. The package **is** published — just not via the `release.yml` → `AUTO_RELEASE_PUBLISH` path this audit was watching, which means that specific automated path is still unexercised even though the package itself is real |
242
+
243
+ The release pipeline is genuinely strong. Correction: this report previously said "for
244
+ a project that hasn't shipped yet" — that referred to GitHub Releases specifically and
245
+ was worded ambiguously; the npm package itself has shipped multiple versions already.
246
+ The open PR #1 (`chore(main): release ssh-mcp-pro 1.2.0`) is still the first opportunity
247
+ to exercise the *automated* `release.yml` → npm-publish path end-to-end — merging it is
248
+ a repository decision, not something this audit takes on itself.
249
+
250
+ ## Package publishing maturity
251
+
252
+ ### npm
253
+
254
+ | Item | Status | Evidence |
255
+ | --- | --- | --- |
256
+ | Package metadata (`name`, `description`, `keywords`, `author`, `license`) | Passed | `package.json` |
257
+ | `repository`/`bugs`/`homepage` fields | Passed | All three present and point to the correct repo |
258
+ | `files` allowlist | Passed | Explicit allowlist (`dist`, `docs` minus `docs/api`, `examples`, key root docs) rather than publishing everything |
259
+ | `exports` / `main` / `types` / `bin` | Passed | `exports: "./dist/index.js"`, `types: "dist/index.d.ts"`, two `bin` entries (`ssh-mcp-pro`, `ssh-mcp-pro-agent`) |
260
+ | `engines` | Passed | Enforced (`engine-strict=true` per `.npmrc`/CONTRIBUTING) |
261
+ | Trusted publishing / OIDC (vs. long-lived token) | Passed | `release-assets` job has `id-token: write`; the publish step explicitly runs `unset NODE_AUTH_TOKEN NPM_CONFIG_USERCONFIG` before `npm publish --provenance` — this is deliberate use of npm's OIDC trusted-publisher flow, not a stored `NPM_TOKEN`. No npm token secret was found anywhere in the workflow set |
262
+ | Publish gating | Passed | Only runs when release-please reports `release_created == 'true'` **and** repo variable `AUTO_RELEASE_PUBLISH == 'true'` — a release PR merge does not by itself publish |
263
+ | Post-publish verification | Passed | Polls `pnpm view ssh-mcp-pro@<version>` up to 18 times with backoff to confirm the publish actually landed |
264
+ | `publint` / `arethetypeswrong` | Missing | Not run. Recommended, not applied — see "Safe refactor opportunities" |
265
+ | Package version | 1.1.5 (unpublished) | `package.json`; nothing published to the npm registry yet since no release has shipped — **Needs human confirmation** by checking `npm view ssh-mcp-pro` directly if this audit's snapshot is stale |
266
+
267
+ ### Docker / GHCR
268
+
269
+ | Item | Status | Evidence |
270
+ | --- | --- | --- |
271
+ | `Dockerfile` | Passed | Multi-stage build, non-root `USER node`, `HEALTHCHECK` defined |
272
+ | `.dockerignore` | Passed | Present |
273
+ | Base image pinning | Passed | `node:24-alpine` pinned by digest (`@sha256:...`), not just a tag |
274
+ | OCI labels | Passed | `title`, `description`, `url`, `documentation`, `source`, `revision`, `licenses`, `created` — all set and verified by a CI smoke-test step that asserts exact label values |
275
+ | Tag policy | Passed | Publishes both the bare version (`1.1.5`) and the release tag; no mutable `latest` tag is pushed |
276
+ | Multi-arch build | Passed | `linux/amd64,linux/arm64` via Buildx + QEMU, `--check` step validates cross-platform buildability even on PRs that don't publish |
277
+ | Provenance/SBOM on the image itself | Passed | `docker buildx build --provenance=true --sbom=true` on the published image (distinct from the npm-package SBOM) |
278
+ | Digest verification post-publish | Passed | Asserts the pushed manifest's digest matches expectations for both platforms, and cross-checks the GHCR package API for the expected tags before declaring success |
279
+ | Image vulnerability scanning (Trivy/Grype) | Partial **[updated]** | Trivy was added to `docker.yml` (advisory, `exit-code: "0"`) after this report was first written. It now runs and reports — and it found real findings: **50+ open CVE alerts** against the built image (`security/code-scanning`, tool `Trivy`), spanning Low through High severity. The large majority are in the Alpine base image's `libssl3`/`libcrypto3` packages and in `npm`'s/`corepack`'s own bundled dependencies (`undici`, `tar`, `sigstore`, `ip-address`, `brace-expansion`, and `pnpm` itself) — not in this project's application code or `dependencies`/`devDependencies`. These are pre-existing characteristics of the `node:24-alpine` base image and its bundled tooling that nobody had visibility into before Trivy was added; they are not a regression from anything in this audit. Status moves from `Missing` to `Partial`: the scanner now exists and is providing real signal, but the findings themselves are unaddressed. See the new recommended issue below |
280
+ | Hadolint (Dockerfile linting) | Missing | Not present. Recommended, low-risk to add |
281
+
282
+ ### PyPI, Go modules, Rust crates, VS Code Marketplace, Homebrew
283
+
284
+ **Not applicable.** No `pyproject.toml`, `go.mod`, `Cargo.toml`, VS Code extension manifest,
285
+ or Homebrew formula exists in this repository, and nothing in the docs suggests one is
286
+ planned. If that changes, re-run this section of the audit against the relevant
287
+ ecosystem's checklist.
288
+
289
+ ## Quality maturity
290
+
291
+ | Item | Status | Evidence |
292
+ | --- | --- | --- |
293
+ | CI workflow | Passed | `ci.yml`: dependency-review, REUSE lint, quality, unit ×3 Node versions, integration, integration-windows, e2e, perf, dependency-freshness, mutation, package, docker |
294
+ | Lint | Passed | ESLint (`eslint.config.mjs`), Prettier (`.prettierrc.json`) |
295
+ | Typecheck | Passed | `tsc --noEmit` via `check:quality` |
296
+ | Unit tests | Passed | Vitest, `test/unit/**` |
297
+ | Integration tests | Passed | `test/integration/**`, including a Windows-specific SSH integration project |
298
+ | Coverage threshold | Passed | `vitest.config.ts` enforces 85–90% branches/functions/lines/statements globally, with a scoped 75–85% threshold for `src/remote/**` |
299
+ | Mutation testing / quality gate | Passed | Stryker targets exact line ranges in `auth.ts`, `policy.ts`, `safety.ts`, `session.ts`, `http-security.ts`, `oauth.ts`, `remote/*` — the security-critical surfaces — with high/low thresholds 80/60 |
300
+ | Dependency review | Passed | `dependency-review-action`, `fail-on-severity: moderate`, runs on every PR (this was already implemented as a `ci.yml` job — no standalone `dependency-review.yml` needed) |
301
+ | Coding standards doc | Passed (added) | `docs/development/coding-standards.md` |
302
+ | Test policy doc | Passed (added) | `docs/development/testing-policy.md` |
303
+ | Dependency management policy doc | Passed (added) | `docs/development/dependency-management.md` |
304
+
305
+ ## Governance maturity
306
+
307
+ | Item | Status | Evidence |
308
+ | --- | --- | --- |
309
+ | GOVERNANCE.md | Passed (added) | Describes the current solo-maintainer model honestly; does not claim a governance board that doesn't exist |
310
+ | MAINTAINERS.md | Passed (added) | Lists `@oaslananka`, cross-references `CODEOWNERS` |
311
+ | ROADMAP.md | Passed (added) | Grounded in real open items (branch protection, first release, CodeQL alerts, contributor growth) rather than invented features |
312
+ | CODEOWNERS | Passed (pre-existing) | `.github/CODEOWNERS` |
313
+ | Support policy | Passed (pre-existing) | `SUPPORT.md` |
314
+ | Deprecation policy | Passed (added) | `docs/development/deprecation-policy.md` — states intent honestly as forward-looking, since no deprecation has ever happened yet |
315
+ | Security Insights (OpenSSF spec) | Passed (added) | `security-insights.yml`, schema-version 2.2.0, verified against the upstream `ossf/security-insights-spec` examples before writing; fields without verifiable evidence (bug bounty, third-party assessment, attestation predicate URIs) are omitted rather than filled with placeholders |
316
+ | Backward compatibility policy | Partial | Implied by `engines` in `package.json` and `MIGRATION.md`, not formalized as policy |
317
+
318
+ ## Community maturity (CHAOSS-style)
319
+
320
+ | Metric | Status | Notes |
321
+ | --- | --- | --- |
322
+ | Bus factor | 1 | Single collaborator with admin rights; this is the single most important maturity constraint in this report |
323
+ | Time to first response | Not applicable (insufficient history) | `open_issues_count: 3` reflects 3 open pull requests (all bot-authored); `list_issues` confirms **0 actual open issues** — no human-filed issue has ever received (or needed) a first response |
324
+ | PR review process | Partial | A ruleset *describing* 1 required approval exists but is unapplied; zero PRs have ever received a human review |
325
+ | Contributor activity | Not applicable (insufficient history) | 0 non-maintainer, non-bot contributors to date |
326
+ | Release frequency | Not applicable (insufficient history) | 0 releases published |
327
+ | `good first issue` / `help wanted` label usage | Not applicable (insufficient history) | Both labels exist in the repo's default label set, but there are 0 open issues to apply them to — usage can't be assessed until issues exist |
328
+ | Documentation discoverability | Passed | README links out to all major docs; this PR adds Diátaxis structure |
329
+ | Change request acceptance process | Passed | `CONTRIBUTING.md` + PR template + required `pnpm run check` |
330
+
331
+ ## License/legal maturity
332
+
333
+ | Item | Status | Evidence |
334
+ | --- | --- | --- |
335
+ | LICENSE | Passed | MIT, correct copyright holder |
336
+ | SPDX identifiers | Passed | `REUSE.toml` aggregate annotation (`SPDX-License-Identifier = "MIT"` for `**`), valid under the REUSE spec's aggregate-precedence mechanism; verified continuously by the `reuse lint` CI step |
337
+ | REUSE readiness | Passed | `REUSE.toml` + `LICENSES/MIT.txt` + CI enforcement |
338
+ | License location | Passed | Root `LICENSE`, `LICENSES/MIT.txt` |
339
+ | Third-party dependency license awareness | Passed | `scripts/check-licenses.mjs` enforces an allowlist (MIT, Apache-2.0, BSD variants, BlueOak-1.0.0, CC-\*, ISC, Python-2.0, Unlicense) as part of `check:quality` |
340
+ | NOTICE file | Not applicable | MIT does not require a NOTICE file; no bundled dependency was identified that imposes one |
341
+
342
+ ## Security/supply-chain maturity
343
+
344
+ | Item | Status | Evidence |
345
+ | --- | --- | --- |
346
+ | SECURITY.md | Passed | Private reporting, 7-day SLA, explicit scope |
347
+ | Private vulnerability reporting | Passed **[updated]** | Confirmed via `GET /repos/oaslananka/ssh-mcp-pro/private-vulnerability-reporting` → `{"enabled": true}`. No longer needs human confirmation |
348
+ | CodeQL | Passed **[updated]** | Workflow present and running weekly + on PR. All 8 originally-open alerts are now closed: #1 (High, `js/clear-text-logging` in `scripts/start-chatgpt-http.mjs`) was fixed in code (the log line no longer interpolates environment-sourced strings directly); #2–#8 were dismissed as false positives via the code-scanning API, each with a rationale re-verified against the current code (not just re-asserting stale documentation) — see `SECURITY_DECISIONS.md` for the write-up. 0 open CodeQL alerts as of this update |
349
+ | Gitleaks / secret scanning | Passed | GitHub native secret scanning **and** push protection are both enabled at the repo level (confirmed via API); this PR adds `gitleaks.yml` as a defense-in-depth layer for full-history scanning, since native secret scanning primarily covers newly pushed content |
350
+ | Dependency review | Passed | `ci.yml` job, `fail-on-severity: moderate` |
351
+ | Trivy (container image scanning) | Partial **[new, added after initial audit]** | Added to `docker.yml`, advisory-only. Surfaced 50+ open alerts against the built image, almost entirely in the Alpine base image and npm/corepack's own bundled dependencies rather than this project's code — see "Package publishing maturity" above. New visibility, not yet acted on |
352
+ | Dependabot | Passed **[updated]** | `dependabot.yml` covers `github-actions` only; npm dependencies are intentionally left to Renovate (`renovate.json`) to avoid two bots opening competing PRs for the same ecosystem — a deliberate design choice, not a gap. **Dependabot vulnerability alerts and automated security fixes are now both enabled** (were disabled at initial audit time) |
353
+ | OSV Scanner | Missing | Not present; `pnpm audit --audit-level moderate` (the `audit` script) covers similar ground for npm advisories |
354
+ | SBOM | Passed | Generated + attested per release |
355
+ | SLSA / provenance | Passed | `actions/attest-build-provenance` (build provenance, not full SLSA level claim) |
356
+ | Minimal Actions permissions | Passed | Verified directly in `release.yml`, `scorecard.yml`, `ci.yml` |
357
+
358
+ ## Developer experience maturity
359
+
360
+ | Item | Status | Evidence |
361
+ | --- | --- | --- |
362
+ | One-command setup | Passed | `Taskfile.yml` (`task install`) + documented `pnpm install --frozen-lockfile` in `CONTRIBUTING.md` |
363
+ | One-command verify | Passed | `task verify` chains lint/format/typecheck/test/build; `pnpm run check` is the fuller CI-equivalent gate |
364
+ | Git hooks | Passed | `pnpm run prepare` wires `.githooks` automatically on install |
365
+ | `.env.example` | Passed (pre-existing) | Present at repo root, documents `SSH_MCP_*` variables |
366
+ | `.editorconfig` | Passed (pre-existing) | Present |
367
+ | Troubleshooting docs | Passed (added) | `docs/troubleshooting.md` |
368
+ | Local setup doc | Passed (added) | `docs/development/local-setup.md` (previously only in `CONTRIBUTING.md`; this cross-links rather than forks it) |
369
+ | `.devcontainer` | Missing | Not present — **Optional**, not required for a Node/pnpm project with a straightforward toolchain |
370
+ | Debug/logging documentation | Partial | `SSH_MCP_DEBUG` is documented in the README config table; no dedicated "how to debug a failing session" guide existed before this PR — added as part of `docs/troubleshooting.md` |
371
+
372
+ ## API/CLI stability
373
+
374
+ | Item | Status | Evidence |
375
+ | --- | --- | --- |
376
+ | Public API surface definition | Partial | The MCP tool/resource/prompt registry is the primary "public API"; it's implicitly defined by `src/tools/*` and exposed via `mcp-contract` tests, but there's no single stability statement (e.g. "tool schemas are covered by SemVer, additions are minor, removals are major") |
377
+ | CLI flag stability | Partial | Flags are enumerated in `src/cli.ts` (now documented in `docs/reference/cli.md`, added by this PR); unknown flags are silently ignored by design, which is forgiving but also means a typo'd flag never errors — worth being aware of, not necessarily a defect |
378
+ | Breaking change policy | Partial | Conventional Commits `!`/`BREAKING CHANGE:` conventions exist and drive release-please's major-version bump, but there's no prose policy stating what counts as a breaking change for *this* project's specific surfaces (tool schema, CLI flags, env vars, HTTP endpoints) |
379
+ | Deprecation policy | Missing (added by this PR) | `docs/development/deprecation-policy.md` — states the current honest position (no formal deprecation window has ever been exercised yet, project is pre-1.x-maturity despite `1.x` versioning) rather than inventing a policy with no track record |
380
+ | Migration guide | Passed (pre-existing) | `MIGRATION.md` |
381
+ | MCP tool schema stability | Needs human confirmation | `test/unit/mcp-contract.test.ts` validates schema shape continuously, which is good signal, but whether schema changes are treated as SemVer-significant is a maintainer policy question, not something inferable from tests alone |
382
+ | Config schema versioning | Missing | Environment variables and the optional `SSH_MCP_POLICY_FILE` JSON have no explicit schema version field; a policy file written for one version isn't guaranteed forward-compatible by anything other than convention |
383
+
384
+ ## README and badge review
385
+
386
+ > **Correction [updated 2026-07-03, later same day]:** the original audit assumed
387
+ > `package.json`'s `1.1.5` was unpublished, based on `list_releases` returning `[]`
388
+ > (zero GitHub Releases). That was true for GitHub Releases but wrong about npm: a
389
+ > direct query against `registry.npmjs.org/ssh-mcp-pro` shows versions `1.0.0` through
390
+ > `1.1.5` actually published, with 613 downloads in the preceding month. **The npm
391
+ > package has been live for some time, published outside the GitHub Release /
392
+ > release-please flow this audit was tracking.** This is a correction to "Release
393
+ > maturity" and "Package publishing maturity" above, not a new event — flagging it here
394
+ > because it directly affects badge accuracy, and because every other statement in this
395
+ > report about "0 releases" refers specifically to GitHub Releases, which is still
396
+ > accurate; npm publishing is a separate, already-exercised channel.
397
+
398
+ Badges were also physically reworked (centered, `<h1 align="center">` title) at the
399
+ maintainer's request, and two badge changes were made based on live verification, not
400
+ guesswork:
401
+
402
+ | Badge | Status | Note |
403
+ | --- | --- | --- |
404
+ | npm version | Passed | Accurate — confirmed published |
405
+ | npm downloads (total) | Passed (added) | New: `img.shields.io/npm/dt/ssh-mcp-pro.svg`. Legitimate now that the package is confirmed live |
406
+ | License | Passed | Accurate |
407
+ | CI | Passed | Accurate, links to `ci.yml` |
408
+ | API Docs | Passed | Accurate, links to the deployed TypeDoc site |
409
+ | OpenSSF Scorecard | Removed | **Verified broken, not just "no data":** `GET api.securityscorecards.dev/projects/github.com/oaslananka/ssh-mcp-pro` returns HTTP 404, and the badge itself rendered as `invalid repo path` on GitHub — a visibly broken red badge, worse than "no score yet." Removed rather than left displaying an error. Re-add once `publish_results` is fixed (see "Scorecard readiness") and a real score exists |
410
+ | OpenSSF Best Practices | Missing | No badge yet — correctly absent, since no badge has been issued (see `docs/openssf-evidence.md`); adding one now would be a false claim |
411
+ | Docker pulls / GitHub release downloads | Not applicable | No GHCR image or GitHub Release has shipped yet. npm downloads (above) is the one adoption signal that's actually real right now |
412
+ | Donation button (Buy Me a Coffee) | Passed | Present, kept below the title/badges rather than above the fold — matches the guidance of using it sparingly |
413
+
414
+ ## Safe refactor opportunities
415
+
416
+ Legend: prioritization (`Required now` / `Recommended` / `Optional` / `Future` / `Not
417
+ applicable` / `Needs human confirmation`). "Safe" here means: no behavior change, no
418
+ public API impact, no build/publish system impact — confirmed by classification, not
419
+ just intent.
420
+
421
+ | Opportunity | Classification | Type | Notes |
422
+ | --- | --- | --- | --- |
423
+ | Community/governance docs (this PR) | Done | Documentation-only | Already applied |
424
+ | Diátaxis doc structure (this PR) | Done | Documentation-only | Already applied |
425
+ | `gitleaks.yml` (this PR) | Done | CI/workflow-only | Already applied, one bug fixed post-merge-attempt (missing `GITHUB_TOKEN`) |
426
+ | Add `publint` as an advisory (non-blocking) npm packaging check | Recommended | CI/workflow-only + new devDependency | Not applied by this audit — adding a devDependency and a new script is a build-tooling change, and the instructions for this audit call for leaving those as recommendations rather than auto-applying them |
427
+ | Add `arethetypeswrong` check | Optional | CI/workflow-only + new devDependency | Same reasoning as `publint`; lower priority since the package ships a single ESM entry point, not a dual CJS/ESM surface where ATW findings are most valuable |
428
+ | Add Trivy or Grype image scanning to `docker.yml` | Recommended | CI/workflow-only | No behavior change to the shipped image; only adds a scan step. Not applied here to keep this PR's diff limited to what was explicitly asked for; filed as a recommended issue |
429
+ | Add Hadolint for Dockerfile linting | Optional | CI/workflow-only | Same reasoning |
430
+ | Formalize a breaking-change/API-stability statement | Recommended | Documentation-only | Partially done via `docs/development/api-stability.md` (added by this PR); the remaining piece is a maintainer decision about what counts as "the public API" for versioning purposes |
431
+ | `docs/development/deprecation-policy.md` | Done | Documentation-only | Already applied |
432
+
433
+ ## High-risk refactor opportunities
434
+
435
+ These are **not applied** by this audit under any circumstance — they require either a
436
+ maintainer decision, affect a live build/publish/security surface, or both:
437
+
438
+ | Opportunity | Classification | Type | Why it's high-risk |
439
+ | --- | --- | --- | --- |
440
+ | Fix the 7 open CodeQL alerts (1 High) | Required now (but not by this audit) | Public-API-adjacent / behavioral | Touches scripts that handle credentials and network I/O (`scripts/start-chatgpt-http.mjs`, `src/remote/agent-cli.ts`, `scripts/lib/command.mjs`); a careless fix could change error handling behavior. Needs a maintainer or a dedicated security-focused PR, not a drive-by doc-audit fix |
441
+ | Apply branch protection to `main` | Required now | GitHub Settings / admin action | Immediately changes how the sole maintainer can push; must be a deliberate choice, not an automated one |
442
+ | Re-enable Scorecard `publish_results` | Recommended | CI/workflow, cross-cutting | Requires removing global `env:`/`defaults:` blocks from multiple workflows simultaneously — a coordinated change with real potential for behavioral drift across CI, release, and Docker workflows |
443
+ | Migrate Conventional Commit tooling to strict enforcement (reject non-conforming commits at push time, not just lint) | Optional | CI/workflow-only, but changes contributor experience | Currently advisory-shaped (`lint:commits`); making it a hard gate is a policy call affecting all future contributors |
444
+ | Add a config-schema version field to `SSH_MCP_POLICY_FILE` | Future | Public-API-impacting | Would require a code change to `src/config.ts`'s policy loader and a decision about backward compatibility for existing deployments' policy files |
445
+ | Add npm ecosystem to `dependabot.yml` | Not applicable (intentional) | Dependency-management | Already assessed and rejected — see "Dependabot" row in Security/supply-chain maturity |
446
+
447
+ ## Missing files (before this PR)
448
+
449
+ - `CODE_OF_CONDUCT.md`
450
+ - `GOVERNANCE.md`
451
+ - `MAINTAINERS.md`
452
+ - `ROADMAP.md`
453
+ - `.bestpractices.json`
454
+ - `docs/openssf-evidence.md`, `docs/openssf-gap-analysis.md`, `docs/openssf-proposal-links.md`
455
+ - Diátaxis folders: `docs/tutorials/`, `docs/how-to/`, `docs/reference/`, `docs/explanation/`
456
+ - `docs/development/*` (coding standards, testing policy, release process, dependency
457
+ management, commit conventions, local setup, API stability, deprecation policy)
458
+ - `docs/security/*` (threat model, release integrity, input validation, assurance case,
459
+ secrets management)
460
+ - `docs/professionalization-plan.md`, `docs/troubleshooting.md`,
461
+ `docs/reference/configuration.md`, `docs/reference/compatibility.md`
462
+ - `security-insights.yml` (OpenSSF Security Insights spec, schema-version 2.2.0 —
463
+ verified against `ossf/security-insights-spec`'s own examples before writing)
464
+
465
+ All of the above are added by this PR. `NOTICE`, `THIRD_PARTY_NOTICES.md`, and
466
+ `CITATION.cff` were considered and intentionally **not** added — see "Not applied
467
+ intentionally" in the PR description.
468
+
469
+ ## Missing workflows (before this PR)
470
+
471
+ - `gitleaks.yml` — added by this PR (low risk: read-only permissions, no secrets required for a public repo, additive to existing native secret scanning)
472
+ - `dependency-review.yml` — **not added**; functionally already covered by the `dependency-review` job inside `ci.yml`. Adding a duplicate standalone workflow would be redundant, not additive
473
+
474
+ ## Risky changes not applied
475
+
476
+ These require a maintainer decision, a GitHub Settings change, or a code behavior
477
+ change, and are intentionally left as recommendations/issues rather than direct edits:
478
+
479
+ 1. **Enabling branch protection / importing the existing ruleset on `main`.** This is a
480
+ repository Settings action with real consequences (it would immediately start
481
+ blocking direct pushes, including future maintenance from the sole maintainer,
482
+ unless bypass is configured). Recommendation, not applied.
483
+ 2. **Fixing the 7 open CodeQL alerts**, especially the High-severity clear-text logging
484
+ finding in `scripts/start-chatgpt-http.mjs`. This is a code change to a script that
485
+ handles credentials/tokens — exactly the kind of change this audit is scoped to flag,
486
+ not silently fix. Filed as a recommended issue below.
487
+ 3. **Re-enabling Scorecard `publish_results`.** The in-workflow comment says this needs
488
+ every workflow's global `env:`/`defaults:` blocks removed first — a cross-cutting
489
+ workflow refactor with a real chance of behavioral side effects. Flagged, not
490
+ attempted.
491
+ 4. **Adding npm ecosystem to `dependabot.yml`.** Would likely duplicate Renovate's
492
+ existing job. Documented as an intentional non-change with rationale instead.
493
+ 5. **Toggling "Dependabot security updates" in repo Settings.** A GitHub Settings
494
+ change with a real effect (auto-opens PRs for vulnerable deps); listed as a manual
495
+ action, not applied via API.
496
+ 6. **Modifying the OpenSSF Scorecard badge or removing it** pending the
497
+ `publish_results` decision above — left as-is, flagged for maintainer awareness.
498
+
499
+ ## Manual GitHub settings required
500
+
501
+ None of these can be done via a file change; they need repository Settings access:
502
+
503
+ | Setting | Current state | Needed action |
504
+ | --- | --- | --- |
505
+ | Branch protection / ruleset on `main` | Not applied (404 from the protection API) | Import `.github/rulesets/main-protection.json` under Settings → Rules, or configure classic branch protection to match it |
506
+ | Required status checks | Described in the unapplied ruleset only | Apply alongside branch protection |
507
+ | Required PR review count | Described in the unapplied ruleset only (1 approval) | Apply alongside branch protection |
508
+ | Private vulnerability reporting | **Done [updated]** — confirmed `enabled: true` via API | No action needed |
509
+ | Dependabot alerts / dependency graph | **Done [updated]** — vulnerability alerts and automated security fixes both enabled via API | No action needed |
510
+ | npm trusted publishing registration | Workflow-side OIDC publish flow is implemented (no stored token, `--provenance`) | On npmjs.com, register this repo/workflow as a trusted publisher for the `ssh-mcp-pro` package (a one-time npmjs.com-side setup outside this repo) — **Needs human confirmation** whether this registration already exists, since it can't be checked from the repo |
511
+ | PyPI trusted publishing | Not applicable | No PyPI package exists |
512
+ | Docker registry (GHCR) settings | Package visibility/linkage not inspected by this audit | Confirm the GHCR package is linked to this repo and set to public visibility once the first image is published |
513
+ | OpenSSF BadgeApp final approval | Not submitted | Submit via bestpractices.dev once the two blockers in `docs/openssf-evidence.md` close |
514
+ | CODEOWNERS enforcement | File exists but isn't enforced without branch protection | Enable "Require review from Code Owners" alongside branch protection |
515
+
516
+ ## Recommended issues
517
+
518
+ The following are proposed as GitHub issues (not opened automatically by this audit,
519
+ per scope — the PR description lists them so the maintainer can file them, or ask for
520
+ them to be filed as a follow-up):
521
+
522
+ 1. ~~**[security] Fix CodeQL alert #1 (High): clear-text logging of sensitive
523
+ environment data in `scripts/start-chatgpt-http.mjs`.**~~ **Done** — fixed in code.
524
+ 2. ~~**[security] Review CodeQL alerts #2–#8 (Medium) and record disposition.**~~
525
+ **Done** — all dismissed with rationale re-verified against current code;
526
+ documented in `SECURITY_DECISIONS.md`.
527
+ 3. **[governance] Decide and apply branch protection for `main`**, either by importing
528
+ `.github/rulesets/main-protection.json` as a GitHub Ruleset or configuring classic
529
+ branch protection to match it. **Still open** — deliberately left for the maintainer.
530
+ 4. **[release] Ship the first release** (merge or re-trigger release-please PR #1) to
531
+ exercise the release pipeline (SBOM, checksums, attestation, optional npm publish)
532
+ end-to-end for the first time. **Still open** — deliberately left for the maintainer;
533
+ triggers a real, hard-to-reverse publish action.
534
+ 5. ~~**[security] Confirm "Private vulnerability reporting" is enabled.**~~ **Done** —
535
+ confirmed already enabled via API.
536
+ 6. **[ci] Re-evaluate OpenSSF Scorecard `publish_results: false`** — either fix the
537
+ blocking `env:`/`defaults:` patterns across workflows, or replace the Scorecard badge
538
+ with language clarifying no public score is currently published. **Still open** —
539
+ investigated, deliberately not applied (see "High-risk refactor opportunities"); a
540
+ relocation across all 7 workflows risks breaking the Windows Integration job's shell
541
+ default in a way that can't be verified without a live Windows Actions run.
542
+ 7. **[governance] Decide on a second maintainer / reviewer** before targeting OpenSSF
543
+ Silver or a "mature OSS" claim that depends on independent code review. **Still
544
+ open** — not something this audit can create by itself.
545
+ 8. ~~**[supply-chain] Add container image vulnerability scanning** (Trivy or Grype).~~
546
+ **Done** — added to `docker.yml`. **New follow-up issue this created:** 50+ open
547
+ Trivy alerts now exist against the built image (Alpine base-image OpenSSL packages,
548
+ npm/corepack's own bundled dependencies). **[supply-chain] Triage the Trivy backlog** —
549
+ decide whether to rebuild on a newer/slimmer base image, prune corepack's cache from
550
+ the final image layer, or accept the current baseline and document why.
551
+ 9. ~~**[packaging] Add `publint`** as an advisory npm packaging check.~~ **Done** —
552
+ added, passes cleanly against the built package.
553
+ 10. **[registry] Confirm npm trusted-publisher registration exists on npmjs.com** for
554
+ this repository/workflow. **Still open** — this is an npmjs.com-side action outside
555
+ the repo; can't be verified or performed via the GitHub API.
556
+
557
+ ## Next actions
558
+
559
+ 1. Merge this PR.
560
+ 2. Work through "Manual GitHub settings" (below / in the PR description) — these cannot
561
+ be done by this automation.
562
+ 3. File the "Recommended issues" above (or approve filing them as a follow-up).
563
+ 4. Ship a first release to validate the release pipeline end-to-end.
564
+ 5. Revisit this report after the first release and after branch protection is applied —
565
+ several `Missing`/`Partial` rows above will likely flip to `Passed` at that point.
@@ -0,0 +1,58 @@
1
+ # Assurance Case
2
+
3
+ An assurance case is an argument, backed by evidence, for why a project should be
4
+ trusted at its current maturity level — not a claim that it's beyond scrutiny. This one
5
+ covers ssh-mcp-pro as of 2026-07-03.
6
+
7
+ ## Claim
8
+
9
+ ssh-mcp-pro's automated engineering controls are strong enough to justify normal OSS
10
+ trust (install it, review its defaults, use its documented policy controls) for a
11
+ project at "pre-first-release, single-maintainer" maturity — but not yet strong enough
12
+ to justify Silver/Gold-tier OpenSSF trust, because several controls that require human
13
+ process (independent code review, sustained release history, resolved security
14
+ findings) have not yet been exercised.
15
+
16
+ ## Supporting evidence
17
+
18
+ 1. **Static analysis runs continuously and is visible.** CodeQL (weekly + per-PR),
19
+ ESLint, and TypeScript strict mode all gate CI. → [ci.yml](../../.github/workflows/ci.yml),
20
+ [codeql.yml](../../.github/workflows/codeql.yml).
21
+ 2. **Security-critical code has targeted, not just incidental, test coverage.**
22
+ Mutation testing specifically targets `auth.ts`, `policy.ts`, `safety.ts`,
23
+ `session.ts`, `http-security.ts`, `oauth.ts`, and the remote control plane. →
24
+ [docs/development/testing-policy.md](../development/testing-policy.md).
25
+ 3. **Defaults are deny-by-default and documented with rationale, not just asserted.** →
26
+ [SECURITY_DECISIONS.md](../../SECURITY_DECISIONS.md),
27
+ [docs/security/threat-model.md](threat-model.md).
28
+ 4. **Supply-chain integrity mechanisms exist for releases** (checksums, SBOM, build
29
+ provenance attestation) → [docs/security/release-integrity.md](release-integrity.md).
30
+ 5. **License and dependency provenance are enforced, not just documented** (REUSE lint
31
+ in CI, license allowlist script, dependency-review-action on every PR).
32
+
33
+ ## Counter-evidence / limits on the claim
34
+
35
+ 1. **No release has ever shipped.** Every mechanism in (4) is implemented but
36
+ unexercised end-to-end.
37
+ 2. **No pull request has ever received independent human review.** PRs merged to date
38
+ are either bot-authored (Dependabot) or authored and self-merged by the sole
39
+ maintainer. The claim above does not extend to "this project has been independently
40
+ verified by a second person" — it hasn't.
41
+ 3. **Branch protection is not actually applied on GitHub**, despite a ruleset
42
+ definition existing in-repo. Anyone with push access (currently just the sole
43
+ maintainer) can push directly to `main` today.
44
+ 4. ~~Seven CodeQL alerts are open, including one High severity.~~ **Resolved
45
+ 2026-07-03** — 1 fixed in code, 7 dismissed with rationale re-verified against
46
+ current code (see `SECURITY_DECISIONS.md`). Separately, Trivy container-image
47
+ scanning (added the same day) found 50+ new open findings against the built Docker
48
+ image — mostly the Alpine base image's bundled tooling, not this project's own code
49
+ — still untriaged as of this update. Static analysis/scanning running is evidence of
50
+ *detection* capability, not evidence that everything it detects has been resolved;
51
+ the Trivy backlog is the current live example of that gap.
52
+
53
+ ## Bottom line
54
+
55
+ Trust the automated controls; don't yet extend that trust to "this has been reviewed by
56
+ more than one person" or "this has shipped and been used in the wild," because neither
57
+ is true yet. Re-assess this case after the items in
58
+ [ROADMAP.md](../../ROADMAP.md#near-term) close.