propeller logo

Proplet

Propeller's Proplet is the Rust-based edge runtime. It receives Wasm tasks from the Manager via MQTT, executes them inside a sandboxed Wasmtime runtime, and publishes results.

The Proplet is Propeller's edge execution runtime. It receives task commands from the Manager over MQTT, fetches WebAssembly binaries, executes them using an embedded or external Wasmtime runtime, and publishes results back over MQTT.

Session behavior: The Proplet uses clean MQTT sessions (clean_session = true). Subscriptions and missed messages are not restored on reconnect. The Proplet re-subscribes to its topics after each connection.

For how the Manager coordinates proplets and schedules tasks, see the Manager documentation. For DAG-based workflows and job execution, see Task Scheduling.

Why the Proplet

Edge-First Execution

WebAssembly workloads need to run where the data lives—on edge devices, gateways, and embedded systems. The Proplet provides:

  • Lightweight runtime: A Rust-based binary that runs on resource-constrained devices
  • Secure sandboxing: WebAssembly's memory-safe sandbox isolates workloads from the host system
  • Protocol efficiency: MQTT communication minimizes bandwidth and handles intermittent connectivity
  • Automatic discovery: Proplets announce themselves to the Manager on startup

Decoupled from Orchestration

The Proplet focuses solely on execution—it doesn't make scheduling decisions or manage workflows. This separation allows:

  • Simple proplet logic: Receive task, execute, report results
  • Centralized intelligence: The Manager handles scheduling, load balancing, and workflow coordination
  • Resilient operation: Proplets continue executing tasks even during temporary Manager disconnects

The following diagram shows how Proplets communicate with the Manager and Proxy over MQTT, illustrating all message topics and their directions:

Proplet MQTT Communication Topology

For the Manager's role in task orchestration, see Manager: Task Operations.

Running the Proplet

Using Docker Compose

The recommended way to run proplets is with Docker Compose:

cd propeller
docker compose -f docker/compose.propeller.yaml --env-file docker/.env up -d

Verify proplets are running:

curl "http://localhost:7070/proplets?limit=10"

Your output should look like this:

{
  "offset": 0,
  "limit": 10,
  "total": 1,
  "proplets": [
    {
      "id": "a95517f9-5655-4cf5-a7c8-aa00290b3895",
      "name": "crimson-falcon",
      "task_count": 0,
      "alive": true,
      "last_alive_at": "2026-03-01T12:00:10Z",
      "metadata": {
        "os": "linux",
        "hostname": "edge-node-1",
        "cpu_arch": "amd64",
        "wasm_runtime": "wasmtime-internal"
      }
    }
  ]
}

Running Standalone

Set the required environment variables and start the proplet binary:

export PROPLET_TENANT_ID="your_tenant_id"
export PROPLET_CHANNEL_ID="your_channel_id"
export PROPLET_ENTITY_ID="your_entity_id"
export PROPLET_API_KEY="your_api_key"
propeller-proplet

Startup logs confirm the runtime and subscriptions:

INFO Starting Proplet (Rust) - Instance ID: 67b2bcb2-9c56-4c57-b163-085d6ec2c313
INFO MQTT client created (TLS: false)
INFO Using Wasmtime runtime
INFO Starting PropletService
INFO Published discovery message
INFO Subscribed to topic: m/.../control/manager/start
INFO Subscribed to topic: m/.../control/manager/stop
INFO Subscribed to topic: m/.../registry/server

Architecture

Runtime Selection

The proplet selects its WebAssembly runtime at startup based on configuration and hardware detection:

RuntimeTriggerUse Case
Embedded WasmtimeDefault (no external runtime configured)Standard workloads, lowest latency
Host RuntimePROPLET_EXTERNAL_WASM_RUNTIME setCustom runtimes, debugging, alternative engines
TEE RuntimeTEE detected + PROPLET_KBS_URI set + encrypted: true taskConfidential computing with encrypted workloads

Component Model Detection

When executing a WebAssembly binary, the Wasmtime runtime automatically detects the module type:

Module TypeDetectionEmbedded WasmtimeHost Runtime
Core moduleMagic bytes 0x00 0x61 0x73 0x6d + version != 0x0dstart_app_core — direct invocationwasmtime run
ComponentMagic bytes indicate component modelstart_app_component — WASI Preview 2wasmtime run
HTTP ProxyComponent with wasi:http/incoming-handler exportstart_app_proxy — built-in HTTPwasmtime serve -Shttp — external process

TEE Detection

At startup, the proplet probes for Trusted Execution Environment support in priority order—TDX first, then SEV/SNP, then SGX:

