You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add D7 config-store-only hard cutoff; fix review-5 findings
- D7: runtime app-config is config-store-only; NO runtime env vars. ts config
push reads env at push time and bakes resolved values into the blob. Stores
open by logical id (name == id); no runtime EDGEZERO__STORES__*__NAME read.
- Task 6: drop local_env_config entirely (D7) - open stores by logical id;
resolves the fastly::ConfigStore-has-no-iter and private-helper findings (R12)
- creative_store IS a Settings KV id (deprecated); include it, exclude
counter_store/opid_store (Fastly-adapter constants)
- Task 2/spec: tighten kind-aware KV inventory (ec_store, consent_store, creative_store)
- Task 4: run the actual core test name; Task 5: one filter per cargo test invocation
Expected (verified): **KV** ids = `ec.ec_store` (`ec_identity_store`, `settings.rs:452`) + `consent.consent_store` (`consent_config.rs:80`); **config** ids = `app_config` (`request_signing.config_store_id`) + the JWKS store (`JWKS_CONFIG_STORE_NAME`); **secret** ids = `secrets` (`request_signing.secret_store_id`), DataDome `ts_secrets`, the S3 secret store, `signing_keys` (`SIGNING_SECRET_STORE_NAME`) — versus `edgezero.toml` declaring only one id per kind. NOTE: `creative_store`/`counter_store`/`opid_store` appear only in `fastly.toml` as **platform**store declarations; they are not logical ids in `Settings`and are out of scope for D5 reconciliation. Confirm this during the run.
52
+
Expected (verified): **KV** ids = `ec.ec_store` (`ec_identity_store`, `settings.rs:452`), `consent.consent_store` (`consent_config.rs:80`), and `auction.creative_store` (`auction_config_types.rs:28`, default `"creative_store"`, **deprecated** — creatives are delivered inline); **config** ids = `app_config` (`request_signing.config_store_id`) + the JWKS store (`JWKS_CONFIG_STORE_NAME`); **secret** ids = `secrets` (`request_signing.secret_store_id`), DataDome `ts_secrets`, the S3 secret store, `signing_keys` (`SIGNING_SECRET_STORE_NAME`) — versus `edgezero.toml` declaring only one id per kind. NOTE: `counter_store` (`RATE_COUNTER_NAME` in the Fastly `rate_limiter.rs`) and `opid_store` are **Fastly-only**platform stores, not `Settings`logical ids — out of scope for D5. `creative_store`**is** a `Settings` id: declare it in `[stores.kv]` (deprecated) so strict lookup can't fail, and flag it for removal in a later phase.
-[ ]**Step 2: Re-type `get_settings_from_config_store`** to `(&ConfigStoreHandle, key: &str)`; in Fastly `load_settings_from_config_store()` open the EdgeZero `FastlyConfigStore` at boot (`ConfigStore::open`/adapter constructor) and wrap in a `ConfigStoreHandle`; in Axum `build_state()` open the EdgeZero Axum config store. The adapter-level boot wiring is exercised by each adapter's existing `build_state` test path (no new Viceroy test needed — the core test above covers the parse logic).
246
246
247
-
-[ ]**Step 3: Run to verify pass** (Fastly + Axum)
247
+
-[ ]**Step 3: Run to verify pass** (core test + adapter boot suites)
Then confirm the adapter boot paths still build/pass via their existing `build_state` coverage:
252
+
Run: `cargo test-fastly && cargo test-axum`
250
253
Expected: PASS.
251
254
252
255
-[ ]**Step 4: Commit**
@@ -275,7 +278,13 @@ These adapters use EdgeZero `dispatch_with_registries` (registries already inser
275
278
-`datadome_reads_secret_from_nondefault_secret_store` — a request to the DataDome integration route: seed the `SecretRegistry` with two ids (default + `ts_secrets`) and the DataDome server-side key under `ts_secrets`; assert the handler reads it (proves non-default **secret** id resolution).
276
279
-`first_party_proxy_reads_s3_secret` — `GET /first-party/proxy` for an S3-auth asset route: seed the S3 secret id; assert the SigV4 path obtains the secret (proves the S3 secret read).
-[ ]**Step 2: Build `RuntimeServices` via the composite** in each adapter's `build_runtime_services`, passing the whole request `ConfigRegistry`/`SecretRegistry` as the composite reader (Task 3) and keeping the existing writer.
281
290
@@ -303,28 +312,16 @@ EdgeZero's Fastly `dispatch_with_registries` and its registry builders are `pub(
303
312
- Test: `crates/trusted-server-adapter-fastly/src/registries.rs` (`#[cfg(test)]`) + a route test
-[ ]**Step 1: Build a local `EnvConfig` reader (EdgeZero's is private).** Fastly Compute has no `std::env`; EdgeZero reads `EDGEZERO__*` from a Fastly Config Store (`env_config_from_runtime_dictionary`), which is **private** in the pinned dep (R12). Write `local_env_config()` in `registries.rs` that opens the `edgezero_runtime_env` Fastly Config Store, iterates its entries, and calls `EnvConfig::from_vars(...)`. If R11/R12 resolves by exposing a public EdgeZero helper, delete this and call that instead.
315
+
- Consumes: `StoresMetadata` (from `Hooks::stores()`), EdgeZero `FastlyConfigStore`/`FastlyKvStore`/`FastlySecretStore` open primitives, `StoreRegistry::from_parts`.
316
+
- Produces: `build_config_registry(&StoresMetadata) -> ConfigRegistry` (+ `_secret_/_kv_` variants) that opens each declared store **by its logical id** (per **D7** hard cutoff — no runtime env/dictionary read; platform store name == logical id).
310
317
311
-
```rust
312
-
// registries.rs
313
-
fnlocal_env_config() ->EnvConfig {
314
-
// Mirror EdgeZero's runtime-dictionary reader: read the well-known
315
-
// Fastly Config Store into (key,value) pairs, then EnvConfig::from_vars.
-[ ]**Step 1: (D7) No runtime env reader.** Per D7 the runtime does **not** read `EDGEZERO__STORES__*__NAME` — stores are opened by **logical id**. This deletes the need for a Fastly runtime-dictionary `EnvConfig` reader (and sidesteps that `fastly::ConfigStore` has no `iter()` and EdgeZero's reader is private). If a deployment ever needs to remap a physical store name, that is handled at provisioning time, not here. No code in this step; it records the design constraint the builders follow.
322
319
323
-
-[ ]**Step 2: Write a failing builder test** — `build_config_registry` yields a registry whose `default()` resolves and whose declared non-default id (e.g. `jwks_store`) resolves; unknown id → `None`. Name: `build_config_registry_resolves_declared_ids`.
320
+
-[ ]**Step 2: Write a failing builder test** — `build_config_registry`opens each declared id by name and yields a registry whose `default()` resolves and whose declared non-default id (`jwks_store`) resolves; an id **not** in `StoresMetadata` is absent (`named("nope").is_none()`). Name: `build_config_registry_resolves_declared_ids`.
-[ ]**Step 3: Implement the three builders** in `registries.rs` (iterate `StoreMetadata.ids`, resolve platform name via `EnvConfig::store_name(kind, id)`, open the EdgeZero store, assemble `StoreRegistry::from_parts`), using `local_env_config()` from Step 1.
324
+
-[ ]**Step 3: Implement the three builders** in `registries.rs`: iterate `StoreMetadata.ids`, open the EdgeZero Fastly store **by the logical id** (`FastlyConfigStore`/`FastlyKvStore`/`FastlySecretStore` open primitive), and assemble `StoreRegistry::from_parts(by_id, default_id)`. No `EnvConfig`, no runtime dictionary.
328
325
329
326
-[ ]**Step 4: Insert registries in the oneshot block** — replace the lone `core_req.extensions_mut().insert(config_store)` at `main.rs:477` with inserts of `ConfigRegistry`/`SecretRegistry`/`KvRegistry` (built via Step 3), preserving the existing `client_info`/`device_signals` inserts.
**D7 — Runtime app-config is config-store-only; no runtime environment variables (hard cutoff).** At runtime, every adapter builds `Settings`**solely** from the config-store blob: open store → verify envelope → deserialize (+ secret walk) → validate. There is **no runtime environment-variable overlay** for app config on any adapter. All env-var influence on config content happens at **push time**: `ts config push` (which runs on a host that has the env vars) reads them, applies the AppConfig env overlay, and bakes the resolved values into the signed blob. Consequences:
99
+
- Delete `Settings::from_toml_and_env` and the `TRUSTED_SERVER__*` overlay entirely (Phase 2) — not merely unused, **forbidden** at runtime.
100
+
- The AppConfig loader's `env_overlay` is a **push-time-only** option; runtime never applies it.
101
+
-**Store-name binding follows the same spirit:** a logical store id resolves to the **platform store of the same name by default** (EdgeZero's `store_name` fallback), so the runtime does **not** need to read `EDGEZERO__STORES__*__NAME` env/dictionary to open stores. Adapters (incl. Fastly's custom path) open stores by **logical id**. If a deployment ever needs a different physical name, that is a **provisioning/manifest** concern resolved at deploy time, never a runtime env read. (This removes the need for a Fastly runtime-dictionary `EnvConfig` reader — see Phase 1 Task 6.)
102
+
103
+
---
104
+
98
105
## 4a. Prerequisites (must resolve before or during Phase 1/2)
99
106
100
107
These are not trusted-server refactors; they are EdgeZero-adapter capability gaps or up-front decisions that gate the phases.
@@ -131,7 +138,7 @@ Recommendation: **(a)** where the adapter env is available at boot (Cloudflare/S
131
138
**Changes:**
132
139
-**Reads (runtime + boot):** route `PlatformConfigStore`/`PlatformSecretStore`**reads** and the `RuntimeServices` config/secret fields through EdgeZero handles resolved from the per-request registries, matching KV — via a **composite store** (reads → EdgeZero, writes → existing management path; see D6-a). Also migrate the **boot-time** config load: Fastly `load_settings_from_config_store()` and Axum `build_state()` read `Settings` at boot through `&FastlyPlatformConfigStore` / `&AxumPlatformConfigStore`**before** request context exists, so those must move to a boot-time EdgeZero config read *before* the bespoke read impls are deleted. Migrate read consumers: `proxy.rs` (S3), `request_signing/{signing,rotation}.rs` (reads), `integrations/datadome/{protection,protection_scope}.rs`.
133
140
-**Writes (D6):**`KeyRotationManager` writes+deletes **config and secrets at request time** (`store_private_key`/`store_public_jwk`/`delete_key` for `/_ts/admin/keys/rotate` + deactivate/delete). EdgeZero `ConfigStore`/`SecretStore` are **read-only by design**. So `management_api.rs`**cannot be deleted in Phase 1 as originally written**. Resolve per D6 before touching it.
134
-
-**Store-id reconciliation (D5, expanded, kind-aware):** every runtime store id referenced by config must be declared in `edgezero.toml` under the **correct kind** — `[stores.kv]`, `[stores.config]`, or `[stores.secrets]` — or strict registry lookup returns `None`. Reconcile at least: **KV** — `ec.ec_store` (`ec_identity_store`), `consent.consent_store`; **config** — the app-config blob store, `request_signing.config_store_id` (`app_config` today), the JWKS/config-list store; **secrets** — `request_signing.secret_store_id` (`secrets` today), DataDome secret store (`ts_secrets`), the S3 secret store — plus all `trusted-server.example.toml` + integration/test fixtures. (`creative_store`/`counter_store`/`opid_store`in `fastly.toml` are platform store declarations, **not** logical ids referenced by `Settings`.)
141
+
-**Store-id reconciliation (D5, expanded, kind-aware):** every runtime store id referenced by config must be declared in `edgezero.toml` under the **correct kind** — `[stores.kv]`, `[stores.config]`, or `[stores.secrets]` — or strict registry lookup returns `None`. Reconcile at least: **KV** — `ec.ec_store` (`ec_identity_store`), `consent.consent_store`, `auction.creative_store` (deprecated — creatives are inline — but still a `Settings` field, so declare it to keep strict lookup safe); **config** — the app-config blob store, `request_signing.config_store_id` (`app_config` today), the JWKS/config-list store; **secrets** — `request_signing.secret_store_id` (`secrets` today), DataDome secret store (`ts_secrets`), the S3 secret store — plus all `trusted-server.example.toml` + integration/test fixtures. (`counter_store`/`opid_store`are **Fastly-adapter** constants (rate limiter / opid), **not**`Settings`logical ids, and are out of scope.)
135
142
-**Fastly registry injection (ties to P0-C):** Fastly's custom `oneshot` path (§1) currently inserts only a `ConfigStoreHandle`, not registries via `dispatch_with_registries`. EdgeZero's `dispatch_with_registries` and its registry builders are **`pub(crate)`** (verified in the pinned checkout), so trusted-server must build the registries **locally** (from `StoresMetadata` + `EnvConfig` + the EdgeZero Fastly store open primitives) and insert them into extensions before `oneshot` — or an EdgeZero public builder must be added upstream (**R11**).
136
143
- Delete the 4× per-adapter `platform.rs` config/secret **read** impls; adapters build registries from `[stores.*]` metadata (via `dispatch_with_registries` on Axum/Cloudflare/Spin, via the Fastly-specific injection above).
@@ -149,7 +156,7 @@ Recommendation: **(a)** where the adapter env is available at boot (Cloudflare/S
149
156
**Changes:**
150
157
- Derive `AppConfig` on the config root (interim: still `TrustedServerAppConfig` until Phase 3 collapses it onto `Settings`) so all adapters use the same store-load path.
151
158
-**Cloudflare** (`adapter-cloudflare/src/app.rs`) and **Spin** (`adapter-spin/src/app.rs`): replace startup config sourcing (Cloudflare's `TRUSTED_SERVER_CONFIG` env side-channel + the native `include_str!` fallback; Spin's baked example TOML) with a **boot-time config-store read** per **P-BOOT (§4a)**. `build_state()` obtains a config-store handle from the adapter env passed to `run_app` (option a) or defers to a lazy first-request cached load (option b). This is the load-bearing detail — settle the mechanism in the Phase 2 plan. Seed each platform's config store (`wrangler.toml` / `runtime-config.toml` / `fastly.toml` local blocks) with the pushed blob under the D5 store id/key.
152
-
- Delete `Settings::from_toml_and_env`, `ENVIRONMENT_VARIABLE_PREFIX/SEPARATOR`, and the `config`**dev-dependency**. Any remaining env overlay uses EdgeZero's `EDGEZERO__*` / AppConfig `<APP>__…` layers.
159
+
- Delete `Settings::from_toml_and_env`, `ENVIRONMENT_VARIABLE_PREFIX/SEPARATOR`, and the `config`**dev-dependency**. Per **D7 (hard cutoff)** there is **no runtime env overlay** — runtime reads the blob only. Env-var influence on config happens exclusively at **push time** via `ts config push` (AppConfig `env_overlay` applied there, then baked into the blob).
153
160
154
161
**Deletions:** both `include_str!` config paths, `from_toml_and_env`, `config` crate dep.
155
162
**Acceptance:** Cloudflare + Spin serve with store-loaded config (no baked TOML); `ts config push` blob is the single source on all four adapters; tests green.
| R7 | P0-C: upstream a header-preserving Fastly dispatch, or keep a permanent Fastly dispatch shim? | Decide with edgezero maintainer (§4a); gates the Fastly end-state and Phase 5. |
252
259
| R8 | P-BOOT: boot-time store handle (a) vs lazy cached first-request load (b), per adapter? | Phase 2 plan (§4a). |
| R12 | Fastly `EnvConfig` reader (`env_config_from_runtime_dictionary`) is **private** upstream; Fastly has no `std::env`. Build a local runtime-dictionary reader, or make R11's public helper a prerequisite. | Phase 1 plan Task 6. |
261
+
| R12 | Fastly `EnvConfig` reader is private / `fastly::ConfigStore` has no `iter()`. |**Resolved by D7** — runtime opens stores by logical id; no store-name env/dictionary read; no local `EnvConfig` reader needed. |
255
262
| R10 | D6: runtime write path for key rotation — keep write-capable admin abstraction (a), move to ops/CLI (b), or upstream an EdgeZero write API (c)? |**Blocks Phase 1 deletions.** Phase 1 plan locks to **(a)**; (b)/(c) → separate plan. |
256
-
| R11 | Should EdgeZero expose a **public** Fastly registry-builder helper (so trusted-server need not maintain local builders)? | Decide with edgezero maintainer; Phase 1 plan uses local builders (Task 6) meanwhile. |
263
+
| R11 | Should EdgeZero expose a **public** Fastly registry-builder helper? | Lower priority under D7 — local builders open by logical id and need only public store constructors. Decide with edgezero maintainer if convenient. |
257
264
| R2 |`StoreName` vs `StoreId` split — still needed after `management_api.rs` deletion? | Phase 1; drop if only the CLI provision path used it. |
258
265
| R3 | EC identity API + Fastly rate limiter are Fastly-only today | Out of scope here; note as a portability follow-up (not blocking). |
259
266
| R4 | Cloudflare/Spin boot-time secret-store access for D3 | Confirm in Phase 3 scoping. |
0 commit comments