Messages
How messages are structured, signed, and delivered between nodes.
Message Overview
All inter-node communication in Starweft is conducted via signed envelopes. Each message is typed with a MsgType, and tampering is prevented through canonical JSON serialization and Ed25519 signatures.
Envelope Structure
Messages are sent and received through a three-stage envelope format.
UnsignedEnvelope
This is the envelope before signing. Fields are populated during message creation, then converted into a signed envelope via sign().
pub struct UnsignedEnvelope<T> {
pub protocol: String, // "starweft/0.1"
pub msg_id: MessageId, // ULID-based unique ID
pub msg_type: MsgType, // Message type
pub from_actor_id: ActorId, // Sender's actor ID
pub to_actor_id: Option<ActorId>, // Recipient (None = broadcast)
pub vision_id: Option<VisionId>,
pub project_id: Option<ProjectId>,
pub task_id: Option<TaskId>,
pub lamport_ts: u64, // Lamport logical timestamp
pub created_at: OffsetDateTime, // Creation timestamp
pub expires_at: Option<OffsetDateTime>, // Expiration time
pub body: T, // Typed message body
}Envelope (Signed)
A signed envelope produced by UnsignedEnvelope.sign(keypair). It includes a MessageSignature.
pub struct Envelope<T> {
// ... same fields as UnsignedEnvelope ...
pub body: T,
pub signature: MessageSignature, // Ed25519 signature
}WireEnvelope (Wire Format)
The format used for network transmission. The body is converted from a typed T to serde_json::Value (untyped JSON).
pub struct WireEnvelope {
// ... same fields as Envelope ...
pub body: Value, // Untyped JSON
pub signature: MessageSignature,
}Canonical JSON Serialization
To guarantee deterministic signatures, the data to be signed is serialized as canonical JSON.
- JSON keys are sorted alphabetically
- Unnecessary whitespace is removed
- Floating-point and datetime representations conform to
serde_jsondefault output
The signing target includes all envelope fields including the body, but excludes the signature field itself.
// Structure used as the signing target
struct SignableEnvelope<'a, T> {
protocol: &'a str,
msg_id: &'a MessageId,
msg_type: &'a MsgType,
from_actor_id: &'a ActorId,
to_actor_id: &'a Option<ActorId>,
vision_id: &'a Option<VisionId>,
project_id: &'a Option<ProjectId>,
task_id: &'a Option<TaskId>,
lamport_ts: u64,
created_at: OffsetDateTime,
expires_at: &'a Option<OffsetDateTime>,
body: &'a T,
}Message Type Reference
The Starweft protocol defines the following message types.
Vision & Project
VisionIntent
A vision (goal definition) submitted by the Principal. The Owner receives this and decomposes it into projects and tasks.
pub struct VisionIntent {
pub title: String, // Vision title
pub raw_vision_text: String, // Free-form vision text
pub constraints: VisionConstraints, // Execution constraints
}Direction: Principal → Owner
ProjectCharter
A project charter generated by the Owner from a vision. It defines the project structure and evaluation policy.
pub struct ProjectCharter {
pub project_id: ProjectId,
pub vision_id: VisionId,
pub principal_actor_id: ActorId, // Requester
pub owner_actor_id: ActorId, // Orchestrator
pub title: String,
pub objective: String,
pub stop_authority_actor_id: ActorId, // Stop authority holder
pub participant_policy: ParticipantPolicy,
pub evaluation_policy: EvaluationPolicy,
}Direction: Owner → Principal (+ broadcast)
Capability Discovery
CapabilityQuery
A node announces its identity and capabilities, and queries for peer capabilities.
pub struct CapabilityQuery {
pub node_id: NodeId,
pub public_key: String, // Base64-encoded public key
pub stop_public_key: Option<String>,
pub capabilities: Vec<String>, // e.g. ["openclaw.execution.v1"]
pub listen_addresses: Vec<String>,
pub requested_at: OffsetDateTime,
}Direction: All roles → Broadcast
Note: This message is self-certifying — the signature is verified using the public_key contained in the body. No prior peer public key registration is required.
CapabilityAdvertisement
Sent in response to a CapabilityQuery, advertising the local node's capabilities.
pub struct CapabilityAdvertisement {
pub node_id: NodeId,
pub public_key: String,
pub stop_public_key: Option<String>,
pub capabilities: Vec<String>,
pub listen_addresses: Vec<String>,
pub advertised_at: OffsetDateTime,
}Direction: All roles → Query originator
Participation Control
JoinOffer
The Owner proposes project participation to a Worker. Includes the required capabilities and a task outline.
pub struct JoinOffer {
pub required_capabilities: Vec<String>,
pub task_outline: String,
pub expected_duration_sec: u64,
}Direction: Owner → Worker
JoinAccept
A Worker accepts a JoinOffer.
pub struct JoinAccept {
pub accepted: bool, // true
pub capabilities_confirmed: Vec<String>,
}Direction: Worker → Owner
JoinReject
A Worker rejects a JoinOffer.
pub struct JoinReject {
pub accepted: bool, // false
pub reason: String,
}Direction: Worker → Owner
Task Execution
TaskDelegated
The Owner delegates a task to a Worker. Includes input data and the expected output schema.
pub struct TaskDelegated {
pub parent_task_id: Option<TaskId>, // Parent task (if this is a subtask)
pub title: String,
pub description: String,
pub objective: String,
pub required_capability: String,
pub input_payload: Value, // Input data for the task
pub expected_output_schema: Value, // JSON Schema for expected output
}Direction: Owner → Worker
TaskProgress
A Worker reports task progress. Automatically generated from the OpenClaw bridge's PROGRESS:<float>:<message> output.
pub struct TaskProgress {
pub progress: f32, // 0.0 to 1.0
pub message: String,
pub updated_at: OffsetDateTime,
}Direction: Worker → Owner
TaskResultSubmitted
A Worker submits the execution result of a task.
pub struct TaskResultSubmitted {
pub status: TaskExecutionStatus, // Completed / Failed / Stopped
pub summary: String,
pub output_payload: Value,
pub artifact_refs: Vec<ArtifactRef>,
pub started_at: OffsetDateTime,
pub finished_at: OffsetDateTime,
}Direction: Worker → Owner
EvaluationIssued
An evaluation certificate issued by the Owner after evaluating a task result.
pub struct EvaluationIssued {
pub subject_actor_id: ActorId, // Worker being evaluated
pub scores: BTreeMap<String, f32>, // quality, speed, reliability, alignment
pub comment: String,
}Direction: Owner → Worker (+ broadcast)
Stop Control
StopOrder
A stop order for a project or task tree. Signed with the stop_authority_key.
pub struct StopOrder {
pub stop_id: StopId,
pub scope_type: StopScopeType, // Project / TaskTree
pub scope_id: String,
pub reason_code: String, // e.g. "misalignment"
pub reason_text: String,
pub issued_at: OffsetDateTime,
}Direction: Principal → Owner → Worker
StopAck
An acknowledgment confirming that a StopOrder has been received and stop processing has begun.
pub struct StopAck {
pub stop_id: StopId,
pub actor_id: ActorId,
pub ack_state: StopAckState, // Stopping
pub acked_at: OffsetDateTime,
}Direction: Worker → Owner → Principal
StopComplete
A final confirmation that stop processing has completed.
pub struct StopComplete {
pub stop_id: StopId,
pub actor_id: ActorId,
pub final_state: StopFinalState, // Stopped
pub completed_at: OffsetDateTime,
}Direction: Worker → Owner → Principal
Snapshots
SnapshotRequest / SnapshotResponse
Used to request and return a state snapshot of a project or task.
pub struct SnapshotRequest {
pub scope_type: SnapshotScopeType, // Project / Task
pub scope_id: String,
}
pub struct SnapshotResponse {
pub scope_type: SnapshotScopeType,
pub scope_id: String,
pub snapshot: Value,
}Direction: Principal → Owner (Request) / Owner → Principal (Response)
Publishing
PublishIntentProposed / PublishIntentSkipped / PublishResultRecorded
A group of messages related to the external publication of task results (e.g., to GitHub).
- PublishIntentProposed: A proposal to publish
- PublishIntentSkipped: A notification that publication was skipped
- PublishResultRecorded: A record of the publication result
Direction: Generated and recorded internally by the Owner
Approval Control
ApprovalGranted / ApprovalApplied
Messages that control the approval gate before task execution.
- ApprovalGranted: The Principal approves execution of a project or task
- ApprovalApplied: The Owner applies the approval and resumes paused tasks
pub struct ApprovalGranted {
pub scope_type: ApprovalScopeType, // Project / Task
pub scope_id: String,
pub approved_at: OffsetDateTime,
}Direction: Principal → Owner (Granted) / Owner → Principal (Applied)
Delivery via the Outbox Pipeline
All messages are delivered through an outbox queue.
queue_outgoing(envelope)
│
▼
outbox_messages (delivery_state = "queued")
│
▼ flush_outbox()
│
├── to_actor_id present → Look up target peer from peer_addresses
│
├── to_actor_id absent → Broadcast to all peers
│ (relay nodes exclude the sender)
│
▼ transport.deliver()
│
├── Success → delivery_state = "delivered_local"
├── Failure → Retry (250ms interval, up to 20 attempts)
└── Limit exceeded → delivery_state = "dead_letter"Message Expiration
If expires_at is set on an envelope, the receiving side rejects the message if the current time exceeds the expiration plus a 30-second skew tolerance.
const ENVELOPE_EXPIRY_SKEW_SEC: i64 = 30;Protocol Version
The current protocol version is "starweft/0.1". If an incoming message has a different protocol version, it is rejected unless allow_legacy_protocols = true is configured.
[compatibility]
protocol_version = "starweft/0.1"
allow_legacy_protocols = false