How OpenHuman Implements Signal Protocol for E2E Agent-to-Agent Communication in tiny.place

OpenHuman wires the Signal protocol for E2E agent-to-agent communication in tiny.place by combining a durable Rust session store with RPC handlers that execute double-ratchet encryption and decryption.

The tinyhumansai/openhuman repository implements Signal protocol E2E agent-to-agent communication using the double-ratchet algorithm in the tiny.place domain. This architecture isolates cryptographic state in dedicated storage while exposing a thin SignalSession API for seamless integration with the OpenHuman backend.

Core Components of the Signal Integration

Persistent Session Storage in signal_store.rs

The foundation of OpenHuman's Signal protocol implementation resides in src/openhuman/tinyplace/signal_store.rs, which provides the FileSessionStore struct. This durable, file-backed store maintains each agent's long-term identity key pair, pre-keys, and per-session state in a format consumable by the Signal double-ratchet.

The FileSessionStore::new constructor initializes the storage directory and warms the cache for fast lookups. For global access across the process, the module exposes global_signal_store helpers that provide a singleton pattern for the session store.

The SignalSession Wrapper

OpenHuman utilizes the SignalSession type from the tinyplace::signal::session module (provided by the tinyplace crate) as the high-level cryptographic interface. Each session is instantiated with an identity key—typically an X25519 public key—and a reference to the FileSessionStore.

The session object exposes encrypt and decrypt methods that execute the double-ratchet forward-secure operations. When creating a new session, the code uses SignalSession::new(my_identity_key, Arc::new(store)) to bind the cryptographic state to the durable storage backend.

Message Flow and RPC Handlers in manifest.rs

The integration point between the Signal protocol and OpenHuman's RPC system lives in src/openhuman/tinyplace/manifest.rs. This file contains the handlers that expose Signal messaging to the rest of the system through JSON-RPC endpoints defined in src/openhuman/tinyplace/schemas.rs.

Sending Encrypted Messages

When an agent initiates a "send Signal DM" RPC call, the handler executes the following sequence:

  1. Loads the peer's pre-key bundle via the tiny.place key management RPCs
  2. Constructs a SignalSession using the shared FileSessionStore and the local identity key obtained via decode_identity_key
  3. Invokes session.encrypt(&peer_prekey_bundle, plaintext) to generate the ciphertext envelope
  4. Hands the encrypted payload to the OpenHuman backend for delivery

Receiving and Decrypting Messages

Inbound Signal envelopes are processed by the "decrypt Signal DM" RPC handler. This handler:

  1. Retrieves the stored session state from the FileSessionStore
  2. Reconstructs the SignalSession using the cached identity key
  3. Calls session.decrypt(&inbound) to advance the ratchet and recover the plaintext
  4. Returns the decrypted payload to the calling agent

End-to-End Verification

Testing the Double-Ratchet Implementation

The correctness of the Signal protocol integration is validated in src/openhuman/tinyplace/signal_e2e_tests.rs. This test suite creates two independent SignalSession instances—Alice and Bob—backed by the same FileSessionStore to simulate a full local round-trip.

The tests verify that messages encrypted by one side decrypt correctly on the other and confirm that the low-level Signal API interoperates properly with the high-level session wrapper. This ensures that session store persistence, key handling, and double-ratchet state management remain synchronized across encryption and decryption operations.

Implementation Example

The following Rust code demonstrates the complete workflow for initializing the Signal protocol infrastructure and executing agent-to-agent encryption:

// 1️⃣ Initialise the durable session store (once per process)
let store = FileSessionStore::new(
    std::path::PathBuf::from("/home/user/.openhuman/tinyplace/signal_store")
).await?;

// 2️⃣ Build a Signal session for the local agent
let my_id_key = my_keypair.public_key; // X25519 public key bytes
let session = SignalSession::new(my_id_key, Arc::new(store));

// 3️⃣ Encrypt a message for a remote agent
let peer_prekey_bundle = fetch_peer_prekey_bundle(peer_crypto_id).await?;
let ciphertext = session.encrypt(&peer_prekey_bundle, b"Hello from Alice!")?;

// 4️⃣ Decrypt an incoming envelope
let inbound = receive_signal_envelope().await?;
let plaintext = session.decrypt(&inbound)?;
assert_eq!(plaintext, b"Hello from Alice!");

Summary

  • OpenHuman implements Signal protocol E2E agent-to-agent communication in tiny.place through a dedicated Rust module combining durable storage and RPC handlers.
  • The FileSessionStore in signal_store.rs persists identity keys, pre-keys, and session state using a file-backed architecture with singleton access via global_signal_store.
  • SignalSession from the tinyplace crate provides the double-ratchet encryption interface, wrapping the cryptographic operations in encrypt and decrypt methods.
  • RPC handlers in manifest.rs orchestrate the message flow, fetching pre-key bundles and managing session lifecycles during send and receive operations.
  • The signal_e2e_tests.rs suite validates end-to-end correctness by simulating bidirectional communication between independent session instances.

Frequently Asked Questions

How does the FileSessionStore handle key persistence?

The FileSessionStore serializes each agent's long-term identity key pair, pre-keys, and per-session state to disk in a format compatible with the Signal double-ratchet. The FileSessionStore::new constructor creates the storage directory structure and initializes an in-memory cache for fast lookups, ensuring cryptographic material persists across process restarts while remaining accessible for rapid session reconstruction.

What encryption algorithm does OpenHuman use for agent messaging?

OpenHuman uses the Signal double-ratchet protocol for end-to-end encryption, specifically employing X25519 elliptic curve keys for identity establishment. The implementation leverages the tinyplace::signal::session module to perform forward-secure encryption and decryption operations that automatically advance the ratchet with each message exchange.

Where are the RPC handlers for Signal messaging defined?

The RPC handlers that manage Signal encryption and decryption are defined in src/openhuman/tinyplace/manifest.rs. This file implements the "send Signal DM" and "decrypt Signal DM" endpoints, which utilize decode_identity_key to extract public keys and coordinate session creation via SignalSession::new before calling the cryptographic methods.

How does OpenHuman verify the Signal protocol implementation?

Verification occurs in src/openhuman/tinyplace/signal_e2e_tests.rs, where the test suite creates independent SignalSession instances representing different agents. These tests confirm that the double-ratchet algorithm correctly encrypts and decrypts messages across distinct sessions while maintaining synchronization with the underlying FileSessionStore, ensuring the production implementation matches the Signal protocol specification.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →