korium 0.2.4

Batteries-included adaptive networking fabric
Documentation

Korium

Rust License Crates.io Documentation

Batteries-included adaptive networking fabric

Korium is a high-performance, secure, and adaptive networking library written in Rust. It provides a robust foundation for building decentralized applications, scale-out fabrics, and distributed services with built-in NAT traversal, efficient PubSub, and a cryptographic identity system.

Why Korium?

  • Zero Configuration — Self-organizing mesh with automatic peer discovery
  • NAT Traversal — Built-in relay infrastructure and path probing via SmartSock
  • Secure by Default — Ed25519 identities with mutual TLS on every connection
  • Namespace Isolation — Multi-tenant support with encrypted, authenticated overlays
  • Adaptive Performance — Latency-tiered DHT with automatic path optimization
  • Complete Stack — PubSub messaging, request-response, direct connections, and membership management
  • SPIFFE Compatible — Optional X.509 URI SAN extensions for enterprise identity interoperability

Quick Start

Add Korium to your Cargo.toml:

[dependencies]
korium = "0.2"
tokio = { version = "1", features = ["full"] }

Create a Node

use korium::Node;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Bind to any available port
    let node = Node::bind("0.0.0.0:0").await?;
    
    println!("Node identity: {}", node.identity());
    println!("Listening on: {}", node.local_addr()?);
    
    // Bootstrap from an existing peer
    node.bootstrap("peer_identity_hex", "192.168.1.100:4433").await?;
    
    Ok(())
}

PubSub Messaging

// Subscribe to a topic
node.subscribe("events/alerts").await?;

// Publish messages (signed with your identity)
node.publish("events/alerts", b"System update available".to_vec()).await?;

// Receive messages
let mut rx = node.messages().await?;
while let Some(msg) = rx.recv().await {
    println!("[{}] from {}: {:?}", msg.topic, &msg.from[..16], msg.data);
}

Request-Response

// Set up a request handler (echo server)
node.set_request_handler(|from, request| {
    println!("Request from {}: {:?}", &from[..16], request);
    request  // Echo back the request as response
}).await?;

// Send a request and get a response
let response = node.send("peer_identity_hex", b"Hello!".to_vec()).await?;
println!("Response: {:?}", response);

// Or use the low-level API for async handling
let mut requests = node.incoming_requests().await?;
while let Some((from, request, response_tx)) = requests.recv().await {
    // Process request asynchronously
    let response = process_request(request);
    response_tx.send(response).ok();
}

Peer Discovery

// Find peers near a target identity
let peers = node.find_peers(target_identity).await?;

// Resolve a peer's published contact record
let contact = node.resolve(&peer_identity).await?;

// Publish your address for others to discover
node.publish_address(vec!["192.168.1.100:4433".to_string()]).await?;

NAT Traversal

// Automatic NAT configuration (helper is a known peer identity in the DHT)
let helper_identity = "abc123..."; // hex-encoded peer identity
let (is_public, relay, incoming_rx) = node.configure_nat(helper_identity, addresses).await?;

if is_public {
    println!("Publicly reachable - can serve as relay");
} else {
    println!("Behind NAT - using relay: {:?}", relay);
    
    // Handle incoming relay connections via mesh signaling
    if let Some(mut rx) = incoming_rx {
        while let Some(incoming) = rx.recv().await {
            node.accept_incoming(&incoming).await?;
        }
    }
}

