Repository navigation
Arm the cross-repo plugin gate with a read-only deploy key - #1067
Merged
Merged
Conversation
The gate has existed since 98dd7d4 but PLUGINS_REPO_TOKEN was never provisioned, so every PR took the warn-only branch: "gate NOT armed … this PR is UNVERIFIED against the plugins". Green, and testing nothing. That cost a production outage. #683 deleted AiSettingsNodeType.AddSkillSource; the plugins repo's Store/Plugin/Source/Localizer.cs still called it, and no build in this repo can see that file. It merged unverified and surfaced only when the mesh recompiled Store/Plugin in production — CS0117, and since every plugin declares `requires: Store`, every nodeType:Store/Plugin partition on memex.systemorph.com (Edu, Agent, Skill, Essentials, Collaboration, DataModelling, Feedback, Instances, Publish) was left on the compilation-error overlay. Store is in VITAL_MODULES precisely so this job would have failed that PR. The plugins repo fixed its side in b10c981, two days later. The credential is a READ-ONLY DEPLOY KEY on Systemorph/MeshWeaver.Plugins, stored here as PLUGINS_REPO_SSH_KEY. The gate needs exactly one capability — read that repo — and a deploy key grants that and nothing else, so a secret every workflow run can read cannot be widened into anything else. Rejected: - a fine-grained PAT is minimal too, but it is minted against a PERSON and EXPIRES; a gate that silently disarms when a token lapses is the exact failure shape this job exists to prevent; - the org's `meshweaver-cloud` App is installed on ALL repos with write scopes (self-update, issues/PR/webhook integrations), so its private key here would let any workflow — including one added by a PR — mint org-wide write tokens. Revoke by deleting the deploy key on the plugins repo and replacing the secret; nothing else depends on it. The warn/fail branches keep their asymmetry: absent credential warns, present-but-unusable fails loudly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Arms the existing cross-repo plugin compatibility gate by switching from a non-existent/unused token secret to a read-only SSH deploy key, so PRs are actually verified against Systemorph/MeshWeaver.Plugins when credentials are available.
Changes:
- Replace
PLUGINS_REPO_TOKENwithPLUGINS_REPO_SSH_KEYand useactions/checkoutviassh-keyto read the plugins repo. - Update gate messaging/comments to reflect deploy-key lifecycle (missing vs revoked/replaced) and the intended security rationale.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+739
to
740
| ssh-key: ${{ secrets.PLUGINS_REPO_SSH_KEY }} | ||
| fetch-depth: 1 |
Contributor
Contributor
Arming the gate exposed a second defect in it: the job locates the tester with find . -path '*/MeshWeaver.PluginTester/bin/*/mw-plugin-test.dll' inside shard 0's build artifact, but that artifact packs "only the TEST projects' bin trees" (its own comment). tools/MeshWeaver.PluginTester is a TOOL, so the sweep never picked it up and the dll was never in the tarball. The very first armed run therefore died on ::error::mw-plugin-test.dll not in the build output — the gate cannot run. having tested nothing — the exact shape this job exists to prevent. It was invisible for as long as the gate was unarmed, because the step never ran. Pack the tester's bin into shard 0 (the artifact the gate already downloads) rather than rebuilding it in the gate: reusing that artifact is what keeps the job inside its 12-minute budget, and a restore+build of the framework graph would blow it. It is the one non-test bin that travels, so the reason is spelled out where the packing happens. Inert for the tests: the run loop iterates shard-assign.sh's project list, not the extracted tree, so an extra bin tree is never mistaken for a test project. Missing bin is a BUILD fault, not a tolerable absence — the solution builds this project (MeshWeaver.slnx), so the pack step fails loudly instead of shipping an artifact the gate cannot use. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
Contributor
Contributor
Contributor
Contributor
rbuergi
added a commit
that referenced
this pull request
Aug 10, 2026
…its trapdoor #1067 landed on the same block. Resolved in its favour on everything that is load-bearing, and against it on the one thing this branch exists to remove: KEPT from #1067 * `ssh-key: ${{ secrets.PLUGINS_REPO_SSH_KEY }}` and the full rationale for a read-only deploy key over a PAT or the org App (including how to rotate it). * Packing tools/MeshWeaver.PluginTester into shard 0's artifact. Arming the gate revealed it could never have run: the artifact packs TEST project bins and the tester is a TOOL, so its binary never travelled. Load-bearing. * Its fuller account of the #683 outage — moved into the preflight comment so the history survives where the rule is now stated. DROPPED from #1067 * `continue-on-error` on the checkout, the "is the credential configured?" probe, and the three outcome-gated branches. Those are the trapdoor. The job already excludes forks at job level, so they only covered the case that must be loud, and `preflight` now proves the credential before the job starts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The gate added in 98dd7d4 was never armed —
PLUGINS_REPO_TOKENwas not among the repo'ssecrets, so every PR took the warn-only branch:
Green, and testing nothing — the exact failure shape the job's own comments say it exists to end.
It has now cost a production outage. #683 deleted
AiSettingsNodeType.AddSkillSource. Theplugins repo's
Store/Plugin/Source/Localizer.csstill called it, and no build in this repo cansee that file — plugin node source is Roslyn-compiled against this assembly from a different repo.
It merged unverified and surfaced only when the mesh recompiled
Store/Pluginin production:CS0117. Since every plugin declaresrequires: Store, that left everynodeType:Store/Pluginpartition on memex.systemorph.com — Edu, Agent, Skill, Essentials, Collaboration, DataModelling,
Feedback, Instances, Publish — on the compilation-error overlay. The plugins repo fixed its side in
b10c981, two days later.Storeis inVITAL_MODULESfor exactly this reason, so an armed gate would have failed #683.The credential
A read-only deploy key on
Systemorph/MeshWeaver.Plugins(created, id159776507), stored hereas
PLUGINS_REPO_SSH_KEY(set). The gate needs exactly one capability — read that one repo — and adeploy key grants that and nothing else, so a secret every workflow run can read cannot be widened.
Two alternatives were considered and rejected, and the reasoning is in the workflow comment so it
survives:
meshweaver-cloudGitHub AppRevoke by deleting the deploy key on the plugins repo and replacing the secret; nothing else depends
on it.
Behaviour unchanged otherwise
The three-way asymmetry is preserved exactly: fork PR → skip with a warning; credential absent →
warn, do not block; credential present but checkout fails → fail loudly (now worded for a
revoked/replaced deploy key rather than an expired token).
main-cd.ymlneeds no change — it builds themw-plugin-testimage but never checks out theplugins repo.
Self-verifying
This PR is the first run with the secret present, so its own
plugin-gatejob is the test: it willcheck out the plugins repo and compile + run
StoreandEssentialsagainst this PR's buildoutput. A green gate here means it is genuinely armed; a red one is the gate doing its job.
🤖 Generated with Claude Code