Packages

This document defines the package catalog and the artifact contract between cup and cup-components. Archive security is expanded in SECURITY.

Repository boundary

cup-components provides:

building each tool
selecting its configured features
including required runtime files
creating info.txt
creating archives
publishing SHA256SUMS

cup provides:

catalog parsing and tuple selection
download and checksum verification
archive safety
metadata and executable validation
canonical installation paths
state and default management

The contract is intentionally file-based. cup does not need knowledge of the component build system, Docker images, MSYS2 packages or Homebrew formulas.

PackageCatalog locations

Installed catalog:

<cup-root>/config/packages.cfg

Repository development copy:

config/packages.cfg

A normal installation uses the installed catalog. A development executable may fall back to the repository copy only when the installed file is missing.

Installation policy and local preferences

Package availability and install selection are separate contracts. The compiled registry remains the sole authority for recognized components, recognized tools and each tool's component. A catalog can make one registered pair available for a platform, but cannot introduce a new component/tool relationship.

The official installation policy is installed at <cup-root>/config/install.cfg; its repository development copy is config/install.cfg. It uses the same blank-line and whole-line comment rules as the catalog and otherwise remains a strict closed key/value document. It defines:

default.<host>.<target>.<component>=<tool>
profile.<name>=<component>,...
toolchain.<name>=<tool>,...

Every reference is validated against the compiled registry. Defaults are scoped by component, host and target. Profiles contain components and resolve each one at install time. Curated toolchains contain explicit tools, with at most one tool per component. The file is covered by SHA256SUMS.common, protected as an official asset and replaced transactionally by cup update cup.

Local choices are stored separately at <cup-root>/config/preferences.txt:

preferred.<host>.<target>.<component>=<tool>

This mutable file has no official checksum because it contains user-controlled preferences. cup parses it strictly, serializes entries deterministically and replaces it atomically. Selection order for an abbreviated component install is:

explicit command selector
scoped user preference
scoped official default
error

The selected tool is then checked against the registry and the exact host/target tuple is looked up in packages.cfg. Profiles intentionally apply this hierarchy to each component. Toolchains never consult local preferences or installed execution defaults. Profile, toolchain, component, tool, platform and symbolic stable values are canonical lowercase; concrete version identifiers remain case-sensitive. Updates operate only on installed state and never select new tools from these preferences.

Catalog record model

The catalog is a line-based key/value document. Blank lines and lines whose first non-whitespace character is # are ignored; every remaining line must be one non-empty key=value record. Unknown fields, duplicates, partial tuples and malformed records are rejected. Keys use:

<component>.<tool>.<host_platform>.<target_platform>.<field>=<value>

Every tuple requires:

stable_version
available_versions
default_format
formats
url_template
checksum_url_template

Example:

compiler.gcc.linux-x64.windows-x64.stable_version=16.1.0-rev1
compiler.gcc.linux-x64.windows-x64.available_versions=16.1.0-rev1
compiler.gcc.linux-x64.windows-x64.default_format=tar.gz
compiler.gcc.linux-x64.windows-x64.formats=tar.xz,tar.gz,zip
compiler.gcc.linux-x64.windows-x64.url_template=
  https://github.com/coffee-clang/cup-components/releases/download/
  gcc-{version}-{host_platform}-{target_platform}/
  gcc-{version}-{host_platform}-{target_platform}.{format}
compiler.gcc.linux-x64.windows-x64.checksum_url_template=
  https://github.com/coffee-clang/cup-components/releases/download/
  gcc-{version}-{host_platform}-{target_platform}/SHA256SUMS

Catalog validation

Loading rejects:

  • malformed key/value lines;
  • empty values;
  • unknown or missing fields;
  • duplicate field keys;
  • duplicate values in available_versions or formats;
  • unsupported component/tool pairs;
  • invalid host or target identifiers;
  • unsupported archive formats;
  • a default format absent from formats;
  • a stable version absent from available_versions;
  • non-HTTPS templates;
  • unknown placeholders;
  • missing required placeholders;
  • format-dependent checksum templates.

The URL template must distinguish the concrete package by tool, version, host, target and format. The checksum template identifies the matching release tuple without depending on archive format.

Validation is strict because a malformed catalog can otherwise map different identities to the same remote asset or make an installation non-reproducible.

URL placeholders

Supported placeholders are:

{tool}
{version}
{host_platform}
{target_platform}
{format}

{format} is used by archive URLs but is not required or accepted as a checksum-release discriminator. Template expansion occurs only after all identity values pass their own validation.

Stable and available versions

stable_version is one concrete version from available_versions.

A command input such as:

gcc@stable

is resolved before a package path or state entry is created. Advancing the catalog pointer does not mutate existing state. cup update explicitly installs the new stable version and may move a matching default while retaining older versions.

