docs: missing_docs sweep — 0 warnings + deny gate + publish-prep decisions (HY-02, HY-04, HY-11)

- document every public-API item across 18 files (openapi_spec model,
  HttpAuthScheme/HttpServiceConfig, HttpClientBuildError + SharedHttpClient
  accessors, RetryAfterMiddleware, GatewayDispatch, gateway error
  mapping, CallRequest/SchemaQuery/SubscribeStream, HttpAdapter +
  ALPNs + builders, decoy/healthz/state, WsSessions/WsPumps,
  from_openapi/from_jsonschema/from_mcp/from_wss/to_mcp, lib.rs module
  docs)
- enforcement: #![deny(missing_docs)] at crate root — stronger than CI
  rustdocflags (every build incl. cfg(test), where rustdoc misses the
  test-support module docs)
- HY-10 (opportunistic): all 8 docs.rs/alkhttp placeholder ADR links +
  the one relative ../docs link converted to plain text; the 10
  pre-existing private/redundant intra-doc-link warnings fixed —
  RUSTDOCFLAGS="-D warnings" cargo doc is fully clean
- HY-11 decision: docs/ + tasks/ excluded from the published package
  (contributor-facing design/process material; ADR references degrade
  to plain text uniformly). cargo publish --dry-run: 38 files, ~889 KiB,
  zero docs/ or tasks/ entries
- HY-04 decision: keep + document — frame_channel0_chunk's unwrap is
  on serializing the acyclic EventEnvelope (unreachable failure);
  # Panics on it and the adjacent WsClient senders state the contract

Verified: cargo test (299 + 5 TLS), --all-features (370 + suites),
--no-default-features (299), clippy --all-targets -D warnings
(default + all-features), fmt --check, cargo doc -D warnings clean,
cargo publish --dry-run --allow-dirty clean.

