The public LUMIS Studio passport for this project. Below is only what the run itself recorded.
This project has no signature: the page shows only what was actually recorded. LUMIS measures no code quality, convergence or risk scores and shows none here.
The author made this project public. Below are the documents of the same run, as the consilium recorded them.
The document is shown in part — the full one is in the owner’s ZIP.
Document Status: Final / Approved Date: September 4, 2026 Author: Product Management & Technical Leadership Target Delivery: Q4 2026
Traditional tenant background and financial screening is broken. Property managers and landlords spend hours manually reviewing PDF bank statements, verifying pay stubs, and attempting to spot sophisticated document tampering. This manual workflow creates long leasing cycles (often taking 3–5 business days), increases tenant drop-off rates, and exposes leasing organizations to document fraud and compliance liabilities under FCRA and GDPR.
RentGuard is an automated B2B SaaS tenant screening platform engineered to evaluate applicant affordability and background reliability in under 60 seconds. By combining Open Banking API integrations, direct document parsing, and real-time AI-driven metadata fraud detection, RentGuard delivers an instantaneous, standardized affordability and risk report directly into property management software (PMS) workflows or native dashboards.
| Persona | Role & Context | Core Motivations & Pain Points | Key System Interactions |
|---|---|---|---|
| Property Manager / Leasing Specialist (Primary Operational User) | Manages day-to-day applicant processing, lease execution, and unit turnaround across 50–500 units. | Pain: Hours wasted manually auditing PDFs; fears approving fraudulent applications. Goal: Rapid turnaround, clear affordability metrics, seamless PMS integration. | Initiates verification links via PMS or RentGuard Dashboard; reviews standardized reports; exports summary reports. |
| Prospective Tenant / Applicant (End User / Data Subject) | Individual applying for a residential rental property. | Pain: Intrusive forms, long wait times, lack of data privacy transparency. Goal: Secure, mobile-first, friction-free verification completed in minutes. | Receives SMS/Email link; grants explicit GDPR/FCRA consent; connects Open Banking or uploads PDF statements and ID. |
| Compliance & Risk Officer (Enterprise Stakeholder) | Oversees legal risk, Fair Housing/FCRA compliance, and data privacy across property portfolios. | Pain: Regulatory fines, non-compliance with data privacy laws, improper adverse decision automation. Goal: Zero data resale, strict GDPR/FCRA compliance, clear legal disclaimers. | Audits data retention policies; verifies system consent workflows; configures access controls and audit logs. |
The table is wider than the screen — scroll it sideways.
> Instant 60-second tenant affordability and reliability verification with high-accuracy document fraud detection, eliminating manual bank statement reviews and shortening leasing cycles.
[ Property Manager ] ---> Dispatches Tokenized Link via PMS / Dashboard
|
v
[ Prospective Tenant ] --> Grants Consent & Connects Open Banking OR Uploads PDFs + ID
|
v
[ RentGuard Pipeline ] -> Analyzes Transactions + Runs 100% Metadata Fraud Engine (< 60s)
|
v
[ Property Manager ] <--- Receives Standardized Affordability & Risk ReportTo maintain operational integrity, strict legal compliance, and technical predictability, all product implementations must adhere to the following invariants:
| ID | Module | Feature Description | Priority |
|---|---|---|---|
| FR-INV-01 | Verification Invites | Single & bulk link dispatch via Email/SMS with custom property tags and expiration controls. | P0 |
| FR-INV-02 | Token Management | Auto-expiring tokenized URLs (72-hour TTL) with single-use session locks. | P0 |
| FR-AUTH-01 | Consent Framework | Multi-jurisdictional consent capture flow with granular GDPR and FCRA disclaimers. | P0 |
| FR-OB-01 | Open Banking | Aggregator integration (e.g., Plaid / TruLayer / Tink) supporting financial connectivity. | P0 |
| FR-OB-02 | Attempt Threshold | Hard cap enforcing max 2 failed Open Banking connection attempts before switching to PDF fallback. | P0 |
| FR-PDF-01 | Statement Ingestion | Client-side validated drag-and-drop zone for native and scanned PDF bank statements. | P0 |
| FR-PDF-02 | Metadata Scan | Streaming PDF structural analysis checking layer integrity, creation tools, modification history, and font anomalies. | P0 |
| FR-ID-01 | Document ID Check | Automated government-issued ID extraction, MRZ parsing, and facial comparison against selfie. | P1 |
| FR-ENG-01 | Income Verification | Recurring income identification algorithm calculating net monthly income, frequency, and stability index. | P0 |
| FR-ENG-02 | Affordability Calc | Automated Rent-to-Income (RTI) ratio computation and recurring debt obligation detection. | P0 |
| FR-ENG-03 | Anomaly Scoring | Transaction-level analysis identifying NSF fees, overdraft occurrences, and unverified large cash deposits. | P1 |
| FR-REP-01 | Standardized Report | Dashboard view and downloadable PDF highlighting raw metrics, risk flags, and mandatory disclaimers. | P0 |
| FR-INT-01 | PMS Integrations | Bi-directional webhook and API integrations for major PMS platforms (AppFolio, Yardi, Buildium, RealPage). | P1 |
| FR-INT-02 | API Key Admin | Self-service management panel for PM admins to generate, revoke, and scope API tokens. | P2 |
The table is wider than the screen — scroll it sideways.
+-----------------------------------------------------------------------------------+
| USER JOURNEY FLOW |
| |
| [Step 1: Invite] ---> [Step 2: Consent] ---> [Step 3: Connect/Upload] |
| | |
| [Step 5: Report] <--- [Step 4: Real-Time Engine] <----+ |
+-----------------------------------------------------------------------------------++-----------------------------------------------------------------------------------+
| SCREEN: Financial Data Connection Flow |
+-----------------------------------------------------------------------------------+
| |
| [ SECURE BANK CONNECT ] (Recommended) |
| Connect instantly via your bank. Fast, encrypted, 100% accurate. |
| [ Connect Bank Account ] <-- Attempt 1/2 |
| |
| ---------------------------------- OR ---------------------------------- |
| |
| [ UPLOAD BANK STATEMENTS ] |
| Drag and drop 3 months of official PDF bank statements. |
| +-----------------------------------------------------------------------------+ |
| | [Icon] Drop PDF statements here or click to browse | |
| +-----------------------------------------------------------------------------+ |
| |
+-----------------------------------------------------------------------------------+PDF Metadata Integrity: VERIFIED or Document Tampering Flagged: MODIFIED_LAYERS_DETECTED).| Operation | Max Allowed Latency (SLA) | Technical Strategy / Implementation Notes |
|---|---|---|
| End-to-End Processing Pipeline | < 60 seconds total | Parallel processing execution pipeline using asynchronous worker pools (Redis/Celery or AWS Lambda). |
| Open Banking API Handshake | < 2000 ms | Enforce aggressive socket timeouts on third-party aggregator calls with immediate retry logic. |
| PDF Metadata Streaming Scan | < 100 ms (Zero-latency) | Execute client-side pre-flight metadata extraction and server-side byte-stream header inspection upon file drop. |
| Dashboard Real-Time Status Push | < 500 ms | Event-driven WebSocket / Server-Sent Events (SSE) updates pushed to PM dashboard upon state changes. |
The table is wider than the screen — scroll it sideways.
+-----------------------------------------------------------------------------------+
| ERROR HANDLING MATRIX |
+-----------------------------------------------------------------------------------+
| Event | Immediate Action | User/System Resolution |
+------------------------------+--------------------------+-------------------------+
| Open Banking Failed (Attempt 2)| Block OB Module | Auto-fallback to PDF |
| PDF Metadata Modified | Continue Report Assembly | Flag "Tampering Detected"|
| Encrypted / Password PDF | Reject File Upload | Prompt for Unlocked PDF |
| Expired Transaction Token | Block Access | Render Re-issuance Link |
+-----------------------------------------------------------------------------------+Document Integrity Flag: Alterations Detected with a granular metadata log attached for PM inspection.To maintain operational focus, prevent feature creep, and protect against regulatory misclassification, the following capabilities are explicitly out of scope for RentGuard:
| Metric Category | Key Performance Indicator (KPI) | Target Baseline / Goal |
|---|---|---|
| Performance SLA | E2E Pipeline Completion Time | 95% of reports delivered in < 60 seconds |
| Conversion Efficiency | Applicant Flow Completion Rate | > 85% completion rate across all initiated invites |
| Open Banking Conversion | Successful OB Connection Rate | > 65% of applicants successfully complete OB verification |
| Fraud Detection Precision | Metadata Alteration Identification Rate | 100% detection rate on structurally altered PDF files |
| System Reliability | Platform Uptime & API Availability | 99.9% operational uptime (excluding scheduled maintenance) |
| Financial / Unit Economics | Open Banking Cost per Verification | < $1.20 average API cost per completed application token |
| Customer Satisfaction | Property Manager Time-to-Lease Reduction | Reduction of leasing decision time from days to < 15 minutes |
The table is wider than the screen — scroll it sideways.
+-----------------------------------------------------------------------------------+
| RISK MATRIX |The document is shown in part — the full one is in the owner’s ZIP.
System Architecture & Technical Specification Blueprint Project: RentGuard Architectural Pattern: Modular Monolith with Asynchronous Task Queue Worker Pipeline Target Environment: B2B Micro-SaaS / PMS Integrations / Web Application
RentGuard is an enterprise-grade tenant screening Micro-SaaS engine engineered for instant Open Banking income verification, PDF document fraud detection, and automated financial scoring. It provides property management companies (PMCs) and landlords with immediate risk verification reports while maintaining zero operational overhead from human-in-the-loop manual review desks.
+-----------------------------------------------------------------------------------+
| RENTGUARD EDGE |
| |
| +-------------------+ +-----------------------+ +---------------+ |
| | React 18 / Vite | <-- | REST API / WebSockets | --> | PMS Plugins | |
| | Tailwind UI | | (TLS 1.3) | | (Partner) | |
| +-------------------+ +-----------------------+ +---------------+ |
+-------------------------------------------|---------------------------------------+
|
v
+-----------------------------------------------------------------------------------+
| MODULAR MONOLITH APPLICATION |
| |
| +---------------------------------------------------------------------------+ |
| | FastAPI Application | |
| | | |
| | +--------------------+ +----------------------+ +-------------------+ | |
| | | Sessions Endpoints | | Reports Endpoints | | Core Auth & PII | | |
| | +--------------------+ +----------------------+ +-------------------+ | |
| +-------------------------------------|-------------------------------------+ |
| | |
| v |
| +---------------------------------------------------------------------------+ |
| | SQLite Core Data Engine | |
| | SQLModel (SQLAlchemy 2.0 + Pydantic v2) / WAL Mode | |
| +---------------------------------------------------------------------------+ |
+-----------------------------------------|-----------------------------------------+
|
+-------------------+-------------------+
| (Async Broker) | (Async Tasks)
v v
+-----------------------+ +-----------------------+
| Redis 7.x | | Celery Workers |
| (Queue / PubSub WS) | | (3-Stage Pipeline) |
+-----------------------+ +-----------------------+
|
+-----------------------+-----------------------+
| | |
v v v
+-------------------+ +-------------------+ +---------------+
| PDF Fraud Engine | | Open Banking Sync | | Scoring Engine|
| (100% Inspection) | | (Max 2 Attempts) | | (Non-Adverse) |
+-------------------+ +-------------------+ +---------------+componentDiagram
title RentGuard Modular Monolith & System Architecture
package "Client Layer" {
[React 18 Frontend] as UI
[PMS Plugin Integration] as PMS
}
package "Monolith Backend (FastAPI)" {
[API Gateway / Router] as Router
[Auth & Security Module] as Auth
[Session Domain Module] as SessionMod
[Report Domain Module] as ReportMod
[SQLModel Engine Engine (SQLite WAL)] as DB
}
package "Asynchronous Queue & Workers" {
[Redis Broker & State] as Redis
[Celery Worker Cluster] as Celery
[PDF Fraud Detector Service] as FraudEngine
[Open Banking Service] as OBEngine
[Financial Scoring Engine] as ScoringEngine
}
UI --> Router : HTTPS REST / WebSockets
PMS --> Router : HTTPS REST (API Keys)
Router --> Auth : Authenticate / Decrypt Token
Router --> SessionMod : Manage Verification State
Router --> ReportMod : Fetch Completed Reports
SessionMod --> DB : Read / Write Session & PII
ReportMod --> DB : Read Immutable Reports
SessionMod --> Redis : Dispatch Async Tasks / Notify WS
Redis --> Celery : Consume Tasks
Celery --> FraudEngine : Execute Metadata & OCR Verification
Celery --> OBEngine : Execute OAuth Sync (Limit: 2)
Celery --> ScoringEngine : Compute Indicators & Disclaimers
ScoringEngine --> DB : Persist Immutable FinancialReport
Celery --> Redis : Broadcast Status via WebSocketsFastAPI / Python 3.12):SQLite in WAL Mode + SQLModel):WAL) mode to achieve concurrent reads and atomic writes without database lock contention.Celery + Redis):PDFFraudDetector: Executes PDF parser checks (looking for mod dates, font anomalies, structure inconsistencies, and editing software stamps like Adobe Acrobat/Canva).OpenBankingConnector: Manages OAuth state tokens and strictly bounds sync attempts to 2 per transaction.ScoringEngine: Computes average monthly net income, cash-flow volatility, recurring debt obligations, and rent-to-income coverage ratios.erDiagram
ORGANIZATION ||--o{ USER : employs
ORGANIZATION ||--o{ APPLICANT : screens
APPLICANT ||--o{ VERIFICATION_SESSION : initiates
VERIFICATION_SESSION ||--o| FINANCIAL_REPORT : produces
ORGANIZATION {
uuid id PK
string name
string api_key_hash
datetime created_at
}
USER {
uuid id PK
uuid organization_id FK
string email
string password_hash
string role
datetime created_at
}
APPLICANT {
uuid id PK
uuid organization_id FK
bytes encrypted_pii
string email_hash
boolean consent_given
datetime consent_timestamp
}
VERIFICATION_SESSION {
uuid id PK
string token UK
uuid applicant_id FK
string status
integer open_banking_attempts
json uploaded_documents_meta
datetime expires_at
datetime created_at
}
FINANCIAL_REPORT {
uuid id PK
uuid verification_session_id FK
float net_monthly_income
float rent_to_income_ratio
integer tampering_flags_count
json indicators_payload
string legal_disclaimer
datetime generated_at
}rentguard-backend/app/models/domain.py)"""
Domain Data Models for RentGuard Screening Platform
Engineered with SQLModel (SQLAlchemy 2.0 + Pydantic v2)
"""
import uuid
from datetime import datetime, timezone
from typing import Optional, List, Dict, Any
from sqlmodel import SQLModel, Field, Relationship, JSON, Column
from sqlalchemy import LargeBinary
class Organization(SQLModel, table=True):
__tablename__: str = "organizations"
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
name: str = Field(index=True)
api_key_hash: str = Field(unique=True, index=True)
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
# Relationships
users: List["User"] = Relationship(back_populates="organization")
applicants: List["Applicant"] = Relationship(back_populates="organization")
class User(SQLModel, table=True):
__tablename__: str = "users"
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
organization_id: uuid.UUID = Field(foreign_key="organizations.id", index=True)
email: str = Field(unique=True, index=True)
password_hash: str
role: str = Field(default="property_manager")
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
# Relationships
organization: Optional[Organization] = Relationship(back_populates="users")
class Applicant(SQLModel, table=True):
__tablename__: str = "applicants"
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
organization_id: uuid.UUID = Field(foreign_key="organizations.id", index=True)
# Encrypted PII (Name, SSN/National ID, Phone) stored as encrypted AES-256-GCM binary byte array
encrypted_pii: bytes = Field(sa_column=Column(LargeBinary, nullable=False))
email_hash: str = Field(index=True) # Deterministic hash for lookups without decrypting
consent_given: bool = Field(default=False)
consent_timestamp: Optional[datetime] = Field(default=None)
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
# Relationships
organization: Optional[Organization] = Relationship(back_populates="applicants")
sessions: List["VerificationSession"] = Relationship(back_populates="applicant")
class VerificationSession(SQLModel, table=True):
__tablename__: str = "verification_sessions"
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
token: str = Field(unique=True, index=True)
applicant_id: uuid.UUID = Field(foreign_key="applicants.id", index=True)
status: str = Field(default="pending", index=True) # pending, processing, completed, failed
open_banking_attempts: int = Field(default=0) # HARD INVARIANT: Max 2 attempts
uploaded_documents_meta: Dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
expires_at: datetime
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
# Relationships
applicant: Optional[Applicant] = Relationship(back_populates="sessions")
financial_report: Optional["FinancialReport"] = Relationship(
back_populates="verification_session", sa_relationship_kwargs={"uselist": False}
)
class FinancialReport(SQLModel, table=True):
__tablename__: str = "financial_reports"
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True, index=True)
verification_session_id: uuid.UUID = Field(
foreign_key="verification_sessions.id", unique=True, index=True
)
net_monthly_income: float
rent_to_income_ratio: float
tampering_flags_count: int
indicators_payload: Dict[str, Any] = Field(default_factory=dict, sa_column=Column(JSON))
# Required FCRA/GDPR non-adverse legal disclaimer string
legal_disclaimer: str = Field(
default=(
"DISCLAIMER: This report provides raw financial verification indicators and automated "
"tampering analysis only. RentGuard does not issue consumer credit scores, nor does it "
"make automated adverse leasing decisions. Property managers must independently evaluate "
"this document in compliance with applicable local fair housing standards."
)
)
generated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
# Relationships
verification_session: Optional[VerificationSession] = Relationship(back_populates="financial_report")rentguard-backend/app/services/pdf_fraud_detector.py)"""
PDF Fraud Detector Engine
Analyzes 100% of non-Open-Banking uploaded bank statements for metadata manipulation,
font inconsistencies, software modification stamps, and stream anomalies.
"""
import io
from typing import Dict, Any, List
from pypdf import PdfReader
class PDFFraudDetector:
SUSPECT_CREATORS = [
"canva", "photoshop", "illustrator", "inkscape", "gimp",
"pdfescape", "smallpdf", "sejda", "iLovePDF"
]
def analyze_pdf_stream(self, file_bytes: bytes) -> Dict[str, Any]:
"""
Executes structural integrity checks on raw PDF byte stream.
"""
reader = PdfReader(io.BytesIO(file_bytes))
metadata = reader.metadata or {}
flags: List[str] = []
is_tampered = False
# 1. Inspect Producer and Creator metadata flags
producer = str(metadata.get("/Producer", "")).lower()
creator = str(metadata.get("/Creator", "")).lower()
for tool in self.SUSPECT_CREATORS:
if tool in producer or tool in creator:
flags.append(f"SUSPICIOUS_CREATOR_TOOL: Document created/edited using '{tool}'.")
is_tampered = True
# 2. Inspect CreationDate vs ModDate discrepancy
creation_date = metadata.get("/CreationDate")
mod_date = metadata.get("/ModDate")
if creation_date and mod_date and creation_date != mod_date:
flags.append(
f"MODIFICATION_DATE_MISMATCH: CreationDate ({creation_date}) differs "
f"from ModDate ({mod_date}). Document was modified post-generation."
)
is_tampered = True
# 3. Analyze Font Consistency & Embedded Subsets
font_names = set()
for page in reader.pages:
if "/Resources" in page and "/Font" in page["/Resources"]:
fonts = page["/Resources"]["/Font"]
if hasattr(fonts, "keys"):
for font_key in fonts.keys():
font_obj = fonts[font_key]
if hasattr(font_obj, "get") and font_obj.get("/BaseFont"):
font_names.add(str(font_obj.get("/BaseFont")))
# Statements edited with basic PDF editors often inject non-standard system fonts (e.g. Arial/Calibri on standard bank templates)
if len(font_names) > 12:
flags.append(f"HIGH_FONT_VARIANCE: Detected {len(font_names)} distinct fonts in bank statement.")
is_tampered = True
# 4. Stream Analysis (XRef & Incremental Updates)
# Multiple updates indicate post-export text modifications
raw_content = file_bytes.decode("latin-1", errors="ignore")
xref_count = raw_content.count("startxref")
if xref_count > 1:
flags.append(f"MULTIPLE_INCREMENTAL_UPDATES: Found {xref_count} startxref tokens indicating manual edits.")
is_tampered = True
return {
"is_tampered": is_tampered,
"flags_count": len(flags),
"flags": flags,
"metadata_summary": {
"producer": producer,
"creator": creator,
"page_count": len(reader.pages),
"detected_fonts_count": len(font_names)
}
}rentguard-backend/app/api/v1/endpoints/sessions.py)"""
API Endpoints for Session Token Lifecycle & Open Banking Connections.
Enforces Maximum 2 Open Banking Attempts and <2000ms Latency SLA.
"""
from datetime import datetime, timedelta, timezone
import secrets
from typing import Any
from fastapi import APIRouter, Depends, HTTPException, Status, UploadFile, File
from sqlmodel import Session, select
from app.models.domain import VerificationSession, Applicant, FinancialReport
from app.services.pdf_fraud_detector import PDFFraudDetector
router = APIRouter(prefix="/sessions", tags=["Verification Sessions"])
# Mock DB Session Dependency
def get_db():
pass
@router.post("", status_code=Status.HTTP_201_CREATED)
def create_verification_session(
applicant_id: str,
db: Session = Depends(get_db)
) -> Any:
"""
Creates a new tenant verification session token for an applicant.
Latency SLA: < 200ms
"""
token = f"rg_live_{secrets.token_urlsafe(32)}"
expires_at = datetime.now(timezone.utc) + timedelta(hours=24)
session = VerificationSession(
token=token,
applicant_id=applicant_id,
status="pending",
open_banking_attempts=0,
expires_at=expires_at
)
# Save to SQLite WAL database
# db.add(session); db.commit(); db.refresh(session)
return {
"status": "success",
"verification_token": token,What the Devil's Advocate challenged and what the Arbiter decided. Every risk got a verdict; the boundaries below are the outcome of the debate, not the first draft.
Added by the arbiter:
Dropped by the arbiter:
| # | risk | decision | action | | - | - | - | - | | 1 | FCRA / CRA Legal Reclassification Exposure | mitigated (invariant added) | Hard-code explicit disclaimers stating outputs are transaction analytics only and enforce non-decisioning report formats. | | 2 | Open Banking Conversion Collapse | accepted (scope changed) | Support structured PDF statement uploading as a seamless fallback while highlighting open banking approval speed benefits. | | 3 | PDF Fraud Detection Vulnerability | mitigated (invariant added) | Combine metadata verification with OCR-raster image analysis and automatically flag tampered documents for re-upload. | | 4 | Open Banking API Cost Squeeze | mitigated (invariant added) | Enforce a hard rate-limit of 2 Open Banking connection attempts per paid applicant session token. | | 5 | Integration of Traditional Credit Bureau Pulls | rejected | Keep credit bureau reselling strictly out of scope to avoid CRA liabilities and focus entirely on open banking analytics. | | 6 | Custom Risk Rule Engine | rejected | Maintain standardized affordability and verification metrics without custom underwriting rule engines. | | 7 | Lease Signing and Workflow Automation | rejected | Explicitly place lease contract generation, e-signatures, and deposit handling into Non-Goals. | | 8 | Manual Fallback Verification Desk | rejected | Reject human-in-the-loop manual review operations to remain a pure zero-operational-overhead software platform. |
Arbiter's rationale: RentGuard solves a critical pain point in tenant fraud detection with low sales complexity and strong willingness to pay. By enforcing hard boundaries against manual review desks, credit bureau pulls, and custom rule engines, the company preserves its high-margin Micro-SaaS economics while maintaining a sub-2000ms latency standard.
Disagreement level: 0.35