API Reference — APT Repo Manager¶
Table of Contents¶
- Authentication
- Standard Response Codes
- Endpoints by Module
- Auth
- Packages
- Upload
- Import
- Security
- SBOM
- Downloads
- Dashboard
- Distributions
- Settings
- Health
- Complete Examples
- CI/CD Integration
- Rate Limiting
Authentication¶
JWT (interactive sessions)¶
Most endpoints require a JWT token passed in the Authorization HTTP header.
Obtain a token:
TOKEN=$(curl -s -X POST http://localhost:8000/auth/token \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"Admin1234!"}' | jq -r .access_token)
Use the token:
API Tokens (CI/CD)¶
For automated pipelines, use a permanent API token in the format repod_xxxxxxxxxx:
API tokens are created and managed via the /auth/api-tokens endpoints (admin only).
Public endpoints (no authentication required)¶
The following endpoints are accessible without a token:
| Endpoint | Description |
|---|---|
POST /auth/token |
Login / obtain JWT |
POST /auth/forgot-password |
Request password reset email |
POST /auth/reset-password |
Reset password with email token |
GET /health |
Full health check |
GET /health/live |
Liveness probe |
GET /health/ready |
Readiness probe |
Standard Response Codes¶
| Code | Meaning |
|---|---|
200 OK |
Request processed successfully |
201 Created |
Resource created successfully |
204 No Content |
Success with no response body |
400 Bad Request |
Invalid parameters or request body |
401 Unauthorized |
Token missing, invalid, or expired |
403 Forbidden |
Insufficient role for this action |
404 Not Found |
Resource not found |
409 Conflict |
Resource already exists (duplicate) |
422 Unprocessable Entity |
Data validation error |
429 Too Many Requests |
Rate limit reached |
500 Internal Server Error |
Server-side error |
Endpoints by Module¶
Auth (/auth)¶
POST /auth/token¶
Public. Log in and obtain a JWT.
curl -s -X POST http://localhost:8000/auth/token \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"Admin1234!"}'
Response:
GET /auth/me¶
Get information about the currently authenticated user.
Response:
POST /auth/change-password¶
Change your own password.
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
http://localhost:8000/auth/change-password \
-d '{"current_password":"Admin1234!","new_password":"NewPass5678!"}'
GET /auth/roles¶
Public. List available roles.
GET /auth/users¶
Admin. List all users.
POST /auth/users¶
Admin. Create a new user.
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
http://localhost:8000/auth/users \
-d '{
"username": "jsmith",
"email": "jsmith@example.com",
"password": "SecurePass1!",
"roles": ["uploader"]
}'
PATCH /auth/users/{username}¶
Admin. Update an existing user.
curl -X PATCH -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
http://localhost:8000/auth/users/jsmith \
-d '{"roles":["uploader","auditor"]}'
DELETE /auth/users/{username}¶
Admin. Delete a user.
POST /auth/users/{username}/reset-password¶
Admin. Reset a user's password.
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
http://localhost:8000/auth/users/jsmith/reset-password \
-d '{"new_password":"TempPass9!"}'
POST /auth/forgot-password¶
Public, rate-limited. Request a password reset email.
curl -X POST http://localhost:8000/auth/forgot-password \
-H "Content-Type: application/json" \
-d '{"email":"jsmith@example.com"}'
POST /auth/reset-password¶
Public, rate-limited. Reset password using the token received by email.
curl -X POST http://localhost:8000/auth/reset-password \
-H "Content-Type: application/json" \
-d '{"token":"<reset_token>","new_password":"NewPass5678!"}'
GET /auth/api-tokens¶
Admin. List existing API tokens.
POST /auth/api-tokens¶
Admin. Create an API token.
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
http://localhost:8000/auth/api-tokens \
-d '{"name":"gitlab-ci","roles":["uploader"]}'
Response:
The raw token
repod_xxxxxxxxxxis shown only once. Save it immediately.
DELETE /auth/api-tokens/{token_id}¶
Admin. Revoke an API token.
Packages (/packages, /artifacts)¶
GET /packages/¶
List packages with search and filters.
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8000/packages/?q=nginx&distribution=jammy"
GET /artifacts/¶
List all artifacts.
GET /artifacts/{name}¶
Get package information.
GET /artifacts/{name}/dependencies¶
Get package dependencies.
GET /artifacts/{name}/install¶
Get the install command for a package.
GET /artifacts/{name}/{version}¶
Get information on a specific version.
GET /artifacts/audit/logs¶
Auditor+. Retrieve audit logs.
POST /artifacts/admin/sync-index¶
Maintainer+. Sync the package index.
Upload (/upload)¶
POST /upload/¶
Uploader+, rate-limited (20/min). Publish a .deb file.
| Form field | Type | Description |
|---|---|---|
file |
file | The .deb file to publish |
distribution |
string | Target codename (e.g. jammy) |
curl -X POST -H "Authorization: Bearer $TOKEN" \
-F "file=@mypackage_1.0.0_amd64.deb" \
-F "distribution=jammy" \
http://localhost:8000/upload/
Response (201):
{
"name": "mypackage",
"version": "1.0.0",
"arch": "amd64",
"distribution": "jammy",
"status": "indexed"
}
Import (/import)¶
GET /import/search¶
Search for a package in the APT index.
| Parameter | Type | Description |
|---|---|---|
q |
string | Search term |
limit |
int | Maximum number of results |
source_id |
string | Filter by APT source |
GET /import/resolve/{package_name}¶
Resolve dependencies online for a package.
POST /import/fetch¶
Uploader+, rate-limited (10/min). Import a package from the internet.
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
http://localhost:8000/import/fetch \
-d '{
"name": "nginx",
"version": "1.24.0",
"arch": "amd64",
"distribution": "jammy"
}'
POST /import/batch¶
Uploader+, rate-limited (5/min). Batch import packages.
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
http://localhost:8000/import/batch \
-d '{
"packages": [
{"name":"nginx","version":"1.24.0","arch":"amd64","distribution":"jammy"},
{"name":"curl","version":"7.81.0","arch":"amd64","distribution":"jammy"}
]
}'
GET /import/sync-status¶
Get the sync status of all APT sources.
POST /import/sync¶
Maintainer+, rate-limited (3/min). Sync all APT sources.
GET /import/groups¶
List import groups.
DELETE /import/groups/{group_name}¶
Delete an import group.
POST /import/sync/{source_id}¶
Maintainer+. Sync a specific APT source.
POST /import/sync-security¶
Maintainer+. Sync security APT sources.
GET /import/sync-schedule¶
Get the next scheduled sync time.
Security (/security)¶
GET /security/vulnerabilities¶
List CVEs across all packages.
GET /security/packages-posture¶
Get the overall security posture summary.
GET /security/packages/{name}/{version}/cve¶
Get CVEs for a specific package version.
GET /security/review-queue¶
Auditor+. Get packages pending security review.
POST /security/packages/{name}/{version}/decide¶
Maintainer+. Make a CVE decision for a package.
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
http://localhost:8000/security/packages/nginx/1.24.0/decide \
-d '{"decision":"accepted","comment":"Risk accepted after analysis."}'
POST /security/packages/{name}/{version}/rescan¶
Maintainer+. Trigger a new vulnerability scan.
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://localhost:8000/security/packages/nginx/1.24.0/rescan
POST /security/packages/{name}/{version}/quarantine¶
Maintainer+. Quarantine a package.
curl -X POST -H "Authorization: Bearer $TOKEN" \
http://localhost:8000/security/packages/nginx/1.24.0/quarantine
GET /security/clamav/status¶
Get ClamAV antivirus engine status.
POST /security/clamav/update¶
Maintainer+. Update the ClamAV signature database.
SBOM (/sbom)¶
GET /sbom/export¶
Export the full SBOM.
| Parameter | Values | Description |
|---|---|---|
format |
cyclonedx, spdx |
Output format |
distribution |
codename | Filter by distribution |
# CycloneDX
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8000/sbom/export?format=cyclonedx&distribution=jammy" \
-o sbom.cdx.json
# SPDX
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8000/sbom/export?format=spdx&distribution=jammy" \
-o sbom.spdx.json
GET /sbom/{name}/{version}¶
Get the SBOM for a specific package.
| Parameter | Values | Description |
|---|---|---|
format |
cyclonedx, spdx |
Output format |
arch |
e.g. amd64 |
Architecture |
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8000/sbom/nginx/1.24.0?format=cyclonedx&arch=amd64" \
-o nginx-sbom.cdx.json
GET /sbom/preview¶
Preview the SBOM (limited results).
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8000/sbom/preview?format=cyclonedx&distribution=jammy&limit=5"
Downloads (/downloads)¶
GET /downloads/stats¶
Get download statistics.
| Parameter | Values | Description |
|---|---|---|
days |
7, 30, 90 |
Time period |
Dashboard (/dashboard)¶
GET /dashboard/stats¶
Get global repository statistics.
GET /dashboard/history¶
Get event history data.
Distributions (/distributions)¶
GET /distributions/¶
List available distributions.
GET /distributions/{codename}/packages¶
List packages present in a distribution.
POST /distributions/promote¶
Promote a package to another distribution.
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
http://localhost:8000/distributions/promote \
-d '{
"name": "nginx",
"version": "1.24.0",
"arch": "amd64",
"from_distribution": "jammy",
"to_distribution": "focal"
}'
POST /distributions/migrate¶
Migrate packages between distributions.
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
http://localhost:8000/distributions/migrate \
-d '{
"from_distribution": "focal",
"to_distribution": "jammy",
"packages": ["nginx","curl"]
}'
POST /distributions/init¶
Initialize a new distribution.
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
http://localhost:8000/distributions/init \
-d '{"codename":"noble","description":"Ubuntu 24.04 LTS"}'
Settings (/settings)¶
All
/settingsendpoints are restricted to admins.
GET /settings/¶
Read the full configuration (passwords masked).
PATCH /settings/¶
Update configuration settings.
curl -X PATCH -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
http://localhost:8000/settings/ \
-d '{"retention_days": 90}'
POST /settings/test-webhook¶
Test the configured webhook.
GET /settings/gpg¶
Get the repository GPG key information.
POST /settings/gpg/generate¶
Generate a new GPG key.
GET /settings/next-sync¶
Get the next scheduled sync time.
POST /settings/test-ldap¶
Test the LDAP connection.
POST /settings/run-retention¶
Manually trigger the retention policy.
POST /settings/test-email¶
Send a test email.
Health (/health)¶
GET /health¶
Public. Full health check with status of all subsystems.
Response:
GET /health/live¶
Public. Liveness probe — always returns 200 if the service is running.
GET /health/ready¶
Public. Readiness probe — indicates whether the service is ready to accept requests.
Complete Examples¶
Create a user and assign a role¶
# 1. Obtain an admin token
TOKEN=$(curl -s -X POST http://localhost:8000/auth/token \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"Admin1234!"}' | jq -r .access_token)
# 2. Create the user
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
http://localhost:8000/auth/users \
-d '{
"username": "deployer",
"email": "deployer@example.com",
"password": "Deploy9876!",
"roles": ["uploader"]
}'
# 3. Create an API token for CI pipelines
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
http://localhost:8000/auth/api-tokens \
-d '{"name":"pipeline-deployer","roles":["uploader"]}' | jq -r .token
Upload and verify a package¶
# Upload
curl -X POST -H "Authorization: Bearer $TOKEN" \
-F "file=@myapp_2.0.0_amd64.deb" \
-F "distribution=jammy" \
http://localhost:8000/upload/
# Verify the package is indexed
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8000/artifacts/myapp/2.0.0
# Check for CVEs
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8000/security/packages/myapp/2.0.0/cve
Import a package from upstream repositories¶
# Search upstream
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8000/import/search?q=curl&limit=5" | jq
# Resolve dependencies
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:8000/import/resolve/curl | jq
# Import
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
http://localhost:8000/import/fetch \
-d '{"name":"curl","version":"7.81.0","arch":"amd64","distribution":"jammy"}'
Export the full SBOM¶
# CycloneDX
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8000/sbom/export?format=cyclonedx&distribution=jammy" \
-o sbom-jammy.cdx.json
# SPDX
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:8000/sbom/export?format=spdx&distribution=jammy" \
-o sbom-jammy.spdx.json
echo "SBOM exported successfully."
CI/CD Integration¶
GitHub Actions — Publish a package¶
name: Publish package
on:
push:
tags:
- 'v*'
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build .deb
run: make deb
- name: Upload to repod
env:
REPOD_TOKEN: ${{ secrets.REPOD_TOKEN }}
REPOD_URL: https://repo.example.com
run: |
curl -X POST "$REPOD_URL/upload/" \
-H "Authorization: Bearer $REPOD_TOKEN" \
-F "file=@mypackage_1.0.0_amd64.deb" \
-F "distribution=jammy"
GitLab CI — Publish a package¶
stages:
- build
- deploy
build:
stage: build
script:
- make deb
deploy:
stage: deploy
script:
- |
curl -X POST "$REPOD_URL/upload/" \
-H "Authorization: Bearer $REPOD_TOKEN" \
-F "file=@mypackage_1.0.0_amd64.deb" \
-F "distribution=jammy"
only:
- tags
Add
REPOD_TOKENandREPOD_URLto your GitLab project's CI/CD variables.
Automated import from upstream¶
#!/usr/bin/env bash
set -euo pipefail
REPOD_URL="https://repo.example.com"
REPOD_TOKEN="${REPOD_TOKEN}"
DISTRIBUTION="jammy"
PACKAGES=("nginx" "curl" "openssl")
for pkg in "${PACKAGES[@]}"; do
echo "Importing $pkg..."
curl -s -X POST -H "Authorization: Bearer $REPOD_TOKEN" \
-H "Content-Type: application/json" \
"$REPOD_URL/import/fetch" \
-d "{\"name\":\"$pkg\",\"arch\":\"amd64\",\"distribution\":\"$DISTRIBUTION\"}"
done
Rate Limiting¶
Certain endpoints are protected by rate limits to prevent abuse.
| Endpoint | Limit |
|---|---|
POST /upload/ |
20 requests / minute |
POST /import/fetch |
10 requests / minute |
POST /import/batch |
5 requests / minute |
POST /import/sync |
3 requests / minute |
POST /auth/forgot-password |
Rate-limited (undisclosed) |
POST /auth/reset-password |
Rate-limited (undisclosed) |
When the limit is exceeded, the server responds with 429 Too Many Requests. Wait for the duration indicated in the Retry-After response header before retrying.
Role levels¶
Roles are cumulative — a higher role includes all permissions of lower roles:
| Role | Permissions |
|---|---|
viewer |
Read-only access |
uploader |
Viewer + publish and import packages |
auditor |
Uploader + access to audit logs and security review queue |
maintainer |
Auditor + sync sources, quarantine packages, make CVE decisions |
admin |
Full access, user management, and configuration |