Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
0476999
Add ESI cacheable-root validation design for #1009
prk-Jr Aug 8, 2026
40de13e
Correct the hold-cost argument in the ESI cacheable-root design
prk-Jr Aug 8, 2026
f15eaba
Add the measurement and Stage 0 implementation plan for #1009
prk-Jr Aug 8, 2026
8e124ca
Record Step A findings for #1009: origin Vary check passes
prk-Jr Aug 8, 2026
86fa20f
Reopen ESI as feasible and separate it from the Stage 0 optimisation
prk-Jr Aug 10, 2026
bc6b400
Fix the docs build and the design breaks in the #1009 spike
prk-Jr Aug 10, 2026
86a39ac
Reconcile the #1009 documents with the request-neutrality correction
prk-Jr Aug 10, 2026
cf204f0
Close the remaining #1009 cross-document contradictions and gate the …
prk-Jr Aug 10, 2026
59dd9b0
Fix stale summaries and rebuild the invariant checker so it cannot fa…
prk-Jr Aug 10, 2026
d420999
Make the #1009 docs invariant gate fail closed
prk-Jr Aug 10, 2026
e286c5c
Remove the #1009 documentation invariant checker
prk-Jr Aug 10, 2026
62c4b70
Align the #1009 spec structure with what it actually covers
prk-Jr Aug 10, 2026
bcf2fd2
Add the esi crate to the Fastly adapter and record Task 1 as passed
prk-Jr Aug 10, 2026
b35f8df
Re-sequence the ESI spike for local-first validation
prk-Jr Aug 10, 2026
7781009
Add AssemblyMode and gate the head seam on template neutrality
prk-Jr Aug 10, 2026
606543f
Add the C2 shared-template cache gate
prk-Jr Aug 10, 2026
d9e0597
Decouple the body-close decision from the head script
prk-Jr Aug 10, 2026
f0ab7ac
Record Task 3 implementation progress and its limits
prk-Jr Aug 10, 2026
672836c
Decide the C2 cache design: cache::core, not read-through
prk-Jr Aug 10, 2026
fee4622
Guard the silent-waste and cookie gaps a review found
prk-Jr Aug 10, 2026
fbff408
Keep request-scoped diagnostics out of the shared template
prk-Jr Aug 11, 2026
fe88b77
Record the Task 3 code review and adopt its gate
prk-Jr Aug 11, 2026
6e6b21a
Render whole documents and compare bytes, and prove the test has teeth
prk-Jr Aug 11, 2026
04a3196
Add the C2 template cache trait, key and null object
prk-Jr Aug 11, 2026
ecc6c53
Back the C2 template cache with Fastly Core Cache
prk-Jr Aug 11, 2026
b688d66
Resolve the Vary chicken-and-egg for the C2 cache key
prk-Jr Aug 11, 2026
577eb85
Fail the C2 gate closed when the origin varies on an unkeyed header
prk-Jr Aug 11, 2026
2db1063
Store the transformed template when the C2 gate authorizes it
prk-Jr Aug 11, 2026
2a2e6c6
Serve a C2 hit without touching the origin
prk-Jr Aug 11, 2026
fe0658d
Execute the Core Cache join the publisher actually uses
prk-Jr Aug 11, 2026
0686431
Give the ESI seam a fragment to include
prk-Jr Aug 11, 2026
9539061
Prove ESI resolves the seam's marker on this toolchain
prk-Jr Aug 11, 2026
0597f54
State the ESI processor's safety settings instead of inheriting them
prk-Jr Aug 11, 2026
2617ecc
Record that ESI's mechanism is proven and its defaults are not safe
prk-Jr Aug 11, 2026
0adb578
Stop a C2 hit from serving a shared-cacheable per-user document
prk-Jr Aug 11, 2026
b3ac59a
Resolve the ESI include in the request path so ads render
prk-Jr Aug 11, 2026
4c55734
Read the origin's Cache-Control, not the one TS just wrote
prk-Jr Aug 11, 2026
76df246
Close the three Task 6 gates that do not need a deployment
prk-Jr Aug 11, 2026
00b8e02
Record the local run, and the pattern the bugs on this branch share
prk-Jr Aug 11, 2026
f1d9cb7
Architect streaming assembly, and correct the latency reading
prk-Jr Aug 11, 2026
bb8811c
Demonstrate that a cached root alone buys the reader nothing
prk-Jr Aug 11, 2026
09b7cb7
Exclude non-GET from the shared cache, and close the ClientFill cover…
prk-Jr Aug 11, 2026
d1edffe
Stop the shared cache from being inert against a real origin
prk-Jr Aug 11, 2026
712f26c
Record the review, including what it says about reviewing
prk-Jr Aug 11, 2026
81eeec6
Add a local harness, and measure a ~100x TTFB regression with it
prk-Jr Aug 11, 2026
073ff79
Fall back to inline whenever no shared template was authorized
prk-Jr Aug 11, 2026
7828af1
Make the seam marker observable, since it is invisible by design
prk-Jr Aug 11, 2026
1761320
Stream the cache hit, so the article arrives before the auction resolves
prk-Jr Aug 11, 2026
edaf311
Ask the origin for identity under shared modes, and make the harness …
prk-Jr Aug 11, 2026
89f7417
Decode the transform output instead of crippling the origin fetch
prk-Jr Aug 11, 2026
a41d3e6
Assemble by byte seam, and carry slots through it
prk-Jr Aug 11, 2026
2bc4624
Make the shared-template path deliver bids, and make its tests notice
prk-Jr Aug 12, 2026
4ec9104
Plan the #1009 main merge and ESI hardening
prk-Jr Aug 12, 2026
b0568ba
Merge main into #1009 ESI cacheable-root work
prk-Jr Aug 12, 2026
e255521
Remove unused parser and client-fill spike paths
prk-Jr Aug 12, 2026
7a53900
Canonicalize shared template cache keys
prk-Jr Aug 12, 2026
6d84ab2
Harden ESI template freshness and miss collapse
prk-Jr Aug 12, 2026
67a5706
Complete ESI byte-seam hardening
prk-Jr Aug 12, 2026
024cb5b
Verify C2 assembly end to end
prk-Jr Aug 12, 2026
cbcbcc4
Document ESI rollout and rollback
prk-Jr Aug 12, 2026
62ab04a
Honor Fastly surrogate freshness for ESI
prk-Jr Aug 12, 2026
fe907ac
Allow reloads to reuse ESI templates
prk-Jr Aug 12, 2026
b80c094
Make ESI template lifetime configurable
prk-Jr Aug 12, 2026
2af557a
Simplify the shared-template ad seam
prk-Jr Aug 13, 2026
4868436
Clarify ESI byte-seam terminology
prk-Jr Aug 13, 2026
d173079
Merge branch 'main' into 1009-esi-cacheable-root-spec
prk-Jr Aug 13, 2026
3843e40
Restore ESI parsing for cold template assembly
prk-Jr Aug 14, 2026
fa37cfb
Merge current main into #1009 ESI cacheable-root work
prk-Jr Aug 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ jobs:
run: echo "viceroy-version=$(grep '^viceroy ' .tool-versions | awk '{print $2}')" >> $GITHUB_OUTPUT
shell: bash

