Skip to content

z0 Principles

Non-negotiable truths that govern all z0 design decisions.

These principles are axiomatic. They are not suggestions, best practices, or guidelines. Every pattern, product, and line of code must be traceable back to these principles. If something violates a principle, it is wrong—regardless of how clever, efficient, or expedient it may seem.

One-Way Door Notation: Principles marked with [ONE-WAY DOOR] represent irreversible architectural decisions. Violating these principles creates technical debt that compounds over time and may be impossible to unwind. Two-way door principles can be revisited as understanding evolves.


1. Economics Must Close the Loop [ONE-WAY DOOR]

Section titled “1. Economics Must Close the Loop [ONE-WAY DOOR]”

Statement: Every action must connect to economic consequence. Costs flow to outcomes. ROI must be calculable.

Why this is #1: The primitives exist to serve economics, not the other way around. “Was this worth it?” is the question that justifies everything else.

Implications:

  • All tool usage has a cost (even if zero, it’s explicitly zero)
  • Every charge traces to an outcome, every cost traces to an invocation
  • Revenue/value is tied back to the assets and actions that produced it
  • The system can answer: “Was this worth it?”
  • Shared costs (infrastructure, overhead) must have allocation rules

Violation examples:

  • Tracking costs without connecting them to outcomes
  • Tracking outcomes without knowing what they cost
  • Building features that can’t answer economic questions
  • Incurring costs that can’t be attributed to any entity or context

Statement: Once a Fact is recorded, it cannot be changed. It happened. The record is permanent.

Why this is foundational: Immutability enables reproducibility, auditing, and trust. If Facts can change, nothing can be verified.

Implications:

  • Facts are append-only
  • Corrections create new Facts, they don’t modify old ones
  • The Fact ledger is the source of truth
  • No “soft deletes” or “updated_at” semantics for Facts

Violation examples:

  • Updating a Fact record to “fix” bad data
  • Deleting Facts to clean up mistakes
  • Using mutable state to represent historical events

Statement: Entity, Fact, Config. Everything else is typed instance, derived calculation, or cached state.

Implications:

  • New concepts must map to existing primitives
  • Adding a primitive requires extraordinary justification and survives adversarial review
  • If a new idea requires a new primitive, it’s probably a product pretending to be infrastructure

Schema Evolution:

  • Primitive schemas evolve through additive changes only
  • New fields are optional with defaults; existing fields are never removed or renamed
  • Breaking changes require new Fact types, not mutations to existing types
  • Schema version is recorded on every Fact for forward compatibility

Relationship Modeling:

  • Entity-to-Entity relationships are Facts (e.g., “parent_assigned”, “linked_to”)
  • Relationships have timestamps and can evolve over time
  • Current relationships are derived from the Fact history
  • Hierarchies emerge from relationship Facts, not from Entity fields

Violation examples:

  • Creating a fourth primitive without proving the three are insufficient
  • Building schemas that don’t derive from Entity, Fact, or Config
  • Treating derived state as if it were primitive
  • Storing relationships as mutable Entity fields instead of Facts

Statement: Time is not an afterthought. Duration, latency, and lifecycle are core properties that affect economics and decisions.

Implications:

  • Every Fact has a timestamp (millisecond precision minimum, with timezone/offset)
  • Entities have temporal properties (created_at, lifecycle states)
  • Time-to-value is measurable
  • Config versions are time-bound (effective_at, superseded_at)
  • Causal ordering is preserved where wall-clock time is insufficient

Violation examples:

  • Storing Facts without timestamps
  • Not being able to answer “how long did this take?”
  • Ignoring when a Config was in effect
  • Using inconsistent time precision across the system

5. Outcomes Are Versioned, Not Rewritten [ONE-WAY DOOR]

Section titled “5. Outcomes Are Versioned, Not Rewritten [ONE-WAY DOOR]”

Statement: Business outcomes (won, lost, qualified) can be revised as new information arrives, but each version is preserved as a new Fact. History shows the evolution of understanding.

Implications:

  • Outcome changes create new Facts with references to prior state
  • “Current” outcome is the latest Fact, but all versions are queryable
  • Attribution can be recalculated against any point in time
  • Auditing shows how understanding changed over time

Violation examples:

  • Overwriting an outcome Fact when status changes
  • Losing the history of outcome revisions
  • Making it impossible to answer “what did we think happened on date X?“

