Skip to content

Use web_time clocks on Cloudflare request paths - #1242

Closed
erik-sv wants to merge 1 commit into
IABTechLab:mainfrom
erik-sv:wasm-safe-clock
Closed

erik-sv wants to merge 1 commit into
IABTechLab:mainfrom
erik-sv:wasm-safe-clock

Conversation

@erik-sv

@erik-sv erik-sv commented Oct 5, 2026

Copy link
Copy Markdown

Summary

  • std::time::Instant::now() and std::time::SystemTime::now() panic on wasm32-unknown-unknown, the Cloudflare Workers target. A server-side auction on Cloudflare calls one of them for every dispatched auction, so the Worker crashes (Cloudflare Worker panics when a server-side auction runs (std::time::Instant::now() on wasm32-unknown-unknown) #1240). This PR moves the request-path call sites in trusted-server-core to web_time.
  • It adds scripts/lint-wasm-clock.sh, run in CI, so the std calls cannot come back.
  • Nothing changes on Fastly, Axum or Spin: on those targets web_time re-exports the std types.

Changes

File Change
crates/trusted-server-core/src/auction/telemetry.rs Instant from web_time. Covers AuctionObservationContext::from_parts (the reported panic) and the elapsed() call.
crates/trusted-server-core/src/integrations/datadome/protection_scope.rs Instant from web_time; Duration stays std::time. Protection-scope cache expiry.
crates/trusted-server-core/src/publisher.rs Instant from web_time for template-cache expiry. origin_shared_ttl derives its SystemTime from web_time::SystemTime::now(); origin_shared_ttl_at still takes a std SystemTime because httpdate parses into that type.
scripts/lint-wasm-clock.sh, scripts/clippy-wasm-clock/clippy.toml A clippy pass on wasm32-unknown-unknown with its own config that denies only std::time::Instant::now and std::time::SystemTime::now.
.github/workflows/format.yml Runs the lint after cargo clippy-cloudflare-wasm.
AGENTS.md Lists the lint under CI gates.

Why a separate lint config. A disallowed-methods rule in the workspace clippy.toml cannot work: on native and wasm32-wasip1, web_time::Instant and web_time::SystemTime are the std types, so the rule also flags every correct web_time call. In a trial it produced about 36 false positives in cargo clippy-fastly. Only wasm32-unknown-unknown has distinct types, so the lint runs on that target alone. It covers trusted-server-core; adapter crates are not linted.

Left alone: chrono::Utc::now() in auction/telemetry.rs, rotation.rs and integrations/datadome/protection.rs. chrono's default wasmbind feature reads the JS clock on wasm32-unknown-unknown. That is inferred from chrono's default features and was not run on Cloudflare.

Closes

Closes #1240

Test plan

Run locally on this branch, based on 80483011:

  • cargo fmt --all -- --check
  • cargo clippy-fastly, clippy-axum, clippy-cloudflare, clippy-cloudflare-wasm, clippy-spin-native, clippy-spin-wasm, clippy-cli, clippy-codegen
  • scripts/lint-wasm-clock.sh
  • cargo test-fastly (3110 passed), cargo test-axum (43), cargo test-cloudflare (53), cargo test-spin (87); 0 failed
  • cargo test --manifest-path crates/trusted-server-integration-tests/Cargo.toml --test parity (17 passed)
  • WASM build: cargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1
  • Docs format, and the root markdown prettier check from AGENTS.md (covers the AGENTS.md edit)

Regression guard: the panic happens only inside a wasm32-unknown-unknown runtime, which the native and Fastly test runners do not provide, so the lint is the regression test. On 80483011 without the core change, scripts/lint-wasm-clock.sh fails on all six call sites (telemetry.rs:201, protection_scope.rs:405 and :435, publisher.rs:2336, :5239 and :6465). With the change it is clean.

Real Cloudflare runtime: before the rebase, on 666953a0, the Cloudflare adapter under wrangler dev --local (worker-build bundle) crashed with RuntimeError: unreachable in AuctionObservationContext::from_parts on an auction page. With this change, the same setup served the page with bids, and the bidder received exactly one auction request.

Not exercised:

  • The template-cache and DataDome paths on a live Worker. They were found by reading the code and are covered by the lint only.
  • JS tests and JS format: no JS changed.

Checklist

  • Changes follow AGENTS.md conventions
  • No unwrap() in production code
  • Uses tracing macros (not println!)
  • New code has a regression guard (the lint above)
  • No secrets or credentials committed

std::time::Instant::now and SystemTime::now panic with
RuntimeError: unreachable on wasm32-unknown-unknown, which crashed the
server-side auction (AuctionObservationContext) and would also crash the
template cache and DataDome protection-scope cache on Cloudflare Workers.

Switch those call sites to web_time (a std re-export on native and WASI).
Add scripts/lint-wasm-clock.sh, run in CI, which uses a wasm32-unknown-unknown
clippy pass to reject std clock calls in trusted-server-core.
@aram356

aram356 commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator

Thanks for this, @erik-sv! #1226 makes the same change (the web_time switch in auction telemetry, the DataDome protection scope and the publisher template cache, plus the wasm32-unknown-unknown clock lint), so we'll fold these fixes into #1226 and land them there.

@aram356

aram356 commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator

Closing since duplicate of #1226

@aram356 aram356 closed this Oct 9, 2026
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.

Cloudflare Worker panics when a server-side auction runs (std::time::Instant::now() on wasm32-unknown-unknown)

2 participants