diff --git a/aidlc-docs/features/gitea-deployment-workflow/construction/u2-data-durability/code/generation-summary.md b/aidlc-docs/features/gitea-deployment-workflow/construction/u2-data-durability/code/generation-summary.md index 209fed3..efad8c3 100644 --- a/aidlc-docs/features/gitea-deployment-workflow/construction/u2-data-durability/code/generation-summary.md +++ b/aidlc-docs/features/gitea-deployment-workflow/construction/u2-data-durability/code/generation-summary.md @@ -85,3 +85,35 @@ Also recorded for Operations: **DEV-05** — keys are stored unencrypted at rest No failures occurred during generation. **Not verifiable at this stage**: the composed startup path (`MigrateCoreDatabase` against a real database, and both hosts actually starting) requires SQL Server. Carried to the phase-level Build and Test stage. + +--- + +## Follow-up found in local testing (2026-07-28) + +Starting both hosts after U2 produced, on the master: + +``` +CryptographicException: The key {2ad1bf17-...} was not found in the key ring + at ApiKeyProtector.Unprotect + at CmsInstanceService.VerifyIntegrityAsync +``` + +**Not a defect in this unit — it is this unit's change meeting data written before it.** The stored +`MasterCmsInstances.ApiKey` had been encrypted under the old file-based key ring in +`%LOCALAPPDATA%\ASP.NET\DataProtection-Keys`; both databases now hold their own freshly generated +single key, which cannot decrypt it. The key ring working correctly is precisely why the old key is +not found. + +**The gap this unit left**: no one-time path for ciphertext that predates the move. Recorded as +**ASM-08** in `requirements.md`. Confirmed with the user on 2026-07-28 that the CMS is not deployed +anywhere yet, so the first deploy meets an empty database and no migration is shipped. If that ever +stops being true, U6 must not deploy until either the affected rows are cleared and the instances +re-registered, or this unit gains a fallback that reads the old ring and re-encrypts. + +Resolved locally by deleting the one stale row on each side and re-registering the slave. + +**Worth noting for U4's alerting**: the two sides fail differently. `Modules.Master/ApiKeyProtector` +lets the exception escape, while `Modules.Availability/MasterApiKeyProtector` catches it and returns +null, so the slave reports "master API key rejected" instead. That is exactly the ambiguity U4's +`MasterApiKeyRejected` event documents — an intruder, **or** an unreadable key ring — occurring in +practice. diff --git a/aidlc-docs/features/gitea-deployment-workflow/inception/requirements/requirements.md b/aidlc-docs/features/gitea-deployment-workflow/inception/requirements/requirements.md index 68f99a2..82233b6 100644 --- a/aidlc-docs/features/gitea-deployment-workflow/inception/requirements/requirements.md +++ b/aidlc-docs/features/gitea-deployment-workflow/inception/requirements/requirements.md @@ -335,6 +335,7 @@ DEV-01…04 are **pre-existing or cost-driven** and none is introduced by this f | ASM-05 | The existing Umami instance at `analytics.slpsoftware.nl` remains available and its script origin can be added to the CSP. | FR-16, FR-18. | Umami setup gains host work, as in the reference project. | | ASM-06 | One Sentry project with environment tags is acceptable for both backend and frontend events of this CMS. | D-19. | Split into more projects; only configuration changes. | | ASM-07 | Existing Gitea secrets for the Pi (`PI_MAIN_*` in the reference project) can be reused or replicated for this repository. | FR-02. | New secrets are created; documented in FR-23. | +| ASM-08 | **No environment holds API keys encrypted under the old file-based key ring.** Confirmed by the user on 2026-07-28: the CMS is not deployed anywhere yet, so the first deploy of U2 meets an empty database. U2 therefore ships **no** one-time re-encryption path for pre-existing ciphertext. | U2 moved the Data Protection key ring from the filesystem into the database. Ciphertext written under the old ring cannot be decrypted by the new one — it fails with `The key {id} was not found in the key ring`. Observed locally on 2026-07-28 against rows written before U2, and resolved by deleting and re-registering. | **U6 must not deploy** until either the affected rows are cleared and the instances re-registered, or U2 gains a migration that reads the old ring as a fallback and re-encrypts. This is a silent, destructive failure of master/slave communication — exactly the class NFR-02 exists to prevent. | --- @@ -353,9 +354,9 @@ DEV-01…04 are **pre-existing or cost-driven** and none is introduced by this f This feature turns a manually deployed modular-monolith CMS into one with an automated, auditable pipeline, on hosting where nothing can be configured server-side. -**24 functional requirements, 10 non-functional requirements, 32 traced decisions, 7 assumptions, 2 remaining open items, 4 documented security deviations.** +**24 functional requirements, 10 non-functional requirements, 32 traced decisions, 8 assumptions, 2 remaining open items, 4 documented security deviations.** -*(FR-24 added and OPEN-02 closed at Application Design on 2026-07-27. OPEN-01 closed at NFR Design on 2026-07-28.)* +*(FR-24 added and OPEN-02 closed at Application Design on 2026-07-27. OPEN-01 closed at NFR Design on 2026-07-28. ASM-08 added on 2026-07-28 after the key-ring migration gap surfaced in local testing.)* The three requirements that carry the most risk if implemented carelessly: