Getting Started#
Before proceeding, install the following prerequisites:
Note:
rustupwill install the Rust toolchain andcargo. You will also use it to add WebAssembly targets.
Clone the repository#
Clone the repository:
git clone https://github.com/absmach/propeller.git
cd propeller
Build and Install the Artifacts#
To install the Magistrala CLI, follow the instructions.
This step compiles all Propeller components (manager, proplet, CLI, proxy, and example WASM modules). Run the following:
make all -j $(nproc)
make install
If make install fails#
You likely don’t have your Go binary path ($GOBIN) configured.
Set it up like this:
export GOBIN=$HOME/go/bin
export PATH=$PATH:$GOBIN
Run make install again afterward.
What the build process does#
During the build, you will see output similar to:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ... -o build/manager cmd/manager/main.go
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ... -o build/proplet cmd/proplet/main.go
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ... -o build/cli cmd/cli/main.go
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ... -o build/proxy cmd/proxy/main.go
GOOS=js GOARCH=wasm tinygo build -buildmode=c-shared -o build/addition.wasm -target wasip1 examples/addition/addition.go
GOOS=js GOARCH=wasm tinygo build -buildmode=c-shared -o build/compute.wasm -target wasip1 examples/compute/compute.go
GOOS=js GOARCH=wasm tinygo build -buildmode=c-shared -o build/hello-world.wasm -target wasip1 examples/hello-world/hello-world.go
This means:
- All Go binaries were built and placed into
build/ - All example WASM modules were built using TinyGo into
build/
Installing the artifacts#
make install copies the compiled binaries into your $GOBIN directory so you can run them directly from your terminal:
cp build/cli $GOBIN/propeller-cli
cp build/manager $GOBIN/propeller-manager
cp build/proplet $GOBIN/propeller-proplet
cp build/proxy $GOBIN/propeller-proxy
Once installed, you can run the commands simply as:
propeller-manager
propeller-proplet
propeller-cli
propeller-proxy
Working with the Rust WASI HTTP Example#
Propeller includes an example Rust WebAssembly HTTP component: sample-wasi-http-rust. Running the Rust WASI example validates your Rust/WASM setup and shows how a single WASI HTTP component works before integrating it with Propeller. This section shows you how to fetch it, build it, run it, and test it.
You can also refer to the original project’s instructions here: https://github.com/bytecodealliance/sample-wasi-http-rust#sample-wasihttp-in-rust
1. Initialize the Git submodules#
From the root of the propeller repository:
git submodule update --init
cd examples/sample-wasi-http-rust
This pulls in the sample-wasi-http-rust example code.
2. Install the required Rust target#
The example builds to a WebAssembly target. If you see errors about missing wasm32-wasip2 or wasm32-wasip1, you need to add the target.
A safe default is to add the Preview 2 target:
rustup target add wasm32-wasip2
If this command fails, update Rust first:
rustup update rustup target add wasm32-wasip2
3. Build the Rust example#
Build the example for the WebAssembly target:
cargo build --target wasm32-wasip2
If the build completes successfully, you will see a message similar to:
Finished `dev` profile [unoptimized + debuginfo] target(s) in X.XXs
At this point, you have a compiled WASI HTTP component.
4. Install cargo-component (once)#
cargo-component makes it easy to work with Wasm components and run them with Wasmtime.
Install it using cargo:
cargo install cargo-component
You only need to do this once on your machine.
5. Serve the Rust component locally#
From inside examples/sample-wasi-http-rust:
cargo component serve
On first run, this may automatically install the wasm32-wasip1 component standard library and then print something like:
Creating component target/wasm32-wasip1/debug/sample_wasi_http_rust.wasm
Running `target/wasm32-wasip1/debug/sample_wasi_http_rust.wasm`
Serving HTTP on http://0.0.0.0:8080/
This means your WASI HTTP component is now running as an HTTP server on port 8080.
Leave this terminal open while the server is running.
6. Test the Rust HTTP endpoints#
Open a new terminal and run the following commands to test the different routes:
Hello world route#
curl -v http://127.0.0.1:8080/
You should see a 200 OK response with a body similar to:
Hello, wasi:http/proxy world!
Wait route#
curl -v http://127.0.0.1:8080/wait
This route sleeps briefly before responding, and you should see:
slept for 1001 millis
Echo body route#
curl -v http://127.0.0.1:8080/echo -d 'hello from John Doe'
The response will echo the request body back:
hello from John Doe
Echo headers route#
curl -v http://127.0.0.1:8080/echo-headers -H 'X-Test: 123' -H 'X-Foo: bar'
You will see the headers reflected in the response:
x-foo: bar
user-agent: curl/...
accept: */*
x-test: 123
Echo trailers route#
curl -v http://127.0.0.1:8080/echo-trailers
This demonstrates handling of HTTP trailers over WASI HTTP.
When you are done: Go back to the terminal running
cargo component serveand pressCtrl+Cto stop the server.
Run SuperMQ and Propeller#
Propeller needs to talk to a running SuperMQ instance. To get everything working, you’ll always do these three high-level steps:
- Start SuperMQ – so the CLI has something to talk to.
- Provision SuperMQ with
propeller-cli provision– this creates the domain, clients, channels, and writes aconfig.tomlfile with all the IDs and keys. - Start (or restart) the Propeller services – so
propeller-manager,propeller-proplet, andpropeller-proxycan readconfig.tomland connect correctly.
You can run the services in two ways:
- Option 1 (recommended): everything via Docker
- Option 2: run the Propeller binaries directly on your machine
Pick one option and follow it from start to finish.
Option 1: Run everything with Docker (recommended)#
In this mode, Docker runs:
- SuperMQ core services (auth, users, clients, domains, channels, adapters, etc.)
- Propeller services: manager, proplet, and proxy
When you run
make start-supermq, you are starting both SuperMQ and the Propeller services defined indocker/compose.yaml.
1. Start the SuperMQ Docker stack (first time)#
From the root of the propeller repo:
cd propeller
make start-supermq
This runs:
docker compose -f docker/compose.yaml --env-file docker/.env up -d
At this point:
- SuperMQ is up and reachable.
- Propeller containers (manager, proplet, proxy) will fail to start correctly on the first run.
That’s OK - they don’t have credentials yet because
config.tomldoesn’t exist.
We only need SuperMQ up now so we can provision it.
2. Provision SuperMQ with propeller-cli#
Now we create everything Propeller needs inside SuperMQ and generate the config file.
Run:
propeller-cli provision
This command will:
- Log you into SuperMQ (you must have a SuperMQ user already created; if not, create one using the supermq-cli,
curl, or the web UI). - Create a domain
- Log your user into that domain
- Create a manager client
- Create a proplet client
- Create a manager channel
- Connect the manager client to the manager channel
- Connect the proplet client to the manager channel
- Write all of these IDs and keys into a
config.tomlfile in the current directory
The process will look something like this:
If it succeeds, you’ll see:
Successfully created config.toml file
Your config.toml will look like:
# SuperMQ Configuration
[manager]
domain_id = "182c0907-002c-4bfd-8bf3-e4f40c58dde6"
client_id = "f2fe9a33-144a-4346-a5d6-38e2eb07815e"
client_key = "ef7da52b-c01f-4b62-9502-6723d639405b"
channel_id = "8c6e1e6c-fc89-43b4-b00b-884a690c7419"
[proplet]
domain_id = "182c0907-002c-4bfd-8bf3-e4f40c58dde6"
client_id = "fa407362-9c5f-41b8-9a09-9d0c0b039287"
client_key = "991c4d03-2f2c-4ba5-97a6-45bead85457e"
channel_id = "8c6e1e6c-fc89-43b4-b00b-884a690c7419"
[proxy]
domain_id = "182c0907-002c-4bfd-8bf3-e4f40c58dde6"
client_id = "fa407362-9c5f-41b8-9a09-9d0c0b039287"
client_key = "991c4d03-2f2c-4ba5-97a6-45bead85457e"
channel_id = "8c6e1e6c-fc89-43b4-b00b-884a690c7419"
3. Mount config.toml into the Docker services#
For the Docker containers to see config.toml, we need to mount it.
If the compose file uses a path under docker/, copy the file:
cp config.toml docker/config.toml
Then, in docker/compose.yaml, make sure the Propeller services (manager, proplet, proxy) have a volume like this:
volumes:
- ./config.toml:/config.toml
# or, if you copied it into docker/:
# - ./config.toml:/config.toml
Uncomment or add these lines as needed.
4. Restart the Docker stack so Propeller reads config.toml#
Now that:
- SuperMQ is provisioned, and
config.tomlexists and is mounted into the containers,
we restart the stack:
make stop-supermq
make start-supermq
On this second start:
propeller-manager,propeller-proplet, andpropeller-proxystart up,- They see
/config.tomlinside the container, - They read the
[manager],[proplet], and[proxy]sections, - They connect to SuperMQ using the correct domain, client IDs, client keys, and channel IDs.
At this point, your system is up and ready to use.
Option 2: Run Propeller binaries directly (without Docker)#
In this mode:
- SuperMQ may still run in Docker (via
make start-supermq), but - You run the Propeller processes (
propeller-manager,propeller-proplet,propeller-proxy) directly on your host.
The provisioning step is the same as in Option 1:
- Start SuperMQ (Docker or however you like)
- Run
propeller-cli provisionto generateconfig.toml
Make sure config.toml is in the directory where you will start the binaries, or set env vars to point them at the correct file.
1. Start the manager#
propeller-manager
If everything is configured correctly, you’ll see logs similar to:
{"time":"2025-06-12T14:13:56.74162598+03:00","level":"INFO","msg":"MQTT connection lost"}
{"time":"2025-06-12T14:13:56.793894993+03:00","level":"INFO","msg":"Subscribe to MQTT topic completed successfully","duration":"52.272009ms"}
{"time":"2025-06-12T14:13:56.794210043+03:00","level":"INFO","msg":"manager service http server listening at localhost:7070 without TLS"}
The manager exposes an HTTP API on localhost:7070.
2. Start the proplet#
In another terminal:
propeller-proplet
Example logs:
{"time":"2025-06-12T14:14:44.362072799+03:00","level":"INFO","msg":"MQTT connection lost"}
{"time":"2025-06-12T14:14:44.398147897+03:00","level":"INFO","msg":"Proplet service is running."}
A proplet will automatically register itself with the manager.
3. Start the proxy#
The proxy needs to know which OCI registry to pull WebAssembly images from. Set a few environment variables, then start it:
export PROXY_REGISTRY_URL="docker.io"
export PROXY_AUTHENTICATE="TRUE"
export PROXY_REGISTRY_USERNAME="" # set if your registry requires auth
export PROXY_REGISTRY_PASSWORD="" # set if your registry requires auth
propeller-proxy
Example logs:
{"time":"2025-06-12T14:15:18.438848211+03:00","level":"INFO","msg":"MQTT connection lost"}
{"time":"2025-06-12T14:15:18.438823293+03:00","level":"INFO","msg":"successfully initialized MQTT and HTTP config"}
{"time":"2025-06-12T14:15:18.438886395+03:00","level":"INFO","msg":"starting proxy service"}
{"time":"2025-06-12T14:15:18.452592155+03:00","level":"INFO","msg":"successfully subscribed to topic"}
Postman Collection#
This is a collection of the API calls that can be used to interact with the Propeller system.
API#
List Proplets#
curl -X GET "http://localhost:7070/proplets"
This will output a response like the following:
{
"offset": 0,
"limit": 100,
"total": 1,
"proplets": [
{
"id": "fa407362-9c5f-41b8-9a09-9d0c0b039287",
"name": "Wojahn-Omohundro",
"task_count": 1,
"alive": true,
"alive_history": [
"2025-06-12T14:22:04.379038459+03:00",
"2025-06-12T14:22:14.378443596+03:00",
"2025-06-12T14:22:24.379305586+03:00",
"2025-06-12T14:22:34.378765631+03:00",
"2025-06-12T14:22:44.381274342+03:00",
"2025-06-12T14:22:54.378152057+03:00",
"2025-06-12T14:23:04.380171407+03:00",
"2025-06-12T14:23:14.379503767+03:00",
"2025-06-12T14:23:24.379971214+03:00",
"2025-06-12T14:23:34.378886406+03:00"
]
}
]
}
Create task#
curl -X POST "http://localhost:7070/tasks" \
-H "Content-Type: application/json" \
-d '{"name": "add", "inputs": [10, 20]}'
This will output a response like the following:
{
"id": "e9858e56-a1dd-4e5a-9288-130f7be783ed",
"name": "add",
"state": 0,
"cli_args": null,
"inputs": [10, 20],
"start_time": "0001-01-01T00:00:00Z",
"finish_time": "0001-01-01T00:00:00Z",
"created_at": "2025-06-12T14:25:22.407167091+03:00",
"updated_at": "0001-01-01T00:00:00Z"
}
You can use the CLI to create a task as follows:
# propeller-cli tasks create <name>
propeller-cli tasks create demo
This will output a response like the following:
{
"created_at": "2025-09-16T10:25:31.491528704Z",
"finish_time": "0001-01-01T00:00:00Z",
"id": "2ccb6b7c-3ce8-4c27-be19-01172954d593",
"name": "demo",
"start_time": "0001-01-01T00:00:00Z",
"updated_at": "0001-01-01T00:00:00Z"
}
Get a task#
curl -X GET "http://localhost:7070/tasks/e9858e56-a1dd-4e5a-9288-130f7be783ed"
This will output a response like the following:
{
"id": "e9858e56-a1dd-4e5a-9288-130f7be783ed",
"name": "add",
"state": 0,
"cli_args": null,
"inputs": [10, 20],
"start_time": "0001-01-01T00:00:00Z",
"finish_time": "0001-01-01T00:00:00Z",
"created_at": "2025-06-12T14:25:22.407167091+03:00",
"updated_at": "0001-01-01T00:00:00Z"
}
You can use the CLI to get a task as follows:
# propeller-cli tasks view <id>
propeller-cli tasks view 2ccb6b7c-3ce8-4c27-be19-01172954d593
This will output a response like the following:
{
"created_at": "2025-09-16T10:25:31.491528704Z",
"finish_time": "0001-01-01T00:00:00Z",
"id": "2ccb6b7c-3ce8-4c27-be19-01172954d593",
"name": "demo",
"start_time": "0001-01-01T00:00:00Z",
"updated_at": "0001-01-01T00:00:00Z"
}
Upload Wasm File#
curl -X PUT "http://localhost:7070/tasks/e9858e56-a1dd-4e5a-9288-130f7be783ed/upload" \
-F 'file=@<propeller_path>/build/addition.wasm'
Update task with base64 encoded Wasm file#
curl --location --request PUT 'http://localhost:7070/tasks/e9858e56-a1dd-4e5a-9288-130f7be783ed' \
--header 'Content-Type: application/json' \
--data '{
"file": "AGFzbQEAAAABBwFgAn9/AX8DAgEABwgBBG1haW4AAAoJAQcAIAAgAWoL"
}'
propeller-cli tasks update e9858e56-a1dd-4e5a-9288-130f7be783ed '{"file": "AGFzbQEAAAABBwFgAn9/AX8DAgEABwgBBG1haW4AAAoJAQcAIAAgAWoL"}'
Start a task#
curl -X POST "http://localhost:7070/tasks/e9858e56-a1dd-4e5a-9288-130f7be783ed/start"
You can use the CLI to start a task as follows:
# propeller-cli tasks start <id>
propeller-cli tasks start 2ccb6b7c-3ce8-4c27-be19-01172954d593
This will output a response like the following:
ok
Stop a task#
curl -X POST "http://localhost:7070/tasks/e9858e56-a1dd-4e5a-9288-130f7be783ed/stop"
Creating Tasks from OCI Registry Images#
For WebAssembly modules stored in an OCI registry, you can specify the image URL during task creation. The proxy will automatically retrieve the WASM file from the registry when the task starts, eliminating the need for manual file uploads.
curl -X POST "http://localhost:7070/tasks" \
-H "Content-Type: application/json" \
-d '{"name": "add", "inputs": [10, 20], "image_url": "docker.io/mrstevenyaga/add.wasm"}'
The proxy will handle pulling the image from the specified OCI registry during task execution, streamlining the deployment process.