5f3eda2680c5b35fb062226be510cd21160b5e8d
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5f3eda2680 |
Makes a redeploy safe for the key ring and the schema
Nothing here is visible in normal operation. Its whole purpose is that swapping the release directory on deploy cannot silently destroy state. Data Protection secures the API keys that authenticate master/slave communication. Two separate defaults would each have destroyed them: keys are held on the filesystem, which a release swap discards, and the application discriminator is derived from the content root path, which changes with every release directory — so even keys stored in a database would have stopped being derivable. Keys now live in ApplicationDbContext and the discriminator is a fixed constant. Losing them produces no error. It produces stored keys that no longer decrypt, which presents as an apparent network fault between a Master and its slaves and is easily misdiagnosed. That is also why the tests assert the resulting configuration rather than the registration: the XmlRepository must be the EF one and the discriminator must be the constant, plus a round-trip proving a value encrypted before a deploy is readable after one. A test that only checked "Data Protection is registered" would have passed in the broken case too. Both modules previously called AddDataProtection() themselves. Module registration runs after the host's, so those calls re-registered the configuration chain and would have overridden the persistent store while IDataProtector still resolved. They are removed, with a comment at each site — the deletion otherwise looks like a regression. Each module's own test project now guards against it being reintroduced. ApplicationDbContext also migrates itself at startup. Deploy targets offer no CLI, so migrations cannot be a manual step on the server. Failures are classified rather than treated alike: a connection failure means the database is not up yet, normal when the app and the database start together after a reboot, and is retried with backoff; a migration failure means something is broken and fails at once. Either way the process does not start, which is what makes the liveness health check trustworthy — an application that cannot reach its schema never answers /health, so monitoring goes red instead of reporting a healthy instance that cannot serve a request. The cost of migrating without a human gate is that migrations must stay forward-compatible and non-destructive, since rollback is "redeploy the previous release". The new migration is purely additive. Also wires this and the preceding hosting commit into both hosts, as they touch the same lines of Program.cs. Two constraints are enforced by documentation rather than code, and belong in the deployment instructions: the key table must never be pruned, and only one instance may migrate a given database at a time. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HHoJpxYXzHACSQguHrC5fw |
||
|
|
29a93ef873 |
Separates website and admin roots, adds /health, hardens the availability gate
Prepares the single-host layout for deployment. The customer's public website moves from wwwroot/ to wwwroot/web/, so a CMS deploy can no longer overwrite content it does not own: with the website in its own directory, the release directory can be swapped without touching it. Each front-end gets its own file provider, and both tolerate a missing directory at startup — a fresh deployment has no website until a separate workspace deploys one, and the CMS must still serve /admin and the API. When the website's index.html is absent, an embedded placeholder is served instead of a 404, which also doubles as proof the CMS itself is running. The placeholder is embedded in the assembly rather than shipped into wwwroot/web/, because that directory is owned and overwritten by the website workspace. Adds GET /health for uptime monitoring. It reports infrastructure liveness only and is deliberately NOT the same thing as /api/v1/Availability/status or /api/v1/System/capabilities: those are CMS domain state that also serve the master/slave protocol. A healthy instance can be switched off by design, and a switched-on instance can be unhealthy, so conflating them would alert on business state and stay silent on real outages. /health is on the availability gate's bypass list for the same reason. Fixes a real defect in the gate's admin bypass. It parsed the bearer token with ReadJwtToken, which reads claims without verifying the signature, so an unauthenticated caller could forge an unsigned token carrying an Owner role claim and bypass the gate that suspends a customer's site. Protected endpoints still rejected them, so nothing leaked — but the gate itself was bypassable. The token is now fully validated against the same parameters as the bearer scheme, resolved from one shared source so the two cannot drift apart. Host wiring for these changes lands with the data-durability commit, since both units touch the same lines of Program.cs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HHoJpxYXzHACSQguHrC5fw |
||
|
|
8568ca43c6 |
Plans the Gitea deployment feature and refreshes the codebase analysis
Adds the AI-DLC inception record for deploying the CMS as a single .NET application on hosting where no server configuration is possible. The reverse-engineering artifacts were regenerated: the previous set predated the Master module, the Slave host, the solution reorganisation and single-host serving, all of which matter for deployment. Findings were verified by running the build, both test suites and the linter rather than inferred, which surfaced two facts the plan depends on: the frontend lint gate currently fails (5 errors), and two transitive packages carry high-severity advisories. Records 24 functional requirements, 32 traced decisions and a seven-unit decomposition whose ordering is load-bearing: durability work must land before the first automated deploy, or the very first deploy is the one that silently breaks master/slave trust. Two conflicts found while designing and carried into the units: - Both modules call AddDataProtection(), which runs after the host and would override a persistent key store while still passing any registration test. - The availability gate runs before authentication, so its admin bypass cannot read HttpContext.User. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HHoJpxYXzHACSQguHrC5fw |