State<T> extractor + nested/array #[secret] support (stacked on #300)#306
Draft
aram356 wants to merge 24 commits into
Draft
State<T> extractor + nested/array #[secret] support (stacked on #300)#306aram356 wants to merge 24 commits into
aram356 wants to merge 24 commits into
Conversation
Design spec for two upstream edgezero-core/edgezero-macros primitives: - State<T> extractor + RouterBuilder::with_state for app-owned shared state - nested/array #[secret] support via path-qualified SecretField metadata Filed under docs/superpowers/specs/. Includes a maintainer-review appendix (§8) verifying every current-mechanics claim against origin/main @ 42843b1 and folding in the corrections found: http-facade use in the router plumbing, the inaccurate lib.rs re-export step, the omitted validate_excluding_secrets consumer (needs nested-ValidationErrors navigation, not a rename), the per-struct guard-enforcement rewording, and B-3 being forced to the secret_fields() fn lowering.
Adds the blockers a follow-up review found (verified against origin/main @ 42843b1) and a Go/No-Go split: - nested-AppConfig CI guard (check_no_nested_app_config.rs) must be inverted - optional-secret metadata (optional: bool) is missing from SecretField - path model must commit to owned segments (Vec/Cow), not &'static - register the app_config helper attribute in the derive - TypedSecretEntry.field_name must be owned for dotted/array paths - enforce container rename_all on nested-only parents - settle array scope before implementing Workstream A is plan-ready now; B waits on the above.
6 tasks
aram356
added a commit
to IABTechLab/trusted-server
that referenced
this pull request
Jul 3, 2026
Switch the six edgezero git deps from branch main to worktree-state-nested-secrets-spec-review (stackpop/edgezero#306, stacked on #300) to pick up the Phase 0 State<T> extractor work. cargo check-axum passes.
…erters Replaces state_inserters: Vec<Arc<dyn Fn(&mut Extensions)>> with a single state_extensions: Extensions on RouterBuilder/RouterInner. with_state inserts by type; dispatch does extensions.extend(state_extensions.clone()). Drops the StateInserter alias, one closure alloc per registered state, and one vtable call per state per request. Identical behavior: Clone+Send+Sync+'static bound, last-write-wins by TypeId.
…T_FIELDS naming An empty #[app_config()] (or bare #[app_config]) previously returned Ok(false) from nested_optin, silently NOT recursing the field and dropping the child's #[secret] metadata. Now errors, matching the documented contract; adds an app_config_empty trybuild fixture. Also renames stale SECRET_FIELDS references in comments + the rename_all diagnostic to secret_fields(), and broadens that diagnostic to mention #[app_config(nested)] children.
… path-aware secret check Push: build_config_envelope_preserves_nested_and_array_secret_names asserts nested + array secret key names survive verbatim into envelope.data. Diff: diff_typed_rejects_empty_nested_secret proves the path-aware typed_secret_checks catches an empty nested secret (naming integrations. server_side_key) before the remote-read step.
… spec + test comments
…intainer-review revised Adds the P0-C (Fastly run_app dispatch fidelity: Set-Cookie multi-value, owns_logging opt-out, raw-request pre-dispatch hook) + P0-D (app! state= injection) design, revised across three review rounds against 47a112c: P0-D reduced to a macro-only change reusing the router Extensions bag; C3 Extensions path + before-conversion scratch-bag ordering; C1 proxy response fidelity; C2 cross-adapter owns_logging + missing_trait_methods emission; full app! argument grammar.
… (app! app-state)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two independent upstream primitives, both stacked on #300 (introspection routes; base
worktree-feature+introspection-routes, auto-retargets tomainwhen #300 merges):State<T>extractor +RouterBuilder::with_state: hand app-owned shared state (Arc<AppState>) to#[action]handlers.#[secret]:#[secret]fields resolved by field path below the config root — inside sub-structs andVec<_>elements — via#[app_config(nested)].Both are breaking, self-contained changes to
edgezero-core/edgezero-macros(+ CLI/adapters for B). Every in-tree consumer is updated in this PR.Workstream A —
State<T>State<T>extractor (FromRequestby type from request extensions;Deref/DerefMut/into_inner; 500 when unregistered).RouterBuilder::with_state<T>stores app state in a singleExtensionsbag; dispatch doesrequest.extensions_mut().extend(state.clone())after the introspection injects (last-write-wins byTypeId). No macro change —#[action]composesState<T>via the genericFromRequestpath.docs/guide/handlers.md"Sharing app state".Workstream B — nested / array
#[secret]SecretField { kind, path: Vec<SecretPathSegment>, optional }(owned),AppConfigMeta::secret_fields() -> Vec<SecretField>(wasconst SECRET_FIELDS),dotted_path().#[app_config(nested)]opt-in recurses into sub-struct /Vec<_>fields, prependingField/ArrayEachonto each child'ssecret_fields(); acceptsOption<String>(→optional); rejectsOption<String>on#[secret(store_ref)];AppConfigRootbound assertion on nested children;#[serde(rename/rename_all)]guards extended along the path.secret_walkis a path navigator (nested objects, arrays per-element, optional absent/nullskip,KeyInNamedStoresibling resolved in the innermost parent, dotted[n]error paths).validate_excluding_secretsprunes secret-leaf validators through nestedStruct/Listerrors (not a flat remove).config validate/push/diff;TypedSecretEntry.field_namecarries the dotted label.#[app_config(nested)].docs/guide/configuration.md"Nested and array secrets".Tests / CI gates (all green locally)
cargo fmt,cargo clippy --workspace --all-targets --all-features -D warnings,cargo test --workspace --all-targets, feature check (fastly cloudflare spin), spinwasm32-wasip2, and the nested-AppConfig audit binary — all pass. app-demo (top-level#[secret]) still resolves. Notable new coverage:State<T>unit/router/#[action]tests;secret_walknested/array/optional/null/named-store; nested/arrayvalidate_excluding_secretspruning; derive happy-path + 6 trybuild UI fixtures; inverted-guard tests; CLI collector + nested/named-store validate tests; a full end-to-end nested + named-store resolution test through the realAppConfig<C>extractor.Notes
docs/superpowers/; the plans supersede stale shapes in the spec body.#[expect]are 3 documenteddead_codesuppressions on#[derive(AppConfig)]test fixtures (matching the file's existing pattern).