Skip to main content

The public catalog: a closed set of shared tables

Orbit resolves tenancy two ways: inbound, from an API key or session; and the reverse, from the destination of an inbound event. The reverse direction needs a small set of tables every tenant and every edge node can read without a tenant context — the public catalog. This page is its hub: it lists the closed set, links each table’s owning design page, and gives the engineering checklist for adding a new one. Tenant isolation documents the schema-per-tenant split; this page documents the deliberate exception to it.

Section 1 — What reaches the edge unauthenticated

Three inbound surfaces reach Orbit before any tenant context exists:
  • Voice. A SIP INVITE addressed to a dialed number. The dialed number is the only input — there is no API key, no caller credential, no session. The edge must decide where the call terminates before media flows.
  • Mobile-originated SMS. An inbound SMS carries a destination DID and a sender. The resolver must answer “which tenant owns this number” before routing rules or inbox assignment run.
  • Delivery receipts. A carrier DLR arrives with no bearer token. Like MO SMS, it resolves tenant from the destination number alone.
Tenant resolution, then, cannot happen per-request on these surfaces — it happens per-event, from the event payload. Any table that joins the public catalog exists to support exactly this per-event resolution, and it is joined by nothing else.

Section 2 — The closed set

The public schema holds catalog and routing rows — one row per platform artifact or per org — never per-tenant working data. The full set, closed at eight tables (the platform DID pool and assignments count as one pair): This set is closed. Nothing else joins it unless a concept page documents the reason — the same ownership discipline this page applies. If you are reading this to answer “is a public table allowed?”, the answer is only yes when a concept page exists for the exception.

Section 3 — Where each owning concept sits

Each table or pair above has one owning design page. These pages own the rationale; this page fetches, it does not repeat.

Section 4 — The four tests a new public-schema table must pass

When you consider adding a table to the public schema, every one of these must hold:
  1. It resolves tenant from payload alone. The edge has no credential context, so the row must carry enough by itself to pin the owning org. If resolution needs anything outside the payload, the table does not belong here.
  2. Resolution is O(1) per event. A public-catalog read is on the inbound hot path. Bounded fallback scans exist to cover drift, not as the steady state — a new row must index its lookup key, not contribute to a scan.
  3. Ownership is re-validated per event. Never cached, never “owned forever.” A recycled DID changes hands; the guard that re-checks ownership on every event is what keeps the new owner’s inbound from landing on the previous owner’s endpoint.
  4. Writes are tenant-bounded. A purchase, admin action, or reconcile backfills rows; it never writes on behalf of no-owner. If a row cannot be attributed to exactly one org at write time, it must not be written.
The drift-convergence mechanism behind tests 2–3 is the hourly inbound-reverse-map reconcile (reverse-map section) plus the operator CLI that runs the same backfill for one tenant or as a dry run. Any new public table that resolves inbound traffic must have an equivalent reconcile before it can go live.

Section 5 — Checklist: adding a public table

Run this before touching the schema, and link the three artifacts it names in your change description:
  • A concept page documents the design rationale, and the table is added to the closed set above — same commit.
  • The read guard is in place: an index on the lookup key, ownership re-checked per event, ambiguity dropped rather than guessed.
  • The write guard is in place: writes are tenant-bounded, and idempotent so the reconcile can converge any drift.
  • A reconcile scheduler and a CLI backfill exist and are wired to converge the reverse-map-like index the table maintains.
  • Tenant isolation Section 1 lists the new table, and this page was updated — because the set is closed, adding to it is a documented decision, not a migration side effect.
The pair of read guards and write guards is enforced in code; the page’s job is to make sure adding to the set is never an accident.