// Alternative: Enable mesh-mediated signaling (no dedicated relay connection)
let mut rx = node.enable_mesh_signaling().await;
while let Some(incoming) = rx.recv().await {
    node.accept_incoming(&incoming).await?;
}

Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                              Node                                   │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌────────────┐  │
│  │  GossipSub  │  │   Crypto    │  │     DHT     │  │   Relay    │  │
│  │   (PubSub)  │  │ (Identity)  │  │ (Discovery) │  │  (Client)  │  │
│  └──────┬──────┘  └─────────────┘  └──────┬──────┘  └─────┬──────┘  │
│         │                                 │                │        │
│  ┌──────┴─────────────────────────────────┴────────────────┴──────┐ │
│  │                          RpcNode                               │ │
│  │            (Connection pooling, request routing)               │ │
│  └────────────────────────────┬───────────────────────────────────┘ │
│  ┌────────────────────────────┴───────────────────────────────────┐ │
│  │                         SmartSock                              │ │
│  │  (Path probing, relay tunnels, virtual addressing, QUIC mux)   │ │
│  └────────────────────────────┬───────────────────────────────────┘ │
│  ┌────────────────────────────┴───────────────────────────────────┐ │
│  │                       QUIC (Quinn)                             │ │
│  └────────────────────────────┬───────────────────────────────────┘ │
│  ┌────────────────────────────┴───────────────────────────────────┐ │
│  │                   UDP Socket + Relay Server                    │ │
│  └────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘

Module Overview

Module Description
node High-level facade exposing the complete public API
transport SmartSock with path probing, relay tunnels, and virtual addresses
rpc Connection pooling, RPC dispatch, and actor-based state management
dht Kademlia-style DHT with latency tiering, adaptive parameters, and peer discovery
gossipsub GossipSub v1.1/v1.2 epidemic broadcast with peer scoring
relay UDP relay server and client with mesh-mediated signaling for NAT traversal
crypto Ed25519 certificates, identity verification, custom TLS
identity Keypairs, endpoint records, and signed address publication
protocols Protocol trait definitions (DhtNodeRpc, GossipSubRpc, RelayRpc, PlainRpc)
messages Protocol message types and bounded serialization
thresholdca FROST threshold CA (K-of-N signing) — requires spiffe feature

Core Concepts

Identity (Ed25519 Public Keys)

Every node has a cryptographic identity derived from an Ed25519 keypair:

let node = Node::bind("0.0.0.0:0").await?;
let identity: String = node.identity();  // 64 hex characters (32 bytes)
let keypair = node.keypair();            // Access for signing

Identities are:

  • Self-certifying — The identity IS the public key
  • Collision-resistant — 256-bit space makes collisions infeasible
  • Verifiable — Every connection verifies peer identity via mTLS

Contact

A Contact represents a reachable peer:

pub struct Contact {
    pub identity: Identity,   // Ed25519 public key
    pub addrs: Vec<String>,   // List of addresses (IP:port)
}

SmartAddr (Virtual Addressing)

SmartSock maps identities to virtual IPv6 addresses in the fd00:c0f1::/32 range:

Identity (32 bytes) → blake3 hash → fd00:c0f1:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx

This enables:

  • Transparent path switching — QUIC sees stable addresses while SmartSock handles path changes
  • Relay abstraction — Applications use identity-based addressing regardless of NAT status

SmartConnect

Automatic connection establishment with fallback:

  1. Try direct connection to published addresses
  2. If direct fails, use peer's designated relays
  3. Configure relay tunnel and establish QUIC connection through relay
// SmartConnect handles all complexity internally
let conn = node.connect("target_identity_hex").await?;

NAT Traversal

Mesh-First Relay Model

Korium uses a mesh-first relay model where any reachable mesh peer can act as a relay:

  1. No dedicated relay servers — Any publicly reachable node serves as a relay
  2. Mesh-mediated signaling — Relay signals forwarded through GossipSub mesh
  3. Opportunistic relaying — Connection attempts try mesh peers as relays
  4. Zero configuration — Works automatically when mesh peers are available

How SmartSock Works

SmartSock implements transparent NAT traversal:

  1. Path Probing — Periodic probes measure RTT to all known paths
  2. Path Selection — Best path chosen (direct preferred, relay as fallback)
  3. Relay Tunnels — UDP packets wrapped in CRLY frames through relay
  4. Automatic Upgrade — Switch from relay to direct when hole-punch succeeds

Protocol Headers

Path Probe (SMPR)

