Skip to content

Align guide docs with the code on main - #373

Open
aram356 wants to merge 5 commits into
mainfrom
worktree-docs-align-guides-with-main
Open

aram356 wants to merge 5 commits into
mainfrom
worktree-docs-align-guides-with-main

Conversation

@aram356

@aram356 aram356 commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Summary

  • A page-by-page audit of docs/guide against the current crates found names that no longer exist, behaviour the code contradicts, and public surface with no coverage. This PR fixes what the audit turned up, so a reader following the guides lands on APIs and commands that exist and behave as described.
  • The audit ran as a rerunnable identifier/flag/link cross-check over every page plus a source-cited review per page group; 66 findings landed, each verified against the cited source line before editing.

Changes

Crate / File Change
docs/guide/adapters/fastly.md Replace phantom dispatch_with_config* with the FastlyService builder; add Custom entry points (runtime_env_config, dispatch_with_registries, RUNTIME_ENV_STORE_NAME, both footguns); buffering, -C, manifest_version = 3, [setup], proxy:: import
docs/guide/adapters/cloudflare.md CloudflareService builder; worker-build build command; context:: / proxy:: imports; wrangler kv key put; Secret Store section; kv/config merged-id collision
docs/guide/adapters/axum.md dev_server::run_app, AxumProxyClient::try_new(), bind-address precedence, EDGEZERO__LOGGING__LEVEL, owns_logging; KV Storage and Secret Store sections; KV no longer listed as unavailable
docs/guide/adapters/spin.md Logging, Proxy Client, Context Access, Testing sections; required = true vs default = ""
docs/guide/adapters/overview.md Streaming current state, proxy-header and logging lists include Spin, Spin Tests, Store Capabilities table, Adapter trait hooks in onboarding
docs/guide/streaming.md, proxying.md Only Cloudflare preserves progressive delivery; SpinProxyClient, AxumProxyClient::try_new()?
docs/guide/routing.md, handlers.md, middleware.md, kv.md Duplicate routes panic at build; Store Extractors (Kv, Config, Secrets, AppConfig<C>); FnMiddleware / middleware_arc; Axum KV file naming
docs/guide/configuration.md app! argument table; adapter metadata component / host / port; auth-* command overrides; rename_all and store_ref rules; compiling secret-resolution example
docs/guide/blob-app-config-migration.md config push --staging as the supported staging path; Axum env-var and wrangler secret put forms; canary key in examples; config.rs diff entry point
docs/guide/manifest-store-migration.md EDGEZERO__LOGGING__* rows; drop dispatch_with_*_handle
docs/guide/cli-reference.md, cli-walkthrough.md -C / --manifest-path serve and deploy forms; healthcheck outputs and token behaviour; Cloudflare --binding --local; pushed-key / pushed-store; EDGEZERO_MANIFEST scope; my-app.toml in the tree; lifecycle commands in the generated CLI
docs/guide/deploy-github-actions.md, docs/specs/edgezero-deploy-github-action.md cache/restore@v6 + cache/save@v6, no checkout; cache-key components; artifact retention and job summary
docs/guide/architecture.md, what-is-edgezero.md, roadmap.md, docs/index.md Spin in the layout, adapter list, feature table, platform table, feature cards; Spin moved to Completed; dispatch_with_config dropped from the diagram
docs/.vitepress/config.mts KV Storage added to the sidebar
crates/edgezero-adapter-axum/src/key_value_store.rs Module doc: .edgezero/kv-<slug>-<hash>.redb, not the removed .edgezero/kv.redb default

Closes

Closes #372
Closes #361
Closes #77

