Three inline findings from the session review of ADR-072/ADR-073:
- fix(auth): Basic door parsing no longer requires UTF-8 — the decoded
password bytes pass through to resolve_from_token byte-faithfully, so
RFC 7617 legacy ISO-8859-1 credentials resolve instead of parsing to
None. ADR-073 already specced raw-bytes passthrough ("decoded and fed
as the AuthToken raw bytes"); the String::from_utf8 round-trip
contradicted it.
- test: pin the with_ws_max_sessions fix against the shape it fixed —
a builder call after with_ws_max_sessions (the pre-router_state()
builder silently reverted the cap).
- test: pin that ownership wiring survives a later HttpAdapter builder
rebuild at the HTTP surface.
Verified: cargo test (451), --all-features (530), clippy --all-targets
-D warnings, fmt --check, fuzz corpus replay 21/21, cargo doc.
17 KiB
Changelog
All notable changes to this crate are documented here. The format is based on Keep a Changelog, and this crate adheres to Semantic Versioning.
[0.8.0] — 2026-10-17
Added
- Door-surface Basic auth tolerance (review 008 AH-1, ADR-073).
The extras-door surface now resolves
Authorization: Basic base64(user:pass)— the password field acts as the token and rides the sameIdentityProvider::resolve_from_token()backend as Bearer (the GitHub-PAT shapegit-lfsand git's smart-HTTP transport authenticate with). No new trait method; the provider's grant table governs exactly as it does for Bearer, and "unparseable / unresolvable → None" fallback semantics are preserved. Basic is scoped to the door surface:bearer_auth_middlewarestays Bearer-only on the gateway endpoints,/openapi.json,/healthz, and the MCP nest, and the WS upgrade path is untouched — a Basic credential contributes no identity on any path a deployment has not explicitly mounted doors on. New public API:server::door_auth_middleware,server::extract_door_identity(bearer_auth_middleware/extract_bearer_identitykeep their Bearer-only semantics and their coverage).
Changed
alkcalldependency bumped to 0.10.0 (the gateway ownership-seam release, alkcall review 012) and the loud ownership default passes through to the HTTP surface (ADR-072):HttpAdapter::with_ownership_provider/HttpAdapter::with_static_ownership_fallbackthread the provider into both spines this crate constructs — the gateway endpoints' per-request spine and the MCP nest's spine (single wiring point inGatewayState::dispatch). An unwired, undeclared spine now refuses aresource_type-gated op with aFORBIDDENenvelope (403 with identity / 401 without) — the behavior break is inherited, not re-decided; the WS native-session path is untouched (it consults ownership natively).- The gateway test-state builder threads ownership configuration; the
three dispositions (unwired refusal, live grant table, declared
static fallback) are pinned at the HTTP surface by
call_resource_gated_*regression tests.
Documented
- Extras door route tables are static (review 008 AH-2). One doc
line on the door-facing surface (
HttpAdapter::with_extra_routes) and in ADR-046: the merged router already owns the decoy fallback and axum refuses a second, so a door attemptingfallback_layerpanics at construction — enumerate multi-segment namespace shapes ({user}/{repo}/...) as pattern routes. Not a mechanism ask; the next door author should not rediscover the panic.
Fixed
HttpAdapter::with_ws_max_sessionsnever stored the freshly built session-slot semaphore on the adapter — any subsequent builder call rebuilt the router with the pre-cap-change semaphore, silently reverting the cap. Builder state is now assembled in one sharedrouter_state()helper, so everywith_*rebuild sees the current configuration. Pinned by the WS session-cap integration test running with a builder call afterwith_ws_max_sessions, and by an ownership-wiring-survives-rebuild test at the HTTP surface.- Door-surface Basic parsing no longer requires the password field to
be UTF-8: the decoded password bytes reach
IdentityProvider::resolve_from_token()byte-faithfully, so RFC 7617 legacy ISO-8859-1 credentials (e.g. a latin-1 user password) that a provider resolves still authenticate instead of parsing toNone.
Minor bump 0.7.0 → 0.8.0 per the dep-wave convention (0.4.0 rode alkcall 0.7.0, 0.5.0 rode 0.8.0, 0.7.0 rode 0.9.0): the re-exported alkcall types are on this crate's public API, so a semver-moving upstream is semver-relevant here.
[0.7.0] — 2026-10-14
Changed
alkcalldependency bumped to 0.9.0 (the stub-sweep release: the four registered-but-always-failing stubs across nine published versions are replaced with real implementations — the marked-pub open contract (S-01),channel/control(S-02),channel/resources/subscribe(S-03), andop/registerforward-through (S-04)). No code change in this crate — none of the 0.9.0 breaking surfaces are constructed or pattern-matched here: noManagerErrormatch arms (the newInvalidAdoptvariant changes nothing), noRelayErrormatching (the removedPubTypedOpenvariant is unreachable from this crate), noEstablishmentconstruction orDebugdependence (the WS ferry passesOption<OpenEstablisher>through unchanged), and theop/registerforward-through refactor does not touch thefrom_wssimport filter or the adapter-registered leaf bundles. The S-01 marked-pub machinery is consumer-side assembly, not an HTTP-surface concern; the four gateway operations and the WS session shape are unchanged.
Fixed
tests/ws_overlay_ops.rsnow expectsregister_imported's newResult<(), String>return (alkcall 0.9.0 API change): the eight connection-overlay registration sites assert success instead of discarding the value. Test-only.fuzz/sharedfollows the main crate to alkcall 0.9 (the harness replay previously resolved alkcall 0.8.1 alongside alkhttp's 0.9.0, two incompatible copies in one build). Infra-only.
Minor bump 0.6.0 → 0.7.0 per the dep-wave convention (0.3.0 rode
alkcall 0.6.0, 0.4.0 rode 0.7.0, 0.5.0 rode 0.8.0): this crate's
public API carries alkcall types through re-exports and the
ProtocolHandler impl, so a semver-breaking upstream is
semver-relevant here.
[0.6.0] — 2026-09-29
Added
- Fuzzing infrastructure adopted (the sibling-crate pattern:
alkcall, alktty, alktunnels, alksocks). The
fuzz/subtree holds five nightly targets (sse_parser,ws_chunk_framing,spec_import,ws_session_opseq,retry_throttle), a stable toolchain shared crate replaying every committed seed (80 + 42 + 16 committed) through the identical invariant functions as plaincargo test— the standing fuzz gate,cargo test --manifest-path fuzz/shared/Cargo.toml— plus a detached campaign runner and a deterministic seed generator.fuzz/is excluded from publish; nightly stays confined to the subtree (MSRV 1.88 unchanged). - New public API for the tested parse surfaces. The incremental
SSE parser widens
pub(crate)→pub(SseParser,SseEvent,SseParseError,SSE_EVENT_BUFFER_CAP,sse_event_envelope) and the WS write pump's chunk walk is extracted as public API (WriteChunkWalk,Walk) — both direct-drive test targets. Fivefuzzingre-export hubs (adapters::forward::fuzzing,adapters::openapi_spec::fuzzing,adapters::input_validation::fuzzingwith the opaqueFuzzCompiledValidator,adapters::from_mcp::fuzzing(`mcp`),client::fuzzing+websocket::byte_adapter::fuzzingwith the mock-socket pump drive) gather the harness access surface at one named path each; the underlying crate-internal items keep their visibility.
Fixed
- SSE dribble drain was quadratic in the dribbled bytes (FWD-20).
SseParser::feedre-walked the whole reassembly buffer per feed, so a subscription drip-feeding one giant unterminated line paid O(n²) (64 KiB ≈ 12.5 s CPU per connection; 1 MiB ran to minutes) with no per-stream total-CPU bound. Line assembly now walks only the arriving chunk — the reassembly buffer holds just the current line — measured linear (1 MiB dribble: 21 ms). Regression test:sse_parser_dribble_feeds_do_not_rescan_the_buffer. WriteChunkWalkfailure arms discarded the already-drained pieces (FWD-21). One feed that drained complete chunks and then hit an oversize header (or the pending cap) returnedWalk::Oversize/PendingOverCapempty, dropping bytes the mux had already counted as written (the walk's consumption-accounting contract was violated; production could not reach the shape, butWriteChunkWalkis public API and the silent drop broke it). The failure variants now carry the pre-failure pieces (Vec<Vec<u8>>) and the write pump emits them before failing the stream (1011 close). Regression seeds cover the drain-then-fail shape in both chunk harnesses.- FWD-22 (harness-assert fix): the fuzz harness's SSE re-frame
round-trip probe is now total over anything JSON-encodable
(
frame_eventemits a guard CR as the line terminator; the terminal-CR skip is dropped). No crate change.
Changed
alkcalldependency consumed at 0.8.1 (patch pickup): the duplicate adopt/open collision fix found by alkcall'smanager_routingfuzz campaign — a duplicate adopt/open no longer replaces the live channel's state. This crate reaches the fixed paths via the sharedChannelManager; no alkcall API surface used here changed.
Changed
alkcalldependency bumped to 0.8.0 (review 008's remediation wave: the graduation upstream asks + the in-tree channel relay). No code change in this crate — alkcall 0.8.0 is additive on the call-plane JSON and the registry seams, and none of its new surfaces are constructed here: this crate builds noEstablishmentreply fields (the WS ferry passesOption<OpenEstablisher>through unchanged; reply-field projection is an establisher-supplier feature), imports no marked specs throughfrom_call(thefrom_wssoperation filter excludes the protocol-session ops; any domain op the remote serves imports as a forwarding bundle exactly as before), and consumes neitherChannelRelaynorHubLegImports/HubLegTemplate(hub-leg assembly remains the consumer's concern at the assembly layer). One ride-through visible on this crate's discovery surface: for a deployment'swith_ws_openable_alpnsopen-op specs whose names are not standardchannels/<seg>/(sub|pub)shapes, the servedservices/list/services/schemaoutput now carries an explicitchannel_open_alpnstring alongside the boolean marker (alkcall 0.8.0's U-1, ADR-047 amendment 3) — so hubs relaying through afrom_callimport of those ops reconstruct the marker instead of seeing plain forwarding stubs. Standard-shape open-op names stay byte-stable. Minor bump per the dep-wave convention (0.3.0 rode alkcall 0.6.0; 0.4.0 rode 0.7.0).
[0.4.1] — 2026-09-10
Changed
-
rust-versionfloor raised 1.85 → 1.88 (patch bump, no API change). The 1.85 claim was already false at the dependency level: the resolved lockfile pullsicu_*2.x (MSRV 1.86 viajsonschema→idna) and — since alkcall 0.7.1 — alkcall itself declares 1.88, so 1.85 toolchains cannot build the tree regardless of what Cargo.toml declared. Raising the floor to 1.88 aligns with the ecosystem MSRV decision (noq QUIC path; recorded for alktls in OQ-TLS-08) and breaks no downstream that could build the crate before. Verified: 1.88 toolchaincargo test --locked+ clippy--all-targets -D warningsclean; one 1.88 clippy lint fixed (uninlined_format_argsin aforward.rstest). -
alkcalldependency consumed at 0.7.1 (lockfile bump; theversion = "0.7"requirement already covered it). No code change forced — alkcall 0.7.1's changes are its own MSRV-floor raise plus the cf-006/cf-007 feature work already consumed in the 0.7.0 wave.
[0.4.0] — 2026-09-07
Changed
alkcalldependency bumped to 0.7.0 (CF-005/006/007 — the connect-side caller-identity seam + the downstream-findings sweep). No code change in this crate: it constructs noServingConfig(the one struct-literal break —from_wsskeeps the pure-consumerChannelClient::from_connection; opt-in serving remains the review-006 Part C deferred decision) and the per-call opener-identity overlay (CF-006) is behavior-only upstream — hub-forwarded opens on the WS ferry now resolve the end client's identity instead of the hub's, the intended effect. alkcall 0.7.0's ADR-016 amendment (the protocol-code list is eight codes) resolves the upstream half of review-006 Part C's doc-drift note.
Changed (wire-surface behavior)
- Gateway error table maps
CONNECTION_CLOSED→ 503 Service Unavailable (the alkcall 0.7.0 CF-007 amendment makes it the eighth protocol code; ADR-023 amended). Previously it fell to the catch-all 500, which misreported a retryable, provably-undelivered call as a permanent server failure. Reachable through the gateway when an imported op's forwarding handler calls into a dropped transport (from_wss's pending-call fail-all is retryableCONNECTION_CLOSED). The code isretryable: true, so the existingretry_afterdetails path applies on 503. Tests pin the mapping and theRetry-Afterpassthrough. Behavioral break for HTTP callers matching on the 500 previously emitted for this code — hence a minor bump, not a patch.
[0.3.0] — 2026-09-07
Changed
alkcalldependency bumped to 0.6.0 (review 007's establishment follow-ups sweep). One consumer-visible change: theOpenHandlersignature gained a plan parameter —Fn(Value, Option<ChannelPlan>, Connection, AuthContext) -> JoinHandle<()>— soOpenableAlpn::newcallers' handler closures gain a_planargument (mechanical). With an establisher attached (ADR-049), itsEstablishment.plan(a typed-opaqueArc<dyn Any + Send + Sync>) is threaded process-locally to the handler's plan parameter: ALPN crates dial/allocate in the establisher and deliver the live handle without a side-channel handoff. This crate's WS ferry passes theOption<OpenEstablisher>through unchanged and constructs noEstablishmentitself, so the ride is otherwise additive — alkcall 0.6.0'schannels::pump_bidihelper (ADR-050) is available to data-plane handlers this crate ferries. Docs updated (websocket.md,OpenableAlpnfield docs, adapter.rs).
[0.2.0] — 2026-09-06
Changed
alkcalldependency bumped to 0.2 with thegatewayfeature enabled. The transport-neutral dispatch spine now comes fromalkcall::gateway(promoted from this crate per alkcall ADR-048):GatewayDispatch,schema_disclosure_denial, andDEFAULT_DEADLINEare re-exported fromalkhttp::gateway; the local copy (gateway/dispatch.rs) was deleted. The spine is used with the 30 s default deadline (identical behavior to the local version). alkcall 0.2 also carries the consumer-findings fixes CF-001..004 documented inalkcall/docs/reviews/consumer-findings-ledger.md.alkcalldependency bumped to 0.4.1 (through 0.3, 0.4.0):- 0.3 — per-session fork, serving loop, and
op/register(the WS op/register ACL surface in this crate builds on it); 0.3.1 —PeerCompositeEnv::peer_operationssolist-peerssees peer-announced ops (UP-03). - 0.4.0 — the registry enforces
input_schemaat call time (ADR-016INVALID_INPUTleg); adapters no longer need to validate before dispatch. - 0.4.1 — early-arrival chunks are parked for un-adopted channels, closing the open/first-data race on the WS overlay.
- 0.5.0 — the channels establisher phase (ADR-049):
OpenableAlpngainsestablisher: Option<OpenEstablisher>andestablisher_timeout: Option<Duration>(both defaultNoneviaOpenableAlpn::new, so existing constructions compile unchanged) plus awith_establisher(establisher, timeout)builder; the WS ferry callsregister_openable_with_establisher, threading both fields through. With an establisher attached, the open op awaits the bounded establishment phase before the reply and resolveschannel:open_failedwithdetails.reasonon failure — the channel never exists consumer-side. Without one, behavior is the pre-0.5 shape. The alkcall 0.5.0open_channelerror-type change needs no alkhttp change (this crate never callsopen_channel;from_wssusesChannelClient::from_connectiononly).
- 0.3 — per-session fork, serving loop, and
/publishchunk validation uses the registry-owned validator (CF-003). The local compile-oncePublishSchemaCache(gateway/schema_cache.rs) is removed —publish_schemais compiled at registration time by alkcall and exposed asOperationRegistry::publish_validator; an un-compilable schema is now a registration error, so the fail-open window this cache closed GW-side cannot exist upstream either. The/publishchunk stream resolves against the registry's cached validator.- The from_wss drop monitor and the WS overlay tests use
CallError::connection_closed(CF-001), and the review-001-ws-eof-signal race tests now assert retryableCONNECTION_CLOSEDon both resolution paths (the drop monitor and the undelivered-request write failure) — the previously tolerated non-retryableINTERNAL: failed to write request frameoutcome on the race path is gone.
Notes
- CF-004 convergence: the alkhttp-local
services/schemadefense-in-depth guard remains, but its shared check (schema_disclosure_denial) is now alkcall's promoted implementation (ADR-048); the alkhttp-local copy is gone. See ADR-071 for the updated disposition.