What We See About You, What We Don't, and Why It Matters
Most privacy companies ask you to trust them. Ciphera is built so you don't have to.
That sentence is easy to write and impossible to prove — unless you open the architecture. This post is a transparency accounting: not a privacy policy written by lawyers, but an engineer's breakdown of exactly what our systems see about you, what they don't, and the architectural reasons for both.
We're publishing this because the privacy industry has a credibility problem. Too many companies say "we don't sell your data" while quietly logging everything you do. 27% of organizations have now banned generative AI tools entirely (Cisco, 2025) — not because the technology is broken, but because they couldn't verify the privacy claims. We'd rather you verify ours. Here's everything.
TL;DR:Ciphera uses OPAQUE (RFC 9807) authentication — your password never reaches our servers in any form. Email addresses are stored in a client-encrypted vault that operators cannot decrypt. Pulse analytics uses daily-rotating session hashes with no cookies and no persistent visitor tracking. Transactional emails are composed in memory, sent through self-hosted infrastructure, and forgotten — we log only delivery metadata, never the message body or destination address.
Updated June 2026: Ciphera ID migrated from SRP-6a to OPAQUE (RFC 9807). Your password still never reaches our servers in any form — OPAQUE strengthens the guarantee by never disclosing even your salt, closing SRP's pre-computation loophole. This post has been corrected in place to describe the live system; the update history at the end records exactly what changed.
Why "We Promise Not to Look" Isn't Good Enough
Every privacy-focused company makes a version of the same claim: we protect your data. But there are two structurally different kinds of claim, and they aren't equally trustworthy.
Class 1: "We promise not to look." This is what most companies offer. They have full technical access to your data but commit, via policy and terms of service, not to misuse it. The weakness: policies change, employees make mistakes, acquisitions happen, and subpoenas arrive. A promise not to look is only as strong as the weakest person who has access.
Class 2: "We can't look — even if we wanted to." This is what Signal, Proton, and Bitwarden offer. The architecture eliminates certain categories of access entirely. If an insider goes rogue, if a government demands data, if an acquisition transfers ownership — the architecture prevents exposure because the capability to access doesn't exist.
Ciphera is a Class 2 company. Not because we're more virtuous, but because we believe the engineering cost of eliminating access is worth more than the operational convenience of having it. When we can't see your data, we don't have to ask you to trust that we won't.
The rest of this post is the specific accounting of where we've achieved that structural inaccessibility — and where we haven't yet.
What We See About You
Here's the honest list. Every item is something at least one Ciphera system has access to at some point during your usage.
| What we see | When | How long | Why |
|---|---|---|---|
| Your email address (3 handling contexts) | See Why → | See Why → | • Plaintext, briefly — in our notification service's process memory during SMTP composition, milliseconds then garbage-collected; SMTP requires a plaintext destination and there is no workaround. • Encrypted at rest — AES-256-GCM in the notification service database with an operator-held key, kept until you delete your account so we can send future emails without asking you to re-enter it. • Blind index — a PBKDF2-SHA256 hash (1,000,000 iterations) computed on your device, stored in the auth database until account deletion; irreversible — we can check "does this hash exist?" but cannot reverse it to an email. |
| Your account UUID | Always | Lifetime of account | We need an identifier to route authenticated requests. The UUID contains no personal information. |
| Notification metadata | When a notification event is created for your workspace (billing alert, security event, team activity, etc.) | Per-category two-phase retention — read items purge in 3–90 days, unread items in 14–180 days depending on type; nightly purge job with a PII-free audit summary. | Event payload follows a typed schema — only allow-listed fields (UUIDs, amounts, enum codes) pass the write-path validator; email addresses, IP addresses, and raw URLs are structurally rejected. Each user has their own receipt row with per-user read/dismiss state and a per-channel delivery log (in-app, email, webhook). Dismiss is a hard DELETE of your receipt — no soft-delete column exists in the schema. Cascades on account delete via a pre-scrub call from the auth service plus in-DB FK cascades. |
| Hashed IP and device display hint | When you log in | Bounded by token expiry (30 days for refresh tokens) | Used for new-device security alerts. IPs are stored as user-specific HMAC hashes (irreversible). User-Agents are parsed to display hints like "Chrome on macOS" before storage — raw User-Agent strings never exist in the database. Applies consistently across audit log, refresh tokens, and known-device tables. |
| Pageview events (Pulse-tracked sites) | When you visit a site using Pulse | 6 months default for new sites (site owners can opt up), then auto-purged | Page path, referrer, country-level location only by default (site owners can opt up to region/city), device type, browser, OS, UTM params — with a daily-rotating session ID. Raw IP is discarded immediately after hash computation. |
| Subscription metadata | For the duration of your subscription | Lifetime of account | Plan name, billing status, dates. Card details are handled by Mollie — we never see card numbers. |
| Aggregated daily statistics (Pulse) | After daily rollup | Indefinite | Total pageviews, visitor counts, bounce rates, durations — per site, per day. No individual-level data. |
| Observability data | During your interactions with Ciphera services | Prometheus metrics: 15 days. Logs: 30 days. Traces: 14 days. Audit log (security events): 180 days. | Request method, path, status code, and response time — but NOT request bodies, NOT email addresses, NOT passwords. Traefik access logs are disabled fleet-wide. Application logs are scrubbed of known PII patterns (emails, IPs, JWTs) before reaching our log aggregator. |
| Operator activity logs | Every SSH login, Docker command, database query, or access to sensitive files | 30 days (auditd logs via Loki) | Every command a Ciphera operator runs on production infrastructure is recorded in an audit trail forwarded to our observability stack. If we — or anyone with access — touch production, it leaves a trace. |
We're not listing these to minimize them. We're listing them so you can audit exactly what exists.
What We Don't See — And the Architecture That Prevents It
Your password
Ciphera's authentication uses OPAQUE (RFC 9807), an asymmetric password-authenticated key exchange. When you sign up, your device stretches your password with Argon2id and runs a blinded exchange with the server — blinded meaning the server processes your registration without ever seeing the password, a hash of it, or even the salt. What the server stores is an opaque credential record. Every login repeats that blinded exchange: the plaintext password, a hash of the password, or any reversible derivation never touches Ciphera infrastructure. (We ran SRP-6a until June 2026; OPAQUE keeps the same zero-knowledge property and removes SRP's one leak — the salt handed out before login, which let attackers precompute guesses ahead of a breach.)
If we get breached, the attacker gets credential records. A record can't be used to log in as you and can't be reversed to your password — the only attack left is per-user brute-force guessing, each guess paying the full Argon2id stretching cost. If we get subpoenaed for your password, we can't comply — not because we chose not to, but because we don't have it.
This isn't how we launched. Our auth database migrations tell the story: migration 039 added the zero-knowledge columns (encrypted vault, email blind index, SRP verifier), migration 045 dropped the legacy columns (email, display name, password hash), and the June 2026 OPAQUE migration retired the SRP verifier in favor of the credential record. The old columns are gone — not deprecated, not soft-deleted, but DROP COLUMN.
Your name and profile data
Everything you'd recognize as "your profile" — name, display name, email — lives inside an encrypted_vault field in the auth database. This vault is encrypted on your device with a vault master key that lives in your browser as a non-extractable key: pages can use it, but no script — ours included — can export its bytes. The key itself is stored wrapped (encrypted) under the export key that only your device derives during the OPAQUE login. The server holds the encrypted vault and the wrapped key, and can decrypt neither. When you log in, your browser unwraps the key locally, decrypts the vault locally, and shows you your own data.
A Ciphera operator with full database access sees: a UUID, a blind index, an OPAQUE credential record, a wrapped key, and a blob of encrypted bytes. No name. No email. No profile.
Your browsing behavior on Pulse-tracked sites
Pulse collects pageview events, but with a privacy model designed so individual visitors cannot be identified or tracked across sessions.
The session identifier is computed server-side: a SHA-256 hash of an HMAC daily salt, the site domain, the visitor's IP address, and their user-agent string. It rotates at midnight UTC. The same visitor on Monday and Tuesday produces two completely unlinked session hashes. There is no cookie, no localStorage token, no client-side visitor ID.
The raw IP address is extracted from the request, used to compute the session hash and an optional country lookup, then actively deleted from the request context. It never reaches the database. The raw User-Agent string was dropped in a database migration whose comment reads: "Storing the raw UA string serves no analytical purpose and contradicts the privacy policy."
Site owners using Pulse see only aggregates: top pages, referrer trends, device categories, engagement percentiles. There is no dashboard view that shows "visitor X visited pages A, B, C." That view doesn't exist because the data model makes it impossible to construct.
Pulse also respects the Do Not Track (DNT) and Global Privacy Control (GPC) browser signals. If your browser sends either signal, Pulse does not track. GPC is legally binding under California's CCPA and recognized by multiple EU regulators.
New Pulse sites start with privacy-first defaults: country-level geographic collection only (no region/city), and 6-month data retention (shorter than the 12 months we previously defaulted to). Site owners can opt up if they need more, but the default is the least-invasive choice.
Your cookies on ciphera.net
There aren't any. Our marketing site sets zero cookies. No session cookie, no consent cookie, no preference cookie. The only scripts loaded are Pulse (our own analytics) and fonts bundled at build time. It's also why this site shows no cookie banner — the legal reason a genuinely cookieless site can skip one is worth understanding.
No Google Analytics. No Meta Pixel. No Facebook SDK. No Hotjar. No Intercom. No session replay. Our privacy policy says this, and the code confirms it.
The Email Paradox: How We Contact You Without Building a Profile
Here's the question most readers are forming: if you can't see my email, how do you email me?
Honest answer: we can, briefly.
When you create a Ciphera account, your browser encrypts your email address using P-256 ECDH (Elliptic Curve Diffie-Hellman) with our notification service's public key and sends the encrypted blob over the wire. Our notification service — which we built internally because every third-party provider (SendGrid, Mailgun, Twilio) would require us to hand over readable email addresses and message contents to their infrastructure — decrypts the blob and stores it encrypted at rest using AES-256-GCM.
When we need to send you something — a password reset, a verification code, a security alert — the service decrypts your email in process memory, composes the message from a template, hands it to our self-hosted Stalwart SMTP server over TLS 1.3, and returns. The plaintext email address exists in process memory for milliseconds — as an explicitly-zeroed byte slice that gets overwritten with zero bytes immediately after the SMTP send completes. It is never written to disk and never logged. (Go's runtime can still hold short-lived string copies internally, but the underlying buffer we control is zeroed deterministically — reducing the plaintext residence window to the minimum technically achievable.)
What we log after sending: which template was used (e.g., "password_reset"), which service triggered it (e.g., "auth"), whether it succeeded, and a timestamp. That's it. Our email log has no column for the recipient address and no column for the message body. We can tell you "a password reset email was sent for user UUID abc123 at 14:32" but not where it went or what it said.
The emails themselves contain no tracking pixels, no open-rate beacons, no click-tracking redirects. Links pass through unmodified. We have no mechanism to know whether you opened the email, clicked the link, or deleted it unread.
Is this zero-knowledge in the strict cryptographic sense? No. We do see your email address, briefly, in memory. But we don't build a profile from it, we don't persist it in any log, we don't track what you do with it after delivery, and we don't share it with any third party. All email infrastructure is self-hosted in Switzerland — no US cloud providers, no third-party email APIs, no subprocessor chain.
The Gaps We're Closing
Honesty means naming the things we haven't fixed yet. Here are the remaining gaps in our privacy architecture, and our plans to close them:
Single-operator access model. One SSH key from one IP address currently controls access to the entire fleet. Every operator command is now recorded in an audit log (SSH authentications, Docker invocations, database queries, access to sensitive files) forwarded to our observability stack with 30-day retention. The missing piece is role-based access control — multiple operator accounts with scoped permissions. That's in active development.
Database backups now have client-side GPG encryption with an off-account passphrase. Backups encrypted at rest with AES-256 server-side, then also gpg --symmetric --cipher-algo AES256 encrypted before upload with a passphrase stored in Bitwarden and a paper DR envelope — never in any Exoscale env var. A compromised cloud account can't read backups without also compromising the passphrase store. The remaining backup gap is off-account replication (same region, same provider) — in progress.
Single-operator access model is narrower than before. PostgreSQL access is now scoped per-service: auth_app can only see the auth database, pulse_app can only see the pulse database, backup_ro has read-only across both. A compromised service can't reach into other services' data. Operator audit logging captures every production command. Role-based access control for human operators (multiple accounts with scoped permissions) is the next step.
These gaps exist because we prioritized getting the core architecture right (zero-knowledge authentication, encrypted vaults, daily-rotating hashes, self-hosted email) before building the operational tooling around it. The architecture prevents data exposure; the gaps are about operational hygiene. Both matter.
Update, 16-04-2026 — Phase 4 of our privacy hardening is now live. Changes since this post was first published: notifications subsystem rebuilt on the privacy principles described above — typed payload registry with allow-listed fields only (emails, IPs, and raw URLs are structurally rejected at the write path), per-user event receipts with hard-delete dismiss (no soft-delete columns anywhere in the schema), per-category two-phase retention (unread and read windows) enforced by a nightly purge job with a PII-free audit summary, AES-256-GCM encrypted webhook URLs, a pre-cascade scrub endpoint that the auth service calls when a user deletes their account (so notification history is purged before the user row drops), and 11 privacy invariants codified as CI-enforced tests — any future commit that regresses one (silent miscategorization, upward retention override, soft-delete column, quiet-hours bypass for non-critical types) fails the build. Earlier phases: device-bound refresh tokens (stolen tokens useless on a different device), cryptographic account deletion (encrypted vault overwritten with random bytes before DELETE, defeating WAL/backup recovery), versioned encryption keys for stored emails (rotatable with lazy re-encryption on access), HMAC-signed requests between services (shared secrets no longer ride on every request), monthly-rotating session secret for Pulse analytics, Subresource Integrity hashes on the Pulse tracking script (CDN tampering rejected by browsers), per-service PostgreSQL roles with least privilege, client-side GPG-encrypted backups with off-account passphrase, MTA-STS policy flipped from testing to enforce (receivers now refuse unencrypted delivery), formal retention policy document, privacy impact assessment in every PR template. Remaining: multi-operator RBAC, off-account backup replication, DANE/TLSA records with auto-rotation — all in Phase 5.
Update, 03-07-2026 — corrected this post to describe the OPAQUE authentication system that replaced SRP-6a in June 2026: the password section now documents the OPAQUE flow (RFC 9807), the profile vault is unlocked by a wrapped master key tied to the OPAQUE export key rather than a PBKDF2-derived key, stored-credential references were updated throughout (SRP verifier → OPAQUE credential record), and the blind-index iteration count was made explicit (1,000,000). The guarantees this post makes did not change — the mechanism enforcing them got stronger.
How You Verify Us
We've told you what we see and don't see. Here's how to check.
Read the architecture guide. Our zero-knowledge encryption guide explains the cryptographic principles behind client-side encryption in detail.
Understand our infrastructure choices. We've written about why Swiss infrastructure matters for data privacy. All production systems run on Swiss infrastructure. No US cloud services touch user data.
Ask us directly. If you have questions about a specific data flow this post doesn't cover, reach out at hello@ciphera.net. We'd rather answer a hard question honestly than have you wonder.
What's coming next. We're working toward a formal transparency report modeled on Signal's and Proton's annual reports, an external security audit, and a public bug bounty program. When those are ready, you'll read about them here.
The principle behind everything described in this post is simple: if a privacy promise can be broken by a bad actor inside the company, a subpoena, an acquisition, or a breach, it isn't really a privacy promise — it's a marketing promise. The engineering challenge we've set ourselves is to make sure that when those things happen to us, your data remains inaccessible. We haven't finished. But we've built the foundation, and now you know exactly what's on it.
FAQ
Frequently Asked Questions
Your email address is stored in a client-encrypted vault that Ciphera operators cannot decrypt. When we send you a transactional email, our notification service briefly decrypts your address in process memory to compose the SMTP message. The plaintext exists for milliseconds, is never logged, and is garbage-collected after the send completes. At rest, your email is AES-256-GCM encrypted.
Your encrypted vault (containing email and profile data) is permanently deleted — the bytes are overwritten with random data before the DELETE to defeat WAL/backup recovery. Your OPAQUE credential record, wrapped vault key, and blind index are removed. All refresh tokens are revoked. Notification metadata (template name, delivery status — not your email address or message body) in the email log is purged, and on the analytics backend every notification receipt belonging to you is hard-deleted via a pre-cascade scrub that the auth service invokes before your user row drops — with in-DB foreign-key cascades handling the rest. Device-tracking hashes and audit log entries are bounded by their retention periods.
No. The Ciphera website uses only Pulse, our own privacy-first analytics platform. Pulse sets zero cookies, stores no raw IP addresses, and generates daily-rotating session identifiers. No Google Analytics, Meta Pixel, Hotjar, or third-party tracking scripts are loaded on any Ciphera property.
Account UUID, creation and login timestamps, subscription status, encrypted vault (which we cannot decrypt without the user's password), OPAQUE credential record (cryptographically useless without the original password), raw IP addresses from device-tracking tables, hashed IPs from audit logs, notification metadata (typed event payloads with allow-listed fields only — no email addresses, IP addresses, or message bodies — plus per-user read/dismiss state and per-channel delivery log), and database backups up to 12 months old containing the same encrypted data.
No. Pulse generates a new session identifier each day using a one-way hash of IP address, user-agent, domain, and date. The same visitor on consecutive days receives different session IDs with no link between them. There are no cookies, no localStorage tokens, and no persistent visitor identifier. Raw IP addresses are discarded immediately after the hash computation.
Related Articles
Get started
Put this into practice.
Ciphera builds privacy-first infrastructure — analytics, identity, bot protection, and email that don’t surveil. The tools this article describes are the ones we run.