Test plan

  • cargo fmt --all -- --check
  • Docs build: cd docs && npm run lint && npm run format && npm run build
  • Other: every behavioural claim edited was checked against the cited source line; the identifier/flag/link cross-check over docs/guide reports only placeholder app names and third-party names
  • cargo test --workspace --all-targets (no Rust code changed; one //! comment)
  • cargo clippy --workspace --all-targets --all-features -- -D warnings (not applicable)
  • cargo check --workspace --all-targets --features "fastly cloudflare spin" (not applicable)
  • WASM builds (not applicable)
  • examples/app-demo workspace (not applicable)
  • Manual testing via edgezero serve --adapter axum (not applicable)

Checklist

  • Changes follow CLAUDE.md conventions
  • No Tokio deps added to core or adapter crates
  • Route params use {id} syntax (not :id)
  • Types imported from edgezero_core (not http crate)
  • Store wiring goes through KvRegistry / ConfigRegistry / SecretRegistry (not the legacy single-handle setters) — see spec §6.6
  • New code has tests (docs only)
  • No secrets or credentials committed

A page-by-page audit of docs/guide against the current crates found
names that no longer exist, behaviour descriptions the code contradicts,
and public surface with no coverage. This commit fixes what the audit
turned up.

Phantom names: dispatch_with_config / dispatch_with_config_handle
(fastly, cloudflare, architecture), AxumProxyClient::default(),
EDGEZERO_SECRET_ prefix, wrangler secret put --binding, diff.rs entry
point, dispatch_with_*_handle, crate-root imports for types that live
in proxy:: / context:: / dev_server::.

Behaviour: response streaming is preserved only on Cloudflare (Fastly,
Spin and Axum buffer); duplicate routes panic at build rather than
first-registered-wins; Axum honours the axum.toml port through the CLI
and reads EDGEZERO__LOGGING__LEVEL, not edgezero.toml; Axum KV files are
.edgezero/kv-<slug>-<hash>.redb; healthcheck emits status-code only when
an HTTP status arrived and degrades to service-level without a token;
EDGEZERO_MANIFEST and the missing-manifest fallback apply to
build/deploy/serve only; Cloudflare local push selects by --binding;
deploy actions use cache/restore@v6 + cache/save@v6 and no checkout;
the cache key includes the workspace path and build-args hash.

Coverage: Fastly custom entry points (runtime_env_config,
dispatch_with_registries, RUNTIME_ENV_STORE_NAME and the two footguns);
FastlyService / CloudflareService builders; store extractors (Kv,
Config, Secrets, AppConfig<C>); FnMiddleware and middleware_arc; app!
argument list; adapter metadata component/host/port and auth-* command
overrides; EDGEZERO__LOGGING__* rows; config push --staging as the
supported staging path; Spin everywhere it was missing (landing page,
platform table, architecture, roadmap, overview tests and capability
table, and Logging / Proxy / Context / Testing sections on its page);
Axum KV and Secret Store sections; Cloudflare Secret Store and the
kv/config merged-id collision; scaffold tree and generated CLI surface;
KV page added to the sidebar.
The dev server derives .edgezero/kv-<slug>-<hash>.redb for every
declared id (kv_store_path in dev_server.rs); the hard-coded
.edgezero/kv.redb default the module doc described no longer exists.
@aram356 aram356 self-assigned this Sep 6, 2026

@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 the locked PR revision against its base. The documentation alignment is broadly correct, with five actionable documentation corrections noted inline.

Comment thread docs/guide/adapters/cloudflare.md Outdated
Comment thread docs/guide/routing.md Outdated
Comment thread docs/guide/manifest-store-migration.md Outdated
Comment thread docs/guide/streaming.md Outdated
Comment thread docs/guide/cli-reference.md Outdated
Five inline comments from the PR 373 review, each checked against
source before editing.

"Cloudflare's documented manual dispatch call does not compile": the
builder's dispatch takes (req, env, ctx) and is async. Documented the
real call with a complete entrypoint example.

"Duplicate routes are not always caught at startup": only Axum builds
the router once at startup; Fastly, Cloudflare, and Spin call
build_app() per request, so the panic lands on the first request after
a successful deploy. Said so and pointed at the Axum dev server as the
pre-deploy check.

"These Fastly logging controls are not applied on the runtime-env path":
FastlyLogging::from(&EnvConfig) derives logger use from ENDPOINT alone
and hard-codes echo_stdout. Marked USE_FASTLY_LOGGER and ECHO_STDOUT as
resolved but not applied, and noted that ENDPOINT is what enables the
logger.

"Buffering adapters do not provide the claimed memory benefit": Axum
collects into an unbounded buffer and Spin caps at 16 MiB. Scoped the
memory claim to Cloudflare and described streaming on the buffering
adapters as composability only.

"EDGEZERO_MANIFEST also applies to auth": run_auth calls
load_manifest_optional and has no --manifest flag. Added auth to the
env-var scope and the missing-manifest fallback.

@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

A genuinely high-quality audit — the corrections I spot-checked held up against source almost without exception, and at least one of them (the Fastly streaming claim) removes a description that was never true of this codebase. Findings below are mostly the audit's own blind spot: places where a correction landed in one file but the identical stale text, or a directly adjacent claim, was left behind. One finding is a hard blocker because it would not compile.

😃 Praise

  • The streaming rewrite fixes a fabricated claim. stream_to_client appears nowhere in crates/ — the old text described an API that does not exist. fastly/src/response.rs:18-25 really does drain the whole Body::Stream via executor::block_on(stream.next()) into a fastly::Body before returning. Correctly scoped, and correctly left alone for Cloudflare, which genuinely streams via CfResponse::from_stream (cloudflare/src/response.rs:30).
  • The Axum bind-precedence rewrite is exact and complete, covering both paths: CLI (axum/src/cli.rs:473-540, env → edgezero.tomlaxum.tomladdr::DEFAULT_*) and direct-binary (dev_server.rs:517-519, which passes None, None so only env or default apply). The old "binds to 127.0.0.1:8787 regardless of axum.toml" was stale.
  • The Store Capabilities table is correct in every cell across all four adapters, including the gc_config_entries / staging-lifecycle exclusivity claim.
  • The cache-key component list matches resolve-project.sh:224 term for term, workspace path and build-args hash included.

Findings

Blocking

  • 🔧 Cloudflare's dispatch_with_registries is pub(crate) — inline on docs/guide/adapters/cloudflare.md:85. Would fail to compile if a reader follows the Fastly page's import pattern.

  • 🔧 Fastly custom entry point omits the owns_logging guard — inline on docs/guide/adapters/fastly.md:145. Breaks the "full parity" promise the section is built on.

  • 🔧 cargo install worker-builder installs nothing — the crate is worker-build. docs/guide/adapters/cloudflare.md:8 (unchanged by this PR) still says:

    - worker-builder: `cargo install worker-builder`
    

    This PR changed line 34 to command = "worker-build --release", matching cloudflare/src/templates/wrangler.toml.hbs:6 — correct, and now sitting 26 lines below a prerequisite command that fails. worker-builder appears nowhere else in the repo. Cheap to fix while in the file.

  • 🔧 The same stale wrangler command survives in a file this PR edited. docs/guide/manifest-store-migration.md:182 still reads Populate the namespace via `wrangler kv:key put` . This PR fixed that exact string in cloudflare.md:201 (wrangler kv key put), and the repo's own CLI uses the space form (cloudflare/src/cli.rs:134). The colon form is the deprecated pre-3.60 spelling. Same one-word fix.

Non-blocking

  • ♻️ proxying.md gained SpinProxyClient but the two bullets above it still say "Fastly and Cloudflare". The PR updated line 59; lines 56-57 were left:

    • L56 "Fastly and Cloudflare preserve streaming bodies; Axum buffers outbound bodies before sending." Spin buffers too, and harder than Axum — spin/src/proxy.rs:29 calls collect_body_bytes, which errors above 16 MiB (spin/src/response.rs:19,38-42). A reader who has just been introduced to Spin's proxy client on line 59 is left assuming it streams.
    • L57 "Fastly and Cloudflare automatically decode gzip/br responses for you." Spin does this as well — spin/src/proxy.rs:52 (decompress_body), stripping content-encoding/content-length at :66-70.

    (The Fastly/Cloudflare halves of L56 are correct: fastly/src/proxy.rs:183-193 writes chunks to a StreamingBody, cloudflare/src/proxy.rs:70-81 uses WorkerBody::from_stream. Proxy streaming and response streaming differ per adapter, which is worth not flattening.)

  • ♻️ .edgezero/kv.redb still stands in core. crates/edgezero-core/src/key_value_store.rs:915, in the put_bytes_with_ttl doc comment: "Keys never accessed after expiration remain in the database until deleted, so .edgezero/kv.redb grows without bound on long-running dev servers." This is the identical stale path the PR corrects in the Axum sibling module doc — same fix, one file over.

  • 🤔 [local_server.*] framing on fastly.md:41 — inline.

  • axum.md:272 "(geolocation)" names a gap that does not exist — inline.

  • key_value_store.rs:9 "each declared KV id" — keyed by resolved store name, not id — inline.

  • Redundant Ok(…?) on fastly.md:150 — inline.

Verification notes

Claims were checked against source rather than read for plausibility. Verified correct, among others: the FastlyService / CloudflareService builder surfaces (every method name, arity, and sync-vs-async — Fastly's dispatch is sync and consumes self, Cloudflare's is async and takes (req, env, ctx), and both docs get this right); RUNTIME_ENV_STORE_NAME and the EDGEZERO__SERVICES__<ID>__… → canonical-key translation; the two Fastly footguns, both backed by existing tests; manifest_version = 3 against the template; store extractors Kv / Config / Secrets / AppConfig<C> with .default() / .named(); FnMiddleware and middleware_arc (router.rs:155); the duplicate-route panic string (router.rs:105) and the per-adapter timing claim (build_app() per request in fastly/cloudflare/spin lib.rs, once at startup in dev_server.rs:352); EDGEZERO_MANIFEST scope, confirmed at all four load_manifest_optional call sites — run_build, run_deploy, run_serve, and auth.rs:30; --key/--staging exclusivity (config.rs:1389-1401); the healthcheck output shape, including status-code being emitted only when a status arrived (fastly/src/cli.rs:5468-5476); pushed-key / pushed-store; the app! argument table against the macro's own "expected state or owns_logging" error; the adapter metadata keys against the manifest's component/crate/host/manifest/port rejection message; wrangler kv bulk put … --binding … --local (cloudflare/src/cli.rs:474); cache/restore@v6 + cache/save@v6 with no actions/checkout in the composite actions; and if: always() on the job-summary step (deploy-fastly/action.yml:449).

Two caveats on coverage. The Fastly/Cloudflare and Axum/Spin areas were verified exhaustively, file by file. The CLI/config and core-guide areas were verified claim-by-claim via targeted source lookups rather than full reads, so a stale claim there is likelier to have been missed than in the first two.

CI Status

Run against fa40699 merged with main (593fc92):

  • cargo fmt --all -- --check: PASS
  • cargo clippy --workspace --all-targets --all-features -- -D warnings: PASS
  • cargo test --workspace --all-targets: PASS — 1421 passed, 0 failed
  • cd docs && npm run lint && npm run format && npm run build: PASS, no dead links

Two trybuild cases corroborate the configuration.md additions directly: secret_store_ref_optional.rs and secret_with_serde_container_rename_all.rs / nested_parent_rename_all.rs all exist as should-fail-to-compile fixtures, so the rename_all and non-Option store_ref rules the PR documents are genuinely enforced.

Comment thread docs/guide/adapters/cloudflare.md Outdated
Comment thread docs/guide/adapters/fastly.md Outdated
Comment thread docs/guide/adapters/fastly.md Outdated
Comment thread docs/guide/adapters/fastly.md Outdated
Comment thread docs/guide/adapters/axum.md Outdated
Comment thread crates/edgezero-adapter-axum/src/key_value_store.rs Outdated
Ten findings from the PR 373 review, each re-checked against source.

"Cloudflare's dispatch_with_registries is pub(crate)": reworded so the
name is not presented as a public entry point, unlike Fastly's.

"Fastly custom entry point omits the owns_logging guard": the example
now mirrors run_app's `use_fastly_logger && !owns_logging()` check.

"cargo install worker-builder installs nothing": the crate is
worker-build; prerequisite fixed.

"The same stale wrangler command survives": `wrangler kv:key put` in
manifest-store-migration.md is now the space form.

"proxying.md bullets still say Fastly and Cloudflare": Spin buffers
outbound bodies with a 16 MiB cap and also decodes gzip/br; both
bullets say so.

".edgezero/kv.redb still stands in core": the put_bytes_with_ttl doc
comment now names the per-store kv-<slug>-<hash>.redb file.

"[local_server.*] framing": provision writes only [setup.*], keyed by
platform name; config push --local writes the config-store stanzas,
kv/secret local seeding is hand-edited.

"(geolocation) names a gap that does not exist": parenthetical
replaced with a claim that holds.

"each declared KV id": files are keyed by resolved store name; the
module doc says store.

"Redundant Ok(...?)": dropped; dispatch_with_registries already returns
the right Result.
@aram356

aram356 commented Sep 17, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the second review in 8f55e2e. The six inline threads each have a reply. The four items that were only in the review body are also in that commit:

  • cargo install worker-builder is now cargo install worker-build (cloudflare.md prerequisites).
  • wrangler kv:key put in manifest-store-migration.md is now the space form.
  • proxying.md's two bullets now include Spin: it buffers outbound bodies with a 16 MiB cap and decodes gzip/br.
  • The .edgezero/kv.redb path in core's put_bytes_with_ttl doc comment now names the per-store kv-<slug>-<hash>.redb file.

Docs lint, format, and build pass locally; cargo fmt and the doc tests for the two touched crates pass.

@aram356
aram356 requested a review from prk-Jr September 17, 2026 06:52

@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

A page-by-page docs-accuracy audit, and the method shows: I verified a large share of the added claims against source and the hit rate is high — the per-adapter streaming matrix, the Store Capabilities table, the app! argument table, the whole CloudflareService builder, every GitHub Actions claim, and the Fastly Custom entry points section are all correct down to individual cells and guard conditions. Several edits fix snippets that previously could not compile. The two prior review rounds were addressed properly; all six findings from the last round are genuinely fixed in 8f55e2e.

The blocking findings share one shape: a claim was corrected in one file and the identical claim left standing in another. Five of the six are that. Since the value of this PR is that a reader can trust the guides, a half-corrected claim is worse than an uncorrected one — the reader now meets both versions and cannot tell which is current.

😃 Praise

  • fastly.md:119-167 (Custom entry points) — a faithful transcription of run_app_with_request_extensions down to the logging.use_fastly_logger && !A::owns_logging() guard, and both documented footguns are real and non-obvious. This is the kind of section that prevents a silent production misconfiguration.
  • overview.md:141-156 (Store Capabilities) — correct in all eight cells across four adapter crates, including the subtle config validate vs --strict split.
  • configuration.md:306-320 and cli-reference.md:240-246 — both replace text that was actively wrong; the healthcheck rewrite is exact on the token-present double verify_version_active, the tokenless caveat, and status-code= being conditional while healthy= always emits.
  • streaming.md:36-53 — correct on all four adapters; the claim it replaced (stream_to_client) had no source anywhere in the repo.
  • cli-walkthrough.md:15-17ActiveVersion / Healthcheck / Rollback were genuinely missing from the documented generated CLI.

Findings

Blocking (6)

All six are inline. In short:

  • 🔧 handlers.md:237 — the new example cannot compile: no free text() function exists anywhere in the tree.
  • 🔧 handlers.md:229-239 — the only block on the page with no use line, and edgezero_core::AppConfig (the path a reader would guess) resolves to the derive macro, not the extractor.
  • 🔧 fastly.md:221-229 — the Logging section still points at edgezero.toml; the manifest→FastlyLogging conversion has no production caller, and echo_stdout is hardcoded. This is the claim the PR fixed in axum.md.
  • 🔧 streaming.md:103 and :115-124 — untouched text contradicts the section this PR added two screens above. :103 claims decompression for all adapters; reqwest is pinned without gzip/brotli and Axum's proxy has no decode path.
  • 🔧 cli-reference.md:143, 187 — two of four provider bullets were upgraded to the scaffolded-manifest command and two left on the built-in fallback, with no indication which is which. The Axum bullet is wrong in a scaffolded project.
  • 🔧 manifest-store-migration.md:142-144dispatch_with_registries presented generically as "the public dispatch entry point"; it is pub on Fastly only, pub(crate) on Cloudflare and Spin, absent on Axum. Same class as the cloudflare.md:85 finding fixed last round.

Non-blocking

Inline: 🤔 cloudflare.md:233-244 (KV section predates the store-registry rewrite), 🤔 cloudflare.md:166-178 (duplicates/contradicts the new Secret Store section), 🤔 overview.md:35, 125 (generic dispatch claim), 🤔 cli-reference.md:611 (--service-id is required on 3 of 4), 🤔 cli-reference.md:609, 615-617 (misses the no-manifest command category), 🤔 core/key_value_store.rs:915-917 (list_keys also sweeps expired keys), plus ten ⛏ nitpicks.

Not inline:

  • cloudflare.md:260Body::streamBody::Stream; the variant is capitalised (crates/edgezero-core/src/body.rs:14). This PR fixed the same lowercase spelling in fastly.md:327 and overview.md:30 and missed this one. The substance is right: Cloudflare is the one adapter that genuinely streams, via CfResponse::from_stream (crates/edgezero-adapter-cloudflare/src/response.rs:30).
  • blob-app-config-migration.md:450 — "(diff.rs holds only the format renderers)" understates it: diff.rs also owns the diff computation collect_changes (crates/edgezero-cli/src/diff.rs:114) next to render_structured / render_json (:165, 210). Suggest "holds the change collection and the format renderers".
  • streaming.md:147 and proxying.md:63 — both "Next Steps" lists still point only at the Fastly and Cloudflare adapter guides, while this PR establishes Spin as a first-class target and adapters/spin.md is already in the sidebar (docs/.vitepress/config.mts:50). Worth adding the Spin link to both.

📌 Out of Scope

  • deploy-github-actions.md:74, 115, 130, 211, 226 — the example workflows still pin actions/checkout@v4 while the repo's own workflows are on v6/v7. Untouched by this PR, but a natural sweep for a docs-accuracy pass; fine as a follow-up issue.

CI Status

Run locally against the PR head (8f55e2e), plus all 21 GitHub checks passing:

  • cargo fmt --all -- --check: PASS
  • cargo clippy --workspace --all-targets --all-features -- -D warnings: PASS
  • cargo test --workspace --all-targets: PASS (0 failed)
  • docs: eslint PASS, prettier PASS, vitepress build PASS

One note on the test plan: it lists cargo test and cargo clippy as not applicable, but this PR does touch two .rs files (edgezero-adapter-axum/src/key_value_store.rs, edgezero-core/src/key_value_store.rs). Doc-comment-only, so the conclusion holds — both gates pass — but the boxes are checkable rather than N/A.

Comment thread docs/guide/handlers.md
) -> Result<Response, EdgeError> {
let store = kv.default().ok_or_else(|| EdgeError::internal(anyhow::anyhow!("no kv")))?;
let hits: u64 = store.get_or("hits", 0).await?;
Ok(text(format!("{} {hits}", cfg.greeting)))

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.

🔧 This example cannot compile: there is no free text() function.

The only text constructors in the tree are Body::text (crates/edgezero-core/src/body.rs:130) and the Text<T> responder, whose tuple field is private and which is built with Text::new (crates/edgezero-core/src/response.rs:50-56). rg 'pub fn text' across crates/ returns body.rs:130 and nothing else — no free text() exists in edgezero-core or in any adapter.

The rest of the snippet does check out, so this is the one line standing between a reader and a working handler: Kv::default()Option<BoundKvStore> (extractor.rs:509), get_or (key_value_store.rs:500), and impl From<KvError> for EdgeError (key_value_store.rs:817) makes the ? valid.

Fix — change the return type and the last line:

) -> Result<Text<String>, EdgeError> {
    let store = kv.default().ok_or_else(|| EdgeError::internal(anyhow::anyhow!("no kv")))?;
    let hits: u64 = store.get_or("hits", 0).await?;
    Ok(Text::new(format!("{} {hits}", cfg.greeting)))
}

Comment thread docs/guide/handlers.md
| `Secrets` | `.default()` / `.named(id)` → a secret-store handle (`require_str(name)`). |
| `AppConfig<C>` | Your typed app config, deserialised from the blob and with `#[secret]` fields resolved. See [Configuration](/guide/configuration). |

```rust

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.

🔧 No use line, and the path a reader would guess resolves to a different item.

This is the only code block on the page without an import line — every other one opens with use edgezero_core::extractor::… (lines 11, 39, 65, 86, 105, 129, 146, 167, 255).

That matters more than usual here. AppConfig, Kv, Config and Secrets live in edgezero_core::extractor (crates/edgezero-core/src/extractor.rs:481, 609, 666, 748) and are not re-exported at the crate root. And edgezero_core::AppConfig is not a missing path — it is a different item, the derive macro re-exported at crates/edgezero-core/src/lib.rs:46:

pub use edgezero_macros::{AppConfig, action, app};

So a reader who guesses the root path gets the macro, and the error message will point at the wrong thing entirely.

Fix — match the page convention (and docs/guide/blob-app-config-migration.md:36):

use edgezero_core::extractor::{AppConfig, Kv};
use edgezero_core::{EdgeError, Text};


```rust
use edgezero_adapter_fastly::FastlyProxyClient;
use edgezero_adapter_fastly::proxy::FastlyProxyClient;

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.

