SecureMed is a privacy-first medical record vault with an AI assistant on top. Patients can upload reports, keep the original file encrypted for personal access, create an anonymized AI-safe copy for search and analysis, and selectively share access with doctors using temporary session codes.
The project is split into:
- A React frontend for registration, login, OCR, anonymization, encryption, record sharing, and chat
- A FastAPI backend for auth, record storage, doctor/patient access control, and AI query orchestration
- A Celery worker that decrypts the anonymized copy and indexes it into ChromaDB
- PostgreSQL for user/report metadata
- Redis for Celery and short-lived doctor sharing sessions
This app uses a dual-path document flow:
- The original report is encrypted in the browser with a patient-controlled AES key and stored for later viewing.
- A separate anonymized text version is encrypted for the server and sent to the worker.
- The worker decrypts only the anonymized copy, chunks it, embeds it, and stores it in ChromaDB for retrieval.
- The AI assistant answers questions from that indexed knowledge base instead of directly exposing raw private files.
In plain English: the vault copy stays encrypted for the patient, while the AI works from a stripped-down anonymized version.
- Create an encrypted vault account
- Generate an RSA key pair in the browser
- Keep the private key encrypted with the account password
- Upload scanned medical files
- Run OCR locally in the browser with
tesseract.js - Review and edit anonymized text before upload
- Store the original file in encrypted form
- Share report access with a doctor using a 6-digit temporary session code
- Ask AI questions about their own uploaded records
- Create a doctor account
- Generate a temporary share code for patients
- Receive access only to reports explicitly shared by patients
- Decrypt and view shared files in the browser
- Ask AI questions about a selected patient
- Use global research mode against the anonymized knowledge base
frontend/
src/pages/ UI flows: login, register, upload, dashboard, chat
src/lib/crypto.js Browser-side crypto helpers
src/ner_service.js Local anonymization pipeline
backend/
app/main.py FastAPI app and API routes
app/routers/auth.py Register/login endpoints
app/tasks.py Celery worker for decrypt + index
app/retrieval.py ChromaDB + embedding logic
app/agent_runner.py LangChain-based AI query runner
app/models.py SQLAlchemy models
generate_keys.py Generates backend RSA key pair
- Frontend: React, Vite, Tailwind CSS, Zustand, Framer Motion
- Browser AI/processing:
tesseract.js,@xenova/transformers,compromise - Backend: FastAPI, SQLAlchemy, Celery
- Storage: PostgreSQL, ChromaDB
- Queue/session store: Redis
- AI/RAG: Sentence Transformers, LangChain, Gemini via
langchain-google-genai
You will need:
- Node.js 18+ and npm
- Python 3.10+
- Docker Desktop or local Redis/PostgreSQL installs
- A Google API key if you want the AI chat to work
Recommended local ports used by the current code:
- Frontend:
5173 - Backend API:
8000 - Redis:
6379 - PostgreSQL:
5433on your machine, mapped to container5432
Follow these steps in order. Open each long-running service in its own terminal.
From the project root:
docker compose up -dThis uses the included docker-compose.yml and starts Redis on localhost:6379.
The backend defaults to:
DATABASE_URL=postgresql://admin:secret@localhost:5433/med_privacy_dbTo match that without changing code, run:
docker run --name med-postgres `
-e POSTGRES_USER=admin `
-e POSTGRES_PASSWORD=secret `
-e POSTGRES_DB=med_privacy_db `
-p 5433:5432 `
-d postgresIf you already have PostgreSQL running somewhere else, that is fine too. Just update DATABASE_URL in backend/.env.
Create backend/.env with values like these:
DATABASE_URL=postgresql://admin:secret@localhost:5433/med_privacy_db
SECRET_KEY=change_this_to_a_long_random_secret
CELERY_BROKER_URL=redis://localhost:6379/0
GOOGLE_API_KEY=your-google-api-key
SERVER_PRIV_KEY_PATH=docs/server_privkey.pem
SERVER_PUB_KEY_PATH=docs/server_pubkey.pem
LLM_MODEL=gemini-2.5-flashNotes:
GOOGLE_API_KEYis required for the chat assistant.- If you only want to test auth/upload/share flows, you can leave
GOOGLE_API_KEYempty, but chat will fail. - The frontend already defaults to
http://localhost:8000, so a frontend env file is optional.
From the backend folder:
python generate_keys.pyThis creates:
backend/docs/server_privkey.pembackend/docs/server_pubkey.pem
From the backend folder:
python -m venv venv
venv\Scripts\activate
pip install -r requirements.txtIf you are on macOS/Linux:
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txtFrom the backend folder:
celery -A app.tasks.celery_app worker --loglevel=info --pool=soloKeep this terminal open.
From the backend folder:
uvicorn app.main:app --reload --port 8000Once it starts, the API is available at:
http://localhost:8000- Swagger docs:
http://localhost:8000/docs
From the frontend folder:
npm install
npm run devOpen the URL shown by Vite, usually:
http://localhost:5173
If you want the quickest happy-path test, use this order:
- Register a patient account.
- Log in with the patient account.
- Upload a medical file.
- Wait for OCR, review the anonymized text, then complete upload.
- Register a separate doctor account in another browser or incognito window.
- Log in as the doctor and generate a 6-digit session code.
- Go back to the patient dashboard, open report sharing, and enter that code.
- Return to the doctor dashboard and open the shared file.
- Use chat as the patient or doctor to query indexed medical content.
The upload flow is easier to understand if you think of it as four stages:
- The file is selected in the browser.
- OCR extracts text locally.
- The text is anonymized locally and shown for review.
- Two encrypted payloads are created:
- The original file is encrypted for patient access.
- The anonymized text is encrypted for the backend worker and AI indexing.
That means the AI pipeline never needs the original unredacted document to answer text questions.
DATABASE_URL: PostgreSQL connection stringSECRET_KEY: JWT signing keyCELERY_BROKER_URL: Redis URL for Celery and session codesGOOGLE_API_KEY: required for Gemini-backed chatSERVER_PRIV_KEY_PATH: backend private key pathSERVER_PUB_KEY_PATH: backend public key pathLLM_MODEL: optional, defaults togemini-2.5-flash
VITE_API_URL: optional, defaults tohttp://localhost:8000
If you want to set it explicitly, create frontend/.env.local:
VITE_API_URL=http://localhost:8000- The backend should be started from inside the
backendfolder. That is where the.envfile anddocs/key paths are expected. - The first OCR/anonymization run can feel slow because browser-side models may need to load.
- The first indexing/search run can also be slow because sentence-transformer embeddings are loaded on demand.
- The doctor and patient private keys are kept in browser memory after login. If the UI says the key is missing, log in again.
docker-compose.ymlonly starts Redis. PostgreSQL still needs to be started separately unless you extend the compose file.
Check that:
- PostgreSQL is running
- The host port matches the
DATABASE_URL - You used
5433:5432if you want to keep the current default config unchanged
Check that:
- The Celery worker is running
- Redis is running
- The worker was able to decrypt and index the anonymized text
Most likely causes:
GOOGLE_API_KEYis missing- The selected LLM model is unavailable
- The document has not been indexed yet
That is expected the first time:
- The frontend may download the browser anonymization model
- The backend may load the embedding model for Chroma indexing
- The AI chat is the most complete part of the assistant flow for text questions.
- The agent prompt references chart/vitals behavior, but the current codebase is mainly wired for text retrieval and question answering.
- There are older backend folders in the repo, but the active app entrypoint is
backend/app/main.py.
docker compose up -dcd backend
celery -A app.tasks.celery_app worker --loglevel=info --pool=solocd backend
uvicorn app.main:app --reload --port 8000cd frontend
npm run devSecureMed is a full-stack prototype for privacy-aware medical record handling. It combines browser-side encryption, OCR, anonymization, doctor-patient sharing, vector search, and LLM-based querying into one workflow that is surprisingly practical to demo locally once Redis, Postgres, the backend keys, and the worker are all running.