Skip to content

Adapt Mocktioneer to edgezero v0.0.7 (extensible CLI, typed config, introspection, deploy lifecycle) - #110

Open
aram356 wants to merge 51 commits into
mainfrom
feature/edgezero-extensible-cli
Open

Adapt Mocktioneer to edgezero v0.0.7 (extensible CLI, typed config, introspection, deploy lifecycle)#110
aram356 wants to merge 51 commits into
mainfrom
feature/edgezero-extensible-cli

Conversation

@aram356

@aram356 aram356 commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Closes #115

What this PR does

Adapts Mocktioneer to the released edgezero v0.0.7, which lands the extensible CLI (#269), pluggable introspection routes (#300), and the config-GC + deploy-lifecycle CLI commands. The design spec and implementation plan that opened this PR live under docs/superpowers/; the implementation follows them end to end.

Merged with main (PR #112: Rust 2024 edition + workspace metadata inheritance) — the workspace is now edition 2024.

Changes

  • Compat: pin the six edgezero-* git deps to the immutable tag v0.0.7; drop the include_str!(manifest) arg from every adapter run_app; migrate the Spin adapter to spin-sdk ~6.0 / wasm32-wasip2 (Fastly stays wasm32-wasip1). Mocktioneer implements no Adapter trait, so v0.0.5's breaking Adapter::provision change does not apply.
  • Typed config: add a validated MocktioneerConfig (bid_cpm, range(exclusive_min = 0.0)), a gitignored mocktioneer.toml with a committed mocktioneer.toml.example template.
  • CLI (mocktioneer-cli): mirrors edgezero's generated <name>-cli. Typed config validate/diff/push over MocktioneerConfig, plus every built-in command adopted as edgezero added them:
    • config gc — reclaim orphaned config-store chunks (v0.0.5; untyped, dry-run by default).
    • active-version / healthcheck / rollback — Fastly staging deploy-lifecycle (v0.0.6).
    • auth / build / deploy / provision / serve (new intentionally dropped for an in-repo CLI).
  • Runtime (fail-loud): the OpenRTB (/openrtb2/auction) and APS (/e/dtb/bid) handlers read bid_cpm from the bound config store via edgezero's typed AppConfig extractor. A fresh deploy must config push once before those endpoints serve bids — until then they return 503 config_out_of_date. FIXED_BID_CPM = 0.20 is the shipped default, not a runtime fallback. Static/creative/pixel endpoints are unaffected.
  • Config storage: config push writes the whole struct as a single canonical-JSON blob envelope (SHA-gated) under the store's key, per adapter (axum local file / cloudflare KV / fastly config store / spin KV).
  • Introspection: mount the framework-supplied /_mocktioneer/{manifest,config,routes} routes from edgezero_core::introspection (unauthenticated — restrict at the network/middleware layer; [environment.secrets] redacted). Hardening tracked in Gate unauthenticated introspection routes before sensitive config lands in the manifest #116.
  • Tests: config-seeded auction dispatched through the Fastly (Viceroy) and Cloudflare (wasm-bindgen) adapters; CORS preflight + GET-serve coverage for the introspection routes; non-finite bid_cpm guard and template↔FIXED_BID_CPM drift pinned; a clap CLI-definition assertion.
  • Plumbing: [stores.config] in the manifest + Spin runtime-config.toml; Dockerfile seeds the default config blob, pins debian:bookworm-slim, and binds 0.0.0.0; docs cover wasip2, fail-loud typed config, introspection, the remote Fastly provision → push → deploy flow, and the new CLI commands; a config validate --strict CI gate + a Docker build/smoke gate that gates the registry push on the smoke test.

Notes

CI

fmt · clippy -D warnings · test --workspace · check --features "fastly cloudflare" · config validate --strict · Docker build + smoke · Playwright · docs ESLint/Prettier — all green.


🤖 Generated with Claude Code

aram356 added 23 commits June 14, 2026 00:47
…tures, pricing docs, docker, prettier exclusion, CLI story)
…I seed, dockerfile cache-hygiene, docs build-exclusion, broader CLI docs)
…t(?Send) test impl, spin release build in verify)
@aram356
aram356 marked this pull request as draft June 16, 2026 15:36
aram356 added 6 commits June 16, 2026 08:37
…n/typed-config, add spin to agent builds, fix stale pricing/cli comments
…ush prereq to quickstarts; patch spin in local overlay
aram356 added a commit that referenced this pull request Jul 13, 2026
…dition 2024

