Reorganize developer docs into devdocs/; new CLAUDE.md and .NET 10 port plan

This commit is contained in:
2026-10-22 11:52:52 -07:00
parent 47c3f9bc28
commit 2dd8a4bb04
10 changed files with 716 additions and 692 deletions

View File

@@ -1,374 +0,0 @@
# AyaNova v8 Backend Build Environment Documentation
> **Purpose**: This document captures the complete backend build process for AyaNova v8, including all deployment targets and installers.
## Overview
The backend build produces **6 deployment packages** from **3 projects**:
### Projects
| Project | Framework | Purpose |
|---------|-----------|---------|
| AyaNova (server) | .NET 8.0 | Main ASP.NET Core web server |
| raven-launcher | .NET 8.0 | Windows launcher for standalone mode |
| AyaNovaQBI | .NET Framework 4.8 | QuickBooks Desktop integration (separate build) |
### Deployment Targets
| Target | Description | Output |
|--------|-------------|--------|
| Subscription Linux x64 | Your hosted service (DigitalOcean) | `ayanova-subscription-linux-x64-server.zip` |
| Perpetual Linux Server | Self-hosted Linux server | `ayanova-linux-x64-server.zip` |
| Perpetual Linux Desktop | Single-user Linux desktop | `ayanova-linux-x64-desktop.zip` |
| Windows Standalone | Self-contained single-user (includes .NET runtime + PostgreSQL) | `ayanova-windows-x64-single-setup.exe` |
| Windows LAN | Network server (requires separate .NET + PostgreSQL) | `ayanova-windows-x64-lan-setup.exe` |
| QuickBooks Integration | Windows desktop app | `ayanova-qbi-setup.exe` (separate build) |
---
## Directory Structure
```
c:\data\code\
├── raven\ # Main repository root
│ ├── build-release.bat # Master build script
│ ├── raven.sln # Visual Studio solution
│ ├── server\
│ │ └── AyaNova\ # ASP.NET Core server project
│ │ ├── AyaNova.csproj
│ │ ├── Program.cs
│ │ ├── Startup.cs
│ │ ├── wwwroot\ # Frontend files copied here
│ │ └── resource\ # Report templates, etc.
│ ├── docs\
│ │ └── 8.0\
│ │ ├── ayanova\ # Main documentation (MkDocs)
│ │ │ └── mkdocs.yml
│ │ └── customer\ # Customer portal documentation
│ │ └── mkdocs.yml
│ ├── dist\
│ │ ├── assets\ # Shared assets (license, configs, icons)
│ │ │ ├── LICENSE
│ │ │ ├── license.rtf
│ │ │ ├── linux-desktop\config.json
│ │ │ ├── linux-server\config.json
│ │ │ ├── lan-install-config.json
│ │ │ └── logo.ico
│ │ ├── install\
│ │ │ └── windows\x64\ # Inno Setup scripts
│ │ │ ├── standalone.iss
│ │ │ └── lan.iss
│ │ ├── installers\ # Build output (generated)
│ │ ├── linux-x64\ # Linux build output (generated)
│ │ ├── subscription-build-linux-x64\ # Subscription build output (generated)
│ │ └── win-x64\ # Windows build output (generated)
│ │ ├── ayanova\
│ │ │ ├── standalone\ # Self-contained build
│ │ │ └── framework-dependent\ # Requires .NET installed
│ │ ├── launcher\ # Launcher build output
│ │ └── postgres-standalone\ # Embedded PostgreSQL for standalone
│ └── graphics\ # Icons, logos
│
├── raven-client\ # Frontend (separate repo/folder)
│ └── ayanova\
│
├── raven-launcher\ # Launcher project
│ ├── raven-launcher.csproj
│ └── config.json
│
└── ravenqbi\ # QuickBooks integration (separate)
├── AyaNovaQBI\
│ └── AyaNovaQBI.csproj
└── install\
└── qbi.iss
```
---
## Build Requirements
### Software
| Tool | Version | Purpose |
|------|---------|---------|
| .NET SDK | 8.0.x | Build ASP.NET Core projects |
| Node.js | 12.22.9 | Build frontend (see frontend docs) |
| Python | 3.11.x | Build MkDocs documentation |
| MkDocs | Latest | Documentation generator |
| Inno Setup | 6.x | Windows installer creation |
| 7-Zip | Latest | Create Linux distribution zips |
| Visual Studio 2022 | Latest | Build QBI project (.NET Framework 4.8) |
### Paths (hardcoded in build scripts)
The build scripts expect these exact paths:
- `C:\data\code\raven\` - Main repository
- `C:\data\code\raven-client\ayanova\` - Frontend project
- `C:\data\code\raven-launcher\` - Launcher project
- `C:\data\code\ravenqbi\` - QuickBooks integration
- `C:\Program Files\7-Zip\7z.exe` - 7-Zip
- `C:\Program Files (x86)\Inno Setup 6\ISCC.exe` - Inno Setup compiler
---
## Build Process (build-release.bat)
The master build script executes in this order:
### 1. Clean Output Folders
```batch
rmdir c:\data\code\raven\server\AyaNova\wwwroot /s/q
mkdir c:\data\code\raven\server\AyaNova\wwwroot
rmdir C:\data\code\raven\dist\installers /s/q
mkdir C:\data\code\raven\dist\installers
```
### 2. Build Documentation (MkDocs)
```batch
cd c:\data\code\raven\docs\8.0\ayanova
mkdocs build
cd c:\data\code\raven\docs\8.0\customer
mkdocs build
```
Output goes to `wwwroot/docs` (configured in mkdocs.yml).
### 3. Build Frontend (Vue.js)
```batch
cd c:\data\code\raven-client\ayanova
call npm run build
xcopy dist\* ..\raven\server\AyaNova\wwwroot\ /e
```
### 4. Build Subscription Linux x64
```batch
dotnet publish --property:PublishDir=C:\data\code\raven\dist\subscription-build-linux-x64\ \
-c Release -r linux-x64 --no-self-contained -p:SUBSCRIPTION_BUILD=true
```
Key flag: `-p:SUBSCRIPTION_BUILD=true` sets a compile-time constant for subscription-specific features.
Then packages to zip with config:
```batch
7z a ayanova-subscription-linux-x64-server.zip subscription-build-linux-x64\*
7z a ayanova-subscription-linux-x64-server.zip assets\linux-server\config.json
```
### 5. Build Perpetual Linux x64
```batch
dotnet publish --property:PublishDir=C:\data\code\raven\dist\linux-x64\ \
-c Release -r linux-x64 --no-self-contained -p:SUBSCRIPTION_BUILD=false
```
Creates two packages (same build, different config):
- `ayanova-linux-x64-desktop.zip` (with desktop config)
- `ayanova-linux-x64-server.zip` (with server config)
### 6. Build Windows Standalone (Self-Contained)
```batch
dotnet publish -c Release --property:PublishDir=C:\data\code\raven\dist\win-x64\ayanova\standalone\ \
-r win-x64 --self-contained
```
This includes the .NET runtime in the output (~150MB larger).
### 7. Build Windows Framework-Dependent
```batch
dotnet publish -c Release --property:PublishDir=C:\data\code\raven\dist\win-x64\ayanova\framework-dependent\ \
-r win-x64 --no-self-contained
```
Requires .NET 8 runtime installed on target machine.
### 8. Build Launcher
```batch
cd C:\data\code\raven-launcher\
dotnet publish -c Release --property:PublishDir=C:\data\code\raven\dist\win-x64\launcher\ \
-r win-x64 --self-contained
```
The launcher is always self-contained (uses `PublishTrimmed` to reduce size).
### 9. Create Windows Installers (Inno Setup)
```batch
ISCC.exe standalone.iss
ISCC.exe lan.iss
```
---
## Project-Specific Notes
### AyaNova Server (AyaNova.csproj)
**Conditional Compilation:**
```xml
<DefineConstants Condition=" '$(SUBSCRIPTION_BUILD)' == 'true' ">$(DefineConstants);SUBSCRIPTION_BUILD</DefineConstants>
```
Use `#if SUBSCRIPTION_BUILD` in code for subscription-only features.
**Key Dependencies:**
- Entity Framework Core 8.0 with Npgsql (PostgreSQL)
- PuppeteerSharp for PDF report generation (downloads Chromium on first run)
- MailKit for email
- JWT authentication
**Resource Files:**
Report templates and other resources are copied to output:
```xml
<Content Include=".\resource\*.*">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
```
### Launcher (raven-launcher.csproj)
Simple console app that:
1. Starts embedded PostgreSQL (standalone mode only)
2. Starts the AyaNova server
3. Opens default browser to the login page
4. Waits for user to close
Uses `PublishTrimmed` to reduce self-contained size.
### QuickBooks Integration (AyaNovaQBI.csproj)
**Important:** This is a .NET Framework 4.8 Windows Forms application, NOT .NET Core.
- Requires Visual Studio 2022 (not VS Code) for the Windows Forms designer
- References `interop.QBFC14.dll` from QuickBooks SDK (must be installed)
- Targets x86 (32-bit) because QuickBooks SDK is 32-bit only
- Has a pre-build event that runs a timestamp utility
**Why Visual Studio is required:**
The Windows Forms designer only works in Visual Studio. The code could theoretically be edited in VS Code, but the designer is essential for the UI work.
---
## Windows Installer Details
### Standalone (standalone.iss)
Includes:
- Self-contained AyaNova server (with .NET runtime)
- Self-contained launcher
- Embedded PostgreSQL (`postgres-standalone` folder)
- Empty database template
Creates desktop shortcut to launcher. User clicks one icon, everything starts.
**Key paths:**
- App: `{autopf}\ayanova`
- Data: `{commonappdata}\ayanova\database`
- Logs: `{commonappdata}\ayanova\logs`
### LAN (lan.iss)
Includes:
- Framework-dependent AyaNova server (no .NET runtime)
- Config file for network mode
Does NOT include PostgreSQL - user must install separately.
Prompts user to download:
- ASP.NET Core Runtime Hosting Bundle
- PostgreSQL
---
## Embedded PostgreSQL (Windows Standalone)
The standalone installer includes a portable PostgreSQL in `dist\win-x64\postgres-standalone\`.
This is a pre-configured PostgreSQL installation that:
- Runs without system installation
- Stores data in `{commonappdata}\ayanova\database`
- Is started/stopped by the launcher
**Source:** You'll need to maintain this - it's a copy of a PostgreSQL Windows binary distribution with a pre-initialized data directory.
---
## QuickBooks Integration Build (Separate)
The QBI is built separately and not included in the main build script.
```batch
cd c:\data\code\ravenqbi\AyaNovaQBI
msbuild /p:Configuration=Release
cd c:\data\code\ravenqbi\install
"C:\Program Files (x86)\Inno Setup 6\ISCC.exe" qbi.iss
```
Output: `c:\data\code\ravenqbi\install\output\ayanova-qbi-setup.exe`
---
## Version Management
Versions must be updated in multiple places:
| File | Location |
|------|----------|
| `AyaNova.csproj` | `<Version>8.2.4</Version>` |
| `standalone.iss` | `#define MyAppVersion "8.2.4"` |
| `lan.iss` | `#define MyAppVersion "8.2.4"` |
| `qbi.iss` | `#define MyAppVersion "8.0.2"` |
| `package.json` (frontend) | `"version": "8.2.4"` |
Consider creating a version file or script to update all at once.
---
## Containerization Considerations
### What CAN be containerized (for build):
1. **Frontend build** - Already done (Dockerfile.frontend)
2. **Documentation build** - Already done (Dockerfile.docs)
3. **Linux server builds** - .NET SDK in Docker can cross-compile for Linux
4. **Windows framework-dependent build** - Can be built in Docker
### What CANNOT be easily containerized:
1. **Windows self-contained builds** - Need Windows container or Windows host
2. **Inno Setup installers** - Windows-only tool
3. **QuickBooks Integration** - .NET Framework 4.8, Windows Forms, requires Visual Studio
4. **Embedded PostgreSQL** - Windows-specific binaries
### Recommendation:
For a VPS-based build:
- Use Docker/Linux for: Frontend, Docs, Linux targets
- Keep Windows laptop for: Windows installers, QBI
- Or: Use a Windows VM/GitHub Actions for Windows builds
---
## Troubleshooting
### "PuppeteerSharp failed to download Chromium"
The server downloads Chromium on first run for PDF generation. If this fails:
1. Check internet connectivity
2. Check firewall/proxy settings
3. Pre-download Chrome and set `REPORT_RENDER_BROWSER_PATH` environment variable
### "SUBSCRIPTION_BUILD not defined"
Ensure you're passing the flag correctly:
```batch
dotnet publish ... -p:SUBSCRIPTION_BUILD=true
```
### Inno Setup errors
- Ensure all source paths exist
- Check that previous build steps completed
- Verify 7-Zip and Inno Setup are installed at expected paths
---
## Version History
| Date | Change |
|------|--------|
| 2026-02-01 | Initial documentation created |

