mtp/docs/CONNECTOR.md
Alex Emmet 02be09ef26
Some checks failed
CI / checks (push) Failing after 4m20s
General Upgrade, NEW: WebServers, Better Docs
2026-07-18 14:40:11 +02:00

4.4 KiB

Connector

This file documents the connection and version negotiation logic.

Registry

The registry module provides a multi-version Registry used by the host for version negotiation. Accessed through the mtp facade (requires the host feature):

use mtp::codec::registry::Registry;

let registry = Registry::builtin(); // loads all TypeMaps from config

// Check if a version is supported
assert!(registry.supports(&Version(1, 0)));

// Find highest mutual version for a client
let client_versions = &[Version(0, 0), Version(1, 0)];
let negotiated = registry.negotiate(client_versions);
assert_eq!(negotiated, Some(Version(1, 0)));

// Look up a version's TypeMap
let tm = registry.get(&Version(2, 0)).unwrap();

The Registry::builtin() constructor uses the TypeMap::vX_Y() methods generated from the config.


Host

The host creates a QUIC server, manages the registry, and handles version negotiation with each connecting client.

Initialization

The host binds to the address and port supplied in HostConfig:

use mtp::host::{HostConfig, MTPHost};

let config = HostConfig::new(
    "0.0.0.0".parse()?,
    4433,
    std::fs::read("cert.pem")?,
    std::fs::read("key.pem")?,
);

let mut host = MTPHost::new(config).await?;

Accepting Connections with Version Negotiation

while let Some(conn) = host.accept().await? {
    // conn.version is the negotiated version
    // conn.codec is a VersionedCodec scoped to that version
    // conn.sender / conn.receiver for raw CommunicationValue I/O

    let msg = conn.receiver.receive().await?;
}

The host reads the reserved opening frame, extracts DataType::Version, calls registry.negotiate, and returns AcceptError::UnsupportedVersion when no registered version matches.

Authentication follows the version-bearing hello when the host enables it. The sequence is defined in Protocol Reference.


Client

The client connects to a host and uses a single compiled-in protocol version.

use mtp::client::{ClientConfig, MTPClient};

let config = ClientConfig::new("https://host.example.com:4433");
let pinned = config.clone().with_pinned_pem(cert_pem_bytes);

// Connect (unauthenticated, existing client)
let conn = MTPClient::connect(config.clone().with_client_id(8765)).await?;

// Authenticated login
let conn = MTPClient::auth_connect(pinned.with_client_id(8765), &keys, &host_pk).await?;

// Registration (new client)
let conn = MTPClient::auth_register(config, &keys, &host_pk).await?;

The client's PROTOCOL_VERSION constant is set by protocol_version in type-maps.yaml and baked in at compile time. The client uses one version and does not import the registry.


Version Negotiation Flow

Client (v2.0)         Host (v0.0, v1.0, v2.0)
    |                        |
    |  QUIC connect          |
    |----------------------->|
    |                        |
    |  CommValue{ Ident. }   |
    |    Version -> "2.0"    |
    |    Id -> 8765          |
    |  (unsigned hello; auth |
    |   challenge follows)   |
    |----------------------->|
    |                        |  registry.negotiate(&[Version(2,0)])
    |                        |  -> Some(Version(2,0))
    |                        |
    |  Response              |  selected v2.0 TypeMap
    |<-----------------------|
    |  Status, version       |
    |                        |
    |  subsequent messages   |
    |  use v2.0 TypeMap      |

If the client sends an unsupported version (e.g. v3.0 when the host only knows up to v2.0), negotiate returns None and the connection is closed.

Protocol Ping and Pong

See Protocol Reference.

Protocol Version Changes

Add a protocol version by adding its type-map entry and protocol_version to the YAML configuration, then rebuild both peers. The type-map build script generates a version-specific TypeMap and keeps the enum as the union of all configured type names.

For a backward-compatible change, keep existing communication and data IDs stable and add new types with the new version. For a breaking change, add a new version and register both versions on the host while clients migrate. A client compiles one protocol version; it can connect only when that version is present in the host registry. Remove an old version only after its clients no longer connect, because the host closes connections whose version is unsupported.