Fibretrace Monet docs/Knowledge base/Domain model + ERD

Domain model — the knowledge graph

Derived from the Supabase migrations (monet snapshot apps/dashboard/supabase/migrations/ plus the newer migrations at Lovable origin/main:supabase/migrations/), cross-checked against hooks and the real REST contract. Everything in this file is VERIFIED against a migration or service file unless marked otherwise. Backing legend (used in the diagram and the tables): GREEN = backed by a real Fibretrace REST endpoint wired in monet today; AMBER = a real endpoint exists but is partial (aggregate-only, read-only, roster-only, or unwired in monet); RED = Supabase-only, zero real REST equivalent.

Entity-relationship diagram

IDENTITY PROGRAMME / CAPACITY PRODUCTION SCANS / VERIFICATION NOMINATIONS / CLAIMS / EVIDENCE companies company_details company_users company_partners facilities scanners facility_licenses fibre_programmes program_owners programme_participants programme_producers programme_reservations reservation_requests program_certificates / documents / record_items pigment_ids production_records sliver_delivery_units (SDU) collections verifications sessions audits session_shares programme_facility_scans sessions_v / audits_v (masking views) verification_connections / deliveries evidence_requests (+shares) product_categories product_category_clients purchase_orders purchase_order_lines purchase_order_verifications purchase_order_verification_lines production_claims production_claim_lines production_claim_sessions claim_verifications purchase_order_evidence_packs public_verification_tokens company_certificates (+facilities, po_scope) evidence pack registry (8 types, code-side) 1:1 1:N 1:N N:M via assignments 1 owner 1:N approve -> reservation company_id pigment_id -> pigment_ids programme/facility FK raises Activated 1:N 1:N 1:N shared with company session UNIQUE -> programme_producers facility_id 1:N category defaults programme_id issuer + partner company PO 1:N session_id UNIQUE status: proposed snapshot claim <-> session manual link on confirm share tokens LEGEND GREEN — backed by real Fibretrace REST and wired in monet today AMBER — real endpoint exists but partial: aggregate-only, read-only, roster-only, or not yet consumed by monet RED — Supabase-only; zero real REST equivalent (BE-blocked) Arrows point from the many side to the one side (FK direction). Dashed boxes are not tables: DB views (masking) and a code-side registry. Supporting tables omitted for legibility are catalogued in the tables below: modules, api_keys, signup_requests, volume adjustments, pigment orders, SDU logs, notifications, invoices, banners, CMS, onboarding, help pages.

1. Identity and infrastructure

Entity Business-relevant fields Relationships Backing
companies name, sharing_enabled, confidence_score/rating/summary/drivers (0-100, bucketed Moderate/Good/Excellent by trigger), unclaimed + invite_token/email/sent_at/claimed_at (partner-CSV placeholder companies) hub of everything AMBER — GET /public-companies (is_public catalog) + GET /company (own); no full registry, private partners resolve “Unknown” in monet
company_details 1:1 companies: company_code (3-letter), company_type, supply_chain_tier (free text, self-declared), registration/billing/tax fields, hide_from_partner_directory, logo_url 1:1 companies GREEN — GET/POST /api/monet/v1/company
company_users name, email, role (owner/admin/auditor/pending/user), phone, blockchain_public_key N:1 companies GREEN — /team/members and invitations
company_partners requester/addressee company, status (pending/accepted/rejected), per-side reference labels; CHECK no-self; UNIQUE unordered pair N:M companies GREEN — /partners CRUD + pending-invitations
company_modules module_name (billing, verified_cta, verifications_menu, scanners_menu, program_owner, legacy sdu), enabled; UNIQUE(company,module) N:1 companies GREEN in monet (mapper wired)
company_api_access, api_keys enabled flag; key_value (REVOKEd from clients, key_last4 generated), description, last_used_at N:1 companies GREEN — /data-partner/api-keys (v2)
signup_requests applicant + company_name/code/type, tier, first facility fields, status, email_verified, mfa_code, approved_company_id/user_id, claim_company_id bootstraps companies/details/facility/first-admin on approval RED
facilities name, country, facility_type, address, status, is_free, os_hub_id, ewr_gin_id/holder_id, expires_at, lat/long N:1 companies; sessions and production happen here GREEN — full /facilities CRUD
scanners serial_number UNIQUE, model, status (+ scanner_facility_assignments join) assigned to facilities GREEN reads/PATCH — but no real delete or facility-assign endpoint; admin CRUD is Supabase fiction
facility_licenses company_id UNIQUE, license_count, subscription_status — count-based, no cost/period fields consumed by non-free active facilities RED (real side: GET /license-summary + POST /license-enquiries, a different shape)

