{"id":33409247,"url":"https://github.com/bvandewe/cml-cloud-manager","last_synced_at":"2026-04-14T04:31:10.729Z","repository":{"id":324468558,"uuid":"1097286949","full_name":"bvandewe/cml-cloud-manager","owner":"bvandewe","description":"A Python web portal for lifecycle automation and secure access management of Cisco Modeling Labs (CML) on AWS EC2.","archived":false,"fork":false,"pushed_at":"2026-03-05T12:07:21.000Z","size":8930,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-05T16:11:33.035Z","etag":null,"topics":["aws-ec2","bootstrap5","cisco-modeling-labs","cloud-management","cml","cqrs","ddd","docker","event-sourcing","fastapi","keycloak","lab-management","microservices","mongodb","otel","redis","sse","vanilla-js"],"latest_commit_sha":null,"homepage":"https://bvandewe.github.io/cml-cloud-manager","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bvandewe.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"docs/security/authentication-flows.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-15T22:07:17.000Z","updated_at":"2026-03-05T12:07:27.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/bvandewe/cml-cloud-manager","commit_stats":null,"previous_names":["bvandewe/cml-cloud-manager"],"tags_count":11,"template":false,"template_full_name":"bvandewe/starter-app","purl":"pkg:github/bvandewe/cml-cloud-manager","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bvandewe%2Fcml-cloud-manager","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bvandewe%2Fcml-cloud-manager/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bvandewe%2Fcml-cloud-manager/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bvandewe%2Fcml-cloud-manager/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bvandewe","download_url":"https://codeload.github.com/bvandewe/cml-cloud-manager/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bvandewe%2Fcml-cloud-manager/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31782736,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-14T02:24:21.117Z","status":"ssl_error","status_checked_at":"2026-04-14T02:24:20.627Z","response_time":153,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws-ec2","bootstrap5","cisco-modeling-labs","cloud-management","cml","cqrs","ddd","docker","event-sourcing","fastapi","keycloak","lab-management","microservices","mongodb","otel","redis","sse","vanilla-js"],"created_at":"2025-11-23T18:11:33.713Z","updated_at":"2026-04-14T04:31:10.723Z","avatar_url":"https://github.com/bvandewe.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Lablet Cloud Manager\n\n[![Python Version](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)\n[![Poetry](https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json)](https://python-poetry.org/)\n[![FastAPI](https://img.shields.io/badge/FastAPI-0.100+-009688.svg?logo=fastapi)](https://fastapi.tiangolo.com)\n[![Neuroglia](https://img.shields.io/badge/Neuroglia-0.6.6-purple.svg)](https://github.com/neuroglia-io/python-framework)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n[![Documentation](https://img.shields.io/badge/docs-mkdocs-blue.svg)](https://bvandewe.github.io/lablet-cloud-manager/)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)\n[![Tests](https://img.shields.io/badge/tests-pytest-orange.svg)](https://docs.pytest.org/)\n[![Coverage](https://img.shields.io/badge/coverage-98%25-brightgreen.svg)](htmlcov/index.html)\n[![Docker](https://img.shields.io/badge/docker-ready-blue.svg?logo=docker)](docker-compose.yml)\n[![Keycloak](https://img.shields.io/badge/auth-Keycloak-orange.svg?logo=keycloak)](https://www.keycloak.org/)\n[![MongoDB](https://img.shields.io/badge/database-MongoDB-green.svg?logo=mongodb)](https://www.mongodb.com/)\n[![Redis](https://img.shields.io/badge/sessions-Redis-red.svg?logo=redis)](https://redis.io/)\n\nAn opinionated Neuroglia FastAPI template showcasing multi-subapp architecture (API + UI), CQRS, RBAC, OAuth2/OIDC, and pluggable infrastructure:\n\n- 🎨 **SubApp Pattern**: Clean separation between API and UI concerns\n- 🔐 **OAuth2/OIDC Authentication**: Keycloak integration with Backend-for-Frontend pattern\n- 🔴 **Redis Session Store**: Distributed sessions for horizontal scaling in Kubernetes\n- 🛡️ **RBAC**: Role-based access control at the application layer\n- 📋 **CQRS Pattern**: Command Query Responsibility Segregation\n- 🎯 **Clean Architecture**: Domain-driven design with clear boundaries\n- ⏰ **Background Task Scheduling**: APScheduler integration with Redis/MongoDB persistence\n- 📊 **Worker Monitoring**: Automated health and metrics collection for CML Workers\n- 🔄 **Real-Time Updates (SSE)**: Live worker status, metrics \u0026 labs pushed to UI\n\n![Cml Cloud Manager demo](./docs/assets/lablet-cloud-manager_v0.1.0.gif)\n\n## 🏗️ Architecture\n\nThe **Lablet Cloud Manager (LCM)** is a distributed system of specialized microservices designed to manage **Cisco Modeling Lab (CML)** infrastructure on AWS. It uses the **Kubernetes Controller Pattern** for declarative resource management built on the **Neuroglia** framework.\n\n### Top Entities\n\n- **LabletDefinition**: Immutable, versioned template for a lab environment (requirements, topologies).\n- **LabletSession**: A runtime session of a definition on a Worker (combines CML lab, LDS LabSession, and child state like `UserSession`, `GradingSession`, `ScoreReport`).\n- **Worker**: AWS EC2 instance running a CML instance.\n\n### Microservices \u0026 Roles\n\nThe system uses a **Control Plane + Controllers** pattern. All mutations to state are handled via the Control Plane API.\n\n1. **Control Plane API (\"The Gateway\")**:\n   - Handles user interaction via REST API and UI.\n   - The **ONLY** service with direct access to the MongoDB state store, utilizing CQRS.\n   - Projects aggregate state changes to `etcd` to trigger controllers.\n2. **Resource Scheduler (\"The Scheduler\")**:\n   - Makes placement decisions and manages the scheduling queue.\n   - Evaluates license affinity, resource limits, and triggers AWS scale-ups.\n3. **Worker Controller (\"The Infrastructure\")**:\n   - Manages CML Worker lifecycle (EC2 instance start/stop/terminate).\n   - Monitors infrastructure metrics via AWS CloudWatch and CML System APIs.\n4. **Lablet Controller (\"The Workload\")**:\n   - Manages the lab lifecycle within CML (import/start/stop).\n   - Integrates with external systems (LDS and Grading Engine) using abstract SPIs and handles inbound CloudEvents.\n\n### Top-Level Data Flow (Event-Driven State-Based Persistence)\n\n- **User Actions**: Users communicate with the Control Plane API, which processes requests and persists state locally in MongoDB.\n- **State Projection**: The Control Plane API publishes domain events as projected state keys to `etcd`.\n- **Reactive Reconciliation**: Controllers watch `etcd` keys. When state changes (e.g., a session goes `PENDING`), the relevant controller wakes up, queries the Control Plane for details, and acts (e.g., Resource Scheduler places the session and updates state to `SCHEDULED`).\n- **External Integration**: Systems like LDS or GradingEngine emit CloudEvents which are received by the Lablet Controller. The controller interprets these events to drive the session lifecycle (e.g., from `RUNNING` to `COLLECTING`).\n\n### Project Structure\n\n```\ncml-cloud-manager/\n├── src/\n│   ├── control-plane-api/             # REST API, SPA UI, State Management (MongoDB writer)\n│   ├── resource-scheduler/            # Timeslot placement and worker capacity solver\n│   ├── worker-controller/             # Infrastructure lifecycle (AWS EC2 + CML System)\n│   ├── lablet-controller/             # Lab lifecycle, LDS \u0026 GradingEngine integration\n│   └── core/                          # Shared domain logic, SPI interfaces, and base classes\n├── docs/                              # Architecture and specification documents (MkDocs)\n├── scripts/                           # Developer utility scripts\n├── deployment/                        # Infrastructure and Keycloak assets\n├── docker-compose.yml                 # Local dev orchestration\n├── Makefile                           # Developer automation commands\n└── README.md\n```\n\n### Container Maintenance and Building\n\nThe LCM is composed of 4 discrete microservices that share a common domain model but run in separate containers.\n\n- **Package Management \u0026 Dependencies:** Each microservice has its own `pyproject.toml` and acts as an independent application managed by Poetry.\n- **Shared Core:** The `src/core/` package contains the shared domain entities, events, and abstract SPI dependencies. It is included in each microservice as a local path dependency (e.g., `lcm-core = {path = \"../core\", develop = true}`).\n- **Containerization:** Each microservice houses its own `Dockerfile` inside its directory (e.g., `src/control-plane-api/Dockerfile`). Because they all depend on `src/core/`, the Docker build context must be set to the project root so the core files can be successfully copied during the build.\n- **Automation:** The root `Makefile` orchestrates building and maintaining these containers locally. Commands like `make build` or `make rebuild-services` build the image for every microservice from the project root.\n\n## 🚀 Quick Start\n\n### Prerequisites\n\n- Python 3.11+\n- Poetry\n- Node.js 20+ (for UI build)\n- Docker \u0026 Docker Compose\n\n### Local Development\n\n1. **Install Dependencies**:\n\n   ```bash\n   make install\n   make install-ui\n   ```\n\n2. **Build UI**:\n\n   ```bash\n   make build-ui\n   ```\n\n3. **Run Application**:\n\n   ```bash\n   make run\n   ```\n\n   Access at `http://localhost:8000`\n\n### Production Deployment\n\n1. **Configure Environment**:\n   Copy the example environment file and update secrets:\n\n   ```bash\n   cp deployment/docker-compose/.env.prod.example deployment/docker-compose/.env.prod\n   nano deployment/docker-compose/.env.prod\n   ```\n\n2. **Start Stack**:\n\n   ```bash\n   make prod-up\n   ```\n\n3. **Access Services**:\n   - **UI**: `http://localhost/`\n   - **Keycloak**: `http://localhost/auth/`\n   - **Grafana**: `http://localhost/grafana/`\n\nSee [Deployment Documentation](deployment/README.md) for details.\n\n- Docker \u0026 Docker Compose (optional)\n\n### Quick Setup (Recommended)\n\nUse the Makefile for easy setup and management:\n\n```bash\nmake setup    # Install backend \u0026 frontend dependencies\nmake run      # Start FastAPI locally\nmake up       # Start full Docker stack (Mongo, Keycloak, Redis, OTEL)\nmake help     # List all available Makefile targets\n```\n\n### Manual Local Development\n\n1. **Install Python dependencies:**\n\n   ```bash\n   poetry install\n   ```\n\n2. **Install frontend dependencies and build UI:**\n\n   ```bash\n   make install-ui\n   make build-ui\n   ```\n\n3. **Run the application:**\n\n   ```bash\n   make run\n   ```\n\n4. **Access the application:**\n   - Application: http://localhost:8000/\n   - API Documentation: http://localhost:8000/api/docs\n\n### Frontend Development Mode\n\nFor hot-reload during UI development:\n\n```bash\n# Terminal 1: Watch and rebuild frontend assets\nmake dev-ui\n\n# Terminal 2: Start backend with hot-reload\nmake run\n```\n\n### Docker Development\n\nRun the complete stack with Docker Compose using the **Makefile** (recommended):\n\n```bash\n# Copy environment variables (first time only)\ncp .env.example .env\n\n# Build and start services\nmake up\n\n# View logs\nmake logs\n\n# Stop services\nmake down\n\n# Rebuild from scratch\nmake rebuild\n```\n\nOr use docker-compose directly:\n\n```bash\n# Start all services\ndocker-compose up\n\n# Or run in background\ndocker-compose up -d\n```\n\nThis will start:\n\n- ✅ Cml Cloud Manager App (http://localhost:8020)\n- ✅ MongoDB (localhost:8022) and Mongo Express (http://localhost:8023)\n- ✅ Keycloak (http://localhost:8021)\n- ✅ OpenTelemetry Collector\n- ✅ UI Builder (auto-rebuild)\n- ✅ Redis (localhost:6379)\n- ✅ Event Player (http://localhost:8024)\n\n## 👥 Test Users\n\nThe application includes test users with different roles:\n\n| Username | Password | Role | Capability Highlights |\n|----------|----------|------|-----------------------|\n| admin | test | admin | Full lifecycle (create/import/start/stop/terminate), monitoring control |\n| manager | test | manager | Start/stop, tag updates, view metrics \u0026 labs |\n| user | test | user | Read-only workers, metrics, labs |\n\nSee [deployment/keycloak/lablet-cloud-manager-realm-export.json](./deployment/keycloak/lablet-cloud-manager-realm-export.json)\n\n## 🔐 Authentication \u0026 RBAC\n\n## 🔄 Real-Time \u0026 Background Jobs\n\n| Feature | Component | Interval / Trigger |\n|---------|-----------|--------------------|\n| SSE Stream | `/api/events/stream` | Persistent (heartbeat 30s) |\n| Labs Refresh | `LabsRefreshJob` | Every 30 min + startup run |\n| Metrics Collection | `WorkerMetricsCollectionJob` | Configurable (`worker_metrics_poll_interval`) |\n| Status Updates | `UpdateCMLWorkerStatusCommand` | Manual \u0026 scheduled reconciliation |\n| Telemetry Events | Domain handlers | On state change |\n\n### SSE-First Worker Metadata\n\nWorker list, details, and telemetry now derive exclusively from Server-Sent Events:\n\n- `worker.snapshot` events provide full authoritative metadata + derived CPU / memory / storage utilization.\n- REST list \u0026 per-row enrichment calls were removed from the UI code; `loadWorkers()` is deprecated.\n- Manual refresh actions will transition to asynchronous scheduling that emits request/skip events and relies on subsequent metrics updates.\n- Simplicity goal: a single state flow (Aggregate → Domain Events → Snapshot Broadcast → UI render).\n\nIf snapshots fail to arrive within a short window, a passive \"Awaiting worker snapshot events\" message is shown instead of performing fallback REST polling.\n\nUI auto-refreshes worker list, details modal, and Labs tab. A badge shows connection status: connected / reconnecting / disconnected / error.\n\n## 👤 Extending Real-Time Events\n\nAdd a new event:\n\n1. Emit a domain event or directly broadcast.\n2. In handler: `await get_sse_relay().broadcast_event(\"my.event\", { id: ... })`\n3. In UI: `sseClient.on('my.event', data =\u003e {/* update UI */})`\n\nKeep payloads lean; prefer IDs and fetch details only when needed.\n\n\n### JWT Authentication\n\n- **Stateless**: No server-side sessions required\n- **Token Storage**: localStorage (not cookies)\n- **Expiration**: 24 hours (configurable)\n- **Claims**: username, user_id, roles, department\n\n### Role-Based Access Control\n\nAuthorization happens in the **application layer** (handlers), not controllers:\n\n- **Admin**: Can view and manage all tasks, can delete tasks\n- **Manager**: Can view tasks in their department\n- **User**: Can only view their assigned tasks\n\nExample RBAC logic in `GetTasksQueryHandler`:\n\n```python\nif \"admin\" in user_roles:\n    tasks = await self.task_repository.get_all_async()\nelif \"manager\" in user_roles:\n    tasks = await self.task_repository.get_by_department_async(department)\nelse:\n    tasks = await self.task_repository.get_by_assignee_async(user_id)\n```\n\n## 🛠️ Configuration\n\n### Environment Variables\n\nCreate a `.env` file (or use `.env.example`):\n\n```bash\n# Application server\nAPP_HOST=127.0.0.1         # Override only if you must expose the API externally\nAPP_PORT=8080\n\n# Keycloak OAuth2/OIDC\n# External URL - browser/Swagger UI accessible (defaults to http://localhost:8021)\nKEYCLOAK_URL=http://localhost:8021\n# Internal URL - backend server-to-server communication (optional, defaults to KEYCLOAK_URL if not set)\n# In Docker: use internal Docker network URL (http://keycloak:8080)\n# In Kubernetes: may be same as KEYCLOAK_URL or intra-cluster URL depending on setup\nKEYCLOAK_URL_INTERNAL=http://keycloak:8080\nKEYCLOAK_REALM=lablet-cloud-manager\nKEYCLOAK_CLIENT_ID=portal-web-app\n\n# Redis Session Storage (for production horizontal scaling)\nREDIS_ENABLED=false          # Set to true for production\nREDIS_URL=redis://redis:6379/0\nREDIS_KEY_PREFIX=session:\n\n# Database\nMONGODB_PASSWORD=neuroglia123\n```\n\n### Redis Session Store\n\nThe application supports two session storage backends:\n\n**Development (default)**: `InMemorySessionStore`\n\n- ⚡ Fast, no external dependencies\n- ⚠️ Sessions lost on restart\n- ❌ Not suitable for multiple instances\n\n**Production**: `RedisSessionStore`\n\n- 🔴 Distributed, shared across pods\n- 📈 Enables horizontal scaling in Kubernetes\n- 💪 Sessions survive pod restarts\n- ⏰ Auto-expiring via Redis TTL\n\nTo enable Redis for production:\n\n```bash\n# In .env file\nREDIS_ENABLED=true\n```\n\nSee `notes/REDIS_SESSION_STORE.md` for detailed documentation on:\n\n- Kubernetes deployment strategies\n- Redis configuration options\n- Testing horizontal scaling\n- Security best practices\n\n### VS Code Setup\n\nThe project includes VS Code settings for:\n\n- ✅ Automatic Poetry venv activation\n- ✅ Python formatter (Black)\n- ✅ Import organization\n- ✅ Pytest integration\n\n## 📚 Documentation\n\n### API Documentation\n\nOnce running, visit http://localhost:8020/api/docs for interactive API documentation.\n\n### Project Documentation\n\nComprehensive documentation is available in the `docs/` directory and online:\n\n- **Online**: https://bvandewe.github.io/lablet-cloud-manager\n- **Local**: Run `make docs-serve` and visit http://127.0.0.1:8000\n\n#### Documentation Topics\n\n- [**Getting Started**](https://bvandewe.github.io/lablet-cloud-manager/getting-started/installation/) - How to install and run the application.\n- [**Architecture**](https://bvandewe.github.io/lablet-cloud-manager/architecture/overview/) - CQRS pattern, dependency injection, design patterns\n- [**Security**](https://bvandewe.github.io/lablet-cloud-manager/security/authentication-flows/) - Dual auth system (session + JWT), OAuth2/OIDC, RBAC\n- [**Development**](https://bvandewe.github.io/lablet-cloud-manager/development/makefile-reference/) - Makefile reference, workflow, testing\n- [**AI Agent Guide**](https://bvandewe.github.io/lablet-cloud-manager/development/ai-agent-guide/) - Comprehensive guide for AI coding agents (and humans!)\n- [**Deployment**](https://bvandewe.github.io/lablet-cloud-manager/deployment/docker-environment/) - Docker environment, deployment, configuration\n- [**Troubleshooting**](https://bvandewe.github.io/lablet-cloud-manager/troubleshooting/common-issues/) - Common issues, known bugs, solutions\n\n#### Documentation Commands\n\n```bash\n# Install documentation dependencies\nmake docs-install\n\n# Serve documentation locally with live reload\nmake docs-serve\n\n# Build documentation site\nmake docs-build\n\n# Deploy to GitHub Pages (maintainers only)\nmake docs-deploy\n```\n\n### Key Endpoints\n\n#### Authentication\n\n- `POST /api/auth/login` - Login and get JWT token\n\n#### Tasks\n\n- `GET /api/tasks` - Get tasks (role-filtered)\n- `POST /api/tasks` - Create new task\n- `PUT /api/tasks/{task_id}` - Update task (with authorization)\n\nAll task endpoints require `Authorization: Bearer {token}` header.\n\n## �️ Makefile Commands\n\nThe project includes a comprehensive Makefile for easy development workflow management:\n\n### Docker Commands\n\n- `make build` - Build Docker image\n- `make dev` - Build and start Docker services with logs\n- `make rebuild` - Rebuild services from scratch (no cache)\n- `make up` - Start services in background\n- `make down` - Stop and remove services\n- `make restart` - Restart all services\n- `make logs` - Show logs from all services\n- `make clean` - Stop services and remove volumes ⚠️\n\n### Local Development Commands\n\n- `make setup` - Complete setup for new developers (install + build)\n- `make install` - Install Python dependencies with Poetry\n- `make install-ui` - Install Node.js dependencies\n- `make build-ui` - Build frontend assets\n- `make dev-ui` - Start UI dev server with hot-reload\n- `make run` - Run application locally with auto-reload\n- `make run-debug` - Run with debug logging\n\n### Testing \u0026 Quality Commands\n\n- `make test` - Run tests\n- `make test-cov` - Run tests with coverage report\n- `make lint` - Run linting checks\n- `make format` - Format code with Black\n\n### Utility Commands\n\n- `make clean` - Clean up caches and generated files\n- `make clean-all` - Clean everything including Docker volumes\n- `make status` - Show current environment status\n- `make info` - Display project information and URLs\n- `make env-check` - Check environment requirements\n- `make help` - Display all available commands\n\n**Example Workflow:**\n\n```bash\n# New developer setup\nmake setup\n\n# Start local development\nmake run\n\n# Or use Docker\nmake docker-up\nmake docker-logs\n\n# Stop Docker services\nmake docker-down\n```\n\n## �🔗 Related Documentation\n\n- [Neuroglia Python Framework](https://bvandewe.github.io/pyneuro/)\n- [Simple UI Sample](https://bvandewe.github.io/pyneuro/samples/simple-ui/)\n- [RBAC Guide](https://bvandewe.github.io/pyneuro/guides/rbac-authorization/)\n- [OAuth \u0026 JWT Reference](https://bvandewe.github.io/pyneuro/references/oauth-oidc-jwt/)\n\n## 🧪 Testing\n\n```bash\n# Run tests\npoetry run pytest\n```\n\n## 🪝 Pre-Commit Hooks\n\nAutomated formatting, linting, and security checks run before you commit to keep the codebase consistent.\n\n### What's Included\n\n- Trailing whitespace / EOF / merge conflict checks\n- Black (Python formatting) + isort (imports)\n- Flake8 (lint) and optional Ruff/extra rules if enabled\n- Prettier for JS/TS/CSS/HTML/JSON/YAML/Markdown\n- Markdownlint (auto-fix basic style issues)\n- Yamllint (with relaxed config)\n- Bandit (Python security scanning)\n- Detect-Secrets (prevents committing secrets)\n\n### Setup\n\n```bash\npoetry add --group dev pre-commit\npoetry run pre-commit install --install-hooks\npoetry run pre-commit run --all-files  # Run on entire repo once\n```\n\nIf you later update hooks:\n\n```bash\npoetry run pre-commit autoupdate\n```\n\n### Enforcing Consistency\n\nCI should run:\n\n```bash\npoetry run pre-commit run --all-files\n```\n\n### DCO Reminder\n\nPre-commit does not enforce DCO; ensure commits include:\n\n```\nSigned-off-by: Your Name \u003cyou@example.com\u003e\n```\n\nUse `git commit -s` to auto-add this line.\n\n\n## 🔁 Rebranding / Forking as a New Project\n\nYou can turn this repository into a new project quickly without manually hunting for every\n`lablet-cloud-manager` occurrence.\n\n### Option 1: Built-in Rename Script (Recommended)\n\nRun a dry run first:\n\n```bash\npython scripts/rename_project.py --new-name \"Acme Tasks\" --dry-run\n```\n\nApply the changes:\n\n```bash\npython scripts/rename_project.py --new-name \"Acme Tasks\"\n```\n\nThis will replace variants:\n\n- `lablet-cloud-manager` (slug)\n- `lablet_cloud_manager` (snake)\n- `Cml Cloud Manager` (title)\n- `LabletCloudManager` (Pascal)\n- `CML_CLOUD_MANAGER` (UPPER_SNAKE)\n- `Cml Cloud Manager API`\n\nOptional flags:\n\n```bash\n# Also adjust Keycloak realm/client identifiers (you must reconfigure Keycloak manually afterward)\npython scripts/rename_project.py --new-name \"Acme Tasks\" --update-keycloak\n\n# Limit to certain folders\npython scripts/rename_project.py --new-name \"Acme Tasks\" --include src docs\n\n# Override derived name styles explicitly\npython scripts/rename_project.py --new-name \"Acme Tasks\" \\\n    --slug acme-tasks --snake acme_tasks --pascal AcmeTasks --upper ACME_TASKS\n```\n\nPost-rename checklist:\n\n1. Rename the repository folder and remote (e.g., `git remote set-url origin ...`).\n2. Adjust Docker image tags / compose service names if needed.\n3. Update Keycloak realm + client IDs if `--update-keycloak` was used.\n4. Search for any remaining branding (e.g., README examples, docs URLs).\n5. Run tests: `poetry run pytest -q`.\n6. Rebuild UI assets: `make build-ui`.\n\n### Option 2: GitHub Template Repo\n\nUsing GitHub's built‑in Template feature lets you create a clean copy of the repository without forking the full commit history. Workflow:\n\n1. Maintainer: In the original repo, go to Settings → General → Enable \"Template repository\".\n2. Consumer: Click \"Use this template\" (instead of Fork). GitHub scaffolds a brand‑new repo with the current contents (no upstream remote linkage).\n3. In your new repo clone, run the rename script (Option 1) to apply your branding and identifiers.\n4. Update any secrets / realms (Keycloak) and run tests.\n\nWhy combine both? The template feature handles repository creation \u0026 initial history isolation; the rename script performs systematic text/style replacements so you don't miss lingering `lablet-cloud-manager` variants. If you skip the script, manual edits are error‑prone (especially mixed case variants and service identifiers).\n\n### Option 3: Cookiecutter (Future)\n\nYou can evolve this into a Cookiecutter template for parameter prompts. A future `cookiecutter.json` might include: app_name, slug, docker_image, keycloak_realm, enable_redis, etc.\n\n### Verify No Leftover Names\n\nRun the rename integrity test (after the script has been applied and test added):\n\n```bash\npoetry run pytest -k rename_integrity -q\n```\n\nIf it fails, it lists files containing residual references.\n\n### Run with coverage\n\n```bash\npoetry run pytest --cov=. --cov-report=html\n```\n\n## 📦 Deployment\n\n### Production Checklist\n\n- [ ] Change `JWT_SECRET_KEY` to a strong random value\n- [ ] Set `DEBUG=False` in settings\n- [ ] Configure proper database (MongoDB/PostgreSQL)\n- [ ] Set up Keycloak for production OAuth/OIDC\n- [ ] Configure CORS for production domains\n- [ ] Set up proper logging and monitoring\n- [ ] Use environment-specific `.env` files\n\n### Docker Production Build\n\n```bash\ndocker build -t lablet-cloud-manager:latest .\ndocker run -p 8000:8000 lablet-cloud-manager:latest\n```\n\n## 🤝 Contributing\n\nThis project follows the Neuroglia Python Framework patterns. See the [development guide](https://bvandewe.github.io/pyneuro/guides/local-development/) for more information.\n\n## 📄 License\n\nLicensed under the Apache License, Version 2.0. See `LICENSE` for the full text.\n\nCopyright © 2025 Cml Cloud Manager Contributors.\n\nYou may not use this project except in compliance with the License. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND.\n\n---\n\nBuilt with ❤️ using [Neuroglia Python Framework](https://github.com/bvandewe/pyneuro)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbvandewe%2Fcml-cloud-manager","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbvandewe%2Fcml-cloud-manager","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbvandewe%2Fcml-cloud-manager/lists"}