{"id":38474649,"url":"https://github.com/slauger/chantal","last_synced_at":"2026-06-26T01:00:17.748Z","repository":{"id":331977324,"uuid":"1131195555","full_name":"slauger/chantal","owner":"slauger","description":"A unified CLI tool for offline repository mirroring across multiple package ecosystems (RPM, APT, APK, Helm and more)","archived":false,"fork":false,"pushed_at":"2026-06-25T23:29:39.000Z","size":9591,"stargazers_count":0,"open_issues_count":12,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-26T00:08:56.058Z","etag":null,"topics":["alpine","apk","apt","debian","deduplication","helm","linux","mirror","patch-management","repository","rhel","rpm","snapshot","ubuntu"],"latest_commit_sha":null,"homepage":"https://slauger.github.io/chantal/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/slauger.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-09T16:08:44.000Z","updated_at":"2026-06-25T23:29:41.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/slauger/chantal","commit_stats":null,"previous_names":["slauger/chantal"],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/slauger/chantal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slauger%2Fchantal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slauger%2Fchantal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slauger%2Fchantal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slauger%2Fchantal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/slauger","download_url":"https://codeload.github.com/slauger/chantal/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slauger%2Fchantal/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34798183,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-25T02:00:05.521Z","response_time":101,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["alpine","apk","apt","debian","deduplication","helm","linux","mirror","patch-management","repository","rhel","rpm","snapshot","ubuntu"],"created_at":"2026-01-17T05:17:13.000Z","updated_at":"2026-06-26T01:00:17.726Z","avatar_url":"https://github.com/slauger.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Chantal\n\n**_Because every other name was already taken._** - A unified CLI tool for offline repository mirroring.\n\n[![Documentation](https://img.shields.io/badge/docs-read%20the%20docs-blue)](https://slauger.github.io/chantal/)\n[![Container](https://img.shields.io/badge/container-ghcr.io-blue)](https://github.com/slauger/chantal/pkgs/container/chantal)\n[![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)\n\n---\n\n## What is Chantal?\n\nA Python-based CLI tool for offline repository mirroring, inspired by pulp-admin, reposync, and aptly.\n\n**The Problem:** Enterprise environments need offline mirrors of RPM/APT repositories with version control, efficient storage, RHEL subscription support, and simple management. Existing tools either:\n- Support only one repository type (`reposync` for RPM, `apt-mirror` for APT)\n- Require complex infrastructure (Pulp needs Celery, RabbitMQ, Redis, PostgreSQL)\n- Lack proper snapshot and deduplication features\n\n**The Solution:** One simple CLI tool. No daemons, no message queues, no complex setup. Just sync repositories, create snapshots, and publish static files. Works with any webserver (Apache, NGINX) - because it's just files.\n\n## Features\n\n- 🔄 **Unified Mirroring** - Multiple repository types in one tool (RPM, DEB/APT, Helm, Alpine APK)\n- 📦 **Deduplication** - Content-addressed storage (SHA256), packages stored once\n- 📸 **Snapshots** - Immutable point-in-time repository states for patch management\n- 🔍 **Views** - Virtual repositories combining multiple repos (e.g., BaseOS + AppStream + EPEL)\n- 🔌 **Modular** - Plugin architecture for repository types\n- 🚫 **No Daemons** - Simple CLI tool (optional scheduler for future automation)\n- 📁 **Static Output** - Serve with any webserver (Apache, NGINX)\n- 🔐 **RHEL CDN Support** - Client certificate authentication for Red Hat repos\n- 🎯 **Smart Filtering** - Pattern-based package filtering with post-processing\n- 🪞 **Mirror, Filtered \u0026 Hosted Modes** - Full metadata mirroring, filtered repos with regenerated metadata, or hosted repos built from your own uploaded packages\n- ⬆️ **Custom Package Upload** - Inject local packages into a repository's pool with `chantal package upload` (RPM, DEB/APT, Helm)\n- 🔏 **Metadata Signing** - Sign regenerated metadata in filtered mode: APT (InRelease/Release.gpg), RPM (repomd.xml.asc) and APK (RSA-signed APKINDEX) so clients can verify the repo\n- ⚡ **Fast Updates** - Check for updates without downloading (like `dnf check-update`)\n- 🚀 **Metadata Caching** - SHA256-based cache for RPM metadata (90-95% faster syncs for RHEL)\n\n**Supported Repository Types:**\n- ✅ **RPM/DNF/YUM** (RHEL, CentOS, Fedora, Rocky, AlmaLinux, EPEL)\n- ✅ **DEB/APT** (Debian, Ubuntu)\n- ✅ **Helm Charts** (Kubernetes, Bitnami, AWS EKS, Prometheus, GitLab)\n- ✅ **Alpine APK** (Alpine Linux, container base images)\n\n---\n\n## Quick Start\n\n### Installation\n\n**Option 1: Container (Recommended)**\n\n```bash\n# Pull from GitHub Container Registry\ndocker pull ghcr.io/slauger/chantal:latest\n\n# Run\ndocker run --rm \\\n  -v $(pwd)/config:/etc/chantal:ro \\\n  -v $(pwd)/data:/var/lib/chantal \\\n  -v $(pwd)/repos:/var/www/repos \\\n  ghcr.io/slauger/chantal:latest --help\n```\n\n**Option 2: PyPI (Recommended for Python users)**\n\n```bash\npip install chantal\n```\n\n**Option 3: From Source**\n\n```bash\ngit clone https://github.com/slauger/chantal.git\ncd chantal\npip install -e .\n```\n\n**Requirements:** Python 3.12+, PostgreSQL or SQLite\n\n### Basic Usage\n\n```bash\n# 1. Initialize database schema\nchantal db init\n\n# 2. Configure repositories (see docs for examples)\nvim /etc/chantal/config.yaml\n\n# 3. Sync repository (RPM, Helm, or APK)\nchantal repo sync --repo-id epel9-latest\n\n# 4. Create snapshot\nchantal snapshot create --repo-id epel9-latest --name 2025-01\n\n# 5. Publish\nchantal publish snapshot --snapshot epel9-latest-2025-01\n```\n\n**Result:** Published repository in `/var/www/repos/` ready to serve with Apache/NGINX.\n\n### Database Management\n\nChantal uses Alembic for database schema migrations:\n\n```bash\n# Initialize database schema (first-time setup)\nchantal db init\n\n# Check current schema version\nchantal db current\n\n# Check schema status and pending migrations\nchantal db status\n\n# Upgrade to latest schema version\nchantal db upgrade\n\n# View migration history\nchantal db history\n\n# Database statistics and verification\nchantal db stats\nchantal db verify\n```\n\n**Note:** Storage directories are created automatically when needed. The `db init` command only initializes the database schema.\n\n---\n\n## Key Features\n\n### Content-Addressed Storage\n- SHA256-based deduplication (2-level directory: `ab/cd/sha256_file.rpm`)\n- Packages stored once, shared across all repositories\n- Typical deduplication: 60-80% across RHEL variants\n\n### Immutable Snapshots\n- Point-in-time freezes for patch management\n- Compare snapshots (`chantal snapshot diff`)\n- Rollback to previous states\n- Atomic view snapshots (freeze all repos simultaneously)\n\n### Virtual Repositories (Views)\n- Combine multiple repos into one: `BaseOS + AppStream + CRB`\n- Mixed repos: `RHEL + EPEL` in single repository\n- Stack-specific views: web server, monitoring, etc.\n\n### Smart Filtering\n```yaml\nfilters:\n  patterns:\n    include: [\"^nginx-.*\", \"^httpd-.*\"]\n    exclude: [\".*-debug.*\"]\n  metadata:\n    architectures:\n      include: [\"x86_64\", \"noarch\"]\n  post_processing:\n    only_latest_version: true\n```\n\n### Zero-Copy Publishing\n- Hardlinks (not copies) to published directories\n- Instant publishing (milliseconds for thousands of packages)\n- Atomic metadata updates\n\n---\n\n## Architecture\n\n```\n/var/lib/chantal/pool/          # Content-addressed storage (SHA256)\n├── ab/cd/sha256_package.rpm\n└── ...\n\n/var/www/repos/                  # Published repositories (hardlinks)\n├── rhel9-baseos/\n│   ├── latest/                  # Current state\n│   └── snapshots/2025-01/       # Immutable snapshot\n└── views/\n    └── rhel9-complete/          # Virtual repository\n        └── latest/\n```\n\n**Database:** PostgreSQL or SQLite (SQLAlchemy models)\n**Plugins:** Extensible architecture for repository types (RPM, DEB/APT, Helm, APK)\n\n---\n\n## Documentation\n\n📚 **Full Documentation:** https://slauger.github.io/chantal/\n\n- [Installation Guide](https://slauger.github.io/chantal/user-guide/installation.html)\n- [Quick Start](https://slauger.github.io/chantal/user-guide/quickstart.html)\n- [CLI Commands](https://slauger.github.io/chantal/user-guide/cli-commands.html)\n- [Configuration](https://slauger.github.io/chantal/configuration/overview.html)\n- [Views (Virtual Repositories)](https://slauger.github.io/chantal/user-guide/views.html)\n- [Architecture](https://slauger.github.io/chantal/architecture/overview.html)\n- [Plugin Development](https://slauger.github.io/chantal/plugins/custom-plugins.html)\n\n---\n\n## Common Workflows\n\n### Patch Management\n```bash\n# Monthly cycle\nchantal repo sync --all\nchantal snapshot create --repo-id rhel9-baseos --name 2025-02\nchantal snapshot diff --repo-id rhel9-baseos 2025-01 2025-02\nchantal publish snapshot --snapshot rhel9-baseos-2025-02\n```\n\n### RHEL Subscription\n```yaml\nrepositories:\n  - id: rhel9-baseos\n    feed: https://cdn.redhat.com/content/dist/rhel9/9/x86_64/baseos/os\n    ssl:\n      client_cert: /etc/pki/entitlement/1234567890.pem\n      client_key: /etc/pki/entitlement/1234567890-key.pem\n```\n\n### Air-Gapped Environments\n```bash\n# Online system\nchantal repo sync --all\ntar czf export.tar.gz /var/lib/chantal /etc/chantal\n\n# Offline system\ntar xzf export.tar.gz\nchantal publish repo --all\n```\n\nSee [Workflows Documentation](https://slauger.github.io/chantal/user-guide/workflows.html) for more examples.\n\n---\n\n## Contributing\n\nContributions welcome! See [GitHub Issues](https://github.com/slauger/chantal/issues) for planned features and improvements.\n\n### Development Setup\n\n**1. Clone and Setup Virtual Environment:**\n```bash\ngit clone https://github.com/slauger/chantal.git\ncd chantal\n\n# Create virtual environment\nmake venv\n# OR manually: python3 -m venv venv\n\n# Activate virtual environment\nsource venv/bin/activate\n\n# Install dependencies\nmake install-dev\n# OR manually: pip install -e \".[dev]\"\n```\n\n**2. Run Local Tests (CI/CD Checks):**\n```bash\n# IMPORTANT: Always use the Makefile targets to ensure correct venv usage!\n\n# Run all linters (same as CI/CD)\nmake lint\n\n# Run all checks (linters + tests)\nmake check\n\n# Individual checks\nmake ruff        # Linting\nmake black       # Code formatting check\nmake yamllint    # YAML linting\nmake mypy        # Type checking\nmake pytest      # Unit tests\n\n# Auto-format code\nmake format\n```\n\n**3. Development Notes:**\n- ⚠️ **Always activate the venv** before running tests (`source venv/bin/activate`)\n- ✅ **Use `make lint`** instead of running tools directly - ensures venv usage\n- ✅ **CI/CD runs the same checks** as `make lint` - local = CI/CD\n- 🔧 All linters are pinned to specific versions for consistency\n- 📝 See `Makefile` for all available targets\n\nRead the [Architecture Documentation](https://slauger.github.io/chantal/architecture/overview.html) before contributing.\n\n---\n\n## License\n\nMIT License - See [LICENSE](LICENSE) file for details.\n\n---\n\n## Credits\n\nDeveloped by [Simon Lauger](https://github.com/slauger)\n\nInspired by: pulp-admin, reposync, aptly, apt-mirror, bandersnatch\n\n---\n\n**📦 Container Images:** `ghcr.io/slauger/chantal:latest`\n\n**📚 Documentation:** https://slauger.github.io/chantal/\n\n**🐛 Issues:** https://github.com/slauger/chantal/issues\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslauger%2Fchantal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fslauger%2Fchantal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslauger%2Fchantal/lists"}