Privacy & Architecture

Everything about how SOVA handles your data — no legalese, just straight talk.

Version 2.0 Last updated: February 2026

1.0 What is SOVA?

SOVA is a Chrome extension that helps you keep track of your subscriptions. It can detect when you're on a checkout page, let you import subscriptions from your Gmail, remind you before trials end or payments renew, and help you cancel things you don't use anymore.

SOVA stores your subscription data locally on your device. If you sync between devices, an encrypted copy goes to our servers — but the encryption key is derived from your account ID on your device, and we don't store the key separately. That means the data on our servers is ciphertext we can't meaningfully read without your account ID.

The Short Version

Your subscription data is stored on your device or uploaded encrypted if you sync. Two things do pass through our servers in plaintext: (1) email reminders — the subscription name and date go to our sending service (SendGrid) to compose the email, then are discarded; and (2) checkout detection — if you click "Analyse", a screenshot is sent to OpenAI via our proxy, then discarded. Both are described in full in sections 4.0 and 4.2.

1.1 How We Approach Privacy

  • Local-first: Your subscription data is stored on your device using Chrome's built-in storage
  • Encrypted sync: If you sync between devices, your data is encrypted on your device before upload using AES-256-GCM. The key is derived from your account ID and is not stored on our servers
  • No identity required: You don't need an email or name to use SOVA. Your account is a random 24-character string
  • Hashed references: When we need to reference a subscription server-side (e.g. to prevent duplicate reminders), we store a keyed HMAC hash. We can't reverse it to get the subscription name
  • Short-lived cloud data: Sync snapshots delete after 7 days of inactivity; transfer codes expire after 24 hours
  • Blockchain log: Certain actions write a hash to Solana as a timestamped record — no subscription content goes on-chain

2.0 What Stays on Your Device

This stuff never leaves your machine. It's stored using Chrome's built-in storage API, which means it lives in your browser's profile folder, encrypted at rest by your OS.

Data What It Is Where
Subscriptions Names, amounts, billing dates, frequencies — everything about your subscriptions Your Device
Email Address Only if you opt into reminders. Used to send you heads-up emails Your Device
Gmail Tokens OAuth tokens for connected Google accounts (for importing subscriptions from email) Your Device
Settings Reminder preferences, currency, theme, etc. Your Device
Account ID A random 24-character string. Not tied to your identity in any way Your Device
Encryption Keys Your HMAC key (for subscription hashing). The sync encryption key is not stored — it is derived from your account ID on demand via PBKDF2 Your Device
Transaction Log A local record of Solana proofs (last 100 entries) so you can audit what happened Your Device

2.1 What Touches Our Servers

We keep this list as small as possible. Here's every single thing our server (Supabase) stores, and what state it's in:

Data What It Actually Is Status
Account ID A random string like "p0lfzfgv3st1ar500550axnc" — means nothing to anyone Pseudonymous
Sync Snapshot An AES-256-GCM encrypted blob of your subscription data. The encryption key is derived from your account ID on your device and is not stored on our server separately Encrypted
Transfer Codes Temporary codes for syncing to a new device. They expire in 24 hours Ephemeral
Reminder Transaction IDs A hash of (account + subscription + event date) to prevent duplicate emails. We can't reverse it to get your subscription name Hashed
Cancellation Intents A keyed hash of the subscription name. Deleted the moment your extension picks it up Ephemeral

2.2 What We Never See

Just to be completely explicit about it:

What We Never Store

Your subscription data in plaintext — it's encrypted before leaving your device
Your browsing history or what pages you visit
Your Gmail inbox contents — scanned entirely on your device, never sent to us
Your Google account credentials or OAuth tokens
Checkout screenshots — processed in-memory by our detection proxy, then discarded
Email reminder content — processed in-memory to send the email, then discarded
Any raw data on Solana — only cryptographic hashes go on-chain

"In-memory" means the data exists in the server process only long enough to complete the operation (send the email, call OpenAI). It is never written to a database, log file, or any persistent storage.

3.0 How Sync Works

When you want your subscriptions on multiple devices, here's the step-by-step of what actually happens:

1
Encrypt on Your Device
Your subscription data is encrypted using AES-256-GCM. The encryption key is derived from your account ID using PBKDF2 (100,000 iterations) — it is not stored anywhere, just re-derived when needed.
2
Upload the Encrypted Blob
The encrypted data is uploaded to Supabase. We see ciphertext. Without your account ID to re-derive the key, we can't decrypt it — but your account ID does live in our database too, so the protection is primarily against third-party breaches, not against us.
3
Other Device Downloads & Decrypts
Your other device downloads the encrypted blob, re-derives the encryption key from your account ID locally, and decrypts everything on-device. The plaintext never goes back to the server.
4
Auto-Cleanup
If you stop syncing, the snapshot automatically gets deleted after 7 days of inactivity. We don't hoard your (encrypted) data.

3.1 Encryption Details

