Quick Start
Build a Hello Flow end to end: install Dex locally, define two Steps with a wait, unblock it with an RPC, and inspect the result in Dex Web.
Choose an SDK once — the preference is stored in this browser (localStorage) and defaults to Python.
Use @superdurable/dex ^0.1.3+. waitFor, execute, and RPC handlers may be async and await Client calls on the same Worker — no sync sidecar. See TypeScript SDK notes.
1. Install Dex server + web
brew install superdurable/tap/dexcli
brew upgrade superdurable/tap/dexcli # when you want the latest
dexcli dev --open
dexcli starts Temporal, Dex Server, and Dex Web. Defaults:
| Service | Address |
|---|---|
| Dex Web | http://127.0.0.1:8802 |
| Dex Server | 127.0.0.1:8801 |
| Temporal Web | http://127.0.0.1:8233 |
See the CLI README for flags and persistence options.
2. Hello Flow design
You will implement:
- A Flow with StepA → StepB
- StepA uses
waitForwith anyOf(timer, channel); StepB has no wait - One Attribute, one Channel, one RPC that updates the attribute and publishes the channel to unblock StepA
- A client (and optional REST controller) to start the Flow and invoke the RPC
- Inspection in Dex Web
Start → StepA (wait anyOf timer | channel)
│ channel msg or timer
▼
StepB (execute only) → complete
3. Flow definition
from datetime import timedelta
from dex import (
Attribute, Channel, Context, Flow, PersistenceSchema, RPCResult,
Step, StepDecision, StepList, Timer, Wait, go_to, graceful_complete, rpc,
)
class StepA(Step[str]):
def __init__(self, unblock: Channel[str]):
self.unblock = unblock
def wait_for(self, context: Context, input: str) -> Wait:
return Wait.any_of(
self.unblock.for_one(),
Timer.by_duration(timedelta(seconds=60)),
)
def execute(self, context: Context, input: str) -> StepDecision:
msgs = self.unblock.results(context)
note = msgs[0] if msgs else "timer"
return go_to(StepB(), f"{input}:{note}")
class StepB(Step[str]):
def execute(self, context: Context, input: str) -> StepDecision:
return graceful_complete(input)
class HelloFlow(Flow[str]):
status = Attribute("status", str)
unblock = Channel("unblock", str)
def get_steps(self) -> StepList[str]:
return StepList.start_step(StepA(self.unblock)).other_steps(StepB())
def get_persistence_schema(self) -> PersistenceSchema:
return PersistenceSchema.attributes(self.status).channels(self.unblock)
@rpc
def nudge(self, context: Context, message: str) -> RPCResult:
self.status.set(context, "nudged")
self.unblock.publish(context, message)
return RPCResult(output="ok")
4. Start the Flow and invoke the RPC
Register the Flow on a Worker pointed at dexcli (worker_target / server address 127.0.0.1:8801), then:
# Start
client.start_flow(HelloFlow(), flow_id="hello-1", input="hi")
# Unblock StepA via RPC (also updates attribute + publishes channel)
client.invoke_rpc(flow_id="hello-1", rpc="nudge", input="from-client")
A thin REST controller is optional — useful when a browser or partner system should start / nudge the Flow. The Python basic example exposes /basic routes in the same process as the Worker.
5. View results in Dex Web
- Open http://127.0.0.1:8802
- Find Flow
hello-1 - Observe StepA waiting, then after the RPC, StepB completing and attributes/channel updates
Replace static/img/quick-start/flow-list.svg with a real Dex Web screenshot
Replace static/img/quick-start/waiting.svg with StepA waiting
Replace static/img/quick-start/completed.svg after RPC completes
Next
- Deepen primitives in Capabilities Overview
- Browse Design Patterns and Use Cases