# Package repositories

Debusine is in the process of gaining the ability to host package repositories. These work by including the necessary index files in debian:suite collections. When the packages in a suite changes, the update_suites workflow generates updated indexes for it. Debusine can serve all the relevant files in an APT-compatible layout.

## Snapshots

It is useful to serve historical snapshots as well as the current state of the archive, as long as Debusine still retains the necessary data under whatever retention policies are in effect. The collection data model already includes timestamps for when collection items were created or removed, and supports retaining items even after they are no longer active in their parent collection; as a result, repository indexes at a particular timestamp can be found by querying for collection items containing indexes that were not created after that timestamp and not removed before that timestamp.

## Overrides

Overrides are used by Debian’s traditional archive management software to store the component, section, and (for binary packages) priority of each package. (Ubuntu’s archive management software also supports phased update percentages, which are handled by overrides; other extensions are possible.)

While the name “override” might suggest that these are only applied where the values are something other than the ones supplied by the package, in fact every package in the archive has overrides even if those are equal to the ones supplied by the package. In Debian’s traditional archive management software, uploads of packages without overrides go into the `NEW` queue for manual review.

Review workflows of this kind are not yet in scope, but we already have per-item data for component, section, and priority in the debian:suite collection which represent the common set of overrides and are considered when generating `Packages` and `Sources` files. Debian also publishes override summaries in an `indices` directory; we treat these as just another kind of repository index file, although they are stored at the archive level rather than at the suite level.

## Access views

### Domains

Since Debusine’s `Set-Cookie` header doesn’t include a `Domain` attribute, its cookies are not available to subdomains. It should therefore be safe to run repositories from a subdomain of `DEBUSINE_FQDN`: content served from that subdomain will not be able to run session fixation attacks on its parent domain.

A reasonable default is `deb.{DEBUSINE_FQDN}`: shorter than `repositories`, not protocol-specific like `ftp`, and familiar from `deb.debian.org`. However, we include the Django setting `DEBUSINE_DEBIAN_ARCHIVE_FQDN` to allow instances to override this.

Repositories will normally be served over HTTPS, but public repositories may also be served over HTTP. This is sometimes useful for bootstrapping minimal environments that lack certificates.

### URL paths

The basic path to a repository is `/{scope}/{workspace}`, relying on the fact that archive collections are singletons. Suites in the archive are mapped onto subdirectories of `dists/` as usual.

Todo

In future, we might also provide the ability to map particular repositories onto different URLs.

by-hash paths are handled specially: they are translated into a search for a repository index in the correct suite with the correct path and checksum. That repository index need not be the active index for its path in the suite; it can be served as long as the suite’s `full_history_retention_period` has not expired.

`/{scope}/{workspace}/{timestamp}`, where `timestamp` has the format `YYYYMMDDTHHMMSSZ`, e.g. `20250529T133100Z` for 13:31 UTC on 29 May 2025, addresses the state of the repository as it existed at `timestamp`. This is done by looking up collection items that have `created_at` and `removed_at` fields bracketing the given timestamp, rather than just those that are active (with `removed_at` unset), and can be done as long as the suite’s `full_history_retention_period` has not expired. This URL format is compatible with snapshot.debian.org and snapshot.ubuntu.com; like those services, a user can request any timestamp as a snapshot ID, and the server will provide the indexes and other files that were current at that point in time.

Todo

Add a Snapshots field to generated `Release` files, pointing to the corresponding URL format.

Todo

Once we’ve defined how repository signing works, we should also publish each repository’s public keys in some standard location.

### Authentication and authorization

APT only handles HTTP Basic Authentication, so we’re stuck with that mechanism, but we can still use our existing user tokens: users may use their user name with their user token acting as a password. Downloading files from a repository requires the “viewer” role on the workspace.

### Cache control

Responses to different URLs in a repository should be cached in different ways, and Debusine sends the `Cache-Control` response header to inform HTTP caches of this.

`by-hash` files under `dists/`, and all files resulting from a timestamp-specific query are immutable: they may be removed, but they won’t be replaced with different contents at the same URL. This is also true for files under `pool/` in archives where `may_reuse_versions` is false. For these files, Debusine sends `Cache-Control: max-age=31536000` (i.e. 365 days, an arbitrary long period).

For other files, Debusine sends `Cache-Control: max-age=1800, proxy-revalidate` to indicate that shared caches should revalidate responses before reuse if they are older than half an hour. (This period is also arbitrary, but should be a good starting point.)

Debusine also sends `Vary: Authorization` for all responses to repository URLs, since its responses depend on whether the requester has the “viewer” role on the workspace.

### Examples

If we were to create an archive collection in `debian/base` on debusine.debian.net and generate indexes for its suites, then APT configuration for it might look something like this:

    Types: deb deb-src
    URIs: https://deb.debusine.debian.net/debian/base
    Suites: bookworm
    Components: main contrib non-free non-free-firmware

A snapshot of it from the start of May 2025 might be as follows (in this case we have to add the snapshot ID to the URL, as we don’t control the `Release` file and can’t add a `Snapshots` field to it):

    Types: deb deb-src
    URIs: https://deb.debusine.debian.net/debian/base/20250501T000000Z
    Suites: bookworm
    Components: main contrib non-free non-free-firmware

An experiment workspace based on `debian/developers` might look like this:

    Types: deb deb-src
    URIs: https://deb.debusine.debian.net/debian/developers-test
    Suites: sid
    Components: main

Or a snapshot, once Debusine’s generated `Release` files gain a `Snapshots` field:

    Types: deb deb-src
    URIs: https://deb.debusine.debian.net/debian/developers-test
    Snapshot: 20250501T000000Z
    Suites: sid
    Components: main

## Key management

By default, each archive has its own OpenPGP signing key, which is used to sign indexes in its suites. This provides a reasonable default for common cases, where needing to rotate keys for an archive for any reason has limited consequences.

The signing key is generated automatically the first time metadata updates for the suites in an archive are needed. An administrator can manually change the configuration for the relevant collections to use different signing keys. For example:

- During key rollovers, a suite’s indexes may be signed using multiple keys.

- In some high-value cases, different suites in the same archive use different signing keys that are generated manually and have carefully-selected expiry periods: Debian itself is an example of this.

- All the workspaces in a scope may be controlled by the same customer, and it may be simpler for them all to use the same signing key.

Todo

Debusine currently only supports OpenPGP signing with software-encrypted keys. It should gain support for generating keys on PKCS#11 tokens and signing using those keys, and for exporting those keys under wrap in order to deal with hardware security models with a limited number of object slots.