🔧 The Logging section just below (:221-229) is the exact stale claim this PR fixed in axum.md, left standing here. Anchoring at the nearest changed line.

It still says:

Fastly uses endpoint-based logging. Configure logging in edgezero.toml:

[adapters.fastly.logging]
endpoint = "stdout"
level = "info"
echo_stdout = true

Three things contradict it:

  1. edgezero.toml is not read at runtime on Fastly — this page says so twice, at :63-64 ("No edgezero.toml is loaded by the runtime") and :121-123. The runtime path is FastlyLogging::from(&EnvConfig) reading EDGEZERO__LOGGING__* (crates/edgezero-adapter-fastly/src/lib.rs:82-106), called from run_app_with_request_extensions at :181.
  2. From<ResolvedLoggingConfig> for FastlyLogging has no production caller. It exists at lib.rs:61-70, but its only call site is the unit test at lib.rs:329. [adapters.<id>.logging] is only ever written, by the scaffolder (crates/edgezero-cli/src/generator.rs:508-519); nothing reads it back.
  3. echo_stdout has no runtime effectlib.rs:100 hardcodes echo_stdout: true, and the doc comment at lib.rs:78-80 says so outright: "EDGEZERO__LOGGING__ECHO_STDOUT is resolved into the EnvConfig for downstream readers but is not applied here." manifest-store-migration.md:96-97 already documents this correctly.

Related, at :243-245: "Fastly logging is wired when you call init_logger (or run_app); otherwise no logger is installed" overstates run_app, which installs a logger only when use_fastly_logger is true — derived solely from endpoint.is_some() (lib.rs:96-97, guard at :181-186). A local Viceroy run with no EDGEZERO__LOGGING__ENDPOINT gets no logger. This page explains that correctly 80 lines earlier at :164-166.

Fix — mirror the shape of the axum.md:99-105 rewrite:

Fastly uses endpoint-based logging. The runtime reads its logging settings from EDGEZERO__LOGGING__LEVEL / EDGEZERO__LOGGING__ENDPOINT in the edgezero_runtime_env Config Store (see Custom entry points), not from edgezero.toml. Setting ENDPOINT is what enables the Fastly logger; with it unset, no platform logger is installed. EDGEZERO__LOGGING__ECHO_STDOUT and __USE_FASTLY_LOGGER are resolved into EnvConfig but not applied on this path — stdout echo is always on, and logger use is derived from ENDPOINT alone. An [adapters.fastly.logging] table in edgezero.toml is consumed only by edgezero new when scaffolding.

Comment thread docs/guide/streaming.md
it is produced. On the buffering adapters a streamed body is collected in full
before the response goes out: Axum into an unbounded buffer, Spin into a buffer
capped at 16 MiB (larger bodies fail), and Fastly into a host-side body that is
only sent once complete. There `Body::stream` is an API-composability convenience,

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.

