Your messages are not ours to read.
TradesMen Messenger is engineered so the operator (us) cannot read your private content. Private keys live on your device. Sensitive admin actions are written to immutable audit logs. Where we still have work to do before public release, we say so directly on this page — no marketing.
What the server cannot read
- Plaintext message bodies
- Plaintext media — photos, videos, files, voice notes
- Voice and video call payloads
- Your private encryption keys
- Contact-invite raw tokens (only HMAC'd hashes are stored)
What metadata we do see
- Account identifiers and your chosen handle
- Public encryption keys and signed pre-keys
- Conversation membership and high-level metadata (size, timestamps, participants)
- Encrypted message records — ciphertext only, with envelope headers
- Reports you choose to send and the reason you submit
- Account email and (optional) phone for recovery, never exposed to other users
Who can see what.
A clear matrix of every data class the platform handles, where it lives, and who can read it. Nothing in this table is hidden by marketing language; if a row reads "operator" that means us, and we list how it's gated.
| Data class | Where it lives | You | Recipients | Operator |
|---|---|---|---|---|
| Message body (text, reactions, edits) | Encrypted on device; ciphertext on server | Yes | Yes | No — never decryptable server-side |
| Attachments & voice notes | Encrypted on device; opaque ciphertext blob in object storage | Yes | Yes | No — server holds the blob, not the key |
| Call media (audio & video) | DTLS-SRTP between participants; relayed via TURN if needed | Yes | Yes | No — TURN relays ciphertext only |
| Private encryption keys | Device only — iOS Keychain or Android Keystore | Yes (via biometric unlock) | No | No — never leaves the device |
| Public keys & signed pre-keys | Server (for delivery routing) | Yes | Yes | Yes — public material only, by design |
| Account email (recovery) | Server | Yes | No — never surfaced | SuperAdmin only, gated by audited operations |
| Account phone (optional recovery) | Server | Yes | No — never surfaced | SuperAdmin only, gated by audited operations |
| Handle & display name | Server | Yes | Yes (when added as a contact) | Yes |
| Conversation membership / timestamps | Server (delivery metadata) | Yes | Yes (own threads) | Yes — minimised but unavoidable for delivery |
| Reports you submit | Server | Yes | No | SuperAdmin reveal flow — written reason + audit log required |
| Push tokens | Server (forwarded to APNs/FCM) | — | — | Yes — payload itself is privacy-safe |
| Contact-invite raw tokens | Returned once at creation; never stored | Yes (until consumed) | Receiver (until consumed) | No — only HMAC'd hash is persisted |
| Audit logs (admin actions) | Server (append-only) | No | No | SuperAdmin read; never overwritten |
What we're defending against — and what we're not.
A clear threat model is more useful than vague claims. Here's the shape of what TradesMen Messenger protects against today, and where the limits are.
What we defend against
- An attacker who compromises the server reading historical messages — ciphertext only.
- An attacker who compromises the server reading historical media — opaque blobs only.
- An attacker observing TLS traffic — everything is on TLS 1.2+.
- An attacker stealing a backup of the database — keys are not in the database.
- A casual operator reading reports — reveal flow requires a written reason and is logged.
- Phishing of the web product — there is no public web user login to phish.
- Push payload leakage — pushes never include sender, recipient, or content.
Out of scope today
- An attacker with full physical control of an unlocked, biometric-bypassed device.
- A targeted attack against an installed app combined with OS-level compromise.
- A motivated attacker correlating who-talks-to-whom across a sufficiently long window — we minimise metadata but not zero it.
- Production-grade end-to-end encryption guarantees on the public store builds — the current builds use a placeholder crypto module gated against release.
The algorithms, not just the buzzwords.
Every "encrypted" line elsewhere on this site maps to one of the rows below. If you're auditing the platform this is the table to start from.
| Use | Primitive | Key / parameter | Notes |
|---|---|---|---|
| Message body / attachment encryption | AES-GCM | 256-bit content key, 96-bit nonce, 128-bit tag | Per-message / per-file content key. Wrapped inside the E2EE envelope to recipients. |
| Identity & signed pre-keys | X25519 (DH) + Ed25519 (sign) | Curve25519 (256-bit) | Per-device. Signed pre-keys carry a fresh signature on every rotation. |
| Symmetric session ratchet | HKDF + AES-GCM | HKDF-SHA-256 → 256-bit AES keys | Forward secrecy on each chain step; placeholder today, vetted Signal/MLS-compatible build before public store releases. |
| Group epoch state | HKDF-derived sender keys | 256-bit per-epoch key | Member-aware: epochs roll on join/leave so old members can't read new content and new members can't read old. |
| TLS in transit | TLS 1.2+ (ECDHE) | P-256 / X25519 + AES-GCM or ChaCha20-Poly1305 | HSTS enabled in production. We never accept plaintext or downgraded ciphersuites. |
| Call media path | DTLS-SRTP | AES-128/256 + HMAC-SHA-1/256 per WebRTC profile | End-to-end between participants. TURN relays the ciphertext only. |
| Password storage at rest | Argon2id | Server-tuned memory / iterations | Per-user salt. Resilient to GPU brute-force. |
| JWT (access tokens) | HMAC-SHA-256 (HS256) | ≥ 32-byte secret (validated at boot) | Strict iss, aud, typ checks. 15-minute access TTL, 30-day refresh TTL. |
| Contact-invite tokens | HMAC-SHA-256 | ≥ 32-byte server-side secret | Server stores only the HMAC'd hash. Plaintext is returned exactly once at creation. |
| Local-storage at rest (mobile) | iOS Keychain / Android Keystore | Hardware-backed where available | Biometric-unlocked. Secure Enclave on iOS; StrongBox on supported Android. |
| Cryptographic randomness | OS CSPRNG | random_bytes / SecRandomCopyBytes / SecureRandom |
Never rand / Math.random. Verified by static checks on every change. |
Device keys, identity, and key change handling.
Per-device key bundles
Each device publishes its own identity key, signed pre-key, and a set of one-time pre-keys. Adding a new device generates a new bundle — your existing devices are not impersonated and cannot be silently replaced.
Safety numbers & key change alerts
Conversations track the public-key fingerprint of the other side. If a peer's key changes — for example after a reinstall or a new device — the app surfaces a key-change banner before it sends the next message, so you can verify before you talk.
Group key handling
Group conversations use member-aware keys with a wrapped epoch state. When members join or leave, keys roll forward without exposing previous content to new members or future content to former members.
Encrypted media uploads
Attachments are encrypted on-device with a per-file content key. The key is delivered inside the E2EE message; the upload itself is opaque ciphertext. No thumbnails, no previews, no scanning on our side.
WebRTC voice and video, with privacy on the wire and in the push.
Encrypted media path
All call media is DTLS-SRTP encrypted between participants. The signalling channel is our authenticated WebSocket. Calls are peer-to-peer where possible; relayed via our coturn server when networks block direct connections.
Short-lived TURN credentials
STUN/TURN credentials are short-lived and bound to the call session. A leaked credential cannot be replayed against an unrelated call later.
Privacy-aware call pushes
Incoming-call pushes include only what's needed to wake the app. Caller identity, recipient identity, and call topic are never embedded in the push payload.
WebRTC handler validation
Server-side WebRTC handler validates call membership for both sender and target on every signalling message, and checks the call is live — preventing replay or off-call signalling abuse.
Operating the service without operating on your data.
A real product needs an operator. The point isn't to pretend nobody runs the service — it's to make sure that running it cannot quietly turn into reading your messages.
SuperAdmin-only console
The admin panel is gated by the SuperAdmin role, an IP allowlist, optional TOTP, and CSRF on every state-changing route. There is no public web user login; ordinary users access the service from the iOS or Android app only.
Audited reveals & bans
Reading reported plaintext, banning a user, exporting a profile, or editing legal documents writes a record into admin_audit_logs with the actor, IP, reason, and timestamp. Reveal flows require a written reason — the panel won't let an admin read content casually.
Privacy-safe reporting
When you report a user or a message from inside the app, you choose what to share. Reports include only the metadata you opt into. The server cannot decrypt reported content without the explicit, audited reveal step.
Force-logout & ban propagation
Bans and force-logouts propagate to every device on the next request. New session refreshes are denied; existing tokens are revoked through a SystemEventService broadcast, not just a UI toggle.
The boring stuff that actually matters.
Rate limiting and abuse controls
Rate limits on auth, contact-invite, and reporting endpoints. Rotating tokens, signed JWTs with strict issuer/audience/typ validation, and revocation paths for compromised devices.
Environment validation at boot
The backend refuses to boot in production with insecure defaults. Required env keys (JWT secret, CORS origins, trusted proxies, contact-invite secret + base URL, push provider settings, E2EE mode) are validated up-front.
Static checks on every change
A static-check runner enforces 90+ privacy invariants on every change — no plaintext columns on the messages table, no plaintext logging, no /api/api routing mistakes, no placeholder-crypto leaks past the production gate, no force-uppercased invite tokens, deep-link manifest sanity, etc.
Retention and deletion
Per-data-class retention policies are enforced by background workers. Account deletion runs on a 7-day grace window before anonymization. Failed jobs surface in the admin dashboard.
Supply chain
Dependencies are pinned with lockfiles and reviewed on update. The mobile apps ship no third-party analytics or ad SDKs. Production push providers (APNs, FCM) are configured via env, not vendored libraries.
Backups
Encrypted database backups; key material is stored separately from data backups so a stolen backup cannot be decrypted in isolation. Backup restore drills are part of the operational checklist.
Where we map to common privacy frameworks.
We're not yet certified against any specific framework. We do build the platform to map cleanly to the principles below — and we're honest about the gap between "designed to" and "audited against."
PIPEDA & PIPA (Canada)
- Accountability: Pimentel Services Ltd. is the operator and contact of record.
- Limiting collection: No phone or email search; no contact-list scraping; no analytics SDKs.
- Limiting use, disclosure, and retention: Per-data-class retention with worker-driven aging; account deletion in seven days.
- Safeguards: E2EE for content, ciphertext-only at rest, audited admin actions.
- Individual access: In-app data export and deletion; written request via /user-data-request.
GDPR (EEA / UK)
- Lawful basis: Contractual necessity for service delivery; consent for optional phone verification.
- Data minimisation: Server stores ciphertext + minimum routing metadata.
- Storage limitation: Configurable retention; default 7-day grace then anonymization.
- Right to access / erasure / portability: In-app export and deletion; written DSAR via privacy contact.
- Data protection by design: Privacy invariants enforced by static checks before merge.
- International transfers: Hosting region documented in the company-tier DPA.
NIST CSF alignment
- Identify: Data classes, owners, and retention policies are documented above.
- Protect: E2EE, biometric local-storage, environment validation, MFA on admin, rate limits.
- Detect: Live status board, immutable audit logs, breach-incident tracking.
- Respond: Documented in /breach-response; on-call paged through internal alerting.
- Recover: Encrypted backups; backup-restore drills; revocable tokens and force-logout.
Industry-aligned data handling
- OSHA / WSIB-style record retention: retention windows configurable on the company tier so safety briefings + incident reports stick around as long as your jurisdiction requires.
- Construction lien / records of work: exports include conversation membership and timestamps; ciphertext bodies live with the device that holds the keys.
- Insurance / claims discovery: SuperAdmin reveal exists for audited legal-process responses, with written reason and immutable audit log.
Compliance certification (ISO 27001, SOC 2 Type II) is on our roadmap but not yet completed. We don't claim a status we haven't earned. Email privacy@example.com if you need our current attestation package.
Where we still have work to do
We don't claim production-grade end-to-end encryption today. The current native builds ship with a placeholder crypto module that's gated against release builds — it cannot be enabled in production unless the production gate is satisfied. Public app-store releases will require a vetted Signal/MLS-compatible crypto module before they ship. The backend is already designed for ciphertext-only storage. For implementation status see E2EE_IMPLEMENTATION_STATUS.md in the repository.
Reporting a security issue
Email security@example.com. Please include reproduction steps, impact, and a way to contact you. We acknowledge reports promptly and will keep you informed through remediation. Coordinated disclosure is welcomed — let us know what timeline you have in mind and we'll work to it.
For privacy questions or data subject requests: privacy@example.com. For everything else: support@example.com.