Inspiration
Every "life memory" product that came before this got acquired by a company whose business model is retention - Limitless by Meta, Bee by Amazon. An advertising or commerce company cannot credibly say "we study your month and then destroy everything." A small team can, if the destruction is mechanical rather than promised.
What it does
Disavow reads a set period of your own life - what you type and say into it,
plus the calendar, sleep and activity exports you already own - and gives you
one honest report: what went with what, with the evidence attached, in
observational language that never diagnoses, advises, or predicts. Then the
data is destroyed so completely nobody can read it again, including us:
the decryption key's deletion is executed by Firestore's own TTL policy, with
no code of ours running. That is checkable rather than claimed: /healthz on
the hosted URL reports store_expires_by_itself: true because it probes the
live TTL policy, and state_survives_restart: true because the service was
restarted and the assessment read back.
How we built it
- Gemini 3.6 Flash through the Gemini API, inline-only, on the paid tier - the client refuses the free tier in code, because the free tier permits human review of prompts.
- Google ADK (
google-adk) drives the review agent: the live proposal loop runs through a real ADKLlmAgentwhose model is a delegatingBaseLlmsubclass, so every framework call still flows through the inline-only, tier-guarded Gemini client - and the binding is executed by the test suite on every run, not trusted on sight. The division of labour is strict: the model writes prose and cites evidence ids; plain code verifies the ids, recomputes every number (a hallucinated mean is caught, never corrected), and a review board of blocking lenses - evidence, arithmetic, boundary language, resource reachability - is the only gate to a person. - Firestore + Cloud KMS: Firestore holds only wrapped keys and assessment clocks; KMS wraps a per-assessment DEK; the TTL policy on the wrapped key IS the burn mechanism.
- Redaction happens on the device (names become Person A before anything is sent) and the server's redaction guard rejects with a 422 anything that slipped through - it never silently cleans, because a silent fix would make the promise unverifiable.
- Pattern findings sit behind measured guards: the effect threshold (Cohen's d >= 1.2) was chosen by running a 200-period synthetic eval harness, which cut false positives from 13% to 1% on pure noise without losing real detections.
- Daily input is a check-in that feels like texting: one question at a time in chat bubbles - mood, sleep, time with your people, money out in your own words, body, dreams - tap answers become the day's numbers, words route through the same on-device redactor as everything else, and an optional voice asks the questions aloud using the device's own speech synthesis, so nothing new ever leaves the phone to make sound.
- The report answers back, and every answer wears its provenance. Findings carry a plain-words restatement ("on your more active days, you got about 1.1 hours more sleep - we checked 30 days") and, where a pattern fired, a REVIEWED answer written by a person and shipped verbatim with the finding's own evidence ids. A separately-prompted model pass may offer suggestions - invitation language only, graded by the same board - and they render under an explicit generated, not reviewed badge. Where both claim the same cell, reviewed wins by rule. Where nothing earned an answer, the blank says why and offers one tap to add that area to tomorrow's check-in.
- The same conversation runs over text messages and a weekly phone call
through a companion with a name - Alannah by default, Aria or Grant by
choice. Texting is its own consent (the disclosure says plainly that a
carrier sees a text in the clear, and redaction happens at our boundary
instead of on the device); replies never repeat the person's words onto a
lock screen unless they turn personal replies on themselves. The call opens
with a gate - press 1 if now is a good time, nothing recorded otherwise -
then reads the report back in the person's own numbers, asks one question,
and the recording is fetched, transcribed inline through the same paid
Gemini path, redacted, sealed under the month's key and deleted from the
carrier in a
finally. Rows from both channels live in one sealed blob per assessment that survives a restart and dies with the key; the burn receipt names it and hashes its ciphertext. - The check-in reads the record before it asks: a heavy calendar day earns "about 5 hours of meetings today, how did those go?", an imported night gets confirmed rather than guessed, and a question skipped last time is asked first next time. Resting heart rate joins from the Apple Health export - the watch's own daily figure only, never a median of raw samples.
- Onboarding shows rather than tells: a stage with depth where a tilted phone plays the text exchange, the call with its press-1 gate, and the burn, with the presence floating in front. The thing you talk to is a choice - a lit sphere, a drawn portrait, or a shaded figure - and every look keeps its eyes closed, because nothing here watches you.
- Onboarding asks for each permission on its own, with why and what is kept, and nothing fires on load; a "Make it yours" step (companion, look, colours, background, the areas a person refuses to discuss, an optional daily face photo that stays on the device and is never analysed) and a "What can be shared" step that explains every choice without asking for one come before the destruction warning. The check-in remembers the person's own recent answers inside the period (numbers and topic names only) to ask a better question the next day, and asks one deeper follow-up after they write, from validated templates behind the same boundary guards as the review.
- The web app is framework-free ES modules; the Android app is a TWA of the
same deployed PWA, live on Google Play as
com.goalsandgambles.disavow.
Challenges
Making "we tell the truth about ourselves" mechanical, and then discovering we had not.
/healthz answers "does state survive a restart?" by probing live, because the
first version read an environment variable and reported healthy while production
403'd. Honesty as a probe, not a claim, became the design rule for every status
surface.
The harder lesson came from auditing our own promise. /review, /timeline and
/readable all gated on the encryption key. /report did not - it assembles
from the timeline held in memory and never decrypts anything, so it was never
written to check. The result: the kill switch returned a receipt saying nobody
could read this any more, including us, and a GET one second later returned the
same month. Found by probing the deployed service, not by reading code. Every
read path is now gated, the burn drops the plaintext rather than guarding it,
and a test asserts all four paths agree - because a lock in front of data that
is still there is not the erasure the receipt claims.
Accomplishments
A review pipeline where nothing uncited, un-recomputed, prescriptive, or unreachable can reach a person - enforced by code lenses, demonstrated by tests that feed the board deliberately bad observations. And a burn that works with the service switched off.
What we learned
"Built and tested" is not "wired". Our audit found the report renderer, the ingest call, and the agent proposer each finished and connected to nothing. The 54-slot rubric - the report's centrepiece - was structurally unanswerable: it was called with a hardcoded empty list, and the proposer parsed the model's domain to validate evidence ids and then discarded it, so no observation could name a cell. The flagship finding in our own marketing, "on the days you ate later you had less sleep", could not fire at all: nothing produced meal timing, and the clock never reached the pattern engine. Both are wired now, and the report answers with real sentences.
Green tests hid all of it. The suites passed while the product's central claims were false, because tests assert what you thought to ask. Probing the deployed service asked different questions and got different answers.
What's next
Google-granted zero-data-retention for the Gemini project. It is requested and
still ungranted, so /healthz reports zdr_granted: false and the app says "not
yet granted" rather than rounding up - the panel is only worth anything if it
keeps saying the uncomfortable thing. Then 211 and FindTreatment.gov live resource
clients; the carrier's toll-free verification for the texting number (the
outbound path is built and budgeted, and stays inert until the carrier clears
it - the weekly call does not need it and works today); and more wearable
imports so the areas the check-in covers in the person's own words - family
time, money, how the body felt - can be answered from measured data as well.
Built With
- cloud-kms
- fastapi
- firestore
- gemini
- google-adk
- javascript
- python
- stripe
- twilio
Log in or sign up for Devpost to join the conversation.