Rolling main documentation
Built from commit f80b604a992ba1144e6c45229a8165c81e52dae0. Content may describe unreleased behavior.
Integration Development
Trusted Server integrations are platform-neutral registrations assembled by IntegrationRegistry. Adapter crates provide I/O through RuntimeServices; integration code must not import Fastly, Cloudflare, Axum, or Spin SDK types.
Choose the narrowest hook
IntegrationProxyowns explicit method/path endpoints and receivesSettings,RuntimeServices, and an EdgeZero-neutral request.IntegrationAttributeRewriterinspects selected HTML attributes.IntegrationScriptRewriterhandles one declared selector.IntegrationHeadInjectorinserts deterministic head markup.IntegrationHtmlPostProcessoris for bounded whole-document work that cannot be performed during streaming.IntegrationRequestFiltermakes an early request decision.
Build one IntegrationRegistration with only the hooks the feature needs. Use with_deferred_js() only for a separately loaded integration module and without_js() when another asset path owns delivery. Proxy routes should be namespaced and bounded; do not introduce a general outbound proxy.
Compiling core-neutral fixture
The fixture below registers an attribute rewriter and constructs every required RuntimeServices service without an adapter dependency. The documentation test extracts this exact fence and compiles it as an isolated crate.
use std::net::IpAddr;
use std::sync::Arc;
use error_stack::Report;
use trusted_server_core::integrations::{
AttributeRewriteAction, IntegrationAttributeContext,
IntegrationAttributeRewriter, IntegrationRegistration,
};
use trusted_server_core::platform::{
BackendNamingPolicy, ClientInfo, GeoInfo, PlatformBackend,
PlatformBackendSpec, PlatformConfigStore, PlatformError, PlatformGeo,
PlatformSecretStore, RuntimeServices, StoreId, StoreName,
UnavailableHttpClient, UnavailableKvStore,
};
struct ReadOnlyStore;
impl PlatformConfigStore for ReadOnlyStore {
fn get(
&self,
_store: &StoreName,
_key: &str,
) -> Result<String, Report<PlatformError>> {
Err(Report::new(PlatformError::Unsupported))
}
fn put(
&self,
_store: &StoreId,
_key: &str,
_value: &str,
) -> Result<(), Report<PlatformError>> {
Err(Report::new(PlatformError::Unsupported))
}
fn delete(
&self,
_store: &StoreId,
_key: &str,
) -> Result<(), Report<PlatformError>> {
Err(Report::new(PlatformError::Unsupported))
}
}
impl PlatformSecretStore for ReadOnlyStore {
fn get_bytes(
&self,
_store: &StoreName,
_key: &str,
) -> Result<Vec<u8>, Report<PlatformError>> {
Err(Report::new(PlatformError::Unsupported))
}
fn create(
&self,
_store: &StoreId,
_key: &str,
_value: &str,
) -> Result<(), Report<PlatformError>> {
Err(Report::new(PlatformError::Unsupported))
}
fn delete(
&self,
_store: &StoreId,
_key: &str,
) -> Result<(), Report<PlatformError>> {
Err(Report::new(PlatformError::Unsupported))
}
}
struct FixtureBackend;
impl PlatformBackend for FixtureBackend {
fn naming_policy(&self) -> BackendNamingPolicy {
BackendNamingPolicy::Axum
}
fn predict_name(
&self,
spec: &PlatformBackendSpec,
) -> Result<String, Report<PlatformError>> {
if spec.host.is_empty() {
Err(Report::new(PlatformError::Backend))
} else {
Ok("fixture-origin".to_owned())
}
}
fn ensure(
&self,
spec: &PlatformBackendSpec,
) -> Result<String, Report<PlatformError>> {
self.predict_name(spec)
}
}
struct FixtureGeo;
impl PlatformGeo for FixtureGeo {
fn lookup(
&self,
_client_ip: Option<IpAddr>,
) -> Result<Option<GeoInfo>, Report<PlatformError>> {
Ok(None)
}
}
struct AssetRewriter;
impl IntegrationAttributeRewriter for AssetRewriter {
fn integration_id(&self) -> &'static str {
"example"
}
fn handles_attribute(&self, attribute: &str) -> bool {
matches!(attribute, "src" | "href")
}
fn rewrite(
&self,
_attribute: &str,
value: &str,
_context: &IntegrationAttributeContext<'_>,
) -> AttributeRewriteAction {
value
.strip_prefix("https://assets.example/")
.map(|path| AttributeRewriteAction::replace(format!("/assets/{path}")))
.unwrap_or_else(AttributeRewriteAction::keep)
}
}
pub fn registration() -> IntegrationRegistration {
IntegrationRegistration::builder("example")
.with_attribute_rewriter(Arc::new(AssetRewriter))
.build()
}
pub fn runtime_services() -> RuntimeServices {
let store = Arc::new(ReadOnlyStore);
RuntimeServices::builder()
.config_store(store.clone())
.secret_store(store)
.kv_store(Arc::new(UnavailableKvStore))
.backend(Arc::new(FixtureBackend))
.http_client(Arc::new(UnavailableHttpClient))
.geo(Arc::new(FixtureGeo))
.client_info(ClientInfo::default())
.build()
}Production adapters replace every unavailable or fixture service with the target implementation. The builder deliberately panics when a required service is omitted, so adapter startup must construct the complete service graph.
Proxy implementation rules
An IntegrationProxy::handle implementation receives the complete runtime service graph. Register or predict backends through services.backend(), send through services.http_client(), bound request and response bodies, and return Report<TrustedServerError> with integration context. The registry strips internal identity headers before dispatch; an integration must opt into any explicit forwarding behavior.
Streaming support is an adapter capability. Request PlatformHttpRequest::with_stream_response() only when the caller has checked supports_streaming_responses(); unsupported adapters must reject the request instead of silently buffering it.
Browser and script guards
Add a browser module only when browser state is required. Immediate modules join the hashed unified bundle; deferred and standalone delivery are explicit registry decisions. Dynamic script interception must register with the shared DOM insertion dispatcher, remain idempotent, and leave unmatched elements untouched. See Trusted Server JavaScript and GPT's guarded handoff.
Registration checklist
- Add typed settings with validation and a disabled default unless the integration is intentionally universal.
- Register the exact capability predicates and route methods.
- Add source and behavior parity records.
- Add positive, negative, body-bound, header, and adapter-capability tests.
- Document configuration, failure behavior, and runtime limitations.
- Run the target aliases from Testing.