Working with Debian / APT

CodeScoring.Save implements an APT-compatible repository with the /deb/<project>/<repository>/ prefix. It is compatible with standard apt, apt-get, and aptitude clients on Debian, Ubuntu, and derived distributions.

Proxy Repository

curl -X POST https://save.example.com/api/v1/repos \
  -H "Content-Type: application/json" \
  -u "<username>:<password>" \
  -d '{
    "project": "infra",
    "name": "debian-proxy",
    "format": "deb",
    "repository_type": "proxy",
    "remote_url": "https://deb.debian.org/debian",
    "cache_ttl": 3600
  }'
Proxy serves upstream indices verbatim

A proxy repository does not generate its own indices. The InRelease, Release, Release.gpg, and Packages* files are proxied from upstream byte for byte, so signatures and checksums remain valid. Packages from pool/ are cached as immutable artifacts; metadata is revalidated after cache_ttl expires. If upstream is unavailable, Save serves the last cached copy of the metadata. Uploads to a proxy repository are rejected: it is read-only.

Hosted Repository

curl -X POST https://save.example.com/api/v1/repos \
  -H "Content-Type: application/json" \
  -u "<username>:<password>" \
  -d '{
    "project": "infra",
    "name": "deb-hosted",
    "format": "deb",
    "repository_type": "hosted"
  }'

When a hosted repository is created, Save immediately publishes an empty stable suite with the main component, so apt-get update works before the first package is uploaded. The indices (Packages, Packages.gz, Release) are regenerated automatically after every package upload or deletion.

URL Scheme

https://save.example.com/cs-save/deb/<project>/<repository>/dists/<suite>/...     # indices
https://save.example.com/cs-save/deb/<project>/<repository>/pool/<component>/...  # packages
https://save.example.com/cs-save/deb/<project>/<repository>/repository.key        # public GPG key

Client Configuration

apt (signed repository)

If metadata signing is enabled on the server (METADATA_SIGNING_ENABLED), Save publishes InRelease and Release.gpg, and the public key is available at <repo>/repository.key:

# Install the repository public key
curl -u "<username>:<password>" \
  -o /etc/apt/keyrings/save.asc \
  https://save.example.com/cs-save/deb/<project>/deb-hosted/repository.key

# APT source with signature verification
echo 'deb [signed-by=/etc/apt/keyrings/save.asc] https://save.example.com/cs-save/deb/<project>/deb-hosted stable main' \
  > /etc/apt/sources.list.d/save.list

# Credentials through auth.conf.d (netrc format)
cat > /etc/apt/auth.conf.d/save.conf << EOF
machine save.example.com
login <username>
password <password>
EOF
chmod 600 /etc/apt/auth.conf.d/save.conf

apt-get update
apt-get install <package>

apt (unsigned repository)

If metadata signing is not enabled, use [trusted=yes]:

echo 'deb [trusted=yes] https://save.example.com/cs-save/deb/<project>/deb-hosted stable main' \
  > /etc/apt/sources.list.d/save.list
apt request order

apt requests InRelease first and falls back to the Release + Release.gpg pair on 404. This is expected behavior: a 404 on InRelease for an unsigned repository is not an error.

Robot accounts in CI

For CI/CD, use a robot account: login = sa$<robot-name>, password = <api-key> in /etc/apt/auth.conf.d/save.conf. For details, see Authentication.

Publishing Packages (hosted)

Uploads are performed with a PUT request to the canonical pool path. The suite and the component are passed as query parameters and default to stable and main:

curl -u "<username>:<password>" \
  -T mypackage_1.0.0_amd64.deb \
  "https://save.example.com/cs-save/deb/<project>/deb-hosted/pool/main/m/mypackage/mypackage_1.0.0_amd64.deb?suite=stable&component=main"

The file name must follow the <name>_<version>_<arch>.deb scheme. Save validates the package control structure and normalizes the path to the canonical form pool/<component>/<first-letter>/<name>/<name>_<version>_<arch>.deb. The resulting path is returned in the response.

An alternative is a multipart POST to the repository root:

curl -u "<username>:<password>" \
  -F "file=@mypackage_1.0.0_amd64.deb" \
  -F "suite=stable" \
  -F "component=main" \
  https://save.example.com/cs-save/deb/<project>/deb-hosted

Packages with the all architecture are automatically published into every concrete architecture of the suite (fan-out). The all pseudo-architecture itself is not advertised as a separate entry in Release.

Forcing Index Regeneration

curl -u "<username>:<password>" \
  -X POST https://save.example.com/cs-save/deb/<project>/deb-hosted/rebuild-index

Repository URL Migration

Use case: migrating an APT repository from Nexus / Artifactory to CodeScoring.Save.

Sourcesources.list line before migrationsources.list line after migration
Nexusdeb https://nexus.host.ru/repository/apt-hosted stable maindeb https://save.example.com/cs-save/deb/<project>/deb-hosted stable main
Artifactorydeb https://jfrog.host.ru/artifactory/deb-local stable maindeb https://save.example.com/cs-save/deb/<project>/deb-hosted stable main
Official repositorydeb https://deb.debian.org/debian bookworm maindeb https://save.example.com/cs-save/deb/<project>/debian-proxy bookworm main

Suites and components remain unchanged. For a proxy repository, Save serves upstream signatures verbatim, so existing signed-by keys, such as the Debian key, keep working.

Troubleshooting

Checking Release

curl -u "<username>:<password>" \
  https://save.example.com/cs-save/deb/<project>/deb-hosted/dists/stable/Release

The response contains the Suite, Components, and Architectures fields and a SHA256 section with references to the Packages indices.

Checking the Packages Index

curl -u "<username>:<password>" \
  https://save.example.com/cs-save/deb/<project>/deb-hosted/dists/stable/main/binary-amd64/Packages

If a package is uploaded but missing from the index, wait a few seconds because indexing is asynchronous, or run rebuild-index.

Checking the Public Key

curl -u "<username>:<password>" \
  https://save.example.com/cs-save/deb/<project>/deb-hosted/repository.key
# Expected: -----BEGIN PGP PUBLIC KEY BLOCK-----
# A 404 means metadata signing is not enabled on the server

Service Status

curl https://save.example.com/health

Repository Audit

curl -u "<username>:<password>" \
  "https://save.example.com/api/v1/admin/audit?resource_type=repository&q=deb-hosted&limit=50"
Was this page helpful?