The Spin description claimed wasm32-wasip2, but on main the adapter builds
wasip1: spin-sdk is 5.2, edgezero.toml declares wasm32-wasip1, and the CI
matrix runs the spin contract tests under wasip1 via wasmtime. wasip2 arrives
with the spin-sdk 6 migration in #110, which adds its own wasip2 CI entry.

Also update CLAUDE.md, which still declared Edition 2021.
… deploy)

The Fastly deployment guide only seeded Viceroy's local state; following the
production flow deployed fail-loud OpenRTB/APS endpoints with no config blob.
Adds the first-deploy sequence, the already-deployed resource-link caveat
(Fastly consumes [setup] only when deploy creates a new service), and a
re-push-after-config-change note.

@prk-Jr prk-Jr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

PR Review

Summary

Adapts Mocktioneer to edgezero v0.0.4 (extensible CLI, blob-model typed config, pluggable introspection routes). The change is well-scoped, thoroughly tested, and has clearly been through several review rounds. No blocking findings. Verdict is COMMENT — a handful of non-blocking questions, one build-cache cleanup, a design-level footgun to keep in view, and a stale doc line. Inline comments carry the file-specific items.

Findings

♻️ Refactor

  • Dockerfile redundant manifest COPYs — six per-crate Cargo.toml copies are superseded by COPY crates ./crates before cargo fetch, so they add no layer caching. Inline at Dockerfile:16.

❓ Questions

  • anyhow in core with no src/ usage — likely required by the AppConfig derive; please confirm, else drop. Inline at crates/mocktioneer-core/Cargo.toml:9.
  • bid_cpm accepts +Infexclusive_min = 0.0 passes infinity; upstream non-finite rejection isn't pinned by a test in this repo. Inline at crates/mocktioneer-core/src/config.rs:15.

🌱 Seeds / 📌 Out of scope

  • Unauthenticated introspection on prod adapters/_mocktioneer/{manifest,config,routes} publicly enumerable on cloudflare/fastly. Documented and safe today; footgun once anything sensitive enters the manifest. Inline at edgezero.toml:15.
  • Breaking runtime change/openrtb2/auction and /e/dtb/bid now return 503 config_out_of_date until an operator runs config push. This is the intended fail-loud behavior and is well documented, but existing deploys that upgrade will start erroring until they push. Please make sure this lands prominently in the release notes / CHANGELOG so it isn't a surprise on rollout.

⛏ Nitpicks

  • Stale docGET /_/sizes still described "with pricing" though cpm is no longer echoed. Inline at README.md:51.

👍 Praise

  • Fail-loud contract is genuinely well tested: assert_config_out_of_date (503 + Retry-After + error.kind = config_out_of_date) exercised at the route level for both OpenRTB and APS, plus config-seeded auction contract tests through the real Fastly (Viceroy) and Cloudflare (wasm-bindgen) runtimes, and a Docker smoke test that gates image publication on an actual live bid. Strong end-to-end coverage of the new config-store path.
  • Security hygiene from the base is preserved throughout: constant-time token comparison, hostname / path-injection validation on ts_domain, log sanitization, and #[serde(deny_unknown_fields)] on the typed config.

CI Status

Verified locally on the branch head:

  • fmt: PASS
  • clippy (--workspace --all-targets --all-features -D warnings): PASS
  • tests (--workspace --all-targets, host targets): PASS

Full remote CI (wasm adapter matrix, Playwright, Docker build + smoke, docs ESLint/Prettier) reported green.

