Tessera RP
PX-09
North Star
One system, drawn for two readers.
Engineers arrive wanting wire formats, table names, the tenancy mechanism and what happens when a source goes down. Finance and operations arrive wanting the value stream and the decisions it supports. Both drawings are here — pick a view.
Fig. 1
The stack, and what moves between layers.
Seven layers. Each one owns a job the layer above it is forbidden from doing, and the arrow between any two names the contract that crosses it.
L1 · Source
Your systems of record
Read where the data already is. Nothing is asked to change on your side.
8
connectors shipping today
15
more named on the roadmap
0
writes back into your ERP
Shipping · protocol
Dynamics 365 F&O · SQL
Business Central · OData v2
NetSuite · SuiteQL
Acumatica · REST
Shopify · GraphQL
Amazon · SP-API
Everything the ERP does not hold
CSV upload
Generic REST
SFTP drop
Google Sheets
L2 · Ingest
Connector SDK + sync runner
Talks to source APIs. Deliberately knows nothing about the warehouse schema.
Resume
watermark cursors, never a restart
Skip
content-hash, so a re-run is cheap
Survive
backoff + jitter, Retry-After, DLQ
L3 · Landing
Raw tier
Append-only JSONB, exactly as the source sent it.
1 table
not one per stream
Replayable
a mapping fix is a re-run, not a re-extract
Drift-proof
a new source field costs nothing here
Keyed by (tenant_id, connection_id, stream, source_id) with a JSONB payload and a content hash.
L4 · Canonical
ERP-agnostic star schema
The interoperability boundary. Above this line, nothing knows which ERP it came from.
14
conformed dimensions
13
fact tables
7
cost columns on every order line
Product cost, inbound freight, duty, outbound freight, payment fee, marketplace fee and returns provision each get their own column — because an ERP books revenue and product cost and none of the other five.
landed_cost and contribution_margin are generated columns. Postgres computes them, so no query can derive them a second way.
L5 · Graph
In build
Entities and the edges between them
Fed from the same canonical rows — it sits beside the star schema, not instead of it.
What it adds
Unbounded traversal
Multi-tier supplier exposure
Component-level recall
Entity resolution
Centrality · communities · cycles
Written as intent, not inventory. Everything above and below this layer is running.
L6 · Semantic
Governed metric catalog + compiler
One definition per metric, and the only thing in the system permitted to emit SQL.
229
metrics across 14 domains
101
dimensions they can be cut by
1
definition each — no second opinion
L7 · Surface
What people actually use
Fifteen surfaces, every one of them resolving the same metric definitions.
Decide
11 persona dashboards, Explore, the assistant
Operate
inventory, procurement, stores, finance, vendors
Keep
alerts, saved views, governed reports, exports
Spans every layer
Four concerns that cannot live in one band, because they hold at every band.
Tenancy
app.current_tenant()
Every tenant-scoped table has RLS ENABLE + FORCE. Access goes through withTenant(), which sets the GUC transaction-local. A forgotten WHERE clause returns zero rows, not another customer's books.
Authorization
role rank · data_scope
owner 40 > admin 30 > analyst 20 > viewer 10 gates mutations. A membership row can carry {"channel":["DTC-US"]} and the compiler appends it as a mandatory filter — a caller cannot widen their own scope.
Sessions
SHA-256, never the token
The cookie holds an opaque random token; only its hash is stored. active_tenant_id lives on the session, so switching tenant is an update rather than a re-login. sso_subject is already on the user model.
Observability
requestId + tenantId
Structured single-line JSON with deep key-based redaction, on every request-scoped line. /api/health for liveness, /api/ready for database reachability.
Fig. 2
The one path to the database.
Every read path — a dashboard tile, the Explore UI, an alert evaluation, the assistant — produces the same object and hands it to the same compiler.
The invariant
Nothing writes SQL except the compiler.
1
QuerySpec
Tile, Explore, alert rule or assistant — all four emit the same object
2
zod validate
Shape and types, before anything is resolved
3
resolve vs catalog
Every id must exist in the governed catalog or it stops here
4
append scope
The caller's row scope is added after their filters, never before
5
emit SQL
Engine-neutral, params bound separately
There is no code path from model output to SQL text. The worst a prompt injection can produce is a QuerySpec — which is then checked against the catalog and against the caller's own data scope before it runs.
Fig. 3
Module boundaries.
A layering only holds if the modules are forbidden from knowing about each other. This is that contract, and it is enforced by the import graph rather than by convention.
| Module | Owns | Must not know about |
|---|---|---|
lib/connectors | Talking to source APIs | The canonical schema |
lib/transform | raw → canonical mapping | HTTP, metrics |
lib/semantic | Metric definitions, SQL emission | React, HTTP |
lib/analytics | Forecast, anomaly, cohort | The database |
lib/personas | Declarative dashboard specs | SQL |
lib/assistant | NL → QuerySpec | SQL |
components/* | Rendering | The database |
The connector layer deliberately does not know the canonical schema. That is what makes adding an ERP a contained change — implement the interface, write a mapper, done.
Fig. 4
What happens when something breaks.
Named failure modes with a stated response, because a data platform is judged on the day a source goes down rather than the day it does not.
Source API down
Backoff with jitter; cursor preserved; connection marked degraded after N failures
Source rate-limits
Honour Retry-After; resume from the persisted cursor
Record unmappable
Dead-letter row; the sync continues; surfaced in Data Admin
Schema drift
Raw tier unaffected; the mapper returns null → DLQ; alert on DLQ volume
Query too slow
statement_timeout → 504 carrying a narrow-your-range message
Query too large
LIMIT max+1 detects truncation; the result is flagged, never silently cut
Missing LLM key
The assistant degrades to deterministic matching; the product still works
Fig. 1
From your systems to a decision.
What happens between pointing it at your systems and somebody acting on a number — and how long each part takes.
01
Connect
Point it at the systems you already run. No migration, no data project, nothing writes back.
Days, not quarters
02
Reconcile
Orders, inventory, receivables, payables and spend land in one shape, whichever system they came from.
One version of the business
03
Govern
Every number gets exactly one definition, owned and certified — so two reports cannot disagree.
Arguments end
04
Decide
Each role opens the questions they actually arrive with, and can ask a new one in English.
Minutes, not a ticket
05
Act
Thresholds watch the numbers, exports land on schedule, and every alert traces back to source rows.
It runs without you
Fig. 2
The number your ERP is getting wrong.
The reason the platform is built around cost components rather than a margin field copied out of the ERP.
18–30%
of net revenue on a typical DTC line — costs the ERP does not book against the order that incurred them.
ERP-reported margin for e-commerce is systematically overstated, and it is overstated in a way that looks fine on the P&L. The ERP books revenue and product cost correctly. It is the other four that get lost:
Outbound freight
Booked to a shipping account, not to the order line that caused it
Payment processing
A single monthly fee, never attributed to the products that generated it
Marketplace fees
Referral and fulfilment fees net out of the payout, invisible on the invoice
Returns
Land in a later period, against a different account, on a different product
So the platform stores all seven cost components against the order line itself, and the database — not a report, not a spreadsheet — computes contribution margin from them. That is the difference between knowing which SKUs make money and believing all of them do.
Fig. 3
What it can answer.
Eight business domains, 229 governed metrics between them. Every one has a single owner and a single definition.
Revenue
19 metrics
Net revenue by channel
Discount leakage
Price realisation
Cohort repeat rate
Margin
23 metrics
True contribution margin
Landed cost decomposition
SKU profitability
Channel mix effect
Inventory
20 metrics
Days of cover
Stockout exposure
Ageing and obsolescence
Replenishment signal
Fulfilment
17 metrics
On-time rate
Cost to serve
Split-shipment cost
Carrier performance
Customers
14 metrics
Acquisition cost
Lifetime value
Concentration risk
Churn signal
Procurement
24 metrics
Supplier lead time
PO ageing
Price variance
Vendor concentration
Finance
27 metrics
AR / AP ageing
Cash conversion cycle
Ledger reconciliation
Working capital
Marketing
13 metrics
Blended CAC
Spend efficiency
Contribution after ad spend
Channel attribution
Fig. 4
Who opens it, and why.
A platform is only used if somebody arrives with a question it was built for. These are the six that recur.
CFO / Finance
“Is the margin we are reporting the margin we are earning?”
ANS
Contribution margin computed from all seven cost components, reconcilable to the ledger.
ACT
Where to push price, and which channels are carrying the others.
Supply chain
“What am I about to run out of, and what is sitting dead?”
ANS
Days of cover against live demand, with ageing and obsolescence beside it.
ACT
What to reorder this week, and what to mark down.
Merchandising
“Which SKUs actually make money once everything is counted?”
ANS
SKU-level profitability after freight, fees and returns.
ACT
What to range, what to drop, what to promote.
Operations
“What is my real cost to serve, by channel?”
ANS
Fulfilment cost attributed to the order line that incurred it.
ACT
Which channels to grow and which to reprice.
Analyst
“Can I answer a question nobody built a dashboard for?”
ANS
Explore across 101 dimensions, or ask in English and get the query it ran.
ACT
A defensible answer, with the definition attached.
Exec
“What changed since last week, without me going to look?”
ANS
Thresholds and anomaly alerts on the same governed numbers.
ACT
Attention spent where something actually moved.
Fig. 5
What changes.
The operational difference, stated as the situations where it shows up.
| Without a governed layer | With Tessera RP | |
|---|---|---|
A new number is needed | A ticket, a week, and a spreadsheet nobody else can reproduce | A catalog entry, defined once, live on every surface |
Two reports disagree | A meeting to work out whose is right | Cannot happen — both resolve the same definition |
A second ERP arrives | The analytics work starts again | A connector and a mapper; every metric above carries over |
Someone asks why | The number is traced by hand, if at all | Drill through to the source rows that produced it |
An analyst leaves | Their logic leaves with them | The definitions are in the platform, owned and certified |
What the layering buys you
Add an ERP, keep the work
A new source is a connector and a mapper. Every metric, dashboard and alert above Layer 4 carries over untouched.
One number, everywhere
Contribution margin resolves the same catalog entry on a tile, in an export, in an alert and in an answer. Two surfaces cannot disagree.
Swap the engine, keep the metrics
The compiler emits engine-neutral SQL. Postgres today; the adapter is the seam for what comes next.
Isolation that survives a bug
Row-level security is enforced by the database, not by application filters — so a mistake returns nothing rather than somebody else's books.