Statement: Every Config has a version. Every Fact that depends on a Config records which version was applied.

Version Lifecycle:

  • Config changes always create new versions (no in-place mutation)
  • Versions have effective_at timestamps; old versions have superseded_at
  • The trigger for a new version is any change to Config content
  • Configs may have draft/active/archived lifecycle states

Implications:

  • Config changes create new versions, not mutations
  • Facts link to config_id AND config_version
  • Historical calculations can be reproduced exactly
  • Audit trail shows what rules applied when

Violation examples:

  • Modifying a Config in place
  • Recording a Fact without the Config version that evaluated it
  • Making it impossible to reconstruct past calculations

Statement: Anything not in the ledger (Entity, Fact, Config) can be rebuilt from the ledger. Cached state is optimization, not truth.

Implications:

  • Cached State (BudgetState, CapState, etc.) is explicitly named and reconcilable
  • Facts win on conflict with cached state
  • Losing cached state is inconvenient, not catastrophic
  • Materialized views are derivatives, not sources
  • Reconciliation points must exist to detect drift between cache and ledger

Violation examples:

  • Treating cached state as authoritative
  • Building features that break if cache is lost
  • Storing derived calculations as if they were Facts

Statement: Failures, retries, timeouts, and exceptions are Facts. If it happened, it’s recorded. Error handling is not exceptional—it’s expected.

Why this matters: Systems fail. The question is whether you can understand how and why. Errors that aren’t Facts are invisible to economics, debugging, and improvement.

Implications:

  • Every error creates a Fact (type: “error” or “failure”)
  • Error Facts include: error_code, message, context, retry_count, resolution
  • Retries are linked to the original attempt via correlation_id
  • Timeout and circuit-breaker events are Facts
  • Error rates and patterns are derivable from the ledger

Error Fact Structure:

Fact {
type: "error",
subtype: "tool_failure" | "timeout" | "validation" | "external",
entity_id: "...",
correlation_id: "original_request_id",
timestamp: "...",
data: {
error_code: "ETIMEDOUT",
message: "Connection timed out after 30s",
retry_attempt: 2,
max_retries: 3,
resolved: false,
resolution_fact_id: null
}
}

Violation examples:

  • Catching and swallowing errors without recording
  • Logging errors but not creating Facts
  • Making it impossible to answer “what went wrong?”
  • Hiding retry attempts from the economic loop

Statement: Economic loops close at defined points in time. Settlement timing (real-time, eventual, batch) is explicit and configurable per context.

Why this matters: “Economics must close the loop” (Principle 1) doesn’t specify when. Different use cases need different settlement timing. RTB needs real-time; monthly billing needs batch.

Settlement Models:

  • Real-time: Loop closes immediately (budget decrement on bid acceptance)
  • Eventual: Loop closes when outcome is confirmed (cost recognized on call completion)
  • Batch: Loop closes on schedule (daily reconciliation, monthly invoicing)

Implications:

  • Every economic event specifies its settlement model
  • “Incurred” vs “settled” are distinct states with their own timestamps
  • Unsettled economics are tracked as pending until resolution
  • Settlement timing is a Config, not hardcoded
  • Reconciliation processes handle settlement failures

Settlement States:

incurred → pending → settled
→ disputed → resolved
→ written_off

Violation examples:

  • Assuming all economics settle immediately
  • Mixing settlement models without explicit tracking
  • Losing track of unsettled costs
  • Not being able to answer “what’s our exposure on pending settlements?“

Statement: AI models are tools. They have costs, latencies, failure modes, and outputs—just like Twilio, Stripe, or any API.

Implications:

  • AI does not get special status in the architecture
  • AI invocations are metered, costed, and tracked like any other tool
  • AI failures are handled the same way as any tool failure
  • AI-created assets follow the same lifecycle as any other asset

Non-Determinism Handling:

  • AI outputs are inherently non-deterministic (same input, different output)
  • Facts involving AI must record: model_id, model_version, temperature/parameters
  • For reproducibility, record the seed if available; otherwise accept non-reproducibility
  • AI confidence scores, if available, are part of the Fact data

AI Fact Structure:

Fact {
type: "tool_invocation",
subtype: "ai_completion",
data: {
model_id: "gpt-4",
model_version: "gpt-4-0125-preview",
parameters: { temperature: 0.7, max_tokens: 1000 },
input_hash: "sha256:...", // For debugging, not reproducibility
output: "...",
latency_ms: 1234,
cost_usd: 0.03,
tokens: { input: 500, output: 200 }
}
}

