Skip to content

SPT Wire Protocol

The SPT Wire protocol provides DID-addressed vector transport over QUIC. Unlike topic-based messaging systems, SPT Wire routes messages by Decentralized Identifier (DID) prefix using scope-aware longest-prefix matching. All connections are authenticated by DIDs verified against the Global Identity Chain (GIC), replacing traditional X.509/mTLS with post-quantum cryptography.

Design Principles

  • DID-based addressing: Routes are resolved by DID prefix, not topic string
  • Scope-aware routing: Longest-prefix matching with multi-hop capability
  • Exactly-once delivery: Monotonic sequence counters per recipient
  • Store-and-forward: Vectors persist for offline recipients (including persistent backends)
  • QUIC transport: Multiplexed, encrypted connections via quinn
  • DID authentication: QUIC handshake uses spt-crypto-provider with ML-KEM/ML-DSA -- no X.509 certificates
  • Adaptive transport: ConnectionCascade transparently falls through QUIC, NAT hole punch, TURN relay, and WebTransport
  • Pub/sub: DID-prefix subscriptions for fan-out delivery

Architecture

text
+-------------------------------------------+
|            Application Layer              |
|  (VectorEnvelope from spt-smartpoint)     |
+-------------------------------------------+
|         spt-wire                          |
|  +------------+  +--------------------+   |
|  | Address    |  | RoutingTable       |   |
|  | (DID)      |  | (prefix -> hop)    |   |
|  +------------+  +--------------------+   |
|  +------------+  +--------------------+   |
|  | Endpoint   |  | TransportChannel   |   |
|  | (QUIC)     |  | (framed vectors)   |   |
|  +------------+  +--------------------+   |
|  +-----------------+  +---------------+   |
|  | ConnectionCascade|  |SubscriptionMgr|  |
|  | (adaptive path) |  | (pub/sub)     |   |
|  +-----------------+  +---------------+   |
|  +------------------------------------+   |
|  |  PersistentVectorStore (S&F)       |   |
|  +------------------------------------+   |
|  +------------------------------------+   |
|  |  WebTransportFallback (TCP/443)    |   |
|  +------------------------------------+   |
+-------------------------------------------+
|  spt-crypto-provider (DID auth, PQ)       |
+-------------------------------------------+
|         quinn / QUIC / UDP                |
+-------------------------------------------+

Crate Names

ADR-101 renamed the transport crates for clarity:

Previous NameCurrent NamePurpose
spt-transportspt-wireThe SPT Wire protocol (QUIC transport, routing, pub/sub)
spt-wirespt-wire-formatBinary segment codec (varint, delta, hash)
spt-wire-bridgespt-wire-bridgeBridges SmartPoint envelopes to QUIC transport

When ADR-088 says "SPT Wire," it means the spt-wire crate.

DID-Based Addressing

Every participant is identified by a TransportAddress wrapping a DID string. The routing table maps DID prefixes to next-hop endpoints, enabling hierarchical routing across Scopes.

rust,ignore
use spt_wire::TransportAddress;

let addr = TransportAddress::from_did("did:said:1:sw:blake3_abc123");

DID Authentication (spt-crypto-provider)

QUIC connections are authenticated using DIDs verified against the GIC Merkle DAG, replacing X.509 certificates entirely. The spt-crypto-provider crate implements the rustls::crypto::CryptoProvider trait with post-quantum algorithms.

Authentication flow:

  1. During QUIC handshake, each side presents a Raw Public Key (RFC 7250) containing its DID and public key
  2. The DidVerifier extracts the DID and resolves it against the GIC
  3. The verifier checks that the key is registered, not revoked, and meets the trust tier requirement
  4. ML-KEM-768 key exchange establishes the session keys
  5. All subsequent data is encrypted with ChaCha20-Poly1305

Crypto suite mapping:

CryptoSuiteKey ExchangeSignatureStatus
pq-v1ML-KEM-768ML-DSA-65Default
hybrid-v1X25519 + ML-KEM-768Ed25519 + ML-DSA-65Transition
classical-v1X25519Ed25519Legacy compatibility

Transport Endpoint

A TransportEndpoint binds to a local UDP socket and serves as both a QUIC server and client. Inbound connections are accepted automatically; outbound connections create a TransportChannel for bidirectional vector exchange.

rust,ignore
use spt_wire::{TransportConfig, TransportEndpoint};

// Start a server endpoint
let server_cfg = TransportConfig::default();
let server = TransportEndpoint::bind(server_cfg).await?;
let server_addr = server.local_addr()?;

// Connect a client to the server
let client_cfg = TransportConfig::default();
let client = TransportEndpoint::bind(client_cfg).await?;
let channel = client.connect(server_addr).await?;

Transport Channel

A TransportChannel is a framed, bidirectional vector pipe over a QUIC connection. It handles length-prefixed framing so that each send_vector / recv_vector call delivers exactly one complete vector.

rust,ignore
// Send a vector
channel.send_vector(b"hello world").await?;

// Receive a vector
let data = channel.recv_vector().await?;

NAT Traversal Integration

The spt-nat crate is wired into spt-wire so that TransportEndpoint automatically attempts NAT traversal when direct connectivity is unavailable. The QuicNatTraversal coordinator handles STUN binding, hole punching, and TURN relay allocation. TURN supports 401 authentication retry and IPv6 addresses.

