Ovillo Download
Contribute · Contributing

Contribute

Contributing

Ovillo is open source and welcomes help. This page is for developers: the ground rules, how the repository is laid out, how to run it safely, which tests to run, and how to send a change.

The project in one paragraph

Ovillo is a local-first, encrypted notebook for a family's care history. It is vanilla ES modules with no build step: index.html, app.mjs and styles.css run in the browser, and server.mjs runs on loopback to serve static files, store the encrypted vault and act as the only route out to an assistant provider. A thin Electron shell in desktop/ packages it as a Mac app. Source: github.com/BraedenBDev/ovillo. For the design, read Architecture and Privacy and security first.

Principles

A change that breaks one of these will not be merged, however good the feature.

Repository layout

PathWhat is in it
index.html, app.mjs, styles.css, viewer.html, viewer.cssThe page, its main script and styles, and the in-app PDF viewer.
server.mjsLoopback server: static files from an allowlist, the ciphertext vault, and the route to an assistant provider. Reads PORT (default 4173) and HP_DATA_DIR.
src/Shared modules: reading and extraction, the record and its holds, labs and units, scrubbing, the assistant's prompts and tools, ui-*.mjs screens, and i18n.mjs (all visible text).
src/providers/Provider adapters (openai-responses.mjs, chat-completions.mjs). Only the server uses them; they are never served to the page.
desktop/Electron shell, build config, staging script and smoke test. Electron lives only here. See Building the app.
tests/Unit tests (*.test.mjs), the browser end-to-end suite (e2e.mjs and e2e-checks/), the mutation gate, the scrubbing benchmark, layout screenshots and a hand-run assistant eval.
fixtures/, samples/Synthetic records, documents and photos for tests and the built-in sample.
scripts/fetch-assets.mjs (builds vendor/) and make-fixtures.mjs.
vendor/Gitignored. PDF reader, OCR, embeddings model, image decoders. Built by npm run assets.
docs/Design documents: architecture, threat model, contracts.

Getting set up

You need Node.js 20 or newer (the engines field in package.json).

  1. Clone the repository and open a terminal in it.
  2. Install the development dependencies: npm install.
  3. Build vendor/ with npm run assets. It copies the readers from node_modules and downloads the OCR language data and the embeddings model once, checking each against a pinned SHA-256.
  4. Start the server against a throwaway data folder, on a port other than your own copy's.
  5. Open the address it prints in a browser and create a new vault with a passphrase you do not mind forgetting.
npm install
npm run assets
HP_DATA_DIR="$(mktemp -d)" PORT=4180 node server.mjs

Then open http://127.0.0.1:4180. The vault in that folder is yours to delete when you finish. To try the app without any documents of your own, use the synthetic files in fixtures/ and samples/.

Important. Always set HP_DATA_DIR to a temporary folder and a spare PORT when developing. Without it the server uses ~/.health-picture, which is where a real family's vault lives. Never load your own or anyone's real records into a development copy.

Tests

CommandWhen to run it
npm testAlways. The unit tests, about 150 of them, and fast.
perl -e 'alarm 560; exec @ARGV' node tests/e2e.mjsBefore a pull request that touches behaviour you can see. A full browser run of about eight minutes on your local Chrome. It can hang, so the alarm ends it after 560 seconds. Set CHROME_PATH if Chrome is not found.
node tests/mutate-gate.mjsBy hand, after touching a hold in src/record.mjs or src/labs.mjs. It removes each hold from a temporary copy and must print "0 mutations survived".
node tests/scrub-bench.mjs vendor/meddocan/test/brat --limit 250By hand, after changing scrubbing. It reports recall for each kind of personal detail against the synthetic MEDDOCAN cases. --show-leaks prints what slipped through.
npm run shots -- <outdir>Before any layout change. It saves a screenshot of every tab at 1290, 900 and 390 pixels wide with the assistant docked.

The MEDDOCAN corpus is not in the repository. It lives in the gitignored vendor/meddocan/, from Zenodo record 4279323. Download it yourself if you work on scrubbing.

For layout work, look at the screenshots rather than trusting a green test. A laptop window with the assistant open leaves the page about 880 pixels wide, so check the 900 and 390 shots as well as 1290. Page layout rules are @container main queries on #main-content, which measure the page and not the window.

The desktop shell has its own smoke test, covered in Building the app.

Translations

Ovillo ships in English and Spanish. Every new piece of visible text needs both, added to src/i18n.mjs. tests/i18n.test.mjs checks that the two languages have the same keys, so a missing Spanish string fails npm test. Write plain, warm sentences and do not use em dashes in any UI string. If your Spanish is not good enough, say so in the pull request and a maintainer will help rather than machine-translate it.

Assistant prompts

The assistant's prompts and tool descriptions live in src/agent-prompts.mjs and src/agent-tools.mjs. When you change any of them, bump PROMPT_VERSION in src/agent-prompts.mjs and update the pinned version and hash in tests/assistant-cases.test.mjs. That test fails until you do, on purpose, so a prompt never changes silently.

tests/assistant-live.mjs runs the real prompts against your own provider and costs quota, so it is run by hand and is never part of npm test. It needs --yes-send, and takes the provider from HP_LIVE_BASE_URL, HP_LIVE_KEY and HP_LIVE_MODEL, or from --config <file>. --dry-run stops before any network call, and the script prints scores only. It runs on synthetic cases; keep it that way.

Commits and pull requests

Reporting bugs and security issues

Report ordinary bugs as GitHub issues on the repository. Say what you did, what you expected and what happened. Do not paste personal data, document text or screenshots of a real record; describe the shape of the problem or reproduce it with a synthetic file.

Report a security problem privately by email to possible@almostimpossible.agency rather than in a public issue. Include steps to reproduce, and again, no real records.

Licence

[Licence to be chosen before the repository goes public]