View File

@@ -1,271 +0,0 @@
# AyaNova v8 Build Environment Documentation
> **Purpose**: This document captures the exact build environment for AyaNova v8 as of February 2026. Use this to recreate the build environment on a new machine or in a container.
## Overview
AyaNova v8 consists of three main build components:
1. **Frontend** - Vue.js 2 application (JavaScript, not TypeScript)
2. **Documentation** - MkDocs with Material theme
3. **Backend** - ASP.NET Core 8.0 server (C#)
The frontend and docs are built first, then copied into the backend project's `wwwroot` folder before the backend is built and packaged for distribution.
---
## Current Build Machine Specifications
| Component | Version | Notes |
|-----------|---------|-------|
| Operating System | Windows 11 | ThinkPad X13 Gen 3 |
| Node.js | v12.22.9 | **End-of-life** - do not upgrade without testing |
| npm | (check with `npm -v`) | Comes with Node |
| Python | 3.11.1 | For MkDocs |
| .NET SDK | 8.0.x | For backend build |
---
## Frontend Build Environment
### Directory Structure
```
c:\data\code\
├── raven-client\
│ └── ayanova\ # Vue.js frontend project
│ ├── src\ # Source code
│ ├── public\ # Static assets
│ ├── dist\ # Build output (generated)
│ ├── package.json
│ ├── vue.config.js
│ └── buildrelease.bat
├── raven\
│ ├── server\
│ │ └── AyaNova\
│ │ └── wwwroot\ # Frontend files copied here after build
│ └── docs\
│ └── 8.0\
│ └── ayanova\ # MkDocs source
│ └── mkdocs.yml
```
### Node.js Version: Critical Information
**Current version: Node.js 12.22.9**
This project uses an old Node version because:
1. The `fibers` package (used by sass-loader for faster Sass compilation) is a native C++ addon that stopped being maintained and doesn't compile on newer Node versions
2. Various other dependencies have peer dependency requirements that break on Node 14+
**Do not upgrade Node without first testing in an isolated environment.**
### Key Dependencies
From `package.json`:
| Package | Version | Purpose |
|---------|---------|---------|
| vue | 2.6.14 | Core framework (Vue 2, end-of-life Dec 2023) |
| vuetify | 2.6.6 | UI component library |
| @vue/cli-service | 4.5.15 | Build tooling |
| webpack | 4.46.0 | Module bundler |
| sass | 1.52.3 | Sass compiler |
| fibers | 4.0.3 | **Problematic** - native addon for Sass |
| monaco-editor | 0.30.1 | Code editor component |
### Build Process
The frontend build is executed via `buildrelease.bat`:
```batch
@echo **************************************************************
@echo ******************** BUILD CLIENT ****************************
@echo **************************************************************
rmdir c:\data\code\raven\server\AyaNova\wwwroot /s/q
mkdir c:\data\code\raven\server\AyaNova\wwwroot
cd c:\data\code\raven\docs\8.0\ayanova
mkdocs build
cd c:\data\code\raven-client\ayanova
call npm run build
xcopy c:\data\code\raven-client\ayanova\dist\* C:\data\code\raven\server\AyaNova\wwwroot\ /e
pause
```
This does:
1. Clears and recreates the backend's wwwroot folder
2. Builds the MkDocs documentation (output goes directly to wwwroot/docs via mkdocs.yml config)
3. Runs `npm run build` which calls `vue-cli-service build`
4. Copies the built frontend from `dist/` to the backend's `wwwroot/`
### Known Build Warnings (Safe to Ignore)
These warnings appear during every build and do not affect the output:
1. **Browserslist warning**: "caniuse-lite is outdated"
- Harmless - just means browser compatibility database is old
2. **Dart Sass division warnings**: "Using / for division is deprecated"
- Come from Vuetify's stylesheets, not your code
- Will continue working until Dart Sass 2.0
3. **Asset size warnings**: Various files exceed 244 KiB
- Expected with Monaco editor (ts.worker.js is 4.5MB)
- Application still works correctly
### NPM Scripts
```json
{
"serve": "vue-cli-service serve", // Development server
"build": "vue-cli-service build", // Production build
"lint": "vue-cli-service lint" // Code linting
}
```
---
## Documentation Build Environment
### MkDocs Configuration
From `mkdocs.yml`:
| Setting | Value |
|---------|-------|
| Theme | Material |
| Output directory | `../../../server/AyaNova/wwwroot/docs` |
| Site URL | https://ayanova.com/docs/ |
### Required Python Packages
```bash
pip install mkdocs mkdocs-material
```
The `pymdownx` extensions (highlight, superfences) come bundled with mkdocs-material.
### Build Command
```bash
cd c:\data\code\raven\docs\8.0\ayanova
mkdocs build
```
Output goes directly to the backend's wwwroot/docs folder.
---
## Backend Build Environment
### .NET Version
- **Target Framework**: .NET 8.0
- **Project Type**: ASP.NET Core
### Build Tools Required
- .NET 8.0 SDK
- Visual Studio 2022 or VS Code with C# extension
### Build Outputs
The backend build produces multiple deployment targets:
1. Linux hosted (DigitalOcean service)
2. Windows networked (IIS hosted)
3. Windows single-user (self-contained)
4. Linux server
5. Linux desktop
Windows installers are created with Inno Setup.
---
## Recreating the Build Environment
### Option 1: Native Installation (Windows)
1. Install Node.js 12.22.9 specifically:
- Download from https://nodejs.org/dist/v12.22.9/
- Or use nvm-windows: `nvm install 12.22.9 && nvm use 12.22.9`
2. Install Python 3.11:
- Download from python.org
- Ensure it's in PATH
3. Install MkDocs:
```bash
pip install mkdocs mkdocs-material
```
4. Install .NET 8.0 SDK:
- Download from https://dotnet.microsoft.com/download
5. Clone/checkout repository from SVN
6. Install frontend dependencies:
```bash
cd c:\data\code\raven-client\ayanova
npm ci
```
Note: Use `npm ci` (not `npm install`) to install exact versions from package-lock.json
### Option 2: Docker (Recommended for Reproducibility)
See `Dockerfile.frontend` and `Dockerfile.docs` in this repository for containerized builds that don't require any local installation except Docker.
---
## Troubleshooting
### "fibers" fails to install
This native module requires:
- Python 2.7 or 3.x
- Visual Studio Build Tools (Windows) or build-essential (Linux)
- Specific Node.js version compatibility
If it fails, you can try removing it (it's optional):
1. Remove `"fibers": "^4.0.3"` from package.json
2. Remove the `fiber: require("fibers")` line from webpack.config.js
3. Run `npm install` again
The build will be slightly slower but should work.
### Node version mismatch
If you see errors about incompatible Node versions:
1. Check your Node version: `node -v`
2. It must be 12.22.9 for guaranteed compatibility
3. Use nvm to switch versions if needed
### "Module not found" errors
Try:
```bash
rm -rf node_modules
rm package-lock.json
npm install
```
---
## Future Modernization Notes
When time permits, consider:
1. **Remove fibers dependency** - It's optional and causes most build issues
2. **Upgrade to Vue 3 + Vite** - Much faster builds, better tooling, active support
3. **Migrate to TypeScript** - Better IDE support and fewer runtime errors
4. **Update Node to LTS** - Current LTS is Node 20.x
These are significant changes that should be done incrementally with thorough testing.
---
## Version History
| Date | Change |
|------|--------|
| 2026-02-01 | Initial documentation created |

100
CLAUDE.md Normal file
View File

@@ -0,0 +1,100 @@
# AyaNova v8 — instructions for Claude Code
Loaded in every session started in this repo, and imported by the CLAUDE.md files in
`raven-client` and `raven-test-integration`. Keep it short: detail lives in `devdocs/`.
Statements marked **[unverified]** came from older notes and have not been checked against
the code. Check one before relying on it, then fix the statement or remove the marker.
## What this is
AyaNova v8: commercial service management software (ayanova.com), sold since 1998,
built and maintained by one developer, John. It is a production product. Hosted
subscription customers and self-hosted customers on Windows and Linux run this code, and
deployed clients call its API routes.
## Repositories
Separate Git repos under `C:\data\code\`, hosted on a self-hosted Gitea. The build scripts
hardcode these paths: do not move or rename the folders.
| Folder | What | Stack |
|---|---|---|
| `raven` | **This repo, the master.** Back-end server, customer manual, build and installer scripts | ASP.NET Core 8, C#, EF Core 8, Npgsql, PostgreSQL |
| `raven-client` | Front end, in `ayanova/` | Vue 2.6, Vuetify 2.6, Vuex, Vue Router, plain JavaScript, Node 12.22.9 |
| `raven-test-integration` | API integration tests against a running server | xUnit, .NET 8 |
| `raven-launcher` | Windows standalone launcher | .NET 8, self-contained, trimmed |
| `ravenqbi` | QuickBooks Desktop integration | .NET Framework 4.8 WinForms, x86, Visual Studio only |
| `sockeye` | Internal licensing server, a near-copy of this codebase, **already ported to .NET 10** (tag `8.1.0`) **[unverified: folder name]** | same as `raven` |
Cypress E2E tests (Cypress 9.7.0, targeting remote devtest servers) also exist
**[unverified: which folder]**.
## Layout of this repo
- `server/AyaNova/` — the application, `AyaNova.csproj`. Publish this csproj directly, never `raven.sln`.
- `docs/` — the **customer** help manual (MkDocs). Not developer documentation. Do not edit unless asked.
- `devdocs/modernization/` and `devdocs/build/` — current developer docs. Anything else in `devdocs/` is old historical notes: ignore unless asked.
- `dist/` — packaging assets, Inno Setup scripts, build outputs.
- `build-release.bat` — master release build.
## Current work: .NET 10 port, branch `net10-port`
Move the server from .NET 8 to .NET 10 and every back-end package to its current release.
Front end: minimal or no change.
**Before any port work, read `devdocs/modernization/NET10-PORT.md`.** It holds the plan,
the checklist and the decisions still open. Update its checklist and log in the same
commit as the change they record.
Sockeye went through the same port first. When a port question comes up, look at how
Sockeye solved it: `git -C ..\sockeye log --oneline net6-baseline..8.1.0`.
The rest of the modernization plan: `devdocs/modernization/ROADMAP.md`.
## Working rules
- No change to production code without John's explicit approval. Propose a plan first for anything beyond a one-line fix.
- One concern per commit, so a regression can be found with `git log` and undone with `git revert`. The version bump is always the first commit on a release branch.
- Do not mix refactoring, bug fixes and features. If you spot something worth refactoring during the port, add it to "Deferred" in `NET10-PORT.md` instead of doing it.
- Dependency changes: back-end packages only, and only on `net10-port`. No front-end dependency changes (Node is pinned at 12.22.9 because of `fibers`).
- Do not add `[ApiController]` to controllers that lack it. It changes model binding and error responses on routes deployed clients call.
- No new features.
- Git: keep it simple and explicit. Do not push, tag, merge or rewrite history unless asked. Tags are annotated and pushed by name.
- Nothing is accepted blindly: show the diff and say why.
- Secrets and customer data never go in a repo. Fixtures that contain customer data live outside the repo and are found through an environment variable.
## Environment
- Windows 11, PowerShell, VS Code. Visual Studio only for `ravenqbi`.
- .NET 10 SDK installed alongside the .NET 8 runtime **[unverified: check `dotnet --info`]**.
- Local server: `http://localhost:7575`, API base `/api/v8/`.
- Compile-time constant `SUBSCRIPTION_BUILD` (`-p:SUBSCRIPTION_BUILD=true`) distinguishes the hosted build; use `#if SUBSCRIPTION_BUILD`.
## Commands
```powershell
dotnet build server\AyaNova\AyaNova.csproj
dotnet run --project server\AyaNova
# integration tests: the server must be running first
cd ..\raven-test-integration; dotnet test raven-integration.csproj
# hosted (subscription) Linux build, as build-release.bat does it
dotnet publish server\AyaNova\AyaNova.csproj -c Release -r linux-x64 --no-self-contained -p:SUBSCRIPTION_BUILD=true
# full release: docs, client, all five packages, installers
.\build-release.bat
```
## Reference (read when relevant, not every session)
| File | Covers |
|---|---|
| `devdocs/modernization/NET10-PORT.md` | The current port: plan, checklist, open questions, log |
| `devdocs/modernization/ROADMAP.md` | Modernization steps and their status |
| `devdocs/modernization/PROJECT-OVERVIEW.md` | Products, deployment targets, business context |
| `devdocs/modernization/CODEBASE-REFERENCE.md` | Stack, structure, configuration, API conventions |
| `devdocs/modernization/TESTING.md` | Test suites and their state |
| `devdocs/build/BUILD.md` | Release build, packaging, version numbers |
| `devdocs/build/DEV-VM-SETUP.md` | Rebuilding the Windows build VM from scratch |

134
devdocs/build/BUILD.md Normal file
View File

@@ -0,0 +1,134 @@
# AyaNova v8 release build
How a full release is built: tools, folder layout, what `build-release.bat` does, and where
version numbers live. Written for .NET 8 (8.2.4); the .NET 10 port updates it.
To set up a machine that can run this from scratch, see `DEV-VM-SETUP.md`.
## What gets built
| Project | Framework | Output |
|---|---|---|
| AyaNova server (`raven/server/AyaNova`) | .NET 8 | Five packages, below |
| Launcher (`raven-launcher`) | .NET 8, self-contained, `PublishTrimmed` | Inside the Windows standalone installer |
| QBI (`ravenqbi/AyaNovaQBI`) | .NET Framework 4.8 | `ayanova-qbi-setup.exe`, built separately |
| Package | Build |
|---|---|
| `ayanova-subscription-linux-x64-server.zip` | linux-x64, framework-dependent, `SUBSCRIPTION_BUILD=true`, server `config.json` |
| `ayanova-linux-x64-server.zip` | linux-x64, framework-dependent, server `config.json` |
| `ayanova-linux-x64-desktop.zip` | Same build, desktop `config.json` |
| `ayanova-windows-x64-single-setup.exe` | win-x64 self-contained server, launcher, embedded PostgreSQL |
| `ayanova-windows-x64-lan-setup.exe` | win-x64 framework-dependent server |
## Tools
| Tool | Version | Used for |
|---|---|---|
| .NET SDK | 8.0.x | Server and launcher |
| Node.js | **12.22.9 exactly** | Front end. Newer versions break `fibers` |
| Python | 3.11 | MkDocs |
| MkDocs + mkdocs-material | current | Customer manual (`pip install mkdocs mkdocs-material`) |
| Inno Setup | 6.x | Windows installers |
| 7-Zip | current | Linux zips |
| Visual Studio | **[unverified: 2022 or 18/2026]** | QBI only |
The scripts hardcode these paths:
- `C:\data\code\raven\`, `C:\data\code\raven-client\ayanova\`, `C:\data\code\raven-launcher\`, `C:\data\code\ravenqbi\`
- `C:\Program Files\7-Zip\7z.exe`
- `C:\Program Files (x86)\Inno Setup 6\ISCC.exe`
## Folder layout
```
C:\data\code\
├── raven\
│ ├── build-release.bat master build script
│ ├── raven.sln
│ ├── server\AyaNova\ AyaNova.csproj, wwwroot\ (generated), resource\
│ ├── docs\8.0\ayanova\ customer manual, mkdocs.yml
│ ├── docs\8.0\customer\ customer portal docs, mkdocs.yml
│ ├── devdocs\ developer docs (this file)
│ ├── dist\
│ │ ├── assets\ LICENSE, license.rtf, logo.ico, lan-install-config.json,
│ │ │ linux-server\config.json, linux-desktop\config.json
│ │ ├── install\windows\x64\ standalone.iss, lan.iss
│ │ ├── win-x64\postgres-standalone\ embedded PostgreSQL (maintained by hand)
│ │ └── installers\ final packages (generated)
│ │ (also generated: linux-x64\, subscription-build-linux-x64\, win-x64\ayanova\..., win-x64\launcher\)
│ └── graphics\
├── raven-client\ayanova\ Vue front end
├── raven-launcher\ raven-launcher.csproj, config.json
└── ravenqbi\ AyaNovaQBI\AyaNovaQBI.csproj, install\qbi.iss
```
## `build-release.bat`, step by step
1. **Clean** `raven\server\AyaNova\wwwroot` and `raven\dist\installers`.
2. **Customer manual**: `mkdocs build` in `docs\8.0\ayanova` and `docs\8.0\customer`. Output goes straight to `wwwroot\docs` (`site_dir` in `mkdocs.yml`).
3. **Front end**: `npm run build` in `raven-client\ayanova`, then `xcopy dist\* ...\raven\server\AyaNova\wwwroot\ /e`. The .NET build does not produce `wwwroot`: whatever is there is what ships.
4. **Subscription Linux**:
```bat
dotnet publish --property:PublishDir=C:\data\code\raven\dist\subscription-build-linux-x64\ -c Release -r linux-x64 --no-self-contained -p:SUBSCRIPTION_BUILD=true
```
Zipped with `assets\linux-server\config.json`.
5. **Perpetual Linux**: the same with `-p:SUBSCRIPTION_BUILD=false` into `dist\linux-x64\`. One build, two zips: desktop and server `config.json`.
6. **Windows standalone**: `-r win-x64 --self-contained` into `dist\win-x64\ayanova\standalone\` (includes the .NET runtime, about 150 MB more).
7. **Windows framework-dependent**: `-r win-x64 --no-self-contained` into `dist\win-x64\ayanova\framework-dependent\`. Needs the .NET runtime on the target.
8. **Launcher**: `dotnet publish -c Release -r win-x64 --self-contained` in `raven-launcher` into `dist\win-x64\launcher\`.
9. **Installers**: `ISCC.exe standalone.iss` and `ISCC.exe lan.iss`.
`raven-client\ayanova\buildrelease.bat` runs steps 1–3 alone (main manual only, not the
customer portal docs).
### Windows installers
- **Standalone** (`standalone.iss`): self-contained server, launcher, embedded PostgreSQL, empty database template, desktop shortcut to the launcher. App in `{autopf}\ayanova`; data in `{commonappdata}\ayanova\database`; logs in `{commonappdata}\ayanova\logs`. The launcher starts PostgreSQL, starts the server, opens the browser at the login page and waits.
- **LAN** (`lan.iss`): framework-dependent server plus network config. No PostgreSQL. Prompts the user to download the ASP.NET Core Hosting Bundle and PostgreSQL.
### QBI (separate)
```bat
cd C:\data\code\ravenqbi\AyaNovaQBI
msbuild /p:Configuration=Release
cd C:\data\code\ravenqbi\install
"C:\Program Files (x86)\Inno Setup 6\ISCC.exe" qbi.iss
```
Output: `ravenqbi\install\output\ayanova-qbi-setup.exe`. Has a pre-build event that runs a
timestamp utility.
## Version numbers
Set in five places for every release:
| File | Setting | At 8.2.4 |
|---|---|---|
| `raven\server\AyaNova\AyaNova.csproj` | `<Version>` | 8.2.4 |
| `raven\dist\install\windows\x64\standalone.iss` | `#define MyAppVersion` | 8.2.4 |
| `raven\dist\install\windows\x64\lan.iss` | `#define MyAppVersion` | 8.2.4 |
| `raven-client\ayanova\package.json` | `"version"` | 8.2.4 |
| `ravenqbi\install\qbi.iss` | `#define MyAppVersion` | 8.0.2 (own cycle) |
## Front-end build notes
- Install dependencies with `npm ci`, never `npm install`: it installs the exact versions in `package-lock.json`.
- Warnings that appear on every build and are harmless: Browserslist "caniuse-lite is outdated"; Dart Sass "Using / for division is deprecated" (from Vuetify); asset size warnings (Monaco's `ts.worker.js` is about 4.5 MB).
- Webpack content-hash file names change on every build even when contents do not. Compare file contents, not names.
## Docker builds
`Dockerfile.frontend` and `Dockerfile.docs` build the front end and the manual in containers
**[unverified: where they live]**. The Linux server packages and the Windows
framework-dependent build could also build in Docker. Windows self-contained builds, Inno
Setup, QBI and the embedded PostgreSQL need Windows.
## Troubleshooting
- **`fibers` fails to install**: it needs Python, Visual Studio Build Tools, and Node exactly 12.22.9. Last resort: remove `"fibers"` from `package.json` and the `fiber: require("fibers")` line from `webpack.config.js`; the build still works, a little slower.
- **Node version errors**: `node -v` must print `v12.22.9`.
- **"Module not found"**: delete `node_modules`, then `npm ci`.
- **PuppeteerSharp cannot download the browser**: check connectivity, or point the server at an installed browser through its browser-path setting.
- **`SUBSCRIPTION_BUILD` not applied**: the flag is `-p:SUBSCRIPTION_BUILD=true` on `dotnet publish`.
- **Inno Setup errors**: an earlier step failed, or 7-Zip / Inno Setup are not at the expected paths.

View File

@@ -1,7 +1,8 @@
# AyaNova v8 Build VM Setup Guide
# AyaNova v8 build VM setup
> **Purpose**: Step-by-step instructions to create a portable Windows virtual machine containing the complete AyaNova v8 build environment. This VM can run on Windows or Linux hosts.
test for commit
Written February 2026 for the .NET 8 build. The .NET 10 port adds the .NET 10 SDK (see `../modernization/NET10-PORT.md`, section 5).
---
## Overview
@@ -237,7 +238,7 @@ Using winget (run in PowerShell as Administrator):
# 7-Zip
winget install --id 7zip.7zip -e
# Git (useful even if using SVN)
# Git
winget install --id Git.Git -e
# VS Code
@@ -294,15 +295,7 @@ This is needed for the QuickBooks Integration project (.NET Framework 4.8, Windo
5. Click **Install**
6. Wait (this takes 20-40 minutes and downloads several GB)
### 4.7 Install TortoiseSVN (if using SVN)
```powershell
winget install --id TortoiseSVN.TortoiseSVN -e
```
Reboot after installation (adds shell integration).
### 4.8 Verify All Installations
### 4.7 Verify All Installations
Open a new PowerShell window and run:
@@ -360,29 +353,21 @@ All items should show versions or [OK].
## Part 5: Set Up Source Code
### 5.1 Check Out from SVN
If your SVN server is accessible from the VM:
### 5.1 Clone from Gitea
```powershell
cd C:\data\code
# Check out main repository
svn checkout https://your-svn-server/path/to/raven raven
# Check out frontend
svn checkout https://your-svn-server/path/to/raven-client raven-client
# Check out launcher
svn checkout https://your-svn-server/path/to/raven-launcher raven-launcher
# Check out QBI
svn checkout https://your-svn-server/path/to/ravenqbi ravenqbi
git clone <gitea-url>/raven.git raven
git clone <gitea-url>/raven-client.git raven-client
git clone <gitea-url>/raven-launcher.git raven-launcher
git clone <gitea-url>/ravenqbi.git ravenqbi
```
The folder names must be exactly these: the build scripts hardcode the paths.
### 5.2 Alternative: Copy from Host via Shared Folder
If SVN isn't accessible, use VirtualBox shared folders:
If Gitea isn't reachable from the VM, use VirtualBox shared folders:
1. In VirtualBox: **Devices → Shared Folders → Shared Folders Settings**
2. Click the folder+ icon
@@ -524,10 +509,10 @@ When to re-export:
```powershell
cd C:\data\code\raven
svn update
git pull
cd C:\data\code\raven-client
svn update
git pull
# etc.
```
@@ -545,7 +530,6 @@ Options:
1. **Shared Folders**: Copy to a shared folder visible on host
2. **Network**: If VM is bridged, access via network share
3. **USB Drive**: Attach a USB drive to the VM
4. **SVN Commit**: Commit build outputs to repo (not recommended for large binaries)
### 8.5 Snapshots for Safety
@@ -582,19 +566,6 @@ ThinkPads are generally excellent Linux laptops. The X13 Gen 3 AMD should work w
- **SSD**: VM performance is heavily dependent on disk speed; SSD is essential
- **RAM**: 32GB on host would be more comfortable than 16GB
### 9.4 Used ThinkPad Recommendations
If you're looking at a used/refurbished ThinkPad for Linux:
| Model | Era | Notes |
|-------|-----|-------|
| X1 Carbon Gen 9/10 | 2021-2022 | Excellent, lightweight |
| T14s Gen 2/3 | 2021-2022 | Good value, business-class |
| T480/T480s | 2018 | Older but very well supported, cheap |
| P14s | 2020+ | More RAM options (up to 48GB) |
Look for 32GB RAM models if possible. Check that the specific CPU is supported (Intel generally has better Linux support than AMD, though AMD works fine too).
---
## Appendix A: Troubleshooting
@@ -620,10 +591,10 @@ If `npm ci` fails with native module errors:
- Check that 3D acceleration is enabled
- Use SSD, not spinning disk
### Can't Access SVN Server
### Can't Reach Gitea
- Check VM network mode (NAT vs Bridged)
- Verify SVN server is accessible from host
- Verify Gitea is reachable from the host
- Check firewall settings on both host and VM
---
@@ -665,7 +636,7 @@ msbuild C:\data\code\ravenqbi\AyaNovaQBI\AyaNovaQBI.csproj /p:Configuration=Rele
|------|------|
| 7-Zip | `C:\Program Files\7-Zip\7z.exe` |
| Inno Setup | `C:\Program Files (x86)\Inno Setup 6\ISCC.exe` |
| MSBuild | `C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe` |
| MSBuild | `C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe` **[unverified]**: 6.3 was changed to `18` (Visual Studio 2026) while 4.6 still says install VS 2022. Confirm which is installed |
---
@@ -674,3 +645,4 @@ msbuild C:\data\code\ravenqbi\AyaNovaQBI\AyaNovaQBI.csproj /p:Configuration=Rele
| Date | Change |
|------|--------|
| 2026-02-01 | Initial guide created |
| 2026-10-22 | SVN steps replaced with Git; moved to `devdocs/build/` |

View File

@@ -0,0 +1,132 @@
# AyaNova v8 codebase reference
Technical facts about the v8 code, gathered from notes written in February–March 2026.
Version numbers are a snapshot from then: **`AyaNova.csproj` and `package.json` are the
truth**. Anything marked **[unverified]** has not been checked against the code.
## Back end — `raven/server/AyaNova/`
### Stack (February 2026, before the .NET 10 port)
| Component | Version | Notes |
|---|---|---|
| .NET / ASP.NET Core | 8.0 | |
| Entity Framework Core | 8.0.8 | |
| Npgsql.EntityFrameworkCore.PostgreSQL | 8.0.4 | |
| Microsoft.AspNetCore.Authentication.JwtBearer | 8.0.4 | Custom JWT implementation |
| PuppeteerSharp | 17.0.0 | PDF reports through headless Chrome |
| MailKit | 4.5.0 | Email |
| Swashbuckle.AspNetCore | 6.5.0 | OpenAPI |
| NLog.Web.AspNetCore | 5.3.11 | Logging |
| Asp.Versioning.Mvc | — | API versioning **[unverified]** |
### Structure
```
raven/server/AyaNova/
├── AyaNova.csproj
├── Program.cs, Startup.cs
├── resource/ report templates and static resources, copied to output
│ └── rpt/ report templates (.ayrt)
└── wwwroot/ built front end and customer manual, copied in by the build
```
Older notes also list `Controllers/`, `Models/`, `Services/` and `Data/` folders
**[unverified]**: that list reads like a generic template, not the actual layout.
Data-list classes live in the `AyaNova.DataList` namespace, and their class names are the
list keys (`WorkOrderDataList`, `CustomerDataList` …).
### Hosted vs self-hosted
```xml
<DefineConstants Condition=" '$(SUBSCRIPTION_BUILD)' == 'true' ">$(DefineConstants);SUBSCRIPTION_BUILD</DefineConstants>
```
```csharp
#if SUBSCRIPTION_BUILD
// hosted-subscription-only code
#endif
```
### Reports
PuppeteerSharp renders PDFs in headless Chrome, downloaded on first run. A customer
setting holds extra browser launch parameters, and an environment variable can point at
an existing browser instead of downloading one (`REPORT_RENDER_BROWSER_PATH` in older
notes **[unverified]**).
### Configuration
Older notes list `ConnectionStrings__DefaultConnection`, `JWT_SECRET` and
`ASPNETCORE_ENVIRONMENT` as the server's settings **[unverified]**. Sockeye, the near-copy,
reads `SOCKEYE_*` keys from a `config.json` beside the app (`SOCKEYE_DB_CONNECTION`,
`SOCKEYE_USE_URLS`, `SOCKEYE_JWT_SECRET`, `SOCKEYE_DATA_PATH` …), so AyaNova likely does the
same with its own prefix. The Linux packages ship a `config.json` from
`dist/assets/linux-server/` or `dist/assets/linux-desktop/`, which supports that.
### API conventions
Confirmed by the integration tests. Base `http://localhost:7575/api/v8/`.
| Call | Response |
|---|---|
| POST | 201, `{ data: <full object> }` |
| GET | 200, `{ data: <full object> }` |
| DELETE | 204, no body |
| PUT on Customer, QuoteItem, PMItem | 200, `{ data: { Concurrency: N } }` (concurrency only) |
| PUT on Quote, PM, Contract, WorkOrder headers | 200, `{ data: <full object> }` |
| Concurrency conflict | 409 |
| Referential integrity violation | 400, `error.code = 2200` |
| Not found | 404, `error.code = 2010` |
Saved data-list filters: `/data-list-filter` (not the older `data-list-view`); list with
`GET /data-list-filter/list?ListKey=WorkOrderDataList`.
## Front end — `raven-client/ayanova/`
### Stack
| Package | Version | Notes |
|---|---|---|
| vue | 2.6.14 | End of life December 2023 |
| vuetify | 2.6.6 | |
| vue-router | 3.5.4 | |
| vuex | 3.6.2 | plus `vuex-persistedstate` 2.7.1 |
| @vue/cli-service | 4.5.15 | |
| webpack | 4.46.0 | |
| sass / sass-loader | 1.52.3 / 8.0.2 | |
| fibers | 4.0.3 | Deprecated native addon; the reason Node is pinned |
| monaco-editor | 0.30.1 | Large worker files |
| others | | chart.js 2.9, luxon 1.28, marked 1.2, dompurify 2.3, jwt-decode 3.1, papaparse 5.3 |
Node.js **12.22.9**, pinned. Plain JavaScript, no TypeScript.
### Structure
```
raven-client/ayanova/
├── src/
│ ├── api/ API utilities, auth, error handling
│ ├── components/ ~80 components
│ ├── views/ ~100 page components
│ ├── plugins/ Vuetify configuration
│ ├── App.vue, main.js, router.js, store.js
├── public/
├── package.json, vue.config.js, webpack.config.js
```
View prefixes: `acc-` accounting (rates, taxes), `adm-` administration, `cust-` customers,
`home-` dashboard and user home, `inv-` inventory, `ops-` server operations, `svc-` service
(work orders, quotes, PMs).
## QuickBooks integration — `ravenqbi/`
.NET Framework 4.8, Windows Forms, x86 only (the QuickBooks SDK is 32-bit). References
`interop.QBFC14.dll`, which needs the QuickBooks SDK installed on the build machine.
Visual Studio is required for the Forms designer. Not part of `build-release.bat`.
## Customer manual — `raven/docs/8.0/`
MkDocs with the Material theme, two sites: `ayanova/` (main manual) and `customer/`
(customer portal). Each `mkdocs.yml` sets `site_dir` to build straight into the server's
`wwwroot/docs`.

View File

@@ -0,0 +1,179 @@
# 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` only if the client changes) |
| 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:
```powershell
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.Cryptography` 2.x goes with MailKit/MimeKit 4.x, because MimeKit 3.x pulls in `Portable.BouncyCastle` and the assemblies clash.
- Publish `server\AyaNova\AyaNova.csproj` directly, 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
- [ ] `raven` and `raven-client` clean on `main` and pushed
- [ ] Annotated tag `net8-baseline` on the pre-port commit in `raven` and `raven-client`, pushed by name (`git push origin net8-baseline`), checked with `git ls-remote --tags origin`
- [ ] Branch `net10-port` created 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-integration` against 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 `alicense` fixture 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 with `SOCKEYE_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-matched `Microsoft.*` package to 10.x in the same step
- [ ] **Hosting model.** `WebHost`, `IWebHost` and `WebHostBuilder` are obsolete in .NET 10 (ASPDEPR008, ASPDEPR004). `Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(web => web.UseStartup<Startup>())` keeps the `Startup` class unchanged
- [ ] **Asp.Versioning.** Older notes say AyaNova already uses `Asp.Versioning.Mvc` **[unverified]**. If not: rename from `Microsoft.AspNetCore.Mvc.Versioning`; `ApiVersion` moves to `Asp.Versioning`; wiring is `services.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.Kind` is 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
- [ ] **BouncyCastle.** If AyaNova references `BouncyCastle`, it becomes `BouncyCastle.Cryptography` 2.x, moved together with MailKit/MimeKit 4.x. In Sockeye, `PemReader` still yields an `AsymmetricCipherKeyPair` for a PKCS#1 key
- [ ] **Swashbuckle 10 / OpenAPI.NET 2.** `Microsoft.OpenApi.Models` merged into `Microsoft.OpenApi`. Bearer requirement becomes `c.AddSecurityRequirement(document => new OpenApiSecurityRequirement { [new OpenApiSecuritySchemeReference("Bearer", document)] = [] })`. No explicit `Microsoft.OpenApi` reference
- [ ] **NLog 6.** `ArchiveNumbering` is gone; `NLogBuilder.ConfigureNLog` is replaced by `NLog.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.NewtonsoftJson` 10.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.DefaultChromiumRevision` is gone: call `DownloadAsync()` with no argument
- `IJSHandle.ExecutionContext` is gone: evaluate on the handle. Evaluate results are `JsonElement?`
- The default download is Chrome for Testing, into `Chrome/` and `ChromeHeadlessShell/` 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-sandbox` on 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 of `Headers.Append`
- [ ] **MiniProfiler.** Confirm it is absent: `git grep -i -e miniprofiler -e mini-profiler -e StackExchange.Profiling` in **both** `raven` and `raven-client`
- [ ] **BinaryFormatter.** Confirm it is unused, or that `EnableUnsafeBinaryFormatterSerialization` is 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 9
- [ ] **`InvariantGlobalization`.** 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 `UseUrls` beats `ASPNETCORE_URLS`. Find where AyaNova takes its URLs from (Sockeye: `SOCKEYE_USE_URLS` in `config.json`) and make sure the documented setting is the one that wins
- [ ] **Logging providers.** `appsettings.json` very likely sets the Debug and Console providers to `Trace`, as Sockeye's does. Check whether `Program.cs` calls `ClearProviders()` before adding NLog. If it does not, `Trace` floods 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.
- [ ] **Case 4676: data-list SQL injection — priority.** The numeric and role filters in `DataListSqlFilterCriteriaBuilder` built 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_dump` call.** If it builds a `postgresql://user:password@host:port/db` URI with the raw password and runs it through a shell for a `> file` redirect, a password with spaces or `@ : / ? # %` breaks backups. Fix: password in `PGPASSWORD` in the process environment, connection parameters as separate arguments through `ProcessStartInfo.ArgumentList` (no shell), output with `-f`
- [ ] **Drop-and-recreate database.** Without `CREATEDB` it 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 only `TypeError … 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 use `ZeroSeconds`, not `MaxSeconds`; date `NotEqual` should exclude the whole minute (`< ZeroSeconds OR > MaxSeconds`). In DEBUG builds, `DataListField.UiFieldDataType` is read before `DataListField` is 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 from `clientTimeStamp.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 as `postgres` for years and never exercised either bug in section 3
- [ ] **Restore with a dedicated role.** `createdb -O <role>`, then `pg_restore --no-owner --no-privileges --role=<role>`. Without `--role`, a superuser restore leaves every table owned by `postgres`, and the integrity check, which counts through `information_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: `ldd` on the downloaded browser binary for missing libraries (install exactly what is reported; several Ubuntu 24.04+ names carry a `t64` suffix); the sandbox under the service account; no orphaned `chrome` processes 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.bat` produces 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.0` is 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.iss` prompts 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
- [ ] 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.md` and `devdocs/build/BUILD.md`
- [ ] Final gate runs on the finished code
- [ ] Merge, per repo:
```powershell
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-ff` keeps a merge commit, so the whole port can be backed out with `git 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-port` locally and on the remote
---
## Open questions
- Release version number (Sockeye's port was released as 8.1.0)
- `raven-launcher` (.NET 8, self-contained): move to .NET 10 in the same release?
- `raven-test-integration`: move to `net10.0` (separate commit) or leave on `net8.0`? It only talks HTTP to the server
- Does the client need a rebuild in this pass, or can its current output be carried across?
- 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 |
|---|---|---|
| | | |

View File

@@ -0,0 +1,71 @@
# AyaNova project overview
Background for anyone, human or AI, working on this code: what the products are, who runs
them where, and the development setup.
## Business
Ground Zero Tech-Works Inc. sells AyaNova, commercial service management software
(https://ayanova.com), directly to customers online since 1998. One person, John, does all
development, support and administration.
Revenue comes from three streams:
- **Hosted subscriptions**: AyaNova v8 run by Ground Zero on DigitalOcean droplets. Built with `SUBSCRIPTION_BUILD=true`
- **Self-hosted v8 licences** with yearly support and update subscriptions. Customers install it on their own Windows or Linux machines
- **v7 legacy support**: maintenance only
Practical consequence for code changes: a change can reach customer-run servers that
Ground Zero cannot see or update, and deployed clients cannot be pushed an update. API
routes, licence handling and database behaviour have to stay compatible.
## Products
| Product | Stack | Status |
|---|---|---|
| **AyaNova v8** | ASP.NET Core 8, C#, PostgreSQL; Vue 2 front end | Active. Customers are slowly moving to it from v7 |
| **AyaNova v7** | Windows desktop app, C#, 20+ years old, Firebird database | Sunset: maintenance only. Depends on discontinued commercial components. Builds in an isolated Windows 7 VirtualBox VM. Customers were told in 2022 it may become unsupportable |
| **Sockeye** | Same stack as v8, a near-copy of its code | Internal: licensing, customer database, cases (history back to FogBugz). Ported to .NET 10 in October 2026 |
| **AyaNova QBI** | .NET Framework 4.8, Windows Forms | QuickBooks Desktop integration for v8. Separate release cycle, rarely changes |
## v8 deployment targets
`build-release.bat` produces five packages; QBI is built separately.
| Target | Package | Runtime |
|---|---|---|
| Hosted subscription, Linux | `ayanova-subscription-linux-x64-server.zip` | Framework-dependent |
| Self-hosted Linux server | `ayanova-linux-x64-server.zip` | Framework-dependent |
| Self-hosted Linux desktop (single user) | `ayanova-linux-x64-desktop.zip` | Framework-dependent |
| Windows standalone (single user) | `ayanova-windows-x64-single-setup.exe` | Self-contained, embedded PostgreSQL, launcher |
| Windows LAN server | `ayanova-windows-x64-lan-setup.exe` | Framework-dependent; customer installs the ASP.NET Core Hosting Bundle and PostgreSQL |
| QuickBooks integration | `ayanova-qbi-setup.exe` | .NET Framework 4.8 |
Hosted customers run on DigitalOcean Ubuntu droplets. They are updated by hand weekly:
Ground Zero's own test droplet first, then customers through the day by time zone.
## Development environment
- **Workstation:** Beelink SER9 Pro+, 32 GB, Windows 11 Pro. Backup: ThinkPad X13 Gen 3
- **Editors:** VS Code for everything except QBI, which needs Visual Studio for the Windows Forms designer
- **Build VM:** a complete Windows 11 build environment in VirtualBox, exported and backed up to several drives (`devdocs/build/DEV-VM-SETUP.md`)
- **Source control:** Git on a self-hosted Gitea on DigitalOcean. Moved from SVN in early 2026
- **Location:** off-grid, Starlink internet, solar power. The workstation is not left running overnight in winter, so do not plan work that depends on overnight runs on it
### Pinned tool versions
| Tool | Version | Why |
|---|---|---|
| Node.js | 12.22.9 | Newer versions break `fibers` |
| Python | 3.11 | MkDocs for the customer manual |
| .NET SDK | 8.0.x, plus 10.x for the port | Server, launcher |
| .NET Framework | 4.8 | QBI only |
| Inno Setup | 6.x | Windows installers |
| 7-Zip | current | Linux zips |
## Working style
- Reliability over speed; patient with build times
- Keep all historical data: cases, emails, old releases
- Careful about backups and disaster recovery
- Changes are reviewed before they are committed; nothing is accepted blindly

View File

@@ -0,0 +1,44 @@
# Modernization roadmap
The order of the modernization work and where each step stands. Steps are lettered as in
the original plan (February–March 2026). Statuses are as of October 2026; any marked
**[unverified]** were not recorded anywhere recent and need John to confirm.
| Step | What | Status |
|---|---|---|
| — | SVN → Git, self-hosted Gitea | ✅ Done |
| — | Build process documented; Docker builds for front end and docs; Windows build VM exported and backed up | ✅ Done |
| A1 | E2E tests with Cypress 9.7.0, targeting remote devtest servers, with version validation and environment-based config | **[unverified]**: the March notes listed it as both done and in progress |
| A2 | Fix the outdated integration tests in `raven-test-integration` | ✅ Done, March 2026: compiles on `net8.0`, broken tests removed or rewritten |
| A3 | Data-list filter and sort integration tests | ✅ Done, March 2026, together with Tier 1–3 gap coverage. February notes still called them stubs; the March notes supersede that **[unverified]** |
| — | Fix the three server bugs the integration suite is waiting on: case 4653, case 4654, the contract issue | Open |
| ▶ | **.NET 10 port of the back end** | **Current**: `NET10-PORT.md` |
| B | Git baseline: tag plus refactor branch | **[unverified]**. The port's `net8-baseline` tag covers the tag half |
| C | Architecture review | Not started |
| D | Refactor: remove redundant code, simplify, improve performance. No behaviour changes | Not started |
| E | Lower-level tests: unit and integration | Not started |
| F | Remaining dependency updates (see below) | Back end: in progress as the .NET 10 port. Front end: not started |
| G | New features, from the case database backlog | Not started |
## Why the .NET 10 port comes before step D
It pulls the back-end half of step F forward. Sockeye, which shares most of this codebase,
was ported first, so the work is mapped out and comes with a list of fixes found along the
way. The integration suite from A2/A3 is the regression gate for it, as it will be for D.
## Step F, front end (not part of the current port)
In this order, each step tested before the next:
1. Remove `fibers`. It is optional for `sass-loader`; builds get a little slower
2. Move Node from 12.22.9 to a current LTS
3. Update the safe dependencies
4. Vue 2 → Vue 3 and Vuetify 2 → Vuetify 3
5. Webpack → Vite
6. Optional: Options API → Composition API; TypeScript for new files only
## Before step D starts
1. The integration suite passes, apart from the three known server bugs
2. Fix those three bugs
3. Then refactor, letting the tests catch regressions

View File

@@ -0,0 +1,37 @@
# Testing
What test suites exist, what they protect, and the rules for adding to them.
## Suites
| Suite | Where | What it covers | State |
|---|---|---|---|
| API integration tests | `raven-test-integration` | Tier 1–3 API coverage against a running server | Green apart from three known server bugs (March 2026). Details in that repo's `CLAUDE.md` |
| E2E | Cypress 9.7.0 **[unverified: which folder]** | User workflows in a browser, against remote devtest servers, with version validation and environment-based config | **[unverified]** |
| Licence verification gate | To be written (`NET10-PORT.md`, section 0) | Every stored v8 licence still verifies | Not started |
| Unit tests | none | — | Roadmap step E |
The old regression and load-test project was disabled as flaky; nothing runs it.
## Critical paths
These must never break. Coverage is checked against this list before refactoring:
1. Authentication and authorization (JWT, roles, 401/403)
2. Customer management
3. Work order creation, status flow, through to invoicing
4. Quotes, PMs and contracts
5. Scheduling
6. Inventory and parts
7. Reports
8. Data lists: filtering, sorting, saved filters, paging
9. Licence verification
## Rules
- **Prove a gate on the unmodified code** before changing anything, so a later failure points at the change, not at the test.
- **Fix or delete a broken test.** Never leave it failing without a recorded reason. A known failure waiting on a server fix is recorded with its case number.
- **Do not test what cannot be seeded.** Tests that need IDs not known at test time (such as warehouse IDs for stock levels) are left out and listed as such.
- **`data-testid` attributes** for anything Cypress selects, so tests do not depend on CSS classes.
- **Fixtures with customer data stay outside the repo**, found through an environment variable.
- New tests go where they protect the most: back-end API first, then browser workflows.