chronolipi
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

Rate limits in the reference build: 40 req/min per client, burst 120 (Pro advertises 600/min). All timestamps are ISO 8601, normalised to IST on the pages. Machine-readable contract: /v1/openapi.json, interactive docs at /v1/docs.
EndpointDoesNotes
GET /v1/healthCounts, which engines are really installed, and what this deployment can and cannot do.Unauthenticated, so an operator can be checked, not trusted.
POST /v1/accountsCreate 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-paramsDoes 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/sessionTrade 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/meMy 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/peoplePeople in an office, their roles and public keys.Removing someone gives every folder they could open a new key version.
POST /v1/invitesAn 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/foldersShared folders and who may open them (edit or view).Enforced on every request; the keys themselves are only ever wrapped to people.
GET /v1/remindersReminders 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/sharesA 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/historyThe document's custody record from the hash-chained log, for the court copy pack.Re-verified each time it is asked for.
GET /v1/planPlan, limits and this month's meter (pages, Ask AI questions, AI-read pages).We count what we can count without reading anything.
POST /v1/uploadsStore 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/:idHashes, 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/indexAttach a client-sealed searchable index to a document.The production path: the server stores the envelope and cannot read it.
POST /v1/searchBM25 over whatever text the owner chose to hand over.Ranking only. Retrieval without the key yields nothing readable.
GET /v1/timelineDate-ordered stream, month buckets, cursor pagination.The bucket is plaintext: a month is a rounding of a timestamp we already store.
POST /v1/exportsSynchronous tar.gz of sealed blobs, envelopes and the audit chain.Free on every plan, readable without us.
GET /v1/ocr/capabilitiesWhich 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/jobsSubmit 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_idPoll 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/:idErasure across blob, index and ledger, with a receipt.Refcounted: shared bytes stay until the last document referencing them is gone.
GET /v1/auditThe hash-chained log, plus a verification pass.Detects silent edits. Backups are encrypted and kept off the server.
Webhooksindex.done, review.needed, integrity.mismatchHMAC-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

All of it is reported by the API itself: GET /v1/health answers capabilities and engines. The dashboard prints the same table on its opener, not only in Settings.
The page saysThis build doesBecause
Tesseract + PaddleOCRTesseract 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 embeddingsHashed term vectors, 4096 dimensions, L2-normalisedDeterministic, no model weights, and rankings that survive a rebuild byte-for-byte
PostgreSQL + Qdrant + S3One SQLite file plus a content-addressed blob storeSame shapes — rows, vectors, dedup — without needing three services to read the code
Ephemeral decrypt-in-RAM workersThe API never decrypts; a separate sandboxed reader decrypts only pages a user explicitly sends for server readingPages 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.