Security at boopr

A technical overview of how we protect your content. Posts and photos are encrypted on your device before they reach our servers; the keys never leave your phone.

Overview

We can't read your posts, and here's why.

Everything you create on boopr is encrypted on your device before it reaches our servers.

Your private keys never leave your device. We rely on @noble/curves and @noble/ciphers on the client and the Go standard crypto package on the server. We don't write our own primitives.

The server only ever stores ciphertext. Decryption happens on your friends' devices, because we don't hold the keys.

XChaCha20-Poly1305 Ed25519 X25519 HKDF BIP39
Threat model

What boopr protects against, and what it doesn't.

boopr's encryption stops a specific list of attacks. Server-side compromise of post or message content. Passive surveillance by a network-level adversary or our own staff. Ad-tech and data-broker tracking of what you said, who you said it to, or what photo you sent. Casual snooping by anyone reading our database.

It doesn't try to stop everything. A compromised device reads your messages because the keys live on the device. A friend who screenshots a boop and forwards it on another app has done so manually; we notify the sender, but we can't undo it. Someone with physical access to your unlocked phone has the same access you do. Social-engineering attacks against you or your friends are out of scope. And we still hold metadata about who posted when and who pulled the post; under valid legal process, that metadata can be compelled even though the content can't.

The limits are part of the design.

Encryption Protocols

How every layer is protected

Posts & Feed

Per-post key encryption

Random content key per post, wrapped individually for each recipient via ECDH.

  • Random 32-byte content key K generated per post
  • Content encrypted with K via XChaCha20-Poly1305
  • Ephemeral X25519 keypair per recipient; ECDH + HKDF derives wrapping key
  • K sealed to each friend individually; recipients can't see each other's keys
Profiles

Deterministic key derivation

Profile data encrypted with key derived from signing key seed via HKDF. Version increments on friend removal, revoking access.

Media

Client-side processing

Images validated via magic bytes, re-encoded to JPEG (strips EXIF), resized, encrypted with XChaCha20-Poly1305 before upload. Server never sees unencrypted media.

Key Management

Your keys. Your device. Your control.

Recovery

BIP39 recovery phrase

24-word phrase (256-bit entropy) generated at signup. Ed25519 signing key derived from seed. X25519 encryption key derived via HKDF-SHA256. Profile key derived with version parameter for rotation. One phrase recovers everything.

At Rest

Hardware-backed storage

iOS Secure Enclave (P-256 ECDH + AES-GCM key wrapping) and Android StrongBox/TEE (AES-256-GCM in KeyStore). 5-phase system: hardware KEK wraps a DEK, DEK derives MMKV encryption key and Keychain wrapping key via HKDF. Four sensitive Keychain services (identity keys, recovery phrase, Signal sessions, prekeys) additionally wrapped with XChaCha20-Poly1305.

Biometric Gate

Auth-required hardware keys

On Android API 30+, hardware key operations require per-use biometric authentication via CryptoObject binding. Frida bypass of the UI gate is insufficient; the TEE/StrongBox itself enforces authentication. iOS uses Keychain access control with passcode requirement.

Trust

Identity key pinning (TOFU)

Identity keys are pinned locally on first contact. If a friend's identity key changes, the session is blocked until the user explicitly acknowledges the change. Prevents silent key substitution attacks.

Revocation

Profile key rotation

Version increments on friend removal. New key derived via HKDF with updated version parameter, profile re-encrypted and redistributed. Old friends cannot decrypt new profile data.

Key hierarchy
Secure Enclave / AndroidKeyStore

P-256 ECDH self-agreement (iOS) or AES-256-GCM key (Android).

Key never leaves hardware. Not extractable, not exportable.

KEK Key Encryption Key

Derived via HKDF-SHA256 from hardware key agreement.

Exists only in hardware-protected memory during operations.

DEK Data Encryption Key

32-byte random value, wrapped by KEK, stored in Keychain/SharedPreferences.

Unwrapped into memory on app launch, cleared on background.

MMKV encryption key

HKDF(DEK, "boopr-hardened-mmkv-key-v1") → 16-byte key.

Encrypts local cache (feed, connections, profiles).

Keychain wrapping key

HKDF(DEK, "boopr-hardened-keychain-wrap-v1") → 32-byte key.

XChaCha20-Poly1305 wraps 4 Tier 1 services: identity keys, recovery phrase, Signal sessions, prekeys.

Biometric gate Android v2

setUserAuthenticationRequired(true) on KeyStore key.

TEE/StrongBox enforces auth per crypto operation.

UI bypass (Frida) insufficient — hardware validates HAT.

Transparency

What we can and cannot see