Tasks: review-001-missing-docs-sweep (final pending task; 42/42)
This commit is contained in:
2026-08-30 08:25:18 +00:00
parent 7ce1ca6fbd
commit 91483a74b4
22 changed files with 395 additions and 31 deletions
+19 -2
View File
@@ -23,9 +23,9 @@
//!
//! ## Connection knobs and boundaries
//!
//! [`HttpAdapter::serve_io`] configures the hyper auto builder with a
//! The accept-loop side (`serve_io`, crate-private) configures the hyper auto builder with a
//! tokio timer and timeouts (values in the method doc). The **concurrency
//! cap is not a knob of this crate**: [`ProtocolHandler::handle`] serves
//! cap is not a knob of this crate**: the `ProtocolHandler::handle` trait method serves
//! exactly one accepted bidirectional stream per call, and the accept
//! loop — how many streams are handled concurrently, on which tasks —
//! belongs to the consumer that owns the `Connection` (or the
@@ -53,7 +53,9 @@ use super::decoy::{decoy_fallback, decoy_method_not_allowed};
use super::healthz::healthz;
use super::state::{DecoyConfig, RouterState};
/// The HTTP/1.1 ALPN (`http/1.1`) `HttpAdapter` registers on (ADR-001).
pub const ALPN_HTTP1: &[u8] = b"http/1.1";
/// The HTTP/2 ALPN (`h2`) `HttpAdapter` registers on (ADR-001).
pub const ALPN_H2: &[u8] = b"h2";
/// The WS upgrade path (ADR-067). Reserved in the default surface; the
@@ -79,6 +81,11 @@ pub const RESERVED_PATHS: &[&str] = &[
WS_UPGRADE_PATH,
];
/// The HTTP server host (ADR-001, ADR-002, ADR-039): an axum
/// `Router` serving the gateway, `/healthz`, `/openapi.json`, the MCP
/// route, the WS upgrade path, and assembly-registered custom routes,
/// behind the bearer-auth middleware — served over one ALPN depending
/// on the constructor.
pub struct HttpAdapter {
identity_provider: Arc<dyn alkcall::core::auth::IdentityProvider>,
registry: Arc<OperationRegistry>,
@@ -94,6 +101,7 @@ pub struct HttpAdapter {
}
impl HttpAdapter {
/// An HTTP/1.1 adapter (registers on `http/1.1` ALPN).
pub fn new(
identity_provider: Arc<dyn alkcall::core::auth::IdentityProvider>,
registry: Arc<OperationRegistry>,
@@ -101,6 +109,7 @@ impl HttpAdapter {
Self::for_alpn(identity_provider, registry, ALPN_HTTP1)
}
/// An HTTP/2 adapter (registers on `h2` ALPN).
pub fn h2(
identity_provider: Arc<dyn alkcall::core::auth::IdentityProvider>,
registry: Arc<OperationRegistry>,
@@ -145,6 +154,8 @@ impl HttpAdapter {
}
}
/// Set the decoy surface for unregistered paths and rebuild the
/// router (custom routes are preserved — SRV-05).
pub fn with_decoy(mut self, decoy: DecoyConfig) -> Self {
self.decoy = decoy.clone();
let state = RouterState {
@@ -165,6 +176,9 @@ impl HttpAdapter {
self
}
/// Mount assembly-provided custom routes under the bearer-auth
/// middleware (ADR-046) and rebuild the router. Reserved paths
/// (`RESERVED_PATHS`) are rejected before the merge.
pub fn with_extra_routes(mut self, routes: Router) -> Self {
let state = RouterState {
registry: Arc::clone(&self.registry),
@@ -241,14 +255,17 @@ impl HttpAdapter {
Arc::new(tokio::sync::Semaphore::new(max_sessions))
}
/// The configured decoy surface (assembly introspection).
pub fn decoy(&self) -> &DecoyConfig {
&self.decoy
}
/// The ALPN this adapter registers on (`http/1.1` or `h2`).
pub fn alpn(&self) -> &'static [u8] {
self.alpn
}
/// The assembled router (for the accept loop the consumer owns).
pub fn router(&self) -> &Router {
&self.router
}
+11
View File
@@ -24,6 +24,9 @@ use axum::response::Response;
use super::DecoyConfig;
/// The fallback handler for unregistered paths (stealth mode, ADR-010):
/// resolves the configured [`DecoyConfig`] variant — a fake nginx 404,
/// a static site, or a redirect.
pub async fn decoy_fallback(State(decoy): State<DecoyConfig>, request: Request) -> Response {
match decoy {
DecoyConfig::NotFound => fake_nginx_404(),
@@ -32,6 +35,8 @@ pub async fn decoy_fallback(State(decoy): State<DecoyConfig>, request: Request)
}
}
/// A fake nginx-format 404 body with a 404 status — the stealth
/// "nothing is here" response that does not disclose the gateway.
pub fn fake_nginx_404() -> Response {
let body = nginx_error_body("404 Not Found");
let mut resp = Response::new(Body::from(body));
@@ -62,10 +67,13 @@ fn nginx_405_response() -> Response {
/// `Router::method_not_allowed_fallback` — applies to every
/// previously registered `MethodRouter` (default surface + extra
/// routes).
/// A plain nginx-format 405 body — the decoy method-not-allowed
/// response for reserved paths hit with unsupported methods.
pub async fn decoy_method_not_allowed() -> Response {
nginx_405_response()
}
/// A 302 redirect to `to` (the `DecoyConfig::Redirect` surface).
pub fn redirect(to: &str) -> Response {
let mut resp = Response::new(Body::empty());
*resp.status_mut() = StatusCode::FOUND;
@@ -75,6 +83,9 @@ pub fn redirect(to: &str) -> Response {
resp
}
/// Serve a static site from `root` for unregistered paths (the
/// `DecoyConfig::StaticSite` surface); paths escaping the root get the
/// fake 404.
pub async fn serve_static(root: &Path, request: Request) -> Response {
let path = request.uri().path();
let resolved = match resolve_static_path(root, path).await {
+3
View File
@@ -10,6 +10,9 @@ use axum::response::IntoResponse;
const HEALTHZ_BODY: &str = "ok";
/// `GET /healthz` — the operational liveness probe. Always answers
/// `200 ok` (text/plain), independent of registry contents; the
/// response is deliberately uninformative (stealth):
pub async fn healthz() -> impl IntoResponse {
(
StatusCode::OK,
+10 -4
View File
@@ -17,10 +17,16 @@ pub enum DecoyConfig {
/// Serve a fake `404 Not Found` (the default — a fake nginx 404).
#[default]
NotFound,
/// Serve a static site from a configured directory.
StaticSite { root: PathBuf },
/// Redirect to a configured URL.
Redirect { to: String },
/// Serve a static site from the given directory root.
StaticSite {
/// The directory tree served for unregistered paths.
root: PathBuf,
},
/// Redirect unregistered paths to the given URL.
Redirect {
/// The redirect target URL.
to: String,
},
}
/// State embedded in the axum `Router`: the registry and identity