2. Programme and capacity

Entity Business-relevant fields Relationships Backing
fibre_programmes name, status enum (hidden/active/expired/deleted), dates, pigment_id, fibre_type, country_of_origin, default_reservation_term_months (DEFAULT 18), heatmap_enabled, logo_url, commercial_description pigment N:1; children below AMBER — GET /fibre-programs(+id) deployed 2026-08-03, tenant-scoped, unwired in monet
program_owners UNIQUE(programme, company) — max one owner per programme via delete-then-insert 1:1-ish programme->company RED
programme_participants role CHECK (producer/participant/manufacturer), UNIQUE(programme, company, role) N:M programme<->company AMBER — returned inside /fibre-programs/{id} detail
programme_producers ceiling_mt, scans_per_mt, pigment_supplied_kg, scan_volume_total (=ceil(ceiling x scans_per_mt), trigger), scans_completed, produced_mt (capped at ceiling), production window; UNIQUE(programme, company) N:M; facilities via programme_producer_facilities (manual_logging_enabled; same-pigment-overlap trigger guard) AMBER — in detail response
programme_facility_scans session_id UNIQUE — one programme association per scan session links sessions -> programme_producers RED
programme_reservations quantity_mt (signed; zero forbidden), quantity_cu (=MT x 1000, sync trigger), notes, expires_at NOT NULL (defaulted created_at + term months by trigger) N:1 programme, N:1 company AMBER — only the summed funnel GET /claim-positions/{id}; no row-level GET; real BE has no expires_at anywhere
reservation_requests quantity_cu, status (pending default), decision_note, reviewed_at/by, approved_reservation_id, expires_at approval inserts the real reservation RED (real side: write-only POST reservations/enquiry email)
programme_volume_adjustments kind CHECK (reserved/claimed), delta_cu signed, resulting_cu, reason, admin_email admin ledger RED
program_certificates / certificate_types / program_certificate_assignments owner-uploaded credentials, status CHECK (active/revoked), expiry programme-level RED (read-only certificates[] array exists on /fibre-programs/{id})
program_documents / program_document_assignments owner-uploaded documents programme-level RED (documents[] in detail response)
program_record_items section CHECK (fibretrace/program/wording_supported/wording_notyet), title, body, visible the owner mini-CMS RED
explore_fibre_programs marketing catalog, status CHECK (available/coming_soon) admin CMS for discovery cards RED

3. Production and hardware

Entity Business-relevant fields Relationships Backing
production_records quantity, unit, weight_kg, status, source (manual/sdu/ewr/sdu_demo/ewr_demo), excluded, short_code (COMPANYCODE+6 digits, trigger), scans_required (1 for SDU/EWR, else ceil(quantity x 0.05)), scans_completed N:1 company/programme/facility; production_files 1:N RED
pigment_ids label UNIQUE, tau_value UNIQUE, int_value, tolerance programmes reference; pigment_company_assignments RED
pigment_orders company, programme, status, quantity_kg producer pigment procurement RED
sliver_delivery_units serial, device_id, model, firmware, status enum (feeding/ended/idle/starting/paused/blocked/fault), bale counters, dose_g_per_kg, env metrics, sliver_remaining_pct, speed_bph N:1 company/facility; sdu_activity_logs 1:N AMBER — real read-only GET /api/v1/sdu/devices family; zero write endpoints

4. Scans and verification

Entity Business-relevant fields Relationships Backing
collections company, programme, status, guid (auto) — the /verify/:guid anchor 1:N verifications GREEN — real orders/collections domain
verifications collection_id (nullable), name, status 1:N sessions RED as a resource (vocabulary exists only client-side against real BE)
sessions session_code, conducted_at, fibre_detected, scan_count, facility_id, production_record_id, direction + material_direction enum (inbound/outbound), scanner_serial, pigment_id, partner_company_id, carrier, shipping_doc_id, linked_outbound_session_id, shipper, shipping_id, counterparty_company_id, programme_id N:1 verification/facility; chain-of-custody pairing via shipping_doc_id RED — no real sessions resource; monet fakes session_id from the audit id
audits session_id, result, ft_id, blockchain_guid, notes N:1 sessions GREEN — GET /sat-audits + POST /sat-audit (but blockchain_guid has no source: always null against real BE)
session_shares UNIQUE(session, shared_with_company) session -> company grants RED
sessions_v / audits_v (views) + mask_ftid() masked session_code/ft_id/blockchain_guid unless claim-linked or setting off; base columns REVOKEd from clients — masking is server-side in Supabase read layer over sessions/audits RED (no real equivalent; session_code_masked flagged as unresolved BE requirement)
verification_connections / connection_deliveries / connection_interest type CHECK (csv/url/webhook), secret (REVOKEd), delivery status (queued/sent/failed); interest = insert-only marketing signal data-connections surface RED
evidence_requests / evidence_request_shares requester/recipient company, programme, quantity_mt, scans_requested, sku, order_reference, lot, status retailer asks supplier for evidence RED