Comment thread Dockerfile
Comment thread crates/mocktioneer-core/Cargo.toml Outdated
Comment thread crates/mocktioneer-core/src/config.rs
Comment thread edgezero.toml
Comment thread README.md Outdated
…d_cpm guard, trim redundant Dockerfile COPYs, correct README

- anyhow was added to mocktioneer-core by this PR but never referenced; the
  AppConfig derive does not need it. Full suite + clippy pass without it.
- Add a test pinning that edgezero's loader rejects bid_cpm = inf/-inf/nan with
  InvalidValue. range(exclusive_min = 0.0) alone lets inf through, so this
  guarantee was relied on in a doc comment but untested here.
- The six per-crate Cargo.toml COPYs were superseded by COPY crates ./crates
  before cargo fetch, buying zero cache reuse. Verified image still builds and
  the smoke test serves a bid.
- /_/sizes no longer echoes cpm; drop "with pricing".

@ChristianPavilonis ChristianPavilonis left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Summary

Reviewed PR #110 against main. All reported CI checks pass, including Rust, adapter WASM, Docker smoke, Playwright, docs, and CodeQL. I found two non-blocking P2 issues; the CORS issue is inline.

The typed-config runtime wiring otherwise looks coherent, and I found no P0/P1 issues.

Additional P2 finding (not inline)

Trusted Server full-flow guide starts Axum without seeding typed configdocs/integrations/trusted-server.md:242-243

This standalone flow starts Axum directly, but it never copies mocktioneer.toml.example or runs config push. On a fresh checkout, the OpenRTB request in the later bidstream step now returns 503 config_out_of_date, so the documented flow cannot reach its verification step. Please add the config-copy and cargo run -p mocktioneer-cli -- config push --adapter axum --yes steps before starting the server. This was folded into the review body because the documentation file is unchanged in this PR and GitHub rejected an inline position for it.

Comment thread edgezero.toml
prk-Jr
prk-Jr previously approved these changes Aug 6, 2026
…spection routes

The three /_mocktioneer/{manifest,config,routes} GET routes had no matching
OPTIONS triggers, so preflight returned 405 while the docs state every route
carries an OPTIONS handler. Adds the three OPTIONS triggers (shared
handle_options) and extends the router-level preflight test to cover them.
@aram356
aram356 dismissed stale reviews from prk-Jr and ChristianPavilonis via 2e697d1 August 6, 2026 20:49
@aram356 aram356 closed this Aug 7, 2026
@aram356 aram356 reopened this Aug 7, 2026

@prk-Jr prk-Jr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

PR Review

Summary

Adapts Mocktioneer to edgezero v0.0.4: manifest arg dropped from every run_app, Spin moved to spin-sdk 6 / wasm32-wasip2, bid_cpm lifted out of a compile-time constant into a validated MocktioneerConfig blob read through the fail-loud AppConfig extractor, a new mocktioneer-cli for typed config validate/diff/push, and framework introspection routes. 60 files, +3731/−1161.

I read every changed file and re-ran the gates locally. No correctness defect found in the Rust changes. The fail-loud migration is the risky part of this PR and it is handled carefully: the contract is asserted at the core-router level and driven end-to-end through the real Fastly and Cloudflare dispatch paths, the Docker image seeds the blob so the container still works out of the box, and the "push before you serve" prerequisite is documented on the README, both API pages, both integration pages and every adapter page.

Everything below is non-blocking. Two questions worth answering, a handful of gaps worth closing, some nits.

Findings

❓ Questions

  • Config-store read on the auction hot pathAppConfig calls ConfigStore::get per request with no caching in the extractor; the Cloudflare backend is a bare store.get(key).text() with no cacheTtl. Each auction is now a billable KV read plus cold-read latency where it used to be a constant. Axum (loads once at startup) and Fastly (edge-resident config store) are unaffected. Inline on routes.rs.
  • Docker publish step rebuilds rather than repushing — the step comment claims it publishes the exact smoke-tested image; labels are added only in that step and the guarantee depends on a gha cache hit. Inline on docker.yml.