For the technically curious, here's exactly what's going on under the hood:

  • Algorithm: AES-256-GCM via the Web Crypto API (the same standard used by banks and governments)
  • Encryption key: Derived from your account ID using PBKDF2 (100,000 iterations, SHA-256, fixed salt). The key is never stored — it is re-derived on your device each time it is needed
  • IV: A fresh random 12-byte initialization vector for every single encryption operation, so identical data produces different ciphertext each time
// What our database actually contains:
{
  "account_id": "p0lfzfgv3st1ar500550axnc",
  "encrypted_data": "base64(random IV + ciphertext + auth tag)",
  "checksum": "a1b2c3d4..."
}

// We see account_id and encrypted_data.
// The key is derived from account_id via PBKDF2 — we choose not to do this.

3.2 When Data Gets Deleted

We auto-delete cloud data so we're holding onto as little as possible at any given time:

Data Auto-Deleted After
Sync Snapshots 7 days of inactivity
Transfer Codes 24 hours
Reminder Transaction IDs 30 days
Cancellation Intents Immediately after your extension picks it up
Sync Snapshot Stays until you delete your account or it expires after 7 days of inactivity
Account Record Never auto-deleted, but it's just a random string with no personal info attached

3.3 Solana Transparency

For certain actions — syncing, sending reminders, generating transfer codes — SOVA writes a hash to Solana as a timestamped record. This gives you an external reference point to check that an action happened at a given time.

Only Hashes, Never Your Data

What goes on Solana is a SHA-256 hash of the request — basically a fingerprint. Your subscription names, amounts, email, none of that is on the blockchain. You can look up the transaction on Solana Explorer and you'll see a hash and an action type (like "sync" or "reminder"), nothing more.

3.3.1 What Gets a Proof

  • Sync to/from cloud — hash of the encrypted sync payload
  • Account registration — hash of the registration request
  • Sending a reminder — hash of the reminder request
  • Transfer codes, email verification, cancellation intents — hash of each request

3.3.2 Transaction Logs

In Settings, you'll find a Transaction Logs section. It shows your recent proofs — each one has a label (like "Sync to cloud" or "Reminder: Netflix"), a Solana signature, and a link to the Explorer so you can verify it yourself. You can filter by category. The log is stored locally (last 100 entries), and sync entries are hidden by default to keep things clean.

3.3.3 What Solana Does and Doesn't Prove

Let's be honest about the limits. Solana proofs are a public audit trail — they prove that a specific payload hash was committed at a specific time. That's genuinely useful for trust and accountability. But they don't prove that we later deleted data on schedule, or that our server handled things correctly beyond matching the commitment. We're transparent about that because we'd rather be upfront than oversell.

4.0 Page Detection Engine

This is the core of SOVA — how it figures out you're on a subscription checkout page. Everything here runs entirely in your browser. No data gets sent anywhere during detection.

4.0.1 How It Works

When you visit any webpage, SOVA's content script runs a multi-layered analysis:

  • URL classification: It checks if the URL looks like a checkout, pricing, settings, feed, article, or other page type based on the path and subdomain
  • DOM inspection: It looks for payment forms — Stripe elements, card number inputs, PayPal buttons, checkout forms. These are "hard evidence" that a real payment is happening
  • Content analysis: It scans the page text for recurring billing language ($X/month, auto-renews, billed annually), subscription CTAs, and price-frequency patterns
  • Negative signal detection: It also looks for reasons NOT to trigger — job listings, social feeds, e-commerce carts for physical goods, donation pages, one-time purchases, pricing comparison pages

4.0.2 The Scoring System

Each signal gets a weighted score based on what type of page it appears on. A payment form on a checkout URL is worth a lot. Marketing buzzwords on a blog are worth very little. The total score has to clear a threshold, and then it goes through a series of verification gates:

  • Pricing Page Gate: Multiple plan cards or heavy comparison language? That's a pricing page, not a checkout — blocked
  • Payment Gate: No actual payment form detected? No detection. A page must have real card inputs, Stripe, or PayPal to trigger
  • Recurring Gate: No recurring billing language? Probably a one-time purchase — blocked
  • One-Time Purchase Gate: Page explicitly says "does not auto-renew" or "one-time gift"? Not a subscription — blocked
  • Donation Gate: Dominated by donation language? Recurring donations aren't subscriptions — blocked
  • Ecommerce Gate: Overwhelmingly a physical goods checkout with just a membership ad in the corner? That's not a subscription checkout — blocked
  • Payment Confidence Dampening: When a real payment form exists, marketing noise in footers and sidebars carries much less weight. Hard evidence (DOM) outranks soft evidence (text patterns)

Privacy During Detection

All of this analysis happens locally in your browser. We don't send the page URL, content, or any signal data anywhere during detection. The only time anything leaves your device is if you click "Track" — at which point a screenshot is sent to our server-side detection proxy (a Supabase Edge Function), which calls OpenAI Vision on your behalf using a server-stored API key. The screenshot is processed in memory, the result is returned to you, and the screenshot is discarded. We don't log or store it. The page URL is sanitized (session tokens, user IDs, personal identifiers stripped) before being sent.

This is the same model as email reminders: data passes through our server in transit to complete a task, and is not retained. We do this server-side so the OpenAI API key never has to live in the extension code.

