# Control Plane (MT-0) Tenant registry, plans, domains, provisioning state, and superadmin accounts for multi-tenant JQC. Self-contained and decoupled from `app/` — the existing single-tenant application is unaffected by this package. See `../MULTI_TENANT_PLAN.md` for the full architecture and roadmap. ## Layout ``` control/ ├── __init__.py # package docs ├── base.py # ControlBase + engine/session (from CONTROL_DATABASE_URL) ├── crypto.py # Fernet encrypt/decrypt for tenant DB passwords ├── time_utils.py # now_eastern() mirror (no app import) ├── models.py # Plan, PlanFeature, Tenant, TenantDomain, │ # Superadmin, ProvisioningJob, TenantAudit ├── seed.py # idempotent baseline-plan seeder ├── cli.py # seed / create-superadmin / list-plans └── migrations/ # standalone Alembic chain (control{N}_…) └── versions/control0001_init.py ← HEAD ``` ## Environment variables (control plane only) | Variable | Purpose | |---|---| | `CONTROL_DATABASE_URL` | e.g. `mysql+pymysql://jqc_control:pw@localhost/jqc_control` | | `CONTROL_FERNET_KEY` | Fernet key for encrypting tenant DB passwords | Generate a Fernet key: ```bash python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" ``` ## Deploy — MT-0 bootstrap (run once) Migrations are independent of the tenant chain and the data plane. The existing app does **not** need to be touched or restarted for MT-0. ```bash # 1. Create the control database + its MySQL user (run as a MySQL admin) mysql -e "CREATE DATABASE jqc_control CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" mysql -e "CREATE USER 'jqc_control'@'localhost' IDENTIFIED BY '';" mysql -e "GRANT ALL PRIVILEGES ON jqc_control.* TO 'jqc_control'@'localhost'; FLUSH PRIVILEGES;" # 2. Export the env vars (add to .env or the systemd unit for the control panel later) export CONTROL_DATABASE_URL='mysql+pymysql://jqc_control:@localhost/jqc_control' export CONTROL_FERNET_KEY='' # 3. Apply the control schema alembic -c control/migrations/alembic.ini upgrade head # 4. Seed baseline plans (Free / Starter / Pro / Enterprise) python -m control.cli seed # 5. Create the first superadmin python -m control.cli create-superadmin --username admin --email you@example.com ``` Verify: ```bash alembic -c control/migrations/alembic.ini current # → control0001_init (head) python -m control.cli list-plans ``` ## Rollback ```bash alembic -c control/migrations/alembic.ini downgrade base # drops all control tables ``` ## Notes - The migration uses INFORMATION_SCHEMA existence checks (Rule 14) — safe to re-run. - Plan seeding is idempotent (upsert by `code`) — re-running updates in place. - Tenant DB passwords are stored Fernet-encrypted in `tenants.db_password_enc`; `Tenant.db_uri` decrypts on demand. Provisioning that *creates* per-tenant MySQL users/grants lands in MT-3. - Control-panel write auditing (`tenant_audit`) is wired in MT-4; the bootstrap CLI logs to stdout only.