┌──────────┬──────────┬──────────┬──────────────┐
│  Magic   │   Type   │  Tx ID   │  Timestamp   │
│  4 bytes │  1 byte  │  8 bytes │   8 bytes    │
└──────────┴──────────┴──────────┴──────────────┘

Relay Frame (CRLY)

┌──────────┬──────────────┬──────────────────────┐
│  Magic   │  Session ID  │    QUIC Payload      │
│  4 bytes │   16 bytes   │     (variable)       │
└──────────┴──────────────┴──────────────────────┘

Path Selection Algorithm

if direct_path.rtt + 10ms < current_path.rtt:
    switch to direct_path
elif relay_path.rtt + 50ms < direct_path.rtt:
    switch to relay_path (relay gets 50ms handicap)

DHT (Distributed Hash Table)

Kademlia Implementation

The DHT is used internally for peer discovery and address publication:

  • 256 k-buckets with configurable k (default: 20, adaptive: 10-30)
  • Iterative lookups with configurable α (default: 3, adaptive: 2-5)
  • S/Kademlia PoW: Identity generation requires Proof-of-Work for Sybil resistance

Key Operations

// Find peers near a target identity
let peers = node.find_peers(target_identity).await?;

// Resolve peer's published contact record
let contact = node.resolve(&peer_id).await?;

// Publish your address for discovery
node.publish_address(vec!["192.168.1.100:4433".to_string()]).await?;

Latency Tiering

The DHT implements Coral-inspired latency tiering:

  • RTT samples collected per /16 IP prefix (IPv4) or /32 prefix (IPv6)
  • K-means clustering groups prefixes into 1-7 latency tiers
  • Tiered lookups prefer faster prefixes for lower latency
  • LRU-bounded — tracks up to 10,000 active prefixes (~1MB memory)

Scalability (10M+ Nodes)

Korium is designed to scale to millions of concurrent peers. Key design decisions enable efficient operation at scale:

Memory Efficiency (Per-Node at 10M Network)

Each node uses constant memory regardless of network size:

Component Memory Design
Routing table ~640 KB 256 buckets × 20 contacts
RTT tiering ~1 MB /16 prefix-based (not per-peer)
Passive view ~13 KB 100 recovery candidates
Connection cache ~200 KB 1,000 LRU connections
Peer scoring ~1 MB 10K active peers scored
Message dedup ~2 MB 10K source sequence windows
Total ~5 MB Bounded, scales to 10M+ nodes

DHT Performance

Metric Value Notes
Lookup hops O(log₂ N) ≈ 23 Standard Kademlia complexity
Parallel queries (α) 2-5 adaptive Reduces under congestion
Bucket size (k) 10-30 adaptive Increases with churn
Routing contacts ~5,120 max 256 buckets × 20

Korium vs Standard Kademlia

Feature Standard Kademlia Korium Benefit
Bucket size Fixed k=20 Adaptive 10-30 Handles churn spikes
Concurrency Fixed α=3 Adaptive 2-5 Load shedding
RTT optimization ❌ None /16 prefix tiering Lower latency paths
Sybil protection ❌ Basic S/Kademlia PoW + per-peer limits Eclipse resistant
Gossip layer ❌ None GossipSub v1.1/v1.2 Fast broadcast, scoring
NAT traversal ❌ None SmartSock + mesh relays Works behind NAT
Identity SHA-1 node IDs Ed25519 + PoW Self-certifying, Sybil-resistant

Scaling Boundaries (Per-Node)

These limits are per-node, not network-wide. With 10M nodes, the network's aggregate capacity scales linearly:

Parameter Per-Node Limit At 10M Nodes Notes
Routing contacts ~5,120 N/A O(log N) = 23 hops at 10M
Contact records 100K entries 1 trillion Distributed across DHT
Scored peers 10,000 100 billion Per-node active peer set
PubSub topics 10,000 100 billion Topics span multiple nodes
Peers per topic 1,000 N/A Gossip efficiency bound
Relay sessions 10,000 100 billion Per-relay server

