Skip to content

Add a server-side ad template switch and cache policy - #1008

Open
ChristianPavilonis wants to merge 4 commits into
mainfrom
issue-1007-cache-control
Open

Add a server-side ad template switch and cache policy#1008
ChristianPavilonis wants to merge 4 commits into
mainfrom
issue-1007-cache-control

Conversation

@ChristianPavilonis

@ChristianPavilonis ChristianPavilonis commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add a clear [creative_opportunities].enabled switch for publisher server-side ad-template delivery.
  • Set inactive successful GET publisher documents to the issue Improve cache header for html content when SSAT is off #1007 browser cache policy (max-age=60), intentionally replacing the origin browser policy while leaving non-200/non-document responses and CDN-specific cache headers unchanged.
  • Keep direct POST /auction available when publisher templates are disabled.

Issue #1007 exposed that publisher HTML caching was tied to whether the server-side ad stack ran, while the global auction setting also controlled unrelated auction behavior. This change separates publisher template delivery from the direct auction API and makes the cache behavior explicit.

Changes

File Change
crates/trusted-server-core/src/creative_opportunities.rs Adds the default-true enabled configuration field and serialization coverage.
crates/trusted-server-core/src/settings.rs Hides creative-opportunity slots from runtime handlers when template delivery is disabled and tests environment overrides.
crates/trusted-server-core/src/config.rs Verifies compatibility for omitted defaults and explicit disabled values.
crates/trusted-server-core/src/publisher.rs Gates publisher HTML and page-bids template delivery, records the disabled-template reason, and tests cache behavior.
crates/trusted-server-core/src/auction/endpoints.rs Proves direct POST /auction still dispatches when templates are disabled.
trusted-server.example.toml Documents the new setting in the example configuration.
docs/guide/configuration.md Documents the switch, cache policy, and environment override.
CHANGELOG.md Records the new switch and cache behavior.
crates/trusted-server-js/lib/src/core/index.ts Clarifies browser defaults when template delivery is gated.
crates/trusted-server-js/lib/src/integrations/gpt/index.ts Clarifies page-bids behavior when template delivery is disabled.
crates/trusted-server-js/lib/test/integrations/gpt/spa_hook.test.ts Updates the related regression-test explanation.

Scope

The change is limited to configuration, core publisher/page-bids execution, direct-auction regression coverage, browser comments, and documentation. Existing adapter routes already use the centralized settings accessor, so no divergent adapter-specific switch was needed. Active server-side templates retain private, no-store; inactive 200 OK GET document HTML uses exactly max-age=60, intentionally replacing the origin browser policy per #1007. Non-200, non-GET, and non-document responses retain the origin policy, request-scoped privacy finalization still takes precedence, and validators plus CDN-specific headers remain unchanged. An empty slot list could disable delivery rollback-safely, but the dedicated switch preserves configured slot definitions for reversible operations; because explicit enabled = false is serialized, the guide documents the required config re-push before rolling back to a pre-field binary.

Closes

Closes #1007

Test plan

  • cargo test-fastly && cargo test-axum
  • cargo clippy-fastly && cargo clippy-axum
  • cargo fmt --all -- --check
  • JS tests: cd crates/trusted-server-js/lib && npx vitest run
  • JS format: cd crates/trusted-server-js/lib && npm run format
  • Docs format: cd docs && npm run format
  • WASM build: cargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1
  • Manual testing via fastly compute serve
  • Other: cargo test-cloudflare, cargo test-spin, focused publisher tests, and all configured native/WASM clippy targets

Checklist

  • Changes follow CLAUDE.md conventions
  • No unwrap() in production code — use expect("should ...")
  • Uses project logging macros, not println!
  • New code has tests
  • No secrets or credentials committed

@ChristianPavilonis ChristianPavilonis self-assigned this Aug 6, 2026
@ChristianPavilonis ChristianPavilonis changed the title Use a short browser cache policy for non-SSAT HTML Use a browser cache policy for non-SSAT HTML Aug 6, 2026
@ChristianPavilonis ChristianPavilonis changed the title Use a browser cache policy for non-SSAT HTML Add a server-side ad template switch and cache policy Aug 7, 2026

@prk-Jr prk-Jr left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Separating publisher template delivery from the global [auction].enabled kill switch is the right boundary, and the direct-/auction regression test proves the separation holds. The configuration half of this PR is well covered.

The cache-policy half needs work. The new else branch does not just preserve origin policy when templates are inactive — it overwrites it, and its only protection is a two-token check for private/no-store. I verified against this branch (throwaway probes in ssat_cache_policy_tests, cargo test -p trusted-server-core) that origin no-cache, max-age=0, must-revalidate and s-maxage=0 are all replaced with max-age=60, and that 500/404/503 HTML responses become cacheable for 60 seconds. Details inline.

Blocking

🔧 wrench

  • Origin revalidation directives silently overwritten — the private/no-store guard misses no-cache, max-age=0, must-revalidate, s-maxage=0. A personalized page marked no-cache by origin, on a repeat visit that emits no Set-Cookie (so the adapter cookie-privacy net does not fire), gets max-age=60 with no private and no Vary — shared-cacheable and replayable to other users for 60s. (crates/trusted-server-core/src/publisher.rs:2986)
  • Non-2xx HTML becomes cacheable for 60s — no status gate on the new branch, so a transient origin 5xx is pinned in every browser and intermediary for a minute past recovery. (crates/trusted-server-core/src/publisher.rs:2961)
  • Rollback with enabled = false is a site-wide 500, and is undocumenteddeny_unknown_fields makes an older binary reject the blob, and load_settings_from_config_store() failing returns 500 for every request (crates/trusted-server-adapter-fastly/src/main.rs:112-118). The plan file states the "fail loud" intent, but neither docs/guide/configuration.md:1315 nor the CHANGELOG.md:12 entry warns operators what "loud" means here. (crates/trusted-server-core/src/config.rs:333)

