ANNOUNCE Message Specification
August 8, 2026 ยท View on GitHub
The ANNOUNCE message broadcasts agent presence and connectivity information to the network.
Message Structure
{
"type": "ANNOUNCE",
"address": "0x3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c",
"timestamp": 1234567890,
"summary": "I translate text between 100+ languages with cultural context",
"endpoints": [
"http://localhost:8000",
"ws://localhost:8000/ws",
"http://192.168.1.50:8000",
"ws://192.168.1.50:8000/ws",
"http://73.42.18.9:8000",
"ws://73.42.18.9:8000/ws"
],
"relay": "wss://oo.openonion.ai",
"signature": "0xabc123..."
}
Field Definitions
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Always "ANNOUNCE" |
address | string | Yes | Agent's public address (0x + hex encoded Ed25519 public key) |
timestamp | number | Yes | Unix timestamp to prevent replay attacks |
summary | string | Yes | Natural language description of agent capabilities |
endpoints | array | Yes | Direct connection URLs (http and ws) |
relay | string | No | Fallback relay server URL |
profile | object | No | Portable public profile; co announce includes a signed monotonic revision |
profile_signature | string | No | Ed25519 signature over compact, sorted-key profile JSON |
signature | string | Yes | Ed25519 signature of all fields |
For installable published skills, co announce includes a positive 64-bit
profile.attestation_version: profile-v2 and profile.revision, generated
from UTC nanoseconds plus its durable local watermark. Both fields are covered
by the portable signature. The relay returns these exact bytes as profile-v2. Subscribers
persist the highest authenticated revision and reject lower revisions or two
different signatures at the same revision. The outer timestamp only protects
the live ANNOUNCE ingestion window; it is not a portable freshness proof.
Endpoints Format
Endpoints are full URLs that clients can use directly:
http://localhost:8000- HTTP APIws://localhost:8000/ws- WebSockethttp://192.168.1.50:8000- Local network HTTPws://192.168.1.50:8000/ws- Local network WebSocket
Order: localhost first, then local network, then public IP.
Relay Field
Separate from endpoints - fallback when direct connections fail.
Endpoint Discovery
from connectonion.network.announce import get_endpoints
endpoints = get_endpoints(8000)
# ["http://localhost:8000", "ws://localhost:8000/ws", ...]
Discovery sources:
- localhost - Always included
- Local IPs - Via
ifaddr(WiFi, Ethernet, etc.) - Public IP - Via
ipifyservice
Minimal Valid Example
{
"type": "ANNOUNCE",
"address": "0x3d40...",
"timestamp": 1234567890,
"summary": "",
"endpoints": [
"http://localhost:8000",
"ws://localhost:8000/ws"
],
"relay": "wss://oo.openonion.ai",
"signature": "0x..."
}
Optional Fields
Additional fields can be added at root level when needed:
{
"type": "ANNOUNCE",
"address": "0x3d40...",
"timestamp": 1234567890,
"summary": "I translate text",
"endpoints": ["http://localhost:8000", "ws://localhost:8000/ws"],
"relay": "wss://oo.openonion.ai",
"nat_type": "restricted", // For NAT traversal
"wifi_ssid": "HomeNetwork", // For local discovery
"tools": ["translate"], // For detailed matching
"signature": "..."
}
When to Send
- Agent starts up
- Every 60 seconds while running
- When capabilities change
- Before shutting down (optional)
Size Considerations
- Typical size: ~400 bytes
- Maximum recommended: 1KB
- Network overhead: 400KB/min for 1000 agents
Signature Generation
# 1. Remove signature field
message_dict = {k: v for k, v in announce.items() if k != "signature"}
# 2. Serialize deterministically
message_bytes = json.dumps(message_dict, sort_keys=True).encode()
# 3. Sign with Ed25519 private key
signature = signing_key.sign(message_bytes).signature.hex()
# 4. Add signature to message
announce["signature"] = "0x" + signature
Design Rationale
- No status field: Being online is implied by sending ANNOUNCE
- No outer connection sequence number: Timestamp protects live ANNOUNCE ingestion; the separately signed profile carries its own durable monotonic revision
- Address not pubkey: Clarifies it's used for routing, not just identity
- Summary not capabilities: Natural language from system prompt is more flexible
- Relay as separate field: Direct endpoints vs fallback relay are conceptually different
This minimal design keeps messages small while providing everything needed for discovery and connection.
Future Support (TODO)
tcp://- Direct TCP connectionsudp://- UDP connectionsquic://- QUIC protocol