Start free

Security

Last updated 2026-07-26

The core idea: enforcement lives in the database, not the app

Application code is the layer most likely to have a bug in it. BalanceMCP's tenant boundary — the rule that your books are yours and no one else's — is enforced by PostgreSQL row-level security policies on the tables themselves, not by a check an application function has to remember to run correctly every time. Every query, whether it comes from the web app or a tool call over MCP, runs against the database under that enforcement.

A connection that could bypass that protection is refused

Row-level security only holds if the database connection can't bypass it — and some administrative connections can, by design, at the database level. BalanceMCP checks the privileges of its own connection on first use and refuses to serve any request at all if that connection could bypass row-level security. That check is not skippable in production; the only escape hatch is for local development and is itself disabled whenever the server thinks it's running in production. A misconfigured deployment fails closed — it serves nothing, rather than silently serving one user another user's books.

API keys are stored as hashes

The API key an AI assistant uses to connect is shown to you once, at creation. What BalanceMCP stores afterward is a cryptographic hash of it, not the key itself — the same principle as how a password ought to be stored. If our database were somehow read by an attacker, there is no key value sitting in a column to steal and reuse.

A key is all-or-nothing today: any valid key can call any of the 19 tools, including the ones that post, reverse, or lock entries. There is no read-only or scoped key yet. Revoking a key takes effect immediately and stops it from authenticating anything further.

Every tool call is written to an append-only audit log

Every authenticated call to a BalanceMCP tool — successful or not — writes a row to an audit log: which tool, when, whether it succeeded, and its arguments with anything that looks like a key, token, or password automatically redacted before being written. That log is append-only by database trigger: update, delete, and even a bulk truncate are all rejected at the database level, for everyone, including us. It is derived from what actually happened, in its own transaction after the tool call settles — not a self-report the tool call could fake.

Requests are rate-limited per key

Each API key is limited to a bounded number of requests per minute, counted in the database rather than in the memory of any one server, so the limit holds regardless of how the request happened to be routed. Going over it returns a clear rate-limit response rather than silently queueing or dropping the request.

What we are not claiming

We are being deliberately specific above because it is easy to overstate security, and worse to be wrong about it after the fact. We do not claim encryption of data at rest beyond whatever the underlying hosting provider does by default. We have not obtained SOC 2 or any other certification. We have not had a third-party penetration test performed. If any of that changes, this page will say so plainly, with specifics — not with a badge.

Reporting a problem

If you believe you've found a security issue in BalanceMCP, we want to know before anyone else does. Describe what you found and how to reproduce it, and give us a reasonable window to fix it before discussing it publicly.