Security

How your data is handled

The short version: security is enforced where your data lives, in the database itself, rather than in the screens in front of it. That's the decision everything below follows from, and it's the one that means a mistake in the interface can't become a data leak.

Permissions live in the database

Every row is guarded by row-level security in Postgres, not by a check in the app. The thing that decides whether you can read a record is the same thing that stores it, so a bug in the interface cannot hand you data you shouldn't see. A second user who tries to read a row they don't own gets nothing, and there is a test that proves it.

API tokens can't outlive your access

A token acts as you, and never as more than you. Its permissions are recomputed from live membership on every single call. If your role is lowered, or you're removed from a base, the token's reach shrinks with it on the next request, with no waiting for a rotation. Tokens are stored only as a hash; the secret is shown once and then exists nowhere we can read it.

Shared links leak nothing you hid

A public view can be read and filtered, but a column you hid isn't merely hidden from the page. It's absent from the query. A visitor can't filter or search by it, so they can't work a value out from how the row count changes. Hidden means gone, not just off-screen.

The audit log can't be edited

Record history is append-only, for everyone, including the owner of the base. Entries are written by the database itself, and there is no path for a person to change or delete one. An audit log its own subject can rewrite isn't an audit log.

Webhooks are signed, and can't be aimed inward

Every delivery carries an HMAC signature over its timestamp and body, so your receiver can prove it came from us and reject a replay. And a webhook can't be pointed at a private address. The target is checked against where it actually resolves, which is what stops it being turned on your own network.

Files are private by default

Attachments live in private storage. The link to a file is signed at read time and expires. We never store a public URL, because a stored one outlives every permission change you make afterwards. Who can open a file is decided by the same row-level rules as the record it's attached to.

Found something? Email hello@swampy.app. We'd genuinely rather hear it from you than not. See also our Privacy Policy.