Skip to content

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.

plaintext
┌──────────────────────────────────────────────────────────┐
│                  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.

toml
[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 rename operation
  • Crash recovery: Automatic recovery from .recv temporary files

libp2p TCP

Used for multi-machine setups and communication across networks. This is the default on Windows environments.

toml
[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.

toml
[discovery]
mdns = true

Event 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.

plaintext
┌──────────────────┐
│  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:

TablePurpose
local_identityThe node's own actor ID, public key, and display name
peer_addressesKnown peers' multiaddr addresses
peer_keysPeers' public keys and capabilities
visionsSubmitted visions
projectsProject state (planning / active / stopping / stopped)
tasksTask state (queued → running → completed/failed/stopped)
task_eventsImmutable log of all messages
task_resultsSummary of task execution results
inbox_messagesReceive queue (with deduplication)
outbox_messagesSend 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.

plaintext
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 status

Ed25519 Signature Verification

All protocol messages are signed with Ed25519.

2

A SignableEnvelope struct is created from the UnsignedEnvelope. All fields, including the body, are included in the signing payload.

4

The SignableEnvelope is serialized into canonical JSON (keys sorted, no whitespace) to produce a deterministic byte sequence.

6

The canonical JSON byte sequence is signed with the Ed25519 private key, generating a MessageSignature (algorithm "ed25519" + key_id + Base64 signature).

8

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.

rust
// 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.

plaintext
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 logic

Dependency direction:

plaintext
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)