---
title: Repository overview
description: How the PharmaFlow monorepo is split across server features, client apps, and shared packages, and why.
---

PharmaFlow is a bun workspaces monorepo with turbo task running. Every app lives under `apps/*` and every shared library under `packages/*`.

### Layout

| Path                 | Package                     | What lives there                                                                                                        |
| -------------------- | --------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `apps/server`        | `@pharmaflow/server`        | Hono API, one folder per feature in `src/features`                                                                       |
| `apps/register`      | `@pharmaflow/register`      | Next.js point of sale register, English only                                                                             |
| `apps/console`       | Rust and Tauri              | Offline desktop shell that hosts the register UI and owns the local mirror, outbox, and sync engine                       |
| `apps/marketing`     | `@pharmaflow/marketing`     | Public marketing site with changelog, terms, privacy, and security pages                                                 |
| `apps/knowledgebase` | `@pharmaflow/knowledgebase` | This Blume documentation site plus the generated `openapi.json`                                                          |
| `packages/database`  | `@pharmaflow/database`      | Drizzle schema under `schema/` combined into `schema.sql`, drizzle migrations, translator, multitenancy, and seed scripts |
| `packages/shared`    | `@pharmaflow/shared`        | Money, pagination, and response helpers, plus the minor unit currency helpers in `src/currency.ts`                        |

`apps/console` has no `package.json`. It is a Rust and Tauri crate (`apps/console/Cargo.toml`, `apps/console/tauri.conf.json`) that wraps the register build and adds the offline engine described next.

### What the console adds

`apps/console` is not a second UI. Its Rust layer owns offline trading:

- **Local mirror** - `apps/console/src/db.rs` creates the `pharmaflow.db` file in the app data dir from `apps/console/sql/local-schema.sql`, keeps `_device`, `_sync_state`, `_outbox`, and `_offline_sessions` as internal tables, and reconciles missing mirror columns on launch.
- **Mirror schema pipeline** - `apps/console/scripts/build-mirror-schema.ts` regenerates `apps/console/sql/local-schema.sql` from `packages/database/schema.sql` through the overlays in `apps/console/scripts/mirror-overlays.ts` and the manifest `apps/console/sql/mirror-overlays.json`. `packages/database` exposes the same run as its `schema:build` and `schema:check` scripts.
- **Outbox** - `apps/console/src/outbox/queue.rs` freezes `client_mutation_id`, `tax_policy`, and the currency fields at queue time, deducts mirror stock earliest expiry first, and records the draw in `stock_json`.
- **Sync engine** - `apps/console/src/sync/push.rs` batches 50 outbox rows to `POST /api/v1/sync/push`, `apps/console/src/sync/pull.rs` follows the `since_rev` cursor, `apps/console/src/sync/bootstrap.rs` loads the snapshot, and `apps/console/src/sync/apply.rs` (`reconcile_accepted_sale`) rekeys an accepted sale to the server id.
- **Offline identity** - `apps/console/src/identity/offline_auth.rs` verifies a stored password hash and honours the `offline_expires_at` window, so a till keeps selling while disconnected.

### The server feature pattern

Each domain is a folder under `apps/server/src/features` with a `route.ts` that mounts one router. Shared concerns live under `apps/server/src/core`: `core/auth/` for JWT (`token.ts`), passwords (`password.ts`), the middleware (`middleware.ts`), and login throttling (`login-attempts.ts`), `core/config/env.ts` for environment reads, `core/database/` for backend selection, transactions, and tenant scoping (`db.ts`, `tx.ts`, `tenancy.ts`), `core/common/` for HTTP helpers, validation, time, and sync visibility (`http.ts`, `validate.ts`, `time.ts`, `visibility.ts`), `core/audit/` for the audit log and outbox (`audit.ts`, `outbox.ts`), `core/logging/logger.ts`, `core/openapi/` (`document.ts`, `components.ts`), and `core/storage/storage.ts` for S3.

The mount table lives in `apps/server/src/app.ts`: it composes the Hono instance and mounts every feature under `apiV1`, for example `apiV1.route("/sales", saleRoutes)`, then serves the whole router at `/api/v1`. The Bun entry `apps/server/src/index.ts` only builds the default exported server object with `port` and `fetch` and runs `ensureLoginAttemptsTable()` from `apps/server/src/core/auth/login-attempts.ts` on startup.

### Where to look next

- Start with [Architecture](/architecture) for the mental model.
- Go to [Sales](/sales) and [Inventory](/inventory) for the two core domains.
- Read [Offline sync](/architecture/offline-sync) before you touch the console.
- Keep the [API reference](/api) open beside the code; every route file maps to one tag there.
