Files
alknet/tasks/http/server/healthz-decoy.md
glm-5.2 e855c8c7eb docs(http): decompose alknet-http spec into 19 implementation tasks
Break the alknet-http architecture spec into atomic, dependency-ordered
tasks in tasks/http/, following the taskgraph frontmatter conventions
used by the call/core/vault crates.

Tasks span 7 phases across 5 module subdirectories (server/, gateway/,
client/, adapters/, websocket/):
- Phase 0: crate-init (foundation)
- Phase 1: gateway-dispatch-spine, error-mapping, shared-http-client
  (shared infrastructure)
- Phase 2: http-adapter, bearer-auth-middleware, gateway-endpoints,
  healthz-decoy (HTTP server surface)
- Phase 3: to-openapi (OpenAPI gateway projection)
- Phase 4: from-openapi (OpenAPI adapter, reqwest forwarding)
- Phase 5: dispatcher-transport-abstraction, upgrade-handler,
  connection-overlay (WebSocket browser bidirectional path)
- Phase 6: from-mcp, to-mcp (MCP adapters, feature-gated)
- Phase 7: review-http, review-websocket, review-mcp, review-http-final
  (quality checkpoints)

The gateway-dispatch-spine task implements the thin shared core
recommended by the gateway-factoring research (concrete struct, not a
trait). The dispatcher-transport-abstraction task is a cross-crate
change to alknet-call (exposes EventEnvelope-level dispatch API for
non-QUIC transports) — the highest-risk task. WebTransport/h3 is
deferred per ADR-044 and has no tasks; from_wss is out of scope.

Validated: 19 tasks, no cycles, 8 parallel generations, critical path
length 8 (through the WebSocket strand).
2026-07-01 07:11:17 +00:00

6.4 KiB

id, name, status, depends_on, scope, risk, impact, level
id name status depends_on scope risk impact level
http/server/healthz-decoy Implement /healthz raw route and stealth decoy fallback (DecoyConfig) pending
http/server/http-adapter
narrow low component implementation

Description

Implement the /healthz raw route and the stealth decoy fallback in src/server/healthz.rs and src/server/decoy.rs. These are the two non-gateway HTTP surfaces on the HttpAdapter router: the one raw operational endpoint (/healthz) and the stealth fallback for unknown paths (the decoy).

GET /healthz (raw route, http-server.md §"/healthz (raw route)")

GET /healthz is a raw HTTP route outside the call protocol — no auth, no operation registration, no OperationContext. It returns 200 OK with a plain-text body (e.g., "ok") if the endpoint is healthy. This is the infrastructure endpoint load balancers and orchestrators call; it must work before identity is resolvable.

/// GET /healthz — raw health check. No auth, no call protocol.
/// Returns 200 OK with plain-text body "ok" if the endpoint is healthy.
async fn healthz() -> impl IntoResponse {
    (StatusCode::OK, [("content-type", "text/plain"), "ok"])
}

Other operational endpoints (metrics, dashboard) are call-protocol operations if built (/metrics/list, /dashboard/view), not raw HTTP routes. healthz is the one exception (ADR-036).

Stealth decoy (http-server.md §"Stealth decoy")