Violation examples:

  • Building “AI-specific” infrastructure that doesn’t generalize
  • Exempting AI costs from economic analysis
  • Treating AI outputs as more trustworthy than other tool outputs
  • Not recording model versions on AI-generated Facts

Statement: Products (applications built on z0) come and go. The primitives and data they generate must outlive them.

Reframe: This is not “your work doesn’t matter”—it’s “primitives outlive products.” The data you generate has value beyond any single application.

Implications:

  • Product-specific logic lives in products, not in core
  • Data schemas are primitive-centric, not product-centric
  • Shutting down a product doesn’t orphan its data
  • New products can be built on existing primitive data
  • Product-specific economic logic must be Config, not hardcoded

Violation examples:

  • Putting product-specific fields in primitive schemas
  • Creating data that only makes sense within one product
  • Building primitives that only serve one use case
  • Hardcoding product assumptions into the interpretation engine

Statement: Budget is not a suggestion or a reporting metric. No budget means not eligible—not in the auction, not considered.

Implications:

  • RTB model: budget determines participation
  • Cached BudgetState checked before any routing decision
  • Exhausted budget = immediate exclusion
  • Budget is a hard constraint, not a soft limit

Budget Exhaustion During Execution:

  • Budget is checked at decision time (auction entry)
  • Once committed, the action completes even if budget exhausts mid-execution
  • Over-budget execution creates a “budget_overrun” Fact
  • Overruns are reconciled in the next settlement cycle
  • Repeated overruns trigger alerts and may adjust future budget checks (headroom)

Budget States:

available → committed → spent
→ released (if action cancelled)

Violation examples:

  • Routing to a buyer who has no budget
  • Treating budget as advisory
  • Allowing systematic overspend without tracking
  • Failing mid-execution due to budget exhaustion (budget checked too late)

Statement: Assets belong to owners (Entities). Campaign context is determined at runtime, not by permanent assignment.

Implications:

  • Assets have owner_id, not campaign_id as permanent attribute
  • Campaign context flows through at invocation time
  • An asset can serve different campaigns over its lifecycle
  • Ownership is about economics; campaign is about context

Violation examples:

  • Permanently assigning assets to campaigns
  • Losing asset history when campaigns change
  • Conflating ownership with operational context

14. Attribution Is Calculation, Not Storage

Section titled “14. Attribution Is Calculation, Not Storage”

Statement: Attribution (which assets/touches get credit for outcomes) is computed by the Interpretation Engine, not stored in the ledger.

Implications:

  • The ledger stores facts: events happened, outcomes resolved
  • Attribution models are Config (per-project setting)
  • Changing the model recalculates results, doesn’t modify Facts
  • Different stakeholders can apply different models to same data

Violation examples:

  • Storing attribution splits as Facts
  • Making attribution immutable
  • Preventing recalculation with different models

When principles conflict (rare, but possible), they are resolved in this order:

  1. Economics Must Close the Loop — this is the “why”; everything else serves this
  2. Facts Are Immutable — history cannot be falsified; trust requires it
  3. Three Primitives Only — the model must be preserved
  4. Time Is First-Class — without it, economics is incomplete
  5. Outcomes Are Versioned — understanding evolves, records remain
  6. Configs Are Versioned — reproducibility requires it
  7. Errors Are First-Class — failures are facts, not exceptions
  8. Derived State Is Disposable — truth lives in the ledger
  9. Settlement Is Temporal — economic timing must be explicit
  10. Budget Is Eligibility — RTB model integrity
  11. Treat AI as Just Another Tool — no special cases
  12. Products Are Disposable — flexibility over lock-in
  13. Assets Are Owned, Not Assigned — clean ownership model
  14. Attribution Is Calculation — interpretation, not fact

One-Way Door Summary: Principles 1-6, 8 are one-way doors. Once you violate immutability, versioning, or error recording, unwinding the damage is extremely difficult. Principles 7, 9-14 are two-way doors—important, but recoverable if you need to adjust.


