craftivus-pos is a Tauri v2 desktop application (Rust backend, React + TypeScript + Vite frontend). This is what runs on the actual till hardware.
craftivus_core backend to activate against (see Installation: Backend), with at least one POS Device (Pending) and one POS Cashier (Pending, at least one Manager) already created.git clone <craftivus-pos-repo-url>
cd pos
npm install
The API base URL is currently a constant in src/lib/api.ts:
const API_BASE_URL = "http://localhost:8000"; // or wherever your bench serves the site
This is a known placeholder — production deployments will need this captured per-shop during onboarding rather than hardcoded, but for now, edit it to match your dev bench.
npm run tauri dev
This starts the Vite dev server and launches the native Tauri window. On first run it compiles the Rust side, which takes a while the first time and is fast on every subsequent run.
On first launch, the till has no identity yet. It needs the activation_code from a POS Device record you created in Desk (see Installation: Backend):
craftivus_core.api.device.activate, which issues it a dedicated Website User plus scoped api_key/api_secret.The activated device still has no cashier logged in. Use a setup_code from a POS Cashier (Pending) record:
craftivus_core.api.cashier.provision, which links that cashier to this device.| What | Where | How it's protected |
|---|---|---|
Device api_key/api_secret |
Stronghold vault | OS-keychain-backed encryption |
| Cashier PINs | SQLite (tauri-plugin-sql) |
Argon2 hash, never the raw PIN |
| Catalog cache (items, prices, stock, images) | SQLite | Plaintext — it's a cache of already-public-to-the-till data |
| Offline sale queue | SQLite | Plaintext locally; submitted over HTTPS with the device's own credentials |
| Printer settings | SQLite | Plaintext (connection details, not credentials) |
SQLite schema is versioned as an ordered list of migrations in src-tauri/src/lib.rs. New migrations must be appended in ascending version order — tauri-plugin-sql applies them by position, not by inspecting a version number, and an out-of-order migration will silently corrupt the schema on upgrade.
See Architecture: why the till uses tauri-plugin-http. If you're adding a new API call from the till, use the existing callMethod-style helpers in src/lib/api.ts — they already route through tauri-plugin-http. Do not reach for the browser's native fetch() from till code; it will hit CORS and simply fail.
npm run tauri build
Produces a platform-native installer/bundle under src-tauri/target/release/bundle/. Signed update channels (Pilot vs. Stable) are on the roadmap but not yet implemented — see the Architecture page's subscription section for where this fits once it exists.
| Symptom | Cause | Fix |
|---|---|---|
Every API call fails, but curl against the same URL works fine |
Using browser fetch() instead of the Rust-side HTTP client |
Route the call through tauri-plugin-http via the existing helpers in src/lib/api.ts |
| A sale submitted just after midnight gets rejected for "insufficient stock" even though there's plenty of stock | posting_date built from UTC while posting_time was built from local time (or vice versa) — silently backdates the sale for any till east of UTC |
Derive both date and time from the same local Date object |
| App won't start after a schema change | A new SQLite migration was inserted out of order, or an existing one was edited in place | Migrations are append-only, ascending version order, never edited retroactively |
| PIN pad accepts on-screen taps but not physical keyboard input | An input was left readOnly |
Cashier PIN entry needs a real controlled input, not a display-only field wired to an on-screen pad |