Developers
The reference API is callable today
Every endpoint below is implemented, and each one is the version the dashboard at app.chronolipi.com actually uses. This is a reference build, not a public multi-tenant service: bring your own host, and read the honest list of substitutions at the bottom.
Endpoints
REST, JSON, idempotent where it can be, no plaintext intake ever
| Endpoint | Does | Notes |
|---|---|---|
| GET /v1/health | Counts, which engines are really installed, and what this deployment can and cannot do. | Unauthenticated, so an operator can be checked, not trusted. |
| POST /v1/accounts | Create a personal or office account: the owner's public key, their sealed root key and the first folders' keys, granted to that public key. | We hash the key-check blobs with Argon2id and cannot invert them. There is no reset. |
| GET /v1/kdf-params | Does this account exist, is it an office, and which salts does this person's password and emergency key use? | Public on purpose: without it a second device cannot derive the same key. |
| POST /v1/session | Trade proof of a person's password, emergency key or remembered device key for a session token. | The token says who may open which folder; it carries nothing that could read a document. |
| GET /v1/me | My folders and their keys (wrapped to my public key), my role, this month's usage and the plan's limits. | A folder key opens only with the private key that never leaves the person's browser. |
| GET /v1/people | People in an office, their roles and public keys. | Removing someone gives every folder they could open a new key version. |
| POST /v1/invites | An invitation link; folder keys are wrapped by the link's secret half, which stays after '#' and never reaches us. | Works once, expires, can be cancelled. |
| POST /v1/folders | Shared folders and who may open them (edit or view). | Enforced on every request; the keys themselves are only ever wrapped to people. |
| GET /v1/reminders | Reminders in folders I can open: the day, who to tell, and what it is about — sealed by the folder key. | Notices by email, WhatsApp or SMS say only how many reminders are due. |
| POST /v1/documents/:id/shares | A view link for one document: its key wrapped by the link's secret, an expiry and a number of openings. | The secret stays after '#'. Every opening is audited; links can be stopped. |
| GET /v1/documents/:id/history | The document's custody record from the hash-chained log, for the court copy pack. | Re-verified each time it is asked for. |
| GET /v1/plan | Plan, limits and this month's meter (pages, Ask AI questions, AI-read pages). | We count what we can count without reading anything. |
| POST /v1/uploads | Store an envelope the browser already sealed. Returns receipt, sizes and index summary. | Content hash is unique per folder: a duplicate is a 409, not a second copy. |
| GET /v1/documents/:id | Hashes, sizes, month bucket, custody note. | Never the content. Titles live in a record sealed by the folder key, not a column. |
| POST /v1/documents/:id/index | Attach a client-sealed searchable index to a document. | The production path: the server stores the envelope and cannot read it. |
| POST /v1/search | BM25 over whatever text the owner chose to hand over. | Ranking only. Retrieval without the key yields nothing readable. |
| GET /v1/timeline | Date-ordered stream, month buckets, cursor pagination. | The bucket is plaintext: a month is a rounding of a timestamp we already store. |
| POST /v1/exports | Synchronous tar.gz of sealed blobs, envelopes and the audit chain. | Free on every plan, readable without us. |
| GET /v1/ocr/capabilities | Which server-reading engines are up, and the sealed reader's public key. | Server reading is opt-in; OCR runs in the browser by default. |
| POST /v1/ocr/jobs | Submit a page sealed to the reader's key (ECDH P-256 + AES-256-GCM), with the user's consent. | The API relays ciphertext; only the sandboxed reader can open it. Logged, without content. |
| GET /v1/ocr/jobs/:job_id | Poll a reading; the answer comes back sealed to the browser, handed out once. | Another vault gets 404; a second pickup gets 404. |
| DELETE /v1/documents/:id | Erasure across blob, index and ledger, with a receipt. | Refcounted: shared bytes stay until the last document referencing them is gone. |
| GET /v1/audit | The hash-chained log, plus a verification pass. | Detects silent edits. Backups are encrypted and kept off the server. |
| Webhooks | index.done, review.needed, integrity.mismatch | HMAC-signed, HTTPS only, secret shown once. |
Field extraction runs today for six sectors. A deed returns
document_date, survey_no, parties[], consideration, registration_no, each with the confidence of the span that produced it. It is regex-shaped in this build, and it is honest about which field came from which match — that is the part most "AI extraction" demos skip.What is substituted, and why
A reference build that hides its substitutions is a marketing page with routes
| The page says | This build does | Because |
|---|---|---|
| Tesseract + PaddleOCR | Tesseract in the browser (14 languages, pinned, integrity-checked) with image clean-up and a review step; opt-in server reading through a sealed, sandboxed worker. No PaddleOCR. | OCR where the key already is keeps the zero-knowledge promise; AI reading of handwriting needs a GPU server, so it ships ready but switched off |
| BGE / E5 embeddings | Hashed term vectors, 4096 dimensions, L2-normalised | Deterministic, no model weights, and rankings that survive a rebuild byte-for-byte |
| PostgreSQL + Qdrant + S3 | One SQLite file plus a content-addressed blob store | Same shapes — rows, vectors, dedup — without needing three services to read the code |
| Ephemeral decrypt-in-RAM workers | The API never decrypts; a separate sandboxed reader decrypts only pages a user explicitly sends for server reading | Pages are sealed to the reader's own key, so the API and network carry ciphertext even then |
Run it yourself: the source archive contains
app/server (FastAPI), app/public (the client, including the Argon2id and AES-GCM code that runs in the browser) and three test suites — primitives in node, wire format in Python against a live server, and the whole UI driven through jsdom. python3 app/tests/integration.py is the one that proves the format is real: it re-implements the client in Python and the two must interoperate.
Get started
Read it as code, not prose
The OpenAPI document is generated by the same module that serves the endpoints, so this table and the implementation cannot drift apart.