Skip to content

Commit 8a36c06

Browse files
committed
Load Spin startup config from a key-value store
The Spin adapter compiled the example config in at build time (`include_str!`), so it ignored `ts config push --adapter spin` and could never serve real operator config. Load Settings at startup from a Spin key-value store instead, matching the Fastly/Axum config-store path. - Add `SpinKvConfigStore`, a Spin key-value-backed `PlatformConfigStore` (async `spin_sdk::key_value`, wasm+spin gated). This is distinct from the variable-backed `ConfigStoreHandleAdapter`: a multi-kilobyte app-config blob does not fit Spin's flat variable namespace. - `build_state` now loads via `get_settings_from_config_store` on the Spin runtime (store id and blob key both `app_config`); native test builds keep the embedded example config since Spin host KV is unavailable there. - Grant the component the `app_config` KV label in spin.toml and declare its backend in a new runtime-config.toml; `serve` passes `--runtime-config-file`. - Unseeded/unreadable store fails closed (503) via the startup error router. - Ignore `.spin/` local runtime state; update the architecture doc. Verified: wasm build, native tests, clippy (wasm + native), and a live `spin up` unseeded run (503). The seeded read is verified on every local layer (the pushed blob is a valid envelope at store/key `app_config`) but not live end-to-end: the installed Spin CLI (4.0) is newer than the Spin 2-3 SQLite KV schema that `ts config push --local` writes, so a Spin-2/3 runtime (or an edgezero-cli update) is needed to confirm seeded -> 200.
1 parent 27b3ec6 commit 8a36c06

7 files changed

Lines changed: 108 additions & 5 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
/spin
1717
/spin.sig
1818

19+
# Spin runtime state (local KV/SQLite created by `spin up`)
20+
.spin/
21+
1922
# EdgeZero local KV store (created by edgezero-adapter-axum framework)
2023
.edgezero/
2124

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Spin runtime configuration for the Trusted Server adapter.
2+
#
3+
# Declares the `app_config` key-value store that holds the Trusted Server
4+
# app-config blob loaded at startup and seeded by `ts config push --adapter
5+
# spin`. Spin auto-provides only the `default` label; any other label (here
6+
# `app_config`) must be declared here or `spin up` fails with
7+
# `unknown key_value_stores label app_config`.
8+
#
9+
# `type = "spin"` uses Spin's built-in SQLite key-value backend (a local
10+
# `.spin/sqlite_key_value.db` file), matching what `ts config push --adapter
11+
# spin --local` writes to. Point it at redis/azure/etc. for a shared backend.
12+
#
13+
# Load it explicitly: `spin up --runtime-config-file runtime-config.toml`.
14+
[key_value_store.app_config]
15+
type = "spin"

crates/trusted-server-adapter-spin/spin.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ source = "../../target/wasm32-wasip1/release/trusted_server_adapter_spin.wasm"
3838
# origins are still served over plaintext http. Follow-up: scope this to the
3939
# configured origins once they can be enumerated from settings.
4040
allowed_outbound_hosts = ["https://*:*", "http://*:*"]
41-
key_value_stores = ["default"]
41+
# `app_config` holds the Trusted Server app-config blob loaded at startup and
42+
# seeded by `ts config push --adapter spin`. Spin auto-provides only `default`;
43+
# `app_config` must be granted here and backed by a `[key_value_store.app_config]`
44+
# stanza in runtime-config.toml (passed via `spin up --runtime-config-file`).
45+
key_value_stores = ["default", "app_config"]
4246

4347
[component.trusted-server.variables]
4448
v_current_x2dkid = "{{ v_current_x2dkid }}"

crates/trusted-server-adapter-spin/src/app.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ use trusted_server_core::request_signing::{
2626
handle_verify_signature,
2727
};
2828
use trusted_server_core::settings::Settings;
29+
#[cfg(all(feature = "spin", target_arch = "wasm32"))]
30+
use trusted_server_core::settings_data::{
31+
default_config_key, default_config_store_name, get_settings_from_config_store,
32+
};
2933

