Changes after the independent review of the layout preview. A person who may not author a template cannot obtain the picture of an unpublished draft by naming a revision. The revision table also holds the frozen drafts that the retained preview writes; a named revision is now resolved only when it is a publication, and any other row is answered "Document definition not found.", with the type and the words of an identity that does not exist, from the service, from the action before and after its form, and from the widget. A draft is previewed through its template alone. The widget of the "Preview layout" action is requested by the identifier of the run and reads the person's choices from the state of that run. A client sends every plain process value as a parameter of the widget request, and the framework's widget route logs the parameters of a request that fails or takes over a second, so the chosen record's identity and the sample's name were logged for a slow preview. The check step now holds the accepted choices as one structured process value, which is not sent, and the widget no longer reads a revision, record, sample, stock or resolution from any request parameter. A source record is read within the channel and credential ceilings the print path applies to the source table, asked before the record is read. A label whose picture alone is refused (PREVIEW_TOO_LARGE and no other refusal) is no longer said to be unprintable: "This label is too dense to preview; printing is not affected." The help of the form says that the longest values picture is drawn with capital W, is indicative, and that the renderer's proof at publication decides. The README records what a host must configure for label previews: no-store for widget responses, which neither a widget renderer nor the widget route sets, and what the request log holds. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
91 lines
15 KiB
Markdown
91 lines
15 KiB
Markdown
# QBit Document Generation
|
|
|
|
> **Version 1.0.** Published to the KOF22 Gitea Maven registry (`com.kingsrook.qbits`). Requires QQQ Core `4.1.0-RC.22`; the bridges need Record Files `0.1.0-RC.1` and Scripting `1.0.0`. See [status](docs/RELEASE-STATUS.md).
|
|
|
|
Start with [current status and build prerequisites](docs/RELEASE-STATUS.md) and [known issues](docs/KNOWN-ISSUES.md). These documents supersede older release-readiness statements below.
|
|
|
|
`foundation/` owns metadata, revisions, requests and audit contracts. `renderer/` contains rendering and vendored corresponding source. `bridges/` contains optional integrations. The root Maven reactor lists all modules.
|
|
|
|
|
|
Java 21 implementation of user-defined document metadata and generation coordination. Built against public QQQ `4.1.0-RC.21`. This is a development snapshot, not a qualified production 1.0 renderer.
|
|
|
|
The QBit owns document types, stock, table bindings, template drafts, immutable published revisions, named asset-version references, batch pins and generation requests. It does not own retained file bytes, a scripting engine, a printer queue, or a generic workflow framework.
|
|
|
|
## Implemented
|
|
|
|
- Native, visible `document_generation_*` tables. Direct writes are denied; validated Java services own changes. Draft HTML/PDF and ZPL fields carry QQQ `CODE_EDITOR` metadata.
|
|
- User-defined types, table/output bindings and exact physical stock in micrometres, margins and DPI. Source and manifest sizes are bounded.
|
|
- Separate draft saves and publication. Publication requires isolated source validation, plus published-script validation when a context script is configured. Revisions copy stock and pin immutable asset/script identities.
|
|
- Authorized single-record and bounded synchronous batch generation. Batch revision and selection hashes persist across repeated calls, and partial failures remain explicit.
|
|
- Matching durable retained-file receipts and strict audit callbacks before success or printing. Request tables contain receipt/provenance fields, never document bytes or context bodies.
|
|
- Reprint through current retained-version authorization, without rerendering. Queue acceptance is reported as `QUEUED`, never as physical printing.
|
|
- Stable request keys prevent blind duplicate execution. Incomplete work requires reconciliation; this implementation does not claim exactly-once processing.
|
|
- Native definition/source editing, display-name renames for types, stock and templates, immutable retained previews and optimistic draft/publication forms in a Document Generation workspace. See [authoring](docs/AUTHORING.md).
|
|
- Durable generation-owned queue/items, independent strict audit, bounded admission/cursors and conservative recovery. Native source record/batch/status/download/reprint processes call the same services. See [queue](docs/DURABLE-QUEUE.md).
|
|
- Explicit opt-in adapter to existing core Jobs with cooperative checkpoints; no additional scheduler. See [Jobs](docs/JOBS.md) for verification state and host requirements.
|
|
|
|
## Host integration
|
|
|
|
Install metadata with `DocumentGenerationQBitProducer.withQBitConfig(config).produce(instance)`. Configure a backend through `DocumentGenerationQBitConfig.withBackendName(...)`. Registration never runs DDL automatically. Apply `DocumentGenerationSchema.mysqlScript(config)` through host migration tooling for native MySQL 8.4. `postgresScript(config)` generates the same tables for the native QQQ PostgreSQL 17 backend (and the file-H2 fixture); the PostgreSQL publication, audit and queue suites pass against `postgres:17` with QQQ 4.1.0-RC.21 (see Verification), but, as with MySQL, that does not qualify a production deployment. See [MySQL qualification](docs/MYSQL.md) for required current engine/settings, metadata mappings and deployment boundaries.
|
|
|
|
Use `DocumentDefinitionService` for types, stock, bindings, assets, drafts and publication. Use `DocumentGenerationService.generate`, `generateBatch` and `reprint` for operational work. Permissions are separate `document-generation.view`, `.configure`, `.publish`, `.generate` and `.print`; each generation/print additionally enforces current source-record access.
|
|
|
|
The host supplies these typed contracts from `GenerationPorts`:
|
|
|
|
| Port | Required behavior |
|
|
| --- | --- |
|
|
| `ContextExecution` | Validate a published immutable build and run it in the shared isolated scripting service; broker QQQ access under the initiating actor. |
|
|
| `IsolatedRenderer` | Authorize assets, validate sources and render in a qualified isolated worker. Enforce time/memory/network/input limits and output/ZPL policies before returning. |
|
|
| `RetainedOutputIntake` | Idempotently retain exact bytes plus source association, return matching hash/length and a committed receipt; reauthorize immutable versions for reprint. |
|
|
| `PrintDispatch` | Reauthorize retained version and destination and submit idempotently to existing printing; return a queue job ID. |
|
|
| `Audit` | Independently commit deduplicated generation/print outcomes before returning; throw on failure. This is not core's best-effort `AuditAction`. |
|
|
| `AssetSelection` | Search and resolve currently authorized, released immutable image/font versions; obtain hash/media/length from the owner, never from a browser claim. |
|
|
|
|
Every default provider fails closed. No adapter calls the trusted in-process rendering proof. Template source is never evaluated by this QBit's JVM code. Optional `bridges/scripting`, `bridges/renderer` and `bridges/record-files` artifacts implement public integration boundaries without dependency cycles. The renderer bridge has a fail-closed production constructor (runsc, pinned digest) and, for trusted local development only, an explicit cold Java transport (`TrustedLocalDockerExchange`) and an optional warm single-use pool; both are non-production, never automatic fallbacks, and documented in [bridges/renderer](bridges/renderer/README.md). The supported durable storage is native MySQL 8.4, native PostgreSQL 17 and synchronous file H2. The host worker/renderer configuration is operator-owned and unqualified until a deployment supplies matching external evidence. Printing integration and the latest owner download extension are tracked separately. A callback alone does not establish production durability or isolation: its implementation and deployment must meet the contract and pass integration tests.
|
|
|
|
Context contains bounded immutable JSON-like data, including the authorized selected `record`. A script may add context objects but cannot replace that reserved original record. Credentials, QQQ sessions, raw host objects and lazy entities are rejected. Source-record permissions are checked before context preparation, before rendering, before intake and before printing. Asset access is rechecked by the isolated renderer adapter, and retained-version access by retained/printing adapters.
|
|
|
|
### What a host must configure for label previews
|
|
|
|
The "Label preview" widget and the "Preview layout" action arrive with the QBit's registration and need no host code. Two things are the host's:
|
|
|
|
- **Send `Cache-Control: no-store` for widget responses.** The widget's answer holds the picture of a label, which in the action can show the values of a record. QQQ's widget route sets no `Cache-Control` header and a widget renderer cannot set response headers, so the host, or the proxy in front of it, must send `no-store` for widget responses. Nothing in this QBit can.
|
|
- **Know what your request log holds.** QQQ's widget route writes the parameters of a widget request that fails or takes longer than one second to the application log. For this widget those parameters hold no record identity and no sample name: from a template's record screen they are the table name and the template's identity, and from the action they are the identifier of the run (`processUUID`) with the plain process values a client adds to it (`previewTemplateId` or `previewRevisionId`, `sourceTable`, and whatever the client itself sent to start the run, which for the standard clients is its selection of the template or revision row). The chosen record, sample, stock and resolution stay in the state of the run and are not sent; this holds for a client that sends plain process values only, as the standard clients do. `DocumentPreviewService` itself logs one line per preview: the actor, the template or revision identity, the kind of data, the resolution, the outcome and the diagnostic codes.
|
|
|
|
## Known boundaries before release
|
|
|
|
Native table/record processes and source-editor forms are implemented through standard QQQ metadata. Results include record navigation and download/preview metadata consumed by generic frontend components. Authenticated delivery requires verified bytes and a completed owner receipt before preview. There is no visual designer. Final integrated browser workflow acceptance, a qualified worker deployment, production storage/restore and printing integration remain release work. Service-only fields cannot be edited through unrestricted generic CRUD. Domain actions use validated services.
|
|
|
|
The original `generateBatch` API remains synchronous; `GenerationQueue` provides separate durable queued selections. Native publication locks and compares the draft fingerprint after isolated validation, then commits the immutable revision and active pointer atomically. Publication rejects ambient and split-backend transactions; memory is a volatile test fixture only. Request states and external side effects do not share one transaction. Generation rejects ambient caller transactions. Known retained versions can be reconciled without rerendering. Unknown intake outcomes and abandoned invocations require owner evidence and a trusted termination provider; they are never automatically recycled into untouched work.
|
|
|
|
The definition permission model is application-wide: configuration grants cover all definitions. Requests require source access; batches are visible only to their initiating actor. Counts and aggregates on request/batch tables are denied because post-query parent filtering cannot make those totals safe. Private queue context/lease fields cannot be queried, sorted, joined or used as unique keys through enforced native actions. Per-tenant template authoring scopes and efficient SQL-level parent filtering need explicit qualification. Explicit retained-owner authority supports archived exact-version history and operational access; without that port the live-source policy remains. See [retained authority](docs/RETAINED-AUTHORITY.md).
|
|
|
|
Production gates still include volume/retained-storage restore; isolated compiler/renderer containment; physical barcode/printer acceptance; user-defined filename/output options; complete host browser workflows; and dependency/license checks. Standalone renderer catalog 9 offers 71 explicit barcode IDs (finite, not universal; see renderer/BARCODES.md). Its trusted warm-engine benchmark is documented in renderer/PERFORMANCE.md and does not establish sandbox/end-to-end capacity. Strict ledger/queue durability accepts synchronous file-H2 and explicitly qualified native MySQL 8.4 and PostgreSQL 17; unsupported backends and metadata/settings drift fail closed.
|
|
|
|
## Modules
|
|
|
|
| Directory | Artifact | Notes |
|
|
| --- | --- | --- |
|
|
| `foundation` | `com.kingsrook.qbits:qbit-document-generation` | Metadata, services, ports and schema. Depends on QQQ core only (RDBMS optional). |
|
|
| `renderer/vendor/openhtml-1.1.105-qqq-p1` | `com.kingsrook.thirdparty:openhtmltopdf-pdfbox:<family revision> (upstream 1.1.105 + QQQ patch p1)` | LGPL-2.1-or-later patched OpenHTML (one file differs from upstream 1.1.105), built from the tracked source with its notices; the renderer depends on it. |
|
|
| `renderer` | `com.kingsrook.qbits:qbit-document-renderer` | Standalone isolated renderer CLI/image source; no QQQ dependency. |
|
|
| `bridges/renderer` | `qbit-document-generation-renderer` | `IsolatedRenderer` port implementation for the renderer. |
|
|
| `bridges/remote-renderer` | `remote-renderer-parent` -> `qqq-remote-renderer-transport`, `qbit-document-generation-remote-renderer` | Remote renderer service and adapter. |
|
|
| `bridges/record-files`, `bridges/printing`, `bridges/scripting` | `qbit-document-generation-record-files`, `-printing`, `-scripting` | Bridges to sibling QBits (Record Files, released Printing 1.0.0, Scripting). |
|
|
|
|
The root `pom.xml` is an aggregator only and lists every module, so one build produces every GAV. Each module keeps its own dependency boundary. Build a subset with `./mvnw -pl <module> -am install` (for example `-pl renderer -am` builds the patched OpenHTML module first); a module also builds alone with `mvn -f <module>/pom.xml install` once its dependencies are installed. All modules share one `${revision}` (default `0.1.0-SNAPSHOT`; releases set `-Drevision=<version>`), and flatten-maven-plugin publishes self-contained POMs.
|
|
|
|
## Verification
|
|
|
|
Run `./mvnw verify` (`.\mvnw.cmd verify` on Windows) at the repository root. The foundation's tests include disposable MySQL 8.4 and PostgreSQL suites started with Testcontainers, so Docker access is required. They verify publication concurrency/commit/rollback, physical pooled-connection restoration, mapping/settings drift refusal, bounded lock contention, current-authorized context selection and immutable pins, audit/receipt failures, restart and incomplete results. They do not qualify production storage, hostile workers or physical printer output.
|
|
|
|
The build enforces Checkstyle plus 85% instruction/90% class coverage in the foundation, and holds the renderer (94% instructions, 91% lines) and its bridge (85% instructions, 82% lines) to their measured baselines; the new packages and classes named in each `pom.xml` must reach 100% line coverage. Bounded mutation campaigns over the scene parser and the host printer gate run in the test phase; see [docs/security/fuzzing.md](docs/security/fuzzing.md). The published `qbit-build-parent` POMs leave `${revision}` in their own version, which causes a Maven parent-resolution warning despite a successful build. That upstream publication issue remains open; it is neither suppressed nor patched.
|
|
|
|
## Maven publication
|
|
|
|
Library POMs are flattened (`ossrh` mode) so an installed POM carries resolved versions and needs no `-Drevision`. Dependencies on the sibling QBits are properties (`qbit-record-files.version`, `qbit-scripting.version`, `qbit-printing.version`) so the owners' released versions can be set in one place.
|
|
|
|
## Corresponding source and licenses
|
|
|
|
`renderer/vendor` carries the hash-pinned corresponding-source archives next to their patches and build scripts: `zint-2.16.0.tar.gz` (GPL/BSD/MIT notices in `LICENSE.zint`, `MIT-ZINGL.txt`) and `openhtml-1.1.105-qqq-p1/openhtmltopdf-pdfbox-1.1.105-qqq-p1-complete-source.zip` (LGPL-2.1-or-later). `inputs.sha256` files pin them; `openhtml-1.1.105-qqq-p1/verify-module-source.sh` proves the Maven module's `pom.xml` and `src/` are byte-identical to the archive. Release artifacts and images must ship these notices and sources, not just a URL.
|