🤔 Thoughts

  • Retag instead of rebuild to make the publish gate literal — inline on docker.yml.
  • Introspection is on by default, on all four adapters, behind Access-Control-Allow-Origin: * — nothing sensitive today, but MOCKTIONEER_PULL_TOKEN becomes world-readable the day someone files it under [environment.variables]. Inline on edgezero.toml.

♻️ Refactor / test coverage

  • examples/openrtb_request.sh and examples/aps_request.sh were missed. Both POST to now-fail-loud endpoints and default to http://127.0.0.1:7676 (Fastly), so on a fresh checkout they return 503 config_out_of_date with no hint why. Every other surface in the repo got the "push the config first" note — these two didn't. A comment near the top pointing at config push --adapter fastly --local, or a preflight that checks the status and prints the fix, would finish the sweep. (Not in the diff, so no inline anchor.)
  • Introspection GET routes have no test — only the OPTIONS preflight is asserted. Inline on endpoints.rs.
  • mocktioneer.toml.example isn't pinned to FIXED_BID_CPM — inline on the template.
  • mocktioneer-cli has no testsArgs::command().debug_assert() is one line. Inline on main.rs.

⛏ Nitpicks

  • @types/node ^26 vs Node 24.12.0 in .tool-versions; eslint 9 → 10 is unrelated scope. Inline on docs/package.json.
  • CLAUDE.md gate 5 documents a different invocation than test.yml runs. Inline.
  • what-is-mocktioneer.md row still labeled "Fixed pricing". Inline.
  • config.rs non-finite test uses fixed-name files in the shared temp dir. Inline.

📌 Out of scope

  • spin up is blocked upstream (spin-sdk 6.0.0 imports wasi:http@0.3.0-rc, no released Spin provides it). The adapter builds and passes the wasmtime contract tests, and the limitation is documented in three places — but edgezero.toml still ships a serve command that cannot work on any current runtime. Worth a tracking issue so it doesn't quietly become permanent.

👍 Praise

  • assert_config_out_of_date asserts status and Retry-After and error.kind — the compatibility contract, not a status code that could match by accident. Inline.
  • loader_rejects_non_finite_bid_cpm pins an upstream guarantee the bid_cpm doc comment leans on. inf > 0.0 is true and TOML can express bid_cpm = inf, so without edgezero's loader guard an operator could serve infinite-priced bids. Testing someone else's invariant is usually a smell; here it's the right call, and the comment says why. Inline.
  • The Fastly and Cloudflare contract tests drive a seeded blob through the real dispatch path (FastlyService / CloudflareService + with_config_handle), not just the core router — so the config-store binding at the adapter boundary is covered, which is exactly where this migration could have silently broken.
  • The Docker smoke test both gates publication and proves the 0.0.0.0 bind by hitting the mapped host port — a loopback bind would fail it. Nice touch, and it catches the class of bug that only shows up in a container.

CI Status

Verified locally against 2e697d1:

  • cargo fmt --all -- --checkPASS
  • cargo clippy --workspace --all-targets --all-features -- -D warningsPASS
  • cargo test --workspace --all-targetsPASS (170 tests: 150 + 12 + 8, 0 failed)

All GitHub checks green: cargo fmt, cargo test, fastly/cloudflare/spin wasm tests, playwright, format-docs, Docker build & push, CodeQL.

Comment thread crates/mocktioneer-core/src/routes.rs
Comment thread crates/mocktioneer-core/src/routes.rs
Comment thread crates/mocktioneer-core/src/routes.rs
Comment thread crates/mocktioneer-core/src/config.rs
Comment thread crates/mocktioneer-core/src/config.rs Outdated
Comment thread .github/workflows/docker.yml
Comment thread mocktioneer.toml.example
Comment thread CLAUDE.md Outdated
Comment thread docs/guide/what-is-mocktioneer.md Outdated
Comment thread docs/package.json Outdated
…ound 6

