Kanbanly

Security

How we protect your account and your content, what technical decisions lie behind it, and how to tell us if you find a flaw.

Last updated September 5, 2026

The principle: let the database say no

The most important security decision in Kanbanly is where permissions are checked. They are not checked in the interface, nor in the code that queries the database: they are checked in the database itself, with row-level security policies.

The difference is enormous and worth understanding. If permissions are checked in the queries, every single query has to remember: across the hundred-plus this application runs, forgetting in one is only a matter of time, and the one that forgets will not raise an error — it will show data it should not, and it will be discovered when somebody complains. With policies in the database, the check applies to every read by definition, without any query having to remember. A badly written query returns less than it expected, not more.

Built on top of that are the board roles — admin, edit, comment, observe — workspace visibility, guest access to a single board, and which fields of your profile each person can see.

Your account and your password

Passwords are held by Supabase's authentication system, hashed with key-derivation functions built for the purpose. Kanbanly does not store passwords in its own database, and neither the application code nor whoever administers it can read yours. When you forget it we cannot tell you what it was: all we can do is give you a way to set a new one.

You can also sign in with Google, in which case there is no password on our side at all: Google identifies you and we receive the confirmation.

Password recovery works through a link sent to your email, with an expiry. While a recovery is half finished, the application takes you back to complete it rather than leaving you halfway with a session in an odd state.

Payments do not pass through us

Your card number, its expiry date and its security code never reach Kanbanly. You enter them on Wompi's form; they process them and they are the ones subject to the payment card industry standards.

What we keep back is only what is needed to show you what was charged and with what: the transaction identifier, the amount, the currency, the status, the card brand and its last four digits. None of that can be used to charge anything.

Every charge carries a unique reference generated by us, and it is that uniqueness which prevents the same period being charged twice. The notices the gateway sends when a charge changes status are signed, and the signature is verified before they are acted on: without that check, anyone who knew the notice address could declare a charge approved that never happened.

Encryption

All traffic between your browser and Kanbanly is encrypted with HTTPS, without exception and with no way to connect otherwise. The same goes for traffic between the application and the database, and between the application and the email and payment providers.

Stored data is encrypted at rest by the database and file storage infrastructure.

The files you upload

Card attachments are stored separately from the database, and the list of attachments on a card is protected by the same access policies as the card itself: whoever cannot open the card does not see its attachments.

There is a size limit per file, and on top of that a cap per plan: a file exceeding your plan's cap is rejected and removed from storage rather than left sitting there taking up space that nobody will look at again.

What we have learned by breaking things

This section is unusual on a security page, and it is here on purpose: we think saying which flaws we have found and how we closed them tells you more about a product's security than a list of adjectives.

The database's default permissions
PostgreSQL lets everyone execute functions unless told otherwise, and the public role the browser speaks through inherits from that "everyone". An audit found around fifty functions left open that way, two of them capable of changing a workspace's plan without paying. They were all closed, and the default permission was changed so that new functions are born closed: fixing the specific case would have left the same hole waiting for the next function.
Comparing against a value that is not there
A check along the lines of "if the owner is not the person asking, stop" stops nothing when there is nobody asking, because comparing with a null value yields neither true nor false. The flaw was found once, explained well, and still reappeared three migrations later in the two functions that move money. They now compare in a way that does distinguish the empty case.
Replacing a function and creating two
In PostgreSQL, "replacing" a function with a different argument list does not replace it: it creates a second one with the same name, and old calls keep going to the old one without failing and without warning. The previous version is now dropped explicitly, and tests check that only one remains.

Limits against abuse

Functions that can generate email from a signed-in account have caps per person and per time window, enforced in the database and not only in the interface. Without them, a form that sends email is an open tap: someone acting in bad faith fills the support inbox and, along the way, burns the provider's quota for everything else Kanbanly sends.

The windows are rolling and do not reset at midnight, precisely so that two full allowances cannot be spent two minutes apart.

How we work

Every change to the database is a numbered migration, with a written explanation of why it is being made and which specific flaw it fixes, and with tests that run against a real PostgreSQL: we check that whoever should not see something does not see it, and not only that whoever should see it does. Checking only the good half is what lets half the flaw through.

The code carries automated tests that run before anything is published. Several of them are guardians: they do not check that something works, but that a rule has not been broken without anyone noticing. Every guardian is broken on purpose when written, to confirm that it fires.

Provider access keys live in server environment variables and do not travel to the browser. The ones that do have to be public are public by design, and behind them stand the database policies that prevent anything improper being done with them.

If you find a security flaw

Tell us. You do not need to be able to explain it in technical terms or to prove anything: if something let you see what you should not have, or simply struck you as odd, we want to know.

Write to us through the application's assistance channel and describe it in as much detail as you can: what you did, what you expected and what happened instead. If you can attach a screenshot, all the better.

We ask two things in return: give us a reasonable window to fix it before making it public, and do not access other people's data beyond the minimum needed to confirm the flaw exists. We are not going to come after anyone who reports in good faith.

What we do not do yet

Being honest about the limits is part of this. Kanbanly does not yet offer two-factor authentication, corporate single sign-on, or compliance certifications audited by third parties. There is no bug bounty programme.

If any of that is essential for your organisation, tell us through Help: knowing who needs it and what for is what decides the order things get done in.