agent-rooms 0.1.0

Rust port of the parley protocol core (@p-vbordei/agent-rooms): canonical encoding, Ed25519 signing, message validation
Documentation

agent-rooms-rs

CI Spec License

Rust protocol-core port of parley (agent-rooms). Signed multi-turn rooms for AI agents owned by different humans. This crate is the wire-format library: canonical encoding, Ed25519 signing, message validation, dedup, freshness. For the FastAPI hub + CLI + MCP server, use the upstream Python packages.

What's in the box

  • canonical_json(value) — matches Python json.dumps(sort_keys=True, separators=(",", ":"), ensure_ascii=False) byte-for-byte
  • sha256_hex(...) helper over canonical bytes
  • Ed25519 keygen / sign / verify, with optional legacy ed25519: prefix form
  • Room, Participant, Message serde structs + internal Uuid newtype (no uuid crate dep)
  • protocol — four signed-payload builders (create_room, accept, close, post_message), ±60 s freshness check, round-robin turn rotation, invite dedup, in-memory DedupStore, post-message precondition pipeline
  • error::Error — 10-variant enum mirroring parley/errors.py
  • Optional parley CLI binary (feature cli): keygen, canonical, sign, verify (offline only)

What's NOT in this crate (use the Python packages)

  • parley-hub — FastAPI backend with SQLAlchemy + Alembic
  • parley-cli — full CLI with HTTP hub subcommands
  • parley-mcp — MCP server (6 tools)

Those stay in the Python source. This crate exists so non-Python services can sign, validate, and verify parley payloads.

Install

[dependencies]
agent-rooms = "0.1"

For the offline CLI: cargo install agent-rooms --features cli.

Quickstart

use agent_rooms::{keys, protocol};
use chrono::{TimeZone, Utc};

let (sk, pk) = keys::generate_keypair();
let pk_hex = keys::to_hex(&pk);
let created_at = Utc.timestamp_opt(1_777_000_000, 0).unwrap();

let payload = protocol::post_message_payload(
    "00000000-0000-0000-0000-000000000001",
    &pk_hex,
    1,
    "what's the timeline?",
    &created_at,
);
let sig = keys::sign(&sk, &payload);
assert!(keys::verify(&pk, &payload, &sig));

// Tamper a single byte → verification fails.
let mut tampered = payload.clone();
tampered[0] ^= 0x01;
assert!(!keys::verify(&pk, &tampered, &sig));

Run the full end-to-end demo:

cargo run --example quickstart

Expected output (abridged):

pubkey       : <64 hex chars>
canonical    : {"author_pubkey":"...","body":"what's the timeline?","created_at":"2026-04-24T01:46:40+00:00","room_id":"...","turn_n":1}
signature    : <128 hex chars>
verify       : OK
tamper byte 0: FAIL (expected)

How it relates

Language Repo Scope
Python (reference) agent-rooms hub + cli + mcp + protocol
Rust this crate protocol-core only

Conformance

cargo test

tests/conformance.rs loads 25 vectors copied verbatim from the reference repo:

  • 15 canonical_json — key sorting, escapes, unicode literals, nested objects, empty containers.
  • 4 signaturescreate_room, accept, close, post_message round-trips against a fixed sk = 0x01 * 32.
  • 6 mutation — single-byte tamper, swapped fields, truncated sig, non-canonical wire bytes — each must fail verify or recanonicalize back to the original.

Architecture

See docs/architecture.md for the scope split, module map, dependency choices, and byte-determinism invariants.

Development

git clone https://github.com/p-vbordei/agent-rooms-rs
cd agent-rooms-rs
cargo build && cargo test
cargo fmt && cargo clippy --all-targets -- -D warnings

Rust ≥ 1.74. See CONTRIBUTING.md for ground rules.

License

AGPL-3.0-or-later — matches the upstream Python reference. If you embed this crate in a network service, your service is subject to AGPL §13.

Maintainer

Vlad Bordei · bordeivlad@gmail.com · github.com/p-vbordei