BuildLeague
Log in

Data Security

Last updated: April 14, 2026

This page describes the security and privacy controls that are actually implemented in BuildLeague today. Every claim below corresponds to code in the public repository — you can read the source yourself to verify it. Where something is not implemented or is configured per-instance, we say so honestly. For the legal framing of how we handle your data, see the Privacy Policy.

1. Authentication and sessions

BuildLeague uses Better Auth with a Drizzle adapter on PostgreSQL. You can sign in with email and password or with Google, GitHub, or Discord OAuth.

  • Email verification is required before you can sign in. Verification links are valid for 24 hours.
  • Passwords are hashed by Better Auth before storage. We never see or store your plaintext password.
  • Session cookies are httpOnly, Secure in production, and use SameSite=Lax. Sessions expire after 30 days of inactivity.
  • When an account is banned or deleted, sessions stop working within ~60 seconds — the shortest cache window we believe is consistent with reasonable performance.
  • Account-link guard: if someone tries to link an OAuth account whose email already belongs to a different active user, the link is rejected. This blocks a class of pre-claim hijacking attacks where an attacker registers an email address before the legitimate owner can.
  • CAPTCHA (operator-configured): if the operator of this instance has configured Cloudflare Turnstile, signup, login, and password reset are protected by a CAPTCHA challenge.

2. Automatic redaction of secrets in your content

Teams submit challenge responses and Proof Card evidence to BuildLeague, and that content sometimes contains things it shouldn't. Before any submission or comment is stored, we run it through a redaction pipeline that strips:

  • API keys for Anthropic, OpenAI, Google, AWS, GitHub, GitLab, Slack, Stripe, Hugging Face, xAI, and DigitalOcean
  • JWTs, bearer tokens, basic-auth credentials embedded in URLs
  • PEM and PGP private key blocks, OpenSSH private keys
  • Database connection strings
  • Generic environment-variable assignments and high-entropy strings sitting next to labels like password= or secret=
  • Operating-system user paths and home directories (Windows, macOS, Linux, WSL, UNC shares)
  • Email addresses, public IPv4 and IPv6 addresses, MAC addresses, shell prompts
  • Usernames extracted from any of the above

Redaction runs on every submission you create, every submission you edit, every comment you write, and every export. The original, unredacted text is not retained — the version stored in the database is the redacted version.

Redaction is best-effort. It is not a substitute for being careful with what you share, and we recommend you review your content before submitting.

3. How we protect what you upload

The only files BuildLeague accepts from users are images for avatars, banners, and inline submission content. We are deliberately strict:

  • Allowlisted formats only: JPEG, PNG, WebP, and GIF. SVG, HTML, and XML are rejected because they can carry executable content.
  • Magic-byte validation: we read the actual file header and ignore whatever the upload claims its MIME type is. A file declared as a JPEG that doesn't start with the JPEG magic bytes is rejected.
  • Polyglot defence: after the magic-byte check, we scan the entire file for embedded <svg, <script, and <html markers and reject anything we find.
  • Per-purpose size limits: avatars are capped at 2 MB, banners at 1 MB, inline images at 5 MB.
  • Per-user storage paths: uploads land at {purpose}/{your-user-id}/{random-uuid}.{ext}. Filenames are random UUIDs; the original filename is not preserved.
  • Presigned URLs (operator-configured): if the operator has configured Cloudflare R2 for object storage, image URLs are signed and expire after ten minutes. Otherwise, files are stored on the server's local disk.

What we do not do: we do not currently strip EXIF metadata from uploaded images. JPEG files in particular can carry GPS coordinates, camera serial numbers, and capture timestamps. If this matters to you, please strip metadata from your images before uploading them. We are evaluating whether to strip metadata automatically in a future change.

4. How we protect the platform

  • HTTPS everywhere, with HSTS in production.
  • Trusted-proxy validation: we only honour X-Forwarded-For headers when the immediate connection comes from a proxy on the operator's allowlist. This prevents attackers from spoofing client IPs to bypass per-IP rate limits.
  • Rate limiting: a Redis-backed sliding-window limiter (with an in-process fallback) caps requests per route, per user, and per IP. Authentication routes are limited per-IP and per-email-hash to defeat credential-stuffing.
  • Security headers: Content-Security-Policy with nonces, X-Content-Type-Options, X-Frame-Options DENY, strict-origin-when-cross-origin referrer policy, and a Permissions-Policy that disables camera, microphone, geolocation, and payment APIs by default.
  • Search visibility filtering: the search index carries a visibility marker on every document and filters per-user, so private submissions and evidence only appear in search results for the teams entitled to see them.

5. What we log and how long we keep it

We try to keep server-side logs to the minimum we need to operate the service safely. Concretely:

  • Sessions: each session row stores your IP address and user-agent string. Sessions are deleted automatically 30 days after they expire.
  • Analytics events: aggregate usage data carries a hashed IP (SHA-256 with a salt that rotates daily) and may be associated with your user ID. Events older than 90 days are deleted automatically.
  • Audit log: sensitive actions like verification credential issuance, evidence export, challenge submission, content flagging, and account deletion are logged with the actor's IP and user-agent. Verification audit entries are deleted after 90 days; moderation entries are retained for compliance.
  • Notifications: read notifications are deleted after 30 days; unread notifications are deleted after 6 months.
  • Moderation log: every moderator action (ban, hide, lock, etc.) is recorded with the moderator's identity and the reason they gave. Platform moderators can read this log.

6. When you delete your account

Account deletion is intentionally honest about what we keep and what we remove. When you delete your account:

  • Your username, display name, email, bio, avatar, and banner are anonymized. Your profile becomes a placeholder identifier.
  • All OAuth connections (Google, GitHub, Discord) are unlinked.
  • All your sessions are revoked immediately. You cannot use your account again after this point.
  • Your avatar and banner image files are removed from storage on a best-effort basis.
  • Your comments are kept but de-authored — the text remains, but there is no author attached.
  • Your submissions and match standings are retained on the platform, attributed to the anonymized placeholder. This is so the challenge outcomes and matches other teams participated in stay intact.
  • The deletion itself is recorded in the audit log.
  • Account deletion is not reversible. There is no grace period and no undo. If you want a specific submission or comment removed before you delete your account, delete it manually first.

You can also export a snapshot of your account before you delete it. The export covers your profile, submission titles and metadata, comment text, match standings, and challenge matches. See the Privacy Policy for the exact contents and the things that are not currently included.

7. Source-visible for transparency

The BuildLeague source code is published in a public repository so anyone can read it and verify the claims on this page. This does not mean the code is open source. BuildLeague is proprietary software; the source is visible for transparency only and is not licensed for reuse, redistribution, or hosting by anyone other than the operator. See the LICENSE file in the repository for the full terms, and the Terms of Service for how this applies to the hosted Service.

8. Reporting a vulnerability

If you've found a security vulnerability in BuildLeague, please report it through GitHub's private vulnerability reporting feature on the source repository. Don't open a public issue and don't post about it publicly until we've had a chance to address it. Full disclosure terms, the research safe-harbour scope, and what to expect from us are documented in SECURITY.md in the repository.