#tsp #affinidi #ssi

affinidi-tsp

Trust Spanning Protocol (TSP) implementation for the Affinidi TDK

15 releases

Uses new Rust 2024

0.1.14 Jul 31, 2026
0.1.13 Jul 19, 2026
0.1.12 Jun 29, 2026
0.1.1 Apr 18, 2026
0.1.0 Mar 26, 2026

#1502 in Cryptography

Download history 65/week @ 2026-06-21 142/week @ 2026-06-28 419/week @ 2026-07-05 991/week @ 2026-07-12 1555/week @ 2026-07-19 1541/week @ 2026-07-26 379/week @ 2026-08-02

4,502 downloads per month
Used in 18 crates (5 directly)

Apache-2.0

370KB
7.5K SLoC

affinidi-tsp

Rust License

A Rust implementation of the Trust Spanning Protocol (TSP), a Trust Over IP Layer 2 protocol for authenticated, encrypted messaging between Verifiable Identifiers (VIDs).

Reference Implementation: The official TSP reference implementation is maintained at github.com/trustoverip/tsp. This crate provides an independent implementation tailored for the Affinidi TDK ecosystem. It wire-interoperates with the reference (tsp-sdk) for every TSP message type (Direct, Routed, Nested, Control), both directions — see the interop status and the interop/ harness.

Using TSP through the TDK? This crate is the low-level protocol. For sending, receiving, relationships, WebSocket delivery, and authentication via the messaging SDK (atm.tsp()), see the TSP cookbook.

Feature Flags

Feature Default Description
did-resolver Yes DID-based VID resolution via affinidi-did-resolver-cache-sdk
messaging-core No Protocol-agnostic adapter for affinidi-messaging-core traits

Cryptographic Suite

Operation Algorithm
Authenticated encryption HPKE-Auth (DHKEM(X25519) + HKDF-SHA256 + ChaCha20Poly1305) per RFC 9180
Signing Ed25519
Message digest BLAKE2s-256
Encoding CESR (Composable Event Streaming Representation)

Quick Start

Add the dependency to your Cargo.toml:

[dependencies]
affinidi-tsp = { version = "0.1" }

Creating an Agent and VIDs

use affinidi_tsp::{TspAgent, PrivateVid};

// Create a TSP agent
let agent = TspAgent::new();

// Generate VIDs for Alice and Bob
let alice = PrivateVid::generate("did:example:alice");
let bob = PrivateVid::generate("did:example:bob");

// Register identities
agent.add_private_vid(alice.clone());
agent.add_private_vid(bob.clone());

Establishing a Relationship

TSP requires an explicit relationship handshake before messages can be exchanged. The lifecycle follows a state machine: NonePending/InviteReceivedBidirectional.

// Alice sends a relationship invite to Bob
let invite = agent.send_relationship_invite("did:example:alice", "did:example:bob")?;

// Bob receives and accepts the invite. The accept derives the invite's thread
// digest from the store (recorded by `receive`), so it only needs the two VIDs.
let received = agent.receive("did:example:bob", &invite.bytes)?;
let accept = agent.send_relationship_accept("did:example:bob", "did:example:alice")?;

// Alice processes the acceptance — relationship is now Bidirectional
agent.receive("did:example:alice", &accept.bytes)?;

Sending and Receiving Messages

Once a bidirectional relationship is established, agents can exchange encrypted, authenticated messages:

// Alice sends a message to Bob
let packed = agent.send("did:example:alice", "did:example:bob", b"Hello, Bob!")?;

// Bob receives and decrypts the message
let received = agent.receive("did:example:bob", &packed.bytes)?;

assert_eq!(received.payload, b"Hello, Bob!");
assert_eq!(received.sender, "did:example:alice");

Cancelling a Relationship

Either party can terminate the relationship at any time:

let cancel = agent.send_relationship_cancel("did:example:alice", "did:example:bob")?;
agent.receive("did:example:bob", &cancel.bytes)?;
// Relationship state returns to None

Relationship State Machine

None ──[SendInvite]──► Pending ──[ReceiveAccept]──► Bidirectional
│                       │                              │
│ [ReceiveInvite][ReceiveCancel][SendCancel/ReceiveCancel]
▼                       ▼                              ▼
InviteReceived          None                           None
│
│ [SendAccept]
▼
Bidirectional

Protocol-Agnostic Usage with messaging-core

For applications that need to support multiple messaging protocols (e.g. both DIDComm and TSP), enable the messaging-core feature to use the affinidi-messaging-core abstraction layer. This provides a unified interface for packing, unpacking, identity resolution, and relationship management across protocols.

[dependencies]
affinidi-tsp = { version = "0.1", features = ["messaging-core"] }
affinidi-messaging-core = "0.1"
use affinidi_tsp::{TspAgent, TspAdapter, PrivateVid};
use affinidi_messaging_core::MessagingProtocol;

// Wrap a TspAgent in the protocol-agnostic adapter
let agent = TspAgent::new();
let vid = PrivateVid::generate("did:example:alice");
agent.add_private_vid(vid);

let adapter = TspAdapter::new(agent)
    .with_default_vid("did:example:alice");

// Use the unified MessagingProtocol trait
let packed = adapter.pack(b"payload", "did:example:alice", "did:example:bob").await?;
let received = adapter.unpack(&packed).await?;
Crate Description
affinidi-messaging-core Protocol-agnostic messaging traits and types
affinidi-messaging-didcomm DIDComm v2.1 implementation
affinidi-messaging-sdk High-level messaging client SDK
affinidi-cesr CESR encoding used for TSP envelopes

License

Apache-2.0

Dependencies

~6–26MB
~362K SLoC