It is your database, in your account, under your policy.
Podium deploys to a Cloudflare account in your name. Nobody at this project can read your speakers' data, because nobody at this project has it. Below is what the software does with it, and at the bottom, what is currently wrong.
Redaction is the default, not a feature.
Adding a field means deciding whether it is personal data before it can ship. That is a rule with a check behind it.
Redacted unless asked for, everywhere
Email addresses, phone numbers, dietary and accessibility notes, travel details, and any answer a form marks personal need their own permission. Without it they are removed, including from a scope that can already read the proposal.
Never in a log line
Application logs carry identifiers, not values. A person is an id, so an email address cannot leak from a log aggregator, a crash report or a support screenshot.
Never in the published schedule
The public snapshot carries titles, times, rooms, speaker names and bios. Personal fields are not redacted from it at render time; they never enter it.
Two things nobody can unlock
Private notes about a person are never visible to that person, and neither is the review data for a proposal they submitted or are credited on. No permission lifts either.
Export and erasure are features, not a support process.
A speaker asking to be removed should not cost you an afternoon and a query you write by hand.
Export
Any person pulls everything held about them as JSON, plus their uploaded files, from their own portal. Nobody has to ask you, and you do not have to build it.
Erasure that reaches the copies
Erasure anonymises the record, clears the profile and deletes the files, keeping the proposal and session content they authored. It also deletes their rows from every linked external table, paused ones included. An erasure that stops at your database is not an erasure when half the programme is mirrored into a spreadsheet.
Anything that cannot be honoured is said out loud
If a provider refuses a delete, the link is left in an error state and surfaced. Nothing reports an erasure that did not fully happen.
Retention with a number on it
Abandoned drafts are purged after an event closes, two years by default. Audit and event logs are kept for seven years, personal data redacted after two.
Six properties the rest of it rests on.
Permissions are recomputed on every request
Nothing caches authority. A revoked role takes effect on the next request, not the next sign-in.
Blind review is enforced by refusal
In a single- or double-blind round, identity is withheld, not hidden with CSS. A reviewer with a declared conflict does not get a warning; the score is refused.
API keys are narrow and mortal
Scoped to named events, read separated from write, personal data its own additive scope, an expiry, a rate limit, and a secret shown once. A key for your marketing site cannot read a review.
Credentials are references, not values
An integration stores the name of a secret, never the secret. It is resolved at call time, which keeps a provider token out of the settings screen, the audit log and any export.
Everything that matters is audited
Who did what, when, from where. Append-only, and surviving an erasure with the identifier alone. Overrides and waivers carry a written reason, so a proposal accepted below quorum has a sentence beside it a year later.
One writer for the schedule
Two organizers dragging the same slot queue behind one another instead of interleaving. A stale edit anywhere else is refused with the current state.
One thing that is currently wrong, and the fix.
The implementation notes carry it in more detail. It is on this page because a security page that lists only strengths is one nobody should believe.
Password hashing is deliberately below the recommended work factor on Cloudflare's free plan, which allows ten milliseconds of processing per request. The correctly-sized hash costs about 345 ms and made signing in fail outright. What ships is roughly two orders of magnitude cheaper to attack offline than the recommendation.
The fix is one line and a plan change: move to the paid Workers plan and put the parameters back. Stored hashes are carried up the next time each person signs in, so there is no migration and no reset.
Two things reduce what it is worth to an attacker. Passwords are optional and off by default in production, with invitations the intended route in, and there is no password reset form to farm. It is still the first thing to fix on a deployment holding real speakers.
What this project does not have
- No SOC 2, ISO 27001 or any audited compliance programme
- No third-party penetration test
- No security team, and no disclosure SLA — report it in the issue tracker
- No hosted service, so no vendor to answer your questionnaire
What you have instead
- The whole source, and the specification it was built against
- Every rule written down before the code, and checked against it in CI
- Your own account, your own region choices, your own backups
- The ability to have your own people review it before you deploy it
The implementation notes, including that disclosure in full →
Read it before you run it.
Every rule on this page is in the model, and every enforcement of it is in the code, in the same repository.
