Repository navigation
feat(control-lane): the deployment generates its announcement key; the control instance registers it - #5822
Conversation
…e control instance registers it - /Admin/Settings/ControlLane gains Generate key / Generate new key (server-side, shown once with its fingerprint, via the platform's write-only secret section). - Control-side texts become 'register the key the deployment generated' plus a fingerprint compare; the issue-side send hint is gone. - Docs: SelfUpdateAnnouncementKey (reversed flow, client steps, /Admin/Settings/ControlLane and /Hosting/Integrations/Deployment/<id>), InstanceSecrets, Architecture map. - Test: generate on the deployment, register on the control instance, fingerprints equal, Test connection mismatch before and match after, key never logged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…l instance verifies from its own store Removes the now-unused hosting.annKey.vaultFiled/vaultSkipped/noVault keys. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Moderate issues remain in key-generation state handling and outdated fallback guidance.
Review effort: Lite
Findings: 2
Open (4)
What changed in this PR
Reverses announcement-key ownership so deployments generate keys and control instances register them.
Changes:
- Adds deployment-side key generation and round-trip coverage.
- Updates English/German localization and architecture documentation.
- Updates control-lane paths and related comments.
| File | Description |
|---|---|
test/Memex.Portal.Shared.Test/ControlLaneKeyRoundTripTest.cs |
Tests deployment-generated key flow. |
src/MeshWeaver.Messaging.Hub/Localization/strings.en.json |
Updates English localization. |
src/MeshWeaver.Messaging.Hub/Localization/strings.de.json |
Updates German localization. |
src/MeshWeaver.Documentation/Data/Architecture/SelfUpdateAnnouncementKey.md |
Documents the reversed flow. |
src/MeshWeaver.Documentation/Data/Architecture/InstanceSecrets.md |
Updates secret usage documentation. |
src/MeshWeaver.Documentation/Data/Architecture.md |
Updates the architecture index. |
memex/Memex.Portal.Shared/Settings/ControlLaneSettingsTab.cs |
Adds key-generation UI wiring. |
memex/Memex.Portal.Shared/SelfUpdate/SelfUpdateHandover.cs |
Updates path references. |
memex/Memex.Portal.Shared/MemexConfiguration.cs |
Updates configuration comments. |
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| "settings.controlLane": "Kontroll-Lane", | ||
| "ui.controlLane": "Kontroll-Lane", | ||
| "ui.mdControlLaneIntro": "Diese Instanz übergibt ihre Plattform-Updates an die **Kontrollinstanz**, die sie ausrollt. Jede Übergabe signiert sie mit einem **Ankündigungsschlüssel**, den die Kontrollinstanz für dieses Deployment ausgestellt hat. Fügen Sie diesen Schlüssel unten ein und klicken Sie auf **Speichern**. Er wird verschlüsselt gespeichert, wirkt sofort und ohne Neustart und wird **nie wieder angezeigt**: Diese Seite zeigt nur seinen Fingerabdruck. Vergleichen Sie den Fingerabdruck mit dem, den die Kontrollinstanz anzeigt, und klicken Sie dann auf **Verbindung testen**.", | ||
| "ui.mdControlLaneIntro": "Diese Instanz übergibt ihre Plattform-Updates an die **Kontrollinstanz**, die sie ausrollt. Jede Übergabe signiert sie mit einem **Ankündigungsschlüssel**. Klicken Sie auf **Schlüssel generieren**, um hier einen zu erzeugen: Er wird **einmal** angezeigt, zusammen mit seinem Fingerabdruck. Senden Sie den Schlüssel über einen **sicheren Kanal** (nie per gewöhnlicher E-Mail) an Systemorph und den Fingerabdruck separat. Sobald Systemorph ihn registriert hat, zeigt **Verbindung testen** eine Übereinstimmung. Der Schlüssel wird verschlüsselt gespeichert, wirkt ohne Neustart und wird nie wieder angezeigt. Falls Sie stattdessen einen Schlüssel von Systemorph erhalten haben, fügen Sie ihn unten ein und klicken Sie auf **Speichern**.", |
There was a problem hiding this comment.
Fixed in cbdb000. ui.controlLaneTestFleet (German) now tells the administrator to click Schlüssel generieren, send the key over a secure channel, and test again once it is registered. It no longer says "ausgestellt".
| "settings.controlLane": "Control lane", | ||
| "ui.controlLane": "Control lane", | ||
| "ui.mdControlLaneIntro": "This instance hands its platform updates to the **control instance**, which rolls them out. It signs each hand-over with an **announcement key** that the control instance issued for this deployment. Paste that key below and press **Save**. It is stored encrypted, takes effect immediately without a restart, and is **never shown again**: this page shows only its fingerprint. Compare the fingerprint with the one the control instance shows, then press **Test connection**.", | ||
| "ui.mdControlLaneIntro": "This instance hands its platform updates to the **control instance**, which rolls them out. It signs each hand-over with an **announcement key**. Click **Generate key** to create one here: it is shown **once**, with its fingerprint. Send the key to Systemorph over a **secure channel** (never plain e-mail), and the fingerprint separately. Once Systemorph has registered it, **Test connection** shows a match. The key is stored encrypted, takes effect without a restart, and is never shown again. If Systemorph gave you a key instead, paste it below and click **Save**.", |
There was a problem hiding this comment.
Fixed in cbdb000. ui.controlLaneTestFleet (English) now says to click Generate key, send the key to Systemorph over a secure channel, and test again once it is registered. The German text got the matching change.
| // Secrets a global admin may ENTER in the portal instead of an operator minting | ||
| // them in a vault (Doc/Architecture/InstanceSecrets). Each slot is a key the code | ||
| // below READS: the self-updater's signing key (Settings ▸ Control lane, on a target | ||
| // below READS: the self-updater's signing key (/Admin/Settings/ControlLane, on a target | ||
| // instance) and a deployment's own announcement key as the control inbox verifies it | ||
| // (issued on the Deployment record page, on the control instance). The fleet-wide |
There was a problem hiding this comment.
Fixed in cbdb000. The slot comment now says the signing key is GENERATED at /Admin/Settings/ControlLane on the target, and the per-deployment key is REGISTERED at /Hosting/Integrations/Deployment/{id} on the control instance, from the value the deployment generated.
| GenerateLabel = host.Localize("ui.controlLaneGenerate"), | ||
| RegenerateLabel = host.Localize("ui.controlLaneRegenerate"), |
There was a problem hiding this comment.
Fixed in cbdb000. The class summary now describes generating the key locally (shown once with its fingerprint, then sent to be registered), with pasting a key as the alternative.
Test Results (shard 2)764 tests ±0 573 ✅ ±0 7m 8s ⏱️ -1s Results for commit cbdb000. ± Comparison against base commit f78fcf6. This pull request removes 25 and adds 9 tests. Note that renamed tests count towards both.♻️ This comment has been updated with latest results. |
…, the slot comment and the fleet-secret test message Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Test Results (shard 5) 5 files ±0 5 suites ±0 14m 1s ⏱️ -52s Results for commit cbdb000. ± Comparison against base commit f78fcf6. This pull request removes 4 and adds 3 tests. Note that renamed tests count towards both. |
Test Results 17 files ±0 17 suites ±0 36m 1s ⏱️ -53s Results for commit cbdb000. ± Comparison against base commit f78fcf6. This pull request removes 29 and adds 12 tests. Note that renamed tests count towards both. |


What
The maintainer decided the announcement-key flow runs the other way round: the deployment generates its own key, and the control instance registers it. The control instance no longer issues the key and hands it to the client. Refs #5757, #5791, #5807.
/Admin/Settings/ControlLane(the target side, on every instance) gains Generate key / Generate new key.InstanceSecrets.Generateand shown ONCE with its fingerprint. This uses the platform's write-only secret section.hosting.annKey.*) now read "register the key the deployment generated", followed by a fingerprint compare. They no longer say "issue".issue→ "Register key", androtate→ "Register new key".keyLabel,keyPlaceholder,fingerprintHint.sendHintand the vault-copy keys (vaultFiled,vaultSkipped,noVault) are removed. They were never used by merged code, only by the held Plugins draft.SelfUpdateAnnouncementKeyis rewritten for the new direction: the shape table, "Generating, registering and testing", the rotation window, the client step-by-step, and owner actions./Admin/Settings/ControlLane(Admin app: /Admin is the Admin app, personal settings are the person's own app, Inbox app for every user #5807) and/Hosting/Integrations/Deployment/<id>(from the app map).InstanceSecretsand the Architecture map are updated to match.check-manual-keyvaultis clean.MemexConfigurationandSelfUpdateHandovernow name the new path.Verification
-warnaserror:Memex.Portal.Shared.Test,MeshWeaver.Documentation.Test,MeshWeaver.Messaging.Hub.Test. Each reports 0 errors and 0 warnings.ControlLane*tests: 23/23 passed. They include the newThe_deployment_generates_the_key_and_the_control_instance_registers_it, which checks:generate);client-b;MeshWeaver.Documentation.Test: 649/649 passed.Locali*: 66/66 passed.Cross-repo
Pairs-with: none — no public type or member is removed.
Implementers: none — no interface member is added.
Mirror-sync:
npm run sync:i18n -- --ref <merged core sha>in the MeshWeaver.Plugins companion #2435 (held draft). It addsui.controlLaneGenerate,ui.controlLaneRegenerate,ui.controlLaneGeneratedNote,hosting.annKey.keyLabel,hosting.annKey.keyPlaceholderandhosting.annKey.fingerprintHint, removeshosting.annKey.sendHint,hosting.annKey.vaultFiled,hosting.annKey.vaultSkippedandhosting.annKey.noVault, and re-wordsui.mdControlLaneIntro,hosting.annKey.intro,hosting.annKey.issue,hosting.annKey.rotateandhosting.annKey.notVerified.Nothing needs recycling after deploy: the settings tab is compiled platform code.
🤖 Generated with Claude Code