Someone is choking, bleeding, or not breathing. You have no signal, no training, and about four seconds of usable attention.
AidEmber takes a plain-language description — typed or spoken — routes it to a pre-authored, source-cited medical protocol, and reads the steps aloud one at a time in large type. Everything runs on-device. Nothing leaves the phone.
A language model must never invent a compression depth. So it doesn't write anything at all — it does exactly one job: pick which protocol applies.
Every medical instruction a user reads or hears is loaded verbatim from a signed JSON file that a human transcribed from a published guideline, and every step carries a link back to it.
If the matcher is wrong, the user is one tap from the right protocol. If the engine were wrong, someone could die — so the engine is a pure state machine over signed data with a test suite that asserts invariants on every node. This is not a v1 limitation. It is the architecture, and the roadmap keeps the boundary exactly here even as the matcher gets smarter.
Anyone may publish a protocol pack. Only reviewed content gets the badge.
| Community | Verified | |
|---|---|---|
| Who can publish | anyone | a board with a signing key |
| Signature | optional, or self-signed | Ed25519, over the pack digest |
| Badge in app | grey “Community” | green “Verified” |
| Shown by default | no — opt-in | yes |
| Source per step | required | required + reviewer sign-off |
| Passes safety invariants | required | required |
Note the last row. Both tiers must pass every invariant — reachability, a call-for-help node, citations on every step, the content lint. A pack that fails those is never installed, by either tier. The tiers differ in who vouched, not in whether the thing is structurally safe.
Every verification failure path lands on community. There is no code path that yields verified by
default, by omission, or by error — each row of that table is asserted in
signature.test.js. The repo ships
a pack that lies about it so you can watch the
downgrade happen.
Three scenarios, English, ERC / Resuscitation Council UK 2025.
| Protocol | Covers | Highlights |
|---|---|---|
cardiac-arrest-adult |
adult | 110 bpm metronome, AED prompts, call-before-you-check |
cardiac-arrest-child |
child · infant | 5 initial breaths, age-branched technique |
choking-adult |
adult | cough → 5 back blows → 5 abdominal thrusts |
choking-child |
child | call early, alternating cycles |
choking-infant |
infant | chest thrusts — abdominal thrusts are blocked by lint |
severe-bleeding |
any | pressure → packing → tourniquet, with the time written down |
59 steps · all 59 cite a source · 31 marked clinically critical · 6 cited guidelines
Any protocol can hand over to another mid-emergency: a choking victim who collapses transfers straight
into CPR, and back still works across the boundary.
No npm install. The engine has zero dependencies, so nothing to fetch.
node tools/build-demo.mjs # inlines engine + packs into one 120 KB HTML file
open apps/demo/aidember-demo.html # then turn your wifi off and use it anywayThe demo ships with a live inspector that prints the routing decision — every keyword that scored, its
weight, and why the runner-up lost. Type nosebleed and watch it correctly refuse to open the
life-threatening bleeding protocol.
Run the tests, checks and tooling
node --test packages/core/test/*.test.js # 103 tests, including 42 routing evals
node tools/verify-pack.mjs # validate + verify signatures on every pack
node tools/i18n-check.mjs # localized packs vs the reference
node tools/i18n-extract.mjs <pack> ru # start a translation
node tools/keygen.mjs my-board # run your own protocol board
node tools/sign-pack.mjs <pack> <key.json> # sign (refuses to sign an invalid pack)Node 20+ and nothing else. CI runs exactly these.
Run the mobile app
cd apps/mobile && npm install && npx expo run:androidExpo / React Native, Android-first. Offline TTS, CPR metronome with haptics, emergency dialling, and the source sheet on every step. Not yet built on a real low-end device — see the roadmap.
Machine-checked in CI and again on the device, for every pack, from any author. A pack that breaks one is not installed.
| Invariant | Why | |
|---|---|---|
| 1 | Every protocol has a reachable call node |
help must always be summonable |
| 2 | Every step resolves a citation | the ⓘ button is never empty |
| 3 | critical steps cite their own source |
pack-level fallback isn't good enough for a dose |
| 4 | No dead ends; every path reaches a terminal | nobody gets trapped with a shaking phone |
| 5 | Text fits the character budget | a person in shock cannot read a paragraph |
| 6 | No auto-advance under a metronome | never move the screen during compressions |
| 7 | Content lint passes | withdrawn techniques may appear only as prohibitions |
| 8 | Numbers survive translation | where translated first-aid content actually kills |
Rule 7 blocks blind finger sweeps, pressure points, tourniquet release, and abdominal thrusts in an infant protocol — unless the sentence is clearly a don't. Rule 8 fails any translation that loses a digit from a depth, dose, count or rate; no reviewer reading fluent prose catches that reliably.
The routing evals are a table of phrases a real bystander might say, paired with the protocol that must be reached — including the ones that must not match. Cases are added when a bug is found, and never deleted.
packages/core/ @aidember/core — the step engine. Zero deps, pure ESM, ~900 lines.
Same file runs under node --test, in the HTML demo, and in React Native.
packages/protocols/ JSON Schema + the ERC-2025-EN pack + a worked example of a pack that lies
apps/mobile/ Expo / React Native app (Android-first, fully offline)
apps/demo/ single-file offline prototype with a live routing inspector
tools/ pack signing, verification, localization, demo build — Node only
evals/ safety evaluation cases for triage routing
docs/ user journey, architecture, protocol format, trust & safety model
|
Open, forever
|
Not open
|
Privacy is a build property, not a policy: the release Android build ships without the INTERNET
permission. There is no network stack through which an utterance could leave the device. Details in
docs/OPEN_CORE.md.
This is v0.1. Stated plainly, because a safety project that oversells itself has already failed:
- Nothing has been medically reviewed, so nothing is signed. The trust store is empty and the
shipped pack declares itself
community. That is why. - The offline speech-to-text module is specified but not linked — the app falls back to typing and the three scenario buttons, which is a complete product.
- The mobile app has not been built on a real low-end device yet.
- No users, no clinical outcomes data.
If you are a clinician willing to review a protocol, that is the single most valuable thing anyone can contribute right now. See CONTRIBUTING.md.
| User journey | what the screens do, and the constraints that shaped them |
| Architecture | modules, data flow, offline strategy, why Expo |
| Protocol format | how to author a pack |
| Trust model | community vs verified, signing, revocation, threat model |
| Safety | invariants, evals, and the failure modes we accept |
| Localization | adding a language or a country |
| Open core | what is open, what is not, and what would make us less open |
| Roadmap | next steps, and where an on-device model slots in |
AidEmber is guidance software. It is not a medical device, not a diagnostic tool, and not a substitute for emergency services or for training. It tells you to call for help first, every time. Protocols are transcriptions of published guidelines, cited on every step; AidEmber is not endorsed by ERC, Resuscitation Council UK, ILCOR or the AHA.
Read docs/SAFETY.md before contributing protocol content.
Code under Apache-2.0. Protocol content under CC BY-SA 4.0 — share-alike is deliberate, so improvements to first-aid content flow back to everyone.
Built in the open, because a safety check nobody can inspect is a claim, not a property.