Tech stack
The full inventory of what GreekManage runs on, where it lives in the codebase, and why we picked it.
Backend
| Concern | Choice | Why |
|---|---|---|
| Language | Python 3.13 | Type hints + native async, Django 6 minimum |
| Web framework | Django 6.0 | Mature ORM, auth, admin, signals, migrations |
| API framework | Django REST Framework 3.17 | Standard for Django REST APIs |
| ASGI server | Daphne | Required for Channels (WebSockets) |
| Production WSGI | Gunicorn | Battle-tested for sync HTTP |
| Realtime | Django Channels 4.3 | WebSocket support for chatbot streaming + live election results |
| DB driver | psycopg 3 (binary) | Modern Python ↔ Postgres |
| Auth | djangorestframework-simplejwt 5.5 | JWT access + refresh, blacklist, rotation |
| SAML | python3-saml 1.16 | SAML 2.0 SP for enterprise SSO |
| OAuth | requests-oauthlib 2.0 | OAuth 2.0 / OIDC (Microsoft, Google, Okta, LinkedIn) |
| Encryption | cryptography 46 (Fernet, MultiFernet) | PII at rest + key rotation |
| Background jobs | Celery 5.6 + Celery Beat | Async tasks + periodic scheduling |
| OpenAPI | drf-spectacular 0.29 | Auto-generated schema → this docs site |
| AI providers | anthropic, openai, google-genai | BYOM per org |
| Excel export | openpyxl 3.1 | XLSX for compliance + finance reports |
| CORS | django-cors-headers 4.9 | Frontend cross-origin |
| Static files | whitenoise 6.12 | Serve static assets without nginx |
| Storage abstraction | django-storages 1.14 | S3 / MinIO backend |
| Static env loading | django-environ 0.13 | .env file → settings |
Database
- PostgreSQL 17 with the
pgvectorextensionpgvectorpowers AI embeddings for semantic search and the chatbot's retrieval-augmented generation
- Row-Level Security (RLS) for tenant isolation on org-scoped tables → Multi-tenancy & RLS
- Indexes: standard B-tree on FKs; trigram (
pg_trgm) on member-search columns
Cache + queue
- Redis 7
- DB 0: Celery broker + result backend
- DB 1: E2E test isolation
- DB 2: staging
- Channels layer: WebSocket pub/sub
Object storage
- MinIO locally (S3-compatible)
- AWS S3 or any S3-compatible (Cloudflare R2, Backblaze B2, Wasabi) in production
- Per-org override via
StorageConfigmodel — orgs can BYO bucket
Frontend
| Concern | Choice | Why |
|---|---|---|
| UI library | React 19 | Server components on the way; concurrent rendering |
| Language | TypeScript 5.9 | Strict mode, no any |
| Build tool | Vite 7 | Fast HMR, ESM-native |
| Styling | Tailwind CSS 4.2 | Utility-first; design tokens via CSS vars |
| Component library | shadcn/ui (Radix UI primitives) | Owned components, accessible, themeable |
| Routing | React Router 7 | Standard client-side routing |
| Server state | TanStack Query 5 | Data fetching, caching, optimistic updates |
| Client state | Zustand 5 | Lightweight stores (auth, user-context, forum-membership) |
| Forms | React Hook Form + Zod | Schema-validated forms |
| Charts | Recharts | Lightweight, composable |
| Animation | Framer Motion | Page transitions, sidebar slide, KPI counts |
| Icons | Lucide React | Consistent 24px grid |
| Date | date-fns | Tree-shakeable, immutable |
| Markdown | react-markdown | Render user content (forum posts, bulletins) |
Mobile
- Capacitor 8 wraps the React frontend as a native iOS / Android app
@aparajita/capacitor-biometric-auth— Face ID / Touch ID / fingerprint unlock- Fastlane — iOS TestFlight + Play Store distribution
- Build config in
frontend/ios/andfrontend/android/
Infrastructure
| Layer | Tooling |
|---|---|
| Local dev | Docker Compose v2 |
| Production orchestration | Kubernetes (manifests in k8s/) |
| TLS | cert-manager + Let's Encrypt |
| Ingress | nginx-ingress |
| Secret management | Kubernetes Secrets (raw); roadmap: External Secrets / Sealed Secrets |
| Image registry | (Customer choice — typically GHCR or AWS ECR) |
CI / CD
| Workflow | Trigger | Purpose |
|---|---|---|
ci.yml | Push, PR | Backend tests + frontend build + Android Gradle build + SAST + audit |
e2e.yml | Push, PR | Playwright smoke + ZAP API + ZAP baseline |
security-nightly.yml | 6am ET daily | Full ZAP active scan, opens GitHub issue on failure |
claude-code-review.yml | PR opened/ready | Claude Code review (advisory) |
docs-site.yml | Path-filtered to docs-site/** | Builds Docusaurus, deploys to GitHub Pages |
Quality + security tools
| Tool | What it checks | Where |
|---|---|---|
| SonarQube | Quality gate (coverage, code smells, duplicates) | sonar-project.properties |
| Bandit | Python SAST | .bandit.yml |
| Semgrep | Multi-language SAST | .semgrep.yml |
| pip-audit | Python dependency CVEs | CI |
| npm audit | JS dependency CVEs | CI |
| Trivy | Filesystem + image CVEs | .trivyignore |
| gitleaks | Secret leaks in commits | .gitleaks.toml |
| OWASP ZAP | Dynamic app scanning (DAST) | E2E + nightly |
Observability
Currently minimal. Logs ship via container stdout; no APM, metrics, or aggregation in place. Adding Sentry + Prometheus is on the roadmap.
Testing
| Layer | Tool |
|---|---|
| Backend unit + integration | pytest + pytest-django |
| Backend coverage | coverage.py (target ≥80%) |
| Frontend unit | Vitest + React Testing Library |
| E2E | Playwright (browser auth fixture, ~132 tests) |
| API security | OWASP ZAP (credentialed scan) |
| Mobile UI | Manual on TestFlight / Play Store internal track |
What's intentionally not used
- GraphQL — REST + auto-generated OpenAPI is sufficient and easier to cache
- WebPack — Vite is faster
- Redux — Zustand + React Query covers everything
- Material-UI — shadcn/ui gives ownership of components
- Heroku / Render — K8s for production-grade scaling and observability
- MongoDB / NoSQL — Postgres + JSONB covers semi-structured cases
Versioning
Backend: SemVer per backend/VERSION and CHANGELOG.md.
Frontend: SemVer per frontend/package.json.
API contract: tracked via drf-spectacular schema; breaking changes bump major version.