edgezero v0.0.5:
- Repin the six edgezero-* deps to tag v0.0.5. Mocktioneer implements no
  Adapter trait, so the breaking provision-trait change does not apply; full
  workspace + wasm targets build unchanged.
- Wire the new untyped `config gc` subcommand (run_config_gc) into
  mocktioneer-cli and document it in configuration.md.

PR #110 review (prk-Jr, all non-blocking):
- config.rs: switch the non-finite-cpm test to tempfile::tempdir() (unique dir,
  auto-cleanup); add example_template_matches_shipped_default pinning the shipped
  template against FIXED_BID_CPM.
- mocktioneer-cli: add Args::command().debug_assert() clap-validity test.
- endpoints.rs: add introspection_routes_serve_json covering the GET
  {routes,manifest,config} responses, not just the OPTIONS preflight.
- docker.yml: publish by re-tagging the smoke-tested image and pushing it,
  instead of a cache-dependent rebuild; labels move to the loaded build.
- CLAUDE.md: match the exact test.yml config-validate invocation.
- docs: relabel 'Fixed pricing' -> 'Deterministic pricing'; pin @types/node to
  ^24 to match the Node 24 toolchain.
@aram356
aram356 requested a review from prk-Jr August 18, 2026 21:00
aram356 added a commit that referenced this pull request Aug 28, 2026
…ition (#112)

* chore: inherit crate metadata from workspace and move to Rust 2024 edition

Add authors, edition, publish, and version to [workspace.package] and have
every crate inherit them, so metadata lives in one place instead of being
re-declared per crate. Each [package] block is now name, description, then the
inherited keys. Descriptions are new.

Bump the workspace edition from 2021 to 2024. The upgrade compiled clean; the
only source churn is mechanical: the 2024 style edition re-sorts imports, and
clippy's collapsible_if now collapses nested if-let pairs into let-chains
(stable in 2024).

* Updated authors

* fix: PR #112 review — Spin adapter targets wasm32-wasip1, CLAUDE.md edition 2024

The Spin description claimed wasm32-wasip2, but on main the adapter builds
wasip1: spin-sdk is 5.2, edgezero.toml declares wasm32-wasip1, and the CI
matrix runs the spin contract tests under wasip1 via wasmtime. wasip2 arrives
with the spin-sdk 6 migration in #110, which adds its own wasip2 CI entry.

Also update CLAUDE.md, which still declared Edition 2021.
…nsible-cli

# Conflicts:
#	crates/mocktioneer-adapter-fastly/tests/contract.rs
#	crates/mocktioneer-core/src/routes.rs
…lback CLI commands

edgezero v0.0.6 introduces three top-level staging-lifecycle commands
(run_active_version / run_healthcheck / run_rollback). Repin the six
edgezero-* deps to tag v0.0.6 and wire all three into mocktioneer-cli, plus
document the deploy -> verify -> roll-back loop in the Fastly adapter guide.
No breaking changes reached mocktioneer (it implements no Adapter trait);
full workspace + all wasm targets build and the suite passes.
@aram356 aram356 changed the title Adapt Mocktioneer to edgezero v0.0.4 (extensible CLI, typed config, introspection routes) Adapt Mocktioneer to edgezero v0.0.6 (extensible CLI, typed config, introspection, deploy lifecycle) Aug 31, 2026
v0.0.7 is a small release (Fastly runtime_env_config exposure #351, rollback
hint fix #352, dependency bumps) — no new CLI commands and no breaking changes
reach mocktioneer. Full workspace + all wasm targets build; suite, clippy, and
the config-validate gate pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@aram356 aram356 changed the title Adapt Mocktioneer to edgezero v0.0.6 (extensible CLI, typed config, introspection, deploy lifecycle) Adapt Mocktioneer to edgezero v0.0.7 (extensible CLI, typed config, introspection, deploy lifecycle) Sep 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Adapt Mocktioneer to edgezero v0.0.4 (extensible CLI, typed config, introspection routes)

3 participants