For developers¶
BlindProof is a three-layer system: a desktop client that watches and encrypts, a backend that aggregates and anchors, and a public timestamping service that provides the immovable evidence. The pages in this section document each layer, the cryptographic primitives that hold them together, and the proof bundle format that comes out the other end.
The whole system at a glance¶
flowchart LR
subgraph Author["Author's machine"]
Editor["Editor<br/>iA Writer, vim, Word..."]
Watcher["Desktop client<br/>file watcher + crypto"]
LocalStore[("SQLite +<br/>encrypted blobs")]
Editor --> Watcher --> LocalStore
end
subgraph Server["blindproof.co.uk (Fly.io)"]
API["Django + Ninja API"]
DB[("Metadata DB")]
Blobs[("Ciphertext blobs")]
API --> DB
API --> Blobs
end
subgraph Public["Public infrastructure"]
OTS["OpenTimestamps<br/>calendars"]
BTC["Bitcoin blockchain"]
OTS --> BTC
end
LocalStore -->|ciphertext + metadata| API
API -->|daily Merkle root| OTS
BTC -->|block height| API
Two things to notice:
Plaintext stops at the Watcher boundary. The editor hands the client the plaintext; the client encrypts it immediately and from that point forward only ciphertext moves. The backend never sees manuscript words, file names, folder paths, or project titles — those are all encrypted on the client before upload.
Nothing author-identifying crosses the API → OTS arrow. What gets sent to the public calendars is a 32-byte SHA-256 root of a Merkle tree built from per-save HMAC commitments. It is independent of author, manuscript, and BlindProof-the-company. The anchor lives in Bitcoin and is verifiable by anyone with the public record.
The three layers¶
- Client — a single-file Python script (
client/blindproof.py, PEP 723 inline deps) that watches a folder, extracts plain text, derives keys from a passphrase, encrypts snapshots, and syncs to the backend. Plaintext never touches disk outside the user's own editor, and never leaves the machine in any form. - Backend API and internals — Django + Django Ninja on Fly.io. Stores ciphertext and metadata, aggregates each user's daily HMAC commitments into a Merkle tree, submits the root to OpenTimestamps, and assembles the signed proof bundle on demand.
- Desktop GUI — a small PyObjC AppKit window that reuses the client's code path, handles enrolment via native dialogs, and carries a Generate proof bundle button that saves the zip into
~/Downloads/.
The evidence chain¶
- Design principles — the four non-negotiable commitments that shape every decision.
- Threat model — who we protect the author from, and who we explicitly do not.
- Proof bundle format — the four files in the zip, their schemas, and how they are assembled.
- Verifying a bundle — what
verify.pydoes end-to-end, and what PASS, PENDING, and FAIL each mean.
Running and shipping¶
- Running locally — each layer.
- Testing — test commands, red/green TDD workflow.
- Deployment & CI — Fly.io, the daily OTS cron, and the docs site itself.
Source of truth¶
The product spec — threat model, non-negotiable constraints, designer brief — lives in blindproof_spec.md at the repo root. These docs summarise and extend it; where they disagree, the spec wins.