Release notes

Every published version of the WUIC Framework, newest first, with the full notes of each release.

Back to downloads

v1.7.21

Back to index

Previous published version: 1.7.20 (1 October 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


A release focused on security and data access. It closes the findings of a complete security review of the framework and brings OData to the same permission level as the CRUD. In short:

  • enableCookieAuthentication accepts a third value, "signed": cookie signed by the server, several sessions per user, logout per single session;
  • OData applies route and column permissions and the user's row constraints, and writes through the framework CRUD;
  • personal tokens let Power BI, Excel and scripts connect to the OData endpoints;
  • the "secure upload" flag of upload columns protects the column's files again;
  • edit and delete honour the per-user restriction in the UI CRUD as well;
  • errors show the user a translated message with a tracking code, technical details only to the superadmin.

Metadata schema changes are applied automatically at startup. The features were verified with end-to-end tests on SQL Server, MySQL, PostgreSQL and Oracle, in the three enableCookieAuthentication modes.


🔐 Authentication and sessions

  • Three modes. enableCookieAuthentication is false, true or "signed". With false the user is whoever the browser cookie declares: development only. A [security] message reports it at startup, the AppSettings editor describes it and asks for confirmation on save. An unrecognised value counts as "signed", with a warning in the log.
  • "signed". The server signs the k-user cookie (HMAC-SHA256, expiry inside the signature): a tampered or expired cookie counts as missing. The same user can have several open sessions, each with its own identifier.
  • Logout and revocation. Logout closes only the session it comes from, copies of the cookie included. An administrator logging out a user, a password reset and a password change close all of that user's sessions in their company.
  • Maximum lifetime. With signedSessionMaxLifetimeHours (default 24, 0 = no limit) a signed session expires at that age from login, even when used continuously.
  • Signing key. cookie-signing-key is generated on first start and saved in appsettings.json. At startup the log shows the key fingerprint (never the key) and warns if the key was not saved.
  • Calls verified on the server. With false the cookie no longer carries forged privileges (administrator, role, company): the user is reloaded from the database.

🔗 OData

  • Permissions. Every entity set requires read permission on the route. A column denied to the user and named in $select, $filter, $orderby or $expand returns 403; otherwise it comes back empty.
  • Row constraints. The query starts from the same SELECT as the CRUD, with the per-user, per-role or per-company restriction, the default filter and logical delete. $filter, $orderby, $skip, $top and $count are applied outside and can only narrow: or 1 eq 1 does not widen the result.
  • $expand. Allowed towards readable routes without row constraints or logical delete; 403 otherwise.
  • Writes. POST, PATCH and DELETE go through insertRecord, updateRecord and deleteRecord: permissions, non-editable columns, triggers, logging fields, change log, workflows and logical delete apply as from the UI. A row hidden from the user returns 404.
  • Multi-company. The data connection is the user's company one, as in the CRUD.
  • Discovery. /odata, /odata/$metadata and /odata/openapi.json require a session. With odataPublicMetadata=true they are public again; the data stays protected.
  • Personal tokens. With apiTokensEnabled=true every user creates from the user menu ("API tokens") wuic_pat_… tokens with a name, an expiry (default 90 days, maximum apiTokenMaxLifetimeDays) and read-only or read-write permission. They work only on /odata, with the owner's permissions. Power BI and Excel use them as the password with "Basic" credentials; scripts with Authorization: Bearer. The token is shown only once; the superadmin sees and revokes everyone's.

🛡️ Security

Best-effort hardening across the whole surface: checks on the values that end up in queries (sorting, operators, aggregates, keys, numeric filters) aligned across the four providers; upload and report paths confined to their folders; administration functions (report designer, report removal and scaffolding, column reordering, restart, OData scaffold) reserved to the superadmin verified on the database; workflow emails only from users who can run the workflow; sample data of the AI-assistant tools only to the superadmin and never with credential columns; guards on users and roles bound to the table, not to the route name; licence public key embedded in the package.

📎 Upload

  • "Secure upload" per column. The files of a column with upload_secure can be read only with a valid session and with the column visible to the user, both from /upload and from /api/UploadImage. The files of the other columns stay public. Columns that store the file in the database are protected by /api/UploadImage.
  • File names. Names with inner dots (invoice-acme.com.pdf) are accepted; names with server-executable extensions stay rejected even in the middle (photo.aspx.png).

🚦 Errors

The user gets a translated message with a tracking code ("Copy code" button in the error dialog). The same code is stored with the full stack in _error__logs. SQL, stacks and internal messages reach only the superadmin. Translations of the new error messages are added automatically at startup.

🐛 Notable bug fixes

  • Per-user restriction on edit and delete. The CRUD UPDATE and DELETE contained only the key: knowing the id, a user could edit another user's row. Now the row must be one the user can see, otherwise 403 errors.auth.route_read_forbidden.
  • Default filter with OR filters. With the OR operator the route's default filter disappeared and the grid showed the rows it was meant to hide. Now it stays in AND.
  • Oracle. Booleans written as 1/0 also on columns with a boolean UI type, in filters and in stored-procedure parameters; functions told apart from procedures in the right schema; OData on the data schema even with the SYSTEM user (before, intermittent ORA-00942 errors).
  • PostgreSQL and Oracle. Inserting with an identity key not sent by the client no longer fails; reports apply the user's permissions and filters as on SQL Server and MySQL.
  • First start under IIS. The initial configuration no longer returns a 500 after a successful installation: migrations complete when the worker restarts.
  • Spreadsheet and synchronisation. With an active filter, edit and delete hit the wrong row; a selection beyond the page no longer reaches the records of later pages; the kanban batch save and offline synchronisation retry only what failed.
  • Menu. The bar appears with labels already translated, without briefly showing the keys.

📦 Updated packages

Package From To
WuicCore 1.7.20 1.7.21
Wuic.Webcore 1.7.20 1.7.21
WuicOData 1.7.20 1.7.21
RuntimeEfCore 1.7.20 1.7.21
Wuic.MySqlProvider 1.7.20 1.7.21
Wuic.PostgresProvider 1.7.20 1.7.21
Wuic.OracleProvider 1.7.20 1.7.21
wuic-framework-lib (npm) 1.7.20 1.7.21

🔧 Recommended operational updates when upgrading

  1. Cookie mode: in production set enableCookieAuthentication to "signed" (or true); false is for development only.
  2. Several instances behind a load balancer: copy the same cookie-signing-key to every instance; with different keys a session is valid only on the instance that created it. Check in the log that the fingerprint matches.
  3. OData clients: clients that read $metadata without a session must authenticate, or set odataPublicMetadata=true. OData writes now run the CRUD triggers and logging and, on routes with logical delete, set the flag instead of deleting the row.
  4. md_service_apply_default_filter: deprecated. The default filter always applies, OData included.
  5. Licence: the machine fingerprint override is set only with the WUIC_LICENSE_MACHINE_FINGERPRINT environment variable; the license-machine-fingerprint-override and license-public-key-pem keys in appsettings.json are ignored.
  6. API tokens: to connect Power BI, Excel or scripts set apiTokensEnabled=true (and optionally apiTokenMaxLifetimeDays) from the AppSettings editor.

v1.7.20

Back to index

Previous published version: 1.7.18 (1 October 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


A maintenance release: it collects the defects found while building an application with the five documented development patterns, starting from the sample project of the source packages, and speeds up the RAG engine on machines without a GPU. It also contains the fixes of 1.7.19, which was released only as NuGet and npm packages: these notes cover both. In short:

  • running scaffolding again with "Create Menu" no longer duplicates the menu entry;
  • on PostgreSQL and Oracle the server-side sorting requested by the grid is applied, also when the list brings a highlighted record to the top;
  • with ng serve, exports can be downloaded from the dev server too;
  • RAG engine searches on CPU are up to about three times faster on machines with few cores;
  • the pages of the five development patterns have code samples that compile, in all five languages.

No changes to metadata, translations or database schema: the upgrade requires no scripts.


🗄️ PostgreSQL and Oracle providers

  • Server-side sorting. With server-side operations enabled, the sort chosen on a grid column never reached the query: records always came back ordered by primary key, even when descending order on another field was requested. The ORDER BY now uses the requested columns and appends the primary key only as the last criterion, as on SQL Server and MySQL.
  • Sorting with a highlighted record. When the list brings a specific record to the top, the requested sort was still discarded in 1.7.19. The order is now: highlighted record, requested columns, primary key.

🤖 RAG engine

  • Faster searches on CPU. Without a GPU, the model compute threads kept spinning between one operation and the next, and the background cache warm-up ran in parallel with the first search: on machines with few cores they competed for the CPU. The threads no longer busy-wait and model computations run one at a time within the process. Searches (chat and MCP tools) are up to about three times faster on machines with few cores, and the first search is no longer slowed down by the warm-up in progress. On PCs with spare cores the timings do not change.

🐛 Notable bug fixes

  • Duplicate menu entry on scaffolding. Running "Scaffold Table" or "Scaffold View" again with "Create Menu" on a table or view that was already scaffolded added a second menu entry for the same route. The entry is now created only if the route does not have one yet.
  • Exports with the dev server. In the client project of the source packages, proxy.conf.js did not forward the /Tmp_export path to the backend: with ng serve the link to the exported file (Excel, CSV, PDF) returned the application page instead of the file. The path is now forwarded and the download works as in the published installation.

📚 Documentation

The pages of the five development patterns have been corrected in every language:

  • Framework + manual: the sample passes the data source with [hardcodedDatasource] and sets [autoload]="true"; without autoload the list loads only the schema, not the records.
  • Framework data + custom component: new sample of setCurrent, addNewRecord, syncData and fetchData called from the custom component.
  • Framework component + custom data: client-side filters build one filter per column, the way the list-grid reads it.
  • Full custom: the component declares the required imports (TableModule, CheckboxModule, ButtonModule, FormsModule).
  • Full autogeneration: the spreadsheet requires the licensed feature (without it, the route opens as a list); the dashboard is not a route archetype and the page no longer lists it among the generated pages.

The documentation is included in the npm library and published on the website.

📦 Updated packages

Package From To
WuicCore 1.7.18 1.7.20
Wuic.Webcore 1.7.18 1.7.20
WuicOData 1.7.18 1.7.20
RuntimeEfCore 1.7.18 1.7.20
Wuic.MySqlProvider 1.7.18 1.7.20
Wuic.PostgresProvider 1.7.18 1.7.20
Wuic.OracleProvider 1.7.18 1.7.20
wuic-framework-lib (npm) 1.7.18 1.7.20

If you already upgraded the packages to 1.7.19, 1.7.20 adds sorting with a highlighted record, the faster RAG engine and the fix to the full autogeneration pattern page.

🔧 Recommended operational updates when upgrading

  1. Projects created from the source packages: the project's proxy.conf.js is not overwritten by the package upgrade. Add the '/Tmp_export' entry with the same configuration as the other backend entries (or copy it from the package's proxy.conf.js), then restart ng serve.
  2. Duplicate menus: if running scaffolding again with an earlier version left duplicate menu entries, delete the extra ones from the menu management.
  3. PostgreSQL and Oracle: no action needed; after the upgrade, grids with server-side operations sort as requested.
  4. RAG engine: no action needed; the updated engine is in the package's rag-engine folder and the fix applies to CPU execution.

v1.7.18

Back to index

Previous published version: 1.7.17 (30 September 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


A maintenance release: it collects the defects found while installing 1.7.17 from scratch on clean machines, with all four supported databases. In short:

  • the MCP search tools no longer time out while the RAG engine is loading;
  • on PostgreSQL the RAG engine loads on its own again after startup;
  • on Oracle XE 21c table and view scaffolding writes the metadata again, including at first run;
  • stack traces sent by crash reporting are reconstructed in full.

No changes to metadata, translations or database schema: the upgrade needs no scripts.


⚠️ Behavior changes

  • MCP tools while the engine is loading. wuic_codebase_search and wuic_ask wait up to 30 seconds (previously 60) for the RAG engine to be ready; if it is still not ready they reply with an error asking to retry in about 60 seconds. Loading continues on the backend, and the repeated call finds the engine ready.

🤖 RAG engine

  • MCP tool timeouts. While the engine was loading, the MCP server started a warm-up search that ran in parallel with the real search: on a machine without a GPU the two slowed each other down and the call exceeded the client's 120-second wait, on Windows too. The MCP server now only starts the loading and waits for it, and the real search gets the client's full time.
  • /api/Rag/Health always starts loading. The endpoint starts loading the engine in the background even when it finds no administrator user to notify about the progress: in that case the engine loads without notifications.
  • PostgreSQL. The administrator lookup failed on PostgreSQL, where isAdmin is a boolean field: the engine never started from /api/Rag/Health and the MCP tools stayed "loading". The administrator is now found and the engine starts as on the other databases.

🗄️ Oracle provider

  • Scaffolding on Oracle XE 21c. Recent versions of the Oracle driver send true/false values as the BOOLEAN type, which Oracle before 23 rejects (ORA-00932). Table and view scaffolding (from the interface and at first run) replied with an error, and the first run ended without the metadata of the application tables. Metadata flags are now written as 0/1, which the NUMBER columns of the metadata tables accept on every Oracle version.

🐛 Notable bug fixes

  • Crash reporting, unreadable stack traces. With CrashReporting:Enabled=true the stack traces sent by installations were not reconstructed on the receiver: the framework's internal names stayed unreadable and crash analysis suffered. From 1.7.18 reports are reconstructed in full. No change to configuration or to the data sent.

📦 Updated packages

Package From To
WuicCore 1.7.17 1.7.18
Wuic.Webcore 1.7.17 1.7.18
WuicOData 1.7.17 1.7.18
RuntimeEfCore 1.7.17 1.7.18
Wuic.MySqlProvider 1.7.17 1.7.18
Wuic.PostgresProvider 1.7.17 1.7.18
Wuic.OracleProvider 1.7.17 1.7.18
wuic-framework-lib (npm) 1.7.17 1.7.18

🔧 Recommended operational updates when upgrading

  1. Coding assistants with the WUIC MCP server: on an existing installation the workspace holds the previous copy of scripts/mcp/wuic-rag-mcp.mjs. Replace it with the one in the package (llm-workspace/templates/app-llm-workspace/scripts/mcp/) or regenerate the workspace as described in llm-workspace/README.md, then restart the MCP client.
  2. Oracle XE 21c: if a first run with 1.7.17 or earlier ended without the metadata of the application tables, repeat the scaffolding of those tables from the interface after upgrading.
  3. Crash reporting: users need to do nothing; reports from previous versions stay as they are, new ones are readable in full.

v1.7.17

Back to index

Previous published version: 1.7.16 (29 September 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


A maintenance release: it collects the defects found while installing 1.7.16 from scratch on clean Windows and Linux machines. In short:

  • the map no longer loads Google Maps with a fake key when no key has been configured;
  • the RAG engine loads much faster on first use, and can be loaded at startup;
  • the MCP search tools wait for the engine instead of timing out;
  • the AppSettings Editor opens in a few seconds;
  • the list-grid row template no longer breaks on the first direct load of a page.

No changes to metadata, translations or database schema: the upgrade requires no scripts.


⚠️ Behavior changes

  • Google Maps key not configured. The installation value __SET_GOOGLE_MAPS_API_KEY__, if not replaced, now counts as a missing key: the map shows the missing-key notice. Previously the browser loaded Google Maps with that text as the key (InvalidKeyMapError and internal Google errors in the console). This applies to any value of the form __SET_...__ in GoogleMaps:ApiKey.
  • MCP tools while the engine is loading. wuic_codebase_search and wuic_ask wait up to 60 seconds for the RAG engine to be ready; if it is not ready yet they return an error asking to retry in about 60 seconds, instead of letting the client's call time out.

🤖 RAG engine

  • Faster first use. The engine warm-up after loading ran a full search, about 35 reranker passes: on a Linux machine without GPU it accounted for about 133 of the 139 seconds of the cold load. It now runs one embedder pass and one reranker pass on a short text. Search results do not change.
  • Optional loading at startup. New key AppSettings:rag-engine-eager-load (default false). With true, if the models are already downloaded, the backend loads the engine in the background at startup, without notifications, and the first search (chat or MCP) does not pay for the cold load. The cost is the engine's RAM, 4.5-6.5 GB, taken even if RAG is not used. With false the engine loads on the first request, as before.
  • MCP server. The wait for the engine lives in the MCP server that the first run installs in the workspace for coding assistants (scripts/mcp/wuic-rag-mcp.mjs). While waiting, the server itself starts loading the engine on the backend.

🐛 Notable bug fixes

  • AppSettings Editor slow to open. With many keys the editor could take tens of seconds to appear after the server response, because every keystroke or update of a text field re-checked the whole editor and recomputed all the translated labels. Now each text field updates on its own and the labels are computed once: opening drops to a few seconds.
  • List-grid, row template on first load. Opening directly the URL of a page with a list-grid fed by a datasource that responds immediately (custom endpoint, hardcodedDatasource), the data could arrive before the application published the row template imports (gridRowImports). The template compiled without those pipes stayed in cache and the rows did not show (in production, an onDestroy on undefined error). The grid now compiles the row template after the imports are published; if the application never publishes them, it compiles without them after 10 seconds, as before.

📦 Updated packages

Package From To
WuicCore 1.7.16 1.7.17
Wuic.Webcore 1.7.16 1.7.17
WuicOData 1.7.16 1.7.17
RuntimeEfCore 1.7.16 1.7.17
Wuic.MySqlProvider 1.7.16 1.7.17
Wuic.PostgresProvider 1.7.16 1.7.17
Wuic.OracleProvider 1.7.16 1.7.17
wuic-framework-lib (npm) 1.7.16 1.7.17

🔧 Recommended operational updates when upgrading

  1. Maps: check that GoogleMaps:ApiKey holds a real key. If it still contains __SET_GOOGLE_MAPS_API_KEY__, after the upgrade maps show the missing-key notice: this is the expected behavior until the key is set.
  2. RAG on dedicated servers: if you use the chat or the MCP tools right after every restart and have enough RAM, set "rag-engine-eager-load": "true" in AppSettings. On shared machines keep the default.
  3. Coding assistants with the WUIC MCP server: on an existing installation the workspace holds the previous copy of scripts/mcp/wuic-rag-mcp.mjs. To get the engine wait, replace it with the one from the package (llm-workspace/templates/app-llm-workspace/scripts/mcp/) or regenerate the workspace as described in llm-workspace/README.md. A client that receives the retry request should simply repeat the call.

v1.7.16

Back to index

Previous published version: 1.7.15 (28 September 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


This version fixes the defects found by a 100-user stress test on the four databases installed on Linux, and reduces the work the server does for each request. In short:

  • geographic columns are written correctly on Oracle and PostgreSQL, with documented point formats and a 400 error for invalid values;
  • a text longer than the column now answers 400 instead of 500;
  • Oracle is much faster under load;
  • the per-IP login attempt limit is configurable.

How it was verified. 100 virtual users for 10 minutes (lists, filters, sorting, inserts, updates, deletes, import, export, reports) on SQL Server, MySQL, PostgreSQL and Oracle Free, each on Ubuntu 24.04 with nginx: between 11,340 and 11,490 operations per database, 0 HTTP errors and no row in the application's error log. Where an item was verified differently, it says so.


⚠️ Behavior changes

  • Per-IP login limit off by default. Up to 1.7.15 it was fixed at 30 attempts per minute per IP. Now loginRateLimitPerIpPerMinute applies, default 0 (off). The per-user limit (5 wrong passwords in 5 minutes) stays always on. On installations exposed to the internet set 30.
  • Invalid position rejected. A value of a point column in an unrecognized format now answers HTTP 400 errors.input.geo_point.invalid and the record is not saved. Before, SQL Server and MySQL saved NULL without an error, Oracle and PostgreSQL answered 500. An empty value saves NULL as before.
  • Text too long: 400. A text longer than the physical column answers HTTP 400 errors.input.value_too_long (or errors.input.value_too_long.column with args.column, when the database names the column) instead of 500 errors.db.sql_exception.
  • Session last activity. LastActivityDate is updated at most once a minute per user, no longer at every request. The session expiry can move by at most 60 seconds.
  • sys_info cache on every database. Before, only SQL Server kept the row cached for 5 seconds; now MySQL, PostgreSQL and Oracle do too. A change made from outside (SQL by hand, another node) is seen within 5 seconds, and a project_metadata_version increase made by another node now clears the metadata caches on these three databases as well.
  • Oracle, new installations. The first run creates the logon trigger WUIC_SESSION_CURSOR_SHARING in every loaded schema (see the Oracle section).

🛡️ Security

Best-effort hardening: configurable per-IP login attempt limit (loginRateLimitPerIpPerMinute, HTTP 429 response errors.auth.login_rate_limited with retryAfterSeconds) with a list of exempt IPs (loginRateLimitExemptIps, comma-separated IPs, treated like loopback: never limited or counted), both read at every login; on PostgreSQL and Oracle the queries that read users and roles by id, username or email pass the values as parameters instead of concatenating them into the SQL text.

🗺️ Geographic columns

  • Oracle: every insert or update with a point or geometry column set failed (ORA-50028), because the provider wrote SQL Server syntax. Now the value is converted to WKB (SRID 0, the same format as the tutorial data) and passed as a parameter.
  • PostgreSQL: same defect, every insert or update with a position set failed. Now the value is written with ST_GeomFromText (SRID 0).
  • Accepted formats for point columns: JSON {"lat": 45.4642, "lng": 9.19} (also lon/long), pair 45.4642, 9.19 (latitude, longitude), WKT POINT(9.19 45.4642) (longitude, latitude), text Lat: 45.4642, Long: 9.19. The decimal separator is the dot. On read the position always comes back as JSON.
  • Invalid values: HTTP 400 errors.input.geo_point.invalid with the column and the value received. On Oracle geometry columns accept two-dimensional WKT POINT, POLYGON and MULTIPOLYGON; an invalid WKT answers errors.input.geo_wkt.invalid. Messages translated into the 5 languages.

Verification: an end-to-end test on the four databases inserts and updates a position in each of the four formats and reads it back with the expected coordinates, checks that an invalid value is rejected with 400 without touching the data and that an empty value saves NULL.

🗄️ Oracle under load

  • First run: after loading each schema the framework creates the logon trigger WUIC_SESSION_CURSOR_SHARING, which sets CURSOR_SHARING = FORCE for the application's sessions, and gathers the schema statistics (DBMS_STATS.GATHER_SCHEMA_STATS). If either step fails, the error goes to the log and the first run continues.
  • Authentication: the user id is compared with the column without TO_CHAR, so Oracle uses the key index instead of reading the whole users table at every request.
  • Reading spatial columns: the BLOB is converted to JSON or WKT in the application server after the read, no longer by a PL/SQL function executed row by row.
  • MAX key: with concurrent inserts on parent and child tables, the block that computes the key could end in a deadlock (ORA-00060). It now retries up to 3 times.

Measured with the same load, before and after these changes (including those of the next section), on an existing installation where trigger and statistics were applied by hand: database time spent in SQL statements from 794 to 61 seconds, hard parses from 73,719 to 7,379, Oracle CPU at the 95th percentile from 26.5% to 6.4%. At the 95th percentile lists go from 1.6 s to 0.16 s, reading a record from 1.28 s to 68 ms, getTableMetadata from 1.95 s to 0.32 s.

⚡ Less work per request

  • sys_info: the row was read again about 7 times per request on MySQL, PostgreSQL and Oracle (on MySQL 23.7% of the database time in the stress test). Now it is read at most once every 5 seconds per tenant, on every database.
  • LastActivityDate: the UPDATE at every authenticated request was 34.7% of the database time on MySQL. Now it runs at most once a minute per user, on every database; the seconds since the last activity are computed by the database, with its own clock.
  • PostgreSQL: on the first run, after loading the scripts, the framework runs ANALYZE, so the first queries are not planned without statistics. The Linux installer does the same.
  • Reports: every print wrote a "report call" row in the error log (_error__logs). It no longer does.

🐛 Notable bug fixes

  • PostgreSQL, filters on boolean columns: a filter on a native boolean column failed with 42883: operator does not exist: boolean = integer.
  • PostgreSQL, SQL errors in lists: they reach the client as errors.db.sql_exception with the SQLSTATE code, instead of errors.server.unhandled.
  • Change log of deletes and updates: on MySQL the change log of deletes was never written. On MySQL and SQL Server the date was passed as text and the conversion depended on the server language (on SQL Server on Linux it failed at every delete). The date is now a typed parameter.
  • Uploads stored in the database: updating a record with an in-database upload column whose name has upper-case letters failed on PostgreSQL (42703). The column name is now quoted, and the same applies on MySQL and SQL Server.
  • MySQL, default sort: on a table without a key in the metadata the list was sorted by the first column even when spatial or binary, with Out of sort memory. Spatial and binary columns are now excluded.
  • MySQL, metadata indexes: the script that creates the indexes on metadata routes and columns failed at every startup and the indexes were never created. They are now created at startup.
  • Maximum length in the metadata: mc_max_length of nvarchar/nchar columns was stored in bytes, i.e. twice the characters: forms let users type up to twice the characters and the save then failed. Scaffolding now stores characters, and in the first-run scripts the lengths are the physical ones: 127 columns fixed in the SQL Server tutorial (including the tutorial views and system tables such as _mail_recipients, _mailing_lists, _notifications, _wuic_workflow_instance_log and scheduler_execution), 21 in the SQL Server minimal profile, 41 on MySQL, 34 on PostgreSQL and 34 on Oracle. The maximum length check in forms now matches the column.
  • Tutorial, temperature archive: on SQL Server the first page of the Warehouse.ColdRoomTemperatures_Archive list (3.65 million rows) timed out under load. Two indexes created at startup bring it from 5.4 s to 6 ms (measured on SQL Server on Linux). On MySQL, PostgreSQL and Oracle an index on the key is created at startup; there the case was not measured.

🐧 Linux installer

  • nginx: worker_connections 4096, worker_rlimit_nofile 16384, and keep-alive connections to Kestrel instead of a new connection for every request (WebSockets unchanged).
  • Databases: MySQL and PostgreSQL with 300 connections and buffers at 25% of RAM (between 128 MB and 8 GB), Oracle Free with PROCESSES 400 (one container restart during installation). SQL Server stays Express.
  • They are meant for a few hundred users: at 100 users the defaults were still enough (MySQL 59 connections out of 151, Oracle 148 processes out of 200). The stress test described above ran with the previous values; these installer steps have not yet been tested on an installation from scratch.
  • An existing installation keeps the previous values. The commands to raise them by hand are in the README of the Linux tarball, section "Capacity for hundreds of concurrent users".

📦 Updated packages

Package From To
WuicCore 1.7.15 1.7.16
Wuic.Webcore 1.7.15 1.7.16
WuicOData 1.7.15 1.7.16
RuntimeEfCore 1.7.15 1.7.16
Wuic.MySqlProvider 1.7.15 1.7.16
Wuic.PostgresProvider 1.7.15 1.7.16
Wuic.OracleProvider 1.7.15 1.7.16
wuic-framework-lib (npm) 1.7.15 1.7.16

🔧 Recommended operational updates when upgrading

  1. Installations exposed to the internet: set "loginRateLimitPerIpPerMinute": "30" in AppSettings, the fixed value of 1.7.15. For stress tests from a single IP add that IP to loginRateLimitExemptIps.
  2. Clients that handle error codes: a text too long and an invalid position now arrive as HTTP 400 with the codes errors.input.value_too_long, errors.input.value_too_long.column and errors.input.geo_point.invalid. On every database, new installations have the messages of the four new codes (errors.input.geo_point.invalid, errors.input.geo_wkt.invalid, errors.input.value_too_long, errors.input.value_too_long.column) translated into the 5 languages; on an existing installation the fallback message is shown until the translations are added from the interface translations management.
  3. Oracle, existing installations: to get the trigger and statistics of new installations, run as the user of each schema (data and metadata):
    CREATE OR REPLACE TRIGGER WUIC_SESSION_CURSOR_SHARING AFTER LOGON ON SCHEMA
    BEGIN EXECUTE IMMEDIATE 'ALTER SESSION SET CURSOR_SHARING = FORCE';
    EXCEPTION WHEN OTHERS THEN NULL; END;
    /
    BEGIN DBMS_STATS.GATHER_SCHEMA_STATS(ownname => SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA')); END;
    /
    
    To go back to the previous behavior: DROP TRIGGER WUIC_SESSION_CURSOR_SHARING.
  4. Linux with many concurrent users: on an existing installation raise the nginx and database limits with the commands in the README of the Linux tarball.
  5. Tutorial on SQL Server: the first startup after the upgrade builds two indexes on Warehouse.ColdRoomTemperatures_Archive (15-25 seconds each, measured): that startup takes longer, only once.
  6. Several nodes on the same database: changes to sys_info made by one node reach the others within 5 seconds.
  7. Lengths in the metadata, existing installations: existing metadata are not corrected automatically. To align them, re-run the table scaffolding or set the column's mc_max_length to the number of characters.

v1.7.15

Back to index

Previous published version: 1.7.14 (25 September 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


This version fixes the defects found by the load test of 1.7.14 and by a test campaign on clean installations of the four databases (SQL Server, MySQL, PostgreSQL, Oracle). In short:

  • opening a route is about ten times faster under load;
  • MAX keys no longer collide on concurrent inserts, and on SQL Server an export no longer blocks writes;
  • import and export are portable across databases, with defects fixed on Oracle and PostgreSQL;
  • logout on Oracle now really closes the session.

🛡️ Security

Logout on Oracle. On Oracle, logout did not invalidate the session token: after logging out, the same cookie could still read data. Logout now closes the session on Oracle too. Oracle users should upgrade.

⚡ Performance

Metadata when opening a route. Reading the metadata of a route (getTableMetadata) is about ten times faster. Permissions per user, role and company are evaluated without compiling an expression for every column, and serialization reuses its configuration instead of rebuilding it on every call. The result is unchanged: same permissions, same JSON.

On the demo server (SQL Server, 100 concurrent users) the 95th percentile of the call went from 2.4-4.5 s to 0.2-0.4 s, and the 95th percentile of the process CPU from 54% to 31%.

🔑 MAX primary key

On routes with md_primary_key_type = "MAX" the key of a new row is the maximum + 1. Up to 1.7.14 the maximum was read with a query separate from the insert: two users saving at the same instant could get the same key, and the second save failed with a primary key violation.

  • The maximum is now computed inside the insert, with a lock on the table until commit: concurrent inserts get consecutive keys.
  • It also applies to the "dependent key" of composite keys (maximum + 1 for each value of the parent key).
  • It applies to inserts from the form, record duplication and Excel import.
  • The lock mechanism depends on the database: UPDLOCK, HOLDLOCK on SQL Server, SELECT ... FOR UPDATE on MySQL, a transaction advisory lock on PostgreSQL, LOCK TABLE ... IN EXCLUSIVE MODE on Oracle.

An import that inserts new rows into a table with a MAX key holds the lock until the end of the file: meanwhile other inserts on the same table wait, and on Oracle updates and deletes too.

📤 Export on SQL Server

An export reads the table for the whole time it writes the file. With the default SQL Server isolation that read blocked inserts and updates on the same table until the end of the export (measured: an update waiting 5.5 s during the export of 70,000 rows).

  • If the data database has ALLOW_SNAPSHOT_ISOLATION ON, the export reads in snapshot isolation and writes no longer wait (same update: about 100 ms). The file contains the same data.
  • The option applies only to exports: other reads do not change behaviour. It coexists with READ_COMMITTED_SNAPSHOT ON.
  • Without the option the export works as before.
  • The tutorial data database is created with READ_COMMITTED_SNAPSHOT ON, which removes the same blocking from grid reads too.

📥 Import and export across databases

  • Oracle, lookups in the export. In lookup columns the export wrote the key instead of the description (for example 16 instead of the person's name). The file did not import back: "no record of 'people' has this description". The export now writes the description, and the export → edit → import round trip works on Oracle as on the other databases.
  • PostgreSQL, import. Every import that looked up existing rows by key failed with 42883: operator does not exist: integer = text. The key read from the file is now converted to the column type.
  • Portable headers. The key column of a lookup (key + description mode) and the parentheses that tell apart two columns with the same label now use the column name in the WUIC metadata, the same on every database, instead of the physical name (on Oracle in upper case, for example CONTACTPERSONID). A file exported from one database imports into another. Files exported before, with the physical name, import as before.

🌐 "Data translations" page

The admin page for record translations (_record_field_translations) now shows the saved translations, with table and user names, on all four databases.

  • MySQL: the list answered with a database error.
  • Oracle: the list failed with ORA-00942, because the user and metadata tables live in another schema. The framework now qualifies the schema and grants the data user the required SELECT on first use.
  • PostgreSQL: the list read the wrong table and did not show the saved translations. It now reads the one in the data database. Since PostgreSQL does not join tables of different databases, user and table names are read with a separate query. Sorting and grouping on these columns work on the key.

🗄️ Oracle

  • Reinstalling over an existing database. In the first run, confirming to recreate the database failed with ORA-01940 if the user still had open sessions. Sessions are now closed and awaited before DROP USER.
  • Tutorial metadata. The Oracle tutorial metadata is aligned with SQL Server: lookup descriptions and other column properties. It applies to new tutorial installations.

🐛 Notable bug fixes

  • SQL code editor: on MySQL, PostgreSQL and Oracle the suggestions for schemas, tables and columns came back empty. They are now complete.
  • Data cache: with cacheDataMinutes enabled, the cached row count never expired, and an expired entry was never cached again. Rows and counts now expire after the configured time.
  • Pivot: saving the same pivot configuration a second time answered 500.
  • Grouping (SQL Server): server-side grouping without aggregates answered 500.
  • Windows installer (IIS): in some cases the application started before the site was configured and stayed in error until the pool was recycled. The installer now recycles the pool before starting the site.

📦 Updated packages

Package From To
WuicCore 1.7.14 1.7.15
Wuic.Webcore 1.7.14 1.7.15
WuicOData 1.7.14 1.7.15
RuntimeEfCore 1.7.14 1.7.15
Wuic.MySqlProvider 1.7.14 1.7.15
Wuic.PostgresProvider 1.7.14 1.7.15
Wuic.OracleProvider 1.7.14 1.7.15
wuic-framework-lib (npm) 1.7.14 1.7.15

🔧 Recommended operational updates when upgrading

  1. SQL Server, exports without blocking: run once ALTER DATABASE [<data database>] SET ALLOW_SNAPSHOT_ISOLATION ON. The option increases tempdb usage while transactions are open.
  2. Large imports on busy MAX key tables: put the key in the file, or switch the table to IDENTITY/SEQUENCE, so the lock is not held for the whole import.
  3. "Data translations" page on existing MySQL, PostgreSQL and Oracle installations: in the table metadata, the _record_field_translations route must use connection DataSQLConnection and must not be a system route. New installations already have it.
  4. Oracle: upgrade, if only for the logout fix.

v1.7.14

Back to index

Previous published version: 1.7.13 (24 September 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


This version comes from a load test with many concurrent users on a public installation: export and import of the same file, filters, sorting, reports. Export is four times faster and goes through a queue that protects the server; import reads back the files produced by export correctly, including lookups resolved from a repeated description. Two defects of grids with lookups and geographic columns are fixed as well.


📤 Export

  • Faster and lighter. An XLSX export of 230,000 rows drops from 28.5 s to 7.4 s and from 12.2 GB to 2.3 GB of allocated memory. The file content does not change.
  • Export queue. The server runs at most maxConcurrentExports exports at the same time (default: half the cores, at least 1). The others wait in arrival order, up to maxQueuedExports (default 10). Beyond that, the request answers 503 errors.metaservice.export.queue_full.
  • Visible wait. While the export is queued, the progress dialog and the notification bell show "Queued: position N". A queued export can be cancelled.
  • Settings applied immediately. Both keys are in the settings editor (App Runtime section) and are read at every export, without a restart.
  • Unique file name. Two exports of the same route started in the same second no longer write the same file (previously all but one answered 500).

📥 Import

Lookup columns in the file: fkey_mode. An importable route chooses how its lookup columns appear in the file, with import.fkey_mode in the route props bag:

  • description (default): the description, as in the grid;
  • key: the key value;
  • both: both, the description under the column title and the key under the physical name.

The import dialog shows the preselected choice and lets you change it for a single import. Export follows the same choice, so an exported file can be imported back as it is. use_descriptive_fkey is still accepted from clients that do not know fkey_mode.

Headers. Every column has its title as header. Only when two columns of the same file have the same title does the physical column name appear in parentheses.

Rules for lookups read from the description.

  • Unique description: its key is used.
  • Description shared by several records: on update the record keeps its current key, if it is one of the possible ones; otherwise the row is rejected with the list of candidate keys.
  • Key and description in the same file that do not match: the row is rejected.

Other fixes.

  • Excel date cells are read as dates: a file exported and imported back without changes is no longer rejected.
  • A primary key that is also a lookup is resolved before the record existence check.
  • The existence check uses SQL parameters instead of concatenating the file values.

🐛 Notable bug fixes

  • Two lookups to the same table: the second column showed an empty description. Every foreign key now has its own alias in the query, on all databases.
  • Geographic columns: sorting the grid by a geography column failed on SQL Server (error 249). In the first-run scripts the geographic columns have mc_disable_sorting enabled, so the grid does not offer sorting on them.
  • Available languages: GetSupportedLanguages returns the languages of the lingue table.
  • Reports: reportQueryTimeout is optional, with 120 seconds when missing; the key is in the appsettings.json of the packages.
  • First-run scripts: sequences and identities start after the loaded data, so the first insert does not collide with an existing key. In the tutorial, Countries, DeliveryMethods, People, StateProvinces, Customers and Invoices generate the key themselves instead of asking for it in the form.
  • Oracle: identities are realigned at startup.

📦 Updated packages

Package From To
WuicCore 1.7.13 1.7.14
Wuic.Webcore 1.7.13 1.7.14
WuicOData 1.7.13 1.7.14
RuntimeEfCore 1.7.13 1.7.14
Wuic.MySqlProvider 1.7.13 1.7.14
Wuic.PostgresProvider 1.7.13 1.7.14
Wuic.OracleProvider 1.7.13 1.7.14
wuic-framework-lib (npm) 1.7.13 1.7.14

🔧 Recommended operational updates when upgrading

  1. Size the export queue: every full export of a large table uses about one core and 300 MB while writing. On a server shared with other applications set maxConcurrentExports below the default (for example 2).
  2. Importable routes: if your files contain keys and not descriptions, set "import": { "fkey_mode": "key" } in the route props bag; with both the export also produces the key columns.
  3. Installations with the tutorial already loaded: the first-run scripts only apply to new installations. To remove sorting from the existing geographic columns run on the metadata database UPDATE _metadati__colonne SET mcdisablesorting = 1 WHERE LOWER(mc_db_column_type) IN ('point', 'geography', 'geometry') and clear the metadata cache.
  4. Clients reading progress from the notification WebSocket: the export progress message has the new queuePosition field, present while the export waits in the queue.

v1.7.13

Back to index

Previously published version: 1.7.12 (20 September 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


This version is mainly a security release. An audit of the entire API surface found methods and controllers reachable without login, or by a user without administration rights, and session data taken from the request instead of the server. All of them were closed, with an automated test for each case. Alongside them come the fixes that emerged from the PostgreSQL, MySQL and Oracle test cells, and the completion of multi-tenant support.

Upgrading is recommended for all installations. Read the final section "Recommended operational updates": a couple of behaviors change.


🛡️ Security

AsmxProxy calls closed by default. Every method reachable from /api/Meta/AsmxProxy/{service}.{method} now requires a valid session, except those declared anonymous. Three attributes in the WEB_UI_CRAFTER.Helpers namespace govern access:

  • [AsmxAnonymous]: method callable without login (login, me, translations, registration, password reset);
  • [AsmxAdmin]: reserved for administrators (superadmin role);
  • [AsmxFirstRun]: anonymous only during first run, then reserved for administrators.

The proxy also only invokes the service classes (MetaService, scaffolding, the application's services in WEB_UI_CRAFTER.ProjectData.Servizi): a fully qualified class name from another namespace is rejected.

Session verified everywhere. Best-effort hardening across all session handling:

  • the k-user cookie is validated on the server for the PostgreSQL and Oracle providers as well (token, expiry, session replaced by a new login);
  • the user for personal settings, menu and pivot is the one from the session, not the one indicated in the request;
  • logout closes only its own session;
  • in multi-tenant, the cookie's superadmin flag counts only if the database confirms it;
  • the user list no longer returns session tokens and IPs.

The controller endpoints are aligned too:

  • the appsettings.json editor, OData, upload and report check session and role on the server;
  • uploads stay within the record's folder;
  • the designer accepts only the project's own .css sheets;
  • the administration functions for webhooks, metrics and license are reserved for administrators.

Notifications. The REST and WebSocket endpoints for notifications are tied to the session's user: a request for another user gets 403 errors.auth.notification_forbidden. The WebSocket also works behind a reverse proxy, thanks to X-Forwarded-For.

Registration off by default. It only turns on with registrationEnabled=true in appsettings.json and never assigns an admin or superadmin role: default-role-id must point to an existing role with no administration rights.

First run.

  • The password chosen in the wizard for the administrator is applied even if the name matches a user that already exists.
  • The MySQL and Oracle first-run scripts no longer contain the automated tests' users.
  • The dedicated wuic_assistant user (WUIC Assistant, MCP server) is created with a password generated for each installation, saved in scripts/mcp/wuic-assistant.credentials.json (excluded from git).

🤖 RAG and WUIC Assistant

  • /api/Rag/Chat requires login; /api/Rag/Query remains without login.
  • The /api/Rag/MetadataDetail details that return data (sample_records, lookup_value, db_*) are reserved for administrators.
  • The LLM key configured on the server is never sent to a provider or address chosen by the caller.
  • The wuic-rag MCP server opens the session by itself when needed, with WUIC_USER/WUIC_PASSWORD or with the wuic_assistant user's credentials file.

🏢 Multi-tenant

  • Separate per-tenant caches for menu, table and column permissions, styles, available routes and data cache: a tenant no longer sees another tenant's entries.
  • Propagating a table to the tenants invalidates the cache of every destination tenant, so the new entry appears immediately in the menus.
  • Per-tenant notifications.
  • PostgreSQL support.

🗄️ Database providers

  • PostgreSQL: pagination, data cache, empty many-to-many filters, themes, dates with any host.
  • MySQL: counts on distinct selects, system constraints, provider loading on Linux.
  • Oracle:
    • pagination, geographic filters (area and distance), record restriction by user and role, stored procedure names, booleans on numeric columns, grouping on long text, data cache;
    • much faster translations: from 3-4.6 s to 0.3-0.7 s;
    • correct number of updated and deleted rows;
    • complete geographic data in the first-run scripts (all states and countries);
    • order approval workflow demo;
    • timeline column names aligned with the other databases.
  • All: typed optimistic concurrency error (409 errors.validation.optimistic_concurrency), single upload path, notification creation dates in UTC (automatic migration), correct role restrictions for users with multiple roles.

🐛 Notable bug fixes

  • Record navigation via URL: moving from one record to another in edit or detail mode, the dialog reloads the new record instead of showing the previous one.
  • data-record-loaded goes back to false when the dialog reloads the record, so automated tests do not read stale data.
  • Geographic filter waits for Google Maps to load before drawing.
  • Scheduler list applies the template before reloading the data.

📦 Updated packages

Package From To
WuicCore 1.7.12 1.7.13
Wuic.Webcore 1.7.12 1.7.13
WuicOData 1.7.12 1.7.13
RuntimeEfCore 1.7.12 1.7.13
Wuic.MySqlProvider 1.7.12 1.7.13
Wuic.PostgresProvider 1.7.12 1.7.13
Wuic.OracleProvider 1.7.12 1.7.13
wuic-framework-lib (npm) 1.7.12 1.7.13

🔧 Recommended operational updates

  1. Custom services called before login: the methods of your services in WEB_UI_CRAFTER.ProjectData.Servizi that must respond without a session need to be marked [AsmxAnonymous]; without it, they respond 401 errors.auth.unauthenticated.
  2. Registration: if you use it, set registrationEnabled=true and check that default-role-id points to a role with no administration rights.
  3. wuic_assistant user on existing installations: the old default password is no longer accepted. Set a new one from an administrator and write it into scripts/mcp/wuic-assistant.credentials.json (or in the WUIC Assistant extension settings).
  4. Oracle and MySQL installations with tutorials up to 1.7.12: check the users table and remove the users wuic_e2e_admin, wuic_e2e_admin_2, wuic_e2e_admin_3 and guest_1, if present.
  5. Tools that read data from /api/Rag/MetadataDetail or used /api/Rag/Chat without login: they now need to authenticate (for data, with an administrator).

v1.7.12

Back to index

Previously published version: 1.7.11 (20 September 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


This version actually closes the reports that printed an empty page on MySQL and PostgreSQL. The two previous versions declared that defect fixed by correcting the query: the query was not the problem, and the report kept coming out with its header and not a single row. The real cause was found by comparing, on the same installation, a report shipped in the package and one built on the spot from that installation's own columns: both printed blank, so the problem was not in the report file.


📊 Reports print their data on MySQL and PostgreSQL

A Stimulsoft report keeps two distinct things in its dictionary: the connection to the database and the type of the data source. When a report was opened, the framework replaced the connection with the one of the engine in use — StiMySqlDatabase, StiPostgreSQLDatabase — but left the data source with the type it had been created with, StiSqlSource, which to Stimulsoft means SQL Server.

A SQL Server source sitting on a MySQL database raises no error: it simply returns an empty dataset. The report was drawn correctly — header, frame, "page 1 of 1" — and printed no row.

Converting the source to the engine's own type already existed in the product, but for Oracle only, in two separate places (the viewer and the designer). It now covers MySQL and PostgreSQL in both. If you have reports that printed blank, there is nothing to regenerate: just open them again.

🔑 Metadata indexes on MySQL

On MySQL installations created by the guided first start, the migration that creates the indexes on the metadata hot paths failed with Fatal error encountered during command execution.

The reason is in the connection string the wizard writes: without Allow User Variables=True the MySQL driver reads every @name in a script as a parameter instead of a server variable. The framework's normal queries do use parameters, so they worked; that migration uses server variables, and died. The key is now added to the metadata connection, both when the wizard composes it and when the backend rewrites it.

🧩 Dynamic query on PostgreSQL

The PostgreSQL gateway called the function that builds dynamic queries passing it 19 arguments, while the PostgreSQL satellite accepts 15: the call always failed with MissingMethodException. The arguments are now the right ones, and that gateway's error message also says how many arguments were passed and how many versions of the method exist — because "method not found", on its own, sends you looking for a method that is in fact there.

📦 Updated packages

Package From To
WuicCore 1.7.11 1.7.12
Wuic.Webcore 1.7.11 1.7.12
WuicOData 1.7.11 1.7.12
RuntimeEfCore 1.7.11 1.7.12
Wuic.MySqlProvider 1.7.11 1.7.12
Wuic.PostgresProvider 1.7.11 1.7.12
Wuic.OracleProvider 1.7.11 1.7.12
wuic-framework-lib (npm) 1.7.11 1.7.12

🔧 Recommended operational updates

  1. No configuration change required: the appsettings.json keys are unchanged.
  2. If you have reports that printed blank on MySQL or PostgreSQL, open them again: there is nothing to regenerate.
  3. On MySQL installations already in service, if you want the index migration to succeed, add Allow User Variables=True to MetaDataSQLConnection in appsettings.json. New installations get it from the guided first start. On MySQL the impact is limited: two of the three indexes are already covered by the route's unique constraint and by the index InnoDB creates on its own for foreign keys.

v1.7.11

Back to index

Previously published version: 1.7.10 (19 September 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


This version closes two defects that 1.7.10 declared fixed and which kept happening anyway: scaffolded reports printing an empty page, and scaffolding a table from the interface answering with an error. They were found by the same round of installs on clean machines that produced the previous version, repeated this time against the published product to check the fixes. They still affect those who do not use SQL Server.


📊 Scaffolded reports really print their data on MySQL and PostgreSQL

1.7.10 introduced the rewriting of the query of reports generated by the framework, because the .mrt that ships inside the package carries the identifiers of the engine it was created on. That rewriting never ran, though: it looked the metadata table up by the name of the dictionary database — which is always Connessione — instead of the name of the data source, which is the route. Finding no table by that name, it returned silently, rewrote nothing and left no trace, and the report kept printing the page with its header and not a single row.

The route is now read from the right place, and every case where the rewriting cannot happen — no table by that name, table with no columns — leaves a line in the backend log. An empty report with no explanation is exactly what made this defect hard to see.

If you have generated reports that printed blank on MySQL or PostgreSQL, there is nothing to regenerate: just open them again.

🧱 Scaffolding a table from the interface on Oracle

On Oracle, generating a table from the interface answered HTTP 500 with ORA-00001: unique constraint ... violated on the menu table, and the route was never created.

It is the same defect fixed for PostgreSQL in the previous version, left uncovered on Oracle. The schema declares the menu key as GENERATED BY DEFAULT AS IDENTITY, but the framework inserts its own system entries with the key computed by hand: the generator does not advance, and the first row that relies on it asks for a key that is already taken. Realigning the generator now covers both engines.

📦 Updated packages

Package From To
WuicCore 1.7.10 1.7.11
Wuic.Webcore 1.7.10 1.7.11
WuicOData 1.7.10 1.7.11
RuntimeEfCore 1.7.10 1.7.11
Wuic.MySqlProvider 1.7.10 1.7.11
Wuic.PostgresProvider 1.7.10 1.7.11
Wuic.OracleProvider 1.7.10 1.7.11
wuic-framework-lib (npm) 1.7.10 1.7.11

🔧 Recommended operational updates

  1. No configuration change: the appsettings.json keys are unchanged.
  2. If you have generated reports on MySQL or PostgreSQL that printed blank, open them again: the query is rewritten at render time, there is nothing to regenerate.
  3. On Oracle, if scaffolding a table from the interface answered with a duplicate key error, try again: the menu key generator is realigned after every insert with an explicit key.

v1.7.10

Back to index

Previously published version: 1.7.9 (18 September 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


This version comes out of a full round of end-to-end installs on clean machines: sixteen combinations, the four distribution packages against the four supported engines, each time starting from the published zip and following the documentation the way a first-time user would. Almost everything it surfaced affects those who do not use SQL Server: reports printing an empty page, a scaffolding call that failed, indexes that were never created. All of them silent — no message on screen, just a feature that did not work.


📊 Reports print their data again on MySQL and PostgreSQL

Opening a tutorial report on MySQL or PostgreSQL, the viewer drew the page — header, frame, "page 1 of 1" — and printed not a single row. The same report worked on SQL Server.

The reason is that a report scaffolded by the framework carries its own query, and that query is written with the identifiers of the engine it was generated on. The .mrt file, however, ships inside the package and is the same for all four engines: the tutorial one holds unquoted uppercase identifiers. On SQL Server that passes, because comparison is case-insensitive there; on PostgreSQL an unquoted identifier is folded to lowercase and no longer matches the columns, which the tutorial creates quoted in PascalCase.

Now, before rendering, reports generated by the framework — recognisable by the marker they carry in the SELECT — have their query rebuilt from the metadata for the engine of the current installation, with the right quoting rules. Reports written by hand in the designer are left alone: their query stays the one you wrote.

🧱 Scaffolding a table from the UI on PostgreSQL

On PostgreSQL, scaffolding a table from the interface answered HTTP 500 with a foreign key violation on _metadati__colonne, and the generated route stayed empty.

The real defect was upstream, and it was twofold. The identity sequence of the menu table falls behind whenever rows are inserted with an explicit key — which the framework does when it adds its own system entries: from then on the first insert that relies on the sequence asks for a key that is already taken. And when the menu entry failed to be created, the code deleted the metadata table row it had just inserted, leaving the columns without their parent: the error that reached the user therefore talked about foreign keys and a table that had nothing to do with it.

The sequence is now realigned after every insert with an explicit key, and a menu that fails to be created no longer takes the table metadata with it: at worst the menu entry is added from the designer.

⚡ Metadata indexes are actually created now

The schema migrations that create the indexes on the metadata hot paths — route name, columns per table, scheduler — were not applied on a fresh installation, for two different reasons.

The first: when a freshly installed application boots, the connection strings are not there yet, so the migrations failed; and because the attempt was considered spent, they were never retried within the process. On a new installation, where nobody restarts the service right away, those indexes were never created. The "database not configured yet" condition no longer consumes the attempt, and the migrations run as soon as the first-run wizard has written the connection strings.

The second, on Oracle only: the index script referenced the columns quoted in lowercase while the schema creates them in uppercase, so it answered ORA-00904 and the migration stopped there.

What this means in practice is on the pages that depend on those indexes: opening a list from the menu, going back into a list already visited, the first entry of the Administration menu. Without the indexes those operations reached tens of seconds on PostgreSQL.

🤖 The VS Code assistant answers questions instead of writing code

Asking the assistant "which columns does route X have?" — even adding "do not modify files" — made it scaffold a component and answer by describing what it had written. The question stayed unanswered and the project was modified against the instruction.

A request that is a question, or that explicitly forbids changes, now puts the assistant in read-only mode: the tools that touch the project are refused, and the answer is built with the query tools (route columns and metadata, code and documentation search).

🔧 Installations that tolerate transient hiccups

  • Windows: when winget answers that another installation is already in progress — typically Windows Update right after boot — the installer no longer stops with "install it manually": it retries three times with a growing wait. That condition clears by itself in a few dozen seconds.
  • Linux, Oracle: the Oracle container reports the listener ready while the first boot is still applying the system user password. The installer used to exit with ORA-01017; it now waits for the credentials to become valid, and if they do not it says so plainly instead of letting the error resurface later dressed as something else.

📚 Documentation

  • "Framework component + Custom data" pattern: a new section explains that on Oracle, when you open the connection yourself with DataSQLConnection, you must move the session to your schema (ALTER SESSION SET CURRENT_SCHEMA) or qualify the tables — otherwise the first query answers ORA-00942 even though the table is there. The other engines do not need it, because the database is in the connection string.
  • Getting started: the VS Code path (workspace file, F5, the Fullstack launcher) is now in the page text and not only inside a code block.
  • Package FIRST_STEPS (English, French, Spanish, German): added the section on how to open the project in VS Code and which npm script serves the frontend. It was only in the Italian version before.
  • Linux source kit README: added the section on renaming the project, which so far was documented for Windows only.

📦 Updated packages

Package From To
WuicCore 1.7.9 1.7.10
Wuic.Webcore 1.7.9 1.7.10
WuicOData 1.7.9 1.7.10
RuntimeEfCore 1.7.9 1.7.10
Wuic.MySqlProvider 1.7.9 1.7.10
Wuic.PostgresProvider 1.7.9 1.7.10
Wuic.OracleProvider 1.7.9 1.7.10
wuic-framework-lib (npm) 1.7.9 1.7.10

🔧 Recommended operational updates

  1. No configuration change: the appsettings.json keys are unchanged.
  2. On PostgreSQL and Oracle installations already in production, the missing indexes are created at the first start with 1.7.10: that first start can take a few seconds longer, once.
  3. If you have scaffolded reports on MySQL, PostgreSQL or Oracle that printed blank, just open them again: there is nothing to regenerate, the query is rewritten at render time.
  4. If one of your controllers opens its own connections on Oracle, check that it moves the session to the data schema or qualifies the tables: see the note on the patterns page.

v1.7.9

Back to index

Previously published version: 1.7.8 (18 September 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


A short, focused release: the loading indicator was lying. On a slow route the page stayed empty for the whole duration of the query, and whatever did appear had already turned the indicator off halfway through. Anyone trying the product on a large table saw a blank screen and concluded it had frozen.


⏳ The grid appears immediately, and the wait is visible

The defect had two halves, and each one hid the other.

The grid did not exist yet. The table — and with it the loading overlay, which belongs to the table — was built only when the data arrived: the component received the metadata together with the query result, not before. On a slow route that meant no headers, no toolbar, no sign of activity: just the page title, for every second of the query. The metadata is now published before the data read starts: the grid mounts empty and the indicator spins for the whole wait. Measured on a route that answers in 4 seconds: the table is on screen after 330 ms instead of 4,300.

The indicator switched off halfway. Every server call turned the "busy" state on and off without counting how many operations were in flight. Opening a route with lookup columns, the framework fires a dozen metadata reads in parallel with the data query: the first one to answer — after half a second — switched the indicator off for everyone, while the real query kept running for another three seconds.

In-flight operations are now counted, and the state goes idle only when the last one finishes. On the same route the indicator covers the entire wait instead of 600 milliseconds.

The fix was verified on every archetype — list, map, scheduler, chart, carousel, kanban, timeline, tree, spreadsheet — measuring for each one when the indicator turns on, when it turns off and when the data appears: in none of them does it switch off before the data.

One effect to be aware of: on routes with many lookup columns the indicator now stays on for a few seconds after the rows are readable, because those metadata reads really are still running. It used to disappear earlier, but it was lying.

🧩 The props-bag suggester actually turns the flag on

In the Suggest md_props_bag panel of the metadata editor, ticking a boolean entry — for example archetypes.list.advancedFilter, the one that replaces column filters with the filter bar — inserted "advancedFilter": false into the JSON. Ticking created the key but copied the sample value, which for those flags is false: you had to notice and fix it by hand.

A ticked boolean entry now goes in as true. Anyone who does not want the flag simply does not tick it: the runtime default is already false. Non-boolean entries still carry the sample value, which there serves as a template to fill in. This applies to both suggesters, the table one and the column one.

📦 Updated packages

Package From To
WuicCore 1.7.8 1.7.9
Wuic.Webcore 1.7.8 1.7.9
WuicOData 1.7.8 1.7.9
RuntimeEfCore 1.7.8 1.7.9
Wuic.MySqlProvider 1.7.8 1.7.9
Wuic.PostgresProvider 1.7.8 1.7.9
Wuic.OracleProvider 1.7.8 1.7.9
wuic-framework-lib (npm) 1.7.8 1.7.9

🔧 Recommended operational updates

  1. No configuration changes: the appsettings.json keys are unchanged.
  2. Once the npm library is updated, rebuild the frontend: both fixes live in the components, not in the metadata.
  3. If your application reads the framework's "busy" state to drive an interface of its own, check it: that state now stays active until the last in-flight operation ends, so it lasts longer than before. It is the correct behaviour, but it is an observable change.
  4. If any of your code switched the busy state off by writing to it directly, move to the toolbox's beginBusy / endBusy, or to resetBusy for an error-recovery path: a direct write switches the indicator off for everybody else's operations too.

v1.7.8

Back to index

Previously published version: 1.7.7 (17 September 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


A release that came out of using the product rather than reading about it: a gesture missing from lists, a sample that existed on three databases out of four, text that stayed in Italian on installations that were not Italian, and documentation pages describing screens other than the real ones.


🖱️ Double-click a row and the edit form opens

Until yesterday a record's edit form opened only from the Edit entry of the row action menu. Now a double-click on the row opens it too.

This is not a parallel shortcut reimplementing the same logic: the framework walks from the clicked row to its action menu and runs that very command. The same permissions apply (md_editable), and so does the conditional rule evaluated on that record (md_conditional_update_rule): where the pencil is missing or disabled, the double-click does nothing. That is a structural guarantee, not a promise — if the rules about who may edit change tomorrow, they change for both at once.

The gesture is ignored when it is aimed at something else: a double-click on a button, a link or a text field (you are using that control, or selecting text), or a row already open for inline editing.

It works on the mobile card layout too, where the list recognises two taps in quick succession on the same card. That was needed because when the gesture originates from a touch the browser does not always deliver the double-click event: two taps stay two separate clicks.

🗓️ The SQL Server tutorial now has the Timeline / Gantt page

The documentation describes the timeline/gantt archetype and the tutorial database already ships the sample data, but on SQL Server the route and the menu entry that open them were missing: they only existed on MySQL, PostgreSQL and Oracle. Anyone installing the tutorial on the default engine had no way to see that view, and the documentation page stayed unverifiable.

The SQL Server tutorial now has Samples → Timeline (Gantt): nine tasks across two projects, with dependencies between tasks, milestones, progress and grouping by project. It is a complete, working example of md_props_bag.archetypes.timeline to copy the configuration from — including the two parts that are most often got wrong: predecessors on a multiselect column backed by a self-referencing bridge table, and grouping on a lookup instead of free text.

🌍 Text that stayed in Italian

  • RAG chatbot. While the engine starts up, the chat answers that it is not ready yet. That message was hand-written in Italian in the code and reached English, French, Spanish and German installations exactly as it was. It is now a key translated into five languages. It no longer mentions "downloading the models" either: since the installation pre-fetches them, the wait is loading into memory, not downloading — the message described something that was not happening.
  • Workflow designer. Six menu commands (Reopen, New graph, Save graph, Delete, Automatic re-layout, Open runner in a new tab) were Italian literals in the code: on an installation in another language the menu came out half translated.
  • The map warning when the Google key is missing. It existed in two languages and only in the SQL Server packages; on the other engines the raw key appeared instead of the message. It is now five languages on all four engines.

🧹 A cleaner tutorial

The twenty Cline Prompt Tests entries are gone from the tutorial menu — leftovers of internal trials that had no reason to be in a sample database.

📚 Documentation

The pages below described the product inaccurately. These are not cosmetic edits: they were instructions that led people the wrong way.

  • First start. The wizard was not described at all. The getting-started guide now explains the two setup modes (Existing database and Tutorial WideWorldImporters, the second only present when the package ships the tutorial), the DataSQLConnection field and the test button everything else depends on — until you press it, the database list stays disabled —, the initial admin user, and how long provisioning takes: 30 s – 2 min in tutorial mode, 1–4 min on an existing database with automatic scaffolding enabled.
  • There is no default password. The same page declared admin / admin as the credentials after first start. That is false: the password is the one chosen in the wizard, and there is no other.
  • Licensing. The procedure said to write the values into the configuration file and restart the backend, and never mentioned that the UI exists. Pasting the license from Administration → AppSettings Editor, License section, makes the backend refresh validation on its own, and the license applies from the next request. A restart is only needed in the other case, when the values are written into the file by hand.
  • Initial scaffolding. It did not say that in tutorial packages there is nothing to scaffold: the metadata database arrives already populated, and the checkbox that registers tables only exists in Existing database mode.
  • List Grid. A new section lists the buttons the toolbar actually shows when a route opens, and the condition for each. The page named only optional or transient commands — those of the import/export progress dialog, which exists only while an import or export is running — and none of the ones you see as soon as you open a list.
  • Designer. The palette list named five entries, three of which do not exist under those names, and said nothing about the rest. It now carries the three real groups and the real component names.
  • Trial mode. An unlicensed installation caps every query at 20 records: a list that reads "20 of 20" on a table with thousands of rows is running in Trial, not faulty. That is now written where it is needed — on the downloads page and in the getting-started guide — not only on the pricing page.

📦 Updated packages

Package From To
WuicCore 1.7.7 1.7.8
Wuic.Webcore 1.7.7 1.7.8
WuicOData 1.7.7 1.7.8
RuntimeEfCore 1.7.7 1.7.8
Wuic.MySqlProvider 1.7.7 1.7.8
Wuic.PostgresProvider 1.7.7 1.7.8
Wuic.OracleProvider 1.7.7 1.7.8
wuic-framework-lib (npm) 1.7.7 1.7.8

🔧 Recommended operational updates

  1. No configuration changes: the appsettings.json keys are unchanged.
  2. Once the npm library is updated, rebuild the frontend: the double-click lives in the list component, not in the metadata.
  3. If your application already had behaviour bound to double-clicking a list row, check it: the framework now opens the edit form on that same gesture.
  4. The Timeline (Gantt) entry appears in new tutorial installations on SQL Server. An existing installation does not need to be redone: the sample data is already there, only the route and the menu entry are missing, and both can be added from the UI.

v1.7.7

Back to index

Previously published version: 1.7.6 (16 September 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


This version fixes five defects found by installing the framework on clean machines, one for each operating-system and database combination. The most important one affects Oracle and PostgreSQL, where a page generated by the framework could fail to open depending on how you spelled its name in the address. The others affect Oracle XE and the Linux source package.


🔤 Routes now open regardless of letter case

A route name is born lowercase: a Supplier table becomes the supplier route. Opening /Supplier/List — that is, typing the name of your own table — worked on SQL Server and MySQL, where the engine ignores letter case, but not on Oracle and PostgreSQL, which answered Route 'Supplier' not found in metadata.

Same address, same scaffolding, a different outcome depending on the database underneath. On Oracle the effect multiplied: the list generated by the initial scaffolding, the page created from the UI, the components that mount a framework widget and the metadata APIs all failed for the same reason, and looked like four separate faults.

Route resolution now falls back to a case-insensitive comparison when the exact one fails: the normal path is unchanged, and the physical names of tables and columns stay exactly as written in the schema.

If an installation held two routes that differ only by letter case, the framework cannot pick one: it now says so with an explicit message (HTTP 409, errors.metadata.route.ambiguous_case) listing the conflicting routes, instead of answering "not found".

🗄️ Oracle XE: scaffolding from the UI

Generating a page from a table through the administration menu answered 500 on Oracle XE:

ORA-00932: inconsistent datatypes: expected NUMBER got BOOLEAN

The metadata columns that represent a switch are numeric, but the provider wrote a boolean into them: a type that in Oracle SQL exists only from 23ai onwards. On 23ai the implicit conversion hid the problem; on XE — the most widely installed edition — the server rejected the insert and scaffolding stopped. It now writes 1 and 0, which work on every version.

🐧 Linux source package

  • The RAG chatbot was off. The package declared the .NET engine active in its own configuration but did not ship it: /api/Rag/Health answered not-initialized forever, with WuicRagEngine.dll not found. The engine is now here too, as it already was in the Windows package.
  • Models are downloaded during the install. On the first question the chatbot had to fetch 4.4 GB of models and index, and for several minutes it only answered "still initialising". install.sh now fetches them while installing, alongside everything else: the first question answers immediately. Skip the download with --skip-prefetch, which restores the previous behaviour. The step never interrupts the install: if the network misbehaves, the chatbot fetches them on first use.
  • The files for coding assistants were not generated. On Linux the first run wrote no AGENTS.md, CLAUDE.md, .mcp.json or skills: the template that produces them was not in the package, and generation was skipped silently. They now ship in both Linux packages.
  • Renaming the project. rename-project.sh looked for the project only in its own folder and the one above, while in the package it sits one level below: run as documented, it exited with WuicTest.csproj not found. It now looks there as well and, when it really finds nothing, lists the paths it tried.
  • The package no longer carries the leftovers of the old Python RAG stack (codebase_embeddings/, rag-setup.ps1, rag-start.ps1), replaced by the .NET engine back in June.

📦 Updated packages

Package From To
WuicCore 1.7.6 1.7.7
Wuic.Webcore 1.7.6 1.7.7
WuicOData 1.7.6 1.7.7
RuntimeEfCore 1.7.6 1.7.7
Wuic.MySqlProvider 1.7.6 1.7.7
Wuic.PostgresProvider 1.7.6 1.7.7
Wuic.OracleProvider 1.7.6 1.7.7
wuic-framework-lib (npm) 1.7.6 1.7.7

🔧 Recommended operational updates

  1. On Oracle and PostgreSQL: if you had marked scaffolded pages as broken, try them again after upgrading — this was most likely the cause.
  2. On Oracle XE: scaffolding from the UI is usable from this version; tables exposed earlier do not need to be redone.
  3. Installing from source on Linux now means 4.4 GB of extra download during the install, or add --skip-prefetch to defer it to the chatbot's first use.
  4. No configuration change is required: the appsettings.json keys are unchanged.

v1.7.6

Back to index

Previously published version: 1.7.4 (16 September 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


This release ships the same day as 1.7.4 and fixes three things found by repeating the installation on clean machines right after publishing it. The important one is about Oracle: first start on an existing database reached the end and reported success, but left the application without the user's tables. The other two are about the Linux package.


🗄️ Oracle: first start on an existing database

Choosing the "existing database" mode, pointing at your own schema and asking for automatic scaffolding took you to the login with a menu containing none of your tables, and no error on screen. The reason was written only in firstrun-scaffold-error.log, next to the application:

firstRun scaffold failed for db='MYSCHEMA' dbms='oracle':
  ORA-01017: invalid credential or not authorized; logon denied

On Oracle the "data database" is the schema, and the framework built the connection using the chosen schema as the user while keeping the password typed in the wizard, which belongs to whoever is configuring: a credential nobody typed and that in general does not exist. Scaffolding, which reused that same connection, could not authenticate; the error landed in a tolerant block and the wizard reported success anyway.

The connection now keeps the user given in the wizard — as it already does on PostgreSQL, where choosing the database does not change your identity — and the working schema is set on the session with ALTER SESSION SET CURRENT_SCHEMA, which is Oracle's own way of operating inside a schema without being its user. Installations where the configuring user is the schema (all tutorial ones among them) behave exactly as before.

In practice: from the wizard through scaffolding to the login, the tables appear in the menu and the lists show data.

🐧 Linux package

  • The RAG chat was off on all four engines. The .NET engine is installed correctly, but the appsettings.linux.*.json profiles carried no rag-* key: the backend fell back to a Python server the Linux package does not ship, and every request answered 503. The keys are now in all four profiles.
  • Report templates were not shipped. The Reports/ folder never made it into the tarball, so on Linux no route offered the Report entry. It is included now.
  • Renaming the project on Linux. The source package offered a single way to make the template your own, and it was a PowerShell script: on a clean Ubuntu pwsh is not there, the installer does not add it and the prerequisites do not ask for it. Next to rename-project.ps1 there is now rename-project.sh, doing the same steps — rename .csproj, controller and VS Code workspace, update contents, align the name in package.json — with --name, --in-place and --backend-port. By default it clones into a new folder and leaves the original untouched.
  • The smoke test failed healthy installations. At the end of the installation the script attempted an administrative login even when the application stays in first-run — the normal condition on Oracle since 1.7.4, where users and metadata are created by the wizard. The installation closed with "Install completed but smoke tests failed" while being perfectly fine. The check is now skipped with a note explaining how to complete it.

🔧 Recommended operational updates

  1. If you downloaded the 1.7.4 Linux tarball in its first hours: download it again. The first published copy carried mismatched assembly versions and the service would not start (Could not load file or assembly 'WuicOData'); the current copy is correct, and this release supersedes it anyway.
  2. On Oracle, an installation already completed with 1.7.4 and left without tables in the menu does not repair itself: redo the first start against an empty metadata database.
  3. No configuration change is required: the appsettings.json keys do not change.

v1.7.4

Back to index

Previously published version: 1.7.3 (14 September 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


This release is almost entirely about Oracle. Repeating installation and first start on clean machines — on Windows with IIS and on Linux with nginx — surfaced four points where the path broke before reaching a working application: loading the tutorial data, creating the users, the RAG chat and, on Linux, the generation of the database password itself. None of the four shows up on the other engines.

The rest are first-start fixes that apply to every engine, plus two to the diagnostics of SQL errors.


🗄️ Oracle: tutorial data and first start

Loading the initial data on Oracle succeeded on 472 statements out of 18,141. The others failed with ORA-01843: not a valid month, because the first-run scripts carry dates and numbers in the Italian convention while the Oracle session used the server defaults. Script execution now sets NLS_DATE_LANGUAGE and NLS_NUMERIC_CHARACTERS before the first statement: 17,889 statements out of 17,889, on Windows and on Linux alike. If you tried Oracle with 1.7.3 and found the tutorial tables half empty, this is why.

Still on Oracle, three fixes to the Linux installation:

  • the password generated for the database could contain the @ character, which breaks the easy-connect string of sqlplus: installation stopped with ORA-12262 in roughly one case out of three, and the offending character appeared in no message;
  • creating the tutorial databases and the authentication users is now left to the first-run wizard, which is the path every other platform already followed. The installation scripts were instead preparing a schema the wizard would redo anyway;
  • appsettings.linux.oracle.json ships with firstRun enabled, so the first browser window lands on the wizard instead of an application without metadata.

💬 RAG chat on Oracle

The chat answered 500 to the first question, with ORA-00933: SQL command not properly ended. The chat queries end with a semicolon — correct in SQL*Plus and on the other engines, but for the Oracle driver it is part of the statement text. The trailing semicolon is now removed before execution, except for PL/SQL blocks, where END; is legitimate.

🚀 First start (all engines)

  • The wizard now stops with a message when the data database you typed is not among those read from the server. It used to carry on: the installation completed and the menu opened, but every grid stayed empty because the metadata pointed at a database that does not exist. The message is translated in all 5 languages.
  • On Linux, appsettings.json in the application folder is a symbolic link to /etc/wuiccore/appsettings.json. The atomic configuration write now resolves the link before writing: without that, the wizard wrote a new file in place of the link, the application kept reading the old one, and the first start never completed.
  • The configuration files written by the installer stay writable by the service (mode 0660, service group). The wizard failed its final save on a freshly created installation.

🔎 Diagnostics of SQL errors

The error envelope the dialog shows an administrator stated two things that were not true:

  • sqlProvider reported mssql for any error that was not MySQL — so for Oracle and PostgreSQL exceptions too. The value now derives from the exception type, and unrecognised providers report unknown instead of a wrong name;
  • query stayed empty on the paths that use ADO.NET directly instead of the Dapper layer (the RAG chat among them), even with diagnostics on. Command capture now covers both paths, with the same masking of sensitive parameters.

🐛 Notable bug fixes

  • LLM model list without an API key: the endpoint contacted the provider even when no key was configured, and the settings page waited for the network response. With no key the locally curated list is now returned immediately.

🔧 Recommended operational updates

  1. If you use Oracle, upgrade before redoing an installation: the fixes above concern the first-start path and do not apply retroactively to a database already half populated. On an installation already spoiled, start again from an empty database.
  2. On Linux, check after the upgrade that appsettings.json in the application folder is still the link to /etc/wuiccore/appsettings.json and not a standalone file left behind by a 1.7.3 first start.
  3. No configuration change is required: the appsettings.json keys do not change.

v1.7.3

Back to index

Previous published version: 1.7.0 (14 August 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


The new front is one-command installation on Windows. The rest are fixes to installation and first run, found by running the whole procedure again on clean machines, on Windows and on Linux, with SQL Server, MySQL and PostgreSQL. Two of them stopped you from reaching the wizard at all: on Linux with MySQL the database load stopped at the first statement, and the wizard's automatic scaffolding did not generate the metadata, leaving the application with an empty menu.


💾 One-command installation on Windows

From a PowerShell window:

irm https://wuic-framework.com/install.ps1 | iex

The script does on its own everything that used to be a list of manual steps: it checks for the ASP.NET Core 10 runtime and installs it if missing, looks for a reachable SQL Server instance and, if it finds none, installs SQL Server 2022 Express, downloads the latest published package, extracts it and opens the browser on the first-run wizard. It is the Windows twin of install.sh, and it works with Windows PowerShell 5.1, so installing PowerShell 7 is not required.

The application is published in IIS: the script enables the IIS features if missing, installs the ASP.NET Core Hosting Bundle, creates the application pool and the site on the chosen port, and grants the pool identity permissions on the folder and a login on SQL Server. Elevation is needed: the script asks for it once, with the standard Windows prompt.

With -Kestrel nothing in IIS is touched and the application runs in a console window, restartable with start-wuic.cmd. It is the right choice when you have no administrator rights, when IIS is disabled by corporate policy, or for a throwaway trial.

The most used options: -WithTutorial downloads the package with the WideWorldImporters demo database, -Port changes the port, -ListenAll listens on all interfaces instead of localhost only, -SqlServer points at an existing instance, -Dbms mysql|postgres installs and configures an engine other than SQL Server.

A requirement to know before choosing the package: the tutorial variant in .bak format contains a native SQL Server 2022 backup and therefore requires SQL Server 2022 or newer, because restore is not backward compatible. On SQL Server 2019 use the SQL-script variant, which is supported from 2019 onwards; the installer detects this by itself and picks the right variant for the instance it finds.

Compared with the first version of the script, three things you would hit immediately have been fixed:

  • with -Dbms mysql or -Dbms postgres the installer wrote its own secrets into the installation folder and then rejected that same folder as "not empty", exiting with code 2 twelve seconds after filling it itself; extraction also deleted the freshly generated root password. The installer's own files no longer count in the check, and the secrets survive extraction;
  • re-running the command on a machine where the installation already existed no longer ends in a dead end;
  • with -Dbms postgres the tutorial requires the PostGIS extension, which on Windows is not installed together with PostgreSQL: the load died halfway through with extension "postgis" is not available, an error that looked like a defect in the SQL scripts. The installer now checks the available extensions and, if PostGIS is missing, installs the bundle for the PostgreSQL version it found.

The installer now says what it is doing during the long phases too: installing SQL Server Express and npm install can go for minutes without printing a line, and until now the window looked stuck. Every ten seconds of silence it prints how long it has been working and what the last meaningful line was.

🐧 Installation on Linux

Three fixes, all on the road to the first run.

The framework did not start on obfuscated packages. The anti-tamper protection applied at release time prevented startup on Linux. It has been removed: symbol obfuscation stays, the protection that blocked execution does not.

The MySQL database load stopped at the first statement. The bootstrap script assumed a database was already selected, while the dumps no longer contain the USE directive, and the load died with ERROR 1046 (3D000): No database selected. Anyone who installed with MySQL using the previous package still hits this error: it is the main reason to install this release.

The configuration profile was not copied into the project. The appsettings.json shipped in the source package points at a named SQL Server instance, which does not exist on Linux: following the documentation, dotnet run died after forty-five seconds with error: 26 - Error Locating Server/Instance Specified and the wizard never appeared. The engine-dependent keys are now copied into the project automatically.

The Ubuntu releases actually supported are now documented: 22.04 and 24.04 LTS. On 24.04 SQL Server needs a compatibility start; 26.04 is not supported by Microsoft for SQL Server.

🌍 First-run wizard in five languages

The first-run wizard spoke Italian only, regardless of the browser. It now shows up in the browser language among the five supported (Italian, English, French, Spanish, German) and, when you choose the administrator's language, the whole page switches to it immediately. For an unsupported language the fallback is English.

The messages the wizard produces during configuration are translated too: connection test result, connection-string validation errors and confirmation of metadata database re-creation.

🐛 Notable bug fixes

  • The wizard's automatic scaffolding did not generate the metadata. On first run, with "Run automatic scaffolding of the selected DB tables" ticked, the application started with an empty menu and no routes: scaffolding ran against a connection other than the one just chosen in the wizard. The connections chosen in the wizard are now actually used, and on first login the menu contains the tables of your own schema.
  • Update or delete without a key: now rejected. If the payload of an update or a delete contained no column recognised by the metadata, the generated WHERE clause came out empty and the statement hit every row in the table, returning a success result on top of that. This happens when a primary key is sent with different casing from the schema. The operation is now rejected with HTTP 400 and the code errors.metaservice.crud.missing_key_predicate, and the message lists the keys received so the cause is immediately clear.
  • Without a Google Maps key the application stayed blocked. A missing key in GoogleMaps:ApiKey produced a JavaScript error that bubbled up to the application error dialog and made the whole interface unusable, not just the map. The map component now shows a non-intrusive notice inside its own view and the rest of the application keeps working.
  • Scene 3D in the Professional plan. The viewer and designer routes for Scene 3D declare the scene3d-designer feature as required, but no plan included it: on any Professional installation the check redirected to the access-denied page. The feature is now part of the Professional plan.
  • First-run scripts two orders of magnitude smaller. The first-run scripts contained the data of the log tables, the chatbot conversations and the dashboard contents: error logs and development chats shipped inside every package. Those tables now travel with their structure only. The first-run metadata script goes from 468 MB to 3.7 MB in the minimal profile and to 14 MB in the tutorial one (the WideWorldImporters demo data is separate and unchanged in size).
  • Chatbot documentation aligned with the real engine. The installation pages still mentioned Python and a separate service for the RAG chatbot. The engine is native .NET/ONNX and runs inside the application process since 1.3.0: no Python, nothing to install separately. The size of the models downloaded on first use has been corrected too — about 4.5 GB.
  • Four empty documentation pages (RAG chatbot, licensing, mailing lists, pivot tables) have been written, in all five languages.

🔧 Recommended operational updates for those upgrading

  1. Anyone who installed with MySQL on Linux starting from the previous package must reinstall with this version: the database load was interrupted and the installation was not complete.
  2. Check the integrations that write through the CRUD APIs: a call that until now updated the whole table without noticing now receives HTTP 400. The error message lists the keys received, and the fix is almost always to align the primary key name with the schema casing.
  3. If you use Scene 3D with a Professional license issued before this version, request an updated license: the scene3d-designer feature must be included in the plan.
  4. For a new installation on Windows, use the one-step command instead of the manual procedure. On a SQL Server 2019 instance choose the tutorial variant with the SQL scripts.
  5. If you use the map component, configure GoogleMaps:ApiKey in appsettings.json: without a key the map shows a notice, but the rest of the application stays usable.

v1.7.1

Back to index

Previously published version: 1.7.0 (15 August 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


A consolidation release with one new front — one-command installation on Windows — and a series of fixes that came out of installing the product from scratch on clean machines, following the documentation to the letter. Several of them concern Linux, where some things did not work at all: the chatbot was missing from the package, reports would not open, and anyone starting from sources could not launch the application.


💾 One-command installation on Windows

From a PowerShell window:

irm https://wuic-framework.com/install.ps1 | iex

The script does on its own everything that used to be a list of manual steps: it checks the ASP.NET Core 10 runtime and installs it if missing, looks for a reachable SQL Server instance and, if it finds none, installs SQL Server Express, downloads the latest published package, extracts it and opens the browser on the first-run wizard. It is the Windows twin of install.sh, and it is compatible with Windows PowerShell 5.1, so it does not require installing PowerShell 7.

The application is published to IIS: the script enables the IIS features if missing, installs the ASP.NET Core Hosting Bundle, creates the application pool and the site on the chosen port, and grants the pool identity the folder permissions and the SQL Server login. Elevation is required: the script asks for it once, through the normal Windows prompt.

With -Kestrel nothing in IIS is touched and the application runs in a console window, restartable with start-wuic.cmd. It is the right choice when you do not have administrator rights, when IIS is disabled by corporate policy, or for a throwaway trial.

The most used options: -WithTutorial downloads the package with the WideWorldImporters demo database, -Port changes the port, -ListenAll listens on all interfaces instead of localhost only, -SqlServer points at an existing instance, -Dbms selects an engine other than SQL Server.

A requirement to know before picking the package: the tutorial variant in .bak format contains a native SQL Server 2022 backup and therefore requires SQL Server 2022 or later, because the restore is not backward compatible. On SQL Server 2019 use the SQL scripts variant, which is supported from 2019 onwards; the installer notices this on its own and picks the right variant for the instance it finds.

On Windows Server the database engine must be installed first. The command obtains missing components through winget, which is not present on Windows Server: without an already reachable instance the installation stops and says so. This applies to every engine, not just SQL Server.

🐧 Linux: the package is finally complete

Anyone installing on Linux found three broken things, all fixed in this version.

The RAG chatbot was not there. The .NET/ONNX semantic search engine was included only in the Windows packages: every Linux tarball shipped without it, and GET /api/Rag/Health answered 503 rag-server-unreachable forever. The tarball now contains it, with the complete linux-x64 native libraries: on a machine with CUDA 12 and cuDNN 9 it uses the GPU with no manual work, otherwise it falls back to CPU.

The semantic engine could not find its own native libraries. It looked for them in PATH, separated by ;, which is the Windows convention; on Linux the loader uses LD_LIBRARY_PATH and colons.

Reports would not open. The cache path was built with Windows backslashes and with a leading slash, so on Linux it ended up in the filesystem root instead of under the application folder. The viewer returned an error that looked like a configuration problem.

And for those starting from sources: the application did not start at all. The WuicCore NuGet package carried an anti-tampering protection whose initialization code is not compatible with the .NET runtime on Linux, and it failed before executing a single line of the application. On Windows the very same library started without problems, which is why the defect stayed invisible for so long. The protection has been removed; the rest of the obfuscation is unchanged.

📦 Source kit: it builds on a clean machine

The developer package carried a few assumptions that only held on the machine that built it.

  • The dependency lock ships inside the package. Without it, npm install on npm 10.9.x — the version shipped with Node.js 22 LTS, that is the one the documentation itself requires — stops while resolving peer dependencies. The lock is now included, the installation is reproducible and two developers get the same tree.
  • The 3D libraries are declared. three, three-gpu-pathtracer, three-mesh-bvh and @dimforge/rapier3d-compat were imported without appearing among the dependencies: on the machine of whoever develops the framework they were present by other routes, on a clean machine the build stopped on an unresolved import. They are optional peer dependencies: those who do not use 3D scenes do not carry them along.
  • The Angular asset paths pointed outside the package and the build failed on non-existent files.
  • The LLM-ready workspace was generated without its own settings.

🌍 First-run wizard

The wizard spoke only Italian, regardless of the browser. It now shows up in the browser language among the five supported ones (Italian, English, French, Spanish, German) and, when you pick the administrator user's language, the whole page switches to it immediately. For an unsupported language the fallback is English. The messages produced during configuration are translated as well: connection test outcome, connection string validation errors and confirmation of metadata database recreation.

Scaffolding an existing database is now on by default. Anyone pointing at their own database and accepting the proposed values ended up with an application with no routes at all, its tables ignored. Tutorial mode is unaffected.

🐛 Notable bug fixes

  • Update or delete without a key: now rejected. If the payload of an update or a delete contained no column recognized by the metadata, the generated WHERE clause was empty and the statement hit every row in the table, returning a success result on top of that. This is the case of a primary key sent with different casing from the schema. The operation is now rejected with HTTP 400 and the code errors.metaservice.crud.missing_key_predicate, and the message lists the keys received so the cause is immediately clear.
  • Maps without a Google key: a notice instead of a block. When the Maps JavaScript API is not loaded — typically because the key has not been configured — the component showed a modal dialog that stopped the whole page. A panel now appears in place of the map explaining what is missing, and the rest of the page stays usable.
  • Reinstalling on MySQL: no more dead end. The engine password is generated by the installer itself; re-running the command on the same machine it could not read it back, probed with an empty password and concluded that the database was not answering, while it was answering just fine. The minimum version check was also fixed: it never ran, because the MySQL warning about the password on the command line ended up in the parsed output.
  • Scene 3D in the Professional plan. The Scene 3D viewer and designer routes declare the scene3d-designer feature as required, but no plan included it: on any Professional installation the check redirected to the access denied page. The feature is now part of the Professional plan.
  • The sample appsettings no longer contain a real password.
  • Chatbot documentation aligned with the actual engine. The installation pages still mentioned Python and a separate service for the RAG chatbot. The engine is native .NET/ONNX and runs inside the application process since 1.3.0: no Python, nothing to install on the side. The size of the models downloaded on first use has also been corrected, and is about 4.5 GB.
  • Four empty documentation pages (RAG chatbot, licensing, distribution lists, pivot tables) have been written, in all five languages.

📚 Prerequisites corrected in the documentation

The getting-started pages listed requirements that did not match what was measured installing on clean machines:

  • Node.js 22 LTS, not "20 or later": it is the version the package is tested with.
  • PowerShell 5.1 is enough. The included scripts run on the PowerShell preinstalled on Windows; PowerShell 7 is recommended, not required.
  • On Windows Server the database must be installed first, before the installation command.

📦 Updated packages

Package From To
WuicCore (NuGet) 1.7.0 1.7.1
wuic-framework-lib (npm) 1.7.0 1.7.1

🔧 Recommended operational updates for those upgrading

  1. Check the integrations that write through the CRUD APIs: a call that until now updated the whole table without noticing will get HTTP 400. The error message lists the keys received, and the fix is almost always aligning the primary key name to the schema casing.
  2. On Linux, replace the package with this version even if the application seems to work: the RAG chatbot and the report viewer were not operational in earlier versions.
  3. If you use Scene 3D with a Professional license issued before this version, request an updated license: the scene3d-designer feature must be included in the plan.
  4. Those starting from the source kit can run npm install with no extra options: the included lock makes the installation reproducible.
  5. For a new installation on Windows, use the one-step command instead of the manual procedure. On a SQL Server 2019 instance choose the tutorial variant with SQL scripts; on Windows Server install the database engine first.

v1.7.0

Back to index

Previously published version: 1.5.0 (21 July 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


Packages replaced on 15 August 2026. The archives published initially shipped an appsettings.json with autoGeneratedQueryTimeout set to 1: with that value, every query taking longer than one second ends in a server error. A few header settings (logo, menu orientation, theme selector, notifications) had also been left behind by a test session. The archives have been rebuilt and their checksums updated.

If you downloaded before 15 August: download the package again, or open appsettings.json and set autoGeneratedQueryTimeout back to 30, removing the keys header-logo, header-logo-position, header-menu-orientation, header-show-theme-selector, header-menu-multicolumn-submenu and header-show-notifications unless you set them yourself. Neither setting touches your data: editing the file and restarting is enough to fix an existing installation.


Three weeks of work on two fronts. The first is appearance: the new Theme Builder is a page for composing custom themes — colors, typography, background, density, grid rendering — that apply to every user of the installation, and the application header has been redesigned and made configurable. The second is integration and observability: the new Webhook Hub receives and sends events with signing and retries, and the Performance Inspector measures fetch and render times per route. The chatbot learns to act on three more surfaces of the application and to query the database schema.


🎨 Theme Builder

A new page under Administration lets you compose custom themes without touching code. Every saved theme appears in the theme selector of all users of the installation, alongside the built-in ones.

What you can set:

  • Primary color, from which the full 11-shade scale is generated: the scale stays clickable, and picking a darker shade promotes it to primary color. A badge shows the WCAG contrast ratio reached (AAA / AA / AA-large / fail).
  • Light and dark surfaces, corner radius and density (comfortable or compact).
  • Typography: font family from a catalog of system fonts or a free-form stack, plus a base size that scales the whole interface.
  • Page background: solid color, gradient or image. The gradient can have an optional slow drift that swings its angle over time; the image is configured with fit, repeat, position and a dark overlay so text stays readable.
  • Grid: alternating rows and the selected row are no longer fixed colors but blends of the surface with an accent, driven by two intensity knobs, with the option of an accent different from the primary color.
  • Light/dark mode left free or forced by the theme.

The preview is live and applies the theme to the whole page while you compose it, without touching the user's own preferences: on leaving, reloading or closing the browser your own theme comes back.

Excel export follows the theme: a file exported while a custom theme is active comes out with that theme's colors — header, alternating rows, borders — instead of generic greys. Fallback styles have been added for every preset family, so a theme that has not been sampled yet still inherits the look of its family.

The built-in theme catalog gains new variants, and the active theme is applied on the very first frame after a reload, without the previous theme flashing.

🖥️ Redesigned, configurable header

The application header is now a framework component: installations inherit it instead of keeping a copy. The top-right block has been compacted — theme selector, light/dark, language, notifications and user area live in a card that expands on hover — and the user area shows name, role, license and sign-out.

A new Header & Menu section in the settings lets you choose the menu orientation (horizontal above or below, vertical left or right), upload a logo and position it, and turn the language selector, theme selector and notifications on or off. You can also disable the automatic multi-column layout of submenu entries: in that case long submenus become scrollable instead of being cut off.

🔗 Webhook Hub

A new system for integrating the application with external services, in both directions.

Outbound: events are queued and delivered asynchronously, with HMAC payload signing, retries at a fixed or exponential interval, a dead letter queue for permanently failed deliveries and the ability to replay a delivery. Every attempt is recorded in the logs.

Inbound: the POST /api/webhooks/inbound endpoint accepts external calls and routes them according to a metadata configuration — SQL execution, HTTP call or method invocation — with anti-replay protection.

Also included are notification policies with a configurable cooldown, an administrative API for managing endpoints, and a scheduler job that drains the queue. The whole configuration — endpoints, events, subscriptions, inbound rules — lives in metadata tables: adding an integration needs neither dedicated code nor a new deployment. The "Webhook Hub" page of the in-app documentation carries the full procedure in 5 languages.

📈 Performance Inspector

The framework can collect fetch and render metrics for every route, aggregate them and show them in an administrative dashboard: average, p95, maximum and count, with a 7-day retention and per-route collection.

The feature is off by default and is enabled with AppSettings:enablePerformanceInspector, in hot-reload. Alongside the metrics there is a data quality inspector, enabled per route from the props bag (extraProps.qualityInspector).

🤖 Chatbot: new actions and schema reading

The RAG chatbot gains ten new action types and the ability to query the structure of the project.

  • Three new contextual surfaces: pivot builder, settings editor and report designer. The chatbot proposes actions on the page you are on. In the settings editor changes are only prepared: saving stays an explicit user gesture. In the dump sent to the model, reserved values — connection strings, passwords, keys, license — are masked, and an exclusion list prevents writing them. In the report designer a new timestamped file is always generated, leaving the open report untouched.
  • Metadata operations: table creation, scaffolding of tables, views and columns, moving menu entries. The two irreversible operations — deleting a column and deleting a menu entry — require an explicit confirmation that is verified server-side as well, so it holds even when the request comes from a client with no user interface.
  • Introspection: the chatbot can list connections (names only, never connection strings), databases, tables, columns and the menu tree. Without these reads it could not know the real identifiers to work on.

A new documentation page lists the supported prompts, verified against the automated tests.

📊 Spreadsheet: column visibility aligned with the grid

The spreadsheet now honours the same visibility flags as the list grid: mc_hide_in_list, mc_hide_in_edit and mc_show_in_filters. A column hidden in the list no longer appears in the sheet, and a column hidden in edit is no longer editable in the cells.

🛡️ Security

Best-effort hardening on report scaffolding: the requested file name is now validated by rejecting absolute paths and disallowed characters, with a check that behaves the same way on Windows and on Linux — it previously relied on a normalization that filtered differently on the two systems. The returned error tells an invalid configuration apart from a generation failure, so integrators do not look for the problem in the wrong place.

🐛 Notable bug fixes

  • Scheduler, first load: the view showed data that was not filtered for the displayed range — typically no events in the current month, even with appointments present. The first load ran before the start and end fields configured for the archetype were available, and no later request corrected it. The fields are now resolved before the filter is composed and, if they arrive afterwards, the data is requested again exactly once.
  • 3D scenes with a professional license: the designer and 3D viewer pages redirected to the access-denied screen, because the feature was not included in any license profile. It is now part of the professional profile.
  • Reports on Linux: report scaffolding failed because fonts were handled through a graphics library available only on Windows.
  • Performance Inspector, aggregation: two overlapping runs of the aggregation job — the dashboard triggers it on its own reload, and it can also be launched manually — ended with a duplicate key error. The raw data window was also not aligned to the daily bucket boundary, so the oldest bucket was rebuilt from a subset of the events.
  • Linux behind a reverse proxy: absolute URLs generated by the backend lost the port and used the wrong scheme when the installation is served on a non-standard port or over plain HTTP.
  • Oracle, saving 3D scenes: saving failed because of a reserved parameter name and of numeric value conversion. Both fixed, together with the handling of quoted lowercase column names.
  • Oracle, OData exposure: columns were forced to uppercase instead of using the physical name declared in the metadata, making entities with mixed-case names unreachable.
  • PostgreSQL and Oracle, lookups in charts and grouping: the descriptive field of a lookup was not resolved from the logical name to the physical one, and grouping showed empty or wrong values.
  • Chatbot, missing confirmations: in four places the confirmation prompt never appeared because of a malformed call; in two of them the deletion (chat history and sessions) happened without asking anything. Every confirmation now goes through the same path and, on error, the answer is "cancel".
  • Metadata import: routes whose database field is empty — that is, those using the application's default database — could not be imported, and the error suggested a permissions problem or the wrong database.
  • 3D viewer: the scene occupied a horizontal band instead of the full height of the page.
  • UI, grid over a themed background: the area below the last row let the page background show through, making the grid look punctured. The grid surface now follows the theme, in light and dark.

🔧 Recommended operational updates for upgrades

  1. Review appsettings.json after the upgrade: the new keys have conservative defaults and need no action, but this is a good moment to go through them.
  2. Performance Inspector: set AppSettings:enablePerformanceInspector to true to enable it; it stays off if the key is absent.
  3. Webhook Hub: nothing to do to make it reachable. Tables, administrative routes and menu entries — gathered in a "Webhook Hub" submenu under Administration — are created on the first menu load, both on a fresh installation and when upgrading from an earlier version. What remains is configuring the integration: outbound takes three rows (the endpoint with target URL, shared secret, timeout and retry policy; the event; the subscription linking them), inbound takes an endpoint with inbound direction and a routing rule. The signature travels in the X-Wuic-Signature header; the full procedure is in the "Webhook Hub" page of the in-app documentation.
  4. Theme Builder: the themes table is created on the first save. If you use custom themes, check the Excel export of a grid to confirm the colors are the expected ones.
  5. Chatbot: the new actions are available after restarting the RAG engine; irreversible operations require confirmation and cannot be applied without it.
  6. Linux behind a reverse proxy: if the installation answers on a non-standard port or over plain HTTP, regenerate the nginx configuration or manually align the existing vhost to proxy_set_header Host $http_host; and proxy_set_header X-Forwarded-Proto $scheme;. Upgrades do not rewrite an existing vhost.

v1.5.0

Back to index

Previously published version: 1.3.2 (18 June 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


A broad release gathering work on several fronts. The RAG chatbot has a simplified, unified LLM configuration, with running a free local model (Qwen via Ollama) now a first-class option and an engine hardened against the quirks of local models; a new Visual Studio Code plugin, WUIC Assistant, brings the same agentic approach into the editor. The new Scene3D Designer brings 3D scene authoring into the app — PBR materials, shader effects, lights with baking, physics, and a viewer that ties objects to data — and rendering is now selectable between WebGL and WebGPU. The Workflow Designer gains an assisted-authoring pack (templates, graph validation, guided dialogs, inline help), and the Dashboard Designer a set of editing improvements.


🤖 RAG Chatbot — unified LLM configuration

The chatbot LLM provider configuration has been consolidated around a single key and an explicit provider list.

  • rag-llm-provider — anthropic / openai / openrouter / ollama, set it explicitly (no default provider: if empty, the chatbot stays in retrieval-only and invokes no LLM). ollama is now a first-class value: it points to a local runtime via rag-llm-base-url, with an OpenAI-compatible format.
  • rag-llm-api-key — the single source of the key, regardless of the chosen provider. It replaces the previous llm-api-key / anthropic-api-key pair (still accepted only as a migration fallback). The special value agent-sdk uses the Agent SDK (claude CLI) via subscription instead of the metered API, if installed.
  • rag-llm-base-url — endpoint override; required for ollama (e.g. http://HOST:11434/v1), optional for the other providers.
  • rag-llm-default-chat-model — model id for the chosen provider.

All keys remain hot-reloaded from appsettings.json: switching provider or model requires no restart.

🧠 Free local LLM (Qwen via Ollama), zero API key

The chatbot can now run entirely on a free open local model — for example Qwen (qwen2.5-coder:32b) served by Ollama on your own machine or on the LAN — with no API key and no per-token cost. Typical configuration in appsettings.json -> AppSettings:

rag-llm-provider           = ollama
rag-llm-base-url           = http://HOST:11434/v1
rag-llm-api-key            = ollama
rag-llm-default-chat-model = qwen2.5-coder:32b

A complete guide to set up the Ollama server (Windows/Linux, LAN exposure, context tuning, persistent startup) is included in the package.

⚙️ Reliable chatbot actions even with local models

The engine was made tolerant of the quirks of local models, which — unlike commercial models — sometimes do not strictly follow the tool-call format. The chatbot now correctly recovers the proposed action even when the model emits it as text or with non-standard JSON escapes. In practice, the actions on the designer and on metadata — table (bulk) buttons, row buttons, conditional styles, callbacks, component injection in the designer — are proposed and applied reliably even with a local LLM.

🧩 Agentic assistant in VS Code — WUIC Assistant

The package now includes a plugin for Visual Studio Code, WUIC Assistant (llm-workspace/plugin/wuic-assistant.vsix): an assistant that already knows the framework conventions and works directly on the open project. It generates Angular components (cards, dashboards with KPI tiles, list-grids with navigation to the edit form), components fed by a custom .NET endpoint, and proposes metadata changes (conditional styles, table and row actions, lookups). Every write goes through a preview before confirmation.

It uses the same local WUIC RAG via the wuic-rag MCP server (started automatically) and the grounding already present in the project, so no manual MCP server setup is required. The LLM model is your choice — local via Ollama (Qwen, zero API key) or Anthropic.

Install from the ZIP:

code --install-extension llm-workspace/plugin/wuic-assistant.vsix

Alternatively, install-llm-workspace.ps1 installs it. Then Ctrl+Shift+P -> WUIC Assistant: Open Chat; choose the provider in settings (wuicAssistant.provider = ollama or anthropic).

🧊 Scene3D Designer (new)

A new visual 3D designer on route #/scene3d_designer, published read-only through the Scene3D Viewer (#/scene3d_viewer/:scene_key). It lets you compose a three-dimensional scene and bind its objects to app data.

  • Palette and import: primitives (cube, sphere, plane, cylinder, cone, torus), groups, lights, camera, 3D text, and Mesh Repeater (instances generated from data). Import of external models in glTF/GLB, OBJ, FBX, STL, and DAE. The palette is extensible from metadata with custom types.
  • PBR materials: metalness, roughness, emissive, opacity, wireframe, flat shading, and face sides; for the physical material also transmission, IOR, thickness, and volumetric attenuation (colored glass).
  • Shader effects: an effect described in JSON (schema-backed, with completion and a "structure" view) is compiled for the active renderer; alternatively, hand-written GLSL shaders on the WebGL renderer.
  • Lighting: scene lights with soft shadows, baking of static lighting into vertex colors (unlit), and — on the WebGL renderer — a photorealistic preview path tracer.
  • Animation and physics: transport controls for imported-asset clips; optional per-object physics with Play/Stop simulation in the designer and autoplay in the viewer.
  • Data binding: each object binds to a WUIC route (with optional record) and maps visual properties (label, color, visibility) to columns; double-clicking a bound object in the viewer opens the record's CRUD.
  • Automatic thumbnails: on save the scene is captured from the canvas and shown as a preview in the "Load scene" list, with no configuration or external process.

The designer and viewer routes require the scene3d-designer feature. The supporting tables are created and updated automatically on first use, across all supported databases.

🖥️ WebGPU renderer (opt-in)

Scene and viewer rendering is now selectable between WebGL (default) and WebGPU (toggled from the toolbar). When WebGPU is unavailable in the browser, the designer stays on WebGL automatically. The chosen mode is saved with the scene and restored on open. With the WebGPU renderer active, light baking runs on the GPU (shadows included), much faster on dense scenes; hand-written GLSL shaders and path tracing remain available on the WebGL renderer.

🔀 Workflow Designer — assisted authoring

The workflow designer (#/workflow-designer) now guides building a process from scratch.

  • Starter templates: "New from template" generates a ready-made graph for common patterns (simple approval, claim/release queue, threshold chain, parallel tasks): you pick the main route and — where needed — the status field, and the graph, actions, and transitions are created already wired.
  • Graph validation: "Validate graph" flags problems before saving (start with no outlets, unreachable nodes, action without target, empty condition, dead branch, incomplete timer or split, permission with a missing role). Clicking a finding frames the node on the canvas. Saving is never blocked: with open issues a summary appears with "Save anyway".
  • Guided configuration: the timer and parallel-task dialogs use dropdowns and route autocompletion instead of free-text fields typed from memory.
  • Onboarding and help: a first-steps checklist on an empty canvas, descriptive palette tooltips, and a "Quick guide" with a legend of shapes and a glossary of concepts (transition, guard, permission, internal action).

🎨 Dashboard Designer — faster editing

  • Snap to grid: toggled from the designer actions menu, it shows the grid on the canvas and automatically aligns dragging, resizing and palette drops. When enabled, the elements already on the canvas are aligned to the grid as well.
  • Normal / absolute flow: a new flag in the actions menu (default: normal flow, no change for existing dashboards). In absolute mode, dropped elements are positioned at the drop coordinates, outside the flow: resizing one does not move the others. Dropping into a container uses the container as the position reference, and the runtime automatically recognizes dashboards saved in this mode.
  • Keyboard shortcuts: Del/Backspace deletes the selected element, arrow keys move it, Ctrl+Z/Ctrl+Y undo/redo. Dragging a selection rectangle from an empty canvas area selects multiple elements: arrows and Del act on the whole selection.
  • JSON and preset import/export: the current dashboard can be exported as a re-importable JSON file (identical to the persisted content), useful to move layouts between environments. Presets save reusable layouts under a name and re-apply in one click.
  • Move between tabs: from the context menu of an element inside a tab, Move to new Tab creates a new tab and migrates the element there (bindings and state preserved); Move to another tab — available when the tabview has multiple tabs — moves it to an existing tab of your choice. The target tab is activated automatically, as is a freshly dropped tab.
  • Import dashboard/preset into an element: from the context menu of a container, a saved dashboard or a preset can be imported directly inside the element; the identifiers of the imported elements are regenerated and internal references (datasources included) remapped, with no collisions with the existing content.

🐛 Notable bug fixes

  • Designer — multi-column layout: injecting a multi-column/multi-area layout (e.g. "3 columns, each with a grid") proposed by the chatbot now populates all areas correctly. Previously, after the first cell, the following ones were not resolved and the components stayed empty.
  • Chatbot — route whitelist: when asking to bind a component to a route with an inexact name (e.g. "provincie" for "stateprovinces"), the chatbot now performs the semantic match and proposes the action, instead of wrongly replying that the route list is still loading.
  • 3D viewer — navigating between scenes: opening different scenes in sequence from the same viewer now loads each scene correctly. Previously the viewer could keep showing the first opened scene.
  • Schema-backed JSON editor: the code editor in JSON mode now offers a "structure" view (toggled with a switch) to add and remove typed properties driven by the schema, without writing JSON by hand.

🔧 Recommended operational updates for upgraders

  1. To use a free local LLM, set in appsettings.json -> AppSettings: rag-llm-provider=ollama, rag-llm-base-url, rag-llm-api-key (placeholder value, e.g. ollama) and rag-llm-default-chat-model.
  2. Migrate the chatbot key to rag-llm-api-key: the previous llm-api-key and anthropic-api-key keep working as fallback, but the recommended configuration uses only rag-llm-api-key.
  3. For the VS Code assistant, install the plugin from the ZIP: code --install-extension llm-workspace/plugin/wuic-assistant.vsix (or let install-llm-workspace.ps1 install it).
  4. To use the Scene3D Designer, enable the scene3d-designer feature in the active license. Support tables are created and migrated automatically on first use; the WebGPU renderer is opt-in from the toolbar, with automatic fallback to WebGL.

v1.3.2

Back to index

Previous published version: 1.3.0 (11 June 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


A consolidation release on the RAG chatbot introduced in 1.3.0: the conversational model is no longer tied to Anthropic — any OpenAI-compatible endpoint, including local runtimes such as Ollama with open models (Qwen), is now configurable and runs without an API key. Alongside this, a set of fixes to the first-run installer, the source package and metadata scaffolding that surfaced on fresh installations, plus a workspace ready for AI coding assistants.


🤖 RAG Chatbot — flexible LLM provider (including local and free)

The chatbot's conversational model is now provider-agnostic. In addition to Anthropic, OpenAI-compatible endpoints are supported, which includes local runtimes (e.g. Ollama): you can run open, free models such as Qwen on your own machine, without an API key and with no per-token cost.

  • rag-llm-provider — anthropic (default) / openai / openrouter. Selects the provider's wire dialect.
  • rag-llm-base-url — endpoint override; pointing it at a local server URL (e.g. http://localhost:11434/v1 for Ollama) makes the chatbot talk to the model locally.
  • rag-llm-default-chat-model — model id for the chosen provider (e.g. a Qwen model on Ollama).
  • llm-api-key — key for the active provider; for local runtimes that don't validate it, a placeholder value (e.g. ollama) is enough. The legacy anthropic-api-key remains valid when rag-llm-provider=anthropic (zero migration).

All keys are hot-reloaded from appsettings.json: switching provider or model requires no restart.

More accurate retrieval — result re-ranking has been refined: the chatbot cites more relevant sources on natural-language queries.

Setup notifications — on first use the .NET engine downloads the ONNX models on demand. The administrator now receives started / ready / error notifications for the download in the bell, across all four DB providers, even when initialization is triggered by a request with no logged-in user.

Automatic GPU acceleration — on a machine with an NVIDIA GPU the engine uses the GPU without installing CUDA: on first launch, besides the ONNX models, it also downloads the required CUDA 12 + cuDNN 9 runtime on demand (~1.8 GB, one time, only if a GPU is present) and wires it up itself. Without a GPU → CPU, no extra download. Manual override with rag-engine-cuda-path.


🧩 Workspace ready for AI coding assistants

Applications generated with the framework now include a set of markdown context files (project description, conventions, operating rules) at the workspace root. These files make agentic AI assistants — Continue, Cline, Cursor and similar — immediately aware of the WUIC structure and conventions, with no proprietary extension to install. Any client that reads the workspace context behaves as a "WUIC-native" assistant.


🐛 Notable bug fixes

  • First-run installer — non-tutorial mode on every DB provider: installing with scaffolding of an existing database (without the tutorial sample data) has been fixed and unified across all supported providers — SQL Server, MySQL, PostgreSQL and Oracle. Resolved the failures caused by SQL dialect differences, target database/schema selection and connection handling that surfaced outside tutorial mode.

  • First-run installer — SQL script path (non-BAK): when provisioning the metadata DB via the incremental SQL script (the alternative to restoring from a .bak), the parser for GO-separated batches mishandled some separators, causing schema creation to fail on fresh installations. The splitter has been fixed and script-based installs now complete cleanly.

  • Source package — .NET RAG engine not found at runtime: in the source package (-src-) the WuicRagEngine.dll engine was placed at the package root, while the executable, started from bin/, looked for it next to itself — the RAG chatbot would not start ("WuicRagEngine.dll not found"). The loader now searches the rag-engine/ folder in several locations (build output, content root, working directory) and finds the engine in both deploy layouts.

  • First-run — chatbot API key persistence: the LLM key entered in the first-install wizard is now written to the canonical appsettings.json actually read by the runtime. Previously, in some layouts, it could land in a copy the process never reads, leaving the chatbot without a key right after install.

  • Metadata scaffolding — diagnostics and robustness: scaffolding the metadata for certain tables could fail with a generic message ("Unable to scaffold metadata table") that masked the real cause. The actual SQL error now propagates to the caller, and the case that triggered it is fixed.

  • Source package — realtime notifications in dev: in the -src- package the dev-server (ng serve) proxy did not forward WebSocket connections to the backend; the notification channel (/ws) timed out and updates only appeared after a manual page reload. The proxy now forwards WebSockets too: notifications arrive in real time.


📦 Updated packages

Package From To
WuicCore 1.3.0 1.3.2
Wuic.Webcore 1.3.0 1.3.2
WuicOData 1.3.0 1.3.2
RuntimeEfCore 1.3.0 1.3.2
Wuic.MySqlProvider 1.3.0 1.3.2
Wuic.PostgresProvider 1.3.0 1.3.2
Wuic.OracleProvider 1.3.0 1.3.2
wuic-framework-lib (NPM) 1.3.0 1.3.2

🔧 Recommended operational updates for upgraders

  1. To run the chatbot with a local, free model (e.g. Qwen via Ollama): set rag-llm-provider=openai, rag-llm-base-url to the local endpoint (e.g. http://localhost:11434/v1) and rag-llm-default-chat-model to the model id; set llm-api-key to a placeholder (e.g. ollama) if the runtime doesn't validate it. No restart: the keys are hot-reloaded.
  2. To stay on Anthropic, no action is needed: anthropic-api-key keeps working with rag-llm-provider=anthropic (default).
  3. The source (-src-) package is lighter: it no longer includes the redundant framework DLLs at the root, which are recreated by dotnet build from the NuGet packages. Downloading the new -src- requires no action.
  4. On first chatbot use with the .NET engine, the administrator will see the ONNX model download progress in the bell. Wait for the "ready" notification before the first Ask.
  5. New apps generated by the framework automatically include the AI-assistant context files at the workspace root; for existing apps they can be regenerated.

v1.3.0

Back to index

Previous published version: 1.2.1 (31 May 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


Minor release focused on the RAG chatbot integration on the framework side: persistent conversation history, automatic context management, hot-reload configuration from appsettings.json and cross-DBMS schema auto-applied at first start. Alongside the main feature, a few metadata scaffolder and chat repository robustness fixes that surfaced in fresh DB provisioning scenarios.

The chatbot is the first WUIC component with server-side state (_rag_chat_sessions + _rag_chat_messages) that spans all four supported providers without manual schema configuration. The first Ask detects the provider, applies the incremental SQL patches in order and starts up. With this release the serving stack can also run natively on .NET (in-process ONNX engine), making the customer deployment independent of Python.


🤖 RAG Chatbot — end-to-end context management

The <wuic-rag-chatbot> component now persists multiple sessions per user, with full conversation history, automatic context summarization and appsettings.json configuration. The feature is opt-in: without anthropic-api-key configured the chatbot stays inactive.

Sessions

  • Conversation history persisted per user. The session survives browser reloads and route changes.
  • Sessions popup ordered by updated_at descending, with title derived from the first prompt (truncated at 100 chars + full tooltip).
  • Inline rename with immediate persistence.

Automatic context management

  • Visual cue % in the chatbot header: a colored circle showing the model's context window usage (green <60% / yellow 60-80% / orange 80-90% / red >90%). The value comes from the tokens actually consumed by the Anthropic API and is persisted per turn, so it survives reload.
  • Auto-compact pre-Ask: when the conversation exceeds the configurable threshold (default 30 turns) and at least 10 turns are not yet summarized, the backend triggers a best-effort compact in the background before the next Ask. The refreshed summary is injected into the system prompt for future turns.
  • On-demand compact: the user can force a compact via the slash command /compact or by clicking the cue circle.
  • Memory facts: the model itself can "pin" high-priority facts via tool use (remember_fact/forget_fact). Facts stay in the system prompt even after a compact (max 20, FIFO eviction).
  • Follow-up questions: the model suggests up to 3 follow-up questions, rendered as clickable chips under the response. Click = pre-fills the input box (does not auto-send).

appsettings.json configuration

  • anthropic-api-key — Anthropic API key, hot-reload. Not hard-coded, never commit to repo.
  • anthropic-default-chat-model — claude-haiku-4-5-20251001 (200k, default) / claude-sonnet-4-5-20250929 / claude-opus-4-5. Drives the context window and the visual cue.
  • anthropic-auto-compact-threshold — integer >=0, default 30. Set to 0 to disable auto-compact (manual /compact remains available).

Cross-DBMS auto-migration

The chat history schema (5 incremental patches) is applied idempotently at the first Ask, on the configured provider (MSSQL / MySQL / PostgreSQL / Oracle). No DBA step required on existing installs.


🛠️ Actions the chatbot can apply to your project

Beyond answering in natural language, the chatbot can propose concrete changes to your project as action chips with an "Apply" button. Each chip shows what it will do (target route, generated code, rationale) and the user decides whether to apply it. Nothing is executed without an explicit click.

Supported action types:

  • Toolbar and row actions — adds custom buttons to a <wuic-list-grid> toolbar or to single-row actions, with generated JavaScript callbacks. Examples: "add an action that exports selected rows to CSV", "put an Approve button on each row".
  • Conditional row and column styles — applies CSS classes to a row or to a single cell based on a JS condition. Examples: "highlight rows with overdue deadline in red", "set green background on the status cell when it equals 'OK'".
  • Column display formula — replaces a column's list representation with a custom HTML/Angular template (badge, icon, link, colored percentage). Example: "show priority as a green/yellow/red colored badge".
  • Form title formula — dynamically computes the edit-form title of a record from its content. Example: "title should be Customer {company_name}".
  • Default value and custom validation — generates callbacks for default values on form open (field pre-fill) or for complex validation (cross-field, custom regex). Examples: "default created_at = today", "validate that email ends with @company.it".
  • Selection-changed and lifecycle callbacks — hooks on form events (record selection change, before-save, after-save, after-delete) for custom side-effects: refresh linked datasources, notifications, application-level audit log.
  • Metadata changes — applies direct edits to table/column metadata (caption, ordering, hide in list/edit, basic validations) without going through the manual metadata editor.
  • SQL snippets in metadata (super-admin) — writes raw SQL fragments to metadata fields concatenated at runtime in auto-generated queries: custom JOIN on the route, custom SELECT clause on a column, computed column formula, lookup display expression. Examples: "compute total on orders as price × quantity", "add join to payments on invoice_id". The chatbot knows the active provider dialect (mssql/mysql/postgres/oracle) and generates SQL with the correct quoting/syntax. Gated D3 operation: requires super-admin privileges server-side, with automatic audit log on _error__logs for every apply.

🎨 New action: dashboard layout from natural language

When the user is on the Designer page of a dashboard, the chatbot exposes a new family of actions that operate directly on the designer canvas (not on persisted metadata).

Supported prompt patterns:

  • "add a grid bound to route cities" → injects DATASOURCE + DATAREPEATER configured and bound;
  • "create a 2×2 table layout" → injects a 2×2 <table> with cells ready to receive other components;
  • "put a vertical splitter with 3 areas" → injects a configured SPLITTER;
  • "change the top-right pane background to red" → modifies the backgroundColor property of the identified component;
  • "add a column to the table" / "remove row 2" → modifies cols/rows of the selected TABLE component;
  • "remove the Revenue KPI" → deletes a component from the canvas by name.

The chatbot knows the full catalog of 31 designer tools (HTML, DATA, CONTAINER groups) and their editable properties. When the user mentions a metadata route with an approximate name ("provincies" instead of "stateprovinces"), the chatbot fuzzy-matches the available routes in your project and shows the resolved real name in the action rationale.

Changes stay on the designer canvas until the user clicks "Save dashboard" — no automatic DB writes, the visual outcome is always reviewed before commit. The designer's undo/redo also covers chatbot-injected actions.


⚙️ Native .NET RAG engine (Python-free deployment)

The RAG chatbot serving stack can now run entirely on .NET, with no separate Python server or virtual environment on the target machine. The retrieval models (embeddings + reranker) are loaded in-process via ONNX Runtime, with GPU (CUDA) acceleration auto-detected and transparent CPU fallback.

  • Activation via appsettings.json: rag-use-dotnet-engine=true selects the .NET engine; the default false keeps the previous behavior.
  • rag-engine-device (auto / cpu / cuda) selects the inference device; rag-engine-profile controls the redaction level of the sources cited in answers.
  • On first startup the required artifacts (ONNX models + index) are downloaded on demand, so the base package stays lightweight.

Practical result: the customer deployment is .NET only — no Python install nor extra native dependencies beyond the .NET runtime. The conversational model call and the retrieval and actions pipeline are identical across both engines.


🐛 Notable bug fixes

  • Callback documentation aligned with the runtime: the callback cookbook described signatures that did not match the actual behavior in two cases. The default value callback writes the value into the record (record[field.mc_nome_colonna] = ...) and the return is ignored; custom validation receives (record, field, vr, wtoolbox) and reports the outcome with a boolean return (false blocks the save) plus vr.message for the displayed text. The previous examples, based on validateResult(...) and on a return for the default value, produced callbacks that did not apply. Documentation corrected in all five languages.

  • Reliability of chatbot-proposed actions: for action requests the chatbot now deterministically emits the matching action chip, and automatically retries on a transient rate-limit of the conversational model instead of silently degrading to a text-only answer.

  • Metadata scaffolder — date vs datetime distinction consolidated: follow-up of the fix introduced in 1.2.1 on generated temporal types. The source-type parser now also covers atypical DDL variants (MySQL DATETIME(0) without precision, PostgreSQL bare timestamp without time-zone qualifier, Oracle TIMESTAMP(n) with explicit precision) — they all continue to map correctly to UI type datetime while preserving the time component at save.

  • Metadata field suggest — mc_suggest_value_callback now normalizes the return value: the DB-configured callback could return a promise or a sync value, but the runtime parser only accepted the sync case. Result: suggest silently failed inside async callbacks. The normalization now awaits Promise.resolve(callback(...)) uniformly.

  • Chat repository — cross-driver Guid: the MySQL.Data driver materializes a CHAR(36) column as Guid when the OldGuids flag is false (default starting from connector version 6.6), causing InvalidCastException on GetString. Same risk on Oracle with RAW(16) storage. The correlation id read now has a fallback cascade (GetGuid → GetString → GetValue with runtime-type switch) — robust on all four providers regardless of driver configuration.

  • Chat repository — MySQL connection not open: the MySQL gateway returned a new MySqlConnection(cs) without calling Open(), asymmetrically with the PostgreSQL and Oracle gateways. The first ExecuteNonQueryAsync of the schema auto-apply failed with "Connection must be valid and open". Added a symmetric OpenConnectionToConnectionString, aligned with the other providers.


📦 Updated packages

Package From To
WuicCore 1.2.1 1.3.0
Wuic.Webcore 1.2.1 1.3.0
WuicOData 1.2.1 1.3.0
RuntimeEfCore 1.2.1 1.3.0
Wuic.MySqlProvider 1.2.1 1.3.0
Wuic.PostgresProvider 1.2.1 1.3.0
Wuic.OracleProvider 1.2.1 1.3.0
wuic-framework-lib (NPM) 1.2.1 1.3.0

🔧 Recommended operational updates

  1. To enable the RAG chatbot, add the anthropic-api-key key (and optionally anthropic-default-chat-model and anthropic-auto-compact-threshold) to appsettings.json. The backend reads the keys in hot-reload — no restart needed.
  2. No DBA step required on existing installs: at the first chatbot Ask, the chat history schema (_rag_chat_sessions + _rag_chat_messages with all columns) is applied idempotently on the provider configured in MetaDataSQLConnection. Auto-migration covers fresh and partially migrated installs.
  3. If the install runs on MySQL / PostgreSQL / Oracle, verify the connection string points to the correct provider and the user has ALTER TABLE privileges on the metadata schema (needed only once, at the first start).
  4. To monitor context window usage, the cue % circle in the chatbot header is the immediate visual driver. Above 80% it is worth running a manual compact (/compact or click the cue) to reduce latency on subsequent turns.
  5. To run the RAG chatbot without Python on the target machine, set rag-use-dotnet-engine=true in appsettings.json (optionally rag-engine-device and rag-engine-profile). On first startup the inference artifacts are downloaded automatically.

v1.2.1

Back to index

Previously published version: 1.2.0 (27 May 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


Maintenance release focused on a class of latent bugs affecting datetime and decimal fields under cross-DBMS / cross-locale scenarios. Most users on Italian-locale workstations have been hit at least once — the time component of timestamps was being truncated to midnight on INSERT and UPDATE, and decimals with non-invariant separators produced ORA-01722 on Oracle whenever the ODP.NET session inherited Windows' Italian culture.

The fixes are transverse to the 4 supported providers (MSSQL, MySQL, PostgreSQL, Oracle) and all end-to-end roundtrip tests pass under both English (en-US) and Italian (Italiano dmy, lc_time=Italian_Italy.1252) DB session locales.


🐛 Notable bug fixes

  • time component truncated on INSERT/UPDATE of DATETIME2 / DATETIME(n) / TIMESTAMP fields: the metadata scaffolder collapsed every temporal source-DB type onto the single UI type date. As a result, a SQL Server DATETIME2(3) column (or MySQL DATETIME(3), PostgreSQL timestamp without time zone, Oracle TIMESTAMP(0)) was treated as a pure date, and the framework emitted '20261231' instead of '20261231 23:59:58' on INSERT/UPDATE — the time entered through the UI was lost on save. The scaffolder now distinguishes date (pure date) from datetime (date + time) and the save preserves the time component down to second precision. Sub-second precision (.fff) remains intentionally truncated to keep consistency with the UI date-time picker, which does not expose it.

  • Oracle ORA-01722: invalid number on NUMBER(p,s) fields from Italian-locale workstations: the providers were emitting numeric values quoted as strings in INSERT/UPDATE (e.g. VALUES (..., '9876.4321', ...)). Oracle then converted the string to a number using the session's NLS_NUMERIC_CHARACTERS, which ODP.NET derives from the .NET thread CurrentCulture: under Italian culture the decimal separator is , and . becomes the group separator → '9876.4321' was parsed as an invalid group expression. Numeric values (decimal, float, double, numeric) are now emitted as unquoted SQL literals: Oracle numeric literals always use . as the decimal point regardless of NLS.

  • Oracle ORA-00904: invalid identifier on tables with quoted-lowercase identifiers: a table created with DDL CREATE TABLE "my_table" ("id" NUMBER, ...) (lowercase quoted, case-preserving) was unreadable by the framework. The quoting logic recognised mixed-case and reserved keywords but treated all-lowercase identifiers as "safe" and emitted them bare (Oracle case-folds bare identifiers to UPPER), causing a mismatch with the physical "id". All-lowercase identifiers are now preserved with explicit quoting.

  • Locale-invariant parsing/formatting of dates and timestamps server-side: the DateTime parse/emit path on Oracle and PostgreSQL was using the thread CurrentCulture. Parsing now tries InvariantCulture first and falls back to CurrentCulture only when needed; formatting for SQL clauses (TO_TIMESTAMP(...) / yyyy-MM-dd HH:mm:ss literal) always uses InvariantCulture. User-visible effect: the round-trip remains bit-perfect regardless of the backend process CultureInfo.CurrentCulture.

  • Oracle ORDER BY on lowercase PK: the automatic ORDER BY clause on the primary key was emitting the column name without going through the quoting logic → ORA-00904 on tables with PK "id" lowercase quoted. The PK now follows the same quoting path as every other column.


🗄️ Cross-locale DB compatibility

End-to-end roundtrip tests now cover the following provider × DB session combinations:

Provider Tested DB session Result
MSSQL @@LANGUAGE=Italian, date_format=dmy, Latin1_General_CI_AS OK
MySQL lc_time_names=en_US, utf8mb4_0900_ai_ci, time_zone=SYSTEM OK
PostgreSQL DateStyle=ISO,DMY, lc_time=Italian_Italy.1252 OK
Oracle NLS_LANGUAGE=AMERICAN, NLS_TERRITORY=AMERICA, NLS_NUMERIC_CHARACTERS=., OK

Date values are asserted invariant end-to-end (2026-12-31T23:59:58.000 stays 2026-12-31T23:59:58.000 regardless of DB session and backend CurrentCulture), as are decimals (9876.4321 stays 9876.4321).


📦 Updated packages

Package From To
WuicCore 1.2.0 1.2.1
Wuic.Webcore 1.2.0 1.2.1
WuicOData 1.2.0 1.2.1
RuntimeEfCore 1.2.0 1.2.1
Wuic.MySqlProvider 1.2.0 1.2.1
Wuic.PostgresProvider 1.2.0 1.2.1
Wuic.OracleProvider 1.2.0 1.2.1
wuic-framework-lib (NPM) 1.2.0 1.2.1

v1.2.0

Back to index

Previous published version: 1.1.0 (13 May 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


This release extends the framework to two new DBMS — PostgreSQL and Oracle — and fixes a Spreadsheet filter bug that surfaced on routes with server-side operations enabled when the column was a lookup.

  • PostgreSQL provider and Oracle provider: both installable as drop-in (postgresql.dll / oracle.dll alongside WuicCore.dll), usable as either data store or metadata store, with feature parity vs MSSQL and MySQL.
  • Spreadsheet filter on lookup columns in server-side mode: the popup now shows the lookup descriptors (e.g., Woodgrove Bank Crandon Lakes) and applies the filter using the foreign-key ID, eliminating the SQL error that strict-typing providers raised.

🗄️ PostgreSQL provider

Drop-in compatible with PostgreSQL 14+ (tested on 16). Installation: drop postgresql.dll next to WuicCore.dll in the IIS site physical path, or in the Linux binary's publish directory. The firstRun setup wizard automatically exposes "PostgreSQL" in the DBMS dropdown when it detects the DLL.

Functional coverage. All core framework surfaces operate natively on PG with the same semantics as the MSSQL/MySQL releases: CRUD, server-side paging, sorting, grouping, aggregations, lookup autocomplete, OData, scheduled jobs, audit, notifications, retry policy, optimistic concurrency, validations, callbacks/events, XLS import/export, PDF export, multi-tenant.

PG-specific types supported. boolean (mapped automatically from/to internal smallint storage used for parity with MSSQL/MySQL), varchar/text, numeric, integer/bigint, timestamp, date, bytea (binary upload), geometry (PostGIS — map display via ST_AsText).

Files preconfigured in the package.

  • appsettings.postgres.json / appsettings.linux.postgres.json / appsettings.multi-tenant.postgres.json — self-contained environments ready to use, activatable with ASPNETCORE_ENVIRONMENT=postgres.
  • dbms/scripts/first-run/*.postgres.sql — metadata bootstrap + WideWorldImporters tutorial DDL/DML.

🗄️ Oracle provider

Drop-in compatible with Oracle 19c / 21c / Free 23c. Installation oracle.dll follows the same pattern as the PostgreSQL provider; "Oracle" appears in the firstRun dropdown automatically.

Functional coverage. Identical to PostgreSQL — all core surfaces with the same semantics as MSSQL/MySQL releases.

Identifier length. Oracle 11g/12.1 (30 char max) is not yet supported — the lookup aliases generated by the framework exceed the limit. Oracle 12.2+ (128 char) is the support floor.

Files preconfigured in the package.

  • appsettings.oracle.json / appsettings.linux.oracle.json / appsettings.multi-tenant.oracle.json.
  • dbms/scripts/first-run/*.oracle.sql — metadata bootstrap + tutorial.

🐛 Notable bug fixes

  • Spreadsheet popup filter on lookup columns when md_server_side_operations=true: the column funnel popup of <wuic-list-spreadsheet> on a lookupByID column displayed raw numeric IDs (e.g., 1, 4, 5) instead of descriptors (e.g., Woodgrove Bank Crandon Lakes). On PG/Oracle the actual filter application produced an SQL error (42601 ilike %% on PostgreSQL, ORA-00904 on Oracle) because the client transmitted the descriptor string against the numeric FK column. The server now emits the joined descriptor (<entity>___<dataTextField>__<colName>) alongside the FK ID, and the client displays the descriptor in the popup but transmits the raw ID as the filter value: the WHERE col = <id> stays numeric and cross-DBMS-safe. No action required on the consumer side.

📦 Updated packages

Package From To
WuicCore 1.1.0 1.2.0
Wuic.Webcore 1.1.0 1.2.0
WuicOData 1.1.0 1.2.0
RuntimeEfCore 1.1.0 1.2.0
Wuic.MySqlProvider 1.1.0 1.2.0
Wuic.PostgresProvider — 1.2.0
Wuic.OracleProvider — 1.2.0
wuic-framework-lib (NPM) 1.1.0 1.2.0

🔧 Recommended operational updates for upgraders

  1. For MSSQL or MySQL users: no action required. The Spreadsheet filter fix applies to all providers transparently after the first client refresh.
  2. To enable PostgreSQL: copy postgresql.dll (along with its runtime dependencies — Npgsql.dll, Npgsql.EntityFrameworkCore.PostgreSQL.dll, Microsoft.Extensions.Logging.Abstractions.dll) into the IIS site physical path or the Linux publish directory, restart the backend. Select PostgreSQL in the firstRun wizard, or point ASPNETCORE_ENVIRONMENT=postgres to use the preconfigured appsettings.postgres.json.
  3. To enable Oracle: same procedure — oracle.dll + Oracle.EntityFrameworkCore.dll + Oracle.ManagedDataAccess.dll. Verify the target DB version is ≥ 12.2 (identifier length constraint).
  4. Client cache: after upgrading, a hard browser refresh (Ctrl+F5) is sufficient to align the client with the new popup filter contract. No server-side metadata invalidation required.

v1.1.0

Back to index

Previously published version: 1.0.20 (12 May 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


Minor bump: this release introduces two structural capabilities that change the framework's deployment model.

  • Multi-tenant: a single framework instance routes data and metadata for N companies across N different DB connections. Per-tenant configuration via columns Aziende.Connessione_DB_Dati / Aziende.CONNESSIONE_DB_Meta; transparent application-level routing via TenantContext (AsyncLocal, survives Task/scheduler boundaries).
  • Per-language menu localization: menu entries (mm_display_string_menu) no longer contain hard-coded Italian labels but stable namespaced keys menu.<scope>.<slug>, resolved at runtime by Angular's translate pipe against _wuic_translations. Switching language from the user selector updates all entries without F5.

🌐 Multi-tenant management

A single framework installation can now serve multiple companies ("tenants") with data and metadata physically isolated on different DBs, with no need to replicate the application nor partition reverse-proxies per host.

Data model. The tenant→connections routing is defined on two columns of the primary metadata DB:

  • Aziende.Connessione_DB_Dati — name of an entry in ConnectionStrings for the tenant's application DB
  • Aziende.CONNESSIONE_DB_Meta — name of an entry in ConnectionStrings for the tenant's metadata DB

The columns contain the entry name, not the literal string. Credential rotation happens by editing appsettings.<env>.json, without touching the DB.

Activation. Flag in appsettings.json (AppSettings section):

"multiConnectionEnabled": "true"

With the flag false (default), behavior remains single-tenant, identical to previous releases. With the flag true, every authenticated HTTP request resolves the AziendaId from the logged-in user and routes GetOpenConnection to that tenant's connection strings.

Transparent routing. All framework DB access points (MetaService.*, scheduler, scaffolding, AsmxProxy CRUD, custom callbacks) consult TenantScope.CurrentAziendaId via AsyncLocal, propagated by the HTTP middleware post-authenticate. Background jobs and custom callbacks declare the tenant explicitly with using (TenantScope.Push(aziendaId)) { ... } when running outside the request context.

Tenant-aware cache. Server-side Application[] keys and local metadata caches are automatically suffixed by AziendaId when the flag is active, preventing metadata bleed between tenants.

Login routing. Table _login_index(username_hash, id_azienda) on the primary DB maps username → tenant for the MetaService.login fallback: after authentication, the k-user cookie carries azienda_id as part of the payload and the middleware creates the correct TenantScope on every subsequent request.

Scaffold propagation. The "Scaffold table" action idempotently propagates table metadata to all tenants listed in Aziende. The propagation runs with an explicit TenantScope on each target and is idempotent: re-runnable, applies only missing changes.

Files shipped in the package:

  • appsettings.multi-tenant.mssql.json / appsettings.multi-tenant.mysql.json — self-contained environment with 6 sample connection strings (1 primary + 5 tenants) and multiConnectionEnabled=true. Activate with ASPNETCORE_ENVIRONMENT=multi-tenant.mssql.
  • dbms/scripts/multi_tenant_aziende_connessioni_mssql.sql / _mysql.sql — DDL to add the two columns to Aziende on existing DBs.

🗺️ Per-language menu localization

Menu entries are now translated dynamically according to user language, without needing to duplicate _metadati__menu records per locale.

Architecture. The mm_display_string_menu field of _metadati__menu holds a stable namespaced key (menu.admin.roles, menu.crm.opportunities, menu.fleet.vehicles, ...). The Angular menu component template applies the translate pipe on item.label, and the key is resolved at runtime from the _wuic_translations dictionary filtered by current language.

Key schema.

menu.<scope>.<slug>
   │       └── snake_case slug (e.g. column_styles, opportunities)
   └── scope = root | admin | demo | crm | fleet | invoice
  • menu.root.* — top-level parents (Administration, Application, Home, ...)
  • menu.admin.* — 36 shared system entries (Roles, Designer, Column Styles, Workflow Designer, ...)
  • menu.demo.* — WideWorldImporters demo content
  • menu.crm.* / menu.fleet.* / menu.invoice.* — tenant-domain-specific entries

Advantage over the previous model.

  • The old model used the Italian text of the label as the translation key (Aziende, Customers, Ruoli). This caused silent case-mismatches (Ruoli vs ruoli, Stili Tabella vs Stili tabella) because the translate pipe is case-sensitive while _wuic_translations has case-insensitive collation: the first MERGE that landed fixed the casing forever, and subsequent case-divergent INSERTs became silent no-ops.
  • The new stable-key model is case-determinate (lowercase by convention), namespaced by scope, and no longer collides with other resources that might use the same Italian text (e.g. a button label "Ruoli" in a dropdown is a different key from menu.admin.roles).

5 supported languages. it-IT, en-US, fr-FR, es-ES, de-DE. Translations live in _wuic_translations (standard format: language, resource, translation). Switching language from the user dropdown in the top right re-reads the dictionary for the new language and re-paints the menu without F5.

Runtime fallback. Current language → en-US → it-IT → raw key. If you see menu.admin.roles literal on screen, it means the key has not been seeded in any of the 5 languages.

Old Italian keys in _wuic_translations are not touched by the upgrade: they may be consumed by other points in the app (instant('Aziende') in code-behind, list-grid headers, page titles) and remain valid.


🐛 Notable bug fixes

  • Dynamic edit forms — Tabs and widgets in md_edit_template templates in production: in production builds the custom HTML templates bound to a route via md_edit_template failed to render PrimeNG 21 Tabs correctly (labels appeared as concatenated plain text without component chrome) and field-editors showed only <!----> placeholders instead of inputs. Cause: the runtime compiler used by the framework for dynamic templates requires explicit enumeration of the standalone components available in the template, and MetadataProviderService.widgetDefinition.dynamicFormImports was incomplete. Added to the baseline TabsModule + Tabs/TabList/Tab/TabPanels/TabPanel, FieldsetModule, DataRepeaterComponent, DataSourceComponent, ImageWrapperComponent. No action required on consumer apps once the wuic-framework-lib package is updated.

🎁 Free apps now available

Starting with this release, three complete applications ship for free on top of the framework — available from the "Free apps" section of the Downloads page:

  • CrmApp — Self-hosted B2B CRM: customer registry, opportunity pipeline with drag-and-drop kanban, activities (calls / meetings / emails), role-based dashboard. (Read the post)
  • FatturazioneElettronica — Italian e-invoicing: FatturaPA v1.2 invoice editor, CADES-BES signature, XSD validation, 4 interchangeable SDI providers (DirectPec free via PEC, ArubaPec / FatturePec / PecIt commercial), legal conservation, IVA registers and liquidation. (Read the post)
  • FlottaMezzi — Fleet management: vehicle / driver registry, automatic deadlines (road tax / inspection / insurance / service / driver license), OBD/GPS geolocation feed, live map, €/km cost roll-ups per vehicle and per driver, TCO reporting. (Read the post)

Each app ships in three formats: IIS ZIP with tutorial DB (ready to restore), IIS ZIP without DB, source ZIP.

License model. The free apps are FREE as-shipped — the <App>.dll binary in the ZIP carries an embedded host-binding-license resource that authorizes the framework runtime without any external key. Only if you rebuild the app from source (for example to add a new controller or change a public signature) do you need a WUIC Developer or Professional license: recompilation produces a binary with a different identity, loses the bundling, and the framework falls back to the standard fingerprint license check.

Extending the free apps without recompiling the binary is covered by the bundling: adding metadata via SQL, Angular components in the wwwroot, jobs in the scheduler table, custom hooks via appsettings.json:customCrudHookClass.


📦 Updated packages

Package From To
WuicCore 1.0.20 1.1.0
Wuic.Webcore 1.0.20 1.1.0
WuicOData 1.0.20 1.1.0
RuntimeEfCore 1.0.20 1.1.0
wuic-framework-lib (NPM) 1.0.20 1.1.0

🔧 Recommended operational updates for those upgrading

  1. For those wanting to enable multi-tenant (opt-in): apply the DDL script dbms/scripts/multi_tenant_aziende_connessioni_mssql.sql (or _mysql.sql) to add the Connessione_DB_Dati and CONNESSIONE_DB_Meta columns to Aziende. Populate Aziende rows with the names of ConnectionStrings entries from appsettings.json. Set AppSettings.multiConnectionEnabled = "true". Restart the backend.
  2. For those staying single-tenant: no action required. Without multiConnectionEnabled=true, tenant routing is disabled and behavior is bit-identical to 1.0.20.
  3. Menu localization — metadata refresh: after upgrade, run once POST /api/Meta/AsmxProxy/MetaService.invalidateMetadataRuntime to reload the menu dictionary on the client side. Alternatively, log out and back in.
  4. Menu localization — migrating an existing project: for projects coming from a previous version with Italian labels in _metadati__menu.mm_display_string_menu, apply two idempotent SQL steps: (a) UPDATE _metadati__menu SET mm_display_string_menu = '<menu.scope.slug>' WHERE mm_display_string_menu = '<old label>' for each entry, following the menu.<scope>.<slug> schema documented above; (b) INSERT/MERGE INTO _wuic_translations (language, resource, translation) 5 rows per new key (one per language). The old rows in _wuic_translations with resource = Italian text remain in the DB and can still be consumed by other callers (instant(), list-grid headers).
  5. Backend hot reload in dev: if you develop with dotnet watch, the backend: kill dll lockers task now requires pwsh 7+ (no longer Windows PowerShell 5.x). The inline C# script for Restart Manager uses Dictionary<,> syntax that is correctly parsed only in PS 7+.

v1.0.20

Back to index

Previous published version: 1.0.19 (4 May 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


A consolidation release after 1.0.19, focused on bug fixes with direct impact on editing and display (INSERT against INSTEAD OF triggers, numeric fields resetting on blur, empty calendar on first navigation, custom SQL preservation in reports) and on a round of alignment of the map component to the new Google Maps APIs: completion of the map archetype options, migration to the Routes API for snap-to-roads, and removal of the dependency on the deprecated drawing library.


🐛 Notable bug fixes

  • INSERT against INSTEAD OF triggers: tables with INSTEAD OF INSERT triggers lost the returned PK from the outer statement (OUTPUT INSERTED.<pk> returned 0 or NULL because the trigger hijacked the INSERT). Fix: the INSERT generated by _Metadati_methods now uses a table variable (@__inserted_pk with OUTPUT ... INTO) as the primary channel and falls back to IDENT_CURRENT('<table>') when the trigger consumes the outer statement. Also resolves SQL Server msg 334 (OUTPUT INSERTED without INTO is not permitted on tables with enabled triggers).
  • field-editor number — reset to 0 on blur: when a metadata column had mc_min_value or mc_max_value set, the numeric field was being reset to 0 on blur instead of keeping the typed value. The range guard fired before the parsing round-trip, reading an intermediate non-numeric value and collapsing it to 0. Correct behaviour: the value is confirmed and clamped to the limits only if actually out of range, otherwise left untouched.
  • map archetype — completed options: the map component archetype gains three properties that close common UX gaps:
    • polyline — polyline overlay for tracking paths (historised GPS records). Grouping by field (groupByField), ordering (orderByField), per-record/per-group colour, optional snap-to-roads, waypoint dots distinct from the interpolated path.
    • clickableIcons — pass-through to google.maps.MapOptions.clickableIcons. When false, Google Maps no longer opens its built-in info window on POIs (stores, transit stops, addresses), which would otherwise intercept clicks meant for custom markers.
    • markerColorField — PinElement colour read from a record field (CSS #rrggbb). Ignored if the record already has a customMarkerImageSrcField set (image/SVG takes precedence).
  • Google Maps Directions API — deprecated: DirectionsService is deprecated as of 2026-02-25. Polyline snap-to-roads now uses the new Routes API (google.maps.routes.Route.computeRoutes), with automatic fallback to legacy DirectionsService (operational through 2027-02-25) for keys not yet migrated. Legacy travel mode → routes mapping unchanged (DRIVING/WALKING/BICYCLING). Automatic batching at 25 waypoints per call.
  • Google Maps Drawing API — removed: the drawing library (google.maps.drawing) is deprecated since 2025-08 and removed in Maps JavaScript API versions released starting May 2026. The DrawingManager logic on MapListComponent was only console.log stubs (no real persisted drawing feature) and has been removed. PointFilterComponent (geo filter by area/circle in list-grids) has been rewritten with manual click+mousemove handlers — same UX (polygon via multiple clicks, circle via centre+radius, dblclick to close), independent of the deprecated library.
  • Reports — custom SQL preservation via __autogenerated sentinel: reports with custom SQL that aliased columns not registered in metadata were losing joins/columns because the auto-generated dynamic query overwrote the user query. Cross-DBMS fix (MSSQL, MySQL, PostgreSQL, Oracle): every auto-generated SELECT now injects 1 AS [__autogenerated] as the first column; the metadata pipeline detects the token at runtime and preserves the custom query when the sentinel is not present. Enables Stimulsoft layouts that read columns not registered as metadata (typical in invoice/PEC templates where the SQL derives computed columns).
  • Calendar — events not visible on first navigation: <wuic-scheduler-list> showed an empty calendar when the user navigated directly to #/<route>/scheduler (FullCalendar initialised with data=[] before the async response; F5 populated it because session cache delivered the data before mount). Fix: explicit event synchronisation via API (removeAllEvents() + addEvent()) after the calendar render, bypassing the unreliable [events]="data" binding of the FullCalendar 6.x Angular wrapper on post-mount updates.

📦 Updated packages

Package From To
WuicCore 1.0.19 1.0.20
Wuic.Webcore 1.0.19 1.0.20
WuicOData 1.0.19 1.0.20
RuntimeEfCore 1.0.19 1.0.20
wuic-framework-lib (NPM) 1.0.19 1.0.20

🔧 Recommended upgrade actions

  1. Run dotnet ef database update if you are on EF migrations.
  2. If you use map polyline snap-to-roads: verify that your Google Maps API key has the Routes API enabled alongside the Directions API. Without the Routes API the framework falls through to the legacy DirectionsService, which still works but is in the deprecation window (sunset 2027-02-25).
  3. If you relied on the standard drawing toolbar of MapListComponent (no known use cases — handlers were stubs): the toolbar has been removed. No action required for geo filters in list-grids — PointFilterComponent keeps the same UX with internal implementation.
  4. To use the new polyline overlay on a map route: add the { enabled: true, groupByField, orderByField, ... } configuration to md_props_bag.archetypes.map.polyline via the designer or a metadata patch.

v1.0.19

Back to index

Previous published version: 1.0.7 (26 April 2026)
Backend: .NET 10 + IIS / Linux nginx
Frontend: Angular 21


In six days of intensive development, WUIC takes a significant leap: from a single Windows IIS deployment to a multi-runtime platform (Windows + native Linux), with a unified typed error handling system, centralized crash reporting, LDAP authentication, and a round of best-effort hardening across the application surface.


🛡️ Security

Best-effort hardening across the entire application surface: authentication throttling, reinforcement of standard HTTP headers (HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy), environment-aware handling of CORS and Swagger, reduced info disclosure in error responses, granular gating of administrative endpoints, additional checks on the runtime SQL path. Default configurations are now more conservative even for demo/staging deployments, with explicit override available via AppSettings.


🚨 Crash Reporting (end-to-end)

New auto-installed crash reporting system that sends .NET + JavaScript stack traces to a self-hosted receiver.

  • Passive capture: unhandled exceptions are collected automatically, deduplicated via stack canonicalization and queued asynchronously — no impact on request latency.
  • Private receiver: errors.wuic-framework.com accepts uploads signed with RSA license signature (zero API keys to manage).
  • GDPR consent: explicit opt-in managed in AppSettings, configurable from the settings editor.
  • Activation: CrashReporting:Enabled=true in appsettings.json + GDPR consent via UI.

🐧 Linux deployment (new)

WUIC is now installable on Ubuntu/Debian in a fully automated way, with the supported stack:

  • .NET runtime + MSSQL Server or MySQL/MariaDB.
  • Python 3.12 + RAG environment.
  • Secrets via systemd credentials.
  • nginx reverse proxy with Let's Encrypt TLS.
  • Post-install smoke test that validates backend, RAG and proxy.

The Linux tarball includes appsettings.linux.mssql.json and appsettings.linux.mysql.json preconfigured for the two supported stacks, plus a README with the step-by-step procedure.


🔐 LDAP Authentication

WUIC login now supports LDAP bind as an alternative to the DB:

  • Configuration via the Authentication:Ldap:* section in appsettings.json (server, base DN, bind template).
  • User auto-provisioning: the local row is created/updated on first login with data from LDAP.
  • DB fallback: if LDAP is unreachable and Authentication:Ldap:FallbackToDbOnFailure=true, login falls back to the traditional DB (admin/admin always remains reachable for recovery).

Compatible with Active Directory, OpenLDAP and Novell directories.


🗄️ MySQL Provider (Wuic.MySqlProvider 0.8.3)

Extended MySQL/MariaDB support to parity with the primary MSSQL:

  • Full functional test coverage (audit, client-side CRUD, concurrency, conditional styling, import-export, OData, retry, stored procedures, translations, validations).
  • Linux-specific bug fixes: default collation, JSON_TYPE quirks, paging hints.

🚦 Unified error system (typed exceptions)

Complete refactor of application error handling:

  • WuicException as base type for all typed application exceptions (part of the framework's public API).
  • WuicErrorCodes: catalog of 27 stable codes (errors.auth.unauthenticated, errors.metadata.props_bag.malformed, errors.db.sql_exception, errors.report.render_failed, etc.).
  • Stable JSON envelope for all error responses: { ok, errorCode, args, traceId, fallbackMessage }. Allows the client to display localized messages instead of technical stack traces.
  • Built-in translations in IT/EN/DE/ES/FR/JA for all known codes.
  • Automatic mapping of known runtime exceptions (SqlException, AuthenticationException, JsonException, etc.) to typed codes.

📊 Excel export

Bulk .xlsx export rewritten on a producer/consumer pipeline and OpenXmlWriter streaming. Impact is mainly on large datasets (tens of thousands of rows and up).

  • Streaming OpenXML instead of incremental DOM-build: typically 50× faster on bulk exports, memory footprint stays bounded even past one million rows.
  • Pipelined DB-read / xlsx-write on a bounded buffer: DB reads no longer block on sheet compression time.
  • Aggregated progress notifications over a dedicated channel: no more one task per update, no more WebSocket storm during long exports.
  • Automatic multi-sheet split when the Excel per-sheet limit of 1,048,576 rows is exceeded. The completion message reports the number of sheets generated.

No action required: the path is active by default for all .xlsx exports triggered from the list-grid toolbar (Export XLS) and from server-side APIs.


🐛 Notable bug fixes

  • isSuperAdmin gating: corrected permission check on multiple endpoints that previously confused isAdmin (per-user role) with isSuperAdmin (source-of-truth role).
  • OData CRUD: fixed edge-case serializations (Decimal→string, DateTime UTC roundtrip, navigation properties).
  • First-run wizard: initial bootstrap now correctly consumes IConfiguration (no longer dependent on legacy app.config).
  • Crash reporting forwarding: bug 2026-04-28 resolved — handled MVC exceptions no longer bypass the crash reporter middleware.

📦 Updated packages

Package From To
WuicCore 1.0.13 1.0.19
Wuic.Webcore 1.0.13 1.0.19
WuicOData 1.0.13 1.0.19
RuntimeEfCore 1.0.13 1.0.19
Wuic.MySqlProvider 0.7.x 0.8.3
wuic-framework-lib (NPM) 1.0.11 1.0.19

🔧 Recommended operational steps for upgraders

  1. Run dotnet ef database update if you are on EF migrations.
  2. Verify appsettings.json: the system now also reads AppSettings:AllowedOrigins (string array) and AppSettings:registrationEnabled (boolean kill-switch). Safe defaults if not specified.
  3. If you use LDAP, configure the Authentication:Ldap:* section in appsettings.json.
  4. For Linux deployment: use the dedicated tarball and follow the included README.
  5. To enable outgoing crash reporting: set CrashReporting:Enabled=true in appsettings.json + accept GDPR consent via UI.