Ensemble Callboard is a self-hosted, open-source management platform for performing arts organizations. Built for boards, staff, and performers who need one place for rehearsal schedules, cast lists, performer rosters, committee membership, and role-targeted announcements — without relying on spreadsheets, email threads, or a paid SaaS product.
| Feature | Who Benefits |
|---|---|
| Rehearsal & performance schedule | Parents, Performers, Staff |
| Role-targeted announcements | Everyone |
| Performer roster & profiles | Board, Staff |
| Cast lists by production | Everyone |
| Committee and board membership | Board, Staff |
| Admin account management | Board |
| Layer | Technology |
|---|---|
| API | Python 3.12, FastAPI, Strawberry GraphQL |
| Database | PostgreSQL 16 (pgvector, uuid-ossp, pgcrypto) |
| ORM / Migrations | SQLAlchemy 2.0 (async), Alembic |
| Auth | OAuth 2.0 / OIDC (Google default; GitHub, Microsoft supported) |
| Frontend | React 18, Vite, TypeScript, TanStack Query, Tailwind CSS |
| Proxy | nginx (single entry point for API and frontend) |
| Containers | Docker, Docker Compose |
| CI/CD | GitHub Actions |
ensemble/
├── .github/workflows/ # CI/CD pipeline
├── db/
│ └── seeds/ # DB extension scripts (run via make seed)
├── docs/
│ ├── architecture.md
│ ├── architecture-decisions.md
│ ├── data-model.md
│ ├── phases.md
│ └── specs/ # Feature one-pagers (one per phase step)
├── hooks/ # Git hooks (install with make install-hooks)
├── services/
│ ├── api/ # FastAPI + Strawberry GraphQL
│ ├── e2e/ # Playwright end-to-end tests
│ ├── proxy/ # nginx reverse proxy config
│ └── web/ # React SPA
├── docker-compose.yml
└── Makefile # Dev shortcuts
- Docker Desktop (with WSL2 backend on Windows)
- Git
- OAuth 2.0 credentials for at least one provider (setup guide below)
git clone https://github.com/pferate/ensemble-callboard.git
cd ensemble-callboard
cp .env.example .env
# Edit .env with your OAuth credentials
make install-hooksmake devThis starts PostgreSQL, the API (hot reload), the React frontend (Vite HMR), and the nginx proxy.
| Service | URL |
|---|---|
| Frontend | http://localhost |
| API docs | http://localhost/docs |
| GraphQL playground | http://localhost/graphql |
make seed # Install PostgreSQL extensions (run once, before migrations)
make migrate # Apply Alembic migrations
make bootstrap ADMIN_EMAIL=you@org.com # Create admin account and default groupsmake bootstrap is idempotent — safe to run again if the admin account already exists.
make dev # Start full local dev stack
make stop # Stop all containers
make restart # Rebuild and restart all containers (preserves data)
make reset # Wipe DB and restart fresh
make migrate # Apply pending Alembic migrations
make bootstrap ADMIN_EMAIL=you@org.com # Create or update the admin account
make seed # Load DB extensions
make test # Run all tests (API + web + E2E)
make api-test # Run API tests only
make web-test # Run web tests only
make e2e-test # Run E2E tests only
make lint # Lint all services (API + web)
make api-lint # Lint API only (ruff)
make web-lint # Lint web only (eslint)
make verify-schema # Check schema.graphql matches live API
make schema # Export GraphQL schema from API to web
make psql # Open PostgreSQL shell
make logs # Tail all container logs
make build # Build all Docker images
make ci # Run all CI checks locally
make install-hooks # Configure git hooks (run once after cloning)At least one provider must be configured. Google is the default.
Using a non-Gmail address? Members don't need a Gmail, Outlook, or GitHub account — they can sign in with Google or Microsoft using any existing email address. Visit accounts.google.com and choose "Use my current email address instead" to create a Google account with an ISP-provided or custom domain address. Microsoft offers the same option at account.microsoft.com.
- Go to Google Cloud Console
- Create a project and enable the Google Identity API
- Create OAuth 2.0 credentials (Web application type)
- Authorized redirect URI:
http://localhost/auth/google/callback
- Authorized redirect URI:
- Copy the Client ID and Secret into your
.env:GOOGLE_CLIENT_ID=... GOOGLE_CLIENT_SECRET=...
- Go to Azure Portal → App registrations → New registration
- Redirect URI:
http://localhost/auth/microsoft/callback
- Redirect URI:
- Copy the Client ID, Secret, and Tenant ID into your
.env:MICROSOFT_CLIENT_ID=... MICROSOFT_CLIENT_SECRET=... MICROSOFT_TENANT_ID=common # or a specific Azure AD tenant ID
- Go to GitHub → Settings → Developer settings → OAuth Apps → New OAuth App
- Authorization callback URL:
http://localhost/auth/github/callback
- Authorization callback URL:
- Copy the Client ID and Secret into your
.env:GITHUB_CLIENT_ID=... GITHUB_CLIENT_SECRET=...
To approve accounts automatically when their email domain matches your organization:
AUTO_APPROVE_EMAIL_DOMAIN=yourdomain.org
Accounts on the auto-approve domain are set to ACTIVE on first login. All others land as PENDING until an account with manage_accounts permission approves them.
Access is controlled by authorization groups, not hardcoded roles. Groups are defined by the organization (e.g. "Board", "Staff", "Volunteers") and assigned permissions from a fixed enum. Accounts belong to one or more groups; their effective permissions are the union of all group permissions.
Permissions: view_schedule, edit_schedule, view_roster, edit_roster, view_announcements, edit_announcements, view_committees, edit_committees, manage_accounts, manage_groups, admin
An admin account can log in and assign groups to others via the admin panel. New accounts are inactive until approved.
See docs/architecture-decisions.md (ADR-021, ADR-022) for the full RBAC design.
| Document | Description |
|---|---|
| docs/architecture.md | System architecture diagram and request flows |
| docs/architecture-decisions.md | Architecture Decision Records (ADRs) |
| docs/data-model.md | Entity relationship diagram and schema notes |
| docs/phases.md | Development phases with step-by-step build plan |
- Phase 0 — Architecture and planning ✓
- Phase 1 — Foundation: auth, RBAC, account management, admin panel ✓
- Phase 2 — Roster: performers, groups, households, season scoping ✓
- Phase 3 — Committees: board and production committee membership ✓
- Phase 4 — Schedule: productions, pieces, cast lists, rehearsal events ✓
- Phase 5 — Communications: email notifications, announcements ← current
- Phase 6 — Semantic search via pgvector (future feature)
See docs/phases.md for the full step-by-step breakdown.
Ensemble is an open project. See CONTRIBUTING.md for setup instructions, coding conventions, and how to submit changes.
If you run a performing arts organization and want to adapt this, open an issue — collaboration is welcome.
GNU Affero General Public License v3.0 (AGPL-3.0)
Free to self-host and modify. If you run a modified version as a network service, you must publish your source under the same license. See LICENSE for the full terms.