Khalid Abdi 6213da9477 feat: admin-provisioned staff, username login & role-based access
Replace the email-invitation flow with admin-provisioned staff accounts and
add role-based access that changes what each member sees.

Backend:
- Enable Better Auth `username` plugin (staff sign in by username); regenerate
  auth schema (+ username/displayUsername on user) and migration 0007.
- Add `doctor` and `reception` roles to the access-control RBAC. `reception` is
  scoped to scheduling + registration (no `prescription` statement).
- New `/api/staff` route: POST creates a user (auth.api.signUpEmail) and adds
  them to the active clinic (auth.api.addMember); GET lists members + usernames.
  Gated by requirePermission({ member: ["create"] }).
- Redact clinical PHI for the reception role in the patients service (read,
  create and update) so demographics-only is enforced server-side.

Frontend:
- usernameClient + Email|Username tabs on the login form.
- lib/roles.ts: useActiveRole + Better-Auth-permission-driven nav visibility,
  default landing, and a route guard (reception -> /appointments, blocked from
  clinical routes). Applied to the sidebar, command palette and auth guard.
- Care team page now provisions staff via a two-step Add-team-member dialog
  (details -> username/password) hitting /api/staff; removes the email-invite
  and pending-invitation UI. New members are contactable from Messages
  automatically (they become org members).
- Hide clinical sections of the patient form and the admin-only settings tabs
  for non-clinical/non-admin roles.

All permission management stays in Better Auth (per the better-auth skills now
referenced in backend/CLAUDE.md).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 19:12:07 +03:00

temetro

temetro is an open-source clinical tool that acts as an AI middleman between clinicians and patient data. Clinicians use a natural-language AI chat to retrieve and organize patient information, displayed as rich record cards.

Its distinguishing idea is a patient-owned data model: instead of (or alongside) living in a doctor's own database, a patient's record can be stored on the patient's own device. When a clinician adds or changes data, they sign it (blockchain-style); the change is written to the patient's record and cannot be modified until the patient approves it through a companion app. temetro can also read existing patient databases and present them in the same organized card UI.

Status. The backend is built — a TypeScript + Express + Postgres API (Drizzle ORM) with authentication and multi-tenant clinics via Better Auth, an org-scoped patient records API, plus appointments, prescriptions, tasks, doctor's notes, analytics, an activity audit log, real-time staff messaging (Socket.io), and notifications. The frontend chat is wired to it (real auth, route protection, clinic switching, live patient data).

Still vision, not built: the patient companion app and the blockchain-style signing / patient-owned storage / approval flow, and the AI chat replies themselves (currently mock — no LLM call yet; a /chat endpoint is the next planned step).

Monorepo layout

This repository is a monorepo containing two independent apps that live side by side (each with its own package.json / node_modules and its own CLAUDE.md):

The marketing landing page lives in its own separate repository (temetro-landing), not here.

Docker Compose builds and runs Postgres, the backend, and the frontend together. From the backend/ directory (the Compose file builds the sibling ../frontend):

cd backend
cp .env.example .env

# generate a strong auth secret and paste it into BETTER_AUTH_SECRET in .env:
openssl rand -base64 32

docker compose up --build

Then open:

Database migrations are applied automatically on backend container start.

Port conflict? If another Postgres already holds host port 5432, set POSTGRES_PORT (e.g. 5433) in backend/.env. The app still talks to Postgres internally on db:5432; only the published host port changes.

Run just the API + database with docker compose up db backend. To browse the database with Adminer: docker compose --profile tools up adminerhttp://localhost:8080.

Run locally without Docker

Run each app in its own terminal (you'll need a local Postgres reachable from DATABASE_URL):

# backend
cd backend
npm install
cp .env.example .env        # point DATABASE_URL at your local Postgres
npm run db:migrate          # apply migrations
npm run dev                 # http://localhost:4000

# frontend (second terminal)
cd frontend
npm install
npm run dev                 # http://localhost:3000

The frontend reads NEXT_PUBLIC_API_URL (default http://localhost:4000) to reach the backend.

If SMTP_HOST is unset, verification / reset / invitation emails are printed to the backend console instead of being sent — no email setup is needed for local development.

Tech stack

  • Frontend: Next.js 16 (App Router) · React 19 · TypeScript · Tailwind CSS v4 · i18next · Socket.io client.
  • Backend: Node ≥ 20 · TypeScript (ESM) · Express 5 · Postgres · Drizzle ORM · Better Auth (email/password + organizations/RBAC) · Socket.io.

License

MIT.

S
Description
An open-source AI middleman between clinicians and patient data.
Readme MIT 6.1 MiB
Languages
TypeScript 97.4%
JavaScript 2%
CSS 0.4%
Dockerfile 0.1%