End-to-End Diary Encryption
As of September 2026, GrowScope encrypts every account's grow diary end-to-end and by default — not an opt-in Settings toggle. Diary content (plants, watering/feeding/climate/PPFD logs, notes, saved AI-history text) is encrypted on the user's own device before it is ever sent to the server, and decrypted only on the user's own device. The server stores only ciphertext for this content; it never holds a key capable of reading it. This page documents the actual mechanism, not a marketing summary of it.
How to do this in GrowScope
- On the first app open after this rollout (new signup or existing account), a full-screen setup step runs automatically — no button to press to start it.
- A 12-word recovery phrase (a BIP-39 mnemonic, 128 bits of entropy) is generated locally, on-device, and shown once.
- The user must confirm two randomly chosen words from the phrase before continuing — this step cannot be skipped or dismissed, because it's the only checkpoint proving the phrase was actually recorded.
- Once confirmed, the diary is encrypted and migrated with a single PUT of the current snapshot; every write after that goes through encryption automatically for the life of the account.
- On a second device, or after clearing browser storage, the app instead asks for that same 12-word phrase to unlock the diary — it is never asked for again for a device that already has it cached locally.
Diary Encryption (grow_data)
- Category
- Privacy & Security
- Purpose
- Encrypt all grow-diary content client-side so the server only ever stores ciphertext.
- Input
- None from the user beyond the one-time recovery-phrase confirmation — encryption itself is automatic on every save
- Location
- Runs transparently on every save/load of the diary; the one visible step is the recovery-phrase screen (mandatory on first encryption, and again if a device needs to unlock)
- User action
- Confirm two words from the generated 12-word recovery phrase (first-time setup only)
- Re-enter the full 12-word phrase to unlock on a new device or after clearing browser storage
- Output
- The diary is stored server-side as opaque encrypted bytes (`grow_data.ciphertext` + `grow_data.nonce` + a key-version number) instead of a readable JSON blob. A per-account wrapped encryption key and its salt (`users.wrapped_mek`, `wrapped_mek_nonce`, `kek_salt`) are also stored, but are cryptographically useless without the user's recovery phrase.
- Used by
- Every diary read/write — plants, logs, notes, saved AI-history text
- Related
- None
Notes
- Key hierarchy, precisely: the 12-word recovery phrase's entropy is stretched into a Key-Encryption Key (KEK) via Argon2id (client-side, ~64 MiB memory cost) with a random per-account salt. That KEK wraps (AES-256-GCM encrypts) a separately generated 256-bit Master Encryption Key (MEK) — it's the wrapped MEK, not the phrase itself, that's uploaded to the server. Individual records are encrypted under keys derived from the MEK via HKDF-SHA256 with a record-specific info string (e.g. the diary uses a fixed "grow_data" info string, since there is one diary per account), so a compromised derived key doesn't expose the MEK or any other record. Actual content encryption uses AES-256-GCM (authenticated encryption — a modified ciphertext fails to decrypt rather than silently producing garbage).
- The recovery phrase is generated once, locally, and is never transmitted to or stored by the server in usable form — only the AES-GCM-wrapped MEK and a public KDF salt are. This means the phrase is the single point of recovery: losing it and losing every device that already has the key cached means that diary's data cannot be recovered by anyone, including GrowScope. This is the deliberate cost of the design being genuinely zero-knowledge rather than merely encrypted — a server-side recovery option would require the server to hold something that could reconstruct the key, which is exactly what this design avoids.
- The one disclosed exception to 'the server never sees plaintext,' stated precisely: this design is zero-knowledge for storage and sync, always — it is deliberately not zero-knowledge for AI features, and is never described as such. Every AI-credit-gated feature works the same way — AI Plant Analysis (health/development/canopy scoring), Trichome Tracker, Pest Scouting, the Fertilizer Advisor, the AI Equipment-Placement Optimizer's free-text analysis, Plant Comparison's AI difference analysis, Grow Coach's weekly reports, and AI-generated card art. In every one of these, tapping the action button is the trigger: your device decrypts only the specific data that action needs (e.g. the photos you selected, or the plant's recent logs — never the whole diary), sends it once to GrowScope's server, which forwards it to a third-party model provider (Anthropic Claude or, via OpenRouter, Google Gemini — chosen automatically per task, not user-selectable) to generate that one response, and returns it. Nothing from that request is written to a database table or a log afterward on GrowScope's side.
- Why this isn't done on-device instead: an earlier design brief asked for a fully local, on-device model (via WebLLM/MLC-LLM, a 1–3B-parameter quantized model running in the browser) as the only way to keep AI features zero-knowledge too. That was rejected for GrowScope as it exists: fertilizer-dose optimization, trichome/pest image analysis, and multi-plant entity resolution are exactly the tasks small on-device models are weakest at, and these are paid, credit-metered features (e.g. the Fertilizer Advisor costs 45 credits/call) priced for Claude/Gemini-class quality — silently downgrading them under a 'zero-knowledge' banner would be a worse outcome for the users who rely on that quality. The trade-off was made deliberately and is stated plainly here rather than obscured: for the one action you explicitly request, a third-party provider processes the plaintext relevant to it.
- Existing (pre-rollout) accounts are migrated lazily: the setup step runs the next time that account's owner actually opens the app, not via a background/batch job. An account that is never opened again after this rollout simply remains on the old, unencrypted storage format indefinitely — this is an accepted trade-off of requiring the recovery phrase to be shown to a real, present user rather than generated unattended.
Frequently asked questions
Can GrowScope read my grow diary?
No. The diary is encrypted on your device before it's sent, and the server only ever stores the encrypted bytes plus a wrapped copy of the encryption key that is cryptographically useless without your recovery phrase — which GrowScope never receives or stores.
What happens if I lose my 12-word recovery phrase?
If you also lose every device that already has the key cached locally, that diary's data cannot be recovered — not by you, and not by GrowScope. This is the direct cost of the encryption being genuinely zero-knowledge: nothing capable of reconstructing your key exists on the server to fall back on.
Is this optional, like a Settings toggle I can turn on or off?
No, not anymore. As of September 2026 every account — new and existing — is encrypted; there is no way to opt out, and no way to revert an already-encrypted account to unencrypted storage.
How does this work with AI photo analysis or AI grow advice, if the server can't read my data?
Only when you explicitly run an AI action (any AI-credit-gated feature — photo analysis, the Fertilizer Advisor, card generation, and the rest): the relevant data is decrypted on your device, sent once to GrowScope's server, and forwarded to a third-party model provider (Anthropic Claude or, via OpenRouter, Google Gemini) to generate that one response — then not stored afterward. Nothing is sent to an AI provider automatically or in the background, and never more data than that specific action needs.
Why isn't the AI itself on-device, so it stays zero-knowledge too?
It was considered and rejected: a small model that runs entirely in a browser (1–3B parameters) can't match the quality of Claude/Gemini-class models on the tasks GrowScope's AI features actually do — fertilizer-dose optimization, trichome/pest image analysis, multi-plant entity resolution — and those are paid, credit-metered features priced for that quality. Rather than silently downgrade them, the design keeps storage and sync zero-knowledge always, and discloses plainly that a per-action AI request is the one place plaintext is processed server-side.