feat(intake): gated keyring GET /keys metadata list + same-origin proxy (Phase 2)

What + why

Phase 2 (P2.2) of epic &60 (closed). To test the signed-API onboarding (register → approve → sign), an operator needs to see which keys exist + their lifecycle status. The craig-intake-keyring sidecar holds the keys but exposes no list route. Add a gated, metadata-only GET /keys on the keyring + a same-origin intake proxy GET /signed/v1/keys (the browser never hits the keyring directly — ADR-042 §D8 internal-only).

Plan: docs/modules/ROOT/pages/plans/craig-intake-portal-phase2-integration-aids.adoc (§ P2.2).

Design (see plan for detail)

  • Keyring: KeyringState::list_all() (sorted, deterministic) + DTO SignerKeyListEntry { kid, display_name, status, created_at } (NO public_key_jwk, NO user_identifier).
  • Gate: new enable_key_list: bool on KeyringSettings (env CRAIG_INTAKE_KEYRING__ENABLE_KEY_LIST, default OFF); the route is conditionally mounted in build_router — absent (→ 404) when off.
  • Intake proxy GET /signed/v1/keys (Shines signed nest), mirroring register_signer_key (reuses the Extension<reqwest::Client> + KeyringUrl); relays JSON; redacts an outage to 500.

Acceptance criteria

  • Keyring GET /keys lists registered keys' metadata (no JWK / no user_identifier) when the gate is ON; route absent → 404 when OFF.
  • Deterministic ordering; unauthenticated-but-gated + internal-only (not host-published) per ADR-042 §D8.
  • Intake proxy relays the list; a keyring outage is a redacted 500.
  • Keyring unit + router tests; intake proxy test green.