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
2. Facts Are Immutable [ONE-WAY DOOR]
Section titled “2. Facts Are Immutable [ONE-WAY DOOR]”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
3. Three Primitives Only [ONE-WAY DOOR]
Section titled “3. Three Primitives Only [ONE-WAY DOOR]”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
4. Time Is First-Class [ONE-WAY DOOR]
Section titled “4. Time Is First-Class [ONE-WAY DOOR]”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?“
6. Configs Are Versioned [ONE-WAY DOOR]
Section titled “6. Configs Are Versioned [ONE-WAY DOOR]”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
7. Derived State Is Disposable
Section titled “7. Derived State Is Disposable”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
8. Errors Are First-Class [ONE-WAY DOOR]
Section titled “8. Errors Are First-Class [ONE-WAY DOOR]”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
9. Settlement Is Temporal
Section titled “9. Settlement Is Temporal”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_offViolation 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?“
10. Treat AI as Just Another Tool
Section titled “10. Treat AI as Just Another Tool”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
11. Products Are Disposable
Section titled “11. Products Are Disposable”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
12. Budget Is Eligibility
Section titled “12. Budget Is Eligibility”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)
13. Assets Are Owned, Not Assigned
Section titled “13. Assets Are Owned, Not Assigned”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
Principle Precedence
Section titled “Principle Precedence”When principles conflict (rare, but possible), they are resolved in this order:
- Economics Must Close the Loop — this is the “why”; everything else serves this
- Facts Are Immutable — history cannot be falsified; trust requires it
- Three Primitives Only — the model must be preserved
- Time Is First-Class — without it, economics is incomplete
- Outcomes Are Versioned — understanding evolves, records remain
- Configs Are Versioned — reproducibility requires it
- Errors Are First-Class — failures are facts, not exceptions
- Derived State Is Disposable — truth lives in the ledger
- Settlement Is Temporal — economic timing must be explicit
- Budget Is Eligibility — RTB model integrity
- Treat AI as Just Another Tool — no special cases
- Products Are Disposable — flexibility over lock-in
- Assets Are Owned, Not Assigned — clean ownership model
- 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.
Applying Principles
Section titled “Applying Principles”When designing or reviewing anything in z0, ask:
- Can this answer “was it worth it?” → Required by Principle 1
- Does this modify historical Facts? → Violates Principle 2
- Does this map to Entity, Fact, or Config? → Required by Principle 3
- Does this track time properly? → Required by Principle 4
- Does this lose outcome history? → Violates Principle 5
- Does this record Config versions on Facts? → Required by Principle 6
- Could this be rebuilt from the ledger? → Required by Principle 7
- Does this record errors as Facts? → Required by Principle 8
- Is settlement timing explicit? → Required by Principle 9
- Does this treat AI specially? → Violates Principle 10
- Is this product-agnostic? → Required by Principle 11
- Does this respect budget as hard constraint? → Required by Principle 12
- Does this confuse ownership with context? → Violates Principle 13
- Does this store attribution as fact? → Violates Principle 14
If any answer is wrong, redesign until all answers are right.
Operational Corollaries
Section titled “Operational Corollaries”These are not principles—they are operational consequences that derive from the principles above.
Tenant ID Is Eventually Consistent
Section titled “Tenant ID Is Eventually Consistent”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:
tenant_idon Entities may be updated when ownership changes- Historical Facts retain their original
tenant_id(immutability, Principle 2) - New Facts receive the current
tenant_idat write time - 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 moveFact { type: "lifecycle", subtype: "tenant_changed", entity_id: "ent_123", timestamp: T, data: { from_tenant: "tenant_A", to_tenant: "tenant_B" }}
// Update EntityEntity { 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:
- Platform services maintain 99.95% uptime (measured monthly, excluding scheduled maintenance)
- Degraded operation is preferred over full failure (e.g., route with cached config vs don’t route)
- Failure isolation prevents cascading outages (circuit breakers, bulkheads, tenant isolation)
- 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_timeuptime% ≥ 99.95% OR incident_review_required = trueThis 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:
- Every write operation MUST support
Idempotency-Keyheaders - Idempotency keys are cached for 24 hours minimum
- Duplicate requests return the original response, not an error
- Idempotency scope is per-tenant (keys don’t collide across tenants)
Implementation:
// Request with idempotencyPOST /v1/factsIdempotency-Key: unique-request-id-12345Content-Type: application/json
{ "type": "charge", ... }
// First request: creates Fact, caches result// Retry with same key: returns cached result, no new Fact createdInvariant:
// 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 R2This 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:
- Circuit breakers are tenant-scoped (one tenant’s failures don’t trip breakers for others)
- Budget exhaustion affects only that tenant’s routing eligibility
- Rate limits are per-tenant, not global
- Error Facts include
tenant_idfor scoped alerting - 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.
The Ledger Is the Moat
Section titled “The Ledger Is the Moat”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:
| Category | Examples | Allocation Strategy |
|---|---|---|
| Direct | Twilio per-minute, OpenAI tokens | 1:1 to invocation via cost Fact |
| Usage-proportional | DO compute time, D1 queries | Pro-rata by tenant usage metrics |
| Fixed platform | Worker deployments, monitoring | Exclude from tenant ROI (z0 OpEx) |
| Storage | D1 rows, R2 objects | Pro-rata by tenant storage footprint |
Allocation Rules:
- Direct costs create cost Facts with explicit
source_idlinking to invocation - Usage-proportional costs are allocated monthly via
costFacts withsubtype: platform_allocation - Fixed platform costs are NOT allocated to tenants (z0 absorbs as operating expense)
- 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_accountWhat 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.