The market's implied yield based on current appraised value. Moves inversely with price.
Methodology & Definitions
Data sources, calculations, dashboards, and limitations across the REPI analytics suite. This document is the reference for any figure or indicator rendered on a REPI dashboard.
Data Sources
eFront Holdings Extract
The primary portfolio registry. A CSV snapshot (currently
REPI eFront 04_03_26.csv) containing ~550 properties with
classifications, ownership, advisors, JV partners, acquisition prices,
regions, metros, and fiscal year ends.
| Field Category | Examples |
|---|---|
| Identity & Classification | Property, Short Name, Custom Classification 1/2 (asset & sub-type) |
| Geography | NCREIF Region, NCREIF Sub Region, Metro Area, State, City, ZIP |
| Ownership & Partnership | SBAF Legal Ownership %, JV Partner, Title Holding Entity, Company (advisor) |
| Dates | Acquisition Date, Disposal Date, JV Buyout Date, Investment Year End |
| Valuation | Acquisition Price, Disposal Price (when realized) |
| Physical | Total GLA, OFC/RTL/APT/LAB GLA, # Units, # Buildings, Year Built, Year Last Renovation |
SitusAMC Quarterly Appraisal Database
Source for forward-looking valuation mechanics. Used by the Monte Carlo volatility calibration and DCF assumptions. Provides property-level appraisals (value, rent, occupancy, cap rates, terminal & discount rates) at quarterly cadence.
Data cleaning: Dollar values stripped of formatting.
Percentages converted to decimals. Rows filtered to live assets.
Invalid sentinels (-, #N/A, #DIV/0!)
treated as missing.
Loan Maturities Extract
A per-property loan file
(REPI_Loan_Maturities/eFront_Loan_Maturities.csv)
powering debt analytics. Columns: Current Leverage Balance (SBA level),
Outstanding Loan Balance (total), Maturity Date, Lender Type, Loan
Type, Life Cycle, Risk Profile, SBA RE Action.
MTF Excel (Most-Recent Valuation)
Property-level NAV and Life Cycle data. Loaded dynamically by pattern match in the REPI_Dashboard directory. Feeds into PRISM scenario analysis at the SBA Investment Level.
Weekly Operating Workbooks
Per-property Excel workbooks (approximately 32 files) containing weekly-cadence operating metrics: occupancy, availability, leased %, market and effective rent, rent-per-SF, vacant units. Drives the Operations dashboard's weekly view.
Income Statements
Quarterly income-statement Excel files, organized by period folder
(1Q25, 4Q25, etc.). Actual vs. budget for
17+ properties; powers the Financial Performance section of the
property report and the Variance Report.
Bitcoin Monthly Prices
btc_monthly.csv — monthly close prices from September
2014 onwards. Used by the Monte Carlo engine when the BTC Sleeve
toggle is enabled. Yahoo refresh runs best-effort on startup; the
committed CSV is the fallback.
Geographic Mapping
NCREIF four-region taxonomy (East, West, South, Midwest). A
geocache.csv maps property names to pre-computed
lat/lon; fallback logic uses metro center then state center then
US center when a property is absent from the cache.
Portfolio Operations
What It Shows
Property-level KPIs, variance reporting, Google Reviews, Monte Carlo projections, investment-story narratives, and the property report across the multifamily portfolio. Weekly workbooks + quarterly income statements feed every view.
Property Report Header
The institutional-style blue header on each property report carries:
property name, location, address, year built, and the asset's
Fiscal Year End — pulled from the eFront
Investment Year End column for 540+ properties. Fuzzy name
matching reconciles Weekly ↔ eFront title differences.
Key Metrics
| Metric | Calculation |
|---|---|
| Total Portfolio Value | Sum of all live-asset valuations at latest quarter |
| YoY Portfolio Appreciation | (Total Value Qt / Total Value Qt−4) − 1 |
| By-Type Aggregates | Group by property type → count, total value, avg implied cap rate, avg terminal rate, avg discount rate, avg YoY value change |
| By-Region Aggregates | Group by NCREIF region → count, total value, avg implied cap rate |
| Estimated NOI | Value × Implied Cap Rate |
| NOI Margin | Actual NOI / Actual Revenue (from income statements) |
| Budget Variance | (Actual − Budget) / Budget × 100 |
Historical Performance
What It Shows
Trailing 1Y / 3Y / 5Y / 10Y composite returns, excess returns measured
in basis points, and strategy attribution versus institutional
benchmarks. Data source: TTG_Data_Historical.xlsx.
How to Interpret
Composite returns reflect time-weighted performance over each trailing window. Excess returns (bps) show out/underperformance vs. the selected benchmark. Strategy attribution decomposes returns into allocation and selection effects.
Note: Returns are calculated from appraisal-based valuations, which inherently smooth short-term volatility compared to transaction-based indices. See §08 Volatility Calibration for how this is addressed in forward-looking models.
Portfolio Holdings & PRISM
Holdings Registry
A 550-property registry from the eFront extract: geographic concentration mapping, advisor and JV partner breakdowns, loan maturity wall, and benchmark positioning against NCREIF NPI / ODCE.
Benchmarks
| Benchmark | Description |
|---|---|
| NCREIF NPI | National Council of Real Estate Investment Fiduciaries Property Index. Quarterly, unleveraged total-return index of institutional-grade commercial real estate. |
| NCREIF ODCE | Open-End Diversified Core Equity. Fund-level benchmark that includes leverage and fees; a closer proxy to realized investor experience. |
PRISM — Portfolio Risk & Investment Scenario Model
An interactive transaction-approval tool. The user enters the attributes of a proposed acquisition — property, advisor, JV partner, ownership %, acquisition price, equity, leverage, NAV, risk profile, life cycle, investment structure — and PRISM shows the before / after impact on every Investment Portfolio Guideline.
Data Unification
PRISM merges four sources at the property level:
| Source | Provides |
|---|---|
| eFront CSV | Property attributes, classifications, acquisition prices, ownership |
| Loan Maturities CSV | SBA leverage balance, total loan balance, life cycle, risk profile |
| MTF Excel (NAV) | Current property-level Net Asset Value (SBA investment level) |
| PRISM Adjustments Excel | Pipeline adjustments: Closed Pipeline NAV, Pending Pipeline NAV, Unfunded Commitment, Unfunded Construction Loan Draws, Unallocated Commitment, GAV |
Property name matching across sources uses a Jaccard token-similarity fuzzy merge so "Carlyle Crossing - Dylan" in the weekly workbook reconciles to the same entity in eFront.
IPG Compliance Matrix (11 checks)
Every proposed transaction is scored against all 11 Investment Portfolio Guidelines. Each check returns a pass / fail badge on the PRISM report.
| # | Guideline | Rule |
|---|---|---|
| 1 | Development & Lease-Up Exposure | (Dev + Initial Leasing + Redevelopment) Adj NAV / Total ≤ 15% |
| 2 | Investment Advisor Concentration | Max advisor Adj NAV / Total RE NAV ≤ 35% |
| 3 | JV Capitalization | Any JV partner total capitalization / Gross NAV ≤ 10% |
| 4 | JV Leverage | Individual JV LTV ≤ 70% (case-by-case) |
| 5 | Metro Area | Max metro Adj NAV / Total ≤ 15% |
| 6 | Portfolio-Level Leverage | Total LEV / Total GAV ≤ 35% |
| 7 | Property Type | Each type within ±15pp of ODCE weight |
| 8 | Region | Each region within ±15pp of ODCE weight |
| 9 | Risk Profile | Core 85–100%, Non-Core 0–15% |
| 10 | Single Investment Concentration | Max single investment NAV / Total ≤ 7% |
| 11 | Wholly Owned Property Leverage | Individual wholly-owned LTV ≤ 50% |
PRISM Dimension Tables
Before / after tables are computed for nine dimensions:
Property Type · Region · Sub-Type · Risk Profile · Life Cycle · Investment Structure · Investment Advisor · JV Partner · Metro Area
Each row carries the full PRISM column set:
NAV + Closed Pipeline + Pending Pipeline + Unfunded + Construction Loans + Unallocated
Adjusted NAV Before + Proposed Transaction NAV
% Adj NAV After − ODCE Weight (shown for Property Type & Region)
Data Validation
What It Does
A standalone dashboard giving the team three tools for building confidence in the eFront holdings extract: a random-question quiz, a rule violation scanner, and an outlier surface. Results persist in SQLite so accuracy trends can be reviewed over time.
Quiz Engine
Eleven templates draw random questions from the 550-property data. Each session is attributed to a display name; answers are graded in real time and persisted with the correct answer for audit.
| Template | Example |
|---|---|
| Count by advisor | How many properties does Heitman manage? |
| Count by asset × region | How many Industrial properties are in the West? |
| Sum acquisition by asset | Total acquisition price of all Retail properties? |
| Sum acquisition by state | Total acquisition price in Texas? |
| Lookup metro / advisor | Which metro is Astoria in? Which advisor runs it? |
| Lookup ownership % | What is SBAF's ownership in Carlyle Crossing - Dylan? |
| Lookup JV partner | Who is the JV partner on Bozzuto I - Enders Place? |
| Lookup year acquired / built | What year was South Beach Marina acquired? |
| Boolean: wholly owned? | Is Dimension wholly owned? |
Grading tolerance: exact match for strings (case-insensitive, trimmed); ±1 percentage point for ownership; ±1% or ±$100K (whichever is larger) for sums; exact for counts and years.
Rule Violations (10 rules)
| ID | Rule |
|---|---|
| R1 | SBAF Legal Ownership % between 0 and 1.0 |
| R2 | Disposal Date ≥ Acquisition Date |
| R3 | Status consistent with presence/absence of Disposal Date |
| R4 | Year Built within [1800, current year] |
| R5 | Year Last Renovation ≥ Year Built and ≤ current year |
| R6 | Total GLA ≥ max component GLA (within 5% tolerance) |
| R7 | Required fields populated (Property, Asset Type, Region, Metro) |
| R8 | JV Partner ↔ Ownership % consistency |
| R9 | Disposal Date and Disposal Price present together |
| R10 | Completeness warnings — missing optional data (Acq Date, Year Built, Metro) |
Outlier Detection (8 heuristics)
| ID | Check |
|---|---|
| O1 | Acquisition price > 2× P95 or < P5/2 within its asset type |
| O2 | SBAF ownership < 50% (portfolio is mostly 95–100%) |
| O3 | Pre-1950 construction (vintage-risk flag) |
| O4 | Zero Total GLA on a non-Agriculture property |
| O5 | Acquisition Date in the future |
| O6 | Active holding period > 15 years |
| O7 | Disposed without a Disposal Price |
| O8 | JV Partner present but ownership = 100% |
Legal Document Library
What It Is
A 6-bucket document repository with drag-and-drop PDF uploads, property / advisor / JV tagging, SQLite-backed indexing, admin-gated delete, and a Florida Statutes Chapter 215 statutory reference panel.
Taxonomy (6 categories)
| Category | Typical Contents | Governs |
|---|---|---|
| Fund & Partnership Governance | LPA, PPM, Side Letters, Subscription Agreements, IPS | §215.44, §215.47, §215.475, §215.48 |
| JV Operating Agreements | JV Operating Agreement, Partnership Agreement, Advisor AMA | §215.4754, §215.4755, §215.855 |
| Property-Level Transactional | PSA, Deed, Title Policy, Closing Binder, Estoppel | §215.47 |
| Debt & Financing | Loan Agreement, Promissory Note, Mortgage, Guaranty, Intercreditor | §215.43, §215.57, §215.684 |
| Leases & Operating | Master Lease, Ground Lease, Property Management Agreement, SNDA | §215.47 |
| Regulatory & Compliance | Single Audit, SOC-2, Divestiture Certs, Board Resolutions, Good Standing | §215.4401, §215.471, §215.472, §215.97, §215.985 |
Upload Behaviors
- PDF only. Non-PDF uploads are rejected server-side.
- 50 MB per file cap. Enforced in both Flask and Nginx (client_max_body_size 60M on
/legal/). - Versioning: duplicate filenames get a timestamp suffix (e.g.
LPA_2026-04-13_010203.pdf) — nothing ever overwrites. - Tagging: at upload, the user optionally tags the document with Property (from 550 eFront names), Advisor (from 8 canonical names), and JV Partner (from 34 known partners).
- Audit log: every upload and delete is timestamped and attributed to the Nginx basic-auth username.
- Admin-gated delete: only
repi_admincan remove files; team uploads freely.
Florida Statutes Chapter 215 — Statutory Reference
The Legal Library ships with a curated reference to eight key sections
of Florida Statutes Chapter 215 (Financial Matters — General
Provisions). Each category card links out to the sections that govern
its contents. Links resolve to
flsenate.gov/Laws/Statutes/2025/...
| Section | Subject |
|---|---|
| §215.44 | Board of Administration — Investment of Trust Funds |
| §215.47 | Authorized Investments — Real Estate Authority |
| §215.475 | Investment Policy Statement (IPS) |
| §215.4754 | Ethics — Investment Advisers & Managers |
| §215.4755 | Certification & Disclosure |
| §215.4401 | Public Records Exemptions — SBA |
| §215.471 | Divestiture & Reporting |
| §215.97 | Florida Single Audit Act |
Snowflake Migration Proposal
Scope
A 9-month implementation proposal for lifting the REPI-ONE stack onto Snowflake, unifying advisor chart-of-accounts, and standing up a sandboxed development environment for continued feature work. This is a planning document — no live Snowflake connection exists yet.
Phased Timeline
| Phase | Months | Deliverables |
|---|---|---|
| 01 · Foundation | 1–3 | Mac Studio + DGX Spark fleet; Snowflake tenant (Dev+Prod); VLAN design; VPN access; schema drafts |
| 02 · Migration | 4–6 | Port eFront, MTF, Income Statements, SitusAMC into Snowflake; chart-of-accounts crosswalk; shadow reconciliation |
| 03 · Cutover & Governance | 7–9 | Production cutover; RBAC; audit logging; runbook; decommission legacy pipelines |
Infrastructure Requirements
- 3–4 Mac Studio M3 Pros with max RAM — development cluster.
- 1 NVIDIA DGX Spark — ML/AI coordinator for document summarization, anomaly detection, forecasting.
- Snowflake tenant with separate Dev and Prod accounts and promotion gates.
- VLAN isolation — dev fleet sandboxed; egress to Snowflake only.
- VPN-gated access — role-scoped, easily revocable per team member.
Supporting Documents
The proposal page hosts seven document categories with the same drag-and-drop + SQLite indexing pattern as the Legal Library: Migration Plan · Chart of Accounts · Hardware Specifications · Network & Security · Accounting DB Schema · Cost Estimate · Risk Register.
Volatility Calibration
The Problem: Appraisal Smoothing
Real estate appraisals lag actual market movements. Observed quarter-to-quarter value changes understate true economic volatility because appraisers anchor to prior valuations. Raw portfolio statistics will therefore look less volatile than they truly are.
Geltner (1993) Unsmoothing
The Geltner unsmoothing correction — an institutional standard — backs out the appraisal lag to estimate true transaction-based volatility.
factor = 1 / (1 − α) = 1 / (1 − 0.40) = 1.667
σNOI = min(raw_vol × 1.667, 0.40)
σcap = raw_volcap × 1.667
| Parameter | Value | Source |
|---|---|---|
| Smoothing parameter (α) | 0.40 | NCREIF / NPI core real estate standard |
| Raw NOI volatility | IQR-winsorized std of YoY value changes | Situs quarterly |
| Raw cap rate volatility | Std of implied cap rate changes | Situs quarterly |
| Small-sample fallback | 8% NOI vol, 70 bps cap vol | Applied when calibration sample < 3 assets |
Outlier handling: Before computing raw volatilities, values are IQR-winsorized at ±3×IQR and capped at 25% to prevent single extreme appraisals from dominating the calibration.
Monte Carlo — NAV Projection
What It Projects
Per-property forward NAV projection at monthly cadence when the BTC Sleeve is on, quarterly otherwise. The engine anchors at the most recent non-null SBA NAV and emits a full distribution of forward paths — fan chart percentiles, a terminal histogram, and ten sample paths for visual interpretation.
Four Simulation Methods
| Method | Model | Best For |
|---|---|---|
| Historical Bootstrap | Resamples observed quarterly returns with replacement | Preserving empirical tails; no distributional assumption |
| Log-Normal GBM | Fits log-normal diffusion (µ, σ) to historical log-returns | Smooth parametric fit; easy to explain; thin-tailed |
| Regime-Switching | 3-state Markov chain (Growth / Stable / Decline) with tercile-based labels and Laplace-smoothed transitions | Modeling sticky regimes; macro-cycle aware |
| Bitcoin Power Law | BTC follows Santostasi's log-log power law with mean-reverting shocks; property returns bootstrapped | BTC Sleeve mode; long-horizon trend-aware BTC modeling |
Controls
| Input | Range | Notes |
|---|---|---|
| Property | Any apartment in the portfolio | Must have ≥ 7 quarterly NAV observations |
| Simulation Method | Bootstrap / GBM / Regime / Power Law | Power Law requires BTC Sleeve on |
| Horizon | 4 – 40 quarters | 1 to 10 years forward |
| Paths | 1,000 – 50,000 | Default 10,000 — stable percentile estimates |
| Fit Window | Last N quarters or "All" | Downweights older regimes if shorter than max |
| BTC Sleeve | Off / On · 2.5 / 5 / 10% | Joint property + BTC simulation with three allocation scenarios |
BTC Sleeve — Joint Simulation
When the BTC Sleeve toggle is on, the engine runs a joint property + Bitcoin simulation at monthly cadence. For Bootstrap and Regime-Switching methods, correlation is preserved automatically because every drawn quarter index carries both its property return and its three paired BTC monthly returns from the same historical period.
Each run produces three portfolio scenarios side-by-side: 2.5%, 5%, and 10% BTC allocation at t₀ (no rebalance). Metrics displayed per scenario: Median Terminal, VaR-95, Expected Shortfall, P(Loss), Best/Worst Case.
Data alignment: BTC monthly data starts Sep 2014. Property quarters ending before that date are dropped. Below-6-quarter alignment returns a "no data" state; the dashboard shows an empty placeholder.
Bitcoin Power Law (Santostasi)
Giovanni Santostasi's thesis: long-run BTC price follows a power function of time since the Genesis block (Jan 3, 2009). The engine fits this model by log-log OLS on the committed BTC monthly history at service startup.
ln(price) = ln(A) + B · ln(days_since_genesis)
Pt = mean_price(datet) · exp(deviationt)
deviationt = φ · deviationt−1 + εt, εt ~ N(0, σresid)
The deviation AR(1) reverts toward the trend with a 24-month
half-life (φ = 0.5^(1/24)). Deviations are clipped at
±3× the support/resistance offsets to prevent explosive excursions.
| Band | Log Offset | Interpretation |
|---|---|---|
| Fair-value (mean) | 0 | Long-run power-law trend line |
| Cycle-low support | −0.6 | ~45% below trend; catches most bear-market lows since 2013 |
| Fair-value / cycle-top | +0.7 | ~100% above trend; catches cycle tops |
Typical fit: exponent B ≈ 5.84 on the current
btc_monthly.csv, consistent with Santostasi's published
B ≈ 5.82. The UI shows the fitted exponent next to a log-price chart
with all three bands overlaid.
Outputs
| Output | Description |
|---|---|
| Percentile Bands | P10 / P25 / P50 / P75 / P90 at every time step |
| Fan Chart | Monthly (BTC on) or quarterly (BTC off) projection envelope |
| Terminal Histogram | Distribution at the final time step with median & VaR markers |
| Sample Paths | Ten randomly-drawn paths for visual texture |
| KPI Strip | Median terminal, VaR-95, ES-95, P(Loss), annualized rate |
| Power Law Band Chart | (Power Law method only) BTC price log chart with support / mean / resistance |
Definitions
A working glossary of the metrics, methods, and benchmarks used across the REPI analytics suite — grouped by the question each one answers.
Valuation & Yield
How income, leverage, and equity are translated into value.
Strips out lease-up, renovation, and transition effects to reveal the asset's underlying yield.
Cap rate applied at hold-period end to estimate sale value. Higher exit rate = lower exit value.
Required return used to discount future cash flows. Reflects the investor's cost of capital.
Gross rental income less operating expenses, before debt service and capex.
Standard leverage ratio. Calculated on an incremental basis in PRISM — i.e., after pipeline adjustments and the proposed transaction.
The headline value the LP realizes. Used as the anchor for Monte Carlo NAV projection and for PRISM compliance math.
Standard unit for small yield or rate differences. 100 bps = one percentage point.
Statistical & Modeling Methods
How uncertainty is measured and simulated.
Limits the pull of a few extreme appraisals on a volatility estimate by capping values at three times the interquartile range.
The share of each appraisal anchored to the prior period. Unsmoothed via 1 / (1 − α) to estimate transaction-based volatility.
Discount rate that makes the net present value of all cash flows — including acquisition and terminal sale — equal to zero.
Rather than one forecast, thousands of randomized scenarios are run so outcomes can be expressed as a distribution and percentiles.
Non-parametric method: future returns drawn at random from the observed historical sample. Preserves empirical tails exactly.
Geometric Brownian Motion — a smooth parametric model. Fits historical µ and σ of log-returns. Thin-tailed relative to real CRE.
Labels quarters by tercile of return, estimates a Laplace-smoothed transition matrix, and bootstraps within the active regime.
Per-time-step envelopes across the simulated path ensemble. P50 is the median outcome; P10–P90 brackets 80% of outcomes.
Benchmarks
The yardsticks the portfolio is measured against.
National Council of Real Estate Investment Fiduciaries Property Index. Quarterly, unleveraged total-return index of institutional-grade U.S. commercial real estate.
Open-End Diversified Core Equity. Fund-level benchmark that includes leverage and management fees — a closer proxy for realized investor experience.
Governance & Compliance
The policy rails that every transaction must respect.
The rulebook checked by PRISM on every proposed transaction. Covers concentration, leverage, risk profile, and benchmark-deviation limits.
Each asset type and each region must sit within 15 percentage points of its NCREIF ODCE reference weight.
Portfolio-level allocation limit between Core and Opportunistic / Non-Core strategies.
Caps aggregate exposure to pre-stabilized assets. Stabilized Operating exposure is the residual.
No one asset can dominate the portfolio. Evaluated after pipeline adjustments.
Limits total capitalization (equity + CFP) to any single JV partnership.
Real Estate Allocation Policy 15-701. Caps exposure to any one investment advisor.
The governing statute for SBA investment authority. Real estate authority lives at §215.47; IPS required by §215.475.
Governs what SBA records may be withheld from public disclosure. Drives the Legal Library's audit-log posture.
Bitcoin Power Law Model
Santostasi-style BTC trend modeling used in the Monte Carlo engine.
The log-log slope of BTC price vs. days since Genesis. REPI fits B locally at startup; current fit ≈ 5.84.
The independent variable. The Bitcoin genesis block was mined January 3, 2009; day 1 anchors the power law.
Fitted with numpy.polyfit (degree 1) on all valid observations. Residuals feed the log-normal shock standard deviation.
Empirical dispersion around the trend. Sets the scale of log-normal shocks applied in forward simulation; floor 0.15, cap 1.20.
Roughly 45% below the fair-value line in price space. Catches most bear-market lows since 2013.
Roughly 2× the fair-value line in price space. Catches historical cycle tops.
AR(1) coefficient on log-deviation from trend. Every 24 months, a shock loses half its pull.
Three side-by-side scenarios rendered in BTC Sleeve mode. No rebalance — allocation applies at t₀.
Known Limitations
Data
- Snapshot calibration. Volatility is typically calibrated from one quarterly cycle. This may under- or over-estimate long-run volatility.
- Appraisal lag. Situs valuations lag actual transaction prices by 0–6 months. The Geltner correction mitigates but does not eliminate this.
- Cross-sectional proxy for NOI volatility. Year-over-year value changes are used as a proxy for NOI volatility; raw NOI data is not in the Situs feed.
- Small samples. When fewer than 3 assets match a property type or state filter, hardcoded fallback volatilities are used (8% NOI, 70 bps cap rate).
PRISM
- Pipeline adjustments are manually maintained. The PRISM Adjustments Excel is the single source of truth; refresh cadence is quarterly.
- ODCE weights are coded. Hardcoded at PDF values; refreshed manually when NCREIF publishes new quarter weights.
- No debt waterfall modeling. Leverage is treated as a single aggregate per property; senior/mezz/preferred equity distinctions are not modeled.
Monte Carlo
- Equity-only. No debt modeling, no waterfall allocations, no LP/GP splits.
- Fixed discount rate. Constant across paths; real-world interest rates are themselves stochastic.
- No CapEx. Simplified NOI stream; major renovation or capex events are not modeled.
- Cap rate bounds. Exit cap rates hard-clamped to 3%–12%; extreme distress or bubble scenarios outside this range are not captured.
- Power Law breaks BTC ↔ RE correlation. Under Power Law method, BTC paths follow the trend model while property returns are bootstrapped independently. The historical monthly linkage is not preserved.
Data Validation
- Single data source. Rules and outliers run against the eFront extract only. Cross-source reconciliation (MTF vs. eFront vs. Situs) is deferred.
- Static quiz templates. Eleven hardcoded question patterns. No temporal or change-detection questions yet.
Legal Library
- No full-text search. Search operates on filename, description, property, and advisor — PDF content is not OCR-indexed.
- Admin-gated delete via HTTP basic auth. Audit log captures the username, but basic auth is transport-layer; HTTPS is recommended before exposing beyond the internal team.
Snowflake Proposal
- No live Snowflake connection. The tile hosts a planning document and document library. Code does not currently query Snowflake.
- Hardware and cost figures are estimates. Subject to vendor quotes and AWS marketplace pricing at procurement.
Platform
- Surface-level health check. Dashboard status is determined by HTTP HEAD probes with a 1.5-second timeout. "Live" confirms the server is responding, not that underlying data is correct or current.
- Polling lag. Status polls every 8 seconds. Brief delay between an actual outage and the status indicator changing.
Code & Security
Source-code architecture and an internal review of code-level vulnerabilities. This section covers the Python application source only — network, transport-layer, and authentication posture are tracked separately.
Platform Codebase — At a Glance
The REPI platform is composed of seven Python / Flask applications plus a shared data directory, spread across roughly 70 Python source files and ~24,500 lines of application code. Each dashboard is a standalone service with its own Gunicorn process; Nginx reverse proxies route traffic by URL prefix.
| Project | Role | Py Files | LOC |
|---|---|---|---|
| REPI_ONE | Hub · landing page · DCF API | 6 | 1,034 |
| REPI_Dashboard | Operations · Monte Carlo · property reports | 20 | 15,215 |
| REPI_eFront_Dashboard | Holdings registry · PRISM scenario analyzer | 14 | 4,479 |
| REPI_Data_Validation | Quiz · rules · outliers | 10 | 1,539 |
| REPI_Legal | 6-bucket document library · Ch. 215 reference | 8 | 929 |
| REPI_Snowflake | Migration proposal · supporting docs | 7 | 678 |
| REPI_Historical_Return | Long-horizon returns · composite attribution | 5 | 646 |
| REPI_Loan_Maturities | CSV-only source (no Python code) | 0 | — |
Stack & Conventions
| Layer | Technology / Convention |
|---|---|
| Language | Python 3.9+ (type hints on public functions; PEP 8) |
| Web framework | Flask 3.0+ (thin routes → logic lives in services/) |
| Data | Pandas 2.0+, NumPy 1.24+ (in-memory; no ORM) |
| Visualization | Plotly 5.18+ (server-rendered JSON → client hydration) |
| Persistence | SQLite (per dashboard; parameterized queries throughout) |
| Docs parsing | openpyxl 3.1+ for Excel; csv stdlib |
| Production server | Gunicorn 21.2+ (sync workers) |
| Proxy fix | werkzeug.middleware.proxy_fix.ProxyFix(x_prefix=1) on every app |
| Config | .env via python-dotenv; secrets never in source |
| Styling | Bespoke CSS (no Bootstrap); DM Sans + DM Mono; shared main.css |
| Charts | Plotly JSON emitted from services/charts.py; hydrated client-side |
Shared Patterns Across Dashboards
services/data_loader.py— loads source data once at startup into anAppDatacontainer that route handlers treat as immutable.services/storage.py— on dashboards with persistence (Legal, Snowflake, Data Validation): SQLite schema + versioned file storage + audit log.services/charts.py— Plotly figure builders returning JSON strings ready for the template.utils/formatters.py—fmt_money,fmt_pct,fmt_bps,fmt_date; kept in sync across all apps.templates/_jump_nav.html— floating dashboard-switcher widget; identical copy in each project.
Code-Level Security Review
Internal audit of the Python source across all seven projects, scanning for SQL injection, path traversal, unsafe deserialization, command injection, secret hardcoding, file-upload validation gaps, CSRF exposure, and template injection.
Summary: The codebase is well-architected for
security. All SQL uses bound parameters. All file operations use
sanitized filenames and send_from_directory with
database-verified IDs. No eval, exec,
pickle.load, or yaml.load on user input.
No hardcoded production secrets. No unsafe
subprocess.shell=True with user input.
Five open items were identified; one is Medium severity (CSRF token absence on state-mutating routes), the rest are Low or Informational.
Positive Findings (Mitigations Already in Place)
- SQL parameterization throughout. Every
sqlite3.execute()call uses?placeholders with bound parameters. No f-string or concatenation-based SQL anywhere in the codebase. - Safe filename handling. Uploads pass through
safe_filename()(regex-stripped toA-Za-z0-9._ -()), forced.pdfextension, andversion_filename()timestamp suffix to prevent overwrites. - PDF-only upload whitelist. MIME check on
application/pdf+ extension match at the server; non-PDF uploads rejected with a clear error. 50 MB size cap enforced pre-save. - Downloads via
send_from_directory. File downloads use a database-looked-up filename — the client only supplies an integerdoc_id, never a path. - Path traversal protection in legacy endpoints.
REPI_Snowflake’s/docs/<path>endpoint usesos.path.normpath()+.startswith(DOCS_DIR)check to block..traversal. - No unsafe deserialization. Zero instances of
pickle.load,yaml.load(withoutsafe_load),eval,exec, orcompileon user-controlled data. - No command injection. Application code never calls
os.systemorsubprocesswithshell=Trueon user input. - No template injection. Jinja2 autoescape is on by default.
|safefilter appears only on server-generated chart JSON, never on user data. - No secrets in source. API keys, passwords, and tokens are not hardcoded. Production secret keys are pulled from environment variables.
- Audit logging. Legal library logs every upload and delete with timestamp + actor (Nginx basic-auth username).
Open Findings (5)
| ID | Category | Severity | File | Finding |
|---|---|---|---|---|
| V-01 | CSRF | Medium | REPI_Data_Validation/flask_app.py |
POST routes lack CSRF tokens.
/quiz, /quiz/new, /quiz/end
mutate session state without token validation. Similar pattern
in upload endpoints across dashboards. Same-origin policy +
Nginx basic auth limit practical risk on the current private
deployment, but an embedded malicious image on any reachable
page could trigger unwanted actions from an authenticated
browser.
|
| V-02 | Secret Defaults | Low | REPI_Legal/flask_app.py · REPI_Data_Validation/flask_app.py |
Dev-only Flask secret-key defaults. Both apps
fall back to string literals
("dev-legal-secret-change-in-prod" etc.) if the
env var is unset. Safe when production is configured
correctly, but a missed deployment step would leave sessions
forgeable. Production must set
REPI_LEGAL_SECRET and
REPI_VALIDATION_SECRET to 32+ random bytes.
|
| V-03 | Authorization | Low | REPI_Snowflake/flask_app.py |
No admin gate on /delete/<doc_id>.
Any authenticated user can delete any document. Legal library
correctly gates delete to repi_admin; Snowflake
should follow the same pattern. Low severity because Snowflake
is an internal proposal tool, but creates an inconsistent
authorization model across the platform.
|
| V-04 | Debug Mode | Informational | REPI_Dashboard/flask_app.py |
Hardcoded debug=True in app.run().
Development-only path — production uses Gunicorn and
bypasses app.run() entirely. Still recommend
gating on FLASK_ENV or FLASK_DEBUG
env var for clarity.
|
| V-05 | Audit Completeness | Informational | REPI_Snowflake/services/storage.py | No audit log on Snowflake document deletes. Legal library logs every mutation with actor + timestamp; the Snowflake library does not. Deletes are silent. Aligning the two storage modules would improve traceability. |
Remediation Priority
- V-01 CSRF (Medium). Integrate
flask-wtf’sCSRFProtect. Add hidden tokens to state-mutating forms. ~1–2 days across all apps. - V-02 Secret keys (Low). Document required env vars in deployment runbook; generate via
python3 -c "import secrets; print(secrets.token_hex(32))". ~30 minutes. - V-03 Snowflake admin gate (Low). Port Legal’s
_is_admin()pattern to Snowflake. ~1 hour. - V-05 Snowflake audit log (Informational). Add an
audit_logtable +_audit()helper matching Legal. ~1 hour. - V-04 Debug mode guard (Informational). One-line conditional. ~5 minutes.
Dependency Risk
Every project pins major versions in requirements.txt:
flask>=3.0.0, pandas>=2.0.0,
numpy>=1.24, plotly>=5.18,
openpyxl>=3.1, gunicorn>=21.2.
As of the last audit cycle, no critical CVEs against the pinned
minimum versions. Dependency hashes are not enforced in CI —
recommend adding pip-compile --generate-hashes before
next major deployment.
Review Cadence
This review is a point-in-time snapshot. Recommended cadence:
- Quarterly — re-run the grep-based security scan for new instances of the 13 vulnerability classes above (SQL injection, path traversal, unsafe deserialization, command injection, CSRF, open redirect, template injection, secret hardcoding, file-upload gaps, input validation gaps, logging of sensitive data, race conditions, dependency freshness).
- At every major release — run
pip-auditagainst all sevenrequirements.txtfiles; file any CVE over Low severity. - On significant new code — new upload, new auth boundary, or new external integration triggers an ad-hoc review.