When designing or reviewing anything in z0, ask:

  1. Can this answer “was it worth it?” → Required by Principle 1
  2. Does this modify historical Facts? → Violates Principle 2
  3. Does this map to Entity, Fact, or Config? → Required by Principle 3
  4. Does this track time properly? → Required by Principle 4
  5. Does this lose outcome history? → Violates Principle 5
  6. Does this record Config versions on Facts? → Required by Principle 6
  7. Could this be rebuilt from the ledger? → Required by Principle 7
  8. Does this record errors as Facts? → Required by Principle 8
  9. Is settlement timing explicit? → Required by Principle 9
  10. Does this treat AI specially? → Violates Principle 10
  11. Is this product-agnostic? → Required by Principle 11
  12. Does this respect budget as hard constraint? → Required by Principle 12
  13. Does this confuse ownership with context? → Violates Principle 13
  14. Does this store attribution as fact? → Violates Principle 14

If any answer is wrong, redesign until all answers are right.


These are not principles—they are operational consequences that derive from the principles above.

Context: Entities and Facts denormalize tenant_id for O(1) query scoping. But tenant relationships can change: a customer account can be reassigned, a campaign can move between accounts.

Policy:

  1. tenant_id on Entities may be updated when ownership changes
  2. Historical Facts retain their original tenant_id (immutability, Principle 2)
  3. New Facts receive the current tenant_id at write time
  4. Reporting queries must account for tenant_id changes over time

Migration Pattern:

// Entity moves from tenant_A to tenant_B at time T
// Before T: Entity.tenant_id = tenant_A, all Facts have tenant_id = tenant_A
// At T: Create lifecycle Fact recording the move
Fact {
type: "lifecycle",
subtype: "tenant_changed",
entity_id: "ent_123",
timestamp: T,
data: { from_tenant: "tenant_A", to_tenant: "tenant_B" }
}
// Update Entity
Entity { id: "ent_123", tenant_id: "tenant_B", ... }
// After T: New Facts have tenant_id = tenant_B
// Historical Facts still have tenant_id = tenant_A (immutable)

Querying Across Tenant Changes:

  • For current state: use Entity.tenant_id
  • For historical analysis: join Facts to lifecycle events, or accept that old Facts show old tenant
  • For compliance/export: include all Facts regardless of current tenant_id

Invariant:

// Lifecycle Fact required for tenant changes
∀ Entity WHERE tenant_id changed → ∃ Fact(lifecycle, tenant_changed)

This accepts eventual consistency as a tradeoff for query performance. The ledger (Facts) remains immutable; only Entity metadata changes.

Availability Enables Economics (Derived from Principle 1)

Section titled “Availability Enables Economics (Derived from Principle 1)”

Context: Economics can only close the loop if the system is operational. Downtime in an RTB platform means zero calls routed, zero revenue, zero data.

Policy:

  1. Platform services maintain 99.95% uptime (measured monthly, excluding scheduled maintenance)
  2. Degraded operation is preferred over full failure (e.g., route with cached config vs don’t route)
  3. Failure isolation prevents cascading outages (circuit breakers, bulkheads, tenant isolation)
  4. Every outage creates error Facts (Principle 8) with economic impact calculated

Architectural Patterns:

  • Circuit breakers on external dependencies (Twilio, AI models, etc.)
  • Cached state for read-heavy operations (BudgetState, CapState)
  • Graceful degradation when interpretation engine is slow
  • Fallback routing when preferred buyers are unavailable

Invariant:

// Platform availability is measurable via error Facts
∀ month → uptime% = (total_time - Σ error_fact.duration) / total_time
uptime% ≥ 99.95% OR incident_review_required = true

This derives from Principle 1 (economics require operation) and Principle 8 (errors are facts), not from a new principle.

Idempotency Is Required (Derived from Principle 2)

Section titled “Idempotency Is Required (Derived from Principle 2)”

Context: Facts are immutable. Network failures cause retries. Without idempotency, retries create duplicate Facts, corrupting the ledger.

Policy:

  1. Every write operation MUST support Idempotency-Key headers
  2. Idempotency keys are cached for 24 hours minimum
  3. Duplicate requests return the original response, not an error
  4. Idempotency scope is per-tenant (keys don’t collide across tenants)

Implementation:

// Request with idempotency
POST /v1/facts
Idempotency-Key: unique-request-id-12345
Content-Type: application/json
{ "type": "charge", ... }
// First request: creates Fact, caches result
// Retry with same key: returns cached result, no new Fact created

Invariant:

// Idempotency prevents duplicate Facts
∀ Idempotency-Key K, requests R1 and R2 with key K →
result(R1) = result(R2) AND Facts created by R1 = Facts created by R2

