Skip to content
/ caas Public

Others persist computation. CaaS persists responsibility. Promises kept even when the promiser is gone.

License

Notifications You must be signed in to change notification settings

Lulzx/caas

Repository files navigation

Commitment-as-a-Service (CaaS)

Others persist computation. CaaS persists responsibility.

Promises kept — even when the promiser is gone.


The Problem

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.


The Solution

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.


CaaS vs Temporal

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.


What Only CaaS Can Do

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.


Quick Start

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

API

Create Commitment

# 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"}}
}'

Send Events

curl -X POST /v1/events -d '{
  "event_type": "tool_complete",
  "event_key": "code_reviewer",
  "payload": {"tool_id": "code_reviewer", "outcome": "success"}
}'

Endpoints

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

Patterns

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

Architecture

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

Commitment Lifecycle

        ┌──────────┐
        │ pending  │
        └────┬─────┘
   ┌─────────┼─────────┐
   ▼         ▼         ▼
cancelled triggered  expired
             │
       ┌─────┴─────┐
       ▼           ▼
   fulfilled    failed

Retry

%{"max_attempts" => 5, "backoff" => "exponential", "initial_delay_seconds" => 5}
# Attempts: immediate → 5s → 25s → 125s → 625s

Configuration

Variable Default
DATABASE_URL -
SECRET_KEY_BASE -
PORT 4000
POOL_SIZE 10

Deployment

# 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

Demos

./demo_concept.sh   # The "aha moment"
./demo_pipeline.sh  # AI code review pipeline
./demo_showcase.sh  # 5 patterns: saga, escalation, heartbeat, fan-out, batch

The Bottom Line

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

About

Others persist computation. CaaS persists responsibility. Promises kept even when the promiser is gone.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published