Roles & permissions¶
Repod uses role-based access control (RBAC) with 5 roles. The roles are not a strict hierarchy — auditor is a lateral role that grants read access to audit and security data without any write capability.
Role definitions¶
| Role | Label | Purpose |
|---|---|---|
reader |
Lecteur | Read-only access to packages and statistics. Intended for client machines and read-only service accounts. |
uploader |
Packager / CI-CD | Can upload and import packages. Intended for CI/CD pipelines. Cannot delete, quarantine, or access audit logs. |
auditor |
Auditeur | Read-only access to audit logs, CVE review queue, and the full package catalog. Cannot upload or write anything. Intended for compliance and security teams. |
maintainer |
Mainteneur | Full package lifecycle management: upload, import, delete, quarantine, sync, CVE rescan, and CVE decisions. Can read audit logs. Cannot manage users or settings. |
admin |
Administrateur | Full access: all maintainer permissions plus user management, settings, and GPG key management. |
Dependency resolver (authentication middleware)¶
The API enforces access using five dependency functions. Each endpoint declares which function it requires:
| Dependency | Roles allowed |
|---|---|
get_current_user |
All authenticated roles (reader, uploader, auditor, maintainer, admin) |
get_uploader_user |
uploader, maintainer, admin |
get_auditor_user |
auditor, maintainer, admin |
get_maintainer_user |
maintainer, admin |
get_admin_user |
admin only |
Permission matrix¶
| Permission | reader | auditor | uploader | maintainer | admin |
|---|---|---|---|---|---|
| Packages | |||||
List packages (GET /packages/) |
✅ | ✅ | ✅ | ✅ | ✅ |
View artifact details (GET /artifacts/{name}) |
✅ | ✅ | ✅ | ✅ | ✅ |
View dependencies (GET /artifacts/{name}/dependencies) |
✅ | ✅ | ✅ | ✅ | ✅ |
Upload packages (POST /upload/) |
❌ | ❌ | ✅ | ✅ | ✅ |
Import from upstream (POST /import/fetch) |
❌ | ❌ | ✅ | ✅ | ✅ |
Batch import (POST /import/batch) |
❌ | ❌ | ✅ | ✅ | ✅ |
Delete all versions (DELETE /artifacts/{name}) |
❌ | ❌ | ❌ | ✅ | ✅ |
Delete specific version (DELETE /artifacts/{name}/{version}) |
❌ | ❌ | ❌ | ✅ | ✅ |
Sync index (POST /artifacts/admin/sync-index) |
❌ | ❌ | ❌ | ✅ | ✅ |
| Distributions | |||||
List distributions (GET /distributions/) |
✅ | ✅ | ✅ | ✅ | ✅ |
| List packages by distribution | ✅ | ✅ | ✅ | ✅ | ✅ |
Initialise distributions (POST /distributions/init) |
❌ | ❌ | ❌ | ✅ | ✅ |
| Promote / migrate (Enterprise only) | ❌ | ❌ | ❌ | ✅ | ✅ |
| Security | |||||
View antivirus status (GET /security/clamav/status) |
✅ | ✅ | ✅ | ✅ | ✅ |
Update antivirus signatures (POST /security/clamav/update) |
❌ | ❌ | ❌ | ❌ | ✅ |
View CVE findings (GET /security/vulnerabilities) |
✅ | ✅ | ✅ | ✅ | ✅ |
View CVE posture (GET /security/packages-posture) |
✅ | ✅ | ✅ | ✅ | ✅ |
View per-package CVE (GET /security/packages/{n}/{v}/cve) |
✅ | ✅ | ✅ | ✅ | ✅ |
View review queue (GET /security/review-queue) |
✅ | ✅ | ✅ | ✅ | ✅ |
Make a CVE decision (POST /security/packages/{n}/{v}/decide) |
❌ | ❌ | ❌ | ✅ | ✅ |
Get decision + SLA (GET /security/packages/{n}/{v}/decision) |
✅ | ✅ | ✅ | ✅ | ✅ |
Trigger CVE rescan (POST /security/packages/{n}/{v}/rescan) |
❌ | ❌ | ❌ | ✅ | ✅ |
Quarantine a package (POST /security/packages/{n}/{v}/quarantine) |
❌ | ❌ | ❌ | ✅ | ✅ |
Security report (GET /security/report) |
✅ | ✅ | ✅ | ✅ | ✅ |
Trigger SLA check (POST /security/check-sla) |
❌ | ❌ | ❌ | ❌ | ✅ |
| SBOM (Enterprise only) | |||||
Export SBOM (GET /sbom/export) |
✅ | ✅ | ✅ | ✅ | ✅ |
Per-package SBOM (GET /sbom/{name}/{version}) |
✅ | ✅ | ✅ | ✅ | ✅ |
SBOM preview (GET /sbom/preview) |
✅ | ✅ | ✅ | ✅ | ✅ |
| Import & sync | |||||
Search upstream index (GET /import/search) |
✅ | ✅ | ✅ | ✅ | ✅ |
Resolve dependencies (GET /import/resolve/{name}) |
✅ | ✅ | ✅ | ✅ | ✅ |
Sync all sources (POST /import/sync) |
❌ | ❌ | ❌ | ✅ | ✅ |
Sync one source (POST /import/sync/{source_id}) |
❌ | ❌ | ❌ | ✅ | ✅ |
Sync security sources (POST /import/sync-security) |
❌ | ❌ | ❌ | ✅ | ✅ |
List import groups (GET /import/groups) |
✅ | ✅ | ✅ | ✅ | ✅ |
Delete import group (DELETE /import/groups/{name}) |
❌ | ❌ | ❌ | ❌ | ✅ |
| Inventory & SSH | |||||
Inventory summary (GET /inventory/summary) |
❌ | ✅ | ❌ | ✅ | ✅ |
List clients (GET /inventory/clients) |
❌ | ✅ | ❌ | ✅ | ✅ |
Client details (GET /inventory/clients/{id}) |
❌ | ✅ | ❌ | ✅ | ✅ |
| Client packages / updates / summary | ❌ | ✅ | ❌ | ✅ | ✅ |
List scans (GET /inventory/scans) |
❌ | ✅ | ❌ | ✅ | ✅ |
Scan status (GET /inventory/clients/{id}/scan/status) |
❌ | ✅ | ❌ | ✅ | ✅ |
CVE results (GET /inventory/cve) |
❌ | ✅ | ❌ | ✅ | ✅ |
Compliance report (GET /inventory/compliance) |
❌ | ✅ | ❌ | ✅ | ✅ |
Trigger scan (POST /inventory/clients/{id}/scan) |
❌ | ❌ | ❌ | ✅ | ✅ |
| Reset SSH fingerprint (TOFU) | ❌ | ❌ | ❌ | ❌ | ✅ |
| Audit trail | |||||
Read audit logs (GET /artifacts/audit/logs) |
❌ | ✅ | ❌ | ✅ | ✅ |
| Users & API tokens | |||||
View current user (GET /auth/me) |
✅ | ✅ | ✅ | ✅ | ✅ |
Change own password (POST /auth/change-password) |
✅ | ✅ | ✅ | ✅ | ✅ |
List all users (GET /auth/users) |
❌ | ❌ | ❌ | ❌ | ✅ |
| Create / update / delete users | ❌ | ❌ | ❌ | ❌ | ✅ |
| Reset another user's password | ❌ | ❌ | ❌ | ❌ | ✅ |
Create API tokens (POST /auth/api-tokens) |
❌ | ❌ | ❌ | ❌ | ✅ |
Revoke API tokens (DELETE /auth/api-tokens/{id}) |
❌ | ❌ | ❌ | ❌ | ✅ |
| Settings (Enterprise only) | |||||
Read settings (GET /settings/) |
❌ | ❌ | ❌ | ❌ | ✅ |
Modify settings (PATCH /settings/) |
❌ | ❌ | ❌ | ❌ | ✅ |
| GPG key info / generate | ❌ | ❌ | ❌ | ❌ | ✅ |
| Test webhook / LDAP / email | ❌ | ❌ | ❌ | ❌ | ✅ |
| Run retention manually | ❌ | ❌ | ❌ | ❌ | ✅ |
| Dashboard & health | |||||
Dashboard stats (GET /dashboard/stats) |
✅ | ✅ | ✅ | ✅ | ✅ |
Dashboard history (GET /dashboard/history) |
✅ | ✅ | ✅ | ✅ | ✅ |
Download stats (GET /downloads/stats) |
✅ | ✅ | ✅ | ✅ | ✅ |
Health endpoints (GET /health*) |
✅ (public) | ✅ | ✅ | ✅ | ✅ |
Enterprise-only endpoints in Community
Endpoints marked "Enterprise only" above are gated behind an active Enterprise license.
Scoped access: distributions and machines (Enterprise)¶
The 5-role matrix above answers "what can this user do?" — for larger fleets, Repod adds a second, independent axis that answers "what can this user see and touch?":
- Per-distribution access restricts which distribution codenames (or Maven/PyPI/npm repositories, or OCI container repositories) a role or group can read from and publish to. A codename with no restrictions configured stays open to everyone with the base role permission — this is opt-in, so existing setups are unaffected until you configure it.
- Per-machine access applies the same restriction to the fleet inventory: which machines (individually, or by tag) a role or group can see, scan, or target with a remote install job.
Both follow the same rules: an admin account always bypasses the
restriction (so a distribution or machine can never end up unreachable by
everyone), and a denial on a read returns 404 rather than 403 — so a
restricted distribution or machine is indistinguishable from one that doesn't
exist, rather than confirming its existence to someone without access.
Role descriptions¶
reader¶
Read-only access to packages, distributions, dashboard, and health endpoints.
Intended for: client machines or service accounts that browse the package catalog without uploading.
auditor¶
Read-only access to packages plus audit logs and the CVE review queue. Cannot upload, delete, or modify anything.
Intended for: compliance officers, CISO team members, and SIEM service accounts. Give this role to users who need complete visibility of security decisions without any operational capability.
Tip
A SIEM service account with role auditor can poll GET /artifacts/audit/logs
for all security events without any write capability.
uploader¶
Can upload and import packages, and search the upstream index. Cannot delete, quarantine, make CVE decisions, or access audit logs.
Intended for: CI/CD pipelines. This is the minimum role for automated publishing. Do not grant a higher role to automated systems.
Warning
If a CI/CD token with uploader role is compromised, the attacker can upload
packages but cannot approve their own CVE-flagged uploads, delete existing packages,
or modify settings.
maintainer¶
Full package lifecycle: upload, import, delete, quarantine, sync sources, trigger rescans, and make CVE decisions (approve/reject packages from the review queue). Can read audit logs. Cannot manage users or modify settings.
Intended for: platform engineers and repository maintainers responsible for daily operations and security remediation.
admin¶
All maintainer capabilities plus user management, API token management, settings (LDAP, webhook, email, CVE policy, retention), and GPG key management.
Intended for: repository administrators. Limit to the minimum number of accounts.
Danger
An admin account can modify CVE policies, disable antivirus or CVE scanning,
and manage all users. Restrict it accordingly.
Assigning roles¶
Web UI¶
Go to Users in the left sidebar. Click the role dropdown next to any user to change their role immediately.
API¶
curl -X PATCH http://REPO_HOST:8000/auth/users/jdupont \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"role": "maintainer"}'
Note: the field is role (singular string), not roles.
LDAP group mapping¶
Configure the mapping in Settings → LDAP. Each role has a corresponding group_<role> field in settings.json. At login, Repod evaluates groups in priority order (admin > maintainer > uploader > auditor > reader) and assigns the highest matching role. If no group matches, default_role is applied (defaults to reader). See Configure LDAP.
API tokens¶
API tokens (format: repod_<random>) are created by admins via POST /auth/api-tokens. Each token is assigned a fixed role at creation time.
# Create a token with uploader role for CI pipelines
curl -X POST http://REPO_HOST:8000/auth/api-tokens \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "ci-pipeline", "role": "uploader"}'
The plaintext token is returned only once in the creation response — it is stored as a hash and cannot be retrieved afterwards.
Valid roles for API tokens: admin, maintainer, uploader, reader, auditor.
Role changes take effect immediately¶
Role changes apply to the user's next API request. Existing JWT tokens embed the role at issuance and are not re-validated mid-session (tokens expire after 60 minutes by default).
To immediately block a user, deactivate the account rather than changing the role. A deactivated account is rejected at the get_current_user dependency on every request, regardless of token validity.