Cloudflare Platform Overview
Why Cloudflare, what we use, and how it maps to z0 primitives.
Prerequisites: PRINCIPLES.md, PRIMITIVES.md
Why Cloudflare
Section titled “Why Cloudflare”z0 requires infrastructure that matches three fundamental needs:
| Need | Why | Cloudflare Answer |
|---|---|---|
| Edge-first | Routing decisions must be fast (< 50ms). Latency kills RTB auctions. | Workers run in 300+ locations, code executes close to users |
| Globally consistent state | Entity ledgers must be authoritative. No split-brain, no eventual consistency surprises. | Durable Objects provide single-threaded, strongly consistent state per entity |
| Serverless economics | Pay for invocations, not idle capacity. Scale to zero when quiet, scale infinitely when busy. | No servers to provision. Usage-based pricing across all services |
The alternative: Running our own distributed database cluster, managing replication, handling failover, provisioning capacity. This is infrastructure work, not z0 work.
The tradeoff: Cloudflare is opinionated. We accept their constraints in exchange for not building distributed systems primitives ourselves.
Platform Components
Section titled “Platform Components”Workers (Compute)
Section titled “Workers (Compute)”What: Serverless JavaScript/TypeScript execution at the edge.
z0 uses for:
- HTTP request handling (API endpoints, webhooks)
- Routing logic evaluation
- Orchestrating Durable Object calls
- External tool integrations (Twilio, OpenAI)
Key properties:
- 0ms cold start (V8 isolates, not containers)
- 30-second CPU time limit per request
- No persistent state (stateless by design)
- Global deployment by default
Constraint: Workers are stateless. All state lives in Durable Objects, D1, or R2.
Durable Objects (Stateful Entities)
Section titled “Durable Objects (Stateful Entities)”What: Single-threaded, globally unique objects with persistent storage.
z0 uses for:
- Per-entity ledgers (the Fact append log)
- Cached state (BudgetState, CapState, AccessState)
- Config version management
- Concurrency control (single-threaded = no race conditions)
Key properties:
- Exactly one instance globally per ID
- Single-threaded execution (serialized access)
- Transactional SQLite storage (local to the DO)
- Automatic geographic placement (moves toward callers)
Why this matters for z0:
// Entity "account_123" → Durable Object "account_123"// All Facts for this account append to this DO's ledger// All budget checks read from this DO's cached state// Single-threaded = no race conditions on budget updatesConstraint: DOs are single-writer. High-throughput scenarios require sharding (see sharding-patterns.md).
D1 (Queryable Storage)
Section titled “D1 (Queryable Storage)”What: Distributed SQLite database.
z0 uses for:
- Cross-entity queries (“all Facts for tenant X”)
- Reporting and analytics aggregation
- Entity lookups by external ID
- Historical Fact queries
Key properties:
- SQL query interface
- Read replicas at the edge
- Automatic replication from primary
- Familiar SQLite semantics
Why this matters for z0:
Durable Objects are optimized for single-entity access. D1 is optimized for cross-entity queries.
// DO: "Give me all Facts for account_123" → Fast, local read// D1: "Give me all charge Facts across all accounts this month" → SQL queryData flow: DO ledger → Queue → D1 (async replication)
Constraint: D1 has eventual consistency from DO writes. Queries may lag by seconds. Use DO for authoritative reads.
R2 (Object Storage)
Section titled “R2 (Object Storage)”What: S3-compatible object storage, zero egress fees.
z0 uses for:
- Call recordings and transcripts (invocation context)
- Document storage (contracts, invoices)
- Fact archive (cold storage for old Facts)
- Large payloads that don’t fit in DO storage
Key properties:
- S3 API compatibility
- No egress fees (unlike AWS S3)
- Integrated with Workers (direct binding)
- Unlimited storage
Why this matters for z0:
Invocations often have associated context (recordings, transcripts, images). This context is too large for DO storage but must be linked to Facts.
Fact { type: "invocation", data: { recording_url: "r2://recordings/inv_123.mp3", transcript_url: "r2://transcripts/inv_123.json" }}Constraint: R2 is object storage, not a database. No querying, no indexing. Store references in D1, content in R2.
Queues (Async Processing)
Section titled “Queues (Async Processing)”What: Message queues for decoupled, async processing.
z0 uses for:
- Fact replication (DO → D1)
- Webhook delivery
- Notification dispatch
- Non-critical background work
Key properties:
- At-least-once delivery
- Automatic retries with backoff
- Dead letter queue support
- Batch consumption
Why this matters for z0:
Not everything needs synchronous execution. Fact writes to D1 can lag. Webhook deliveries can retry. Background reconciliation can batch.
// Sync path (must be fast):Request → Worker → DO.append_fact → Response
// Async path (can be slow):DO.append_fact → Queue → D1.insert → Queue → Webhook.deliverConstraint: At-least-once means handlers must be idempotent. Use Fact IDs for deduplication.
Workflows (Durable Execution)
Section titled “Workflows (Durable Execution)”What: Long-running, resumable workflows with automatic state persistence.
z0 uses for:
- Multi-step billing cycles (invoice creation → delivery → payment tracking)
- Reconciliation runs (may take minutes, must survive failures)
- External integrations with retry logic
- Any process that spans multiple async steps
Key properties:
- Automatic state persistence between steps
- Survives Worker restarts and failures
- Built-in retry and error handling
- Can wait for external events (timers, webhooks)
Why this matters for z0:
Some processes take longer than Worker limits allow and must not lose progress:
Workflow: MonthlyBilling Step 1: Query all charge Facts for period → save state Step 2: Generate invoice documents → save state Step 3: Deliver invoices via email → save state Step 4: Record invoice_issued Facts → completeIf Step 3 fails, the workflow resumes from Step 3, not Step 1.
Constraint: Workflows have higher latency than direct Worker calls. Use for background processes, not real-time paths.
Analytics Engine (Metrics)
Section titled “Analytics Engine (Metrics)”What: High-cardinality time-series metrics storage and query.
z0 uses for:
- Real-time operational metrics (invocations/sec, error rates)
- Per-tenant usage dashboards
- Per-asset performance metrics
- SLA monitoring and alerting
Key properties:
- High-cardinality support (tenant_id, asset_id, tool_id as dimensions)
- Sub-second query latency
- 90-day retention
- SQL-like query interface
Why this matters for z0:
Facts answer “what happened.” Analytics Engine answers “how is the system performing right now.”
// Analytics Engine query:// "Invocations per second by tenant for the last hour"
SELECT tenant_id, SUM(_sample_interval) as invocationsFROM z0_invocationsWHERE timestamp > NOW() - INTERVAL '1 hour'GROUP BY tenant_id, blob1ORDER BY invocations DESCConstraint: Analytics Engine is for metrics, not Facts. Facts go in DO ledgers. Metrics derived from Facts go in Analytics Engine.
Mapping to z0 Primitives
Section titled “Mapping to z0 Primitives”| z0 Primitive | Source of Truth | Query Storage | Why |
|---|---|---|---|
| Entity | Durable Object | D1 | DO owns canonical state; D1 enables cross-entity queries |
| Fact | Durable Object ledger | D1 (replicated) | DO ensures append-only integrity; D1 enables reporting |
| Config | Durable Object | D1 (replicated) | DO enforces versioning semantics; D1 enables lookups |
Entity → Durable Object
Section titled “Entity → Durable Object”Each Entity gets a Durable Object. The DO ID combines the DO namespace with the Entity ID:
Entity ID: "acct_123" (includes type prefix)DO ID: "account_acct_123" (DO namespace + entity ID)
Entity(account, "acct_123") → DO("account_acct_123")Entity(asset, "asset_456") → DO("asset_asset_456")Entity(contact, "cont_789") → DO("contact_cont_789")ID Scheme:
- Entity ID has a type prefix:
acct_,asset_,cont_,deal_ - DO ID prepends the DO class namespace:
account_,asset_,contact_,deal_ - This allows routing to the correct DO class while preserving full entity identity
The DO holds:
- Entity metadata (mutable)
- Fact ledger for this entity (append-only)
- Cached state derived from Facts (reconcilable)
Fact → DO Ledger + D1
Section titled “Fact → DO Ledger + D1”Facts are appended to entity DOs, then replicated to D1:
1. Worker receives invocation2. Worker calls DO("asset_asset_456").appendFact(invocationFact)3. DO writes to local SQLite (transactional, durable)4. DO enqueues replication message5. Queue consumer writes to D16. D1 available for cross-entity queriesAuthoritative source: DO ledger (always consistent) Query source: D1 (eventually consistent, queryable)
Config → Versioned DO Storage
Section titled “Config → Versioned DO Storage”Configs live in DOs with version management:
1. Worker requests Config update2. DO reads current version (e.g., v3)3. DO writes new row (v4) with effective_at = now()4. DO updates v3 with superseded_at = now()5. DO returns new version6. Change replicates to D1 for queriesSingle-threaded DO execution ensures no version conflicts.
Cross-DO Transaction Patterns
Section titled “Cross-DO Transaction Patterns”Durable Objects provide single-entity transactions, but many business operations span multiple entities. z0 uses saga patterns orchestrated by Workflows to coordinate these operations.
The Problem
Section titled “The Problem”DOs are single-entity. There’s no distributed transaction across DOs:
// This is NOT atomic:DO("account_acct_123").deductBudget(100)DO("account_acct_456").addBudget(100)
// If the second call fails, the first already committed.// System is now inconsistent.The Solution: Sagas with Compensation
Section titled “The Solution: Sagas with Compensation”A saga is a sequence of local transactions where each step has a compensating action that undoes it on failure.
Workflow: TransferBudget(from: acct_123, to: acct_456, amount: 100)
Step 1: Reserve funds from source Action: DO("account_acct_123").reserveBudget(100) Compensate: DO("account_acct_123").releaseReservation(100)
Step 2: Add funds to destination Action: DO("account_acct_456").addBudget(100) Compensate: DO("account_acct_456").deductBudget(100)
Step 3: Confirm reservation (converts to deduction) Action: DO("account_acct_123").confirmReservation(100) Compensate: (none - final step)
On failure at any step: Execute compensations in reverse order Record failure FactCommon Cross-Entity Operations
Section titled “Common Cross-Entity Operations”| Operation | Entities Involved | Saga Steps |
|---|---|---|
| Budget transfer | Two accounts | Reserve → Add → Confirm |
| Contact ownership move | Contact + two tenants | Create lifecycle Fact → Update tenant_id → Record in both account ledgers |
| Campaign budget split | Campaign + multiple accounts | Validate totals → Reserve from each → Confirm all |
| Charge distribution | Outcome + multiple accounts | Calculate splits → Record charges to each account |
Implementation Pattern
Section titled “Implementation Pattern”Workflows provide the durability needed for sagas:
export class TransferBudgetWorkflow extends Workflow { async run(params: { from: string; to: string; amount: number }) { const { from, to, amount } = params;
// Step 1: Reserve from source const reservation = await this.step("reserve", async () => { const fromDO = this.env.ACCOUNT_LEDGER.get(from); return fromDO.reserveBudget(amount); });
// Step 2: Add to destination (with compensation on failure) try { await this.step("add", async () => { const toDO = this.env.ACCOUNT_LEDGER.get(to); return toDO.addBudget(amount); }); } catch (error) { // Compensate: release reservation await this.step("compensate-reserve", async () => { const fromDO = this.env.ACCOUNT_LEDGER.get(from); return fromDO.releaseReservation(reservation.id); }); throw error; }
// Step 3: Confirm reservation await this.step("confirm", async () => { const fromDO = this.env.ACCOUNT_LEDGER.get(from); return fromDO.confirmReservation(reservation.id); });
// Record completion Fact await this.step("record", async () => { // Both accounts get a lifecycle Fact recording the transfer await Promise.all([ this.env.ACCOUNT_LEDGER.get(from).appendFact({ type: "lifecycle", subtype: "budget_transferred_out", data: { to, amount, workflow_id: this.id } }), this.env.ACCOUNT_LEDGER.get(to).appendFact({ type: "lifecycle", subtype: "budget_transferred_in", data: { from, amount, workflow_id: this.id } }) ]); }); }}Saga Design Principles
Section titled “Saga Design Principles”- Each step must be idempotent — Workflows may retry failed steps
- Compensations must be safe to run multiple times — Network failures may cause duplicate compensations
- Reservations over direct mutations — Reserve first, confirm later allows rollback
- Record all steps as Facts — Full audit trail of what happened and why
- Workflow ID links all Facts — Cross-entity operations can be reconstructed
Failure Modes
Section titled “Failure Modes”| Failure | Result | Recovery |
|---|---|---|
| Step fails, compensation succeeds | Clean rollback | None needed |
| Step fails, compensation fails | Partial state | Manual intervention + reconciliation |
| Workflow crashes mid-saga | Incomplete | Workflow resumes from last saved step |
| Destination DO unavailable | Blocked | Workflow retries with backoff |
Key insight: Sagas trade atomicity for availability. The system may be temporarily inconsistent, but compensations restore consistency. This is acceptable because Facts provide the audit trail to detect and resolve issues.
What Cloudflare Does NOT Provide
Section titled “What Cloudflare Does NOT Provide”Cloudflare provides infrastructure primitives. z0 adds:
| Gap | What’s Missing | z0 Solution |
|---|---|---|
| Business primitives | No concept of Entity, Fact, Config | Primitives layer (PRIMITIVES.md) |
| Ledger semantics | DOs have SQLite, not append-only logs | Ledger pattern on top of DO storage |
| Config versioning | No built-in version management | Version semantics in Config schema |
| Cross-entity transactions | DOs are single-entity | Saga patterns, eventual consistency |
| Cached state reconciliation | No concept of derived state | Reconciliation Fact pattern |
| Multi-tenancy | Basic isolation only | tenant_id denormalization, access control |
| Economic attribution | No business logic | Attribution calculation in Interpretation Engine |
| Autonomy guardrails | No AI-specific features | Autonomy Config + Decision Facts |
Key insight: Cloudflare gives us distributed systems primitives done right. z0 adds economic truth and business semantics on top.
Component Selection Guide
Section titled “Component Selection Guide”| Scenario | Use | Not |
|---|---|---|
| Single-entity read/write | Durable Object | D1 |
| Cross-entity query | D1 | Durable Object |
| Large blob storage | R2 | D1 or DO |
| Async background work | Queue + Worker | Workflow |
| Long-running process | Workflow | Queue + Worker |
| Real-time metrics | Analytics Engine | D1 |
| Authoritative Fact read | Durable Object | D1 |
| Reporting query | D1 | Durable Object |
Summary
Section titled “Summary”┌─────────────────────────────────────────────────────────────┐│ z0 on Cloudflare │├─────────────────────────────────────────────────────────────┤│ z0 Layer (This is what we build) ││ ┌─────────────────────────────────────────────────────────┐││ │ Primitives: Entity, Fact, Config │││ │ Patterns: Ledger, Sharding, Reconciliation │││ │ Products: Call Tracking, Billing, Autonomy │││ └─────────────────────────────────────────────────────────┘│├─────────────────────────────────────────────────────────────┤│ Cloudflare Layer (This is what we use) ││ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ││ │ Workers │ │ Durable │ │ D1 │ │ R2 │ ││ │ (compute)│ │ Objects │ │ (query) │ │ (blobs) │ ││ └──────────┘ └──────────┘ └──────────┘ └──────────┘ ││ ┌──────────┐ ┌──────────┐ ┌──────────┐ ││ │ Queues │ │Workflows │ │Analytics │ ││ │ (async) │ │(durable) │ │ Engine │ ││ └──────────┘ └──────────┘ └──────────┘ │└─────────────────────────────────────────────────────────────┘Cloudflare provides the distributed systems foundation. z0 provides the economic truth layer. Together, they answer: “Was it worth it?”
Further Reading
Section titled “Further Reading”- durable-objects.md — Deep dive on DO patterns
- d1.md — Query patterns and replication
- queues.md — Async processing patterns
- workflows.md — Durable execution patterns
- analytics-engine.md — Metrics and dashboards
- observability.md — Traces, metrics, alerting
- agents-sdk.md — AI-powered autonomous agents