Your documents never leave the browser. Here is exactly what does.
Kernstra is a client-side PDF SDK: it renders, edits, signs and saves on the device. There is no upload, no processing server and no copy of your files in our custody. Each load sends one license status check, and every field in it is listed on this page.
Everything inside the dashed line. Nothing of yours outside it.
The Kernstra Web SDK is a library that runs inside your page. It reads the document from wherever you serve it, does all of its work in the browser, and hands the result back to your code. The dashed line is the browser tab.
- Render worker
- Rendering engine
- Text layout
The risks a PDF component actually carries, and what we did about each.
Written for the person doing your security review. Every item here is in the SDK today, not on a roadmap.
Supply chain
The risk: a tampered engine or a swapped dependency runs on your page with your users' documents in memory.
- The engine, the text-shaping and font-subsetting binaries and every bundled font are checked against SHA-256 digests built into the SDK, from the store and from the cache. An engine that is not this version’s never executes; a binary or font that fails is never used.
- The store’s address is a signed claim in your license and the digests decide what is accepted, so nothing runs that we did not publish. No credential travels with a download.
- No third-party CDN, and no font service by default. Under a license that includes self-hosting, every file comes from your own servers and is verified the same way.
- Open-source components are listed with their licenses in the third-party notices shipped in the package.
Malicious documents
The risk: a crafted PDF uses the viewer to run script on your page, exfiltrate data or write something dangerous into a saved file.
- Pages are drawn as pixels on a canvas, and the selectable text over them is plain text, never HTML. JavaScript in a document never runs, and the SDK tells your code when a file declares some.
- Links are checked against a scheme allowlist: only http, https, mailto and tel are followed, by default after the reader confirms. A javascript: or data: address never is.
- Input is bounded: a page is read as at most 4,096 annotations, oversized text marks are clamped to the page, and a page tree that claims pages it lacks is refused. A placed image is capped at 10 MB by default and refused before decoding if it claims over 64 million pixels; SVG is never accepted.
- Filenames taken from a document are sanitized before they reach a download.
The license key
The risk: a key in client-side code is copied and used elsewhere, or is treated as a secret it was never meant to be.
- A key is a signed token, verified in the browser before anything loads. It names its own licensing API and asset store in signed claims, so there is nothing to redirect, and only answers the licensing API signed count.
- A production key runs only on the origins its license names. A load anywhere else is refused and reported, so the owner sees where it was tried. Development keys watermark every page.
- Each check is signed with a key the browser creates and keeps, so a token bound to one browser is refused from any other. Kernstra can pause or revoke a key; it takes effect at the next load, never mid-work.
- If the licensing API cannot be reached, a valid license loads cleanly for 48 hours after its last signed answer, then output is watermarked until a check succeeds. Offline licenses never check.
Encryption and permissions
The risk: a protected document is opened without its password, or a permission the author set is silently dropped on save.
- Password-protected documents are decrypted on the device with the password your code supplies. The password is never sent anywhere, including to us.
- Saving preserves the document's encryption and permission bits, and the Security tool can add owner and user passwords and restrict printing, copying and modification.
- Permissions are honored by the viewer's tools, and a forbidden operation fails with a named error your code can handle.
- Signatures placed with the sign tool are visible marks, and the signature panel reads existing digital signatures and leads with "Not verified" rather than implying trust it cannot prove.
What the SDK does not do
script-src 'self' 'wasm-unsafe-eval', worker-src 'self', img-src 'self' data:, style-src 'self' 'nonce-…' and connect-src 'self' plus the asset store and licensing API your license names. No inline script, no wildcard hosts; an offline license needs connect-src 'self' alone. The Get started guide has the full header.Self-hosted or air-gapped: choose how little leaves your network. Down to nothing.
All three run the same SDK with the same tools. The difference is only where the engine and fonts come from and whether the status check is made.
Your app, plus Kernstra’s two endpoints
Your bundler serves the SDK from your domain. The engine, text tools and fonts come from the asset store by digest, verified and cached, and each load sends the status check.
- Engine and fonts
- Kernstra’s asset store, verified and cached
- Status check
- On each load
// no URLs to set: the license names both // on each load, the status check: // POST <licensing api>/license/status // once per browser, then from the cache: // GET <asset store>/engine/<digest> await Kernstra.init({ license }); new PDFViewer(el, doc);
Everything from your origin
A license that includes self-hosting, today an offline license, serves the binaries and fonts from your own servers. Fetch each once by digest; the SDK verifies them as it verifies a download.
- Engine and fonts
- Your servers, verified by digest
- Status check
- None: an offline license
await Kernstra.init({ license, // an offline license engineUrl: '/assets/pdf-engine.bin', shaperUrl: '/assets/pdf-shaper.bin', subsetterUrl: '/assets/pdf-subset.bin', }); new PDFViewer(el, doc, { fonts: { fontsBaseUrl: '/assets/fonts/' }, });
No request leaves the network at all
An offline license names no licensing API and no store, so the SDK contacts neither. Turn font downloads off to keep to the standard fonts, the document’s own and yours.
- Engine and fonts
- Your servers; no font downloads
- Status check
- None: an offline license
await Kernstra.init({ license, // an offline license engineUrl: '/assets/pdf-engine.bin', shaperUrl: '/assets/pdf-shaper.bin', workerUrl: '/assets/render.worker.js', }); new PDFViewer(el, doc, { fonts: { download: false }, });
One status check per load. Here is all of it, field by field.
This is the entire request. It exists so your license can be confirmed and the browsers that use it counted against your plan, without us knowing who is behind them.
| Field | What it is | What it is not |
|---|---|---|
| token | Your license token, sent as a bearer header. Which license, and the origins and features it grants. | Not a secret: it is meant to ship in your page, and a copy runs only where the license allows. |
| nonce | A random value, fresh each time. The signed answer must repeat it, so an old answer cannot be replayed. | Not derived from anything. Used once. |
| time | The browser's clock. An answer counts only when it was signed within 48 hours of it. | Not the time zone or locale. |
| browser id | A pseudonymous identifier of this browser on your origin, from a key the browser creates once and keeps. It lets one browser be counted once and tells a copied token apart. Name it in your privacy notice. | Not derived from the device, the person, cookies or the document. Gone when site data is cleared. |
| sdk facts | About the SDK itself: its version, the engine digest, how the load went (its kind, where the token came from, failure counts, the age of a grace period, the last error code) and whether the identifier persisted. | Not page counts, file names or sizes, screen size, locale or time zone. |
| origin | The page's origin, added by the browser, as to every request. Checked against the origins the license names. | Not added by the SDK, and not the page path or query string. |
Never in the check, never anywhere else
- Document bytes or page images
- Text, search terms or form values
- File names, sizes or titles
- Passwords, authors or signatures
- Anything about the person
- Page counts, screen or locale
- Cookies: the SDK sets none
- Anything on a timer, or while idle
The browser adds its user agent to every request it makes, this one included, and the connection carries its IP address. The SDK adds nothing about the page or the person.
Every request the SDK can make
- To
- The licensing API the signed token names
- Carries
- The status-check fields above
Kernstra.init(), setLicense(), and opening a document when the last signed answer is over an hour old. Never on a timer; never for an offline license.
- To
- The asset store the token names
- Carries
- Nothing: the URL is the file's kind and its digest
First load on a browser (the engine); first use of a text tool (the shaping and subsetting binaries, a substitute font). A font request shows, by digest, which substitute a document needed; self-hosting or turning font downloads off removes it.
- To
- Your own URLs
- Carries
- Nothing
The same moments, under a license that includes self-hosting.
- To
- Your own origin
- Carries
- The document's bytes, to render them
When you set workerUrl: viewing runs on it under every license; after an edit, only under a production license that includes export, with clean output.
- To
- Where you point them
- Carries
- What you pass
Loading a URL, an upload, a stamp image.
- To
- fonts.googleapis.com
- Carries
- Nothing (the browser's request)
Only with the signature dialog's web fonts turned on. Off by default.
- To
- The link's address
- Carries
- Nothing
Only when the reader clicks it and confirms; http, https, mailto and tel only.
Everything the SDK keeps in the browser
kernstra-license-token, -update, -receipt, -retry-after, -diagThe presented token, a token adopted from a signed update, the last signed answer, a back-off deadline, failure counters
Until the license is signed out or revoked, or the site data is cleared. Kept across the SDK's own teardown, so the next load does not wait.kernstra-licenseA key pair the browser keeps: its pseudonymous identity for the status check
Until the site data is clearedkernstra-assetsThe verified engine, shaping and subsetting binaries and bundled fonts, checked again on every read
Until the site data is clearednothing written to diskSaved signatures (at most 3), the person's own stamps (at most 8), recent images and colors
The page's lifetime. Keeping them is yours, through the SDK's events.No sessionStorage, no cookies. Every store belongs to your page's origin; two tenants on one origin share them.
Personal data: two kinds of people, and only one of them has an account with us.
A pseudonymous browser identifier. Nothing else: no name, email, account or document. The licensing service counts how many browsers use your license; it never learns who is behind them.
- Name, work email, company
- Console sign-in and audit events
- Billing details; card numbers only at our payment processor
- Support conversations you start
Sub-processors
SeesBilling contact, company, payment method
Never document dataSeesAccount details, license keys, status checks with their pseudonymous browser identifiers, download requests
Never document dataSeesEmail addresses and the content of emails
Never document dataSeesFiles you attach to a support case, including a document only if you choose to share one for diagnosis
The providers are named, with their regions, in our Privacy Policy. Changes are announced 30 days ahead.
Commitments
Everything a reviewer asks for, in one pack.
Sent to a work email within one working day. Most questionnaires are answered from these four documents alone.
- Architecture and data-flow summaryThis page as a two-page PDF, with the diagram, the status check and the stores, for attaching to a review.PDF · 2 pages
- Third-party component listEvery open-source component in the package with version and license.Text · shipped in the package
- Sub-processor list and DPANamed providers and regions, and the agreement ready to sign.PDF
- Questionnaire answersSend us yours and we return it completed. Common frameworks are pre-filled.Your format
Security review questions, with short answers.
Can you prove no document data is sent?
Yes, and you should not take our word for it. Open your browser's network tab, use the viewer, and read every request. Only two kinds reach Kernstra: the license status check, whose fields are listed above, and downloads of the engine, text tools and fonts by digest, which carry nothing. A content security policy can enforce it: allow your origin and the two origins your license names, and nothing else can be reached.
Where is the engine hosted?
In Kernstra’s asset store, not in the package. The first load on a browser downloads it by digest, checks it against the SHA-256 built into the SDK and keeps it in Cache Storage, so later loads download nothing. Under a license that includes self-hosting you serve it yourself, verified the same way. No third-party CDN is involved.
Does the SDK need any browser permissions?
None. No camera, no clipboard beyond what the person copies, no cookies. It keeps the license state in localStorage, a key pair in IndexedDB and the verified binaries and fonts in Cache Storage, all listed above, and runs under a strict content security policy without inline script.
What about the fonts?
When a text tool needs a font the document does not carry, the SDK downloads a bundled one from the asset store by digest and verifies it; it asks no font service. That request carries no data, though its digest shows which substitute a document needed. Self-host the fonts, or turn their download off, and it is not made. The one exception is the web fonts of the signature dialog: they are off by default, and load from Google Fonts when you turn them on.
Is the license key safe in client-side code?
Yes, that is the intended use. A production key runs only on the origins its license names and is confirmed with our licensing service on every load, so a copy used on another site is refused. It cannot read your account or your documents.
Are you certified?
We will tell you plainly what we have when you ask for the pack, and we will not imply a certification we do not hold. What you can verify yourself today, on this page and in your network tab, does not depend on a certificate.