Security Practices
Access control
Access to production systems is granted on a need-to-know basis. The number of people who can read raw profile data is small and every access is logged.
- Administrative access requires a real account; there are no shared service-account credentials handed around
- All administrative actions are logged so we can reconstruct what happened after an incident
Authentication & sessions
We do the obvious things, but they're worth saying out loud:
- Passwords are hashed with bcrypt before storage. We never store a password we can read
- Session cookies are signed JWTs that reference a database row checked on every request — revoking the row kills the cookie instantly, even mid-session
- Password reset revokes every active session for the account, on every device, immediately
- Email verification is required before a user can upload documents or generate a paid report; an unverified squatter who guessed someone else's email can browse the marketing surface but cannot transact
- Optional Google or Apple SSO via OAuth, with the provider's email-verification claim treated as authoritative
Code & deploy
Every change goes through CI before reaching production:
- Type-check, unit, integration, end-to-end (Playwright), and accessibility (axe-core) checks on every pull request
- Production deploys are reproducible from a single git commit — no manual file edits on the server
- Secrets (API keys, database credentials, session signing key) live in restricted-permission env files on the server, not in the git repository
- Code review is required for any change touching authentication, authorization, or data-access paths
Reporting a vulnerability
If you find a security issue, please email security@collegesignal.ai. We aim to acknowledge within 72 hours and to coordinate disclosure under a reasonable embargo while we ship a fix. We do not currently run a paid bug-bounty program, but we credit good-faith reports in any post-mortem we publish.
Data isolation
Every student's data is scoped to its account at the application's data-access layer. Reads and writes are filtered by the calling user's identity on every query — a logged-in user can only ever load students they own or were explicitly invited to via a single-use, email-matched token. Cross-account access is impossible by construction, not merely by policy.