# Suprnova - complete manual > Every chapter of the Suprnova manual, inlined. Version 4.6.2. Source: https://github.com/eas4ai/suprnova Individual chapters are at https://suprnova.app/docs/.md; the index is at https://suprnova.app/llms.txt. --- # Prologue --- # Changelog A readable, per-version log of what changed in Suprnova. Each version section is that version's release record. A version is released when its version commit and matching `v` tag are pushed atomically. Newest first. ## 4.6.2 - 2026-10-23 ### Added - **`HttpEmbedder` reports the usage its provider sends.** `Embedder` gains `embed_documents_metered` and `embed_query_metered`, which return the vectors with the provider's own `usage` (`prompt_tokens`, `total_tokens`) as a `Metered`; the HTTP provider reads it from the OpenAI-compatible response and sums it across batches, and every other provider reports `None`, so an application that pays per token settles the bill from the provider's count instead of an estimate (#212). - **`HttpEmbedder` runs over an application-owned transport.** `HttpEmbedder::with_transport` takes an `EmbeddingTransport`: the application sends each `TransportRequest` (the pinned endpoint, headers, JSON body, timeout and response cap) through its own HTTP client, credential resolver and admission rules and returns a `TransportResponse` with the status, headers and body. The embedding protocol, retries, `Retry-After`, parsing and vector validation stay in the framework, which also refuses a body over the cap whatever the transport did (#213). - **`LazyEmbedder` registers a shipped profile and loads the model on first use.** `SearchRegistry::register_embedder` can take a `LazyEmbedder` built from a shipped `ModelProfile` and a loader; the service starts and tests run without downloading weights. The generation coordinator calls the new `Embedder::resolve_profile` before it pins a generation, so the generation still records the loaded model's revision, and a loaded model whose id or dimensions differ from the shipped profile is refused by name (#214). - **A search generation an interrupted build left behind can be reclaimed.** `generations::reclaim_generation(index, generation)` and `search:reclaim` release the creation lease of a `creating` generation so the next create call resumes at once instead of waiting the 60-second lease out, publish a `building` generation that was never switched to, and report one that is already current. The caller vouches for exclusive ownership, such as its own migration lock; `SearchEngine::release_creation_lease` is the driver hook, a no-op for drivers without leases (#215). - **A Cloudflare CDN driver for static publication.** `CloudflareDriver` (feature `cdn-cloudflare`, on by default and free of new dependencies) works the way an R2 bucket with a custom domain already does: the bucket is the publication disk through `S3Config` with the R2 endpoint, the custom domain serves it, and nothing runs at the edge. The driver owns a few rules on the zone, created on the first switch and corrected on every later one: two URL rewrites that serve `/about` and `/about/` from the mirrored `about/index.html`, a response header rule that carries the published headers, and a cache rule that caches a response only when its own `Cache-Control` allows a shared cache, as the published objects' does. It purges by URL in batches of 100 or by prefix, and removes its rules on a first-publication rollback. `CloudflareDriver::for_origin` covers a hostname that mixes dynamic pages: the proxied application serves the published objects itself and the driver keeps the cache rule and the purges. R2 charges no egress. - **An application's own 5xx page is sent as built.** With debug off, the framework answered every browser 5xx with its minimal status-only view, which also replaced a 503 page the application built on purpose, with its recovery link and `Content-Security-Policy`. `HttpResponse::error_view()` marks such a response as the application's own error view, and the production view leaves it alone, body and headers. Unmarked 5xx responses keep the minimal view; with debug on the development error page still replaces a marked response that carries an error report (#217). ## 4.6.1 - 2026-10-23 ### Fixed - **A Live action's framework frames fit their stack budget again in a debug build.** The static publication lookup that 4.6.0 added to every `GET` request built its 65 KB future in the request's own frame, so the frames below a Live action grew past the 1 MiB budget that leaves the action room on Tokio's default 2 MiB stack, and a deep action in a debug build could overflow it. The lookup now builds in a frame of its own that returns first. - **A fresh scaffold passes its own page budget test and `cargo fmt --check`.** The React, Vue and Svelte kits' `tests/page_budget.rs` audited `routes::register()` alone, so the Live script every page loads from `/__live/assets` returned 404 and the test failed on a new application. It now audits the router `cmd/main.rs` serves, and the notes topic's gate rule moved from `bootstrap::register` into `live::authorize()` so both can call it. The kits also wrote the Live topic setup, the asset share and the page budget test on lines rustfmt rewrites; the templates now carry rustfmt's layout, and the test's layout no longer depends on the application name. - **The page budget audit's URL mode counts the bytes that cross the wire.** `budget:audit ` and `PageBudget::audit_url` asked for uncompressed responses and counted decoded bytes as transfer, while a HAR capture of the same visit counted the compressed bytes, so the two modes disagreed on a page that serves gzip. A URL audit now asks for gzip, counts the compressed bytes for the transfer, asset and font rules, and reads the decoded body for the rest. This fix landed after the `v4.6.1` tag. ## 4.6.0 - 2026-10-22 ### Added - **Lossless PNG recompression.** `Image::recompress_png(PngEffort)` runs oxipng over a PNG after the pipeline's pixel operations, and the synchronous `suprnova::recompress_png` helper does the same on PNG bytes. `PngEffort::Fast` is oxipng's default level; `PngEffort::Maximum` is level 6 with Zopfli. Pixels, transparency, indexed palettes, the ICC profile and the colour chunks are kept; text, time and location metadata are removed; the output is never larger than the input. The `media` feature carries the dependency. - **Inertia pages in RenderCache.** A page declares its cache shape on the `InertiaProps` struct: `#[inertia(cacheable)]` on the struct, and `#[inertia(public)]` or `#[inertia(per_request)]` on every field (a field without a marker, an unknown field name or the marker on a non-props type fails to compile). `Inertia::cache_page::(route, component, public_shared, per_request_shared)` registers the page at boot. RenderCache stores the public shell and stitches the per-request props in after the route guards run; the assembled response is `private, no-store`. Representations are keyed on `X-Inertia`, the partial set, the asset version and the other protocol headers, which the response lists in `Vary`; a stale asset version answers 409 before any cached bytes leave. `suprnova generate-types` writes the declarations into `inertia-props.ts` as `inertiaPropCache` (`manual/render-cache.md`, `manual/frontend-typescript-types.md`). - **A page budget audit.** `TestClient::audit_page(url, &PageBudget)` renders a page through the in-process request path, follows the assets the document references (scripts, stylesheets and their imports, images and source sets, fonts, icons, media, inline styles, the web app manifest) and returns every failed rule with the asset, the rule, the measured value and the allowed value. The rules cover total transfer, per-asset size, raster format against size (WebP or AVIF with a fallback above the threshold; a PNG within a margin of its oxipng fast-preset size), the one-year immutable policy on fingerprinted assets, no cookie on or for a static asset, no redirect on the first request, a font budget and the third-party hosts the page reaches. The `budget:audit ` console command applies the same rules to a live URL or a HAR capture. Every budget is configurable on `PageBudget` and, for the command, through `PAGE_BUDGET_*` environment variables (`manual/page-budget.md`). All three modes measure the application's own resources and list every third-party host, which the allowed-host rule governs, so a HAR capture and the in-process audit of one page agree. The React, Vue, Svelte and Live starter kits ship `tests/page_budget.rs` over their public pages; the dogfood application runs the same audit. - **Static publication of public Inertia pages.** A route declared with `Router::public_page::` (or a public group plus `Inertia::public_page::`) renders without a session, CSRF token or nonce, and the application publishes its HTML and JSON objects, its fingerprinted assets and its transcoded images to a storage disk the CDN serves. Publication runs as an application boot task after the listener is bound, so the health check never waits for it. Every page references its assets by versioned key and the manifest write is the only switch, so the previous publication stays live until the new one is complete; a storage lease and a conditional manifest write keep two publishers from racing, and graceful shutdown waits for a delivery in progress. Pages regenerate by dependency tag after a committed transaction or job, delivered across processes through the bound `BroadcastHub`, and on a revalidate interval. The `CdnDriver` trait ships a CloudFront driver (origin switch, invalidation, a viewer-response function that restores the recorded headers); without a driver the origin serves the published objects from the manifest and mirrors them to stable bucket keys after the switch. Public pages get a hash-based `Content-Security-Policy` with `'unsafe-inline'` and nonces removed from every script directive; private pages keep their nonces, and the origin sends `Strict-Transport-Security` only on HTTPS it can trust. Images are published as WebP at quality 85 or higher, AVIF when ImageMagick is configured, with the metadata-stripped original as the fallback, and kept PNGs go through `recompress_png` at the maximum preset. Same-origin absolute image URLs and inline raster `data:` images go through the same preparation and are published as objects; a page whose own CSS (a `style` attribute or a `