Architecture
Understand Starweft's peer-to-peer architecture and communication model.
System Overview
Starweft operates on a fully peer-to-peer architecture with no central server. Every node runs as a CLI process on the user's local machine, requiring no cloud infrastructure or database servers whatsoever.
┌──────────────────────────────────────────────────────────┐
│ User's Local Machine │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ principal │────▶│ owner │ │
│ │ (CLI) │ │ (CLI) │ │
│ │ │ │ │ │
│ │ Ed25519 Key │ │ Ed25519 Key │ │
│ │ SQLite WAL │ │ SQLite WAL │ │
│ └──────────────┘ └──────┬──────┘ │
│ │ │
│ TaskDelegated │
│ │ │
│ ┌────────▼────────┐ │
│ │ worker │ │
│ │ (CLI) │ │
│ │ │ │
│ │ Ed25519 Key │ │
│ │ SQLite WAL │ │
│ │ OpenClaw bridge │ │
│ └─────────────────┘ │
│ │
│ Transport: Unix socket (local_mailbox) │
│ or libp2p TCP (multi-machine setup) │
└──────────────────────────────────────────────────────────┘Each node is an independent CLI process that maintains the following:
- Ed25519 key pair -- Used for signing and verifying messages
- SQLite WAL database -- State persistence via event sourcing
- inbox/outbox pipeline -- Message receive and send queues
Transport Layer
Starweft provides two transport backends.
local_mailbox (File-Based)
The default transport on Unix environments. It uses a /unix/ multiaddr path as a mailbox and receives messages through atomic file renames.
[node]
listen = ["/unix//home/user/.starweft/mailbox.sock"]
[p2p]
transport = "local_mailbox"- Ideal for inter-node communication on a single machine
- Messages are atomically retrieved via the filesystem
renameoperation - Crash recovery: Automatic recovery from
.recvtemporary files
libp2p TCP
Used for multi-machine setups and communication across networks. This is the default on Windows environments.
[node]
listen = ["/ip4/0.0.0.0/tcp/4001"]
[p2p]
transport = "libp2p"- Encrypted communication via the Noise protocol
- Stream multiplexing with the Yamux multiplexer
- CBOR-encoded request-response protocol (
/starweft/0.1) - Automatic reconnection to seed peers (every 5 seconds)
- Idle connections maintained for 300 seconds
- Inbox capacity limit: 10,000 messages
Automatic Peer Discovery via mDNS
When using the libp2p transport, enabling mDNS allows automatic detection of peers on the local network.
[discovery]
mdns = trueEvent Sourcing and SQLite WAL
Starweft employs an event sourcing pattern. All protocol messages are recorded as immutable events in the task_events table, while projection tables (projects, tasks, task_results, etc.) provide the current state view.
┌──────────────────┐
│ Incoming Message │
│ (WireEnvelope) │
└────────┬─────────┘
│
▼
┌──────────────────┐ ┌──────────────────┐
│ task_events │────▶│ projects │
│ (Event Log) │ │ tasks │
│ ──────────── │ │ task_results │
│ msg_id (PK) │ │ artifacts │
│ msg_type │ │ evaluation_certs│
│ body_json │ │ stop_orders │
│ signature_json │ │ (Projections) │
│ lamport_ts │ └──────────────────┘
└──────────────────┘Key table structure:
| Table | Purpose |
|---|---|
local_identity | The node's own actor ID, public key, and display name |
peer_addresses | Known peers' multiaddr addresses |
peer_keys | Peers' public keys and capabilities |
visions | Submitted visions |
projects | Project state (planning / active / stopping / stopped) |
tasks | Task state (queued → running → completed/failed/stopped) |
task_events | Immutable log of all messages |
task_results | Summary of task execution results |
inbox_messages | Receive queue (with deduplication) |
outbox_messages | Send queue (with retry and dead-letter management) |
SQLite's WAL (Write-Ahead Logging) mode ensures read/write concurrency and fault tolerance.
inbox/outbox Pipeline
Message sending and receiving is managed through an inbox/outbox pattern via the RuntimePipeline.
Receive flow:
transport.receive()
→ JSON deserialization of WireEnvelope
→ Signature verification (Ed25519)
→ Deduplication check (inbox_message_processed)
→ Routed to appropriate handler by MsgType via route_incoming_wire()
→ Projection update
→ mark_inbox_message_processed()
Send flow:
RuntimePipeline.queue_outgoing()
→ Saved to outbox_messages with queued status
flush_outbox()
→ Delivery destination resolved from peer_addresses
→ Sent via transport.deliver()
→ Success: status updated to delivered
→ Failure: retry (250ms interval, up to 20 attempts)
→ Limit exceeded: transitions to dead_letter statusEd25519 Signature Verification
All protocol messages are signed with Ed25519.
A SignableEnvelope struct is created from the UnsignedEnvelope. All fields, including the body, are included in the signing payload.
The SignableEnvelope is serialized into canonical JSON (keys sorted, no whitespace) to produce a deterministic byte sequence.
The canonical JSON byte sequence is signed with the Ed25519 private key, generating a MessageSignature (algorithm "ed25519" + key_id + Base64 signature).
The receiving side reconstructs the same canonical JSON using the sender's public key and verifies the signature. Peers' public keys are exchanged in a self-certifying manner through CapabilityQuery / CapabilityAdvertisement messages.
// Signature structure
pub struct MessageSignature {
pub alg: String, // "ed25519"
pub key_id: KeyId, // Unique identifier for the key
pub sig: String, // Base64-encoded signature
}Crate Dependency Structure
The Starweft codebase is organized into crates separated by responsibility.
apps/starweft/
└── src/ — CLI core (commands, runtime, TUI)
crates/
├── starweft-protocol/ — Message types, envelopes, signature verification
├── starweft-store/ — SQLite persistence, event sourcing
├── starweft-runtime/ — inbox/outbox pipeline
├── starweft-p2p/ — local_mailbox + libp2p transport
├── starweft-crypto/ — Ed25519 key management, canonical JSON
├── starweft-id/ — ULID-based typed IDs
├── starweft-observation/ — Task decomposition, evaluation scoring
├── starweft-openclaw-bridge/ — OpenClaw subprocess execution
└── starweft-stop/ — Stop control logicDependency direction:
starweft (CLI)
├── starweft-protocol ← starweft-crypto, starweft-id
├── starweft-store ← starweft-protocol, starweft-crypto, starweft-id, starweft-stop
├── starweft-runtime ← starweft-protocol, starweft-store
├── starweft-p2p ← (libp2p, multiaddr)
├── starweft-observation ← starweft-protocol
├── starweft-openclaw-bridge ← starweft-protocol
└── starweft-stop ← (standalone)