TEEDetection Method
Intel TDX/dev/tdx_guest, /sys/firmware/tdx_guest, cpuinfo TDX flags
AMD SEV/SNP/dev/sev, EFI variables (SevStatus), cpuinfo SEV flags
Intel SGX/dev/sgx_enclave, /dev/sgx/enclave, /dev/isgx (legacy)

If a TEE is detected and PROPLET_KBS_URI is not set, the proplet exits with an error—encrypted workload support requires the Key Broker Service.

For the complete TEE setup and encrypted workload flow, see the TEE guide.

Task Execution

Proplet's Role

When the Manager sends a task start command, the proplet handles the execution lifecycle:

1. Command validation — The proplet validates the StartRequest payload, ensuring required fields are present (id, name, and encrypted workload fields when applicable).

2. Targeting filter — Skipped entirely when broadcast is true. Otherwise, if proplet_id is set, the proplet checks whether it matches its own ID and silently drops the command if not. If neither broadcast nor proplet_id is set, all proplets execute the task.

3. Duplicate detection — If the task is already running, the proplet ignores the duplicate command.

4. Binary acquisition — The proplet obtains the WebAssembly binary via one of three paths:

  • Inline base64: If file is set, decode directly from the payload
  • HTTP/HTTPS fetch: If image_url starts with http:// or https://, fetch the binary directly over HTTP (up to 100 MB; response Content-Type must be application/wasm or application/octet-stream)
  • Registry fetch: Otherwise, if image_url is set, request chunks from the Proxy via MQTT

5. Runtime execution — The appropriate runtime executes the module with the provided inputs and environment variables.

6. Result publishing — Upon completion (or failure), the proplet publishes results back to the Manager.

The following diagram illustrates this complete task execution flow, showing the decision points and paths through the system:

Proplet Task Execution Flow

For how the Manager handles task scheduling and result processing, see Manager: Task Operations.

Binary Fetching

When a task specifies image_url instead of an inline file, the proplet requests the binary from the Proxy service:

  1. Proplet publishes a fetch request to registry/proplet with the image URL
  2. Proxy pulls the image from the container registry (GHCR, Docker Hub, etc.)
  3. Proxy streams binary chunks to registry/server
  4. Proplet assembles chunks into the complete binary

Chunk assembly includes a 5-minute TTL—incomplete assemblies are automatically expired by a background task.

The diagram below shows the complete binary fetching flow, from the Proplet's request through the Proxy to the container registry and back:

Binary Fetching via Proxy

Task Stopping

When the Manager sends a stop command:

  1. The proplet looks up the task in running_tasks
  2. For proxy tasks, it signals cancellation via a watch channel
  3. For all tasks, it aborts the Tokio task handle
  4. The task is removed from the running tasks map

Configuration

The proplet is configured through environment variables. Values can also be loaded from a config.toml file generated by propeller-cli provision.

Core Settings

VariableDescriptionDefault
PROPLET_LOG_LEVELLog level (debug, info, warn, error)info
PROPLET_LIVELINESS_INTERVALHeartbeat publish interval in seconds10
PROPLET_METRICS_INTERVALProplet-level metrics publish interval in seconds10
PROPLET_ENABLE_MONITORINGEnable per-task process monitoringtrue

MQTT Connection

VariableDescriptionDefault
PROPLET_MQTT_ADDRESSMQTT broker URLtcp://localhost:1883
PROPLET_MQTT_TIMEOUTMQTT operation timeout in seconds30
PROPLET_MQTT_QOSMQTT Quality of Service level2
PROPLET_MQTT_KEEP_ALIVEMQTT keepalive interval in seconds30
PROPLET_MQTT_MAX_PACKET_SIZEMaximum MQTT packet size in bytes10485760
PROPLET_MQTT_INFLIGHTMaximum in-flight MQTT messages10

Atom Authentication

VariableDescriptionDefault
PROPLET_TENANT_IDAtom tenant identifier(required)
PROPLET_CHANNEL_IDAtom channel (resource) identifier(required)
PROPLET_ENTITY_IDAtom entity ID for MQTT authentication(required)
PROPLET_API_KEYAtom API key for MQTT authentication(required)

These values are generated by propeller-cli provision. For details on provisioning, see Getting Started.

Runtime Configuration

