Last updated · 2026-05-06
Security
What we hold, how it's protected, how to report a vulnerability.
At a glance:Squarekin is a free expense-splitting tool operated by Squarekin LLC. The data we hold is the bare minimum needed to run the service — emails, group / expense / settlement records, optional receipt images, and (only if you opt in) your live trip location for up to 7 days. We don’t sell or share that data with advertisers, and we don’t use it to profile you.
1. Data we store
- Account — email, optional name, bcrypt password hash (or passkey credential id), 2FA configuration if you enabled it, account preferences.
- Groups + expenses + settlements — what you enter to use the app. Visible only to you and the other group members.
- Member contact details — email (always), name + phone (only when you provide them, e.g. via the device contact picker).
- Family / household member rows— display name, adult/child tag, optional age, optional “is head of household” flag. Created by you for Home / family groups so the app can label expenses with the actual payer (a 16+ dependent can be picked in the “Paid by” dropdown). Anyone aged ≤18 is auto-classified as a child and can never be marked head — enforced both client- and server-side.
- Optional receipt images — stored on the server under
/app/data/receipts; magic-byte sniffed at upload to reject spoofed file types. - Live location(opt-in only) — your last-known lat/lon while you have “Share my live location” enabled on a trip group. Pruned daily after 7 days of inactivity.
- Server logs — IP addresses are hashed (sha-256) rather than stored raw. Plain logs retained 30 days.
2. How it’s protected
2.1 In transit
- HTTPS + HSTS for every request.
- Tight Content Security Policy (frame-src, img-src, connect-src locked down per third-party).
2.2 At rest
- Passwords stored as bcrypt hashes (never plaintext; never logged).
- Magic-link tokens stored as sha-256 hashes — a leaked database can’t replay pending links.
- Session cookies are httpOnly + Secure + SameSite=Lax. Trusted-device cookies are stored as sha-256 hashes only.
- Music-service OAuth tokens (when used) are AES-256-GCM encrypted at rest.
- SQLite database, backed up nightly to the host volume.
2.3 Authentication
- Magic-link is the only way to claim a placeholder account. When someone is invited by email and a placeholder User row is created, signup-with-password cannot claim that email — only the magic-link consume flow can, since it proves the user controls the inbox. This blocks the email-takeover vector.
- Login brute-force lockout (failed-attempt counter + temporary lock).
- 2FA (TOTP / email / SMS) supported. Required on admin tier.
2.4 Authorisation
- Every group / expense / settlement / waypoint / member-location API checks that the caller is a member of the relevant group before reading or writing.
- The /api/users/exist lookup (which powers the “✓ On Squarekin” badge in the Contact Picker) is auth-gated, capped at 50 emails per call, rate-limited at 200 lookups per user per day, and returns booleans only — never names or other PII.
2.5 Input validation
- Lat/lon ranges checked on every write so a stuck client can’t poison the live-trip map for the rest of the group.
- Currency / travel-mode / waypoint-name validation lives in a single shared helper (
lib/edvd-validation.ts) so rules can’t drift between routes. - XSS-safe HTML escaping in all dynamically-built map markers and email templates.
- Receipt uploads are magic-byte sniffed at the server so a renamed
.execan’t be mis-classified as an image.
2.6 Rate limiting + abuse
- Per-user-per-day caps on every external-API surface: 50 invite emails, 30 attractions lookups, 60 route lookups, 120 geocodes, 200 user-exist checks, 30 destination suggestions.
- Cron-only endpoints (invite reminders, location pruning) gated by
CRON_SECRETwith constant-time comparison.
3. What we don’t do
- We do not sell personal information.
- We do not share data with advertisers or trackers.
- We do not run third-party analytics or pixels.
- We do not retain raw IP addresses beyond 30 days; only the sha-256 hash is kept for unique-visitor counts.
- We do not enrol you in profiling or automated decisions that have legal effect.
4. Reporting a vulnerability
Email security@squarekin.com with as much detail as you can share — affected URL, repro steps, suspected impact. Please do not run automated scanners against the production endpoints; if you need to validate a finding, ping us first and we can grant a sandbox account.
We aim to acknowledge within 72 hours and patch validated vulnerabilities within 14 days for HIGH/CRITICAL findings, 30 days for MEDIUM. We don’t currently offer a paid bug bounty, but we’ll credit reporters who request it on the next changelog.
5. Continuous safeguards
- GitHub Actions CI runs Trivy filesystem + image scans for HIGH / CRITICAL CVEs on every push, plus Prisma schema validation, typecheck, unit tests, and pa11y accessibility audit. A failed scan blocks the merge.
- Dependencies tracked by Dependabot — security PRs are reviewed and merged on a rolling basis.
- Production deploys are container-based (distroless runtime image, non-root user) with health-check verification and automatic rollback if the new container fails to come up.
6. Changes to this page
We’ll bump the “Last updated” date and post a short changelog at the top of any meaningful update. For minor wording fixes we just edit in place.
