15 KiB
AyaNova 8 back end → .NET 10
The plan and running record for the port. Tick items and add a log line in the same commit as the change they record. Anything found along the way that is out of scope goes under "Deferred", not into the branch.
| Starting point | AyaNova 8.2.4: ASP.NET Core 8, EF Core 8 |
| Target | net10.0, every back-end package at its current release |
| Front end | Minimal or no change. Rebuild only if needed (open question) |
| Branch | net10-port in raven (and raven-client and raven-test-integration and raven-launcher) |
| Baseline tag | net8-baseline, on the last commit before the port |
| Release version | Not yet decided (open question) |
Where this plan comes from
Sockeye, the internal licensing server, is a near-copy of this codebase. It was ported from
.NET 6 to .NET 10 in September–October 2026 and released as 8.1.0. This plan is that
port's carry-forward list (Appendix A of the Sockeye migration runbook), arranged as a
checklist.
AyaNova starts from .NET 8, not .NET 6. So every item is "check whether this applies",
not "this will apply". Several items will turn out to be already done or not present. Mark
those n/a with a one-line reason rather than deleting them.
When an item is unclear, look at how Sockeye did it:
git -C ..\sockeye log --oneline net6-baseline..8.1.0
git -C ..\sockeye show <commit>
Where AyaNova differs from Sockeye: customers choose their own database role names and passwords, every customer uses reports, and AyaNova verifies licences rather than issuing them. Items touching those three areas carry more weight here than they did in Sockeye.
Scope
- In: target framework; all NuGet packages in
server/AyaNova; code changes those require; the Sockeye-found fixes in section 3, each as its own commit. - Out: front-end dependency changes; refactoring (Roadmap step D); new features; PostgreSQL major upgrades.
- Undecided: see "Open questions".
Working method
- Version bump first. One concern per commit.
- Prove each gate on the unmodified code before changing anything, so a later failure points at the port, not at the test.
- Packages that must move together: framework-matched
Microsoft.*packages all go to 10.x in one step.BouncyCastle.Cryptography2.x goes with MailKit/MimeKit 4.x, because MimeKit 3.x pulls inPortable.BouncyCastleand the assemblies clash. - Publish
server\AyaNova\AyaNova.csprojdirectly, never the solution, so no test project can reach the artifact.
0. Before changing anything
- Record any port work already done in earlier sessions (branch, commits) here first, and reconcile it with this list
ravenandraven-clientclean onmainand pushed- Annotated tag
net8-baselineon the pre-port commit inravenandraven-client, pushed by name (git push origin net8-baseline), checked withgit ls-remote --tags origin - Branch
net10-portcreated and pushed with upstream - First commit: version bump in every place listed in
devdocs/build/BUILD.md→ "Version numbers" - Dev machine has the .NET 10 SDK and keeps the .NET 8 runtime for baseline comparisons
Gates (prove on the unmodified code)
- Integration suite baseline. Run
raven-test-integrationagainst the unmodified build and record the counts in the log below. Expected: everything passes except the three known server-side failures listed in that repo's CLAUDE.md - Licence verification gate. Find out exactly how AyaNova 8 verifies a v8 licence: library, public key, and when it re-checks. If it uses BouncyCastle, write a test that runs every v8 row of Sockeye's
alicensefixture through AyaNova's own verification routine. The fixture is customer data: it stays outside the repo and is found through an environment variable, as Sockeye's re-sign harness does withSOCKEYE_RESIGN_FIXTURE. Must be green on unmodified code. A regression here would reject licences customers already have installed - Report baseline. Render the advanced reports on the unmodified build and keep the PDFs for comparison. Sockeye shares only one report with AyaNova, so Sockeye's testing says little about AyaNova's reports
- E2E baseline. Cypress run against a devtest server on the unmodified build
1. Framework and packages
net8.0→net10.0, with every framework-matchedMicrosoft.*package to 10.x in the same step- Hosting model.
WebHost,IWebHostandWebHostBuilderare obsolete in .NET 10 (ASPDEPR008, ASPDEPR004).Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(web => web.UseStartup<Startup>())keeps theStartupclass unchanged - Asp.Versioning. Older notes say AyaNova already uses
Asp.Versioning.Mvc[unverified]. If not: rename fromMicrosoft.AspNetCore.Mvc.Versioning;ApiVersionmoves toAsp.Versioning; wiring isservices.AddApiVersioning(...).AddMvc().AddApiExplorer(...). Leave AV0014 warnings: do not add[ApiController] - Npgsql / EF Core 10.
- Check
Npgsql.EnableLegacyTimestampBehavior. Sockeye's was commented out. If AyaNova sets it,DateTime.Kindis a real port item - Check for
ConfigureWarnings(w => w.Throw()). If present, every new EF warning becomes an exception naming its ID: fix the query, or move that ID to.Log(...) - Exercise LINQ-based queries screen by screen. Data-list filters build raw SQL and are unaffected
- Check
- BouncyCastle. If AyaNova references
BouncyCastle, it becomesBouncyCastle.Cryptography2.x, moved together with MailKit/MimeKit 4.x. In Sockeye,PemReaderstill yields anAsymmetricCipherKeyPairfor a PKCS#1 key - Swashbuckle 10 / OpenAPI.NET 2.
Microsoft.OpenApi.Modelsmerged intoMicrosoft.OpenApi. Bearer requirement becomesc.AddSecurityRequirement(document => new OpenApiSecurityRequirement { [new OpenApiSecuritySchemeReference("Bearer", document)] = [] }). No explicitMicrosoft.OpenApireference - NLog 6.
ArchiveNumberingis gone;NLogBuilder.ConfigureNLogis replaced byNLog.LogManager.Configuration = logConfig. Archive numbers keep incrementing and the un-numbered file is always current. If the log viewer in the UI sorts by file name, the order breaks past 99 - Newtonsoft.Json.
Mvc.NewtonsoftJson10.x sets a 13.0.3 floor (NU1605). Pin it as a direct reference - JwtBearer 10.x, which also retires the vulnerable IdentityModel 6.10.0 dependency if AyaNova still resolves it
- SMTP. Any use of
System.Net.Mail.SmtpClient(SYSLIB0014) moves to MailKit 4.x, as in Sockeye and contact - PuppeteerSharp → 25.x. Reporting is AyaNova's main Puppeteer surface
BrowserFetcher.DefaultChromiumRevisionis gone: callDownloadAsync()with no argumentIJSHandle.ExecutionContextis gone: evaluate on the handle. Evaluate results areJsonElement?- The default download is Chrome for Testing, into
Chrome/andChromeHeadlessShell/beside the app. AyaNova 8.2.0 already moved from Chromium to Chrome to follow Puppeteer's default, so the browser choice is settled - Launch arguments must be separate array elements (
--no-sandboxon its own). This includes the report-render browser params setting customers can edit - AppArmor restricts unprivileged user namespaces on Ubuntu 24.04+, which affects the Chrome sandbox
- Remaining packages to current releases
- Warnings: CA2022 →
ReadExactly; ASP0019 → header indexer instead ofHeaders.Append - MiniProfiler. Confirm it is absent:
git grep -i -e miniprofiler -e mini-profiler -e StackExchange.Profilingin bothravenandraven-client - BinaryFormatter. Confirm it is unused, or that
EnableUnsafeBinaryFormatterSerializationis already false System.GC.Server. Keep the Web SDK default (true). On one vCPU the runtime uses workstation GC anyway, and DATAS keeps server GC lean since .NET 9InvariantGlobalization. Decide on EF Core string comparison and date-parsing grounds. Still open in Sockeye too
2. Configuration behaviour to check
- URL binding precedence. An in-code
UseUrlsbeatsASPNETCORE_URLS. Find where AyaNova takes its URLs from (Sockeye:SOCKEYE_USE_URLSinconfig.json) and make sure the documented setting is the one that wins - Logging providers.
appsettings.jsonvery likely sets the Debug and Console providers toTrace, as Sockeye's does. Check whetherProgram.cscallsClearProviders()before adding NLog. If it does not,Tracefloods the journal under systemd. Fix the levels in the repo either way - Data Protection keys. Only matters if AyaNova encrypts anything with Data Protection and runs under a service account with no home directory: then the key ring must be persisted explicitly. A JWT-only API is unaffected
3. Fixes found through Sockeye (each its own commit)
These are bugs, not port mechanics, so each is a separate commit that can be reverted alone. Sockeye included its equivalents in the port branch. John decides per item whether they go in this branch or a follow-up release. UPDATE: John has decided these all go in this branch and some more additional cases in the case database that are urgent, exact cases TBD.
- Case 4676: data-list SQL injection — priority. The numeric and role filters in
DataListSqlFilterCriteriaBuilderbuilt SQL from input. In AyaNova any authenticated user who can open a data list could reach them. Port the Sockeye fix DbUtil.AdminConnectionString. If it is_dbConnectionString.Replace(_dbName, "postgres"), any customer whose role name, password or host contains the database name as text gets a mangled admin connection, and the boot check authenticates as the wrong user. Fix:new NpgsqlConnectionStringBuilder(cs) { Database = "postgres" }.ConnectionString- Backup
pg_dumpcall. If it builds apostgresql://user:password@host:port/dbURI with the raw password and runs it through a shell for a> fileredirect, a password with spaces or@ : / ? # %breaks backups. Fix: password inPGPASSWORDin the process environment, connection parameters as separate arguments throughProcessStartInfo.ArgumentList(no shell), output with-f - Drop-and-recreate database. Without
CREATEDBit drops the database and then fails to create it. Test the permission before dropping, or change the order - Report browser download location. Point it under the data directory in code. Beside the app it is read-only under a hardened systemd unit (
ProtectSystem=strict), and a redeploy that replaces the app folder deletes it - Report failures must not kill the server. On the Sockeye destination a failed browser download was an unhandled exception that ended the whole process (systemd
core-dump, then restart); the client saw onlyTypeError … reading 'length'. Catch download and launch failures, log them through NLog, and return an error to the client - Data-list date filters, small fixes.
>=and<should useZeroSeconds, notMaxSeconds; dateNotEqualshould exclude the whole minute (< ZeroSeconds OR > MaxSeconds). In DEBUG builds,DataListField.UiFieldDataTypeis read beforeDataListFieldis null-checked - Relative date tokens use the server's time zone (
*today*,*yesterday*…), which is wrong for any user in a different zone from the server. The fix builds the range fromclientTimeStamp.Offset, with care for ranges that cross a DST change. This changes behaviour and affects the integration tests'TIME_ZONE_ADJUSTMENT. Probably better as a follow-up release
Not port items, listed so they are not forgotten: the three server bugs the integration suite is waiting on (case 4653, case 4654, the contract issue).
4. Test under realistic conditions
- Database role. Run under a non-superuser role named like the database, with a password containing spaces and
@ : / ? # %. Boot, on-demand backup, scheduled backup, restore. Sockeye ran aspostgresfor years and never exercised either bug in section 3 - Restore with a dedicated role.
createdb -O <role>, thenpg_restore --no-owner --no-privileges --role=<role>. Without--role, a superuser restore leaves every table owned bypostgres, and the integrity check, which counts throughinformation_schema, fails. Compare with the restore instructions in the customer manual (docs/) and note any change needed - Reports, all advanced reports, compared with the baseline PDFs. On Windows and on Linux. On Linux also:
lddon the downloaded browser binary for missing libraries (install exactly what is reported; several Ubuntu 24.04+ names carry at64suffix); the sandbox under the service account; no orphanedchromeprocesses after a cancelled render - Integration suite: same result as the baseline
- Licence verification gate: green
- Cypress E2E against a devtest server running the .NET 10 build
build-release.batproduces all five packages; install and start each one: Windows standalone, Windows LAN, Linux server, Linux desktop, subscription Linux
5. Release
- Every host that runs a framework-dependent build needs the .NET 10 runtime before it gets the release:
- Hosted subscription droplets (Ubuntu 24.04). How
aspnetcore-runtime-10.0is obtained on 24.04 needs checking [unverified]. On 26.04 it is in Ubuntu's own archive - Self-hosted Linux customers (server and desktop zips are framework-dependent)
- Windows LAN:
lan.issprompts for the ASP.NET Core Hosting Bundle. Update its text and link to .NET 10 - Windows standalone and the launcher are self-contained: no runtime needed on the customer's machine
- Hosted subscription droplets (Ubuntu 24.04). How
- Customer manual (
docs/): system requirements and upgrade instructions for self-hosted customers - Build VM: install the .NET 10 SDK; update
devdocs/build/DEV-VM-SETUP.mdanddevdocs/build/BUILD.md - Final gate runs on the finished code
- Merge, per repo:
git switch net10-port; git status; git push; git fetch origin git switch main; git pull git log --oneline net10-port..main # must print nothing: main untouched git merge --no-ff net10-port -m "Merge net10-port: .NET 10 port" git diff net10-port main # must print nothing: main is exactly what was tested git push origin main git tag -a <version> -m "<release note>" git push origin <version> git ls-remote --tags origin--no-ffkeeps a merge commit, so the whole port can be backed out withgit revert -m 1 <merge> - Rebuild the client from
main, publish, and test the published artifact itself - Rollout: own test droplet first, then customer droplets by time zone, as in the weekly routine
- After release: delete
net10-portlocally and on the remote
Open questions
- Release version number (Sockeye's port was released as 8.1.0) Answer: 8.3.0 will be the version
raven-launcher(.NET 8, self-contained): move to .NET 10 in the same release? Answer: Yesraven-test-integration: move tonet10.0(separate commit) or leave onnet8.0? It only talks HTTP to the server Answer: Move to net10.0 for consistency- Does the client need a rebuild in this pass, or can its current output be carried across? Answer: yes it will have minor changes as well as version number to 8.3.0
- Section 3: which fixes go in this branch, which in a follow-up
- How the .NET 10 runtime gets onto Ubuntu 24.04 hosts
Deferred
Found during the port, out of scope. One line each.
Log
| Date | Step | Result |
|---|---|---|