Security and personal data
What the app processes, the legal basis under the LGPD, and how each item is protected. Core principle: minimization — we only keep what a feature truly needs, for the shortest time possible, and never in cleartext when we can avoid it.
Data inventory
| Data | Where / why | Legal basis | Protection |
|---|---|---|---|
| Device token (pseudonymous credential, no login) | Identifies the device to save lists in the cloud and record consent. | Consent (toggle "Save lists to the cloud"). | In the app: secure storage/Keystore. On the server: never stored in cleartext — Redis keys use a salted SHA-256 hash of the token (like a password hash). A Redis dump reveals neither a usable token nor links a known token to its data. Never logged. |
| Anonymous measurement id | Estimate unique users (audience). | Legitimate interest, with opt-out. | Only enters salted into a HyperLogLog (aggregated cardinality, irreversible). No per-device record. Details in the LIA. |
| Location (lat/lon) | Find prices near the user (radius). | Performance of the service requested by the user. | Not stored. Used transiently in the SEFAZ call and
rounded (~11 m) only in the cache key. Not written to logs (search is
POST). No analytics event stores coordinates. |
| Shopping lists | Shareable links and cloud history (when consented). | Consent (cloud) / performance of the service (link). | Stored under UUID, linked to the device only via the hash above. Deletable (right to erasure). Idle TTL of 30/90 days. |
| IP address | Daily search rate limit (anti-abuse). | Legitimate interest (security/abuse). | Never in cleartext: the limit key stores only a salted hash of the IP, with a 24 h TTL. Buckets the client without keeping addresses on disk. |
| Feedback text (👍/👎, "wrong item", free-form note) | Detect AI normalization mistakes. | Legitimate interest. | Anonymous (device token is not stored with it). Lives in a capped stream (1000 items) visible only in the admin panel. |
| Search terms | Ranking of most searched / not found items. | Legitimate interest. | Aggregated by count (sorted set), not linked to a user. The event feed stores only item count, match rate, and source — not per-user terms, no token, no IP. |
Third-party secrets (SEFAZ token, keys)
Credentials the backend uses to talk to third parties (SEFAZ
AppToken is the current case) are not kept in the repository, in
.env, in logs, or in any API response. They are entered once in the
admin panel (Settings), encrypted with Fernet
(AES-128-CBC + HMAC), and stored in Redis. The panel only shows a short
fingerprint so the operator can confirm which value is active — the value
itself is never returned. Rotation takes effect immediately, without a restart.
Honest threat model
The backend must send the token to SEFAZ in cleartext, so the process
must hold the value in memory. No scheme hides a secret
from someone with root on the same machine (they can read process
memory or the decryption key). Hashing also does not work: it is one-way, and
we need the value back. What this architecture actually guarantees:
- the secret is never written to disk in cleartext (AOF/RDB dumps and Redis backups hold only ciphertext);
- it never appears in git,
.env, logs, or API responses (write-only field); - an operator/agent doing normal work never stumbles on the value — decrypting it is a deliberate, visible act.
The only residual key is SECRET_ENCRYPTION_KEY (Fernet key), in the
server .env. Ciphertext (Redis) and key (env) live in
separate places — neither alone leaks the secret. Truly hiding from
root would require an external KMS/HSM, which this single VPS does not have.
Data subject rights
- Erasure:
DELETE /api/v1/device/medeletes all server-side device state (consent + associated lists). - Access:
GET /api/v1/device/meshows what the server stores for that device. - Objection (measurement): opt-out in Settings → "Anonymous usage statistics".
References
- LGPD — Art. 6 (minimization, security), Art. 7/Art. 11 (legal bases), Art. 18 (data subject rights), Art. 46 (information security).
- See also the usage measurement LIA and the Privacy Policy.