--- id: review-002-fwd16-missing-capability name: Loud error on missing capability instead of silent unauthenticated request (FWD-16) status: pending depends_on: [] scope: narrow risk: low impact: component level: implementation tags: [adapters, review-002, security, from-openapi] --- ## Description Review 002 FWD-16 [major]. The FWD-08 remediation made every *malformed* credential fail loudly, but the **missing-capability arm stays silent**: `forward.rs:200-238` — ```rust if let Some(scheme) = auth_scheme { if let Some(secret) = context.capabilities.get(namespace) { // ... four loud arms for malformed names/values } // capability == None → falls through: the request is sent // with NO credential and NO diagnostic } ``` `Capabilities::get` keys on `api_key:`/`http_token:` prefixes (alkcall `core/types.rs:97-107`), so an assembly layer that registered the secret under a wrong key form (or forgot) produces corrupted upstream 401s at call time with zero local diagnostics. This is the *most probable* misconfiguration shape and the one inconsistent arm in an otherwise-loud match — every other arm was made loud by review-001's FWD-08 fix (tested at forward.rs:1646-1736). ## Acceptance Criteria - [ ] When `auth_scheme.is_some()` and the capability is absent, the request is NOT sent; the caller gets an `INTERNAL`-family error naming the missing capability key (e.g. "capability `http_token:{namespace}` is absent; refusing to send the request unauthenticated") — message contains no secret material - [ ] When `auth_scheme.is_none()` the behavior is unchanged (unauthenticated ops stay unauthenticated) - [ ] Wire test: authed op, empty capabilities → error envelope (and the test asserts via the existing `spawn_responder`-style seam that the upstream received zero requests) - [ ] Existing FWD-08 loud-arm tests unchanged and passing - [ ] Doc note in the module doc: the loud-missing matrix now covers malformed name/value AND absent capability - [ ] `cargo test`, `cargo clippy --all-targets -- -D warnings`, `cargo fmt --check` pass ## References - docs/reviews/002-post-remediation-review.md (Part D', FWD-16) - src/adapters/forward.rs:200-238 (the silent arm), :1646-1736 (the FWD-08 test family to mirror) - docs/architecture/decisions/014-secret-material-flow-and-capability-injection.md - tasks/adapters/review-001-forward-url-safety.md (FWD-08's loud-error work this completes) ## Notes Error class choice: `INTERNAL` (misconfiguration, not caller fault) — matches the FWD-08 precedent. Non-retryable, since re-sending cannot succeed without the assembly layer changing. If a deployment legitimately runs authed-ops with *optionally*-present credentials (none known today), that would be a new config flag — do not preemptively add one; the invariant (ADR-014: credentials flow only via Capabilities) argues loud-and-closed.