🔧 This new section is correct, but two untouched sections below it now assert the opposite. Anchoring here because this is the text that makes the contradiction visible.

streaming.md:103 — "EdgeZero automatically decompresses gzip and brotli responses from upstream services", stated unconditionally. Axum does not: reqwest is pinned default-features = false, features = ["rustls", "blocking", "json"] (Cargo.toml:64) — no gzip, no brotli — and rg 'gzip|brotli|decode|decompress' crates/edgezero-adapter-axum/src/proxy.rs returns nothing. This PR correctly narrowed the twin claim to "Fastly, Cloudflare, and Spin" at docs/guide/proxying.md:57; the same fix is needed here.

streaming.md:115-124 — "Streaming is essential for … Responses larger than available memory" and "Always stream large responses rather than buffering" directly contradict line 50-52 of this section ("Body::stream is an API-composability convenience, not a memory saving"). The Chunked Transfer section at :126-142 reads the same way.

A reader who lands on Memory Considerations first takes away exactly the belief this PR set out to correct.

Fix: scope :103 to Fastly/Cloudflare/Spin, and qualify the Memory Considerations and Chunked Transfer sections — e.g. "applies to Cloudflare today; the other adapters buffer, see How Streaming Works above".

**Provider behavior:**

- **Fastly**: Runs `fastly compute serve`
- **Fastly**: Runs `fastly compute serve -C <adapter-crate-dir>`

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.