Version strings are identifiers, not values interpreted through semantic version precedence. The catalog decides availability and stable selection.

Archive formats

Current supported formats are:

tar.xz
tar.gz
zip

The tuple's default_format is used when install receives no override. --format or -f can select another value only when it appears in that tuple's formats list.

cup uses libarchive directly. Runtime installation does not invoke external tar, gzip, xz or unzip programs. The detected stream must match the selected format; file extensions and catalog values are not accepted as proof of the actual archive type.

Package identity

One package identity contains:

component
tool
host platform
target platform
version

Canonical installation path:

<cup-root>/components/<component>/<tool>/<host>/<target>/<version>/

Canonical cache directory and filename:

<cup-root>/cache/<component>/<tool>/<host>/<target>/<version>/
  <tool>-<version>-<host>-<target>.<format>

The cache filename is built locally from validated identity fields. It is not copied from a URL path or response header.

Archive root

An archive must contain exactly one common top-level directory. Internal names use printable portable ASCII segments and cannot collide under ASCII case-folding or as file-versus-directory paths. The root name is not used as the trusted identity; after extraction, info.txt must match the identity selected from the command and catalog.

The package root can contain tool-specific directories such as:

bin/
lib/
libexec/
include/
share/
<target-triple>/

cup does not require every package to have the same internal layout. It requires valid metadata and declared executable entries.

info.txt

Every package root contains a line-based info.txt generated by cup-components.

Required identity fields:

package.component
package.tool
package.version
platform.host
platform.target

Additional grouped fields can include:

entry.*
features.*
contents.*
config.*

Example:

package.component=compiler
package.tool=gcc
package.version=16.1.0-rev1
platform.host=linux-x64
platform.target=linux-x64
entry.gcc=bin/gcc
features.c=true
features.cpp=true
contents.self_contained=true
config.languages=c,c++,lto

Parsing rejects malformed lines, empty keys or values, duplicate keys and input that exceeds defined line, key or value limits.

Executable entries

Each entry.<name> value is a safe relative path inside the package. Package validation requires the declared object to be a regular executable file.

The entry name is later used to derive managed commands:

native target       <entry>
cross target        <target>-<entry>

Entry-point planning rejects duplicate names, collisions between defaults and a wrapper named cup. See STATE.

Package validation

A package is valid only when:

  • the canonical identity is syntactically valid;
  • the package root is a directory;
  • info.txt is a regular file;
  • all required identity fields are present;
  • metadata identity matches the canonical path;
  • every declared entry path is safe and exists as an executable regular file.

Read-only protection of info.txt is a separate managed invariant. Installation applies it, doctor diagnoses drift and repair restores it, but a permission change does not alter the package identity or make metadata parsing a different command-specific contract.

inspect, default, info, doctor and state reconciliation use the same package contract rather than implementing command-specific interpretations.

Package scan and adoption

repair scans the canonical component hierarchy. A valid package not present in state can be adopted because its path and metadata provide a complete identity.

An invalid object at the complete version level can be quarantined only when its path provides a safe, canonical identity. Ambiguous or unrecognized paths are reported and left unchanged.

The scan records both returned entries and total entries. If results were truncated by capacity limits, repair stops before changing packages or state. This avoids treating an incomplete observation as a complete model.

Cache behavior

A cached archive is reused only after its checksum is verified against the release SHA256SUMS. If extraction or package validation fails for a cached archive, cup discards it and performs one network refresh. A second failure is reported; it is not retried indefinitely.

This distinction handles a stale or locally corrupted cache without hiding a consistently invalid published package.

Version revisions

A tool release can include a packaging revision in its concrete version, for example:

16.1.0-rev1

The complete string is part of the identity. cup does not separate upstream version from packaging revision. This keeps catalog, asset tag, archive name, metadata, state and path comparison exact.

Limits

Current in-memory package scanning limits are defined in include/package.h. Archive limits are defined in include/constants.h: 262,144 entries, 16 GiB for one regular file, 64 GiB total extracted bytes and 64 path segments. These are conservative ceilings for complete compiler and debugger packages, not allocation targets. Exceeding one produces a failing or incomplete result rather than silent truncation. This repository does not contain published cup-components package artifacts from which to claim a final measured maximum, so final tuning remains coordinated with an inventory of those release packages.

Implementation and verification

Package-module responsibilities are listed in ARCHITECTURE. The focused and process-level checks for these contracts are listed in TESTING.

  • ARCHITECTURE — registry and package model;
  • SECURITY — archive and checksum protections;
  • STATE — installed identities and defaults;
  • COMMANDS — package-facing CLI behavior.

Need help?
Please wait: the project is still pre-alpha!

Cup of Coffee is an open source project inspired by Cargo, Rustup Crates, and Conda-forge.