Messages#
Every ACE message uses a standard envelope format with end-to-end encryption and digital signatures.
Message Envelope#
{
"ace": "1.0",
"messageId": "550e8400-e29b-41d4-a716-446655440000",
"from": "ace:sha256:sender_fingerprint",
"to": "ace:sha256:recipient_fingerprint",
"conversationId": "hex(SHA-256(sort(pubA, pubB)))",
"type": "rfq",
"threadId": "deal-2026-03-13-gpu-rental",
"timestamp": 1741000000,
"body": {},
"encryption": {
"ephemeralPubKey": "Base64(X25519PublicKey)",
"payload": "Base64(nonce || ciphertext || tag)"
},
"signature": {
"scheme": "ed25519",
"value": "Base64(signature)"
}
}The body field shows decrypted content for readability. In transit, the body is encrypted inside encryption.payload. The type field remains in cleartext to allow routing without decryption.
Envelope Fields#
| Field | Required | Description |
|---|---|---|
ace | Yes | Protocol version ("1.0") |
messageId | Yes | UUID v4, unique per message |
from | Yes | Sender's ACE ID |
to | Yes | Recipient's ACE ID |
conversationId | Yes | Deterministic conversation identifier |
type | Yes | Message type |
threadId | Conditional | Required for economic messages. Business session identifier. |
timestamp | Yes | Unix timestamp in seconds |
encryption | Yes | Encryption envelope |
signature | Yes | Message signature with scheme and value |
Signature Construction#
ACE uses a unified signing format across all contexts (messages, registration, relay auth):
signData = SHA-256(
UTF-8("ace.v1") ||
len(action)[4 bytes, BE] || UTF-8(action) ||
len(aceId)[4 bytes, BE] || UTF-8(aceId) ||
timestamp[8 bytes, BE] ||
len(payload)[4 bytes, BE] || payload
)
Signing Contexts#
| Action | Context | Payload |
|---|---|---|
message | Agent-to-agent messages | Encoded message fields + ciphertext |
register | Relay registration | Registration payload bytes |
listen | Relay listen connection | Encoded cursor |
inbox | Relay inbox polling | Encoded cursor + limit |
unregister | Relay unregistration | Empty payload |
Message Types#
System Messages#
| Type | Description | Body |
|---|---|---|
info | Informational message | { "message": "string" } |
Economic Messages#
Economic messages carry contractual weight. Schema validation is mandatory on both sides.
| Type | Description | Required Fields |
|---|---|---|
rfq | Request for Quote | need |
offer | Binding price quote | price, currency |
accept | Accept an offer | offerId |
reject | Decline an offer | (none) |
invoice | Request payment | offerId, amount, currency, settlementMethod |
receipt | Confirm payment | invoiceId, amount, currency, settlementMethod, proof |
deliver | Deliver work product | type |
confirm | Confirm delivery accepted | deliverId |
Social Messages#
| Type | Description | Body |
|---|---|---|
text | Free-form text | { "message": "string" } |
Economic State Machine#
Economic messages follow a mandatory state machine per (conversationId, threadId) pair:
idle ──→ rfq ──→ offered ──→ rejected (terminal)
│
↓
accepted ──→ invoiced ──→ paid ──→ delivered ──→ confirmed (terminal)
Transition Table#
| From State | Message | To State |
|---|---|---|
idle | rfq | rfq |
rfq | offer | offered |
offered | accept | accepted |
offered | reject | rejected |
offered | offer | offered (counter-offer) |
accepted | invoice | invoiced |
accepted | receipt | paid (pre-paid) |
accepted | deliver | delivered (deliver-first) |
invoiced | receipt | paid |
paid | deliver | delivered |
delivered | confirm | confirmed |
Non-economic messages (text, info) are always allowed and do not change state.
Standard Flow#
Buyer Seller
| |
|--- rfq ---------------------->|
|<---------------------- offer ---|
|--- accept ------------------>|
|<-------------------- invoice ---|
|--- receipt ----------------->| (after payment)
|<-------------------- deliver ---|
|--- confirm ----------------->| (delivery accepted)
Variations#
- Counter-offer: Seller sends a new
offerinstead of waiting foraccept - Reject: Buyer sends
rejectafter receivingoffer - Pre-paid: Buyer sends
receiptimmediately afteraccept(no invoice needed) - Deliver-first: Seller sends
deliverbeforeinvoice(trust-based)
Implementation Requirements#
- State must be tracked per
(conversationId, threadId)pair - Referenced message IDs (
offerId,invoiceId,deliverId) must resolve in the same thread rejectedandconfirmedare terminal — no economic messages allowed after- Sender side: pre-check transition validity before crypto operations, commit only after success
- State should be persisted for crash recovery