Skip to content

Configuration

The edgezero.toml manifest describes an EdgeZero application, providing a single source of truth for routing, middleware, adapters, and environment configuration.

Overview

New workspaces scaffolded with edgezero new include this manifest by default. The manifest drives both runtime routing and CLI commands.

toml
[app]
name = "my-app"
entry = "crates/my-app-core"
middleware = ["edgezero_core::middleware::RequestLogger"]

[[triggers.http]]
id = "root"
path = "/"
methods = ["GET"]
handler = "my_app_core::handlers::root"

[adapters.fastly]
# Fastly-specific configuration

[adapters.cloudflare]
# Cloudflare-specific configuration

App Section

The [app] section defines application metadata:

toml
[app]
name = "demo"
entry = "crates/demo-core"
middleware = ["edgezero_core::middleware::RequestLogger"]
FieldRequiredDescription
nameNoDisplay name for the application (defaults to "EdgeZero App")
entryNoPath to the core crate containing handlers (recommended for tooling)
versionNoReserved for future compatibility; currently ignored
kindNoReserved for future compatibility; currently ignored
middlewareNoList of middleware to apply globally

Middleware

Manifest-driven middleware are applied in order before routes:

toml
[app]
middleware = [
  "edgezero_core::middleware::RequestLogger",
  "my_app_core::cors::Cors"
]

Each item must be:

  • A publicly accessible path
  • Either a unit struct or zero-argument constructor
  • Implementing edgezero_core::middleware::Middleware

HTTP Triggers

The [[triggers.http]] array defines routes:

toml
[[triggers.http]]
id = "root"
path = "/"
methods = ["GET"]
handler = "my_app_core::handlers::root"

[[triggers.http]]
id = "echo"
path = "/echo/{name}"
methods = ["GET", "POST"]
handler = "my_app_core::handlers::echo"
adapters = ["fastly", "cloudflare"]
body-mode = "buffered"
FieldRequiredDescription
idNoStable identifier for tooling
pathYesURI template ({param} for params, {*rest} for catch-all)
methodsNoAllowed HTTP methods (defaults to GET)
handlerNoPath to handler function (required for app! route wiring)
adaptersNoIntended adapter filter (metadata; app! currently ignores)
descriptionNoHuman-readable description for docs or tooling
body-modeNobuffered or stream

Adapter filters

The adapters field is currently metadata for tooling; app! wires all triggers regardless of adapter.

Environment Section

Declare environment variables and secrets:

toml
[environment]

[[environment.variables]]
name = "API_BASE_URL"
env = "API_BASE_URL"
value = "https://example.com/api"

[[environment.secrets]]
name = "API_TOKEN"
adapters = ["fastly", "cloudflare"]
env = "API_TOKEN"

Variables

FieldRequiredDescription
nameYesVariable name in application
descriptionNoHuman-readable description
envNoEnvironment key (defaults to name)
valueNoDefault value
adaptersNoLimit to specific adapters

Variables with a default value are injected when running CLI commands.

Secrets

FieldRequiredDescription
nameYesSecret name in application
descriptionNoHuman-readable description
envNoEnvironment key (defaults to name)
adaptersNoLimit to specific adapters

Secrets must be present in the environment; missing secrets abort CLI commands with an error.

These declarations are for CLI and deployment workflows. To expose a runtime secret store to request handlers, configure [stores.secrets].

Runtime Secret Stores

Use [stores.secrets] when your application reads secrets at request time via the Secrets extractor. This is separate from [[environment.secrets]]:

  • [[environment.secrets]] declares required environment variables for CLI commands
  • [stores.secrets] enables runtime secret lookup during request handling
toml
[stores.secrets]
name = "EDGEZERO_SECRETS"

[stores.secrets.adapters.fastly]
name = "MY_FASTLY_SECRETS"

Global Fields

FieldRequiredDescription
enabledNoWhether secrets are enabled for adapters without overrides (defaults to true when the section is present)
nameNoStore or binding name (defaults to EDGEZERO_SECRETS)

Per-Adapter Overrides

FieldRequiredDescription
adapters.<adapter>.enabledNoOverride whether that adapter exposes secrets
adapters.<adapter>.nameNoOverride the adapter-specific store name

Adapter Behavior

  • Axum reads secrets from process environment variables of the same name.
  • Fastly opens the configured secret store name from fastly.toml.
  • Cloudflare reads Worker Secrets individually; the configured name is metadata only.

If [stores.secrets] is omitted, the Secrets extractor is not attached for that adapter.

Stores Section

Use [stores.config] for small read-only runtime configuration such as feature flags, JWKS metadata, or service settings:

toml
[stores.config]
name = "app_config"

[stores.config.defaults]
"greeting" = "hello from config store"
"service.timeout_ms" = "1500"

[stores.config.adapters.cloudflare]
name = "app_config"
FieldRequiredDescription
nameNoGlobal store or binding name; if omitted but the section is present, adapters fall back to EDGEZERO_CONFIG
adaptersNoPer-adapter name overrides, keyed by supported lowercase adapter name (axum, cloudflare, fastly)
defaultsNoLocal default values used by the Axum adapter when env vars are absent; this key set is also Axum's env allowlist

