Privacy & Security
The technical detail of how we secure your account and data, for users who want to actually verify it.
Passwords
Stored as scrypt hashes with a per-user salt
(server-generated, 16 random bytes). Verification is
timing-safe (crypto.timingSafeEqual). We never see
or store plaintext passwords. Minimum length 10 characters;
maximum 128.
After 5 failed login attempts, the account is locked for 15 minutes. After 10, it's locked for an hour. Combined with per-IP and per-email rate limits, brute-forcing one account is effectively impossible.
Sessions
Two parallel mechanisms:
- Opaque session token — 256 bits of entropy, stored in your phone's secure storage (Keychain on iOS, Keystore on Android). Rotated on every login, password change, and password reset.
- RS256 JWT — short-lived (1 hour), signed with a 2048-bit RSA private key kept exclusively in our Convex environment. Public key published at our JWKS endpoint for verifiers.
Sessions older than 90 days are auto-invalidated; you'll need to log in again.
Data at rest
- Primary database: Convex on AWS eu-west-1 (Ireland). Encrypted with AES-256, keys managed by AWS KMS.
- File storage (photos, voice messages): Convex object storage, same region, same encryption.
- Backups: continuous point-in-time recovery, 30-day retention, encrypted.
Data in transit
- TLS 1.3 mandatory, no fallback to older versions.
- HSTS preload on all our domains — your browser refuses to talk to us over HTTP.
- Certificate pinning in the mobile apps for the Convex domain to defend against MITM with a corporate / state-issued certificate.
Uploads
Every uploaded photo and voice clip is fetched server-side after upload, the first 16 bytes are sniffed for magic numbers, and anything that isn't a valid image or audio file is deleted and the carrier message rewritten to a placeholder. This closes stored-XSS via image upload.
Payments
Card data never touches our servers. Stripe Checkout handles the entire payment flow on Stripe's PCI-DSS Level 1 certified infrastructure. We store a Stripe customer id and the last 4 digits of your card; the rest stays with Stripe.
Admin access
Every admin account requires TOTP-based 2FA.
Admin sessions are 12-hour scoped tokens prefixed with
as_ — distinct from user tokens, so a compromised
user session can never escalate.
Audit trail
Every privileged operation — bans, refunds, coin grants, account
deletes, admin enrollments — writes an immutable
auditEvents row with actor, subject, timestamp, and
metadata. Audit logs are retained 90 days for normal review and
indefinitely if linked to an investigation.
Reporting a vulnerability
We follow coordinated disclosure. Email security@skillflirt.com with reproduction steps. We aim to triage within 24 hours and fix critical issues within 7 days. We publicly credit researchers in the next changelog and send swag.
Out of scope: social engineering of our staff, physical attacks, and findings against third-party services we use (Stripe, Convex, Firebase) — report those upstream.
Past audits
We run automated security audits via a published methodology;
the full report for the latest pass is in our repository at
APP_SECURITY_AUDIT.md. Last full audit: May 2026
— 4 passes, 9 findings, all remediated.
Related: Privacy Policy · Privacy Tips