Key Design Decisions

  1. Prefix-based RTT — Tracking RTT per /16 IP prefix instead of per-peer reduces memory from O(N) to O(65K) while maintaining routing quality through statistical sampling.

  2. Adaptive parameters — k and α automatically adjust based on observed churn rate, preventing cascade failures during network instability.

  3. Bounded data structures — All caches use LRU eviction with fixed caps, ensuring memory stays constant regardless of network size.

GossipSub (PubSub)

GossipSub v1.1/v1.2 Implementation

Korium implements the full GossipSub v1.1 specification with v1.2 extensions:

  • Peer Scoring (P1-P7): Time in mesh, message delivery, invalid messages, IP colocation
  • Adaptive Gossip: D_score mesh quotas, Opportunistic Grafting, Flood Publishing
  • IDontWant (v1.2): Bandwidth optimization for large messages
  • Mesh Management: D, D_lo, D_hi, D_out, D_score parameters
  • Prune Backoff: Exponential backoff for pruned peers

Epidemic Broadcast

GossipSub implements efficient topic-based publish/subscribe:

  • Mesh overlay — Each topic maintains a mesh of connected peers
  • Eager push — Messages forwarded immediately to mesh peers
  • Flood publishing — Publishers send to all peers above publish threshold
  • Gossip protocol — IHave/IWant metadata exchange for reliability
  • Relay signaling — NAT traversal signals forwarded through mesh peers

Message Flow

Publisher → Mesh Push → Subscribers
              ↓
         Gossip (IHave)
              ↓
         IWant requests
              ↓
         Message delivery

Message Authentication

All published messages include Ed25519 signatures:

// Messages are signed with publisher's keypair
node.publish("topic", data).await?;

// Signatures verified on receipt (invalid messages rejected)
let msg = rx.recv().await?;  // msg.from is verified sender

Rate Limiting

Limit Value
Publish rate 100/sec
Per-peer receive rate 50/sec
Max message size 64 KB
Max topics 10,000
Max peers per topic 1,000

Security

Defense Layers

Layer Protection
Identity Ed25519 keypairs, identity = public key
Transport Mutual TLS on all QUIC connections
RPC Identity verification on every request
Storage Per-peer quotas, rate limiting, content validation
Routing Rate-limited insertions, ping verification, S/Kademlia PoW
PubSub Message signatures, replay detection, peer scoring (P1-P7), IP colocation (P6)

Security Constants

Constant Value Purpose
MAX_VALUE_SIZE 1 MB DHT value limit
MAX_RESPONSE_SIZE 1 MB RPC response limit
MAX_SESSIONS 10,000 Relay session limit
MAX_SESSIONS_PER_IP 50 Per-IP relay rate limit
PER_PEER_STORAGE_QUOTA 1 MB DHT storage per peer
PER_PEER_ENTRY_LIMIT 100 DHT entries per peer
MAX_CONCURRENT_STREAMS 64 QUIC streams per connection
POW_DIFFICULTY 24 bits Identity PoW (Sybil resistance)

Namespaces (Tenant Isolation)

Korium supports namespace-based isolation for multi-tenant deployments. Namespaces create cryptographically isolated overlays on top of the global mesh infrastructure.

Architecture: Global Infrastructure, Isolated Application Traffic

┌─────────────────────────────────────────────────────────────────────┐
│                         Global Infrastructure                       │
│   ┌─────────────┐    ┌─────────────┐    ┌─────────────┐             │
│   │     DHT     │    │    Relay    │    │ Bootstrap   │             │
│   │ (Discovery) │    │ (NAT Trav)  │    │  (Routing)  │             │
│   └─────────────┘    └─────────────┘    └─────────────┘             │
│                          ↑  ↑  ↑                                    │
│                          │  │  │   All nodes share infrastructure   │
│   ───────────────────────┼──┼──┼──────────────────────────────────  │
│                          │  │  │                                    │
│  ┌───────────────────────┴──┴──┴─────────────────────────────────┐  │
│  │                    Namespace Gating Layer                     │  │
│  │         Challenge-Response + Payload Encryption               │  │
│  └───────────────────────────────────────────────────────────────┘  │
│                          ↓  ↓  ↓                                    │
│         ┌────────────────┴──┴──┴────────────────┐                   │
│         │        Isolated Application Traffic   │                   │
│         │   ┌─────────────┐    ┌─────────────┐  │                   │
│         │   │  GossipSub  │    │    Plain    │  │                   │
│         │   │   (PubSub)  │    │   (Req/Res) │  │                   │
│         │   └─────────────┘    └─────────────┘  │                   │
│         └───────────────────────────────────────┘                   │
└─────────────────────────────────────────────────────────────────────┘

