Epicrisis CompanionNot a medical device

The box of paper in the cupboard, turned into something you can read.

Thirty years of test results, discharge summaries, ultrasound reports — printed by clinics that no longer exist, in countries you no longer live in, in languages your current doctor does not read.

Epicrisis Companion turns that box into an archive on your own computer. Every value exactly as it was printed, in the language it was printed in, next to the reference range printed beside it, one click from the original page.

You run it for yourself, then for your father, then for your grandmother — one instance, one archive per person, and the archives never touch.

Self-hosted Free for your family No account Runs offline RU · UK · EN · ES · EL

Who it is for

The same program, three different reasons to open it.

For yourself

The appointment is in an hour and the doctor asks how long your haemoglobin has been at that level. Three years — and here are the eleven measurements, in the units each of the three laboratories used.

For your father with type 2 diabetes

Fifteen years of glycated haemoglobin in one line: before it was found, the two years it was bad, the year treatment started, and every year since. The picture no single form contains, and the one every endocrinologist asks for.

For your grandmother

Her archive is small, on paper, in the language of another decade, and nobody but the family will ever type it up. Twenty documents are still twenty documents you can hand over as a chart instead of a plastic bag.

Fifteen years of HbA1c
A father's diabetes, in one line. The shaded band is the reference range printed on the form beside that value, drawn around the value it belongs to. Where a form printed no range there is no band, because the range on the form before it says nothing about a value printed without one. It steps because laboratories differ — not because anything was calculated. The rows below are Russian where his clinic was Russian, and English after he moved.

Every page, in pictures

All of it is the built-in demo: three archives of people who do not exist, drawn as scanned forms in five languages and then transcribed, checked and indexed for real — without a single model call and without anything leaving the machine. Drag the strip, or use the buttons. Click a picture to open it whole.

The charts are careful on purpose

Most of the work in this program is refusing to be helpful.

One test, two units, two charts
One test, two countries, two charts. Creatinine measured in µmol/L in one country and mg/dL in another. Nothing is converted: the moment a number is converted it is no longer the number printed on the page a doctor can ask you to show.
Blood, urine and stool kept apart
Blood, urine and stool are kept apart, by a tab. The same printed word — Protein, Glucose, Leukocytes — is a different measurement in blood and in urine, and a chart that puts them on one line lies quietly for years.

The machine checks itself

Models misread pages. So the archive does not trust its own reading.

The page of what needs checking
Checks that run with no model at all: a stored number that differs from the printed one, a date nobody could make out, a corner nobody could read, and the same blood draw filed three times. Copies come as a group with one already chosen — you change it only if the choice was wrong. Nothing on this page is changed for you.

Five languages, one test

Read and in daily use in Russian, Ukrainian, English, Spanish and Greek — including Greek capitals that lose their accents, headings spaced o u t like this, and tables printed sideways.

A Spanish laboratory form
The same program, a Spanish form: Hemoglobina, Hematíes, mg/dL, and the laboratory's own arrow in the margin.
A Russian laboratory form
And a Russian one: Гемоглобин, Эритроциты, мкмоль/л — the same test as the English form above, kept as one test and never rewritten.
One test, every spelling
Гемоглобін, Гемоглобин, Hemoglobina, Haemoglobin, Αιμοσφαιρίνη are one test. A model proposes the grouping, a second and deliberately different model reads it again, a reference is consulted where that is allowed — and then the person whose archive it is approves it. After that the vocabulary is stable, which is what makes a fifteen-year chart possible at all.

“Couldn't I just upload the files to a chatbot?”

For one page, yes — and it will read it better than any rule. For an archive it is a different job, and the difference is structural rather than a matter of prompting.

A model reading your filesEpicrisis
ScaleThousands of pages do not fit in a context windowRead once, stored, indexed; the whole archive answers in milliseconds
RepeatabilityAsk twice, get two answersThe reading is a file on your disk; tomorrow's answer is today's
ProvenanceA number in a chatEvery value carries its file, page and date, and links to the scan
Your correctionsDie with the conversationStored apart from the model's output, keyed to the printed line, reapplied after every re-read
Catching mistakesYou have to noticeDeterministic checks find them; a second, different model reads again and disagreements are shown
Units and specimensQuietly converted, quietly mergedNever converted silently; split by unit and by specimen, and where one test printed at two scales is drawn as one history, every moved point says so
VocabularyRegrouped differently every timeOne vocabulary, approved once, in five languages
Several peopleOne pileOne archive per person, one index file each, by construction
With no model at allNothing worksSearch, charts, index, checks and the whole dashboard
The model is used for the one thing it is genuinely better at: reading a page. Everything built on top of that reading is ordinary code you can inspect — which is why this archive keeps working when the models change.

What it has been run on

Real medical records of real people, and whose they are is nobody's business. Nothing from them appears here: every picture on this page comes from epicrisis demo, which invents its own people. Every count of what those archives hold is a floor and only ever goes up; the size of the test suite is exact, because this repository can prove it.

600+
documents read
6 000+
values, as printed
1989–2026
years covered
5
languages
150+
institution names on the forms
500+
approved test groups
2063
tests, no network, no model

Your archive stays yours

The whole design assumes the machine is yours and the network is not.

On the machine

  • Self-hosted. Your computer or your own server. No account, no service, no telemetry.
  • The dashboard listens on 127.0.0.1 only. From anywhere else, through an SSH tunnel.
  • Your scans are read-only. Nothing is copied, moved or renamed. Everything the program derives lives under data/. Most of it is made again by code alone: the index and the checks in seconds, the walk of the folder in minutes. The readings — what each page is and what was printed on it — only by paying a model to read the documents again, and it will read them a little differently.
  • Seven things under data/ are your own work, and nothing rebuilds them: your corrections, your verdicts on findings, the indicators you approved, the doctors and clinics you said were one, the earlier readings kept when a later one displaced them, your conversations, and the rules you wrote for your own laboratory's forms. epicrisis backup <folder> copies exactly those, and nothing else, in one place. Do not delete data/ without them.
  • Each person is a separate database. A folder is one owner's; two owners' folders may not contain one another; ids are random, because folder names carry surnames.

Before anything is sent to a model

  • A one-time written consent, on a page that shows you exactly what would be sent. Change the notice and it asks again.
  • Your own key or subscription, one isolated call per document, carrying that document's pages and nothing else.
  • The steps that need no model never ask — inventory, validation, index, search and every chart.

If you let an assistant read it over the network

  • Read-only tools only. Nothing writes, nothing runs, nothing reads a file outside the index.
  • An unguessable secret path. Without it the server answers as though nothing were there.
  • A private tunnel (Tailscale Funnel) that admits only the connector's own network.
  • A six-digit code from your authenticator (RFC 6238). unlock returns a pass good for four hours by default, and that window is a setting on the same page; every tool refuses without it, unless the owner has let that one link off the code (connector code off) or opened it from the machine itself (mcp-lock open) — both are the owner's own deliberate acts, and the links page says of each link which it is; lock_archive ends it early. The secret behind that code is generated on your server, read once into your phone, and never travels through a conversation.
  • An access log of time, address, tool name and whether the call was let through — never the secret, never the question, never the answer. A log of a medical archive that holds the questions is a second copy of the archive.
A secret path and a private tunnel say where a request came from and nothing at all about who sent it. The code from a phone is the only part a stranger cannot copy out of an address bar.
Three archives and the network lock
Three archives on one server, each at its own stage of the pipeline, and below them the read-only network tools with the state of the lock that guards them.

How you may use it

Two licences — free for a person and their family, commercial for an organisation — and which one you fall under is written into the licence itself, not promised on a page that can be edited later.

For you and your family

Free, for good

Keeping and reading medical records — your own, your household's, and those of relatives or friends you help without being paid — costs nothing and always will. So does reading the code, changing it, publishing your changes and running it to see what it does.

You run it yourself, and you use it at your own risk. It is provided as is, with no warranty of any kind: it is not a medical device, it does not say whether a value is normal, and a transcription from a scan can be wrong — which is why every value is one click from the page it came from. The page, not the program, is the authority.

Business Source License 1.1 · free production use for a person keeping records of their own, their household's, or of relatives and friends they help without being paid · every version becomes Apache 2.0 four years after it is published

For insurers — underwriting, claims, risk

A commercial licence

An insurer, a reinsurer, an assistance company — and any other organisation running this program, or offering what it does to other people as a product or a service — needs a commercial licence. It carries:

  • the right to run it in production, for your own people and your own clients;
  • a data processing agreement under Article 28 GDPR, and answers to a security review;
  • releases, and a named person to write to when something is wrong;
  • terms of liability a business can sign — the free version is provided as is, and that is the honest position for something given away.

The first teams this is built for are medical underwriting and claims: a full case history before the decision instead of a stack of PDFs, and the scan behind every number. Self-hosted, inside your own perimeter. It extracts and cites; it does not score, price or decide.

Three modes, and the strictest is the default. As printed: the archive is shown as the forms wrote it and the application compares nothing. May also read the values: a model may say what a value means and how it moved, and the application still computes nothing. No limits set here: the model answers under no rule of ours, and this is the one mode in which the application itself compares a number with the range printed beside it on its own form and counts what fell outside — so this is where a question such as when a value first left its printed range is answered. The mode is chosen on the Settings page of your own instance, by you, and nothing here changes who is responsible for a decision.

Write to artem.berman@gmail.com and say what your organisation does and how many people would use it.

Business Source License 1.1 · running it in production by or for an organisation is not covered by the free grant and needs a commercial licence · reading the code, changing it and evaluating it are free for everyone · every version becomes Apache 2.0 four years after it is published

For clinics and hospitals

A commercial licence

A clinic, a hospital, a laboratory, a practice — any organisation running this program for its own clinicians and its own patients — needs a commercial licence. It carries the same things: the right to run it in production, a data processing agreement under Article 28 GDPR and answers to a security review, releases, a named person to write to, and terms of liability an organisation can sign.

What it is for here is the paper a patient brings from somewhere else: other clinics' discharge letters, other laboratories' forms, often in several languages and spanning decades. It becomes one archive your clinician can read before the visit ends — one timeline per test across laboratories, the range printed on each form kept as that form printed it, and every value one click from the scan it came from. It reads and cites; it does not diagnose, triage or decide, and the page, not the program, is the authority. It holds what the patient brought and what could be read from it: a test missing from this archive is not a test that was never taken, and the program says as much wherever it answers with nothing.

Three modes, and the strictest is the default. As printed: the archive is shown as the forms wrote it and the application compares nothing. May also read the values: a model may say what a value means and how it moved, and the application still computes nothing. No limits set here: the model answers under no rule of ours, and this is the one mode in which the application itself compares a number with the range printed beside it on its own form. The last two were written for a person reading their own records — the model is told that the person asking is the person the records are about — so the mode that fits a clinic reading somebody else's file is the first. The mode is chosen on the Settings page of your own instance, by you, and nothing here changes who is responsible for a clinical decision.

One archive per patient, isolated by construction, and every page carries the name of the one whose records are open. Self-hosted, inside your own perimeter: no vendor cloud and no telemetry, and model calls go out with your own key or not at all.

Write to artem.berman@gmail.com and say what your organisation does and how many people would use it.

Business Source License 1.1 · running it in production by or for an organisation is not covered by the free grant and needs a commercial licence · reading the code, changing it and evaluating it are free for everyone · every version becomes Apache 2.0 four years after it is published

Try it without a page of your own

The same four commands whichever of the two you are: a person about to type up a parent's folder, or a team looking at this before it goes anywhere near a real case.

Python 3.14 and uv. This project asks uv to use a Python already on the machine rather than fetching one, so install 3.14 the way your system installs Pythons — brew install python@3.14 on macOS — rather than with uv python install. One system library too: libmagic, which tells a file's kind from its first bytes — brew install libmagic on macOS, apt install libmagic1 on Debian or Ubuntu, python-magic-bin on Windows.

git clone https://github.com/bermana-net/epicrisis && cd epicrisis
uv sync
uv run epicrisis demo --into /tmp/demo        # three invented archives, no model called
uv run epicrisis serve --data-dir /tmp/demo/data

That demo is everything on this page: three lives that never happened — Vera, her father Anders, her grandmother Zoya — drawn as scanned forms in five languages, transcribed, checked and indexed for real, with not one call leaving your machine.

Then, when you are ready — in this order:

uv run epicrisis sources add ~/scans --owner "Your name"
uv run epicrisis serve                        # open /consent and read what would be sent
uv run epicrisis update          # inventory → classify → extract → validate → index

The steps that read documents need a model: either the claude command signed in with your own Claude subscription, or ANTHROPIC_API_KEY in a .env file with the engine changed on the Settings page. Without one the other steps still work and the dashboard says which is missing. Nothing is sent before you read, once, what would go — which is why serve comes before update here: run them the other way round and the two steps in the middle are skipped, and you are left with an archive that has been listed and not read.