What we can see

  • That an account exists, identified only by UUID
  • Public keys: Ed25519 and X25519
  • Encrypted blobs we cannot decrypt
  • When content was sent, not what it said
  • That two users are connected
  • Salted HMAC of IP for abuse detection (raw IP is never stored; the salt rotates monthly so the hashes can't be correlated across months)
  • Subscription status, via Apple and Google
  • The invite chain: who invited whom
  • Block relationships
  • Report metadata: reason, description, user IDs. Never report content.
  • Account trust scores derived from metadata
  • Moderation action history: warnings, bans
  • Platform type: iOS or Android
  • That a screenshot event happened, not the screen itself

What we cannot see

  • Post content, captions, or comments
  • Profile details (name, bio, avatar, birthday)
  • Photos and videos
  • Audience names or membership
  • Mute lists
  • Screenshot narc notification content
  • Push notification content (content-free pings only)
  • Recovery phrase (never transmitted)
Retention & legal process

What we keep, for how long, and what happens with a subpoena.

We retain only what we need to run the service. Raw IP addresses are never stored: they live in memory for rate-limiting and are cleared within 24 hours, never written to disk or logs. For abuse detection we store a salted HMAC of IP (not the IP itself), and the salt rotates monthly so the hashes can't be correlated across months. Encrypted backups can persist as ciphertext for up to 90 days after deletion (we still can't decrypt them). Push device tokens roll off after 90 days of inactivity. Subscription records stick around for three years after cancellation because California's Automatic Renewal Law requires it. Account data, profiles, posts, and media are removed within 30 days of account deletion.

Under valid legal process (subpoena, court order, or search warrant under ECPA and the SCA), we can hand over: account UUID, public keys, account creation date, the platform you're on, and the encrypted blobs we hold. We cannot hand over IP addresses (we don't store them), post content, captions, photos, profile fields, friend lists, or audience membership, because those are either never stored or encrypted with keys we don't have. Where law allows, we notify affected users; if a gag order applies, we don't.

A full encrypted-data export feature is on the post-launch roadmap. When it ships, the bundle will be encrypted with your recovery phrase and decryptable on a new device or independently if we ever shut down, using documented standard primitives (XChaCha20-Poly1305, Ed25519, X25519, HKDF) rather than a proprietary container. Until then, your 24-word recovery phrase already restores your account, posts, and profile on a new device.

Starting on the one-year anniversary of public launch (August 15, 2027), we'll publish an annual transparency report listing the number of legal requests received, the categories of data turned over, and the number of users affected. The full process and retention rules live in our Privacy Policy.

Implementation

Libraries we actually use

Operation Algorithm Implementation
Signing keys Ed25519 crypto/ed25519 + @noble/curves
Encryption keys X25519 crypto/ecdh + @noble/curves
Symmetric encryption XChaCha20-Poly1305 golang.org/x/crypto + @noble/ciphers
Key derivation HKDF-SHA256 @noble/hashes/hkdf + crypto/hmac
Password hashing Argon2id golang.org/x/crypto/argon2
Random generation CSPRNG crypto/rand + crypto.getRandomValues
Screenshot narc ECDH + XChaCha20 @noble/curves + @noble/ciphers
Recovery phrase BIP39 (256-bit) @scure/bip39
iOS key wrapping P-256 ECDH + AES-GCM CryptoKit (Apple)
Android key wrapping AES-256-GCM AndroidKeyStore (StrongBox/TEE)

We don't write our own crypto. The libraries above are the reference implementations maintained by their authors. We have not yet commissioned an external audit of how we use them; the surface is small (we don't custom-implement primitives), and we plan to commission a formal third-party review within twelve months of public launch.

Security Practices

Other guarantees

Keys and nonces come from CSPRNGs: crypto/rand on the Go backend, crypto.getRandomValues on the client. Math.random never touches security-relevant code. Signature checks, HMACs, and TOFU identity comparisons run in constant time so an attacker can't learn bytes from response timing. Private keys and symmetric keys sit in Uint8Array buffers we zero out as soon as the operation finishes.

Photos are re-encoded on your phone before encryption, which drops GPS coordinates, camera model, capture time, and the rest of the EXIF block. The server never sees the original file. Push notifications carry a routing ID and nothing else: no sender name, no text, no thumbnail. Your device composes the notification text locally after it decrypts the payload.

Every HKDF derivation uses a distinct versioned info string (boopr-post-recipient-key, boopr-screenshot-narc-v1, and so on) so the same seed never produces the same subkey twice.

Responsible disclosure

Bug bounty program

If you find a vulnerability, email [email protected]. PGP key on request. We acknowledge new reports within 48 hours and complete initial triage within 7 days. Critical issues we aim to fix within 7 days, high severity within 30, medium and low within 90.

In scope: the iOS and Android apps, the backend API and infrastructure, and this site. Out of scope: third-party services we depend on (Apple, Google, our hosting provider), social-engineering attacks against the team, denial-of-service attacks, and findings from automated scanning that don't show a real impact path.

We don't yet have monetary bounties; we're a small self-funded team. Reports that lead to a fix get public credit on this page (with your permission), boopr+ for life, and merch. Monetary rewards will roll in as the app's revenue does. We will never threaten legal action against good-faith research, and we don't enforce non-disclosure beyond a coordinated 90-day window.

Questions about our security?

[email protected]
App Store Aug 15, 2026
Google Play Aug 15, 2026