Documentation

KinetiRx Documentation

Everything you need to install, configure, and operate KinetiRx for your pharmacy. Scroll through — every section is on this one page.

Introduction

KinetiRx (Pharma Care Pro) is a self-hosted pharmacy and small-clinic management system: point-of-sale billing with GST invoicing, medicine inventory with batch/expiry tracking, patient records and a due-khata credit ledger, OPD visit scheduling, daily sales/cash-drawer reconciliation, expense tracking, doctor outreach campaigns, and role-based employee accounts — all backed by a PostgreSQL database you own, on hardware you control.

The stack is a Go + Gin backend, a React 19 + Vite frontend served by nginx, and PostgreSQL — three containers, one Docker Compose file. Optional Google Gemini integration adds purchase-bill OCR scanning and a clinical assistant; without an API key, those features degrade to an offline fallback response instead of erroring.

Getting Started

Installation

Get KinetiRx running on a server in a few minutes. The install script is the only prerequisite you need — it checks for everything else.

One-command install (recommended)

Pick your OS and run the command — it checks for Docker (and git), installs either if missing, clones the repository, and runs the installer:

$
curl -fsSL https://raw.githubusercontent.com/Raktim94/KinetiRx/main/scripts/quickstart.sh | bash

Installs Docker Desktop and git via Homebrew if either is missing.

The installer generates deploy/.envwith a random Postgres password and JWT secret (only if one doesn’t already exist — safe to re-run), builds both images, starts the stack, and waits for it to report healthy.

Manual install

If you’d rather run the steps yourself:

git clone https://github.com/Raktim94/KinetiRx.git && cd KinetiRx
cp deploy/.env.example deploy/.env
# Edit deploy/.env and set POSTGRES_PASSWORD and JWT_SECRET —
# both required, compose refuses to start without them.
# Generate with: openssl rand -hex 32

docker compose -f deploy/docker-compose.yml --env-file deploy/.env up -d --build

Re-run the same docker compose ... up -d --build command any time — for example after git pull — to rebuild and restart. It never touches existing data.

First Login & Setup

No pre-set password, no demo credentials — you create the admin account yourself.

Open http://localhost:3080 and you’ll land on a Create Admin Accountscreen — enter a name and password and you’re logged straight in. This is a one-time step: the account is created with employee ID EMP-ADMIN-1, the admin role, and every permission, and the endpoint behind it (POST /api/auth/setup) permanently refuses to run again once any employee exists.

No password-reset flow yet: keep the password you choose somewhere safe. If you lose it, an existing admin can set a new one for the account from Employee Control (PUT /api/employees/:id).

Prefer to pre-set the password instead of using the screen? Set KINETIRX_ADMIN_PASSWORD in deploy/.env before first boot — whichever happens first (the env var on boot, or the signup screen on first visit) wins.

Using KinetiRx

POS & Billing

The Smart Pharmacy POS handles strip and loose dispensing, GST-inclusive pricing, per-item discounts, and mixed payment modes (cash, UPI, card, due, or a cash/due or cash/online split). Every completed sale is recorded to /api/sales(append-only — corrections are new sale records, not edits) and reflected immediately in the Dashboard’s revenue and cash-drawer figures.

The Daily Sales Registerreconciles the physical cash drawer against the day’s recorded sales: opening cash, denomination counts, the cash/UPI/card split, and the closing difference, stored as a singleton-per-day record via /api/daily-register.

Inventory & OCR

Medicine and lab-test stock live in one table (/api/medicines), distinguished by itemType. Each item tracks batch, expiry, rack location, distributor, GST rate, and stock count, and the Dashboard surfaces short-expiry and low-stock alerts automatically.

Inward OCR uses Gemini to read a photographed or scanned purchase bill and extract line items (name, batch, expiry, quantity, rate, MRP, GST) directly into inventory — set GEMINI_API_KEY to enable it. Without a key, POST /api/ocr/parse-bill returns a clearly-flagged fallback response instead of failing.

Patients & Due-Khata

Patient records (/api/patients) hold visit history, purchase history, and blood-test tracking. The due-khata credit ledger (/api/due-khata) is a separate resource for tracking what a patient owes and their payment history, with WhatsApp reminder links built into the UI.

OPD & Employees

OPD visits (/api/opd-visits) schedule outpatient appointments and follow-ups. Employees (/api/employees) are role-based staff accounts — any employee can hold a specific set of tab permissions, or the admin role, which implicitly passes every permission check. Creating, updating, or deleting an employee always requires admin specifically; permission grants alone are never enough for those three actions.

Administration

Environment Variables

Backend (also see backend/.env.example):

DATABASE_URL              # Postgres connection string — required
JWT_SECRET                # Signs access tokens, 16+ chars — required
KINETIRX_ADMIN_PASSWORD   # Optional — pre-seeds the admin account on first boot
GEMINI_API_KEY            # Optional — enables OCR + AI assistant
PORT                      # default 8080
GIN_MODE                  # default release
ALLOWED_ORIGINS           # default localhost:5173,localhost:3000

Deploy-level, in addition to the above (see deploy/.env.example):

POSTGRES_USER      # default kinetirx
POSTGRES_PASSWORD  # required
POSTGRES_DB        # default kinetirx
HTTP_PORT          # default 3080 — the app you visit
BACKEND_PORT       # default 8080 — direct backend access

CasaOS / ZimaOS

One-click install from a NAS App Store, using the same x-casaos v2 compose-extension spec CasaOS and ZimaOS both read.