Key Properties:

  • DHT/Relay are global — All nodes participate in routing and relay infrastructure
  • GossipSub/Plain are gated — Only peers with matching namespace secrets can exchange application traffic
  • Authentication — Challenge-response protocol verifies namespace membership before accepting streams
  • Encryption — ChaCha20-Poly1305 authenticated encryption protects all application payloads

Creating a Namespaced Node

use korium::{Node, NamespaceConfig};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Shared secret for the namespace (must be identical across all nodes in namespace)
    let master_secret: [u8; 32] = derive_from_your_key_management_system();
    
    let node = Node::builder("0.0.0.0:0")
        .namespace_config(NamespaceConfig::new(master_secret))
        .build()
        .await?;
    
    // This node can:
    // ✓ Participate in global DHT (peer discovery)
    // ✓ Use global relay infrastructure (NAT traversal)
    // ✓ Only exchange GossipSub/Plain with peers that know master_secret
    
    Ok(())
}

NamespaceConfig Options

// Default: 1-hour epochs, 2-epoch grace period
let config = NamespaceConfig::new(master_secret);

// Custom epoch duration and grace period
let config = NamespaceConfig::new_with_params(
    master_secret,
    Duration::from_secs(3600),  // Epoch duration (key rotation interval)
    3,                          // Grace epochs (tolerance for clock skew)
);

Security Mechanisms

1. Challenge-Response Authentication

Before accepting GossipSub or Plain streams, peers verify namespace membership:

Initiator                                    Responder
    │                                            │
    │──── Connect (QUIC mTLS) ───────────────────│
    │                                            │
    │──── Open Stream (GossipSub/Plain) ─────────│
    │                                            │
    │◄─── Challenge(nonce) ──────────────────────│
    │                                            │
    │──── Response(BLAKE3(session_secret ║       │
    │              nonce ║ pubkeys)) ────────────│
    │                                            │
    │     ✓ Verified: Accept stream              │
    │     ✗ Failed: Reject stream                │

The challenge response is bound to:

  • Session secret — Derived from master_secret + epoch
  • Connection — Both peer public keys included
  • Freshness — Random nonce prevents replay

2. Payload Encryption (ChaCha20-Poly1305)

All GossipSub and Plain payloads are encrypted:

Encryption Key = BLAKE3("korium-ns-encrypt-v1:" ║ master_secret ║ epoch)

Ciphertext = nonce (12 bytes) ║ encrypted_data ║ auth_tag (16 bytes)

Properties:

  • Confidentiality — Payload contents hidden from non-members
  • Integrity — AEAD authentication detects tampering
  • Epoch rotation — Keys rotate periodically; grace period allows decryption during transitions

Namespace Isolation Properties

Traffic Type Namespaced Node Behavior
DHT Global — Discovers all peers, serves routing
Relay Global — Uses/provides relay for all peers
GossipSub Isolated — Only sends/receives with namespace peers
Plain (Req/Res) Isolated — Only sends/receives with namespace peers

Identity PoW Binding (Optional)

For additional Sybil resistance within namespaces, bind PoW to the namespace:

// Generate keypair with PoW bound to namespace hash
let (keypair, proof) = Keypair::generate_with_pow_for_namespace("my-namespace")?;