VariableDescriptionDefault
PROPLET_EXTERNAL_WASM_RUNTIMEPath to an external Wasm runtime executable. If empty, uses embedded Wasmtime.""
PROPLET_DIRSColon-separated list of host directories to preopen for WASI access (e.g. /data:/tmp).""
PROPLET_HTTP_ENABLEDEnable the built-in HTTP proxy server for WASI HTTP componentsfalse
PROPLET_HTTP_PROXY_PORTPort for the built-in HTTP proxy server8222
PROPLET_HAL_ENABLEDEnable the Hardware Abstraction Layertrue

HTTP TLS

When PROPLET_HTTP_ENABLED=true, WASM components can make outgoing HTTPS requests using wasi:http/outgoing-handler. These settings control TLS verification for those outbound requests:

VariableDescriptionDefault
PROPLET_HTTP_TLS_CA_CERTPath to a CA certificate PEM file to trust for HTTPS""
PROPLET_HTTP_TLS_INSECURE_SKIP_VERIFYSkip TLS certificate verification (insecure, testing only)false

By default, the proplet trusts the system's root CA store (webpki-roots) for all outbound HTTPS requests. When PROPLET_HTTP_TLS_CA_CERT is set, the specified CA certificate is added to the trust store alongside the system roots, allowing HTTPS connections to internal servers with custom or self-signed certificates.

When PROPLET_HTTP_TLS_INSECURE_SKIP_VERIFY=true, all TLS certificate verification is disabled — use for testing only.

