From 5a5f377b2e3d061fa9a1c817c41f3bd2e59fb68a Mon Sep 17 00:00:00 2001 From: prk-Jr Date: Wed, 17 Jun 2026 12:24:14 +0530 Subject: [PATCH 01/15] Add edgezero extensible-cli repin finding and implementation plan --- .../plans/2026-06-17-edgezero-269-repin.md | 427 ++++++++++++++ ...edgezero-269-repin-breaking-api-finding.md | 557 ++++++++++++++++++ 2 files changed, 984 insertions(+) create mode 100644 docs/superpowers/plans/2026-06-17-edgezero-269-repin.md create mode 100644 docs/superpowers/specs/2026-06-16-edgezero-269-repin-breaking-api-finding.md diff --git a/docs/superpowers/plans/2026-06-17-edgezero-269-repin.md b/docs/superpowers/plans/2026-06-17-edgezero-269-repin.md new file mode 100644 index 00000000..43118c19 --- /dev/null +++ b/docs/superpowers/plans/2026-06-17-edgezero-269-repin.md @@ -0,0 +1,427 @@ +# EdgeZero #269 Repin 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:** Repin trusted-server's edgezero dependency from its current pin (`38198f9` on the PR14 base; `170b74b` is the stack's _original_ PR1–13 pin) to post-#269 and fix the only forced code break (`Body::into_bytes` → `Option`), keeping the bespoke `platform/` layer unchanged. + +**Architecture:** Mechanical dependency bump on a dedicated branch off `feature/edgezero-pr14-entry-point-dual-path` (never in-place on a reviewed PR), then propagate up the stack by merge. The "test" for this work is the compiler + full CI gate: RED = build errors at `Body` sinks, GREEN = full gate passes. No new abstractions; the A/B/C convergence (store registry, typed `AppConfig`, entry-point) is **out of scope** — separate follow-on plans. + +**Tech Stack:** Rust 2024, cargo, `wasm32-wasip1` (Fastly via Viceroy), edgezero git dep, `error-stack`. + +**Source spec:** [2026-06-16-edgezero-269-repin-breaking-api-finding.md](../specs/2026-06-16-edgezero-269-repin-breaking-api-finding.md) — §2 (sink list), §5 (steps), §8 (gate), §11 (stack/merge-up). + +--- + +## Scope & non-goals + +**In scope:** repin the 4 `edgezero-*` deps; fix the 18 `Body::into_bytes` sinks (8 production + 10 test); reconcile the integration-tests lockfile; drop the two never-compiled adapter deps; pass the full gate; merge up PR14→PR20. + +**Out of scope (separate plans):** store convergence onto edgezero `ConfigStore`/`SecretStore`/`StoreRegistry` (spec §6 B); typed `AppConfig` two-tier config (§6 C / Christian's CLI port); entry-point `run_app`/`app!` adoption (§6 C). Do **not** start these here. + +**Key constraints:** + +- `as_bytes` changed but has **no** trusted-server sink — only `into_bytes` needs edits (spec §2). +- All sinks are buffered (`Once`) bodies → fix with `.expect("should …")`, never `unwrap_or_default()`. +- Line numbers below are **PR14-base**; they shift per branch. **Re-derive the exact sink set from the compiler on each branch** — do not trust hardcoded lines after PR14. +- Pin to the #269 HEAD sha while #269 is open; **re-pin to edgezero `main` after #269 merges**. + +--- + +## File structure + +| File | Change | Responsibility | +| ------------------------------------------------------------- | ---------------------------------------------------- | --------------------------- | +| `Cargo.toml` | Modify lines 59–62 | The 4 `edgezero-*` git pins | +| `Cargo.lock` | Regenerated | Root lock | +| `crates/integration-tests/Cargo.lock` | Reconcile | Shared-dep lock (CI gate) | +| `crates/trusted-server-core/src/proxy.rs` | 5 sinks (38, 1550, 1665 prod; 2034, 2795, 2851 test) | proxy/asset body reads | +| `crates/trusted-server-core/src/publisher.rs` | 4 sinks (46 prod; 748, 1079, 1562 test) | publisher body reads | +| `crates/trusted-server-core/src/auction/endpoints.rs` | 1 sink (81 prod) | auction body read | +| `crates/trusted-server-core/src/auction/formats.rs` | 1 sink (444 test) | auction test helper | +| `crates/trusted-server-core/src/request_signing/endpoints.rs` | 4 sinks (103, 246, 365 prod; 464 test) | signing endpoint body reads | +| `crates/trusted-server-core/src/integrations/prebid.rs` | 1 sink (2067 test) | prebid test | +| `crates/trusted-server-core/src/integrations/testlight.rs` | 1 sink (461 test) | testlight test | + +`http_util.rs:456` (`enforce_max_body_size(bytes: &[u8], …)`) is **not** a sink — no edit. + +--- + +## Fix shapes (apply the matching one at each sink) + +```rust +// Shape A — value consumed directly (body_as_reader; let body = …into_bytes()) +let body = resp.into_body().into_bytes() + .expect("should have a buffered body"); + +// Shape B — chained .to_vec() (String::from_utf8(…into_bytes().to_vec())) +String::from_utf8( + resp.into_body().into_bytes() + .expect("should have a buffered body") + .to_vec(), +) + +// Shape C — bound, then borrowed into &[u8]/&Bytes (enforce_max_body_size(&b)/from_slice(&b)) +let b = req.into_body().into_bytes() + .expect("should have a buffered request body"); +enforce_max_body_size(&b, …)?; +serde_json::from_slice(&b)?; +``` + +`cargo fmt` will rewrap; write the one-liner and let it format. + +--- + +## Task 0: Create the dedicated branch off PR14 + +**Files:** none (git only) + +- [ ] **Step 1: Branch off PR14 (not in-place, not main)** + +```bash +git fetch origin +git checkout -b feature/edgezero-269-repin feature/edgezero-pr14-entry-point-dual-path +``` + +- [ ] **Step 2: Confirm base + capture the authoritative "from" pin** + +Run: `git log -1 --format='%s' && grep -m1 'edgezero-core' Cargo.toml` +Expected: PR14 tip; the dep line prints the **base pin = `rev = "38198f9…"`**. + +> Pin clarity: the Goal's `170b74b` is the _stack's original_ pin (PR1–13), **not +> this branch's base.** PR14's base is `38198f9` (spec §11). The **only** +> authoritative "from" value is whatever this grep prints — use that, not a +> hardcoded sha, if the branch has advanced. + +--- + +## Task 1: Repin edgezero to #269 + regenerate root lock + +**Files:** Modify `Cargo.toml:59-62`, regenerate `Cargo.lock` + +- [ ] **Step 1: Repin all 4 deps** + +Replace the base pin captured in Task 0 Step 2 (`rev = "38198f9…"` on the 4 +`edgezero-*` lines) with `rev = "2eeccc9748daba92b9adf6afe4df105e79269ae9"` +(#269 HEAD). (After #269 merges, use the edgezero `main` sha instead — see spec §9.) + +- [ ] **Step 2: Resolve the lock FIRST — separate resolution failure from compile-RED** + +Run: `cargo generate-lockfile` +Expected: lock resolves with no error. **If this fails** (MSRV / feature +unification / `spin-sdk` graph — the spec's #1 transitive risk, §5.1/§10), STOP +and surface it: that is a _resolution_ break, not the expected `Body` compile-RED, +and must be triaged before continuing. + +- [ ] **Step 3: Capture the RED baseline (build only after the lock resolves)** + +Run: `cargo build --workspace --all-targets 2>/tmp/ez_red.log; grep -cE '^error' /tmp/ez_red.log` +Expected: ~27 errors (`E0308`/`E0599`/`E0624`). This is the RED state. (Log goes to +`/tmp` — keep build artifacts out of the repo tree.) + +- [ ] **Step 4: Sanity — every error is at a known `Body` sink file, nothing else** + +Filter by **location**, not error-kind (an unexpected break could share a kind): + +Run: + +```bash +grep -A1 '^error' /tmp/ez_red.log | grep -- '-->' \ + | grep -vE 'trusted-server-core/src/(proxy|publisher|auction/(endpoints|formats)|request_signing/endpoints|integrations/(prebid|testlight)|http_util)\.rs' \ + && echo "UNEXPECTED — stop & investigate" || echo OK +``` + +Expected: `OK` (every error points into a known sink file from §2). Any other +location ⇒ unexpected transitive break; STOP and surface it (spec §5). + +- [ ] **Step 5: Commit the repin (still RED — that's expected)** + +```bash +git add Cargo.toml Cargo.lock +git commit -m "Repin edgezero to the extensible-cli branch (stackpop/edgezero PR 269)" +``` + +--- + +## Task 2: Fix production sinks — `proxy.rs` + +**Files:** Modify `crates/trusted-server-core/src/proxy.rs` (sinks 38, 1550, 1665) + +- [ ] **Step 1: Confirm current errors (RED)** + +Run: `cargo check --workspace 2>&1 | grep 'proxy.rs'` +Expected: errors **within `proxy.rs`** (exact line numbers vary per branch — re-derive +from this output; do not trust the §2 PR14 numbers after PR14). Expect a `body_as_reader` +`Cursor::new(body.into_bytes())` site plus two POST-body `let body_bytes = req.into_body().into_bytes();` sites. + +- [ ] **Step 2: Fix `body_as_reader` (Shape A)** + +`Cursor::new(body.into_bytes())` → `Cursor::new(body.into_bytes().expect("should have a buffered body"))` + +- [ ] **Step 3: Fix the two POST-body bindings (Shape C)** + +`let body_bytes = req.into_body().into_bytes();` → +`let body_bytes = req.into_body().into_bytes().expect("should have a buffered request body");` + +> `replace_all` only if the two lines are byte-identical (same indentation). **Read +> each site first**; if the replace count ≠ 2, fall back to per-site edits. Both are +> production code (not test). + +- [ ] **Step 4: Verify proxy.rs errors cleared** + +Run: `cargo check --workspace 2>&1 | grep -c 'proxy.rs' ; echo done` +Expected: `0` proxy.rs errors (other files may still error — fine). + +--- + +## Task 3: Fix production sinks — `publisher.rs` + `auction/endpoints.rs` + +**Files:** Modify `publisher.rs` (line 46), `auction/endpoints.rs` (line 81) + +- [ ] **Step 1: Fix `publisher.rs:46` `body_as_reader` (Shape A)** + +`std::io::Cursor::new(body.into_bytes())` → `std::io::Cursor::new(body.into_bytes().expect("should have a buffered body"))` + +- [ ] **Step 2: Fix `auction/endpoints.rs:81` (Shape C)** + +`let body_bytes = body.into_bytes();` → `let body_bytes = body.into_bytes().expect("should have a buffered request body");` + +- [ ] **Step 3: Verify both cleared** + +Run: `cargo check --workspace 2>&1 | grep -cE 'publisher.rs|auction/endpoints.rs'` +Expected: `0`. + +--- + +## Task 4: Fix production sinks — `request_signing/endpoints.rs` + +**Files:** Modify `request_signing/endpoints.rs` (lines 103, 246, 365) + +- [ ] **Step 1: Fix all three `req.into_body()` bindings (Shape C)** + +`replace_all` of ` let body = req.into_body().into_bytes();` → + +```rust + let body = req + .into_body() + .into_bytes() + .expect("should have a buffered request body"); +``` + +> Expect 3 identical occurrences, all production. **Read the sites first**; if the +> replace count ≠ 3 (indentation differs), fall back to per-site edits. The +> `json_response(body: String)` site (`String::into_bytes`) is a false positive — +> **do not touch** (verify by checking the receiver is `String`, not `Body`). + +- [ ] **Step 2: Verify lib/bin build is GREEN** + +Run: `cargo build --workspace` +Expected: **success** (all 8 production sinks fixed). Tests still red — next. + +- [ ] **Step 3: Commit production fixes** + +```bash +git add crates/trusted-server-core/src +git commit -m "Adapt Body::into_bytes Option return at production sinks" +``` + +--- + +## Task 5: Fix test sinks + +**Files:** Modify `proxy.rs` (2034, 2795, 2851), `publisher.rs` (748, 1079, 1562), `auction/formats.rs` (444), `request_signing/endpoints.rs` (464), `integrations/prebid.rs` (2067), `integrations/testlight.rs` (461) + +- [ ] **Step 1: Confirm test errors (RED)** + +Run: `cargo build --workspace --all-targets 2>&1 | grep -E '^error' | wc -l` +Expected: ~12 remaining errors, all in test code at the lines above. + +- [ ] **Step 2: Apply the matching shape at each test sink** + +- `String::from_utf8(… .into_bytes().to_vec())` → Shape B (`.expect("should have a buffered body")` before `.to_vec()`): `proxy.rs:2034`, `publisher.rs:748`, `prebid.rs:2067`, `request_signing/endpoints.rs:464`. +- `serde_json::from_slice(&… .into_bytes())` → Shape C (bind, `.expect`, borrow): `auction/formats.rs:444`, `testlight.rs:461`. +- `let x = … .into_bytes();` → Shape A (`.expect`): `proxy.rs:2795`, `proxy.rs:2851`, `publisher.rs:1079`, `publisher.rs:1562`. + +(The `request_signing/endpoints.rs:452` test helper is `str::as_bytes` — false positive, **do not touch**.) + +- [ ] **Step 3: Verify `--all-targets` is GREEN** + +Run: `cargo build --workspace --all-targets` +Expected: **success** — all 18 sinks fixed. + +- [ ] **Step 4: Commit test fixes** + +```bash +git add crates/trusted-server-core/src +git commit -m "Adapt Body::into_bytes Option return in tests" +``` + +--- + +## Task 6: Drop never-compiled adapter deps + +**Files:** Modify `Cargo.toml` (remove `edgezero-adapter-axum`, `edgezero-adapter-cloudflare` from `[workspace.dependencies]`) + +- [ ] **Step 1: Confirm they are absent from the graph** + +Run: `cargo tree -i edgezero-adapter-axum; cargo tree -i edgezero-adapter-cloudflare` +Expected: both → "did not match any packages" (no member uses them — spec §1). + +- [ ] **Step 2: Remove the two lines from `Cargo.toml` `[workspace.dependencies]`** + +Delete the `edgezero-adapter-axum = …` and `edgezero-adapter-cloudflare = …` lines (keep `edgezero-adapter-fastly` and `edgezero-core`). + +- [ ] **Step 3: Verify still builds** + +Run: `cargo build --workspace --all-targets` +Expected: success (nothing referenced them). + +- [ ] **Step 4: Commit** + +```bash +git add Cargo.toml Cargo.lock +git commit -m "Drop unused edgezero axum/cloudflare workspace deps" +``` + +> If a future `trusted-server-adapter-{axum,cloudflare}` consumer lands, re-add then. Skip this task if the team prefers to keep them pinned for symmetry — note the decision. + +--- + +## Task 7: Reconcile the integration-tests lockfile + +> **Ordering matters.** This runs _after_ all root dependency changes (repin Task 1 +> +> - drop-adapters Task 6) and _after_ `trusted-server-core` is GREEN (Tasks 2–5). +> `crates/integration-tests` is a **separate workspace** that path-deps +> `trusted-server-core` (`Cargo.toml:13`); building it any earlier fails on the +> Body errors, not lock drift — confounding the check. + +**Files:** `crates/integration-tests/Cargo.lock` (and `crates/openrtb-codegen/Cargo.lock` if it drifts) + +- [ ] **Step 1: Resolve + build the integration-tests workspace** + +Run: `( cd crates/integration-tests && cargo generate-lockfile && cargo build --workspace 2>&1 | tail -20 )` +Expected: lock resolves and it builds. Because core is now green, any failure here +is a _real_ signal — shared-dep drift or a genuine break — not the Body RED. + +- [ ] **Step 2: If shared-dep drift, reconcile with targeted updates only** + +For each mismatched shared dep (e.g. `bytes`, `http`, `serde`): +Run: `( cd crates/integration-tests && cargo update -p --precise )` +**Never** a blanket `cargo update`. (Project CI gate: shared direct deps must match +root.) Repeat in `crates/openrtb-codegen` if it drifted. + +- [ ] **Step 3: Verify** + +Run: `( cd crates/integration-tests && cargo build --workspace )` +Expected: success. + +- [ ] **Step 4: Commit if changed** + +```bash +git add crates/integration-tests/Cargo.lock crates/openrtb-codegen/Cargo.lock +git commit -m "Reconcile integration-tests lockfile after edgezero repin" || echo "nothing to commit" +``` + +--- + +## Task 8: Full verification gate + +**Files:** none (verification only) + +- [ ] **Step 1: Compile gate (host + all-targets)** + +Run: `cargo build --workspace --all-targets` +Expected: success. + +- [ ] **Step 2: wasm32-wasip1 (Fastly deploy target)** + +Run: `cargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1` +Expected: success. (First leg not yet verified at spec-freeze — this is the gate that proves it.) + +- [ ] **Step 3: Tests** + +Run: `cargo test --workspace` +Expected: pass. Watch for behavioral diffs in body-handling tests (they exercise the `.expect()` paths). + +- [ ] **Step 4: Clippy + fmt** + +Run: `cargo clippy --workspace --all-targets --all-features -- -D warnings && cargo fmt --all -- --check` +Expected: clean. + +- [ ] **Step 5: integration-tests + JS gates (per CLAUDE.md CI)** + +Lock already reconciled (Task 7) — this just runs the suites. +Run: `( cd crates/integration-tests && cargo test --workspace )` and `( cd crates/js/lib && npx vitest run )` +Expected: pass (JS untouched — sanity only). + +- [ ] **Step 6: Commit any fmt fixups** + +```bash +git add crates Cargo.toml Cargo.lock && git commit -m "Verification gate fixups" || echo "nothing to commit" +``` + +(Scope the add — never `git add -A`, which would sweep stray build logs into the commit.) + +--- + +## Task 9: Open the PR (PR14-based dedicated branch) + +**Files:** none + +- [ ] **Step 1: Push + open PR targeting the PR14 branch (or wherever the stack lands)** + +```bash +git push -u origin feature/edgezero-269-repin +gh pr create --base feature/edgezero-pr14-entry-point-dual-path \ + --title "Repin edgezero to #269 and adapt Body::into_bytes Option return" \ + --body "See docs/superpowers/specs/2026-06-16-edgezero-269-repin-breaking-api-finding.md" +``` + +> Do not push or open the PR until the user approves (per project git rules). Confirm the base branch with the team — the stack tip may have advanced. + +--- + +## Task 10: Propagate up the stack (merge, not rebase) + +**Files:** none (per-branch merge + gate) + +> **Approval gate:** merging up mutates 6 review branches. Do **not** run this task +> (or any `git push`) until the user approves — same rule as Task 9. + +For each branch PR15 → PR16 → PR17 → PR18 → PR19 → PR20, in order: + +- [ ] **Step 1: Merge the repin forward** + +```bash +git checkout feature/edgezero-pr15-remove-fastly-core +git merge feature/edgezero-269-repin # merge, not rebase (team preference) +``` + +- [ ] **Step 2: Re-derive sinks from the compiler (line numbers/sink set shift per layer)** + +Run: `cargo build --workspace --all-targets 2>&1 | grep -E 'into_bytes|Body'` +Resolve any new/moved sinks with the §2 fix shapes. PR15 (remove-fastly-core) and PR16+ move/delete these files — expect manual conflict resolution, not clean fast-forward. + +- [ ] **Step 3: Run the full gate (Task 8) on this branch** + +Expected: green before moving to the next branch up. + +- [ ] **Step 4: Commit the merge resolution (if any)** + +```bash +git add -A && git commit --no-edit || echo "nothing to commit (clean fast-forward)" +``` + +Repeat for each branch up to PR20. + +--- + +## Follow-on plans (NOT this plan) + +Per spec §6/§9, after the repin lands and #269 merges to edgezero `main`: + +1. **Re-pin to `main`** (one-line dep change + gate). +2. **Store convergence** (spec §6 B): map `PlatformConfigStore`/`SecretStore` reads onto edgezero `ConfigStore`/`SecretStore`/`StoreRegistry` + thin write-CRUD extension. +3. **Typed `AppConfig` (two-tier)** + **CLI port** (spec §6 C / §7) — Christian. Shared contract = the config struct + `[stores.config]` id; agree before either starts. + +Each gets its own spec → plan cycle. diff --git a/docs/superpowers/specs/2026-06-16-edgezero-269-repin-breaking-api-finding.md b/docs/superpowers/specs/2026-06-16-edgezero-269-repin-breaking-api-finding.md new file mode 100644 index 00000000..35b6a089 --- /dev/null +++ b/docs/superpowers/specs/2026-06-16-edgezero-269-repin-breaking-api-finding.md @@ -0,0 +1,557 @@ +# Finding: edgezero #269 repin — breaking-API surface for trusted-server + +- **Date:** 2026-06-16 (build-verified 2026-06-17, §10) +- **Author:** Prakash (HTTP port); Christian owns the CLI port (see §7) +- **Upstream:** [stackpop/edgezero#269](https://github.com/stackpop/edgezero/pull/269) + "EdgeZero CLI Extensions" — head `feature/extensible-cli`, base `main`, + **OPEN / unmerged** as of 2026-06-16. Sibling adaptation precedent: + [stackpop/mocktioneer#110](https://github.com/stackpop/mocktioneer/pull/110) + (design + plan only, docs-only). +- **Companion doc:** extends + [2026-03-19-edgezero-migration-design.md](./2026-03-19-edgezero-migration-design.md) + (the original Fastly→EdgeZero migration, PRs 1–17; PR13 merged, branch now on + PR19 canary cutover). +- **Method:** diffed the exact pin `170b74b` (current) against + `feature/extensible-cli` HEAD (`git diff 170b74b..HEAD`, 257 commits, + +28,969 / −5,754 across 143 files), then cross-referenced every changed + public symbol against trusted-server's actual consumption sites. All + signatures below are quoted verbatim from the two refs; call sites are + `file:line` in this repo. + +--- + +## 0. TL;DR + +1. **trusted-server currently pins edgezero at `170b74b`** (March 2026, "unified + key-value store abstraction #165"). `feature/extensible-cli` is **257 commits + ahead** of that exact commit — and `170b74b` is a clean ancestor, so a repin + to post-#269 swallows the **entire** delta, not just #269's own commits. + +2. **Almost none of #269's headline breaks reach trusted-server.** The original + migration deliberately wrapped edgezero behind trusted-server's own + `platform/` trait layer (`RuntimeServices`, `PlatformConfigStore`, + `PlatformSecretStore`, `PlatformKvStore`, `PlatformHttpClient`). As a result + trusted-server uses **none** of: `run_app`, the `app!` macro, + `RequestContext`, edgezero extractors, typed `AppConfig`, or `[stores.*]` + manifest tables. Every one of those is where #269's breakage lives. + +3. **The actual code-level break is a single method:** + `edgezero_core::body::Body::into_bytes()` now returns `Option` instead + of panicking. (`as_bytes()` changed the same way but has **no** trusted-server + call site — §2.) **Compiler-enumerated** (not rg-guessed): **18 sink bindings + — 8 production + 10 test-only** (§2/§10). Mechanical fix. + +4. **`KvError` going `#[non_exhaustive]` + two new variants does _not_ break us** + — trusted-server only _constructs_ `KvError::Unavailable` and never + exhaustively matches the enum. + +5. **Strategic question for the HTTP port:** #269 matures edgezero's _own_ + first-class multi-store registry, async `ConfigStore`/`SecretStore`, + `Config`/`Secrets`/`Kv` extractors, and typed `AppConfig`. These now overlap + heavily with trusted-server's bespoke `platform/` layer. The HTTP port can be + a **minimal repin** (keep the bespoke layer) or a **convergence** onto + edgezero's surface. See §6. + +6. **The stack already walks edgezero forward** — pins are _not_ frozen at + `170b74b`: PR1–13 = `170b74b`, PR14–18 = `38198f9`, PR19–20 = `ce6bcf7`. The + #269 repin is the next step of a bump the team already does. PR14 is where + trusted-server _starts_ consuming edgezero's high-level surface + (`RequestContext`/`EdgeError`/middleware/router) — yet a real build (§10) + proves even that base breaks on **nothing but `Body`**. See §11. + +7. **Plan (agreed):** do the upgrade on a **dedicated branch off PR14** (not on + main, not in-place on any reviewed PR), then **merge up** the stack; **re-pin + to edgezero `main` after #269 merges**. Full-adaptation roadmap (store + convergence + typed config + entry-point) is a _separate, optional_ track — + **not** forced by the repin (§11). + +--- + +## 1. What trusted-server actually consumes from edgezero + +Verified by `rg` across `crates/`. The dependency is a **thin, low-level slice** +of `edgezero-core` plus one type from `edgezero-adapter-fastly`. + +| edgezero symbol | trusted-server usage | Reaches #269 break? | +| ---------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | +| `edgezero_core::body::Body` (alias `EdgeBody`) | pervasive — bodies on every request/response, integrations, publisher, auction | **YES** (`into_bytes` → `Option`; `as_bytes` changed too but has **no** TS sink) | +| `edgezero_core::http::{Request, Response, request_builder, response_builder, HeaderValue, …}` | request/response construction in `platform.rs`, `proxy.rs`, `http_util.rs`, tests | No (stable; alias names unchanged) | +| `edgezero_core::key_value_store::{KvStore (as `PlatformKvStore`), KvError, KvHandle, KvPage, NoopKvStore}` | KV trait impls, EC identity graph, `UnavailableKvStore` stub | No (trait sigs unchanged; `KvError` change is inert for us — §3.2) | +| `edgezero_adapter_fastly::key_value_store::FastlyKvStore` | `platform.rs:13` — only symbol used from the fastly adapter | No (`open()` sig unchanged) | +| `edgezero-adapter-axum`, `edgezero-adapter-cloudflare` (workspace deps) | **declared in root `[workspace.dependencies]` only; no member crate references them — `cargo tree -i edgezero-adapter-axum` / `-cloudflare` return "did not match any packages"** | No (absent from the dependency graph — not compiled at all; see §9 Q4 → drop) | +| `edgezero-adapter-spin` | **not a dependency** — `trusted-server-adapter-spin` is an in-repo stub | No (edgezero's Spin SDK6/wasip2 churn never reaches us) | + +**Not used at all** (and therefore immune to #269): `run_app`, `app!`, +`RequestContext`, `FromRequest`/extractors, `EdgeError`, `IntoResponse`, +`ProxyClient`/edgezero `proxy`, typed `AppConfig`, manifest `[stores.*]` / +`[adapters.*]` tables. trusted-server's manifest is `trusted-server.toml` (a +bespoke `Settings` struct in `settings.rs`), **not** an edgezero `edgezero.toml`. + +--- + +## 2. The one break that reaches trusted-server: `Body` → `Option` + +`crates/edgezero-core/src/body.rs`. The `Body` enum shape is **unchanged** — +`Body::Once(Bytes)` / `Body::Stream(LocalBoxStream<…>)` — so trusted-server's +pattern matches in `platform.rs` survive. Two accessor return types changed: + +| Item | BASE (`170b74b`) | HEAD (`feature/extensible-cli`) | Break | +| ------------------ | -------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ----------- | +| `Body::as_bytes` | `pub fn as_bytes(&self) -> &[u8]` (panics on `Stream`) `body.rs:48` | `pub fn as_bytes(&self) -> Option<&[u8]>` (`None` on `Stream`) `body.rs:24` | return type | +| `Body::into_bytes` | `pub fn into_bytes(self) -> Bytes` (panics on `Stream`) `body.rs:55` | `pub fn into_bytes(self) -> Option` (`None` on `Stream`) `body.rs:62` | return type | + +Everything else on `Body` is intact: `empty()`, `from_bytes()`, `from_stream()`, +`into_stream()`, `is_stream()`, `text()`, `json()`, `to_json()`, and `stream()` +(the earlier subagent claim that `stream()` was removed is **wrong** — it moved +in source order only). `From` impls unchanged. + +**Behavior** (not just signatures) verified for the accessor neighbours: `Body::to_json` +is byte-identical BASE↔HEAD and matches on the `Once`/`Stream` enum directly — +it never calls `as_bytes`, so the `Option`-return change cannot leak into JSON +deserialization. `text()`/`json()`/`to_json()` are unused in trusted-server +regardless. + +### Affected call sites — compiler-enumerated (authoritative) + +> **Source of truth = the compiler, not `rg`.** An earlier hand-built `rg` list +> was wrong (missed production sinks `proxy.rs:38` and `auction/endpoints.rs:81`; +> mis-tagged tests as production). The list below is the exhaustive set from +> `cargo build --workspace --all-targets` on the repinned spike (§10): **27 +> compiler errors collapsing to 18 distinct `into_bytes()` sink bindings.** All +> are `Body` (`EdgeBody`); **no `as_bytes` site exists** in trusted-server. + +Line numbers are **PR14-base**; they shift per branch as the stack rewrites these +files — re-derive from the compiler on whatever branch you repin (the §8 gate does +this). One binding often produces several errors (`.len()`, `.to_vec()`, +`from_slice(&…)`, `from_utf8(&…)` on the now-`Option`). + +**Production (8) — fail plain `cargo build` (lib + bin):** + +| Binding site (PR14) | Shape | +| ------------------------------------ | ------------------------------------------------------------------- | +| `proxy.rs:38` (`body_as_reader`) | `Cursor::new(body.into_bytes())` | +| `publisher.rs:46` (`body_as_reader`) | `Cursor::new(body.into_bytes())` | +| `auction/endpoints.rs:81` | `let b = body.into_bytes(); b.len(); from_slice(&b)` | +| `proxy.rs:1550` | `let b = req.into_body().into_bytes(); enforce(&b); from_utf8(&b)` | +| `proxy.rs:1665` | same shape (rebuild path) | +| `request_signing/endpoints.rs:103` | `let b = req.into_body().into_bytes(); enforce(&b); from_slice(&b)` | +| `request_signing/endpoints.rs:246` | same (rotate; also `b.is_empty()`) | +| `request_signing/endpoints.rs:365` | same (deactivate) | + +**Test-only (10) — fail only `cargo test` / `--all-targets`, invisible to plain +`cargo build`:** + +`auction/formats.rs:444`, `integrations/prebid.rs:2067`, +`integrations/testlight.rs:461`, `proxy.rs:2034`, `proxy.rs:2795`, +`proxy.rs:2851`, `publisher.rs:748`, `publisher.rs:1079`, `publisher.rs:1562`, +`request_signing/endpoints.rs:464`. + +**NOT a sink:** `http_util.rs:456` appears in errors as the _expected_ side — it's +the `enforce_max_body_size(bytes: &[u8], …)` signature. No edit; `&Bytes` derefs +to `&[u8]` once the caller unwraps. + +**False positives — leave untouched** (receiver is `str`/`String`/`FromUtf8Error`, +not `Body`; confirmed by source): +`http_util.rs:286,320` (`str::as_bytes`), `request_signing/endpoints.rs:23` +(`String::into_bytes`), `request_signing/endpoints.rs:452` (test, `str::as_bytes`), +`sourcepoint.rs:571` / `datadome.rs:323` (`rewrite_script_content() -> String`), +`sourcepoint.rs:822` (`FromUtf8Error::into_bytes`). + +### Fix — three distinct shapes + +All sinks are buffered (`Once`) bodies, so use an explicit `.expect()` with a +`should`-style message (per CLAUDE.md), **not** `unwrap_or_default()`. If a future +sink is genuinely streaming, branch on `None` / use `into_stream()`. + +```rust +// Shape A — value consumed directly (e.g. proxy.rs:38, publisher.rs:46) +let body = resp.into_body().into_bytes() + .expect("should have a buffered body"); + +// Shape B — chained .to_vec() (e.g. prebid.rs:2067, proxy.rs:2034) +String::from_utf8( + resp.into_body().into_bytes() + .expect("should have a buffered body") + .to_vec(), +) + +// Shape C — bound, then borrowed into &[u8] / &Bytes (e.g. proxy.rs:1550, +// auction/endpoints.rs:81, request_signing/endpoints.rs:*) +let b = req.into_body().into_bytes() + .expect("should have a buffered request body"); +enforce_max_body_size(&b, …)?; // &Bytes → &[u8] +serde_json::from_slice(&b)?; // borrow the unwrapped Bytes +``` + +No signature changes propagate to callers (the locals are consumed in place). + +--- + +## 3. Low-level surface that changed upstream but is INERT for trusted-server + +These changed across the 257-commit delta but do **not** break our build, +verified against actual usage. Documented so the repin reviewer doesn't chase +ghosts. + +### 3.1 `KvStore` trait — reordered, not re-signed + +`key_value_store.rs`. All methods keep identical signatures +(`get_bytes`/`put_bytes`/`put_bytes_with_ttl`/`delete`/`list_keys_page`/`exists`, +all `async`, same params/returns). Only source ordering changed (clippy +`arbitrary_source_item_ordering`). trusted-server's `UnavailableKvStore` / +`NoopKvStore` impls and `KvIdentityGraph` calls are unaffected. + +### 3.2 `KvError` — `#[non_exhaustive]` + new variants (inert here) + +| | BASE | HEAD | +| -------- | ------------------------------------ | --------------------------------------------------------------------------- | +| attr | (none) | `#[non_exhaustive]` `key_value_store.rs:302` | +| variants | `NotFound { key }`, `Unavailable`, … | adds `LimitExceeded { message }` `:311`, `Unsupported { operation }` `:328` | + +Inert for us: trusted-server only **constructs** `KvError::Unavailable` (a unit +variant — still constructible downstream under enum-level `#[non_exhaustive]`) in +`platform/kv.rs`, and never writes an exhaustive `match` on `KvError`. No catch-arm +needed. + +### 3.3 `KvPage`, `KvHandle`, `NoopKvStore`, `FastlyKvStore::open`, http builders + +All present and signature-stable: + +- `KvPage` — same fields (`keys`, `cursor`/etc.), alphabetized only. +- `KvHandle` `key_value_store.rs:354`, `NoopKvStore` `:818` — exist. +- `edgezero_core::http::request_builder()` / `response_builder()` — same + signatures; `RequestBuilder`/`ResponseBuilder` are still exported alias names + (now aliasing `HttpRequestBuilder`/`HttpResponseBuilder` internally, transparent + to us); `Request`/`Response`/`Method`/`StatusCode`/`HeaderValue`/… aliases + unchanged. +- `edgezero_adapter_fastly::key_value_store::FastlyKvStore::open(name: &str) -> +Result` — unchanged. + +--- + +## 4. Full #269 breaking-API catalog (does NOT reach trusted-server today) + +Recorded for completeness — these are the framework-level breaks that bite +_consumers who use edgezero's high-level surface_ (e.g. mocktioneer). They matter +to us only **if** the HTTP port chooses convergence (§6) or when Christian's CLI +port (§7) adopts typed config. Grouped by subsystem. + +### 4.1 Adapter entrypoints — `run_app` dropped the manifest arg + +| Adapter | BASE | HEAD | +| ---------- | -------------------------------------------------------------------- | -------------------------------------------------- | +| axum | `pub fn run_app(manifest_src: &str) -> anyhow::Result<()>` | `pub fn run_app() -> anyhow::Result<()>` | +| cloudflare | `run_app(manifest_src: &str, req, env, ctx)` | `run_app(req, env, ctx)` | +| fastly | `run_app(manifest_src: &str, req)` | `run_app(req)` | + +Manifest/store config now flows from `A::stores()` (macro-baked) + `EDGEZERO__*` +env vars instead of an `include_str!` manifest string. **Not used by us** (we have +a manual `fn main()` event loop, not `run_app`). + +### 4.2 `Hooks::stores()` + `StoresMetadata` + +New `fn stores() -> StoresMetadata` on the `Hooks` trait (default impl returns +empty). The `app!` macro auto-emits it from `[stores.*]`. **Not used by us** (no +`app!`, no `Hooks` impl). + +### 4.3 Manifest `[stores.*]` hard rewrite + +Old per-adapter shape is now a **hard load error**: + +```toml +# BASE (now rejected) +[stores.kv] +name = "MY_KV" +[stores.kv.adapters.cloudflare] +name = "CF_BINDING" + +# HEAD (portable; names move to env) +[stores.kv] +ids = ["default"] +default = "default" +``` + +`[adapters..stores.*]` and unknown `[adapters..*]` subtables now +fail `manifest.validate()`. `Manifest::kv_store_name()` removed → runtime +`EnvConfig::store_name(kind, id)` (`EDGEZERO__STORES______NAME`). +**Not used by us** (no edgezero manifest). + +### 4.4 Multi-store registry + async `ConfigStore`/`SecretStore` + +New `store_registry.rs`: `StoreRegistry` with `default()`/`named(id)`, aliases +`KvRegistry`/`ConfigRegistry`/`SecretRegistry`, and `BoundSecretStore` (binds +platform store name per logical id). New async read traits +`ConfigStore::get(&self, key) -> Result, ConfigStoreError>` and +`SecretStore::get_bytes(&self, store_name, key) -> Result, …>`. +**Overlaps directly with our `PlatformConfigStore`/`PlatformSecretStore`** — see +§6. Not consumed today. + +### 4.5 `RequestContext` store accessors + +`kv_handle()` removed; replaced by `kv_store(id)`/`kv_store_default()` (+ config +& secret variants) returning `Option`. **Not used by us** (no +`RequestContext`). + +### 4.6 Extractor overhaul + +`Kv(KvHandle)` → `Kv(KvRegistry)` with `.default()`/`.named(id)`; new +`Config`/`Secrets` extractors. **Not used by us.** + +### 4.7 `#[derive(AppConfig)]` + `#[secret]` (entirely new) + +New derive macro (`edgezero-macros/src/app_config.rs`) emitting `AppConfigMeta` +with `SECRET_FIELDS`. `#[secret]` / `#[secret(store_ref)]` only on scalar +`String` fields; rejects `Option`, `Cow`, non-scalars, `serde(rename)`, +container `rename_all`, duplicate/`=`/unknown-arg forms (compile-fail UI tests). +Pairs with CLI `config validate`/`config push`. **This is Christian's CLI-port +territory** (§7); net-new adoption, not a break. + +### 4.8 `EdgeError` / `IntoResponse` / `ProxyResponse` + +`EdgeError` now `#[non_exhaustive]`, gains `NotImplemented`, `source()`→`inner()`. +`IntoResponse::into_response` now returns `Result`. +`ProxyResponse::into_response` returns `Result`. **None used by us** (we use +`error_stack::Report` and never touch `EdgeError`/edgezero +`IntoResponse`/edgezero `proxy`). + +### 4.9 CLI surface (new) — inventory only + +New `edgezero-cli` commands: `auth` (login/logout/status), `provision`, +`config validate`, `config push`, `demo` (replaces `dev`). Generated `-cli` +crate per app. Typed entrypoints a consumer crate calls: +`run_config_validate_typed::()`, `run_config_push_typed::()`, plus +`run_{auth,build,deploy,provision,serve}`. **Christian's port** (§7). + +### 4.10 Spin adapter — SDK 6.0 / wasip2 + +edgezero's Spin adapter moved to `spin-sdk ~6.0`, `wasm32-wasip1`→`wasip2`, +`#[http_component]`→`#[http_service]`, `IncomingRequest`→`Request`, async stores. +**Does not reach us** — trusted-server does not depend on `edgezero-adapter-spin`; +our `trusted-server-adapter-spin` is an in-repo stub. + +--- + +## 5. Net repin work for the HTTP port (minimal path) + +If the HTTP port is a **straight repin** (keep the bespoke `platform/` layer): + +1. Bump the four `edgezero-*` git deps in root `Cargo.toml` from `rev = "170b74b"` + to the #269 branch (then to `main` post-merge), regenerate root `Cargo.lock`. +2. **Reconcile `crates/integration-tests/Cargo.lock`** (it has its own lock; so + does `crates/openrtb-codegen/`). CI enforces that shared direct deps match + between the root and integration-tests lockfiles, and the 257-commit edgezero + delta will drag shared transitive deps (bytes/http/serde/…). Fix with targeted + `cargo update -p --precise ` in the integration-tests workspace — + **never** a blanket `cargo update`. +3. Fix the **18 `Body::into_bytes()` sink bindings** (§2) — 8 production + 10 + test — with explicit `.expect("should …")` / `None` handling. +4. Run the full gate (§8): host + Fastly `wasm32-wasip1`, **`--all-targets`**, + clippy `-D warnings`, `cargo test --workspace`, `cargo fmt --check`. + +**Status:** compilation is now **verified** (host, lib + tests — §10): the forced +code delta is the `Body` sinks and nothing else. **Still unverified:** +`wasm32-wasip1`, clippy, full test pass, and lockfile reconciliation (step 2). +Source-API diffing cannot see transitive breaks (dep bumps, MSRV, feature +unification, `spin-sdk ~6.0` lock entries); the §8 matrix is the proof, not this +document. "Does not reach us" (§4.10) is true at _source_ level but does not by +itself prove the lock graph resolves or that wasm builds. + +### 5.1 Risks & assumptions + +| Risk / assumption | Mitigation | +| ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Pinning to an **OPEN, unmerged, force-pushable** ref (`feature/extensible-cli`) | Pin to the branch only if we must move pre-merge; re-pin to edgezero `main` after #269 merges (mirrors mocktioneer #110). | +| **Transitive build breakage** unseen by source diffing (lock re-resolution, MSRV, features, spin-sdk 6) | Verification gate above — actually build on a scratch branch before sign-off. | +| Branch rebased / dep destabilizes after we pin | **Rollback = single-commit revert.** `170b74b` stays recoverable; the repin is one `Cargo.toml`/`Cargo.lock` commit — `git revert` it to return to the known-good pin. | +| Assumption: all 18 `Body` sinks are buffered (`Once`) bodies | True today (compiler-confirmed receivers); if a future sink is genuinely streaming, use `into_stream()` / branch on `None` instead of `.expect()`. | +| **integration-tests lockfile drift** — CI fails if shared direct deps diverge between root and `crates/integration-tests/Cargo.lock` | Reconcile with targeted `cargo update -p --precise` (§5 step 2); never blanket-update. | +| **Test-only sinks slip through** a `cargo build`-only check | Gate runs `--all-targets` + `cargo test` (§8) — 10 of 18 sinks are test-only (§10). | + +--- + +## 6. Strategic divergence (decision for the HTTP port) + +The original migration built a trusted-server-owned abstraction: +`RuntimeServices { config_store, secret_store, kv_store, backend, http_client, +geo, client_info }` with `PlatformConfigStore`/`PlatformSecretStore`(full CRUD)/ +`PlatformKvStore`/`PlatformHttpClient` traits. #269 ships edgezero's _own_ +first-class equivalents: async `ConfigStore`/`SecretStore` read traits, the +multi-store `StoreRegistry`, `Config`/`Secrets`/`Kv` extractors, env-var store +binding, and typed `AppConfig`. + +So two parallel abstractions now exist for the same job. The original design doc +already flagged this risk (PR2: "these must not coexist as parallel abstractions +… file an EdgeZero issue to generalize `ProxyClient` into `HttpClient`"). #269 is +edgezero answering that — on the store/config axis. + +| Option | What it means | Cost | Pull | +| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **A. Minimal repin** | Keep `platform/` layer; only fix `Body`. | 18 sinks | Fastest; preserves CRUD writes (edgezero stores are read-only) and the `select()` fan-out we depend on. | +| **B. Converge stores** | Map `PlatformConfigStore`/`SecretStore` reads onto edgezero's `ConfigStore`/`SecretStore` + `StoreRegistry`; keep our write-CRUD as an extension. | medium | Aligns with framework direction; less bespoke code. But edgezero read traits don't cover our management writes (key rotation), so the layer can't fully dissolve. | +| **C. Full adoption** | Also take `run_app`/`app!`/`RequestContext`/extractors/typed `AppConfig`. | large | Matches mocktioneer; big rewrite of our manual dispatch + `Settings`. | + +**Recommendation:** ship **A** as the repin (unblocks everything, tiny diff), +then evaluate **B** as a separate follow-up once #269 lands on `main`. **C** is a +roadmap question, not a repin question — and it's where the HTTP/CLI split +actually pays off: typed `AppConfig` (C/§4.7) is Christian's CLI surface, and our +read-store convergence (B) is the HTTP surface. The shared contract between the +two of you is **the typed config struct + its `[stores.config]` declaration** — +agree its shape and store id before either port starts. + +--- + +## 7. HTTP-port vs CLI-port split + +| | HTTP port (Prakash) | CLI port (Christian) | +| ----------------- | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | +| #269 axis | runtime / stores / adapters (§4.1–4.6, 4.8, 4.10) | CLI + typed config (§4.7, 4.9) | +| repin-forced work | `Body` fix (§2) | none (trusted-server has no edgezero CLI today) | +| net-new adoption | optional store convergence (§6 B) | `-cli` crate, `auth`/`provision`/`config validate`/`config push`, `#[derive(AppConfig)]`, CI `config validate --strict` gate | +| shared seam | **reads** the typed config from the bound store | **defines/validates/pushes** the typed config | + +Note: because trusted-server uses a bespoke `Settings` + `trusted-server.toml` +(not edgezero config), Christian's CLI port is largely a **net-new adoption** +(mirroring mocktioneer #110 §3.5–3.9), not a break-fix. Sequence the typed-config +struct contract first; both ports depend on it. + +--- + +## 8. Verification commands + +```bash +# reproduce the upstream diff base (full clone; 170b74b is not in a shallow fetch) +cd /tmp && rm -rf ez && git clone https://github.com/stackpop/edgezero ez && cd ez +git fetch origin feature/extensible-cli +git diff 170b74b..origin/feature/extensible-cli -- crates/edgezero-core/src/body.rs + +# after repin, in this repo — the full gate: +cargo build --workspace --all-targets # CRITICAL: --all-targets, else 10 test-only sinks hide +cargo build --package trusted-server-adapter-fastly --target wasm32-wasip1 +cargo test --workspace +cargo clippy --workspace --all-targets --all-features -- -D warnings +cargo fmt --all -- --check + +# integration-tests lockfile gate (separate workspace lock): +( cd crates/integration-tests && cargo build --workspace ) # must resolve against root deps +``` + +Expected pre-fix failures: type errors at the 18 `Body` sinks (§2) — `Option` +has no `.len()`/`.to_vec()`, can't pass where `Bytes`/`&[u8]` expected. **8 surface +under plain `cargo build`; the other 10 only under `--all-targets`/`cargo test`.** + +Compilation green is **verified** (§10). The remaining legs — wasm, clippy, full +test, lockfile reconciliation — are the proof of "done"; transitive lock/MSRV/ +feature breaks surface only here, not in source-API diffing. **Re-run this gate on +every branch as the pin advances** (§11), since the sink set and line numbers +shift per layer. + +--- + +## 9. Decisions & open questions + +**Decided (this review cycle):** + +- **Repin target:** pin the upgrade branch to `feature/extensible-cli` (or its + HEAD sha) to start now; **re-pin to edgezero `main` after #269 merges.** +- **Where:** dedicated branch **off PR14** — _not_ main, _not_ in-place on any + reviewed PR — then **merge up** the stack (merge, not rebase). See §11. +- **Scope:** the _forced_ repin work is minimal (the 18 `Body` sinks, §2/§10). The + full A+B+C adaptation (store convergence onto edgezero's `ConfigStore`/ + `SecretStore`/`StoreRegistry`; two-tier typed `AppConfig`; entry-point + convergence) is a **separate, optional roadmap** — see §6 and the companion + full-adaptation design — and is **not** required by the repin. +- **Platform layer:** _hybrid_ — converge stores onto edgezero (+ a thin + write-CRUD extension for rotation), keep `PlatformHttpClient`/`PlatformBackend`/ + `Geo`/`ClientInfo` (edgezero gaps). Roadmap, not repin. +- **`edgezero-adapter-axum`/`cloudflare`:** drop — absent from the dependency + graph (`cargo tree -i` matches no package, §1); not compiled. + +**Still open:** + +1. Typed-config struct + `[stores.config]` id: the shared HTTP/CLI contract — + agree with Christian before either port lands (roadmap, not repin). +2. wasm32-wasip1 + clippy + test legs of the verification gate (§10 covered host + build only). + +--- + +## 10. Verified build result (spike) + +The §5 "prediction, not a result" hedge is **discharged for compilation** (host). +A throwaway branch `spike/edgezero-269-upgrade` was cut **off PR14** (base pin +`38198f9`), repinned to #269 HEAD (`2eeccc9`), and built twice: + +``` +cargo build --workspace → exit 101, 15 errors (lib + bin only) +cargo build --workspace --all-targets → exit 101, 27 errors (adds tests) +``` + +**Every error is downstream of `Body::into_bytes` → `Option`, all in +`trusted-server-core`. Zero errors from `RequestContext`, `EdgeError`, middleware, +router, or any other #269 churn — even though PR14 imports all of those.** This +empirically confirms the central thesis: the repin's forced code change is the +`Body` break and nothing else. + +The two runs differ by design and this gap is the lesson: + +- **Plain `cargo build`** compiles lib + bin → the **8 production** sinks (§2). +- **`--all-targets`** also compiles tests → **+10 test-only** sinks. These are + **invisible to plain `cargo build`** and only fail under `cargo test` / + `--all-targets`. A repin that greens `cargo build` but skips `--all-targets` + would ship a red test suite. **The gate (§8) must include `--all-targets`.** + +27 raw errors collapse to **18 distinct `into_bytes()` bindings** (one binding → +several errors via `.len()`/`.to_vec()`/`from_slice(&…)`/`from_utf8(&…)`). Full +enumeration with the production/test split is §2 — that list is now the +compiler's, superseding the earlier rg attempt (which missed `proxy.rs:38` and +`auction/endpoints.rs:81`). + +**Not yet run** (remaining gate legs): `wasm32-wasip1` build, `cargo clippy -D +warnings`, `cargo test --workspace`, and the integration-tests lockfile +reconciliation (§5.1). Compilation-green ≠ gate-green. + +> Evidence branch kept (repin only; trial code edits reverted) so the failing +> build is reproducible. No real branch was modified. + +--- + +## 11. Impact on the in-flight stacked migration branches + +The stack is `PR1 → … → PR20`, partially linear / partially diverged +(PR15/PR16/PR19 are not clean descendants of their predecessor — merges +happened). Pins climb in steps: + +| Branches | edgezero pin | date | +| ----------- | ------------ | ------ | +| PR1–13 | `170b74b` | Mar 18 | +| PR14–18 | `38198f9` | Apr 9 | +| PR19–20 | `ce6bcf7` | May 21 | +| (#269 HEAD) | `2eeccc9` | Jun 12 | + +**Key facts:** + +- `Body::into_bytes`→`Option` landed in `7ec2ad1` ("strict clippy #257", **Jun + 12**) — _after every current stack pin_ (latest is May 21). So **no existing + branch has absorbed the `Body` break**; whichever branch first bumps to a + rev ≥ Jun-12 eats all 18 sinks (§2). +- **PR14 is the inflection**: it introduces `run_app`/`RequestContext`/ + `EdgeError`/`middleware`/`router` consumption (PR13 has none). That is the + high-level surface #269 churned — _but the §10 build proves it does not break_. +- main's "only `Body`" story is therefore true **for the whole stack**, not just + main. The earlier worry that PR14+ would drag in context/error/router breakage + is **disproven by build**. + +**Why not repin at the bottom (main / `upgrade-edgezero-http-layer`):** that base +predates the consuming code, so it can't exercise PR14+ at all; the real build +signal only exists from PR14 up. + +**Why a dedicated branch off PR14, not in-place on PR14:** PR14 is still under +review; folding a version bump into it conflates two review concerns and forces +rework when review feedback rewrites the migrated code. A branch _on top of_ PR14 +gets the same build signal without disturbing any open review. + +**Propagation:** land the repin + `Body` fix once on the dedicated branch, then +**merge up** PR14→15→16→17→18→19→20 (merge, not rebase, per team preference). +Conflicts will cluster in the files every layer rewrites — `publisher.rs`, +`proxy.rs`, `request_signing/endpoints.rs`, `auction/endpoints.rs`. Run the §10 +gate (host + wasm + clippy + test) **per branch as the pin advances**, not once. From 7e70dc67c8abd8ccc83b110881487b4b49b1770d Mon Sep 17 00:00:00 2001 From: prk-Jr Date: Thu, 18 Jun 2026 13:19:37 +0530 Subject: [PATCH 02/15] Revise repin spec and plan for ts-cli-next convergence --- .../plans/2026-06-17-edgezero-269-repin.md | 494 ++++++------------ ...edgezero-269-repin-breaking-api-finding.md | 138 ++++- 2 files changed, 298 insertions(+), 334 deletions(-) diff --git a/docs/superpowers/plans/2026-06-17-edgezero-269-repin.md b/docs/superpowers/plans/2026-06-17-edgezero-269-repin.md index 43118c19..bfdff332 100644 --- a/docs/superpowers/plans/2026-06-17-edgezero-269-repin.md +++ b/docs/superpowers/plans/2026-06-17-edgezero-269-repin.md @@ -1,427 +1,267 @@ -# EdgeZero #269 Repin Implementation Plan +# EdgeZero #269 HTTP-Layer 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:** Repin trusted-server's edgezero dependency from its current pin (`38198f9` on the PR14 base; `170b74b` is the stack's _original_ PR1–13 pin) to post-#269 and fix the only forced code break (`Body::into_bytes` → `Option`), keeping the bespoke `platform/` layer unchanged. +**Goal:** Land the HTTP-layer (runtime) half of adopting edgezero `stackpop/edgezero#269` in trusted-server — by **converging onto Christian's `feature/ts-cli-next`** (which already carries the repin, the `Body` fixes, and runtime Settings-from-config-store for Fastly), then closing the runtime gaps it leaves: seed-before-serve safety, secrets/KV runtime wiring, non-Fastly adapters, and the missing runtime-config-store spec. -**Architecture:** Mechanical dependency bump on a dedicated branch off `feature/edgezero-pr14-entry-point-dual-path` (never in-place on a reviewed PR), then propagate up the stack by merge. The "test" for this work is the compiler + full CI gate: RED = build errors at `Body` sinks, GREEN = full gate passes. No new abstractions; the A/B/C convergence (store registry, typed `AppConfig`, entry-point) is **out of scope** — separate follow-on plans. +**Architecture:** trusted-server keeps its bespoke `platform/` layer (`RuntimeServices` + `PlatformConfigStore`/`SecretStore`/`KvStore`). #269's only forced code break is `Body::into_bytes() → Option` (18 sinks — Appendix A). Christian's branch already fixes those and wires `get_settings_from_services()` to rebuild `Settings` from the `app_config` config store via the shared `config_payload` flatten/hash contract. Our work is the **runtime-side hardening + spec**, not a parallel repin. -**Tech Stack:** Rust 2024, cargo, `wasm32-wasip1` (Fastly via Viceroy), edgezero git dep, `error-stack`. +**Tech Stack:** Rust 2024, cargo, `wasm32-wasip1` (Fastly via Viceroy), edgezero git dep (`2eeccc9`, #269 HEAD), `error-stack`. -**Source spec:** [2026-06-16-edgezero-269-repin-breaking-api-finding.md](../specs/2026-06-16-edgezero-269-repin-breaking-api-finding.md) — §2 (sink list), §5 (steps), §8 (gate), §11 (stack/merge-up). +**Source spec:** [2026-06-16-edgezero-269-repin-breaking-api-finding.md](../specs/2026-06-16-edgezero-269-repin-breaking-api-finding.md) — esp. **§12** (convergence), §2 (sinks), §9 (decisions). --- -## Scope & non-goals - -**In scope:** repin the 4 `edgezero-*` deps; fix the 18 `Body::into_bytes` sinks (8 production + 10 test); reconcile the integration-tests lockfile; drop the two never-compiled adapter deps; pass the full gate; merge up PR14→PR20. - -**Out of scope (separate plans):** store convergence onto edgezero `ConfigStore`/`SecretStore`/`StoreRegistry` (spec §6 B); typed `AppConfig` two-tier config (§6 C / Christian's CLI port); entry-point `run_app`/`app!` adoption (§6 C). Do **not** start these here. - -**Key constraints:** +## Strategy change (why this plan was rewritten) -- `as_bytes` changed but has **no** trusted-server sink — only `into_bytes` needs edits (spec §2). -- All sinks are buffered (`Once`) bodies → fix with `.expect("should …")`, never `unwrap_or_default()`. -- Line numbers below are **PR14-base**; they shift per branch. **Re-derive the exact sink set from the compiler on each branch** — do not trust hardcoded lines after PR14. -- Pin to the #269 HEAD sha while #269 is open; **re-pin to edgezero `main` after #269 merges**. +The prior version of this plan was a standalone minimal-repin off PR14. Investigation of `feature/ts-cli-next` (2026-06-18, spec §12) showed that branch is **not just CLI** — it already implements the end-to-end Fastly config-store migration: same #269 pin, the `Body` fixes, store ids, the `config_payload` contract, **and** runtime `Settings`-from-store load. So a separate Fastly repin is **redundant**. This plan now **builds on his branch** and focuses on the runtime gaps. The verified `Body`-sink enumeration is preserved as Appendix A (still the authoritative sink reference when his ad-hoc fixes merge up the stack). --- -## File structure +## Open decisions — resolve at Phase 0 before coding -| File | Change | Responsibility | -| ------------------------------------------------------------- | ---------------------------------------------------- | --------------------------- | -| `Cargo.toml` | Modify lines 59–62 | The 4 `edgezero-*` git pins | -| `Cargo.lock` | Regenerated | Root lock | -| `crates/integration-tests/Cargo.lock` | Reconcile | Shared-dep lock (CI gate) | -| `crates/trusted-server-core/src/proxy.rs` | 5 sinks (38, 1550, 1665 prod; 2034, 2795, 2851 test) | proxy/asset body reads | -| `crates/trusted-server-core/src/publisher.rs` | 4 sinks (46 prod; 748, 1079, 1562 test) | publisher body reads | -| `crates/trusted-server-core/src/auction/endpoints.rs` | 1 sink (81 prod) | auction body read | -| `crates/trusted-server-core/src/auction/formats.rs` | 1 sink (444 test) | auction test helper | -| `crates/trusted-server-core/src/request_signing/endpoints.rs` | 4 sinks (103, 246, 365 prod; 464 test) | signing endpoint body reads | -| `crates/trusted-server-core/src/integrations/prebid.rs` | 1 sink (2067 test) | prebid test | -| `crates/trusted-server-core/src/integrations/testlight.rs` | 1 sink (461 test) | testlight test | +| # | Decision | Recommendation | +| --- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | +| D1 | Build on `feature/ts-cli-next` vs keep the PR14-stack minimal-repin | **Build on his** (his is end-to-end for Fastly; ours duplicates it) | +| D2 | Whole-`Settings` → store (his) vs two-tier small `AppConfig` (our spec §6) | **Adopt his whole-`Settings`** (one source of truth; already implemented) | +| D3 | `Body` fix style | **His `ok_or_else` (graceful)** over `.expect()` (spec §2) | +| D4 | Empty/unseeded store behavior | **Decide explicitly** (Phase 2) — today it's a hard fail / outage | +| D5 | CLI-driven secret push (he punts) vs runtime secret writes (already exist via `management_api.rs`) | Keep runtime rotation; treat CLI secret-push as a later follow-up | +| D6 | Branch/merge topology — his branch is off `main`, the stack is PR14→PR20 | Phase 5 — confirm with team | -`http_util.rs:456` (`enforce_max_body_size(bytes: &[u8], …)`) is **not** a sink — no edit. +Do **not** start Phase 1 until **D1–D3** are confirmed (they set the base branch +and code style). **D4–D6 are sequenced, not skipped:** D4 (empty-store response) +is resolved in Phase 2 Step 5, D5 (secret-write boundary) in Phase 3 Step 2, D6 +(branch topology) in Phase 5 Step 1. --- -## Fix shapes (apply the matching one at each sink) +## Scope & non-goals -```rust -// Shape A — value consumed directly (body_as_reader; let body = …into_bytes()) -let body = resp.into_body().into_bytes() - .expect("should have a buffered body"); - -// Shape B — chained .to_vec() (String::from_utf8(…into_bytes().to_vec())) -String::from_utf8( - resp.into_body().into_bytes() - .expect("should have a buffered body") - .to_vec(), -) - -// Shape C — bound, then borrowed into &[u8]/&Bytes (enforce_max_body_size(&b)/from_slice(&b)) -let b = req.into_body().into_bytes() - .expect("should have a buffered request body"); -enforce_max_body_size(&b, …)?; -serde_json::from_slice(&b)?; -``` +**In scope:** converge onto his branch; verify the repin + `Body` fixes are complete against Appendix A; run the full gate (host, **wasm32-wasip1**, **`--all-targets`**, clippy, test) + integration-tests lockfile; harden runtime config-store loading (empty/malformed-store, seed-before-serve); confirm secrets/`ec_identity_store` KV runtime wiring; write the runtime-config-store spec; merge up the stack. -`cargo fmt` will rewrap; write the one-liner and let it format. +**Out of scope (separate plans):** the CLI crate itself (`ts config`/`audit` — Christian); CLI-driven secret push; full edgezero `run_app`/`app!`/extractor adoption (he kept the bespoke layer, so do we); non-Fastly adapter _feature_ parity beyond making them build. --- -## Task 0: Create the dedicated branch off PR14 - -**Files:** none (git only) - -- [ ] **Step 1: Branch off PR14 (not in-place, not main)** - -```bash -git fetch origin -git checkout -b feature/edgezero-269-repin feature/edgezero-pr14-entry-point-dual-path -``` - -- [ ] **Step 2: Confirm base + capture the authoritative "from" pin** - -Run: `git log -1 --format='%s' && grep -m1 'edgezero-core' Cargo.toml` -Expected: PR14 tip; the dep line prints the **base pin = `rev = "38198f9…"`**. +## File structure (what we touch / extend, on his branch) -> Pin clarity: the Goal's `170b74b` is the _stack's original_ pin (PR1–13), **not -> this branch's base.** PR14's base is `38198f9` (spec §11). The **only** -> authoritative "from" value is whatever this grep prints — use that, not a -> hardcoded sha, if the branch has advanced. +| File | Role | Our action | +| ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------- | +| `Cargo.toml` / `Cargo.lock` | edgezero pinned `2eeccc9` | verify; re-pin to `main` post-merge (Phase 5) | +| `crates/trusted-server-core/src/config_payload.rs` | flatten/hash contract (shared seam) | **read-only reference** — do not fork | +| `crates/trusted-server-core/src/settings_data.rs` | `get_settings_from_services` runtime load | **harden** empty/malformed behavior (Phase 2) | +| `crates/trusted-server-adapter-fastly/src/main.rs` | entry point: build services → load settings | **harden** the settings-error path (Phase 2) | +| `edgezero.toml` | store ids: `app_config` / `secrets` / `ec_identity_store` | verify; reference in the spec | +| `crates/trusted-server-core/src/{proxy,publisher,auction/endpoints,auction/formats,request_signing/endpoints}.rs`, `integrations/{prebid,testlight}.rs` | `Body` sinks | **verify** all 18 covered (Appendix A) | +| `crates/trusted-server-adapter-{cloudflare,spin}` | stubs, untouched by him | **make build** under #269 (Phase 3) | +| `docs/superpowers/specs/-runtime-config-store.md` | the missing spec | **create** (Phase 4) | --- -## Task 1: Repin edgezero to #269 + regenerate root lock - -**Files:** Modify `Cargo.toml:59-62`, regenerate `Cargo.lock` - -- [ ] **Step 1: Repin all 4 deps** - -Replace the base pin captured in Task 0 Step 2 (`rev = "38198f9…"` on the 4 -`edgezero-*` lines) with `rev = "2eeccc9748daba92b9adf6afe4df105e79269ae9"` -(#269 HEAD). (After #269 merges, use the edgezero `main` sha instead — see spec §9.) - -- [ ] **Step 2: Resolve the lock FIRST — separate resolution failure from compile-RED** - -Run: `cargo generate-lockfile` -Expected: lock resolves with no error. **If this fails** (MSRV / feature -unification / `spin-sdk` graph — the spec's #1 transitive risk, §5.1/§10), STOP -and surface it: that is a _resolution_ break, not the expected `Body` compile-RED, -and must be triaged before continuing. +## Phase 0: Convergence decision + adopt the base -- [ ] **Step 3: Capture the RED baseline (build only after the lock resolves)** +- [ ] **Step 1: Confirm D1–D3** with the team (record in the spec §9). If D1 = "build on his," proceed; if "keep PR14-stack," fall back to Appendix B (the minimal-repin tasks). -Run: `cargo build --workspace --all-targets 2>/tmp/ez_red.log; grep -cE '^error' /tmp/ez_red.log` -Expected: ~27 errors (`E0308`/`E0599`/`E0624`). This is the RED state. (Log goes to -`/tmp` — keep build artifacts out of the repo tree.) - -- [ ] **Step 4: Sanity — every error is at a known `Body` sink file, nothing else** - -Filter by **location**, not error-kind (an unexpected break could share a kind): - -Run: +- [ ] **Step 2: Create the HTTP-layer branch off his branch** ```bash -grep -A1 '^error' /tmp/ez_red.log | grep -- '-->' \ - | grep -vE 'trusted-server-core/src/(proxy|publisher|auction/(endpoints|formats)|request_signing/endpoints|integrations/(prebid|testlight)|http_util)\.rs' \ - && echo "UNEXPECTED — stop & investigate" || echo OK -``` - -Expected: `OK` (every error points into a known sink file from §2). Any other -location ⇒ unexpected transitive break; STOP and surface it (spec §5). - -- [ ] **Step 5: Commit the repin (still RED — that's expected)** - -```bash -git add Cargo.toml Cargo.lock -git commit -m "Repin edgezero to the extensible-cli branch (stackpop/edgezero PR 269)" +git fetch origin +# Record the exact SHA — his branch is an unmerged WIP and may force-push/rebase. +git rev-parse origin/feature/ts-cli-next # note this; if he rebases, re-base from the new SHA + coordinate +git checkout -b feature/edgezero-269-http origin/feature/ts-cli-next ``` ---- - -## Task 2: Fix production sinks — `proxy.rs` - -**Files:** Modify `crates/trusted-server-core/src/proxy.rs` (sinks 38, 1550, 1665) - -- [ ] **Step 1: Confirm current errors (RED)** - -Run: `cargo check --workspace 2>&1 | grep 'proxy.rs'` -Expected: errors **within `proxy.rs`** (exact line numbers vary per branch — re-derive -from this output; do not trust the §2 PR14 numbers after PR14). Expect a `body_as_reader` -`Cursor::new(body.into_bytes())` site plus two POST-body `let body_bytes = req.into_body().into_bytes();` sites. - -- [ ] **Step 2: Fix `body_as_reader` (Shape A)** - -`Cursor::new(body.into_bytes())` → `Cursor::new(body.into_bytes().expect("should have a buffered body"))` - -- [ ] **Step 3: Fix the two POST-body bindings (Shape C)** - -`let body_bytes = req.into_body().into_bytes();` → -`let body_bytes = req.into_body().into_bytes().expect("should have a buffered request body");` - -> `replace_all` only if the two lines are byte-identical (same indentation). **Read -> each site first**; if the replace count ≠ 2, fall back to per-site edits. Both are -> production code (not test). +- [ ] **Step 3: Baseline build (inherit his state)** -- [ ] **Step 4: Verify proxy.rs errors cleared** - -Run: `cargo check --workspace 2>&1 | grep -c 'proxy.rs' ; echo done` -Expected: `0` proxy.rs errors (other files may still error — fine). +Run: `cargo build --workspace --all-targets 2>/tmp/ez_base.log; echo "exit=$?"` +Expected: **green** (his branch should already compile). If red, capture and triage before any new work. --- -## Task 3: Fix production sinks — `publisher.rs` + `auction/endpoints.rs` - -**Files:** Modify `publisher.rs` (line 46), `auction/endpoints.rs` (line 81) - -- [ ] **Step 1: Fix `publisher.rs:46` `body_as_reader` (Shape A)** - -`std::io::Cursor::new(body.into_bytes())` → `std::io::Cursor::new(body.into_bytes().expect("should have a buffered body"))` - -- [ ] **Step 2: Fix `auction/endpoints.rs:81` (Shape C)** - -`let body_bytes = body.into_bytes();` → `let body_bytes = body.into_bytes().expect("should have a buffered request body");` +## Phase 1: Verify the inherited repin + `Body` fixes -- [ ] **Step 3: Verify both cleared** +His `Body` fixes were ad-hoc (driven by his build), not enumerated. Verify completeness against Appendix A, and run the **full** gate (he is unlikely to have run wasm + `--all-targets` + clippy on every leg). -Run: `cargo check --workspace 2>&1 | grep -cE 'publisher.rs|auction/endpoints.rs'` -Expected: `0`. +- [ ] **Step 1: Enumerate the sinks (locate, don't "prove")** ---- - -## Task 4: Fix production sinks — `request_signing/endpoints.rs` - -**Files:** Modify `request_signing/endpoints.rs` (lines 103, 246, 365) +Run: `git grep -nE 'into_bytes\(\)' crates/trusted-server-core/src -- 'proxy.rs' 'publisher.rs' 'auction/endpoints.rs' 'auction/formats.rs' 'request_signing/endpoints.rs' 'integrations/prebid.rs' 'integrations/testlight.rs'` +Expect **18 sites** (8 prod + 10 test). Eyeball each has an `Option` handler +(`.ok_or_else`/`.expect`/`.unwrap_or_default`). **Note: grep cannot prove +correctness** — a fixed Shape-C `let b = …into_bytes().ok_or_else(…)?;` and a +broken bare `.into_bytes()` both contain `.into_bytes()`. This step is enumeration +only; the **authoritative completeness proof is Step 2's green `--all-targets` + +`cargo test`.** Appendix A line numbers are **PR14-base and do NOT apply** to this +`main`-based branch — trust the grep _count_ (18), not the numbers. -- [ ] **Step 1: Fix all three `req.into_body()` bindings (Shape C)** - -`replace_all` of ` let body = req.into_body().into_bytes();` → - -```rust - let body = req - .into_body() - .into_bytes() - .expect("should have a buffered request body"); -``` - -> Expect 3 identical occurrences, all production. **Read the sites first**; if the -> replace count ≠ 3 (indentation differs), fall back to per-site edits. The -> `json_response(body: String)` site (`String::into_bytes`) is a false positive — -> **do not touch** (verify by checking the receiver is `String`, not `Body`). - -- [ ] **Step 2: Verify lib/bin build is GREEN** - -Run: `cargo build --workspace` -Expected: **success** (all 8 production sinks fixed). Tests still red — next. - -- [ ] **Step 3: Commit production fixes** +- [ ] **Step 2: Full gate (the legs he likely skipped)** ```bash -git add crates/trusted-server-core/src -git commit -m "Adapt Body::into_bytes Option return at production sinks" +cargo build --workspace --all-targets +cargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1 +cargo test --workspace +cargo clippy --workspace --all-targets --all-features -- -D warnings +cargo fmt --all -- --check ``` ---- - -## Task 5: Fix test sinks - -**Files:** Modify `proxy.rs` (2034, 2795, 2851), `publisher.rs` (748, 1079, 1562), `auction/formats.rs` (444), `request_signing/endpoints.rs` (464), `integrations/prebid.rs` (2067), `integrations/testlight.rs` (461) - -- [ ] **Step 1: Confirm test errors (RED)** - -Run: `cargo build --workspace --all-targets 2>&1 | grep -E '^error' | wc -l` -Expected: ~12 remaining errors, all in test code at the lines above. - -- [ ] **Step 2: Apply the matching shape at each test sink** - -- `String::from_utf8(… .into_bytes().to_vec())` → Shape B (`.expect("should have a buffered body")` before `.to_vec()`): `proxy.rs:2034`, `publisher.rs:748`, `prebid.rs:2067`, `request_signing/endpoints.rs:464`. -- `serde_json::from_slice(&… .into_bytes())` → Shape C (bind, `.expect`, borrow): `auction/formats.rs:444`, `testlight.rs:461`. -- `let x = … .into_bytes();` → Shape A (`.expect`): `proxy.rs:2795`, `proxy.rs:2851`, `publisher.rs:1079`, `publisher.rs:1562`. - -(The `request_signing/endpoints.rs:452` test helper is `str::as_bytes` — false positive, **do not touch**.) +Expected: all green. Any failure here is the real signal — fix before Phase 2. -- [ ] **Step 3: Verify `--all-targets` is GREEN** +- [ ] **Step 3: integration-tests lockfile** -Run: `cargo build --workspace --all-targets` -Expected: **success** — all 18 sinks fixed. +`crates/integration-tests` is a separate workspace that path-deps `trusted-server-core`. +Run: `( cd crates/integration-tests && cargo build --workspace )` first (don't +`generate-lockfile` — that can re-resolve and _cause_ drift). Only if it fails on +shared-dep mismatch: `cargo update -p --precise ` (never +blanket). Repeat for `crates/openrtb-codegen` if it drifts. -- [ ] **Step 4: Commit test fixes** +- [ ] **Step 4: Commit any gate fixups** ```bash -git add crates/trusted-server-core/src -git commit -m "Adapt Body::into_bytes Option return in tests" +git add crates Cargo.toml Cargo.lock && git commit -m "Complete Body sink coverage and pass full gate on #269" || echo "nothing to commit" ``` --- -## Task 6: Drop never-compiled adapter deps +## Phase 2: Runtime config-store hardening (the core HTTP-layer deliverable) -**Files:** Modify `Cargo.toml` (remove `edgezero-adapter-axum`, `edgezero-adapter-cloudflare` from `[workspace.dependencies]`) +**Problem (verified against his `main.rs`):** `get_settings_from_services` → +`get_settings_from_config_store` reads `ts-config-keys` first; on an +**empty/unseeded store** `read_config_entry`'s `?` propagates a `Configuration` +error. His settings-error arm **does serve a response** — +`to_error_response(&e).send_to_client(); return;` (not a bare return, not an +opaque default; `fn main()` returns `()` and serves explicitly). So the issue is +**not** "no response" — it is that **every route returns a generic error** until +the store is seeded, and the error is **indistinguishable from a real config +bug**. Fresh deploy before `ts config push` = **total outage with an opaque 500**. +The gap our layer owns: make the unseeded case **actionable** (clear message) and +**correctly classified** (retryable 503, not 500). -- [ ] **Step 1: Confirm they are absent from the graph** +> **Call chain (read first):** `get_settings_from_services(&runtime_services)` → +> `get_settings_from_config_store(&dyn PlatformConfigStore, &StoreName)` → +> `read_config_entry` (per key) → `settings_from_config_entries` (hash verify). +> The in-memory `PlatformConfigStore` fake **already exists** as +> `MemoryConfigStore` in `settings_data.rs` tests (around line 84) — reuse it; do +> not write a new one. Confirm the exact constructor (`MemoryConfigStore { entries }` +> vs `::new(...)`) before writing the test below. -Run: `cargo tree -i edgezero-adapter-axum; cargo tree -i edgezero-adapter-cloudflare` -Expected: both → "did not match any packages" (no member uses them — spec §1). +- [ ] **Step 1: Write a failing test — empty store yields an actionable, typed error (not a generic read failure)** -- [ ] **Step 2: Remove the two lines from `Cargo.toml` `[workspace.dependencies]`** +In `settings_data.rs` tests (reuse `MemoryConfigStore`): -Delete the `edgezero-adapter-axum = …` and `edgezero-adapter-cloudflare = …` lines (keep `edgezero-adapter-fastly` and `edgezero-core`). - -- [ ] **Step 3: Verify still builds** - -Run: `cargo build --workspace --all-targets` -Expected: success (nothing referenced them). - -- [ ] **Step 4: Commit** - -```bash -git add Cargo.toml Cargo.lock -git commit -m "Drop unused edgezero axum/cloudflare workspace deps" +```rust +#[test] +fn empty_config_store_reports_unseeded_not_generic_failure() { + let store = MemoryConfigStore::new(BTreeMap::new()); // no ts-config-keys + let err = get_settings_from_config_store(&store, &StoreName::from("app_config")) + .expect_err("empty store should error"); + // assert it carries an actionable "config store not seeded — run `ts config push`" context + assert!(format!("{err:?}").contains("not seeded") || format!("{err:?}").contains("ts config push")); +} ``` -> If a future `trusted-server-adapter-{axum,cloudflare}` consumer lands, re-add then. Skip this task if the team prefers to keep them pinned for symmetry — note the decision. +- [ ] **Step 2: Run it — verify it fails** (`cargo test -p trusted-server-core empty_config_store -- --nocapture`). Expected: FAIL (current error message is generic "failed to read … key `ts-config-keys`"). ---- - -## Task 7: Reconcile the integration-tests lockfile - -> **Ordering matters.** This runs _after_ all root dependency changes (repin Task 1 -> -> - drop-adapters Task 6) and _after_ `trusted-server-core` is GREEN (Tasks 2–5). -> `crates/integration-tests` is a **separate workspace** that path-deps -> `trusted-server-core` (`Cargo.toml:13`); building it any earlier fails on the -> Body errors, not lock drift — confounding the check. +- [ ] **Step 3: Implement — distinguish "unseeded" from "read error"** -**Files:** `crates/integration-tests/Cargo.lock` (and `crates/openrtb-codegen/Cargo.lock` if it drifts) +In `read_config_entry` / `get_settings_from_config_store`, when the **metadata** key (`ts-config-keys`) is absent, attach an actionable context (e.g. `TrustedServerError::Configuration` with `"config store `{store}`is not seeded — run`ts config push --adapter fastly`"`). Keep transport/read failures distinct. -- [ ] **Step 1: Resolve + build the integration-tests workspace** +- [ ] **Step 4: Run the test — verify it passes.** -Run: `( cd crates/integration-tests && cargo generate-lockfile && cargo build --workspace 2>&1 | tail -20 )` -Expected: lock resolves and it builds. Because core is now green, any failure here -is a _real_ signal — shared-dep drift or a genuine break — not the Body RED. +- [ ] **Step 5: Decide + implement the adapter response (D4)** -- [ ] **Step 2: If shared-dep drift, reconcile with targeted updates only** +His settings-error arm already serves via `to_error_response(&e).send_to_client(); return;`. +Two options — **decide D4 here:** +(a) keep the arm, but have `to_error_response` map the new "unseeded" error context +to **503** (retryable) instead of 500; or +(b) special-case the unseeded error in `main.rs` before `to_error_response`: +`FastlyResponse::from_status(503).with_body_text_plain("config not provisioned — run `ts config push`").send_to_client(); return;` +(matches the existing `from_status(...).with_body_text_plain(...).send_to_client()` +idiom at `main.rs:119–121`). Add an adapter test asserting **503 + body** for the +unseeded case. This turns an opaque 500 into an observable, actionable signal — +and keeps real config bugs as 500. -For each mismatched shared dep (e.g. `bytes`, `http`, `serde`): -Run: `( cd crates/integration-tests && cargo update -p --precise )` -**Never** a blanket `cargo update`. (Project CI gate: shared direct deps must match -root.) Repeat in `crates/openrtb-codegen` if it drifted. +- [ ] **Step 6: Malformed-store test** — seed a `ts-config-hash` that doesn't match the entries; assert `settings_from_config_entries` errors on hash mismatch (his code already verifies; add the test if absent so the contract is locked). -- [ ] **Step 3: Verify** +- [ ] **Step 7: Confirm secrets + KV runtime wiring** + - `secrets` store: request-signing reads signing keys via `PlatformSecretStore` (pre-existing `management_api.rs` provides write CRUD). Add/confirm a test that a missing signing secret degrades to a clear error, not a panic. + - `ec_identity_store` KV: `main.rs` starts `UnavailableKvStore` and EC routes lazily bind the configured store. Confirm a non-EC route still serves when EC KV is unavailable (existing behavior — add a regression test if missing). -Run: `( cd crates/integration-tests && cargo build --workspace )` -Expected: success. - -- [ ] **Step 4: Commit if changed** +- [ ] **Step 8: Commit** ```bash -git add crates/integration-tests/Cargo.lock crates/openrtb-codegen/Cargo.lock -git commit -m "Reconcile integration-tests lockfile after edgezero repin" || echo "nothing to commit" +git add crates && git commit -m "Harden runtime config-store load: actionable unseeded error and 503 response" ``` --- -## Task 8: Full verification gate - -**Files:** none (verification only) - -- [ ] **Step 1: Compile gate (host + all-targets)** +## Phase 3: Adapter + build-surface gaps -Run: `cargo build --workspace --all-targets` -Expected: success. +- [ ] **Step 1: Make non-Fastly adapters build under #269** -- [ ] **Step 2: wasm32-wasip1 (Fastly deploy target)** +First confirm what "builds" means for these stubs — spec §1 notes +cloudflare/axum are **absent from the dependency graph** (not currently compiled). +If the crate has no real wasm entry, "builds" = `cargo check -p trusted-server-adapter-cloudflare` +on host; only use `--target wasm32-unknown-unknown` (install the target first) if +it has a genuine worker entry point. Same judgment for spin. +If they break on `Body`/edgezero churn, apply the Appendix A fix shapes. They are stubs — goal is **compiles**, not feature parity (out of scope). -Run: `cargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1` -Expected: success. (First leg not yet verified at spec-freeze — this is the gate that proves it.) +- [ ] **Step 2: Document the secret-write boundary (D5)** -- [ ] **Step 3: Tests** +Confirm: runtime key-rotation secret writes work via `management_api.rs` (pre-existing); CLI-driven secret _push_ is deferred (Christian punts it). Capture this split in the spec so it is a recorded decision, not an accident. -Run: `cargo test --workspace` -Expected: pass. Watch for behavioral diffs in body-handling tests (they exercise the `.expect()` paths). - -- [ ] **Step 4: Clippy + fmt** - -Run: `cargo clippy --workspace --all-targets --all-features -- -D warnings && cargo fmt --all -- --check` -Expected: clean. - -- [ ] **Step 5: integration-tests + JS gates (per CLAUDE.md CI)** - -Lock already reconciled (Task 7) — this just runs the suites. -Run: `( cd crates/integration-tests && cargo test --workspace )` and `( cd crates/js/lib && npx vitest run )` -Expected: pass (JS untouched — sanity only). - -- [ ] **Step 6: Commit any fmt fixups** - -```bash -git add crates Cargo.toml Cargo.lock && git commit -m "Verification gate fixups" || echo "nothing to commit" -``` - -(Scope the add — never `git add -A`, which would sweep stray build logs into the commit.) +- [ ] **Step 3: Commit any adapter fixups.** --- -## Task 9: Open the PR (PR14-based dedicated branch) - -**Files:** none +## Phase 4: Runtime-config-store spec (the doc his CLI design references but never wrote) -- [ ] **Step 1: Push + open PR targeting the PR14 branch (or wherever the stack lands)** - -```bash -git push -u origin feature/edgezero-269-repin -gh pr create --base feature/edgezero-pr14-entry-point-dual-path \ - --title "Repin edgezero to #269 and adapt Body::into_bytes Option return" \ - --body "See docs/superpowers/specs/2026-06-16-edgezero-269-repin-breaking-api-finding.md" -``` +- [ ] **Step 1: Write `docs/superpowers/specs/-runtime-config-store.md`** covering: + - the load sequence (`build_runtime_services` → `get_settings_from_services` → `settings_from_config_entries`); + - the **shared `config_payload` contract** (escaping, sorted-key canonicalization, `sha256` over settings-only entries, `ts-config-*` reserved keys) — reference, do not duplicate; + - the **seed-before-serve** operational contract + the 503 unseeded behavior (Phase 2); + - empty / missing-key / malformed-hash / transport-error matrix; + - store-name resolution + `EDGEZERO__STORES__CONFIG__APP_CONFIG__NAME` override; + - secrets/KV runtime read paths + the secret-write boundary (D5); + - non-Fastly adapter status. -> Do not push or open the PR until the user approves (per project git rules). Confirm the base branch with the team — the stack tip may have advanced. +- [ ] **Step 2: Docs gate** — `cd docs && npm run format` (prettier-clean), then commit. --- -## Task 10: Propagate up the stack (merge, not rebase) +## Phase 5: Stack propagation + re-pin -**Files:** none (per-branch merge + gate) +- [ ] **Step 1: Reconcile topology (D6).** His branch is off `main`; the migration stack is PR14→PR20. Confirm with the team whether the HTTP-layer branch merges via `main` (with his) or threads the stack. Do not push/merge without approval. -> **Approval gate:** merging up mutates 6 review branches. Do **not** run this task -> (or any `git push`) until the user approves — same rule as Task 9. +- [ ] **Step 2: Re-pin to edgezero `main` after #269 merges** — one-line dep change in `Cargo.toml`, regenerate lock, re-run the Phase 1 gate. -For each branch PR15 → PR16 → PR17 → PR18 → PR19 → PR20, in order: +- [ ] **Step 3: Open the PR (approval-gated).** Base = whatever Step 1 resolves. Assign `@me`. Summary: HTTP-layer convergence + runtime hardening + spec. -- [ ] **Step 1: Merge the repin forward** - -```bash -git checkout feature/edgezero-pr15-remove-fastly-core -git merge feature/edgezero-269-repin # merge, not rebase (team preference) -``` - -- [ ] **Step 2: Re-derive sinks from the compiler (line numbers/sink set shift per layer)** - -Run: `cargo build --workspace --all-targets 2>&1 | grep -E 'into_bytes|Body'` -Resolve any new/moved sinks with the §2 fix shapes. PR15 (remove-fastly-core) and PR16+ move/delete these files — expect manual conflict resolution, not clean fast-forward. - -- [ ] **Step 3: Run the full gate (Task 8) on this branch** - -Expected: green before moving to the next branch up. - -- [ ] **Step 4: Commit the merge resolution (if any)** +--- -```bash -git add -A && git commit --no-edit || echo "nothing to commit (clean fast-forward)" -``` +## Risks & watch points -Repeat for each branch up to PR20. +| Risk | Mitigation | +| ------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Fresh deploy = outage** (unseeded store, no fallback) | Phase 2: actionable error + 503; document seed-before-serve; consider a provisioning gate in deploy | +| His `Body` fixes incomplete vs our 18 sinks | Phase 1 Step 1 cross-check against Appendix A | +| He likely didn't run wasm + `--all-targets` + clippy on every leg | Phase 1 Step 2 runs the full matrix | +| Pinned to an **open, force-pushable** #269 ref | Re-pin to `main` post-merge (Phase 5); rollback = revert the dep commit | +| **Building on a colleague's unmerged WIP branch** (`feature/ts-cli-next`) — it may rebase/force-push out from under us, vanishing our merge-base | Record its SHA at Phase 0 Step 2; if he rebases, re-base from the new SHA and coordinate before any merge; keep our additions as discrete commits so they re-cherry-pick cleanly | +| integration-tests lockfile drift | Phase 1 Step 3, targeted `--precise` only | +| Branch topology (his off `main`, stack off PR14) | Phase 5 Step 1, confirm with team | +| Whole-`Settings`-in-store enlarges blast radius of a bad push | hash verification (his) + malformed-store test (Phase 2 Step 6) | --- -## Follow-on plans (NOT this plan) +## Appendix A — verified `Body::into_bytes` sink reference (authoritative) + +From the compiler spike (spec §2/§10): **18 sink bindings, 8 production + 10 test-only**, all `into_bytes` (no `as_bytes` sink). The line numbers below are **PR14-base — they do NOT apply to the `main`-based `feature/ts-cli-next`**; use them only as a count/shape reference (8 prod + 10 test). On any branch, the compiler (`--all-targets`) is the source of truth. Use this to confirm Christian's ad-hoc fixes are complete and when merging up the stack. -Per spec §6/§9, after the repin lands and #269 merges to edgezero `main`: +- **Production (8):** `proxy.rs:38`, `publisher.rs:46`, `auction/endpoints.rs:81`, `proxy.rs:1550`, `proxy.rs:1665`, `request_signing/endpoints.rs:103/246/365`. +- **Test-only (10):** `auction/formats.rs:444`, `prebid.rs:2067`, `testlight.rs:461`, `proxy.rs:2034/2795/2851`, `publisher.rs:748/1079/1562`, `request_signing/endpoints.rs:464`. +- **Not a sink:** `http_util.rs:456` (the `enforce_max_body_size(bytes: &[u8], …)` signature). +- **Fix style (D3):** production → `into_bytes().ok_or_else(|| )?`; compression/test → `unwrap_or_default()`; only `.expect("should …")` where a buffered body is truly invariant. -1. **Re-pin to `main`** (one-line dep change + gate). -2. **Store convergence** (spec §6 B): map `PlatformConfigStore`/`SecretStore` reads onto edgezero `ConfigStore`/`SecretStore`/`StoreRegistry` + thin write-CRUD extension. -3. **Typed `AppConfig` (two-tier)** + **CLI port** (spec §6 C / §7) — Christian. Shared contract = the config struct + `[stores.config]` id; agree before either starts. +## Appendix B — fallback: standalone minimal-repin (only if D1 = "keep PR14 stack") -Each gets its own spec → plan cycle. +If the team rejects building on his branch, the original minimal-repin still applies: branch off PR14, repin to `2eeccc9`, fix the Appendix A sinks with the D3 style, reconcile the integration-tests lock, full gate, merge up PR14→PR20. (This duplicates his Fastly work and is **not** recommended — see spec §12.) diff --git a/docs/superpowers/specs/2026-06-16-edgezero-269-repin-breaking-api-finding.md b/docs/superpowers/specs/2026-06-16-edgezero-269-repin-breaking-api-finding.md index 35b6a089..c9c55412 100644 --- a/docs/superpowers/specs/2026-06-16-edgezero-269-repin-breaking-api-finding.md +++ b/docs/superpowers/specs/2026-06-16-edgezero-269-repin-breaking-api-finding.md @@ -65,6 +65,15 @@ convergence + typed config + entry-point) is a _separate, optional_ track — **not** forced by the repin (§11). +8. **Superseded for Fastly by `feature/ts-cli-next` (§12).** Christian's "CLI" + branch already implements the end-to-end Fastly config-store migration — same + #269 pin, the `Body` fixes (graceful `ok_or_else`, not `.expect()`), the store + ids, the `config_payload` flatten/hash contract, **and runtime + Settings-from-store load** (`get_settings_from_services`). So our minimal-repin + (#771) is largely redundant for Fastly. **Revised: build on his branch**; our + real HTTP-layer deliverable is the **runtime-config-store spec** his CLI doc + references but never wrote. See §12. + --- ## 1. What trusted-server actually consumes from edgezero @@ -86,6 +95,10 @@ of `edgezero-core` plus one type from `edgezero-adapter-fastly`. `ProxyClient`/edgezero `proxy`, typed `AppConfig`, manifest `[stores.*]` / `[adapters.*]` tables. trusted-server's manifest is `trusted-server.toml` (a bespoke `Settings` struct in `settings.rs`), **not** an edgezero `edgezero.toml`. +(Baseline as of the current pin / pre-`ts-cli-next`. Christian's branch adds an +`edgezero.toml` and deletes `trusted-server.toml` — but still reads config through +the **bespoke `PlatformConfigStore`**, not edgezero's first-class store/extractor, +so this "uses none of …" list stays true even there. See §12.) --- @@ -157,11 +170,30 @@ not `Body`; confirmed by source): `sourcepoint.rs:571` / `datadome.rs:323` (`rewrite_script_content() -> String`), `sourcepoint.rs:822` (`FromUtf8Error::into_bytes`). -### Fix — three distinct shapes +### Fix — style (updated per `feature/ts-cli-next`) + +> **Revised guidance.** Christian's branch already fixed these sinks and chose +> **`into_bytes().ok_or_else(|| )?`** (graceful error, no panic) for +> production request/response handlers, `unwrap_or_default()` for +> compression/test paths, and reserved `.expect()` for genuinely-unreachable +> spots. That is **better than a blanket `.expect()`** — a streaming/empty body +> must not panic the worker. **Adopt his approach:** propagate an error at +> production handler sinks; only `.expect("should …")` where a buffered body is +> truly invariant (and never `unwrap_or_default()` where an empty body would +> silently corrupt behavior). Align with his exact per-sink choices when we +> converge (§12). -All sinks are buffered (`Once`) bodies, so use an explicit `.expect()` with a -`should`-style message (per CLAUDE.md), **not** `unwrap_or_default()`. If a future -sink is genuinely streaming, branch on `None` / use `into_stream()`. +For a production handler sink, prefer (error variant illustrative — match the +existing one at each call site, not necessarily `BadRequest`): + +```rust +let bytes = req.into_body().into_bytes().ok_or_else(|| { + Report::new(TrustedServerError::BadRequest { message: "request body should be buffered".into() }) +})?; +``` + +The three mechanical shapes below still apply (substitute `ok_or_else(…)?` for +`.expect(…)` at production sinks): ```rust // Shape A — value consumed directly (e.g. proxy.rs:38, publisher.rs:46) @@ -468,9 +500,22 @@ shift per layer. **Still open:** -1. Typed-config struct + `[stores.config]` id: the shared HTTP/CLI contract — - agree with Christian before either port lands (roadmap, not repin). -2. wasm32-wasip1 + clippy + test legs of the verification gate (§10 covered host +1. **Convergence with `feature/ts-cli-next` (§12).** Christian's branch already + implements the end-to-end Fastly config-store migration (repin + Body fix + + runtime Settings-from-store), so our minimal-repin (#771) is largely subsumed + for Fastly. Decide: rebase our HTTP work onto his config system vs keep the + PR14-stack repin. **Recommend: build on his.** +2. **Body-fix style conflict** — his `ok_or_else` (graceful) vs our former + `.expect()`. Resolved in §2 (adopt his); confirm per-sink alignment on merge. +3. **Secret-write conflict** — he punts secret-store writes (key rotation) until + edgezero exposes write primitives; our original migration design kept + write-CRUD in TS. Decide which holds. +4. **Shared config contract — now CONCRETE, not "to agree":** store ids + `app_config` / `secrets` / `ec_identity_store` (his `edgezero.toml`) and the + `config_payload` flatten/hash rules (his core module). The remaining gap is + the **runtime-config-store spec** his doc references but never wrote — that is + our HTTP-layer deliverable (§12). +5. wasm32-wasip1 + clippy + test legs of the verification gate (§10 covered host build only). --- @@ -555,3 +600,82 @@ gets the same build signal without disturbing any open review. Conflicts will cluster in the files every layer rewrites — `publisher.rs`, `proxy.rs`, `request_signing/endpoints.rs`, `auction/endpoints.rs`. Run the §10 gate (host + wasm + clippy + test) **per branch as the pin advances**, not once. + +--- + +## 12. Convergence with `feature/ts-cli-next` (Christian's branch) + +Inspected 2026-06-18. The "CLI" branch is **not just CLI** — it is an +**end-to-end config-store migration for the Fastly adapter**, off `main`, pinned +to the **same #269 HEAD** (`2eeccc9`) we used. It already carries the repin, the +`Body` fixes, the CLI crate, _and_ runtime Settings-loading from the config store. +This materially changes our plan. + +### 12.1 What it already implements (overlaps our work) + +| Area | His branch | Effect on us | +| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | +| #269 repin | deps pinned `2eeccc9` | our repin (#771) duplicated for Fastly | +| `Body` sink fixes | `into_bytes().ok_or_else(…)?` (prod), `unwrap_or_default()` (compress/test) | supersedes our `.expect()` style (§2) | +| Store ids | `edgezero.toml`: config `app_config`, secrets `secrets`, kv `ec_identity_store` | the shared seam, concrete | +| Config contract | `trusted-server-core/src/config_payload.rs` — flatten all of `Settings` → entries + `sha256` (`ts-config-hash`/`ts-config-keys`), reversible | shared core module; CLI pushes, runtime reads | +| **Runtime load** | adapter `main.rs`: `build_runtime_services()` → `get_settings_from_services()` reads `app_config`, rebuilds `Settings` | **this is the HTTP-layer pattern, already wired for Fastly** | +| `trusted-server.toml` | **deleted**; replaced by `trusted-server.example.toml` | config now lives in the store, seeded via `ts config push` | + +### 12.2 The HTTP-layer pattern, demonstrated + +`crates/trusted-server-adapter-fastly/src/main.rs` (his branch): + +```rust +let runtime_services = build_runtime_services(&req, kv_store); // config store available first +let settings = match get_settings_from_services(&runtime_services) { … }; // load Settings FROM store +``` + +`get_settings_from_services` → resolves the store name via +`env_config.store_name("config", DEFAULT_CONFIG_STORE_ID)` (`= "app_config"`) → +reads `ts-config-keys` / `ts-config-hash` / each entry → +`settings_from_config_entries` (verifies hash) → `Settings`. **That entry-point +sequence is exactly what "our HTTP layer" was meant to build.** + +**Crucial: he reads through the bespoke `PlatformConfigStore`, not edgezero's +store surface.** `services.config_store()` returns `&dyn PlatformConfigStore` +(impl `FastlyPlatformConfigStore`); he uses **none** of edgezero's #269 +`ConfigStore`/`StoreRegistry`/`Config` extractor/`RequestContext` (grep-confirmed: +zero in his `main.rs`/`settings_data.rs`). So he converged the config **source** +(toml → store) while **keeping `RuntimeServices` + the `platform/` layer** — i.e. +he took our §6 **hybrid** path, not full edgezero adoption. This also keeps §1's +"uses none of …" list accurate on his branch, and validates that our HTTP layer +should bind the store via `PlatformConfigStore`, not edgezero's extractor. + +### 12.3 Runtime contract (new, important) + +A **missing key is a hard error** — there is **no `trusted-server.toml` +fallback** anymore. The settings-error arm in `main.rs` **does serve a response** +(`to_error_response(&e).send_to_client(); return;`) — so an unseeded store yields +a **generic 500 on every route** (not a silent no-response), and that 500 is +**indistinguishable from a real config bug**. Net: the worker **cannot serve real +routes until the store is seeded** (`ts config push`). This is a new +deploy-ordering requirement (seed-before-serve) and an operational risk — the HTTP +layer should make the unseeded case **actionable** (clear message) and **correctly +classified** (retryable 503, not 500). See the plan's Phase 2. + +### 12.4 Conflicts to resolve (see §9) + +1. **Body-fix style** — adopt his `ok_or_else` (done in §2); align per-sink. +2. **Secret writes** — he punts key-rotation secret writes until edgezero adds + write primitives; our original migration design kept write-CRUD in TS. Decide. +3. **Base branch** — his off `main`; ours off the PR14 stack. His already carries + repin+Body, so for Fastly our minimal-repin is redundant. +4. **Whole-`Settings` vs two-tier** — he flattens _all_ of `Settings` into the + store (not our two-tier small `AppConfig`). One source of truth; bigger blast. + +### 12.5 Revised recommendation + +- **Build on his branch, don't run a parallel repin.** Our #771 minimal-repin is + superseded for Fastly; keep it only as the verified breaking-API reference. +- **Our HTTP-layer deliverable = the "runtime-config-store spec" his CLI doc + references but never wrote** — document `get_settings_from_services`, the + flatten/reconstruct rules (shared `config_payload`), the seed-before-serve + contract, empty/malformed-store behavior, and non-Fastly adapter wiring. +- **Keep our compiler-verified `Body` enumeration (§2/§10)** as the authoritative + sink reference when merging his ad-hoc fixes up the stack. From 2563490411f5cc3a9451c5d39387ec5a6c7e87a0 Mon Sep 17 00:00:00 2001 From: prk-Jr Date: Thu, 18 Jun 2026 16:43:47 +0530 Subject: [PATCH 03/15] Add edgezero #269 HTTP-layer runtime design spec --- ...-edgezero-269-http-layer-runtime-design.md | 266 ++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 docs/superpowers/specs/2026-06-18-edgezero-269-http-layer-runtime-design.md diff --git a/docs/superpowers/specs/2026-06-18-edgezero-269-http-layer-runtime-design.md b/docs/superpowers/specs/2026-06-18-edgezero-269-http-layer-runtime-design.md new file mode 100644 index 00000000..69a618cc --- /dev/null +++ b/docs/superpowers/specs/2026-06-18-edgezero-269-http-layer-runtime-design.md @@ -0,0 +1,266 @@ +# Design: EdgeZero #269 HTTP-Layer Runtime + +- **Date:** 2026-06-18 +- **Author:** Prakash (HTTP-layer / runtime). CLI side: Christian (`feature/ts-cli-next`). +- **Status:** design — base build verified green (see §6). +- **Base:** edgezero `stackpop/edgezero#269` (`feature/extensible-cli`, HEAD `2eeccc9`), + adopted on `main` via `feature/ts-cli-next`. Our work branches off it + (`feature/edgezero-269-http`). +- **Companion docs:** references + [2026-06-16-edgezero-269-repin-breaking-api-finding.md](./2026-06-16-edgezero-269-repin-breaking-api-finding.md) + for the breaking-API / `Body`-sink detail (not duplicated here). Subsumes the + plan's Phase-4 "runtime-config-store spec" — this is that, widened to all + runtime surfaces. + +--- + +## 1. Scope & base assumptions + +This spec governs the **runtime (HTTP-layer) half** of running trusted-server on +edgezero #269. It is the source of truth for how the Fastly adapter boots, loads +configuration, and serves requests under #269. + +**Convergence model (decided):** `main` is the #269 convergence point. +`ts-cli-next` (off `main`) lands first, establishing #269 on `main`; our +HTTP-layer work branches off it and also targets `main`. **The PR14→PR20 +migration stack is explicitly out of scope** — it reconciles to #269 on its own +schedule and is not a dependency of, or dependent on, this work. + +**Inherited from `ts-cli-next`** (already done, verified green — §6): + +- the #269 dependency repin (`2eeccc9`); +- the `Body::into_bytes() → Option` fixes across `trusted-server-core`; +- the **Fastly adapter migration** to the #269 API (the dual-path entry point was + removed — §2.2); +- runtime `Settings`-from-config-store load (`get_settings_from_services`). + +**What we own (add on top):** runtime hardening of the config-store load path +(§4), the non-Fastly adapter build state (§4.2), the secret-write boundary +decision (§4.3), and this spec. + +**Architecture invariant:** trusted-server keeps its **bespoke `platform/` +layer** (`RuntimeServices` + `PlatformConfigStore`/`PlatformSecretStore`/ +`PlatformKvStore`). We do **not** adopt edgezero's first-class +`ConfigStore`/`StoreRegistry`/extractor/`RequestContext`. The #269 convergence is +of the config **source** (TOML → config store), not the abstraction. + +--- + +## 2. Surface inventory + +Every surface #269 touches, with current state and owner. "Inherited" = done on +`ts-cli-next`; "ours" = HTTP-layer work in this spec. + +| Surface | State under #269 | Owner | +| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- | +| `Body::into_bytes()` → `Option` (`trusted-server-core`) | Fixed: `request_body_bytes` helper + graceful `ok_or_else` (prod) / `unwrap_or_default` (test); `body_as_reader` returns `Result`. 18 sinks (finding §2). | inherited | +| **Adapter entry flow** | Dual-path (`into_core_request` + router `oneshot` + middleware) **removed**; converts via `compat::from_fastly_request` → `route_request` (§2.2). | inherited | +| **Config-store load** | `Settings` rebuilt at boot from the `app_config` config store via the `config_payload` flatten/hash contract (§3). `trusted-server.toml` deleted. | inherited; **hardening = ours** (§4.1) | +| `secrets` store | Read via `PlatformSecretStore`; write CRUD (key rotation) via pre-existing `adapter-fastly/src/management_api.rs`. | inherited; **boundary doc = ours** (§4.3) | +| `ec_identity_store` KV | Adapter boots `UnavailableKvStore`; EC routes lazily bind the configured store at dispatch. | inherited; **regression test = ours** | +| **`fastly` 0.11 / 0.12 coexistence** | edgezero #269 pulls `fastly 0.12.1`; trusted-server core+adapter stay on `fastly 0.11.13`. Bridged at `compat::from_fastly_request` (core, 0.11). Both versions resolve in the tree (§2.3). | inherited (constraint) | +| integration-tests lockfile | Separate workspace, path-deps core; shared deps must match root. | verify (§4) | +| CI gates | fmt / clippy `-D warnings` / `cargo test --workspace` / wasm32-wasip1 — all green on base (§6). | verify per change | +| JS (`crates/js`) | Untouched by #269. | n/a | + +### 2.1 Store ids (`edgezero.toml`) + +| Kind | id | Runtime use | +| ------- | ------------------- | ----------------------------- | +| config | `app_config` | `Settings` source (§3) | +| secrets | `secrets` | signing keys; rotation writes | +| kv | `ec_identity_store` | EC identity graph | + +### 2.2 Adapter entry flow (why the dual-path is gone) + +PR14 introduced a dual-path entry: `edgezero_adapter_fastly::into_core_request` +plus an edgezero router `oneshot` and a middleware chain keyed on +`FastlyRequestContext`. Under #269 those symbols are **fastly-0.12-bound**, but +the adapter builds the request with **fastly-0.11** — an unbridgeable version +mismatch. The migrated adapter (inherited) abandons that path: it converts the +Fastly request via `trusted_server_core::compat::from_fastly_request` +(fastly-0.11, in core) and routes through `route_request`, deleting `app.rs` and +`middleware.rs`. **This spec does not revive the dual-path.** + +### 2.3 The `fastly` version split (load-bearing constraint) + +The 0.11/0.12 coexistence is **deliberate and required**, not a smell: +edgezero #269 internally uses `fastly 0.12`; trusted-server stays on `fastly +0.11`. The only safe bridge is `compat::from_fastly_request` (a core, 0.11 +function that produces the platform-neutral `http` request). **Do not** call +edgezero APIs that take/return a fastly-0.12 `Request`/`Response` directly from +adapter code built on 0.11 — that reintroduces the PR14 dead end. All +adapter↔core hand-off goes through `compat` and the bespoke `platform/` types. + +--- + +## 3. Runtime config-store load (core design) + +### 3.1 Load sequence + +At adapter boot (`crates/trusted-server-adapter-fastly/src/main.rs`): + +``` +build_runtime_services(&req, kv_store) // config store available first + → get_settings_from_services(&services) // settings_data.rs + → get_settings_from_config_store(services.config_store(), &store_name) + → read_config_entry(CONFIG_KEYS_KEY) // "ts-config-keys" + → read_config_entry(CONFIG_HASH_KEY) // "ts-config-hash" + → read_config_entry() + → settings_from_config_entries(entries) // hash verify + reconstruct +``` + +`store_name` resolves via `EnvConfig::store_name("config", DEFAULT_CONFIG_STORE_ID)` +where `DEFAULT_CONFIG_STORE_ID = "app_config"`, overridable by +`EDGEZERO__STORES__CONFIG__APP_CONFIG__NAME`. + +**Store dependency ordering (resolves the apparent §2 tension):** the **config +store is required at boot** — if `app_config` is unseeded, settings load fails and +_no_ route serves (§3.3). The **`ec_identity_store` KV is optional/lazy** — the +adapter boots `UnavailableKvStore` and EC routes bind it at dispatch, so EC-KV +being unavailable degrades only EC routes while everything else serves. The §2 +"non-EC routes still serve" resilience therefore holds **only after** config load +has succeeded. + +### 3.2 The `config_payload` contract (shared seam — reference only) + +`trusted-server-core/src/config_payload.rs` is the **single bidirectional +contract**: the CLI flattens `Settings` → config-store entries; the runtime +reconstructs from the same module. Do **not** fork it. Properties (per the CLI +design): escaped dotted keys (`\` → `\\`, `.` → `\.`); leaf values as canonical +JSON; `ts-config-keys` (sorted key array) + `ts-config-hash` (`sha256` over +settings-only entries) metadata; `ts-config-*` reserved. + +### 3.3 Behavior matrix (the contract this spec locks) + +| Situation | Current (inherited) | Target (ours) | +| --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Unseeded store** (no `ts-config-keys`) | `read_config_entry` errors → `to_error_response` serves a **generic 500** on every route; indistinguishable from a real config bug | **Actionable, classified:** detect the missing-metadata case and surface it as a **distinct error that maps to 503** (see §4.1 — a context-attach alone stays 500), with message `"config store not seeded — run \`ts config push\`"`; real config bugs stay 500 | +| Present but **missing a listed key** | `read_config_entry` **already names the key** (`failed to read … key \`{key}\``) → 500 | **No new work** — a key listed in `ts-config-keys` but absent is genuine corruption; correctly stays 500 with the key named | +| **Malformed hash** (entries don't match `ts-config-hash`) | `settings_from_config_entries` errors | keep; add a test locking it | +| **Transport/read error** (backend hiccup) | propagates → 500 | keep (genuine 500) | +| Seeded + valid | `Settings` loads | unchanged | + +### 3.4 Seed-before-serve (operational contract) + +Because `Settings` lives only in the store (no `trusted-server.toml` fallback), +**the service cannot serve real routes until `ts config push` seeds +`app_config`.** The runtime makes the unseeded state **observable** (503 + +actionable message, §3.3), but observability is not availability — the store +**must** be seeded for the service to function. + +**This bites existing production, not just fresh installs.** `trusted-server.toml` +is deleted, so the moment the #269 wasm goes live on a service whose `app_config` +store is empty, **every route 503s** — an instant outage, not an edge case. + +**Cutover migration (one-time, ordered):** + +1. Export the currently-live `Settings` to a config payload (the CLI's + `config push` flattens `Settings` via `config_payload` — §3.2). +2. **Seed `app_config` first** (`ts config push --adapter fastly`), verify the + store holds `ts-config-keys`/`ts-config-hash` + entries. +3. **Then** deploy the #269 wasm. + +Never deploy the wasm before the store is seeded. This ordering is the contract; +§5 carries it as the top risk + the rollback. + +**503 is "not provisioned," not "retry and it heals."** The unseeded state needs +an operator action (seed), so the response body must say so (`run \`ts config +push\``); do not imply auto-recovery (e.g. no misleading short `Retry-After`). + +--- + +## 4. HTTP-layer work we add + +### 4.1 Config-store load hardening (the core deliverable) + +Implement §3.3's target column, TDD-first, in `settings_data.rs` (+ the adapter +response). Reuse the existing `MemoryConfigStore` test fake. Test at **both +layers** — neither alone proves the contract: + +- **core** (`settings_data.rs` / `error.rs`): unseeded store → the new variant, + and the variant's `status_code()` == 503; malformed-hash → error. (No adapter + needed.) +- **adapter** (`trusted-server-adapter-fastly`): the unseeded error actually + reaches the client as **503** via `to_error_response` (an end-to-end check that + the variant→status mapping isn't bypassed). + +The missing-listed-key case is already covered (names the key, stays 500 — §3.3); +keep transport errors as 500. + +**Mechanism (required — a context-attach does NOT change the status).** Today the +load chain funnels every failure into `TrustedServerError::Configuration`, which +`error.rs` maps to a flat **500**; `change_context`/`attach` only adds a message, +it does **not** alter `status_code()`. So to get a 503 for the unseeded case, +pick one: + +- **(a) New error variant (preferred):** add `TrustedServerError::ConfigStoreUnseeded` + (or similar) and map it to `StatusCode::SERVICE_UNAVAILABLE` in + `error.rs` — there is precedent: the `KvStore`-unavailable arm already maps to + 503 (`error.rs:~125`). `get_settings_from_config_store` returns this variant + when the `ts-config-keys` metadata key is absent (vs a transport error, which + stays `Configuration`/500). +- **(b) Adapter status override:** in `main.rs`, match the unseeded error before + `to_error_response` and emit `FastlyResponse::from_status(503)` explicitly. + +Prefer (a) — it keeps the classification in core (testable without the adapter) +and reuses the existing 503 precedent. (Detailed steps live in the implementation +plan, not here.) + +### 4.2 Non-Fastly adapters + +`trusted-server-adapter-{cloudflare,spin}` are stubs (cloudflare/axum are absent +from the dependency graph). Goal: they **compile** under #269 — not feature +parity. Confirm what "builds" means per stub (`cargo check -p …` on host vs a +real wasm target) before acting. + +### 4.3 Secret-write boundary (decision to record) + +Runtime key-rotation secret **writes** already work via the pre-existing +`crates/trusted-server-adapter-fastly/src/management_api.rs` +(`FastlyPlatformSecretStore` CRUD). The CLI's `config push` +**does not** push secrets (deferred by the CLI design until edgezero exposes +secret-store write primitives). Net: **runtime rotation stays; CLI-driven secret +push is out of scope.** Recorded so the split is intentional. + +--- + +## 5. Risks + +| Risk | Mitigation | +| ----------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Cutover outage on existing prod** — `trusted-server.toml` deleted; deploying the #269 wasm before `app_config` is seeded 503s every route instantly | §3.4 cutover migration: **seed the store first, then deploy the wasm** (ordered); §3.3 makes the unseeded state observable | +| **No rollback path once `trusted-server.toml` is gone** | Rollback = **redeploy the pre-#269 wasm** (still reads `trusted-server.toml`); keep that build artifact available through cutover; the repin is also a single-commit revert | +| Unseeded / fresh install can't serve | §3.3 actionable 503 + §3.4 seed-before-serve | +| **Two `fastly` versions in the wasm binary** (0.11 + 0.12 coexist, §2.3) | Builds green today (§6); watch binary size / duplicate-symbol bloat; the 0.12 bump is deferred (§7), not a fix | +| **`fastly` 0.11/0.12 coexistence** misused — calling 0.12 edgezero APIs from 0.11 adapter code | §2.3: all hand-off via `compat` + `platform/`; never revive the dual-path | +| **`ts-cli-next` is unmerged WIP** (force-pushable, off `main`) | record its SHA when branching; re-base from new SHA + coordinate if it moves; keep our additions as discrete commits | +| Whole-`Settings`-in-store enlarges blast radius of a bad push | `ts-config-hash` verification + malformed-store test (§3.3) | +| `config_payload` forked / drifts | treat as read-only shared seam (§3.2); both sides import one module | +| integration-tests lockfile drift after repin reaches main | targeted `cargo update -p --precise`; never blanket | + +--- + +## 6. Verification (base, this branch) + +`feature/edgezero-269-http` off `ts-cli-next` (`14a91cc1`), pinned `2eeccc9`, +verified **green** on 2026-06-18: + +- `cargo build --workspace --all-targets` — 0 errors +- `cargo build -p trusted-server-adapter-fastly --release --target wasm32-wasip1` — ok +- `cargo test --workspace` — 1372 + 38 + 21 + 2 pass, 0 fail +- `cargo clippy --workspace --all-targets --all-features -- -D warnings` — exit 0 +- `cargo fmt --all -- --check` — clean + +The hardening (§4.1) must keep all of the above green and add its own tests. + +--- + +## 7. Out of scope + +- The PR14→PR20 migration stack and its reconciliation to #269 (separate effort). +- edgezero `run_app`/`app!`/extractor/`RequestContext` adoption (we keep the + bespoke `platform/` layer). +- CLI-driven secret push; the CLI crate itself (`ts config`/`audit`). +- Bumping trusted-server to `fastly 0.12` (the 0.11/0.12 bridge via `compat` is + the chosen design). From 7479b66fe4a4d3b2cb1794cbb13058f9eba21a17 Mon Sep 17 00:00:00 2001 From: prk-Jr Date: Thu, 18 Jun 2026 17:03:33 +0530 Subject: [PATCH 04/15] Refine runtime spec to read-vs-reconstruct 503 model and note get-Option follow-up --- ...-edgezero-269-http-layer-runtime-design.md | 107 +++++++++++------- 1 file changed, 68 insertions(+), 39 deletions(-) diff --git a/docs/superpowers/specs/2026-06-18-edgezero-269-http-layer-runtime-design.md b/docs/superpowers/specs/2026-06-18-edgezero-269-http-layer-runtime-design.md index 69a618cc..0f652eb4 100644 --- a/docs/superpowers/specs/2026-06-18-edgezero-269-http-layer-runtime-design.md +++ b/docs/superpowers/specs/2026-06-18-edgezero-269-http-layer-runtime-design.md @@ -133,13 +133,23 @@ settings-only entries) metadata; `ts-config-*` reserved. ### 3.3 Behavior matrix (the contract this spec locks) -| Situation | Current (inherited) | Target (ours) | -| --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Unseeded store** (no `ts-config-keys`) | `read_config_entry` errors → `to_error_response` serves a **generic 500** on every route; indistinguishable from a real config bug | **Actionable, classified:** detect the missing-metadata case and surface it as a **distinct error that maps to 503** (see §4.1 — a context-attach alone stays 500), with message `"config store not seeded — run \`ts config push\`"`; real config bugs stay 500 | -| Present but **missing a listed key** | `read_config_entry` **already names the key** (`failed to read … key \`{key}\``) → 500 | **No new work** — a key listed in `ts-config-keys` but absent is genuine corruption; correctly stays 500 with the key named | -| **Malformed hash** (entries don't match `ts-config-hash`) | `settings_from_config_entries` errors | keep; add a test locking it | -| **Transport/read error** (backend hiccup) | propagates → 500 | keep (genuine 500) | -| Seeded + valid | `Settings` loads | unchanged | +**The boundary is "couldn't load the config" vs "loaded it but it's corrupt"** — +because `PlatformConfigStore::get` collapses key-absent and transport failure into +the same `PlatformError::ConfigStore` (`platform.rs:50-66`), the runtime cannot +cheaply tell "unseeded" from "transient backend" today (see §4.4 for the long-term +fix). So we classify by **where** the failure occurs, not by trying to subdivide a +read error: + +| Situation | Current (inherited) | Target (ours) | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| **Config-store read failure** — `read_config_entry` returns `Err` for any reason: store unseeded (`ts-config-keys` absent), transient backend hiccup, or a listed key missing | every case → generic **500**; indistinguishable from a real bug | **503** (`SERVICE_UNAVAILABLE`) via one new `TrustedServerError` variant, actionable message `"config store unavailable or not seeded — run \`ts config push\`"`. 503 is correct: unseeded → seed it; transient → retry. | +| **Reconstruct / verify failure** — config read OK but `settings_from_config_entries` fails (hash mismatch, unparseable value) | `Configuration`/`Settings` → 500 | **500** (genuine corruption / bug) — unchanged | +| Seeded + valid | `Settings` loads | unchanged | + +The 503/500 split is exactly the read-vs-reconstruct boundary in +`get_settings_from_config_store` → `read_config_entry` (read; → 503) vs +`settings_from_config_entries` (reconstruct; → 500). No `PlatformError` change +needed. ### 3.4 Seed-before-serve (operational contract) @@ -164,9 +174,11 @@ store is empty, **every route 503s** — an instant outage, not an edge case. Never deploy the wasm before the store is seeded. This ordering is the contract; §5 carries it as the top risk + the rollback. -**503 is "not provisioned," not "retry and it heals."** The unseeded state needs -an operator action (seed), so the response body must say so (`run \`ts config -push\``); do not imply auto-recovery (e.g. no misleading short `Retry-After`). +**503 covers two cases — message must serve both.** Under option Y (§3.3) a 503 +means "couldn't load config": either **unseeded** (operator must `ts config push`) +or a **transient backend** error (genuinely retryable). The response body should +name both paths (`config store unavailable or not seeded — run \`ts config +push\``) so an operator isn't misled into only retrying when a seed is required. --- @@ -174,38 +186,55 @@ push\``); do not imply auto-recovery (e.g. no misleading short `Retry-After`). ### 4.1 Config-store load hardening (the core deliverable) -Implement §3.3's target column, TDD-first, in `settings_data.rs` (+ the adapter -response). Reuse the existing `MemoryConfigStore` test fake. Test at **both -layers** — neither alone proves the contract: +Implement §3.3's target column, TDD-first, in `settings_data.rs` + `error.rs`. +Reuse the existing `MemoryConfigStore` test fake. Test at **both layers** — +neither alone proves the contract: -- **core** (`settings_data.rs` / `error.rs`): unseeded store → the new variant, - and the variant's `status_code()` == 503; malformed-hash → error. (No adapter - needed.) -- **adapter** (`trusted-server-adapter-fastly`): the unseeded error actually - reaches the client as **503** via `to_error_response` (an end-to-end check that +- **core** (`settings_data.rs` / `error.rs`): a config-store **read failure** → + the new variant, and the variant's `status_code()` == **503**; a + malformed-hash (reconstruct failure) → stays **500**. +- **adapter** (`trusted-server-adapter-fastly`): the read-failure error actually + reaches the client as **503** via `to_error_response` (end-to-end check that the variant→status mapping isn't bypassed). -The missing-listed-key case is already covered (names the key, stays 500 — §3.3); -keep transport errors as 500. - -**Mechanism (required — a context-attach does NOT change the status).** Today the -load chain funnels every failure into `TrustedServerError::Configuration`, which -`error.rs` maps to a flat **500**; `change_context`/`attach` only adds a message, -it does **not** alter `status_code()`. So to get a 503 for the unseeded case, -pick one: - -- **(a) New error variant (preferred):** add `TrustedServerError::ConfigStoreUnseeded` - (or similar) and map it to `StatusCode::SERVICE_UNAVAILABLE` in - `error.rs` — there is precedent: the `KvStore`-unavailable arm already maps to - 503 (`error.rs:~125`). `get_settings_from_config_store` returns this variant - when the `ts-config-keys` metadata key is absent (vs a transport error, which - stays `Configuration`/500). -- **(b) Adapter status override:** in `main.rs`, match the unseeded error before - `to_error_response` and emit `FastlyResponse::from_status(503)` explicitly. - -Prefer (a) — it keeps the classification in core (testable without the adapter) -and reuses the existing 503 precedent. (Detailed steps live in the implementation -plan, not here.) +**Mechanism — one new variant, no platform-layer change (option Y).** A +`change_context`/`attach` does **not** alter `status_code()`, and +`PlatformConfigStore::get` cannot distinguish key-absent from transport error +(both `PlatformError::ConfigStore` — §4.4). So classify by **call site**: + +1. Add `TrustedServerError::ConfigStoreUnavailable` (or similar), mapped to + `StatusCode::SERVICE_UNAVAILABLE` in `error.rs` — precedent: the `KvStore` arm + already maps to 503 (`error.rs:125`). +2. In `get_settings_from_config_store` / `read_config_entry`, `change_context` + **read failures** (the `config_store.get(...)` path — `ts-config-keys`, + `ts-config-hash`, each entry) to `ConfigStoreUnavailable` (→ 503) with the + actionable message. +3. Leave `settings_from_config_entries` failures (hash mismatch, unparseable) + as `Configuration`/`Settings` (→ 500). That is the only change — the + read-vs-reconstruct boundary does the classification. + +No new `PlatformError` variant, no change to `PlatformConfigStore` or any of its +impls. (Detailed steps live in the implementation plan, not here.) + +### 4.4 Long-term: `PlatformConfigStore::get → Result>` (tracked follow-up — NOT this work) + +The reason §4.1 can't cleanly separate "unseeded" from "transient backend" is a +**pre-existing trait-shape smell**: `PlatformConfigStore::get` (defined in PR2 / +#545, on `main` — not `ts-cli-next`) returns `Result` and folds +key-absent into `Err(PlatformError::ConfigStore)`, same as a transport failure. + +The durable fix is to make absence a **value**, not an error — +`get(...) -> Result, Report>` (`Ok(None)` = absent, +`Err` = real failure). This is **exactly edgezero #269's own `ConfigStore::get` +shape** (`Result, ConfigStoreError>`), so it is also the +**store-convergence** direction (finding §6 B). With it, unseeded (`Ok(None)` on +`ts-config-keys`) becomes distinguishable from transient (`Err`) for free, and +§4.1's option Y sharpens into the precise option X **without rework**. + +**Out of scope here** (it's a pre-existing trait `ts-cli-next` only consumes, and +touches every `PlatformConfigStore` impl). Track it on the store-convergence +follow-up; optionally surface it as a non-blocking comment on the `ts-cli-next` +PR (its config-load path is the consumer that exposes the limitation). ### 4.2 Non-Fastly adapters From 57abcf81dcf67fc501f679aa3644ce65e9f0bfba Mon Sep 17 00:00:00 2001 From: prk-Jr Date: Thu, 18 Jun 2026 17:09:09 +0530 Subject: [PATCH 05/15] Add HTTP-layer runtime implementation plan --- ...6-06-18-edgezero-269-http-layer-runtime.md | 312 ++++++++++++++++++ 1 file changed, 312 insertions(+) create mode 100644 docs/superpowers/plans/2026-06-18-edgezero-269-http-layer-runtime.md diff --git a/docs/superpowers/plans/2026-06-18-edgezero-269-http-layer-runtime.md b/docs/superpowers/plans/2026-06-18-edgezero-269-http-layer-runtime.md new file mode 100644 index 00000000..a17abba1 --- /dev/null +++ b/docs/superpowers/plans/2026-06-18-edgezero-269-http-layer-runtime.md @@ -0,0 +1,312 @@ +# EdgeZero #269 HTTP-Layer Runtime 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:** Harden the runtime config-store load so an unseeded/unavailable `app_config` store returns an actionable **503** (not an opaque 500), confirm the non-Fastly adapters still build, and record the secret-write boundary — on top of the inherited #269 base. + +**Architecture:** trusted-server boots by rebuilding `Settings` from the `app_config` config store (`get_settings_from_services` → `get_settings_from_config_store` → `read_config_entry` per key → `settings_from_config_entries`). We classify failures by **call site**: a **config-store read failure** (unseeded, transient, or a missing listed key — all `PlatformConfigStore::get → Err`) maps to a new `TrustedServerError::ConfigStoreUnavailable` → **503**; a **reconstruct/verify failure** (`settings_from_config_entries`: hash mismatch, unparseable) stays `Configuration` → **500**. One new error variant, no platform-layer change (spec option Y). + +**Tech Stack:** Rust 2024, `error-stack` (`Report`), `derive_more::Display`, cargo, `wasm32-wasip1`. + +**Source spec:** [2026-06-18-edgezero-269-http-layer-runtime-design.md](../specs/2026-06-18-edgezero-269-http-layer-runtime-design.md) — §3.3 behavior matrix, §4.1 mechanism, §4.4 the `get→Option` follow-up (out of scope here). + +**Branch:** `feature/edgezero-269-http` (off `ts-cli-next` `14a91cc1`, edgezero `2eeccc9`), inherited base verified green. + +--- + +## Scope & non-goals + +**In scope:** the new `ConfigStoreUnavailable` 503 variant + read-failure classification (§4.1); core + adapter tests; malformed-hash stays-500 test; non-Fastly build check (§4.2); secret-write boundary note (§4.3). + +**Out of scope:** `PlatformConfigStore::get → Result>` convergence (spec §4.4 — pre-existing trait, store-convergence follow-up); the PR14→PR20 stack; CLI changes; edgezero extractor/`run_app` adoption. + +--- + +## File structure + +| File | Change | +| -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `crates/trusted-server-core/src/error.rs` | Add `ConfigStoreUnavailable { message: String }` variant + `status_code()` arm → `SERVICE_UNAVAILABLE` | +| `crates/trusted-server-core/src/settings_data.rs` | `read_config_entry` `change_context` → `ConfigStoreUnavailable` (actionable message); `get_settings_from_config_store` metadata reads inherit it; **reconstruct path unchanged**; add tests | +| `crates/trusted-server-adapter-fastly/src/` (test) | Adapter test: read-failure error → **503** to client via `to_error_response` | + +`settings_from_config_entries` and `config_payload.rs` are **not** touched (reconstruct/verify stays 500; shared seam read-only). + +--- + +## Task 1: Add the `ConfigStoreUnavailable` error variant (→ 503) + +**Files:** Modify `crates/trusted-server-core/src/error.rs` + +- [ ] **Step 1: Write the failing test** (in `error.rs` `#[cfg(test)]`) + +```rust +#[test] +fn config_store_unavailable_maps_to_503() { + let err = TrustedServerError::ConfigStoreUnavailable { + message: "config store unavailable or not seeded".to_string(), + }; + assert_eq!(err.status_code(), StatusCode::SERVICE_UNAVAILABLE); +} +``` + +- [ ] **Step 2: Run it — verify it fails** + +Run: `cargo test -p trusted-server-core config_store_unavailable_maps_to_503` +Expected: FAIL to compile — variant doesn't exist yet. + +- [ ] **Step 3: Add the variant + mapping** + +In the `TrustedServerError` enum, beside `Configuration { message: String }` (mirror its `#[display(...)]` style): + +```rust +/// Config store could not be read (unseeded, transient backend, or a +/// listed key missing) — the service cannot load Settings. Retryable / fix +/// by seeding the store. +#[display("Config store unavailable: {message}")] +ConfigStoreUnavailable { message: String }, +``` + +In `status_code()`, beside the `KvStore` 503 arm (`error.rs:125`): + +```rust +Self::ConfigStoreUnavailable { .. } => StatusCode::SERVICE_UNAVAILABLE, +``` + +**Also (required — or Step 4 won't compile):** `error.rs` has an existing +exhaustiveness-guard test `status_code_maps_each_error_variant_to_expected_http_response` +(~lines 246-390) whose `let _guard: fn(&TrustedServerError) = |error| match error { … }` +lists **every** variant with no wildcard. Add the new variant to that guard arm +(and a case to its `cases` array) in this step, e.g.: + +```rust +TrustedServerError::ConfigStoreUnavailable { .. } => {} +``` + +Skipping this turns Step 4 into a non-exhaustive-match **compile error**, not a pass. + +- [ ] **Step 4: Run it — verify it passes** + +Run: `cargo test -p trusted-server-core config_store_unavailable_maps_to_503` +Expected: PASS. + +- [ ] **Step 5: Commit** + +```bash +git add crates/trusted-server-core/src/error.rs +git commit -m "Add ConfigStoreUnavailable error variant mapping to 503" +``` + +--- + +## Task 2: Classify config-store read failures as `ConfigStoreUnavailable` + +**Files:** Modify `crates/trusted-server-core/src/settings_data.rs` + +Background (confirmed): `read_config_entry` wraps `config_store.get(...)` with +`change_context(TrustedServerError::Configuration { … })` today (→ 500). The +in-memory test fake is `MemoryConfigStore { entries: BTreeMap }` +(**struct literal, no `::new`**); its `get` returns `Err(PlatformError::ConfigStore)` +for a missing key — so an empty map models the unseeded store. + +- [ ] **Step 1: Write the failing tests** (in `settings_data.rs` `#[cfg(test)]`, reuse `MemoryConfigStore`) + +**Imports (required — `status_code()` is a trait method):** the test module is +`use super::*`, which does **not** bring in the status trait. Add to the test +module: `use crate::error::IntoHttpResponse;` (precedent: `proxy.rs` and +`request_signing/endpoints.rs` test modules do the same). `http::StatusCode` works +as a bare path (`http` is a direct dep) — no `use` needed for it. + +```rust +#[test] +fn unseeded_store_is_config_store_unavailable_503() { + let store = MemoryConfigStore { entries: BTreeMap::new() }; // no ts-config-keys + let err = get_settings_from_config_store(&store, &StoreName::from("app_config")) + .expect_err("unseeded store must error"); + assert_eq!( + err.current_context().status_code(), + http::StatusCode::SERVICE_UNAVAILABLE, + "unseeded/unavailable config store should be 503" + ); +} + +#[test] +fn malformed_hash_stays_500() { + // Build a valid payload, then corrupt the hash entry so reconstruct fails. + let mut payload = build_config_payload( + &Settings::from_toml(&crate_test_settings_str()).expect("should parse"), + ) + .expect("should build payload") + .entries; + payload.insert(CONFIG_HASH_KEY.to_string(), "sha256:deadbeef".to_string()); + let store = MemoryConfigStore { entries: payload }; + let err = get_settings_from_config_store(&store, &StoreName::from("app_config")) + .expect_err("hash mismatch must error"); + assert_eq!( + err.current_context().status_code(), + http::StatusCode::INTERNAL_SERVER_ERROR, + "corrupt (loaded-but-invalid) config should stay 500" + ); +} +``` + +- [ ] **Step 2: Run — verify the unseeded test fails, malformed passes** + +Run: `cargo test -p trusted-server-core -- unseeded_store_is_config_store_unavailable malformed_hash_stays_500` +Expected: `unseeded_…` FAILS (currently 500); `malformed_hash_stays_500` PASSES already (reconstruct path unchanged). If `malformed` fails, the corruption isn't reaching `settings_from_config_entries` — re-check the fixture before touching code. + +- [ ] **Step 3: Implement — flip only the read path** + +In `read_config_entry`, change the `change_context` target from +`TrustedServerError::Configuration` to the new variant with an actionable message: + +```rust +fn read_config_entry( + config_store: &dyn PlatformConfigStore, + store_name: &StoreName, + key: &str, +) -> Result> { + config_store + .get(store_name, key) + .change_context(TrustedServerError::ConfigStoreUnavailable { + message: format!( + "config store `{store_name}` unavailable or not seeded \ + (failed to read `{key}`) — run `ts config push`" + ), + }) +} +``` + +Do **not** change the `serde_json::from_str(&keys_raw)` parse (`Configuration`/500 +— that's reconstruct of metadata, genuine corruption) or `settings_from_config_entries`. + +- [ ] **Step 4: Run — both tests pass + full core suite** + +Run: `cargo test -p trusted-server-core` +Expected: the two new tests PASS; the existing `settings_data` round-trip/load tests still PASS (seeded path unchanged). + +- [ ] **Step 5: Commit** + +```bash +git add crates/trusted-server-core/src/settings_data.rs +git commit -m "Classify config-store read failures as ConfigStoreUnavailable (503)" +``` + +--- + +## Task 3: Adapter end-to-end — read failure reaches the client as 503 + +**Files:** Add a test in `crates/trusted-server-adapter-fastly/src/` (follow the existing test module style — see `route_tests.rs` for how adapter tests assert status; `to_error_response` lives in `crate::error`). + +- [ ] **Step 1: Write the failing test** + +Assert that the adapter's error→response path maps a `ConfigStoreUnavailable` +error to a 503 response. Minimal, no live store: + +```rust +#[test] +fn config_store_unavailable_error_renders_503() { + use trusted_server_core::error::TrustedServerError; + let err = error_stack::Report::new(TrustedServerError::ConfigStoreUnavailable { + message: "unseeded".to_string(), + }); + let resp = crate::error::to_error_response(&err); + assert_eq!(resp.get_status(), fastly::http::StatusCode::SERVICE_UNAVAILABLE); +} +``` + +(Confirm `to_error_response`'s exact signature/return type — adjust the call and +the status accessor to match `route_tests.rs` conventions. If `to_error_response` +takes the error by value or a different ref, follow the existing call sites.) + +- [ ] **Step 2: Run — verify it fails** (compile or assertion), then make it pass + +Run: `cargo test -p trusted-server-adapter-fastly config_store_unavailable_error_renders_503` +If it fails only because `to_error_response` already maps via `status_code()` (Task 1), it should pass once the call signature is correct — this test **locks** that the variant→503 mapping isn't bypassed by the adapter. + +- [ ] **Step 3: Commit** + +```bash +git add crates/trusted-server-adapter-fastly/src +git commit -m "Lock adapter 503 response for ConfigStoreUnavailable" +``` + +--- + +## Task 4: Confirm non-Fastly adapters still build + +**Files:** none (verification) + +- [ ] **Step 1: Determine what "builds" means per stub** + +`trusted-server-adapter-{cloudflare,spin}` are stubs (cloudflare/axum absent from +the dependency graph). Run host checks: +`cargo check -p trusted-server-adapter-cloudflare` and `-p trusted-server-adapter-spin`. +Only attempt `--target wasm32-unknown-unknown` if the crate has a real worker +entry (install the target first). + +- [ ] **Step 2: If anything breaks under #269**, apply the §4.1 / finding fix shapes (likely none — our change is additive to core). Expected: green with no edits. + +- [ ] **Step 3: Commit any fixups** (likely none). + +--- + +## Task 5: Record the secret-write boundary (§4.3) + +**Files:** Modify a doc comment near the signing/secret store wiring (no behavior change) + +- [ ] **Step 1:** Add a brief doc comment where signing secrets are read (or at + `management_api.rs`'s secret-write entry) noting: runtime key-rotation writes go + through `FastlyPlatformSecretStore` CRUD (pre-existing); CLI-driven secret push + is deferred (spec §4.3). One comment so the split is discoverable in code, not + only the spec. + +- [ ] **Step 2: Commit** + +```bash +git add crates/trusted-server-adapter-fastly/src +git commit -m "Document runtime-vs-CLI secret-write boundary" || echo "nothing to commit" +``` + +> Skip this task if the team prefers the boundary lives only in the spec. + +--- + +## Task 6: Full gate + +**Files:** none (verification) + +- [ ] **Step 1: Run the gate** + +```bash +cargo build --workspace --all-targets +cargo build -p trusted-server-adapter-fastly --release --target wasm32-wasip1 +cargo test --workspace +cargo clippy --workspace --all-targets --all-features -- -D warnings +cargo fmt --all -- --check +``` + +Expected: all green (the §6 baseline + the new tests). + +- [ ] **Step 2: integration-tests lockfile** (separate workspace, path-deps core) + +Run: `( cd crates/integration-tests && cargo build --workspace )`. Only on +shared-dep mismatch: `cargo update -p --precise ` (never +blanket). + +- [ ] **Step 3: Commit any fmt fixups** + +```bash +git add crates Cargo.toml Cargo.lock && git commit -m "Gate fixups" || echo "nothing to commit" +``` + +--- + +## Risks & notes + +| Risk / note | Handling | +| --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | +| `to_error_response` signature differs from the Task 3 sketch | Confirm against `route_tests.rs` call sites; the test is illustrative | +| The unseeded message also fires on transient backend errors | By design (spec §3.3/§3.4 option Y) — 503 covers both; message names both paths | +| `get→Option` would let us split unseeded vs transient precisely | **Out of scope** (spec §4.4) — tracked store-convergence follow-up; this plan does not block on it | +| `MemoryConfigStore` constructor | Struct literal `MemoryConfigStore { entries }` — **no `::new`** (confirmed) | From fdc7ad6627dd948169b8f8b9b8d23f4f2ce05204 Mon Sep 17 00:00:00 2001 From: prk-Jr Date: Thu, 18 Jun 2026 17:16:07 +0530 Subject: [PATCH 06/15] Add ConfigStoreUnavailable error variant mapping to 503 --- crates/trusted-server-core/src/error.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/crates/trusted-server-core/src/error.rs b/crates/trusted-server-core/src/error.rs index 1b697f68..cf10b8f3 100644 --- a/crates/trusted-server-core/src/error.rs +++ b/crates/trusted-server-core/src/error.rs @@ -26,6 +26,11 @@ pub enum TrustedServerError { #[display("Configuration error: {message}")] Configuration { message: String }, + /// Config store could not be read (unseeded, transient backend, or a listed + /// key missing) — Settings cannot be loaded. Retryable / fix by seeding. + #[display("Config store unavailable: {message}")] + ConfigStoreUnavailable { message: String }, + /// Auction orchestration error. #[display("Auction error: {message}")] Auction { message: String }, @@ -123,6 +128,7 @@ impl IntoHttpResponse for TrustedServerError { Self::InvalidHeaderValue { .. } => StatusCode::BAD_REQUEST, Self::InvalidUtf8 { .. } => StatusCode::INTERNAL_SERVER_ERROR, Self::KvStore { .. } => StatusCode::SERVICE_UNAVAILABLE, + Self::ConfigStoreUnavailable { .. } => StatusCode::SERVICE_UNAVAILABLE, Self::Prebid { .. } => StatusCode::BAD_GATEWAY, Self::Integration { .. } => StatusCode::BAD_GATEWAY, Self::Proxy { .. } => StatusCode::BAD_GATEWAY, @@ -242,6 +248,14 @@ mod tests { assert_eq!(error.user_message(), "Invalid header value"); } + #[test] + fn config_store_unavailable_maps_to_503() { + let err = TrustedServerError::ConfigStoreUnavailable { + message: "config store unavailable or not seeded".to_string(), + }; + assert_eq!(err.status_code(), StatusCode::SERVICE_UNAVAILABLE); + } + #[test] fn status_code_maps_each_error_variant_to_expected_http_response() { // Compile-time guard: adding a TrustedServerError variant without @@ -264,6 +278,7 @@ mod tests { | TrustedServerError::EdgeCookie { .. } | TrustedServerError::PartnerNotFound { .. } | TrustedServerError::RequestTooLarge { .. } + | TrustedServerError::ConfigStoreUnavailable { .. } | TrustedServerError::InsecureDefault { .. } => (), }; @@ -341,6 +356,12 @@ mod tests { }, StatusCode::SERVICE_UNAVAILABLE, ), + ( + TrustedServerError::ConfigStoreUnavailable { + message: "config store unavailable".to_string(), + }, + StatusCode::SERVICE_UNAVAILABLE, + ), ( TrustedServerError::Auction { message: "auction failed".to_string(), From 80e72af7964d20703fb8b104c6ed985470ae6e63 Mon Sep 17 00:00:00 2001 From: prk-Jr Date: Thu, 18 Jun 2026 17:19:19 +0530 Subject: [PATCH 07/15] Classify config-store read failures as ConfigStoreUnavailable (503) --- .../trusted-server-core/src/settings_data.rs | 39 +++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/crates/trusted-server-core/src/settings_data.rs b/crates/trusted-server-core/src/settings_data.rs index efcfbcd4..e200ff8e 100644 --- a/crates/trusted-server-core/src/settings_data.rs +++ b/crates/trusted-server-core/src/settings_data.rs @@ -66,10 +66,10 @@ fn read_config_entry( ) -> Result> { config_store .get(store_name, key) - .change_context(TrustedServerError::Configuration { + .change_context(TrustedServerError::ConfigStoreUnavailable { message: format!( - "failed to read Trusted Server app config key `{key}` from config store `{store_name}`" - ), + "config store `{store_name}` unavailable or not seeded (failed to read `{key}`) — run `ts config push`" + ), }) } @@ -77,6 +77,7 @@ fn read_config_entry( mod tests { use super::*; use crate::config_payload::build_config_payload; + use crate::error::IntoHttpResponse; use crate::platform::PlatformError; use crate::settings::Settings; use crate::test_support::tests::crate_test_settings_str; @@ -142,4 +143,36 @@ mod tests { "error should mention missing keys metadata" ); } + + #[test] + fn unseeded_store_is_config_store_unavailable_503() { + let store = MemoryConfigStore { + entries: BTreeMap::new(), + }; + let err = get_settings_from_config_store(&store, &StoreName::from("app_config")) + .expect_err("unseeded store must error"); + assert_eq!( + err.current_context().status_code(), + http::StatusCode::SERVICE_UNAVAILABLE, + "unseeded store read failure should map to 503" + ); + } + + #[test] + fn malformed_hash_stays_500() { + let mut entries = build_config_payload( + &Settings::from_toml(&crate_test_settings_str()).expect("should parse"), + ) + .expect("should build payload") + .entries; + entries.insert(CONFIG_HASH_KEY.to_string(), "sha256:deadbeef".to_string()); + let store = MemoryConfigStore { entries }; + let err = get_settings_from_config_store(&store, &StoreName::from("app_config")) + .expect_err("hash mismatch must error"); + assert_eq!( + err.current_context().status_code(), + http::StatusCode::INTERNAL_SERVER_ERROR, + "reconstruct/verify failure should stay 500" + ); + } } From 907ae7fdf5363011a419c64648ad10ca9a96c786 Mon Sep 17 00:00:00 2001 From: prk-Jr Date: Thu, 18 Jun 2026 17:21:41 +0530 Subject: [PATCH 08/15] Lock adapter 503 response for ConfigStoreUnavailable --- .../trusted-server-adapter-fastly/src/error.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/crates/trusted-server-adapter-fastly/src/error.rs b/crates/trusted-server-adapter-fastly/src/error.rs index 560a2e18..fc75a05a 100644 --- a/crates/trusted-server-adapter-fastly/src/error.rs +++ b/crates/trusted-server-adapter-fastly/src/error.rs @@ -17,3 +17,21 @@ pub fn to_error_response(report: &Report) -> Response { Response::from_status(root_error.status_code()) .with_body_text_plain(&format!("{}\n", root_error.user_message())) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn config_store_unavailable_renders_503() { + let report = Report::new(TrustedServerError::ConfigStoreUnavailable { + message: "config store unavailable or not seeded".to_string(), + }); + let resp = to_error_response(&report); + assert_eq!( + resp.get_status(), + fastly::http::StatusCode::SERVICE_UNAVAILABLE, + "should render 503 for ConfigStoreUnavailable" + ); + } +} From a09cadccfaebdd7510b455bea2e9278b87dc4bc9 Mon Sep 17 00:00:00 2001 From: prk-Jr Date: Thu, 18 Jun 2026 17:38:56 +0530 Subject: [PATCH 09/15] Reconcile integration-tests lockfile to edgezero #269 --- crates/integration-tests/Cargo.lock | 247 +--------------------------- 1 file changed, 3 insertions(+), 244 deletions(-) diff --git a/crates/integration-tests/Cargo.lock b/crates/integration-tests/Cargo.lock index 32d8f1db..107502e3 100644 --- a/crates/integration-tests/Cargo.lock +++ b/crates/integration-tests/Cargo.lock @@ -126,12 +126,6 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" -[[package]] -name = "arraydeque" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" - [[package]] name = "astral-tokio-tar" version = "0.5.6" @@ -277,9 +271,6 @@ name = "bitflags" version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" -dependencies = [ - "serde_core", -] [[package]] name = "bitstream-io" @@ -528,61 +519,12 @@ version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" -[[package]] -name = "config" -version = "0.15.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e68cfe19cd7d23ffde002c24ffa5cda73931913ef394d5eaaa32037dc940c0c" -dependencies = [ - "async-trait", - "convert_case 0.6.0", - "json5", - "pathdiff", - "ron", - "rust-ini", - "serde-untagged", - "serde_core", - "serde_json", - "toml", - "winnow", - "yaml-rust2", -] - [[package]] name = "const-oid" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "const-random" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" -dependencies = [ - "const-random-macro", -] - -[[package]] -name = "const-random-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom 0.2.17", - "once_cell", - "tiny-keccak", -] - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "convert_case" version = "0.10.0" @@ -665,12 +607,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crunchy" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" - [[package]] name = "crypto-bigint" version = "0.5.5" @@ -872,7 +808,7 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "convert_case 0.10.0", + "convert_case", "proc-macro2", "quote", "rustc_version", @@ -912,15 +848,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "dlv-list" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" -dependencies = [ - "const-random", -] - [[package]] name = "docker_credential" version = "1.3.3" @@ -996,7 +923,7 @@ dependencies = [ [[package]] name = "edgezero-core" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?rev=170b74b#170b74bd2c9933b7d561f7ccdb67c53b239e9527" +source = "git+https://github.com/stackpop/edgezero?rev=2eeccc9748daba92b9adf6afe4df105e79269ae9#2eeccc9748daba92b9adf6afe4df105e79269ae9" dependencies = [ "anyhow", "async-compression", @@ -1024,7 +951,7 @@ dependencies = [ [[package]] name = "edgezero-macros" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?rev=170b74b#170b74bd2c9933b7d561f7ccdb67c53b239e9527" +source = "git+https://github.com/stackpop/edgezero?rev=2eeccc9748daba92b9adf6afe4df105e79269ae9#2eeccc9748daba92b9adf6afe4df105e79269ae9" dependencies = [ "log", "proc-macro2", @@ -1113,17 +1040,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "erased-serde" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" -dependencies = [ - "serde", - "serde_core", - "typeid", -] - [[package]] name = "errno" version = "0.3.14" @@ -1505,12 +1421,6 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" - [[package]] name = "hashbrown" version = "0.15.5" @@ -1531,15 +1441,6 @@ dependencies = [ "foldhash 0.2.0", ] -[[package]] -name = "hashlink" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" -dependencies = [ - "hashbrown 0.15.5", -] - [[package]] name = "heck" version = "0.5.0" @@ -2075,17 +1976,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "json5" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" -dependencies = [ - "pest", - "pest_derive", - "serde", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -2441,16 +2331,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "ordered-multimap" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" -dependencies = [ - "dlv-list", - "hashbrown 0.14.5", -] - [[package]] name = "p256" version = "0.13.2" @@ -2519,61 +2399,12 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "pathdiff" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" - [[package]] name = "percent-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" -[[package]] -name = "pest" -version = "2.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" -dependencies = [ - "memchr", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "pest_meta" -version = "2.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" -dependencies = [ - "pest", - "sha2 0.10.9", -] - [[package]] name = "phf" version = "0.11.3" @@ -3082,20 +2913,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "ron" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4147b952f3f819eca0e99527022f7d6a8d05f111aeb0a62960c74eb283bec8fc" -dependencies = [ - "bitflags 2.11.1", - "once_cell", - "serde", - "serde_derive", - "typeid", - "unicode-ident", -] - [[package]] name = "rsa" version = "0.9.10" @@ -3116,16 +2933,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rust-ini" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7" -dependencies = [ - "cfg-if", - "ordered-multimap", -] - [[package]] name = "rustc-hash" version = "2.1.2" @@ -3376,18 +3183,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-untagged" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" -dependencies = [ - "erased-serde", - "serde", - "serde_core", - "typeid", -] - [[package]] name = "serde_core" version = "1.0.228" @@ -3849,15 +3644,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - [[package]] name = "tinystr" version = "0.8.3" @@ -4122,7 +3908,6 @@ dependencies = [ "bytes", "chacha20poly1305", "chrono", - "config", "cookie", "derive_more 2.1.1", "ed25519-dalek", @@ -4180,24 +3965,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "typeid" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" - [[package]] name = "typenum" version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" -[[package]] -name = "ucd-trie" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" - [[package]] name = "ulid" version = "1.2.1" @@ -4729,9 +4502,6 @@ name = "winnow" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" -dependencies = [ - "memchr", -] [[package]] name = "wit-bindgen" @@ -4855,17 +4625,6 @@ dependencies = [ "rustix", ] -[[package]] -name = "yaml-rust2" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2462ea039c445496d8793d052e13787f2b90e750b833afee748e601c17621ed9" -dependencies = [ - "arraydeque", - "encoding_rs", - "hashlink", -] - [[package]] name = "yoke" version = "0.8.2" From 78539cc61fdc331926626a950ba3be9b22764f97 Mon Sep 17 00:00:00 2001 From: prk-Jr Date: Thu, 18 Jun 2026 17:47:14 +0530 Subject: [PATCH 10/15] Correct config-load # Errors docs for the 503/500 split --- .../trusted-server-core/src/settings_data.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/crates/trusted-server-core/src/settings_data.rs b/crates/trusted-server-core/src/settings_data.rs index e200ff8e..8422e0a5 100644 --- a/crates/trusted-server-core/src/settings_data.rs +++ b/crates/trusted-server-core/src/settings_data.rs @@ -17,9 +17,12 @@ const DEFAULT_CONFIG_STORE_ID: &str = "app_config"; /// /// # Errors /// -/// Returns [`TrustedServerError::Configuration`] when metadata or any flattened -/// config entry is missing, cannot be read, fails hash verification, or fails -/// Trusted Server settings validation. +/// Returns [`TrustedServerError::ConfigStoreUnavailable`] (HTTP 503) when a +/// config-store entry cannot be read (store unseeded, transient backend, or a +/// listed key missing), and [`TrustedServerError::Configuration`] / +/// [`TrustedServerError::Settings`] (HTTP 500) when the read succeeds but +/// reconstruction fails (metadata unparseable, hash verification, or settings +/// validation). pub fn get_settings_from_services( services: &RuntimeServices, ) -> Result> { @@ -32,9 +35,12 @@ pub fn get_settings_from_services( /// /// # Errors /// -/// Returns [`TrustedServerError::Configuration`] when metadata or any flattened -/// config entry is missing, cannot be read, fails hash verification, or fails -/// Trusted Server settings validation. +/// Returns [`TrustedServerError::ConfigStoreUnavailable`] (HTTP 503) when a +/// config-store entry cannot be read (store unseeded, transient backend, or a +/// listed key missing), and [`TrustedServerError::Configuration`] / +/// [`TrustedServerError::Settings`] (HTTP 500) when the read succeeds but +/// reconstruction fails (metadata unparseable, hash verification, or settings +/// validation). pub fn get_settings_from_config_store( config_store: &dyn PlatformConfigStore, store_name: &StoreName, From 766b441ff6124378f41cde198d92c19d4e5a7bb1 Mon Sep 17 00:00:00 2001 From: prk-Jr Date: Thu, 18 Jun 2026 18:14:34 +0530 Subject: [PATCH 11/15] Lock actionable config-store hint rides the error chain to logs --- crates/trusted-server-core/src/settings_data.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/trusted-server-core/src/settings_data.rs b/crates/trusted-server-core/src/settings_data.rs index 8422e0a5..d15dce20 100644 --- a/crates/trusted-server-core/src/settings_data.rs +++ b/crates/trusted-server-core/src/settings_data.rs @@ -162,6 +162,13 @@ mod tests { http::StatusCode::SERVICE_UNAVAILABLE, "unseeded store read failure should map to 503" ); + // The actionable hint must ride the error chain so it reaches the + // server log (the operator's channel); the public 503 body stays + // generic by design. + assert!( + format!("{err:?}").contains("ts config push"), + "error chain should carry the actionable `ts config push` hint for logs" + ); } #[test] From bc701ae013460a4a18912063d9a63ef239f64c72 Mon Sep 17 00:00:00 2001 From: prk-Jr Date: Thu, 18 Jun 2026 18:14:34 +0530 Subject: [PATCH 12/15] Clarify 503: actionable text to logs, client body stays generic --- ...-edgezero-269-http-layer-runtime-design.md | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/docs/superpowers/specs/2026-06-18-edgezero-269-http-layer-runtime-design.md b/docs/superpowers/specs/2026-06-18-edgezero-269-http-layer-runtime-design.md index 0f652eb4..eca7b329 100644 --- a/docs/superpowers/specs/2026-06-18-edgezero-269-http-layer-runtime-design.md +++ b/docs/superpowers/specs/2026-06-18-edgezero-269-http-layer-runtime-design.md @@ -155,9 +155,9 @@ needed. Because `Settings` lives only in the store (no `trusted-server.toml` fallback), **the service cannot serve real routes until `ts config push` seeds -`app_config`.** The runtime makes the unseeded state **observable** (503 + -actionable message, §3.3), but observability is not availability — the store -**must** be seeded for the service to function. +`app_config`.** The runtime makes the unseeded state **observable** (503 status + +an actionable message in the **server logs** — §3.3), but observability is not +availability — the store **must** be seeded for the service to function. **This bites existing production, not just fresh installs.** `trusted-server.toml` is deleted, so the moment the #269 wasm goes live on a service whose `app_config` @@ -174,11 +174,16 @@ store is empty, **every route 503s** — an instant outage, not an edge case. Never deploy the wasm before the store is seeded. This ordering is the contract; §5 carries it as the top risk + the rollback. -**503 covers two cases — message must serve both.** Under option Y (§3.3) a 503 -means "couldn't load config": either **unseeded** (operator must `ts config push`) -or a **transient backend** error (genuinely retryable). The response body should -name both paths (`config store unavailable or not seeded — run \`ts config -push\``) so an operator isn't misled into only retrying when a seed is required. +**503 covers two cases; the actionable text goes to LOGS, not the client body.** +Under option Y (§3.3) a 503 means "couldn't load config": either **unseeded** +(operator must `ts config push`) or a **transient backend** error (genuinely +retryable). The actionable message — `config store \`{store}\` unavailable or not +seeded (failed to read \`{key}\`) — run \`ts config push\`` — is carried on the +error (`ConfigStoreUnavailable`'s `Display`) and surfaced in the **server log** +(`main.rs` logs the full error chain). The **client 503 body stays generic** +(`user_message()`'s catch-all, "An internal error occurred") **by design** — per +the security rule, don't leak internal tooling/paths to public clients; detail +lives server-side. So: operator-actionable in logs, safe-generic to the client. --- @@ -216,6 +221,13 @@ neither alone proves the contract: No new `PlatformError` variant, no change to `PlatformConfigStore` or any of its impls. (Detailed steps live in the implementation plan, not here.) +**Do NOT add a `user_message()` arm for this variant.** The actionable text must +reach **logs only**; the public client body stays generic via `user_message()`'s +catch-all (§3.4, security). The `ConfigStoreUnavailable` `Display` (carrying the +"run `ts config push`" message) flows to the log via `main.rs`'s error-chain dump +— that is the operator's channel. Adding a `user_message()` arm would leak +internal tooling into public 503 bodies. + ### 4.4 Long-term: `PlatformConfigStore::get → Result>` (tracked follow-up — NOT this work) The reason §4.1 can't cleanly separate "unseeded" from "transient backend" is a From a36b70b13e24fa0e79a1ec5bbe676966e9bfc534 Mon Sep 17 00:00:00 2001 From: prk-Jr Date: Thu, 18 Jun 2026 18:53:51 +0530 Subject: [PATCH 13/15] Give ConfigStoreUnavailable a store_name field and cover missing-listed-key 503 --- .../src/error.rs | 3 +- crates/trusted-server-core/src/error.rs | 8 ++++-- .../trusted-server-core/src/settings_data.rs | 28 ++++++++++++++++++- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/crates/trusted-server-adapter-fastly/src/error.rs b/crates/trusted-server-adapter-fastly/src/error.rs index fc75a05a..12215f71 100644 --- a/crates/trusted-server-adapter-fastly/src/error.rs +++ b/crates/trusted-server-adapter-fastly/src/error.rs @@ -25,7 +25,8 @@ mod tests { #[test] fn config_store_unavailable_renders_503() { let report = Report::new(TrustedServerError::ConfigStoreUnavailable { - message: "config store unavailable or not seeded".to_string(), + store_name: "app_config".to_string(), + message: "not seeded".to_string(), }); let resp = to_error_response(&report); assert_eq!( diff --git a/crates/trusted-server-core/src/error.rs b/crates/trusted-server-core/src/error.rs index cf10b8f3..4981129f 100644 --- a/crates/trusted-server-core/src/error.rs +++ b/crates/trusted-server-core/src/error.rs @@ -28,8 +28,8 @@ pub enum TrustedServerError { /// Config store could not be read (unseeded, transient backend, or a listed /// key missing) — Settings cannot be loaded. Retryable / fix by seeding. - #[display("Config store unavailable: {message}")] - ConfigStoreUnavailable { message: String }, + #[display("Config store unavailable: {store_name} - {message}")] + ConfigStoreUnavailable { store_name: String, message: String }, /// Auction orchestration error. #[display("Auction error: {message}")] @@ -251,7 +251,8 @@ mod tests { #[test] fn config_store_unavailable_maps_to_503() { let err = TrustedServerError::ConfigStoreUnavailable { - message: "config store unavailable or not seeded".to_string(), + store_name: "app_config".to_string(), + message: "not seeded".to_string(), }; assert_eq!(err.status_code(), StatusCode::SERVICE_UNAVAILABLE); } @@ -358,6 +359,7 @@ mod tests { ), ( TrustedServerError::ConfigStoreUnavailable { + store_name: "app_config".to_string(), message: "config store unavailable".to_string(), }, StatusCode::SERVICE_UNAVAILABLE, diff --git a/crates/trusted-server-core/src/settings_data.rs b/crates/trusted-server-core/src/settings_data.rs index d15dce20..92d6bdfb 100644 --- a/crates/trusted-server-core/src/settings_data.rs +++ b/crates/trusted-server-core/src/settings_data.rs @@ -73,8 +73,9 @@ fn read_config_entry( config_store .get(store_name, key) .change_context(TrustedServerError::ConfigStoreUnavailable { + store_name: store_name.to_string(), message: format!( - "config store `{store_name}` unavailable or not seeded (failed to read `{key}`) — run `ts config push`" + "unavailable or not seeded (failed to read `{key}`) — run `ts config push`" ), }) } @@ -188,4 +189,29 @@ mod tests { "reconstruct/verify failure should stay 500" ); } + + #[test] + fn missing_listed_key_is_503() { + // Metadata (`ts-config-keys` / `ts-config-hash`) reads succeed, but a key + // the metadata lists is absent — still a read failure → 503. + let mut entries = build_config_payload( + &Settings::from_toml(&crate_test_settings_str()).expect("should parse"), + ) + .expect("should build payload") + .entries; + let victim = entries + .keys() + .find(|key| !key.starts_with("ts-config-")) + .cloned() + .expect("payload should have at least one settings key"); + entries.remove(&victim); + let store = MemoryConfigStore { entries }; + let err = get_settings_from_config_store(&store, &StoreName::from("app_config")) + .expect_err("missing listed key must error"); + assert_eq!( + err.current_context().status_code(), + http::StatusCode::SERVICE_UNAVAILABLE, + "a listed key missing is a config-store read failure → 503" + ); + } } From f1695855bebc141600eb9fd9f74b6d6cde73c4f4 Mon Sep 17 00:00:00 2001 From: prk-Jr Date: Thu, 18 Jun 2026 21:12:19 +0530 Subject: [PATCH 14/15] Revert "Reconcile integration-tests lockfile to edgezero #269" This reverts commit a09cadccfaebdd7510b455bea2e9278b87dc4bc9. --- crates/integration-tests/Cargo.lock | 247 +++++++++++++++++++++++++++- 1 file changed, 244 insertions(+), 3 deletions(-) diff --git a/crates/integration-tests/Cargo.lock b/crates/integration-tests/Cargo.lock index 107502e3..32d8f1db 100644 --- a/crates/integration-tests/Cargo.lock +++ b/crates/integration-tests/Cargo.lock @@ -126,6 +126,12 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "arraydeque" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" + [[package]] name = "astral-tokio-tar" version = "0.5.6" @@ -271,6 +277,9 @@ name = "bitflags" version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +dependencies = [ + "serde_core", +] [[package]] name = "bitstream-io" @@ -519,12 +528,61 @@ version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" +[[package]] +name = "config" +version = "0.15.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e68cfe19cd7d23ffde002c24ffa5cda73931913ef394d5eaaa32037dc940c0c" +dependencies = [ + "async-trait", + "convert_case 0.6.0", + "json5", + "pathdiff", + "ron", + "rust-ini", + "serde-untagged", + "serde_core", + "serde_json", + "toml", + "winnow", + "yaml-rust2", +] + [[package]] name = "const-oid" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "convert_case" version = "0.10.0" @@ -607,6 +665,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-bigint" version = "0.5.5" @@ -808,7 +872,7 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "convert_case", + "convert_case 0.10.0", "proc-macro2", "quote", "rustc_version", @@ -848,6 +912,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + [[package]] name = "docker_credential" version = "1.3.3" @@ -923,7 +996,7 @@ dependencies = [ [[package]] name = "edgezero-core" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?rev=2eeccc9748daba92b9adf6afe4df105e79269ae9#2eeccc9748daba92b9adf6afe4df105e79269ae9" +source = "git+https://github.com/stackpop/edgezero?rev=170b74b#170b74bd2c9933b7d561f7ccdb67c53b239e9527" dependencies = [ "anyhow", "async-compression", @@ -951,7 +1024,7 @@ dependencies = [ [[package]] name = "edgezero-macros" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?rev=2eeccc9748daba92b9adf6afe4df105e79269ae9#2eeccc9748daba92b9adf6afe4df105e79269ae9" +source = "git+https://github.com/stackpop/edgezero?rev=170b74b#170b74bd2c9933b7d561f7ccdb67c53b239e9527" dependencies = [ "log", "proc-macro2", @@ -1040,6 +1113,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "erased-serde" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + [[package]] name = "errno" version = "0.3.14" @@ -1421,6 +1505,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hashbrown" version = "0.15.5" @@ -1441,6 +1531,15 @@ dependencies = [ "foldhash 0.2.0", ] +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + [[package]] name = "heck" version = "0.5.0" @@ -1976,6 +2075,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -2331,6 +2441,16 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "ordered-multimap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" +dependencies = [ + "dlv-list", + "hashbrown 0.14.5", +] + [[package]] name = "p256" version = "0.13.2" @@ -2399,12 +2519,61 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + [[package]] name = "percent-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pest" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pest_meta" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" +dependencies = [ + "pest", + "sha2 0.10.9", +] + [[package]] name = "phf" version = "0.11.3" @@ -2913,6 +3082,20 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ron" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4147b952f3f819eca0e99527022f7d6a8d05f111aeb0a62960c74eb283bec8fc" +dependencies = [ + "bitflags 2.11.1", + "once_cell", + "serde", + "serde_derive", + "typeid", + "unicode-ident", +] + [[package]] name = "rsa" version = "0.9.10" @@ -2933,6 +3116,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rust-ini" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + [[package]] name = "rustc-hash" version = "2.1.2" @@ -3183,6 +3376,18 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-untagged" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +dependencies = [ + "erased-serde", + "serde", + "serde_core", + "typeid", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -3644,6 +3849,15 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinystr" version = "0.8.3" @@ -3908,6 +4122,7 @@ dependencies = [ "bytes", "chacha20poly1305", "chrono", + "config", "cookie", "derive_more 2.1.1", "ed25519-dalek", @@ -3965,12 +4180,24 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "typenum" version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "ulid" version = "1.2.1" @@ -4502,6 +4729,9 @@ name = "winnow" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] [[package]] name = "wit-bindgen" @@ -4625,6 +4855,17 @@ dependencies = [ "rustix", ] +[[package]] +name = "yaml-rust2" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2462ea039c445496d8793d052e13787f2b90e750b833afee748e601c17621ed9" +dependencies = [ + "arraydeque", + "encoding_rs", + "hashlink", +] + [[package]] name = "yoke" version = "0.8.2" From 5d18b22463d9c3814eb54d91170ad3a166d76b90 Mon Sep 17 00:00:00 2001 From: prk-Jr Date: Sat, 20 Jun 2026 20:06:53 +0530 Subject: [PATCH 15/15] Reconcile integration-tests lockfile to edgezero 2eeccc9 --- crates/integration-tests/Cargo.lock | 247 +--------------------------- 1 file changed, 3 insertions(+), 244 deletions(-) diff --git a/crates/integration-tests/Cargo.lock b/crates/integration-tests/Cargo.lock index 32d8f1db..107502e3 100644 --- a/crates/integration-tests/Cargo.lock +++ b/crates/integration-tests/Cargo.lock @@ -126,12 +126,6 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" -[[package]] -name = "arraydeque" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" - [[package]] name = "astral-tokio-tar" version = "0.5.6" @@ -277,9 +271,6 @@ name = "bitflags" version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" -dependencies = [ - "serde_core", -] [[package]] name = "bitstream-io" @@ -528,61 +519,12 @@ version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" -[[package]] -name = "config" -version = "0.15.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e68cfe19cd7d23ffde002c24ffa5cda73931913ef394d5eaaa32037dc940c0c" -dependencies = [ - "async-trait", - "convert_case 0.6.0", - "json5", - "pathdiff", - "ron", - "rust-ini", - "serde-untagged", - "serde_core", - "serde_json", - "toml", - "winnow", - "yaml-rust2", -] - [[package]] name = "const-oid" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "const-random" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" -dependencies = [ - "const-random-macro", -] - -[[package]] -name = "const-random-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom 0.2.17", - "once_cell", - "tiny-keccak", -] - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "convert_case" version = "0.10.0" @@ -665,12 +607,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crunchy" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" - [[package]] name = "crypto-bigint" version = "0.5.5" @@ -872,7 +808,7 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "convert_case 0.10.0", + "convert_case", "proc-macro2", "quote", "rustc_version", @@ -912,15 +848,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "dlv-list" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" -dependencies = [ - "const-random", -] - [[package]] name = "docker_credential" version = "1.3.3" @@ -996,7 +923,7 @@ dependencies = [ [[package]] name = "edgezero-core" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?rev=170b74b#170b74bd2c9933b7d561f7ccdb67c53b239e9527" +source = "git+https://github.com/stackpop/edgezero?rev=2eeccc9748daba92b9adf6afe4df105e79269ae9#2eeccc9748daba92b9adf6afe4df105e79269ae9" dependencies = [ "anyhow", "async-compression", @@ -1024,7 +951,7 @@ dependencies = [ [[package]] name = "edgezero-macros" version = "0.1.0" -source = "git+https://github.com/stackpop/edgezero?rev=170b74b#170b74bd2c9933b7d561f7ccdb67c53b239e9527" +source = "git+https://github.com/stackpop/edgezero?rev=2eeccc9748daba92b9adf6afe4df105e79269ae9#2eeccc9748daba92b9adf6afe4df105e79269ae9" dependencies = [ "log", "proc-macro2", @@ -1113,17 +1040,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "erased-serde" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" -dependencies = [ - "serde", - "serde_core", - "typeid", -] - [[package]] name = "errno" version = "0.3.14" @@ -1505,12 +1421,6 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" - [[package]] name = "hashbrown" version = "0.15.5" @@ -1531,15 +1441,6 @@ dependencies = [ "foldhash 0.2.0", ] -[[package]] -name = "hashlink" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" -dependencies = [ - "hashbrown 0.15.5", -] - [[package]] name = "heck" version = "0.5.0" @@ -2075,17 +1976,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "json5" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" -dependencies = [ - "pest", - "pest_derive", - "serde", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -2441,16 +2331,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "ordered-multimap" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" -dependencies = [ - "dlv-list", - "hashbrown 0.14.5", -] - [[package]] name = "p256" version = "0.13.2" @@ -2519,61 +2399,12 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "pathdiff" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" - [[package]] name = "percent-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" -[[package]] -name = "pest" -version = "2.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" -dependencies = [ - "memchr", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "pest_meta" -version = "2.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" -dependencies = [ - "pest", - "sha2 0.10.9", -] - [[package]] name = "phf" version = "0.11.3" @@ -3082,20 +2913,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "ron" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4147b952f3f819eca0e99527022f7d6a8d05f111aeb0a62960c74eb283bec8fc" -dependencies = [ - "bitflags 2.11.1", - "once_cell", - "serde", - "serde_derive", - "typeid", - "unicode-ident", -] - [[package]] name = "rsa" version = "0.9.10" @@ -3116,16 +2933,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rust-ini" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7" -dependencies = [ - "cfg-if", - "ordered-multimap", -] - [[package]] name = "rustc-hash" version = "2.1.2" @@ -3376,18 +3183,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-untagged" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" -dependencies = [ - "erased-serde", - "serde", - "serde_core", - "typeid", -] - [[package]] name = "serde_core" version = "1.0.228" @@ -3849,15 +3644,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - [[package]] name = "tinystr" version = "0.8.3" @@ -4122,7 +3908,6 @@ dependencies = [ "bytes", "chacha20poly1305", "chrono", - "config", "cookie", "derive_more 2.1.1", "ed25519-dalek", @@ -4180,24 +3965,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "typeid" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" - [[package]] name = "typenum" version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" -[[package]] -name = "ucd-trie" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" - [[package]] name = "ulid" version = "1.2.1" @@ -4729,9 +4502,6 @@ name = "winnow" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" -dependencies = [ - "memchr", -] [[package]] name = "wit-bindgen" @@ -4855,17 +4625,6 @@ dependencies = [ "rustix", ] -[[package]] -name = "yaml-rust2" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2462ea039c445496d8793d052e13787f2b90e750b833afee748e601c17621ed9" -dependencies = [ - "arraydeque", - "encoding_rs", - "hashlink", -] - [[package]] name = "yoke" version = "0.8.2"