Files

Echo runnable local demo

Optional module (qqq-echo-demo) that turns the Echo example into a server you can start. The library module examples/echo does not depend on it. It reuses MobileScannersDemo (database, scanner/admin accounts, native server) and adds Collaboration, Record Files, Printing, Document Generation and Echo from their public production classes. Nothing is simulated: the renderer is the owner's cold Docker bridge, output is retained by Record Files, and printing is Printing's own service. The server binds 127.0.0.1 only.

Which launcher?

Goal Use
Try login, device approval and basic barcode capture only The basic Scan Capture demo, scripts/Start-Demo.ps1 (local demo). Not this module.
Run the full scan, render and print flow on a developer machine TrustedLocalEchoDemo (below).
Production-style cold renderer sandbox EchoDemo; it fails closed unless the owner's runsc runtime is available.
Keep using the maintained local server's existing database Either launcher with ECHO_EXISTING_DATABASE set (adoption notes). Only this mode accepts host port 57832.

There is no default port: pass one as the argument or set ECHO_DEMO_PORT. A local run prints its URL in the startup log (ECHO_DEMO_READY port=<port>, then http://127.0.0.1:<port>).

Status

The demo and Echo library tests pass with Checkstyle clean (counts change per run; see session state for the latest reported results). The QQQ default is 4.1.0-RC.21 and Printing defaults to released 1.0.0; the other owner qbits remain qualified owner inputs until their public release. The composed acceptance test passes with a controlled synthetic renderer and a simulated Printing queue, so it is not physical output. TC22 physical acceptance is separately operator-reported as passing (November 4, group report). Not production-ready and not a public SDK release. The dated CLAUDE-HANDOFF.md is a historical checkpoint.

Trusted local development launcher

TrustedLocalEchoDemo is a separate, explicit main for a developer machine. It starts the same server, accounts, permissions and loopback binding as EchoDemo, but builds the owner's IsolatedRendererBridge(exchange.limits(), RetainedAssetReader, exchange) over the renderer owner's supported public TrustedLocalDockerExchange, built from TrustedLocalDockerExchange.configuration(...). This is not a production sandbox qualification: it has no repository digest, uses the owner's literal unqualified local qualification id, and is not gVisor. The production EchoDemo default is unchanged and fails closed, and neither launcher falls back to the other. The owner's exchange and its tests are the transport boundary; this demo adds no transport of its own.

The exchange requires the renderer artifact qbit-document-generation-renderer at a version that contains it. The demo POM's renderer.version property defaults to ${generation.version}; override it for the private cohort, for example -Drenderer.version=0.1.0-echo.gen64c9pu507.tl1. Only the direct renderer dependency uses that property.

The launcher passes the owner factory: the Docker executable and exact image id from the environment below, a state directory <ECHO_DEMO_ROOT>\renderer-state (created if absent, absolute, not a symbolic link), the fixed owner id echo-local, the deadline, maximum bytes and pages, and a maximum concurrency of 1. Use a fresh private ECHO_DEMO_ROOT for each runtime: the journal there must belong to this process alone. At startup it calls the exchange's reapExpired() once, which reclaims only this owner's expired leases left by a killed process and logs a warning with the count of any it could not reclaim.

Extra environment (in addition to the shared settings below, except the ECHO_RENDERER_* cold-bridge variables, which this launcher does not use):

Variable Meaning
ECHO_LOCAL_RENDERER Must be exactly TRUSTED_LOCAL_DEVELOPMENT; anything else refuses startup.
ECHO_LOCAL_RENDERER_DOCKER Absolute path of an existing Docker executable.
ECHO_LOCAL_RENDERER_IMAGE_ID The exact local image id, sha256: plus 64 lowercase hex characters. Tags and repository digests are refused.
ECHO_LOCAL_RENDERER_DEADLINE_SECONDS Optional, 1-30, default 10.

ECHO_RENDERER_MAXIMUM_BYTES (default 1048576) and ECHO_RENDERER_MAXIMUM_PAGES (default 1) also apply. The owner's exchange refuses the render unless the daemon reports runc, linux and exactly the configured image id. Launch exactly as in "Build and start" but with this main class:

java -cp "examples\echo\demo\target\classes;$(Get-Content examples\echo\demo\target\classpath.txt)" com.kingsrook.examples.echo.demo.TrustedLocalEchoDemo 48123

The log line reports renderer=trusted-local-development. The unit tests (TrustedLocalRendererSettingsTest) never start Docker.

Build and start

From the repository root, with the checked-in wrapper (mvnw.cmd on Windows) and the tuple installed in your Maven repository:

.\mvnw.cmd -B -f examples\echo\demo\pom.xml verify
.\mvnw.cmd -B -f examples\echo\demo\pom.xml dependency:build-classpath "-Dmdep.outputFile=examples\echo\demo\target\classpath.txt"

Set the configuration below in the environment (never on the command line), then:

java -cp "examples\echo\demo\target\classes;$(Get-Content examples\echo\demo\target\classpath.txt)" com.kingsrook.examples.echo.demo.EchoDemo 48123

The optional argument is the port; otherwise ECHO_DEMO_PORT. There is no default port, and the sign-in passwords are never given on the command line (see below; to copy an existing demo account's password without displaying it, use scripts/Copy-DemoPassword.ps1). Ports 1024-65535 are accepted except the protected local runtimes listed in EchoDemoSettings. mvnw.cmd -f examples\echo\demo\pom.xml exec:java is equivalent when the environment is set.

Configuration (environment only)

Required:

Variable Meaning
ECHO_DEMO_ROOT Existing private absolute directory (not a symlink). The H2 file database lives in <root>\db\echo.
ECHO_DEMO_PORT Loopback port, unless passed as the argument.
ECHO_PRINTING_TENANT Printing tenant label. Operator sessions are locked to it.
ECHO_PRINTING_ADMIN_TOKEN 32-1024 character bearer token for Printing's administration routes.
ECHO_RENDERER_DOCKER Absolute path of the Docker executable.
ECHO_RENDERER_IMAGE Digest-pinned image repo@sha256:... (the qualified catalog-9 cold image).
ECHO_RENDERER_IMAGE_ID sha256:... image id.
ECHO_RENDERER_QUALIFICATION_ID Owner qualification evidence id.
ECHO_RENDERER_REQUIRED_QUALIFICATION_ID Must equal the evidence id above.
ECHO_RENDERER_OWNER_ID Deployment owner id for renderer admission.
ECHO_RENDERER_STATE_DIR Existing private absolute directory for renderer state.
MOBILE_DEMO_SCANNER_PASSWORD, MOBILE_DEMO_ADMIN_PASSWORD First startup only, 12+ characters, as in the mobile demo. Existing accounts are never changed.

Optional: ECHO_RENDERER_DEADLINE_SECONDS (10, max 30), ECHO_RENDERER_MAXIMUM_BYTES (1048576), ECHO_RENDERER_MAXIMUM_PAGES (1), MOBILE_BRAND_* (as the mobile demo), ECHO_RELEASE_GENERATED_FILES=true.

Retained files are stored by LocalFilesystemAttachmentStorage (the attachment owner's own storage SPI) under <root>\objects. Keys are hashed into file names, writes are temp-file-then-link and never replace an object. It adds no policy: Record Files and the attachment owner still decide quarantine, authorization and lifecycle. Single host, demo only.

ECHO_RELEASE_GENERATED_FILES=true sets Record Files' generated-file quarantine policy to off. Without it generated output is quarantined, Echo sees it as unavailable, and every print is refused. Turn it on only if you trust every producer allowed to retain generated output in this deployment.

The legacy ECHO_TEMPLATE_ID, ECHO_TEMPLATE_MEDIA_ID and ECHO_TEMPLATE_MEDIA_REVISION pin no longer selects a document and is ignored. The operator chooses the document at the start of every run from the published templates described below.

Accounts and permissions

scanner receives exactly: echo.use, printing_operator, document-generation.view|generate|print, record-files.view|manage (manage is the owner's intake permission), .attach on echoScan and the three mobile test-data tables (testOrder, testOrderItem, testOrderEvent) and the printing_tenant row key. admin receives the same plus printing_admin (Printing's native pairing/approval/setup processes) and document-generation.configure|publish to prepare templates. The scanner never gets these. Only admin also receives record-files.release, so approved captured photos can be released through the normal audited process; nobody gets Record Files configure/review/audit. Scans still need an enrolled and approved device, so use the existing mobile enrollment flow.

Operator order

  1. Start the host. Enroll the Windows printing agent against /printing/v1 and let it report printer media.
  2. As admin, publish one or more ZPL templates for the reported media in Document Generation (scene per EchoTemplatePublisher; give each its own name with EchoTemplatePublisher.publish(definitions, media, name)). Each published, compatible template becomes a separate Document choice.
  3. As the scanner, open Echo: choose the printer, then the paper or label size, then the Document (offered only from published echoScan templates whose stock equals that size and whose scene draws the scanned value and type dynamically). The three choices are fixed for the run; leave Echo and reopen it to change them.

Echo scan photos (mobile Record Files)

The host also installs the existing qbit-mobile-scanners-record-files integration, so an approved scanner can attach photos to one of its own retained Echo scans. Nothing new is served: Record Files' registered, versioned record-files upload API (/qqq/v1/qbit/record-files/...) and the independently versioned mobile-record-files widget vocabulary are advertised in /qqq/v1/version, behind the same native auth and device guards.

  • Process: echoScanPhotos ("Echo scan photos"), listed in the Echo app. It requires echo.use, echoScan.attach and a verified approved device. The first step lists the actor's own scans (the table's record security lock decides; label is the barcode and type, never the record id). The server then re-reads the chosen scan through the collaboration owner's visibility and attach checks and builds the RECORD_FILES_CAPTURE widget from the record that check returned, on the fixed echoScan table. The client never supplies a table or record reference, and rendering the widget directly names no parent. Record Files authorizes the parent again on every upload operation.
  • Policy: JPEG/PNG only, 20 MiB per file, up to four files, 256 KiB chunks, private staging under the demo's objects directory (separate staging namespace). The Echo host sets Record Files' quarantineCapturedFiles=false (leaving quarantineNewFiles=true and the generated-file setting unchanged), so a JPEG/PNG saved from this process by a device that MobileCaptureIdentityProvider currently verifies as approved is registered AVAILABLE and can be previewed immediately. The same upload API on any other host, and every upload, import or attachment-panel file here, stay QUARANTINED until released. This is a host trust decision, not a content scan or human review; no release event is written and review status is unchanged. Requires a Record Files version that contains RecordFilesQBitConfig.withQuarantineCapturedFiles(Boolean). The owner's change is an open, unmerged PR; this checkout's dependency pin has not been updated and builds only against the local owner input until a public version is pinned. Files already uploaded stay QUARANTINED until released through the normal record_files_file_record_files_release process.
  • Test-data records: the host also registers the test orders, items and events as Record Files parents, so a scanner can add photos from a record's Actions menu. The same approved-device policy and authorization re-check apply. Release of any quarantined file is admin-only (record-files.release).
  • Needs a real scan first (for the echoScanPhotos process): the list is empty until the scanner has Echo scans. Nothing is seeded outside this module's tests.
  • Existing databases: the Record Files upload tables are added by the same tolerated-reapply schema step; plan the migration for a database from another version.
  • Not claimed: one synthetic gallery PNG uploaded from the signed MC9400 app was observed AVAILABLE with matching parent, type, size and SHA-256 (see session state). No camera-hardware capture and no full preview of that photo are claimed.

Existing databases

Startup applies each owner schema with "already exists" tolerated, plus the four shipped Generation H2 migrations (output filenames, warm batch completion, print media profile and print copies, the last adding the nullable copies column where null means one copy). It does not migrate a database created by a different version. Plan migration separately for anything beyond a fresh demo root.