Move your workflows from Temporal to Strait.
Migrating from Temporal
This guide helps you migrate from Temporal to Strait. While both handle workflow orchestration, Strait takes a simpler, HTTP-first approach.
Concept Mapping
| Temporal | Strait | Notes |
|---|---|---|
| Workflow | Workflow | DAG of steps with dependencies |
| Activity | Job | HTTP endpoint that does the work |
| Workflow Execution | Workflow Run | A single execution of a workflow |
| Activity Execution | Run | A single execution of a job step |
| Task Queue | Job queue | PostgreSQL-backed, no external broker |
| Worker | Worker mode | Built into the Strait binary |
| Signal | Event Trigger | Pause and wait for external events |
| Timer | sleep step / delayed_secs | Built into workflow steps |
| Child Workflow | Sub-workflow | Nested workflow execution |
| Retry Policy | retry_strategy | exponential, linear, fixed |
| Search Attributes | Job metadata | Queryable via API |
Key Differences
-
No SDK lock-in -- Temporal requires language-specific SDKs for workflows and activities. Strait uses HTTP endpoints -- your job logic can be in any language or framework.
-
Simpler deployment -- Temporal requires a cluster (server + database + Elasticsearch). Strait is a single binary with PostgreSQL.
-
Declarative workflows -- Temporal workflows are imperative code. Strait workflows are declarative JSON DAGs with step conditions and template variables.
-
No replay/determinism constraints -- Temporal replays workflow history, requiring deterministic code. Strait steps are independent HTTP calls with no replay semantics.
Migration Steps
1. Convert Activities to Jobs
Temporal Activity:
func SendEmail(ctx context.Context, to string, subject string) error {
return emailClient.Send(to, subject)
}Strait Job: Create the job definition, then implement the HTTP endpoint:
strait jobs create --name "send-email" \
--endpoint "https://your-app.com/api/jobs/send-email"2. Convert Workflows to DAGs
Temporal Workflow:
func OrderWorkflow(ctx workflow.Context, order Order) error {
err := workflow.ExecuteActivity(ctx, ValidateOrder, order).Get(ctx, nil)
err = workflow.ExecuteActivity(ctx, ChargePayment, order).Get(ctx, nil)
err = workflow.ExecuteActivity(ctx, FulfillOrder, order).Get(ctx, nil)
return err
}Strait Workflow:
{
"name": "order-workflow",
"steps": [
{ "name": "validate", "job": "validate-order" },
{ "name": "charge", "job": "charge-payment", "depends_on": ["validate"] },
{ "name": "fulfill", "job": "fulfill-order", "depends_on": ["charge"] }
]
}3. Convert Signals to Event Triggers
Temporal:
ch := workflow.GetSignalChannel(ctx, "approval")
ch.Receive(ctx, &approved)Strait: Use an approval step or event trigger:
{
"name": "review",
"type": "approval",
"depends_on": ["analyze"],
"timeout_secs": 86400
}4. Replace Timers with Sleep Steps
Temporal:
workflow.Sleep(ctx, 24*time.Hour)Strait:
{
"name": "wait",
"type": "sleep",
"config": { "duration_secs": 86400 },
"depends_on": ["notify"]
}What You Gain
- Simpler operations -- Single binary, no cluster management
- Language-agnostic -- HTTP endpoints instead of SDK-specific workers
- Built-in cost tracking -- Budget controls for AI workloads
- Real-time CDC -- Stream changes to your app via PostgreSQL WAL