Running the Platform
Install prerequisites, configure env, seed the demo, boot server plus register plus docs, and verify with health and login.
You go from a clean machine to a working pharmacy in about ten minutes. You need Bun, pinned to 1.4.3 by the packageManager field in package.json, and Git. The server runs on SQLite through DATABASE_PATH by default, and it switches to PostgreSQL only when you set DATABASE_URL.
Check your prerequisites
You confirm the toolchain before cloning so version errors surface early. The pinned bun version lives in the packageManager field of the root package.json; a mismatched runtime is the most common cause of lockfile churn.
bun --versionIf it does not print 1.4.3, install that version before you continue.
Clone and install
You clone the repo, check out the branch you are working on, and install every workspace at once.
git clone https://github.com/vernonthedev/pharmaflow.git
cd pharmaflow
bun installConfigure the server environment
You copy apps/server/.env.example to apps/server/.env and fill in your values. Bun loads .env from the working directory when apps/server/src/index.ts boots, and the full key list in the example file is read through readEnv in apps/server/src/core/config/env.ts. JWT_SECRET must be at least 32 characters or every boot fails fast.
cp apps/server/.env.example apps/server/.envThe server defaults to SQLite: leave DATABASE_PATH unset and apps/server/src/core/database/db.ts falls back to resolveDatabaseFile in packages/database/src/paths.ts, which points at a database file under packages/database/data/. Set an absolute DATABASE_PATH to use another SQLite file, or point DATABASE_URL at a Postgres instance instead. PORT serves the API, and ALLOWED_ORIGINS already lists the register, the marketing app, and the Tauri console in the example file.
JWT_SECRET=<at-least-32-characters-long-random-string>
PORT=8000
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:3001,tauri://localhost,https://tauri.localhost,http://tauri.localhostSeed the demo data
You create the demo branches, users, medicines, batches, and the DEMO10 coupon with the seed script in packages/database.
bun --cwd packages/database seedYou now sign in as admin@demo.pharmaflow, pharm1@demo.pharmaflow, or pharm2@demo.pharmaflow, all with password Demo@1234. You reset any time by running the same command again.
Boot server, register, and docs
You run each app in its own terminal. The API serves port 8000, the register serves port 3000, and the docs print their own URL.
bun run dev:serverbun run --cwd apps/register devbun run --cwd apps/knowledgebase devVerify health and login
You hit GET /health in apps/server/src/index.ts for the smoke check, then you log in and store a token for the rest of your session. Router routes mount under the /api/v1 prefix that apps/server/src/app.ts applies with app.route("/api/v1", apiV1), so auth sits at /api/v1/auth.
curl http://localhost:8000/healthTOKEN=$(curl -s -X POST http://localhost:8000/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"admin@demo.pharmaflow","password":"Demo@1234"}' \
| python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
curl http://localhost:8000/api/v1/auth/me -H "Authorization: Bearer $TOKEN"Start the desktop console
You run the Tauri v2 console wrapping the register UI from the repository root:
bunx -p @tauri-apps/cli tauri devThe shell config in apps/console/tauri.conf.json points devUrl at http://localhost:3000 and its beforeDevCommand starts the register dev server for you, so you can skip the separate register terminal when you use the console. The Rust backend lives in apps/console/src.
Next steps
Walk your first counter sale in /guides/selling-end-to-end, connect an offline device in /guides/offline-first-flow, and learn the auth model in /platform/authentication-and-users.