- name: Retrieve Node.js version
id: node-version
run: echo "node-version=$(grep '^nodejs ' .tool-versions | awk '{print $2}')" >> $GITHUB_OUTPUT
shell: bash

- name: Set up Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
Expand All @@ -45,9 +50,20 @@ jobs:
if: steps.cache-viceroy.outputs.cache-hit != 'true'
run: cargo install viceroy --version "${{ steps.viceroy-version.outputs.viceroy-version }}" --locked --force

- name: Use Node.js for the served-seam contract
uses: actions/setup-node@v4
with:
node-version: ${{ steps.node-version.outputs.node-version }}

- name: Run tests
run: cargo test-fastly

- name: Run C2 ESI local harness
run: ./scripts/c2-local-test.sh esi

- name: Run inline control harness
run: ./scripts/c2-local-test.sh inline

test-axum:
name: cargo test (axum native)
runs-on: ubuntu-latest
Expand Down
113 changes: 102 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ getrandom = "0.2"
hex = "0.4.3"
hmac = "0.12.1"
http = "1.4.0"
httpdate = "1.0.3"
http-body-util = "0.1"
hyper = "1"
hyper-util = "0.1"
Expand Down
2 changes: 2 additions & 0 deletions crates/trusted-server-adapter-fastly/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ async-trait = { workspace = true }
base64 = { workspace = true }
bytes = { workspace = true }
chrono = { workspace = true }
derive_more = { workspace = true }
edgezero-adapter-fastly = { workspace = true, features = ["fastly"] }
edgezero-core = { workspace = true }
error-stack = { workspace = true }
esi = { git = "https://github.com/stackpop/esi.git", rev = "4c53feab4d22ad9a84641b4c46f3f63bc6d197e2" }
fastly = { workspace = true }
fern = { workspace = true }
futures = { workspace = true }
Expand Down
40 changes: 39 additions & 1 deletion crates/trusted-server-adapter-fastly/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,11 @@ fn build_per_request_services(state: &AppState, ctx: &RequestContext) -> Runtime
.config_store(Arc::new(FastlyPlatformConfigStore))
.secret_store(Arc::new(FastlyPlatformSecretStore))
.kv_store(Arc::clone(&state.default_kv_store))
// Spike-only (#1009). Constructed unconditionally, but only read when the
// assembly mode is a shared-template one — which defaults to Inline, so this
// is inert until an operator opts in.
.template_cache(Arc::new(crate::template_cache::FastlyTemplateCache::new()))
.template_assembler(Arc::new(crate::esi_assembly::FastlyTemplateAssembler))
.backend(Arc::new(FastlyPlatformBackend))
.http_client(Arc::new(FastlyPlatformHttpClient))
.geo(Arc::new(FastlyPlatformGeo))
Expand Down Expand Up @@ -1239,12 +1244,15 @@ mod tests {

use super::{
AppState, NAMED_ROUTES, NamedRouteHandler, PAGE_BIDS_LEGACY_PATH, PAGE_BIDS_PATH,
TrustedServerApp, build_state_from_settings, startup_error_router,
TrustedServerApp, build_per_request_services, build_state_from_settings,
startup_error_router,
};
use bytes::Bytes;
use edgezero_core::body::Body;
use edgezero_core::context::RequestContext;
use edgezero_core::http::{Method, Response, StatusCode, header, request_builder};
use edgezero_core::key_value_store::NoopKvStore;
use edgezero_core::params::PathParams;
use edgezero_core::router::RouterService;
use std::net::{IpAddr, Ipv4Addr};
use std::sync::Mutex;
Expand Down Expand Up @@ -1379,6 +1387,36 @@ mod tests {
TrustedServerApp::routes_for_state(&state)
}

#[test]
fn per_request_services_register_the_fastly_template_assembler() {
let state = build_state_from_settings(test_settings()).expect("should build test state");
let context = RequestContext::new(
empty_request(Method::GET, "/article"),
PathParams::default(),
);

let services = build_per_request_services(&state, &context);
let template = format!(
"<html><body>article{}</body></html>",
trusted_server_core::publisher::AD_ASSEMBLY_SEAM
);
let fragment = b"<script>reader state</script>";
let assembled = services
.template_assembler()
.assemble(template.as_bytes(), fragment)
.expect("Fastly services should provide ESI assembly");

assert_eq!(
assembled,
template
.replace(
trusted_server_core::publisher::AD_ASSEMBLY_SEAM,
std::str::from_utf8(fragment).expect("fragment should be UTF-8")
)
.into_bytes()
);
}

/// Builds a router whose `AppState` uses a registry containing the given
/// request filters (and no routes), so dispatch-level request-filter
/// behavior can be exercised without a real integration.
Expand Down
Loading
Loading