A consent-locked self-screening companion. Connect your own X account and privately see the symptom-domain signals in your own posts β mapped to validated screeners and the DSM-5 criteria they relate to.
Important
Screening signals, not a diagnosis. A screen is not a diagnosis, and short public posts cannot establish DSM-5 duration, pervasiveness, impairment, or rule-outs. Mirror is an educational, non-clinical tool. If anything it surfaces resonates, talk to a clinician β not this page. In crisis? In the US, call or text 988.
It is trivial to make an LLM emit a confident "diagnosis" from someone's tweets. It is also invalid, stigmatizing, and β pointed at a named, non-consenting person β a defamation and harassment engine. So Mirror is built the opposite way:
You can only ever screen yourself. Server-side, Mirror calls only
GET /2/users/meand reads that user's own tweets. There is no field to type someone else's handle and no code path to fetch one. Signing in is the consent.
Tweets are analyzed for a single request and never stored. The Anthropic key stays server-side. See ETHICS.md β removing the consent lock is explicitly out of scope.
- Symptom-domain signals β e.g. low mood, anxiety, sleep disruption β each with a strength, the validated screener it maps to (PHQ-9, GAD-7, ISI, β¦), the DSM-5 criterion area, and a confidence.
- Candidate screening directions β with a column for what the text cannot establish, which is the honest gap between language and a diagnosis.
- On an ordinary timeline, it usually surfaces little or nothing β which is the correct result.
sequenceDiagram
participant U as You (browser)
participant M as Mirror (Next.js on Vercel)
participant X as X API
participant C as Claude
U->>M: Connect your X account
M->>X: OAuth 2.0 + PKCE (tweet.read users.read)
X-->>U: Authorize?
U-->>X: Approve (your own account)
X-->>M: code β access token (httpOnly cookie)
U->>M: /screen
M->>X: GET /2/users/me + your OWN tweets only
M->>C: aggregate language β screening signals (tool-use)
C-->>M: domains + screening directions + "can't-establish"
M-->>U: your private report (nothing stored)
git clone https://github.com/acuestamd/mirror && cd mirror
npm install
cp .env.example .env.local # fill in the four values below
npm run dev # http://127.0.0.1:3000-
Create an X OAuth 2.0 app at developer.x.com β Projects & Apps: type Web App / Confidential, scopes
tweet.read users.read offline.access, callbackhttps://<your-deployment>/api/callback(reading tweets needs a paid X API tier). -
Set the environment variables:
Variable Description X_CLIENT_IDOAuth 2.0 client ID X_CLIENT_SECRETOAuth 2.0 client secret X_REDIRECT_URIhttps://<your-deployment>/api/callback(usehttp://127.0.0.1:3000/api/callbackfor dev)ANTHROPIC_API_KEYAnthropic API key (server-only)
app/
page.tsx landing + consent-lock explainer
screen/page.tsx your result (client-rendered)
api/login start OAuth (PKCE + state)
api/callback verify state, exchange code, set session cookie
api/screen read YOUR OWN timeline β screening signals
api/logout clear session
lib/
twitter.ts X OAuth 2.0 + read helpers (users/me + own tweets only)
screen.ts Claude screening engine (tool-use, structured output)
pkce.ts PKCE + state
- Public timelines are curated and incomplete β absence of signal is not evidence of wellbeing.
- The signals are population-level correlates, not valid indicators about any individual.
- Not a medical device; not affiliated with X, the APA, or any health authority.
PRs welcome β see CONTRIBUTING.md and the Code of Conduct. Security issues: SECURITY.md. Ethical boundaries: ETHICS.md.
MIT.