rust,ignore
use spt_wire::TransportEndpoint;
use spt_nat::NatTraversalManager;

let endpoint = TransportEndpoint::bind(config).await?;
let nat_mgr = NatTraversalManager::new(stun_servers, turn_credentials);

// connect_with_nat tries direct, then hole punch, then TURN
let channel = endpoint.connect_with_nat(&target_did, &nat_mgr).await?;

ConnectionCascade

The ConnectionCascade provides adaptive transport selection. When a direct QUIC connection fails, it automatically falls through to the next transport mechanism:

text
1. Direct QUIC (UDP/443) -- fastest, works for public IPs + same LAN
   | fails
2. Hole Punch (STUN + simultaneous open) -- works for most consumer NATs
   | fails
3. TURN Relay -- works for symmetric NAT
   | fails
4. WebTransport over HTTPS (TCP/443) -- works through corporate firewalls
   | fails
5. QUIC-over-TCP -- last resort, maximum compatibility

The caller receives a TransportChannel regardless of which path was selected. The same SmartPoint pair authentication and E2E encryption apply on every path.

WebTransport Fallback

When UDP is completely blocked (common in corporate environments), WebTransportFallback tunnels QUIC semantics over HTTP/3 WebTransport:

rust,ignore
use spt_wire::WebTransportFallback;

let fallback = WebTransportFallback::new(https_url, pair);
let channel = fallback.connect().await?;
// channel is a standard TransportChannel -- caller doesn't know it's over HTTPS

WebTransport adds approximately 15% latency compared to direct QUIC. It is used only as a fallback, never as the preferred path.

Pub/Sub (DID-Prefix Subscriptions)

SPT Wire extends point-to-point transport with DID-prefix subscriptions for fan-out delivery. The SubscriptionManager matches published vectors against subscriber prefixes using the same longest-prefix matching as the routing table.

rust,ignore
use spt_wire::TransportEndpoint;

// Subscribe to all vectors in an organization
let handle = endpoint.subscribe("did:said:1:sw:blake3_org", |envelope| {
    // Process received vector
});

// Publish a vector to all matching subscribers
endpoint.publish(envelope).await?;

Subscriptions are scope-aware: a subscriber only receives vectors they are authorized to see based on their SmartPoint pair capabilities.

Store-and-Forward

When the recipient is offline, vectors are persisted in a VectorStore. The PersistentVectorStore backend (added in ADR-101) ensures store-and-forward data survives process restarts, replacing the previous in-memory-only implementation.

Each stored vector is wrapped in a StoredVector with metadata (sender DID, recipient DID, timestamp). When the recipient reconnects, pending vectors are flushed in order.

The InMemoryVectorStore remains available for testing and lightweight deployments.

Routing Table

The RoutingTable maps DID prefixes to next-hop socket addresses. Lookups use longest-prefix matching, which enables hierarchical routing across organizational boundaries.

rust,ignore
use spt_wire::RoutingTable;

let mut table = RoutingTable::new();
table.insert("did:said:1:sw:blake3_org", "10.0.0.1:4433".parse()?);
table.insert("did:said:1:sw:blake3_org:team_a", "10.0.1.1:4433".parse()?);

// Routes to 10.0.1.1 (longest prefix match)
let hop = table.lookup("did:said:1:sw:blake3_org:team_a:alice");

Wire Bridge

The spt-wire-bridge crate connects the SmartPoint envelope layer to the QUIC transport layer. Every message flowing over the wire is automatically sealed with a VectorEnvelope before sending and opened on receipt.

text
Application
    |
    v
WireBridge::send_message(content)
    |  VectorEnvelope::seal(pair, content)
    |  channel.send_vector(sealed_bytes)
    v
QUIC (spt-wire)
    |
    v
WireBridge::recv_message()
    |  channel.recv_vector()
    |  VectorEnvelope::open(pair, sealed_bytes)
    v
Application
rust,ignore
use spt_wire_bridge::{WireBridge, WireSession};

let bridge = WireBridge::new(channel, smartpoint_pair);
bridge.send_message(b"encrypted payload").await?;
let decrypted = bridge.recv_message().await?;

Key Types

TypeCrateDescription
TransportEndpointspt-wireQUIC server/client endpoint
TransportChannelspt-wireFramed bidirectional vector pipe
TransportConfigspt-wireEndpoint configuration
TransportAddressspt-wireDID-based address
RoutingTablespt-wireDID prefix to next-hop mapping
ConnectionCascadespt-wireAdaptive transport path selection (QUIC/punch/TURN/WT)
SubscriptionManagerspt-wireDID-prefix pub/sub subscription management
PersistentVectorStorespt-wirePersistent store-and-forward backend
VectorStorespt-wireTrait for store-and-forward backends
InMemoryVectorStorespt-wireIn-memory store-and-forward (testing)
StoredVectorspt-wirePersisted vector with metadata
WebTransportFallbackspt-wireHTTPS fallback when UDP is blocked
SptCryptoProviderspt-crypto-providerrustls CryptoProvider with ML-KEM/ML-DSA
DidVerifierspt-crypto-providerDID-based QUIC authentication against GIC
WireBridgespt-wire-bridgeEnvelope + transport integration
WireSessionspt-wire-bridgeSession state for a bridge

Released under the MIT License.