feat(adapters): from_wss consumer adapter behind the wss feature (ADR-070)

- FromWss: dial wss:// -> split_tungstenite_to_bytes (client-side twin
  of the axum WS byte-adapter; one seam, both directions, OQ-01) ->
  Connection::from_bidi(b"alk/channels") -> alkcall ChannelClient
  (channel 0 install + dispatch loop) -> alkcall from_call importer.
  No protocol fork: specs mirror the remote, provenance FromCall.
- Drop semantics (OQ-03 v1): session drop -> monitor fails all
  in-flight pendings retryable CONNECTION_CLOSED (WsPumps::read_eof
  Notify); no 30s-deadline hang.
- Bearer token via constructor/assembly layer (ADR-014 no-env-vars).

Production fix in the WS server half (upgrade.rs): the upgrade
identity now propagates to channel 0's CallConnection (was
AuthContext::anonymous -> dispatcher saw no identity, ACL checks ran
unauthenticated; services/list filtered scoped ops for all callers).

9 in-module tests incl. full round-trip consumer<->server (both halves
of the adapter together), ACL end-to-end, drop-no-hang.

Verified: cargo test (227 lib default), --all-features (227 lib + 5
MCP + 10 WS integration), clippy -D warnings (both), fmt.
This commit is contained in:
2026-08-28 14:54:09 +00:00
parent 4ac337c3a5
commit 3a906cbd6a
6 changed files with 824 additions and 19 deletions
+17 -3
View File
@@ -35,10 +35,15 @@ pub async fn run_channels_session(
) {
let (byte_stream, _pumps) = split_ws_to_bytes(socket);
let conn = Connection::from_bidi(byte_stream, b"alk/channels".to_vec(), None);
let _ = conn.set_identity(identity);
let _ = conn.set_identity(identity.clone());
let adapter = ChannelsAdapter::new(install_channel_zero(registry), policy);
let auth = AuthContext::anonymous(b"alk/channels");
let auth = AuthContext {
identity: Some(identity),
alpn: b"alk/channels".to_vec(),
remote_addr: None,
tls_client_fingerprint: None,
};
if let Err(e) = ProtocolHandler::handle(&adapter, conn, &auth).await {
tracing::warn!(error = %e, "channels session ended");
}
@@ -57,9 +62,18 @@ pub async fn run_channels_session(
fn install_channel_zero(
registry: Arc<OperationRegistry>,
) -> alkcall::channels::adapter::InstallChannelZero {
Arc::new(move |_manager, channel0_conn, _auth| {
Arc::new(move |_manager, channel0_conn, auth| {
let registry = Arc::clone(&registry);
tokio::spawn(async move {
// The WS identity rides the upgrade request; propagate it to
// channel 0's `CallConnection` so the dispatcher's
// `resolve_identity` (and thus `AccessControl::check` on
// `services/list` and every operation) sees it. Without this
// the freshly constructed channel-0 `Connection` carries no
// identity and all ACL-restricted ops look unauthenticated.
if let Some(identity) = auth.identity.clone() {
let _ = channel0_conn.set_identity(identity);
}
let channel0_bidi = match channel0_conn.accept_bi().await {
Ok(s) => s,
Err(_) => return,