Skip to content

Security

How the data is kept

What the code does to protect a workspace's data. Every sentence here names a mechanism that exists in the product today, not a policy. The privacy notice says what is held and who else receives it; this page says how it is kept.

Where the data lives

Everything in the product is rows in one Postgres database, run by Neon in Frankfurt, Germany (eu-central-1). Files are the exception, and they have their own section below.

Every table carries row level security policies, and the application connects as a role that cannot see past them. Each request runs inside a transaction that names the signed-in person, and the policies decide which rows that person's workspace membership allows. A workspace cannot read another workspace's rows even when the application asks for them, because the database, not the application, answers.

Money is a further permission on the same policies. A member without fee visibility cannot read a fee, a commission or a net figure at any layer, and a member's access to each module (diary, paper, contacts, promos, money) is none, read or write, enforced on writes by the same policies.

Double bookings

The bookings table carries an exclusion constraint: one artist cannot hold two confirmed or contracted bookings on overlapping dates. It is refused by the database before any screen can show it. Holds are exempt, because several holds on one date is what a hold is for, and the diary marks the clash instead.

Files and links

Every file lives on Cloudflare R2 and is never public: PDFs, riders, audio masters and their streaming versions, artwork. The browser reaches a file through an address the server signs for that request; a signed address to read a file lasts five minutes, and one to upload a file lasts ten. No credential for the storage ever reaches a browser.

A link to a contract, an invoice, an advance sheet, a release or a demo inbox is a token. Only its hash is stored, so a copy of the database yields no working link. Each link is addressed to one recipient, it expires (a contract link after thirty days, an advance sheet after sixty, a demo submission after fourteen), and it can be revoked from the booking or the release, which takes effect on the next request.

The audit log

Every contract and money event writes an audit row in the same transaction as the change: who did it, what it was, and the record before and after. A contract created, sent, signed, countersigned or voided; an invoice created, issued, paid or voided; a fee, a commission, a bonus or a cost set or changed. Membership changes, workspace settings and account deletion are audited the same way.

Signing in

Sign in with a password or with a six-digit code sent to your email address. A code is stored only as a hash, good for ten minutes, with at most five codes an hour to one address. A password is at least ten characters, stored only as a salted hash (scrypt), and refused if it has appeared in a known data breach: only the first five characters of its SHA-1 are sent to the breach list, never the password. An address is proved by a code before an account can be used, password attempts are limited per address, and setting a new password signs out every other session. A passkey (Face ID, a fingerprint, a device PIN or a security key) can be added on a signed-in session; we keep only its public key, and the private key never leaves your device. Two-step sign-in can be turned on in Your account: then a password or an email code also asks for a code from an authenticator app, or one of ten single-use backup codes, and the app's secret is stored encrypted. A session lasts thirty days and is refreshed once a day while it is used, and signing out ends it on the server.

In the browser

Every page is served with a content security policy that allows scripts from the product's own origin only, refuses to be put in a frame by any site, and sends no referrer, so a link token in an address is never handed to a third party by the browser. The site is served over HTTPS only, with the strict transport header set for two years.

POPIA

The responsible party is Under Bridges Entity (Pty) Ltd, and its Information Officer is Joseph Mbedzi. A workspace is the responsible party for the people in its own contact list, and the product gives it the means to answer them: every contact's record can be exported as a file or erased from the contact page, an owner or admin can download everything the workspace holds as personal data from settings, and any member can delete their own account from settings.

Every company that receives personal information on the product's behalf, and what each one receives, is named in the privacy notice.

Reporting a problem

If you find a security problem, write to info@underbridges.co.za with what you found and how to reproduce it, and leave other people's personal information out of the message. The address reaches the Information Officer directly, not a queue.