// Build node with namespace-bound identity
let node = Node::builder("0.0.0.0:0")
    .with_keypair(keypair, proof)
    .namespace_config(NamespaceConfig::new(master_secret))
    .build()
    .await?;

This ensures:

  • PoW cannot be reused across namespaces (hash is bound to namespace)
  • namespace_hash is hidden from serialized contacts (prevents namespace enumeration)
  • Attackers cannot forge namespace membership by observing DHT traffic

Use Cases

Scenario Configuration
Multi-tenant SaaS Each tenant gets a unique master_secret
Private networks Namespace acts as a VPN overlay on public infrastructure
Regulatory compliance Isolate data flows while sharing routing infrastructure
Development/staging Separate environments on shared mesh

SPIFFE Compatibility (Optional)

Korium supports SPIFFE (Secure Production Identity Framework for Everyone) as an optional interoperability layer. This enables Korium nodes to present SPIFFE-compliant X.509 certificates for integration with enterprise service meshes and identity-aware proxies.

The spiffe feature includes:

  • SPIFFE ID generation — X.509 URI SAN extensions with trust domain/workload identifiers
  • Threshold CA — Distributed K-of-N certificate signing using FROST signatures

Architecture

Important: SPIFFE compatibility is additive—it does NOT replace Korium's native Ed25519 self-certifying identity model. The SPIFFE ID is embedded as a URI SAN (Subject Alternative Name) in X.509 certificates and is cryptographically bound to the node's identity.

SPIFFE ID Format:
spiffe://{trust_domain}/{identity_hex}[/{workload_path}]

Example:
spiffe://production.example.com/d4f5a6b7c8.../api-gateway

Enabling SPIFFE

Add the feature flag to your Cargo.toml:

[dependencies]
korium = { version = "0.2", features = ["spiffe"] }

Creating a Node with SPIFFE

use korium::Node;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let node = Node::builder("0.0.0.0:0")
        .spiffe_trust_domain("production.example.com")
        .spiffe_workload_path("api-gateway")
        .build()
        .await?;
    
    println!("Node identity: {}", node.identity());
    // Certificate now contains URI SAN: spiffe://production.example.com/{identity}/api-gateway
    
    Ok(())
}

SPIFFE API

Method Description
NodeBuilder::spiffe_trust_domain(domain) Set the SPIFFE trust domain
NodeBuilder::spiffe_workload_path(path) Set optional workload path suffix

Certificates with SPIFFE URI SANs are generated automatically when building a node with spiffe_trust_domain() set.

Verification Without a CA

Korium does not use a central Certificate Authority. Instead, verification relies on the self-certifying property of Ed25519 identities:

Standard SPIFFE:
  SPIRE CA signs SVID → Verifier trusts CA → Trusts workload identity

Korium (Self-Certifying):
  TLS handshake proves key possession → Public key IS the identity
  → SPIFFE ID is metadata bound to verified key

Verification Flow:

  1. TLS Handshake — Peer must prove possession of the private key (cryptographic proof)
  2. Extract Public Key — Verifier extracts Ed25519 public key from the X.509 certificate
  3. Identity = Key — The 32-byte public key IS the canonical identity (no trust delegation)
  4. SPIFFE ID Validation — If SPIFFE ID present, verify the identity hex in the URI path matches the certificate's public key
// Verification pseudocode
let cert_pubkey = extract_pubkey_from_cert(&peer_cert);  // From TLS handshake
let expected_identity = Identity(cert_pubkey);           // Identity = Public Key

if let Some(spiffe_id) = extract_spiffe_id_from_cert(&peer_cert)? {
    // Validate SPIFFE ID is bound to the same identity
    let spiffe_identity = validate_spiffe_id(&spiffe_id, "my-trust-domain")?;
    assert_eq!(spiffe_identity, expected_identity);  // Cryptographic binding
}

Why This Works:

Property Standard SPIFFE Korium
Trust anchor CA certificate None (self-certifying)
Identity proof CA signature TLS key possession
Revocation CA-managed Peer scoring / DHT expiry
SPIFFE ID role Primary identity Interoperability metadata