This derives from Principle 2 (Facts are immutable). If retries could create duplicates, the ledger would become inconsistent.

Blast Radius Is One Tenant (Derived from Tenant Isolation)

Section titled “Blast Radius Is One Tenant (Derived from Tenant Isolation)”

Context: Multi-tenant platforms must prevent failures from cascading. One tenant’s budget exhaustion, traffic spike, or misconfiguration must not affect other tenants.

Policy:

  1. Circuit breakers are tenant-scoped (one tenant’s failures don’t trip breakers for others)
  2. Budget exhaustion affects only that tenant’s routing eligibility
  3. Rate limits are per-tenant, not global
  4. Error Facts include tenant_id for scoped alerting
  5. Noisy neighbor protection via per-tenant resource quotas

Architectural Patterns:

  • Durable Objects are keyed by entity_id (tenant-scoped by design)
  • Queue consumers process tenant batches independently
  • D1 queries always filter by tenant_id
  • Cached state is tenant-partitioned

Invariant:

// Failures are tenant-isolated
∀ failure F affecting tenant T →
other_tenants.availability = unchanged AND
other_tenants.latency = unchanged (within SLO)

This derives from the platform’s multi-tenant architecture. Tenant isolation is not optional—it’s structural.

Context: The three primitives (Principle 3) are not defensible—anyone can copy Entity/Fact/Config. What’s defensible is the accumulated data.

Competitive Position:

  • Facts accumulate over time; history becomes irreplaceable
  • Context compounds: each new Fact is more valuable because of existing Facts
  • Interpretation improves: more data enables better attribution models
  • Network effects: more buyers in RTB = better matching for everyone

Implication: The moat is not the architecture—it’s the ledger. Protect and grow the ledger.

Shared Costs Must Be Allocated (Derived from Principle 1)

Section titled “Shared Costs Must Be Allocated (Derived from Principle 1)”

Context: Platform infrastructure (DO compute, D1 storage, R2, queues) incurs costs that don’t trace directly to individual invocations. Without allocation rules, these costs create a gap in the economic loop.

Cost Categories:

CategoryExamplesAllocation Strategy
DirectTwilio per-minute, OpenAI tokens1:1 to invocation via cost Fact
Usage-proportionalDO compute time, D1 queriesPro-rata by tenant usage metrics
Fixed platformWorker deployments, monitoringExclude from tenant ROI (z0 OpEx)
StorageD1 rows, R2 objectsPro-rata by tenant storage footprint

Allocation Rules:

  1. Direct costs create cost Facts with explicit source_id linking to invocation
  2. Usage-proportional costs are allocated monthly via cost Facts with subtype: platform_allocation
  3. Fixed platform costs are NOT allocated to tenants (z0 absorbs as operating expense)
  4. Storage costs are allocated based on periodic snapshots of tenant data volume

Platform Allocation Fact:

Fact {
type: "cost",
subtype: "platform_allocation",
entity_id: "acct_tenant_123",
timestamp: end_of_period,
data: {
period_start: "2025-01-01",
period_end: "2025-01-31",
allocation_basis: "do_compute_ms",
tenant_usage: 1_500_000,
total_usage: 100_000_000,
tenant_share: 0.015,
allocated_cost: 45.00
},
config_id: "cfg_platform_allocation",
config_version: 1
}

Allocation Config:

Config {
type: "platform_allocation",
settings: {
allocation_method: "usage_proportional",
metrics: ["do_compute_ms", "d1_queries", "r2_storage_gb"],
weights: { do_compute_ms: 0.5, d1_queries: 0.3, r2_storage_gb: 0.2 },
minimum_allocation: 0, // No minimum charge
allocation_frequency: "monthly"
}
}

Invariants:

// All non-fixed costs must be allocated
∀ platform_cost → ∃ Fact(cost, platform_allocation) OR cost.category = "fixed_platform"
// Allocation must sum to total
Σ tenant_allocations = total_usage_proportional_cost
// Tenants can query their platform costs
∀ tenant → can calculate: Σ Fact(cost) WHERE entity_id = tenant_account

What this enables:

  • Tenants see true cost of platform usage
  • z0 can identify high-cost tenants for pricing discussions
  • ROI calculations include platform overhead
  • No “invisible” costs that break the economic loop

This derives from Principle 1 (economics must close the loop). Costs that can’t be attributed to entities violate the principle.