The manifest lives at casaos/docker-compose.yml in the repository, along with the icon, thumbnail, and screenshots the store listing needs. It has been submitted to the official CasaOS App Store — pending maintainer review, not merged yet.

Install it right now, before the PR merges

CasaOS and ZimaOS can both install directly from a compose file URL. Go to App Store → + → Install a customized app (CasaOS) or Custom Install / Install via Compose (ZimaOS) and paste:

https://raw.githubusercontent.com/Raktim94/KinetiRx/main/casaos/docker-compose.yml

The Postgres password and JWT secret ship with real default values in the manifest — change both before using this beyond a local trial. KINETIRX_ADMIN_PASSWORD is left blank on purpose so the first-run signup screen shows, same as every other install path.

Backup & Data

All application data lives in Postgres — the named volume kinetirx_postgres_data for a plain Docker Compose deploy, or /DATA/AppData/kinetirx/postgreson a CasaOS/ZimaOS box (that path follows CasaOS’s own backup/restore convention, so its UI can back it up like any other app).

# Backup
docker exec deploy-postgres-1 pg_dump -U kinetirx kinetirx > kinetirx-backup.sql

# Restore
cat kinetirx-backup.sql | docker exec -i deploy-postgres-1 psql -U kinetirx kinetirx

Security Model

  • Passwords are bcrypt-hashed; nothing sensitive is logged.
  • JWT access tokens are the only auth mechanism — every non-public route is authorized server-side against the caller’s role/permissions, not just gated by the frontend UI.
  • Postgres is never published to the host in either the Docker Compose or CasaOS deployment path — only reachable from other containers on the same Compose network.
  • POST /api/auth/setup (first-run account creation) only ever succeeds once, and POST /api/auth/login returns an identical error for a wrong identifier or a wrong password, to prevent account enumeration.
Developer

Architecture

The frontend never talks to the backend cross-origin in production: nginx (inside the frontend container) reverse-proxies /api/*to the backend service over the internal Compose network, so the browser only ever sees one origin. The backend is the sole source of truth for authorization — every route (bar health-check, login, and setup) checks the caller’s JWT and role/permissions server-side, regardless of what the SPA renders.

Browser ──HTTP──▶ frontend (nginx, serves the SPA,
                     proxies /api/* same-origin)
                       │
                       ▼
                   backend (Go + Gin, JWT auth)
                       │
                       ▼
                   PostgreSQL

Optional: backend ──▶ Google Gemini (OCR + AI assistant,
                        falls back offline if GEMINI_API_KEY is unset)

API Reference

Every resource below follows the same REST shape — GET /api/<resource> (list), GET /api/<resource>/:id, POST, PUT /:id, DELETE /:id — gated by the permission shown, or an admin role which passes every check. The full request/response shapes for every field live in backend/API.md in the repository.

ResourceBase pathPermissionPurpose
Medicines / Inventory/api/medicinesinventoryMedicine & lab-test stock, batch/expiry, distributor tracking
Patients/api/patientspatientsPatient records, visit history, blood-test tracking
Due-Khata/api/due-khatadue-khataPatient credit ledger — dues and payment history
Sales/api/salesdaily-sales (read) / pos (create)POS invoice history — append-only, no update/delete
Expenses/api/expensesexpensesDay-to-day expense logging by category
Needed Medicines/api/needed-medsmedicine-ordersMedicines needed/ordered from distributors
OPD Visits/api/opd-visitsopdOutpatient visit scheduling and follow-ups
Distributors/api/distributorsinventoryDistributor directory
Marketing Campaigns/api/marketing-campaignsbusiness-devDoctor outreach campaigns
Worksheet Tasks/api/worksheet-tasksbusiness-devBusiness-development task list
Employees/api/employeesemployee-mgmt (read) / admin (write)Staff accounts, roles, and per-tab permissions

Singleton and non-CRUD endpoints

  • GET /api/health — no auth, liveness check
  • GET /api/auth/setup-status, POST /api/auth/setup — no auth, first-run account creation
  • POST /api/auth/login, GET /api/auth/me — session
  • GET / PUT /api/daily-register — one row, cash-drawer closing
  • GET / PUT /api/invoice-config — one row, invoice letterhead/GST/DL settings
  • POST /api/ocr/parse-bill, POST /api/ai/ask — Gemini-backed, degrade to a fallback response when GEMINI_API_KEY is unset

MCP Server

mcp-server/ is a Model Context Protocol server that lets an MCP-aware AI assistant (Claude Desktop, Claude Code) operate a running KinetiRx instance through tool calls — inventory lookup, patient/due-khata lookup, daily register, recording sales/expenses, and more. It speaks MCP over stdio, not a network port, so it’s gated behind the mcp Compose profile rather than part of the always-on stack:

docker compose -f deploy/docker-compose.yml --env-file deploy/.env \
  --profile mcp run --rm -T mcp-server

Set KINETIRX_MCP_USERNAME / KINETIRX_MCP_PASSWORD in deploy/.envto a real employee’s credentials first — defaults to the seeded admin ID. Full tool list in mcp-server/README.md.

Local Development

Requires Go 1.26+, Node.js, and a local Postgres instance (or just run the postgres service from the compose file).

Backend

cd backend
cp .env.example .env   # set DATABASE_URL, JWT_SECRET
go run ./cmd/server

Runs on :8080 by default; migrations in backend/migrations/ run automatically on start.

Frontend

cd frontend
npm install
npm run dev

Vite dev server at http://localhost:5173. Other scripts: npm run build, npm run lint (tsc --noEmit).