For paths that are not the gateway endpoints (/search, /schema, /call, /batch, /subscribe), /healthz, /openapi.json, the MCP route, the WS upgrade route, or a custom route per ADR-046, the HTTP handler serves a decoy. The decoy is configurable (DecoyConfig):

  • NotFound — A fake 404 Not Found (the default — matches the reference implementation's "fake nginx 404").
  • StaticSite { root } — Serve a static site from a configured directory. For deployments that want a real decoy website.
  • Redirect { to } — Redirect to a configured URL.

The decoy is the stealth surface: a port scanner or a client that doesn't offer alknet ALPNs connects on h2/http/1.1 and sees the decoy. Real services use alknet/ssh, alknet/call, etc. The decoy config is a two-way-door default (an operator picks what to serve); the existence of the stealth path is fixed by ADR-010.

Custom routes (ADR-046) take precedence over the decoy — a path matched by a custom route is served by it, not the decoy; the decoy is the fallback for paths matched by neither the default surface nor a custom route.

The fallback handler

/// Fallback handler for unknown paths (stealth decoy). Serves the
/// configured DecoyConfig: fake 404 (default), static site, or redirect.
async fn decoy_fallback(
    State(decoy): State<DecoyConfig>,
    request: Request,
) -> Response {
    match decoy {
        DecoyConfig::NotFound => fake_nginx_404(),
        DecoyConfig::StaticSite { root } => serve_static(root, request).await,
        DecoyConfig::Redirect { to } => redirect(to),
    }
}

The NotFound variant should match the reference implementation's "fake nginx 404" — a realistic 404 page that looks like a generic web server, not an alknet-specific error. The exact body is a two-way-door implementation detail; the one-way constraint is that it does not leak alknet's presence (no alknet headers, no alknet error format).

Wiring into the router

The healthz route and the decoy_fallback are wired into the axum Router by the http-adapter task. This task implements the handlers; the http-adapter task's router construction calls them:

// In the http-adapter task's router construction:
let router = Router::new()
    .route("/healthz", get(healthz))           // this task
    .fallback(decoy_fallback)                  // this task
    // ... gateway endpoints, /openapi.json, MCP, WS upgrade ...

Acceptance Criteria

  • GET /healthz handler returns 200 OK with plain-text body "ok"
  • /healthz requires no auth (no Bearer token check)
  • /healthz does not construct an OperationContext (raw route)
  • DecoyConfig::NotFound serves a fake 404 (no alknet-specific headers/format)
  • DecoyConfig::StaticSite { root } serves static files from root
  • DecoyConfig::Redirect { to } returns an HTTP redirect to to
  • DecoyConfig::default() returns NotFound
  • Decoy fallback serves for paths not matched by any other route
  • Custom routes (ADR-046) take precedence over decoy (decoy is fallback only)
  • Gateway endpoints, /healthz, /openapi.json, MCP route, WS upgrade take precedence over decoy
  • Decoy does not leak alknet presence (no alknet headers, no alknet error format)
  • Unit test: /healthz returns 200 + "ok"
  • Unit test: unknown path with NotFound decoy → 404
  • Unit test: unknown path with StaticSite decoy → static file
  • Unit test: unknown path with Redirect decoy → redirect
  • Unit test: /healthz works with no Authorization header
  • Integration test: custom route matched → custom handler (not decoy)
  • Integration test: unknown path not matched by custom route → decoy
  • cargo test -p alknet-http succeeds
  • cargo clippy -p alknet-http --all-targets succeeds with no warnings

References

  • docs/architecture/crates/http/http-server.md — /healthz (§"/healthz (raw route)"), Stealth decoy (§"Stealth decoy")
  • docs/architecture/decisions/010-alpn-router-and-endpoint.md — ADR-010 (stealth, decoy existence)
  • docs/architecture/decisions/036-http-to-call-operation-mapping.md — ADR-036 (/healthz is the one raw route)
  • docs/architecture/decisions/046-assembly-layer-custom-http-routes.md — ADR-046 (custom routes take precedence over decoy)

Notes

/healthz is the one raw route — no auth, no call protocol, no OperationContext. It must work before identity is resolvable (load balancers call it). The decoy is the stealth surface: a port scanner sees the decoy, not alknet. The decoy config is a two-way-door (operator picks NotFound/StaticSite/Redirect); the existence of the stealth path is fixed by ADR-010. The NotFound variant should look like a generic web server's 404, not an alknet error — no alknet headers, no alknet format. Custom routes take precedence over the decoy; the decoy is the fallback for paths matched by neither the default surface nor a custom route.

Summary

To be filled on completion