Skip to content

Add platform abstraction layer with traits and RuntimeServices#545

Open
prk-Jr wants to merge 13 commits intomainfrom
feature/edgezero-pr2-platform-traits
Open

Add platform abstraction layer with traits and RuntimeServices#545
prk-Jr wants to merge 13 commits intomainfrom
feature/edgezero-pr2-platform-traits

Conversation

@prk-Jr
Copy link
Collaborator

@prk-Jr prk-Jr commented Mar 20, 2026

Summary

  • Introduces trusted-server-core::platform module with six platform-neutral traits (PlatformConfigStore, PlatformSecretStore, PlatformKvStore, PlatformBackend, PlatformHttpClient, PlatformGeo) as the first step toward non-Fastly adapter support — full decoupling of fastly from core lands in Remove fastly from core crate #496
  • Defines ClientInfo (extract-once plain data struct), PlatformError enum, and RuntimeServices aggregate that threads all platform services into route_request
  • Moves GeoInfo to platform/types as platform-neutral data and adds a geo_from_fastly mapping function; wires full Fastly implementations in the adapter crate

Note: trusted-server-core still depends on the fastly crate directly (via geo.rs, backend.rs, fastly_storage.rs). These are legacy call-sites that will be migrated in follow-up PRs as part of the EdgeZero migration (#480). The new platform/ module is step 2 of that plan — the fastly dep removal from core is step 15 (#496).

Changes

File Change
crates/trusted-server-core/src/platform/mod.rs New module: RuntimeServices, re-exports, object-safety assertions, unit tests
crates/trusted-server-core/src/platform/traits.rs New: six platform traits with async_trait + WASM-conditional ?Send
crates/trusted-server-core/src/platform/types.rs New: ClientInfo, GeoInfo (moved here from geo.rs); RuntimeServicesBuilder replaces new() constructor; StoreName/StoreId newtypes
crates/trusted-server-core/src/platform/error.rs New: PlatformError enum
crates/trusted-server-core/src/platform/http.rs New: PlatformHttpClient request/response types
crates/trusted-server-core/src/platform/kv.rs New: UnavailableKvStore platform-neutral fallback
crates/trusted-server-core/src/geo.rs Refactored: delegates to platform/types, adds geo_from_fastly
crates/trusted-server-core/src/backend.rs Updated to use PlatformBackend trait
crates/trusted-server-core/src/fastly_storage.rs Adds PlatformKvStore / PlatformConfigStore impl wiring
crates/trusted-server-adapter-fastly/src/platform.rs New: Fastly implementations of all platform traits; uses try_open/try_get throughout
crates/trusted-server-adapter-fastly/src/main.rs Constructs RuntimeServices, threads into route_request; KV store failure degrades gracefully
crates/trusted-server-adapter-fastly/Cargo.toml Adds async-trait, futures dependencies
crates/trusted-server-core/Cargo.toml Adds async-trait dependency

Closes

Closes #483

Test plan

  • cargo test --workspace
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo fmt --all -- --check
  • JS tests: cd crates/js/lib && npx vitest run
  • JS format: cd crates/js/lib && npm run format
  • Docs format: cd docs && npm run format
  • WASM build: cargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1
  • Manual testing via fastly compute serve

Checklist

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

prk-Jr added 5 commits March 18, 2026 16:54
Rename crates/common → crates/trusted-server-core and crates/fastly →
crates/trusted-server-adapter-fastly following the EdgeZero naming
convention. Add EdgeZero workspace dependencies pinned to rev 170b74b.
Update all references across docs, CI workflows, scripts, agent files,
and configuration.
Introduces trusted-server-core::platform with PlatformConfigStore,
PlatformSecretStore, PlatformKvStore, PlatformBackend, PlatformHttpClient,
and PlatformGeo traits alongside ClientInfo, PlatformError, and
RuntimeServices. Wires the Fastly adapter implementations and threads
RuntimeServices into route_request. Moves GeoInfo to platform/types as
platform-neutral data and adds geo_from_fastly for field mapping.
@prk-Jr prk-Jr self-assigned this Mar 20, 2026
prk-Jr added 4 commits March 20, 2026 21:53
- Defer KV store opening: replace early error return with a local
  UnavailableKvStore fallback so routes that do not need synthetic ID
  access succeed when the KV store is missing or temporarily unavailable
- Use ConfigStore::try_open + try_get and SecretStore::try_get throughout
  FastlyPlatformConfigStore and FastlyPlatformSecretStore to honour the
  Result contract instead of panicking on open/lookup failure
- Encapsulate RuntimeServices service fields as pub(crate) with public
  getter methods (config_store, secret_store, backend, http_client, geo)
  and a pub new() constructor; adapter updated to use new()
- Reference #487 in FastlyPlatformHttpClient stub (PR 6 implements it)
- Remove unused KvPage re-export from platform/mod.rs
- Use super::KvHandle shorthand in RuntimeServices::kv_handle()
Copy link
Collaborator

@aram356 aram356 left a comment

Choose a reason for hiding this comment

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

Summary

Solid architectural direction — this PR lays the groundwork for decoupling trusted-server-core from the Fastly SDK. The trait design, object-safety assertions, and graceful KV degradation are well done. A few issues to address before merge, mostly around constructor ergonomics and a question about Send bounds.

Blocking

🔧 wrench

  • RuntimeServices::new takes 7 args with lint suppression: At the boundary today, will exceed it when creative_store/counter_store are added per the TODO. Use a builder pattern instead. (crates/trusted-server-core/src/platform/types.rs:103)
  • PR description overstates decoupling: trusted-server-core still depends on fastly directly (geo.rs, backend.rs, fastly_storage.rs, settings.rs). The description says this "enables future non-Fastly adapter support" — should clarify this is step N of M and the fastly dep removal comes in a follow-up.

❓ question

  • PlatformPendingRequest is !Send — intentional?: Box<dyn Any> is !Send, while PlatformHttpClient: Send + Sync. Will this asymmetry hold for future adapters using tokio::spawn? (crates/trusted-server-core/src/platform/http.rs:64)

Non-blocking

🤔 thinking

  • PlatformBackendSpec duplicates BackendConfig fields: Both types have scheme, host, port, certificate_check, first_byte_timeout — one owns, the other borrows. Every ensure call allocates owned Strings immediately borrowed. Consider sharing the type.
  • Asymmetric key semantics on PlatformConfigStore: get takes store_name, put/delete take store_id. Newtype wrappers would make misuse a compile error. (crates/trusted-server-core/src/platform/traits.rs:19)

♻️ refactor

  • geo_from_fastly should live in adapter, not core: Imports fastly::geo::Geo in core, tying it to Fastly. Being pub means new code could accidentally depend on it. (crates/trusted-server-core/src/geo.rs:10)
  • UnavailableKvStore should live in core, not adapter: Platform-neutral fallback that any future adapter would duplicate. (crates/trusted-server-adapter-fastly/src/platform.rs:276)

⛏ nitpick

  • Prefer attach_with over attach(format!(...)): Avoids allocation unless the report is inspected. Appears throughout platform.rs.

🌱 seedling

  • No Debug impl for RuntimeServices: A manual Debug impl printing client_info while omitting service handles would help debugging.

CI Status

  • fmt: PASS
  • clippy: PASS
  • rust tests: PASS
  • js tests: PASS

///
/// The core stores this as an opaque support type. Adapter implementations can
/// recover their concrete runtime handle through [`Self::downcast`].
pub struct PlatformPendingRequest {
Copy link
Collaborator

Choose a reason for hiding this comment

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

questionPlatformPendingRequest uses Box<dyn Any> which is !Send

PlatformPendingRequest stores Box<dyn Any> (!Send), while RuntimeServices stores Arc<dyn PlatformHttpClient> requiring Send + Sync. The trait contract says PlatformHttpClient: Send + Sync but async methods' futures are !Send (via #[async_trait(?Send)]).

Is this intentional asymmetry? Will it hold for future adapters (e.g., an Axum adapter using tokio::spawn)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Intentional — documented in the struct's doc comment. Box<dyn Any> (not Box<dyn Any + Send>) matches the #[async_trait(?Send)] contract: edgezero_core::body::Body wraps a LocalBoxStream that is !Send by design for wasm32 compatibility. A future multi-threaded adapter (e.g. Axum) would use Arc rather than relying on Send here.

@prk-Jr
Copy link
Collaborator Author

prk-Jr commented Mar 22, 2026

Addressing the review findings:

♻️ geo_from_fastly should live in adapter
Agreed. Moving it requires also reworking GeoInfo::from_request (which calls it) and removing the fastly dep from core entirely — that's the explicit scope of #496. Tracked there.

🤔 PlatformBackendSpec duplicates BackendConfig fields
Agreed. Unifying the two types is tracked in #487 (Backend + HTTP client traits). Added a note on that issue.

attach_with over attach(format!(...))
Report::attach_with does not exist in error-stack 0.6 — only ResultExt::attach_with does (for Result types). All format!() calls are already inside map_err closures so they are only evaluated on the error path. No allocation saving is possible here with the current API.

All other blocking and non-blocking items have been addressed in the latest commits.

@prk-Jr prk-Jr requested a review from aram356 March 22, 2026 16:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Define platform traits, ClientInfo, PlatformError, RuntimeServices

2 participants