Skip to content

Add optional asynchronous HTML document finalization - #1290

Draft
prk-Jr wants to merge 6 commits into
mainfrom
feat/1241-async-html-finalization
Draft

prk-Jr wants to merge 6 commits into
mainfrom
feat/1241-async-html-finalization

Conversation

@prk-Jr

@prk-Jr prk-Jr commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Let explicitly registered HTML integrations await request-time I/O after synchronous document EOF, so full-document integrations work on every adapter.
  • Keep the generic streaming traits unchanged. Execute ordered HTML terminal callbacks with core-owned bounded buffers, atomic replacements, one deadline, and unchanged-byte fallback.
  • Apply cache/privacy safeguards before lookup, preserve auction assembly and encoding order, and add cross-runtime regression tests, a fictional consumer, and reviewed design/implementation documentation.

The new capability belongs to Trusted Server's HTML lifecycle and runtime service container; no EdgeZero revision change is needed. This implements #1241 through an explicit HTML finalizer registration rather than adding an asynchronous method to every generic StreamProcessor. All synchronous processors reach EOF before the ordered terminal chain; this deliberately differs from interleaving asynchronous finishes between generic processors.

Enabled finalizers bypass shared-template reads, reservations, and stores and return eligible synthesized HTML with private, no-store. Raw-origin readthrough caching remains independent. This conservative first version removes transformed-template and downstream HTML caching even for page-level signing. A cacheable mode needs reader-neutral output, external-data/key identity, expiry, invalidation, failure-caching, and hit-execution rules; a user-independent flag alone is insufficient. The default chain budget is 5000 ms, configurable from 1–60000 ms. Fastly has no cooperative timer; consumers must bound their blocking host calls.

Review fixes

  • Expose the actual response status to finalizers; complete 404/500 HTML remains eligible so each integration can choose its policy.
  • Bypass rewriting and terminal I/O for opted-in HTML with status 206 or any Content-Range. Preserve encoded bytes, framing, validators, and ordinary origin policy before synthesized/inactive-ad/tag-suppression policy runs. Independent cookie and diagnostics privacy remains intact.
  • Construct terminal input before taking auction ownership. Reduce replay flushes while preserving the explicit pre-auction barrier, including missing body-end cases.
  • Correct the precisely traced Workers auction telemetry clock to the already-used web_time::Instant. The fresh release combined control now passes; broader Cloudflare Worker panics when a server-side auction runs (std::time::Instant::now() on wasm32-unknown-unknown) #1240 clock work remains separate.
  • Simplify poisoned writer handling, tidy imports, improve timeout validation errors, explain ignored cleanup exceptions, and reap forcibly killed children.
  • Add real-process cleanup tests and actual Axum finalization/auction harness coverage to CI with a bounded step and retained failure diagnostics.
  • Document the required public registry field migration, cache cost, consumer marker-placement tests, Fastly transport limits, and overflow latency. Marker counts remain a cardinality guard; neighboring bytes cannot prove HTML parsing context.

API shape and cacheable-finalization policy remain explicit maintainer design decisions. This PR stays a draft.

Changes

