Diamond Stone: every technology, what it does, and how they fit
Date: 2026-11-11. Revision 1. Drawn in docs/stack-map.html.
The complete list of what the platform is built from. For each piece: what it is in
plain words, what it does here, how, what it gains us, and what it connects to.
docs/PLAN.md sections 6, 13 and 14 hold the decisions; this document explains them.
docs/system-flows.html shows what the system does with these parts, and
docs/FRONTEND.md shows how the interface is coded.
Nothing here has been built or measured. Where a supplier is not chosen it says "to choose". Library names in section 9 are proposals to confirm in Stage 0.
1. The picture in one paragraph
A person opens an installed web app on their phone. The screens are Svelte, served as plain files by Cloudflare from Muscat. Totals and checks appear at once because the company's rules, written in Rust, run on the phone as WebAssembly. Every change goes through a small Cloudflare gateway to one Rust server on Google Cloud in Doha, which runs the same rules again as the final word and writes to PostgreSQL. In the same write it queues anything that must leave the system (a WhatsApp message, an email, a post, an e-invoice), and a background worker sends it. No screen ever talks to the database or to another company's service directly.
2. Five layers
| Layer | Where it runs | What is in it |
|---|---|---|
| People | Their phones; for the website and portal also tablets and desks | Staff app, client portal, website and store, driver link; later a small Android driver app |
| On the phone | The browser | Svelte screens, the shared interface package, the rules as WebAssembly, a service worker, a local database, the passkey |
| Edge | Cloudflare | DNS and TLS, static hosting, the gateway Worker, firewall and human check, Access, R2 file store |
| Server | Google Cloud, Doha | The API service, the rules library, the outbox worker and jobs, the document maker, PostgreSQL, Cloud Storage, Secret Manager, Scheduler and Tasks, logging |
| Outside | Other companies | WhatsApp, Gmail, text messages, the channel service, Jev, a language model, a document reader, container tracking, e-invoicing, search engines, the bank |
Each layer talks only to the one beside it. The outside layer is reached only by the server, and only through the outbox or a named adapter.
3. Three ideas that hold it together
One set of rules, three places. Money, VAT, pay, landed cost and validation are written once, in Rust. The server runs them as the final word. The phone runs the very same code as WebAssembly, so a total shown on the phone is the total the server will post. The website's calculators use it too. There is no second version to drift.
One shape of data, end to end. The database tables are checked against the server code when it is built. The server publishes a description of what it offers. The TypeScript the screens use is generated from that description. Rename a column and every place that used it fails to build until it is fixed.
One way out. Nothing leaves the system except through a "to send" table written in the same transaction as the change itself. A message is never sent for a change that did not save, and a saved change never loses its message. Each item is sent once, retried with growing gaps, and handed to a person if it keeps failing.
4. The languages
| Piece | What it is | What it does here | How | What it gains us |
|---|---|---|---|---|
| Rust | A programming language that catches whole classes of mistakes before the program runs | The server, the rules library, the document maker, the workers | One codebase compiled two ways: natively for the server, to WebAssembly for the phone | Money and tax logic that cannot silently go wrong; one language for the rules and the books; fast and small on the server |
| WebAssembly | A way to run compiled code inside a browser at near-native speed | Runs the rules on the phone; reads codes from the camera; shrinks photos; works out the bending maps for the top glass level | Built from the Rust code; loaded in a background worker so it never delays a tap | Totals that match the server exactly; calculators that work offline; no second copy of the rules in another language |
| TypeScript | JavaScript with types | All screen code | The types for the API are generated, not written by hand | A changed field breaks the build, not the customer |
| SQL | The database's language | Tables, constraints, migrations | Kept as numbered files in the repository | The structure of the data is reviewed like any other change |
What does not go in WebAssembly: the screens themselves, anything that touches the page, and anything that is only a few lines. It is used where the same answer is needed on both sides, or where the work is heavy.
5. On the phone
| Piece | What it is | What it does here | How | What it gains us |
|---|---|---|---|---|
| Svelte 5 | A framework for screens that compiles away to small, fast code | Every screen, component and movement | Components in four layers: primitives, components, patterns, screens | Small downloads and smooth movement on a mid-range phone |
| SvelteKit | The app framework around Svelte: pages, navigation, building | Three apps: apps/staff, apps/portal, apps/site |
Built to static files. The website is fully made in advance as pages | Nothing to run or patch for the screens; pages that search and AI crawlers can read without running scripts |
Shared interface package (packages/ui) |
One library of tokens, shell and components | Used by all three apps | A package in the same repository | The Halo, the Omnibar and every component look and behave the same on every staff screen. The website and portal have no Halo or Omnibar and use their own smaller set of parts (docs/SITE-DESIGN.md); they share the brand tokens only |
| Design tokens | Named values for colour, type, space, glass and motion | The only place a value is written | Kept in a standard format; turned into CSS when built | One change restyles everything; a check fails on stray values |
| Service worker | A small program the browser runs beside the app | Opens the app without signal; holds the outbox; shows notifications; updates the app | Caches the app's shell; never caches another person's data | Drivers and inspectors keep working in a yard with no signal |
| Local database (IndexedDB) | Storage inside the browser | Drafts, the outbox, recently seen records | Encrypted with a key the passkey unlocks; cleared on sign-out | Nothing typed is lost; nothing readable is left behind |
| Passkeys (WebAuthn, FIDO2) | Sign-in by the phone's own fingerprint or face, no password | Sign-in after the first code; confirming money actions | The phone keeps a private key; the server keeps only the public half | Cannot be phished or guessed; nothing to remember |
| Web Push | Notifications to an installed web app | Alerts for approvals, messages, late promises | Sent by the server; the text never contains amounts | People are told without the app open |
| Camera | The browser's access to the camera | Slab photos, bills, cards, delivery proof, codes | Shot once at full size; resized on the phone before upload | Small uploads on mobile data; originals kept for evidence |
| Web Share | The phone's own share sheet, from a web page | Passing a PDF to any other app, or to print | Offered where the phone supports it; otherwise a download | No app-specific sharing code |
| Maps (MapLibre and PMTiles) | Open-source maps drawn on the phone from one self-hosted file | Shipment map; delivery pin; the masked customer view | Tiles served from our own Cloudflare store | No per-view fees and no licence limits on tracking |
| Modern browser features | View transitions, scroll-driven animation, container queries | Screen changes, shared elements, the band shrinking | Used where supported, with a fade where not | Smooth movement without heavy script |
6. The edge (Cloudflare)
| Piece | What it does here | How | What it gains us |
|---|---|---|---|
| DNS, TLS, cache | The front door to every address | Always HTTPS; modern TLS only | Fast in Oman from the Muscat location |
| Static hosting | Serves the three apps and the website | Each release is a whole version; roll back in one step | No web servers to run |
| Gateway Worker | Checks the session, limits abuse, forwards to the server | Holds no business rules | Bad traffic never reaches the server |
| Firewall, bot settings, human check | Protects forms and sign-in; decides which crawlers may read | Search and answer crawlers allowed; abuse blocked | The site is found, and not flooded |
| Access | A lock in front of test and admin addresses | Sign-in before the page even loads | Unfinished work is never public |
| R2 | Holds map tiles, public images, brand files | Public files only | No download fees |
7. The server (Rust on Google Cloud)
| Piece | What it is | What it does here | How | What it gains us |
|---|---|---|---|---|
| API service (axum, on tokio) | A Rust web framework, and the engine that runs many requests at once | Every request from every app | Small handlers; shared steps for session, role, rate and logging | One fast service that costs nothing when idle |
Rules library (crates/core) |
Our own Rust library | Money in whole baisa (and each other currency in its own smallest unit, never added across currencies), conversion and rounding, VAT, posting rules, pay, landed cost, container maths, validation | Pure functions with no knowledge of the web or the database; heavily tested | The one place a figure is worked out |
| Database library (sqlx) | How the server talks to PostgreSQL | Every query | Queries are checked against the real database when the code is built | A query that does not match the tables cannot be shipped |
| API description (OpenAPI) | A published, machine-readable list of what the server offers | Generates the TypeScript client; documents the join with the channel service | Produced from the Rust code itself | Screens and server cannot disagree |
| Record stages | A fixed list of stages for each kind of record | Orders, shipments, invoices, cases, months | Written as Rust types, so an impossible step will not compile | A record cannot skip a step |
| Passkey library | The server half of passkeys | Registering and checking sign-ins | A maintained library, not our own cryptography | Fewer ways to get it wrong |
| Outbox worker | A background loop | Sends every message, post, e-invoice and notification | Reads the "to send" table; one-time keys; retries; gives up to a person | Nothing sent twice, nothing lost |
| Jobs | Scheduled and queued work | Reminders, timers, nightly checks that the books agree, polling, month-end | Cloud Scheduler and Tasks call the service | No always-on machine to pay for |
| Document maker | Turns a record into a fixed PDF | Quotes, proformas, invoices, packing lists, statements, payslips, letters | Templates plus the record; English and Arabic; stored once and never made again | One source for what the customer receives. See docs/DOCUMENTS.md |
| Adapters | One small module per outside service | WhatsApp, mail, text, Jev, language model, document reader, tracking, e-invoicing | Each hides the supplier behind our own shape | Any supplier can be replaced without touching the app |
8. Data
| Piece | What it does here | How | What it gains us |
|---|---|---|---|
| PostgreSQL (on Cloud SQL) | Every record, the ledger, the audit trail, the outbox | Real tables with constraints; one transaction per action; migrations in the repository | Durable, checkable, and understood by every accountant's tools |
| Append-only ledger | The books | The database itself refuses edits and deletions; corrections are new entries | An auditor can rely on it |
| Audit trail | Who did what, when, from which device | Written in the same transaction as the change | Disputes are settled by looking, not remembering |
| Full-text search | The Search screen | Built into the database; filtered by role on the server | No separate search service to run |
| Cloud Storage | Bills, slips, photos, PDFs, backups | Private; viewed through links that expire in minutes | Files are never public by accident |
| Backups | Recovery from a mistake or a failure | Daily copies to a second place; rewind to any minute; a restore test each quarter | A bad day is not a lost business |
Other Google Cloud pieces: Cloud Run (runs the server on demand, one service, Doha), Secret Manager (every key for an outside service; none in code or on a laptop), Scheduler and Tasks (clocks and queues), Logging and Monitoring (errors, uptime, alerts to the owner's phone), Artifact Registry (holds each built version of the server) and identity federation (GitHub deploys by proving who it is each time, so there is no downloadable key to steal).
9. Outside services
All are reached only by the server. Each sits behind an adapter and each has a stated
fallback (docs/system-flows.html, "outside services").
| Service | Used for | Status |
|---|---|---|
| WhatsApp Business Platform | Customer messages, sign-in codes, updates, documents | Official platform only |
| Gmail API | Shared mailboxes into the inbox; sending documents | Internal Workspace app |
| Text message provider | Fallback for sign-in codes | To choose |
| Channel service | Publishing to social platforms, comments, results | Separate build (docs/SOCIAL.md) |
| Jev | Typed choices: sort, route, score, rank | Behind an adapter; version pinned |
| Language model | Drafts, summaries, translation | To choose |
| Document reader | Bills, packing lists, cards from a photograph | To choose |
| Container tracking | Sea shipments | To choose |
| E-invoicing provider | Tax invoices to the Tax Authority | 2027; to choose from the accredited list |
| Search engines | IndexNow on publish; nightly reports | |
| Bank | Payment files out, statements in | By a person; files only |
| Payment provider | Card payment for samples | Not built first |
Proposed libraries, to confirm in Stage 0: axum, tokio, sqlx, serde, utoipa
(API description), webauthn-rs (passkeys), wasm-bindgen (WebAssembly bridge),
typst (document maker), rxing or similar (code reading), maplibre-gl and
pmtiles (maps), Playwright, Storybook, axe.
10. Building and checking
| Piece | What it does here | What it gains us |
|---|---|---|
| GitHub and Actions | Every change is checked, built and deployed on merge | Nothing ships unchecked or by hand |
| Rust tests | Unit tests, and thousands of generated cases for money maths | Rounding errors found by a machine, not a customer |
| Playwright | Opens every screen in real browsers, in both languages, sizes, themes and surface levels; compares with approved pictures | A change that breaks a screen is seen |
| Component workshop (Storybook) | Shows every component in every state | The owner can look at a part before it is in a screen |
| Accessibility and speed checks (axe, Lighthouse) | Run on every screen; budgets fail the build | Problems caught early. These find about half of accessibility problems; people test the rest |
| Linters | Rust, TypeScript, CSS and token rules | No stray values; no known-bad patterns |
| Dependency and secret scanning | Watches every library and every commit | A known weakness or a pasted key stops the line |
| AI coding assistants | Writing and reviewing code under the repository's rules | Speed, with the same gates as a person. Sign-in, money and permissions are always reviewed by a person |
From a change to the phones: a change on a branch → checks → Rust tests and both builds → screens type-checked against the generated client → browser tests → a person reviews → database migration, then the server to Cloud Run → apps to Cloudflare, the old version kept → each phone updates itself on next open.
11. What talks to what
A filled cell means the row calls or reads the column.
| Rules | Local store | Gateway | API | Database | Files | Secrets | Outbox | Outside | Document maker | |
|---|---|---|---|---|---|---|---|---|---|---|
| Screens | ● | ● | ● | |||||||
| Service worker | ● | ● | ||||||||
| Gateway Worker | ● | |||||||||
| API service | ● | ● | ● | ● | ● | |||||
| Outbox worker | ● | ● | ● | ● | ||||||
| Jobs | ● | ● | ● | ● | ● | |||||
| Document maker | ● | ● | ● | |||||||
| Channel service | ● | ● | ||||||||
| Outside services (webhooks) | ● |
The empty cells are rules: a screen never reaches the database, files or an outside service; the gateway holds no business logic; only the outbox worker and jobs send anything out.
12. How one action passes through everything
A customer accepts a quote in the portal.
- Svelte shows the quote; the total was worked out on the phone by the rules as WebAssembly.
- The customer confirms with their passkey.
- The request goes through Cloudflare to the gateway Worker, which checks the session.
- The API service checks the role, then asks the rules library for the total again. Its answer is the one that counts.
- One PostgreSQL transaction writes the order, locks the quote version, records the acceptance with the terms version, writes the audit line, and adds "make and send the proforma" to the outbox.
- The document maker produces the proforma PDF and stores it in Cloud Storage with its fingerprint.
- The outbox worker sends it by WhatsApp or Gmail, by the customer's choice.
- Web Push tells purchasing and the accountant; their Halo rings change.
- If the phone had no signal at step 3, the service worker kept the acceptance in the local database and sent it, once, when signal returned.
13. Why these, and what each costs
| Choice | What it gains | What it costs | Why still chosen |
|---|---|---|---|
| Rust for the server and the rules | Correct money logic; the same rules on the phone | Fewer developers know it; slower to write the first time | The books must be right, and the phone must agree with the server |
| The rules as WebAssembly | Instant, identical totals; offline calculators | A larger first download; a build step | Loaded in the background; only the rules, not the whole app |
| Svelte rather than React | Smaller and faster on a mid-range phone | A smaller community | The A55 is the limit, not the developer's laptop |
| Static apps on Cloudflare | Nothing to run, patch or scale | Pages cannot be made per request | The data comes from the API; the pages need not |
| One API service | Simple to run and to reason about | Everything is in one place | Right for a company this size; it can be split later |
| PostgreSQL, real tables | Durable, checkable, standard | Needs proper design and migrations | It replaces the old demo's document file inside the container |
| The outbox | Nothing sent twice or lost | Messages leave a moment later | A second's delay is invisible; a lost invoice is not |
| Two clouds | Each does what it is best at | Two accounts, two bills | Server and database together in Doha; the edge in Muscat |
| A web app, not a native app | One codebase; no app store; instant updates | Some phone features are missing, more so on iPhones | Staff use Android; the one missing piece (driver location) gets a small wrapper |
| Outside services behind adapters | Any can be replaced | A little more code | Several are not chosen yet, and Jev is early |
14. Not yet chosen
| Piece | What decides it | When |
|---|---|---|
| Document maker engine | Correct Arabic, archive-grade PDF, small on the server. Proposed: Typst | With docs/DOCUMENTS.md |
| Language model and document reader | Where they run; accuracy on our own papers; cost; the data protection law | Stage 0 |
| Text message provider | Reach and cost in Oman | Stage 0 |
| Container tracking | Coverage of the lines used from Sohar | Stage 3 |
| E-invoicing provider | The Tax Authority's accredited list | Before 2027 |
| Payment provider | Whether card payment is wanted at all | Later |
| Visit statistics | Privacy, and the cookie notice that follows | With the website |
Installing (added 11 November 2026)
Each of the three apps is installable from its own address. How people get it, the
browsers that can and cannot install, and how updates reach phones are in
docs/INSTALL.md.
Guardrails and controls (12 November 2026)
For building with several AI agents, and against the failures a red-team review
found: docs/CRITIQUE.md sections 6 and 7. In short: one generated contract; every
route refuses by default and has a permission test; money only in the rules core;
everything that leaves goes through the outbox, with signed once-only inbound events
and sending switched off in restores and test copies; a customer view of every
record with no supplier fields; the canary test for origin; limits the database
enforces; the audit trail chained and anchored daily; company-owned accounts with
two administrators and hardware keys.