refactor(gateway): migrate to alkcall 0.2 promoted gateway module

Bump the alkcall dependency to 0.2 (with the gateway feature) and
converge on the promoted shared pieces:

- The local dispatch spine (gateway/dispatch.rs, 721 lines) is deleted;
  GatewayDispatch, schema_disclosure_denial, and DEFAULT_DEADLINE are
  re-exported from alkcall::gateway (alkcall ADR-048). The 30 s default
  deadline preserves the previous behavior exactly.
- gateway/schema_cache.rs (PublishSchemaCache) is deleted: alkcall CF-003
  compiles publish_schema at registration time and exposes
  OperationRegistry::publish_validator; the /publish chunk stream
  resolves against it. Un-compilable schemas are now rejected at
  registration, so the two end-to-end fail-closed tests were reworked
  into a registration-rejection test (a stronger guarantee).
- schema_disclosure_denial consumers (to_mcp, routes) use alkcall's
  promoted implementation; the alkhttp-local copy is gone (ADR-071
  updated: the guard stays as defense-in-depth, the implementation no
  longer forks).
- CF-001: from_wss drop monitor and the WS overlay tests use
  CallError::connection_closed; the review-001-ws-eof-signal race tests
  now assert retryable CONNECTION_CLOSED on both resolution paths (the
  tolerated non-retryable INTERNAL write-failure outcome is gone).
- Added CHANGELOG.md (Keep a Changelog), Unreleased section records the
  bump and convergence.

Verification: cargo test default 453 ok, wss 470 ok, mcp 526 ok,
all-features 575 ok; clippy -D warnings clean (default + all-features,
all-targets); fmt clean; cargo doc warning-free.

Net: -1093 lines.
This commit is contained in:
2026-08-31 10:36:32 +00:00
parent 2ec02fd578
commit 8e8e1f2b14
15 changed files with 188 additions and 1235 deletions
+1 -1
View File
@@ -68,7 +68,7 @@ types — the former `alknet-core` and `alknet-call` merged).
| [068](decisions/068-gateway-publish-endpoint.md) | Gateway `/publish` Endpoint | 6th gateway endpoint for `OperationType::Pub` (producer→consumer streaming); NDJSON request body → `call.published` chunks |
| [069](decisions/069-webtransport-out-of-scope.md) | WebTransport Out of Scope | h3/WebTransport removed from alkhttp scope entirely (an alknet concern); supersedes the deferral framing of ADR-044 |
| [070](decisions/070-from-wss-consumer-adapter.md) | `from_wss` Consumer Adapter | Import a remote node's operations over WSS — same-protocol importer, channels-over-WS as transport; `wss` feature gate |
| [071](decisions/071-dispatch-schema-guard.md) | Dispatch-Spine `services/schema` Op-Path Guard | `GatewayDispatch` applies the GET `/schema` visibility+ACL checks to the meta-op's inner `name` (review-002 PRJ-16); alkcall CF-004 is the complete fix, this stays as defense-in-depth |
| [071](decisions/071-dispatch-schema-guard.md) | Dispatch-Spine `services/schema` Op-Path Guard | `GatewayDispatch` applies the GET `/schema` visibility+ACL checks to the meta-op's inner `name` (review-002 PRJ-16); alkcall CF-004 is the complete fix, this stays as defense-in-depth (now via the promoted `alkcall::gateway` shared check — spine + guard themselves live in alkcall per ADR-048) |
## Relevant Open Questions
@@ -53,23 +53,53 @@ registry rejects non-`Pub` operations before the handler runs and the
sink ignores the input, so nothing is projected there.
The visibility + ACL check itself is one shared function
(`gateway::dispatch::schema_disclosure_denial`) used by the HTTP GET
`/schema` route, the dispatch-spine guard, and the MCP `schema` tool,
so the transports cannot drift.
(`schema_disclosure_denial`) used by the HTTP GET `/schema` route, the
dispatch-spine guard, and the MCP `schema` tool, so the transports
cannot drift.
## Consequences
- **Transport-level invariant (PRJ-16's wording):** no alkhttp
transport can fetch, through any path, a spec the GET `/schema`
route would deny for the same identity.
- **When CF-004 lands, the guard stays.** The alkcall-side handler
check is the complete fix for every transport (wire, overlays, peer
composition); this per-transport check remains as defense-in-depth.
Do not remove it.
- Wire-observable behavior changes only for the previously-leaking
requests: they now get the same 404/403 the GET route returns.
Legitimate `services/schema` calls (allowed inner names) are
unaffected.
- **When CF-004 landed, the guard stayed — and was promoted.** The
alkcall-side handler check is the complete fix for every transport
(wire, overlays, peer composition; alkcall commit `8cb2a6e`). The
alkhttp spine, the GET `/schema` route, the MCP `schema` tool, and
the spine's `services/schema` op-path guard all now converge on the
**shared promoted implementation**: `alkcall::gateway::
schema_disclosure_denial` (alkcall ADR-048). The local copy was
deleted on the alkcall-0.2 bump — one implementation, so the
transports still cannot drift, and no alkhttp-local fork remains.
Do not re-introduce a local copy.
- **The dispatch spine itself is promoted too.** `GatewayDispatch`
(the `invoke`/`invoke_streaming`/`invoke_sink` spine with the
root-`OperationContext` invariants and the inner-`name` guard) is
`alkcall::gateway::GatewayDispatch` (feature `gateway`, enabled
unconditionally by the alkcall dependency — the gateway endpoints
are this crate's sole invoke path, ADR-047). The alkhttp-local copy
was deleted; only the HTTP-specific layers remain here (the
CallError → HTTP mapping in `gateway::error`, NDJSON/SSE framing,
body caps, batch envelopes, and this ADR's original
FORBIDDEN-vs-spec-404 split on the GET route).
- **`/publish` chunk validation is registry-backed.** The local
compile-once `PublishSchemaCache` was deleted: alkcall CF-003
compiles `publish_schema` at registration time and exposes the
cached validator as `OperationRegistry::publish_validator`. The
`/publish` chunk stream resolves against that cache; the
fail-closed guarantee moved upstream with it (an un-compilable
schema is a registration error, so an unvalidated-ingest path
cannot be constructed).
- **CF-001 resolution:** alkcall's `CallError::connection_closed`
(retryable `CONNECTION_CLOSED`) covers the write-failure race; the
from_wss drop-monitor race tests assert retryable-only (the
previously-tolerated non-retryable `INTERNAL: failed to write
request frame` outcome is gone from the wire vocabulary on the
undelivered-request path).
- Wire-observable behavior is unchanged from the pre-promotion guard:
previously-leaking requests still get the same 404/403 the GET
route returns; legitimate `services/schema` calls (allowed inner
names) are unaffected.
- The guard matches the outer registration's `spec.name` against the
`services/schema` constant rather than the raw request string, so
leading-slash variants (`/services/schema`) hit the same check.