Runtime behavior by adapter:

  • Fastly reads from a Fastly Config Store resource link.
  • Cloudflare reads from a single JSON string binding in wrangler.toml [vars].
  • Axum reads only the env vars declared in defaults, then falls back to defaults.

When [stores.config] is present, the app! macro generates config-store metadata on the App type. The standard adapter run_app helpers use that metadata to inject a config-store handle into request extensions automatically, so handlers can call ctx.config_store().

Treat config-store keys like API surface: validate or allowlist any user-controlled lookup before calling ctx.config_store()?.get(...).

Adapters Section

Each adapter has its own configuration block:

toml
[adapters.fastly.adapter]
crate = "crates/demo-adapter-fastly"
manifest = "crates/demo-adapter-fastly/fastly.toml"

[adapters.fastly.build]
target = "wasm32-wasip1"
profile = "release"

[adapters.fastly.commands]
build = "cargo build --release --target wasm32-wasip1 -p demo-adapter-fastly"
serve = "fastly compute serve -C crates/demo-adapter-fastly"
deploy = "fastly compute deploy -C crates/demo-adapter-fastly"

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

Adapter Metadata

FieldDescription
cratePath to adapter crate
manifestPath to provider manifest (fastly.toml, wrangler.toml)

Build Configuration

FieldDescription
targetRust compilation target
profileBuild profile (release, dev)
featuresCargo features to enable

Commands

FieldDescription
buildCommand for edgezero build --adapter <name>
serveCommand for edgezero serve --adapter <name>
deployCommand for edgezero deploy --adapter <name>

When commands are omitted, the CLI falls back to built-in adapter helpers.

Logging

Logging can be configured per adapter under [adapters.<name>.logging] or via a top-level [logging.<name>] block. If both are present, the adapter-specific block takes precedence.

FieldAdaptersDescription
endpointFastlyLog endpoint name
levelAllLog level: trace, debug, info, warn, error, off
echo_stdoutFastly, AxumMirror logs to stdout

Note: Cloudflare logging is not wired to a built-in logger yet.

Full Example

toml
[app]
name = "my-app"
entry = "crates/my-app-core"
middleware = [
  "edgezero_core::middleware::RequestLogger",
  "my_app_core::middleware::Cors"
]

[[triggers.http]]
id = "root"
path = "/"
methods = ["GET"]
handler = "my_app_core::handlers::root"

[[triggers.http]]
id = "echo"
path = "/echo/{name}"
methods = ["GET"]
handler = "my_app_core::handlers::echo"

[[triggers.http]]
id = "api"
path = "/api/{*rest}"
methods = ["GET", "POST", "PUT", "DELETE"]
handler = "my_app_core::handlers::api_proxy"
body-mode = "stream"

[environment]

[[environment.variables]]
name = "API_URL"
value = "https://api.example.com"

[[environment.secrets]]
name = "API_KEY"

[stores.secrets]
name = "EDGEZERO_SECRETS"

[adapters.fastly.adapter]
crate = "crates/my-app-adapter-fastly"
manifest = "crates/my-app-adapter-fastly/fastly.toml"

[adapters.fastly.build]
target = "wasm32-wasip1"
profile = "release"

[adapters.fastly.commands]
build = "fastly build -C crates/my-app-adapter-fastly"
deploy = "fastly compute deploy -C crates/my-app-adapter-fastly"
serve = "fastly compute serve -C crates/my-app-adapter-fastly"

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

[adapters.cloudflare.adapter]
crate = "crates/my-app-adapter-cloudflare"
manifest = "crates/my-app-adapter-cloudflare/wrangler.toml"

[adapters.cloudflare.build]
target = "wasm32-unknown-unknown"
profile = "release"

[adapters.cloudflare.commands]
build = "wrangler build --cwd crates/my-app-adapter-cloudflare"
deploy = "wrangler deploy --cwd crates/my-app-adapter-cloudflare"
serve = "wrangler dev --cwd crates/my-app-adapter-cloudflare"

[adapters.cloudflare.logging]
level = "info"

[adapters.axum.adapter]
crate = "crates/my-app-adapter-axum"
manifest = "crates/my-app-adapter-axum/axum.toml"

[adapters.axum.commands]
build = "cargo build --release -p my-app-adapter-axum"
serve = "cargo run -p my-app-adapter-axum"

Using the Manifest

app! Macro

Generate router wiring from the manifest:

rust
// In your core crate's lib.rs
mod handlers;

edgezero_core::app!("../../edgezero.toml");

The macro:

  • Parses HTTP triggers
  • Generates route registration
  • Wires middleware from the manifest
  • Generates config-store metadata from [stores.config] when present
  • Creates the App struct that implements Hooks (use App::build_app())

ManifestLoader

Load the manifest programmatically:

rust
use edgezero_core::manifest::ManifestLoader;
use std::path::Path;

let manifest = ManifestLoader::from_path(Path::new("edgezero.toml"))?;
let app_name = manifest
    .manifest()
    .app
    .name
    .as_deref()
    .unwrap_or("EdgeZero App");
println!("App name: {}", app_name);

Validation

ManifestLoader validates:

  • Non-empty string fields when present (names, paths, commands)
  • Supported HTTP methods and body-mode values
  • Well-formed logging levels and adapter logging config

Errors are surfaced at startup or during macro expansion.

Next Steps

Released under the Apache License 2.0.