Technology Stack
Full stack detail — backend, frontend, infrastructure, and key architectural decisions.
Backend
| Layer | Technology | Rationale |
| Language | Python 3.12 | Founder has prior expertise; strong data/ML ecosystem for CQI Phase 3 |
| Framework | Django 5 DRF | Rapid development, strong ORM, excellent admin panel for early-stage data management |
| Identity / SSO | django-oauth-toolkit | OAuth2 + OIDC server; custom claims for cannabis license identity |
| Task queue | Celery Redis | CQI async scoring, COA PDF parsing, menu sync, notifications |
| Database | PostgreSQL 16 PostGIS | PostGIS for ICC Maps geospatial queries; strong relational model for orders/compliance |
| COA parsing | pdfplumber | Lab test PDF extraction; supplemented by LLM prompt for unstructured layouts |
| API docs | drf-spectacular | Auto-generated OpenAPI/Swagger UI at /api/docs/ |
| Linting / format | ruff black | Enforced on save in Cursor dev container |
| Testing | pytest factory_boy | Test factories for all models; fixtures for auth, business, lot, order scenarios |
Django App Structure
| App | Key models | Responsibilities |
identity | User, BusinessProfile, BusinessLicense, BusinessMember | SSO token authority, KYB, role-based access |
ccx | Lot, CertificateOfAnalysis, Offer, Order | Trading lifecycle, offline payment state machine, COA upload |
cqi | ScoringConfig, CQIScore | Scoring engine, Celery tasks, config management |
core | — | Shared admin registrations, utilities, base permissions |
Frontend
| Layer | Technology | Notes |
| Framework | React 18 TypeScript Vite | Two web apps: CCX (port 3000) and BMC (port 3001) |
| Styling | Tailwind CSS | Custom design system with ICC brand palette; dark mode implemented |
| Server state | TanStack Query | API data fetching, caching, optimistic updates |
| Client state | Zustand | Auth store with hasProduct() and isRole() helpers; persisted to localStorage |
| Forms | React Hook Form Zod | Schema validation on all inputs; resolver pattern |
| HTTP client | Axios | Interceptors for Bearer token injection and transparent refresh-on-401 with queue |
| Routing | React Router v6 | Protected routes with RequireAuth guard component |
| Mobile (Phase 4) | React Native | ~70–80% code reuse from web; three apps: CCX, Maps, Social |
Infrastructure
Celery task queues
| Queue | Tasks |
cqi | score_lot, rescore_all_active_lots |
default | COA PDF parsing, notifications, order status updates |
notifications | Email OTP, license expiry alerts, payment confirmation |
AWS S3 storage
| Path | Contents |
coas/ | Certificate of Analysis PDFs |
lot_photos/ | Product images |
licenses/ | Business license documents (KYB) |
proof_of_funds/ | Buyer proof-of-funds uploads |
staticfiles/ | Production static assets (prod only) |
Key Architectural Decisions
Why Django over FastAPI / Node?
Founder has Django expertise. FastAPI would offer better raw throughput for high-concurrency trading, but the productivity cost of switching stacks outweighs the benefit at this stage. The CCX trading engine can be extracted to a FastAPI microservice in Phase 2 if volume justifies it.
Why custom SSO over Auth0/Okta?
Cannabis license number and business type must be first-class identity attributes — not custom claims. Cross-product SSO requires a token authority we control. Long-term, the SSO becomes a distribution moat as new ICC products are added.
Why offline payment settlement?
Cannabis banking is restricted under federal law. Phase 1 uses dual in-app confirmation of offline payments (wire, check). Data model pre-stubs escrow fields for future in-app payments when regulations allow. Eliminates the banking partner dependency at launch.
Why rule-based CQI in Phase 1?
ML models require training data that doesn't exist yet. The weighted rule-based model produces scores immediately and generates the labeled dataset (lots + clearing prices) needed to train an XGBoost/LightGBM model in Phase 3.