Skip to content

Security: trh-ds/recall

Security

SECURITY.md

Security Policy

Recall reads notifications, calendar entries and microphone input. That's a lot of trust for a pre-alpha project to ask for, so this file states plainly what the project defends against, what it doesn't, and how to tell us when we've got it wrong.

Reporting a vulnerability

Please don't open a public issue.

Use GitHub's private vulnerability reporting: Security → Report a vulnerability on this repository. That opens a private thread visible only to the maintainers.

Please include what you were able to do, the steps to reproduce it, and the version or commit you were on. If it involves a provider key or any real user data, describe the class of problem rather than pasting the material itself.

What to expect: this is currently a small project with one maintainer, so please allow a few days for an initial response. We'll confirm receipt, tell you whether we can reproduce it, and agree a disclosure timeline with you. Fixes land on main; there are no releases to backport to yet.

Supported versions

Pre-alpha. Only main is supported. There is no published release, no APK distribution channel, and no Play Store listing.

Threat model

What the architecture is actually designed to resist.

Protected

Threat Defence
Provider API keys extracted from the APK Keys are never in the app. EXPO_PUBLIC_* vars are inlined into the JS bundle and are trivially recoverable from a shipped APK, so keys live only in the relay's server-side environment. The app ships one EXPO_PUBLIC_API_URL.
A compromised relay leaking user history It has nothing to leak. server/ is stateless — no database, no session, no cache. It holds a request only for the life of that request.
A relay operator profiling users Runtime logs record provider name, latency and token counts. Message content is never logged, by policy and by code.
Provider error bodies echoing request content back to the client Failover surfaces HTTP status codes only. A provider's response body is never forwarded to the phone.
A stale client sending malformed requests CONTRACT_VERSION is checked on every request; a mismatch is a clean 400.
Untrusted request bodies server/lib/guard.ts enforces byte, message-count and per-message length caps before anything reaches a provider, and validates the device header against an allowlist pattern (this also prevents Redis key injection into the rate limiter).
Abuse of an open LLM endpoint Per-device and per-IP rate limits. These fail closed in production: if the rate limiter is unconfigured or erroring, requests are refused rather than allowed through.
Processing before consent The consent gate wraps the navigator rather than being a route, so no navigation order reaches a screen without it, and the LLM client refuses to send at the single egress choke point.
Silent agent actions The LLM can only select from a registry of named tools; it never executes code. Tools run on the device, and write actions route through the on-device confirmation gate.

Not protected

Stated plainly, because a threat model that claims everything is worthless.

  • A rooted or physically compromised device. SQLite and MMKV are protected by the Android app sandbox and nothing more. Recall does not encrypt its database at rest, and there's no app-level passcode. Anyone with root, or with your unlocked phone, can read everything Recall holds.
  • The device ID is not authentication. It buckets requests for rate limiting and nothing else. It's generated with Math.random(), it's not a secret, and anyone who extracts the relay URL can mint their own. It authorises nothing because there is nothing to authorise — the relay has no user data to protect. If quota abuse ever becomes real, the fix is per-tester invite codes.
  • The LLM providers themselves. Text sent for processing reaches NVIDIA, Groq or Google under their terms, not ours. This is disclosed to the user in the consent notice, and it is the single reason egress minimisation is a rule rather than a preference. Recall sends the smallest slice a task needs — but once it's sent, their policies apply.
  • Prompt injection. A malicious notification or a hostile PDF could influence what the planner selects. The blast radius is bounded by design — the model can only choose among registered tools, tools only touch local data, and anything that writes requires a tap — but a crafted input could still produce a misleading suggestion. Treat suggestions as suggestions.
  • Network-level observation. The relay is HTTPS in production. Debug builds enable cleartext HTTP so you can point the app at a laptop on your LAN; that is a development affordance and must never be relied on in a distributed build.
  • Denial of service against your own relay. Rate limits protect the quota, not availability.

For contributors and operators

If you run your own relay:

  • Use restricted, low-quota provider keys. Assume any key you deploy will eventually be misused; cap the damage in advance at the provider.
  • A key that has ever been in a client bundle is a public key. Rotate it. During development this project's keys were briefly built into a debug APK; they are treated as compromised and are being replaced.
  • Never commit real keys. .env and server/.env.local are gitignored; .env.example and server/.env.example are tracked and must stay empty.
  • Configure the rate limiter before exposing the relay publicly. Without UPSTASH_REDIS_REST_* set, production refuses every request by design — that failure mode is deliberate, so don't "fix" it by removing the check.
  • Never log message content, and don't add an analytics SDK that ships content off the device.

Privacy and DPDP

Recall is built to India's Digital Personal Data Protection Act 2023 and the DPDP Rules 2025: plain-language consent before any processing, disclosure of third-party LLM egress, in-app export and delete, consent withdrawal that purges, and least-privilege permissions requested in context.

A privacy problem — data leaving the device that shouldn't, a consent flow that can be bypassed, more text sent to a provider than a task needs — is a security report as far as this project is concerned. Please report it the same way.

There aren't any published security advisories