How Canopy handles your data
This page documents what we encrypt, what we don't, where your data is stored, and what our limitations are.
How your data flows through Canopy
Sensitive content (article bodies, feed URLs, summaries) passes through the same pipeline: fetch, encrypt, store. Article titles and URLs remain in plaintext for search and display. The encryption key is loaded as an environment variable at startup and held in process memory — it is never stored in the database.
What's encrypted — and what isn't
We encrypt content fields like article bodies, feed URLs, and summaries. Article titles, URLs, and functional flags (read/starred states) are stored in plaintext so the app can search, filter, and sort without decrypting the database.
Encrypted (AES-256-GCM)
- Feed URLs & feed names
- Site URLs
- Article content & summaries
- Newsletter content & summaries
Not Encrypted
- Account ID (random, not PII)
- Article titles & article URLs
- Read / starred / saved states
- Timestamps (created, updated)
- Feed type (rss, newsletter, podcast)
- Podcast enclosure URLs & duration
Article titles and URLs are stored unencrypted so the app can display and search your article list without decrypting the full database. They contain public information (headlines from public websites), not private data you created.
What a database record looks like
Encrypted field format
Each encrypted value is three base64-encoded segments separated by colons:
What we protect against — and what we don't
No system is perfect. Here are the specific threats we address and the ones we don't.
Protected
Feed URLs and article content are encrypted. Article titles and URLs are stored in plaintext but are public information from public websites, not private data.
No analytics, no tracking pixels, no third-party JavaScript except Stripe on the checkout page.
If our database were stolen, attackers would get encrypted content and feed URLs. Article titles and URLs would be readable, but these are public headlines from public websites.
We cannot produce readable feed data without the encryption key. We operate under EU (Finnish) jurisdiction.
Stripe processes payments via a random token that maps to your account. After 20 days the token is deleted and the link between your payment and your account no longer exists. We never see or store your card number.
Honest Limitations
If an attacker gains access to the running server process, they could read the encryption key from memory and decrypt stored data.
When Canopy fetches your feeds, the source servers see our server's IP. An observer with access to those logs could infer that someone on Canopy follows a given feed — but not who.
If TLS is broken or a certificate authority is compromised, data in transit could be intercepted. We use Let's Encrypt with automatic renewal.
Canopy is not open-source. You are trusting that we run the code we describe. We publish this page as a commitment, but we cannot cryptographically prove it.
Where your data lives
The server and database are in one location. Here's what we use and what we don't.
Server & Database
- Hetzner Cloud, Helsinki, Finland
- PostgreSQL with encrypted fields
- GDPR jurisdiction (EU)
- No US cloud providers in the data path
What We Don't Use
- No analytics (no Google Analytics, no Plausible, nothing)
- No CDN tracking or edge logging
- No third-party JavaScript (except Stripe on the checkout page)
- No tracking cookies (session-only auth cookie)
- No error tracking (no Sentry, no LogRocket)
- No A/B testing
Each of these is a trade-off. We have no visibility into bugs, performance, or user behavior.
How we strip newsletters clean
Most newsletters contain invisible tracking pixels and click-tracking URLs. Canopy removes them during processing.