{"id":50916392,"url":"https://github.com/quinnjr/package-repository-server","last_synced_at":"2026-06-16T15:32:14.862Z","repository":{"id":335218342,"uuid":"1128579322","full_name":"quinnjr/package-repository-server","owner":"quinnjr","description":"Multi-format package repository server supporting DEB, RPM, Arch, and Alpine packages with automatic indexing, GPG signing, and S3-compatible storage","archived":false,"fork":false,"pushed_at":"2026-06-11T15:42:44.000Z","size":946,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-11T16:08:52.588Z","etag":null,"topics":["actix-web","alpine-linux","angular","arch-linux","deb","docker","kubernetes","package-repository","rpm","rust"],"latest_commit_sha":null,"homepage":null,"language":"Rust","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/quinnjr.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":".github/SECURITY.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},"funding":{"patreon":"PegasusHeavyIndustries","custom":["https://www.patreon.com/c/PegasusHeavyIndustries?vanity=user"]}},"created_at":"2026-01-05T21:14:09.000Z","updated_at":"2026-06-11T15:42:51.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/quinnjr/package-repository-server","commit_stats":null,"previous_names":["pegasusheavy/package-repository-server","quinnjr/package-repository-server"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/quinnjr/package-repository-server","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quinnjr%2Fpackage-repository-server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quinnjr%2Fpackage-repository-server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quinnjr%2Fpackage-repository-server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quinnjr%2Fpackage-repository-server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/quinnjr","download_url":"https://codeload.github.com/quinnjr/package-repository-server/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quinnjr%2Fpackage-repository-server/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34412788,"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-16T02:00:06.860Z","response_time":126,"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":["actix-web","alpine-linux","angular","arch-linux","deb","docker","kubernetes","package-repository","rpm","rust"],"created_at":"2026-06-16T15:32:14.755Z","updated_at":"2026-06-16T15:32:14.854Z","avatar_url":"https://github.com/quinnjr.png","language":"Rust","funding_links":["https://patreon.com/PegasusHeavyIndustries","https://www.patreon.com/c/PegasusHeavyIndustries?vanity=user"],"categories":[],"sub_categories":[],"readme":"# Package Repository Server\n\nA self-hosted, multi-format package repository server supporting **DEB** (Debian/Ubuntu), **RPM** (RHEL/Fedora/Rocky), **Arch Linux**, and **Alpine Linux** packages with automatic indexing, GPG signing, and multi-architecture support (x86_64/ARM64).\n\nBuilt entirely in Rust - uses **Actix** for the API server and **Ferron** for static file serving.\n\n## Features\n\n- **Multi-Format Support**: APT, YUM/DNF, Pacman, and APK repositories in one server\n- **Multi-Architecture**: Full support for x86_64 (amd64) and ARM64 (aarch64)\n- **Automatic Indexing**: Packages are automatically indexed and signed on upload\n- **GPG Signing**: All repository metadata is GPG-signed for security\n- **SSO Authentication**: OAuth 2.0 / OpenID Connect support (Google, GitHub, GitLab, Microsoft, Okta, Auth0, Keycloak)\n- **Stateless Backend**: No session storage required - horizontally scalable out of the box\n- **REST API**: Full API for CI/CD integration\n- **S3 Compatible Storage**: Optional S3-compatible backend (AWS S3, MinIO, DigitalOcean Spaces, etc.)\n- **Pure Rust**: Actix API server + Ferron static file server\n- **Cloud Ready**: Terraform modules for AWS, GCP, Azure, DigitalOcean, and Vultr\n\n## Quick Start\n\n### Docker Compose (HTTP)\n\n```bash\n# Clone the repository\ngit clone https://github.com/your-org/package-repo.git\ncd package-repo\n\n# Generate an API key\nAPI_KEY=$(openssl rand -hex 32)\necho \"Your API key: $API_KEY\"\n\n# Start the server\nAPI_KEYS=$API_KEY docker-compose -f docker/docker-compose.yml up -d\n\n# Repositories at http://localhost/deb, /rpm, /arch, /alpine\n```\n\n### Docker Compose with HTTPS (Let's Encrypt)\n\n```bash\n# Set your domain and email\nexport DOMAIN=packages.example.com\nexport ADMIN_EMAIL=admin@example.com\nexport API_KEYS=$(openssl rand -hex 32)\n\n# Start with TLS\ndocker-compose -f docker/docker-compose.yml -f docker/docker-compose.tls.yml up -d\n\n# Obtain Let's Encrypt certificate\ndocker exec package-repo setup-ssl letsencrypt\n\n# Restart to apply\ndocker-compose restart\n```\n\n### Self-Signed Certificate (Testing)\n\n```bash\n# Generate self-signed cert\ndocker exec package-repo setup-ssl selfsigned\n\n# Restart to apply\ndocker-compose restart\n```\n\n### Build from Source\n\n```bash\n# Build everything\nmake build\n\n# Run\nmake run\n\n# View logs\nmake logs\n```\n\n## Client Configuration\n\n### One-Liner Setup (Recommended)\n\nThe easiest way to add the repository to your system:\n\n```bash\n# Ubuntu/Debian (APT)\ncurl -fsSL https://packages.example.com/setup/apt | sudo bash\n\n# RHEL/Fedora/Rocky (YUM/DNF)\ncurl -fsSL https://packages.example.com/setup/rpm | sudo bash\n\n# Arch Linux (Pacman)\ncurl -fsSL https://packages.example.com/setup/arch | sudo bash\n\n# Alpine Linux (APK)\ncurl -fsSL https://packages.example.com/setup/alpine | sudo sh\n```\n\nThat's it! After running the one-liner, you can install packages normally:\n- APT: `sudo apt install \u003cpackage\u003e`\n- DNF: `sudo dnf install \u003cpackage\u003e`\n- Pacman: `sudo pacman -S \u003cpackage\u003e`\n- APK: `apk add \u003cpackage\u003e`\n\n### Manual Configuration\n\n\u003cdetails\u003e\n\u003csummary\u003eClick to expand manual setup instructions\u003c/summary\u003e\n\n#### Debian/Ubuntu (APT)\n\n```bash\n# Add GPG key\ncurl -fsSL https://packages.example.com/repo.gpg | sudo gpg --dearmor -o /usr/share/keyrings/custom-repo.gpg\n\n# Add repository\necho \"deb [signed-by=/usr/share/keyrings/custom-repo.gpg] https://packages.example.com/deb stable main\" | \\\n  sudo tee /etc/apt/sources.list.d/custom.list\n\n# Update and install\nsudo apt update\n```\n\n#### RHEL/Fedora/Rocky (YUM/DNF)\n\n```bash\n# Create repo file\nsudo tee /etc/yum.repos.d/custom.repo \u003c\u003c EOF\n[custom-repo]\nname=Custom Repository\nbaseurl=https://packages.example.com/rpm/\\$basearch/\nenabled=1\ngpgcheck=1\ngpgkey=https://packages.example.com/repo.gpg\nEOF\n```\n\n#### Arch Linux (Pacman)\n\n```bash\n# Add to /etc/pacman.conf\n[custom]\nSigLevel = Optional TrustAll\nServer = https://packages.example.com/arch/$arch\n\n# Sync databases\nsudo pacman -Sy\n```\n\n#### Alpine Linux (APK)\n\n```bash\n# Add repository\necho \"https://packages.example.com/alpine/v3.19/main\" \u003e\u003e /etc/apk/repositories\n\n# Import key\nwget -qO /etc/apk/keys/repo.rsa.pub https://packages.example.com/repo.gpg\n```\n\n\u003c/details\u003e\n\n## API Usage\n\n### Upload a Package\n\n```bash\n# Upload DEB package\ncurl -X POST \\\n  -H \"X-API-Key: your-api-key\" \\\n  -F \"file=@mypackage_1.0.0_amd64.deb\" \\\n  https://packages.example.com/api/v1/upload/deb\n\n# Upload RPM package\ncurl -X POST \\\n  -H \"X-API-Key: your-api-key\" \\\n  -F \"file=@mypackage-1.0.0-1.x86_64.rpm\" \\\n  https://packages.example.com/api/v1/upload/rpm\n\n# Upload Arch package\ncurl -X POST \\\n  -H \"X-API-Key: your-api-key\" \\\n  -F \"file=@mypackage-1.0.0-1-x86_64.pkg.tar.zst\" \\\n  https://packages.example.com/api/v1/upload/arch\n\n# Upload Alpine package\ncurl -X POST \\\n  -H \"X-API-Key: your-api-key\" \\\n  -F \"file=@mypackage-1.0.0-r0.apk\" \\\n  https://packages.example.com/api/v1/upload/alpine\n```\n\n### List Packages\n\n```bash\n# List all packages\ncurl https://packages.example.com/api/v1/packages\n\n# List by type\ncurl https://packages.example.com/api/v1/packages/deb\ncurl https://packages.example.com/api/v1/packages/rpm\n```\n\n### Delete a Package\n\n```bash\ncurl -X DELETE \\\n  -H \"X-API-Key: your-api-key\" \\\n  https://packages.example.com/api/v1/packages/deb/mypackage\n```\n\n### Rebuild Repository\n\n```bash\ncurl -X POST \\\n  -H \"X-API-Key: your-api-key\" \\\n  https://packages.example.com/api/v1/repos/deb/rebuild\n```\n\n## CI/CD Integration\n\nRepository indexes **automatically update** when packages are uploaded - no manual rebuild required. This makes it easy to integrate with any CI/CD system.\n\n### Quick Example (Any CI System)\n\n```bash\n# Upload triggers automatic re-indexing\ncurl -X POST \\\n  -H \"X-API-Key: $PACKAGE_REPO_API_KEY\" \\\n  -F \"file=@mypackage_1.0.0_amd64.deb\" \\\n  https://packages.example.com/api/v1/upload/deb\n```\n\n### Ready-to-Use CI Configurations\n\nExample configurations are provided in `ci-examples/`:\n\n| Platform | File | Trigger |\n|----------|------|---------|\n| GitHub Actions | `github-actions.yml` | Release published, manual |\n| GitLab CI | `gitlab-ci.yml` | Tags (v*) |\n| Bitbucket Pipelines | `bitbucket-pipelines.yml` | Tags (v*), manual |\n| Jenkins | `jenkins-pipeline.groovy` | Parameterized |\n| Azure DevOps | `azure-devops.yml` | Tags (v*) |\n| Drone CI | `drone-ci.yml` | Tags |\n\n### Required Secrets/Variables\n\nConfigure these in your CI system:\n\n| Variable | Description |\n|----------|-------------|\n| `PACKAGE_REPO_URL` | Your repository URL (e.g., `https://packages.example.com`) |\n| `PACKAGE_REPO_API_KEY` | API key for authentication |\n\n### GitHub Actions Example\n\n```yaml\n# .github/workflows/publish.yml\nname: Publish Package\non:\n  release:\n    types: [published]\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Build package\n        run: dpkg-deb --build ./package mypackage_${{ github.ref_name }}_amd64.deb\n\n      - name: Upload to repository\n        run: |\n          curl -X POST \\\n            -H \"X-API-Key: ${{ secrets.PACKAGE_REPO_API_KEY }}\" \\\n            -F \"file=@mypackage_${{ github.ref_name }}_amd64.deb\" \\\n            \"${{ vars.PACKAGE_REPO_URL }}/api/v1/upload/deb\"\n```\n\n### GitLab CI Example\n\n```yaml\n# .gitlab-ci.yml\npublish:\n  stage: deploy\n  image: curlimages/curl:latest\n  script:\n    - |\n      curl -X POST \\\n        -H \"X-API-Key: ${PACKAGE_REPO_API_KEY}\" \\\n        -F \"file=@mypackage_${CI_COMMIT_TAG}_amd64.deb\" \\\n        \"${PACKAGE_REPO_URL}/api/v1/upload/deb\"\n  rules:\n    - if: $CI_COMMIT_TAG\n```\n\n## Deployment\n\n### Kubernetes\n\n```bash\n# Apply manifests directly\nkubectl apply -f kubernetes/\n\n# Or use Helm\nhelm install package-repo ./helm/package-repo \\\n  --namespace package-repo \\\n  --create-namespace \\\n  --set ingress.enabled=true \\\n  --set ingress.hosts[0].host=packages.example.com \\\n  --set config.apiKeys[0]=\"your-secure-api-key\"\n```\n\n### Terraform\n\n#### AWS (EKS)\n\n```bash\ncd terraform/aws\n\ncat \u003e terraform.tfvars \u003c\u003c EOF\neks_cluster_name = \"my-cluster\"\ndomain           = \"packages.example.com\"\napi_keys         = [\"your-secure-api-key\"]\nuse_s3_storage   = true\ns3_bucket_name   = \"my-packages\"\nEOF\n\nterraform init\nterraform apply\n```\n\n#### GCP (GKE)\n\n```bash\ncd terraform/gcp\n\ncat \u003e terraform.tfvars \u003c\u003c EOF\ngcp_project          = \"my-project\"\ngke_cluster_name     = \"my-cluster\"\ngke_cluster_location = \"us-central1\"\ndomain               = \"packages.example.com\"\napi_keys             = [\"your-secure-api-key\"]\nEOF\n\nterraform init\nterraform apply\n```\n\n#### Azure (AKS)\n\n```bash\ncd terraform/azure\n\ncat \u003e terraform.tfvars \u003c\u003c EOF\nresource_group_name = \"my-rg\"\naks_cluster_name    = \"my-cluster\"\ndomain              = \"packages.example.com\"\napi_keys            = [\"your-secure-api-key\"]\nEOF\n\nterraform init\nterraform apply\n```\n\n#### DigitalOcean (DOKS)\n\n```bash\ncd terraform/digitalocean\n\ncat \u003e terraform.tfvars \u003c\u003c EOF\ndo_token          = \"your-do-token\"\ndoks_cluster_name = \"my-cluster\"\ndomain            = \"packages.example.com\"\napi_keys          = [\"your-secure-api-key\"]\nEOF\n\nterraform init\nterraform apply\n```\n\n#### Vultr (VKE)\n\n```bash\ncd terraform/vultr\n\ncat \u003e terraform.tfvars \u003c\u003c EOF\nvultr_api_key    = \"your-vultr-key\"\nvke_cluster_name = \"my-cluster\"\ndomain           = \"packages.example.com\"\napi_keys         = [\"your-secure-api-key\"]\nEOF\n\nterraform init\nterraform apply\n```\n\n## Authentication\n\n### API Key Authentication\n\nFor service accounts, CI/CD, and automation:\n\n```bash\n# Generate a secure API key\nAPI_KEY=$(openssl rand -hex 32)\n\n# Use in requests\ncurl -H \"X-API-Key: $API_KEY\" \\\n  -F \"file=@package.deb\" \\\n  https://packages.example.com/api/v1/upload/deb\n```\n\n### SSO Authentication\n\nEnable OAuth 2.0 / OpenID Connect for user login:\n\n```bash\n# Enable SSO\nexport SSO_ENABLED=true\nexport SSO_JWT_SECRET=$(openssl rand -hex 32)\nexport SSO_BASE_URL=https://packages.example.com\n\n# Configure Google SSO\nexport SSO_GOOGLE_ENABLED=true\nexport SSO_GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com\nexport SSO_GOOGLE_CLIENT_SECRET=your-secret\nexport SSO_GOOGLE_ALLOWED_DOMAINS=example.com\n\n# Start server\ndocker-compose up -d\n```\n\n**Supported SSO Providers:**\n- Google (Google Workspace)\n- GitHub\n- GitLab (SaaS and self-hosted)\n- Microsoft / Azure AD\n- Okta\n- Auth0\n- Keycloak\n- Generic OIDC (any compliant provider)\n\nSee [docs/SSO_CONFIGURATION.md](docs/SSO_CONFIGURATION.md) for complete SSO setup guide.\n\n## Configuration\n\n### Environment Variables\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `API_KEYS` | Comma-separated list of valid API keys | `default-change-me` |\n| `REPO_DATA_DIR` | Package storage directory | `/data/packages` |\n| `REPO_GPG_DIR` | GPG keys directory | `/data/gpg` |\n| `RUST_LOG` | Log level (trace, debug, info, warn, error) | `info` |\n| `S3_ENABLED` | Enable S3 storage backend | `false` |\n| `S3_ENDPOINT` | S3 endpoint URL | `` |\n| `S3_BUCKET` | S3 bucket name | `packages` |\n| `S3_REGION` | S3 region | `us-east-1` |\n| `S3_ACCESS_KEY` | S3 access key | `` |\n| `S3_SECRET_KEY` | S3 secret key | `` |\n| `SSO_ENABLED` | Enable SSO authentication | `false` |\n| `SSO_JWT_SECRET` | JWT secret for session tokens | `` |\n| `SSO_BASE_URL` | Base URL for OAuth redirects | `` |\n\n### Helm Values\n\nSee `helm/package-repo/values.yaml` for all available options.\n\n## Architecture\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│                    Package Repository Server                 │\n├─────────────────────────────────────────────────────────────┤\n│  ┌─────────────────────────┐  ┌───────────────────────────┐ │\n│  │     Actix REST API      │  │    Package Processor      │ │\n│  │     (Port 8080)         │  │    (Sign \u0026 Index)         │ │\n│  └─────────────────────────┘  └───────────────────────────┘ │\n├─────────────────────────────────────────────────────────────┤\n│  ┌─────────────────────────────────────────────────────────┐│\n│  │              Ferron (Static File Serving)               ││\n│  │  /deb/* → APT repo    /rpm/* → YUM repo                ││\n│  │  /arch/* → Pacman     /alpine/* → APK repo             ││\n│  └─────────────────────────────────────────────────────────┘│\n├─────────────────────────────────────────────────────────────┤\n│  ┌─────────────────────────────────────────────────────────┐│\n│  │              Storage (Local / S3-compatible)            ││\n│  └─────────────────────────────────────────────────────────┘│\n└─────────────────────────────────────────────────────────────┘\n```\n\n## Development\n\n```bash\n# Run development server\nmake dev\n\n# Run tests\nmake test\n\n# Lint code\nmake lint\n\n# Format code\nmake fmt\n```\n\n## Security\n\n- Always use strong, randomly generated API keys\n- Enable TLS in production (use cert-manager for automatic certificates)\n- GPG keys are auto-generated on first run; backup `/data/gpg` for persistence\n- Consider network policies to restrict access\n\n## License\n\nApache License 2.0 - See [LICENSE](LICENSE) file for details.\n\nCopyright 2026 Pegasus Heavy Industries LLC\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquinnjr%2Fpackage-repository-server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fquinnjr%2Fpackage-repository-server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquinnjr%2Fpackage-repository-server/lists"}