BeforeYouSign is a Next.js web app that helps renters review residential lease documents before signing. Users upload a PDF lease, paste plain text, or load sample lease fixtures. The server validates the request, extracts and normalizes lease text, runs rule-based clause detection and deterministic review-priority scoring, then builds a structured report with grounded evidence. Results stay in the browser for the current review and can be exported as Markdown.
Lease agreements are long and written in dense legal language. Renters often struggle to quickly spot costs, notice and renewal rules, maintenance or utility responsibilities, and clauses worth questioning. This project reduces that friction by highlighting likely pressure points and summarizing them in plain language, while staying informational (not legal advice).
- PDF lease upload with server-side text extraction (
pdf-parse), plus paste text and built-in sample leases frompublic/sample-leases/. - Structured report UI: named report sections for summary, terms to review, money and fees, deadlines, responsibilities, questions, state checks, next steps, and “not clearly stated” items when applicable.
- Evidence linking: grounded findings show the exact quote, page, and source span; an explicit action opens and highlights that span in the extracted text viewer.
- Rule-based snippet extraction (rent, deposit, fees, notice, renewal, maintenance, utilities, and vague phrases) plus a deterministic review-priority band with reasons.
- Rule-only analysis: the current product path has no model call; the report is assembled from lease pattern matches, extracted text, and curated state-reference metadata.
- State-aware scope: all 50 states can be selected; Texas has curated statewide renter references, while other states receive general lease review only.
- Analysis transparency so users can see extraction quality, matched clauses, evidence coverage, and risk signals.
- Full report Markdown export (client-side download) plus existing question checklist export.
See the historical pattern-extraction audit for decisions and deferred scope. The supported current workflow is deterministic and synchronous.
Frontend: Next.js App Router, React, TypeScript, Tailwind CSS v4, Lucide icons, and shadcn-style UI primitives (src/components/ui/). The supported product theme is light-only. pdf-lib is used for client-side PDF preview metadata.
Backend: Node.js Next.js Route Handler — POST /api/analyze (src/app/api/analyze/) with request validation, upload limits, in-process concurrency guards, and structured error responses.
Analysis: Synchronous, rule-only lease pattern matching, risk scoring, Texas topic scanning, evidence registration, and structured report assembly.
Supporting code: pdf-parse for server-side PDF text extraction, pdf-lib for browser preview, curated Texas reference metadata, Vitest, and Playwright QA scripts.
There is no database, account system, background job, or runtime call to an external analysis service.
git clone https://github.com/ChimdumebiNebolisa/BeforeYouSign.git
cd BeforeYouSignnpm ciUse Node.js 24. Local development, CI, and Vercel use the version declared in .nvmrc.
Create a .env.local file in the root (you can start from .env.local.example):
Windows (cmd): copy .env.local.example .env.local
macOS / Linux: cp .env.local.example .env.local
No API key is required for local development. The supported environment variables are:
BYS_TRUST_PROXY_HEADERS=0 # Set to 1 only when the deployment proxy overwrites client IP headers.
BYS_ANALYSIS_EVENTS=1 # Set to 0 to disable metadata-only analysis event logs.
When proxy headers are not explicitly trusted, the app uses the global in-process concurrency cap. When they are trusted, it also applies a per-client cap. Analysis event logs contain metadata only; lease text is not logged.
npm run devOpen the local URL shown in the terminal (typically http://localhost:3000).
For this project:
- Choose intake: Select the rental-property state, then upload a PDF, paste lease text, or load a sample lease from the UI (
src/components/beforeyousign/). - Submit analysis: After the lease confirmation, the client sends
POST /api/analyze— multipart/form-data (file,stateCode) for PDFs or JSON (leaseText,fileName,stateCode) for pasted/sample text. - Validate and admit:
runAnalysisPipelinechecks the content type, state, request size, PDF signature, page/character limits, and in-flight analysis caps before processing. - Prepare the document: PDFs are extracted per page through
extractPdfTextPagesand normalized; pasted/sample text is normalized and represented as one synthetic page. Extraction quality and a content-deriveddocumentIdare recorded. - Run deterministic analysis:
runDeterministicAnalysiscalls the rule finders, computes the risk band and reasons, flags unclear phrases, and runs the Texas renter scan only when Texas is selected. - Assemble grounded results:
createRuleOnlyAnalyzerbuilds the fallback report, registers evidence spans, and returns an evidence index.assembleSuccessResponsepackages the report, pages, snippets, risk fields, state guidance, and extraction metadata. - Render and export: The client keeps the response in memory and renders
LeaseReportView,LeaseTextViewer, andTechnicalDetailsPanel. Findings can link back to source text, and the report/checklist can be downloaded as Markdown.
The main boundaries are:
src/app/: App Router entry points, layout, assets, and POST /api/analyze.
src/components/beforeyousign/: Intake, loading, report, source-text viewer, details, and export UI.
src/components/ui/: Shared UI (e.g. Button).
src/lib/analysis/pipeline/: Request validation, document preparation, deterministic orchestration, and response assembly.
src/lib/analysis/: Rule finders, scoring, report schema/normalization, fallback report, and exports.
src/lib/evidence/: Page segmentation, evidence registry, response index, and source highlighting data.
src/lib/pdf/: PDF extraction (`pdf-parse`), text normalization, and extraction-quality checks.
src/lib/jurisdiction/: Supported states and state-guidance status.
src/lib/legal-reference/: Curated Texas renter references and lease-topic scanner.
public/sample-leases/: Text fixtures used by the sample-lease flow.
tests/: Unit, integration, end-to-end, and lease/report fixtures.
scripts/: Production-server smoke and browser-QA runners.The request is synchronous. There is no database or queue; the report and extracted text live in client state after the response. Texas references are curated static metadata and are not scraped at runtime.
flowchart LR
subgraph Browser["Browser — React client"]
Intake["LandingClient<br/>state + PDF / paste / sample"]
Request["POST /api/analyze"]
Results["LeaseReportView + LeaseTextViewer<br/>TechnicalDetailsPanel"]
Export["Markdown report +<br/>question checklist"]
Intake --> Request
Results --> Export
end
subgraph Server["Next.js server — Node.js"]
Route["route.ts<br/>POST /api/analyze"]
Pipeline["runAnalysisPipeline"]
Validate["validate-intake.ts<br/>content type • state • limits • slots"]
Document["analyze-document.ts<br/>extract/build pages • quality • documentId"]
Deterministic["deterministic.ts<br/>rule snippets • risk • Texas scan"]
Engine["rule-only-analyzer.ts<br/>report • evidence registry"]
Response["assemble-response.ts<br/>structured JSON response"]
Route --> Pipeline --> Validate --> Document --> Deterministic --> Engine --> Response
PDF["pdf/extract-text.ts<br/>pdf-parse per page"]
Normalize["pdf/normalize.ts"]
Rules["analysis/rules.ts"]
Score["analysis/scoring.ts"]
Texas["legal-reference/<br/>texas-renter-scan.ts"]
Evidence["evidence/registry.ts<br/>index.ts"]
Document -->|PDF input| PDF --> Normalize
Deterministic --> Rules
Deterministic --> Score
Deterministic --> Texas
Engine --> Evidence
end
Request -->|fetch| Route
Response -->|JSON| Results
Flow notes: pasted/sample text is normalized during intake and skips PDF extraction. pdf-lib only supports browser-side preview metadata. The current AnalysisMode is rules_only; there is no AI/model branch in the supported request path.
Pull requests and pushes to main run the required ci workflow: clean install, lint, typecheck, unit tests, coverage, deterministic evaluation, legal-reference metadata checks, a production build, Playwright browser tests, browser-QA scripts, an API smoke test, support-script syntax checks, and a production dependency audit.
Run the same static and analysis checks locally:
npm run lint
npm run typecheck
npm test
npm run test:coverage
npm run evaluate
npm run verify:legal-metadata
npm run buildThe CI workflow also verifies that committed support scripts parse successfully:
node --check scripts/phase2-scan-smoke.mjs
node --check scripts/smoke-test.mjs
node --check scripts/phase1-browser-qa.mjs
node --check scripts/phase2-browser-qa.mjs
node --check scripts/run-scan-smoke.mjs
node --check scripts/evaluate.mjs
node --check scripts/run-browser-qa.mjs
node --check scripts/verify-legal-metadata.mjsRun production-build browser tests after npm run build:
npm run test:e2everify:legal-metadata checks reference IDs, review dates, URL syntax, and disclaimer metadata; it does not validate legal meaning. Complete the quarterly source review in LEGAL_SOURCE_REVIEW.md separately.
The smoke and browser-QA runners start and stop their own production server. Build first, then run:
npm run smoke:scan
npm run qa:smoke
npm run qa:phase1
npm run qa:phase2Install Playwright browser binaries first if you have not run browser QA on this machine:
npx playwright install chromium firefox webkitThe browser QA scripts use Playwright and write ignored screenshots under test-results/browser-qa/. CI uploads failed browser artifacts for seven days.
- No user accounts or persisted reports — results live in browser memory and are lost when the page is closed or a new review is started.
- Rule-only analysis — regex and heuristic matching can miss clauses, produce false positives, or lack the context a lawyer would apply.
- Single synchronous HTTP request — the route allows up to 60 seconds and the browser waits up to 55 seconds; very large or slow inputs may time out.
- PDF text extraction is not OCR — scanned image-only PDFs may yield little or no extractable text.
- Input limits apply — PDF files are capped at 4 MiB and 100 pages; extracted or pasted text is capped at 120,000 characters. JSON request bodies are capped at 512 KiB.
- State scope is limited — Texas has curated statewide renter references; other states receive general lease review, and city rules are not checked.
- Evidence highlighting uses extracted-text offsets and quote matching; minor mismatches between source text and normalized quotes can prevent a highlight.
- Concurrency protection is in-process — the global and optional per-client caps reset with the server instance and are not a substitute for edge/platform rate limiting in a multi-instance deployment.