# Command Sovereignty
B2B revenue forecasting and leak detection SaaS for home improvement companies. Connect CRM, accounting, and advertising tools — surface where revenue is being lost before it disappears.
[Live](https://builtdomains.com)
## What It Does
Command Sovereignty ties together disconnected business tools (CRM, QuickBooks, Google Ads, Five9, Jobber, etc.) into a single revenue operations view. Core features:
| Feature | Description |
|---|---|
| **Revenue Forecasting** | Project revenue, pipeline, and costs with confidence scoring |
| **Revenue Leak Detection** | Automated detectors for overdue invoices, stale CRM leads, speed-to-lead, project margin variance, and more |
| **Connector Integration** | OAuth 2.0 connections to 16+ tools: QuickBooks, HubSpot, Five9, Google Ads/Sheets, Jobber, ServiceTitan, Slack, and more |
| **Cascading Goals** | Org → department → team → rep goal trees with KPI tracking |
| **Estimate Funnel** | Track estimates from creation through close, synced with Jobber |
| **Lead Generation** | Campaign management, attribution tracking, optimization engine |
| **Coaching & Scorecards** | Coach/rep pairings with focus areas and performance tracking |
| **Multi-company Portfolios** | Super admins can manage multiple business accounts |
## Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ Cloudflare Tunnel │
│ builtdomains.com ───────────────────────────────────────── │
└───────────────────────────┬─────────────────────────────────┘
│ HTTPS
▼
┌─────────────────────────────────────────────────────────────┐
│ Workstation (Debian Linux) │
│ │
│ ┌──────────────┐ port 5003 ┌──────────────────────┐ │
│ │ Gunicorn │ ◄───────────── │ Flask (app/) │ │
│ │ (systemd) │ │ │ │
│ └──────────────┘ │ - 17 blueprints │ │
│ │ - 16 connectors │ │
│ │ - APScheduler │ │
│ │ - Leak detectors │ │
│ └──────────┬───────────┘ │
│ │ │
│ ┌──────────────────┴───────────┐ │
│ │ SQLite (instance/auth.db) │ │
│ │ 21+ tables, WAL mode │ │
│ └──────────────────────────────┘ │
│ │
│ Backups: scripts/backup_db.sh (cron every 6h) │
│ Watchdog: systemd timer (5m) — auto-restores on DB wipe │
└─────────────────────────────────────────────────────────────┘
```
### Tech Stack
| Layer | Technology |
|---|---|
| **Frontend** | React 19, TypeScript, Vite 8, Tailwind CSS 4, React Router 7, Recharts, TanStack Query |
| **Backend** | Flask, Flask-Login, Flask-SQLAlchemy, Flask-Limiter, Flask-Mail |
| **Database** | SQLite (WAL mode), migrating to PostgreSQL via `scripts/migrate-to-postgres.sh` |
| **Auth** | Cookie-based sessions (Flask-Login), session revocation via UserSession table |
| **Scheduler** | APScheduler (leak detection, forecast updates, KPI aggregation) |
| **Payments** | Stripe (billing, checkout) |
| **Reverse Proxy** | Cloudflare Tunnel → localhost:5003 |
| **Process Manager** | Gunicorn, systemd user service |
## Project Structure
```
command-sovereignty/
├── app/
│ ├── __init__.py # App factory, extensions, security headers
│ ├── models.py # SQLAlchemy models (21+ tables)
│ ├── config.py # Configuration
│ ├── scheduler.py # APScheduler init (leak detectors, forecast updater)
│ ├── connectors/ # External integrations (16+ OAuth connectors)
│ ├── routes/ # Flask blueprints (17 route modules)
│ ├── services/
│ │ ├── forecast_service.py
│ │ ├── forecast_updater.py
│ │ ├── kpi_service.py
│ │ ├── leak_detectors/ # Revenue leak detection rules
│ │ ├── leak_alerts/ # Alert delivery (Slack, email)
│ │ ├── leak_remediation/
│ │ ├── leak_resolution/
│ │ ├── leak_severity/
│ │ └── lead_gen/ # Attribution, campaign gen, optimization
│ └── utils/ # CSRF, encryption, email, pagination, tenancy
├── frontend/ # React SPA (Vite + TypeScript)
├── migrations/ # SQL migration scripts (001–007)
├── scripts/
│ └── backup_db.sh # Automated DB backup (cron)
├── templates/ # Flask Jinja2 templates
├── instance/
│ └── auth.db # SQLite database (DO NOT run db.drop_all())
├── backups/ # Compressed DB backups (auth_YYYYMMDD_HHMMSS.db.gz)
├── wsgi.py # Gunicorn entry point
├── run.py # Dev server entry point
├── gunicorn.conf.py # Gunicorn configuration
├── .venv/ # Python virtual environment
└── AGENTS.md # Internal dev context (paths, pitfalls, rules)
```
## Quick Start
### Prerequisites
- Python 3.11+
- Node.js 18+
- Redis (for rate limiting — optional, graceful fallback)
### Backend Setup
```bash
cd /home/vincent/projects/command-sovereignty
# Create venv (skip if .venv/ exists)
python3 -m venv .venv
source .venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Set up environment
# Copy ~/.config/command-sovereignty/env as reference
# Required: SECRET_KEY, DATABASE_URL
# Optional: MAIL_*, OAUTH_*_CLIENT_ID/SECRET, RATELIMIT_*
# Run migrations
sqlite3 instance/auth.db < migrations/001_baseline.sql
sqlite3 instance/auth.db < migrations/002_profiles_role_allow_admin.sql
# ... apply remaining migrations in order (003–007)
# Start dev server
python run.py
```
### Frontend Setup
```bash
cd frontend
npm install
npm run dev # Dev server
npm run build # Production build → ../static/dist/
```
### Production
```bash
# Build frontend once
cd frontend && node node_modules/vite/bin/vite.js build && cd ..
# Start systemd service
systemctl --user start command-sovereignty.service
systemctl --user enable command-sovereignty.service
# Check status
systemctl --user status command-sovereignty.service
tail -50 logs/app.log
```
## Database
The database lives at `instance/auth.db` — **not** at the project root. A stale `auth.db` at root was deleted in July 2026.
### Critical Rules
1. **Never** run `db.drop_all()` — no migration system to recover from it
2. Before touching the DB: `sqlite3 instance/auth.db "SELECT count(*) FROM profiles;"`
3. If profiles count is 0, restore from `backups/`
4. Backups run every 6h via `scripts/backup_db.sh`
5. A watchdog timer (every 5m) detects DB wipes and auto-restores
### Migration to PostgreSQL
```bash
bash scripts/migrate-to-postgres.sh
```
Auto-installs `psql` if missing. Updates `DATABASE_URL` in `~/.config/command-sovereignty/env`.
## Security
- Session cookies: HttpOnly, SameSite=Lax, Secure (HTTPS only)
- Session fixation protection (re-issue ID each request)
- Session revocation (UserSession table — delete record → invalidate session)
- Brute force protection: failed login tracking, account lockout
- TOTP 2FA support per user
- CSRF protection on state-changing routes
- CSP headers (restricts inline scripts, frames, form actions)
- HSTS, X-Frame-Options, X-Content-Type-Options, X-XSS-Protection
- Request body cap: 16MB
- Rate limiting via Flask-Limiter (respects Cloudflare CF-Connecting-IP)
- Encrypted connector credentials at rest (Fernet symmetric encryption)
## Connectors
| Connector | Auth Method |
|---|---|
| QuickBooks | OAuth 2.0 |
| HubSpot | OAuth 2.0 |
| Google Ads | OAuth 2.0 |
| Google Sheets | OAuth 2.0 |
| Slack | OAuth 2.0 |
| Facebook Ads | OAuth 2.0 |
| Five9 | Per-connector API key |
| Jobber | OAuth 2.0 |
| ServiceTitan | OAuth 2.0 |
| JobNimbus | API key |
| LeadPerfection | Per-connector API key |
| Marlimar | Per-connector API key |
| Zapier | OAuth 2.0 |
| ANGI | OAuth 2.0 |
| SMS | Twilio API |
| Generic REST | Configurable |
## Monitoring
| Component | How |
|---|---|
| App health | `GET /health` — returns DB status, 503 on wipe detection |
| Logs | `logs/app.log` (10MB rotating, 5 backups) |
| DB backups | `scripts/backup_db.sh` every 6h, logged to `backups/backup.log` |
| DB watchdog | `command-sovereignty-db-watchdog.timer` (5m), auto-restores |
| System service | `systemctl --user status command-sovereignty.service` |
## Development Notes
- **Vite build quirk:** `npx vite build` triggers false background detection. Use `node node_modules/vite/bin/vite.js build` instead.
- **Login is cookie-based** (Flask-Login sessions), NOT JWT.
- **Auth model:** `User` table is `profiles` in SQLite.
- **paginate_query** returns `(list, dict)`, NOT a Pagination object — don't call `.items()` on the first element.
- **SESSION_COOKIE_SECURE=true** — cookies only work on HTTPS (via Cloudflare tunnel), not localhost HTTP.
## License
Private — see term sheet in `term_sheet.md` for co-founder agreement details.