3034
use crate::middleware::{AuthMiddleware, FinalizeResponseMiddleware, NormalizeMiddleware};
3135
use crate::platform::build_runtime_services;
@@ -48,8 +52,34 @@ pub struct AppState {
4852
/// Returns an error when settings, the auction orchestrator, or the integration
4953
/// registry fail to initialise.
5054
fn build_state() -> Result<Arc<AppState>, Report<TrustedServerError>> {
51-
let settings = Settings::from_toml(include_str!("../../../trusted-server.example.toml"))?;
52-
build_state_with_settings(settings)
55+
build_state_with_settings(load_startup_settings()?)
56+
}
57+
58+
/// Loads startup [`Settings`] on the Spin runtime from the app-config blob in
59+
/// the Spin key-value store seeded by `ts config push --adapter spin` (store id
60+
/// and blob key both resolve to `app_config`).
61+
///
62+
/// # Errors
63+
///
64+
/// Returns [`TrustedServerError::ConfigStoreUnavailable`] (HTTP 503) when the
65+
/// store is unseeded or unreadable, and [`TrustedServerError::Configuration`]
66+
/// (HTTP 500) when the blob fails envelope/settings verification.
67+
#[cfg(all(feature = "spin", target_arch = "wasm32"))]
68+
fn load_startup_settings() -> Result<Settings, Report<TrustedServerError>> {
69+
let store_name = default_config_store_name();
70+
let config_key = default_config_key();
71+
get_settings_from_config_store(
72+
&crate::platform::SpinKvConfigStore,
73+
&store_name,
74+
&config_key,
75+
)
76+
}
77+
78+
/// Loads startup [`Settings`] from the embedded example config on non-Spin
79+
/// (native test) builds, where Spin host key-value functions are unavailable.
80+
#[cfg(not(all(feature = "spin", target_arch = "wasm32")))]
81+
fn load_startup_settings() -> Result<Settings, Report<TrustedServerError>> {
82+
Settings::from_toml(include_str!("../../../trusted-server.example.toml"))
5383
}
5484

5585
/// Build the application state from explicit settings.

crates/trusted-server-adapter-spin/src/platform.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,57 @@ impl PlatformConfigStore for NoopConfigStore {
6161
}
6262
}
6363

64+
/// Spin key-value-backed [`PlatformConfigStore`] used to load the app-config
65+
/// blob at startup.
66+
///
67+
/// `ts config push --adapter spin` writes the app-config blob into a Spin
68+
/// key-value store (label = the config store id, key = the blob key), so
69+
/// startup opens that store by name and reads the blob. This is deliberately
70+
/// distinct from [`ConfigStoreHandleAdapter`], which reads per-request
71+
/// request-signing config from Spin component *variables*: a multi-kilobyte
72+
/// blob does not fit Spin's flat variable namespace.
73+
#[cfg(all(feature = "spin", target_arch = "wasm32"))]
74+
pub(crate) struct SpinKvConfigStore;
75+
76+
#[cfg(all(feature = "spin", target_arch = "wasm32"))]
77+
impl PlatformConfigStore for SpinKvConfigStore {
78+
fn get(&self, store_name: &StoreName, key: &str) -> Result<String, Report<PlatformError>> {
79+
let label = store_name.as_ref();
80+
let store =
81+
futures::executor::block_on(spin_sdk::key_value::Store::open(label)).map_err(|e| {
82+
Report::new(PlatformError::ConfigStore).attach(format!(
83+
"failed to open Spin key-value store `{label}`: {e}"
84+
))
85+
})?;
86+
let bytes = futures::executor::block_on(store.get(key))
87+
.map_err(|e| {
88+
Report::new(PlatformError::ConfigStore).attach(format!(
89+
"Spin key-value lookup for `{key}` in `{label}` failed: {e}"
90+
))
91+
})?
92+
.ok_or_else(|| {
93+
Report::new(PlatformError::ConfigStore).attach(format!(
94+
"key `{key}` not found in Spin key-value store `{label}`"
95+
))
96+
})?;
97+
String::from_utf8(bytes).map_err(|e| {
98+
Report::new(PlatformError::ConfigStore).attach(format!(
99+
"Spin key-value value for `{key}` is not valid UTF-8: {e}"
100+
))
101+
})
102+
}
103+
104+
fn put(&self, _: &StoreId, _: &str, _: &str) -> Result<(), Report<PlatformError>> {
105+
Err(Report::new(PlatformError::ConfigStore)
106+
.attach("config store writes are not supported on Spin"))
107+
}
108+
109+
fn delete(&self, _: &StoreId, _: &str) -> Result<(), Report<PlatformError>> {
110+
Err(Report::new(PlatformError::ConfigStore)
111+
.attach("config store writes are not supported on Spin"))
112+
}
113+
}
114+
64115
#[cfg(not(all(feature = "spin", target_arch = "wasm32")))]
65116
struct NoopSecretStore;
66117

docs/guide/architecture.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ Native Axum dev/test adapter (native binary):
6666
Fermyon Spin adapter (`wasm32-wasip1` component):
6767

6868
- Production-capable deployment target for the Spin runtime
69-
- Platform services (config store, secret store, KV) backed by Spin component variables and the EdgeZero KV handle
69+
- Startup app-config blob loaded from a Spin key-value store (`app_config`, seeded by `ts config push --adapter spin`); per-request request-signing config and secrets read from Spin component variables; KV via the EdgeZero KV handle
7070
- Outbound HTTP via `spin_sdk::http::send` — no configurable per-request timeout (see rustdoc)
7171
- Single auction provider only; multi-provider fan-out requires the Fastly adapter
7272

edgezero.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,5 +101,5 @@ features = ["spin"]
101101

102102
[adapters.spin.commands]
103103
build = "cargo build --package trusted-server-adapter-spin --target wasm32-wasip1 --features spin --release"
104-
serve = "spin up --from crates/trusted-server-adapter-spin"
104+
serve = "spin up --from crates/trusted-server-adapter-spin --runtime-config-file crates/trusted-server-adapter-spin/runtime-config.toml"
105105
deploy = "spin deploy --from crates/trusted-server-adapter-spin"

0 commit comments

Comments
 (0)