The same TLS configuration also applies when the proplet itself fetches WASM binaries over HTTPS (via image_url starting with https://).

Inbound HTTPS

WASM proxy components (those exporting wasi:http/incoming-handler) receive HTTP requests through the proplet's built-in reverse proxy. The proplet does not terminate TLS directly. To serve proxy components over HTTPS, deploy a standard reverse proxy in front of the proplet:

Configure the reverse proxy to accept HTTPS on the desired port and proxy requests as plain HTTP to the port where the WASM proxy component is listening. This is the same approach used throughout the Propeller ecosystem (MQTT over TLS, gRPC over TLS, etc.).

Docker Compose

In docker/compose.propeller.yaml, uncomment the environment variables and volume mount under the proplet service:

services:
  proplet:
    environment:
      # Outgoing HTTP TLS — uncomment to trust custom/self-signed CA certificates
      # for HTTPS requests made by WASM workloads and proplet itself.
      # PROPLET_HTTP_TLS_CA_CERT: ${PROPLET_HTTP_TLS_CA_CERT}
      # PROPLET_HTTP_TLS_INSECURE_SKIP_VERIFY: ${PROPLET_HTTP_TLS_INSECURE_SKIP_VERIFY}
    volumes:
      # HTTP TLS CA cert (uncomment to trust custom CAs for outgoing HTTPS):
      # - type: bind
      #   source: ./ssl/certs/ca.crt
      #   target: /etc/propeller/tls/http-ca.crt
      #   read_only: true

In docker/.env, uncomment the variables:

PROPLET_HTTP_TLS_CA_CERT=/etc/propeller/tls/http-ca.crt
# PROPLET_HTTP_TLS_INSECURE_SKIP_VERIFY=false

The CA certificate is generated alongside the other TLS certificates by running make all (or make ca) in docker/ssl/. It lives at docker/ssl/certs/ca.crt. See the certificate generation section for details.

Running as a Binary

Set the environment variables before starting the proplet:

export PROPLET_HTTP_ENABLED=true
export PROPLET_HTTP_TLS_CA_CERT=/path/to/ca.crt
# export PROPLET_HTTP_TLS_INSECURE_SKIP_VERIFY=true  # testing only
./propeller-proplet

The PROPLET_HTTP_TLS_CA_CERT path must point to a PEM-encoded CA certificate file on the host filesystem.

TEE Configuration

Required only when running inside a Trusted Execution Environment with encrypted workloads:

VariableDescriptionDefault
PROPLET_KBS_URIURI of the Key Broker Service""
PROPLET_AA_CONFIG_PATHPath to the Attestation Agent configuration file""

If a TEE is detected and PROPLET_KBS_URI is not set, the proplet exits with an error.

Config File Fallback

VariableDescriptionDefault
PROPLET_CONFIG_FILEPath to the TOML config fileconfig.toml
PROPLET_CONFIG_SECTIONSection name within the TOML config fileproplet

Heartbeats and Metrics

Liveliness

The proplet sends periodic heartbeats to indicate availability. The Manager uses these heartbeats to track proplet liveness. A proplet is considered alive if its last heartbeat arrived within 10 seconds. Dead proplets are excluded from task scheduling.

For how the Manager uses heartbeats for scheduling decisions, see Manager: Proplet Management.

Proplet Metrics

When monitoring is enabled, the proplet collects and publishes system-level metrics (CPU and memory) to control/proplet/metrics.

Per-Task Metrics

For running tasks, the proplet can publish process-level metrics including CPU usage, memory consumption, disk I/O, thread count, and file descriptors. These are published to control/proplet/task_metrics and include aggregated statistics.

Discovery

On startup, the proplet announces itself to the Manager by publishing a discovery message to control/proplet/create with its ID and namespace.

The Manager receives this message and creates a proplet record with a generated human-readable name (e.g., "crimson-falcon"). If the proplet disconnects unexpectedly, the MQTT broker delivers the Last Will message, notifying the Manager of the disconnect.

MQTT Topics

TopicDirectionDescription
m/{tenant_id}/c/{channel_id}/control/proplet/createProplet → ManagerStartup discovery announcement
m/{tenant_id}/c/{channel_id}/control/proplet/aliveProplet → ManagerPeriodic heartbeat
m/{tenant_id}/c/{channel_id}/control/proplet/metricsProplet → ManagerProplet-level resource metrics
m/{tenant_id}/c/{channel_id}/control/proplet/task_metricsProplet → ManagerPer-task process metrics
m/{tenant_id}/c/{channel_id}/control/proplet/resultsProplet → ManagerTask execution results
m/{tenant_id}/c/{channel_id}/control/manager/startManager → PropletTask start command
m/{tenant_id}/c/{channel_id}/control/manager/stopManager → PropletTask stop command
m/{tenant_id}/c/{channel_id}/registry/propletProplet → ProxyRequest Wasm binary chunks
m/{tenant_id}/c/{channel_id}/registry/serverProxy → PropletWasm binary chunk delivery

Using the Host Runtime

The Proplet supports running Wasm modules using an external runtime binary instead of the embedded Wasmtime. When PROPLET_EXTERNAL_WASM_RUNTIME is set, the Proplet uses the which crate to locate the binary in $PATH. If the binary is not found at the specified path or in $PATH, the Proplet logs a warning and falls back to the embedded Wasmtime runtime.

# Use a specific runtime binary
export PROPLET_EXTERNAL_WASM_RUNTIME="/usr/bin/wasmtime"
propeller-proplet

# Or place the binary in $PATH and let the Proplet discover it
export PROPLET_EXTERNAL_WASM_RUNTIME="wasmtime"
propeller-proplet

The proplet detects the module type from the binary and invokes the appropriate subcommand:

Module TypeSubcommandExample
Core modulewasmtime run <file> [cli_args…]Standard execution with task inputs
Componentwasmtime run <file> [cli_args…]WASI Preview 2 component execution
HTTP Proxywasmtime serve -Shttp [cli_args…] <file>HTTP server for WASI incoming-handler

Proxy Components

When the Wasm binary exports wasi:http/incoming-handler, the host runtime spawns wasmtime serve -Shttp <file> as an external HTTP server process. This enables the external runtime to handle the same proxy component workloads that the embedded runtime supports.

The task result contains the actual port the server is listening on:

{
  "task_id": "…",
  "status": "completed",
  "result": "started at port 8222"
}

Port Allocation

When PROPLET_HTTP_PROXY_PORT is set (default 8222), the host runtime probes that port first. If the port is busy, it tries the next port (8223, 8224, etc.) up to 100 attempts. If cli_args includes --addr (e.g. --addr 0.0.0.0:9093), the specified port is used directly without fallback.

CLI Arguments

Proxy components accept cli_args for wasmtime serve flags:

{
  "name": "http-guest",
  "image_url": "ghcr.io/myorg/http-proxy:v1",
  "cli_args": ["--addr", "0.0.0.0:9093"],
  "daemon": true
}

The task must set "daemon": true since wasmtime serve runs indefinitely. When daemon is true, the proplet tracks the subprocess and reports the assigned port in the task result rather than waiting for it to exit.

Core & Component Tasks

For non-proxy modules, the proplet invokes wasmtime run <file> with any cli_args from the task:

{
  "name": "add",
  "image_url": "ghcr.io/myorg/addition:v1",
  "cli_args": ["--invoke", "add"],
  "inputs": [10, 20]
}

Federated Learning

The proplet participates in federated learning rounds by executing WASM training modules and reporting model updates. The proplet automatically fetches model weights from the Model Registry and datasets from the Local Data Store based on environment variables set by the Coordinator.

For the complete FL architecture and training lifecycle, see Federated Learning. For a hands-on example, see Federated Learning Example.

On this page