File Change
crates/trusted-server-core/src/integrations/html_finalization.rs Add borrowing local-future contracts with actual response status, controlled outcomes/errors, deadline handle, and bounded replacement writer.
crates/trusted-server-core/src/integrations/mod.rs Export finalizer contracts and register the test-only fictional consumer.
crates/trusted-server-core/src/integrations/registry.rs Add explicit ordered finalizer metadata and registration; disabled registrations remain inactive.
crates/trusted-server-core/src/html_processor.rs Share request-local document state across parsing, synchronous processors, and finalizer construction.
crates/trusted-server-core/src/platform/timer.rs Define the optional platform timer service.
crates/trusted-server-core/src/platform/mod.rs Export the timer and test service preservation.
crates/trusted-server-core/src/platform/types.rs Add default-absent timer builder/accessor support to runtime services.
crates/trusted-server-core/src/settings.rs Add and validate the global finalization timeout and test defaults/boundaries.
crates/trusted-server-core/src/publisher/html_finalization.rs Implement bounded collection/replay, ordered finishing, deadline cancellation, candidate validation, and validated materialized deflate EOF.
crates/trusted-server-core/src/publisher.rs Route every opted-in body/sink path; gate template caching and headers early; preserve seam/encoding/telemetry behavior; add route, cache, codec, cancellation, and winning-bid regressions. Box the already-buffered cold-template fallback to prevent a reproduced WASI stack trap.
crates/trusted-server-core/src/config.rs Admit fictional fixture configuration only under test-utils.
crates/trusted-server-core/src/test_support.rs Expose fixture helpers and reusable test HTTP service construction.
crates/trusted-server-core/src/test_support/async_html_finalization.rs Add a feature-gated fictional consumer using real request HTTP services after complete EOF.
crates/trusted-server-adapter-axum/src/app.rs Supply registry metadata before publisher cache admission.
crates/trusted-server-adapter-axum/src/platform.rs Install the Tokio timer and verify service construction.
crates/trusted-server-adapter-cloudflare/Cargo.toml Add opt-in adapter test-utils forwarding.
crates/trusted-server-adapter-cloudflare/src/app.rs Supply registry metadata before publisher cache admission.
crates/trusted-server-adapter-cloudflare/src/platform.rs Install Workers Delay on WASM.
crates/trusted-server-adapter-cloudflare/src/lib.rs Add feature-gated fixture logging and panic diagnostics.
crates/trusted-server-adapter-fastly/src/app.rs Supply registry metadata and verify cooperative timer absence.
crates/trusted-server-adapter-spin/Cargo.toml Add opt-in adapter test-utils forwarding.
crates/trusted-server-adapter-spin/src/app.rs Supply registry metadata before publisher cache admission.
crates/trusted-server-adapter-spin/src/platform.rs Install Spin SDK sleep on WASM.
crates/trusted-server-adapter-spin/src/lib.rs Add feature-gated fixture diagnostics.
crates/trusted-server-integration-tests/tests/parity.rs Verify yielding finalizer output and privacy on host adapters.
scripts/async-html-finalization-local-test.sh Provide the actual-runtime harness entry point.
scripts/fixtures/async-html-finalization-origin.py Serve a delayed document and delayed/hung lookup on loopback.
scripts/fixtures/async-html-finalization-runtime.py Build fresh artifacts, generate isolated configuration, run all four runtimes, and assert exact output and deadlines.
trusted-server.example.toml Document the finalization timeout default.
docs/guide/configuration.md Document timeout, buffering, and cache/privacy effects.
docs/guide/integration-guide.md Document registration, supported entry points, services, fallback, and runtime checks.
docs/superpowers/specs/2026-10-12-1241-async-html-finalization-design.md Record the investigated design, alternatives, ownership boundary, and acceptance criteria.
docs/superpowers/plans/2026-10-12-1241-async-html-finalization.md Record the implementation plan and bidirectional specification coverage.
docs/superpowers/reviews/2026-10-12-1241-async-html-finalization-implementation.md Map implementation and tests to requirements; record review, verification, necessary refinements, and runtime limitations.
crates/trusted-server-core/src/auction/telemetry.rs Use the existing platform-compatible monotonic clock for auction observation.
.github/workflows/test.yml Run actual Axum finalization and cleanup tests with Python 3.12 and failure artifacts.
scripts/fixtures/test_async_html_finalization_runtime.py Verify already-exited, graceful-TERM, and ignored-TERM then KILL/reap cleanup against real processes.
docs/superpowers/plans/2026-10-12-1241-review-hardening.md Record reviewed refinements, red/green evidence, complete verification, and remaining design decisions.

Closes

Closes #1241

Related: #1226 makes the same web_time::Instant import change in auction telemetry as part of broader clock work. This PR includes only the targeted auction-telemetry correction for #1240; its other clock paths remain separate. KV adapter wiring under #1139 and a real signing integration are outside this change.

Test plan

