Security
Written for someone deciding whether to trust us with a roster of children. It says what exists, and what does not.
Access is enforced in the database, not in the app
Every table carries row-level security, and the rules run inside PostgreSQL. That matters because it means a bug in a screen, a mistyped query, or a request crafted by hand cannot return rows the signed-in person is not entitled to — the check is not in the code that asks the question.
Operations that need more privilege than a caller has — redeeming an invite, deciding an application, assigning equipment — run through named database functions with the authorisation test written into each one. There is no general-purpose write path that a client can reach around them.
Elevated server keys are not embedded in the mobile app, the application site, or the operator console. A copy of any of those clients grants no more access than the person holding it already has.
Who can see a child's details
An instructor sees the families in their own programs. A guardian sees their own household. Neither can read across into another program or another family, and that boundary is the database's, not a screen's.
An organisation can appoint coordinators. A coordinator can read across the programs their organisation runs — rosters, applications, payment status — because that is the job. The grant is deliberately read-only: coordinators hold no write permission on any of it, and they cannot read a child's individual attendance record. Attendance reaches them only as aggregate figures.
Membership is never silently deleted. When someone's access is revoked, the record of what they could once see is kept, so the question "who could read this roster in March" has an answer.
What we ask the device for
The mobile app requests exactly one permission: notifications. It does not ask for location, camera, microphone, contacts, or the photo library, and the libraries that would provide them are not installed.
Session tokens are held in the device's secure storage rather than ordinary app storage. All traffic is over HTTPS.
Abuse and rate limits
Invite codes are eight characters, generated with a cryptographic random source, and exclude characters that are easily confused. Redemption is limited to five attempts per fifteen minutes. Application submissions are rate limited separately.
Email that Sessionly sends is read-only to families: there are no action links in it that change anything on a click. Anything that alters a roster happens behind a sign-in.
Deletion, stated accurately
This is the part most often overstated, so: erasure anonymises rather than deleting outright, and it can refuse.
A household that erases its account keeps its erasure — names, contact details and application content are removed. The instructor's attendance history survives with the child's name taken off it, because a register that silently loses rows is not a register. Where another guardian is still active, the enrolment is handed to them rather than destroyed.
Deletion is refused, with an explanation, while the person asking still owns a program that is running. Sessionly will not strip a live class away from the families enrolled in it as a side effect of one person closing their account.
The Privacy Policy sets out field by field what is removed and what remains.
Who else processes your data
Sessionly runs on Supabase (database, authentication), sends email through Resend, delivers mobile updates and notifications through Expo, reports crashes to Sentry, and serves its websites through Cloudflare and Vercel. There is no advertising network and no analytics broker; nothing here is sold or shared for marketing.
Encryption at rest is a property of the managed database platform rather than a control this codebase implements, and we describe it that way on purpose.
How we verify all of this
The access rules are covered by an automated suite that asserts them as behaviour: it signs in as each role and checks what they can and cannot read, including cases that were once bugs. Object counts and the bodies of privileged database functions are pinned, so an authorisation check cannot be removed quietly — a change to one is a reviewable change to the expectation as well.
The database schema is rebuilt from scratch and re-tested on every change, so what is documented is what a fresh install actually produces.
What we do not have
No SOC 2, ISO 27001, or comparable certification. No third-party penetration test. No bug bounty. No 24-hour monitoring, and no contractual uptime commitment. Sessionly is a small operation and these would be untrue if claimed.
If your organisation needs a data processing agreement, a security questionnaire completed, or terms beyond what is published here, write to us and we will work through it properly rather than pointing at a page.
Reporting a vulnerability
Email ops@usesessionly.com with enough detail to reproduce the issue. You will get an acknowledgement within two working days.
Please do not access, modify or retain data belonging to anyone else, and give us a reasonable chance to fix the problem before describing it publicly. We will not pursue you for a good-faith report made on those terms. There is no bounty programme; we will credit you if you would like to be credited.