Skip to content
danileauPublic

About

Zero-knowledge health notebook for chronic conditions — log your day in a few minutes, hand your doctor a clean PDF. Every key is derived in the browser; the server stores opaque blobs and cannot read a single entry. SvelteKit, Flask, PostgreSQL, self-hosted.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

ciphra

A zero-knowledge personal health notebook. ciphra lets people living with a chronic condition log their day in a few minutes each evening — symptoms, episodes, triggers, vitals, medications, notes — and hand their doctor a clean PDF at the next visit. Every entry reaches the server as an opaque blob it mathematically cannot read — alongside the bookkeeping any account needs, which docs/SECURITY_MODEL.md lists in full.

ciphra grew out of epilepc, a Symfony epilepsy tracker built as a diploma thesis. epilepc encrypted data server-side (the server held the key) and hard-coded one condition's schema. ciphra keeps the idea — "give your doctor a grid they can read in seconds" — and fixes both: encryption moves entirely into the browser, and a blueprint system opens it to any condition.

What makes it zero-knowledge

All key derivation and encryption happen in the browser. The password never leaves the device. The server receives a hashed credential and opaque ciphertext, and stores exactly that. A database breach yields encrypted blobs, nothing else. The full model — threat model, key hierarchy, what the browser caches — is in docs/SECURITY_MODEL.md. Found a vulnerability? SECURITY.md says where to send it.

Quick start

Requires Docker + Docker Compose.

git clone <repo-url> ciphra && cd ciphra
cp .env.example .env          # then fill in SECRET_KEY (≥32 chars)
docker compose up --build

Then open http://localhost:8080 — nginx serves the app and proxies the API.

Service URL Role
nginx http://localhost:8080 Unified entry point
frontend http://localhost:5173 SvelteKit dev server (direct)
api http://localhost:5050 Flask API (direct)
postgres localhost:5433 Database
redis (internal) Rate-limit counters (prod)

A SECRET_KEY of 32+ characters is mandatory — the API refuses to start without it. Generate one with python -c 'import secrets; print(secrets.token_hex(32))'.

Documentation

Doc What's in it
docs/ARCHITECTURE.md System design — services, data model, crypto, the blueprint/cohort system, the doctor PDF
docs/DEVELOPING.md Local setup, seeding demo data, running tests, project layout
docs/OPERATIONS.md Production ops — CI/CD pipeline, pull-based deploy, cron jobs, monitoring, backup + restore, disaster recovery
docs/THREAT_MODEL.md Operational threat model — adversary classes, attack-surface inventory, supply-chain + backup integrity, JS-swap problem
docs/SECURITY_MODEL.md Security model — what is and isn't protected, the key hierarchy, what the browser caches, how to verify it yourself
docs/INCIDENT_RESPONSE.md Incident playbook — severity model, detect→learn loop, per-scenario playbooks, the incident-record format
docs/backlog.md Forward-looking backlog — the larger items (each with a ready continuation prompt), operator actions, and decisions made
docs/FEATURES.md What ciphra does today, feature by feature
CHANGELOG.md What changed in each release — also readable in-app at /docs → Changelog
docs/VERSIONING.md SemVer policy (what's a patch / feature / breaking change) + the release process
SECURITY.md Vulnerability reporting policy — where to report, what's in scope, what response to expect
LICENSE GNU Affero General Public License v3.0 — the terms ciphra is offered under
THIRD-PARTY-NOTICES.md Third-party code that ships inside ciphra, and its licenses
frontend/src/lib/components/README.md The design-system reference (components, tokens)
docs/archive/ Retired specs and origin documents, kept for provenance

Tech stack

  • Frontend — SvelteKit + TypeScript, Vite, client-side WebCrypto + Argon2id (WASM)
  • API — Flask (Python 3.11), gunicorn (schema initialised via api/entrypoint.sh), JWT auth
  • Database — PostgreSQL 15, one opaque encrypted-document table
  • Rate-limit store — Redis (in-memory only, lives in the data plane so counters survive app restarts)
  • Deployment — Docker Compose; nginx reverse proxy (Cloudflare-fronted in production)

ciphra is a serious tool for managing a real condition — not a wellness app. No gamification, no email required, four languages (DE/EN/FR/IT).

License

ciphra is free software under the GNU Affero General Public License v3.0 (AGPL-3.0-only). You may run it, study it, change it, and share it — including commercially.

The AGPL adds one obligation beyond the GPL, and it is the reason it was chosen here. Under §13, if you run a modified ciphra as a network service, you must offer your users the source of the version you are running. Self-hosting a patched copy privately is fine; serving it to other people while keeping your changes closed is not.

So if you modify ciphra and put it online, point the app at your own repository:

PUBLIC_SOURCE_URL=https://git.example.org/you/ciphra

The app renders that URL in its footer and on the landing page's "Verify it yourself" card. It is read at runtime, so setting it on a pulled image is enough — no rebuild. Left unset, the app links to this repository, which is the truth only for an unmodified deployment. A modified instance that still links here is not compliant: it shows users code that is not what it is serving. frontend/src/lib/source.ts has the details, and a test fails the build if any component hardcodes a repository URL again.

Zero-knowledge is a claim you should not have to take on faith. The license is half of what makes it checkable: it guarantees the source of a running instance is available. The other half is the signed release images — cosign-keyless via GitHub OIDC, logged to Rekor — which tie the published source to the bytes actually served.

The name is not covered. "ciphra", the wordmark, the asterisk and the icons in brand_assets/ are not licensed under the AGPL. Fork the code freely; call your fork something else, so nobody mistakes your deployment for this one.

About

Zero-knowledge health notebook for chronic conditions — log your day in a few minutes, hand your doctor a clean PDF. Every key is derived in the browser; the server stores opaque blobs and cannot read a single entry. SvelteKit, Flask, PostgreSQL, self-hosted.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages