Follow the flow. Every euro has a story.
Public Money Mirror identifies savings and anomalies in public spending, powers success-fee recoveries for governments, and offers enterprise/API licenses and freemium consumer subscriptions.
- Docker & Docker Compose
- Python 3.11+ (for local development)
- Node.js 20+ (for web frontend)
- Clone and setup:
git clone <repo-url>
cd public-money-mirror
cp .env.sample .env
# Edit .env with your configuration- Start services:
make devThis starts:
- Backend API at
http://localhost:8000 - Streamlit dashboard at
http://localhost:8501 - Next.js public web at
http://localhost:3000 - Postgres database at
localhost:5432 - Redis at
localhost:6379 - Nginx proxy at
http://localhost:80
- Seed data:
make seed- Generate demo recovery kit:
make demo_kitGET /health- Health checkPOST /auth/login- Login (returns JWT)POST /auth/signup- SignupGET /auth/me- Current user info
GET /cases?limit=&min_score=&min_euro=- List ranked casesGET /cases/{id}- Get case detailsGET /cases/{id}/evidence- Get case evidenceGET /stories/latest?limit=3- Get latest story cardsGET /benchmarks/unit_price?cpv=®ion=&year=- Get benchmarksGET /entities/search?q=- Search entities
POST /recovery_kits/{case_id}- Generate recovery kitPOST /invoices/success_fee- Create success fee invoice
GET /alerts- User's alertsPOST /alerts- Create alertGET /premium/status- Premium subscription status
GET /api/v1/risk_score?supplier_id=- Supplier risk scoreGET /api/v1/cases/export.csv- Export cases CSV
# Login
curl -X POST http://localhost:8000/auth/login \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=admin@pmm.local&password=admin123"
# Get cases
curl http://localhost:8000/cases?limit=10&min_score=50
# Get latest stories
curl http://localhost:8000/stories/latest?limit=3
# Generate recovery kit (requires auth)
curl -X POST http://localhost:8000/recovery_kits/1 \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"include_benchmarks": true, "include_alternatives": true}'make dev- Start development environmentmake test- Run testsmake fmt- Format code (black, ruff)make lint- Lint code (ruff, mypy)make seed- Seed synthetic datamake demo_kit- Generate demo recovery kitmake clean- Clean up containersmake build- Build Docker images
See .env.sample for all configuration options. Key variables:
DATABASE_URL- Postgres connection stringREDIS_URL- Redis connection stringSECRET_KEY- JWT secret keySTRIPE_SECRET_KEY- Stripe secret keySTRIPE_WEBHOOK_SECRET- Stripe webhook secret
admin- Full accessanalyst- Can create cases, recovery kits, invoicesgov_client- Read-only + request recovery kitsconsumer- Public site access + premium alerts
src/
├── backend/ # FastAPI backend
│ └── app/
│ ├── models/ # SQLModel ORM models
│ ├── schemas/ # Pydantic schemas
│ ├── routers/ # API routes
│ └── services/ # Business logic
├── etl/ # Prefect ETL flows
│ └── flows/ # Data ingestion flows
├── analytics/ # Analytics & ML
│ ├── features/ # Feature engineering
│ └── models/ # Case ranking models
├── frontend_streamlit/ # Operator dashboard
└── web_public/ # Next.js public site
scripts/ # Utility scripts
docs/ # Documentation
deploy/ # Docker & deployment configs
- Price outliers: Robust z-score using MAD (Median Absolute Deviation)
- Single-bidder risk: Detects low-competition tenders
- Bid rotation: Collusion heuristic using n-gram entropy
- Time overruns: Planned vs. actual delivery dates
- Supplier dependency: HHI (Herfindahl-Hirschman Index) concentration
- Weighted ensemble scoring (0-100)
- EUR potential estimation with conservative shrinkage
- Explainability blob with top features and rationale
- Risk tags for quick categorization
- Benchmark analysis with percentile breakdowns
- Alternative supplier suggestions
- Draft renegotiation letters
- PDF export for government clients
- Success Fees (B2G): 15-25% of realised savings
- Enterprise/API Licenses (B2B): Access to risk scores, CSV exports
- Premium Subscriptions (B2C): €4.99/month for alerts & dossiers
- OpenSpending (Germany): Budget aggregates
- Bundeshaushalt: Federal budget CSVs
- EU TED: Public procurement notices (Tenders Electronic Daily)
make testRuns pytest with coverage for:
- Unit tests (anomaly detection, entity resolution)
- API tests (FastAPI TestClient)
- Integration tests
GitHub Actions runs on PR:
- Tests
- Linting (ruff, black)
- Type checking (mypy)
On main branch:
- Builds and pushes Docker images
docs/README.md- General documentationdocs/PILOT_MOU_TEMPLATE.md- Success-fee pilot MoU templatedocs/SUCCESS_FEE_TERMS.md- Success fee terms & definitionsdocs/PRIVACY_SECURITY.md- Privacy & security controlsdocs/ARCHITECTURE.md- System architecture diagrams
Proprietary - All rights reserved
For questions or issues, contact: support@publicmoneymirror.com
Dieses Projekt ist Teil eines Open-Source-Ökosystems für digitale Demokratie:
| Projekt | Frage | Link |
|---|---|---|
| FairEint | Was sollte Deutschland anders machen? | GitHub · Live |
| GitLaw | Was steht im Gesetz? | GitHub · Live |
| Public Money Mirror | Wohin fließt das Steuergeld? | GitHub |
| SafeVoice | Wer wird online angegriffen? | GitHub |
Alle Projekte: github.com/mikelninh · Unterstützen: Ko-fi · GitHub Sponsors
Dieses Projekt ist Teil eines Open-Source-Ökosystems für digitale Demokratie:
| Projekt | Frage | Link |
|---|---|---|
| FairEint | Was sollte Deutschland anders machen? | GitHub · Live |
| GitLaw | Was steht im Gesetz? | GitHub · Live |
| Public Money Mirror | Wohin fließt das Steuergeld? | GitHub |
| SafeVoice | Wer wird online angegriffen? | GitHub |
Alle Projekte: github.com/mikelninh · Unterstützen: Ko-fi · GitHub Sponsors