diff --git a/CHANGELOG.md b/CHANGELOG.md index 38b48b7..ea96a08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,10 @@ - Use named facility choices during enrollment and revalidate them under the current user's tenant/record permissions. - Require release security/CodeQL gates, verified artifact promotion and build provenance; report the real packaged QBit version. - Enforce configurable atomic queue/storage/rate budgets and cumulative printer limits, with bounded management pagination and stable replay at capacity. -- Full local verification passed 65 tests with zero failures/errors/skips and Checkstyle violations. Final hosted checks remain pending; this entry does not claim release or live storage migration. +- Consume bounded claim/cancel request bodies before mutation so a persistent HTTP connection remains usable for the subsequent status event. +- Pin H2 2.4.240 to fix close-time compaction data loss; verify file-backed schema/settings across cold reopens and document closed-backup upgrade qualification. +- Verify the exact frontend-inclusive runtime inventory in Maven, Trivy and OSV; fail on malformed or incomplete scanner evidence. +- Full local verification passed 67 tests with zero failures/errors/skips and Checkstyle violations. Final dependency-change hosted checks remain pending; this entry does not claim release or live storage migration. ## 0.1.3-rc.1 — 2026-10-30 diff --git a/README.md b/README.md index 4b6ddb6..00270c5 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ Open `http://127.0.0.1:18082`. On Linux/macOS use `:` as the classpath separator ## Authenticated local application -`PrintingLocalHost` combines the real QQQ dashboard, native processes, authenticated sign-in and device routes against one durable H2 database. It binds only to loopback, uses QQQ table-based authentication, and starts the native Quartz retention schedule through `QApplicationLauncher`. Existing pairing and job history are retained when the database and tenant are preserved. See [local host setup](docs/LOCAL-HOST.md) for explicit credentials, the required frontend runtime profile and a reviewed cutover procedure. +`PrintingLocalHost` combines the real QQQ dashboard, native processes, authenticated sign-in and device routes against one durable H2 database. It binds only to loopback, uses QQQ table-based authentication, and starts the native Quartz retention schedule through `QApplicationLauncher`. Existing pairing and job history are retained when the database and tenant are preserved. Use the pinned H2 2.4.240 runtime; older 2.3.232 file-backed deployments require a closed backup and cold-reopen qualification. See [local host setup](docs/LOCAL-HOST.md) for explicit credentials, the required frontend/runtime dependencies and a reviewed cutover procedure. The protocol-only `PrintingDemo` and synthetic, mock-authenticated `PrintingUiDemo` remain separate. Do not point the synthetic demo at real paired printer data. The local host retains database document storage until an actual dedicated S3 binding is configured in a deployment-owned application; it does not claim a remote cloud deployment. diff --git a/docs/LOCAL-HOST.md b/docs/LOCAL-HOST.md index c6a6e59..16fdfc1 100644 --- a/docs/LOCAL-HOST.md +++ b/docs/LOCAL-HOST.md @@ -31,7 +31,7 @@ There is no default browser password and no account chosen automatically. Startu Use Java 21 and the Maven wrapper. The `ui-demo` profile supplies the real frontend-next runtime assets for both UI-capable launch targets: ```powershell -.\mvnw.cmd -Pui-demo verify dependency:build-classpath '-Dmdep.outputFile=target/local-host-classpath.txt' +.\mvnw.cmd -Pui-demo verify dependency:build-classpath '-Dmdep.outputFile=target/local-host-classpath.txt' '-DincludeScope=runtime' $dependencies = (Get-Content target/local-host-classpath.txt -Raw).Trim() & "$env:JAVA_HOME/bin/java.exe" -cp "target/classes;$dependencies" com.kingsrook.qbits.printing.PrintingLocalHost ``` @@ -40,6 +40,12 @@ Set the required environment values securely before launch. Maven contract tests This command runs the host in the foreground. `QApplicationLauncher` owns graceful HTTP/scheduler shutdown. It does not install an operating-system service or auto-start registration; the deploying operator must provide supervision, protected credential loading, logs and backups for unattended use. +## Embedded database runtime + +Use the pinned **H2 2.4.240** runtime for file-backed local hosts. H2 2.3.232 has a confirmed close-time compaction data-loss defect ([issue 4247](https://github.com/h2database/h2database/issues/4247), [fix 4249](https://github.com/h2database/h2database/pull/4249)). Keep exactly one H2 driver JAR on the classpath; replacing the application JAR alone does not replace its runtime dependencies. Regenerate the runtime classpath from the current POM and remove the old driver from the new deployment payload. + +Before upgrading, back up the closed database and validate a separate copy through startup, full shutdown and cold reopen with the fixed driver. Confirm paired records, retention settings and job history after reopening, without rerunning initialization merely to make missing tables reappear. Keep the pre-upgrade backup for rollback; do not point an older driver at a database already opened by the new runtime. A successful fresh-database test does not certify every existing database file. Normal compaction remains enabled; no warm-up, retry or delay is part of the fix. + ## Cut over an existing paired protocol server 1. Identify the exact existing database, tenant, management credential and connector URL. Inspect all pending and uncertain jobs through bounded management pages before a maintenance window. diff --git a/docs/SESSION-STATE.md b/docs/SESSION-STATE.md index 1fe871e..7f05ddb 100644 --- a/docs/SESSION-STATE.md +++ b/docs/SESSION-STATE.md @@ -2,9 +2,11 @@ ## Audit candidate 0.1.4-rc.1 +H2 follow-up: isolated file-backed acceptance identified the upstream H2 2.3.232 close-time compaction data-loss defect (#4247/#4249). All migration statements succeeded and the four audit tables existed before closing, but disappeared on reopening. Identical restored-copy comparisons passed with diagnostic-only compaction disablement or with **only H2 replaced by 2.4.240** using normal compaction. The POM now pins the fixed runtime; no warm-up/retry/delay workaround is added. Full isolated frontend-profile verification passes **67 tests, zero failures/errors/skips and zero Checkstyle violations**. All **204** selected dependencies match Maven/SBOM/Trivy/OSV; refreshed Trivy reports zero HIGH/CRITICAL and OSV reports zero malicious findings. Independent review found no blocker. Fresh hosted checks and exact-artifact native/cold-restart qualification remain before live cutover. The public cold-reopen test is durability coverage, not a claim that every fresh synthetic database reproduces the old-driver defect. + HTTP follow-up: the runtime-inventory commit `60ce6e4` passed hosted security, CodeQL and PR gates, but its UI-profile build exposed a connection failure on the first status event after a claim. A deterministic split-body regression proved claim returned before consuming its request body. Claim and cancel now consume the existing bounded JSON body after device authentication and before mutation. The focused regression fails before the change and passes afterward; full isolated UI-profile verification passes **66 tests and zero Checkstyle violations**. The exact Ubuntu timing failure was not reproduced locally; fresh hosted verification is required. No retries or assertions were weakened. -Native deployment qualification is still in progress. Exact-artifact startup and native authentication/query/retention checks pass against fresh closed-backup copies, with the default 365-day policy. A prepared launch against a particular earlier copy returned a missing-settings-table error, while changing only the copy's filename passed; the cause remains under investigation. This is not claimed to be a fixed application defect. The real paired host remains unchanged, and no cleanup or physical printing was submitted by these checks. +Native deployment qualification is still in progress. Authentication/query/retention checks and an actual scheduled zero-removal tick pass on isolated copies, with the default 365-day policy. The prepared-copy missing-table failure was traced to the H2 defect above; filename, helper/bootstrap and generic legacy-schema theories were independently ruled out. The real paired host remains unchanged. No manual cleanup or physical printing was submitted by these checks. Runtime inventory follow-up: the authenticated host commit `78e0911` passed hosted checks, but a local scanner comparison found that the original POM scan omitted the frontend profile and selected a different transitive version from Maven. The pending workflow fix enables `ui-demo` for verification and generates a pinned CycloneDX Maven runtime SBOM for both Trivy and OSV. Exact coordinate/version guards reject incomplete or divergent inventories. Locally, all **204** selected runtime dependencies match the SBOM and Trivy inventory; ten new guard tests, six existing SARIF tests, actionlint and offline zizmor medium checks pass. Local vulnerability checks used the existing cached database; fresh hosted gates are still required for these workflow changes. The corrected deployment dependency payload excludes test libraries; no host was deployed by this follow-up. @@ -14,7 +16,7 @@ The new authenticated `PrintingLocalHost` connects native QQQ screens and device Earlier fixes add replay-safe native processes, validated facility selectors, strict wire fields, duplicate-risk preservation, dedicated QQQ S3 storage and native admin retention with a **365-day default**. Active, failed and uncertain work remains protected. Stop older writers before admission-accounting migration; existing content is preserved. See [audit evidence](AUDIT-2026-10-30.md), [operations](OPERATIONS.md) and [storage](STORAGE-AND-RETENTION.md). -Draft PR #10 passed every required hosted check at `aeac1a6`; its CI JAR/POM checksums were verified. The authenticated local-host addition requires fresh hosted checks before deployment. No live database migration, S3 provisioning, purge, merge, release or host deployment has occurred. Real PostgreSQL/S3, remote host authentication, multi-node operation and sustained physical throughput remain unqualified. All main-branch requirements apply to administrators. The low-severity same-repository workflow advisory has a documented [compatibility assessment](CI-COMPATIBILITY.md); no security check is disabled. +Draft PR #10 passed every required hosted check at `d52dfd7`, including the authenticated host, exact runtime inventory and HTTP follow-up; its CI JAR/POM checksums were verified. The H2 dependency correction requires fresh hosted checks before deployment. No live database migration, S3 provisioning, purge, merge, release or host deployment has occurred. Real PostgreSQL/S3, remote host authentication, multi-node operation and sustained physical throughput remain unqualified. All main-branch requirements apply to administrators. The low-severity same-repository workflow advisory has a documented [compatibility assessment](CI-COMPATIBILITY.md); no security check is disabled. Master coordination at `R:\agent-comms\qqq-cloud-printing.md` is acknowledged. Record Files owns indefinitely retained originals; printing owns its dedicated delivery copies. Their compiled source-reader/actor authorization contract remains pending. Standalone byte submission continues. Installed Windows baseline remains 0.1.7; unsigned prereleases are user-approved. diff --git a/docs/TODO.md b/docs/TODO.md index ceab602..ebd4bdf 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -1,9 +1,11 @@ # Follow-up - [x] Consume bounded claim/cancel bodies before side effects; split-body regression fails before fix, full isolated UI-profile gate passes 66 tests. -- [ ] Verify the HTTP follow-up on fresh hosted checks and resolve the isolated prepared-copy retention discrepancy before live cutover. +- [x] Verify the HTTP follow-up on fresh hosted checks at d52dfd7. +- [x] Identify and fix the prepared-copy retention failure by pinning H2 2.4.240; controlled old/fixed-driver comparison and full 67-test gate pass. +- [ ] Verify fresh H2-change hosted checks and the exact dependency payload through native startup, shutdown and cold reopen before live cutover. - [x] Reconcile UI-profile Maven runtime dependencies with scanner inventories; 204 exact coordinates, ten guard regressions and workflow lint pass locally. -- [ ] Run fresh hosted security/build gates for the runtime-SBOM workflow correction before authenticated-host deployment. +- [x] Run hosted security/build gates for the runtime-SBOM workflow correction at d52dfd7; dependency follow-up requires its own fresh checks. - [x] Finish audit regressions for native-process replay, strict options/inventory validation, retained duplicate risk and event field bounds; full gate passes 53 tests. - [x] Implement admin-configurable document retention (one-year default), bounded scheduled cleanup and existing QQQ object-storage integration, retaining history/receipts and protecting active or uncertain output. diff --git a/pom.xml b/pom.xml index 6d947f4..2ec00bb 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,8 @@ com.kingsrook.qqqqqq-backend-module-rdbms${qqq.version} com.kingsrook.qqqqqq-backend-module-filesystem${qqq.version} com.kingsrook.qqqqqq-middleware-javalin${qqq.version} - com.h2databaseh22.3.232runtime + + com.h2databaseh22.4.240runtime org.junit.jupiterjunit-jupiter6.0.1test ui-demo diff --git a/src/test/java/com/kingsrook/qbits/printing/PrintingMigrationTest.java b/src/test/java/com/kingsrook/qbits/printing/PrintingMigrationTest.java index 8470478..febb21d 100644 --- a/src/test/java/com/kingsrook/qbits/printing/PrintingMigrationTest.java +++ b/src/test/java/com/kingsrook/qbits/printing/PrintingMigrationTest.java @@ -21,6 +21,7 @@ package com.kingsrook.qbits.printing; +import java.nio.file.Path; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; @@ -30,8 +31,10 @@ import java.util.UUID; import org.json.JSONArray; import org.json.JSONObject; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; /******************************************************************************* @@ -100,6 +103,42 @@ class PrintingMigrationTest + /*************************************************************************** + ** File-backed state survives last-connection closure without reinitialization. + ** This checks cold reopen durability, not reproduction of every old-driver file. + ***************************************************************************/ + @Test + void preservesAuditSchemaAndSettingsAcrossColdReopens(@TempDir Path directory) throws Exception + { + String url = "jdbc:h2:file:" + directory.resolve("cold-reopen").toAbsolutePath(); + DriverDataSource source = new DriverDataSource(url, "sa", ""); + PrintingService service = new PrintingService(source, "tenant-a"); + service.initialize(); + assertEquals(365, service.getRetentionSettings().getInt("days")); + service.saveRetentionSettings(400, false); + JSONObject device = service.enroll(service.createEnrollment("default", "Synthetic station") + .put("deviceName", "Synthetic station").put("agentVersion", "test")); + for(int restart = 0; restart < 3; restart++) + { + DriverDataSource reopened = new DriverDataSource(url + ";IFEXISTS=TRUE", "sa", ""); + PrintingService restarted = new PrintingService(reopened, "tenant-a"); + assertEquals(400, restarted.getRetentionSettings().getInt("days")); + assertEquals(device.getString("deviceId"), restarted.authenticate(device.getString("deviceToken"))); + try(Connection connection = reopened.getConnection()) + { + for(String table : List.of("printing_settings", "printing_document_orphan", "printing_resource_usage", "printing_admission_event")) + { + try(PreparedStatement statement = connection.prepareStatement("SELECT COUNT(*) FROM " + table); java.sql.ResultSet rows = statement.executeQuery()) + { + assertTrue(rows.next()); + } + } + } + } + } + + + /*************************************************************************** ** Mixed namespaces are not silently merged or discarded. ***************************************************************************/