Security Considerations

  • Cryptographic Binding: The identity hex is embedded in the SPIFFE URI path, ensuring the SPIFFE ID cannot be forged independently of the Ed25519 keypair
  • No Central Authority: Unlike standard SPIFFE deployments, Korium nodes self-issue certificates—the SPIFFE ID provides format compatibility, not centralized trust
  • Zero Runtime Cost: When the spiffe feature is disabled, no SPIFFE code is compiled
  • No Revocation Infrastructure: Without a CA, certificate revocation relies on DHT record expiry and GossipSub peer scoring—nodes with bad behavior are deprioritized, not revoked

Threshold CA

For enterprise integrations requiring CA-backed certificates, the spiffe feature includes a distributed threshold CA using FROST (Flexible Round-Optimized Schnorr Threshold) signatures. This enables K-of-N signing without any single party holding the complete CA private key.

How It Works

┌─────────────────────────────────────────────────────────────────────┐
│                    Threshold CA Architecture                         │
│                                                                      │
│  1. DKG (one-time setup, external):                                 │
│     - N signers run 3-round protocol                                │
│     - Each signer gets KeyPackage (private share)                   │
│     - All get combined CA public key                                │
│                                                                      │
│  2. Signing (per certificate):                                       │
│     - Requester broadcasts CSR to signers                           │
│     - K signers validate and produce partial signatures             │
│     - Requester combines into valid FROST signature                 │
│                                                                      │
│  Trust Model:                                                        │
│     - Verifier trusts CA_pubkey (32 bytes)                          │
│     - CA_pubkey created by committee via DKG                        │
│     - Any K honest signers can produce valid signatures             │
└─────────────────────────────────────────────────────────────────────┘

Note: DKG (Distributed Key Generation) is performed externally using the Korium CLI or custom tooling. The internal DKG protocol types are not part of the public API—nodes receive pre-generated SignerState from the ceremony.

Node Integration

For automatic signing, configure nodes as signers using NodeBuilder:

use korium::{Node, CaRequestConfig, SignerState};

// === Signer Node ===
// Load signer state from encrypted storage (from DKG ceremony)
let signer_state = SignerState::deserialize(&encrypted_state)?;

let signer_node = Node::builder("0.0.0.0:4433")
    .spiffe_trust_domain("example.org")
    .as_ca_signer(signer_state)
    .build()
    .await?;
// Node now automatically responds to CSR requests:
// 1. Listens on GossipSub `csr` topic for CSR broadcasts
// 2. Sends commitment via RPC to requester
// 3. Receives sign-request via RPC with all commitments
// 4. Responds with signature share via RPC

// === Requesting Node ===
let node = Node::builder("0.0.0.0:4433")
    .spiffe_trust_domain("example.org")
    .build()
    .await?;

// First bootstrap into the mesh
node.bootstrap(&bootstrap_identity, &bootstrap_addr).await?;

// Then request CA certificate
let config = CaRequestConfig {
    signer_identities: vec![signer1, signer2, signer3],
    min_signers: 2,
    ca_public_key,
    timeout: Duration::from_secs(30),
};
let cert_der = node.request_ca_certificate_from_mesh("example.org", Some("api-gw"), &config).await?;
// cert_der is a valid X.509 certificate signed by the threshold CA

Threshold CA Protocol

┌─────────────┐   GossipSub: csr    ┌─────────────┐
│  Requester  │ ─────────────────▶  │   Signers   │
│             │                     │  (K of N)   │
│             │ ◀─────────────────  │             │
│             │  RPC: commitment    └─────────────┘
│             │                           │
│             │  ─────────────────▶       │
│             │  RPC: sign-request        │
│             │  (all commitments)        │
│             │ ◀─────────────────        │
│             │  RPC: signature share     │
└─────────────┘                           │
      │ aggregate K shares                │
      ▼                                   │
   [CA-signed X.509 cert]                 │

Threshold CA API

