Package
package installs, upgrades, holds, and removes Debian packages, either from apt repositories or from .deb archives bundled with your configuration. Both providers use the same package database, so a package name and architecture can be declared only once, whichever provider you use. An all-architecture package conflicts with any other declaration of that name.
An omitted provider selects apt when target facts advertise package.apt.v1. provider = "apt" or provider = "dpkg" chooses one; approved plugins can add further providers, which pin their implementation identity into the catalog and never join automatic selection unless they declare a priority. A provider block must match the selected provider; unrelated blocks, duplicate blocks, and unknown options fail.
Null omits optional version, source, checksum, and held. Required policy fields reject null. All common metadata, including ordering, notifications, noop, sensitivity, tags, description, and timeout, is available. Resource handles and static name, ensure, version, architecture, and held references preserve dependencies. Omitted runtime-derived values cannot be exported. Outputs can declare resource("package").
Apt provider
The apt provider handles candidate selection, index refresh, command options, and mutation behavior. It uses apt-cache policy, apt-get, and the shared dpkg-query installed-state reader.
present preserves any fully installed version unless version is supplied. latest selects the repository candidate using Debian version ordering and preserves an installed version newer than the candidate. Installs pin the selected version and forbid implicit package removals. A read-only apt simulation validates dependency resolution before mutation; removals that would remove unrelated packages are refused. Apt retains its native authentication and essential-package protections.
Refresh success updates /var/lib/apt/periodic/update-success-stamp; if_stale uses its modification time, treating missing or future stamps as stale. A failed refresh fails the resource and is not retried by other apt resources in the same run. Only present/latest resources request refresh. Refresh alone reports unchanged, so it does not imply a package consumer changed.
Noop never refreshes indexes. A missing candidate or a latest candidate that may change after refresh is reported with uncertain = true; unresolved predictions defer dependents. Real apply fails if no candidate is available after any requested refresh.
Dpkg provider
The dpkg provider handles archive metadata checks, archive installation, and removal behavior. It uses dpkg-deb through stdin to inspect archives without creating candidates during plan. It verifies the archive digest, package name, architecture, and any declared version before installation.
Dpkg installs a bundled archive using an anonymous seekable file that is unlinked immediately, so interruption leaves no archive candidate to clean up. It does not fetch dependencies or invoke apt repair. Missing dependencies may leave an unpacked or partly configured package; those states are reported and fail convergence until dependencies are satisfied.
URL downloads and target-host archive paths are not implemented. Bundle a configuration-relative archive instead. Foreign architectures must already be enabled on the host; Rivet does not mutate the architecture registry.
Execution and verification
Local real apply requires root and the existing host apply lock. Apt and dpkg also acquire their native package database locks and fail when another package manager holds them. A package resource has a shared 300-second deadline across inspection, refresh, mutation, and verification. Commands use explicit argv and a controlled noninteractive environment. Stdout capture is bounded to 1 MiB for queries; mutation output and stderr are drained and discarded. Timeouts terminate the owned process group; escaped descendants may remain.
Changing a currently held package requires held = false; the provider never silently overrides a hold. A held package must be unheld (held = false) while present before declaring it absent. absent removes the package while preserving conffiles; purged also removes package-managed configuration.
Mutation intent is durable before refresh, hold changes, or package changes. Postcondition inspection confirms package status, managed version, and hold state. Failed/partial mutations preserve uncertainty in the journal. A subsequent compatible apply re-inspects current state and converges it, reporting historical uncertainty; obsolete intent is retained as with files. There is no automatic rollback or automatic retry.
The disposable-host test tests/host/package_workflow.py exercises these behaviors; see testing.
Tool contracts: apt-get, dpkg, dpkg-query, and dpkg-deb.