Dinar POS · reference

How Dinar POS is built

One Rust core owns every rule and every query. The desktop talks to it over HTTP on loopback, and the phone will use the same routes in M6, so no screen has to know whether the core runs next to it or on another machine.

Desktop UI React 19, TanStack Router and Query, i18n fr / en / ar apps/desktop/src runs in a Tauri 2 window or in Vite for dev Phone (M6) Expo thin client, same routes apps/mobile packages/shared DTO types generated by ts-rs, fetch client with runtime guards: integers only, safe range just types-check diffs the commit HTTP, loopback 127.0.0.1 types cross once, generated crates/api axum 0.8 · GET/POST /products · GET /categories · /health CORS names its origins · one error envelope {code, message} · no auth until M4 Tauri embeds it in-process bind(0), stopped on window exit CoreError mapped to a status, never re-derived crates/core money Money, Bps, pct, totals, stamp, words services products, categories, settings: the rules repos the only diesel queries, every one scoped by shop_id SQLite, bundled diesel, embedded migrations STRICT, one file per shop fixtures/money/*.json: the same cases run under cargo test and under vitest against design/shared/money.js, so the mockups and the core cannot drift.
rules live herestoragelater milestone

The six rules the code is held to

  1. Money is integer centimes with checked arithmetic. No float near a total.
  2. One HTTP contract: the desktop and the phone are the same caller over the same routes.
  3. Every query is scoped by shop_id; the API answers for one shop.
  4. Errors are mapped, never re-derived: one enum per layer, a translation key per code, no Rust or SQL text on a screen.
  5. No unwrap or expect in shipped Rust; no as assertions in TypeScript.
  6. Types cross the boundary once, generated; a stale commit fails the gate.