4.1 Gmail Import

You can connect your Google account(s) and SOVA will scan your emails to find subscriptions you might have missed. Here's how it works and what we can and can't see:

4.1.1 How Gmail Scanning Works

1
You Sign In with Google
SOVA uses Google's OAuth flow. We request read-only access to your Gmail (gmail.readonly scope) — we can't send, delete, or modify any emails.
2
Smart Email Search
SOVA runs targeted Gmail searches (things like "subscription OR billing OR renewal") directly through the Gmail API. It doesn't download your entire inbox — just the emails that match subscription-related keywords.
3
Client-Side Parsing
Every matched email is parsed entirely on your device. The algorithm looks for recurring billing patterns, known service domains, price/frequency pairs, and filters out one-off purchases, spam, and marketing. None of your email content is sent to our servers.
4
You Review the Results
Found subscriptions are shown as flashcards. You can edit the name, adjust the billing date, keep or discard each one. Only the ones you approve get added to your local subscription list.

Your Emails Never Leave Your Device

The Gmail API calls go directly from your browser to Google's servers. Email content is fetched into your browser's memory, parsed locally, and then discarded. Our servers are not involved at any point during the scan. We never see your emails, your OAuth tokens, or anything from your Google account.

4.1.2 Multiple Accounts

You can connect as many Google accounts as you want. Each one gets its own OAuth token stored locally on your device. You can scan any of them independently, and disconnect them at any time. When you disconnect an account, its token is deleted from local storage immediately.

4.2 Email Reminders

SOVA can email you a heads-up before a trial ends or a payment renews. Here's how that works — and the one place where we're not fully zero-knowledge:

  • Your email address is stored locally only and never saved on our servers
  • When it's time to send a reminder, the extension sends the payload (email, subscription name, amount, days until event) to our Supabase Edge Function, which forwards it to SendGrid for delivery
  • This is the one step where our server briefly sees your data in transit. It processes the payload to send the email, then discards it. We don't log, store, or retain any of that content
  • We're upfront about this because we'd rather be honest than claim something we can't prove

Deduplication

Nobody wants the same reminder email three times. To prevent that, we generate a transaction ID:

transaction_id = SHA-256(account_id + subscription_name + type + event_date)

Using the event date (not today's date) means you get exactly one email per billing event, no matter how many times you open the extension. Once it's marked "sent," it won't fire again for that event.

4.3 Cancellation Flow

When you hit "Cancel" on a subscription, SOVA tries to take you directly to the right cancellation page. Here's the priority order:

  1. Check a local database of 300+ known cancellation URLs (Netflix, Spotify, Adobe, etc.)
  2. Try fuzzy matching for typos and variations (so "Netflixx" still works)
  3. Check a local AI cache of links discovered in the past 30 days
  4. If nothing matches, ask OpenAI for the cancellation page (only the service name is sent — nothing else)
  5. Cache that AI result locally for 30 days so we don't have to ask again
  6. Fall back to the subscription's source URL as a last resort

After redirecting you, a confirmation overlay asks "Done cancelling?" so SOVA can track whether you actually went through with it.

4.4 Cross-Device Cancellation

Reminder emails include a "Cancel" button. Say you get a reminder on your phone and want to cancel right there — even without the extension installed. Here's what happens:

1
Click Cancel in the Email
The link hits our edge function with a hashed identifier of the subscription — not the name itself.
2
Record the Intent
Our server stores a keyed hash (HMAC) of the subscription name. We never see the actual name.
3
Redirect to the Cancellation Page
You're instantly sent to the actual cancellation URL.
4
Extension Picks It Up
Next time you open SOVA on any device, it matches the hash to your subscriptions and marks it as cancelled.
5
Intent Gets Deleted
Once processed, the intent is immediately wiped from our server. No trace left.

5.0 Why We Ask for These Permissions

Chrome extensions have to declare their permissions upfront. Here's every permission SOVA requests and exactly why:

Permission Why We Need It
storage To save your subscriptions, settings, and encryption keys locally on your device. This is Chrome's built-in storage — it doesn't go to any server.
activeTab To take a screenshot of the checkout page when you click "Track." Only triggers on user action, never in the background.
tabs To open cancellation pages, the welcome page, and the privacy doc in new tabs.
alarms To schedule periodic checks for upcoming billing dates and trial expirations so reminders go out on time.
notifications To show you browser notifications when a trial is about to end or a payment is coming up.
identity For the Gmail import feature — this handles Google OAuth sign-in so you can connect your Google account securely through Chrome's built-in auth flow.
host_permissions (all URLs) The page detection engine needs to run on every page to detect subscription checkouts. It also needs to communicate with our Supabase backend and the Gmail API. The content script is lightweight and only activates full detection logic on pages that match initial URL heuristics.

A Note on "All URLs"

We know "access to all websites" sounds broad. The reality: our content script runs a fast URL check first, and skips pages that obviously aren't checkout pages (social feeds, search results, articles, videos, etc.). On pages that do look like they might be checkout pages, it runs the full detection — but all of that analysis happens locally in your browser. No page content, URLs, or browsing data is ever sent to us.