From bc13f2773ba97e8de07f7ba6ef6f3d0a3e37427f Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Mon, 20 Jul 2026 16:38:03 -0700 Subject: [PATCH 01/21] Upgrade EdgeZero to the deploy-actions branch Point the edgezero-* dependencies at the feature/edgezero-deploy-actions branch (PR #316) and adapt Trusted Server to its API changes: - Wire the new ts CLI subcommands surfaced by edgezero-cli: active-version, healthcheck, and rollback, plus deploy --stage and a --version flag, with argument-parsing coverage. - Migrate TrustedServerAppConfig to the AppConfigMeta::secret_fields() method that replaces the removed SECRET_FIELDS associated constant. --- Cargo.lock | 37 +++-- Cargo.toml | 12 +- crates/trusted-server-cli/src/run.rs | 174 ++++++++++++++++++++++- crates/trusted-server-core/src/config.rs | 4 +- 4 files changed, 205 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 68f14e753..bd92ec5ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1398,7 +1398,7 @@ dependencies = [ [[package]] name = "edgezero-adapter" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?tag=v0.0.4#9e661ae520a8130660f18fd10f42703d7f3e050b" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#145f1699d8e8f51027804f21faddbf985cff9a1e" dependencies = [ "toml", ] @@ -1406,7 +1406,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-axum" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?tag=v0.0.4#9e661ae520a8130660f18fd10f42703d7f3e050b" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#145f1699d8e8f51027804f21faddbf985cff9a1e" dependencies = [ "anyhow", "async-trait", @@ -1434,7 +1434,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-cloudflare" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?tag=v0.0.4#9e661ae520a8130660f18fd10f42703d7f3e050b" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#145f1699d8e8f51027804f21faddbf985cff9a1e" dependencies = [ "anyhow", "async-trait", @@ -1449,7 +1449,7 @@ dependencies = [ "log", "serde_json", "tempfile", - "toml_edit", + "toml_edit 0.25.12+spec-1.1.0", "walkdir", "worker", ] @@ -1457,7 +1457,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-fastly" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?tag=v0.0.4#9e661ae520a8130660f18fd10f42703d7f3e050b" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#145f1699d8e8f51027804f21faddbf985cff9a1e" dependencies = [ "anyhow", "async-stream", @@ -1479,14 +1479,14 @@ dependencies = [ "serde_json", "sha2 0.10.9", "thiserror 2.0.18", - "toml_edit", + "toml_edit 0.25.12+spec-1.1.0", "walkdir", ] [[package]] name = "edgezero-adapter-spin" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?tag=v0.0.4#9e661ae520a8130660f18fd10f42703d7f3e050b" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#145f1699d8e8f51027804f21faddbf985cff9a1e" dependencies = [ "anyhow", "async-trait", @@ -1506,14 +1506,14 @@ dependencies = [ "subtle", "thiserror 2.0.18", "toml", - "toml_edit", + "toml_edit 0.25.12+spec-1.1.0", "walkdir", ] [[package]] name = "edgezero-cli" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?tag=v0.0.4#9e661ae520a8130660f18fd10f42703d7f3e050b" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#145f1699d8e8f51027804f21faddbf985cff9a1e" dependencies = [ "chrono", "clap", @@ -1538,7 +1538,7 @@ dependencies = [ [[package]] name = "edgezero-core" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?tag=v0.0.4#9e661ae520a8130660f18fd10f42703d7f3e050b" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#145f1699d8e8f51027804f21faddbf985cff9a1e" dependencies = [ "anyhow", "async-compression", @@ -1569,7 +1569,7 @@ dependencies = [ [[package]] name = "edgezero-macros" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?tag=v0.0.4#9e661ae520a8130660f18fd10f42703d7f3e050b" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#145f1699d8e8f51027804f21faddbf985cff9a1e" dependencies = [ "log", "proc-macro2", @@ -5074,6 +5074,19 @@ dependencies = [ "winnow 0.7.15", ] +[[package]] +name = "toml_edit" +version = "0.25.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.3", +] + [[package]] name = "toml_parser" version = "1.1.2+spec-1.1.0" @@ -5339,7 +5352,7 @@ dependencies = [ "tokio", "tokio-rustls", "toml", - "toml_edit", + "toml_edit 0.23.10+spec-1.0.0", "trusted-server-core", "url", "webpki-roots", diff --git a/Cargo.toml b/Cargo.toml index 695099d49..54569599b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,12 +53,12 @@ criterion = { version = "0.5", default-features = false, features = ["cargo_benc derive_more = { version = "2.0", features = ["display", "error"] } directories = "5" ed25519-dalek = { version = "2.2", features = ["rand_core"] } -edgezero-adapter-axum = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.4", default-features = false } -edgezero-adapter-cloudflare = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.4", default-features = false } -edgezero-adapter-fastly = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.4", default-features = false } -edgezero-adapter-spin = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.4", default-features = false } -edgezero-cli = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.4" } -edgezero-core = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.4", default-features = false } +edgezero-adapter-axum = { git = "https://github.com/stackpop/edgezero", branch = "feature/edgezero-deploy-actions", default-features = false } +edgezero-adapter-cloudflare = { git = "https://github.com/stackpop/edgezero", branch = "feature/edgezero-deploy-actions", default-features = false } +edgezero-adapter-fastly = { git = "https://github.com/stackpop/edgezero", branch = "feature/edgezero-deploy-actions", default-features = false } +edgezero-adapter-spin = { git = "https://github.com/stackpop/edgezero", branch = "feature/edgezero-deploy-actions", default-features = false } +edgezero-cli = { git = "https://github.com/stackpop/edgezero", branch = "feature/edgezero-deploy-actions" } +edgezero-core = { git = "https://github.com/stackpop/edgezero", branch = "feature/edgezero-deploy-actions", default-features = false } env_logger = "0.11" error-stack = "0.6" fastly = "0.12" diff --git a/crates/trusted-server-cli/src/run.rs b/crates/trusted-server-cli/src/run.rs index 1b0bdfa29..b3be949a5 100644 --- a/crates/trusted-server-cli/src/run.rs +++ b/crates/trusted-server-cli/src/run.rs @@ -2,8 +2,8 @@ use std::process; use clap::{Parser, Subcommand}; use edgezero_cli::args::{ - AuthArgs, BuildArgs, ConfigDiffArgs, ConfigPushArgs, ConfigValidateArgs, DeployArgs, - ProvisionArgs, ServeArgs, + ActiveVersionArgs, AuthArgs, BuildArgs, ConfigDiffArgs, ConfigPushArgs, ConfigValidateArgs, + DeployArgs, HealthcheckArgs, ProvisionArgs, RollbackArgs, ServeArgs, }; use trusted_server_core::config::TrustedServerAppConfig; @@ -13,7 +13,7 @@ use crate::commands::config::init::{ConfigInitArgs, run_config_init}; use crate::prebid_bundle::{NpmPrebidBundleGenerator, PrebidBundleArgs, run_bundle}; #[derive(Debug, Parser)] -#[command(name = "ts", about = "Trusted Server CLI")] +#[command(name = "ts", version, about = "Trusted Server CLI")] struct Args { #[command(subcommand)] command: Command, @@ -21,6 +21,8 @@ struct Args { #[derive(Debug, Subcommand)] enum Command { + /// Print the currently active deployment version for a target adapter. + ActiveVersion(ActiveVersionArgs), /// Audit a public page and write draft Trusted Server artifacts. Audit(AuditArgs), /// Sign in / out / status against an `EdgeZero` adapter. @@ -32,10 +34,14 @@ enum Command { Config(ConfigCommand), /// Deploy the project through a target adapter. Deploy(DeployArgs), + /// Probe a deployed version until it reports healthy. + Healthcheck(HealthcheckArgs), /// Trusted Server Prebid commands. Prebid(PrebidArgs), /// Provision platform resources through a target adapter. Provision(ProvisionArgs), + /// Roll a service back to a previously active deployment version. + Rollback(RollbackArgs), /// Serve the project locally through a target adapter. Serve(ServeArgs), /// Local developer tools (e.g. the macOS-only production-hostname proxy). @@ -79,6 +85,7 @@ pub fn run_from_env() -> Result<(), String> { fn dispatch(args: Args) -> Result<(), String> { match args.command { + Command::ActiveVersion(args) => edgezero_cli::run_active_version(&args), Command::Audit(args) => { let stdout = std::io::stdout(); let mut out = stdout.lock(); @@ -102,6 +109,7 @@ fn dispatch(args: Args) -> Result<(), String> { edgezero_cli::run_config_validate_typed::(&args) } Command::Deploy(args) => edgezero_cli::run_deploy(&args), + Command::Healthcheck(args) => edgezero_cli::run_healthcheck(&args), Command::Prebid(prebid) => { let mut generator = NpmPrebidBundleGenerator; let mut stdout = std::io::stdout(); @@ -113,6 +121,7 @@ fn dispatch(args: Args) -> Result<(), String> { } } Command::Provision(args) => edgezero_cli::run_provision(&args), + Command::Rollback(args) => edgezero_cli::run_rollback(&args), Command::Serve(args) => edgezero_cli::run_serve(&args), Command::Dev(command) => crate::commands::dev::run(command), } @@ -131,6 +140,165 @@ mod tests { Args::try_parse_from(args).expect("should parse args") } + #[test] + fn parses_active_version() { + let args = parse(&[ + "ts", + "active-version", + "--adapter", + "fastly", + "--service-id", + "service-123", + ]); + let Command::ActiveVersion(active_version) = args.command else { + panic!("expected active-version command"); + }; + assert_eq!(active_version.adapter, "fastly"); + assert_eq!(active_version.service_id, "service-123"); + } + + #[test] + fn parses_healthcheck_with_retry_defaults() { + let args = parse(&[ + "ts", + "healthcheck", + "--adapter", + "fastly", + "--service-id", + "service-123", + "--version", + "7", + "--domain", + "edge.example", + ]); + let Command::Healthcheck(healthcheck) = args.command else { + panic!("expected healthcheck command"); + }; + assert_eq!(healthcheck.domain, "edge.example"); + assert_eq!(healthcheck.version, "7"); + assert_eq!(healthcheck.retry, 3, "should default to 3 retries"); + assert_eq!( + healthcheck.retry_delay, 5, + "should default to a 5s retry delay" + ); + assert_eq!(healthcheck.timeout, 10, "should default to a 10s timeout"); + assert!(!healthcheck.staging, "should probe production by default"); + } + + #[test] + fn parses_healthcheck_with_staging_overrides() { + let args = parse(&[ + "ts", + "healthcheck", + "--adapter", + "fastly", + "--service-id", + "service-123", + "--version", + "7", + "--domain", + "edge.example", + "--staging", + "--retry", + "9", + "--retry-delay", + "2", + "--timeout", + "30", + ]); + let Command::Healthcheck(healthcheck) = args.command else { + panic!("expected healthcheck command"); + }; + assert!(healthcheck.staging); + assert_eq!(healthcheck.retry, 9); + assert_eq!(healthcheck.retry_delay, 2); + assert_eq!(healthcheck.timeout, 30); + } + + #[test] + fn healthcheck_requires_domain() { + Args::try_parse_from([ + "ts", + "healthcheck", + "--adapter", + "fastly", + "--service-id", + "service-123", + "--version", + "7", + ]) + .expect_err("should reject healthcheck without a domain"); + } + + #[test] + fn parses_rollback_with_explicit_target() { + let args = parse(&[ + "ts", + "rollback", + "--adapter", + "fastly", + "--service-id", + "service-123", + "--version", + "8", + "--rollback-to", + "7", + ]); + let Command::Rollback(rollback) = args.command else { + panic!("expected rollback command"); + }; + assert_eq!(rollback.version, "8"); + assert_eq!(rollback.rollback_to, Some("7".to_owned())); + assert!(!rollback.staging); + } + + #[test] + fn parses_staging_rollback_without_target() { + let args = parse(&[ + "ts", + "rollback", + "--adapter", + "fastly", + "--service-id", + "service-123", + "--version", + "8", + "--staging", + ]); + let Command::Rollback(rollback) = args.command else { + panic!("expected rollback command"); + }; + assert!(rollback.staging); + assert_eq!( + rollback.rollback_to, None, + "staging rollback should not need an explicit target" + ); + } + + #[test] + fn rollback_requires_service_id() { + Args::try_parse_from(["ts", "rollback", "--adapter", "fastly", "--version", "8"]) + .expect_err("should reject rollback without a service id"); + } + + #[test] + fn parses_deploy_with_staging_flags() { + let args = parse(&[ + "ts", + "deploy", + "--adapter", + "fastly", + "--service-id", + "service-123", + "--stage", + ]); + let Command::Deploy(deploy) = args.command else { + panic!("expected deploy command"); + }; + assert_eq!(deploy.service_id, Some("service-123".to_owned())); + assert!(deploy.stage); + } + #[test] fn parses_audit_with_default_outputs() { let args = parse(&["ts", "audit", "https://publisher.example"]); diff --git a/crates/trusted-server-core/src/config.rs b/crates/trusted-server-core/src/config.rs index 7bbecd747..991ed7a2f 100644 --- a/crates/trusted-server-core/src/config.rs +++ b/crates/trusted-server-core/src/config.rs @@ -110,7 +110,9 @@ impl edgezero_core::app_config::AppConfigMeta for TrustedServerAppConfig { // app-config blob. Migrating app-level secrets to `EdgeZero` secret-store // references needs nested/array extraction support and operator migration // work tracked separately. - const SECRET_FIELDS: &'static [edgezero_core::app_config::SecretField] = &[]; + fn secret_fields() -> Vec { + Vec::new() + } } /// Runs Trusted Server deploy-time validation for pushed app config. From b743345fd7d43eaf36e0fce86ba448e27e9e5b6e Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Mon, 3 Aug 2026 17:51:39 -0700 Subject: [PATCH 02/21] Update EdgeZero to latest deploy-actions branch tip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-resolve the six edgezero-* deps from 145f1699 to bb441162 (current tip of feature/edgezero-deploy-actions, PR #316). The deploy staging flag was renamed there from --stage to --staging, standardizing on the same verb healthcheck/rollback/config-push already use; update the deploy CLI parse test to match. No production dispatch change is needed — ts passes the edgezero-cli arg structs through, so the renamed flag is picked up automatically. --- Cargo.lock | 26 +++++++++++++------------- crates/trusted-server-cli/src/run.rs | 4 ++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 72fa293ce..e5caa1a7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -767,7 +767,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] @@ -1398,7 +1398,7 @@ dependencies = [ [[package]] name = "edgezero-adapter" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#145f1699d8e8f51027804f21faddbf985cff9a1e" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#bb4411625856472b1279a3db49aeeac5e8b1507e" dependencies = [ "toml", ] @@ -1406,7 +1406,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-axum" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#145f1699d8e8f51027804f21faddbf985cff9a1e" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#bb4411625856472b1279a3db49aeeac5e8b1507e" dependencies = [ "anyhow", "async-trait", @@ -1434,7 +1434,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-cloudflare" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#145f1699d8e8f51027804f21faddbf985cff9a1e" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#bb4411625856472b1279a3db49aeeac5e8b1507e" dependencies = [ "anyhow", "async-trait", @@ -1457,7 +1457,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-fastly" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#145f1699d8e8f51027804f21faddbf985cff9a1e" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#bb4411625856472b1279a3db49aeeac5e8b1507e" dependencies = [ "anyhow", "async-stream", @@ -1486,7 +1486,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-spin" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#145f1699d8e8f51027804f21faddbf985cff9a1e" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#bb4411625856472b1279a3db49aeeac5e8b1507e" dependencies = [ "anyhow", "async-trait", @@ -1513,7 +1513,7 @@ dependencies = [ [[package]] name = "edgezero-cli" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#145f1699d8e8f51027804f21faddbf985cff9a1e" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#bb4411625856472b1279a3db49aeeac5e8b1507e" dependencies = [ "chrono", "clap", @@ -1538,7 +1538,7 @@ dependencies = [ [[package]] name = "edgezero-core" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#145f1699d8e8f51027804f21faddbf985cff9a1e" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#bb4411625856472b1279a3db49aeeac5e8b1507e" dependencies = [ "anyhow", "async-compression", @@ -1569,7 +1569,7 @@ dependencies = [ [[package]] name = "edgezero-macros" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#145f1699d8e8f51027804f21faddbf985cff9a1e" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#bb4411625856472b1279a3db49aeeac5e8b1507e" dependencies = [ "log", "proc-macro2", @@ -3604,7 +3604,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" dependencies = [ "heck", - "itertools 0.13.0", + "itertools 0.10.5", "log", "multimap", "once_cell", @@ -3624,7 +3624,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.118", @@ -3637,7 +3637,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.118", @@ -5908,7 +5908,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] diff --git a/crates/trusted-server-cli/src/run.rs b/crates/trusted-server-cli/src/run.rs index b3be949a5..7374c56a7 100644 --- a/crates/trusted-server-cli/src/run.rs +++ b/crates/trusted-server-cli/src/run.rs @@ -290,13 +290,13 @@ mod tests { "fastly", "--service-id", "service-123", - "--stage", + "--staging", ]); let Command::Deploy(deploy) = args.command else { panic!("expected deploy command"); }; assert_eq!(deploy.service_id, Some("service-123".to_owned())); - assert!(deploy.stage); + assert!(deploy.staging); } #[test] From 54203900d8fb7da9338a39c5e47822d55aba6219 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Sat, 15 Aug 2026 13:59:57 -0700 Subject: [PATCH 03/21] Sync EdgeZero to latest deploy-actions tip and adopt config gc Re-resolve the six edgezero-* deps from bb441162 to 908e229a (current tip of feature/edgezero-deploy-actions, PR #316), and adapt the ts CLI to its surface changes: - Wire the new `ts config gc` subcommand (reclaims orphaned config-store chunk entries) to edgezero_cli::run_config_gc, with parse coverage for the preview default, destructive --yes/--older-than sweep, and the --dry-run/--yes conflict. - Lock the hardened deploy staging behavior: --stage was renamed to --staging and deploy passthrough is now last=true, so a stray --stage fails closed at parse time instead of routing a staging-intended deploy to production. Add tests for the rejection and for post---- passthrough capture. --- Cargo.lock | 33 +++++++---- crates/trusted-server-cli/src/run.rs | 88 +++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e5caa1a7a..32e0bd696 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -767,7 +767,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -1398,7 +1398,7 @@ dependencies = [ [[package]] name = "edgezero-adapter" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#bb4411625856472b1279a3db49aeeac5e8b1507e" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#908e229a424e837717450c36c656d73891466d08" dependencies = [ "toml", ] @@ -1406,7 +1406,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-axum" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#bb4411625856472b1279a3db49aeeac5e8b1507e" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#908e229a424e837717450c36c656d73891466d08" dependencies = [ "anyhow", "async-trait", @@ -1434,7 +1434,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-cloudflare" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#bb4411625856472b1279a3db49aeeac5e8b1507e" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#908e229a424e837717450c36c656d73891466d08" dependencies = [ "anyhow", "async-trait", @@ -1457,7 +1457,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-fastly" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#bb4411625856472b1279a3db49aeeac5e8b1507e" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#908e229a424e837717450c36c656d73891466d08" dependencies = [ "anyhow", "async-stream", @@ -1486,7 +1486,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-spin" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#bb4411625856472b1279a3db49aeeac5e8b1507e" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#908e229a424e837717450c36c656d73891466d08" dependencies = [ "anyhow", "async-trait", @@ -1513,7 +1513,7 @@ dependencies = [ [[package]] name = "edgezero-cli" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#bb4411625856472b1279a3db49aeeac5e8b1507e" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#908e229a424e837717450c36c656d73891466d08" dependencies = [ "chrono", "clap", @@ -1538,7 +1538,7 @@ dependencies = [ [[package]] name = "edgezero-core" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#bb4411625856472b1279a3db49aeeac5e8b1507e" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#908e229a424e837717450c36c656d73891466d08" dependencies = [ "anyhow", "async-compression", @@ -1569,14 +1569,14 @@ dependencies = [ [[package]] name = "edgezero-macros" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#bb4411625856472b1279a3db49aeeac5e8b1507e" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#908e229a424e837717450c36c656d73891466d08" dependencies = [ "log", "proc-macro2", "quote", "serde", "serde_json", - "syn 2.0.118", + "syn 3.0.3", "toml", "validator", ] @@ -4769,6 +4769,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "sync_wrapper" version = "1.0.2" @@ -5908,7 +5919,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/crates/trusted-server-cli/src/run.rs b/crates/trusted-server-cli/src/run.rs index 7374c56a7..a395281be 100644 --- a/crates/trusted-server-cli/src/run.rs +++ b/crates/trusted-server-cli/src/run.rs @@ -2,8 +2,8 @@ use std::process; use clap::{Parser, Subcommand}; use edgezero_cli::args::{ - ActiveVersionArgs, AuthArgs, BuildArgs, ConfigDiffArgs, ConfigPushArgs, ConfigValidateArgs, - DeployArgs, HealthcheckArgs, ProvisionArgs, RollbackArgs, ServeArgs, + ActiveVersionArgs, AuthArgs, BuildArgs, ConfigDiffArgs, ConfigGcArgs, ConfigPushArgs, + ConfigValidateArgs, DeployArgs, HealthcheckArgs, ProvisionArgs, RollbackArgs, ServeArgs, }; use trusted_server_core::config::TrustedServerAppConfig; @@ -55,6 +55,8 @@ enum ConfigCommand { Init(ConfigInitArgs), /// Diff `trusted-server.toml` against the live `EdgeZero` config. Diff(ConfigDiffArgs), + /// Reclaim orphaned chunk entries leaked from prior oversized pushes. + Gc(ConfigGcArgs), /// Push `trusted-server.toml` as a blob envelope through `EdgeZero`. Push(ConfigPushArgs), /// Validate `edgezero.toml` and the typed Trusted Server config. @@ -102,6 +104,7 @@ fn dispatch(args: Args) -> Result<(), String> { Err(err) => Err(err), } } + Command::Config(ConfigCommand::Gc(args)) => edgezero_cli::run_config_gc(&args), Command::Config(ConfigCommand::Push(args)) => { edgezero_cli::run_config_push_typed::(&args) } @@ -299,6 +302,39 @@ mod tests { assert!(deploy.staging); } + #[test] + fn deploy_rejects_renamed_stage_flag_before_separator() { + // `--stage` was renamed to `--staging`, and adapter passthrough is + // `last = true` (only captured after `--`). A stray `--stage` before the + // separator must fail closed at parse time rather than being swallowed as + // passthrough, which would leave `staging` false and route a + // staging-intended deploy to production. + Args::try_parse_from(["ts", "deploy", "--adapter", "fastly", "--stage"]) + .expect_err("should reject the renamed-away --stage flag, not route it to production"); + } + + #[test] + fn deploy_captures_adapter_passthrough_after_separator() { + let args = parse(&[ + "ts", + "deploy", + "--adapter", + "fastly", + "--", + "--comment", + "ci", + ]); + let Command::Deploy(deploy) = args.command else { + panic!("expected deploy command"); + }; + assert!(!deploy.staging, "should default to a production deploy"); + assert_eq!( + deploy.adapter_args, + vec!["--comment", "ci"], + "should capture args after -- as adapter passthrough" + ); + } + #[test] fn parses_audit_with_default_outputs() { let args = parse(&["ts", "audit", "https://publisher.example"]); @@ -438,6 +474,54 @@ mod tests { assert!(!diff.no_env); } + #[test] + fn config_gc_previews_by_default() { + let args = parse(&["ts", "config", "gc", "--adapter", "fastly"]); + let Command::Config(ConfigCommand::Gc(gc)) = args.command else { + panic!("expected config gc command"); + }; + assert_eq!(gc.adapter, "fastly"); + assert_eq!( + gc.older_than, None, + "should not require an older-than window to preview" + ); + assert!(!gc.dry_run); + assert!(!gc.no_env); + } + + #[test] + fn config_gc_parses_destructive_sweep() { + let args = parse(&[ + "ts", + "config", + "gc", + "--adapter", + "fastly", + "--yes", + "--older-than", + "7d", + ]); + let Command::Config(ConfigCommand::Gc(gc)) = args.command else { + panic!("expected config gc command"); + }; + assert!(gc.yes); + assert_eq!(gc.older_than, Some("7d".to_owned())); + } + + #[test] + fn config_gc_rejects_dry_run_with_yes() { + Args::try_parse_from([ + "ts", + "config", + "gc", + "--adapter", + "fastly", + "--dry-run", + "--yes", + ]) + .expect_err("should reject conflicting --dry-run and --yes"); + } + #[test] fn config_validate_uses_edgezero_app_config_flag() { let args = parse(&[ From fe5767e61aa8cdbeb17cb1b2b5e59a00a03640d1 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Sun, 16 Aug 2026 00:14:38 -0700 Subject: [PATCH 04/21] Sync EdgeZero to deploy-actions tip 5f3d648c Re-resolve the six edgezero-* deps from 908e229a to 5f3d648c (current tip of feature/edgezero-deploy-actions, PR #316). The upstream change is an internal review-addressing pass (redact config-store errors, fix version parse, log cleanup, docs) confined to the Fastly adapter CLI; no ts CLI surface change, so no run.rs adaptation is needed. --- Cargo.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7ea08f4cc..5e3388bd0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -767,7 +767,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] @@ -1398,7 +1398,7 @@ dependencies = [ [[package]] name = "edgezero-adapter" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#908e229a424e837717450c36c656d73891466d08" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" dependencies = [ "toml", ] @@ -1406,7 +1406,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-axum" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#908e229a424e837717450c36c656d73891466d08" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" dependencies = [ "anyhow", "async-trait", @@ -1434,7 +1434,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-cloudflare" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#908e229a424e837717450c36c656d73891466d08" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" dependencies = [ "anyhow", "async-trait", @@ -1457,7 +1457,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-fastly" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#908e229a424e837717450c36c656d73891466d08" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" dependencies = [ "anyhow", "async-stream", @@ -1486,7 +1486,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-spin" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#908e229a424e837717450c36c656d73891466d08" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" dependencies = [ "anyhow", "async-trait", @@ -1513,7 +1513,7 @@ dependencies = [ [[package]] name = "edgezero-cli" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#908e229a424e837717450c36c656d73891466d08" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" dependencies = [ "chrono", "clap", @@ -1538,7 +1538,7 @@ dependencies = [ [[package]] name = "edgezero-core" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#908e229a424e837717450c36c656d73891466d08" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" dependencies = [ "anyhow", "async-compression", @@ -1569,7 +1569,7 @@ dependencies = [ [[package]] name = "edgezero-macros" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#908e229a424e837717450c36c656d73891466d08" +source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" dependencies = [ "log", "proc-macro2", @@ -5920,7 +5920,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] From f90f638464c8f1b3935d4880dc237f885c5bfcab Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Thu, 20 Aug 2026 21:57:19 -0700 Subject: [PATCH 05/21] Pin edgezero dependencies to immutable rev instead of branch Replace the mutable branch = "feature/edgezero-deploy-actions" reference with rev = "5f3d648c", the exact commit the lockfile already resolved, so cargo update or an unlocked resolve cannot silently retarget the branch tip. Rebuild the lock from main with a scoped update of the six edgezero packages. The re-resolve of the edgezero subtree unifies wide version ranges onto co-present lower majors (prost-build/prost-derive onto itertools 0.10.5, colored/winapi-util onto windows-sys 0.48.0); restore those five dependency edges to main's picks so the only non-edgezero lock changes are syn 3.0.3 and toml_edit 0.25.12, both required by the new edgezero tree. Verified with cargo metadata/check --locked and the host CLI test suite. --- Cargo.lock | 26 +++++++++++++------------- Cargo.toml | 12 ++++++------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5e3388bd0..d1e185558 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -767,7 +767,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -1398,7 +1398,7 @@ dependencies = [ [[package]] name = "edgezero-adapter" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" +source = "git+https://github.com/stackpop/edgezero?rev=5f3d648c3c6c38fc6e6b22b5c65c66177363aad8#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" dependencies = [ "toml", ] @@ -1406,7 +1406,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-axum" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" +source = "git+https://github.com/stackpop/edgezero?rev=5f3d648c3c6c38fc6e6b22b5c65c66177363aad8#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" dependencies = [ "anyhow", "async-trait", @@ -1434,7 +1434,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-cloudflare" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" +source = "git+https://github.com/stackpop/edgezero?rev=5f3d648c3c6c38fc6e6b22b5c65c66177363aad8#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" dependencies = [ "anyhow", "async-trait", @@ -1457,7 +1457,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-fastly" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" +source = "git+https://github.com/stackpop/edgezero?rev=5f3d648c3c6c38fc6e6b22b5c65c66177363aad8#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" dependencies = [ "anyhow", "async-stream", @@ -1486,7 +1486,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-spin" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" +source = "git+https://github.com/stackpop/edgezero?rev=5f3d648c3c6c38fc6e6b22b5c65c66177363aad8#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" dependencies = [ "anyhow", "async-trait", @@ -1513,7 +1513,7 @@ dependencies = [ [[package]] name = "edgezero-cli" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" +source = "git+https://github.com/stackpop/edgezero?rev=5f3d648c3c6c38fc6e6b22b5c65c66177363aad8#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" dependencies = [ "chrono", "clap", @@ -1538,7 +1538,7 @@ dependencies = [ [[package]] name = "edgezero-core" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" +source = "git+https://github.com/stackpop/edgezero?rev=5f3d648c3c6c38fc6e6b22b5c65c66177363aad8#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" dependencies = [ "anyhow", "async-compression", @@ -1569,7 +1569,7 @@ dependencies = [ [[package]] name = "edgezero-macros" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?branch=feature%2Fedgezero-deploy-actions#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" +source = "git+https://github.com/stackpop/edgezero?rev=5f3d648c3c6c38fc6e6b22b5c65c66177363aad8#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" dependencies = [ "log", "proc-macro2", @@ -3604,7 +3604,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" dependencies = [ "heck", - "itertools 0.10.5", + "itertools 0.13.0", "log", "multimap", "once_cell", @@ -3624,7 +3624,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.13.0", "proc-macro2", "quote", "syn 2.0.118", @@ -3637,7 +3637,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.13.0", "proc-macro2", "quote", "syn 2.0.118", @@ -5920,7 +5920,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index a5a63ca3a..f2baf59f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,12 +54,12 @@ criterion = { version = "0.5", default-features = false, features = ["cargo_benc derive_more = { version = "2.0", features = ["display", "error"] } directories = "5" ed25519-dalek = { version = "2.2", features = ["rand_core"] } -edgezero-adapter-axum = { git = "https://github.com/stackpop/edgezero", branch = "feature/edgezero-deploy-actions", default-features = false } -edgezero-adapter-cloudflare = { git = "https://github.com/stackpop/edgezero", branch = "feature/edgezero-deploy-actions", default-features = false } -edgezero-adapter-fastly = { git = "https://github.com/stackpop/edgezero", branch = "feature/edgezero-deploy-actions", default-features = false } -edgezero-adapter-spin = { git = "https://github.com/stackpop/edgezero", branch = "feature/edgezero-deploy-actions", default-features = false } -edgezero-cli = { git = "https://github.com/stackpop/edgezero", branch = "feature/edgezero-deploy-actions" } -edgezero-core = { git = "https://github.com/stackpop/edgezero", branch = "feature/edgezero-deploy-actions", default-features = false } +edgezero-adapter-axum = { git = "https://github.com/stackpop/edgezero", rev = "5f3d648c3c6c38fc6e6b22b5c65c66177363aad8", default-features = false } +edgezero-adapter-cloudflare = { git = "https://github.com/stackpop/edgezero", rev = "5f3d648c3c6c38fc6e6b22b5c65c66177363aad8", default-features = false } +edgezero-adapter-fastly = { git = "https://github.com/stackpop/edgezero", rev = "5f3d648c3c6c38fc6e6b22b5c65c66177363aad8", default-features = false } +edgezero-adapter-spin = { git = "https://github.com/stackpop/edgezero", rev = "5f3d648c3c6c38fc6e6b22b5c65c66177363aad8", default-features = false } +edgezero-cli = { git = "https://github.com/stackpop/edgezero", rev = "5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" } +edgezero-core = { git = "https://github.com/stackpop/edgezero", rev = "5f3d648c3c6c38fc6e6b22b5c65c66177363aad8", default-features = false } env_logger = "0.11" error-stack = "0.6" fastly = "0.12" From bf47680d38e819b5cde6cdc1d695ad411afb6a5f Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Thu, 20 Aug 2026 21:57:19 -0700 Subject: [PATCH 06/21] Document new CLI lifecycle and config gc commands Cover active-version, healthcheck, rollback, deploy --staging with the -- passthrough boundary, and the destructive config gc surface (preview default, --yes with a required --older-than window, and gc's different --no-env meaning). Drop the stale EdgeZero v0.0.4 qualifier from the env-overlay note; the leaves-only behavior is unchanged at the pinned rev. --- docs/guide/cli.md | 63 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/docs/guide/cli.md b/docs/guide/cli.md index b6829895e..fe442ed40 100644 --- a/docs/guide/cli.md +++ b/docs/guide/cli.md @@ -65,8 +65,8 @@ ts config push --adapter fastly `config validate`, `config diff`, and `config push` use EdgeZero's typed app-config loader. By default that loader applies `TRUSTED_SERVER__...` -environment overlays before validation, comparison, and blob creation. EdgeZero -v0.0.4 only overrides leaves already present in the TOML; add newly introduced +environment overlays before validation, comparison, and blob creation. The +overlay only overrides leaves already present in the TOML; add newly introduced fields to existing configs before relying on their overrides. Pass `--no-env` for file-only operation. See [Configuration](/guide/configuration#environment-variable-overrides-typed-cli) for migration and rollback guidance. @@ -76,6 +76,29 @@ Trusted Server settings JSON. This blob model is intentional because full Trusted Server configs can exceed Fastly limits when split into one config-store entry per setting. +Reclaim orphaned chunk entries leaked from prior oversized pushes: + +```bash +ts config gc --adapter fastly +``` + +Without `--yes`, `config gc` only previews: it reports what it would delete and +deletes nothing. `--dry-run` states that intent explicitly and conflicts with +`--yes`. To actually delete, pass `--yes` together with `--older-than ` +(`s`/`m`/`h`/`d` suffixes, e.g. `7d`; a bare number means seconds): + +```bash +ts config gc --adapter fastly --yes --older-than 7d +``` + +`config gc` sweeps every root in the selected physical store, so `--older-than` +is a safety assertion about the whole store: nothing in it changed within the +window and no writer is targeting it. Unlike the other `config` subcommands, +`gc` never loads the typed app config; its `--no-env` flag instead ignores +`EDGEZERO__STORES__CONFIG____NAME` when resolving which physical store to +sweep. On a destructive run, check the store id `gc` reports before passing +`--yes`. + ## Lifecycle commands Lifecycle commands delegate to the selected EdgeZero adapter: @@ -88,6 +111,42 @@ ts deploy --adapter fastly ts serve --adapter fastly ``` +`ts deploy` accepts `--staging` (Fastly only) to build and upload a staged +draft version cloned from the active one instead of activating a production +deploy. Adapter passthrough arguments must follow a `--` separator; unknown +flags before `--` (including the renamed-away `--stage`) are rejected at parse +time rather than forwarded: + +```bash +ts deploy --adapter fastly --service-id --staging +ts deploy --adapter fastly -- --comment "release" +``` + +Inspect and verify deployments with the deploy lifecycle commands: + +```bash +# Print the currently active deployment version +ts active-version --adapter fastly --service-id + +# Probe a deployed version until it reports healthy +ts healthcheck --adapter fastly --service-id \ + --version --domain edge.example + +# Re-activate a previously active version +ts rollback --adapter fastly --service-id \ + --version --rollback-to +``` + +`healthcheck` probes `/` by default (`--path` overrides) and retries 3 times +with a 5 second delay and 10 second timeout (`--retry`, `--retry-delay`, +`--timeout`). With `--staging` it resolves the staged version's IP from the +service id and probes that instead of the production endpoint. + +`rollback` cannot infer the production rollback target: Fastly exposes no +metadata to tell a previously live version from a staged one, so pass the +version to re-activate via `--rollback-to`. With `--staging`, it deactivates +the staged `--version` instead and needs no `--rollback-to`. + ## Audit a public page `ts audit` loads a public page in a fresh headless Chrome/Chromium session, From 40dd9b37a6f2e1aff2fe75fb7006f00a5f56d7a8 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Thu, 20 Aug 2026 21:58:36 -0700 Subject: [PATCH 07/21] Drop stale EdgeZero v0.0.4 qualifiers from env-overlay docs The dependency is no longer pinned at v0.0.4. The leaves-only overlay behavior is unchanged at the pinned rev (apply_env_overlay still only overrides keys already present in the parsed tree), so keep the behavior claims and remove the version qualifier. --- docs/guide/auction-orchestration.md | 4 ++-- docs/guide/configuration.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/guide/auction-orchestration.md b/docs/guide/auction-orchestration.md index b4ba6b797..df79b2ee1 100644 --- a/docs/guide/auction-orchestration.md +++ b/docs/guide/auction-orchestration.md @@ -757,8 +757,8 @@ timeout_ms = 500 # Mediator budget (called after providers) ### Environment Variable Overrides The typed `ts config validate`, `ts config diff`, and `ts config push` flows can -override auction values that already exist in the TOML. EdgeZero v0.0.4 does -not create missing leaves, so existing configs must add **both** +override auction values that already exist in the TOML. EdgeZero's env overlay +does not create missing leaves, so existing configs must add **both** `rewrite_creatives = true` and `sanitize_creatives = false` under `[auction]` before relying on the corresponding environment overrides — an override for a missing leaf is silently ignored. diff --git a/docs/guide/configuration.md b/docs/guide/configuration.md index ddb6544ce..16008e8a6 100644 --- a/docs/guide/configuration.md +++ b/docs/guide/configuration.md @@ -114,7 +114,7 @@ base TOML configuration by `ts config validate`, `ts config diff`, and stored in the app-config blob. Changing an environment variable requires rerunning validation and pushing the resolved config, not rebuilding the binary. -EdgeZero v0.0.4 only overrides leaves that already exist in the parsed TOML; it +EdgeZero's env overlay only overrides leaves that already exist in the parsed TOML; it does not create missing fields. Add newly introduced defaulted fields to an existing config before relying on their environment overrides. Pass `--no-env` to use file values without the overlay. @@ -1292,7 +1292,7 @@ remove that field's non-default value (and any environment override), run `ts config validate`, push the resulting default-compatible blob, and only then roll back the binary. -**Environment overlays:** EdgeZero v0.0.4 overlays cannot create missing TOML +**Environment overlays:** EdgeZero's env overlays cannot create missing TOML leaves. Existing configs must add **both** leaves under `[auction]` (`rewrite_creatives` and `sanitize_creatives`) before `TRUSTED_SERVER__AUCTION__REWRITE_CREATIVES` / @@ -1638,7 +1638,7 @@ trusted-server.dev.toml # Development overrides **Environment Variables Not Applied**: - Run the override through `ts config validate`, `ts config diff`, or `ts config push` -- Verify the target leaf already exists in `trusted-server.toml`; EdgeZero v0.0.4 does not create missing fields +- Verify the target leaf already exists in `trusted-server.toml`; the env overlay does not create missing fields - Verify prefix: `TRUSTED_SERVER__` - Check separator: `__` (double underscore) - Confirm the variable is exported: `echo $VARIABLE_NAME` From 87546d36d7c138e4d0e6e8463cddc5aa39bc840d Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Fri, 21 Aug 2026 22:27:23 -0700 Subject: [PATCH 08/21] Document the staged-deploy config leg and correct healthcheck retry wording A staged deploy only redirects the staged version's config selector at the _staging key; it does not copy the production blob. Add the config push/diff --staging step to the staging sequence so following the guide no longer stages a version whose config key nothing wrote, and note the --key conflict. Reword the healthcheck defaults: --retry 3 is 3 total attempts (attempts = retry.max(1) upstream), not 3 retries after a first try. --- docs/guide/cli.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/docs/guide/cli.md b/docs/guide/cli.md index fe442ed40..cf28803f9 100644 --- a/docs/guide/cli.md +++ b/docs/guide/cli.md @@ -122,6 +122,21 @@ ts deploy --adapter fastly --service-id --staging ts deploy --adapter fastly -- --comment "release" ``` +A staged deploy only redirects the staged version's config selector at the +`_staging` key — it does not copy the production config blob +there. Push the staged config before probing the staged version, or it comes up +with no config at all: + +```bash +ts config push --adapter fastly --staging +ts config diff --adapter fastly --staging +``` + +`--staging` on `config push` / `config diff` writes and compares the +`_staging` key in the same store. It is mutually exclusive +with `--key`: the staging key is derived from the store's logical id, so an +explicit key would be written where nothing reads it. + Inspect and verify deployments with the deploy lifecycle commands: ```bash @@ -137,8 +152,9 @@ ts rollback --adapter fastly --service-id \ --version --rollback-to ``` -`healthcheck` probes `/` by default (`--path` overrides) and retries 3 times -with a 5 second delay and 10 second timeout (`--retry`, `--retry-delay`, +`healthcheck` probes `/` by default (`--path` overrides) and makes 3 total +attempts — not 3 retries after a first try — with a 5 second delay between +attempts and a 10 second per-attempt timeout (`--retry`, `--retry-delay`, `--timeout`). With `--staging` it resolves the staged version's IP from the service id and probes that instead of the production endpoint. From 01d7f611bebe1e185ccfecfe2ae07b5b9196f7fb Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Fri, 21 Aug 2026 22:27:23 -0700 Subject: [PATCH 09/21] Extend CLI parse tests for staging config flags, gc gate, and --version Add parse coverage for config push/diff --staging including the --key conflict, assert healthcheck's path default and reword the retry assertion to total attempts, assert config gc's yes default since --yes is the only destructive gate, and pin the top-level --version flag via ErrorKind::DisplayVersion. --- crates/trusted-server-cli/src/run.rs | 66 +++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/crates/trusted-server-cli/src/run.rs b/crates/trusted-server-cli/src/run.rs index a395281be..ae10ae014 100644 --- a/crates/trusted-server-cli/src/run.rs +++ b/crates/trusted-server-cli/src/run.rs @@ -143,6 +143,17 @@ mod tests { Args::try_parse_from(args).expect("should parse args") } + #[test] + fn top_level_version_flag_is_available() { + let err = Args::try_parse_from(["ts", "--version"]) + .expect_err("should short-circuit parsing on --version"); + assert_eq!( + err.kind(), + clap::error::ErrorKind::DisplayVersion, + "should print the version rather than fail to parse" + ); + } + #[test] fn parses_active_version() { let args = parse(&[ @@ -179,7 +190,11 @@ mod tests { }; assert_eq!(healthcheck.domain, "edge.example"); assert_eq!(healthcheck.version, "7"); - assert_eq!(healthcheck.retry, 3, "should default to 3 retries"); + assert_eq!(healthcheck.path, "/", "should default to probing `/`"); + assert_eq!( + healthcheck.retry, 3, + "should default to 3 total attempts, not 3 retries after a first try" + ); assert_eq!( healthcheck.retry_delay, 5, "should default to a 5s retry delay" @@ -474,6 +489,51 @@ mod tests { assert!(!diff.no_env); } + #[test] + fn config_push_parses_staging_and_rejects_explicit_key() { + let args = parse(&["ts", "config", "push", "--adapter", "fastly", "--staging"]); + let Command::Config(ConfigCommand::Push(push)) = args.command else { + panic!("expected config push command"); + }; + assert!(push.staging, "should target the derived staging key"); + + Args::try_parse_from([ + "ts", + "config", + "push", + "--adapter", + "fastly", + "--staging", + "--key", + "custom", + ]) + .expect_err("should reject --key with --staging; the staging key is derived"); + } + + #[test] + fn config_diff_parses_staging_and_rejects_explicit_key() { + let args = parse(&["ts", "config", "diff", "--adapter", "fastly", "--staging"]); + let Command::Config(ConfigCommand::Diff(diff)) = args.command else { + panic!("expected config diff command"); + }; + assert!( + diff.staging, + "should compare against the derived staging key" + ); + + Args::try_parse_from([ + "ts", + "config", + "diff", + "--adapter", + "fastly", + "--staging", + "--key", + "custom", + ]) + .expect_err("should reject --key with --staging; the staging key is derived"); + } + #[test] fn config_gc_previews_by_default() { let args = parse(&["ts", "config", "gc", "--adapter", "fastly"]); @@ -487,6 +547,10 @@ mod tests { ); assert!(!gc.dry_run); assert!(!gc.no_env); + assert!( + !gc.yes, + "should not delete without an explicit --yes; --yes is the only destructive gate" + ); } #[test] From 4aee0527221cbabd3ef99c65b6c65e035ad0e5bc Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Fri, 21 Aug 2026 22:27:23 -0700 Subject: [PATCH 10/21] Refresh secret-fields comment and drop last v0.0.4 qualifiers The pinned edgezero rev ships nested/array secret extraction, so the secret_fields comment no longer names it as the blocker; what remains is spelling out the paths in this hand-written impl (it does not inherit the derive's) plus operator migration. Drop the stale v0.0.4 qualifiers the earlier docs pass missed in the example config and the env-overlay test. --- crates/trusted-server-cli/tests/config_env_overlay.rs | 2 +- crates/trusted-server-core/src/config.rs | 5 +++-- trusted-server.example.toml | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/trusted-server-cli/tests/config_env_overlay.rs b/crates/trusted-server-cli/tests/config_env_overlay.rs index 35263c0eb..851110ca7 100644 --- a/crates/trusted-server-cli/tests/config_env_overlay.rs +++ b/crates/trusted-server-cli/tests/config_env_overlay.rs @@ -44,7 +44,7 @@ fn migrated_legacy_project() -> MigratedProject { let mut document = LEGACY_CONFIG .parse::() .expect("should parse legacy integration config"); - // EdgeZero v0.0.4 environment overlays cannot create missing TOML leaves, + // EdgeZero environment overlays cannot create missing TOML leaves, // so a migrated config must carry both creative-processing leaves for the // corresponding environment variables to take effect. document["auction"]["rewrite_creatives"] = value(true); diff --git a/crates/trusted-server-core/src/config.rs b/crates/trusted-server-core/src/config.rs index b983c0ab6..331f7ab33 100644 --- a/crates/trusted-server-core/src/config.rs +++ b/crates/trusted-server-core/src/config.rs @@ -110,8 +110,9 @@ impl edgezero_core::app_config::AppConfigMeta for TrustedServerAppConfig { // Phase 1 intentionally preserves the existing inline-settings model: // `ts config push` publishes the validated Trusted Server config as one // app-config blob. Migrating app-level secrets to `EdgeZero` secret-store - // references needs nested/array extraction support and operator migration - // work tracked separately. + // references needs the secret-field paths spelled out here (this + // hand-written impl does not inherit the derive's nested/array paths) + // plus operator migration work tracked separately. fn secret_fields() -> Vec { Vec::new() } diff --git a/trusted-server.example.toml b/trusted-server.example.toml index 717b4d54b..4c2fa302e 100644 --- a/trusted-server.example.toml +++ b/trusted-server.example.toml @@ -147,7 +147,7 @@ enabled = false [auction] enabled = false -# Defaults to true. Keep this leaf present when using the EdgeZero v0.0.4 +# Defaults to true. Keep this leaf present when using the EdgeZero # environment override. Set false to return unre-written winning-bid adm, # skipping proxy/click URL conversion and creative TSJS injection. # Sanitization is controlled separately by `sanitize_creatives` below. From 6462eb84d77a2f72383ca535c9acac75a4d6ee85 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Wed, 26 Aug 2026 21:48:14 -0700 Subject: [PATCH 11/21] Upgrade edgezero dependencies to the v0.0.6 release stackpop/edgezero#316 was squash-merged and released as v0.0.6, so the six deps move from the interim rev = "5f3d648c" pin to tag = "v0.0.6" (c8d6e414). The squash makes this different bytes rather than a mechanical reference swap: the CLI argument surface is byte-identical to the prior pin, the documented behaviors (healthcheck attempts clamp, staging selector-only mirror, leaves-only env overlay) were re-verified at the tag, and the full local gate list was re-run. The lock is rebuilt from main with a scoped update and the five resolver-unified edges restored; the only non-edgezero changes are syn 3.0.4 and toml_edit 0.25.12. --- Cargo.lock | 22 +++++++++++----------- Cargo.toml | 12 ++++++------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c6f9ec4f9..7f7587c8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1427,7 +1427,7 @@ dependencies = [ [[package]] name = "edgezero-adapter" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?rev=5f3d648c3c6c38fc6e6b22b5c65c66177363aad8#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" +source = "git+https://github.com/stackpop/edgezero?tag=v0.0.6#c8d6e414e2095875c429b36dc8099ef26bf2842b" dependencies = [ "toml", ] @@ -1435,7 +1435,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-axum" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?rev=5f3d648c3c6c38fc6e6b22b5c65c66177363aad8#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" +source = "git+https://github.com/stackpop/edgezero?tag=v0.0.6#c8d6e414e2095875c429b36dc8099ef26bf2842b" dependencies = [ "anyhow", "async-trait", @@ -1463,7 +1463,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-cloudflare" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?rev=5f3d648c3c6c38fc6e6b22b5c65c66177363aad8#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" +source = "git+https://github.com/stackpop/edgezero?tag=v0.0.6#c8d6e414e2095875c429b36dc8099ef26bf2842b" dependencies = [ "anyhow", "async-trait", @@ -1486,7 +1486,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-fastly" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?rev=5f3d648c3c6c38fc6e6b22b5c65c66177363aad8#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" +source = "git+https://github.com/stackpop/edgezero?tag=v0.0.6#c8d6e414e2095875c429b36dc8099ef26bf2842b" dependencies = [ "anyhow", "async-stream", @@ -1515,7 +1515,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-spin" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?rev=5f3d648c3c6c38fc6e6b22b5c65c66177363aad8#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" +source = "git+https://github.com/stackpop/edgezero?tag=v0.0.6#c8d6e414e2095875c429b36dc8099ef26bf2842b" dependencies = [ "anyhow", "async-trait", @@ -1542,7 +1542,7 @@ dependencies = [ [[package]] name = "edgezero-cli" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?rev=5f3d648c3c6c38fc6e6b22b5c65c66177363aad8#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" +source = "git+https://github.com/stackpop/edgezero?tag=v0.0.6#c8d6e414e2095875c429b36dc8099ef26bf2842b" dependencies = [ "chrono", "clap", @@ -1567,7 +1567,7 @@ dependencies = [ [[package]] name = "edgezero-core" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?rev=5f3d648c3c6c38fc6e6b22b5c65c66177363aad8#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" +source = "git+https://github.com/stackpop/edgezero?tag=v0.0.6#c8d6e414e2095875c429b36dc8099ef26bf2842b" dependencies = [ "anyhow", "async-compression", @@ -1598,14 +1598,14 @@ dependencies = [ [[package]] name = "edgezero-macros" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?rev=5f3d648c3c6c38fc6e6b22b5c65c66177363aad8#5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" +source = "git+https://github.com/stackpop/edgezero?tag=v0.0.6#c8d6e414e2095875c429b36dc8099ef26bf2842b" dependencies = [ "log", "proc-macro2", "quote", "serde", "serde_json", - "syn 3.0.3", + "syn 3.0.4", "toml", "validator", ] @@ -4860,9 +4860,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 59db5cc1e..30212ba25 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,12 +54,12 @@ criterion = { version = "0.5", default-features = false, features = ["cargo_benc derive_more = { version = "2.0", features = ["display", "error"] } directories = "5" ed25519-dalek = { version = "2.2", features = ["rand_core"] } -edgezero-adapter-axum = { git = "https://github.com/stackpop/edgezero", rev = "5f3d648c3c6c38fc6e6b22b5c65c66177363aad8", default-features = false } -edgezero-adapter-cloudflare = { git = "https://github.com/stackpop/edgezero", rev = "5f3d648c3c6c38fc6e6b22b5c65c66177363aad8", default-features = false } -edgezero-adapter-fastly = { git = "https://github.com/stackpop/edgezero", rev = "5f3d648c3c6c38fc6e6b22b5c65c66177363aad8", default-features = false } -edgezero-adapter-spin = { git = "https://github.com/stackpop/edgezero", rev = "5f3d648c3c6c38fc6e6b22b5c65c66177363aad8", default-features = false } -edgezero-cli = { git = "https://github.com/stackpop/edgezero", rev = "5f3d648c3c6c38fc6e6b22b5c65c66177363aad8" } -edgezero-core = { git = "https://github.com/stackpop/edgezero", rev = "5f3d648c3c6c38fc6e6b22b5c65c66177363aad8", default-features = false } +edgezero-adapter-axum = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.6", default-features = false } +edgezero-adapter-cloudflare = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.6", default-features = false } +edgezero-adapter-fastly = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.6", default-features = false } +edgezero-adapter-spin = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.6", default-features = false } +edgezero-cli = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.6" } +edgezero-core = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.6", default-features = false } env_logger = "0.11" error-stack = "0.6" esi = "0.7.2" From 89c171e9a81a9bba335032a66bdd9727ee00faed Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Wed, 26 Aug 2026 22:02:05 -0700 Subject: [PATCH 12/21] Correct staging and rollback lifecycle docs, cover config gc --store Apply review round 4: capture the production rollback target with active-version before deploying (nothing emits it afterwards), label the lifecycle trio Fastly-only, state where healthcheck's comes from, name --store alongside --no-env as a way a destructive gc sweep can retarget, and present the deploy -- separator as the migration it is. Document the staging config limitation honestly: a staged version currently loads the production blob because the Fastly entry point does not read the version-linked edgezero_runtime_env selectors. The non-hack fix needs edgezero to expose its runtime-env loader as public API, filed as stackpop/edgezero#349 with the downstream wiring tracked in #1082; the stale previous-version rollback hint is stackpop/edgezero#350. Add parse coverage for config gc --store. --- crates/trusted-server-cli/src/run.rs | 25 ++++++++++++++++++++ docs/guide/cli.md | 34 ++++++++++++++++++++-------- 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/crates/trusted-server-cli/src/run.rs b/crates/trusted-server-cli/src/run.rs index ae10ae014..069a1d26b 100644 --- a/crates/trusted-server-cli/src/run.rs +++ b/crates/trusted-server-cli/src/run.rs @@ -547,12 +547,37 @@ mod tests { ); assert!(!gc.dry_run); assert!(!gc.no_env); + assert_eq!( + gc.store, None, + "should default to the manifest's config-store id" + ); assert!( !gc.yes, "should not delete without an explicit --yes; --yes is the only destructive gate" ); } + #[test] + fn config_gc_parses_store_override() { + let args = parse(&[ + "ts", + "config", + "gc", + "--adapter", + "fastly", + "--store", + "other_config_store", + ]); + let Command::Config(ConfigCommand::Gc(gc)) = args.command else { + panic!("expected config gc command"); + }; + assert_eq!( + gc.store, + Some("other_config_store".to_owned()), + "should retarget which store a sweep inspects" + ); + } + #[test] fn config_gc_parses_destructive_sweep() { let args = parse(&[ diff --git a/docs/guide/cli.md b/docs/guide/cli.md index cf28803f9..c79776463 100644 --- a/docs/guide/cli.md +++ b/docs/guide/cli.md @@ -96,8 +96,9 @@ is a safety assertion about the whole store: nothing in it changed within the window and no writer is targeting it. Unlike the other `config` subcommands, `gc` never loads the typed app config; its `--no-env` flag instead ignores `EDGEZERO__STORES__CONFIG____NAME` when resolving which physical store to -sweep. On a destructive run, check the store id `gc` reports before passing -`--yes`. +sweep, and `--store ` overrides the manifest's config-store id outright. +Both change which store gets swept, so on a destructive run check the store id +`gc` reports before passing `--yes`. ## Lifecycle commands @@ -113,9 +114,11 @@ ts serve --adapter fastly `ts deploy` accepts `--staging` (Fastly only) to build and upload a staged draft version cloned from the active one instead of activating a production -deploy. Adapter passthrough arguments must follow a `--` separator; unknown +deploy. Adapter passthrough arguments must now follow a `--` separator; unknown flags before `--` (including the renamed-away `--stage`) are rejected at parse -time rather than forwarded: +time rather than forwarded. This is a change: passthrough args previously +worked without the separator, so existing runbooks and CI jobs that pass +adapter flags directly need the `--` added: ```bash ts deploy --adapter fastly --service-id --staging @@ -124,30 +127,41 @@ ts deploy --adapter fastly -- --comment "release" A staged deploy only redirects the staged version's config selector at the `_staging` key — it does not copy the production config blob -there. Push the staged config before probing the staged version, or it comes up -with no config at all: +there. Push the staged config before probing the staged version: ```bash ts config push --adapter fastly --staging ts config diff --adapter fastly --staging ``` +> **Known limitation:** Trusted Server's Fastly entry point does not yet read +> the version-linked `edgezero_runtime_env` selectors, so a staged version +> currently loads the **production** config blob rather than the staged one — +> a staging healthcheck exercises the new binary against production config. +> Selector resolution for custom entry points is tracked upstream in EdgeZero; +> until it lands, do not rely on `--staging` to validate a config change. + `--staging` on `config push` / `config diff` writes and compares the `_staging` key in the same store. It is mutually exclusive with `--key`: the staging key is derived from the store's logical id, so an explicit key would be written where nothing reads it. -Inspect and verify deployments with the deploy lifecycle commands: +Inspect and verify deployments with the deploy lifecycle commands. All three are +Fastly-only — the axum, cloudflare, and spin adapters reject them: ```bash -# Print the currently active deployment version +# Capture the production rollback target BEFORE deploying: after a deploy this +# prints the NEW version, and Fastly keeps no record of which version was live +# before it, so the target is then unrecoverable. ts active-version --adapter fastly --service-id -# Probe a deployed version until it reports healthy +# Probe a deployed version until it reports healthy. `` is the version +# the deploy activated; pass `--service-id` to `ts deploy` and it emits that as +# a machine-readable `version=` line. ts healthcheck --adapter fastly --service-id \ --version --domain edge.example -# Re-activate a previously active version +# Re-activate the version captured before the deploy ts rollback --adapter fastly --service-id \ --version --rollback-to ``` From c71c582d7bfe46756af490e9df7c13188c96648d Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Tue, 1 Sep 2026 10:07:19 -0700 Subject: [PATCH 13/21] Document EdgeZero v0.0.7 upgrade design --- ...26-09-01-edgezero-v0.0.7-upgrade-design.md | 244 ++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 docs/superpowers/specs/2026-09-01-edgezero-v0.0.7-upgrade-design.md diff --git a/docs/superpowers/specs/2026-09-01-edgezero-v0.0.7-upgrade-design.md b/docs/superpowers/specs/2026-09-01-edgezero-v0.0.7-upgrade-design.md new file mode 100644 index 000000000..3b7acbc92 --- /dev/null +++ b/docs/superpowers/specs/2026-09-01-edgezero-v0.0.7-upgrade-design.md @@ -0,0 +1,244 @@ +# PR #940 EdgeZero v0.0.7 Upgrade + +**PR:** #940 +**Issues:** #939, #1082 +**Date:** 2026-09-01 +**Status:** Approved design + +## Problem + +PR #940 already upgrades Trusted Server from EdgeZero v0.0.4 to v0.0.6 and +adopts the functional changes introduced through v0.0.6: the Fastly deployment +lifecycle commands, typed config staging, `config gc`, and the +`AppConfigMeta::secret_fields()` API. + +EdgeZero v0.0.7 exposes `runtime_env_config` for applications with custom Fastly +entry points. Trusted Server needs that API because its streaming entry point +bypasses `edgezero_adapter_fastly::run_app`. Today the entry point opens the +physical `trusted_server_config` store directly and its selector helpers call +`EnvConfig::from_env()`. Compute does not provide the version-linked selector +values through process environment variables, so a staged version silently +reads the production `trusted_server_config` key instead of +`trusted_server_config_staging`. + +That mismatch means `ts config push --staging` writes a value the staged binary +does not read, and `ts healthcheck --staging` can false-pass a binary/config +pair that was never exercised together. + +## Goals + +- Upgrade all six EdgeZero workspace dependencies to the v0.0.7 tag while + keeping lockfile churn limited to the dependency graph required by that tag. +- Use EdgeZero's public Fastly runtime selector loader exactly once per request + and use the resulting `EnvConfig` consistently for every settings load. +- Preserve existing native-adapter behavior, where selectors continue to come + from `EnvConfig::from_env()`. +- Declare the logical `trusted_server_config` metadata required for EdgeZero to + derive the runtime store-name and key selector names. +- Prove production, staging, and physical-store-name selector behavior with + focused tests. +- Close the remaining v0.0.4-to-v0.0.7 CLI proof gaps without reimplementing + EdgeZero command handlers. +- Remove the obsolete staging warning and align rollback guidance with the + corrected v0.0.7 behavior. +- Update PR #940 to describe the complete v0.0.7 audit and close #939 and #1082 + when the PR merges. + +## Non-goals + +- Do not expose `ts new`. +- Do not expose EdgeZero's contributor-only `demo` command. +- Do not add or adopt EdgeZero deployment-action workflows. +- Do not create a new pull request; all branch and GitHub metadata changes apply + to the existing PR #940. +- Do not rewrite Trusted Server around EdgeZero's `app!` macro or `State` + extractor. Trusted Server's explicit application state and custom streaming + Fastly dispatch remain in place. +- Do not migrate Trusted Server settings secrets into EdgeZero secret-field + metadata. The hand-written `AppConfigMeta` implementation continues to return + no secret fields. +- Do not duplicate EdgeZero's runtime-store name, selector key spellings, or + selector derivation rules in Trusted Server. +- Do not change unrelated dependencies or application behavior. + +## Existing PR Coverage + +The v0.0.4-to-v0.0.6 work already in PR #940 is retained: + +- all operational EdgeZero commands are delegated by `ts`: `active-version`, + `auth`, `build`, `config diff`, `config gc`, `config push`, + `config validate`, `deploy`, `healthcheck`, `provision`, `rollback`, and + `serve`; +- staging flags and fail-closed deploy passthrough parsing are covered; +- `config gc` is dry-run by default and requires the EdgeZero safety gates for + deletion; +- `TrustedServerAppConfig` implements the new `secret_fields()` method; +- the custom Fastly path already captures raw request metadata and preserves + duplicate response headers; and +- the lifecycle and config-GC operator documentation is already present. + +The upgrade must extend this work, not replace or duplicate it. + +## Design + +### 1. Dependency and lockfile upgrade + +Change the workspace pins for `edgezero-adapter-axum`, +`edgezero-adapter-cloudflare`, `edgezero-adapter-fastly`, +`edgezero-adapter-spin`, `edgezero-cli`, and `edgezero-core` from `v0.0.6` to +`v0.0.7` in one edit. + +Refresh only the EdgeZero git dependency graph in `Cargo.lock`. Review the +lockfile diff and reject unrelated package upgrades. Confirm every EdgeZero +package resolves to the v0.0.7 tag commit. + +### 2. Explicit selector helpers + +Add selector helpers in `trusted-server-core::settings_data` that accept an +`&EnvConfig` and resolve: + +- the physical store name for logical config id `trusted_server_config`; and +- the blob key for that logical config id. + +Keep the current zero-argument default helpers as native-compatible wrappers. +They construct `EnvConfig::from_env()` and delegate to the explicit helpers, so +Axum and existing native callers retain their current environment-overlay +behavior. + +This keeps selector policy in one place while allowing Fastly to pass the +version-linked configuration that process environment variables cannot supply +on Compute. + +### 3. Trusted Server store metadata + +Override `TrustedServerApp::stores()` in the Fastly `Hooks` implementation. Its +config metadata declares: + +- default logical id: `trusted_server_config`; and +- ids: `["trusted_server_config"]`. + +Other metadata fields remain at their existing defaults because this change is +specifically the app-config selector path used by the custom entry point. The +logical id remains consistent with `edgezero.toml`. + +### 4. Single Fastly runtime configuration + +After the health-response fast path and logger initialization, call +`edgezero_adapter_fastly::runtime_env_config(TrustedServerApp::stores())` once. +Pass the returned `EnvConfig` by reference into the custom `edgezero_main` flow. + +The same reference must control all selector-sensitive operations: + +1. the JA4 debug endpoint's settings load; +2. resolution and opening of the physical config store; +3. initial application/state construction; and +4. both fallback settings reloads used during response finalization. + +The settings loader in the Fastly adapter accepts `&EnvConfig`, resolves the +store name and key with the explicit core helpers, and then uses the existing +platform config-store abstraction and blob/chunk verification path. Application +construction threads the reference down to that loader. `Hooks::routes()` uses +the native/default wrapper when no Fastly runtime configuration is supplied, +preserving its existing trait contract. + +Resolving the configuration once prevents different loads within one request +from observing different selector sources. EdgeZero remains the owner of the +optional `edgezero_runtime_env` store name, key derivation, missing-store +warning, and empty-config fallback. + +### 5. Focused behavior and CLI tests + +Add selector tests proving: + +- an empty `EnvConfig` selects physical store `trusted_server_config` and blob + key `trusted_server_config`; +- a staging key override selects `trusted_server_config_staging`; +- a physical store-name override selects the overridden store while preserving + the independently selected key; and +- `TrustedServerApp::stores()` exposes exactly the expected config metadata. + +Complete the v0.0.4-to-v0.0.7 CLI argument audit with focused parser tests for: + +- an explicit `healthcheck --path`; and +- the `config gc` targeting flags, including `--store` and `--no-env`. + +These tests prove Trusted Server carries the relevant EdgeZero arguments +without duplicating upstream execution tests. The existing tests continue to +cover default healthcheck behavior, staging, retry controls, config-GC preview +and destructive gates, deployment passthrough, and the other lifecycle +commands. + +### 6. Documentation and PR lifecycle + +Remove the known-limitation callout that says staged versions read production +config. Replace it with the correct ordering: push the staging config, stage the +binary, and healthcheck the staged version, which now reads the staging +selector. + +Keep rollback guidance explicit that operators must capture the active version +before deploying by running `ts active-version`, or use an orchestration layer +that captures the equivalent previous-version value. Do not add an action +workflow to this repository. + +After verification: + +- commit and push the implementation branch; +- rename PR #940 from v0.0.6 to v0.0.7; +- update the PR description with the complete v0.0.4-to-v0.0.7 functional audit, + the selector fix, CLI coverage, exclusions, and fresh verification evidence; +- retain `Closes #939`; and +- add `Closes #1082`, allowing both issues to close when the PR merges rather + than closing them manually before merge. + +## Error Handling + +No new public error type is introduced. Failure to open the selected physical +Fastly config store continues through the existing `fastly::Error` path. +Missing or invalid settings continue through `Report` and +the existing startup/fallback response policies. + +When `edgezero_runtime_env` is missing, EdgeZero logs its warning and returns an +empty `EnvConfig`; Trusted Server then uses the same production defaults it uses +today. A present but invalid physical store override fails through the normal +store-open error instead of silently reverting to another store. + +## Verification Strategy + +Implementation follows red-green-refactor for the selector and CLI proof gaps. +Run focused tests after each code change, then run every local CI gate required +by `CLAUDE.md` before claiming completion: + +- `cargo fmt --all -- --check`; +- all six target-matched clippy aliases for Fastly, Axum, Cloudflare native and + Wasm, and Spin native and Wasm; +- the Fastly, Axum, Cloudflare, and Spin test aliases; +- the native Trusted Server CLI test script; +- formatting, tests, and clippy for the cross-adapter parity crate; +- Cargo metadata with `--locked` to validate the narrow lockfile refresh; +- the Trusted Server JavaScript build, Vitest, ESLint, and Prettier checks; and +- documentation ESLint, Prettier, and VitePress build/dead-link checks. + +Also run the build/check coverage exercised by repository CI: Axum native; +Fastly release Wasm; Cloudflare native and Wasm; Spin native, Wasm, and release +Wasm; the core HTML-processor benchmark smoke; and the OpenRTB codegen tests and +clippy. Run the Fastly ESI and inline local harnesses when their checked-in CI +prerequisites are available. After pushing, wait for and report every required +PR #940 check, including any platform-specific harness that cannot be reproduced +locally. + +The final handoff and PR description report the commands and observed results, +not only expected outcomes. + +## Expected Files + +- `Cargo.toml` +- `Cargo.lock` +- `crates/trusted-server-core/src/settings_data.rs` +- `crates/trusted-server-adapter-fastly/src/app.rs` +- `crates/trusted-server-adapter-fastly/src/main.rs` +- `crates/trusted-server-cli/src/run.rs` +- `docs/guide/cli.md` +- `docs/superpowers/specs/2026-09-01-edgezero-v0.0.7-upgrade-design.md` + +GitHub PR title/body updates are external metadata changes performed only after +the branch is verified and pushed. From b5331abef31f730d904cdd3e6ecf8818897dd359 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Tue, 1 Sep 2026 10:24:48 -0700 Subject: [PATCH 14/21] Plan EdgeZero v0.0.7 upgrade --- .../2026-09-01-edgezero-v0.0.7-upgrade.md | 785 ++++++++++++++++++ 1 file changed, 785 insertions(+) create mode 100644 docs/superpowers/plans/2026-09-01-edgezero-v0.0.7-upgrade.md diff --git a/docs/superpowers/plans/2026-09-01-edgezero-v0.0.7-upgrade.md b/docs/superpowers/plans/2026-09-01-edgezero-v0.0.7-upgrade.md new file mode 100644 index 000000000..d497f36aa --- /dev/null +++ b/docs/superpowers/plans/2026-09-01-edgezero-v0.0.7-upgrade.md @@ -0,0 +1,785 @@ +# EdgeZero v0.0.7 Upgrade Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Complete the existing PR #940 upgrade from EdgeZero v0.0.4 to v0.0.7, including correct Fastly staging selectors and exhaustive Trusted Server CLI proof, without adding `ts new`, deployment-action workflows, or a new PR. + +**Architecture:** Keep Trusted Server's custom Fastly streaming entry point and explicit application state. Add `EnvConfig`-accepting config selectors in core, preserve zero-argument native wrappers, declare the Fastly app's logical config-store metadata, and resolve EdgeZero's `runtime_env_config` once per Fastly request so every settings load uses one physical store/key selection. + +**Tech Stack:** Rust 1.95, Cargo git dependencies, EdgeZero v0.0.7, Fastly Compute/Viceroy, Clap, error-stack, GitHub CLI, Vitest, ESLint, Prettier, VitePress + +**Spec:** `docs/superpowers/specs/2026-09-01-edgezero-v0.0.7-upgrade-design.md` + +--- + +## File Map + +| File | Responsibility | +| -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | +| `Cargo.toml` | Pin all six direct EdgeZero dependencies to one release tag. | +| `Cargo.lock` | Record the v0.0.7 EdgeZero source revision and only required transitive changes. | +| `crates/trusted-server-core/src/settings_data.rs` | Resolve the logical app-config id into a physical store name and blob key from an explicit `EnvConfig`; retain native wrappers. | +| `crates/trusted-server-adapter-fastly/src/app.rs` | Declare `Hooks::stores()` metadata and thread one `EnvConfig` through app/state construction and settings loading. | +| `crates/trusted-server-adapter-fastly/src/main.rs` | Load Fastly runtime selectors once and use them for JA4, store opening, initial app construction, and fallback reloads. | +| `crates/trusted-server-cli/src/run.rs` | Add the two remaining parser proofs: `healthcheck --path` and GC targeting flags. | +| `docs/guide/cli.md` | Remove the obsolete staging limitation and state the corrected staging/rollback lifecycle. | +| Existing PR #940 metadata | Rename to v0.0.7, document the complete audit, and add `Closes #1082` while retaining `Closes #939`. | + +No new source file, command implementation, workflow, or pull request is needed. + +### Task 1: Upgrade the EdgeZero dependency graph narrowly + +**Files:** + +- Modify: `Cargo.toml:57-62` +- Modify: `Cargo.lock:1428-1605` and dependency entries required by EdgeZero v0.0.7 + +- [ ] **Step 1: Record the pre-update dependency sources** + +Run: + +```bash +rg -n '^name = "edgezero-|^source = "git\+https://github.com/stackpop/edgezero' Cargo.lock +git diff --stat origin/main...HEAD +``` + +Expected: every EdgeZero lock entry points to `tag=v0.0.6` at `c8d6e414`; the existing PR diff is understood before lockfile regeneration. + +- [ ] **Step 2: Change all six direct pins in one edit** + +In `Cargo.toml`, change only these tags: + +```toml +edgezero-adapter-axum = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.7", default-features = false } +edgezero-adapter-cloudflare = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.7", default-features = false } +edgezero-adapter-fastly = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.7", default-features = false } +edgezero-adapter-spin = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.7", default-features = false } +edgezero-cli = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.7" } +edgezero-core = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.7", default-features = false } +``` + +- [ ] **Step 3: Refresh only the shared EdgeZero git source** + +Run: + +```bash +cargo update -p edgezero-core +``` + +Expected: Cargo resolves the shared `stackpop/edgezero?tag=v0.0.7` source and updates all packages from that source. If Cargo reports the package specification is ambiguous, rerun with the exact current `edgezero-core` version printed by `cargo tree -p edgezero-core`. + +- [ ] **Step 4: Audit the lockfile** + +Run: + +```bash +git diff -- Cargo.toml Cargo.lock +rg -n '^name = "edgezero-|^source = "git\+https://github.com/stackpop/edgezero' Cargo.lock +cargo metadata --locked --no-deps +``` + +Expected: + +- all EdgeZero packages, including transitive `edgezero-adapter` and `edgezero-macros`, point to `tag=v0.0.7#5c9886e51d17...`; +- registry dependency changes are only those required by the v0.0.7 graph; +- no unrelated workspace dependency changes appear; and +- locked metadata succeeds. + +- [ ] **Step 5: Compile the affected APIs before writing integration code** + +Run: + +```bash +cargo check-fastly +./scripts/test-cli.sh +``` + +Expected: Fastly compilation and the existing CLI suite pass against v0.0.7. Any compile error is an audited upstream API change and must be resolved minimally before continuing. + +- [ ] **Step 6: Commit the dependency upgrade** + +```bash +git add Cargo.toml Cargo.lock +git commit -m "Upgrade EdgeZero dependencies to v0.0.7" +``` + +### Task 2: Add explicit config selector helpers + +Use `@superpowers:test-driven-development` for this task. + +**Files:** + +- Modify: `crates/trusted-server-core/src/settings_data.rs:11-41` +- Test: `crates/trusted-server-core/src/settings_data.rs` unit-test module + +- [ ] **Step 1: Add failing production and override selector tests** + +Add these tests to the existing `settings_data` test module: + +```rust +#[test] +fn config_selectors_default_to_the_logical_store_id() { + let env = EnvConfig::default(); + + assert_eq!( + config_store_name(&env), + StoreName::from("trusted_server_config") + ); + assert_eq!(config_key(&env), "trusted_server_config"); +} + +#[test] +fn config_selectors_apply_staging_key_and_physical_name_overrides() { + let env = EnvConfig::from_vars([ + ( + "EDGEZERO__STORES__CONFIG__TRUSTED_SERVER_CONFIG__KEY", + "trusted_server_config_staging", + ), + ( + "EDGEZERO__STORES__CONFIG__TRUSTED_SERVER_CONFIG__NAME", + "publisher-config-store", + ), + ]); + + assert_eq!( + config_store_name(&env), + StoreName::from("publisher-config-store") + ); + assert_eq!(config_key(&env), "trusted_server_config_staging"); +} +``` + +The combined override test proves the store name and blob key are independently selected. + +- [ ] **Step 2: Run the focused test and verify the red state** + +Run: + +```bash +cargo test-fastly config_selectors_ +``` + +Expected: compilation fails because `config_store_name` and `config_key` do not exist. + +- [ ] **Step 3: Implement the explicit helpers and native wrappers** + +Replace the duplicated direct lookups with: + +```rust +/// Resolves the physical `EdgeZero` app-config store name. +#[must_use] +pub fn config_store_name(env: &EnvConfig) -> StoreName { + StoreName::from(env.store_name("config", DEFAULT_CONFIG_STORE_ID)) +} + +/// Resolves the config-store key containing the app-config blob. +#[must_use] +pub fn config_key(env: &EnvConfig) -> String { + env.store_key("config", DEFAULT_CONFIG_STORE_ID) +} + +/// Returns the default `EdgeZero` app-config store name. +#[must_use] +pub fn default_config_store_name() -> StoreName { + config_store_name(&EnvConfig::from_env()) +} + +/// Returns the default config-store key containing the app-config blob. +#[must_use] +pub fn default_config_key() -> String { + config_key(&EnvConfig::from_env()) +} +``` + +Do not remove the zero-argument wrappers: Axum and the trait-default Fastly route construction still use native environment overlays. + +- [ ] **Step 4: Run focused and adapter regression tests** + +Run: + +```bash +cargo test-fastly config_selectors_ +cargo test-axum +``` + +Expected: both new selector tests pass and Axum behavior remains green. + +- [ ] **Step 5: Commit the selector boundary** + +```bash +git add crates/trusted-server-core/src/settings_data.rs +git commit -m "Add explicit EdgeZero config selectors" +``` + +### Task 3: Wire one Fastly runtime configuration through every settings load + +Use `@superpowers:test-driven-development` for the metadata contract, then let target compilation prove every call site accepts the explicit configuration. + +**Files:** + +- Modify: `crates/trusted-server-adapter-fastly/src/app.rs:115-180` +- Modify: `crates/trusted-server-adapter-fastly/src/app.rs:1231-1300` +- Modify: `crates/trusted-server-adapter-fastly/src/main.rs:1-130` +- Modify: `crates/trusted-server-adapter-fastly/src/main.rs:190-240` +- Test: `crates/trusted-server-adapter-fastly/src/app.rs` unit-test module + +- [ ] **Step 1: Add the failing store-metadata contract test** + +Import `Hooks` and `StoreMetadata` in the Fastly app tests and add: + +```rust +#[test] +fn trusted_server_app_declares_config_store_metadata() { + let stores = TrustedServerApp::stores(); + + assert_eq!( + stores.config, + Some(StoreMetadata { + default: "trusted_server_config", + ids: &["trusted_server_config"], + }) + ); + assert_eq!(stores.kv, None); + assert_eq!(stores.secrets, None); +} +``` + +- [ ] **Step 2: Run the metadata test and verify the red state** + +Run: + +```bash +cargo test-fastly trusted_server_app_declares_config_store_metadata +``` + +Expected: the assertion fails because the handwritten `Hooks` implementation currently inherits empty `StoresMetadata`. + +- [ ] **Step 3: Declare the config metadata** + +Change the app imports to include `StoreMetadata`, `StoresMetadata`, and `EnvConfig`. Add this method to `impl Hooks for TrustedServerApp`: + +```rust +fn stores() -> StoresMetadata { + StoresMetadata { + config: Some(StoreMetadata { + default: "trusted_server_config", + ids: &["trusted_server_config"], + }), + ..StoresMetadata::default() + } +} +``` + +Run the focused metadata test again. Expected: PASS. + +- [ ] **Step 4: Parameterize Fastly settings and application construction** + +In `app.rs`, import `config_key` and `config_store_name`. Change the selector-sensitive functions to: + +```rust +pub(crate) fn build_state( + env: &EnvConfig, +) -> Result, Report> { + build_state_from_settings(load_settings_from_config_store(env)?) +} + +pub(crate) fn load_settings_from_config_store( + env: &EnvConfig, +) -> Result> { + let store_name = config_store_name(env); + let key = config_key(env); + get_settings_from_config_store(&FastlyPlatformConfigStore, &store_name, &key) +} +``` + +Thread `&EnvConfig` through: + +```rust +pub(crate) fn build_app_with_state(env: &EnvConfig) -> (App, Option>) +fn router_with_state(env: &EnvConfig) -> (RouterService, Option>) +``` + +`build_app_with_state` passes `env` to `router_with_state`; `router_with_state` passes it to `build_state`. + +Preserve the `Hooks::routes()` contract with the native wrapper: + +```rust +fn routes() -> RouterService { + let env = EnvConfig::from_env(); + Self::router_with_state(&env).0 +} +``` + +- [ ] **Step 5: Resolve Fastly runtime configuration once** + +In `main.rs`: + +- import `edgezero_adapter_fastly::runtime_env_config`; +- import `edgezero_core::app::Hooks as _`; +- import `edgezero_core::env_config::EnvConfig`; and +- import `trusted_server_core::settings_data::config_store_name`. + +Remove `TRUSTED_SERVER_CONFIG_STORE`. Parameterize store opening: + +```rust +fn open_trusted_server_config_store( + env: &EnvConfig, +) -> Result { + let store_name = config_store_name(env); + let store = EdgeZeroFastlyConfigStore::try_open(store_name.as_ref()).map_err(|e| { + fastly::Error::msg(format!( + "failed to open config store `{store_name}`: {e}" + )) + })?; + Ok(ConfigStoreHandle::new(Arc::new(store))) +} +``` + +After the health fast path and logger initialization, load once: + +```rust +logging::init_logger(); +let env = runtime_env_config(TrustedServerApp::stores()); +edgezero_main(req, &env); +``` + +Change `edgezero_main` to accept `env: &EnvConfig`. + +- [ ] **Step 6: Thread the same reference through all five selector-sensitive loads** + +Within `edgezero_main`, change exactly these call sites: + +```rust +load_settings_from_config_store(env) // JA4 debug path +open_trusted_server_config_store(env) // physical store open +TrustedServerApp::build_app_with_state(env) // initial app construction +load_settings_from_config_store(env) // finalize fallback reload +load_settings_from_config_store(env) // EC-finalize fallback reload +``` + +Run: + +```bash +rg -n 'load_settings_from_config_store\(\)|open_trusted_server_config_store\(\)|build_app_with_state\(\)' crates/trusted-server-adapter-fastly/src +``` + +Expected: no zero-argument Fastly call remains. + +- [ ] **Step 7: Compile and test the full Fastly path** + +Run: + +```bash +cargo fmt --all +cargo check-fastly +cargo test-fastly +``` + +Expected: the Fastly adapter and core compile for `wasm32-wasip1`; selector and metadata tests pass; existing request, response, streaming, and route regressions remain green. + +- [ ] **Step 8: Commit the runtime selector integration** + +```bash +git add crates/trusted-server-adapter-fastly/src/app.rs crates/trusted-server-adapter-fastly/src/main.rs +git commit -m "Use EdgeZero runtime selectors on Fastly" +``` + +### Task 4: Complete the CLI argument proof + +**Files:** + +- Modify: `crates/trusted-server-cli/src/run.rs:207-234` +- Modify: `crates/trusted-server-cli/src/run.rs:561-579` + +- [ ] **Step 1: Prove explicit healthcheck paths** + +Extend `parses_healthcheck_with_staging_overrides` with: + +```rust +"--path", +"/ready", +``` + +and assert: + +```rust +assert_eq!(healthcheck.path, "/ready"); +``` + +- [ ] **Step 2: Prove both GC targeting flags** + +Extend `config_gc_parses_store_override` with `--no-env` and add: + +```rust +assert!(gc.no_env); +``` + +Keep the existing `gc.store == Some("other_config_store")` assertion. Together they prove the manifest's logical id can be overridden and physical-name environment mapping can be ignored. + +- [ ] **Step 3: Run the native CLI suite** + +Run: + +```bash +./scripts/test-cli.sh +``` + +Expected: all existing and new CLI parser tests pass. No `NewArgs`, `Command::New`, `run_new`, or deployment workflow is introduced. + +- [ ] **Step 4: Re-run the public command audit** + +Run: + +```bash +rg -n 'edgezero_cli::run_' crates/trusted-server-cli/src/run.rs +rg -n 'NewArgs|Command::New|run_new' crates/trusted-server-cli +``` + +Expected: every operational v0.0.7 handler is delegated; the second command returns no match. + +- [ ] **Step 5: Commit the CLI proof** + +```bash +git add crates/trusted-server-cli/src/run.rs +git commit -m "Complete EdgeZero CLI argument coverage" +``` + +### Task 5: Correct the operator documentation + +**Files:** + +- Modify: `docs/guide/cli.md:128-178` + +- [ ] **Step 1: Replace the obsolete staging limitation** + +Delete the blockquote claiming the staged binary reads production config. Replace it with: + +```markdown +The staged version resolves its app-config key through the version-linked +`edgezero_runtime_env` store. After `ts config push --staging`, the staged +binary reads `_staging` while the active production version +continues to read the production key. +``` + +- [ ] **Step 2: Make rollback capture guidance match v0.0.7** + +Retain the existing capture-before-deploy command and add one explicit sentence after the rollback example: + +```markdown +Capture the rollback target before mutating production with +`ts active-version`, or use an orchestration layer that captures the same +previous-version value before deployment. `ts deploy` cannot reconstruct that +value after the active version changes. +``` + +Do not add deployment-action workflow instructions. + +- [ ] **Step 3: Format and inspect the documentation** + +Run: + +```bash +./crates/trusted-server-js/lib/node_modules/.bin/prettier --check docs/guide/cli.md +git diff --check +``` + +Expected: formatting passes and the diff contains no stale limitation or claim that `ts new`/deploy workflows were added. + +- [ ] **Step 4: Commit the documentation** + +```bash +git add docs/guide/cli.md +git commit -m "Document EdgeZero staging selector support" +``` + +### Task 6: Run the complete verification matrix + +Use `@superpowers:verification-before-completion`. Do not update PR claims until these commands and required PR checks have observed results. + +**Files:** + +- Verify only; modify source only when a gate exposes a real defect + +- [ ] **Step 1: Verify lock and whitespace integrity** + +Run: + +```bash +cargo metadata --locked --no-deps +git diff --check +cargo fmt --all -- --check +``` + +Expected: all commands exit 0. + +- [ ] **Step 2: Run all target-matched clippy gates** + +Run: + +```bash +cargo clippy-fastly +cargo clippy-axum +cargo clippy-cloudflare +cargo clippy-cloudflare-wasm +cargo clippy-spin-native +cargo clippy-spin-wasm +``` + +Then run the explicitly separated host crates: + +```bash +cargo clippy --package trusted-server-cli --target "$(rustc -vV | sed -n 's/host: //p')" --all-targets --all-features -- -D warnings +cargo clippy --package trusted-server-openrtb-codegen --target "$(rustc -vV | sed -n 's/host: //p')" --all-targets -- -D warnings +cargo clippy --manifest-path crates/trusted-server-integration-tests/Cargo.toml --all-targets -- -D warnings +``` + +Expected: every lint command exits 0 with warnings denied. + +- [ ] **Step 3: Run all adapter and CLI tests** + +Run: + +```bash +cargo test-fastly +cargo test-axum +cargo test-cloudflare +cargo test-spin +./scripts/test-cli.sh +``` + +Expected: all adapter, core, and CLI tests pass. + +- [ ] **Step 4: Run parity, codegen, and benchmark smoke** + +Run: + +```bash +cargo fmt --manifest-path crates/trusted-server-integration-tests/Cargo.toml -- --check +cargo test --manifest-path crates/trusted-server-integration-tests/Cargo.toml --test parity +cargo test --package trusted-server-openrtb-codegen --target "$(rustc -vV | sed -n 's/host: //p')" +cargo bench -p trusted-server-core --bench html_processor_bench -- --test +``` + +Expected: parity, codegen, and benchmark smoke all pass. + +- [ ] **Step 5: Run all adapter build/check gates** + +Run: + +```bash +cargo build -p trusted-server-adapter-axum +cargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1 +cargo check -p trusted-server-adapter-cloudflare +cargo check-cloudflare +cargo check -p trusted-server-adapter-spin +cargo check-spin +``` + +Run the Spin release build with the same non-secret test fixture values used by `.github/workflows/test.yml`: + +```bash +TRUSTED_SERVER__PUBLISHER__ORIGIN_URL=http://127.0.0.1:8080 \ +TRUSTED_SERVER__PUBLISHER__PROXY_SECRET=integration-test-proxy-secret \ +TRUSTED_SERVER__EC__PASSPHRASE=integration-test-ec-secret-padded-32 \ +TRUSTED_SERVER__PROXY__CERTIFICATE_CHECK=false \ +cargo build --package trusted-server-adapter-spin --target wasm32-wasip1 --features spin --release +``` + +Expected: every native and Wasm build/check exits 0. + +- [ ] **Step 6: Run JavaScript gates** + +Run: + +```bash +cd crates/trusted-server-js/lib +node build-all.mjs +npx vitest run +npm run lint +npm run format +``` + +Expected: build succeeds; Vitest, ESLint, and Prettier pass. Return to the repository root afterward. + +- [ ] **Step 7: Run documentation gates** + +Run: + +```bash +cd docs +npm run lint +npm run format +npm run build +``` + +Expected: ESLint and Prettier pass; VitePress builds with no dead links. Return to the repository root afterward. + +- [ ] **Step 8: Run the served-seam harnesses when local prerequisites are available** + +Run: + +```bash +BID_DELAY=3 ./scripts/template-cache-local-test.sh esi +BID_DELAY=3 ./scripts/template-cache-local-test.sh inline +``` + +Expected: both Fastly/Viceroy harnesses pass. If a checked-in CI prerequisite is unavailable locally, record the exact reason and require the corresponding PR check to pass before completion. + +- [ ] **Step 9: Review the final branch diff** + +Run: + +```bash +git status --short --branch +git diff origin/main...HEAD --stat +git diff origin/main...HEAD -- Cargo.toml Cargo.lock crates/trusted-server-core/src/settings_data.rs crates/trusted-server-adapter-fastly/src/app.rs crates/trusted-server-adapter-fastly/src/main.rs crates/trusted-server-cli/src/run.rs docs/guide/cli.md +``` + +Expected: only the approved PR #940 changes are present; no `ts new`, deploy workflow, new PR scaffold, unrelated refactor, or unrelated lock churn appears. + +### Task 7: Push and update the existing PR #940 + +**External state:** + +- Push: existing branch `worktree-edgezero-316-upgrade` +- Modify: existing PR #940 title and body +- Do not create: any new branch-backed PR + +- [ ] **Step 1: Confirm branch and PR identity** + +Run: + +```bash +git status --short --branch +git rev-parse --abbrev-ref HEAD +gh pr view 940 --json number,state,headRefName,baseRefName,title,url +``` + +Expected: the current branch is the head of open PR #940. Stop rather than create another PR if the identity differs. + +- [ ] **Step 2: Push the verified commits to the existing branch** + +Run: + +```bash +git push origin worktree-edgezero-316-upgrade +``` + +Expected: the existing PR #940 head advances. Do not run `gh pr create`. + +- [ ] **Step 3: Wait for required PR #940 checks** + +Run: + +```bash +gh pr checks 940 --watch +``` + +Expected: every required check passes, including any Fastly harness or platform-specific gate not reproducible locally. If a check fails, inspect logs, fix only the demonstrated in-scope defect, rerun the relevant local gate, commit, push to the same branch, and watch PR #940 again. + +- [ ] **Step 4: Prepare the complete PR #940 body with observed evidence** + +Create `/private/tmp/pr-940-v0.0.7-body.md` with these exact sections, replacing the old v0.0.6 known-limitation section: + +```markdown +## Summary + +Upgrades all six EdgeZero workspace dependencies from v0.0.4 to v0.0.7 and +adapts Trusted Server to every relevant functional change in that range. + +- Retains the v0.0.6 lifecycle integration: `active-version`, `healthcheck`, + `rollback`, `config gc`, deploy/config staging, fail-closed deploy + passthrough, and the `AppConfigMeta::secret_fields()` migration. +- Uses v0.0.7's public `runtime_env_config` API in Trusted Server's custom + Fastly entry point, so staged binaries read `trusted_server_config_staging` + and production continues to read `trusted_server_config`. +- Declares Trusted Server's config-store metadata and uses one resolved + `EnvConfig` for JA4 settings, physical store opening, initial application + construction, and both fallback reloads. +- Completes downstream CLI argument coverage for explicit healthcheck paths and + config-GC targeting flags. + +## v0.0.4 to v0.0.7 audit + +- State/app-state and nested-secret APIs: compatible with Trusted Server's + existing explicit state and intentionally empty EdgeZero secret metadata. +- Fastly dispatch fidelity: Trusted Server retains its raw-request metadata + capture and duplicate-header-preserving streaming response path. +- Chunked config GC: exposed as `ts config gc` with EdgeZero's preview and + deletion safety gates. +- Fastly staging lifecycle: exposed through the operational `ts` commands and + now connected to version-linked runtime config selectors. +- Rollback hint correction: inherited from v0.0.7 and documented with + capture-before-deploy ordering. + +## CLI surface + +Trusted Server delegates `active-version`, `auth`, `build`, `config diff`, +`config gc`, `config push`, `config validate`, `deploy`, `healthcheck`, +`provision`, `rollback`, and `serve` to EdgeZero. + +`ts new`, the contributor-only demo command, and deployment-action workflows +are intentionally excluded. + +## Verification + +All required local and PR gates passed on the pushed PR head: + +- Rust: locked metadata, formatting, all target-matched clippy aliases, adapter + tests, native CLI, parity, codegen, benchmark smoke, and native/Wasm + build/check coverage. +- JavaScript: build, Vitest, ESLint, and Prettier. +- Documentation: ESLint, Prettier, and VitePress dead-link build. +- Required PR #940 checks: all passed, including the Fastly served-seam + harnesses and platform-specific jobs. + +## Out of scope + +- Adding `ts new`. +- Adding EdgeZero deployment-action workflows. +- Creating a new pull request. + +Closes #939 +Closes #1082 +``` + +Use `apply_patch` to create the temporary body file; do not add it to git. Include only claims established by Task 6 and Step 3. If a local optional harness was unavailable, name the passing required PR check that supplied that evidence instead of claiming a local run. + +- [ ] **Step 5: Update only PR #940** + +Run: + +```bash +gh pr edit 940 --title "Upgrade EdgeZero to v0.0.7" --body-file /private/tmp/pr-940-v0.0.7-body.md +``` + +Expected: PR #940 is renamed and its body contains both closing keywords. No new PR exists. + +- [ ] **Step 6: Verify remote metadata, mergeability, and checks** + +Run: + +```bash +gh pr view 940 --json number,state,title,body,url,mergeable,headRefOid +gh pr checks 940 +``` + +Expected: + +- number is 940; +- title is `Upgrade EdgeZero to v0.0.7`; +- body contains `Closes #939` and `Closes #1082`; +- head OID matches the pushed local HEAD; and +- the PR remains open and mergeable or reports GitHub's temporary `UNKNOWN` while recalculating; and +- required checks still report success after the metadata-only edit. + +- [ ] **Step 7: Final handoff** + +Report: + +- the final commit SHA and PR #940 URL; +- the six v0.0.7 dependency pins and resolved EdgeZero commit; +- the selector behavior now proven; +- the complete operational CLI surface and explicit exclusions; +- local verification results and required PR check status; and +- that #939 and #1082 will close on merge. From a1fa6b1a986db67b033896bc09de051fe6b7af0d Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Tue, 1 Sep 2026 10:45:36 -0700 Subject: [PATCH 15/21] Upgrade EdgeZero dependencies to v0.0.7 --- Cargo.lock | 16 ++++++++-------- Cargo.toml | 12 ++++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7f7587c8b..8e1ceb1f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1427,7 +1427,7 @@ dependencies = [ [[package]] name = "edgezero-adapter" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?tag=v0.0.6#c8d6e414e2095875c429b36dc8099ef26bf2842b" +source = "git+https://github.com/stackpop/edgezero?tag=v0.0.7#5c9886e51d17e6969531356bacdf27f144ac8a2e" dependencies = [ "toml", ] @@ -1435,7 +1435,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-axum" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?tag=v0.0.6#c8d6e414e2095875c429b36dc8099ef26bf2842b" +source = "git+https://github.com/stackpop/edgezero?tag=v0.0.7#5c9886e51d17e6969531356bacdf27f144ac8a2e" dependencies = [ "anyhow", "async-trait", @@ -1463,7 +1463,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-cloudflare" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?tag=v0.0.6#c8d6e414e2095875c429b36dc8099ef26bf2842b" +source = "git+https://github.com/stackpop/edgezero?tag=v0.0.7#5c9886e51d17e6969531356bacdf27f144ac8a2e" dependencies = [ "anyhow", "async-trait", @@ -1486,7 +1486,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-fastly" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?tag=v0.0.6#c8d6e414e2095875c429b36dc8099ef26bf2842b" +source = "git+https://github.com/stackpop/edgezero?tag=v0.0.7#5c9886e51d17e6969531356bacdf27f144ac8a2e" dependencies = [ "anyhow", "async-stream", @@ -1515,7 +1515,7 @@ dependencies = [ [[package]] name = "edgezero-adapter-spin" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?tag=v0.0.6#c8d6e414e2095875c429b36dc8099ef26bf2842b" +source = "git+https://github.com/stackpop/edgezero?tag=v0.0.7#5c9886e51d17e6969531356bacdf27f144ac8a2e" dependencies = [ "anyhow", "async-trait", @@ -1542,7 +1542,7 @@ dependencies = [ [[package]] name = "edgezero-cli" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?tag=v0.0.6#c8d6e414e2095875c429b36dc8099ef26bf2842b" +source = "git+https://github.com/stackpop/edgezero?tag=v0.0.7#5c9886e51d17e6969531356bacdf27f144ac8a2e" dependencies = [ "chrono", "clap", @@ -1567,7 +1567,7 @@ dependencies = [ [[package]] name = "edgezero-core" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?tag=v0.0.6#c8d6e414e2095875c429b36dc8099ef26bf2842b" +source = "git+https://github.com/stackpop/edgezero?tag=v0.0.7#5c9886e51d17e6969531356bacdf27f144ac8a2e" dependencies = [ "anyhow", "async-compression", @@ -1598,7 +1598,7 @@ dependencies = [ [[package]] name = "edgezero-macros" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?tag=v0.0.6#c8d6e414e2095875c429b36dc8099ef26bf2842b" +source = "git+https://github.com/stackpop/edgezero?tag=v0.0.7#5c9886e51d17e6969531356bacdf27f144ac8a2e" dependencies = [ "log", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 30212ba25..7faba7553 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,12 +54,12 @@ criterion = { version = "0.5", default-features = false, features = ["cargo_benc derive_more = { version = "2.0", features = ["display", "error"] } directories = "5" ed25519-dalek = { version = "2.2", features = ["rand_core"] } -edgezero-adapter-axum = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.6", default-features = false } -edgezero-adapter-cloudflare = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.6", default-features = false } -edgezero-adapter-fastly = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.6", default-features = false } -edgezero-adapter-spin = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.6", default-features = false } -edgezero-cli = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.6" } -edgezero-core = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.6", default-features = false } +edgezero-adapter-axum = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.7", default-features = false } +edgezero-adapter-cloudflare = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.7", default-features = false } +edgezero-adapter-fastly = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.7", default-features = false } +edgezero-adapter-spin = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.7", default-features = false } +edgezero-cli = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.7" } +edgezero-core = { git = "https://github.com/stackpop/edgezero", tag = "v0.0.7", default-features = false } env_logger = "0.11" error-stack = "0.6" esi = "0.7.2" From 05557b59c26deefd1df84059c3aeabe8ff4a09af Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Tue, 1 Sep 2026 10:48:21 -0700 Subject: [PATCH 16/21] Add explicit EdgeZero config selectors --- .../trusted-server-core/src/settings_data.rs | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/crates/trusted-server-core/src/settings_data.rs b/crates/trusted-server-core/src/settings_data.rs index 06ea548fc..5e60417d5 100644 --- a/crates/trusted-server-core/src/settings_data.rs +++ b/crates/trusted-server-core/src/settings_data.rs @@ -28,16 +28,28 @@ struct FastlyChunkRef { sha256: String, } +/// Resolves the `EdgeZero` app-config store name from runtime configuration. +#[must_use] +pub fn config_store_name(env: &EnvConfig) -> StoreName { + StoreName::from(env.store_name("config", DEFAULT_CONFIG_STORE_ID)) +} + +/// Resolves the config-store key containing the app-config blob. +#[must_use] +pub fn config_key(env: &EnvConfig) -> String { + env.store_key("config", DEFAULT_CONFIG_STORE_ID) +} + /// Returns the default `EdgeZero` app-config store name. #[must_use] pub fn default_config_store_name() -> StoreName { - StoreName::from(EnvConfig::from_env().store_name("config", DEFAULT_CONFIG_STORE_ID)) + config_store_name(&EnvConfig::from_env()) } /// Returns the default config-store key containing the app-config blob. #[must_use] pub fn default_config_key() -> String { - EnvConfig::from_env().store_key("config", DEFAULT_CONFIG_STORE_ID) + config_key(&EnvConfig::from_env()) } /// Loads [`Settings`] from a platform config store and key. @@ -219,6 +231,40 @@ mod tests { serde_json::to_string(&envelope).expect("should serialize envelope") } + #[test] + fn config_selectors_default_to_the_logical_store_id() { + let env = EnvConfig::default(); + + assert_eq!( + config_store_name(&env), + StoreName::from("trusted_server_config") + ); + assert_eq!(config_key(&env), "trusted_server_config"); + } + + #[test] + fn config_key_selects_the_staging_key() { + let env = EnvConfig::from_vars([( + "EDGEZERO__STORES__CONFIG__TRUSTED_SERVER_CONFIG__KEY", + "trusted_server_config_staging", + )]); + + assert_eq!(config_key(&env), "trusted_server_config_staging"); + } + + #[test] + fn config_store_name_applies_the_physical_name_override() { + let env = EnvConfig::from_vars([( + "EDGEZERO__STORES__CONFIG__TRUSTED_SERVER_CONFIG__NAME", + "publisher-config-store", + )]); + + assert_eq!( + config_store_name(&env), + StoreName::from("publisher-config-store") + ); + } + #[test] fn loads_settings_from_config_blob_entry() { let settings = From 8436790bed79d7f7994d1c0cba7f4da14692c9d7 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Tue, 1 Sep 2026 10:51:36 -0700 Subject: [PATCH 17/21] Use EdgeZero runtime selectors on Fastly --- .../trusted-server-adapter-fastly/src/app.rs | 55 ++++++++++++++----- .../trusted-server-adapter-fastly/src/main.rs | 30 +++++----- 2 files changed, 58 insertions(+), 27 deletions(-) diff --git a/crates/trusted-server-adapter-fastly/src/app.rs b/crates/trusted-server-adapter-fastly/src/app.rs index 06a0a155f..4ba61f603 100644 --- a/crates/trusted-server-adapter-fastly/src/app.rs +++ b/crates/trusted-server-adapter-fastly/src/app.rs @@ -90,8 +90,9 @@ use std::sync::Arc; use crate::rate_limiter::{FastlyRateLimiter, RATE_COUNTER_NAME}; use edgezero_adapter_fastly::context::FastlyRequestContext; -use edgezero_core::app::{App, Hooks}; +use edgezero_core::app::{App, Hooks, StoreMetadata, StoresMetadata}; use edgezero_core::context::RequestContext; +use edgezero_core::env_config::EnvConfig; use edgezero_core::error::EdgeError; use edgezero_core::http::{ HandlerFuture, HeaderValue, Method, Request, Response, StatusCode, header, @@ -135,7 +136,7 @@ use trusted_server_core::request_signing::{ }; use trusted_server_core::settings::{ProxyAssetRoute, Settings}; use trusted_server_core::settings_data::{ - default_config_key, default_config_store_name, get_settings_from_config_store, + config_key, config_store_name, get_settings_from_config_store, }; use trusted_server_core::tester_cookie::{handle_clear_tester, handle_set_tester}; @@ -167,14 +168,16 @@ pub(crate) struct AppState { /// /// Returns an error when settings, the auction orchestrator, or the integration /// registry fail to initialise. -pub(crate) fn build_state() -> Result, Report> { - build_state_from_settings(load_settings_from_config_store()?) +pub(crate) fn build_state(env: &EnvConfig) -> Result, Report> { + build_state_from_settings(load_settings_from_config_store(env)?) } -pub(crate) fn load_settings_from_config_store() -> Result> { - let store_name = default_config_store_name(); - let config_key = default_config_key(); - get_settings_from_config_store(&FastlyPlatformConfigStore, &store_name, &config_key) +pub(crate) fn load_settings_from_config_store( + env: &EnvConfig, +) -> Result> { + let store_name = config_store_name(env); + let key = config_key(env); + get_settings_from_config_store(&FastlyPlatformConfigStore, &store_name, &key) } pub(crate) fn build_state_from_settings( @@ -1228,15 +1231,15 @@ fn fallback_route_handler( pub struct TrustedServerApp; impl TrustedServerApp { - pub(crate) fn build_app_with_state() -> (App, Option>) { - let (router, state) = Self::router_with_state(); + pub(crate) fn build_app_with_state(env: &EnvConfig) -> (App, Option>) { + let (router, state) = Self::router_with_state(env); let mut app = App::with_name(router, Self::name()); Self::configure(&mut app); (app, state) } - fn router_with_state() -> (RouterService, Option>) { - let state = match build_state() { + fn router_with_state(env: &EnvConfig) -> (RouterService, Option>) { + let state = match build_state(env) { Ok(state) => state, Err(ref e) => { log::error!("failed to build application state: {:?}", e); @@ -1294,7 +1297,17 @@ impl Hooks for TrustedServerApp { } fn routes() -> RouterService { - Self::router_with_state().0 + Self::router_with_state(&EnvConfig::from_env()).0 + } + + fn stores() -> StoresMetadata { + StoresMetadata { + config: Some(StoreMetadata { + default: "trusted_server_config", + ids: &["trusted_server_config"], + }), + ..StoresMetadata::default() + } } } @@ -1313,6 +1326,7 @@ mod tests { }; use base64::Engine as _; use bytes::Bytes; + use edgezero_core::app::{Hooks as _, StoreMetadata}; use edgezero_core::body::Body; use edgezero_core::context::RequestContext; use edgezero_core::http::{Method, Response, StatusCode, header, request_builder}; @@ -1453,6 +1467,21 @@ mod tests { TrustedServerApp::routes_for_state(&state) } + #[test] + fn trusted_server_app_declares_config_store_metadata() { + let stores = TrustedServerApp::stores(); + + assert_eq!( + stores.config, + Some(StoreMetadata { + default: "trusted_server_config", + ids: &["trusted_server_config"], + }) + ); + assert_eq!(stores.kv, None); + assert_eq!(stores.secrets, None); + } + #[test] fn per_request_services_register_the_fastly_template_assembler() { let state = build_state_from_settings(test_settings()).expect("should build test state"); diff --git a/crates/trusted-server-adapter-fastly/src/main.rs b/crates/trusted-server-adapter-fastly/src/main.rs index 90879c3ba..8a73a80db 100644 --- a/crates/trusted-server-adapter-fastly/src/main.rs +++ b/crates/trusted-server-adapter-fastly/src/main.rs @@ -2,8 +2,11 @@ use std::sync::Arc; use edgezero_adapter_fastly::config_store::FastlyConfigStore as EdgeZeroFastlyConfigStore; use edgezero_adapter_fastly::request::into_core_request; +use edgezero_adapter_fastly::runtime_env_config; +use edgezero_core::app::Hooks as _; use edgezero_core::body::Body as EdgeBody; use edgezero_core::config_store::ConfigStoreHandle; +use edgezero_core::env_config::EnvConfig; use edgezero_core::error::EdgeError; use edgezero_core::http::{Request as HttpRequest, Response as HttpResponse}; use edgezero_core::response::IntoResponse; @@ -26,6 +29,7 @@ use trusted_server_core::platform::RuntimeServices; use trusted_server_core::proxy::{AssetProxyCachePolicy, stream_asset_body}; use trusted_server_core::response_privacy::TerminalPrivateResponse; use trusted_server_core::settings::Settings; +use trusted_server_core::settings_data::config_store_name; mod app; mod backend; @@ -46,18 +50,15 @@ use crate::middleware::{HEADER_X_TS_FINALIZED, apply_finalize_headers, resolve_g use crate::platform::{FastlyPlatformGeo, client_info_from_request}; use crate::rate_limiter::{FastlyRateLimiter, RATE_COUNTER_NAME}; -const TRUSTED_SERVER_CONFIG_STORE: &str = "trusted_server_config"; - /// Opens the Fastly Config Store used by the `EdgeZero` dispatcher. /// /// # Errors /// /// Returns [`fastly::Error`] if the config store cannot be opened. -fn open_trusted_server_config_store() -> Result { - let store = EdgeZeroFastlyConfigStore::try_open(TRUSTED_SERVER_CONFIG_STORE).map_err(|e| { - fastly::Error::msg(format!( - "failed to open config store `{TRUSTED_SERVER_CONFIG_STORE}`: {e}" - )) +fn open_trusted_server_config_store(env: &EnvConfig) -> Result { + let store_name = config_store_name(env); + let store = EdgeZeroFastlyConfigStore::try_open(store_name.as_ref()).map_err(|e| { + fastly::Error::msg(format!("failed to open config store `{store_name}`: {e}")) })?; Ok(ConfigStoreHandle::new(Arc::new(store))) } @@ -85,16 +86,17 @@ fn main() { } logging::init_logger(); - edgezero_main(req); + let env = runtime_env_config(TrustedServerApp::stores()); + edgezero_main(req, &env); } /// Handles a request through the `EdgeZero` router path. -fn edgezero_main(mut req: FastlyRequest) { +fn edgezero_main(mut req: FastlyRequest, env: &EnvConfig) { // Short-circuit the JA4 debug probe before app construction. Must run here // because TLS/JA4 accessors are only available on FastlyRequest before // conversion to edgezero types. if req.get_method() == FastlyMethod::GET && req.get_path() == "/_ts/debug/ja4" { - match load_settings_from_config_store() { + match load_settings_from_config_store(env) { Ok(settings) if settings.debug.ja4_endpoint_enabled => { build_ja4_debug_response(&req).send_to_client(); } @@ -111,7 +113,7 @@ fn edgezero_main(mut req: FastlyRequest) { return; } - let config_store = match open_trusted_server_config_store() { + let config_store = match open_trusted_server_config_store(env) { Ok(cs) => cs, Err(e) => { log::error!("failed to open config store: {e}"); @@ -122,7 +124,7 @@ fn edgezero_main(mut req: FastlyRequest) { } }; - let (app, app_state) = TrustedServerApp::build_app_with_state(); + let (app, app_state) = TrustedServerApp::build_app_with_state(env); let settings_snapshot = app_state.as_ref().map(|state| Arc::clone(&state.settings)); let trusted_client_ip = settings_snapshot .as_deref() @@ -197,7 +199,7 @@ fn edgezero_main(mut req: FastlyRequest) { if let Some(settings) = settings_snapshot.as_deref() { apply_entry_point_finalize_headers(settings, &mut response, client_ip); } else { - match load_settings_from_config_store() { + match load_settings_from_config_store(env) { Ok(settings) => { apply_entry_point_finalize_headers(&settings, &mut response, client_ip); } @@ -227,7 +229,7 @@ fn edgezero_main(mut req: FastlyRequest) { } } } else { - match load_settings_from_config_store() { + match load_settings_from_config_store(env) { Ok(settings) => { match apply_edgezero_ec_finalize(&settings, &ec_state, &mut response) { Ok(partner_registry) => { From 7cc0808aa535bc4e619fcdc8f8cbc4f0fb1ab8ae Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Tue, 1 Sep 2026 10:52:23 -0700 Subject: [PATCH 18/21] Complete EdgeZero CLI argument coverage --- crates/trusted-server-cli/src/run.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/trusted-server-cli/src/run.rs b/crates/trusted-server-cli/src/run.rs index 069a1d26b..13009d448 100644 --- a/crates/trusted-server-cli/src/run.rs +++ b/crates/trusted-server-cli/src/run.rs @@ -216,6 +216,8 @@ mod tests { "7", "--domain", "edge.example", + "--path", + "/ready", "--staging", "--retry", "9", @@ -227,6 +229,7 @@ mod tests { let Command::Healthcheck(healthcheck) = args.command else { panic!("expected healthcheck command"); }; + assert_eq!(healthcheck.path, "/ready"); assert!(healthcheck.staging); assert_eq!(healthcheck.retry, 9); assert_eq!(healthcheck.retry_delay, 2); @@ -567,6 +570,7 @@ mod tests { "fastly", "--store", "other_config_store", + "--no-env", ]); let Command::Config(ConfigCommand::Gc(gc)) = args.command else { panic!("expected config gc command"); @@ -576,6 +580,7 @@ mod tests { Some("other_config_store".to_owned()), "should retarget which store a sweep inspects" ); + assert!(gc.no_env, "should disable environment overlays"); } #[test] From 40c5a324bb581510d0a7103600f274da48eb728f Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Tue, 1 Sep 2026 10:53:07 -0700 Subject: [PATCH 19/21] Document EdgeZero staging selector support --- docs/guide/cli.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/guide/cli.md b/docs/guide/cli.md index c79776463..17f0e870f 100644 --- a/docs/guide/cli.md +++ b/docs/guide/cli.md @@ -134,12 +134,10 @@ ts config push --adapter fastly --staging ts config diff --adapter fastly --staging ``` -> **Known limitation:** Trusted Server's Fastly entry point does not yet read -> the version-linked `edgezero_runtime_env` selectors, so a staged version -> currently loads the **production** config blob rather than the staged one — -> a staging healthcheck exercises the new binary against production config. -> Selector resolution for custom entry points is tracked upstream in EdgeZero; -> until it lands, do not rely on `--staging` to validate a config change. +The staged version resolves its app-config key through the version-linked +`edgezero_runtime_env` store. After `ts config push --staging`, the staged +binary reads `_staging` while the active production version +continues to read the production key. `--staging` on `config push` / `config diff` writes and compares the `_staging` key in the same store. It is mutually exclusive @@ -166,6 +164,11 @@ ts rollback --adapter fastly --service-id \ --version --rollback-to ``` +Capture the rollback target before mutating production with +`ts active-version`, or use an orchestration layer that captures the same +previous-version value before deployment. `ts deploy` cannot reconstruct that +value after the active version changes. + `healthcheck` probes `/` by default (`--path` overrides) and makes 3 total attempts — not 3 retries after a first try — with a 5 second delay between attempts and a 10 second per-attempt timeout (`--retry`, `--retry-delay`, From e9a790f73dab1b5a1183841d8f98d4d03abcc3e6 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Tue, 1 Sep 2026 11:07:39 -0700 Subject: [PATCH 20/21] Fix template cache harness timeout budgets --- scripts/template-cache-local-test.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/template-cache-local-test.sh b/scripts/template-cache-local-test.sh index cc7e9eb87..ad9939d0a 100755 --- a/scripts/template-cache-local-test.sh +++ b/scripts/template-cache-local-test.sh @@ -199,8 +199,11 @@ s = s.replace('[integrations.prebid]\nenabled = false\nserver_url = "https://pre s = s.replace('providers = []', 'providers = ["prebid"]', 1) s = s.replace('\n[proxy]\n', '\n[proxy]\nallowed_domains = ["assets.example.com", "127.0.0.1"]\n', 1) s = s.replace('[auction]\nenabled = false', '[auction]\nenabled = true', 1) -s = s.replace('auction_timeout_ms = 500', 'auction_timeout_ms = 3000', 1) -s = s.replace('timeout_ms = 2000', 'timeout_ms = 3000', 1) +s = s.replace('auction_timeout_ms = 500', 'auction_timeout_ms = 5000', 1) +# Both the overall auction and provider transport budgets must exceed the +# harness's deliberate three-second bid delay. +s = s.replace('timeout_ms = 2000', 'timeout_ms = 5000', 1) +s = s.replace('timeout_ms = 1000', 'timeout_ms = 5000', 1) # The template-cache keys go directly under the table header. The slot is a table of its own # and must go at the end: inserted here it would swallow every scalar key that From 0f1c38c46256ff10ff1bbf169e20561d54918ca1 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Tue, 1 Sep 2026 11:12:11 -0700 Subject: [PATCH 21/21] Address final self-review findings --- crates/trusted-server-core/src/settings_data.rs | 17 ++++++++++++----- ...2026-09-01-edgezero-v0.0.7-upgrade-design.md | 9 ++++++--- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/crates/trusted-server-core/src/settings_data.rs b/crates/trusted-server-core/src/settings_data.rs index 5e60417d5..e9caa08d2 100644 --- a/crates/trusted-server-core/src/settings_data.rs +++ b/crates/trusted-server-core/src/settings_data.rs @@ -253,16 +253,23 @@ mod tests { } #[test] - fn config_store_name_applies_the_physical_name_override() { - let env = EnvConfig::from_vars([( - "EDGEZERO__STORES__CONFIG__TRUSTED_SERVER_CONFIG__NAME", - "publisher-config-store", - )]); + fn config_store_name_override_preserves_the_independently_selected_key() { + let env = EnvConfig::from_vars([ + ( + "EDGEZERO__STORES__CONFIG__TRUSTED_SERVER_CONFIG__NAME", + "publisher-config-store", + ), + ( + "EDGEZERO__STORES__CONFIG__TRUSTED_SERVER_CONFIG__KEY", + "trusted_server_config_staging", + ), + ]); assert_eq!( config_store_name(&env), StoreName::from("publisher-config-store") ); + assert_eq!(config_key(&env), "trusted_server_config_staging"); } #[test] diff --git a/docs/superpowers/specs/2026-09-01-edgezero-v0.0.7-upgrade-design.md b/docs/superpowers/specs/2026-09-01-edgezero-v0.0.7-upgrade-design.md index 3b7acbc92..4cc88f6dd 100644 --- a/docs/superpowers/specs/2026-09-01-edgezero-v0.0.7-upgrade-design.md +++ b/docs/superpowers/specs/2026-09-01-edgezero-v0.0.7-upgrade-design.md @@ -1,8 +1,11 @@ # PR #940 EdgeZero v0.0.7 Upgrade -**PR:** #940 -**Issues:** #939, #1082 -**Date:** 2026-09-01 +**PR:** #940 + +**Issues:** #939, #1082 + +**Date:** 2026-09-01 + **Status:** Approved design ## Problem