glm-5.3-flash e2ca981eb6 chore: raise rust-version floor to 1.88; fix 1.88 clippy lints
The 1.85 claim was already false at the dependency level: the resolved
lockfile pulls icu_* 2.x (MSRV 1.86) and wasip2 1.87 transitively via
jsonschema -> idna, so 1.85 toolchains cannot build 0.7.0 regardless of
what Cargo.toml declares. Raising the floor to 1.88 aligns with the
noq QUIC path (per the ecosystem MSRV audit) and breaks no downstream
that could build the crate before.

- Cargo.toml: rust-version 1.85 -> 1.88 (version-line bump deferred to
  release time; semver-checks passes either way)
- src/channels/mux.rs: collapse else { if .. } (collapsible_else_if)
- src/core/types.rs, src/registry/registration.rs: inline format args
  (uninlined_format_args)

Verification:
- 1.88: cargo test --locked (631 pass), clippy --all-targets -D warnings
- stable 1.94: test --locked, clippy -D warnings, --all-features, fmt,
  doc --no-deps
- wasm32-unknown-unknown: cargo check
- cargo semver-checks check-release: no semver update required
2026-09-09 19:32:50 +00:00
2026-08-11 08:48:35 +00:00
2026-08-11 08:48:35 +00:00

alkcall

Call + channels RPC: structured JSON operations, streaming subscriptions, service discovery, and N-channel multiplexing over one transport stream.

This crate unifies the call protocol and the channels protocol, plus the vendored core types formerly in alknet-core. It is a pure protocol crate — no networking, no transport dependencies. Downstream crates (alktty, alktunnels, alktrader) compose on top of it.

Quick start

Producer — register an operation and run a dispatcher

use std::sync::Arc;
use alkcall::core::{Capabilities, IdentityProvider};
use alkcall::protocol::{CallAdapter, connection::CallConnection};
use alkcall::registry::{
    registration::{HandlerRegistration, HandlerKind, OperationRegistry, make_handler},
    spec::{OperationSpec, OperationType, Visibility, AccessControl},
};

let registry = OperationRegistry::new();
registry.register(HandlerRegistration::new(
    OperationSpec::new(
        "echo/run",
        OperationType::Query,
        Visibility::External,
        serde_json::json!({}),
        serde_json::json!({}),
        vec![],
        AccessControl::default(),
        None,
    ),
    HandlerKind::Once(make_handler(|input, ctx| async move {
        alkcall::protocol::wire::ResponseEnvelope::ok(ctx.request_id, input)
    })),
    alkcall::registry::registration::OperationProvenance::Local,
    None,
    None,
    Capabilities::new(),
)).unwrap();

let registry = Arc::new(registry);
let provider: Arc<dyn IdentityProvider> = /* your identity provider */;

let adapter = CallAdapter::new(registry, provider);
// adapter implements ProtocolHandler — call adapter.handle(connection, &auth).await

Consumer — call an operation

use alkcall::core::Connection;
use alkcall::protocol::connection::CallConnection;

let connection = Connection::from_bidi(
    transport_stream,
    b"alk/call".to_vec(),
    Some(remote_addr),
);
let conn = CallConnection::new(connection);

let response = conn.call("echo/run", serde_json::json!({"msg": "hello"})).await;
assert!(response.result.is_ok());

Channels — open a channel and call through channel 0

use alkcall::channels::client::ChannelClient;
use alkcall::core::Connection;

let connection = Connection::from_bidi(
    transport_stream,
    b"alk/channels".to_vec(),
    Some(remote_addr),
);
let client = ChannelClient::from_connection(connection).await?;

let response = client.call_open_op(
    "echo/run",
    serde_json::json!({"msg": "hello"}),
).await;

from_call — discover and import remote operations

use alkcall::client::{from_call, FromCallConfig};

let registrations = from_call(&conn, FromCallConfig::new()).await?;
for reg in registrations {
    conn.register_imported(reg);
}
// now call remote ops as if they were local
let response = conn.call("remote/status", serde_json::json!({})).await;

Serving your own ops as a connected consumer

The call protocol is symmetric — both sides of a connection can serve ops. A ChannelClient built with from_connection is a pure consumer (inbound call.requested frames are dropped); pass a ServingConfig to also serve your registry to the peer, and use op/register to announce which ops you serve:

use std::sync::Arc;
use alkcall::channels::client::{ChannelClient, ServingConfig};
use alkcall::registry::discovery::install_bootstrap_discovery;

let registry = Arc::new(OperationRegistry::new());
// ... register your ops on the registry, then:
install_bootstrap_discovery(&registry)?;

let client = ChannelClient::from_connection_with_serving(
    connection,
    Some(ServingConfig {
        registry: Arc::clone(&registry),
        identity_provider: provider,
        identity: None, // peer identity: transport `Connection::set_identity` propagates
    }),
).await?;
// peer-callable ops resolve against `registry` on channel 0;
// `client.call_open_op` still works — both directions share the pump

Architecture

alkcall is a pure protocol crate — no networking, no transport dependencies. It provides the call and channels protocols as a library. Downstream crates compose on top of it in a layered dependency chain.

Role Call protocol Channels protocol
Producer Registers ops on an OperationRegistry, runs a Dispatcher Runs a ChannelsAdapter, registers openable ALPNs via ChannelCore::register_openable
Consumer Uses CallConnection to call ops, uses from_call to discover/import remote ops; may also serve its own ops (from_connection_with_serving) Uses ChannelClient to open channels via call_open_op + open_channel
Hub Both: runs a Dispatcher for ops it produces, holds CallConnections to spokes for ops it consumes Both: runs a ChannelsAdapter for inbound connections, holds ChannelClients to spokes
Spoke / Worker Both: produces ops (its own services), consumes hub ops Both: produces channels (TTY, tunnel), may consume hub channels

A single process can be a producer of some ops, a consumer of others, a channel opener for TTY, and a channel acceptor for tunnels — all on the same alk/channels connection.

Documentation

  • Architecture docs — the authoritative spec: ADRs, wire formats, protocol contracts, and composition patterns.
  • API docs — full crate documentation on docs.rs.
  • Open questions — tracked deferred decisions and feature gaps.

License

MIT OR Apache-2.0

S
Description
No description provided
Readme
4.2 MiB
Languages
Rust 100%