Type/Function Description
SignerState Holds private key share (generated externally, serialize for persistence)
CaPublicKey Distributable CA public key
CaRequestConfig Configuration for requesting CA-signed certificates
ThresholdCaConfig Configuration (N signers, K threshold, trust domain)
ThresholdCaError Error type for threshold CA operations
NodeBuilder::as_ca_signer() Configure node as CA signer
Node::request_ca_certificate_from_mesh() Request CA-signed certificate

Security Properties

Property Value
Byzantine Tolerance Survives up to N-K malicious signers
No Trusted Dealer DKG ensures no party sees complete key
Identifiable Aborts Misbehaving signers can be detected
Key Shares Must be stored encrypted at rest
Signature Algorithm FROST (RFC 9591) over Ed25519

GossipSub Topics (Threshold CA)

Topic Purpose
csr Certificate signing request broadcast

Note: Commitments and signature shares are exchanged via RPC (point-to-point), not GossipSub, for privacy and efficiency.

CLI Usage

Running a Node

# Start a node on a random port
cargo run

# Start with specific bind address
cargo run -- --bind 0.0.0.0:4433

# Bootstrap from existing peer
cargo run -- --bootstrap 192.168.1.100:4433/abc123...def456

# With debug logging
RUST_LOG=debug cargo run

Chatroom Example

# Terminal 1: Start first node
cargo run --example chatroom -- --name Alice --room dev

# Terminal 2: Join with bootstrap (copy the bootstrap string from Terminal 1)
cargo run --example chatroom -- --name Bob --room dev --bootstrap <bootstrap_string>

The chatroom demonstrates:

  • PubSub messaging (/room messages)
  • Direct messaging (/dm <identity> <message>)
  • Peer discovery (/peers)

Testing

# Run all tests
cargo test

# Run with logging
RUST_LOG=debug cargo test

# Run specific test
cargo test test_smart_addr

# Run integration tests
cargo test --test node_public_api

# Run relay tests
cargo test --test relay_infrastructure

# Run Threshold CA tests
cargo test --features "spiffe,test-pow" thresholdca

# Spawn local cluster (7 nodes)
./scripts/spawn_cluster.sh

Dependencies

Crate Purpose
quinn QUIC implementation
tokio Async runtime
ed25519-dalek Ed25519 signatures
blake3 Fast cryptographic hashing
rustls TLS implementation
bincode Binary serialization
lru LRU caches
tracing Structured logging
rcgen X.509 certificate generation (URI SAN for SPIFFE)
x509-parser Certificate parsing (SPIFFE ID extraction)
frost-ed25519 FROST threshold signatures (optional, spiffe feature)

References

NAT Traversal with QUIC

  • Liang, J., et al. (2024). Implementing NAT Hole Punching with QUIC. VTC2024-Fall. arXiv:2408.01791

    Demonstrates QUIC hole punching advantages and connection migration saving 2 RTTs.

Distributed Hash Tables

  • Freedman, M. J., et al. (2004). Democratizing Content Publication with Coral. NSDI '04. PDF

    Introduced "sloppy" DHT with latency-based clustering—inspiration for Korium's tiering system.

  • Baumgart, I. & Mies, S. (2007). S/Kademlia: A Practicable Approach Towards Secure Key-Based Routing. ICPP '07.

    The S/Kademlia specification that Korium implements for Sybil-resistant identity generation via Proof-of-Work.

GossipSub / PlumTree

  • Vyzovitis, D., et al. (2020). GossipSub: Attack-Resilient Message Propagation in the Filecoin and ETH2.0 Networks.

    The GossipSub v1.1 specification that Korium's PubSub implementation follows, including peer scoring (P1-P7), Adaptive Gossip, and mesh management.

  • Leitão, J., Pereira, J., & Rodrigues, L. (2007). Epidemic Broadcast Trees. SRDS '07.

    The PlumTree paper that influenced GossipSub's design, combining gossip reliability with efficient message propagation.

License

MIT License - see LICENSE for details.