Security
Reporting a vulnerability
Section titled “Reporting a vulnerability”Mail [email protected]. If you want to encrypt it, say so and we will send you
a key.
Tell us what you found, how to reproduce it, and what you think the impact is. A working proof of concept helps more than a scanner report. You will get a human reply within 3 business days, and a fix or a plan within 90 days for anything we accept.
What we ask of you. Do not run automated scanners against the production API — they cost us money per page and tell us nothing. Do not access, modify or delete another customer’s data; if you find a way to, stop and tell us. Do not run denial-of-service tests. Test against your own account.
What we promise you. We will not pursue legal action against anyone acting in good faith under these rules, we will keep you informed while we fix it, and we will credit you when we announce the fix unless you would rather we did not.
There is no paid bug bounty. There is one person and no marketing budget, and promising money we have not budgeted would be worse than saying so plainly.
What we claim, and what we do not
Section titled “What we claim, and what we do not”How documents are handled
Section titled “How documents are handled”We do not retain your documents. A document sent for conversion is not kept after the request it was sent for. Today the conversion endpoints go further than that rule requires: there is no upload endpoint, so the bytes exist in memory for the life of the request and there is nothing to delete afterwards.
The conversion result is a different matter and is cached for 24 hours, keyed
to your account and project, so an identical repeat request does not bill you
twice. Send cache: false and nothing is written at all. The 24 hours is
enforced by a bucket lifecycle rule rather than by a code path that has to
remember.
Conversion is isolated from everything else. PDF parsing is the most hostile input this service takes: a PDF is a program, and the decoders that read one have been the source of serious vulnerabilities in every reader ever written. Conversion therefore runs in its own service, on its own machines, reachable only by our API and never directly by you. The container runs as an unprivileged user from a distroless base — no shell, no package manager, no interpreter — and holds no credential beyond the one it needs to answer our calls.
We are not finished here, and would rather say so. The in-process syscall sandbox described in our design — a seccomp filter and a pre-forked worker model that would make a compromised parser unable to reach the network or the filesystem even in principle — is designed but not yet built. Until it is, the container boundary and the absent decoders are what stand between a hostile document and the rest of the system. If that is not enough for your threat model, it is a reasonable reason to wait.
The dangerous decoders are not compiled in. The build contains no JavaScript engine and no XFA implementation — the two largest historical attack surfaces in PDF rendering — and the default conversion path decodes no images at all. The build fails if a source manifest tries to name any of them.
Retention is enforced, not intended. Request metadata — which includes your source IP address — is deleted after 540 days by a scheduled job, and cached output after 24 hours by a storage lifecycle rule. The privacy policy is the full table.
The renderer’s provenance is pinned. The exact upstream revision of the PDF library we compile is recorded in the repository, our modifications to it are a reviewed patch series, and a build gate refuses to ship an artifact whose snapshot is more than 120 days old.
How your account is protected
Section titled “How your account is protected”API keys are stored as peppered hashes, so a key is shown once, at creation, and is replaced rather than recovered if you lose it.
Keys carry a checksum, so a leaked key can be recognised as ours and validated as syntactically real without being sent to us.
Sign-in is Google OAuth, so there is no password for us to hold or for an attacker to take from us.
Revocation takes effect on the next request. Key status is read from the account’s own store on every authenticated call rather than from a cache with a lifetime.
How payments are protected
Section titled “How payments are protected”Card payments are authorised, judged, and only then captured. Three-D Secure is requested on every card purchase, and an authorisation that comes back without a confirmed liability shift is cancelled rather than captured, so no money moves — though you may see a pending hold, which your bank releases on its own schedule.
Card details go to Stripe, not to us.
Credits are prepaid, which limits what a compromised key can spend to the balance on the account, and every account has a burn cap on top of that.
Data residency
Section titled “Data residency”The United States. Conversion runs in us-central1; account and ledger data are
stored in the United States and served from Cloudflare’s global edge.
Sub-processors
Section titled “Sub-processors”Listed, with what each one does, on the privacy page.
Machine-readable
Section titled “Machine-readable”https://api.kaho.ai/.well-known/security.txt and
https://docs.kaho.ai/.well-known/security.txt carry the same contact in the
format scanners expect.