🔧 Two of these four bullets were upgraded to the scaffolded-manifest command, two were left on the built-in fallback, and the page doesn't say which is which.

There are two distinct code paths, and manifest-declared commands take precedence (crates/edgezero-cli/src/adapter.rs:111-128):

  • Fastlyfastly compute serve -C <adapter-crate-dir> is the scaffold template (crates/edgezero-adapter-fastly/src/cli.rs:57-61). The built-in fallback runs fastly compute serve with no -C, from the fastly.toml directory (fastly/src/cli.rs:4290-4302).
  • Axum — the opposite. cargo run --manifest-path <dir>/Cargo.toml is the built-in fallback (axum/src/cli.rs:392-411, path built at :636); the scaffold template is cargo run -p {crate} (axum/src/cli.rs:88-89) — which is the text this PR deleted.

Since edgezero new always emits [adapters.<name>.commands] with build/deploy/serve (crates/edgezero-cli/src/generator.rs:503), in a scaffolded project the Axum bullet is wrong and the Fastly bullet is right; outside one it is exactly the reverse. Cloudflare and Spin stay abbreviated and match neither form (templates wrangler dev --cwd {crate_dir} / spin up --from {crate_dir} --runtime-config-file … at cloudflare/src/cli.rs:40-44, spin/src/cli.rs:51-55; built-ins wrangler dev --config <wrangler.toml> at cloudflare/src/cli.rs:1122 and bare spin up at spin/src/cli.rs:1210).

Fix — name the split instead of upgrading two bullets:

**Provider behavior:** a scaffolded project's `[adapters.<name>.commands]` wins;
these are the built-in fallbacks used when that table is absent.

- **Fastly**: `fastly compute serve` (run from the `fastly.toml` directory).
  Scaffolded manifests declare `fastly compute serve -C <adapter-crate-dir>`.
- **Cloudflare**: `wrangler dev --config <wrangler.toml>`
- **Spin**: `spin up`
- **Axum**: `cargo run --manifest-path <adapter-crate-dir>/Cargo.toml`
  (scaffolded manifests declare `cargo run -p <adapter-crate>`)

or on a hand-written `Hooks` impl (`fn owns_logging() -> bool { true }`). Every
adapter's `run_app` honors it, so the app is responsible for logger setup.

### Custom entry points

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.

😃 This section is excellent, and it is the kind that prevents a silent production misconfiguration.

The snippet is a faithful transcription of run_app_with_request_extensions (crates/edgezero-adapter-fastly/src/lib.rs:171-188), down to the logging.use_fastly_logger && !A::owns_logging() guard and the endpoint.as_deref().unwrap_or("stdout") fallback, and it compiles against the real API (dispatch_with_registries(&App, FastlyRequest, StoresMetadata, &EnvConfig, F) at request.rs:337-346; init_logger(&str, LevelFilter, bool) re-exported at lib.rs:127).

RUNTIME_ENV_STORE_NAME really is public (lib.rs:49) with exactly the value "edgezero_runtime_env", and the "fixed because staged deploys link a per-service twin under that name" rationale matches both its doc comment and cli.rs:5245-5268. Both documented footguns are real and non-obvious: run_app_with_config genuinely skips runtime_env_config (lib.rs:305-321), and the empty-stores() trap is exactly what crates/edgezero-core/src/app.rs:144-150 sets up.

| [Spin](/guide/adapters/spin) | Fermyon Spin | `wasm32-wasip2` | Stable |
| [Axum](/guide/adapters/axum) | Native (Tokio) | Host | Stable |

### Store Capabilities

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.

😃 This table is correct in all eight cells, which is not a given for a matrix assembled across four adapter crates.

Fastly single_store_kinds&[] (crates/edgezero-adapter-fastly/src/cli.rs:1059-1065) with merged_id_kinds falling through to the trait default &[] (crates/edgezero-adapter/src/registry.rs:351-353); Cloudflare &["secrets"] / &["kv","config"] (cloudflare/src/cli.rs:523-528, :170-186); Spin the same (spin/src/cli.rs:456-461, :171-177); Axum &["secrets"] / default &[] (axum/src/cli.rs:354-358).

The config validate vs --strict distinction is right too: merged-id collisions run unconditionally via reject_merged_id_collisions in run_adapter_shared_checks (crates/edgezero-cli/src/config.rs:1569, called at :1529-1530), while single-store enforcement is gated behind --strict (config.rs:1531-1532, :1891-1897).

let value = ctx
.secret_store(&cfg.vault)?
.secret_store(&cfg.vault)
.ok_or_else(|| EdgeError::service_unavailable("unknown secret store"))?

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.

😃 This fixes a snippet that could not compile. secret_store_default() / secret_store(id) return Option<BoundSecretStore> (crates/edgezero-core/src/context.rs:200, 211), so the old ? was invalid. The new ok_or_else(EdgeError::service_unavailable) (crates/edgezero-core/src/error.rs:192) plus single-arg BoundSecretStore::require_str (crates/edgezero-core/src/store_registry.rs:103) and From<SecretError> for EdgeError (crates/edgezero-core/src/secret_store.rs:131) all line up.

The rename_all and "store_ref may not be Option<String>" claims just above (:295-300) match crates/edgezero-macros/src/app_config.rs:89-97 and :383-387 verbatim, including the "secret field or nested child" trigger condition.

Comment thread docs/guide/routing.md
Registering two routes that conflict for the same method (for example, two routes that both look
like `/users/{id}`) panics when the router is built with `duplicate route definition for <path>`.
When that happens depends on the adapter: Axum builds the router once at startup, so the conflict
surfaces before any traffic, but the Fastly, Cloudflare, and Spin entry points call `build_app()`

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.

😃 This adapter-lifecycle explanation is exactly right and a genuinely useful addition — it turns an abstract panic into a deployment-shaped risk a reader can act on. build_app() is called per request in Fastly (crates/edgezero-adapter-fastly/src/lib.rs:186, 314), Cloudflare (cloudflare/src/lib.rs:111) and Spin (spin/src/lib.rs:121), and exactly once at startup in Axum (axum/src/dev_server.rs:352).

Comment thread docs/guide/streaming.md
2. The adapter writes chunks sequentially to the provider's output API
3. Fastly uses `stream_to_client`, Cloudflare uses `ReadableStream`
4. The client receives data as it becomes available
2. Cloudflare wraps the stream in a `ReadableStream` (`Response::from_stream`), so

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.

😃 The per-adapter streaming matrix is correct on all four, and the claim it replaced ("Fastly uses stream_to_client") had no source anywhere in the repo.

Cloudflare CfResponse::from_stream (crates/edgezero-adapter-cloudflare/src/response.rs:24-32, with a streaming_body_converts_without_buffering test); Spin buffers via collect_body_bytes with MAX_BODY_SIZE = 16 * 1024 * 1024 and errors past it (spin/src/response.rs:19, 24-48); Axum block_on into an unbounded Vec (axum/src/response.rs:22-40); Fastly host-side body assembled before send (fastly/src/response.rs:19-26).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants