RSA-2048
a key pair per device
AES-GCM
a fresh key per message
Offline-first
appointments load from a local cache
Pusher
realtime chat and push
The brief
Medical conversations that shouldn’t sit readable in a database
Shifaa connects patients with their doctors: book a visit, then message the doctor directly about symptoms, results, and medication. Those messages are exactly what neither side wants stored in readable form.
HTTPS protects a message on the way to the server. It says nothing about the server itself. For a clinic, the stronger guarantee is that the server can never read the conversation — so that’s what I built.
Appointments are designed for weak connections too. The list opens instantly from a local sqflite cache and refreshes in the background, so a slow network never leaves a patient staring at a spinner.


The hard part
Encryption the server can’t undo
Every device creates its own 2048-bit RSA key pair on first launch. The private key stays in secure on-device storage and never leaves the phone.
Each message is encrypted with a fresh AES-GCM key, and that key is wrapped with the recipient’s public RSA key before sending. The server only relays ciphertext over Pusher Channels. Attachments follow the same model, and decryption runs in compute() so heavy files never freeze the UI.


What’s production-ready, and what isn’t yet
The encrypted chat and the offline appointment cache are complete, and I’d defend them in any code review.
Treatment plans and a couple of notification paths are UI-first: the screens are real, but they aren’t yet backed by a live service. They’re the first thing I’d finish on the way to production.
Stack