5. Nominations, claims, evidence

Entity Business-relevant fields Relationships Backing
product_categories ref, kind (primary/custom), hs_codes[], baseline_net_weight_kg, default_blend_pct, loss_multiplier (internal-only), gsm; client scoping via product_category_clients feeds PO line defaults RED
purchase_orders company_id (retailer/issuer), partner_company_id (manufacturer), po_reference (UNIQUE per issuer+partner while unarchived), sku, programme_id, volume_mt (CHECK >= 0), volume_claimed_mt, volume_cu/volume_claimed_cu (Tier 0 source of truth, MT synced x1000), status enum (draft/shared/linked/ready/proposed/partially_claimed/fully_claimed/closed), archived, linking_due_at, last_reminder_at, supplier_closed_at/note N:1 both companies + programme RED — no real PO resource anywhere; real /orders is Collections
purchase_order_lines line_no, sku, quantity, hs_code, product_category_id, net_weight_kg, blend_pct, loss_multiplier, cu_total (summed to header by trigger), quantity_claimed, overridden, excluded N:1 PO; N:1 category RED
purchase_order_verifications session_id UNIQUE — a scan session links to at most one PO ever; DB trigger enforces partner-facility-or-shared and blocks when PO fully_claimed/closed; bumps PO to ready PO <-> session RED
purchase_order_verification_lines tags scans to specific PO lines join RED
production_claims programme_id, company_id (the RETAILER/PO issuer), purchase_order_id, quantity_mt/quantity_cu, status CHECK (proposed/confirmed/rejected) — app also writes revoked with revoked_at/by/reason, see open question Q10 — plus confirmed/rejected metadata, rejection_reason, shipment_ref, shipped_on N:1 programme/company/PO AMBER — POST /api/monet/v1/claims exists (single-step, different model); no list/lifecycle endpoints
production_claim_lines per-line allocation snapshot: quantity, cu, cu_per_piece, net_weight_kg, blend_pct, loss_multiplier N:1 claim -> PO line RED
production_claim_sessions UNIQUE(company_id, session_id) — a company cannot claim the same scan twice claim <-> session RED
claim_verifications claim_id, session_id UNIQUE — manual (non-PO) claim-to-scan links; dropped in the April reset, re-created 2026-07-22 claim <-> session RED
purchase_order_evidence_packs PO, claim, volume_mt, is_partial; insert trigger meters volume against PO remaining and recomputes claimed/status created on confirm RED
public_verification_tokens token UNIQUE; references at least one of session/verification/claim; enabled toggle; seeded for trade/sustainability/procurement on confirm public /c/:token access RED
Evidence pack registry (code) 8 pack types with sections and shareability: verification-record, sustainability, procurement, retailer, assurance, regulator, dpp, supplier src/lib/evidencePacks.ts RED (data side)
company_certificates / company_certificate_facilities / po_scope_certificates company-level certs with all-or-specific-facility scoping; po_scope_certificates: PO UNIQUE -> certificate settings certificates + PO scoping RED (real /certificate-types is staff-only admin CRUD)

6. Admin, settings, messaging

Entity Fields Backing
app_settings key/value jsonb; seeded billing_enabled_site_wide, obfuscate_verification_ids, chatbase_widget GREEN in monet as a mapper; the obfuscation key itself has no real BE home
cms_strings, help_pages, admin_banners (+dismissals) admin CMS content; banner severity CHECK RED
notifications / notification_templates / notification_preferences server-generated rows; real BE notifications are read-only GET + mark-read (no client insert, no link field) AMBER — GET /user/notifications real; Lovable’s client-insert pattern has no equivalent
onboarding_tasks company task list with due dates RED
invoices invoice_number, purchase_order (free text), billing_period, amount, currency, status GREEN — /company/invoices

Cardinality highlights worth memorising

One owner company per programme. One producer row per company per programme. One reservation row per adjustment (uniqueness deliberately dropped 2026-08-16 to enable the ledger). One partnership per unordered company pair, never self. A scan session appears at most once in each of: programme_facility_scans, purchase_order_verifications, claim_verifications (UNIQUE session_id), and at most once per company in production_claim_sessions. A PO reference is unique per issuer+partner while unarchived. A public token must point at something (CHECK session/verification/claim not all null). All VERIFIED (schema section 6 of the migration sweep).