Checks below were run locally on the implementation branch. Hosted CI results remain separate.

  • cargo fmt --all -- --check and git diff --check.
  • cargo test-fastly && cargo test-fastly-reuse && cargo test-axum && cargo test-cloudflare && cargo test-spin.
  • All eight target-matched Clippy aliases: Fastly, Axum, Workers native/WASM, Spin native/WASM, CLI, and codegen.
  • Core primary-package all-target/all-feature Clippy, all-feature tests, native build-digest tests/lint, CLI and codegen tests.
  • Native adapters: Axum 44, Workers 53, Spin 87; core all-feature unit tests 2939; host parity 18; exact CI Rust runtime selection 63 plus separate Fastly EC lifecycle.
  • JS build, type checks, and Vitest: 50 files / 1208 tests.
  • JS format: cd crates/trusted-server-js/lib && npm run format.
  • Docs lint/build/format and repository Markdown format.
  • Fresh ordinary Fastly, Workers, and Spin release artifacts and Axum build; fictional fixture ID absent from all four.
  • 128-case publisher matrix across document shapes, codecs, auction presence, body source, and sink kind.
  • Non-Send yielding, ordering, shared deadline, timer ties, cancellation, poisoned/invalid candidates, unchanged-byte allocation identity, collection overflow, and fatal codec/sink regressions.
  • New partial-response, actual-status, and flush regressions: 36 focused native tests, including 64 sink cases, eight ranged-origin cases, nine live/inactive/suppressed origin-policy cases, and gated auction-prefix delivery. Final pinned WASI core gate: 2932 passed / 6 existing ignored.
  • Positive winning-bid finalization and raw-origin cache-double miss/hit regressions on native and pinned WASI runner.
  • Existing template-cache controls on pinned Viceroy 0.17: ESI 22, inline 9, and purge 34 checks, including cookie variants. Real winning bids preserved; ESI GPT scheduler check passed.
  • Existing browser controls: Next.js 21 passed / 1 expected skip; WordPress 11 passed / 11 expected skips; ownership 10 scenarios; escaping 2 tests.
  • Actual Axum, Workers, Spin, and Fastly disabled/success/hung controls; exact fallback body preserved at a 750 ms fixture budget.
  • Release-profile Workers/Spin/Fastly fixture controls. Fastly hung-call proof uses isolated Viceroy 0.19, which supports backend timeout simulation; repository pins remain unchanged.
  • Actual enabled-auction fallback controls on all four adapters, including a fresh release Workers run after the traced telemetry clock correction. Broader Cloudflare Worker panics when a server-side auction runs (std::time::Instant::now() on wasm32-unknown-unknown) #1240 paths are not declared fixed.
  • Harness cleanup: three real-process tests pass, including forced termination/reap. Actual Axum harness with --auction is now wired into CI; its exact command passed locally.
  • fastly compute serve: local Fastly verification used Viceroy directly.

Independent architecture and final reviews found no remaining blocking findings in this implementation. The re-review found no remaining blocking issues. Spin/Fastly actual release controls preceded the final range-policy-only correction; final native/WASI tests and fresh ordinary artifacts verify that correction. Workers and Axum actual controls were rerun afterward. Hosted CI results for the new commits remain separate from local results.

See the review hardening and latest verification and the implementation and verification record for evidence boundaries, runtime timings, and spec/plan alignment.

Checklist

  • Changes follow AGENTS.md conventions.
  • No new unwrap() in production code.
  • Uses the repository's log macros for production diagnostics.
  • New behavior has positive, negative, boundary, cancellation, and adapter tests.
  • No secrets or credentials committed; runtime harness configuration is fictional and isolated.
  • Scope reviewed: no generic streaming API change, EdgeZero repin, JS behavior change, KV wiring, or broad clock refactor. The traced telemetry clock correction is isolated in its own commit.

@prk-Jr prk-Jr self-assigned this Oct 12, 2026
Comment thread scripts/fixtures/async-html-finalization-runtime.py Fixed
Comment thread scripts/fixtures/async-html-finalization-runtime.py Fixed
Comment on lines +44 to +45
"import signal, time; signal.signal(signal.SIGTERM, signal.SIG_IGN); "
"print('example-ready', flush=True); time.sleep(300)"],

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Let an HTML stream processor await I/O when the document ends

2 participants