❓ question

  • Absent [creative_opportunities] section also gets its cache policy rewrittenis_some_and makes "never configured" behave like "explicitly disabled", so deployments that never enabled server-side ad templates have their origin Cache-Control replaced with max-age=60. On main those responses passed through untouched. Intended blast radius? (crates/trusted-server-core/src/publisher.rs:2643)

Non-blocking

🤔 thinking

  • max-age=60 is an unexplained magic constant — no derivation in the CHANGELOG, configuration guide, or plan file, and not operator-tunable. (crates/trusted-server-core/src/publisher.rs:2992)
  • The empty-slot disable already existed and was rollback-safe — worth stating in the PR body why the new field's rollback cost was accepted. (crates/trusted-server-core/src/creative_opportunities.rs:206)

♻️ refactor

  • ad_templates_enabled / ad_templates_disabled are not complements — both are false when the section is absent; an explicit three-state enum would make that unmissable. (crates/trusted-server-core/src/publisher.rs:2644)

⛏ nitpick

  • should_run_server_side_ad_stack still takes 7 arguments — the new struct absorbed only 2 of the 8 flags, leaving 6 positional bools at every call site. (crates/trusted-server-core/src/publisher.rs:1765)
  • Test name contradicts its assertiondisabled_creative_opportunities_flag_is_visible_to_legacy_schema asserts expect_err, i.e. the legacy schema rejects the field. ..._is_rejected_by_legacy_schema would read correctly. (crates/trusted-server-core/src/config.rs:333)

📝 note

  • Cache-policy test matrix has the same gap as the codenavigation_without_matched_slots_preserves_private_origin_cache_policy covers "private, max-age=0" and "No-Store" only. Once the two wrench findings are settled, no-cache and a non-200 status belong in the same loop, or the regressions will not be caught. (crates/trusted-server-core/src/publisher.rs:5071)

👍 praise

  • Direct /auction regression testTemplateSwitchProbeProvider counts real provider invocations rather than asserting a status code, so it would actually fail if the template flag were later threaded into handle_auction. (crates/trusted-server-core/src/auction/endpoints.rs:707)
  • Rollback-compatible serialization of the defaultskip_serializing_if keeping default true out of pushed blobs matches the existing section_root precedent and keeps the no-opt-in case safe. (crates/trusted-server-core/src/creative_opportunities.rs:204)

CI Status

All 19 GitHub checks pass on 58054463.

  • fmt: PASS
  • clippy (fastly / axum / cloudflare native+wasm / spin native+wasm): PASS
  • rust tests (fastly, axum native, cloudflare, spin, cross-adapter parity, ts CLI): PASS
  • js tests (vitest): PASS
  • format-typescript / format-docs: PASS
  • integration + browser integration tests: PASS

The findings above are behavioral gaps that the current test matrix does not exercise, not CI failures.

Comment thread crates/trusted-server-core/src/publisher.rs Outdated
Comment thread crates/trusted-server-core/src/publisher.rs
Comment thread crates/trusted-server-core/src/config.rs Outdated
Comment thread crates/trusted-server-core/src/publisher.rs
Comment thread crates/trusted-server-core/src/publisher.rs Outdated
Comment thread crates/trusted-server-core/src/creative_opportunities.rs
Comment thread crates/trusted-server-core/src/publisher.rs
Comment thread crates/trusted-server-core/src/publisher.rs
Comment thread crates/trusted-server-core/src/auction/endpoints.rs
Comment thread crates/trusted-server-core/src/creative_opportunities.rs

@aram356 aram356 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

The switch mechanics are solid: default-true serde field with rollback-aware serialization, consistent accessor/handler gating, POST /auction independence proven by a provider-probe test, and validation still runs when disabled. The blocking concern is concentrated in the new cache-clamp branch, which overrides origin freshness directives beyond the private/no-store preserve-guard.

Blocking

🔧 wrench

  • Cache clamp overrides origin freshness directives beyond private/no-store: origins sending no-cache, must-revalidate, or max-age=0 get replaced with max-age=60 (crates/trusted-server-core/src/publisher.rs:2988 — see inline comment)

Non-blocking

🤔 thinking

  • Clamp blast radius: applies to all HTML, all methods, and publishers with no [creative_opportunities] section at all (crates/trusted-server-core/src/publisher.rs:2980 — see inline comment)
  • enabled = false config blobs break not-yet-upgraded binaries: the explicit-false rollback hazard is codified in a test but undocumented for operators (crates/trusted-server-core/src/config.rs:333 — see inline comment)

🌱 seedling

  • Hardcoded 60-second TTL: likely needs to become configurable when SSAT is re-architected for cacheability (crates/trusted-server-core/src/publisher.rs:2992 — see inline comment)

CI Status

  • fmt: PASS
  • clippy (all targets): PASS
  • rust tests (fastly/axum/cloudflare/spin/parity/CLI): PASS
  • js tests (vitest): PASS
  • browser integration tests: PASS

Comment thread crates/trusted-server-core/src/publisher.rs Outdated
Comment thread crates/trusted-server-core/src/publisher.rs Outdated
Comment thread crates/trusted-server-core/src/config.rs Outdated
Comment thread crates/trusted-server-core/src/publisher.rs Outdated
@aram356 aram356 added this to the 202608 milestone Aug 13, 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.

Improve cache header for html content when SSAT is off

3 participants