Others persist computation. CaaS persists responsibility.
Promises kept — even when the promiser is gone.
User: "Remind me to review the PR in 10 minutes."
Agent: "Got it! I'll remind you."
↓ 5 minutes later
╔═══════════════════════════════════╗
║ SESSION TIMEOUT - AGENT DIED ║
╚═══════════════════════════════════╝
↓ 10 minutes later
...nothing happens.
Promise broken.
AI agents are stateless, ephemeral, and can't schedule. You're binding a future obligation to something that cannot exist in the future.
Agent authors ───▶ CaaS owns ───▶ Webhook fires
intent obligation (guaranteed)
[ephemeral] [durable] [delivered]
[can die] [survives] [retried]
curl -X POST /v1/commitments -d '{
"trigger_type": "time_after",
"trigger_config": {"timestamp": "2024-01-15T10:30:00Z"},
"obligation_config": {"target": "https://app.com/webhook", "payload": {...}}
}'The agent dies. The promise lives. The webhook fires.
Yes, Temporal can technically do this. The difference is the abstraction.
| Temporal | CaaS | |
|---|---|---|
| Unit of work | Workflow execution | Commitment |
| To remind in 10 min | Define workflow → Deploy workers → Start execution | POST JSON |
| What persists | Execution history | Obligation record |
| Mental model | "Run this code durably" | "Keep this promise" |
# Temporal: define workflow, deploy workers, start execution
@workflow.defn
class ReminderWorkflow:
@workflow.run
async def run(self, target, message, delay):
await asyncio.sleep(delay)
await workflow.execute_activity(send_webhook, target, message)
client = await Client.connect("temporal:7233")
await client.start_workflow(ReminderWorkflow.run, ...)# CaaS: POST and leave
curl -X POST /v1/commitments -d '{"trigger_type": "time_after", ...}'When Temporal wins: Complex control flow, human-in-the-loop, queryable state, existing infrastructure.
When CaaS wins: Simple obligations, no workflow definitions, agents that just want to POST and leave.
Novel obligations without pre-deployed code:
# Agent composes this on the fly — no one pre-wrote a workflow for it
curl -X POST /v1/commitments -d '{
"trigger_type": "compound",
"trigger_config": {
"all": [
{"event_type": "price.update", "filters": {"symbol": "ETH", "price_gte": 5000}},
{"event_type": "time.weekday"},
{"event_type": "user.presence", "filters": {"status": "online"}}
]
},
"obligation_config": {"target": "https://app.com/notify", "payload": {"alert": "buy"}}
}'| Temporal | CaaS | |
|---|---|---|
| New trigger combination | Write code, deploy workers | POST JSON |
| Agent capability | Invoke pre-defined workflows | Author novel obligations |
Temporal requires someone to have written the workflow. CaaS lets the agent be the workflow author.
git clone https://github.com/lulzx/caas.git && cd caas
docker-compose up -d
docker-compose exec app mix ecto.migrate
curl http://localhost:4000/health# Time-based
curl -X POST /v1/commitments -H "Authorization: Bearer $KEY" -d '{
"trigger_type": "time_after",
"trigger_config": {"timestamp": "2024-01-15T10:30:00Z"},
"obligation_config": {"target": "https://app.com/hook", "payload": {"msg": "hi"}}
}'
# Event-based
curl -X POST /v1/commitments -d '{
"trigger_type": "webhook",
"trigger_config": {"event_type": "payment.completed", "filters": {"amount_gte": 100}},
"obligation_config": {"target": "https://app.com/hook", "payload": {"action": "receipt"}}
}'
# Tool completion
curl -X POST /v1/commitments -d '{
"trigger_type": "tool_complete",
"trigger_config": {"tool_id": "code_reviewer", "outcome": "success"},
"obligation_config": {"target": "https://app.com/hook", "payload": {"next": "merge"}}
}'curl -X POST /v1/events -d '{
"event_type": "tool_complete",
"event_key": "code_reviewer",
"payload": {"tool_id": "code_reviewer", "outcome": "success"}
}'| Method | Path | Description |
|---|---|---|
POST |
/v1/commitments |
Create commitment |
GET |
/v1/commitments |
List commitments |
GET |
/v1/commitments/:id |
Get commitment |
DELETE |
/v1/commitments/:id |
Cancel commitment |
POST |
/v1/commitments/batch |
Batch create |
POST |
/v1/commitments/batch/cancel |
Batch cancel |
POST |
/v1/events |
Send event |
GET |
/health |
Health check |
GET |
/metrics |
Prometheus metrics |
Saga — Chain commitments for multi-step transactions:
[Payment] ──▶ [Inventory] ──▶ [Shipping] ──▶ [Confirm]
Escalation — Time-based SLA monitoring:
0min ──▶ 5min ──▶ 15min ──▶ 30min
Assign Escalate PAGE
Dead Man's Switch — Alert if no heartbeat:
[Heartbeat] resets timer. No heartbeat? ──▶ ALERT
Fan-out/Fan-in — Coordinate parallel agents:
Orchestrator spawns [A1] [A2] [A3] ──▶ All complete ──▶ Next step
Each commitment is an isolated Erlang process (OTP GenServer):
┌─────────────────────────────────────────────────────┐
│ CommitmentSupervisor │
├─────────────────────────────────────────────────────┤
│ [Server] [Server] [Server] [Server] ... │
│ C-001 C-002 C-003 C-004 │
└─────────────────────────────────────────────────────┘
- Isolation: One failure doesn't affect others
- Fault tolerance: Crashed processes restart automatically
- Concurrency: Thousands of commitments in parallel
┌──────────┐
│ pending │
└────┬─────┘
┌─────────┼─────────┐
▼ ▼ ▼
cancelled triggered expired
│
┌─────┴─────┐
▼ ▼
fulfilled failed
%{"max_attempts" => 5, "backoff" => "exponential", "initial_delay_seconds" => 5}
# Attempts: immediate → 5s → 25s → 125s → 625s| Variable | Default |
|---|---|
DATABASE_URL |
- |
SECRET_KEY_BASE |
- |
PORT |
4000 |
POOL_SIZE |
10 |
# Fly.io
fly launch && fly secrets set SECRET_KEY_BASE=$(mix phx.gen.secret) && fly deploy
# Docker
docker build -t caas . && docker run -p 4000:4000 -e DATABASE_URL=... -e SECRET_KEY_BASE=... caas./demo_concept.sh # The "aha moment"
./demo_pipeline.sh # AI code review pipeline
./demo_showcase.sh # 5 patterns: saga, escalation, heartbeat, fan-out, batch| Guarantee | |
|---|---|
| Temporal | "If this workflow is running, it will finish." |
| CaaS | "Someone promised this — therefore it will happen." |
Others persist computation. CaaS persists responsibility.
Agents come and go. Commitments persist.
MIT License