Security

How your data is held

Keln is operated by Keln Holdings LLC. Every claim on this page describes something that is actually in the code today. Where a protection depends on how a deployment is configured, it says so, and where Keln has a known limitation, that is written down too — a security page that only lists strengths is a marketing page wearing a lab coat.

What Keln never holds

Card numbers

Subscription payments are handled entirely by Stripe. Card details are entered on Stripe's own pages and never reach a Keln server, which is why Keln has no card data to lose.

Your clients' payments

When a client pays for an appointment they use your payment link — your Stripe, PayPal or Square. Keln stores the URL and shows a button. The money moves between you and them; Keln is not in the path at all.

Plaintext passwords

Passwords are hashed with scrypt and a per-account random salt before storage. There is no route, admin screen or support process that can reveal a password, because the original is never written down.

Protecting what is stored

Contact details are encrypted at rest

Names, emails, phone numbers, addresses and notes on your prospects are sealed field by field with AES-256-GCM before they touch the disk, under a key held in the deployment's environment rather than in the database. Someone who obtained a copy of the database file alone would not be able to read them. This requires an encryption key to be configured on the deployment; without one, Keln logs a warning at boot and stores those fields in the clear.

Writes are atomic, and the write is fsynced — so is the rename

Every save is written to a temporary file, flushed to the disk, and then renamed over the real one. Renaming within a filesystem is atomic, so a reader sees either the whole old database or the whole new one and never a half-written file. The directory itself is fsynced after the rename, which is what makes the publication of the new file survive a power loss — the step most "atomic write" implementations leave out, and the one that decides whether your data is still there after the lights come back.

Backups are verified before they are taken, and refuse rather than overwrite

The backup job parses the database before copying it. If it will not parse, nothing is copied and nothing is rotated away, so the last known-good snapshots stay exactly where they are. A backup system that faithfully copies a corrupt file, on a schedule, with a retention limit, will quietly destroy every restore point you have — this one is built not to.

Verified snapshots are then encrypted with AES-256-GCM and pushed off the machine, under a key that is separate from the one protecting the live database and that never leaves the server. Local snapshots sit on the same disk as the data they protect; only an off-site copy survives that disk dying. The restore path is a script we can run, not a procedure we have written down and never tried.

Two-factor authentication

Any account can add a time-based code from an authenticator app, and admin accounts must — the admin surface stays closed until it is on, because that account can reach every customer's data. Codes are verified against RFC 6238, and a code that has been used is never accepted again: without that, anyone who reads a code over your shoulder has up to ninety seconds to use it themselves. Ten single-use recovery codes are issued once, shown once, and stored hashed — so if you lose the printout, nobody at Keln can read them back to you either.

Sessions

Sign-ins are stored server-side in SQLite, not in the cookie. The cookie itself is httpOnly (unreadable by any script on the page), sameSite=lax, and marked secure in production so it is never sent over plain HTTP. Changing your password invalidates every other session, and the session identifier is regenerated at the moment you sign in — so a cookie planted before sign-in is not the one that ends up authenticated.

Rate limits and bot protection

Sign-in, sign-up, password reset, email sending and portal bookings each carry their own rate limit, sized to what that route is for rather than to one global number. Sign-in, sign-up and portal bookings additionally require a Cloudflare Turnstile challenge that is verified on the server — a challenge that is only rendered and never checked is worse than none, because it reports a locked door that is standing open. Password reset, resend-verification and the waitlist carry the same check: each of them sends mail to an address the caller chose, which without a challenge makes them a free mailer running on our own domain reputation.

The client portal

Private links are stored hashed

The link that lets a client see their appointments without an account is a 256-bit random token. Only its SHA-256 hash is stored, so reading the database does not hand anyone another person's bookings. Each booking mints a fresh link and older ones keep working, so a returning client is never locked out.

A client can only ever see an allowlist

What a client sees of their own record is constructed field by field in one function — never copied wholesale from the stored row. That is a deliberate choice: it is the only way to be certain a client never reads their own pipeline stage, the value you put on the deal, a private note about them, or a line of their transit log. Ownership is checked against the token, so knowing an appointment's identifier is not permission to touch it.

A portal that is switched off returns "not found"

Not "closed", not "disabled" — the address behaves exactly as one that was never claimed. Turning your booking page off should not confirm to a stranger that it exists.

Your data is yours

Export

Everything on your account can be exported as a single JSON file from Settings, at any time, without asking anyone.

Deletion is deletion

Deleting your account erases the record rather than flagging it as hidden. A soft flag while the row still sits in the database would make the promise in our Privacy Policy untrue.

No ads, no resale

Your pipeline is not a dataset. It is not sold, and it is not used to target anything at you.

What we would tell an auditor

The honest list of what Keln is not, as of today. It is here because a security page you cannot check is worth nothing, and because these are the things we would rather you heard from us.

Reporting something

If you find a vulnerability, write to support with the words "security report" in the subject. Tell us what you found and how to reproduce it. We will confirm receipt, keep you updated, and we will not threaten you for looking. Please do not run automated scans against production or access an account that is not yours — a proof of concept against your own account is enough, and it is what we would ask for anyway.