Artifact renderer packs
Artifact renderer packs add reviewed visualizations for typed Artifacts without putting product-specific code in Kaizen. They are trusted application code, not member content and not an App Garden extension. Version 1 installs a pack only by composing immutable Kaizen API and web images from exact base-image and pack digests.
Kaizen never downloads or discovers a pack at runtime. A stored Artifact cannot install code, choose a module path, or enable a pack. Unknown and disabled renderer contracts remain inert and use the Artifact's text fallback.
What a pack release contains
A pack release publishes one signed, digest-addressed OCI artifact containing:
- a Python distribution with the backend validation entry point;
- an offline browser module, optional stylesheet, and declared logical assets;
- a closed manifest with every file hash, licenses, SBOM, and provenance; and
- a composition tool that produces final API and web images from exact Kaizen base-image digests.
Composition also produces a deployment attestation mapping the source pack,
base images, each base host-capability SHA-256 and signature evidence, and
derived images. Keep that attestation with the release record.
The API and web base capability receipts must carry the same recomputable
release_pair_id, exact source revision, product version, and linux/amd64
platform. Arm64 core images advertise their actual linux/arm64 target and a
different pair ID; renderer-pack composition remains unsupported for them.
Validate the two extracted receipts with
generate_renderer_host_capabilities.py validate-pair; matching API versions
or mutable image tags are not pair evidence. The repository runbook
docs/operations/artifact-renderer-host-release-pairs.md defines the canonical
identity bytes and the signed-image/provenance evidence that the composer must
bind into its attestation.
The API and web images must contain byte-identical copies of
installed.json; the authenticated API publishes its SHA-256 and the browser
requires the fetched web receipt to match before selecting a renderer.
Install while disabled
- Verify the pack signature, exact OCI digest, manifest, SBOM, licenses, and supported Kaizen base digests using the pack repository's release procedure. The composer must verify the exact signed base image digests and their role-specific renderer host-capability files; a missing file, API/web role swap, or unsupported receipt, backend, browser, or network-policy version is terminal.
- Run the pack's offline composer. The API-derived image installs the exact wheel and receipt; the web-derived image installs the browser files and the same receipt. Do not use a mutable tag as deployment evidence.
- Roll
api-backend,background-worker, anddocument-workerto the same derived API image digest, andfrontendto its paired derived web digest. KeepARTIFACT_RENDERER_PACKS_ENABLEDempty. - Confirm all services start,
/auth/mereports the pack as installed and disabled, ordinary Artifacts still use their native or text view, and the API/web receipt digests match. - Restart
api-backend,background-worker,document-worker, andfrontendfrom the deployed image pair. Repeat the receipt-parity and disabled-fallback checks; no runtime install or network download should be necessary.
Pack receipt versions use SemVer. Python distribution versions are compared
after PEP 440 normalization, so a receipt version such as 1.2.3-rc.1 matches
wheel metadata 1.2.3rc1. The version must have a valid PEP 440 representation;
an unrepresentable version or a different release, prerelease, or build suffix
prevents startup even while the pack is disabled. Plugin identities still
report the exact receipt version.
JavaScript assets may declare either text/javascript or
application/javascript; these aliases match the nginx module response. All
other media types must match exactly, and byte length and SHA-256 checks apply
before any asset is returned. When a proxy compresses a response, the bounds
and receipt digest apply to the decoded bytes delivered by Fetch; the compressed
wire Content-Length is not treated as the decoded asset size.
Presence is deliberately not execution authority. While disabled, the API does not import the pack distribution or run its validators, and the browser does not load its module.
Enable an exact pack
After the disabled rollout is healthy, set the deployment setting to the one reviewed identity:
ARTIFACT_RENDERER_PACKS_ENABLED=["<pack-id>@sha256:<64-lowercase-hex>"]
The environment value is a JSON list; a bare scalar is invalid and prevents startup. Put multiple exact references in that same JSON array. Each reference must match an installed receipt exactly; an unknown digest, duplicate reference, invalid receipt, missing distribution, or plugin identity mismatch prevents the affected API-family process from starting.
This allow-list is deployment-owned bootstrap configuration, not an Admin Configuration override. Change it only in the environment used to start every API-family service, and use an empty environment list to disable every pack. A stale database row is ignored, so config-store availability cannot change the environment's trusted-code authority.
Roll the API-family services together. Then open an existing Artifact whose
exact (renderer.type, payload.type, payload.version) claim belongs to the
pack. Confirm the reviewed view appears and that changing any part of that
triple returns to the text fallback.
Security boundary
The backend plugin is trusted server code, so enabling a pack requires the same review as an application dependency. It may only validate the raw and normalized Artifact; it cannot replace the envelope or turn Artifact fields into import paths. A stored Artifact may predate the pack or have been admitted while the pack was disabled, so every detail read revalidates its exact content against the currently active backend registry. The API returns an admission bound to that content SHA-256 and exact pack identity. The browser hashes the received content and requires that admission before it can select the pack. Rejection removes only the reviewed presentation: the stored text and JSON remain accessible.
The browser module runs in a fresh opaque-origin iframe with scripts only. It
has no same-origin authority, cookies, storage, API access, forms, child frames,
or parent-navigation authority. A strict CSP blocks ordinary fetches and
subresource loads. The parent fetches only receipt-declared same-origin assets,
enforces per-file and per-session byte/request limits, verifies media type,
size, and SHA-256, and transfers the bytes over a private MessagePort.
Every brokered asset request has a 20-second deadline covering response headers
and the complete body, including requests made after the renderer has mounted.
A stalled request aborts and restores the fallback through the same terminal
failure path as a corrupt asset.
The frame permits WebAssembly compilation required by verified visualization
engines, but not general JavaScript string evaluation; only exact signed
first-party bytes admitted by the receipt and broker may reach that frame.
Failure, timeout, Artifact navigation, or disposal destroys the iframe browsing
context before showing the text fallback.
The outer host, not the pack, owns the top-level security marking. A named
security.markings.banner is rendered as exact text in sticky Artifact chrome
outside the iframe. It remains visible while the module loads, during WebGL
display, after a renderer error, and beside text fallback. Packs may present
more detailed portion markings inside their reviewed view, but must not repeat,
cover, reinterpret, or replace the host banner.
This isolation reduces browser authority but does not replace supply-chain trust. Review, sign, and exact-digest pin every first-party pack release. Runtime third-party packs are not supported.
Disable, uninstall, and recover
To disable, remove the exact reference from
ARTIFACT_RENDERER_PACKS_ENABLED and roll every API-family service. Leave the
derived images in place until an existing matching Artifact has been verified
to use the text fallback. Stored Artifact bytes do not change.
To uninstall, first complete that disable check, then atomically restore the exact pre-install API and web base-image digests. Verify the canonical empty receipt in both images and retain the composition and rollout attestations. Do not delete or rewrite Artifacts, pack objects, or receipts as a rollback mechanism.
If an API-family process refuses startup, treat that as image or configuration drift. Restore the last known matching image pair or disable the exact pack; never point the receipt loader at a writable mount. If the browser receipt is missing, malformed, or different from the API receipt, the browser fails closed to the text view.
Air-gapped deployments
Transfer the already composed, digest-verified final images through the normal offline image bundle. No runtime package registry, CDN, sidecar, shared volume, or network fetch is required. Include the pack OCI artifact, signed manifest, SBOM, composition attestation, final image digests, and rollback base-image digests in the offline release packet.
Manual UAT
Capture evidence for both disabled and enabled phases:
- Prove exact API/web image digests and byte-identical receipt SHA-256.
- With the pack installed but disabled, prove no backend plugin load and text fallback for an exact matching Artifact.
- Enable only the exact digest and prove one existing matching Artifact renders without creating, recomputing, or fetching a source result. Also open a matching legacy fixture rejected by the backend validator and prove it stays on text fallback while its exact JSON remains available.
- Prove a wrong renderer type, payload type, or payload version remains inert.
- Deny or corrupt one declared browser asset and prove bounded failure removes the iframe and restores the text fallback while the exact top-level security banner remains visible.
- Record browser traffic during normal renderer operation and require zero renderer-originated requests, including aborted attempts. Record each request's initiating frame and complete URL: parent brokerage may access only exact declared same-origin asset URLs, with no added query parameters.
- Resize the Artifact pane to a wide working canvas with both pointer and keyboard controls, enter full screen, then exit and confirm the saved pane width returns without remounting a different Artifact revision.
- Capture the exact banner text while the renderer is loading, after it sends the generic render-complete handshake, and after a forced renderer failure returns to text fallback. Require the same host-owned text in all three states. Do not require a particular renderer DOM or graphics technology.
- Restart every Kaizen pod while the exact pack remains enabled. After the rollout settles, hard-refresh the browser and prove the same stored Artifact renders from the same installed pack identity without rerunning its source job, changing its envelope, or downloading code at runtime.
- Disable, then restore the exact base-image digests, and prove the same stored Artifact remains accessible through its text and JSON views.
Never include credentials, source payloads, or restricted Artifact content in the UAT report.
Pack CI integration goldens
A pack repository should run both generic-host goldens against its actual composed outputs. It must publish two pack-owned conformance Artifacts alongside those outputs: one accepted Artifact and one structurally valid Artifact that the pack deliberately rejects under its own payload rules. Both must name an exact claim in the receipt. Core never mutates a guessed payload field to create the negative case.
The backend golden safely installs the exact wheel in an isolated temporary import path, discovers its declared entry point through the real registry loader, admits the accepted Artifact, and sends the rejected Artifact through raw validation, normalized-envelope validation, the write boundary, and stored content read admission:
KAIZEN_RENDERER_PACK_GOLDEN_API_RECEIPT=/path/to/api/installed.json \
KAIZEN_RENDERER_PACK_GOLDEN_WHEEL=/path/to/renderer.whl \
KAIZEN_RENDERER_PACK_GOLDEN_ARTIFACT=/path/to/artifact.json \
KAIZEN_RENDERER_PACK_GOLDEN_INVALID_ARTIFACT=/path/to/invalid-artifact.json \
uv run pytest -q tests/contracts/test_artifact_renderer_pack_golden.py
The browser golden takes the paired receipts, composed web root, and same
accepted Artifact, verifies the exact content-bound admission and pack
selection, then loads the exact module through RendererFrame in Chromium. A
pass is the production host's generic ABI completion handshake after the pack's
awaited mountRendererV1 returns; the golden does not require a canvas, WebGL,
or any other pack-specific output technology:
cd web/kaizen
KAIZEN_RENDERER_PACK_GOLDEN_API_RECEIPT=/path/to/api/installed.json \
KAIZEN_RENDERER_PACK_GOLDEN_WEB_RECEIPT=/path/to/web/installed.json \
KAIZEN_RENDERER_PACK_GOLDEN_WEB_ROOT=/path/to/composed/web \
KAIZEN_RENDERER_PACK_GOLDEN_ARTIFACT=/path/to/artifact.json \
npm exec vitest run -- src/components/RendererPackGolden.test.ts
To run both lanes with one checked-in command, pass the same six outputs to the core runner:
uv run python scripts/run_renderer_pack_goldens.py \
--api-receipt /path/to/api/installed.json \
--wheel /path/to/renderer.whl \
--accepted-artifact /path/to/artifact.json \
--invalid-artifact /path/to/invalid-artifact.json \
--web-receipt /path/to/web/installed.json \
--web-root /path/to/composed/web
Do not replace these with mirrored fixtures. The pack CI must pin the exact compatible Kaizen core commit and use outputs from the signed composition it is preparing to release. The accepted and rejected Artifacts must be pack-owned, reviewed conformance inputs emitted by that pinned build, not host-authored payload mutations.
Standalone applications
The renderer host has no application discovery or launch capability. Pack identity and payload fields select a reviewed visualization only; they do not select an App Garden deployment or create application navigation. Standalone application workflows belong outside the generic Artifact renderer host.