{"id":49752514,"url":"https://github.com/giovi321/asclepius","last_synced_at":"2026-05-28T23:01:38.057Z","repository":{"id":355920465,"uuid":"1230229576","full_name":"giovi321/asclepius","owner":"giovi321","description":"Self-hosted medical records manager: OCR + LLM extraction, RAG search, doctor-share links, DICOM viewer.","archived":false,"fork":false,"pushed_at":"2026-05-28T22:18:23.000Z","size":2973,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-28T22:18:37.817Z","etag":null,"topics":["dicom","docker","fastapi","llm","medical-records","ocr","rag","react","self-hosted"],"latest_commit_sha":null,"homepage":"https://giovi321.github.io/asclepius/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/giovi321.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-05T19:57:12.000Z","updated_at":"2026-05-28T22:18:26.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/giovi321/asclepius","commit_stats":null,"previous_names":["giovi321/asclepius"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/giovi321/asclepius","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giovi321%2Fasclepius","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giovi321%2Fasclepius/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giovi321%2Fasclepius/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giovi321%2Fasclepius/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/giovi321","download_url":"https://codeload.github.com/giovi321/asclepius/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giovi321%2Fasclepius/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33629560,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-28T02:00:06.440Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dicom","docker","fastapi","llm","medical-records","ocr","rag","react","self-hosted"],"created_at":"2026-05-10T13:31:22.858Z","updated_at":"2026-05-28T23:01:38.041Z","avatar_url":"https://github.com/giovi321.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/public/assets/logo.svg\" alt=\"Asclepius\" width=\"120\" /\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eAsclepius\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/giovi321/asclepius/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://github.com/giovi321/asclepius/actions/workflows/ci.yml/badge.svg\" alt=\"CI\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/giovi321/asclepius/actions/workflows/docker.yml\"\u003e\u003cimg src=\"https://github.com/giovi321/asclepius/actions/workflows/docker.yml/badge.svg\" alt=\"Docker\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/giovi321/asclepius/actions/workflows/docs.yml\"\u003e\u003cimg src=\"https://github.com/giovi321/asclepius/actions/workflows/docs.yml/badge.svg\" alt=\"Docs\"\u003e\u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/license-MIT-blue.svg\" alt=\"License: MIT\"\u003e\u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/python-3.13%2B-blue\" alt=\"Python 3.13+\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/node-20%2B-green\" alt=\"Node 20+\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://giovi321.github.io/asclepius/\"\u003e\u003cimg src=\"https://img.shields.io/badge/Read_the_docs-2563eb?style=for-the-badge\u0026logo=readthedocs\u0026logoColor=white\" alt=\"Read the documentation\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003e **Personal-use software.** Not HIPAA / GDPR certified. Do not expose to the public internet without OIDC + TLS. See [Before you self-host](#before-you-self-host) below.\n\nIf you've ever spent twenty minutes hunting for a blood test from three years ago, this is for you.\n\nAsclepius takes the pile of PDFs, scans, discharge letters, and phone photos of paper reports that builds up over a lifetime and turns it into something you can actually search. Drop a file into the inbox folder. The app runs OCR, asks an LLM to pull out the dates, diagnoses, lab values, and medications, and files everything under the right person and year.\n\nAfter that, you can plot a hemoglobin trend across a decade, scroll a timeline of every appointment, or just ask plain-language questions about your history. When a specialist needs to look at part of your file, you can hand them a one-time link plus a 6-digit code instead of emailing PDFs around.\n\nIt runs in one Docker container on your own machine, with an optional sibling container that exposes only the doctor-share surface to the internet. Your records stay there unless you tell them otherwise.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/public/assets/diagrams/journey.svg\" alt=\"From a stack of paper to one searchable archive\" width=\"900\" /\u003e\n\u003c/p\u003e\n\n## Quick start\n\n```bash\ngit clone https://github.com/giovi321/asclepius.git\ncd asclepius\ndocker compose up -d\n```\n\nOpen \u003chttp://localhost:8070\u003e. A first-launch wizard creates your admin account and your first patient profile, then point the **Settings** page at one LLM provider (Ollama, vLLM, Claude, or OpenAI) and you're ready to drop files into the inbox. Your saved settings are persisted to `./data/settings.yaml`.\n\nYou'll need an LLM somewhere, a local Ollama instance, a vLLM server, or a Claude or OpenAI API key. The container is small on purpose; the model lives elsewhere.\n\n## What it does\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/public/assets/diagrams/hero.svg\" alt=\"Asclepius pipeline overview\" width=\"900\" /\u003e\n\u003c/p\u003e\n\n- Drops PDFs, images, DICOM files, and DICOM zip bundles (the format every imaging CD ships in) into the pipeline. OCR can be Tesseract, Google Vision, or any LLM with vision; extraction can be Ollama, vLLM, Claude, or OpenAI.\n- Long documents (over 5 pages) get split into logical sections, lab block, clinical notes, discharge summary, and each section is extracted on its own.\n- Lab results are normalized across visits and languages. A \"ferritin\" in Italian, a typo'd \"Ferrytin\" in a German report, and \"ferritin (s)\" with weird units all end up on the same trend chart.\n- A built-in DICOM viewer handles MRI and CT studies with windowing, zoom, and pan, and can attach the radiology PDF report next to the frames so you read the doctor's narrative and the images in the same place.\n- The timeline view groups documents into medical events, a diagnosis, a course of treatment, a surgery and the follow-ups around it.\n- Full-text search (SQLite FTS5) and a small RAG chat layer let you ask things like _when did I last get a tetanus shot?_\n- Multi-patient with role-based access, so you can keep records for a partner or your kids in the same install with separate access.\n- When you correct an extracted field, the change is captured and used as a few-shot example for similar documents later.\n- Every provider list (OCR, LLM, Vision-LLM) supports priority fallback, so a flaky endpoint hands off to the next one without you noticing.\n\n## Sharing records with an outside doctor\n\nWhen you need a second opinion, you don't want to attach a stack of scans to an email or hand over your whole archive. Asclepius has a dedicated share surface for outside clinicians: pick the documents that matter, give them a link, read out a code by phone, done.\n\n- **Curated, not all-or-nothing.** Tick one document or many from the list, as long as they belong to the same patient. The doctor sees only what you ticked, never the rest of the file, and free-text encounter notes are hidden by default.\n- **One-time link + out-of-band OTP.** The URL alone does nothing. The doctor has to request a 6-digit code on the landing page, and you read it back to them on a separate channel (typically a phone call). Five wrong attempts and the code burns; codes expire after 10 minutes; sessions are an absolute 2 hours with no refresh.\n- **Watermarked, no-download viewer.** PDFs are rendered server-side with a faint vector watermark on every page carrying the recipient's name and UTC timestamp. The viewer fetches bytes into memory, so right-click, Ctrl+S, and Ctrl+P are intercepted.\n- **Targeted translation, not a free LLM tunnel.** The doctor can translate the current page or drag a rectangle to translate a region. Whole-document translation isn't exposed. Rate-limited per session and per share. You pick which OCR and LLM provider runs the translation, per share or globally.\n- **Full audit trail.** Every OTP request, view, file fetch, translate, and logout is logged with timestamp, IP, and user-agent. Revoke kills active sessions instantly.\n- **Safe internet exposure (optional second container).** The bundled `docker-compose.yml` ships a sibling service `asclepius-share` that runs the same image with `ASCLEPIUS_MODE=share`. It mounts only the doctor-share routes (`/api/share/*` and the `/share/...` SPA pages); admin login, patient CRUD, settings, pipeline, and even the share-management endpoints all return 404. Bind that one port to the public internet behind your TLS proxy and keep the core admin port (`8070`) on the LAN. Set `ASCLEPIUS_SHARE_PUBLIC_URL` on the core container so the link the admin copies points at the public doctor host instead of the LAN admin host.\n\nFull reference: [Doctor shares](docs/src/content/docs/admin-guide/doctor-shares.md).\n\n## Tech stack\n\n| Component  | Technology                                                              |\n| ---------- | ----------------------------------------------------------------------- |\n| Backend    | Python + FastAPI                                                        |\n| Frontend   | React + TypeScript + Vite + shadcn/ui                                   |\n| Database   | SQLite (via aiosqlite)                                                  |\n| OCR        | Tesseract 5, LLM Vision, Google Vision                                  |\n| LLM        | Ollama, vLLM, Claude API, OpenAI API                                    |\n| Vision-LLM | Ollama (Qwen2.5-VL, MiniCPM-V, …), Claude vision, GPT-4o                |\n| DICOM      | pydicom (server-side render to PNG with optional window-level override) |\n| Deployment | Docker Compose                                                          |\n\n## Before you self-host\n\nAsclepius is personal-use software. It is built for one person managing their own family's records on a trusted home network or a single laptop, not a hardened service for the open internet.\n\nA few things stop being safe the moment strangers can reach it:\n\n- The bundled username/password login has no rate limiting, no MFA, and no account lockout. Anyone who can reach port `8070` can hammer it.\n- The chat feature lets the LLM author SQLite `SELECT` queries against your medical database. That makes it a prompt-injection target. Hostile content in a document, OCR result, or chat message can coerce the model into reading rows it should not, and SQL injection through the chat tool-call is a real concern if untrusted users can reach the endpoint.\n- Files in the inbox flow into LLM prompts, so any document from an untrusted source is a potential injection vector.\n\nFor anything beyond a single user on a trusted LAN, front it with an OIDC provider like [Authentik](https://goauthentik.io/), Keycloak, or Auth0, or keep the whole thing behind a VPN.\n\nThe exception is the doctor-share surface. The `asclepius-share` container in `docker-compose.yml` is the supported way to publish only that surface: same image, `ASCLEPIUS_MODE=share`, every other route returns 404. See the [doctor-shares guide](docs/src/content/docs/admin-guide/doctor-shares.md) for the deployment walkthrough.\n\n## Development\n\n### Backend\n\n```bash\ncd backend\npython -m venv .venv\nsource .venv/bin/activate  # or .venv\\Scripts\\activate on Windows\npip install -e \".[dev]\"\nuvicorn asclepius.main:app --reload\n```\n\n### Frontend\n\n```bash\ncd frontend\nnpm install\nnpm run dev\n```\n\n## Contributing\n\nContributions are welcome. Read [`CONTRIBUTING.md`](CONTRIBUTING.md) first for dev setup, coding style, and the PR checklist. To report a security issue privately, open an advisory from the repo's [Security tab](https://github.com/giovi321/asclepius/security).\n\nQuick starts:\n\n```bash\n# Backend tests + lint\ncd backend\npip install -e \".[dev]\"\nruff check .\npytest\n\n# Frontend type-check + build\ncd frontend\nnpm install\nnpx tsc --noEmit\nnpm run build\n```\n\n## License\n\nReleased under the [MIT License](LICENSE).\n\nBundled medical reference data (LOINC, ATC, ICD-10) is covered by separate third-party licenses, see [`NOTICE`](NOTICE) for required attributions, including the LOINC short notice required by Section 10 of the LOINC license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgiovi321%2Fasclepius","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgiovi321%2Fasclepius","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgiovi321%2Fasclepius/lists"}