Inspiration

What it does

How we built it

Challenges we ran into

Accomplishments that we're proud of

What we learned

What's next for RealDoor

RealDoor

Your copilot for affordable-housing applications.

An application-readiness copilot for renters applying to affordable (LIHTC) housing. It reads your pay stubs, shows your confirmed income next to the actual HUD income limit for your household — with a citation — and assembles a clean, downloadable application packet. It never tells you whether you qualify. That's still your housing authority's call. RealDoor just makes sure you walk in ready.

Inspiration

Affordable-housing applications fail for boring, heartbreaking reasons: a missing pay stub, a number that doesn't match, an expired letter, a household-size box filled in wrong. Not because the applicant didn't qualify — because the paperwork wasn't ready. The rules are public but buried; the math is simple but easy to get wrong; and the person with the most at stake usually has the least clear picture of where they stand.

We wanted to close that gap without becoming yet another black-box "AI decides your future" tool. The whole idea rests on one line we refused to cross: RealDoor shows you the numbers, it never renders a verdict. Eligibility belongs to the housing authority.

What it does

A linear three-step journey:

  1. Profile — Upload a pay stub (PDF or a phone photo). RealDoor extracts only allowlisted fields — employer, gross pay, pay period, pay date, YTD — each shown next to its exact spot on the original document, with a confidence level. You confirm or correct every field before it's used anywhere.
  2. Understand — See your confirmed income beside the real HUD MTSP income limit for your household size, with the source citation and effective date. Ask plain questions about the rules and get answers that must cite a rule or abstain.
  3. Prepare — RealDoor diffs what you have against a checklist, flags what's missing, and renders one downloadable packet: profile summary, the cited calculation, and checklist status. Delete everything with one click.

No login. A session cookie ties your data together and is the single unit of deletion.

How we built it

  • Backend: FastAPI, Postgres (encrypted at rest) for session state, ChromaDB as the rules corpus, OpenAI for document extraction and grounded RAG answers.
  • Frontend: Next.js + shadcn/ui (Radix underneath), giving keyboard, focus, and ARIA correctness for free — status is never color-only, and every step announces completion through a live region.
  • Data: HUD MTSP 2026 income limits for Boston-Cambridge, versioned by effective date. We generated 20–30 synthetic pay stubs with matching gold-field JSON — including deliberate edge cases (missing field, expired date, blurry scan, and one carrying an injected "ignore previous instructions" attack).
  • Deployed from day one: frontend on Vercel, backend + Postgres on Render, so we tested and demoed against a stable URL the whole build, not just at the end.

Three design decisions did the heavy lifting:

  • The math is not the model's job. /calculate is plain deterministic code. The LLM never does arithmetic and isn't in the call path — it only retrieves the threshold's source passage. The comparison can't hallucinate.
  • Eligibility is structurally impossible. There is no eligibility field in any schema or any LLM output type. Nothing to disable — it was never buildable.
  • Retrieved text is data, never instructions. RAG chunks are passed delimited, and /ask must cite or abstain — no answer without a source.

What we learned

  • Constraints are a feature. "The model cannot decide eligibility" sounds like a limitation until you realize it's the entire trust proposition — and enforcing it in the schema is far stronger than enforcing it in a prompt.
  • Determinism beats cleverness where it counts. Pulling arithmetic out of the LLM removed a whole class of failure and made the output auditable.
  • Accessibility is cheaper early. Leaning on Radix primitives from the first component meant keyboard and screen-reader support came along for free instead of as a retrofit.
  • Ship to a real URL immediately. Deploying on day one surfaced integration issues while they were still cheap to fix.

Challenges we faced

  • Trustworthy extraction from messy inputs. Phone photos and blurry scans forced a vision path, and confidence had to actually mean something — we validated field-level accuracy and confidence calibration against gold data before the demo, not live during it.
  • Defending the guardrails on purpose. We built adversarial tests into the demo: an injected-instruction pay stub (extraction still returns only allowlisted fields) and a "just tell me if I qualify" question (/ask deflects to the rule and citation).
  • Keeping the UI honest and live. State had to stay in sync — e.g. the household-size stepper now live-updates the threshold and difference — without ever implying a decision the app is structurally forbidden from making.
  • Privacy that's checkable, not asserted. OpenAI calls run with no-training / zero-data retention, the audit log records actions but never field values or document content, and session deletion truly wipes the row and files — provable with a follow-up request.

Built With

Share this project:

Updates